Compare commits

...
6 Commits
Author SHA1 Message Date
Hakase f9d52d14d0 Submodule update 2026-01-10 16:33:31 +09:00
Hakase 94e5c04900 Latest update - GitHub 7c2524a (1.29.5) 2025-12-17 13:01:20 +09:00
Hakase 82f432938a Submodule update 2025-12-17 12:59:05 +09:00
Hakase 694f182c33 Latest update - GitHub 7c2524a 2025-12-17 11:54:42 +09:00
Hakase d42d1aa241 Latest update - GitHub fd0848b (1.29.3) 2025-11-03 21:09:28 +09:00
Hakase 602334da65 Submodule update 2025-11-03 21:07:59 +09:00
68 changed files with 6698 additions and 554 deletions
+15
View File
@@ -0,0 +1,15 @@
---
body:
- type: markdown
attributes:
value: |
For NGINX troubleshooting/technical help, please visit our community forum instead of asking your questions here. We will politely redirect these types of questions to the forum.
- type: textarea
id: general
attributes:
label: What would you like to discuss?
description: Please provide as much context as possible. Remember that only general discussions related to the NGINX codebase will be addressed on GitHub. For anything else, please visit our [community forum](https://community.nginx.org/).
value: |
I would like to discuss...
validations:
required: true
+15
View File
@@ -0,0 +1,15 @@
---
body:
- type: markdown
attributes:
value: |
For NGINX troubleshooting/technical help, please visit our community forum instead of asking your questions here. We will politely redirect these types of questions to the forum.
- type: textarea
id: ideas
attributes:
label: What idea would you like to discuss?
description: Please provide as much context as possible. Remember that only ideas related to the NGINX codebase will be addressed on GitHub. For anything else, please visit our [community forum](https://community.nginx.org/).
value: |
I have an idea for...
validations:
required: true
+15
View File
@@ -0,0 +1,15 @@
---
body:
- type: markdown
attributes:
value: |
For NGINX troubleshooting/technical help, please visit our community forum instead of asking your questions here. We will politely redirect these types of questions to the forum.
- type: textarea
id: q-a
attributes:
label: What question do you have?
description: Please provide as much context as possible. Remember that only questions related to the NGINX codebase will be addressed on GitHub. For anything else, please visit our [community forum](https://community.nginx.org/).
value: |
I would like to know...
validations:
required: true
-38
View File
@@ -1,38 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ""
labels: "bug"
---
### Environment
Include the result of the following commands:
- `nginx -V`
- `uname -a`
### Description
Describe the bug in full detail including expected and actual behavior.
Specify conditions that caused it. Provide the relevant part of nginx
configuration and debug log.
- [ ] The bug is reproducible with the latest version of nginx
- [ ] The nginx configuration is minimized to the smallest possible
to reproduce the issue and doesn't contain third-party modules
#### nginx configuration
```
# Your nginx configuration here
```
or share the configuration in [gist](https://gist.github.com/).
#### nginx debug log
It is advised to enable
[debug logging](http://nginx.org/en/docs/debugging_log.html).
```
# Your nginx debug log here
```
or share the debug log in [gist](https://gist.github.com/).
+106
View File
@@ -0,0 +1,106 @@
---
name: 🐛 Bug report
description: Create a report to help us improve
labels: bug
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
Before you continue filling out this report, please take a moment to check that your bug has not been [already reported on GitHub][issue search], is reproducible with the latest version of nginx, and does not involve any third-party modules 🙌
Remember to redact any sensitive information such as authentication credentials and/or license keys!
**Note:** If you are seeking community support, please start a new topic in the [NGINX Community forum][forum]. If you wish to discuss the codebase, please start a new thread via [GitHub discussions][discussions].
[issue search]: https://github.com/nginx/nginx/search?q=is%3Aissue&type=issues
[discussions]: https://github.com/nginx/nginx/discussions
[forum]: https://community.nginx.org
- type: textarea
id: overview
attributes:
label: Bug Overview
description: A clear and concise overview of the bug.
placeholder: When I do "X", "Y" happens instead of "Z".
validations:
required: true
- type: textarea
id: behavior
attributes:
label: Expected Behavior
description: A clear and concise description of what you expected to happen.
placeholder: When I do "X", I expect "Z" to happen.
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to Reproduce the Bug
description: Detail the series of steps required to reproduce the bug.
placeholder: When I run "X" using [...], "X" fails with "Y" error message. If I check the terminal outputs and/or logs, I see the following info.
validations:
required: true
- type: textarea
id: configuration
attributes:
label: NGINX Configuration
description: Please provide your NGINX configuration. Minimize it to the smallest possible configuration that reproduces the issue.
value: |
```
# Your NGINX configuration
```
validations:
required: true
- type: textarea
id: version
attributes:
label: NGINX version and build configuration options
description: Please provide details about your NGINX build.
value: |
The output of `nginx -V`: [...]
validations:
required: true
- type: textarea
id: environment
attributes:
label: Environment where NGINX is being built and/or deployed
description: Please provide details about your environment.
value: |
- Target deployment platform: [e.g. AWS/GCP/local cluster/etc...]
- Target OS: [e.g. RHEL 9/Ubuntu 24.04/etc...]
validations:
required: true
- type: textarea
id: architecture
attributes:
label: Architecture where NGINX is being built and/or deployed
description: Please provide details about your deployment environment.
value: |
The output of `uname -a`: [...]
validations:
required: true
- type: textarea
id: logs
attributes:
label: NGINX Debug Log
description: Please provide your NGINX debug log. See this [doc](http://nginx.org/en/docs/debugging_log.html) for details on how to enable it.
value: |
```
# Your NGINX debug log
```
- type: textarea
id: context
attributes:
label: Additional Context
description: Add any other context about the problem here.
placeholder: Feel free to add any other context/information/screenshots/etc... that you think might be relevant to this issue in here.
+12
View File
@@ -0,0 +1,12 @@
---
blank_issues_enabled: false
contact_links:
- name: 💬 Talk to the NGINX community!
url: https://community.nginx.org
about: A community forum for NGINX users, developers, and contributors
- name: 📝 Code of Conduct
url: https://www.contributor-covenant.org/version/2/1/code_of_conduct
about: NGINX follows the Contributor Covenant Code of Conduct to ensure a safe and inclusive community
- name: 💼 For commercial & enterprise users
url: https://www.f5.com/products/nginx
about: F5 offers a wide range of NGINX products for commercial & enterprise users
-18
View File
@@ -1,18 +0,0 @@
---
name: Feature request
about: Suggest a feature for nginx
title: ""
labels: "feature"
---
### Describe the feature you'd like to add to nginx
A clear and concise description of the feature.
### Describe the problem this feature solves
A clear and concise description of the problem.
### Additional context
Add any other context about the feature request here.
@@ -0,0 +1,40 @@
---
name: ✨ Feature request
description: Suggest an idea for this project
labels: enhancement
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this feature request!
Before you continue filling out this request, please take a moment to check that your feature has not been [already requested on GitHub][issue search] 🙌
**Note:** If you are seeking community support, please start a new topic in the [NGINX Community forum][forum]. If you wish to discuss the codebase, please start a new thread via [GitHub discussions][discussions].
[issue search]: https://github.com/nginx/nginx/search?q=is%3Aissue&type=issues
[discussions]: https://github.com/nginx/nginx/discussions
[forum]: https://community.nginx.org
- type: textarea
id: overview
attributes:
label: Feature Overview
description: A clear and concise description of what the feature request is.
placeholder: I would like this project to be able to do "X".
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives Considered
description: Detail any potential alternative solutions/workarounds you've used or considered.
placeholder: I have done/might be able to do "X" in this project by doing "Y".
- type: textarea
id: context
attributes:
label: Additional Context
description: Add any other context about the problem here.
placeholder: Feel free to add any other context/information/screenshots/etc... that you think might be relevant to this feature request here.
+6 -6
View File
@@ -1,10 +1,10 @@
### Proposed changes ### Proposed changes
Describe the use case and detail of the change. Describe the use case and detail of the change. If this PR addresses an issue on GitHub, make sure to include a link to that issue using one of the [supported keywords](https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue) in this PR's description or commit message.
If this pull request addresses an issue on GitHub, make sure to reference that ### Checklist
issue using one of the
[supported keywords](https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue).
Before creating a pull request, make sure to comply with the Before creating a PR, run through this checklist and mark each as complete:
[Contributing Guidelines](https://github.com/nginx/nginx/blob/master/CONTRIBUTING.md).
- [ ] I have read the [contributing guidelines](/CONTRIBUTING.md).
- [ ] I have checked that NGINX compiles and runs after adding my changes.
+41
View File
@@ -0,0 +1,41 @@
---
name: F5 CLA
on:
issue_comment:
types: [created]
pull_request_target:
types: [opened, closed, synchronize]
permissions: read-all
jobs:
f5-cla:
name: F5 CLA
runs-on: ubuntu-24.04
permissions:
actions: write
pull-requests: write
statuses: write
steps:
- name: Run F5 Contributor License Agreement (CLA) assistant
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have hereby read the F5 CLA and agree to its terms') || github.event_name == 'pull_request_target'
uses: contributor-assistant/github-action@ca4a40a7d1004f18d9960b404b97e5f30a505a08 # v2.6.1
with:
# Path to the CLA document.
path-to-document: https://github.com/f5/f5-cla/blob/main/docs/f5_cla.md
# Custom CLA messages.
custom-notsigned-prcomment: '🎉 Thank you for your contribution! It appears you have not yet signed the [F5 Contributor License Agreement (CLA)](https://github.com/f5/f5-cla/blob/main/docs/f5_cla.md), which is required for your changes to be incorporated into an F5 Open Source Software (OSS) project. Please kindly read the [F5 CLA](https://github.com/f5/f5-cla/blob/main/docs/f5_cla.md) and reply on a new comment with the following text to agree:'
custom-pr-sign-comment: 'I have hereby read the F5 CLA and agree to its terms'
custom-allsigned-prcomment: '✅ All required contributors have signed the F5 CLA for this PR. Thank you!'
# Remote repository storing CLA signatures.
remote-organization-name: f5
remote-repository-name: f5-cla-data
# Branch where CLA signatures are stored.
branch: main
path-to-signatures: signatures/signatures.json
# Comma separated list of usernames for maintainers or any other individuals who should not be prompted for a CLA.
# NOTE: You will want to edit the usernames to suit your project needs.
allowlist: bot*
# Do not lock PRs after a merge.
lock-pullrequest-aftermerge: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PERSONAL_ACCESS_TOKEN: ${{ secrets.F5_CLA_TOKEN }}
+24 -11
View File
@@ -5,34 +5,34 @@ We really appreciate that you are considering contributing!
## Table of Contents ## Table of Contents
- [Ask a Question](#ask-a-question)
- [Report a Bug](#report-a-bug) - [Report a Bug](#report-a-bug)
- [Suggest a Feature or Enhancement](#suggest-a-feature-or-enhancement) - [Suggest a Feature or Enhancement](#suggest-a-feature-or-enhancement)
- [Open a Discussion](#open-a-discussion) - [Open a Discussion](#open-a-discussion)
- [Submit a Pull Request](#submit-a-pull-request) - [Submit a Pull Request](#submit-a-pull-request)
- [Issue Lifecycle](#issue-lifecycle) - [Issue Lifecycle](#issue-lifecycle)
## Ask a Question
To ask a question, open an issue on GitHub with the label `question`.
## Report a Bug ## Report a Bug
To report a bug, open an issue on GitHub with the label `bug` using the To report a bug, open an issue on GitHub with the label `bug` using the
available bug report issue template. Before reporting a bug, make sure the available [bug report issue form](/.github/ISSUE_TEMPLATE/bug_report.yml).
issue has not already been reported. Please ensure the bug has not already been reported. **If the bug is a
potential security vulnerability, please report it using our
[security policy](/SECURITY.md).**
## Suggest a Feature or Enhancement ## Suggest a Feature or Enhancement
To suggest a feature or enhancement, open an issue on GitHub with the label To suggest a feature or enhancement, please create an issue on GitHub with the
`feature` or `enhancement` using the available feature request issue template. label `enhancement` using the available
[feature request issue form](/.github/ISSUE_TEMPLATE/feature_request.yml).
Please ensure the feature or enhancement has not already been suggested. Please ensure the feature or enhancement has not already been suggested.
## Open a Discussion ## Open a Discussion
If you want to engage in a conversation with the community and maintainers, If you want to engage in a conversation with the community and maintainers,
we encourage you to use we encourage you to use
[GitHub Discussions](https://github.com/nginx/nginx/discussions). [GitHub Discussions](https://github.com/nginx/nginx/discussions) to discuss
the NGINX codebase or the [NGINX Community forum](https://community.nginx.org)
to chat anything else NGINX (including troubleshooting).
## Submit a Pull Request ## Submit a Pull Request
@@ -89,7 +89,20 @@ git clone https://github.com/nginx/nginx-tests.git
``` ```
- Submitting a change implies granting project a permission to use it under the - Submitting a change implies granting project a permission to use it under the
[BSD-2-Clause license](https://github.com/nginx/nginx/blob/master/LICENSE) [BSD-2-Clause license](/LICENSE)
### F5 Contributor License Agreement (CLA)
F5 requires all contributors to agree to the terms of the F5 CLA
(available [here](https://github.com/f5/f5-cla/blob/main/docs/f5_cla.md))
before any of their changes can be incorporated into an F5 Open Source
repository (even contributions to the F5 CLA itself!).
If you have not yet agreed to the F5 CLA terms and submit a PR to this
repository, a bot will prompt you to view and agree to the F5 CLA.
You will have to agree to the F5 CLA terms through a comment in the PR
before any of your changes can be merged. Your agreement signature
will be safely stored by F5 and no longer be required in future PRs.
## Issue Lifecycle ## Issue Lifecycle
+48
View File
@@ -0,0 +1,48 @@
# Support
## Ask a Question
We use GitHub issues for tracking bugs and feature requests
related to this project.
If you don't know how something in the codebase works, are curious if NGINX
is capable of achieving your desired functionality or want to discuss the
implementation of an existing or in development feature, please start a
GitHub discussion!
## NGINX Specific Questions and/or Issues
This project isn't the right place to get support for NGINX and/or NGINX
troubleshooting questions, but the following resources are available below.
Thanks for your understanding!
### Community Forum
We have a [community forum](https://community.nginx.org/)!
If you have any NGINX specific questions and/or issues,
try checking out the [`NGINX category`](https://community.nginx.org/c/projects/nginx/23).
For general discussions around anything tangentially NGINX related,
check out the [`General Discussion category`](https://community.nginx.org/c/general-discussion/34).
Both fellow community members and NGINXers might be able to help you! :)
### Documentation
For a comprehensive list of all NGINX directives, check out <https://nginx.org>.
For a comprehensive list of administration and deployment guides for all
NGINX products, check out <https://docs.nginx.com>.
## Contributing
Please see the [contributing guide](/CONTRIBUTING.md) for guidelines
on how to best contribute to this project.
## Commercial Support
Commercial support for this project is available.
Please get in touch with [F5 sales](https://www.f5.com/products/get-f5/)
or check your contract details for more information!
## Community Support
Community support is offered on a best effort basis through any of our active communities.
-9
View File
@@ -5,15 +5,6 @@
# clang # clang
NGX_CLANG_VER=`$CC -v 2>&1 | grep 'version' 2>&1 \
| sed -n -e 's/^.*clang version \(.*\)/\1/p' \
-e 's/^.*LLVM version \(.*\)/\1/p'`
echo " + clang version: $NGX_CLANG_VER"
have=NGX_COMPILER value="\"clang $NGX_CLANG_VER\"" . auto/define
CC_TEST_FLAGS="-pipe" CC_TEST_FLAGS="-pipe"
+2 -10
View File
@@ -8,14 +8,6 @@
# 4.0.0, 4.0.1, 4.1.0 # 4.0.0, 4.0.1, 4.1.0
NGX_GCC_VER=`$CC -v 2>&1 | grep 'gcc version' 2>&1 \
| sed -e 's/^.* version \(.*\)/\1/'`
echo " + gcc version: $NGX_GCC_VER"
have=NGX_COMPILER value="\"gcc $NGX_GCC_VER\"" . auto/define
# Solaris 7's /usr/ccs/bin/as does not support "-pipe" # Solaris 7's /usr/ccs/bin/as does not support "-pipe"
CC_TEST_FLAGS="-pipe" CC_TEST_FLAGS="-pipe"
@@ -112,7 +104,7 @@ esac
CC_AUX_FLAGS="$CC_AUX_FLAGS $CPU_OPT" CC_AUX_FLAGS="$CC_AUX_FLAGS $CPU_OPT"
case "$NGX_GCC_VER" in case "$NGX_CC_VER" in
2.7*) 2.7*)
# batch build # batch build
CPU_OPT= CPU_OPT=
@@ -145,7 +137,7 @@ CFLAGS="$CFLAGS -Wall -Wpointer-arith"
#CFLAGS="$CFLAGS -Winline" #CFLAGS="$CFLAGS -Winline"
#CFLAGS="$CFLAGS -Wmissing-prototypes" #CFLAGS="$CFLAGS -Wmissing-prototypes"
case "$NGX_GCC_VER" in case "$NGX_CC_VER" in
2.*) 2.*)
# we have a lot of the unused function arguments # we have a lot of the unused function arguments
CFLAGS="$CFLAGS -Wno-unused" CFLAGS="$CFLAGS -Wno-unused"
+1 -9
View File
@@ -5,14 +5,6 @@
# Intel C++ compiler 7.1, 8.0, 8.1, 9.0, 11.1 # Intel C++ compiler 7.1, 8.0, 8.1, 9.0, 11.1
NGX_ICC_VER=`$CC -V 2>&1 | grep 'Version' 2>&1 \
| sed -e 's/^.* Version \([^ ]*\) *Build.*$/\1/'`
echo " + icc version: $NGX_ICC_VER"
have=NGX_COMPILER value="\"Intel C Compiler $NGX_ICC_VER\"" . auto/define
# optimizations # optimizations
CFLAGS="$CFLAGS -O" CFLAGS="$CFLAGS -O"
@@ -83,7 +75,7 @@ CFLAGS="$CFLAGS -wd1418"
# external declaration in primary source file # external declaration in primary source file
CFLAGS="$CFLAGS -wd1419" CFLAGS="$CFLAGS -wd1419"
case "$NGX_ICC_VER" in case "$NGX_CC_VER" in
9.*) 9.*)
# "cc" clobber ignored, warnings for Linux's htonl()/htons() # "cc" clobber ignored, warnings for Linux's htonl()/htons()
CFLAGS="$CFLAGS -wd1469" CFLAGS="$CFLAGS -wd1469"
+2 -10
View File
@@ -11,20 +11,12 @@
# MSVC 2015 (14.0) cl 19.00 # MSVC 2015 (14.0) cl 19.00
NGX_MSVC_VER=`$NGX_WINE $CC 2>&1 | grep 'C/C++.* [0-9][0-9]*\.[0-9]' 2>&1 \ ngx_msvc_ver=`echo $NGX_CC_VER | sed -e 's/^\([0-9]*\).*/\1/'`
| sed -e 's/^.* \([0-9][0-9]*\.[0-9].*\)/\1/'`
echo " + cl version: $NGX_MSVC_VER"
have=NGX_COMPILER value="\"cl $NGX_MSVC_VER\"" . auto/define
ngx_msvc_ver=`echo $NGX_MSVC_VER | sed -e 's/^\([0-9]*\).*/\1/'`
# detect x64 builds # detect x64 builds
case "$NGX_MSVC_VER" in case "$NGX_CC_VER" in
*ARM64) *ARM64)
NGX_MACHINE=arm64 NGX_MACHINE=arm64
+33
View File
@@ -28,6 +28,13 @@ if [ "$CC" = cl ]; then
NGX_CC_NAME=msvc NGX_CC_NAME=msvc
echo " + using Microsoft Visual C++ compiler" echo " + using Microsoft Visual C++ compiler"
NGX_CC_VER=`$NGX_WINE $CC 2>&1 \
| grep 'C/C++.* [0-9][0-9]*\.[0-9]' 2>&1 \
| sed -e 's/^.* \([0-9][0-9]*\.[0-9].*\)/\1/'`
echo " + cl version: $NGX_CC_VER"
have=NGX_COMPILER value="\"cl $NGX_CC_VER\"" . auto/define
elif [ "$CC" = wcl386 ]; then elif [ "$CC" = wcl386 ]; then
NGX_CC_NAME=owc NGX_CC_NAME=owc
echo " + using Open Watcom C compiler" echo " + using Open Watcom C compiler"
@@ -40,22 +47,48 @@ elif `$CC -V 2>&1 | grep '^Intel(R) C' >/dev/null 2>&1`; then
NGX_CC_NAME=icc NGX_CC_NAME=icc
echo " + using Intel C++ compiler" echo " + using Intel C++ compiler"
NGX_CC_VER=`$CC -V 2>&1 \
| sed -n -e 's/^.* Version \([^ ]*\) *Build.*$/\1/p'`
echo " + icc version: $NGX_CC_VER"
have=NGX_COMPILER value="\"Intel C Compiler $NGX_CC_VER\"" . auto/define
elif `$CC -v 2>&1 | grep 'gcc version' >/dev/null 2>&1`; then elif `$CC -v 2>&1 | grep 'gcc version' >/dev/null 2>&1`; then
NGX_CC_NAME=gcc NGX_CC_NAME=gcc
echo " + using GNU C compiler" echo " + using GNU C compiler"
NGX_CC_VER=`$CC -v 2>&1 | sed -n -e 's/^.*gcc version \(.*\)/\1/p'`
echo " + gcc version: $NGX_CC_VER"
have=NGX_COMPILER value="\"gcc $NGX_CC_VER\"" . auto/define
elif `$CC -v 2>&1 | grep 'clang version' >/dev/null 2>&1`; then elif `$CC -v 2>&1 | grep 'clang version' >/dev/null 2>&1`; then
NGX_CC_NAME=clang NGX_CC_NAME=clang
echo " + using Clang C compiler" echo " + using Clang C compiler"
NGX_CC_VER=`$CC -v 2>&1 | sed -n -e 's/^.*clang version \(.*\)/\1/p'`
echo " + clang version: $NGX_CC_VER"
have=NGX_COMPILER value="\"clang $NGX_CC_VER\"" . auto/define
elif `$CC -v 2>&1 | grep 'LLVM version' >/dev/null 2>&1`; then elif `$CC -v 2>&1 | grep 'LLVM version' >/dev/null 2>&1`; then
NGX_CC_NAME=clang NGX_CC_NAME=clang
echo " + using Clang C compiler" echo " + using Clang C compiler"
NGX_CC_VER=`$CC -v 2>&1 | sed -n -e 's/^.*LLVM version \(.*\)/\1/p'`
echo " + clang version: $NGX_CC_VER"
have=NGX_COMPILER value="\"clang $NGX_CC_VER\"" . auto/define
elif `$CC -V 2>&1 | grep 'Sun C' >/dev/null 2>&1`; then elif `$CC -V 2>&1 | grep 'Sun C' >/dev/null 2>&1`; then
NGX_CC_NAME=sunc NGX_CC_NAME=sunc
echo " + using Sun C compiler" echo " + using Sun C compiler"
NGX_CC_VER=`$CC -V 2>&1 | sed -n -e 's/^.* Sun C \(.*\)/\1/p'`
echo " + Sun C version: $NGX_CC_VER"
have=NGX_COMPILER value="\"Sun C $NGX_CC_VER\"" . auto/define
elif `$CC -V 2>&1 | grep '^Compaq C' >/dev/null 2>&1`; then elif `$CC -V 2>&1 | grep '^Compaq C' >/dev/null 2>&1`; then
NGX_CC_NAME=ccc NGX_CC_NAME=ccc
echo " + using Compaq C compiler" echo " + using Compaq C compiler"
-7
View File
@@ -13,13 +13,6 @@
# Sun C 5.13 SunOS_i386 2014/10/20 Oracle Solaris Studio 12.4 # Sun C 5.13 SunOS_i386 2014/10/20 Oracle Solaris Studio 12.4
# Sun C 5.14 SunOS_i386 2016/05/31 Oracle Developer Studio 12.5 # Sun C 5.14 SunOS_i386 2016/05/31 Oracle Developer Studio 12.5
NGX_SUNC_VER=`$CC -V 2>&1 | grep 'Sun C' 2>&1 \
| sed -e 's/^.* Sun C \(.*\)/\1/'`
echo " + Sun C version: $NGX_SUNC_VER"
have=NGX_COMPILER value="\"Sun C $NGX_SUNC_VER\"" . auto/define
cat << END > $NGX_AUTOTEST.c cat << END > $NGX_AUTOTEST.c
+1
View File
@@ -37,6 +37,7 @@ if [ $PCRE_LIBRARY = PCRE2 ]; then
pcre2_xclass.c" pcre2_xclass.c"
ngx_pcre_test="pcre2_chkdint.c \ ngx_pcre_test="pcre2_chkdint.c \
pcre2_compile_cgroup.c \
pcre2_compile_class.c \ pcre2_compile_class.c \
pcre2_convert.c \ pcre2_convert.c \
pcre2_extuni.c \ pcre2_extuni.c \
+12 -1
View File
@@ -733,7 +733,7 @@ if [ $HTTP = YES ]; then
ngx_module_name=ngx_http_proxy_module ngx_module_name=ngx_http_proxy_module
ngx_module_incs= ngx_module_incs=
ngx_module_deps= ngx_module_deps=src/http/modules/ngx_http_proxy_module.h
ngx_module_srcs=src/http/modules/ngx_http_proxy_module.c ngx_module_srcs=src/http/modules/ngx_http_proxy_module.c
ngx_module_libs= ngx_module_libs=
ngx_module_link=$HTTP_PROXY ngx_module_link=$HTTP_PROXY
@@ -785,6 +785,17 @@ if [ $HTTP = YES ]; then
. auto/module . auto/module
fi fi
if [ $HTTP_PROXY = YES -a $HTTP_V2 = YES ]; then
ngx_module_name=ngx_http_proxy_v2_module
ngx_module_incs=
ngx_module_deps=
ngx_module_srcs=src/http/modules/ngx_http_proxy_v2_module.c
ngx_module_libs=
ngx_module_link=$HTTP_V2
. auto/module
fi
if [ $HTTP_PERL != NO ]; then if [ $HTTP_PERL != NO ]; then
ngx_module_name=ngx_http_perl_module ngx_module_name=ngx_http_perl_module
ngx_module_incs=src/http/modules/perl ngx_module_incs=src/http/modules/perl
+232 -2
View File
@@ -5,6 +5,236 @@
<change_log title="nginx"> <change_log title="nginx">
<changes ver="1.29.4" date="2025-12-09">
<change type="feature">
<para lang="ru">
модуль ngx_http_proxy_module поддерживает HTTP/2.
</para>
<para lang="en">
the ngx_http_proxy_module supports HTTP/2.
</para>
</change>
<change type="feature">
<para lang="ru">
поддержка расширения TLS Encrypted ClientHello
при использовании ветки разработки ECH OpenSSL;
директива ssl_ech_file.<br/>
Спасибо Stephen Farrell.
</para>
<para lang="en">
Encrypted ClientHello TLS extension support
when using OpenSSL ECH feature branch;
the "ssl_ech_file" directive.<br/>
Thanks to Stephen Farrell.
</para>
</change>
<change type="change">
<para lang="ru">
валидация хоста и порта в строке запроса,
в заголовке "Host" и псевдо-заголовке ":authority"
изменена на соответствующую RFC 3986.
</para>
<para lang="en">
validation of host and port in the request line,
"Host" header field, and ":authority" pseudo-header field
has been changed to follow RFC 3986.
</para>
</change>
<change type="change">
<para lang="ru">
теперь одиночный символ LF, используемый для перевода строки
в chunked-теле запроса или ответа, считается ошибкой.
</para>
<para lang="en">
now a single LF used as a line terminator
in a chunked request or response body is considered an error.
</para>
</change>
<change type="bugfix">
<para lang="ru">
при использовании HTTP/3 с OpenSSL 3.5.1 и новее
в рабочем процессе мог произойти segmentation fault;
ошибка появилась в 1.29.1.<br/>
Спасибо Jan Svojanovsky.
</para>
<para lang="en">
when using HTTP/3 with OpenSSL 3.5.1 or newer
a segmentation fault might occur in a worker process;
the bug had appeared in 1.29.1.<br/>
Thanks to Jan Svojanovsky.
</para>
</change>
<change type="bugfix">
<para lang="ru">
при совместном использовании директив try_files и proxy_pass с URI
в рабочем процессе мог произойти segmentation fault.
</para>
<para lang="en">
a segmentation fault might occur in a worker process
if the "try_files" directive and "proxy_pass" with a URI were used.
</para>
</change>
</changes>
<changes ver="1.29.3" date="2025-10-28">
<change type="feature">
<para lang="ru">
директивы add_header_inherit и add_trailer_inherit.
</para>
<para lang="en">
the "add_header_inherit" and "add_trailer_inherit" directives.
</para>
</change>
<change type="feature">
<para lang="ru">
переменные $request_port и $is_request_port.
</para>
<para lang="en">
the $request_port and $is_request_port variables.
</para>
</change>
<change type="feature">
<para lang="ru">
переменные $ssl_sigalg и $ssl_client_sigalg.
</para>
<para lang="en">
the $ssl_sigalg and $ssl_client_sigalg variables.
</para>
</change>
<change type="feature">
<para lang="ru">
параметр volatile директивы geo.
</para>
<para lang="en">
the "volatile" parameter of the "geo" directive.
</para>
</change>
<change type="feature">
<para lang="ru">
теперь сжатие сертификатов доступно с BoringSSL.
</para>
<para lang="en">
now certificate compression is available with BoringSSL.
</para>
</change>
<change type="bugfix">
<para lang="ru">
теперь сжатие сертификатов запрещено при включенном OCSP stapling.
</para>
<para lang="en">
now certificate compression is disabled with OCSP stapling.
</para>
</change>
</changes>
<changes ver="1.29.2" date="2025-10-07">
<change type="feature">
<para lang="ru">
теперь nginx можно собрать с AWS-LC.<br/>
Спасибо Samuel Chiang.
</para>
<para lang="en">
now nginx can be built with AWS-LC.<br/>
Thanks Samuel Chiang.
</para>
</change>
<change type="bugfix">
<para lang="ru">
теперь директива ssl_protocols работает
в виртуальном сервере, отличном от сервера по умолчанию,
при использовании OpenSSL 1.1.1 и новее.
</para>
<para lang="en">
now the "ssl_protocols" directive works
in a virtual server different from the default server
when using OpenSSL 1.1.1 or newer.
</para>
</change>
<change type="bugfix">
<para lang="ru">
при использовании TLSv1.3 с OpenSSL и клиентских сертификатов
SSL handshake всегда завершался ошибкой
при восстановлении сессии с другим значением SNI;
ошибка появилась в 1.27.4.
</para>
<para lang="en">
SSL handshake always failed
when using TLSv1.3 with OpenSSL and client certificates
and resuming a session with a different SNI value;
the bug had appeared in 1.27.4.
</para>
</change>
<change type="bugfix">
<para lang="ru">
при использовании QUIC и директивы ssl_reject_handshake
в логах могли появляться сообщения
"ignoring stale global SSL error";
ошибка появилась в 1.29.0.<br/>
Спасибо Владимиру Хомутову.
</para>
<para lang="en">
the "ignoring stale global SSL error"
alerts might appear in logs
when using QUIC and the "ssl_reject_handshake" directive;
the bug had appeared in 1.29.0.<br/>
Thanks to Vladimir Homutov.
</para>
</change>
<change type="bugfix">
<para lang="ru">
в обработке delta-seconds
в строке "Cache-Control" в заголовке ответа бэкенда.
</para>
<para lang="en">
in delta-seconds processing
in the "Cache-Control" backend response header line.
</para>
</change>
<change type="bugfix">
<para lang="ru">
команда XCLIENT не использовала кодировку xtext.<br/>
Спасибо Igor Morgenstern из Aisle Research.
</para>
<para lang="en">
an XCLIENT command didn't use the xtext encoding.<br/>
Thanks to Igor Morgenstern of Aisle Research.
</para>
</change>
<change type="bugfix">
<para lang="ru">
в кешировании SSL-сертификатов во время переконфигурации.
</para>
<para lang="en">
in SSL certificate caching during reconfiguration.
</para>
</change>
</changes>
<changes ver="1.29.1" date="2025-08-13"> <changes ver="1.29.1" date="2025-08-13">
<change type="security"> <change type="security">
@@ -8736,7 +8966,7 @@ Thanks to Piotr Sikora.
Спасибо Piotr Sikora. Спасибо Piotr Sikora.
</para> </para>
<para lang="en"> <para lang="en">
now nginx can be build with BoringSSL and LibreSSL.<br/> now nginx can be built with BoringSSL and LibreSSL.<br/>
Thanks to Piotr Sikora. Thanks to Piotr Sikora.
</para> </para>
</change> </change>
@@ -26019,7 +26249,7 @@ the ECONNABORTED error log level was changed to "error" from "crit".
модуль ngx_http_perl_module не собирался без модуля ngx_http_ssi_filter_module. модуль ngx_http_perl_module не собирался без модуля ngx_http_ssi_filter_module.
</para> </para>
<para lang="en"> <para lang="en">
the ngx_http_perl_module could not be build without the ngx_http_perl_module could not be built without
the ngx_http_ssi_filter_module. the ngx_http_ssi_filter_module.
</para> </para>
</change> </change>
+2 -2
View File
@@ -6,9 +6,9 @@ TEMP = tmp
CC = cl CC = cl
OBJS = objs.msvc8 OBJS = objs.msvc8
OPENSSL = openssl-3.5.2 OPENSSL = openssl-3.5.4
ZLIB = zlib-1.3.1 ZLIB = zlib-1.3.1
PCRE = pcre2-10.45 PCRE = pcre2-10.46
release: export release: export
+1 -1
View File
@@ -3,7 +3,7 @@ make -f misc/GNUmakefile release
the required tools: the required tools:
*) xsltproc to build CHANGES, *) xsltproc to build CHANGES,
*) xslscript.pl ( http://hg.nginx.org/xslscript ) to build XSLTs *) xslscript.pl ( https://github.com/nginx/xslscript ) to build XSLTs
from XSLScript sources. from XSLScript sources.
+2 -2
View File
@@ -9,8 +9,8 @@
#define _NGINX_H_INCLUDED_ #define _NGINX_H_INCLUDED_
#define nginx_version 1029002 #define nginx_version 1029005
#define NGINX_VERSION "1.29.2" #define NGINX_VERSION "1.29.5"
#define NGINX_VER "nginx/" NGINX_VERSION " by Hakase" #define NGINX_VER "nginx/" NGINX_VERSION " by Hakase"
#ifndef NGINX_SERVER #ifndef NGINX_SERVER
+29 -3
View File
@@ -1494,8 +1494,9 @@ ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len)
uintptr_t uintptr_t
ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type) ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type)
{ {
ngx_uint_t n; u_char prefix;
uint32_t *escape; uint32_t *escape;
ngx_uint_t n;
static u_char hex[] = "0123456789ABCDEF"; static u_char hex[] = "0123456789ABCDEF";
/* /*
@@ -1633,11 +1634,36 @@ ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type)
/* mail_auth is the same as memcached */ /* mail_auth is the same as memcached */
/* " ", "+", "=", not allowed */
static uint32_t mail_xtext[] = {
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
/* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
0x20000801, /* 0010 0000 0000 0000 0000 1000 0000 0001 */
/* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
/* ~}| {zyx wvut srqp onml kjih gfed cba` */
0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
};
static uint32_t *map[] = static uint32_t *map[] =
{ uri, args, uri_component, html, refresh, memcached, memcached }; { uri, args, uri_component, html, refresh, memcached, memcached,
mail_xtext };
static u_char map_char[] =
{ '%', '%', '%', '%', '%', '%', '%', '+' };
escape = map[type]; escape = map[type];
prefix = map_char[type];
if (dst == NULL) { if (dst == NULL) {
@@ -1658,7 +1684,7 @@ ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type)
while (size) { while (size) {
if (escape[*src >> 5] & (1U << (*src & 0x1f))) { if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
*dst++ = '%'; *dst++ = prefix;
*dst++ = hex[*src >> 4]; *dst++ = hex[*src >> 4];
*dst++ = hex[*src & 0xf]; *dst++ = hex[*src & 0xf];
src++; src++;
+1
View File
@@ -203,6 +203,7 @@ u_char *ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len);
#define NGX_ESCAPE_REFRESH 4 #define NGX_ESCAPE_REFRESH 4
#define NGX_ESCAPE_MEMCACHED 5 #define NGX_ESCAPE_MEMCACHED 5
#define NGX_ESCAPE_MAIL_AUTH 6 #define NGX_ESCAPE_MAIL_AUTH 6
#define NGX_ESCAPE_MAIL_XTEXT 7
#define NGX_UNESCAPE_URI 1 #define NGX_UNESCAPE_URI 1
#define NGX_UNESCAPE_REDIRECT 2 #define NGX_UNESCAPE_REDIRECT 2
+597 -10
View File
@@ -9,6 +9,10 @@
#include <ngx_core.h> #include <ngx_core.h>
#include <ngx_event.h> #include <ngx_event.h>
#if (NGX_ZLIB && defined TLSEXT_cert_compression_zlib)
#include <zlib.h>
#endif
#define NGX_SSL_PASSWORD_BUFFER_SIZE 4096 #define NGX_SSL_PASSWORD_BUFFER_SIZE 4096
@@ -19,6 +23,13 @@ typedef struct {
static ngx_inline ngx_int_t ngx_ssl_cert_already_in_hash(void); static ngx_inline ngx_int_t ngx_ssl_cert_already_in_hash(void);
#if (NGX_ZLIB && defined TLSEXT_cert_compression_zlib)
static int ngx_ssl_cert_compression_callback(ngx_ssl_conn_t *ssl_conn,
CBB *out, const uint8_t *in, size_t in_len);
static void *ngx_ssl_cert_compression_alloc(void *opaque, u_int items,
u_int size);
static void ngx_ssl_cert_compression_free(void *opaque, void *address);
#endif
static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store); static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store);
static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where,
int ret); int ret);
@@ -128,6 +139,8 @@ int ngx_ssl_ticket_keys_index;
int ngx_ssl_ocsp_index; int ngx_ssl_ocsp_index;
int ngx_ssl_index; int ngx_ssl_index;
int ngx_ssl_certificate_name_index; int ngx_ssl_certificate_name_index;
int ngx_ssl_certificate_comp_index;
int ngx_ssl_client_hello_arg_index;
u_char ngx_ssl_session_buffer[NGX_SSL_MAX_SESSION_SIZE]; u_char ngx_ssl_session_buffer[NGX_SSL_MAX_SESSION_SIZE];
@@ -270,6 +283,21 @@ ngx_ssl_init(ngx_log_t *log)
return NGX_ERROR; return NGX_ERROR;
} }
ngx_ssl_certificate_comp_index = X509_get_ex_new_index(0, NULL, NULL, NULL,
NULL);
if (ngx_ssl_certificate_comp_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed");
return NGX_ERROR;
}
ngx_ssl_client_hello_arg_index = SSL_CTX_get_ex_new_index(0, NULL, NULL,
NULL, NULL);
if (ngx_ssl_client_hello_arg_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0,
"SSL_CTX_get_ex_new_index() failed");
return NGX_ERROR;
}
return NGX_OK; return NGX_OK;
} }
@@ -449,10 +477,18 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
{ {
char *err; char *err;
X509 *x509, **elm; X509 *x509, **elm;
u_long n;
EVP_PKEY *pkey; EVP_PKEY *pkey;
ngx_uint_t mask;
STACK_OF(X509) *chain; STACK_OF(X509) *chain;
chain = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_CERT, &err, cert, NULL); mask = 0;
elm = NULL;
retry:
chain = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_CERT | mask,
&err, cert, NULL);
if (chain == NULL) { if (chain == NULL) {
if (err != NULL) { if (err != NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
@@ -492,11 +528,16 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
} }
} }
elm = ngx_array_push(&ssl->certs);
if (elm == NULL) { if (elm == NULL) {
X509_free(x509); elm = ngx_array_push(&ssl->certs);
sk_X509_pop_free(chain, X509_free); if (elm == NULL) {
return NGX_ERROR; X509_free(x509);
sk_X509_pop_free(chain, X509_free);
return NGX_ERROR;
}
} else {
X509_free(*elm);
} }
*elm = x509; *elm = x509;
@@ -519,11 +560,21 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
} }
#else #else
{
int n;
/* SSL_CTX_set0_chain() is only available in OpenSSL 1.0.2+ */ /* SSL_CTX_set0_chain() is only available in OpenSSL 1.0.2+ */
#ifdef SSL_CTRL_CLEAR_EXTRA_CHAIN_CERTS
/* OpenSSL 1.0.1+ */
SSL_CTX_clear_extra_chain_certs(ssl->ctx);
#else
if (ssl->ctx->extra_certs) {
sk_X509_pop_free(ssl->ctx->extra_certs, X509_free);
ssl->ctx->extra_certs = NULL;
}
#endif
n = sk_X509_num(chain); n = sk_X509_num(chain);
while (n--) { while (n--) {
@@ -539,10 +590,11 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
} }
sk_X509_free(chain); sk_X509_free(chain);
}
#endif #endif
pkey = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_PKEY, &err, key, passwords); pkey = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_PKEY | mask,
&err, key, passwords);
if (pkey == NULL) { if (pkey == NULL) {
if (err != NULL) { if (err != NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
@@ -554,9 +606,23 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
} }
if (SSL_CTX_use_PrivateKey(ssl->ctx, pkey) == 0) { if (SSL_CTX_use_PrivateKey(ssl->ctx, pkey) == 0) {
EVP_PKEY_free(pkey);
/* there can be mismatched pairs on uneven cache update */
n = ERR_peek_last_error();
if (ERR_GET_LIB(n) == ERR_LIB_X509
&& ERR_GET_REASON(n) == X509_R_KEY_VALUES_MISMATCH
&& mask == 0)
{
ERR_clear_error();
mask = NGX_SSL_CACHE_INVALIDATE;
goto retry;
}
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_use_PrivateKey(\"%s\") failed", key->data); "SSL_CTX_use_PrivateKey(\"%s\") failed", key->data);
EVP_PKEY_free(pkey);
return NGX_ERROR; return NGX_ERROR;
} }
@@ -682,6 +748,18 @@ ngx_ssl_certificate_compression(ngx_conf_t *cf, ngx_ssl_t *ssl,
SSL_CTX_clear_options(ssl->ctx, SSL_OP_NO_TX_CERTIFICATE_COMPRESSION); SSL_CTX_clear_options(ssl->ctx, SSL_OP_NO_TX_CERTIFICATE_COMPRESSION);
#elif (NGX_ZLIB && defined TLSEXT_cert_compression_zlib)
if (SSL_CTX_add_cert_compression_alg(ssl->ctx, TLSEXT_cert_compression_zlib,
ngx_ssl_cert_compression_callback,
NULL)
== 0)
{
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_add_cert_compression_alg() failed");
return NGX_ERROR;
}
#else #else
ngx_log_error(NGX_LOG_WARN, ssl->log, 0, ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
@@ -694,6 +772,155 @@ ngx_ssl_certificate_compression(ngx_conf_t *cf, ngx_ssl_t *ssl,
} }
#if (NGX_ZLIB && defined TLSEXT_cert_compression_zlib)
static int
ngx_ssl_cert_compression_callback(ngx_ssl_conn_t *ssl_conn, CBB *out,
const uint8_t *in, size_t in_len)
{
int rc;
X509 *cert;
u_char *p;
z_stream zstream;
ngx_str_t *comp, tmp;
ngx_pool_t *pool;
ngx_connection_t *c;
#ifdef OPENSSL_IS_BORINGSSL
{
SSL_CTX *ssl_ctx;
ngx_ssl_t *ssl;
/* BoringSSL doesn't have certificate slots, we take the last set */
ssl_ctx = SSL_get_SSL_CTX(ssl_conn);
ssl = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_index);
cert = ((X509 **) ssl->certs.elts)[ssl->certs.nelts - 1];
}
#else
/*
* AWS-LC saves leaf certificate in SSL to associate with SSL_CTX,
* see https://github.com/aws/aws-lc/commit/e1ba2b3e5
*/
cert = SSL_get_certificate(ssl_conn);
#endif
comp = X509_get_ex_data(cert, ngx_ssl_certificate_comp_index);
if (comp != NULL) {
return CBB_add_bytes(out, comp->data, comp->len);
}
c = ngx_ssl_get_connection(ssl_conn);
pool = ngx_create_pool(256, c->log);
if (pool == NULL) {
return 0;
}
pool->log = c->log;
ngx_memzero(&zstream, sizeof(z_stream));
zstream.zalloc = ngx_ssl_cert_compression_alloc;
zstream.zfree = ngx_ssl_cert_compression_free;
zstream.opaque = pool;
rc = deflateInit(&zstream, Z_DEFAULT_COMPRESSION);
if (rc != Z_OK) {
ngx_log_error(NGX_LOG_ALERT, c->log, 0, "deflateInit() failed: %d", rc);
goto error;
}
tmp.len = deflateBound(&zstream, in_len);
tmp.data = ngx_palloc(pool, tmp.len);
if (tmp.data == NULL) {
goto error;
}
zstream.next_in = (u_char *) in;
zstream.avail_in = in_len;
zstream.next_out = tmp.data;
zstream.avail_out = tmp.len;
rc = deflate(&zstream, Z_FINISH);
if (rc != Z_STREAM_END) {
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"deflate(Z_FINISH) failed: %d", rc);
goto error;
}
tmp.len -= zstream.avail_out;
rc = deflateEnd(&zstream);
if (rc != Z_OK) {
ngx_log_error(NGX_LOG_ALERT, c->log, 0, "deflateEnd() failed: %d", rc);
goto error;
}
p = ngx_alloc(sizeof(ngx_str_t) + tmp.len, c->log);
if (p == NULL) {
goto error;
}
comp = (ngx_str_t *) p;
comp->len = tmp.len;
comp->data = p + sizeof(ngx_str_t);
ngx_memcpy(comp->data, tmp.data, tmp.len);
if (X509_set_ex_data(cert, ngx_ssl_certificate_comp_index, p) == 0) {
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "X509_set_ex_data() failed");
ngx_free(p);
}
rc = CBB_add_bytes(out, tmp.data, tmp.len);
ngx_destroy_pool(pool);
return rc;
error:
ngx_destroy_pool(pool);
return 0;
}
static void *
ngx_ssl_cert_compression_alloc(void *opaque, u_int items, u_int size)
{
ngx_pool_t *pool = opaque;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pool->log, 0,
"cert compression alloc: n:%ud s:%ud", items, size);
return ngx_palloc(pool, items * size);
}
static void
ngx_ssl_cert_compression_free(void *opaque, void *address)
{
#if 0
ngx_pool_t *pool = opaque;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pool->log, 0,
"cert compression free: %p", address);
#endif
}
#endif
ngx_int_t ngx_int_t
ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers, ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers,
ngx_uint_t prefer_server_ciphers) ngx_uint_t prefer_server_ciphers)
@@ -1426,6 +1653,105 @@ ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
} }
ngx_int_t
ngx_ssl_ech_files(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *filenames)
{
#ifdef SSL_OP_ECH_GREASE
int numkeys;
BIO *in;
ngx_int_t rc;
ngx_str_t *filename;
ngx_uint_t i;
OSSL_ECHSTORE *es;
if (filenames == NULL) {
return NGX_OK;
}
es = OSSL_ECHSTORE_new(NULL, NULL);
if (es == NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "OSSL_ECHSTORE_new() failed");
return NGX_ERROR;
}
rc = NGX_ERROR;
filename = filenames->elts;
for (i = 0; i < filenames->nelts; i++) {
if (ngx_conf_full_name(cf->cycle, &filename[i], 1) != NGX_OK) {
goto cleanup;
}
in = BIO_new_file((char *) filename[i].data, "r");
if (in == NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"BIO_new_file(\"%s\") failed", filename[i].data);
goto cleanup;
}
/*
* We only set the ECHConfigList from the first file read to use
* in ECH retry-configs.
*
* That allows many sensible key rotation schemes so that the
* values sent in ECH retry-configs are smaller and current.
* For example, if the first file name has the current ECH
* private key, and a second one has the previously used key
* that some clients may still use due to DNS caching.
*/
if (OSSL_ECHSTORE_read_pem(es, in, i ? OSSL_ECH_NO_RETRY
: OSSL_ECH_FOR_RETRY)
!= 1)
{
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"OSSL_ECHSTORE_read_pem(%s) failed",
filename[i].data);
BIO_free(in);
goto cleanup;
}
BIO_free(in);
}
/*
* load the ECH store after checking there's at least one ECH
* private key in there (the PEM file spec allows zero or one
* private key per file)
*/
if (OSSL_ECHSTORE_num_keys(es, &numkeys) != 1) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"OSSL_ECHSTORE_num_keys(%s) failed");
goto cleanup;
}
if (numkeys > 0 && SSL_CTX_set1_echstore(ssl->ctx, es) != 1) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_set1_echstore() failed");
goto cleanup;
}
rc = NGX_OK;
cleanup:
OSSL_ECHSTORE_free(es);
return rc;
#else
if (filenames != NULL) {
ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
"\"ssl_ech_file\" is not supported on this platform, "
"ignored");
}
return NGX_OK;
#endif
}
ngx_int_t ngx_int_t
ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name) ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name)
{ {
@@ -1645,6 +1971,131 @@ ngx_ssl_new_client_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
} }
ngx_int_t
ngx_ssl_set_client_hello_callback(ngx_ssl_t *ssl, ngx_ssl_client_hello_arg *cb)
{
#ifdef SSL_CLIENT_HELLO_SUCCESS
SSL_CTX_set_client_hello_cb(ssl->ctx, ngx_ssl_client_hello_callback, NULL);
if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_client_hello_arg_index, cb) == 0)
{
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_set_ex_data() failed");
return NGX_ERROR;
}
#elif defined OPENSSL_IS_BORINGSSL
SSL_CTX_set_select_certificate_cb(ssl->ctx, ngx_ssl_select_certificate);
if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_client_hello_arg_index, cb) == 0)
{
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_set_ex_data() failed");
return NGX_ERROR;
}
#endif
return NGX_OK;
}
#ifdef SSL_CLIENT_HELLO_SUCCESS
int
ngx_ssl_client_hello_callback(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
{
u_char *p;
size_t len;
ngx_int_t rc;
ngx_str_t host;
ngx_connection_t *c;
ngx_ssl_client_hello_arg *cb;
c = ngx_ssl_get_connection(ssl_conn);
cb = SSL_CTX_get_ex_data(c->ssl->session_ctx,
ngx_ssl_client_hello_arg_index);
if (SSL_client_hello_get0_ext(ssl_conn, TLSEXT_TYPE_server_name,
(const unsigned char **) &p, &len)
== 0)
{
ngx_str_null(&host);
goto done;
}
/*
* RFC 6066 mandates non-zero HostName length, we follow OpenSSL.
* No more than one ServerName is expected.
*/
if (len < 5
|| (size_t) (p[0] << 8) + p[1] + 2 != len
|| p[2] != TLSEXT_NAMETYPE_host_name
|| (size_t) (p[3] << 8) + p[4] + 2 + 3 != len)
{
*ad = SSL_AD_DECODE_ERROR;
return SSL_CLIENT_HELLO_ERROR;
}
len -= 5;
p += 5;
if (len > TLSEXT_MAXLEN_host_name || ngx_strlchr(p, p + len, '\0')) {
*ad = SSL_AD_UNRECOGNIZED_NAME;
return SSL_CLIENT_HELLO_ERROR;
}
host.len = len;
host.data = p;
done:
rc = cb->servername(ssl_conn, ad, &host);
if (rc == SSL_TLSEXT_ERR_ALERT_FATAL) {
return SSL_CLIENT_HELLO_ERROR;
}
return SSL_CLIENT_HELLO_SUCCESS;
}
#elif defined OPENSSL_IS_BORINGSSL
enum ssl_select_cert_result_t ngx_ssl_select_certificate(
const SSL_CLIENT_HELLO *client_hello)
{
int ad;
ngx_int_t rc;
ngx_ssl_conn_t *ssl_conn;
ngx_connection_t *c;
ngx_ssl_client_hello_arg *cb;
ssl_conn = client_hello->ssl;
c = ngx_ssl_get_connection(ssl_conn);
cb = SSL_CTX_get_ex_data(c->ssl->session_ctx,
ngx_ssl_client_hello_arg_index);
/*
* BoringSSL sends a hardcoded "handshake_failure" alert on errors,
* we use it to map SSL_AD_INTERNAL_ERROR. To preserve other alert
* values, error handling is postponed to the servername callback.
*/
rc = cb->servername(ssl_conn, &ad, NULL);
if (rc == SSL_TLSEXT_ERR_ALERT_FATAL && ad == SSL_AD_INTERNAL_ERROR) {
return ssl_select_cert_error;
}
return ssl_select_cert_success;
}
#endif
ngx_int_t ngx_int_t
ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags) ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags)
{ {
@@ -4831,10 +5282,19 @@ ngx_ssl_cleanup_ctx(void *data)
ngx_ssl_t *ssl = data; ngx_ssl_t *ssl = data;
X509 *cert; X509 *cert;
u_char *p;
ngx_uint_t i; ngx_uint_t i;
for (i = 0; i < ssl->certs.nelts; i++) { for (i = 0; i < ssl->certs.nelts; i++) {
cert = ((X509 **) ssl->certs.elts)[i]; cert = ((X509 **) ssl->certs.elts)[i];
p = X509_get_ex_data(cert, ngx_ssl_certificate_comp_index);
if (p) {
ngx_free(p);
X509_set_ex_data(cert, ngx_ssl_certificate_comp_index, NULL);
}
X509_free(cert); X509_free(cert);
} }
@@ -5231,6 +5691,32 @@ ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
} }
ngx_int_t
ngx_ssl_get_sigalg(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
#ifdef SSL_get0_signature_name
const char *name;
if (SSL_get0_signature_name(c->ssl->connection, &name)) {
s->len = ngx_strlen(name);
s->data = ngx_pnalloc(pool, s->len);
if (s->data == NULL) {
return NGX_ERROR;
}
ngx_memcpy(s->data, name, s->len);
return NGX_OK;
}
#endif
s->len = 0;
return NGX_OK;
}
ngx_int_t ngx_int_t
ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{ {
@@ -5360,6 +5846,81 @@ ngx_ssl_get_alpn_protocol(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
} }
ngx_int_t
ngx_ssl_get_ech_status(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
#ifdef SSL_OP_ECH_GREASE
int echrv;
char *inner_sni, *outer_sni;
inner_sni = NULL;
outer_sni = NULL;
echrv = SSL_ech_get1_status(c->ssl->connection, &inner_sni, &outer_sni);
switch (echrv) {
case SSL_ECH_STATUS_NOT_TRIED:
ngx_str_set(s, "NOT_TRIED");
break;
case SSL_ECH_STATUS_SUCCESS:
ngx_str_set(s, "SUCCESS");
break;
case SSL_ECH_STATUS_GREASE:
ngx_str_set(s, "GREASE");
break;
case SSL_ECH_STATUS_BACKEND:
ngx_str_set(s, "BACKEND");
break;
default:
ngx_str_set(s, "FAILED");
break;
}
OPENSSL_free(inner_sni);
OPENSSL_free(outer_sni);
#else
s->len = 0;
#endif
return NGX_OK;
}
ngx_int_t
ngx_ssl_get_ech_outer_server_name(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s)
{
#if defined(SSL_OP_ECH_GREASE)
int echrv;
char *inner_sni, *outer_sni;
inner_sni = NULL;
outer_sni = NULL;
echrv = SSL_ech_get1_status(c->ssl->connection, &inner_sni, &outer_sni);
if (echrv == SSL_ECH_STATUS_SUCCESS && outer_sni) {
s->len = ngx_strlen(outer_sni);
s->data = ngx_pnalloc(pool, s->len);
if (s->data == NULL) {
return NGX_ERROR;
}
ngx_memcpy(s->data, outer_sni, s->len);
} else {
s->len = 0;
}
OPENSSL_free(inner_sni);
OPENSSL_free(outer_sni);
#else
s->len = 0;
#endif
return NGX_OK;
}
ngx_int_t ngx_int_t
ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{ {
@@ -5976,6 +6537,32 @@ ngx_ssl_parse_time(
} }
ngx_int_t
ngx_ssl_get_client_sigalg(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
#ifdef SSL_get0_peer_signature_name
const char *name;
if (SSL_get0_peer_signature_name(c->ssl->connection, &name)) {
s->len = ngx_strlen(name);
s->data = ngx_pnalloc(pool, s->len);
if (s->data == NULL) {
return NGX_ERROR;
}
ngx_memcpy(s->data, name, s->len);
return NGX_OK;
}
#endif
s->len = 0;
return NGX_OK;
}
static void * static void *
ngx_openssl_create_conf(ngx_cycle_t *cycle) ngx_openssl_create_conf(ngx_cycle_t *cycle)
{ {
+29 -8
View File
@@ -26,14 +26,6 @@
#include <openssl/engine.h> #include <openssl/engine.h>
#endif #endif
#include <openssl/evp.h> #include <openssl/evp.h>
#if (NGX_QUIC)
#ifdef OPENSSL_IS_BORINGSSL
#include <openssl/hkdf.h>
#include <openssl/chacha.h>
#else
#include <openssl/kdf.h>
#endif
#endif
#include <openssl/hmac.h> #include <openssl/hmac.h>
#ifndef OPENSSL_NO_OCSP #ifndef OPENSSL_NO_OCSP
#include <openssl/ocsp.h> #include <openssl/ocsp.h>
@@ -161,6 +153,7 @@ struct ngx_ssl_connection_s {
unsigned in_ocsp:1; unsigned in_ocsp:1;
unsigned early_preread:1; unsigned early_preread:1;
unsigned write_blocked:1; unsigned write_blocked:1;
unsigned sni_accepted:1;
ngx_ssl_dyn_rec_t dyn_rec; ngx_ssl_dyn_rec_t dyn_rec;
ngx_msec_t dyn_rec_last_write; ngx_msec_t dyn_rec_last_write;
@@ -211,6 +204,13 @@ typedef struct {
} ngx_ssl_session_cache_t; } ngx_ssl_session_cache_t;
typedef int (*ngx_ssl_servername_pt)(ngx_ssl_conn_t *, int *, void *);
typedef struct {
ngx_ssl_servername_pt servername;
} ngx_ssl_client_hello_arg;
#define NGX_SSL_SSLv2 0x0002 #define NGX_SSL_SSLv2 0x0002
#define NGX_SSL_SSLv3 0x0004 #define NGX_SSL_SSLv3 0x0004
#define NGX_SSL_TLSv1 0x0008 #define NGX_SSL_TLSv1 0x0008
@@ -285,6 +285,8 @@ ngx_array_t *ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file);
ngx_array_t *ngx_ssl_preserve_passwords(ngx_conf_t *cf, ngx_array_t *ngx_ssl_preserve_passwords(ngx_conf_t *cf,
ngx_array_t *passwords); ngx_array_t *passwords);
ngx_int_t ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file); ngx_int_t ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file);
ngx_int_t ngx_ssl_ech_files(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_array_t *filename);
ngx_int_t ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name); ngx_int_t ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name);
ngx_int_t ngx_ssl_early_data(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_int_t ngx_ssl_early_data(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_uint_t enable); ngx_uint_t enable);
@@ -300,6 +302,15 @@ ngx_int_t ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_array_t *paths); ngx_array_t *paths);
ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data); ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data);
ngx_int_t ngx_ssl_set_client_hello_callback(ngx_ssl_t *ssl,
ngx_ssl_client_hello_arg *cb);
#ifdef SSL_CLIENT_HELLO_SUCCESS
int ngx_ssl_client_hello_callback(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg);
#elif defined OPENSSL_IS_BORINGSSL
enum ssl_select_cert_result_t ngx_ssl_select_certificate(
const SSL_CLIENT_HELLO *client_hello);
#endif
ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c,
ngx_uint_t flags); ngx_uint_t flags);
@@ -333,6 +344,8 @@ ngx_int_t ngx_ssl_get_curve(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s); ngx_str_t *s);
ngx_int_t ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_int_t ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s); ngx_str_t *s);
ngx_int_t ngx_ssl_get_sigalg(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool, ngx_int_t ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s); ngx_str_t *s);
ngx_int_t ngx_ssl_get_session_reused(ngx_connection_t *c, ngx_pool_t *pool, ngx_int_t ngx_ssl_get_session_reused(ngx_connection_t *c, ngx_pool_t *pool,
@@ -341,6 +354,10 @@ ngx_int_t ngx_ssl_get_early_data(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s); ngx_str_t *s);
ngx_int_t ngx_ssl_get_server_name(ngx_connection_t *c, ngx_pool_t *pool, ngx_int_t ngx_ssl_get_server_name(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s); ngx_str_t *s);
ngx_int_t ngx_ssl_get_ech_status(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_get_ech_outer_server_name(ngx_connection_t *c,
ngx_pool_t *pool, ngx_str_t *s);
ngx_int_t ngx_ssl_get_alpn_protocol(ngx_connection_t *c, ngx_pool_t *pool, ngx_int_t ngx_ssl_get_alpn_protocol(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s); ngx_str_t *s);
ngx_int_t ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_int_t ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool,
@@ -369,6 +386,8 @@ ngx_int_t ngx_ssl_get_client_v_end(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s); ngx_str_t *s);
ngx_int_t ngx_ssl_get_client_v_remain(ngx_connection_t *c, ngx_pool_t *pool, ngx_int_t ngx_ssl_get_client_v_remain(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s); ngx_str_t *s);
ngx_int_t ngx_ssl_get_client_sigalg(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_handshake(ngx_connection_t *c); ngx_int_t ngx_ssl_handshake(ngx_connection_t *c);
@@ -396,6 +415,8 @@ extern int ngx_ssl_ticket_keys_index;
extern int ngx_ssl_ocsp_index; extern int ngx_ssl_ocsp_index;
extern int ngx_ssl_index; extern int ngx_ssl_index;
extern int ngx_ssl_certificate_name_index; extern int ngx_ssl_certificate_name_index;
extern int ngx_ssl_certificate_comp_index;
extern int ngx_ssl_client_hello_arg_index;
extern u_char ngx_ssl_session_buffer[NGX_SSL_MAX_SESSION_SIZE]; extern u_char ngx_ssl_session_buffer[NGX_SSL_MAX_SESSION_SIZE];
+15 -2
View File
@@ -193,6 +193,7 @@ ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err,
time_t mtime; time_t mtime;
uint32_t hash; uint32_t hash;
ngx_int_t rc; ngx_int_t rc;
ngx_uint_t invalidate;
ngx_file_uniq_t uniq; ngx_file_uniq_t uniq;
ngx_file_info_t fi; ngx_file_info_t fi;
ngx_ssl_cache_t *cache, *old_cache; ngx_ssl_cache_t *cache, *old_cache;
@@ -202,10 +203,17 @@ ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err,
*err = NULL; *err = NULL;
invalidate = index & NGX_SSL_CACHE_INVALIDATE;
index &= ~NGX_SSL_CACHE_INVALIDATE;
if (ngx_ssl_cache_init_key(cf->pool, index, path, &id) != NGX_OK) { if (ngx_ssl_cache_init_key(cf->pool, index, path, &id) != NGX_OK) {
return NULL; return NULL;
} }
if (id.type == NGX_SSL_CACHE_DATA) {
invalidate = 0;
}
cache = (ngx_ssl_cache_t *) ngx_get_conf(cf->cycle->conf_ctx, cache = (ngx_ssl_cache_t *) ngx_get_conf(cf->cycle->conf_ctx,
ngx_openssl_cache_module); ngx_openssl_cache_module);
@@ -215,7 +223,12 @@ ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err,
cn = ngx_ssl_cache_lookup(cache, type, &id, hash); cn = ngx_ssl_cache_lookup(cache, type, &id, hash);
if (cn != NULL) { if (cn != NULL) {
return type->ref(err, cn->value); if (!invalidate) {
return type->ref(err, cn->value);
}
type->free(cn->value);
ngx_rbtree_delete(&cache->rbtree, &cn->node);
} }
value = NULL; value = NULL;
@@ -236,7 +249,7 @@ ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err,
old_cache = ngx_ssl_cache_get_old_conf(cf->cycle); old_cache = ngx_ssl_cache_get_old_conf(cf->cycle);
if (old_cache && old_cache->inheritable) { if (old_cache && old_cache->inheritable && !invalidate) {
cn = ngx_ssl_cache_lookup(old_cache, type, &id, hash); cn = ngx_ssl_cache_lookup(old_cache, type, &id, hash);
if (cn != NULL) { if (cn != NULL) {
+2 -1
View File
@@ -18,7 +18,8 @@
#elif (defined SSL_R_MISSING_QUIC_TRANSPORT_PARAMETERS_EXTENSION) #elif (defined SSL_R_MISSING_QUIC_TRANSPORT_PARAMETERS_EXTENSION)
#define NGX_QUIC_QUICTLS_API 1 #define NGX_QUIC_QUICTLS_API 1
#elif (defined OPENSSL_IS_BORINGSSL || defined LIBRESSL_VERSION_NUMBER) #elif (defined OPENSSL_IS_BORINGSSL || defined OPENSSL_IS_AWSLC \
|| defined LIBRESSL_VERSION_NUMBER)
#define NGX_QUIC_BORINGSSL_API 1 #define NGX_QUIC_BORINGSSL_API 1
#else #else
+21 -15
View File
@@ -8,6 +8,12 @@
#include <ngx_core.h> #include <ngx_core.h>
#include <ngx_event.h> #include <ngx_event.h>
#include <ngx_event_quic_connection.h> #include <ngx_event_quic_connection.h>
#if (NGX_QUIC_BORINGSSL_EVP_API)
#include <openssl/hkdf.h>
#include <openssl/chacha.h>
#else
#include <openssl/kdf.h>
#endif
/* RFC 9001, 5.4.1. Header Protection Application: 5-byte mask */ /* RFC 9001, 5.4.1. Header Protection Application: 5-byte mask */
@@ -33,7 +39,7 @@ static uint64_t ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask,
static ngx_int_t ngx_quic_crypto_open(ngx_quic_secret_t *s, ngx_str_t *out, static ngx_int_t ngx_quic_crypto_open(ngx_quic_secret_t *s, ngx_str_t *out,
const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log); const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log);
#ifndef OPENSSL_IS_BORINGSSL #if !(NGX_QUIC_BORINGSSL_EVP_API)
static ngx_int_t ngx_quic_crypto_common(ngx_quic_secret_t *s, ngx_str_t *out, static ngx_int_t ngx_quic_crypto_common(ngx_quic_secret_t *s, ngx_str_t *out,
const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log); const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log);
#endif #endif
@@ -58,7 +64,7 @@ ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers)
switch (id) { switch (id) {
case TLS1_3_CK_AES_128_GCM_SHA256: case TLS1_3_CK_AES_128_GCM_SHA256:
#ifdef OPENSSL_IS_BORINGSSL #if (NGX_QUIC_BORINGSSL_EVP_API)
ciphers->c = EVP_aead_aes_128_gcm(); ciphers->c = EVP_aead_aes_128_gcm();
#else #else
ciphers->c = EVP_aes_128_gcm(); ciphers->c = EVP_aes_128_gcm();
@@ -69,7 +75,7 @@ ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers)
break; break;
case TLS1_3_CK_AES_256_GCM_SHA384: case TLS1_3_CK_AES_256_GCM_SHA384:
#ifdef OPENSSL_IS_BORINGSSL #if (NGX_QUIC_BORINGSSL_EVP_API)
ciphers->c = EVP_aead_aes_256_gcm(); ciphers->c = EVP_aead_aes_256_gcm();
#else #else
ciphers->c = EVP_aes_256_gcm(); ciphers->c = EVP_aes_256_gcm();
@@ -80,12 +86,12 @@ ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers)
break; break;
case TLS1_3_CK_CHACHA20_POLY1305_SHA256: case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
#ifdef OPENSSL_IS_BORINGSSL #if (NGX_QUIC_BORINGSSL_EVP_API)
ciphers->c = EVP_aead_chacha20_poly1305(); ciphers->c = EVP_aead_chacha20_poly1305();
#else #else
ciphers->c = EVP_chacha20_poly1305(); ciphers->c = EVP_chacha20_poly1305();
#endif #endif
#ifdef OPENSSL_IS_BORINGSSL #if (NGX_QUIC_BORINGSSL_EVP_API)
ciphers->hp = (const EVP_CIPHER *) EVP_aead_chacha20_poly1305(); ciphers->hp = (const EVP_CIPHER *) EVP_aead_chacha20_poly1305();
#else #else
ciphers->hp = EVP_chacha20(); ciphers->hp = EVP_chacha20();
@@ -94,7 +100,7 @@ ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers)
len = 32; len = 32;
break; break;
#ifndef OPENSSL_IS_BORINGSSL #if !(NGX_QUIC_BORINGSSL_EVP_API)
case TLS1_3_CK_AES_128_CCM_SHA256: case TLS1_3_CK_AES_128_CCM_SHA256:
ciphers->c = EVP_aes_128_ccm(); ciphers->c = EVP_aes_128_ccm();
ciphers->hp = EVP_aes_128_ctr(); ciphers->hp = EVP_aes_128_ctr();
@@ -263,7 +269,7 @@ static ngx_int_t
ngx_hkdf_expand(u_char *out_key, size_t out_len, const EVP_MD *digest, ngx_hkdf_expand(u_char *out_key, size_t out_len, const EVP_MD *digest,
const uint8_t *prk, size_t prk_len, const u_char *info, size_t info_len) const uint8_t *prk, size_t prk_len, const u_char *info, size_t info_len)
{ {
#ifdef OPENSSL_IS_BORINGSSL #if (NGX_QUIC_BORINGSSL_EVP_API)
if (HKDF_expand(out_key, out_len, digest, prk, prk_len, info, info_len) if (HKDF_expand(out_key, out_len, digest, prk, prk_len, info, info_len)
== 0) == 0)
@@ -325,7 +331,7 @@ ngx_hkdf_extract(u_char *out_key, size_t *out_len, const EVP_MD *digest,
const u_char *secret, size_t secret_len, const u_char *salt, const u_char *secret, size_t secret_len, const u_char *salt,
size_t salt_len) size_t salt_len)
{ {
#ifdef OPENSSL_IS_BORINGSSL #if (NGX_QUIC_BORINGSSL_EVP_API)
if (HKDF_extract(out_key, out_len, digest, secret, secret_len, salt, if (HKDF_extract(out_key, out_len, digest, secret, secret_len, salt,
salt_len) salt_len)
@@ -388,7 +394,7 @@ ngx_quic_crypto_init(const ngx_quic_cipher_t *cipher, ngx_quic_secret_t *s,
ngx_quic_md_t *key, ngx_int_t enc, ngx_log_t *log) ngx_quic_md_t *key, ngx_int_t enc, ngx_log_t *log)
{ {
#ifdef OPENSSL_IS_BORINGSSL #if (NGX_QUIC_BORINGSSL_EVP_API)
EVP_AEAD_CTX *ctx; EVP_AEAD_CTX *ctx;
ctx = EVP_AEAD_CTX_new(cipher, key->data, key->len, ctx = EVP_AEAD_CTX_new(cipher, key->data, key->len,
@@ -448,7 +454,7 @@ static ngx_int_t
ngx_quic_crypto_open(ngx_quic_secret_t *s, ngx_str_t *out, const u_char *nonce, ngx_quic_crypto_open(ngx_quic_secret_t *s, ngx_str_t *out, const u_char *nonce,
ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log) ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log)
{ {
#ifdef OPENSSL_IS_BORINGSSL #if (NGX_QUIC_BORINGSSL_EVP_API)
if (EVP_AEAD_CTX_open(s->ctx, out->data, &out->len, out->len, nonce, if (EVP_AEAD_CTX_open(s->ctx, out->data, &out->len, out->len, nonce,
s->iv.len, in->data, in->len, ad->data, ad->len) s->iv.len, in->data, in->len, ad->data, ad->len)
!= 1) != 1)
@@ -468,7 +474,7 @@ ngx_int_t
ngx_quic_crypto_seal(ngx_quic_secret_t *s, ngx_str_t *out, const u_char *nonce, ngx_quic_crypto_seal(ngx_quic_secret_t *s, ngx_str_t *out, const u_char *nonce,
ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log) ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log)
{ {
#ifdef OPENSSL_IS_BORINGSSL #if (NGX_QUIC_BORINGSSL_EVP_API)
if (EVP_AEAD_CTX_seal(s->ctx, out->data, &out->len, out->len, nonce, if (EVP_AEAD_CTX_seal(s->ctx, out->data, &out->len, out->len, nonce,
s->iv.len, in->data, in->len, ad->data, ad->len) s->iv.len, in->data, in->len, ad->data, ad->len)
!= 1) != 1)
@@ -484,7 +490,7 @@ ngx_quic_crypto_seal(ngx_quic_secret_t *s, ngx_str_t *out, const u_char *nonce,
} }
#ifndef OPENSSL_IS_BORINGSSL #if !(NGX_QUIC_BORINGSSL_EVP_API)
static ngx_int_t static ngx_int_t
ngx_quic_crypto_common(ngx_quic_secret_t *s, ngx_str_t *out, ngx_quic_crypto_common(ngx_quic_secret_t *s, ngx_str_t *out,
@@ -563,7 +569,7 @@ void
ngx_quic_crypto_cleanup(ngx_quic_secret_t *s) ngx_quic_crypto_cleanup(ngx_quic_secret_t *s)
{ {
if (s->ctx) { if (s->ctx) {
#ifdef OPENSSL_IS_BORINGSSL #if (NGX_QUIC_BORINGSSL_EVP_API)
EVP_AEAD_CTX_free(s->ctx); EVP_AEAD_CTX_free(s->ctx);
#else #else
EVP_CIPHER_CTX_free(s->ctx); EVP_CIPHER_CTX_free(s->ctx);
@@ -579,7 +585,7 @@ ngx_quic_crypto_hp_init(const EVP_CIPHER *cipher, ngx_quic_secret_t *s,
{ {
EVP_CIPHER_CTX *ctx; EVP_CIPHER_CTX *ctx;
#ifdef OPENSSL_IS_BORINGSSL #if (NGX_QUIC_BORINGSSL_EVP_API)
if (cipher == (EVP_CIPHER *) EVP_aead_chacha20_poly1305()) { if (cipher == (EVP_CIPHER *) EVP_aead_chacha20_poly1305()) {
/* no EVP interface */ /* no EVP interface */
s->hp_ctx = NULL; s->hp_ctx = NULL;
@@ -615,7 +621,7 @@ ngx_quic_crypto_hp(ngx_quic_secret_t *s, u_char *out, u_char *in,
ctx = s->hp_ctx; ctx = s->hp_ctx;
#ifdef OPENSSL_IS_BORINGSSL #if (NGX_QUIC_BORINGSSL_EVP_API)
uint32_t cnt; uint32_t cnt;
if (ctx == NULL) { if (ctx == NULL) {
+3 -1
View File
@@ -22,10 +22,12 @@
#define NGX_QUIC_MAX_MD_SIZE 48 #define NGX_QUIC_MAX_MD_SIZE 48
#ifdef OPENSSL_IS_BORINGSSL #if (defined OPENSSL_IS_BORINGSSL || defined OPENSSL_IS_AWSLC)
#define NGX_QUIC_BORINGSSL_EVP_API 1
#define ngx_quic_cipher_t EVP_AEAD #define ngx_quic_cipher_t EVP_AEAD
#define ngx_quic_crypto_ctx_t EVP_AEAD_CTX #define ngx_quic_crypto_ctx_t EVP_AEAD_CTX
#else #else
#define NGX_QUIC_BORINGSSL_EVP_API 0
#define ngx_quic_cipher_t EVP_CIPHER #define ngx_quic_cipher_t EVP_CIPHER
#define ngx_quic_crypto_ctx_t EVP_CIPHER_CTX #define ngx_quic_crypto_ctx_t EVP_CIPHER_CTX
#endif #endif
+24 -13
View File
@@ -185,7 +185,13 @@ ngx_quic_cbs_release_rcd(ngx_ssl_conn_t *ssl_conn, size_t bytes_read, void *arg)
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ngx_quic_cbs_release_rcd len:%uz", bytes_read); "quic ngx_quic_cbs_release_rcd len:%uz", bytes_read);
/* already closed on handshake failure */
qc = ngx_quic_get_connection(c); qc = ngx_quic_get_connection(c);
if (qc == NULL) {
return 1;
}
ctx = ngx_quic_get_send_ctx(qc, qc->read_level); ctx = ngx_quic_get_send_ctx(qc, qc->read_level);
cl = ngx_quic_read_buffer(c, &ctx->crypto, bytes_read); cl = ngx_quic_read_buffer(c, &ctx->crypto, bytes_read);
@@ -695,30 +701,35 @@ ngx_quic_handshake(ngx_connection_t *c)
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n);
if (qc->error) {
return NGX_ERROR;
}
if (n <= 0) { if (n <= 0) {
sslerr = SSL_get_error(ssl_conn, n); sslerr = SSL_get_error(ssl_conn, n);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d",
sslerr); sslerr);
if (c->ssl->handshake_rejected) {
ngx_connection_error(c, 0, "handshake rejected");
ERR_clear_error();
return NGX_ERROR;
}
if (qc->error) {
ngx_connection_error(c, 0, "SSL_do_handshake() failed");
ERR_clear_error();
return NGX_ERROR;
}
if (sslerr != SSL_ERROR_WANT_READ) { if (sslerr != SSL_ERROR_WANT_READ) {
if (c->ssl->handshake_rejected) {
ngx_connection_error(c, 0, "handshake rejected");
ERR_clear_error();
return NGX_ERROR;
}
ngx_ssl_connection_error(c, sslerr, 0, "SSL_do_handshake() failed"); ngx_ssl_connection_error(c, sslerr, 0, "SSL_do_handshake() failed");
return NGX_ERROR; return NGX_ERROR;
} }
} }
if (qc->error) {
ngx_connection_error(c, 0, "SSL_do_handshake() failed");
return NGX_ERROR;
}
if (!SSL_is_init_finished(ssl_conn)) { if (!SSL_is_init_finished(ssl_conn)) {
if (ngx_quic_keys_available(qc->keys, NGX_QUIC_ENCRYPTION_EARLY_DATA, 0) if (ngx_quic_keys_available(qc->keys, NGX_QUIC_ENCRYPTION_EARLY_DATA, 0)
&& qc->client_tp_done) && qc->client_tp_done)
@@ -968,7 +979,7 @@ ngx_quic_init_connection(ngx_connection_t *c)
} }
#endif #endif
#ifdef OPENSSL_IS_BORINGSSL #if (defined OPENSSL_IS_BORINGSSL || defined OPENSSL_IS_AWSLC)
if (SSL_set_quic_early_data_context(ssl_conn, p, clen) == 0) { if (SSL_set_quic_early_data_context(ssl_conn, p, clen) == 0) {
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
"quic SSL_set_quic_early_data_context() failed"); "quic SSL_set_quic_early_data_context() failed");
@@ -66,7 +66,9 @@ ngx_http_chunked_header_filter(ngx_http_request_t *r)
|| r->headers_out.status == NGX_HTTP_NO_CONTENT || r->headers_out.status == NGX_HTTP_NO_CONTENT
|| r->headers_out.status < NGX_HTTP_OK || r->headers_out.status < NGX_HTTP_OK
|| r != r->main || r != r->main
|| r->method == NGX_HTTP_HEAD) || r->method == NGX_HTTP_HEAD
|| (r->method == NGX_HTTP_CONNECT
&& r->headers_out.status < NGX_HTTP_SPECIAL_RESPONSE))
{ {
return ngx_http_next_header_filter(r); return ngx_http_next_header_filter(r);
} }
+12
View File
@@ -63,6 +63,7 @@ typedef struct {
unsigned allow_binary_include:1; unsigned allow_binary_include:1;
unsigned binary_include:1; unsigned binary_include:1;
unsigned proxy_recursive:1; unsigned proxy_recursive:1;
unsigned no_cacheable:1;
} ngx_http_geo_conf_ctx_t; } ngx_http_geo_conf_ctx_t;
@@ -463,6 +464,7 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+ sizeof(ngx_http_variable_value_t) + sizeof(ngx_http_variable_value_t)
+ 0x10000 * sizeof(ngx_http_geo_range_t *); + 0x10000 * sizeof(ngx_http_geo_range_t *);
ctx.allow_binary_include = 1; ctx.allow_binary_include = 1;
ctx.no_cacheable = 0;
save = *cf; save = *cf;
cf->pool = pool; cf->pool = pool;
@@ -478,6 +480,10 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
goto failed; goto failed;
} }
if (ctx.no_cacheable) {
var->flags |= NGX_HTTP_VAR_NOCACHEABLE;
}
geo->proxies = ctx.proxies; geo->proxies = ctx.proxies;
geo->proxy_recursive = ctx.proxy_recursive; geo->proxy_recursive = ctx.proxy_recursive;
@@ -623,6 +629,12 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
rv = NGX_CONF_OK; rv = NGX_CONF_OK;
goto done; goto done;
} }
else if (ngx_strcmp(value[0].data, "volatile") == 0) {
ctx->no_cacheable = 1;
rv = NGX_CONF_OK;
goto done;
}
} }
if (cf->args->nelts != 2) { if (cf->args->nelts != 2) {
@@ -10,6 +10,11 @@
#include <ngx_http.h> #include <ngx_http.h>
#define NGX_HTTP_HEADERS_INHERIT_OFF 0
#define NGX_HTTP_HEADERS_INHERIT_ON 1
#define NGX_HTTP_HEADERS_INHERIT_MERGE 2
typedef struct ngx_http_header_val_s ngx_http_header_val_t; typedef struct ngx_http_header_val_s ngx_http_header_val_t;
typedef ngx_int_t (*ngx_http_set_header_pt)(ngx_http_request_t *r, typedef ngx_int_t (*ngx_http_set_header_pt)(ngx_http_request_t *r,
@@ -49,6 +54,8 @@ typedef struct {
ngx_http_complex_value_t *expires_value; ngx_http_complex_value_t *expires_value;
ngx_array_t *headers; ngx_array_t *headers;
ngx_array_t *trailers; ngx_array_t *trailers;
ngx_uint_t headers_inherit;
ngx_uint_t trailers_inherit;
} ngx_http_headers_conf_t; } ngx_http_headers_conf_t;
@@ -97,6 +104,14 @@ static ngx_http_set_header_t ngx_http_set_headers[] = {
}; };
static ngx_conf_enum_t ngx_http_headers_inherit[] = {
{ ngx_string("off"), NGX_HTTP_HEADERS_INHERIT_OFF },
{ ngx_string("on"), NGX_HTTP_HEADERS_INHERIT_ON },
{ ngx_string("merge"), NGX_HTTP_HEADERS_INHERIT_MERGE },
{ ngx_null_string, 0 }
};
static ngx_command_t ngx_http_headers_filter_commands[] = { static ngx_command_t ngx_http_headers_filter_commands[] = {
{ ngx_string("expires"), { ngx_string("expires"),
@@ -123,6 +138,22 @@ static ngx_command_t ngx_http_headers_filter_commands[] = {
offsetof(ngx_http_headers_conf_t, trailers), offsetof(ngx_http_headers_conf_t, trailers),
NULL }, NULL },
{ ngx_string("add_header_inherit"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|NGX_CONF_TAKE1,
ngx_conf_set_enum_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_headers_conf_t, headers_inherit),
&ngx_http_headers_inherit },
{ ngx_string("add_trailer_inherit"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|NGX_CONF_TAKE1,
ngx_conf_set_enum_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_headers_conf_t, trailers_inherit),
&ngx_http_headers_inherit },
ngx_null_command ngx_null_command
}; };
@@ -657,6 +688,8 @@ ngx_http_headers_create_conf(ngx_conf_t *cf)
*/ */
conf->expires = NGX_HTTP_EXPIRES_UNSET; conf->expires = NGX_HTTP_EXPIRES_UNSET;
conf->headers_inherit = NGX_CONF_UNSET_UINT;
conf->trailers_inherit = NGX_CONF_UNSET_UINT;
return conf; return conf;
} }
@@ -668,6 +701,8 @@ ngx_http_headers_merge_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_http_headers_conf_t *prev = parent; ngx_http_headers_conf_t *prev = parent;
ngx_http_headers_conf_t *conf = child; ngx_http_headers_conf_t *conf = child;
ngx_http_header_val_t *hv;
if (conf->expires == NGX_HTTP_EXPIRES_UNSET) { if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {
conf->expires = prev->expires; conf->expires = prev->expires;
conf->expires_time = prev->expires_time; conf->expires_time = prev->expires_time;
@@ -678,12 +713,43 @@ ngx_http_headers_merge_conf(ngx_conf_t *cf, void *parent, void *child)
} }
} }
if (conf->headers == NULL) { ngx_conf_merge_uint_value(conf->headers_inherit, prev->headers_inherit,
conf->headers = prev->headers; NGX_HTTP_HEADERS_INHERIT_ON);
ngx_conf_merge_uint_value(conf->trailers_inherit, prev->trailers_inherit,
NGX_HTTP_HEADERS_INHERIT_ON);
if (conf->headers_inherit != NGX_HTTP_HEADERS_INHERIT_OFF
&& prev->headers)
{
if (conf->headers == NULL) {
conf->headers = prev->headers;
} else if (conf->headers_inherit == NGX_HTTP_HEADERS_INHERIT_MERGE) {
hv = ngx_array_push_n(conf->headers, prev->headers->nelts);
if (hv == NULL) {
return NGX_CONF_ERROR;
}
ngx_memcpy(hv, prev->headers->elts,
sizeof(ngx_http_header_val_t) * prev->headers->nelts);
}
} }
if (conf->trailers == NULL) { if (conf->trailers_inherit != NGX_HTTP_HEADERS_INHERIT_OFF
conf->trailers = prev->trailers; && prev->trailers)
{
if (conf->trailers == NULL) {
conf->trailers = prev->trailers;
} else if (conf->trailers_inherit == NGX_HTTP_HEADERS_INHERIT_MERGE) {
hv = ngx_array_push_n(conf->trailers, prev->trailers->nelts);
if (hv == NULL) {
return NGX_CONF_ERROR;
}
ngx_memcpy(hv, prev->trailers->elts,
sizeof(ngx_http_header_val_t) * prev->trailers->nelts);
}
} }
return NGX_CONF_OK; return NGX_CONF_OK;
+1 -1
View File
@@ -490,7 +490,7 @@ ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
if (value[i].len == 0) { if (value[i].len == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"index \"%V\" in \"index\" directive is invalid", "index \"%V\" in \"index\" directive is invalid",
&value[1]); &value[i]);
return NGX_CONF_ERROR; return NGX_CONF_ERROR;
} }
+294 -123
View File
@@ -8,6 +8,7 @@
#include <ngx_config.h> #include <ngx_config.h>
#include <ngx_core.h> #include <ngx_core.h>
#include <ngx_http.h> #include <ngx_http.h>
#include <ngx_http_proxy_module.h>
#define NGX_HTTP_PROXY_COOKIE_SECURE 0x0001 #define NGX_HTTP_PROXY_COOKIE_SECURE 0x0001
@@ -23,11 +24,6 @@
#define NGX_HTTP_PROXY_COOKIE_SAMESITE_OFF 0x0400 #define NGX_HTTP_PROXY_COOKIE_SAMESITE_OFF 0x0400
typedef struct {
ngx_array_t caches; /* ngx_http_file_cache_t * */
} ngx_http_proxy_main_conf_t;
typedef struct ngx_http_proxy_rewrite_s ngx_http_proxy_rewrite_t; typedef struct ngx_http_proxy_rewrite_s ngx_http_proxy_rewrite_t;
typedef ngx_int_t (*ngx_http_proxy_rewrite_pt)(ngx_http_request_t *r, typedef ngx_int_t (*ngx_http_proxy_rewrite_pt)(ngx_http_request_t *r,
@@ -61,94 +57,6 @@ typedef struct {
} ngx_http_proxy_cookie_flags_t; } ngx_http_proxy_cookie_flags_t;
typedef struct {
ngx_str_t key_start;
ngx_str_t schema;
ngx_str_t host_header;
ngx_str_t port;
ngx_str_t uri;
} ngx_http_proxy_vars_t;
typedef struct {
ngx_array_t *flushes;
ngx_array_t *lengths;
ngx_array_t *values;
ngx_hash_t hash;
} ngx_http_proxy_headers_t;
typedef struct {
ngx_http_upstream_conf_t upstream;
ngx_array_t *body_flushes;
ngx_array_t *body_lengths;
ngx_array_t *body_values;
ngx_str_t body_source;
ngx_http_proxy_headers_t headers;
#if (NGX_HTTP_CACHE)
ngx_http_proxy_headers_t headers_cache;
#endif
ngx_array_t *headers_source;
ngx_array_t *proxy_lengths;
ngx_array_t *proxy_values;
ngx_array_t *redirects;
ngx_array_t *cookie_domains;
ngx_array_t *cookie_paths;
ngx_array_t *cookie_flags;
ngx_http_complex_value_t *method;
ngx_str_t location;
ngx_str_t url;
#if (NGX_HTTP_CACHE)
ngx_http_complex_value_t cache_key;
#endif
ngx_http_proxy_vars_t vars;
ngx_flag_t redirect;
ngx_uint_t http_version;
ngx_uint_t headers_hash_max_size;
ngx_uint_t headers_hash_bucket_size;
#if (NGX_HTTP_SSL)
ngx_uint_t ssl;
ngx_uint_t ssl_protocols;
ngx_str_t ssl_ciphers;
ngx_uint_t ssl_verify_depth;
ngx_str_t ssl_trusted_certificate;
ngx_str_t ssl_crl;
ngx_array_t *ssl_conf_commands;
#endif
} ngx_http_proxy_loc_conf_t;
typedef struct {
ngx_http_status_t status;
ngx_http_chunked_t chunked;
ngx_http_proxy_vars_t vars;
off_t internal_body_length;
ngx_chain_t *free;
ngx_chain_t *busy;
unsigned head:1;
unsigned internal_chunked:1;
unsigned header_sent:1;
} ngx_http_proxy_ctx_t;
static ngx_int_t ngx_http_proxy_eval(ngx_http_request_t *r,
ngx_http_proxy_ctx_t *ctx, ngx_http_proxy_loc_conf_t *plcf);
#if (NGX_HTTP_CACHE)
static ngx_int_t ngx_http_proxy_create_key(ngx_http_request_t *r);
#endif
static ngx_int_t ngx_http_proxy_create_request(ngx_http_request_t *r); static ngx_int_t ngx_http_proxy_create_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_proxy_reinit_request(ngx_http_request_t *r); static ngx_int_t ngx_http_proxy_reinit_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_proxy_body_output_filter(void *data, ngx_chain_t *in); static ngx_int_t ngx_http_proxy_body_output_filter(void *data, ngx_chain_t *in);
@@ -163,6 +71,8 @@ static ngx_int_t ngx_http_proxy_non_buffered_copy_filter(void *data,
ssize_t bytes); ssize_t bytes);
static ngx_int_t ngx_http_proxy_non_buffered_chunked_filter(void *data, static ngx_int_t ngx_http_proxy_non_buffered_chunked_filter(void *data,
ssize_t bytes); ssize_t bytes);
static ngx_int_t ngx_http_proxy_process_trailer(ngx_http_request_t *r,
ngx_buf_t *buf);
static void ngx_http_proxy_abort_request(ngx_http_request_t *r); static void ngx_http_proxy_abort_request(ngx_http_request_t *r);
static void ngx_http_proxy_finalize_request(ngx_http_request_t *r, static void ngx_http_proxy_finalize_request(ngx_http_request_t *r,
ngx_int_t rc); ngx_int_t rc);
@@ -174,15 +84,14 @@ static ngx_int_t ngx_http_proxy_port_variable(ngx_http_request_t *r,
static ngx_int_t static ngx_int_t
ngx_http_proxy_add_x_forwarded_for_variable(ngx_http_request_t *r, ngx_http_proxy_add_x_forwarded_for_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data); ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t
ngx_http_proxy_internal_connection_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t static ngx_int_t
ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r, ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data); ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_proxy_internal_chunked_variable(ngx_http_request_t *r, static ngx_int_t ngx_http_proxy_internal_chunked_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data); ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r,
ngx_table_elt_t *h, size_t prefix);
static ngx_int_t ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r,
ngx_table_elt_t *h);
static ngx_int_t ngx_http_proxy_parse_cookie(ngx_str_t *value, static ngx_int_t ngx_http_proxy_parse_cookie(ngx_str_t *value,
ngx_array_t *attrs); ngx_array_t *attrs);
static ngx_int_t ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r, static ngx_int_t ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r,
@@ -289,6 +198,9 @@ static ngx_conf_post_t ngx_http_proxy_ssl_conf_command_post =
static ngx_conf_enum_t ngx_http_proxy_http_version[] = { static ngx_conf_enum_t ngx_http_proxy_http_version[] = {
{ ngx_string("1.0"), NGX_HTTP_VERSION_10 }, { ngx_string("1.0"), NGX_HTTP_VERSION_10 },
{ ngx_string("1.1"), NGX_HTTP_VERSION_11 }, { ngx_string("1.1"), NGX_HTTP_VERSION_11 },
#if (NGX_HTTP_V2)
{ ngx_string("2"), NGX_HTTP_VERSION_20 },
#endif
{ ngx_null_string, 0 } { ngx_null_string, 0 }
}; };
@@ -459,6 +371,13 @@ static ngx_command_t ngx_http_proxy_commands[] = {
offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_request_body), offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_request_body),
NULL }, NULL },
{ ngx_string("proxy_pass_trailers"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_trailers),
NULL },
{ ngx_string("proxy_buffer_size"), { ngx_string("proxy_buffer_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot, ngx_conf_set_size_slot,
@@ -829,8 +748,8 @@ static char ngx_http_proxy_version_11[] = " HTTP/1.1" CRLF;
static ngx_keyval_t ngx_http_proxy_headers[] = { static ngx_keyval_t ngx_http_proxy_headers[] = {
{ ngx_string("Host"), ngx_string("$proxy_host") }, { ngx_string("Host"), ngx_string("$proxy_internal_host") },
{ ngx_string("Connection"), ngx_string("close") }, { ngx_string("Connection"), ngx_string("$proxy_internal_connection") },
{ ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") }, { ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") },
{ ngx_string("Transfer-Encoding"), ngx_string("$proxy_internal_chunked") }, { ngx_string("Transfer-Encoding"), ngx_string("$proxy_internal_chunked") },
{ ngx_string("TE"), ngx_string("") }, { ngx_string("TE"), ngx_string("") },
@@ -857,8 +776,8 @@ static ngx_str_t ngx_http_proxy_hide_headers[] = {
#if (NGX_HTTP_CACHE) #if (NGX_HTTP_CACHE)
static ngx_keyval_t ngx_http_proxy_cache_headers[] = { static ngx_keyval_t ngx_http_proxy_cache_headers[] = {
{ ngx_string("Host"), ngx_string("$proxy_host") }, { ngx_string("Host"), ngx_string("$proxy_internal_host") },
{ ngx_string("Connection"), ngx_string("close") }, { ngx_string("Connection"), ngx_string("$proxy_internal_connection") },
{ ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") }, { ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") },
{ ngx_string("Transfer-Encoding"), ngx_string("$proxy_internal_chunked") }, { ngx_string("Transfer-Encoding"), ngx_string("$proxy_internal_chunked") },
{ ngx_string("TE"), ngx_string("") }, { ngx_string("TE"), ngx_string("") },
@@ -893,6 +812,14 @@ static ngx_http_variable_t ngx_http_proxy_vars[] = {
{ ngx_string("proxy_add_via"), NULL, NULL, 0, NGX_HTTP_VAR_NOHASH, 0 }, { ngx_string("proxy_add_via"), NULL, NULL, 0, NGX_HTTP_VAR_NOHASH, 0 },
#endif #endif
{ ngx_string("proxy_internal_host"), NULL,
ngx_http_proxy_host_variable, 1,
NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
{ ngx_string("proxy_internal_connection"), NULL,
ngx_http_proxy_internal_connection_variable, 0,
NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
{ ngx_string("proxy_internal_body_length"), NULL, { ngx_string("proxy_internal_body_length"), NULL,
ngx_http_proxy_internal_body_length_variable, 0, ngx_http_proxy_internal_body_length_variable, 0,
NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
@@ -951,6 +878,14 @@ ngx_http_proxy_handler(ngx_http_request_t *r)
ngx_http_proxy_main_conf_t *pmcf; ngx_http_proxy_main_conf_t *pmcf;
#endif #endif
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
#if (NGX_HTTP_V2)
if (plcf->http_version == NGX_HTTP_VERSION_20) {
return ngx_http_proxy_v2_handler(r);
}
#endif
if (ngx_http_upstream_create(r) != NGX_OK) { if (ngx_http_upstream_create(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR; return NGX_HTTP_INTERNAL_SERVER_ERROR;
} }
@@ -960,9 +895,9 @@ ngx_http_proxy_handler(ngx_http_request_t *r)
return NGX_HTTP_INTERNAL_SERVER_ERROR; return NGX_HTTP_INTERNAL_SERVER_ERROR;
} }
ngx_http_set_ctx(r, ctx, ngx_http_proxy_module); ctx->legacy = 1;
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); ngx_http_set_ctx(r, ctx, ngx_http_proxy_module);
u = r->upstream; u = r->upstream;
@@ -1039,7 +974,7 @@ ngx_http_proxy_handler(ngx_http_request_t *r)
} }
static ngx_int_t ngx_int_t
ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx, ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx,
ngx_http_proxy_loc_conf_t *plcf) ngx_http_proxy_loc_conf_t *plcf)
{ {
@@ -1143,7 +1078,7 @@ ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx,
#if (NGX_HTTP_CACHE) #if (NGX_HTTP_CACHE)
static ngx_int_t ngx_int_t
ngx_http_proxy_create_key(ngx_http_request_t *r) ngx_http_proxy_create_key(ngx_http_request_t *r)
{ {
size_t len, loc_len; size_t len, loc_len;
@@ -1195,7 +1130,8 @@ ngx_http_proxy_create_key(ngx_http_request_t *r)
return NGX_OK; return NGX_OK;
} }
loc_len = (r->valid_location && ctx->vars.uri.len) ? plcf->location.len : 0; loc_len = (r->valid_location && ctx->vars.uri.len)
? ngx_min(plcf->location.len, r->uri.len) : 0;
if (r->quoted_uri || r->internal) { if (r->quoted_uri || r->internal) {
escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len, escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len,
@@ -1307,8 +1243,8 @@ ngx_http_proxy_create_request(ngx_http_request_t *r)
uri_len = r->unparsed_uri.len; uri_len = r->unparsed_uri.len;
} else { } else {
loc_len = (r->valid_location && ctx->vars.uri.len) ? loc_len = (r->valid_location && ctx->vars.uri.len)
plcf->location.len : 0; ? ngx_min(plcf->location.len, r->uri.len) : 0;
if (r->quoted_uri || r->internal) { if (r->quoted_uri || r->internal) {
escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len, escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len,
@@ -2107,7 +2043,7 @@ ngx_http_proxy_input_filter_init(void *data)
/* chunked */ /* chunked */
u->pipe->input_filter = ngx_http_proxy_chunked_filter; u->pipe->input_filter = ngx_http_proxy_chunked_filter;
u->pipe->length = 3; /* "0" LF LF */ u->pipe->length = 5; /* "0" CRLF CRLF */
u->input_filter = ngx_http_proxy_non_buffered_chunked_filter; u->input_filter = ngx_http_proxy_non_buffered_chunked_filter;
u->length = 1; u->length = 1;
@@ -2213,11 +2149,12 @@ ngx_http_proxy_copy_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
static ngx_int_t static ngx_int_t
ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
{ {
ngx_int_t rc; ngx_int_t rc;
ngx_buf_t *b, **prev; ngx_buf_t *b, **prev;
ngx_chain_t *cl; ngx_chain_t *cl;
ngx_http_request_t *r; ngx_http_request_t *r;
ngx_http_proxy_ctx_t *ctx; ngx_http_proxy_ctx_t *ctx;
ngx_http_proxy_loc_conf_t *plcf;
if (buf->pos == buf->last) { if (buf->pos == buf->last) {
return NGX_OK; return NGX_OK;
@@ -2248,11 +2185,39 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
} }
b = NULL; b = NULL;
if (ctx->trailers) {
rc = ngx_http_proxy_process_trailer(r, buf);
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (rc == NGX_OK) {
/* a whole response has been parsed successfully */
p->length = 0;
r->upstream->keepalive = !r->upstream->headers_in.connection_close;
if (buf->pos != buf->last) {
ngx_log_error(NGX_LOG_WARN, p->log, 0,
"upstream sent data after trailers");
r->upstream->keepalive = 0;
}
}
goto free_buf;
}
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
prev = &buf->shadow; prev = &buf->shadow;
for ( ;; ) { for ( ;; ) {
rc = ngx_http_parse_chunked(r, buf, &ctx->chunked); rc = ngx_http_parse_chunked(r, buf, &ctx->chunked,
plcf->upstream.pass_trailers);
if (rc == NGX_OK) { if (rc == NGX_OK) {
@@ -2307,6 +2272,19 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
if (rc == NGX_DONE) { if (rc == NGX_DONE) {
if (plcf->upstream.pass_trailers) {
rc = ngx_http_proxy_process_trailer(r, buf);
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (rc == NGX_AGAIN) {
p->length = 1;
break;
}
}
/* a whole response has been parsed successfully */ /* a whole response has been parsed successfully */
p->length = 0; p->length = 0;
@@ -2338,6 +2316,8 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
return NGX_ERROR; return NGX_ERROR;
} }
free_buf:
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, p->log, 0, ngx_log_debug2(NGX_LOG_DEBUG_HTTP, p->log, 0,
"http proxy chunked state %ui, length %O", "http proxy chunked state %ui, length %O",
ctx->chunked.state, p->length); ctx->chunked.state, p->length);
@@ -2433,11 +2413,14 @@ ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes)
{ {
ngx_http_request_t *r = data; ngx_http_request_t *r = data;
ngx_int_t rc; ngx_int_t rc;
ngx_buf_t *b, *buf; ngx_buf_t *b, *buf;
ngx_chain_t *cl, **ll; ngx_chain_t *cl, **ll;
ngx_http_upstream_t *u; ngx_http_upstream_t *u;
ngx_http_proxy_ctx_t *ctx; ngx_http_proxy_ctx_t *ctx;
ngx_http_proxy_loc_conf_t *plcf;
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
@@ -2451,13 +2434,38 @@ ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes)
buf->pos = buf->last; buf->pos = buf->last;
buf->last += bytes; buf->last += bytes;
if (ctx->trailers) {
rc = ngx_http_proxy_process_trailer(r, buf);
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (rc == NGX_OK) {
/* a whole response has been parsed successfully */
r->upstream->keepalive = !u->headers_in.connection_close;
u->length = 0;
if (buf->pos != buf->last) {
ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
"upstream sent data after trailers");
u->keepalive = 0;
}
}
return NGX_OK;
}
for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) { for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
ll = &cl->next; ll = &cl->next;
} }
for ( ;; ) { for ( ;; ) {
rc = ngx_http_parse_chunked(r, buf, &ctx->chunked); rc = ngx_http_parse_chunked(r, buf, &ctx->chunked,
plcf->upstream.pass_trailers);
if (rc == NGX_OK) { if (rc == NGX_OK) {
@@ -2499,6 +2507,19 @@ ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes)
if (rc == NGX_DONE) { if (rc == NGX_DONE) {
if (plcf->upstream.pass_trailers) {
rc = ngx_http_proxy_process_trailer(r, buf);
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (rc == NGX_AGAIN) {
u->length = 1;
break;
}
}
/* a whole response has been parsed successfully */ /* a whole response has been parsed successfully */
u->keepalive = !u->headers_in.connection_close; u->keepalive = !u->headers_in.connection_close;
@@ -2529,6 +2550,115 @@ ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes)
} }
static ngx_int_t
ngx_http_proxy_process_trailer(ngx_http_request_t *r, ngx_buf_t *buf)
{
size_t len;
ngx_int_t rc;
ngx_buf_t *b;
ngx_table_elt_t *h;
ngx_http_proxy_ctx_t *ctx;
ngx_http_proxy_loc_conf_t *plcf;
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
if (ctx->trailers == NULL) {
ctx->trailers = ngx_create_temp_buf(r->pool,
plcf->upstream.buffer_size);
if (ctx->trailers == NULL) {
return NGX_ERROR;
}
}
b = ctx->trailers;
len = ngx_min(buf->last - buf->pos, b->end - b->last);
b->last = ngx_cpymem(b->last, buf->pos, len);
for ( ;; ) {
rc = ngx_http_parse_header_line(r, b, 1);
if (rc == NGX_OK) {
/* a header line has been parsed successfully */
h = ngx_list_push(&r->upstream->headers_in.trailers);
if (h == NULL) {
return NGX_ERROR;
}
h->hash = r->header_hash;
h->key.len = r->header_name_end - r->header_name_start;
h->value.len = r->header_end - r->header_start;
h->key.data = ngx_pnalloc(r->pool,
h->key.len + 1 + h->value.len + 1 + h->key.len);
if (h->key.data == NULL) {
h->hash = 0;
return NGX_ERROR;
}
h->value.data = h->key.data + h->key.len + 1;
h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1;
ngx_memcpy(h->key.data, r->header_name_start, h->key.len);
h->key.data[h->key.len] = '\0';
ngx_memcpy(h->value.data, r->header_start, h->value.len);
h->value.data[h->value.len] = '\0';
if (h->key.len == r->lowcase_index) {
ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
} else {
ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy trailer: \"%V: %V\"",
&h->key, &h->value);
continue;
}
if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
/* a whole header has been parsed successfully */
buf->pos += len - (b->last - b->pos);
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy trailer done");
return NGX_OK;
}
if (rc == NGX_AGAIN) {
buf->pos += len;
if (b->last == b->end) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent too big trailers");
return NGX_ERROR;
}
return NGX_AGAIN;
}
/* rc == NGX_HTTP_PARSE_INVALID_HEADER */
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid trailer: \"%*s\\x%02xd...\"",
r->header_end - r->header_name_start,
r->header_name_start, *r->header_end);
return NGX_ERROR;
}
}
static void static void
ngx_http_proxy_abort_request(ngx_http_request_t *r) ngx_http_proxy_abort_request(ngx_http_request_t *r)
{ {
@@ -2562,6 +2692,11 @@ ngx_http_proxy_host_variable(ngx_http_request_t *r,
return NGX_OK; return NGX_OK;
} }
if (data == 1 && !ctx->legacy) {
v->not_found = 1;
return NGX_OK;
}
v->len = ctx->vars.host_header.len; v->len = ctx->vars.host_header.len;
v->valid = 1; v->valid = 1;
v->no_cacheable = 0; v->no_cacheable = 0;
@@ -2642,6 +2777,29 @@ ngx_http_proxy_add_x_forwarded_for_variable(ngx_http_request_t *r,
} }
static ngx_int_t
ngx_http_proxy_internal_connection_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_http_proxy_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
if (ctx == NULL || !ctx->legacy) {
v->not_found = 1;
return NGX_OK;
}
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
ngx_str_set(v, "close");
return NGX_OK;
}
static ngx_int_t static ngx_int_t
ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r, ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data) ngx_http_variable_value_t *v, uintptr_t data)
@@ -2695,7 +2853,7 @@ ngx_http_proxy_internal_chunked_variable(ngx_http_request_t *r,
} }
static ngx_int_t ngx_int_t
ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, ngx_table_elt_t *h,
size_t prefix) size_t prefix)
{ {
@@ -2727,7 +2885,7 @@ ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, ngx_table_elt_t *h,
} }
static ngx_int_t ngx_int_t
ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, ngx_table_elt_t *h) ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, ngx_table_elt_t *h)
{ {
u_char *p; u_char *p;
@@ -3372,6 +3530,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
* conf->headers.values = NULL; * conf->headers.values = NULL;
* conf->headers.hash = { NULL, 0 }; * conf->headers.hash = { NULL, 0 };
* conf->headers_cache.lengths = NULL; * conf->headers_cache.lengths = NULL;
* conf->host_set = 0;
* conf->headers_cache.values = NULL; * conf->headers_cache.values = NULL;
* conf->headers_cache.hash = { NULL, 0 }; * conf->headers_cache.hash = { NULL, 0 };
* conf->body_lengths = NULL; * conf->body_lengths = NULL;
@@ -3411,6 +3570,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
conf->upstream.pass_request_headers = NGX_CONF_UNSET; conf->upstream.pass_request_headers = NGX_CONF_UNSET;
conf->upstream.pass_request_body = NGX_CONF_UNSET; conf->upstream.pass_request_body = NGX_CONF_UNSET;
conf->upstream.pass_trailers = NGX_CONF_UNSET;
#if (NGX_HTTP_CACHE) #if (NGX_HTTP_CACHE)
conf->upstream.cache = NGX_CONF_UNSET; conf->upstream.cache = NGX_CONF_UNSET;
@@ -3754,6 +3914,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->upstream.pass_request_body, ngx_conf_merge_value(conf->upstream.pass_request_body,
prev->upstream.pass_request_body, 1); prev->upstream.pass_request_body, 1);
ngx_conf_merge_value(conf->upstream.pass_trailers,
prev->upstream.pass_trailers, 0);
ngx_conf_merge_value(conf->upstream.intercept_errors, ngx_conf_merge_value(conf->upstream.intercept_errors,
prev->upstream.intercept_errors, 0); prev->upstream.intercept_errors, 0);
@@ -3943,6 +4106,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
#if (NGX_HTTP_CACHE) #if (NGX_HTTP_CACHE)
conf->headers_cache = prev->headers_cache; conf->headers_cache = prev->headers_cache;
#endif #endif
conf->host_set = prev->host_set;
} }
rc = ngx_http_proxy_init_headers(cf, conf, &conf->headers, rc = ngx_http_proxy_init_headers(cf, conf, &conf->headers,
@@ -3975,6 +4139,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
#if (NGX_HTTP_CACHE) #if (NGX_HTTP_CACHE)
prev->headers_cache = conf->headers_cache; prev->headers_cache = conf->headers_cache;
#endif #endif
prev->host_set = conf->host_set;
} }
return NGX_CONF_OK; return NGX_CONF_OK;
@@ -4027,6 +4192,12 @@ ngx_http_proxy_init_headers(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *conf,
src = conf->headers_source->elts; src = conf->headers_source->elts;
for (i = 0; i < conf->headers_source->nelts; i++) { for (i = 0; i < conf->headers_source->nelts; i++) {
if (src[i].key.len == 4
&& ngx_strncasecmp(src[i].key.data, (u_char *) "Host", 4) == 0)
{
conf->host_set = 1;
}
s = ngx_array_push(&headers_merged); s = ngx_array_push(&headers_merged);
if (s == NULL) { if (s == NULL) {
return NGX_ERROR; return NGX_ERROR;
+127
View File
@@ -0,0 +1,127 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_HTTP_PROXY_H_INCLUDED_
#define _NGX_HTTP_PROXY_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
typedef struct {
ngx_array_t caches; /* ngx_http_file_cache_t * */
} ngx_http_proxy_main_conf_t;
typedef struct {
ngx_str_t key_start;
ngx_str_t schema;
ngx_str_t host_header;
ngx_str_t port;
ngx_str_t uri;
} ngx_http_proxy_vars_t;
typedef struct {
ngx_array_t *flushes;
ngx_array_t *lengths;
ngx_array_t *values;
ngx_hash_t hash;
} ngx_http_proxy_headers_t;
typedef struct {
ngx_http_upstream_conf_t upstream;
ngx_array_t *body_flushes;
ngx_array_t *body_lengths;
ngx_array_t *body_values;
ngx_str_t body_source;
ngx_http_proxy_headers_t headers;
#if (NGX_HTTP_CACHE)
ngx_http_proxy_headers_t headers_cache;
#endif
ngx_array_t *headers_source;
ngx_uint_t host_set;
ngx_array_t *proxy_lengths;
ngx_array_t *proxy_values;
ngx_array_t *redirects;
ngx_array_t *cookie_domains;
ngx_array_t *cookie_paths;
ngx_array_t *cookie_flags;
ngx_http_complex_value_t *method;
ngx_str_t location;
ngx_str_t url;
#if (NGX_HTTP_CACHE)
ngx_http_complex_value_t cache_key;
#endif
ngx_http_proxy_vars_t vars;
ngx_flag_t redirect;
ngx_uint_t http_version;
ngx_uint_t headers_hash_max_size;
ngx_uint_t headers_hash_bucket_size;
#if (NGX_HTTP_SSL || NGX_COMPAT)
ngx_uint_t ssl;
ngx_uint_t ssl_protocols;
ngx_str_t ssl_ciphers;
ngx_uint_t ssl_verify_depth;
ngx_str_t ssl_trusted_certificate;
ngx_str_t ssl_crl;
ngx_array_t *ssl_conf_commands;
#endif
} ngx_http_proxy_loc_conf_t;
typedef struct {
ngx_http_status_t status;
ngx_http_chunked_t chunked;
ngx_http_proxy_vars_t vars;
off_t internal_body_length;
ngx_chain_t *free;
ngx_chain_t *busy;
ngx_buf_t *trailers;
unsigned head:1;
unsigned internal_chunked:1;
unsigned header_sent:1;
unsigned legacy:1;
} ngx_http_proxy_ctx_t;
ngx_int_t ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx,
ngx_http_proxy_loc_conf_t *plcf);
#if (NGX_HTTP_CACHE)
ngx_int_t ngx_http_proxy_create_key(ngx_http_request_t *r);
#endif
ngx_int_t ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r,
ngx_table_elt_t *h, size_t prefix);
ngx_int_t ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r,
ngx_table_elt_t *h);
#if (NGX_HTTP_V2)
ngx_int_t ngx_http_proxy_v2_handler(ngx_http_request_t *r);
#endif
extern ngx_module_t ngx_http_proxy_module;
#endif /* _NGX_HTTP_PROXY_H_INCLUDED_ */
+4160
View File
@@ -0,0 +1,4160 @@
/*
* Copyright (C) Maxim Dounin
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include <ngx_http_proxy_module.h>
typedef enum {
ngx_http_proxy_v2_st_start = 0,
ngx_http_proxy_v2_st_length_2,
ngx_http_proxy_v2_st_length_3,
ngx_http_proxy_v2_st_type,
ngx_http_proxy_v2_st_flags,
ngx_http_proxy_v2_st_stream_id,
ngx_http_proxy_v2_st_stream_id_2,
ngx_http_proxy_v2_st_stream_id_3,
ngx_http_proxy_v2_st_stream_id_4,
ngx_http_proxy_v2_st_payload,
ngx_http_proxy_v2_st_padding
} ngx_http_proxy_v2_state_e;
typedef struct {
size_t init_window;
size_t send_window;
size_t recv_window;
ngx_uint_t last_stream_id;
} ngx_http_proxy_v2_conn_t;
typedef struct {
ngx_http_proxy_ctx_t ctx;
ngx_http_proxy_v2_state_e state;
ngx_uint_t frame_state;
ngx_uint_t fragment_state;
ngx_chain_t *in;
ngx_chain_t *out;
ngx_chain_t *free;
ngx_chain_t *busy;
ngx_http_proxy_v2_conn_t *connection;
ngx_uint_t id;
ngx_uint_t pings;
ngx_uint_t settings;
off_t length;
ssize_t send_window;
size_t recv_window;
size_t rest;
ngx_uint_t stream_id;
u_char type;
u_char flags;
u_char padding;
ngx_uint_t error;
ngx_uint_t window_update;
ngx_uint_t setting_id;
ngx_uint_t setting_value;
u_char ping_data[8];
ngx_uint_t index;
ngx_str_t name;
ngx_str_t value;
u_char *field_end;
size_t field_length;
size_t field_rest;
u_char field_state;
unsigned literal:1;
unsigned field_huffman:1;
unsigned header_sent:1;
unsigned output_closed:1;
unsigned output_blocked:1;
unsigned parsing_headers:1;
unsigned end_stream:1;
unsigned done:1;
unsigned status:1;
unsigned rst:1;
unsigned goaway:1;
} ngx_http_proxy_v2_ctx_t;
typedef struct {
u_char length_0;
u_char length_1;
u_char length_2;
u_char type;
u_char flags;
u_char stream_id_0;
u_char stream_id_1;
u_char stream_id_2;
u_char stream_id_3;
} ngx_http_proxy_v2_frame_t;
static ngx_int_t ngx_http_proxy_v2_create_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_proxy_v2_reinit_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_proxy_v2_body_output_filter(void *data,
ngx_chain_t *in);
static ngx_int_t ngx_http_proxy_v2_process_header(ngx_http_request_t *r);
static ngx_int_t ngx_http_proxy_v2_filter_init(void *data);
static ngx_int_t ngx_http_proxy_v2_non_buffered_filter(void *data,
ssize_t bytes);
static ngx_int_t ngx_http_proxy_v2_body_filter(ngx_event_pipe_t *p,
ngx_buf_t *buf);
static ngx_int_t ngx_http_proxy_v2_process_control_frame(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b);
static ngx_int_t ngx_http_proxy_v2_skip_frame(ngx_http_proxy_v2_ctx_t *ctx,
ngx_buf_t *b);
static ngx_int_t ngx_http_proxy_v2_process_frames(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b);
static ngx_int_t ngx_http_proxy_v2_parse_frame(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b);
static ngx_int_t ngx_http_proxy_v2_parse_header(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b);
static ngx_int_t ngx_http_proxy_v2_parse_fragment(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b);
static ngx_int_t ngx_http_proxy_v2_validate_header_name(ngx_http_request_t *r,
ngx_str_t *s);
static ngx_int_t ngx_http_proxy_v2_validate_header_value(ngx_http_request_t *r,
ngx_str_t *s);
static ngx_int_t ngx_http_proxy_v2_parse_rst_stream(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b);
static ngx_int_t ngx_http_proxy_v2_parse_goaway(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b);
static ngx_int_t ngx_http_proxy_v2_parse_window_update(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b);
static ngx_int_t ngx_http_proxy_v2_parse_settings(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b);
static ngx_int_t ngx_http_proxy_v2_parse_ping(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b);
static ngx_int_t ngx_http_proxy_v2_send_settings_ack(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx);
static ngx_int_t ngx_http_proxy_v2_send_ping_ack(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx);
static ngx_int_t ngx_http_proxy_v2_send_window_update(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx);
static ngx_chain_t *ngx_http_proxy_v2_get_buf(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx);
static ngx_http_proxy_v2_ctx_t *
ngx_http_proxy_v2_get_ctx(ngx_http_request_t *r);
static ngx_int_t ngx_http_proxy_v2_get_connection_data(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_peer_connection_t *pc);
static void ngx_http_proxy_v2_cleanup(void *data);
static void ngx_http_proxy_v2_abort_request(ngx_http_request_t *r);
static void ngx_http_proxy_v2_finalize_request(ngx_http_request_t *r,
ngx_int_t rc);
static ngx_http_module_t ngx_http_proxy_v2_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_http_proxy_v2_module = {
NGX_MODULE_V1,
&ngx_http_proxy_v2_module_ctx, /* module context */
NULL, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static u_char ngx_http_proxy_v2_connection_start[] =
"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" /* connection preface */
"\x00\x00\x12\x04\x00\x00\x00\x00\x00" /* settings frame */
"\x00\x01\x00\x00\x00\x00" /* header table size */
"\x00\x02\x00\x00\x00\x00" /* disable push */
"\x00\x04\x7f\xff\xff\xff" /* initial window */
"\x00\x00\x04\x08\x00\x00\x00\x00\x00" /* window update frame */
"\x7f\xff\x00\x00";
ngx_int_t
ngx_http_proxy_v2_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_http_upstream_t *u;
ngx_http_proxy_v2_ctx_t *ctx;
ngx_http_proxy_loc_conf_t *plcf;
#if (NGX_HTTP_CACHE)
ngx_http_proxy_main_conf_t *pmcf;
#endif
if (ngx_http_upstream_create(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_v2_ctx_t));
if (ctx == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_http_set_ctx(r, ctx, ngx_http_proxy_v2_module);
ngx_http_set_ctx(r, &ctx->ctx, ngx_http_proxy_module);
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
plcf->upstream.preserve_output = 1;
u = r->upstream;
if (plcf->proxy_lengths == NULL) {
ctx->ctx.vars = plcf->vars;
u->schema = plcf->vars.schema;
#if (NGX_HTTP_SSL)
u->ssl = plcf->ssl;
#endif
} else {
if (ngx_http_proxy_eval(r, &ctx->ctx, plcf) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
}
#if (NGX_HTTP_SSL)
ngx_str_set(&u->ssl_alpn_protocol, NGX_HTTP_V2_ALPN_PROTO);
#endif
u->output.tag = (ngx_buf_tag_t) &ngx_http_proxy_v2_module;
u->conf = &plcf->upstream;
#if (NGX_HTTP_CACHE)
pmcf = ngx_http_get_module_main_conf(r, ngx_http_proxy_module);
u->caches = &pmcf->caches;
u->create_key = ngx_http_proxy_create_key;
#endif
u->create_request = ngx_http_proxy_v2_create_request;
u->reinit_request = ngx_http_proxy_v2_reinit_request;
u->process_header = ngx_http_proxy_v2_process_header;
u->abort_request = ngx_http_proxy_v2_abort_request;
u->finalize_request = ngx_http_proxy_v2_finalize_request;
if (plcf->redirects) {
u->rewrite_redirect = ngx_http_proxy_rewrite_redirect;
}
if (plcf->cookie_domains || plcf->cookie_paths || plcf->cookie_flags) {
u->rewrite_cookie = ngx_http_proxy_rewrite_cookie;
}
u->buffering = plcf->upstream.buffering;
u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
if (u->pipe == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
u->pipe->input_filter = ngx_http_proxy_v2_body_filter;
u->pipe->input_ctx = r;
u->input_filter_init = ngx_http_proxy_v2_filter_init;
u->input_filter = ngx_http_proxy_v2_non_buffered_filter;
u->input_filter_ctx = r;
u->accel = 1;
if (!plcf->upstream.request_buffering
&& plcf->body_values == NULL && plcf->upstream.pass_request_body)
{
r->request_body_no_buffering = 1;
}
rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return rc;
}
return NGX_DONE;
}
static ngx_int_t
ngx_http_proxy_v2_create_request(ngx_http_request_t *r)
{
u_char *p, *tmp, *key_tmp, *val_tmp, *headers_frame;
size_t len, tmp_len, key_len, val_len, uri_len,
loc_len, body_len;
uintptr_t escape;
ngx_buf_t *b;
ngx_str_t method, *host;
ngx_uint_t i, next, unparsed_uri;
ngx_chain_t *cl, *body;
ngx_list_part_t *part;
ngx_table_elt_t *header;
ngx_http_upstream_t *u;
ngx_http_proxy_v2_ctx_t *ctx;
ngx_http_script_code_pt code;
ngx_http_script_engine_t e, le;
ngx_http_proxy_headers_t *headers;
ngx_http_proxy_v2_frame_t *f;
ngx_http_proxy_loc_conf_t *plcf;
ngx_http_script_len_code_pt lcode;
u = r->upstream;
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
#if (NGX_HTTP_CACHE)
headers = u->cacheable ? &plcf->headers_cache : &plcf->headers;
#else
headers = &plcf->headers;
#endif
if (u->method.len) {
/* HEAD was changed to GET to cache response */
method = u->method;
} else if (plcf->method) {
if (ngx_http_complex_value(r, plcf->method, &method) != NGX_OK) {
return NGX_ERROR;
}
} else {
method = r->method_name;
}
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_v2_module);
if (method.len == 4
&& ngx_strncasecmp(method.data, (u_char *) "HEAD", 4) == 0)
{
ctx->ctx.head = 1;
}
len = sizeof(ngx_http_proxy_v2_connection_start) - 1
+ sizeof(ngx_http_proxy_v2_frame_t); /* headers frame */
/* :method header */
if ((method.len == 3 && ngx_strncmp(method.data, "GET", 3) == 0)
|| (method.len == 4 && ngx_strncmp(method.data, "POST", 4) == 0))
{
len += 1;
tmp_len = 0;
} else {
len += 1 + NGX_HTTP_V2_INT_OCTETS + method.len;
tmp_len = method.len;
}
/* :scheme header */
len += 1;
/* :path header */
escape = 0;
loc_len = 0;
unparsed_uri = 0;
if (plcf->proxy_lengths && ctx->ctx.vars.uri.len) {
uri_len = ctx->ctx.vars.uri.len;
} else if (ctx->ctx.vars.uri.len == 0 && r->valid_unparsed_uri) {
unparsed_uri = 1;
uri_len = r->unparsed_uri.len;
} else {
loc_len = (r->valid_location && ctx->ctx.vars.uri.len)
? ngx_min(plcf->location.len, r->uri.len) : 0;
if (r->quoted_uri || r->internal) {
escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len,
r->uri.len - loc_len, NGX_ESCAPE_URI);
}
uri_len = ctx->ctx.vars.uri.len + r->uri.len - loc_len + escape
+ sizeof("?") - 1 + r->args.len;
}
if (uri_len == 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"zero length URI to proxy");
return NGX_ERROR;
}
len += 1 + NGX_HTTP_V2_INT_OCTETS + uri_len;
if (tmp_len < uri_len) {
tmp_len = uri_len;
}
/* :authority header */
host = &ctx->ctx.vars.host_header;
if (!plcf->host_set) {
len += 1 + NGX_HTTP_V2_INT_OCTETS + host->len;
if (tmp_len < host->len) {
tmp_len = host->len;
}
}
/* other headers */
ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
ngx_http_script_flush_no_cacheable_variables(r, plcf->body_flushes);
ngx_http_script_flush_no_cacheable_variables(r, headers->flushes);
body_len = 0;
if (plcf->body_lengths) {
le.ip = plcf->body_lengths->elts;
le.request = r;
le.flushed = 1;
while (*(uintptr_t *) le.ip) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
body_len += lcode(&le);
}
ctx->ctx.internal_body_length = body_len;
len += sizeof(ngx_http_proxy_v2_frame_t);
len += body_len;
} else if (r->headers_in.chunked && r->reading_body) {
ctx->ctx.internal_body_length = -1;
} else {
ctx->ctx.internal_body_length = r->headers_in.content_length_n;
}
le.ip = headers->lengths->elts;
le.request = r;
le.flushed = 1;
while (*(uintptr_t *) le.ip) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
key_len = lcode(&le);
for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
}
le.ip += sizeof(uintptr_t);
if (val_len == 0) {
continue;
}
len += 1 + NGX_HTTP_V2_INT_OCTETS + key_len
+ NGX_HTTP_V2_INT_OCTETS + val_len;
if (tmp_len < key_len) {
tmp_len = key_len;
}
if (tmp_len < val_len) {
tmp_len = val_len;
}
}
if (plcf->upstream.pass_request_headers) {
part = &r->headers_in.headers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (ngx_hash_find(&headers->hash, header[i].hash,
header[i].lowcase_key, header[i].key.len))
{
continue;
}
len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len
+ NGX_HTTP_V2_INT_OCTETS + header[i].value.len;
if (tmp_len < header[i].key.len) {
tmp_len = header[i].key.len;
}
if (tmp_len < header[i].value.len) {
tmp_len = header[i].value.len;
}
}
}
/* continuation frames */
len += sizeof(ngx_http_proxy_v2_frame_t)
* (len / NGX_HTTP_V2_DEFAULT_FRAME_SIZE);
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) {
return NGX_ERROR;
}
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = b;
cl->next = NULL;
tmp = ngx_palloc(r->pool, tmp_len * 3);
if (tmp == NULL) {
return NGX_ERROR;
}
key_tmp = tmp + tmp_len;
val_tmp = tmp + 2 * tmp_len;
/* connection preface */
b->last = ngx_copy(b->last, ngx_http_proxy_v2_connection_start,
sizeof(ngx_http_proxy_v2_connection_start) - 1);
/* headers frame */
headers_frame = b->last;
f = (ngx_http_proxy_v2_frame_t *) b->last;
b->last += sizeof(ngx_http_proxy_v2_frame_t);
f->length_0 = 0;
f->length_1 = 0;
f->length_2 = 0;
f->type = NGX_HTTP_V2_HEADERS_FRAME;
f->flags = 0;
f->stream_id_0 = 0;
f->stream_id_1 = 0;
f->stream_id_2 = 0;
f->stream_id_3 = 1;
if (method.len == 3 && ngx_strncmp(method.data, "GET", 3) == 0) {
*b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_GET_INDEX);
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header: \":method: GET\"");
} else if (method.len == 4 && ngx_strncmp(method.data, "POST", 4) == 0) {
*b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_POST_INDEX);
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header: \":method: POST\"");
} else {
*b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_METHOD_INDEX);
b->last = ngx_http_v2_write_value(b->last, method.data,
method.len, tmp);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header: \":method: %V\"", &method);
}
#if (NGX_HTTP_SSL)
if (u->ssl) {
*b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTPS_INDEX);
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header: \":scheme: https\"");
} else
#endif
{
*b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX);
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header: \":scheme: http\"");
}
if (plcf->proxy_lengths && ctx->ctx.vars.uri.len) {
*b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);
b->last = ngx_http_v2_write_value(b->last, ctx->ctx.vars.uri.data,
ctx->ctx.vars.uri.len, tmp);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header: \":path: %V\"", &ctx->ctx.vars.uri);
} else if (unparsed_uri) {
if (r->unparsed_uri.len == 1 && r->unparsed_uri.data[0] == '/') {
*b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_PATH_ROOT_INDEX);
} else {
*b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);
b->last = ngx_http_v2_write_value(b->last, r->unparsed_uri.data,
r->unparsed_uri.len, tmp);
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header: \":path: %V\"", &r->unparsed_uri);
} else {
p = val_tmp;
if (r->valid_location) {
p = ngx_copy(p, ctx->ctx.vars.uri.data, ctx->ctx.vars.uri.len);
}
if (escape) {
ngx_escape_uri(p, r->uri.data + loc_len,
r->uri.len - loc_len, NGX_ESCAPE_URI);
p += r->uri.len - loc_len + escape;
} else {
p = ngx_copy(p, r->uri.data + loc_len, r->uri.len - loc_len);
}
if (r->args.len > 0) {
*p++ = '?';
p = ngx_copy(p, r->args.data, r->args.len);
}
*b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);
b->last = ngx_http_v2_write_value(b->last, val_tmp, p - val_tmp, tmp);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header: \":path: %*s\"", p - val_tmp,
val_tmp);
}
if (!plcf->host_set) {
*b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_AUTHORITY_INDEX);
b->last = ngx_http_v2_write_value(b->last, host->data, host->len, tmp);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header: \":authority: %V\"", host);
}
ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
e.ip = headers->values->elts;
e.request = r;
e.flushed = 1;
le.ip = headers->lengths->elts;
while (*(uintptr_t *) le.ip) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
key_len = lcode(&le);
for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
}
le.ip += sizeof(uintptr_t);
if (val_len == 0) {
e.skip = 1;
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
e.ip += sizeof(uintptr_t);
e.skip = 0;
continue;
}
*b->last++ = 0;
e.pos = key_tmp;
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
b->last = ngx_http_v2_write_name(b->last, key_tmp, key_len, tmp);
e.pos = val_tmp;
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
e.ip += sizeof(uintptr_t);
b->last = ngx_http_v2_write_value(b->last, val_tmp, val_len, tmp);
#if (NGX_DEBUG)
if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) {
ngx_strlow(key_tmp, key_tmp, key_len);
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header: \"%*s: %*s\"",
key_len, key_tmp, val_len, val_tmp);
}
#endif
}
if (plcf->upstream.pass_request_headers) {
part = &r->headers_in.headers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (ngx_hash_find(&headers->hash, header[i].hash,
header[i].lowcase_key, header[i].key.len))
{
continue;
}
*b->last++ = 0;
b->last = ngx_http_v2_write_name(b->last, header[i].key.data,
header[i].key.len, tmp);
b->last = ngx_http_v2_write_value(b->last, header[i].value.data,
header[i].value.len, tmp);
#if (NGX_DEBUG)
if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) {
ngx_strlow(tmp, header[i].key.data, header[i].key.len);
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header: \"%*s: %V\"",
header[i].key.len, tmp, &header[i].value);
}
#endif
}
}
/* update headers frame length */
len = b->last - headers_frame - sizeof(ngx_http_proxy_v2_frame_t);
if (len > NGX_HTTP_V2_DEFAULT_FRAME_SIZE) {
len = NGX_HTTP_V2_DEFAULT_FRAME_SIZE;
next = 1;
} else {
next = 0;
}
f = (ngx_http_proxy_v2_frame_t *) headers_frame;
f->length_0 = (u_char) ((len >> 16) & 0xff);
f->length_1 = (u_char) ((len >> 8) & 0xff);
f->length_2 = (u_char) (len & 0xff);
/* create additional continuation frames */
p = headers_frame;
while (next) {
p += sizeof(ngx_http_proxy_v2_frame_t) + NGX_HTTP_V2_DEFAULT_FRAME_SIZE;
len = b->last - p;
ngx_memmove(p + sizeof(ngx_http_proxy_v2_frame_t), p, len);
b->last += sizeof(ngx_http_proxy_v2_frame_t);
if (len > NGX_HTTP_V2_DEFAULT_FRAME_SIZE) {
len = NGX_HTTP_V2_DEFAULT_FRAME_SIZE;
next = 1;
} else {
next = 0;
}
f = (ngx_http_proxy_v2_frame_t *) p;
f->length_0 = (u_char) ((len >> 16) & 0xff);
f->length_1 = (u_char) ((len >> 8) & 0xff);
f->length_2 = (u_char) (len & 0xff);
f->type = NGX_HTTP_V2_CONTINUATION_FRAME;
f->flags = 0;
f->stream_id_0 = 0;
f->stream_id_1 = 0;
f->stream_id_2 = 0;
f->stream_id_3 = 1;
}
f->flags |= NGX_HTTP_V2_END_HEADERS_FLAG;
if (plcf->body_values) {
f = (ngx_http_proxy_v2_frame_t *) b->last;
b->last += sizeof(ngx_http_proxy_v2_frame_t);
f->length_0 = (u_char) ((body_len >> 16) & 0xff);
f->length_1 = (u_char) ((body_len >> 8) & 0xff);
f->length_2 = (u_char) (body_len & 0xff);
f->type = NGX_HTTP_V2_DATA_FRAME;
f->flags = NGX_HTTP_V2_END_STREAM_FLAG;
f->stream_id_0 = 0;
f->stream_id_1 = 0;
f->stream_id_2 = 0;
f->stream_id_3 = 1;
e.ip = plcf->body_values->elts;
e.pos = b->last;
e.request = r;
e.flushed = 1;
e.skip = 0;
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
b->last = e.pos;
}
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header: %*xs%s, len: %uz",
(size_t) ngx_min(b->last - b->pos, 256), b->pos,
b->last - b->pos > 256 ? "..." : "",
b->last - b->pos);
if (r->request_body_no_buffering) {
u->request_bufs = cl;
} else if (plcf->body_values == NULL && plcf->upstream.pass_request_body) {
body = u->request_bufs;
u->request_bufs = cl;
if (body == NULL) {
f = (ngx_http_proxy_v2_frame_t *) headers_frame;
f->flags |= NGX_HTTP_V2_END_STREAM_FLAG;
}
while (body) {
b = ngx_alloc_buf(r->pool);
if (b == NULL) {
return NGX_ERROR;
}
ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));
cl->next = ngx_alloc_chain_link(r->pool);
if (cl->next == NULL) {
return NGX_ERROR;
}
cl = cl->next;
cl->buf = b;
body = body->next;
}
b->last_buf = 1;
} else {
u->request_bufs = cl;
if (plcf->body_values == NULL) {
f = (ngx_http_proxy_v2_frame_t *) headers_frame;
f->flags |= NGX_HTTP_V2_END_STREAM_FLAG;
}
}
u->output.output_filter = ngx_http_proxy_v2_body_output_filter;
u->output.filter_ctx = r;
b->flush = 1;
cl->next = NULL;
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_v2_reinit_request(ngx_http_request_t *r)
{
ngx_http_proxy_v2_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_v2_module);
if (ctx == NULL) {
return NGX_OK;
}
ctx->state = 0;
ctx->header_sent = 0;
ctx->output_closed = 0;
ctx->output_blocked = 0;
ctx->parsing_headers = 0;
ctx->end_stream = 0;
ctx->done = 0;
ctx->status = 0;
ctx->rst = 0;
ctx->goaway = 0;
ctx->connection = NULL;
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_v2_body_output_filter(void *data, ngx_chain_t *in)
{
ngx_http_request_t *r = data;
off_t file_pos;
u_char *p, *pos, *start;
size_t len, limit;
ngx_buf_t *b;
ngx_int_t rc;
ngx_uint_t next, last;
ngx_chain_t *cl, *out, *ln, **ll;
ngx_http_upstream_t *u;
ngx_http_proxy_v2_ctx_t *ctx;
ngx_http_proxy_v2_frame_t *f;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy output filter");
ctx = ngx_http_proxy_v2_get_ctx(r);
if (ctx == NULL) {
return NGX_ERROR;
}
if (in) {
if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
return NGX_ERROR;
}
}
out = NULL;
ll = &out;
if (!ctx->header_sent) {
/* first buffer contains headers */
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy output header");
ctx->header_sent = 1;
if (ctx->id != 1) {
/*
* keepalive connection: skip connection preface,
* update stream identifiers
*/
b = ctx->in->buf;
b->pos += sizeof(ngx_http_proxy_v2_connection_start) - 1;
p = b->pos;
while (p < b->last) {
f = (ngx_http_proxy_v2_frame_t *) p;
p += sizeof(ngx_http_proxy_v2_frame_t);
f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff);
f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff);
f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff);
f->stream_id_3 = (u_char) (ctx->id & 0xff);
p += (f->length_0 << 16) + (f->length_1 << 8) + f->length_2;
}
}
if (ctx->in->buf->last_buf) {
ctx->output_closed = 1;
}
*ll = ctx->in;
ll = &ctx->in->next;
ctx->in = ctx->in->next;
}
if (ctx->out) {
/* queued control frames */
*ll = ctx->out;
for (cl = ctx->out, ll = &cl->next; cl; cl = cl->next) {
ll = &cl->next;
}
ctx->out = NULL;
}
f = NULL;
last = 0;
limit = ngx_max(0, ctx->send_window);
if (limit > ctx->connection->send_window) {
limit = ctx->connection->send_window;
}
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy output limit: %uz w:%z:%uz",
limit, ctx->send_window, ctx->connection->send_window);
#if (NGX_SUPPRESS_WARN)
file_pos = 0;
pos = NULL;
cl = NULL;
#endif
in = ctx->in;
while (in && limit > 0) {
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
"http proxy output in l:%d f:%d %p, pos %p, size: %z "
"file: %O, size: %O",
in->buf->last_buf,
in->buf->in_file,
in->buf->start, in->buf->pos,
in->buf->last - in->buf->pos,
in->buf->file_pos,
in->buf->file_last - in->buf->file_pos);
if (ngx_buf_special(in->buf)) {
goto next;
}
if (in->buf->in_file) {
file_pos = in->buf->file_pos;
} else {
pos = in->buf->pos;
}
next = 0;
do {
cl = ngx_http_proxy_v2_get_buf(r, ctx);
if (cl == NULL) {
return NGX_ERROR;
}
b = cl->buf;
f = (ngx_http_proxy_v2_frame_t *) b->last;
b->last += sizeof(ngx_http_proxy_v2_frame_t);
*ll = cl;
ll = &cl->next;
cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
if (cl == NULL) {
return NGX_ERROR;
}
b = cl->buf;
start = b->start;
ngx_memcpy(b, in->buf, sizeof(ngx_buf_t));
/*
* restore b->start to preserve memory allocated in the buffer,
* to reuse it later for headers and control frames
*/
b->start = start;
if (in->buf->in_file) {
b->file_pos = file_pos;
file_pos += ngx_min(NGX_HTTP_V2_DEFAULT_FRAME_SIZE, limit);
if (file_pos >= in->buf->file_last) {
file_pos = in->buf->file_last;
next = 1;
}
b->file_last = file_pos;
len = (ngx_uint_t) (file_pos - b->file_pos);
} else {
b->pos = pos;
pos += ngx_min(NGX_HTTP_V2_DEFAULT_FRAME_SIZE, limit);
if (pos >= in->buf->last) {
pos = in->buf->last;
next = 1;
}
b->last = pos;
len = (ngx_uint_t) (pos - b->pos);
}
b->tag = (ngx_buf_tag_t) &ngx_http_proxy_v2_body_output_filter;
b->shadow = in->buf;
b->last_shadow = next;
b->last_buf = 0;
b->last_in_chain = 0;
*ll = cl;
ll = &cl->next;
f->length_0 = (u_char) ((len >> 16) & 0xff);
f->length_1 = (u_char) ((len >> 8) & 0xff);
f->length_2 = (u_char) (len & 0xff);
f->type = NGX_HTTP_V2_DATA_FRAME;
f->flags = 0;
f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff);
f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff);
f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff);
f->stream_id_3 = (u_char) (ctx->id & 0xff);
limit -= len;
ctx->send_window -= len;
ctx->connection->send_window -= len;
} while (!next && limit > 0);
if (!next) {
/*
* if the buffer wasn't fully sent due to flow control limits,
* preserve position for future use
*/
if (in->buf->in_file) {
in->buf->file_pos = file_pos;
} else {
in->buf->pos = pos;
}
break;
}
next:
if (in->buf->last_buf) {
last = 1;
}
ln = in;
in = in->next;
ngx_free_chain(r->pool, ln);
}
ctx->in = in;
if (last) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy output last");
ctx->output_closed = 1;
if (f) {
f->flags |= NGX_HTTP_V2_END_STREAM_FLAG;
} else {
cl = ngx_http_proxy_v2_get_buf(r, ctx);
if (cl == NULL) {
return NGX_ERROR;
}
b = cl->buf;
f = (ngx_http_proxy_v2_frame_t *) b->last;
b->last += sizeof(ngx_http_proxy_v2_frame_t);
f->length_0 = 0;
f->length_1 = 0;
f->length_2 = 0;
f->type = NGX_HTTP_V2_DATA_FRAME;
f->flags = NGX_HTTP_V2_END_STREAM_FLAG;
f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff);
f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff);
f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff);
f->stream_id_3 = (u_char) (ctx->id & 0xff);
*ll = cl;
ll = &cl->next;
}
cl->buf->last_buf = 1;
}
*ll = NULL;
#if (NGX_DEBUG)
for (cl = out; cl; cl = cl->next) {
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
"http proxy output out l:%d f:%d %p, pos %p, size: %z "
"file: %O, size: %O",
cl->buf->last_buf,
cl->buf->in_file,
cl->buf->start, cl->buf->pos,
cl->buf->last - cl->buf->pos,
cl->buf->file_pos,
cl->buf->file_last - cl->buf->file_pos);
}
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy output limit: %uz w:%z:%uz",
limit, ctx->send_window, ctx->connection->send_window);
#endif
rc = ngx_chain_writer(&r->upstream->writer, out);
ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out,
(ngx_buf_tag_t) &ngx_http_proxy_v2_body_output_filter);
for (cl = ctx->free; cl; cl = cl->next) {
/* mark original buffers as sent */
if (cl->buf->shadow) {
if (cl->buf->last_shadow) {
b = cl->buf->shadow;
b->pos = b->last;
}
cl->buf->shadow = NULL;
}
}
if (rc == NGX_OK && ctx->in) {
rc = NGX_AGAIN;
}
if (rc == NGX_AGAIN) {
ctx->output_blocked = 1;
} else {
ctx->output_blocked = 0;
}
if (ctx->done) {
/*
* We have already got the response and were sending some additional
* control frames. Even if there is still something unsent, stop
* here anyway.
*/
u = r->upstream;
u->length = 0;
u->pipe->length = 0;
if (ctx->in == NULL
&& ctx->out == NULL
&& ctx->output_closed
&& !ctx->output_blocked
&& !ctx->goaway
&& ctx->state == ngx_http_proxy_v2_st_start)
{
u->keepalive = 1;
}
ngx_post_event(u->peer.connection->read, &ngx_posted_events);
}
return rc;
}
static ngx_int_t
ngx_http_proxy_v2_process_header(ngx_http_request_t *r)
{
u_char *pos;
ngx_str_t *status_line;
ngx_int_t rc, status;
ngx_buf_t *b;
ngx_table_elt_t *h;
ngx_http_upstream_t *u;
ngx_http_proxy_v2_ctx_t *ctx;
ngx_http_upstream_header_t *hh;
ngx_http_upstream_main_conf_t *umcf;
u = r->upstream;
b = &u->buffer;
pos = b->pos;
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy response: %*xs%s, len: %uz",
(size_t) ngx_min(b->last - b->pos, 256),
b->pos, b->last - b->pos > 256 ? "..." : "",
b->last - b->pos);
ctx = ngx_http_proxy_v2_get_ctx(r);
if (ctx == NULL) {
return NGX_ERROR;
}
umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
for ( ;; ) {
if (ctx->state < ngx_http_proxy_v2_st_payload) {
rc = ngx_http_proxy_v2_parse_frame(r, ctx, b);
if (rc == NGX_AGAIN) {
/*
* there can be a lot of window update frames,
* so we reset buffer if it is empty and we haven't
* started parsing headers yet
*/
if (!ctx->parsing_headers) {
b->pos = pos;
b->last = b->pos;
}
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
/*
* RFC 7540 says that implementations MUST discard frames
* that have unknown or unsupported types. However, extension
* frames that appear in the middle of a header block are
* not permitted. Also, for obvious reasons CONTINUATION frames
* cannot appear before headers, and DATA frames are not expected
* to appear before all headers are parsed.
*/
if (ctx->type == NGX_HTTP_V2_DATA_FRAME
|| (ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME
&& !ctx->parsing_headers)
|| (ctx->type != NGX_HTTP_V2_CONTINUATION_FRAME
&& ctx->parsing_headers))
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent unexpected http2 frame: %d",
ctx->type);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
if (ctx->stream_id && ctx->stream_id != ctx->id) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent frame for unknown stream %ui",
ctx->stream_id);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
}
/* frame payload */
if (u->peer.connection) {
if (ctx->type == NGX_HTTP_V2_RST_STREAM_FRAME) {
rc = ngx_http_proxy_v2_parse_rst_stream(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream rejected request with error %ui",
ctx->error);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
rc = ngx_http_proxy_v2_process_control_frame(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
if (rc == NGX_OK) {
continue;
}
}
if (ctx->type != NGX_HTTP_V2_HEADERS_FRAME
&& ctx->type != NGX_HTTP_V2_CONTINUATION_FRAME)
{
/* priority, unknown frames */
rc = ngx_http_proxy_v2_skip_frame(ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
continue;
}
/* headers */
for ( ;; ) {
rc = ngx_http_proxy_v2_parse_header(r, ctx, b);
if (rc == NGX_AGAIN) {
break;
}
if (rc == NGX_OK) {
/* a header line has been parsed successfully */
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header: \"%V: %V\"",
&ctx->name, &ctx->value);
if (ctx->name.len && ctx->name.data[0] == ':') {
if (ctx->name.len != sizeof(":status") - 1
|| ngx_strncmp(ctx->name.data, ":status",
sizeof(":status") - 1)
!= 0)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid header \"%V: %V\"",
&ctx->name, &ctx->value);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
if (ctx->status) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent duplicate :status header");
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
status_line = &ctx->value;
if (status_line->len != 3) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid :status \"%V\"",
status_line);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
status = ngx_atoi(status_line->data, 3);
if (status == NGX_ERROR) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid :status \"%V\"",
status_line);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
if (status < NGX_HTTP_OK && status != NGX_HTTP_EARLY_HINTS)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent unexpected :status \"%V\"",
status_line);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
u->headers_in.status_n = status;
if (u->state && u->state->status == 0) {
u->state->status = status;
}
ctx->status = 1;
continue;
} else if (!ctx->status) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent no :status header");
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
h = ngx_list_push(&u->headers_in.headers);
if (h == NULL) {
return NGX_ERROR;
}
h->key = ctx->name;
h->value = ctx->value;
h->lowcase_key = h->key.data;
h->hash = ngx_hash_key(h->key.data, h->key.len);
if (u->headers_in.status_n == NGX_HTTP_EARLY_HINTS) {
continue;
}
hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
h->lowcase_key, h->key.len);
if (hh) {
rc = hh->handler(r, h, hh->offset);
if (rc != NGX_OK) {
return rc;
}
}
continue;
}
if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
/* a whole header has been parsed successfully */
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header done");
if (u->headers_in.status_n == NGX_HTTP_EARLY_HINTS) {
if (ctx->end_stream) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream prematurely closed stream");
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
ctx->status = 0;
return NGX_HTTP_UPSTREAM_EARLY_HINTS;
}
if (ctx->end_stream
&& ctx->in == NULL
&& ctx->out == NULL
&& ctx->output_closed
&& !ctx->output_blocked
&& !ctx->goaway
&& b->last == b->pos)
{
u->keepalive = 1;
}
return NGX_OK;
}
/* there was error while a header line parsing */
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid header");
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
/* rc == NGX_AGAIN */
if (ctx->rest == 0) {
ctx->state = ngx_http_proxy_v2_st_start;
continue;
}
return NGX_AGAIN;
}
}
static ngx_int_t
ngx_http_proxy_v2_filter_init(void *data)
{
ngx_http_request_t *r = data;
ngx_http_upstream_t *u;
ngx_http_proxy_v2_ctx_t *ctx;
u = r->upstream;
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_v2_module);
if (ctx == NULL) {
return NGX_ERROR;
}
if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT
|| u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED
|| ctx->ctx.head)
{
ctx->length = 0;
} else {
ctx->length = u->headers_in.content_length_n;
}
if (ctx->end_stream) {
if (ctx->length > 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream prematurely closed stream");
return NGX_ERROR;
}
u->length = 0;
u->pipe->length = 0;
ctx->done = 1;
} else {
u->length = 1;
u->pipe->length = 1;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_v2_non_buffered_filter(void *data, ssize_t bytes)
{
ngx_http_request_t *r = data;
ngx_int_t rc;
ngx_buf_t *b, *buf;
ngx_chain_t *cl, **ll;
ngx_http_upstream_t *u;
ngx_http_proxy_v2_ctx_t *ctx;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy filter bytes:%z", bytes);
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_v2_module);
if (ctx == NULL) {
return NGX_ERROR;
}
u = r->upstream;
b = &u->buffer;
b->pos = b->last;
b->last += bytes;
for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
ll = &cl->next;
}
for ( ;; ) {
rc = ngx_http_proxy_v2_process_frames(r, ctx, b);
if (rc == NGX_OK) {
cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);
if (cl == NULL) {
return NGX_ERROR;
}
*ll = cl;
ll = &cl->next;
buf = cl->buf;
buf->flush = 1;
buf->memory = 1;
buf->pos = b->pos;
buf->tag = u->output.tag;
if (b->last - b->pos >= (ssize_t) ctx->rest - ctx->padding) {
b->pos += ctx->rest - ctx->padding;
buf->last = b->pos;
ctx->rest = ctx->padding;
} else {
ctx->rest -= b->last - b->pos;
b->pos = b->last;
buf->last = b->pos;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy output buf %p", buf->pos);
if (ctx->length != -1) {
if (buf->last - buf->pos > ctx->length) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent response body larger "
"than indicated content length");
return NGX_ERROR;
}
ctx->length -= buf->last - buf->pos;
}
continue;
}
if (rc == NGX_DONE) {
u->length = 0;
break;
}
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
/* invalid response */
return NGX_ERROR;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_v2_body_filter(ngx_event_pipe_t *p, ngx_buf_t *b)
{
ngx_int_t rc;
ngx_buf_t *buf, **prev;
ngx_chain_t *cl;
ngx_http_request_t *r;
ngx_http_proxy_v2_ctx_t *ctx;
if (b->pos == b->last) {
return NGX_OK;
}
r = p->input_ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_v2_module);
if (ctx == NULL) {
return NGX_ERROR;
}
buf = NULL;
prev = &b->shadow;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy filter bytes:%z", b->last - b->pos);
for ( ;; ) {
rc = ngx_http_proxy_v2_process_frames(r, ctx, b);
if (rc == NGX_OK) {
/* copy data frame payload for buffering */
cl = ngx_chain_get_free_buf(p->pool, &p->free);
if (cl == NULL) {
return NGX_ERROR;
}
buf = cl->buf;
ngx_memzero(buf, sizeof(ngx_buf_t));
buf->pos = b->pos;
buf->start = b->start;
buf->end = b->end;
buf->tag = p->tag;
buf->temporary = 1;
buf->recycled = 1;
*prev = buf;
prev = &buf->shadow;
if (p->in) {
*p->last_in = cl;
} else {
p->in = cl;
}
p->last_in = &cl->next;
/* STUB */ buf->num = b->num;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy copy buf %p", buf->pos);
if (b->last - b->pos >= (ssize_t) ctx->rest - ctx->padding) {
b->pos += ctx->rest - ctx->padding;
buf->last = b->pos;
ctx->rest = ctx->padding;
} else {
ctx->rest -= b->last - b->pos;
b->pos = b->last;
buf->last = b->pos;
}
if (ctx->length != -1) {
if (buf->last - buf->pos > ctx->length) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent response body larger "
"than indicated content length");
return NGX_ERROR;
}
ctx->length -= buf->last - buf->pos;
}
continue;
}
if (rc == NGX_DONE) {
p->length = 0;
break;
}
if (rc == NGX_AGAIN) {
break;
}
/* invalid response */
return NGX_ERROR;
}
if (buf) {
buf->shadow = b;
buf->last_shadow = 1;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
"input buf %p %z", buf->pos, buf->last - buf->pos);
return NGX_OK;
}
/* there is no data record in the buf, add it to free chain */
if (ngx_event_pipe_add_free_buf(p, b) != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_v2_process_control_frame(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
{
ngx_int_t rc;
ngx_http_upstream_t *u;
u = r->upstream;
if (ctx->type == NGX_HTTP_V2_GOAWAY_FRAME) {
rc = ngx_http_proxy_v2_parse_goaway(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
/*
* If stream_id is lower than one we use, our
* request won't be processed and needs to be retried.
* If stream_id is greater or equal to the one we use,
* we can continue normally (except we can't use this
* connection for additional requests). If there is
* a real error, the connection will be closed.
*/
if (ctx->stream_id < ctx->id) {
/* TODO: we can retry non-idempotent requests */
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent goaway with error %ui",
ctx->error);
return NGX_ERROR;
}
ctx->goaway = 1;
return NGX_OK;
}
if (ctx->type == NGX_HTTP_V2_WINDOW_UPDATE_FRAME) {
rc = ngx_http_proxy_v2_parse_window_update(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (ctx->in) {
ngx_post_event(u->peer.connection->write, &ngx_posted_events);
}
return NGX_OK;
}
if (ctx->type == NGX_HTTP_V2_SETTINGS_FRAME) {
rc = ngx_http_proxy_v2_parse_settings(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (ctx->in) {
ngx_post_event(u->peer.connection->write, &ngx_posted_events);
}
return NGX_OK;
}
if (ctx->type == NGX_HTTP_V2_PING_FRAME) {
rc = ngx_http_proxy_v2_parse_ping(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
ngx_post_event(u->peer.connection->write, &ngx_posted_events);
return NGX_OK;
}
if (ctx->type == NGX_HTTP_V2_PUSH_PROMISE_FRAME) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent unexpected push promise frame");
return NGX_ERROR;
}
return NGX_DECLINED;
}
static ngx_int_t
ngx_http_proxy_v2_skip_frame(ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
{
if (b->last - b->pos < (ssize_t) ctx->rest) {
ctx->rest -= b->last - b->pos;
b->pos = b->last;
return NGX_AGAIN;
}
b->pos += ctx->rest;
ctx->rest = 0;
ctx->state = ngx_http_proxy_v2_st_start;
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_v2_process_frames(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
{
ngx_int_t rc;
ngx_table_elt_t *h;
ngx_http_upstream_t *u;
u = r->upstream;
for ( ;; ) {
if (ctx->state < ngx_http_proxy_v2_st_payload) {
rc = ngx_http_proxy_v2_parse_frame(r, ctx, b);
if (rc == NGX_AGAIN) {
if (ctx->done) {
if (ctx->length > 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream prematurely closed stream");
return NGX_ERROR;
}
/*
* We have finished parsing the response and the
* remaining control frames. If there are unsent
* control frames, post a write event to send them.
*/
if (ctx->out) {
ngx_post_event(u->peer.connection->write,
&ngx_posted_events);
return NGX_AGAIN;
}
if (ctx->in == NULL
&& ctx->output_closed
&& !ctx->output_blocked
&& !ctx->goaway
&& ctx->state == ngx_http_proxy_v2_st_start)
{
u->keepalive = 1;
}
return NGX_DONE;
}
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if ((ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME
&& !ctx->parsing_headers)
|| (ctx->type != NGX_HTTP_V2_CONTINUATION_FRAME
&& ctx->parsing_headers))
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent unexpected http2 frame: %d",
ctx->type);
return NGX_ERROR;
}
if (ctx->type == NGX_HTTP_V2_DATA_FRAME) {
if (ctx->stream_id != ctx->id) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent data frame "
"for unknown stream %ui",
ctx->stream_id);
return NGX_ERROR;
}
if (ctx->rest > ctx->recv_window) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream violated stream flow control, "
"received %uz data frame with window %uz",
ctx->rest, ctx->recv_window);
return NGX_ERROR;
}
if (ctx->rest > ctx->connection->recv_window) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream violated connection flow control, "
"received %uz data frame with window %uz",
ctx->rest, ctx->connection->recv_window);
return NGX_ERROR;
}
ctx->recv_window -= ctx->rest;
ctx->connection->recv_window -= ctx->rest;
if (ctx->connection->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4
|| ctx->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4)
{
if (ngx_http_proxy_v2_send_window_update(r, ctx)
!= NGX_OK)
{
return NGX_ERROR;
}
ngx_post_event(u->peer.connection->write,
&ngx_posted_events);
}
}
if (ctx->stream_id && ctx->stream_id != ctx->id) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent frame for unknown stream %ui",
ctx->stream_id);
return NGX_ERROR;
}
if (ctx->stream_id && ctx->done
&& ctx->type != NGX_HTTP_V2_RST_STREAM_FRAME
&& ctx->type != NGX_HTTP_V2_WINDOW_UPDATE_FRAME)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent frame for closed stream %ui",
ctx->stream_id);
return NGX_ERROR;
}
ctx->padding = 0;
}
if (ctx->state == ngx_http_proxy_v2_st_padding) {
if (b->last - b->pos < (ssize_t) ctx->rest) {
ctx->rest -= b->last - b->pos;
b->pos = b->last;
return NGX_AGAIN;
}
b->pos += ctx->rest;
ctx->rest = 0;
ctx->state = ngx_http_proxy_v2_st_start;
if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) {
ctx->done = 1;
}
continue;
}
/* frame payload */
if (ctx->type == NGX_HTTP_V2_RST_STREAM_FRAME) {
rc = ngx_http_proxy_v2_parse_rst_stream(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (ctx->error || !ctx->done) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream rejected request with error %ui",
ctx->error);
return NGX_ERROR;
}
if (ctx->rst) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent frame for closed stream %ui",
ctx->stream_id);
return NGX_ERROR;
}
ctx->rst = 1;
continue;
}
rc = ngx_http_proxy_v2_process_control_frame(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (rc == NGX_OK) {
continue;
}
if (ctx->type == NGX_HTTP_V2_HEADERS_FRAME
|| ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME)
{
for ( ;; ) {
rc = ngx_http_proxy_v2_parse_header(r, ctx, b);
if (rc == NGX_AGAIN) {
break;
}
if (rc == NGX_OK) {
/* a header line has been parsed successfully */
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy trailer: \"%V: %V\"",
&ctx->name, &ctx->value);
if (ctx->name.len && ctx->name.data[0] == ':') {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid "
"trailer \"%V: %V\"",
&ctx->name, &ctx->value);
return NGX_ERROR;
}
h = ngx_list_push(&u->headers_in.trailers);
if (h == NULL) {
return NGX_ERROR;
}
h->key = ctx->name;
h->value = ctx->value;
h->lowcase_key = h->key.data;
h->hash = ngx_hash_key(h->key.data, h->key.len);
continue;
}
if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
/* a whole header has been parsed successfully */
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy trailer done");
if (ctx->end_stream) {
ctx->done = 1;
break;
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent trailer without "
"end stream flag");
return NGX_ERROR;
}
/* there was error while a header line parsing */
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid trailer");
return NGX_ERROR;
}
if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
continue;
}
/* rc == NGX_AGAIN */
if (ctx->rest == 0) {
ctx->state = ngx_http_proxy_v2_st_start;
continue;
}
return NGX_AGAIN;
}
if (ctx->type != NGX_HTTP_V2_DATA_FRAME) {
/* priority, unknown frames */
rc = ngx_http_proxy_v2_skip_frame(ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
continue;
}
/*
* data frame:
*
* +---------------+
* |Pad Length? (8)|
* +---------------+-----------------------------------------------+
* | Data (*) ...
* +---------------------------------------------------------------+
* | Padding (*) ...
* +---------------------------------------------------------------+
*/
if (ctx->flags & NGX_HTTP_V2_PADDED_FLAG) {
if (ctx->rest == 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent too short http2 frame");
return NGX_ERROR;
}
if (b->pos == b->last) {
return NGX_AGAIN;
}
ctx->flags &= ~NGX_HTTP_V2_PADDED_FLAG;
ctx->padding = *b->pos++;
ctx->rest -= 1;
if (ctx->padding > ctx->rest) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent http2 frame with too long "
"padding: %d in frame %uz",
ctx->padding, ctx->rest);
return NGX_ERROR;
}
continue;
}
if (ctx->padding == ctx->rest) {
if (ctx->padding) {
ctx->state = ngx_http_proxy_v2_st_padding;
} else {
ctx->state = ngx_http_proxy_v2_st_start;
if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) {
ctx->done = 1;
}
}
continue;
}
if (b->pos == b->last) {
return NGX_AGAIN;
}
return NGX_OK;
}
}
static ngx_int_t
ngx_http_proxy_v2_parse_frame(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
{
u_char ch, *p;
ngx_http_proxy_v2_state_e state;
state = ctx->state;
for (p = b->pos; p < b->last; p++) {
ch = *p;
#if 0
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy frame byte: %02Xd, s:%d", ch, state);
#endif
switch (state) {
case ngx_http_proxy_v2_st_start:
ctx->rest = ch << 16;
state = ngx_http_proxy_v2_st_length_2;
break;
case ngx_http_proxy_v2_st_length_2:
ctx->rest |= ch << 8;
state = ngx_http_proxy_v2_st_length_3;
break;
case ngx_http_proxy_v2_st_length_3:
ctx->rest |= ch;
if (ctx->rest > NGX_HTTP_V2_DEFAULT_FRAME_SIZE) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent too large http2 frame: %uz",
ctx->rest);
return NGX_ERROR;
}
state = ngx_http_proxy_v2_st_type;
break;
case ngx_http_proxy_v2_st_type:
ctx->type = ch;
state = ngx_http_proxy_v2_st_flags;
break;
case ngx_http_proxy_v2_st_flags:
ctx->flags = ch;
state = ngx_http_proxy_v2_st_stream_id;
break;
case ngx_http_proxy_v2_st_stream_id:
ctx->stream_id = (ch & 0x7f) << 24;
state = ngx_http_proxy_v2_st_stream_id_2;
break;
case ngx_http_proxy_v2_st_stream_id_2:
ctx->stream_id |= ch << 16;
state = ngx_http_proxy_v2_st_stream_id_3;
break;
case ngx_http_proxy_v2_st_stream_id_3:
ctx->stream_id |= ch << 8;
state = ngx_http_proxy_v2_st_stream_id_4;
break;
case ngx_http_proxy_v2_st_stream_id_4:
ctx->stream_id |= ch;
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy frame: %d, len: %uz, f:%d, i:%ui",
ctx->type, ctx->rest, ctx->flags, ctx->stream_id);
b->pos = p + 1;
ctx->state = ngx_http_proxy_v2_st_payload;
ctx->frame_state = 0;
return NGX_OK;
/* suppress warning */
case ngx_http_proxy_v2_st_payload:
case ngx_http_proxy_v2_st_padding:
break;
}
}
b->pos = p;
ctx->state = state;
return NGX_AGAIN;
}
static ngx_int_t
ngx_http_proxy_v2_parse_header(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
{
u_char ch, *p, *last;
size_t min;
ngx_int_t rc;
enum {
sw_start = 0,
sw_padding_length,
sw_dependency,
sw_dependency_2,
sw_dependency_3,
sw_dependency_4,
sw_weight,
sw_fragment,
sw_padding
} state;
state = ctx->frame_state;
if (state == sw_start) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy parse header: start");
if (ctx->type == NGX_HTTP_V2_HEADERS_FRAME) {
ctx->parsing_headers = 1;
ctx->fragment_state = 0;
min = (ctx->flags & NGX_HTTP_V2_PADDED_FLAG ? 1 : 0)
+ (ctx->flags & NGX_HTTP_V2_PRIORITY_FLAG ? 5 : 0);
if (ctx->rest < min) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent headers frame "
"with invalid length: %uz",
ctx->rest);
return NGX_ERROR;
}
if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) {
ctx->end_stream = 1;
}
if (ctx->flags & NGX_HTTP_V2_PADDED_FLAG) {
state = sw_padding_length;
} else if (ctx->flags & NGX_HTTP_V2_PRIORITY_FLAG) {
state = sw_dependency;
} else {
state = sw_fragment;
}
} else if (ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME) {
state = sw_fragment;
}
ctx->padding = 0;
ctx->frame_state = state;
}
if (state < sw_fragment) {
if (b->last - b->pos < (ssize_t) ctx->rest) {
last = b->last;
} else {
last = b->pos + ctx->rest;
}
for (p = b->pos; p < last; p++) {
ch = *p;
#if 0
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header byte: %02Xd s:%d", ch, state);
#endif
/*
* headers frame:
*
* +---------------+
* |Pad Length? (8)|
* +-+-------------+----------------------------------------------+
* |E| Stream Dependency? (31) |
* +-+-------------+----------------------------------------------+
* | Weight? (8) |
* +-+-------------+----------------------------------------------+
* | Header Block Fragment (*) ...
* +--------------------------------------------------------------+
* | Padding (*) ...
* +--------------------------------------------------------------+
*/
switch (state) {
case sw_padding_length:
ctx->padding = ch;
if (ctx->flags & NGX_HTTP_V2_PRIORITY_FLAG) {
state = sw_dependency;
break;
}
goto fragment;
case sw_dependency:
state = sw_dependency_2;
break;
case sw_dependency_2:
state = sw_dependency_3;
break;
case sw_dependency_3:
state = sw_dependency_4;
break;
case sw_dependency_4:
state = sw_weight;
break;
case sw_weight:
goto fragment;
/* suppress warning */
case sw_start:
case sw_fragment:
case sw_padding:
break;
}
}
ctx->rest -= p - b->pos;
b->pos = p;
ctx->frame_state = state;
return NGX_AGAIN;
fragment:
p++;
ctx->rest -= p - b->pos;
b->pos = p;
if (ctx->padding > ctx->rest) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent http2 frame with too long "
"padding: %d in frame %uz",
ctx->padding, ctx->rest);
return NGX_ERROR;
}
state = sw_fragment;
ctx->frame_state = state;
}
if (state == sw_fragment) {
rc = ngx_http_proxy_v2_parse_fragment(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (rc == NGX_OK) {
return NGX_OK;
}
/* rc == NGX_DONE */
state = sw_padding;
ctx->frame_state = state;
}
if (state == sw_padding) {
if (b->last - b->pos < (ssize_t) ctx->rest) {
ctx->rest -= b->last - b->pos;
b->pos = b->last;
return NGX_AGAIN;
}
b->pos += ctx->rest;
ctx->rest = 0;
ctx->state = ngx_http_proxy_v2_st_start;
if (ctx->flags & NGX_HTTP_V2_END_HEADERS_FLAG) {
if (ctx->fragment_state) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent truncated http2 header");
return NGX_ERROR;
}
ctx->parsing_headers = 0;
return NGX_HTTP_PARSE_HEADER_DONE;
}
return NGX_AGAIN;
}
/* unreachable */
return NGX_ERROR;
}
static ngx_int_t
ngx_http_proxy_v2_parse_fragment(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
{
u_char ch, *p, *last;
size_t size;
ngx_uint_t index, size_update;
enum {
sw_start = 0,
sw_index,
sw_name_length,
sw_name_length_2,
sw_name_length_3,
sw_name_length_4,
sw_name,
sw_name_bytes,
sw_value_length,
sw_value_length_2,
sw_value_length_3,
sw_value_length_4,
sw_value,
sw_value_bytes
} state;
/* header block fragment */
#if 0
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header fragment %p:%p rest:%uz",
b->pos, b->last, ctx->rest);
#endif
if (b->last - b->pos < (ssize_t) ctx->rest - ctx->padding) {
last = b->last;
} else {
last = b->pos + ctx->rest - ctx->padding;
}
state = ctx->fragment_state;
for (p = b->pos; p < last; p++) {
ch = *p;
#if 0
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header byte: %02Xd s:%d", ch, state);
#endif
switch (state) {
case sw_start:
ctx->index = 0;
if ((ch & 0x80) == 0x80) {
/*
* indexed header:
*
* 0 1 2 3 4 5 6 7
* +---+---+---+---+---+---+---+---+
* | 1 | Index (7+) |
* +---+---------------------------+
*/
index = ch & ~0x80;
if (index == 0 || index > 61) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid http2 "
"table index: %ui", index);
return NGX_ERROR;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy indexed header: %ui", index);
ctx->index = index;
ctx->literal = 0;
goto done;
} else if ((ch & 0xc0) == 0x40) {
/*
* literal header with incremental indexing:
*
* 0 1 2 3 4 5 6 7
* +---+---+---+---+---+---+---+---+
* | 0 | 1 | Index (6+) |
* +---+---+-----------------------+
* | H | Value Length (7+) |
* +---+---------------------------+
* | Value String (Length octets) |
* +-------------------------------+
*
* 0 1 2 3 4 5 6 7
* +---+---+---+---+---+---+---+---+
* | 0 | 1 | 0 |
* +---+---+-----------------------+
* | H | Name Length (7+) |
* +---+---------------------------+
* | Name String (Length octets) |
* +---+---------------------------+
* | H | Value Length (7+) |
* +---+---------------------------+
* | Value String (Length octets) |
* +-------------------------------+
*/
index = ch & ~0xc0;
if (index > 61) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid http2 "
"table index: %ui", index);
return NGX_ERROR;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy literal header: %ui", index);
if (index == 0) {
state = sw_name_length;
break;
}
ctx->index = index;
ctx->literal = 1;
state = sw_value_length;
break;
} else if ((ch & 0xe0) == 0x20) {
/*
* dynamic table size update:
*
* 0 1 2 3 4 5 6 7
* +---+---+---+---+---+---+---+---+
* | 0 | 0 | 1 | Max size (5+) |
* +---+---------------------------+
*/
size_update = ch & ~0xe0;
if (size_update > 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid http2 "
"dynamic table size update: %ui",
size_update);
return NGX_ERROR;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy table size update: %ui",
size_update);
break;
} else if ((ch & 0xf0) == 0x10) {
/*
* literal header field never indexed:
*
* 0 1 2 3 4 5 6 7
* +---+---+---+---+---+---+---+---+
* | 0 | 0 | 0 | 1 | Index (4+) |
* +---+---+-----------------------+
* | H | Value Length (7+) |
* +---+---------------------------+
* | Value String (Length octets) |
* +-------------------------------+
*
* 0 1 2 3 4 5 6 7
* +---+---+---+---+---+---+---+---+
* | 0 | 0 | 0 | 1 | 0 |
* +---+---+-----------------------+
* | H | Name Length (7+) |
* +---+---------------------------+
* | Name String (Length octets) |
* +---+---------------------------+
* | H | Value Length (7+) |
* +---+---------------------------+
* | Value String (Length octets) |
* +-------------------------------+
*/
index = ch & ~0xf0;
if (index == 0x0f) {
ctx->index = index;
ctx->literal = 1;
state = sw_index;
break;
}
if (index == 0) {
state = sw_name_length;
break;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy literal header never indexed: %ui",
index);
ctx->index = index;
ctx->literal = 1;
state = sw_value_length;
break;
} else if ((ch & 0xf0) == 0x00) {
/*
* literal header field without indexing:
*
* 0 1 2 3 4 5 6 7
* +---+---+---+---+---+---+---+---+
* | 0 | 0 | 0 | 0 | Index (4+) |
* +---+---+-----------------------+
* | H | Value Length (7+) |
* +---+---------------------------+
* | Value String (Length octets) |
* +-------------------------------+
*
* 0 1 2 3 4 5 6 7
* +---+---+---+---+---+---+---+---+
* | 0 | 0 | 0 | 0 | 0 |
* +---+---+-----------------------+
* | H | Name Length (7+) |
* +---+---------------------------+
* | Name String (Length octets) |
* +---+---------------------------+
* | H | Value Length (7+) |
* +---+---------------------------+
* | Value String (Length octets) |
* +-------------------------------+
*/
index = ch & ~0xf0;
if (index == 0x0f) {
ctx->index = index;
ctx->literal = 1;
state = sw_index;
break;
}
if (index == 0) {
state = sw_name_length;
break;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy literal header without indexing: %ui",
index);
ctx->index = index;
ctx->literal = 1;
state = sw_value_length;
break;
}
/* not reached */
return NGX_ERROR;
case sw_index:
ctx->index = ctx->index + (ch & ~0x80);
if (ch & 0x80) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent http2 table index "
"with continuation flag");
return NGX_ERROR;
}
if (ctx->index > 61) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid http2 "
"table index: %ui", ctx->index);
return NGX_ERROR;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header index: %ui", ctx->index);
state = sw_value_length;
break;
case sw_name_length:
ctx->field_huffman = ch & 0x80 ? 1 : 0;
ctx->field_length = ch & ~0x80;
if (ctx->field_length == 0x7f) {
state = sw_name_length_2;
break;
}
if (ctx->field_length == 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent zero http2 "
"header name length");
return NGX_ERROR;
}
state = sw_name;
break;
case sw_name_length_2:
ctx->field_length += ch & ~0x80;
if (ch & 0x80) {
state = sw_name_length_3;
break;
}
state = sw_name;
break;
case sw_name_length_3:
ctx->field_length += (ch & ~0x80) << 7;
if (ch & 0x80) {
state = sw_name_length_4;
break;
}
state = sw_name;
break;
case sw_name_length_4:
ctx->field_length += (ch & ~0x80) << 14;
if (ch & 0x80) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent too large http2 "
"header name length");
return NGX_ERROR;
}
state = sw_name;
break;
case sw_name:
ctx->name.len = ctx->field_huffman ?
ctx->field_length * 8 / 5 : ctx->field_length;
ctx->name.data = ngx_pnalloc(r->pool, ctx->name.len + 1);
if (ctx->name.data == NULL) {
return NGX_ERROR;
}
ctx->field_end = ctx->name.data;
ctx->field_rest = ctx->field_length;
ctx->field_state = 0;
state = sw_name_bytes;
/* fall through */
case sw_name_bytes:
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy name: len:%uz h:%d last:%uz, rest:%uz",
ctx->field_length,
ctx->field_huffman,
last - p,
ctx->rest - (p - b->pos));
size = ngx_min(last - p, (ssize_t) ctx->field_rest);
ctx->field_rest -= size;
if (ctx->field_huffman) {
if (ngx_http_huff_decode(&ctx->field_state, p, size,
&ctx->field_end,
ctx->field_rest == 0,
r->connection->log)
!= NGX_OK)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid encoded header");
return NGX_ERROR;
}
ctx->name.len = ctx->field_end - ctx->name.data;
ctx->name.data[ctx->name.len] = '\0';
} else {
ctx->field_end = ngx_cpymem(ctx->field_end, p, size);
ctx->name.data[ctx->name.len] = '\0';
}
p += size - 1;
if (ctx->field_rest == 0) {
state = sw_value_length;
}
break;
case sw_value_length:
ctx->field_huffman = ch & 0x80 ? 1 : 0;
ctx->field_length = ch & ~0x80;
if (ctx->field_length == 0x7f) {
state = sw_value_length_2;
break;
}
if (ctx->field_length == 0) {
ngx_str_set(&ctx->value, "");
goto done;
}
state = sw_value;
break;
case sw_value_length_2:
ctx->field_length += ch & ~0x80;
if (ch & 0x80) {
state = sw_value_length_3;
break;
}
state = sw_value;
break;
case sw_value_length_3:
ctx->field_length += (ch & ~0x80) << 7;
if (ch & 0x80) {
state = sw_value_length_4;
break;
}
state = sw_value;
break;
case sw_value_length_4:
ctx->field_length += (ch & ~0x80) << 14;
if (ch & 0x80) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent too large http2 "
"header value length");
return NGX_ERROR;
}
state = sw_value;
break;
case sw_value:
ctx->value.len = ctx->field_huffman ?
ctx->field_length * 8 / 5 : ctx->field_length;
ctx->value.data = ngx_pnalloc(r->pool, ctx->value.len + 1);
if (ctx->value.data == NULL) {
return NGX_ERROR;
}
ctx->field_end = ctx->value.data;
ctx->field_rest = ctx->field_length;
ctx->field_state = 0;
state = sw_value_bytes;
/* fall through */
case sw_value_bytes:
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy value: len:%uz h:%d last:%uz, rest:%uz",
ctx->field_length,
ctx->field_huffman,
last - p,
ctx->rest - (p - b->pos));
size = ngx_min(last - p, (ssize_t) ctx->field_rest);
ctx->field_rest -= size;
if (ctx->field_huffman) {
if (ngx_http_huff_decode(&ctx->field_state, p, size,
&ctx->field_end,
ctx->field_rest == 0,
r->connection->log)
!= NGX_OK)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid encoded header");
return NGX_ERROR;
}
ctx->value.len = ctx->field_end - ctx->value.data;
ctx->value.data[ctx->value.len] = '\0';
} else {
ctx->field_end = ngx_cpymem(ctx->field_end, p, size);
ctx->value.data[ctx->value.len] = '\0';
}
p += size - 1;
if (ctx->field_rest == 0) {
goto done;
}
break;
}
continue;
done:
p++;
ctx->rest -= p - b->pos;
ctx->fragment_state = sw_start;
b->pos = p;
if (ctx->index) {
ctx->name = *ngx_http_v2_get_static_name(ctx->index);
}
if (ctx->index && !ctx->literal) {
ctx->value = *ngx_http_v2_get_static_value(ctx->index);
}
if (!ctx->index) {
if (ngx_http_proxy_v2_validate_header_name(r, &ctx->name)
!= NGX_OK)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid header: \"%V: %V\"",
&ctx->name, &ctx->value);
return NGX_ERROR;
}
}
if (!ctx->index || ctx->literal) {
if (ngx_http_proxy_v2_validate_header_value(r, &ctx->value)
!= NGX_OK)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid header: \"%V: %V\"",
&ctx->name, &ctx->value);
return NGX_ERROR;
}
}
return NGX_OK;
}
ctx->rest -= p - b->pos;
ctx->fragment_state = state;
b->pos = p;
if (ctx->rest > ctx->padding) {
return NGX_AGAIN;
}
return NGX_DONE;
}
static ngx_int_t
ngx_http_proxy_v2_validate_header_name(ngx_http_request_t *r, ngx_str_t *s)
{
u_char ch;
ngx_uint_t i;
for (i = 0; i < s->len; i++) {
ch = s->data[i];
if (ch == ':' && i > 0) {
return NGX_ERROR;
}
if (ch >= 'A' && ch <= 'Z') {
return NGX_ERROR;
}
if (ch <= 0x20 || ch == 0x7f) {
return NGX_ERROR;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_v2_validate_header_value(ngx_http_request_t *r, ngx_str_t *s)
{
u_char ch;
ngx_uint_t i;
for (i = 0; i < s->len; i++) {
ch = s->data[i];
if (ch == '\0' || ch == CR || ch == LF) {
return NGX_ERROR;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_v2_parse_rst_stream(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
{
u_char ch, *p, *last;
enum {
sw_start = 0,
sw_error_2,
sw_error_3,
sw_error_4
} state;
if (b->last - b->pos < (ssize_t) ctx->rest) {
last = b->last;
} else {
last = b->pos + ctx->rest;
}
state = ctx->frame_state;
if (state == sw_start) {
if (ctx->rest != 4) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent rst stream frame "
"with invalid length: %uz",
ctx->rest);
return NGX_ERROR;
}
}
for (p = b->pos; p < last; p++) {
ch = *p;
#if 0
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy rst byte: %02Xd s:%d", ch, state);
#endif
switch (state) {
case sw_start:
ctx->error = (ngx_uint_t) ch << 24;
state = sw_error_2;
break;
case sw_error_2:
ctx->error |= ch << 16;
state = sw_error_3;
break;
case sw_error_3:
ctx->error |= ch << 8;
state = sw_error_4;
break;
case sw_error_4:
ctx->error |= ch;
state = sw_start;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy error: %ui", ctx->error);
break;
}
}
ctx->rest -= p - b->pos;
ctx->frame_state = state;
b->pos = p;
if (ctx->rest > 0) {
return NGX_AGAIN;
}
ctx->state = ngx_http_proxy_v2_st_start;
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_v2_parse_goaway(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
{
u_char ch, *p, *last;
enum {
sw_start = 0,
sw_last_stream_id_2,
sw_last_stream_id_3,
sw_last_stream_id_4,
sw_error,
sw_error_2,
sw_error_3,
sw_error_4,
sw_debug
} state;
if (b->last - b->pos < (ssize_t) ctx->rest) {
last = b->last;
} else {
last = b->pos + ctx->rest;
}
state = ctx->frame_state;
if (state == sw_start) {
if (ctx->stream_id) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent goaway frame "
"with non-zero stream id: %ui",
ctx->stream_id);
return NGX_ERROR;
}
if (ctx->rest < 8) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent goaway frame "
"with invalid length: %uz",
ctx->rest);
return NGX_ERROR;
}
}
for (p = b->pos; p < last; p++) {
ch = *p;
#if 0
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy goaway byte: %02Xd s:%d", ch, state);
#endif
switch (state) {
case sw_start:
ctx->stream_id = (ch & 0x7f) << 24;
state = sw_last_stream_id_2;
break;
case sw_last_stream_id_2:
ctx->stream_id |= ch << 16;
state = sw_last_stream_id_3;
break;
case sw_last_stream_id_3:
ctx->stream_id |= ch << 8;
state = sw_last_stream_id_4;
break;
case sw_last_stream_id_4:
ctx->stream_id |= ch;
state = sw_error;
break;
case sw_error:
ctx->error = (ngx_uint_t) ch << 24;
state = sw_error_2;
break;
case sw_error_2:
ctx->error |= ch << 16;
state = sw_error_3;
break;
case sw_error_3:
ctx->error |= ch << 8;
state = sw_error_4;
break;
case sw_error_4:
ctx->error |= ch;
state = sw_debug;
break;
case sw_debug:
break;
}
}
ctx->rest -= p - b->pos;
ctx->frame_state = state;
b->pos = p;
if (ctx->rest > 0) {
return NGX_AGAIN;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy goaway: %ui, stream %ui",
ctx->error, ctx->stream_id);
ctx->state = ngx_http_proxy_v2_st_start;
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_v2_parse_window_update(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
{
u_char ch, *p, *last;
enum {
sw_start = 0,
sw_size_2,
sw_size_3,
sw_size_4
} state;
if (b->last - b->pos < (ssize_t) ctx->rest) {
last = b->last;
} else {
last = b->pos + ctx->rest;
}
state = ctx->frame_state;
if (state == sw_start) {
if (ctx->rest != 4) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent window update frame "
"with invalid length: %uz",
ctx->rest);
return NGX_ERROR;
}
}
for (p = b->pos; p < last; p++) {
ch = *p;
#if 0
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy window update byte: %02Xd s:%d", ch, state);
#endif
switch (state) {
case sw_start:
ctx->window_update = (ch & 0x7f) << 24;
state = sw_size_2;
break;
case sw_size_2:
ctx->window_update |= ch << 16;
state = sw_size_3;
break;
case sw_size_3:
ctx->window_update |= ch << 8;
state = sw_size_4;
break;
case sw_size_4:
ctx->window_update |= ch;
state = sw_start;
break;
}
}
ctx->rest -= p - b->pos;
ctx->frame_state = state;
b->pos = p;
if (ctx->rest > 0) {
return NGX_AGAIN;
}
ctx->state = ngx_http_proxy_v2_st_start;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy window update: %ui", ctx->window_update);
if (ctx->stream_id) {
if (ctx->window_update > (size_t) NGX_HTTP_V2_MAX_WINDOW
- ctx->send_window)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent too large window update");
return NGX_ERROR;
}
ctx->send_window += ctx->window_update;
} else {
if (ctx->window_update > NGX_HTTP_V2_MAX_WINDOW
- ctx->connection->send_window)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent too large window update");
return NGX_ERROR;
}
ctx->connection->send_window += ctx->window_update;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_v2_parse_settings(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
{
u_char ch, *p, *last;
ssize_t window_update;
enum {
sw_start = 0,
sw_id,
sw_id_2,
sw_value,
sw_value_2,
sw_value_3,
sw_value_4
} state;
if (b->last - b->pos < (ssize_t) ctx->rest) {
last = b->last;
} else {
last = b->pos + ctx->rest;
}
state = ctx->frame_state;
if (state == sw_start) {
if (ctx->stream_id) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent settings frame "
"with non-zero stream id: %ui",
ctx->stream_id);
return NGX_ERROR;
}
if (ctx->flags & NGX_HTTP_V2_ACK_FLAG) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy settings ack");
if (ctx->rest != 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent settings frame "
"with ack flag and non-zero length: %uz",
ctx->rest);
return NGX_ERROR;
}
ctx->state = ngx_http_proxy_v2_st_start;
return NGX_OK;
}
if (ctx->rest % 6 != 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent settings frame "
"with invalid length: %uz",
ctx->rest);
return NGX_ERROR;
}
if (ctx->free == NULL && ctx->settings++ > 1000) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent too many settings frames");
return NGX_ERROR;
}
}
for (p = b->pos; p < last; p++) {
ch = *p;
#if 0
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy settings byte: %02Xd s:%d", ch, state);
#endif
switch (state) {
case sw_start:
case sw_id:
ctx->setting_id = ch << 8;
state = sw_id_2;
break;
case sw_id_2:
ctx->setting_id |= ch;
state = sw_value;
break;
case sw_value:
ctx->setting_value = (ngx_uint_t) ch << 24;
state = sw_value_2;
break;
case sw_value_2:
ctx->setting_value |= ch << 16;
state = sw_value_3;
break;
case sw_value_3:
ctx->setting_value |= ch << 8;
state = sw_value_4;
break;
case sw_value_4:
ctx->setting_value |= ch;
state = sw_id;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy setting: %ui %ui",
ctx->setting_id, ctx->setting_value);
/*
* The following settings are defined by the protocol:
*
* SETTINGS_HEADER_TABLE_SIZE, SETTINGS_ENABLE_PUSH,
* SETTINGS_MAX_CONCURRENT_STREAMS, SETTINGS_INITIAL_WINDOW_SIZE,
* SETTINGS_MAX_FRAME_SIZE, SETTINGS_MAX_HEADER_LIST_SIZE
*
* Only SETTINGS_INITIAL_WINDOW_SIZE seems to be needed in
* a simple client.
*/
if (ctx->setting_id == 0x04) {
/* SETTINGS_INITIAL_WINDOW_SIZE */
if (ctx->setting_value > NGX_HTTP_V2_MAX_WINDOW) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent settings frame "
"with too large initial window size: %ui",
ctx->setting_value);
return NGX_ERROR;
}
window_update = ctx->setting_value
- ctx->connection->init_window;
ctx->connection->init_window = ctx->setting_value;
if (ctx->send_window > 0
&& window_update > (ssize_t) NGX_HTTP_V2_MAX_WINDOW
- ctx->send_window)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent settings frame "
"with too large initial window size: %ui",
ctx->setting_value);
return NGX_ERROR;
}
ctx->send_window += window_update;
}
break;
}
}
ctx->rest -= p - b->pos;
ctx->frame_state = state;
b->pos = p;
if (ctx->rest > 0) {
return NGX_AGAIN;
}
ctx->state = ngx_http_proxy_v2_st_start;
return ngx_http_proxy_v2_send_settings_ack(r, ctx);
}
static ngx_int_t
ngx_http_proxy_v2_parse_ping(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
{
u_char ch, *p, *last;
enum {
sw_start = 0,
sw_data_2,
sw_data_3,
sw_data_4,
sw_data_5,
sw_data_6,
sw_data_7,
sw_data_8
} state;
if (b->last - b->pos < (ssize_t) ctx->rest) {
last = b->last;
} else {
last = b->pos + ctx->rest;
}
state = ctx->frame_state;
if (state == sw_start) {
if (ctx->stream_id) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent ping frame "
"with non-zero stream id: %ui",
ctx->stream_id);
return NGX_ERROR;
}
if (ctx->rest != 8) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent ping frame "
"with invalid length: %uz",
ctx->rest);
return NGX_ERROR;
}
if (ctx->flags & NGX_HTTP_V2_ACK_FLAG) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent ping frame with ack flag");
return NGX_ERROR;
}
if (ctx->free == NULL && ctx->pings++ > 1000) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent too many ping frames");
return NGX_ERROR;
}
}
for (p = b->pos; p < last; p++) {
ch = *p;
#if 0
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy ping byte: %02Xd s:%d", ch, state);
#endif
if (state < sw_data_8) {
ctx->ping_data[state] = ch;
state++;
} else {
ctx->ping_data[7] = ch;
state = sw_start;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy ping");
}
}
ctx->rest -= p - b->pos;
ctx->frame_state = state;
b->pos = p;
if (ctx->rest > 0) {
return NGX_AGAIN;
}
ctx->state = ngx_http_proxy_v2_st_start;
return ngx_http_proxy_v2_send_ping_ack(r, ctx);
}
static ngx_int_t
ngx_http_proxy_v2_send_settings_ack(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx)
{
ngx_chain_t *cl, **ll;
ngx_http_proxy_v2_frame_t *f;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy send settings ack");
for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) {
ll = &cl->next;
}
cl = ngx_http_proxy_v2_get_buf(r, ctx);
if (cl == NULL) {
return NGX_ERROR;
}
f = (ngx_http_proxy_v2_frame_t *) cl->buf->last;
cl->buf->last += sizeof(ngx_http_proxy_v2_frame_t);
f->length_0 = 0;
f->length_1 = 0;
f->length_2 = 0;
f->type = NGX_HTTP_V2_SETTINGS_FRAME;
f->flags = NGX_HTTP_V2_ACK_FLAG;
f->stream_id_0 = 0;
f->stream_id_1 = 0;
f->stream_id_2 = 0;
f->stream_id_3 = 0;
*ll = cl;
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_v2_send_ping_ack(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx)
{
ngx_chain_t *cl, **ll;
ngx_http_proxy_v2_frame_t *f;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy send ping ack");
for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) {
ll = &cl->next;
}
cl = ngx_http_proxy_v2_get_buf(r, ctx);
if (cl == NULL) {
return NGX_ERROR;
}
f = (ngx_http_proxy_v2_frame_t *) cl->buf->last;
cl->buf->last += sizeof(ngx_http_proxy_v2_frame_t);
f->length_0 = 0;
f->length_1 = 0;
f->length_2 = 8;
f->type = NGX_HTTP_V2_PING_FRAME;
f->flags = NGX_HTTP_V2_ACK_FLAG;
f->stream_id_0 = 0;
f->stream_id_1 = 0;
f->stream_id_2 = 0;
f->stream_id_3 = 0;
cl->buf->last = ngx_copy(cl->buf->last, ctx->ping_data, 8);
*ll = cl;
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_v2_send_window_update(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx)
{
size_t n;
ngx_chain_t *cl, **ll;
ngx_http_proxy_v2_frame_t *f;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy send window update: %uz %uz",
ctx->connection->recv_window, ctx->recv_window);
for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) {
ll = &cl->next;
}
cl = ngx_http_proxy_v2_get_buf(r, ctx);
if (cl == NULL) {
return NGX_ERROR;
}
f = (ngx_http_proxy_v2_frame_t *) cl->buf->last;
cl->buf->last += sizeof(ngx_http_proxy_v2_frame_t);
f->length_0 = 0;
f->length_1 = 0;
f->length_2 = 4;
f->type = NGX_HTTP_V2_WINDOW_UPDATE_FRAME;
f->flags = 0;
f->stream_id_0 = 0;
f->stream_id_1 = 0;
f->stream_id_2 = 0;
f->stream_id_3 = 0;
n = NGX_HTTP_V2_MAX_WINDOW - ctx->connection->recv_window;
ctx->connection->recv_window = NGX_HTTP_V2_MAX_WINDOW;
*cl->buf->last++ = (u_char) ((n >> 24) & 0xff);
*cl->buf->last++ = (u_char) ((n >> 16) & 0xff);
*cl->buf->last++ = (u_char) ((n >> 8) & 0xff);
*cl->buf->last++ = (u_char) (n & 0xff);
f = (ngx_http_proxy_v2_frame_t *) cl->buf->last;
cl->buf->last += sizeof(ngx_http_proxy_v2_frame_t);
f->length_0 = 0;
f->length_1 = 0;
f->length_2 = 4;
f->type = NGX_HTTP_V2_WINDOW_UPDATE_FRAME;
f->flags = 0;
f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff);
f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff);
f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff);
f->stream_id_3 = (u_char) (ctx->id & 0xff);
n = NGX_HTTP_V2_MAX_WINDOW - ctx->recv_window;
ctx->recv_window = NGX_HTTP_V2_MAX_WINDOW;
*cl->buf->last++ = (u_char) ((n >> 24) & 0xff);
*cl->buf->last++ = (u_char) ((n >> 16) & 0xff);
*cl->buf->last++ = (u_char) ((n >> 8) & 0xff);
*cl->buf->last++ = (u_char) (n & 0xff);
*ll = cl;
return NGX_OK;
}
static ngx_chain_t *
ngx_http_proxy_v2_get_buf(ngx_http_request_t *r, ngx_http_proxy_v2_ctx_t *ctx)
{
u_char *start;
ngx_buf_t *b;
ngx_chain_t *cl;
cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
if (cl == NULL) {
return NULL;
}
b = cl->buf;
start = b->start;
if (start == NULL) {
/*
* each buffer is large enough to hold two window update
* frames in a row
*/
start = ngx_palloc(r->pool, 2 * sizeof(ngx_http_proxy_v2_frame_t) + 8);
if (start == NULL) {
return NULL;
}
}
ngx_memzero(b, sizeof(ngx_buf_t));
b->start = start;
b->pos = start;
b->last = start;
b->end = start + 2 * sizeof(ngx_http_proxy_v2_frame_t) + 8;
b->tag = (ngx_buf_tag_t) &ngx_http_proxy_v2_body_output_filter;
b->temporary = 1;
b->flush = 1;
return cl;
}
static ngx_http_proxy_v2_ctx_t *
ngx_http_proxy_v2_get_ctx(ngx_http_request_t *r)
{
ngx_http_upstream_t *u;
ngx_http_proxy_v2_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_v2_module);
if (ctx->connection == NULL) {
u = r->upstream;
if (ngx_http_proxy_v2_get_connection_data(r, ctx, &u->peer) != NGX_OK) {
return NULL;
}
}
return ctx;
}
static ngx_int_t
ngx_http_proxy_v2_get_connection_data(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_peer_connection_t *pc)
{
ngx_connection_t *c;
ngx_pool_cleanup_t *cln;
c = pc->connection;
if (c == NULL) {
ctx->connection = ngx_palloc(r->pool, sizeof(ngx_http_proxy_v2_conn_t));
if (ctx->connection == NULL) {
return NGX_ERROR;
}
goto done;
}
if (pc->cached) {
/*
* for cached connections, connection data can be found
* in the cleanup handler
*/
for (cln = c->pool->cleanup; cln; cln = cln->next) {
if (cln->handler == ngx_http_proxy_v2_cleanup) {
ctx->connection = cln->data;
break;
}
}
if (ctx->connection == NULL) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"no connection data found for "
"keepalive http2 connection");
return NGX_ERROR;
}
ctx->send_window = ctx->connection->init_window;
ctx->recv_window = NGX_HTTP_V2_MAX_WINDOW;
ctx->connection->last_stream_id += 2;
ctx->id = ctx->connection->last_stream_id;
return NGX_OK;
}
cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_http_proxy_v2_conn_t));
if (cln == NULL) {
return NGX_ERROR;
}
cln->handler = ngx_http_proxy_v2_cleanup;
ctx->connection = cln->data;
done:
ctx->connection->init_window = NGX_HTTP_V2_DEFAULT_WINDOW;
ctx->connection->send_window = NGX_HTTP_V2_DEFAULT_WINDOW;
ctx->connection->recv_window = NGX_HTTP_V2_MAX_WINDOW;
ctx->send_window = NGX_HTTP_V2_DEFAULT_WINDOW;
ctx->recv_window = NGX_HTTP_V2_MAX_WINDOW;
ctx->id = 1;
ctx->connection->last_stream_id = 1;
return NGX_OK;
}
static void
ngx_http_proxy_v2_cleanup(void *data)
{
#if 0
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http proxy cleanup");
#endif
return;
}
static void
ngx_http_proxy_v2_abort_request(ngx_http_request_t *r)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"abort proxy http2 request");
return;
}
static void
ngx_http_proxy_v2_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"finalize proxy http2 request");
return;
}
+42 -3
View File
@@ -117,6 +117,13 @@ static ngx_command_t ngx_http_ssl_commands[] = {
0, 0,
NULL }, NULL },
{ ngx_string("ssl_ech_file"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, ech_files),
NULL },
{ ngx_string("ssl_password_file"), { ngx_string("ssl_password_file"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_http_ssl_password_file, ngx_http_ssl_password_file,
@@ -237,7 +244,7 @@ static ngx_command_t ngx_http_ssl_commands[] = {
NULL }, NULL },
{ ngx_string("ssl_ocsp"), { ngx_string("ssl_ocsp"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_enum_slot, ngx_conf_set_enum_slot,
NGX_HTTP_SRV_CONF_OFFSET, NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, ocsp), offsetof(ngx_http_ssl_srv_conf_t, ocsp),
@@ -393,6 +400,9 @@ static ngx_http_variable_t ngx_http_ssl_vars[] = {
{ ngx_string("ssl_curves"), NULL, ngx_http_ssl_variable, { ngx_string("ssl_curves"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_curves, NGX_HTTP_VAR_CHANGEABLE, 0 }, (uintptr_t) ngx_ssl_get_curves, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_sigalg"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_sigalg, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_session_id"), NULL, ngx_http_ssl_variable, { ngx_string("ssl_session_id"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_session_id, NGX_HTTP_VAR_CHANGEABLE, 0 }, (uintptr_t) ngx_ssl_get_session_id, NGX_HTTP_VAR_CHANGEABLE, 0 },
@@ -409,6 +419,13 @@ static ngx_http_variable_t ngx_http_ssl_vars[] = {
{ ngx_string("ssl_alpn_protocol"), NULL, ngx_http_ssl_variable, { ngx_string("ssl_alpn_protocol"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_alpn_protocol, NGX_HTTP_VAR_CHANGEABLE, 0 }, (uintptr_t) ngx_ssl_get_alpn_protocol, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_ech_status"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_ech_status, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_ech_outer_server_name"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_ech_outer_server_name,
NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_client_cert"), NULL, ngx_http_ssl_variable, { ngx_string("ssl_client_cert"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_certificate, NGX_HTTP_VAR_CHANGEABLE, 0 }, (uintptr_t) ngx_ssl_get_certificate, NGX_HTTP_VAR_CHANGEABLE, 0 },
@@ -450,6 +467,9 @@ static ngx_http_variable_t ngx_http_ssl_vars[] = {
{ ngx_string("ssl_client_v_remain"), NULL, ngx_http_ssl_variable, { ngx_string("ssl_client_v_remain"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_client_v_remain, NGX_HTTP_VAR_CHANGEABLE, 0 }, (uintptr_t) ngx_ssl_get_client_v_remain, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_client_sigalg"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_client_sigalg, NGX_HTTP_VAR_CHANGEABLE, 0 },
ngx_http_null_variable ngx_http_null_variable
}; };
@@ -672,6 +692,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
sscf->certificates = NGX_CONF_UNSET_PTR; sscf->certificates = NGX_CONF_UNSET_PTR;
sscf->certificate_keys = NGX_CONF_UNSET_PTR; sscf->certificate_keys = NGX_CONF_UNSET_PTR;
sscf->certificate_cache = NGX_CONF_UNSET_PTR; sscf->certificate_cache = NGX_CONF_UNSET_PTR;
sscf->ech_files = NGX_CONF_UNSET_PTR;
sscf->passwords = NGX_CONF_UNSET_PTR; sscf->passwords = NGX_CONF_UNSET_PTR;
sscf->conf_commands = NGX_CONF_UNSET_PTR; sscf->conf_commands = NGX_CONF_UNSET_PTR;
sscf->builtin_session_cache = NGX_CONF_UNSET; sscf->builtin_session_cache = NGX_CONF_UNSET;
@@ -728,6 +749,8 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_ptr_value(conf->certificate_cache, prev->certificate_cache, ngx_conf_merge_ptr_value(conf->certificate_cache, prev->certificate_cache,
NULL); NULL);
ngx_conf_merge_ptr_value(conf->ech_files, prev->ech_files, NULL);
ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL); ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);
ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, ""); ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
@@ -803,6 +826,12 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
cln->data = &conf->ssl; cln->data = &conf->ssl;
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
{
static ngx_ssl_client_hello_arg cb = { ngx_http_ssl_servername };
if (ngx_ssl_set_client_hello_callback(&conf->ssl, &cb) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx, if (SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx,
ngx_http_ssl_servername) ngx_http_ssl_servername)
@@ -813,7 +842,7 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
"dynamically to an OpenSSL library which has no tlsext support, " "dynamically to an OpenSSL library which has no tlsext support, "
"therefore SNI is not available"); "therefore SNI is not available");
} }
}
#endif #endif
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
@@ -918,6 +947,10 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
return NGX_CONF_ERROR; return NGX_CONF_ERROR;
} }
if (ngx_ssl_ech_files(cf, &conf->ssl, conf->ech_files) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) { if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) {
return NGX_CONF_ERROR; return NGX_CONF_ERROR;
} }
@@ -956,13 +989,19 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
if (conf->stapling) { if (conf->stapling) {
if (conf->certificate_compression) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"\"ssl_stapling\" is incompatible with "
"\"ssl_certificate_compression\"");
return NGX_CONF_ERROR;
}
if (ngx_ssl_stapling(cf, &conf->ssl, &conf->stapling_file, if (ngx_ssl_stapling(cf, &conf->ssl, &conf->stapling_file,
&conf->stapling_responder, conf->stapling_verify) &conf->stapling_responder, conf->stapling_verify)
!= NGX_OK) != NGX_OK)
{ {
return NGX_CONF_ERROR; return NGX_CONF_ERROR;
} }
} }
if (ngx_ssl_early_data(cf, &conf->ssl, conf->early_data) != NGX_OK) { if (ngx_ssl_early_data(cf, &conf->ssl, conf->early_data) != NGX_OK) {
+1
View File
@@ -49,6 +49,7 @@ typedef struct {
ngx_str_t ciphers; ngx_str_t ciphers;
ngx_array_t *ech_files;
ngx_array_t *passwords; ngx_array_t *passwords;
ngx_array_t *conf_commands; ngx_array_t *conf_commands;
+3 -3
View File
@@ -117,7 +117,7 @@ ngx_int_t ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len,
void ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, void ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri,
ngx_str_t *args); ngx_str_t *args);
ngx_int_t ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b, ngx_int_t ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
ngx_http_chunked_t *ctx); ngx_http_chunked_t *ctx, ngx_uint_t keep_trailers);
ngx_http_request_t *ngx_http_create_request(ngx_connection_t *c); ngx_http_request_t *ngx_http_create_request(ngx_connection_t *c);
@@ -130,8 +130,8 @@ ngx_int_t ngx_http_post_request(ngx_http_request_t *r,
ngx_http_posted_request_t *pr); ngx_http_posted_request_t *pr);
ngx_int_t ngx_http_set_virtual_server(ngx_http_request_t *r, ngx_int_t ngx_http_set_virtual_server(ngx_http_request_t *r,
ngx_str_t *host); ngx_str_t *host);
ngx_int_t ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_int_t ngx_http_validate_host(ngx_str_t *host, in_port_t *port,
ngx_uint_t alloc); ngx_pool_t *pool, ngx_uint_t alloc);
void ngx_http_close_request(ngx_http_request_t *r, ngx_int_t rc); void ngx_http_close_request(ngx_http_request_t *r, ngx_int_t rc);
void ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc); void ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc);
void ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc); void ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc);
+1
View File
@@ -206,6 +206,7 @@ typedef struct {
#if (NGX_PCRE) #if (NGX_PCRE)
unsigned captures:1; unsigned captures:1;
#endif #endif
unsigned allow_connect:1;
ngx_http_core_loc_conf_t **named_locations; ngx_http_core_loc_conf_t **named_locations;
} ngx_http_core_srv_conf_t; } ngx_http_core_srv_conf_t;
+69 -28
View File
@@ -111,6 +111,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
sw_schema, sw_schema,
sw_schema_slash, sw_schema_slash,
sw_schema_slash_slash, sw_schema_slash_slash,
sw_spaces_before_host,
sw_host_start, sw_host_start,
sw_host, sw_host,
sw_host_end, sw_host_end,
@@ -158,6 +159,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
if (ch == ' ') { if (ch == ' ') {
r->method_end = p - 1; r->method_end = p - 1;
m = r->request_start; m = r->request_start;
state = sw_spaces_before_uri;
switch (p - m) { switch (p - m) {
@@ -247,6 +249,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
if (ngx_str7_cmp(m, 'C', 'O', 'N', 'N', 'E', 'C', 'T', ' ')) if (ngx_str7_cmp(m, 'C', 'O', 'N', 'N', 'E', 'C', 'T', ' '))
{ {
r->method = NGX_HTTP_CONNECT; r->method = NGX_HTTP_CONNECT;
state = sw_spaces_before_host;
} }
break; break;
@@ -269,7 +272,6 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
break; break;
} }
state = sw_spaces_before_uri;
break; break;
} }
@@ -345,6 +347,14 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
} }
break; break;
case sw_spaces_before_host:
if (ch == ' ') {
break;
}
/* fall through */
case sw_host_start: case sw_host_start:
r->host_start = p; r->host_start = p;
@@ -373,12 +383,18 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
case sw_host_end: case sw_host_end:
r->host_end = p; if (ch == ':') {
switch (ch) {
case ':':
state = sw_port; state = sw_port;
break; break;
}
r->host_end = p;
if (r->method == NGX_HTTP_CONNECT) {
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
switch (ch) {
case '/': case '/':
r->uri_start = p; r->uri_start = p;
state = sw_after_slash_in_uri; state = sw_after_slash_in_uri;
@@ -449,6 +465,17 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
break; break;
} }
r->host_end = p;
if (r->method == NGX_HTTP_CONNECT) {
if (ch == ' ') {
state = sw_http_09;
break;
}
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
switch (ch) { switch (ch) {
case '/': case '/':
r->uri_start = p; r->uri_start = p;
@@ -684,6 +711,16 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
case sw_http_HTTP: case sw_http_HTTP:
switch (ch) { switch (ch) {
case '/': case '/':
/*
* use single "/" from request line to preserve pointers,
* if request line will be copied to large client buffer
*/
if (r->method == NGX_HTTP_CONNECT) {
r->uri_start = p;
r->uri_end = p + 1;
}
state = sw_first_major_digit; state = sw_first_major_digit;
break; break;
default: default:
@@ -2140,7 +2177,7 @@ ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args)
ngx_int_t ngx_int_t
ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b, ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
ngx_http_chunked_t *ctx) ngx_http_chunked_t *ctx, ngx_uint_t keep_trailers)
{ {
u_char *pos, ch, c; u_char *pos, ch, c;
ngx_int_t rc; ngx_int_t rc;
@@ -2217,9 +2254,6 @@ ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
case CR: case CR:
state = sw_last_chunk_extension_almost_done; state = sw_last_chunk_extension_almost_done;
break; break;
case LF:
state = sw_trailer;
break;
case ';': case ';':
case ' ': case ' ':
case '\t': case '\t':
@@ -2236,9 +2270,6 @@ ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
case CR: case CR:
state = sw_chunk_extension_almost_done; state = sw_chunk_extension_almost_done;
break; break;
case LF:
state = sw_chunk_data;
break;
case ';': case ';':
case ' ': case ' ':
case '\t': case '\t':
@@ -2256,7 +2287,7 @@ ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
state = sw_chunk_extension_almost_done; state = sw_chunk_extension_almost_done;
break; break;
case LF: case LF:
state = sw_chunk_data; goto invalid;
} }
break; break;
@@ -2276,9 +2307,6 @@ ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
case CR: case CR:
state = sw_after_data_almost_done; state = sw_after_data_almost_done;
break; break;
case LF:
state = sw_chunk_start;
break;
default: default:
goto invalid; goto invalid;
} }
@@ -2297,12 +2325,15 @@ ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
state = sw_last_chunk_extension_almost_done; state = sw_last_chunk_extension_almost_done;
break; break;
case LF: case LF:
state = sw_trailer; goto invalid;
} }
break; break;
case sw_last_chunk_extension_almost_done: case sw_last_chunk_extension_almost_done:
if (ch == LF) { if (ch == LF) {
if (keep_trailers) {
goto done;
}
state = sw_trailer; state = sw_trailer;
break; break;
} }
@@ -2314,7 +2345,7 @@ ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
state = sw_trailer_almost_done; state = sw_trailer_almost_done;
break; break;
case LF: case LF:
goto done; goto invalid;
default: default:
state = sw_trailer_header; state = sw_trailer_header;
} }
@@ -2332,7 +2363,7 @@ ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
state = sw_trailer_header_almost_done; state = sw_trailer_header_almost_done;
break; break;
case LF: case LF:
state = sw_trailer; goto invalid;
} }
break; break;
@@ -2358,35 +2389,45 @@ data:
switch (state) { switch (state) {
case sw_chunk_start: case sw_chunk_start:
ctx->length = 3 /* "0" LF LF */; ctx->length = 5 /* "0" CRLF CRLF */;
break; break;
case sw_chunk_size: case sw_chunk_size:
ctx->length = 1 /* LF */ ctx->length = 2 /* CRLF */
+ (ctx->size ? ctx->size + 4 /* LF "0" LF LF */ + (ctx->size ? ctx->size + 7 /* CRLF "0" CRLF CRLF */
: 1 /* LF */); : 2 /* CRLF */);
break; break;
case sw_chunk_extension: case sw_chunk_extension:
ctx->length = 2 /* CRLF */ + ctx->size + 7 /* CRLF "0" CRLF CRLF */;
break;
case sw_chunk_extension_almost_done: case sw_chunk_extension_almost_done:
ctx->length = 1 /* LF */ + ctx->size + 4 /* LF "0" LF LF */; ctx->length = 1 /* LF */ + ctx->size + 7 /* CRLF "0" CRLF CRLF */;
break; break;
case sw_chunk_data: case sw_chunk_data:
ctx->length = ctx->size + 4 /* LF "0" LF LF */; ctx->length = ctx->size + 7 /* CRLF "0" CRLF CRLF */;
break; break;
case sw_after_data: case sw_after_data:
ctx->length = 7 /* CRLF "0" CRLF CRLF */;
break;
case sw_after_data_almost_done: case sw_after_data_almost_done:
ctx->length = 4 /* LF "0" LF LF */; ctx->length = 6 /* LF "0" CRLF CRLF */;
break; break;
case sw_last_chunk_extension: case sw_last_chunk_extension:
ctx->length = 4 /* CRLF CRLF */;
break;
case sw_last_chunk_extension_almost_done: case sw_last_chunk_extension_almost_done:
ctx->length = 2 /* LF LF */; ctx->length = 3 /* LF CRLF */;
break; break;
case sw_trailer: case sw_trailer:
ctx->length = 2 /* CRLF */;
break;
case sw_trailer_almost_done: case sw_trailer_almost_done:
ctx->length = 1 /* LF */; ctx->length = 1 /* LF */;
break; break;
case sw_trailer_header: case sw_trailer_header:
ctx->length = 4 /* CRLF CRLF */;
break;
case sw_trailer_header_almost_done: case sw_trailer_header_almost_done:
ctx->length = 2 /* LF LF */; ctx->length = 3 /* LF CRLF */;
break; break;
} }
+193 -80
View File
@@ -891,28 +891,47 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
return SSL_TLSEXT_ERR_ALERT_FATAL; return SSL_TLSEXT_ERR_ALERT_FATAL;
} }
if (c->ssl->sni_accepted) {
return SSL_TLSEXT_ERR_OK;
}
if (c->ssl->handshake_rejected) {
*ad = SSL_AD_UNRECOGNIZED_NAME;
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
hc = c->data; hc = c->data;
servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name); if (arg != NULL) {
host = *(ngx_str_t *) arg;
if (servername == NULL) { if (host.data == NULL) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"SSL server name: null"); "SSL server name: null");
goto done; goto done;
}
} else {
servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name);
if (servername == NULL) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"SSL server name: null");
goto done;
}
host.len = ngx_strlen(servername);
host.data = (u_char *) servername;
} }
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"SSL server name: \"%s\"", servername); "SSL server name: \"%V\"", &host);
host.len = ngx_strlen(servername);
if (host.len == 0) { if (host.len == 0) {
goto done; goto done;
} }
host.data = (u_char *) servername; rc = ngx_http_validate_host(&host, NULL, c->pool, 1);
rc = ngx_http_validate_host(&host, c->pool, 1);
if (rc == NGX_ERROR) { if (rc == NGX_ERROR) {
goto error; goto error;
@@ -933,31 +952,6 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
goto done; goto done;
} }
sscf = ngx_http_get_module_srv_conf(cscf->ctx, ngx_http_ssl_module);
#if (defined TLS1_3_VERSION \
&& !defined LIBRESSL_VERSION_NUMBER && !defined OPENSSL_IS_BORINGSSL)
/*
* SSL_SESSION_get0_hostname() is only available in OpenSSL 1.1.1+,
* but servername being negotiated in every TLSv1.3 handshake
* is only returned in OpenSSL 1.1.1+ as well
*/
if (sscf->verify) {
const char *hostname;
hostname = SSL_SESSION_get0_hostname(SSL_get0_session(ssl_conn));
if (hostname != NULL && ngx_strcmp(hostname, servername) != 0) {
c->ssl->handshake_rejected = 1;
*ad = SSL_AD_ACCESS_DENIED;
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
}
#endif
hc->ssl_servername = ngx_palloc(c->pool, sizeof(ngx_str_t)); hc->ssl_servername = ngx_palloc(c->pool, sizeof(ngx_str_t));
if (hc->ssl_servername == NULL) { if (hc->ssl_servername == NULL) {
goto error; goto error;
@@ -971,6 +965,8 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
ngx_set_connection_log(c, clcf->error_log); ngx_set_connection_log(c, clcf->error_log);
sscf = ngx_http_get_module_srv_conf(cscf->ctx, ngx_http_ssl_module);
c->ssl->buffer_size = sscf->buffer_size; c->ssl->buffer_size = sscf->buffer_size;
if (sscf->ssl.ctx) { if (sscf->ssl.ctx) {
@@ -1019,6 +1015,7 @@ done:
return SSL_TLSEXT_ERR_ALERT_FATAL; return SSL_TLSEXT_ERR_ALERT_FATAL;
} }
c->ssl->sni_accepted = 1;
return SSL_TLSEXT_ERR_OK; return SSL_TLSEXT_ERR_OK;
error: error:
@@ -1110,6 +1107,7 @@ ngx_http_process_request_line(ngx_event_t *rev)
ssize_t n; ssize_t n;
ngx_int_t rc, rv; ngx_int_t rc, rv;
ngx_str_t host; ngx_str_t host;
in_port_t port;
ngx_connection_t *c; ngx_connection_t *c;
ngx_http_request_t *r; ngx_http_request_t *r;
@@ -1172,7 +1170,7 @@ ngx_http_process_request_line(ngx_event_t *rev)
host.len = r->host_end - r->host_start; host.len = r->host_end - r->host_start;
host.data = r->host_start; host.data = r->host_start;
rc = ngx_http_validate_host(&host, r->pool, 0); rc = ngx_http_validate_host(&host, &port, r->pool, 0);
if (rc == NGX_DECLINED) { if (rc == NGX_DECLINED) {
ngx_log_error(NGX_LOG_INFO, c->log, 0, ngx_log_error(NGX_LOG_INFO, c->log, 0,
@@ -1191,6 +1189,7 @@ ngx_http_process_request_line(ngx_event_t *rev)
} }
r->headers_in.server = host; r->headers_in.server = host;
r->port = port;
} }
if (r->http_version < NGX_HTTP_VERSION_10) { if (r->http_version < NGX_HTTP_VERSION_10) {
@@ -1851,6 +1850,7 @@ ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h,
{ {
ngx_int_t rc; ngx_int_t rc;
ngx_str_t host; ngx_str_t host;
in_port_t port;
if (r->headers_in.host) { if (r->headers_in.host) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
@@ -1867,7 +1867,7 @@ ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h,
host = h->value; host = h->value;
rc = ngx_http_validate_host(&host, r->pool, 0); rc = ngx_http_validate_host(&host, &port, r->pool, 0);
if (rc == NGX_DECLINED) { if (rc == NGX_DECLINED) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
@@ -1890,6 +1890,7 @@ ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h,
} }
r->headers_in.server = host; r->headers_in.server = host;
r->port = port;
return NGX_OK; return NGX_OK;
} }
@@ -1988,6 +1989,8 @@ ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h,
static ngx_int_t static ngx_int_t
ngx_http_process_request_header(ngx_http_request_t *r) ngx_http_process_request_header(ngx_http_request_t *r)
{ {
ngx_http_core_srv_conf_t *cscf;
if (r->headers_in.server.len == 0 if (r->headers_in.server.len == 0
&& ngx_http_set_virtual_server(r, &r->headers_in.server) && ngx_http_set_virtual_server(r, &r->headers_in.server)
== NGX_ERROR) == NGX_ERROR)
@@ -2056,7 +2059,11 @@ ngx_http_process_request_header(ngx_http_request_t *r)
} }
} }
if (r->method == NGX_HTTP_CONNECT) { cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
if (r->method == NGX_HTTP_CONNECT
&& (r->http_version != NGX_HTTP_VERSION_11 || !cscf->allow_connect))
{
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent CONNECT method"); "client sent CONNECT method");
ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED); ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);
@@ -2167,74 +2174,176 @@ ngx_http_process_request(ngx_http_request_t *r)
ngx_int_t ngx_int_t
ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc) ngx_http_validate_host(ngx_str_t *host, in_port_t *portp, ngx_pool_t *pool,
ngx_uint_t alloc)
{ {
u_char *h, ch; u_char *h, ch;
size_t i, dot_pos, host_len; size_t i, dot_pos, host_len;
ngx_int_t port;
enum { enum {
sw_usual = 0, sw_host_start = 0,
sw_literal, sw_host,
sw_rest sw_host_ip_literal,
sw_host_end,
sw_port,
} state; } state;
dot_pos = host->len; dot_pos = host->len;
host_len = host->len; host_len = host->len;
port = 0;
h = host->data; h = host->data;
state = sw_usual; state = sw_host_start;
for (i = 0; i < host->len; i++) { for (i = 0; i < host->len; i++) {
ch = h[i]; ch = h[i];
switch (ch) { switch (state) {
case '.': case sw_host_start:
if (dot_pos == i - 1) {
return NGX_DECLINED;
}
dot_pos = i;
break;
case ':': if (ch == '[') {
if (state == sw_usual) { state = sw_host_ip_literal;
host_len = i; break;
state = sw_rest;
}
break;
case '[':
if (i == 0) {
state = sw_literal;
}
break;
case ']':
if (state == sw_literal) {
host_len = i + 1;
state = sw_rest;
}
break;
default:
if (ngx_path_separator(ch)) {
return NGX_DECLINED;
} }
if (ch <= 0x20 || ch == 0x7f) { state = sw_host;
return NGX_DECLINED;
} /* fall through */
case sw_host:
if (ch >= 'A' && ch <= 'Z') { if (ch >= 'A' && ch <= 'Z') {
alloc = 1; alloc = 1;
break;
} }
if (ch >= 'a' && ch <= 'z') {
break;
}
if (ch >= '0' && ch <= '9') {
break;
}
switch (ch) {
case ':':
host_len = i;
state = sw_port;
break;
case '-':
break;
case '.':
if (dot_pos == i - 1) {
return NGX_DECLINED;
}
dot_pos = i;
break;
case '_':
case '~':
/* unreserved */
break;
case '!':
case '$':
case '&':
case '\'':
case '(':
case ')':
case '*':
case '+':
case ',':
case ';':
case '=':
/* sub-delims */
break;
case '%':
/* pct-encoded */
break;
default:
return NGX_DECLINED;
}
break; break;
case sw_host_ip_literal:
if (ch >= 'A' && ch <= 'Z') {
alloc = 1;
break;
}
if (ch >= 'a' && ch <= 'z') {
break;
}
if (ch >= '0' && ch <= '9') {
break;
}
switch (ch) {
case ':':
break;
case ']':
host_len = i + 1;
state = sw_host_end;
break;
case '-':
break;
case '.':
if (dot_pos == i - 1) {
return NGX_DECLINED;
}
dot_pos = i;
break;
case '_':
case '~':
/* unreserved */
break;
case '!':
case '$':
case '&':
case '\'':
case '(':
case ')':
case '*':
case '+':
case ',':
case ';':
case '=':
/* sub-delims */
break;
default:
return NGX_DECLINED;
}
break;
case sw_host_end:
if (ch == ':') {
state = sw_port;
break;
}
return NGX_DECLINED;
case sw_port:
if (ch >= '0' && ch <= '9') {
if (port >= 6553 && (port > 6553 || (ch - '0') > 5)) {
return NGX_DECLINED;
}
port = port * 10 + (ch - '0');
break;
}
return NGX_DECLINED;
} }
} }
if (state == sw_host_ip_literal) {
return NGX_DECLINED;
}
if (dot_pos == host_len - 1) { if (dot_pos == host_len - 1) {
host_len--; host_len--;
} }
@@ -2254,6 +2363,10 @@ ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc)
host->len = host_len; host->len = host_len;
if (portp) {
*portp = port;
}
return NGX_OK; return NGX_OK;
} }
+2
View File
@@ -461,6 +461,8 @@ struct ngx_http_request_s {
ngx_http_cleanup_t *cleanup; ngx_http_cleanup_t *cleanup;
in_port_t port;
unsigned count:16; unsigned count:16;
unsigned subrequests:8; unsigned subrequests:8;
unsigned blocked:8; unsigned blocked:8;
+2 -2
View File
@@ -870,7 +870,7 @@ ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b)
for ( ;; ) { for ( ;; ) {
rc = ngx_http_parse_chunked(r, b, rb->chunked); rc = ngx_http_parse_chunked(r, b, rb->chunked, 0);
if (rc == NGX_OK) { if (rc == NGX_OK) {
@@ -1131,7 +1131,7 @@ ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in)
cl->buf->file_pos, cl->buf->file_pos,
cl->buf->file_last - cl->buf->file_pos); cl->buf->file_last - cl->buf->file_pos);
rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked); rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked, 0);
if (rc == NGX_OK) { if (rc == NGX_OK) {
+66 -34
View File
@@ -116,6 +116,10 @@ static ngx_int_t ngx_http_upstream_process_set_cookie(ngx_http_request_t *r,
static ngx_int_t static ngx_int_t
ngx_http_upstream_process_cache_control(ngx_http_request_t *r, ngx_http_upstream_process_cache_control(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset); ngx_table_elt_t *h, ngx_uint_t offset);
#if (NGX_HTTP_CACHE)
static ngx_int_t ngx_http_upstream_process_delta_seconds(u_char *p,
u_char *last);
#endif
static ngx_int_t ngx_http_upstream_ignore_header_line(ngx_http_request_t *r, static ngx_int_t ngx_http_upstream_ignore_header_line(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset); ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_process_expires(ngx_http_request_t *r, static ngx_int_t ngx_http_upstream_process_expires(ngx_http_request_t *r,
@@ -1767,6 +1771,23 @@ ngx_http_upstream_ssl_init_connection(ngx_http_request_t *r,
} }
} }
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
if (u->ssl_alpn_protocol.len) {
if (SSL_set_alpn_protos(c->ssl->connection, u->ssl_alpn_protocol.data,
u->ssl_alpn_protocol.len)
!= 0)
{
ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
"SSL_set_alpn_protos() failed");
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
}
#endif
if (u->conf->ssl_session_reuse) { if (u->conf->ssl_session_reuse) {
c->ssl->save_session = ngx_http_upstream_ssl_save_session; c->ssl->save_session = ngx_http_upstream_ssl_save_session;
@@ -5066,18 +5087,9 @@ ngx_http_upstream_process_cache_control(ngx_http_request_t *r,
} }
if (p) { if (p) {
n = 0; n = ngx_http_upstream_process_delta_seconds(p + offset, last);
for (p += offset; p < last; p++) {
if (*p == ',' || *p == ';' || *p == ' ') {
break;
}
if (*p >= '0' && *p <= '9') {
n = n * 10 + (*p - '0');
continue;
}
if (n == NGX_ERROR) {
u->cacheable = 0; u->cacheable = 0;
return NGX_OK; return NGX_OK;
} }
@@ -5087,7 +5099,8 @@ ngx_http_upstream_process_cache_control(ngx_http_request_t *r,
return NGX_OK; return NGX_OK;
} }
r->cache->valid_sec = ngx_time() + n; r->cache->valid_sec = ngx_min((ngx_uint_t) ngx_time() + n,
NGX_MAX_INT_T_VALUE);
u->headers_in.expired = 0; u->headers_in.expired = 0;
} }
@@ -5097,18 +5110,9 @@ extensions:
23 - 1); 23 - 1);
if (p) { if (p) {
n = 0; n = ngx_http_upstream_process_delta_seconds(p + 23, last);
for (p += 23; p < last; p++) {
if (*p == ',' || *p == ';' || *p == ' ') {
break;
}
if (*p >= '0' && *p <= '9') {
n = n * 10 + (*p - '0');
continue;
}
if (n == NGX_ERROR) {
u->cacheable = 0; u->cacheable = 0;
return NGX_OK; return NGX_OK;
} }
@@ -5120,18 +5124,9 @@ extensions:
p = ngx_strlcasestrn(start, last, (u_char *) "stale-if-error=", 15 - 1); p = ngx_strlcasestrn(start, last, (u_char *) "stale-if-error=", 15 - 1);
if (p) { if (p) {
n = 0; n = ngx_http_upstream_process_delta_seconds(p + 15, last);
for (p += 15; p < last; p++) {
if (*p == ',' || *p == ';' || *p == ' ') {
break;
}
if (*p >= '0' && *p <= '9') {
n = n * 10 + (*p - '0');
continue;
}
if (n == NGX_ERROR) {
u->cacheable = 0; u->cacheable = 0;
return NGX_OK; return NGX_OK;
} }
@@ -5145,6 +5140,41 @@ extensions:
} }
#if (NGX_HTTP_CACHE)
static ngx_int_t
ngx_http_upstream_process_delta_seconds(u_char *p, u_char *last)
{
ngx_int_t n, cutoff, cutlim;
cutoff = NGX_MAX_INT_T_VALUE / 10;
cutlim = NGX_MAX_INT_T_VALUE % 10;
n = 0;
for ( /* void */ ; p < last; p++) {
if (*p == ',' || *p == ';' || *p == ' ') {
break;
}
if (*p < '0' || *p > '9') {
return NGX_ERROR;
}
if (n >= cutoff && (n > cutoff || *p - '0' > cutlim)) {
n = NGX_MAX_INT_T_VALUE;
break;
}
n = n * 10 + (*p - '0');
}
return n;
}
#endif
static ngx_int_t static ngx_int_t
ngx_http_upstream_process_expires(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_http_upstream_process_expires(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset) ngx_uint_t offset)
@@ -6885,6 +6915,7 @@ ngx_http_upstream_set_local(ngx_http_request_t *r, ngx_http_upstream_t *u,
} }
if (val.len == 0) { if (val.len == 0) {
u->peer.local = NULL;
return NGX_OK; return NGX_OK;
} }
@@ -6901,6 +6932,7 @@ ngx_http_upstream_set_local(ngx_http_request_t *r, ngx_http_upstream_t *u,
if (rc != NGX_OK) { if (rc != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"invalid local address \"%V\"", &val); "invalid local address \"%V\"", &val);
u->peer.local = NULL;
return NGX_OK; return NGX_OK;
} }
+2 -1
View File
@@ -253,7 +253,7 @@ typedef struct {
ngx_str_t module; ngx_str_t module;
NGX_COMPAT_BEGIN(2) NGX_COMPAT_BEGIN(6)
NGX_COMPAT_END NGX_COMPAT_END
} ngx_http_upstream_conf_t; } ngx_http_upstream_conf_t;
@@ -390,6 +390,7 @@ struct ngx_http_upstream_s {
#if (NGX_HTTP_SSL || NGX_COMPAT) #if (NGX_HTTP_SSL || NGX_COMPAT)
ngx_str_t ssl_name; ngx_str_t ssl_name;
ngx_str_t ssl_alpn_protocol;
#endif #endif
ngx_http_cleanup_pt *cleanup; ngx_http_cleanup_pt *cleanup;
+55
View File
@@ -71,6 +71,10 @@ static ngx_int_t ngx_http_variable_scheme(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data); ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_https(ngx_http_request_t *r, static ngx_int_t ngx_http_variable_https(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data); ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_request_port(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_is_request_port(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static void ngx_http_variable_set_args(ngx_http_request_t *r, static void ngx_http_variable_set_args(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data); ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_is_args(ngx_http_request_t *r, static ngx_int_t ngx_http_variable_is_args(ngx_http_request_t *r,
@@ -231,6 +235,12 @@ static ngx_http_variable_t ngx_http_core_variables[] = {
{ ngx_string("https"), NULL, ngx_http_variable_https, 0, 0, 0 }, { ngx_string("https"), NULL, ngx_http_variable_https, 0, 0, 0 },
{ ngx_string("request_port"), NULL,
ngx_http_variable_request_port, 0, 0, 0 },
{ ngx_string("is_request_port"), NULL,
ngx_http_variable_is_request_port, 0, 0, 0 },
{ ngx_string("request_uri"), NULL, ngx_http_variable_request, { ngx_string("request_uri"), NULL, ngx_http_variable_request,
offsetof(ngx_http_request_t, unparsed_uri), 0, 0 }, offsetof(ngx_http_request_t, unparsed_uri), 0, 0 },
@@ -1540,6 +1550,51 @@ ngx_http_variable_https(ngx_http_request_t *r,
} }
static ngx_int_t
ngx_http_variable_request_port(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_uint_t port;
v->len = 0;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1);
if (v->data == NULL) {
return NGX_ERROR;
}
port = r->port;
if (port > 0 && port < 65536) {
v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_is_request_port(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
if (r->port == 0) {
*v = ngx_http_variable_null_value;
return NGX_OK;
}
v->len = 1;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = (u_char *) ":";
return NGX_OK;
}
static void static void
ngx_http_variable_set_args(ngx_http_request_t *r, ngx_http_variable_set_args(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data) ngx_http_variable_value_t *v, uintptr_t data)
+8 -7
View File
@@ -3528,6 +3528,7 @@ static ngx_int_t
ngx_http_v2_parse_authority(ngx_http_request_t *r, ngx_str_t *value) ngx_http_v2_parse_authority(ngx_http_request_t *r, ngx_str_t *value)
{ {
ngx_int_t rc; ngx_int_t rc;
in_port_t port;
if (r->host_start) { if (r->host_start) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
@@ -3538,7 +3539,7 @@ ngx_http_v2_parse_authority(ngx_http_request_t *r, ngx_str_t *value)
r->host_start = value->data; r->host_start = value->data;
r->host_end = value->data + value->len; r->host_end = value->data + value->len;
rc = ngx_http_validate_host(value, r->pool, 0); rc = ngx_http_validate_host(value, &port, r->pool, 0);
if (rc == NGX_DECLINED) { if (rc == NGX_DECLINED) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
@@ -3560,6 +3561,7 @@ ngx_http_v2_parse_authority(ngx_http_request_t *r, ngx_str_t *value)
} }
r->headers_in.server = *value; r->headers_in.server = *value;
r->port = port;
return NGX_OK; return NGX_OK;
} }
@@ -4111,15 +4113,14 @@ ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos,
n = size; n = size;
} }
if (n > 0) {
rb->buf->last = ngx_cpymem(rb->buf->last, pos, n);
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 request body recv %uz", n); "http2 request body recv %uz", n);
pos += n; if (n > 0) {
size -= n; rb->buf->last = ngx_cpymem(rb->buf->last, pos, n);
pos += n;
size -= n;
}
if (size == 0 && last) { if (size == 0 && last) {
rb->rest = 0; rb->rest = 0;
+3 -1
View File
@@ -904,6 +904,7 @@ ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r)
u_char *p; u_char *p;
ngx_int_t rc; ngx_int_t rc;
ngx_str_t host; ngx_str_t host;
in_port_t port;
if (r->request_line.len) { if (r->request_line.len) {
return NGX_OK; return NGX_OK;
@@ -961,7 +962,7 @@ ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r)
host.len = r->host_end - r->host_start; host.len = r->host_end - r->host_start;
host.data = r->host_start; host.data = r->host_start;
rc = ngx_http_validate_host(&host, r->pool, 0); rc = ngx_http_validate_host(&host, &port, r->pool, 0);
if (rc == NGX_DECLINED) { if (rc == NGX_DECLINED) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
@@ -979,6 +980,7 @@ ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r)
} }
r->headers_in.server = host; r->headers_in.server = host;
r->port = port;
} }
if (ngx_list_init(&r->headers_in.headers, r->pool, 20, if (ngx_list_init(&r->headers_in.headers, r->pool, 20,
+13 -1
View File
@@ -531,6 +531,7 @@ ngx_mail_proxy_smtp_handler(ngx_event_t *rev)
ngx_int_t rc; ngx_int_t rc;
ngx_str_t line, auth, encoded; ngx_str_t line, auth, encoded;
ngx_buf_t *b; ngx_buf_t *b;
uintptr_t n;
ngx_connection_t *c; ngx_connection_t *c;
ngx_mail_session_t *s; ngx_mail_session_t *s;
ngx_mail_proxy_conf_t *pcf; ngx_mail_proxy_conf_t *pcf;
@@ -627,6 +628,10 @@ ngx_mail_proxy_smtp_handler(ngx_event_t *rev)
CRLF) - 1 CRLF) - 1
+ s->connection->addr_text.len + s->login.len + s->host.len; + s->connection->addr_text.len + s->login.len + s->host.len;
n = ngx_escape_uri(NULL, s->login.data, s->login.len,
NGX_ESCAPE_MAIL_XTEXT);
line.len += n * 2;
#if (NGX_HAVE_INET6) #if (NGX_HAVE_INET6)
if (s->connection->sockaddr->sa_family == AF_INET6) { if (s->connection->sockaddr->sa_family == AF_INET6) {
line.len += sizeof("IPV6:") - 1; line.len += sizeof("IPV6:") - 1;
@@ -654,7 +659,14 @@ ngx_mail_proxy_smtp_handler(ngx_event_t *rev)
if (s->login.len && !pcf->smtp_auth) { if (s->login.len && !pcf->smtp_auth) {
p = ngx_cpymem(p, " LOGIN=", sizeof(" LOGIN=") - 1); p = ngx_cpymem(p, " LOGIN=", sizeof(" LOGIN=") - 1);
p = ngx_copy(p, s->login.data, s->login.len);
if (n == 0) {
p = ngx_copy(p, s->login.data, s->login.len);
} else {
p = (u_char *) ngx_escape_uri(p, s->login.data, s->login.len,
NGX_ESCAPE_MAIL_XTEXT);
}
} }
p = ngx_cpymem(p, " NAME=", sizeof(" NAME=") - 1); p = ngx_cpymem(p, " NAME=", sizeof(" NAME=") - 1);
+141 -40
View File
@@ -474,72 +474,173 @@ ngx_stream_core_content_phase(ngx_stream_session_t *s,
ngx_int_t ngx_int_t
ngx_stream_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc) ngx_stream_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc)
{ {
u_char *h, ch; u_char *h, ch;
size_t i, dot_pos, host_len; size_t i, dot_pos, host_len;
ngx_int_t port;
enum { enum {
sw_usual = 0, sw_host_start = 0,
sw_literal, sw_host,
sw_rest sw_host_ip_literal,
sw_host_end,
sw_port,
} state; } state;
dot_pos = host->len; dot_pos = host->len;
host_len = host->len; host_len = host->len;
port = 0;
h = host->data; h = host->data;
state = sw_usual; state = sw_host_start;
for (i = 0; i < host->len; i++) { for (i = 0; i < host->len; i++) {
ch = h[i]; ch = h[i];
switch (ch) { switch (state) {
case '.': case sw_host_start:
if (dot_pos == i - 1) {
return NGX_DECLINED;
}
dot_pos = i;
break;
case ':': if (ch == '[') {
if (state == sw_usual) { state = sw_host_ip_literal;
host_len = i; break;
state = sw_rest;
}
break;
case '[':
if (i == 0) {
state = sw_literal;
}
break;
case ']':
if (state == sw_literal) {
host_len = i + 1;
state = sw_rest;
}
break;
default:
if (ngx_path_separator(ch)) {
return NGX_DECLINED;
} }
if (ch <= 0x20 || ch == 0x7f) { state = sw_host;
return NGX_DECLINED;
} /* fall through */
case sw_host:
if (ch >= 'A' && ch <= 'Z') { if (ch >= 'A' && ch <= 'Z') {
alloc = 1; alloc = 1;
break;
} }
if (ch >= 'a' && ch <= 'z') {
break;
}
if (ch >= '0' && ch <= '9') {
break;
}
switch (ch) {
case ':':
host_len = i;
state = sw_port;
break;
case '-':
break;
case '.':
if (dot_pos == i - 1) {
return NGX_DECLINED;
}
dot_pos = i;
break;
case '_':
case '~':
/* unreserved */
break;
case '!':
case '$':
case '&':
case '\'':
case '(':
case ')':
case '*':
case '+':
case ',':
case ';':
case '=':
/* sub-delims */
break;
case '%':
/* pct-encoded */
break;
default:
return NGX_DECLINED;
}
break; break;
case sw_host_ip_literal:
if (ch >= 'A' && ch <= 'Z') {
alloc = 1;
break;
}
if (ch >= 'a' && ch <= 'z') {
break;
}
if (ch >= '0' && ch <= '9') {
break;
}
switch (ch) {
case ':':
break;
case ']':
host_len = i + 1;
state = sw_host_end;
break;
case '-':
break;
case '.':
if (dot_pos == i - 1) {
return NGX_DECLINED;
}
dot_pos = i;
break;
case '_':
case '~':
/* unreserved */
break;
case '!':
case '$':
case '&':
case '\'':
case '(':
case ')':
case '*':
case '+':
case ',':
case ';':
case '=':
/* sub-delims */
break;
default:
return NGX_DECLINED;
}
break;
case sw_host_end:
if (ch == ':') {
state = sw_port;
break;
}
return NGX_DECLINED;
case sw_port:
if (ch >= '0' && ch <= '9') {
if (port >= 6553 && (port > 6553 || (ch - '0') > 5)) {
return NGX_DECLINED;
}
port = port * 10 + (ch - '0');
break;
}
return NGX_DECLINED;
} }
} }
if (state == sw_host_ip_literal) {
return NGX_DECLINED;
}
if (dot_pos == host_len - 1) { if (dot_pos == host_len - 1) {
host_len--; host_len--;
} }
+12
View File
@@ -61,6 +61,7 @@ typedef struct {
unsigned outside_entries:1; unsigned outside_entries:1;
unsigned allow_binary_include:1; unsigned allow_binary_include:1;
unsigned binary_include:1; unsigned binary_include:1;
unsigned no_cacheable:1;
} ngx_stream_geo_conf_ctx_t; } ngx_stream_geo_conf_ctx_t;
@@ -433,6 +434,7 @@ ngx_stream_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+ sizeof(ngx_stream_variable_value_t) + sizeof(ngx_stream_variable_value_t)
+ 0x10000 * sizeof(ngx_stream_geo_range_t *); + 0x10000 * sizeof(ngx_stream_geo_range_t *);
ctx.allow_binary_include = 1; ctx.allow_binary_include = 1;
ctx.no_cacheable = 0;
save = *cf; save = *cf;
cf->pool = pool; cf->pool = pool;
@@ -448,6 +450,10 @@ ngx_stream_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
goto failed; goto failed;
} }
if (ctx.no_cacheable) {
var->flags |= NGX_STREAM_VAR_NOCACHEABLE;
}
if (ctx.ranges) { if (ctx.ranges) {
if (ctx.high.low && !ctx.binary_include) { if (ctx.high.low && !ctx.binary_include) {
@@ -583,6 +589,12 @@ ngx_stream_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
goto done; goto done;
} }
else if (ngx_strcmp(value[0].data, "volatile") == 0) {
ctx->no_cacheable = 1;
rv = NGX_CONF_OK;
goto done;
}
} }
if (cf->args->nelts != 2) { if (cf->args->nelts != 2) {
+2
View File
@@ -680,6 +680,7 @@ ngx_stream_proxy_set_local(ngx_stream_session_t *s, ngx_stream_upstream_t *u,
} }
if (val.len == 0) { if (val.len == 0) {
u->peer.local = NULL;
return NGX_OK; return NGX_OK;
} }
@@ -696,6 +697,7 @@ ngx_stream_proxy_set_local(ngx_stream_session_t *s, ngx_stream_upstream_t *u,
if (rc != NGX_OK) { if (rc != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"invalid local address \"%V\"", &val); "invalid local address \"%V\"", &val);
u->peer.local = NULL;
return NGX_OK; return NGX_OK;
} }
+75 -37
View File
@@ -126,6 +126,13 @@ static ngx_command_t ngx_stream_ssl_commands[] = {
0, 0,
NULL }, NULL },
{ ngx_string("ssl_ech_file"),
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
NGX_STREAM_SRV_CONF_OFFSET,
offsetof(ngx_stream_ssl_srv_conf_t, ech_files),
NULL },
{ ngx_string("ssl_password_file"), { ngx_string("ssl_password_file"),
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
ngx_stream_ssl_password_file, ngx_stream_ssl_password_file,
@@ -239,7 +246,7 @@ static ngx_command_t ngx_stream_ssl_commands[] = {
NULL }, NULL },
{ ngx_string("ssl_ocsp"), { ngx_string("ssl_ocsp"),
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_enum_slot, ngx_conf_set_enum_slot,
NGX_STREAM_SRV_CONF_OFFSET, NGX_STREAM_SRV_CONF_OFFSET,
offsetof(ngx_stream_ssl_srv_conf_t, ocsp), offsetof(ngx_stream_ssl_srv_conf_t, ocsp),
@@ -357,6 +364,9 @@ static ngx_stream_variable_t ngx_stream_ssl_vars[] = {
{ ngx_string("ssl_curves"), NULL, ngx_stream_ssl_variable, { ngx_string("ssl_curves"), NULL, ngx_stream_ssl_variable,
(uintptr_t) ngx_ssl_get_curves, NGX_STREAM_VAR_CHANGEABLE, 0 }, (uintptr_t) ngx_ssl_get_curves, NGX_STREAM_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_sigalg"), NULL, ngx_stream_ssl_variable,
(uintptr_t) ngx_ssl_get_sigalg, NGX_STREAM_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_session_id"), NULL, ngx_stream_ssl_variable, { ngx_string("ssl_session_id"), NULL, ngx_stream_ssl_variable,
(uintptr_t) ngx_ssl_get_session_id, NGX_STREAM_VAR_CHANGEABLE, 0 }, (uintptr_t) ngx_ssl_get_session_id, NGX_STREAM_VAR_CHANGEABLE, 0 },
@@ -369,6 +379,13 @@ static ngx_stream_variable_t ngx_stream_ssl_vars[] = {
{ ngx_string("ssl_alpn_protocol"), NULL, ngx_stream_ssl_variable, { ngx_string("ssl_alpn_protocol"), NULL, ngx_stream_ssl_variable,
(uintptr_t) ngx_ssl_get_alpn_protocol, NGX_STREAM_VAR_CHANGEABLE, 0 }, (uintptr_t) ngx_ssl_get_alpn_protocol, NGX_STREAM_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_ech_status"), NULL, ngx_stream_ssl_variable,
(uintptr_t) ngx_ssl_get_ech_status, NGX_STREAM_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_ech_outer_server_name"), NULL, ngx_stream_ssl_variable,
(uintptr_t) ngx_ssl_get_ech_outer_server_name,
NGX_STREAM_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_client_cert"), NULL, ngx_stream_ssl_variable, { ngx_string("ssl_client_cert"), NULL, ngx_stream_ssl_variable,
(uintptr_t) ngx_ssl_get_certificate, NGX_STREAM_VAR_CHANGEABLE, 0 }, (uintptr_t) ngx_ssl_get_certificate, NGX_STREAM_VAR_CHANGEABLE, 0 },
@@ -404,6 +421,9 @@ static ngx_stream_variable_t ngx_stream_ssl_vars[] = {
{ ngx_string("ssl_client_v_remain"), NULL, ngx_stream_ssl_variable, { ngx_string("ssl_client_v_remain"), NULL, ngx_stream_ssl_variable,
(uintptr_t) ngx_ssl_get_client_v_remain, NGX_STREAM_VAR_CHANGEABLE, 0 }, (uintptr_t) ngx_ssl_get_client_v_remain, NGX_STREAM_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_client_sigalg"), NULL, ngx_stream_ssl_variable,
(uintptr_t) ngx_ssl_get_client_sigalg, NGX_STREAM_VAR_CHANGEABLE, 0 },
ngx_stream_null_variable ngx_stream_null_variable
}; };
@@ -555,27 +575,46 @@ ngx_stream_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
return SSL_TLSEXT_ERR_ALERT_FATAL; return SSL_TLSEXT_ERR_ALERT_FATAL;
} }
if (c->ssl->sni_accepted) {
return SSL_TLSEXT_ERR_OK;
}
if (c->ssl->handshake_rejected) {
*ad = SSL_AD_UNRECOGNIZED_NAME;
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
s = c->data; s = c->data;
servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name); if (arg) {
host = *(ngx_str_t *) arg;
if (servername == NULL) { if (host.data == NULL) {
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
"SSL server name: null"); "SSL server name: null");
goto done; goto done;
}
} else {
servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name);
if (servername == NULL) {
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
"SSL server name: null");
goto done;
}
host.len = ngx_strlen(servername);
host.data = (u_char *) servername;
} }
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
"SSL server name: \"%s\"", servername); "SSL server name: \"%V\"", &host);
host.len = ngx_strlen(servername);
if (host.len == 0) { if (host.len == 0) {
goto done; goto done;
} }
host.data = (u_char *) servername;
rc = ngx_stream_validate_host(&host, c->pool, 1); rc = ngx_stream_validate_host(&host, c->pool, 1);
if (rc == NGX_ERROR) { if (rc == NGX_ERROR) {
@@ -596,35 +635,12 @@ ngx_stream_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
goto done; goto done;
} }
sscf = ngx_stream_get_module_srv_conf(cscf->ctx, ngx_stream_ssl_module);
#if (defined TLS1_3_VERSION \
&& !defined LIBRESSL_VERSION_NUMBER && !defined OPENSSL_IS_BORINGSSL)
/*
* SSL_SESSION_get0_hostname() is only available in OpenSSL 1.1.1+,
* but servername being negotiated in every TLSv1.3 handshake
* is only returned in OpenSSL 1.1.1+ as well
*/
if (sscf->verify) {
const char *hostname;
hostname = SSL_SESSION_get0_hostname(SSL_get0_session(ssl_conn));
if (hostname != NULL && ngx_strcmp(hostname, servername) != 0) {
c->ssl->handshake_rejected = 1;
*ad = SSL_AD_ACCESS_DENIED;
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
}
#endif
s->srv_conf = cscf->ctx->srv_conf; s->srv_conf = cscf->ctx->srv_conf;
ngx_set_connection_log(c, cscf->error_log); ngx_set_connection_log(c, cscf->error_log);
sscf = ngx_stream_get_module_srv_conf(cscf->ctx, ngx_stream_ssl_module);
if (sscf->ssl.ctx) { if (sscf->ssl.ctx) {
if (SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx) == NULL) { if (SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx) == NULL) {
goto error; goto error;
@@ -663,6 +679,7 @@ done:
return SSL_TLSEXT_ERR_ALERT_FATAL; return SSL_TLSEXT_ERR_ALERT_FATAL;
} }
c->ssl->sni_accepted = 1;
return SSL_TLSEXT_ERR_OK; return SSL_TLSEXT_ERR_OK;
error: error:
@@ -885,6 +902,7 @@ ngx_stream_ssl_create_srv_conf(ngx_conf_t *cf)
sscf->certificates = NGX_CONF_UNSET_PTR; sscf->certificates = NGX_CONF_UNSET_PTR;
sscf->certificate_keys = NGX_CONF_UNSET_PTR; sscf->certificate_keys = NGX_CONF_UNSET_PTR;
sscf->certificate_cache = NGX_CONF_UNSET_PTR; sscf->certificate_cache = NGX_CONF_UNSET_PTR;
sscf->ech_files = NGX_CONF_UNSET_PTR;
sscf->passwords = NGX_CONF_UNSET_PTR; sscf->passwords = NGX_CONF_UNSET_PTR;
sscf->conf_commands = NGX_CONF_UNSET_PTR; sscf->conf_commands = NGX_CONF_UNSET_PTR;
sscf->prefer_server_ciphers = NGX_CONF_UNSET; sscf->prefer_server_ciphers = NGX_CONF_UNSET;
@@ -940,6 +958,8 @@ ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_ptr_value(conf->certificate_cache, prev->certificate_cache, ngx_conf_merge_ptr_value(conf->certificate_cache, prev->certificate_cache,
NULL); NULL);
ngx_conf_merge_ptr_value(conf->ech_files, prev->ech_files, NULL);
ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL); ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);
ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, ""); ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
@@ -1002,8 +1022,16 @@ ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
cln->data = &conf->ssl; cln->data = &conf->ssl;
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
{
static ngx_ssl_client_hello_arg cb = { ngx_stream_ssl_servername };
if (ngx_ssl_set_client_hello_callback(&conf->ssl, &cb) != NGX_OK) {
return NGX_CONF_ERROR;
}
SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx, SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx,
ngx_stream_ssl_servername); ngx_stream_ssl_servername);
}
#endif #endif
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
@@ -1109,6 +1137,10 @@ ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
return NGX_CONF_ERROR; return NGX_CONF_ERROR;
} }
if (ngx_ssl_ech_files(cf, &conf->ssl, conf->ech_files) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) { if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) {
return NGX_CONF_ERROR; return NGX_CONF_ERROR;
} }
@@ -1148,13 +1180,19 @@ ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
if (conf->stapling) { if (conf->stapling) {
if (conf->certificate_compression) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"\"ssl_stapling\" is incompatible with "
"\"ssl_certificate_compression\"");
return NGX_CONF_ERROR;
}
if (ngx_ssl_stapling(cf, &conf->ssl, &conf->stapling_file, if (ngx_ssl_stapling(cf, &conf->ssl, &conf->stapling_file,
&conf->stapling_responder, conf->stapling_verify) &conf->stapling_responder, conf->stapling_verify)
!= NGX_OK) != NGX_OK)
{ {
return NGX_CONF_ERROR; return NGX_CONF_ERROR;
} }
} }
if (ngx_ssl_conf_commands(cf, &conf->ssl, conf->conf_commands) != NGX_OK) { if (ngx_ssl_conf_commands(cf, &conf->ssl, conf->conf_commands) != NGX_OK) {
+1
View File
@@ -49,6 +49,7 @@ typedef struct {
ngx_str_t ciphers; ngx_str_t ciphers;
ngx_array_t *ech_files;
ngx_array_t *passwords; ngx_array_t *passwords;
ngx_array_t *conf_commands; ngx_array_t *conf_commands;