From mdounin at mdounin.ru Mon Apr 1 02:02:56 2024 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 1 Apr 2024 05:02:56 +0300 Subject: [nginx] Update mime-types In-Reply-To: References: <21d6fa47ad3cc9ef2a2fc54f85a7349f@elven.pw> <4943345f2efed54da9bddc75355fe1fa@elven.pw> Message-ID: Hello! On Sun, Mar 31, 2024 at 03:34:28PM +0300, Lafiel wrote: > Maxim Dounin ?????(?) 2024-03-31 00:12: > > Just for the record, I've checked IIS mappings as available in > > applicationHost.config on hosts with IIS installed. Not sure if > > there is a current mapping officially available online, but at > > least some not very old copy can be found here: > > > > https://github.com/aspnet/AspLabs/blob/main/src/dotnet-iis/src/dotnet-iis/Resources/applicationHost.config#L370 > > As in Apache, a large of extensions in mime types are specified > here for Excel and PowerPoint :) Yes, that's somewhat expected. > > Well, Boulder simply ignores the Accept request header, that is, > > it ignores client preference for a particular type, nothing more. > > It provides no information if changing the response type will > > affect various clients or not. > > Tried updating comit and added more information with examples. Thanks, but the information added seems to only confirm previously discussed points: application/pem-certificate-chain is not well suited as a generic type for PEM files, and not really used as such. Dropped for now, thanks for trying. > > I think we can consider this at some point later, if/when there > > will be more information available. > > Tried updating comit. Thanks for trying, let's postpone it for now. [...] -- Maxim Dounin http://mdounin.ru/ From lafiel at elven.pw Mon Apr 1 16:54:04 2024 From: lafiel at elven.pw (Lafiel) Date: Mon, 01 Apr 2024 19:54:04 +0300 Subject: [nginx] Update mime-types In-Reply-To: References: <21d6fa47ad3cc9ef2a2fc54f85a7349f@elven.pw> <4943345f2efed54da9bddc75355fe1fa@elven.pw> Message-ID: <634749a5006c12a3506368f177fcf421@elven.pw> Hello! Maxim Dounin ?????(?) 2024-04-01 05:02: > Thanks, but the information added seems to only confirm > previously > discussed points: application/pem-certificate-chain is not > well > suited as a generic type for PEM files, and not really used > as > such. Dropped for now, thanks for > trying. Then why do other services use different mime types for them? -- Best regards, Lafiel mailto:lafiel at elven.pw -------------- next part -------------- A non-text attachment was scrubbed... Name: 0xFAB0C3D2.asc Type: application/pgp-keys Size: 1461 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 228 bytes Desc: OpenPGP digital signature URL: From lafiel at elven.pw Mon Apr 1 19:51:01 2024 From: lafiel at elven.pw (Lafiel) Date: Mon, 01 Apr 2024 22:51:01 +0300 Subject: [nginx] Update mime-types In-Reply-To: References: <6fd7b4aae9f284157bcd51b2eb936b82@elven.pw> <21d6fa47ad3cc9ef2a2fc54f85a7349f@elven.pw> Message-ID: <87f4537b40b612de96f383ff82cff02c@elven.pw> Hello! Add new commits. -- Best regards, Lafiel mailto:lafiel at elven.pw -------------- next part -------------- A non-text attachment was scrubbed... Name: mime_types_03.patch Type: text/x-diff Size: 7510 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 0xFAB0C3D2.asc Type: application/pgp-keys Size: 1461 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 228 bytes Desc: OpenPGP digital signature URL: From mdounin at mdounin.ru Tue Apr 2 00:36:51 2024 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 2 Apr 2024 03:36:51 +0300 Subject: [nginx] Update mime-types In-Reply-To: <634749a5006c12a3506368f177fcf421@elven.pw> References: <4943345f2efed54da9bddc75355fe1fa@elven.pw> <634749a5006c12a3506368f177fcf421@elven.pw> Message-ID: Hello! On Mon, Apr 01, 2024 at 07:54:04PM +0300, Lafiel wrote: > Maxim Dounin ?????(?) 2024-04-01 05:02: > > Thanks, but the information added seems to only confirm previously > > discussed points: application/pem-certificate-chain is not well > > suited as a generic type for PEM files, and not really used as > > such. Dropped for now, thanks for trying. > > Then why do other services use different mime types for them? None of the mentioned services use application/pem-certificate-chain for PEM files. In particular, both letsencrypt.org and cacerts.digicert.com use application/x-pem-file for .pem files as mentioned in your commit log. That is, using application/pem-certificate-chain does not look like a valid option - both based on common sense and the examples services mentioned. As another example, cacert.org uses the .crt extension for the certificate in PEM format (see https://www.cacert.org/index.php?id=3), and it is reported to be application/x-x509-ca-cert, and the same for the .der extension (and DER format): $ curl -sI https://www.cacert.org/certs/root_X0F.crt | grep ^Content-Type Content-Type: application/x-x509-ca-cert $ curl -sI https://www.cacert.org/certs/root_X0F.der | grep ^Content-Type Content-Type: application/x-x509-ca-cert That is, the mapping nginx currently use is known to work. While application/x-pem-file might be a better option based on usage by more popular CAs, it is not clear if the change is needed and how it will affect usage. It is also highly questionable to change just ".pem" and not ".crt", which is widely used for certificates in PEM format. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Tue Apr 2 01:05:19 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 02 Apr 2024 04:05:19 +0300 Subject: [nginx-site] Documented max_commands in mail module. Message-ID: details: http://freenginx.org/hg/nginx-site/rev/d4639ff91e10 branches: changeset: 3069:d4639ff91e10 user: Maxim Dounin date: Mon Apr 01 04:08:07 2024 +0300 description: Documented max_commands in mail module. diffstat: xml/en/docs/mail/ngx_mail_core_module.xml | 18 +++++++++++++++++- xml/ru/docs/mail/ngx_mail_core_module.xml | 18 +++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diffs (70 lines): diff --git a/xml/en/docs/mail/ngx_mail_core_module.xml b/xml/en/docs/mail/ngx_mail_core_module.xml --- a/xml/en/docs/mail/ngx_mail_core_module.xml +++ b/xml/en/docs/mail/ngx_mail_core_module.xml @@ -10,7 +10,7 @@ + rev="23">
@@ -247,6 +247,22 @@ are specified. + +number +1000 +mail +server +1.25.5 + + +Sets the maximum number of commands allowed during authentication. +If the limit is reached and proxying to the backend is not started yet, +the connection is closed. + + + + + number 5 diff --git a/xml/ru/docs/mail/ngx_mail_core_module.xml b/xml/ru/docs/mail/ngx_mail_core_module.xml --- a/xml/ru/docs/mail/ngx_mail_core_module.xml +++ b/xml/ru/docs/mail/ngx_mail_core_module.xml @@ -10,7 +10,7 @@ + rev="23">
@@ -251,6 +251,22 @@ FreeBSD?9.0-STABLE) + +????? +1000 +mail +server +1.25.5 + + +?????? ???????????? ?????????? ??????, ??????????? ? ???????? ??????????????. +???? ??????????? ??????????, ? ????????????? ?? ?????? ?? ????????, +?????????? ???????????. + + + + + ????? 5 From mdounin at mdounin.ru Tue Apr 2 02:53:12 2024 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 2 Apr 2024 05:53:12 +0300 Subject: freenginx-1.25.5 changes draft Message-ID: Hello! Changes with freenginx 1.25.5 02 Apr 2024 *) Change: MIME type for the "bmp" extension has been changed to "image/bmp", for the "rar" extension to "application/vnd.rar", and the "deb" and "udeb" extensions now use the "application/vnd.debian.binary-package" MIME type. Thanks to Yuriy Izorkin. *) Change: the "client_max_body_size" directive is now used to limit total size of ignored chunk extensions and trailer headers when reading a request body using chunked transfer encoding. Thanks to Bartek Nowotarski. *) Change: now at most one empty line is allowed before a request. *) Feature: logging to syslog errors are now reported at most once per second. *) Feature: the "max_commands" directive in the mail proxy module. *) Bugfix: incorrect server name might be returned in responses when using the "server_tokens off" directive. Thanks to Tatsuya Kaneko and Antanas Pata?ius. *) Bugfix: when reading a request body from a fast connection reading for a long time was possible. ????????? ? freenginx 1.25.5 02.04.2024 *) ?????????: MIME-??? ??? ?????????? bmp ??????? ?? "image/bmp", ??? ?????????? rar ?? "application/vnd.rar", ? ??? ?????????? deb ? udeb ?????? ???????????? MIME-??? "application/vnd.debian.binary-package". ??????? ???? ????????. *) ?????????: ??? ?????? ???? ??????? ? ?????????????? chunked transfer encoding ????????? ?????? ???????????? chunk extensions ? trailer headers ?????? ????????? ?????????? client_max_body_size. ??????? Bartek Nowotarski. *) ?????????: ?????? ????? ???????? ??????????? ?? ????? ????? ?????? ??????. *) ??????????: ?????? ?????? ? syslog ?????? ??????????? ?? ???? ?????? ???? ? ???????. *) ??????????: ????????? max_commands ? ???????? ??????-???????. *) ???????????: ??? ????????????? ????????? "server_tokens off" ? ??????? ????? ???????????? ???????????? ??? ???????. ??????? Tatsuya Kaneko ? Antanas Pata?ius. *) ???????????: ??? ?????? ???? ??????? ?? ???????? ?????????? ???? ???????? ?????? ? ??????? ??????? ???????. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Tue Apr 2 18:41:19 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 02 Apr 2024 21:41:19 +0300 Subject: [nginx] freenginx-1.25.5-RELEASE Message-ID: details: http://freenginx.org/hg/nginx/rev/2956b59565c9 branches: changeset: 9243:2956b59565c9 user: Maxim Dounin date: Tue Apr 02 20:55:20 2024 +0300 description: freenginx-1.25.5-RELEASE diffstat: docs/xml/nginx/changes.xml | 88 ++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 88 insertions(+), 0 deletions(-) diffs (98 lines): diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml --- a/docs/xml/nginx/changes.xml +++ b/docs/xml/nginx/changes.xml @@ -7,6 +7,94 @@
+ + + + +MIME-??? ??? ?????????? bmp ??????? ?? "image/bmp", +??? ?????????? rar ?? "application/vnd.rar", +? ??? ?????????? deb ? udeb ?????? ???????????? +MIME-??? "application/vnd.debian.binary-package".
+??????? ???? ????????. +
+ +MIME type for the "bmp" extension has been changed to "image/bmp", +for the "rar" extension to "application/vnd.rar", +and the "deb" and "udeb" extensions now use +the "application/vnd.debian.binary-package" MIME type.
+Thanks to Yuriy Izorkin. +
+
+ + + +??? ?????? ???? ??????? ? ?????????????? chunked transfer encoding +????????? ?????? ???????????? chunk extensions ? trailer headers +?????? ????????? ?????????? client_max_body_size.
+??????? Bartek Nowotarski. +
+ +the "client_max_body_size" directive is now used to limit +total size of ignored chunk extensions and trailer headers +when reading a request body using chunked transfer encoding.
+Thanks to Bartek Nowotarski. +
+
+ + + +?????? ????? ???????? ??????????? ?? ????? ????? ?????? ??????. + + +now at most one empty line is allowed before a request. + + + + + +?????? ?????? ? syslog ?????? ??????????? ?? ???? ?????? ???? ? ???????. + + +logging to syslog errors are now reported at most once per second. + + + + + +????????? max_commands ? ???????? ??????-???????. + + +the "max_commands" directive in the mail proxy module. + + + + + +??? ????????????? ????????? "server_tokens off" +? ??????? ????? ???????????? ???????????? ??? ???????.
+??????? Tatsuya Kaneko ? Antanas Pata?ius. +
+ +incorrect server name might be returned in responses +when using the "server_tokens off" directive.
+Thanks to Tatsuya Kaneko and Antanas Pata?ius. +
+
+ + + +??? ?????? ???? ??????? ?? ???????? ?????????? +???? ???????? ?????? ? ??????? ??????? ???????. + + +when reading a request body from a fast connection +reading for a long time was possible. + + + +
+ + From mdounin at mdounin.ru Tue Apr 2 18:41:19 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 02 Apr 2024 21:41:19 +0300 Subject: [nginx] release-1.25.5 tag Message-ID: details: http://freenginx.org/hg/nginx/rev/ccd7e4022a24 branches: changeset: 9244:ccd7e4022a24 user: Maxim Dounin date: Tue Apr 02 20:55:21 2024 +0300 description: release-1.25.5 tag diffstat: .hgtags | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diffs (8 lines): diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -477,3 +477,4 @@ f8134640e8615448205785cf00b0bc810489b495 1d839f05409d1a50d0f15a2bf36547001f99ae40 release-1.25.2 294a3d07234f8f65d7b0e0b0e2c5b05c12c5da0a release-1.25.3 ab948bfa042d7a7b20c3e730d7e9675cc172324f release-1.25.4 +2956b59565c91baa79d13d6411f2404614c0134e release-1.25.5 From mdounin at mdounin.ru Tue Apr 2 18:45:09 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 02 Apr 2024 21:45:09 +0300 Subject: [nginx-site] freenginx-1.25.5 Message-ID: details: http://freenginx.org/hg/nginx-site/rev/619538194516 branches: changeset: 3070:619538194516 user: Maxim Dounin date: Tue Apr 02 21:11:37 2024 +0300 description: freenginx-1.25.5 diffstat: text/en/CHANGES | 28 ++++++++++++++++++++++++++++ text/ru/CHANGES.ru | 28 ++++++++++++++++++++++++++++ xml/index.xml | 7 +++++++ xml/versions.xml | 1 + 4 files changed, 64 insertions(+), 0 deletions(-) diffs (100 lines): diff --git a/text/en/CHANGES b/text/en/CHANGES --- a/text/en/CHANGES +++ b/text/en/CHANGES @@ -1,4 +1,32 @@ +Changes with freenginx 1.25.5 02 Apr 2024 + + *) Change: MIME type for the "bmp" extension has been changed to + "image/bmp", for the "rar" extension to "application/vnd.rar", and + the "deb" and "udeb" extensions now use the + "application/vnd.debian.binary-package" MIME type. + Thanks to Yuriy Izorkin. + + *) Change: the "client_max_body_size" directive is now used to limit + total size of ignored chunk extensions and trailer headers when + reading a request body using chunked transfer encoding. + Thanks to Bartek Nowotarski. + + *) Change: now at most one empty line is allowed before a request. + + *) Feature: logging to syslog errors are now reported at most once per + second. + + *) Feature: the "max_commands" directive in the mail proxy module. + + *) Bugfix: incorrect server name might be returned in responses when + using the "server_tokens off" directive. + Thanks to Tatsuya Kaneko and Antanas Pata?ius. + + *) Bugfix: when reading a request body from a fast connection reading + for a long time was possible. + + Changes with freenginx 1.25.4 20 Feb 2024 *) Change: now the "freenginx" name is used in responses. diff --git a/text/ru/CHANGES.ru b/text/ru/CHANGES.ru --- a/text/ru/CHANGES.ru +++ b/text/ru/CHANGES.ru @@ -1,4 +1,32 @@ +????????? ? freenginx 1.25.5 02.04.2024 + + *) ?????????: MIME-??? ??? ?????????? bmp ??????? ?? "image/bmp", ??? + ?????????? rar ?? "application/vnd.rar", ? ??? ?????????? deb ? udeb + ?????? ???????????? MIME-??? "application/vnd.debian.binary-package". + ??????? ???? ????????. + + *) ?????????: ??? ?????? ???? ??????? ? ?????????????? chunked transfer + encoding ????????? ?????? ???????????? chunk extensions ? trailer + headers ?????? ????????? ?????????? client_max_body_size. + ??????? Bartek Nowotarski. + + *) ?????????: ?????? ????? ???????? ??????????? ?? ????? ????? ?????? + ??????. + + *) ??????????: ?????? ?????? ? syslog ?????? ??????????? ?? ???? ?????? + ???? ? ???????. + + *) ??????????: ????????? max_commands ? ???????? ??????-???????. + + *) ???????????: ??? ????????????? ????????? "server_tokens off" ? + ??????? ????? ???????????? ???????????? ??? ???????. + ??????? Tatsuya Kaneko ? Antanas Pata?ius. + + *) ???????????: ??? ?????? ???? ??????? ?? ???????? ?????????? ???? + ???????? ?????? ? ??????? ??????? ???????. + + ????????? ? freenginx 1.25.4 20.02.2024 *) ?????????: ?????? ? ??????? ???????????? ??? "freenginx". diff --git a/xml/index.xml b/xml/index.xml --- a/xml/index.xml +++ b/xml/index.xml @@ -8,6 +8,13 @@ + + +freenginx-1.25.5 +mainline version has been released. + + + freenginx-1.25.4 diff --git a/xml/versions.xml b/xml/versions.xml --- a/xml/versions.xml +++ b/xml/versions.xml @@ -9,6 +9,7 @@ + From mdounin at mdounin.ru Tue Apr 2 22:11:03 2024 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 3 Apr 2024 01:11:03 +0300 Subject: [nginx] Update mime-types In-Reply-To: <87f4537b40b612de96f383ff82cff02c@elven.pw> References: <6fd7b4aae9f284157bcd51b2eb936b82@elven.pw> <21d6fa47ad3cc9ef2a2fc54f85a7349f@elven.pw> <87f4537b40b612de96f383ff82cff02c@elven.pw> Message-ID: Hello! On Mon, Apr 01, 2024 at 10:51:01PM +0300, Lafiel wrote: > Hello! > > Add new commits. > > > -- > Best regards, > Lafiel > mailto:lafiel at elven.pw > # HG changeset patch > # User Yuriy Izorkin > # Date 1711994502 -10800 > # Mon Apr 01 21:01:42 2024 +0300 > # Node ID e064324f144bf15d1b2dd563591c1c8080d7b596 > # Parent ddcedfa3a80996ac5c1407ecc35a58617b1ae62d > MIME: changed type for iso files to application/vnd.efi.iso. > > For .iso files, the application/vnd.efi.iso MIME type is used > according to IANA [1]. > > Although this media type were registered for the purpose of > using them with EFI, they were defined broadly to include > all uncompressed disk images [2]. > > Link for reference: > > [1] https://www.iana.org/assignments/media-types/application/vnd.efi.iso > [2] https://gitlab.freedesktop.org/xdg/shared-mime-info/-/merge_requests/220 > > diff -r ddcedfa3a809 -r e064324f144b conf/mime.types > --- a/conf/mime.types Sat Mar 30 05:10:40 2024 +0300 > +++ b/conf/mime.types Mon Apr 01 21:01:42 2024 +0300 > @@ -37,6 +37,7 @@ > application/rtf rtf; > application/vnd.apple.mpegurl m3u8; > application/vnd.debian.binary-package deb udeb; > + application/vnd.efi.iso iso; > application/vnd.google-earth.kml+xml kml; > application/vnd.google-earth.kmz kmz; > application/vnd.ms-excel xls; > @@ -75,7 +76,7 @@ > > application/octet-stream bin exe dll; > application/octet-stream dmg; > - application/octet-stream iso img; > + application/octet-stream img; > application/octet-stream msi msp msm; > > audio/midi mid midi kar; > # HG changeset patch > # User Yuriy Izorkin > # Date 1711994644 -10800 > # Mon Apr 01 21:04:04 2024 +0300 > # Node ID 4e30b3da1605d3888e09498039201f5c01a13c87 > # Parent e064324f144bf15d1b2dd563591c1c8080d7b596 > MIME: changed type for img files to application/vnd.efi.img. > > For .img files, the application/vnd.efi.img MIME type is used > according to IANA [1]. > > Although this media type were registered for the purpose of > using them with EFI, they were defined broadly to include > all uncompressed disk images [2]. > > Link for reference: > > [1] https://www.iana.org/assignments/media-types/application/vnd.efi.img > [2] https://gitlab.freedesktop.org/xdg/shared-mime-info/-/merge_requests/220 > > diff -r e064324f144b -r 4e30b3da1605 conf/mime.types > --- a/conf/mime.types Mon Apr 01 21:01:42 2024 +0300 > +++ b/conf/mime.types Mon Apr 01 21:04:04 2024 +0300 > @@ -37,6 +37,7 @@ > application/rtf rtf; > application/vnd.apple.mpegurl m3u8; > application/vnd.debian.binary-package deb udeb; > + application/vnd.efi.img img; > application/vnd.efi.iso iso; > application/vnd.google-earth.kml+xml kml; > application/vnd.google-earth.kmz kmz; > @@ -76,7 +77,6 @@ > > application/octet-stream bin exe dll; > application/octet-stream dmg; > - application/octet-stream img; > application/octet-stream msi msp msm; > > audio/midi mid midi kar; These two seems to be closely related and probably can be merged into one patch. Still, I'm somewhat sceptical about the idea of using vendor-specific type for general formats. Preserving existing application/octet-stream type might be a better option. Any specific reasons for the change? > # HG changeset patch > # User Yuriy Izorkin > # Date 1711997030 -10800 > # Mon Apr 01 21:43:50 2024 +0300 > # Node ID ac4a1f54e4a522334b2d92a25f361b89a2bb73c3 > # Parent 4e30b3da1605d3888e09498039201f5c01a13c87 > MIME: changed type for mid files to audio/sp-midi. > > For .mid files, the audio/sp-midi MIME type is used according > to IANA [1] and IETF [2]. > > Link for reference: > > [1] https://www.iana.org/assignments/media-types/audio/sp-midi > [2] https://datatracker.ietf.org/doc/draft-hamalainen-sp-midi-mimetype/ > > diff -r 4e30b3da1605 -r ac4a1f54e4a5 conf/mime.types > --- a/conf/mime.types Mon Apr 01 21:04:04 2024 +0300 > +++ b/conf/mime.types Mon Apr 01 21:43:50 2024 +0300 > @@ -79,9 +79,10 @@ > application/octet-stream dmg; > application/octet-stream msi msp msm; > > - audio/midi mid midi kar; > + audio/midi midi kar; > audio/mpeg mp3; > audio/ogg ogg; > + audio/sp-midi mid; > audio/x-m4a m4a; > audio/x-realaudio ra; > This looks wrong. Scalable Polyphony MIDI is a separate standard, which defines additional features. It does not mean that all MIDI files are SP-MIDI files. > # HG changeset patch > # User Yuriy Izorkin > # Date 1711998487 -10800 > # Mon Apr 01 22:08:07 2024 +0300 > # Node ID 2b9beb8ab5d2a1dced6fc8094907a0ea020faa4b > # Parent ac4a1f54e4a522334b2d92a25f361b89a2bb73c3 > MIME: update file extensions for audio/ogg type. > > For .oga, .ogg, .opus and .spx files, the MIME type is audio/ogg > > In addition to .ogg files, the audio/ogg MIME type contains files > .oga, .opus and .spx files [1]. > > In Apache also uses this file extensions [2]: > $ awk '/audio\/ogg/' mime.types > audio/ogg oga ogg spx opus > > Link for reference: > > [1] https://www.iana.org/assignments/media-types/audio/ogg > [2] https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types > > diff -r ac4a1f54e4a5 -r 2b9beb8ab5d2 conf/mime.types > --- a/conf/mime.types Mon Apr 01 21:43:50 2024 +0300 > +++ b/conf/mime.types Mon Apr 01 22:08:07 2024 +0300 > @@ -81,7 +81,7 @@ > > audio/midi midi kar; > audio/mpeg mp3; > - audio/ogg ogg; > + audio/ogg oga ogg opus; > audio/sp-midi mid; > audio/x-m4a m4a; > audio/x-realaudio ra; Both oga and opus seems to be almost unused: $ grep -E '^(ogg|oga|opus|spx),' Downloads/httparchive_exts.csv ogg,15195,29547 oga,383,751 opus,14,92 As well as spx, which is mentioned in the commit log, but not in the patch. > # HG changeset patch > # User Yuriy Izorkin > # Date 1712000395 -10800 > # Mon Apr 01 22:39:55 2024 +0300 > # Node ID 2535f4e69b764d9e7795db735f8f6f833e58883d > # Parent 2b9beb8ab5d2a1dced6fc8094907a0ea020faa4b > MIME: changed type for m4a to audio/mp4. > > For m4a files, the audio/mp4 MIME type is used in accordance > with section 2 of RFC 4337 [1]. > > In Apache also uses this MIME type [2]: > $ awk '/audio\/mp4/' mime.types > audio/mp4 m4a mp4a > > Link for reference: > > [1] https://www.rfc-editor.org/rfc/rfc4337#section-2 > [2] https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types > > diff -r 2b9beb8ab5d2 -r 2535f4e69b76 conf/mime.types > --- a/conf/mime.types Mon Apr 01 22:08:07 2024 +0300 > +++ b/conf/mime.types Mon Apr 01 22:39:55 2024 +0300 > @@ -80,10 +80,10 @@ > application/octet-stream msi msp msm; > > audio/midi midi kar; > + audio/mp4 m4a; > audio/mpeg mp3; > audio/ogg oga ogg opus; > audio/sp-midi mid; > - audio/x-m4a m4a; > audio/x-realaudio ra; > > video/3gpp 3gpp 3gp; If at all, this should be also change m4v, which is a similar mp4-based Apple format for video. I doubt it needs changing though. Any specific reasons for the change? -- Maxim Dounin http://mdounin.ru/ From lafiel at elven.pw Wed Apr 3 09:13:43 2024 From: lafiel at elven.pw (Lafiel) Date: Wed, 03 Apr 2024 12:13:43 +0300 Subject: [nginx] Update mime-types In-Reply-To: References: <6fd7b4aae9f284157bcd51b2eb936b82@elven.pw> <21d6fa47ad3cc9ef2a2fc54f85a7349f@elven.pw> <87f4537b40b612de96f383ff82cff02c@elven.pw> Message-ID: <5763befa5830add8e2b97a9b2473d80c@elven.pw> Hello! Maxim Dounin ?????(?) 2024-04-03 01:11: > These two seems to be closely related and probably can be > merged > into one > patch. > > Still, I'm somewhat sceptical about the idea of > using > vendor-specific type for general formats. Preserving > existing > application/octet-stream type might be a better > option. > > Any specific reasons for the > change? These types are recommended for booting a RAM disk over HTTP. Update commit. > This looks wrong. Scalable Polyphony MIDI is a separate > standard, > which defines additional features. It does not mean that all > MIDI > files are SP-MIDI > files. Ok. > Both oga and opus seems to be almost > unused: > > $ grep -E '^(ogg|oga|opus|spx),' > Downloads/httparchive_exts.csv > ogg,15195,29547 > oga,383,751 > opus,14,92 > > As well as spx, which is mentioned in the commit log, but not > in > the > patch. Added .spx files. Update commit and comments. Also I'm guessing that httparchive.org's statistics are not comprehensive enough. Example for iso files: ext,total_pages,total_requests iso,11,12 > If at all, this should be also change m4v, which is a > similar > mp4-based Apple format for video. I doubt it needs > changing > though. Any specific reasons for the > change? Later I planned to add changes for the video/mp4 mime type. Now I have added a commit. To formats OGG and MP4, there are application/ogg and application/mp4 types. Apache has them. Maybe they should be added too? -- Best regards, Lafiel mailto:lafiel at elven.pw -------------- next part -------------- A non-text attachment was scrubbed... Name: 0xFAB0C3D2.asc Type: application/pgp-keys Size: 1461 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 228 bytes Desc: OpenPGP digital signature URL: From lafiel at elven.pw Wed Apr 3 09:15:14 2024 From: lafiel at elven.pw (Lafiel) Date: Wed, 03 Apr 2024 12:15:14 +0300 Subject: [nginx] Update mime-types In-Reply-To: References: <6fd7b4aae9f284157bcd51b2eb936b82@elven.pw> <21d6fa47ad3cc9ef2a2fc54f85a7349f@elven.pw> <87f4537b40b612de96f383ff82cff02c@elven.pw> Message-ID: <74f59de405f0342fbc16acd29038d778@elven.pw> Hello! Maxim Dounin ?????(?) 2024-04-03 01:11: > These two seems to be closely related and probably can be > merged > into one > patch. > > Still, I'm somewhat sceptical about the idea of > using > vendor-specific type for general formats. Preserving > existing > application/octet-stream type might be a better > option. > > Any specific reasons for the > change? These types are recommended for booting a RAM disk over HTTP. Update commit. > This looks wrong. Scalable Polyphony MIDI is a separate > standard, > which defines additional features. It does not mean that all > MIDI > files are SP-MIDI > files. Ok. > Both oga and opus seems to be almost > unused: > > $ grep -E '^(ogg|oga|opus|spx),' > Downloads/httparchive_exts.csv > ogg,15195,29547 > oga,383,751 > opus,14,92 > > As well as spx, which is mentioned in the commit log, but not > in > the > patch. Added .spx files. Update commit and comments. Also I'm guessing that httparchive.org's statistics are not comprehensive enough. Example for iso files: ext,total_pages,total_requests iso,11,12 > If at all, this should be also change m4v, which is a > similar > mp4-based Apple format for video. I doubt it needs > changing > though. Any specific reasons for the > change? Later I planned to add changes for the video/mp4 mime type. Now I have added a commit. To formats OGG and MP4, there are application/ogg and application/mp4 types. Apache has them. Maybe they should be added too? -- Best regards, Lafiel mailto:lafiel at elven.pw -------------- next part -------------- A non-text attachment was scrubbed... Name: mime_types_03.patch Type: text/x-diff Size: 7864 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 0xFAB0C3D2.asc Type: application/pgp-keys Size: 1461 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 228 bytes Desc: OpenPGP digital signature URL: From mdounin at mdounin.ru Mon Apr 8 20:48:20 2024 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 8 Apr 2024 23:48:20 +0300 Subject: [nginx] Update mime-types In-Reply-To: <74f59de405f0342fbc16acd29038d778@elven.pw> References: <6fd7b4aae9f284157bcd51b2eb936b82@elven.pw> <21d6fa47ad3cc9ef2a2fc54f85a7349f@elven.pw> <87f4537b40b612de96f383ff82cff02c@elven.pw> <74f59de405f0342fbc16acd29038d778@elven.pw> Message-ID: Hello! On Wed, Apr 03, 2024 at 12:15:14PM +0300, Lafiel wrote: > Hello! > > Maxim Dounin ?????(?) 2024-04-03 01:11: > > These two seems to be closely related and probably can be merged > > into one patch. > > > > Still, I'm somewhat sceptical about the idea of using > > vendor-specific type for general formats. Preserving existing > > application/octet-stream type might be a better option. > > > > Any specific reasons for the change? > > These types are recommended for booting a RAM disk over HTTP. > Update commit. Interesting. The fun fact is that it won't work anyway, since MIME types as checked by EDK II HTTP Boot driver are misspelled, notably it checks for "application/vnd.efi-img" (note "efi-img" instead of "efi.img") and "application/vnd.efi-iso" (the same). For reference, as of current master: https://github.com/tianocore/edk2/blob/b7f8779/NetworkPkg/HttpBootDxe/HttpBootDxe.h#L79 https://github.com/tianocore/edk2/blob/b7f8779/NetworkPkg/HttpBootDxe/HttpBootSupport.c#L661 Note well that if the MIME type is not matched, extension-based matching is used (wiki page linked says "if "Content-Type" header is not present", which is a quite different behaviour). Given the wrong MIME types in matching, extension-based matching is the only code path which works in practice, and any MIME type, including "application/binary", is expected to work well. Dropped for now, thanks. [...] > > Both oga and opus seems to be almost unused: > > > > $ grep -E '^(ogg|oga|opus|spx),' Downloads/httparchive_exts.csv > > ogg,15195,29547 > > oga,383,751 > > opus,14,92 > > > > As well as spx, which is mentioned in the commit log, but not in > > the patch. > > Added .spx files. > Update commit and comments. > > Also I'm guessing that httparchive.org's statistics are not > comprehensive enough. > Example for iso files: > ext,total_pages,total_requests > iso,11,12 Sure, as discussed previously, HTTP Archive data is mostly focused on how sites are built, and not on various files available for download (https://freenginx.org/pipermail/nginx-devel/2024-March/000099.html). Other sources to demonstrate significance of particular extensions are welcome. (Also, Theora support seems to be removed from browsers, see https://www.phoronix.com/news/Mozilla-Eyes-Dropping-Theora.) Dropped, thanks for trying. > > If at all, this should be also change m4v, which is a similar > > mp4-based Apple format for video. I doubt it needs changing > > though. Any specific reasons for the change? > > Later I planned to add changes for the video/mp4 mime type. Now I > have added a commit. Both m4a and m4v use the same container format as mp4, but they are not mp4. Some of the files might be compatible with mp4, but that's not something guaranteed. As such, using audio/mp4 and video/mp4 types for them is questionable. Hence the question about reasons. Dropped for now, thanks for trying. > To formats OGG and MP4, there are application/ogg and application/mp4 > types. Apache has them. Maybe they should be added too? I don't think it is a good idea to add types unless there practical requests. [...] -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Tue Apr 9 00:13:43 2024 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 9 Apr 2024 03:13:43 +0300 Subject: freenginx-1.26.0 draft Message-ID: Hello! Below are patches for the freenginx-1.26.0 release, and corresponding changes to the site. Comments are welcome. # HG changeset patch # User Maxim Dounin # Date 1712621110 -10800 # Tue Apr 09 03:05:10 2024 +0300 # Branch stable-1.26 # Node ID 4bf1593032eb5fd4af2475d84cb95b638ccadc5d # Parent ccd7e4022a2498a930c9e0927a9820f7ea17ae13 Stable branch. diff --git a/src/core/nginx.h b/src/core/nginx.h --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1025005 -#define NGINX_VERSION "1.25.5" +#define nginx_version 1026000 +#define NGINX_VERSION "1.26.0" #define NGINX_NAME "freenginx" #define NGINX_VER NGINX_NAME "/" NGINX_VERSION # HG changeset patch # User Maxim Dounin # Date 1712621234 -10800 # Tue Apr 09 03:07:14 2024 +0300 # Branch stable-1.26 # Node ID d68e71aa551d87a1dda3173134169376aa786bcb # Parent 4bf1593032eb5fd4af2475d84cb95b638ccadc5d freenginx-1.26.0-RELEASE diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml --- a/docs/xml/nginx/changes.xml +++ b/docs/xml/nginx/changes.xml @@ -7,6 +7,20 @@
+ + + + +?????????? ????? 1.26.x. + + +1.26.x stable branch. + + + + + + # HG changeset patch # User Maxim Dounin # Date 1712621235 -10800 # Tue Apr 09 03:07:15 2024 +0300 # Branch stable-1.26 # Node ID acb6b8f4bfd884df9831f94beacc256742d1bd19 # Parent d68e71aa551d87a1dda3173134169376aa786bcb release-1.26.0 tag diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -478,3 +478,4 @@ 1d839f05409d1a50d0f15a2bf36547001f99ae40 294a3d07234f8f65d7b0e0b0e2c5b05c12c5da0a release-1.25.3 ab948bfa042d7a7b20c3e730d7e9675cc172324f release-1.25.4 2956b59565c91baa79d13d6411f2404614c0134e release-1.25.5 +d68e71aa551d87a1dda3173134169376aa786bcb release-1.26.0 # HG changeset patch # User Maxim Dounin # Date 1712621506 -10800 # Tue Apr 09 03:11:46 2024 +0300 # Node ID 036ded8db5dffc2419872d250f09428e40a0fc30 # Parent 619538194516298f17b2ec7fff283c8607394b96 freenginx-1.26.0 diff --git a/text/en/CHANGES b/text/en/CHANGES-1.26 copy from text/en/CHANGES copy to text/en/CHANGES-1.26 --- a/text/en/CHANGES +++ b/text/en/CHANGES-1.26 @@ -1,4 +1,9 @@ +Changes with freenginx 1.26.0 09 Apr 2024 + + *) 1.26.x stable branch. + + Changes with freenginx 1.25.5 02 Apr 2024 *) Change: MIME type for the "bmp" extension has been changed to diff --git a/text/ru/CHANGES.ru b/text/ru/CHANGES.ru-1.26 copy from text/ru/CHANGES.ru copy to text/ru/CHANGES.ru-1.26 --- a/text/ru/CHANGES.ru +++ b/text/ru/CHANGES.ru-1.26 @@ -1,4 +1,9 @@ +????????? ? freenginx 1.26.0 09.04.2024 + + *) ?????????? ????? 1.26.x. + + ????????? ? freenginx 1.25.5 02.04.2024 *) ?????????: MIME-??? ??? ?????????? bmp ??????? ?? "image/bmp", ??? diff --git a/xml/en/download.xml b/xml/en/download.xml --- a/xml/en/download.xml +++ b/xml/en/download.xml @@ -8,7 +8,7 @@
+ rev="6">
@@ -17,6 +17,14 @@
+ +
+ + + +
+ +
diff --git a/xml/index.xml b/xml/index.xml --- a/xml/index.xml +++ b/xml/index.xml @@ -8,6 +8,20 @@ + + +freenginx-1.26.0 +stable version has been released, +incorporating new features and bug fixes from the 1.25.x mainline branch — +including +experimental +HTTP/3 support, +improved mitigation of various DoS attacks, +fixes in AIO handling, +and more. + + + freenginx-1.25.5 diff --git a/xml/ru/download.xml b/xml/ru/download.xml --- a/xml/ru/download.xml +++ b/xml/ru/download.xml @@ -8,7 +8,7 @@
+ rev="6">
@@ -18,6 +18,13 @@
+
+ + + +
+ +
diff --git a/xml/versions.xml b/xml/versions.xml --- a/xml/versions.xml +++ b/xml/versions.xml @@ -10,6 +10,14 @@ + + + + + + + + @@ -19,7 +27,7 @@ - + -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Tue Apr 9 13:28:14 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 09 Apr 2024 16:28:14 +0300 Subject: [nginx] Stable branch. Message-ID: details: http://freenginx.org/hg/nginx/rev/4bf1593032eb branches: stable-1.26 changeset: 9245:4bf1593032eb user: Maxim Dounin date: Tue Apr 09 03:05:10 2024 +0300 description: Stable branch. diffstat: src/core/nginx.h | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diffs (14 lines): diff --git a/src/core/nginx.h b/src/core/nginx.h --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1025005 -#define NGINX_VERSION "1.25.5" +#define nginx_version 1026000 +#define NGINX_VERSION "1.26.0" #define NGINX_NAME "freenginx" #define NGINX_VER NGINX_NAME "/" NGINX_VERSION From mdounin at mdounin.ru Tue Apr 9 13:28:14 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 09 Apr 2024 16:28:14 +0300 Subject: [nginx] freenginx-1.26.0-RELEASE Message-ID: details: http://freenginx.org/hg/nginx/rev/d68e71aa551d branches: stable-1.26 changeset: 9246:d68e71aa551d user: Maxim Dounin date: Tue Apr 09 03:07:14 2024 +0300 description: freenginx-1.26.0-RELEASE diffstat: docs/xml/nginx/changes.xml | 14 ++++++++++++++ 1 files changed, 14 insertions(+), 0 deletions(-) diffs (24 lines): diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml --- a/docs/xml/nginx/changes.xml +++ b/docs/xml/nginx/changes.xml @@ -7,6 +7,20 @@
+ + + + +?????????? ????? 1.26.x. + + +1.26.x stable branch. + + + + + + From mdounin at mdounin.ru Tue Apr 9 13:28:14 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 09 Apr 2024 16:28:14 +0300 Subject: [nginx] release-1.26.0 tag Message-ID: details: http://freenginx.org/hg/nginx/rev/acb6b8f4bfd8 branches: stable-1.26 changeset: 9247:acb6b8f4bfd8 user: Maxim Dounin date: Tue Apr 09 03:07:15 2024 +0300 description: release-1.26.0 tag diffstat: .hgtags | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diffs (8 lines): diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -478,3 +478,4 @@ 1d839f05409d1a50d0f15a2bf36547001f99ae40 294a3d07234f8f65d7b0e0b0e2c5b05c12c5da0a release-1.25.3 ab948bfa042d7a7b20c3e730d7e9675cc172324f release-1.25.4 2956b59565c91baa79d13d6411f2404614c0134e release-1.25.5 +d68e71aa551d87a1dda3173134169376aa786bcb release-1.26.0 From mdounin at mdounin.ru Tue Apr 9 13:28:57 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 09 Apr 2024 16:28:57 +0300 Subject: [nginx-site] freenginx-1.26.0 Message-ID: details: http://freenginx.org/hg/nginx-site/rev/036ded8db5df branches: changeset: 3071:036ded8db5df user: Maxim Dounin date: Tue Apr 09 03:11:46 2024 +0300 description: freenginx-1.26.0 diffstat: text/en/CHANGES-1.26 | 5 +++++ text/ru/CHANGES.ru-1.26 | 5 +++++ xml/en/download.xml | 10 +++++++++- xml/index.xml | 14 ++++++++++++++ xml/ru/download.xml | 9 ++++++++- xml/versions.xml | 10 +++++++++- 6 files changed, 50 insertions(+), 3 deletions(-) diffs (134 lines): diff --git a/text/en/CHANGES b/text/en/CHANGES-1.26 copy from text/en/CHANGES copy to text/en/CHANGES-1.26 --- a/text/en/CHANGES +++ b/text/en/CHANGES-1.26 @@ -1,4 +1,9 @@ +Changes with freenginx 1.26.0 09 Apr 2024 + + *) 1.26.x stable branch. + + Changes with freenginx 1.25.5 02 Apr 2024 *) Change: MIME type for the "bmp" extension has been changed to diff --git a/text/ru/CHANGES.ru b/text/ru/CHANGES.ru-1.26 copy from text/ru/CHANGES.ru copy to text/ru/CHANGES.ru-1.26 --- a/text/ru/CHANGES.ru +++ b/text/ru/CHANGES.ru-1.26 @@ -1,4 +1,9 @@ +????????? ? freenginx 1.26.0 09.04.2024 + + *) ?????????? ????? 1.26.x. + + ????????? ? freenginx 1.25.5 02.04.2024 *) ?????????: MIME-??? ??? ?????????? bmp ??????? ?? "image/bmp", ??? diff --git a/xml/en/download.xml b/xml/en/download.xml --- a/xml/en/download.xml +++ b/xml/en/download.xml @@ -8,7 +8,7 @@
+ rev="6">
@@ -17,6 +17,14 @@
+ +
+ + + +
+ +
diff --git a/xml/index.xml b/xml/index.xml --- a/xml/index.xml +++ b/xml/index.xml @@ -8,6 +8,20 @@ + + +freenginx-1.26.0 +stable version has been released, +incorporating new features and bug fixes from the 1.25.x mainline branch — +including +experimental +HTTP/3 support, +improved mitigation of various DoS attacks, +fixes in AIO handling, +and more. + + + freenginx-1.25.5 diff --git a/xml/ru/download.xml b/xml/ru/download.xml --- a/xml/ru/download.xml +++ b/xml/ru/download.xml @@ -8,7 +8,7 @@
+ rev="6">
@@ -18,6 +18,13 @@
+
+ + + +
+ +
diff --git a/xml/versions.xml b/xml/versions.xml --- a/xml/versions.xml +++ b/xml/versions.xml @@ -10,6 +10,14 @@ + + + + + + + + @@ -19,7 +27,7 @@ - + From mdounin at mdounin.ru Tue Apr 9 16:12:47 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Tue, 09 Apr 2024 19:12:47 +0300 Subject: [PATCH 0 of 9] site name cleanup Message-ID: Hello! Here is a patch series mostly focused on using the "freenginx" name on the site where appropriate. Review/commends are welcome. -- Maxim Dounin From mdounin at mdounin.ru Tue Apr 9 16:12:48 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Tue, 09 Apr 2024 19:12:48 +0300 Subject: [PATCH 1 of 9] Renamed news page In-Reply-To: References: Message-ID: <7f493ec56c127d8363a2.1712679168@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1712675656 -10800 # Tue Apr 09 18:14:16 2024 +0300 # Node ID 7f493ec56c127d8363a2f49cb5aaedef471136b6 # Parent 036ded8db5dffc2419872d250f09428e40a0fc30 Renamed news page. diff --git a/xml/index.xml b/xml/index.xml --- a/xml/index.xml +++ b/xml/index.xml @@ -6,7 +6,7 @@ - + From mdounin at mdounin.ru Tue Apr 9 16:12:49 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Tue, 09 Apr 2024 19:12:49 +0300 Subject: [PATCH 2 of 9] Rewritten about page In-Reply-To: References: Message-ID: <5d894c73896f3d8f01cd.1712679169@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1712675660 -10800 # Tue Apr 09 18:14:20 2024 +0300 # Node ID 5d894c73896f3d8f01cd86e1282c6d078b1c8ebb # Parent 7f493ec56c127d8363a2f49cb5aaedef471136b6 Rewritten about page. diff --git a/xml/en/index.xml b/xml/en/index.xml --- a/xml/en/index.xml +++ b/xml/en/index.xml @@ -5,33 +5,20 @@ -
+ rev="164">
-nginx [engine x] is an HTTP and reverse proxy server, +freenginx is an effort to preserve free and open development of +nginx [engine x], an HTTP and reverse proxy server, a mail proxy server, and a generic TCP/UDP proxy server, originally written by Igor Sysoev. -For a long time, it has been running -on many heavily loaded Russian sites including -Yandex, -Mail.Ru, -VK, and -Rambler. -According to Netcraft, nginx served or proxied -20.71% -busiest sites in January 2024. -Here are some of the success stories: -Dropbox, -Netflix, -Wordpress.com, -FastMail.FM. diff --git a/xml/ru/index.xml b/xml/ru/index.xml --- a/xml/ru/index.xml +++ b/xml/ru/index.xml @@ -5,33 +5,20 @@ -
+ rev="164">
-nginx [engine x]—??? HTTP-?????? ? ???????? ??????-??????, -???????? ??????-??????, -? ????? TCP/UDP ??????-?????? ?????? ??????????, -?????????? ?????????? ?????? ????????. -??? ?????????? ????? ?? ??????????? -??????? ?????? ????????????????? ?????????? ??????, ????? ??? -??????, -Mail.Ru, -????????? ? -???????. -???????? ?????????? Netcraft nginx ?????????? ??? ??????????? -20.71% -????? ??????????? ?????? ? ?????? 2024 ????. -??? ????????? ??????? ????????? ????????? nginx (?????? ?? ?????????? ?????): -Dropbox, -Netflix, -Wordpress.com, -FastMail.FM. +freenginx — ??????, ??????????? ????????? ? ???????? ?????????? +nginx [engine x], HTTP-??????? ? ????????? ??????-???????, +????????? ??????-???????, +? ????? TCP/UDP ??????-??????? ?????? ??????????, +?????????? ??????????? ?????? ????????. From mdounin at mdounin.ru Tue Apr 9 16:12:50 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Tue, 09 Apr 2024 19:12:50 +0300 Subject: [PATCH 3 of 9] Renamed security advisories page In-Reply-To: References: Message-ID: <4bad1151981536430aae.1712679170@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1712675710 -10800 # Tue Apr 09 18:15:10 2024 +0300 # Node ID 4bad1151981536430aaefac29ce16a88aca01acd # Parent 5d894c73896f3d8f01cd86e1282c6d078b1c8ebb Renamed security advisories page. diff --git a/xml/en/security_advisories.xml b/xml/en/security_advisories.xml --- a/xml/en/security_advisories.xml +++ b/xml/en/security_advisories.xml @@ -5,7 +5,7 @@ -
@@ -13,7 +13,7 @@
-All nginx security issues should be reported to +All freenginx security issues should be reported to security-alert at freenginx.org. From mdounin at mdounin.ru Tue Apr 9 16:12:51 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Tue, 09 Apr 2024 19:12:51 +0300 Subject: [PATCH 4 of 9] Renamed download page In-Reply-To: References: Message-ID: <6fcdefd1df4ed9c8b884.1712679171@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1712675771 -10800 # Tue Apr 09 18:16:11 2024 +0300 # Node ID 6fcdefd1df4ed9c8b88485a1fd5d7afff45f054c # Parent 4bad1151981536430aaefac29ce16a88aca01acd Renamed download page. diff --git a/xml/en/download.xml b/xml/en/download.xml --- a/xml/en/download.xml +++ b/xml/en/download.xml @@ -5,10 +5,10 @@ -
+ rev="7">
diff --git a/xml/ru/download.xml b/xml/ru/download.xml --- a/xml/ru/download.xml +++ b/xml/ru/download.xml @@ -5,10 +5,10 @@ -
+ rev="7">
From mdounin at mdounin.ru Tue Apr 9 16:12:52 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Tue, 09 Apr 2024 19:12:52 +0300 Subject: [PATCH 5 of 9] Updated Windows archive names In-Reply-To: References: Message-ID: # HG changeset patch # User Maxim Dounin # Date 1712676021 -10800 # Tue Apr 09 18:20:21 2024 +0300 # Node ID be1ef1d56e96bad2724a59b2400ade1d334986ab # Parent 6fcdefd1df4ed9c8b88485a1fd5d7afff45f054c Updated Windows archive names. diff --git a/xml/en/docs/windows.xml b/xml/en/docs/windows.xml --- a/xml/en/docs/windows.xml +++ b/xml/en/docs/windows.xml @@ -5,20 +5,20 @@ -
+ rev="6">
-Version of nginx for Windows uses the native Win32 API (not the Cygwin emulation -layer). +Version of freenginx for Windows uses the native Win32 API +(not the Cygwin emulation layer). Only the select and poll (1.15.9) connection processing methods are currently used, so high performance and scalability should not be expected. -Due to this and some other known issues version of nginx for Windows +Due to this and some other known issues version of freenginx for Windows is considered to be a beta version. At this time, it provides almost the same functionality as a UNIX version of nginx except for @@ -26,18 +26,18 @@ XSLT filter, image filter, GeoIP module, -To install nginx/Windows, download +To install freenginx/Windows, download the latest mainline version distribution (), since the mainline branch of nginx contains all known fixes. Then unpack the distribution, go to the -nginx- +freenginx- directory, and run nginx. Here is an example for the drive C: root directory: cd c:\ -unzip nginx-.zip -cd nginx- +unzip freenginx-.zip +cd freenginx- start nginx @@ -63,10 +63,10 @@ for the reason in the logs\error.l -nginx/Windows uses the directory where it has been run as the prefix +freenginx/Windows uses the directory where it has been run as the prefix for relative paths in the configuration. In the example above, the prefix is -C:\nginx-\. +C:\freenginx-\. Paths in a configuration file must be specified in UNIX-style using forward slashes: @@ -77,7 +77,7 @@ root C:/web/html; -nginx/Windows runs as a standard console application (not a service), +freenginx/Windows runs as a standard console application (not a service), and it can be managed using the following commands: diff --git a/xml/ru/docs/windows.xml b/xml/ru/docs/windows.xml --- a/xml/ru/docs/windows.xml +++ b/xml/ru/docs/windows.xml @@ -5,7 +5,7 @@ -
@@ -13,11 +13,11 @@
-?????? nginx ??? Windows ?????????? ???????? Win32 API (?? ???????? Cygwin). +?????? freenginx ??? Windows ?????????? ???????? Win32 API (?? ???????? Cygwin). ? ????????? ?????? ? ???????? ??????? ????????? ?????????? ???????????? select ? poll (1.15.9), ??????? ?? ????? ??????? ??????? ?????????????????? ? ????????????????. -? ???? ????? ? ???? ?????? ????????? ??????? ?????? nginx ??? Windows +? ???? ????? ? ???? ?????? ????????? ??????? ?????? freenginx ??? Windows ??????????????? ???? ??? ????-??????. ?? ?????? ?????? ? ??? ???????? ??????????? ??? ????????????????, ??? ? ? ?????? nginx ??? UNIX, ?? ??????????? @@ -25,22 +25,22 @@ XSLT-???????, ??????? ???????????, ?????? GeoIP ? ??????????? ????? Perl. -????? ?????????? nginx/Windows, ???????? +????? ?????????? freenginx/Windows, ???????? ??????????? ????????? ???????? ?????? (), ????????? ???????? ????? nginx ???????? ??? ????????? ???????????. ????? ?????????? ???????????, ????????? ? ??????? -nginx- +freenginx- ? ????????? nginx. ??? ?????? ??? ????????? ???????? ?? ????? C: cd c:\ -unzip nginx-.zip -cd nginx- +unzip freenginx-.zip +cd freenginx- start nginx -????? ??????? ???????? nginx, ????????? ??????? ????????? ?????? +????? ??????? ???????? freenginx, ????????? ??????? ????????? ?????? tasklist: @@ -62,10 +62,10 @@ nginx.exe 1332 Console 0 3?112 K -nginx/Windows ?????????? ???????, ? ??????? ?? ??? ???????, ? ???????? +freenginx/Windows ?????????? ???????, ? ??????? ?? ??? ???????, ? ???????? ???????? ??? ????????????? ????? ? ????????????. ? ??????????????? ??????? ????????? ???????? -C:\nginx-\. +C:\freenginx-\. ???? ? ???????????????? ????? ?????? ?????????? ? UNIX-????? ? ?????????????? ?????? ??????: @@ -76,7 +76,7 @@ root C:/web/html; -nginx/Windows ???????? ??? ??????????? ?????????? ?????????? (?? ??????) +freenginx/Windows ???????? ??? ??????????? ?????????? ?????????? (?? ??????) ? ??????????? ??? ?????? ????????? ??????:
From mdounin at mdounin.ru Tue Apr 9 16:12:53 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Tue, 09 Apr 2024 19:12:53 +0300 Subject: [PATCH 6 of 9] Renamed documentation page In-Reply-To: References: Message-ID: <07f4d29a90c0eef8d73f.1712679173@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1712676074 -10800 # Tue Apr 09 18:21:14 2024 +0300 # Node ID 07f4d29a90c0eef8d73f7c2f8ab32916327d100d # Parent be1ef1d56e96bad2724a59b2400ade1d334986ab Renamed documentation page. diff --git a/xml/en/docs/index.xml b/xml/en/docs/index.xml --- a/xml/en/docs/index.xml +++ b/xml/en/docs/index.xml @@ -5,10 +5,10 @@ -
diff --git a/xml/ru/docs/index.xml b/xml/ru/docs/index.xml --- a/xml/ru/docs/index.xml +++ b/xml/ru/docs/index.xml @@ -5,10 +5,10 @@ -
From mdounin at mdounin.ru Tue Apr 9 16:12:54 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Tue, 09 Apr 2024 19:12:54 +0300 Subject: [PATCH 7 of 9] Renamed support page In-Reply-To: References: Message-ID: <7437050401cf5b760e63.1712679174@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1712676361 -10800 # Tue Apr 09 18:26:01 2024 +0300 # Node ID 7437050401cf5b760e63f56440480c064eb73fca # Parent 07f4d29a90c0eef8d73f7c2f8ab32916327d100d Renamed support page. diff --git a/xml/en/support.xml b/xml/en/support.xml --- a/xml/en/support.xml +++ b/xml/en/support.xml @@ -5,10 +5,10 @@ -
+ rev="13">
diff --git a/xml/ru/support.xml b/xml/ru/support.xml --- a/xml/ru/support.xml +++ b/xml/ru/support.xml @@ -5,10 +5,10 @@ -
+ rev="13">
From mdounin at mdounin.ru Tue Apr 9 16:12:55 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Tue, 09 Apr 2024 19:12:55 +0300 Subject: [PATCH 8 of 9] Changed name in Russian about menu link In-Reply-To: References: Message-ID: <03b5cd59c55fdbc40744.1712679175@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1712676375 -10800 # Tue Apr 09 18:26:15 2024 +0300 # Node ID 03b5cd59c55fdbc4074499127f79370ec96d3d44 # Parent 7437050401cf5b760e63f56440480c064eb73fca Changed name in Russian about menu link. diff --git a/xml/menu.xml b/xml/menu.xml --- a/xml/menu.xml +++ b/xml/menu.xml @@ -35,7 +35,7 @@ ??????? - ?? nginx + ? freenginx ??????? ???????????? ???????????? From mdounin at mdounin.ru Tue Apr 9 16:12:56 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Tue, 09 Apr 2024 19:12:56 +0300 Subject: [PATCH 9 of 9] Removed books from menu In-Reply-To: References: Message-ID: # HG changeset patch # User Maxim Dounin # Date 1712676564 -10800 # Tue Apr 09 18:29:24 2024 +0300 # Node ID d099a7bc5d226cabdefe8994374d0bbfd84ad07b # Parent 03b5cd59c55fdbc4074499127f79370ec96d3d44 Removed books from menu. diff --git a/xml/menu.xml b/xml/menu.xml --- a/xml/menu.xml +++ b/xml/menu.xml @@ -22,7 +22,6 @@ security documentation faq - books support @@ -40,7 +39,6 @@ ???????????? ???????????? faq - ????? ????????? From mdounin at mdounin.ru Tue Apr 9 16:44:24 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Tue, 09 Apr 2024 19:44:24 +0300 Subject: [PATCH] Tests: synced LICENSE text with bsd-style-copyright Message-ID: # HG changeset patch # User Maxim Dounin # Date 1712680805 -10800 # Tue Apr 09 19:40:05 2024 +0300 # Node ID fe80aebae728b6e8fccafe1ded1e8ae12a0ac72d # Parent 0b1a671c20c16e00dfc724caffff8170441c703f Tests: synced LICENSE text with bsd-style-copyright. diff --git a/LICENSE b/LICENSE --- a/LICENSE +++ b/LICENSE @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) From mdounin at mdounin.ru Thu Apr 11 17:37:52 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Thu, 11 Apr 2024 20:37:52 +0300 Subject: [nginx-tests] Tests: synced LICENSE text with bsd-style-copyright. Message-ID: details: http://freenginx.org/hg/nginx-tests/rev/fe80aebae728 branches: changeset: 1956:fe80aebae728 user: Maxim Dounin date: Tue Apr 09 19:40:05 2024 +0300 description: Tests: synced LICENSE text with bsd-style-copyright. diffstat: LICENSE | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diffs (16 lines): diff --git a/LICENSE b/LICENSE --- a/LICENSE +++ b/LICENSE @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) From mb at x14.nl Fri Apr 12 20:11:40 2024 From: mb at x14.nl (=?iso-8859-1?q?Michiel_W=2E_Beijen?=) Date: Fri, 12 Apr 2024 22:11:40 +0200 Subject: [PATCH] Use RFC 9110 descriptions for status 413 and 414 Message-ID: <58cd6cb2adba48ff9336.1712952700@foaf.home.x14.nl> # HG changeset patch # User Michiel W. Beijen # Date 1712950069 -7200 # Fri Apr 12 21:27:49 2024 +0200 # Node ID 58cd6cb2adba48ff9336c2896046e155074fdffd # Parent ccd7e4022a2498a930c9e0927a9820f7ea17ae13 Use RFC 9110 descriptions for status 413 and 414 RFC 9110 is accepted as a Standards Track document in June 2022. It obsoletes the earlier RFC 7231. It uses slightly different names for HTTP status codes 413 and 414. Rename the constants and documents returned by nginx to reflect the new naming. Keep the old constants in place for backwards compatibility. ref: https://www.rfc-editor.org/rfc/rfc9110.html diff -r ccd7e4022a24 -r 58cd6cb2adba src/http/modules/perl/nginx.pm --- a/src/http/modules/perl/nginx.pm Tue Apr 02 20:55:21 2024 +0300 +++ b/src/http/modules/perl/nginx.pm Fri Apr 12 21:27:49 2024 +0200 @@ -37,7 +37,9 @@ HTTP_CONFLICT HTTP_GONE HTTP_LENGTH_REQUIRED + HTTP_CONTENT_TOO_LARGE HTTP_REQUEST_ENTITY_TOO_LARGE + HTTP_URI_TOO_LONG HTTP_REQUEST_URI_TOO_LARGE HTTP_UNSUPPORTED_MEDIA_TYPE HTTP_RANGE_NOT_SATISFIABLE @@ -86,8 +88,8 @@ use constant HTTP_CONFLICT => 409; use constant HTTP_GONE => 410; use constant HTTP_LENGTH_REQUIRED => 411; -use constant HTTP_REQUEST_ENTITY_TOO_LARGE => 413; -use constant HTTP_REQUEST_URI_TOO_LARGE => 414; +use constant HTTP_CONTENT_TOO_LARGE => 413; +use constant HTTP_URI_TOO_LONG => 414; use constant HTTP_UNSUPPORTED_MEDIA_TYPE => 415; use constant HTTP_RANGE_NOT_SATISFIABLE => 416; @@ -99,6 +101,10 @@ use constant HTTP_GATEWAY_TIME_OUT => 504; use constant HTTP_INSUFFICIENT_STORAGE => 507; +# For backwards compatibility +use constant HTTP_REQUEST_ENTITY_TOO_LARGE => HTTP_CONTENT_TOO_LARGE; +use constant HTTP_REQUEST_URI_TOO_LARGE => HTTP_URI_TOO_LONG; + sub rflush { my $r = shift; diff -r ccd7e4022a24 -r 58cd6cb2adba src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c Tue Apr 02 20:55:21 2024 +0300 +++ b/src/http/ngx_http_core_module.c Fri Apr 12 21:27:49 2024 +0200 @@ -993,7 +993,7 @@ r->expect_tested = 1; (void) ngx_http_discard_request_body(r); - ngx_http_finalize_request(r, NGX_HTTP_REQUEST_ENTITY_TOO_LARGE); + ngx_http_finalize_request(r, NGX_HTTP_CONTENT_TOO_LARGE); return NGX_OK; } diff -r ccd7e4022a24 -r 58cd6cb2adba src/http/ngx_http_header_filter_module.c --- a/src/http/ngx_http_header_filter_module.c Tue Apr 02 20:55:21 2024 +0300 +++ b/src/http/ngx_http_header_filter_module.c Fri Apr 12 21:27:49 2024 +0200 @@ -93,8 +93,8 @@ ngx_string("410 Gone"), ngx_string("411 Length Required"), ngx_string("412 Precondition Failed"), - ngx_string("413 Request Entity Too Large"), - ngx_string("414 Request-URI Too Large"), + ngx_string("413 Content Too Large"), + ngx_string("414 URI Too Large"), ngx_string("415 Unsupported Media Type"), ngx_string("416 Requested Range Not Satisfiable"), ngx_null_string, /* "417 Expectation Failed" */ diff -r ccd7e4022a24 -r 58cd6cb2adba src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Tue Apr 02 20:55:21 2024 +0300 +++ b/src/http/ngx_http_request.c Fri Apr 12 21:27:49 2024 +0200 @@ -1232,7 +1232,7 @@ ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent too long URI"); - ngx_http_finalize_request(r, NGX_HTTP_REQUEST_URI_TOO_LARGE); + ngx_http_finalize_request(r, NGX_HTTP_URI_TOO_LONG); break; } } diff -r ccd7e4022a24 -r 58cd6cb2adba src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h Tue Apr 02 20:55:21 2024 +0300 +++ b/src/http/ngx_http_request.h Fri Apr 12 21:27:49 2024 +0200 @@ -98,13 +98,16 @@ #define NGX_HTTP_CONFLICT 409 #define NGX_HTTP_LENGTH_REQUIRED 411 #define NGX_HTTP_PRECONDITION_FAILED 412 -#define NGX_HTTP_REQUEST_ENTITY_TOO_LARGE 413 -#define NGX_HTTP_REQUEST_URI_TOO_LARGE 414 +#define NGX_HTTP_CONTENT_TOO_LARGE 413 +#define NGX_HTTP_URI_TOO_LONG 414 #define NGX_HTTP_UNSUPPORTED_MEDIA_TYPE 415 #define NGX_HTTP_RANGE_NOT_SATISFIABLE 416 #define NGX_HTTP_MISDIRECTED_REQUEST 421 #define NGX_HTTP_TOO_MANY_REQUESTS 429 +/* For backwards compatibility*/ +#define NGX_HTTP_REQUEST_ENTITY_TOO_LARGE NGX_HTTP_CONTENT_TOO_LARGE +#define NGX_HTTP_REQUEST_URI_TOO_LARGE NGX_HTTP_URI_TOO_LONG /* Our own HTTP codes */ diff -r ccd7e4022a24 -r 58cd6cb2adba src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c Tue Apr 02 20:55:21 2024 +0300 +++ b/src/http/ngx_http_request_body.c Fri Apr 12 21:27:49 2024 +0200 @@ -1148,7 +1148,7 @@ r->lingering_close = 1; - return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; + return NGX_HTTP_CONTENT_TOO_LARGE; } if (clcf->client_max_body_size @@ -1163,7 +1163,7 @@ r->lingering_close = 1; - return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; + return NGX_HTTP_CONTENT_TOO_LARGE; } if (b @@ -1263,7 +1263,7 @@ r->lingering_close = 1; - return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; + return NGX_HTTP_CONTENT_TOO_LARGE; } /* set rb->rest, amount of data we want to see next time */ diff -r ccd7e4022a24 -r 58cd6cb2adba src/http/ngx_http_special_response.c --- a/src/http/ngx_http_special_response.c Tue Apr 02 20:55:21 2024 +0300 +++ b/src/http/ngx_http_special_response.c Fri Apr 12 21:27:49 2024 +0200 @@ -195,17 +195,17 @@ static char ngx_http_error_413_page[] = "" CRLF -"413 Request Entity Too Large" CRLF +"413 Content Too Large" CRLF "" CRLF -"

413 Request Entity Too Large

" CRLF +"

413 Content Too Large

" CRLF ; static char ngx_http_error_414_page[] = "" CRLF -"414 Request-URI Too Large" CRLF +"414 URI Too Long" CRLF "" CRLF -"

414 Request-URI Too Large

" CRLF +"

414 URI Too Long

" CRLF ; @@ -428,8 +428,8 @@ if (r->keepalive) { switch (error) { case NGX_HTTP_BAD_REQUEST: - case NGX_HTTP_REQUEST_ENTITY_TOO_LARGE: - case NGX_HTTP_REQUEST_URI_TOO_LARGE: + case NGX_HTTP_CONTENT_TOO_LARGE: + case NGX_HTTP_URI_TOO_LONG: case NGX_HTTP_TO_HTTPS: case NGX_HTTPS_CERT_ERROR: case NGX_HTTPS_NO_CERT: diff -r ccd7e4022a24 -r 58cd6cb2adba src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c Tue Apr 02 20:55:21 2024 +0300 +++ b/src/http/v2/ngx_http_v2.c Fri Apr 12 21:27:49 2024 +0200 @@ -4116,7 +4116,7 @@ "client intended to send too large chunked body: " "%O bytes", rb->received); - return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; + return NGX_HTTP_CONTENT_TOO_LARGE; } } diff -r ccd7e4022a24 -r 58cd6cb2adba src/http/v3/ngx_http_v3_request.c --- a/src/http/v3/ngx_http_v3_request.c Tue Apr 02 20:55:21 2024 +0300 +++ b/src/http/v3/ngx_http_v3_request.c Fri Apr 12 21:27:49 2024 +0200 @@ -1581,7 +1581,7 @@ "body: %O+%ui bytes", rb->received, st->length); - return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; + return NGX_HTTP_CONTENT_TOO_LARGE; } continue; From mdounin at mdounin.ru Sun Apr 14 01:00:28 2024 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sun, 14 Apr 2024 04:00:28 +0300 Subject: [PATCH] Use RFC 9110 descriptions for status 413 and 414 In-Reply-To: <58cd6cb2adba48ff9336.1712952700@foaf.home.x14.nl> References: <58cd6cb2adba48ff9336.1712952700@foaf.home.x14.nl> Message-ID: Hello! On Fri, Apr 12, 2024 at 10:11:40PM +0200, Michiel W. Beijen wrote: > # HG changeset patch > # User Michiel W. Beijen > # Date 1712950069 -7200 > # Fri Apr 12 21:27:49 2024 +0200 > # Node ID 58cd6cb2adba48ff9336c2896046e155074fdffd > # Parent ccd7e4022a2498a930c9e0927a9820f7ea17ae13 > Use RFC 9110 descriptions for status 413 and 414 > > RFC 9110 is accepted as a Standards Track document in June 2022. > It obsoletes the earlier RFC 7231. It uses slightly different names for > HTTP status codes 413 and 414. > > Rename the constants and documents returned by nginx to reflect the new > naming. > Keep the old constants in place for backwards compatibility. > > ref: https://www.rfc-editor.org/rfc/rfc9110.html Thanks for the patch. Note that 413 was "Payload Too Large" since RFC 7231, which was then changed into "Content Too Large" in RFC 9110. Both reason phrases, unfortunately, look worse than "Request Entity Too Large" as introduced in RFC 2068, as they require additional information to understand if the request or the response content is the source of the problem. And 414 was "Request-URI Too Long" since introduction in RFC 2068, and changed to "URI Too Long" in RFC 7231 (and not changed in RFC 9110). That is, "Too Large" part is not consistent with any RFCs, it should be "Too Long". The NGX_HTTP_REQUEST_URI_TOO_LARGE name dates back to 12:055ed05235ae (nginx-0.0.1-2002-09-13-18:47:42 import), and likely was borrowed from Apache, which also uses similar name (HTTP_REQUEST_URI_TOO_LARGE, see https://svn.apache.org/repos/asf/httpd/httpd/trunk/include/httpd.h). Overall, I tend to think that it would be wrong to change defines, even if providing compatibility shims. OTOH, we can consider updating text representations, similarly to how it is already the case for 302 (which is NGX_HTTP_MOVED_TEMPORARILY, but "302 Found"). (But I would rather refrain from changing 413 due to the above reason.) [...] > diff -r ccd7e4022a24 -r 58cd6cb2adba src/http/ngx_http_header_filter_module.c > --- a/src/http/ngx_http_header_filter_module.c Tue Apr 02 20:55:21 2024 +0300 > +++ b/src/http/ngx_http_header_filter_module.c Fri Apr 12 21:27:49 2024 +0200 > @@ -93,8 +93,8 @@ > ngx_string("410 Gone"), > ngx_string("411 Length Required"), > ngx_string("412 Precondition Failed"), > - ngx_string("413 Request Entity Too Large"), > - ngx_string("414 Request-URI Too Large"), > + ngx_string("413 Content Too Large"), > + ngx_string("414 URI Too Large"), Typo: should be "... Too Long". [...] -- Maxim Dounin http://mdounin.ru/ From mb at x14.nl Sun Apr 14 10:26:23 2024 From: mb at x14.nl (=?iso-8859-1?q?Michiel_W=2E_Beijen?=) Date: Sun, 14 Apr 2024 12:26:23 +0200 Subject: [PATCH] Use RFC9110 HTTP status code descriptions Message-ID: <3375171a24a16400b000.1713090383@foaf.home.x14.nl> # HG changeset patch # User Michiel W. Beijen # Date 1713090220 -7200 # Sun Apr 14 12:23:40 2024 +0200 # Branch stable-1.26 # Node ID 3375171a24a16400b000805e45df1b57e4dd1775 # Parent cecfdf9a8420fcdeb69d090cd7fd9169967e0007 Use RFC9110 HTTP status code descriptions This patch changes the status codes and status pages served by freenginx to use the wording from RFC9110 Exeption for HTTP status code 413 "Request Entity Too Large", which is "Content Too Large" in the RFC. In that case we would lose information about what actually is too large, the request or the response. diff -r cecfdf9a8420 -r 3375171a24a1 src/http/ngx_http_header_filter_module.c --- a/src/http/ngx_http_header_filter_module.c Sun Apr 14 12:03:29 2024 +0200 +++ b/src/http/ngx_http_header_filter_module.c Sun Apr 14 12:23:40 2024 +0200 @@ -69,7 +69,7 @@ /* ngx_null_string, */ /* "300 Multiple Choices" */ ngx_string("301 Moved Permanently"), - ngx_string("302 Moved Temporarily"), + ngx_string("302 Found"), ngx_string("303 See Other"), ngx_string("304 Not Modified"), ngx_null_string, /* "305 Use Proxy" */ @@ -85,18 +85,18 @@ ngx_string("402 Payment Required"), ngx_string("403 Forbidden"), ngx_string("404 Not Found"), - ngx_string("405 Not Allowed"), + ngx_string("405 Method Not Allowed"), ngx_string("406 Not Acceptable"), ngx_null_string, /* "407 Proxy Authentication Required" */ - ngx_string("408 Request Time-out"), + ngx_string("408 Request Timeout"), ngx_string("409 Conflict"), ngx_string("410 Gone"), ngx_string("411 Length Required"), ngx_string("412 Precondition Failed"), ngx_string("413 Request Entity Too Large"), - ngx_string("414 Request-URI Too Large"), + ngx_string("414 URI Too Long"), ngx_string("415 Unsupported Media Type"), - ngx_string("416 Requested Range Not Satisfiable"), + ngx_string("416 Range Not Satisfiable"), ngx_null_string, /* "417 Expectation Failed" */ ngx_null_string, /* "418 unused" */ ngx_null_string, /* "419 unused" */ @@ -117,8 +117,8 @@ ngx_string("500 Internal Server Error"), ngx_string("501 Not Implemented"), ngx_string("502 Bad Gateway"), - ngx_string("503 Service Temporarily Unavailable"), - ngx_string("504 Gateway Time-out"), + ngx_string("503 Service Unavailable"), + ngx_string("504 Gateway Timeout"), ngx_string("505 HTTP Version Not Supported"), ngx_null_string, /* "506 Variant Also Negotiates" */ ngx_string("507 Insufficient Storage"), diff -r cecfdf9a8420 -r 3375171a24a1 src/http/ngx_http_special_response.c --- a/src/http/ngx_http_special_response.c Sun Apr 14 12:03:29 2024 +0200 +++ b/src/http/ngx_http_special_response.c Sun Apr 14 12:23:40 2024 +0200 @@ -107,9 +107,9 @@ static char ngx_http_error_401_page[] = "" CRLF -"401 Authorization Required" CRLF +"401 Unauthorized" CRLF "" CRLF -"

401 Authorization Required

" CRLF +"

401 Unauthorized

" CRLF ; @@ -139,9 +139,9 @@ static char ngx_http_error_405_page[] = "" CRLF -"405 Not Allowed" CRLF +"405 Method Not Allowed" CRLF "" CRLF -"

405 Not Allowed

" CRLF +"

405 Method Not Allowed

" CRLF ; @@ -155,9 +155,9 @@ static char ngx_http_error_408_page[] = "" CRLF -"408 Request Time-out" CRLF +"408 Request Timeout" CRLF "" CRLF -"

408 Request Time-out

" CRLF +"

408 Request Timeout

" CRLF ; @@ -203,9 +203,9 @@ static char ngx_http_error_414_page[] = "" CRLF -"414 Request-URI Too Large" CRLF +"414 URI Too Long" CRLF "" CRLF -"

414 Request-URI Too Large

" CRLF +"

414 URI Too Long

" CRLF ; @@ -219,9 +219,9 @@ static char ngx_http_error_416_page[] = "" CRLF -"416 Requested Range Not Satisfiable" CRLF +"416 Range Not Satisfiable" CRLF "" CRLF -"

416 Requested Range Not Satisfiable

" CRLF +"

416 Range Not Satisfiable

" CRLF ; @@ -307,17 +307,17 @@ static char ngx_http_error_503_page[] = "" CRLF -"503 Service Temporarily Unavailable" CRLF +"503 Service Unavailable" CRLF "" CRLF -"

503 Service Temporarily Unavailable

" CRLF +"

503 Service Unavailable

" CRLF ; static char ngx_http_error_504_page[] = "" CRLF -"504 Gateway Time-out" CRLF +"504 Gateway Timeout" CRLF "" CRLF -"

504 Gateway Time-out

" CRLF +"

504 Gateway Timeout

" CRLF ; From mb at x14.nl Sun Apr 14 10:28:27 2024 From: mb at x14.nl (Michiel Beijen) Date: Sun, 14 Apr 2024 12:28:27 +0200 Subject: [PATCH] Use RFC 9110 descriptions for status 413 and 414 In-Reply-To: References: <58cd6cb2adba48ff9336.1712952700@foaf.home.x14.nl> Message-ID: Hi, On 14-04-2024 03:00, Maxim Dounin wrote: > Thanks for the patch. > Note that 413 was "Payload Too Large" since RFC 7231, which was > then changed into "Content Too Large" in RFC 9110. Both reason > phrases, unfortunately, look worse than "Request Entity Too Large" > as introduced in RFC 2068, as they require additional information > to understand if the request or the response content is the source > of the problem. > > And 414 was "Request-URI Too Long" since introduction in RFC 2068, > and changed to "URI Too Long" in RFC 7231 (and not changed in RFC > 9110). That is, "Too Large" part is not consistent with any RFCs, > it should be "Too Long". The NGX_HTTP_REQUEST_URI_TOO_LARGE name > dates back to 12:055ed05235ae (nginx-0.0.1-2002-09-13-18:47:42 > import), and likely was borrowed from Apache, which also uses > similar name (HTTP_REQUEST_URI_TOO_LARGE, see > https://svn.apache.org/repos/asf/httpd/httpd/trunk/include/httpd.h). > > Overall, I tend to think that it would be wrong to change defines, > even if providing compatibility shims. OTOH, we can consider > updating text representations, similarly to how it is already the > case for 302 (which is NGX_HTTP_MOVED_TEMPORARILY, but "302 > Found"). (But I would rather refrain from changing 413 due to > the above reason.) Thanks for the status code archeology. I was already wondering about the differences between the RFCs and nginx descriptions. I've created a new patch where only the content generated by NGINX is modified, and not the defines. -- Michiel From mdounin at mdounin.ru Sun Apr 14 12:51:33 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sun, 14 Apr 2024 15:51:33 +0300 Subject: [nginx-site] Renamed news page. Message-ID: details: http://freenginx.org/hg/nginx-site/rev/7f493ec56c12 branches: changeset: 3072:7f493ec56c12 user: Maxim Dounin date: Tue Apr 09 18:14:16 2024 +0300 description: Renamed news page. diffstat: xml/index.xml | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff --git a/xml/index.xml b/xml/index.xml --- a/xml/index.xml +++ b/xml/index.xml @@ -6,7 +6,7 @@ - + From mdounin at mdounin.ru Sun Apr 14 12:51:34 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sun, 14 Apr 2024 15:51:34 +0300 Subject: [nginx-site] Rewritten about page. Message-ID: details: http://freenginx.org/hg/nginx-site/rev/5d894c73896f branches: changeset: 3073:5d894c73896f user: Maxim Dounin date: Tue Apr 09 18:14:20 2024 +0300 description: Rewritten about page. diffstat: xml/en/index.xml | 21 ++++----------------- xml/ru/index.xml | 27 +++++++-------------------- 2 files changed, 11 insertions(+), 37 deletions(-) diffs (85 lines): diff --git a/xml/en/index.xml b/xml/en/index.xml --- a/xml/en/index.xml +++ b/xml/en/index.xml @@ -5,33 +5,20 @@ -
+ rev="164">
-nginx [engine x] is an HTTP and reverse proxy server, +freenginx is an effort to preserve free and open development of +nginx [engine x], an HTTP and reverse proxy server, a mail proxy server, and a generic TCP/UDP proxy server, originally written by Igor Sysoev. -For a long time, it has been running -on many heavily loaded Russian sites including -Yandex, -Mail.Ru, -VK, and -Rambler. -According to Netcraft, nginx served or proxied -20.71% -busiest sites in January 2024. -Here are some of the success stories: -Dropbox, -Netflix, -Wordpress.com, -FastMail.FM. diff --git a/xml/ru/index.xml b/xml/ru/index.xml --- a/xml/ru/index.xml +++ b/xml/ru/index.xml @@ -5,33 +5,20 @@ -
+ rev="164">
-nginx [engine x]—??? HTTP-?????? ? ???????? ??????-??????, -???????? ??????-??????, -? ????? TCP/UDP ??????-?????? ?????? ??????????, -?????????? ?????????? ?????? ????????. -??? ?????????? ????? ?? ??????????? -??????? ?????? ????????????????? ?????????? ??????, ????? ??? -??????, -Mail.Ru, -????????? ? -???????. -???????? ?????????? Netcraft nginx ?????????? ??? ??????????? -20.71% -????? ??????????? ?????? ? ?????? 2024 ????. -??? ????????? ??????? ????????? ????????? nginx (?????? ?? ?????????? ?????): -Dropbox, -Netflix, -Wordpress.com, -FastMail.FM. +freenginx — ??????, ??????????? ????????? ? ???????? ?????????? +nginx [engine x], HTTP-??????? ? ????????? ??????-???????, +????????? ??????-???????, +? ????? TCP/UDP ??????-??????? ?????? ??????????, +?????????? ??????????? ?????? ????????. From mdounin at mdounin.ru Sun Apr 14 12:51:34 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sun, 14 Apr 2024 15:51:34 +0300 Subject: [nginx-site] Renamed security advisories page. Message-ID: details: http://freenginx.org/hg/nginx-site/rev/4bad11519815 branches: changeset: 3074:4bad11519815 user: Maxim Dounin date: Tue Apr 09 18:15:10 2024 +0300 description: Renamed security advisories page. diffstat: xml/en/security_advisories.xml | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diffs (21 lines): diff --git a/xml/en/security_advisories.xml b/xml/en/security_advisories.xml --- a/xml/en/security_advisories.xml +++ b/xml/en/security_advisories.xml @@ -5,7 +5,7 @@ -
@@ -13,7 +13,7 @@
-All nginx security issues should be reported to +All freenginx security issues should be reported to security-alert at freenginx.org. From mdounin at mdounin.ru Sun Apr 14 12:51:34 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sun, 14 Apr 2024 15:51:34 +0300 Subject: [nginx-site] Renamed download page. Message-ID: details: http://freenginx.org/hg/nginx-site/rev/6fcdefd1df4e branches: changeset: 3075:6fcdefd1df4e user: Maxim Dounin date: Tue Apr 09 18:16:11 2024 +0300 description: Renamed download page. diffstat: xml/en/download.xml | 4 ++-- xml/ru/download.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diffs (32 lines): diff --git a/xml/en/download.xml b/xml/en/download.xml --- a/xml/en/download.xml +++ b/xml/en/download.xml @@ -5,10 +5,10 @@ -
+ rev="7">
diff --git a/xml/ru/download.xml b/xml/ru/download.xml --- a/xml/ru/download.xml +++ b/xml/ru/download.xml @@ -5,10 +5,10 @@ -
+ rev="7">
From mdounin at mdounin.ru Sun Apr 14 12:51:34 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sun, 14 Apr 2024 15:51:34 +0300 Subject: [nginx-site] Updated Windows archive names. Message-ID: details: http://freenginx.org/hg/nginx-site/rev/be1ef1d56e96 branches: changeset: 3076:be1ef1d56e96 user: Maxim Dounin date: Tue Apr 09 18:20:21 2024 +0300 description: Updated Windows archive names. diffstat: xml/en/docs/windows.xml | 24 ++++++++++++------------ xml/ru/docs/windows.xml | 22 +++++++++++----------- 2 files changed, 23 insertions(+), 23 deletions(-) diffs (150 lines): diff --git a/xml/en/docs/windows.xml b/xml/en/docs/windows.xml --- a/xml/en/docs/windows.xml +++ b/xml/en/docs/windows.xml @@ -5,20 +5,20 @@ -
+ rev="6">
-Version of nginx for Windows uses the native Win32 API (not the Cygwin emulation -layer). +Version of freenginx for Windows uses the native Win32 API +(not the Cygwin emulation layer). Only the select and poll (1.15.9) connection processing methods are currently used, so high performance and scalability should not be expected. -Due to this and some other known issues version of nginx for Windows +Due to this and some other known issues version of freenginx for Windows is considered to be a beta version. At this time, it provides almost the same functionality as a UNIX version of nginx except for @@ -26,18 +26,18 @@ XSLT filter, image filter, GeoIP module, -To install nginx/Windows, download +To install freenginx/Windows, download the latest mainline version distribution (), since the mainline branch of nginx contains all known fixes. Then unpack the distribution, go to the -nginx- +freenginx- directory, and run nginx. Here is an example for the drive C: root directory: cd c:\ -unzip nginx-.zip -cd nginx- +unzip freenginx-.zip +cd freenginx- start nginx @@ -63,10 +63,10 @@ for the reason in the logs\error.l -nginx/Windows uses the directory where it has been run as the prefix +freenginx/Windows uses the directory where it has been run as the prefix for relative paths in the configuration. In the example above, the prefix is -C:\nginx-\. +C:\freenginx-\. Paths in a configuration file must be specified in UNIX-style using forward slashes: @@ -77,7 +77,7 @@ root C:/web/html; -nginx/Windows runs as a standard console application (not a service), +freenginx/Windows runs as a standard console application (not a service), and it can be managed using the following commands:
diff --git a/xml/ru/docs/windows.xml b/xml/ru/docs/windows.xml --- a/xml/ru/docs/windows.xml +++ b/xml/ru/docs/windows.xml @@ -5,7 +5,7 @@ -
@@ -13,11 +13,11 @@
-?????? nginx ??? Windows ?????????? ???????? Win32 API (?? ???????? Cygwin). +?????? freenginx ??? Windows ?????????? ???????? Win32 API (?? ???????? Cygwin). ? ????????? ?????? ? ???????? ??????? ????????? ?????????? ???????????? select ? poll (1.15.9), ??????? ?? ????? ??????? ??????? ?????????????????? ? ????????????????. -? ???? ????? ? ???? ?????? ????????? ??????? ?????? nginx ??? Windows +? ???? ????? ? ???? ?????? ????????? ??????? ?????? freenginx ??? Windows ??????????????? ???? ??? ????-??????. ?? ?????? ?????? ? ??? ???????? ??????????? ??? ????????????????, ??? ? ? ?????? nginx ??? UNIX, ?? ??????????? @@ -25,22 +25,22 @@ XSLT-???????, ??????? ???????????, ?????? GeoIP ? ??????????? ????? Perl. -????? ?????????? nginx/Windows, ???????? +????? ?????????? freenginx/Windows, ???????? ??????????? ????????? ???????? ?????? (), ????????? ???????? ????? nginx ???????? ??? ????????? ???????????. ????? ?????????? ???????????, ????????? ? ??????? -nginx- +freenginx- ? ????????? nginx. ??? ?????? ??? ????????? ???????? ?? ????? C: cd c:\ -unzip nginx-.zip -cd nginx- +unzip freenginx-.zip +cd freenginx- start nginx -????? ??????? ???????? nginx, ????????? ??????? ????????? ?????? +????? ??????? ???????? freenginx, ????????? ??????? ????????? ?????? tasklist: @@ -62,10 +62,10 @@ nginx.exe 1332 Console 0 3?112 K -nginx/Windows ?????????? ???????, ? ??????? ?? ??? ???????, ? ???????? +freenginx/Windows ?????????? ???????, ? ??????? ?? ??? ???????, ? ???????? ???????? ??? ????????????? ????? ? ????????????. ? ??????????????? ??????? ????????? ???????? -C:\nginx-\. +C:\freenginx-\. ???? ? ???????????????? ????? ?????? ?????????? ? UNIX-????? ? ?????????????? ?????? ??????: @@ -76,7 +76,7 @@ root C:/web/html; -nginx/Windows ???????? ??? ??????????? ?????????? ?????????? (?? ??????) +freenginx/Windows ???????? ??? ??????????? ?????????? ?????????? (?? ??????) ? ??????????? ??? ?????? ????????? ??????:
From mdounin at mdounin.ru Sun Apr 14 12:51:34 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sun, 14 Apr 2024 15:51:34 +0300 Subject: [nginx-site] Renamed documentation page. Message-ID: details: http://freenginx.org/hg/nginx-site/rev/07f4d29a90c0 branches: changeset: 3077:07f4d29a90c0 user: Maxim Dounin date: Tue Apr 09 18:21:14 2024 +0300 description: Renamed documentation page. diffstat: xml/en/docs/index.xml | 4 ++-- xml/ru/docs/index.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diffs (32 lines): diff --git a/xml/en/docs/index.xml b/xml/en/docs/index.xml --- a/xml/en/docs/index.xml +++ b/xml/en/docs/index.xml @@ -5,10 +5,10 @@ -
diff --git a/xml/ru/docs/index.xml b/xml/ru/docs/index.xml --- a/xml/ru/docs/index.xml +++ b/xml/ru/docs/index.xml @@ -5,10 +5,10 @@ -
From mdounin at mdounin.ru Sun Apr 14 12:51:34 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sun, 14 Apr 2024 15:51:34 +0300 Subject: [nginx-site] Renamed support page. Message-ID: details: http://freenginx.org/hg/nginx-site/rev/7437050401cf branches: changeset: 3078:7437050401cf user: Maxim Dounin date: Tue Apr 09 18:26:01 2024 +0300 description: Renamed support page. diffstat: xml/en/support.xml | 4 ++-- xml/ru/support.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diffs (32 lines): diff --git a/xml/en/support.xml b/xml/en/support.xml --- a/xml/en/support.xml +++ b/xml/en/support.xml @@ -5,10 +5,10 @@ -
+ rev="13">
diff --git a/xml/ru/support.xml b/xml/ru/support.xml --- a/xml/ru/support.xml +++ b/xml/ru/support.xml @@ -5,10 +5,10 @@ -
+ rev="13">
From mdounin at mdounin.ru Sun Apr 14 12:51:34 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sun, 14 Apr 2024 15:51:34 +0300 Subject: [nginx-site] Changed name in Russian about menu link. Message-ID: details: http://freenginx.org/hg/nginx-site/rev/03b5cd59c55f branches: changeset: 3079:03b5cd59c55f user: Maxim Dounin date: Tue Apr 09 18:26:15 2024 +0300 description: Changed name in Russian about menu link. diffstat: xml/menu.xml | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff --git a/xml/menu.xml b/xml/menu.xml --- a/xml/menu.xml +++ b/xml/menu.xml @@ -35,7 +35,7 @@ ??????? - ?? nginx + ? freenginx ??????? ???????????? ???????????? From mdounin at mdounin.ru Sun Apr 14 12:51:34 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sun, 14 Apr 2024 15:51:34 +0300 Subject: [nginx-site] Removed books from menu. Message-ID: details: http://freenginx.org/hg/nginx-site/rev/d099a7bc5d22 branches: changeset: 3080:d099a7bc5d22 user: Maxim Dounin date: Tue Apr 09 18:29:24 2024 +0300 description: Removed books from menu. diffstat: xml/menu.xml | 2 -- 1 files changed, 0 insertions(+), 2 deletions(-) diffs (19 lines): diff --git a/xml/menu.xml b/xml/menu.xml --- a/xml/menu.xml +++ b/xml/menu.xml @@ -22,7 +22,6 @@ security documentation faq - books support @@ -40,7 +39,6 @@ ???????????? ???????????? faq - ????? ????????? From mdounin at mdounin.ru Sun Apr 14 20:15:08 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sun, 14 Apr 2024 23:15:08 +0300 Subject: [PATCH] XSLScript: added CDATA support Message-ID: # HG changeset patch # User Maxim Dounin # Date 1713125471 -10800 # Sun Apr 14 23:11:11 2024 +0300 # Node ID cc0402def1f9c4c8a8089cb2d8ad7795dd17668b # Parent 01dc9ba12e1ba472f22d6f1be3eaa3c2b4f2879e XSLScript: added CDATA support. Notably, this simplifies adding javascript code to templates, which otherwise require complex escaping. Requested by Igor Sysoev. diff --git a/xslscript.pl b/xslscript.pl --- a/xslscript.pl +++ b/xslscript.pl @@ -45,6 +45,7 @@ startrule : item(s) eofile { $return = $item{'item(s)'}; 1 } item : ").)*/ms "-->" { $return = ""; 1 } +# cdata, + +cdata : /((?!]]>).)*/ms "]]>" + { $return = ""; 1 } + # special chars: ', ", {, }, \ # if used in text, they needs to be escaped with backslash From mdounin at mdounin.ru Sat Apr 20 00:59:48 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 20 Apr 2024 03:59:48 +0300 Subject: [PATCH 00 of 14] request body discarding cleanup Message-ID: Hello! The following patch series is focused on various edge cases related to reading and discarding of a request body. Notably, it should be now safe (from the request body point of view) to configure complex processing for errors, such as 413 (Request Entity Too Large), including proxying, without additional precautions, such as "proxy_set_header Content-Length '';" and "proxy_pass_request_body off;". The series also fixes the issue observed with error_page 413 and HTTP/2 (and HTTP/3), which previously required relaxed client_max_body_size limit in the error page location to work. Review and testing appreciated. -- Maxim Dounin From mdounin at mdounin.ru Sat Apr 20 00:59:49 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 20 Apr 2024 03:59:49 +0300 Subject: [PATCH 01 of 14] Reordered checks for Content-Length and Transfer-Encoding In-Reply-To: References: Message-ID: <3c408152180f04a54c44.1713574789@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1713574627 -10800 # Sat Apr 20 03:57:07 2024 +0300 # Node ID 3c408152180f04a54c44d482cf1c9c52b63480d9 # Parent ccd7e4022a2498a930c9e0927a9820f7ea17ae13 Reordered checks for Content-Length and Transfer-Encoding. This ensures that r->headers_in.content_length_n is not set when both Content-Length and Transfer-Encoding headers are present, making it slightly safer to use complex processing for 400 (Bad Request) errors. diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1968,6 +1968,15 @@ ngx_http_process_request_header(ngx_http } if (r->headers_in.content_length) { + if (r->headers_in.transfer_encoding) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent \"Content-Length\" and " + "\"Transfer-Encoding\" headers " + "at the same time"); + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; + } + r->headers_in.content_length_n = ngx_atoof(r->headers_in.content_length->value.data, r->headers_in.content_length->value.len); @@ -1993,15 +2002,6 @@ ngx_http_process_request_header(ngx_http && ngx_strncasecmp(r->headers_in.transfer_encoding->value.data, (u_char *) "chunked", 7) == 0) { - if (r->headers_in.content_length) { - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client sent \"Content-Length\" and " - "\"Transfer-Encoding\" headers " - "at the same time"); - ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); - return NGX_ERROR; - } - r->headers_in.chunked = 1; } else { From mdounin at mdounin.ru Sat Apr 20 00:59:50 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 20 Apr 2024 03:59:50 +0300 Subject: [PATCH 02 of 14] HTTP/3: fixed handling of request body larger than Content-Length In-Reply-To: References: Message-ID: <0946ec7c4a6a26fda6ed.1713574790@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1713574628 -10800 # Sat Apr 20 03:57:08 2024 +0300 # Node ID 0946ec7c4a6a26fda6ed03cb1f1f877984ec5614 # Parent 3c408152180f04a54c44d482cf1c9c52b63480d9 HTTP/3: fixed handling of request body larger than Content-Length. Previously, 413 (Request entity too large) was returned, and incorrect "client intended to send too large body" error message was logged. Fix is to return 400 (Bad request) and log the "client intended to send body data larger than declared" error message, similarly to what HTTP/2 code does. Additionally, previously "client_max_body_size 0;" was incorrectly handled by the HTTP/3 code, resulting in 413 instead of no limit. This is also fixed by the correct checks added. diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -1482,7 +1482,6 @@ ngx_http_v3_do_read_client_request_body( static ngx_int_t ngx_http_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { - off_t max; size_t size; u_char *p; ngx_int_t rc; @@ -1510,14 +1509,6 @@ ngx_http_v3_request_body_filter(ngx_http rb->rest = cscf->large_client_header_buffers.size; } - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - - max = r->headers_in.content_length_n; - - if (max == -1 && clcf->client_max_body_size) { - max = clcf->client_max_body_size; - } - out = NULL; ll = &out; last = 0; @@ -1575,7 +1566,12 @@ ngx_http_v3_request_body_filter(ngx_http /* rc == NGX_OK */ - if (max != -1 && (uint64_t) (max - rb->received) < st->length) { + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->client_max_body_size + && (uint64_t) (clcf->client_max_body_size - rb->received) + < st->length) + { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "client intended to send too large " "body: %O+%ui bytes", @@ -1584,6 +1580,16 @@ ngx_http_v3_request_body_filter(ngx_http return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; } + if (r->headers_in.content_length_n != -1 + && rb->received > r->headers_in.content_length_n) + { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client intended to send body data " + "larger than declared"); + + return NGX_HTTP_BAD_REQUEST; + } + continue; } From mdounin at mdounin.ru Sat Apr 20 00:59:51 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 20 Apr 2024 03:59:51 +0300 Subject: [PATCH 03 of 14] Proxy: use zero Content-Length while discarding request body In-Reply-To: References: Message-ID: # HG changeset patch # User Maxim Dounin # Date 1713574630 -10800 # Sat Apr 20 03:57:10 2024 +0300 # Node ID c485429c713b5d24ba8329e95164b517cf8a1b87 # Parent 0946ec7c4a6a26fda6ed03cb1f1f877984ec5614 Proxy: use zero Content-Length while discarding request body. Previously, r->headers_in.content_length_n was used, which is not 0 till the request body is fully discarded, and the request might hang. diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -1341,6 +1341,9 @@ ngx_http_proxy_create_request(ngx_http_r ctx->internal_body_length = -1; ctx->internal_chunked = 1; + } else if (r->discard_body) { + ctx->internal_body_length = 0; + } else { ctx->internal_body_length = r->headers_in.content_length_n; } From mdounin at mdounin.ru Sat Apr 20 00:59:52 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 20 Apr 2024 03:59:52 +0300 Subject: [PATCH 04 of 14] Reset c->log->action on redirect to a named location In-Reply-To: References: Message-ID: # HG changeset patch # User Maxim Dounin # Date 1713574632 -10800 # Sat Apr 20 03:57:12 2024 +0300 # Node ID e59f07d8682810caa6693f707928593651fb9038 # Parent c485429c713b5d24ba8329e95164b517cf8a1b87 Reset c->log->action on redirect to a named location. This matches what happens on internal redirects in ngx_http_handler(), and ensures that stale log actions, such as "sending to client" from the upstream module, are not used after an error_page redirection to a named location. diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -2596,6 +2596,8 @@ ngx_http_named_location(ngx_http_request "using location: %V \"%V?%V\"", name, &r->uri, &r->args); + r->connection->log->action = NULL; + r->internal = 1; r->content_handler = NULL; r->uri_changed = 0; From mdounin at mdounin.ru Sat Apr 20 00:59:53 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 20 Apr 2024 03:59:53 +0300 Subject: [PATCH 05 of 14] Request body: provided log action for reading request body In-Reply-To: References: Message-ID: <7997083c8deb47dd84b2.1713574793@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1713574634 -10800 # Sat Apr 20 03:57:14 2024 +0300 # Node ID 7997083c8deb47dd84b2f2d323ac404d370d704a # Parent e59f07d8682810caa6693f707928593651fb9038 Request body: provided log action for reading request body. diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -85,6 +85,8 @@ ngx_http_read_client_request_body(ngx_ht return NGX_OK; } + r->connection->log->action = "reading request body"; + #if (NGX_HTTP_V2) if (r->stream) { rc = ngx_http_v2_read_request_body(r); @@ -159,6 +161,7 @@ ngx_http_read_client_request_body(ngx_ht if (rb->rest == 0 && rb->last_saved) { /* the whole request body was pre-read */ r->request_body_no_buffering = 0; + r->connection->log->action = NULL; post_handler(r); return NGX_OK; } @@ -217,6 +220,7 @@ done: } r->read_event_handler = ngx_http_block_reading; + r->connection->log->action = NULL; post_handler(r); } @@ -456,6 +460,7 @@ ngx_http_do_read_client_request_body(ngx if (!r->request_body_no_buffering) { r->read_event_handler = ngx_http_block_reading; + r->connection->log->action = NULL; rb->post_handler(r); } diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -3807,6 +3807,7 @@ ngx_http_v2_read_request_body(ngx_http_r if (stream->skip_data) { r->request_body_no_buffering = 0; + r->connection->log->action = NULL; rb->post_handler(r); return NGX_OK; } @@ -4061,6 +4062,7 @@ ngx_http_v2_process_request_body(ngx_htt } r->read_event_handler = ngx_http_block_reading; + r->connection->log->action = NULL; rb->post_handler(r); return NGX_OK; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -1250,6 +1250,7 @@ ngx_http_v3_read_request_body(ngx_http_r if (rb->rest == 0 && rb->last_saved) { /* the whole request body was pre-read */ r->request_body_no_buffering = 0; + r->connection->log->action = NULL; rb->post_handler(r); return NGX_OK; } @@ -1472,6 +1473,7 @@ ngx_http_v3_do_read_client_request_body( if (!r->request_body_no_buffering) { r->read_event_handler = ngx_http_block_reading; + r->connection->log->action = NULL; rb->post_handler(r); } From mdounin at mdounin.ru Sat Apr 20 00:59:54 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 20 Apr 2024 03:59:54 +0300 Subject: [PATCH 06 of 14] Request body: logging of timeouts In-Reply-To: References: Message-ID: <4476851eed617ebc2c01.1713574794@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1713574635 -10800 # Sat Apr 20 03:57:15 2024 +0300 # Node ID 4476851eed617ebc2c0125ab208c98d9634742e1 # Parent 7997083c8deb47dd84b2f2d323ac404d370d704a Request body: logging of timeouts. diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -262,6 +262,8 @@ ngx_http_read_unbuffered_request_body(ng #endif if (r->connection->read->timedout) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, NGX_ETIMEDOUT, + "client timed out"); r->connection->timedout = 1; return NGX_HTTP_REQUEST_TIME_OUT; } @@ -282,6 +284,8 @@ ngx_http_read_client_request_body_handle ngx_int_t rc; if (r->connection->read->timedout) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, NGX_ETIMEDOUT, + "client timed out"); r->connection->timedout = 1; ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT); return; diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -2361,6 +2361,7 @@ ngx_http_upstream_read_request_handler(n if (c->read->timedout) { c->timedout = 1; + ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT); return; } diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -4285,6 +4285,9 @@ ngx_http_v2_read_unbuffered_request_body if (fc->read->timedout) { if (stream->recv_window) { + ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, + "client timed out"); + stream->skip_data = 1; fc->timedout = 1; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -1281,6 +1281,8 @@ ngx_http_v3_read_client_request_body_han ngx_int_t rc; if (r->connection->read->timedout) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, NGX_ETIMEDOUT, + "client timed out"); r->connection->timedout = 1; ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT); return; @@ -1300,6 +1302,8 @@ ngx_http_v3_read_unbuffered_request_body ngx_int_t rc; if (r->connection->read->timedout) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, NGX_ETIMEDOUT, + "client timed out"); r->connection->timedout = 1; return NGX_HTTP_REQUEST_TIME_OUT; } From mdounin at mdounin.ru Sat Apr 20 00:59:55 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 20 Apr 2024 03:59:55 +0300 Subject: [PATCH 07 of 14] Request body: block reading when returning errors In-Reply-To: References: Message-ID: <42c2333af8bacd65b509.1713574795@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1713574637 -10800 # Sat Apr 20 03:57:17 2024 +0300 # Node ID 42c2333af8bacd65b50933119dcc47c9343f6cd2 # Parent 4476851eed617ebc2c0125ab208c98d9634742e1 Request body: block reading when returning errors. If reading is not blocked, additional client activity can result in undefined behaviour, including segfaults, as seen with proxying with proxy_ignore_client_abort before 4072:cf334deeea66. While unlikely with low-level errors being returned when reading the request body, it might be the case, for example, when chunked transfer encoding is used, and error_page 400 (or 413) is proxied with proxy_ignore_client_abort. diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -226,6 +226,7 @@ done: if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { r->main->count--; + r->read_event_handler = ngx_http_block_reading; } return rc; @@ -294,6 +295,7 @@ ngx_http_read_client_request_body_handle rc = ngx_http_do_read_client_request_body(r); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + r->read_event_handler = ngx_http_block_reading; ngx_http_finalize_request(r, rc); } } From mdounin at mdounin.ru Sat Apr 20 00:59:56 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 20 Apr 2024 03:59:56 +0300 Subject: [PATCH 08 of 14] Request body: improved debug logging In-Reply-To: References: Message-ID: <976af6df50499768d08b.1713574796@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1713574638 -10800 # Sat Apr 20 03:57:18 2024 +0300 # Node ID 976af6df50499768d08b176e9ae0ec0e1096ae88 # Parent 42c2333af8bacd65b50933119dcc47c9343f6cd2 Request body: improved debug logging. diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -48,6 +48,9 @@ ngx_http_read_client_request_body(ngx_ht return NGX_OK; } + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http read request body"); + if (ngx_http_test_expect(r) != NGX_OK) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; @@ -649,6 +652,9 @@ ngx_http_discard_request_body(ngx_http_r return NGX_OK; } + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http set discard body"); + #if (NGX_HTTP_V2) if (r->stream) { r->stream->skip_data = 1; @@ -668,8 +674,6 @@ ngx_http_discard_request_body(ngx_http_r rev = r->connection->read; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body"); - if (rev->timer_set) { ngx_del_timer(rev); } From mdounin at mdounin.ru Sat Apr 20 00:59:57 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 20 Apr 2024 03:59:57 +0300 Subject: [PATCH 09 of 14] Request body: simplified error handling in HTTP/2 In-Reply-To: References: Message-ID: <20eed25e08bb2b46bd5d.1713574797@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1713574640 -10800 # Sat Apr 20 03:57:20 2024 +0300 # Node ID 20eed25e08bb2b46bd5deddba63f5b4d4779e4a4 # Parent 976af6df50499768d08b176e9ae0ec0e1096ae88 Request body: simplified error handling in HTTP/2. No functional changes. diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -4238,11 +4238,7 @@ ngx_http_v2_read_client_request_body_han if (window < stream->recv_window) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "http2 negative window update"); - - stream->skip_data = 1; - - ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return; + goto error; } return; @@ -4252,18 +4248,24 @@ ngx_http_v2_read_client_request_body_han window - stream->recv_window) == NGX_ERROR) { - stream->skip_data = 1; - ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return; + goto error; } stream->recv_window = window; if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) { - stream->skip_data = 1; - ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return; - } + goto error; + } + + return; + +error: + + stream->skip_data = 1; + + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } From mdounin at mdounin.ru Sat Apr 20 00:59:58 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 20 Apr 2024 03:59:58 +0300 Subject: [PATCH 10 of 14] Request body: body is now cleared on errors In-Reply-To: References: Message-ID: <6ce5ad05c4f81b9a9dd6.1713574798@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1713574642 -10800 # Sat Apr 20 03:57:22 2024 +0300 # Node ID 6ce5ad05c4f81b9a9dd626c16a3f1e424ef9010f # Parent 20eed25e08bb2b46bd5deddba63f5b4d4779e4a4 Request body: body is now cleared on errors. Previously, after errors the request body was left in a potentially inconsistent state, with r->headers_in.content_length_n which might be larger than buffers actually stored in r->request_body->bufs (or not set at all, in case of HTTP/2 and HTTP/3). This can cause issues if the request body is subsequently used during error_page handling, such as when proxying. Fix is to clear r->request_body->bufs if this happens, and set r->headers_in.content_length_n to 0, much like it happens when ngx_http_discard_request_body() is called when returning 413 from ngx_http_core_find_config_phase() for requests with Content-Length. diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -228,6 +228,11 @@ done: } if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + + r->lingering_close = 1; + r->headers_in.content_length_n = 0; + r->request_body->bufs = NULL; + r->main->count--; r->read_event_handler = ngx_http_block_reading; } @@ -298,6 +303,11 @@ ngx_http_read_client_request_body_handle rc = ngx_http_do_read_client_request_body(r); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + + r->lingering_close = 1; + r->headers_in.content_length_n = 0; + r->request_body->bufs = NULL; + r->read_event_handler = ngx_http_block_reading; ngx_http_finalize_request(r, rc); } @@ -1161,8 +1171,6 @@ ngx_http_request_body_chunked_filter(ngx ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "client sent too many chunk extensions"); - r->lingering_close = 1; - return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; } @@ -1176,8 +1184,6 @@ ngx_http_request_body_chunked_filter(ngx r->headers_in.content_length_n, rb->chunked->size); - r->lingering_close = 1; - return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; } @@ -1276,8 +1282,6 @@ ngx_http_request_body_chunked_filter(ngx "client sent too many chunk extensions " "or trailer headers"); - r->lingering_close = 1; - return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; } diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -1106,7 +1106,11 @@ ngx_http_v2_state_read_data(ngx_http_v2_ stream->in_closed, 0); if (rc != NGX_OK && rc != NGX_AGAIN) { + stream->skip_data = 1; + r->headers_in.content_length_n = 0; + r->request_body->bufs = NULL; + ngx_http_finalize_request(r, rc); } @@ -3768,6 +3772,7 @@ ngx_http_v2_run_request(ngx_http_request "client prematurely closed stream"); r->stream->skip_data = 1; + r->headers_in.content_length_n = 0; ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); goto failed; @@ -4199,7 +4204,11 @@ ngx_http_v2_read_client_request_body_han rc = ngx_http_v2_process_request_body(r, NULL, 0, r->stream->in_closed, 1); if (rc != NGX_OK && rc != NGX_AGAIN) { + r->stream->skip_data = 1; + r->headers_in.content_length_n = 0; + r->request_body->bufs = NULL; + ngx_http_finalize_request(r, rc); return; } @@ -4262,6 +4271,8 @@ ngx_http_v2_read_client_request_body_han error: stream->skip_data = 1; + r->headers_in.content_length_n = 0; + r->request_body->bufs = NULL; ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -1291,6 +1291,10 @@ ngx_http_v3_read_client_request_body_han rc = ngx_http_v3_do_read_client_request_body(r); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + + r->headers_in.content_length_n = 0; + r->request_body->bufs = NULL; + ngx_http_finalize_request(r, rc); } } From mdounin at mdounin.ru Sat Apr 20 00:59:59 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 20 Apr 2024 03:59:59 +0300 Subject: [PATCH 11 of 14] Request body: handling of body after unbuffered reading In-Reply-To: References: Message-ID: <718f5b5737277ed252d7.1713574799@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1713574643 -10800 # Sat Apr 20 03:57:23 2024 +0300 # Node ID 718f5b5737277ed252d7bddd2f60c47c2a05f42a # Parent 6ce5ad05c4f81b9a9dd626c16a3f1e424ef9010f Request body: handling of body after unbuffered reading. As long as unbuffered reading of the request body was used, and an attempt to read the request body is made again, such as when redirecting the request to an error page, the request body is now cleared to make sure it can be used safely. Further, the r->reading_body flag, if it is still set, is cleared (along with disabling keepalive and enabling lingering close), so the code which uses the request body, such as when proxying, is not confused and doesn't try to use "Transfer-Encoding: chunked". Note that this change makes the workaround for HTTP/2 issues with unbuffered proxying and error pages, as introduced in 7561:9f1f9d6e056a, ineffective (since r->reading_body now cleared along with r->reading_body_no_buffering). Though the workaround is anyway not needed after 7924:d9e009b39596, hence it is removed. This makes it safer to use complex processing of error pages with unbuffered proxying. diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -305,6 +305,7 @@ typedef struct { ngx_chain_t *busy; ngx_http_chunked_t *chunked; ngx_http_client_body_handler_pt post_handler; + unsigned no_buffering:1; unsigned filter_need_buffering:1; unsigned last_sent:1; unsigned last_saved:1; diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -44,6 +44,18 @@ ngx_http_read_client_request_body(ngx_ht if (r != r->main || r->request_body || r->discard_body) { r->request_body_no_buffering = 0; + + if (r->request_body && r->request_body->no_buffering) { + r->headers_in.content_length_n = 0; + r->request_body->bufs = NULL; + + if (r->reading_body) { + r->reading_body = 0; + r->keepalive = 0; + r->lingering_close = 1; + } + } + post_handler(r); return NGX_OK; } @@ -72,6 +84,7 @@ ngx_http_read_client_request_body(ngx_ht * rb->busy = NULL; * rb->chunked = NULL; * rb->received = 0; + * rb->no_buffering = 0; * rb->filter_need_buffering = 0; * rb->last_sent = 0; * rb->last_saved = 0; @@ -220,6 +233,7 @@ done: } else { /* rc == NGX_AGAIN */ r->reading_body = 1; + r->request_body->no_buffering = 1; } r->read_event_handler = ngx_http_block_reading; diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -1078,13 +1078,6 @@ ngx_http_v2_state_read_data(ngx_http_v2_ r = stream->request; fc = r->connection; - if (r->reading_body && !r->request_body_no_buffering) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "skipping http2 DATA frame"); - - return ngx_http_v2_state_skip_padded(h2c, pos, end); - } - if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, "skipping http2 DATA frame"); From mdounin at mdounin.ru Sat Apr 20 01:00:00 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 20 Apr 2024 04:00:00 +0300 Subject: [PATCH 12 of 14] Request body: error_page 413 handling with HTTP/2 and HTTP/3 In-Reply-To: References: Message-ID: # HG changeset patch # User Maxim Dounin # Date 1713574645 -10800 # Sat Apr 20 03:57:25 2024 +0300 # Node ID b842b7f11593169f0b44cc1b6aa74e631c17c292 # Parent 718f5b5737277ed252d7bddd2f60c47c2a05f42a Request body: error_page 413 handling with HTTP/2 and HTTP/3. When the client_max_body_size limit in ngx_http_core_find_config_phase() is hit, nginx calls the ngx_http_discard_request_body() function, which normally sets the r->discard_body flag while discarding the body, and then reduces the r->headers_in.content_length_n field to 0 when the body is completely discarded. As such, the client_max_body_size check is skipped if the request is redirected to an error page, and this makes it possible to use "error_page 413" without additional settings. This only works with HTTP/1.x though. The HTTP/2 and HTTP/3 request body discarding code paths failed to set r->discard_body or reset r->headers_in.content_length_n, so configuring "error_page 413" did notwork without additionally clearing the client_max_body_size limit in the location with error page. Fix is to set r->headers_in.content_length_n to 0 in the HTTP/2 and HTTP/3 request body discarding code paths (if there is a body). This is essentially what happens with HTTP/1.x when the body is completely discarded, and makes it possible to use "error_page 413" with HTTP/2 and HTTP/3 without additional settings. Additionally, r->discard_body flag is also set. For HTTP/2, it is not needed, but serves as an optimization. For HTTP/3, it ensures that the request body cannot be read after it was discarded, thus bypassing the client_max_body_size limit. Further, the r->discard_body flag is now always set after the request body is discarded (and not cleared once it is fully discarded). While the body is being discarded, the new r->discarding_body flag is now used. This slightly optimizes existing code paths in ngx_http_read_client_request_body() and ngx_http_discard_request_body(), and also makes it easier to only set ngx_http_discarded_request_body_handler() for HTTP/1.x. diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -2456,6 +2456,7 @@ ngx_http_subrequest(ngx_http_request_t * sr->internal = 1; sr->discard_body = r->discard_body; + sr->discarding_body = r->discarding_body; sr->expect_tested = 1; sr->main_filter_need_in_memory = r->main_filter_need_in_memory; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -2762,7 +2762,7 @@ ngx_http_finalize_connection(ngx_http_re if (r->main->count != 1) { - if (r->discard_body) { + if (r->discarding_body) { r->read_event_handler = ngx_http_discarded_request_body_handler; ngx_add_timer(r->connection->read, clcf->lingering_timeout); @@ -2820,7 +2820,7 @@ ngx_http_set_write_handler(ngx_http_requ r->http_state = NGX_HTTP_WRITING_REQUEST_STATE; - r->read_event_handler = r->discard_body ? + r->read_event_handler = r->discarding_body ? ngx_http_discarded_request_body_handler: ngx_http_test_reading; r->write_event_handler = ngx_http_writer; diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -538,6 +538,7 @@ struct ngx_http_request_s { unsigned keepalive:1; unsigned lingering_close:1; unsigned discard_body:1; + unsigned discarding_body:1; unsigned reading_body:1; unsigned internal:1; unsigned error_page:1; diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -682,12 +682,24 @@ ngx_http_discard_request_body(ngx_http_r #if (NGX_HTTP_V2) if (r->stream) { r->stream->skip_data = 1; + + if (r->headers_in.content_length_n > 0 || r->headers_in.chunked) { + r->headers_in.content_length_n = 0; + r->discard_body = 1; + } + return NGX_OK; } #endif #if (NGX_HTTP_V3) if (r->http_version == NGX_HTTP_VERSION_30) { + + if (r->headers_in.content_length_n > 0 || r->headers_in.chunked) { + r->headers_in.content_length_n = 0; + r->discard_body = 1; + } + return NGX_OK; } #endif @@ -706,6 +718,8 @@ ngx_http_discard_request_body(ngx_http_r return NGX_OK; } + r->discard_body = 1; + size = r->header_in->last - r->header_in->pos; if (size || r->headers_in.chunked) { @@ -740,7 +754,7 @@ ngx_http_discard_request_body(ngx_http_r } r->count++; - r->discard_body = 1; + r->discarding_body = 1; return NGX_OK; } @@ -769,7 +783,7 @@ ngx_http_discarded_request_body_handler( timer = (ngx_msec_t) r->lingering_time - (ngx_msec_t) ngx_time(); if ((ngx_msec_int_t) timer <= 0) { - r->discard_body = 0; + r->discarding_body = 0; r->lingering_close = 0; ngx_http_finalize_request(r, NGX_ERROR); return; @@ -782,7 +796,7 @@ ngx_http_discarded_request_body_handler( rc = ngx_http_read_discarded_request_body(r); if (rc == NGX_OK) { - r->discard_body = 0; + r->discarding_body = 0; r->lingering_close = 0; r->lingering_time = 0; ngx_http_finalize_request(r, NGX_DONE); From mdounin at mdounin.ru Sat Apr 20 01:00:01 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 20 Apr 2024 04:00:01 +0300 Subject: [PATCH 13 of 14] Modified $content_length to match available request body length In-Reply-To: References: Message-ID: <4a81de7ba1d061f90960.1713574801@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1713574647 -10800 # Sat Apr 20 03:57:27 2024 +0300 # Node ID 4a81de7ba1d061f90960a5cc44d3a08e8c45fc1e # Parent b842b7f11593169f0b44cc1b6aa74e631c17c292 Modified $content_length to match available request body length. As long as the request body was discarded or there was an error during reading, it now follows r->headers_in.content_length_n and reflects the fact that no request body is available, similarly to how Content-Length as used by the proxy module does. This makes complex processing of various error pages safer, notably when using fastcgi_pass, uwsgi_pass, or grpc_pass, where the $content_length variable is used to set length. diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -1184,17 +1184,24 @@ ngx_http_variable_content_length(ngx_htt { u_char *p; - if (r->headers_in.content_length) { + if (r->reading_body && r->headers_in.content_length) { v->len = r->headers_in.content_length->value.len; v->data = r->headers_in.content_length->value.data; v->valid = 1; - v->no_cacheable = 0; + v->no_cacheable = 1; v->not_found = 0; } else if (r->reading_body) { v->not_found = 1; v->no_cacheable = 1; + } else if (r->discard_body) { + v->len = 1; + v->data = (u_char *) "0"; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + } else if (r->headers_in.content_length_n >= 0) { p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN); if (p == NULL) { @@ -1204,7 +1211,7 @@ ngx_http_variable_content_length(ngx_htt v->len = ngx_sprintf(p, "%O", r->headers_in.content_length_n) - p; v->data = p; v->valid = 1; - v->no_cacheable = 0; + v->no_cacheable = 1; v->not_found = 0; } else if (r->headers_in.chunked) { From mdounin at mdounin.ru Sat Apr 20 01:00:02 2024 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 20 Apr 2024 04:00:02 +0300 Subject: [PATCH 14 of 14] Request body: discarded body now treated as no body In-Reply-To: References: Message-ID: <7462f09d224220ae96ad.1713574802@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1713574649 -10800 # Sat Apr 20 03:57:29 2024 +0300 # Node ID 7462f09d224220ae96ad181c823c2760cec8ba54 # Parent 4a81de7ba1d061f90960a5cc44d3a08e8c45fc1e Request body: discarded body now treated as no body. Notably, proxying of such requests now uses no Content-Length instead of "Content-Length: 0", and the $content_length variable is empty (instead of "0"). This might be beneficial from correctness point of view, since requests with discarded body, such as during processing of error pages, do not pretend there is a zero-length body, but instead do not contain body at all. For example, this might be important for PUT requests, where a zero-length body could be incorrectly interpreted as a real request body. This also slightly simplifies the code. diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -1342,7 +1342,7 @@ ngx_http_proxy_create_request(ngx_http_r ctx->internal_chunked = 1; } else if (r->discard_body) { - ctx->internal_body_length = 0; + ctx->internal_body_length = -1; } else { ctx->internal_body_length = r->headers_in.content_length_n; diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -46,7 +46,7 @@ ngx_http_read_client_request_body(ngx_ht r->request_body_no_buffering = 0; if (r->request_body && r->request_body->no_buffering) { - r->headers_in.content_length_n = 0; + r->discard_body = 1; r->request_body->bufs = NULL; if (r->reading_body) { @@ -244,7 +244,7 @@ done: if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { r->lingering_close = 1; - r->headers_in.content_length_n = 0; + r->discard_body = 1; r->request_body->bufs = NULL; r->main->count--; @@ -319,7 +319,7 @@ ngx_http_read_client_request_body_handle if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { r->lingering_close = 1; - r->headers_in.content_length_n = 0; + r->discard_body = 1; r->request_body->bufs = NULL; r->read_event_handler = ngx_http_block_reading; @@ -679,27 +679,17 @@ ngx_http_discard_request_body(ngx_http_r ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http set discard body"); + r->discard_body = 1; + #if (NGX_HTTP_V2) if (r->stream) { r->stream->skip_data = 1; - - if (r->headers_in.content_length_n > 0 || r->headers_in.chunked) { - r->headers_in.content_length_n = 0; - r->discard_body = 1; - } - return NGX_OK; } #endif #if (NGX_HTTP_V3) if (r->http_version == NGX_HTTP_VERSION_30) { - - if (r->headers_in.content_length_n > 0 || r->headers_in.chunked) { - r->headers_in.content_length_n = 0; - r->discard_body = 1; - } - return NGX_OK; } #endif @@ -718,8 +708,6 @@ ngx_http_discard_request_body(ngx_http_r return NGX_OK; } - r->discard_body = 1; - size = r->header_in->last - r->header_in->pos; if (size || r->headers_in.chunked) { diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -1196,11 +1196,7 @@ ngx_http_variable_content_length(ngx_htt v->no_cacheable = 1; } else if (r->discard_body) { - v->len = 1; - v->data = (u_char *) "0"; - v->valid = 1; - v->no_cacheable = 0; - v->not_found = 0; + v->not_found = 1; } else if (r->headers_in.content_length_n >= 0) { p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN); diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -1101,7 +1101,7 @@ ngx_http_v2_state_read_data(ngx_http_v2_ if (rc != NGX_OK && rc != NGX_AGAIN) { stream->skip_data = 1; - r->headers_in.content_length_n = 0; + r->discard_body = 1; r->request_body->bufs = NULL; ngx_http_finalize_request(r, rc); @@ -3765,7 +3765,7 @@ ngx_http_v2_run_request(ngx_http_request "client prematurely closed stream"); r->stream->skip_data = 1; - r->headers_in.content_length_n = 0; + r->discard_body = 1; ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); goto failed; @@ -4199,7 +4199,7 @@ ngx_http_v2_read_client_request_body_han if (rc != NGX_OK && rc != NGX_AGAIN) { r->stream->skip_data = 1; - r->headers_in.content_length_n = 0; + r->discard_body = 1; r->request_body->bufs = NULL; ngx_http_finalize_request(r, rc); @@ -4264,7 +4264,7 @@ ngx_http_v2_read_client_request_body_han error: stream->skip_data = 1; - r->headers_in.content_length_n = 0; + r->discard_body = 1; r->request_body->bufs = NULL; ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -1292,7 +1292,7 @@ ngx_http_v3_read_client_request_body_han if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { - r->headers_in.content_length_n = 0; + r->discard_body = 1; r->request_body->bufs = NULL; ngx_http_finalize_request(r, rc); From mdounin at mdounin.ru Sat Apr 20 01:28:53 2024 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sat, 20 Apr 2024 04:28:53 +0300 Subject: [PATCH 00 of 14] request body discarding cleanup In-Reply-To: References: Message-ID: Hello! On Sat, Apr 20, 2024 at 03:59:48AM +0300, Maxim Dounin wrote: > The following patch series is focused on various edge cases related > to reading and discarding of a request body. > > Notably, it should be now safe (from the request body point of view) > to configure complex processing for errors, such as 413 (Request Entity > Too Large), including proxying, without additional precautions, such > as "proxy_set_header Content-Length '';" and "proxy_pass_request_body off;". > > The series also fixes the issue observed with error_page 413 and HTTP/2 > (and HTTP/3), which previously required relaxed client_max_body_size > limit in the error page location to work. > > Review and testing appreciated. And here are tests: # HG changeset patch # User Maxim Dounin # Date 1712763700 -10800 # Wed Apr 10 18:41:40 2024 +0300 # Node ID 9e62f95165d75c2d78b07d88f46f8aeca57697b5 # Parent fe80aebae728b6e8fccafe1ded1e8ae12a0ac72d Tests: fixed HTTP/3 stream offset adjustment when sending body. Previously, size of the body without the DATA frame header was used, resulting in incorrect offset being used in further QUIC frames, making it impossible to send the request body in multiple DATA frames. diff --git a/lib/Test/Nginx/HTTP3.pm b/lib/Test/Nginx/HTTP3.pm --- a/lib/Test/Nginx/HTTP3.pm +++ b/lib/Test/Nginx/HTTP3.pm @@ -470,7 +470,7 @@ sub h3_body { my $buf = pack_body($self, $body) if defined $body; my $offset = $self->{streams}{$sid}{sent}; - $self->{streams}{$sid}{sent} += length($body); + $self->{streams}{$sid}{sent} += length($buf); $self->raw_write($self->build_stream($buf, start => $extra->{body_more}, sid => $sid, offset => $offset)); } # HG changeset patch # User Maxim Dounin # Date 1712763707 -10800 # Wed Apr 10 18:41:47 2024 +0300 # Node ID 5f664310b16b810e820880dd51d0151ce99a8464 # Parent 9e62f95165d75c2d78b07d88f46f8aeca57697b5 Tests: fixed HTTP/3 build_int() for large integers. diff --git a/lib/Test/Nginx/HTTP3.pm b/lib/Test/Nginx/HTTP3.pm --- a/lib/Test/Nginx/HTTP3.pm +++ b/lib/Test/Nginx/HTTP3.pm @@ -2087,14 +2087,14 @@ sub build_int { } else { pack("C*", - build_int_set->($value, 7, 3), - build_int_set->($value, 6, 0), - build_int_set->($value, 5, 0), - build_int_set->($value, 4, 0), - build_int_set->($value, 3, 0), - build_int_set->($value, 2, 0), - build_int_set->($value, 1, 0), - build_int_set->($value, 0, 0), + $build_int_set->($value, 7, 3), + $build_int_set->($value, 6, 0), + $build_int_set->($value, 5, 0), + $build_int_set->($value, 4, 0), + $build_int_set->($value, 3, 0), + $build_int_set->($value, 2, 0), + $build_int_set->($value, 1, 0), + $build_int_set->($value, 0, 0), ); } } # HG changeset patch # User Maxim Dounin # Date 1713557015 -10800 # Fri Apr 19 23:03:35 2024 +0300 # Node ID b6396847375982059a2a61992676ca0256279b03 # Parent 5f664310b16b810e820880dd51d0151ce99a8464 Tests: adjusted http_headers_multi.t for $content_length changes. The $content_length variable is going to be not available after discarding the request body. As such, the relevant location is now proxied, so the request body is not discarded. diff --git a/http_headers_multi.t b/http_headers_multi.t --- a/http_headers_multi.t +++ b/http_headers_multi.t @@ -96,7 +96,7 @@ http { add_header X-Host $host; add_header X-Remote-User $remote_user; - return 204; + proxy_pass http://127.0.0.1:8080/backend; } location /d { # HG changeset patch # User Maxim Dounin # Date 1713562558 -10800 # Sat Apr 20 00:35:58 2024 +0300 # Node ID 94d7d4030fccc84fa5d27230dc0e1dd2367e9194 # Parent b6396847375982059a2a61992676ca0256279b03 Tests: tests for usage of discarded body. The client_max_body_size limit should be ignored when the request body is already discarded. In HTTP/1.x, this is done by checking the r->discard_body flag when the body is being discarded, and because r->headers_in.content_length_n is 0 when it's already discarded. This, however, does not happen with HTTP/2 and HTTP/3, and therefore "error_page 413" does not work without relaxing the limit. Further, with proxy_pass, r->headers_in.content_length_n is used to determine length of the request body, and therefore is not correct if discarding of the request body isn't yet complete. While discarding the request body, r->headers_in.content_length_n contains the rest of the body to discard (or, in case of chunked request body, the rest of the current chunk to discard). Similarly, the $content_length variable uses r->headers_in.content_length if available, and also incorrect. The $content_length variable is used when proxying with fastcgi_pass, grpc_pass, and uwsgi_pass (scgi_pass uses the value calculated based on the actual request body buffers, and therefore works correctly). diff --git a/body_discard.t b/body_discard.t new file mode 100644 --- /dev/null +++ b/body_discard.t @@ -0,0 +1,449 @@ +#!/usr/bin/perl + +# (C) Maxim Dounin + +# Tests for discarding request body. + +############################################################################### + +use warnings; +use strict; + +use Test::More; +use Socket qw/ CRLF /; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +my $t = Test::Nginx->new() + ->has(qw/http proxy rewrite addition memcached/); + +plan(skip_all => 'not yet') unless $t->has_version('1.25.5'); + +$t->plan(33)->write_file_expand('nginx.conf', <<'EOF'); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +http { + %%TEST_GLOBALS_HTTP%% + + server { + listen 127.0.0.1:8080; + server_name localhost; + + lingering_timeout 1s; + add_header X-Body body:$content_length:$request_body:; + + client_max_body_size 1k; + + error_page 400 /proxy/error400; + + location / { + error_page 413 /error413; + proxy_pass http://127.0.0.1:8082; + } + + location /error413 { + return 200 "custom error 413"; + } + + location /add { + return 200 "main response"; + add_before_body /add/before; + addition_types *; + client_max_body_size 1m; + } + + location /add/before { + proxy_pass http://127.0.0.1:8081; + } + + location /memcached { + client_max_body_size 1m; + error_page 502 /memcached/error502; + memcached_pass 127.0.0.1:8083; + set $memcached_key $request_uri; + } + + location /memcached/error502 { + proxy_pass http://127.0.0.1:8081; + } + + location /proxy { + client_max_body_size 1; + error_page 413 /proxy/error413; + error_page 400 /proxy/error400; + error_page 502 /proxy/error502; + proxy_pass http://127.0.0.1:8083; + } + + location /proxy/error413 { + proxy_pass http://127.0.0.1:8081; + } + + location /proxy/error400 { + proxy_pass http://127.0.0.1:8081; + } + + location /proxy/error502 { + proxy_pass http://127.0.0.1:8081; + } + + location /unbuf { + client_max_body_size 1m; + error_page 502 /unbuf/error502; + proxy_pass http://127.0.0.1:8083; + proxy_request_buffering off; + proxy_http_version 1.1; + } + + location /unbuf/error502 { + client_max_body_size 1m; + proxy_pass http://127.0.0.1:8081; + } + + location /length { + client_max_body_size 1; + error_page 413 /length/error413; + error_page 502 /length/error502; + proxy_pass http://127.0.0.1:8083; + } + + location /length/error413 { + return 200 "frontend body:$content_length:$request_body:"; + } + + location /length/error502 { + return 200 "frontend body:$content_length:$request_body:"; + } + } + + server { + listen 127.0.0.1:8081; + server_name localhost; + + location / { + proxy_pass http://127.0.0.1:8082; + proxy_set_header X-Body body:$content_length:$request_body:; + } + } + + server { + listen 127.0.0.1:8082; + server_name localhost; + + return 200 "backend $http_x_body"; + } + + server { + listen 127.0.0.1:8083; + server_name localhost; + + return 444; + } +} + +EOF + +$t->run(); + +############################################################################### + +# error_page 413 should work without redefining client_max_body_size + +like(http( + 'POST / HTTP/1.0' . CRLF . + 'Content-Length: 10000' . CRLF . CRLF . + '0123456789' +), qr/ 413 .*custom error 413/s, 'custom error 413'); + +# subrequest after discarding body + +like(http( + 'GET /add HTTP/1.0' . CRLF . CRLF +), qr/backend body:::.*main response/s, 'add'); + +like(http( + 'POST /add HTTP/1.0' . CRLF . + 'Content-Length: 10' . CRLF . CRLF . + '0123456789' +), qr/backend body:::.*main response/s, 'add small'); + +like(http( + 'POST /add HTTP/1.0' . CRLF . + 'Content-Length: 10000' . CRLF . CRLF . + '0123456789' +), qr/backend body:::.*main response/s, 'add long'); + +like(http( + 'POST /add HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + 'a' . CRLF . + '0123456789' . CRLF . + '0' . CRLF . CRLF +), qr/backend body:::.*main response/s, 'add chunked'); + +like(http( + 'POST /add HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + '1' . CRLF . + 'X' . CRLF . + '9' . CRLF . + '123456789' . CRLF . + '0' . CRLF . CRLF +), qr/backend body:::.*main response/s, 'add chunked multi'); + +like(http( + 'POST /add HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + 'ffff' . CRLF . + '0123456789' +), qr/backend body:::.*main response/s, 'add chunked long'); + +# error_page 502 with proxy_pass after discarding body + +like(http( + 'GET /memcached HTTP/1.0' . CRLF . CRLF +), qr/ 502 .*backend body:::/s, 'memcached'); + +like(http( + 'GET /memcached HTTP/1.0' . CRLF . + 'Content-Length: 10' . CRLF . CRLF . + '0123456789' +), qr/ 502 .*backend body:::/s, 'memcached small'); + +like(http( + 'GET /memcached HTTP/1.0' . CRLF . + 'Content-Length: 10000' . CRLF . CRLF . + '0123456789' +), qr/ 502 .*backend body:::/s, 'memcached long'); + +like(http( + 'GET /memcached HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + 'a' . CRLF . + '0123456789' . CRLF . + '0' . CRLF . CRLF +), qr/ 502 .*backend body:::/s, 'memcached chunked'); + +like(http( + 'GET /memcached HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + '1' . CRLF . + 'X' . CRLF . + '9' . CRLF . + '123456789' . CRLF . + '0' . CRLF . CRLF +), qr/ 502 .*backend body:::/s, 'memcached chunked multi'); + +like(http( + 'GET /memcached HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + 'ffff' . CRLF . + '0123456789' +), qr/ 502 .*backend body:::/s, 'memcached chunked long'); + +# error_page 413 with proxy_pass + +like(http( + 'GET /proxy HTTP/1.0' . CRLF . CRLF +), qr/ 502 .*backend body:::/s, 'proxy'); + +like(http( + 'POST /proxy HTTP/1.0' . CRLF . + 'Content-Length: 10' . CRLF . CRLF . + '0123456789' +), qr/ 413 .*backend body:::/s, 'proxy small'); + +like(http( + 'POST /proxy HTTP/1.0' . CRLF . + 'Content-Length: 10000' . CRLF . CRLF . + '0123456789' +), qr/ 413 .*backend body:::/s, 'proxy long'); + +like(http( + 'POST /proxy HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + 'a' . CRLF . + '0123456789' . CRLF . + '0' . CRLF . CRLF +), qr/ 413 .*backend body:::/s, 'proxy chunked'); + +like(http( + 'POST /proxy HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + '1' . CRLF . + 'X' . CRLF . + '9' . CRLF . + '123456789' . CRLF . + '0' . CRLF . CRLF +), qr/ 413 .*backend body:::/s, 'proxy chunked multi'); + +like(http( + 'POST /proxy HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + 'ffff' . CRLF . + '0123456789' +), qr/ 413 .*backend body:::/s, 'proxy chunked long'); + +# error_page 400 with proxy_pass + +# note that "chunked and length" test triggers 400 during parsing +# request headers, and therefore needs error_page at server level + +like(http( + 'POST /proxy HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + '1' . CRLF . + 'X' . CRLF . + 'X' . CRLF +), qr/ 400 .*backend body:::/s, 'proxy chunked bad'); + +like(http( + 'POST /proxy HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Content-Length: 10' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + '0' . CRLF . CRLF +), qr/ 400 .*backend body:::/s, 'proxy chunked and length'); + +# error_page 502 after proxy with request buffering disabled + +like(http( + 'GET /unbuf HTTP/1.0' . CRLF . CRLF +), qr/ 502 .*backend body:::/s, 'unbuf proxy'); + +like(http( + 'POST /unbuf HTTP/1.0' . CRLF . + 'Content-Length: 10' . CRLF . CRLF . + '0', + sleep => 0.1, + body => + '123456789' +), qr/ 502 .*backend body:::/s, 'unbuf proxy small'); + +like(http( + 'POST /unbuf HTTP/1.0' . CRLF . + 'Content-Length: 10000' . CRLF . CRLF . + '0123456789' +), qr/ 502 .*backend body:::/s, 'unbuf proxy long'); + +like(http( + 'POST /unbuf HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF, + sleep => 0.1, + body => + 'a' . CRLF . + '0123456789' . CRLF . + '0' . CRLF . CRLF +), qr/ 502 .*backend body:::/s, 'unbuf proxy chunked'); + +like(http( + 'POST /unbuf HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + '1' . CRLF . + 'X' . CRLF, + sleep => 0.1, + body => + '9' . CRLF . + '123456789' . CRLF . + '0' . CRLF . CRLF +), qr/ 502 .*backend body:::/s, 'unbuf proxy chunked multi'); + +like(http( + 'POST /unbuf HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + 'ffff' . CRLF . + '0123456789' +), qr/ 502 .*backend body:::/s, 'unbuf proxy chunked long'); + +# error_page 413 and $content_length +# (used in fastcgi_pass, grpc_pass, uwsgi_pass) + +like(http( + 'GET /length HTTP/1.0' . CRLF . CRLF +), qr/ 502 .*frontend body:::/s, '$content_length'); + +like(http( + 'POST /length HTTP/1.0' . CRLF . + 'Content-Length: 10' . CRLF . CRLF . + '0123456789' +), qr/ 413 .*frontend body:::/s, '$content_length small'); + +like(http( + 'POST /length HTTP/1.0' . CRLF . + 'Content-Length: 10000' . CRLF . CRLF . + '0123456789' +), qr/ 413 .*frontend body:::/s, '$content_length long'); + +like(http( + 'POST /length HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + 'a' . CRLF . + '0123456789' . CRLF . + '0' . CRLF . CRLF +), qr/ 413 .*frontend body:::/s, '$content_length chunked'); + +like(http( + 'POST /length HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + '1' . CRLF . + 'X' . CRLF . + '9' . CRLF . + '123456789' . CRLF . + '0' . CRLF . CRLF +), qr/ 413 .*frontend body:::/s, '$content_length chunked multi'); + +like(http( + 'POST /length HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + 'ffff' . CRLF . + '0123456789' +), qr/ 413 .*frontend body:::/s, '$content_length chunked long'); + +############################################################################### diff --git a/h2_request_body_discard.t b/h2_request_body_discard.t new file mode 100644 --- /dev/null +++ b/h2_request_body_discard.t @@ -0,0 +1,401 @@ +#!/usr/bin/perl + +# (C) Maxim Dounin + +# Tests for discarding request body with HTTP/2. + +############################################################################### + +use warnings; +use strict; + +use Test::More; +use Socket qw/ CRLF /; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; +use Test::Nginx::HTTP2; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +my $t = Test::Nginx->new() + ->has(qw/http http_v2 proxy rewrite addition/); + +plan(skip_all => 'not yet') unless $t->has_version('1.25.5'); + +$t->plan(34)->write_file_expand('nginx.conf', <<'EOF'); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +http { + %%TEST_GLOBALS_HTTP%% + + server { + listen 127.0.0.1:8080; + server_name localhost; + + http2 on; + + lingering_timeout 1s; + add_header X-Body body:$content_length:$request_body:; + + client_max_body_size 1k; + + error_page 400 /proxy/error400; + + location / { + error_page 413 /error413; + proxy_pass http://127.0.0.1:8082; + } + + location /error413 { + return 200 "custom error 413"; + } + + location /add { + return 200 "main response"; + add_before_body /add/before; + addition_types *; + client_max_body_size 1m; + } + + location /add/before { + proxy_pass http://127.0.0.1:8081; + } + + location /memcached { + client_max_body_size 1m; + error_page 502 /memcached/error502; + memcached_pass 127.0.0.1:8083; + set $memcached_key $request_uri; + } + + location /memcached/error502 { + proxy_pass http://127.0.0.1:8081; + } + + location /proxy { + client_max_body_size 3; + error_page 413 /proxy/error413; + error_page 400 /proxy/error400; + error_page 502 /proxy/error502; + proxy_pass http://127.0.0.1:8083; + } + + location /proxy/error413 { + proxy_pass http://127.0.0.1:8081; + } + + location /proxy/error400 { + proxy_pass http://127.0.0.1:8081; + } + + location /proxy/error502 { + proxy_pass http://127.0.0.1:8081; + } + + location /unbuf { + client_max_body_size 1m; + error_page 502 /unbuf/error502; + proxy_pass http://127.0.0.1:8083; + proxy_request_buffering off; + proxy_http_version 1.1; + } + + location /unbuf/error502 { + client_max_body_size 1m; + proxy_pass http://127.0.0.1:8081; + } + + location /length { + client_max_body_size 1; + error_page 413 /length/error413; + error_page 502 /length/error502; + proxy_pass http://127.0.0.1:8083; + } + + location /length/error413 { + return 200 "frontend body:$content_length:$request_body:"; + } + + location /length/error502 { + return 200 "frontend body:$content_length:$request_body:"; + } + } + + server { + listen 127.0.0.1:8081; + server_name localhost; + + location / { + proxy_pass http://127.0.0.1:8082; + proxy_set_header X-Body body:$content_length:$request_body:; + } + } + + server { + listen 127.0.0.1:8082; + server_name localhost; + + return 200 "backend $http_x_body"; + } + + server { + listen 127.0.0.1:8083; + server_name localhost; + + return 444; + } +} + +EOF + +$t->run(); + +############################################################################### + +# error_page 413 should work without redefining client_max_body_size + +like(http2_get_body('/', '0123456789' x 128), + qr/status: 413.*custom error 413/s, 'custom error 413'); + +# subrequest after discarding body + +like(http2_get('/add'), qr/backend body:::.*main response/s, 'add'); + +like(http2_get_body('/add', '0123456789'), + qr/backend body:::.*main response/s, 'add small'); + +like(http2_get_body_incomplete('/add', 10000, '0123456789'), + qr/backend body:::.*main response/s, 'add long'); + +like(http2_get_body_nolen('/add', '0123456789'), + qr/backend body:::.*main response/s, 'add nolen'); + +like(http2_get_body_nolen('/add', '0', '123456789'), + qr/backend body:::.*main response/s, 'add nolen multi'); + +like(http2_get_body_incomplete_nolen('/add', 10000, '0123456789'), + qr/backend body:::.*main response/s, 'add chunked long'); + +# error_page 502 with proxy_pass after discarding body + +like(http2_get('/memcached'), qr/backend body:::/s, 'memcached'); + +like(http2_get_body('/memcached', '0123456789'), + qr/status: 502.*backend body:::/s, 'memcached small'); + +like(http2_get_body_incomplete('/memcached', 10000, '0123456789'), + qr/status: 502.*backend body:::/s, 'memcached long'); + +like(http2_get_body_nolen('/memcached', '0123456789'), + qr/status: 502.*backend body:::/s, 'memcached nolen'); + +like(http2_get_body_nolen('/memcached', '0', '123456789'), + qr/status: 502.*backend body:::/s, 'memcached nolen multi'); + +like(http2_get_body_incomplete_nolen('/memcached', 10000, '0123456789'), + qr/status: 502.*backend body:::/s, 'memcached nolen long'); + +# error_page 413 with proxy_pass + +like(http2_get('/proxy'), + qr/status: 502.*backend body:::/s, 'proxy'); + +like(http2_get_body('/proxy', '0123456789'), + qr/status: 413.*backend body:::/s, 'proxy small'); + +like(http2_get_body_incomplete('/proxy', 10000, '0123456789'), + qr/status: 413.*backend body:::/s, 'proxy long'); + +like(http2_get_body_nolen('/proxy', '0123456789'), + qr/status: 413.*backend body:::/s, 'proxy nolen'); + +like(http2_get_body_nolen('/proxy', '0', '123456789'), + qr/status: 413.*backend body:::/s, 'proxy nolen multi'); + +like(http2_get_body_incomplete_nolen('/proxy', 10000, '0123456789'), + qr/status: 413.*backend body:::/s, 'proxy nolen long'); + +# error_page 400 with proxy_pass + +# note that "proxy too short" test triggers 400 during parsing +# request headers, and therefore needs error_page at server level + +like(http2_get_body_custom('/proxy', 1), + qr/status: 400.*backend body:::/s, 'proxy too short'); +like(http2_get_body_custom('/proxy', 1, ''), + qr/status: 400.*backend body:::/s, 'proxy too short body'); +like(http2_get_body_custom('/proxy', 1, '01'), + qr/status: 400.*backend body:::/s, 'proxy too long'); + +# error_page 502 after proxy with request buffering disabled + +like(http2_get('/unbuf'), + qr/status: 502.*backend body:::/s, 'unbuf proxy'); + +like(http2_get_body('/unbuf', '0123456789'), + qr/status: 502.*backend body:::/s, 'unbuf proxy small'); + +like(http2_get_body_incomplete('/unbuf', 10000, '0123456789'), + qr/status: 502.*backend body:::/s, 'unbuf proxy long'); + +like(http2_get_body_nolen('/unbuf', '0123456789'), + qr/status: 502.*backend body:::/s, 'unbuf proxy nolen'); + +like(http2_get_body_nolen('/unbuf', '0', '123456789'), + qr/status: 502.*backend body:::/s, 'unbuf proxy nolen multi'); + +like(http2_get_body_incomplete_nolen('/unbuf', 10000, '0123456789'), + qr/status: 502.*backend body:::/s, 'unbuf proxy nolen long'); + +# error_page 413 and $content_length +# (used in fastcgi_pass, grpc_pass, uwsgi_pass) + +like(http2_get('/length'), + qr/status: 502.*frontend body:::/s, '$content_length'); + +like(http2_get_body('/length', '0123456789'), + qr/status: 413.*frontend body:::/s, '$content_length small'); + +like(http2_get_body_incomplete('/length', 10000, '0123456789'), + qr/status: 413.*frontend body:::/s, '$content_length long'); + +like(http2_get_body_nolen('/length', '0123456789'), + qr/status: 413.*frontend body:::/s, '$content_length nolen'); + +like(http2_get_body_nolen('/length', '0', '123456789'), + qr/status: 413.*frontend body:::/s, '$content_length nolen multi'); + +like(http2_get_body_incomplete_nolen('/length', 10000, '0123456789'), + qr/status: 413.*frontend body:::/s, '$content_length nolen long'); + +############################################################################### + +sub http2_get { + my ($uri) = @_; + + my $s = Test::Nginx::HTTP2->new(); + my $sid = $s->new_stream({ path => $uri }); + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my ($data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" . $data->{data}; +} + +sub http2_get_body { + my ($uri, $body) = @_; + + my $s = Test::Nginx::HTTP2->new(); + my $sid = $s->new_stream({ path => $uri, body => $body }); + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my ($data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" . $data->{data}; +} + +sub http2_get_body_nolen { + my ($uri, $body, $body2) = @_; + + my $s = Test::Nginx::HTTP2->new(); + my $sid = $s->new_stream({ path => $uri, body_more => 1 }); + + if (defined $body2) { + $s->h2_body($body, { body_more => 1 }); + $s->h2_body($body2); + } else { + $s->h2_body($body); + } + + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my ($data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" . $data->{data}; +} + +sub http2_get_body_incomplete { + my ($uri, $len, $body) = @_; + + my $s = Test::Nginx::HTTP2->new(); + my $sid = $s->new_stream({ + headers => [ + { name => ':method', value => 'GET' }, + { name => ':scheme', value => 'http' }, + { name => ':path', value => $uri }, + { name => ':authority', value => 'localhost' }, + { name => 'content-length', value => $len }, + ], + body_more => 1 + }); + $s->h2_body($body, { body_more => 1 }); + + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my ($data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" . $data->{data}; +} + +sub http2_get_body_incomplete_nolen { + my ($uri, $len, $body) = @_; + + my $s = Test::Nginx::HTTP2->new(); + my $sid = $s->new_stream({ path => $uri, body_more => 1 }); + $s->h2_body($body, { body_more => 1 }); + + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my ($data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" . $data->{data}; +} + +sub http2_get_body_custom { + my ($uri, $len, $body) = @_; + + my $s = Test::Nginx::HTTP2->new(); + my $sid = $s->new_stream({ + headers => [ + { name => ':method', value => 'GET' }, + { name => ':scheme', value => 'http' }, + { name => ':path', value => $uri }, + { name => ':authority', value => 'localhost' }, + { name => 'content-length', value => $len }, + ], + body_more => (defined $body ? 1 : undef) + }); + $s->h2_body($body) if defined $body; + + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my ($data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" . $data->{data}; +} + +############################################################################### diff --git a/h3_request_body_discard.t b/h3_request_body_discard.t new file mode 100644 --- /dev/null +++ b/h3_request_body_discard.t @@ -0,0 +1,424 @@ +#!/usr/bin/perl + +# (C) Maxim Dounin + +# Tests for discarding request body with HTTP/3. + +############################################################################### + +use warnings; +use strict; + +use Test::More; +use Socket qw/ CRLF /; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; +use Test::Nginx::HTTP3; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +my $t = Test::Nginx->new() + ->has(qw/http http_v3 proxy rewrite addition memcached cryptx/) + ->has_daemon('openssl'); + +plan(skip_all => 'not yet') unless $t->has_version('1.25.5'); + +$t->plan(33)->write_file_expand('nginx.conf', <<'EOF'); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +http { + %%TEST_GLOBALS_HTTP%% + + ssl_certificate localhost.crt; + ssl_certificate_key localhost.key; + + server { + listen 127.0.0.1:%%PORT_8980_UDP%% quic; + server_name localhost; + + lingering_timeout 1s; + add_header X-Body body:$content_length:$request_body:; + + client_max_body_size 1k; + + location / { + error_page 413 /error413; + proxy_pass http://127.0.0.1:8082; + } + + location /error413 { + return 200 "custom error 413"; + } + + location /add { + return 200 "main response"; + add_before_body /add/before; + addition_types *; + client_max_body_size 1m; + } + + location /add/before { + proxy_pass http://127.0.0.1:8081; + } + + location /memcached { + client_max_body_size 1m; + error_page 502 /memcached/error502; + memcached_pass 127.0.0.1:8083; + set $memcached_key $request_uri; + } + + location /memcached/error502 { + proxy_pass http://127.0.0.1:8081; + } + + location /proxy { + client_max_body_size 3; + error_page 413 /proxy/error413; + error_page 400 /proxy/error400; + error_page 502 /proxy/error502; + proxy_pass http://127.0.0.1:8083; + } + + location /proxy/error413 { + proxy_pass http://127.0.0.1:8081; + } + + location /proxy/error400 { + proxy_pass http://127.0.0.1:8081; + } + + location /proxy/error502 { + proxy_pass http://127.0.0.1:8081; + } + + location /unbuf { + client_max_body_size 1m; + error_page 502 /unbuf/error502; + proxy_pass http://127.0.0.1:8083; + proxy_request_buffering off; + proxy_http_version 1.1; + } + + location /unbuf/error502 { + client_max_body_size 1m; + proxy_pass http://127.0.0.1:8081; + } + + location /length { + client_max_body_size 1; + error_page 413 /length/error413; + error_page 502 /length/error502; + proxy_pass http://127.0.0.1:8083; + } + + location /length/error413 { + return 200 "frontend body:$content_length:$request_body:"; + } + + location /length/error502 { + return 200 "frontend body:$content_length:$request_body:"; + } + } + + server { + listen 127.0.0.1:8081; + server_name localhost; + + location / { + proxy_pass http://127.0.0.1:8082; + proxy_set_header X-Body body:$content_length:$request_body:; + } + } + + server { + listen 127.0.0.1:8082; + server_name localhost; + + return 200 "backend $http_x_body"; + } + + server { + listen 127.0.0.1:8083; + server_name localhost; + + return 444; + } +} + +EOF + +$t->write_file('openssl.conf', <testdir(); + +foreach my $name ('localhost') { + system('openssl req -x509 -new ' + . "-config $d/openssl.conf -subj /CN=$name/ " + . "-out $d/$name.crt -keyout $d/$name.key " + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't create certificate for $name: $!\n"; +} + +$t->run(); + +############################################################################### + +# error_page 413 should work without redefining client_max_body_size + +like(http3_get_body('/', '0123456789' x 128), + qr/status: 413.*custom error 413/s, 'custom error 413'); + +# subrequest after discarding body + +like(http3_get('/add'), qr/backend body:::.*main response/s, 'add'); + +like(http3_get_body('/add', '0123456789'), + qr/backend body:::.*main response/s, 'add small'); + +like(http3_get_body_incomplete('/add', 10000, '0123456789'), + qr/backend body:::.*main response/s, 'add long'); + +like(http3_get_body_nolen('/add', '0123456789'), + qr/backend body:::.*main response/s, 'add nolen'); + +like(http3_get_body_nolen('/add', '0', '123456789'), + qr/backend body:::.*main response/s, 'add nolen multi'); + +like(http3_get_body_incomplete_nolen('/add', 10000, '0123456789'), + qr/backend body:::.*main response/s, 'add chunked long'); + +# error_page 502 with proxy_pass after discarding body + +like(http3_get('/memcached'), qr/backend body:::/s, 'memcached'); + +like(http3_get_body('/memcached', '0123456789'), + qr/status: 502.*backend body:::/s, 'memcached small'); + +like(http3_get_body_incomplete('/memcached', 10000, '0123456789'), + qr/status: 502.*backend body:::/s, 'memcached long'); + +like(http3_get_body_nolen('/memcached', '0123456789'), + qr/status: 502.*backend body:::/s, 'memcached nolen'); + +like(http3_get_body_nolen('/memcached', '0', '123456789'), + qr/status: 502.*backend body:::/s, 'memcached nolen multi'); + +like(http3_get_body_incomplete_nolen('/memcached', 10000, '0123456789'), + qr/status: 502.*backend body:::/s, 'memcached nolen long'); + +# error_page 413 with proxy_pass + +like(http3_get('/proxy'), + qr/status: 502.*backend body:::/s, 'proxy'); + +like(http3_get_body('/proxy', '0123456789'), + qr/status: 413.*backend body:::/s, 'proxy small'); + +like(http3_get_body_incomplete('/proxy', 10000, '0123456789'), + qr/status: 413.*backend body:::/s, 'proxy long'); + +like(http3_get_body_nolen('/proxy', '0123456789'), + qr/status: 413.*backend body:::/s, 'proxy nolen'); + +like(http3_get_body_nolen('/proxy', '0', '123456789'), + qr/status: 413.*backend body:::/s, 'proxy nolen multi'); + +like(http3_get_body_incomplete_nolen('/proxy', '0123456789'), + qr/status: 413.*backend body:::/s, 'proxy nolen long'); + +# error_page 400 with proxy_pass + +like(http3_get_body_custom('/proxy', 1, ''), + qr/status: 400.*backend body:::/s, 'proxy too short'); +like(http3_get_body_custom('/proxy', 1, '01'), + qr/status: 400.*backend body:::/s, 'proxy too long'); + +# error_page 502 after proxy with request buffering disabled + +like(http3_get('/unbuf'), + qr/status: 502.*backend body:::/s, 'unbuf proxy'); + +like(http3_get_body_custom('/unbuf', 10, '0123456789', sleep => 0.1), + qr/status: 502.*backend body:::/s, 'unbuf proxy small'); + +like(http3_get_body_incomplete('/unbuf', 10000, '0123456789'), + qr/status: 502.*backend body:::/s, 'unbuf proxy long'); + +like(http3_get_body_nolen('/unbuf', '0123456789'), + qr/status: 502.*backend body:::/s, 'unbuf proxy nolen'); + +like(http3_get_body_nolen('/unbuf', '0', '123456789'), + qr/status: 502.*backend body:::/s, 'unbuf proxy nolen multi'); + +like(http3_get_body_incomplete_nolen('/unbuf', 10000, '0123456789'), + qr/status: 502.*backend body:::/s, 'unbuf proxy nolen long'); + +# error_page 413 and $content_length +# (used in fastcgi_pass, grpc_pass, uwsgi_pass) + +like(http3_get('/length'), + qr/status: 502.*frontend body:::/s, '$content_length'); + +like(http3_get_body('/length', '0123456789'), + qr/status: 413.*frontend body:::/s, '$content_length small'); + +like(http3_get_body_incomplete('/length', 10000, '0123456789'), + qr/status: 413.*frontend body:::/s, '$content_length long'); + +like(http3_get_body_nolen('/length', '0123456789'), + qr/status: 413.*frontend body:::/s, '$content_length nolen'); + +like(http3_get_body_nolen('/length', '0', '123456789'), + qr/status: 413.*frontend body:::/s, '$content_length nolen multi'); + +like(http3_get_body_incomplete_nolen('/length', 10000, '0123456789'), + qr/status: 413.*frontend body:::/s, '$content_length nolen long'); + +############################################################################### + +sub http3_get { + my ($uri) = @_; + + my $s = Test::Nginx::HTTP3->new(); + my $sid = $s->new_stream({ path => $uri }); + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my (@data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" + . join("", map { $_->{data} } @data); +} + +sub http3_get_body { + my ($uri, $body) = @_; + + my $s = Test::Nginx::HTTP3->new(); + my $sid = $s->new_stream({ path => $uri, body => $body }); + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my (@data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" + . join("", map { $_->{data} } @data); +} + +sub http3_get_body_nolen { + my ($uri, $body, $body2) = @_; + + my $s = Test::Nginx::HTTP3->new(); + my $sid = $s->new_stream({ path => $uri, body_more => 1 }); + + if (defined $body2) { + select undef, undef, undef, 0.1; + $s->h3_body($body, $sid, { body_more => 1 }); + select undef, undef, undef, 0.1; + $s->h3_body($body2, $sid); + } else { + select undef, undef, undef, 0.1; + $s->h3_body($body, $sid); + } + + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my (@data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" + . join("", map { $_->{data} } @data); +} + +sub http3_get_body_incomplete { + my ($uri, $len, $body) = @_; + + my $s = Test::Nginx::HTTP3->new(); + my $sid = $s->new_stream({ + headers => [ + { name => ':method', value => 'GET' }, + { name => ':scheme', value => 'http' }, + { name => ':path', value => $uri }, + { name => ':authority', value => 'localhost' }, + { name => 'content-length', value => $len }, + ], + body_more => 1 + }); + $s->h3_body($body, $sid, { body_more => 1 }); + + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my (@data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" + . join("", map { $_->{data} } @data); +} + +sub http3_get_body_incomplete_nolen { + my ($uri, $body) = @_; + + my $s = Test::Nginx::HTTP3->new(); + my $sid = $s->new_stream({ path => $uri, body_more => 1 }); + $s->h3_body($body, $sid, { body_more => 1 }); + + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my (@data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" + . join("", map { $_->{data} } @data); +} + +sub http3_get_body_custom { + my ($uri, $len, $body, %extra) = @_; + + my $s = Test::Nginx::HTTP3->new(); + my $sid = $s->new_stream({ + headers => [ + { name => ':method', value => 'GET' }, + { name => ':scheme', value => 'http' }, + { name => ':path', value => $uri }, + { name => ':authority', value => 'localhost' }, + { name => 'content-length', value => $len }, + ], + body_more => 1 + }); + select undef, undef, undef, $extra{sleep} if $extra{sleep}; + $s->h3_body($body, $sid); + + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my (@data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" + . join("", map { $_->{data} } @data); +} + +############################################################################### -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Sat Apr 20 14:38:08 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sat, 20 Apr 2024 17:38:08 +0300 Subject: [xslscript] XSLScript: added CDATA support. Message-ID: details: http://freenginx.org/hg/xslscript/rev/cc0402def1f9 branches: changeset: 12:cc0402def1f9 user: Maxim Dounin date: Sun Apr 14 23:11:11 2024 +0300 description: XSLScript: added CDATA support. Notably, this simplifies adding javascript code to templates, which otherwise require complex escaping. Requested by Igor Sysoev. diffstat: xslscript.pl | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-) diffs (23 lines): diff --git a/xslscript.pl b/xslscript.pl --- a/xslscript.pl +++ b/xslscript.pl @@ -45,6 +45,7 @@ startrule : item(s) eofile { $return = $item{'item(s)'}; 1 } item : ").)*/ms "-->" { $return = ""; 1 } +# cdata, + +cdata : /((?!]]>).)*/ms "]]>" + { $return = ""; 1 } + # special chars: ', ", {, }, \ # if used in text, they needs to be escaped with backslash From mdounin at mdounin.ru Sat Apr 20 18:21:14 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sat, 20 Apr 2024 21:21:14 +0300 Subject: [nginx-tests] Tests: removed dependencies on 405 error text. Message-ID: details: http://freenginx.org/hg/nginx-tests/rev/c544b7120a6d branches: changeset: 1957:c544b7120a6d user: Maxim Dounin date: Sat Apr 20 20:58:42 2024 +0300 description: Tests: removed dependencies on 405 error text. It is going to be changed from "405 Not Allowed" to "405 Method Not Allowed" to match RFC description. diffstat: empty_gif.t | 2 +- http_method.t | 4 ++-- stub_status.t | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diffs (45 lines): diff --git a/empty_gif.t b/empty_gif.t --- a/empty_gif.t +++ b/empty_gif.t @@ -62,7 +62,7 @@ EOF is(http_get_body('/'), $gif, 'empty gif'); like(http_get('/'), qr!Content-Type: image/gif!i, 'get content type'); like(http_head('/'), qr!Content-Type: image/gif!i, 'head content type'); -like(http('PUT / HTTP/1.0' . CRLF . CRLF), qr!405 Not Allowed!i, 'put'); +like(http('PUT / HTTP/1.0' . CRLF . CRLF), qr! 405 !i, 'put'); ############################################################################### diff --git a/http_method.t b/http_method.t --- a/http_method.t +++ b/http_method.t @@ -49,7 +49,7 @@ EOF ############################################################################### -like(http(< details: http://freenginx.org/hg/nginx/rev/8aac1f3bb39b branches: changeset: 9248:8aac1f3bb39b user: Maxim Dounin date: Sat Apr 20 20:28:14 2024 +0300 description: Version bump. diffstat: src/core/nginx.h | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diffs (14 lines): diff --git a/src/core/nginx.h b/src/core/nginx.h --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1025005 -#define NGINX_VERSION "1.25.5" +#define nginx_version 1027000 +#define NGINX_VERSION "1.27.0" #define NGINX_NAME "freenginx" #define NGINX_VER NGINX_NAME "/" NGINX_VERSION From mdounin at mdounin.ru Sat Apr 20 18:21:52 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sat, 20 Apr 2024 21:21:52 +0300 Subject: [nginx] Updated HTTP status code descriptions to match RFC 9110. Message-ID: details: http://freenginx.org/hg/nginx/rev/afd36b161859 branches: changeset: 9249:afd36b161859 user: Michiel W. Beijen date: Sun Apr 14 12:23:40 2024 +0200 description: Updated HTTP status code descriptions to match RFC 9110. This patch changes the status codes and status pages served by freenginx to use the wording from RFC 9110. Exception for HTTP status code 413 "Request Entity Too Large", which is "Content Too Large" in the RFC. In that case we would lose information about what actually is too large, the request or the response. diffstat: src/http/ngx_http_header_filter_module.c | 14 +++++++------- src/http/ngx_http_special_response.c | 28 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 21 deletions(-) diffs (131 lines): diff --git a/src/http/ngx_http_header_filter_module.c b/src/http/ngx_http_header_filter_module.c --- a/src/http/ngx_http_header_filter_module.c +++ b/src/http/ngx_http_header_filter_module.c @@ -69,7 +69,7 @@ static ngx_str_t ngx_http_status_lines[] /* ngx_null_string, */ /* "300 Multiple Choices" */ ngx_string("301 Moved Permanently"), - ngx_string("302 Moved Temporarily"), + ngx_string("302 Found"), ngx_string("303 See Other"), ngx_string("304 Not Modified"), ngx_null_string, /* "305 Use Proxy" */ @@ -85,18 +85,18 @@ static ngx_str_t ngx_http_status_lines[] ngx_string("402 Payment Required"), ngx_string("403 Forbidden"), ngx_string("404 Not Found"), - ngx_string("405 Not Allowed"), + ngx_string("405 Method Not Allowed"), ngx_string("406 Not Acceptable"), ngx_null_string, /* "407 Proxy Authentication Required" */ - ngx_string("408 Request Time-out"), + ngx_string("408 Request Timeout"), ngx_string("409 Conflict"), ngx_string("410 Gone"), ngx_string("411 Length Required"), ngx_string("412 Precondition Failed"), ngx_string("413 Request Entity Too Large"), - ngx_string("414 Request-URI Too Large"), + ngx_string("414 URI Too Long"), ngx_string("415 Unsupported Media Type"), - ngx_string("416 Requested Range Not Satisfiable"), + ngx_string("416 Range Not Satisfiable"), ngx_null_string, /* "417 Expectation Failed" */ ngx_null_string, /* "418 unused" */ ngx_null_string, /* "419 unused" */ @@ -117,8 +117,8 @@ static ngx_str_t ngx_http_status_lines[] ngx_string("500 Internal Server Error"), ngx_string("501 Not Implemented"), ngx_string("502 Bad Gateway"), - ngx_string("503 Service Temporarily Unavailable"), - ngx_string("504 Gateway Time-out"), + ngx_string("503 Service Unavailable"), + ngx_string("504 Gateway Timeout"), ngx_string("505 HTTP Version Not Supported"), ngx_null_string, /* "506 Variant Also Negotiates" */ ngx_string("507 Insufficient Storage"), diff --git a/src/http/ngx_http_special_response.c b/src/http/ngx_http_special_response.c --- a/src/http/ngx_http_special_response.c +++ b/src/http/ngx_http_special_response.c @@ -107,9 +107,9 @@ static char ngx_http_error_400_page[] = static char ngx_http_error_401_page[] = "" CRLF -"401 Authorization Required" CRLF +"401 Unauthorized" CRLF "" CRLF -"

401 Authorization Required

" CRLF +"

401 Unauthorized

" CRLF ; @@ -139,9 +139,9 @@ static char ngx_http_error_404_page[] = static char ngx_http_error_405_page[] = "" CRLF -"405 Not Allowed" CRLF +"405 Method Not Allowed" CRLF "" CRLF -"

405 Not Allowed

" CRLF +"

405 Method Not Allowed

" CRLF ; @@ -155,9 +155,9 @@ static char ngx_http_error_406_page[] = static char ngx_http_error_408_page[] = "" CRLF -"408 Request Time-out" CRLF +"408 Request Timeout" CRLF "" CRLF -"

408 Request Time-out

" CRLF +"

408 Request Timeout

" CRLF ; @@ -203,9 +203,9 @@ static char ngx_http_error_413_page[] = static char ngx_http_error_414_page[] = "" CRLF -"414 Request-URI Too Large" CRLF +"414 URI Too Long" CRLF "" CRLF -"

414 Request-URI Too Large

" CRLF +"

414 URI Too Long

" CRLF ; @@ -219,9 +219,9 @@ static char ngx_http_error_415_page[] = static char ngx_http_error_416_page[] = "" CRLF -"416 Requested Range Not Satisfiable" CRLF +"416 Range Not Satisfiable" CRLF "" CRLF -"

416 Requested Range Not Satisfiable

" CRLF +"

416 Range Not Satisfiable

" CRLF ; @@ -307,17 +307,17 @@ static char ngx_http_error_502_page[] = static char ngx_http_error_503_page[] = "" CRLF -"503 Service Temporarily Unavailable" CRLF +"503 Service Unavailable" CRLF "" CRLF -"

503 Service Temporarily Unavailable

" CRLF +"

503 Service Unavailable

" CRLF ; static char ngx_http_error_504_page[] = "" CRLF -"504 Gateway Time-out" CRLF +"504 Gateway Timeout" CRLF "" CRLF -"

504 Gateway Time-out

" CRLF +"

504 Gateway Timeout

" CRLF ; From mdounin at mdounin.ru Sat Apr 20 18:22:25 2024 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sat, 20 Apr 2024 21:22:25 +0300 Subject: [PATCH] Use RFC9110 HTTP status code descriptions In-Reply-To: <3375171a24a16400b000.1713090383@foaf.home.x14.nl> References: <3375171a24a16400b000.1713090383@foaf.home.x14.nl> Message-ID: Hello! On Sun, Apr 14, 2024 at 12:26:23PM +0200, Michiel W. Beijen wrote: > # HG changeset patch > # User Michiel W. Beijen > # Date 1713090220 -7200 > # Sun Apr 14 12:23:40 2024 +0200 > # Branch stable-1.26 > # Node ID 3375171a24a16400b000805e45df1b57e4dd1775 > # Parent cecfdf9a8420fcdeb69d090cd7fd9169967e0007 > Use RFC9110 HTTP status code descriptions Nitpicking: Updated HTTP status code descriptions to match RFC 9110. > > This patch changes the status codes and status pages served by > freenginx to use the wording from RFC9110 ... RFC 9110. > > Exeption for HTTP status code 413 "Request Entity Too Large", Typo: "Exception...". > which is "Content Too Large" in the RFC. In that case we would > lose information about what actually is too large, the request > or the response. > > diff -r cecfdf9a8420 -r 3375171a24a1 src/http/ngx_http_header_filter_module.c > --- a/src/http/ngx_http_header_filter_module.c Sun Apr 14 12:03:29 2024 +0200 > +++ b/src/http/ngx_http_header_filter_module.c Sun Apr 14 12:23:40 2024 +0200 > @@ -69,7 +69,7 @@ > /* ngx_null_string, */ /* "300 Multiple Choices" */ > > ngx_string("301 Moved Permanently"), > - ngx_string("302 Moved Temporarily"), > + ngx_string("302 Found"), Looks good. > ngx_string("303 See Other"), > ngx_string("304 Not Modified"), > ngx_null_string, /* "305 Use Proxy" */ > @@ -85,18 +85,18 @@ > ngx_string("402 Payment Required"), > ngx_string("403 Forbidden"), > ngx_string("404 Not Found"), > - ngx_string("405 Not Allowed"), > + ngx_string("405 Method Not Allowed"), Looks good. Not sure where "Not Allowed" comes from, it seems to be written as "Method Not Allowed" since introduction in RFC 2068, as well as in Apache since at least 1.2.6. > ngx_string("406 Not Acceptable"), > ngx_null_string, /* "407 Proxy Authentication Required" */ > - ngx_string("408 Request Time-out"), > + ngx_string("408 Request Timeout"), Looks good. The "Time-out" spelling was used in some places in RFC 2068, where the code was introduced, and in RFC 2616 (notably, in section 6.1.1 Status Code and Reason Phrase, https://datatracker.ietf.org/doc/html/rfc2616#section-6.1.1), while "Timeout" in other places (e.g., section 10.4.9 408 Request Timeout, https://datatracker.ietf.org/doc/html/rfc2616#section-10.4.9). It was cleaned up in RFC 7231. This is also the spelling Apache used till 2.4.3. > ngx_string("409 Conflict"), > ngx_string("410 Gone"), > ngx_string("411 Length Required"), > ngx_string("412 Precondition Failed"), > ngx_string("413 Request Entity Too Large"), > - ngx_string("414 Request-URI Too Large"), > + ngx_string("414 URI Too Long"), Looks good, as previously discussed. > ngx_string("415 Unsupported Media Type"), > - ngx_string("416 Requested Range Not Satisfiable"), > + ngx_string("416 Range Not Satisfiable"), Looks good. The "416 Requested Range Not Satisfiable" error was introduced in RFC 2616, and was later updated to "416 Range Not Satisfiable" in RFC 7233. > ngx_null_string, /* "417 Expectation Failed" */ > ngx_null_string, /* "418 unused" */ > ngx_null_string, /* "419 unused" */ > @@ -117,8 +117,8 @@ > ngx_string("500 Internal Server Error"), > ngx_string("501 Not Implemented"), > ngx_string("502 Bad Gateway"), > - ngx_string("503 Service Temporarily Unavailable"), > - ngx_string("504 Gateway Time-out"), > + ngx_string("503 Service Unavailable"), Looks good. The "503 Service Unavailable" form seems to exists since introduction of the code in RFC 1945. The "503 Service Temporarily Unavailable" was used in Apache before 1.3.0 though. > + ngx_string("504 Gateway Timeout"), Looks good. The "504 Gateway Timeout" form is used since introduction of the code in RFC 1945, but similarly to the 408 error, "Time-out" spelling was used in Apache till 2.4.3. Similarly to the 408 error, "Time-out" spelling was used in Apache till 2.4.3. > ngx_string("505 HTTP Version Not Supported"), > ngx_null_string, /* "506 Variant Also Negotiates" */ > ngx_string("507 Insufficient Storage"), > diff -r cecfdf9a8420 -r 3375171a24a1 src/http/ngx_http_special_response.c > --- a/src/http/ngx_http_special_response.c Sun Apr 14 12:03:29 2024 +0200 > +++ b/src/http/ngx_http_special_response.c Sun Apr 14 12:23:40 2024 +0200 > @@ -107,9 +107,9 @@ > > static char ngx_http_error_401_page[] = > "" CRLF > -"401 Authorization Required" CRLF > +"401 Unauthorized" CRLF > "" CRLF > -"

401 Authorization Required

" CRLF > +"

401 Unauthorized

" CRLF > ; > > > @@ -139,9 +139,9 @@ > > static char ngx_http_error_405_page[] = > "" CRLF > -"405 Not Allowed" CRLF > +"405 Method Not Allowed" CRLF > "" CRLF > -"

405 Not Allowed

" CRLF > +"

405 Method Not Allowed

" CRLF > ; > > > @@ -155,9 +155,9 @@ > > static char ngx_http_error_408_page[] = > "" CRLF > -"408 Request Time-out" CRLF > +"408 Request Timeout" CRLF > "" CRLF > -"

408 Request Time-out

" CRLF > +"

408 Request Timeout

" CRLF > ; > > > @@ -203,9 +203,9 @@ > > static char ngx_http_error_414_page[] = > "" CRLF > -"414 Request-URI Too Large" CRLF > +"414 URI Too Long" CRLF > "" CRLF > -"

414 Request-URI Too Large

" CRLF > +"

414 URI Too Long

" CRLF > ; > > > @@ -219,9 +219,9 @@ > > static char ngx_http_error_416_page[] = > "" CRLF > -"416 Requested Range Not Satisfiable" CRLF > +"416 Range Not Satisfiable" CRLF > "" CRLF > -"

416 Requested Range Not Satisfiable

" CRLF > +"

416 Range Not Satisfiable

" CRLF > ; > > > @@ -307,17 +307,17 @@ > > static char ngx_http_error_503_page[] = > "" CRLF > -"503 Service Temporarily Unavailable" CRLF > +"503 Service Unavailable" CRLF > "" CRLF > -"

503 Service Temporarily Unavailable

" CRLF > +"

503 Service Unavailable

" CRLF > ; > > > static char ngx_http_error_504_page[] = > "" CRLF > -"504 Gateway Time-out" CRLF > +"504 Gateway Timeout" CRLF > "" CRLF > -"

504 Gateway Time-out

" CRLF > +"

504 Gateway Timeout

" CRLF > ; > > Pushed with the mentioned commit log fixes and a patch for tests to remove dependencies on the particular 405 error text, thanks. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Mon Apr 22 17:48:49 2024 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 22 Apr 2024 20:48:49 +0300 Subject: [PATCH 02 of 14] HTTP/3: fixed handling of request body larger than Content-Length In-Reply-To: <0946ec7c4a6a26fda6ed.1713574790@vm-bsd.mdounin.ru> References: <0946ec7c4a6a26fda6ed.1713574790@vm-bsd.mdounin.ru> Message-ID: Hello! On Sat, Apr 20, 2024 at 03:59:50AM +0300, Maxim Dounin wrote: > # HG changeset patch > # User Maxim Dounin > # Date 1713574628 -10800 > # Sat Apr 20 03:57:08 2024 +0300 > # Node ID 0946ec7c4a6a26fda6ed03cb1f1f877984ec5614 > # Parent 3c408152180f04a54c44d482cf1c9c52b63480d9 > HTTP/3: fixed handling of request body larger than Content-Length. > > Previously, 413 (Request entity too large) was returned, and incorrect > "client intended to send too large body" error message was logged. > Fix is to return 400 (Bad request) and log the "client intended to send > body data larger than declared" error message, similarly to what HTTP/2 > code does. > > Additionally, previously "client_max_body_size 0;" was incorrectly handled > by the HTTP/3 code, resulting in 413 instead of no limit. This is also > fixed by the correct checks added. > > diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c > --- a/src/http/v3/ngx_http_v3_request.c > +++ b/src/http/v3/ngx_http_v3_request.c > @@ -1482,7 +1482,6 @@ ngx_http_v3_do_read_client_request_body( > static ngx_int_t > ngx_http_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in) > { > - off_t max; > size_t size; > u_char *p; > ngx_int_t rc; > @@ -1510,14 +1509,6 @@ ngx_http_v3_request_body_filter(ngx_http > rb->rest = cscf->large_client_header_buffers.size; > } > > - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); > - > - max = r->headers_in.content_length_n; > - > - if (max == -1 && clcf->client_max_body_size) { > - max = clcf->client_max_body_size; > - } > - > out = NULL; > ll = &out; > last = 0; > @@ -1575,7 +1566,12 @@ ngx_http_v3_request_body_filter(ngx_http > > /* rc == NGX_OK */ > > - if (max != -1 && (uint64_t) (max - rb->received) < st->length) { > + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); > + > + if (clcf->client_max_body_size > + && (uint64_t) (clcf->client_max_body_size - rb->received) > + < st->length) > + { > ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, > "client intended to send too large " > "body: %O+%ui bytes", > @@ -1584,6 +1580,16 @@ ngx_http_v3_request_body_filter(ngx_http > return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; > } > > + if (r->headers_in.content_length_n != -1 > + && rb->received > r->headers_in.content_length_n) > + { Err, similarly to the above check, this should be: if (r->headers_in.content_length_n != -1 - && rb->received > r->headers_in.content_length_n) + && (uint64_t) (r->headers_in.content_length_n + - rb->received) + < st->length) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "client intended to send body data " Since, in contrast to the HTTP/2 code, this is checked before rb->received is updated. > + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, > + "client intended to send body data " > + "larger than declared"); > + > + return NGX_HTTP_BAD_REQUEST; > + } > + > continue; > } > -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Sat Apr 27 15:56:46 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sat, 27 Apr 2024 18:56:46 +0300 Subject: [nginx] Reordered checks for Content-Length and Transfer-Encoding. Message-ID: details: http://freenginx.org/hg/nginx/rev/55a5a40dccde branches: changeset: 9250:55a5a40dccde user: Maxim Dounin date: Sat Apr 27 18:16:27 2024 +0300 description: Reordered checks for Content-Length and Transfer-Encoding. This ensures that r->headers_in.content_length_n is not set when both Content-Length and Transfer-Encoding headers are present, making it slightly safer to use complex processing for 400 (Bad Request) errors. diffstat: src/http/ngx_http_request.c | 18 +++++++++--------- 1 files changed, 9 insertions(+), 9 deletions(-) diffs (35 lines): diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1968,6 +1968,15 @@ ngx_http_process_request_header(ngx_http } if (r->headers_in.content_length) { + if (r->headers_in.transfer_encoding) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent \"Content-Length\" and " + "\"Transfer-Encoding\" headers " + "at the same time"); + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; + } + r->headers_in.content_length_n = ngx_atoof(r->headers_in.content_length->value.data, r->headers_in.content_length->value.len); @@ -1993,15 +2002,6 @@ ngx_http_process_request_header(ngx_http && ngx_strncasecmp(r->headers_in.transfer_encoding->value.data, (u_char *) "chunked", 7) == 0) { - if (r->headers_in.content_length) { - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client sent \"Content-Length\" and " - "\"Transfer-Encoding\" headers " - "at the same time"); - ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); - return NGX_ERROR; - } - r->headers_in.chunked = 1; } else { From mdounin at mdounin.ru Sat Apr 27 15:56:46 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sat, 27 Apr 2024 18:56:46 +0300 Subject: [nginx] HTTP/3: fixed handling of request body larger than Conte... Message-ID: details: http://freenginx.org/hg/nginx/rev/3728a0ed243a branches: changeset: 9251:3728a0ed243a user: Maxim Dounin date: Sat Apr 27 18:17:03 2024 +0300 description: HTTP/3: fixed handling of request body larger than Content-Length. Previously, 413 (Request entity too large) was returned, and incorrect "client intended to send too large body" error message was logged. Fix is to return 400 (Bad request) and log the "client intended to send body data larger than declared" error message, similarly to what HTTP/2 code does. Additionally, previously "client_max_body_size 0;" was incorrectly handled by the HTTP/3 code, resulting in 413 instead of no limit. This is also fixed by the correct checks added. diffstat: src/http/v3/ngx_http_v3_request.c | 28 ++++++++++++++++++---------- 1 files changed, 18 insertions(+), 10 deletions(-) diffs (59 lines): diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -1482,7 +1482,6 @@ ngx_http_v3_do_read_client_request_body( static ngx_int_t ngx_http_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { - off_t max; size_t size; u_char *p; ngx_int_t rc; @@ -1510,14 +1509,6 @@ ngx_http_v3_request_body_filter(ngx_http rb->rest = cscf->large_client_header_buffers.size; } - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - - max = r->headers_in.content_length_n; - - if (max == -1 && clcf->client_max_body_size) { - max = clcf->client_max_body_size; - } - out = NULL; ll = &out; last = 0; @@ -1575,7 +1566,12 @@ ngx_http_v3_request_body_filter(ngx_http /* rc == NGX_OK */ - if (max != -1 && (uint64_t) (max - rb->received) < st->length) { + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->client_max_body_size + && (uint64_t) (clcf->client_max_body_size - rb->received) + < st->length) + { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "client intended to send too large " "body: %O+%ui bytes", @@ -1584,6 +1580,18 @@ ngx_http_v3_request_body_filter(ngx_http return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; } + if (r->headers_in.content_length_n != -1 + && (uint64_t) (r->headers_in.content_length_n + - rb->received) + < st->length) + { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client intended to send body data " + "larger than declared"); + + return NGX_HTTP_BAD_REQUEST; + } + continue; } From mdounin at mdounin.ru Sat Apr 27 15:56:46 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sat, 27 Apr 2024 18:56:46 +0300 Subject: [nginx] Proxy: use zero Content-Length while discarding request ... Message-ID: details: http://freenginx.org/hg/nginx/rev/51e0dc713784 branches: changeset: 9252:51e0dc713784 user: Maxim Dounin date: Sat Apr 27 18:18:13 2024 +0300 description: Proxy: use zero Content-Length while discarding request body. Previously, r->headers_in.content_length_n was used, which is not 0 till the request body is fully discarded, and the request might hang. diffstat: src/http/modules/ngx_http_proxy_module.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diffs (13 lines): diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -1341,6 +1341,9 @@ ngx_http_proxy_create_request(ngx_http_r ctx->internal_body_length = -1; ctx->internal_chunked = 1; + } else if (r->discard_body) { + ctx->internal_body_length = 0; + } else { ctx->internal_body_length = r->headers_in.content_length_n; } From mdounin at mdounin.ru Sat Apr 27 15:56:46 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sat, 27 Apr 2024 18:56:46 +0300 Subject: [nginx] Reset c->log->action on redirect to a named location. Message-ID: details: http://freenginx.org/hg/nginx/rev/a6649497a762 branches: changeset: 9253:a6649497a762 user: Maxim Dounin date: Sat Apr 27 18:18:36 2024 +0300 description: Reset c->log->action on redirect to a named location. This matches what happens on internal redirects in ngx_http_handler(), and ensures that stale log actions, such as "sending to client" from the upstream module, are not used after an error_page redirection to a named location. diffstat: src/http/ngx_http_core_module.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diffs (12 lines): diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -2596,6 +2596,8 @@ ngx_http_named_location(ngx_http_request "using location: %V \"%V?%V\"", name, &r->uri, &r->args); + r->connection->log->action = NULL; + r->internal = 1; r->content_handler = NULL; r->uri_changed = 0; From mdounin at mdounin.ru Sat Apr 27 15:56:46 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sat, 27 Apr 2024 18:56:46 +0300 Subject: [nginx] Request body: provided log action for reading request body. Message-ID: details: http://freenginx.org/hg/nginx/rev/cb1e214efe41 branches: changeset: 9254:cb1e214efe41 user: Maxim Dounin date: Sat Apr 27 18:19:07 2024 +0300 description: Request body: provided log action for reading request body. diffstat: src/http/ngx_http_request_body.c | 5 +++++ src/http/v2/ngx_http_v2.c | 2 ++ src/http/v3/ngx_http_v3_request.c | 2 ++ 3 files changed, 9 insertions(+), 0 deletions(-) diffs (74 lines): diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -85,6 +85,8 @@ ngx_http_read_client_request_body(ngx_ht return NGX_OK; } + r->connection->log->action = "reading request body"; + #if (NGX_HTTP_V2) if (r->stream) { rc = ngx_http_v2_read_request_body(r); @@ -159,6 +161,7 @@ ngx_http_read_client_request_body(ngx_ht if (rb->rest == 0 && rb->last_saved) { /* the whole request body was pre-read */ r->request_body_no_buffering = 0; + r->connection->log->action = NULL; post_handler(r); return NGX_OK; } @@ -217,6 +220,7 @@ done: } r->read_event_handler = ngx_http_block_reading; + r->connection->log->action = NULL; post_handler(r); } @@ -456,6 +460,7 @@ ngx_http_do_read_client_request_body(ngx if (!r->request_body_no_buffering) { r->read_event_handler = ngx_http_block_reading; + r->connection->log->action = NULL; rb->post_handler(r); } diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -3807,6 +3807,7 @@ ngx_http_v2_read_request_body(ngx_http_r if (stream->skip_data) { r->request_body_no_buffering = 0; + r->connection->log->action = NULL; rb->post_handler(r); return NGX_OK; } @@ -4061,6 +4062,7 @@ ngx_http_v2_process_request_body(ngx_htt } r->read_event_handler = ngx_http_block_reading; + r->connection->log->action = NULL; rb->post_handler(r); return NGX_OK; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -1250,6 +1250,7 @@ ngx_http_v3_read_request_body(ngx_http_r if (rb->rest == 0 && rb->last_saved) { /* the whole request body was pre-read */ r->request_body_no_buffering = 0; + r->connection->log->action = NULL; rb->post_handler(r); return NGX_OK; } @@ -1472,6 +1473,7 @@ ngx_http_v3_do_read_client_request_body( if (!r->request_body_no_buffering) { r->read_event_handler = ngx_http_block_reading; + r->connection->log->action = NULL; rb->post_handler(r); } From mdounin at mdounin.ru Sat Apr 27 15:56:46 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sat, 27 Apr 2024 18:56:46 +0300 Subject: [nginx] Request body: logging of timeouts. Message-ID: details: http://freenginx.org/hg/nginx/rev/208a4adb82ef branches: changeset: 9255:208a4adb82ef user: Maxim Dounin date: Sat Apr 27 18:19:27 2024 +0300 description: Request body: logging of timeouts. diffstat: src/http/ngx_http_request_body.c | 4 ++++ src/http/ngx_http_upstream.c | 1 + src/http/v2/ngx_http_v2.c | 3 +++ src/http/v3/ngx_http_v3_request.c | 4 ++++ 4 files changed, 12 insertions(+), 0 deletions(-) diffs (66 lines): diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -262,6 +262,8 @@ ngx_http_read_unbuffered_request_body(ng #endif if (r->connection->read->timedout) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, NGX_ETIMEDOUT, + "client timed out"); r->connection->timedout = 1; return NGX_HTTP_REQUEST_TIME_OUT; } @@ -282,6 +284,8 @@ ngx_http_read_client_request_body_handle ngx_int_t rc; if (r->connection->read->timedout) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, NGX_ETIMEDOUT, + "client timed out"); r->connection->timedout = 1; ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT); return; diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -2361,6 +2361,7 @@ ngx_http_upstream_read_request_handler(n if (c->read->timedout) { c->timedout = 1; + ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT); return; } diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -4285,6 +4285,9 @@ ngx_http_v2_read_unbuffered_request_body if (fc->read->timedout) { if (stream->recv_window) { + ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, + "client timed out"); + stream->skip_data = 1; fc->timedout = 1; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -1281,6 +1281,8 @@ ngx_http_v3_read_client_request_body_han ngx_int_t rc; if (r->connection->read->timedout) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, NGX_ETIMEDOUT, + "client timed out"); r->connection->timedout = 1; ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT); return; @@ -1300,6 +1302,8 @@ ngx_http_v3_read_unbuffered_request_body ngx_int_t rc; if (r->connection->read->timedout) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, NGX_ETIMEDOUT, + "client timed out"); r->connection->timedout = 1; return NGX_HTTP_REQUEST_TIME_OUT; } From mdounin at mdounin.ru Sat Apr 27 15:56:47 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sat, 27 Apr 2024 18:56:47 +0300 Subject: [nginx] Request body: block reading when returning errors. Message-ID: details: http://freenginx.org/hg/nginx/rev/43fe0edddba1 branches: changeset: 9256:43fe0edddba1 user: Maxim Dounin date: Sat Apr 27 18:20:17 2024 +0300 description: Request body: block reading when returning errors. If reading is not blocked, additional client activity can result in undefined behaviour, including segfaults, as seen with proxying with proxy_ignore_client_abort before 4072:cf334deeea66. While unlikely with low-level errors being returned when reading the request body, it might be the case, for example, when chunked transfer encoding is used, and error_page 400 (or 413) is proxied with proxy_ignore_client_abort. diffstat: src/http/ngx_http_request_body.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diffs (19 lines): diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -226,6 +226,7 @@ done: if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { r->main->count--; + r->read_event_handler = ngx_http_block_reading; } return rc; @@ -294,6 +295,7 @@ ngx_http_read_client_request_body_handle rc = ngx_http_do_read_client_request_body(r); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + r->read_event_handler = ngx_http_block_reading; ngx_http_finalize_request(r, rc); } } From mdounin at mdounin.ru Sat Apr 27 15:56:47 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sat, 27 Apr 2024 18:56:47 +0300 Subject: [nginx] Request body: improved debug logging. Message-ID: details: http://freenginx.org/hg/nginx/rev/0748264a1278 branches: changeset: 9257:0748264a1278 user: Maxim Dounin date: Sat Apr 27 18:21:05 2024 +0300 description: Request body: improved debug logging. diffstat: src/http/ngx_http_request_body.c | 8 ++++++-- 1 files changed, 6 insertions(+), 2 deletions(-) diffs (32 lines): diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -48,6 +48,9 @@ ngx_http_read_client_request_body(ngx_ht return NGX_OK; } + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http read request body"); + if (ngx_http_test_expect(r) != NGX_OK) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; @@ -649,6 +652,9 @@ ngx_http_discard_request_body(ngx_http_r return NGX_OK; } + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http set discard body"); + #if (NGX_HTTP_V2) if (r->stream) { r->stream->skip_data = 1; @@ -668,8 +674,6 @@ ngx_http_discard_request_body(ngx_http_r rev = r->connection->read; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body"); - if (rev->timer_set) { ngx_del_timer(rev); } From mdounin at mdounin.ru Sat Apr 27 15:56:47 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sat, 27 Apr 2024 18:56:47 +0300 Subject: [nginx] Request body: simplified error handling in HTTP/2. Message-ID: details: http://freenginx.org/hg/nginx/rev/c9550e77186c branches: changeset: 9258:c9550e77186c user: Maxim Dounin date: Sat Apr 27 18:21:25 2024 +0300 description: Request body: simplified error handling in HTTP/2. No functional changes. diffstat: src/http/v2/ngx_http_v2.c | 26 ++++++++++++++------------ 1 files changed, 14 insertions(+), 12 deletions(-) diffs (48 lines): diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -4238,11 +4238,7 @@ ngx_http_v2_read_client_request_body_han if (window < stream->recv_window) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "http2 negative window update"); - - stream->skip_data = 1; - - ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return; + goto error; } return; @@ -4252,18 +4248,24 @@ ngx_http_v2_read_client_request_body_han window - stream->recv_window) == NGX_ERROR) { - stream->skip_data = 1; - ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return; + goto error; } stream->recv_window = window; if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) { - stream->skip_data = 1; - ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return; - } + goto error; + } + + return; + +error: + + stream->skip_data = 1; + + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } From mdounin at mdounin.ru Sat Apr 27 15:56:47 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sat, 27 Apr 2024 18:56:47 +0300 Subject: [nginx] Request body: body is now cleared on errors. Message-ID: details: http://freenginx.org/hg/nginx/rev/81082b5521dd branches: changeset: 9259:81082b5521dd user: Maxim Dounin date: Sat Apr 27 18:21:38 2024 +0300 description: Request body: body is now cleared on errors. Previously, after errors the request body was left in a potentially inconsistent state, with r->headers_in.content_length_n which might be larger than buffers actually stored in r->request_body->bufs (or not set at all, in case of HTTP/2 and HTTP/3). This can cause issues if the request body is subsequently used during error_page handling, such as when proxying. Fix is to clear r->request_body->bufs if this happens, and set r->headers_in.content_length_n to 0, much like it happens when ngx_http_discard_request_body() is called when returning 413 from ngx_http_core_find_config_phase() for requests with Content-Length. diffstat: src/http/ngx_http_request_body.c | 16 ++++++++++------ src/http/v2/ngx_http_v2.c | 11 +++++++++++ src/http/v3/ngx_http_v3_request.c | 4 ++++ 3 files changed, 25 insertions(+), 6 deletions(-) diffs (112 lines): diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -228,6 +228,11 @@ done: } if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + + r->lingering_close = 1; + r->headers_in.content_length_n = 0; + r->request_body->bufs = NULL; + r->main->count--; r->read_event_handler = ngx_http_block_reading; } @@ -298,6 +303,11 @@ ngx_http_read_client_request_body_handle rc = ngx_http_do_read_client_request_body(r); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + + r->lingering_close = 1; + r->headers_in.content_length_n = 0; + r->request_body->bufs = NULL; + r->read_event_handler = ngx_http_block_reading; ngx_http_finalize_request(r, rc); } @@ -1161,8 +1171,6 @@ ngx_http_request_body_chunked_filter(ngx ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "client sent too many chunk extensions"); - r->lingering_close = 1; - return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; } @@ -1176,8 +1184,6 @@ ngx_http_request_body_chunked_filter(ngx r->headers_in.content_length_n, rb->chunked->size); - r->lingering_close = 1; - return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; } @@ -1276,8 +1282,6 @@ ngx_http_request_body_chunked_filter(ngx "client sent too many chunk extensions " "or trailer headers"); - r->lingering_close = 1; - return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; } diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -1106,7 +1106,11 @@ ngx_http_v2_state_read_data(ngx_http_v2_ stream->in_closed, 0); if (rc != NGX_OK && rc != NGX_AGAIN) { + stream->skip_data = 1; + r->headers_in.content_length_n = 0; + r->request_body->bufs = NULL; + ngx_http_finalize_request(r, rc); } @@ -3768,6 +3772,7 @@ ngx_http_v2_run_request(ngx_http_request "client prematurely closed stream"); r->stream->skip_data = 1; + r->headers_in.content_length_n = 0; ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); goto failed; @@ -4199,7 +4204,11 @@ ngx_http_v2_read_client_request_body_han rc = ngx_http_v2_process_request_body(r, NULL, 0, r->stream->in_closed, 1); if (rc != NGX_OK && rc != NGX_AGAIN) { + r->stream->skip_data = 1; + r->headers_in.content_length_n = 0; + r->request_body->bufs = NULL; + ngx_http_finalize_request(r, rc); return; } @@ -4262,6 +4271,8 @@ ngx_http_v2_read_client_request_body_han error: stream->skip_data = 1; + r->headers_in.content_length_n = 0; + r->request_body->bufs = NULL; ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -1291,6 +1291,10 @@ ngx_http_v3_read_client_request_body_han rc = ngx_http_v3_do_read_client_request_body(r); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + + r->headers_in.content_length_n = 0; + r->request_body->bufs = NULL; + ngx_http_finalize_request(r, rc); } } From mdounin at mdounin.ru Sat Apr 27 15:56:47 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sat, 27 Apr 2024 18:56:47 +0300 Subject: [nginx] Request body: handling of body after unbuffered reading. Message-ID: details: http://freenginx.org/hg/nginx/rev/ac5635650bc6 branches: changeset: 9260:ac5635650bc6 user: Maxim Dounin date: Sat Apr 27 18:22:07 2024 +0300 description: Request body: handling of body after unbuffered reading. As long as unbuffered reading of the request body was used, and an attempt to read the request body is made again, such as when redirecting the request to an error page, the request body is now cleared to make sure it can be used safely. Further, the r->reading_body flag, if it is still set, is cleared (along with disabling keepalive and enabling lingering close), so the code which uses the request body, such as when proxying, is not confused and doesn't try to use "Transfer-Encoding: chunked". Note that this change makes the workaround for HTTP/2 issues with unbuffered proxying and error pages, as introduced in 7561:9f1f9d6e056a, ineffective (since r->reading_body now cleared along with r->reading_body_no_buffering). Though the workaround is anyway not needed after 7924:d9e009b39596, hence it is removed. This makes it safer to use complex processing of error pages with unbuffered proxying. diffstat: src/http/ngx_http_request.h | 1 + src/http/ngx_http_request_body.c | 14 ++++++++++++++ src/http/v2/ngx_http_v2.c | 7 ------- 3 files changed, 15 insertions(+), 7 deletions(-) diffs (66 lines): diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -305,6 +305,7 @@ typedef struct { ngx_chain_t *busy; ngx_http_chunked_t *chunked; ngx_http_client_body_handler_pt post_handler; + unsigned no_buffering:1; unsigned filter_need_buffering:1; unsigned last_sent:1; unsigned last_saved:1; diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -44,6 +44,18 @@ ngx_http_read_client_request_body(ngx_ht if (r != r->main || r->request_body || r->discard_body) { r->request_body_no_buffering = 0; + + if (r->request_body && r->request_body->no_buffering) { + r->headers_in.content_length_n = 0; + r->request_body->bufs = NULL; + + if (r->reading_body) { + r->reading_body = 0; + r->keepalive = 0; + r->lingering_close = 1; + } + } + post_handler(r); return NGX_OK; } @@ -72,6 +84,7 @@ ngx_http_read_client_request_body(ngx_ht * rb->busy = NULL; * rb->chunked = NULL; * rb->received = 0; + * rb->no_buffering = 0; * rb->filter_need_buffering = 0; * rb->last_sent = 0; * rb->last_saved = 0; @@ -220,6 +233,7 @@ done: } else { /* rc == NGX_AGAIN */ r->reading_body = 1; + r->request_body->no_buffering = 1; } r->read_event_handler = ngx_http_block_reading; diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -1078,13 +1078,6 @@ ngx_http_v2_state_read_data(ngx_http_v2_ r = stream->request; fc = r->connection; - if (r->reading_body && !r->request_body_no_buffering) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "skipping http2 DATA frame"); - - return ngx_http_v2_state_skip_padded(h2c, pos, end); - } - if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, "skipping http2 DATA frame"); From mdounin at mdounin.ru Sat Apr 27 15:56:47 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sat, 27 Apr 2024 18:56:47 +0300 Subject: [nginx] Request body: error_page 413 handling with HTTP/2 and HT... Message-ID: details: http://freenginx.org/hg/nginx/rev/f798ecafec05 branches: changeset: 9261:f798ecafec05 user: Maxim Dounin date: Sat Apr 27 18:22:38 2024 +0300 description: Request body: error_page 413 handling with HTTP/2 and HTTP/3. When the client_max_body_size limit in ngx_http_core_find_config_phase() is hit, nginx calls the ngx_http_discard_request_body() function, which normally sets the r->discard_body flag while discarding the body, and then reduces the r->headers_in.content_length_n field to 0 when the body is completely discarded. As such, the client_max_body_size check is skipped if the request is redirected to an error page, and this makes it possible to use "error_page 413" without additional settings. This only works with HTTP/1.x though. The HTTP/2 and HTTP/3 request body discarding code paths failed to set r->discard_body or reset r->headers_in.content_length_n, so configuring "error_page 413" did notwork without additionally clearing the client_max_body_size limit in the location with error page. Fix is to set r->headers_in.content_length_n to 0 in the HTTP/2 and HTTP/3 request body discarding code paths (if there is a body). This is essentially what happens with HTTP/1.x when the body is completely discarded, and makes it possible to use "error_page 413" with HTTP/2 and HTTP/3 without additional settings. Additionally, r->discard_body flag is also set. For HTTP/2, it is not needed, but serves as an optimization. For HTTP/3, it ensures that the request body cannot be read after it was discarded, thus bypassing the client_max_body_size limit. Further, the r->discard_body flag is now always set after the request body is discarded (and not cleared once it is fully discarded). While the body is being discarded, the new r->discarding_body flag is now used. This slightly optimizes existing code paths in ngx_http_read_client_request_body() and ngx_http_discard_request_body(), and also makes it easier to only set ngx_http_discarded_request_body_handler() for HTTP/1.x. diffstat: src/http/ngx_http_core_module.c | 1 + src/http/ngx_http_request.c | 4 ++-- src/http/ngx_http_request.h | 1 + src/http/ngx_http_request_body.c | 20 +++++++++++++++++--- 4 files changed, 21 insertions(+), 5 deletions(-) diffs (107 lines): diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -2456,6 +2456,7 @@ ngx_http_subrequest(ngx_http_request_t * sr->internal = 1; sr->discard_body = r->discard_body; + sr->discarding_body = r->discarding_body; sr->expect_tested = 1; sr->main_filter_need_in_memory = r->main_filter_need_in_memory; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -2762,7 +2762,7 @@ ngx_http_finalize_connection(ngx_http_re if (r->main->count != 1) { - if (r->discard_body) { + if (r->discarding_body) { r->read_event_handler = ngx_http_discarded_request_body_handler; ngx_add_timer(r->connection->read, clcf->lingering_timeout); @@ -2820,7 +2820,7 @@ ngx_http_set_write_handler(ngx_http_requ r->http_state = NGX_HTTP_WRITING_REQUEST_STATE; - r->read_event_handler = r->discard_body ? + r->read_event_handler = r->discarding_body ? ngx_http_discarded_request_body_handler: ngx_http_test_reading; r->write_event_handler = ngx_http_writer; diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -538,6 +538,7 @@ struct ngx_http_request_s { unsigned keepalive:1; unsigned lingering_close:1; unsigned discard_body:1; + unsigned discarding_body:1; unsigned reading_body:1; unsigned internal:1; unsigned error_page:1; diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -682,12 +682,24 @@ ngx_http_discard_request_body(ngx_http_r #if (NGX_HTTP_V2) if (r->stream) { r->stream->skip_data = 1; + + if (r->headers_in.content_length_n > 0 || r->headers_in.chunked) { + r->headers_in.content_length_n = 0; + r->discard_body = 1; + } + return NGX_OK; } #endif #if (NGX_HTTP_V3) if (r->http_version == NGX_HTTP_VERSION_30) { + + if (r->headers_in.content_length_n > 0 || r->headers_in.chunked) { + r->headers_in.content_length_n = 0; + r->discard_body = 1; + } + return NGX_OK; } #endif @@ -706,6 +718,8 @@ ngx_http_discard_request_body(ngx_http_r return NGX_OK; } + r->discard_body = 1; + size = r->header_in->last - r->header_in->pos; if (size || r->headers_in.chunked) { @@ -740,7 +754,7 @@ ngx_http_discard_request_body(ngx_http_r } r->count++; - r->discard_body = 1; + r->discarding_body = 1; return NGX_OK; } @@ -769,7 +783,7 @@ ngx_http_discarded_request_body_handler( timer = (ngx_msec_t) r->lingering_time - (ngx_msec_t) ngx_time(); if ((ngx_msec_int_t) timer <= 0) { - r->discard_body = 0; + r->discarding_body = 0; r->lingering_close = 0; ngx_http_finalize_request(r, NGX_ERROR); return; @@ -782,7 +796,7 @@ ngx_http_discarded_request_body_handler( rc = ngx_http_read_discarded_request_body(r); if (rc == NGX_OK) { - r->discard_body = 0; + r->discarding_body = 0; r->lingering_close = 0; r->lingering_time = 0; ngx_http_finalize_request(r, NGX_DONE); From mdounin at mdounin.ru Sat Apr 27 15:56:48 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sat, 27 Apr 2024 18:56:48 +0300 Subject: [nginx] Modified $content_length to match available request body... Message-ID: details: http://freenginx.org/hg/nginx/rev/106b3832e7ef branches: changeset: 9262:106b3832e7ef user: Maxim Dounin date: Sat Apr 27 18:23:22 2024 +0300 description: Modified $content_length to match available request body length. As long as the request body was discarded or there was an error during reading, it now follows r->headers_in.content_length_n and reflects the fact that no request body is available, similarly to how Content-Length as used by the proxy module does. This makes complex processing of various error pages safer, notably when using fastcgi_pass, uwsgi_pass, or grpc_pass, where the $content_length variable is used to set length. diffstat: src/http/ngx_http_variables.c | 13 ++++++++++--- 1 files changed, 10 insertions(+), 3 deletions(-) diffs (39 lines): diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -1184,17 +1184,24 @@ ngx_http_variable_content_length(ngx_htt { u_char *p; - if (r->headers_in.content_length) { + if (r->reading_body && r->headers_in.content_length) { v->len = r->headers_in.content_length->value.len; v->data = r->headers_in.content_length->value.data; v->valid = 1; - v->no_cacheable = 0; + v->no_cacheable = 1; v->not_found = 0; } else if (r->reading_body) { v->not_found = 1; v->no_cacheable = 1; + } else if (r->discard_body) { + v->len = 1; + v->data = (u_char *) "0"; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + } else if (r->headers_in.content_length_n >= 0) { p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN); if (p == NULL) { @@ -1204,7 +1211,7 @@ ngx_http_variable_content_length(ngx_htt v->len = ngx_sprintf(p, "%O", r->headers_in.content_length_n) - p; v->data = p; v->valid = 1; - v->no_cacheable = 0; + v->no_cacheable = 1; v->not_found = 0; } else if (r->headers_in.chunked) { From mdounin at mdounin.ru Sat Apr 27 15:56:48 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sat, 27 Apr 2024 18:56:48 +0300 Subject: [nginx] Request body: discarded body now treated as no body. Message-ID: details: http://freenginx.org/hg/nginx/rev/388a801e9bb9 branches: changeset: 9263:388a801e9bb9 user: Maxim Dounin date: Sat Apr 27 18:23:52 2024 +0300 description: Request body: discarded body now treated as no body. Notably, proxying of such requests now uses no Content-Length instead of "Content-Length: 0", and the $content_length variable is empty (instead of "0"). This might be beneficial from correctness point of view, since requests with discarded body, such as during processing of error pages, do not pretend there is a zero-length body, but instead do not contain body at all. For example, this might be important for PUT requests, where a zero-length body could be incorrectly interpreted as a real request body. This also slightly simplifies the code. diffstat: src/http/modules/ngx_http_proxy_module.c | 2 +- src/http/ngx_http_request_body.c | 22 +++++----------------- src/http/ngx_http_variables.c | 6 +----- src/http/v2/ngx_http_v2.c | 8 ++++---- src/http/v3/ngx_http_v3_request.c | 2 +- 5 files changed, 12 insertions(+), 28 deletions(-) diffs (148 lines): diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -1342,7 +1342,7 @@ ngx_http_proxy_create_request(ngx_http_r ctx->internal_chunked = 1; } else if (r->discard_body) { - ctx->internal_body_length = 0; + ctx->internal_body_length = -1; } else { ctx->internal_body_length = r->headers_in.content_length_n; diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -46,7 +46,7 @@ ngx_http_read_client_request_body(ngx_ht r->request_body_no_buffering = 0; if (r->request_body && r->request_body->no_buffering) { - r->headers_in.content_length_n = 0; + r->discard_body = 1; r->request_body->bufs = NULL; if (r->reading_body) { @@ -244,7 +244,7 @@ done: if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { r->lingering_close = 1; - r->headers_in.content_length_n = 0; + r->discard_body = 1; r->request_body->bufs = NULL; r->main->count--; @@ -319,7 +319,7 @@ ngx_http_read_client_request_body_handle if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { r->lingering_close = 1; - r->headers_in.content_length_n = 0; + r->discard_body = 1; r->request_body->bufs = NULL; r->read_event_handler = ngx_http_block_reading; @@ -679,27 +679,17 @@ ngx_http_discard_request_body(ngx_http_r ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http set discard body"); + r->discard_body = 1; + #if (NGX_HTTP_V2) if (r->stream) { r->stream->skip_data = 1; - - if (r->headers_in.content_length_n > 0 || r->headers_in.chunked) { - r->headers_in.content_length_n = 0; - r->discard_body = 1; - } - return NGX_OK; } #endif #if (NGX_HTTP_V3) if (r->http_version == NGX_HTTP_VERSION_30) { - - if (r->headers_in.content_length_n > 0 || r->headers_in.chunked) { - r->headers_in.content_length_n = 0; - r->discard_body = 1; - } - return NGX_OK; } #endif @@ -718,8 +708,6 @@ ngx_http_discard_request_body(ngx_http_r return NGX_OK; } - r->discard_body = 1; - size = r->header_in->last - r->header_in->pos; if (size || r->headers_in.chunked) { diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -1196,11 +1196,7 @@ ngx_http_variable_content_length(ngx_htt v->no_cacheable = 1; } else if (r->discard_body) { - v->len = 1; - v->data = (u_char *) "0"; - v->valid = 1; - v->no_cacheable = 0; - v->not_found = 0; + v->not_found = 1; } else if (r->headers_in.content_length_n >= 0) { p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN); diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -1101,7 +1101,7 @@ ngx_http_v2_state_read_data(ngx_http_v2_ if (rc != NGX_OK && rc != NGX_AGAIN) { stream->skip_data = 1; - r->headers_in.content_length_n = 0; + r->discard_body = 1; r->request_body->bufs = NULL; ngx_http_finalize_request(r, rc); @@ -3765,7 +3765,7 @@ ngx_http_v2_run_request(ngx_http_request "client prematurely closed stream"); r->stream->skip_data = 1; - r->headers_in.content_length_n = 0; + r->discard_body = 1; ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); goto failed; @@ -4199,7 +4199,7 @@ ngx_http_v2_read_client_request_body_han if (rc != NGX_OK && rc != NGX_AGAIN) { r->stream->skip_data = 1; - r->headers_in.content_length_n = 0; + r->discard_body = 1; r->request_body->bufs = NULL; ngx_http_finalize_request(r, rc); @@ -4264,7 +4264,7 @@ ngx_http_v2_read_client_request_body_han error: stream->skip_data = 1; - r->headers_in.content_length_n = 0; + r->discard_body = 1; r->request_body->bufs = NULL; ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -1292,7 +1292,7 @@ ngx_http_v3_read_client_request_body_han if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { - r->headers_in.content_length_n = 0; + r->discard_body = 1; r->request_body->bufs = NULL; ngx_http_finalize_request(r, rc); From mdounin at mdounin.ru Sat Apr 27 15:57:04 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sat, 27 Apr 2024 18:57:04 +0300 Subject: [nginx-tests] Tests: fixed HTTP/3 stream offset adjustment when ... Message-ID: details: http://freenginx.org/hg/nginx-tests/rev/70302d2090ad branches: changeset: 1958:70302d2090ad user: Maxim Dounin date: Sat Apr 27 18:55:08 2024 +0300 description: Tests: fixed HTTP/3 stream offset adjustment when sending body. Previously, size of the body without the DATA frame header was used, resulting in incorrect offset being used in further QUIC frames, making it impossible to send the request body in multiple DATA frames. diffstat: lib/Test/Nginx/HTTP3.pm | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff --git a/lib/Test/Nginx/HTTP3.pm b/lib/Test/Nginx/HTTP3.pm --- a/lib/Test/Nginx/HTTP3.pm +++ b/lib/Test/Nginx/HTTP3.pm @@ -470,7 +470,7 @@ sub h3_body { my $buf = pack_body($self, $body) if defined $body; my $offset = $self->{streams}{$sid}{sent}; - $self->{streams}{$sid}{sent} += length($body); + $self->{streams}{$sid}{sent} += length($buf); $self->raw_write($self->build_stream($buf, start => $extra->{body_more}, sid => $sid, offset => $offset)); } From mdounin at mdounin.ru Sat Apr 27 15:57:04 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sat, 27 Apr 2024 18:57:04 +0300 Subject: [nginx-tests] Tests: fixed HTTP/3 build_int() for large integers. Message-ID: details: http://freenginx.org/hg/nginx-tests/rev/9877dce42a59 branches: changeset: 1959:9877dce42a59 user: Maxim Dounin date: Sat Apr 27 18:55:14 2024 +0300 description: Tests: fixed HTTP/3 build_int() for large integers. diffstat: lib/Test/Nginx/HTTP3.pm | 16 ++++++++-------- 1 files changed, 8 insertions(+), 8 deletions(-) diffs (26 lines): diff --git a/lib/Test/Nginx/HTTP3.pm b/lib/Test/Nginx/HTTP3.pm --- a/lib/Test/Nginx/HTTP3.pm +++ b/lib/Test/Nginx/HTTP3.pm @@ -2087,14 +2087,14 @@ sub build_int { } else { pack("C*", - build_int_set->($value, 7, 3), - build_int_set->($value, 6, 0), - build_int_set->($value, 5, 0), - build_int_set->($value, 4, 0), - build_int_set->($value, 3, 0), - build_int_set->($value, 2, 0), - build_int_set->($value, 1, 0), - build_int_set->($value, 0, 0), + $build_int_set->($value, 7, 3), + $build_int_set->($value, 6, 0), + $build_int_set->($value, 5, 0), + $build_int_set->($value, 4, 0), + $build_int_set->($value, 3, 0), + $build_int_set->($value, 2, 0), + $build_int_set->($value, 1, 0), + $build_int_set->($value, 0, 0), ); } } From mdounin at mdounin.ru Sat Apr 27 15:57:04 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sat, 27 Apr 2024 18:57:04 +0300 Subject: [nginx-tests] Tests: adjusted http_headers_multi.t for $content_... Message-ID: details: http://freenginx.org/hg/nginx-tests/rev/e44ee916b959 branches: changeset: 1960:e44ee916b959 user: Maxim Dounin date: Sat Apr 27 18:55:21 2024 +0300 description: Tests: adjusted http_headers_multi.t for $content_length changes. The $content_length variable is going to be not available after discarding the request body. As such, the relevant location is now proxied, so the request body is not discarded. diffstat: http_headers_multi.t | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff --git a/http_headers_multi.t b/http_headers_multi.t --- a/http_headers_multi.t +++ b/http_headers_multi.t @@ -96,7 +96,7 @@ http { add_header X-Host $host; add_header X-Remote-User $remote_user; - return 204; + proxy_pass http://127.0.0.1:8080/backend; } location /d { From mdounin at mdounin.ru Sat Apr 27 15:57:04 2024 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sat, 27 Apr 2024 18:57:04 +0300 Subject: [nginx-tests] Tests: tests for usage of discarded body. Message-ID: details: http://freenginx.org/hg/nginx-tests/rev/fe6f22da53ec branches: changeset: 1961:fe6f22da53ec user: Maxim Dounin date: Sat Apr 27 18:55:50 2024 +0300 description: Tests: tests for usage of discarded body. The client_max_body_size limit should be ignored when the request body is already discarded. In HTTP/1.x, this is done by checking the r->discard_body flag when the body is being discarded, and because r->headers_in.content_length_n is 0 when it's already discarded. This, however, does not happen with HTTP/2 and HTTP/3, and therefore "error_page 413" does not work without relaxing the limit. Further, with proxy_pass, r->headers_in.content_length_n is used to determine length of the request body, and therefore is not correct if discarding of the request body isn't yet complete. While discarding the request body, r->headers_in.content_length_n contains the rest of the body to discard (or, in case of chunked request body, the rest of the current chunk to discard). Similarly, the $content_length variable uses r->headers_in.content_length if available, and also incorrect. The $content_length variable is used when proxying with fastcgi_pass, grpc_pass, and uwsgi_pass (scgi_pass uses the value calculated based on the actual request body buffers, and therefore works correctly). diffstat: body_discard.t | 449 ++++++++++++++++++++++++++++++++++++++++++++++ h2_request_body_discard.t | 407 +++++++++++++++++++++++++++++++++++++++++ h3_request_body_discard.t | 426 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1282 insertions(+), 0 deletions(-) diffs (1297 lines): diff --git a/body_discard.t b/body_discard.t new file mode 100644 --- /dev/null +++ b/body_discard.t @@ -0,0 +1,449 @@ +#!/usr/bin/perl + +# (C) Maxim Dounin + +# Tests for discarding request body. + +############################################################################### + +use warnings; +use strict; + +use Test::More; +use Socket qw/ CRLF /; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +my $t = Test::Nginx->new() + ->has(qw/http proxy rewrite addition memcached/); + +plan(skip_all => 'not yet') unless $t->has_version('1.27.0'); + +$t->plan(33)->write_file_expand('nginx.conf', <<'EOF'); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +http { + %%TEST_GLOBALS_HTTP%% + + server { + listen 127.0.0.1:8080; + server_name localhost; + + lingering_timeout 1s; + add_header X-Body body:$content_length:$request_body:; + + client_max_body_size 1k; + + error_page 400 /proxy/error400; + + location / { + error_page 413 /error413; + proxy_pass http://127.0.0.1:8082; + } + + location /error413 { + return 200 "custom error 413"; + } + + location /add { + return 200 "main response"; + add_before_body /add/before; + addition_types *; + client_max_body_size 1m; + } + + location /add/before { + proxy_pass http://127.0.0.1:8081; + } + + location /memcached { + client_max_body_size 1m; + error_page 502 /memcached/error502; + memcached_pass 127.0.0.1:8083; + set $memcached_key $request_uri; + } + + location /memcached/error502 { + proxy_pass http://127.0.0.1:8081; + } + + location /proxy { + client_max_body_size 1; + error_page 413 /proxy/error413; + error_page 400 /proxy/error400; + error_page 502 /proxy/error502; + proxy_pass http://127.0.0.1:8083; + } + + location /proxy/error413 { + proxy_pass http://127.0.0.1:8081; + } + + location /proxy/error400 { + proxy_pass http://127.0.0.1:8081; + } + + location /proxy/error502 { + proxy_pass http://127.0.0.1:8081; + } + + location /unbuf { + client_max_body_size 1m; + error_page 502 /unbuf/error502; + proxy_pass http://127.0.0.1:8083; + proxy_request_buffering off; + proxy_http_version 1.1; + } + + location /unbuf/error502 { + client_max_body_size 1m; + proxy_pass http://127.0.0.1:8081; + } + + location /length { + client_max_body_size 1; + error_page 413 /length/error413; + error_page 502 /length/error502; + proxy_pass http://127.0.0.1:8083; + } + + location /length/error413 { + return 200 "frontend body:$content_length:$request_body:"; + } + + location /length/error502 { + return 200 "frontend body:$content_length:$request_body:"; + } + } + + server { + listen 127.0.0.1:8081; + server_name localhost; + + location / { + proxy_pass http://127.0.0.1:8082; + proxy_set_header X-Body body:$content_length:$request_body:; + } + } + + server { + listen 127.0.0.1:8082; + server_name localhost; + + return 200 "backend $http_x_body"; + } + + server { + listen 127.0.0.1:8083; + server_name localhost; + + return 444; + } +} + +EOF + +$t->run(); + +############################################################################### + +# error_page 413 should work without redefining client_max_body_size + +like(http( + 'POST / HTTP/1.0' . CRLF . + 'Content-Length: 10000' . CRLF . CRLF . + '0123456789' +), qr/ 413 .*custom error 413/s, 'custom error 413'); + +# subrequest after discarding body + +like(http( + 'GET /add HTTP/1.0' . CRLF . CRLF +), qr/backend body:::.*main response/s, 'add'); + +like(http( + 'POST /add HTTP/1.0' . CRLF . + 'Content-Length: 10' . CRLF . CRLF . + '0123456789' +), qr/backend body:::.*main response/s, 'add small'); + +like(http( + 'POST /add HTTP/1.0' . CRLF . + 'Content-Length: 10000' . CRLF . CRLF . + '0123456789' +), qr/backend body:::.*main response/s, 'add long'); + +like(http( + 'POST /add HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + 'a' . CRLF . + '0123456789' . CRLF . + '0' . CRLF . CRLF +), qr/backend body:::.*main response/s, 'add chunked'); + +like(http( + 'POST /add HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + '1' . CRLF . + 'X' . CRLF . + '9' . CRLF . + '123456789' . CRLF . + '0' . CRLF . CRLF +), qr/backend body:::.*main response/s, 'add chunked multi'); + +like(http( + 'POST /add HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + 'ffff' . CRLF . + '0123456789' +), qr/backend body:::.*main response/s, 'add chunked long'); + +# error_page 502 with proxy_pass after discarding body + +like(http( + 'GET /memcached HTTP/1.0' . CRLF . CRLF +), qr/ 502 .*backend body:::/s, 'memcached'); + +like(http( + 'GET /memcached HTTP/1.0' . CRLF . + 'Content-Length: 10' . CRLF . CRLF . + '0123456789' +), qr/ 502 .*backend body:::/s, 'memcached small'); + +like(http( + 'GET /memcached HTTP/1.0' . CRLF . + 'Content-Length: 10000' . CRLF . CRLF . + '0123456789' +), qr/ 502 .*backend body:::/s, 'memcached long'); + +like(http( + 'GET /memcached HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + 'a' . CRLF . + '0123456789' . CRLF . + '0' . CRLF . CRLF +), qr/ 502 .*backend body:::/s, 'memcached chunked'); + +like(http( + 'GET /memcached HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + '1' . CRLF . + 'X' . CRLF . + '9' . CRLF . + '123456789' . CRLF . + '0' . CRLF . CRLF +), qr/ 502 .*backend body:::/s, 'memcached chunked multi'); + +like(http( + 'GET /memcached HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + 'ffff' . CRLF . + '0123456789' +), qr/ 502 .*backend body:::/s, 'memcached chunked long'); + +# error_page 413 with proxy_pass + +like(http( + 'GET /proxy HTTP/1.0' . CRLF . CRLF +), qr/ 502 .*backend body:::/s, 'proxy'); + +like(http( + 'POST /proxy HTTP/1.0' . CRLF . + 'Content-Length: 10' . CRLF . CRLF . + '0123456789' +), qr/ 413 .*backend body:::/s, 'proxy small'); + +like(http( + 'POST /proxy HTTP/1.0' . CRLF . + 'Content-Length: 10000' . CRLF . CRLF . + '0123456789' +), qr/ 413 .*backend body:::/s, 'proxy long'); + +like(http( + 'POST /proxy HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + 'a' . CRLF . + '0123456789' . CRLF . + '0' . CRLF . CRLF +), qr/ 413 .*backend body:::/s, 'proxy chunked'); + +like(http( + 'POST /proxy HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + '1' . CRLF . + 'X' . CRLF . + '9' . CRLF . + '123456789' . CRLF . + '0' . CRLF . CRLF +), qr/ 413 .*backend body:::/s, 'proxy chunked multi'); + +like(http( + 'POST /proxy HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + 'ffff' . CRLF . + '0123456789' +), qr/ 413 .*backend body:::/s, 'proxy chunked long'); + +# error_page 400 with proxy_pass + +# note that "chunked and length" test triggers 400 during parsing +# request headers, and therefore needs error_page at server level + +like(http( + 'POST /proxy HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + '1' . CRLF . + 'X' . CRLF . + 'X' . CRLF +), qr/ 400 .*backend body:::/s, 'proxy chunked bad'); + +like(http( + 'POST /proxy HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Content-Length: 10' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + '0' . CRLF . CRLF +), qr/ 400 .*backend body:::/s, 'proxy chunked and length'); + +# error_page 502 after proxy with request buffering disabled + +like(http( + 'GET /unbuf HTTP/1.0' . CRLF . CRLF +), qr/ 502 .*backend body:::/s, 'unbuf proxy'); + +like(http( + 'POST /unbuf HTTP/1.0' . CRLF . + 'Content-Length: 10' . CRLF . CRLF . + '0', + sleep => 0.1, + body => + '123456789' +), qr/ 502 .*backend body:::/s, 'unbuf proxy small'); + +like(http( + 'POST /unbuf HTTP/1.0' . CRLF . + 'Content-Length: 10000' . CRLF . CRLF . + '0123456789' +), qr/ 502 .*backend body:::/s, 'unbuf proxy long'); + +like(http( + 'POST /unbuf HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF, + sleep => 0.1, + body => + 'a' . CRLF . + '0123456789' . CRLF . + '0' . CRLF . CRLF +), qr/ 502 .*backend body:::/s, 'unbuf proxy chunked'); + +like(http( + 'POST /unbuf HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + '1' . CRLF . + 'X' . CRLF, + sleep => 0.1, + body => + '9' . CRLF . + '123456789' . CRLF . + '0' . CRLF . CRLF +), qr/ 502 .*backend body:::/s, 'unbuf proxy chunked multi'); + +like(http( + 'POST /unbuf HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + 'ffff' . CRLF . + '0123456789' +), qr/ 502 .*backend body:::/s, 'unbuf proxy chunked long'); + +# error_page 413 and $content_length +# (used in fastcgi_pass, grpc_pass, uwsgi_pass) + +like(http( + 'GET /length HTTP/1.0' . CRLF . CRLF +), qr/ 502 .*frontend body:::/s, '$content_length'); + +like(http( + 'POST /length HTTP/1.0' . CRLF . + 'Content-Length: 10' . CRLF . CRLF . + '0123456789' +), qr/ 413 .*frontend body:::/s, '$content_length small'); + +like(http( + 'POST /length HTTP/1.0' . CRLF . + 'Content-Length: 10000' . CRLF . CRLF . + '0123456789' +), qr/ 413 .*frontend body:::/s, '$content_length long'); + +like(http( + 'POST /length HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + 'a' . CRLF . + '0123456789' . CRLF . + '0' . CRLF . CRLF +), qr/ 413 .*frontend body:::/s, '$content_length chunked'); + +like(http( + 'POST /length HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + '1' . CRLF . + 'X' . CRLF . + '9' . CRLF . + '123456789' . CRLF . + '0' . CRLF . CRLF +), qr/ 413 .*frontend body:::/s, '$content_length chunked multi'); + +like(http( + 'POST /length HTTP/1.1' . CRLF . + 'Host: localhost' . CRLF . + 'Connection: close' . CRLF . + 'Transfer-Encoding: chunked' . CRLF . CRLF . + 'ffff' . CRLF . + '0123456789' +), qr/ 413 .*frontend body:::/s, '$content_length chunked long'); + +############################################################################### diff --git a/h2_request_body_discard.t b/h2_request_body_discard.t new file mode 100644 --- /dev/null +++ b/h2_request_body_discard.t @@ -0,0 +1,407 @@ +#!/usr/bin/perl + +# (C) Maxim Dounin + +# Tests for discarding request body with HTTP/2. + +############################################################################### + +use warnings; +use strict; + +use Test::More; +use Socket qw/ CRLF /; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; +use Test::Nginx::HTTP2; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +my $t = Test::Nginx->new() + ->has(qw/http http_v2 proxy rewrite addition memcached/); + +plan(skip_all => 'not yet') unless $t->has_version('1.27.0'); + +$t->plan(38)->write_file_expand('nginx.conf', <<'EOF'); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +http { + %%TEST_GLOBALS_HTTP%% + + server { + listen 127.0.0.1:8080; + server_name localhost; + + http2 on; + + lingering_timeout 1s; + add_header X-Body body:$content_length:$request_body:; + + client_max_body_size 1k; + + error_page 400 /proxy/error400; + + location / { + error_page 413 /error413; + proxy_pass http://127.0.0.1:8082; + } + + location /error413 { + return 200 "custom error 413"; + } + + location /add { + return 200 "main response"; + add_before_body /add/before; + addition_types *; + client_max_body_size 1m; + } + + location /add/before { + proxy_pass http://127.0.0.1:8081; + } + + location /memcached { + client_max_body_size 1m; + error_page 502 /memcached/error502; + memcached_pass 127.0.0.1:8083; + set $memcached_key $request_uri; + } + + location /memcached/error502 { + proxy_pass http://127.0.0.1:8081; + } + + location /proxy { + client_max_body_size 3; + error_page 413 /proxy/error413; + error_page 400 /proxy/error400; + error_page 502 /proxy/error502; + proxy_pass http://127.0.0.1:8083; + } + + location /proxy/error413 { + proxy_pass http://127.0.0.1:8081; + } + + location /proxy/error400 { + proxy_pass http://127.0.0.1:8081; + } + + location /proxy/error502 { + proxy_pass http://127.0.0.1:8081; + } + + location /unbuf { + client_max_body_size 1m; + error_page 502 /unbuf/error502; + proxy_pass http://127.0.0.1:8083; + proxy_request_buffering off; + proxy_http_version 1.1; + } + + location /unbuf/error502 { + client_max_body_size 1m; + proxy_pass http://127.0.0.1:8081; + } + + location /unbuf2 { + client_max_body_size 1m; + error_page 400 /unbuf2/error400; + proxy_pass http://127.0.0.1:8081; + proxy_request_buffering off; + proxy_http_version 1.1; + } + + location /unbuf2/error400 { + client_max_body_size 1m; + proxy_pass http://127.0.0.1:8081; + } + + location /length { + client_max_body_size 1; + error_page 413 /length/error413; + error_page 502 /length/error502; + proxy_pass http://127.0.0.1:8083; + } + + location /length/error413 { + return 200 "frontend body:$content_length:$request_body:"; + } + + location /length/error502 { + return 200 "frontend body:$content_length:$request_body:"; + } + } + + server { + listen 127.0.0.1:8081; + server_name localhost; + + location / { + proxy_pass http://127.0.0.1:8082; + proxy_set_header X-Body body:$content_length:$request_body:; + } + } + + server { + listen 127.0.0.1:8082; + server_name localhost; + + return 200 "backend $http_x_body"; + } + + server { + listen 127.0.0.1:8083; + server_name localhost; + + return 444; + } +} + +EOF + +$t->run(); + +############################################################################### + +# error_page 413 should work without redefining client_max_body_size + +like(http2_get_body('/', '0123456789' x 128), + qr/status: 413.*custom error 413/s, 'custom error 413'); + +# subrequest after discarding body + +like(http2_get('/add'), + qr/backend body:::.*main response/s, 'add'); +like(http2_get_body('/add', '0123456789'), + qr/backend body:::.*main response/s, 'add small'); +like(http2_get_body_incomplete('/add', 10000, '0123456789'), + qr/backend body:::.*main response/s, 'add long'); +like(http2_get_body_nolen('/add', '0123456789'), + qr/backend body:::.*main response/s, 'add nolen'); +like(http2_get_body_nolen('/add', '0', '123456789'), + qr/backend body:::.*main response/s, 'add nolen multi'); +like(http2_get_body_incomplete_nolen('/add', 10000, '0123456789'), + qr/backend body:::.*main response/s, 'add chunked long'); + +# error_page 502 with proxy_pass after discarding body + +like(http2_get('/memcached'), + qr/backend body:::/s, 'memcached'); +like(http2_get_body('/memcached', '0123456789'), + qr/status: 502.*backend body:::/s, 'memcached small'); +like(http2_get_body_incomplete('/memcached', 10000, '0123456789'), + qr/status: 502.*backend body:::/s, 'memcached long'); +like(http2_get_body_nolen('/memcached', '0123456789'), + qr/status: 502.*backend body:::/s, 'memcached nolen'); +like(http2_get_body_nolen('/memcached', '0', '123456789'), + qr/status: 502.*backend body:::/s, 'memcached nolen multi'); +like(http2_get_body_incomplete_nolen('/memcached', 10000, '0123456789'), + qr/status: 502.*backend body:::/s, 'memcached nolen long'); + +# error_page 413 with proxy_pass + +like(http2_get('/proxy'), + qr/status: 502.*backend body:::/s, 'proxy'); +like(http2_get_body('/proxy', '0123456789'), + qr/status: 413.*backend body:::/s, 'proxy small'); +like(http2_get_body_incomplete('/proxy', 10000, '0123456789'), + qr/status: 413.*backend body:::/s, 'proxy long'); +like(http2_get_body_nolen('/proxy', '0123456789'), + qr/status: 413.*backend body:::/s, 'proxy nolen'); +like(http2_get_body_nolen('/proxy', '0', '123456789'), + qr/status: 413.*backend body:::/s, 'proxy nolen multi'); +like(http2_get_body_incomplete_nolen('/proxy', 10000, '0123456789'), + qr/status: 413.*backend body:::/s, 'proxy nolen long'); + +# error_page 400 with proxy_pass + +# note that "proxy too short" test triggers 400 during parsing +# request headers, and therefore needs error_page at server level + +like(http2_get_body_custom('/proxy', 1), + qr/status: 400.*backend body:::/s, 'proxy too short'); +like(http2_get_body_custom('/proxy', 1, ''), + qr/status: 400.*backend body:::/s, 'proxy too short body'); +like(http2_get_body_custom('/proxy', 1, '01'), + qr/status: 400.*backend body:::/s, 'proxy too long'); +like(http2_get_body_custom('/proxy', 1, '01', more => 1), + qr/status: 400.*backend body:::/s, 'proxy too long more'); + +# error_page 502 after proxy with request buffering disabled + +like(http2_get('/unbuf'), + qr/status: 502.*backend body:::/s, 'unbuf proxy'); +like(http2_get_body('/unbuf', '0123456789'), + qr/status: 502.*backend body:::/s, 'unbuf proxy small'); +like(http2_get_body_incomplete('/unbuf', 10000, '0123456789'), + qr/status: 502.*backend body:::/s, 'unbuf proxy long'); +like(http2_get_body_nolen('/unbuf', '0123456789'), + qr/status: 502.*backend body:::/s, 'unbuf proxy nolen'); +like(http2_get_body_nolen('/unbuf', '0', '123456789'), + qr/status: 502.*backend body:::/s, 'unbuf proxy nolen multi'); +like(http2_get_body_incomplete_nolen('/unbuf', 10000, '0123456789'), + qr/status: 502.*backend body:::/s, 'unbuf proxy nolen long'); + +# error_page 400 after proxy with request buffering disabled + +like(http2_get_body_custom('/unbuf2', 1, '', sleep => 0.1), + qr/status: 400.*backend body:::/s, 'unbuf too short'); +like(http2_get_body_custom('/unbuf2', 1, '01', sleep => 0.1), + qr/status: 400.*backend body:::/s, 'unbuf too long'); +like(http2_get_body_custom('/unbuf2', 1, '01', sleep => 0.1, more => 1), + qr/status: 400.*backend body:::/s, 'unbuf too long more'); + +# error_page 413 and $content_length +# (used in fastcgi_pass, grpc_pass, uwsgi_pass) + +like(http2_get('/length'), + qr/status: 502.*frontend body:::/s, '$content_length'); +like(http2_get_body('/length', '0123456789'), + qr/status: 413.*frontend body:::/s, '$content_length small'); +like(http2_get_body_incomplete('/length', 10000, '0123456789'), + qr/status: 413.*frontend body:::/s, '$content_length long'); +like(http2_get_body_nolen('/length', '0123456789'), + qr/status: 413.*frontend body:::/s, '$content_length nolen'); +like(http2_get_body_nolen('/length', '0', '123456789'), + qr/status: 413.*frontend body:::/s, '$content_length nolen multi'); +like(http2_get_body_incomplete_nolen('/length', 10000, '0123456789'), + qr/status: 413.*frontend body:::/s, '$content_length nolen long'); + +############################################################################### + +sub http2_get { + my ($uri) = @_; + + my $s = Test::Nginx::HTTP2->new(); + my $sid = $s->new_stream({ path => $uri }); + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my ($data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" . $data->{data}; +} + +sub http2_get_body { + my ($uri, $body) = @_; + + my $s = Test::Nginx::HTTP2->new(); + my $sid = $s->new_stream({ path => $uri, body => $body }); + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my ($data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" . $data->{data}; +} + +sub http2_get_body_nolen { + my ($uri, $body, $body2) = @_; + + my $s = Test::Nginx::HTTP2->new(); + my $sid = $s->new_stream({ path => $uri, body_more => 1 }); + + if (defined $body2) { + $s->h2_body($body, { body_more => 1 }); + $s->h2_body($body2); + } else { + $s->h2_body($body); + } + + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my ($data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" . $data->{data}; +} + +sub http2_get_body_incomplete { + my ($uri, $len, $body) = @_; + + my $s = Test::Nginx::HTTP2->new(); + my $sid = $s->new_stream({ + headers => [ + { name => ':method', value => 'GET' }, + { name => ':scheme', value => 'http' }, + { name => ':path', value => $uri }, + { name => ':authority', value => 'localhost' }, + { name => 'content-length', value => $len }, + ], + body_more => 1 + }); + $s->h2_body($body, { body_more => 1 }); + + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my ($data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" . $data->{data}; +} + +sub http2_get_body_incomplete_nolen { + my ($uri, $len, $body) = @_; + + my $s = Test::Nginx::HTTP2->new(); + my $sid = $s->new_stream({ path => $uri, body_more => 1 }); + $s->h2_body($body, { body_more => 1 }); + + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my ($data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" . $data->{data}; +} + +sub http2_get_body_custom { + my ($uri, $len, $body, %extra) = @_; + + my $s = Test::Nginx::HTTP2->new(); + my $sid = $s->new_stream({ + headers => [ + { name => ':method', value => 'GET' }, + { name => ':scheme', value => 'http' }, + { name => ':path', value => $uri }, + { name => ':authority', value => 'localhost' }, + { name => 'content-length', value => $len }, + ], + body_more => (defined $body ? 1 : undef) + }); + + if (defined $body) { + select undef, undef, undef, $extra{sleep} if $extra{sleep}; + $s->h2_body($body, { body_more => 1 }); + $s->h2_body('') unless $extra{more}; + } + + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my ($data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" . $data->{data}; +} + +############################################################################### diff --git a/h3_request_body_discard.t b/h3_request_body_discard.t new file mode 100644 --- /dev/null +++ b/h3_request_body_discard.t @@ -0,0 +1,426 @@ +#!/usr/bin/perl + +# (C) Maxim Dounin + +# Tests for discarding request body with HTTP/3. + +############################################################################### + +use warnings; +use strict; + +use Test::More; +use Socket qw/ CRLF /; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; +use Test::Nginx::HTTP3; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +my $t = Test::Nginx->new() + ->has(qw/http http_v3 proxy rewrite addition memcached cryptx/) + ->has_daemon('openssl'); + +plan(skip_all => 'not yet') unless $t->has_version('1.27.0'); + +$t->plan(37)->write_file_expand('nginx.conf', <<'EOF'); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +http { + %%TEST_GLOBALS_HTTP%% + + ssl_certificate localhost.crt; + ssl_certificate_key localhost.key; + + server { + listen 127.0.0.1:%%PORT_8980_UDP%% quic; + server_name localhost; + + lingering_timeout 1s; + add_header X-Body body:$content_length:$request_body:; + + client_max_body_size 1k; + + location / { + error_page 413 /error413; + proxy_pass http://127.0.0.1:8082; + } + + location /error413 { + return 200 "custom error 413"; + } + + location /add { + return 200 "main response"; + add_before_body /add/before; + addition_types *; + client_max_body_size 1m; + } + + location /add/before { + proxy_pass http://127.0.0.1:8081; + } + + location /memcached { + client_max_body_size 1m; + error_page 502 /memcached/error502; + memcached_pass 127.0.0.1:8083; + set $memcached_key $request_uri; + } + + location /memcached/error502 { + proxy_pass http://127.0.0.1:8081; + } + + location /proxy { + client_max_body_size 3; + error_page 413 /proxy/error413; + error_page 400 /proxy/error400; + error_page 502 /proxy/error502; + proxy_pass http://127.0.0.1:8083; + } + + location /proxy/error413 { + proxy_pass http://127.0.0.1:8081; + } + + location /proxy/error400 { + proxy_pass http://127.0.0.1:8081; + } + + location /proxy/error502 { + proxy_pass http://127.0.0.1:8081; + } + + location /unbuf { + client_max_body_size 1m; + error_page 502 /unbuf/error502; + proxy_pass http://127.0.0.1:8083; + proxy_request_buffering off; + proxy_http_version 1.1; + } + + location /unbuf/error502 { + client_max_body_size 1m; + proxy_pass http://127.0.0.1:8081; + } + + location /unbuf2 { + client_max_body_size 1m; + error_page 400 /unbuf2/error400; + proxy_pass http://127.0.0.1:8081; + proxy_request_buffering off; + proxy_http_version 1.1; + } + + location /unbuf2/error400 { + client_max_body_size 1m; + proxy_pass http://127.0.0.1:8081; + } + + location /length { + client_max_body_size 1; + error_page 413 /length/error413; + error_page 502 /length/error502; + proxy_pass http://127.0.0.1:8083; + } + + location /length/error413 { + return 200 "frontend body:$content_length:$request_body:"; + } + + location /length/error502 { + return 200 "frontend body:$content_length:$request_body:"; + } + } + + server { + listen 127.0.0.1:8081; + server_name localhost; + + location / { + proxy_pass http://127.0.0.1:8082; + proxy_set_header X-Body body:$content_length:$request_body:; + } + } + + server { + listen 127.0.0.1:8082; + server_name localhost; + + return 200 "backend $http_x_body"; + } + + server { + listen 127.0.0.1:8083; + server_name localhost; + + return 444; + } +} + +EOF + +$t->write_file('openssl.conf', <testdir(); + +foreach my $name ('localhost') { + system('openssl req -x509 -new ' + . "-config $d/openssl.conf -subj /CN=$name/ " + . "-out $d/$name.crt -keyout $d/$name.key " + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't create certificate for $name: $!\n"; +} + +$t->run(); + +############################################################################### + +# error_page 413 should work without redefining client_max_body_size + +like(http3_get_body('/', '0123456789' x 128), + qr/status: 413.*custom error 413/s, 'custom error 413'); + +# subrequest after discarding body + +like(http3_get('/add'), + qr/backend body:::.*main response/s, 'add'); +like(http3_get_body('/add', '0123456789'), + qr/backend body:::.*main response/s, 'add small'); +like(http3_get_body_incomplete('/add', 10000, '0123456789'), + qr/backend body:::.*main response/s, 'add long'); +like(http3_get_body_nolen('/add', '0123456789'), + qr/backend body:::.*main response/s, 'add nolen'); +like(http3_get_body_nolen('/add', '0', '123456789'), + qr/backend body:::.*main response/s, 'add nolen multi'); +like(http3_get_body_incomplete_nolen('/add', 10000, '0123456789'), + qr/backend body:::.*main response/s, 'add chunked long'); + +# error_page 502 with proxy_pass after discarding body + +like(http3_get('/memcached'), + qr/backend body:::/s, 'memcached'); +like(http3_get_body('/memcached', '0123456789'), + qr/status: 502.*backend body:::/s, 'memcached small'); +like(http3_get_body_incomplete('/memcached', 10000, '0123456789'), + qr/status: 502.*backend body:::/s, 'memcached long'); +like(http3_get_body_nolen('/memcached', '0123456789'), + qr/status: 502.*backend body:::/s, 'memcached nolen'); +like(http3_get_body_nolen('/memcached', '0', '123456789'), + qr/status: 502.*backend body:::/s, 'memcached nolen multi'); +like(http3_get_body_incomplete_nolen('/memcached', 10000, '0123456789'), + qr/status: 502.*backend body:::/s, 'memcached nolen long'); + +# error_page 413 with proxy_pass + +like(http3_get('/proxy'), + qr/status: 502.*backend body:::/s, 'proxy'); +like(http3_get_body('/proxy', '0123456789'), + qr/status: 413.*backend body:::/s, 'proxy small'); +like(http3_get_body_incomplete('/proxy', 10000, '0123456789'), + qr/status: 413.*backend body:::/s, 'proxy long'); +like(http3_get_body_nolen('/proxy', '0123456789'), + qr/status: 413.*backend body:::/s, 'proxy nolen'); +like(http3_get_body_nolen('/proxy', '0', '123456789'), + qr/status: 413.*backend body:::/s, 'proxy nolen multi'); +like(http3_get_body_incomplete_nolen('/proxy', '0123456789'), + qr/status: 413.*backend body:::/s, 'proxy nolen long'); + +# error_page 400 with proxy_pass + +like(http3_get_body_custom('/proxy', 1, ''), + qr/status: 400.*backend body:::/s, 'proxy too short'); +like(http3_get_body_custom('/proxy', 1, '01'), + qr/status: 400.*backend body:::/s, 'proxy too long'); +like(http3_get_body_custom('/proxy', 1, '01', more => 1), + qr/status: 400.*backend body:::/s, 'proxy too long more'); + +# error_page 502 after proxy with request buffering disabled + +like(http3_get('/unbuf'), + qr/status: 502.*backend body:::/s, 'unbuf proxy'); +like(http3_get_body_custom('/unbuf', 10, '0123456789', sleep => 0.1), + qr/status: 502.*backend body:::/s, 'unbuf proxy small'); +like(http3_get_body_incomplete('/unbuf', 10000, '0123456789'), + qr/status: 502.*backend body:::/s, 'unbuf proxy long'); +like(http3_get_body_nolen('/unbuf', '0123456789'), + qr/status: 502.*backend body:::/s, 'unbuf proxy nolen'); +like(http3_get_body_nolen('/unbuf', '0', '123456789'), + qr/status: 502.*backend body:::/s, 'unbuf proxy nolen multi'); +like(http3_get_body_incomplete_nolen('/unbuf', 10000, '0123456789'), + qr/status: 502.*backend body:::/s, 'unbuf proxy nolen long'); + +# error_page 400 after proxy with request buffering disabled + +like(http3_get_body_custom('/unbuf2', 1, '', sleep => 0.1), + qr/status: 400.*backend body:::/s, 'unbuf too short'); +like(http3_get_body_custom('/unbuf2', 1, '01', sleep => 0.1), + qr/status: 400.*backend body:::/s, 'unbuf too long'); +like(http3_get_body_custom('/unbuf2', 1, '01', sleep => 0.1, more => 1), + qr/status: 400.*backend body:::/s, 'unbuf too long more'); + +# error_page 413 and $content_length +# (used in fastcgi_pass, grpc_pass, uwsgi_pass) + +like(http3_get('/length'), + qr/status: 502.*frontend body:::/s, '$content_length'); +like(http3_get_body('/length', '0123456789'), + qr/status: 413.*frontend body:::/s, '$content_length small'); +like(http3_get_body_incomplete('/length', 10000, '0123456789'), + qr/status: 413.*frontend body:::/s, '$content_length long'); +like(http3_get_body_nolen('/length', '0123456789'), + qr/status: 413.*frontend body:::/s, '$content_length nolen'); +like(http3_get_body_nolen('/length', '0', '123456789'), + qr/status: 413.*frontend body:::/s, '$content_length nolen multi'); +like(http3_get_body_incomplete_nolen('/length', 10000, '0123456789'), + qr/status: 413.*frontend body:::/s, '$content_length nolen long'); + +############################################################################### + +sub http3_get { + my ($uri) = @_; + + my $s = Test::Nginx::HTTP3->new(); + my $sid = $s->new_stream({ path => $uri }); + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my (@data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" + . join("", map { $_->{data} } @data); +} + +sub http3_get_body { + my ($uri, $body) = @_; + + my $s = Test::Nginx::HTTP3->new(); + my $sid = $s->new_stream({ path => $uri, body => $body }); + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my (@data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" + . join("", map { $_->{data} } @data); +} + +sub http3_get_body_nolen { + my ($uri, $body, $body2) = @_; + + my $s = Test::Nginx::HTTP3->new(); + my $sid = $s->new_stream({ path => $uri, body_more => 1 }); + + if (defined $body2) { + select undef, undef, undef, 0.1; + $s->h3_body($body, $sid, { body_more => 1 }); + select undef, undef, undef, 0.1; + $s->h3_body($body2, $sid); + } else { + select undef, undef, undef, 0.1; + $s->h3_body($body, $sid); + } + + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my (@data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" + . join("", map { $_->{data} } @data); +} + +sub http3_get_body_incomplete { + my ($uri, $len, $body) = @_; + + my $s = Test::Nginx::HTTP3->new(); + my $sid = $s->new_stream({ + headers => [ + { name => ':method', value => 'GET' }, + { name => ':scheme', value => 'http' }, + { name => ':path', value => $uri }, + { name => ':authority', value => 'localhost' }, + { name => 'content-length', value => $len }, + ], + body_more => 1 + }); + $s->h3_body($body, $sid, { body_more => 1 }); + + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my (@data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" + . join("", map { $_->{data} } @data); +} + +sub http3_get_body_incomplete_nolen { + my ($uri, $body) = @_; + + my $s = Test::Nginx::HTTP3->new(); + my $sid = $s->new_stream({ path => $uri, body_more => 1 }); + $s->h3_body($body, $sid, { body_more => 1 }); + + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my (@data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" + . join("", map { $_->{data} } @data); +} + +sub http3_get_body_custom { + my ($uri, $len, $body, %extra) = @_; + + my $s = Test::Nginx::HTTP3->new(); + my $sid = $s->new_stream({ + headers => [ + { name => ':method', value => 'GET' }, + { name => ':scheme', value => 'http' }, + { name => ':path', value => $uri }, + { name => ':authority', value => 'localhost' }, + { name => 'content-length', value => $len }, + ], + body_more => 1 + }); + select undef, undef, undef, $extra{sleep} if $extra{sleep}; + $s->h3_body($body, $sid, { body_more => 1 }); + $s->h3_body('', $sid) unless $extra{more}; + + my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); + + my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; + my (@data) = grep { $_->{type} eq "DATA" } @$frames; + + return join("\n", map { "$_: " . $frame->{headers}->{$_}; } + keys %{$frame->{headers}}) . "\n\n" + . join("", map { $_->{data} } @data); +} + +###############################################################################