Compare commits
12
Commits
da24305046
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9d52d14d0 | ||
|
|
94e5c04900 | ||
|
|
82f432938a | ||
|
|
694f182c33 | ||
|
|
d42d1aa241 | ||
|
|
602334da65 | ||
|
|
b65b2d1663 | ||
|
|
45064635e8 | ||
|
|
5a9ef13268 | ||
|
|
f35d500e17 | ||
|
|
ae58265dda | ||
|
|
90c231613f |
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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/).
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -1,10 +1,10 @@
|
||||
### 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
|
||||
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).
|
||||
### Checklist
|
||||
|
||||
Before creating a pull request, make sure to comply with the
|
||||
[Contributing Guidelines](https://github.com/nginx/nginx/blob/master/CONTRIBUTING.md).
|
||||
Before creating a PR, run through this checklist and mark each as complete:
|
||||
|
||||
- [ ] I have read the [contributing guidelines](/CONTRIBUTING.md).
|
||||
- [ ] I have checked that NGINX compiles and runs after adding my changes.
|
||||
@@ -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
@@ -5,34 +5,34 @@ We really appreciate that you are considering contributing!
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Ask a Question](#ask-a-question)
|
||||
- [Report a Bug](#report-a-bug)
|
||||
- [Suggest a Feature or Enhancement](#suggest-a-feature-or-enhancement)
|
||||
- [Open a Discussion](#open-a-discussion)
|
||||
- [Submit a Pull Request](#submit-a-pull-request)
|
||||
- [Issue Lifecycle](#issue-lifecycle)
|
||||
|
||||
## Ask a Question
|
||||
|
||||
To ask a question, open an issue on GitHub with the label `question`.
|
||||
|
||||
## Report a Bug
|
||||
|
||||
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
|
||||
issue has not already been reported.
|
||||
available [bug report issue form](/.github/ISSUE_TEMPLATE/bug_report.yml).
|
||||
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
|
||||
|
||||
To suggest a feature or enhancement, open an issue on GitHub with the label
|
||||
`feature` or `enhancement` using the available feature request issue template.
|
||||
To suggest a feature or enhancement, please create an issue on GitHub with the
|
||||
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.
|
||||
|
||||
## Open a Discussion
|
||||
|
||||
If you want to engage in a conversation with the community and maintainers,
|
||||
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
|
||||
|
||||
@@ -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
|
||||
[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
|
||||
|
||||
|
||||
+48
@@ -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.
|
||||
@@ -5,15 +5,6 @@
|
||||
# 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"
|
||||
|
||||
|
||||
|
||||
+2
-10
@@ -8,14 +8,6 @@
|
||||
# 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"
|
||||
|
||||
CC_TEST_FLAGS="-pipe"
|
||||
@@ -112,7 +104,7 @@ esac
|
||||
|
||||
CC_AUX_FLAGS="$CC_AUX_FLAGS $CPU_OPT"
|
||||
|
||||
case "$NGX_GCC_VER" in
|
||||
case "$NGX_CC_VER" in
|
||||
2.7*)
|
||||
# batch build
|
||||
CPU_OPT=
|
||||
@@ -145,7 +137,7 @@ CFLAGS="$CFLAGS -Wall -Wpointer-arith"
|
||||
#CFLAGS="$CFLAGS -Winline"
|
||||
#CFLAGS="$CFLAGS -Wmissing-prototypes"
|
||||
|
||||
case "$NGX_GCC_VER" in
|
||||
case "$NGX_CC_VER" in
|
||||
2.*)
|
||||
# we have a lot of the unused function arguments
|
||||
CFLAGS="$CFLAGS -Wno-unused"
|
||||
|
||||
+1
-9
@@ -5,14 +5,6 @@
|
||||
|
||||
# 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
|
||||
|
||||
CFLAGS="$CFLAGS -O"
|
||||
@@ -83,7 +75,7 @@ CFLAGS="$CFLAGS -wd1418"
|
||||
# external declaration in primary source file
|
||||
CFLAGS="$CFLAGS -wd1419"
|
||||
|
||||
case "$NGX_ICC_VER" in
|
||||
case "$NGX_CC_VER" in
|
||||
9.*)
|
||||
# "cc" clobber ignored, warnings for Linux's htonl()/htons()
|
||||
CFLAGS="$CFLAGS -wd1469"
|
||||
|
||||
+6
-10
@@ -11,20 +11,16 @@
|
||||
# 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 \
|
||||
| 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/'`
|
||||
ngx_msvc_ver=`echo $NGX_CC_VER | sed -e 's/^\([0-9]*\).*/\1/'`
|
||||
|
||||
|
||||
# detect x64 builds
|
||||
|
||||
case "$NGX_MSVC_VER" in
|
||||
case "$NGX_CC_VER" in
|
||||
|
||||
*ARM64)
|
||||
NGX_MACHINE=arm64
|
||||
;;
|
||||
|
||||
*x64)
|
||||
NGX_MACHINE=amd64
|
||||
|
||||
@@ -28,6 +28,13 @@ if [ "$CC" = cl ]; then
|
||||
NGX_CC_NAME=msvc
|
||||
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
|
||||
NGX_CC_NAME=owc
|
||||
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
|
||||
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
|
||||
NGX_CC_NAME=gcc
|
||||
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
|
||||
NGX_CC_NAME=clang
|
||||
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
|
||||
NGX_CC_NAME=clang
|
||||
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
|
||||
NGX_CC_NAME=sunc
|
||||
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
|
||||
NGX_CC_NAME=ccc
|
||||
echo " + using Compaq C compiler"
|
||||
|
||||
@@ -13,13 +13,6 @@
|
||||
# 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
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ if [ $OPENSSL != NONE ]; then
|
||||
|
||||
if [ $USE_OPENSSL_QUIC = YES ]; then
|
||||
have=NGX_QUIC . auto/have
|
||||
have=NGX_QUIC_OPENSSL_COMPAT . auto/have
|
||||
fi
|
||||
|
||||
case "$CC" in
|
||||
@@ -148,14 +147,18 @@ else
|
||||
|
||||
if [ $USE_OPENSSL_QUIC = YES ]; then
|
||||
|
||||
ngx_feature="OpenSSL QUIC support"
|
||||
ngx_feature="OpenSSL QUIC API"
|
||||
ngx_feature_name="NGX_QUIC"
|
||||
ngx_feature_test="SSL_set_quic_method(NULL, NULL)"
|
||||
ngx_feature_test="SSL_set_quic_tls_cbs(NULL, NULL, NULL)"
|
||||
. auto/feature
|
||||
|
||||
if [ $ngx_found = no ]; then
|
||||
have=NGX_QUIC_OPENSSL_COMPAT . auto/have
|
||||
ngx_feature="BoringSSL-like QUIC API"
|
||||
ngx_feature_test="SSL_set_quic_method(NULL, NULL)"
|
||||
. auto/feature
|
||||
fi
|
||||
|
||||
if [ $ngx_found = no ]; then
|
||||
ngx_feature="OpenSSL QUIC compatibility"
|
||||
ngx_feature_test="SSL_CTX_add_custom_ext(NULL, 0, 0,
|
||||
NULL, NULL, NULL, NULL, NULL)"
|
||||
|
||||
@@ -13,6 +13,10 @@ case "$CC" in
|
||||
OPENSSL_TARGET=VC-WIN64A
|
||||
;;
|
||||
|
||||
arm64)
|
||||
OPENSSL_TARGET=VC-WIN64-ARM
|
||||
;;
|
||||
|
||||
*)
|
||||
OPENSSL_TARGET=VC-WIN32
|
||||
;;
|
||||
|
||||
+4
-1
@@ -36,7 +36,10 @@ if [ $PCRE_LIBRARY = PCRE2 ]; then
|
||||
pcre2_valid_utf.c \
|
||||
pcre2_xclass.c"
|
||||
|
||||
ngx_pcre_test="pcre2_convert.c \
|
||||
ngx_pcre_test="pcre2_chkdint.c \
|
||||
pcre2_compile_cgroup.c \
|
||||
pcre2_compile_class.c \
|
||||
pcre2_convert.c \
|
||||
pcre2_extuni.c \
|
||||
pcre2_find_bracket.c \
|
||||
pcre2_script_run.c \
|
||||
|
||||
+12
-1
@@ -733,7 +733,7 @@ if [ $HTTP = YES ]; then
|
||||
|
||||
ngx_module_name=ngx_http_proxy_module
|
||||
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_libs=
|
||||
ngx_module_link=$HTTP_PROXY
|
||||
@@ -785,6 +785,17 @@ if [ $HTTP = YES ]; then
|
||||
. auto/module
|
||||
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
|
||||
ngx_module_name=ngx_http_perl_module
|
||||
ngx_module_incs=src/http/modules/perl
|
||||
|
||||
@@ -118,3 +118,19 @@ ngx_feature_libs=
|
||||
ngx_feature_test="int32_t lock = 0;
|
||||
if (!OSAtomicCompareAndSwap32Barrier(0, 1, &lock)) return 1"
|
||||
. auto/feature
|
||||
|
||||
|
||||
ngx_feature="TCP_KEEPALIVE"
|
||||
ngx_feature_name="NGX_HAVE_KEEPALIVE_TUNABLE"
|
||||
ngx_feature_run=no
|
||||
ngx_feature_incs="#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>"
|
||||
ngx_feature_path=
|
||||
ngx_feature_libs=
|
||||
ngx_feature_test="setsockopt(0, IPPROTO_TCP, TCP_KEEPALIVE, NULL, 0);
|
||||
setsockopt(0, IPPROTO_TCP, TCP_KEEPINTVL, NULL, 0);
|
||||
setsockopt(0, IPPROTO_TCP, TCP_KEEPCNT, NULL, 0)"
|
||||
. auto/feature
|
||||
|
||||
NGX_KEEPALIVE_CHECKED=YES
|
||||
@@ -129,28 +129,8 @@ if test -z "$NGX_KQUEUE_CHECKED"; then
|
||||
fi
|
||||
|
||||
|
||||
if [ "$NGX_SYSTEM" = "NetBSD" ]; then
|
||||
|
||||
# NetBSD 2.0 incompatibly defines kevent.udata as "intptr_t"
|
||||
|
||||
cat << END >> $NGX_AUTO_CONFIG_H
|
||||
|
||||
#define NGX_KQUEUE_UDATA_T
|
||||
|
||||
END
|
||||
|
||||
else
|
||||
cat << END >> $NGX_AUTO_CONFIG_H
|
||||
|
||||
#define NGX_KQUEUE_UDATA_T (void *)
|
||||
|
||||
END
|
||||
|
||||
fi
|
||||
|
||||
|
||||
ngx_feature="crypt()"
|
||||
ngx_feature_name=
|
||||
ngx_feature_name="NGX_HAVE_CRYPT"
|
||||
ngx_feature_run=no
|
||||
ngx_feature_incs=
|
||||
ngx_feature_path=
|
||||
@@ -162,7 +142,7 @@ ngx_feature_test="crypt(\"test\", \"salt\");"
|
||||
if [ $ngx_found = no ]; then
|
||||
|
||||
ngx_feature="crypt() in libcrypt"
|
||||
ngx_feature_name=
|
||||
ngx_feature_name="NGX_HAVE_CRYPT"
|
||||
ngx_feature_run=no
|
||||
ngx_feature_incs=
|
||||
ngx_feature_path=
|
||||
@@ -508,18 +488,20 @@ ngx_feature_test="setsockopt(0, IPPROTO_TCP, TCP_DEFER_ACCEPT, NULL, 0)"
|
||||
. auto/feature
|
||||
|
||||
|
||||
ngx_feature="TCP_KEEPIDLE"
|
||||
ngx_feature_name="NGX_HAVE_KEEPALIVE_TUNABLE"
|
||||
ngx_feature_run=no
|
||||
ngx_feature_incs="#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>"
|
||||
ngx_feature_path=
|
||||
ngx_feature_libs=
|
||||
ngx_feature_test="setsockopt(0, IPPROTO_TCP, TCP_KEEPIDLE, NULL, 0);
|
||||
setsockopt(0, IPPROTO_TCP, TCP_KEEPINTVL, NULL, 0);
|
||||
setsockopt(0, IPPROTO_TCP, TCP_KEEPCNT, NULL, 0)"
|
||||
. auto/feature
|
||||
if test -z "$NGX_KEEPALIVE_CHECKED"; then
|
||||
ngx_feature="TCP_KEEPIDLE"
|
||||
ngx_feature_name="NGX_HAVE_KEEPALIVE_TUNABLE"
|
||||
ngx_feature_run=no
|
||||
ngx_feature_incs="#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>"
|
||||
ngx_feature_path=
|
||||
ngx_feature_libs=
|
||||
ngx_feature_test="setsockopt(0, IPPROTO_TCP, TCP_KEEPIDLE, NULL, 0);
|
||||
setsockopt(0, IPPROTO_TCP, TCP_KEEPINTVL, NULL, 0);
|
||||
setsockopt(0, IPPROTO_TCP, TCP_KEEPCNT, NULL, 0)"
|
||||
. auto/feature
|
||||
fi
|
||||
|
||||
|
||||
ngx_feature="TCP_FASTOPEN"
|
||||
|
||||
@@ -36,8 +36,6 @@ http {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
#charset koi8-r;
|
||||
|
||||
#access_log logs/host.access.log main;
|
||||
|
||||
location / {
|
||||
|
||||
+600
-2
@@ -5,6 +5,604 @@
|
||||
<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">
|
||||
|
||||
<change type="security">
|
||||
<para lang="ru">
|
||||
обработка специально созданного логина/пароля при использовании
|
||||
метода аутентификации "none" в модуле ngx_mail_smtp_module
|
||||
могла приводить к отправке серверу аутентификации
|
||||
части содержимого памяти рабочего процесса (CVE-2025-53859).
|
||||
</para>
|
||||
<para lang="en">
|
||||
processing of a specially crafted login/password when using
|
||||
the "none" authentication method in the ngx_mail_smtp_module
|
||||
might cause worker process memory disclosure
|
||||
to the authentication server (CVE-2025-53859).
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="change">
|
||||
<para lang="ru">
|
||||
теперь сжатие сертификатов в протоколе TLSv1.3 по умолчанию запрещено.
|
||||
</para>
|
||||
<para lang="en">
|
||||
now TLSv1.3 certificate compression is disabled by default.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
директива ssl_certificate_compression.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the "ssl_certificate_compression" directive.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
поддержка 0-RTT в QUIC при использовании OpenSSL 3.5.1 и новее.
|
||||
</para>
|
||||
<para lang="en">
|
||||
support for 0-RTT in QUIC when using OpenSSL 3.5.1 or newer.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
при использовании HTTP/2 и директивы early_hints
|
||||
ответ 103 мог буферизироваться.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the 103 response might be buffered
|
||||
when using HTTP/2 and the "early_hints" directive.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
в обработке заголовков запроса "Host" и ":authority"
|
||||
с одинаковыми значениями при использовании HTTP/2;
|
||||
ошибка появилась в 1.17.9.
|
||||
</para>
|
||||
<para lang="en">
|
||||
in handling "Host" and ":authority" header lines
|
||||
with equal values when using HTTP/2;
|
||||
the bug had appeared in 1.17.9.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
в обработке заголовка запроса "Host" с портом
|
||||
при использовании HTTP/3.
|
||||
</para>
|
||||
<para lang="en">
|
||||
in handling "Host" header lines with a port
|
||||
when using HTTP/3.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
nginx не собирался под NetBSD 10.0.
|
||||
</para>
|
||||
<para lang="en">
|
||||
nginx could not be built on NetBSD 10.0.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
в работе параметра none директивы smtp_auth.
|
||||
</para>
|
||||
<para lang="en">
|
||||
in the "none" parameter of the "smtp_auth" directive.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
</changes>
|
||||
|
||||
|
||||
<changes ver="1.29.0" date="2025-06-24">
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
поддержка ответа с кодом 103 от proxy- и gRPC-бэкендов;
|
||||
директива early_hints.
|
||||
</para>
|
||||
<para lang="en">
|
||||
support for response code 103 from proxy and gRPC backends;
|
||||
the "early_hints" directive.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
возможность загрузки секретных ключей с аппаратных устройств
|
||||
с помощью OpenSSL provider.
|
||||
</para>
|
||||
<para lang="en">
|
||||
loading of secret keys from hardware tokens
|
||||
with OpenSSL provider.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
поддержка параметра so_keepalive директивы listen на macOS.
|
||||
</para>
|
||||
<para lang="en">
|
||||
support for the "so_keepalive" parameter of the "listen" directive on macOS.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="change">
|
||||
<para lang="ru">
|
||||
уровень логгирования ошибок SSL в QUIC handshake
|
||||
изменён с уровня error на crit для критических ошибок
|
||||
и на info для всех остальных;
|
||||
уровень логгирования неподдерживаемых транспортных параметров QUIC
|
||||
понижен с уровня info до debug.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the logging level of SSL errors in a QUIC handshake
|
||||
has been changed from "error" to "crit" for critical errors,
|
||||
and to "info" for the rest;
|
||||
the logging level of unsupported QUIC transport parameters
|
||||
has been lowered from "info" to "debug".
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="change">
|
||||
<para lang="ru">
|
||||
бинарная версия nginx/Windows теперь использует для сборки Windows SDK 10.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the native nginx/Windows binary release is now built using Windows SDK 10.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
nginx не собирался gcc 15,
|
||||
если использовались модули ngx_http_v2_module и ngx_http_v3_module.
|
||||
</para>
|
||||
<para lang="en">
|
||||
nginx could not be built by gcc 15
|
||||
if ngx_http_v2_module or ngx_http_v3_module modules were used.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
nginx мог не собираться gcc 14 и новее с оптимизацией -O3 -flto,
|
||||
если использовался модуль ngx_http_v3_module.
|
||||
</para>
|
||||
<para lang="en">
|
||||
nginx might not be built by gcc 14 or newer with -O3 -flto optimization
|
||||
if ngx_http_v3_module was used.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change>
|
||||
<para lang="ru">
|
||||
Исправления и улучшения в HTTP/3.
|
||||
</para>
|
||||
<para lang="en">
|
||||
Bugfixes and improvements in HTTP/3.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
</changes>
|
||||
|
||||
|
||||
<changes ver="1.27.5" date="2025-04-16">
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
контроль перегрузки CUBIC в соединениях QUIC.
|
||||
</para>
|
||||
<para lang="en">
|
||||
CUBIC congestion control in QUIC connections.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="change">
|
||||
<para lang="ru">
|
||||
ограничение на максимальный размер кешируемых в разделяемой памяти
|
||||
SSL-сессий поднято до 8192.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the maximum size limit for SSL sessions cached in shared memory
|
||||
has been raised to 8192.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
в директивах grpc_ssl_password_file, proxy_ssl_password_file и
|
||||
uwsgi_ssl_password_file
|
||||
при загрузке SSL-сертификатов и зашифрованных ключей из переменных;
|
||||
ошибка появилась в 1.23.1.
|
||||
</para>
|
||||
<para lang="en">
|
||||
in the "grpc_ssl_password_file", "proxy_ssl_password_file", and
|
||||
"uwsgi_ssl_password_file" directives
|
||||
when loading SSL certificates and encrypted keys from variables;
|
||||
the bug had appeared in 1.23.1.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
в переменных $ssl_curve и $ssl_curves
|
||||
при использовании подключаемых кривых в OpenSSL.
|
||||
</para>
|
||||
<para lang="en">
|
||||
in the $ssl_curve and $ssl_curves variables
|
||||
when using pluggable curves in OpenSSL.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
nginx не собирался с musl libc.<br/>
|
||||
Спасибо Piotr Sikora.
|
||||
</para>
|
||||
<para lang="en">
|
||||
nginx could not be built with musl libc.<br/>
|
||||
Thanks to Piotr Sikora.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change>
|
||||
<para lang="ru">
|
||||
Улучшения производительности и исправления в HTTP/3.
|
||||
</para>
|
||||
<para lang="en">
|
||||
Performance improvements and bugfixes in HTTP/3.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
</changes>
|
||||
|
||||
|
||||
<changes ver="1.27.4" date="2025-02-05">
|
||||
|
||||
<change type="security">
|
||||
<para lang="ru">
|
||||
недостаточная проверка в обработке виртуальных серверов
|
||||
при использовании SNI в TLSv1.3 позволяла повторно использовать
|
||||
SSL-сессию в контексте другого виртуального сервера,
|
||||
чтобы обойти проверку клиентских SSL-сертификатов (CVE-2025-23419).
|
||||
</para>
|
||||
<para lang="en">
|
||||
insufficient check in virtual servers handling with TLSv1.3 SNI
|
||||
allowed to reuse SSL sessions in a different virtual server,
|
||||
to bypass client SSL certificates verification (CVE-2025-23419).
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
директивы ssl_object_cache_inheritable, ssl_certificate_cache,
|
||||
proxy_ssl_certificate_cache, grpc_ssl_certificate_cache
|
||||
и uwsgi_ssl_certificate_cache.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the "ssl_object_cache_inheritable", "ssl_certificate_cache",
|
||||
"proxy_ssl_certificate_cache", "grpc_ssl_certificate_cache",
|
||||
and "uwsgi_ssl_certificate_cache" directives.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
директива keepalive_min_timeout.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the "keepalive_min_timeout" directive.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="workaround">
|
||||
<para lang="ru">
|
||||
при использовании zlib-ng
|
||||
в логах появлялись сообщения "gzip filter failed to use preallocated memory".
|
||||
</para>
|
||||
<para lang="en">
|
||||
"gzip filter failed to use preallocated memory" alerts appeared in logs
|
||||
when using zlib-ng.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
nginx не мог собрать библиотеку libatomic из исходных текстов,
|
||||
если использовался параметр --with-libatomic=DIR.
|
||||
</para>
|
||||
<para lang="en">
|
||||
nginx could not build libatomic library using the library sources
|
||||
if the --with-libatomic=DIR option was used.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
могла происходить ошибка установления соединения
|
||||
при использовании 0-RTT в QUIC;
|
||||
ошибка появилась в 1.27.1.
|
||||
</para>
|
||||
<para lang="en">
|
||||
QUIC connection might not be established when using 0-RTT;
|
||||
the bug had appeared in 1.27.1.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
теперь nginx игнорирует пакеты согласования версий QUIC от клиентов.
|
||||
</para>
|
||||
<para lang="en">
|
||||
nginx now ignores QUIC version negotiation packets from clients.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
nginx не собирался на Solaris 10 и более ранних
|
||||
с модулем ngx_http_v3_module.
|
||||
</para>
|
||||
<para lang="en">
|
||||
nginx could not be built on Solaris 10 and earlier
|
||||
with the ngx_http_v3_module.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change>
|
||||
<para lang="ru">
|
||||
Исправления в HTTP/3.
|
||||
</para>
|
||||
<para lang="en">
|
||||
Bugfixes in HTTP/3.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
</changes>
|
||||
|
||||
|
||||
<changes ver="1.27.3" date="2024-11-26">
|
||||
|
||||
<change type="feature">
|
||||
@@ -8368,7 +8966,7 @@ Thanks to Piotr Sikora.
|
||||
Спасибо Piotr Sikora.
|
||||
</para>
|
||||
<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.
|
||||
</para>
|
||||
</change>
|
||||
@@ -25651,7 +26249,7 @@ the ECONNABORTED error log level was changed to "error" from "crit".
|
||||
модуль ngx_http_perl_module не собирался без модуля ngx_http_ssi_filter_module.
|
||||
</para>
|
||||
<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.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
+1
-1
Submodule lib/boringssl updated: 571c76e919...e056f59c7d
Submodule lib/headers-more-nginx-module updated: f8f80997f1...e76a51306b
+1
-1
Submodule lib/naxsi updated: a0c7694b09...f52861ebe5
Submodule lib/nginx-http-flv-module updated: 2cae45cb0c...b89c1acc64
+1
-1
Submodule lib/nginx-module-vts updated: 88a3870e5a...37d319fd39
+1
-1
Submodule lib/ngx_cache_purge updated: a84b0f3f08...a0e7ef4085
+1
-1
Submodule lib/ngx_devel_kit updated: 91e30eb050...bd44d16302
+1
-1
Submodule lib/ngx_dynamic_etag updated: 39a1801a59...58dd26102e
+1
-1
Submodule lib/pcre updated: 82ebebfd0d...91606be5e8
+1
-1
Submodule lib/zlib-ng updated: 94aacd8bd6...bbd2c9025c
+1
-1
Submodule lib/zstd-nginx-module updated: f4ba115e0b...057a7d339a
+5
-4
@@ -6,9 +6,9 @@ TEMP = tmp
|
||||
|
||||
CC = cl
|
||||
OBJS = objs.msvc8
|
||||
OPENSSL = openssl-3.0.15
|
||||
OPENSSL = openssl-3.5.4
|
||||
ZLIB = zlib-1.3.1
|
||||
PCRE = pcre2-10.39
|
||||
PCRE = pcre2-10.46
|
||||
|
||||
|
||||
release: export
|
||||
@@ -76,7 +76,8 @@ win32:
|
||||
--with-stream_realip_module \
|
||||
--with-stream_ssl_preread_module \
|
||||
--with-openssl=$(OBJS)/lib/$(OPENSSL) \
|
||||
--with-openssl-opt="no-asm no-tests -D_WIN32_WINNT=0x0501" \
|
||||
--with-openssl-opt="no-asm no-tests no-makedepend \
|
||||
-D_WIN32_WINNT=0x0501" \
|
||||
--with-http_ssl_module \
|
||||
--with-mail_ssl_module \
|
||||
--with-stream_ssl_module
|
||||
@@ -109,7 +110,7 @@ zip: export
|
||||
cp -p $(OBJS)/lib/$(OPENSSL)/LICENSE.txt \
|
||||
$(TEMP)/$(NGINX)/docs/OpenSSL.LICENSE
|
||||
|
||||
cp -p $(OBJS)/lib/$(PCRE)/LICENCE \
|
||||
cp -p $(OBJS)/lib/$(PCRE)/LICENCE.md \
|
||||
$(TEMP)/$(NGINX)/docs/PCRE.LICENCE
|
||||
|
||||
sed -ne '/^ (C) 1995-20/,/^ jloup@gzip\.org/p' \
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ make -f misc/GNUmakefile release
|
||||
|
||||
the required tools:
|
||||
*) 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.
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -9,8 +9,8 @@
|
||||
#define _NGINX_H_INCLUDED_
|
||||
|
||||
|
||||
#define nginx_version 1027004
|
||||
#define NGINX_VERSION "1.27.4"
|
||||
#define nginx_version 1029005
|
||||
#define NGINX_VERSION "1.29.5"
|
||||
#define NGINX_VER "nginx/" NGINX_VERSION " by Hakase"
|
||||
|
||||
#ifndef NGINX_SERVER
|
||||
|
||||
@@ -94,7 +94,7 @@ typedef intptr_t ngx_flag_t;
|
||||
|
||||
|
||||
#ifndef NGX_ALIGNMENT
|
||||
#define NGX_ALIGNMENT sizeof(unsigned long) /* platform word */
|
||||
#define NGX_ALIGNMENT sizeof(uintptr_t) /* platform word */
|
||||
#endif
|
||||
|
||||
#define ngx_align(d, a) (((d) + (a - 1)) & ~(a - 1))
|
||||
|
||||
@@ -765,6 +765,8 @@ ngx_configure_listening_sockets(ngx_cycle_t *cycle)
|
||||
|
||||
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
|
||||
|
||||
#if !(NGX_DARWIN)
|
||||
|
||||
if (ls[i].keepidle) {
|
||||
value = ls[i].keepidle;
|
||||
|
||||
@@ -782,6 +784,8 @@ ngx_configure_listening_sockets(ngx_cycle_t *cycle)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (ls[i].keepintvl) {
|
||||
value = ls[i].keepintvl;
|
||||
|
||||
|
||||
+29
-3
@@ -1494,8 +1494,9 @@ ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len)
|
||||
uintptr_t
|
||||
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;
|
||||
ngx_uint_t n;
|
||||
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 */
|
||||
|
||||
/* " ", "+", "=", 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[] =
|
||||
{ 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];
|
||||
prefix = map_char[type];
|
||||
|
||||
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) {
|
||||
if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
|
||||
*dst++ = '%';
|
||||
*dst++ = prefix;
|
||||
*dst++ = hex[*src >> 4];
|
||||
*dst++ = hex[*src & 0xf];
|
||||
src++;
|
||||
|
||||
@@ -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_MEMCACHED 5
|
||||
#define NGX_ESCAPE_MAIL_AUTH 6
|
||||
#define NGX_ESCAPE_MAIL_XTEXT 7
|
||||
|
||||
#define NGX_UNESCAPE_URI 1
|
||||
#define NGX_UNESCAPE_REDIRECT 2
|
||||
|
||||
@@ -207,7 +207,7 @@ ngx_thread_pool_exit_handler(void *data, ngx_log_t *log)
|
||||
|
||||
*lock = 0;
|
||||
|
||||
pthread_exit(0);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,6 +10,15 @@
|
||||
#include <ngx_event.h>
|
||||
|
||||
|
||||
/* NetBSD up to 10.0 incompatibly defines kevent.udata as "intptr_t" */
|
||||
|
||||
#if (__NetBSD__ && __NetBSD_Version__ < 1000000000)
|
||||
#define NGX_KQUEUE_UDATA_T
|
||||
#else
|
||||
#define NGX_KQUEUE_UDATA_T (void *)
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t changes;
|
||||
ngx_uint_t events;
|
||||
@@ -191,7 +200,7 @@ ngx_kqueue_init(ngx_cycle_t *cycle, ngx_msec_t timer)
|
||||
kev.flags = EV_ADD|EV_ENABLE;
|
||||
kev.fflags = 0;
|
||||
kev.data = timer;
|
||||
kev.udata = 0;
|
||||
kev.udata = NGX_KQUEUE_UDATA_T (uintptr_t) 0;
|
||||
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 0;
|
||||
@@ -237,7 +246,7 @@ ngx_kqueue_notify_init(ngx_log_t *log)
|
||||
notify_kev.data = 0;
|
||||
notify_kev.flags = EV_ADD|EV_CLEAR;
|
||||
notify_kev.fflags = 0;
|
||||
notify_kev.udata = 0;
|
||||
notify_kev.udata = NGX_KQUEUE_UDATA_T (uintptr_t) 0;
|
||||
|
||||
if (kevent(ngx_kqueue, ¬ify_kev, 1, NULL, 0, NULL) == -1) {
|
||||
ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
|
||||
|
||||
@@ -203,6 +203,23 @@ ngx_event_accept(ngx_event_t *ev)
|
||||
}
|
||||
}
|
||||
|
||||
#if (NGX_HAVE_KEEPALIVE_TUNABLE && NGX_DARWIN)
|
||||
|
||||
/* Darwin doesn't inherit TCP_KEEPALIVE from a listening socket */
|
||||
|
||||
if (ls->keepidle) {
|
||||
if (setsockopt(s, IPPROTO_TCP, TCP_KEEPALIVE,
|
||||
(const void *) &ls->keepidle, sizeof(int))
|
||||
== -1)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
|
||||
"setsockopt(TCP_KEEPALIVE, %d) failed, ignored",
|
||||
ls->keepidle);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
*log = ls->log;
|
||||
|
||||
c->recv = ngx_recv;
|
||||
|
||||
+664
-26
@@ -9,6 +9,10 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_event.h>
|
||||
|
||||
#if (NGX_ZLIB && defined TLSEXT_cert_compression_zlib)
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
|
||||
#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);
|
||||
#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 void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where,
|
||||
int ret);
|
||||
@@ -45,8 +56,6 @@ static ssize_t ngx_ssl_sendfile(ngx_connection_t *c, ngx_buf_t *file,
|
||||
size_t size);
|
||||
static void ngx_ssl_read_handler(ngx_event_t *rev);
|
||||
static void ngx_ssl_shutdown_handler(ngx_event_t *ev);
|
||||
static void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr,
|
||||
ngx_err_t err, char *text);
|
||||
static void ngx_ssl_clear_error(ngx_log_t *log);
|
||||
|
||||
static ngx_int_t ngx_ssl_session_id_context(ngx_ssl_t *ssl,
|
||||
@@ -130,6 +139,11 @@ int ngx_ssl_ticket_keys_index;
|
||||
int ngx_ssl_ocsp_index;
|
||||
int ngx_ssl_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];
|
||||
|
||||
|
||||
ngx_int_t
|
||||
@@ -269,6 +283,21 @@ ngx_ssl_init(ngx_log_t *log)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -386,6 +415,11 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data)
|
||||
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_COMPRESSION);
|
||||
#endif
|
||||
|
||||
#ifdef SSL_OP_NO_TX_CERTIFICATE_COMPRESSION
|
||||
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TX_CERTIFICATE_COMPRESSION);
|
||||
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_RX_CERTIFICATE_COMPRESSION);
|
||||
#endif
|
||||
|
||||
#ifdef SSL_OP_NO_ANTI_REPLAY
|
||||
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_ANTI_REPLAY);
|
||||
#endif
|
||||
@@ -443,10 +477,18 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
|
||||
{
|
||||
char *err;
|
||||
X509 *x509, **elm;
|
||||
u_long n;
|
||||
EVP_PKEY *pkey;
|
||||
ngx_uint_t mask;
|
||||
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 (err != NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
@@ -486,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) {
|
||||
X509_free(x509);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NGX_ERROR;
|
||||
elm = ngx_array_push(&ssl->certs);
|
||||
if (elm == NULL) {
|
||||
X509_free(x509);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
} else {
|
||||
X509_free(*elm);
|
||||
}
|
||||
|
||||
*elm = x509;
|
||||
@@ -513,11 +560,21 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
|
||||
}
|
||||
|
||||
#else
|
||||
{
|
||||
int n;
|
||||
|
||||
/* 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);
|
||||
|
||||
while (n--) {
|
||||
@@ -533,10 +590,11 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
|
||||
}
|
||||
|
||||
sk_X509_free(chain);
|
||||
}
|
||||
|
||||
#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 (err != NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
@@ -548,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) {
|
||||
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,
|
||||
"SSL_CTX_use_PrivateKey(\"%s\") failed", key->data);
|
||||
EVP_PKEY_free(pkey);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
@@ -658,6 +730,197 @@ retry:
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_certificate_compression(ngx_conf_t *cf, ngx_ssl_t *ssl,
|
||||
ngx_uint_t enable)
|
||||
{
|
||||
if (!enable) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
#ifdef SSL_OP_NO_TX_CERTIFICATE_COMPRESSION
|
||||
|
||||
if (SSL_CTX_compress_certs(ssl->ctx, 0) == 0) {
|
||||
ngx_ssl_error(NGX_LOG_WARN, ssl->log, 0,
|
||||
"SSL_CTX_compress_certs() failed, ignored");
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
|
||||
"\"ssl_certificate_compression\" is not supported "
|
||||
"on this platform, ignored");
|
||||
|
||||
#endif
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
#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_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers,
|
||||
ngx_uint_t prefer_server_ciphers)
|
||||
@@ -1312,6 +1575,8 @@ ngx_ssl_passwords_cleanup(void *data)
|
||||
ngx_int_t
|
||||
ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
|
||||
{
|
||||
#ifndef OPENSSL_NO_DH
|
||||
|
||||
BIO *bio;
|
||||
|
||||
if (file->len == 0) {
|
||||
@@ -1371,7 +1636,7 @@ ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
|
||||
if (SSL_CTX_set0_tmp_dh_pkey(ssl->ctx, dh) != 1) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"SSL_CTX_set0_tmp_dh_pkey(\"%s\") failed", file->data);
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x3000001fL)
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x30000010L)
|
||||
EVP_PKEY_free(dh);
|
||||
#endif
|
||||
BIO_free(bio);
|
||||
@@ -1382,10 +1647,111 @@ ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
|
||||
|
||||
BIO_free(bio);
|
||||
|
||||
#endif
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
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_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name)
|
||||
{
|
||||
@@ -1605,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_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags)
|
||||
{
|
||||
@@ -3333,7 +3824,7 @@ ngx_ssl_shutdown_handler(ngx_event_t *ev)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
void
|
||||
ngx_ssl_connection_error(ngx_connection_t *c, int sslerr, ngx_err_t err,
|
||||
char *text)
|
||||
{
|
||||
@@ -3928,7 +4419,6 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
|
||||
ngx_slab_pool_t *shpool;
|
||||
ngx_ssl_sess_id_t *sess_id;
|
||||
ngx_ssl_session_cache_t *cache;
|
||||
u_char buf[NGX_SSL_MAX_SESSION_SIZE];
|
||||
|
||||
#ifdef TLS1_3_VERSION
|
||||
|
||||
@@ -3955,7 +4445,7 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = buf;
|
||||
p = ngx_ssl_session_buffer;
|
||||
i2d_SSL_SESSION(sess, &p);
|
||||
|
||||
session_id = (u_char *) SSL_SESSION_get_id(sess, &session_id_length);
|
||||
@@ -4019,7 +4509,7 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
|
||||
|
||||
#endif
|
||||
|
||||
ngx_memcpy(sess_id->session, buf, len);
|
||||
ngx_memcpy(sess_id->session, ngx_ssl_session_buffer, len);
|
||||
ngx_memcpy(sess_id->id, session_id, session_id_length);
|
||||
|
||||
hash = ngx_crc32_short(session_id, session_id_length);
|
||||
@@ -4073,12 +4563,11 @@ ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn,
|
||||
const u_char *p;
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
ngx_slab_pool_t *shpool;
|
||||
ngx_connection_t *c;
|
||||
ngx_rbtree_node_t *node, *sentinel;
|
||||
ngx_ssl_session_t *sess;
|
||||
ngx_ssl_sess_id_t *sess_id;
|
||||
ngx_ssl_session_cache_t *cache;
|
||||
u_char buf[NGX_SSL_MAX_SESSION_SIZE];
|
||||
ngx_connection_t *c;
|
||||
|
||||
hash = ngx_crc32_short((u_char *) (uintptr_t) id, (size_t) len);
|
||||
*copy = 0;
|
||||
@@ -4126,11 +4615,11 @@ ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn,
|
||||
if (sess_id->expire > ngx_time()) {
|
||||
slen = sess_id->len;
|
||||
|
||||
ngx_memcpy(buf, sess_id->session, slen);
|
||||
ngx_memcpy(ngx_ssl_session_buffer, sess_id->session, slen);
|
||||
|
||||
ngx_shmtx_unlock(&shpool->mutex);
|
||||
|
||||
p = buf;
|
||||
p = ngx_ssl_session_buffer;
|
||||
sess = d2i_SSL_SESSION(NULL, &p, slen);
|
||||
|
||||
return sess;
|
||||
@@ -4793,10 +5282,19 @@ ngx_ssl_cleanup_ctx(void *data)
|
||||
ngx_ssl_t *ssl = data;
|
||||
|
||||
X509 *cert;
|
||||
u_char *p;
|
||||
ngx_uint_t i;
|
||||
|
||||
for (i = 0; i < ssl->certs.nelts; 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);
|
||||
}
|
||||
|
||||
@@ -5078,7 +5576,8 @@ ngx_ssl_get_curve(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
{
|
||||
#ifdef SSL_get_negotiated_group
|
||||
|
||||
int nid;
|
||||
int nid;
|
||||
const char *name;
|
||||
|
||||
nid = SSL_get_negotiated_group(c->ssl->connection);
|
||||
|
||||
@@ -5090,14 +5589,20 @@ ngx_ssl_get_curve(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
s->len = sizeof("0x0000") - 1;
|
||||
name = SSL_group_to_name(c->ssl->connection, nid);
|
||||
|
||||
s->len = name ? ngx_strlen(name) : sizeof("0x0000") - 1;
|
||||
s->data = ngx_pnalloc(pool, s->len);
|
||||
if (s->data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_sprintf(s->data, "0x%04xd", nid & 0xffff);
|
||||
if (name) {
|
||||
ngx_memcpy(s->data, name, s->len);
|
||||
|
||||
} else {
|
||||
ngx_sprintf(s->data, "0x%04xd", nid & 0xffff);
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -5117,6 +5622,7 @@ ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
int *curves, n, i, nid;
|
||||
u_char *p;
|
||||
size_t len;
|
||||
const char *name;
|
||||
|
||||
n = SSL_get1_curves(c->ssl->connection, NULL);
|
||||
|
||||
@@ -5137,7 +5643,9 @@ ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
nid = curves[i];
|
||||
|
||||
if (nid & TLSEXT_nid_unknown) {
|
||||
len += sizeof("0x0000") - 1;
|
||||
name = SSL_group_to_name(c->ssl->connection, nid);
|
||||
|
||||
len += name ? ngx_strlen(name) : sizeof("0x0000") - 1;
|
||||
|
||||
} else {
|
||||
len += ngx_strlen(OBJ_nid2sn(nid));
|
||||
@@ -5157,7 +5665,10 @@ ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
nid = curves[i];
|
||||
|
||||
if (nid & TLSEXT_nid_unknown) {
|
||||
p = ngx_sprintf(p, "0x%04xd", nid & 0xffff);
|
||||
name = SSL_group_to_name(c->ssl->connection, nid);
|
||||
|
||||
p = name ? ngx_cpymem(p, name, ngx_strlen(name))
|
||||
: ngx_sprintf(p, "0x%04xd", nid & 0xffff);
|
||||
|
||||
} else {
|
||||
p = ngx_sprintf(p, "%s", OBJ_nid2sn(nid));
|
||||
@@ -5180,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_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
{
|
||||
@@ -5309,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_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
{
|
||||
@@ -5925,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 *
|
||||
ngx_openssl_create_conf(ngx_cycle_t *cycle)
|
||||
{
|
||||
|
||||
@@ -19,19 +19,13 @@
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/conf.h>
|
||||
#include <openssl/crypto.h>
|
||||
#ifndef OPENSSL_NO_DH
|
||||
#include <openssl/dh.h>
|
||||
#endif
|
||||
#ifndef OPENSSL_NO_ENGINE
|
||||
#include <openssl/engine.h>
|
||||
#endif
|
||||
#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>
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
#include <openssl/ocsp.h>
|
||||
@@ -83,6 +77,22 @@
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef OPENSSL_NO_DEPRECATED_3_4
|
||||
#define SSL_SESSION_get_time(s) SSL_SESSION_get_time_ex(s)
|
||||
#define SSL_SESSION_set_time(s, t) SSL_SESSION_set_time_ex(s, t)
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef OPENSSL_NO_DEPRECATED_3_0
|
||||
#define EVP_CIPHER_CTX_cipher(c) EVP_CIPHER_CTX_get0_cipher(c)
|
||||
#endif
|
||||
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
|
||||
#define SSL_group_to_name(s, nid) NULL
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct ngx_ssl_ocsp_s ngx_ssl_ocsp_t;
|
||||
|
||||
|
||||
@@ -143,6 +153,7 @@ struct ngx_ssl_connection_s {
|
||||
unsigned in_ocsp:1;
|
||||
unsigned early_preread:1;
|
||||
unsigned write_blocked:1;
|
||||
unsigned sni_accepted:1;
|
||||
|
||||
ngx_ssl_dyn_rec_t dyn_rec;
|
||||
ngx_msec_t dyn_rec_last_write;
|
||||
@@ -193,6 +204,13 @@ typedef struct {
|
||||
} 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_SSLv3 0x0004
|
||||
#define NGX_SSL_TLSv1 0x0008
|
||||
@@ -232,6 +250,8 @@ ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
|
||||
ngx_int_t ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
ngx_str_t *cert, ngx_str_t *key, ngx_ssl_cache_t *cache,
|
||||
ngx_array_t *passwords);
|
||||
ngx_int_t ngx_ssl_certificate_compression(ngx_conf_t *cf, ngx_ssl_t *ssl,
|
||||
ngx_uint_t enable);
|
||||
|
||||
ngx_int_t ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers,
|
||||
ngx_uint_t prefer_server_ciphers);
|
||||
@@ -265,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 *passwords);
|
||||
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_early_data(ngx_conf_t *cf, ngx_ssl_t *ssl,
|
||||
ngx_uint_t enable);
|
||||
@@ -280,6 +302,15 @@ ngx_int_t ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl,
|
||||
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_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_uint_t flags);
|
||||
|
||||
@@ -313,6 +344,8 @@ ngx_int_t ngx_ssl_get_curve(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
ngx_str_t *s);
|
||||
ngx_int_t 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);
|
||||
ngx_int_t ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
ngx_str_t *s);
|
||||
ngx_int_t ngx_ssl_get_session_reused(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
@@ -321,6 +354,10 @@ ngx_int_t ngx_ssl_get_early_data(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
ngx_str_t *s);
|
||||
ngx_int_t ngx_ssl_get_server_name(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);
|
||||
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_str_t *s);
|
||||
ngx_int_t ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
@@ -349,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_int_t ngx_ssl_get_client_v_remain(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
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);
|
||||
@@ -362,6 +401,8 @@ ngx_chain_t *ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in,
|
||||
off_t limit);
|
||||
void ngx_ssl_free_buffer(ngx_connection_t *c);
|
||||
ngx_int_t ngx_ssl_shutdown(ngx_connection_t *c);
|
||||
void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr, ngx_err_t err,
|
||||
char *text);
|
||||
void ngx_cdecl ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
|
||||
char *fmt, ...);
|
||||
void ngx_ssl_cleanup_ctx(void *data);
|
||||
@@ -374,6 +415,11 @@ extern int ngx_ssl_ticket_keys_index;
|
||||
extern int ngx_ssl_ocsp_index;
|
||||
extern int ngx_ssl_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];
|
||||
|
||||
|
||||
#endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */
|
||||
@@ -8,10 +8,16 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_event.h>
|
||||
|
||||
#ifdef ERR_R_OSSL_STORE_LIB
|
||||
#include <openssl/store.h>
|
||||
#include <openssl/ui.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define NGX_SSL_CACHE_PATH 0
|
||||
#define NGX_SSL_CACHE_DATA 1
|
||||
#define NGX_SSL_CACHE_ENGINE 2
|
||||
#define NGX_SSL_CACHE_STORE 3
|
||||
|
||||
#define NGX_SSL_CACHE_DISABLED (ngx_array_t *) (uintptr_t) -1
|
||||
|
||||
@@ -116,6 +122,8 @@ static void ngx_ssl_cache_node_insert(ngx_rbtree_node_t *temp,
|
||||
static void ngx_ssl_cache_node_free(ngx_rbtree_t *rbtree,
|
||||
ngx_ssl_cache_node_t *cn);
|
||||
|
||||
static ngx_int_t ngx_openssl_cache_init_worker(ngx_cycle_t *cycle);
|
||||
|
||||
|
||||
static ngx_command_t ngx_openssl_cache_commands[] = {
|
||||
|
||||
@@ -144,7 +152,7 @@ ngx_module_t ngx_openssl_cache_module = {
|
||||
NGX_CORE_MODULE, /* module type */
|
||||
NULL, /* init master */
|
||||
NULL, /* init module */
|
||||
NULL, /* init process */
|
||||
ngx_openssl_cache_init_worker, /* init process */
|
||||
NULL, /* init thread */
|
||||
NULL, /* exit thread */
|
||||
NULL, /* exit process */
|
||||
@@ -185,6 +193,7 @@ ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err,
|
||||
time_t mtime;
|
||||
uint32_t hash;
|
||||
ngx_int_t rc;
|
||||
ngx_uint_t invalidate;
|
||||
ngx_file_uniq_t uniq;
|
||||
ngx_file_info_t fi;
|
||||
ngx_ssl_cache_t *cache, *old_cache;
|
||||
@@ -194,10 +203,17 @@ ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err,
|
||||
|
||||
*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) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (id.type == NGX_SSL_CACHE_DATA) {
|
||||
invalidate = 0;
|
||||
}
|
||||
|
||||
cache = (ngx_ssl_cache_t *) ngx_get_conf(cf->cycle->conf_ctx,
|
||||
ngx_openssl_cache_module);
|
||||
|
||||
@@ -207,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);
|
||||
|
||||
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;
|
||||
@@ -228,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);
|
||||
|
||||
if (old_cache && old_cache->inheritable) {
|
||||
if (old_cache && old_cache->inheritable && !invalidate) {
|
||||
cn = ngx_ssl_cache_lookup(old_cache, type, &id, hash);
|
||||
|
||||
if (cn != NULL) {
|
||||
@@ -444,6 +465,11 @@ ngx_ssl_cache_init_key(ngx_pool_t *pool, ngx_uint_t index, ngx_str_t *path,
|
||||
{
|
||||
id->type = NGX_SSL_CACHE_ENGINE;
|
||||
|
||||
} else if (index == NGX_SSL_CACHE_PKEY
|
||||
&& ngx_strncmp(path->data, "store:", sizeof("store:") - 1) == 0)
|
||||
{
|
||||
id->type = NGX_SSL_CACHE_STORE;
|
||||
|
||||
} else {
|
||||
if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, path)
|
||||
!= NGX_OK)
|
||||
@@ -694,7 +720,7 @@ ngx_ssl_cache_pkey_create(ngx_ssl_cache_key_t *id, char **err, void *data)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pkey = ENGINE_load_private_key(engine, (char *) last, 0, 0);
|
||||
pkey = ENGINE_load_private_key(engine, (char *) last, NULL, NULL);
|
||||
|
||||
if (pkey == NULL) {
|
||||
*err = "ENGINE_load_private_key() failed";
|
||||
@@ -714,11 +740,6 @@ ngx_ssl_cache_pkey_create(ngx_ssl_cache_key_t *id, char **err, void *data)
|
||||
#endif
|
||||
}
|
||||
|
||||
bio = ngx_ssl_cache_create_bio(id, err);
|
||||
if (bio == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cb_data.encrypted = 0;
|
||||
|
||||
if (*passwords) {
|
||||
@@ -734,6 +755,76 @@ ngx_ssl_cache_pkey_create(ngx_ssl_cache_key_t *id, char **err, void *data)
|
||||
cb = NULL;
|
||||
}
|
||||
|
||||
if (id->type == NGX_SSL_CACHE_STORE) {
|
||||
|
||||
#ifdef ERR_R_OSSL_STORE_LIB
|
||||
|
||||
u_char *uri;
|
||||
UI_METHOD *method;
|
||||
OSSL_STORE_CTX *store;
|
||||
OSSL_STORE_INFO *info;
|
||||
|
||||
method = (cb != NULL) ? UI_UTIL_wrap_read_pem_callback(cb, 0) : NULL;
|
||||
uri = id->data + sizeof("store:") - 1;
|
||||
|
||||
store = OSSL_STORE_open((char *) uri, method, pwd, NULL, NULL);
|
||||
|
||||
if (store == NULL) {
|
||||
*err = "OSSL_STORE_open() failed";
|
||||
|
||||
if (method != NULL) {
|
||||
UI_destroy_method(method);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pkey = NULL;
|
||||
|
||||
while (pkey == NULL && !OSSL_STORE_eof(store)) {
|
||||
info = OSSL_STORE_load(store);
|
||||
|
||||
if (info == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (OSSL_STORE_INFO_get_type(info) == OSSL_STORE_INFO_PKEY) {
|
||||
pkey = OSSL_STORE_INFO_get1_PKEY(info);
|
||||
}
|
||||
|
||||
OSSL_STORE_INFO_free(info);
|
||||
}
|
||||
|
||||
OSSL_STORE_close(store);
|
||||
|
||||
if (method != NULL) {
|
||||
UI_destroy_method(method);
|
||||
}
|
||||
|
||||
if (pkey == NULL) {
|
||||
*err = "OSSL_STORE_load() failed";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cb_data.encrypted) {
|
||||
*passwords = NGX_SSL_CACHE_DISABLED;
|
||||
}
|
||||
|
||||
return pkey;
|
||||
|
||||
#else
|
||||
|
||||
*err = "loading \"store:...\" certificate keys is not supported";
|
||||
return NULL;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
bio = ngx_ssl_cache_create_bio(id, err);
|
||||
if (bio == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
pkey = PEM_read_bio_PrivateKey(bio, NULL, cb, pwd);
|
||||
@@ -1157,3 +1248,20 @@ ngx_ssl_cache_node_insert(ngx_rbtree_node_t *temp,
|
||||
node->right = sentinel;
|
||||
ngx_rbt_red(node);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_openssl_cache_init_worker(ngx_cycle_t *cycle)
|
||||
{
|
||||
#ifdef ERR_R_OSSL_STORE_LIB
|
||||
|
||||
if (ngx_process != NGX_PROCESS_WORKER) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
UI_set_default_method(UI_null());
|
||||
|
||||
#endif
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -72,7 +72,7 @@ ngx_quic_connstate_dbg(ngx_connection_t *c)
|
||||
|
||||
if (qc) {
|
||||
|
||||
if (qc->error != (ngx_uint_t) -1) {
|
||||
if (qc->error) {
|
||||
p = ngx_slprintf(p, last, "%s", qc->error_app ? " app" : "");
|
||||
p = ngx_slprintf(p, last, " error:%ui", qc->error);
|
||||
|
||||
@@ -135,6 +135,9 @@ ngx_quic_apply_transport_params(ngx_connection_t *c, ngx_quic_tp_t *ctp)
|
||||
if (scid.len != ctp->initial_scid.len
|
||||
|| ngx_memcmp(scid.data, ctp->initial_scid.data, scid.len) != 0)
|
||||
{
|
||||
qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR;
|
||||
qc->error_reason = "invalid initial_source_connection_id";
|
||||
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"quic client initial_source_connection_id mismatch");
|
||||
return NGX_ERROR;
|
||||
@@ -257,9 +260,9 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf,
|
||||
qc->send_ctx[i].pending_ack = NGX_QUIC_UNSET_PN;
|
||||
}
|
||||
|
||||
qc->send_ctx[0].level = ssl_encryption_initial;
|
||||
qc->send_ctx[1].level = ssl_encryption_handshake;
|
||||
qc->send_ctx[2].level = ssl_encryption_application;
|
||||
qc->send_ctx[0].level = NGX_QUIC_ENCRYPTION_INITIAL;
|
||||
qc->send_ctx[1].level = NGX_QUIC_ENCRYPTION_HANDSHAKE;
|
||||
qc->send_ctx[2].level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
|
||||
ngx_queue_init(&qc->free_frames);
|
||||
|
||||
@@ -308,11 +311,16 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf,
|
||||
qc->streams.client_max_streams_uni = qc->tp.initial_max_streams_uni;
|
||||
qc->streams.client_max_streams_bidi = qc->tp.initial_max_streams_bidi;
|
||||
|
||||
qc->congestion.window = ngx_min(10 * qc->tp.max_udp_payload_size,
|
||||
ngx_max(2 * qc->tp.max_udp_payload_size,
|
||||
qc->congestion.window = ngx_min(10 * NGX_QUIC_MIN_INITIAL_SIZE,
|
||||
ngx_max(2 * NGX_QUIC_MIN_INITIAL_SIZE,
|
||||
14720));
|
||||
qc->congestion.ssthresh = (size_t) -1;
|
||||
qc->congestion.recovery_start = ngx_current_msec;
|
||||
qc->congestion.mtu = NGX_QUIC_MIN_INITIAL_SIZE;
|
||||
qc->congestion.recovery_start = ngx_current_msec - 1;
|
||||
|
||||
qc->max_frames = (conf->max_concurrent_streams_uni
|
||||
+ conf->max_concurrent_streams_bidi)
|
||||
* conf->stream_buffer_size / 2000;
|
||||
|
||||
if (pkt->validated && pkt->retried) {
|
||||
qc->tp.retry_scid.len = pkt->dcid.len;
|
||||
@@ -512,7 +520,7 @@ ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc)
|
||||
* to terminate the connection immediately.
|
||||
*/
|
||||
|
||||
if (qc->error == (ngx_uint_t) -1) {
|
||||
if (qc->error == 0 && rc == NGX_ERROR) {
|
||||
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
|
||||
qc->error_app = 0;
|
||||
}
|
||||
@@ -792,13 +800,13 @@ ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
|
||||
pkt->dcid.len, &pkt->dcid);
|
||||
|
||||
#if (NGX_DEBUG)
|
||||
if (pkt->level != ssl_encryption_application) {
|
||||
if (pkt->level != NGX_QUIC_ENCRYPTION_APPLICATION) {
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic packet rx scid len:%uz %xV",
|
||||
pkt->scid.len, &pkt->scid);
|
||||
}
|
||||
|
||||
if (pkt->level == ssl_encryption_initial) {
|
||||
if (pkt->level == NGX_QUIC_ENCRYPTION_INITIAL) {
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic address validation token len:%uz %xV",
|
||||
pkt->token.len, &pkt->token);
|
||||
@@ -815,7 +823,7 @@ ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
if (pkt->level != ssl_encryption_application) {
|
||||
if (pkt->level != NGX_QUIC_ENCRYPTION_APPLICATION) {
|
||||
|
||||
if (pkt->version != qc->version) {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
@@ -845,7 +853,9 @@ ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
|
||||
|
||||
rc = ngx_quic_handle_payload(c, pkt);
|
||||
|
||||
if (rc == NGX_DECLINED && pkt->level == ssl_encryption_application) {
|
||||
if (rc == NGX_DECLINED
|
||||
&& pkt->level == NGX_QUIC_ENCRYPTION_APPLICATION)
|
||||
{
|
||||
if (ngx_quic_handle_stateless_reset(c, pkt) == NGX_OK) {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"quic stateless reset packet detected");
|
||||
@@ -866,11 +876,11 @@ ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
|
||||
return ngx_quic_negotiate_version(c, pkt);
|
||||
}
|
||||
|
||||
if (pkt->level == ssl_encryption_application) {
|
||||
if (pkt->level == NGX_QUIC_ENCRYPTION_APPLICATION) {
|
||||
return ngx_quic_send_stateless_reset(c, conf, pkt);
|
||||
}
|
||||
|
||||
if (pkt->level != ssl_encryption_initial) {
|
||||
if (pkt->level != NGX_QUIC_ENCRYPTION_INITIAL) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic expected initial, got handshake");
|
||||
return NGX_ERROR;
|
||||
@@ -953,8 +963,8 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
|
||||
qc->error = (ngx_uint_t) -1;
|
||||
qc->error_reason = 0;
|
||||
qc->error = 0;
|
||||
qc->error_reason = NULL;
|
||||
|
||||
c->log->action = "decrypting packet";
|
||||
|
||||
@@ -965,10 +975,10 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
#if !defined (OPENSSL_IS_BORINGSSL)
|
||||
/* OpenSSL provides read keys for an application level before it's ready */
|
||||
#if (NGX_QUIC_QUICTLS_API)
|
||||
/* QuicTLS provides app read keys before completing handshake */
|
||||
|
||||
if (pkt->level == ssl_encryption_application && !c->ssl->handshaked) {
|
||||
if (pkt->level == NGX_QUIC_ENCRYPTION_APPLICATION && !c->ssl->handshaked) {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"quic no %s keys ready, ignoring packet",
|
||||
ngx_quic_level_name(pkt->level));
|
||||
@@ -1006,14 +1016,14 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
||||
}
|
||||
}
|
||||
|
||||
if (pkt->level == ssl_encryption_handshake) {
|
||||
if (pkt->level == NGX_QUIC_ENCRYPTION_HANDSHAKE) {
|
||||
/*
|
||||
* RFC 9001, 4.9.1. Discarding Initial Keys
|
||||
*
|
||||
* The successful use of Handshake packets indicates
|
||||
* that no more Initial packets need to be exchanged
|
||||
*/
|
||||
ngx_quic_discard_ctx(c, ssl_encryption_initial);
|
||||
ngx_quic_discard_ctx(c, NGX_QUIC_ENCRYPTION_INITIAL);
|
||||
|
||||
if (!qc->path->validated) {
|
||||
qc->path->validated = 1;
|
||||
@@ -1022,14 +1032,14 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
||||
}
|
||||
}
|
||||
|
||||
if (pkt->level == ssl_encryption_application) {
|
||||
if (pkt->level == NGX_QUIC_ENCRYPTION_APPLICATION) {
|
||||
/*
|
||||
* RFC 9001, 4.9.3. Discarding 0-RTT Keys
|
||||
*
|
||||
* After receiving a 1-RTT packet, servers MUST discard
|
||||
* 0-RTT keys within a short time
|
||||
*/
|
||||
ngx_quic_keys_discard(qc->keys, ssl_encryption_early_data);
|
||||
ngx_quic_keys_discard(qc->keys, NGX_QUIC_ENCRYPTION_EARLY_DATA);
|
||||
}
|
||||
|
||||
if (qc->closing) {
|
||||
@@ -1056,7 +1066,7 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
||||
|
||||
c->log->action = "handling payload";
|
||||
|
||||
if (pkt->level != ssl_encryption_application) {
|
||||
if (pkt->level != NGX_QUIC_ENCRYPTION_APPLICATION) {
|
||||
return ngx_quic_handle_frames(c, pkt);
|
||||
}
|
||||
|
||||
@@ -1081,7 +1091,7 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
||||
|
||||
|
||||
void
|
||||
ngx_quic_discard_ctx(ngx_connection_t *c, enum ssl_encryption_level_t level)
|
||||
ngx_quic_discard_ctx(ngx_connection_t *c, ngx_uint_t level)
|
||||
{
|
||||
ngx_queue_t *q;
|
||||
ngx_quic_frame_t *f;
|
||||
@@ -1122,7 +1132,7 @@ ngx_quic_discard_ctx(ngx_connection_t *c, enum ssl_encryption_level_t level)
|
||||
ngx_quic_free_frame(c, f);
|
||||
}
|
||||
|
||||
if (level == ssl_encryption_initial) {
|
||||
if (level == NGX_QUIC_ENCRYPTION_INITIAL) {
|
||||
/* close temporary listener with initial dcid */
|
||||
qsock = ngx_quic_find_socket(c, NGX_QUIC_UNSET_PN);
|
||||
if (qsock) {
|
||||
|
||||
@@ -12,6 +12,22 @@
|
||||
#include <ngx_core.h>
|
||||
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x30500010L)
|
||||
#define NGX_QUIC_OPENSSL_API 1
|
||||
|
||||
#elif (defined SSL_R_MISSING_QUIC_TRANSPORT_PARAMETERS_EXTENSION)
|
||||
#define NGX_QUIC_QUICTLS_API 1
|
||||
|
||||
#elif (defined OPENSSL_IS_BORINGSSL || defined OPENSSL_IS_AWSLC \
|
||||
|| defined LIBRESSL_VERSION_NUMBER)
|
||||
#define NGX_QUIC_BORINGSSL_API 1
|
||||
|
||||
#else
|
||||
#define NGX_QUIC_BORINGSSL_API 1
|
||||
#define NGX_QUIC_OPENSSL_COMPAT 1
|
||||
#endif
|
||||
|
||||
|
||||
#define NGX_QUIC_MAX_UDP_PAYLOAD_SIZE 65527
|
||||
|
||||
#define NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT 3
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
/* RFC 9002, 7.6.1. Duration: kPersistentCongestionThreshold */
|
||||
#define NGX_QUIC_PERSISTENT_CONGESTION_THR 3
|
||||
|
||||
/* CUBIC parameters x10 */
|
||||
#define NGX_QUIC_CUBIC_BETA 7
|
||||
#define NGX_QUIC_CUBIC_C 4
|
||||
|
||||
|
||||
/* send time of ACK'ed packets */
|
||||
typedef struct {
|
||||
@@ -29,18 +33,22 @@ typedef struct {
|
||||
} ngx_quic_ack_stat_t;
|
||||
|
||||
|
||||
static ngx_inline ngx_msec_t ngx_quic_lost_threshold(ngx_quic_connection_t *qc);
|
||||
static ngx_inline ngx_msec_t ngx_quic_time_threshold(ngx_quic_connection_t *qc);
|
||||
static uint64_t ngx_quic_packet_threshold(ngx_quic_send_ctx_t *ctx);
|
||||
static void ngx_quic_rtt_sample(ngx_connection_t *c, ngx_quic_ack_frame_t *ack,
|
||||
enum ssl_encryption_level_t level, ngx_msec_t send_time);
|
||||
ngx_uint_t level, ngx_msec_t send_time);
|
||||
static ngx_int_t ngx_quic_handle_ack_frame_range(ngx_connection_t *c,
|
||||
ngx_quic_send_ctx_t *ctx, uint64_t min, uint64_t max,
|
||||
ngx_quic_ack_stat_t *st);
|
||||
static size_t ngx_quic_congestion_cubic(ngx_connection_t *c);
|
||||
static void ngx_quic_drop_ack_ranges(ngx_connection_t *c,
|
||||
ngx_quic_send_ctx_t *ctx, uint64_t pn);
|
||||
static ngx_int_t ngx_quic_detect_lost(ngx_connection_t *c,
|
||||
ngx_quic_ack_stat_t *st);
|
||||
static ngx_msec_t ngx_quic_congestion_cubic_time(ngx_connection_t *c);
|
||||
static ngx_msec_t ngx_quic_pcg_duration(ngx_connection_t *c);
|
||||
static void ngx_quic_persistent_congestion(ngx_connection_t *c);
|
||||
static ngx_msec_t ngx_quic_oldest_sent_packet(ngx_connection_t *c);
|
||||
static void ngx_quic_congestion_lost(ngx_connection_t *c,
|
||||
ngx_quic_frame_t *frame);
|
||||
static void ngx_quic_lost_handler(ngx_event_t *ev);
|
||||
@@ -48,7 +56,7 @@ static void ngx_quic_lost_handler(ngx_event_t *ev);
|
||||
|
||||
/* RFC 9002, 6.1.2. Time Threshold: kTimeThreshold, kGranularity */
|
||||
static ngx_inline ngx_msec_t
|
||||
ngx_quic_lost_threshold(ngx_quic_connection_t *qc)
|
||||
ngx_quic_time_threshold(ngx_quic_connection_t *qc)
|
||||
{
|
||||
ngx_msec_t thr;
|
||||
|
||||
@@ -59,6 +67,29 @@ ngx_quic_lost_threshold(ngx_quic_connection_t *qc)
|
||||
}
|
||||
|
||||
|
||||
static uint64_t
|
||||
ngx_quic_packet_threshold(ngx_quic_send_ctx_t *ctx)
|
||||
{
|
||||
uint64_t pkt_thr;
|
||||
ngx_queue_t *q;
|
||||
ngx_quic_frame_t *f;
|
||||
|
||||
if (ngx_queue_empty(&ctx->sent)) {
|
||||
return NGX_QUIC_PKT_THR;
|
||||
}
|
||||
|
||||
q = ngx_queue_head(&ctx->sent);
|
||||
f = ngx_queue_data(q, ngx_quic_frame_t, queue);
|
||||
pkt_thr = (ctx->pnum - f->pnum) / 2;
|
||||
|
||||
if (pkt_thr <= NGX_QUIC_PKT_THR) {
|
||||
return NGX_QUIC_PKT_THR;
|
||||
}
|
||||
|
||||
return pkt_thr;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
|
||||
ngx_quic_frame_t *f)
|
||||
@@ -77,7 +108,7 @@ ngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
|
||||
ctx = ngx_quic_get_send_ctx(qc, pkt->level);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_handle_ack_frame level:%d", pkt->level);
|
||||
"quic ngx_quic_handle_ack_frame level:%ui", pkt->level);
|
||||
|
||||
ack = &f->u.ack;
|
||||
|
||||
@@ -176,7 +207,7 @@ ngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
|
||||
|
||||
static void
|
||||
ngx_quic_rtt_sample(ngx_connection_t *c, ngx_quic_ack_frame_t *ack,
|
||||
enum ssl_encryption_level_t level, ngx_msec_t send_time)
|
||||
ngx_uint_t level, ngx_msec_t send_time)
|
||||
{
|
||||
ngx_msec_t latest_rtt, ack_delay, adjusted_rtt, rttvar_sample;
|
||||
ngx_quic_connection_t *qc;
|
||||
@@ -229,7 +260,7 @@ ngx_quic_handle_ack_frame_range(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
|
||||
if (ctx->level == ssl_encryption_application) {
|
||||
if (ctx->level == NGX_QUIC_ENCRYPTION_APPLICATION) {
|
||||
if (ngx_quic_handle_path_mtu(c, qc->path, min, max) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
@@ -313,8 +344,9 @@ ngx_quic_handle_ack_frame_range(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
void
|
||||
ngx_quic_congestion_ack(ngx_connection_t *c, ngx_quic_frame_t *f)
|
||||
{
|
||||
size_t w_cubic;
|
||||
ngx_uint_t blocked;
|
||||
ngx_msec_t timer;
|
||||
ngx_msec_t now, timer;
|
||||
ngx_quic_congestion_t *cg;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
@@ -329,16 +361,34 @@ ngx_quic_congestion_ack(ngx_connection_t *c, ngx_quic_frame_t *f)
|
||||
return;
|
||||
}
|
||||
|
||||
now = ngx_current_msec;
|
||||
|
||||
blocked = (cg->in_flight >= cg->window) ? 1 : 0;
|
||||
|
||||
cg->in_flight -= f->plen;
|
||||
|
||||
/* prevent recovery_start from wrapping */
|
||||
|
||||
timer = now - cg->recovery_start;
|
||||
|
||||
if ((ngx_msec_int_t) timer < 0) {
|
||||
cg->recovery_start = ngx_quic_oldest_sent_packet(c) - 1;
|
||||
}
|
||||
|
||||
timer = f->send_time - cg->recovery_start;
|
||||
|
||||
if ((ngx_msec_int_t) timer <= 0) {
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion ack recovery win:%uz ss:%z if:%uz",
|
||||
cg->window, cg->ssthresh, cg->in_flight);
|
||||
"quic congestion ack rec t:%M win:%uz if:%uz",
|
||||
now, cg->window, cg->in_flight);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (cg->idle) {
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion ack idle t:%M win:%uz if:%uz",
|
||||
now, cg->window, cg->in_flight);
|
||||
|
||||
goto done;
|
||||
}
|
||||
@@ -346,24 +396,51 @@ ngx_quic_congestion_ack(ngx_connection_t *c, ngx_quic_frame_t *f)
|
||||
if (cg->window < cg->ssthresh) {
|
||||
cg->window += f->plen;
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion slow start win:%uz ss:%z if:%uz",
|
||||
cg->window, cg->ssthresh, cg->in_flight);
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion ack ss t:%M win:%uz ss:%z if:%uz",
|
||||
now, cg->window, cg->ssthresh, cg->in_flight);
|
||||
|
||||
} else {
|
||||
cg->window += qc->tp.max_udp_payload_size * f->plen / cg->window;
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion avoidance win:%uz ss:%z if:%uz",
|
||||
cg->window, cg->ssthresh, cg->in_flight);
|
||||
}
|
||||
/* RFC 9438, 4.2. Window Increase Function */
|
||||
|
||||
/* prevent recovery_start from wrapping */
|
||||
w_cubic = ngx_quic_congestion_cubic(c);
|
||||
|
||||
timer = cg->recovery_start - ngx_current_msec + qc->tp.max_idle_timeout * 2;
|
||||
if (cg->window < cg->w_prior) {
|
||||
cg->w_est += (uint64_t) cg->mtu * f->plen
|
||||
* 3 * (10 - NGX_QUIC_CUBIC_BETA)
|
||||
/ (10 + NGX_QUIC_CUBIC_BETA) / cg->window;
|
||||
|
||||
if ((ngx_msec_int_t) timer < 0) {
|
||||
cg->recovery_start = ngx_current_msec - qc->tp.max_idle_timeout * 2;
|
||||
} else {
|
||||
cg->w_est += (uint64_t) cg->mtu * f->plen / cg->window;
|
||||
}
|
||||
|
||||
if (w_cubic < cg->w_est) {
|
||||
cg->window = cg->w_est;
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion ack reno t:%M win:%uz c:%uz if:%uz",
|
||||
now, cg->window, w_cubic, cg->in_flight);
|
||||
|
||||
} else if (w_cubic > cg->window) {
|
||||
|
||||
if (w_cubic >= cg->window * 3 / 2) {
|
||||
cg->window += cg->mtu / 2;
|
||||
|
||||
} else {
|
||||
cg->window += (uint64_t) cg->mtu * (w_cubic - cg->window)
|
||||
/ cg->window;
|
||||
}
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion ack cubic t:%M win:%uz c:%uz if:%uz",
|
||||
now, cg->window, w_cubic, cg->in_flight);
|
||||
|
||||
} else {
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion ack skip t:%M win:%uz c:%uz if:%uz",
|
||||
now, cg->window, w_cubic, cg->in_flight);
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
@@ -374,6 +451,87 @@ done:
|
||||
}
|
||||
|
||||
|
||||
static size_t
|
||||
ngx_quic_congestion_cubic(ngx_connection_t *c)
|
||||
{
|
||||
int64_t w, t, cc;
|
||||
ngx_msec_t now;
|
||||
ngx_quic_congestion_t *cg;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
cg = &qc->congestion;
|
||||
|
||||
ngx_quic_congestion_idle(c, cg->idle);
|
||||
|
||||
now = ngx_current_msec;
|
||||
t = (ngx_msec_int_t) (now - cg->k);
|
||||
|
||||
if (t > 1000000) {
|
||||
w = NGX_MAX_SIZE_T_VALUE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (t < -1000000) {
|
||||
w = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* RFC 9438, Figure 1
|
||||
*
|
||||
* w_cubic = C * (t_msec / 1000) ^ 3 * mtu + w_max
|
||||
*/
|
||||
|
||||
cc = 10000000000ll / (int64_t) cg->mtu / NGX_QUIC_CUBIC_C;
|
||||
w = t * t * t / cc + (int64_t) cg->w_max;
|
||||
|
||||
if (w > NGX_MAX_SIZE_T_VALUE) {
|
||||
w = NGX_MAX_SIZE_T_VALUE;
|
||||
}
|
||||
|
||||
if (w < 0) {
|
||||
w = 0;
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic cubic t:%L w:%L wm:%uz", t, w, cg->w_max);
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ngx_quic_congestion_idle(ngx_connection_t *c, ngx_uint_t idle)
|
||||
{
|
||||
ngx_msec_t now;
|
||||
ngx_quic_congestion_t *cg;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
cg = &qc->congestion;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion idle:%ui", idle);
|
||||
|
||||
if (cg->window >= cg->ssthresh) {
|
||||
/* RFC 9438, 5.8. Behavior for Application-Limited Flows */
|
||||
|
||||
now = ngx_current_msec;
|
||||
|
||||
if (cg->idle) {
|
||||
cg->k += now - cg->idle_start;
|
||||
}
|
||||
|
||||
cg->idle_start = now;
|
||||
}
|
||||
|
||||
cg->idle = idle;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_quic_drop_ack_ranges(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
uint64_t pn)
|
||||
@@ -435,6 +593,7 @@ ngx_quic_drop_ack_ranges(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
static ngx_int_t
|
||||
ngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)
|
||||
{
|
||||
uint64_t pkt_thr;
|
||||
ngx_uint_t i, nlost;
|
||||
ngx_msec_t now, wait, thr, oldest, newest;
|
||||
ngx_queue_t *q;
|
||||
@@ -444,11 +603,12 @@ ngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
now = ngx_current_msec;
|
||||
thr = ngx_quic_lost_threshold(qc);
|
||||
thr = ngx_quic_time_threshold(qc);
|
||||
|
||||
/* send time of lost packets across all send contexts */
|
||||
oldest = NGX_TIMER_INFINITE;
|
||||
newest = NGX_TIMER_INFINITE;
|
||||
#if (NGX_SUPPRESS_WARN)
|
||||
oldest = now;
|
||||
newest = now;
|
||||
#endif
|
||||
|
||||
nlost = 0;
|
||||
|
||||
@@ -460,6 +620,8 @@ ngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)
|
||||
continue;
|
||||
}
|
||||
|
||||
pkt_thr = ngx_quic_packet_threshold(ctx);
|
||||
|
||||
while (!ngx_queue_empty(&ctx->sent)) {
|
||||
|
||||
q = ngx_queue_head(&ctx->sent);
|
||||
@@ -471,23 +633,27 @@ ngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)
|
||||
|
||||
wait = start->send_time + thr - now;
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic detect_lost pnum:%uL thr:%M wait:%i level:%d",
|
||||
start->pnum, thr, (ngx_int_t) wait, start->level);
|
||||
ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic detect_lost pnum:%uL thr:%M pthr:%uL wait:%i level:%ui",
|
||||
start->pnum, thr, pkt_thr, (ngx_int_t) wait, start->level);
|
||||
|
||||
if ((ngx_msec_int_t) wait > 0
|
||||
&& ctx->largest_ack - start->pnum < NGX_QUIC_PKT_THR)
|
||||
&& ctx->largest_ack - start->pnum < pkt_thr)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (start->send_time > qc->first_rtt) {
|
||||
if ((ngx_msec_int_t) (start->send_time - qc->first_rtt) > 0) {
|
||||
|
||||
if (oldest == NGX_TIMER_INFINITE || start->send_time < oldest) {
|
||||
if (nlost == 0
|
||||
|| (ngx_msec_int_t) (start->send_time - oldest) < 0)
|
||||
{
|
||||
oldest = start->send_time;
|
||||
}
|
||||
|
||||
if (newest == NGX_TIMER_INFINITE || start->send_time > newest) {
|
||||
if (nlost == 0
|
||||
|| (ngx_msec_int_t) (start->send_time - newest) > 0)
|
||||
{
|
||||
newest = start->send_time;
|
||||
}
|
||||
|
||||
@@ -508,8 +674,9 @@ ngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)
|
||||
* latest ACK frame.
|
||||
*/
|
||||
|
||||
if (st && nlost >= 2 && (st->newest < oldest || st->oldest > newest)) {
|
||||
|
||||
if (st && nlost >= 2 && ((ngx_msec_int_t) (st->newest - oldest) < 0
|
||||
|| (ngx_msec_int_t) (st->oldest - newest) > 0))
|
||||
{
|
||||
if (newest - oldest > ngx_quic_pcg_duration(c)) {
|
||||
ngx_quic_persistent_congestion(c);
|
||||
}
|
||||
@@ -547,11 +714,43 @@ ngx_quic_persistent_congestion(ngx_connection_t *c)
|
||||
qc = ngx_quic_get_connection(c);
|
||||
cg = &qc->congestion;
|
||||
|
||||
cg->recovery_start = ngx_current_msec;
|
||||
cg->window = qc->tp.max_udp_payload_size * 2;
|
||||
cg->mtu = qc->path->mtu;
|
||||
cg->recovery_start = ngx_quic_oldest_sent_packet(c) - 1;
|
||||
cg->window = cg->mtu * 2;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic persistent congestion win:%uz", cg->window);
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion persistent t:%M win:%uz",
|
||||
ngx_current_msec, cg->window);
|
||||
}
|
||||
|
||||
|
||||
static ngx_msec_t
|
||||
ngx_quic_oldest_sent_packet(ngx_connection_t *c)
|
||||
{
|
||||
ngx_msec_t oldest;
|
||||
ngx_uint_t i;
|
||||
ngx_queue_t *q;
|
||||
ngx_quic_frame_t *start;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
oldest = ngx_current_msec;
|
||||
|
||||
for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
|
||||
ctx = &qc->send_ctx[i];
|
||||
|
||||
if (!ngx_queue_empty(&ctx->sent)) {
|
||||
q = ngx_queue_head(&ctx->sent);
|
||||
start = ngx_queue_data(q, ngx_quic_frame_t, queue);
|
||||
|
||||
if ((ngx_msec_int_t) (start->send_time - oldest) < 0) {
|
||||
oldest = start->send_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return oldest;
|
||||
}
|
||||
|
||||
|
||||
@@ -588,7 +787,7 @@ ngx_quic_resend_frames(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
|
||||
switch (f->type) {
|
||||
case NGX_QUIC_FT_ACK:
|
||||
case NGX_QUIC_FT_ACK_ECN:
|
||||
if (ctx->level == ssl_encryption_application) {
|
||||
if (ctx->level == NGX_QUIC_ENCRYPTION_APPLICATION) {
|
||||
/* force generation of most recent acknowledgment */
|
||||
ctx->send_ack = NGX_QUIC_MAX_ACK_GAP;
|
||||
}
|
||||
@@ -659,7 +858,7 @@ static void
|
||||
ngx_quic_congestion_lost(ngx_connection_t *c, ngx_quic_frame_t *f)
|
||||
{
|
||||
ngx_uint_t blocked;
|
||||
ngx_msec_t timer;
|
||||
ngx_msec_t now, timer;
|
||||
ngx_quic_congestion_t *cg;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
@@ -681,26 +880,41 @@ ngx_quic_congestion_lost(ngx_connection_t *c, ngx_quic_frame_t *f)
|
||||
|
||||
timer = f->send_time - cg->recovery_start;
|
||||
|
||||
now = ngx_current_msec;
|
||||
|
||||
if ((ngx_msec_int_t) timer <= 0) {
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion lost recovery win:%uz ss:%z if:%uz",
|
||||
cg->window, cg->ssthresh, cg->in_flight);
|
||||
"quic congestion lost rec t:%M win:%uz if:%uz",
|
||||
now, cg->window, cg->in_flight);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
cg->recovery_start = ngx_current_msec;
|
||||
cg->window /= 2;
|
||||
if (f->ignore_loss) {
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion lost ignore t:%M win:%uz if:%uz",
|
||||
now, cg->window, cg->in_flight);
|
||||
|
||||
if (cg->window < qc->tp.max_udp_payload_size * 2) {
|
||||
cg->window = qc->tp.max_udp_payload_size * 2;
|
||||
goto done;
|
||||
}
|
||||
|
||||
cg->ssthresh = cg->window;
|
||||
/* RFC 9438, 4.6. Multiplicative Decrease */
|
||||
|
||||
cg->mtu = qc->path->mtu;
|
||||
cg->recovery_start = now;
|
||||
cg->w_prior = cg->window;
|
||||
/* RFC 9438, 4.7. Fast Convergence */
|
||||
cg->w_max = (cg->window < cg->w_max)
|
||||
? cg->window * (10 + NGX_QUIC_CUBIC_BETA) / 20 : cg->window;
|
||||
cg->ssthresh = cg->in_flight * NGX_QUIC_CUBIC_BETA / 10;
|
||||
cg->window = ngx_max(cg->ssthresh, cg->mtu * 2);
|
||||
cg->w_est = cg->window;
|
||||
cg->k = now + ngx_quic_congestion_cubic_time(c);
|
||||
cg->idle_start = now;
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion lost win:%uz ss:%z if:%uz",
|
||||
cg->window, cg->ssthresh, cg->in_flight);
|
||||
"quic congestion lost t:%M win:%uz if:%uz",
|
||||
now, cg->window, cg->in_flight);
|
||||
|
||||
done:
|
||||
|
||||
@@ -710,9 +924,62 @@ done:
|
||||
}
|
||||
|
||||
|
||||
static ngx_msec_t
|
||||
ngx_quic_congestion_cubic_time(ngx_connection_t *c)
|
||||
{
|
||||
int64_t v, x, d, cc;
|
||||
ngx_uint_t n;
|
||||
ngx_quic_congestion_t *cg;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
cg = &qc->congestion;
|
||||
|
||||
/*
|
||||
* RFC 9438, Figure 2
|
||||
*
|
||||
* k_msec = ((w_max - cwnd_epoch) / C / mtu) ^ 1/3 * 1000
|
||||
*/
|
||||
|
||||
if (cg->w_max <= cg->window) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
cc = 10000000000ll / (int64_t) cg->mtu / NGX_QUIC_CUBIC_C;
|
||||
v = (int64_t) (cg->w_max - cg->window) * cc;
|
||||
|
||||
/*
|
||||
* Newton-Raphson method for x ^ 3 = v:
|
||||
*
|
||||
* x_next = (2 * x_prev + v / x_prev ^ 2) / 3
|
||||
*/
|
||||
|
||||
x = 5000;
|
||||
|
||||
for (n = 1; n <= 10; n++) {
|
||||
d = (v / x / x - x) / 3;
|
||||
x += d;
|
||||
|
||||
if (ngx_abs(d) <= 100) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (x > NGX_MAX_SIZE_T_VALUE) {
|
||||
return NGX_MAX_SIZE_T_VALUE;
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic cubic time:%L n:%ui", x, n);
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ngx_quic_set_lost_timer(ngx_connection_t *c)
|
||||
{
|
||||
uint64_t pkt_thr;
|
||||
ngx_uint_t i;
|
||||
ngx_msec_t now;
|
||||
ngx_queue_t *q;
|
||||
@@ -738,10 +1005,12 @@ ngx_quic_set_lost_timer(ngx_connection_t *c)
|
||||
q = ngx_queue_head(&ctx->sent);
|
||||
f = ngx_queue_data(q, ngx_quic_frame_t, queue);
|
||||
w = (ngx_msec_int_t)
|
||||
(f->send_time + ngx_quic_lost_threshold(qc) - now);
|
||||
(f->send_time + ngx_quic_time_threshold(qc) - now);
|
||||
|
||||
if (f->pnum <= ctx->largest_ack) {
|
||||
if (w < 0 || ctx->largest_ack - f->pnum >= NGX_QUIC_PKT_THR) {
|
||||
pkt_thr = ngx_quic_packet_threshold(ctx);
|
||||
|
||||
if (w < 0 || ctx->largest_ack - f->pnum >= pkt_thr) {
|
||||
w = 0;
|
||||
}
|
||||
|
||||
@@ -804,7 +1073,7 @@ ngx_quic_pto(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
|
||||
duration = qc->avg_rtt;
|
||||
duration += ngx_max(4 * qc->rttvar, NGX_QUIC_TIME_GRANULARITY);
|
||||
|
||||
if (ctx->level == ssl_encryption_application && c->ssl->handshaked) {
|
||||
if (ctx->level == NGX_QUIC_ENCRYPTION_APPLICATION && c->ssl->handshaked) {
|
||||
duration += qc->ctp.max_ack_delay;
|
||||
}
|
||||
|
||||
@@ -1159,7 +1428,7 @@ ngx_quic_generate_ack(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (ctx->level == ssl_encryption_application) {
|
||||
if (ctx->level == NGX_QUIC_ENCRYPTION_APPLICATION) {
|
||||
|
||||
delay = ngx_current_msec - ctx->ack_delay_start;
|
||||
qc = ngx_quic_get_connection(c);
|
||||
|
||||
@@ -17,6 +17,7 @@ ngx_int_t ngx_quic_handle_ack_frame(ngx_connection_t *c,
|
||||
|
||||
void ngx_quic_congestion_ack(ngx_connection_t *c,
|
||||
ngx_quic_frame_t *frame);
|
||||
void ngx_quic_congestion_idle(ngx_connection_t *c, ngx_uint_t idle);
|
||||
void ngx_quic_resend_frames(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx);
|
||||
void ngx_quic_set_lost_timer(ngx_connection_t *c);
|
||||
void ngx_quic_pto_handler(ngx_event_t *ev);
|
||||
|
||||
@@ -17,6 +17,15 @@
|
||||
/* #define NGX_QUIC_DEBUG_ALLOC */ /* log frames and bufs alloc */
|
||||
/* #define NGX_QUIC_DEBUG_CRYPTO */
|
||||
|
||||
#define NGX_QUIC_ENCRYPTION_INITIAL 0
|
||||
#define NGX_QUIC_ENCRYPTION_EARLY_DATA 1
|
||||
#define NGX_QUIC_ENCRYPTION_HANDSHAKE 2
|
||||
#define NGX_QUIC_ENCRYPTION_APPLICATION 3
|
||||
#define NGX_QUIC_ENCRYPTION_LAST 4
|
||||
|
||||
#define NGX_QUIC_SEND_CTX_LAST (NGX_QUIC_ENCRYPTION_LAST - 1)
|
||||
|
||||
|
||||
typedef struct ngx_quic_connection_s ngx_quic_connection_t;
|
||||
typedef struct ngx_quic_server_id_s ngx_quic_server_id_t;
|
||||
typedef struct ngx_quic_client_id_s ngx_quic_client_id_t;
|
||||
@@ -46,8 +55,6 @@ typedef struct ngx_quic_keys_s ngx_quic_keys_t;
|
||||
|
||||
#define NGX_QUIC_UNSET_PN (uint64_t) -1
|
||||
|
||||
#define NGX_QUIC_SEND_CTX_LAST (NGX_QUIC_ENCRYPTION_LAST - 1)
|
||||
|
||||
/* 0-RTT and 1-RTT data exist in the same packet number space,
|
||||
* so we have 3 packet number spaces:
|
||||
*
|
||||
@@ -56,9 +63,9 @@ typedef struct ngx_quic_keys_s ngx_quic_keys_t;
|
||||
* 2 - 0-RTT and 1-RTT
|
||||
*/
|
||||
#define ngx_quic_get_send_ctx(qc, level) \
|
||||
((level) == ssl_encryption_initial) ? &((qc)->send_ctx[0]) \
|
||||
: (((level) == ssl_encryption_handshake) ? &((qc)->send_ctx[1]) \
|
||||
: &((qc)->send_ctx[2]))
|
||||
((level) == NGX_QUIC_ENCRYPTION_INITIAL) ? &((qc)->send_ctx[0]) \
|
||||
: (((level) == NGX_QUIC_ENCRYPTION_HANDSHAKE) ? &((qc)->send_ctx[1]) \
|
||||
: &((qc)->send_ctx[2]))
|
||||
|
||||
#define ngx_quic_get_connection(c) \
|
||||
(((c)->udp) ? (((ngx_quic_socket_t *)((c)->udp))->quic) : NULL)
|
||||
@@ -168,7 +175,14 @@ typedef struct {
|
||||
size_t in_flight;
|
||||
size_t window;
|
||||
size_t ssthresh;
|
||||
size_t w_max;
|
||||
size_t w_est;
|
||||
size_t w_prior;
|
||||
size_t mtu;
|
||||
ngx_msec_t recovery_start;
|
||||
ngx_msec_t idle_start;
|
||||
ngx_msec_t k;
|
||||
ngx_uint_t idle; /* unsigned idle:1; */
|
||||
} ngx_quic_congestion_t;
|
||||
|
||||
|
||||
@@ -181,7 +195,7 @@ typedef struct {
|
||||
* are also Initial packets.
|
||||
*/
|
||||
struct ngx_quic_send_ctx_s {
|
||||
enum ssl_encryption_level_t level;
|
||||
ngx_uint_t level;
|
||||
|
||||
ngx_quic_buffer_t crypto;
|
||||
uint64_t crypto_sent;
|
||||
@@ -254,6 +268,7 @@ struct ngx_quic_connection_s {
|
||||
ngx_buf_t *free_shadow_bufs;
|
||||
|
||||
ngx_uint_t nframes;
|
||||
ngx_uint_t max_frames;
|
||||
#ifdef NGX_QUIC_DEBUG_ALLOC
|
||||
ngx_uint_t nbufs;
|
||||
ngx_uint_t nshadowbufs;
|
||||
@@ -271,7 +286,7 @@ struct ngx_quic_connection_s {
|
||||
off_t received;
|
||||
|
||||
ngx_uint_t error;
|
||||
enum ssl_encryption_level_t error_level;
|
||||
ngx_uint_t error_level;
|
||||
ngx_uint_t error_ftype;
|
||||
const char *error_reason;
|
||||
|
||||
@@ -286,13 +301,17 @@ struct ngx_quic_connection_s {
|
||||
unsigned key_phase:1;
|
||||
unsigned validated:1;
|
||||
unsigned client_tp_done:1;
|
||||
|
||||
#if (NGX_QUIC_OPENSSL_API)
|
||||
unsigned read_level:2;
|
||||
unsigned write_level:2;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
ngx_int_t ngx_quic_apply_transport_params(ngx_connection_t *c,
|
||||
ngx_quic_tp_t *ctp);
|
||||
void ngx_quic_discard_ctx(ngx_connection_t *c,
|
||||
enum ssl_encryption_level_t level);
|
||||
void ngx_quic_discard_ctx(ngx_connection_t *c, ngx_uint_t level);
|
||||
void ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc);
|
||||
void ngx_quic_shutdown_quic(ngx_connection_t *c);
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ ngx_quic_handle_new_connection_id_frame(ngx_connection_t *c,
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_RETIRE_CONNECTION_ID;
|
||||
frame->u.retire_cid.sequence_number = f->seqnum;
|
||||
|
||||
@@ -452,7 +452,7 @@ ngx_quic_send_server_id(ngx_connection_t *c, ngx_quic_server_id_t *sid)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_NEW_CONNECTION_ID;
|
||||
frame->u.ncid.seqnum = sid->seqnum;
|
||||
frame->u.ncid.retire = 0;
|
||||
@@ -485,7 +485,7 @@ ngx_quic_free_client_id(ngx_connection_t *c, ngx_quic_client_id_t *cid)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_RETIRE_CONNECTION_ID;
|
||||
frame->u.retire_cid.sequence_number = cid->seqnum;
|
||||
|
||||
|
||||
@@ -214,7 +214,7 @@ ngx_quic_alloc_frame(ngx_connection_t *c)
|
||||
"quic reuse frame n:%ui", qc->nframes);
|
||||
#endif
|
||||
|
||||
} else if (qc->nframes < 10000) {
|
||||
} else if (qc->nframes < qc->max_frames) {
|
||||
frame = ngx_palloc(c->pool, sizeof(ngx_quic_frame_t));
|
||||
if (frame == NULL) {
|
||||
return NULL;
|
||||
|
||||
@@ -40,7 +40,7 @@ ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
|
||||
ngx_quic_frame_t *fp;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
if (pkt->level != ssl_encryption_application || pkt->path_challenged) {
|
||||
if (pkt->level != NGX_QUIC_ENCRYPTION_APPLICATION || pkt->path_challenged) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ignoring PATH_CHALLENGE");
|
||||
return NGX_OK;
|
||||
@@ -55,7 +55,7 @@ ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
fp->level = ssl_encryption_application;
|
||||
fp->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
fp->type = NGX_QUIC_FT_PATH_RESPONSE;
|
||||
fp->u.path_response = *f;
|
||||
|
||||
@@ -93,7 +93,7 @@ ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
fp->level = ssl_encryption_application;
|
||||
fp->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
fp->type = NGX_QUIC_FT_PING;
|
||||
|
||||
ngx_quic_queue_frame(qc, fp);
|
||||
@@ -177,16 +177,17 @@ valid:
|
||||
if (rst) {
|
||||
/* prevent old path packets contribution to congestion control */
|
||||
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
|
||||
qc->rst_pnum = ctx->pnum;
|
||||
|
||||
ngx_memzero(&qc->congestion, sizeof(ngx_quic_congestion_t));
|
||||
|
||||
qc->congestion.window = ngx_min(10 * qc->tp.max_udp_payload_size,
|
||||
ngx_max(2 * qc->tp.max_udp_payload_size,
|
||||
qc->congestion.window = ngx_min(10 * NGX_QUIC_MIN_INITIAL_SIZE,
|
||||
ngx_max(2 * NGX_QUIC_MIN_INITIAL_SIZE,
|
||||
14720));
|
||||
qc->congestion.ssthresh = (size_t) -1;
|
||||
qc->congestion.recovery_start = ngx_current_msec;
|
||||
qc->congestion.mtu = NGX_QUIC_MIN_INITIAL_SIZE;
|
||||
qc->congestion.recovery_start = ngx_current_msec - 1;
|
||||
|
||||
ngx_quic_init_rtt(qc);
|
||||
}
|
||||
@@ -548,7 +549,7 @@ ngx_quic_validate_path(ngx_connection_t *c, ngx_quic_path_t *path)
|
||||
|
||||
(void) ngx_quic_send_path_challenge(c, path);
|
||||
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
|
||||
pto = ngx_max(ngx_quic_pto(c, ctx), 1000);
|
||||
|
||||
path->expires = ngx_current_msec + pto;
|
||||
@@ -578,7 +579,7 @@ ngx_quic_send_path_challenge(ngx_connection_t *c, ngx_quic_path_t *path)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_PATH_CHALLENGE;
|
||||
|
||||
ngx_memcpy(frame->u.path_challenge.data, path->challenge[n], 8);
|
||||
@@ -766,7 +767,7 @@ ngx_quic_expire_path_validation(ngx_connection_t *c, ngx_quic_path_t *path)
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
|
||||
|
||||
if (++path->tries < NGX_QUIC_PATH_RETRIES) {
|
||||
pto = ngx_max(ngx_quic_pto(c, ctx), 1000) << path->tries;
|
||||
@@ -829,7 +830,7 @@ ngx_quic_expire_path_mtu_delay(ngx_connection_t *c, ngx_quic_path_t *path)
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
|
||||
|
||||
path->tries = 0;
|
||||
|
||||
@@ -875,7 +876,7 @@ ngx_quic_expire_path_mtu_discovery(ngx_connection_t *c, ngx_quic_path_t *path)
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
|
||||
|
||||
if (++path->tries < NGX_QUIC_PATH_RETRIES) {
|
||||
rc = ngx_quic_send_path_mtu_probe(c, path);
|
||||
@@ -921,11 +922,13 @@ ngx_quic_send_path_mtu_probe(ngx_connection_t *c, ngx_quic_path_t *path)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_PING;
|
||||
frame->ignore_loss = 1;
|
||||
frame->ignore_congestion = 1;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
|
||||
pnum = ctx->pnum;
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
|
||||
@@ -35,8 +35,6 @@ typedef struct {
|
||||
ngx_str_t payload;
|
||||
uint64_t number;
|
||||
ngx_quic_compat_keys_t *keys;
|
||||
|
||||
enum ssl_encryption_level_t level;
|
||||
} ngx_quic_compat_record_t;
|
||||
|
||||
|
||||
@@ -435,11 +433,10 @@ ngx_quic_compat_message_callback(int write_p, int version, int content_type,
|
||||
|
||||
case SSL3_RT_HANDSHAKE:
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic compat tx %s len:%uz ",
|
||||
ngx_quic_level_name(level), len);
|
||||
"quic compat tx level:%d len:%uz", level, len);
|
||||
|
||||
if (com->method->add_handshake_data(ssl, level, buf, len) != 1) {
|
||||
goto failed;
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -449,11 +446,11 @@ ngx_quic_compat_message_callback(int write_p, int version, int content_type,
|
||||
alert = ((u_char *) buf)[1];
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic compat %s alert:%ui len:%uz ",
|
||||
ngx_quic_level_name(level), alert, len);
|
||||
"quic compat level:%d alert:%ui len:%uz",
|
||||
level, alert, len);
|
||||
|
||||
if (com->method->send_alert(ssl, level, alert) != 1) {
|
||||
goto failed;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,10 +458,6 @@ ngx_quic_compat_message_callback(int write_p, int version, int content_type,
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
failed:
|
||||
|
||||
ngx_post_event(&qc->close, &ngx_posted_events);
|
||||
}
|
||||
|
||||
|
||||
@@ -487,8 +480,8 @@ SSL_provide_quic_data(SSL *ssl, enum ssl_encryption_level_t level,
|
||||
|
||||
c = ngx_ssl_get_connection(ssl);
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic compat rx %s len:%uz",
|
||||
ngx_quic_level_name(level), len);
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic compat rx level:%d len:%uz", level, len);
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
com = qc->compat;
|
||||
@@ -501,7 +494,6 @@ SSL_provide_quic_data(SSL *ssl, enum ssl_encryption_level_t level,
|
||||
rec.log = c->log;
|
||||
rec.number = com->read_record++;
|
||||
rec.keys = &com->keys;
|
||||
rec.level = level;
|
||||
|
||||
if (level == ssl_encryption_initial) {
|
||||
n = ngx_min(len, 65535);
|
||||
|
||||
@@ -7,11 +7,6 @@
|
||||
#ifndef _NGX_EVENT_QUIC_OPENSSL_COMPAT_H_INCLUDED_
|
||||
#define _NGX_EVENT_QUIC_OPENSSL_COMPAT_H_INCLUDED_
|
||||
|
||||
#if defined SSL_R_MISSING_QUIC_TRANSPORT_PARAMETERS_EXTENSION \
|
||||
|| defined LIBRESSL_VERSION_NUMBER
|
||||
#undef NGX_QUIC_OPENSSL_COMPAT
|
||||
#else
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
@@ -53,7 +48,4 @@ int SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params,
|
||||
void SSL_get_peer_quic_transport_params(const SSL *ssl,
|
||||
const uint8_t **out_params, size_t *out_params_len);
|
||||
|
||||
|
||||
#endif /* TLSEXT_TYPE_quic_transport_parameters */
|
||||
|
||||
#endif /* _NGX_EVENT_QUIC_OPENSSL_COMPAT_H_INCLUDED_ */
|
||||
@@ -45,9 +45,9 @@
|
||||
|
||||
|
||||
static ngx_int_t ngx_quic_create_datagrams(ngx_connection_t *c);
|
||||
static void ngx_quic_commit_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx);
|
||||
static void ngx_quic_revert_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
uint64_t pnum);
|
||||
static void ngx_quic_commit_send(ngx_connection_t *c);
|
||||
static void ngx_quic_revert_send(ngx_connection_t *c,
|
||||
uint64_t preserved_pnum[NGX_QUIC_SEND_CTX_LAST]);
|
||||
#if ((NGX_HAVE_UDP_SEGMENT) && (NGX_HAVE_MSGHDR_MSG_CONTROL))
|
||||
static ngx_uint_t ngx_quic_allow_segmentation(ngx_connection_t *c);
|
||||
static ngx_int_t ngx_quic_create_segments(ngx_connection_t *c);
|
||||
@@ -55,7 +55,8 @@ static ssize_t ngx_quic_send_segments(ngx_connection_t *c, u_char *buf,
|
||||
size_t len, struct sockaddr *sockaddr, socklen_t socklen, size_t segment);
|
||||
#endif
|
||||
static ssize_t ngx_quic_output_packet(ngx_connection_t *c,
|
||||
ngx_quic_send_ctx_t *ctx, u_char *data, size_t max, size_t min);
|
||||
ngx_quic_send_ctx_t *ctx, u_char *data, size_t max, size_t min,
|
||||
ngx_uint_t ack_only);
|
||||
static void ngx_quic_init_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
ngx_quic_header_t *pkt, ngx_quic_path_t *path);
|
||||
static ngx_uint_t ngx_quic_get_padding_level(ngx_connection_t *c);
|
||||
@@ -127,8 +128,11 @@ ngx_quic_create_datagrams(ngx_connection_t *c)
|
||||
cg = &qc->congestion;
|
||||
path = qc->path;
|
||||
|
||||
while (cg->in_flight < cg->window) {
|
||||
#if (NGX_SUPPRESS_WARN)
|
||||
ngx_memzero(preserved_pnum, sizeof(preserved_pnum));
|
||||
#endif
|
||||
|
||||
do {
|
||||
p = dst;
|
||||
|
||||
len = ngx_quic_path_limit(c, path, path->mtu);
|
||||
@@ -150,16 +154,12 @@ ngx_quic_create_datagrams(ngx_connection_t *c)
|
||||
|
||||
if (min > len) {
|
||||
/* padding can't be applied - avoid sending the packet */
|
||||
|
||||
while (i-- > 0) {
|
||||
ctx = &qc->send_ctx[i];
|
||||
ngx_quic_revert_send(c, ctx, preserved_pnum[i]);
|
||||
}
|
||||
|
||||
ngx_quic_revert_send(c, preserved_pnum);
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
n = ngx_quic_output_packet(c, ctx, p, len, min);
|
||||
n = ngx_quic_output_packet(c, ctx, p, len, min,
|
||||
cg->in_flight >= cg->window);
|
||||
if (n == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
@@ -180,73 +180,95 @@ ngx_quic_create_datagrams(ngx_connection_t *c)
|
||||
}
|
||||
|
||||
if (n == NGX_AGAIN) {
|
||||
for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
|
||||
ngx_quic_revert_send(c, &qc->send_ctx[i], preserved_pnum[i]);
|
||||
}
|
||||
|
||||
ngx_quic_revert_send(c, preserved_pnum);
|
||||
ngx_add_timer(&qc->push, NGX_QUIC_SOCKET_RETRY_DELAY);
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
|
||||
ngx_quic_commit_send(c, &qc->send_ctx[i]);
|
||||
}
|
||||
ngx_quic_commit_send(c);
|
||||
|
||||
path->sent += len;
|
||||
}
|
||||
|
||||
} while (cg->in_flight < cg->window);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_quic_commit_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
|
||||
ngx_quic_commit_send(ngx_connection_t *c)
|
||||
{
|
||||
ngx_uint_t i, idle;
|
||||
ngx_queue_t *q;
|
||||
ngx_quic_frame_t *f;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_congestion_t *cg;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
|
||||
cg = &qc->congestion;
|
||||
|
||||
while (!ngx_queue_empty(&ctx->sending)) {
|
||||
idle = 1;
|
||||
|
||||
q = ngx_queue_head(&ctx->sending);
|
||||
f = ngx_queue_data(q, ngx_quic_frame_t, queue);
|
||||
for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
|
||||
ctx = &qc->send_ctx[i];
|
||||
|
||||
ngx_queue_remove(q);
|
||||
if (!ngx_queue_empty(&ctx->frames)) {
|
||||
idle = 0;
|
||||
}
|
||||
|
||||
if (f->pkt_need_ack && !qc->closing) {
|
||||
ngx_queue_insert_tail(&ctx->sent, q);
|
||||
while (!ngx_queue_empty(&ctx->sending)) {
|
||||
|
||||
cg->in_flight += f->plen;
|
||||
q = ngx_queue_head(&ctx->sending);
|
||||
f = ngx_queue_data(q, ngx_quic_frame_t, queue);
|
||||
|
||||
} else {
|
||||
ngx_quic_free_frame(c, f);
|
||||
ngx_queue_remove(q);
|
||||
|
||||
if (f->pkt_need_ack && !qc->closing) {
|
||||
ngx_queue_insert_tail(&ctx->sent, q);
|
||||
|
||||
cg->in_flight += f->plen;
|
||||
|
||||
} else {
|
||||
ngx_quic_free_frame(c, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion send if:%uz", cg->in_flight);
|
||||
|
||||
ngx_quic_congestion_idle(c, idle);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_quic_revert_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
uint64_t pnum)
|
||||
ngx_quic_revert_send(ngx_connection_t *c, uint64_t pnum[NGX_QUIC_SEND_CTX_LAST])
|
||||
{
|
||||
ngx_queue_t *q;
|
||||
ngx_uint_t i;
|
||||
ngx_queue_t *q;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
while (!ngx_queue_empty(&ctx->sending)) {
|
||||
qc = ngx_quic_get_connection(c);
|
||||
|
||||
q = ngx_queue_last(&ctx->sending);
|
||||
ngx_queue_remove(q);
|
||||
ngx_queue_insert_head(&ctx->frames, q);
|
||||
for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
|
||||
ctx = &qc->send_ctx[i];
|
||||
|
||||
if (ngx_queue_empty(&ctx->sending)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
do {
|
||||
q = ngx_queue_last(&ctx->sending);
|
||||
ngx_queue_remove(q);
|
||||
ngx_queue_insert_head(&ctx->frames, q);
|
||||
} while (!ngx_queue_empty(&ctx->sending));
|
||||
|
||||
ctx->pnum = pnum[i];
|
||||
}
|
||||
|
||||
ctx->pnum = pnum;
|
||||
ngx_quic_congestion_idle(c, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -272,17 +294,17 @@ ngx_quic_allow_segmentation(ngx_connection_t *c)
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_initial);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_INITIAL);
|
||||
if (!ngx_queue_empty(&ctx->frames)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_handshake);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_HANDSHAKE);
|
||||
if (!ngx_queue_empty(&ctx->frames)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
|
||||
|
||||
bytes = 0;
|
||||
len = ngx_min(qc->path->mtu, NGX_QUIC_MAX_UDP_SEGMENT_BUF);
|
||||
@@ -295,6 +317,10 @@ ngx_quic_allow_segmentation(ngx_connection_t *c)
|
||||
|
||||
bytes += f->len;
|
||||
|
||||
if (qc->congestion.in_flight + bytes >= qc->congestion.window) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bytes > len * 3) {
|
||||
/* require at least ~3 full packets to batch */
|
||||
return 1;
|
||||
@@ -311,19 +337,19 @@ ngx_quic_create_segments(ngx_connection_t *c)
|
||||
size_t len, segsize;
|
||||
ssize_t n;
|
||||
u_char *p, *end;
|
||||
uint64_t preserved_pnum;
|
||||
ngx_uint_t nseg;
|
||||
ngx_uint_t nseg, level;
|
||||
ngx_quic_path_t *path;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_congestion_t *cg;
|
||||
ngx_quic_connection_t *qc;
|
||||
static u_char dst[NGX_QUIC_MAX_UDP_SEGMENT_BUF];
|
||||
static uint64_t preserved_pnum[NGX_QUIC_SEND_CTX_LAST];
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
cg = &qc->congestion;
|
||||
path = qc->path;
|
||||
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
|
||||
|
||||
if (ngx_quic_generate_ack(c, ctx) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
@@ -335,7 +361,8 @@ ngx_quic_create_segments(ngx_connection_t *c)
|
||||
|
||||
nseg = 0;
|
||||
|
||||
preserved_pnum = ctx->pnum;
|
||||
level = ctx - qc->send_ctx;
|
||||
preserved_pnum[level] = ctx->pnum;
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
@@ -343,7 +370,7 @@ ngx_quic_create_segments(ngx_connection_t *c)
|
||||
|
||||
if (len && cg->in_flight + (p - dst) < cg->window) {
|
||||
|
||||
n = ngx_quic_output_packet(c, ctx, p, len, len);
|
||||
n = ngx_quic_output_packet(c, ctx, p, len, len, 0);
|
||||
if (n == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
@@ -369,19 +396,18 @@ ngx_quic_create_segments(ngx_connection_t *c)
|
||||
}
|
||||
|
||||
if (n == NGX_AGAIN) {
|
||||
ngx_quic_revert_send(c, ctx, preserved_pnum);
|
||||
|
||||
ngx_quic_revert_send(c, preserved_pnum);
|
||||
ngx_add_timer(&qc->push, NGX_QUIC_SOCKET_RETRY_DELAY);
|
||||
break;
|
||||
}
|
||||
|
||||
ngx_quic_commit_send(c, ctx);
|
||||
ngx_quic_commit_send(c);
|
||||
|
||||
path->sent += n;
|
||||
|
||||
p = dst;
|
||||
nseg = 0;
|
||||
preserved_pnum = ctx->pnum;
|
||||
preserved_pnum[level] = ctx->pnum;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,7 +500,7 @@ ngx_quic_get_padding_level(ngx_connection_t *c)
|
||||
*/
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_initial);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_INITIAL);
|
||||
|
||||
for (q = ngx_queue_head(&ctx->frames);
|
||||
q != ngx_queue_sentinel(&ctx->frames);
|
||||
@@ -501,7 +527,7 @@ ngx_quic_get_padding_level(ngx_connection_t *c)
|
||||
|
||||
static ssize_t
|
||||
ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
u_char *data, size_t max, size_t min)
|
||||
u_char *data, size_t max, size_t min, ngx_uint_t ack_only)
|
||||
{
|
||||
size_t len, pad, min_payload, max_payload;
|
||||
u_char *p;
|
||||
@@ -565,6 +591,10 @@ ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
{
|
||||
f = ngx_queue_data(q, ngx_quic_frame_t, queue);
|
||||
|
||||
if (ack_only && f->type != NGX_QUIC_FT_ACK) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (len >= max_payload) {
|
||||
break;
|
||||
}
|
||||
@@ -657,10 +687,10 @@ ngx_quic_init_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
|
||||
pkt->flags = NGX_QUIC_PKT_FIXED_BIT;
|
||||
|
||||
if (ctx->level == ssl_encryption_initial) {
|
||||
if (ctx->level == NGX_QUIC_ENCRYPTION_INITIAL) {
|
||||
pkt->flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_INITIAL;
|
||||
|
||||
} else if (ctx->level == ssl_encryption_handshake) {
|
||||
} else if (ctx->level == NGX_QUIC_ENCRYPTION_HANDSHAKE) {
|
||||
pkt->flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_HANDSHAKE;
|
||||
|
||||
} else {
|
||||
@@ -1073,7 +1103,7 @@ ngx_quic_send_new_token(ngx_connection_t *c, ngx_quic_path_t *path)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_NEW_TOKEN;
|
||||
frame->data = out;
|
||||
frame->u.token.length = token.len;
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_event.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 */
|
||||
@@ -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,
|
||||
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,
|
||||
const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log);
|
||||
#endif
|
||||
@@ -58,7 +64,7 @@ ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers)
|
||||
switch (id) {
|
||||
|
||||
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();
|
||||
#else
|
||||
ciphers->c = EVP_aes_128_gcm();
|
||||
@@ -69,7 +75,7 @@ ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers)
|
||||
break;
|
||||
|
||||
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();
|
||||
#else
|
||||
ciphers->c = EVP_aes_256_gcm();
|
||||
@@ -80,12 +86,12 @@ ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers)
|
||||
break;
|
||||
|
||||
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#if (NGX_QUIC_BORINGSSL_EVP_API)
|
||||
ciphers->c = EVP_aead_chacha20_poly1305();
|
||||
#else
|
||||
ciphers->c = EVP_chacha20_poly1305();
|
||||
#endif
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#if (NGX_QUIC_BORINGSSL_EVP_API)
|
||||
ciphers->hp = (const EVP_CIPHER *) EVP_aead_chacha20_poly1305();
|
||||
#else
|
||||
ciphers->hp = EVP_chacha20();
|
||||
@@ -94,7 +100,7 @@ ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers)
|
||||
len = 32;
|
||||
break;
|
||||
|
||||
#ifndef OPENSSL_IS_BORINGSSL
|
||||
#if !(NGX_QUIC_BORINGSSL_EVP_API)
|
||||
case TLS1_3_CK_AES_128_CCM_SHA256:
|
||||
ciphers->c = EVP_aes_128_ccm();
|
||||
ciphers->hp = EVP_aes_128_ctr();
|
||||
@@ -125,12 +131,13 @@ ngx_quic_keys_set_initial_secret(ngx_quic_keys_t *keys, ngx_str_t *secret,
|
||||
ngx_quic_secret_t *client, *server;
|
||||
ngx_quic_ciphers_t ciphers;
|
||||
|
||||
static const uint8_t salt[20] =
|
||||
"\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17"
|
||||
"\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb\x7f\x0a";
|
||||
static const uint8_t salt[20] = {
|
||||
0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17,
|
||||
0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a
|
||||
};
|
||||
|
||||
client = &keys->secrets[ssl_encryption_initial].client;
|
||||
server = &keys->secrets[ssl_encryption_initial].server;
|
||||
client = &keys->secrets[NGX_QUIC_ENCRYPTION_INITIAL].client;
|
||||
server = &keys->secrets[NGX_QUIC_ENCRYPTION_INITIAL].server;
|
||||
|
||||
/*
|
||||
* RFC 9001, section 5. Packet Protection
|
||||
@@ -262,7 +269,7 @@ static ngx_int_t
|
||||
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)
|
||||
{
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#if (NGX_QUIC_BORINGSSL_EVP_API)
|
||||
|
||||
if (HKDF_expand(out_key, out_len, digest, prk, prk_len, info, info_len)
|
||||
== 0)
|
||||
@@ -324,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,
|
||||
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,
|
||||
salt_len)
|
||||
@@ -387,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)
|
||||
{
|
||||
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#if (NGX_QUIC_BORINGSSL_EVP_API)
|
||||
EVP_AEAD_CTX *ctx;
|
||||
|
||||
ctx = EVP_AEAD_CTX_new(cipher, key->data, key->len,
|
||||
@@ -447,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_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,
|
||||
s->iv.len, in->data, in->len, ad->data, ad->len)
|
||||
!= 1)
|
||||
@@ -467,7 +474,7 @@ ngx_int_t
|
||||
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)
|
||||
{
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#if (NGX_QUIC_BORINGSSL_EVP_API)
|
||||
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)
|
||||
!= 1)
|
||||
@@ -483,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
|
||||
ngx_quic_crypto_common(ngx_quic_secret_t *s, ngx_str_t *out,
|
||||
@@ -562,7 +569,7 @@ void
|
||||
ngx_quic_crypto_cleanup(ngx_quic_secret_t *s)
|
||||
{
|
||||
if (s->ctx) {
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#if (NGX_QUIC_BORINGSSL_EVP_API)
|
||||
EVP_AEAD_CTX_free(s->ctx);
|
||||
#else
|
||||
EVP_CIPHER_CTX_free(s->ctx);
|
||||
@@ -578,7 +585,7 @@ ngx_quic_crypto_hp_init(const EVP_CIPHER *cipher, ngx_quic_secret_t *s,
|
||||
{
|
||||
EVP_CIPHER_CTX *ctx;
|
||||
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#if (NGX_QUIC_BORINGSSL_EVP_API)
|
||||
if (cipher == (EVP_CIPHER *) EVP_aead_chacha20_poly1305()) {
|
||||
/* no EVP interface */
|
||||
s->hp_ctx = NULL;
|
||||
@@ -614,7 +621,7 @@ ngx_quic_crypto_hp(ngx_quic_secret_t *s, u_char *out, u_char *in,
|
||||
|
||||
ctx = s->hp_ctx;
|
||||
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#if (NGX_QUIC_BORINGSSL_EVP_API)
|
||||
uint32_t cnt;
|
||||
|
||||
if (ctx == NULL) {
|
||||
@@ -655,8 +662,8 @@ ngx_quic_crypto_hp_cleanup(ngx_quic_secret_t *s)
|
||||
|
||||
ngx_int_t
|
||||
ngx_quic_keys_set_encryption_secret(ngx_log_t *log, ngx_uint_t is_write,
|
||||
ngx_quic_keys_t *keys, enum ssl_encryption_level_t level,
|
||||
const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len)
|
||||
ngx_quic_keys_t *keys, ngx_uint_t level, const SSL_CIPHER *cipher,
|
||||
const uint8_t *secret, size_t secret_len)
|
||||
{
|
||||
ngx_int_t key_len;
|
||||
ngx_str_t secret_str;
|
||||
@@ -721,8 +728,8 @@ ngx_quic_keys_set_encryption_secret(ngx_log_t *log, ngx_uint_t is_write,
|
||||
|
||||
|
||||
ngx_uint_t
|
||||
ngx_quic_keys_available(ngx_quic_keys_t *keys,
|
||||
enum ssl_encryption_level_t level, ngx_uint_t is_write)
|
||||
ngx_quic_keys_available(ngx_quic_keys_t *keys, ngx_uint_t level,
|
||||
ngx_uint_t is_write)
|
||||
{
|
||||
if (is_write == 0) {
|
||||
return keys->secrets[level].client.ctx != NULL;
|
||||
@@ -733,8 +740,7 @@ ngx_quic_keys_available(ngx_quic_keys_t *keys,
|
||||
|
||||
|
||||
void
|
||||
ngx_quic_keys_discard(ngx_quic_keys_t *keys,
|
||||
enum ssl_encryption_level_t level)
|
||||
ngx_quic_keys_discard(ngx_quic_keys_t *keys, ngx_uint_t level)
|
||||
{
|
||||
ngx_quic_secret_t *client, *server;
|
||||
|
||||
@@ -764,7 +770,7 @@ ngx_quic_keys_switch(ngx_connection_t *c, ngx_quic_keys_t *keys)
|
||||
{
|
||||
ngx_quic_secrets_t *current, *next, tmp;
|
||||
|
||||
current = &keys->secrets[ssl_encryption_application];
|
||||
current = &keys->secrets[NGX_QUIC_ENCRYPTION_APPLICATION];
|
||||
next = &keys->next_key;
|
||||
|
||||
ngx_quic_crypto_cleanup(¤t->client);
|
||||
@@ -793,7 +799,7 @@ ngx_quic_keys_update(ngx_event_t *ev)
|
||||
qc = ngx_quic_get_connection(c);
|
||||
keys = qc->keys;
|
||||
|
||||
current = &keys->secrets[ssl_encryption_application];
|
||||
current = &keys->secrets[NGX_QUIC_ENCRYPTION_APPLICATION];
|
||||
next = &keys->next_key;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic key update");
|
||||
@@ -958,8 +964,9 @@ ngx_quic_create_retry_packet(ngx_quic_header_t *pkt, ngx_str_t *res)
|
||||
/* 5.8. Retry Packet Integrity */
|
||||
static ngx_quic_md_t key = ngx_quic_md(
|
||||
"\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e");
|
||||
static const u_char nonce[NGX_QUIC_IV_LEN] =
|
||||
"\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb";
|
||||
static const u_char nonce[NGX_QUIC_IV_LEN] = {
|
||||
0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2, 0x23, 0x98, 0x25, 0xbb
|
||||
};
|
||||
static ngx_str_t in = ngx_string("");
|
||||
|
||||
ad.data = res->data;
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
#include <ngx_event_quic_transport.h>
|
||||
|
||||
|
||||
#define NGX_QUIC_ENCRYPTION_LAST ((ssl_encryption_application) + 1)
|
||||
|
||||
/* RFC 5116, 5.1/5.3 and RFC 8439, 2.3/2.5 for all supported ciphers */
|
||||
#define NGX_QUIC_IV_LEN 12
|
||||
#define NGX_QUIC_TAG_LEN 16
|
||||
@@ -24,10 +22,12 @@
|
||||
#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_crypto_ctx_t EVP_AEAD_CTX
|
||||
#else
|
||||
#define NGX_QUIC_BORINGSSL_EVP_API 0
|
||||
#define ngx_quic_cipher_t EVP_CIPHER
|
||||
#define ngx_quic_crypto_ctx_t EVP_CIPHER_CTX
|
||||
#endif
|
||||
@@ -94,13 +94,11 @@ typedef struct {
|
||||
ngx_int_t ngx_quic_keys_set_initial_secret(ngx_quic_keys_t *keys,
|
||||
ngx_str_t *secret, ngx_log_t *log);
|
||||
ngx_int_t ngx_quic_keys_set_encryption_secret(ngx_log_t *log,
|
||||
ngx_uint_t is_write, ngx_quic_keys_t *keys,
|
||||
enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
|
||||
const uint8_t *secret, size_t secret_len);
|
||||
ngx_uint_t ngx_quic_keys_available(ngx_quic_keys_t *keys,
|
||||
enum ssl_encryption_level_t level, ngx_uint_t is_write);
|
||||
void ngx_quic_keys_discard(ngx_quic_keys_t *keys,
|
||||
enum ssl_encryption_level_t level);
|
||||
ngx_uint_t is_write, ngx_quic_keys_t *keys, ngx_uint_t level,
|
||||
const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len);
|
||||
ngx_uint_t ngx_quic_keys_available(ngx_quic_keys_t *keys, ngx_uint_t level,
|
||||
ngx_uint_t is_write);
|
||||
void ngx_quic_keys_discard(ngx_quic_keys_t *keys, ngx_uint_t level);
|
||||
void ngx_quic_keys_switch(ngx_connection_t *c, ngx_quic_keys_t *keys);
|
||||
void ngx_quic_keys_update(ngx_event_t *ev);
|
||||
void ngx_quic_keys_cleanup(ngx_quic_keys_t *keys);
|
||||
|
||||
+522
-126
@@ -10,13 +10,6 @@
|
||||
#include <ngx_event_quic_connection.h>
|
||||
|
||||
|
||||
#if defined OPENSSL_IS_BORINGSSL \
|
||||
|| defined LIBRESSL_VERSION_NUMBER \
|
||||
|| NGX_QUIC_OPENSSL_COMPAT
|
||||
#define NGX_QUIC_BORINGSSL_API 1
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* RFC 9000, 7.5. Cryptographic Message Buffering
|
||||
*
|
||||
@@ -25,43 +18,349 @@
|
||||
#define NGX_QUIC_MAX_BUFFERED 65535
|
||||
|
||||
|
||||
#if (NGX_QUIC_OPENSSL_API)
|
||||
|
||||
static int ngx_quic_cbs_send(ngx_ssl_conn_t *ssl_conn,
|
||||
const unsigned char *data, size_t len, size_t *consumed, void *arg);
|
||||
static int ngx_quic_cbs_recv_rcd(ngx_ssl_conn_t *ssl_conn,
|
||||
const unsigned char **data, size_t *bytes_read, void *arg);
|
||||
static int ngx_quic_cbs_release_rcd(ngx_ssl_conn_t *ssl_conn,
|
||||
size_t bytes_read, void *arg);
|
||||
static int ngx_quic_cbs_yield_secret(ngx_ssl_conn_t *ssl_conn, uint32_t level,
|
||||
int direction, const unsigned char *secret, size_t secret_len, void *arg);
|
||||
static int ngx_quic_cbs_got_transport_params(ngx_ssl_conn_t *ssl_conn,
|
||||
const unsigned char *params, size_t params_len, void *arg);
|
||||
static int ngx_quic_cbs_alert(ngx_ssl_conn_t *ssl_conn, unsigned char alert,
|
||||
void *arg);
|
||||
|
||||
#else /* NGX_QUIC_BORINGSSL_API || NGX_QUIC_QUICTLS_API */
|
||||
|
||||
static ngx_inline ngx_uint_t ngx_quic_map_encryption_level(
|
||||
enum ssl_encryption_level_t ssl_level);
|
||||
|
||||
#if (NGX_QUIC_BORINGSSL_API)
|
||||
static int ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
|
||||
enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
|
||||
enum ssl_encryption_level_t ssl_level, const SSL_CIPHER *cipher,
|
||||
const uint8_t *secret, size_t secret_len);
|
||||
static int ngx_quic_set_write_secret(ngx_ssl_conn_t *ssl_conn,
|
||||
enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
|
||||
enum ssl_encryption_level_t ssl_level, const SSL_CIPHER *cipher,
|
||||
const uint8_t *secret, size_t secret_len);
|
||||
#else
|
||||
#else /* NGX_QUIC_QUICTLS_API */
|
||||
static int ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
|
||||
enum ssl_encryption_level_t level, const uint8_t *read_secret,
|
||||
enum ssl_encryption_level_t ssl_level, const uint8_t *read_secret,
|
||||
const uint8_t *write_secret, size_t secret_len);
|
||||
#endif
|
||||
|
||||
static int ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
|
||||
enum ssl_encryption_level_t level, const uint8_t *data, size_t len);
|
||||
enum ssl_encryption_level_t ssl_level, const uint8_t *data, size_t len);
|
||||
static int ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn);
|
||||
static int ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn,
|
||||
enum ssl_encryption_level_t level, uint8_t alert);
|
||||
static ngx_int_t ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
|
||||
enum ssl_encryption_level_t level);
|
||||
enum ssl_encryption_level_t ssl_level, uint8_t alert);
|
||||
|
||||
#endif
|
||||
|
||||
static ngx_int_t ngx_quic_handshake(ngx_connection_t *c);
|
||||
static ngx_int_t ngx_quic_crypto_provide(ngx_connection_t *c, ngx_uint_t level);
|
||||
|
||||
|
||||
#if (NGX_QUIC_OPENSSL_API)
|
||||
|
||||
static int
|
||||
ngx_quic_cbs_send(ngx_ssl_conn_t *ssl_conn,
|
||||
const unsigned char *data, size_t len, size_t *consumed, void *arg)
|
||||
{
|
||||
ngx_connection_t *c = arg;
|
||||
|
||||
ngx_chain_t *out;
|
||||
unsigned int alpn_len;
|
||||
ngx_quic_frame_t *frame;
|
||||
const unsigned char *alpn_data;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_cbs_send len:%uz", len);
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
|
||||
*consumed = 0;
|
||||
|
||||
SSL_get0_alpn_selected(ssl_conn, &alpn_data, &alpn_len);
|
||||
|
||||
if (alpn_len == 0) {
|
||||
qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_NO_APPLICATION_PROTOCOL);
|
||||
qc->error_reason = "missing ALPN extension";
|
||||
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"quic missing ALPN extension");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!qc->client_tp_done) {
|
||||
/* RFC 9001, 8.2. QUIC Transport Parameters Extension */
|
||||
qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_MISSING_EXTENSION);
|
||||
qc->error_reason = "missing transport parameters";
|
||||
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"missing transport parameters");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ctx = ngx_quic_get_send_ctx(qc, qc->write_level);
|
||||
|
||||
out = ngx_quic_copy_buffer(c, (u_char *) data, len);
|
||||
if (out == NGX_CHAIN_ERROR) {
|
||||
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
frame = ngx_quic_alloc_frame(c);
|
||||
if (frame == NULL) {
|
||||
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
frame->data = out;
|
||||
frame->level = qc->write_level;
|
||||
frame->type = NGX_QUIC_FT_CRYPTO;
|
||||
frame->u.crypto.offset = ctx->crypto_sent;
|
||||
frame->u.crypto.length = len;
|
||||
|
||||
ctx->crypto_sent += len;
|
||||
*consumed = len;
|
||||
|
||||
ngx_quic_queue_frame(qc, frame);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ngx_quic_cbs_recv_rcd(ngx_ssl_conn_t *ssl_conn,
|
||||
const unsigned char **data, size_t *bytes_read, void *arg)
|
||||
{
|
||||
ngx_connection_t *c = arg;
|
||||
|
||||
ngx_buf_t *b;
|
||||
ngx_chain_t *cl;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_cbs_recv_rcd");
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
ctx = ngx_quic_get_send_ctx(qc, qc->read_level);
|
||||
|
||||
for (cl = ctx->crypto.chain; cl; cl = cl->next) {
|
||||
b = cl->buf;
|
||||
|
||||
if (b->sync) {
|
||||
/* hole */
|
||||
|
||||
*bytes_read = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
*data = b->pos;
|
||||
*bytes_read = b->last - b->pos;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ngx_quic_cbs_release_rcd(ngx_ssl_conn_t *ssl_conn, size_t bytes_read, void *arg)
|
||||
{
|
||||
ngx_connection_t *c = arg;
|
||||
|
||||
ngx_chain_t *cl;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_cbs_release_rcd len:%uz", bytes_read);
|
||||
|
||||
/* already closed on handshake failure */
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
if (qc == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
ctx = ngx_quic_get_send_ctx(qc, qc->read_level);
|
||||
|
||||
cl = ngx_quic_read_buffer(c, &ctx->crypto, bytes_read);
|
||||
if (cl == NGX_CHAIN_ERROR) {
|
||||
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ngx_quic_free_chain(c, cl);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ngx_quic_cbs_yield_secret(ngx_ssl_conn_t *ssl_conn, uint32_t ssl_level,
|
||||
int direction, const unsigned char *secret, size_t secret_len, void *arg)
|
||||
{
|
||||
ngx_connection_t *c = arg;
|
||||
|
||||
ngx_uint_t level;
|
||||
const SSL_CIPHER *cipher;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_cbs_yield_secret() level:%uD", ssl_level);
|
||||
#ifdef NGX_QUIC_DEBUG_CRYPTO
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic %s secret len:%uz %*xs",
|
||||
direction ? "write" : "read", secret_len,
|
||||
secret_len, secret);
|
||||
#endif
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
cipher = SSL_get_current_cipher(ssl_conn);
|
||||
|
||||
switch (ssl_level) {
|
||||
case OSSL_RECORD_PROTECTION_LEVEL_NONE:
|
||||
level = NGX_QUIC_ENCRYPTION_INITIAL;
|
||||
break;
|
||||
case OSSL_RECORD_PROTECTION_LEVEL_EARLY:
|
||||
level = NGX_QUIC_ENCRYPTION_EARLY_DATA;
|
||||
break;
|
||||
case OSSL_RECORD_PROTECTION_LEVEL_HANDSHAKE:
|
||||
level = NGX_QUIC_ENCRYPTION_HANDSHAKE;
|
||||
break;
|
||||
default: /* OSSL_RECORD_PROTECTION_LEVEL_APPLICATION */
|
||||
level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ngx_quic_keys_set_encryption_secret(c->log, direction, qc->keys, level,
|
||||
cipher, secret, secret_len)
|
||||
!= NGX_OK)
|
||||
{
|
||||
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (direction) {
|
||||
qc->write_level = level;
|
||||
|
||||
} else {
|
||||
qc->read_level = level;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ngx_quic_cbs_got_transport_params(ngx_ssl_conn_t *ssl_conn,
|
||||
const unsigned char *params, size_t params_len, void *arg)
|
||||
{
|
||||
ngx_connection_t *c = arg;
|
||||
|
||||
u_char *p, *end;
|
||||
ngx_quic_tp_t ctp;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_cbs_got_transport_params() len:%uz",
|
||||
params_len);
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
|
||||
/* defaults for parameters not sent by client */
|
||||
ngx_memcpy(&ctp, &qc->ctp, sizeof(ngx_quic_tp_t));
|
||||
|
||||
p = (u_char *) params;
|
||||
end = p + params_len;
|
||||
|
||||
if (ngx_quic_parse_transport_params(p, end, &ctp, c->log) != NGX_OK) {
|
||||
qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR;
|
||||
qc->error_reason = "failed to process transport parameters";
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ngx_quic_apply_transport_params(c, &ctp) != NGX_OK) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
qc->client_tp_done = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ngx_quic_cbs_alert(ngx_ssl_conn_t *ssl_conn, unsigned char alert, void *arg)
|
||||
{
|
||||
ngx_connection_t *c = arg;
|
||||
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_cbs_alert() alert:%d", (int) alert);
|
||||
|
||||
/* already closed on regular shutdown */
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
if (qc == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
qc->error = NGX_QUIC_ERR_CRYPTO(alert);
|
||||
qc->error_reason = "handshake failed";
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
#else /* NGX_QUIC_BORINGSSL_API || NGX_QUIC_QUICTLS_API */
|
||||
|
||||
|
||||
static ngx_inline ngx_uint_t
|
||||
ngx_quic_map_encryption_level(enum ssl_encryption_level_t ssl_level)
|
||||
{
|
||||
switch (ssl_level) {
|
||||
case ssl_encryption_initial:
|
||||
return NGX_QUIC_ENCRYPTION_INITIAL;
|
||||
case ssl_encryption_early_data:
|
||||
return NGX_QUIC_ENCRYPTION_EARLY_DATA;
|
||||
case ssl_encryption_handshake:
|
||||
return NGX_QUIC_ENCRYPTION_HANDSHAKE;
|
||||
default: /* ssl_encryption_application */
|
||||
return NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if (NGX_QUIC_BORINGSSL_API)
|
||||
|
||||
static int
|
||||
ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
|
||||
enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
|
||||
enum ssl_encryption_level_t ssl_level, const SSL_CIPHER *cipher,
|
||||
const uint8_t *rsecret, size_t secret_len)
|
||||
{
|
||||
ngx_uint_t level;
|
||||
ngx_connection_t *c;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
|
||||
c = ngx_ssl_get_connection(ssl_conn);
|
||||
qc = ngx_quic_get_connection(c);
|
||||
level = ngx_quic_map_encryption_level(ssl_level);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_set_read_secret() level:%d", level);
|
||||
"quic ngx_quic_set_read_secret() level:%d", ssl_level);
|
||||
#ifdef NGX_QUIC_DEBUG_CRYPTO
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic read secret len:%uz %*xs", secret_len,
|
||||
@@ -72,7 +371,7 @@ ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
|
||||
cipher, rsecret, secret_len)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return 0;
|
||||
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -81,17 +380,19 @@ ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
|
||||
|
||||
static int
|
||||
ngx_quic_set_write_secret(ngx_ssl_conn_t *ssl_conn,
|
||||
enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
|
||||
enum ssl_encryption_level_t ssl_level, const SSL_CIPHER *cipher,
|
||||
const uint8_t *wsecret, size_t secret_len)
|
||||
{
|
||||
ngx_uint_t level;
|
||||
ngx_connection_t *c;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
|
||||
c = ngx_ssl_get_connection(ssl_conn);
|
||||
qc = ngx_quic_get_connection(c);
|
||||
level = ngx_quic_map_encryption_level(ssl_level);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_set_write_secret() level:%d", level);
|
||||
"quic ngx_quic_set_write_secret() level:%d", ssl_level);
|
||||
#ifdef NGX_QUIC_DEBUG_CRYPTO
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic write secret len:%uz %*xs", secret_len,
|
||||
@@ -102,28 +403,31 @@ ngx_quic_set_write_secret(ngx_ssl_conn_t *ssl_conn,
|
||||
cipher, wsecret, secret_len)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return 0;
|
||||
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#else
|
||||
#else /* NGX_QUIC_QUICTLS_API */
|
||||
|
||||
static int
|
||||
ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
|
||||
enum ssl_encryption_level_t level, const uint8_t *rsecret,
|
||||
enum ssl_encryption_level_t ssl_level, const uint8_t *rsecret,
|
||||
const uint8_t *wsecret, size_t secret_len)
|
||||
{
|
||||
ngx_uint_t level;
|
||||
ngx_connection_t *c;
|
||||
const SSL_CIPHER *cipher;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
|
||||
c = ngx_ssl_get_connection(ssl_conn);
|
||||
qc = ngx_quic_get_connection(c);
|
||||
level = ngx_quic_map_encryption_level(ssl_level);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_set_encryption_secrets() level:%d", level);
|
||||
"quic ngx_quic_set_encryption_secrets() level:%d",
|
||||
ssl_level);
|
||||
#ifdef NGX_QUIC_DEBUG_CRYPTO
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic read secret len:%uz %*xs", secret_len,
|
||||
@@ -136,10 +440,11 @@ ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
|
||||
cipher, rsecret, secret_len)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return 0;
|
||||
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (level == ssl_encryption_early_data) {
|
||||
if (level == NGX_QUIC_ENCRYPTION_EARLY_DATA) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -153,7 +458,7 @@ ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
|
||||
cipher, wsecret, secret_len)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return 0;
|
||||
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -164,24 +469,24 @@ ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
|
||||
|
||||
static int
|
||||
ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
|
||||
enum ssl_encryption_level_t level, const uint8_t *data, size_t len)
|
||||
enum ssl_encryption_level_t ssl_level, const uint8_t *data, size_t len)
|
||||
{
|
||||
u_char *p, *end;
|
||||
size_t client_params_len;
|
||||
ngx_uint_t level;
|
||||
ngx_chain_t *out;
|
||||
unsigned int alpn_len;
|
||||
const uint8_t *client_params;
|
||||
ngx_quic_tp_t ctp;
|
||||
ngx_quic_frame_t *frame;
|
||||
ngx_connection_t *c;
|
||||
const unsigned char *alpn_data;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_connection_t *qc;
|
||||
#if defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
|
||||
unsigned int alpn_len;
|
||||
const unsigned char *alpn_data;
|
||||
#endif
|
||||
|
||||
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
|
||||
c = ngx_ssl_get_connection(ssl_conn);
|
||||
qc = ngx_quic_get_connection(c);
|
||||
level = ngx_quic_map_encryption_level(ssl_level);
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_add_handshake_data");
|
||||
@@ -193,21 +498,20 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
|
||||
* here;
|
||||
*/
|
||||
|
||||
#if defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
|
||||
|
||||
SSL_get0_alpn_selected(ssl_conn, &alpn_data, &alpn_len);
|
||||
|
||||
if (alpn_len == 0) {
|
||||
qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_NO_APPLICATION_PROTOCOL);
|
||||
qc->error_reason = "unsupported protocol in ALPN extension";
|
||||
if (qc->error == 0) {
|
||||
qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_NO_APPLICATION_PROTOCOL);
|
||||
qc->error_reason = "missing ALPN extension";
|
||||
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"quic unsupported protocol in ALPN extension");
|
||||
return 0;
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"quic missing ALPN extension");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
SSL_get_peer_quic_transport_params(ssl_conn, &client_params,
|
||||
&client_params_len);
|
||||
|
||||
@@ -217,12 +521,16 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
|
||||
|
||||
if (client_params_len == 0) {
|
||||
/* RFC 9001, 8.2. QUIC Transport Parameters Extension */
|
||||
qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_MISSING_EXTENSION);
|
||||
qc->error_reason = "missing transport parameters";
|
||||
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"missing transport parameters");
|
||||
return 0;
|
||||
if (qc->error == 0) {
|
||||
qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_MISSING_EXTENSION);
|
||||
qc->error_reason = "missing transport parameters";
|
||||
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"missing transport parameters");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
p = (u_char *) client_params;
|
||||
@@ -237,11 +545,11 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
|
||||
qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR;
|
||||
qc->error_reason = "failed to process transport parameters";
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ngx_quic_apply_transport_params(c, &ctp) != NGX_OK) {
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
qc->client_tp_done = 1;
|
||||
@@ -251,12 +559,14 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
|
||||
|
||||
out = ngx_quic_copy_buffer(c, (u_char *) data, len);
|
||||
if (out == NGX_CHAIN_ERROR) {
|
||||
return 0;
|
||||
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
frame = ngx_quic_alloc_frame(c);
|
||||
if (frame == NULL) {
|
||||
return 0;
|
||||
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
frame->data = out;
|
||||
@@ -279,7 +589,7 @@ ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn)
|
||||
#if (NGX_DEBUG)
|
||||
ngx_connection_t *c;
|
||||
|
||||
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
|
||||
c = ngx_ssl_get_connection(ssl_conn);
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_flush_flight()");
|
||||
@@ -289,17 +599,17 @@ ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn)
|
||||
|
||||
|
||||
static int
|
||||
ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level,
|
||||
uint8_t alert)
|
||||
ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn,
|
||||
enum ssl_encryption_level_t ssl_level, uint8_t alert)
|
||||
{
|
||||
ngx_connection_t *c;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
|
||||
c = ngx_ssl_get_connection(ssl_conn);
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_send_alert() level:%s alert:%d",
|
||||
ngx_quic_level_name(level), (int) alert);
|
||||
"quic ngx_quic_send_alert() level:%d alert:%d",
|
||||
ssl_level, (int) alert);
|
||||
|
||||
/* already closed on regular shutdown */
|
||||
|
||||
@@ -314,13 +624,14 @@ ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level,
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
|
||||
ngx_quic_frame_t *frame)
|
||||
{
|
||||
uint64_t last;
|
||||
ngx_chain_t *cl;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_connection_t *qc;
|
||||
ngx_quic_crypto_frame_t *f;
|
||||
@@ -343,13 +654,13 @@ ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
|
||||
}
|
||||
|
||||
if (last <= ctx->crypto.offset) {
|
||||
if (pkt->level == ssl_encryption_initial) {
|
||||
if (pkt->level == NGX_QUIC_ENCRYPTION_INITIAL) {
|
||||
/* speeding up handshake completion */
|
||||
|
||||
if (!ngx_queue_empty(&ctx->sent)) {
|
||||
ngx_quic_resend_frames(c, ctx);
|
||||
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_handshake);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_HANDSHAKE);
|
||||
while (!ngx_queue_empty(&ctx->sent)) {
|
||||
ngx_quic_resend_frames(c, ctx);
|
||||
}
|
||||
@@ -359,43 +670,25 @@ ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (f->offset == ctx->crypto.offset) {
|
||||
if (ngx_quic_crypto_input(c, frame->data, pkt->level) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_quic_skip_buffer(c, &ctx->crypto, last);
|
||||
|
||||
} else {
|
||||
if (ngx_quic_write_buffer(c, &ctx->crypto, frame->data, f->length,
|
||||
f->offset)
|
||||
== NGX_CHAIN_ERROR)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
if (ngx_quic_write_buffer(c, &ctx->crypto, frame->data, f->length,
|
||||
f->offset)
|
||||
== NGX_CHAIN_ERROR)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cl = ngx_quic_read_buffer(c, &ctx->crypto, (uint64_t) -1);
|
||||
|
||||
if (cl) {
|
||||
if (ngx_quic_crypto_input(c, cl, pkt->level) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_quic_free_chain(c, cl);
|
||||
if (ngx_quic_crypto_provide(c, pkt->level) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
return ngx_quic_handshake(c);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
|
||||
enum ssl_encryption_level_t level)
|
||||
ngx_quic_handshake(ngx_connection_t *c)
|
||||
{
|
||||
int n, sslerr;
|
||||
ngx_buf_t *b;
|
||||
ngx_chain_t *cl;
|
||||
ngx_ssl_conn_t *ssl_conn;
|
||||
ngx_quic_frame_t *frame;
|
||||
ngx_quic_connection_t *qc;
|
||||
@@ -404,16 +697,6 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
|
||||
|
||||
ssl_conn = c->ssl->connection;
|
||||
|
||||
for (cl = data; cl; cl = cl->next) {
|
||||
b = cl->buf;
|
||||
|
||||
if (!SSL_provide_quic_data(ssl_conn, level, b->pos, b->last - b->pos)) {
|
||||
ngx_ssl_error(NGX_LOG_INFO, c->log, 0,
|
||||
"SSL_provide_quic_data() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
n = SSL_do_handshake(ssl_conn);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n);
|
||||
@@ -424,22 +707,31 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d",
|
||||
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 (c->ssl->handshake_rejected) {
|
||||
ngx_connection_error(c, 0, "handshake rejected");
|
||||
ERR_clear_error();
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "SSL_do_handshake() failed");
|
||||
ngx_ssl_connection_error(c, sslerr, 0, "SSL_do_handshake() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (n <= 0 || SSL_in_init(ssl_conn)) {
|
||||
if (ngx_quic_keys_available(qc->keys, ssl_encryption_early_data, 0)
|
||||
if (qc->error) {
|
||||
ngx_connection_error(c, 0, "SSL_do_handshake() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (!SSL_is_init_finished(ssl_conn)) {
|
||||
if (ngx_quic_keys_available(qc->keys, NGX_QUIC_ENCRYPTION_EARLY_DATA, 0)
|
||||
&& qc->client_tp_done)
|
||||
{
|
||||
if (ngx_quic_init_streams(c) != NGX_OK) {
|
||||
@@ -461,7 +753,7 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_HANDSHAKE_DONE;
|
||||
ngx_quic_queue_frame(qc, frame);
|
||||
|
||||
@@ -485,7 +777,7 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
|
||||
* An endpoint MUST discard its Handshake keys
|
||||
* when the TLS handshake is confirmed.
|
||||
*/
|
||||
ngx_quic_discard_ctx(c, ssl_encryption_handshake);
|
||||
ngx_quic_discard_ctx(c, NGX_QUIC_ENCRYPTION_HANDSHAKE);
|
||||
|
||||
ngx_quic_discover_path_mtu(c, qc->path);
|
||||
|
||||
@@ -502,17 +794,97 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_quic_crypto_provide(ngx_connection_t *c, ngx_uint_t level)
|
||||
{
|
||||
#if (NGX_QUIC_BORINGSSL_API || NGX_QUIC_QUICTLS_API)
|
||||
|
||||
ngx_buf_t *b;
|
||||
ngx_chain_t *out, *cl;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_connection_t *qc;
|
||||
enum ssl_encryption_level_t ssl_level;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
ctx = ngx_quic_get_send_ctx(qc, level);
|
||||
|
||||
out = ngx_quic_read_buffer(c, &ctx->crypto, (uint64_t) -1);
|
||||
if (out == NGX_CHAIN_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
switch (level) {
|
||||
case NGX_QUIC_ENCRYPTION_INITIAL:
|
||||
ssl_level = ssl_encryption_initial;
|
||||
break;
|
||||
case NGX_QUIC_ENCRYPTION_EARLY_DATA:
|
||||
ssl_level = ssl_encryption_early_data;
|
||||
break;
|
||||
case NGX_QUIC_ENCRYPTION_HANDSHAKE:
|
||||
ssl_level = ssl_encryption_handshake;
|
||||
break;
|
||||
default: /* NGX_QUIC_ENCRYPTION_APPLICATION */
|
||||
ssl_level = ssl_encryption_application;
|
||||
break;
|
||||
}
|
||||
|
||||
for (cl = out; cl; cl = cl->next) {
|
||||
b = cl->buf;
|
||||
|
||||
if (!SSL_provide_quic_data(c->ssl->connection, ssl_level, b->pos,
|
||||
b->last - b->pos))
|
||||
{
|
||||
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"SSL_provide_quic_data() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
ngx_quic_free_chain(c, out);
|
||||
|
||||
#endif
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_quic_init_connection(ngx_connection_t *c)
|
||||
{
|
||||
u_char *p;
|
||||
size_t clen;
|
||||
ssize_t len;
|
||||
ngx_str_t dcid;
|
||||
ngx_ssl_conn_t *ssl_conn;
|
||||
ngx_quic_socket_t *qsock;
|
||||
ngx_quic_connection_t *qc;
|
||||
static SSL_QUIC_METHOD quic_method;
|
||||
u_char *p;
|
||||
size_t clen;
|
||||
ssize_t len;
|
||||
ngx_str_t dcid;
|
||||
ngx_ssl_conn_t *ssl_conn;
|
||||
ngx_quic_socket_t *qsock;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
#if (NGX_QUIC_OPENSSL_API)
|
||||
static const OSSL_DISPATCH qtdis[] = {
|
||||
|
||||
{ OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_SEND,
|
||||
(void (*)(void)) ngx_quic_cbs_send },
|
||||
|
||||
{ OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_RECV_RCD,
|
||||
(void (*)(void)) ngx_quic_cbs_recv_rcd },
|
||||
|
||||
{ OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_RELEASE_RCD,
|
||||
(void (*)(void)) ngx_quic_cbs_release_rcd },
|
||||
|
||||
{ OSSL_FUNC_SSL_QUIC_TLS_YIELD_SECRET,
|
||||
(void (*)(void)) ngx_quic_cbs_yield_secret },
|
||||
|
||||
{ OSSL_FUNC_SSL_QUIC_TLS_GOT_TRANSPORT_PARAMS,
|
||||
(void (*)(void)) ngx_quic_cbs_got_transport_params },
|
||||
|
||||
{ OSSL_FUNC_SSL_QUIC_TLS_ALERT,
|
||||
(void (*)(void)) ngx_quic_cbs_alert },
|
||||
|
||||
{ 0, NULL }
|
||||
};
|
||||
#else /* NGX_QUIC_BORINGSSL_API || NGX_QUIC_QUICTLS_API */
|
||||
static SSL_QUIC_METHOD quic_method;
|
||||
#endif
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
|
||||
@@ -524,6 +896,20 @@ ngx_quic_init_connection(ngx_connection_t *c)
|
||||
|
||||
ssl_conn = c->ssl->connection;
|
||||
|
||||
#if (NGX_QUIC_OPENSSL_API)
|
||||
|
||||
if (SSL_set_quic_tls_cbs(ssl_conn, qtdis, c) == 0) {
|
||||
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"quic SSL_set_quic_tls_cbs() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (SSL_CTX_get_max_early_data(qc->conf->ssl->ctx)) {
|
||||
SSL_set_quic_tls_early_data_enabled(ssl_conn, 1);
|
||||
}
|
||||
|
||||
#else /* NGX_QUIC_BORINGSSL_API || NGX_QUIC_QUICTLS_API */
|
||||
|
||||
if (!quic_method.send_alert) {
|
||||
#if (NGX_QUIC_BORINGSSL_API)
|
||||
quic_method.set_read_secret = ngx_quic_set_read_secret;
|
||||
@@ -537,15 +923,17 @@ ngx_quic_init_connection(ngx_connection_t *c)
|
||||
}
|
||||
|
||||
if (SSL_set_quic_method(ssl_conn, &quic_method) == 0) {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"quic SSL_set_quic_method() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
#ifdef OPENSSL_INFO_QUIC
|
||||
#if (NGX_QUIC_QUICTLS_API)
|
||||
if (SSL_CTX_get_max_early_data(qc->conf->ssl->ctx)) {
|
||||
SSL_set_quic_early_data_enabled(ssl_conn, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
qsock = ngx_quic_get_socket(c);
|
||||
@@ -577,15 +965,23 @@ ngx_quic_init_connection(ngx_connection_t *c)
|
||||
"quic transport parameters len:%uz %*xs", len, len, p);
|
||||
#endif
|
||||
|
||||
#if (NGX_QUIC_OPENSSL_API)
|
||||
if (SSL_set_quic_tls_transport_params(ssl_conn, p, len) == 0) {
|
||||
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"quic SSL_set_quic_tls_transport_params() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
#else
|
||||
if (SSL_set_quic_transport_params(ssl_conn, p, len) == 0) {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"quic SSL_set_quic_transport_params() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
#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) {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"quic SSL_set_quic_early_data_context() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
@@ -280,7 +280,7 @@ ngx_quic_do_reset_stream(ngx_quic_stream_t *qs, ngx_uint_t err)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_RESET_STREAM;
|
||||
frame->u.reset_stream.id = qs->id;
|
||||
frame->u.reset_stream.error_code = err;
|
||||
@@ -367,7 +367,7 @@ ngx_quic_shutdown_stream_recv(ngx_connection_t *c)
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pc->log, 0,
|
||||
"quic stream id:0x%xL recv shutdown", qs->id);
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_STOP_SENDING;
|
||||
frame->u.stop_sending.id = qs->id;
|
||||
frame->u.stop_sending.error_code = qc->conf->stream_close_code;
|
||||
@@ -527,7 +527,7 @@ ngx_quic_reject_stream(ngx_connection_t *c, uint64_t id)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_RESET_STREAM;
|
||||
frame->u.reset_stream.id = id;
|
||||
frame->u.reset_stream.error_code = code;
|
||||
@@ -540,7 +540,7 @@ ngx_quic_reject_stream(ngx_connection_t *c, uint64_t id)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_STOP_SENDING;
|
||||
frame->u.stop_sending.id = id;
|
||||
frame->u.stop_sending.error_code = code;
|
||||
@@ -1062,7 +1062,7 @@ ngx_quic_stream_flush(ngx_quic_stream_t *qs)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_STREAM;
|
||||
frame->data = out;
|
||||
|
||||
@@ -1180,7 +1180,7 @@ ngx_quic_close_stream(ngx_quic_stream_t *qs)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_MAX_STREAMS;
|
||||
|
||||
if (qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) {
|
||||
@@ -1771,7 +1771,7 @@ ngx_quic_update_max_stream_data(ngx_quic_stream_t *qs)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_MAX_STREAM_DATA;
|
||||
frame->u.max_stream_data.id = qs->id;
|
||||
frame->u.max_stream_data.limit = qs->recv_max_data;
|
||||
@@ -1807,7 +1807,7 @@ ngx_quic_update_max_data(ngx_connection_t *c)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_MAX_DATA;
|
||||
frame->u.max_data.max_data = qc->streams.recv_max_data;
|
||||
|
||||
|
||||
@@ -281,7 +281,7 @@ ngx_int_t
|
||||
ngx_quic_parse_packet(ngx_quic_header_t *pkt)
|
||||
{
|
||||
if (!ngx_quic_long_pkt(pkt->flags)) {
|
||||
pkt->level = ssl_encryption_application;
|
||||
pkt->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
|
||||
if (ngx_quic_parse_short_header(pkt, NGX_QUIC_SERVER_CID_LEN) != NGX_OK)
|
||||
{
|
||||
@@ -468,13 +468,13 @@ ngx_quic_parse_long_header_v1(ngx_quic_header_t *pkt)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
pkt->level = ssl_encryption_initial;
|
||||
pkt->level = NGX_QUIC_ENCRYPTION_INITIAL;
|
||||
|
||||
} else if (ngx_quic_pkt_zrtt(pkt->flags)) {
|
||||
pkt->level = ssl_encryption_early_data;
|
||||
pkt->level = NGX_QUIC_ENCRYPTION_EARLY_DATA;
|
||||
|
||||
} else if (ngx_quic_pkt_hs(pkt->flags)) {
|
||||
pkt->level = ssl_encryption_handshake;
|
||||
pkt->level = NGX_QUIC_ENCRYPTION_HANDSHAKE;
|
||||
|
||||
} else {
|
||||
ngx_log_error(NGX_LOG_INFO, pkt->log, 0,
|
||||
@@ -593,7 +593,7 @@ ngx_quic_payload_size(ngx_quic_header_t *pkt, size_t pkt_len)
|
||||
|
||||
/* flags, version, dcid and scid with lengths and zero-length token */
|
||||
len = 5 + 2 + pkt->dcid.len + pkt->scid.len
|
||||
+ (pkt->level == ssl_encryption_initial ? 1 : 0);
|
||||
+ (pkt->level == NGX_QUIC_ENCRYPTION_INITIAL ? 1 : 0);
|
||||
|
||||
if (len > pkt_len) {
|
||||
return 0;
|
||||
@@ -632,7 +632,7 @@ ngx_quic_create_long_header(ngx_quic_header_t *pkt, u_char *out,
|
||||
if (out == NULL) {
|
||||
return 5 + 2 + pkt->dcid.len + pkt->scid.len
|
||||
+ ngx_quic_varint_len(rem_len) + pkt->num_len
|
||||
+ (pkt->level == ssl_encryption_initial ? 1 : 0);
|
||||
+ (pkt->level == NGX_QUIC_ENCRYPTION_INITIAL ? 1 : 0);
|
||||
}
|
||||
|
||||
p = start = out;
|
||||
@@ -647,7 +647,7 @@ ngx_quic_create_long_header(ngx_quic_header_t *pkt, u_char *out,
|
||||
*p++ = pkt->scid.len;
|
||||
p = ngx_cpymem(p, pkt->scid.data, pkt->scid.len);
|
||||
|
||||
if (pkt->level == ssl_encryption_initial) {
|
||||
if (pkt->level == NGX_QUIC_ENCRYPTION_INITIAL) {
|
||||
ngx_quic_build_int(&p, 0);
|
||||
}
|
||||
|
||||
@@ -1773,7 +1773,7 @@ ngx_quic_parse_transport_params(u_char *p, u_char *end, ngx_quic_tp_t *tp,
|
||||
}
|
||||
|
||||
if (rc == NGX_DECLINED) {
|
||||
ngx_log_error(NGX_LOG_INFO, log, 0,
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0,
|
||||
"quic %s transport param id:0x%xL, skipped",
|
||||
(id % 31 == 27) ? "reserved" : "unknown", id);
|
||||
}
|
||||
|
||||
@@ -47,9 +47,9 @@
|
||||
(ngx_quic_long_pkt(flags) ? 0x0F : 0x1F)
|
||||
|
||||
#define ngx_quic_level_name(lvl) \
|
||||
(lvl == ssl_encryption_application) ? "app" \
|
||||
: (lvl == ssl_encryption_initial) ? "init" \
|
||||
: (lvl == ssl_encryption_handshake) ? "hs" : "early"
|
||||
(lvl == NGX_QUIC_ENCRYPTION_APPLICATION) ? "app" \
|
||||
: (lvl == NGX_QUIC_ENCRYPTION_INITIAL) ? "init" \
|
||||
: (lvl == NGX_QUIC_ENCRYPTION_HANDSHAKE) ? "hs" : "early"
|
||||
|
||||
#define NGX_QUIC_MAX_CID_LEN 20
|
||||
#define NGX_QUIC_SERVER_CID_LEN NGX_QUIC_MAX_CID_LEN
|
||||
@@ -262,7 +262,7 @@ typedef struct ngx_quic_frame_s ngx_quic_frame_t;
|
||||
|
||||
struct ngx_quic_frame_s {
|
||||
ngx_uint_t type;
|
||||
enum ssl_encryption_level_t level;
|
||||
ngx_uint_t level;
|
||||
ngx_queue_t queue;
|
||||
uint64_t pnum;
|
||||
size_t plen;
|
||||
@@ -271,6 +271,7 @@ struct ngx_quic_frame_s {
|
||||
unsigned need_ack:1;
|
||||
unsigned pkt_need_ack:1;
|
||||
unsigned ignore_congestion:1;
|
||||
unsigned ignore_loss:1;
|
||||
|
||||
ngx_chain_t *data;
|
||||
union {
|
||||
@@ -309,7 +310,7 @@ typedef struct {
|
||||
uint8_t flags;
|
||||
uint32_t version;
|
||||
ngx_str_t token;
|
||||
enum ssl_encryption_level_t level;
|
||||
ngx_uint_t level;
|
||||
ngx_uint_t error;
|
||||
|
||||
/* filled in by parser */
|
||||
|
||||
@@ -245,7 +245,7 @@ ngx_http_addition_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
|
||||
&prev->types_keys, &prev->types,
|
||||
ngx_http_html_default_types)
|
||||
!= NGX_OK)
|
||||
!= NGX_CONF_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
@@ -253,7 +253,8 @@ ngx_http_auth_basic_handler(ngx_http_request_t *r)
|
||||
pwd.len = i - passwd;
|
||||
pwd.data = ngx_pnalloc(r->pool, pwd.len + 1);
|
||||
if (pwd.data == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ngx_cpystrn(pwd.data, &buf[passwd], pwd.len + 1);
|
||||
|
||||
@@ -1332,6 +1332,12 @@ ngx_http_charset_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
|
||||
table = ctx->table;
|
||||
|
||||
if (ctx->charset->utf8) {
|
||||
if (value[1].len / 2 > NGX_UTF_LEN - 1) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid value \"%V\"", &value[1]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
p = &table->src2dst[src * NGX_UTF_LEN];
|
||||
|
||||
*p++ = (u_char) (value[1].len / 2);
|
||||
@@ -1558,7 +1564,7 @@ ngx_http_charset_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
|
||||
&prev->types_keys, &prev->types,
|
||||
ngx_http_charset_default_types)
|
||||
!= NGX_OK)
|
||||
!= NGX_CONF_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
@@ -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_OK
|
||||
|| 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);
|
||||
}
|
||||
|
||||
@@ -3130,7 +3130,7 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
|
||||
prev->upstream.temp_path,
|
||||
&ngx_http_fastcgi_temp_path)
|
||||
!= NGX_OK)
|
||||
!= NGX_CONF_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ typedef struct {
|
||||
unsigned allow_binary_include:1;
|
||||
unsigned binary_include:1;
|
||||
unsigned proxy_recursive:1;
|
||||
unsigned no_cacheable:1;
|
||||
} 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)
|
||||
+ 0x10000 * sizeof(ngx_http_geo_range_t *);
|
||||
ctx.allow_binary_include = 1;
|
||||
ctx.no_cacheable = 0;
|
||||
|
||||
save = *cf;
|
||||
cf->pool = pool;
|
||||
@@ -478,6 +480,10 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (ctx.no_cacheable) {
|
||||
var->flags |= NGX_HTTP_VAR_NOCACHEABLE;
|
||||
}
|
||||
|
||||
geo->proxies = ctx.proxies;
|
||||
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;
|
||||
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) {
|
||||
|
||||
@@ -1869,7 +1869,8 @@ ngx_http_grpc_process_header(ngx_http_request_t *r)
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
if (status < NGX_HTTP_OK) {
|
||||
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);
|
||||
@@ -1902,6 +1903,10 @@ ngx_http_grpc_process_header(ngx_http_request_t *r)
|
||||
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);
|
||||
|
||||
@@ -1923,6 +1928,17 @@ ngx_http_grpc_process_header(ngx_http_request_t *r)
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"grpc 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) {
|
||||
u->headers_in.content_length_n = 0;
|
||||
|
||||
@@ -4413,6 +4429,7 @@ ngx_http_grpc_create_loc_conf(ngx_conf_t *cf)
|
||||
conf->upstream.pass_request_body = 1;
|
||||
conf->upstream.force_ranges = 0;
|
||||
conf->upstream.pass_trailers = 1;
|
||||
conf->upstream.pass_early_hints = 1;
|
||||
conf->upstream.preserve_output = 1;
|
||||
|
||||
conf->headers_source = NGX_CONF_UNSET_PTR;
|
||||
@@ -4509,8 +4526,13 @@ ngx_http_grpc_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
prev->upstream.ssl_certificate_key, NULL);
|
||||
ngx_conf_merge_ptr_value(conf->upstream.ssl_certificate_cache,
|
||||
prev->upstream.ssl_certificate_cache, NULL);
|
||||
ngx_conf_merge_ptr_value(conf->upstream.ssl_passwords,
|
||||
prev->upstream.ssl_passwords, NULL);
|
||||
|
||||
if (ngx_http_upstream_merge_ssl_passwords(cf, &conf->upstream,
|
||||
&prev->upstream)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ngx_conf_merge_ptr_value(conf->ssl_conf_commands,
|
||||
prev->ssl_conf_commands, NULL);
|
||||
@@ -5077,16 +5099,9 @@ ngx_http_grpc_set_ssl(ngx_conf_t *cf, ngx_http_grpc_loc_conf_t *glcf)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (glcf->upstream.ssl_certificate->lengths
|
||||
|| glcf->upstream.ssl_certificate_key->lengths)
|
||||
if (glcf->upstream.ssl_certificate->lengths == NULL
|
||||
&& glcf->upstream.ssl_certificate_key->lengths == NULL)
|
||||
{
|
||||
glcf->upstream.ssl_passwords =
|
||||
ngx_ssl_preserve_passwords(cf, glcf->upstream.ssl_passwords);
|
||||
if (glcf->upstream.ssl_passwords == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (ngx_ssl_certificate(cf, glcf->upstream.ssl,
|
||||
&glcf->upstream.ssl_certificate->value,
|
||||
&glcf->upstream.ssl_certificate_key->value,
|
||||
|
||||
@@ -304,7 +304,7 @@ ngx_http_gunzip_filter_inflate_start(ngx_http_request_t *r,
|
||||
{
|
||||
int rc;
|
||||
|
||||
ctx->zstream.next_in = Z_NULL;
|
||||
ctx->zstream.next_in = NULL;
|
||||
ctx->zstream.avail_in = 0;
|
||||
|
||||
ctx->zstream.zalloc = ngx_http_gunzip_filter_alloc;
|
||||
|
||||
@@ -1115,7 +1115,7 @@ ngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
|
||||
&prev->types_keys, &prev->types,
|
||||
ngx_http_html_default_types)
|
||||
!= NGX_OK)
|
||||
!= NGX_CONF_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,11 @@
|
||||
#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 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_array_t *headers;
|
||||
ngx_array_t *trailers;
|
||||
ngx_uint_t headers_inherit;
|
||||
ngx_uint_t trailers_inherit;
|
||||
} 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[] = {
|
||||
|
||||
{ ngx_string("expires"),
|
||||
@@ -123,6 +138,22 @@ static ngx_command_t ngx_http_headers_filter_commands[] = {
|
||||
offsetof(ngx_http_headers_conf_t, trailers),
|
||||
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
|
||||
};
|
||||
|
||||
@@ -657,6 +688,8 @@ ngx_http_headers_create_conf(ngx_conf_t *cf)
|
||||
*/
|
||||
|
||||
conf->expires = NGX_HTTP_EXPIRES_UNSET;
|
||||
conf->headers_inherit = NGX_CONF_UNSET_UINT;
|
||||
conf->trailers_inherit = NGX_CONF_UNSET_UINT;
|
||||
|
||||
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 *conf = child;
|
||||
|
||||
ngx_http_header_val_t *hv;
|
||||
|
||||
if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {
|
||||
conf->expires = prev->expires;
|
||||
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) {
|
||||
conf->headers = prev->headers;
|
||||
ngx_conf_merge_uint_value(conf->headers_inherit, prev->headers_inherit,
|
||||
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) {
|
||||
conf->trailers = prev->trailers;
|
||||
if (conf->trailers_inherit != NGX_HTTP_HEADERS_INHERIT_OFF
|
||||
&& 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;
|
||||
|
||||
@@ -490,7 +490,7 @@ ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
if (value[i].len == 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"index \"%V\" in \"index\" directive is invalid",
|
||||
&value[1]);
|
||||
&value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
#include <ngx_http_proxy_module.h>
|
||||
|
||||
|
||||
#define NGX_HTTP_PROXY_COOKIE_SECURE 0x0001
|
||||
@@ -23,11 +24,6 @@
|
||||
#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 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;
|
||||
|
||||
|
||||
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_reinit_request(ngx_http_request_t *r);
|
||||
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);
|
||||
static ngx_int_t 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);
|
||||
static void ngx_http_proxy_abort_request(ngx_http_request_t *r);
|
||||
static void ngx_http_proxy_finalize_request(ngx_http_request_t *r,
|
||||
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
|
||||
ngx_http_proxy_add_x_forwarded_for_variable(ngx_http_request_t *r,
|
||||
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
|
||||
ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t ngx_http_proxy_internal_chunked_variable(ngx_http_request_t *r,
|
||||
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,
|
||||
ngx_array_t *attrs);
|
||||
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[] = {
|
||||
{ ngx_string("1.0"), NGX_HTTP_VERSION_10 },
|
||||
{ ngx_string("1.1"), NGX_HTTP_VERSION_11 },
|
||||
#if (NGX_HTTP_V2)
|
||||
{ ngx_string("2"), NGX_HTTP_VERSION_20 },
|
||||
#endif
|
||||
{ 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),
|
||||
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_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
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[] = {
|
||||
{ ngx_string("Host"), ngx_string("$proxy_host") },
|
||||
{ ngx_string("Connection"), ngx_string("close") },
|
||||
{ ngx_string("Host"), ngx_string("$proxy_internal_host") },
|
||||
{ ngx_string("Connection"), ngx_string("$proxy_internal_connection") },
|
||||
{ ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") },
|
||||
{ ngx_string("Transfer-Encoding"), ngx_string("$proxy_internal_chunked") },
|
||||
{ ngx_string("TE"), ngx_string("") },
|
||||
@@ -857,8 +776,8 @@ static ngx_str_t ngx_http_proxy_hide_headers[] = {
|
||||
#if (NGX_HTTP_CACHE)
|
||||
|
||||
static ngx_keyval_t ngx_http_proxy_cache_headers[] = {
|
||||
{ ngx_string("Host"), ngx_string("$proxy_host") },
|
||||
{ ngx_string("Connection"), ngx_string("close") },
|
||||
{ ngx_string("Host"), ngx_string("$proxy_internal_host") },
|
||||
{ ngx_string("Connection"), ngx_string("$proxy_internal_connection") },
|
||||
{ ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") },
|
||||
{ ngx_string("Transfer-Encoding"), ngx_string("$proxy_internal_chunked") },
|
||||
{ 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 },
|
||||
#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_http_proxy_internal_body_length_variable, 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;
|
||||
#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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -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_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)
|
||||
|
||||
static ngx_int_t
|
||||
ngx_int_t
|
||||
ngx_http_proxy_create_key(ngx_http_request_t *r)
|
||||
{
|
||||
size_t len, loc_len;
|
||||
@@ -1195,7 +1130,8 @@ ngx_http_proxy_create_key(ngx_http_request_t *r)
|
||||
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) {
|
||||
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;
|
||||
|
||||
} else {
|
||||
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) {
|
||||
escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len,
|
||||
@@ -1877,6 +1813,13 @@ ngx_http_proxy_process_status_line(ngx_http_request_t *r)
|
||||
u->headers_in.status_n, &u->headers_in.status_line);
|
||||
|
||||
if (ctx->status.http_version < NGX_HTTP_VERSION_11) {
|
||||
|
||||
if (ctx->status.code == NGX_HTTP_EARLY_HINTS) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent HTTP/1.0 response with early hints");
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
u->headers_in.connection_close = 1;
|
||||
}
|
||||
|
||||
@@ -1938,6 +1881,14 @@ ngx_http_proxy_process_header(ngx_http_request_t *r)
|
||||
ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header: \"%V: %V\"",
|
||||
&h->key, &h->value);
|
||||
|
||||
if (r->upstream->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);
|
||||
|
||||
@@ -1949,10 +1900,6 @@ ngx_http_proxy_process_header(ngx_http_request_t *r)
|
||||
}
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header: \"%V: %V\"",
|
||||
&h->key, &h->value);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1963,6 +1910,20 @@ ngx_http_proxy_process_header(ngx_http_request_t *r)
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header done");
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
|
||||
|
||||
if (r->upstream->headers_in.status_n == NGX_HTTP_EARLY_HINTS) {
|
||||
ctx->status.code = 0;
|
||||
ctx->status.count = 0;
|
||||
ctx->status.start = NULL;
|
||||
ctx->status.end = NULL;
|
||||
|
||||
r->upstream->process_header =
|
||||
ngx_http_proxy_process_status_line;
|
||||
r->state = 0;
|
||||
return NGX_HTTP_UPSTREAM_EARLY_HINTS;
|
||||
}
|
||||
|
||||
/*
|
||||
* if no "Server" and "Date" in header line,
|
||||
* then add the special empty headers
|
||||
@@ -2010,8 +1971,6 @@ ngx_http_proxy_process_header(ngx_http_request_t *r)
|
||||
* connections alive in case of r->header_only or X-Accel-Redirect
|
||||
*/
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
|
||||
|
||||
if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT
|
||||
|| u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED
|
||||
|| ctx->head
|
||||
@@ -2084,7 +2043,7 @@ ngx_http_proxy_input_filter_init(void *data)
|
||||
/* chunked */
|
||||
|
||||
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->length = 1;
|
||||
@@ -2190,11 +2149,12 @@ ngx_http_proxy_copy_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_buf_t *b, **prev;
|
||||
ngx_chain_t *cl;
|
||||
ngx_http_request_t *r;
|
||||
ngx_http_proxy_ctx_t *ctx;
|
||||
ngx_int_t rc;
|
||||
ngx_buf_t *b, **prev;
|
||||
ngx_chain_t *cl;
|
||||
ngx_http_request_t *r;
|
||||
ngx_http_proxy_ctx_t *ctx;
|
||||
ngx_http_proxy_loc_conf_t *plcf;
|
||||
|
||||
if (buf->pos == buf->last) {
|
||||
return NGX_OK;
|
||||
@@ -2225,11 +2185,39 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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) {
|
||||
|
||||
@@ -2284,6 +2272,19 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
|
||||
|
||||
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 */
|
||||
|
||||
p->length = 0;
|
||||
@@ -2315,6 +2316,8 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
free_buf:
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, p->log, 0,
|
||||
"http proxy chunked state %ui, length %O",
|
||||
ctx->chunked.state, p->length);
|
||||
@@ -2410,11 +2413,14 @@ ngx_http_proxy_non_buffered_chunked_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_ctx_t *ctx;
|
||||
ngx_int_t rc;
|
||||
ngx_buf_t *b, *buf;
|
||||
ngx_chain_t *cl, **ll;
|
||||
ngx_http_upstream_t *u;
|
||||
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);
|
||||
|
||||
@@ -2428,13 +2434,38 @@ ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes)
|
||||
buf->pos = buf->last;
|
||||
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) {
|
||||
ll = &cl->next;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -2476,6 +2507,19 @@ ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes)
|
||||
|
||||
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 */
|
||||
|
||||
u->keepalive = !u->headers_in.connection_close;
|
||||
@@ -2506,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
|
||||
ngx_http_proxy_abort_request(ngx_http_request_t *r)
|
||||
{
|
||||
@@ -2539,6 +2692,11 @@ ngx_http_proxy_host_variable(ngx_http_request_t *r,
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (data == 1 && !ctx->legacy) {
|
||||
v->not_found = 1;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
v->len = ctx->vars.host_header.len;
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
@@ -2619,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
|
||||
ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data)
|
||||
@@ -2672,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,
|
||||
size_t prefix)
|
||||
{
|
||||
@@ -2704,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)
|
||||
{
|
||||
u_char *p;
|
||||
@@ -3349,6 +3530,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
|
||||
* conf->headers.values = NULL;
|
||||
* conf->headers.hash = { NULL, 0 };
|
||||
* conf->headers_cache.lengths = NULL;
|
||||
* conf->host_set = 0;
|
||||
* conf->headers_cache.values = NULL;
|
||||
* conf->headers_cache.hash = { NULL, 0 };
|
||||
* conf->body_lengths = NULL;
|
||||
@@ -3388,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_body = NGX_CONF_UNSET;
|
||||
conf->upstream.pass_trailers = NGX_CONF_UNSET;
|
||||
|
||||
#if (NGX_HTTP_CACHE)
|
||||
conf->upstream.cache = NGX_CONF_UNSET;
|
||||
@@ -3422,10 +3605,10 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
|
||||
conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;
|
||||
#endif
|
||||
|
||||
/* "proxy_cyclic_temp_file" is disabled */
|
||||
/* the hardcoded values */
|
||||
conf->upstream.cyclic_temp_file = 0;
|
||||
|
||||
conf->upstream.change_buffering = 1;
|
||||
conf->upstream.pass_early_hints = 1;
|
||||
|
||||
conf->headers_source = NGX_CONF_UNSET_PTR;
|
||||
|
||||
@@ -3638,7 +3821,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
|
||||
prev->upstream.temp_path,
|
||||
&ngx_http_proxy_temp_path)
|
||||
!= NGX_OK)
|
||||
!= NGX_CONF_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
@@ -3731,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,
|
||||
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,
|
||||
prev->upstream.intercept_errors, 0);
|
||||
|
||||
@@ -3767,8 +3953,13 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
prev->upstream.ssl_certificate_key, NULL);
|
||||
ngx_conf_merge_ptr_value(conf->upstream.ssl_certificate_cache,
|
||||
prev->upstream.ssl_certificate_cache, NULL);
|
||||
ngx_conf_merge_ptr_value(conf->upstream.ssl_passwords,
|
||||
prev->upstream.ssl_passwords, NULL);
|
||||
|
||||
if (ngx_http_upstream_merge_ssl_passwords(cf, &conf->upstream,
|
||||
&prev->upstream)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ngx_conf_merge_ptr_value(conf->ssl_conf_commands,
|
||||
prev->ssl_conf_commands, NULL);
|
||||
@@ -3915,6 +4106,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
#if (NGX_HTTP_CACHE)
|
||||
conf->headers_cache = prev->headers_cache;
|
||||
#endif
|
||||
conf->host_set = prev->host_set;
|
||||
}
|
||||
|
||||
rc = ngx_http_proxy_init_headers(cf, conf, &conf->headers,
|
||||
@@ -3947,6 +4139,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
#if (NGX_HTTP_CACHE)
|
||||
prev->headers_cache = conf->headers_cache;
|
||||
#endif
|
||||
prev->host_set = conf->host_set;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
@@ -3999,6 +4192,12 @@ ngx_http_proxy_init_headers(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *conf,
|
||||
src = conf->headers_source->elts;
|
||||
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);
|
||||
if (s == NULL) {
|
||||
return NGX_ERROR;
|
||||
@@ -5128,16 +5327,9 @@ ngx_http_proxy_set_ssl(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *plcf)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (plcf->upstream.ssl_certificate->lengths
|
||||
|| plcf->upstream.ssl_certificate_key->lengths)
|
||||
if (plcf->upstream.ssl_certificate->lengths == NULL
|
||||
&& plcf->upstream.ssl_certificate_key->lengths == NULL)
|
||||
{
|
||||
plcf->upstream.ssl_passwords =
|
||||
ngx_ssl_preserve_passwords(cf, plcf->upstream.ssl_passwords);
|
||||
if (plcf->upstream.ssl_passwords == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (ngx_ssl_certificate(cf, plcf->upstream.ssl,
|
||||
&plcf->upstream.ssl_certificate->value,
|
||||
&plcf->upstream.ssl_certificate_key->value,
|
||||
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1543,7 +1543,7 @@ ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
|
||||
prev->upstream.temp_path,
|
||||
&ngx_http_scgi_temp_path)
|
||||
!= NGX_OK)
|
||||
!= NGX_CONF_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
@@ -419,13 +419,13 @@ ngx_http_slice_range_variable(ngx_http_request_t *r,
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_http_set_ctx(r, ctx, ngx_http_slice_filter_module);
|
||||
|
||||
p = ngx_pnalloc(r->pool, sizeof("bytes=-") - 1 + 2 * NGX_OFF_T_LEN);
|
||||
if (p == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_http_set_ctx(r, ctx, ngx_http_slice_filter_module);
|
||||
|
||||
ctx->start = slcf->size * (ngx_http_slice_get_start(r) / slcf->size);
|
||||
|
||||
ctx->range.data = p;
|
||||
|
||||
@@ -820,7 +820,7 @@ ngx_http_ssi_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
||||
}
|
||||
|
||||
for (prm = cmd->params; prm->name.len; prm++) {
|
||||
if (prm->mandatory && params[prm->index] == 0) {
|
||||
if (prm->mandatory && params[prm->index] == NULL) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"mandatory \"%V\" parameter is absent "
|
||||
"in \"%V\" SSI command",
|
||||
@@ -2942,7 +2942,7 @@ ngx_http_ssi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
|
||||
&prev->types_keys, &prev->types,
|
||||
ngx_http_html_default_types)
|
||||
!= NGX_OK)
|
||||
!= NGX_CONF_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
@@ -117,6 +117,13 @@ static ngx_command_t ngx_http_ssl_commands[] = {
|
||||
0,
|
||||
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_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_http_ssl_password_file,
|
||||
@@ -124,6 +131,13 @@ static ngx_command_t ngx_http_ssl_commands[] = {
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_certificate_compression"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, certificate_compression),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_dhparam"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
@@ -230,7 +244,7 @@ static ngx_command_t ngx_http_ssl_commands[] = {
|
||||
NULL },
|
||||
|
||||
{ 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_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, ocsp),
|
||||
@@ -386,6 +400,9 @@ static ngx_http_variable_t ngx_http_ssl_vars[] = {
|
||||
{ ngx_string("ssl_curves"), NULL, ngx_http_ssl_variable,
|
||||
(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,
|
||||
(uintptr_t) ngx_ssl_get_session_id, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
@@ -402,6 +419,13 @@ static ngx_http_variable_t ngx_http_ssl_vars[] = {
|
||||
{ ngx_string("ssl_alpn_protocol"), NULL, ngx_http_ssl_variable,
|
||||
(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,
|
||||
(uintptr_t) ngx_ssl_get_certificate, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
@@ -443,6 +467,9 @@ static ngx_http_variable_t ngx_http_ssl_vars[] = {
|
||||
{ ngx_string("ssl_client_v_remain"), NULL, ngx_http_ssl_variable,
|
||||
(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
|
||||
};
|
||||
|
||||
@@ -656,6 +683,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
|
||||
*/
|
||||
|
||||
sscf->prefer_server_ciphers = NGX_CONF_UNSET;
|
||||
sscf->certificate_compression = NGX_CONF_UNSET;
|
||||
sscf->early_data = NGX_CONF_UNSET;
|
||||
sscf->reject_handshake = NGX_CONF_UNSET;
|
||||
sscf->buffer_size = NGX_CONF_UNSET_SIZE;
|
||||
@@ -664,6 +692,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
|
||||
sscf->certificates = NGX_CONF_UNSET_PTR;
|
||||
sscf->certificate_keys = 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->conf_commands = NGX_CONF_UNSET_PTR;
|
||||
sscf->builtin_session_cache = NGX_CONF_UNSET;
|
||||
@@ -698,6 +727,9 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
ngx_conf_merge_value(conf->prefer_server_ciphers,
|
||||
prev->prefer_server_ciphers, 1);
|
||||
|
||||
ngx_conf_merge_value(conf->certificate_compression,
|
||||
prev->certificate_compression, 0);
|
||||
|
||||
ngx_conf_merge_value(conf->early_data, prev->early_data, 1);
|
||||
ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0);
|
||||
|
||||
@@ -717,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,
|
||||
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_str_value(conf->dhparam, prev->dhparam, "");
|
||||
@@ -792,6 +826,12 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
cln->data = &conf->ssl;
|
||||
|
||||
#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,
|
||||
ngx_http_ssl_servername)
|
||||
@@ -802,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, "
|
||||
"therefore SNI is not available");
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
|
||||
@@ -846,6 +886,13 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_ssl_certificate_compression(cf, &conf->ssl,
|
||||
conf->certificate_compression)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
conf->ssl.buffer_size = conf->buffer_size;
|
||||
@@ -900,6 +947,10 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
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) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
@@ -938,13 +989,19 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
|
||||
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,
|
||||
&conf->stapling_responder, conf->stapling_verify)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (ngx_ssl_early_data(cf, &conf->ssl, conf->early_data) != NGX_OK) {
|
||||
|
||||
@@ -18,6 +18,7 @@ typedef struct {
|
||||
ngx_ssl_t ssl;
|
||||
|
||||
ngx_flag_t prefer_server_ciphers;
|
||||
ngx_flag_t certificate_compression;
|
||||
ngx_flag_t early_data;
|
||||
ngx_flag_t reject_handshake;
|
||||
|
||||
@@ -48,6 +49,7 @@ typedef struct {
|
||||
|
||||
ngx_str_t ciphers;
|
||||
|
||||
ngx_array_t *ech_files;
|
||||
ngx_array_t *passwords;
|
||||
ngx_array_t *conf_commands;
|
||||
|
||||
|
||||
@@ -901,7 +901,7 @@ ngx_http_sub_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
|
||||
&prev->types_keys, &prev->types,
|
||||
ngx_http_html_default_types)
|
||||
!= NGX_OK)
|
||||
!= NGX_CONF_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
@@ -1803,7 +1803,7 @@ ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
|
||||
prev->upstream.temp_path,
|
||||
&ngx_http_uwsgi_temp_path)
|
||||
!= NGX_OK)
|
||||
!= NGX_CONF_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
@@ -1933,8 +1933,13 @@ ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
prev->upstream.ssl_certificate_key, NULL);
|
||||
ngx_conf_merge_ptr_value(conf->upstream.ssl_certificate_cache,
|
||||
prev->upstream.ssl_certificate_cache, NULL);
|
||||
ngx_conf_merge_ptr_value(conf->upstream.ssl_passwords,
|
||||
prev->upstream.ssl_passwords, NULL);
|
||||
|
||||
if (ngx_http_upstream_merge_ssl_passwords(cf, &conf->upstream,
|
||||
&prev->upstream)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ngx_conf_merge_ptr_value(conf->ssl_conf_commands,
|
||||
prev->ssl_conf_commands, NULL);
|
||||
@@ -2685,16 +2690,9 @@ ngx_http_uwsgi_set_ssl(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *uwcf)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (uwcf->upstream.ssl_certificate->lengths
|
||||
|| uwcf->upstream.ssl_certificate_key->lengths)
|
||||
if (uwcf->upstream.ssl_certificate->lengths == NULL
|
||||
&& uwcf->upstream.ssl_certificate_key->lengths == NULL)
|
||||
{
|
||||
uwcf->upstream.ssl_passwords =
|
||||
ngx_ssl_preserve_passwords(cf, uwcf->upstream.ssl_passwords);
|
||||
if (uwcf->upstream.ssl_passwords == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (ngx_ssl_certificate(cf, uwcf->upstream.ssl,
|
||||
&uwcf->upstream.ssl_certificate->value,
|
||||
&uwcf->upstream.ssl_certificate_key->value,
|
||||
|
||||
@@ -1112,7 +1112,7 @@ ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
|
||||
&prev->types_keys, &prev->types,
|
||||
ngx_http_xslt_default_types)
|
||||
!= NGX_OK)
|
||||
!= NGX_CONF_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ ngx_uint_t ngx_http_max_module;
|
||||
|
||||
|
||||
ngx_http_output_header_filter_pt ngx_http_top_header_filter;
|
||||
ngx_http_output_header_filter_pt ngx_http_top_early_hints_filter;
|
||||
ngx_http_output_body_filter_pt ngx_http_top_body_filter;
|
||||
ngx_http_request_body_filter_pt ngx_http_top_request_body_filter;
|
||||
|
||||
|
||||
+5
-4
@@ -117,12 +117,11 @@ 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,
|
||||
ngx_str_t *args);
|
||||
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_int_t ngx_http_process_request_uri(ngx_http_request_t *r);
|
||||
ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r);
|
||||
void ngx_http_process_request(ngx_http_request_t *r);
|
||||
void ngx_http_update_location_config(ngx_http_request_t *r);
|
||||
void ngx_http_handler(ngx_http_request_t *r);
|
||||
@@ -131,8 +130,8 @@ ngx_int_t ngx_http_post_request(ngx_http_request_t *r,
|
||||
ngx_http_posted_request_t *pr);
|
||||
ngx_int_t ngx_http_set_virtual_server(ngx_http_request_t *r,
|
||||
ngx_str_t *host);
|
||||
ngx_int_t ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool,
|
||||
ngx_uint_t alloc);
|
||||
ngx_int_t ngx_http_validate_host(ngx_str_t *host, in_port_t *port,
|
||||
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_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);
|
||||
@@ -152,6 +151,7 @@ ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r,
|
||||
ngx_int_t ngx_http_read_unbuffered_request_body(ngx_http_request_t *r);
|
||||
|
||||
ngx_int_t ngx_http_send_header(ngx_http_request_t *r);
|
||||
ngx_int_t ngx_http_send_early_hints(ngx_http_request_t *r);
|
||||
ngx_int_t ngx_http_special_response_handler(ngx_http_request_t *r,
|
||||
ngx_int_t error);
|
||||
ngx_int_t ngx_http_filter_finalize_request(ngx_http_request_t *r,
|
||||
@@ -191,6 +191,7 @@ extern ngx_str_t ngx_http_html_default_types[];
|
||||
|
||||
|
||||
extern ngx_http_output_header_filter_pt ngx_http_top_header_filter;
|
||||
extern ngx_http_output_header_filter_pt ngx_http_top_early_hints_filter;
|
||||
extern ngx_http_output_body_filter_pt ngx_http_top_body_filter;
|
||||
extern ngx_http_request_body_filter_pt ngx_http_top_request_body_filter;
|
||||
|
||||
|
||||
@@ -509,6 +509,13 @@ static ngx_command_t ngx_http_core_commands[] = {
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("keepalive_min_timeout"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_msec_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_core_loc_conf_t, keepalive_min_timeout),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("keepalive_requests"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_num_slot,
|
||||
@@ -663,6 +670,13 @@ static ngx_command_t ngx_http_core_commands[] = {
|
||||
offsetof(ngx_http_core_loc_conf_t, etag),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("early_hints"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
|
||||
ngx_http_set_predicate_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_core_loc_conf_t, early_hints),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("error_page"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|
||||
|NGX_CONF_2MORE,
|
||||
@@ -1850,6 +1864,37 @@ ngx_http_send_header(ngx_http_request_t *r)
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_send_early_hints(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
|
||||
if (r->post_action) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (r->header_sent) {
|
||||
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
|
||||
"header already sent");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
||||
|
||||
rc = ngx_http_test_predicates(r, clcf->early_hints);
|
||||
|
||||
if (rc != NGX_DECLINED) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http send early hints \"%V?%V\"", &r->uri, &r->args);
|
||||
|
||||
return ngx_http_top_early_hints_filter(r);
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
||||
{
|
||||
@@ -2320,6 +2365,7 @@ ngx_http_subrequest(ngx_http_request_t *r,
|
||||
ngx_connection_t *c;
|
||||
ngx_http_request_t *sr;
|
||||
ngx_http_core_srv_conf_t *cscf;
|
||||
ngx_http_posted_request_t *posted;
|
||||
ngx_http_postponed_request_t *pr, *p;
|
||||
|
||||
if (r->subrequests == 0) {
|
||||
@@ -2373,6 +2419,11 @@ ngx_http_subrequest(ngx_http_request_t *r,
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
posted = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t));
|
||||
if (posted == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
|
||||
sr->main_conf = cscf->ctx->main_conf;
|
||||
sr->srv_conf = cscf->ctx->srv_conf;
|
||||
@@ -2431,10 +2482,6 @@ ngx_http_subrequest(ngx_http_request_t *r,
|
||||
}
|
||||
|
||||
if (!sr->background) {
|
||||
if (c->data == r && r->postponed == NULL) {
|
||||
c->data = sr;
|
||||
}
|
||||
|
||||
pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));
|
||||
if (pr == NULL) {
|
||||
return NGX_ERROR;
|
||||
@@ -2444,6 +2491,10 @@ ngx_http_subrequest(ngx_http_request_t *r,
|
||||
pr->out = NULL;
|
||||
pr->next = NULL;
|
||||
|
||||
if (c->data == r && r->postponed == NULL) {
|
||||
c->data = sr;
|
||||
}
|
||||
|
||||
if (r->postponed) {
|
||||
for (p = r->postponed; p->next; p = p->next) { /* void */ }
|
||||
p->next = pr;
|
||||
@@ -2491,7 +2542,7 @@ ngx_http_subrequest(ngx_http_request_t *r,
|
||||
ngx_http_update_location_config(sr);
|
||||
}
|
||||
|
||||
return ngx_http_post_request(sr, NULL);
|
||||
return ngx_http_post_request(sr, posted);
|
||||
}
|
||||
|
||||
|
||||
@@ -3606,6 +3657,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t *cf)
|
||||
clcf->keepalive_time = NGX_CONF_UNSET_MSEC;
|
||||
clcf->keepalive_timeout = NGX_CONF_UNSET_MSEC;
|
||||
clcf->keepalive_header = NGX_CONF_UNSET;
|
||||
clcf->keepalive_min_timeout = NGX_CONF_UNSET_MSEC;
|
||||
clcf->keepalive_requests = NGX_CONF_UNSET_UINT;
|
||||
clcf->lingering_close = NGX_CONF_UNSET_UINT;
|
||||
clcf->lingering_time = NGX_CONF_UNSET_MSEC;
|
||||
@@ -3623,6 +3675,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t *cf)
|
||||
clcf->chunked_transfer_encoding = NGX_CONF_UNSET;
|
||||
clcf->etag = NGX_CONF_UNSET;
|
||||
clcf->server_tokens = NGX_CONF_UNSET_UINT;
|
||||
clcf->early_hints = NGX_CONF_UNSET_PTR;
|
||||
clcf->types_hash_max_size = NGX_CONF_UNSET_UINT;
|
||||
clcf->types_hash_bucket_size = NGX_CONF_UNSET_UINT;
|
||||
|
||||
@@ -3844,6 +3897,8 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
prev->keepalive_timeout, 75000);
|
||||
ngx_conf_merge_sec_value(conf->keepalive_header,
|
||||
prev->keepalive_header, 0);
|
||||
ngx_conf_merge_msec_value(conf->keepalive_min_timeout,
|
||||
prev->keepalive_min_timeout, 0);
|
||||
ngx_conf_merge_uint_value(conf->keepalive_requests,
|
||||
prev->keepalive_requests, 1000);
|
||||
ngx_conf_merge_uint_value(conf->lingering_close,
|
||||
@@ -3876,7 +3931,7 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
if (ngx_conf_merge_path_value(cf, &conf->client_body_temp_path,
|
||||
prev->client_body_temp_path,
|
||||
&ngx_http_client_temp_path)
|
||||
!= NGX_OK)
|
||||
!= NGX_CONF_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
@@ -3901,6 +3956,8 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
ngx_conf_merge_uint_value(conf->server_tokens, prev->server_tokens,
|
||||
NGX_HTTP_SERVER_TOKENS_ON);
|
||||
|
||||
ngx_conf_merge_ptr_value(conf->early_hints, prev->early_hints, NULL);
|
||||
|
||||
ngx_conf_merge_ptr_value(conf->open_file_cache,
|
||||
prev->open_file_cache, NULL);
|
||||
|
||||
|
||||
@@ -206,6 +206,7 @@ typedef struct {
|
||||
#if (NGX_PCRE)
|
||||
unsigned captures:1;
|
||||
#endif
|
||||
unsigned allow_connect:1;
|
||||
|
||||
ngx_http_core_loc_conf_t **named_locations;
|
||||
} ngx_http_core_srv_conf_t;
|
||||
@@ -370,6 +371,7 @@ struct ngx_http_core_loc_conf_s {
|
||||
ngx_msec_t send_timeout; /* send_timeout */
|
||||
ngx_msec_t keepalive_time; /* keepalive_time */
|
||||
ngx_msec_t keepalive_timeout; /* keepalive_timeout */
|
||||
ngx_msec_t keepalive_min_timeout; /* keepalive_min_timeout */
|
||||
ngx_msec_t lingering_time; /* lingering_time */
|
||||
ngx_msec_t lingering_timeout; /* lingering_timeout */
|
||||
ngx_msec_t resolver_timeout; /* resolver_timeout */
|
||||
@@ -429,6 +431,8 @@ struct ngx_http_core_loc_conf_s {
|
||||
ngx_http_complex_value_t *disable_symlinks_from;
|
||||
#endif
|
||||
|
||||
ngx_array_t *early_hints; /* early_hints */
|
||||
|
||||
ngx_array_t *error_pages; /* error_page */
|
||||
|
||||
ngx_path_t *client_body_temp_path; /* client_body_temp_path */
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
static ngx_int_t ngx_http_header_filter_init(ngx_conf_t *cf);
|
||||
static ngx_int_t ngx_http_header_filter(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_early_hints_filter(ngx_http_request_t *r);
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_header_filter_module_ctx = {
|
||||
@@ -50,6 +51,9 @@ static u_char ngx_http_server_string[] = "Server: " NGINX_SERVER CRLF;
|
||||
static u_char ngx_http_server_full_string[] = "Server: " NGINX_SERVER_FULL CRLF;
|
||||
static u_char ngx_http_server_build_string[] = "Server: " NGINX_SERVER_FULL_BUILD CRLF;
|
||||
|
||||
static ngx_str_t ngx_http_early_hints_status_line =
|
||||
ngx_string("HTTP/1.1 103 Early Hints" CRLF);
|
||||
|
||||
|
||||
static ngx_str_t ngx_http_status_lines[] = {
|
||||
|
||||
@@ -625,10 +629,113 @@ ngx_http_header_filter(ngx_http_request_t *r)
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_early_hints_filter(ngx_http_request_t *r)
|
||||
{
|
||||
size_t len;
|
||||
ngx_buf_t *b;
|
||||
ngx_uint_t i;
|
||||
ngx_chain_t out;
|
||||
ngx_list_part_t *part;
|
||||
ngx_table_elt_t *header;
|
||||
|
||||
if (r != r->main) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (r->http_version < NGX_HTTP_VERSION_11) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
len = 0;
|
||||
|
||||
part = &r->headers_out.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 (header[i].hash == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
len += header[i].key.len + sizeof(": ") - 1 + header[i].value.len
|
||||
+ sizeof(CRLF) - 1;
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
len += ngx_http_early_hints_status_line.len
|
||||
/* the end of the early hints */
|
||||
+ sizeof(CRLF) - 1;
|
||||
|
||||
b = ngx_create_temp_buf(r->pool, len);
|
||||
if (b == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b->last = ngx_copy(b->last, ngx_http_early_hints_status_line.data,
|
||||
ngx_http_early_hints_status_line.len);
|
||||
|
||||
part = &r->headers_out.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 (header[i].hash == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);
|
||||
*b->last++ = ':'; *b->last++ = ' ';
|
||||
|
||||
b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len);
|
||||
*b->last++ = CR; *b->last++ = LF;
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"%*s", (size_t) (b->last - b->pos), b->pos);
|
||||
|
||||
/* the end of HTTP early hints */
|
||||
*b->last++ = CR; *b->last++ = LF;
|
||||
|
||||
r->header_size = b->last - b->pos;
|
||||
|
||||
b->flush = 1;
|
||||
|
||||
out.buf = b;
|
||||
out.next = NULL;
|
||||
|
||||
return ngx_http_write_filter(r, &out);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_header_filter_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_top_header_filter = ngx_http_header_filter;
|
||||
ngx_http_top_early_hints_filter = ngx_http_early_hints_filter;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
+69
-28
@@ -111,6 +111,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
|
||||
sw_schema,
|
||||
sw_schema_slash,
|
||||
sw_schema_slash_slash,
|
||||
sw_spaces_before_host,
|
||||
sw_host_start,
|
||||
sw_host,
|
||||
sw_host_end,
|
||||
@@ -158,6 +159,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
|
||||
if (ch == ' ') {
|
||||
r->method_end = p - 1;
|
||||
m = r->request_start;
|
||||
state = sw_spaces_before_uri;
|
||||
|
||||
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', ' '))
|
||||
{
|
||||
r->method = NGX_HTTP_CONNECT;
|
||||
state = sw_spaces_before_host;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -269,7 +272,6 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
|
||||
break;
|
||||
}
|
||||
|
||||
state = sw_spaces_before_uri;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -345,6 +347,14 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
|
||||
}
|
||||
break;
|
||||
|
||||
case sw_spaces_before_host:
|
||||
|
||||
if (ch == ' ') {
|
||||
break;
|
||||
}
|
||||
|
||||
/* fall through */
|
||||
|
||||
case sw_host_start:
|
||||
|
||||
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:
|
||||
|
||||
r->host_end = p;
|
||||
|
||||
switch (ch) {
|
||||
case ':':
|
||||
if (ch == ':') {
|
||||
state = sw_port;
|
||||
break;
|
||||
}
|
||||
|
||||
r->host_end = p;
|
||||
|
||||
if (r->method == NGX_HTTP_CONNECT) {
|
||||
return NGX_HTTP_PARSE_INVALID_REQUEST;
|
||||
}
|
||||
|
||||
switch (ch) {
|
||||
case '/':
|
||||
r->uri_start = p;
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
case '/':
|
||||
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:
|
||||
switch (ch) {
|
||||
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;
|
||||
break;
|
||||
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_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;
|
||||
ngx_int_t rc;
|
||||
@@ -2217,9 +2254,6 @@ ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
|
||||
case CR:
|
||||
state = sw_last_chunk_extension_almost_done;
|
||||
break;
|
||||
case LF:
|
||||
state = sw_trailer;
|
||||
break;
|
||||
case ';':
|
||||
case ' ':
|
||||
case '\t':
|
||||
@@ -2236,9 +2270,6 @@ ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
|
||||
case CR:
|
||||
state = sw_chunk_extension_almost_done;
|
||||
break;
|
||||
case LF:
|
||||
state = sw_chunk_data;
|
||||
break;
|
||||
case ';':
|
||||
case ' ':
|
||||
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;
|
||||
break;
|
||||
case LF:
|
||||
state = sw_chunk_data;
|
||||
goto invalid;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -2276,9 +2307,6 @@ ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
|
||||
case CR:
|
||||
state = sw_after_data_almost_done;
|
||||
break;
|
||||
case LF:
|
||||
state = sw_chunk_start;
|
||||
break;
|
||||
default:
|
||||
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;
|
||||
break;
|
||||
case LF:
|
||||
state = sw_trailer;
|
||||
goto invalid;
|
||||
}
|
||||
break;
|
||||
|
||||
case sw_last_chunk_extension_almost_done:
|
||||
if (ch == LF) {
|
||||
if (keep_trailers) {
|
||||
goto done;
|
||||
}
|
||||
state = sw_trailer;
|
||||
break;
|
||||
}
|
||||
@@ -2314,7 +2345,7 @@ ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
|
||||
state = sw_trailer_almost_done;
|
||||
break;
|
||||
case LF:
|
||||
goto done;
|
||||
goto invalid;
|
||||
default:
|
||||
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;
|
||||
break;
|
||||
case LF:
|
||||
state = sw_trailer;
|
||||
goto invalid;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -2358,35 +2389,45 @@ data:
|
||||
switch (state) {
|
||||
|
||||
case sw_chunk_start:
|
||||
ctx->length = 3 /* "0" LF LF */;
|
||||
ctx->length = 5 /* "0" CRLF CRLF */;
|
||||
break;
|
||||
case sw_chunk_size:
|
||||
ctx->length = 1 /* LF */
|
||||
+ (ctx->size ? ctx->size + 4 /* LF "0" LF LF */
|
||||
: 1 /* LF */);
|
||||
ctx->length = 2 /* CRLF */
|
||||
+ (ctx->size ? ctx->size + 7 /* CRLF "0" CRLF CRLF */
|
||||
: 2 /* CRLF */);
|
||||
break;
|
||||
case sw_chunk_extension:
|
||||
ctx->length = 2 /* CRLF */ + ctx->size + 7 /* CRLF "0" CRLF CRLF */;
|
||||
break;
|
||||
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;
|
||||
case sw_chunk_data:
|
||||
ctx->length = ctx->size + 4 /* LF "0" LF LF */;
|
||||
ctx->length = ctx->size + 7 /* CRLF "0" CRLF CRLF */;
|
||||
break;
|
||||
case sw_after_data:
|
||||
ctx->length = 7 /* CRLF "0" CRLF CRLF */;
|
||||
break;
|
||||
case sw_after_data_almost_done:
|
||||
ctx->length = 4 /* LF "0" LF LF */;
|
||||
ctx->length = 6 /* LF "0" CRLF CRLF */;
|
||||
break;
|
||||
case sw_last_chunk_extension:
|
||||
ctx->length = 4 /* CRLF CRLF */;
|
||||
break;
|
||||
case sw_last_chunk_extension_almost_done:
|
||||
ctx->length = 2 /* LF LF */;
|
||||
ctx->length = 3 /* LF CRLF */;
|
||||
break;
|
||||
case sw_trailer:
|
||||
ctx->length = 2 /* CRLF */;
|
||||
break;
|
||||
case sw_trailer_almost_done:
|
||||
ctx->length = 1 /* LF */;
|
||||
break;
|
||||
case sw_trailer_header:
|
||||
ctx->length = 4 /* CRLF CRLF */;
|
||||
break;
|
||||
case sw_trailer_header_almost_done:
|
||||
ctx->length = 2 /* LF LF */;
|
||||
ctx->length = 3 /* LF CRLF */;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
+237
-64
@@ -29,6 +29,7 @@ static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r,
|
||||
static ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r,
|
||||
ngx_table_elt_t *h, ngx_uint_t offset);
|
||||
|
||||
static ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_find_virtual_server(ngx_connection_t *c,
|
||||
ngx_http_virtual_names_t *virtual_names, ngx_str_t *host,
|
||||
ngx_http_request_t *r, ngx_http_core_srv_conf_t **cscfp);
|
||||
@@ -890,28 +891,47 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
|
||||
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;
|
||||
|
||||
servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name);
|
||||
if (arg != NULL) {
|
||||
host = *(ngx_str_t *) arg;
|
||||
|
||||
if (servername == NULL) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
||||
"SSL server name: null");
|
||||
goto done;
|
||||
if (host.data == NULL) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
||||
"SSL server name: null");
|
||||
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,
|
||||
"SSL server name: \"%s\"", servername);
|
||||
|
||||
host.len = ngx_strlen(servername);
|
||||
"SSL server name: \"%V\"", &host);
|
||||
|
||||
if (host.len == 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
host.data = (u_char *) servername;
|
||||
|
||||
rc = ngx_http_validate_host(&host, c->pool, 1);
|
||||
rc = ngx_http_validate_host(&host, NULL, c->pool, 1);
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
goto error;
|
||||
@@ -945,7 +965,7 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
|
||||
|
||||
ngx_set_connection_log(c, clcf->error_log);
|
||||
|
||||
sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);
|
||||
sscf = ngx_http_get_module_srv_conf(cscf->ctx, ngx_http_ssl_module);
|
||||
|
||||
c->ssl->buffer_size = sscf->buffer_size;
|
||||
|
||||
@@ -995,6 +1015,7 @@ done:
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
c->ssl->sni_accepted = 1;
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
|
||||
error:
|
||||
@@ -1086,6 +1107,7 @@ ngx_http_process_request_line(ngx_event_t *rev)
|
||||
ssize_t n;
|
||||
ngx_int_t rc, rv;
|
||||
ngx_str_t host;
|
||||
in_port_t port;
|
||||
ngx_connection_t *c;
|
||||
ngx_http_request_t *r;
|
||||
|
||||
@@ -1148,7 +1170,7 @@ ngx_http_process_request_line(ngx_event_t *rev)
|
||||
host.len = r->host_end - 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) {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
@@ -1167,6 +1189,7 @@ ngx_http_process_request_line(ngx_event_t *rev)
|
||||
}
|
||||
|
||||
r->headers_in.server = host;
|
||||
r->port = port;
|
||||
}
|
||||
|
||||
if (r->http_version < NGX_HTTP_VERSION_10) {
|
||||
@@ -1827,6 +1850,7 @@ ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h,
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_str_t host;
|
||||
in_port_t port;
|
||||
|
||||
if (r->headers_in.host) {
|
||||
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
||||
@@ -1843,7 +1867,7 @@ ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h,
|
||||
|
||||
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) {
|
||||
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
||||
@@ -1866,6 +1890,7 @@ ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h,
|
||||
}
|
||||
|
||||
r->headers_in.server = host;
|
||||
r->port = port;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -1961,9 +1986,11 @@ ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h,
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
static ngx_int_t
|
||||
ngx_http_process_request_header(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_http_core_srv_conf_t *cscf;
|
||||
|
||||
if (r->headers_in.server.len == 0
|
||||
&& ngx_http_set_virtual_server(r, &r->headers_in.server)
|
||||
== NGX_ERROR)
|
||||
@@ -2032,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,
|
||||
"client sent CONNECT method");
|
||||
ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);
|
||||
@@ -2143,74 +2174,176 @@ ngx_http_process_request(ngx_http_request_t *r)
|
||||
|
||||
|
||||
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;
|
||||
size_t i, dot_pos, host_len;
|
||||
u_char *h, ch;
|
||||
size_t i, dot_pos, host_len;
|
||||
ngx_int_t port;
|
||||
|
||||
enum {
|
||||
sw_usual = 0,
|
||||
sw_literal,
|
||||
sw_rest
|
||||
sw_host_start = 0,
|
||||
sw_host,
|
||||
sw_host_ip_literal,
|
||||
sw_host_end,
|
||||
sw_port,
|
||||
} state;
|
||||
|
||||
dot_pos = host->len;
|
||||
host_len = host->len;
|
||||
port = 0;
|
||||
|
||||
h = host->data;
|
||||
|
||||
state = sw_usual;
|
||||
state = sw_host_start;
|
||||
|
||||
for (i = 0; i < host->len; i++) {
|
||||
ch = h[i];
|
||||
|
||||
switch (ch) {
|
||||
switch (state) {
|
||||
|
||||
case '.':
|
||||
if (dot_pos == i - 1) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
dot_pos = i;
|
||||
break;
|
||||
case sw_host_start:
|
||||
|
||||
case ':':
|
||||
if (state == sw_usual) {
|
||||
host_len = i;
|
||||
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 == '[') {
|
||||
state = sw_host_ip_literal;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ch <= 0x20 || ch == 0x7f) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
state = sw_host;
|
||||
|
||||
/* fall through */
|
||||
|
||||
case sw_host:
|
||||
|
||||
if (ch >= 'A' && ch <= 'Z') {
|
||||
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;
|
||||
|
||||
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) {
|
||||
host_len--;
|
||||
}
|
||||
@@ -2230,6 +2363,10 @@ ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc)
|
||||
|
||||
host->len = host_len;
|
||||
|
||||
if (portp) {
|
||||
*portp = port;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
@@ -2799,6 +2936,13 @@ ngx_http_finalize_connection(ngx_http_request_t *r)
|
||||
r->lingering_close = 1;
|
||||
}
|
||||
|
||||
if (r->keepalive
|
||||
&& clcf->keepalive_min_timeout > 0)
|
||||
{
|
||||
ngx_http_set_keepalive(r);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ngx_terminate
|
||||
&& !ngx_exiting
|
||||
&& r->keepalive
|
||||
@@ -3301,10 +3445,22 @@ ngx_http_set_keepalive(ngx_http_request_t *r)
|
||||
r->http_state = NGX_HTTP_KEEPALIVE_STATE;
|
||||
#endif
|
||||
|
||||
c->idle = 1;
|
||||
ngx_reusable_connection(c, 1);
|
||||
if (clcf->keepalive_min_timeout == 0) {
|
||||
c->idle = 1;
|
||||
ngx_reusable_connection(c, 1);
|
||||
}
|
||||
|
||||
ngx_add_timer(rev, clcf->keepalive_timeout);
|
||||
if (clcf->keepalive_min_timeout > 0
|
||||
&& clcf->keepalive_timeout > clcf->keepalive_min_timeout)
|
||||
{
|
||||
hc->keepalive_timeout = clcf->keepalive_timeout
|
||||
- clcf->keepalive_min_timeout;
|
||||
|
||||
} else {
|
||||
hc->keepalive_timeout = 0;
|
||||
}
|
||||
|
||||
ngx_add_timer(rev, clcf->keepalive_timeout - hc->keepalive_timeout);
|
||||
|
||||
if (rev->ready) {
|
||||
ngx_post_event(rev, &ngx_posted_events);
|
||||
@@ -3315,15 +3471,32 @@ ngx_http_set_keepalive(ngx_http_request_t *r)
|
||||
static void
|
||||
ngx_http_keepalive_handler(ngx_event_t *rev)
|
||||
{
|
||||
size_t size;
|
||||
ssize_t n;
|
||||
ngx_buf_t *b;
|
||||
ngx_connection_t *c;
|
||||
size_t size;
|
||||
ssize_t n;
|
||||
ngx_buf_t *b;
|
||||
ngx_connection_t *c;
|
||||
ngx_http_connection_t *hc;
|
||||
|
||||
c = rev->data;
|
||||
hc = c->data;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http keepalive handler");
|
||||
|
||||
if (!ngx_terminate
|
||||
&& !ngx_exiting
|
||||
&& rev->timedout
|
||||
&& hc->keepalive_timeout > 0)
|
||||
{
|
||||
c->idle = 1;
|
||||
ngx_reusable_connection(c, 1);
|
||||
|
||||
ngx_add_timer(rev, hc->keepalive_timeout);
|
||||
|
||||
hc->keepalive_timeout = 0;
|
||||
rev->timedout = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (rev->timedout || c->close) {
|
||||
ngx_http_close_connection(c);
|
||||
return;
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
#define NGX_HTTP_CONTINUE 100
|
||||
#define NGX_HTTP_SWITCHING_PROTOCOLS 101
|
||||
#define NGX_HTTP_PROCESSING 102
|
||||
#define NGX_HTTP_EARLY_HINTS 103
|
||||
|
||||
#define NGX_HTTP_OK 200
|
||||
#define NGX_HTTP_CREATED 201
|
||||
@@ -329,6 +330,8 @@ typedef struct {
|
||||
|
||||
ngx_chain_t *free;
|
||||
|
||||
ngx_msec_t keepalive_timeout;
|
||||
|
||||
unsigned ssl:1;
|
||||
unsigned proxy_protocol:1;
|
||||
} ngx_http_connection_t;
|
||||
@@ -458,6 +461,8 @@ struct ngx_http_request_s {
|
||||
|
||||
ngx_http_cleanup_t *cleanup;
|
||||
|
||||
in_port_t port;
|
||||
|
||||
unsigned count:16;
|
||||
unsigned subrequests:8;
|
||||
unsigned blocked:8;
|
||||
|
||||
@@ -870,7 +870,7 @@ ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b)
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
rc = ngx_http_parse_chunked(r, b, rb->chunked);
|
||||
rc = ngx_http_parse_chunked(r, b, rb->chunked, 0);
|
||||
|
||||
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_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) {
|
||||
|
||||
|
||||
+279
-35
@@ -48,6 +48,9 @@ static void ngx_http_upstream_send_request_handler(ngx_http_request_t *r,
|
||||
static void ngx_http_upstream_read_request_handler(ngx_http_request_t *r);
|
||||
static void ngx_http_upstream_process_header(ngx_http_request_t *r,
|
||||
ngx_http_upstream_t *u);
|
||||
static ngx_int_t ngx_http_upstream_process_early_hints(ngx_http_request_t *r,
|
||||
ngx_http_upstream_t *u);
|
||||
static void ngx_http_upstream_early_hints_writer(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_upstream_test_next(ngx_http_request_t *r,
|
||||
ngx_http_upstream_t *u);
|
||||
static ngx_int_t ngx_http_upstream_intercept_errors(ngx_http_request_t *r,
|
||||
@@ -113,6 +116,10 @@ static ngx_int_t ngx_http_upstream_process_set_cookie(ngx_http_request_t *r,
|
||||
static ngx_int_t
|
||||
ngx_http_upstream_process_cache_control(ngx_http_request_t *r,
|
||||
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,
|
||||
ngx_table_elt_t *h, ngx_uint_t offset);
|
||||
static ngx_int_t ngx_http_upstream_process_expires(ngx_http_request_t *r,
|
||||
@@ -1120,7 +1127,7 @@ ngx_http_upstream_cache_send(ngx_http_request_t *r, ngx_http_upstream_t *u)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
if (rc == NGX_AGAIN || rc == NGX_HTTP_UPSTREAM_EARLY_HINTS) {
|
||||
rc = NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
@@ -1764,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) {
|
||||
c->ssl->save_session = ngx_http_upstream_ssl_save_session;
|
||||
|
||||
@@ -2530,6 +2554,18 @@ ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rc == NGX_HTTP_UPSTREAM_EARLY_HINTS) {
|
||||
rc = ngx_http_upstream_process_early_hints(r, u);
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
rc = u->process_header(r);
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2567,6 +2603,148 @@ ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u)
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_upstream_process_early_hints(ngx_http_request_t *r,
|
||||
ngx_http_upstream_t *u)
|
||||
{
|
||||
u_char *p;
|
||||
ngx_uint_t i;
|
||||
ngx_list_part_t *part;
|
||||
ngx_table_elt_t *h, *ho;
|
||||
ngx_connection_t *c;
|
||||
|
||||
c = r->connection;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http upstream early hints");
|
||||
|
||||
if (u->conf->pass_early_hints) {
|
||||
|
||||
u->early_hints_length += u->buffer.pos - u->buffer.start;
|
||||
|
||||
if (u->early_hints_length <= (off_t) u->conf->buffer_size) {
|
||||
|
||||
part = &u->headers_in.headers.part;
|
||||
h = part->elts;
|
||||
|
||||
for (i = 0; /* void */; i++) {
|
||||
|
||||
if (i >= part->nelts) {
|
||||
if (part->next == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
part = part->next;
|
||||
h = part->elts;
|
||||
i = 0;
|
||||
}
|
||||
|
||||
if (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash,
|
||||
h[i].lowcase_key, h[i].key.len))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ho = ngx_list_push(&r->headers_out.headers);
|
||||
if (ho == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*ho = h[i];
|
||||
}
|
||||
|
||||
if (ngx_http_send_early_hints(r) == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (c->buffered) {
|
||||
if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
r->write_event_handler = ngx_http_upstream_early_hints_writer;
|
||||
}
|
||||
|
||||
} else {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"upstream sent too big early hints");
|
||||
}
|
||||
}
|
||||
|
||||
ngx_http_clean_header(r);
|
||||
|
||||
ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));
|
||||
u->headers_in.content_length_n = -1;
|
||||
u->headers_in.last_modified_time = -1;
|
||||
|
||||
if (ngx_list_init(&u->headers_in.headers, r->pool, 8,
|
||||
sizeof(ngx_table_elt_t))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_list_init(&u->headers_in.trailers, r->pool, 2,
|
||||
sizeof(ngx_table_elt_t))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
p = u->buffer.pos;
|
||||
|
||||
u->buffer.pos = u->buffer.start;
|
||||
|
||||
#if (NGX_HTTP_CACHE)
|
||||
|
||||
if (r->cache) {
|
||||
u->buffer.pos += r->cache->header_start;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
u->buffer.last = ngx_movemem(u->buffer.pos, p, u->buffer.last - p);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_upstream_early_hints_writer(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_connection_t *c;
|
||||
ngx_http_upstream_t *u;
|
||||
|
||||
c = r->connection;
|
||||
u = r->upstream;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
||||
"http upstream early hints writer");
|
||||
|
||||
c->log->action = "sending early hints to client";
|
||||
|
||||
if (ngx_http_write_filter(r, NULL) == NGX_ERROR) {
|
||||
ngx_http_upstream_finalize_request(r, u,
|
||||
NGX_HTTP_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!c->buffered) {
|
||||
if (!u->store && !r->post_action && !u->conf->ignore_client_abort) {
|
||||
r->write_event_handler =
|
||||
ngx_http_upstream_wr_check_broken_connection;
|
||||
|
||||
} else {
|
||||
r->write_event_handler = ngx_http_request_empty_handler;
|
||||
}
|
||||
}
|
||||
|
||||
if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
|
||||
ngx_http_upstream_finalize_request(r, u,
|
||||
NGX_HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_upstream_test_next(ngx_http_request_t *r, ngx_http_upstream_t *u)
|
||||
{
|
||||
@@ -4909,18 +5087,9 @@ ngx_http_upstream_process_cache_control(ngx_http_request_t *r,
|
||||
}
|
||||
|
||||
if (p) {
|
||||
n = 0;
|
||||
|
||||
for (p += offset; p < last; p++) {
|
||||
if (*p == ',' || *p == ';' || *p == ' ') {
|
||||
break;
|
||||
}
|
||||
|
||||
if (*p >= '0' && *p <= '9') {
|
||||
n = n * 10 + (*p - '0');
|
||||
continue;
|
||||
}
|
||||
n = ngx_http_upstream_process_delta_seconds(p + offset, last);
|
||||
|
||||
if (n == NGX_ERROR) {
|
||||
u->cacheable = 0;
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -4930,7 +5099,8 @@ ngx_http_upstream_process_cache_control(ngx_http_request_t *r,
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -4940,18 +5110,9 @@ extensions:
|
||||
23 - 1);
|
||||
|
||||
if (p) {
|
||||
n = 0;
|
||||
|
||||
for (p += 23; p < last; p++) {
|
||||
if (*p == ',' || *p == ';' || *p == ' ') {
|
||||
break;
|
||||
}
|
||||
|
||||
if (*p >= '0' && *p <= '9') {
|
||||
n = n * 10 + (*p - '0');
|
||||
continue;
|
||||
}
|
||||
n = ngx_http_upstream_process_delta_seconds(p + 23, last);
|
||||
|
||||
if (n == NGX_ERROR) {
|
||||
u->cacheable = 0;
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -4963,18 +5124,9 @@ extensions:
|
||||
p = ngx_strlcasestrn(start, last, (u_char *) "stale-if-error=", 15 - 1);
|
||||
|
||||
if (p) {
|
||||
n = 0;
|
||||
|
||||
for (p += 15; p < last; p++) {
|
||||
if (*p == ',' || *p == ';' || *p == ' ') {
|
||||
break;
|
||||
}
|
||||
|
||||
if (*p >= '0' && *p <= '9') {
|
||||
n = n * 10 + (*p - '0');
|
||||
continue;
|
||||
}
|
||||
n = ngx_http_upstream_process_delta_seconds(p + 15, last);
|
||||
|
||||
if (n == NGX_ERROR) {
|
||||
u->cacheable = 0;
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -4988,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
|
||||
ngx_http_upstream_process_expires(ngx_http_request_t *r, ngx_table_elt_t *h,
|
||||
ngx_uint_t offset)
|
||||
@@ -6728,6 +6915,7 @@ ngx_http_upstream_set_local(ngx_http_request_t *r, ngx_http_upstream_t *u,
|
||||
}
|
||||
|
||||
if (val.len == 0) {
|
||||
u->peer.local = NULL;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
@@ -6744,6 +6932,7 @@ ngx_http_upstream_set_local(ngx_http_request_t *r, ngx_http_upstream_t *u,
|
||||
if (rc != NGX_OK) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"invalid local address \"%V\"", &val);
|
||||
u->peer.local = NULL;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
@@ -6921,6 +7110,61 @@ ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf,
|
||||
}
|
||||
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_upstream_merge_ssl_passwords(ngx_conf_t *cf,
|
||||
ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev)
|
||||
{
|
||||
ngx_uint_t preserve;
|
||||
|
||||
ngx_conf_merge_ptr_value(conf->ssl_passwords, prev->ssl_passwords, NULL);
|
||||
|
||||
if (conf->ssl_certificate == NULL
|
||||
|| conf->ssl_certificate->value.len == 0
|
||||
|| conf->ssl_certificate_key == NULL)
|
||||
{
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (conf->ssl_certificate->lengths == NULL
|
||||
&& conf->ssl_certificate_key->lengths == NULL)
|
||||
{
|
||||
if (conf->ssl_passwords && conf->ssl_passwords->pool == NULL) {
|
||||
/* un-preserve empty password list */
|
||||
conf->ssl_passwords = NULL;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (conf->ssl_passwords && conf->ssl_passwords->pool != cf->temp_pool) {
|
||||
/* already preserved */
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
preserve = (conf->ssl_passwords == prev->ssl_passwords) ? 1 : 0;
|
||||
|
||||
conf->ssl_passwords = ngx_ssl_preserve_passwords(cf, conf->ssl_passwords);
|
||||
if (conf->ssl_passwords == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* special handling to keep a preserved ssl_passwords copy
|
||||
* in the previous configuration to inherit it to all children
|
||||
*/
|
||||
|
||||
if (preserve) {
|
||||
prev->ssl_passwords = conf->ssl_passwords;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_upstream_create_main_conf(ngx_conf_t *cf)
|
||||
{
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
|NGX_HTTP_UPSTREAM_FT_HTTP_429)
|
||||
|
||||
#define NGX_HTTP_UPSTREAM_INVALID_HEADER 40
|
||||
#define NGX_HTTP_UPSTREAM_EARLY_HINTS 41
|
||||
|
||||
|
||||
#define NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT 0x00000002
|
||||
@@ -184,6 +185,8 @@ typedef struct {
|
||||
ngx_flag_t request_buffering;
|
||||
ngx_flag_t pass_request_headers;
|
||||
ngx_flag_t pass_request_body;
|
||||
ngx_flag_t pass_trailers;
|
||||
ngx_flag_t pass_early_hints;
|
||||
|
||||
ngx_flag_t ignore_client_abort;
|
||||
ngx_flag_t intercept_errors;
|
||||
@@ -232,7 +235,6 @@ typedef struct {
|
||||
signed store:2;
|
||||
unsigned intercept_404:1;
|
||||
unsigned change_buffering:1;
|
||||
unsigned pass_trailers:1;
|
||||
unsigned preserve_output:1;
|
||||
|
||||
#if (NGX_HTTP_SSL || NGX_COMPAT)
|
||||
@@ -251,7 +253,7 @@ typedef struct {
|
||||
|
||||
ngx_str_t module;
|
||||
|
||||
NGX_COMPAT_BEGIN(2)
|
||||
NGX_COMPAT_BEGIN(6)
|
||||
NGX_COMPAT_END
|
||||
} ngx_http_upstream_conf_t;
|
||||
|
||||
@@ -354,6 +356,7 @@ struct ngx_http_upstream_s {
|
||||
|
||||
ngx_buf_t buffer;
|
||||
off_t length;
|
||||
off_t early_hints_length;
|
||||
|
||||
ngx_chain_t *out_bufs;
|
||||
ngx_chain_t *busy_bufs;
|
||||
@@ -387,6 +390,7 @@ struct ngx_http_upstream_s {
|
||||
|
||||
#if (NGX_HTTP_SSL || NGX_COMPAT)
|
||||
ngx_str_t ssl_name;
|
||||
ngx_str_t ssl_alpn_protocol;
|
||||
#endif
|
||||
|
||||
ngx_http_cleanup_pt *cleanup;
|
||||
@@ -437,6 +441,10 @@ char *ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
ngx_int_t ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf,
|
||||
ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev,
|
||||
ngx_str_t *default_hide_headers, ngx_hash_init_t *hash);
|
||||
#if (NGX_HTTP_SSL)
|
||||
ngx_int_t ngx_http_upstream_merge_ssl_passwords(ngx_conf_t *cf,
|
||||
ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev);
|
||||
#endif
|
||||
|
||||
|
||||
#define ngx_http_conf_upstream_srv_conf(uscf, module) \
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user