diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6960314
--- /dev/null
+++ b/README.md
@@ -0,0 +1,27 @@
+### 工程简介:
+
+#### 工程包括PC端以及android端:其中PC端包括AI运算部分以及Web服务部分,android端包括app以及网页部分。上述除Web服务部分均具有手语翻译功能,且均可实现本地运算与云上运算。
+
+#### android端包括app及网页部分,在设备算力足够的情况下进行本地运算,如果算力不足则可选择云上运算。如果您想为本项目贡献更多,可以分享您的算力为他人提供运算。android端主要模块如下:
+
+```
+media_camera()//获取摄像头的视频流
+media_pipe()//获取云上视频流,为他人提供运算
+hands_recegnation()//对手语进行识别,转化为自然语言
+word_show()//在本地输出转化后的自然语言
+word_send()//向云端发送转化后的自然语言,为他人提供运算
+word_input()//本地输入自然语言转化为手语动画
+word_recv()//接收云端的数据转化为手语
+hands_translation()//将输入的自然语言转化为手语数据
+hands_3d()//根据数据生成3d手语动画
+```
+
+#### PC端包括AI运算部分以及Web服务部分,AI部分为其它设备提供算力支持,Web服务部分则进行提供android端网页链接及设备算力共享服务。PC端主要模块如下:
+
+```
+包含android所有算力共享服务
+nginx-rtmp()//提供视频流传输服务
+load_alancing()//负载均衡,为算力共享设备选择提供服务
+Webserver()//为网页提供后端支持
+```
+
diff --git a/doc/用例描述(已作废).docx b/doc/用例描述(已作废).docx
new file mode 100644
index 0000000..0195e60
--- /dev/null
+++ b/doc/用例描述(已作废).docx
@@ -0,0 +1,46 @@
+ͼ
+
+
+Ƶȡ
+ҵĿ꣺ȡƵϢ
+ִߣϵͳ
+ǰͷ
+
+1ͷ
+2ȡͷϢ
+3ػƶ
+չ
+1a: ѡǰúͷ
+
+ֻȡ
+ҵĿ꣺ȡϢ
+ִߣϵͳ
+ǰƶ˻
+
+1ȡϢ
+2ֽзƬ
+3ػƶ
+
+
+
+ҵĿ꣺Ϣз
+ִߣϵͳ
+
+1Ϣ
+2ݷӦ
+3ݷ͵ƶ˻
+4Ϣû
+չ
+4aжƵתΪֻתΪ
+
+
+ҵĿ꣺ؾ⼰web
+ִߣ
+
+1ҳ
+2ûз
+3û֮ݹͨƵݵȣ
+չ
+4aû
+
+
diff --git a/doc/第一次作业.md b/doc/第一次作业.md
new file mode 100644
index 0000000..1063a61
--- /dev/null
+++ b/doc/第一次作业.md
@@ -0,0 +1,148 @@
+## 1、系统说明:
+
+##### 手语翻译项目旨在构建一个手语翻译系统,方便手语与自然语言的相互转换,这个系统的主要功能有:
+
+1. 实现手语视频录制,并转化为自然语言
+2. 完成自然语言转化为手语动画
+3. 实现设备间算力共享
+
+##### 手语翻译系统只有一类用户:用户
+
+功能1,2供用户使用
+
+功能3根据用户意愿开启
+
+##### 该系统必须满足以下限制:
+
+1. 能以较低的性能要求实现运算
+2. 仅在用户允许情况下使用算力共享
+
+## 2、需求获取与分析
+
+### 2.1系统初步用例图:
+
+![](/home/smj/Work/work2/初步用例图.png)
+
+### 2.2用例描述与顺序图:
+
+**用例名**:输入自然语言
+**业务目标**:用户输入需要翻译的自然语言
+**执行者**:用户
+**前置条件**:app正常运行
+**基本事件流**:
+
+ 1、用户打开手语翻译app
+ 2、用户点击输入框
+ 3、用户输入自然语言
+ 4、用户提交自然语言
+**扩展交互动作**:
+
+ 1a: 修改输入内容
+
+ 1a1:直接在输入框进行文本编辑
+
+**后置条件**:输入数据无损失
+
+**可靠性需求**:数据安全性无限接近100%
+
+![](/home/smj/Work/work2/输入自然语言.png)
+
+**用例名**:获取手语视频
+**业务目标**:用户上传视频到app
+**执行者**:用户
+**前置条件**:app正常运行
+**基本事件流**:
+
+ 1、用户打开手语翻译app
+ 2、用户允许app开启摄像头
+ 3、用户打开摄像头
+ 4、app获取摄像头视频流
+**扩展交互动作**:
+
+ 1a: 重新录制视频
+
+ 1a1:删除已录制视频并重新录制
+
+**后置条件**:硬件正常时,能获取无损视频
+
+**可靠性要求**:视频在传输过程中受损率无限接近0%
+
+![](/home/smj/Work/work2/获取手语视频.png)
+
+**用例名**:翻译自然语言为动画
+**业务目标**:将用户输入或云端获取的自然语言转换为3D动画
+**执行者**:用户
+**前置条件**:
+
+ 1、网络正常连接或本地算力足够
+ 2、用户已输入自然语言
+**基本事件流**:
+ 1、用户点击“语言翻译”按钮
+ 2、app对获取自然语言进行翻译
+**扩展交互动作**:
+
+ 1a: 选择本地运算或云端运算
+
+ 1a1:根据选择来确定运算设备
+
+**后置条件**:在词库内有对应数据时,可生成完整动画
+
+**性能要求**:响应事件小于200ms
+
+**可靠性要求**:保证词条对应数据100%正确
+
+![](/home/smj/Work/work2/翻译自然语言为动画.png)
+
+**用例名**:翻译视频为自然语言
+**业务目标**:将用户输入或云端获取的视频转换为自然语言
+**执行者**:用户
+**前置条件**:
+
+ 1、网络正常连接或本地算力足够
+ 2、用户已输入视频
+**基本事件流**:
+ 1、用户点击“视频翻译”按钮
+ 2、app对视频进行翻译
+**扩展交互动作**:
+
+ 1a: 选择本地运算或云端运算
+
+ 1a1:根据选择来确定运算设备
+
+**后置条件**:当视频内容有效时,根据视频可生成对应自然语言
+
+**性能要求**:每秒处理速度达到30fps
+
+**可靠性要求**:翻译直译准确率达到90%以上
+
+![](/home/smj/Work/work2/翻译视频为自然语言.png)
+
+**用例名**:显示动画
+**业务目标**:在app显示数据转换后的3D动画
+**执行者**:用户
+**前置条件**:app已经获取3D动画数据
+**基本事件流**:
+ 1、app显示生成3D动画
+
+**后置条件**:当动画生成后,在屏幕指定位置展示动画
+
+**可靠性要求**:动作混淆错位等错误发生率为0%
+
+![](/home/smj/Work/work2/显示动画.png)
+
+**用例名**:显示自然语言
+**业务目标**:在app显示数据转换后的自然语言
+**执行者**:用户
+**前置条件**:app已经获取处理后的自然语言
+**基本事件流**:
+ 1、app显示翻译后的自然语言
+
+**后置条件**:当收到自然语言时,在屏幕指定位置生成文字
+
+**可靠性要求**::保证数据准确度为100%
+
+![](/home/smj/Work/work2/显示自然语言.png)
+
+### 2.3分析类图:
+
+![](/home/smj/Work/work2/分析类图.png)
diff --git a/doc/第一次作业.pdf b/doc/第一次作业.pdf
new file mode 100644
index 0000000..7a25a89
Binary files /dev/null and b/doc/第一次作业.pdf differ
diff --git a/doc/第二次作业.md b/doc/第二次作业.md
new file mode 100644
index 0000000..f2ee684
--- /dev/null
+++ b/doc/第二次作业.md
@@ -0,0 +1,44 @@
+
+
+## 3、体系结构设计:
+
+### 3.1体系结构的逻辑视图
+
+![](/home/smj/Documents/软工/体系结构逻辑视图.drawio.png)
+
+### 3.2体系结构设计的开发视图
+
+![](/home/smj/Documents/软工/体系结构开发视图.drawio.png)
+
+
+
+## 4、用户界面设计
+
+### 4.1系统界面的外观设计及其类表示
+
+#### 4.1.1视频翻译界面及其类表示:
+
+![](/home/smj/Work/for_you/model/app/im1.png)
+
+![](/home/smj/Work/work2/手语翻译.png)
+
+#### 4.1.2文字翻译界面及其类表示:
+
+![](/home/smj/Work/for_you/model/app/im2.png)
+
+![](/home/smj/Work/work2/自然语言翻译.png)
+
+#### 4.1.3设置界面及其类表示:
+
+![im3](/home/smj/Work/for_you/model/app/im3.png)
+
+![](/home/smj/Work/work2/设置.png)
+
+### 4.2界面流的类表示:
+
+![](/home/smj/Work/work2/界面流类表示.png)
+
+### 4.3界面流的顺序图:
+
+![](/home/smj/Work/work2/界面流顺序图.png)
+
diff --git a/doc/第二次作业.pdf b/doc/第二次作业.pdf
new file mode 100644
index 0000000..ebd4a31
Binary files /dev/null and b/doc/第二次作业.pdf differ
diff --git a/doc/软件需求构思.docx b/doc/软件需求构思.docx
new file mode 100644
index 0000000..e767ccb
Binary files /dev/null and b/doc/软件需求构思.docx differ
diff --git a/model/app/im1.png b/model/app/im1.png
new file mode 100644
index 0000000..80ea7b8
Binary files /dev/null and b/model/app/im1.png differ
diff --git a/model/app/im2.png b/model/app/im2.png
new file mode 100644
index 0000000..5ae8383
Binary files /dev/null and b/model/app/im2.png differ
diff --git a/model/app/im3.png b/model/app/im3.png
new file mode 100644
index 0000000..813b27a
Binary files /dev/null and b/model/app/im3.png differ
diff --git a/model/体系结构/体系结构开发视图.drawio.png b/model/体系结构/体系结构开发视图.drawio.png
new file mode 100644
index 0000000..19737c6
Binary files /dev/null and b/model/体系结构/体系结构开发视图.drawio.png differ
diff --git a/model/体系结构/体系结构逻辑视图.drawio.png b/model/体系结构/体系结构逻辑视图.drawio.png
new file mode 100644
index 0000000..a0715d4
Binary files /dev/null and b/model/体系结构/体系结构逻辑视图.drawio.png differ
diff --git a/model/分析类图.drawio.png b/model/分析类图.drawio.png
deleted file mode 100644
index 208014d..0000000
Binary files a/model/分析类图.drawio.png and /dev/null differ
diff --git a/model/用例及分析类图/分析类图.png b/model/用例及分析类图/分析类图.png
new file mode 100644
index 0000000..dff90b5
Binary files /dev/null and b/model/用例及分析类图/分析类图.png differ
diff --git a/model/用例及分析类图/初步用例图.png b/model/用例及分析类图/初步用例图.png
new file mode 100644
index 0000000..d19fd7e
Binary files /dev/null and b/model/用例及分析类图/初步用例图.png differ
diff --git a/model/用例图.png b/model/用例图.png
deleted file mode 100644
index 84a2512..0000000
Binary files a/model/用例图.png and /dev/null differ
diff --git a/model/用例顺序图/显示动画.png b/model/用例顺序图/显示动画.png
new file mode 100644
index 0000000..562ada0
Binary files /dev/null and b/model/用例顺序图/显示动画.png differ
diff --git a/model/用例顺序图/显示自然语言.png b/model/用例顺序图/显示自然语言.png
new file mode 100644
index 0000000..b0c9d2b
Binary files /dev/null and b/model/用例顺序图/显示自然语言.png differ
diff --git a/model/用例顺序图/翻译自然语言为动画.png b/model/用例顺序图/翻译自然语言为动画.png
new file mode 100644
index 0000000..53b5165
Binary files /dev/null and b/model/用例顺序图/翻译自然语言为动画.png differ
diff --git a/model/用例顺序图/翻译视频为自然语言.png b/model/用例顺序图/翻译视频为自然语言.png
new file mode 100644
index 0000000..978e8c5
Binary files /dev/null and b/model/用例顺序图/翻译视频为自然语言.png differ
diff --git a/model/用例顺序图/获取手语视频.png b/model/用例顺序图/获取手语视频.png
new file mode 100644
index 0000000..bb461ae
Binary files /dev/null and b/model/用例顺序图/获取手语视频.png differ
diff --git a/model/用例顺序图/输入自然语言.png b/model/用例顺序图/输入自然语言.png
new file mode 100644
index 0000000..9ffd5b4
Binary files /dev/null and b/model/用例顺序图/输入自然语言.png differ
diff --git a/model/界面流/Setting.png b/model/界面流/Setting.png
new file mode 100644
index 0000000..3e708d7
Binary files /dev/null and b/model/界面流/Setting.png differ
diff --git a/model/界面流/SignTranslation.png b/model/界面流/SignTranslation.png
new file mode 100644
index 0000000..5cc1e68
Binary files /dev/null and b/model/界面流/SignTranslation.png differ
diff --git a/model/界面流/VideoTranslation.png b/model/界面流/VideoTranslation.png
new file mode 100644
index 0000000..fd171a2
Binary files /dev/null and b/model/界面流/VideoTranslation.png differ
diff --git a/model/界面流/界面流的顺序图.png b/model/界面流/界面流的顺序图.png
new file mode 100644
index 0000000..78e3408
Binary files /dev/null and b/model/界面流/界面流的顺序图.png differ
diff --git a/model/界面流/界面流顺序图.png b/model/界面流/界面流顺序图.png
new file mode 100644
index 0000000..03e5649
Binary files /dev/null and b/model/界面流/界面流顺序图.png differ
diff --git a/model/顺序图.png b/model/顺序图.png
deleted file mode 100644
index f86fefa..0000000
Binary files a/model/顺序图.png and /dev/null differ
diff --git a/src/nginx-1.18.0/CHANGES b/src/nginx-1.18.0/CHANGES
new file mode 100644
index 0000000..6da9172
--- /dev/null
+++ b/src/nginx-1.18.0/CHANGES
@@ -0,0 +1,8508 @@
+
+Changes with nginx 1.18.0 21 Apr 2020
+
+ *) 1.18.x stable branch.
+
+
+Changes with nginx 1.17.10 14 Apr 2020
+
+ *) Feature: the "auth_delay" directive.
+
+
+Changes with nginx 1.17.9 03 Mar 2020
+
+ *) Change: now nginx does not allow several "Host" request header lines.
+
+ *) Bugfix: nginx ignored additional "Transfer-Encoding" request header
+ lines.
+
+ *) Bugfix: socket leak when using HTTP/2.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if OCSP
+ stapling was used.
+
+ *) Bugfix: in the ngx_http_mp4_module.
+
+ *) Bugfix: nginx used status code 494 instead of 400 if errors with code
+ 494 were redirected with the "error_page" directive.
+
+ *) Bugfix: socket leak when using subrequests in the njs module and the
+ "aio" directive.
+
+
+Changes with nginx 1.17.8 21 Jan 2020
+
+ *) Feature: variables support in the "grpc_pass" directive.
+
+ *) Bugfix: a timeout might occur while handling pipelined requests in an
+ SSL connection; the bug had appeared in 1.17.5.
+
+ *) Bugfix: in the "debug_points" directive when using HTTP/2.
+ Thanks to Daniil Bondarev.
+
+
+Changes with nginx 1.17.7 24 Dec 2019
+
+ *) Bugfix: a segmentation fault might occur on start or during
+ reconfiguration if the "rewrite" directive with an empty replacement
+ string was used in the configuration.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "break" directive was used with the "alias" directive or with the
+ "proxy_pass" directive with a URI.
+
+ *) Bugfix: the "Location" response header line might contain garbage if
+ the request URI was rewritten to the one containing a null character.
+
+ *) Bugfix: requests with bodies were handled incorrectly when returning
+ redirections with the "error_page" directive; the bug had appeared in
+ 0.7.12.
+
+ *) Bugfix: socket leak when using HTTP/2.
+
+ *) Bugfix: a timeout might occur while handling pipelined requests in an
+ SSL connection; the bug had appeared in 1.17.5.
+
+ *) Bugfix: in the ngx_http_dav_module.
+
+
+Changes with nginx 1.17.6 19 Nov 2019
+
+ *) Feature: the $proxy_protocol_server_addr and
+ $proxy_protocol_server_port variables.
+
+ *) Feature: the "limit_conn_dry_run" directive.
+
+ *) Feature: the $limit_req_status and $limit_conn_status variables.
+
+
+Changes with nginx 1.17.5 22 Oct 2019
+
+ *) Feature: now nginx uses ioctl(FIONREAD), if available, to avoid
+ reading from a fast connection for a long time.
+
+ *) Bugfix: incomplete escaped characters at the end of the request URI
+ were ignored.
+
+ *) Bugfix: "/." and "/.." at the end of the request URI were not
+ normalized.
+
+ *) Bugfix: in the "merge_slashes" directive.
+
+ *) Bugfix: in the "ignore_invalid_headers" directive.
+ Thanks to Alan Kemp.
+
+ *) Bugfix: nginx could not be built with MinGW-w64 gcc 8.1 or newer.
+
+
+Changes with nginx 1.17.4 24 Sep 2019
+
+ *) Change: better detection of incorrect client behavior in HTTP/2.
+
+ *) Change: in handling of not fully read client request body when
+ returning errors in HTTP/2.
+
+ *) Bugfix: the "worker_shutdown_timeout" directive might not work when
+ using HTTP/2.
+
+ *) Bugfix: a segmentation fault might occur in a worker process when
+ using HTTP/2 and the "proxy_request_buffering" directive.
+
+ *) Bugfix: the ECONNABORTED error log level was "crit" instead of
+ "error" on Windows when using SSL.
+
+ *) Bugfix: nginx ignored extra data when using chunked transfer
+ encoding.
+
+ *) Bugfix: nginx always returned the 500 error if the "return" directive
+ was used and an error occurred during reading client request body.
+
+ *) Bugfix: in memory allocation error handling.
+
+
+Changes with nginx 1.17.3 13 Aug 2019
+
+ *) Security: when using HTTP/2 a client might cause excessive memory
+ consumption and CPU usage (CVE-2019-9511, CVE-2019-9513,
+ CVE-2019-9516).
+
+ *) Bugfix: "zero size buf" alerts might appear in logs when using
+ gzipping; the bug had appeared in 1.17.2.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "resolver" directive was used in SMTP proxy.
+
+
+Changes with nginx 1.17.2 23 Jul 2019
+
+ *) Change: minimum supported zlib version is 1.2.0.4.
+ Thanks to Ilya Leoshkevich.
+
+ *) Change: the $r->internal_redirect() embedded perl method now expects
+ escaped URIs.
+
+ *) Feature: it is now possible to switch to a named location using the
+ $r->internal_redirect() embedded perl method.
+
+ *) Bugfix: in error handling in embedded perl.
+
+ *) Bugfix: a segmentation fault might occur on start or during
+ reconfiguration if hash bucket size larger than 64 kilobytes was used
+ in the configuration.
+
+ *) Bugfix: nginx might hog CPU during unbuffered proxying and when
+ proxying WebSocket connections if the select, poll, or /dev/poll
+ methods were used.
+
+ *) Bugfix: in the ngx_http_xslt_filter_module.
+
+ *) Bugfix: in the ngx_http_ssi_filter_module.
+
+
+Changes with nginx 1.17.1 25 Jun 2019
+
+ *) Feature: the "limit_req_dry_run" directive.
+
+ *) Feature: when using the "hash" directive inside the "upstream" block
+ an empty hash key now triggers round-robin balancing.
+ Thanks to Niklas Keller.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if
+ caching was used along with the "image_filter" directive, and errors
+ with code 415 were redirected with the "error_page" directive; the
+ bug had appeared in 1.11.10.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if
+ embedded perl was used; the bug had appeared in 1.7.3.
+
+
+Changes with nginx 1.17.0 21 May 2019
+
+ *) Feature: variables support in the "limit_rate" and "limit_rate_after"
+ directives.
+
+ *) Feature: variables support in the "proxy_upload_rate" and
+ "proxy_download_rate" directives in the stream module.
+
+ *) Change: minimum supported OpenSSL version is 0.9.8.
+
+ *) Change: now the postpone filter is always built.
+
+ *) Bugfix: the "include" directive did not work inside the "if" and
+ "limit_except" blocks.
+
+ *) Bugfix: in byte ranges processing.
+
+
+Changes with nginx 1.15.12 16 Apr 2019
+
+ *) Bugfix: a segmentation fault might occur in a worker process if
+ variables were used in the "ssl_certificate" or "ssl_certificate_key"
+ directives and OCSP stapling was enabled.
+
+
+Changes with nginx 1.15.11 09 Apr 2019
+
+ *) Bugfix: in the "ssl_stapling_file" directive on Windows.
+
+
+Changes with nginx 1.15.10 26 Mar 2019
+
+ *) Change: when using a hostname in the "listen" directive nginx now
+ creates listening sockets for all addresses the hostname resolves to
+ (previously, only the first address was used).
+
+ *) Feature: port ranges in the "listen" directive.
+
+ *) Feature: loading of SSL certificates and secret keys from variables.
+
+ *) Workaround: the $ssl_server_name variable might be empty when using
+ OpenSSL 1.1.1.
+
+ *) Bugfix: nginx/Windows could not be built with Visual Studio 2015 or
+ newer; the bug had appeared in 1.15.9.
+
+
+Changes with nginx 1.15.9 26 Feb 2019
+
+ *) Feature: variables support in the "ssl_certificate" and
+ "ssl_certificate_key" directives.
+
+ *) Feature: the "poll" method is now available on Windows when using
+ Windows Vista or newer.
+
+ *) Bugfix: if the "select" method was used on Windows and an error
+ occurred while establishing a backend connection, nginx waited for
+ the connection establishment timeout to expire.
+
+ *) Bugfix: the "proxy_upload_rate" and "proxy_download_rate" directives
+ in the stream module worked incorrectly when proxying UDP datagrams.
+
+
+Changes with nginx 1.15.8 25 Dec 2018
+
+ *) Feature: the $upstream_bytes_sent variable.
+ Thanks to Piotr Sikora.
+
+ *) Feature: new directives in vim syntax highlighting scripts.
+ Thanks to Gena Makhomed.
+
+ *) Bugfix: in the "proxy_cache_background_update" directive.
+
+ *) Bugfix: in the "geo" directive when using unix domain listen sockets.
+
+ *) Workaround: the "ignoring stale global SSL error ... bad length"
+ alerts might appear in logs when using the "ssl_early_data" directive
+ with OpenSSL.
+
+ *) Bugfix: in nginx/Windows.
+
+ *) Bugfix: in the ngx_http_autoindex_module on 32-bit platforms.
+
+
+Changes with nginx 1.15.7 27 Nov 2018
+
+ *) Feature: the "proxy_requests" directive in the stream module.
+
+ *) Feature: the "delay" parameter of the "limit_req" directive.
+ Thanks to Vladislav Shabanov and Peter Shchuchkin.
+
+ *) Bugfix: memory leak on errors during reconfiguration.
+
+ *) Bugfix: in the $upstream_response_time, $upstream_connect_time, and
+ $upstream_header_time variables.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ ngx_http_mp4_module was used on 32-bit platforms.
+
+
+Changes with nginx 1.15.6 06 Nov 2018
+
+ *) Security: when using HTTP/2 a client might cause excessive memory
+ consumption (CVE-2018-16843) and CPU usage (CVE-2018-16844).
+
+ *) Security: processing of a specially crafted mp4 file with the
+ ngx_http_mp4_module might result in worker process memory disclosure
+ (CVE-2018-16845).
+
+ *) Feature: the "proxy_socket_keepalive", "fastcgi_socket_keepalive",
+ "grpc_socket_keepalive", "memcached_socket_keepalive",
+ "scgi_socket_keepalive", and "uwsgi_socket_keepalive" directives.
+
+ *) Bugfix: if nginx was built with OpenSSL 1.1.0 and used with OpenSSL
+ 1.1.1, the TLS 1.3 protocol was always enabled.
+
+ *) Bugfix: working with gRPC backends might result in excessive memory
+ consumption.
+
+
+Changes with nginx 1.15.5 02 Oct 2018
+
+ *) Bugfix: a segmentation fault might occur in a worker process when
+ using OpenSSL 1.1.0h or newer; the bug had appeared in 1.15.4.
+
+ *) Bugfix: of minor potential bugs.
+
+
+Changes with nginx 1.15.4 25 Sep 2018
+
+ *) Feature: now the "ssl_early_data" directive can be used with OpenSSL.
+
+ *) Bugfix: in the ngx_http_uwsgi_module.
+ Thanks to Chris Caputo.
+
+ *) Bugfix: connections with some gRPC backends might not be cached when
+ using the "keepalive" directive.
+
+ *) Bugfix: a socket leak might occur when using the "error_page"
+ directive to redirect early request processing errors, notably errors
+ with code 400.
+
+ *) Bugfix: the "return" directive did not change the response code when
+ returning errors if the request was redirected by the "error_page"
+ directive.
+
+ *) Bugfix: standard error pages and responses of the
+ ngx_http_autoindex_module module used the "bgcolor" attribute, and
+ might be displayed incorrectly when using custom color settings in
+ browsers.
+ Thanks to Nova DasSarma.
+
+ *) Change: the logging level of the "no suitable key share" and "no
+ suitable signature algorithm" SSL errors has been lowered from "crit"
+ to "info".
+
+
+Changes with nginx 1.15.3 28 Aug 2018
+
+ *) Feature: now TLSv1.3 can be used with BoringSSL.
+
+ *) Feature: the "ssl_early_data" directive, currently available with
+ BoringSSL.
+
+ *) Feature: the "keepalive_timeout" and "keepalive_requests" directives
+ in the "upstream" block.
+
+ *) Bugfix: the ngx_http_dav_module did not truncate destination file
+ when copying a file over an existing one with the COPY method.
+
+ *) Bugfix: the ngx_http_dav_module used zero access rights on the
+ destination file and did not preserve file modification time when
+ moving a file between different file systems with the MOVE method.
+
+ *) Bugfix: the ngx_http_dav_module used default access rights when
+ copying a file with the COPY method.
+
+ *) Workaround: some clients might not work when using HTTP/2; the bug
+ had appeared in 1.13.5.
+
+ *) Bugfix: nginx could not be built with LibreSSL 2.8.0.
+
+
+Changes with nginx 1.15.2 24 Jul 2018
+
+ *) Feature: the $ssl_preread_protocol variable in the
+ ngx_stream_ssl_preread_module.
+
+ *) Feature: now when using the "reset_timedout_connection" directive
+ nginx will reset connections being closed with the 444 code.
+
+ *) Change: a logging level of the "http request", "https proxy request",
+ "unsupported protocol", and "version too low" SSL errors has been
+ lowered from "crit" to "info".
+
+ *) Bugfix: DNS requests were not resent if initial sending of a request
+ failed.
+
+ *) Bugfix: the "reuseport" parameter of the "listen" directive was
+ ignored if the number of worker processes was specified after the
+ "listen" directive.
+
+ *) Bugfix: when using OpenSSL 1.1.0 or newer it was not possible to
+ switch off "ssl_prefer_server_ciphers" in a virtual server if it was
+ switched on in the default server.
+
+ *) Bugfix: SSL session reuse with upstream servers did not work with the
+ TLS 1.3 protocol.
+
+
+Changes with nginx 1.15.1 03 Jul 2018
+
+ *) Feature: the "random" directive inside the "upstream" block.
+
+ *) Feature: improved performance when using the "hash" and "ip_hash"
+ directives with the "zone" directive.
+
+ *) Feature: the "reuseport" parameter of the "listen" directive now uses
+ SO_REUSEPORT_LB on FreeBSD 12.
+
+ *) Bugfix: HTTP/2 server push did not work if SSL was terminated by a
+ proxy server in front of nginx.
+
+ *) Bugfix: the "tcp_nopush" directive was always used on backend
+ connections.
+
+ *) Bugfix: sending a disk-buffered request body to a gRPC backend might
+ fail.
+
+
+Changes with nginx 1.15.0 05 Jun 2018
+
+ *) Change: the "ssl" directive is deprecated; the "ssl" parameter of the
+ "listen" directive should be used instead.
+
+ *) Change: now nginx detects missing SSL certificates during
+ configuration testing when using the "ssl" parameter of the "listen"
+ directive.
+
+ *) Feature: now the stream module can handle multiple incoming UDP
+ datagrams from a client within a single session.
+
+ *) Bugfix: it was possible to specify an incorrect response code in the
+ "proxy_cache_valid" directive.
+
+ *) Bugfix: nginx could not be built by gcc 8.1.
+
+ *) Bugfix: logging to syslog stopped on local IP address changes.
+
+ *) Bugfix: nginx could not be built by clang with CUDA SDK installed;
+ the bug had appeared in 1.13.8.
+
+ *) Bugfix: "getsockopt(TCP_FASTOPEN) ... failed" messages might appear
+ in logs during binary upgrade when using unix domain listen sockets
+ on FreeBSD.
+
+ *) Bugfix: nginx could not be built on Fedora 28 Linux.
+
+ *) Bugfix: request processing rate might exceed configured rate when
+ using the "limit_req" directive.
+
+ *) Bugfix: in handling of client addresses when using unix domain listen
+ sockets to work with datagrams on Linux.
+
+ *) Bugfix: in memory allocation error handling.
+
+
+Changes with nginx 1.13.12 10 Apr 2018
+
+ *) Bugfix: connections with gRPC backends might be closed unexpectedly
+ when returning a large response.
+
+
+Changes with nginx 1.13.11 03 Apr 2018
+
+ *) Feature: the "proxy_protocol" parameter of the "listen" directive now
+ supports the PROXY protocol version 2.
+
+ *) Bugfix: nginx could not be built with OpenSSL 1.1.1 statically on
+ Linux.
+
+ *) Bugfix: in the "http_404", "http_500", etc. parameters of the
+ "proxy_next_upstream" directive.
+
+
+Changes with nginx 1.13.10 20 Mar 2018
+
+ *) Feature: the "set" parameter of the "include" SSI directive now
+ allows writing arbitrary responses to a variable; the
+ "subrequest_output_buffer_size" directive defines maximum response
+ size.
+
+ *) Feature: now nginx uses clock_gettime(CLOCK_MONOTONIC) if available,
+ to avoid timeouts being incorrectly triggered on system time changes.
+
+ *) Feature: the "escape=none" parameter of the "log_format" directive.
+ Thanks to Johannes Baiter and Calin Don.
+
+ *) Feature: the $ssl_preread_alpn_protocols variable in the
+ ngx_stream_ssl_preread_module.
+
+ *) Feature: the ngx_http_grpc_module.
+
+ *) Bugfix: in memory allocation error handling in the "geo" directive.
+
+ *) Bugfix: when using variables in the "auth_basic_user_file" directive
+ a null character might appear in logs.
+ Thanks to Vadim Filimonov.
+
+
+Changes with nginx 1.13.9 20 Feb 2018
+
+ *) Feature: HTTP/2 server push support; the "http2_push" and
+ "http2_push_preload" directives.
+
+ *) Bugfix: "header already sent" alerts might appear in logs when using
+ cache; the bug had appeared in 1.9.13.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "ssl_verify_client" directive was used and no SSL certificate was
+ specified in a virtual server.
+
+ *) Bugfix: in the ngx_http_v2_module.
+
+ *) Bugfix: in the ngx_http_dav_module.
+
+
+Changes with nginx 1.13.8 26 Dec 2017
+
+ *) Feature: now nginx automatically preserves the CAP_NET_RAW capability
+ in worker processes when using the "transparent" parameter of the
+ "proxy_bind", "fastcgi_bind", "memcached_bind", "scgi_bind", and
+ "uwsgi_bind" directives.
+
+ *) Feature: improved CPU cache line size detection.
+ Thanks to Debayan Ghosh.
+
+ *) Feature: new directives in vim syntax highlighting scripts.
+ Thanks to Gena Makhomed.
+
+ *) Bugfix: binary upgrade refused to work if nginx was re-parented to a
+ process with PID different from 1 after its parent process has
+ finished.
+
+ *) Bugfix: the ngx_http_autoindex_module incorrectly handled requests
+ with bodies.
+
+ *) Bugfix: in the "proxy_limit_rate" directive when used with the
+ "keepalive" directive.
+
+ *) Bugfix: some parts of a response might be buffered when using
+ "proxy_buffering off" if the client connection used SSL.
+ Thanks to Patryk Lesiewicz.
+
+ *) Bugfix: in the "proxy_cache_background_update" directive.
+
+ *) Bugfix: it was not possible to start a parameter with a variable in
+ the "${name}" form with the name in curly brackets without enclosing
+ the parameter into single or double quotes.
+
+
+Changes with nginx 1.13.7 21 Nov 2017
+
+ *) Bugfix: in the $upstream_status variable.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if a
+ backend returned a "101 Switching Protocols" response to a
+ subrequest.
+
+ *) Bugfix: a segmentation fault occurred in a master process if a shared
+ memory zone size was changed during a reconfiguration and the
+ reconfiguration failed.
+
+ *) Bugfix: in the ngx_http_fastcgi_module.
+
+ *) Bugfix: nginx returned the 500 error if parameters without variables
+ were specified in the "xslt_stylesheet" directive.
+
+ *) Workaround: "gzip filter failed to use preallocated memory" alerts
+ appeared in logs when using a zlib library variant from Intel.
+
+ *) Bugfix: the "worker_shutdown_timeout" directive did not work when
+ using mail proxy and when proxying WebSocket connections.
+
+
+Changes with nginx 1.13.6 10 Oct 2017
+
+ *) Bugfix: switching to the next upstream server in the stream module
+ did not work when using the "ssl_preread" directive.
+
+ *) Bugfix: in the ngx_http_v2_module.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: nginx did not support dates after the year 2038 on 32-bit
+ platforms with 64-bit time_t.
+
+ *) Bugfix: in handling of dates prior to the year 1970 and after the
+ year 10000.
+
+ *) Bugfix: in the stream module timeouts waiting for UDP datagrams from
+ upstream servers were not logged or logged at the "info" level
+ instead of "error".
+
+ *) Bugfix: when using HTTP/2 nginx might return the 400 response without
+ logging the reason.
+
+ *) Bugfix: in processing of corrupted cache files.
+
+ *) Bugfix: cache control headers were ignored when caching errors
+ intercepted by error_page.
+
+ *) Bugfix: when using HTTP/2 client request body might be corrupted.
+
+ *) Bugfix: in handling of client addresses when using unix domain
+ sockets.
+
+ *) Bugfix: nginx hogged CPU when using the "hash ... consistent"
+ directive in the upstream block if large weights were used and all or
+ most of the servers were unavailable.
+
+
+Changes with nginx 1.13.5 05 Sep 2017
+
+ *) Feature: the $ssl_client_escaped_cert variable.
+
+ *) Bugfix: the "ssl_session_ticket_key" directive and the "include"
+ parameter of the "geo" directive did not work on Windows.
+
+ *) Bugfix: incorrect response length was returned on 32-bit platforms
+ when requesting more than 4 gigabytes with multiple ranges.
+
+ *) Bugfix: the "expires modified" directive and processing of the
+ "If-Range" request header line did not use the response last
+ modification time if proxying without caching was used.
+
+
+Changes with nginx 1.13.4 08 Aug 2017
+
+ *) Feature: the ngx_http_mirror_module.
+
+ *) Bugfix: client connections might be dropped during configuration
+ testing when using the "reuseport" parameter of the "listen"
+ directive on Linux.
+
+ *) Bugfix: request body might not be available in subrequests if it was
+ saved to a file and proxying was used.
+
+ *) Bugfix: cleaning cache based on the "max_size" parameter did not work
+ on Windows.
+
+ *) Bugfix: any shared memory allocation required 4096 bytes on Windows.
+
+ *) Bugfix: nginx worker might be terminated abnormally when using the
+ "zone" directive inside the "upstream" block on Windows.
+
+
+Changes with nginx 1.13.3 11 Jul 2017
+
+ *) Security: a specially crafted request might result in an integer
+ overflow and incorrect processing of ranges in the range filter,
+ potentially resulting in sensitive information leak (CVE-2017-7529).
+
+
+Changes with nginx 1.13.2 27 Jun 2017
+
+ *) Change: nginx now returns 200 instead of 416 when a range starting
+ with 0 is requested from an empty file.
+
+ *) Feature: the "add_trailer" directive.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: nginx could not be built on Cygwin and NetBSD; the bug had
+ appeared in 1.13.0.
+
+ *) Bugfix: nginx could not be built under MSYS2 / MinGW 64-bit.
+ Thanks to Orgad Shaneh.
+
+ *) Bugfix: a segmentation fault might occur in a worker process when
+ using SSI with many includes and proxy_pass with variables.
+
+ *) Bugfix: in the ngx_http_v2_module.
+ Thanks to Piotr Sikora.
+
+
+Changes with nginx 1.13.1 30 May 2017
+
+ *) Feature: now a hostname can be used as the "set_real_ip_from"
+ directive parameter.
+
+ *) Feature: vim syntax highlighting scripts improvements.
+
+ *) Feature: the "worker_cpu_affinity" directive now works on DragonFly
+ BSD.
+ Thanks to Sepherosa Ziehau.
+
+ *) Bugfix: SSL renegotiation on backend connections did not work when
+ using OpenSSL before 1.1.0.
+
+ *) Workaround: nginx could not be built with Oracle Developer Studio
+ 12.5.
+
+ *) Workaround: now cache manager ignores long locked cache entries when
+ cleaning cache based on the "max_size" parameter.
+
+ *) Bugfix: client SSL connections were immediately closed if deferred
+ accept and the "proxy_protocol" parameter of the "listen" directive
+ were used.
+
+ *) Bugfix: in the "proxy_cache_background_update" directive.
+
+ *) Workaround: now the "tcp_nodelay" directive sets the TCP_NODELAY
+ option before an SSL handshake.
+
+
+Changes with nginx 1.13.0 25 Apr 2017
+
+ *) Change: SSL renegotiation is now allowed on backend connections.
+
+ *) Feature: the "rcvbuf" and "sndbuf" parameters of the "listen"
+ directives of the mail proxy and stream modules.
+
+ *) Feature: the "return" and "error_page" directives can now be used to
+ return 308 redirections.
+ Thanks to Simon Leblanc.
+
+ *) Feature: the "TLSv1.3" parameter of the "ssl_protocols" directive.
+
+ *) Feature: when logging signals nginx now logs PID of the process which
+ sent the signal.
+
+ *) Bugfix: in memory allocation error handling.
+
+ *) Bugfix: if a server in the stream module listened on a wildcard
+ address, the source address of a response UDP datagram could differ
+ from the original datagram destination address.
+
+
+Changes with nginx 1.11.13 04 Apr 2017
+
+ *) Feature: the "http_429" parameter of the "proxy_next_upstream",
+ "fastcgi_next_upstream", "scgi_next_upstream", and
+ "uwsgi_next_upstream" directives.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: in memory allocation error handling.
+
+ *) Bugfix: requests might hang when using the "sendfile" and
+ "timer_resolution" directives on Linux.
+
+ *) Bugfix: requests might hang when using the "sendfile" and "aio_write"
+ directives with subrequests.
+
+ *) Bugfix: in the ngx_http_v2_module.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: a segmentation fault might occur in a worker process when
+ using HTTP/2.
+
+ *) Bugfix: requests might hang when using the "limit_rate",
+ "sendfile_max_chunk", "limit_req" directives, or the $r->sleep()
+ embedded perl method with subrequests.
+
+ *) Bugfix: in the ngx_http_slice_module.
+
+
+Changes with nginx 1.11.12 24 Mar 2017
+
+ *) Bugfix: nginx might hog CPU; the bug had appeared in 1.11.11.
+
+
+Changes with nginx 1.11.11 21 Mar 2017
+
+ *) Feature: the "worker_shutdown_timeout" directive.
+
+ *) Feature: vim syntax highlighting scripts improvements.
+ Thanks to Wei-Ko Kao.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ $limit_rate variable was set to an empty string.
+
+ *) Bugfix: the "proxy_cache_background_update",
+ "fastcgi_cache_background_update", "scgi_cache_background_update",
+ and "uwsgi_cache_background_update" directives might work incorrectly
+ if the "if" directive was used.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if
+ number of large_client_header_buffers in a virtual server was
+ different from the one in the default server.
+
+ *) Bugfix: in the mail proxy server.
+
+
+Changes with nginx 1.11.10 14 Feb 2017
+
+ *) Change: cache header format has been changed, previously cached
+ responses will be invalidated.
+
+ *) Feature: support of "stale-while-revalidate" and "stale-if-error"
+ extensions in the "Cache-Control" backend response header line.
+
+ *) Feature: the "proxy_cache_background_update",
+ "fastcgi_cache_background_update", "scgi_cache_background_update",
+ and "uwsgi_cache_background_update" directives.
+
+ *) Feature: nginx is now able to cache responses with the "Vary" header
+ line up to 128 characters long (instead of 42 characters in previous
+ versions).
+
+ *) Feature: the "build" parameter of the "server_tokens" directive.
+ Thanks to Tom Thorogood.
+
+ *) Bugfix: "[crit] SSL_write() failed" messages might appear in logs
+ when handling requests with the "Expect: 100-continue" request header
+ line.
+
+ *) Bugfix: the ngx_http_slice_module did not work in named locations.
+
+ *) Bugfix: a segmentation fault might occur in a worker process when
+ using AIO after an "X-Accel-Redirect" redirection.
+
+ *) Bugfix: reduced memory consumption for long-lived requests using
+ gzipping.
+
+
+Changes with nginx 1.11.9 24 Jan 2017
+
+ *) Bugfix: nginx might hog CPU when using the stream module; the bug had
+ appeared in 1.11.5.
+
+ *) Bugfix: EXTERNAL authentication mechanism in mail proxy was accepted
+ even if it was not enabled in the configuration.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "ssl_verify_client" directive of the stream module was used.
+
+ *) Bugfix: the "ssl_verify_client" directive of the stream module might
+ not work.
+
+ *) Bugfix: closing keepalive connections due to no free worker
+ connections might be too aggressive.
+ Thanks to Joel Cunningham.
+
+ *) Bugfix: an incorrect response might be returned when using the
+ "sendfile" directive on FreeBSD and macOS; the bug had appeared in
+ 1.7.8.
+
+ *) Bugfix: a truncated response might be stored in cache when using the
+ "aio_write" directive.
+
+ *) Bugfix: a socket leak might occur when using the "aio_write"
+ directive.
+
+
+Changes with nginx 1.11.8 27 Dec 2016
+
+ *) Feature: the "absolute_redirect" directive.
+
+ *) Feature: the "escape" parameter of the "log_format" directive.
+
+ *) Feature: client SSL certificates verification in the stream module.
+
+ *) Feature: the "ssl_session_ticket_key" directive supports AES256
+ encryption of TLS session tickets when used with 80-byte keys.
+
+ *) Feature: vim-commentary support in vim scripts.
+ Thanks to Armin Grodon.
+
+ *) Bugfix: recursion when evaluating variables was not limited.
+
+ *) Bugfix: in the ngx_stream_ssl_preread_module.
+
+ *) Bugfix: if a server in an upstream in the stream module failed, it
+ was considered alive only when a test connection sent to it after
+ fail_timeout was closed; now a successfully established connection is
+ enough.
+
+ *) Bugfix: nginx/Windows could not be built with 64-bit Visual Studio.
+
+ *) Bugfix: nginx/Windows could not be built with OpenSSL 1.1.0.
+
+
+Changes with nginx 1.11.7 13 Dec 2016
+
+ *) Change: now in case of a client certificate verification error the
+ $ssl_client_verify variable contains a string with the failure
+ reason, for example, "FAILED:certificate has expired".
+
+ *) Feature: the $ssl_ciphers, $ssl_curves, $ssl_client_v_start,
+ $ssl_client_v_end, and $ssl_client_v_remain variables.
+
+ *) Feature: the "volatile" parameter of the "map" directive.
+
+ *) Bugfix: dependencies specified for a module were ignored while
+ building dynamic modules.
+
+ *) Bugfix: when using HTTP/2 and the "limit_req" or "auth_request"
+ directives client request body might be corrupted; the bug had
+ appeared in 1.11.0.
+
+ *) Bugfix: a segmentation fault might occur in a worker process when
+ using HTTP/2; the bug had appeared in 1.11.3.
+
+ *) Bugfix: in the ngx_http_mp4_module.
+ Thanks to Congcong Hu.
+
+ *) Bugfix: in the ngx_http_perl_module.
+
+
+Changes with nginx 1.11.6 15 Nov 2016
+
+ *) Change: format of the $ssl_client_s_dn and $ssl_client_i_dn variables
+ has been changed to follow RFC 2253 (RFC 4514); values in the old
+ format are available in the $ssl_client_s_dn_legacy and
+ $ssl_client_i_dn_legacy variables.
+
+ *) Change: when storing temporary files in a cache directory they will
+ be stored in the same subdirectories as corresponding cache files
+ instead of a separate subdirectory for temporary files.
+
+ *) Feature: EXTERNAL authentication mechanism support in mail proxy.
+ Thanks to Robert Norris.
+
+ *) Feature: WebP support in the ngx_http_image_filter_module.
+
+ *) Feature: variables support in the "proxy_method" directive.
+ Thanks to Dmitry Lazurkin.
+
+ *) Feature: the "http2_max_requests" directive in the
+ ngx_http_v2_module.
+
+ *) Feature: the "proxy_cache_max_range_offset",
+ "fastcgi_cache_max_range_offset", "scgi_cache_max_range_offset", and
+ "uwsgi_cache_max_range_offset" directives.
+
+ *) Bugfix: graceful shutdown of old worker processes might require
+ infinite time when using HTTP/2.
+
+ *) Bugfix: in the ngx_http_mp4_module.
+
+ *) Bugfix: "ignore long locked inactive cache entry" alerts might appear
+ in logs when proxying WebSocket connections with caching enabled.
+
+ *) Bugfix: nginx did not write anything to log and returned a response
+ with code 502 instead of 504 when a timeout occurred during an SSL
+ handshake to a backend.
+
+
+Changes with nginx 1.11.5 11 Oct 2016
+
+ *) Change: the --with-ipv6 configure option was removed, now IPv6
+ support is configured automatically.
+
+ *) Change: now if there are no available servers in an upstream, nginx
+ will not reset number of failures of all servers as it previously
+ did, but will wait for fail_timeout to expire.
+
+ *) Feature: the ngx_stream_ssl_preread_module.
+
+ *) Feature: the "server" directive in the "upstream" context supports
+ the "max_conns" parameter.
+
+ *) Feature: the --with-compat configure option.
+
+ *) Feature: "manager_files", "manager_threshold", and "manager_sleep"
+ parameters of the "proxy_cache_path", "fastcgi_cache_path",
+ "scgi_cache_path", and "uwsgi_cache_path" directives.
+
+ *) Bugfix: flags passed by the --with-ld-opt configure option were not
+ used while building perl module.
+
+ *) Bugfix: in the "add_after_body" directive when used with the
+ "sub_filter" directive.
+
+ *) Bugfix: in the $realip_remote_addr variable.
+
+ *) Bugfix: the "dav_access", "proxy_store_access",
+ "fastcgi_store_access", "scgi_store_access", and "uwsgi_store_access"
+ directives ignored permissions specified for user.
+
+ *) Bugfix: unix domain listen sockets might not be inherited during
+ binary upgrade on Linux.
+
+ *) Bugfix: nginx returned the 400 response on requests with the "-"
+ character in the HTTP method.
+
+
+Changes with nginx 1.11.4 13 Sep 2016
+
+ *) Feature: the $upstream_bytes_received variable.
+
+ *) Feature: the $bytes_received, $session_time, $protocol, $status,
+ $upstream_addr, $upstream_bytes_sent, $upstream_bytes_received,
+ $upstream_connect_time, $upstream_first_byte_time, and
+ $upstream_session_time variables in the stream module.
+
+ *) Feature: the ngx_stream_log_module.
+
+ *) Feature: the "proxy_protocol" parameter of the "listen" directive,
+ the $proxy_protocol_addr and $proxy_protocol_port variables in the
+ stream module.
+
+ *) Feature: the ngx_stream_realip_module.
+
+ *) Bugfix: nginx could not be built with the stream module and the
+ ngx_http_ssl_module, but without ngx_stream_ssl_module; the bug had
+ appeared in 1.11.3.
+
+ *) Feature: the IP_BIND_ADDRESS_NO_PORT socket option was not used; the
+ bug had appeared in 1.11.2.
+
+ *) Bugfix: in the "ranges" parameter of the "geo" directive.
+
+ *) Bugfix: an incorrect response might be returned when using the "aio
+ threads" and "sendfile" directives; the bug had appeared in 1.9.13.
+
+
+Changes with nginx 1.11.3 26 Jul 2016
+
+ *) Change: now the "accept_mutex" directive is turned off by default.
+
+ *) Feature: now nginx uses EPOLLEXCLUSIVE on Linux.
+
+ *) Feature: the ngx_stream_geo_module.
+
+ *) Feature: the ngx_stream_geoip_module.
+
+ *) Feature: the ngx_stream_split_clients_module.
+
+ *) Feature: variables support in the "proxy_pass" and "proxy_ssl_name"
+ directives in the stream module.
+
+ *) Bugfix: socket leak when using HTTP/2.
+
+ *) Bugfix: in configure tests.
+ Thanks to Piotr Sikora.
+
+
+Changes with nginx 1.11.2 05 Jul 2016
+
+ *) Change: now nginx always uses internal MD5 and SHA1 implementations;
+ the --with-md5 and --with-sha1 configure options were canceled.
+
+ *) Feature: variables support in the stream module.
+
+ *) Feature: the ngx_stream_map_module.
+
+ *) Feature: the ngx_stream_return_module.
+
+ *) Feature: a port can be specified in the "proxy_bind", "fastcgi_bind",
+ "memcached_bind", "scgi_bind", and "uwsgi_bind" directives.
+
+ *) Feature: now nginx uses the IP_BIND_ADDRESS_NO_PORT socket option
+ when available.
+
+ *) Bugfix: a segmentation fault might occur in a worker process when
+ using HTTP/2 and the "proxy_request_buffering" directive.
+
+ *) Bugfix: the "Content-Length" request header line was always added to
+ requests passed to backends, including requests without body, when
+ using HTTP/2.
+
+ *) Bugfix: "http request count is zero" alerts might appear in logs when
+ using HTTP/2.
+
+ *) Bugfix: unnecessary buffering might occur when using the "sub_filter"
+ directive; the issue had appeared in 1.9.4.
+
+
+Changes with nginx 1.11.1 31 May 2016
+
+ *) Security: a segmentation fault might occur in a worker process while
+ writing a specially crafted request body to a temporary file
+ (CVE-2016-4450); the bug had appeared in 1.3.9.
+
+
+Changes with nginx 1.11.0 24 May 2016
+
+ *) Feature: the "transparent" parameter of the "proxy_bind",
+ "fastcgi_bind", "memcached_bind", "scgi_bind", and "uwsgi_bind"
+ directives.
+
+ *) Feature: the $request_id variable.
+
+ *) Feature: the "map" directive supports combinations of multiple
+ variables as resulting values.
+
+ *) Feature: now nginx checks if EPOLLRDHUP events are supported by
+ kernel, and optimizes connection handling accordingly if the "epoll"
+ method is used.
+
+ *) Feature: the "ssl_certificate" and "ssl_certificate_key" directives
+ can be specified multiple times to load certificates of different
+ types (for example, RSA and ECDSA).
+
+ *) Feature: the "ssl_ecdh_curve" directive now allows specifying a list
+ of curves when using OpenSSL 1.0.2 or newer; by default a list built
+ into OpenSSL is used.
+
+ *) Change: to use DHE ciphers it is now required to specify parameters
+ using the "ssl_dhparam" directive.
+
+ *) Feature: the $proxy_protocol_port variable.
+
+ *) Feature: the $realip_remote_port variable in the
+ ngx_http_realip_module.
+
+ *) Feature: the ngx_http_realip_module is now able to set the client
+ port in addition to the address.
+
+ *) Change: the "421 Misdirected Request" response now used when
+ rejecting requests to a virtual server different from one negotiated
+ during an SSL handshake; this improves interoperability with some
+ HTTP/2 clients when using client certificates.
+
+ *) Change: HTTP/2 clients can now start sending request body
+ immediately; the "http2_body_preread_size" directive controls size of
+ the buffer used before nginx will start reading client request body.
+
+ *) Bugfix: cached error responses were not updated when using the
+ "proxy_cache_bypass" directive.
+
+
+Changes with nginx 1.9.15 19 Apr 2016
+
+ *) Bugfix: "recv() failed" errors might occur when using HHVM as a
+ FastCGI server.
+
+ *) Bugfix: when using HTTP/2 and the "limit_req" or "auth_request"
+ directives a timeout or a "client violated flow control" error might
+ occur while reading client request body; the bug had appeared in
+ 1.9.14.
+
+ *) Workaround: a response might not be shown by some browsers if HTTP/2
+ was used and client request body was not fully read; the bug had
+ appeared in 1.9.14.
+
+ *) Bugfix: connections might hang when using the "aio threads"
+ directive.
+ Thanks to Mindaugas Rasiukevicius.
+
+
+Changes with nginx 1.9.14 05 Apr 2016
+
+ *) Feature: OpenSSL 1.1.0 compatibility.
+
+ *) Feature: the "proxy_request_buffering", "fastcgi_request_buffering",
+ "scgi_request_buffering", and "uwsgi_request_buffering" directives
+ now work with HTTP/2.
+
+ *) Bugfix: "zero size buf in output" alerts might appear in logs when
+ using HTTP/2.
+
+ *) Bugfix: the "client_max_body_size" directive might work incorrectly
+ when using HTTP/2.
+
+ *) Bugfix: of minor bugs in logging.
+
+
+Changes with nginx 1.9.13 29 Mar 2016
+
+ *) Change: non-idempotent requests (POST, LOCK, PATCH) are no longer
+ passed to the next server by default if a request has been sent to a
+ backend; the "non_idempotent" parameter of the "proxy_next_upstream"
+ directive explicitly allows retrying such requests.
+
+ *) Feature: the ngx_http_perl_module can be built dynamically.
+
+ *) Feature: UDP support in the stream module.
+
+ *) Feature: the "aio_write" directive.
+
+ *) Feature: now cache manager monitors number of elements in caches and
+ tries to avoid cache keys zone overflows.
+
+ *) Bugfix: "task already active" and "second aio post" alerts might
+ appear in logs when using the "sendfile" and "aio" directives with
+ subrequests.
+
+ *) Bugfix: "zero size buf in output" alerts might appear in logs if
+ caching was used and a client closed a connection prematurely.
+
+ *) Bugfix: connections with clients might be closed needlessly if
+ caching was used.
+ Thanks to Justin Li.
+
+ *) Bugfix: nginx might hog CPU if the "sendfile" directive was used on
+ Linux or Solaris and a file being sent was changed during sending.
+
+ *) Bugfix: connections might hang when using the "sendfile" and "aio
+ threads" directives.
+
+ *) Bugfix: in the "proxy_pass", "fastcgi_pass", "scgi_pass", and
+ "uwsgi_pass" directives when using variables.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: in the ngx_http_sub_filter_module.
+
+ *) Bugfix: if an error occurred in a cached backend connection, the
+ request was passed to the next server regardless of the
+ proxy_next_upstream directive.
+
+ *) Bugfix: "CreateFile() failed" errors when creating temporary files on
+ Windows.
+
+
+Changes with nginx 1.9.12 24 Feb 2016
+
+ *) Feature: Huffman encoding of response headers in HTTP/2.
+ Thanks to Vlad Krasnov.
+
+ *) Feature: the "worker_cpu_affinity" directive now supports more than
+ 64 CPUs.
+
+ *) Bugfix: compatibility with 3rd party C++ modules; the bug had
+ appeared in 1.9.11.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: nginx could not be built statically with OpenSSL on Linux;
+ the bug had appeared in 1.9.11.
+
+ *) Bugfix: the "add_header ... always" directive with an empty value did
+ not delete "Last-Modified" and "ETag" header lines from error
+ responses.
+
+ *) Workaround: "called a function you should not call" and "shutdown
+ while in init" messages might appear in logs when using OpenSSL
+ 1.0.2f.
+
+ *) Bugfix: invalid headers might be logged incorrectly.
+
+ *) Bugfix: socket leak when using HTTP/2.
+
+ *) Bugfix: in the ngx_http_v2_module.
+
+
+Changes with nginx 1.9.11 09 Feb 2016
+
+ *) Feature: TCP support in resolver.
+
+ *) Feature: dynamic modules.
+
+ *) Bugfix: the $request_length variable did not include size of request
+ headers when using HTTP/2.
+
+ *) Bugfix: in the ngx_http_v2_module.
+
+
+Changes with nginx 1.9.10 26 Jan 2016
+
+ *) Security: invalid pointer dereference might occur during DNS server
+ response processing if the "resolver" directive was used, allowing an
+ attacker who is able to forge UDP packets from the DNS server to
+ cause segmentation fault in a worker process (CVE-2016-0742).
+
+ *) Security: use-after-free condition might occur during CNAME response
+ processing if the "resolver" directive was used, allowing an attacker
+ who is able to trigger name resolution to cause segmentation fault in
+ a worker process, or might have potential other impact
+ (CVE-2016-0746).
+
+ *) Security: CNAME resolution was insufficiently limited if the
+ "resolver" directive was used, allowing an attacker who is able to
+ trigger arbitrary name resolution to cause excessive resource
+ consumption in worker processes (CVE-2016-0747).
+
+ *) Feature: the "auto" parameter of the "worker_cpu_affinity" directive.
+
+ *) Bugfix: the "proxy_protocol" parameter of the "listen" directive did
+ not work with IPv6 listen sockets.
+
+ *) Bugfix: connections to upstream servers might be cached incorrectly
+ when using the "keepalive" directive.
+
+ *) Bugfix: proxying used the HTTP method of the original request after
+ an "X-Accel-Redirect" redirection.
+
+
+Changes with nginx 1.9.9 09 Dec 2015
+
+ *) Bugfix: proxying to unix domain sockets did not work when using
+ variables; the bug had appeared in 1.9.8.
+
+
+Changes with nginx 1.9.8 08 Dec 2015
+
+ *) Feature: pwritev() support.
+
+ *) Feature: the "include" directive inside the "upstream" block.
+
+ *) Feature: the ngx_http_slice_module.
+
+ *) Bugfix: a segmentation fault might occur in a worker process when
+ using LibreSSL; the bug had appeared in 1.9.6.
+
+ *) Bugfix: nginx could not be built on OS X in some cases.
+
+
+Changes with nginx 1.9.7 17 Nov 2015
+
+ *) Feature: the "nohostname" parameter of logging to syslog.
+
+ *) Feature: the "proxy_cache_convert_head" directive.
+
+ *) Feature: the $realip_remote_addr variable in the
+ ngx_http_realip_module.
+
+ *) Bugfix: the "expires" directive might not work when using variables.
+
+ *) Bugfix: a segmentation fault might occur in a worker process when
+ using HTTP/2; the bug had appeared in 1.9.6.
+
+ *) Bugfix: if nginx was built with the ngx_http_v2_module it was
+ possible to use the HTTP/2 protocol even if the "http2" parameter of
+ the "listen" directive was not specified.
+
+ *) Bugfix: in the ngx_http_v2_module.
+
+
+Changes with nginx 1.9.6 27 Oct 2015
+
+ *) Bugfix: a segmentation fault might occur in a worker process when
+ using HTTP/2.
+ Thanks to Piotr Sikora and Denis Andzakovic.
+
+ *) Bugfix: the $server_protocol variable was empty when using HTTP/2.
+
+ *) Bugfix: backend SSL connections in the stream module might be timed
+ out unexpectedly.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if
+ different ssl_session_cache settings were used in different virtual
+ servers.
+
+ *) Bugfix: nginx/Windows could not be built with MinGW gcc; the bug had
+ appeared in 1.9.4.
+ Thanks to Kouhei Sutou.
+
+ *) Bugfix: time was not updated when the timer_resolution directive was
+ used on Windows.
+
+ *) Miscellaneous minor fixes and improvements.
+ Thanks to Markus Linnala, Kurtis Nusbaum and Piotr Sikora.
+
+
+Changes with nginx 1.9.5 22 Sep 2015
+
+ *) Feature: the ngx_http_v2_module (replaces ngx_http_spdy_module).
+ Thanks to Dropbox and Automattic for sponsoring this work.
+
+ *) Change: now the "output_buffers" directive uses two buffers by
+ default.
+
+ *) Change: now nginx limits subrequests recursion, not simultaneous
+ subrequests.
+
+ *) Change: now nginx checks the whole cache key when returning a
+ response from cache.
+ Thanks to Gena Makhomed and Sergey Brester.
+
+ *) Bugfix: "header already sent" alerts might appear in logs when using
+ cache; the bug had appeared in 1.7.5.
+
+ *) Bugfix: "writev() failed (4: Interrupted system call)" errors might
+ appear in logs when using CephFS and the "timer_resolution" directive
+ on Linux.
+
+ *) Bugfix: in invalid configurations handling.
+ Thanks to Markus Linnala.
+
+ *) Bugfix: a segmentation fault occurred in a worker process if the
+ "sub_filter" directive was used at http level; the bug had appeared
+ in 1.9.4.
+
+
+Changes with nginx 1.9.4 18 Aug 2015
+
+ *) Change: the "proxy_downstream_buffer" and "proxy_upstream_buffer"
+ directives of the stream module are replaced with the
+ "proxy_buffer_size" directive.
+
+ *) Feature: the "tcp_nodelay" directive in the stream module.
+
+ *) Feature: multiple "sub_filter" directives can be used simultaneously.
+
+ *) Feature: variables support in the search string of the "sub_filter"
+ directive.
+
+ *) Workaround: configuration testing might fail under Linux OpenVZ.
+ Thanks to Gena Makhomed.
+
+ *) Bugfix: old worker processes might hog CPU after reconfiguration with
+ a large number of worker_connections.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "try_files" and "alias" directives were used inside a location given
+ by a regular expression; the bug had appeared in 1.7.1.
+
+ *) Bugfix: the "try_files" directive inside a nested location given by a
+ regular expression worked incorrectly if the "alias" directive was
+ used in the outer location.
+
+ *) Bugfix: in hash table initialization error handling.
+
+ *) Bugfix: nginx could not be built with Visual Studio 2015.
+
+
+Changes with nginx 1.9.3 14 Jul 2015
+
+ *) Change: duplicate "http", "mail", and "stream" blocks are now
+ disallowed.
+
+ *) Feature: connection limiting in the stream module.
+
+ *) Feature: data rate limiting in the stream module.
+
+ *) Bugfix: the "zone" directive inside the "upstream" block did not work
+ on Windows.
+
+ *) Bugfix: compatibility with LibreSSL in the stream module.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: in the "--builddir" configure parameter.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: the "ssl_stapling_file" directive did not work; the bug had
+ appeared in 1.9.2.
+ Thanks to Faidon Liambotis and Brandon Black.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "ssl_stapling" directive was used; the bug had appeared in 1.9.2.
+ Thanks to Matthew Baldwin.
+
+
+Changes with nginx 1.9.2 16 Jun 2015
+
+ *) Feature: the "backlog" parameter of the "listen" directives of the
+ mail proxy and stream modules.
+
+ *) Feature: the "allow" and "deny" directives in the stream module.
+
+ *) Feature: the "proxy_bind" directive in the stream module.
+
+ *) Feature: the "proxy_protocol" directive in the stream module.
+
+ *) Feature: the -T switch.
+
+ *) Feature: the REQUEST_SCHEME parameter added to the fastcgi.conf,
+ fastcgi_params, scgi_params, and uwsgi_params standard configuration
+ files.
+
+ *) Bugfix: the "reuseport" parameter of the "listen" directive of the
+ stream module did not work.
+
+ *) Bugfix: OCSP stapling might return an expired OCSP response in some
+ cases.
+
+
+Changes with nginx 1.9.1 26 May 2015
+
+ *) Change: now SSLv3 protocol is disabled by default.
+
+ *) Change: some long deprecated directives are not supported anymore.
+
+ *) Feature: the "reuseport" parameter of the "listen" directive.
+ Thanks to Yingqi Lu at Intel and Sepherosa Ziehau.
+
+ *) Feature: the $upstream_connect_time variable.
+
+ *) Bugfix: in the "hash" directive on big-endian platforms.
+
+ *) Bugfix: nginx might fail to start on some old Linux variants; the bug
+ had appeared in 1.7.11.
+
+ *) Bugfix: in IP address parsing.
+ Thanks to Sergey Polovko.
+
+
+Changes with nginx 1.9.0 28 Apr 2015
+
+ *) Change: obsolete aio and rtsig event methods have been removed.
+
+ *) Feature: the "zone" directive inside the "upstream" block.
+
+ *) Feature: the stream module.
+
+ *) Feature: byte ranges support in the ngx_http_memcached_module.
+ Thanks to Martin Mlynář.
+
+ *) Feature: shared memory can now be used on Windows versions with
+ address space layout randomization.
+ Thanks to Sergey Brester.
+
+ *) Feature: the "error_log" directive can now be used on mail and server
+ levels in mail proxy.
+
+ *) Bugfix: the "proxy_protocol" parameter of the "listen" directive did
+ not work if not specified in the first "listen" directive for a
+ listen socket.
+
+
+Changes with nginx 1.7.12 07 Apr 2015
+
+ *) Feature: now the "tcp_nodelay" directive works with backend SSL
+ connections.
+
+ *) Feature: now thread pools can be used to read cache file headers.
+
+ *) Bugfix: in the "proxy_request_buffering" directive.
+
+ *) Bugfix: a segmentation fault might occur in a worker process when
+ using thread pools on Linux.
+
+ *) Bugfix: in error handling when using the "ssl_stapling" directive.
+ Thanks to Filipe da Silva.
+
+ *) Bugfix: in the ngx_http_spdy_module.
+
+
+Changes with nginx 1.7.11 24 Mar 2015
+
+ *) Change: the "sendfile" parameter of the "aio" directive is
+ deprecated; now nginx automatically uses AIO to pre-load data for
+ sendfile if both "aio" and "sendfile" directives are used.
+
+ *) Feature: experimental thread pools support.
+
+ *) Feature: the "proxy_request_buffering", "fastcgi_request_buffering",
+ "scgi_request_buffering", and "uwsgi_request_buffering" directives.
+
+ *) Feature: request body filters experimental API.
+
+ *) Feature: client SSL certificates support in mail proxy.
+ Thanks to Sven Peter, Franck Levionnois, and Filipe Da Silva.
+
+ *) Feature: startup speedup when using the "hash ... consistent"
+ directive in the upstream block.
+ Thanks to Wai Keen Woon.
+
+ *) Feature: debug logging into a cyclic memory buffer.
+
+ *) Bugfix: in hash table handling.
+ Thanks to Chris West.
+
+ *) Bugfix: in the "proxy_cache_revalidate" directive.
+
+ *) Bugfix: SSL connections might hang if deferred accept or the
+ "proxy_protocol" parameter of the "listen" directive were used.
+ Thanks to James Hamlin.
+
+ *) Bugfix: the $upstream_response_time variable might contain a wrong
+ value if the "image_filter" directive was used.
+
+ *) Bugfix: in integer overflow handling.
+ Thanks to Régis Leroy.
+
+ *) Bugfix: it was not possible to enable SSLv3 with LibreSSL.
+
+ *) Bugfix: the "ignoring stale global SSL error ... called a function
+ you should not call" alerts appeared in logs when using LibreSSL.
+
+ *) Bugfix: certificates specified by the "ssl_client_certificate" and
+ "ssl_trusted_certificate" directives were inadvertently used to
+ automatically construct certificate chains.
+
+
+Changes with nginx 1.7.10 10 Feb 2015
+
+ *) Feature: the "use_temp_path" parameter of the "proxy_cache_path",
+ "fastcgi_cache_path", "scgi_cache_path", and "uwsgi_cache_path"
+ directives.
+
+ *) Feature: the $upstream_header_time variable.
+
+ *) Workaround: now on disk overflow nginx tries to write error logs once
+ a second only.
+
+ *) Bugfix: the "try_files" directive did not ignore normal files while
+ testing directories.
+ Thanks to Damien Tournoud.
+
+ *) Bugfix: alerts "sendfile() failed" if the "sendfile" directive was
+ used on OS X; the bug had appeared in 1.7.8.
+
+ *) Bugfix: alerts "sem_post() failed" might appear in logs.
+
+ *) Bugfix: nginx could not be built with musl libc.
+ Thanks to James Taylor.
+
+ *) Bugfix: nginx could not be built on Tru64 UNIX.
+ Thanks to Goetz T. Fischer.
+
+
+Changes with nginx 1.7.9 23 Dec 2014
+
+ *) Feature: variables support in the "proxy_cache", "fastcgi_cache",
+ "scgi_cache", and "uwsgi_cache" directives.
+
+ *) Feature: variables support in the "expires" directive.
+
+ *) Feature: loading of secret keys from hardware tokens with OpenSSL
+ engines.
+ Thanks to Dmitrii Pichulin.
+
+ *) Feature: the "autoindex_format" directive.
+
+ *) Bugfix: cache revalidation is now only used for responses with 200
+ and 206 status codes.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: the "TE" client request header line was passed to backends
+ while proxying.
+
+ *) Bugfix: the "proxy_pass", "fastcgi_pass", "scgi_pass", and
+ "uwsgi_pass" directives might not work correctly inside the "if" and
+ "limit_except" blocks.
+
+ *) Bugfix: the "proxy_store" directive with the "on" parameter was
+ ignored if the "proxy_store" directive with an explicitly specified
+ file path was used on a previous level.
+
+ *) Bugfix: nginx could not be built with BoringSSL.
+ Thanks to Lukas Tribus.
+
+
+Changes with nginx 1.7.8 02 Dec 2014
+
+ *) Change: now the "If-Modified-Since", "If-Range", etc. client request
+ header lines are passed to a backend while caching if nginx knows in
+ advance that the response will not be cached (e.g., when using
+ proxy_cache_min_uses).
+
+ *) Change: now after proxy_cache_lock_timeout nginx sends a request to a
+ backend with caching disabled; the new directives
+ "proxy_cache_lock_age", "fastcgi_cache_lock_age",
+ "scgi_cache_lock_age", and "uwsgi_cache_lock_age" specify a time
+ after which the lock will be released and another attempt to cache a
+ response will be made.
+
+ *) Change: the "log_format" directive can now be used only at http
+ level.
+
+ *) Feature: the "proxy_ssl_certificate", "proxy_ssl_certificate_key",
+ "proxy_ssl_password_file", "uwsgi_ssl_certificate",
+ "uwsgi_ssl_certificate_key", and "uwsgi_ssl_password_file"
+ directives.
+ Thanks to Piotr Sikora.
+
+ *) Feature: it is now possible to switch to a named location using
+ "X-Accel-Redirect".
+ Thanks to Toshikuni Fukaya.
+
+ *) Feature: now the "tcp_nodelay" directive works with SPDY connections.
+
+ *) Feature: new directives in vim syntax highliting scripts.
+ Thanks to Peter Wu.
+
+ *) Bugfix: nginx ignored the "s-maxage" value in the "Cache-Control"
+ backend response header line.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: in the ngx_http_spdy_module.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: in the "ssl_password_file" directive when using OpenSSL
+ 0.9.8zc, 1.0.0o, 1.0.1j.
+
+ *) Bugfix: alerts "header already sent" appeared in logs if the
+ "post_action" directive was used; the bug had appeared in 1.5.4.
+
+ *) Bugfix: alerts "the http output chain is empty" might appear in logs
+ if the "postpone_output 0" directive was used with SSI includes.
+
+ *) Bugfix: in the "proxy_cache_lock" directive with SSI subrequests.
+ Thanks to Yichun Zhang.
+
+
+Changes with nginx 1.7.7 28 Oct 2014
+
+ *) Change: now nginx takes into account the "Vary" header line in a
+ backend response while caching.
+
+ *) Feature: the "proxy_force_ranges", "fastcgi_force_ranges",
+ "scgi_force_ranges", and "uwsgi_force_ranges" directives.
+
+ *) Feature: the "proxy_limit_rate", "fastcgi_limit_rate",
+ "scgi_limit_rate", and "uwsgi_limit_rate" directives.
+
+ *) Feature: the "Vary" parameter of the "proxy_ignore_headers",
+ "fastcgi_ignore_headers", "scgi_ignore_headers", and
+ "uwsgi_ignore_headers" directives.
+
+ *) Bugfix: the last part of a response received from a backend with
+ unbufferred proxy might not be sent to a client if "gzip" or "gunzip"
+ directives were used.
+
+ *) Bugfix: in the "proxy_cache_revalidate" directive.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: in error handling.
+ Thanks to Yichun Zhang and Daniil Bondarev.
+
+ *) Bugfix: in the "proxy_next_upstream_tries" and
+ "proxy_next_upstream_timeout" directives.
+ Thanks to Feng Gu.
+
+ *) Bugfix: nginx/Windows could not be built with MinGW-w64 gcc.
+ Thanks to Kouhei Sutou.
+
+
+Changes with nginx 1.7.6 30 Sep 2014
+
+ *) Change: the deprecated "limit_zone" directive is not supported
+ anymore.
+
+ *) Feature: the "limit_conn_zone" and "limit_req_zone" directives now
+ can be used with combinations of multiple variables.
+
+ *) Bugfix: request body might be transmitted incorrectly when retrying a
+ FastCGI request to the next upstream server.
+
+ *) Bugfix: in logging to syslog.
+
+
+Changes with nginx 1.7.5 16 Sep 2014
+
+ *) Security: it was possible to reuse SSL sessions in unrelated contexts
+ if a shared SSL session cache or the same TLS session ticket key was
+ used for multiple "server" blocks (CVE-2014-3616).
+ Thanks to Antoine Delignat-Lavaud.
+
+ *) Change: now the "stub_status" directive does not require a parameter.
+
+ *) Feature: the "always" parameter of the "add_header" directive.
+
+ *) Feature: the "proxy_next_upstream_tries",
+ "proxy_next_upstream_timeout", "fastcgi_next_upstream_tries",
+ "fastcgi_next_upstream_timeout", "memcached_next_upstream_tries",
+ "memcached_next_upstream_timeout", "scgi_next_upstream_tries",
+ "scgi_next_upstream_timeout", "uwsgi_next_upstream_tries", and
+ "uwsgi_next_upstream_timeout" directives.
+
+ *) Bugfix: in the "if" parameter of the "access_log" directive.
+
+ *) Bugfix: in the ngx_http_perl_module.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: the "listen" directive of the mail proxy module did not allow
+ to specify more than two parameters.
+
+ *) Bugfix: the "sub_filter" directive did not work with a string to
+ replace consisting of a single character.
+
+ *) Bugfix: requests might hang if resolver was used and a timeout
+ occurred during a DNS request.
+
+ *) Bugfix: in the ngx_http_spdy_module when using with AIO.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "set" directive was used to change the "$http_...", "$sent_http_...",
+ or "$upstream_http_..." variables.
+
+ *) Bugfix: in memory allocation error handling.
+ Thanks to Markus Linnala and Feng Gu.
+
+
+Changes with nginx 1.7.4 05 Aug 2014
+
+ *) Security: pipelined commands were not discarded after STARTTLS
+ command in SMTP proxy (CVE-2014-3556); the bug had appeared in 1.5.6.
+ Thanks to Chris Boulton.
+
+ *) Change: URI escaping now uses uppercase hexadecimal digits.
+ Thanks to Piotr Sikora.
+
+ *) Feature: now nginx can be build with BoringSSL and LibreSSL.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: requests might hang if resolver was used and a DNS server
+ returned a malformed response; the bug had appeared in 1.5.8.
+
+ *) Bugfix: in the ngx_http_spdy_module.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: the $uri variable might contain garbage when returning errors
+ with code 400.
+ Thanks to Sergey Bobrov.
+
+ *) Bugfix: in error handling in the "proxy_store" directive and the
+ ngx_http_dav_module.
+ Thanks to Feng Gu.
+
+ *) Bugfix: a segmentation fault might occur if logging of errors to
+ syslog was used; the bug had appeared in 1.7.1.
+
+ *) Bugfix: the $geoip_latitude, $geoip_longitude, $geoip_dma_code, and
+ $geoip_area_code variables might not work.
+ Thanks to Yichun Zhang.
+
+ *) Bugfix: in memory allocation error handling.
+ Thanks to Tatsuhiko Kubo and Piotr Sikora.
+
+
+Changes with nginx 1.7.3 08 Jul 2014
+
+ *) Feature: weak entity tags are now preserved on response
+ modifications, and strong ones are changed to weak.
+
+ *) Feature: cache revalidation now uses If-None-Match header if
+ possible.
+
+ *) Feature: the "ssl_password_file" directive.
+
+ *) Bugfix: the If-None-Match request header line was ignored if there
+ was no Last-Modified header in a response returned from cache.
+
+ *) Bugfix: "peer closed connection in SSL handshake" messages were
+ logged at "info" level instead of "error" while connecting to
+ backends.
+
+ *) Bugfix: in the ngx_http_dav_module module in nginx/Windows.
+
+ *) Bugfix: SPDY connections might be closed prematurely if caching was
+ used.
+
+
+Changes with nginx 1.7.2 17 Jun 2014
+
+ *) Feature: the "hash" directive inside the "upstream" block.
+
+ *) Feature: defragmentation of free shared memory blocks.
+ Thanks to Wandenberg Peixoto and Yichun Zhang.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ default value of the "access_log" directive was used; the bug had
+ appeared in 1.7.0.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: trailing slash was mistakenly removed from the last parameter
+ of the "try_files" directive.
+
+ *) Bugfix: nginx could not be built on OS X in some cases.
+
+ *) Bugfix: in the ngx_http_spdy_module.
+
+
+Changes with nginx 1.7.1 27 May 2014
+
+ *) Feature: the "$upstream_cookie_..." variables.
+
+ *) Feature: the $ssl_client_fingerprint variable.
+
+ *) Feature: the "error_log" and "access_log" directives now support
+ logging to syslog.
+
+ *) Feature: the mail proxy now logs client port on connect.
+
+ *) Bugfix: memory leak if the "ssl_stapling" directive was used.
+ Thanks to Filipe da Silva.
+
+ *) Bugfix: the "alias" directive used inside a location given by a
+ regular expression worked incorrectly if the "if" or "limit_except"
+ directives were used.
+
+ *) Bugfix: the "charset" directive did not set a charset to encoded
+ backend responses.
+
+ *) Bugfix: a "proxy_pass" directive without URI part might use original
+ request after the $args variable was set.
+ Thanks to Yichun Zhang.
+
+ *) Bugfix: in the "none" parameter in the "smtp_auth" directive; the bug
+ had appeared in 1.5.6.
+ Thanks to Svyatoslav Nikolsky.
+
+ *) Bugfix: if sub_filter and SSI were used together, then responses
+ might be transferred incorrectly.
+
+ *) Bugfix: nginx could not be built with the --with-file-aio option on
+ Linux/aarch64.
+
+
+Changes with nginx 1.7.0 24 Apr 2014
+
+ *) Feature: backend SSL certificate verification.
+
+ *) Feature: support for SNI while working with SSL backends.
+
+ *) Feature: the $ssl_server_name variable.
+
+ *) Feature: the "if" parameter of the "access_log" directive.
+
+
+Changes with nginx 1.5.13 08 Apr 2014
+
+ *) Change: improved hash table handling; the default values of the
+ "variables_hash_max_size" and "types_hash_bucket_size" were changed
+ to 1024 and 64 respectively.
+
+ *) Feature: the ngx_http_mp4_module now supports the "end" argument.
+
+ *) Feature: byte ranges support in the ngx_http_mp4_module and while
+ saving responses to cache.
+
+ *) Bugfix: alerts "ngx_slab_alloc() failed: no memory" no longer logged
+ when using shared memory in the "ssl_session_cache" directive and in
+ the ngx_http_limit_req_module.
+
+ *) Bugfix: the "underscores_in_headers" directive did not allow
+ underscore as a first character of a header.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: cache manager might hog CPU on exit in nginx/Windows.
+
+ *) Bugfix: nginx/Windows terminated abnormally if the
+ "ssl_session_cache" directive was used with the "shared" parameter.
+
+ *) Bugfix: in the ngx_http_spdy_module.
+
+
+Changes with nginx 1.5.12 18 Mar 2014
+
+ *) Security: a heap memory buffer overflow might occur in a worker
+ process while handling a specially crafted request by
+ ngx_http_spdy_module, potentially resulting in arbitrary code
+ execution (CVE-2014-0133).
+ Thanks to Lucas Molas, researcher at Programa STIC, Fundación Dr.
+ Manuel Sadosky, Buenos Aires, Argentina.
+
+ *) Feature: the "proxy_protocol" parameters of the "listen" and
+ "real_ip_header" directives, the $proxy_protocol_addr variable.
+
+ *) Bugfix: in the "fastcgi_next_upstream" directive.
+ Thanks to Lucas Molas.
+
+
+Changes with nginx 1.5.11 04 Mar 2014
+
+ *) Security: memory corruption might occur in a worker process on 32-bit
+ platforms while handling a specially crafted request by
+ ngx_http_spdy_module, potentially resulting in arbitrary code
+ execution (CVE-2014-0088); the bug had appeared in 1.5.10.
+ Thanks to Lucas Molas, researcher at Programa STIC, Fundación Dr.
+ Manuel Sadosky, Buenos Aires, Argentina.
+
+ *) Feature: the $ssl_session_reused variable.
+
+ *) Bugfix: the "client_max_body_size" directive might not work when
+ reading a request body using chunked transfer encoding; the bug had
+ appeared in 1.3.9.
+ Thanks to Lucas Molas.
+
+ *) Bugfix: a segmentation fault might occur in a worker process when
+ proxying WebSocket connections.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ ngx_http_spdy_module was used on 32-bit platforms; the bug had
+ appeared in 1.5.10.
+
+ *) Bugfix: the $upstream_status variable might contain wrong data if the
+ "proxy_cache_use_stale" or "proxy_cache_revalidate" directives were
+ used.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if
+ errors with code 400 were redirected to a named location using the
+ "error_page" directive.
+
+ *) Bugfix: nginx/Windows could not be built with Visual Studio 2013.
+
+
+Changes with nginx 1.5.10 04 Feb 2014
+
+ *) Feature: the ngx_http_spdy_module now uses SPDY 3.1 protocol.
+ Thanks to Automattic and MaxCDN for sponsoring this work.
+
+ *) Feature: the ngx_http_mp4_module now skips tracks too short for a
+ seek requested.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ $ssl_session_id variable was used in logs; the bug had appeared in
+ 1.5.9.
+
+ *) Bugfix: the $date_local and $date_gmt variables used wrong format
+ outside of the ngx_http_ssi_filter_module.
+
+ *) Bugfix: client connections might be immediately closed if deferred
+ accept was used; the bug had appeared in 1.3.15.
+
+ *) Bugfix: alerts "getsockopt(TCP_FASTOPEN) ... failed" appeared in logs
+ during binary upgrade on Linux; the bug had appeared in 1.5.8.
+ Thanks to Piotr Sikora.
+
+
+Changes with nginx 1.5.9 22 Jan 2014
+
+ *) Change: now nginx expects escaped URIs in "X-Accel-Redirect" headers.
+
+ *) Feature: the "ssl_buffer_size" directive.
+
+ *) Feature: the "limit_rate" directive can now be used to rate limit
+ responses sent in SPDY connections.
+
+ *) Feature: the "spdy_chunk_size" directive.
+
+ *) Feature: the "ssl_session_tickets" directive.
+ Thanks to Dirkjan Bussink.
+
+ *) Bugfix: the $ssl_session_id variable contained full session
+ serialized instead of just a session id.
+ Thanks to Ivan Ristić.
+
+ *) Bugfix: nginx incorrectly handled escaped "?" character in the
+ "include" SSI command.
+
+ *) Bugfix: the ngx_http_dav_module did not unescape destination URI of
+ the COPY and MOVE methods.
+
+ *) Bugfix: resolver did not understand domain names with a trailing dot.
+ Thanks to Yichun Zhang.
+
+ *) Bugfix: alerts "zero size buf in output" might appear in logs while
+ proxying; the bug had appeared in 1.3.9.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ ngx_http_spdy_module was used.
+
+ *) Bugfix: proxied WebSocket connections might hang right after
+ handshake if the select, poll, or /dev/poll methods were used.
+
+ *) Bugfix: the "xclient" directive of the mail proxy module incorrectly
+ handled IPv6 client addresses.
+
+
+Changes with nginx 1.5.8 17 Dec 2013
+
+ *) Feature: IPv6 support in resolver.
+
+ *) Feature: the "listen" directive supports the "fastopen" parameter.
+ Thanks to Mathew Rodley.
+
+ *) Feature: SSL support in the ngx_http_uwsgi_module.
+ Thanks to Roberto De Ioris.
+
+ *) Feature: vim syntax highlighting scripts were added to contrib.
+ Thanks to Evan Miller.
+
+ *) Bugfix: a timeout might occur while reading client request body in an
+ SSL connection using chunked transfer encoding.
+
+ *) Bugfix: the "master_process" directive did not work correctly in
+ nginx/Windows.
+
+ *) Bugfix: the "setfib" parameter of the "listen" directive might not
+ work.
+
+ *) Bugfix: in the ngx_http_spdy_module.
+
+
+Changes with nginx 1.5.7 19 Nov 2013
+
+ *) Security: a character following an unescaped space in a request line
+ was handled incorrectly (CVE-2013-4547); the bug had appeared in
+ 0.8.41.
+ Thanks to Ivan Fratric of the Google Security Team.
+
+ *) Change: a logging level of auth_basic errors about no user/password
+ provided has been lowered from "error" to "info".
+
+ *) Feature: the "proxy_cache_revalidate", "fastcgi_cache_revalidate",
+ "scgi_cache_revalidate", and "uwsgi_cache_revalidate" directives.
+
+ *) Feature: the "ssl_session_ticket_key" directive.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: the directive "add_header Cache-Control ''" added a
+ "Cache-Control" response header line with an empty value.
+
+ *) Bugfix: the "satisfy any" directive might return 403 error instead of
+ 401 if auth_request and auth_basic directives were used.
+ Thanks to Jan Marc Hoffmann.
+
+ *) Bugfix: the "accept_filter" and "deferred" parameters of the "listen"
+ directive were ignored for listen sockets created during binary
+ upgrade.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: some data received from a backend with unbufferred proxy
+ might not be sent to a client immediately if "gzip" or "gunzip"
+ directives were used.
+ Thanks to Yichun Zhang.
+
+ *) Bugfix: in error handling in ngx_http_gunzip_filter_module.
+
+ *) Bugfix: responses might hang if the ngx_http_spdy_module was used
+ with the "auth_request" directive.
+
+ *) Bugfix: memory leak in nginx/Windows.
+
+
+Changes with nginx 1.5.6 01 Oct 2013
+
+ *) Feature: the "fastcgi_buffering" directive.
+
+ *) Feature: the "proxy_ssl_protocols" and "proxy_ssl_ciphers"
+ directives.
+ Thanks to Piotr Sikora.
+
+ *) Feature: optimization of SSL handshakes when using long certificate
+ chains.
+
+ *) Feature: the mail proxy supports SMTP pipelining.
+
+ *) Bugfix: in the ngx_http_auth_basic_module when using "$apr1$"
+ password encryption method.
+ Thanks to Markus Linnala.
+
+ *) Bugfix: in MacOSX, Cygwin, and nginx/Windows incorrect location might
+ be used to process a request if locations were given using characters
+ in different cases.
+
+ *) Bugfix: automatic redirect with appended trailing slash for proxied
+ locations might not work.
+
+ *) Bugfix: in the mail proxy server.
+
+ *) Bugfix: in the ngx_http_spdy_module.
+
+
+Changes with nginx 1.5.5 17 Sep 2013
+
+ *) Change: now nginx assumes HTTP/1.0 by default if it is not able to
+ detect protocol reliably.
+
+ *) Feature: the "disable_symlinks" directive now uses O_PATH on Linux.
+
+ *) Feature: now nginx uses EPOLLRDHUP events to detect premature
+ connection close by clients if the "epoll" method is used.
+
+ *) Bugfix: in the "valid_referers" directive if the "server_names"
+ parameter was used.
+
+ *) Bugfix: the $request_time variable did not work in nginx/Windows.
+
+ *) Bugfix: in the "image_filter" directive.
+ Thanks to Lanshun Zhou.
+
+ *) Bugfix: OpenSSL 1.0.1f compatibility.
+ Thanks to Piotr Sikora.
+
+
+Changes with nginx 1.5.4 27 Aug 2013
+
+ *) Change: the "js" extension MIME type has been changed to
+ "application/javascript"; default value of the "charset_types"
+ directive was changed accordingly.
+
+ *) Change: now the "image_filter" directive with the "size" parameter
+ returns responses with the "application/json" MIME type.
+
+ *) Feature: the ngx_http_auth_request_module.
+
+ *) Bugfix: a segmentation fault might occur on start or during
+ reconfiguration if the "try_files" directive was used with an empty
+ parameter.
+
+ *) Bugfix: memory leak if relative paths were specified using variables
+ in the "root" or "auth_basic_user_file" directives.
+
+ *) Bugfix: the "valid_referers" directive incorrectly executed regular
+ expressions if a "Referer" header started with "https://".
+ Thanks to Liangbin Li.
+
+ *) Bugfix: responses might hang if subrequests were used and an SSL
+ handshake error happened during subrequest processing.
+ Thanks to Aviram Cohen.
+
+ *) Bugfix: in the ngx_http_autoindex_module.
+
+ *) Bugfix: in the ngx_http_spdy_module.
+
+
+Changes with nginx 1.5.3 30 Jul 2013
+
+ *) Change in internal API: now u->length defaults to -1 if working with
+ backends in unbuffered mode.
+
+ *) Change: now after receiving an incomplete response from a backend
+ server nginx tries to send an available part of the response to a
+ client, and then closes client connection.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ ngx_http_spdy_module was used with the "client_body_in_file_only"
+ directive.
+
+ *) Bugfix: the "so_keepalive" parameter of the "listen" directive might
+ be handled incorrectly on DragonFlyBSD.
+ Thanks to Sepherosa Ziehau.
+
+ *) Bugfix: in the ngx_http_xslt_filter_module.
+
+ *) Bugfix: in the ngx_http_sub_filter_module.
+
+
+Changes with nginx 1.5.2 02 Jul 2013
+
+ *) Feature: now several "error_log" directives can be used.
+
+ *) Bugfix: the $r->header_in() embedded perl method did not return value
+ of the "Cookie" and "X-Forwarded-For" request header lines; the bug
+ had appeared in 1.3.14.
+
+ *) Bugfix: in the ngx_http_spdy_module.
+ Thanks to Jim Radford.
+
+ *) Bugfix: nginx could not be built on Linux with x32 ABI.
+ Thanks to Serguei Ivantsov.
+
+
+Changes with nginx 1.5.1 04 Jun 2013
+
+ *) Feature: the "ssi_last_modified", "sub_filter_last_modified", and
+ "xslt_last_modified" directives.
+ Thanks to Alexey Kolpakov.
+
+ *) Feature: the "http_403" parameter of the "proxy_next_upstream",
+ "fastcgi_next_upstream", "scgi_next_upstream", and
+ "uwsgi_next_upstream" directives.
+
+ *) Feature: the "allow" and "deny" directives now support unix domain
+ sockets.
+
+ *) Bugfix: nginx could not be built with the ngx_mail_ssl_module, but
+ without ngx_http_ssl_module; the bug had appeared in 1.3.14.
+
+ *) Bugfix: in the "proxy_set_body" directive.
+ Thanks to Lanshun Zhou.
+
+ *) Bugfix: in the "lingering_time" directive.
+ Thanks to Lanshun Zhou.
+
+ *) Bugfix: the "fail_timeout" parameter of the "server" directive in the
+ "upstream" context might not work if "max_fails" parameter was used;
+ the bug had appeared in 1.3.0.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "ssl_stapling" directive was used.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: in the mail proxy server.
+ Thanks to Filipe Da Silva.
+
+ *) Bugfix: nginx/Windows might stop accepting connections if several
+ worker processes were used.
+
+
+Changes with nginx 1.5.0 07 May 2013
+
+ *) Security: a stack-based buffer overflow might occur in a worker
+ process while handling a specially crafted request, potentially
+ resulting in arbitrary code execution (CVE-2013-2028); the bug had
+ appeared in 1.3.9.
+ Thanks to Greg MacManus, iSIGHT Partners Labs.
+
+
+Changes with nginx 1.4.0 24 Apr 2013
+
+ *) Bugfix: nginx could not be built with the ngx_http_perl_module if the
+ --with-openssl option was used; the bug had appeared in 1.3.16.
+
+ *) Bugfix: in a request body handling in the ngx_http_perl_module; the
+ bug had appeared in 1.3.9.
+
+
+Changes with nginx 1.3.16 16 Apr 2013
+
+ *) Bugfix: a segmentation fault might occur in a worker process if
+ subrequests were used; the bug had appeared in 1.3.9.
+
+ *) Bugfix: the "tcp_nodelay" directive caused an error if a WebSocket
+ connection was proxied into a unix domain socket.
+
+ *) Bugfix: the $upstream_response_length variable has an incorrect value
+ "0" if buffering was not used.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: in the eventport and /dev/poll methods.
+
+
+Changes with nginx 1.3.15 26 Mar 2013
+
+ *) Change: opening and closing a connection without sending any data in
+ it is no longer logged to access_log with error code 400.
+
+ *) Feature: the ngx_http_spdy_module.
+ Thanks to Automattic for sponsoring this work.
+
+ *) Feature: the "limit_req_status" and "limit_conn_status" directives.
+ Thanks to Nick Marden.
+
+ *) Feature: the "image_filter_interlace" directive.
+ Thanks to Ian Babrou.
+
+ *) Feature: $connections_waiting variable in the
+ ngx_http_stub_status_module.
+
+ *) Feature: the mail proxy module now supports IPv6 backends.
+
+ *) Bugfix: request body might be transmitted incorrectly when retrying a
+ request to the next upstream server; the bug had appeared in 1.3.9.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: in the "client_body_in_file_only" directive; the bug had
+ appeared in 1.3.9.
+
+ *) Bugfix: responses might hang if subrequests were used and a DNS error
+ happened during subrequest processing.
+ Thanks to Lanshun Zhou.
+
+ *) Bugfix: in backend usage accounting.
+
+
+Changes with nginx 1.3.14 05 Mar 2013
+
+ *) Feature: $connections_active, $connections_reading, and
+ $connections_writing variables in the ngx_http_stub_status_module.
+
+ *) Feature: support of WebSocket connections in the
+ ngx_http_uwsgi_module and ngx_http_scgi_module.
+
+ *) Bugfix: in virtual servers handling with SNI.
+
+ *) Bugfix: new sessions were not always stored if the "ssl_session_cache
+ shared" directive was used and there was no free space in shared
+ memory.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: multiple X-Forwarded-For headers were handled incorrectly.
+ Thanks to Neal Poole for sponsoring this work.
+
+ *) Bugfix: in the ngx_http_mp4_module.
+ Thanks to Gernot Vormayr.
+
+
+Changes with nginx 1.3.13 19 Feb 2013
+
+ *) Change: a compiler with name "cc" is now used by default.
+
+ *) Feature: support for proxying of WebSocket connections.
+ Thanks to Apcera and CloudBees for sponsoring this work.
+
+ *) Feature: the "auth_basic_user_file" directive supports "{SHA}"
+ password encryption method.
+ Thanks to Louis Opter.
+
+
+Changes with nginx 1.3.12 05 Feb 2013
+
+ *) Feature: variables support in the "proxy_bind", "fastcgi_bind",
+ "memcached_bind", "scgi_bind", and "uwsgi_bind" directives.
+
+ *) Feature: the $pipe, $request_length, $time_iso8601, and $time_local
+ variables can now be used not only in the "log_format" directive.
+ Thanks to Kiril Kalchev.
+
+ *) Feature: IPv6 support in the ngx_http_geoip_module.
+ Thanks to Gregor Kališnik.
+
+ *) Bugfix: in the "proxy_method" directive.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if
+ resolver was used with the poll method.
+
+ *) Bugfix: nginx might hog CPU during SSL handshake with a backend if
+ the select, poll, or /dev/poll methods were used.
+
+ *) Bugfix: the "[crit] SSL_write() failed (SSL:)" error.
+
+ *) Bugfix: in the "client_body_in_file_only" directive; the bug had
+ appeared in 1.3.9.
+
+ *) Bugfix: in the "fastcgi_keep_conn" directive.
+
+
+Changes with nginx 1.3.11 10 Jan 2013
+
+ *) Bugfix: a segmentation fault might occur if logging was used; the bug
+ had appeared in 1.3.10.
+
+ *) Bugfix: the "proxy_pass" directive did not work with IP addresses
+ without port specified; the bug had appeared in 1.3.10.
+
+ *) Bugfix: a segmentation fault occurred on start or during
+ reconfiguration if the "keepalive" directive was specified more than
+ once in a single upstream block.
+
+ *) Bugfix: parameter "default" of the "geo" directive did not set
+ default value for IPv6 addresses.
+
+
+Changes with nginx 1.3.10 25 Dec 2012
+
+ *) Change: domain names specified in configuration file are now resolved
+ to IPv6 addresses as well as IPv4 ones.
+
+ *) Change: now if the "include" directive with mask is used on Unix
+ systems, included files are sorted in alphabetical order.
+
+ *) Change: the "add_header" directive adds headers to 201 responses.
+
+ *) Feature: the "geo" directive now supports IPv6 addresses in CIDR
+ notation.
+
+ *) Feature: the "flush" and "gzip" parameters of the "access_log"
+ directive.
+
+ *) Feature: variables support in the "auth_basic" directive.
+
+ *) Bugfix: nginx could not be built with the ngx_http_perl_module in
+ some cases.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ ngx_http_xslt_module was used.
+
+ *) Bugfix: nginx could not be built on MacOSX in some cases.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: the "limit_rate" directive with high rates might result in
+ truncated responses on 32-bit platforms.
+ Thanks to Alexey Antropov.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "if" directive was used.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: a "100 Continue" response was issued with "413 Request Entity
+ Too Large" responses.
+
+ *) Bugfix: the "image_filter", "image_filter_jpeg_quality" and
+ "image_filter_sharpen" directives might be inherited incorrectly.
+ Thanks to Ian Babrou.
+
+ *) Bugfix: "crypt_r() failed" errors might appear if the "auth_basic"
+ directive was used on Linux.
+
+ *) Bugfix: in backup servers handling.
+ Thanks to Thomas Chen.
+
+ *) Bugfix: proxied HEAD requests might return incorrect response if the
+ "gzip" directive was used.
+
+
+Changes with nginx 1.3.9 27 Nov 2012
+
+ *) Feature: support for chunked transfer encoding while reading client
+ request body.
+
+ *) Feature: the $request_time and $msec variables can now be used not
+ only in the "log_format" directive.
+
+ *) Bugfix: cache manager and cache loader processes might not be able to
+ start if more than 512 listen sockets were used.
+
+ *) Bugfix: in the ngx_http_dav_module.
+
+
+Changes with nginx 1.3.8 30 Oct 2012
+
+ *) Feature: the "optional_no_ca" parameter of the "ssl_verify_client"
+ directive.
+ Thanks to Mike Kazantsev and Eric O'Connor.
+
+ *) Feature: the $bytes_sent, $connection, and $connection_requests
+ variables can now be used not only in the "log_format" directive.
+ Thanks to Benjamin Grössing.
+
+ *) Feature: the "auto" parameter of the "worker_processes" directive.
+
+ *) Bugfix: "cache file ... has md5 collision" alert.
+
+ *) Bugfix: in the ngx_http_gunzip_filter_module.
+
+ *) Bugfix: in the "ssl_stapling" directive.
+
+
+Changes with nginx 1.3.7 02 Oct 2012
+
+ *) Feature: OCSP stapling support.
+ Thanks to Comodo, DigiCert and GlobalSign for sponsoring this work.
+
+ *) Feature: the "ssl_trusted_certificate" directive.
+
+ *) Feature: resolver now randomly rotates addresses returned from cache.
+ Thanks to Anton Jouline.
+
+ *) Bugfix: OpenSSL 0.9.7 compatibility.
+
+
+Changes with nginx 1.3.6 12 Sep 2012
+
+ *) Feature: the ngx_http_gunzip_filter_module.
+
+ *) Feature: the "memcached_gzip_flag" directive.
+
+ *) Feature: the "always" parameter of the "gzip_static" directive.
+
+ *) Bugfix: in the "limit_req" directive; the bug had appeared in 1.1.14.
+ Thanks to Charles Chen.
+
+ *) Bugfix: nginx could not be built by gcc 4.7 with -O2 optimization if
+ the --with-ipv6 option was used.
+
+
+Changes with nginx 1.3.5 21 Aug 2012
+
+ *) Change: the ngx_http_mp4_module module no longer skips tracks in
+ formats other than H.264 and AAC.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "map" directive was used with variables as values.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "geo" directive was used with the "ranges" parameter but without the
+ "default" parameter; the bug had appeared in 0.8.43.
+ Thanks to Zhen Chen and Weibin Yao.
+
+ *) Bugfix: in the -p command-line parameter handling.
+
+ *) Bugfix: in the mail proxy server.
+
+ *) Bugfix: of minor potential bugs.
+ Thanks to Coverity.
+
+ *) Bugfix: nginx/Windows could not be built with Visual Studio 2005
+ Express.
+ Thanks to HAYASHI Kentaro.
+
+
+Changes with nginx 1.3.4 31 Jul 2012
+
+ *) Change: the "ipv6only" parameter is now turned on by default for
+ listening IPv6 sockets.
+
+ *) Feature: the Clang compiler support.
+
+ *) Bugfix: extra listening sockets might be created.
+ Thanks to Roman Odaisky.
+
+ *) Bugfix: nginx/Windows might hog CPU if a worker process failed to
+ start.
+ Thanks to Ricardo Villalobos Guevara.
+
+ *) Bugfix: the "proxy_pass_header", "fastcgi_pass_header",
+ "scgi_pass_header", "uwsgi_pass_header", "proxy_hide_header",
+ "fastcgi_hide_header", "scgi_hide_header", and "uwsgi_hide_header"
+ directives might be inherited incorrectly.
+
+
+Changes with nginx 1.3.3 10 Jul 2012
+
+ *) Feature: entity tags support and the "etag" directive.
+
+ *) Bugfix: trailing dot in a source value was not ignored if the "map"
+ directive was used with the "hostnames" parameter.
+
+ *) Bugfix: incorrect location might be used to process a request if a
+ URI was changed via a "rewrite" directive before an internal redirect
+ to a named location.
+
+
+Changes with nginx 1.3.2 26 Jun 2012
+
+ *) Change: the "single" parameter of the "keepalive" directive is now
+ ignored.
+
+ *) Change: SSL compression is now disabled when using all versions of
+ OpenSSL, including ones prior to 1.0.0.
+
+ *) Feature: it is now possible to use the "ip_hash" directive to balance
+ IPv6 clients.
+
+ *) Feature: the $status variable can now be used not only in the
+ "log_format" directive.
+
+ *) Bugfix: a segmentation fault might occur in a worker process on
+ shutdown if the "resolver" directive was used.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ ngx_http_mp4_module was used.
+
+ *) Bugfix: in the ngx_http_mp4_module.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if
+ conflicting wildcard server names were used.
+
+ *) Bugfix: nginx might be terminated abnormally on a SIGBUS signal on
+ ARM platform.
+
+ *) Bugfix: an alert "sendmsg() failed (9: Bad file number)" on HP-UX
+ while reconfiguration.
+
+
+Changes with nginx 1.3.1 05 Jun 2012
+
+ *) Security: now nginx/Windows ignores trailing dot in URI path
+ component, and does not allow URIs with ":$" in it.
+ Thanks to Vladimir Kochetkov, Positive Research Center.
+
+ *) Feature: the "proxy_pass", "fastcgi_pass", "scgi_pass", "uwsgi_pass"
+ directives, and the "server" directive inside the "upstream" block,
+ now support IPv6 addresses.
+
+ *) Feature: the "resolver" directive now supports IPv6 addresses and an
+ optional port specification.
+
+ *) Feature: the "least_conn" directive inside the "upstream" block.
+
+ *) Feature: it is now possible to specify a weight for servers while
+ using the "ip_hash" directive.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "image_filter" directive was used; the bug had appeared in 1.3.0.
+
+ *) Bugfix: nginx could not be built with ngx_cpp_test_module; the bug
+ had appeared in 1.1.12.
+
+ *) Bugfix: access to variables from SSI and embedded perl module might
+ not work after reconfiguration.
+ Thanks to Yichun Zhang.
+
+ *) Bugfix: in the ngx_http_xslt_filter_module.
+ Thanks to Kuramoto Eiji.
+
+ *) Bugfix: memory leak if $geoip_org variable was used.
+ Thanks to Denis F. Latypoff.
+
+ *) Bugfix: in the "proxy_cookie_domain" and "proxy_cookie_path"
+ directives.
+
+
+Changes with nginx 1.3.0 15 May 2012
+
+ *) Feature: the "debug_connection" directive now supports IPv6 addresses
+ and the "unix:" parameter.
+
+ *) Feature: the "set_real_ip_from" directive and the "proxy" parameter
+ of the "geo" directive now support IPv6 addresses.
+
+ *) Feature: the "real_ip_recursive", "geoip_proxy", and
+ "geoip_proxy_recursive" directives.
+
+ *) Feature: the "proxy_recursive" parameter of the "geo" directive.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "resolver" directive was used.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "fastcgi_pass", "scgi_pass", or "uwsgi_pass" directives were used and
+ backend returned incorrect response.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "rewrite" directive was used and new request arguments in a
+ replacement used variables.
+
+ *) Bugfix: nginx might hog CPU if the open file resource limit was
+ reached.
+
+ *) Bugfix: nginx might loop infinitely over backends if the
+ "proxy_next_upstream" directive with the "http_404" parameter was
+ used and there were backup servers specified in an upstream block.
+
+ *) Bugfix: adding the "down" parameter of the "server" directive might
+ cause unneeded client redistribution among backend servers if the
+ "ip_hash" directive was used.
+
+ *) Bugfix: socket leak.
+ Thanks to Yichun Zhang.
+
+ *) Bugfix: in the ngx_http_fastcgi_module.
+
+
+Changes with nginx 1.2.0 23 Apr 2012
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "try_files" directive was used; the bug had appeared in 1.1.19.
+
+ *) Bugfix: response might be truncated if there were more than IOV_MAX
+ buffers used.
+
+ *) Bugfix: in the "crop" parameter of the "image_filter" directive.
+ Thanks to Maxim Bublis.
+
+
+Changes with nginx 1.1.19 12 Apr 2012
+
+ *) Security: specially crafted mp4 file might allow to overwrite memory
+ locations in a worker process if the ngx_http_mp4_module was used,
+ potentially resulting in arbitrary code execution (CVE-2012-2089).
+ Thanks to Matthew Daley.
+
+ *) Bugfix: nginx/Windows might be terminated abnormally.
+ Thanks to Vincent Lee.
+
+ *) Bugfix: nginx hogged CPU if all servers in an upstream were marked as
+ "backup".
+
+ *) Bugfix: the "allow" and "deny" directives might be inherited
+ incorrectly if they were used with IPv6 addresses.
+
+ *) Bugfix: the "modern_browser" and "ancient_browser" directives might
+ be inherited incorrectly.
+
+ *) Bugfix: timeouts might be handled incorrectly on Solaris/SPARC.
+
+ *) Bugfix: in the ngx_http_mp4_module.
+
+
+Changes with nginx 1.1.18 28 Mar 2012
+
+ *) Change: keepalive connections are no longer disabled for Safari by
+ default.
+
+ *) Feature: the $connection_requests variable.
+
+ *) Feature: $tcpinfo_rtt, $tcpinfo_rttvar, $tcpinfo_snd_cwnd and
+ $tcpinfo_rcv_space variables.
+
+ *) Feature: the "worker_cpu_affinity" directive now works on FreeBSD.
+
+ *) Feature: the "xslt_param" and "xslt_string_param" directives.
+ Thanks to Samuel Behan.
+
+ *) Bugfix: in configure tests.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: in the ngx_http_xslt_filter_module.
+
+ *) Bugfix: nginx could not be built on Debian GNU/Hurd.
+
+
+Changes with nginx 1.1.17 15 Mar 2012
+
+ *) Security: content of previously freed memory might be sent to a
+ client if backend returned specially crafted response.
+ Thanks to Matthew Daley.
+
+ *) Bugfix: in the embedded perl module if used from SSI.
+ Thanks to Matthew Daley.
+
+ *) Bugfix: in the ngx_http_uwsgi_module.
+
+
+Changes with nginx 1.1.16 29 Feb 2012
+
+ *) Change: the simultaneous subrequest limit has been raised to 200.
+
+ *) Feature: the "from" parameter of the "disable_symlinks" directive.
+
+ *) Feature: the "return" and "error_page" directives can now be used to
+ return 307 redirections.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "resolver" directive was used and there was no "error_log" directive
+ specified at global level.
+ Thanks to Roman Arutyunyan.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "proxy_http_version 1.1" or "fastcgi_keep_conn on" directives were
+ used.
+
+ *) Bugfix: memory leaks.
+ Thanks to Lanshun Zhou.
+
+ *) Bugfix: in the "disable_symlinks" directive.
+
+ *) Bugfix: on ZFS filesystem disk cache size might be calculated
+ incorrectly; the bug had appeared in 1.0.1.
+
+ *) Bugfix: nginx could not be built by the icc 12.1 compiler.
+
+ *) Bugfix: nginx could not be built by gcc on Solaris; the bug had
+ appeared in 1.1.15.
+
+
+Changes with nginx 1.1.15 15 Feb 2012
+
+ *) Feature: the "disable_symlinks" directive.
+
+ *) Feature: the "proxy_cookie_domain" and "proxy_cookie_path"
+ directives.
+
+ *) Bugfix: nginx might log incorrect error "upstream prematurely closed
+ connection" instead of correct "upstream sent too big header" one.
+ Thanks to Feibo Li.
+
+ *) Bugfix: nginx could not be built with the ngx_http_perl_module if the
+ --with-openssl option was used.
+
+ *) Bugfix: the number of internal redirects to named locations was not
+ limited.
+
+ *) Bugfix: calling $r->flush() multiple times might cause errors in the
+ ngx_http_gzip_filter_module.
+
+ *) Bugfix: temporary files might be not removed if the "proxy_store"
+ directive was used with SSI includes.
+
+ *) Bugfix: in some cases non-cacheable variables (such as the $args
+ variable) returned old empty cached value.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if too
+ many SSI subrequests were issued simultaneously; the bug had appeared
+ in 0.7.25.
+
+
+Changes with nginx 1.1.14 30 Jan 2012
+
+ *) Feature: multiple "limit_req" limits may be used simultaneously.
+
+ *) Bugfix: in error handling while connecting to a backend.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: in AIO error handling on FreeBSD.
+
+ *) Bugfix: in the OpenSSL library initialization.
+
+ *) Bugfix: the "proxy_redirect" directives might be inherited
+ incorrectly.
+
+ *) Bugfix: memory leak during reconfiguration if the "pcre_jit"
+ directive was used.
+
+
+Changes with nginx 1.1.13 16 Jan 2012
+
+ *) Feature: the "TLSv1.1" and "TLSv1.2" parameters of the
+ "ssl_protocols" directive.
+
+ *) Bugfix: the "limit_req" directive parameters were not inherited
+ correctly; the bug had appeared in 1.1.12.
+
+ *) Bugfix: the "proxy_redirect" directive incorrectly processed
+ "Refresh" header if regular expression were used.
+
+ *) Bugfix: the "proxy_cache_use_stale" directive with "error" parameter
+ did not return answer from cache if there were no live upstreams.
+
+ *) Bugfix: the "worker_cpu_affinity" directive might not work.
+
+ *) Bugfix: nginx could not be built on Solaris; the bug had appeared in
+ 1.1.12.
+
+ *) Bugfix: in the ngx_http_mp4_module.
+
+
+Changes with nginx 1.1.12 26 Dec 2011
+
+ *) Change: a "proxy_pass" directive without URI part now uses changed
+ URI after redirection with the "error_page" directive.
+ Thanks to Lanshun Zhou.
+
+ *) Feature: the "proxy/fastcgi/scgi/uwsgi_cache_lock",
+ "proxy/fastcgi/scgi/uwsgi_cache_lock_timeout" directives.
+
+ *) Feature: the "pcre_jit" directive.
+
+ *) Feature: the "if" SSI command supports captures in regular
+ expressions.
+
+ *) Bugfix: the "if" SSI command did not work inside the "block" command.
+
+ *) Bugfix: the "limit_conn_log_level" and "limit_req_log_level"
+ directives might not work.
+
+ *) Bugfix: the "limit_rate" directive did not allow to use full
+ throughput, even if limit value was very high.
+
+ *) Bugfix: the "sendfile_max_chunk" directive did not work, if the
+ "limit_rate" directive was used.
+
+ *) Bugfix: a "proxy_pass" directive without URI part always used
+ original request URI if variables were used.
+
+ *) Bugfix: a "proxy_pass" directive without URI part might use original
+ request after redirection with the "try_files" directive.
+ Thanks to Lanshun Zhou.
+
+ *) Bugfix: in the ngx_http_scgi_module.
+
+ *) Bugfix: in the ngx_http_mp4_module.
+
+ *) Bugfix: nginx could not be built on Solaris; the bug had appeared in
+ 1.1.9.
+
+
+Changes with nginx 1.1.11 12 Dec 2011
+
+ *) Feature: the "so_keepalive" parameter of the "listen" directive.
+ Thanks to Vsevolod Stakhov.
+
+ *) Feature: the "if_not_empty" parameter of the
+ "fastcgi/scgi/uwsgi_param" directives.
+
+ *) Feature: the $https variable.
+
+ *) Feature: the "proxy_redirect" directive supports variables in the
+ first parameter.
+
+ *) Feature: the "proxy_redirect" directive supports regular expressions.
+
+ *) Bugfix: the $sent_http_cache_control variable might contain a wrong
+ value if the "expires" directive was used.
+ Thanks to Yichun Zhang.
+
+ *) Bugfix: the "read_ahead" directive might not work combined with
+ "try_files" and "open_file_cache".
+
+ *) Bugfix: a segmentation fault might occur in a worker process if small
+ time was used in the "inactive" parameter of the "proxy_cache_path"
+ directive.
+
+ *) Bugfix: responses from cache might hang.
+
+
+Changes with nginx 1.1.10 30 Nov 2011
+
+ *) Bugfix: a segmentation fault occurred in a worker process if AIO was
+ used on Linux; the bug had appeared in 1.1.9.
+
+
+Changes with nginx 1.1.9 28 Nov 2011
+
+ *) Change: now double quotes are encoded in an "echo" SSI-command
+ output.
+ Thanks to Zaur Abasmirzoev.
+
+ *) Feature: the "valid" parameter of the "resolver" directive. By
+ default TTL returned by a DNS server is used.
+ Thanks to Kirill A. Korinskiy.
+
+ *) Bugfix: nginx might hang after a worker process abnormal termination.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if SNI
+ was used; the bug had appeared in 1.1.2.
+
+ *) Bugfix: in the "keepalive_disable" directive; the bug had appeared in
+ 1.1.8.
+ Thanks to Alexander Usov.
+
+ *) Bugfix: SIGWINCH signal did not work after first binary upgrade; the
+ bug had appeared in 1.1.1.
+
+ *) Bugfix: backend responses with length not matching "Content-Length"
+ header line are no longer cached.
+
+ *) Bugfix: in the "scgi_param" directive, if complex parameters were
+ used.
+
+ *) Bugfix: in the "epoll" event method.
+ Thanks to Yichun Zhang.
+
+ *) Bugfix: in the ngx_http_flv_module.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: in the ngx_http_mp4_module.
+
+ *) Bugfix: IPv6 addresses are now handled properly in a request line and
+ in a "Host" request header line.
+
+ *) Bugfix: "add_header" and "expires" directives did not work if a
+ request was proxied and response status code was 206.
+
+ *) Bugfix: nginx could not be built on FreeBSD 10.
+
+ *) Bugfix: nginx could not be built on AIX.
+
+
+Changes with nginx 1.1.8 14 Nov 2011
+
+ *) Change: the ngx_http_limit_zone_module was renamed to the
+ ngx_http_limit_conn_module.
+
+ *) Change: the "limit_zone" directive was superseded by the
+ "limit_conn_zone" directive with a new syntax.
+
+ *) Feature: support for multiple "limit_conn" limits on the same level.
+
+ *) Feature: the "image_filter_sharpen" directive.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if
+ resolver got a big DNS response.
+ Thanks to Ben Hawkes.
+
+ *) Bugfix: in cache key calculation if internal MD5 implementation was
+ used; the bug had appeared in 1.0.4.
+
+ *) Bugfix: the "If-Modified-Since", "If-Range", etc. client request
+ header lines might be passed to backend while caching; or not passed
+ without caching if caching was enabled in another part of the
+ configuration.
+
+ *) Bugfix: the module ngx_http_mp4_module sent incorrect
+ "Content-Length" response header line if the "start" argument was
+ used.
+ Thanks to Piotr Sikora.
+
+
+Changes with nginx 1.1.7 31 Oct 2011
+
+ *) Feature: support of several DNS servers in the "resolver" directive.
+ Thanks to Kirill A. Korinskiy.
+
+ *) Bugfix: a segmentation fault occurred on start or during
+ reconfiguration if the "ssl" directive was used at http level and
+ there was no "ssl_certificate" defined.
+
+ *) Bugfix: reduced memory consumption while proxying big files if they
+ were buffered to disk.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if
+ "proxy_http_version 1.1" directive was used.
+
+ *) Bugfix: in the "expires @time" directive.
+
+
+Changes with nginx 1.1.6 17 Oct 2011
+
+ *) Change in internal API: now module context data are cleared while
+ internal redirect to named location.
+ Requested by Yichun Zhang.
+
+ *) Change: if a server in an upstream failed, only one request will be
+ sent to it after fail_timeout; the server will be considered alive if
+ it will successfully respond to the request.
+
+ *) Change: now the 0x7F-0xFF characters are escaped as \xXX in an
+ access_log.
+
+ *) Feature: "proxy/fastcgi/scgi/uwsgi_ignore_headers" directives support
+ the following additional values: X-Accel-Limit-Rate,
+ X-Accel-Buffering, X-Accel-Charset.
+
+ *) Feature: decrease of memory consumption if SSL is used.
+
+ *) Bugfix: some UTF-8 characters were processed incorrectly.
+ Thanks to Alexey Kuts.
+
+ *) Bugfix: the ngx_http_rewrite_module directives specified at "server"
+ level were executed twice if no matching locations were defined.
+
+ *) Bugfix: a socket leak might occurred if "aio sendfile" was used.
+
+ *) Bugfix: connections with fast clients might be closed after
+ send_timeout if file AIO was used.
+
+ *) Bugfix: in the ngx_http_autoindex_module.
+
+ *) Bugfix: the module ngx_http_mp4_module did not support seeking on
+ 32-bit platforms.
+
+
+Changes with nginx 1.1.5 05 Oct 2011
+
+ *) Feature: the "uwsgi_buffering" and "scgi_buffering" directives.
+ Thanks to Peter Smit.
+
+ *) Bugfix: non-cacheable responses might be cached if
+ "proxy_cache_bypass" directive was used.
+ Thanks to John Ferlito.
+
+ *) Bugfix: in HTTP/1.1 support in the ngx_http_proxy_module.
+
+ *) Bugfix: cached responses with an empty body were returned
+ incorrectly; the bug had appeared in 0.8.31.
+
+ *) Bugfix: 201 responses of the ngx_http_dav_module were incorrect; the
+ bug had appeared in 0.8.32.
+
+ *) Bugfix: in the "return" directive.
+
+ *) Bugfix: the "ssl_session_cache builtin" directive caused segmentation
+ fault; the bug had appeared in 1.1.1.
+
+
+Changes with nginx 1.1.4 20 Sep 2011
+
+ *) Feature: the ngx_http_upstream_keepalive module.
+
+ *) Feature: the "proxy_http_version" directive.
+
+ *) Feature: the "fastcgi_keep_conn" directive.
+
+ *) Feature: the "worker_aio_requests" directive.
+
+ *) Bugfix: if nginx was built --with-file-aio it could not be run on
+ Linux kernel which did not support AIO.
+
+ *) Bugfix: in Linux AIO error processing.
+ Thanks to Hagai Avrahami.
+
+ *) Bugfix: reduced memory consumption for long-lived requests.
+
+ *) Bugfix: the module ngx_http_mp4_module did not support 64-bit MP4
+ "co64" atom.
+
+
+Changes with nginx 1.1.3 14 Sep 2011
+
+ *) Feature: the module ngx_http_mp4_module.
+
+ *) Bugfix: in Linux AIO combined with open_file_cache.
+
+ *) Bugfix: open_file_cache did not update file info on retest if file
+ was not atomically changed.
+
+ *) Bugfix: nginx could not be built on MacOSX 10.7.
+
+
+Changes with nginx 1.1.2 05 Sep 2011
+
+ *) Change: now if total size of all ranges is greater than source
+ response size, then nginx disables ranges and returns just the source
+ response.
+
+ *) Feature: the "max_ranges" directive.
+
+ *) Bugfix: the "ssl_verify_client", "ssl_verify_depth", and
+ "ssl_prefer_server_ciphers" directives might work incorrectly if SNI
+ was used.
+
+ *) Bugfix: in the "proxy/fastcgi/scgi/uwsgi_ignore_client_abort"
+ directives.
+
+
+Changes with nginx 1.1.1 22 Aug 2011
+
+ *) Change: now cache loader processes either as many files as specified
+ by "loader_files" parameter or works no longer than time specified by
+ the "loader_threshold" parameter during each iteration.
+
+ *) Change: now SIGWINCH signal works only in daemon mode.
+
+ *) Feature: now shared zones and caches use POSIX semaphores on Solaris.
+ Thanks to Den Ivanov.
+
+ *) Feature: accept filters are now supported on NetBSD.
+
+ *) Bugfix: nginx could not be built on Linux 3.0.
+
+ *) Bugfix: nginx did not use gzipping in some cases; the bug had
+ appeared in 1.1.0.
+
+ *) Bugfix: request body might be processed incorrectly if client used
+ pipelining.
+
+ *) Bugfix: in the "request_body_in_single_buf" directive.
+
+ *) Bugfix: in "proxy_set_body" and "proxy_pass_request_body" directives
+ if SSL connection to backend was used.
+
+ *) Bugfix: nginx hogged CPU if all servers in an upstream were marked as
+ "down".
+
+ *) Bugfix: a segmentation fault might occur during reconfiguration if
+ ssl_session_cache was defined but not used in previous configuration.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if many
+ backup servers were used in an upstream.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if
+ "fastcgi/scgi/uwsgi_param" directives were used with values starting
+ with "HTTP_"; the bug had appeared in 0.8.40.
+
+
+Changes with nginx 1.1.0 01 Aug 2011
+
+ *) Feature: cache loader run time decrease.
+
+ *) Feature: "loader_files", "loader_sleep", and "loader_threshold"
+ options of the "proxy/fastcgi/scgi/uwsgi_cache_path" directives.
+
+ *) Feature: loading time decrease of configuration with large number of
+ HTTPS sites.
+
+ *) Feature: now nginx supports ECDHE key exchange ciphers.
+ Thanks to Adrian Kotelba.
+
+ *) Feature: the "lingering_close" directive.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: in closing connection for pipelined requests.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: nginx did not disable gzipping if client sent "gzip;q=0" in
+ "Accept-Encoding" request header line.
+
+ *) Bugfix: in timeout in unbuffered proxied mode.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: memory leaks when a "proxy_pass" directive contains variables
+ and proxies to an HTTPS backend.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: in parameter validation of a "proxy_pass" directive with
+ variables.
+ Thanks to Lanshun Zhou.
+
+ *) Bugfix: SSL did not work on QNX.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: SSL modules could not be built by gcc 4.6 without
+ --with-debug option.
+
+
+Changes with nginx 1.0.5 19 Jul 2011
+
+ *) Change: now default SSL ciphers are "HIGH:!aNULL:!MD5".
+ Thanks to Rob Stradling.
+
+ *) Feature: the "referer_hash_max_size" and "referer_hash_bucket_size"
+ directives.
+ Thanks to Witold Filipczyk.
+
+ *) Feature: $uid_reset variable.
+
+ *) Bugfix: a segmentation fault might occur in a worker process, if a
+ caching was used.
+ Thanks to Lanshun Zhou.
+
+ *) Bugfix: worker processes may got caught in an endless loop during
+ reconfiguration, if a caching was used; the bug had appeared in
+ 0.8.48.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: "stalled cache updating" alert.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 1.0.4 01 Jun 2011
+
+ *) Change: now regular expressions case sensitivity in the "map"
+ directive is given by prefixes "~" or "~*".
+
+ *) Feature: now shared zones and caches use POSIX semaphores on Linux.
+ Thanks to Denis F. Latypoff.
+
+ *) Bugfix: "stalled cache updating" alert.
+
+ *) Bugfix: nginx could not be built --without-http_auth_basic_module;
+ the bug had appeared in 1.0.3.
+
+
+Changes with nginx 1.0.3 25 May 2011
+
+ *) Feature: the "auth_basic_user_file" directive supports "$apr1",
+ "{PLAIN}", and "{SSHA}" password encryption methods.
+ Thanks to Maxim Dounin.
+
+ *) Feature: the "geoip_org" directive and $geoip_org variable.
+ Thanks to Alexander Uskov, Arnaud Granal, and Denis F. Latypoff.
+
+ *) Feature: ngx_http_geo_module and ngx_http_geoip_module support IPv4
+ addresses mapped to IPv6 addresses.
+
+ *) Bugfix: a segmentation fault occurred in a worker process during
+ testing IPv4 address mapped to IPv6 address, if access or deny rules
+ were defined only for IPv6; the bug had appeared in 0.8.22.
+
+ *) Bugfix: a cached response may be broken if "proxy/fastcgi/scgi/
+ uwsgi_cache_bypass" and "proxy/fastcgi/scgi/uwsgi_no_cache" directive
+ values were different; the bug had appeared in 0.8.46.
+
+
+Changes with nginx 1.0.2 10 May 2011
+
+ *) Feature: now shared zones and caches use POSIX semaphores.
+
+ *) Bugfix: in the "rotate" parameter of the "image_filter" directive.
+ Thanks to Adam Bocim.
+
+ *) Bugfix: nginx could not be built on Solaris; the bug had appeared in
+ 1.0.1.
+
+
+Changes with nginx 1.0.1 03 May 2011
+
+ *) Change: now the "split_clients" directive uses MurmurHash2 algorithm
+ because of better distribution.
+ Thanks to Oleg Mamontov.
+
+ *) Change: now long strings starting with zero are not considered as
+ false values.
+ Thanks to Maxim Dounin.
+
+ *) Change: now nginx uses a default listen backlog value 511 on Linux.
+
+ *) Feature: the $upstream_... variables may be used in the SSI and perl
+ modules.
+
+ *) Bugfix: now nginx limits better disk cache size.
+ Thanks to Oleg Mamontov.
+
+ *) Bugfix: a segmentation fault might occur while parsing incorrect IPv4
+ address; the bug had appeared in 0.9.3.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: nginx could not be built by gcc 4.6 without --with-debug
+ option.
+
+ *) Bugfix: nginx could not be built on Solaris 9 and earlier; the bug
+ had appeared in 0.9.3.
+ Thanks to Dagobert Michelsen.
+
+ *) Bugfix: $request_time variable had invalid values if subrequests were
+ used; the bug had appeared in 0.8.47.
+ Thanks to Igor A. Valcov.
+
+
+Changes with nginx 1.0.0 12 Apr 2011
+
+ *) Bugfix: a cache manager might hog CPU after reload.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: an "image_filter crop" directive worked incorrectly coupled
+ with an "image_filter rotate 180" directive.
+
+ *) Bugfix: a "satisfy any" directive disabled custom 401 error page.
+
+
+Changes with nginx 0.9.7 04 Apr 2011
+
+ *) Feature: now keepalive connections may be closed premature, if there
+ are no free worker connections.
+ Thanks to Maxim Dounin.
+
+ *) Feature: the "rotate" parameter of the "image_filter" directive.
+ Thanks to Adam Bocim.
+
+ *) Bugfix: a case when a backend in "fastcgi_pass", "scgi_pass", or
+ "uwsgi_pass" directives is given by expression and refers to a
+ defined upstream.
+
+
+Changes with nginx 0.9.6 21 Mar 2011
+
+ *) Feature: the "map" directive supports regular expressions as value of
+ the first parameter.
+
+ *) Feature: $time_iso8601 access_log variable.
+ Thanks to Michael Lustfield.
+
+
+Changes with nginx 0.9.5 21 Feb 2011
+
+ *) Change: now nginx uses a default listen backlog value -1 on Linux.
+ Thanks to Andrei Nigmatulin.
+
+ *) Feature: the "utf8" parameter of "geoip_country" and "geoip_city"
+ directives.
+ Thanks to Denis F. Latypoff.
+
+ *) Bugfix: in a default "proxy_redirect" directive if "proxy_pass"
+ directive has no URI part.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: an "error_page" directive did not work with nonstandard error
+ codes; the bug had appeared in 0.8.53.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.9.4 21 Jan 2011
+
+ *) Feature: the "server_name" directive supports the $hostname variable.
+
+ *) Feature: 494 code for "Request Header Too Large" error.
+
+
+Changes with nginx 0.9.3 13 Dec 2010
+
+ *) Bugfix: if there was a single server for given IPv6 address:port
+ pair, then captures in regular expressions in a "server_name"
+ directive did not work.
+
+ *) Bugfix: nginx could not be built on Solaris; the bug had appeared in
+ 0.9.0.
+
+
+Changes with nginx 0.9.2 06 Dec 2010
+
+ *) Feature: the "If-Unmodified-Since" client request header line
+ support.
+
+ *) Workaround: fallback to accept() syscall if accept4() was not
+ implemented; the issue had appeared in 0.9.0.
+
+ *) Bugfix: nginx could not be built on Cygwin; the bug had appeared in
+ 0.9.0.
+
+ *) Bugfix: for OpenSSL vulnerability CVE-2010-4180.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.9.1 30 Nov 2010
+
+ *) Bugfix: "return CODE message" directives did not work; the bug had
+ appeared in 0.9.0.
+
+
+Changes with nginx 0.9.0 29 Nov 2010
+
+ *) Feature: the "keepalive_disable" directive.
+
+ *) Feature: the "map" directive supports variables as value of a defined
+ variable.
+
+ *) Feature: the "map" directive supports empty strings as value of the
+ first parameter.
+
+ *) Feature: the "map" directive supports expressions as the first
+ parameter.
+
+ *) Feature: nginx(8) manual page.
+ Thanks to Sergey Osokin.
+
+ *) Feature: Linux accept4() support.
+ Thanks to Simon Liu.
+
+ *) Workaround: elimination of Linux linker warning about "sys_errlist"
+ and "sys_nerr"; the warning had appeared in 0.8.35.
+
+ *) Bugfix: a segmentation fault might occur in a worker process, if the
+ "auth_basic" directive was used.
+ Thanks to Michail Laletin.
+
+ *) Bugfix: compatibility with ngx_http_eval_module; the bug had appeared
+ in 0.8.42.
+
+
+Changes with nginx 0.8.53 18 Oct 2010
+
+ *) Feature: now the "error_page" directive allows to change a status
+ code in a redirect.
+
+ *) Feature: the "gzip_disable" directive supports special "degradation"
+ mask.
+
+ *) Bugfix: a socket leak might occurred if file AIO was used.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: if the first server had no "listen" directive and there was
+ no explicit default server, then a next server with a "listen"
+ directive became the default server; the bug had appeared in 0.8.21.
+
+
+Changes with nginx 0.8.52 28 Sep 2010
+
+ *) Bugfix: nginx used SSL mode for a listen socket if any listen option
+ was set; the bug had appeared in 0.8.51.
+
+
+Changes with nginx 0.8.51 27 Sep 2010
+
+ *) Change: the "secure_link_expires" directive has been canceled.
+
+ *) Change: a logging level of resolver errors has been lowered from
+ "alert" to "error".
+
+ *) Feature: now a listen socket "ssl" parameter may be set several
+ times.
+
+
+Changes with nginx 0.8.50 02 Sep 2010
+
+ *) Feature: the "secure_link", "secure_link_md5", and
+ "secure_link_expires" directives of the ngx_http_secure_link_module.
+
+ *) Feature: the -q switch.
+ Thanks to Gena Makhomed.
+
+ *) Bugfix: worker processes may got caught in an endless loop during
+ reconfiguration, if a caching was used; the bug had appeared in
+ 0.8.48.
+
+ *) Bugfix: in the "gzip_disable" directive.
+ Thanks to Derrick Petzold.
+
+ *) Bugfix: nginx/Windows could not send stop, quit, reopen, and reload
+ signals to a process run in other session.
+
+
+Changes with nginx 0.8.49 09 Aug 2010
+
+ *) Feature: the "image_filter_jpeg_quality" directive supports
+ variables.
+
+ *) Bugfix: a segmentation fault might occur in a worker process, if the
+ $geoip_region_name variables was used; the bug had appeared in
+ 0.8.48.
+
+ *) Bugfix: errors intercepted by error_page were cached only for next
+ request; the bug had appeared in 0.8.48.
+
+
+Changes with nginx 0.8.48 03 Aug 2010
+
+ *) Change: now the "server_name" directive default value is an empty
+ name "".
+ Thanks to Gena Makhomed.
+
+ *) Change: now the "server_name_in_redirect" directive default value is
+ "off".
+
+ *) Feature: the $geoip_dma_code, $geoip_area_code, and
+ $geoip_region_name variables.
+ Thanks to Christine McGonagle.
+
+ *) Bugfix: the "proxy_pass", "fastcgi_pass", "uwsgi_pass", and
+ "scgi_pass" directives were not inherited inside "limit_except"
+ blocks.
+
+ *) Bugfix: the "proxy_cache_min_uses", "fastcgi_cache_min_uses"
+ "uwsgi_cache_min_uses", and "scgi_cache_min_uses" directives did not
+ work; the bug had appeared in 0.8.46.
+
+ *) Bugfix: the "fastcgi_split_path_info" directive used incorrectly
+ captures, if only parts of an URI were captured.
+ Thanks to Yuriy Taraday and Frank Enderle.
+
+ *) Bugfix: the "rewrite" directive did not escape a ";" character during
+ copying from URI to query string.
+ Thanks to Daisuke Murase.
+
+ *) Bugfix: the ngx_http_image_filter_module closed a connection, if an
+ image was larger than "image_filter_buffer" size.
+
+
+Changes with nginx 0.8.47 28 Jul 2010
+
+ *) Bugfix: $request_time variable had invalid values for subrequests.
+
+ *) Bugfix: errors intercepted by error_page could not be cached.
+
+ *) Bugfix: a cache manager process may got caught in an endless loop, if
+ max_size parameter was used; the bug had appeared in 0.8.46.
+
+
+Changes with nginx 0.8.46 19 Jul 2010
+
+ *) Change: now the "proxy_no_cache", "fastcgi_no_cache",
+ "uwsgi_no_cache", and "scgi_no_cache" directives affect on a cached
+ response saving only.
+
+ *) Feature: the "proxy_cache_bypass", "fastcgi_cache_bypass",
+ "uwsgi_cache_bypass", and "scgi_cache_bypass" directives.
+
+ *) Bugfix: nginx did not free memory in cache keys zones if there was an
+ error during working with backend: the memory was freed only after
+ inactivity time or on memory low condition.
+
+
+Changes with nginx 0.8.45 13 Jul 2010
+
+ *) Feature: ngx_http_xslt_filter improvements.
+ Thanks to Laurence Rowe.
+
+ *) Bugfix: SSI response might be truncated after include with
+ wait="yes"; the bug had appeared in 0.7.25.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: the "listen" directive did not support the "setfib=0"
+ parameter.
+
+
+Changes with nginx 0.8.44 05 Jul 2010
+
+ *) Change: now nginx does not cache by default backend responses, if
+ they have a "Set-Cookie" header line.
+
+ *) Feature: the "listen" directive supports the "setfib" parameter.
+ Thanks to Andrew Filonov.
+
+ *) Bugfix: the "sub_filter" directive might change character case on
+ partial match.
+
+ *) Bugfix: compatibility with HP/UX.
+
+ *) Bugfix: compatibility with AIX xlC_r compiler.
+
+ *) Bugfix: nginx treated large SSLv2 packets as plain requests.
+ Thanks to Miroslaw Jaworski.
+
+
+Changes with nginx 0.8.43 30 Jun 2010
+
+ *) Feature: large geo ranges base loading speed-up.
+
+ *) Bugfix: an error_page redirection to "location /zero {return 204;}"
+ without changing status code kept the error body; the bug had
+ appeared in 0.8.42.
+
+ *) Bugfix: nginx might close IPv6 listen socket during reconfiguration.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: the $uid_set variable may be used at any request processing
+ stage.
+
+
+Changes with nginx 0.8.42 21 Jun 2010
+
+ *) Change: now nginx tests locations given by regular expressions, if
+ request was matched exactly by a location given by a prefix string.
+ The previous behavior has been introduced in 0.7.1.
+
+ *) Feature: the ngx_http_scgi_module.
+ Thanks to Manlio Perillo.
+
+ *) Feature: a text answer may be added to a "return" directive.
+
+
+Changes with nginx 0.8.41 15 Jun 2010
+
+ *) Security: nginx/Windows worker might be terminated abnormally if a
+ requested file name has invalid UTF-8 encoding.
+
+ *) Change: now nginx allows to use spaces in a request line.
+
+ *) Bugfix: the "proxy_redirect" directive changed incorrectly a backend
+ "Refresh" response header line.
+ Thanks to Andrey Andreew and Max Sogin.
+
+ *) Bugfix: nginx did not support path without host name in "Destination"
+ request header line.
+
+
+Changes with nginx 0.8.40 07 Jun 2010
+
+ *) Security: now nginx/Windows ignores default file stream name.
+ Thanks to Jose Antonio Vazquez Gonzalez.
+
+ *) Feature: the ngx_http_uwsgi_module.
+ Thanks to Roberto De Ioris.
+
+ *) Feature: a "fastcgi_param" directive with value starting with "HTTP_"
+ overrides a client request header line.
+
+ *) Bugfix: the "If-Modified-Since", "If-Range", etc. client request
+ header lines were passed to FastCGI-server while caching.
+
+ *) Bugfix: listen unix domain socket could not be changed during
+ reconfiguration.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.8.39 31 May 2010
+
+ *) Bugfix: an inherited "alias" directive worked incorrectly in
+ inclusive location.
+
+ *) Bugfix: in "alias" with variables and "try_files" directives
+ combination.
+
+ *) Bugfix: listen unix domain and IPv6 sockets did not inherit while
+ online upgrade.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.8.38 24 May 2010
+
+ *) Feature: the "proxy_no_cache" and "fastcgi_no_cache" directives.
+
+ *) Feature: now the "rewrite" directive does a redirect automatically if
+ the $scheme variable is used.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: now "limit_req" delay directive conforms to the described
+ algorithm.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: the $uid_got variable might not be used in the SSI and perl
+ modules.
+
+
+Changes with nginx 0.8.37 17 May 2010
+
+ *) Feature: the ngx_http_split_clients_module.
+
+ *) Feature: the "map" directive supports keys more than 255 characters.
+
+ *) Bugfix: nginx ignored the "private" and "no-store" values in the
+ "Cache-Control" backend response header line.
+
+ *) Bugfix: a "stub" parameter of an "include" SSI directive was not
+ used, if empty response has 200 status code.
+
+ *) Bugfix: if a proxied or FastCGI request was internally redirected to
+ another proxied or FastCGI location, then a segmentation fault might
+ occur in a worker process; the bug had appeared in 0.8.33.
+ Thanks to Yichun Zhang.
+
+ *) Bugfix: IMAP connections may hang until they timed out while talking
+ to Zimbra server.
+ Thanks to Alan Batie.
+
+
+Changes with nginx 0.8.36 22 Apr 2010
+
+ *) Bugfix: the ngx_http_dav_module handled incorrectly the DELETE, COPY,
+ and MOVE methods for symlinks.
+
+ *) Bugfix: values of the $query_string, $arg_..., etc. variables cached
+ in main request were used by the SSI module in subrequests.
+
+ *) Bugfix: a variable value was repeatedly encoded after each an "echo"
+ SSI-command output; the bug had appeared in 0.6.14.
+
+ *) Bugfix: a worker process hung if a FIFO file was requested.
+ Thanks to Vicente Aguilar and Maxim Dounin.
+
+ *) Bugfix: OpenSSL-1.0.0 compatibility on 64-bit Linux.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: nginx could not be built --without-http-cache; the bug had
+ appeared in 0.8.35.
+
+
+Changes with nginx 0.8.35 01 Apr 2010
+
+ *) Change: now the charset filter runs before the SSI filter.
+
+ *) Feature: the "chunked_transfer_encoding" directive.
+
+ *) Bugfix: an "&" character was not escaped when it was copied in
+ arguments part in a rewrite rule.
+
+ *) Bugfix: nginx might be terminated abnormally while a signal
+ processing or if the directive "timer_resolution" was used on
+ platforms which do not support kqueue or eventport notification
+ methods.
+ Thanks to George Xie and Maxim Dounin.
+
+ *) Bugfix: if temporary files and permanent storage area resided at
+ different file systems, then permanent file modification times were
+ incorrect.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: ngx_http_memcached_module might issue the error message
+ "memcached sent invalid trailer".
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: nginx could not built zlib-1.2.4 library using the library
+ sources.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: a segmentation fault occurred in a worker process, if there
+ was large stderr output before FastCGI response; the bug had appeared
+ in 0.8.34.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.8.34 03 Mar 2010
+
+ *) Bugfix: nginx did not support all ciphers and digests used in client
+ certificates.
+ Thanks to Innocenty Enikeew.
+
+ *) Bugfix: nginx cached incorrectly FastCGI responses if there was large
+ stderr output before response.
+
+ *) Bugfix: nginx did not support HTTPS referrers.
+
+ *) Bugfix: nginx/Windows might not find file if path in configuration
+ was given in other character case; the bug had appeared in 0.8.33.
+
+ *) Bugfix: the $date_local variable has an incorrect value, if the "%s"
+ format was used.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: if ssl_session_cache was not set or was set to "none", then
+ during client certificate verify the error "session id context
+ uninitialized" might occur; the bug had appeared in 0.7.1.
+
+ *) Bugfix: a geo range returned default value if the range included two
+ or more /16 networks and did not begin at /16 network boundary.
+
+ *) Bugfix: a block used in a "stub" parameter of an "include" SSI
+ directive was output with "text/plain" MIME type.
+
+ *) Bugfix: $r->sleep() did not work; the bug had appeared in 0.8.11.
+
+
+Changes with nginx 0.8.33 01 Feb 2010
+
+ *) Security: now nginx/Windows ignores trailing spaces in URI.
+ Thanks to Dan Crowley, Core Security Technologies.
+
+ *) Security: now nginx/Windows ignores short files names.
+ Thanks to Dan Crowley, Core Security Technologies.
+
+ *) Change: now keepalive connections after POST requests are not
+ disabled for MSIE 7.0+.
+ Thanks to Adam Lounds.
+
+ *) Workaround: now keepalive connections are disabled for Safari.
+ Thanks to Joshua Sierles.
+
+ *) Bugfix: if a proxied or FastCGI request was internally redirected to
+ another proxied or FastCGI location, then $upstream_response_time
+ variable may have abnormally large value; the bug had appeared in
+ 0.8.7.
+
+ *) Bugfix: a segmentation fault might occur in a worker process, while
+ discarding a request body; the bug had appeared in 0.8.11.
+
+
+Changes with nginx 0.8.32 11 Jan 2010
+
+ *) Bugfix: UTF-8 encoding usage in the ngx_http_autoindex_module.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: regular expression named captures worked for two names only.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: now the "localhost" name is used in the "Host" request header
+ line, if an unix domain socket is defined in the "auth_http"
+ directive.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: nginx did not support chunked transfer encoding for 201
+ responses.
+ Thanks to Julian Reich.
+
+ *) Bugfix: if the "expires modified" set date in the past, then a
+ negative number was set in the "Cache-Control" response header line.
+ Thanks to Alex Kapranoff.
+
+
+Changes with nginx 0.8.31 23 Dec 2009
+
+ *) Feature: now the "error_page" directive may redirect the 301 and 302
+ responses.
+
+ *) Feature: the $geoip_city_continent_code, $geoip_latitude, and
+ $geoip_longitude variables.
+ Thanks to Arvind Sundararajan.
+
+ *) Feature: now the ngx_http_image_filter_module deletes always EXIF and
+ other application specific data if the data consume more than 5% of a
+ JPEG file.
+
+ *) Bugfix: nginx closed a connection if a cached response had an empty
+ body.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: nginx might not be built by gcc 4.x if the -O2 or higher
+ optimization option was used.
+ Thanks to Maxim Dounin and Denis F. Latypoff.
+
+ *) Bugfix: regular expressions in location were always tested in
+ case-sensitive mode; the bug had appeared in 0.8.25.
+
+ *) Bugfix: nginx cached a 304 response if there was the "If-None-Match"
+ header line in a proxied request.
+ Thanks to Tim Dettrick and David Kostal.
+
+ *) Bugfix: nginx/Windows tried to delete a temporary file twice if the
+ file should replace an already existent file.
+
+
+Changes with nginx 0.8.30 15 Dec 2009
+
+ *) Change: now the default buffer size of the
+ "large_client_header_buffers" directive is 8K.
+ Thanks to Andrew Cholakian.
+
+ *) Feature: the conf/fastcgi.conf for simple FastCGI configurations.
+
+ *) Bugfix: nginx/Windows tried to rename a temporary file twice if the
+ file should replace an already existent file.
+
+ *) Bugfix: of "double free or corruption" error issued if host could not
+ be resolved; the bug had appeared in 0.8.22.
+ Thanks to Konstantin Svist.
+
+ *) Bugfix: in libatomic usage on some platforms.
+ Thanks to W-Mark Kubacki.
+
+
+Changes with nginx 0.8.29 30 Nov 2009
+
+ *) Change: now the "009" status code is written to an access log for
+ proxied HTTP/0.9 responses.
+
+ *) Feature: the "addition_types", "charset_types", "gzip_types",
+ "ssi_types", "sub_filter_types", and "xslt_types" directives support
+ an "*" parameter.
+
+ *) Feature: GCC 4.1+ built-in atomic operations usage.
+ Thanks to W-Mark Kubacki.
+
+ *) Feature: the --with-libatomic[=DIR] option in the configure.
+ Thanks to W-Mark Kubacki.
+
+ *) Bugfix: listen unix domain socket had limited access rights.
+
+ *) Bugfix: cached HTTP/0.9 responses were handled incorrectly.
+
+ *) Bugfix: regular expression named captures given by "?P<...>" did not
+ work in a "server_name" directive.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.8.28 23 Nov 2009
+
+ *) Bugfix: nginx could not be built with the --without-pcre parameter;
+ the bug had appeared in 0.8.25.
+
+
+Changes with nginx 0.8.27 17 Nov 2009
+
+ *) Bugfix: regular expressions did not work in nginx/Windows; the bug
+ had appeared in 0.8.25.
+
+
+Changes with nginx 0.8.26 16 Nov 2009
+
+ *) Bugfix: in captures usage in "rewrite" directive; the bug had
+ appeared in 0.8.25.
+
+ *) Bugfix: nginx could not be built without the --with-debug option; the
+ bug had appeared in 0.8.25.
+
+
+Changes with nginx 0.8.25 16 Nov 2009
+
+ *) Change: now no message is written in an error log if a variable is
+ not found by $r->variable() method.
+
+ *) Feature: the ngx_http_degradation_module.
+
+ *) Feature: regular expression named captures.
+
+ *) Feature: now URI part is not required a "proxy_pass" directive if
+ variables are used.
+
+ *) Feature: now the "msie_padding" directive works for Chrome too.
+
+ *) Bugfix: a segmentation fault occurred in a worker process on low
+ memory condition; the bug had appeared in 0.8.18.
+
+ *) Bugfix: nginx sent gzipped responses to clients those do not support
+ gzip, if "gzip_static on" and "gzip_vary off"; the bug had appeared
+ in 0.8.16.
+
+
+Changes with nginx 0.8.24 11 Nov 2009
+
+ *) Bugfix: nginx always added "Content-Encoding: gzip" response header
+ line in 304 responses sent by ngx_http_gzip_static_module.
+
+ *) Bugfix: nginx could not be built without the --with-debug option; the
+ bug had appeared in 0.8.23.
+
+ *) Bugfix: the "unix:" parameter of the "set_real_ip_from" directive
+ inherited incorrectly from previous level.
+
+ *) Bugfix: in resolving empty name.
+
+
+Changes with nginx 0.8.23 11 Nov 2009
+
+ *) Security: now SSL/TLS renegotiation is disabled.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: listen unix domain socket did not inherit while online
+ upgrade.
+
+ *) Bugfix: the "unix:" parameter of the "set_real_ip_from" directive did
+ not without yet another directive with any IP address.
+
+ *) Bugfix: segmentation fault and infinite looping in resolver.
+
+ *) Bugfix: in resolver.
+ Thanks to Artem Bokhan.
+
+
+Changes with nginx 0.8.22 03 Nov 2009
+
+ *) Feature: the "proxy_bind", "fastcgi_bind", and "memcached_bind"
+ directives.
+
+ *) Feature: the "access" and the "deny" directives support IPv6.
+
+ *) Feature: the "set_real_ip_from" directive supports IPv6 addresses in
+ request headers.
+
+ *) Feature: the "unix:" parameter of the "set_real_ip_from" directive.
+
+ *) Bugfix: nginx did not delete unix domain socket after configuration
+ testing.
+
+ *) Bugfix: nginx deleted unix domain socket while online upgrade.
+
+ *) Bugfix: the "!-x" operator did not work.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: a segmentation fault might occur in a worker process, if
+ limit_rate was used in HTTPS server.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: a segmentation fault might occur in a worker process while
+ $limit_rate logging.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: a segmentation fault might occur in a worker process, if
+ there was no "listen" directive in "server" block; the bug had
+ appeared in 0.8.21.
+
+
+Changes with nginx 0.8.21 26 Oct 2009
+
+ *) Feature: now the "-V" switch shows TLS SNI support.
+
+ *) Feature: the "listen" directive of the HTTP module supports unix
+ domain sockets.
+ Thanks to Hongli Lai.
+
+ *) Feature: the "default_server" parameter of the "listen" directive.
+
+ *) Feature: now a "default" parameter is not required to set listen
+ socket options.
+
+ *) Bugfix: nginx did not support dates in 2038 year on 32-bit platforms;
+
+ *) Bugfix: socket leak; the bug had appeared in 0.8.11.
+
+
+Changes with nginx 0.8.20 14 Oct 2009
+
+ *) Change: now default SSL ciphers are "HIGH:!ADH:!MD5".
+
+ *) Bugfix: the ngx_http_autoindex_module did not show the trailing slash
+ in links to a directory; the bug had appeared in 0.7.15.
+
+ *) Bugfix: nginx did not close a log file set by the --error-log-path
+ configuration option; the bug had appeared in 0.7.53.
+
+ *) Bugfix: nginx did not treat a comma as separator in the
+ "Cache-Control" backend response header line.
+
+ *) Bugfix: nginx/Windows might not create temporary file, a cache file,
+ or "proxy/fastcgi_store"d file if a worker had no enough access
+ rights for top level directories.
+
+ *) Bugfix: the "Set-Cookie" and "P3P" FastCGI response header lines were
+ not hidden while caching if no "fastcgi_hide_header" directives were
+ used with any parameters.
+
+ *) Bugfix: nginx counted incorrectly disk cache size.
+
+
+Changes with nginx 0.8.19 06 Oct 2009
+
+ *) Change: now SSLv2 protocol is disabled by default.
+
+ *) Change: now default SSL ciphers are "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM".
+
+ *) Bugfix: a "limit_req" directive did not work; the bug had appeared in
+ 0.8.18.
+
+
+Changes with nginx 0.8.18 06 Oct 2009
+
+ *) Feature: the "read_ahead" directive.
+
+ *) Feature: now several "perl_modules" directives may be used.
+
+ *) Feature: the "limit_req_log_level" and "limit_conn_log_level"
+ directives.
+
+ *) Bugfix: now "limit_req" directive conforms to the leaky bucket
+ algorithm.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: nginx did not work on Linux/sparc.
+ Thanks to Marcus Ramberg.
+
+ *) Bugfix: nginx sent '\0' in a "Location" response header line on MKCOL
+ request.
+ Thanks to Xie Zhenye.
+
+ *) Bugfix: zero status code was logged instead of 499 status code; the
+ bug had appeared in 0.8.11.
+
+ *) Bugfix: socket leak; the bug had appeared in 0.8.11.
+
+
+Changes with nginx 0.8.17 28 Sep 2009
+
+ *) Security: now "/../" are disabled in "Destination" request header
+ line.
+
+ *) Change: now $host variable value is always low case.
+
+ *) Feature: the $ssl_session_id variable.
+
+ *) Bugfix: socket leak; the bug had appeared in 0.8.11.
+
+
+Changes with nginx 0.8.16 22 Sep 2009
+
+ *) Feature: the "image_filter_transparency" directive.
+
+ *) Bugfix: "addition_types" directive was incorrectly named
+ "addtion_types".
+
+ *) Bugfix: resolver cache poisoning.
+ Thanks to Matthew Dempsky.
+
+ *) Bugfix: memory leak in resolver.
+ Thanks to Matthew Dempsky.
+
+ *) Bugfix: invalid request line in $request variable was written in
+ access_log only if error_log was set to "info" or "debug" level.
+
+ *) Bugfix: in PNG alpha-channel support in the
+ ngx_http_image_filter_module.
+
+ *) Bugfix: nginx always added "Vary: Accept-Encoding" response header
+ line, if both "gzip_static" and "gzip_vary" were on.
+
+ *) Bugfix: in UTF-8 encoding support by "try_files" directive in
+ nginx/Windows.
+
+ *) Bugfix: in "post_action" directive usage; the bug had appeared in
+ 0.8.11.
+ Thanks to Igor Artemiev.
+
+
+Changes with nginx 0.8.15 14 Sep 2009
+
+ *) Security: a segmentation fault might occur in worker process while
+ specially crafted request handling.
+ Thanks to Chris Ries.
+
+ *) Bugfix: if names .domain.tld, .sub.domain.tld, and .domain-some.tld
+ were defined, then the name .sub.domain.tld was matched by
+ .domain.tld.
+
+ *) Bugfix: in transparency support in the ngx_http_image_filter_module.
+
+ *) Bugfix: in file AIO.
+
+ *) Bugfix: in X-Accel-Redirect usage; the bug had appeared in 0.8.11.
+
+ *) Bugfix: in embedded perl module; the bug had appeared in 0.8.11.
+
+
+Changes with nginx 0.8.14 07 Sep 2009
+
+ *) Bugfix: an expired cached response might stick in the "UPDATING"
+ state.
+
+ *) Bugfix: a segmentation fault might occur in worker process, if
+ error_log was set to info or debug level.
+ Thanks to Sergey Bochenkov.
+
+ *) Bugfix: in embedded perl module; the bug had appeared in 0.8.11.
+
+ *) Bugfix: an "error_page" directive did not redirect a 413 error; the
+ bug had appeared in 0.6.10.
+
+
+Changes with nginx 0.8.13 31 Aug 2009
+
+ *) Bugfix: in the "aio sendfile" directive; the bug had appeared in
+ 0.8.12.
+
+ *) Bugfix: nginx could not be built without the --with-file-aio option
+ on FreeBSD; the bug had appeared in 0.8.12.
+
+
+Changes with nginx 0.8.12 31 Aug 2009
+
+ *) Feature: the "sendfile" parameter in the "aio" directive on FreeBSD.
+
+ *) Bugfix: in try_files; the bug had appeared in 0.8.11.
+
+ *) Bugfix: in memcached; the bug had appeared in 0.8.11.
+
+
+Changes with nginx 0.8.11 28 Aug 2009
+
+ *) Change: now directive "gzip_disable msie6" does not disable gzipping
+ for MSIE 6.0 SV1.
+
+ *) Feature: file AIO support on FreeBSD and Linux.
+
+ *) Feature: the "directio_alignment" directive.
+
+
+Changes with nginx 0.8.10 24 Aug 2009
+
+ *) Bugfix: memory leaks if GeoIP City database was used.
+
+ *) Bugfix: in copying temporary files to permanent storage area; the bug
+ had appeared in 0.8.9.
+
+
+Changes with nginx 0.8.9 17 Aug 2009
+
+ *) Feature: now the start cache loader runs in a separate process; this
+ should improve large caches handling.
+
+ *) Feature: now temporary files and permanent storage area may reside at
+ different file systems.
+
+
+Changes with nginx 0.8.8 10 Aug 2009
+
+ *) Bugfix: in handling FastCGI headers split in records.
+
+ *) Bugfix: a segmentation fault occurred in worker process, if a request
+ was handled in two proxied or FastCGIed locations and a caching was
+ enabled in the first location; the bug had appeared in 0.8.7.
+
+
+Changes with nginx 0.8.7 27 Jul 2009
+
+ *) Change: minimum supported OpenSSL version is 0.9.7.
+
+ *) Change: the "ask" parameter of the "ssl_verify_client" directive was
+ changed to the "optional" parameter and now it checks a client
+ certificate if it was offered.
+ Thanks to Brice Figureau.
+
+ *) Feature: the $ssl_client_verify variable.
+ Thanks to Brice Figureau.
+
+ *) Feature: the "ssl_crl" directive.
+ Thanks to Brice Figureau.
+
+ *) Feature: the "proxy" parameter of the "geo" directive.
+
+ *) Feature: the "image_filter" directive supports variables for setting
+ size.
+
+ *) Bugfix: the $ssl_client_cert variable usage corrupted memory; the bug
+ had appeared in 0.7.7.
+ Thanks to Sergey Zhuravlev.
+
+ *) Bugfix: "proxy_pass_header" and "fastcgi_pass_header" directives did
+ not pass to a client the "X-Accel-Redirect", "X-Accel-Limit-Rate",
+ "X-Accel-Buffering", and "X-Accel-Charset" lines from backend
+ response header.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: in handling "Last-Modified" and "Accept-Ranges" backend
+ response header lines; the bug had appeared in 0.7.44.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: the "[alert] zero size buf" error if subrequest returns an
+ empty response; the bug had appeared in 0.8.5.
+
+
+Changes with nginx 0.8.6 20 Jul 2009
+
+ *) Feature: the ngx_http_geoip_module.
+
+ *) Bugfix: XSLT filter may fail with message "not well formed XML
+ document" for valid XML document.
+ Thanks to Kuramoto Eiji.
+
+ *) Bugfix: now in MacOSX, Cygwin, and nginx/Windows locations given by a
+ regular expression are always tested in case insensitive mode.
+
+ *) Bugfix: now nginx/Windows ignores trailing dots in URI.
+ Thanks to Hugo Leisink.
+
+ *) Bugfix: name of file specified in --conf-path was not honored during
+ installation; the bug had appeared in 0.6.6.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.8.5 13 Jul 2009
+
+ *) Bugfix: now nginx allows underscores in a request method.
+
+ *) Bugfix: a 500 error code was returned for invalid login/password
+ while HTTP Basic authentication on Windows.
+
+ *) Bugfix: ngx_http_perl_module responses did not work in subrequests.
+
+ *) Bugfix: in ngx_http_limit_req_module.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.8.4 22 Jun 2009
+
+ *) Bugfix: nginx could not be built --without-http-cache; the bug had
+ appeared in 0.8.3.
+
+
+Changes with nginx 0.8.3 19 Jun 2009
+
+ *) Feature: the $upstream_cache_status variable.
+
+ *) Bugfix: nginx could not be built on MacOSX 10.6.
+
+ *) Bugfix: nginx could not be built --without-http-cache; the bug had
+ appeared in 0.8.2.
+
+ *) Bugfix: a segmentation fault occurred in worker process, if a backend
+ 401 error was intercepted and the backend did not set the
+ "WWW-Authenticate" response header line.
+ Thanks to Eugene Mychlo.
+
+
+Changes with nginx 0.8.2 15 Jun 2009
+
+ *) Bugfix: in open_file_cache and proxy/fastcgi cache interaction on
+ start up.
+
+ *) Bugfix: open_file_cache might cache open file descriptors too long;
+ the bug had appeared in 0.7.4.
+
+
+Changes with nginx 0.8.1 08 Jun 2009
+
+ *) Feature: the "updating" parameter in "proxy_cache_use_stale" and
+ "fastcgi_cache_use_stale" directives.
+
+ *) Bugfix: the "If-Modified-Since", "If-Range", etc. client request
+ header lines were passed to backend while caching if no
+ "proxy_set_header" directive was used with any parameters.
+
+ *) Bugfix: the "Set-Cookie" and "P3P" response header lines were not
+ hidden while caching if no "proxy_hide_header/fastcgi_hide_header"
+ directives were used with any parameters.
+
+ *) Bugfix: the ngx_http_image_filter_module did not support GIF87a
+ format.
+ Thanks to Denis Ilyinyh.
+
+ *) Bugfix: nginx could not be built modules on Solaris 10 and early; the
+ bug had appeared in 0.7.56.
+
+
+Changes with nginx 0.8.0 02 Jun 2009
+
+ *) Feature: the "keepalive_requests" directive.
+
+ *) Feature: the "limit_rate_after" directive.
+ Thanks to Ivan Debnar.
+
+ *) Bugfix: XLST filter did not work in subrequests.
+
+ *) Bugfix: in relative paths handling in nginx/Windows.
+
+ *) Bugfix: in proxy_store, fastcgi_store, proxy_cache, and fastcgi_cache
+ in nginx/Windows.
+
+ *) Bugfix: in memory allocation error handling.
+ Thanks to Maxim Dounin and Kirill A. Korinskiy.
+
+
+Changes with nginx 0.7.59 25 May 2009
+
+ *) Feature: the "proxy_cache_methods" and "fastcgi_cache_methods"
+ directives.
+
+ *) Bugfix: socket leak; the bug had appeared in 0.7.25.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: a segmentation fault occurred in worker process, if a request
+ had no body and the $request_body variable was used;
+ the bug had appeared in 0.7.58.
+
+ *) Bugfix: the SSL modules might not built on Solaris and Linux;
+ the bug had appeared in 0.7.56.
+
+ *) Bugfix: ngx_http_xslt_filter_module responses were not handled by
+ SSI, charset, and gzip filters.
+
+ *) Bugfix: a "charset" directive did not set a charset to
+ ngx_http_gzip_static_module responses.
+
+
+Changes with nginx 0.7.58 18 May 2009
+
+ *) Feature: a "listen" directive of the mail proxy module supports IPv6.
+
+ *) Feature: the "image_filter_jpeg_quality" directive.
+
+ *) Feature: the "client_body_in_single_buffer" directive.
+
+ *) Feature: the $request_body variable.
+
+ *) Bugfix: in ngx_http_autoindex_module in file name links having a ":"
+ symbol in the name.
+
+ *) Bugfix: "make upgrade" procedure did not work; the bug had appeared
+ in 0.7.53.
+ Thanks to Denis F. Latypoff.
+
+
+Changes with nginx 0.7.57 12 May 2009
+
+ *) Bugfix: a floating-point fault occurred in worker process, if the
+ ngx_http_image_filter_module errors were redirected to named
+ location; the bug had appeared in 0.7.56.
+
+
+Changes with nginx 0.7.56 11 May 2009
+
+ *) Feature: nginx/Windows supports IPv6 in a "listen" directive of the
+ HTTP module.
+
+ *) Bugfix: in ngx_http_image_filter_module.
+
+
+Changes with nginx 0.7.55 06 May 2009
+
+ *) Bugfix: the http_XXX parameters in "proxy_cache_use_stale" and
+ "fastcgi_cache_use_stale" directives did not work.
+
+ *) Bugfix: fastcgi cache did not cache header only responses.
+
+ *) Bugfix: of "select() failed (9: Bad file descriptor)" error in
+ nginx/Unix and "select() failed (10038: ...)" error in nginx/Windows.
+
+ *) Bugfix: a segmentation fault might occur in worker process, if an
+ "debug_connection" directive was used; the bug had appeared in
+ 0.7.54.
+
+ *) Bugfix: fix ngx_http_image_filter_module building errors.
+
+ *) Bugfix: the files bigger than 2G could not be transferred using
+ $r->sendfile.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.7.54 01 May 2009
+
+ *) Feature: the ngx_http_image_filter_module.
+
+ *) Feature: the "proxy_ignore_headers" and "fastcgi_ignore_headers"
+ directives.
+
+ *) Bugfix: a segmentation fault might occur in worker process, if an
+ "open_file_cache_errors off" directive was used; the bug had appeared
+ in 0.7.53.
+
+ *) Bugfix: the "port_in_redirect off" directive did not work; the bug
+ had appeared in 0.7.39.
+
+ *) Bugfix: improve handling of "select" method errors.
+
+ *) Bugfix: of "select() failed (10022: ...)" error in nginx/Windows.
+
+ *) Bugfix: in error text descriptions in nginx/Windows; the bug had
+ appeared in 0.7.53.
+
+
+Changes with nginx 0.7.53 27 Apr 2009
+
+ *) Change: now a log set by --error-log-path is created from the very
+ start-up.
+
+ *) Feature: now the start up errors and warnings are outputted to an
+ error_log and stderr.
+
+ *) Feature: the empty --prefix= configure parameter forces nginx to use
+ a directory where it was run as prefix.
+
+ *) Feature: the -p switch.
+
+ *) Feature: the -s switch on Unix platforms.
+
+ *) Feature: the -? and -h switches.
+ Thanks to Jerome Loyet.
+
+ *) Feature: now switches may be set in condensed form.
+
+ *) Bugfix: nginx/Windows did not work if configuration file was given by
+ the -c switch.
+
+ *) Bugfix: temporary files might be not removed if the "proxy_store",
+ "fastcgi_store", "proxy_cache", or "fastcgi_cache" were used.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: an incorrect value was passed to mail proxy authentication
+ server in "Auth-Method" header line; the bug had appeared
+ in 0.7.34.
+ Thanks to Simon Lecaille.
+
+ *) Bugfix: system error text descriptions were not logged on Linux;
+ the bug had appeared in 0.7.45.
+
+ *) Bugfix: the "fastcgi_cache_min_uses" directive did not work.
+ Thanks to Andrew Vorobyoff.
+
+
+Changes with nginx 0.7.52 20 Apr 2009
+
+ *) Feature: the first native Windows binary release.
+
+ *) Bugfix: in processing HEAD method while caching.
+
+ *) Bugfix: in processing the "If-Modified-Since", "If-Range", etc.
+ client request header lines while caching.
+
+ *) Bugfix: now the "Set-Cookie" and "P3P" header lines are hidden in
+ cacheable responses.
+
+ *) Bugfix: if nginx was built with the ngx_http_perl_module and with a
+ perl which supports threads, then during a master process exit the
+ message "panic: MUTEX_LOCK" might be issued.
+
+ *) Bugfix: nginx could not be built --without-http-cache; the bug had
+ appeared in 0.7.48.
+
+ *) Bugfix: nginx could not be built on platforms different from i386,
+ amd64, sparc, and ppc; the bug had appeared in 0.7.42.
+
+
+Changes with nginx 0.7.51 12 Apr 2009
+
+ *) Feature: the "try_files" directive supports a response code in the
+ fallback parameter.
+
+ *) Feature: now any response code can be used in the "return" directive.
+
+ *) Bugfix: the "error_page" directive made an external redirect without
+ query string; the bug had appeared in 0.7.44.
+
+ *) Bugfix: if servers listened on several defined explicitly addresses,
+ then virtual servers might not work; the bug had appeared in 0.7.39.
+
+
+Changes with nginx 0.7.50 06 Apr 2009
+
+ *) Bugfix: the $arg_... variables did not work; the bug had appeared in
+ 0.7.49.
+
+
+Changes with nginx 0.7.49 06 Apr 2009
+
+ *) Bugfix: a segmentation fault might occur in worker process, if the
+ $arg_... variables were used; the bug had appeared in 0.7.48.
+
+
+Changes with nginx 0.7.48 06 Apr 2009
+
+ *) Feature: the "proxy_cache_key" directive.
+
+ *) Bugfix: now nginx takes into account the "X-Accel-Expires",
+ "Expires", and "Cache-Control" header lines in a backend response.
+
+ *) Bugfix: now nginx caches responses for the GET requests only.
+
+ *) Bugfix: the "fastcgi_cache_key" directive was not inherited.
+
+ *) Bugfix: the $arg_... variables did not work with SSI subrequests.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: nginx could not be built with uclibc library.
+ Thanks to Timothy Redaelli.
+
+ *) Bugfix: nginx could not be built on OpenBSD; the bug had
+ appeared in 0.7.46.
+
+
+Changes with nginx 0.7.47 01 Apr 2009
+
+ *) Bugfix: nginx could not be built on FreeBSD 6 and early versions; the
+ bug had appeared in 0.7.46.
+
+ *) Bugfix: nginx could not be built on MacOSX; the bug had
+ appeared in 0.7.46.
+
+ *) Bugfix: if the "max_size" parameter was set, then the cache manager
+ might purge a whole cache; the bug had appeared in 0.7.46.
+
+ *) Change: a segmentation fault might occur in worker process, if the
+ "proxy_cache"/"fastcgi_cache" and the "proxy_cache_valid"/
+ "fastcgi_cache_valid" were set on different levels; the bug had
+ appeared in 0.7.46.
+
+ *) Bugfix: a segmentation fault might occur in worker process, if a
+ request was redirected to a proxied or FastCGI server via error_page
+ or try_files; the bug had appeared in 0.7.44.
+
+
+Changes with nginx 0.7.46 30 Mar 2009
+
+ *) Bugfix: the previous release tarball was incorrect.
+
+
+Changes with nginx 0.7.45 30 Mar 2009
+
+ *) Change: now the "proxy_cache" and the "proxy_cache_valid" directives
+ can be set on different levels.
+
+ *) Change: the "clean_time" parameter of the "proxy_cache_path"
+ directive is canceled.
+
+ *) Feature: the "max_size" parameter of the "proxy_cache_path"
+ directive.
+
+ *) Feature: the ngx_http_fastcgi_module preliminary cache support.
+
+ *) Feature: now on shared memory allocation errors directive and zone
+ names are logged.
+
+ *) Bugfix: the directive "add_header last-modified ''" did not delete a
+ "Last-Modified" response header line; the bug had appeared in 0.7.44.
+
+ *) Bugfix: a relative path in the "auth_basic_user_file" directive given
+ without variables did not work; the bug had appeared in 0.7.44.
+ Thanks to Jerome Loyet.
+
+ *) Bugfix: in an "alias" directive given using variables without
+ references to captures of regular expressions; the bug had appeared
+ in 0.7.42.
+
+
+Changes with nginx 0.7.44 23 Mar 2009
+
+ *) Feature: the ngx_http_proxy_module preliminary cache support.
+
+ *) Feature: the --with-pcre option in the configure.
+
+ *) Feature: the "try_files" directive is now allowed on the server block
+ level.
+
+ *) Bugfix: the "try_files" directive handled incorrectly a query string
+ in a fallback parameter.
+
+ *) Bugfix: the "try_files" directive might test incorrectly directories.
+
+ *) Bugfix: if there was a single server for given address:port pair,
+ then captures in regular expressions in a "server_name" directive did
+ not work.
+
+
+Changes with nginx 0.7.43 18 Mar 2009
+
+ *) Bugfix: a request was handled incorrectly, if a "root" directive used
+ variables; the bug had appeared in 0.7.42.
+
+ *) Bugfix: if a server listened on wildcard address, then the
+ $server_addr variable value was "0.0.0.0"; the bug had appeared in
+ 0.7.36.
+
+
+Changes with nginx 0.7.42 16 Mar 2009
+
+ *) Change: now the "Invalid argument" error returned by
+ setsockopt(TCP_NODELAY) on Solaris, is ignored.
+
+ *) Change: now if a file specified in a "auth_basic_user_file" directive
+ is absent, then the 403 error is returned instead of the 500 one.
+
+ *) Feature: the "auth_basic_user_file" directive supports variables.
+ Thanks to Kirill A. Korinskiy.
+
+ *) Feature: the "listen" directive supports the "ipv6only" parameter.
+ Thanks to Zhang Hua.
+
+ *) Bugfix: in an "alias" directive with references to captures of
+ regular expressions; the bug had appeared in 0.7.40.
+
+ *) Bugfix: compatibility with Tru64 UNIX.
+ Thanks to Dustin Marquess.
+
+ *) Bugfix: nginx could not be built without PCRE library; the bug had
+ appeared in 0.7.41.
+
+
+Changes with nginx 0.7.41 11 Mar 2009
+
+ *) Bugfix: a segmentation fault might occur in worker process, if a
+ "server_name" or a "location" directives had captures in regular
+ expressions; the issue had appeared in 0.7.40.
+ Thanks to Vladimir Sopot.
+
+
+Changes with nginx 0.7.40 09 Mar 2009
+
+ *) Feature: the "location" directive supports captures in regular
+ expressions.
+
+ *) Feature: an "alias" directive with capture references may be used
+ inside a location given by a regular expression with captures.
+
+ *) Feature: the "server_name" directive supports captures in regular
+ expressions.
+
+ *) Workaround: the ngx_http_autoindex_module did not show the trailing
+ slash in directories on XFS filesystem; the issue had appeared in
+ 0.7.15.
+ Thanks to Dmitry Kuzmenko.
+
+
+Changes with nginx 0.7.39 02 Mar 2009
+
+ *) Bugfix: large response with SSI might hang, if gzipping was enabled;
+ the bug had appeared in 0.7.28.
+ Thanks to Artem Bokhan.
+
+ *) Bugfix: a segmentation fault might occur in worker process, if short
+ static variants are used in a "try_files" directive.
+
+
+Changes with nginx 0.7.38 23 Feb 2009
+
+ *) Feature: authentication failures logging.
+
+ *) Bugfix: name/password in auth_basic_user_file were ignored after odd
+ number of empty lines.
+ Thanks to Alexander Zagrebin.
+
+ *) Bugfix: a segmentation fault occurred in a master process, if long
+ path was used in unix domain socket; the bug had appeared in 0.7.36.
+
+
+Changes with nginx 0.7.37 21 Feb 2009
+
+ *) Bugfix: directives using upstreams did not work; the bug had appeared
+ in 0.7.36.
+
+
+Changes with nginx 0.7.36 21 Feb 2009
+
+ *) Feature: a preliminary IPv6 support; the "listen" directive of the
+ HTTP module supports IPv6.
+
+ *) Bugfix: the $ancient_browser variable did not work for browsers
+ preset by a "modern_browser" directives.
+
+
+Changes with nginx 0.7.35 16 Feb 2009
+
+ *) Bugfix: a "ssl_engine" directive did not use a SSL-accelerator for
+ asymmetric ciphers.
+ Thanks to Marcin Gozdalik.
+
+ *) Bugfix: a "try_files" directive set MIME type depending on an
+ original request extension.
+
+ *) Bugfix: "*domain.tld" names were handled incorrectly in
+ "server_name", "valid_referers", and "map" directives, if
+ ".domain.tld" and ".subdomain.domain.tld" wildcards were used;
+ the bug had appeared in 0.7.9.
+
+
+Changes with nginx 0.7.34 10 Feb 2009
+
+ *) Feature: the "off" parameter of the "if_modified_since" directive.
+
+ *) Feature: now nginx sends an HELO/EHLO command after a XCLIENT
+ command.
+ Thanks to Maxim Dounin.
+
+ *) Feature: Microsoft specific "AUTH LOGIN with User Name" mode support
+ in mail proxy server.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: in a redirect rewrite directive original arguments were
+ concatenated with new arguments by a "?" rather than an "&";
+ the bug had appeared in 0.1.18.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: nginx could not be built on AIX.
+
+
+Changes with nginx 0.7.33 02 Feb 2009
+
+ *) Bugfix: a double response might be returned if the epoll or rtsig
+ methods are used and a redirect was returned to a request with body.
+ Thanks to Eden Li.
+
+ *) Bugfix: the $sent_http_location variable was empty for some redirects
+ types.
+
+ *) Bugfix: a segmentation fault might occur in worker process if
+ "resolver" directive was used in SMTP proxy.
+
+
+Changes with nginx 0.7.32 26 Jan 2009
+
+ *) Feature: now a directory existence testing can be set explicitly in
+ the "try_files" directive.
+
+ *) Bugfix: fastcgi_store stored files not always.
+
+ *) Bugfix: in geo ranges.
+
+ *) Bugfix: in shared memory allocations if nginx was built without
+ debugging.
+ Thanks to Andrey Kvasov.
+
+
+Changes with nginx 0.7.31 19 Jan 2009
+
+ *) Change: now the "try_files" directive tests files only and ignores
+ directories.
+
+ *) Feature: the "fastcgi_split_path_info" directive.
+
+ *) Bugfixes in an "Expect" request header line support.
+
+ *) Bugfixes in geo ranges.
+
+ *) Bugfix: in a miss case ngx_http_memcached_module returned the "END"
+ line as response body instead of default 404 page body; the bug had
+ appeared in 0.7.18.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: while SMTP proxying nginx issued message "250 2.0.0 OK"
+ instead of "235 2.0.0 OK"; the bug had appeared in 0.7.22.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.7.30 24 Dec 2008
+
+ *) Bugfix: a segmentation fault occurred in worker process, if variables
+ were used in the "fastcgi_pass" or "proxy_pass" directives and host
+ name must be resolved; the bug had appeared in 0.7.29.
+
+
+Changes with nginx 0.7.29 24 Dec 2008
+
+ *) Bugfix: the "fastcgi_pass" and "proxy_pass" directives did not
+ support variables if unix domain sockets were used.
+
+ *) Bugfixes in subrequest processing; the bugs had appeared in 0.7.25.
+
+ *) Bugfix: a "100 Continue" response was issued for HTTP/1.0 requests;
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: in memory allocation in the ngx_http_gzip_filter_module on
+ Cygwin.
+
+
+Changes with nginx 0.7.28 22 Dec 2008
+
+ *) Change: in memory allocation in the ngx_http_gzip_filter_module.
+
+ *) Change: the default "gzip_buffers" directive values have been changed
+ to 32 4k or 16 8k from 4 4k/8k.
+
+
+Changes with nginx 0.7.27 15 Dec 2008
+
+ *) Feature: the "try_files" directive.
+
+ *) Feature: variables support in the "fastcgi_pass" directive.
+
+ *) Feature: now the $geo variable may get an address from a variable.
+ Thanks to Andrei Nigmatulin.
+
+ *) Feature: now a location's modifier may be used without space before
+ name.
+
+ *) Feature: the $upstream_response_length variable.
+
+ *) Bugfix: now a "add_header" directive does not add an empty value.
+
+ *) Bugfix: if zero length static file was requested, then nginx just
+ closed connection; the bug had appeared in 0.7.25.
+
+ *) Bugfix: a MOVE method could not move file in non-existent directory.
+
+ *) Bugfix: a segmentation fault occurred in worker process, if no one
+ named location was defined in server, but some one was used in an
+ error_page directive.
+ Thanks to Sergey Bochenkov.
+
+
+Changes with nginx 0.7.26 08 Dec 2008
+
+ *) Bugfix: in subrequest processing; the bug had appeared in 0.7.25.
+
+
+Changes with nginx 0.7.25 08 Dec 2008
+
+ *) Change: in subrequest processing.
+
+ *) Change: now POSTs without "Content-Length" header line are allowed.
+
+ *) Bugfix: now the "limit_req" and "limit_conn" directives log a
+ prohibition reason.
+
+ *) Bugfix: in the "delete" parameter of the "geo" directive.
+
+
+Changes with nginx 0.7.24 01 Dec 2008
+
+ *) Feature: the "if_modified_since" directive.
+
+ *) Bugfix: nginx did not process a FastCGI server response, if the
+ server send too many messages to stderr before response.
+
+ *) Bugfix: the "$cookie_..." variables did not work in the SSI and the
+ perl module.
+
+
+Changes with nginx 0.7.23 27 Nov 2008
+
+ *) Feature: the "delete" and "ranges" parameters in the "geo" directive.
+
+ *) Feature: speeding up loading of geo base with large number of values.
+
+ *) Feature: decrease of memory required for geo base load.
+
+
+Changes with nginx 0.7.22 20 Nov 2008
+
+ *) Feature: the "none" parameter in the "smtp_auth" directive.
+ Thanks to Maxim Dounin.
+
+ *) Feature: the "$cookie_..." variables.
+
+ *) Bugfix: the "directio" directive did not work in XFS filesystem.
+
+ *) Bugfix: the resolver did not understand big DNS responses.
+ Thanks to Zyb.
+
+
+Changes with nginx 0.7.21 11 Nov 2008
+
+ *) Changes in the ngx_http_limit_req_module.
+
+ *) Feature: the EXSLT support in the ngx_http_xslt_module.
+ Thanks to Denis F. Latypoff.
+
+ *) Workaround: compatibility with glibc 2.3.
+ Thanks to Eric Benson and Maxim Dounin.
+
+ *) Bugfix: nginx could not run on MacOSX 10.4 and earlier; the bug had
+ appeared in 0.7.6.
+
+
+Changes with nginx 0.7.20 10 Nov 2008
+
+ *) Changes in the ngx_http_gzip_filter_module.
+
+ *) Feature: the ngx_http_limit_req_module.
+
+ *) Bugfix: worker processes might exit on a SIGBUS signal on sparc and
+ ppc platforms; the bug had appeared in 0.7.3.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: the "proxy_pass http://host/some:uri" directives did not
+ work; the bug had appeared in 0.7.12.
+
+ *) Bugfix: in HTTPS mode requests might fail with the "bad write retry"
+ error.
+
+ *) Bugfix: the ngx_http_secure_link_module did not work inside
+ locations, whose names are less than 3 characters.
+
+ *) Bugfix: $server_addr variable might have no value.
+
+
+Changes with nginx 0.7.19 13 Oct 2008
+
+ *) Bugfix: version number update.
+
+
+Changes with nginx 0.7.18 13 Oct 2008
+
+ *) Change: the "underscores_in_headers" directive; now nginx does not
+ allows underscores in a client request header line names.
+
+ *) Feature: the ngx_http_secure_link_module.
+
+ *) Feature: the "real_ip_header" directive supports any header.
+
+ *) Feature: the "log_subrequest" directive.
+
+ *) Feature: the $realpath_root variable.
+
+ *) Feature: the "http_502" and "http_504" parameters of the
+ "proxy_next_upstream" directive.
+
+ *) Bugfix: the "http_503" parameter of the "proxy_next_upstream" or
+ "fastcgi_next_upstream" directives did not work.
+
+ *) Bugfix: nginx might send a "Transfer-Encoding: chunked" header line
+ for HEAD requests.
+
+ *) Bugfix: now accept threshold depends on worker_connections.
+
+
+Changes with nginx 0.7.17 15 Sep 2008
+
+ *) Feature: now the "directio" directive works on Linux.
+
+ *) Feature: the $pid variable.
+
+ *) Bugfix: the "directio" optimization that had appeared in 0.7.15 did
+ not work with open_file_cache.
+
+ *) Bugfix: the "access_log" with variables did not work on Linux; the
+ bug had appeared in 0.7.7.
+
+ *) Bugfix: the ngx_http_charset_module did not understand quoted charset
+ name received from backend.
+
+
+Changes with nginx 0.7.16 08 Sep 2008
+
+ *) Bugfix: nginx could not be built on 64-bit platforms; the bug had
+ appeared in 0.7.15.
+
+
+Changes with nginx 0.7.15 08 Sep 2008
+
+ *) Feature: the ngx_http_random_index_module.
+
+ *) Feature: the "directio" directive has been optimized for file
+ requests starting from arbitrary position.
+
+ *) Feature: the "directio" directive turns off sendfile if it is
+ necessary.
+
+ *) Feature: now nginx allows underscores in a client request header line
+ names.
+
+
+Changes with nginx 0.7.14 01 Sep 2008
+
+ *) Change: now the ssl_certificate and ssl_certificate_key directives
+ have no default values.
+
+ *) Feature: the "listen" directive supports the "ssl" parameter.
+
+ *) Feature: now nginx takes into account a time zone change while
+ reconfiguration on FreeBSD and Linux.
+
+ *) Bugfix: the "listen" directive parameters such as "backlog",
+ "rcvbuf", etc. were not set, if a default server was not the first
+ one.
+
+ *) Bugfix: if URI part captured by a "rewrite" directive was used as a
+ query string, then the query string was not escaped.
+
+ *) Bugfix: configuration file validity test improvements.
+
+
+Changes with nginx 0.7.13 26 Aug 2008
+
+ *) Bugfix: nginx could not be built on Linux and Solaris; the bug had
+ appeared in 0.7.12.
+
+
+Changes with nginx 0.7.12 26 Aug 2008
+
+ *) Feature: the "server_name" directive supports empty name "".
+
+ *) Feature: the "gzip_disable" directive supports special "msie6" mask.
+
+ *) Bugfix: if the "max_fails=0" parameter was used in upstream with
+ several servers, then a worker process exited on a SIGFPE signal.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: a request body was dropped while redirection via an
+ "error_page" directive.
+
+ *) Bugfix: a full response was returned for request method HEAD while
+ redirection via an "error_page" directive.
+
+ *) Bugfix: the $r->header_in() method did not return value of the
+ "Host", "User-Agent", and "Connection" request header lines; the bug
+ had appeared in 0.7.0.
+
+
+Changes with nginx 0.7.11 18 Aug 2008
+
+ *) Change: now ngx_http_charset_module does not work by default with
+ text/css MIME type.
+
+ *) Feature: now nginx returns the 405 status code for POST method
+ requesting a static file only if the file exists.
+
+ *) Feature: the "proxy_ssl_session_reuse" directive.
+
+ *) Bugfix: a "proxy_pass" directive without URI part might use original
+ request after the "X-Accel-Redirect" redirection was used.
+
+ *) Bugfix: if a directory has search only rights and the first index
+ file was absent, then nginx returned the 500 status code.
+
+ *) Bugfix: in inclusive locations; the bugs had appeared in 0.7.1.
+
+
+Changes with nginx 0.7.10 13 Aug 2008
+
+ *) Bugfix: in the "addition_types", "charset_types", "gzip_types",
+ "ssi_types", "sub_filter_types", and "xslt_types" directives; the
+ bugs had appeared in 0.7.9.
+
+ *) Bugfix: of recursive error_page for 500 status code.
+
+ *) Bugfix: now the ngx_http_realip_module sets address not for whole
+ keepalive connection, but for each request passed via the connection.
+
+
+Changes with nginx 0.7.9 12 Aug 2008
+
+ *) Change: now ngx_http_charset_module works by default with following
+ MIME types: text/html, text/css, text/xml, text/plain,
+ text/vnd.wap.wml, application/x-javascript, and application/rss+xml.
+
+ *) Feature: the "charset_types" and "addition_types" directives.
+
+ *) Feature: now the "gzip_types", "ssi_types", and "sub_filter_types"
+ directives use hash.
+
+ *) Feature: the ngx_cpp_test_module.
+
+ *) Feature: the "expires" directive supports daily time.
+
+ *) Feature: the ngx_http_xslt_module improvements and bug fixing.
+ Thanks to Denis F. Latypoff and Maxim Dounin.
+
+ *) Bugfix: the "log_not_found" directive did not work for index files
+ tests.
+
+ *) Bugfix: HTTPS connections might hang, if kqueue, epoll, rtsig, or
+ eventport methods were used; the bug had appeared in 0.7.7.
+
+ *) Bugfix: if the "server_name", "valid_referers", and "map" directives
+ used an "*.domain.tld" wildcard and exact name "domain.tld" was not
+ set, then the exact name was matched by the wildcard; the bug had
+ appeared in 0.3.18.
+
+
+Changes with nginx 0.7.8 04 Aug 2008
+
+ *) Feature: the ngx_http_xslt_module.
+
+ *) Feature: the "$arg_..." variables.
+
+ *) Feature: Solaris directio support.
+ Thanks to Ivan Debnar.
+
+ *) Bugfix: now if FastCGI server sends a "Location" header line without
+ status line, then nginx uses 302 status code.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.7.7 30 Jul 2008
+
+ *) Change: now the EAGAIN error returned by connect() is not considered
+ as temporary error.
+
+ *) Change: now the $ssl_client_cert variable value is a certificate with
+ TAB character intended before each line except first one; an
+ unchanged certificate is available in the $ssl_client_raw_cert
+ variable.
+
+ *) Feature: the "ask" parameter in the "ssl_verify_client" directive.
+
+ *) Feature: byte-range processing improvements.
+ Thanks to Maxim Dounin.
+
+ *) Feature: the "directio" directive.
+ Thanks to Jiang Hong.
+
+ *) Feature: MacOSX 10.5 sendfile() support.
+
+ *) Bugfix: now in MacOSX and Cygwin locations are tested in case
+ insensitive mode; however, the compare is provided by single-byte
+ locales only.
+
+ *) Bugfix: mail proxy SSL connections hanged, if select, poll, or
+ /dev/poll methods were used.
+
+ *) Bugfix: UTF-8 encoding usage in the ngx_http_autoindex_module.
+
+
+Changes with nginx 0.7.6 07 Jul 2008
+
+ *) Bugfix: now if variables are used in the "access_log" directive a
+ request root existence is always tested.
+
+ *) Bugfix: the ngx_http_flv_module did not support several values in a
+ query string.
+
+
+Changes with nginx 0.7.5 01 Jul 2008
+
+ *) Bugfixes in variables support in the "access_log" directive; the bugs
+ had appeared in 0.7.4.
+
+ *) Bugfix: nginx could not be built --without-http_gzip_module; the bug
+ had appeared in 0.7.3.
+ Thanks to Kirill A. Korinskiy.
+
+ *) Bugfix: if sub_filter and SSI were used together, then responses
+ might were transferred incorrectly.
+
+
+Changes with nginx 0.7.4 30 Jun 2008
+
+ *) Feature: variables support in the "access_log" directive.
+
+ *) Feature: the "open_log_file_cache" directive.
+
+ *) Feature: the -g switch.
+
+ *) Feature: the "Expect" request header line support.
+
+ *) Bugfix: large SSI inclusions might be truncated.
+
+
+Changes with nginx 0.7.3 23 Jun 2008
+
+ *) Change: the "rss" extension MIME type has been changed to
+ "application/rss+xml".
+
+ *) Change: now the "gzip_vary" directive turned on issues a
+ "Vary: Accept-Encoding" header line for uncompressed responses too.
+
+ *) Feature: now the "rewrite" directive does a redirect automatically if
+ the "https://" protocol is used.
+
+ *) Bugfix: the "proxy_pass" directive did not work with the HTTPS
+ protocol; the bug had appeared in 0.6.9.
+
+
+Changes with nginx 0.7.2 16 Jun 2008
+
+ *) Feature: now nginx supports EDH key exchange ciphers.
+
+ *) Feature: the "ssl_dhparam" directive.
+
+ *) Feature: the $ssl_client_cert variable.
+ Thanks to Manlio Perillo.
+
+ *) Bugfix: after changing URI via a "rewrite" directive nginx did not
+ search a new location; the bug had appeared in 0.7.1.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: nginx could not be built without PCRE library; the bug had
+ appeared in 0.7.1.
+
+ *) Bugfix: when a request to a directory was redirected with the slash
+ added, nginx dropped a query string from the original request.
+
+
+Changes with nginx 0.7.1 26 May 2008
+
+ *) Change: now locations are searched in a tree.
+
+ *) Change: the "optimize_server_names" directive was canceled due to the
+ "server_name_in_redirect" directive introduction.
+
+ *) Change: some long deprecated directives are not supported anymore.
+
+ *) Change: the "none" parameter in the "ssl_session_cache" directive;
+ now this is default parameter.
+ Thanks to Rob Mueller.
+
+ *) Bugfix: worker processes might not catch reconfiguration and log
+ rotation signals.
+
+ *) Bugfix: nginx could not be built on latest Fedora 9 Linux.
+ Thanks to Roxis.
+
+
+Changes with nginx 0.7.0 19 May 2008
+
+ *) Change: now the 0x00-0x1F, '"' and '\' characters are escaped as \xXX
+ in an access_log.
+ Thanks to Maxim Dounin.
+
+ *) Change: now nginx allows several "Host" request header line.
+
+ *) Feature: the "modified" flag in the "expires" directive.
+
+ *) Feature: the $uid_got and $uid_set variables may be used at any
+ request processing stage.
+
+ *) Feature: the $hostname variable.
+ Thanks to Andrei Nigmatulin.
+
+ *) Feature: DESTDIR support.
+ Thanks to Todd A. Fisher and Andras Voroskoi.
+
+ *) Bugfix: a segmentation fault might occur in worker process on Linux,
+ if keepalive was enabled.
+
+
+Changes with nginx 0.6.31 12 May 2008
+
+ *) Bugfix: nginx did not process FastCGI response if header was at the
+ end of FastCGI record; the bug had appeared in 0.6.2.
+ Thanks to Sergey Serov.
+
+ *) Bugfix: a segmentation fault might occur in worker process if a file
+ was deleted and the "open_file_cache_errors" directive was off.
+
+
+Changes with nginx 0.6.30 29 Apr 2008
+
+ *) Change: now if an "include" directive pattern does not match any
+ file, then nginx does not issue an error.
+
+ *) Feature: now the time in directives may be specified without spaces,
+ for example, "1h50m".
+
+ *) Bugfix: memory leaks if the "ssl_verify_client" directive was on.
+ Thanks to Chavelle Vincent.
+
+ *) Bugfix: the "sub_filter" directive might set text to change into
+ output.
+
+ *) Bugfix: the "error_page" directive did not take into account
+ arguments in redirected URI.
+
+ *) Bugfix: now nginx always opens files in binary mode under Cygwin.
+
+ *) Bugfix: nginx could not be built on OpenBSD; the bug had appeared in
+ 0.6.15.
+
+
+Changes with nginx 0.6.29 18 Mar 2008
+
+ *) Feature: the ngx_google_perftools_module.
+
+ *) Bugfix: the ngx_http_perl_module could not be built on 64-bit
+ platforms; the bug had appeared in 0.6.27.
+
+
+Changes with nginx 0.6.28 13 Mar 2008
+
+ *) Bugfix: the rtsig method could not be built; the bug had appeared in
+ 0.6.27.
+
+
+Changes with nginx 0.6.27 12 Mar 2008
+
+ *) Change: now by default the rtsig method is not built on
+ Linux 2.6.18+.
+
+ *) Change: now a request method is not changed while redirection to a
+ named location via an "error_page" directive.
+
+ *) Feature: the "resolver" and "resolver_timeout" directives in SMTP
+ proxy.
+
+ *) Feature: the "post_action" directive supports named locations.
+
+ *) Bugfix: a segmentation fault occurred in worker process, if a request
+ was redirected from proxy, FastCGI, or memcached location to static
+ named locations.
+
+ *) Bugfix: browsers did not repeat SSL handshake if there is no valid
+ client certificate in first handshake.
+ Thanks to Alexander V. Inyukhin.
+
+ *) Bugfix: if response code 495-497 was redirected via an "error_page"
+ directive without code change, then nginx tried to allocate too many
+ memory.
+
+ *) Bugfix: memory leak in long-lived non buffered connections.
+
+ *) Bugfix: memory leak in resolver.
+
+ *) Bugfix: a segmentation fault occurred in worker process, if a request
+ was redirected from proxy, FastCGI, or memcached location to static
+ named locations.
+
+ *) Bugfix: in the $proxy_host and $proxy_port variables caching.
+ Thanks to Sergey Bochenkov.
+
+ *) Bugfix: a "proxy_pass" directive with variables used incorrectly the
+ same port as in another "proxy_pass" directive with the same host
+ name and without variables.
+ Thanks to Sergey Bochenkov.
+
+ *) Bugfix: an alert "sendmsg() failed (9: Bad file descriptor)" on some
+ 64-bit platforms while reconfiguration.
+
+ *) Bugfix: a segmentation fault occurred in worker process, if empty
+ stub block was used second time in SSI.
+
+ *) Bugfix: in copying URI part contained escaped symbols into arguments.
+
+
+Changes with nginx 0.6.26 11 Feb 2008
+
+ *) Bugfix: the "proxy_store" and "fastcgi_store" directives did not
+ check a response length.
+
+ *) Bugfix: a segmentation fault occurred in worker process, if big value
+ was used in a "expires" directive.
+ Thanks to Joaquin Cuenca Abela.
+
+ *) Bugfix: nginx incorrectly detected cache line size on Pentium 4.
+ Thanks to Gena Makhomed.
+
+ *) Bugfix: in proxied or FastCGI subrequests a client original method
+ was used instead of the GET method.
+
+ *) Bugfix: socket leak in HTTPS mode if deferred accept was used.
+ Thanks to Ben Maurer.
+
+ *) Bugfix: nginx issued the bogus error message "SSL_shutdown() failed
+ (SSL: )"; the bug had appeared in 0.6.23.
+
+ *) Bugfix: in HTTPS mode requests might fail with the "bad write retry"
+ error; the bug had appeared in 0.6.23.
+
+
+Changes with nginx 0.6.25 08 Jan 2008
+
+ *) Change: now the "server_name_in_redirect" directive is used instead
+ of the "server_name" directive's special "*" parameter.
+
+ *) Change: now wildcard and regex names can be used as main name in a
+ "server_name" directive.
+
+ *) Change: the "satisfy_any" directive was replaced by the "satisfy"
+ directive.
+
+ *) Workaround: old worker processes might hog CPU after reconfiguration
+ if they was run under Linux OpenVZ.
+
+ *) Feature: the "min_delete_depth" directive.
+
+ *) Bugfix: the COPY and MOVE methods did not work with single files.
+
+ *) Bugfix: the ngx_http_gzip_static_module did not allow the
+ ngx_http_dav_module to work; the bug had appeared in 0.6.23.
+
+ *) Bugfix: socket leak in HTTPS mode if deferred accept was used.
+ Thanks to Ben Maurer.
+
+ *) Bugfix: nginx could not be built without PCRE library; the bug had
+ appeared in 0.6.23.
+
+
+Changes with nginx 0.6.24 27 Dec 2007
+
+ *) Bugfix: a segmentation fault might occur in worker process if HTTPS
+ was used; the bug had appeared in 0.6.23.
+
+
+Changes with nginx 0.6.23 27 Dec 2007
+
+ *) Change: the "off" parameter in the "ssl_session_cache" directive; now
+ this is default parameter.
+
+ *) Change: the "open_file_cache_retest" directive was renamed to the
+ "open_file_cache_valid".
+
+ *) Feature: the "open_file_cache_min_uses" directive.
+
+ *) Feature: the ngx_http_gzip_static_module.
+
+ *) Feature: the "gzip_disable" directive.
+
+ *) Feature: the "memcached_pass" directive may be used inside the "if"
+ block.
+
+ *) Bugfix: a segmentation fault occurred in worker process, if the
+ "memcached_pass" and "if" directives were used in the same location.
+
+ *) Bugfix: if a "satisfy_any on" directive was used and not all access
+ and auth modules directives were set, then other given access and
+ auth directives were not tested;
+
+ *) Bugfix: regex parameters in a "valid_referers" directive were not
+ inherited from previous level.
+
+ *) Bugfix: a "post_action" directive did run if a request was completed
+ with 499 status code.
+
+ *) Bugfix: optimization of 16K buffer usage in a SSL connection.
+ Thanks to Ben Maurer.
+
+ *) Bugfix: the STARTTLS in SMTP mode did not work.
+ Thanks to Oleg Motienko.
+
+ *) Bugfix: in HTTPS mode requests might fail with the "bad write retry"
+ error; the bug had appeared in 0.5.13.
+
+
+Changes with nginx 0.6.22 19 Dec 2007
+
+ *) Change: now all ngx_http_perl_module methods return values copied to
+ perl's allocated memory.
+
+ *) Bugfix: if nginx was built with ngx_http_perl_module, the perl before
+ 5.8.6 was used, and perl supported threads, then during
+ reconfiguration the master process aborted; the bug had appeared in
+ 0.5.9.
+ Thanks to Boris Zhmurov.
+
+ *) Bugfix: the ngx_http_perl_module methods may get invalid values of
+ the regex captures.
+
+ *) Bugfix: a segmentation fault occurred in worker process, if the
+ $r->has_request_body() method was called for a request whose small
+ request body was already received.
+
+ *) Bugfix: large_client_header_buffers did not freed before going to
+ keep-alive state.
+ Thanks to Olexander Shtepa.
+
+ *) Bugfix: the last address was missed in the $upstream_addr variable;
+ the bug had appeared in 0.6.18.
+
+ *) Bugfix: the "fastcgi_catch_stderr" directive did return error code;
+ now it returns 502 code, that can be rerouted to a next server using
+ the "fastcgi_next_upstream invalid_header" directive.
+
+ *) Bugfix: a segmentation fault occurred in master process if the
+ "fastcgi_catch_stderr" directive was used; the bug had appeared in
+ 0.6.10.
+ Thanks to Manlio Perillo.
+
+
+Changes with nginx 0.6.21 03 Dec 2007
+
+ *) Change: if variable values used in a "proxy_pass" directive contain
+ IP-addresses only, then a "resolver" directive is not mandatory.
+
+ *) Bugfix: a segmentation fault might occur in worker process if a
+ "proxy_pass" directive with URI-part was used; the bug had appeared
+ in 0.6.19.
+
+ *) Bugfix: if resolver was used on platform that does not support
+ kqueue, then nginx issued an alert "name is out of response".
+ Thanks to Andrei Nigmatulin.
+
+ *) Bugfix: if the $server_protocol was used in FastCGI parameters and a
+ request line length was near to the "client_header_buffer_size"
+ directive value, then nginx issued an alert "fastcgi: the request
+ record is too big".
+
+ *) Bugfix: if a plain text HTTP/0.9 version request was made to HTTPS
+ server, then nginx returned usual response.
+
+
+Changes with nginx 0.6.20 28 Nov 2007
+
+ *) Bugfix: a segmentation fault might occur in worker process if a
+ "proxy_pass" directive with URI-part was used; the bug had appeared
+ in 0.6.19.
+
+
+Changes with nginx 0.6.19 27 Nov 2007
+
+ *) Bugfix: the 0.6.18 version could not be built.
+
+
+Changes with nginx 0.6.18 27 Nov 2007
+
+ *) Change: now the ngx_http_userid_module adds start time microseconds
+ to the cookie field contains a pid value.
+
+ *) Change: now the full request line instead of URI only is written to
+ error_log.
+
+ *) Feature: variables support in the "proxy_pass" directive.
+
+ *) Feature: the "resolver" and "resolver_timeout" directives.
+
+ *) Feature: now the directive "add_header last-modified ''" deletes a
+ "Last-Modified" response header line.
+
+ *) Bugfix: the "limit_rate" directive did not allow to use full
+ throughput, even if limit value was very high.
+
+
+Changes with nginx 0.6.17 15 Nov 2007
+
+ *) Feature: the "If-Range" request header line support.
+ Thanks to Alexander V. Inyukhin.
+
+ *) Bugfix: URL double escaping in a redirect of the "msie_refresh"
+ directive; the bug had appeared in 0.6.4.
+
+ *) Bugfix: the "autoindex" directive did not work with the "alias /"
+ directive.
+
+ *) Bugfix: a segmentation fault might occur in worker process if
+ subrequests were used.
+
+ *) Bugfix: the big responses may be transferred truncated if SSL and
+ gzip were used.
+
+ *) Bugfix: the $status variable was equal to 0 if a proxied server
+ returned response in HTTP/0.9 version.
+
+
+Changes with nginx 0.6.16 29 Oct 2007
+
+ *) Change: now the uname(2) is used on Linux instead of procfs.
+ Thanks to Ilya Novikov.
+
+ *) Bugfix: if the "?" character was in a "error_page" directive, then it
+ was escaped in a proxied request; the bug had appeared in 0.6.11.
+
+ *) Bugfix: compatibility with mget.
+
+
+Changes with nginx 0.6.15 22 Oct 2007
+
+ *) Feature: Cygwin compatibility.
+ Thanks to Vladimir Kutakov.
+
+ *) Feature: the "merge_slashes" directive.
+
+ *) Feature: the "gzip_vary" directive.
+
+ *) Feature: the "server_tokens" directive.
+
+ *) Bugfix: nginx did not unescape URI in the "include" SSI command.
+
+ *) Bugfix: the segmentation fault was occurred on start or while
+ reconfiguration if variable was used in the "charset" or
+ "source_charset" directives.
+
+ *) Bugfix: nginx returned the 400 response on requests like
+ "GET http://www.domain.com HTTP/1.0".
+ Thanks to James Oakley.
+
+ *) Bugfix: if request with request body was redirected using the
+ "error_page" directive, then nginx tried to read the request body
+ again; the bug had appeared in 0.6.7.
+
+ *) Bugfix: a segmentation fault occurred in worker process if no
+ server_name was explicitly defined for server processing request; the
+ bug had appeared in 0.6.7.
+
+
+Changes with nginx 0.6.14 15 Oct 2007
+
+ *) Change: now by default the "echo" SSI command uses entity encoding.
+
+ *) Feature: the "encoding" parameter in the "echo" SSI command.
+
+ *) Feature: the "access_log" directive may be used inside the
+ "limit_except" block.
+
+ *) Bugfix: if all upstream servers were failed, then all servers had got
+ weight the was equal one until servers became alive; the bug had
+ appeared in 0.6.6.
+
+ *) Bugfix: a segmentation fault occurred in worker process if
+ $date_local and $date_gmt were used outside the
+ ngx_http_ssi_filter_module.
+
+ *) Bugfix: a segmentation fault might occur in worker process if debug
+ log was enabled.
+ Thanks to Andrei Nigmatulin.
+
+ *) Bugfix: ngx_http_memcached_module did not set
+ $upstream_response_time.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: a worker process may got caught in an endless loop, if the
+ memcached was used.
+
+ *) Bugfix: nginx supported low case only "close" and "keep-alive" values
+ in the "Connection" request header line; the bug had appeared in
+ 0.6.11.
+
+ *) Bugfix: sub_filter did not work with empty substitution.
+
+ *) Bugfix: in sub_filter parsing.
+
+
+Changes with nginx 0.6.13 24 Sep 2007
+
+ *) Bugfix: nginx did not close directory file on HEAD request if
+ autoindex was used.
+ Thanks to Arkadiusz Patyk.
+
+
+Changes with nginx 0.6.12 21 Sep 2007
+
+ *) Change: mail proxy was split on three modules: pop3, imap and smtp.
+
+ *) Feature: the --without-mail_pop3_module, --without-mail_imap_module,
+ and --without-mail_smtp_module configuration parameters.
+
+ *) Feature: the "smtp_greeting_delay" and "smtp_client_buffer"
+ directives of the ngx_mail_smtp_module.
+
+ *) Bugfix: the trailing wildcards did not work; the bug had appeared in
+ 0.6.9.
+
+ *) Bugfix: nginx could not start on Solaris if the shared PCRE library
+ located in non-standard place was used.
+
+ *) Bugfix: the "proxy_hide_header" and "fastcgi_hide_header" directives
+ did not hide response header lines whose name was longer than 32
+ characters.
+ Thanks to Manlio Perillo.
+
+
+Changes with nginx 0.6.11 11 Sep 2007
+
+ *) Bugfix: active connection counter always increased if mail proxy was
+ used.
+
+ *) Bugfix: if backend returned response header only using non-buffered
+ proxy, then nginx closed backend connection on timeout.
+
+ *) Bugfix: nginx did not support several "Connection" request header
+ lines.
+
+ *) Bugfix: if the "max_fails" was set for upstream server, then after
+ first failure server weight was always one; the bug had appeared in
+ 0.6.6.
+
+
+Changes with nginx 0.6.10 03 Sep 2007
+
+ *) Feature: the "open_file_cache", "open_file_cache_retest", and
+ "open_file_cache_errors" directives.
+
+ *) Bugfix: socket leak; the bug had appeared in 0.6.7.
+
+ *) Bugfix: a charset set by the "charset" directive was not appended to
+ the "Content-Type" header set by $r->send_http_header().
+
+ *) Bugfix: a segmentation fault might occur in worker process if
+ /dev/poll method was used.
+
+
+Changes with nginx 0.6.9 28 Aug 2007
+
+ *) Bugfix: a worker process may got caught in an endless loop, if the
+ HTTPS protocol was used; the bug had appeared in 0.6.7.
+
+ *) Bugfix: if server listened on two addresses or ports and trailing
+ wildcard was used, then nginx did not run.
+
+ *) Bugfix: the "ip_hash" directive might incorrectly mark servers as
+ down.
+
+ *) Bugfix: nginx could not be built on amd64; the bug had appeared in
+ 0.6.8.
+
+
+Changes with nginx 0.6.8 20 Aug 2007
+
+ *) Change: now nginx tries to set the "worker_priority",
+ "worker_rlimit_nofile", "worker_rlimit_core", and
+ "worker_rlimit_sigpending" without super-user privileges.
+
+ *) Change: now nginx escapes space and "%" in request to a mail proxy
+ authentication server.
+
+ *) Change: now nginx escapes "%" in $memcached_key variable.
+
+ *) Bugfix: nginx used path relative to configuration prefix for
+ non-absolute configuration file path specified in the "-c" key; the
+ bug had appeared in 0.6.6.
+
+ *) Bugfix: nginx did not work on FreeBSD/sparc64.
+
+
+Changes with nginx 0.6.7 15 Aug 2007
+
+ *) Change: now the paths specified in the "include",
+ "auth_basic_user_file", "perl_modules", "ssl_certificate",
+ "ssl_certificate_key", and "ssl_client_certificate" directives are
+ relative to directory of nginx configuration file nginx.conf, but not
+ to nginx prefix directory.
+
+ *) Change: the --sysconfdir=PATH option in configure was canceled.
+
+ *) Change: the special make target "upgrade1" was defined for online
+ upgrade of 0.1.x versions.
+
+ *) Feature: the "server_name" and "valid_referers" directives support
+ regular expressions.
+
+ *) Feature: the "server" directive in the "upstream" context supports
+ the "backup" parameter.
+
+ *) Feature: the ngx_http_perl_module supports the
+ $r->discard_request_body.
+
+ *) Feature: the "add_header Last-Modified ..." directive changes the
+ "Last-Modified" response header line.
+
+ *) Bugfix: if a response different than 200 was returned to a request
+ with body and connection went to the keep-alive state after the
+ request, then nginx returned 400 for the next request.
+
+ *) Bugfix: a segmentation fault occurred in worker process if invalid
+ address was set in the "auth_http" directive.
+
+ *) Bugfix: now nginx uses default listen backlog value 511 on all
+ platforms except FreeBSD.
+ Thanks to Jiang Hong.
+
+ *) Bugfix: a worker process may got caught in an endless loop, if a
+ "server" inside "upstream" block was marked as "down"; the bug had
+ appeared in 0.6.6.
+
+ *) Bugfix: now Solaris sendfilev() is not used to transfer the client
+ request body to FastCGI-server via the unix domain socket.
+
+
+Changes with nginx 0.6.6 30 Jul 2007
+
+ *) Feature: the --sysconfdir=PATH option in configure.
+
+ *) Feature: named locations.
+
+ *) Feature: the $args variable can be set with the "set" directive.
+
+ *) Feature: the $is_args variable.
+
+ *) Bugfix: fair big weight upstream balancer.
+
+ *) Bugfix: if a client has closed connection to mail proxy then nginx
+ might not close connection to backend.
+
+ *) Bugfix: if the same host without specified port was used as backend
+ for HTTP and HTTPS, then nginx used only one port - 80 or 443.
+
+ *) Bugfix: fix building on Solaris/amd64 by Sun Studio 11 and early
+ versions; the bug had appeared in 0.6.4.
+
+
+Changes with nginx 0.6.5 23 Jul 2007
+
+ *) Feature: $nginx_version variable.
+ Thanks to Nick S. Grechukh.
+
+ *) Feature: the mail proxy supports AUTHENTICATE in IMAP mode.
+ Thanks to Maxim Dounin.
+
+ *) Feature: the mail proxy supports STARTTLS in SMTP mode.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: now nginx escapes space in $memcached_key variable.
+
+ *) Bugfix: nginx was incorrectly built by Sun Studio on Solaris/amd64.
+ Thanks to Jiang Hong.
+
+ *) Bugfix: of minor potential bugs.
+ Thanks to Coverity's Scan.
+
+
+Changes with nginx 0.6.4 17 Jul 2007
+
+ *) Security: the "msie_refresh" directive allowed XSS.
+ Thanks to Maxim Boguk.
+
+ *) Change: the "proxy_store" and "fastcgi_store" directives were
+ changed.
+
+ *) Feature: the "proxy_store_access" and "fastcgi_store_access"
+ directives.
+
+ *) Bugfix: nginx did not work on Solaris/sparc64 if it was built by Sun
+ Studio.
+ Thanks to Andrei Nigmatulin.
+
+ *) Workaround: for Sun Studio 12.
+ Thanks to Jiang Hong.
+
+
+Changes with nginx 0.6.3 12 Jul 2007
+
+ *) Feature: the "proxy_store" and "fastcgi_store" directives.
+
+ *) Bugfix: a segmentation fault might occur in worker process if the
+ "auth_http_header" directive was used.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: a segmentation fault occurred in worker process if the
+ CRAM-MD5 authentication method was used, but it was not enabled.
+
+ *) Bugfix: a segmentation fault might occur in worker process when the
+ HTTPS protocol was used in the "proxy_pass" directive.
+
+ *) Bugfix: a segmentation fault might occur in worker process if the
+ eventport method was used.
+
+ *) Bugfix: the "proxy_ignore_client_abort" and
+ "fastcgi_ignore_client_abort" directives did not work; the bug had
+ appeared in 0.5.13.
+
+
+Changes with nginx 0.6.2 09 Jul 2007
+
+ *) Bugfix: if the FastCGI header was split in records, then nginx passed
+ garbage in the header to a client.
+
+
+Changes with nginx 0.6.1 17 Jun 2007
+
+ *) Bugfix: in SSI parsing.
+
+ *) Bugfix: if remote SSI subrequest was used, then posterior local file
+ subrequest might transferred to client in wrong order.
+
+ *) Bugfix: large SSI inclusions buffered in temporary files were
+ truncated.
+
+ *) Bugfix: the perl $$ variable value in ngx_http_perl_module was equal
+ to the master process identification number.
+
+
+Changes with nginx 0.6.0 14 Jun 2007
+
+ *) Feature: the "server_name", "map", and "valid_referers" directives
+ support the "www.example.*" wildcards.
+
+
+Changes with nginx 0.5.25 11 Jun 2007
+
+ *) Bugfix: nginx could not be built with the
+ --without-http_rewrite_module parameter; the bug had appeared in
+ 0.5.24.
+
+
+Changes with nginx 0.5.24 06 Jun 2007
+
+ *) Security: the "ssl_verify_client" directive did not work if request
+ was made using HTTP/0.9.
+
+ *) Bugfix: a part of response body might be passed uncompressed if gzip
+ was used; the bug had appeared in 0.5.23.
+
+
+Changes with nginx 0.5.23 04 Jun 2007
+
+ *) Feature: the ngx_http_ssl_module supports Server Name Indication TLS
+ extension.
+
+ *) Feature: the "fastcgi_catch_stderr" directive.
+ Thanks to Nick S. Grechukh, OWOX project.
+
+ *) Bugfix: a segmentation fault occurred in master process if two
+ virtual servers should bind() to the overlapping ports.
+
+ *) Bugfix: if nginx was built with ngx_http_perl_module and perl
+ supported threads, then during second reconfiguration the error
+ messages "panic: MUTEX_LOCK" and "perl_parse() failed" were issued.
+
+ *) Bugfix: in the HTTPS protocol in the "proxy_pass" directive.
+
+
+Changes with nginx 0.5.22 29 May 2007
+
+ *) Bugfix: a big request body might not be passed to backend; the bug
+ had appeared in 0.5.21.
+
+
+Changes with nginx 0.5.21 28 May 2007
+
+ *) Bugfix: if server has more than about ten locations, then regex
+ locations might be chosen not in that order as they were specified.
+
+ *) Bugfix: a worker process may got caught in an endless loop on 64-bit
+ platform, if the 33-rd or next in succession backend has failed.
+ Thanks to Anton Povarov.
+
+ *) Bugfix: a bus error might occur on Solaris/sparc64 if the PCRE
+ library was used.
+ Thanks to Andrei Nigmatulin.
+
+ *) Bugfix: in the HTTPS protocol in the "proxy_pass" directive.
+
+
+Changes with nginx 0.5.20 07 May 2007
+
+ *) Feature: the "sendfile_max_chunk" directive.
+
+ *) Feature: the "$http_...", "$sent_http_...", and "$upstream_http_..."
+ variables may be changed using the "set" directive.
+
+ *) Bugfix: a segmentation fault might occur in worker process if the SSI
+ command 'if expr="$var = /"' was used.
+
+ *) Bugfix: trailing boundary of multipart range response was transferred
+ incorrectly.
+ Thanks to Evan Miller.
+
+ *) Bugfix: nginx did not work on Solaris/sparc64 if it was built by Sun
+ Studio.
+ Thanks to Andrei Nigmatulin.
+
+ *) Bugfix: the ngx_http_perl_module could not be built by Solaris make.
+ Thanks to Andrei Nigmatulin.
+
+
+Changes with nginx 0.5.19 24 Apr 2007
+
+ *) Change: now the $request_time variable has millisecond precision.
+
+ *) Change: the method $r->rflush of ngx_http_perl_module was renamed to
+ the $r->flush.
+
+ *) Feature: the $upstream_addr variable.
+
+ *) Feature: the "proxy_headers_hash_max_size" and
+ "proxy_headers_hash_bucket_size" directives.
+ Thanks to Volodymyr Kostyrko.
+
+ *) Bugfix: the files more than 2G could not be transferred using
+ sendfile and limit_rate on 64-bit platforms.
+
+ *) Bugfix: the files more than 2G could not be transferred using
+ sendfile on 64-bit Linux.
+
+
+Changes with nginx 0.5.18 19 Apr 2007
+
+ *) Feature: the ngx_http_sub_filter_module.
+
+ *) Feature: the "$upstream_http_..." variables.
+
+ *) Feature: now the $upstream_status and $upstream_response_time
+ variables keep data about all upstreams before X-Accel-Redirect.
+
+ *) Bugfix: a segmentation fault occurred in master process after first
+ reconfiguration and receiving any signal if nginx was built with
+ ngx_http_perl_module and perl did not support multiplicity; the bug
+ had appeared in 0.5.9.
+
+ *) Bugfix: if perl did not support multiplicity, then after
+ reconfiguration perl code did not work; the bug had appeared in
+ 0.3.38.
+
+
+Changes with nginx 0.5.17 02 Apr 2007
+
+ *) Change: now nginx always returns the 405 status for the TRACE method.
+
+ *) Feature: now nginx supports the "include" directive inside the
+ "types" block.
+
+ *) Bugfix: the $document_root variable usage in the "root" and "alias"
+ directives is disabled: this caused recursive stack overflow.
+
+ *) Bugfix: in the HTTPS protocol in the "proxy_pass" directive.
+
+ *) Bugfix: in some cases non-cacheable variables (such as $uri variable)
+ returned old cached value.
+
+
+Changes with nginx 0.5.16 26 Mar 2007
+
+ *) Bugfix: the C-class network was not used as hash key in the "ip_hash"
+ directive.
+ Thanks to Pavel Yarkovoy.
+
+ *) Bugfix: a segmentation fault might occur in worker process if a
+ charset was set in the "Content-Type" header line and the line has
+ trailing ";"; the bug had appeared in 0.3.50.
+
+ *) Bugfix: the "[alert] zero size buf" error when FastCGI server was
+ used and a request body written in a temporary file was multiple of
+ 32K.
+
+ *) Bugfix: nginx could not be built on Solaris without the --with-debug
+ option; the bug had appeared in 0.5.15.
+
+
+Changes with nginx 0.5.15 19 Mar 2007
+
+ *) Feature: the mail proxy supports authenticated SMTP proxying and the
+ "smtp_auth", "smtp_capabilities", and "xclient" directives.
+ Thanks to Anton Yuzhaninov and Maxim Dounin.
+
+ *) Feature: now the keep-alive connections are closed just after
+ receiving the reconfiguration signal.
+
+ *) Change: the "imap" and "auth" directives were renamed to the "mail"
+ and "pop3_auth" directives.
+
+ *) Bugfix: a segmentation fault occurred in worker process if the
+ CRAM-MD5 authentication method was used and the APOP method was
+ disabled.
+
+ *) Bugfix: if the "starttls only" directive was used in POP3 protocol,
+ then nginx allowed authentication without switching to the SSL mode.
+
+ *) Bugfix: worker processes did not exit after reconfiguration and did
+ not rotate logs if the eventport method was used.
+
+ *) Bugfix: a worker process may got caught in an endless loop, if the
+ "ip_hash" directive was used.
+
+ *) Bugfix: now nginx does not log some alerts if eventport or /dev/poll
+ methods are used.
+
+
+Changes with nginx 0.5.14 23 Feb 2007
+
+ *) Bugfix: nginx ignored superfluous closing "}" in the end of
+ configuration file.
+
+
+Changes with nginx 0.5.13 19 Feb 2007
+
+ *) Feature: the COPY and MOVE methods.
+
+ *) Bugfix: the ngx_http_realip_module set garbage for requests passed
+ via keep-alive connection.
+
+ *) Bugfix: nginx did not work on big-endian 64-bit Linux.
+ Thanks to Andrei Nigmatulin.
+
+ *) Bugfix: now when IMAP/POP3 proxy receives too long command it closes
+ the connection right away, but not after timeout.
+
+ *) Bugfix: if the "epoll" method was used and a client closed a
+ connection prematurely, then nginx closed the connection after a send
+ timeout only.
+
+ *) Bugfix: nginx could not be built on platforms different from i386,
+ amd64, sparc, and ppc; the bug had appeared in 0.5.8.
+
+
+Changes with nginx 0.5.12 12 Feb 2007
+
+ *) Bugfix: nginx could not be built on platforms different from i386,
+ amd64, sparc, and ppc; the bug had appeared in 0.5.8.
+
+ *) Bugfix: a segmentation fault might occur in worker process if the
+ temporary files were used while working with FastCGI server; the bug
+ had appeared in 0.5.8.
+
+ *) Bugfix: a segmentation fault might occur in worker process if the
+ $fastcgi_script_name variable was logged.
+
+ *) Bugfix: ngx_http_perl_module could not be built on Solaris.
+
+
+Changes with nginx 0.5.11 05 Feb 2007
+
+ *) Feature: now configure detects system PCRE library in MacPorts.
+ Thanks to Chris McGrath.
+
+ *) Bugfix: the response was incorrect if several ranges were requested;
+ the bug had appeared in 0.5.6.
+
+ *) Bugfix: the "create_full_put_path" directive could not create the
+ intermediate directories if no "dav_access" directive was set.
+ Thanks to Evan Miller.
+
+ *) Bugfix: the "0" response code might be logged in the access_log
+ instead of the "400" and "408" error codes.
+
+ *) Bugfix: a segmentation fault might occur in worker process if nginx
+ was built with -O2 optimization.
+
+
+Changes with nginx 0.5.10 26 Jan 2007
+
+ *) Bugfix: while online executable file upgrade the new master process
+ did not inherit the listening sockets; the bug had appeared in 0.5.9.
+
+ *) Bugfix: a segmentation fault might occur in worker process if nginx
+ was built with -O2 optimization; the bug had appeared in 0.5.1.
+
+
+Changes with nginx 0.5.9 25 Jan 2007
+
+ *) Change: now the ngx_http_memcached_module uses the $memcached_key
+ variable value as a key.
+
+ *) Feature: the $memcached_key variable.
+
+ *) Feature: the "clean" parameter in the "client_body_in_file_only"
+ directive.
+
+ *) Feature: the "env" directive.
+
+ *) Feature: the "sendfile" directive is available inside the "if" block.
+
+ *) Feature: now on failure of the writing to access nginx logs a message
+ to error_log, but not more often than once a minute.
+
+ *) Bugfix: the "access_log off" directive did not always turn off the
+ logging.
+
+
+Changes with nginx 0.5.8 19 Jan 2007
+
+ *) Bugfix: a segmentation fault might occur if
+ "client_body_in_file_only on" was used and a request body was small.
+
+ *) Bugfix: a segmentation fault occurred if
+ "client_body_in_file_only on" and "proxy_pass_request_body off" or
+ "fastcgi_pass_request_body off" directives were used, and nginx
+ switched to a next upstream.
+
+ *) Bugfix: if the "proxy_buffering off" directive was used and a client
+ connection was non-active, then the connection was closed after send
+ timeout; the bug had appeared in 0.4.7.
+
+ *) Bugfix: if the "epoll" method was used and a client closed a
+ connection prematurely, then nginx closed the connection after a send
+ timeout only.
+
+ *) Bugfix: the "[alert] zero size buf" error when FastCGI server was
+ used.
+
+ *) Bugfixes in the "limit_zone" directive.
+
+
+Changes with nginx 0.5.7 15 Jan 2007
+
+ *) Feature: the ssl_session_cache storage optimization.
+
+ *) Bugfixes in the "ssl_session_cache" and "limit_zone" directives.
+
+ *) Bugfix: the segmentation fault was occurred on start or while
+ reconfiguration if the "ssl_session_cache" or "limit_zone" directives
+ were used on 64-bit platforms.
+
+ *) Bugfix: a segmentation fault occurred if the "add_before_body" or
+ "add_after_body" directives were used and there was no "Content-Type"
+ header line in response.
+
+ *) Bugfix: the OpenSSL library was always built with the threads
+ support.
+ Thanks to Den Ivanov.
+
+ *) Bugfix: the PCRE-6.5+ library and the icc compiler compatibility.
+
+
+Changes with nginx 0.5.6 09 Jan 2007
+
+ *) Change: now the ngx_http_index_module ignores all methods except the
+ GET, HEAD, and POST methods.
+
+ *) Feature: the ngx_http_limit_zone_module.
+
+ *) Feature: the $binary_remote_addr variable.
+
+ *) Feature: the "ssl_session_cache" directives of the
+ ngx_http_ssl_module and ngx_imap_ssl_module.
+
+ *) Feature: the DELETE method supports recursive removal.
+
+ *) Bugfix: the byte-ranges were transferred incorrectly if the
+ $r->sendfile() was used.
+
+
+Changes with nginx 0.5.5 24 Dec 2006
+
+ *) Change: the -v switch does not show compiler information any more.
+
+ *) Feature: the -V switch.
+
+ *) Feature: the "worker_rlimit_core" directive supports size in K, M,
+ and G.
+
+ *) Bugfix: the nginx.pm module now could be installed by an unprivileged
+ user.
+
+ *) Bugfix: a segmentation fault might occur if the $r->request_body or
+ $r->request_body_file methods were used.
+
+ *) Bugfix: the ppc platform specific bugs.
+
+
+Changes with nginx 0.5.4 15 Dec 2006
+
+ *) Feature: the "perl" directive may be used inside the "limit_except"
+ block.
+
+ *) Bugfix: the ngx_http_dav_module required the "Date" request header
+ line for the DELETE method.
+
+ *) Bugfix: if one only parameter was used in the "dav_access" directive,
+ then nginx might report about configuration error.
+
+ *) Bugfix: a segmentation fault might occur if the $host variable was
+ used; the bug had appeared in 0.4.14.
+
+
+Changes with nginx 0.5.3 13 Dec 2006
+
+ *) Feature: the ngx_http_perl_module supports the $r->status,
+ $r->log_error, and $r->sleep methods.
+
+ *) Feature: the $r->variable method supports variables that do not exist
+ in nginx configuration.
+
+ *) Bugfix: the $r->has_request_body method did not work.
+
+
+Changes with nginx 0.5.2 11 Dec 2006
+
+ *) Bugfix: if the "proxy_pass" directive used the name of the "upstream"
+ block, then nginx tried to resolve the name; the bug had appeared in
+ 0.5.1.
+
+
+Changes with nginx 0.5.1 11 Dec 2006
+
+ *) Bugfix: the "post_action" directive might not run after a
+ unsuccessful completion of a request.
+
+ *) Workaround: for Eudora for Mac; the bug had appeared in 0.4.11.
+ Thanks to Bron Gondwana.
+
+ *) Bugfix: if the "upstream" name was used in the "fastcgi_pass", then
+ the message "no port in upstream" was issued; the bug had appeared in
+ 0.5.0.
+
+ *) Bugfix: if the "proxy_pass" and "fastcgi_pass" directives used the
+ same servers but different ports, then these directives uses the
+ first described port; the bug had appeared in 0.5.0.
+
+ *) Bugfix: if the "proxy_pass" and "fastcgi_pass" directives used the
+ unix domain sockets, then these directives used first described
+ socket; the bug had appeared in 0.5.0.
+
+ *) Bugfix: ngx_http_auth_basic_module ignored the user if it was in the
+ last line in the password file and there was no the carriage return,
+ the line feed, or the ":" symbol after the password.
+
+ *) Bugfix: the $upstream_response_time variable might be equal to
+ "0.000", although response time was more than 1 millisecond.
+
+
+Changes with nginx 0.5.0 04 Dec 2006
+
+ *) Change: the parameters in the "%name" form in the "log_format"
+ directive are not supported anymore.
+
+ *) Change: the "proxy_upstream_max_fails",
+ "proxy_upstream_fail_timeout", "fastcgi_upstream_max_fails",
+ "fastcgi_upstream_fail_timeout", "memcached_upstream_max_fails", and
+ "memcached_upstream_fail_timeout" directives are not supported
+ anymore.
+
+ *) Feature: the "server" directive in the "upstream" context supports
+ the "max_fails", "fail_timeout", and "down" parameters.
+
+ *) Feature: the "ip_hash" directive inside the "upstream" block.
+
+ *) Feature: the WAIT status in the "Auth-Status" header line of the
+ IMAP/POP3 proxy authentication server response.
+
+ *) Bugfix: nginx could not be built on 64-bit platforms; the bug had
+ appeared in 0.4.14.
+
+
+Changes with nginx 0.4.14 27 Nov 2006
+
+ *) Feature: the "proxy_pass_error_message" directive in IMAP/POP3 proxy.
+
+ *) Feature: now configure detects system PCRE library on FreeBSD, Linux,
+ and NetBSD.
+
+ *) Bugfix: ngx_http_perl_module did not work with perl built with the
+ threads support; the bug had appeared in 0.3.38.
+
+ *) Bugfix: ngx_http_perl_module did not work if perl was called
+ recursively.
+
+ *) Bugfix: nginx ignored a host name in a request line.
+
+ *) Bugfix: a worker process may got caught in an endless loop, if a
+ FastCGI server sent too many data to the stderr.
+
+ *) Bugfix: the $upstream_response_time variable may be negative if the
+ system time was changed backward.
+
+ *) Bugfix: the "Auth-Login-Attempt" parameter was not sent to IMAP/POP3
+ proxy authentication server when POP3 was used.
+
+ *) Bugfix: a segmentation fault might occur if connect to IMAP/POP3
+ proxy authentication server failed.
+
+
+Changes with nginx 0.4.13 15 Nov 2006
+
+ *) Feature: the "proxy_pass" directive may be used inside the
+ "limit_except" block.
+
+ *) Feature: the "limit_except" directive supports all WebDAV methods.
+
+ *) Bugfix: if the "add_before_body" directive was used without the
+ "add_after_body" directive, then a response did not transferred
+ complete.
+
+ *) Bugfix: a large request body did not receive if the epoll method and
+ the deferred accept() were used.
+
+ *) Bugfix: a charset could not be set for ngx_http_autoindex_module
+ responses; the bug had appeared in 0.3.50.
+
+ *) Bugfix: the "[alert] zero size buf" error when FastCGI server was
+ used;
+
+ *) Bugfix: the --group= configuration parameter was ignored.
+ Thanks to Thomas Moschny.
+
+ *) Bugfix: the 50th subrequest in SSI response did not work; the bug had
+ appeared in 0.3.50.
+
+
+Changes with nginx 0.4.12 31 Oct 2006
+
+ *) Feature: the ngx_http_perl_module supports the $r->variable method.
+
+ *) Bugfix: if a big static file was included using SSI in a response,
+ then the response may be transferred incomplete.
+
+ *) Bugfix: nginx did not omit the "#fragment" part in URI.
+
+
+Changes with nginx 0.4.11 25 Oct 2006
+
+ *) Feature: the POP3 proxy supports the AUTH LOGIN PLAIN and CRAM-MD5.
+
+ *) Feature: the ngx_http_perl_module supports the $r->allow_ranges
+ method.
+
+ *) Bugfix: if the APOP was enabled in the POP3 proxy, then the USER/PASS
+ commands might not work; the bug had appeared in 0.4.10.
+
+
+Changes with nginx 0.4.10 23 Oct 2006
+
+ *) Feature: the POP3 proxy supports the APOP command.
+
+ *) Bugfix: if the select, poll or /dev/poll methods were used, then
+ while waiting authentication server response the IMAP/POP3 proxy
+ hogged CPU.
+
+ *) Bugfix: a segmentation fault might occur if the $server_addr variable
+ was used in the "map" directive.
+
+ *) Bugfix: the ngx_http_flv_module did not support the byte ranges for
+ full responses; the bug had appeared in 0.4.7.
+
+ *) Bugfix: nginx could not be built on Debian amd64; the bug had
+ appeared in 0.4.9.
+
+
+Changes with nginx 0.4.9 13 Oct 2006
+
+ *) Feature: the "set" parameter in the "include" SSI command.
+
+ *) Feature: the ngx_http_perl_module now tests the nginx.pm module
+ version.
+
+
+Changes with nginx 0.4.8 11 Oct 2006
+
+ *) Bugfix: if an "include" SSI command were before another "include" SSI
+ command with a "wait" parameter, then the "wait" parameter might not
+ work.
+
+ *) Bugfix: the ngx_http_flv_module added the FLV header to the full
+ responses.
+ Thanks to Alexey Kovyrin.
+
+
+Changes with nginx 0.4.7 10 Oct 2006
+
+ *) Feature: the ngx_http_flv_module.
+
+ *) Feature: the $request_body_file variable.
+
+ *) Feature: the "charset" and "source_charset" directives support the
+ variables.
+
+ *) Bugfix: if an "include" SSI command were before another "include" SSI
+ command with a "wait" parameter, then the "wait" parameter might not
+ work.
+
+ *) Bugfix: if the "proxy_buffering off" directive was used or while
+ working with memcached the connections might not be closed on
+ timeout.
+
+ *) Bugfix: nginx did not run on 64-bit platforms except amd64, sparc64,
+ and ppc64.
+
+
+Changes with nginx 0.4.6 06 Oct 2006
+
+ *) Bugfix: nginx did not run on 64-bit platforms except amd64, sparc64,
+ and ppc64.
+
+ *) Bugfix: nginx sent the chunked response for HTTP/1.1 request,
+ if its length was set by text string in the
+ $r->headers_out("Content-Length", ...) method.
+
+ *) Bugfix: after redirecting error by an "error_page" directive any
+ ngx_http_rewrite_module directive returned this error code; the bug
+ had appeared in 0.4.4.
+
+
+Changes with nginx 0.4.5 02 Oct 2006
+
+ *) Bugfix: nginx could not be built on Linux and Solaris; the bug had
+ appeared in 0.4.4.
+
+
+Changes with nginx 0.4.4 02 Oct 2006
+
+ *) Feature: the $scheme variable.
+
+ *) Feature: the "expires" directive supports the "max" parameter.
+
+ *) Feature: the "include" directive supports the "*" mask.
+ Thanks to Jonathan Dance.
+
+ *) Bugfix: the "return" directive always overrode the "error_page"
+ response code redirected by the "error_page" directive.
+
+ *) Bugfix: a segmentation fault occurred if zero-length body was in PUT
+ method.
+
+ *) Bugfix: the redirect was changed incorrectly if the variables were
+ used in the "proxy_redirect" directive.
+
+
+Changes with nginx 0.4.3 26 Sep 2006
+
+ *) Change: now the 499 error could not be redirected using an
+ "error_page" directive.
+
+ *) Feature: the Solaris 10 event ports support.
+
+ *) Feature: the ngx_http_browser_module.
+
+ *) Bugfix: a segmentation fault may occur while redirecting the 400
+ error to the proxied server using a "proxy_pass" directive.
+
+ *) Bugfix: a segmentation fault occurred if an unix domain socket was
+ used in a "proxy_pass" directive; the bug had appeared in 0.3.47.
+
+ *) Bugfix: SSI did work with memcached and nonbuffered responses.
+
+ *) Workaround: of the Sun Studio PAUSE hardware capability bug.
+
+
+Changes with nginx 0.4.2 14 Sep 2006
+
+ *) Bugfix: the O_NOATIME flag support on Linux was canceled; the bug had
+ appeared in 0.4.1.
+
+
+Changes with nginx 0.4.1 14 Sep 2006
+
+ *) Bugfix: the DragonFlyBSD compatibility.
+ Thanks to Pavel Nazarov.
+
+ *) Workaround: of bug in 64-bit Linux sendfile(), when file is more than
+ 2G.
+
+ *) Feature: now on Linux nginx uses O_NOATIME flag for static requests.
+ Thanks to Yusuf Goolamabbas.
+
+
+Changes with nginx 0.4.0 30 Aug 2006
+
+ *) Change in internal API: the HTTP modules initialization was moved
+ from the init module phase to the HTTP postconfiguration phase.
+
+ *) Change: now the request body is not read beforehand for the
+ ngx_http_perl_module: it's required to start the reading using the
+ $r->has_request_body method.
+
+ *) Feature: the ngx_http_perl_module supports the DECLINED return code.
+
+ *) Feature: the ngx_http_dav_module supports the incoming "Date" header
+ line for the PUT method.
+
+ *) Feature: the "ssi" directive is available inside the "if" block.
+
+ *) Bugfix: a segmentation fault occurred if there was an "index"
+ directive with variables and the first index name was without
+ variables; the bug had appeared in 0.1.29.
+
+
+Changes with nginx 0.3.61 28 Aug 2006
+
+ *) Change: now the "tcp_nodelay" directive is turned on by default.
+
+ *) Feature: the "msie_refresh" directive.
+
+ *) Feature: the "recursive_error_pages" directive.
+
+ *) Bugfix: the "rewrite" directive returned incorrect redirect, if the
+ redirect had the captured escaped symbols from original URI.
+
+
+Changes with nginx 0.3.60 18 Aug 2006
+
+ *) Bugfix: a worker process may got caught in an endless loop while an
+ error redirection; the bug had appeared in 0.3.59.
+
+
+Changes with nginx 0.3.59 16 Aug 2006
+
+ *) Feature: now is possible to do several redirection using the
+ "error_page" directive.
+
+ *) Bugfix: the "dav_access" directive did not support three parameters.
+
+ *) Bugfix: the "error_page" directive did not changes the "Content-Type"
+ header line after the "X-Accel-Redirect" was used; the bug had
+ appeared in 0.3.58.
+
+
+Changes with nginx 0.3.58 14 Aug 2006
+
+ *) Feature: the "error_page" directive supports the variables.
+
+ *) Change: now the procfs interface instead of sysctl is used on Linux.
+
+ *) Change: now the "Content-Type" header line is inherited from first
+ response when the "X-Accel-Redirect" was used.
+
+ *) Bugfix: the "error_page" directive did not redirect the 413 error.
+
+ *) Bugfix: the trailing "?" did not remove old arguments if no new
+ arguments were added to a rewritten URI.
+
+ *) Bugfix: nginx could not run on 64-bit FreeBSD 7.0-CURRENT.
+
+
+Changes with nginx 0.3.57 09 Aug 2006
+
+ *) Feature: the $ssl_client_serial variable.
+
+ *) Bugfix: in the "!-e" operator of the "if" directive.
+ Thanks to Andrian Budanstov.
+
+ *) Bugfix: while a client certificate verification nginx did not send to
+ a client the required certificates information.
+
+ *) Bugfix: the $document_root variable did not support the variables in
+ the "root" directive.
+
+
+Changes with nginx 0.3.56 04 Aug 2006
+
+ *) Feature: the "dav_access" directive.
+
+ *) Feature: the "if" directive supports the "-d", "!-d", "-e", "!-e",
+ "-x", and "!-x" operators.
+
+ *) Bugfix: a segmentation fault occurred if a request returned a
+ redirect and some sent to client header lines were logged in the
+ access log.
+
+
+Changes with nginx 0.3.55 28 Jul 2006
+
+ *) Feature: the "stub" parameter in the "include" SSI command.
+
+ *) Feature: the "block" SSI command.
+
+ *) Feature: the unicode2nginx script was added to contrib.
+
+ *) Bugfix: if a "root" was specified by variable only, then the root was
+ relative to a server prefix.
+
+ *) Bugfix: if the request contained "//" or "/./" and escaped symbols
+ after them, then the proxied request was sent unescaped.
+
+ *) Bugfix: the $r->header_in("Cookie") of the ngx_http_perl_module now
+ returns all "Cookie" header lines.
+
+ *) Bugfix: a segmentation fault occurred if
+ "client_body_in_file_only on" was used and nginx switched to a next
+ upstream.
+
+ *) Bugfix: on some condition while reconfiguration character codes
+ inside the "charset_map" may be treated invalid; the bug had appeared
+ in 0.3.50.
+
+
+Changes with nginx 0.3.54 11 Jul 2006
+
+ *) Feature: nginx now logs the subrequest information to the error log.
+
+ *) Feature: the "proxy_next_upstream", "fastcgi_next_upstream", and
+ "memcached_next_upstream" directives support the "off" parameter.
+
+ *) Feature: the "debug_connection" directive supports the CIDR address
+ form.
+
+ *) Bugfix: if a response of proxied server or FastCGI server was
+ converted from UTF-8 or back, then it may be transferred incomplete.
+
+ *) Bugfix: the $upstream_response_time variable had the time of the
+ first request to a backend only.
+
+ *) Bugfix: nginx could not be built on amd64 platform; the bug had
+ appeared in 0.3.53.
+
+
+Changes with nginx 0.3.53 07 Jul 2006
+
+ *) Change: the "add_header" directive adds the string to 204, 301, and
+ 302 responses.
+
+ *) Feature: the "server" directive in the "upstream" context supports
+ the "weight" parameter.
+
+ *) Feature: the "server_name" directive supports the "*" wildcard.
+
+ *) Feature: nginx supports the request body size more than 2G.
+
+ *) Bugfix: if a client was successfully authorized using "satisfy_any
+ on", then anyway the message "access forbidden by rule" was written
+ in the log.
+
+ *) Bugfix: the "PUT" method may erroneously not create a file and return
+ the 409 code.
+
+ *) Bugfix: if the IMAP/POP3 backend returned an error, then nginx
+ continued proxying anyway.
+
+
+Changes with nginx 0.3.52 03 Jul 2006
+
+ *) Change: the ngx_http_index_module behavior for the "POST /" requests
+ is reverted to the 0.3.40 version state: the module now does not
+ return the 405 error.
+
+ *) Bugfix: the worker process may got caught in an endless loop if the
+ limit rate was used; the bug had appeared in 0.3.37.
+
+ *) Bugfix: ngx_http_charset_module logged "unknown charset" alert, even
+ if the recoding was not needed; the bug had appeared in 0.3.50.
+
+ *) Bugfix: if a code response of the PUT request was 409, then a
+ temporary file was not removed.
+
+
+Changes with nginx 0.3.51 30 Jun 2006
+
+ *) Bugfix: the "<" symbols might disappeared some conditions in the SSI;
+ the bug had appeared in 0.3.50.
+
+
+Changes with nginx 0.3.50 28 Jun 2006
+
+ *) Change: the "proxy_redirect_errors" and "fastcgi_redirect_errors"
+ directives was renamed to the "proxy_intercept_errors" and
+ "fastcgi_intercept_errors" directives.
+
+ *) Feature: the ngx_http_charset_module supports the recoding from the
+ single byte encodings to the UTF-8 encoding and back.
+
+ *) Feature: the "X-Accel-Charset" response header line is supported in
+ proxy and FastCGI mode.
+
+ *) Bugfix: the "\" escape symbol in the "\"" and "\'" pairs in the SSI
+ command was removed only if the command also has the "$" symbol.
+
+ *) Bugfix: the "" CRLF
+"" CRLF
+"" CRLF
+"" CRLF
+"" CRLF
+"" CRLF
+;
+
+
+static u_char ngx_http_msie_refresh_head[] =
+"
" CRLF;
+
+
+static char ngx_http_error_301_page[] =
+"" CRLF
+"301 Moved Permanently" CRLF
+"" CRLF
+"301 Moved Permanently
" CRLF
+;
+
+
+static char ngx_http_error_302_page[] =
+"" CRLF
+"302 Found" CRLF
+"" CRLF
+"302 Found
" CRLF
+;
+
+
+static char ngx_http_error_303_page[] =
+"" CRLF
+"303 See Other" CRLF
+"" CRLF
+"303 See Other
" CRLF
+;
+
+
+static char ngx_http_error_307_page[] =
+"" CRLF
+"307 Temporary Redirect" CRLF
+"" CRLF
+"307 Temporary Redirect
" CRLF
+;
+
+
+static char ngx_http_error_308_page[] =
+"" CRLF
+"308 Permanent Redirect" CRLF
+"" CRLF
+"308 Permanent Redirect
" CRLF
+;
+
+
+static char ngx_http_error_400_page[] =
+"" CRLF
+"400 Bad Request" CRLF
+"" CRLF
+"400 Bad Request
" CRLF
+;
+
+
+static char ngx_http_error_401_page[] =
+"" CRLF
+"401 Authorization Required" CRLF
+"" CRLF
+"401 Authorization Required
" CRLF
+;
+
+
+static char ngx_http_error_402_page[] =
+"" CRLF
+"402 Payment Required" CRLF
+"" CRLF
+"402 Payment Required
" CRLF
+;
+
+
+static char ngx_http_error_403_page[] =
+"" CRLF
+"403 Forbidden" CRLF
+"" CRLF
+"403 Forbidden
" CRLF
+;
+
+
+static char ngx_http_error_404_page[] =
+"" CRLF
+"404 Not Found" CRLF
+"" CRLF
+"404 Not Found
" CRLF
+;
+
+
+static char ngx_http_error_405_page[] =
+"" CRLF
+"405 Not Allowed" CRLF
+"" CRLF
+"405 Not Allowed
" CRLF
+;
+
+
+static char ngx_http_error_406_page[] =
+"" CRLF
+"406 Not Acceptable" CRLF
+"" CRLF
+"406 Not Acceptable
" CRLF
+;
+
+
+static char ngx_http_error_408_page[] =
+"" CRLF
+"408 Request Time-out" CRLF
+"" CRLF
+"408 Request Time-out
" CRLF
+;
+
+
+static char ngx_http_error_409_page[] =
+"" CRLF
+"409 Conflict" CRLF
+"" CRLF
+"409 Conflict
" CRLF
+;
+
+
+static char ngx_http_error_410_page[] =
+"" CRLF
+"410 Gone" CRLF
+"" CRLF
+"410 Gone
" CRLF
+;
+
+
+static char ngx_http_error_411_page[] =
+"" CRLF
+"411 Length Required" CRLF
+"" CRLF
+"411 Length Required
" CRLF
+;
+
+
+static char ngx_http_error_412_page[] =
+"" CRLF
+"412 Precondition Failed" CRLF
+"" CRLF
+"412 Precondition Failed
" CRLF
+;
+
+
+static char ngx_http_error_413_page[] =
+"" CRLF
+"413 Request Entity Too Large" CRLF
+"" CRLF
+"413 Request Entity Too Large
" CRLF
+;
+
+
+static char ngx_http_error_414_page[] =
+"" CRLF
+"414 Request-URI Too Large" CRLF
+"" CRLF
+"414 Request-URI Too Large
" CRLF
+;
+
+
+static char ngx_http_error_415_page[] =
+"" CRLF
+"415 Unsupported Media Type" CRLF
+"" CRLF
+"415 Unsupported Media Type
" CRLF
+;
+
+
+static char ngx_http_error_416_page[] =
+"" CRLF
+"416 Requested Range Not Satisfiable" CRLF
+"" CRLF
+"416 Requested Range Not Satisfiable
" CRLF
+;
+
+
+static char ngx_http_error_421_page[] =
+"" CRLF
+"421 Misdirected Request" CRLF
+"" CRLF
+"421 Misdirected Request
" CRLF
+;
+
+
+static char ngx_http_error_429_page[] =
+"" CRLF
+"429 Too Many Requests" CRLF
+"" CRLF
+"429 Too Many Requests
" CRLF
+;
+
+
+static char ngx_http_error_494_page[] =
+"" CRLF
+"400 Request Header Or Cookie Too Large"
+CRLF
+"" CRLF
+"400 Bad Request
" CRLF
+"Request Header Or Cookie Too Large" CRLF
+;
+
+
+static char ngx_http_error_495_page[] =
+"" CRLF
+"400 The SSL certificate error"
+CRLF
+"" CRLF
+"400 Bad Request
" CRLF
+"The SSL certificate error" CRLF
+;
+
+
+static char ngx_http_error_496_page[] =
+"" CRLF
+"400 No required SSL certificate was sent"
+CRLF
+"" CRLF
+"400 Bad Request
" CRLF
+"No required SSL certificate was sent" CRLF
+;
+
+
+static char ngx_http_error_497_page[] =
+"" CRLF
+"400 The plain HTTP request was sent to HTTPS port"
+CRLF
+"" CRLF
+"400 Bad Request
" CRLF
+"The plain HTTP request was sent to HTTPS port" CRLF
+;
+
+
+static char ngx_http_error_500_page[] =
+"" CRLF
+"500 Internal Server Error" CRLF
+"" CRLF
+"500 Internal Server Error
" CRLF
+;
+
+
+static char ngx_http_error_501_page[] =
+"" CRLF
+"501 Not Implemented" CRLF
+"" CRLF
+"501 Not Implemented
" CRLF
+;
+
+
+static char ngx_http_error_502_page[] =
+"" CRLF
+"502 Bad Gateway" CRLF
+"" CRLF
+"502 Bad Gateway
" CRLF
+;
+
+
+static char ngx_http_error_503_page[] =
+"" CRLF
+"503 Service Temporarily Unavailable" CRLF
+"" CRLF
+"503 Service Temporarily Unavailable
" CRLF
+;
+
+
+static char ngx_http_error_504_page[] =
+"" CRLF
+"504 Gateway Time-out" CRLF
+"" CRLF
+"504 Gateway Time-out
" CRLF
+;
+
+
+static char ngx_http_error_505_page[] =
+"" CRLF
+"505 HTTP Version Not Supported" CRLF
+"" CRLF
+"505 HTTP Version Not Supported
" CRLF
+;
+
+
+static char ngx_http_error_507_page[] =
+"" CRLF
+"507 Insufficient Storage" CRLF
+"" CRLF
+"507 Insufficient Storage
" CRLF
+;
+
+
+static ngx_str_t ngx_http_error_pages[] = {
+
+ ngx_null_string, /* 201, 204 */
+
+#define NGX_HTTP_LAST_2XX 202
+#define NGX_HTTP_OFF_3XX (NGX_HTTP_LAST_2XX - 201)
+
+ /* ngx_null_string, */ /* 300 */
+ ngx_string(ngx_http_error_301_page),
+ ngx_string(ngx_http_error_302_page),
+ ngx_string(ngx_http_error_303_page),
+ ngx_null_string, /* 304 */
+ ngx_null_string, /* 305 */
+ ngx_null_string, /* 306 */
+ ngx_string(ngx_http_error_307_page),
+ ngx_string(ngx_http_error_308_page),
+
+#define NGX_HTTP_LAST_3XX 309
+#define NGX_HTTP_OFF_4XX (NGX_HTTP_LAST_3XX - 301 + NGX_HTTP_OFF_3XX)
+
+ ngx_string(ngx_http_error_400_page),
+ ngx_string(ngx_http_error_401_page),
+ ngx_string(ngx_http_error_402_page),
+ ngx_string(ngx_http_error_403_page),
+ ngx_string(ngx_http_error_404_page),
+ ngx_string(ngx_http_error_405_page),
+ ngx_string(ngx_http_error_406_page),
+ ngx_null_string, /* 407 */
+ ngx_string(ngx_http_error_408_page),
+ ngx_string(ngx_http_error_409_page),
+ ngx_string(ngx_http_error_410_page),
+ ngx_string(ngx_http_error_411_page),
+ ngx_string(ngx_http_error_412_page),
+ ngx_string(ngx_http_error_413_page),
+ ngx_string(ngx_http_error_414_page),
+ ngx_string(ngx_http_error_415_page),
+ ngx_string(ngx_http_error_416_page),
+ ngx_null_string, /* 417 */
+ ngx_null_string, /* 418 */
+ ngx_null_string, /* 419 */
+ ngx_null_string, /* 420 */
+ ngx_string(ngx_http_error_421_page),
+ ngx_null_string, /* 422 */
+ ngx_null_string, /* 423 */
+ ngx_null_string, /* 424 */
+ ngx_null_string, /* 425 */
+ ngx_null_string, /* 426 */
+ ngx_null_string, /* 427 */
+ ngx_null_string, /* 428 */
+ ngx_string(ngx_http_error_429_page),
+
+#define NGX_HTTP_LAST_4XX 430
+#define NGX_HTTP_OFF_5XX (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX)
+
+ ngx_string(ngx_http_error_494_page), /* 494, request header too large */
+ ngx_string(ngx_http_error_495_page), /* 495, https certificate error */
+ ngx_string(ngx_http_error_496_page), /* 496, https no certificate */
+ ngx_string(ngx_http_error_497_page), /* 497, http to https */
+ ngx_string(ngx_http_error_404_page), /* 498, canceled */
+ ngx_null_string, /* 499, client has closed connection */
+
+ ngx_string(ngx_http_error_500_page),
+ ngx_string(ngx_http_error_501_page),
+ ngx_string(ngx_http_error_502_page),
+ ngx_string(ngx_http_error_503_page),
+ ngx_string(ngx_http_error_504_page),
+ ngx_string(ngx_http_error_505_page),
+ ngx_null_string, /* 506 */
+ ngx_string(ngx_http_error_507_page)
+
+#define NGX_HTTP_LAST_5XX 508
+
+};
+
+
+ngx_int_t
+ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error)
+{
+ ngx_uint_t i, err;
+ ngx_http_err_page_t *err_page;
+ ngx_http_core_loc_conf_t *clcf;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http special response: %i, \"%V?%V\"",
+ error, &r->uri, &r->args);
+
+ r->err_status = error;
+
+ 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_TO_HTTPS:
+ case NGX_HTTPS_CERT_ERROR:
+ case NGX_HTTPS_NO_CERT:
+ case NGX_HTTP_INTERNAL_SERVER_ERROR:
+ case NGX_HTTP_NOT_IMPLEMENTED:
+ r->keepalive = 0;
+ }
+ }
+
+ if (r->lingering_close) {
+ switch (error) {
+ case NGX_HTTP_BAD_REQUEST:
+ case NGX_HTTP_TO_HTTPS:
+ case NGX_HTTPS_CERT_ERROR:
+ case NGX_HTTPS_NO_CERT:
+ r->lingering_close = 0;
+ }
+ }
+
+ r->headers_out.content_type.len = 0;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (!r->error_page && clcf->error_pages && r->uri_changes != 0) {
+
+ if (clcf->recursive_error_pages == 0) {
+ r->error_page = 1;
+ }
+
+ err_page = clcf->error_pages->elts;
+
+ for (i = 0; i < clcf->error_pages->nelts; i++) {
+ if (err_page[i].status == error) {
+ return ngx_http_send_error_page(r, &err_page[i]);
+ }
+ }
+ }
+
+ r->expect_tested = 1;
+
+ if (ngx_http_discard_request_body(r) != NGX_OK) {
+ r->keepalive = 0;
+ }
+
+ if (clcf->msie_refresh
+ && r->headers_in.msie
+ && (error == NGX_HTTP_MOVED_PERMANENTLY
+ || error == NGX_HTTP_MOVED_TEMPORARILY))
+ {
+ return ngx_http_send_refresh(r);
+ }
+
+ if (error == NGX_HTTP_CREATED) {
+ /* 201 */
+ err = 0;
+
+ } else if (error == NGX_HTTP_NO_CONTENT) {
+ /* 204 */
+ err = 0;
+
+ } else if (error >= NGX_HTTP_MOVED_PERMANENTLY
+ && error < NGX_HTTP_LAST_3XX)
+ {
+ /* 3XX */
+ err = error - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_OFF_3XX;
+
+ } else if (error >= NGX_HTTP_BAD_REQUEST
+ && error < NGX_HTTP_LAST_4XX)
+ {
+ /* 4XX */
+ err = error - NGX_HTTP_BAD_REQUEST + NGX_HTTP_OFF_4XX;
+
+ } else if (error >= NGX_HTTP_NGINX_CODES
+ && error < NGX_HTTP_LAST_5XX)
+ {
+ /* 49X, 5XX */
+ err = error - NGX_HTTP_NGINX_CODES + NGX_HTTP_OFF_5XX;
+ switch (error) {
+ case NGX_HTTP_TO_HTTPS:
+ case NGX_HTTPS_CERT_ERROR:
+ case NGX_HTTPS_NO_CERT:
+ case NGX_HTTP_REQUEST_HEADER_TOO_LARGE:
+ r->err_status = NGX_HTTP_BAD_REQUEST;
+ }
+
+ } else {
+ /* unknown code, zero body */
+ err = 0;
+ }
+
+ return ngx_http_send_special_response(r, clcf, err);
+}
+
+
+ngx_int_t
+ngx_http_filter_finalize_request(ngx_http_request_t *r, ngx_module_t *m,
+ ngx_int_t error)
+{
+ void *ctx;
+ ngx_int_t rc;
+
+ ngx_http_clean_header(r);
+
+ ctx = NULL;
+
+ if (m) {
+ ctx = r->ctx[m->ctx_index];
+ }
+
+ /* clear the modules contexts */
+ ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);
+
+ if (m) {
+ r->ctx[m->ctx_index] = ctx;
+ }
+
+ r->filter_finalize = 1;
+
+ rc = ngx_http_special_response_handler(r, error);
+
+ /* NGX_ERROR resets any pending data */
+
+ switch (rc) {
+
+ case NGX_OK:
+ case NGX_DONE:
+ return NGX_ERROR;
+
+ default:
+ return rc;
+ }
+}
+
+
+void
+ngx_http_clean_header(ngx_http_request_t *r)
+{
+ ngx_memzero(&r->headers_out.status,
+ sizeof(ngx_http_headers_out_t)
+ - offsetof(ngx_http_headers_out_t, status));
+
+ r->headers_out.headers.part.nelts = 0;
+ r->headers_out.headers.part.next = NULL;
+ r->headers_out.headers.last = &r->headers_out.headers.part;
+
+ r->headers_out.content_length_n = -1;
+ r->headers_out.last_modified_time = -1;
+}
+
+
+static ngx_int_t
+ngx_http_send_error_page(ngx_http_request_t *r, ngx_http_err_page_t *err_page)
+{
+ ngx_int_t overwrite;
+ ngx_str_t uri, args;
+ ngx_table_elt_t *location;
+ ngx_http_core_loc_conf_t *clcf;
+
+ overwrite = err_page->overwrite;
+
+ if (overwrite && overwrite != NGX_HTTP_OK) {
+ r->expect_tested = 1;
+ }
+
+ if (overwrite >= 0) {
+ r->err_status = overwrite;
+ }
+
+ if (ngx_http_complex_value(r, &err_page->value, &uri) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (uri.len && uri.data[0] == '/') {
+
+ if (err_page->value.lengths) {
+ ngx_http_split_args(r, &uri, &args);
+
+ } else {
+ args = err_page->args;
+ }
+
+ if (r->method != NGX_HTTP_HEAD) {
+ r->method = NGX_HTTP_GET;
+ r->method_name = ngx_http_core_get_method;
+ }
+
+ return ngx_http_internal_redirect(r, &uri, &args);
+ }
+
+ if (uri.len && uri.data[0] == '@') {
+ return ngx_http_named_location(r, &uri);
+ }
+
+ r->expect_tested = 1;
+
+ if (ngx_http_discard_request_body(r) != NGX_OK) {
+ r->keepalive = 0;
+ }
+
+ location = ngx_list_push(&r->headers_out.headers);
+
+ if (location == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (overwrite != NGX_HTTP_MOVED_PERMANENTLY
+ && overwrite != NGX_HTTP_MOVED_TEMPORARILY
+ && overwrite != NGX_HTTP_SEE_OTHER
+ && overwrite != NGX_HTTP_TEMPORARY_REDIRECT
+ && overwrite != NGX_HTTP_PERMANENT_REDIRECT)
+ {
+ r->err_status = NGX_HTTP_MOVED_TEMPORARILY;
+ }
+
+ location->hash = 1;
+ ngx_str_set(&location->key, "Location");
+ location->value = uri;
+
+ ngx_http_clear_location(r);
+
+ r->headers_out.location = location;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->msie_refresh && r->headers_in.msie) {
+ return ngx_http_send_refresh(r);
+ }
+
+ return ngx_http_send_special_response(r, clcf, r->err_status
+ - NGX_HTTP_MOVED_PERMANENTLY
+ + NGX_HTTP_OFF_3XX);
+}
+
+
+static ngx_int_t
+ngx_http_send_special_response(ngx_http_request_t *r,
+ ngx_http_core_loc_conf_t *clcf, ngx_uint_t err)
+{
+ u_char *tail;
+ size_t len;
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ ngx_uint_t msie_padding;
+ ngx_chain_t out[3];
+
+ if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {
+ len = sizeof(ngx_http_error_full_tail) - 1;
+ tail = ngx_http_error_full_tail;
+
+ } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {
+ len = sizeof(ngx_http_error_build_tail) - 1;
+ tail = ngx_http_error_build_tail;
+
+ } else {
+ len = sizeof(ngx_http_error_tail) - 1;
+ tail = ngx_http_error_tail;
+ }
+
+ msie_padding = 0;
+
+ if (ngx_http_error_pages[err].len) {
+ r->headers_out.content_length_n = ngx_http_error_pages[err].len + len;
+ if (clcf->msie_padding
+ && (r->headers_in.msie || r->headers_in.chrome)
+ && r->http_version >= NGX_HTTP_VERSION_10
+ && err >= NGX_HTTP_OFF_4XX)
+ {
+ r->headers_out.content_length_n +=
+ sizeof(ngx_http_msie_padding) - 1;
+ msie_padding = 1;
+ }
+
+ r->headers_out.content_type_len = sizeof("text/html") - 1;
+ ngx_str_set(&r->headers_out.content_type, "text/html");
+ r->headers_out.content_type_lowcase = NULL;
+
+ } else {
+ r->headers_out.content_length_n = 0;
+ }
+
+ if (r->headers_out.content_length) {
+ r->headers_out.content_length->hash = 0;
+ r->headers_out.content_length = NULL;
+ }
+
+ ngx_http_clear_accept_ranges(r);
+ ngx_http_clear_last_modified(r);
+ ngx_http_clear_etag(r);
+
+ rc = ngx_http_send_header(r);
+
+ if (rc == NGX_ERROR || r->header_only) {
+ return rc;
+ }
+
+ if (ngx_http_error_pages[err].len == 0) {
+ return ngx_http_send_special(r, NGX_HTTP_LAST);
+ }
+
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ b->memory = 1;
+ b->pos = ngx_http_error_pages[err].data;
+ b->last = ngx_http_error_pages[err].data + ngx_http_error_pages[err].len;
+
+ out[0].buf = b;
+ out[0].next = &out[1];
+
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ b->memory = 1;
+
+ b->pos = tail;
+ b->last = tail + len;
+
+ out[1].buf = b;
+ out[1].next = NULL;
+
+ if (msie_padding) {
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ b->memory = 1;
+ b->pos = ngx_http_msie_padding;
+ b->last = ngx_http_msie_padding + sizeof(ngx_http_msie_padding) - 1;
+
+ out[1].next = &out[2];
+ out[2].buf = b;
+ out[2].next = NULL;
+ }
+
+ if (r == r->main) {
+ b->last_buf = 1;
+ }
+
+ b->last_in_chain = 1;
+
+ return ngx_http_output_filter(r, &out[0]);
+}
+
+
+static ngx_int_t
+ngx_http_send_refresh(ngx_http_request_t *r)
+{
+ u_char *p, *location;
+ size_t len, size;
+ uintptr_t escape;
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ ngx_chain_t out;
+
+ len = r->headers_out.location->value.len;
+ location = r->headers_out.location->value.data;
+
+ escape = 2 * ngx_escape_uri(NULL, location, len, NGX_ESCAPE_REFRESH);
+
+ size = sizeof(ngx_http_msie_refresh_head) - 1
+ + escape + len
+ + sizeof(ngx_http_msie_refresh_tail) - 1;
+
+ r->err_status = NGX_HTTP_OK;
+
+ r->headers_out.content_type_len = sizeof("text/html") - 1;
+ ngx_str_set(&r->headers_out.content_type, "text/html");
+ r->headers_out.content_type_lowcase = NULL;
+
+ r->headers_out.location->hash = 0;
+ r->headers_out.location = NULL;
+
+ r->headers_out.content_length_n = size;
+
+ if (r->headers_out.content_length) {
+ r->headers_out.content_length->hash = 0;
+ r->headers_out.content_length = NULL;
+ }
+
+ ngx_http_clear_accept_ranges(r);
+ ngx_http_clear_last_modified(r);
+ ngx_http_clear_etag(r);
+
+ rc = ngx_http_send_header(r);
+
+ if (rc == NGX_ERROR || r->header_only) {
+ return rc;
+ }
+
+ b = ngx_create_temp_buf(r->pool, size);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ p = ngx_cpymem(b->pos, ngx_http_msie_refresh_head,
+ sizeof(ngx_http_msie_refresh_head) - 1);
+
+ if (escape == 0) {
+ p = ngx_cpymem(p, location, len);
+
+ } else {
+ p = (u_char *) ngx_escape_uri(p, location, len, NGX_ESCAPE_REFRESH);
+ }
+
+ b->last = ngx_cpymem(p, ngx_http_msie_refresh_tail,
+ sizeof(ngx_http_msie_refresh_tail) - 1);
+
+ b->last_buf = (r == r->main) ? 1 : 0;
+ b->last_in_chain = 1;
+
+ out.buf = b;
+ out.next = NULL;
+
+ return ngx_http_output_filter(r, &out);
+}
diff --git a/src/nginx-1.18.0/src/http/ngx_http_upstream.c b/src/nginx-1.18.0/src/http/ngx_http_upstream.c
new file mode 100644
index 0000000..89e1319
--- /dev/null
+++ b/src/nginx-1.18.0/src/http/ngx_http_upstream.c
@@ -0,0 +1,6500 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+#include
+
+
+#if (NGX_HTTP_CACHE)
+static ngx_int_t ngx_http_upstream_cache(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static ngx_int_t ngx_http_upstream_cache_get(ngx_http_request_t *r,
+ ngx_http_upstream_t *u, ngx_http_file_cache_t **cache);
+static ngx_int_t ngx_http_upstream_cache_send(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static ngx_int_t ngx_http_upstream_cache_background_update(
+ ngx_http_request_t *r, ngx_http_upstream_t *u);
+static ngx_int_t ngx_http_upstream_cache_check_range(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static ngx_int_t ngx_http_upstream_cache_status(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_upstream_cache_last_modified(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_upstream_cache_etag(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+#endif
+
+static void ngx_http_upstream_init_request(ngx_http_request_t *r);
+static void ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx);
+static void ngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r);
+static void ngx_http_upstream_wr_check_broken_connection(ngx_http_request_t *r);
+static void ngx_http_upstream_check_broken_connection(ngx_http_request_t *r,
+ ngx_event_t *ev);
+static void ngx_http_upstream_connect(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static ngx_int_t ngx_http_upstream_reinit(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_send_request(ngx_http_request_t *r,
+ ngx_http_upstream_t *u, ngx_uint_t do_write);
+static ngx_int_t ngx_http_upstream_send_request_body(ngx_http_request_t *r,
+ ngx_http_upstream_t *u, ngx_uint_t do_write);
+static void ngx_http_upstream_send_request_handler(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_read_request_handler(ngx_http_request_t *r);
+static void ngx_http_upstream_process_header(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static ngx_int_t ngx_http_upstream_test_next(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static ngx_int_t ngx_http_upstream_intercept_errors(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static ngx_int_t ngx_http_upstream_test_connect(ngx_connection_t *c);
+static ngx_int_t ngx_http_upstream_process_headers(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static ngx_int_t ngx_http_upstream_process_trailers(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_send_response(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_upgrade(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_upgraded_read_downstream(ngx_http_request_t *r);
+static void ngx_http_upstream_upgraded_write_downstream(ngx_http_request_t *r);
+static void ngx_http_upstream_upgraded_read_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_upgraded_write_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_process_upgraded(ngx_http_request_t *r,
+ ngx_uint_t from_upstream, ngx_uint_t do_write);
+static void
+ ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r);
+static void
+ ngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void
+ ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,
+ ngx_uint_t do_write);
+static ngx_int_t ngx_http_upstream_non_buffered_filter_init(void *data);
+static ngx_int_t ngx_http_upstream_non_buffered_filter(void *data,
+ ssize_t bytes);
+#if (NGX_THREADS)
+static ngx_int_t ngx_http_upstream_thread_handler(ngx_thread_task_t *task,
+ ngx_file_t *file);
+static void ngx_http_upstream_thread_event_handler(ngx_event_t *ev);
+#endif
+static ngx_int_t ngx_http_upstream_output_filter(void *data,
+ ngx_chain_t *chain);
+static void ngx_http_upstream_process_downstream(ngx_http_request_t *r);
+static void ngx_http_upstream_process_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_process_request(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_store(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_dummy_handler(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_next(ngx_http_request_t *r,
+ ngx_http_upstream_t *u, ngx_uint_t ft_type);
+static void ngx_http_upstream_cleanup(void *data);
+static void ngx_http_upstream_finalize_request(ngx_http_request_t *r,
+ ngx_http_upstream_t *u, ngx_int_t rc);
+
+static ngx_int_t ngx_http_upstream_process_header_line(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_content_length(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_last_modified(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_set_cookie(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t
+ ngx_http_upstream_process_cache_control(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_ignore_header_line(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_expires(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_accel_expires(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_limit_rate(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_buffering(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_charset(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_connection(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t
+ ngx_http_upstream_process_transfer_encoding(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_vary(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_copy_header_line(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t
+ ngx_http_upstream_copy_multi_header_lines(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_copy_content_type(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_copy_last_modified(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_rewrite_location(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_rewrite_refresh(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_rewrite_set_cookie(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+
+#if (NGX_HTTP_GZIP)
+static ngx_int_t ngx_http_upstream_copy_content_encoding(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+#endif
+
+static ngx_int_t ngx_http_upstream_add_variables(ngx_conf_t *cf);
+static ngx_int_t ngx_http_upstream_addr_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_upstream_status_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_upstream_response_time_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_upstream_response_length_variable(
+ ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_upstream_header_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_upstream_trailer_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_upstream_cookie_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+
+static char *ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy);
+static char *ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+static ngx_int_t ngx_http_upstream_set_local(ngx_http_request_t *r,
+ ngx_http_upstream_t *u, ngx_http_upstream_local_t *local);
+
+static void *ngx_http_upstream_create_main_conf(ngx_conf_t *cf);
+static char *ngx_http_upstream_init_main_conf(ngx_conf_t *cf, void *conf);
+
+#if (NGX_HTTP_SSL)
+static void ngx_http_upstream_ssl_init_connection(ngx_http_request_t *,
+ ngx_http_upstream_t *u, ngx_connection_t *c);
+static void ngx_http_upstream_ssl_handshake_handler(ngx_connection_t *c);
+static void ngx_http_upstream_ssl_handshake(ngx_http_request_t *,
+ ngx_http_upstream_t *u, ngx_connection_t *c);
+static void ngx_http_upstream_ssl_save_session(ngx_connection_t *c);
+static ngx_int_t ngx_http_upstream_ssl_name(ngx_http_request_t *r,
+ ngx_http_upstream_t *u, ngx_connection_t *c);
+#endif
+
+
+static ngx_http_upstream_header_t ngx_http_upstream_headers_in[] = {
+
+ { ngx_string("Status"),
+ ngx_http_upstream_process_header_line,
+ offsetof(ngx_http_upstream_headers_in_t, status),
+ ngx_http_upstream_copy_header_line, 0, 0 },
+
+ { ngx_string("Content-Type"),
+ ngx_http_upstream_process_header_line,
+ offsetof(ngx_http_upstream_headers_in_t, content_type),
+ ngx_http_upstream_copy_content_type, 0, 1 },
+
+ { ngx_string("Content-Length"),
+ ngx_http_upstream_process_content_length, 0,
+ ngx_http_upstream_ignore_header_line, 0, 0 },
+
+ { ngx_string("Date"),
+ ngx_http_upstream_process_header_line,
+ offsetof(ngx_http_upstream_headers_in_t, date),
+ ngx_http_upstream_copy_header_line,
+ offsetof(ngx_http_headers_out_t, date), 0 },
+
+ { ngx_string("Last-Modified"),
+ ngx_http_upstream_process_last_modified, 0,
+ ngx_http_upstream_copy_last_modified, 0, 0 },
+
+ { ngx_string("ETag"),
+ ngx_http_upstream_process_header_line,
+ offsetof(ngx_http_upstream_headers_in_t, etag),
+ ngx_http_upstream_copy_header_line,
+ offsetof(ngx_http_headers_out_t, etag), 0 },
+
+ { ngx_string("Server"),
+ ngx_http_upstream_process_header_line,
+ offsetof(ngx_http_upstream_headers_in_t, server),
+ ngx_http_upstream_copy_header_line,
+ offsetof(ngx_http_headers_out_t, server), 0 },
+
+ { ngx_string("WWW-Authenticate"),
+ ngx_http_upstream_process_header_line,
+ offsetof(ngx_http_upstream_headers_in_t, www_authenticate),
+ ngx_http_upstream_copy_header_line, 0, 0 },
+
+ { ngx_string("Location"),
+ ngx_http_upstream_process_header_line,
+ offsetof(ngx_http_upstream_headers_in_t, location),
+ ngx_http_upstream_rewrite_location, 0, 0 },
+
+ { ngx_string("Refresh"),
+ ngx_http_upstream_ignore_header_line, 0,
+ ngx_http_upstream_rewrite_refresh, 0, 0 },
+
+ { ngx_string("Set-Cookie"),
+ ngx_http_upstream_process_set_cookie,
+ offsetof(ngx_http_upstream_headers_in_t, cookies),
+ ngx_http_upstream_rewrite_set_cookie, 0, 1 },
+
+ { ngx_string("Content-Disposition"),
+ ngx_http_upstream_ignore_header_line, 0,
+ ngx_http_upstream_copy_header_line, 0, 1 },
+
+ { ngx_string("Cache-Control"),
+ ngx_http_upstream_process_cache_control, 0,
+ ngx_http_upstream_copy_multi_header_lines,
+ offsetof(ngx_http_headers_out_t, cache_control), 1 },
+
+ { ngx_string("Expires"),
+ ngx_http_upstream_process_expires, 0,
+ ngx_http_upstream_copy_header_line,
+ offsetof(ngx_http_headers_out_t, expires), 1 },
+
+ { ngx_string("Accept-Ranges"),
+ ngx_http_upstream_process_header_line,
+ offsetof(ngx_http_upstream_headers_in_t, accept_ranges),
+ ngx_http_upstream_copy_allow_ranges,
+ offsetof(ngx_http_headers_out_t, accept_ranges), 1 },
+
+ { ngx_string("Content-Range"),
+ ngx_http_upstream_ignore_header_line, 0,
+ ngx_http_upstream_copy_header_line,
+ offsetof(ngx_http_headers_out_t, content_range), 0 },
+
+ { ngx_string("Connection"),
+ ngx_http_upstream_process_connection, 0,
+ ngx_http_upstream_ignore_header_line, 0, 0 },
+
+ { ngx_string("Keep-Alive"),
+ ngx_http_upstream_ignore_header_line, 0,
+ ngx_http_upstream_ignore_header_line, 0, 0 },
+
+ { ngx_string("Vary"),
+ ngx_http_upstream_process_vary, 0,
+ ngx_http_upstream_copy_header_line, 0, 0 },
+
+ { ngx_string("Link"),
+ ngx_http_upstream_ignore_header_line, 0,
+ ngx_http_upstream_copy_multi_header_lines,
+ offsetof(ngx_http_headers_out_t, link), 0 },
+
+ { ngx_string("X-Accel-Expires"),
+ ngx_http_upstream_process_accel_expires, 0,
+ ngx_http_upstream_copy_header_line, 0, 0 },
+
+ { ngx_string("X-Accel-Redirect"),
+ ngx_http_upstream_process_header_line,
+ offsetof(ngx_http_upstream_headers_in_t, x_accel_redirect),
+ ngx_http_upstream_copy_header_line, 0, 0 },
+
+ { ngx_string("X-Accel-Limit-Rate"),
+ ngx_http_upstream_process_limit_rate, 0,
+ ngx_http_upstream_copy_header_line, 0, 0 },
+
+ { ngx_string("X-Accel-Buffering"),
+ ngx_http_upstream_process_buffering, 0,
+ ngx_http_upstream_copy_header_line, 0, 0 },
+
+ { ngx_string("X-Accel-Charset"),
+ ngx_http_upstream_process_charset, 0,
+ ngx_http_upstream_copy_header_line, 0, 0 },
+
+ { ngx_string("Transfer-Encoding"),
+ ngx_http_upstream_process_transfer_encoding, 0,
+ ngx_http_upstream_ignore_header_line, 0, 0 },
+
+#if (NGX_HTTP_GZIP)
+ { ngx_string("Content-Encoding"),
+ ngx_http_upstream_process_header_line,
+ offsetof(ngx_http_upstream_headers_in_t, content_encoding),
+ ngx_http_upstream_copy_content_encoding, 0, 0 },
+#endif
+
+ { ngx_null_string, NULL, 0, NULL, 0, 0 }
+};
+
+
+static ngx_command_t ngx_http_upstream_commands[] = {
+
+ { ngx_string("upstream"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1,
+ ngx_http_upstream,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("server"),
+ NGX_HTTP_UPS_CONF|NGX_CONF_1MORE,
+ ngx_http_upstream_server,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_upstream_module_ctx = {
+ ngx_http_upstream_add_variables, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ ngx_http_upstream_create_main_conf, /* create main configuration */
+ ngx_http_upstream_init_main_conf, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_upstream_module = {
+ NGX_MODULE_V1,
+ &ngx_http_upstream_module_ctx, /* module context */
+ ngx_http_upstream_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_variable_t ngx_http_upstream_vars[] = {
+
+ { ngx_string("upstream_addr"), NULL,
+ ngx_http_upstream_addr_variable, 0,
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("upstream_status"), NULL,
+ ngx_http_upstream_status_variable, 0,
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("upstream_connect_time"), NULL,
+ ngx_http_upstream_response_time_variable, 2,
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("upstream_header_time"), NULL,
+ ngx_http_upstream_response_time_variable, 1,
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("upstream_response_time"), NULL,
+ ngx_http_upstream_response_time_variable, 0,
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("upstream_response_length"), NULL,
+ ngx_http_upstream_response_length_variable, 0,
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("upstream_bytes_received"), NULL,
+ ngx_http_upstream_response_length_variable, 1,
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("upstream_bytes_sent"), NULL,
+ ngx_http_upstream_response_length_variable, 2,
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+#if (NGX_HTTP_CACHE)
+
+ { ngx_string("upstream_cache_status"), NULL,
+ ngx_http_upstream_cache_status, 0,
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("upstream_cache_last_modified"), NULL,
+ ngx_http_upstream_cache_last_modified, 0,
+ NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
+ { ngx_string("upstream_cache_etag"), NULL,
+ ngx_http_upstream_cache_etag, 0,
+ NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
+#endif
+
+ { ngx_string("upstream_http_"), NULL, ngx_http_upstream_header_variable,
+ 0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_PREFIX, 0 },
+
+ { ngx_string("upstream_trailer_"), NULL, ngx_http_upstream_trailer_variable,
+ 0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_PREFIX, 0 },
+
+ { ngx_string("upstream_cookie_"), NULL, ngx_http_upstream_cookie_variable,
+ 0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_PREFIX, 0 },
+
+ ngx_http_null_variable
+};
+
+
+static ngx_http_upstream_next_t ngx_http_upstream_next_errors[] = {
+ { 500, NGX_HTTP_UPSTREAM_FT_HTTP_500 },
+ { 502, NGX_HTTP_UPSTREAM_FT_HTTP_502 },
+ { 503, NGX_HTTP_UPSTREAM_FT_HTTP_503 },
+ { 504, NGX_HTTP_UPSTREAM_FT_HTTP_504 },
+ { 403, NGX_HTTP_UPSTREAM_FT_HTTP_403 },
+ { 404, NGX_HTTP_UPSTREAM_FT_HTTP_404 },
+ { 429, NGX_HTTP_UPSTREAM_FT_HTTP_429 },
+ { 0, 0 }
+};
+
+
+ngx_conf_bitmask_t ngx_http_upstream_cache_method_mask[] = {
+ { ngx_string("GET"), NGX_HTTP_GET },
+ { ngx_string("HEAD"), NGX_HTTP_HEAD },
+ { ngx_string("POST"), NGX_HTTP_POST },
+ { ngx_null_string, 0 }
+};
+
+
+ngx_conf_bitmask_t ngx_http_upstream_ignore_headers_masks[] = {
+ { ngx_string("X-Accel-Redirect"), NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT },
+ { ngx_string("X-Accel-Expires"), NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES },
+ { ngx_string("X-Accel-Limit-Rate"), NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE },
+ { ngx_string("X-Accel-Buffering"), NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING },
+ { ngx_string("X-Accel-Charset"), NGX_HTTP_UPSTREAM_IGN_XA_CHARSET },
+ { ngx_string("Expires"), NGX_HTTP_UPSTREAM_IGN_EXPIRES },
+ { ngx_string("Cache-Control"), NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL },
+ { ngx_string("Set-Cookie"), NGX_HTTP_UPSTREAM_IGN_SET_COOKIE },
+ { ngx_string("Vary"), NGX_HTTP_UPSTREAM_IGN_VARY },
+ { ngx_null_string, 0 }
+};
+
+
+ngx_int_t
+ngx_http_upstream_create(ngx_http_request_t *r)
+{
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+
+ if (u && u->cleanup) {
+ r->main->count++;
+ ngx_http_upstream_cleanup(r);
+ }
+
+ u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));
+ if (u == NULL) {
+ return NGX_ERROR;
+ }
+
+ r->upstream = u;
+
+ u->peer.log = r->connection->log;
+ u->peer.log_error = NGX_ERROR_ERR;
+
+#if (NGX_HTTP_CACHE)
+ r->cache = NULL;
+#endif
+
+ u->headers_in.content_length_n = -1;
+ u->headers_in.last_modified_time = -1;
+
+ return NGX_OK;
+}
+
+
+void
+ngx_http_upstream_init(ngx_http_request_t *r)
+{
+ ngx_connection_t *c;
+
+ c = r->connection;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http init upstream, client timer: %d", c->read->timer_set);
+
+#if (NGX_HTTP_V2)
+ if (r->stream) {
+ ngx_http_upstream_init_request(r);
+ return;
+ }
+#endif
+
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+
+ if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {
+
+ if (!c->write->active) {
+ if (ngx_add_event(c->write, NGX_WRITE_EVENT, NGX_CLEAR_EVENT)
+ == NGX_ERROR)
+ {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+ }
+ }
+
+ ngx_http_upstream_init_request(r);
+}
+
+
+static void
+ngx_http_upstream_init_request(ngx_http_request_t *r)
+{
+ ngx_str_t *host;
+ ngx_uint_t i;
+ ngx_resolver_ctx_t *ctx, temp;
+ ngx_http_cleanup_t *cln;
+ ngx_http_upstream_t *u;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_upstream_srv_conf_t *uscf, **uscfp;
+ ngx_http_upstream_main_conf_t *umcf;
+
+ if (r->aio) {
+ return;
+ }
+
+ u = r->upstream;
+
+#if (NGX_HTTP_CACHE)
+
+ if (u->conf->cache) {
+ ngx_int_t rc;
+
+ rc = ngx_http_upstream_cache(r, u);
+
+ if (rc == NGX_BUSY) {
+ r->write_event_handler = ngx_http_upstream_init_request;
+ return;
+ }
+
+ r->write_event_handler = ngx_http_request_empty_handler;
+
+ if (rc == NGX_ERROR) {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (rc == NGX_OK) {
+ rc = ngx_http_upstream_cache_send(r, u);
+
+ if (rc == NGX_DONE) {
+ return;
+ }
+
+ if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) {
+ rc = NGX_DECLINED;
+ r->cached = 0;
+ u->buffer.start = NULL;
+ u->cache_status = NGX_HTTP_CACHE_MISS;
+ u->request_sent = 1;
+ }
+ }
+
+ if (rc != NGX_DECLINED) {
+ ngx_http_finalize_request(r, rc);
+ return;
+ }
+ }
+
+#endif
+
+ u->store = u->conf->store;
+
+ if (!u->store && !r->post_action && !u->conf->ignore_client_abort) {
+ r->read_event_handler = ngx_http_upstream_rd_check_broken_connection;
+ r->write_event_handler = ngx_http_upstream_wr_check_broken_connection;
+ }
+
+ if (r->request_body) {
+ u->request_bufs = r->request_body->bufs;
+ }
+
+ if (u->create_request(r) != NGX_OK) {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (ngx_http_upstream_set_local(r, u, u->conf->local) != NGX_OK) {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (u->conf->socket_keepalive) {
+ u->peer.so_keepalive = 1;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ u->output.alignment = clcf->directio_alignment;
+ u->output.pool = r->pool;
+ u->output.bufs.num = 1;
+ u->output.bufs.size = clcf->client_body_buffer_size;
+
+ if (u->output.output_filter == NULL) {
+ u->output.output_filter = ngx_chain_writer;
+ u->output.filter_ctx = &u->writer;
+ }
+
+ u->writer.pool = r->pool;
+
+ if (r->upstream_states == NULL) {
+
+ r->upstream_states = ngx_array_create(r->pool, 1,
+ sizeof(ngx_http_upstream_state_t));
+ if (r->upstream_states == NULL) {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ } else {
+
+ u->state = ngx_array_push(r->upstream_states);
+ if (u->state == NULL) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ ngx_memzero(u->state, sizeof(ngx_http_upstream_state_t));
+ }
+
+ cln = ngx_http_cleanup_add(r, 0);
+ if (cln == NULL) {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ cln->handler = ngx_http_upstream_cleanup;
+ cln->data = r;
+ u->cleanup = &cln->handler;
+
+ if (u->resolved == NULL) {
+
+ uscf = u->conf->upstream;
+
+ } else {
+
+#if (NGX_HTTP_SSL)
+ u->ssl_name = u->resolved->host;
+#endif
+
+ host = &u->resolved->host;
+
+ umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
+
+ uscfp = umcf->upstreams.elts;
+
+ for (i = 0; i < umcf->upstreams.nelts; i++) {
+
+ uscf = uscfp[i];
+
+ if (uscf->host.len == host->len
+ && ((uscf->port == 0 && u->resolved->no_port)
+ || uscf->port == u->resolved->port)
+ && ngx_strncasecmp(uscf->host.data, host->data, host->len) == 0)
+ {
+ goto found;
+ }
+ }
+
+ if (u->resolved->sockaddr) {
+
+ if (u->resolved->port == 0
+ && u->resolved->sockaddr->sa_family != AF_UNIX)
+ {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "no port in upstream \"%V\"", host);
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (ngx_http_upstream_create_round_robin_peer(r, u->resolved)
+ != NGX_OK)
+ {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ ngx_http_upstream_connect(r, u);
+
+ return;
+ }
+
+ if (u->resolved->port == 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "no port in upstream \"%V\"", host);
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ temp.name = *host;
+
+ ctx = ngx_resolve_start(clcf->resolver, &temp);
+ if (ctx == NULL) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (ctx == NGX_NO_RESOLVER) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "no resolver defined to resolve %V", host);
+
+ ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);
+ return;
+ }
+
+ ctx->name = *host;
+ ctx->handler = ngx_http_upstream_resolve_handler;
+ ctx->data = r;
+ ctx->timeout = clcf->resolver_timeout;
+
+ u->resolved->ctx = ctx;
+
+ if (ngx_resolve_name(ctx) != NGX_OK) {
+ u->resolved->ctx = NULL;
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ return;
+ }
+
+found:
+
+ if (uscf == NULL) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "no upstream configuration");
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ u->upstream = uscf;
+
+#if (NGX_HTTP_SSL)
+ u->ssl_name = uscf->host;
+#endif
+
+ if (uscf->peer.init(r, uscf) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ u->peer.start_time = ngx_current_msec;
+
+ if (u->conf->next_upstream_tries
+ && u->peer.tries > u->conf->next_upstream_tries)
+ {
+ u->peer.tries = u->conf->next_upstream_tries;
+ }
+
+ ngx_http_upstream_connect(r, u);
+}
+
+
+#if (NGX_HTTP_CACHE)
+
+static ngx_int_t
+ngx_http_upstream_cache(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ ngx_int_t rc;
+ ngx_http_cache_t *c;
+ ngx_http_file_cache_t *cache;
+
+ c = r->cache;
+
+ if (c == NULL) {
+
+ if (!(r->method & u->conf->cache_methods)) {
+ return NGX_DECLINED;
+ }
+
+ rc = ngx_http_upstream_cache_get(r, u, &cache);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ if (r->method == NGX_HTTP_HEAD && u->conf->cache_convert_head) {
+ u->method = ngx_http_core_get_method;
+ }
+
+ if (ngx_http_file_cache_new(r) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (u->create_key(r) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ /* TODO: add keys */
+
+ ngx_http_file_cache_create_key(r);
+
+ if (r->cache->header_start + 256 > u->conf->buffer_size) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "%V_buffer_size %uz is not enough for cache key, "
+ "it should be increased to at least %uz",
+ &u->conf->module, u->conf->buffer_size,
+ ngx_align(r->cache->header_start + 256, 1024));
+
+ r->cache = NULL;
+ return NGX_DECLINED;
+ }
+
+ u->cacheable = 1;
+
+ c = r->cache;
+
+ c->body_start = u->conf->buffer_size;
+ c->min_uses = u->conf->cache_min_uses;
+ c->file_cache = cache;
+
+ switch (ngx_http_test_predicates(r, u->conf->cache_bypass)) {
+
+ case NGX_ERROR:
+ return NGX_ERROR;
+
+ case NGX_DECLINED:
+ u->cache_status = NGX_HTTP_CACHE_BYPASS;
+ return NGX_DECLINED;
+
+ default: /* NGX_OK */
+ break;
+ }
+
+ c->lock = u->conf->cache_lock;
+ c->lock_timeout = u->conf->cache_lock_timeout;
+ c->lock_age = u->conf->cache_lock_age;
+
+ u->cache_status = NGX_HTTP_CACHE_MISS;
+ }
+
+ rc = ngx_http_file_cache_open(r);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http upstream cache: %i", rc);
+
+ switch (rc) {
+
+ case NGX_HTTP_CACHE_STALE:
+
+ if (((u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING)
+ || c->stale_updating) && !r->background
+ && u->conf->cache_background_update)
+ {
+ if (ngx_http_upstream_cache_background_update(r, u) == NGX_OK) {
+ r->cache->background = 1;
+ u->cache_status = rc;
+ rc = NGX_OK;
+
+ } else {
+ rc = NGX_ERROR;
+ }
+ }
+
+ break;
+
+ case NGX_HTTP_CACHE_UPDATING:
+
+ if (((u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING)
+ || c->stale_updating) && !r->background)
+ {
+ u->cache_status = rc;
+ rc = NGX_OK;
+
+ } else {
+ rc = NGX_HTTP_CACHE_STALE;
+ }
+
+ break;
+
+ case NGX_OK:
+ u->cache_status = NGX_HTTP_CACHE_HIT;
+ }
+
+ switch (rc) {
+
+ case NGX_OK:
+
+ return NGX_OK;
+
+ case NGX_HTTP_CACHE_STALE:
+
+ c->valid_sec = 0;
+ c->updating_sec = 0;
+ c->error_sec = 0;
+
+ u->buffer.start = NULL;
+ u->cache_status = NGX_HTTP_CACHE_EXPIRED;
+
+ break;
+
+ case NGX_DECLINED:
+
+ if ((size_t) (u->buffer.end - u->buffer.start) < u->conf->buffer_size) {
+ u->buffer.start = NULL;
+
+ } else {
+ u->buffer.pos = u->buffer.start + c->header_start;
+ u->buffer.last = u->buffer.pos;
+ }
+
+ break;
+
+ case NGX_HTTP_CACHE_SCARCE:
+
+ u->cacheable = 0;
+
+ break;
+
+ case NGX_AGAIN:
+
+ return NGX_BUSY;
+
+ case NGX_ERROR:
+
+ return NGX_ERROR;
+
+ default:
+
+ /* cached NGX_HTTP_BAD_GATEWAY, NGX_HTTP_GATEWAY_TIME_OUT, etc. */
+
+ u->cache_status = NGX_HTTP_CACHE_HIT;
+
+ return rc;
+ }
+
+ if (ngx_http_upstream_cache_check_range(r, u) == NGX_DECLINED) {
+ u->cacheable = 0;
+ }
+
+ r->cached = 0;
+
+ return NGX_DECLINED;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_cache_get(ngx_http_request_t *r, ngx_http_upstream_t *u,
+ ngx_http_file_cache_t **cache)
+{
+ ngx_str_t *name, val;
+ ngx_uint_t i;
+ ngx_http_file_cache_t **caches;
+
+ if (u->conf->cache_zone) {
+ *cache = u->conf->cache_zone->data;
+ return NGX_OK;
+ }
+
+ if (ngx_http_complex_value(r, u->conf->cache_value, &val) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (val.len == 0
+ || (val.len == 3 && ngx_strncmp(val.data, "off", 3) == 0))
+ {
+ return NGX_DECLINED;
+ }
+
+ caches = u->caches->elts;
+
+ for (i = 0; i < u->caches->nelts; i++) {
+ name = &caches[i]->shm_zone->shm.name;
+
+ if (name->len == val.len
+ && ngx_strncmp(name->data, val.data, val.len) == 0)
+ {
+ *cache = caches[i];
+ return NGX_OK;
+ }
+ }
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "cache \"%V\" not found", &val);
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_cache_send(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ ngx_int_t rc;
+ ngx_http_cache_t *c;
+
+ r->cached = 1;
+ c = r->cache;
+
+ if (c->header_start == c->body_start) {
+ r->http_version = NGX_HTTP_VERSION_9;
+ return ngx_http_cache_send(r);
+ }
+
+ /* TODO: cache stack */
+
+ u->buffer = *c->buf;
+ u->buffer.pos += c->header_start;
+
+ ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));
+ u->headers_in.content_length_n = -1;
+ u->headers_in.last_modified_time = -1;
+
+ if (ngx_list_init(&u->headers_in.headers, r->pool, 8,
+ sizeof(ngx_table_elt_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (ngx_list_init(&u->headers_in.trailers, r->pool, 2,
+ sizeof(ngx_table_elt_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ rc = u->process_header(r);
+
+ if (rc == NGX_OK) {
+
+ if (ngx_http_upstream_process_headers(r, u) != NGX_OK) {
+ return NGX_DONE;
+ }
+
+ return ngx_http_cache_send(r);
+ }
+
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (rc == NGX_AGAIN) {
+ rc = NGX_HTTP_UPSTREAM_INVALID_HEADER;
+ }
+
+ /* rc == NGX_HTTP_UPSTREAM_INVALID_HEADER */
+
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
+ "cache file \"%s\" contains invalid header",
+ c->file.name.data);
+
+ /* TODO: delete file */
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_cache_background_update(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ ngx_http_request_t *sr;
+
+ if (r == r->main) {
+ r->preserve_body = 1;
+ }
+
+ if (ngx_http_subrequest(r, &r->uri, &r->args, &sr, NULL,
+ NGX_HTTP_SUBREQUEST_CLONE
+ |NGX_HTTP_SUBREQUEST_BACKGROUND)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ sr->header_only = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_cache_check_range(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ off_t offset;
+ u_char *p, *start;
+ ngx_table_elt_t *h;
+
+ h = r->headers_in.range;
+
+ if (h == NULL
+ || !u->cacheable
+ || u->conf->cache_max_range_offset == NGX_MAX_OFF_T_VALUE)
+ {
+ return NGX_OK;
+ }
+
+ if (u->conf->cache_max_range_offset == 0) {
+ return NGX_DECLINED;
+ }
+
+ if (h->value.len < 7
+ || ngx_strncasecmp(h->value.data, (u_char *) "bytes=", 6) != 0)
+ {
+ return NGX_OK;
+ }
+
+ p = h->value.data + 6;
+
+ while (*p == ' ') { p++; }
+
+ if (*p == '-') {
+ return NGX_DECLINED;
+ }
+
+ start = p;
+
+ while (*p >= '0' && *p <= '9') { p++; }
+
+ offset = ngx_atoof(start, p - start);
+
+ if (offset >= u->conf->cache_max_range_offset) {
+ return NGX_DECLINED;
+ }
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static void
+ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx)
+{
+ ngx_uint_t run_posted;
+ ngx_connection_t *c;
+ ngx_http_request_t *r;
+ ngx_http_upstream_t *u;
+ ngx_http_upstream_resolved_t *ur;
+
+ run_posted = ctx->async;
+
+ r = ctx->data;
+ c = r->connection;
+
+ u = r->upstream;
+ ur = u->resolved;
+
+ ngx_http_set_log_request(c->log, r);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream resolve: \"%V?%V\"", &r->uri, &r->args);
+
+ if (ctx->state) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "%V could not be resolved (%i: %s)",
+ &ctx->name, ctx->state,
+ ngx_resolver_strerror(ctx->state));
+
+ ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);
+ goto failed;
+ }
+
+ ur->naddrs = ctx->naddrs;
+ ur->addrs = ctx->addrs;
+
+#if (NGX_DEBUG)
+ {
+ u_char text[NGX_SOCKADDR_STRLEN];
+ ngx_str_t addr;
+ ngx_uint_t i;
+
+ addr.data = text;
+
+ for (i = 0; i < ctx->naddrs; i++) {
+ addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen,
+ text, NGX_SOCKADDR_STRLEN, 0);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "name was resolved to %V", &addr);
+ }
+ }
+#endif
+
+ if (ngx_http_upstream_create_round_robin_peer(r, ur) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ goto failed;
+ }
+
+ ngx_resolve_name_done(ctx);
+ ur->ctx = NULL;
+
+ u->peer.start_time = ngx_current_msec;
+
+ if (u->conf->next_upstream_tries
+ && u->peer.tries > u->conf->next_upstream_tries)
+ {
+ u->peer.tries = u->conf->next_upstream_tries;
+ }
+
+ ngx_http_upstream_connect(r, u);
+
+failed:
+
+ if (run_posted) {
+ ngx_http_run_posted_requests(c);
+ }
+}
+
+
+static void
+ngx_http_upstream_handler(ngx_event_t *ev)
+{
+ ngx_connection_t *c;
+ ngx_http_request_t *r;
+ ngx_http_upstream_t *u;
+
+ c = ev->data;
+ r = c->data;
+
+ u = r->upstream;
+ c = r->connection;
+
+ ngx_http_set_log_request(c->log, r);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream request: \"%V?%V\"", &r->uri, &r->args);
+
+ if (ev->delayed && ev->timedout) {
+ ev->delayed = 0;
+ ev->timedout = 0;
+ }
+
+ if (ev->write) {
+ u->write_event_handler(r, u);
+
+ } else {
+ u->read_event_handler(r, u);
+ }
+
+ ngx_http_run_posted_requests(c);
+}
+
+
+static void
+ngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r)
+{
+ ngx_http_upstream_check_broken_connection(r, r->connection->read);
+}
+
+
+static void
+ngx_http_upstream_wr_check_broken_connection(ngx_http_request_t *r)
+{
+ ngx_http_upstream_check_broken_connection(r, r->connection->write);
+}
+
+
+static void
+ngx_http_upstream_check_broken_connection(ngx_http_request_t *r,
+ ngx_event_t *ev)
+{
+ int n;
+ char buf[1];
+ ngx_err_t err;
+ ngx_int_t event;
+ ngx_connection_t *c;
+ ngx_http_upstream_t *u;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0,
+ "http upstream check client, write event:%d, \"%V\"",
+ ev->write, &r->uri);
+
+ c = r->connection;
+ u = r->upstream;
+
+ if (c->error) {
+ if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) {
+
+ event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT;
+
+ if (ngx_del_event(ev, event, 0) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+ }
+
+ if (!u->cacheable) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ }
+
+ return;
+ }
+
+#if (NGX_HTTP_V2)
+ if (r->stream) {
+ return;
+ }
+#endif
+
+#if (NGX_HAVE_KQUEUE)
+
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+
+ if (!ev->pending_eof) {
+ return;
+ }
+
+ ev->eof = 1;
+ c->error = 1;
+
+ if (ev->kq_errno) {
+ ev->error = 1;
+ }
+
+ if (!u->cacheable && u->peer.connection) {
+ ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno,
+ "kevent() reported that client prematurely closed "
+ "connection, so upstream connection is closed too");
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ return;
+ }
+
+ ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno,
+ "kevent() reported that client prematurely closed "
+ "connection");
+
+ if (u->peer.connection == NULL) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ }
+
+ return;
+ }
+
+#endif
+
+#if (NGX_HAVE_EPOLLRDHUP)
+
+ if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) && ngx_use_epoll_rdhup) {
+ socklen_t len;
+
+ if (!ev->pending_eof) {
+ return;
+ }
+
+ ev->eof = 1;
+ c->error = 1;
+
+ err = 0;
+ len = sizeof(ngx_err_t);
+
+ /*
+ * BSDs and Linux return 0 and set a pending error in err
+ * Solaris returns -1 and sets errno
+ */
+
+ if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len)
+ == -1)
+ {
+ err = ngx_socket_errno;
+ }
+
+ if (err) {
+ ev->error = 1;
+ }
+
+ if (!u->cacheable && u->peer.connection) {
+ ngx_log_error(NGX_LOG_INFO, ev->log, err,
+ "epoll_wait() reported that client prematurely closed "
+ "connection, so upstream connection is closed too");
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ return;
+ }
+
+ ngx_log_error(NGX_LOG_INFO, ev->log, err,
+ "epoll_wait() reported that client prematurely closed "
+ "connection");
+
+ if (u->peer.connection == NULL) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ }
+
+ return;
+ }
+
+#endif
+
+ n = recv(c->fd, buf, 1, MSG_PEEK);
+
+ err = ngx_socket_errno;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, err,
+ "http upstream recv(): %d", n);
+
+ if (ev->write && (n >= 0 || err == NGX_EAGAIN)) {
+ return;
+ }
+
+ if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) {
+
+ event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT;
+
+ if (ngx_del_event(ev, event, 0) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+ }
+
+ if (n > 0) {
+ return;
+ }
+
+ if (n == -1) {
+ if (err == NGX_EAGAIN) {
+ return;
+ }
+
+ ev->error = 1;
+
+ } else { /* n == 0 */
+ err = 0;
+ }
+
+ ev->eof = 1;
+ c->error = 1;
+
+ if (!u->cacheable && u->peer.connection) {
+ ngx_log_error(NGX_LOG_INFO, ev->log, err,
+ "client prematurely closed connection, "
+ "so upstream connection is closed too");
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ return;
+ }
+
+ ngx_log_error(NGX_LOG_INFO, ev->log, err,
+ "client prematurely closed connection");
+
+ if (u->peer.connection == NULL) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ }
+}
+
+
+static void
+ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ ngx_int_t rc;
+ ngx_connection_t *c;
+
+ r->connection->log->action = "connecting to upstream";
+
+ if (u->state && u->state->response_time == (ngx_msec_t) -1) {
+ u->state->response_time = ngx_current_msec - u->start_time;
+ }
+
+ u->state = ngx_array_push(r->upstream_states);
+ if (u->state == NULL) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ ngx_memzero(u->state, sizeof(ngx_http_upstream_state_t));
+
+ u->start_time = ngx_current_msec;
+
+ u->state->response_time = (ngx_msec_t) -1;
+ u->state->connect_time = (ngx_msec_t) -1;
+ u->state->header_time = (ngx_msec_t) -1;
+
+ rc = ngx_event_connect_peer(&u->peer);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http upstream connect: %i", rc);
+
+ if (rc == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ u->state->peer = u->peer.name;
+
+ if (rc == NGX_BUSY) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no live upstreams");
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_NOLIVE);
+ return;
+ }
+
+ if (rc == NGX_DECLINED) {
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
+ return;
+ }
+
+ /* rc == NGX_OK || rc == NGX_AGAIN || rc == NGX_DONE */
+
+ c = u->peer.connection;
+
+ c->requests++;
+
+ c->data = r;
+
+ c->write->handler = ngx_http_upstream_handler;
+ c->read->handler = ngx_http_upstream_handler;
+
+ u->write_event_handler = ngx_http_upstream_send_request_handler;
+ u->read_event_handler = ngx_http_upstream_process_header;
+
+ c->sendfile &= r->connection->sendfile;
+ u->output.sendfile = c->sendfile;
+
+ if (r->connection->tcp_nopush == NGX_TCP_NOPUSH_DISABLED) {
+ c->tcp_nopush = NGX_TCP_NOPUSH_DISABLED;
+ }
+
+ if (c->pool == NULL) {
+
+ /* we need separate pool here to be able to cache SSL connections */
+
+ c->pool = ngx_create_pool(128, r->connection->log);
+ if (c->pool == NULL) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+ }
+
+ c->log = r->connection->log;
+ c->pool->log = c->log;
+ c->read->log = c->log;
+ c->write->log = c->log;
+
+ /* init or reinit the ngx_output_chain() and ngx_chain_writer() contexts */
+
+ u->writer.out = NULL;
+ u->writer.last = &u->writer.out;
+ u->writer.connection = c;
+ u->writer.limit = 0;
+
+ if (u->request_sent) {
+ if (ngx_http_upstream_reinit(r, u) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+ }
+
+ if (r->request_body
+ && r->request_body->buf
+ && r->request_body->temp_file
+ && r == r->main)
+ {
+ /*
+ * the r->request_body->buf can be reused for one request only,
+ * the subrequests should allocate their own temporary bufs
+ */
+
+ u->output.free = ngx_alloc_chain_link(r->pool);
+ if (u->output.free == NULL) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ u->output.free->buf = r->request_body->buf;
+ u->output.free->next = NULL;
+ u->output.allocated = 1;
+
+ r->request_body->buf->pos = r->request_body->buf->start;
+ r->request_body->buf->last = r->request_body->buf->start;
+ r->request_body->buf->tag = u->output.tag;
+ }
+
+ u->request_sent = 0;
+ u->request_body_sent = 0;
+ u->request_body_blocked = 0;
+
+ if (rc == NGX_AGAIN) {
+ ngx_add_timer(c->write, u->conf->connect_timeout);
+ return;
+ }
+
+#if (NGX_HTTP_SSL)
+
+ if (u->ssl && c->ssl == NULL) {
+ ngx_http_upstream_ssl_init_connection(r, u, c);
+ return;
+ }
+
+#endif
+
+ ngx_http_upstream_send_request(r, u, 1);
+}
+
+
+#if (NGX_HTTP_SSL)
+
+static void
+ngx_http_upstream_ssl_init_connection(ngx_http_request_t *r,
+ ngx_http_upstream_t *u, ngx_connection_t *c)
+{
+ ngx_int_t rc;
+ ngx_http_core_loc_conf_t *clcf;
+
+ if (ngx_http_upstream_test_connect(c) != NGX_OK) {
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
+ return;
+ }
+
+ if (ngx_ssl_create_connection(u->conf->ssl, c,
+ NGX_SSL_BUFFER|NGX_SSL_CLIENT)
+ != NGX_OK)
+ {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ c->sendfile = 0;
+ u->output.sendfile = 0;
+
+ if (u->conf->ssl_server_name || u->conf->ssl_verify) {
+ if (ngx_http_upstream_ssl_name(r, u, c) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+ }
+
+ if (u->conf->ssl_session_reuse) {
+ c->ssl->save_session = ngx_http_upstream_ssl_save_session;
+
+ if (u->peer.set_session(&u->peer, u->peer.data) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ /* abbreviated SSL handshake may interact badly with Nagle */
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+ }
+
+ r->connection->log->action = "SSL handshaking to upstream";
+
+ rc = ngx_ssl_handshake(c);
+
+ if (rc == NGX_AGAIN) {
+
+ if (!c->write->timer_set) {
+ ngx_add_timer(c->write, u->conf->connect_timeout);
+ }
+
+ c->ssl->handler = ngx_http_upstream_ssl_handshake_handler;
+ return;
+ }
+
+ ngx_http_upstream_ssl_handshake(r, u, c);
+}
+
+
+static void
+ngx_http_upstream_ssl_handshake_handler(ngx_connection_t *c)
+{
+ ngx_http_request_t *r;
+ ngx_http_upstream_t *u;
+
+ r = c->data;
+
+ u = r->upstream;
+ c = r->connection;
+
+ ngx_http_set_log_request(c->log, r);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream ssl handshake: \"%V?%V\"",
+ &r->uri, &r->args);
+
+ ngx_http_upstream_ssl_handshake(r, u, u->peer.connection);
+
+ ngx_http_run_posted_requests(c);
+}
+
+
+static void
+ngx_http_upstream_ssl_handshake(ngx_http_request_t *r, ngx_http_upstream_t *u,
+ ngx_connection_t *c)
+{
+ long rc;
+
+ if (c->ssl->handshaked) {
+
+ if (u->conf->ssl_verify) {
+ rc = SSL_get_verify_result(c->ssl->connection);
+
+ if (rc != X509_V_OK) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "upstream SSL certificate verify error: (%l:%s)",
+ rc, X509_verify_cert_error_string(rc));
+ goto failed;
+ }
+
+ if (ngx_ssl_check_host(c, &u->ssl_name) != NGX_OK) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "upstream SSL certificate does not match \"%V\"",
+ &u->ssl_name);
+ goto failed;
+ }
+ }
+
+ c->write->handler = ngx_http_upstream_handler;
+ c->read->handler = ngx_http_upstream_handler;
+
+ ngx_http_upstream_send_request(r, u, 1);
+
+ return;
+ }
+
+ if (c->write->timedout) {
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);
+ return;
+ }
+
+failed:
+
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
+}
+
+
+static void
+ngx_http_upstream_ssl_save_session(ngx_connection_t *c)
+{
+ ngx_http_request_t *r;
+ ngx_http_upstream_t *u;
+
+ if (c->idle) {
+ return;
+ }
+
+ r = c->data;
+
+ u = r->upstream;
+ c = r->connection;
+
+ ngx_http_set_log_request(c->log, r);
+
+ u->peer.save_session(&u->peer, u->peer.data);
+}
+
+
+static ngx_int_t
+ngx_http_upstream_ssl_name(ngx_http_request_t *r, ngx_http_upstream_t *u,
+ ngx_connection_t *c)
+{
+ u_char *p, *last;
+ ngx_str_t name;
+
+ if (u->conf->ssl_name) {
+ if (ngx_http_complex_value(r, u->conf->ssl_name, &name) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ } else {
+ name = u->ssl_name;
+ }
+
+ if (name.len == 0) {
+ goto done;
+ }
+
+ /*
+ * ssl name here may contain port, notably if derived from $proxy_host
+ * or $http_host; we have to strip it
+ */
+
+ p = name.data;
+ last = name.data + name.len;
+
+ if (*p == '[') {
+ p = ngx_strlchr(p, last, ']');
+
+ if (p == NULL) {
+ p = name.data;
+ }
+ }
+
+ p = ngx_strlchr(p, last, ':');
+
+ if (p != NULL) {
+ name.len = p - name.data;
+ }
+
+ if (!u->conf->ssl_server_name) {
+ goto done;
+ }
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+
+ /* as per RFC 6066, literal IPv4 and IPv6 addresses are not permitted */
+
+ if (name.len == 0 || *name.data == '[') {
+ goto done;
+ }
+
+ if (ngx_inet_addr(name.data, name.len) != INADDR_NONE) {
+ goto done;
+ }
+
+ /*
+ * SSL_set_tlsext_host_name() needs a null-terminated string,
+ * hence we explicitly null-terminate name here
+ */
+
+ p = ngx_pnalloc(r->pool, name.len + 1);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ (void) ngx_cpystrn(p, name.data, name.len + 1);
+
+ name.data = p;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "upstream SSL server name: \"%s\"", name.data);
+
+ if (SSL_set_tlsext_host_name(c->ssl->connection,
+ (char *) name.data)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_ERR, r->connection->log, 0,
+ "SSL_set_tlsext_host_name(\"%s\") failed", name.data);
+ return NGX_ERROR;
+ }
+
+#endif
+
+done:
+
+ u->ssl_name = name;
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_upstream_reinit(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ off_t file_pos;
+ ngx_chain_t *cl;
+
+ if (u->reinit_request(r) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ u->keepalive = 0;
+ u->upgrade = 0;
+
+ ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));
+ u->headers_in.content_length_n = -1;
+ u->headers_in.last_modified_time = -1;
+
+ if (ngx_list_init(&u->headers_in.headers, r->pool, 8,
+ sizeof(ngx_table_elt_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (ngx_list_init(&u->headers_in.trailers, r->pool, 2,
+ sizeof(ngx_table_elt_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ /* reinit the request chain */
+
+ file_pos = 0;
+
+ for (cl = u->request_bufs; cl; cl = cl->next) {
+ cl->buf->pos = cl->buf->start;
+
+ /* there is at most one file */
+
+ if (cl->buf->in_file) {
+ cl->buf->file_pos = file_pos;
+ file_pos = cl->buf->file_last;
+ }
+ }
+
+ /* reinit the subrequest's ngx_output_chain() context */
+
+ if (r->request_body && r->request_body->temp_file
+ && r != r->main && u->output.buf)
+ {
+ u->output.free = ngx_alloc_chain_link(r->pool);
+ if (u->output.free == NULL) {
+ return NGX_ERROR;
+ }
+
+ u->output.free->buf = u->output.buf;
+ u->output.free->next = NULL;
+
+ u->output.buf->pos = u->output.buf->start;
+ u->output.buf->last = u->output.buf->start;
+ }
+
+ u->output.buf = NULL;
+ u->output.in = NULL;
+ u->output.busy = NULL;
+
+ /* reinit u->buffer */
+
+ u->buffer.pos = u->buffer.start;
+
+#if (NGX_HTTP_CACHE)
+
+ if (r->cache) {
+ u->buffer.pos += r->cache->header_start;
+ }
+
+#endif
+
+ u->buffer.last = u->buffer.pos;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t *u,
+ ngx_uint_t do_write)
+{
+ ngx_int_t rc;
+ ngx_connection_t *c;
+
+ c = u->peer.connection;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream send request");
+
+ if (u->state->connect_time == (ngx_msec_t) -1) {
+ u->state->connect_time = ngx_current_msec - u->start_time;
+ }
+
+ if (!u->request_sent && ngx_http_upstream_test_connect(c) != NGX_OK) {
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
+ return;
+ }
+
+ c->log->action = "sending request to upstream";
+
+ rc = ngx_http_upstream_send_request_body(r, u, do_write);
+
+ if (rc == NGX_ERROR) {
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
+ return;
+ }
+
+ if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+ ngx_http_upstream_finalize_request(r, u, rc);
+ return;
+ }
+
+ if (rc == NGX_AGAIN) {
+ if (!c->write->ready || u->request_body_blocked) {
+ ngx_add_timer(c->write, u->conf->send_timeout);
+
+ } else if (c->write->timer_set) {
+ ngx_del_timer(c->write);
+ }
+
+ if (ngx_handle_write_event(c->write, u->conf->send_lowat) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (c->write->ready && c->tcp_nopush == NGX_TCP_NOPUSH_SET) {
+ if (ngx_tcp_push(c->fd) == -1) {
+ ngx_log_error(NGX_LOG_CRIT, c->log, ngx_socket_errno,
+ ngx_tcp_push_n " failed");
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;
+ }
+
+ return;
+ }
+
+ /* rc == NGX_OK */
+
+ if (c->write->timer_set) {
+ ngx_del_timer(c->write);
+ }
+
+ if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) {
+ if (ngx_tcp_push(c->fd) == -1) {
+ ngx_log_error(NGX_LOG_CRIT, c->log, ngx_socket_errno,
+ ngx_tcp_push_n " failed");
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;
+ }
+
+ if (!u->conf->preserve_output) {
+ u->write_event_handler = ngx_http_upstream_dummy_handler;
+ }
+
+ if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (!u->request_body_sent) {
+ u->request_body_sent = 1;
+
+ if (u->header_sent) {
+ return;
+ }
+
+ ngx_add_timer(c->read, u->conf->read_timeout);
+
+ if (c->read->ready) {
+ ngx_http_upstream_process_header(r, u);
+ return;
+ }
+ }
+}
+
+
+static ngx_int_t
+ngx_http_upstream_send_request_body(ngx_http_request_t *r,
+ ngx_http_upstream_t *u, ngx_uint_t do_write)
+{
+ ngx_int_t rc;
+ ngx_chain_t *out, *cl, *ln;
+ ngx_connection_t *c;
+ ngx_http_core_loc_conf_t *clcf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http upstream send request body");
+
+ if (!r->request_body_no_buffering) {
+
+ /* buffered request body */
+
+ if (!u->request_sent) {
+ u->request_sent = 1;
+ out = u->request_bufs;
+
+ } else {
+ out = NULL;
+ }
+
+ rc = ngx_output_chain(&u->output, out);
+
+ if (rc == NGX_AGAIN) {
+ u->request_body_blocked = 1;
+
+ } else {
+ u->request_body_blocked = 0;
+ }
+
+ return rc;
+ }
+
+ if (!u->request_sent) {
+ u->request_sent = 1;
+ out = u->request_bufs;
+
+ if (r->request_body->bufs) {
+ for (cl = out; cl->next; cl = cl->next) { /* void */ }
+ cl->next = r->request_body->bufs;
+ r->request_body->bufs = NULL;
+ }
+
+ c = u->peer.connection;
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ r->read_event_handler = ngx_http_upstream_read_request_handler;
+
+ } else {
+ out = NULL;
+ }
+
+ for ( ;; ) {
+
+ if (do_write) {
+ rc = ngx_output_chain(&u->output, out);
+
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ while (out) {
+ ln = out;
+ out = out->next;
+ ngx_free_chain(r->pool, ln);
+ }
+
+ if (rc == NGX_AGAIN) {
+ u->request_body_blocked = 1;
+
+ } else {
+ u->request_body_blocked = 0;
+ }
+
+ if (rc == NGX_OK && !r->reading_body) {
+ break;
+ }
+ }
+
+ if (r->reading_body) {
+ /* read client request body */
+
+ rc = ngx_http_read_unbuffered_request_body(r);
+
+ if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+ return rc;
+ }
+
+ out = r->request_body->bufs;
+ r->request_body->bufs = NULL;
+ }
+
+ /* stop if there is nothing to send */
+
+ if (out == NULL) {
+ rc = NGX_AGAIN;
+ break;
+ }
+
+ do_write = 1;
+ }
+
+ if (!r->reading_body) {
+ if (!u->store && !r->post_action && !u->conf->ignore_client_abort) {
+ r->read_event_handler =
+ ngx_http_upstream_rd_check_broken_connection;
+ }
+ }
+
+ return rc;
+}
+
+
+static void
+ngx_http_upstream_send_request_handler(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ ngx_connection_t *c;
+
+ c = u->peer.connection;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http upstream send request handler");
+
+ if (c->write->timedout) {
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);
+ return;
+ }
+
+#if (NGX_HTTP_SSL)
+
+ if (u->ssl && c->ssl == NULL) {
+ ngx_http_upstream_ssl_init_connection(r, u, c);
+ return;
+ }
+
+#endif
+
+ if (u->header_sent && !u->conf->preserve_output) {
+ u->write_event_handler = ngx_http_upstream_dummy_handler;
+
+ (void) ngx_handle_write_event(c->write, 0);
+
+ return;
+ }
+
+ ngx_http_upstream_send_request(r, u, 1);
+}
+
+
+static void
+ngx_http_upstream_read_request_handler(ngx_http_request_t *r)
+{
+ ngx_connection_t *c;
+ ngx_http_upstream_t *u;
+
+ c = r->connection;
+ u = r->upstream;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http upstream read request handler");
+
+ if (c->read->timedout) {
+ c->timedout = 1;
+ ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT);
+ return;
+ }
+
+ ngx_http_upstream_send_request(r, u, 0);
+}
+
+
+static void
+ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ ssize_t n;
+ ngx_int_t rc;
+ ngx_connection_t *c;
+
+ c = u->peer.connection;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream process header");
+
+ c->log->action = "reading response header from upstream";
+
+ if (c->read->timedout) {
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);
+ return;
+ }
+
+ if (!u->request_sent && ngx_http_upstream_test_connect(c) != NGX_OK) {
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
+ return;
+ }
+
+ if (u->buffer.start == NULL) {
+ u->buffer.start = ngx_palloc(r->pool, u->conf->buffer_size);
+ if (u->buffer.start == NULL) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ u->buffer.pos = u->buffer.start;
+ u->buffer.last = u->buffer.start;
+ u->buffer.end = u->buffer.start + u->conf->buffer_size;
+ u->buffer.temporary = 1;
+
+ u->buffer.tag = u->output.tag;
+
+ if (ngx_list_init(&u->headers_in.headers, r->pool, 8,
+ sizeof(ngx_table_elt_t))
+ != NGX_OK)
+ {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (ngx_list_init(&u->headers_in.trailers, r->pool, 2,
+ sizeof(ngx_table_elt_t))
+ != NGX_OK)
+ {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ if (r->cache) {
+ u->buffer.pos += r->cache->header_start;
+ u->buffer.last = u->buffer.pos;
+ }
+#endif
+ }
+
+ for ( ;; ) {
+
+ n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last);
+
+ if (n == NGX_AGAIN) {
+#if 0
+ ngx_add_timer(rev, u->read_timeout);
+#endif
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ return;
+ }
+
+ if (n == 0) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "upstream prematurely closed connection");
+ }
+
+ if (n == NGX_ERROR || n == 0) {
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
+ return;
+ }
+
+ u->state->bytes_received += n;
+
+ u->buffer.last += n;
+
+#if 0
+ u->valid_header_in = 0;
+
+ u->peer.cached = 0;
+#endif
+
+ rc = u->process_header(r);
+
+ if (rc == NGX_AGAIN) {
+
+ if (u->buffer.last == u->buffer.end) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "upstream sent too big header");
+
+ ngx_http_upstream_next(r, u,
+ NGX_HTTP_UPSTREAM_FT_INVALID_HEADER);
+ return;
+ }
+
+ continue;
+ }
+
+ break;
+ }
+
+ if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) {
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_INVALID_HEADER);
+ return;
+ }
+
+ if (rc == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ /* rc == NGX_OK */
+
+ u->state->header_time = ngx_current_msec - u->start_time;
+
+ if (u->headers_in.status_n >= NGX_HTTP_SPECIAL_RESPONSE) {
+
+ if (ngx_http_upstream_test_next(r, u) == NGX_OK) {
+ return;
+ }
+
+ if (ngx_http_upstream_intercept_errors(r, u) == NGX_OK) {
+ return;
+ }
+ }
+
+ if (ngx_http_upstream_process_headers(r, u) != NGX_OK) {
+ return;
+ }
+
+ ngx_http_upstream_send_response(r, u);
+}
+
+
+static ngx_int_t
+ngx_http_upstream_test_next(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ ngx_msec_t timeout;
+ ngx_uint_t status, mask;
+ ngx_http_upstream_next_t *un;
+
+ status = u->headers_in.status_n;
+
+ for (un = ngx_http_upstream_next_errors; un->status; un++) {
+
+ if (status != un->status) {
+ continue;
+ }
+
+ timeout = u->conf->next_upstream_timeout;
+
+ if (u->request_sent
+ && (r->method & (NGX_HTTP_POST|NGX_HTTP_LOCK|NGX_HTTP_PATCH)))
+ {
+ mask = un->mask | NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT;
+
+ } else {
+ mask = un->mask;
+ }
+
+ if (u->peer.tries > 1
+ && ((u->conf->next_upstream & mask) == mask)
+ && !(u->request_sent && r->request_body_no_buffering)
+ && !(timeout && ngx_current_msec - u->peer.start_time >= timeout))
+ {
+ ngx_http_upstream_next(r, u, un->mask);
+ return NGX_OK;
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ if (u->cache_status == NGX_HTTP_CACHE_EXPIRED
+ && ((u->conf->cache_use_stale & un->mask) || r->cache->stale_error))
+ {
+ ngx_int_t rc;
+
+ rc = u->reinit_request(r);
+
+ if (rc != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, rc);
+ return NGX_OK;
+ }
+
+ u->cache_status = NGX_HTTP_CACHE_STALE;
+ rc = ngx_http_upstream_cache_send(r, u);
+
+ if (rc == NGX_DONE) {
+ return NGX_OK;
+ }
+
+ if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) {
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ ngx_http_upstream_finalize_request(r, u, rc);
+ return NGX_OK;
+ }
+
+#endif
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ if (status == NGX_HTTP_NOT_MODIFIED
+ && u->cache_status == NGX_HTTP_CACHE_EXPIRED
+ && u->conf->cache_revalidate)
+ {
+ time_t now, valid, updating, error;
+ ngx_int_t rc;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http upstream not modified");
+
+ now = ngx_time();
+
+ valid = r->cache->valid_sec;
+ updating = r->cache->updating_sec;
+ error = r->cache->error_sec;
+
+ rc = u->reinit_request(r);
+
+ if (rc != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, rc);
+ return NGX_OK;
+ }
+
+ u->cache_status = NGX_HTTP_CACHE_REVALIDATED;
+ rc = ngx_http_upstream_cache_send(r, u);
+
+ if (rc == NGX_DONE) {
+ return NGX_OK;
+ }
+
+ if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) {
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (valid == 0) {
+ valid = r->cache->valid_sec;
+ updating = r->cache->updating_sec;
+ error = r->cache->error_sec;
+ }
+
+ if (valid == 0) {
+ valid = ngx_http_file_cache_valid(u->conf->cache_valid,
+ u->headers_in.status_n);
+ if (valid) {
+ valid = now + valid;
+ }
+ }
+
+ if (valid) {
+ r->cache->valid_sec = valid;
+ r->cache->updating_sec = updating;
+ r->cache->error_sec = error;
+
+ r->cache->date = now;
+
+ ngx_http_file_cache_update_header(r);
+ }
+
+ ngx_http_upstream_finalize_request(r, u, rc);
+ return NGX_OK;
+ }
+
+#endif
+
+ return NGX_DECLINED;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_intercept_errors(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ ngx_int_t status;
+ ngx_uint_t i;
+ ngx_table_elt_t *h;
+ ngx_http_err_page_t *err_page;
+ ngx_http_core_loc_conf_t *clcf;
+
+ status = u->headers_in.status_n;
+
+ if (status == NGX_HTTP_NOT_FOUND && u->conf->intercept_404) {
+ ngx_http_upstream_finalize_request(r, u, NGX_HTTP_NOT_FOUND);
+ return NGX_OK;
+ }
+
+ if (!u->conf->intercept_errors) {
+ return NGX_DECLINED;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->error_pages == NULL) {
+ return NGX_DECLINED;
+ }
+
+ err_page = clcf->error_pages->elts;
+ for (i = 0; i < clcf->error_pages->nelts; i++) {
+
+ if (err_page[i].status == status) {
+
+ if (status == NGX_HTTP_UNAUTHORIZED
+ && u->headers_in.www_authenticate)
+ {
+ h = ngx_list_push(&r->headers_out.headers);
+
+ if (h == NULL) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_OK;
+ }
+
+ *h = *u->headers_in.www_authenticate;
+
+ r->headers_out.www_authenticate = h;
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ if (r->cache) {
+
+ if (u->cacheable) {
+ time_t valid;
+
+ valid = r->cache->valid_sec;
+
+ if (valid == 0) {
+ valid = ngx_http_file_cache_valid(u->conf->cache_valid,
+ status);
+ if (valid) {
+ r->cache->valid_sec = ngx_time() + valid;
+ }
+ }
+
+ if (valid) {
+ r->cache->error = status;
+ }
+ }
+
+ ngx_http_file_cache_free(r->cache, u->pipe->temp_file);
+ }
+#endif
+ ngx_http_upstream_finalize_request(r, u, status);
+
+ return NGX_OK;
+ }
+ }
+
+ return NGX_DECLINED;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_test_connect(ngx_connection_t *c)
+{
+ int err;
+ socklen_t len;
+
+#if (NGX_HAVE_KQUEUE)
+
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+ if (c->write->pending_eof || c->read->pending_eof) {
+ if (c->write->pending_eof) {
+ err = c->write->kq_errno;
+
+ } else {
+ err = c->read->kq_errno;
+ }
+
+ c->log->action = "connecting to upstream";
+ (void) ngx_connection_error(c, err,
+ "kevent() reported that connect() failed");
+ return NGX_ERROR;
+ }
+
+ } else
+#endif
+ {
+ err = 0;
+ len = sizeof(int);
+
+ /*
+ * BSDs and Linux return 0 and set a pending error in err
+ * Solaris returns -1 and sets errno
+ */
+
+ if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len)
+ == -1)
+ {
+ err = ngx_socket_errno;
+ }
+
+ if (err) {
+ c->log->action = "connecting to upstream";
+ (void) ngx_connection_error(c, err, "connect() failed");
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_headers(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ ngx_str_t uri, args;
+ ngx_uint_t i, flags;
+ ngx_list_part_t *part;
+ ngx_table_elt_t *h;
+ ngx_http_upstream_header_t *hh;
+ ngx_http_upstream_main_conf_t *umcf;
+
+ umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
+
+ if (u->headers_in.x_accel_redirect
+ && !(u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT))
+ {
+ ngx_http_upstream_finalize_request(r, u, NGX_DECLINED);
+
+ part = &u->headers_in.headers.part;
+ h = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ h = part->elts;
+ i = 0;
+ }
+
+ hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash,
+ h[i].lowcase_key, h[i].key.len);
+
+ if (hh && hh->redirect) {
+ if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) {
+ ngx_http_finalize_request(r,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_DONE;
+ }
+ }
+ }
+
+ uri = u->headers_in.x_accel_redirect->value;
+
+ if (uri.data[0] == '@') {
+ ngx_http_named_location(r, &uri);
+
+ } else {
+ ngx_str_null(&args);
+ flags = NGX_HTTP_LOG_UNSAFE;
+
+ if (ngx_http_parse_unsafe_uri(r, &uri, &args, &flags) != NGX_OK) {
+ ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
+ return NGX_DONE;
+ }
+
+ if (r->method != NGX_HTTP_HEAD) {
+ r->method = NGX_HTTP_GET;
+ r->method_name = ngx_http_core_get_method;
+ }
+
+ ngx_http_internal_redirect(r, &uri, &args);
+ }
+
+ ngx_http_finalize_request(r, NGX_DONE);
+ return NGX_DONE;
+ }
+
+ part = &u->headers_in.headers.part;
+ h = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ h = part->elts;
+ i = 0;
+ }
+
+ if (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash,
+ h[i].lowcase_key, h[i].key.len))
+ {
+ continue;
+ }
+
+ hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash,
+ h[i].lowcase_key, h[i].key.len);
+
+ if (hh) {
+ if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_DONE;
+ }
+
+ continue;
+ }
+
+ if (ngx_http_upstream_copy_header_line(r, &h[i], 0) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_DONE;
+ }
+ }
+
+ if (r->headers_out.server && r->headers_out.server->value.data == NULL) {
+ r->headers_out.server->hash = 0;
+ }
+
+ if (r->headers_out.date && r->headers_out.date->value.data == NULL) {
+ r->headers_out.date->hash = 0;
+ }
+
+ r->headers_out.status = u->headers_in.status_n;
+ r->headers_out.status_line = u->headers_in.status_line;
+
+ r->headers_out.content_length_n = u->headers_in.content_length_n;
+
+ r->disable_not_modified = !u->cacheable;
+
+ if (u->conf->force_ranges) {
+ r->allow_ranges = 1;
+ r->single_range = 1;
+
+#if (NGX_HTTP_CACHE)
+ if (r->cached) {
+ r->single_range = 0;
+ }
+#endif
+ }
+
+ u->length = -1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_trailers(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ ngx_uint_t i;
+ ngx_list_part_t *part;
+ ngx_table_elt_t *h, *ho;
+
+ if (!u->conf->pass_trailers) {
+ return NGX_OK;
+ }
+
+ part = &u->headers_in.trailers.part;
+ h = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ h = part->elts;
+ i = 0;
+ }
+
+ if (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash,
+ h[i].lowcase_key, h[i].key.len))
+ {
+ continue;
+ }
+
+ ho = ngx_list_push(&r->headers_out.trailers);
+ if (ho == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ho = h[i];
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ ssize_t n;
+ ngx_int_t rc;
+ ngx_event_pipe_t *p;
+ ngx_connection_t *c;
+ ngx_http_core_loc_conf_t *clcf;
+
+ rc = ngx_http_send_header(r);
+
+ if (rc == NGX_ERROR || rc > NGX_OK || r->post_action) {
+ ngx_http_upstream_finalize_request(r, u, rc);
+ return;
+ }
+
+ u->header_sent = 1;
+
+ if (u->upgrade) {
+
+#if (NGX_HTTP_CACHE)
+
+ if (r->cache) {
+ ngx_http_file_cache_free(r->cache, u->pipe->temp_file);
+ }
+
+#endif
+
+ ngx_http_upstream_upgrade(r, u);
+ return;
+ }
+
+ c = r->connection;
+
+ if (r->header_only) {
+
+ if (!u->buffering) {
+ ngx_http_upstream_finalize_request(r, u, rc);
+ return;
+ }
+
+ if (!u->cacheable && !u->store) {
+ ngx_http_upstream_finalize_request(r, u, rc);
+ return;
+ }
+
+ u->pipe->downstream_error = 1;
+ }
+
+ if (r->request_body && r->request_body->temp_file
+ && r == r->main && !r->preserve_body
+ && !u->conf->preserve_output)
+ {
+ ngx_pool_run_cleanup_file(r->pool, r->request_body->temp_file->file.fd);
+ r->request_body->temp_file->file.fd = NGX_INVALID_FILE;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (!u->buffering) {
+
+#if (NGX_HTTP_CACHE)
+
+ if (r->cache) {
+ ngx_http_file_cache_free(r->cache, u->pipe->temp_file);
+ }
+
+#endif
+
+ if (u->input_filter == NULL) {
+ u->input_filter_init = ngx_http_upstream_non_buffered_filter_init;
+ u->input_filter = ngx_http_upstream_non_buffered_filter;
+ u->input_filter_ctx = r;
+ }
+
+ u->read_event_handler = ngx_http_upstream_process_non_buffered_upstream;
+ r->write_event_handler =
+ ngx_http_upstream_process_non_buffered_downstream;
+
+ r->limit_rate = 0;
+ r->limit_rate_set = 1;
+
+ if (u->input_filter_init(u->input_filter_ctx) == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ if (clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ n = u->buffer.last - u->buffer.pos;
+
+ if (n) {
+ u->buffer.last = u->buffer.pos;
+
+ u->state->response_length += n;
+
+ if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ ngx_http_upstream_process_non_buffered_downstream(r);
+
+ } else {
+ u->buffer.pos = u->buffer.start;
+ u->buffer.last = u->buffer.start;
+
+ if (ngx_http_send_special(r, NGX_HTTP_FLUSH) == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ if (u->peer.connection->read->ready || u->length == 0) {
+ ngx_http_upstream_process_non_buffered_upstream(r, u);
+ }
+ }
+
+ return;
+ }
+
+ /* TODO: preallocate event_pipe bufs, look "Content-Length" */
+
+#if (NGX_HTTP_CACHE)
+
+ if (r->cache && r->cache->file.fd != NGX_INVALID_FILE) {
+ ngx_pool_run_cleanup_file(r->pool, r->cache->file.fd);
+ r->cache->file.fd = NGX_INVALID_FILE;
+ }
+
+ switch (ngx_http_test_predicates(r, u->conf->no_cache)) {
+
+ case NGX_ERROR:
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+
+ case NGX_DECLINED:
+ u->cacheable = 0;
+ break;
+
+ default: /* NGX_OK */
+
+ if (u->cache_status == NGX_HTTP_CACHE_BYPASS) {
+
+ /* create cache if previously bypassed */
+
+ if (ngx_http_file_cache_create(r) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+ }
+
+ break;
+ }
+
+ if (u->cacheable) {
+ time_t now, valid;
+
+ now = ngx_time();
+
+ valid = r->cache->valid_sec;
+
+ if (valid == 0) {
+ valid = ngx_http_file_cache_valid(u->conf->cache_valid,
+ u->headers_in.status_n);
+ if (valid) {
+ r->cache->valid_sec = now + valid;
+ }
+ }
+
+ if (valid) {
+ r->cache->date = now;
+ r->cache->body_start = (u_short) (u->buffer.pos - u->buffer.start);
+
+ if (u->headers_in.status_n == NGX_HTTP_OK
+ || u->headers_in.status_n == NGX_HTTP_PARTIAL_CONTENT)
+ {
+ r->cache->last_modified = u->headers_in.last_modified_time;
+
+ if (u->headers_in.etag) {
+ r->cache->etag = u->headers_in.etag->value;
+
+ } else {
+ ngx_str_null(&r->cache->etag);
+ }
+
+ } else {
+ r->cache->last_modified = -1;
+ ngx_str_null(&r->cache->etag);
+ }
+
+ if (ngx_http_file_cache_set_header(r, u->buffer.start) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ } else {
+ u->cacheable = 0;
+ }
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http cacheable: %d", u->cacheable);
+
+ if (u->cacheable == 0 && r->cache) {
+ ngx_http_file_cache_free(r->cache, u->pipe->temp_file);
+ }
+
+ if (r->header_only && !u->cacheable && !u->store) {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+#endif
+
+ p = u->pipe;
+
+ p->output_filter = ngx_http_upstream_output_filter;
+ p->output_ctx = r;
+ p->tag = u->output.tag;
+ p->bufs = u->conf->bufs;
+ p->busy_size = u->conf->busy_buffers_size;
+ p->upstream = u->peer.connection;
+ p->downstream = c;
+ p->pool = r->pool;
+ p->log = c->log;
+ p->limit_rate = u->conf->limit_rate;
+ p->start_sec = ngx_time();
+
+ p->cacheable = u->cacheable || u->store;
+
+ p->temp_file = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
+ if (p->temp_file == NULL) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ p->temp_file->file.fd = NGX_INVALID_FILE;
+ p->temp_file->file.log = c->log;
+ p->temp_file->path = u->conf->temp_path;
+ p->temp_file->pool = r->pool;
+
+ if (p->cacheable) {
+ p->temp_file->persistent = 1;
+
+#if (NGX_HTTP_CACHE)
+ if (r->cache && !r->cache->file_cache->use_temp_path) {
+ p->temp_file->path = r->cache->file_cache->path;
+ p->temp_file->file.name = r->cache->file.name;
+ }
+#endif
+
+ } else {
+ p->temp_file->log_level = NGX_LOG_WARN;
+ p->temp_file->warn = "an upstream response is buffered "
+ "to a temporary file";
+ }
+
+ p->max_temp_file_size = u->conf->max_temp_file_size;
+ p->temp_file_write_size = u->conf->temp_file_write_size;
+
+#if (NGX_THREADS)
+ if (clcf->aio == NGX_HTTP_AIO_THREADS && clcf->aio_write) {
+ p->thread_handler = ngx_http_upstream_thread_handler;
+ p->thread_ctx = r;
+ }
+#endif
+
+ p->preread_bufs = ngx_alloc_chain_link(r->pool);
+ if (p->preread_bufs == NULL) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ p->preread_bufs->buf = &u->buffer;
+ p->preread_bufs->next = NULL;
+ u->buffer.recycled = 1;
+
+ p->preread_size = u->buffer.last - u->buffer.pos;
+
+ if (u->cacheable) {
+
+ p->buf_to_file = ngx_calloc_buf(r->pool);
+ if (p->buf_to_file == NULL) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ p->buf_to_file->start = u->buffer.start;
+ p->buf_to_file->pos = u->buffer.start;
+ p->buf_to_file->last = u->buffer.pos;
+ p->buf_to_file->temporary = 1;
+ }
+
+ if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
+ /* the posted aio operation may corrupt a shadow buffer */
+ p->single_buf = 1;
+ }
+
+ /* TODO: p->free_bufs = 0 if use ngx_create_chain_of_bufs() */
+ p->free_bufs = 1;
+
+ /*
+ * event_pipe would do u->buffer.last += p->preread_size
+ * as though these bytes were read
+ */
+ u->buffer.last = u->buffer.pos;
+
+ if (u->conf->cyclic_temp_file) {
+
+ /*
+ * we need to disable the use of sendfile() if we use cyclic temp file
+ * because the writing a new data may interfere with sendfile()
+ * that uses the same kernel file pages (at least on FreeBSD)
+ */
+
+ p->cyclic_temp_file = 1;
+ c->sendfile = 0;
+
+ } else {
+ p->cyclic_temp_file = 0;
+ }
+
+ p->read_timeout = u->conf->read_timeout;
+ p->send_timeout = clcf->send_timeout;
+ p->send_lowat = clcf->send_lowat;
+
+ p->length = -1;
+
+ if (u->input_filter_init
+ && u->input_filter_init(p->input_ctx) != NGX_OK)
+ {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ u->read_event_handler = ngx_http_upstream_process_upstream;
+ r->write_event_handler = ngx_http_upstream_process_downstream;
+
+ ngx_http_upstream_process_upstream(r, u);
+}
+
+
+static void
+ngx_http_upstream_upgrade(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ ngx_connection_t *c;
+ ngx_http_core_loc_conf_t *clcf;
+
+ c = r->connection;
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ /* TODO: prevent upgrade if not requested or not possible */
+
+ if (r != r->main) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "connection upgrade in subrequest");
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ r->keepalive = 0;
+ c->log->action = "proxying upgraded connection";
+
+ u->read_event_handler = ngx_http_upstream_upgraded_read_upstream;
+ u->write_event_handler = ngx_http_upstream_upgraded_write_upstream;
+ r->read_event_handler = ngx_http_upstream_upgraded_read_downstream;
+ r->write_event_handler = ngx_http_upstream_upgraded_write_downstream;
+
+ if (clcf->tcp_nodelay) {
+
+ if (ngx_tcp_nodelay(c) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ if (ngx_tcp_nodelay(u->peer.connection) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+ }
+
+ if (ngx_http_send_special(r, NGX_HTTP_FLUSH) == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ if (u->peer.connection->read->ready
+ || u->buffer.pos != u->buffer.last)
+ {
+ ngx_post_event(c->read, &ngx_posted_events);
+ ngx_http_upstream_process_upgraded(r, 1, 1);
+ return;
+ }
+
+ ngx_http_upstream_process_upgraded(r, 0, 1);
+}
+
+
+static void
+ngx_http_upstream_upgraded_read_downstream(ngx_http_request_t *r)
+{
+ ngx_http_upstream_process_upgraded(r, 0, 0);
+}
+
+
+static void
+ngx_http_upstream_upgraded_write_downstream(ngx_http_request_t *r)
+{
+ ngx_http_upstream_process_upgraded(r, 1, 1);
+}
+
+
+static void
+ngx_http_upstream_upgraded_read_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ ngx_http_upstream_process_upgraded(r, 1, 0);
+}
+
+
+static void
+ngx_http_upstream_upgraded_write_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ ngx_http_upstream_process_upgraded(r, 0, 1);
+}
+
+
+static void
+ngx_http_upstream_process_upgraded(ngx_http_request_t *r,
+ ngx_uint_t from_upstream, ngx_uint_t do_write)
+{
+ size_t size;
+ ssize_t n;
+ ngx_buf_t *b;
+ ngx_uint_t flags;
+ ngx_connection_t *c, *downstream, *upstream, *dst, *src;
+ ngx_http_upstream_t *u;
+ ngx_http_core_loc_conf_t *clcf;
+
+ c = r->connection;
+ u = r->upstream;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream process upgraded, fu:%ui", from_upstream);
+
+ downstream = c;
+ upstream = u->peer.connection;
+
+ if (downstream->write->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;
+ }
+
+ if (upstream->read->timedout || upstream->write->timedout) {
+ ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
+ ngx_http_upstream_finalize_request(r, u, NGX_HTTP_GATEWAY_TIME_OUT);
+ return;
+ }
+
+ if (from_upstream) {
+ src = upstream;
+ dst = downstream;
+ b = &u->buffer;
+
+ } else {
+ src = downstream;
+ dst = upstream;
+ b = &u->from_client;
+
+ if (r->header_in->last > r->header_in->pos) {
+ b = r->header_in;
+ b->end = b->last;
+ do_write = 1;
+ }
+
+ if (b->start == NULL) {
+ b->start = ngx_palloc(r->pool, u->conf->buffer_size);
+ if (b->start == NULL) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ b->pos = b->start;
+ b->last = b->start;
+ b->end = b->start + u->conf->buffer_size;
+ b->temporary = 1;
+ b->tag = u->output.tag;
+ }
+ }
+
+ for ( ;; ) {
+
+ if (do_write) {
+
+ size = b->last - b->pos;
+
+ if (size && dst->write->ready) {
+
+ n = dst->send(dst, b->pos, size);
+
+ if (n == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ if (n > 0) {
+ b->pos += n;
+
+ if (b->pos == b->last) {
+ b->pos = b->start;
+ b->last = b->start;
+ }
+ }
+ }
+ }
+
+ size = b->end - b->last;
+
+ if (size && src->read->ready) {
+
+ n = src->recv(src, b->last, size);
+
+ if (n == NGX_AGAIN || n == 0) {
+ break;
+ }
+
+ if (n > 0) {
+ do_write = 1;
+ b->last += n;
+
+ if (from_upstream) {
+ u->state->bytes_received += n;
+ }
+
+ continue;
+ }
+
+ if (n == NGX_ERROR) {
+ src->read->eof = 1;
+ }
+ }
+
+ break;
+ }
+
+ if ((upstream->read->eof && u->buffer.pos == u->buffer.last)
+ || (downstream->read->eof && u->from_client.pos == u->from_client.last)
+ || (downstream->read->eof && upstream->read->eof))
+ {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream upgraded done");
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (ngx_handle_write_event(upstream->write, u->conf->send_lowat)
+ != NGX_OK)
+ {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ if (upstream->write->active && !upstream->write->ready) {
+ ngx_add_timer(upstream->write, u->conf->send_timeout);
+
+ } else if (upstream->write->timer_set) {
+ ngx_del_timer(upstream->write);
+ }
+
+ if (upstream->read->eof || upstream->read->error) {
+ flags = NGX_CLOSE_EVENT;
+
+ } else {
+ flags = 0;
+ }
+
+ if (ngx_handle_read_event(upstream->read, flags) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ if (upstream->read->active && !upstream->read->ready) {
+ ngx_add_timer(upstream->read, u->conf->read_timeout);
+
+ } else if (upstream->read->timer_set) {
+ ngx_del_timer(upstream->read);
+ }
+
+ if (ngx_handle_write_event(downstream->write, clcf->send_lowat)
+ != NGX_OK)
+ {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ if (downstream->read->eof || downstream->read->error) {
+ flags = NGX_CLOSE_EVENT;
+
+ } else {
+ flags = 0;
+ }
+
+ if (ngx_handle_read_event(downstream->read, flags) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ if (downstream->write->active && !downstream->write->ready) {
+ ngx_add_timer(downstream->write, clcf->send_timeout);
+
+ } else if (downstream->write->timer_set) {
+ ngx_del_timer(downstream->write);
+ }
+}
+
+
+static void
+ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r)
+{
+ ngx_event_t *wev;
+ ngx_connection_t *c;
+ ngx_http_upstream_t *u;
+
+ c = r->connection;
+ u = r->upstream;
+ wev = c->write;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream process non buffered downstream");
+
+ c->log->action = "sending to client";
+
+ if (wev->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;
+ }
+
+ ngx_http_upstream_process_non_buffered_request(r, 1);
+}
+
+
+static void
+ngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ ngx_connection_t *c;
+
+ c = u->peer.connection;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream process non buffered upstream");
+
+ c->log->action = "reading upstream";
+
+ if (c->read->timedout) {
+ ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
+ ngx_http_upstream_finalize_request(r, u, NGX_HTTP_GATEWAY_TIME_OUT);
+ return;
+ }
+
+ ngx_http_upstream_process_non_buffered_request(r, 0);
+}
+
+
+static void
+ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,
+ ngx_uint_t do_write)
+{
+ size_t size;
+ ssize_t n;
+ ngx_buf_t *b;
+ ngx_int_t rc;
+ ngx_uint_t flags;
+ ngx_connection_t *downstream, *upstream;
+ ngx_http_upstream_t *u;
+ ngx_http_core_loc_conf_t *clcf;
+
+ u = r->upstream;
+ downstream = r->connection;
+ upstream = u->peer.connection;
+
+ b = &u->buffer;
+
+ do_write = do_write || u->length == 0;
+
+ for ( ;; ) {
+
+ if (do_write) {
+
+ if (u->out_bufs || u->busy_bufs || downstream->buffered) {
+ rc = ngx_http_output_filter(r, u->out_bufs);
+
+ if (rc == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ ngx_chain_update_chains(r->pool, &u->free_bufs, &u->busy_bufs,
+ &u->out_bufs, u->output.tag);
+ }
+
+ if (u->busy_bufs == NULL) {
+
+ if (u->length == 0
+ || (upstream->read->eof && u->length == -1))
+ {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (upstream->read->eof) {
+ ngx_log_error(NGX_LOG_ERR, upstream->log, 0,
+ "upstream prematurely closed connection");
+
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_BAD_GATEWAY);
+ return;
+ }
+
+ if (upstream->read->error) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_BAD_GATEWAY);
+ return;
+ }
+
+ b->pos = b->start;
+ b->last = b->start;
+ }
+ }
+
+ size = b->end - b->last;
+
+ if (size && upstream->read->ready) {
+
+ n = upstream->recv(upstream, b->last, size);
+
+ if (n == NGX_AGAIN) {
+ break;
+ }
+
+ if (n > 0) {
+ u->state->bytes_received += n;
+ u->state->response_length += n;
+
+ if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+ }
+
+ do_write = 1;
+
+ continue;
+ }
+
+ break;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (downstream->data == r) {
+ if (ngx_handle_write_event(downstream->write, clcf->send_lowat)
+ != NGX_OK)
+ {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+ }
+
+ if (downstream->write->active && !downstream->write->ready) {
+ ngx_add_timer(downstream->write, clcf->send_timeout);
+
+ } else if (downstream->write->timer_set) {
+ ngx_del_timer(downstream->write);
+ }
+
+ if (upstream->read->eof || upstream->read->error) {
+ flags = NGX_CLOSE_EVENT;
+
+ } else {
+ flags = 0;
+ }
+
+ if (ngx_handle_read_event(upstream->read, flags) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ if (upstream->read->active && !upstream->read->ready) {
+ ngx_add_timer(upstream->read, u->conf->read_timeout);
+
+ } else if (upstream->read->timer_set) {
+ ngx_del_timer(upstream->read);
+ }
+}
+
+
+static ngx_int_t
+ngx_http_upstream_non_buffered_filter_init(void *data)
+{
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_non_buffered_filter(void *data, ssize_t bytes)
+{
+ ngx_http_request_t *r = data;
+
+ ngx_buf_t *b;
+ ngx_chain_t *cl, **ll;
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+
+ for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
+ ll = &cl->next;
+ }
+
+ cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ll = cl;
+
+ cl->buf->flush = 1;
+ cl->buf->memory = 1;
+
+ b = &u->buffer;
+
+ cl->buf->pos = b->last;
+ b->last += bytes;
+ cl->buf->last = b->last;
+ cl->buf->tag = u->output.tag;
+
+ if (u->length == -1) {
+ return NGX_OK;
+ }
+
+ u->length -= bytes;
+
+ return NGX_OK;
+}
+
+
+#if (NGX_THREADS)
+
+static ngx_int_t
+ngx_http_upstream_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)
+{
+ ngx_str_t name;
+ ngx_event_pipe_t *p;
+ ngx_thread_pool_t *tp;
+ ngx_http_request_t *r;
+ ngx_http_core_loc_conf_t *clcf;
+
+ r = file->thread_ctx;
+ p = r->upstream->pipe;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+ tp = clcf->thread_pool;
+
+ if (tp == NULL) {
+ if (ngx_http_complex_value(r, clcf->thread_pool_value, &name)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ tp = ngx_thread_pool_get((ngx_cycle_t *) ngx_cycle, &name);
+
+ if (tp == NULL) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "thread pool \"%V\" not found", &name);
+ return NGX_ERROR;
+ }
+ }
+
+ task->event.data = r;
+ task->event.handler = ngx_http_upstream_thread_event_handler;
+
+ if (ngx_thread_task_post(tp, task) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ r->main->blocked++;
+ r->aio = 1;
+ p->aio = 1;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_upstream_thread_event_handler(ngx_event_t *ev)
+{
+ ngx_connection_t *c;
+ ngx_http_request_t *r;
+
+ r = ev->data;
+ c = r->connection;
+
+ ngx_http_set_log_request(c->log, r);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream thread: \"%V?%V\"", &r->uri, &r->args);
+
+ r->main->blocked--;
+ r->aio = 0;
+
+ if (r->done) {
+ /*
+ * trigger connection event handler if the subrequest was
+ * already finalized; this can happen if the handler is used
+ * for sendfile() in threads
+ */
+
+ c->write->handler(c->write);
+
+ } else {
+ r->write_event_handler(r);
+ ngx_http_run_posted_requests(c);
+ }
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_upstream_output_filter(void *data, ngx_chain_t *chain)
+{
+ ngx_int_t rc;
+ ngx_event_pipe_t *p;
+ ngx_http_request_t *r;
+
+ r = data;
+ p = r->upstream->pipe;
+
+ rc = ngx_http_output_filter(r, chain);
+
+ p->aio = r->aio;
+
+ return rc;
+}
+
+
+static void
+ngx_http_upstream_process_downstream(ngx_http_request_t *r)
+{
+ ngx_event_t *wev;
+ ngx_connection_t *c;
+ ngx_event_pipe_t *p;
+ ngx_http_upstream_t *u;
+
+ c = r->connection;
+ u = r->upstream;
+ p = u->pipe;
+ wev = c->write;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream process downstream");
+
+ c->log->action = "sending to client";
+
+#if (NGX_THREADS)
+ p->aio = r->aio;
+#endif
+
+ if (wev->timedout) {
+
+ p->downstream_error = 1;
+ c->timedout = 1;
+ ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");
+
+ } else {
+
+ if (wev->delayed) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http downstream delayed");
+
+ if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ }
+
+ return;
+ }
+
+ if (ngx_event_pipe(p, 1) == NGX_ABORT) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+ }
+
+ ngx_http_upstream_process_request(r, u);
+}
+
+
+static void
+ngx_http_upstream_process_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ ngx_event_t *rev;
+ ngx_event_pipe_t *p;
+ ngx_connection_t *c;
+
+ c = u->peer.connection;
+ p = u->pipe;
+ rev = c->read;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream process upstream");
+
+ c->log->action = "reading upstream";
+
+ if (rev->timedout) {
+
+ p->upstream_error = 1;
+ ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
+
+ } else {
+
+ if (rev->delayed) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream delayed");
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ }
+
+ return;
+ }
+
+ if (ngx_event_pipe(p, 0) == NGX_ABORT) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+ }
+
+ ngx_http_upstream_process_request(r, u);
+}
+
+
+static void
+ngx_http_upstream_process_request(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ ngx_temp_file_t *tf;
+ ngx_event_pipe_t *p;
+
+ p = u->pipe;
+
+#if (NGX_THREADS)
+
+ if (p->writing && !p->aio) {
+
+ /*
+ * make sure to call ngx_event_pipe()
+ * if there is an incomplete aio write
+ */
+
+ if (ngx_event_pipe(p, 1) == NGX_ABORT) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+ }
+
+ if (p->writing) {
+ return;
+ }
+
+#endif
+
+ if (u->peer.connection) {
+
+ if (u->store) {
+
+ if (p->upstream_eof || p->upstream_done) {
+
+ tf = p->temp_file;
+
+ if (u->headers_in.status_n == NGX_HTTP_OK
+ && (p->upstream_done || p->length == -1)
+ && (u->headers_in.content_length_n == -1
+ || u->headers_in.content_length_n == tf->offset))
+ {
+ ngx_http_upstream_store(r, u);
+ }
+ }
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ if (u->cacheable) {
+
+ if (p->upstream_done) {
+ ngx_http_file_cache_update(r, p->temp_file);
+
+ } else if (p->upstream_eof) {
+
+ tf = p->temp_file;
+
+ if (p->length == -1
+ && (u->headers_in.content_length_n == -1
+ || u->headers_in.content_length_n
+ == tf->offset - (off_t) r->cache->body_start))
+ {
+ ngx_http_file_cache_update(r, tf);
+
+ } else {
+ ngx_http_file_cache_free(r->cache, tf);
+ }
+
+ } else if (p->upstream_error) {
+ ngx_http_file_cache_free(r->cache, p->temp_file);
+ }
+ }
+
+#endif
+
+ if (p->upstream_done || p->upstream_eof || p->upstream_error) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http upstream exit: %p", p->out);
+
+ if (p->upstream_done
+ || (p->upstream_eof && p->length == -1))
+ {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (p->upstream_eof) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "upstream prematurely closed connection");
+ }
+
+ ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);
+ return;
+ }
+ }
+
+ if (p->downstream_error) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http upstream downstream error");
+
+ if (!u->cacheable && !u->store && u->peer.connection) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ }
+ }
+}
+
+
+static void
+ngx_http_upstream_store(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ size_t root;
+ time_t lm;
+ ngx_str_t path;
+ ngx_temp_file_t *tf;
+ ngx_ext_rename_file_t ext;
+
+ tf = u->pipe->temp_file;
+
+ if (tf->file.fd == NGX_INVALID_FILE) {
+
+ /* create file for empty 200 response */
+
+ tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
+ if (tf == NULL) {
+ return;
+ }
+
+ tf->file.fd = NGX_INVALID_FILE;
+ tf->file.log = r->connection->log;
+ tf->path = u->conf->temp_path;
+ tf->pool = r->pool;
+ tf->persistent = 1;
+
+ if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,
+ tf->persistent, tf->clean, tf->access)
+ != NGX_OK)
+ {
+ return;
+ }
+
+ u->pipe->temp_file = tf;
+ }
+
+ ext.access = u->conf->store_access;
+ ext.path_access = u->conf->store_access;
+ ext.time = -1;
+ ext.create_path = 1;
+ ext.delete_file = 1;
+ ext.log = r->connection->log;
+
+ if (u->headers_in.last_modified) {
+
+ lm = ngx_parse_http_time(u->headers_in.last_modified->value.data,
+ u->headers_in.last_modified->value.len);
+
+ if (lm != NGX_ERROR) {
+ ext.time = lm;
+ ext.fd = tf->file.fd;
+ }
+ }
+
+ if (u->conf->store_lengths == NULL) {
+
+ if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
+ return;
+ }
+
+ } else {
+ if (ngx_http_script_run(r, &path, u->conf->store_lengths->elts, 0,
+ u->conf->store_values->elts)
+ == NULL)
+ {
+ return;
+ }
+ }
+
+ path.len--;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "upstream stores \"%s\" to \"%s\"",
+ tf->file.name.data, path.data);
+
+ (void) ngx_ext_rename_file(&tf->file.name, &path, &ext);
+
+ u->store = 0;
+}
+
+
+static void
+ngx_http_upstream_dummy_handler(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http upstream dummy handler");
+}
+
+
+static void
+ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u,
+ ngx_uint_t ft_type)
+{
+ ngx_msec_t timeout;
+ ngx_uint_t status, state;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http next upstream, %xi", ft_type);
+
+ if (u->peer.sockaddr) {
+
+ if (u->peer.connection) {
+ u->state->bytes_sent = u->peer.connection->sent;
+ }
+
+ if (ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_403
+ || ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_404)
+ {
+ state = NGX_PEER_NEXT;
+
+ } else {
+ state = NGX_PEER_FAILED;
+ }
+
+ u->peer.free(&u->peer, u->peer.data, state);
+ u->peer.sockaddr = NULL;
+ }
+
+ if (ft_type == NGX_HTTP_UPSTREAM_FT_TIMEOUT) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_ETIMEDOUT,
+ "upstream timed out");
+ }
+
+ if (u->peer.cached && ft_type == NGX_HTTP_UPSTREAM_FT_ERROR) {
+ /* TODO: inform balancer instead */
+ u->peer.tries++;
+ }
+
+ switch (ft_type) {
+
+ case NGX_HTTP_UPSTREAM_FT_TIMEOUT:
+ case NGX_HTTP_UPSTREAM_FT_HTTP_504:
+ status = NGX_HTTP_GATEWAY_TIME_OUT;
+ break;
+
+ case NGX_HTTP_UPSTREAM_FT_HTTP_500:
+ status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ break;
+
+ case NGX_HTTP_UPSTREAM_FT_HTTP_503:
+ status = NGX_HTTP_SERVICE_UNAVAILABLE;
+ break;
+
+ case NGX_HTTP_UPSTREAM_FT_HTTP_403:
+ status = NGX_HTTP_FORBIDDEN;
+ break;
+
+ case NGX_HTTP_UPSTREAM_FT_HTTP_404:
+ status = NGX_HTTP_NOT_FOUND;
+ break;
+
+ case NGX_HTTP_UPSTREAM_FT_HTTP_429:
+ status = NGX_HTTP_TOO_MANY_REQUESTS;
+ break;
+
+ /*
+ * NGX_HTTP_UPSTREAM_FT_BUSY_LOCK and NGX_HTTP_UPSTREAM_FT_MAX_WAITING
+ * never reach here
+ */
+
+ default:
+ status = NGX_HTTP_BAD_GATEWAY;
+ }
+
+ if (r->connection->error) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ return;
+ }
+
+ u->state->status = status;
+
+ timeout = u->conf->next_upstream_timeout;
+
+ if (u->request_sent
+ && (r->method & (NGX_HTTP_POST|NGX_HTTP_LOCK|NGX_HTTP_PATCH)))
+ {
+ ft_type |= NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT;
+ }
+
+ if (u->peer.tries == 0
+ || ((u->conf->next_upstream & ft_type) != ft_type)
+ || (u->request_sent && r->request_body_no_buffering)
+ || (timeout && ngx_current_msec - u->peer.start_time >= timeout))
+ {
+#if (NGX_HTTP_CACHE)
+
+ if (u->cache_status == NGX_HTTP_CACHE_EXPIRED
+ && ((u->conf->cache_use_stale & ft_type) || r->cache->stale_error))
+ {
+ ngx_int_t rc;
+
+ rc = u->reinit_request(r);
+
+ if (rc != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, rc);
+ return;
+ }
+
+ u->cache_status = NGX_HTTP_CACHE_STALE;
+ rc = ngx_http_upstream_cache_send(r, u);
+
+ if (rc == NGX_DONE) {
+ return;
+ }
+
+ if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) {
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ ngx_http_upstream_finalize_request(r, u, rc);
+ return;
+ }
+#endif
+
+ ngx_http_upstream_finalize_request(r, u, status);
+ return;
+ }
+
+ if (u->peer.connection) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "close http upstream connection: %d",
+ u->peer.connection->fd);
+#if (NGX_HTTP_SSL)
+
+ if (u->peer.connection->ssl) {
+ u->peer.connection->ssl->no_wait_shutdown = 1;
+ u->peer.connection->ssl->no_send_shutdown = 1;
+
+ (void) ngx_ssl_shutdown(u->peer.connection);
+ }
+#endif
+
+ if (u->peer.connection->pool) {
+ ngx_destroy_pool(u->peer.connection->pool);
+ }
+
+ ngx_close_connection(u->peer.connection);
+ u->peer.connection = NULL;
+ }
+
+ ngx_http_upstream_connect(r, u);
+}
+
+
+static void
+ngx_http_upstream_cleanup(void *data)
+{
+ ngx_http_request_t *r = data;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "cleanup http upstream request: \"%V\"", &r->uri);
+
+ ngx_http_upstream_finalize_request(r, r->upstream, NGX_DONE);
+}
+
+
+static void
+ngx_http_upstream_finalize_request(ngx_http_request_t *r,
+ ngx_http_upstream_t *u, ngx_int_t rc)
+{
+ ngx_uint_t flush;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "finalize http upstream request: %i", rc);
+
+ if (u->cleanup == NULL) {
+ /* the request was already finalized */
+ ngx_http_finalize_request(r, NGX_DONE);
+ return;
+ }
+
+ *u->cleanup = NULL;
+ u->cleanup = NULL;
+
+ if (u->resolved && u->resolved->ctx) {
+ ngx_resolve_name_done(u->resolved->ctx);
+ u->resolved->ctx = NULL;
+ }
+
+ if (u->state && u->state->response_time == (ngx_msec_t) -1) {
+ u->state->response_time = ngx_current_msec - u->start_time;
+
+ if (u->pipe && u->pipe->read_length) {
+ u->state->bytes_received += u->pipe->read_length
+ - u->pipe->preread_size;
+ u->state->response_length = u->pipe->read_length;
+ }
+
+ if (u->peer.connection) {
+ u->state->bytes_sent = u->peer.connection->sent;
+ }
+ }
+
+ u->finalize_request(r, rc);
+
+ if (u->peer.free && u->peer.sockaddr) {
+ u->peer.free(&u->peer, u->peer.data, 0);
+ u->peer.sockaddr = NULL;
+ }
+
+ if (u->peer.connection) {
+
+#if (NGX_HTTP_SSL)
+
+ /* TODO: do not shutdown persistent connection */
+
+ if (u->peer.connection->ssl) {
+
+ /*
+ * We send the "close notify" shutdown alert to the upstream only
+ * and do not wait its "close notify" shutdown alert.
+ * It is acceptable according to the TLS standard.
+ */
+
+ u->peer.connection->ssl->no_wait_shutdown = 1;
+
+ (void) ngx_ssl_shutdown(u->peer.connection);
+ }
+#endif
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "close http upstream connection: %d",
+ u->peer.connection->fd);
+
+ if (u->peer.connection->pool) {
+ ngx_destroy_pool(u->peer.connection->pool);
+ }
+
+ ngx_close_connection(u->peer.connection);
+ }
+
+ u->peer.connection = NULL;
+
+ if (u->pipe && u->pipe->temp_file) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http upstream temp fd: %d",
+ u->pipe->temp_file->file.fd);
+ }
+
+ if (u->store && u->pipe && u->pipe->temp_file
+ && u->pipe->temp_file->file.fd != NGX_INVALID_FILE)
+ {
+ if (ngx_delete_file(u->pipe->temp_file->file.name.data)
+ == NGX_FILE_ERROR)
+ {
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+ ngx_delete_file_n " \"%s\" failed",
+ u->pipe->temp_file->file.name.data);
+ }
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ if (r->cache) {
+
+ if (u->cacheable) {
+
+ if (rc == NGX_HTTP_BAD_GATEWAY || rc == NGX_HTTP_GATEWAY_TIME_OUT) {
+ time_t valid;
+
+ valid = ngx_http_file_cache_valid(u->conf->cache_valid, rc);
+
+ if (valid) {
+ r->cache->valid_sec = ngx_time() + valid;
+ r->cache->error = rc;
+ }
+ }
+ }
+
+ ngx_http_file_cache_free(r->cache, u->pipe->temp_file);
+ }
+
+#endif
+
+ r->read_event_handler = ngx_http_block_reading;
+
+ if (rc == NGX_DECLINED) {
+ return;
+ }
+
+ r->connection->log->action = "sending to client";
+
+ if (!u->header_sent
+ || rc == NGX_HTTP_REQUEST_TIME_OUT
+ || rc == NGX_HTTP_CLIENT_CLOSED_REQUEST)
+ {
+ ngx_http_finalize_request(r, rc);
+ return;
+ }
+
+ flush = 0;
+
+ if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+ rc = NGX_ERROR;
+ flush = 1;
+ }
+
+ if (r->header_only
+ || (u->pipe && u->pipe->downstream_error))
+ {
+ ngx_http_finalize_request(r, rc);
+ return;
+ }
+
+ if (rc == 0) {
+
+ if (ngx_http_upstream_process_trailers(r, u) != NGX_OK) {
+ ngx_http_finalize_request(r, NGX_ERROR);
+ return;
+ }
+
+ rc = ngx_http_send_special(r, NGX_HTTP_LAST);
+
+ } else if (flush) {
+ r->keepalive = 0;
+ rc = ngx_http_send_special(r, NGX_HTTP_FLUSH);
+ }
+
+ ngx_http_finalize_request(r, rc);
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ ngx_table_elt_t **ph;
+
+ ph = (ngx_table_elt_t **) ((char *) &r->upstream->headers_in + offset);
+
+ if (*ph == NULL) {
+ *ph = h;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_ignore_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_content_length(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset)
+{
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+
+ u->headers_in.content_length = h;
+ u->headers_in.content_length_n = ngx_atoof(h->value.data, h->value.len);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_last_modified(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset)
+{
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+
+ u->headers_in.last_modified = h;
+ u->headers_in.last_modified_time = ngx_parse_http_time(h->value.data,
+ h->value.len);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ ngx_array_t *pa;
+ ngx_table_elt_t **ph;
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+ pa = &u->headers_in.cookies;
+
+ if (pa->elts == NULL) {
+ if (ngx_array_init(pa, r->pool, 1, sizeof(ngx_table_elt_t *)) != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+ ph = ngx_array_push(pa);
+ if (ph == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ph = h;
+
+#if (NGX_HTTP_CACHE)
+ if (!(u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_SET_COOKIE)) {
+ u->cacheable = 0;
+ }
+#endif
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_cache_control(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset)
+{
+ ngx_array_t *pa;
+ ngx_table_elt_t **ph;
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+ pa = &u->headers_in.cache_control;
+
+ if (pa->elts == NULL) {
+ if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+ ph = ngx_array_push(pa);
+ if (ph == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ph = h;
+
+#if (NGX_HTTP_CACHE)
+ {
+ u_char *p, *start, *last;
+ ngx_int_t n;
+
+ if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL) {
+ return NGX_OK;
+ }
+
+ if (r->cache == NULL) {
+ return NGX_OK;
+ }
+
+ if (r->cache->valid_sec != 0 && u->headers_in.x_accel_expires != NULL) {
+ return NGX_OK;
+ }
+
+ start = h->value.data;
+ last = start + h->value.len;
+
+ if (ngx_strlcasestrn(start, last, (u_char *) "no-cache", 8 - 1) != NULL
+ || ngx_strlcasestrn(start, last, (u_char *) "no-store", 8 - 1) != NULL
+ || ngx_strlcasestrn(start, last, (u_char *) "private", 7 - 1) != NULL)
+ {
+ u->cacheable = 0;
+ return NGX_OK;
+ }
+
+ p = ngx_strlcasestrn(start, last, (u_char *) "s-maxage=", 9 - 1);
+ offset = 9;
+
+ if (p == NULL) {
+ p = ngx_strlcasestrn(start, last, (u_char *) "max-age=", 8 - 1);
+ offset = 8;
+ }
+
+ if (p) {
+ n = 0;
+
+ for (p += offset; p < last; p++) {
+ if (*p == ',' || *p == ';' || *p == ' ') {
+ break;
+ }
+
+ if (*p >= '0' && *p <= '9') {
+ n = n * 10 + (*p - '0');
+ continue;
+ }
+
+ u->cacheable = 0;
+ return NGX_OK;
+ }
+
+ if (n == 0) {
+ u->cacheable = 0;
+ return NGX_OK;
+ }
+
+ r->cache->valid_sec = ngx_time() + n;
+ }
+
+ p = ngx_strlcasestrn(start, last, (u_char *) "stale-while-revalidate=",
+ 23 - 1);
+
+ if (p) {
+ n = 0;
+
+ for (p += 23; p < last; p++) {
+ if (*p == ',' || *p == ';' || *p == ' ') {
+ break;
+ }
+
+ if (*p >= '0' && *p <= '9') {
+ n = n * 10 + (*p - '0');
+ continue;
+ }
+
+ u->cacheable = 0;
+ return NGX_OK;
+ }
+
+ r->cache->updating_sec = n;
+ r->cache->error_sec = n;
+ }
+
+ p = ngx_strlcasestrn(start, last, (u_char *) "stale-if-error=", 15 - 1);
+
+ if (p) {
+ n = 0;
+
+ for (p += 15; p < last; p++) {
+ if (*p == ',' || *p == ';' || *p == ' ') {
+ break;
+ }
+
+ if (*p >= '0' && *p <= '9') {
+ n = n * 10 + (*p - '0');
+ continue;
+ }
+
+ u->cacheable = 0;
+ return NGX_OK;
+ }
+
+ r->cache->error_sec = n;
+ }
+ }
+#endif
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_expires(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+ u->headers_in.expires = h;
+
+#if (NGX_HTTP_CACHE)
+ {
+ time_t expires;
+
+ if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_EXPIRES) {
+ return NGX_OK;
+ }
+
+ if (r->cache == NULL) {
+ return NGX_OK;
+ }
+
+ if (r->cache->valid_sec != 0) {
+ return NGX_OK;
+ }
+
+ expires = ngx_parse_http_time(h->value.data, h->value.len);
+
+ if (expires == NGX_ERROR || expires < ngx_time()) {
+ u->cacheable = 0;
+ return NGX_OK;
+ }
+
+ r->cache->valid_sec = expires;
+ }
+#endif
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_accel_expires(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset)
+{
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+ u->headers_in.x_accel_expires = h;
+
+#if (NGX_HTTP_CACHE)
+ {
+ u_char *p;
+ size_t len;
+ ngx_int_t n;
+
+ if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES) {
+ return NGX_OK;
+ }
+
+ if (r->cache == NULL) {
+ return NGX_OK;
+ }
+
+ len = h->value.len;
+ p = h->value.data;
+
+ if (p[0] != '@') {
+ n = ngx_atoi(p, len);
+
+ switch (n) {
+ case 0:
+ u->cacheable = 0;
+ /* fall through */
+
+ case NGX_ERROR:
+ return NGX_OK;
+
+ default:
+ r->cache->valid_sec = ngx_time() + n;
+ return NGX_OK;
+ }
+ }
+
+ p++;
+ len--;
+
+ n = ngx_atoi(p, len);
+
+ if (n != NGX_ERROR) {
+ r->cache->valid_sec = n;
+ }
+ }
+#endif
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_limit_rate(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ ngx_int_t n;
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+ u->headers_in.x_accel_limit_rate = h;
+
+ if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE) {
+ return NGX_OK;
+ }
+
+ n = ngx_atoi(h->value.data, h->value.len);
+
+ if (n != NGX_ERROR) {
+ r->limit_rate = (size_t) n;
+ r->limit_rate_set = 1;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_buffering(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ u_char c0, c1, c2;
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+
+ if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING) {
+ return NGX_OK;
+ }
+
+ if (u->conf->change_buffering) {
+
+ if (h->value.len == 2) {
+ c0 = ngx_tolower(h->value.data[0]);
+ c1 = ngx_tolower(h->value.data[1]);
+
+ if (c0 == 'n' && c1 == 'o') {
+ u->buffering = 0;
+ }
+
+ } else if (h->value.len == 3) {
+ c0 = ngx_tolower(h->value.data[0]);
+ c1 = ngx_tolower(h->value.data[1]);
+ c2 = ngx_tolower(h->value.data[2]);
+
+ if (c0 == 'y' && c1 == 'e' && c2 == 's') {
+ u->buffering = 1;
+ }
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_charset(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ if (r->upstream->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_CHARSET) {
+ return NGX_OK;
+ }
+
+ r->headers_out.override_charset = &h->value;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ r->upstream->headers_in.connection = h;
+
+ if (ngx_strlcasestrn(h->value.data, h->value.data + h->value.len,
+ (u_char *) "close", 5 - 1)
+ != NULL)
+ {
+ r->upstream->headers_in.connection_close = 1;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_transfer_encoding(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset)
+{
+ r->upstream->headers_in.transfer_encoding = h;
+
+ if (ngx_strlcasestrn(h->value.data, h->value.data + h->value.len,
+ (u_char *) "chunked", 7 - 1)
+ != NULL)
+ {
+ r->upstream->headers_in.chunked = 1;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_vary(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset)
+{
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+ u->headers_in.vary = h;
+
+#if (NGX_HTTP_CACHE)
+
+ if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_VARY) {
+ return NGX_OK;
+ }
+
+ if (r->cache == NULL) {
+ return NGX_OK;
+ }
+
+ if (h->value.len > NGX_HTTP_CACHE_VARY_LEN
+ || (h->value.len == 1 && h->value.data[0] == '*'))
+ {
+ u->cacheable = 0;
+ }
+
+ r->cache->vary = h->value;
+
+#endif
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_copy_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ ngx_table_elt_t *ho, **ph;
+
+ ho = ngx_list_push(&r->headers_out.headers);
+ if (ho == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ho = *h;
+
+ if (offset) {
+ ph = (ngx_table_elt_t **) ((char *) &r->headers_out + offset);
+ *ph = ho;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_copy_multi_header_lines(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset)
+{
+ ngx_array_t *pa;
+ ngx_table_elt_t *ho, **ph;
+
+ pa = (ngx_array_t *) ((char *) &r->headers_out + offset);
+
+ if (pa->elts == NULL) {
+ if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+ ho = ngx_list_push(&r->headers_out.headers);
+ if (ho == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ho = *h;
+
+ ph = ngx_array_push(pa);
+ if (ph == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ph = ho;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_copy_content_type(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ u_char *p, *last;
+
+ r->headers_out.content_type_len = h->value.len;
+ r->headers_out.content_type = h->value;
+ r->headers_out.content_type_lowcase = NULL;
+
+ for (p = h->value.data; *p; p++) {
+
+ if (*p != ';') {
+ continue;
+ }
+
+ last = p;
+
+ while (*++p == ' ') { /* void */ }
+
+ if (*p == '\0') {
+ return NGX_OK;
+ }
+
+ if (ngx_strncasecmp(p, (u_char *) "charset=", 8) != 0) {
+ continue;
+ }
+
+ p += 8;
+
+ r->headers_out.content_type_len = last - h->value.data;
+
+ if (*p == '"') {
+ p++;
+ }
+
+ last = h->value.data + h->value.len;
+
+ if (*(last - 1) == '"') {
+ last--;
+ }
+
+ r->headers_out.charset.len = last - p;
+ r->headers_out.charset.data = p;
+
+ return NGX_OK;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_copy_last_modified(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ ngx_table_elt_t *ho;
+
+ ho = ngx_list_push(&r->headers_out.headers);
+ if (ho == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ho = *h;
+
+ r->headers_out.last_modified = ho;
+ r->headers_out.last_modified_time =
+ r->upstream->headers_in.last_modified_time;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_rewrite_location(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ ngx_int_t rc;
+ ngx_table_elt_t *ho;
+
+ ho = ngx_list_push(&r->headers_out.headers);
+ if (ho == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ho = *h;
+
+ if (r->upstream->rewrite_redirect) {
+ rc = r->upstream->rewrite_redirect(r, ho, 0);
+
+ if (rc == NGX_DECLINED) {
+ return NGX_OK;
+ }
+
+ if (rc == NGX_OK) {
+ r->headers_out.location = ho;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "rewritten location: \"%V\"", &ho->value);
+ }
+
+ return rc;
+ }
+
+ if (ho->value.data[0] != '/') {
+ r->headers_out.location = ho;
+ }
+
+ /*
+ * we do not set r->headers_out.location here to avoid handling
+ * relative redirects in ngx_http_header_filter()
+ */
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_rewrite_refresh(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ u_char *p;
+ ngx_int_t rc;
+ ngx_table_elt_t *ho;
+
+ ho = ngx_list_push(&r->headers_out.headers);
+ if (ho == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ho = *h;
+
+ if (r->upstream->rewrite_redirect) {
+
+ p = ngx_strcasestrn(ho->value.data, "url=", 4 - 1);
+
+ if (p) {
+ rc = r->upstream->rewrite_redirect(r, ho, p + 4 - ho->value.data);
+
+ } else {
+ return NGX_OK;
+ }
+
+ if (rc == NGX_DECLINED) {
+ return NGX_OK;
+ }
+
+ if (rc == NGX_OK) {
+ r->headers_out.refresh = ho;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "rewritten refresh: \"%V\"", &ho->value);
+ }
+
+ return rc;
+ }
+
+ r->headers_out.refresh = ho;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_rewrite_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ ngx_int_t rc;
+ ngx_table_elt_t *ho;
+
+ ho = ngx_list_push(&r->headers_out.headers);
+ if (ho == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ho = *h;
+
+ if (r->upstream->rewrite_cookie) {
+ rc = r->upstream->rewrite_cookie(r, ho);
+
+ if (rc == NGX_DECLINED) {
+ return NGX_OK;
+ }
+
+#if (NGX_DEBUG)
+ if (rc == NGX_OK) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "rewritten cookie: \"%V\"", &ho->value);
+ }
+#endif
+
+ return rc;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset)
+{
+ ngx_table_elt_t *ho;
+
+ if (r->upstream->conf->force_ranges) {
+ return NGX_OK;
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ if (r->cached) {
+ r->allow_ranges = 1;
+ return NGX_OK;
+ }
+
+ if (r->upstream->cacheable) {
+ r->allow_ranges = 1;
+ r->single_range = 1;
+ return NGX_OK;
+ }
+
+#endif
+
+ ho = ngx_list_push(&r->headers_out.headers);
+ if (ho == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ho = *h;
+
+ r->headers_out.accept_ranges = ho;
+
+ return NGX_OK;
+}
+
+
+#if (NGX_HTTP_GZIP)
+
+static ngx_int_t
+ngx_http_upstream_copy_content_encoding(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset)
+{
+ ngx_table_elt_t *ho;
+
+ ho = ngx_list_push(&r->headers_out.headers);
+ if (ho == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ho = *h;
+
+ r->headers_out.content_encoding = ho;
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_upstream_add_variables(ngx_conf_t *cf)
+{
+ ngx_http_variable_t *var, *v;
+
+ for (v = ngx_http_upstream_vars; v->name.len; v++) {
+ var = ngx_http_add_variable(cf, &v->name, v->flags);
+ if (var == NULL) {
+ return NGX_ERROR;
+ }
+
+ var->get_handler = v->get_handler;
+ var->data = v->data;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_addr_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+ size_t len;
+ ngx_uint_t i;
+ ngx_http_upstream_state_t *state;
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ len = 0;
+ state = r->upstream_states->elts;
+
+ for (i = 0; i < r->upstream_states->nelts; i++) {
+ if (state[i].peer) {
+ len += state[i].peer->len + 2;
+
+ } else {
+ len += 3;
+ }
+ }
+
+ p = ngx_pnalloc(r->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->data = p;
+
+ i = 0;
+
+ for ( ;; ) {
+ if (state[i].peer) {
+ p = ngx_cpymem(p, state[i].peer->data, state[i].peer->len);
+ }
+
+ if (++i == r->upstream_states->nelts) {
+ break;
+ }
+
+ if (state[i].peer) {
+ *p++ = ',';
+ *p++ = ' ';
+
+ } else {
+ *p++ = ' ';
+ *p++ = ':';
+ *p++ = ' ';
+
+ if (++i == r->upstream_states->nelts) {
+ break;
+ }
+
+ continue;
+ }
+ }
+
+ v->len = p - v->data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_status_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+ size_t len;
+ ngx_uint_t i;
+ ngx_http_upstream_state_t *state;
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ len = r->upstream_states->nelts * (3 + 2);
+
+ p = ngx_pnalloc(r->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->data = p;
+
+ i = 0;
+ state = r->upstream_states->elts;
+
+ for ( ;; ) {
+ if (state[i].status) {
+ p = ngx_sprintf(p, "%ui", state[i].status);
+
+ } else {
+ *p++ = '-';
+ }
+
+ if (++i == r->upstream_states->nelts) {
+ break;
+ }
+
+ if (state[i].peer) {
+ *p++ = ',';
+ *p++ = ' ';
+
+ } else {
+ *p++ = ' ';
+ *p++ = ':';
+ *p++ = ' ';
+
+ if (++i == r->upstream_states->nelts) {
+ break;
+ }
+
+ continue;
+ }
+ }
+
+ v->len = p - v->data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_response_time_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+ size_t len;
+ ngx_uint_t i;
+ ngx_msec_int_t ms;
+ ngx_http_upstream_state_t *state;
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ len = r->upstream_states->nelts * (NGX_TIME_T_LEN + 4 + 2);
+
+ p = ngx_pnalloc(r->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->data = p;
+
+ i = 0;
+ state = r->upstream_states->elts;
+
+ for ( ;; ) {
+
+ if (data == 1) {
+ ms = state[i].header_time;
+
+ } else if (data == 2) {
+ ms = state[i].connect_time;
+
+ } else {
+ ms = state[i].response_time;
+ }
+
+ if (ms != -1) {
+ ms = ngx_max(ms, 0);
+ p = ngx_sprintf(p, "%T.%03M", (time_t) ms / 1000, ms % 1000);
+
+ } else {
+ *p++ = '-';
+ }
+
+ if (++i == r->upstream_states->nelts) {
+ break;
+ }
+
+ if (state[i].peer) {
+ *p++ = ',';
+ *p++ = ' ';
+
+ } else {
+ *p++ = ' ';
+ *p++ = ':';
+ *p++ = ' ';
+
+ if (++i == r->upstream_states->nelts) {
+ break;
+ }
+
+ continue;
+ }
+ }
+
+ v->len = p - v->data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_response_length_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+ size_t len;
+ ngx_uint_t i;
+ ngx_http_upstream_state_t *state;
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ len = r->upstream_states->nelts * (NGX_OFF_T_LEN + 2);
+
+ p = ngx_pnalloc(r->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->data = p;
+
+ i = 0;
+ state = r->upstream_states->elts;
+
+ for ( ;; ) {
+
+ if (data == 1) {
+ p = ngx_sprintf(p, "%O", state[i].bytes_received);
+
+ } else if (data == 2) {
+ p = ngx_sprintf(p, "%O", state[i].bytes_sent);
+
+ } else {
+ p = ngx_sprintf(p, "%O", state[i].response_length);
+ }
+
+ if (++i == r->upstream_states->nelts) {
+ break;
+ }
+
+ if (state[i].peer) {
+ *p++ = ',';
+ *p++ = ' ';
+
+ } else {
+ *p++ = ' ';
+ *p++ = ':';
+ *p++ = ' ';
+
+ if (++i == r->upstream_states->nelts) {
+ break;
+ }
+
+ continue;
+ }
+ }
+
+ v->len = p - v->data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_header_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ if (r->upstream == NULL) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,
+ &r->upstream->headers_in.headers.part,
+ sizeof("upstream_http_") - 1);
+}
+
+
+static ngx_int_t
+ngx_http_upstream_trailer_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ if (r->upstream == NULL) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,
+ &r->upstream->headers_in.trailers.part,
+ sizeof("upstream_trailer_") - 1);
+}
+
+
+static ngx_int_t
+ngx_http_upstream_cookie_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_str_t *name = (ngx_str_t *) data;
+
+ ngx_str_t cookie, s;
+
+ if (r->upstream == NULL) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ s.len = name->len - (sizeof("upstream_cookie_") - 1);
+ s.data = name->data + sizeof("upstream_cookie_") - 1;
+
+ if (ngx_http_parse_set_cookie_lines(&r->upstream->headers_in.cookies,
+ &s, &cookie)
+ == NGX_DECLINED)
+ {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ v->len = cookie.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = cookie.data;
+
+ return NGX_OK;
+}
+
+
+#if (NGX_HTTP_CACHE)
+
+static ngx_int_t
+ngx_http_upstream_cache_status(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_uint_t n;
+
+ if (r->upstream == NULL || r->upstream->cache_status == 0) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ n = r->upstream->cache_status - 1;
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->len = ngx_http_cache_status[n].len;
+ v->data = ngx_http_cache_status[n].data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_cache_last_modified(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ if (r->upstream == NULL
+ || !r->upstream->conf->cache_revalidate
+ || r->upstream->cache_status != NGX_HTTP_CACHE_EXPIRED
+ || r->cache->last_modified == -1)
+ {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ p = ngx_pnalloc(r->pool, sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_http_time(p, r->cache->last_modified) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_cache_etag(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ if (r->upstream == NULL
+ || !r->upstream->conf->cache_revalidate
+ || r->upstream->cache_status != NGX_HTTP_CACHE_EXPIRED
+ || r->cache->etag.len == 0)
+ {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->len = r->cache->etag.len;
+ v->data = r->cache->etag.data;
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static char *
+ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
+{
+ char *rv;
+ void *mconf;
+ ngx_str_t *value;
+ ngx_url_t u;
+ ngx_uint_t m;
+ ngx_conf_t pcf;
+ ngx_http_module_t *module;
+ ngx_http_conf_ctx_t *ctx, *http_ctx;
+ ngx_http_upstream_srv_conf_t *uscf;
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ value = cf->args->elts;
+ u.host = value[1];
+ u.no_resolve = 1;
+ u.no_port = 1;
+
+ uscf = ngx_http_upstream_add(cf, &u, NGX_HTTP_UPSTREAM_CREATE
+ |NGX_HTTP_UPSTREAM_WEIGHT
+ |NGX_HTTP_UPSTREAM_MAX_CONNS
+ |NGX_HTTP_UPSTREAM_MAX_FAILS
+ |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
+ |NGX_HTTP_UPSTREAM_DOWN
+ |NGX_HTTP_UPSTREAM_BACKUP);
+ if (uscf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ http_ctx = cf->ctx;
+ ctx->main_conf = http_ctx->main_conf;
+
+ /* the upstream{}'s srv_conf */
+
+ ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
+ if (ctx->srv_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->srv_conf[ngx_http_upstream_module.ctx_index] = uscf;
+
+ uscf->srv_conf = ctx->srv_conf;
+
+
+ /* the upstream{}'s loc_conf */
+
+ ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
+ if (ctx->loc_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ for (m = 0; cf->cycle->modules[m]; m++) {
+ if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {
+ continue;
+ }
+
+ module = cf->cycle->modules[m]->ctx;
+
+ if (module->create_srv_conf) {
+ mconf = module->create_srv_conf(cf);
+ if (mconf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->srv_conf[cf->cycle->modules[m]->ctx_index] = mconf;
+ }
+
+ if (module->create_loc_conf) {
+ mconf = module->create_loc_conf(cf);
+ if (mconf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->loc_conf[cf->cycle->modules[m]->ctx_index] = mconf;
+ }
+ }
+
+ uscf->servers = ngx_array_create(cf->pool, 4,
+ sizeof(ngx_http_upstream_server_t));
+ if (uscf->servers == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+
+ /* parse inside upstream{} */
+
+ pcf = *cf;
+ cf->ctx = ctx;
+ cf->cmd_type = NGX_HTTP_UPS_CONF;
+
+ rv = ngx_conf_parse(cf, NULL);
+
+ *cf = pcf;
+
+ if (rv != NGX_CONF_OK) {
+ return rv;
+ }
+
+ if (uscf->servers->nelts == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "no servers are inside upstream");
+ return NGX_CONF_ERROR;
+ }
+
+ return rv;
+}
+
+
+static char *
+ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_upstream_srv_conf_t *uscf = conf;
+
+ time_t fail_timeout;
+ ngx_str_t *value, s;
+ ngx_url_t u;
+ ngx_int_t weight, max_conns, max_fails;
+ ngx_uint_t i;
+ ngx_http_upstream_server_t *us;
+
+ us = ngx_array_push(uscf->servers);
+ if (us == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(us, sizeof(ngx_http_upstream_server_t));
+
+ value = cf->args->elts;
+
+ weight = 1;
+ max_conns = 0;
+ max_fails = 1;
+ fail_timeout = 10;
+
+ for (i = 2; i < cf->args->nelts; i++) {
+
+ if (ngx_strncmp(value[i].data, "weight=", 7) == 0) {
+
+ if (!(uscf->flags & NGX_HTTP_UPSTREAM_WEIGHT)) {
+ goto not_supported;
+ }
+
+ weight = ngx_atoi(&value[i].data[7], value[i].len - 7);
+
+ if (weight == NGX_ERROR || weight == 0) {
+ goto invalid;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "max_conns=", 10) == 0) {
+
+ if (!(uscf->flags & NGX_HTTP_UPSTREAM_MAX_CONNS)) {
+ goto not_supported;
+ }
+
+ max_conns = ngx_atoi(&value[i].data[10], value[i].len - 10);
+
+ if (max_conns == NGX_ERROR) {
+ goto invalid;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "max_fails=", 10) == 0) {
+
+ if (!(uscf->flags & NGX_HTTP_UPSTREAM_MAX_FAILS)) {
+ goto not_supported;
+ }
+
+ max_fails = ngx_atoi(&value[i].data[10], value[i].len - 10);
+
+ if (max_fails == NGX_ERROR) {
+ goto invalid;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "fail_timeout=", 13) == 0) {
+
+ if (!(uscf->flags & NGX_HTTP_UPSTREAM_FAIL_TIMEOUT)) {
+ goto not_supported;
+ }
+
+ s.len = value[i].len - 13;
+ s.data = &value[i].data[13];
+
+ fail_timeout = ngx_parse_time(&s, 1);
+
+ if (fail_timeout == (time_t) NGX_ERROR) {
+ goto invalid;
+ }
+
+ continue;
+ }
+
+ if (ngx_strcmp(value[i].data, "backup") == 0) {
+
+ if (!(uscf->flags & NGX_HTTP_UPSTREAM_BACKUP)) {
+ goto not_supported;
+ }
+
+ us->backup = 1;
+
+ continue;
+ }
+
+ if (ngx_strcmp(value[i].data, "down") == 0) {
+
+ if (!(uscf->flags & NGX_HTTP_UPSTREAM_DOWN)) {
+ goto not_supported;
+ }
+
+ us->down = 1;
+
+ continue;
+ }
+
+ goto invalid;
+ }
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.url = value[1];
+ u.default_port = 80;
+
+ if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
+ if (u.err) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "%s in upstream \"%V\"", u.err, &u.url);
+ }
+
+ return NGX_CONF_ERROR;
+ }
+
+ us->name = u.url;
+ us->addrs = u.addrs;
+ us->naddrs = u.naddrs;
+ us->weight = weight;
+ us->max_conns = max_conns;
+ us->max_fails = max_fails;
+ us->fail_timeout = fail_timeout;
+
+ return NGX_CONF_OK;
+
+invalid:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[i]);
+
+ return NGX_CONF_ERROR;
+
+not_supported:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "balancing method does not support parameter \"%V\"",
+ &value[i]);
+
+ return NGX_CONF_ERROR;
+}
+
+
+ngx_http_upstream_srv_conf_t *
+ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags)
+{
+ ngx_uint_t i;
+ ngx_http_upstream_server_t *us;
+ ngx_http_upstream_srv_conf_t *uscf, **uscfp;
+ ngx_http_upstream_main_conf_t *umcf;
+
+ if (!(flags & NGX_HTTP_UPSTREAM_CREATE)) {
+
+ if (ngx_parse_url(cf->pool, u) != NGX_OK) {
+ if (u->err) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "%s in upstream \"%V\"", u->err, &u->url);
+ }
+
+ return NULL;
+ }
+ }
+
+ umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module);
+
+ uscfp = umcf->upstreams.elts;
+
+ for (i = 0; i < umcf->upstreams.nelts; i++) {
+
+ if (uscfp[i]->host.len != u->host.len
+ || ngx_strncasecmp(uscfp[i]->host.data, u->host.data, u->host.len)
+ != 0)
+ {
+ continue;
+ }
+
+ if ((flags & NGX_HTTP_UPSTREAM_CREATE)
+ && (uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE))
+ {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "duplicate upstream \"%V\"", &u->host);
+ return NULL;
+ }
+
+ if ((uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE) && !u->no_port) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "upstream \"%V\" may not have port %d",
+ &u->host, u->port);
+ return NULL;
+ }
+
+ if ((flags & NGX_HTTP_UPSTREAM_CREATE) && !uscfp[i]->no_port) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "upstream \"%V\" may not have port %d in %s:%ui",
+ &u->host, uscfp[i]->port,
+ uscfp[i]->file_name, uscfp[i]->line);
+ return NULL;
+ }
+
+ if (uscfp[i]->port && u->port
+ && uscfp[i]->port != u->port)
+ {
+ continue;
+ }
+
+ if (flags & NGX_HTTP_UPSTREAM_CREATE) {
+ uscfp[i]->flags = flags;
+ uscfp[i]->port = 0;
+ }
+
+ return uscfp[i];
+ }
+
+ uscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_srv_conf_t));
+ if (uscf == NULL) {
+ return NULL;
+ }
+
+ uscf->flags = flags;
+ uscf->host = u->host;
+ uscf->file_name = cf->conf_file->file.name.data;
+ uscf->line = cf->conf_file->line;
+ uscf->port = u->port;
+ uscf->no_port = u->no_port;
+
+ if (u->naddrs == 1 && (u->port || u->family == AF_UNIX)) {
+ uscf->servers = ngx_array_create(cf->pool, 1,
+ sizeof(ngx_http_upstream_server_t));
+ if (uscf->servers == NULL) {
+ return NULL;
+ }
+
+ us = ngx_array_push(uscf->servers);
+ if (us == NULL) {
+ return NULL;
+ }
+
+ ngx_memzero(us, sizeof(ngx_http_upstream_server_t));
+
+ us->addrs = u->addrs;
+ us->naddrs = 1;
+ }
+
+ uscfp = ngx_array_push(&umcf->upstreams);
+ if (uscfp == NULL) {
+ return NULL;
+ }
+
+ *uscfp = uscf;
+
+ return uscf;
+}
+
+
+char *
+ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ char *p = conf;
+
+ ngx_int_t rc;
+ ngx_str_t *value;
+ ngx_http_complex_value_t cv;
+ ngx_http_upstream_local_t **plocal, *local;
+ ngx_http_compile_complex_value_t ccv;
+
+ plocal = (ngx_http_upstream_local_t **) (p + cmd->offset);
+
+ if (*plocal != NGX_CONF_UNSET_PTR) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ if (cf->args->nelts == 2 && ngx_strcmp(value[1].data, "off") == 0) {
+ *plocal = NULL;
+ return NGX_CONF_OK;
+ }
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = &cv;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ local = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_local_t));
+ if (local == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *plocal = local;
+
+ if (cv.lengths) {
+ local->value = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
+ if (local->value == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *local->value = cv;
+
+ } else {
+ local->addr = ngx_palloc(cf->pool, sizeof(ngx_addr_t));
+ if (local->addr == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ rc = ngx_parse_addr_port(cf->pool, local->addr, value[1].data,
+ value[1].len);
+
+ switch (rc) {
+ case NGX_OK:
+ local->addr->name = value[1];
+ break;
+
+ case NGX_DECLINED:
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid address \"%V\"", &value[1]);
+ /* fall through */
+
+ default:
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ if (cf->args->nelts > 2) {
+ if (ngx_strcmp(value[2].data, "transparent") == 0) {
+#if (NGX_HAVE_TRANSPARENT_PROXY)
+ ngx_core_conf_t *ccf;
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(cf->cycle->conf_ctx,
+ ngx_core_module);
+
+ ccf->transparent = 1;
+ local->transparent = 1;
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "transparent proxying is not supported "
+ "on this platform, ignored");
+#endif
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_set_local(ngx_http_request_t *r, ngx_http_upstream_t *u,
+ ngx_http_upstream_local_t *local)
+{
+ ngx_int_t rc;
+ ngx_str_t val;
+ ngx_addr_t *addr;
+
+ if (local == NULL) {
+ u->peer.local = NULL;
+ return NGX_OK;
+ }
+
+#if (NGX_HAVE_TRANSPARENT_PROXY)
+ u->peer.transparent = local->transparent;
+#endif
+
+ if (local->value == NULL) {
+ u->peer.local = local->addr;
+ return NGX_OK;
+ }
+
+ if (ngx_http_complex_value(r, local->value, &val) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (val.len == 0) {
+ return NGX_OK;
+ }
+
+ addr = ngx_palloc(r->pool, sizeof(ngx_addr_t));
+ if (addr == NULL) {
+ return NGX_ERROR;
+ }
+
+ rc = ngx_parse_addr_port(r->pool, addr, val.data, val.len);
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (rc != NGX_OK) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "invalid local address \"%V\"", &val);
+ return NGX_OK;
+ }
+
+ addr->name = val;
+ u->peer.local = addr;
+
+ return NGX_OK;
+}
+
+
+char *
+ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ char *p = conf;
+
+ ngx_str_t *value;
+ ngx_array_t **a;
+ ngx_http_upstream_param_t *param;
+
+ a = (ngx_array_t **) (p + cmd->offset);
+
+ if (*a == NULL) {
+ *a = ngx_array_create(cf->pool, 4, sizeof(ngx_http_upstream_param_t));
+ if (*a == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ param = ngx_array_push(*a);
+ if (param == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ value = cf->args->elts;
+
+ param->key = value[1];
+ param->value = value[2];
+ param->skip_empty = 0;
+
+ if (cf->args->nelts == 4) {
+ if (ngx_strcmp(value[3].data, "if_not_empty") != 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[3]);
+ return NGX_CONF_ERROR;
+ }
+
+ param->skip_empty = 1;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+ngx_int_t
+ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf,
+ ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev,
+ ngx_str_t *default_hide_headers, ngx_hash_init_t *hash)
+{
+ ngx_str_t *h;
+ ngx_uint_t i, j;
+ ngx_array_t hide_headers;
+ ngx_hash_key_t *hk;
+
+ if (conf->hide_headers == NGX_CONF_UNSET_PTR
+ && conf->pass_headers == NGX_CONF_UNSET_PTR)
+ {
+ conf->hide_headers = prev->hide_headers;
+ conf->pass_headers = prev->pass_headers;
+
+ conf->hide_headers_hash = prev->hide_headers_hash;
+
+ if (conf->hide_headers_hash.buckets) {
+ return NGX_OK;
+ }
+
+ } else {
+ if (conf->hide_headers == NGX_CONF_UNSET_PTR) {
+ conf->hide_headers = prev->hide_headers;
+ }
+
+ if (conf->pass_headers == NGX_CONF_UNSET_PTR) {
+ conf->pass_headers = prev->pass_headers;
+ }
+ }
+
+ if (ngx_array_init(&hide_headers, cf->temp_pool, 4, sizeof(ngx_hash_key_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ for (h = default_hide_headers; h->len; h++) {
+ hk = ngx_array_push(&hide_headers);
+ if (hk == NULL) {
+ return NGX_ERROR;
+ }
+
+ hk->key = *h;
+ hk->key_hash = ngx_hash_key_lc(h->data, h->len);
+ hk->value = (void *) 1;
+ }
+
+ if (conf->hide_headers != NGX_CONF_UNSET_PTR) {
+
+ h = conf->hide_headers->elts;
+
+ for (i = 0; i < conf->hide_headers->nelts; i++) {
+
+ hk = hide_headers.elts;
+
+ for (j = 0; j < hide_headers.nelts; j++) {
+ if (ngx_strcasecmp(h[i].data, hk[j].key.data) == 0) {
+ goto exist;
+ }
+ }
+
+ hk = ngx_array_push(&hide_headers);
+ if (hk == NULL) {
+ return NGX_ERROR;
+ }
+
+ hk->key = h[i];
+ hk->key_hash = ngx_hash_key_lc(h[i].data, h[i].len);
+ hk->value = (void *) 1;
+
+ exist:
+
+ continue;
+ }
+ }
+
+ if (conf->pass_headers != NGX_CONF_UNSET_PTR) {
+
+ h = conf->pass_headers->elts;
+ hk = hide_headers.elts;
+
+ for (i = 0; i < conf->pass_headers->nelts; i++) {
+ for (j = 0; j < hide_headers.nelts; j++) {
+
+ if (hk[j].key.data == NULL) {
+ continue;
+ }
+
+ if (ngx_strcasecmp(h[i].data, hk[j].key.data) == 0) {
+ hk[j].key.data = NULL;
+ break;
+ }
+ }
+ }
+ }
+
+ hash->hash = &conf->hide_headers_hash;
+ hash->key = ngx_hash_key_lc;
+ hash->pool = cf->pool;
+ hash->temp_pool = NULL;
+
+ if (ngx_hash_init(hash, hide_headers.elts, hide_headers.nelts) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ /*
+ * special handling to preserve conf->hide_headers_hash
+ * in the "http" section to inherit it to all servers
+ */
+
+ if (prev->hide_headers_hash.buckets == NULL
+ && conf->hide_headers == prev->hide_headers
+ && conf->pass_headers == prev->pass_headers)
+ {
+ prev->hide_headers_hash = conf->hide_headers_hash;
+ }
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_upstream_create_main_conf(ngx_conf_t *cf)
+{
+ ngx_http_upstream_main_conf_t *umcf;
+
+ umcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_main_conf_t));
+ if (umcf == NULL) {
+ return NULL;
+ }
+
+ if (ngx_array_init(&umcf->upstreams, cf->pool, 4,
+ sizeof(ngx_http_upstream_srv_conf_t *))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ return umcf;
+}
+
+
+static char *
+ngx_http_upstream_init_main_conf(ngx_conf_t *cf, void *conf)
+{
+ ngx_http_upstream_main_conf_t *umcf = conf;
+
+ ngx_uint_t i;
+ ngx_array_t headers_in;
+ ngx_hash_key_t *hk;
+ ngx_hash_init_t hash;
+ ngx_http_upstream_init_pt init;
+ ngx_http_upstream_header_t *header;
+ ngx_http_upstream_srv_conf_t **uscfp;
+
+ uscfp = umcf->upstreams.elts;
+
+ for (i = 0; i < umcf->upstreams.nelts; i++) {
+
+ init = uscfp[i]->peer.init_upstream ? uscfp[i]->peer.init_upstream:
+ ngx_http_upstream_init_round_robin;
+
+ if (init(cf, uscfp[i]) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+
+ /* upstream_headers_in_hash */
+
+ if (ngx_array_init(&headers_in, cf->temp_pool, 32, sizeof(ngx_hash_key_t))
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ for (header = ngx_http_upstream_headers_in; header->name.len; header++) {
+ hk = ngx_array_push(&headers_in);
+ if (hk == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ hk->key = header->name;
+ hk->key_hash = ngx_hash_key_lc(header->name.data, header->name.len);
+ hk->value = header;
+ }
+
+ hash.hash = &umcf->headers_in_hash;
+ hash.key = ngx_hash_key_lc;
+ hash.max_size = 512;
+ hash.bucket_size = ngx_align(64, ngx_cacheline_size);
+ hash.name = "upstream_headers_in_hash";
+ hash.pool = cf->pool;
+ hash.temp_pool = NULL;
+
+ if (ngx_hash_init(&hash, headers_in.elts, headers_in.nelts) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/src/nginx-1.18.0/src/http/ngx_http_upstream.h b/src/nginx-1.18.0/src/http/ngx_http_upstream.h
new file mode 100644
index 0000000..6079d72
--- /dev/null
+++ b/src/nginx-1.18.0/src/http/ngx_http_upstream.h
@@ -0,0 +1,437 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_HTTP_UPSTREAM_H_INCLUDED_
+#define _NGX_HTTP_UPSTREAM_H_INCLUDED_
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+#define NGX_HTTP_UPSTREAM_FT_ERROR 0x00000002
+#define NGX_HTTP_UPSTREAM_FT_TIMEOUT 0x00000004
+#define NGX_HTTP_UPSTREAM_FT_INVALID_HEADER 0x00000008
+#define NGX_HTTP_UPSTREAM_FT_HTTP_500 0x00000010
+#define NGX_HTTP_UPSTREAM_FT_HTTP_502 0x00000020
+#define NGX_HTTP_UPSTREAM_FT_HTTP_503 0x00000040
+#define NGX_HTTP_UPSTREAM_FT_HTTP_504 0x00000080
+#define NGX_HTTP_UPSTREAM_FT_HTTP_403 0x00000100
+#define NGX_HTTP_UPSTREAM_FT_HTTP_404 0x00000200
+#define NGX_HTTP_UPSTREAM_FT_HTTP_429 0x00000400
+#define NGX_HTTP_UPSTREAM_FT_UPDATING 0x00000800
+#define NGX_HTTP_UPSTREAM_FT_BUSY_LOCK 0x00001000
+#define NGX_HTTP_UPSTREAM_FT_MAX_WAITING 0x00002000
+#define NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT 0x00004000
+#define NGX_HTTP_UPSTREAM_FT_NOLIVE 0x40000000
+#define NGX_HTTP_UPSTREAM_FT_OFF 0x80000000
+
+#define NGX_HTTP_UPSTREAM_FT_STATUS (NGX_HTTP_UPSTREAM_FT_HTTP_500 \
+ |NGX_HTTP_UPSTREAM_FT_HTTP_502 \
+ |NGX_HTTP_UPSTREAM_FT_HTTP_503 \
+ |NGX_HTTP_UPSTREAM_FT_HTTP_504 \
+ |NGX_HTTP_UPSTREAM_FT_HTTP_403 \
+ |NGX_HTTP_UPSTREAM_FT_HTTP_404 \
+ |NGX_HTTP_UPSTREAM_FT_HTTP_429)
+
+#define NGX_HTTP_UPSTREAM_INVALID_HEADER 40
+
+
+#define NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT 0x00000002
+#define NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES 0x00000004
+#define NGX_HTTP_UPSTREAM_IGN_EXPIRES 0x00000008
+#define NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL 0x00000010
+#define NGX_HTTP_UPSTREAM_IGN_SET_COOKIE 0x00000020
+#define NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE 0x00000040
+#define NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING 0x00000080
+#define NGX_HTTP_UPSTREAM_IGN_XA_CHARSET 0x00000100
+#define NGX_HTTP_UPSTREAM_IGN_VARY 0x00000200
+
+
+typedef struct {
+ ngx_uint_t status;
+ ngx_msec_t response_time;
+ ngx_msec_t connect_time;
+ ngx_msec_t header_time;
+ ngx_msec_t queue_time;
+ off_t response_length;
+ off_t bytes_received;
+ off_t bytes_sent;
+
+ ngx_str_t *peer;
+} ngx_http_upstream_state_t;
+
+
+typedef struct {
+ ngx_hash_t headers_in_hash;
+ ngx_array_t upstreams;
+ /* ngx_http_upstream_srv_conf_t */
+} ngx_http_upstream_main_conf_t;
+
+typedef struct ngx_http_upstream_srv_conf_s ngx_http_upstream_srv_conf_t;
+
+typedef ngx_int_t (*ngx_http_upstream_init_pt)(ngx_conf_t *cf,
+ ngx_http_upstream_srv_conf_t *us);
+typedef ngx_int_t (*ngx_http_upstream_init_peer_pt)(ngx_http_request_t *r,
+ ngx_http_upstream_srv_conf_t *us);
+
+
+typedef struct {
+ ngx_http_upstream_init_pt init_upstream;
+ ngx_http_upstream_init_peer_pt init;
+ void *data;
+} ngx_http_upstream_peer_t;
+
+
+typedef struct {
+ ngx_str_t name;
+ ngx_addr_t *addrs;
+ ngx_uint_t naddrs;
+ ngx_uint_t weight;
+ ngx_uint_t max_conns;
+ ngx_uint_t max_fails;
+ time_t fail_timeout;
+ ngx_msec_t slow_start;
+ ngx_uint_t down;
+
+ unsigned backup:1;
+
+ NGX_COMPAT_BEGIN(6)
+ NGX_COMPAT_END
+} ngx_http_upstream_server_t;
+
+
+#define NGX_HTTP_UPSTREAM_CREATE 0x0001
+#define NGX_HTTP_UPSTREAM_WEIGHT 0x0002
+#define NGX_HTTP_UPSTREAM_MAX_FAILS 0x0004
+#define NGX_HTTP_UPSTREAM_FAIL_TIMEOUT 0x0008
+#define NGX_HTTP_UPSTREAM_DOWN 0x0010
+#define NGX_HTTP_UPSTREAM_BACKUP 0x0020
+#define NGX_HTTP_UPSTREAM_MAX_CONNS 0x0100
+
+
+struct ngx_http_upstream_srv_conf_s {
+ ngx_http_upstream_peer_t peer;
+ void **srv_conf;
+
+ ngx_array_t *servers; /* ngx_http_upstream_server_t */
+
+ ngx_uint_t flags;
+ ngx_str_t host;
+ u_char *file_name;
+ ngx_uint_t line;
+ in_port_t port;
+ ngx_uint_t no_port; /* unsigned no_port:1 */
+
+#if (NGX_HTTP_UPSTREAM_ZONE)
+ ngx_shm_zone_t *shm_zone;
+#endif
+};
+
+
+typedef struct {
+ ngx_addr_t *addr;
+ ngx_http_complex_value_t *value;
+#if (NGX_HAVE_TRANSPARENT_PROXY)
+ ngx_uint_t transparent; /* unsigned transparent:1; */
+#endif
+} ngx_http_upstream_local_t;
+
+
+typedef struct {
+ ngx_http_upstream_srv_conf_t *upstream;
+
+ ngx_msec_t connect_timeout;
+ ngx_msec_t send_timeout;
+ ngx_msec_t read_timeout;
+ ngx_msec_t next_upstream_timeout;
+
+ size_t send_lowat;
+ size_t buffer_size;
+ size_t limit_rate;
+
+ size_t busy_buffers_size;
+ size_t max_temp_file_size;
+ size_t temp_file_write_size;
+
+ size_t busy_buffers_size_conf;
+ size_t max_temp_file_size_conf;
+ size_t temp_file_write_size_conf;
+
+ ngx_bufs_t bufs;
+
+ ngx_uint_t ignore_headers;
+ ngx_uint_t next_upstream;
+ ngx_uint_t store_access;
+ ngx_uint_t next_upstream_tries;
+ ngx_flag_t buffering;
+ ngx_flag_t request_buffering;
+ ngx_flag_t pass_request_headers;
+ ngx_flag_t pass_request_body;
+
+ ngx_flag_t ignore_client_abort;
+ ngx_flag_t intercept_errors;
+ ngx_flag_t cyclic_temp_file;
+ ngx_flag_t force_ranges;
+
+ ngx_path_t *temp_path;
+
+ ngx_hash_t hide_headers_hash;
+ ngx_array_t *hide_headers;
+ ngx_array_t *pass_headers;
+
+ ngx_http_upstream_local_t *local;
+ ngx_flag_t socket_keepalive;
+
+#if (NGX_HTTP_CACHE)
+ ngx_shm_zone_t *cache_zone;
+ ngx_http_complex_value_t *cache_value;
+
+ ngx_uint_t cache_min_uses;
+ ngx_uint_t cache_use_stale;
+ ngx_uint_t cache_methods;
+
+ off_t cache_max_range_offset;
+
+ ngx_flag_t cache_lock;
+ ngx_msec_t cache_lock_timeout;
+ ngx_msec_t cache_lock_age;
+
+ ngx_flag_t cache_revalidate;
+ ngx_flag_t cache_convert_head;
+ ngx_flag_t cache_background_update;
+
+ ngx_array_t *cache_valid;
+ ngx_array_t *cache_bypass;
+ ngx_array_t *cache_purge;
+ ngx_array_t *no_cache;
+#endif
+
+ ngx_array_t *store_lengths;
+ ngx_array_t *store_values;
+
+#if (NGX_HTTP_CACHE)
+ signed cache:2;
+#endif
+ signed store:2;
+ unsigned intercept_404:1;
+ unsigned change_buffering:1;
+ unsigned pass_trailers:1;
+ unsigned preserve_output:1;
+
+#if (NGX_HTTP_SSL || NGX_COMPAT)
+ ngx_ssl_t *ssl;
+ ngx_flag_t ssl_session_reuse;
+
+ ngx_http_complex_value_t *ssl_name;
+ ngx_flag_t ssl_server_name;
+ ngx_flag_t ssl_verify;
+#endif
+
+ ngx_str_t module;
+
+ NGX_COMPAT_BEGIN(2)
+ NGX_COMPAT_END
+} ngx_http_upstream_conf_t;
+
+
+typedef struct {
+ ngx_str_t name;
+ ngx_http_header_handler_pt handler;
+ ngx_uint_t offset;
+ ngx_http_header_handler_pt copy_handler;
+ ngx_uint_t conf;
+ ngx_uint_t redirect; /* unsigned redirect:1; */
+} ngx_http_upstream_header_t;
+
+
+typedef struct {
+ ngx_list_t headers;
+ ngx_list_t trailers;
+
+ ngx_uint_t status_n;
+ ngx_str_t status_line;
+
+ ngx_table_elt_t *status;
+ ngx_table_elt_t *date;
+ ngx_table_elt_t *server;
+ ngx_table_elt_t *connection;
+
+ ngx_table_elt_t *expires;
+ ngx_table_elt_t *etag;
+ ngx_table_elt_t *x_accel_expires;
+ ngx_table_elt_t *x_accel_redirect;
+ ngx_table_elt_t *x_accel_limit_rate;
+
+ ngx_table_elt_t *content_type;
+ ngx_table_elt_t *content_length;
+
+ ngx_table_elt_t *last_modified;
+ ngx_table_elt_t *location;
+ ngx_table_elt_t *accept_ranges;
+ ngx_table_elt_t *www_authenticate;
+ ngx_table_elt_t *transfer_encoding;
+ ngx_table_elt_t *vary;
+
+#if (NGX_HTTP_GZIP)
+ ngx_table_elt_t *content_encoding;
+#endif
+
+ ngx_array_t cache_control;
+ ngx_array_t cookies;
+
+ off_t content_length_n;
+ time_t last_modified_time;
+
+ unsigned connection_close:1;
+ unsigned chunked:1;
+} ngx_http_upstream_headers_in_t;
+
+
+typedef struct {
+ ngx_str_t host;
+ in_port_t port;
+ ngx_uint_t no_port; /* unsigned no_port:1 */
+
+ ngx_uint_t naddrs;
+ ngx_resolver_addr_t *addrs;
+
+ struct sockaddr *sockaddr;
+ socklen_t socklen;
+ ngx_str_t name;
+
+ ngx_resolver_ctx_t *ctx;
+} ngx_http_upstream_resolved_t;
+
+
+typedef void (*ngx_http_upstream_handler_pt)(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+
+
+struct ngx_http_upstream_s {
+ ngx_http_upstream_handler_pt read_event_handler;
+ ngx_http_upstream_handler_pt write_event_handler;
+
+ ngx_peer_connection_t peer;
+
+ ngx_event_pipe_t *pipe;
+
+ ngx_chain_t *request_bufs;
+
+ ngx_output_chain_ctx_t output;
+ ngx_chain_writer_ctx_t writer;
+
+ ngx_http_upstream_conf_t *conf;
+ ngx_http_upstream_srv_conf_t *upstream;
+#if (NGX_HTTP_CACHE)
+ ngx_array_t *caches;
+#endif
+
+ ngx_http_upstream_headers_in_t headers_in;
+
+ ngx_http_upstream_resolved_t *resolved;
+
+ ngx_buf_t from_client;
+
+ ngx_buf_t buffer;
+ off_t length;
+
+ ngx_chain_t *out_bufs;
+ ngx_chain_t *busy_bufs;
+ ngx_chain_t *free_bufs;
+
+ ngx_int_t (*input_filter_init)(void *data);
+ ngx_int_t (*input_filter)(void *data, ssize_t bytes);
+ void *input_filter_ctx;
+
+#if (NGX_HTTP_CACHE)
+ ngx_int_t (*create_key)(ngx_http_request_t *r);
+#endif
+ ngx_int_t (*create_request)(ngx_http_request_t *r);
+ ngx_int_t (*reinit_request)(ngx_http_request_t *r);
+ ngx_int_t (*process_header)(ngx_http_request_t *r);
+ void (*abort_request)(ngx_http_request_t *r);
+ void (*finalize_request)(ngx_http_request_t *r,
+ ngx_int_t rc);
+ ngx_int_t (*rewrite_redirect)(ngx_http_request_t *r,
+ ngx_table_elt_t *h, size_t prefix);
+ ngx_int_t (*rewrite_cookie)(ngx_http_request_t *r,
+ ngx_table_elt_t *h);
+
+ ngx_msec_t start_time;
+
+ ngx_http_upstream_state_t *state;
+
+ ngx_str_t method;
+ ngx_str_t schema;
+ ngx_str_t uri;
+
+#if (NGX_HTTP_SSL || NGX_COMPAT)
+ ngx_str_t ssl_name;
+#endif
+
+ ngx_http_cleanup_pt *cleanup;
+
+ unsigned store:1;
+ unsigned cacheable:1;
+ unsigned accel:1;
+ unsigned ssl:1;
+#if (NGX_HTTP_CACHE)
+ unsigned cache_status:3;
+#endif
+
+ unsigned buffering:1;
+ unsigned keepalive:1;
+ unsigned upgrade:1;
+
+ unsigned request_sent:1;
+ unsigned request_body_sent:1;
+ unsigned request_body_blocked:1;
+ unsigned header_sent:1;
+};
+
+
+typedef struct {
+ ngx_uint_t status;
+ ngx_uint_t mask;
+} ngx_http_upstream_next_t;
+
+
+typedef struct {
+ ngx_str_t key;
+ ngx_str_t value;
+ ngx_uint_t skip_empty;
+} ngx_http_upstream_param_t;
+
+
+ngx_int_t ngx_http_upstream_create(ngx_http_request_t *r);
+void ngx_http_upstream_init(ngx_http_request_t *r);
+ngx_http_upstream_srv_conf_t *ngx_http_upstream_add(ngx_conf_t *cf,
+ ngx_url_t *u, ngx_uint_t flags);
+char *ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+char *ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+ngx_int_t ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf,
+ ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev,
+ ngx_str_t *default_hide_headers, ngx_hash_init_t *hash);
+
+
+#define ngx_http_conf_upstream_srv_conf(uscf, module) \
+ uscf->srv_conf[module.ctx_index]
+
+
+extern ngx_module_t ngx_http_upstream_module;
+extern ngx_conf_bitmask_t ngx_http_upstream_cache_method_mask[];
+extern ngx_conf_bitmask_t ngx_http_upstream_ignore_headers_masks[];
+
+
+#endif /* _NGX_HTTP_UPSTREAM_H_INCLUDED_ */
diff --git a/src/nginx-1.18.0/src/http/ngx_http_upstream_round_robin.c b/src/nginx-1.18.0/src/http/ngx_http_upstream_round_robin.c
new file mode 100644
index 0000000..8e7b4ea
--- /dev/null
+++ b/src/nginx-1.18.0/src/http/ngx_http_upstream_round_robin.c
@@ -0,0 +1,839 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+#include
+
+
+#define ngx_http_upstream_tries(p) ((p)->number \
+ + ((p)->next ? (p)->next->number : 0))
+
+
+static ngx_http_upstream_rr_peer_t *ngx_http_upstream_get_peer(
+ ngx_http_upstream_rr_peer_data_t *rrp);
+
+#if (NGX_HTTP_SSL)
+
+static ngx_int_t ngx_http_upstream_empty_set_session(ngx_peer_connection_t *pc,
+ void *data);
+static void ngx_http_upstream_empty_save_session(ngx_peer_connection_t *pc,
+ void *data);
+
+#endif
+
+
+ngx_int_t
+ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
+ ngx_http_upstream_srv_conf_t *us)
+{
+ ngx_url_t u;
+ ngx_uint_t i, j, n, w;
+ ngx_http_upstream_server_t *server;
+ ngx_http_upstream_rr_peer_t *peer, **peerp;
+ ngx_http_upstream_rr_peers_t *peers, *backup;
+
+ us->peer.init = ngx_http_upstream_init_round_robin_peer;
+
+ if (us->servers) {
+ server = us->servers->elts;
+
+ n = 0;
+ w = 0;
+
+ for (i = 0; i < us->servers->nelts; i++) {
+ if (server[i].backup) {
+ continue;
+ }
+
+ n += server[i].naddrs;
+ w += server[i].naddrs * server[i].weight;
+ }
+
+ if (n == 0) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no servers in upstream \"%V\" in %s:%ui",
+ &us->host, us->file_name, us->line);
+ return NGX_ERROR;
+ }
+
+ peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t));
+ if (peers == NULL) {
+ return NGX_ERROR;
+ }
+
+ peer = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peer_t) * n);
+ if (peer == NULL) {
+ return NGX_ERROR;
+ }
+
+ peers->single = (n == 1);
+ peers->number = n;
+ peers->weighted = (w != n);
+ peers->total_weight = w;
+ peers->name = &us->host;
+
+ n = 0;
+ peerp = &peers->peer;
+
+ for (i = 0; i < us->servers->nelts; i++) {
+ if (server[i].backup) {
+ continue;
+ }
+
+ for (j = 0; j < server[i].naddrs; j++) {
+ peer[n].sockaddr = server[i].addrs[j].sockaddr;
+ peer[n].socklen = server[i].addrs[j].socklen;
+ peer[n].name = server[i].addrs[j].name;
+ peer[n].weight = server[i].weight;
+ peer[n].effective_weight = server[i].weight;
+ peer[n].current_weight = 0;
+ peer[n].max_conns = server[i].max_conns;
+ peer[n].max_fails = server[i].max_fails;
+ peer[n].fail_timeout = server[i].fail_timeout;
+ peer[n].down = server[i].down;
+ peer[n].server = server[i].name;
+
+ *peerp = &peer[n];
+ peerp = &peer[n].next;
+ n++;
+ }
+ }
+
+ us->peer.data = peers;
+
+ /* backup servers */
+
+ n = 0;
+ w = 0;
+
+ for (i = 0; i < us->servers->nelts; i++) {
+ if (!server[i].backup) {
+ continue;
+ }
+
+ n += server[i].naddrs;
+ w += server[i].naddrs * server[i].weight;
+ }
+
+ if (n == 0) {
+ return NGX_OK;
+ }
+
+ backup = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t));
+ if (backup == NULL) {
+ return NGX_ERROR;
+ }
+
+ peer = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peer_t) * n);
+ if (peer == NULL) {
+ return NGX_ERROR;
+ }
+
+ peers->single = 0;
+ backup->single = 0;
+ backup->number = n;
+ backup->weighted = (w != n);
+ backup->total_weight = w;
+ backup->name = &us->host;
+
+ n = 0;
+ peerp = &backup->peer;
+
+ for (i = 0; i < us->servers->nelts; i++) {
+ if (!server[i].backup) {
+ continue;
+ }
+
+ for (j = 0; j < server[i].naddrs; j++) {
+ peer[n].sockaddr = server[i].addrs[j].sockaddr;
+ peer[n].socklen = server[i].addrs[j].socklen;
+ peer[n].name = server[i].addrs[j].name;
+ peer[n].weight = server[i].weight;
+ peer[n].effective_weight = server[i].weight;
+ peer[n].current_weight = 0;
+ peer[n].max_conns = server[i].max_conns;
+ peer[n].max_fails = server[i].max_fails;
+ peer[n].fail_timeout = server[i].fail_timeout;
+ peer[n].down = server[i].down;
+ peer[n].server = server[i].name;
+
+ *peerp = &peer[n];
+ peerp = &peer[n].next;
+ n++;
+ }
+ }
+
+ peers->next = backup;
+
+ return NGX_OK;
+ }
+
+
+ /* an upstream implicitly defined by proxy_pass, etc. */
+
+ if (us->port == 0) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no port in upstream \"%V\" in %s:%ui",
+ &us->host, us->file_name, us->line);
+ return NGX_ERROR;
+ }
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.host = us->host;
+ u.port = us->port;
+
+ if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {
+ if (u.err) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "%s in upstream \"%V\" in %s:%ui",
+ u.err, &us->host, us->file_name, us->line);
+ }
+
+ return NGX_ERROR;
+ }
+
+ n = u.naddrs;
+
+ peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t));
+ if (peers == NULL) {
+ return NGX_ERROR;
+ }
+
+ peer = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peer_t) * n);
+ if (peer == NULL) {
+ return NGX_ERROR;
+ }
+
+ peers->single = (n == 1);
+ peers->number = n;
+ peers->weighted = 0;
+ peers->total_weight = n;
+ peers->name = &us->host;
+
+ peerp = &peers->peer;
+
+ for (i = 0; i < u.naddrs; i++) {
+ peer[i].sockaddr = u.addrs[i].sockaddr;
+ peer[i].socklen = u.addrs[i].socklen;
+ peer[i].name = u.addrs[i].name;
+ peer[i].weight = 1;
+ peer[i].effective_weight = 1;
+ peer[i].current_weight = 0;
+ peer[i].max_conns = 0;
+ peer[i].max_fails = 1;
+ peer[i].fail_timeout = 10;
+ *peerp = &peer[i];
+ peerp = &peer[i].next;
+ }
+
+ us->peer.data = peers;
+
+ /* implicitly defined upstream has no backup servers */
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r,
+ ngx_http_upstream_srv_conf_t *us)
+{
+ ngx_uint_t n;
+ ngx_http_upstream_rr_peer_data_t *rrp;
+
+ rrp = r->upstream->peer.data;
+
+ if (rrp == NULL) {
+ rrp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_rr_peer_data_t));
+ if (rrp == NULL) {
+ return NGX_ERROR;
+ }
+
+ r->upstream->peer.data = rrp;
+ }
+
+ rrp->peers = us->peer.data;
+ rrp->current = NULL;
+ rrp->config = 0;
+
+ n = rrp->peers->number;
+
+ if (rrp->peers->next && rrp->peers->next->number > n) {
+ n = rrp->peers->next->number;
+ }
+
+ if (n <= 8 * sizeof(uintptr_t)) {
+ rrp->tried = &rrp->data;
+ rrp->data = 0;
+
+ } else {
+ n = (n + (8 * sizeof(uintptr_t) - 1)) / (8 * sizeof(uintptr_t));
+
+ rrp->tried = ngx_pcalloc(r->pool, n * sizeof(uintptr_t));
+ if (rrp->tried == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ r->upstream->peer.get = ngx_http_upstream_get_round_robin_peer;
+ r->upstream->peer.free = ngx_http_upstream_free_round_robin_peer;
+ r->upstream->peer.tries = ngx_http_upstream_tries(rrp->peers);
+#if (NGX_HTTP_SSL)
+ r->upstream->peer.set_session =
+ ngx_http_upstream_set_round_robin_peer_session;
+ r->upstream->peer.save_session =
+ ngx_http_upstream_save_round_robin_peer_session;
+#endif
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
+ ngx_http_upstream_resolved_t *ur)
+{
+ u_char *p;
+ size_t len;
+ socklen_t socklen;
+ ngx_uint_t i, n;
+ struct sockaddr *sockaddr;
+ ngx_http_upstream_rr_peer_t *peer, **peerp;
+ ngx_http_upstream_rr_peers_t *peers;
+ ngx_http_upstream_rr_peer_data_t *rrp;
+
+ rrp = r->upstream->peer.data;
+
+ if (rrp == NULL) {
+ rrp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_rr_peer_data_t));
+ if (rrp == NULL) {
+ return NGX_ERROR;
+ }
+
+ r->upstream->peer.data = rrp;
+ }
+
+ peers = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_rr_peers_t));
+ if (peers == NULL) {
+ return NGX_ERROR;
+ }
+
+ peer = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_rr_peer_t)
+ * ur->naddrs);
+ if (peer == NULL) {
+ return NGX_ERROR;
+ }
+
+ peers->single = (ur->naddrs == 1);
+ peers->number = ur->naddrs;
+ peers->name = &ur->host;
+
+ if (ur->sockaddr) {
+ peer[0].sockaddr = ur->sockaddr;
+ peer[0].socklen = ur->socklen;
+ peer[0].name = ur->name.data ? ur->name : ur->host;
+ peer[0].weight = 1;
+ peer[0].effective_weight = 1;
+ peer[0].current_weight = 0;
+ peer[0].max_conns = 0;
+ peer[0].max_fails = 1;
+ peer[0].fail_timeout = 10;
+ peers->peer = peer;
+
+ } else {
+ peerp = &peers->peer;
+
+ for (i = 0; i < ur->naddrs; i++) {
+
+ socklen = ur->addrs[i].socklen;
+
+ sockaddr = ngx_palloc(r->pool, socklen);
+ if (sockaddr == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen);
+ ngx_inet_set_port(sockaddr, ur->port);
+
+ p = ngx_pnalloc(r->pool, NGX_SOCKADDR_STRLEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);
+
+ peer[i].sockaddr = sockaddr;
+ peer[i].socklen = socklen;
+ peer[i].name.len = len;
+ peer[i].name.data = p;
+ peer[i].weight = 1;
+ peer[i].effective_weight = 1;
+ peer[i].current_weight = 0;
+ peer[i].max_conns = 0;
+ peer[i].max_fails = 1;
+ peer[i].fail_timeout = 10;
+ *peerp = &peer[i];
+ peerp = &peer[i].next;
+ }
+ }
+
+ rrp->peers = peers;
+ rrp->current = NULL;
+ rrp->config = 0;
+
+ if (rrp->peers->number <= 8 * sizeof(uintptr_t)) {
+ rrp->tried = &rrp->data;
+ rrp->data = 0;
+
+ } else {
+ n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))
+ / (8 * sizeof(uintptr_t));
+
+ rrp->tried = ngx_pcalloc(r->pool, n * sizeof(uintptr_t));
+ if (rrp->tried == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ r->upstream->peer.get = ngx_http_upstream_get_round_robin_peer;
+ r->upstream->peer.free = ngx_http_upstream_free_round_robin_peer;
+ r->upstream->peer.tries = ngx_http_upstream_tries(rrp->peers);
+#if (NGX_HTTP_SSL)
+ r->upstream->peer.set_session = ngx_http_upstream_empty_set_session;
+ r->upstream->peer.save_session = ngx_http_upstream_empty_save_session;
+#endif
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_http_upstream_rr_peer_data_t *rrp = data;
+
+ ngx_int_t rc;
+ ngx_uint_t i, n;
+ ngx_http_upstream_rr_peer_t *peer;
+ ngx_http_upstream_rr_peers_t *peers;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "get rr peer, try: %ui", pc->tries);
+
+ pc->cached = 0;
+ pc->connection = NULL;
+
+ peers = rrp->peers;
+ ngx_http_upstream_rr_peers_wlock(peers);
+
+ if (peers->single) {
+ peer = peers->peer;
+
+ if (peer->down) {
+ goto failed;
+ }
+
+ if (peer->max_conns && peer->conns >= peer->max_conns) {
+ goto failed;
+ }
+
+ rrp->current = peer;
+
+ } else {
+
+ /* there are several peers */
+
+ peer = ngx_http_upstream_get_peer(rrp);
+
+ if (peer == NULL) {
+ goto failed;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "get rr peer, current: %p %i",
+ peer, peer->current_weight);
+ }
+
+ pc->sockaddr = peer->sockaddr;
+ pc->socklen = peer->socklen;
+ pc->name = &peer->name;
+
+ peer->conns++;
+
+ ngx_http_upstream_rr_peers_unlock(peers);
+
+ return NGX_OK;
+
+failed:
+
+ if (peers->next) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, "backup servers");
+
+ rrp->peers = peers->next;
+
+ n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))
+ / (8 * sizeof(uintptr_t));
+
+ for (i = 0; i < n; i++) {
+ rrp->tried[i] = 0;
+ }
+
+ ngx_http_upstream_rr_peers_unlock(peers);
+
+ rc = ngx_http_upstream_get_round_robin_peer(pc, rrp);
+
+ if (rc != NGX_BUSY) {
+ return rc;
+ }
+
+ ngx_http_upstream_rr_peers_wlock(peers);
+ }
+
+ ngx_http_upstream_rr_peers_unlock(peers);
+
+ pc->name = peers->name;
+
+ return NGX_BUSY;
+}
+
+
+static ngx_http_upstream_rr_peer_t *
+ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)
+{
+ time_t now;
+ uintptr_t m;
+ ngx_int_t total;
+ ngx_uint_t i, n, p;
+ ngx_http_upstream_rr_peer_t *peer, *best;
+
+ now = ngx_time();
+
+ best = NULL;
+ total = 0;
+
+#if (NGX_SUPPRESS_WARN)
+ p = 0;
+#endif
+
+ for (peer = rrp->peers->peer, i = 0;
+ peer;
+ peer = peer->next, i++)
+ {
+ n = i / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
+
+ if (rrp->tried[n] & m) {
+ continue;
+ }
+
+ if (peer->down) {
+ continue;
+ }
+
+ if (peer->max_fails
+ && peer->fails >= peer->max_fails
+ && now - peer->checked <= peer->fail_timeout)
+ {
+ continue;
+ }
+
+ if (peer->max_conns && peer->conns >= peer->max_conns) {
+ continue;
+ }
+
+ peer->current_weight += peer->effective_weight;
+ total += peer->effective_weight;
+
+ if (peer->effective_weight < peer->weight) {
+ peer->effective_weight++;
+ }
+
+ if (best == NULL || peer->current_weight > best->current_weight) {
+ best = peer;
+ p = i;
+ }
+ }
+
+ if (best == NULL) {
+ return NULL;
+ }
+
+ rrp->current = best;
+
+ n = p / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
+
+ rrp->tried[n] |= m;
+
+ best->current_weight -= total;
+
+ if (now - best->checked > best->fail_timeout) {
+ best->checked = now;
+ }
+
+ return best;
+}
+
+
+void
+ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data,
+ ngx_uint_t state)
+{
+ ngx_http_upstream_rr_peer_data_t *rrp = data;
+
+ time_t now;
+ ngx_http_upstream_rr_peer_t *peer;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "free rr peer %ui %ui", pc->tries, state);
+
+ /* TODO: NGX_PEER_KEEPALIVE */
+
+ peer = rrp->current;
+
+ ngx_http_upstream_rr_peers_rlock(rrp->peers);
+ ngx_http_upstream_rr_peer_lock(rrp->peers, peer);
+
+ if (rrp->peers->single) {
+
+ peer->conns--;
+
+ ngx_http_upstream_rr_peer_unlock(rrp->peers, peer);
+ ngx_http_upstream_rr_peers_unlock(rrp->peers);
+
+ pc->tries = 0;
+ return;
+ }
+
+ if (state & NGX_PEER_FAILED) {
+ now = ngx_time();
+
+ peer->fails++;
+ peer->accessed = now;
+ peer->checked = now;
+
+ if (peer->max_fails) {
+ peer->effective_weight -= peer->weight / peer->max_fails;
+
+ if (peer->fails >= peer->max_fails) {
+ ngx_log_error(NGX_LOG_WARN, pc->log, 0,
+ "upstream server temporarily disabled");
+ }
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "free rr peer failed: %p %i",
+ peer, peer->effective_weight);
+
+ if (peer->effective_weight < 0) {
+ peer->effective_weight = 0;
+ }
+
+ } else {
+
+ /* mark peer live if check passed */
+
+ if (peer->accessed < peer->checked) {
+ peer->fails = 0;
+ }
+ }
+
+ peer->conns--;
+
+ ngx_http_upstream_rr_peer_unlock(rrp->peers, peer);
+ ngx_http_upstream_rr_peers_unlock(rrp->peers);
+
+ if (pc->tries) {
+ pc->tries--;
+ }
+}
+
+
+#if (NGX_HTTP_SSL)
+
+ngx_int_t
+ngx_http_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc,
+ void *data)
+{
+ ngx_http_upstream_rr_peer_data_t *rrp = data;
+
+ ngx_int_t rc;
+ ngx_ssl_session_t *ssl_session;
+ ngx_http_upstream_rr_peer_t *peer;
+#if (NGX_HTTP_UPSTREAM_ZONE)
+ int len;
+ const u_char *p;
+ ngx_http_upstream_rr_peers_t *peers;
+ u_char buf[NGX_SSL_MAX_SESSION_SIZE];
+#endif
+
+ peer = rrp->current;
+
+#if (NGX_HTTP_UPSTREAM_ZONE)
+ peers = rrp->peers;
+
+ if (peers->shpool) {
+ ngx_http_upstream_rr_peers_rlock(peers);
+ ngx_http_upstream_rr_peer_lock(peers, peer);
+
+ if (peer->ssl_session == NULL) {
+ ngx_http_upstream_rr_peer_unlock(peers, peer);
+ ngx_http_upstream_rr_peers_unlock(peers);
+ return NGX_OK;
+ }
+
+ len = peer->ssl_session_len;
+
+ ngx_memcpy(buf, peer->ssl_session, len);
+
+ ngx_http_upstream_rr_peer_unlock(peers, peer);
+ ngx_http_upstream_rr_peers_unlock(peers);
+
+ p = buf;
+ ssl_session = d2i_SSL_SESSION(NULL, &p, len);
+
+ rc = ngx_ssl_set_session(pc->connection, ssl_session);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "set session: %p", ssl_session);
+
+ ngx_ssl_free_session(ssl_session);
+
+ return rc;
+ }
+#endif
+
+ ssl_session = peer->ssl_session;
+
+ rc = ngx_ssl_set_session(pc->connection, ssl_session);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "set session: %p", ssl_session);
+
+ return rc;
+}
+
+
+void
+ngx_http_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,
+ void *data)
+{
+ ngx_http_upstream_rr_peer_data_t *rrp = data;
+
+ ngx_ssl_session_t *old_ssl_session, *ssl_session;
+ ngx_http_upstream_rr_peer_t *peer;
+#if (NGX_HTTP_UPSTREAM_ZONE)
+ int len;
+ u_char *p;
+ ngx_http_upstream_rr_peers_t *peers;
+ u_char buf[NGX_SSL_MAX_SESSION_SIZE];
+#endif
+
+#if (NGX_HTTP_UPSTREAM_ZONE)
+ peers = rrp->peers;
+
+ if (peers->shpool) {
+
+ ssl_session = ngx_ssl_get0_session(pc->connection);
+
+ if (ssl_session == NULL) {
+ return;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "save session: %p", ssl_session);
+
+ len = i2d_SSL_SESSION(ssl_session, NULL);
+
+ /* do not cache too big session */
+
+ if (len > NGX_SSL_MAX_SESSION_SIZE) {
+ return;
+ }
+
+ p = buf;
+ (void) i2d_SSL_SESSION(ssl_session, &p);
+
+ peer = rrp->current;
+
+ ngx_http_upstream_rr_peers_rlock(peers);
+ ngx_http_upstream_rr_peer_lock(peers, peer);
+
+ if (len > peer->ssl_session_len) {
+ ngx_shmtx_lock(&peers->shpool->mutex);
+
+ if (peer->ssl_session) {
+ ngx_slab_free_locked(peers->shpool, peer->ssl_session);
+ }
+
+ peer->ssl_session = ngx_slab_alloc_locked(peers->shpool, len);
+
+ ngx_shmtx_unlock(&peers->shpool->mutex);
+
+ if (peer->ssl_session == NULL) {
+ peer->ssl_session_len = 0;
+
+ ngx_http_upstream_rr_peer_unlock(peers, peer);
+ ngx_http_upstream_rr_peers_unlock(peers);
+ return;
+ }
+
+ peer->ssl_session_len = len;
+ }
+
+ ngx_memcpy(peer->ssl_session, buf, len);
+
+ ngx_http_upstream_rr_peer_unlock(peers, peer);
+ ngx_http_upstream_rr_peers_unlock(peers);
+
+ return;
+ }
+#endif
+
+ ssl_session = ngx_ssl_get_session(pc->connection);
+
+ if (ssl_session == NULL) {
+ return;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "save session: %p", ssl_session);
+
+ peer = rrp->current;
+
+ old_ssl_session = peer->ssl_session;
+ peer->ssl_session = ssl_session;
+
+ if (old_ssl_session) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "old session: %p", old_ssl_session);
+
+ /* TODO: may block */
+
+ ngx_ssl_free_session(old_ssl_session);
+ }
+}
+
+
+static ngx_int_t
+ngx_http_upstream_empty_set_session(ngx_peer_connection_t *pc, void *data)
+{
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_upstream_empty_save_session(ngx_peer_connection_t *pc, void *data)
+{
+ return;
+}
+
+#endif
diff --git a/src/nginx-1.18.0/src/http/ngx_http_upstream_round_robin.h b/src/nginx-1.18.0/src/http/ngx_http_upstream_round_robin.h
new file mode 100644
index 0000000..45f258d
--- /dev/null
+++ b/src/nginx-1.18.0/src/http/ngx_http_upstream_round_robin.h
@@ -0,0 +1,156 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_
+#define _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_
+
+
+#include
+#include
+#include
+
+
+typedef struct ngx_http_upstream_rr_peer_s ngx_http_upstream_rr_peer_t;
+
+struct ngx_http_upstream_rr_peer_s {
+ struct sockaddr *sockaddr;
+ socklen_t socklen;
+ ngx_str_t name;
+ ngx_str_t server;
+
+ ngx_int_t current_weight;
+ ngx_int_t effective_weight;
+ ngx_int_t weight;
+
+ ngx_uint_t conns;
+ ngx_uint_t max_conns;
+
+ ngx_uint_t fails;
+ time_t accessed;
+ time_t checked;
+
+ ngx_uint_t max_fails;
+ time_t fail_timeout;
+ ngx_msec_t slow_start;
+ ngx_msec_t start_time;
+
+ ngx_uint_t down;
+
+#if (NGX_HTTP_SSL || NGX_COMPAT)
+ void *ssl_session;
+ int ssl_session_len;
+#endif
+
+#if (NGX_HTTP_UPSTREAM_ZONE)
+ ngx_atomic_t lock;
+#endif
+
+ ngx_http_upstream_rr_peer_t *next;
+
+ NGX_COMPAT_BEGIN(32)
+ NGX_COMPAT_END
+};
+
+
+typedef struct ngx_http_upstream_rr_peers_s ngx_http_upstream_rr_peers_t;
+
+struct ngx_http_upstream_rr_peers_s {
+ ngx_uint_t number;
+
+#if (NGX_HTTP_UPSTREAM_ZONE)
+ ngx_slab_pool_t *shpool;
+ ngx_atomic_t rwlock;
+ ngx_http_upstream_rr_peers_t *zone_next;
+#endif
+
+ ngx_uint_t total_weight;
+
+ unsigned single:1;
+ unsigned weighted:1;
+
+ ngx_str_t *name;
+
+ ngx_http_upstream_rr_peers_t *next;
+
+ ngx_http_upstream_rr_peer_t *peer;
+};
+
+
+#if (NGX_HTTP_UPSTREAM_ZONE)
+
+#define ngx_http_upstream_rr_peers_rlock(peers) \
+ \
+ if (peers->shpool) { \
+ ngx_rwlock_rlock(&peers->rwlock); \
+ }
+
+#define ngx_http_upstream_rr_peers_wlock(peers) \
+ \
+ if (peers->shpool) { \
+ ngx_rwlock_wlock(&peers->rwlock); \
+ }
+
+#define ngx_http_upstream_rr_peers_unlock(peers) \
+ \
+ if (peers->shpool) { \
+ ngx_rwlock_unlock(&peers->rwlock); \
+ }
+
+
+#define ngx_http_upstream_rr_peer_lock(peers, peer) \
+ \
+ if (peers->shpool) { \
+ ngx_rwlock_wlock(&peer->lock); \
+ }
+
+#define ngx_http_upstream_rr_peer_unlock(peers, peer) \
+ \
+ if (peers->shpool) { \
+ ngx_rwlock_unlock(&peer->lock); \
+ }
+
+#else
+
+#define ngx_http_upstream_rr_peers_rlock(peers)
+#define ngx_http_upstream_rr_peers_wlock(peers)
+#define ngx_http_upstream_rr_peers_unlock(peers)
+#define ngx_http_upstream_rr_peer_lock(peers, peer)
+#define ngx_http_upstream_rr_peer_unlock(peers, peer)
+
+#endif
+
+
+typedef struct {
+ ngx_uint_t config;
+ ngx_http_upstream_rr_peers_t *peers;
+ ngx_http_upstream_rr_peer_t *current;
+ uintptr_t *tried;
+ uintptr_t data;
+} ngx_http_upstream_rr_peer_data_t;
+
+
+ngx_int_t ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
+ ngx_http_upstream_srv_conf_t *us);
+ngx_int_t ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r,
+ ngx_http_upstream_srv_conf_t *us);
+ngx_int_t ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
+ ngx_http_upstream_resolved_t *ur);
+ngx_int_t ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc,
+ void *data);
+void ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc,
+ void *data, ngx_uint_t state);
+
+#if (NGX_HTTP_SSL)
+ngx_int_t
+ ngx_http_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc,
+ void *data);
+void ngx_http_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,
+ void *data);
+#endif
+
+
+#endif /* _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_ */
diff --git a/src/nginx-1.18.0/src/http/ngx_http_variables.c b/src/nginx-1.18.0/src/http/ngx_http_variables.c
new file mode 100644
index 0000000..e067cf0
--- /dev/null
+++ b/src/nginx-1.18.0/src/http/ngx_http_variables.c
@@ -0,0 +1,2740 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+#include
+#include
+
+
+static ngx_http_variable_t *ngx_http_add_prefix_variable(ngx_conf_t *cf,
+ ngx_str_t *name, ngx_uint_t flags);
+
+static ngx_int_t ngx_http_variable_request(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+#if 0
+static void ngx_http_variable_request_set(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+#endif
+static ngx_int_t ngx_http_variable_request_get_size(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_header(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+
+static ngx_int_t ngx_http_variable_cookies(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_headers(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_headers_internal(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data, u_char sep);
+
+static ngx_int_t ngx_http_variable_unknown_header_in(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_unknown_header_out(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_unknown_trailer_out(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_request_line(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_cookie(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_argument(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+#if (NGX_HAVE_TCP_INFO)
+static ngx_int_t ngx_http_variable_tcpinfo(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+#endif
+
+static ngx_int_t ngx_http_variable_content_length(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_host(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_binary_remote_addr(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_remote_addr(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_remote_port(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_proxy_protocol_addr(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_proxy_protocol_port(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_scheme(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_https(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static void ngx_http_variable_set_args(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_is_args(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_document_root(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_realpath_root(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_request_filename(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_server_name(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_request_method(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_remote_user(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_bytes_sent(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_body_bytes_sent(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_pipe(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_request_completion(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_request_body(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_request_body_file(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_request_length(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_request_time(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_request_id(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_status(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+
+static ngx_int_t ngx_http_variable_sent_content_type(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_sent_content_length(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_sent_location(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_sent_last_modified(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_sent_connection(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_sent_keep_alive(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_sent_transfer_encoding(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static void ngx_http_variable_set_limit_rate(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+
+static ngx_int_t ngx_http_variable_connection(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_connection_requests(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+
+static ngx_int_t ngx_http_variable_nginx_version(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_hostname(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_pid(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_msec(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_time_iso8601(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_time_local(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+
+/*
+ * TODO:
+ * Apache CGI: AUTH_TYPE, PATH_INFO (null), PATH_TRANSLATED
+ * REMOTE_HOST (null), REMOTE_IDENT (null),
+ * SERVER_SOFTWARE
+ *
+ * Apache SSI: DOCUMENT_NAME, LAST_MODIFIED, USER_NAME (file owner)
+ */
+
+/*
+ * the $http_host, $http_user_agent, $http_referer, and $http_via
+ * variables may be handled by generic
+ * ngx_http_variable_unknown_header_in(), but for performance reasons
+ * they are handled using dedicated entries
+ */
+
+static ngx_http_variable_t ngx_http_core_variables[] = {
+
+ { ngx_string("http_host"), NULL, ngx_http_variable_header,
+ offsetof(ngx_http_request_t, headers_in.host), 0, 0 },
+
+ { ngx_string("http_user_agent"), NULL, ngx_http_variable_header,
+ offsetof(ngx_http_request_t, headers_in.user_agent), 0, 0 },
+
+ { ngx_string("http_referer"), NULL, ngx_http_variable_header,
+ offsetof(ngx_http_request_t, headers_in.referer), 0, 0 },
+
+#if (NGX_HTTP_GZIP)
+ { ngx_string("http_via"), NULL, ngx_http_variable_header,
+ offsetof(ngx_http_request_t, headers_in.via), 0, 0 },
+#endif
+
+#if (NGX_HTTP_X_FORWARDED_FOR)
+ { ngx_string("http_x_forwarded_for"), NULL, ngx_http_variable_headers,
+ offsetof(ngx_http_request_t, headers_in.x_forwarded_for), 0, 0 },
+#endif
+
+ { ngx_string("http_cookie"), NULL, ngx_http_variable_cookies,
+ offsetof(ngx_http_request_t, headers_in.cookies), 0, 0 },
+
+ { ngx_string("content_length"), NULL, ngx_http_variable_content_length,
+ 0, 0, 0 },
+
+ { ngx_string("content_type"), NULL, ngx_http_variable_header,
+ offsetof(ngx_http_request_t, headers_in.content_type), 0, 0 },
+
+ { ngx_string("host"), NULL, ngx_http_variable_host, 0, 0, 0 },
+
+ { ngx_string("binary_remote_addr"), NULL,
+ ngx_http_variable_binary_remote_addr, 0, 0, 0 },
+
+ { ngx_string("remote_addr"), NULL, ngx_http_variable_remote_addr, 0, 0, 0 },
+
+ { ngx_string("remote_port"), NULL, ngx_http_variable_remote_port, 0, 0, 0 },
+
+ { ngx_string("proxy_protocol_addr"), NULL,
+ ngx_http_variable_proxy_protocol_addr,
+ offsetof(ngx_proxy_protocol_t, src_addr), 0, 0 },
+
+ { ngx_string("proxy_protocol_port"), NULL,
+ ngx_http_variable_proxy_protocol_port,
+ offsetof(ngx_proxy_protocol_t, src_port), 0, 0 },
+
+ { ngx_string("proxy_protocol_server_addr"), NULL,
+ ngx_http_variable_proxy_protocol_addr,
+ offsetof(ngx_proxy_protocol_t, dst_addr), 0, 0 },
+
+ { ngx_string("proxy_protocol_server_port"), NULL,
+ ngx_http_variable_proxy_protocol_port,
+ offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 },
+
+ { ngx_string("server_addr"), NULL, ngx_http_variable_server_addr, 0, 0, 0 },
+
+ { ngx_string("server_port"), NULL, ngx_http_variable_server_port, 0, 0, 0 },
+
+ { ngx_string("server_protocol"), NULL, ngx_http_variable_request,
+ offsetof(ngx_http_request_t, http_protocol), 0, 0 },
+
+ { ngx_string("scheme"), NULL, ngx_http_variable_scheme, 0, 0, 0 },
+
+ { ngx_string("https"), NULL, ngx_http_variable_https, 0, 0, 0 },
+
+ { ngx_string("request_uri"), NULL, ngx_http_variable_request,
+ offsetof(ngx_http_request_t, unparsed_uri), 0, 0 },
+
+ { ngx_string("uri"), NULL, ngx_http_variable_request,
+ offsetof(ngx_http_request_t, uri),
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("document_uri"), NULL, ngx_http_variable_request,
+ offsetof(ngx_http_request_t, uri),
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("request"), NULL, ngx_http_variable_request_line, 0, 0, 0 },
+
+ { ngx_string("document_root"), NULL,
+ ngx_http_variable_document_root, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("realpath_root"), NULL,
+ ngx_http_variable_realpath_root, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("query_string"), NULL, ngx_http_variable_request,
+ offsetof(ngx_http_request_t, args),
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("args"),
+ ngx_http_variable_set_args,
+ ngx_http_variable_request,
+ offsetof(ngx_http_request_t, args),
+ NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("is_args"), NULL, ngx_http_variable_is_args,
+ 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("request_filename"), NULL,
+ ngx_http_variable_request_filename, 0,
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("server_name"), NULL, ngx_http_variable_server_name, 0, 0, 0 },
+
+ { ngx_string("request_method"), NULL,
+ ngx_http_variable_request_method, 0,
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("remote_user"), NULL, ngx_http_variable_remote_user, 0, 0, 0 },
+
+ { ngx_string("bytes_sent"), NULL, ngx_http_variable_bytes_sent,
+ 0, 0, 0 },
+
+ { ngx_string("body_bytes_sent"), NULL, ngx_http_variable_body_bytes_sent,
+ 0, 0, 0 },
+
+ { ngx_string("pipe"), NULL, ngx_http_variable_pipe,
+ 0, 0, 0 },
+
+ { ngx_string("request_completion"), NULL,
+ ngx_http_variable_request_completion,
+ 0, 0, 0 },
+
+ { ngx_string("request_body"), NULL,
+ ngx_http_variable_request_body,
+ 0, 0, 0 },
+
+ { ngx_string("request_body_file"), NULL,
+ ngx_http_variable_request_body_file,
+ 0, 0, 0 },
+
+ { ngx_string("request_length"), NULL, ngx_http_variable_request_length,
+ 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("request_time"), NULL, ngx_http_variable_request_time,
+ 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("request_id"), NULL,
+ ngx_http_variable_request_id,
+ 0, 0, 0 },
+
+ { ngx_string("status"), NULL,
+ ngx_http_variable_status, 0,
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("sent_http_content_type"), NULL,
+ ngx_http_variable_sent_content_type, 0, 0, 0 },
+
+ { ngx_string("sent_http_content_length"), NULL,
+ ngx_http_variable_sent_content_length, 0, 0, 0 },
+
+ { ngx_string("sent_http_location"), NULL,
+ ngx_http_variable_sent_location, 0, 0, 0 },
+
+ { ngx_string("sent_http_last_modified"), NULL,
+ ngx_http_variable_sent_last_modified, 0, 0, 0 },
+
+ { ngx_string("sent_http_connection"), NULL,
+ ngx_http_variable_sent_connection, 0, 0, 0 },
+
+ { ngx_string("sent_http_keep_alive"), NULL,
+ ngx_http_variable_sent_keep_alive, 0, 0, 0 },
+
+ { ngx_string("sent_http_transfer_encoding"), NULL,
+ ngx_http_variable_sent_transfer_encoding, 0, 0, 0 },
+
+ { ngx_string("sent_http_cache_control"), NULL, ngx_http_variable_headers,
+ offsetof(ngx_http_request_t, headers_out.cache_control), 0, 0 },
+
+ { ngx_string("sent_http_link"), NULL, ngx_http_variable_headers,
+ offsetof(ngx_http_request_t, headers_out.link), 0, 0 },
+
+ { ngx_string("limit_rate"), ngx_http_variable_set_limit_rate,
+ ngx_http_variable_request_get_size,
+ offsetof(ngx_http_request_t, limit_rate),
+ NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("connection"), NULL,
+ ngx_http_variable_connection, 0, 0, 0 },
+
+ { ngx_string("connection_requests"), NULL,
+ ngx_http_variable_connection_requests, 0, 0, 0 },
+
+ { ngx_string("nginx_version"), NULL, ngx_http_variable_nginx_version,
+ 0, 0, 0 },
+
+ { ngx_string("hostname"), NULL, ngx_http_variable_hostname,
+ 0, 0, 0 },
+
+ { ngx_string("pid"), NULL, ngx_http_variable_pid,
+ 0, 0, 0 },
+
+ { ngx_string("msec"), NULL, ngx_http_variable_msec,
+ 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("time_iso8601"), NULL, ngx_http_variable_time_iso8601,
+ 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("time_local"), NULL, ngx_http_variable_time_local,
+ 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+#if (NGX_HAVE_TCP_INFO)
+ { ngx_string("tcpinfo_rtt"), NULL, ngx_http_variable_tcpinfo,
+ 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("tcpinfo_rttvar"), NULL, ngx_http_variable_tcpinfo,
+ 1, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("tcpinfo_snd_cwnd"), NULL, ngx_http_variable_tcpinfo,
+ 2, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("tcpinfo_rcv_space"), NULL, ngx_http_variable_tcpinfo,
+ 3, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+#endif
+
+ { ngx_string("http_"), NULL, ngx_http_variable_unknown_header_in,
+ 0, NGX_HTTP_VAR_PREFIX, 0 },
+
+ { ngx_string("sent_http_"), NULL, ngx_http_variable_unknown_header_out,
+ 0, NGX_HTTP_VAR_PREFIX, 0 },
+
+ { ngx_string("sent_trailer_"), NULL, ngx_http_variable_unknown_trailer_out,
+ 0, NGX_HTTP_VAR_PREFIX, 0 },
+
+ { ngx_string("cookie_"), NULL, ngx_http_variable_cookie,
+ 0, NGX_HTTP_VAR_PREFIX, 0 },
+
+ { ngx_string("arg_"), NULL, ngx_http_variable_argument,
+ 0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_PREFIX, 0 },
+
+ ngx_http_null_variable
+};
+
+
+ngx_http_variable_value_t ngx_http_variable_null_value =
+ ngx_http_variable("");
+ngx_http_variable_value_t ngx_http_variable_true_value =
+ ngx_http_variable("1");
+
+
+static ngx_uint_t ngx_http_variable_depth = 100;
+
+
+ngx_http_variable_t *
+ngx_http_add_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags)
+{
+ ngx_int_t rc;
+ ngx_uint_t i;
+ ngx_hash_key_t *key;
+ ngx_http_variable_t *v;
+ ngx_http_core_main_conf_t *cmcf;
+
+ if (name->len == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid variable name \"$\"");
+ return NULL;
+ }
+
+ if (flags & NGX_HTTP_VAR_PREFIX) {
+ return ngx_http_add_prefix_variable(cf, name, flags);
+ }
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ key = cmcf->variables_keys->keys.elts;
+ for (i = 0; i < cmcf->variables_keys->keys.nelts; i++) {
+ if (name->len != key[i].key.len
+ || ngx_strncasecmp(name->data, key[i].key.data, name->len) != 0)
+ {
+ continue;
+ }
+
+ v = key[i].value;
+
+ if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the duplicate \"%V\" variable", name);
+ return NULL;
+ }
+
+ if (!(flags & NGX_HTTP_VAR_WEAK)) {
+ v->flags &= ~NGX_HTTP_VAR_WEAK;
+ }
+
+ return v;
+ }
+
+ v = ngx_palloc(cf->pool, sizeof(ngx_http_variable_t));
+ if (v == NULL) {
+ return NULL;
+ }
+
+ v->name.len = name->len;
+ v->name.data = ngx_pnalloc(cf->pool, name->len);
+ if (v->name.data == NULL) {
+ return NULL;
+ }
+
+ ngx_strlow(v->name.data, name->data, name->len);
+
+ v->set_handler = NULL;
+ v->get_handler = NULL;
+ v->data = 0;
+ v->flags = flags;
+ v->index = 0;
+
+ rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v, 0);
+
+ if (rc == NGX_ERROR) {
+ return NULL;
+ }
+
+ if (rc == NGX_BUSY) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "conflicting variable name \"%V\"", name);
+ return NULL;
+ }
+
+ return v;
+}
+
+
+static ngx_http_variable_t *
+ngx_http_add_prefix_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags)
+{
+ ngx_uint_t i;
+ ngx_http_variable_t *v;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ v = cmcf->prefix_variables.elts;
+ for (i = 0; i < cmcf->prefix_variables.nelts; i++) {
+ if (name->len != v[i].name.len
+ || ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0)
+ {
+ continue;
+ }
+
+ v = &v[i];
+
+ if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the duplicate \"%V\" variable", name);
+ return NULL;
+ }
+
+ if (!(flags & NGX_HTTP_VAR_WEAK)) {
+ v->flags &= ~NGX_HTTP_VAR_WEAK;
+ }
+
+ return v;
+ }
+
+ v = ngx_array_push(&cmcf->prefix_variables);
+ if (v == NULL) {
+ return NULL;
+ }
+
+ v->name.len = name->len;
+ v->name.data = ngx_pnalloc(cf->pool, name->len);
+ if (v->name.data == NULL) {
+ return NULL;
+ }
+
+ ngx_strlow(v->name.data, name->data, name->len);
+
+ v->set_handler = NULL;
+ v->get_handler = NULL;
+ v->data = 0;
+ v->flags = flags;
+ v->index = 0;
+
+ return v;
+}
+
+
+ngx_int_t
+ngx_http_get_variable_index(ngx_conf_t *cf, ngx_str_t *name)
+{
+ ngx_uint_t i;
+ ngx_http_variable_t *v;
+ ngx_http_core_main_conf_t *cmcf;
+
+ if (name->len == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid variable name \"$\"");
+ return NGX_ERROR;
+ }
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ v = cmcf->variables.elts;
+
+ if (v == NULL) {
+ if (ngx_array_init(&cmcf->variables, cf->pool, 4,
+ sizeof(ngx_http_variable_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ } else {
+ for (i = 0; i < cmcf->variables.nelts; i++) {
+ if (name->len != v[i].name.len
+ || ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0)
+ {
+ continue;
+ }
+
+ return i;
+ }
+ }
+
+ v = ngx_array_push(&cmcf->variables);
+ if (v == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->name.len = name->len;
+ v->name.data = ngx_pnalloc(cf->pool, name->len);
+ if (v->name.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_strlow(v->name.data, name->data, name->len);
+
+ v->set_handler = NULL;
+ v->get_handler = NULL;
+ v->data = 0;
+ v->flags = 0;
+ v->index = cmcf->variables.nelts - 1;
+
+ return v->index;
+}
+
+
+ngx_http_variable_value_t *
+ngx_http_get_indexed_variable(ngx_http_request_t *r, ngx_uint_t index)
+{
+ ngx_http_variable_t *v;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+ if (cmcf->variables.nelts <= index) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "unknown variable index: %ui", index);
+ return NULL;
+ }
+
+ if (r->variables[index].not_found || r->variables[index].valid) {
+ return &r->variables[index];
+ }
+
+ v = cmcf->variables.elts;
+
+ if (ngx_http_variable_depth == 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "cycle while evaluating variable \"%V\"",
+ &v[index].name);
+ return NULL;
+ }
+
+ ngx_http_variable_depth--;
+
+ if (v[index].get_handler(r, &r->variables[index], v[index].data)
+ == NGX_OK)
+ {
+ ngx_http_variable_depth++;
+
+ if (v[index].flags & NGX_HTTP_VAR_NOCACHEABLE) {
+ r->variables[index].no_cacheable = 1;
+ }
+
+ return &r->variables[index];
+ }
+
+ ngx_http_variable_depth++;
+
+ r->variables[index].valid = 0;
+ r->variables[index].not_found = 1;
+
+ return NULL;
+}
+
+
+ngx_http_variable_value_t *
+ngx_http_get_flushed_variable(ngx_http_request_t *r, ngx_uint_t index)
+{
+ ngx_http_variable_value_t *v;
+
+ v = &r->variables[index];
+
+ if (v->valid || v->not_found) {
+ if (!v->no_cacheable) {
+ return v;
+ }
+
+ v->valid = 0;
+ v->not_found = 0;
+ }
+
+ return ngx_http_get_indexed_variable(r, index);
+}
+
+
+ngx_http_variable_value_t *
+ngx_http_get_variable(ngx_http_request_t *r, ngx_str_t *name, ngx_uint_t key)
+{
+ size_t len;
+ ngx_uint_t i, n;
+ ngx_http_variable_t *v;
+ ngx_http_variable_value_t *vv;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+ v = ngx_hash_find(&cmcf->variables_hash, key, name->data, name->len);
+
+ if (v) {
+ if (v->flags & NGX_HTTP_VAR_INDEXED) {
+ return ngx_http_get_flushed_variable(r, v->index);
+ }
+
+ if (ngx_http_variable_depth == 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "cycle while evaluating variable \"%V\"", name);
+ return NULL;
+ }
+
+ ngx_http_variable_depth--;
+
+ vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
+
+ if (vv && v->get_handler(r, vv, v->data) == NGX_OK) {
+ ngx_http_variable_depth++;
+ return vv;
+ }
+
+ ngx_http_variable_depth++;
+ return NULL;
+ }
+
+ vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
+ if (vv == NULL) {
+ return NULL;
+ }
+
+ len = 0;
+
+ v = cmcf->prefix_variables.elts;
+ n = cmcf->prefix_variables.nelts;
+
+ for (i = 0; i < cmcf->prefix_variables.nelts; i++) {
+ if (name->len >= v[i].name.len && name->len > len
+ && ngx_strncmp(name->data, v[i].name.data, v[i].name.len) == 0)
+ {
+ len = v[i].name.len;
+ n = i;
+ }
+ }
+
+ if (n != cmcf->prefix_variables.nelts) {
+ if (v[n].get_handler(r, vv, (uintptr_t) name) == NGX_OK) {
+ return vv;
+ }
+
+ return NULL;
+ }
+
+ vv->not_found = 1;
+
+ return vv;
+}
+
+
+static ngx_int_t
+ngx_http_variable_request(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ ngx_str_t *s;
+
+ s = (ngx_str_t *) ((char *) r + data);
+
+ if (s->data) {
+ v->len = s->len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = s->data;
+
+ } else {
+ v->not_found = 1;
+ }
+
+ return NGX_OK;
+}
+
+
+#if 0
+
+static void
+ngx_http_variable_request_set(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_str_t *s;
+
+ s = (ngx_str_t *) ((char *) r + data);
+
+ s->len = v->len;
+ s->data = v->data;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_variable_request_get_size(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ size_t *sp;
+
+ sp = (size_t *) ((char *) r + data);
+
+ v->data = ngx_pnalloc(r->pool, NGX_SIZE_T_LEN);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(v->data, "%uz", *sp) - v->data;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_header(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ ngx_table_elt_t *h;
+
+ h = *(ngx_table_elt_t **) ((char *) r + data);
+
+ if (h) {
+ v->len = h->value.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = h->value.data;
+
+ } else {
+ v->not_found = 1;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_cookies(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ return ngx_http_variable_headers_internal(r, v, data, ';');
+}
+
+
+static ngx_int_t
+ngx_http_variable_headers(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ return ngx_http_variable_headers_internal(r, v, data, ',');
+}
+
+
+static ngx_int_t
+ngx_http_variable_headers_internal(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data, u_char sep)
+{
+ size_t len;
+ u_char *p, *end;
+ ngx_uint_t i, n;
+ ngx_array_t *a;
+ ngx_table_elt_t **h;
+
+ a = (ngx_array_t *) ((char *) r + data);
+
+ n = a->nelts;
+ h = a->elts;
+
+ len = 0;
+
+ for (i = 0; i < n; i++) {
+
+ if (h[i]->hash == 0) {
+ continue;
+ }
+
+ len += h[i]->value.len + 2;
+ }
+
+ if (len == 0) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ len -= 2;
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ if (n == 1) {
+ v->len = (*h)->value.len;
+ v->data = (*h)->value.data;
+
+ return NGX_OK;
+ }
+
+ p = ngx_pnalloc(r->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = len;
+ v->data = p;
+
+ end = p + len;
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (h[i]->hash == 0) {
+ continue;
+ }
+
+ p = ngx_copy(p, h[i]->value.data, h[i]->value.len);
+
+ if (p == end) {
+ break;
+ }
+
+ *p++ = sep; *p++ = ' ';
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_unknown_header_in(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,
+ &r->headers_in.headers.part,
+ sizeof("http_") - 1);
+}
+
+
+static ngx_int_t
+ngx_http_variable_unknown_header_out(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,
+ &r->headers_out.headers.part,
+ sizeof("sent_http_") - 1);
+}
+
+
+static ngx_int_t
+ngx_http_variable_unknown_trailer_out(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,
+ &r->headers_out.trailers.part,
+ sizeof("sent_trailer_") - 1);
+}
+
+
+ngx_int_t
+ngx_http_variable_unknown_header(ngx_http_variable_value_t *v, ngx_str_t *var,
+ ngx_list_part_t *part, size_t prefix)
+{
+ u_char ch;
+ ngx_uint_t i, n;
+ ngx_table_elt_t *header;
+
+ header = part->elts;
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ if (header[i].hash == 0) {
+ continue;
+ }
+
+ for (n = 0; n + prefix < var->len && n < header[i].key.len; n++) {
+ ch = header[i].key.data[n];
+
+ if (ch >= 'A' && ch <= 'Z') {
+ ch |= 0x20;
+
+ } else if (ch == '-') {
+ ch = '_';
+ }
+
+ if (var->data[n + prefix] != ch) {
+ break;
+ }
+ }
+
+ if (n + prefix == var->len && n == header[i].key.len) {
+ v->len = header[i].value.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = header[i].value.data;
+
+ return NGX_OK;
+ }
+ }
+
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_request_line(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p, *s;
+
+ s = r->request_line.data;
+
+ if (s == NULL) {
+ s = r->request_start;
+
+ if (s == NULL) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ for (p = s; p < r->header_in->last; p++) {
+ if (*p == CR || *p == LF) {
+ break;
+ }
+ }
+
+ r->request_line.len = p - s;
+ r->request_line.data = s;
+ }
+
+ v->len = r->request_line.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = s;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_cookie(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ ngx_str_t *name = (ngx_str_t *) data;
+
+ ngx_str_t cookie, s;
+
+ s.len = name->len - (sizeof("cookie_") - 1);
+ s.data = name->data + sizeof("cookie_") - 1;
+
+ if (ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &s, &cookie)
+ == NGX_DECLINED)
+ {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ v->len = cookie.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = cookie.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_argument(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ ngx_str_t *name = (ngx_str_t *) data;
+
+ u_char *arg;
+ size_t len;
+ ngx_str_t value;
+
+ len = name->len - (sizeof("arg_") - 1);
+ arg = name->data + sizeof("arg_") - 1;
+
+ if (ngx_http_arg(r, arg, len, &value) != NGX_OK) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ v->data = value.data;
+ v->len = value.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ return NGX_OK;
+}
+
+
+#if (NGX_HAVE_TCP_INFO)
+
+static ngx_int_t
+ngx_http_variable_tcpinfo(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ struct tcp_info ti;
+ socklen_t len;
+ uint32_t value;
+
+ len = sizeof(struct tcp_info);
+ if (getsockopt(r->connection->fd, IPPROTO_TCP, TCP_INFO, &ti, &len) == -1) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ v->data = ngx_pnalloc(r->pool, NGX_INT32_LEN);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ switch (data) {
+ case 0:
+ value = ti.tcpi_rtt;
+ break;
+
+ case 1:
+ value = ti.tcpi_rttvar;
+ break;
+
+ case 2:
+ value = ti.tcpi_snd_cwnd;
+ break;
+
+ case 3:
+ value = ti.tcpi_rcv_space;
+ break;
+
+ /* suppress warning */
+ default:
+ value = 0;
+ break;
+ }
+
+ v->len = ngx_sprintf(v->data, "%uD", value) - v->data;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_variable_content_length(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ if (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->not_found = 0;
+
+ } else if (r->reading_body) {
+ v->not_found = 1;
+ v->no_cacheable = 1;
+
+ } else if (r->headers_in.content_length_n >= 0) {
+ p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(p, "%O", r->headers_in.content_length_n) - p;
+ v->data = p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ } else {
+ v->not_found = 1;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ ngx_http_core_srv_conf_t *cscf;
+
+ if (r->headers_in.server.len) {
+ v->len = r->headers_in.server.len;
+ v->data = r->headers_in.server.data;
+
+ } else {
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+ v->len = cscf->server_name.len;
+ v->data = cscf->server_name.data;
+ }
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_binary_remote_addr(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ struct sockaddr_in *sin;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+
+ switch (r->connection->sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
+
+ v->len = sizeof(struct in6_addr);
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = sin6->sin6_addr.s6_addr;
+
+ break;
+#endif
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+ case AF_UNIX:
+
+ v->len = r->connection->addr_text.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = r->connection->addr_text.data;
+
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) r->connection->sockaddr;
+
+ v->len = sizeof(in_addr_t);
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) &sin->sin_addr;
+
+ break;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_remote_addr(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ v->len = r->connection->addr_text.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = r->connection->addr_text.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_remote_port(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_uint_t port;
+
+ v->len = 0;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ port = ngx_inet_get_port(r->connection->sockaddr);
+
+ if (port > 0 && port < 65536) {
+ v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_proxy_protocol_addr(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_str_t *addr;
+ ngx_proxy_protocol_t *pp;
+
+ pp = r->connection->proxy_protocol;
+ if (pp == NULL) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ addr = (ngx_str_t *) ((char *) pp + data);
+
+ v->len = addr->len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = addr->data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_proxy_protocol_port(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_uint_t port;
+ ngx_proxy_protocol_t *pp;
+
+ pp = r->connection->proxy_protocol;
+ if (pp == NULL) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ v->len = 0;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ port = *(in_port_t *) ((char *) pp + data);
+
+ if (port > 0 && port < 65536) {
+ v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_server_addr(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_str_t s;
+ u_char addr[NGX_SOCKADDR_STRLEN];
+
+ s.len = NGX_SOCKADDR_STRLEN;
+ s.data = addr;
+
+ if (ngx_connection_local_sockaddr(r->connection, &s, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ s.data = ngx_pnalloc(r->pool, s.len);
+ if (s.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s.data, addr, s.len);
+
+ v->len = s.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = s.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_server_port(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_uint_t port;
+
+ v->len = 0;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ if (ngx_connection_local_sockaddr(r->connection, NULL, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ port = ngx_inet_get_port(r->connection->local_sockaddr);
+
+ if (port > 0 && port < 65536) {
+ v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_scheme(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+#if (NGX_HTTP_SSL)
+
+ if (r->connection->ssl) {
+ v->len = sizeof("https") - 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) "https";
+
+ return NGX_OK;
+ }
+
+#endif
+
+ v->len = sizeof("http") - 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) "http";
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_https(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+#if (NGX_HTTP_SSL)
+
+ if (r->connection->ssl) {
+ v->len = sizeof("on") - 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) "on";
+
+ return NGX_OK;
+ }
+
+#endif
+
+ *v = ngx_http_variable_null_value;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_variable_set_args(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ r->args.len = v->len;
+ r->args.data = v->data;
+ r->valid_unparsed_uri = 0;
+}
+
+
+static ngx_int_t
+ngx_http_variable_is_args(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ if (r->args.len == 0) {
+ *v = ngx_http_variable_null_value;
+ return NGX_OK;
+ }
+
+ v->len = 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) "?";
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_document_root(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_str_t path;
+ ngx_http_core_loc_conf_t *clcf;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->root_lengths == NULL) {
+ v->len = clcf->root.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = clcf->root.data;
+
+ } else {
+ if (ngx_http_script_run(r, &path, clcf->root_lengths->elts, 0,
+ clcf->root_values->elts)
+ == NULL)
+ {
+ return NGX_ERROR;
+ }
+
+ if (ngx_get_full_name(r->pool, (ngx_str_t *) &ngx_cycle->prefix, &path)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ v->len = path.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = path.data;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_realpath_root(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *real;
+ size_t len;
+ ngx_str_t path;
+ ngx_http_core_loc_conf_t *clcf;
+#if (NGX_HAVE_MAX_PATH)
+ u_char buffer[NGX_MAX_PATH];
+#endif
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->root_lengths == NULL) {
+ path = clcf->root;
+
+ } else {
+ if (ngx_http_script_run(r, &path, clcf->root_lengths->elts, 1,
+ clcf->root_values->elts)
+ == NULL)
+ {
+ return NGX_ERROR;
+ }
+
+ path.data[path.len - 1] = '\0';
+
+ if (ngx_get_full_name(r->pool, (ngx_str_t *) &ngx_cycle->prefix, &path)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+#if (NGX_HAVE_MAX_PATH)
+ real = buffer;
+#else
+ real = NULL;
+#endif
+
+ real = ngx_realpath(path.data, real);
+
+ if (real == NULL) {
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+ ngx_realpath_n " \"%s\" failed", path.data);
+ return NGX_ERROR;
+ }
+
+ len = ngx_strlen(real);
+
+ v->data = ngx_pnalloc(r->pool, len);
+ if (v->data == NULL) {
+#if !(NGX_HAVE_MAX_PATH)
+ ngx_free(real);
+#endif
+ return NGX_ERROR;
+ }
+
+ v->len = len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ ngx_memcpy(v->data, real, len);
+
+#if !(NGX_HAVE_MAX_PATH)
+ ngx_free(real);
+#endif
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_request_filename(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ size_t root;
+ ngx_str_t path;
+
+ if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
+ return NGX_ERROR;
+ }
+
+ /* ngx_http_map_uri_to_path() allocates memory for terminating '\0' */
+
+ v->len = path.len - 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = path.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_server_name(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_http_core_srv_conf_t *cscf;
+
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+ v->len = cscf->server_name.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = cscf->server_name.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_request_method(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ if (r->main->method_name.data) {
+ v->len = r->main->method_name.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = r->main->method_name.data;
+
+ } else {
+ v->not_found = 1;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_remote_user(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_int_t rc;
+
+ rc = ngx_http_auth_basic_user(r);
+
+ if (rc == NGX_DECLINED) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ v->len = r->headers_in.user.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = r->headers_in.user.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_bytes_sent(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(p, "%O", r->connection->sent) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_body_bytes_sent(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ off_t sent;
+ u_char *p;
+
+ sent = r->connection->sent - r->header_size;
+
+ if (sent < 0) {
+ sent = 0;
+ }
+
+ p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(p, "%O", sent) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_pipe(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ v->data = (u_char *) (r->pipeline ? "p" : ".");
+ v->len = 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_status(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_uint_t status;
+
+ v->data = ngx_pnalloc(r->pool, NGX_INT_T_LEN);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (r->err_status) {
+ status = r->err_status;
+
+ } else if (r->headers_out.status) {
+ status = r->headers_out.status;
+
+ } else if (r->http_version == NGX_HTTP_VERSION_9) {
+ status = 9;
+
+ } else {
+ status = 0;
+ }
+
+ v->len = ngx_sprintf(v->data, "%03ui", status) - v->data;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_sent_content_type(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ if (r->headers_out.content_type.len) {
+ v->len = r->headers_out.content_type.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = r->headers_out.content_type.data;
+
+ } else {
+ v->not_found = 1;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_sent_content_length(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ if (r->headers_out.content_length) {
+ v->len = r->headers_out.content_length->value.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = r->headers_out.content_length->value.data;
+
+ return NGX_OK;
+ }
+
+ if (r->headers_out.content_length_n >= 0) {
+ p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(p, "%O", r->headers_out.content_length_n) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+ }
+
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_sent_location(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_str_t name;
+
+ if (r->headers_out.location) {
+ v->len = r->headers_out.location->value.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = r->headers_out.location->value.data;
+
+ return NGX_OK;
+ }
+
+ ngx_str_set(&name, "sent_http_location");
+
+ return ngx_http_variable_unknown_header(v, &name,
+ &r->headers_out.headers.part,
+ sizeof("sent_http_") - 1);
+}
+
+
+static ngx_int_t
+ngx_http_variable_sent_last_modified(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ if (r->headers_out.last_modified) {
+ v->len = r->headers_out.last_modified->value.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = r->headers_out.last_modified->value.data;
+
+ return NGX_OK;
+ }
+
+ if (r->headers_out.last_modified_time >= 0) {
+ p = ngx_pnalloc(r->pool, sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_http_time(p, r->headers_out.last_modified_time) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+ }
+
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_sent_connection(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ size_t len;
+ char *p;
+
+ if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
+ len = sizeof("upgrade") - 1;
+ p = "upgrade";
+
+ } else if (r->keepalive) {
+ len = sizeof("keep-alive") - 1;
+ p = "keep-alive";
+
+ } else {
+ len = sizeof("close") - 1;
+ p = "close";
+ }
+
+ v->len = len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_sent_keep_alive(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+ ngx_http_core_loc_conf_t *clcf;
+
+ if (r->keepalive) {
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->keepalive_header) {
+
+ p = ngx_pnalloc(r->pool, sizeof("timeout=") - 1 + NGX_TIME_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(p, "timeout=%T", clcf->keepalive_header) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+ }
+ }
+
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_sent_transfer_encoding(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ if (r->chunked) {
+ v->len = sizeof("chunked") - 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) "chunked";
+
+ } else {
+ v->not_found = 1;
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_variable_set_limit_rate(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ssize_t s;
+ ngx_str_t val;
+
+ val.len = v->len;
+ val.data = v->data;
+
+ s = ngx_parse_size(&val);
+
+ if (s == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "invalid $limit_rate \"%V\"", &val);
+ return;
+ }
+
+ r->limit_rate = s;
+ r->limit_rate_set = 1;
+}
+
+
+static ngx_int_t
+ngx_http_variable_request_completion(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ if (r->request_complete) {
+ v->len = 2;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) "OK";
+
+ return NGX_OK;
+ }
+
+ *v = ngx_http_variable_null_value;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_request_body(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+ size_t len;
+ ngx_buf_t *buf;
+ ngx_chain_t *cl;
+
+ if (r->request_body == NULL
+ || r->request_body->bufs == NULL
+ || r->request_body->temp_file)
+ {
+ v->not_found = 1;
+
+ return NGX_OK;
+ }
+
+ cl = r->request_body->bufs;
+ buf = cl->buf;
+
+ if (cl->next == NULL) {
+ v->len = buf->last - buf->pos;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = buf->pos;
+
+ return NGX_OK;
+ }
+
+ len = buf->last - buf->pos;
+ cl = cl->next;
+
+ for ( /* void */ ; cl; cl = cl->next) {
+ buf = cl->buf;
+ len += buf->last - buf->pos;
+ }
+
+ p = ngx_pnalloc(r->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->data = p;
+ cl = r->request_body->bufs;
+
+ for ( /* void */ ; cl; cl = cl->next) {
+ buf = cl->buf;
+ p = ngx_cpymem(p, buf->pos, buf->last - buf->pos);
+ }
+
+ v->len = len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_request_body_file(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ if (r->request_body == NULL || r->request_body->temp_file == NULL) {
+ v->not_found = 1;
+
+ return NGX_OK;
+ }
+
+ v->len = r->request_body->temp_file->file.name.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = r->request_body->temp_file->file.name.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_request_length(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(p, "%O", r->request_length) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_request_time(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+ ngx_time_t *tp;
+ ngx_msec_int_t ms;
+
+ p = ngx_pnalloc(r->pool, NGX_TIME_T_LEN + 4);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ tp = ngx_timeofday();
+
+ ms = (ngx_msec_int_t)
+ ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec));
+ ms = ngx_max(ms, 0);
+
+ v->len = ngx_sprintf(p, "%T.%03M", (time_t) ms / 1000, ms % 1000) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_request_id(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *id;
+
+#if (NGX_OPENSSL)
+ u_char random_bytes[16];
+#endif
+
+ id = ngx_pnalloc(r->pool, 32);
+ if (id == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ v->len = 32;
+ v->data = id;
+
+#if (NGX_OPENSSL)
+
+ if (RAND_bytes(random_bytes, 16) == 1) {
+ ngx_hex_dump(id, random_bytes, 16);
+ return NGX_OK;
+ }
+
+ ngx_ssl_error(NGX_LOG_ERR, r->connection->log, 0, "RAND_bytes() failed");
+
+#endif
+
+ ngx_sprintf(id, "%08xD%08xD%08xD%08xD",
+ (uint32_t) ngx_random(), (uint32_t) ngx_random(),
+ (uint32_t) ngx_random(), (uint32_t) ngx_random());
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_connection(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ p = ngx_pnalloc(r->pool, NGX_ATOMIC_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(p, "%uA", r->connection->number) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_connection_requests(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ p = ngx_pnalloc(r->pool, NGX_INT_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(p, "%ui", r->connection->requests) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_nginx_version(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ v->len = sizeof(NGINX_VERSION) - 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) NGINX_VERSION;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_hostname(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ v->len = ngx_cycle->hostname.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = ngx_cycle->hostname.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_pid(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ p = ngx_pnalloc(r->pool, NGX_INT64_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(p, "%P", ngx_pid) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_msec(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+ ngx_time_t *tp;
+
+ p = ngx_pnalloc(r->pool, NGX_TIME_T_LEN + 4);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ tp = ngx_timeofday();
+
+ v->len = ngx_sprintf(p, "%T.%03M", tp->sec, tp->msec) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_time_iso8601(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ p = ngx_pnalloc(r->pool, ngx_cached_http_log_iso8601.len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(p, ngx_cached_http_log_iso8601.data,
+ ngx_cached_http_log_iso8601.len);
+
+ v->len = ngx_cached_http_log_iso8601.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_time_local(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ p = ngx_pnalloc(r->pool, ngx_cached_http_log_time.len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(p, ngx_cached_http_log_time.data, ngx_cached_http_log_time.len);
+
+ v->len = ngx_cached_http_log_time.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+void *
+ngx_http_map_find(ngx_http_request_t *r, ngx_http_map_t *map, ngx_str_t *match)
+{
+ void *value;
+ u_char *low;
+ size_t len;
+ ngx_uint_t key;
+
+ len = match->len;
+
+ if (len) {
+ low = ngx_pnalloc(r->pool, len);
+ if (low == NULL) {
+ return NULL;
+ }
+
+ } else {
+ low = NULL;
+ }
+
+ key = ngx_hash_strlow(low, match->data, len);
+
+ value = ngx_hash_find_combined(&map->hash, key, low, len);
+ if (value) {
+ return value;
+ }
+
+#if (NGX_PCRE)
+
+ if (len && map->nregex) {
+ ngx_int_t n;
+ ngx_uint_t i;
+ ngx_http_map_regex_t *reg;
+
+ reg = map->regex;
+
+ for (i = 0; i < map->nregex; i++) {
+
+ n = ngx_http_regex_exec(r, reg[i].regex, match);
+
+ if (n == NGX_OK) {
+ return reg[i].value;
+ }
+
+ if (n == NGX_DECLINED) {
+ continue;
+ }
+
+ /* NGX_ERROR */
+
+ return NULL;
+ }
+ }
+
+#endif
+
+ return NULL;
+}
+
+
+#if (NGX_PCRE)
+
+static ngx_int_t
+ngx_http_variable_not_found(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ v->not_found = 1;
+ return NGX_OK;
+}
+
+
+ngx_http_regex_t *
+ngx_http_regex_compile(ngx_conf_t *cf, ngx_regex_compile_t *rc)
+{
+ u_char *p;
+ size_t size;
+ ngx_str_t name;
+ ngx_uint_t i, n;
+ ngx_http_variable_t *v;
+ ngx_http_regex_t *re;
+ ngx_http_regex_variable_t *rv;
+ ngx_http_core_main_conf_t *cmcf;
+
+ rc->pool = cf->pool;
+
+ if (ngx_regex_compile(rc) != NGX_OK) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc->err);
+ return NULL;
+ }
+
+ re = ngx_pcalloc(cf->pool, sizeof(ngx_http_regex_t));
+ if (re == NULL) {
+ return NULL;
+ }
+
+ re->regex = rc->regex;
+ re->ncaptures = rc->captures;
+ re->name = rc->pattern;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+ cmcf->ncaptures = ngx_max(cmcf->ncaptures, re->ncaptures);
+
+ n = (ngx_uint_t) rc->named_captures;
+
+ if (n == 0) {
+ return re;
+ }
+
+ rv = ngx_palloc(rc->pool, n * sizeof(ngx_http_regex_variable_t));
+ if (rv == NULL) {
+ return NULL;
+ }
+
+ re->variables = rv;
+ re->nvariables = n;
+
+ size = rc->name_size;
+ p = rc->names;
+
+ for (i = 0; i < n; i++) {
+ rv[i].capture = 2 * ((p[0] << 8) + p[1]);
+
+ name.data = &p[2];
+ name.len = ngx_strlen(name.data);
+
+ v = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
+ if (v == NULL) {
+ return NULL;
+ }
+
+ rv[i].index = ngx_http_get_variable_index(cf, &name);
+ if (rv[i].index == NGX_ERROR) {
+ return NULL;
+ }
+
+ v->get_handler = ngx_http_variable_not_found;
+
+ p += size;
+ }
+
+ return re;
+}
+
+
+ngx_int_t
+ngx_http_regex_exec(ngx_http_request_t *r, ngx_http_regex_t *re, ngx_str_t *s)
+{
+ ngx_int_t rc, index;
+ ngx_uint_t i, n, len;
+ ngx_http_variable_value_t *vv;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+ if (re->ncaptures) {
+ len = cmcf->ncaptures;
+
+ if (r->captures == NULL || r->realloc_captures) {
+ r->realloc_captures = 0;
+
+ r->captures = ngx_palloc(r->pool, len * sizeof(int));
+ if (r->captures == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ } else {
+ len = 0;
+ }
+
+ rc = ngx_regex_exec(re->regex, s, r->captures, len);
+
+ if (rc == NGX_REGEX_NO_MATCHED) {
+ return NGX_DECLINED;
+ }
+
+ if (rc < 0) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"",
+ rc, s, &re->name);
+ return NGX_ERROR;
+ }
+
+ for (i = 0; i < re->nvariables; i++) {
+
+ n = re->variables[i].capture;
+ index = re->variables[i].index;
+ vv = &r->variables[index];
+
+ vv->len = r->captures[n + 1] - r->captures[n];
+ vv->valid = 1;
+ vv->no_cacheable = 0;
+ vv->not_found = 0;
+ vv->data = &s->data[r->captures[n]];
+
+#if (NGX_DEBUG)
+ {
+ ngx_http_variable_t *v;
+
+ v = cmcf->variables.elts;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http regex set $%V to \"%v\"", &v[index].name, vv);
+ }
+#endif
+ }
+
+ r->ncaptures = rc * 2;
+ r->captures_data = s->data;
+
+ return NGX_OK;
+}
+
+#endif
+
+
+ngx_int_t
+ngx_http_variables_add_core_vars(ngx_conf_t *cf)
+{
+ ngx_http_variable_t *cv, *v;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ cmcf->variables_keys = ngx_pcalloc(cf->temp_pool,
+ sizeof(ngx_hash_keys_arrays_t));
+ if (cmcf->variables_keys == NULL) {
+ return NGX_ERROR;
+ }
+
+ cmcf->variables_keys->pool = cf->pool;
+ cmcf->variables_keys->temp_pool = cf->pool;
+
+ if (ngx_hash_keys_array_init(cmcf->variables_keys, NGX_HASH_SMALL)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (ngx_array_init(&cmcf->prefix_variables, cf->pool, 8,
+ sizeof(ngx_http_variable_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ for (cv = ngx_http_core_variables; cv->name.len; cv++) {
+ v = ngx_http_add_variable(cf, &cv->name, cv->flags);
+ if (v == NULL) {
+ return NGX_ERROR;
+ }
+
+ *v = *cv;
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_variables_init_vars(ngx_conf_t *cf)
+{
+ size_t len;
+ ngx_uint_t i, n;
+ ngx_hash_key_t *key;
+ ngx_hash_init_t hash;
+ ngx_http_variable_t *v, *av, *pv;
+ ngx_http_core_main_conf_t *cmcf;
+
+ /* set the handlers for the indexed http variables */
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ v = cmcf->variables.elts;
+ pv = cmcf->prefix_variables.elts;
+ key = cmcf->variables_keys->keys.elts;
+
+ for (i = 0; i < cmcf->variables.nelts; i++) {
+
+ for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) {
+
+ av = key[n].value;
+
+ if (v[i].name.len == key[n].key.len
+ && ngx_strncmp(v[i].name.data, key[n].key.data, v[i].name.len)
+ == 0)
+ {
+ v[i].get_handler = av->get_handler;
+ v[i].data = av->data;
+
+ av->flags |= NGX_HTTP_VAR_INDEXED;
+ v[i].flags = av->flags;
+
+ av->index = i;
+
+ if (av->get_handler == NULL
+ || (av->flags & NGX_HTTP_VAR_WEAK))
+ {
+ break;
+ }
+
+ goto next;
+ }
+ }
+
+ len = 0;
+ av = NULL;
+
+ for (n = 0; n < cmcf->prefix_variables.nelts; n++) {
+ if (v[i].name.len >= pv[n].name.len && v[i].name.len > len
+ && ngx_strncmp(v[i].name.data, pv[n].name.data, pv[n].name.len)
+ == 0)
+ {
+ av = &pv[n];
+ len = pv[n].name.len;
+ }
+ }
+
+ if (av) {
+ v[i].get_handler = av->get_handler;
+ v[i].data = (uintptr_t) &v[i].name;
+ v[i].flags = av->flags;
+
+ goto next;
+ }
+
+ if (v[i].get_handler == NULL) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "unknown \"%V\" variable", &v[i].name);
+
+ return NGX_ERROR;
+ }
+
+ next:
+ continue;
+ }
+
+
+ for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) {
+ av = key[n].value;
+
+ if (av->flags & NGX_HTTP_VAR_NOHASH) {
+ key[n].key.data = NULL;
+ }
+ }
+
+
+ hash.hash = &cmcf->variables_hash;
+ hash.key = ngx_hash_key;
+ hash.max_size = cmcf->variables_hash_max_size;
+ hash.bucket_size = cmcf->variables_hash_bucket_size;
+ hash.name = "variables_hash";
+ hash.pool = cf->pool;
+ hash.temp_pool = NULL;
+
+ if (ngx_hash_init(&hash, cmcf->variables_keys->keys.elts,
+ cmcf->variables_keys->keys.nelts)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ cmcf->variables_keys = NULL;
+
+ return NGX_OK;
+}
diff --git a/src/nginx-1.18.0/src/http/ngx_http_variables.h b/src/nginx-1.18.0/src/http/ngx_http_variables.h
new file mode 100644
index 0000000..f3f7f3c
--- /dev/null
+++ b/src/nginx-1.18.0/src/http/ngx_http_variables.h
@@ -0,0 +1,116 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_HTTP_VARIABLES_H_INCLUDED_
+#define _NGX_HTTP_VARIABLES_H_INCLUDED_
+
+
+#include
+#include
+#include
+
+
+typedef ngx_variable_value_t ngx_http_variable_value_t;
+
+#define ngx_http_variable(v) { sizeof(v) - 1, 1, 0, 0, 0, (u_char *) v }
+
+typedef struct ngx_http_variable_s ngx_http_variable_t;
+
+typedef void (*ngx_http_set_variable_pt) (ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+typedef ngx_int_t (*ngx_http_get_variable_pt) (ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+
+
+#define NGX_HTTP_VAR_CHANGEABLE 1
+#define NGX_HTTP_VAR_NOCACHEABLE 2
+#define NGX_HTTP_VAR_INDEXED 4
+#define NGX_HTTP_VAR_NOHASH 8
+#define NGX_HTTP_VAR_WEAK 16
+#define NGX_HTTP_VAR_PREFIX 32
+
+
+struct ngx_http_variable_s {
+ ngx_str_t name; /* must be first to build the hash */
+ ngx_http_set_variable_pt set_handler;
+ ngx_http_get_variable_pt get_handler;
+ uintptr_t data;
+ ngx_uint_t flags;
+ ngx_uint_t index;
+};
+
+#define ngx_http_null_variable { ngx_null_string, NULL, NULL, 0, 0, 0 }
+
+
+ngx_http_variable_t *ngx_http_add_variable(ngx_conf_t *cf, ngx_str_t *name,
+ ngx_uint_t flags);
+ngx_int_t ngx_http_get_variable_index(ngx_conf_t *cf, ngx_str_t *name);
+ngx_http_variable_value_t *ngx_http_get_indexed_variable(ngx_http_request_t *r,
+ ngx_uint_t index);
+ngx_http_variable_value_t *ngx_http_get_flushed_variable(ngx_http_request_t *r,
+ ngx_uint_t index);
+
+ngx_http_variable_value_t *ngx_http_get_variable(ngx_http_request_t *r,
+ ngx_str_t *name, ngx_uint_t key);
+
+ngx_int_t ngx_http_variable_unknown_header(ngx_http_variable_value_t *v,
+ ngx_str_t *var, ngx_list_part_t *part, size_t prefix);
+
+
+#if (NGX_PCRE)
+
+typedef struct {
+ ngx_uint_t capture;
+ ngx_int_t index;
+} ngx_http_regex_variable_t;
+
+
+typedef struct {
+ ngx_regex_t *regex;
+ ngx_uint_t ncaptures;
+ ngx_http_regex_variable_t *variables;
+ ngx_uint_t nvariables;
+ ngx_str_t name;
+} ngx_http_regex_t;
+
+
+typedef struct {
+ ngx_http_regex_t *regex;
+ void *value;
+} ngx_http_map_regex_t;
+
+
+ngx_http_regex_t *ngx_http_regex_compile(ngx_conf_t *cf,
+ ngx_regex_compile_t *rc);
+ngx_int_t ngx_http_regex_exec(ngx_http_request_t *r, ngx_http_regex_t *re,
+ ngx_str_t *s);
+
+#endif
+
+
+typedef struct {
+ ngx_hash_combined_t hash;
+#if (NGX_PCRE)
+ ngx_http_map_regex_t *regex;
+ ngx_uint_t nregex;
+#endif
+} ngx_http_map_t;
+
+
+void *ngx_http_map_find(ngx_http_request_t *r, ngx_http_map_t *map,
+ ngx_str_t *match);
+
+
+ngx_int_t ngx_http_variables_add_core_vars(ngx_conf_t *cf);
+ngx_int_t ngx_http_variables_init_vars(ngx_conf_t *cf);
+
+
+extern ngx_http_variable_value_t ngx_http_variable_null_value;
+extern ngx_http_variable_value_t ngx_http_variable_true_value;
+
+
+#endif /* _NGX_HTTP_VARIABLES_H_INCLUDED_ */
diff --git a/src/nginx-1.18.0/src/http/ngx_http_write_filter_module.c b/src/nginx-1.18.0/src/http/ngx_http_write_filter_module.c
new file mode 100644
index 0000000..6a5d957
--- /dev/null
+++ b/src/nginx-1.18.0/src/http/ngx_http_write_filter_module.c
@@ -0,0 +1,367 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+#include
+
+
+static ngx_int_t ngx_http_write_filter_init(ngx_conf_t *cf);
+
+
+static ngx_http_module_t ngx_http_write_filter_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_write_filter_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL, /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_write_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_write_filter_module_ctx, /* module context */
+ NULL, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+ngx_int_t
+ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ off_t size, sent, nsent, limit;
+ ngx_uint_t last, flush, sync;
+ ngx_msec_t delay;
+ ngx_chain_t *cl, *ln, **ll, *chain;
+ ngx_connection_t *c;
+ ngx_http_core_loc_conf_t *clcf;
+
+ c = r->connection;
+
+ if (c->error) {
+ return NGX_ERROR;
+ }
+
+ size = 0;
+ flush = 0;
+ sync = 0;
+ last = 0;
+ ll = &r->out;
+
+ /* find the size, the flush point and the last link of the saved chain */
+
+ for (cl = r->out; cl; cl = cl->next) {
+ ll = &cl->next;
+
+ ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "write old buf t:%d f:%d %p, pos %p, size: %z "
+ "file: %O, size: %O",
+ cl->buf->temporary, cl->buf->in_file,
+ cl->buf->start, cl->buf->pos,
+ cl->buf->last - cl->buf->pos,
+ cl->buf->file_pos,
+ cl->buf->file_last - cl->buf->file_pos);
+
+ if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "zero size buf in writer "
+ "t:%d r:%d f:%d %p %p-%p %p %O-%O",
+ cl->buf->temporary,
+ cl->buf->recycled,
+ cl->buf->in_file,
+ cl->buf->start,
+ cl->buf->pos,
+ cl->buf->last,
+ cl->buf->file,
+ cl->buf->file_pos,
+ cl->buf->file_last);
+
+ ngx_debug_point();
+ return NGX_ERROR;
+ }
+
+ if (ngx_buf_size(cl->buf) < 0) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "negative size buf in writer "
+ "t:%d r:%d f:%d %p %p-%p %p %O-%O",
+ cl->buf->temporary,
+ cl->buf->recycled,
+ cl->buf->in_file,
+ cl->buf->start,
+ cl->buf->pos,
+ cl->buf->last,
+ cl->buf->file,
+ cl->buf->file_pos,
+ cl->buf->file_last);
+
+ ngx_debug_point();
+ return NGX_ERROR;
+ }
+
+ size += ngx_buf_size(cl->buf);
+
+ if (cl->buf->flush || cl->buf->recycled) {
+ flush = 1;
+ }
+
+ if (cl->buf->sync) {
+ sync = 1;
+ }
+
+ if (cl->buf->last_buf) {
+ last = 1;
+ }
+ }
+
+ /* add the new chain to the existent one */
+
+ for (ln = in; ln; ln = ln->next) {
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = ln->buf;
+ *ll = cl;
+ ll = &cl->next;
+
+ ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "write new buf t:%d f:%d %p, pos %p, size: %z "
+ "file: %O, size: %O",
+ cl->buf->temporary, cl->buf->in_file,
+ cl->buf->start, cl->buf->pos,
+ cl->buf->last - cl->buf->pos,
+ cl->buf->file_pos,
+ cl->buf->file_last - cl->buf->file_pos);
+
+ if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "zero size buf in writer "
+ "t:%d r:%d f:%d %p %p-%p %p %O-%O",
+ cl->buf->temporary,
+ cl->buf->recycled,
+ cl->buf->in_file,
+ cl->buf->start,
+ cl->buf->pos,
+ cl->buf->last,
+ cl->buf->file,
+ cl->buf->file_pos,
+ cl->buf->file_last);
+
+ ngx_debug_point();
+ return NGX_ERROR;
+ }
+
+ if (ngx_buf_size(cl->buf) < 0) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "negative size buf in writer "
+ "t:%d r:%d f:%d %p %p-%p %p %O-%O",
+ cl->buf->temporary,
+ cl->buf->recycled,
+ cl->buf->in_file,
+ cl->buf->start,
+ cl->buf->pos,
+ cl->buf->last,
+ cl->buf->file,
+ cl->buf->file_pos,
+ cl->buf->file_last);
+
+ ngx_debug_point();
+ return NGX_ERROR;
+ }
+
+ size += ngx_buf_size(cl->buf);
+
+ if (cl->buf->flush || cl->buf->recycled) {
+ flush = 1;
+ }
+
+ if (cl->buf->sync) {
+ sync = 1;
+ }
+
+ if (cl->buf->last_buf) {
+ last = 1;
+ }
+ }
+
+ *ll = NULL;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http write filter: l:%ui f:%ui s:%O", last, flush, size);
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ /*
+ * avoid the output if there are no last buf, no flush point,
+ * there are the incoming bufs and the size of all bufs
+ * is smaller than "postpone_output" directive
+ */
+
+ if (!last && !flush && in && size < (off_t) clcf->postpone_output) {
+ return NGX_OK;
+ }
+
+ if (c->write->delayed) {
+ c->buffered |= NGX_HTTP_WRITE_BUFFERED;
+ return NGX_AGAIN;
+ }
+
+ if (size == 0
+ && !(c->buffered & NGX_LOWLEVEL_BUFFERED)
+ && !(last && c->need_last_buf))
+ {
+ if (last || flush || sync) {
+ for (cl = r->out; cl; /* void */) {
+ ln = cl;
+ cl = cl->next;
+ ngx_free_chain(r->pool, ln);
+ }
+
+ r->out = NULL;
+ c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;
+
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "the http output chain is empty");
+
+ ngx_debug_point();
+
+ return NGX_ERROR;
+ }
+
+ if (!r->limit_rate_set) {
+ r->limit_rate = ngx_http_complex_value_size(r, clcf->limit_rate, 0);
+ r->limit_rate_set = 1;
+ }
+
+ if (r->limit_rate) {
+
+ if (!r->limit_rate_after_set) {
+ r->limit_rate_after = ngx_http_complex_value_size(r,
+ clcf->limit_rate_after, 0);
+ r->limit_rate_after_set = 1;
+ }
+
+ limit = (off_t) r->limit_rate * (ngx_time() - r->start_sec + 1)
+ - (c->sent - r->limit_rate_after);
+
+ if (limit <= 0) {
+ c->write->delayed = 1;
+ delay = (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1);
+ ngx_add_timer(c->write, delay);
+
+ c->buffered |= NGX_HTTP_WRITE_BUFFERED;
+
+ return NGX_AGAIN;
+ }
+
+ if (clcf->sendfile_max_chunk
+ && (off_t) clcf->sendfile_max_chunk < limit)
+ {
+ limit = clcf->sendfile_max_chunk;
+ }
+
+ } else {
+ limit = clcf->sendfile_max_chunk;
+ }
+
+ sent = c->sent;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http write filter limit %O", limit);
+
+ chain = c->send_chain(c, r->out, limit);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http write filter %p", chain);
+
+ if (chain == NGX_CHAIN_ERROR) {
+ c->error = 1;
+ return NGX_ERROR;
+ }
+
+ if (r->limit_rate) {
+
+ nsent = c->sent;
+
+ if (r->limit_rate_after) {
+
+ sent -= r->limit_rate_after;
+ if (sent < 0) {
+ sent = 0;
+ }
+
+ nsent -= r->limit_rate_after;
+ if (nsent < 0) {
+ nsent = 0;
+ }
+ }
+
+ delay = (ngx_msec_t) ((nsent - sent) * 1000 / r->limit_rate);
+
+ if (delay > 0) {
+ limit = 0;
+ c->write->delayed = 1;
+ ngx_add_timer(c->write, delay);
+ }
+ }
+
+ if (limit
+ && c->write->ready
+ && c->sent - sent >= limit - (off_t) (2 * ngx_pagesize))
+ {
+ c->write->delayed = 1;
+ ngx_add_timer(c->write, 1);
+ }
+
+ for (cl = r->out; cl && cl != chain; /* void */) {
+ ln = cl;
+ cl = cl->next;
+ ngx_free_chain(r->pool, ln);
+ }
+
+ r->out = chain;
+
+ if (chain) {
+ c->buffered |= NGX_HTTP_WRITE_BUFFERED;
+ return NGX_AGAIN;
+ }
+
+ c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;
+
+ if ((c->buffered & NGX_LOWLEVEL_BUFFERED) && r->postponed == NULL) {
+ return NGX_AGAIN;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_write_filter_init(ngx_conf_t *cf)
+{
+ ngx_http_top_body_filter = ngx_http_write_filter;
+
+ return NGX_OK;
+}
diff --git a/src/nginx-1.18.0/src/http/v2/ngx_http_v2.c b/src/nginx-1.18.0/src/http/v2/ngx_http_v2.c
new file mode 100644
index 0000000..8b0fc53
--- /dev/null
+++ b/src/nginx-1.18.0/src/http/v2/ngx_http_v2.c
@@ -0,0 +1,4795 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ * Copyright (C) Valentin V. Bartenev
+ */
+
+
+#include
+#include
+#include
+#include
+
+
+typedef struct {
+ ngx_str_t name;
+ ngx_uint_t offset;
+ ngx_uint_t hash;
+ ngx_http_header_t *hh;
+} ngx_http_v2_parse_header_t;
+
+
+/* errors */
+#define NGX_HTTP_V2_NO_ERROR 0x0
+#define NGX_HTTP_V2_PROTOCOL_ERROR 0x1
+#define NGX_HTTP_V2_INTERNAL_ERROR 0x2
+#define NGX_HTTP_V2_FLOW_CTRL_ERROR 0x3
+#define NGX_HTTP_V2_SETTINGS_TIMEOUT 0x4
+#define NGX_HTTP_V2_STREAM_CLOSED 0x5
+#define NGX_HTTP_V2_SIZE_ERROR 0x6
+#define NGX_HTTP_V2_REFUSED_STREAM 0x7
+#define NGX_HTTP_V2_CANCEL 0x8
+#define NGX_HTTP_V2_COMP_ERROR 0x9
+#define NGX_HTTP_V2_CONNECT_ERROR 0xa
+#define NGX_HTTP_V2_ENHANCE_YOUR_CALM 0xb
+#define NGX_HTTP_V2_INADEQUATE_SECURITY 0xc
+#define NGX_HTTP_V2_HTTP_1_1_REQUIRED 0xd
+
+/* frame sizes */
+#define NGX_HTTP_V2_SETTINGS_ACK_SIZE 0
+#define NGX_HTTP_V2_RST_STREAM_SIZE 4
+#define NGX_HTTP_V2_PRIORITY_SIZE 5
+#define NGX_HTTP_V2_PING_SIZE 8
+#define NGX_HTTP_V2_GOAWAY_SIZE 8
+#define NGX_HTTP_V2_WINDOW_UPDATE_SIZE 4
+
+#define NGX_HTTP_V2_SETTINGS_PARAM_SIZE 6
+
+/* settings fields */
+#define NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING 0x1
+#define NGX_HTTP_V2_ENABLE_PUSH_SETTING 0x2
+#define NGX_HTTP_V2_MAX_STREAMS_SETTING 0x3
+#define NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING 0x4
+#define NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING 0x5
+
+#define NGX_HTTP_V2_FRAME_BUFFER_SIZE 24
+
+#define NGX_HTTP_V2_ROOT (void *) -1
+
+
+static void ngx_http_v2_read_handler(ngx_event_t *rev);
+static void ngx_http_v2_write_handler(ngx_event_t *wev);
+static void ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c);
+
+static u_char *ngx_http_v2_state_proxy_protocol(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_preface(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_preface_end(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_head(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_header_block(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_field_len(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_field_huff(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_field_raw(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_field_skip(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_process_header(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_header_complete(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_handle_continuation(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end, ngx_http_v2_handler_pt handler);
+static u_char *ngx_http_v2_state_priority(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_rst_stream(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_settings(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_push_promise(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_ping(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_goaway(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_window_update(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_continuation(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_complete(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_skip_padded(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_skip(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_v2_state_save(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end, ngx_http_v2_handler_pt handler);
+static u_char *ngx_http_v2_state_headers_save(ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end, ngx_http_v2_handler_pt handler);
+static u_char *ngx_http_v2_connection_error(ngx_http_v2_connection_t *h2c,
+ ngx_uint_t err);
+
+static ngx_int_t ngx_http_v2_parse_int(ngx_http_v2_connection_t *h2c,
+ u_char **pos, u_char *end, ngx_uint_t prefix);
+
+static ngx_http_v2_stream_t *ngx_http_v2_create_stream(
+ ngx_http_v2_connection_t *h2c, ngx_uint_t push);
+static ngx_http_v2_node_t *ngx_http_v2_get_node_by_id(
+ ngx_http_v2_connection_t *h2c, ngx_uint_t sid, ngx_uint_t alloc);
+static ngx_http_v2_node_t *ngx_http_v2_get_closed_node(
+ ngx_http_v2_connection_t *h2c);
+#define ngx_http_v2_index_size(h2scf) (h2scf->streams_index_mask + 1)
+#define ngx_http_v2_index(h2scf, sid) ((sid >> 1) & h2scf->streams_index_mask)
+
+static ngx_int_t ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c);
+static ngx_int_t ngx_http_v2_settings_frame_handler(
+ ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);
+static ngx_int_t ngx_http_v2_send_window_update(ngx_http_v2_connection_t *h2c,
+ ngx_uint_t sid, size_t window);
+static ngx_int_t ngx_http_v2_send_rst_stream(ngx_http_v2_connection_t *h2c,
+ ngx_uint_t sid, ngx_uint_t status);
+static ngx_int_t ngx_http_v2_send_goaway(ngx_http_v2_connection_t *h2c,
+ ngx_uint_t status);
+
+static ngx_http_v2_out_frame_t *ngx_http_v2_get_frame(
+ ngx_http_v2_connection_t *h2c, size_t length, ngx_uint_t type,
+ u_char flags, ngx_uint_t sid);
+static ngx_int_t ngx_http_v2_frame_handler(ngx_http_v2_connection_t *h2c,
+ ngx_http_v2_out_frame_t *frame);
+
+static ngx_int_t ngx_http_v2_validate_header(ngx_http_request_t *r,
+ ngx_http_v2_header_t *header);
+static ngx_int_t ngx_http_v2_pseudo_header(ngx_http_request_t *r,
+ ngx_http_v2_header_t *header);
+static ngx_int_t ngx_http_v2_parse_path(ngx_http_request_t *r,
+ ngx_str_t *value);
+static ngx_int_t ngx_http_v2_parse_method(ngx_http_request_t *r,
+ ngx_str_t *value);
+static ngx_int_t ngx_http_v2_parse_scheme(ngx_http_request_t *r,
+ ngx_str_t *value);
+static ngx_int_t ngx_http_v2_parse_authority(ngx_http_request_t *r,
+ ngx_str_t *value);
+static ngx_int_t ngx_http_v2_parse_header(ngx_http_request_t *r,
+ ngx_http_v2_parse_header_t *header, ngx_str_t *value);
+static ngx_int_t ngx_http_v2_construct_request_line(ngx_http_request_t *r);
+static ngx_int_t ngx_http_v2_cookie(ngx_http_request_t *r,
+ ngx_http_v2_header_t *header);
+static ngx_int_t ngx_http_v2_construct_cookie_header(ngx_http_request_t *r);
+static void ngx_http_v2_run_request(ngx_http_request_t *r);
+static void ngx_http_v2_run_request_handler(ngx_event_t *ev);
+static ngx_int_t ngx_http_v2_process_request_body(ngx_http_request_t *r,
+ u_char *pos, size_t size, ngx_uint_t last);
+static ngx_int_t ngx_http_v2_filter_request_body(ngx_http_request_t *r);
+static void ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r);
+
+static ngx_int_t ngx_http_v2_terminate_stream(ngx_http_v2_connection_t *h2c,
+ ngx_http_v2_stream_t *stream, ngx_uint_t status);
+static void ngx_http_v2_close_stream_handler(ngx_event_t *ev);
+static void ngx_http_v2_retry_close_stream_handler(ngx_event_t *ev);
+static void ngx_http_v2_handle_connection_handler(ngx_event_t *rev);
+static void ngx_http_v2_idle_handler(ngx_event_t *rev);
+static void ngx_http_v2_finalize_connection(ngx_http_v2_connection_t *h2c,
+ ngx_uint_t status);
+
+static ngx_int_t ngx_http_v2_adjust_windows(ngx_http_v2_connection_t *h2c,
+ ssize_t delta);
+static void ngx_http_v2_set_dependency(ngx_http_v2_connection_t *h2c,
+ ngx_http_v2_node_t *node, ngx_uint_t depend, ngx_uint_t exclusive);
+static void ngx_http_v2_node_children_update(ngx_http_v2_node_t *node);
+
+static void ngx_http_v2_pool_cleanup(void *data);
+
+
+static ngx_http_v2_handler_pt ngx_http_v2_frame_states[] = {
+ ngx_http_v2_state_data, /* NGX_HTTP_V2_DATA_FRAME */
+ ngx_http_v2_state_headers, /* NGX_HTTP_V2_HEADERS_FRAME */
+ ngx_http_v2_state_priority, /* NGX_HTTP_V2_PRIORITY_FRAME */
+ ngx_http_v2_state_rst_stream, /* NGX_HTTP_V2_RST_STREAM_FRAME */
+ ngx_http_v2_state_settings, /* NGX_HTTP_V2_SETTINGS_FRAME */
+ ngx_http_v2_state_push_promise, /* NGX_HTTP_V2_PUSH_PROMISE_FRAME */
+ ngx_http_v2_state_ping, /* NGX_HTTP_V2_PING_FRAME */
+ ngx_http_v2_state_goaway, /* NGX_HTTP_V2_GOAWAY_FRAME */
+ ngx_http_v2_state_window_update, /* NGX_HTTP_V2_WINDOW_UPDATE_FRAME */
+ ngx_http_v2_state_continuation /* NGX_HTTP_V2_CONTINUATION_FRAME */
+};
+
+#define NGX_HTTP_V2_FRAME_STATES \
+ (sizeof(ngx_http_v2_frame_states) / sizeof(ngx_http_v2_handler_pt))
+
+
+static ngx_http_v2_parse_header_t ngx_http_v2_parse_headers[] = {
+ { ngx_string("host"),
+ offsetof(ngx_http_headers_in_t, host), 0, NULL },
+
+ { ngx_string("accept-encoding"),
+ offsetof(ngx_http_headers_in_t, accept_encoding), 0, NULL },
+
+ { ngx_string("accept-language"),
+ offsetof(ngx_http_headers_in_t, accept_language), 0, NULL },
+
+ { ngx_string("user-agent"),
+ offsetof(ngx_http_headers_in_t, user_agent), 0, NULL },
+
+ { ngx_null_string, 0, 0, NULL }
+};
+
+
+void
+ngx_http_v2_init(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_pool_cleanup_t *cln;
+ ngx_http_connection_t *hc;
+ ngx_http_v2_srv_conf_t *h2scf;
+ ngx_http_v2_main_conf_t *h2mcf;
+ ngx_http_v2_connection_t *h2c;
+
+ c = rev->data;
+ hc = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "init http2 connection");
+
+ c->log->action = "processing HTTP/2 connection";
+
+ h2mcf = ngx_http_get_module_main_conf(hc->conf_ctx, ngx_http_v2_module);
+
+ if (h2mcf->recv_buffer == NULL) {
+ h2mcf->recv_buffer = ngx_palloc(ngx_cycle->pool,
+ h2mcf->recv_buffer_size);
+ if (h2mcf->recv_buffer == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+ }
+
+ h2c = ngx_pcalloc(c->pool, sizeof(ngx_http_v2_connection_t));
+ if (h2c == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ h2c->connection = c;
+ h2c->http_connection = hc;
+
+ h2c->send_window = NGX_HTTP_V2_DEFAULT_WINDOW;
+ h2c->recv_window = NGX_HTTP_V2_MAX_WINDOW;
+
+ h2c->init_window = NGX_HTTP_V2_DEFAULT_WINDOW;
+
+ h2c->frame_size = NGX_HTTP_V2_DEFAULT_FRAME_SIZE;
+
+ h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module);
+
+ h2c->concurrent_pushes = h2scf->concurrent_pushes;
+ h2c->priority_limit = h2scf->concurrent_streams;
+
+ h2c->pool = ngx_create_pool(h2scf->pool_size, h2c->connection->log);
+ if (h2c->pool == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ cln = ngx_pool_cleanup_add(c->pool, 0);
+ if (cln == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ cln->handler = ngx_http_v2_pool_cleanup;
+ cln->data = h2c;
+
+ h2c->streams_index = ngx_pcalloc(c->pool, ngx_http_v2_index_size(h2scf)
+ * sizeof(ngx_http_v2_node_t *));
+ if (h2c->streams_index == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ if (ngx_http_v2_send_settings(h2c) == NGX_ERROR) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ if (ngx_http_v2_send_window_update(h2c, 0, NGX_HTTP_V2_MAX_WINDOW
+ - NGX_HTTP_V2_DEFAULT_WINDOW)
+ == NGX_ERROR)
+ {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ h2c->state.handler = hc->proxy_protocol ? ngx_http_v2_state_proxy_protocol
+ : ngx_http_v2_state_preface;
+
+ ngx_queue_init(&h2c->waiting);
+ ngx_queue_init(&h2c->dependencies);
+ ngx_queue_init(&h2c->closed);
+
+ c->data = h2c;
+
+ rev->handler = ngx_http_v2_read_handler;
+ c->write->handler = ngx_http_v2_write_handler;
+
+ c->idle = 1;
+
+ ngx_http_v2_read_handler(rev);
+}
+
+
+static void
+ngx_http_v2_read_handler(ngx_event_t *rev)
+{
+ u_char *p, *end;
+ size_t available;
+ ssize_t n;
+ ngx_connection_t *c;
+ ngx_http_v2_main_conf_t *h2mcf;
+ ngx_http_v2_connection_t *h2c;
+
+ c = rev->data;
+ h2c = c->data;
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
+ return;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http2 read handler");
+
+ h2c->blocked = 1;
+
+ if (c->close) {
+ c->close = 0;
+
+ if (c->error) {
+ ngx_http_v2_finalize_connection(h2c, 0);
+ return;
+ }
+
+ if (!h2c->goaway) {
+ h2c->goaway = 1;
+
+ if (ngx_http_v2_send_goaway(h2c, NGX_HTTP_V2_NO_ERROR)
+ == NGX_ERROR)
+ {
+ ngx_http_v2_finalize_connection(h2c, 0);
+ return;
+ }
+
+ if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) {
+ ngx_http_v2_finalize_connection(h2c, 0);
+ return;
+ }
+ }
+
+ h2c->blocked = 0;
+
+ return;
+ }
+
+ h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx,
+ ngx_http_v2_module);
+
+ available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE;
+
+ do {
+ p = h2mcf->recv_buffer;
+
+ ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE);
+ end = p + h2c->state.buffer_used;
+
+ n = c->recv(c, end, available);
+
+ if (n == NGX_AGAIN) {
+ break;
+ }
+
+ if (n == 0
+ && (h2c->state.incomplete || h2c->processing || h2c->pushing))
+ {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client prematurely closed connection");
+ }
+
+ if (n == 0 || n == NGX_ERROR) {
+ c->error = 1;
+ ngx_http_v2_finalize_connection(h2c, 0);
+ return;
+ }
+
+ end += n;
+
+ h2c->state.buffer_used = 0;
+ h2c->state.incomplete = 0;
+
+ do {
+ p = h2c->state.handler(h2c, p, end);
+
+ if (p == NULL) {
+ return;
+ }
+
+ } while (p != end);
+
+ h2c->total_bytes += n;
+
+ if (h2c->total_bytes / 8 > h2c->payload_bytes + 1048576) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, "http2 flood detected");
+ ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_NO_ERROR);
+ return;
+ }
+
+ } while (rev->ready);
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
+ return;
+ }
+
+ if (h2c->last_out && ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) {
+ ngx_http_v2_finalize_connection(h2c, 0);
+ return;
+ }
+
+ h2c->blocked = 0;
+
+ if (h2c->processing || h2c->pushing) {
+ if (rev->timer_set) {
+ ngx_del_timer(rev);
+ }
+
+ return;
+ }
+
+ ngx_http_v2_handle_connection(h2c);
+}
+
+
+static void
+ngx_http_v2_write_handler(ngx_event_t *wev)
+{
+ ngx_int_t rc;
+ ngx_connection_t *c;
+ ngx_http_v2_connection_t *h2c;
+
+ c = wev->data;
+ h2c = c->data;
+
+ if (wev->timedout) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http2 write event timed out");
+ c->error = 1;
+ ngx_http_v2_finalize_connection(h2c, 0);
+ return;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http2 write handler");
+
+ if (h2c->last_out == NULL && !c->buffered) {
+
+ if (wev->timer_set) {
+ ngx_del_timer(wev);
+ }
+
+ ngx_http_v2_handle_connection(h2c);
+ return;
+ }
+
+ h2c->blocked = 1;
+
+ rc = ngx_http_v2_send_output_queue(h2c);
+
+ if (rc == NGX_ERROR) {
+ ngx_http_v2_finalize_connection(h2c, 0);
+ return;
+ }
+
+ h2c->blocked = 0;
+
+ if (rc == NGX_AGAIN) {
+ return;
+ }
+
+ ngx_http_v2_handle_connection(h2c);
+}
+
+
+ngx_int_t
+ngx_http_v2_send_output_queue(ngx_http_v2_connection_t *h2c)
+{
+ int tcp_nodelay;
+ ngx_chain_t *cl;
+ ngx_event_t *wev;
+ ngx_connection_t *c;
+ ngx_http_v2_out_frame_t *out, *frame, *fn;
+ ngx_http_core_loc_conf_t *clcf;
+
+ c = h2c->connection;
+ wev = c->write;
+
+ if (c->error) {
+ goto error;
+ }
+
+ if (!wev->ready) {
+ return NGX_AGAIN;
+ }
+
+ cl = NULL;
+ out = NULL;
+
+ for (frame = h2c->last_out; frame; frame = fn) {
+ frame->last->next = cl;
+ cl = frame->first;
+
+ fn = frame->next;
+ frame->next = out;
+ out = frame;
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http2 frame out: %p sid:%ui bl:%d len:%uz",
+ out, out->stream ? out->stream->node->id : 0,
+ out->blocked, out->length);
+ }
+
+ cl = c->send_chain(c, cl, 0);
+
+ if (cl == NGX_CHAIN_ERROR) {
+ goto error;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx,
+ ngx_http_core_module);
+
+ if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
+ goto error;
+ }
+
+ if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) {
+ if (ngx_tcp_push(c->fd) == -1) {
+ ngx_connection_error(c, ngx_socket_errno, ngx_tcp_push_n " failed");
+ goto error;
+ }
+
+ c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;
+ tcp_nodelay = ngx_tcp_nodelay_and_tcp_nopush ? 1 : 0;
+
+ } else {
+ tcp_nodelay = 1;
+ }
+
+ if (tcp_nodelay && clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) {
+ goto error;
+ }
+
+ for ( /* void */ ; out; out = fn) {
+ fn = out->next;
+
+ if (out->handler(h2c, out) != NGX_OK) {
+ out->blocked = 1;
+ break;
+ }
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http2 frame sent: %p sid:%ui bl:%d len:%uz",
+ out, out->stream ? out->stream->node->id : 0,
+ out->blocked, out->length);
+ }
+
+ frame = NULL;
+
+ for ( /* void */ ; out; out = fn) {
+ fn = out->next;
+ out->next = frame;
+ frame = out;
+ }
+
+ h2c->last_out = frame;
+
+ if (!wev->ready) {
+ ngx_add_timer(wev, clcf->send_timeout);
+ return NGX_AGAIN;
+ }
+
+ if (wev->timer_set) {
+ ngx_del_timer(wev);
+ }
+
+ return NGX_OK;
+
+error:
+
+ c->error = 1;
+
+ if (!h2c->blocked) {
+ ngx_post_event(wev, &ngx_posted_events);
+ }
+
+ return NGX_ERROR;
+}
+
+
+static void
+ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c)
+{
+ ngx_int_t rc;
+ ngx_connection_t *c;
+ ngx_http_v2_srv_conf_t *h2scf;
+
+ if (h2c->last_out || h2c->processing || h2c->pushing) {
+ return;
+ }
+
+ c = h2c->connection;
+
+ if (c->error) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ if (c->buffered) {
+ h2c->blocked = 1;
+
+ rc = ngx_http_v2_send_output_queue(h2c);
+
+ h2c->blocked = 0;
+
+ if (rc == NGX_ERROR) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ if (rc == NGX_AGAIN) {
+ return;
+ }
+
+ /* rc == NGX_OK */
+ }
+
+ if (h2c->goaway) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
+ ngx_http_v2_module);
+ if (h2c->state.incomplete) {
+ ngx_add_timer(c->read, h2scf->recv_timeout);
+ return;
+ }
+
+ ngx_destroy_pool(h2c->pool);
+
+ h2c->pool = NULL;
+ h2c->free_frames = NULL;
+ h2c->frames = 0;
+ h2c->free_fake_connections = NULL;
+
+#if (NGX_HTTP_SSL)
+ if (c->ssl) {
+ ngx_ssl_free_buffer(c);
+ }
+#endif
+
+ c->destroyed = 1;
+ ngx_reusable_connection(c, 1);
+
+ c->write->handler = ngx_http_empty_handler;
+ c->read->handler = ngx_http_v2_idle_handler;
+
+ if (c->write->timer_set) {
+ ngx_del_timer(c->write);
+ }
+
+ ngx_add_timer(c->read, h2scf->idle_timeout);
+}
+
+
+static u_char *
+ngx_http_v2_state_proxy_protocol(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end)
+{
+ ngx_log_t *log;
+
+ log = h2c->connection->log;
+ log->action = "reading PROXY protocol";
+
+ pos = ngx_proxy_protocol_read(h2c->connection, pos, end);
+
+ log->action = "processing HTTP/2 connection";
+
+ if (pos == NULL) {
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
+ }
+
+ return ngx_http_v2_state_preface(h2c, pos, end);
+}
+
+
+static u_char *
+ngx_http_v2_state_preface(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end)
+{
+ static const u_char preface[] = "PRI * HTTP/2.0\r\n";
+
+ if ((size_t) (end - pos) < sizeof(preface) - 1) {
+ return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_preface);
+ }
+
+ if (ngx_memcmp(pos, preface, sizeof(preface) - 1) != 0) {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "invalid http2 connection preface \"%*s\"",
+ sizeof(preface) - 1, pos);
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
+ }
+
+ return ngx_http_v2_state_preface_end(h2c, pos + sizeof(preface) - 1, end);
+}
+
+
+static u_char *
+ngx_http_v2_state_preface_end(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end)
+{
+ static const u_char preface[] = "\r\nSM\r\n\r\n";
+
+ if ((size_t) (end - pos) < sizeof(preface) - 1) {
+ return ngx_http_v2_state_save(h2c, pos, end,
+ ngx_http_v2_state_preface_end);
+ }
+
+ if (ngx_memcmp(pos, preface, sizeof(preface) - 1) != 0) {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "invalid http2 connection preface \"%*s\"",
+ sizeof(preface) - 1, pos);
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 preface verified");
+
+ return ngx_http_v2_state_head(h2c, pos + sizeof(preface) - 1, end);
+}
+
+
+static u_char *
+ngx_http_v2_state_head(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)
+{
+ uint32_t head;
+ ngx_uint_t type;
+
+ if (end - pos < NGX_HTTP_V2_FRAME_HEADER_SIZE) {
+ return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_head);
+ }
+
+ head = ngx_http_v2_parse_uint32(pos);
+
+ h2c->state.length = ngx_http_v2_parse_length(head);
+ h2c->state.flags = pos[4];
+
+ h2c->state.sid = ngx_http_v2_parse_sid(&pos[5]);
+
+ pos += NGX_HTTP_V2_FRAME_HEADER_SIZE;
+
+ type = ngx_http_v2_parse_type(head);
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 frame type:%ui f:%Xd l:%uz sid:%ui",
+ type, h2c->state.flags, h2c->state.length, h2c->state.sid);
+
+ if (type >= NGX_HTTP_V2_FRAME_STATES) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent frame with unknown type %ui", type);
+ return ngx_http_v2_state_skip(h2c, pos, end);
+ }
+
+ return ngx_http_v2_frame_states[type](h2c, pos, end);
+}
+
+
+static u_char *
+ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)
+{
+ size_t size;
+ ngx_http_v2_node_t *node;
+ ngx_http_v2_stream_t *stream;
+
+ size = h2c->state.length;
+
+ if (h2c->state.flags & NGX_HTTP_V2_PADDED_FLAG) {
+
+ if (h2c->state.length == 0) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent padded DATA frame "
+ "with incorrect length: 0");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
+ }
+
+ if (end - pos == 0) {
+ return ngx_http_v2_state_save(h2c, pos, end,
+ ngx_http_v2_state_data);
+ }
+
+ h2c->state.padding = *pos++;
+
+ if (h2c->state.padding >= size) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent padded DATA frame "
+ "with incorrect length: %uz, padding: %uz",
+ size, h2c->state.padding);
+
+ return ngx_http_v2_connection_error(h2c,
+ NGX_HTTP_V2_PROTOCOL_ERROR);
+ }
+
+ h2c->state.length -= 1 + h2c->state.padding;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 DATA frame");
+
+ if (size > h2c->recv_window) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client violated connection flow control: "
+ "received DATA frame length %uz, available window %uz",
+ size, h2c->recv_window);
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_FLOW_CTRL_ERROR);
+ }
+
+ h2c->recv_window -= size;
+
+ if (h2c->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4) {
+
+ if (ngx_http_v2_send_window_update(h2c, 0, NGX_HTTP_V2_MAX_WINDOW
+ - h2c->recv_window)
+ == NGX_ERROR)
+ {
+ return ngx_http_v2_connection_error(h2c,
+ NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ h2c->recv_window = NGX_HTTP_V2_MAX_WINDOW;
+ }
+
+ node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 0);
+
+ if (node == NULL || node->stream == NULL) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "unknown http2 stream");
+
+ return ngx_http_v2_state_skip_padded(h2c, pos, end);
+ }
+
+ stream = node->stream;
+
+ if (size > stream->recv_window) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client violated flow control for stream %ui: "
+ "received DATA frame length %uz, available window %uz",
+ node->id, size, stream->recv_window);
+
+ if (ngx_http_v2_terminate_stream(h2c, stream,
+ NGX_HTTP_V2_FLOW_CTRL_ERROR)
+ == NGX_ERROR)
+ {
+ return ngx_http_v2_connection_error(h2c,
+ NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ return ngx_http_v2_state_skip_padded(h2c, pos, end);
+ }
+
+ stream->recv_window -= size;
+
+ if (stream->no_flow_control
+ && stream->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4)
+ {
+ if (ngx_http_v2_send_window_update(h2c, node->id,
+ NGX_HTTP_V2_MAX_WINDOW
+ - stream->recv_window)
+ == NGX_ERROR)
+ {
+ return ngx_http_v2_connection_error(h2c,
+ NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ stream->recv_window = NGX_HTTP_V2_MAX_WINDOW;
+ }
+
+ if (stream->in_closed) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent DATA frame for half-closed stream %ui",
+ node->id);
+
+ if (ngx_http_v2_terminate_stream(h2c, stream,
+ NGX_HTTP_V2_STREAM_CLOSED)
+ == NGX_ERROR)
+ {
+ return ngx_http_v2_connection_error(h2c,
+ NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ return ngx_http_v2_state_skip_padded(h2c, pos, end);
+ }
+
+ h2c->state.stream = stream;
+
+ return ngx_http_v2_state_read_data(h2c, pos, end);
+}
+
+
+static u_char *
+ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end)
+{
+ size_t size;
+ ngx_buf_t *buf;
+ ngx_int_t rc;
+ ngx_http_request_t *r;
+ ngx_http_v2_stream_t *stream;
+ ngx_http_v2_srv_conf_t *h2scf;
+
+ stream = h2c->state.stream;
+
+ if (stream == NULL) {
+ return ngx_http_v2_state_skip_padded(h2c, pos, end);
+ }
+
+ if (stream->skip_data) {
+ 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);
+ }
+
+ r = stream->request;
+
+ 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);
+ }
+
+ size = end - pos;
+
+ if (size >= h2c->state.length) {
+ size = h2c->state.length;
+ stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG;
+ }
+
+ h2c->payload_bytes += size;
+
+ if (r->request_body) {
+ rc = ngx_http_v2_process_request_body(r, pos, size, stream->in_closed);
+
+ if (rc != NGX_OK) {
+ stream->skip_data = 1;
+ ngx_http_finalize_request(r, rc);
+ }
+
+ } else if (size) {
+ buf = stream->preread;
+
+ if (buf == NULL) {
+ h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module);
+
+ buf = ngx_create_temp_buf(r->pool, h2scf->preread_size);
+ if (buf == NULL) {
+ return ngx_http_v2_connection_error(h2c,
+ NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ stream->preread = buf;
+ }
+
+ if (size > (size_t) (buf->end - buf->last)) {
+ ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0,
+ "http2 preread buffer overflow");
+ return ngx_http_v2_connection_error(h2c,
+ NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ buf->last = ngx_cpymem(buf->last, pos, size);
+ }
+
+ pos += size;
+ h2c->state.length -= size;
+
+ if (h2c->state.length) {
+ return ngx_http_v2_state_save(h2c, pos, end,
+ ngx_http_v2_state_read_data);
+ }
+
+ if (h2c->state.padding) {
+ return ngx_http_v2_state_skip_padded(h2c, pos, end);
+ }
+
+ return ngx_http_v2_state_complete(h2c, pos, end);
+}
+
+
+static u_char *
+ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end)
+{
+ size_t size;
+ ngx_uint_t padded, priority, depend, dependency, excl, weight;
+ ngx_uint_t status;
+ ngx_http_v2_node_t *node;
+ ngx_http_v2_stream_t *stream;
+ ngx_http_v2_srv_conf_t *h2scf;
+
+ padded = h2c->state.flags & NGX_HTTP_V2_PADDED_FLAG;
+ priority = h2c->state.flags & NGX_HTTP_V2_PRIORITY_FLAG;
+
+ size = 0;
+
+ if (padded) {
+ size++;
+ }
+
+ if (priority) {
+ size += sizeof(uint32_t) + 1;
+ }
+
+ if (h2c->state.length < size) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent HEADERS frame with incorrect length %uz",
+ h2c->state.length);
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
+ }
+
+ if (h2c->state.length == size) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent HEADERS frame with empty header block");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
+ }
+
+ if (h2c->goaway) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "skipping http2 HEADERS frame");
+ return ngx_http_v2_state_skip(h2c, pos, end);
+ }
+
+ if ((size_t) (end - pos) < size) {
+ return ngx_http_v2_state_save(h2c, pos, end,
+ ngx_http_v2_state_headers);
+ }
+
+ h2c->state.length -= size;
+
+ if (padded) {
+ h2c->state.padding = *pos++;
+
+ if (h2c->state.padding > h2c->state.length) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent padded HEADERS frame "
+ "with incorrect length: %uz, padding: %uz",
+ h2c->state.length, h2c->state.padding);
+
+ return ngx_http_v2_connection_error(h2c,
+ NGX_HTTP_V2_PROTOCOL_ERROR);
+ }
+
+ h2c->state.length -= h2c->state.padding;
+ }
+
+ depend = 0;
+ excl = 0;
+ weight = NGX_HTTP_V2_DEFAULT_WEIGHT;
+
+ if (priority) {
+ dependency = ngx_http_v2_parse_uint32(pos);
+
+ depend = dependency & 0x7fffffff;
+ excl = dependency >> 31;
+ weight = pos[4] + 1;
+
+ pos += sizeof(uint32_t) + 1;
+ }
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 HEADERS frame sid:%ui "
+ "depends on %ui excl:%ui weight:%ui",
+ h2c->state.sid, depend, excl, weight);
+
+ if (h2c->state.sid % 2 == 0 || h2c->state.sid <= h2c->last_sid) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent HEADERS frame with incorrect identifier "
+ "%ui, the last was %ui", h2c->state.sid, h2c->last_sid);
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
+ }
+
+ if (depend == h2c->state.sid) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent HEADERS frame for stream %ui "
+ "with incorrect dependency", h2c->state.sid);
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
+ }
+
+ h2c->last_sid = h2c->state.sid;
+
+ h2c->state.pool = ngx_create_pool(1024, h2c->connection->log);
+ if (h2c->state.pool == NULL) {
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
+ ngx_http_v2_module);
+
+ h2c->state.header_limit = h2scf->max_header_size;
+
+ if (h2c->processing >= h2scf->concurrent_streams) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "concurrent streams exceeded %ui", h2c->processing);
+
+ status = NGX_HTTP_V2_REFUSED_STREAM;
+ goto rst_stream;
+ }
+
+ if (!h2c->settings_ack
+ && !(h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG)
+ && h2scf->preread_size < NGX_HTTP_V2_DEFAULT_WINDOW)
+ {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent stream with data "
+ "before settings were acknowledged");
+
+ status = NGX_HTTP_V2_REFUSED_STREAM;
+ goto rst_stream;
+ }
+
+ node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 1);
+
+ if (node == NULL) {
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ if (node->parent) {
+ ngx_queue_remove(&node->reuse);
+ h2c->closed_nodes--;
+ }
+
+ stream = ngx_http_v2_create_stream(h2c, 0);
+ if (stream == NULL) {
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ h2c->state.stream = stream;
+
+ stream->pool = h2c->state.pool;
+ h2c->state.keep_pool = 1;
+
+ stream->request->request_length = h2c->state.length;
+
+ stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG;
+ stream->node = node;
+
+ node->stream = stream;
+
+ if (priority || node->parent == NULL) {
+ node->weight = weight;
+ ngx_http_v2_set_dependency(h2c, node, depend, excl);
+ }
+
+ if (h2c->connection->requests >= h2scf->max_requests) {
+ h2c->goaway = 1;
+
+ if (ngx_http_v2_send_goaway(h2c, NGX_HTTP_V2_NO_ERROR) == NGX_ERROR) {
+ return ngx_http_v2_connection_error(h2c,
+ NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+ }
+
+ return ngx_http_v2_state_header_block(h2c, pos, end);
+
+rst_stream:
+
+ if (ngx_http_v2_send_rst_stream(h2c, h2c->state.sid, status) != NGX_OK) {
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ return ngx_http_v2_state_header_block(h2c, pos, end);
+}
+
+
+static u_char *
+ngx_http_v2_state_header_block(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end)
+{
+ u_char ch;
+ ngx_int_t value;
+ ngx_uint_t indexed, size_update, prefix;
+
+ if (end - pos < 1) {
+ return ngx_http_v2_state_headers_save(h2c, pos, end,
+ ngx_http_v2_state_header_block);
+ }
+
+ if (!(h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG)
+ && h2c->state.length < NGX_HTTP_V2_INT_OCTETS)
+ {
+ return ngx_http_v2_handle_continuation(h2c, pos, end,
+ ngx_http_v2_state_header_block);
+ }
+
+ size_update = 0;
+ indexed = 0;
+
+ ch = *pos;
+
+ if (ch >= (1 << 7)) {
+ /* indexed header field */
+ indexed = 1;
+ prefix = ngx_http_v2_prefix(7);
+
+ } else if (ch >= (1 << 6)) {
+ /* literal header field with incremental indexing */
+ h2c->state.index = 1;
+ prefix = ngx_http_v2_prefix(6);
+
+ } else if (ch >= (1 << 5)) {
+ /* dynamic table size update */
+ size_update = 1;
+ prefix = ngx_http_v2_prefix(5);
+
+ } else if (ch >= (1 << 4)) {
+ /* literal header field never indexed */
+ prefix = ngx_http_v2_prefix(4);
+
+ } else {
+ /* literal header field without indexing */
+ prefix = ngx_http_v2_prefix(4);
+ }
+
+ value = ngx_http_v2_parse_int(h2c, &pos, end, prefix);
+
+ if (value < 0) {
+ if (value == NGX_AGAIN) {
+ return ngx_http_v2_state_headers_save(h2c, pos, end,
+ ngx_http_v2_state_header_block);
+ }
+
+ if (value == NGX_DECLINED) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent header block with too long %s value",
+ size_update ? "size update" : "header index");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR);
+ }
+
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent header block with incorrect length");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
+ }
+
+ if (indexed) {
+ if (ngx_http_v2_get_indexed_header(h2c, value, 0) != NGX_OK) {
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR);
+ }
+
+ return ngx_http_v2_state_process_header(h2c, pos, end);
+ }
+
+ if (size_update) {
+ if (ngx_http_v2_table_size(h2c, value) != NGX_OK) {
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR);
+ }
+
+ return ngx_http_v2_state_header_complete(h2c, pos, end);
+ }
+
+ if (value == 0) {
+ h2c->state.parse_name = 1;
+
+ } else if (ngx_http_v2_get_indexed_header(h2c, value, 1) != NGX_OK) {
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR);
+ }
+
+ h2c->state.parse_value = 1;
+
+ return ngx_http_v2_state_field_len(h2c, pos, end);
+}
+
+
+static u_char *
+ngx_http_v2_state_field_len(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end)
+{
+ size_t alloc;
+ ngx_int_t len;
+ ngx_uint_t huff;
+ ngx_http_v2_srv_conf_t *h2scf;
+
+ if (!(h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG)
+ && h2c->state.length < NGX_HTTP_V2_INT_OCTETS)
+ {
+ return ngx_http_v2_handle_continuation(h2c, pos, end,
+ ngx_http_v2_state_field_len);
+ }
+
+ if (h2c->state.length < 1) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent header block with incorrect length");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
+ }
+
+ if (end - pos < 1) {
+ return ngx_http_v2_state_headers_save(h2c, pos, end,
+ ngx_http_v2_state_field_len);
+ }
+
+ huff = *pos >> 7;
+ len = ngx_http_v2_parse_int(h2c, &pos, end, ngx_http_v2_prefix(7));
+
+ if (len < 0) {
+ if (len == NGX_AGAIN) {
+ return ngx_http_v2_state_headers_save(h2c, pos, end,
+ ngx_http_v2_state_field_len);
+ }
+
+ if (len == NGX_DECLINED) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent header field with too long length value");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR);
+ }
+
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent header block with incorrect length");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 %s string, len:%i",
+ huff ? "encoded" : "raw", len);
+
+ h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
+ ngx_http_v2_module);
+
+ if ((size_t) len > h2scf->max_field_size) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client exceeded http2_max_field_size limit");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_ENHANCE_YOUR_CALM);
+ }
+
+ h2c->state.field_rest = len;
+
+ if (h2c->state.stream == NULL && !h2c->state.index) {
+ return ngx_http_v2_state_field_skip(h2c, pos, end);
+ }
+
+ alloc = (huff ? len * 8 / 5 : len) + 1;
+
+ h2c->state.field_start = ngx_pnalloc(h2c->state.pool, alloc);
+ if (h2c->state.field_start == NULL) {
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ h2c->state.field_end = h2c->state.field_start;
+
+ if (huff) {
+ return ngx_http_v2_state_field_huff(h2c, pos, end);
+ }
+
+ return ngx_http_v2_state_field_raw(h2c, pos, end);
+}
+
+
+static u_char *
+ngx_http_v2_state_field_huff(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end)
+{
+ size_t size;
+
+ size = end - pos;
+
+ if (size > h2c->state.field_rest) {
+ size = h2c->state.field_rest;
+ }
+
+ if (size > h2c->state.length) {
+ size = h2c->state.length;
+ }
+
+ h2c->state.length -= size;
+ h2c->state.field_rest -= size;
+
+ if (ngx_http_v2_huff_decode(&h2c->state.field_state, pos, size,
+ &h2c->state.field_end,
+ h2c->state.field_rest == 0,
+ h2c->connection->log)
+ != NGX_OK)
+ {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent invalid encoded header field");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR);
+ }
+
+ pos += size;
+
+ if (h2c->state.field_rest == 0) {
+ *h2c->state.field_end = '\0';
+ return ngx_http_v2_state_process_header(h2c, pos, end);
+ }
+
+ if (h2c->state.length) {
+ return ngx_http_v2_state_headers_save(h2c, pos, end,
+ ngx_http_v2_state_field_huff);
+ }
+
+ if (h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent header field with incorrect length");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
+ }
+
+ return ngx_http_v2_handle_continuation(h2c, pos, end,
+ ngx_http_v2_state_field_huff);
+}
+
+
+static u_char *
+ngx_http_v2_state_field_raw(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end)
+{
+ size_t size;
+
+ size = end - pos;
+
+ if (size > h2c->state.field_rest) {
+ size = h2c->state.field_rest;
+ }
+
+ if (size > h2c->state.length) {
+ size = h2c->state.length;
+ }
+
+ h2c->state.length -= size;
+ h2c->state.field_rest -= size;
+
+ h2c->state.field_end = ngx_cpymem(h2c->state.field_end, pos, size);
+
+ pos += size;
+
+ if (h2c->state.field_rest == 0) {
+ *h2c->state.field_end = '\0';
+ return ngx_http_v2_state_process_header(h2c, pos, end);
+ }
+
+ if (h2c->state.length) {
+ return ngx_http_v2_state_headers_save(h2c, pos, end,
+ ngx_http_v2_state_field_raw);
+ }
+
+ if (h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent header field with incorrect length");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
+ }
+
+ return ngx_http_v2_handle_continuation(h2c, pos, end,
+ ngx_http_v2_state_field_raw);
+}
+
+
+static u_char *
+ngx_http_v2_state_field_skip(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end)
+{
+ size_t size;
+
+ size = end - pos;
+
+ if (size > h2c->state.field_rest) {
+ size = h2c->state.field_rest;
+ }
+
+ if (size > h2c->state.length) {
+ size = h2c->state.length;
+ }
+
+ h2c->state.length -= size;
+ h2c->state.field_rest -= size;
+
+ pos += size;
+
+ if (h2c->state.field_rest == 0) {
+ return ngx_http_v2_state_process_header(h2c, pos, end);
+ }
+
+ if (h2c->state.length) {
+ return ngx_http_v2_state_save(h2c, pos, end,
+ ngx_http_v2_state_field_skip);
+ }
+
+ if (h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent header field with incorrect length");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
+ }
+
+ return ngx_http_v2_handle_continuation(h2c, pos, end,
+ ngx_http_v2_state_field_skip);
+}
+
+
+static u_char *
+ngx_http_v2_state_process_header(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end)
+{
+ size_t len;
+ ngx_int_t rc;
+ ngx_table_elt_t *h;
+ ngx_http_header_t *hh;
+ ngx_http_request_t *r;
+ ngx_http_v2_header_t *header;
+ ngx_http_core_srv_conf_t *cscf;
+ ngx_http_core_main_conf_t *cmcf;
+
+ static ngx_str_t cookie = ngx_string("cookie");
+
+ header = &h2c->state.header;
+
+ if (h2c->state.parse_name) {
+ h2c->state.parse_name = 0;
+
+ header->name.len = h2c->state.field_end - h2c->state.field_start;
+ header->name.data = h2c->state.field_start;
+
+ if (header->name.len == 0) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent zero header name length");
+
+ return ngx_http_v2_connection_error(h2c,
+ NGX_HTTP_V2_PROTOCOL_ERROR);
+ }
+
+ return ngx_http_v2_state_field_len(h2c, pos, end);
+ }
+
+ if (h2c->state.parse_value) {
+ h2c->state.parse_value = 0;
+
+ header->value.len = h2c->state.field_end - h2c->state.field_start;
+ header->value.data = h2c->state.field_start;
+ }
+
+ len = header->name.len + header->value.len;
+
+ if (len > h2c->state.header_limit) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client exceeded http2_max_header_size limit");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_ENHANCE_YOUR_CALM);
+ }
+
+ h2c->state.header_limit -= len;
+
+ if (h2c->state.index) {
+ if (ngx_http_v2_add_header(h2c, header) != NGX_OK) {
+ return ngx_http_v2_connection_error(h2c,
+ NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ h2c->state.index = 0;
+ }
+
+ if (h2c->state.stream == NULL) {
+ return ngx_http_v2_state_header_complete(h2c, pos, end);
+ }
+
+ r = h2c->state.stream->request;
+
+ /* TODO Optimization: validate headers while parsing. */
+ if (ngx_http_v2_validate_header(r, header) != NGX_OK) {
+ if (ngx_http_v2_terminate_stream(h2c, h2c->state.stream,
+ NGX_HTTP_V2_PROTOCOL_ERROR)
+ == NGX_ERROR)
+ {
+ return ngx_http_v2_connection_error(h2c,
+ NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ goto error;
+ }
+
+ if (header->name.data[0] == ':') {
+ rc = ngx_http_v2_pseudo_header(r, header);
+
+ if (rc == NGX_OK) {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http2 header: \":%V: %V\"",
+ &header->name, &header->value);
+
+ return ngx_http_v2_state_header_complete(h2c, pos, end);
+ }
+
+ if (rc == NGX_ABORT) {
+ goto error;
+ }
+
+ if (rc == NGX_DECLINED) {
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ goto error;
+ }
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ if (r->invalid_header) {
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+ if (cscf->ignore_invalid_headers) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent invalid header: \"%V\"", &header->name);
+
+ return ngx_http_v2_state_header_complete(h2c, pos, end);
+ }
+ }
+
+ if (header->name.len == cookie.len
+ && ngx_memcmp(header->name.data, cookie.data, cookie.len) == 0)
+ {
+ if (ngx_http_v2_cookie(r, header) != NGX_OK) {
+ return ngx_http_v2_connection_error(h2c,
+ NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ } else {
+ h = ngx_list_push(&r->headers_in.headers);
+ if (h == NULL) {
+ return ngx_http_v2_connection_error(h2c,
+ NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ h->key.len = header->name.len;
+ h->key.data = header->name.data;
+
+ /*
+ * TODO Optimization: precalculate hash
+ * and handler for indexed headers.
+ */
+ h->hash = ngx_hash_key(h->key.data, h->key.len);
+
+ h->value.len = header->value.len;
+ h->value.data = header->value.data;
+
+ h->lowcase_key = h->key.data;
+
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+ hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,
+ h->lowcase_key, h->key.len);
+
+ if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
+ goto error;
+ }
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http2 header: \"%V: %V\"",
+ &header->name, &header->value);
+
+ return ngx_http_v2_state_header_complete(h2c, pos, end);
+
+error:
+
+ h2c->state.stream = NULL;
+
+ return ngx_http_v2_state_header_complete(h2c, pos, end);
+}
+
+
+static u_char *
+ngx_http_v2_state_header_complete(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end)
+{
+ ngx_http_v2_stream_t *stream;
+
+ if (h2c->state.length) {
+ if (end - pos > 0) {
+ h2c->state.handler = ngx_http_v2_state_header_block;
+ return pos;
+ }
+
+ return ngx_http_v2_state_headers_save(h2c, pos, end,
+ ngx_http_v2_state_header_block);
+ }
+
+ if (!(h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG)) {
+ return ngx_http_v2_handle_continuation(h2c, pos, end,
+ ngx_http_v2_state_header_complete);
+ }
+
+ stream = h2c->state.stream;
+
+ if (stream) {
+ ngx_http_v2_run_request(stream->request);
+ }
+
+ if (!h2c->state.keep_pool) {
+ ngx_destroy_pool(h2c->state.pool);
+ }
+
+ h2c->state.pool = NULL;
+ h2c->state.keep_pool = 0;
+
+ if (h2c->state.padding) {
+ return ngx_http_v2_state_skip_padded(h2c, pos, end);
+ }
+
+ return ngx_http_v2_state_complete(h2c, pos, end);
+}
+
+
+static u_char *
+ngx_http_v2_handle_continuation(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end, ngx_http_v2_handler_pt handler)
+{
+ u_char *p;
+ size_t len, skip;
+ uint32_t head;
+
+ len = h2c->state.length;
+
+ if (h2c->state.padding && (size_t) (end - pos) > len) {
+ skip = ngx_min(h2c->state.padding, (end - pos) - len);
+
+ h2c->state.padding -= skip;
+
+ p = pos;
+ pos += skip;
+ ngx_memmove(pos, p, len);
+ }
+
+ if ((size_t) (end - pos) < len + NGX_HTTP_V2_FRAME_HEADER_SIZE) {
+ return ngx_http_v2_state_headers_save(h2c, pos, end, handler);
+ }
+
+ p = pos + len;
+
+ head = ngx_http_v2_parse_uint32(p);
+
+ if (ngx_http_v2_parse_type(head) != NGX_HTTP_V2_CONTINUATION_FRAME) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent inappropriate frame while CONTINUATION was expected");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
+ }
+
+ h2c->state.flags |= p[4];
+
+ if (h2c->state.sid != ngx_http_v2_parse_sid(&p[5])) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent CONTINUATION frame with incorrect identifier");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
+ }
+
+ p = pos;
+ pos += NGX_HTTP_V2_FRAME_HEADER_SIZE;
+
+ ngx_memcpy(pos, p, len);
+
+ len = ngx_http_v2_parse_length(head);
+
+ h2c->state.length += len;
+
+ if (h2c->state.stream) {
+ h2c->state.stream->request->request_length += len;
+ }
+
+ h2c->state.handler = handler;
+ return pos;
+}
+
+
+static u_char *
+ngx_http_v2_state_priority(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end)
+{
+ ngx_uint_t depend, dependency, excl, weight;
+ ngx_http_v2_node_t *node;
+
+ if (h2c->state.length != NGX_HTTP_V2_PRIORITY_SIZE) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent PRIORITY frame with incorrect length %uz",
+ h2c->state.length);
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
+ }
+
+ if (--h2c->priority_limit == 0) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent too many PRIORITY frames");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_ENHANCE_YOUR_CALM);
+ }
+
+ if (end - pos < NGX_HTTP_V2_PRIORITY_SIZE) {
+ return ngx_http_v2_state_save(h2c, pos, end,
+ ngx_http_v2_state_priority);
+ }
+
+ dependency = ngx_http_v2_parse_uint32(pos);
+
+ depend = dependency & 0x7fffffff;
+ excl = dependency >> 31;
+ weight = pos[4] + 1;
+
+ pos += NGX_HTTP_V2_PRIORITY_SIZE;
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 PRIORITY frame sid:%ui "
+ "depends on %ui excl:%ui weight:%ui",
+ h2c->state.sid, depend, excl, weight);
+
+ if (h2c->state.sid == 0) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent PRIORITY frame with incorrect identifier");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
+ }
+
+ if (depend == h2c->state.sid) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent PRIORITY frame for stream %ui "
+ "with incorrect dependency", h2c->state.sid);
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
+ }
+
+ node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 1);
+
+ if (node == NULL) {
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ node->weight = weight;
+
+ if (node->stream == NULL) {
+ if (node->parent == NULL) {
+ h2c->closed_nodes++;
+
+ } else {
+ ngx_queue_remove(&node->reuse);
+ }
+
+ ngx_queue_insert_tail(&h2c->closed, &node->reuse);
+ }
+
+ ngx_http_v2_set_dependency(h2c, node, depend, excl);
+
+ return ngx_http_v2_state_complete(h2c, pos, end);
+}
+
+
+static u_char *
+ngx_http_v2_state_rst_stream(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end)
+{
+ ngx_uint_t status;
+ ngx_event_t *ev;
+ ngx_connection_t *fc;
+ ngx_http_v2_node_t *node;
+ ngx_http_v2_stream_t *stream;
+
+ if (h2c->state.length != NGX_HTTP_V2_RST_STREAM_SIZE) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent RST_STREAM frame with incorrect length %uz",
+ h2c->state.length);
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
+ }
+
+ if (end - pos < NGX_HTTP_V2_RST_STREAM_SIZE) {
+ return ngx_http_v2_state_save(h2c, pos, end,
+ ngx_http_v2_state_rst_stream);
+ }
+
+ status = ngx_http_v2_parse_uint32(pos);
+
+ pos += NGX_HTTP_V2_RST_STREAM_SIZE;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 RST_STREAM frame, sid:%ui status:%ui",
+ h2c->state.sid, status);
+
+ if (h2c->state.sid == 0) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent RST_STREAM frame with incorrect identifier");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
+ }
+
+ node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 0);
+
+ if (node == NULL || node->stream == NULL) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "unknown http2 stream");
+
+ return ngx_http_v2_state_complete(h2c, pos, end);
+ }
+
+ stream = node->stream;
+
+ stream->in_closed = 1;
+ stream->out_closed = 1;
+
+ fc = stream->request->connection;
+ fc->error = 1;
+
+ switch (status) {
+
+ case NGX_HTTP_V2_CANCEL:
+ ngx_log_error(NGX_LOG_INFO, fc->log, 0,
+ "client canceled stream %ui", h2c->state.sid);
+ break;
+
+ case NGX_HTTP_V2_REFUSED_STREAM:
+ ngx_log_error(NGX_LOG_INFO, fc->log, 0,
+ "client refused stream %ui", h2c->state.sid);
+ break;
+
+ case NGX_HTTP_V2_INTERNAL_ERROR:
+ ngx_log_error(NGX_LOG_INFO, fc->log, 0,
+ "client terminated stream %ui due to internal error",
+ h2c->state.sid);
+ break;
+
+ default:
+ ngx_log_error(NGX_LOG_INFO, fc->log, 0,
+ "client terminated stream %ui with status %ui",
+ h2c->state.sid, status);
+ break;
+ }
+
+ ev = fc->read;
+ ev->handler(ev);
+
+ return ngx_http_v2_state_complete(h2c, pos, end);
+}
+
+
+static u_char *
+ngx_http_v2_state_settings(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end)
+{
+ if (h2c->state.flags == NGX_HTTP_V2_ACK_FLAG) {
+
+ if (h2c->state.length != 0) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent SETTINGS frame with the ACK flag "
+ "and nonzero length");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
+ }
+
+ h2c->settings_ack = 1;
+
+ return ngx_http_v2_state_complete(h2c, pos, end);
+ }
+
+ if (h2c->state.length % NGX_HTTP_V2_SETTINGS_PARAM_SIZE) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent SETTINGS frame with incorrect length %uz",
+ h2c->state.length);
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 SETTINGS frame");
+
+ return ngx_http_v2_state_settings_params(h2c, pos, end);
+}
+
+
+static u_char *
+ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end)
+{
+ ssize_t window_delta;
+ ngx_uint_t id, value;
+ ngx_http_v2_srv_conf_t *h2scf;
+ ngx_http_v2_out_frame_t *frame;
+
+ window_delta = 0;
+
+ while (h2c->state.length) {
+ if (end - pos < NGX_HTTP_V2_SETTINGS_PARAM_SIZE) {
+ return ngx_http_v2_state_save(h2c, pos, end,
+ ngx_http_v2_state_settings_params);
+ }
+
+ h2c->state.length -= NGX_HTTP_V2_SETTINGS_PARAM_SIZE;
+
+ id = ngx_http_v2_parse_uint16(pos);
+ value = ngx_http_v2_parse_uint32(&pos[2]);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 setting %ui:%ui", id, value);
+
+ switch (id) {
+
+ case NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING:
+
+ if (value > NGX_HTTP_V2_MAX_WINDOW) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent SETTINGS frame with incorrect "
+ "INITIAL_WINDOW_SIZE value %ui", value);
+
+ return ngx_http_v2_connection_error(h2c,
+ NGX_HTTP_V2_FLOW_CTRL_ERROR);
+ }
+
+ window_delta = value - h2c->init_window;
+ break;
+
+ case NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING:
+
+ if (value > NGX_HTTP_V2_MAX_FRAME_SIZE
+ || value < NGX_HTTP_V2_DEFAULT_FRAME_SIZE)
+ {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent SETTINGS frame with incorrect "
+ "MAX_FRAME_SIZE value %ui", value);
+
+ return ngx_http_v2_connection_error(h2c,
+ NGX_HTTP_V2_PROTOCOL_ERROR);
+ }
+
+ h2c->frame_size = value;
+ break;
+
+ case NGX_HTTP_V2_ENABLE_PUSH_SETTING:
+
+ if (value > 1) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent SETTINGS frame with incorrect "
+ "ENABLE_PUSH value %ui", value);
+
+ return ngx_http_v2_connection_error(h2c,
+ NGX_HTTP_V2_PROTOCOL_ERROR);
+ }
+
+ h2c->push_disabled = !value;
+ break;
+
+ case NGX_HTTP_V2_MAX_STREAMS_SETTING:
+ h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
+ ngx_http_v2_module);
+
+ h2c->concurrent_pushes = ngx_min(value, h2scf->concurrent_pushes);
+ break;
+
+ case NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING:
+
+ h2c->table_update = 1;
+ break;
+
+ default:
+ break;
+ }
+
+ pos += NGX_HTTP_V2_SETTINGS_PARAM_SIZE;
+ }
+
+ frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_SETTINGS_ACK_SIZE,
+ NGX_HTTP_V2_SETTINGS_FRAME,
+ NGX_HTTP_V2_ACK_FLAG, 0);
+ if (frame == NULL) {
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ ngx_http_v2_queue_ordered_frame(h2c, frame);
+
+ if (window_delta) {
+ h2c->init_window += window_delta;
+
+ if (ngx_http_v2_adjust_windows(h2c, window_delta) != NGX_OK) {
+ return ngx_http_v2_connection_error(h2c,
+ NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+ }
+
+ return ngx_http_v2_state_complete(h2c, pos, end);
+}
+
+
+static u_char *
+ngx_http_v2_state_push_promise(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end)
+{
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent PUSH_PROMISE frame");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
+}
+
+
+static u_char *
+ngx_http_v2_state_ping(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)
+{
+ ngx_buf_t *buf;
+ ngx_http_v2_out_frame_t *frame;
+
+ if (h2c->state.length != NGX_HTTP_V2_PING_SIZE) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent PING frame with incorrect length %uz",
+ h2c->state.length);
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
+ }
+
+ if (end - pos < NGX_HTTP_V2_PING_SIZE) {
+ return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_ping);
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 PING frame");
+
+ if (h2c->state.flags & NGX_HTTP_V2_ACK_FLAG) {
+ return ngx_http_v2_state_skip(h2c, pos, end);
+ }
+
+ frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_PING_SIZE,
+ NGX_HTTP_V2_PING_FRAME,
+ NGX_HTTP_V2_ACK_FLAG, 0);
+ if (frame == NULL) {
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ buf = frame->first->buf;
+
+ buf->last = ngx_cpymem(buf->last, pos, NGX_HTTP_V2_PING_SIZE);
+
+ ngx_http_v2_queue_blocked_frame(h2c, frame);
+
+ return ngx_http_v2_state_complete(h2c, pos + NGX_HTTP_V2_PING_SIZE, end);
+}
+
+
+static u_char *
+ngx_http_v2_state_goaway(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end)
+{
+#if (NGX_DEBUG)
+ ngx_uint_t last_sid, error;
+#endif
+
+ if (h2c->state.length < NGX_HTTP_V2_GOAWAY_SIZE) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent GOAWAY frame "
+ "with incorrect length %uz", h2c->state.length);
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
+ }
+
+ if (end - pos < NGX_HTTP_V2_GOAWAY_SIZE) {
+ return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_goaway);
+ }
+
+#if (NGX_DEBUG)
+ h2c->state.length -= NGX_HTTP_V2_GOAWAY_SIZE;
+
+ last_sid = ngx_http_v2_parse_sid(pos);
+ error = ngx_http_v2_parse_uint32(&pos[4]);
+
+ pos += NGX_HTTP_V2_GOAWAY_SIZE;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 GOAWAY frame: last sid %ui, error %ui",
+ last_sid, error);
+#endif
+
+ return ngx_http_v2_state_skip(h2c, pos, end);
+}
+
+
+static u_char *
+ngx_http_v2_state_window_update(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end)
+{
+ size_t window;
+ ngx_event_t *wev;
+ ngx_queue_t *q;
+ ngx_http_v2_node_t *node;
+ ngx_http_v2_stream_t *stream;
+
+ if (h2c->state.length != NGX_HTTP_V2_WINDOW_UPDATE_SIZE) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent WINDOW_UPDATE frame "
+ "with incorrect length %uz", h2c->state.length);
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
+ }
+
+ if (end - pos < NGX_HTTP_V2_WINDOW_UPDATE_SIZE) {
+ return ngx_http_v2_state_save(h2c, pos, end,
+ ngx_http_v2_state_window_update);
+ }
+
+ window = ngx_http_v2_parse_window(pos);
+
+ pos += NGX_HTTP_V2_WINDOW_UPDATE_SIZE;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 WINDOW_UPDATE frame sid:%ui window:%uz",
+ h2c->state.sid, window);
+
+ if (window == 0) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent WINDOW_UPDATE frame "
+ "with incorrect window increment 0");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
+ }
+
+ if (h2c->state.sid) {
+ node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 0);
+
+ if (node == NULL || node->stream == NULL) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "unknown http2 stream");
+
+ return ngx_http_v2_state_complete(h2c, pos, end);
+ }
+
+ stream = node->stream;
+
+ if (window > (size_t) (NGX_HTTP_V2_MAX_WINDOW - stream->send_window)) {
+
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client violated flow control for stream %ui: "
+ "received WINDOW_UPDATE frame "
+ "with window increment %uz "
+ "not allowed for window %z",
+ h2c->state.sid, window, stream->send_window);
+
+ if (ngx_http_v2_terminate_stream(h2c, stream,
+ NGX_HTTP_V2_FLOW_CTRL_ERROR)
+ == NGX_ERROR)
+ {
+ return ngx_http_v2_connection_error(h2c,
+ NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ return ngx_http_v2_state_complete(h2c, pos, end);
+ }
+
+ stream->send_window += window;
+
+ if (stream->exhausted) {
+ stream->exhausted = 0;
+
+ wev = stream->request->connection->write;
+
+ wev->active = 0;
+ wev->ready = 1;
+
+ if (!wev->delayed) {
+ wev->handler(wev);
+ }
+ }
+
+ return ngx_http_v2_state_complete(h2c, pos, end);
+ }
+
+ if (window > NGX_HTTP_V2_MAX_WINDOW - h2c->send_window) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client violated connection flow control: "
+ "received WINDOW_UPDATE frame "
+ "with window increment %uz "
+ "not allowed for window %uz",
+ window, h2c->send_window);
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_FLOW_CTRL_ERROR);
+ }
+
+ h2c->send_window += window;
+
+ while (!ngx_queue_empty(&h2c->waiting)) {
+ q = ngx_queue_head(&h2c->waiting);
+
+ ngx_queue_remove(q);
+
+ stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue);
+
+ stream->waiting = 0;
+
+ wev = stream->request->connection->write;
+
+ wev->active = 0;
+ wev->ready = 1;
+
+ if (!wev->delayed) {
+ wev->handler(wev);
+
+ if (h2c->send_window == 0) {
+ break;
+ }
+ }
+ }
+
+ return ngx_http_v2_state_complete(h2c, pos, end);
+}
+
+
+static u_char *
+ngx_http_v2_state_continuation(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end)
+{
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent unexpected CONTINUATION frame");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
+}
+
+
+static u_char *
+ngx_http_v2_state_complete(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end)
+{
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 frame complete pos:%p end:%p", pos, end);
+
+ if (pos > end) {
+ ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0,
+ "receive buffer overrun");
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ h2c->state.stream = NULL;
+ h2c->state.handler = ngx_http_v2_state_head;
+
+ return pos;
+}
+
+
+static u_char *
+ngx_http_v2_state_skip_padded(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end)
+{
+ h2c->state.length += h2c->state.padding;
+ h2c->state.padding = 0;
+
+ return ngx_http_v2_state_skip(h2c, pos, end);
+}
+
+
+static u_char *
+ngx_http_v2_state_skip(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)
+{
+ size_t size;
+
+ size = end - pos;
+
+ if (size < h2c->state.length) {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 frame skip %uz of %uz", size, h2c->state.length);
+
+ h2c->state.length -= size;
+ return ngx_http_v2_state_save(h2c, end, end, ngx_http_v2_state_skip);
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 frame skip %uz", h2c->state.length);
+
+ return ngx_http_v2_state_complete(h2c, pos + h2c->state.length, end);
+}
+
+
+static u_char *
+ngx_http_v2_state_save(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end,
+ ngx_http_v2_handler_pt handler)
+{
+ size_t size;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 frame state save pos:%p end:%p handler:%p",
+ pos, end, handler);
+
+ size = end - pos;
+
+ if (size > NGX_HTTP_V2_STATE_BUFFER_SIZE) {
+ ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0,
+ "state buffer overflow: %uz bytes required", size);
+
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ ngx_memcpy(h2c->state.buffer, pos, NGX_HTTP_V2_STATE_BUFFER_SIZE);
+
+ h2c->state.buffer_used = size;
+ h2c->state.handler = handler;
+ h2c->state.incomplete = 1;
+
+ return end;
+}
+
+
+static u_char *
+ngx_http_v2_state_headers_save(ngx_http_v2_connection_t *h2c, u_char *pos,
+ u_char *end, ngx_http_v2_handler_pt handler)
+{
+ ngx_event_t *rev;
+ ngx_http_request_t *r;
+ ngx_http_core_srv_conf_t *cscf;
+
+ if (h2c->state.stream) {
+ r = h2c->state.stream->request;
+ rev = r->connection->read;
+
+ if (!rev->timer_set) {
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+ ngx_add_timer(rev, cscf->client_header_timeout);
+ }
+ }
+
+ return ngx_http_v2_state_save(h2c, pos, end, handler);
+}
+
+
+static u_char *
+ngx_http_v2_connection_error(ngx_http_v2_connection_t *h2c,
+ ngx_uint_t err)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 state connection error");
+
+ ngx_http_v2_finalize_connection(h2c, err);
+
+ return NULL;
+}
+
+
+static ngx_int_t
+ngx_http_v2_parse_int(ngx_http_v2_connection_t *h2c, u_char **pos, u_char *end,
+ ngx_uint_t prefix)
+{
+ u_char *start, *p;
+ ngx_uint_t value, octet, shift;
+
+ start = *pos;
+ p = start;
+
+ value = *p++ & prefix;
+
+ if (value != prefix) {
+ if (h2c->state.length == 0) {
+ return NGX_ERROR;
+ }
+
+ h2c->state.length--;
+
+ *pos = p;
+ return value;
+ }
+
+ if (end - start > NGX_HTTP_V2_INT_OCTETS) {
+ end = start + NGX_HTTP_V2_INT_OCTETS;
+ }
+
+ for (shift = 0; p != end; shift += 7) {
+ octet = *p++;
+
+ value += (octet & 0x7f) << shift;
+
+ if (octet < 128) {
+ if ((size_t) (p - start) > h2c->state.length) {
+ return NGX_ERROR;
+ }
+
+ h2c->state.length -= p - start;
+
+ *pos = p;
+ return value;
+ }
+ }
+
+ if ((size_t) (end - start) >= h2c->state.length) {
+ return NGX_ERROR;
+ }
+
+ if (end == start + NGX_HTTP_V2_INT_OCTETS) {
+ return NGX_DECLINED;
+ }
+
+ return NGX_AGAIN;
+}
+
+
+ngx_http_v2_stream_t *
+ngx_http_v2_push_stream(ngx_http_v2_stream_t *parent, ngx_str_t *path)
+{
+ ngx_int_t rc;
+ ngx_str_t value;
+ ngx_pool_t *pool;
+ ngx_uint_t index;
+ ngx_table_elt_t **h;
+ ngx_connection_t *fc;
+ ngx_http_request_t *r;
+ ngx_http_v2_node_t *node;
+ ngx_http_v2_stream_t *stream;
+ ngx_http_v2_srv_conf_t *h2scf;
+ ngx_http_v2_connection_t *h2c;
+ ngx_http_v2_parse_header_t *header;
+
+ h2c = parent->connection;
+
+ pool = ngx_create_pool(1024, h2c->connection->log);
+ if (pool == NULL) {
+ goto rst_stream;
+ }
+
+ node = ngx_http_v2_get_node_by_id(h2c, h2c->last_push, 1);
+
+ if (node == NULL) {
+ ngx_destroy_pool(pool);
+ goto rst_stream;
+ }
+
+ stream = ngx_http_v2_create_stream(h2c, 1);
+ if (stream == NULL) {
+
+ if (node->parent == NULL) {
+ h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
+ ngx_http_v2_module);
+
+ index = ngx_http_v2_index(h2scf, h2c->last_push);
+ h2c->streams_index[index] = node->index;
+
+ ngx_queue_insert_tail(&h2c->closed, &node->reuse);
+ h2c->closed_nodes++;
+ }
+
+ ngx_destroy_pool(pool);
+ goto rst_stream;
+ }
+
+ if (node->parent) {
+ ngx_queue_remove(&node->reuse);
+ h2c->closed_nodes--;
+ }
+
+ stream->pool = pool;
+
+ r = stream->request;
+ fc = r->connection;
+
+ stream->in_closed = 1;
+ stream->node = node;
+
+ node->stream = stream;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 push stream sid:%ui "
+ "depends on %ui excl:0 weight:16",
+ h2c->last_push, parent->node->id);
+
+ node->weight = NGX_HTTP_V2_DEFAULT_WEIGHT;
+ ngx_http_v2_set_dependency(h2c, node, parent->node->id, 0);
+
+ r->method_name = ngx_http_core_get_method;
+ r->method = NGX_HTTP_GET;
+
+ r->schema.data = ngx_pstrdup(pool, &parent->request->schema);
+ if (r->schema.data == NULL) {
+ goto close;
+ }
+
+ r->schema.len = parent->request->schema.len;
+
+ value.data = ngx_pstrdup(pool, path);
+ if (value.data == NULL) {
+ goto close;
+ }
+
+ value.len = path->len;
+
+ rc = ngx_http_v2_parse_path(r, &value);
+
+ if (rc != NGX_OK) {
+ goto error;
+ }
+
+ for (header = ngx_http_v2_parse_headers; header->name.len; header++) {
+ h = (ngx_table_elt_t **)
+ ((char *) &parent->request->headers_in + header->offset);
+
+ if (*h == NULL) {
+ continue;
+ }
+
+ value.len = (*h)->value.len;
+
+ value.data = ngx_pnalloc(pool, value.len + 1);
+ if (value.data == NULL) {
+ goto close;
+ }
+
+ ngx_memcpy(value.data, (*h)->value.data, value.len);
+ value.data[value.len] = '\0';
+
+ rc = ngx_http_v2_parse_header(r, header, &value);
+
+ if (rc != NGX_OK) {
+ goto error;
+ }
+ }
+
+ fc->write->handler = ngx_http_v2_run_request_handler;
+ ngx_post_event(fc->write, &ngx_posted_events);
+
+ return stream;
+
+error:
+
+ if (rc == NGX_ABORT) {
+ /* header handler has already finalized request */
+ ngx_http_run_posted_requests(fc);
+ return NULL;
+ }
+
+ if (rc == NGX_DECLINED) {
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ ngx_http_run_posted_requests(fc);
+ return NULL;
+ }
+
+close:
+
+ ngx_http_v2_close_stream(stream, NGX_HTTP_INTERNAL_SERVER_ERROR);
+
+ return NULL;
+
+rst_stream:
+
+ if (ngx_http_v2_send_rst_stream(h2c, h2c->last_push,
+ NGX_HTTP_INTERNAL_SERVER_ERROR)
+ != NGX_OK)
+ {
+ h2c->connection->error = 1;
+ }
+
+ return NULL;
+}
+
+
+static ngx_int_t
+ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c)
+{
+ size_t len;
+ ngx_buf_t *buf;
+ ngx_chain_t *cl;
+ ngx_http_v2_srv_conf_t *h2scf;
+ ngx_http_v2_out_frame_t *frame;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 send SETTINGS frame");
+
+ frame = ngx_palloc(h2c->pool, sizeof(ngx_http_v2_out_frame_t));
+ if (frame == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl = ngx_alloc_chain_link(h2c->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ len = NGX_HTTP_V2_SETTINGS_PARAM_SIZE * 3;
+
+ buf = ngx_create_temp_buf(h2c->pool, NGX_HTTP_V2_FRAME_HEADER_SIZE + len);
+ if (buf == NULL) {
+ return NGX_ERROR;
+ }
+
+ buf->last_buf = 1;
+
+ cl->buf = buf;
+ cl->next = NULL;
+
+ frame->first = cl;
+ frame->last = cl;
+ frame->handler = ngx_http_v2_settings_frame_handler;
+ frame->stream = NULL;
+#if (NGX_DEBUG)
+ frame->length = len;
+#endif
+ frame->blocked = 0;
+
+ buf->last = ngx_http_v2_write_len_and_type(buf->last, len,
+ NGX_HTTP_V2_SETTINGS_FRAME);
+
+ *buf->last++ = NGX_HTTP_V2_NO_FLAG;
+
+ buf->last = ngx_http_v2_write_sid(buf->last, 0);
+
+ h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
+ ngx_http_v2_module);
+
+ buf->last = ngx_http_v2_write_uint16(buf->last,
+ NGX_HTTP_V2_MAX_STREAMS_SETTING);
+ buf->last = ngx_http_v2_write_uint32(buf->last,
+ h2scf->concurrent_streams);
+
+ buf->last = ngx_http_v2_write_uint16(buf->last,
+ NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING);
+ buf->last = ngx_http_v2_write_uint32(buf->last, h2scf->preread_size);
+
+ buf->last = ngx_http_v2_write_uint16(buf->last,
+ NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING);
+ buf->last = ngx_http_v2_write_uint32(buf->last,
+ NGX_HTTP_V2_MAX_FRAME_SIZE);
+
+ ngx_http_v2_queue_blocked_frame(h2c, frame);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_v2_settings_frame_handler(ngx_http_v2_connection_t *h2c,
+ ngx_http_v2_out_frame_t *frame)
+{
+ ngx_buf_t *buf;
+
+ buf = frame->first->buf;
+
+ if (buf->pos != buf->last) {
+ return NGX_AGAIN;
+ }
+
+ ngx_free_chain(h2c->pool, frame->first);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_v2_send_window_update(ngx_http_v2_connection_t *h2c, ngx_uint_t sid,
+ size_t window)
+{
+ ngx_buf_t *buf;
+ ngx_http_v2_out_frame_t *frame;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 send WINDOW_UPDATE frame sid:%ui, window:%uz",
+ sid, window);
+
+ frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_WINDOW_UPDATE_SIZE,
+ NGX_HTTP_V2_WINDOW_UPDATE_FRAME,
+ NGX_HTTP_V2_NO_FLAG, sid);
+ if (frame == NULL) {
+ return NGX_ERROR;
+ }
+
+ buf = frame->first->buf;
+
+ buf->last = ngx_http_v2_write_uint32(buf->last, window);
+
+ ngx_http_v2_queue_blocked_frame(h2c, frame);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_v2_send_rst_stream(ngx_http_v2_connection_t *h2c, ngx_uint_t sid,
+ ngx_uint_t status)
+{
+ ngx_buf_t *buf;
+ ngx_http_v2_out_frame_t *frame;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 send RST_STREAM frame sid:%ui, status:%ui",
+ sid, status);
+
+ frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_RST_STREAM_SIZE,
+ NGX_HTTP_V2_RST_STREAM_FRAME,
+ NGX_HTTP_V2_NO_FLAG, sid);
+ if (frame == NULL) {
+ return NGX_ERROR;
+ }
+
+ buf = frame->first->buf;
+
+ buf->last = ngx_http_v2_write_uint32(buf->last, status);
+
+ ngx_http_v2_queue_blocked_frame(h2c, frame);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_v2_send_goaway(ngx_http_v2_connection_t *h2c, ngx_uint_t status)
+{
+ ngx_buf_t *buf;
+ ngx_http_v2_out_frame_t *frame;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 send GOAWAY frame: last sid %ui, error %ui",
+ h2c->last_sid, status);
+
+ frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_GOAWAY_SIZE,
+ NGX_HTTP_V2_GOAWAY_FRAME,
+ NGX_HTTP_V2_NO_FLAG, 0);
+ if (frame == NULL) {
+ return NGX_ERROR;
+ }
+
+ buf = frame->first->buf;
+
+ buf->last = ngx_http_v2_write_sid(buf->last, h2c->last_sid);
+ buf->last = ngx_http_v2_write_uint32(buf->last, status);
+
+ ngx_http_v2_queue_blocked_frame(h2c, frame);
+
+ return NGX_OK;
+}
+
+
+static ngx_http_v2_out_frame_t *
+ngx_http_v2_get_frame(ngx_http_v2_connection_t *h2c, size_t length,
+ ngx_uint_t type, u_char flags, ngx_uint_t sid)
+{
+ ngx_buf_t *buf;
+ ngx_pool_t *pool;
+ ngx_http_v2_out_frame_t *frame;
+
+ frame = h2c->free_frames;
+
+ if (frame) {
+ h2c->free_frames = frame->next;
+
+ buf = frame->first->buf;
+ buf->pos = buf->start;
+
+ frame->blocked = 0;
+
+ } else if (h2c->frames < 10000) {
+ pool = h2c->pool ? h2c->pool : h2c->connection->pool;
+
+ frame = ngx_pcalloc(pool, sizeof(ngx_http_v2_out_frame_t));
+ if (frame == NULL) {
+ return NULL;
+ }
+
+ frame->first = ngx_alloc_chain_link(pool);
+ if (frame->first == NULL) {
+ return NULL;
+ }
+
+ buf = ngx_create_temp_buf(pool, NGX_HTTP_V2_FRAME_BUFFER_SIZE);
+ if (buf == NULL) {
+ return NULL;
+ }
+
+ buf->last_buf = 1;
+
+ frame->first->buf = buf;
+ frame->last = frame->first;
+
+ frame->handler = ngx_http_v2_frame_handler;
+
+ h2c->frames++;
+
+ } else {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "http2 flood detected");
+
+ h2c->connection->error = 1;
+ return NULL;
+ }
+
+#if (NGX_DEBUG)
+ if (length > NGX_HTTP_V2_FRAME_BUFFER_SIZE - NGX_HTTP_V2_FRAME_HEADER_SIZE)
+ {
+ ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0,
+ "requested control frame is too large: %uz", length);
+ return NULL;
+ }
+#endif
+
+ frame->length = length;
+
+ buf->last = ngx_http_v2_write_len_and_type(buf->pos, length, type);
+
+ *buf->last++ = flags;
+
+ buf->last = ngx_http_v2_write_sid(buf->last, sid);
+
+ return frame;
+}
+
+
+static ngx_int_t
+ngx_http_v2_frame_handler(ngx_http_v2_connection_t *h2c,
+ ngx_http_v2_out_frame_t *frame)
+{
+ ngx_buf_t *buf;
+
+ buf = frame->first->buf;
+
+ if (buf->pos != buf->last) {
+ return NGX_AGAIN;
+ }
+
+ frame->next = h2c->free_frames;
+ h2c->free_frames = frame;
+
+ h2c->total_bytes += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length;
+
+ return NGX_OK;
+}
+
+
+static ngx_http_v2_stream_t *
+ngx_http_v2_create_stream(ngx_http_v2_connection_t *h2c, ngx_uint_t push)
+{
+ ngx_log_t *log;
+ ngx_event_t *rev, *wev;
+ ngx_connection_t *fc;
+ ngx_http_log_ctx_t *ctx;
+ ngx_http_request_t *r;
+ ngx_http_v2_stream_t *stream;
+ ngx_http_v2_srv_conf_t *h2scf;
+ ngx_http_core_srv_conf_t *cscf;
+
+ fc = h2c->free_fake_connections;
+
+ if (fc) {
+ h2c->free_fake_connections = fc->data;
+
+ rev = fc->read;
+ wev = fc->write;
+ log = fc->log;
+ ctx = log->data;
+
+ } else {
+ fc = ngx_palloc(h2c->pool, sizeof(ngx_connection_t));
+ if (fc == NULL) {
+ return NULL;
+ }
+
+ rev = ngx_palloc(h2c->pool, sizeof(ngx_event_t));
+ if (rev == NULL) {
+ return NULL;
+ }
+
+ wev = ngx_palloc(h2c->pool, sizeof(ngx_event_t));
+ if (wev == NULL) {
+ return NULL;
+ }
+
+ log = ngx_palloc(h2c->pool, sizeof(ngx_log_t));
+ if (log == NULL) {
+ return NULL;
+ }
+
+ ctx = ngx_palloc(h2c->pool, sizeof(ngx_http_log_ctx_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ ctx->connection = fc;
+ ctx->request = NULL;
+ ctx->current_request = NULL;
+ }
+
+ ngx_memcpy(log, h2c->connection->log, sizeof(ngx_log_t));
+
+ log->data = ctx;
+
+ if (push) {
+ log->action = "processing pushed request headers";
+
+ } else {
+ log->action = "reading client request headers";
+ }
+
+ ngx_memzero(rev, sizeof(ngx_event_t));
+
+ rev->data = fc;
+ rev->ready = 1;
+ rev->handler = ngx_http_v2_close_stream_handler;
+ rev->log = log;
+
+ ngx_memcpy(wev, rev, sizeof(ngx_event_t));
+
+ wev->write = 1;
+
+ ngx_memcpy(fc, h2c->connection, sizeof(ngx_connection_t));
+
+ fc->data = h2c->http_connection;
+ fc->read = rev;
+ fc->write = wev;
+ fc->sent = 0;
+ fc->log = log;
+ fc->buffered = 0;
+ fc->sndlowat = 1;
+ fc->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;
+
+ r = ngx_http_create_request(fc);
+ if (r == NULL) {
+ return NULL;
+ }
+
+ ngx_str_set(&r->http_protocol, "HTTP/2.0");
+
+ r->http_version = NGX_HTTP_VERSION_20;
+ r->valid_location = 1;
+
+ fc->data = r;
+ h2c->connection->requests++;
+
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+ r->header_in = ngx_create_temp_buf(r->pool,
+ cscf->client_header_buffer_size);
+ if (r->header_in == NULL) {
+ ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NULL;
+ }
+
+ if (ngx_list_init(&r->headers_in.headers, r->pool, 20,
+ sizeof(ngx_table_elt_t))
+ != NGX_OK)
+ {
+ ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NULL;
+ }
+
+ r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;
+
+ stream = ngx_pcalloc(r->pool, sizeof(ngx_http_v2_stream_t));
+ if (stream == NULL) {
+ ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NULL;
+ }
+
+ r->stream = stream;
+
+ stream->request = r;
+ stream->connection = h2c;
+
+ h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module);
+
+ stream->send_window = h2c->init_window;
+ stream->recv_window = h2scf->preread_size;
+
+ if (push) {
+ h2c->pushing++;
+
+ } else {
+ h2c->processing++;
+ }
+
+ h2c->priority_limit += h2scf->concurrent_streams;
+
+ return stream;
+}
+
+
+static ngx_http_v2_node_t *
+ngx_http_v2_get_node_by_id(ngx_http_v2_connection_t *h2c, ngx_uint_t sid,
+ ngx_uint_t alloc)
+{
+ ngx_uint_t index;
+ ngx_http_v2_node_t *node;
+ ngx_http_v2_srv_conf_t *h2scf;
+
+ h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
+ ngx_http_v2_module);
+
+ index = ngx_http_v2_index(h2scf, sid);
+
+ for (node = h2c->streams_index[index]; node; node = node->index) {
+
+ if (node->id == sid) {
+ return node;
+ }
+ }
+
+ if (!alloc) {
+ return NULL;
+ }
+
+ if (h2c->closed_nodes < 32) {
+ node = ngx_pcalloc(h2c->connection->pool, sizeof(ngx_http_v2_node_t));
+ if (node == NULL) {
+ return NULL;
+ }
+
+ } else {
+ node = ngx_http_v2_get_closed_node(h2c);
+ }
+
+ node->id = sid;
+
+ ngx_queue_init(&node->children);
+
+ node->index = h2c->streams_index[index];
+ h2c->streams_index[index] = node;
+
+ return node;
+}
+
+
+static ngx_http_v2_node_t *
+ngx_http_v2_get_closed_node(ngx_http_v2_connection_t *h2c)
+{
+ ngx_uint_t weight;
+ ngx_queue_t *q, *children;
+ ngx_http_v2_node_t *node, **next, *n, *parent, *child;
+ ngx_http_v2_srv_conf_t *h2scf;
+
+ h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
+ ngx_http_v2_module);
+
+ h2c->closed_nodes--;
+
+ q = ngx_queue_head(&h2c->closed);
+
+ ngx_queue_remove(q);
+
+ node = ngx_queue_data(q, ngx_http_v2_node_t, reuse);
+
+ next = &h2c->streams_index[ngx_http_v2_index(h2scf, node->id)];
+
+ for ( ;; ) {
+ n = *next;
+
+ if (n == node) {
+ *next = n->index;
+ break;
+ }
+
+ next = &n->index;
+ }
+
+ ngx_queue_remove(&node->queue);
+
+ weight = 0;
+
+ for (q = ngx_queue_head(&node->children);
+ q != ngx_queue_sentinel(&node->children);
+ q = ngx_queue_next(q))
+ {
+ child = ngx_queue_data(q, ngx_http_v2_node_t, queue);
+ weight += child->weight;
+ }
+
+ parent = node->parent;
+
+ for (q = ngx_queue_head(&node->children);
+ q != ngx_queue_sentinel(&node->children);
+ q = ngx_queue_next(q))
+ {
+ child = ngx_queue_data(q, ngx_http_v2_node_t, queue);
+ child->parent = parent;
+ child->weight = node->weight * child->weight / weight;
+
+ if (child->weight == 0) {
+ child->weight = 1;
+ }
+ }
+
+ if (parent == NGX_HTTP_V2_ROOT) {
+ node->rank = 0;
+ node->rel_weight = 1.0;
+
+ children = &h2c->dependencies;
+
+ } else {
+ node->rank = parent->rank;
+ node->rel_weight = parent->rel_weight;
+
+ children = &parent->children;
+ }
+
+ ngx_http_v2_node_children_update(node);
+ ngx_queue_add(children, &node->children);
+
+ ngx_memzero(node, sizeof(ngx_http_v2_node_t));
+
+ return node;
+}
+
+
+static ngx_int_t
+ngx_http_v2_validate_header(ngx_http_request_t *r, ngx_http_v2_header_t *header)
+{
+ u_char ch;
+ ngx_uint_t i;
+ ngx_http_core_srv_conf_t *cscf;
+
+ r->invalid_header = 0;
+
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+ for (i = (header->name.data[0] == ':'); i != header->name.len; i++) {
+ ch = header->name.data[i];
+
+ if ((ch >= 'a' && ch <= 'z')
+ || (ch == '-')
+ || (ch >= '0' && ch <= '9')
+ || (ch == '_' && cscf->underscores_in_headers))
+ {
+ continue;
+ }
+
+ if (ch == '\0' || ch == LF || ch == CR || ch == ':'
+ || (ch >= 'A' && ch <= 'Z'))
+ {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent invalid header name: \"%V\"",
+ &header->name);
+
+ return NGX_ERROR;
+ }
+
+ r->invalid_header = 1;
+ }
+
+ for (i = 0; i != header->value.len; i++) {
+ ch = header->value.data[i];
+
+ if (ch == '\0' || ch == LF || ch == CR) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent header \"%V\" with "
+ "invalid value: \"%V\"",
+ &header->name, &header->value);
+
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_v2_pseudo_header(ngx_http_request_t *r, ngx_http_v2_header_t *header)
+{
+ header->name.len--;
+ header->name.data++;
+
+ switch (header->name.len) {
+ case 4:
+ if (ngx_memcmp(header->name.data, "path", sizeof("path") - 1)
+ == 0)
+ {
+ return ngx_http_v2_parse_path(r, &header->value);
+ }
+
+ break;
+
+ case 6:
+ if (ngx_memcmp(header->name.data, "method", sizeof("method") - 1)
+ == 0)
+ {
+ return ngx_http_v2_parse_method(r, &header->value);
+ }
+
+ if (ngx_memcmp(header->name.data, "scheme", sizeof("scheme") - 1)
+ == 0)
+ {
+ return ngx_http_v2_parse_scheme(r, &header->value);
+ }
+
+ break;
+
+ case 9:
+ if (ngx_memcmp(header->name.data, "authority", sizeof("authority") - 1)
+ == 0)
+ {
+ return ngx_http_v2_parse_authority(r, &header->value);
+ }
+
+ break;
+ }
+
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent unknown pseudo-header \":%V\"",
+ &header->name);
+
+ return NGX_DECLINED;
+}
+
+
+static ngx_int_t
+ngx_http_v2_parse_path(ngx_http_request_t *r, ngx_str_t *value)
+{
+ if (r->unparsed_uri.len) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent duplicate :path header");
+
+ return NGX_DECLINED;
+ }
+
+ if (value->len == 0) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent empty :path header");
+
+ return NGX_DECLINED;
+ }
+
+ r->uri_start = value->data;
+ r->uri_end = value->data + value->len;
+
+ if (ngx_http_parse_uri(r) != NGX_OK) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent invalid :path header: \"%V\"", value);
+
+ return NGX_DECLINED;
+ }
+
+ if (ngx_http_process_request_uri(r) != NGX_OK) {
+ /*
+ * request has been finalized already
+ * in ngx_http_process_request_uri()
+ */
+ return NGX_ABORT;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_v2_parse_method(ngx_http_request_t *r, ngx_str_t *value)
+{
+ size_t k, len;
+ ngx_uint_t n;
+ const u_char *p, *m;
+
+ /*
+ * This array takes less than 256 sequential bytes,
+ * and if typical CPU cache line size is 64 bytes,
+ * it is prefetched for 4 load operations.
+ */
+ static const struct {
+ u_char len;
+ const u_char method[11];
+ uint32_t value;
+ } tests[] = {
+ { 3, "GET", NGX_HTTP_GET },
+ { 4, "POST", NGX_HTTP_POST },
+ { 4, "HEAD", NGX_HTTP_HEAD },
+ { 7, "OPTIONS", NGX_HTTP_OPTIONS },
+ { 8, "PROPFIND", NGX_HTTP_PROPFIND },
+ { 3, "PUT", NGX_HTTP_PUT },
+ { 5, "MKCOL", NGX_HTTP_MKCOL },
+ { 6, "DELETE", NGX_HTTP_DELETE },
+ { 4, "COPY", NGX_HTTP_COPY },
+ { 4, "MOVE", NGX_HTTP_MOVE },
+ { 9, "PROPPATCH", NGX_HTTP_PROPPATCH },
+ { 4, "LOCK", NGX_HTTP_LOCK },
+ { 6, "UNLOCK", NGX_HTTP_UNLOCK },
+ { 5, "PATCH", NGX_HTTP_PATCH },
+ { 5, "TRACE", NGX_HTTP_TRACE }
+ }, *test;
+
+ if (r->method_name.len) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent duplicate :method header");
+
+ return NGX_DECLINED;
+ }
+
+ if (value->len == 0) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent empty :method header");
+
+ return NGX_DECLINED;
+ }
+
+ r->method_name.len = value->len;
+ r->method_name.data = value->data;
+
+ len = r->method_name.len;
+ n = sizeof(tests) / sizeof(tests[0]);
+ test = tests;
+
+ do {
+ if (len == test->len) {
+ p = r->method_name.data;
+ m = test->method;
+ k = len;
+
+ do {
+ if (*p++ != *m++) {
+ goto next;
+ }
+ } while (--k);
+
+ r->method = test->value;
+ return NGX_OK;
+ }
+
+ next:
+ test++;
+
+ } while (--n);
+
+ p = r->method_name.data;
+
+ do {
+ if ((*p < 'A' || *p > 'Z') && *p != '_' && *p != '-') {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent invalid method: \"%V\"",
+ &r->method_name);
+
+ return NGX_DECLINED;
+ }
+
+ p++;
+
+ } while (--len);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_v2_parse_scheme(ngx_http_request_t *r, ngx_str_t *value)
+{
+ u_char c, ch;
+ ngx_uint_t i;
+
+ if (r->schema.len) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent duplicate :scheme header");
+
+ return NGX_DECLINED;
+ }
+
+ if (value->len == 0) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent empty :scheme header");
+
+ return NGX_DECLINED;
+ }
+
+ for (i = 0; i < value->len; i++) {
+ ch = value->data[i];
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ continue;
+ }
+
+ if (((ch >= '0' && ch <= '9') || ch == '+' || ch == '-' || ch == '.')
+ && i > 0)
+ {
+ continue;
+ }
+
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent invalid :scheme header: \"%V\"", value);
+
+ return NGX_DECLINED;
+ }
+
+ r->schema = *value;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_v2_parse_authority(ngx_http_request_t *r, ngx_str_t *value)
+{
+ return ngx_http_v2_parse_header(r, &ngx_http_v2_parse_headers[0], value);
+}
+
+
+static ngx_int_t
+ngx_http_v2_parse_header(ngx_http_request_t *r,
+ ngx_http_v2_parse_header_t *header, ngx_str_t *value)
+{
+ ngx_table_elt_t *h;
+ ngx_http_core_main_conf_t *cmcf;
+
+ h = ngx_list_push(&r->headers_in.headers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->key.len = header->name.len;
+ h->key.data = header->name.data;
+ h->lowcase_key = header->name.data;
+
+ if (header->hh == NULL) {
+ header->hash = ngx_hash_key(header->name.data, header->name.len);
+
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+ header->hh = ngx_hash_find(&cmcf->headers_in_hash, header->hash,
+ h->lowcase_key, h->key.len);
+ if (header->hh == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ h->hash = header->hash;
+
+ h->value.len = value->len;
+ h->value.data = value->data;
+
+ if (header->hh->handler(r, h, header->hh->offset) != NGX_OK) {
+ /* header handler has already finalized request */
+ return NGX_ABORT;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_v2_construct_request_line(ngx_http_request_t *r)
+{
+ u_char *p;
+
+ static const u_char ending[] = " HTTP/2.0";
+
+ if (r->method_name.len == 0
+ || r->schema.len == 0
+ || r->unparsed_uri.len == 0)
+ {
+ if (r->method_name.len == 0) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent no :method header");
+
+ } else if (r->schema.len == 0) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent no :scheme header");
+
+ } else {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent no :path header");
+ }
+
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ return NGX_ERROR;
+ }
+
+ r->request_line.len = r->method_name.len + 1
+ + r->unparsed_uri.len
+ + sizeof(ending) - 1;
+
+ p = ngx_pnalloc(r->pool, r->request_line.len + 1);
+ if (p == NULL) {
+ ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_ERROR;
+ }
+
+ r->request_line.data = p;
+
+ p = ngx_cpymem(p, r->method_name.data, r->method_name.len);
+
+ *p++ = ' ';
+
+ p = ngx_cpymem(p, r->unparsed_uri.data, r->unparsed_uri.len);
+
+ ngx_memcpy(p, ending, sizeof(ending));
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http2 request line: \"%V\"", &r->request_line);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_v2_cookie(ngx_http_request_t *r, ngx_http_v2_header_t *header)
+{
+ ngx_str_t *val;
+ ngx_array_t *cookies;
+
+ cookies = r->stream->cookies;
+
+ if (cookies == NULL) {
+ cookies = ngx_array_create(r->pool, 2, sizeof(ngx_str_t));
+ if (cookies == NULL) {
+ return NGX_ERROR;
+ }
+
+ r->stream->cookies = cookies;
+ }
+
+ val = ngx_array_push(cookies);
+ if (val == NULL) {
+ return NGX_ERROR;
+ }
+
+ val->len = header->value.len;
+ val->data = header->value.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_v2_construct_cookie_header(ngx_http_request_t *r)
+{
+ u_char *buf, *p, *end;
+ size_t len;
+ ngx_str_t *vals;
+ ngx_uint_t i;
+ ngx_array_t *cookies;
+ ngx_table_elt_t *h;
+ ngx_http_header_t *hh;
+ ngx_http_core_main_conf_t *cmcf;
+
+ static ngx_str_t cookie = ngx_string("cookie");
+
+ cookies = r->stream->cookies;
+
+ if (cookies == NULL) {
+ return NGX_OK;
+ }
+
+ vals = cookies->elts;
+
+ i = 0;
+ len = 0;
+
+ do {
+ len += vals[i].len + 2;
+ } while (++i != cookies->nelts);
+
+ len -= 2;
+
+ buf = ngx_pnalloc(r->pool, len + 1);
+ if (buf == NULL) {
+ ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_ERROR;
+ }
+
+ p = buf;
+ end = buf + len;
+
+ for (i = 0; /* void */ ; i++) {
+
+ p = ngx_cpymem(p, vals[i].data, vals[i].len);
+
+ if (p == end) {
+ *p = '\0';
+ break;
+ }
+
+ *p++ = ';'; *p++ = ' ';
+ }
+
+ h = ngx_list_push(&r->headers_in.headers);
+ if (h == NULL) {
+ ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_ERROR;
+ }
+
+ h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(
+ ngx_hash('c', 'o'), 'o'), 'k'), 'i'), 'e');
+
+ h->key.len = cookie.len;
+ h->key.data = cookie.data;
+
+ h->value.len = len;
+ h->value.data = buf;
+
+ h->lowcase_key = cookie.data;
+
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+ hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,
+ h->lowcase_key, h->key.len);
+
+ if (hh == NULL) {
+ ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_ERROR;
+ }
+
+ if (hh->handler(r, h, hh->offset) != NGX_OK) {
+ /*
+ * request has been finalized already
+ * in ngx_http_process_multi_header_lines()
+ */
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_v2_run_request(ngx_http_request_t *r)
+{
+ ngx_connection_t *fc;
+ ngx_http_v2_connection_t *h2c;
+
+ fc = r->connection;
+
+ if (ngx_http_v2_construct_request_line(r) != NGX_OK) {
+ goto failed;
+ }
+
+ if (ngx_http_v2_construct_cookie_header(r) != NGX_OK) {
+ goto failed;
+ }
+
+ r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;
+
+ if (ngx_http_process_request_header(r) != NGX_OK) {
+ goto failed;
+ }
+
+ if (r->headers_in.content_length_n > 0 && r->stream->in_closed) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client prematurely closed stream");
+
+ r->stream->skip_data = 1;
+
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ goto failed;
+ }
+
+ if (r->headers_in.content_length_n == -1 && !r->stream->in_closed) {
+ r->headers_in.chunked = 1;
+ }
+
+ h2c = r->stream->connection;
+
+ h2c->payload_bytes += r->request_length;
+
+ ngx_http_process_request(r);
+
+failed:
+
+ ngx_http_run_posted_requests(fc);
+}
+
+
+static void
+ngx_http_v2_run_request_handler(ngx_event_t *ev)
+{
+ ngx_connection_t *fc;
+ ngx_http_request_t *r;
+
+ fc = ev->data;
+ r = fc->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 run request handler");
+
+ ngx_http_v2_run_request(r);
+}
+
+
+ngx_int_t
+ngx_http_v2_read_request_body(ngx_http_request_t *r)
+{
+ off_t len;
+ size_t size;
+ ngx_buf_t *buf;
+ ngx_int_t rc;
+ ngx_http_v2_stream_t *stream;
+ ngx_http_v2_srv_conf_t *h2scf;
+ ngx_http_request_body_t *rb;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_v2_connection_t *h2c;
+
+ stream = r->stream;
+ rb = r->request_body;
+
+ if (stream->skip_data) {
+ r->request_body_no_buffering = 0;
+ rb->post_handler(r);
+ return NGX_OK;
+ }
+
+ h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module);
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ len = r->headers_in.content_length_n;
+
+ if (r->request_body_no_buffering && !stream->in_closed) {
+
+ if (len < 0 || len > (off_t) clcf->client_body_buffer_size) {
+ len = clcf->client_body_buffer_size;
+ }
+
+ /*
+ * We need a room to store data up to the stream's initial window size,
+ * at least until this window will be exhausted.
+ */
+
+ if (len < (off_t) h2scf->preread_size) {
+ len = h2scf->preread_size;
+ }
+
+ if (len > NGX_HTTP_V2_MAX_WINDOW) {
+ len = NGX_HTTP_V2_MAX_WINDOW;
+ }
+
+ rb->buf = ngx_create_temp_buf(r->pool, (size_t) len);
+
+ } else if (len >= 0 && len <= (off_t) clcf->client_body_buffer_size
+ && !r->request_body_in_file_only)
+ {
+ rb->buf = ngx_create_temp_buf(r->pool, (size_t) len);
+
+ } else {
+ rb->buf = ngx_calloc_buf(r->pool);
+
+ if (rb->buf != NULL) {
+ rb->buf->sync = 1;
+ }
+ }
+
+ if (rb->buf == NULL) {
+ stream->skip_data = 1;
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ rb->rest = 1;
+
+ buf = stream->preread;
+
+ if (stream->in_closed) {
+ r->request_body_no_buffering = 0;
+
+ if (buf) {
+ rc = ngx_http_v2_process_request_body(r, buf->pos,
+ buf->last - buf->pos, 1);
+ ngx_pfree(r->pool, buf->start);
+ return rc;
+ }
+
+ return ngx_http_v2_process_request_body(r, NULL, 0, 1);
+ }
+
+ if (buf) {
+ rc = ngx_http_v2_process_request_body(r, buf->pos,
+ buf->last - buf->pos, 0);
+
+ ngx_pfree(r->pool, buf->start);
+
+ if (rc != NGX_OK) {
+ stream->skip_data = 1;
+ return rc;
+ }
+ }
+
+ if (r->request_body_no_buffering) {
+ size = (size_t) len - h2scf->preread_size;
+
+ } else {
+ stream->no_flow_control = 1;
+ size = NGX_HTTP_V2_MAX_WINDOW - stream->recv_window;
+ }
+
+ if (size) {
+ if (ngx_http_v2_send_window_update(stream->connection,
+ stream->node->id, size)
+ == NGX_ERROR)
+ {
+ stream->skip_data = 1;
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ h2c = stream->connection;
+
+ if (!h2c->blocked) {
+ if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) {
+ stream->skip_data = 1;
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+ }
+
+ stream->recv_window += size;
+ }
+
+ if (!buf) {
+ ngx_add_timer(r->connection->read, clcf->client_body_timeout);
+ }
+
+ r->read_event_handler = ngx_http_v2_read_client_request_body_handler;
+ r->write_event_handler = ngx_http_request_empty_handler;
+
+ return NGX_AGAIN;
+}
+
+
+static ngx_int_t
+ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos,
+ size_t size, ngx_uint_t last)
+{
+ ngx_buf_t *buf;
+ ngx_int_t rc;
+ ngx_connection_t *fc;
+ ngx_http_request_body_t *rb;
+ ngx_http_core_loc_conf_t *clcf;
+
+ fc = r->connection;
+ rb = r->request_body;
+ buf = rb->buf;
+
+ if (size) {
+ if (buf->sync) {
+ buf->pos = buf->start = pos;
+ buf->last = buf->end = pos + size;
+
+ r->request_body_in_file_only = 1;
+
+ } else {
+ if (size > (size_t) (buf->end - buf->last)) {
+ ngx_log_error(NGX_LOG_INFO, fc->log, 0,
+ "client intended to send body data "
+ "larger than declared");
+
+ return NGX_HTTP_BAD_REQUEST;
+ }
+
+ buf->last = ngx_cpymem(buf->last, pos, size);
+ }
+ }
+
+ if (last) {
+ rb->rest = 0;
+
+ if (fc->read->timer_set) {
+ ngx_del_timer(fc->read);
+ }
+
+ if (r->request_body_no_buffering) {
+ ngx_post_event(fc->read, &ngx_posted_events);
+ return NGX_OK;
+ }
+
+ rc = ngx_http_v2_filter_request_body(r);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ if (buf->sync) {
+ /* prevent reusing this buffer in the upstream module */
+ rb->buf = NULL;
+ }
+
+ if (r->headers_in.chunked) {
+ r->headers_in.content_length_n = rb->received;
+ }
+
+ r->read_event_handler = ngx_http_block_reading;
+ rb->post_handler(r);
+
+ return NGX_OK;
+ }
+
+ if (size == 0) {
+ return NGX_OK;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+ ngx_add_timer(fc->read, clcf->client_body_timeout);
+
+ if (r->request_body_no_buffering) {
+ ngx_post_event(fc->read, &ngx_posted_events);
+ return NGX_OK;
+ }
+
+ if (buf->sync) {
+ return ngx_http_v2_filter_request_body(r);
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_v2_filter_request_body(ngx_http_request_t *r)
+{
+ ngx_buf_t *b, *buf;
+ ngx_int_t rc;
+ ngx_chain_t *cl;
+ ngx_http_request_body_t *rb;
+ ngx_http_core_loc_conf_t *clcf;
+
+ rb = r->request_body;
+ buf = rb->buf;
+
+ if (buf->pos == buf->last && rb->rest) {
+ cl = NULL;
+ goto update;
+ }
+
+ cl = ngx_chain_get_free_buf(r->pool, &rb->free);
+ if (cl == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ b = cl->buf;
+
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ if (buf->pos != buf->last) {
+ r->request_length += buf->last - buf->pos;
+ rb->received += buf->last - buf->pos;
+
+ if (r->headers_in.content_length_n != -1) {
+ if (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;
+ }
+
+ } else {
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->client_max_body_size
+ && rb->received > clcf->client_max_body_size)
+ {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "client intended to send too large chunked body: "
+ "%O bytes", rb->received);
+
+ return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
+ }
+ }
+
+ b->temporary = 1;
+ b->pos = buf->pos;
+ b->last = buf->last;
+ b->start = b->pos;
+ b->end = b->last;
+
+ buf->pos = buf->last;
+ }
+
+ if (!rb->rest) {
+ if (r->headers_in.content_length_n != -1
+ && r->headers_in.content_length_n != rb->received)
+ {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client prematurely closed stream: "
+ "only %O out of %O bytes of request body received",
+ rb->received, r->headers_in.content_length_n);
+
+ return NGX_HTTP_BAD_REQUEST;
+ }
+
+ b->last_buf = 1;
+ }
+
+ b->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_request_body;
+ b->flush = r->request_body_no_buffering;
+
+update:
+
+ rc = ngx_http_top_request_body_filter(r, cl);
+
+ ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &cl,
+ (ngx_buf_tag_t) &ngx_http_v2_filter_request_body);
+
+ return rc;
+}
+
+
+static void
+ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r)
+{
+ ngx_connection_t *fc;
+
+ fc = r->connection;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 read client request body handler");
+
+ if (fc->read->timedout) {
+ ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, "client timed out");
+
+ fc->timedout = 1;
+ r->stream->skip_data = 1;
+
+ ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
+ return;
+ }
+
+ if (fc->error) {
+ ngx_log_error(NGX_LOG_INFO, fc->log, 0,
+ "client prematurely closed stream");
+
+ r->stream->skip_data = 1;
+
+ ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ return;
+ }
+}
+
+
+ngx_int_t
+ngx_http_v2_read_unbuffered_request_body(ngx_http_request_t *r)
+{
+ size_t window;
+ ngx_buf_t *buf;
+ ngx_int_t rc;
+ ngx_connection_t *fc;
+ ngx_http_v2_stream_t *stream;
+ ngx_http_v2_connection_t *h2c;
+ ngx_http_core_loc_conf_t *clcf;
+
+ stream = r->stream;
+ fc = r->connection;
+
+ if (fc->read->timedout) {
+ if (stream->recv_window) {
+ stream->skip_data = 1;
+ fc->timedout = 1;
+
+ return NGX_HTTP_REQUEST_TIME_OUT;
+ }
+
+ fc->read->timedout = 0;
+ }
+
+ if (fc->error) {
+ stream->skip_data = 1;
+ return NGX_HTTP_BAD_REQUEST;
+ }
+
+ rc = ngx_http_v2_filter_request_body(r);
+
+ if (rc != NGX_OK) {
+ stream->skip_data = 1;
+ return rc;
+ }
+
+ if (!r->request_body->rest) {
+ return NGX_OK;
+ }
+
+ if (r->request_body->busy != NULL) {
+ return NGX_AGAIN;
+ }
+
+ buf = r->request_body->buf;
+
+ buf->pos = buf->start;
+ buf->last = buf->start;
+
+ window = buf->end - buf->start;
+ h2c = stream->connection;
+
+ if (h2c->state.stream == stream) {
+ window -= h2c->state.length;
+ }
+
+ if (window <= stream->recv_window) {
+ if (window < stream->recv_window) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "http2 negative window update");
+ stream->skip_data = 1;
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ return NGX_AGAIN;
+ }
+
+ if (ngx_http_v2_send_window_update(h2c, stream->node->id,
+ window - stream->recv_window)
+ == NGX_ERROR)
+ {
+ stream->skip_data = 1;
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) {
+ stream->skip_data = 1;
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (stream->recv_window == 0) {
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+ ngx_add_timer(fc->read, clcf->client_body_timeout);
+ }
+
+ stream->recv_window = window;
+
+ return NGX_AGAIN;
+}
+
+
+static ngx_int_t
+ngx_http_v2_terminate_stream(ngx_http_v2_connection_t *h2c,
+ ngx_http_v2_stream_t *stream, ngx_uint_t status)
+{
+ ngx_event_t *rev;
+ ngx_connection_t *fc;
+
+ if (stream->rst_sent) {
+ return NGX_OK;
+ }
+
+ if (ngx_http_v2_send_rst_stream(h2c, stream->node->id, status)
+ == NGX_ERROR)
+ {
+ return NGX_ERROR;
+ }
+
+ stream->rst_sent = 1;
+ stream->skip_data = 1;
+
+ fc = stream->request->connection;
+ fc->error = 1;
+
+ rev = fc->read;
+ rev->handler(rev);
+
+ return NGX_OK;
+}
+
+
+void
+ngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc)
+{
+ ngx_pool_t *pool;
+ ngx_uint_t push;
+ ngx_event_t *ev;
+ ngx_connection_t *fc;
+ ngx_http_v2_node_t *node;
+ ngx_http_v2_connection_t *h2c;
+
+ h2c = stream->connection;
+ node = stream->node;
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 close stream %ui, queued %ui, "
+ "processing %ui, pushing %ui",
+ node->id, stream->queued, h2c->processing, h2c->pushing);
+
+ fc = stream->request->connection;
+
+ if (stream->queued) {
+ fc->error = 1;
+ fc->write->handler = ngx_http_v2_retry_close_stream_handler;
+ fc->read->handler = ngx_http_v2_retry_close_stream_handler;
+ return;
+ }
+
+ if (!stream->rst_sent && !h2c->connection->error) {
+
+ if (!stream->out_closed) {
+ if (ngx_http_v2_send_rst_stream(h2c, node->id,
+ fc->timedout ? NGX_HTTP_V2_PROTOCOL_ERROR
+ : NGX_HTTP_V2_INTERNAL_ERROR)
+ != NGX_OK)
+ {
+ h2c->connection->error = 1;
+ }
+
+ } else if (!stream->in_closed) {
+ if (ngx_http_v2_send_rst_stream(h2c, node->id, NGX_HTTP_V2_NO_ERROR)
+ != NGX_OK)
+ {
+ h2c->connection->error = 1;
+ }
+ }
+ }
+
+ if (h2c->state.stream == stream) {
+ h2c->state.stream = NULL;
+ }
+
+ push = stream->node->id % 2 == 0;
+
+ node->stream = NULL;
+
+ ngx_queue_insert_tail(&h2c->closed, &node->reuse);
+ h2c->closed_nodes++;
+
+ /*
+ * This pool keeps decoded request headers which can be used by log phase
+ * handlers in ngx_http_free_request().
+ *
+ * The pointer is stored into local variable because the stream object
+ * will be destroyed after a call to ngx_http_free_request().
+ */
+ pool = stream->pool;
+
+ h2c->frames -= stream->frames;
+
+ ngx_http_free_request(stream->request, rc);
+
+ if (pool != h2c->state.pool) {
+ ngx_destroy_pool(pool);
+
+ } else {
+ /* pool will be destroyed when the complete header is parsed */
+ h2c->state.keep_pool = 0;
+ }
+
+ ev = fc->read;
+
+ if (ev->timer_set) {
+ ngx_del_timer(ev);
+ }
+
+ if (ev->posted) {
+ ngx_delete_posted_event(ev);
+ }
+
+ ev = fc->write;
+
+ if (ev->timer_set) {
+ ngx_del_timer(ev);
+ }
+
+ if (ev->posted) {
+ ngx_delete_posted_event(ev);
+ }
+
+ fc->data = h2c->free_fake_connections;
+ h2c->free_fake_connections = fc;
+
+ if (push) {
+ h2c->pushing--;
+
+ } else {
+ h2c->processing--;
+ }
+
+ if (h2c->processing || h2c->pushing || h2c->blocked) {
+ return;
+ }
+
+ ev = h2c->connection->read;
+
+ ev->handler = ngx_http_v2_handle_connection_handler;
+ ngx_post_event(ev, &ngx_posted_events);
+}
+
+
+static void
+ngx_http_v2_close_stream_handler(ngx_event_t *ev)
+{
+ ngx_connection_t *fc;
+ ngx_http_request_t *r;
+
+ fc = ev->data;
+ r = fc->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 close stream handler");
+
+ if (ev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, "client timed out");
+
+ fc->timedout = 1;
+
+ ngx_http_v2_close_stream(r->stream, NGX_HTTP_REQUEST_TIME_OUT);
+ return;
+ }
+
+ ngx_http_v2_close_stream(r->stream, 0);
+}
+
+
+static void
+ngx_http_v2_retry_close_stream_handler(ngx_event_t *ev)
+{
+ ngx_connection_t *fc;
+ ngx_http_request_t *r;
+
+ fc = ev->data;
+ r = fc->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 retry close stream handler");
+
+ ngx_http_v2_close_stream(r->stream, 0);
+}
+
+
+static void
+ngx_http_v2_handle_connection_handler(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_http_v2_connection_t *h2c;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "http2 handle connection handler");
+
+ c = rev->data;
+ h2c = c->data;
+
+ if (c->error) {
+ ngx_http_v2_finalize_connection(h2c, 0);
+ return;
+ }
+
+ rev->handler = ngx_http_v2_read_handler;
+
+ if (rev->ready) {
+ ngx_http_v2_read_handler(rev);
+ return;
+ }
+
+ if (h2c->last_out && ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) {
+ ngx_http_v2_finalize_connection(h2c, 0);
+ return;
+ }
+
+ ngx_http_v2_handle_connection(c->data);
+}
+
+
+static void
+ngx_http_v2_idle_handler(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_http_v2_srv_conf_t *h2scf;
+ ngx_http_v2_connection_t *h2c;
+
+ c = rev->data;
+ h2c = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http2 idle handler");
+
+ if (rev->timedout || c->close) {
+ ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_NO_ERROR);
+ return;
+ }
+
+#if (NGX_HAVE_KQUEUE)
+
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+ if (rev->pending_eof) {
+ c->log->handler = NULL;
+ ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno,
+ "kevent() reported that client %V closed "
+ "idle connection", &c->addr_text);
+#if (NGX_HTTP_SSL)
+ if (c->ssl) {
+ c->ssl->no_send_shutdown = 1;
+ }
+#endif
+ ngx_http_close_connection(c);
+ return;
+ }
+ }
+
+#endif
+
+ h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
+ ngx_http_v2_module);
+
+ if (h2c->idle++ > 10 * h2scf->max_requests) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "http2 flood detected");
+ ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_NO_ERROR);
+ return;
+ }
+
+ c->destroyed = 0;
+ ngx_reusable_connection(c, 0);
+
+ h2c->pool = ngx_create_pool(h2scf->pool_size, h2c->connection->log);
+ if (h2c->pool == NULL) {
+ ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
+ return;
+ }
+
+ c->write->handler = ngx_http_v2_write_handler;
+
+ rev->handler = ngx_http_v2_read_handler;
+ ngx_http_v2_read_handler(rev);
+}
+
+
+static void
+ngx_http_v2_finalize_connection(ngx_http_v2_connection_t *h2c,
+ ngx_uint_t status)
+{
+ ngx_uint_t i, size;
+ ngx_event_t *ev;
+ ngx_connection_t *c, *fc;
+ ngx_http_request_t *r;
+ ngx_http_v2_node_t *node;
+ ngx_http_v2_stream_t *stream;
+ ngx_http_v2_srv_conf_t *h2scf;
+
+ c = h2c->connection;
+
+ h2c->blocked = 1;
+
+ if (!c->error && !h2c->goaway) {
+ if (ngx_http_v2_send_goaway(h2c, status) != NGX_ERROR) {
+ (void) ngx_http_v2_send_output_queue(h2c);
+ }
+ }
+
+ c->error = 1;
+
+ if (!h2c->processing && !h2c->pushing) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ c->read->handler = ngx_http_empty_handler;
+ c->write->handler = ngx_http_empty_handler;
+
+ h2c->last_out = NULL;
+
+ h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
+ ngx_http_v2_module);
+
+ size = ngx_http_v2_index_size(h2scf);
+
+ for (i = 0; i < size; i++) {
+
+ for (node = h2c->streams_index[i]; node; node = node->index) {
+ stream = node->stream;
+
+ if (stream == NULL) {
+ continue;
+ }
+
+ stream->waiting = 0;
+
+ r = stream->request;
+ fc = r->connection;
+
+ fc->error = 1;
+
+ if (stream->queued) {
+ stream->queued = 0;
+
+ ev = fc->write;
+ ev->active = 0;
+ ev->ready = 1;
+
+ } else {
+ ev = fc->read;
+ }
+
+ ev->eof = 1;
+ ev->handler(ev);
+ }
+ }
+
+ h2c->blocked = 0;
+
+ if (h2c->processing || h2c->pushing) {
+ return;
+ }
+
+ ngx_http_close_connection(c);
+}
+
+
+static ngx_int_t
+ngx_http_v2_adjust_windows(ngx_http_v2_connection_t *h2c, ssize_t delta)
+{
+ ngx_uint_t i, size;
+ ngx_event_t *wev;
+ ngx_http_v2_node_t *node;
+ ngx_http_v2_stream_t *stream;
+ ngx_http_v2_srv_conf_t *h2scf;
+
+ h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
+ ngx_http_v2_module);
+
+ size = ngx_http_v2_index_size(h2scf);
+
+ for (i = 0; i < size; i++) {
+
+ for (node = h2c->streams_index[i]; node; node = node->index) {
+ stream = node->stream;
+
+ if (stream == NULL) {
+ continue;
+ }
+
+ if (delta > 0
+ && stream->send_window
+ > (ssize_t) (NGX_HTTP_V2_MAX_WINDOW - delta))
+ {
+ if (ngx_http_v2_terminate_stream(h2c, stream,
+ NGX_HTTP_V2_FLOW_CTRL_ERROR)
+ == NGX_ERROR)
+ {
+ return NGX_ERROR;
+ }
+
+ continue;
+ }
+
+ stream->send_window += delta;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2:%ui adjusted window: %z",
+ node->id, stream->send_window);
+
+ if (stream->send_window > 0 && stream->exhausted) {
+ stream->exhausted = 0;
+
+ wev = stream->request->connection->write;
+
+ wev->active = 0;
+ wev->ready = 1;
+
+ if (!wev->delayed) {
+ wev->handler(wev);
+ }
+ }
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_v2_set_dependency(ngx_http_v2_connection_t *h2c,
+ ngx_http_v2_node_t *node, ngx_uint_t depend, ngx_uint_t exclusive)
+{
+ ngx_queue_t *children, *q;
+ ngx_http_v2_node_t *parent, *child, *next;
+
+ parent = depend ? ngx_http_v2_get_node_by_id(h2c, depend, 0) : NULL;
+
+ if (parent == NULL) {
+ parent = NGX_HTTP_V2_ROOT;
+
+ if (depend != 0) {
+ exclusive = 0;
+ }
+
+ node->rank = 1;
+ node->rel_weight = (1.0 / 256) * node->weight;
+
+ children = &h2c->dependencies;
+
+ } else {
+ if (node->parent != NULL) {
+
+ for (next = parent->parent;
+ next != NGX_HTTP_V2_ROOT && next->rank >= node->rank;
+ next = next->parent)
+ {
+ if (next != node) {
+ continue;
+ }
+
+ ngx_queue_remove(&parent->queue);
+ ngx_queue_insert_after(&node->queue, &parent->queue);
+
+ parent->parent = node->parent;
+
+ if (node->parent == NGX_HTTP_V2_ROOT) {
+ parent->rank = 1;
+ parent->rel_weight = (1.0 / 256) * parent->weight;
+
+ } else {
+ parent->rank = node->parent->rank + 1;
+ parent->rel_weight = (node->parent->rel_weight / 256)
+ * parent->weight;
+ }
+
+ if (!exclusive) {
+ ngx_http_v2_node_children_update(parent);
+ }
+
+ break;
+ }
+ }
+
+ node->rank = parent->rank + 1;
+ node->rel_weight = (parent->rel_weight / 256) * node->weight;
+
+ if (parent->stream == NULL) {
+ ngx_queue_remove(&parent->reuse);
+ ngx_queue_insert_tail(&h2c->closed, &parent->reuse);
+ }
+
+ children = &parent->children;
+ }
+
+ if (exclusive) {
+ for (q = ngx_queue_head(children);
+ q != ngx_queue_sentinel(children);
+ q = ngx_queue_next(q))
+ {
+ child = ngx_queue_data(q, ngx_http_v2_node_t, queue);
+ child->parent = node;
+ }
+
+ ngx_queue_add(&node->children, children);
+ ngx_queue_init(children);
+ }
+
+ if (node->parent != NULL) {
+ ngx_queue_remove(&node->queue);
+ }
+
+ ngx_queue_insert_tail(children, &node->queue);
+
+ node->parent = parent;
+
+ ngx_http_v2_node_children_update(node);
+}
+
+
+static void
+ngx_http_v2_node_children_update(ngx_http_v2_node_t *node)
+{
+ ngx_queue_t *q;
+ ngx_http_v2_node_t *child;
+
+ for (q = ngx_queue_head(&node->children);
+ q != ngx_queue_sentinel(&node->children);
+ q = ngx_queue_next(q))
+ {
+ child = ngx_queue_data(q, ngx_http_v2_node_t, queue);
+
+ child->rank = node->rank + 1;
+ child->rel_weight = (node->rel_weight / 256) * child->weight;
+
+ ngx_http_v2_node_children_update(child);
+ }
+}
+
+
+static void
+ngx_http_v2_pool_cleanup(void *data)
+{
+ ngx_http_v2_connection_t *h2c = data;
+
+ if (h2c->state.pool) {
+ ngx_destroy_pool(h2c->state.pool);
+ }
+
+ if (h2c->pool) {
+ ngx_destroy_pool(h2c->pool);
+ }
+}
diff --git a/src/nginx-1.18.0/src/http/v2/ngx_http_v2.h b/src/nginx-1.18.0/src/http/v2/ngx_http_v2.h
new file mode 100644
index 0000000..59ddf54
--- /dev/null
+++ b/src/nginx-1.18.0/src/http/v2/ngx_http_v2.h
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) Nginx, Inc.
+ * Copyright (C) Valentin V. Bartenev
+ */
+
+
+#ifndef _NGX_HTTP_V2_H_INCLUDED_
+#define _NGX_HTTP_V2_H_INCLUDED_
+
+
+#include
+#include
+#include
+
+
+#define NGX_HTTP_V2_ALPN_ADVERTISE "\x02h2"
+#define NGX_HTTP_V2_NPN_ADVERTISE NGX_HTTP_V2_ALPN_ADVERTISE
+
+#define NGX_HTTP_V2_STATE_BUFFER_SIZE 16
+
+#define NGX_HTTP_V2_DEFAULT_FRAME_SIZE (1 << 14)
+#define NGX_HTTP_V2_MAX_FRAME_SIZE ((1 << 24) - 1)
+
+#define NGX_HTTP_V2_INT_OCTETS 4
+#define NGX_HTTP_V2_MAX_FIELD \
+ (127 + (1 << (NGX_HTTP_V2_INT_OCTETS - 1) * 7) - 1)
+
+#define NGX_HTTP_V2_STREAM_ID_SIZE 4
+
+#define NGX_HTTP_V2_FRAME_HEADER_SIZE 9
+
+/* frame types */
+#define NGX_HTTP_V2_DATA_FRAME 0x0
+#define NGX_HTTP_V2_HEADERS_FRAME 0x1
+#define NGX_HTTP_V2_PRIORITY_FRAME 0x2
+#define NGX_HTTP_V2_RST_STREAM_FRAME 0x3
+#define NGX_HTTP_V2_SETTINGS_FRAME 0x4
+#define NGX_HTTP_V2_PUSH_PROMISE_FRAME 0x5
+#define NGX_HTTP_V2_PING_FRAME 0x6
+#define NGX_HTTP_V2_GOAWAY_FRAME 0x7
+#define NGX_HTTP_V2_WINDOW_UPDATE_FRAME 0x8
+#define NGX_HTTP_V2_CONTINUATION_FRAME 0x9
+
+/* frame flags */
+#define NGX_HTTP_V2_NO_FLAG 0x00
+#define NGX_HTTP_V2_ACK_FLAG 0x01
+#define NGX_HTTP_V2_END_STREAM_FLAG 0x01
+#define NGX_HTTP_V2_END_HEADERS_FLAG 0x04
+#define NGX_HTTP_V2_PADDED_FLAG 0x08
+#define NGX_HTTP_V2_PRIORITY_FLAG 0x20
+
+#define NGX_HTTP_V2_MAX_WINDOW ((1U << 31) - 1)
+#define NGX_HTTP_V2_DEFAULT_WINDOW 65535
+
+#define NGX_HTTP_V2_DEFAULT_WEIGHT 16
+
+
+typedef struct ngx_http_v2_connection_s ngx_http_v2_connection_t;
+typedef struct ngx_http_v2_node_s ngx_http_v2_node_t;
+typedef struct ngx_http_v2_out_frame_s ngx_http_v2_out_frame_t;
+
+
+typedef u_char *(*ngx_http_v2_handler_pt) (ngx_http_v2_connection_t *h2c,
+ u_char *pos, u_char *end);
+
+
+typedef struct {
+ ngx_str_t name;
+ ngx_str_t value;
+} ngx_http_v2_header_t;
+
+
+typedef struct {
+ ngx_uint_t sid;
+ size_t length;
+ size_t padding;
+ unsigned flags:8;
+
+ unsigned incomplete:1;
+ unsigned keep_pool:1;
+
+ /* HPACK */
+ unsigned parse_name:1;
+ unsigned parse_value:1;
+ unsigned index:1;
+ ngx_http_v2_header_t header;
+ size_t header_limit;
+ u_char field_state;
+ u_char *field_start;
+ u_char *field_end;
+ size_t field_rest;
+ ngx_pool_t *pool;
+
+ ngx_http_v2_stream_t *stream;
+
+ u_char buffer[NGX_HTTP_V2_STATE_BUFFER_SIZE];
+ size_t buffer_used;
+ ngx_http_v2_handler_pt handler;
+} ngx_http_v2_state_t;
+
+
+
+typedef struct {
+ ngx_http_v2_header_t **entries;
+
+ ngx_uint_t added;
+ ngx_uint_t deleted;
+ ngx_uint_t reused;
+ ngx_uint_t allocated;
+
+ size_t size;
+ size_t free;
+ u_char *storage;
+ u_char *pos;
+} ngx_http_v2_hpack_t;
+
+
+struct ngx_http_v2_connection_s {
+ ngx_connection_t *connection;
+ ngx_http_connection_t *http_connection;
+
+ off_t total_bytes;
+ off_t payload_bytes;
+
+ ngx_uint_t processing;
+ ngx_uint_t frames;
+ ngx_uint_t idle;
+ ngx_uint_t priority_limit;
+
+ ngx_uint_t pushing;
+ ngx_uint_t concurrent_pushes;
+
+ size_t send_window;
+ size_t recv_window;
+ size_t init_window;
+
+ size_t frame_size;
+
+ ngx_queue_t waiting;
+
+ ngx_http_v2_state_t state;
+
+ ngx_http_v2_hpack_t hpack;
+
+ ngx_pool_t *pool;
+
+ ngx_http_v2_out_frame_t *free_frames;
+ ngx_connection_t *free_fake_connections;
+
+ ngx_http_v2_node_t **streams_index;
+
+ ngx_http_v2_out_frame_t *last_out;
+
+ ngx_queue_t dependencies;
+ ngx_queue_t closed;
+
+ ngx_uint_t last_sid;
+ ngx_uint_t last_push;
+
+ unsigned closed_nodes:8;
+ unsigned settings_ack:1;
+ unsigned table_update:1;
+ unsigned blocked:1;
+ unsigned goaway:1;
+ unsigned push_disabled:1;
+};
+
+
+struct ngx_http_v2_node_s {
+ ngx_uint_t id;
+ ngx_http_v2_node_t *index;
+ ngx_http_v2_node_t *parent;
+ ngx_queue_t queue;
+ ngx_queue_t children;
+ ngx_queue_t reuse;
+ ngx_uint_t rank;
+ ngx_uint_t weight;
+ double rel_weight;
+ ngx_http_v2_stream_t *stream;
+};
+
+
+struct ngx_http_v2_stream_s {
+ ngx_http_request_t *request;
+ ngx_http_v2_connection_t *connection;
+ ngx_http_v2_node_t *node;
+
+ ngx_uint_t queued;
+
+ /*
+ * A change to SETTINGS_INITIAL_WINDOW_SIZE could cause the
+ * send_window to become negative, hence it's signed.
+ */
+ ssize_t send_window;
+ size_t recv_window;
+
+ ngx_buf_t *preread;
+
+ ngx_uint_t frames;
+
+ ngx_http_v2_out_frame_t *free_frames;
+ ngx_chain_t *free_frame_headers;
+ ngx_chain_t *free_bufs;
+
+ ngx_queue_t queue;
+
+ ngx_array_t *cookies;
+
+ ngx_pool_t *pool;
+
+ unsigned waiting:1;
+ unsigned blocked:1;
+ unsigned exhausted:1;
+ unsigned in_closed:1;
+ unsigned out_closed:1;
+ unsigned rst_sent:1;
+ unsigned no_flow_control:1;
+ unsigned skip_data:1;
+};
+
+
+struct ngx_http_v2_out_frame_s {
+ ngx_http_v2_out_frame_t *next;
+ ngx_chain_t *first;
+ ngx_chain_t *last;
+ ngx_int_t (*handler)(ngx_http_v2_connection_t *h2c,
+ ngx_http_v2_out_frame_t *frame);
+
+ ngx_http_v2_stream_t *stream;
+ size_t length;
+
+ unsigned blocked:1;
+ unsigned fin:1;
+};
+
+
+static ngx_inline void
+ngx_http_v2_queue_frame(ngx_http_v2_connection_t *h2c,
+ ngx_http_v2_out_frame_t *frame)
+{
+ ngx_http_v2_out_frame_t **out;
+
+ for (out = &h2c->last_out; *out; out = &(*out)->next) {
+
+ if ((*out)->blocked || (*out)->stream == NULL) {
+ break;
+ }
+
+ if ((*out)->stream->node->rank < frame->stream->node->rank
+ || ((*out)->stream->node->rank == frame->stream->node->rank
+ && (*out)->stream->node->rel_weight
+ >= frame->stream->node->rel_weight))
+ {
+ break;
+ }
+ }
+
+ frame->next = *out;
+ *out = frame;
+}
+
+
+static ngx_inline void
+ngx_http_v2_queue_blocked_frame(ngx_http_v2_connection_t *h2c,
+ ngx_http_v2_out_frame_t *frame)
+{
+ ngx_http_v2_out_frame_t **out;
+
+ for (out = &h2c->last_out; *out; out = &(*out)->next) {
+
+ if ((*out)->blocked || (*out)->stream == NULL) {
+ break;
+ }
+ }
+
+ frame->next = *out;
+ *out = frame;
+}
+
+
+static ngx_inline void
+ngx_http_v2_queue_ordered_frame(ngx_http_v2_connection_t *h2c,
+ ngx_http_v2_out_frame_t *frame)
+{
+ frame->next = h2c->last_out;
+ h2c->last_out = frame;
+}
+
+
+void ngx_http_v2_init(ngx_event_t *rev);
+
+ngx_int_t ngx_http_v2_read_request_body(ngx_http_request_t *r);
+ngx_int_t ngx_http_v2_read_unbuffered_request_body(ngx_http_request_t *r);
+
+ngx_http_v2_stream_t *ngx_http_v2_push_stream(ngx_http_v2_stream_t *parent,
+ ngx_str_t *path);
+
+void ngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc);
+
+ngx_int_t ngx_http_v2_send_output_queue(ngx_http_v2_connection_t *h2c);
+
+
+ngx_str_t *ngx_http_v2_get_static_name(ngx_uint_t index);
+ngx_str_t *ngx_http_v2_get_static_value(ngx_uint_t index);
+
+ngx_int_t ngx_http_v2_get_indexed_header(ngx_http_v2_connection_t *h2c,
+ ngx_uint_t index, ngx_uint_t name_only);
+ngx_int_t ngx_http_v2_add_header(ngx_http_v2_connection_t *h2c,
+ ngx_http_v2_header_t *header);
+ngx_int_t ngx_http_v2_table_size(ngx_http_v2_connection_t *h2c, size_t size);
+
+
+ngx_int_t ngx_http_v2_huff_decode(u_char *state, u_char *src, size_t len,
+ u_char **dst, ngx_uint_t last, ngx_log_t *log);
+size_t ngx_http_v2_huff_encode(u_char *src, size_t len, u_char *dst,
+ ngx_uint_t lower);
+
+
+#define ngx_http_v2_prefix(bits) ((1 << (bits)) - 1)
+
+
+#if (NGX_HAVE_NONALIGNED)
+
+#define ngx_http_v2_parse_uint16(p) ntohs(*(uint16_t *) (p))
+#define ngx_http_v2_parse_uint32(p) ntohl(*(uint32_t *) (p))
+
+#else
+
+#define ngx_http_v2_parse_uint16(p) ((p)[0] << 8 | (p)[1])
+#define ngx_http_v2_parse_uint32(p) \
+ ((uint32_t) (p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3])
+
+#endif
+
+#define ngx_http_v2_parse_length(p) ((p) >> 8)
+#define ngx_http_v2_parse_type(p) ((p) & 0xff)
+#define ngx_http_v2_parse_sid(p) (ngx_http_v2_parse_uint32(p) & 0x7fffffff)
+#define ngx_http_v2_parse_window(p) (ngx_http_v2_parse_uint32(p) & 0x7fffffff)
+
+
+#define ngx_http_v2_write_uint16_aligned(p, s) \
+ (*(uint16_t *) (p) = htons((uint16_t) (s)), (p) + sizeof(uint16_t))
+#define ngx_http_v2_write_uint32_aligned(p, s) \
+ (*(uint32_t *) (p) = htonl((uint32_t) (s)), (p) + sizeof(uint32_t))
+
+#if (NGX_HAVE_NONALIGNED)
+
+#define ngx_http_v2_write_uint16 ngx_http_v2_write_uint16_aligned
+#define ngx_http_v2_write_uint32 ngx_http_v2_write_uint32_aligned
+
+#else
+
+#define ngx_http_v2_write_uint16(p, s) \
+ ((p)[0] = (u_char) ((s) >> 8), \
+ (p)[1] = (u_char) (s), \
+ (p) + sizeof(uint16_t))
+
+#define ngx_http_v2_write_uint32(p, s) \
+ ((p)[0] = (u_char) ((s) >> 24), \
+ (p)[1] = (u_char) ((s) >> 16), \
+ (p)[2] = (u_char) ((s) >> 8), \
+ (p)[3] = (u_char) (s), \
+ (p) + sizeof(uint32_t))
+
+#endif
+
+#define ngx_http_v2_write_len_and_type(p, l, t) \
+ ngx_http_v2_write_uint32_aligned(p, (l) << 8 | (t))
+
+#define ngx_http_v2_write_sid ngx_http_v2_write_uint32
+
+
+#define ngx_http_v2_indexed(i) (128 + (i))
+#define ngx_http_v2_inc_indexed(i) (64 + (i))
+
+#define ngx_http_v2_write_name(dst, src, len, tmp) \
+ ngx_http_v2_string_encode(dst, src, len, tmp, 1)
+#define ngx_http_v2_write_value(dst, src, len, tmp) \
+ ngx_http_v2_string_encode(dst, src, len, tmp, 0)
+
+#define NGX_HTTP_V2_ENCODE_RAW 0
+#define NGX_HTTP_V2_ENCODE_HUFF 0x80
+
+#define NGX_HTTP_V2_AUTHORITY_INDEX 1
+
+#define NGX_HTTP_V2_METHOD_INDEX 2
+#define NGX_HTTP_V2_METHOD_GET_INDEX 2
+#define NGX_HTTP_V2_METHOD_POST_INDEX 3
+
+#define NGX_HTTP_V2_PATH_INDEX 4
+#define NGX_HTTP_V2_PATH_ROOT_INDEX 4
+
+#define NGX_HTTP_V2_SCHEME_HTTP_INDEX 6
+#define NGX_HTTP_V2_SCHEME_HTTPS_INDEX 7
+
+#define NGX_HTTP_V2_STATUS_INDEX 8
+#define NGX_HTTP_V2_STATUS_200_INDEX 8
+#define NGX_HTTP_V2_STATUS_204_INDEX 9
+#define NGX_HTTP_V2_STATUS_206_INDEX 10
+#define NGX_HTTP_V2_STATUS_304_INDEX 11
+#define NGX_HTTP_V2_STATUS_400_INDEX 12
+#define NGX_HTTP_V2_STATUS_404_INDEX 13
+#define NGX_HTTP_V2_STATUS_500_INDEX 14
+
+#define NGX_HTTP_V2_ACCEPT_ENCODING_INDEX 16
+#define NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX 17
+#define NGX_HTTP_V2_CONTENT_LENGTH_INDEX 28
+#define NGX_HTTP_V2_CONTENT_TYPE_INDEX 31
+#define NGX_HTTP_V2_DATE_INDEX 33
+#define NGX_HTTP_V2_LAST_MODIFIED_INDEX 44
+#define NGX_HTTP_V2_LOCATION_INDEX 46
+#define NGX_HTTP_V2_SERVER_INDEX 54
+#define NGX_HTTP_V2_USER_AGENT_INDEX 58
+#define NGX_HTTP_V2_VARY_INDEX 59
+
+
+u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len,
+ u_char *tmp, ngx_uint_t lower);
+
+
+#endif /* _NGX_HTTP_V2_H_INCLUDED_ */
diff --git a/src/nginx-1.18.0/src/http/v2/ngx_http_v2_encode.c b/src/nginx-1.18.0/src/http/v2/ngx_http_v2_encode.c
new file mode 100644
index 0000000..ac79208
--- /dev/null
+++ b/src/nginx-1.18.0/src/http/v2/ngx_http_v2_encode.c
@@ -0,0 +1,62 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ * Copyright (C) Valentin V. Bartenev
+ */
+
+
+#include
+#include
+#include
+
+
+static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,
+ ngx_uint_t value);
+
+
+u_char *
+ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp,
+ ngx_uint_t lower)
+{
+ size_t hlen;
+
+ hlen = ngx_http_v2_huff_encode(src, len, tmp, lower);
+
+ if (hlen > 0) {
+ *dst = NGX_HTTP_V2_ENCODE_HUFF;
+ dst = ngx_http_v2_write_int(dst, ngx_http_v2_prefix(7), hlen);
+ return ngx_cpymem(dst, tmp, hlen);
+ }
+
+ *dst = NGX_HTTP_V2_ENCODE_RAW;
+ dst = ngx_http_v2_write_int(dst, ngx_http_v2_prefix(7), len);
+
+ if (lower) {
+ ngx_strlow(dst, src, len);
+ return dst + len;
+ }
+
+ return ngx_cpymem(dst, src, len);
+}
+
+
+static u_char *
+ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value)
+{
+ if (value < prefix) {
+ *pos++ |= value;
+ return pos;
+ }
+
+ *pos++ |= prefix;
+ value -= prefix;
+
+ while (value >= 128) {
+ *pos++ = value % 128 + 128;
+ value /= 128;
+ }
+
+ *pos++ = (u_char) value;
+
+ return pos;
+}
diff --git a/src/nginx-1.18.0/src/http/v2/ngx_http_v2_filter_module.c b/src/nginx-1.18.0/src/http/v2/ngx_http_v2_filter_module.c
new file mode 100644
index 0000000..a6e5e7d
--- /dev/null
+++ b/src/nginx-1.18.0/src/http/v2/ngx_http_v2_filter_module.c
@@ -0,0 +1,2176 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ * Copyright (C) Valentin V. Bartenev
+ * Copyright (C) Ruslan Ermilov
+ */
+
+
+#include
+#include
+#include
+#include
+#include
+
+
+/*
+ * This returns precise number of octets for values in range 0..253
+ * and estimate number for the rest, but not smaller than required.
+ */
+
+#define ngx_http_v2_integer_octets(v) (1 + (v) / 127)
+
+#define ngx_http_v2_literal_size(h) \
+ (ngx_http_v2_integer_octets(sizeof(h) - 1) + sizeof(h) - 1)
+
+
+#define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1
+
+
+typedef struct {
+ ngx_str_t name;
+ u_char index;
+ ngx_uint_t offset;
+} ngx_http_v2_push_header_t;
+
+
+static ngx_http_v2_push_header_t ngx_http_v2_push_headers[] = {
+ { ngx_string(":authority"), NGX_HTTP_V2_AUTHORITY_INDEX,
+ offsetof(ngx_http_headers_in_t, host) },
+
+ { ngx_string("accept-encoding"), NGX_HTTP_V2_ACCEPT_ENCODING_INDEX,
+ offsetof(ngx_http_headers_in_t, accept_encoding) },
+
+ { ngx_string("accept-language"), NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX,
+ offsetof(ngx_http_headers_in_t, accept_language) },
+
+ { ngx_string("user-agent"), NGX_HTTP_V2_USER_AGENT_INDEX,
+ offsetof(ngx_http_headers_in_t, user_agent) },
+};
+
+#define NGX_HTTP_V2_PUSH_HEADERS \
+ (sizeof(ngx_http_v2_push_headers) / sizeof(ngx_http_v2_push_header_t))
+
+
+static ngx_int_t ngx_http_v2_push_resources(ngx_http_request_t *r);
+static ngx_int_t ngx_http_v2_push_resource(ngx_http_request_t *r,
+ ngx_str_t *path, ngx_str_t *binary);
+
+static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame(
+ ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin);
+static ngx_http_v2_out_frame_t *ngx_http_v2_create_push_frame(
+ ngx_http_request_t *r, u_char *pos, u_char *end);
+static ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame(
+ ngx_http_request_t *r);
+
+static ngx_chain_t *ngx_http_v2_send_chain(ngx_connection_t *fc,
+ ngx_chain_t *in, off_t limit);
+
+static ngx_chain_t *ngx_http_v2_filter_get_shadow(
+ ngx_http_v2_stream_t *stream, ngx_buf_t *buf, off_t offset, off_t size);
+static ngx_http_v2_out_frame_t *ngx_http_v2_filter_get_data_frame(
+ ngx_http_v2_stream_t *stream, size_t len, ngx_chain_t *first,
+ ngx_chain_t *last);
+
+static ngx_inline ngx_int_t ngx_http_v2_flow_control(
+ ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream);
+static void ngx_http_v2_waiting_queue(ngx_http_v2_connection_t *h2c,
+ ngx_http_v2_stream_t *stream);
+
+static ngx_inline ngx_int_t ngx_http_v2_filter_send(
+ ngx_connection_t *fc, ngx_http_v2_stream_t *stream);
+
+static ngx_int_t ngx_http_v2_headers_frame_handler(
+ ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);
+static ngx_int_t ngx_http_v2_push_frame_handler(
+ ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);
+static ngx_int_t ngx_http_v2_data_frame_handler(
+ ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);
+static ngx_inline void ngx_http_v2_handle_frame(
+ ngx_http_v2_stream_t *stream, ngx_http_v2_out_frame_t *frame);
+static ngx_inline void ngx_http_v2_handle_stream(
+ ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream);
+
+static void ngx_http_v2_filter_cleanup(void *data);
+
+static ngx_int_t ngx_http_v2_filter_init(ngx_conf_t *cf);
+
+
+static ngx_http_module_t ngx_http_v2_filter_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_v2_filter_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_v2_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_v2_filter_module_ctx, /* module context */
+ NULL, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
+
+
+static ngx_int_t
+ngx_http_v2_header_filter(ngx_http_request_t *r)
+{
+ u_char status, *pos, *start, *p, *tmp;
+ size_t len, tmp_len;
+ ngx_str_t host, location;
+ ngx_uint_t i, port, fin;
+ ngx_list_part_t *part;
+ ngx_table_elt_t *header;
+ ngx_connection_t *fc;
+ ngx_http_cleanup_t *cln;
+ ngx_http_v2_stream_t *stream;
+ ngx_http_v2_out_frame_t *frame;
+ ngx_http_v2_connection_t *h2c;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_core_srv_conf_t *cscf;
+ u_char addr[NGX_SOCKADDR_STRLEN];
+
+ static const u_char nginx[5] = "\x84\xaa\x63\x55\xe7";
+#if (NGX_HTTP_GZIP)
+ static const u_char accept_encoding[12] =
+ "\x8b\x84\x84\x2d\x69\x5b\x05\x44\x3c\x86\xaa\x6f";
+#endif
+
+ static size_t nginx_ver_len = ngx_http_v2_literal_size(NGINX_VER);
+ static u_char nginx_ver[ngx_http_v2_literal_size(NGINX_VER)];
+
+ static size_t nginx_ver_build_len =
+ ngx_http_v2_literal_size(NGINX_VER_BUILD);
+ static u_char nginx_ver_build[ngx_http_v2_literal_size(NGINX_VER_BUILD)];
+
+ stream = r->stream;
+
+ if (!stream) {
+ return ngx_http_next_header_filter(r);
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http2 header filter");
+
+ if (r->header_sent) {
+ return NGX_OK;
+ }
+
+ r->header_sent = 1;
+
+ if (r != r->main) {
+ return NGX_OK;
+ }
+
+ fc = r->connection;
+
+ if (fc->error) {
+ return NGX_ERROR;
+ }
+
+ if (r->method == NGX_HTTP_HEAD) {
+ r->header_only = 1;
+ }
+
+ switch (r->headers_out.status) {
+
+ case NGX_HTTP_OK:
+ status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_200_INDEX);
+ break;
+
+ case NGX_HTTP_NO_CONTENT:
+ r->header_only = 1;
+
+ ngx_str_null(&r->headers_out.content_type);
+
+ r->headers_out.content_length = NULL;
+ r->headers_out.content_length_n = -1;
+
+ r->headers_out.last_modified_time = -1;
+ r->headers_out.last_modified = NULL;
+
+ status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_204_INDEX);
+ break;
+
+ case NGX_HTTP_PARTIAL_CONTENT:
+ status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_206_INDEX);
+ break;
+
+ case NGX_HTTP_NOT_MODIFIED:
+ r->header_only = 1;
+ status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_304_INDEX);
+ break;
+
+ default:
+ r->headers_out.last_modified_time = -1;
+ r->headers_out.last_modified = NULL;
+
+ switch (r->headers_out.status) {
+
+ case NGX_HTTP_BAD_REQUEST:
+ status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_400_INDEX);
+ break;
+
+ case NGX_HTTP_NOT_FOUND:
+ status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_404_INDEX);
+ break;
+
+ case NGX_HTTP_INTERNAL_SERVER_ERROR:
+ status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_500_INDEX);
+ break;
+
+ default:
+ status = 0;
+ }
+ }
+
+ h2c = stream->connection;
+
+ if (!h2c->push_disabled && !h2c->goaway
+ && stream->node->id % 2 == 1
+ && r->method != NGX_HTTP_HEAD)
+ {
+ if (ngx_http_v2_push_resources(r) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ len = h2c->table_update ? 1 : 0;
+
+ len += status ? 1 : 1 + ngx_http_v2_literal_size("418");
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (r->headers_out.server == NULL) {
+
+ if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {
+ len += 1 + nginx_ver_len;
+
+ } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {
+ len += 1 + nginx_ver_build_len;
+
+ } else {
+ len += 1 + sizeof(nginx);
+ }
+ }
+
+ if (r->headers_out.date == NULL) {
+ len += 1 + ngx_http_v2_literal_size("Wed, 31 Dec 1986 18:00:00 GMT");
+ }
+
+ if (r->headers_out.content_type.len) {
+ len += 1 + NGX_HTTP_V2_INT_OCTETS + r->headers_out.content_type.len;
+
+ if (r->headers_out.content_type_len == r->headers_out.content_type.len
+ && r->headers_out.charset.len)
+ {
+ len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
+ }
+ }
+
+ if (r->headers_out.content_length == NULL
+ && r->headers_out.content_length_n >= 0)
+ {
+ len += 1 + ngx_http_v2_integer_octets(NGX_OFF_T_LEN) + NGX_OFF_T_LEN;
+ }
+
+ if (r->headers_out.last_modified == NULL
+ && r->headers_out.last_modified_time != -1)
+ {
+ len += 1 + ngx_http_v2_literal_size("Wed, 31 Dec 1986 18:00:00 GMT");
+ }
+
+ if (r->headers_out.location && r->headers_out.location->value.len) {
+
+ if (r->headers_out.location->value.data[0] == '/'
+ && clcf->absolute_redirect)
+ {
+ if (clcf->server_name_in_redirect) {
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+ host = cscf->server_name;
+
+ } else if (r->headers_in.server.len) {
+ host = r->headers_in.server;
+
+ } else {
+ host.len = NGX_SOCKADDR_STRLEN;
+ host.data = addr;
+
+ if (ngx_connection_local_sockaddr(fc, &host, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ port = ngx_inet_get_port(fc->local_sockaddr);
+
+ location.len = sizeof("https://") - 1 + host.len
+ + r->headers_out.location->value.len;
+
+ if (clcf->port_in_redirect) {
+
+#if (NGX_HTTP_SSL)
+ if (fc->ssl)
+ port = (port == 443) ? 0 : port;
+ else
+#endif
+ port = (port == 80) ? 0 : port;
+
+ } else {
+ port = 0;
+ }
+
+ if (port) {
+ location.len += sizeof(":65535") - 1;
+ }
+
+ location.data = ngx_pnalloc(r->pool, location.len);
+ if (location.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ p = ngx_cpymem(location.data, "http", sizeof("http") - 1);
+
+#if (NGX_HTTP_SSL)
+ if (fc->ssl) {
+ *p++ = 's';
+ }
+#endif
+
+ *p++ = ':'; *p++ = '/'; *p++ = '/';
+ p = ngx_cpymem(p, host.data, host.len);
+
+ if (port) {
+ p = ngx_sprintf(p, ":%ui", port);
+ }
+
+ p = ngx_cpymem(p, r->headers_out.location->value.data,
+ r->headers_out.location->value.len);
+
+ /* update r->headers_out.location->value for possible logging */
+
+ r->headers_out.location->value.len = p - location.data;
+ r->headers_out.location->value.data = location.data;
+ ngx_str_set(&r->headers_out.location->key, "Location");
+ }
+
+ r->headers_out.location->hash = 0;
+
+ len += 1 + NGX_HTTP_V2_INT_OCTETS + r->headers_out.location->value.len;
+ }
+
+ tmp_len = len;
+
+#if (NGX_HTTP_GZIP)
+ if (r->gzip_vary) {
+ if (clcf->gzip_vary) {
+ len += 1 + sizeof(accept_encoding);
+
+ } else {
+ r->gzip_vary = 0;
+ }
+ }
+#endif
+
+ part = &r->headers_out.headers.part;
+ header = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ if (header[i].hash == 0) {
+ continue;
+ }
+
+ if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) {
+ ngx_log_error(NGX_LOG_CRIT, fc->log, 0,
+ "too long response header name: \"%V\"",
+ &header[i].key);
+ return NGX_ERROR;
+ }
+
+ if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) {
+ ngx_log_error(NGX_LOG_CRIT, fc->log, 0,
+ "too long response header value: \"%V: %V\"",
+ &header[i].key, &header[i].value);
+ return NGX_ERROR;
+ }
+
+ len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len
+ + NGX_HTTP_V2_INT_OCTETS + header[i].value.len;
+
+ if (header[i].key.len > tmp_len) {
+ tmp_len = header[i].key.len;
+ }
+
+ if (header[i].value.len > tmp_len) {
+ tmp_len = header[i].value.len;
+ }
+ }
+
+ tmp = ngx_palloc(r->pool, tmp_len);
+ pos = ngx_pnalloc(r->pool, len);
+
+ if (pos == NULL || tmp == NULL) {
+ return NGX_ERROR;
+ }
+
+ start = pos;
+
+ if (h2c->table_update) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 table size update: 0");
+ *pos++ = (1 << 5) | 0;
+ h2c->table_update = 0;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 output header: \":status: %03ui\"",
+ r->headers_out.status);
+
+ if (status) {
+ *pos++ = status;
+
+ } else {
+ *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX);
+ *pos++ = NGX_HTTP_V2_ENCODE_RAW | 3;
+ pos = ngx_sprintf(pos, "%03ui", r->headers_out.status);
+ }
+
+ if (r->headers_out.server == NULL) {
+
+ if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 output header: \"server: %s\"",
+ NGINX_VER);
+
+ } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 output header: \"server: %s\"",
+ NGINX_VER_BUILD);
+
+ } else {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 output header: \"server: nginx\"");
+ }
+
+ *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX);
+
+ if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {
+ if (nginx_ver[0] == '\0') {
+ p = ngx_http_v2_write_value(nginx_ver, (u_char *) NGINX_VER,
+ sizeof(NGINX_VER) - 1, tmp);
+ nginx_ver_len = p - nginx_ver;
+ }
+
+ pos = ngx_cpymem(pos, nginx_ver, nginx_ver_len);
+
+ } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {
+ if (nginx_ver_build[0] == '\0') {
+ p = ngx_http_v2_write_value(nginx_ver_build,
+ (u_char *) NGINX_VER_BUILD,
+ sizeof(NGINX_VER_BUILD) - 1, tmp);
+ nginx_ver_build_len = p - nginx_ver_build;
+ }
+
+ pos = ngx_cpymem(pos, nginx_ver_build, nginx_ver_build_len);
+
+ } else {
+ pos = ngx_cpymem(pos, nginx, sizeof(nginx));
+ }
+ }
+
+ if (r->headers_out.date == NULL) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 output header: \"date: %V\"",
+ &ngx_cached_http_time);
+
+ *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX);
+ pos = ngx_http_v2_write_value(pos, ngx_cached_http_time.data,
+ ngx_cached_http_time.len, tmp);
+ }
+
+ if (r->headers_out.content_type.len) {
+ *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_TYPE_INDEX);
+
+ if (r->headers_out.content_type_len == r->headers_out.content_type.len
+ && r->headers_out.charset.len)
+ {
+ len = r->headers_out.content_type.len + sizeof("; charset=") - 1
+ + r->headers_out.charset.len;
+
+ p = ngx_pnalloc(r->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ p = ngx_cpymem(p, r->headers_out.content_type.data,
+ r->headers_out.content_type.len);
+
+ p = ngx_cpymem(p, "; charset=", sizeof("; charset=") - 1);
+
+ p = ngx_cpymem(p, r->headers_out.charset.data,
+ r->headers_out.charset.len);
+
+ /* updated r->headers_out.content_type is also needed for logging */
+
+ r->headers_out.content_type.len = len;
+ r->headers_out.content_type.data = p - len;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 output header: \"content-type: %V\"",
+ &r->headers_out.content_type);
+
+ pos = ngx_http_v2_write_value(pos, r->headers_out.content_type.data,
+ r->headers_out.content_type.len, tmp);
+ }
+
+ if (r->headers_out.content_length == NULL
+ && r->headers_out.content_length_n >= 0)
+ {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 output header: \"content-length: %O\"",
+ r->headers_out.content_length_n);
+
+ *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_LENGTH_INDEX);
+
+ p = pos;
+ pos = ngx_sprintf(pos + 1, "%O", r->headers_out.content_length_n);
+ *p = NGX_HTTP_V2_ENCODE_RAW | (u_char) (pos - p - 1);
+ }
+
+ if (r->headers_out.last_modified == NULL
+ && r->headers_out.last_modified_time != -1)
+ {
+ *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX);
+
+ ngx_http_time(pos, r->headers_out.last_modified_time);
+ len = sizeof("Wed, 31 Dec 1986 18:00:00 GMT") - 1;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 output header: \"last-modified: %*s\"",
+ len, pos);
+
+ /*
+ * Date will always be encoded using huffman in the temporary buffer,
+ * so it's safe here to use src and dst pointing to the same address.
+ */
+ pos = ngx_http_v2_write_value(pos, pos, len, tmp);
+ }
+
+ if (r->headers_out.location && r->headers_out.location->value.len) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 output header: \"location: %V\"",
+ &r->headers_out.location->value);
+
+ *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX);
+ pos = ngx_http_v2_write_value(pos, r->headers_out.location->value.data,
+ r->headers_out.location->value.len, tmp);
+ }
+
+#if (NGX_HTTP_GZIP)
+ if (r->gzip_vary) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 output header: \"vary: Accept-Encoding\"");
+
+ *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX);
+ pos = ngx_cpymem(pos, accept_encoding, sizeof(accept_encoding));
+ }
+#endif
+
+ part = &r->headers_out.headers.part;
+ header = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ if (header[i].hash == 0) {
+ continue;
+ }
+
+#if (NGX_DEBUG)
+ if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {
+ ngx_strlow(tmp, header[i].key.data, header[i].key.len);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 output header: \"%*s: %V\"",
+ header[i].key.len, tmp, &header[i].value);
+ }
+#endif
+
+ *pos++ = 0;
+
+ pos = ngx_http_v2_write_name(pos, header[i].key.data,
+ header[i].key.len, tmp);
+
+ pos = ngx_http_v2_write_value(pos, header[i].value.data,
+ header[i].value.len, tmp);
+ }
+
+ fin = r->header_only
+ || (r->headers_out.content_length_n == 0 && !r->expect_trailers);
+
+ frame = ngx_http_v2_create_headers_frame(r, start, pos, fin);
+ if (frame == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_v2_queue_blocked_frame(h2c, frame);
+
+ stream->queued++;
+
+ cln = ngx_http_cleanup_add(r, 0);
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln->handler = ngx_http_v2_filter_cleanup;
+ cln->data = stream;
+
+ fc->send_chain = ngx_http_v2_send_chain;
+ fc->need_last_buf = 1;
+
+ return ngx_http_v2_filter_send(fc, stream);
+}
+
+
+static ngx_int_t
+ngx_http_v2_push_resources(ngx_http_request_t *r)
+{
+ u_char *start, *end, *last;
+ ngx_int_t rc;
+ ngx_str_t path;
+ ngx_uint_t i, push;
+ ngx_table_elt_t **h;
+ ngx_http_v2_loc_conf_t *h2lcf;
+ ngx_http_complex_value_t *pushes;
+ ngx_str_t binary[NGX_HTTP_V2_PUSH_HEADERS];
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http2 push resources");
+
+ ngx_memzero(binary, NGX_HTTP_V2_PUSH_HEADERS * sizeof(ngx_str_t));
+
+ h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module);
+
+ if (h2lcf->pushes) {
+ pushes = h2lcf->pushes->elts;
+
+ for (i = 0; i < h2lcf->pushes->nelts; i++) {
+
+ if (ngx_http_complex_value(r, &pushes[i], &path) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (path.len == 0) {
+ continue;
+ }
+
+ if (path.len == 3 && ngx_strncmp(path.data, "off", 3) == 0) {
+ continue;
+ }
+
+ rc = ngx_http_v2_push_resource(r, &path, binary);
+
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (rc == NGX_ABORT) {
+ return NGX_OK;
+ }
+
+ /* NGX_OK, NGX_DECLINED */
+ }
+ }
+
+ if (!h2lcf->push_preload) {
+ return NGX_OK;
+ }
+
+ h = r->headers_out.link.elts;
+
+ for (i = 0; i < r->headers_out.link.nelts; i++) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http2 parse link: \"%V\"", &h[i]->value);
+
+ start = h[i]->value.data;
+ end = h[i]->value.data + h[i]->value.len;
+
+ next_link:
+
+ while (start < end && *start == ' ') { start++; }
+
+ if (start == end || *start++ != '<') {
+ continue;
+ }
+
+ while (start < end && *start == ' ') { start++; }
+
+ for (last = start; last < end && *last != '>'; last++) {
+ /* void */
+ }
+
+ if (last == start || last == end) {
+ continue;
+ }
+
+ path.len = last - start;
+ path.data = start;
+
+ start = last + 1;
+
+ while (start < end && *start == ' ') { start++; }
+
+ if (start == end) {
+ continue;
+ }
+
+ if (*start == ',') {
+ start++;
+ goto next_link;
+ }
+
+ if (*start++ != ';') {
+ continue;
+ }
+
+ last = ngx_strlchr(start, end, ',');
+
+ if (last == NULL) {
+ last = end;
+ }
+
+ push = 0;
+
+ for ( ;; ) {
+
+ while (start < last && *start == ' ') { start++; }
+
+ if (last - start >= 6
+ && ngx_strncasecmp(start, (u_char *) "nopush", 6) == 0)
+ {
+ start += 6;
+
+ if (start == last || *start == ' ' || *start == ';') {
+ push = 0;
+ break;
+ }
+
+ goto next_param;
+ }
+
+ if (last - start >= 11
+ && ngx_strncasecmp(start, (u_char *) "rel=preload", 11) == 0)
+ {
+ start += 11;
+
+ if (start == last || *start == ' ' || *start == ';') {
+ push = 1;
+ }
+
+ goto next_param;
+ }
+
+ if (last - start >= 4
+ && ngx_strncasecmp(start, (u_char *) "rel=", 4) == 0)
+ {
+ start += 4;
+
+ while (start < last && *start == ' ') { start++; }
+
+ if (start == last || *start++ != '"') {
+ goto next_param;
+ }
+
+ for ( ;; ) {
+
+ while (start < last && *start == ' ') { start++; }
+
+ if (last - start >= 7
+ && ngx_strncasecmp(start, (u_char *) "preload", 7) == 0)
+ {
+ start += 7;
+
+ if (start < last && (*start == ' ' || *start == '"')) {
+ push = 1;
+ break;
+ }
+ }
+
+ while (start < last && *start != ' ' && *start != '"') {
+ start++;
+ }
+
+ if (start == last) {
+ break;
+ }
+
+ if (*start == '"') {
+ break;
+ }
+
+ start++;
+ }
+ }
+
+ next_param:
+
+ start = ngx_strlchr(start, last, ';');
+
+ if (start == NULL) {
+ break;
+ }
+
+ start++;
+ }
+
+ if (push) {
+ while (path.len && path.data[path.len - 1] == ' ') {
+ path.len--;
+ }
+ }
+
+ if (push && path.len
+ && !(path.len > 1 && path.data[0] == '/' && path.data[1] == '/'))
+ {
+ rc = ngx_http_v2_push_resource(r, &path, binary);
+
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (rc == NGX_ABORT) {
+ return NGX_OK;
+ }
+
+ /* NGX_OK, NGX_DECLINED */
+ }
+
+ if (last < end) {
+ start = last + 1;
+ goto next_link;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_v2_push_resource(ngx_http_request_t *r, ngx_str_t *path,
+ ngx_str_t *binary)
+{
+ u_char *start, *pos, *tmp;
+ size_t len;
+ ngx_str_t *value;
+ ngx_uint_t i;
+ ngx_table_elt_t **h;
+ ngx_connection_t *fc;
+ ngx_http_v2_stream_t *stream;
+ ngx_http_v2_out_frame_t *frame;
+ ngx_http_v2_connection_t *h2c;
+ ngx_http_v2_push_header_t *ph;
+
+ fc = r->connection;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 push resource");
+
+ stream = r->stream;
+ h2c = stream->connection;
+
+ if (!ngx_path_separator(path->data[0])) {
+ ngx_log_error(NGX_LOG_WARN, fc->log, 0,
+ "non-absolute path \"%V\" not pushed", path);
+ return NGX_DECLINED;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 pushing:%ui limit:%ui",
+ h2c->pushing, h2c->concurrent_pushes);
+
+ if (h2c->pushing >= h2c->concurrent_pushes) {
+ return NGX_ABORT;
+ }
+
+ if (h2c->last_push == 0x7ffffffe) {
+ return NGX_ABORT;
+ }
+
+ if (path->len > NGX_HTTP_V2_MAX_FIELD) {
+ return NGX_DECLINED;
+ }
+
+ if (r->headers_in.host == NULL) {
+ return NGX_ABORT;
+ }
+
+ ph = ngx_http_v2_push_headers;
+
+ len = ngx_max(r->schema.len, path->len);
+
+ if (binary[0].len) {
+ tmp = ngx_palloc(r->pool, len);
+ if (tmp == NULL) {
+ return NGX_ERROR;
+ }
+
+ } else {
+ for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
+ h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);
+
+ if (*h) {
+ len = ngx_max(len, (*h)->value.len);
+ }
+ }
+
+ tmp = ngx_palloc(r->pool, len);
+ if (tmp == NULL) {
+ return NGX_ERROR;
+ }
+
+ for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
+ h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);
+
+ if (*h == NULL) {
+ continue;
+ }
+
+ value = &(*h)->value;
+
+ len = 1 + NGX_HTTP_V2_INT_OCTETS + value->len;
+
+ pos = ngx_pnalloc(r->pool, len);
+ if (pos == NULL) {
+ return NGX_ERROR;
+ }
+
+ binary[i].data = pos;
+
+ *pos++ = ngx_http_v2_inc_indexed(ph[i].index);
+ pos = ngx_http_v2_write_value(pos, value->data, value->len, tmp);
+
+ binary[i].len = pos - binary[i].data;
+ }
+ }
+
+ len = (h2c->table_update ? 1 : 0)
+ + 1
+ + 1 + NGX_HTTP_V2_INT_OCTETS + path->len
+ + 1 + NGX_HTTP_V2_INT_OCTETS + r->schema.len;
+
+ for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
+ len += binary[i].len;
+ }
+
+ pos = ngx_pnalloc(r->pool, len);
+ if (pos == NULL) {
+ return NGX_ERROR;
+ }
+
+ start = pos;
+
+ if (h2c->table_update) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 table size update: 0");
+ *pos++ = (1 << 5) | 0;
+ h2c->table_update = 0;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 push header: \":method: GET\"");
+
+ *pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_GET_INDEX);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 push header: \":path: %V\"", path);
+
+ *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);
+ pos = ngx_http_v2_write_value(pos, path->data, path->len, tmp);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 push header: \":scheme: %V\"", &r->schema);
+
+ if (r->schema.len == 5 && ngx_strncmp(r->schema.data, "https", 5) == 0) {
+ *pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTPS_INDEX);
+
+ } else if (r->schema.len == 4
+ && ngx_strncmp(r->schema.data, "http", 4) == 0)
+ {
+ *pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX);
+
+ } else {
+ *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX);
+ pos = ngx_http_v2_write_value(pos, r->schema.data, r->schema.len, tmp);
+ }
+
+ for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
+ h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);
+
+ if (*h == NULL) {
+ continue;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 push header: \"%V: %V\"",
+ &ph[i].name, &(*h)->value);
+
+ pos = ngx_cpymem(pos, binary[i].data, binary[i].len);
+ }
+
+ frame = ngx_http_v2_create_push_frame(r, start, pos);
+ if (frame == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_v2_queue_blocked_frame(h2c, frame);
+
+ stream->queued++;
+
+ stream = ngx_http_v2_push_stream(stream, path);
+
+ if (stream) {
+ stream->request->request_length = pos - start;
+ return NGX_OK;
+ }
+
+ return NGX_ERROR;
+}
+
+
+static ngx_http_v2_out_frame_t *
+ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos,
+ u_char *end, ngx_uint_t fin)
+{
+ u_char type, flags;
+ size_t rest, frame_size;
+ ngx_buf_t *b;
+ ngx_chain_t *cl, **ll;
+ ngx_http_v2_stream_t *stream;
+ ngx_http_v2_out_frame_t *frame;
+
+ stream = r->stream;
+ rest = end - pos;
+
+ frame = ngx_palloc(r->pool, sizeof(ngx_http_v2_out_frame_t));
+ if (frame == NULL) {
+ return NULL;
+ }
+
+ frame->handler = ngx_http_v2_headers_frame_handler;
+ frame->stream = stream;
+ frame->length = rest;
+ frame->blocked = 1;
+ frame->fin = fin;
+
+ ll = &frame->first;
+
+ type = NGX_HTTP_V2_HEADERS_FRAME;
+ flags = fin ? NGX_HTTP_V2_END_STREAM_FLAG : NGX_HTTP_V2_NO_FLAG;
+ frame_size = stream->connection->frame_size;
+
+ for ( ;; ) {
+ if (rest <= frame_size) {
+ frame_size = rest;
+ flags |= NGX_HTTP_V2_END_HEADERS_FLAG;
+ }
+
+ b = ngx_create_temp_buf(r->pool, NGX_HTTP_V2_FRAME_HEADER_SIZE);
+ if (b == NULL) {
+ return NULL;
+ }
+
+ b->last = ngx_http_v2_write_len_and_type(b->last, frame_size, type);
+ *b->last++ = flags;
+ b->last = ngx_http_v2_write_sid(b->last, stream->node->id);
+
+ b->tag = (ngx_buf_tag_t) &ngx_http_v2_module;
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ cl->buf = b;
+
+ *ll = cl;
+ ll = &cl->next;
+
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ return NULL;
+ }
+
+ b->pos = pos;
+
+ pos += frame_size;
+
+ b->last = pos;
+ b->start = b->pos;
+ b->end = b->last;
+ b->temporary = 1;
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ cl->buf = b;
+
+ *ll = cl;
+ ll = &cl->next;
+
+ rest -= frame_size;
+
+ if (rest) {
+ frame->length += NGX_HTTP_V2_FRAME_HEADER_SIZE;
+
+ type = NGX_HTTP_V2_CONTINUATION_FRAME;
+ flags = NGX_HTTP_V2_NO_FLAG;
+ continue;
+ }
+
+ b->last_buf = fin;
+ cl->next = NULL;
+ frame->last = cl;
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http2:%ui create HEADERS frame %p: len:%uz fin:%ui",
+ stream->node->id, frame, frame->length, fin);
+
+ return frame;
+ }
+}
+
+
+static ngx_http_v2_out_frame_t *
+ngx_http_v2_create_push_frame(ngx_http_request_t *r, u_char *pos, u_char *end)
+{
+ u_char type, flags;
+ size_t rest, frame_size, len;
+ ngx_buf_t *b;
+ ngx_chain_t *cl, **ll;
+ ngx_http_v2_stream_t *stream;
+ ngx_http_v2_out_frame_t *frame;
+ ngx_http_v2_connection_t *h2c;
+
+ stream = r->stream;
+ h2c = stream->connection;
+ rest = NGX_HTTP_V2_STREAM_ID_SIZE + (end - pos);
+
+ frame = ngx_palloc(r->pool, sizeof(ngx_http_v2_out_frame_t));
+ if (frame == NULL) {
+ return NULL;
+ }
+
+ frame->handler = ngx_http_v2_push_frame_handler;
+ frame->stream = stream;
+ frame->length = rest;
+ frame->blocked = 1;
+ frame->fin = 0;
+
+ ll = &frame->first;
+
+ type = NGX_HTTP_V2_PUSH_PROMISE_FRAME;
+ flags = NGX_HTTP_V2_NO_FLAG;
+ frame_size = h2c->frame_size;
+
+ for ( ;; ) {
+ if (rest <= frame_size) {
+ frame_size = rest;
+ flags |= NGX_HTTP_V2_END_HEADERS_FLAG;
+ }
+
+ b = ngx_create_temp_buf(r->pool,
+ NGX_HTTP_V2_FRAME_HEADER_SIZE
+ + ((type == NGX_HTTP_V2_PUSH_PROMISE_FRAME)
+ ? NGX_HTTP_V2_STREAM_ID_SIZE : 0));
+ if (b == NULL) {
+ return NULL;
+ }
+
+ b->last = ngx_http_v2_write_len_and_type(b->last, frame_size, type);
+ *b->last++ = flags;
+ b->last = ngx_http_v2_write_sid(b->last, stream->node->id);
+
+ b->tag = (ngx_buf_tag_t) &ngx_http_v2_module;
+
+ if (type == NGX_HTTP_V2_PUSH_PROMISE_FRAME) {
+ h2c->last_push += 2;
+
+ b->last = ngx_http_v2_write_sid(b->last, h2c->last_push);
+ len = frame_size - NGX_HTTP_V2_STREAM_ID_SIZE;
+
+ } else {
+ len = frame_size;
+ }
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ cl->buf = b;
+
+ *ll = cl;
+ ll = &cl->next;
+
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ return NULL;
+ }
+
+ b->pos = pos;
+
+ pos += len;
+
+ b->last = pos;
+ b->start = b->pos;
+ b->end = b->last;
+ b->temporary = 1;
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ cl->buf = b;
+
+ *ll = cl;
+ ll = &cl->next;
+
+ rest -= frame_size;
+
+ if (rest) {
+ frame->length += NGX_HTTP_V2_FRAME_HEADER_SIZE;
+
+ type = NGX_HTTP_V2_CONTINUATION_FRAME;
+ continue;
+ }
+
+ cl->next = NULL;
+ frame->last = cl;
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http2:%ui create PUSH_PROMISE frame %p: "
+ "sid:%ui len:%uz",
+ stream->node->id, frame, h2c->last_push,
+ frame->length);
+
+ return frame;
+ }
+}
+
+
+static ngx_http_v2_out_frame_t *
+ngx_http_v2_create_trailers_frame(ngx_http_request_t *r)
+{
+ u_char *pos, *start, *tmp;
+ size_t len, tmp_len;
+ ngx_uint_t i;
+ ngx_list_part_t *part;
+ ngx_table_elt_t *header;
+ ngx_connection_t *fc;
+
+ fc = r->connection;
+ len = 0;
+ tmp_len = 0;
+
+ part = &r->headers_out.trailers.part;
+ header = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ if (header[i].hash == 0) {
+ continue;
+ }
+
+ if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) {
+ ngx_log_error(NGX_LOG_CRIT, fc->log, 0,
+ "too long response trailer name: \"%V\"",
+ &header[i].key);
+ return NULL;
+ }
+
+ if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) {
+ ngx_log_error(NGX_LOG_CRIT, fc->log, 0,
+ "too long response trailer value: \"%V: %V\"",
+ &header[i].key, &header[i].value);
+ return NULL;
+ }
+
+ len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len
+ + NGX_HTTP_V2_INT_OCTETS + header[i].value.len;
+
+ if (header[i].key.len > tmp_len) {
+ tmp_len = header[i].key.len;
+ }
+
+ if (header[i].value.len > tmp_len) {
+ tmp_len = header[i].value.len;
+ }
+ }
+
+ if (len == 0) {
+ return NGX_HTTP_V2_NO_TRAILERS;
+ }
+
+ tmp = ngx_palloc(r->pool, tmp_len);
+ pos = ngx_pnalloc(r->pool, len);
+
+ if (pos == NULL || tmp == NULL) {
+ return NULL;
+ }
+
+ start = pos;
+
+ part = &r->headers_out.trailers.part;
+ header = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ if (header[i].hash == 0) {
+ continue;
+ }
+
+#if (NGX_DEBUG)
+ if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {
+ ngx_strlow(tmp, header[i].key.data, header[i].key.len);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 output trailer: \"%*s: %V\"",
+ header[i].key.len, tmp, &header[i].value);
+ }
+#endif
+
+ *pos++ = 0;
+
+ pos = ngx_http_v2_write_name(pos, header[i].key.data,
+ header[i].key.len, tmp);
+
+ pos = ngx_http_v2_write_value(pos, header[i].value.data,
+ header[i].value.len, tmp);
+ }
+
+ return ngx_http_v2_create_headers_frame(r, start, pos, 1);
+}
+
+
+static ngx_chain_t *
+ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)
+{
+ off_t size, offset;
+ size_t rest, frame_size;
+ ngx_chain_t *cl, *out, **ln;
+ ngx_http_request_t *r;
+ ngx_http_v2_stream_t *stream;
+ ngx_http_v2_loc_conf_t *h2lcf;
+ ngx_http_v2_out_frame_t *frame, *trailers;
+ ngx_http_v2_connection_t *h2c;
+
+ r = fc->data;
+ stream = r->stream;
+
+#if (NGX_SUPPRESS_WARN)
+ size = 0;
+#endif
+
+ while (in) {
+ size = ngx_buf_size(in->buf);
+
+ if (size || in->buf->last_buf) {
+ break;
+ }
+
+ in = in->next;
+ }
+
+ if (in == NULL || stream->out_closed) {
+
+ if (size) {
+ ngx_log_error(NGX_LOG_ERR, fc->log, 0,
+ "output on closed stream");
+ return NGX_CHAIN_ERROR;
+ }
+
+ if (stream->queued) {
+ fc->write->active = 1;
+ fc->write->ready = 0;
+
+ } else {
+ fc->buffered &= ~NGX_HTTP_V2_BUFFERED;
+ }
+
+ return NULL;
+ }
+
+ h2c = stream->connection;
+
+ if (size && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
+ fc->write->active = 1;
+ fc->write->ready = 0;
+ return in;
+ }
+
+ if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ cl->buf = in->buf;
+ in->buf = cl->buf->shadow;
+
+ offset = ngx_buf_in_memory(in->buf)
+ ? (cl->buf->pos - in->buf->pos)
+ : (cl->buf->file_pos - in->buf->file_pos);
+
+ cl->next = stream->free_bufs;
+ stream->free_bufs = cl;
+
+ } else {
+ offset = 0;
+ }
+
+ if (limit == 0 || limit > (off_t) h2c->send_window) {
+ limit = h2c->send_window;
+ }
+
+ if (limit > stream->send_window) {
+ limit = (stream->send_window > 0) ? stream->send_window : 0;
+ }
+
+ h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module);
+
+ frame_size = (h2lcf->chunk_size < h2c->frame_size)
+ ? h2lcf->chunk_size : h2c->frame_size;
+
+ trailers = NGX_HTTP_V2_NO_TRAILERS;
+
+#if (NGX_SUPPRESS_WARN)
+ cl = NULL;
+#endif
+
+ for ( ;; ) {
+ if ((off_t) frame_size > limit) {
+ frame_size = (size_t) limit;
+ }
+
+ ln = &out;
+ rest = frame_size;
+
+ while ((off_t) rest >= size) {
+
+ if (offset) {
+ cl = ngx_http_v2_filter_get_shadow(stream, in->buf,
+ offset, size);
+ if (cl == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ offset = 0;
+
+ } else {
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ cl->buf = in->buf;
+ }
+
+ *ln = cl;
+ ln = &cl->next;
+
+ rest -= (size_t) size;
+ in = in->next;
+
+ if (in == NULL) {
+ frame_size -= rest;
+ rest = 0;
+ break;
+ }
+
+ size = ngx_buf_size(in->buf);
+ }
+
+ if (rest) {
+ cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, rest);
+ if (cl == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ cl->buf->flush = 0;
+ cl->buf->last_buf = 0;
+
+ *ln = cl;
+
+ offset += rest;
+ size -= rest;
+ }
+
+ if (cl->buf->last_buf) {
+ trailers = ngx_http_v2_create_trailers_frame(r);
+ if (trailers == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ if (trailers != NGX_HTTP_V2_NO_TRAILERS) {
+ cl->buf->last_buf = 0;
+ }
+ }
+
+ if (frame_size || cl->buf->last_buf) {
+ frame = ngx_http_v2_filter_get_data_frame(stream, frame_size,
+ out, cl);
+ if (frame == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ ngx_http_v2_queue_frame(h2c, frame);
+
+ h2c->send_window -= frame_size;
+
+ stream->send_window -= frame_size;
+ stream->queued++;
+ }
+
+ if (in == NULL) {
+
+ if (trailers != NGX_HTTP_V2_NO_TRAILERS) {
+ ngx_http_v2_queue_frame(h2c, trailers);
+ stream->queued++;
+ }
+
+ break;
+ }
+
+ limit -= frame_size;
+
+ if (limit == 0) {
+ break;
+ }
+ }
+
+ if (offset) {
+ cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, size);
+ if (cl == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ in->buf = cl->buf;
+ ngx_free_chain(r->pool, cl);
+ }
+
+ if (ngx_http_v2_filter_send(fc, stream) == NGX_ERROR) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ if (in && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
+ fc->write->active = 1;
+ fc->write->ready = 0;
+ }
+
+ return in;
+}
+
+
+static ngx_chain_t *
+ngx_http_v2_filter_get_shadow(ngx_http_v2_stream_t *stream, ngx_buf_t *buf,
+ off_t offset, off_t size)
+{
+ ngx_buf_t *chunk;
+ ngx_chain_t *cl;
+
+ cl = ngx_chain_get_free_buf(stream->request->pool, &stream->free_bufs);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ chunk = cl->buf;
+
+ ngx_memcpy(chunk, buf, sizeof(ngx_buf_t));
+
+ chunk->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow;
+ chunk->shadow = buf;
+
+ if (ngx_buf_in_memory(chunk)) {
+ chunk->pos += offset;
+ chunk->last = chunk->pos + size;
+ }
+
+ if (chunk->in_file) {
+ chunk->file_pos += offset;
+ chunk->file_last = chunk->file_pos + size;
+ }
+
+ return cl;
+}
+
+
+static ngx_http_v2_out_frame_t *
+ngx_http_v2_filter_get_data_frame(ngx_http_v2_stream_t *stream,
+ size_t len, ngx_chain_t *first, ngx_chain_t *last)
+{
+ u_char flags;
+ ngx_buf_t *buf;
+ ngx_chain_t *cl;
+ ngx_http_v2_out_frame_t *frame;
+ ngx_http_v2_connection_t *h2c;
+
+ frame = stream->free_frames;
+ h2c = stream->connection;
+
+ if (frame) {
+ stream->free_frames = frame->next;
+
+ } else if (h2c->frames < 10000) {
+ frame = ngx_palloc(stream->request->pool,
+ sizeof(ngx_http_v2_out_frame_t));
+ if (frame == NULL) {
+ return NULL;
+ }
+
+ stream->frames++;
+ h2c->frames++;
+
+ } else {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "http2 flood detected");
+
+ h2c->connection->error = 1;
+ return NULL;
+ }
+
+ flags = last->buf->last_buf ? NGX_HTTP_V2_END_STREAM_FLAG : 0;
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,
+ "http2:%ui create DATA frame %p: len:%uz flags:%ui",
+ stream->node->id, frame, len, (ngx_uint_t) flags);
+
+ cl = ngx_chain_get_free_buf(stream->request->pool,
+ &stream->free_frame_headers);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ buf = cl->buf;
+
+ if (buf->start == NULL) {
+ buf->start = ngx_palloc(stream->request->pool,
+ NGX_HTTP_V2_FRAME_HEADER_SIZE);
+ if (buf->start == NULL) {
+ return NULL;
+ }
+
+ buf->end = buf->start + NGX_HTTP_V2_FRAME_HEADER_SIZE;
+ buf->last = buf->end;
+
+ buf->tag = (ngx_buf_tag_t) &ngx_http_v2_module;
+ buf->memory = 1;
+ }
+
+ buf->pos = buf->start;
+ buf->last = buf->pos;
+
+ buf->last = ngx_http_v2_write_len_and_type(buf->last, len,
+ NGX_HTTP_V2_DATA_FRAME);
+ *buf->last++ = flags;
+
+ buf->last = ngx_http_v2_write_sid(buf->last, stream->node->id);
+
+ cl->next = first;
+ first = cl;
+
+ last->buf->flush = 1;
+
+ frame->first = first;
+ frame->last = last;
+ frame->handler = ngx_http_v2_data_frame_handler;
+ frame->stream = stream;
+ frame->length = len;
+ frame->blocked = 0;
+ frame->fin = last->buf->last_buf;
+
+ return frame;
+}
+
+
+static ngx_inline ngx_int_t
+ngx_http_v2_flow_control(ngx_http_v2_connection_t *h2c,
+ ngx_http_v2_stream_t *stream)
+{
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2:%ui windows: conn:%uz stream:%z",
+ stream->node->id, h2c->send_window, stream->send_window);
+
+ if (stream->send_window <= 0) {
+ stream->exhausted = 1;
+ return NGX_DECLINED;
+ }
+
+ if (h2c->send_window == 0) {
+ ngx_http_v2_waiting_queue(h2c, stream);
+ return NGX_DECLINED;
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_v2_waiting_queue(ngx_http_v2_connection_t *h2c,
+ ngx_http_v2_stream_t *stream)
+{
+ ngx_queue_t *q;
+ ngx_http_v2_stream_t *s;
+
+ if (stream->waiting) {
+ return;
+ }
+
+ stream->waiting = 1;
+
+ for (q = ngx_queue_last(&h2c->waiting);
+ q != ngx_queue_sentinel(&h2c->waiting);
+ q = ngx_queue_prev(q))
+ {
+ s = ngx_queue_data(q, ngx_http_v2_stream_t, queue);
+
+ if (s->node->rank < stream->node->rank
+ || (s->node->rank == stream->node->rank
+ && s->node->rel_weight >= stream->node->rel_weight))
+ {
+ break;
+ }
+ }
+
+ ngx_queue_insert_after(q, &stream->queue);
+}
+
+
+static ngx_inline ngx_int_t
+ngx_http_v2_filter_send(ngx_connection_t *fc, ngx_http_v2_stream_t *stream)
+{
+ stream->blocked = 1;
+
+ if (ngx_http_v2_send_output_queue(stream->connection) == NGX_ERROR) {
+ fc->error = 1;
+ return NGX_ERROR;
+ }
+
+ stream->blocked = 0;
+
+ if (stream->queued) {
+ fc->buffered |= NGX_HTTP_V2_BUFFERED;
+ fc->write->active = 1;
+ fc->write->ready = 0;
+ return NGX_AGAIN;
+ }
+
+ fc->buffered &= ~NGX_HTTP_V2_BUFFERED;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_v2_headers_frame_handler(ngx_http_v2_connection_t *h2c,
+ ngx_http_v2_out_frame_t *frame)
+{
+ ngx_chain_t *cl, *ln;
+ ngx_http_v2_stream_t *stream;
+
+ stream = frame->stream;
+ cl = frame->first;
+
+ for ( ;; ) {
+ if (cl->buf->pos != cl->buf->last) {
+ frame->first = cl;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2:%ui HEADERS frame %p was sent partially",
+ stream->node->id, frame);
+
+ return NGX_AGAIN;
+ }
+
+ ln = cl->next;
+
+ if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) {
+ cl->next = stream->free_frame_headers;
+ stream->free_frame_headers = cl;
+
+ } else {
+ cl->next = stream->free_bufs;
+ stream->free_bufs = cl;
+ }
+
+ if (cl == frame->last) {
+ break;
+ }
+
+ cl = ln;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2:%ui HEADERS frame %p was sent",
+ stream->node->id, frame);
+
+ stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE
+ + frame->length;
+
+ h2c->payload_bytes += frame->length;
+
+ ngx_http_v2_handle_frame(stream, frame);
+
+ ngx_http_v2_handle_stream(h2c, stream);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_v2_push_frame_handler(ngx_http_v2_connection_t *h2c,
+ ngx_http_v2_out_frame_t *frame)
+{
+ ngx_chain_t *cl, *ln;
+ ngx_http_v2_stream_t *stream;
+
+ stream = frame->stream;
+ cl = frame->first;
+
+ for ( ;; ) {
+ if (cl->buf->pos != cl->buf->last) {
+ frame->first = cl;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2:%ui PUSH_PROMISE frame %p was sent partially",
+ stream->node->id, frame);
+
+ return NGX_AGAIN;
+ }
+
+ ln = cl->next;
+
+ if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) {
+ cl->next = stream->free_frame_headers;
+ stream->free_frame_headers = cl;
+
+ } else {
+ cl->next = stream->free_bufs;
+ stream->free_bufs = cl;
+ }
+
+ if (cl == frame->last) {
+ break;
+ }
+
+ cl = ln;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2:%ui PUSH_PROMISE frame %p was sent",
+ stream->node->id, frame);
+
+ stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE
+ + frame->length;
+
+ h2c->payload_bytes += frame->length;
+
+ ngx_http_v2_handle_frame(stream, frame);
+
+ ngx_http_v2_handle_stream(h2c, stream);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_v2_data_frame_handler(ngx_http_v2_connection_t *h2c,
+ ngx_http_v2_out_frame_t *frame)
+{
+ ngx_buf_t *buf;
+ ngx_chain_t *cl, *ln;
+ ngx_http_v2_stream_t *stream;
+
+ stream = frame->stream;
+ cl = frame->first;
+
+ if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) {
+
+ if (cl->buf->pos != cl->buf->last) {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2:%ui DATA frame %p was sent partially",
+ stream->node->id, frame);
+
+ return NGX_AGAIN;
+ }
+
+ ln = cl->next;
+
+ cl->next = stream->free_frame_headers;
+ stream->free_frame_headers = cl;
+
+ if (cl == frame->last) {
+ goto done;
+ }
+
+ cl = ln;
+ }
+
+ for ( ;; ) {
+ if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
+ buf = cl->buf->shadow;
+
+ if (ngx_buf_in_memory(buf)) {
+ buf->pos = cl->buf->pos;
+ }
+
+ if (buf->in_file) {
+ buf->file_pos = cl->buf->file_pos;
+ }
+ }
+
+ if (ngx_buf_size(cl->buf) != 0) {
+
+ if (cl != frame->first) {
+ frame->first = cl;
+ ngx_http_v2_handle_stream(h2c, stream);
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2:%ui DATA frame %p was sent partially",
+ stream->node->id, frame);
+
+ return NGX_AGAIN;
+ }
+
+ ln = cl->next;
+
+ if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
+ cl->next = stream->free_bufs;
+ stream->free_bufs = cl;
+
+ } else {
+ ngx_free_chain(stream->request->pool, cl);
+ }
+
+ if (cl == frame->last) {
+ goto done;
+ }
+
+ cl = ln;
+ }
+
+done:
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2:%ui DATA frame %p was sent",
+ stream->node->id, frame);
+
+ stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE;
+
+ h2c->payload_bytes += frame->length;
+
+ ngx_http_v2_handle_frame(stream, frame);
+
+ ngx_http_v2_handle_stream(h2c, stream);
+
+ return NGX_OK;
+}
+
+
+static ngx_inline void
+ngx_http_v2_handle_frame(ngx_http_v2_stream_t *stream,
+ ngx_http_v2_out_frame_t *frame)
+{
+ ngx_http_request_t *r;
+ ngx_http_v2_connection_t *h2c;
+
+ r = stream->request;
+
+ r->connection->sent += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length;
+
+ h2c = stream->connection;
+
+ h2c->total_bytes += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length;
+
+ if (frame->fin) {
+ stream->out_closed = 1;
+ }
+
+ frame->next = stream->free_frames;
+ stream->free_frames = frame;
+
+ stream->queued--;
+}
+
+
+static ngx_inline void
+ngx_http_v2_handle_stream(ngx_http_v2_connection_t *h2c,
+ ngx_http_v2_stream_t *stream)
+{
+ ngx_event_t *wev;
+ ngx_connection_t *fc;
+
+ if (stream->waiting || stream->blocked) {
+ return;
+ }
+
+ fc = stream->request->connection;
+
+ if (!fc->error && stream->exhausted) {
+ return;
+ }
+
+ wev = fc->write;
+
+ wev->active = 0;
+ wev->ready = 1;
+
+ if (!fc->error && wev->delayed) {
+ return;
+ }
+
+ ngx_post_event(wev, &ngx_posted_events);
+}
+
+
+static void
+ngx_http_v2_filter_cleanup(void *data)
+{
+ ngx_http_v2_stream_t *stream = data;
+
+ size_t window;
+ ngx_event_t *wev;
+ ngx_queue_t *q;
+ ngx_http_v2_out_frame_t *frame, **fn;
+ ngx_http_v2_connection_t *h2c;
+
+ if (stream->waiting) {
+ stream->waiting = 0;
+ ngx_queue_remove(&stream->queue);
+ }
+
+ if (stream->queued == 0) {
+ return;
+ }
+
+ window = 0;
+ h2c = stream->connection;
+ fn = &h2c->last_out;
+
+ for ( ;; ) {
+ frame = *fn;
+
+ if (frame == NULL) {
+ break;
+ }
+
+ if (frame->stream == stream && !frame->blocked) {
+ *fn = frame->next;
+
+ window += frame->length;
+
+ if (--stream->queued == 0) {
+ break;
+ }
+
+ continue;
+ }
+
+ fn = &frame->next;
+ }
+
+ if (h2c->send_window == 0 && window) {
+
+ while (!ngx_queue_empty(&h2c->waiting)) {
+ q = ngx_queue_head(&h2c->waiting);
+
+ ngx_queue_remove(q);
+
+ stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue);
+
+ stream->waiting = 0;
+
+ wev = stream->request->connection->write;
+
+ wev->active = 0;
+ wev->ready = 1;
+
+ if (!wev->delayed) {
+ ngx_post_event(wev, &ngx_posted_events);
+ }
+ }
+ }
+
+ h2c->send_window += window;
+}
+
+
+static ngx_int_t
+ngx_http_v2_filter_init(ngx_conf_t *cf)
+{
+ ngx_http_next_header_filter = ngx_http_top_header_filter;
+ ngx_http_top_header_filter = ngx_http_v2_header_filter;
+
+ return NGX_OK;
+}
diff --git a/src/nginx-1.18.0/src/http/v2/ngx_http_v2_huff_decode.c b/src/nginx-1.18.0/src/http/v2/ngx_http_v2_huff_decode.c
new file mode 100644
index 0000000..49ca576
--- /dev/null
+++ b/src/nginx-1.18.0/src/http/v2/ngx_http_v2_huff_decode.c
@@ -0,0 +1,2714 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ * Copyright (C) Valentin V. Bartenev
+ */
+
+
+#include
+#include
+#include
+
+
+typedef struct {
+ u_char next;
+ u_char emit;
+ u_char sym;
+ u_char ending;
+} ngx_http_v2_huff_decode_code_t;
+
+
+static ngx_inline ngx_int_t ngx_http_v2_huff_decode_bits(u_char *state,
+ u_char *ending, ngx_uint_t bits, u_char **dst);
+
+
+static ngx_http_v2_huff_decode_code_t ngx_http_v2_huff_decode_codes[256][16] =
+{
+ /* 0 */
+ {
+ {0x04, 0x00, 0x00, 0x00}, {0x05, 0x00, 0x00, 0x00},
+ {0x07, 0x00, 0x00, 0x00}, {0x08, 0x00, 0x00, 0x00},
+ {0x0b, 0x00, 0x00, 0x00}, {0x0c, 0x00, 0x00, 0x00},
+ {0x10, 0x00, 0x00, 0x00}, {0x13, 0x00, 0x00, 0x00},
+ {0x19, 0x00, 0x00, 0x00}, {0x1c, 0x00, 0x00, 0x00},
+ {0x20, 0x00, 0x00, 0x00}, {0x23, 0x00, 0x00, 0x00},
+ {0x2a, 0x00, 0x00, 0x00}, {0x31, 0x00, 0x00, 0x00},
+ {0x39, 0x00, 0x00, 0x00}, {0x40, 0x00, 0x00, 0x01}
+ },
+ {
+ {0x00, 0x01, 0x30, 0x01}, {0x00, 0x01, 0x31, 0x01},
+ {0x00, 0x01, 0x32, 0x01}, {0x00, 0x01, 0x61, 0x01},
+ {0x00, 0x01, 0x63, 0x01}, {0x00, 0x01, 0x65, 0x01},
+ {0x00, 0x01, 0x69, 0x01}, {0x00, 0x01, 0x6f, 0x01},
+ {0x00, 0x01, 0x73, 0x01}, {0x00, 0x01, 0x74, 0x01},
+ {0x0d, 0x00, 0x00, 0x00}, {0x0e, 0x00, 0x00, 0x00},
+ {0x11, 0x00, 0x00, 0x00}, {0x12, 0x00, 0x00, 0x00},
+ {0x14, 0x00, 0x00, 0x00}, {0x15, 0x00, 0x00, 0x00}
+ },
+ {
+ {0x01, 0x01, 0x30, 0x00}, {0x16, 0x01, 0x30, 0x01},
+ {0x01, 0x01, 0x31, 0x00}, {0x16, 0x01, 0x31, 0x01},
+ {0x01, 0x01, 0x32, 0x00}, {0x16, 0x01, 0x32, 0x01},
+ {0x01, 0x01, 0x61, 0x00}, {0x16, 0x01, 0x61, 0x01},
+ {0x01, 0x01, 0x63, 0x00}, {0x16, 0x01, 0x63, 0x01},
+ {0x01, 0x01, 0x65, 0x00}, {0x16, 0x01, 0x65, 0x01},
+ {0x01, 0x01, 0x69, 0x00}, {0x16, 0x01, 0x69, 0x01},
+ {0x01, 0x01, 0x6f, 0x00}, {0x16, 0x01, 0x6f, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x30, 0x00}, {0x09, 0x01, 0x30, 0x00},
+ {0x17, 0x01, 0x30, 0x00}, {0x28, 0x01, 0x30, 0x01},
+ {0x02, 0x01, 0x31, 0x00}, {0x09, 0x01, 0x31, 0x00},
+ {0x17, 0x01, 0x31, 0x00}, {0x28, 0x01, 0x31, 0x01},
+ {0x02, 0x01, 0x32, 0x00}, {0x09, 0x01, 0x32, 0x00},
+ {0x17, 0x01, 0x32, 0x00}, {0x28, 0x01, 0x32, 0x01},
+ {0x02, 0x01, 0x61, 0x00}, {0x09, 0x01, 0x61, 0x00},
+ {0x17, 0x01, 0x61, 0x00}, {0x28, 0x01, 0x61, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x30, 0x00}, {0x06, 0x01, 0x30, 0x00},
+ {0x0a, 0x01, 0x30, 0x00}, {0x0f, 0x01, 0x30, 0x00},
+ {0x18, 0x01, 0x30, 0x00}, {0x1f, 0x01, 0x30, 0x00},
+ {0x29, 0x01, 0x30, 0x00}, {0x38, 0x01, 0x30, 0x01},
+ {0x03, 0x01, 0x31, 0x00}, {0x06, 0x01, 0x31, 0x00},
+ {0x0a, 0x01, 0x31, 0x00}, {0x0f, 0x01, 0x31, 0x00},
+ {0x18, 0x01, 0x31, 0x00}, {0x1f, 0x01, 0x31, 0x00},
+ {0x29, 0x01, 0x31, 0x00}, {0x38, 0x01, 0x31, 0x01}
+ },
+ /* 5 */
+ {
+ {0x03, 0x01, 0x32, 0x00}, {0x06, 0x01, 0x32, 0x00},
+ {0x0a, 0x01, 0x32, 0x00}, {0x0f, 0x01, 0x32, 0x00},
+ {0x18, 0x01, 0x32, 0x00}, {0x1f, 0x01, 0x32, 0x00},
+ {0x29, 0x01, 0x32, 0x00}, {0x38, 0x01, 0x32, 0x01},
+ {0x03, 0x01, 0x61, 0x00}, {0x06, 0x01, 0x61, 0x00},
+ {0x0a, 0x01, 0x61, 0x00}, {0x0f, 0x01, 0x61, 0x00},
+ {0x18, 0x01, 0x61, 0x00}, {0x1f, 0x01, 0x61, 0x00},
+ {0x29, 0x01, 0x61, 0x00}, {0x38, 0x01, 0x61, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x63, 0x00}, {0x09, 0x01, 0x63, 0x00},
+ {0x17, 0x01, 0x63, 0x00}, {0x28, 0x01, 0x63, 0x01},
+ {0x02, 0x01, 0x65, 0x00}, {0x09, 0x01, 0x65, 0x00},
+ {0x17, 0x01, 0x65, 0x00}, {0x28, 0x01, 0x65, 0x01},
+ {0x02, 0x01, 0x69, 0x00}, {0x09, 0x01, 0x69, 0x00},
+ {0x17, 0x01, 0x69, 0x00}, {0x28, 0x01, 0x69, 0x01},
+ {0x02, 0x01, 0x6f, 0x00}, {0x09, 0x01, 0x6f, 0x00},
+ {0x17, 0x01, 0x6f, 0x00}, {0x28, 0x01, 0x6f, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x63, 0x00}, {0x06, 0x01, 0x63, 0x00},
+ {0x0a, 0x01, 0x63, 0x00}, {0x0f, 0x01, 0x63, 0x00},
+ {0x18, 0x01, 0x63, 0x00}, {0x1f, 0x01, 0x63, 0x00},
+ {0x29, 0x01, 0x63, 0x00}, {0x38, 0x01, 0x63, 0x01},
+ {0x03, 0x01, 0x65, 0x00}, {0x06, 0x01, 0x65, 0x00},
+ {0x0a, 0x01, 0x65, 0x00}, {0x0f, 0x01, 0x65, 0x00},
+ {0x18, 0x01, 0x65, 0x00}, {0x1f, 0x01, 0x65, 0x00},
+ {0x29, 0x01, 0x65, 0x00}, {0x38, 0x01, 0x65, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x69, 0x00}, {0x06, 0x01, 0x69, 0x00},
+ {0x0a, 0x01, 0x69, 0x00}, {0x0f, 0x01, 0x69, 0x00},
+ {0x18, 0x01, 0x69, 0x00}, {0x1f, 0x01, 0x69, 0x00},
+ {0x29, 0x01, 0x69, 0x00}, {0x38, 0x01, 0x69, 0x01},
+ {0x03, 0x01, 0x6f, 0x00}, {0x06, 0x01, 0x6f, 0x00},
+ {0x0a, 0x01, 0x6f, 0x00}, {0x0f, 0x01, 0x6f, 0x00},
+ {0x18, 0x01, 0x6f, 0x00}, {0x1f, 0x01, 0x6f, 0x00},
+ {0x29, 0x01, 0x6f, 0x00}, {0x38, 0x01, 0x6f, 0x01}
+ },
+ {
+ {0x01, 0x01, 0x73, 0x00}, {0x16, 0x01, 0x73, 0x01},
+ {0x01, 0x01, 0x74, 0x00}, {0x16, 0x01, 0x74, 0x01},
+ {0x00, 0x01, 0x20, 0x01}, {0x00, 0x01, 0x25, 0x01},
+ {0x00, 0x01, 0x2d, 0x01}, {0x00, 0x01, 0x2e, 0x01},
+ {0x00, 0x01, 0x2f, 0x01}, {0x00, 0x01, 0x33, 0x01},
+ {0x00, 0x01, 0x34, 0x01}, {0x00, 0x01, 0x35, 0x01},
+ {0x00, 0x01, 0x36, 0x01}, {0x00, 0x01, 0x37, 0x01},
+ {0x00, 0x01, 0x38, 0x01}, {0x00, 0x01, 0x39, 0x01}
+ },
+ /* 10 */
+ {
+ {0x02, 0x01, 0x73, 0x00}, {0x09, 0x01, 0x73, 0x00},
+ {0x17, 0x01, 0x73, 0x00}, {0x28, 0x01, 0x73, 0x01},
+ {0x02, 0x01, 0x74, 0x00}, {0x09, 0x01, 0x74, 0x00},
+ {0x17, 0x01, 0x74, 0x00}, {0x28, 0x01, 0x74, 0x01},
+ {0x01, 0x01, 0x20, 0x00}, {0x16, 0x01, 0x20, 0x01},
+ {0x01, 0x01, 0x25, 0x00}, {0x16, 0x01, 0x25, 0x01},
+ {0x01, 0x01, 0x2d, 0x00}, {0x16, 0x01, 0x2d, 0x01},
+ {0x01, 0x01, 0x2e, 0x00}, {0x16, 0x01, 0x2e, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x73, 0x00}, {0x06, 0x01, 0x73, 0x00},
+ {0x0a, 0x01, 0x73, 0x00}, {0x0f, 0x01, 0x73, 0x00},
+ {0x18, 0x01, 0x73, 0x00}, {0x1f, 0x01, 0x73, 0x00},
+ {0x29, 0x01, 0x73, 0x00}, {0x38, 0x01, 0x73, 0x01},
+ {0x03, 0x01, 0x74, 0x00}, {0x06, 0x01, 0x74, 0x00},
+ {0x0a, 0x01, 0x74, 0x00}, {0x0f, 0x01, 0x74, 0x00},
+ {0x18, 0x01, 0x74, 0x00}, {0x1f, 0x01, 0x74, 0x00},
+ {0x29, 0x01, 0x74, 0x00}, {0x38, 0x01, 0x74, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x20, 0x00}, {0x09, 0x01, 0x20, 0x00},
+ {0x17, 0x01, 0x20, 0x00}, {0x28, 0x01, 0x20, 0x01},
+ {0x02, 0x01, 0x25, 0x00}, {0x09, 0x01, 0x25, 0x00},
+ {0x17, 0x01, 0x25, 0x00}, {0x28, 0x01, 0x25, 0x01},
+ {0x02, 0x01, 0x2d, 0x00}, {0x09, 0x01, 0x2d, 0x00},
+ {0x17, 0x01, 0x2d, 0x00}, {0x28, 0x01, 0x2d, 0x01},
+ {0x02, 0x01, 0x2e, 0x00}, {0x09, 0x01, 0x2e, 0x00},
+ {0x17, 0x01, 0x2e, 0x00}, {0x28, 0x01, 0x2e, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x20, 0x00}, {0x06, 0x01, 0x20, 0x00},
+ {0x0a, 0x01, 0x20, 0x00}, {0x0f, 0x01, 0x20, 0x00},
+ {0x18, 0x01, 0x20, 0x00}, {0x1f, 0x01, 0x20, 0x00},
+ {0x29, 0x01, 0x20, 0x00}, {0x38, 0x01, 0x20, 0x01},
+ {0x03, 0x01, 0x25, 0x00}, {0x06, 0x01, 0x25, 0x00},
+ {0x0a, 0x01, 0x25, 0x00}, {0x0f, 0x01, 0x25, 0x00},
+ {0x18, 0x01, 0x25, 0x00}, {0x1f, 0x01, 0x25, 0x00},
+ {0x29, 0x01, 0x25, 0x00}, {0x38, 0x01, 0x25, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x2d, 0x00}, {0x06, 0x01, 0x2d, 0x00},
+ {0x0a, 0x01, 0x2d, 0x00}, {0x0f, 0x01, 0x2d, 0x00},
+ {0x18, 0x01, 0x2d, 0x00}, {0x1f, 0x01, 0x2d, 0x00},
+ {0x29, 0x01, 0x2d, 0x00}, {0x38, 0x01, 0x2d, 0x01},
+ {0x03, 0x01, 0x2e, 0x00}, {0x06, 0x01, 0x2e, 0x00},
+ {0x0a, 0x01, 0x2e, 0x00}, {0x0f, 0x01, 0x2e, 0x00},
+ {0x18, 0x01, 0x2e, 0x00}, {0x1f, 0x01, 0x2e, 0x00},
+ {0x29, 0x01, 0x2e, 0x00}, {0x38, 0x01, 0x2e, 0x01}
+ },
+ /* 15 */
+ {
+ {0x01, 0x01, 0x2f, 0x00}, {0x16, 0x01, 0x2f, 0x01},
+ {0x01, 0x01, 0x33, 0x00}, {0x16, 0x01, 0x33, 0x01},
+ {0x01, 0x01, 0x34, 0x00}, {0x16, 0x01, 0x34, 0x01},
+ {0x01, 0x01, 0x35, 0x00}, {0x16, 0x01, 0x35, 0x01},
+ {0x01, 0x01, 0x36, 0x00}, {0x16, 0x01, 0x36, 0x01},
+ {0x01, 0x01, 0x37, 0x00}, {0x16, 0x01, 0x37, 0x01},
+ {0x01, 0x01, 0x38, 0x00}, {0x16, 0x01, 0x38, 0x01},
+ {0x01, 0x01, 0x39, 0x00}, {0x16, 0x01, 0x39, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x2f, 0x00}, {0x09, 0x01, 0x2f, 0x00},
+ {0x17, 0x01, 0x2f, 0x00}, {0x28, 0x01, 0x2f, 0x01},
+ {0x02, 0x01, 0x33, 0x00}, {0x09, 0x01, 0x33, 0x00},
+ {0x17, 0x01, 0x33, 0x00}, {0x28, 0x01, 0x33, 0x01},
+ {0x02, 0x01, 0x34, 0x00}, {0x09, 0x01, 0x34, 0x00},
+ {0x17, 0x01, 0x34, 0x00}, {0x28, 0x01, 0x34, 0x01},
+ {0x02, 0x01, 0x35, 0x00}, {0x09, 0x01, 0x35, 0x00},
+ {0x17, 0x01, 0x35, 0x00}, {0x28, 0x01, 0x35, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x2f, 0x00}, {0x06, 0x01, 0x2f, 0x00},
+ {0x0a, 0x01, 0x2f, 0x00}, {0x0f, 0x01, 0x2f, 0x00},
+ {0x18, 0x01, 0x2f, 0x00}, {0x1f, 0x01, 0x2f, 0x00},
+ {0x29, 0x01, 0x2f, 0x00}, {0x38, 0x01, 0x2f, 0x01},
+ {0x03, 0x01, 0x33, 0x00}, {0x06, 0x01, 0x33, 0x00},
+ {0x0a, 0x01, 0x33, 0x00}, {0x0f, 0x01, 0x33, 0x00},
+ {0x18, 0x01, 0x33, 0x00}, {0x1f, 0x01, 0x33, 0x00},
+ {0x29, 0x01, 0x33, 0x00}, {0x38, 0x01, 0x33, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x34, 0x00}, {0x06, 0x01, 0x34, 0x00},
+ {0x0a, 0x01, 0x34, 0x00}, {0x0f, 0x01, 0x34, 0x00},
+ {0x18, 0x01, 0x34, 0x00}, {0x1f, 0x01, 0x34, 0x00},
+ {0x29, 0x01, 0x34, 0x00}, {0x38, 0x01, 0x34, 0x01},
+ {0x03, 0x01, 0x35, 0x00}, {0x06, 0x01, 0x35, 0x00},
+ {0x0a, 0x01, 0x35, 0x00}, {0x0f, 0x01, 0x35, 0x00},
+ {0x18, 0x01, 0x35, 0x00}, {0x1f, 0x01, 0x35, 0x00},
+ {0x29, 0x01, 0x35, 0x00}, {0x38, 0x01, 0x35, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x36, 0x00}, {0x09, 0x01, 0x36, 0x00},
+ {0x17, 0x01, 0x36, 0x00}, {0x28, 0x01, 0x36, 0x01},
+ {0x02, 0x01, 0x37, 0x00}, {0x09, 0x01, 0x37, 0x00},
+ {0x17, 0x01, 0x37, 0x00}, {0x28, 0x01, 0x37, 0x01},
+ {0x02, 0x01, 0x38, 0x00}, {0x09, 0x01, 0x38, 0x00},
+ {0x17, 0x01, 0x38, 0x00}, {0x28, 0x01, 0x38, 0x01},
+ {0x02, 0x01, 0x39, 0x00}, {0x09, 0x01, 0x39, 0x00},
+ {0x17, 0x01, 0x39, 0x00}, {0x28, 0x01, 0x39, 0x01}
+ },
+ /* 20 */
+ {
+ {0x03, 0x01, 0x36, 0x00}, {0x06, 0x01, 0x36, 0x00},
+ {0x0a, 0x01, 0x36, 0x00}, {0x0f, 0x01, 0x36, 0x00},
+ {0x18, 0x01, 0x36, 0x00}, {0x1f, 0x01, 0x36, 0x00},
+ {0x29, 0x01, 0x36, 0x00}, {0x38, 0x01, 0x36, 0x01},
+ {0x03, 0x01, 0x37, 0x00}, {0x06, 0x01, 0x37, 0x00},
+ {0x0a, 0x01, 0x37, 0x00}, {0x0f, 0x01, 0x37, 0x00},
+ {0x18, 0x01, 0x37, 0x00}, {0x1f, 0x01, 0x37, 0x00},
+ {0x29, 0x01, 0x37, 0x00}, {0x38, 0x01, 0x37, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x38, 0x00}, {0x06, 0x01, 0x38, 0x00},
+ {0x0a, 0x01, 0x38, 0x00}, {0x0f, 0x01, 0x38, 0x00},
+ {0x18, 0x01, 0x38, 0x00}, {0x1f, 0x01, 0x38, 0x00},
+ {0x29, 0x01, 0x38, 0x00}, {0x38, 0x01, 0x38, 0x01},
+ {0x03, 0x01, 0x39, 0x00}, {0x06, 0x01, 0x39, 0x00},
+ {0x0a, 0x01, 0x39, 0x00}, {0x0f, 0x01, 0x39, 0x00},
+ {0x18, 0x01, 0x39, 0x00}, {0x1f, 0x01, 0x39, 0x00},
+ {0x29, 0x01, 0x39, 0x00}, {0x38, 0x01, 0x39, 0x01}
+ },
+ {
+ {0x1a, 0x00, 0x00, 0x00}, {0x1b, 0x00, 0x00, 0x00},
+ {0x1d, 0x00, 0x00, 0x00}, {0x1e, 0x00, 0x00, 0x00},
+ {0x21, 0x00, 0x00, 0x00}, {0x22, 0x00, 0x00, 0x00},
+ {0x24, 0x00, 0x00, 0x00}, {0x25, 0x00, 0x00, 0x00},
+ {0x2b, 0x00, 0x00, 0x00}, {0x2e, 0x00, 0x00, 0x00},
+ {0x32, 0x00, 0x00, 0x00}, {0x35, 0x00, 0x00, 0x00},
+ {0x3a, 0x00, 0x00, 0x00}, {0x3d, 0x00, 0x00, 0x00},
+ {0x41, 0x00, 0x00, 0x00}, {0x44, 0x00, 0x00, 0x01}
+ },
+ {
+ {0x00, 0x01, 0x3d, 0x01}, {0x00, 0x01, 0x41, 0x01},
+ {0x00, 0x01, 0x5f, 0x01}, {0x00, 0x01, 0x62, 0x01},
+ {0x00, 0x01, 0x64, 0x01}, {0x00, 0x01, 0x66, 0x01},
+ {0x00, 0x01, 0x67, 0x01}, {0x00, 0x01, 0x68, 0x01},
+ {0x00, 0x01, 0x6c, 0x01}, {0x00, 0x01, 0x6d, 0x01},
+ {0x00, 0x01, 0x6e, 0x01}, {0x00, 0x01, 0x70, 0x01},
+ {0x00, 0x01, 0x72, 0x01}, {0x00, 0x01, 0x75, 0x01},
+ {0x26, 0x00, 0x00, 0x00}, {0x27, 0x00, 0x00, 0x00}
+ },
+ {
+ {0x01, 0x01, 0x3d, 0x00}, {0x16, 0x01, 0x3d, 0x01},
+ {0x01, 0x01, 0x41, 0x00}, {0x16, 0x01, 0x41, 0x01},
+ {0x01, 0x01, 0x5f, 0x00}, {0x16, 0x01, 0x5f, 0x01},
+ {0x01, 0x01, 0x62, 0x00}, {0x16, 0x01, 0x62, 0x01},
+ {0x01, 0x01, 0x64, 0x00}, {0x16, 0x01, 0x64, 0x01},
+ {0x01, 0x01, 0x66, 0x00}, {0x16, 0x01, 0x66, 0x01},
+ {0x01, 0x01, 0x67, 0x00}, {0x16, 0x01, 0x67, 0x01},
+ {0x01, 0x01, 0x68, 0x00}, {0x16, 0x01, 0x68, 0x01}
+ },
+ /* 25 */
+ {
+ {0x02, 0x01, 0x3d, 0x00}, {0x09, 0x01, 0x3d, 0x00},
+ {0x17, 0x01, 0x3d, 0x00}, {0x28, 0x01, 0x3d, 0x01},
+ {0x02, 0x01, 0x41, 0x00}, {0x09, 0x01, 0x41, 0x00},
+ {0x17, 0x01, 0x41, 0x00}, {0x28, 0x01, 0x41, 0x01},
+ {0x02, 0x01, 0x5f, 0x00}, {0x09, 0x01, 0x5f, 0x00},
+ {0x17, 0x01, 0x5f, 0x00}, {0x28, 0x01, 0x5f, 0x01},
+ {0x02, 0x01, 0x62, 0x00}, {0x09, 0x01, 0x62, 0x00},
+ {0x17, 0x01, 0x62, 0x00}, {0x28, 0x01, 0x62, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x3d, 0x00}, {0x06, 0x01, 0x3d, 0x00},
+ {0x0a, 0x01, 0x3d, 0x00}, {0x0f, 0x01, 0x3d, 0x00},
+ {0x18, 0x01, 0x3d, 0x00}, {0x1f, 0x01, 0x3d, 0x00},
+ {0x29, 0x01, 0x3d, 0x00}, {0x38, 0x01, 0x3d, 0x01},
+ {0x03, 0x01, 0x41, 0x00}, {0x06, 0x01, 0x41, 0x00},
+ {0x0a, 0x01, 0x41, 0x00}, {0x0f, 0x01, 0x41, 0x00},
+ {0x18, 0x01, 0x41, 0x00}, {0x1f, 0x01, 0x41, 0x00},
+ {0x29, 0x01, 0x41, 0x00}, {0x38, 0x01, 0x41, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x5f, 0x00}, {0x06, 0x01, 0x5f, 0x00},
+ {0x0a, 0x01, 0x5f, 0x00}, {0x0f, 0x01, 0x5f, 0x00},
+ {0x18, 0x01, 0x5f, 0x00}, {0x1f, 0x01, 0x5f, 0x00},
+ {0x29, 0x01, 0x5f, 0x00}, {0x38, 0x01, 0x5f, 0x01},
+ {0x03, 0x01, 0x62, 0x00}, {0x06, 0x01, 0x62, 0x00},
+ {0x0a, 0x01, 0x62, 0x00}, {0x0f, 0x01, 0x62, 0x00},
+ {0x18, 0x01, 0x62, 0x00}, {0x1f, 0x01, 0x62, 0x00},
+ {0x29, 0x01, 0x62, 0x00}, {0x38, 0x01, 0x62, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x64, 0x00}, {0x09, 0x01, 0x64, 0x00},
+ {0x17, 0x01, 0x64, 0x00}, {0x28, 0x01, 0x64, 0x01},
+ {0x02, 0x01, 0x66, 0x00}, {0x09, 0x01, 0x66, 0x00},
+ {0x17, 0x01, 0x66, 0x00}, {0x28, 0x01, 0x66, 0x01},
+ {0x02, 0x01, 0x67, 0x00}, {0x09, 0x01, 0x67, 0x00},
+ {0x17, 0x01, 0x67, 0x00}, {0x28, 0x01, 0x67, 0x01},
+ {0x02, 0x01, 0x68, 0x00}, {0x09, 0x01, 0x68, 0x00},
+ {0x17, 0x01, 0x68, 0x00}, {0x28, 0x01, 0x68, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x64, 0x00}, {0x06, 0x01, 0x64, 0x00},
+ {0x0a, 0x01, 0x64, 0x00}, {0x0f, 0x01, 0x64, 0x00},
+ {0x18, 0x01, 0x64, 0x00}, {0x1f, 0x01, 0x64, 0x00},
+ {0x29, 0x01, 0x64, 0x00}, {0x38, 0x01, 0x64, 0x01},
+ {0x03, 0x01, 0x66, 0x00}, {0x06, 0x01, 0x66, 0x00},
+ {0x0a, 0x01, 0x66, 0x00}, {0x0f, 0x01, 0x66, 0x00},
+ {0x18, 0x01, 0x66, 0x00}, {0x1f, 0x01, 0x66, 0x00},
+ {0x29, 0x01, 0x66, 0x00}, {0x38, 0x01, 0x66, 0x01}
+ },
+ /* 30 */
+ {
+ {0x03, 0x01, 0x67, 0x00}, {0x06, 0x01, 0x67, 0x00},
+ {0x0a, 0x01, 0x67, 0x00}, {0x0f, 0x01, 0x67, 0x00},
+ {0x18, 0x01, 0x67, 0x00}, {0x1f, 0x01, 0x67, 0x00},
+ {0x29, 0x01, 0x67, 0x00}, {0x38, 0x01, 0x67, 0x01},
+ {0x03, 0x01, 0x68, 0x00}, {0x06, 0x01, 0x68, 0x00},
+ {0x0a, 0x01, 0x68, 0x00}, {0x0f, 0x01, 0x68, 0x00},
+ {0x18, 0x01, 0x68, 0x00}, {0x1f, 0x01, 0x68, 0x00},
+ {0x29, 0x01, 0x68, 0x00}, {0x38, 0x01, 0x68, 0x01}
+ },
+ {
+ {0x01, 0x01, 0x6c, 0x00}, {0x16, 0x01, 0x6c, 0x01},
+ {0x01, 0x01, 0x6d, 0x00}, {0x16, 0x01, 0x6d, 0x01},
+ {0x01, 0x01, 0x6e, 0x00}, {0x16, 0x01, 0x6e, 0x01},
+ {0x01, 0x01, 0x70, 0x00}, {0x16, 0x01, 0x70, 0x01},
+ {0x01, 0x01, 0x72, 0x00}, {0x16, 0x01, 0x72, 0x01},
+ {0x01, 0x01, 0x75, 0x00}, {0x16, 0x01, 0x75, 0x01},
+ {0x00, 0x01, 0x3a, 0x01}, {0x00, 0x01, 0x42, 0x01},
+ {0x00, 0x01, 0x43, 0x01}, {0x00, 0x01, 0x44, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x6c, 0x00}, {0x09, 0x01, 0x6c, 0x00},
+ {0x17, 0x01, 0x6c, 0x00}, {0x28, 0x01, 0x6c, 0x01},
+ {0x02, 0x01, 0x6d, 0x00}, {0x09, 0x01, 0x6d, 0x00},
+ {0x17, 0x01, 0x6d, 0x00}, {0x28, 0x01, 0x6d, 0x01},
+ {0x02, 0x01, 0x6e, 0x00}, {0x09, 0x01, 0x6e, 0x00},
+ {0x17, 0x01, 0x6e, 0x00}, {0x28, 0x01, 0x6e, 0x01},
+ {0x02, 0x01, 0x70, 0x00}, {0x09, 0x01, 0x70, 0x00},
+ {0x17, 0x01, 0x70, 0x00}, {0x28, 0x01, 0x70, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x6c, 0x00}, {0x06, 0x01, 0x6c, 0x00},
+ {0x0a, 0x01, 0x6c, 0x00}, {0x0f, 0x01, 0x6c, 0x00},
+ {0x18, 0x01, 0x6c, 0x00}, {0x1f, 0x01, 0x6c, 0x00},
+ {0x29, 0x01, 0x6c, 0x00}, {0x38, 0x01, 0x6c, 0x01},
+ {0x03, 0x01, 0x6d, 0x00}, {0x06, 0x01, 0x6d, 0x00},
+ {0x0a, 0x01, 0x6d, 0x00}, {0x0f, 0x01, 0x6d, 0x00},
+ {0x18, 0x01, 0x6d, 0x00}, {0x1f, 0x01, 0x6d, 0x00},
+ {0x29, 0x01, 0x6d, 0x00}, {0x38, 0x01, 0x6d, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x6e, 0x00}, {0x06, 0x01, 0x6e, 0x00},
+ {0x0a, 0x01, 0x6e, 0x00}, {0x0f, 0x01, 0x6e, 0x00},
+ {0x18, 0x01, 0x6e, 0x00}, {0x1f, 0x01, 0x6e, 0x00},
+ {0x29, 0x01, 0x6e, 0x00}, {0x38, 0x01, 0x6e, 0x01},
+ {0x03, 0x01, 0x70, 0x00}, {0x06, 0x01, 0x70, 0x00},
+ {0x0a, 0x01, 0x70, 0x00}, {0x0f, 0x01, 0x70, 0x00},
+ {0x18, 0x01, 0x70, 0x00}, {0x1f, 0x01, 0x70, 0x00},
+ {0x29, 0x01, 0x70, 0x00}, {0x38, 0x01, 0x70, 0x01}
+ },
+ /* 35 */
+ {
+ {0x02, 0x01, 0x72, 0x00}, {0x09, 0x01, 0x72, 0x00},
+ {0x17, 0x01, 0x72, 0x00}, {0x28, 0x01, 0x72, 0x01},
+ {0x02, 0x01, 0x75, 0x00}, {0x09, 0x01, 0x75, 0x00},
+ {0x17, 0x01, 0x75, 0x00}, {0x28, 0x01, 0x75, 0x01},
+ {0x01, 0x01, 0x3a, 0x00}, {0x16, 0x01, 0x3a, 0x01},
+ {0x01, 0x01, 0x42, 0x00}, {0x16, 0x01, 0x42, 0x01},
+ {0x01, 0x01, 0x43, 0x00}, {0x16, 0x01, 0x43, 0x01},
+ {0x01, 0x01, 0x44, 0x00}, {0x16, 0x01, 0x44, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x72, 0x00}, {0x06, 0x01, 0x72, 0x00},
+ {0x0a, 0x01, 0x72, 0x00}, {0x0f, 0x01, 0x72, 0x00},
+ {0x18, 0x01, 0x72, 0x00}, {0x1f, 0x01, 0x72, 0x00},
+ {0x29, 0x01, 0x72, 0x00}, {0x38, 0x01, 0x72, 0x01},
+ {0x03, 0x01, 0x75, 0x00}, {0x06, 0x01, 0x75, 0x00},
+ {0x0a, 0x01, 0x75, 0x00}, {0x0f, 0x01, 0x75, 0x00},
+ {0x18, 0x01, 0x75, 0x00}, {0x1f, 0x01, 0x75, 0x00},
+ {0x29, 0x01, 0x75, 0x00}, {0x38, 0x01, 0x75, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x3a, 0x00}, {0x09, 0x01, 0x3a, 0x00},
+ {0x17, 0x01, 0x3a, 0x00}, {0x28, 0x01, 0x3a, 0x01},
+ {0x02, 0x01, 0x42, 0x00}, {0x09, 0x01, 0x42, 0x00},
+ {0x17, 0x01, 0x42, 0x00}, {0x28, 0x01, 0x42, 0x01},
+ {0x02, 0x01, 0x43, 0x00}, {0x09, 0x01, 0x43, 0x00},
+ {0x17, 0x01, 0x43, 0x00}, {0x28, 0x01, 0x43, 0x01},
+ {0x02, 0x01, 0x44, 0x00}, {0x09, 0x01, 0x44, 0x00},
+ {0x17, 0x01, 0x44, 0x00}, {0x28, 0x01, 0x44, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x3a, 0x00}, {0x06, 0x01, 0x3a, 0x00},
+ {0x0a, 0x01, 0x3a, 0x00}, {0x0f, 0x01, 0x3a, 0x00},
+ {0x18, 0x01, 0x3a, 0x00}, {0x1f, 0x01, 0x3a, 0x00},
+ {0x29, 0x01, 0x3a, 0x00}, {0x38, 0x01, 0x3a, 0x01},
+ {0x03, 0x01, 0x42, 0x00}, {0x06, 0x01, 0x42, 0x00},
+ {0x0a, 0x01, 0x42, 0x00}, {0x0f, 0x01, 0x42, 0x00},
+ {0x18, 0x01, 0x42, 0x00}, {0x1f, 0x01, 0x42, 0x00},
+ {0x29, 0x01, 0x42, 0x00}, {0x38, 0x01, 0x42, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x43, 0x00}, {0x06, 0x01, 0x43, 0x00},
+ {0x0a, 0x01, 0x43, 0x00}, {0x0f, 0x01, 0x43, 0x00},
+ {0x18, 0x01, 0x43, 0x00}, {0x1f, 0x01, 0x43, 0x00},
+ {0x29, 0x01, 0x43, 0x00}, {0x38, 0x01, 0x43, 0x01},
+ {0x03, 0x01, 0x44, 0x00}, {0x06, 0x01, 0x44, 0x00},
+ {0x0a, 0x01, 0x44, 0x00}, {0x0f, 0x01, 0x44, 0x00},
+ {0x18, 0x01, 0x44, 0x00}, {0x1f, 0x01, 0x44, 0x00},
+ {0x29, 0x01, 0x44, 0x00}, {0x38, 0x01, 0x44, 0x01}
+ },
+ /* 40 */
+ {
+ {0x2c, 0x00, 0x00, 0x00}, {0x2d, 0x00, 0x00, 0x00},
+ {0x2f, 0x00, 0x00, 0x00}, {0x30, 0x00, 0x00, 0x00},
+ {0x33, 0x00, 0x00, 0x00}, {0x34, 0x00, 0x00, 0x00},
+ {0x36, 0x00, 0x00, 0x00}, {0x37, 0x00, 0x00, 0x00},
+ {0x3b, 0x00, 0x00, 0x00}, {0x3c, 0x00, 0x00, 0x00},
+ {0x3e, 0x00, 0x00, 0x00}, {0x3f, 0x00, 0x00, 0x00},
+ {0x42, 0x00, 0x00, 0x00}, {0x43, 0x00, 0x00, 0x00},
+ {0x45, 0x00, 0x00, 0x00}, {0x48, 0x00, 0x00, 0x01}
+ },
+ {
+ {0x00, 0x01, 0x45, 0x01}, {0x00, 0x01, 0x46, 0x01},
+ {0x00, 0x01, 0x47, 0x01}, {0x00, 0x01, 0x48, 0x01},
+ {0x00, 0x01, 0x49, 0x01}, {0x00, 0x01, 0x4a, 0x01},
+ {0x00, 0x01, 0x4b, 0x01}, {0x00, 0x01, 0x4c, 0x01},
+ {0x00, 0x01, 0x4d, 0x01}, {0x00, 0x01, 0x4e, 0x01},
+ {0x00, 0x01, 0x4f, 0x01}, {0x00, 0x01, 0x50, 0x01},
+ {0x00, 0x01, 0x51, 0x01}, {0x00, 0x01, 0x52, 0x01},
+ {0x00, 0x01, 0x53, 0x01}, {0x00, 0x01, 0x54, 0x01}
+ },
+ {
+ {0x01, 0x01, 0x45, 0x00}, {0x16, 0x01, 0x45, 0x01},
+ {0x01, 0x01, 0x46, 0x00}, {0x16, 0x01, 0x46, 0x01},
+ {0x01, 0x01, 0x47, 0x00}, {0x16, 0x01, 0x47, 0x01},
+ {0x01, 0x01, 0x48, 0x00}, {0x16, 0x01, 0x48, 0x01},
+ {0x01, 0x01, 0x49, 0x00}, {0x16, 0x01, 0x49, 0x01},
+ {0x01, 0x01, 0x4a, 0x00}, {0x16, 0x01, 0x4a, 0x01},
+ {0x01, 0x01, 0x4b, 0x00}, {0x16, 0x01, 0x4b, 0x01},
+ {0x01, 0x01, 0x4c, 0x00}, {0x16, 0x01, 0x4c, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x45, 0x00}, {0x09, 0x01, 0x45, 0x00},
+ {0x17, 0x01, 0x45, 0x00}, {0x28, 0x01, 0x45, 0x01},
+ {0x02, 0x01, 0x46, 0x00}, {0x09, 0x01, 0x46, 0x00},
+ {0x17, 0x01, 0x46, 0x00}, {0x28, 0x01, 0x46, 0x01},
+ {0x02, 0x01, 0x47, 0x00}, {0x09, 0x01, 0x47, 0x00},
+ {0x17, 0x01, 0x47, 0x00}, {0x28, 0x01, 0x47, 0x01},
+ {0x02, 0x01, 0x48, 0x00}, {0x09, 0x01, 0x48, 0x00},
+ {0x17, 0x01, 0x48, 0x00}, {0x28, 0x01, 0x48, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x45, 0x00}, {0x06, 0x01, 0x45, 0x00},
+ {0x0a, 0x01, 0x45, 0x00}, {0x0f, 0x01, 0x45, 0x00},
+ {0x18, 0x01, 0x45, 0x00}, {0x1f, 0x01, 0x45, 0x00},
+ {0x29, 0x01, 0x45, 0x00}, {0x38, 0x01, 0x45, 0x01},
+ {0x03, 0x01, 0x46, 0x00}, {0x06, 0x01, 0x46, 0x00},
+ {0x0a, 0x01, 0x46, 0x00}, {0x0f, 0x01, 0x46, 0x00},
+ {0x18, 0x01, 0x46, 0x00}, {0x1f, 0x01, 0x46, 0x00},
+ {0x29, 0x01, 0x46, 0x00}, {0x38, 0x01, 0x46, 0x01}
+ },
+ /* 45 */
+ {
+ {0x03, 0x01, 0x47, 0x00}, {0x06, 0x01, 0x47, 0x00},
+ {0x0a, 0x01, 0x47, 0x00}, {0x0f, 0x01, 0x47, 0x00},
+ {0x18, 0x01, 0x47, 0x00}, {0x1f, 0x01, 0x47, 0x00},
+ {0x29, 0x01, 0x47, 0x00}, {0x38, 0x01, 0x47, 0x01},
+ {0x03, 0x01, 0x48, 0x00}, {0x06, 0x01, 0x48, 0x00},
+ {0x0a, 0x01, 0x48, 0x00}, {0x0f, 0x01, 0x48, 0x00},
+ {0x18, 0x01, 0x48, 0x00}, {0x1f, 0x01, 0x48, 0x00},
+ {0x29, 0x01, 0x48, 0x00}, {0x38, 0x01, 0x48, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x49, 0x00}, {0x09, 0x01, 0x49, 0x00},
+ {0x17, 0x01, 0x49, 0x00}, {0x28, 0x01, 0x49, 0x01},
+ {0x02, 0x01, 0x4a, 0x00}, {0x09, 0x01, 0x4a, 0x00},
+ {0x17, 0x01, 0x4a, 0x00}, {0x28, 0x01, 0x4a, 0x01},
+ {0x02, 0x01, 0x4b, 0x00}, {0x09, 0x01, 0x4b, 0x00},
+ {0x17, 0x01, 0x4b, 0x00}, {0x28, 0x01, 0x4b, 0x01},
+ {0x02, 0x01, 0x4c, 0x00}, {0x09, 0x01, 0x4c, 0x00},
+ {0x17, 0x01, 0x4c, 0x00}, {0x28, 0x01, 0x4c, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x49, 0x00}, {0x06, 0x01, 0x49, 0x00},
+ {0x0a, 0x01, 0x49, 0x00}, {0x0f, 0x01, 0x49, 0x00},
+ {0x18, 0x01, 0x49, 0x00}, {0x1f, 0x01, 0x49, 0x00},
+ {0x29, 0x01, 0x49, 0x00}, {0x38, 0x01, 0x49, 0x01},
+ {0x03, 0x01, 0x4a, 0x00}, {0x06, 0x01, 0x4a, 0x00},
+ {0x0a, 0x01, 0x4a, 0x00}, {0x0f, 0x01, 0x4a, 0x00},
+ {0x18, 0x01, 0x4a, 0x00}, {0x1f, 0x01, 0x4a, 0x00},
+ {0x29, 0x01, 0x4a, 0x00}, {0x38, 0x01, 0x4a, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x4b, 0x00}, {0x06, 0x01, 0x4b, 0x00},
+ {0x0a, 0x01, 0x4b, 0x00}, {0x0f, 0x01, 0x4b, 0x00},
+ {0x18, 0x01, 0x4b, 0x00}, {0x1f, 0x01, 0x4b, 0x00},
+ {0x29, 0x01, 0x4b, 0x00}, {0x38, 0x01, 0x4b, 0x01},
+ {0x03, 0x01, 0x4c, 0x00}, {0x06, 0x01, 0x4c, 0x00},
+ {0x0a, 0x01, 0x4c, 0x00}, {0x0f, 0x01, 0x4c, 0x00},
+ {0x18, 0x01, 0x4c, 0x00}, {0x1f, 0x01, 0x4c, 0x00},
+ {0x29, 0x01, 0x4c, 0x00}, {0x38, 0x01, 0x4c, 0x01}
+ },
+ {
+ {0x01, 0x01, 0x4d, 0x00}, {0x16, 0x01, 0x4d, 0x01},
+ {0x01, 0x01, 0x4e, 0x00}, {0x16, 0x01, 0x4e, 0x01},
+ {0x01, 0x01, 0x4f, 0x00}, {0x16, 0x01, 0x4f, 0x01},
+ {0x01, 0x01, 0x50, 0x00}, {0x16, 0x01, 0x50, 0x01},
+ {0x01, 0x01, 0x51, 0x00}, {0x16, 0x01, 0x51, 0x01},
+ {0x01, 0x01, 0x52, 0x00}, {0x16, 0x01, 0x52, 0x01},
+ {0x01, 0x01, 0x53, 0x00}, {0x16, 0x01, 0x53, 0x01},
+ {0x01, 0x01, 0x54, 0x00}, {0x16, 0x01, 0x54, 0x01}
+ },
+ /* 50 */
+ {
+ {0x02, 0x01, 0x4d, 0x00}, {0x09, 0x01, 0x4d, 0x00},
+ {0x17, 0x01, 0x4d, 0x00}, {0x28, 0x01, 0x4d, 0x01},
+ {0x02, 0x01, 0x4e, 0x00}, {0x09, 0x01, 0x4e, 0x00},
+ {0x17, 0x01, 0x4e, 0x00}, {0x28, 0x01, 0x4e, 0x01},
+ {0x02, 0x01, 0x4f, 0x00}, {0x09, 0x01, 0x4f, 0x00},
+ {0x17, 0x01, 0x4f, 0x00}, {0x28, 0x01, 0x4f, 0x01},
+ {0x02, 0x01, 0x50, 0x00}, {0x09, 0x01, 0x50, 0x00},
+ {0x17, 0x01, 0x50, 0x00}, {0x28, 0x01, 0x50, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x4d, 0x00}, {0x06, 0x01, 0x4d, 0x00},
+ {0x0a, 0x01, 0x4d, 0x00}, {0x0f, 0x01, 0x4d, 0x00},
+ {0x18, 0x01, 0x4d, 0x00}, {0x1f, 0x01, 0x4d, 0x00},
+ {0x29, 0x01, 0x4d, 0x00}, {0x38, 0x01, 0x4d, 0x01},
+ {0x03, 0x01, 0x4e, 0x00}, {0x06, 0x01, 0x4e, 0x00},
+ {0x0a, 0x01, 0x4e, 0x00}, {0x0f, 0x01, 0x4e, 0x00},
+ {0x18, 0x01, 0x4e, 0x00}, {0x1f, 0x01, 0x4e, 0x00},
+ {0x29, 0x01, 0x4e, 0x00}, {0x38, 0x01, 0x4e, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x4f, 0x00}, {0x06, 0x01, 0x4f, 0x00},
+ {0x0a, 0x01, 0x4f, 0x00}, {0x0f, 0x01, 0x4f, 0x00},
+ {0x18, 0x01, 0x4f, 0x00}, {0x1f, 0x01, 0x4f, 0x00},
+ {0x29, 0x01, 0x4f, 0x00}, {0x38, 0x01, 0x4f, 0x01},
+ {0x03, 0x01, 0x50, 0x00}, {0x06, 0x01, 0x50, 0x00},
+ {0x0a, 0x01, 0x50, 0x00}, {0x0f, 0x01, 0x50, 0x00},
+ {0x18, 0x01, 0x50, 0x00}, {0x1f, 0x01, 0x50, 0x00},
+ {0x29, 0x01, 0x50, 0x00}, {0x38, 0x01, 0x50, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x51, 0x00}, {0x09, 0x01, 0x51, 0x00},
+ {0x17, 0x01, 0x51, 0x00}, {0x28, 0x01, 0x51, 0x01},
+ {0x02, 0x01, 0x52, 0x00}, {0x09, 0x01, 0x52, 0x00},
+ {0x17, 0x01, 0x52, 0x00}, {0x28, 0x01, 0x52, 0x01},
+ {0x02, 0x01, 0x53, 0x00}, {0x09, 0x01, 0x53, 0x00},
+ {0x17, 0x01, 0x53, 0x00}, {0x28, 0x01, 0x53, 0x01},
+ {0x02, 0x01, 0x54, 0x00}, {0x09, 0x01, 0x54, 0x00},
+ {0x17, 0x01, 0x54, 0x00}, {0x28, 0x01, 0x54, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x51, 0x00}, {0x06, 0x01, 0x51, 0x00},
+ {0x0a, 0x01, 0x51, 0x00}, {0x0f, 0x01, 0x51, 0x00},
+ {0x18, 0x01, 0x51, 0x00}, {0x1f, 0x01, 0x51, 0x00},
+ {0x29, 0x01, 0x51, 0x00}, {0x38, 0x01, 0x51, 0x01},
+ {0x03, 0x01, 0x52, 0x00}, {0x06, 0x01, 0x52, 0x00},
+ {0x0a, 0x01, 0x52, 0x00}, {0x0f, 0x01, 0x52, 0x00},
+ {0x18, 0x01, 0x52, 0x00}, {0x1f, 0x01, 0x52, 0x00},
+ {0x29, 0x01, 0x52, 0x00}, {0x38, 0x01, 0x52, 0x01}
+ },
+ /* 55 */
+ {
+ {0x03, 0x01, 0x53, 0x00}, {0x06, 0x01, 0x53, 0x00},
+ {0x0a, 0x01, 0x53, 0x00}, {0x0f, 0x01, 0x53, 0x00},
+ {0x18, 0x01, 0x53, 0x00}, {0x1f, 0x01, 0x53, 0x00},
+ {0x29, 0x01, 0x53, 0x00}, {0x38, 0x01, 0x53, 0x01},
+ {0x03, 0x01, 0x54, 0x00}, {0x06, 0x01, 0x54, 0x00},
+ {0x0a, 0x01, 0x54, 0x00}, {0x0f, 0x01, 0x54, 0x00},
+ {0x18, 0x01, 0x54, 0x00}, {0x1f, 0x01, 0x54, 0x00},
+ {0x29, 0x01, 0x54, 0x00}, {0x38, 0x01, 0x54, 0x01}
+ },
+ {
+ {0x00, 0x01, 0x55, 0x01}, {0x00, 0x01, 0x56, 0x01},
+ {0x00, 0x01, 0x57, 0x01}, {0x00, 0x01, 0x59, 0x01},
+ {0x00, 0x01, 0x6a, 0x01}, {0x00, 0x01, 0x6b, 0x01},
+ {0x00, 0x01, 0x71, 0x01}, {0x00, 0x01, 0x76, 0x01},
+ {0x00, 0x01, 0x77, 0x01}, {0x00, 0x01, 0x78, 0x01},
+ {0x00, 0x01, 0x79, 0x01}, {0x00, 0x01, 0x7a, 0x01},
+ {0x46, 0x00, 0x00, 0x00}, {0x47, 0x00, 0x00, 0x00},
+ {0x49, 0x00, 0x00, 0x00}, {0x4a, 0x00, 0x00, 0x01}
+ },
+ {
+ {0x01, 0x01, 0x55, 0x00}, {0x16, 0x01, 0x55, 0x01},
+ {0x01, 0x01, 0x56, 0x00}, {0x16, 0x01, 0x56, 0x01},
+ {0x01, 0x01, 0x57, 0x00}, {0x16, 0x01, 0x57, 0x01},
+ {0x01, 0x01, 0x59, 0x00}, {0x16, 0x01, 0x59, 0x01},
+ {0x01, 0x01, 0x6a, 0x00}, {0x16, 0x01, 0x6a, 0x01},
+ {0x01, 0x01, 0x6b, 0x00}, {0x16, 0x01, 0x6b, 0x01},
+ {0x01, 0x01, 0x71, 0x00}, {0x16, 0x01, 0x71, 0x01},
+ {0x01, 0x01, 0x76, 0x00}, {0x16, 0x01, 0x76, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x55, 0x00}, {0x09, 0x01, 0x55, 0x00},
+ {0x17, 0x01, 0x55, 0x00}, {0x28, 0x01, 0x55, 0x01},
+ {0x02, 0x01, 0x56, 0x00}, {0x09, 0x01, 0x56, 0x00},
+ {0x17, 0x01, 0x56, 0x00}, {0x28, 0x01, 0x56, 0x01},
+ {0x02, 0x01, 0x57, 0x00}, {0x09, 0x01, 0x57, 0x00},
+ {0x17, 0x01, 0x57, 0x00}, {0x28, 0x01, 0x57, 0x01},
+ {0x02, 0x01, 0x59, 0x00}, {0x09, 0x01, 0x59, 0x00},
+ {0x17, 0x01, 0x59, 0x00}, {0x28, 0x01, 0x59, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x55, 0x00}, {0x06, 0x01, 0x55, 0x00},
+ {0x0a, 0x01, 0x55, 0x00}, {0x0f, 0x01, 0x55, 0x00},
+ {0x18, 0x01, 0x55, 0x00}, {0x1f, 0x01, 0x55, 0x00},
+ {0x29, 0x01, 0x55, 0x00}, {0x38, 0x01, 0x55, 0x01},
+ {0x03, 0x01, 0x56, 0x00}, {0x06, 0x01, 0x56, 0x00},
+ {0x0a, 0x01, 0x56, 0x00}, {0x0f, 0x01, 0x56, 0x00},
+ {0x18, 0x01, 0x56, 0x00}, {0x1f, 0x01, 0x56, 0x00},
+ {0x29, 0x01, 0x56, 0x00}, {0x38, 0x01, 0x56, 0x01}
+ },
+ /* 60 */
+ {
+ {0x03, 0x01, 0x57, 0x00}, {0x06, 0x01, 0x57, 0x00},
+ {0x0a, 0x01, 0x57, 0x00}, {0x0f, 0x01, 0x57, 0x00},
+ {0x18, 0x01, 0x57, 0x00}, {0x1f, 0x01, 0x57, 0x00},
+ {0x29, 0x01, 0x57, 0x00}, {0x38, 0x01, 0x57, 0x01},
+ {0x03, 0x01, 0x59, 0x00}, {0x06, 0x01, 0x59, 0x00},
+ {0x0a, 0x01, 0x59, 0x00}, {0x0f, 0x01, 0x59, 0x00},
+ {0x18, 0x01, 0x59, 0x00}, {0x1f, 0x01, 0x59, 0x00},
+ {0x29, 0x01, 0x59, 0x00}, {0x38, 0x01, 0x59, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x6a, 0x00}, {0x09, 0x01, 0x6a, 0x00},
+ {0x17, 0x01, 0x6a, 0x00}, {0x28, 0x01, 0x6a, 0x01},
+ {0x02, 0x01, 0x6b, 0x00}, {0x09, 0x01, 0x6b, 0x00},
+ {0x17, 0x01, 0x6b, 0x00}, {0x28, 0x01, 0x6b, 0x01},
+ {0x02, 0x01, 0x71, 0x00}, {0x09, 0x01, 0x71, 0x00},
+ {0x17, 0x01, 0x71, 0x00}, {0x28, 0x01, 0x71, 0x01},
+ {0x02, 0x01, 0x76, 0x00}, {0x09, 0x01, 0x76, 0x00},
+ {0x17, 0x01, 0x76, 0x00}, {0x28, 0x01, 0x76, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x6a, 0x00}, {0x06, 0x01, 0x6a, 0x00},
+ {0x0a, 0x01, 0x6a, 0x00}, {0x0f, 0x01, 0x6a, 0x00},
+ {0x18, 0x01, 0x6a, 0x00}, {0x1f, 0x01, 0x6a, 0x00},
+ {0x29, 0x01, 0x6a, 0x00}, {0x38, 0x01, 0x6a, 0x01},
+ {0x03, 0x01, 0x6b, 0x00}, {0x06, 0x01, 0x6b, 0x00},
+ {0x0a, 0x01, 0x6b, 0x00}, {0x0f, 0x01, 0x6b, 0x00},
+ {0x18, 0x01, 0x6b, 0x00}, {0x1f, 0x01, 0x6b, 0x00},
+ {0x29, 0x01, 0x6b, 0x00}, {0x38, 0x01, 0x6b, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x71, 0x00}, {0x06, 0x01, 0x71, 0x00},
+ {0x0a, 0x01, 0x71, 0x00}, {0x0f, 0x01, 0x71, 0x00},
+ {0x18, 0x01, 0x71, 0x00}, {0x1f, 0x01, 0x71, 0x00},
+ {0x29, 0x01, 0x71, 0x00}, {0x38, 0x01, 0x71, 0x01},
+ {0x03, 0x01, 0x76, 0x00}, {0x06, 0x01, 0x76, 0x00},
+ {0x0a, 0x01, 0x76, 0x00}, {0x0f, 0x01, 0x76, 0x00},
+ {0x18, 0x01, 0x76, 0x00}, {0x1f, 0x01, 0x76, 0x00},
+ {0x29, 0x01, 0x76, 0x00}, {0x38, 0x01, 0x76, 0x01}
+ },
+ {
+ {0x01, 0x01, 0x77, 0x00}, {0x16, 0x01, 0x77, 0x01},
+ {0x01, 0x01, 0x78, 0x00}, {0x16, 0x01, 0x78, 0x01},
+ {0x01, 0x01, 0x79, 0x00}, {0x16, 0x01, 0x79, 0x01},
+ {0x01, 0x01, 0x7a, 0x00}, {0x16, 0x01, 0x7a, 0x01},
+ {0x00, 0x01, 0x26, 0x01}, {0x00, 0x01, 0x2a, 0x01},
+ {0x00, 0x01, 0x2c, 0x01}, {0x00, 0x01, 0x3b, 0x01},
+ {0x00, 0x01, 0x58, 0x01}, {0x00, 0x01, 0x5a, 0x01},
+ {0x4b, 0x00, 0x00, 0x00}, {0x4e, 0x00, 0x00, 0x01}
+ },
+ /* 65 */
+ {
+ {0x02, 0x01, 0x77, 0x00}, {0x09, 0x01, 0x77, 0x00},
+ {0x17, 0x01, 0x77, 0x00}, {0x28, 0x01, 0x77, 0x01},
+ {0x02, 0x01, 0x78, 0x00}, {0x09, 0x01, 0x78, 0x00},
+ {0x17, 0x01, 0x78, 0x00}, {0x28, 0x01, 0x78, 0x01},
+ {0x02, 0x01, 0x79, 0x00}, {0x09, 0x01, 0x79, 0x00},
+ {0x17, 0x01, 0x79, 0x00}, {0x28, 0x01, 0x79, 0x01},
+ {0x02, 0x01, 0x7a, 0x00}, {0x09, 0x01, 0x7a, 0x00},
+ {0x17, 0x01, 0x7a, 0x00}, {0x28, 0x01, 0x7a, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x77, 0x00}, {0x06, 0x01, 0x77, 0x00},
+ {0x0a, 0x01, 0x77, 0x00}, {0x0f, 0x01, 0x77, 0x00},
+ {0x18, 0x01, 0x77, 0x00}, {0x1f, 0x01, 0x77, 0x00},
+ {0x29, 0x01, 0x77, 0x00}, {0x38, 0x01, 0x77, 0x01},
+ {0x03, 0x01, 0x78, 0x00}, {0x06, 0x01, 0x78, 0x00},
+ {0x0a, 0x01, 0x78, 0x00}, {0x0f, 0x01, 0x78, 0x00},
+ {0x18, 0x01, 0x78, 0x00}, {0x1f, 0x01, 0x78, 0x00},
+ {0x29, 0x01, 0x78, 0x00}, {0x38, 0x01, 0x78, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x79, 0x00}, {0x06, 0x01, 0x79, 0x00},
+ {0x0a, 0x01, 0x79, 0x00}, {0x0f, 0x01, 0x79, 0x00},
+ {0x18, 0x01, 0x79, 0x00}, {0x1f, 0x01, 0x79, 0x00},
+ {0x29, 0x01, 0x79, 0x00}, {0x38, 0x01, 0x79, 0x01},
+ {0x03, 0x01, 0x7a, 0x00}, {0x06, 0x01, 0x7a, 0x00},
+ {0x0a, 0x01, 0x7a, 0x00}, {0x0f, 0x01, 0x7a, 0x00},
+ {0x18, 0x01, 0x7a, 0x00}, {0x1f, 0x01, 0x7a, 0x00},
+ {0x29, 0x01, 0x7a, 0x00}, {0x38, 0x01, 0x7a, 0x01}
+ },
+ {
+ {0x01, 0x01, 0x26, 0x00}, {0x16, 0x01, 0x26, 0x01},
+ {0x01, 0x01, 0x2a, 0x00}, {0x16, 0x01, 0x2a, 0x01},
+ {0x01, 0x01, 0x2c, 0x00}, {0x16, 0x01, 0x2c, 0x01},
+ {0x01, 0x01, 0x3b, 0x00}, {0x16, 0x01, 0x3b, 0x01},
+ {0x01, 0x01, 0x58, 0x00}, {0x16, 0x01, 0x58, 0x01},
+ {0x01, 0x01, 0x5a, 0x00}, {0x16, 0x01, 0x5a, 0x01},
+ {0x4c, 0x00, 0x00, 0x00}, {0x4d, 0x00, 0x00, 0x00},
+ {0x4f, 0x00, 0x00, 0x00}, {0x51, 0x00, 0x00, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x26, 0x00}, {0x09, 0x01, 0x26, 0x00},
+ {0x17, 0x01, 0x26, 0x00}, {0x28, 0x01, 0x26, 0x01},
+ {0x02, 0x01, 0x2a, 0x00}, {0x09, 0x01, 0x2a, 0x00},
+ {0x17, 0x01, 0x2a, 0x00}, {0x28, 0x01, 0x2a, 0x01},
+ {0x02, 0x01, 0x2c, 0x00}, {0x09, 0x01, 0x2c, 0x00},
+ {0x17, 0x01, 0x2c, 0x00}, {0x28, 0x01, 0x2c, 0x01},
+ {0x02, 0x01, 0x3b, 0x00}, {0x09, 0x01, 0x3b, 0x00},
+ {0x17, 0x01, 0x3b, 0x00}, {0x28, 0x01, 0x3b, 0x01}
+ },
+ /* 70 */
+ {
+ {0x03, 0x01, 0x26, 0x00}, {0x06, 0x01, 0x26, 0x00},
+ {0x0a, 0x01, 0x26, 0x00}, {0x0f, 0x01, 0x26, 0x00},
+ {0x18, 0x01, 0x26, 0x00}, {0x1f, 0x01, 0x26, 0x00},
+ {0x29, 0x01, 0x26, 0x00}, {0x38, 0x01, 0x26, 0x01},
+ {0x03, 0x01, 0x2a, 0x00}, {0x06, 0x01, 0x2a, 0x00},
+ {0x0a, 0x01, 0x2a, 0x00}, {0x0f, 0x01, 0x2a, 0x00},
+ {0x18, 0x01, 0x2a, 0x00}, {0x1f, 0x01, 0x2a, 0x00},
+ {0x29, 0x01, 0x2a, 0x00}, {0x38, 0x01, 0x2a, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x2c, 0x00}, {0x06, 0x01, 0x2c, 0x00},
+ {0x0a, 0x01, 0x2c, 0x00}, {0x0f, 0x01, 0x2c, 0x00},
+ {0x18, 0x01, 0x2c, 0x00}, {0x1f, 0x01, 0x2c, 0x00},
+ {0x29, 0x01, 0x2c, 0x00}, {0x38, 0x01, 0x2c, 0x01},
+ {0x03, 0x01, 0x3b, 0x00}, {0x06, 0x01, 0x3b, 0x00},
+ {0x0a, 0x01, 0x3b, 0x00}, {0x0f, 0x01, 0x3b, 0x00},
+ {0x18, 0x01, 0x3b, 0x00}, {0x1f, 0x01, 0x3b, 0x00},
+ {0x29, 0x01, 0x3b, 0x00}, {0x38, 0x01, 0x3b, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x58, 0x00}, {0x09, 0x01, 0x58, 0x00},
+ {0x17, 0x01, 0x58, 0x00}, {0x28, 0x01, 0x58, 0x01},
+ {0x02, 0x01, 0x5a, 0x00}, {0x09, 0x01, 0x5a, 0x00},
+ {0x17, 0x01, 0x5a, 0x00}, {0x28, 0x01, 0x5a, 0x01},
+ {0x00, 0x01, 0x21, 0x01}, {0x00, 0x01, 0x22, 0x01},
+ {0x00, 0x01, 0x28, 0x01}, {0x00, 0x01, 0x29, 0x01},
+ {0x00, 0x01, 0x3f, 0x01}, {0x50, 0x00, 0x00, 0x00},
+ {0x52, 0x00, 0x00, 0x00}, {0x54, 0x00, 0x00, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x58, 0x00}, {0x06, 0x01, 0x58, 0x00},
+ {0x0a, 0x01, 0x58, 0x00}, {0x0f, 0x01, 0x58, 0x00},
+ {0x18, 0x01, 0x58, 0x00}, {0x1f, 0x01, 0x58, 0x00},
+ {0x29, 0x01, 0x58, 0x00}, {0x38, 0x01, 0x58, 0x01},
+ {0x03, 0x01, 0x5a, 0x00}, {0x06, 0x01, 0x5a, 0x00},
+ {0x0a, 0x01, 0x5a, 0x00}, {0x0f, 0x01, 0x5a, 0x00},
+ {0x18, 0x01, 0x5a, 0x00}, {0x1f, 0x01, 0x5a, 0x00},
+ {0x29, 0x01, 0x5a, 0x00}, {0x38, 0x01, 0x5a, 0x01}
+ },
+ {
+ {0x01, 0x01, 0x21, 0x00}, {0x16, 0x01, 0x21, 0x01},
+ {0x01, 0x01, 0x22, 0x00}, {0x16, 0x01, 0x22, 0x01},
+ {0x01, 0x01, 0x28, 0x00}, {0x16, 0x01, 0x28, 0x01},
+ {0x01, 0x01, 0x29, 0x00}, {0x16, 0x01, 0x29, 0x01},
+ {0x01, 0x01, 0x3f, 0x00}, {0x16, 0x01, 0x3f, 0x01},
+ {0x00, 0x01, 0x27, 0x01}, {0x00, 0x01, 0x2b, 0x01},
+ {0x00, 0x01, 0x7c, 0x01}, {0x53, 0x00, 0x00, 0x00},
+ {0x55, 0x00, 0x00, 0x00}, {0x58, 0x00, 0x00, 0x01}
+ },
+ /* 75 */
+ {
+ {0x02, 0x01, 0x21, 0x00}, {0x09, 0x01, 0x21, 0x00},
+ {0x17, 0x01, 0x21, 0x00}, {0x28, 0x01, 0x21, 0x01},
+ {0x02, 0x01, 0x22, 0x00}, {0x09, 0x01, 0x22, 0x00},
+ {0x17, 0x01, 0x22, 0x00}, {0x28, 0x01, 0x22, 0x01},
+ {0x02, 0x01, 0x28, 0x00}, {0x09, 0x01, 0x28, 0x00},
+ {0x17, 0x01, 0x28, 0x00}, {0x28, 0x01, 0x28, 0x01},
+ {0x02, 0x01, 0x29, 0x00}, {0x09, 0x01, 0x29, 0x00},
+ {0x17, 0x01, 0x29, 0x00}, {0x28, 0x01, 0x29, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x21, 0x00}, {0x06, 0x01, 0x21, 0x00},
+ {0x0a, 0x01, 0x21, 0x00}, {0x0f, 0x01, 0x21, 0x00},
+ {0x18, 0x01, 0x21, 0x00}, {0x1f, 0x01, 0x21, 0x00},
+ {0x29, 0x01, 0x21, 0x00}, {0x38, 0x01, 0x21, 0x01},
+ {0x03, 0x01, 0x22, 0x00}, {0x06, 0x01, 0x22, 0x00},
+ {0x0a, 0x01, 0x22, 0x00}, {0x0f, 0x01, 0x22, 0x00},
+ {0x18, 0x01, 0x22, 0x00}, {0x1f, 0x01, 0x22, 0x00},
+ {0x29, 0x01, 0x22, 0x00}, {0x38, 0x01, 0x22, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x28, 0x00}, {0x06, 0x01, 0x28, 0x00},
+ {0x0a, 0x01, 0x28, 0x00}, {0x0f, 0x01, 0x28, 0x00},
+ {0x18, 0x01, 0x28, 0x00}, {0x1f, 0x01, 0x28, 0x00},
+ {0x29, 0x01, 0x28, 0x00}, {0x38, 0x01, 0x28, 0x01},
+ {0x03, 0x01, 0x29, 0x00}, {0x06, 0x01, 0x29, 0x00},
+ {0x0a, 0x01, 0x29, 0x00}, {0x0f, 0x01, 0x29, 0x00},
+ {0x18, 0x01, 0x29, 0x00}, {0x1f, 0x01, 0x29, 0x00},
+ {0x29, 0x01, 0x29, 0x00}, {0x38, 0x01, 0x29, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x3f, 0x00}, {0x09, 0x01, 0x3f, 0x00},
+ {0x17, 0x01, 0x3f, 0x00}, {0x28, 0x01, 0x3f, 0x01},
+ {0x01, 0x01, 0x27, 0x00}, {0x16, 0x01, 0x27, 0x01},
+ {0x01, 0x01, 0x2b, 0x00}, {0x16, 0x01, 0x2b, 0x01},
+ {0x01, 0x01, 0x7c, 0x00}, {0x16, 0x01, 0x7c, 0x01},
+ {0x00, 0x01, 0x23, 0x01}, {0x00, 0x01, 0x3e, 0x01},
+ {0x56, 0x00, 0x00, 0x00}, {0x57, 0x00, 0x00, 0x00},
+ {0x59, 0x00, 0x00, 0x00}, {0x5a, 0x00, 0x00, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x3f, 0x00}, {0x06, 0x01, 0x3f, 0x00},
+ {0x0a, 0x01, 0x3f, 0x00}, {0x0f, 0x01, 0x3f, 0x00},
+ {0x18, 0x01, 0x3f, 0x00}, {0x1f, 0x01, 0x3f, 0x00},
+ {0x29, 0x01, 0x3f, 0x00}, {0x38, 0x01, 0x3f, 0x01},
+ {0x02, 0x01, 0x27, 0x00}, {0x09, 0x01, 0x27, 0x00},
+ {0x17, 0x01, 0x27, 0x00}, {0x28, 0x01, 0x27, 0x01},
+ {0x02, 0x01, 0x2b, 0x00}, {0x09, 0x01, 0x2b, 0x00},
+ {0x17, 0x01, 0x2b, 0x00}, {0x28, 0x01, 0x2b, 0x01}
+ },
+ /* 80 */
+ {
+ {0x03, 0x01, 0x27, 0x00}, {0x06, 0x01, 0x27, 0x00},
+ {0x0a, 0x01, 0x27, 0x00}, {0x0f, 0x01, 0x27, 0x00},
+ {0x18, 0x01, 0x27, 0x00}, {0x1f, 0x01, 0x27, 0x00},
+ {0x29, 0x01, 0x27, 0x00}, {0x38, 0x01, 0x27, 0x01},
+ {0x03, 0x01, 0x2b, 0x00}, {0x06, 0x01, 0x2b, 0x00},
+ {0x0a, 0x01, 0x2b, 0x00}, {0x0f, 0x01, 0x2b, 0x00},
+ {0x18, 0x01, 0x2b, 0x00}, {0x1f, 0x01, 0x2b, 0x00},
+ {0x29, 0x01, 0x2b, 0x00}, {0x38, 0x01, 0x2b, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x7c, 0x00}, {0x09, 0x01, 0x7c, 0x00},
+ {0x17, 0x01, 0x7c, 0x00}, {0x28, 0x01, 0x7c, 0x01},
+ {0x01, 0x01, 0x23, 0x00}, {0x16, 0x01, 0x23, 0x01},
+ {0x01, 0x01, 0x3e, 0x00}, {0x16, 0x01, 0x3e, 0x01},
+ {0x00, 0x01, 0x00, 0x01}, {0x00, 0x01, 0x24, 0x01},
+ {0x00, 0x01, 0x40, 0x01}, {0x00, 0x01, 0x5b, 0x01},
+ {0x00, 0x01, 0x5d, 0x01}, {0x00, 0x01, 0x7e, 0x01},
+ {0x5b, 0x00, 0x00, 0x00}, {0x5c, 0x00, 0x00, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x7c, 0x00}, {0x06, 0x01, 0x7c, 0x00},
+ {0x0a, 0x01, 0x7c, 0x00}, {0x0f, 0x01, 0x7c, 0x00},
+ {0x18, 0x01, 0x7c, 0x00}, {0x1f, 0x01, 0x7c, 0x00},
+ {0x29, 0x01, 0x7c, 0x00}, {0x38, 0x01, 0x7c, 0x01},
+ {0x02, 0x01, 0x23, 0x00}, {0x09, 0x01, 0x23, 0x00},
+ {0x17, 0x01, 0x23, 0x00}, {0x28, 0x01, 0x23, 0x01},
+ {0x02, 0x01, 0x3e, 0x00}, {0x09, 0x01, 0x3e, 0x00},
+ {0x17, 0x01, 0x3e, 0x00}, {0x28, 0x01, 0x3e, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x23, 0x00}, {0x06, 0x01, 0x23, 0x00},
+ {0x0a, 0x01, 0x23, 0x00}, {0x0f, 0x01, 0x23, 0x00},
+ {0x18, 0x01, 0x23, 0x00}, {0x1f, 0x01, 0x23, 0x00},
+ {0x29, 0x01, 0x23, 0x00}, {0x38, 0x01, 0x23, 0x01},
+ {0x03, 0x01, 0x3e, 0x00}, {0x06, 0x01, 0x3e, 0x00},
+ {0x0a, 0x01, 0x3e, 0x00}, {0x0f, 0x01, 0x3e, 0x00},
+ {0x18, 0x01, 0x3e, 0x00}, {0x1f, 0x01, 0x3e, 0x00},
+ {0x29, 0x01, 0x3e, 0x00}, {0x38, 0x01, 0x3e, 0x01}
+ },
+ {
+ {0x01, 0x01, 0x00, 0x00}, {0x16, 0x01, 0x00, 0x01},
+ {0x01, 0x01, 0x24, 0x00}, {0x16, 0x01, 0x24, 0x01},
+ {0x01, 0x01, 0x40, 0x00}, {0x16, 0x01, 0x40, 0x01},
+ {0x01, 0x01, 0x5b, 0x00}, {0x16, 0x01, 0x5b, 0x01},
+ {0x01, 0x01, 0x5d, 0x00}, {0x16, 0x01, 0x5d, 0x01},
+ {0x01, 0x01, 0x7e, 0x00}, {0x16, 0x01, 0x7e, 0x01},
+ {0x00, 0x01, 0x5e, 0x01}, {0x00, 0x01, 0x7d, 0x01},
+ {0x5d, 0x00, 0x00, 0x00}, {0x5e, 0x00, 0x00, 0x01}
+ },
+ /* 85 */
+ {
+ {0x02, 0x01, 0x00, 0x00}, {0x09, 0x01, 0x00, 0x00},
+ {0x17, 0x01, 0x00, 0x00}, {0x28, 0x01, 0x00, 0x01},
+ {0x02, 0x01, 0x24, 0x00}, {0x09, 0x01, 0x24, 0x00},
+ {0x17, 0x01, 0x24, 0x00}, {0x28, 0x01, 0x24, 0x01},
+ {0x02, 0x01, 0x40, 0x00}, {0x09, 0x01, 0x40, 0x00},
+ {0x17, 0x01, 0x40, 0x00}, {0x28, 0x01, 0x40, 0x01},
+ {0x02, 0x01, 0x5b, 0x00}, {0x09, 0x01, 0x5b, 0x00},
+ {0x17, 0x01, 0x5b, 0x00}, {0x28, 0x01, 0x5b, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x00, 0x00}, {0x06, 0x01, 0x00, 0x00},
+ {0x0a, 0x01, 0x00, 0x00}, {0x0f, 0x01, 0x00, 0x00},
+ {0x18, 0x01, 0x00, 0x00}, {0x1f, 0x01, 0x00, 0x00},
+ {0x29, 0x01, 0x00, 0x00}, {0x38, 0x01, 0x00, 0x01},
+ {0x03, 0x01, 0x24, 0x00}, {0x06, 0x01, 0x24, 0x00},
+ {0x0a, 0x01, 0x24, 0x00}, {0x0f, 0x01, 0x24, 0x00},
+ {0x18, 0x01, 0x24, 0x00}, {0x1f, 0x01, 0x24, 0x00},
+ {0x29, 0x01, 0x24, 0x00}, {0x38, 0x01, 0x24, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x40, 0x00}, {0x06, 0x01, 0x40, 0x00},
+ {0x0a, 0x01, 0x40, 0x00}, {0x0f, 0x01, 0x40, 0x00},
+ {0x18, 0x01, 0x40, 0x00}, {0x1f, 0x01, 0x40, 0x00},
+ {0x29, 0x01, 0x40, 0x00}, {0x38, 0x01, 0x40, 0x01},
+ {0x03, 0x01, 0x5b, 0x00}, {0x06, 0x01, 0x5b, 0x00},
+ {0x0a, 0x01, 0x5b, 0x00}, {0x0f, 0x01, 0x5b, 0x00},
+ {0x18, 0x01, 0x5b, 0x00}, {0x1f, 0x01, 0x5b, 0x00},
+ {0x29, 0x01, 0x5b, 0x00}, {0x38, 0x01, 0x5b, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x5d, 0x00}, {0x09, 0x01, 0x5d, 0x00},
+ {0x17, 0x01, 0x5d, 0x00}, {0x28, 0x01, 0x5d, 0x01},
+ {0x02, 0x01, 0x7e, 0x00}, {0x09, 0x01, 0x7e, 0x00},
+ {0x17, 0x01, 0x7e, 0x00}, {0x28, 0x01, 0x7e, 0x01},
+ {0x01, 0x01, 0x5e, 0x00}, {0x16, 0x01, 0x5e, 0x01},
+ {0x01, 0x01, 0x7d, 0x00}, {0x16, 0x01, 0x7d, 0x01},
+ {0x00, 0x01, 0x3c, 0x01}, {0x00, 0x01, 0x60, 0x01},
+ {0x00, 0x01, 0x7b, 0x01}, {0x5f, 0x00, 0x00, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x5d, 0x00}, {0x06, 0x01, 0x5d, 0x00},
+ {0x0a, 0x01, 0x5d, 0x00}, {0x0f, 0x01, 0x5d, 0x00},
+ {0x18, 0x01, 0x5d, 0x00}, {0x1f, 0x01, 0x5d, 0x00},
+ {0x29, 0x01, 0x5d, 0x00}, {0x38, 0x01, 0x5d, 0x01},
+ {0x03, 0x01, 0x7e, 0x00}, {0x06, 0x01, 0x7e, 0x00},
+ {0x0a, 0x01, 0x7e, 0x00}, {0x0f, 0x01, 0x7e, 0x00},
+ {0x18, 0x01, 0x7e, 0x00}, {0x1f, 0x01, 0x7e, 0x00},
+ {0x29, 0x01, 0x7e, 0x00}, {0x38, 0x01, 0x7e, 0x01}
+ },
+ /* 90 */
+ {
+ {0x02, 0x01, 0x5e, 0x00}, {0x09, 0x01, 0x5e, 0x00},
+ {0x17, 0x01, 0x5e, 0x00}, {0x28, 0x01, 0x5e, 0x01},
+ {0x02, 0x01, 0x7d, 0x00}, {0x09, 0x01, 0x7d, 0x00},
+ {0x17, 0x01, 0x7d, 0x00}, {0x28, 0x01, 0x7d, 0x01},
+ {0x01, 0x01, 0x3c, 0x00}, {0x16, 0x01, 0x3c, 0x01},
+ {0x01, 0x01, 0x60, 0x00}, {0x16, 0x01, 0x60, 0x01},
+ {0x01, 0x01, 0x7b, 0x00}, {0x16, 0x01, 0x7b, 0x01},
+ {0x60, 0x00, 0x00, 0x00}, {0x6e, 0x00, 0x00, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x5e, 0x00}, {0x06, 0x01, 0x5e, 0x00},
+ {0x0a, 0x01, 0x5e, 0x00}, {0x0f, 0x01, 0x5e, 0x00},
+ {0x18, 0x01, 0x5e, 0x00}, {0x1f, 0x01, 0x5e, 0x00},
+ {0x29, 0x01, 0x5e, 0x00}, {0x38, 0x01, 0x5e, 0x01},
+ {0x03, 0x01, 0x7d, 0x00}, {0x06, 0x01, 0x7d, 0x00},
+ {0x0a, 0x01, 0x7d, 0x00}, {0x0f, 0x01, 0x7d, 0x00},
+ {0x18, 0x01, 0x7d, 0x00}, {0x1f, 0x01, 0x7d, 0x00},
+ {0x29, 0x01, 0x7d, 0x00}, {0x38, 0x01, 0x7d, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x3c, 0x00}, {0x09, 0x01, 0x3c, 0x00},
+ {0x17, 0x01, 0x3c, 0x00}, {0x28, 0x01, 0x3c, 0x01},
+ {0x02, 0x01, 0x60, 0x00}, {0x09, 0x01, 0x60, 0x00},
+ {0x17, 0x01, 0x60, 0x00}, {0x28, 0x01, 0x60, 0x01},
+ {0x02, 0x01, 0x7b, 0x00}, {0x09, 0x01, 0x7b, 0x00},
+ {0x17, 0x01, 0x7b, 0x00}, {0x28, 0x01, 0x7b, 0x01},
+ {0x61, 0x00, 0x00, 0x00}, {0x65, 0x00, 0x00, 0x00},
+ {0x6f, 0x00, 0x00, 0x00}, {0x85, 0x00, 0x00, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x3c, 0x00}, {0x06, 0x01, 0x3c, 0x00},
+ {0x0a, 0x01, 0x3c, 0x00}, {0x0f, 0x01, 0x3c, 0x00},
+ {0x18, 0x01, 0x3c, 0x00}, {0x1f, 0x01, 0x3c, 0x00},
+ {0x29, 0x01, 0x3c, 0x00}, {0x38, 0x01, 0x3c, 0x01},
+ {0x03, 0x01, 0x60, 0x00}, {0x06, 0x01, 0x60, 0x00},
+ {0x0a, 0x01, 0x60, 0x00}, {0x0f, 0x01, 0x60, 0x00},
+ {0x18, 0x01, 0x60, 0x00}, {0x1f, 0x01, 0x60, 0x00},
+ {0x29, 0x01, 0x60, 0x00}, {0x38, 0x01, 0x60, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x7b, 0x00}, {0x06, 0x01, 0x7b, 0x00},
+ {0x0a, 0x01, 0x7b, 0x00}, {0x0f, 0x01, 0x7b, 0x00},
+ {0x18, 0x01, 0x7b, 0x00}, {0x1f, 0x01, 0x7b, 0x00},
+ {0x29, 0x01, 0x7b, 0x00}, {0x38, 0x01, 0x7b, 0x01},
+ {0x62, 0x00, 0x00, 0x00}, {0x63, 0x00, 0x00, 0x00},
+ {0x66, 0x00, 0x00, 0x00}, {0x69, 0x00, 0x00, 0x00},
+ {0x70, 0x00, 0x00, 0x00}, {0x77, 0x00, 0x00, 0x00},
+ {0x86, 0x00, 0x00, 0x00}, {0x99, 0x00, 0x00, 0x01}
+ },
+ /* 95 */
+ {
+ {0x00, 0x01, 0x5c, 0x01}, {0x00, 0x01, 0xc3, 0x01},
+ {0x00, 0x01, 0xd0, 0x01}, {0x64, 0x00, 0x00, 0x00},
+ {0x67, 0x00, 0x00, 0x00}, {0x68, 0x00, 0x00, 0x00},
+ {0x6a, 0x00, 0x00, 0x00}, {0x6b, 0x00, 0x00, 0x00},
+ {0x71, 0x00, 0x00, 0x00}, {0x74, 0x00, 0x00, 0x00},
+ {0x78, 0x00, 0x00, 0x00}, {0x7e, 0x00, 0x00, 0x00},
+ {0x87, 0x00, 0x00, 0x00}, {0x8e, 0x00, 0x00, 0x00},
+ {0x9a, 0x00, 0x00, 0x00}, {0xa9, 0x00, 0x00, 0x01}
+ },
+ {
+ {0x01, 0x01, 0x5c, 0x00}, {0x16, 0x01, 0x5c, 0x01},
+ {0x01, 0x01, 0xc3, 0x00}, {0x16, 0x01, 0xc3, 0x01},
+ {0x01, 0x01, 0xd0, 0x00}, {0x16, 0x01, 0xd0, 0x01},
+ {0x00, 0x01, 0x80, 0x01}, {0x00, 0x01, 0x82, 0x01},
+ {0x00, 0x01, 0x83, 0x01}, {0x00, 0x01, 0xa2, 0x01},
+ {0x00, 0x01, 0xb8, 0x01}, {0x00, 0x01, 0xc2, 0x01},
+ {0x00, 0x01, 0xe0, 0x01}, {0x00, 0x01, 0xe2, 0x01},
+ {0x6c, 0x00, 0x00, 0x00}, {0x6d, 0x00, 0x00, 0x00}
+ },
+ {
+ {0x02, 0x01, 0x5c, 0x00}, {0x09, 0x01, 0x5c, 0x00},
+ {0x17, 0x01, 0x5c, 0x00}, {0x28, 0x01, 0x5c, 0x01},
+ {0x02, 0x01, 0xc3, 0x00}, {0x09, 0x01, 0xc3, 0x00},
+ {0x17, 0x01, 0xc3, 0x00}, {0x28, 0x01, 0xc3, 0x01},
+ {0x02, 0x01, 0xd0, 0x00}, {0x09, 0x01, 0xd0, 0x00},
+ {0x17, 0x01, 0xd0, 0x00}, {0x28, 0x01, 0xd0, 0x01},
+ {0x01, 0x01, 0x80, 0x00}, {0x16, 0x01, 0x80, 0x01},
+ {0x01, 0x01, 0x82, 0x00}, {0x16, 0x01, 0x82, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x5c, 0x00}, {0x06, 0x01, 0x5c, 0x00},
+ {0x0a, 0x01, 0x5c, 0x00}, {0x0f, 0x01, 0x5c, 0x00},
+ {0x18, 0x01, 0x5c, 0x00}, {0x1f, 0x01, 0x5c, 0x00},
+ {0x29, 0x01, 0x5c, 0x00}, {0x38, 0x01, 0x5c, 0x01},
+ {0x03, 0x01, 0xc3, 0x00}, {0x06, 0x01, 0xc3, 0x00},
+ {0x0a, 0x01, 0xc3, 0x00}, {0x0f, 0x01, 0xc3, 0x00},
+ {0x18, 0x01, 0xc3, 0x00}, {0x1f, 0x01, 0xc3, 0x00},
+ {0x29, 0x01, 0xc3, 0x00}, {0x38, 0x01, 0xc3, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xd0, 0x00}, {0x06, 0x01, 0xd0, 0x00},
+ {0x0a, 0x01, 0xd0, 0x00}, {0x0f, 0x01, 0xd0, 0x00},
+ {0x18, 0x01, 0xd0, 0x00}, {0x1f, 0x01, 0xd0, 0x00},
+ {0x29, 0x01, 0xd0, 0x00}, {0x38, 0x01, 0xd0, 0x01},
+ {0x02, 0x01, 0x80, 0x00}, {0x09, 0x01, 0x80, 0x00},
+ {0x17, 0x01, 0x80, 0x00}, {0x28, 0x01, 0x80, 0x01},
+ {0x02, 0x01, 0x82, 0x00}, {0x09, 0x01, 0x82, 0x00},
+ {0x17, 0x01, 0x82, 0x00}, {0x28, 0x01, 0x82, 0x01}
+ },
+ /* 100 */
+ {
+ {0x03, 0x01, 0x80, 0x00}, {0x06, 0x01, 0x80, 0x00},
+ {0x0a, 0x01, 0x80, 0x00}, {0x0f, 0x01, 0x80, 0x00},
+ {0x18, 0x01, 0x80, 0x00}, {0x1f, 0x01, 0x80, 0x00},
+ {0x29, 0x01, 0x80, 0x00}, {0x38, 0x01, 0x80, 0x01},
+ {0x03, 0x01, 0x82, 0x00}, {0x06, 0x01, 0x82, 0x00},
+ {0x0a, 0x01, 0x82, 0x00}, {0x0f, 0x01, 0x82, 0x00},
+ {0x18, 0x01, 0x82, 0x00}, {0x1f, 0x01, 0x82, 0x00},
+ {0x29, 0x01, 0x82, 0x00}, {0x38, 0x01, 0x82, 0x01}
+ },
+ {
+ {0x01, 0x01, 0x83, 0x00}, {0x16, 0x01, 0x83, 0x01},
+ {0x01, 0x01, 0xa2, 0x00}, {0x16, 0x01, 0xa2, 0x01},
+ {0x01, 0x01, 0xb8, 0x00}, {0x16, 0x01, 0xb8, 0x01},
+ {0x01, 0x01, 0xc2, 0x00}, {0x16, 0x01, 0xc2, 0x01},
+ {0x01, 0x01, 0xe0, 0x00}, {0x16, 0x01, 0xe0, 0x01},
+ {0x01, 0x01, 0xe2, 0x00}, {0x16, 0x01, 0xe2, 0x01},
+ {0x00, 0x01, 0x99, 0x01}, {0x00, 0x01, 0xa1, 0x01},
+ {0x00, 0x01, 0xa7, 0x01}, {0x00, 0x01, 0xac, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x83, 0x00}, {0x09, 0x01, 0x83, 0x00},
+ {0x17, 0x01, 0x83, 0x00}, {0x28, 0x01, 0x83, 0x01},
+ {0x02, 0x01, 0xa2, 0x00}, {0x09, 0x01, 0xa2, 0x00},
+ {0x17, 0x01, 0xa2, 0x00}, {0x28, 0x01, 0xa2, 0x01},
+ {0x02, 0x01, 0xb8, 0x00}, {0x09, 0x01, 0xb8, 0x00},
+ {0x17, 0x01, 0xb8, 0x00}, {0x28, 0x01, 0xb8, 0x01},
+ {0x02, 0x01, 0xc2, 0x00}, {0x09, 0x01, 0xc2, 0x00},
+ {0x17, 0x01, 0xc2, 0x00}, {0x28, 0x01, 0xc2, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x83, 0x00}, {0x06, 0x01, 0x83, 0x00},
+ {0x0a, 0x01, 0x83, 0x00}, {0x0f, 0x01, 0x83, 0x00},
+ {0x18, 0x01, 0x83, 0x00}, {0x1f, 0x01, 0x83, 0x00},
+ {0x29, 0x01, 0x83, 0x00}, {0x38, 0x01, 0x83, 0x01},
+ {0x03, 0x01, 0xa2, 0x00}, {0x06, 0x01, 0xa2, 0x00},
+ {0x0a, 0x01, 0xa2, 0x00}, {0x0f, 0x01, 0xa2, 0x00},
+ {0x18, 0x01, 0xa2, 0x00}, {0x1f, 0x01, 0xa2, 0x00},
+ {0x29, 0x01, 0xa2, 0x00}, {0x38, 0x01, 0xa2, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xb8, 0x00}, {0x06, 0x01, 0xb8, 0x00},
+ {0x0a, 0x01, 0xb8, 0x00}, {0x0f, 0x01, 0xb8, 0x00},
+ {0x18, 0x01, 0xb8, 0x00}, {0x1f, 0x01, 0xb8, 0x00},
+ {0x29, 0x01, 0xb8, 0x00}, {0x38, 0x01, 0xb8, 0x01},
+ {0x03, 0x01, 0xc2, 0x00}, {0x06, 0x01, 0xc2, 0x00},
+ {0x0a, 0x01, 0xc2, 0x00}, {0x0f, 0x01, 0xc2, 0x00},
+ {0x18, 0x01, 0xc2, 0x00}, {0x1f, 0x01, 0xc2, 0x00},
+ {0x29, 0x01, 0xc2, 0x00}, {0x38, 0x01, 0xc2, 0x01}
+ },
+ /* 105 */
+ {
+ {0x02, 0x01, 0xe0, 0x00}, {0x09, 0x01, 0xe0, 0x00},
+ {0x17, 0x01, 0xe0, 0x00}, {0x28, 0x01, 0xe0, 0x01},
+ {0x02, 0x01, 0xe2, 0x00}, {0x09, 0x01, 0xe2, 0x00},
+ {0x17, 0x01, 0xe2, 0x00}, {0x28, 0x01, 0xe2, 0x01},
+ {0x01, 0x01, 0x99, 0x00}, {0x16, 0x01, 0x99, 0x01},
+ {0x01, 0x01, 0xa1, 0x00}, {0x16, 0x01, 0xa1, 0x01},
+ {0x01, 0x01, 0xa7, 0x00}, {0x16, 0x01, 0xa7, 0x01},
+ {0x01, 0x01, 0xac, 0x00}, {0x16, 0x01, 0xac, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xe0, 0x00}, {0x06, 0x01, 0xe0, 0x00},
+ {0x0a, 0x01, 0xe0, 0x00}, {0x0f, 0x01, 0xe0, 0x00},
+ {0x18, 0x01, 0xe0, 0x00}, {0x1f, 0x01, 0xe0, 0x00},
+ {0x29, 0x01, 0xe0, 0x00}, {0x38, 0x01, 0xe0, 0x01},
+ {0x03, 0x01, 0xe2, 0x00}, {0x06, 0x01, 0xe2, 0x00},
+ {0x0a, 0x01, 0xe2, 0x00}, {0x0f, 0x01, 0xe2, 0x00},
+ {0x18, 0x01, 0xe2, 0x00}, {0x1f, 0x01, 0xe2, 0x00},
+ {0x29, 0x01, 0xe2, 0x00}, {0x38, 0x01, 0xe2, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x99, 0x00}, {0x09, 0x01, 0x99, 0x00},
+ {0x17, 0x01, 0x99, 0x00}, {0x28, 0x01, 0x99, 0x01},
+ {0x02, 0x01, 0xa1, 0x00}, {0x09, 0x01, 0xa1, 0x00},
+ {0x17, 0x01, 0xa1, 0x00}, {0x28, 0x01, 0xa1, 0x01},
+ {0x02, 0x01, 0xa7, 0x00}, {0x09, 0x01, 0xa7, 0x00},
+ {0x17, 0x01, 0xa7, 0x00}, {0x28, 0x01, 0xa7, 0x01},
+ {0x02, 0x01, 0xac, 0x00}, {0x09, 0x01, 0xac, 0x00},
+ {0x17, 0x01, 0xac, 0x00}, {0x28, 0x01, 0xac, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x99, 0x00}, {0x06, 0x01, 0x99, 0x00},
+ {0x0a, 0x01, 0x99, 0x00}, {0x0f, 0x01, 0x99, 0x00},
+ {0x18, 0x01, 0x99, 0x00}, {0x1f, 0x01, 0x99, 0x00},
+ {0x29, 0x01, 0x99, 0x00}, {0x38, 0x01, 0x99, 0x01},
+ {0x03, 0x01, 0xa1, 0x00}, {0x06, 0x01, 0xa1, 0x00},
+ {0x0a, 0x01, 0xa1, 0x00}, {0x0f, 0x01, 0xa1, 0x00},
+ {0x18, 0x01, 0xa1, 0x00}, {0x1f, 0x01, 0xa1, 0x00},
+ {0x29, 0x01, 0xa1, 0x00}, {0x38, 0x01, 0xa1, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xa7, 0x00}, {0x06, 0x01, 0xa7, 0x00},
+ {0x0a, 0x01, 0xa7, 0x00}, {0x0f, 0x01, 0xa7, 0x00},
+ {0x18, 0x01, 0xa7, 0x00}, {0x1f, 0x01, 0xa7, 0x00},
+ {0x29, 0x01, 0xa7, 0x00}, {0x38, 0x01, 0xa7, 0x01},
+ {0x03, 0x01, 0xac, 0x00}, {0x06, 0x01, 0xac, 0x00},
+ {0x0a, 0x01, 0xac, 0x00}, {0x0f, 0x01, 0xac, 0x00},
+ {0x18, 0x01, 0xac, 0x00}, {0x1f, 0x01, 0xac, 0x00},
+ {0x29, 0x01, 0xac, 0x00}, {0x38, 0x01, 0xac, 0x01}
+ },
+ /* 110 */
+ {
+ {0x72, 0x00, 0x00, 0x00}, {0x73, 0x00, 0x00, 0x00},
+ {0x75, 0x00, 0x00, 0x00}, {0x76, 0x00, 0x00, 0x00},
+ {0x79, 0x00, 0x00, 0x00}, {0x7b, 0x00, 0x00, 0x00},
+ {0x7f, 0x00, 0x00, 0x00}, {0x82, 0x00, 0x00, 0x00},
+ {0x88, 0x00, 0x00, 0x00}, {0x8b, 0x00, 0x00, 0x00},
+ {0x8f, 0x00, 0x00, 0x00}, {0x92, 0x00, 0x00, 0x00},
+ {0x9b, 0x00, 0x00, 0x00}, {0xa2, 0x00, 0x00, 0x00},
+ {0xaa, 0x00, 0x00, 0x00}, {0xb4, 0x00, 0x00, 0x01}
+ },
+ {
+ {0x00, 0x01, 0xb0, 0x01}, {0x00, 0x01, 0xb1, 0x01},
+ {0x00, 0x01, 0xb3, 0x01}, {0x00, 0x01, 0xd1, 0x01},
+ {0x00, 0x01, 0xd8, 0x01}, {0x00, 0x01, 0xd9, 0x01},
+ {0x00, 0x01, 0xe3, 0x01}, {0x00, 0x01, 0xe5, 0x01},
+ {0x00, 0x01, 0xe6, 0x01}, {0x7a, 0x00, 0x00, 0x00},
+ {0x7c, 0x00, 0x00, 0x00}, {0x7d, 0x00, 0x00, 0x00},
+ {0x80, 0x00, 0x00, 0x00}, {0x81, 0x00, 0x00, 0x00},
+ {0x83, 0x00, 0x00, 0x00}, {0x84, 0x00, 0x00, 0x00}
+ },
+ {
+ {0x01, 0x01, 0xb0, 0x00}, {0x16, 0x01, 0xb0, 0x01},
+ {0x01, 0x01, 0xb1, 0x00}, {0x16, 0x01, 0xb1, 0x01},
+ {0x01, 0x01, 0xb3, 0x00}, {0x16, 0x01, 0xb3, 0x01},
+ {0x01, 0x01, 0xd1, 0x00}, {0x16, 0x01, 0xd1, 0x01},
+ {0x01, 0x01, 0xd8, 0x00}, {0x16, 0x01, 0xd8, 0x01},
+ {0x01, 0x01, 0xd9, 0x00}, {0x16, 0x01, 0xd9, 0x01},
+ {0x01, 0x01, 0xe3, 0x00}, {0x16, 0x01, 0xe3, 0x01},
+ {0x01, 0x01, 0xe5, 0x00}, {0x16, 0x01, 0xe5, 0x01}
+ },
+ {
+ {0x02, 0x01, 0xb0, 0x00}, {0x09, 0x01, 0xb0, 0x00},
+ {0x17, 0x01, 0xb0, 0x00}, {0x28, 0x01, 0xb0, 0x01},
+ {0x02, 0x01, 0xb1, 0x00}, {0x09, 0x01, 0xb1, 0x00},
+ {0x17, 0x01, 0xb1, 0x00}, {0x28, 0x01, 0xb1, 0x01},
+ {0x02, 0x01, 0xb3, 0x00}, {0x09, 0x01, 0xb3, 0x00},
+ {0x17, 0x01, 0xb3, 0x00}, {0x28, 0x01, 0xb3, 0x01},
+ {0x02, 0x01, 0xd1, 0x00}, {0x09, 0x01, 0xd1, 0x00},
+ {0x17, 0x01, 0xd1, 0x00}, {0x28, 0x01, 0xd1, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xb0, 0x00}, {0x06, 0x01, 0xb0, 0x00},
+ {0x0a, 0x01, 0xb0, 0x00}, {0x0f, 0x01, 0xb0, 0x00},
+ {0x18, 0x01, 0xb0, 0x00}, {0x1f, 0x01, 0xb0, 0x00},
+ {0x29, 0x01, 0xb0, 0x00}, {0x38, 0x01, 0xb0, 0x01},
+ {0x03, 0x01, 0xb1, 0x00}, {0x06, 0x01, 0xb1, 0x00},
+ {0x0a, 0x01, 0xb1, 0x00}, {0x0f, 0x01, 0xb1, 0x00},
+ {0x18, 0x01, 0xb1, 0x00}, {0x1f, 0x01, 0xb1, 0x00},
+ {0x29, 0x01, 0xb1, 0x00}, {0x38, 0x01, 0xb1, 0x01}
+ },
+ /* 115 */
+ {
+ {0x03, 0x01, 0xb3, 0x00}, {0x06, 0x01, 0xb3, 0x00},
+ {0x0a, 0x01, 0xb3, 0x00}, {0x0f, 0x01, 0xb3, 0x00},
+ {0x18, 0x01, 0xb3, 0x00}, {0x1f, 0x01, 0xb3, 0x00},
+ {0x29, 0x01, 0xb3, 0x00}, {0x38, 0x01, 0xb3, 0x01},
+ {0x03, 0x01, 0xd1, 0x00}, {0x06, 0x01, 0xd1, 0x00},
+ {0x0a, 0x01, 0xd1, 0x00}, {0x0f, 0x01, 0xd1, 0x00},
+ {0x18, 0x01, 0xd1, 0x00}, {0x1f, 0x01, 0xd1, 0x00},
+ {0x29, 0x01, 0xd1, 0x00}, {0x38, 0x01, 0xd1, 0x01}
+ },
+ {
+ {0x02, 0x01, 0xd8, 0x00}, {0x09, 0x01, 0xd8, 0x00},
+ {0x17, 0x01, 0xd8, 0x00}, {0x28, 0x01, 0xd8, 0x01},
+ {0x02, 0x01, 0xd9, 0x00}, {0x09, 0x01, 0xd9, 0x00},
+ {0x17, 0x01, 0xd9, 0x00}, {0x28, 0x01, 0xd9, 0x01},
+ {0x02, 0x01, 0xe3, 0x00}, {0x09, 0x01, 0xe3, 0x00},
+ {0x17, 0x01, 0xe3, 0x00}, {0x28, 0x01, 0xe3, 0x01},
+ {0x02, 0x01, 0xe5, 0x00}, {0x09, 0x01, 0xe5, 0x00},
+ {0x17, 0x01, 0xe5, 0x00}, {0x28, 0x01, 0xe5, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xd8, 0x00}, {0x06, 0x01, 0xd8, 0x00},
+ {0x0a, 0x01, 0xd8, 0x00}, {0x0f, 0x01, 0xd8, 0x00},
+ {0x18, 0x01, 0xd8, 0x00}, {0x1f, 0x01, 0xd8, 0x00},
+ {0x29, 0x01, 0xd8, 0x00}, {0x38, 0x01, 0xd8, 0x01},
+ {0x03, 0x01, 0xd9, 0x00}, {0x06, 0x01, 0xd9, 0x00},
+ {0x0a, 0x01, 0xd9, 0x00}, {0x0f, 0x01, 0xd9, 0x00},
+ {0x18, 0x01, 0xd9, 0x00}, {0x1f, 0x01, 0xd9, 0x00},
+ {0x29, 0x01, 0xd9, 0x00}, {0x38, 0x01, 0xd9, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xe3, 0x00}, {0x06, 0x01, 0xe3, 0x00},
+ {0x0a, 0x01, 0xe3, 0x00}, {0x0f, 0x01, 0xe3, 0x00},
+ {0x18, 0x01, 0xe3, 0x00}, {0x1f, 0x01, 0xe3, 0x00},
+ {0x29, 0x01, 0xe3, 0x00}, {0x38, 0x01, 0xe3, 0x01},
+ {0x03, 0x01, 0xe5, 0x00}, {0x06, 0x01, 0xe5, 0x00},
+ {0x0a, 0x01, 0xe5, 0x00}, {0x0f, 0x01, 0xe5, 0x00},
+ {0x18, 0x01, 0xe5, 0x00}, {0x1f, 0x01, 0xe5, 0x00},
+ {0x29, 0x01, 0xe5, 0x00}, {0x38, 0x01, 0xe5, 0x01}
+ },
+ {
+ {0x01, 0x01, 0xe6, 0x00}, {0x16, 0x01, 0xe6, 0x01},
+ {0x00, 0x01, 0x81, 0x01}, {0x00, 0x01, 0x84, 0x01},
+ {0x00, 0x01, 0x85, 0x01}, {0x00, 0x01, 0x86, 0x01},
+ {0x00, 0x01, 0x88, 0x01}, {0x00, 0x01, 0x92, 0x01},
+ {0x00, 0x01, 0x9a, 0x01}, {0x00, 0x01, 0x9c, 0x01},
+ {0x00, 0x01, 0xa0, 0x01}, {0x00, 0x01, 0xa3, 0x01},
+ {0x00, 0x01, 0xa4, 0x01}, {0x00, 0x01, 0xa9, 0x01},
+ {0x00, 0x01, 0xaa, 0x01}, {0x00, 0x01, 0xad, 0x01}
+ },
+ /* 120 */
+ {
+ {0x02, 0x01, 0xe6, 0x00}, {0x09, 0x01, 0xe6, 0x00},
+ {0x17, 0x01, 0xe6, 0x00}, {0x28, 0x01, 0xe6, 0x01},
+ {0x01, 0x01, 0x81, 0x00}, {0x16, 0x01, 0x81, 0x01},
+ {0x01, 0x01, 0x84, 0x00}, {0x16, 0x01, 0x84, 0x01},
+ {0x01, 0x01, 0x85, 0x00}, {0x16, 0x01, 0x85, 0x01},
+ {0x01, 0x01, 0x86, 0x00}, {0x16, 0x01, 0x86, 0x01},
+ {0x01, 0x01, 0x88, 0x00}, {0x16, 0x01, 0x88, 0x01},
+ {0x01, 0x01, 0x92, 0x00}, {0x16, 0x01, 0x92, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xe6, 0x00}, {0x06, 0x01, 0xe6, 0x00},
+ {0x0a, 0x01, 0xe6, 0x00}, {0x0f, 0x01, 0xe6, 0x00},
+ {0x18, 0x01, 0xe6, 0x00}, {0x1f, 0x01, 0xe6, 0x00},
+ {0x29, 0x01, 0xe6, 0x00}, {0x38, 0x01, 0xe6, 0x01},
+ {0x02, 0x01, 0x81, 0x00}, {0x09, 0x01, 0x81, 0x00},
+ {0x17, 0x01, 0x81, 0x00}, {0x28, 0x01, 0x81, 0x01},
+ {0x02, 0x01, 0x84, 0x00}, {0x09, 0x01, 0x84, 0x00},
+ {0x17, 0x01, 0x84, 0x00}, {0x28, 0x01, 0x84, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x81, 0x00}, {0x06, 0x01, 0x81, 0x00},
+ {0x0a, 0x01, 0x81, 0x00}, {0x0f, 0x01, 0x81, 0x00},
+ {0x18, 0x01, 0x81, 0x00}, {0x1f, 0x01, 0x81, 0x00},
+ {0x29, 0x01, 0x81, 0x00}, {0x38, 0x01, 0x81, 0x01},
+ {0x03, 0x01, 0x84, 0x00}, {0x06, 0x01, 0x84, 0x00},
+ {0x0a, 0x01, 0x84, 0x00}, {0x0f, 0x01, 0x84, 0x00},
+ {0x18, 0x01, 0x84, 0x00}, {0x1f, 0x01, 0x84, 0x00},
+ {0x29, 0x01, 0x84, 0x00}, {0x38, 0x01, 0x84, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x85, 0x00}, {0x09, 0x01, 0x85, 0x00},
+ {0x17, 0x01, 0x85, 0x00}, {0x28, 0x01, 0x85, 0x01},
+ {0x02, 0x01, 0x86, 0x00}, {0x09, 0x01, 0x86, 0x00},
+ {0x17, 0x01, 0x86, 0x00}, {0x28, 0x01, 0x86, 0x01},
+ {0x02, 0x01, 0x88, 0x00}, {0x09, 0x01, 0x88, 0x00},
+ {0x17, 0x01, 0x88, 0x00}, {0x28, 0x01, 0x88, 0x01},
+ {0x02, 0x01, 0x92, 0x00}, {0x09, 0x01, 0x92, 0x00},
+ {0x17, 0x01, 0x92, 0x00}, {0x28, 0x01, 0x92, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x85, 0x00}, {0x06, 0x01, 0x85, 0x00},
+ {0x0a, 0x01, 0x85, 0x00}, {0x0f, 0x01, 0x85, 0x00},
+ {0x18, 0x01, 0x85, 0x00}, {0x1f, 0x01, 0x85, 0x00},
+ {0x29, 0x01, 0x85, 0x00}, {0x38, 0x01, 0x85, 0x01},
+ {0x03, 0x01, 0x86, 0x00}, {0x06, 0x01, 0x86, 0x00},
+ {0x0a, 0x01, 0x86, 0x00}, {0x0f, 0x01, 0x86, 0x00},
+ {0x18, 0x01, 0x86, 0x00}, {0x1f, 0x01, 0x86, 0x00},
+ {0x29, 0x01, 0x86, 0x00}, {0x38, 0x01, 0x86, 0x01}
+ },
+ /* 125 */
+ {
+ {0x03, 0x01, 0x88, 0x00}, {0x06, 0x01, 0x88, 0x00},
+ {0x0a, 0x01, 0x88, 0x00}, {0x0f, 0x01, 0x88, 0x00},
+ {0x18, 0x01, 0x88, 0x00}, {0x1f, 0x01, 0x88, 0x00},
+ {0x29, 0x01, 0x88, 0x00}, {0x38, 0x01, 0x88, 0x01},
+ {0x03, 0x01, 0x92, 0x00}, {0x06, 0x01, 0x92, 0x00},
+ {0x0a, 0x01, 0x92, 0x00}, {0x0f, 0x01, 0x92, 0x00},
+ {0x18, 0x01, 0x92, 0x00}, {0x1f, 0x01, 0x92, 0x00},
+ {0x29, 0x01, 0x92, 0x00}, {0x38, 0x01, 0x92, 0x01}
+ },
+ {
+ {0x01, 0x01, 0x9a, 0x00}, {0x16, 0x01, 0x9a, 0x01},
+ {0x01, 0x01, 0x9c, 0x00}, {0x16, 0x01, 0x9c, 0x01},
+ {0x01, 0x01, 0xa0, 0x00}, {0x16, 0x01, 0xa0, 0x01},
+ {0x01, 0x01, 0xa3, 0x00}, {0x16, 0x01, 0xa3, 0x01},
+ {0x01, 0x01, 0xa4, 0x00}, {0x16, 0x01, 0xa4, 0x01},
+ {0x01, 0x01, 0xa9, 0x00}, {0x16, 0x01, 0xa9, 0x01},
+ {0x01, 0x01, 0xaa, 0x00}, {0x16, 0x01, 0xaa, 0x01},
+ {0x01, 0x01, 0xad, 0x00}, {0x16, 0x01, 0xad, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x9a, 0x00}, {0x09, 0x01, 0x9a, 0x00},
+ {0x17, 0x01, 0x9a, 0x00}, {0x28, 0x01, 0x9a, 0x01},
+ {0x02, 0x01, 0x9c, 0x00}, {0x09, 0x01, 0x9c, 0x00},
+ {0x17, 0x01, 0x9c, 0x00}, {0x28, 0x01, 0x9c, 0x01},
+ {0x02, 0x01, 0xa0, 0x00}, {0x09, 0x01, 0xa0, 0x00},
+ {0x17, 0x01, 0xa0, 0x00}, {0x28, 0x01, 0xa0, 0x01},
+ {0x02, 0x01, 0xa3, 0x00}, {0x09, 0x01, 0xa3, 0x00},
+ {0x17, 0x01, 0xa3, 0x00}, {0x28, 0x01, 0xa3, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x9a, 0x00}, {0x06, 0x01, 0x9a, 0x00},
+ {0x0a, 0x01, 0x9a, 0x00}, {0x0f, 0x01, 0x9a, 0x00},
+ {0x18, 0x01, 0x9a, 0x00}, {0x1f, 0x01, 0x9a, 0x00},
+ {0x29, 0x01, 0x9a, 0x00}, {0x38, 0x01, 0x9a, 0x01},
+ {0x03, 0x01, 0x9c, 0x00}, {0x06, 0x01, 0x9c, 0x00},
+ {0x0a, 0x01, 0x9c, 0x00}, {0x0f, 0x01, 0x9c, 0x00},
+ {0x18, 0x01, 0x9c, 0x00}, {0x1f, 0x01, 0x9c, 0x00},
+ {0x29, 0x01, 0x9c, 0x00}, {0x38, 0x01, 0x9c, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xa0, 0x00}, {0x06, 0x01, 0xa0, 0x00},
+ {0x0a, 0x01, 0xa0, 0x00}, {0x0f, 0x01, 0xa0, 0x00},
+ {0x18, 0x01, 0xa0, 0x00}, {0x1f, 0x01, 0xa0, 0x00},
+ {0x29, 0x01, 0xa0, 0x00}, {0x38, 0x01, 0xa0, 0x01},
+ {0x03, 0x01, 0xa3, 0x00}, {0x06, 0x01, 0xa3, 0x00},
+ {0x0a, 0x01, 0xa3, 0x00}, {0x0f, 0x01, 0xa3, 0x00},
+ {0x18, 0x01, 0xa3, 0x00}, {0x1f, 0x01, 0xa3, 0x00},
+ {0x29, 0x01, 0xa3, 0x00}, {0x38, 0x01, 0xa3, 0x01}
+ },
+ /* 130 */
+ {
+ {0x02, 0x01, 0xa4, 0x00}, {0x09, 0x01, 0xa4, 0x00},
+ {0x17, 0x01, 0xa4, 0x00}, {0x28, 0x01, 0xa4, 0x01},
+ {0x02, 0x01, 0xa9, 0x00}, {0x09, 0x01, 0xa9, 0x00},
+ {0x17, 0x01, 0xa9, 0x00}, {0x28, 0x01, 0xa9, 0x01},
+ {0x02, 0x01, 0xaa, 0x00}, {0x09, 0x01, 0xaa, 0x00},
+ {0x17, 0x01, 0xaa, 0x00}, {0x28, 0x01, 0xaa, 0x01},
+ {0x02, 0x01, 0xad, 0x00}, {0x09, 0x01, 0xad, 0x00},
+ {0x17, 0x01, 0xad, 0x00}, {0x28, 0x01, 0xad, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xa4, 0x00}, {0x06, 0x01, 0xa4, 0x00},
+ {0x0a, 0x01, 0xa4, 0x00}, {0x0f, 0x01, 0xa4, 0x00},
+ {0x18, 0x01, 0xa4, 0x00}, {0x1f, 0x01, 0xa4, 0x00},
+ {0x29, 0x01, 0xa4, 0x00}, {0x38, 0x01, 0xa4, 0x01},
+ {0x03, 0x01, 0xa9, 0x00}, {0x06, 0x01, 0xa9, 0x00},
+ {0x0a, 0x01, 0xa9, 0x00}, {0x0f, 0x01, 0xa9, 0x00},
+ {0x18, 0x01, 0xa9, 0x00}, {0x1f, 0x01, 0xa9, 0x00},
+ {0x29, 0x01, 0xa9, 0x00}, {0x38, 0x01, 0xa9, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xaa, 0x00}, {0x06, 0x01, 0xaa, 0x00},
+ {0x0a, 0x01, 0xaa, 0x00}, {0x0f, 0x01, 0xaa, 0x00},
+ {0x18, 0x01, 0xaa, 0x00}, {0x1f, 0x01, 0xaa, 0x00},
+ {0x29, 0x01, 0xaa, 0x00}, {0x38, 0x01, 0xaa, 0x01},
+ {0x03, 0x01, 0xad, 0x00}, {0x06, 0x01, 0xad, 0x00},
+ {0x0a, 0x01, 0xad, 0x00}, {0x0f, 0x01, 0xad, 0x00},
+ {0x18, 0x01, 0xad, 0x00}, {0x1f, 0x01, 0xad, 0x00},
+ {0x29, 0x01, 0xad, 0x00}, {0x38, 0x01, 0xad, 0x01}
+ },
+ {
+ {0x89, 0x00, 0x00, 0x00}, {0x8a, 0x00, 0x00, 0x00},
+ {0x8c, 0x00, 0x00, 0x00}, {0x8d, 0x00, 0x00, 0x00},
+ {0x90, 0x00, 0x00, 0x00}, {0x91, 0x00, 0x00, 0x00},
+ {0x93, 0x00, 0x00, 0x00}, {0x96, 0x00, 0x00, 0x00},
+ {0x9c, 0x00, 0x00, 0x00}, {0x9f, 0x00, 0x00, 0x00},
+ {0xa3, 0x00, 0x00, 0x00}, {0xa6, 0x00, 0x00, 0x00},
+ {0xab, 0x00, 0x00, 0x00}, {0xae, 0x00, 0x00, 0x00},
+ {0xb5, 0x00, 0x00, 0x00}, {0xbe, 0x00, 0x00, 0x01}
+ },
+ {
+ {0x00, 0x01, 0xb2, 0x01}, {0x00, 0x01, 0xb5, 0x01},
+ {0x00, 0x01, 0xb9, 0x01}, {0x00, 0x01, 0xba, 0x01},
+ {0x00, 0x01, 0xbb, 0x01}, {0x00, 0x01, 0xbd, 0x01},
+ {0x00, 0x01, 0xbe, 0x01}, {0x00, 0x01, 0xc4, 0x01},
+ {0x00, 0x01, 0xc6, 0x01}, {0x00, 0x01, 0xe4, 0x01},
+ {0x00, 0x01, 0xe8, 0x01}, {0x00, 0x01, 0xe9, 0x01},
+ {0x94, 0x00, 0x00, 0x00}, {0x95, 0x00, 0x00, 0x00},
+ {0x97, 0x00, 0x00, 0x00}, {0x98, 0x00, 0x00, 0x00}
+ },
+ /* 135 */
+ {
+ {0x01, 0x01, 0xb2, 0x00}, {0x16, 0x01, 0xb2, 0x01},
+ {0x01, 0x01, 0xb5, 0x00}, {0x16, 0x01, 0xb5, 0x01},
+ {0x01, 0x01, 0xb9, 0x00}, {0x16, 0x01, 0xb9, 0x01},
+ {0x01, 0x01, 0xba, 0x00}, {0x16, 0x01, 0xba, 0x01},
+ {0x01, 0x01, 0xbb, 0x00}, {0x16, 0x01, 0xbb, 0x01},
+ {0x01, 0x01, 0xbd, 0x00}, {0x16, 0x01, 0xbd, 0x01},
+ {0x01, 0x01, 0xbe, 0x00}, {0x16, 0x01, 0xbe, 0x01},
+ {0x01, 0x01, 0xc4, 0x00}, {0x16, 0x01, 0xc4, 0x01}
+ },
+ {
+ {0x02, 0x01, 0xb2, 0x00}, {0x09, 0x01, 0xb2, 0x00},
+ {0x17, 0x01, 0xb2, 0x00}, {0x28, 0x01, 0xb2, 0x01},
+ {0x02, 0x01, 0xb5, 0x00}, {0x09, 0x01, 0xb5, 0x00},
+ {0x17, 0x01, 0xb5, 0x00}, {0x28, 0x01, 0xb5, 0x01},
+ {0x02, 0x01, 0xb9, 0x00}, {0x09, 0x01, 0xb9, 0x00},
+ {0x17, 0x01, 0xb9, 0x00}, {0x28, 0x01, 0xb9, 0x01},
+ {0x02, 0x01, 0xba, 0x00}, {0x09, 0x01, 0xba, 0x00},
+ {0x17, 0x01, 0xba, 0x00}, {0x28, 0x01, 0xba, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xb2, 0x00}, {0x06, 0x01, 0xb2, 0x00},
+ {0x0a, 0x01, 0xb2, 0x00}, {0x0f, 0x01, 0xb2, 0x00},
+ {0x18, 0x01, 0xb2, 0x00}, {0x1f, 0x01, 0xb2, 0x00},
+ {0x29, 0x01, 0xb2, 0x00}, {0x38, 0x01, 0xb2, 0x01},
+ {0x03, 0x01, 0xb5, 0x00}, {0x06, 0x01, 0xb5, 0x00},
+ {0x0a, 0x01, 0xb5, 0x00}, {0x0f, 0x01, 0xb5, 0x00},
+ {0x18, 0x01, 0xb5, 0x00}, {0x1f, 0x01, 0xb5, 0x00},
+ {0x29, 0x01, 0xb5, 0x00}, {0x38, 0x01, 0xb5, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xb9, 0x00}, {0x06, 0x01, 0xb9, 0x00},
+ {0x0a, 0x01, 0xb9, 0x00}, {0x0f, 0x01, 0xb9, 0x00},
+ {0x18, 0x01, 0xb9, 0x00}, {0x1f, 0x01, 0xb9, 0x00},
+ {0x29, 0x01, 0xb9, 0x00}, {0x38, 0x01, 0xb9, 0x01},
+ {0x03, 0x01, 0xba, 0x00}, {0x06, 0x01, 0xba, 0x00},
+ {0x0a, 0x01, 0xba, 0x00}, {0x0f, 0x01, 0xba, 0x00},
+ {0x18, 0x01, 0xba, 0x00}, {0x1f, 0x01, 0xba, 0x00},
+ {0x29, 0x01, 0xba, 0x00}, {0x38, 0x01, 0xba, 0x01}
+ },
+ {
+ {0x02, 0x01, 0xbb, 0x00}, {0x09, 0x01, 0xbb, 0x00},
+ {0x17, 0x01, 0xbb, 0x00}, {0x28, 0x01, 0xbb, 0x01},
+ {0x02, 0x01, 0xbd, 0x00}, {0x09, 0x01, 0xbd, 0x00},
+ {0x17, 0x01, 0xbd, 0x00}, {0x28, 0x01, 0xbd, 0x01},
+ {0x02, 0x01, 0xbe, 0x00}, {0x09, 0x01, 0xbe, 0x00},
+ {0x17, 0x01, 0xbe, 0x00}, {0x28, 0x01, 0xbe, 0x01},
+ {0x02, 0x01, 0xc4, 0x00}, {0x09, 0x01, 0xc4, 0x00},
+ {0x17, 0x01, 0xc4, 0x00}, {0x28, 0x01, 0xc4, 0x01}
+ },
+ /* 140 */
+ {
+ {0x03, 0x01, 0xbb, 0x00}, {0x06, 0x01, 0xbb, 0x00},
+ {0x0a, 0x01, 0xbb, 0x00}, {0x0f, 0x01, 0xbb, 0x00},
+ {0x18, 0x01, 0xbb, 0x00}, {0x1f, 0x01, 0xbb, 0x00},
+ {0x29, 0x01, 0xbb, 0x00}, {0x38, 0x01, 0xbb, 0x01},
+ {0x03, 0x01, 0xbd, 0x00}, {0x06, 0x01, 0xbd, 0x00},
+ {0x0a, 0x01, 0xbd, 0x00}, {0x0f, 0x01, 0xbd, 0x00},
+ {0x18, 0x01, 0xbd, 0x00}, {0x1f, 0x01, 0xbd, 0x00},
+ {0x29, 0x01, 0xbd, 0x00}, {0x38, 0x01, 0xbd, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xbe, 0x00}, {0x06, 0x01, 0xbe, 0x00},
+ {0x0a, 0x01, 0xbe, 0x00}, {0x0f, 0x01, 0xbe, 0x00},
+ {0x18, 0x01, 0xbe, 0x00}, {0x1f, 0x01, 0xbe, 0x00},
+ {0x29, 0x01, 0xbe, 0x00}, {0x38, 0x01, 0xbe, 0x01},
+ {0x03, 0x01, 0xc4, 0x00}, {0x06, 0x01, 0xc4, 0x00},
+ {0x0a, 0x01, 0xc4, 0x00}, {0x0f, 0x01, 0xc4, 0x00},
+ {0x18, 0x01, 0xc4, 0x00}, {0x1f, 0x01, 0xc4, 0x00},
+ {0x29, 0x01, 0xc4, 0x00}, {0x38, 0x01, 0xc4, 0x01}
+ },
+ {
+ {0x01, 0x01, 0xc6, 0x00}, {0x16, 0x01, 0xc6, 0x01},
+ {0x01, 0x01, 0xe4, 0x00}, {0x16, 0x01, 0xe4, 0x01},
+ {0x01, 0x01, 0xe8, 0x00}, {0x16, 0x01, 0xe8, 0x01},
+ {0x01, 0x01, 0xe9, 0x00}, {0x16, 0x01, 0xe9, 0x01},
+ {0x00, 0x01, 0x01, 0x01}, {0x00, 0x01, 0x87, 0x01},
+ {0x00, 0x01, 0x89, 0x01}, {0x00, 0x01, 0x8a, 0x01},
+ {0x00, 0x01, 0x8b, 0x01}, {0x00, 0x01, 0x8c, 0x01},
+ {0x00, 0x01, 0x8d, 0x01}, {0x00, 0x01, 0x8f, 0x01}
+ },
+ {
+ {0x02, 0x01, 0xc6, 0x00}, {0x09, 0x01, 0xc6, 0x00},
+ {0x17, 0x01, 0xc6, 0x00}, {0x28, 0x01, 0xc6, 0x01},
+ {0x02, 0x01, 0xe4, 0x00}, {0x09, 0x01, 0xe4, 0x00},
+ {0x17, 0x01, 0xe4, 0x00}, {0x28, 0x01, 0xe4, 0x01},
+ {0x02, 0x01, 0xe8, 0x00}, {0x09, 0x01, 0xe8, 0x00},
+ {0x17, 0x01, 0xe8, 0x00}, {0x28, 0x01, 0xe8, 0x01},
+ {0x02, 0x01, 0xe9, 0x00}, {0x09, 0x01, 0xe9, 0x00},
+ {0x17, 0x01, 0xe9, 0x00}, {0x28, 0x01, 0xe9, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xc6, 0x00}, {0x06, 0x01, 0xc6, 0x00},
+ {0x0a, 0x01, 0xc6, 0x00}, {0x0f, 0x01, 0xc6, 0x00},
+ {0x18, 0x01, 0xc6, 0x00}, {0x1f, 0x01, 0xc6, 0x00},
+ {0x29, 0x01, 0xc6, 0x00}, {0x38, 0x01, 0xc6, 0x01},
+ {0x03, 0x01, 0xe4, 0x00}, {0x06, 0x01, 0xe4, 0x00},
+ {0x0a, 0x01, 0xe4, 0x00}, {0x0f, 0x01, 0xe4, 0x00},
+ {0x18, 0x01, 0xe4, 0x00}, {0x1f, 0x01, 0xe4, 0x00},
+ {0x29, 0x01, 0xe4, 0x00}, {0x38, 0x01, 0xe4, 0x01}
+ },
+ /* 145 */
+ {
+ {0x03, 0x01, 0xe8, 0x00}, {0x06, 0x01, 0xe8, 0x00},
+ {0x0a, 0x01, 0xe8, 0x00}, {0x0f, 0x01, 0xe8, 0x00},
+ {0x18, 0x01, 0xe8, 0x00}, {0x1f, 0x01, 0xe8, 0x00},
+ {0x29, 0x01, 0xe8, 0x00}, {0x38, 0x01, 0xe8, 0x01},
+ {0x03, 0x01, 0xe9, 0x00}, {0x06, 0x01, 0xe9, 0x00},
+ {0x0a, 0x01, 0xe9, 0x00}, {0x0f, 0x01, 0xe9, 0x00},
+ {0x18, 0x01, 0xe9, 0x00}, {0x1f, 0x01, 0xe9, 0x00},
+ {0x29, 0x01, 0xe9, 0x00}, {0x38, 0x01, 0xe9, 0x01}
+ },
+ {
+ {0x01, 0x01, 0x01, 0x00}, {0x16, 0x01, 0x01, 0x01},
+ {0x01, 0x01, 0x87, 0x00}, {0x16, 0x01, 0x87, 0x01},
+ {0x01, 0x01, 0x89, 0x00}, {0x16, 0x01, 0x89, 0x01},
+ {0x01, 0x01, 0x8a, 0x00}, {0x16, 0x01, 0x8a, 0x01},
+ {0x01, 0x01, 0x8b, 0x00}, {0x16, 0x01, 0x8b, 0x01},
+ {0x01, 0x01, 0x8c, 0x00}, {0x16, 0x01, 0x8c, 0x01},
+ {0x01, 0x01, 0x8d, 0x00}, {0x16, 0x01, 0x8d, 0x01},
+ {0x01, 0x01, 0x8f, 0x00}, {0x16, 0x01, 0x8f, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x01, 0x00}, {0x09, 0x01, 0x01, 0x00},
+ {0x17, 0x01, 0x01, 0x00}, {0x28, 0x01, 0x01, 0x01},
+ {0x02, 0x01, 0x87, 0x00}, {0x09, 0x01, 0x87, 0x00},
+ {0x17, 0x01, 0x87, 0x00}, {0x28, 0x01, 0x87, 0x01},
+ {0x02, 0x01, 0x89, 0x00}, {0x09, 0x01, 0x89, 0x00},
+ {0x17, 0x01, 0x89, 0x00}, {0x28, 0x01, 0x89, 0x01},
+ {0x02, 0x01, 0x8a, 0x00}, {0x09, 0x01, 0x8a, 0x00},
+ {0x17, 0x01, 0x8a, 0x00}, {0x28, 0x01, 0x8a, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x01, 0x00}, {0x06, 0x01, 0x01, 0x00},
+ {0x0a, 0x01, 0x01, 0x00}, {0x0f, 0x01, 0x01, 0x00},
+ {0x18, 0x01, 0x01, 0x00}, {0x1f, 0x01, 0x01, 0x00},
+ {0x29, 0x01, 0x01, 0x00}, {0x38, 0x01, 0x01, 0x01},
+ {0x03, 0x01, 0x87, 0x00}, {0x06, 0x01, 0x87, 0x00},
+ {0x0a, 0x01, 0x87, 0x00}, {0x0f, 0x01, 0x87, 0x00},
+ {0x18, 0x01, 0x87, 0x00}, {0x1f, 0x01, 0x87, 0x00},
+ {0x29, 0x01, 0x87, 0x00}, {0x38, 0x01, 0x87, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x89, 0x00}, {0x06, 0x01, 0x89, 0x00},
+ {0x0a, 0x01, 0x89, 0x00}, {0x0f, 0x01, 0x89, 0x00},
+ {0x18, 0x01, 0x89, 0x00}, {0x1f, 0x01, 0x89, 0x00},
+ {0x29, 0x01, 0x89, 0x00}, {0x38, 0x01, 0x89, 0x01},
+ {0x03, 0x01, 0x8a, 0x00}, {0x06, 0x01, 0x8a, 0x00},
+ {0x0a, 0x01, 0x8a, 0x00}, {0x0f, 0x01, 0x8a, 0x00},
+ {0x18, 0x01, 0x8a, 0x00}, {0x1f, 0x01, 0x8a, 0x00},
+ {0x29, 0x01, 0x8a, 0x00}, {0x38, 0x01, 0x8a, 0x01}
+ },
+ /* 150 */
+ {
+ {0x02, 0x01, 0x8b, 0x00}, {0x09, 0x01, 0x8b, 0x00},
+ {0x17, 0x01, 0x8b, 0x00}, {0x28, 0x01, 0x8b, 0x01},
+ {0x02, 0x01, 0x8c, 0x00}, {0x09, 0x01, 0x8c, 0x00},
+ {0x17, 0x01, 0x8c, 0x00}, {0x28, 0x01, 0x8c, 0x01},
+ {0x02, 0x01, 0x8d, 0x00}, {0x09, 0x01, 0x8d, 0x00},
+ {0x17, 0x01, 0x8d, 0x00}, {0x28, 0x01, 0x8d, 0x01},
+ {0x02, 0x01, 0x8f, 0x00}, {0x09, 0x01, 0x8f, 0x00},
+ {0x17, 0x01, 0x8f, 0x00}, {0x28, 0x01, 0x8f, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x8b, 0x00}, {0x06, 0x01, 0x8b, 0x00},
+ {0x0a, 0x01, 0x8b, 0x00}, {0x0f, 0x01, 0x8b, 0x00},
+ {0x18, 0x01, 0x8b, 0x00}, {0x1f, 0x01, 0x8b, 0x00},
+ {0x29, 0x01, 0x8b, 0x00}, {0x38, 0x01, 0x8b, 0x01},
+ {0x03, 0x01, 0x8c, 0x00}, {0x06, 0x01, 0x8c, 0x00},
+ {0x0a, 0x01, 0x8c, 0x00}, {0x0f, 0x01, 0x8c, 0x00},
+ {0x18, 0x01, 0x8c, 0x00}, {0x1f, 0x01, 0x8c, 0x00},
+ {0x29, 0x01, 0x8c, 0x00}, {0x38, 0x01, 0x8c, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x8d, 0x00}, {0x06, 0x01, 0x8d, 0x00},
+ {0x0a, 0x01, 0x8d, 0x00}, {0x0f, 0x01, 0x8d, 0x00},
+ {0x18, 0x01, 0x8d, 0x00}, {0x1f, 0x01, 0x8d, 0x00},
+ {0x29, 0x01, 0x8d, 0x00}, {0x38, 0x01, 0x8d, 0x01},
+ {0x03, 0x01, 0x8f, 0x00}, {0x06, 0x01, 0x8f, 0x00},
+ {0x0a, 0x01, 0x8f, 0x00}, {0x0f, 0x01, 0x8f, 0x00},
+ {0x18, 0x01, 0x8f, 0x00}, {0x1f, 0x01, 0x8f, 0x00},
+ {0x29, 0x01, 0x8f, 0x00}, {0x38, 0x01, 0x8f, 0x01}
+ },
+ {
+ {0x9d, 0x00, 0x00, 0x00}, {0x9e, 0x00, 0x00, 0x00},
+ {0xa0, 0x00, 0x00, 0x00}, {0xa1, 0x00, 0x00, 0x00},
+ {0xa4, 0x00, 0x00, 0x00}, {0xa5, 0x00, 0x00, 0x00},
+ {0xa7, 0x00, 0x00, 0x00}, {0xa8, 0x00, 0x00, 0x00},
+ {0xac, 0x00, 0x00, 0x00}, {0xad, 0x00, 0x00, 0x00},
+ {0xaf, 0x00, 0x00, 0x00}, {0xb1, 0x00, 0x00, 0x00},
+ {0xb6, 0x00, 0x00, 0x00}, {0xb9, 0x00, 0x00, 0x00},
+ {0xbf, 0x00, 0x00, 0x00}, {0xcf, 0x00, 0x00, 0x01}
+ },
+ {
+ {0x00, 0x01, 0x93, 0x01}, {0x00, 0x01, 0x95, 0x01},
+ {0x00, 0x01, 0x96, 0x01}, {0x00, 0x01, 0x97, 0x01},
+ {0x00, 0x01, 0x98, 0x01}, {0x00, 0x01, 0x9b, 0x01},
+ {0x00, 0x01, 0x9d, 0x01}, {0x00, 0x01, 0x9e, 0x01},
+ {0x00, 0x01, 0xa5, 0x01}, {0x00, 0x01, 0xa6, 0x01},
+ {0x00, 0x01, 0xa8, 0x01}, {0x00, 0x01, 0xae, 0x01},
+ {0x00, 0x01, 0xaf, 0x01}, {0x00, 0x01, 0xb4, 0x01},
+ {0x00, 0x01, 0xb6, 0x01}, {0x00, 0x01, 0xb7, 0x01}
+ },
+ /* 155 */
+ {
+ {0x01, 0x01, 0x93, 0x00}, {0x16, 0x01, 0x93, 0x01},
+ {0x01, 0x01, 0x95, 0x00}, {0x16, 0x01, 0x95, 0x01},
+ {0x01, 0x01, 0x96, 0x00}, {0x16, 0x01, 0x96, 0x01},
+ {0x01, 0x01, 0x97, 0x00}, {0x16, 0x01, 0x97, 0x01},
+ {0x01, 0x01, 0x98, 0x00}, {0x16, 0x01, 0x98, 0x01},
+ {0x01, 0x01, 0x9b, 0x00}, {0x16, 0x01, 0x9b, 0x01},
+ {0x01, 0x01, 0x9d, 0x00}, {0x16, 0x01, 0x9d, 0x01},
+ {0x01, 0x01, 0x9e, 0x00}, {0x16, 0x01, 0x9e, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x93, 0x00}, {0x09, 0x01, 0x93, 0x00},
+ {0x17, 0x01, 0x93, 0x00}, {0x28, 0x01, 0x93, 0x01},
+ {0x02, 0x01, 0x95, 0x00}, {0x09, 0x01, 0x95, 0x00},
+ {0x17, 0x01, 0x95, 0x00}, {0x28, 0x01, 0x95, 0x01},
+ {0x02, 0x01, 0x96, 0x00}, {0x09, 0x01, 0x96, 0x00},
+ {0x17, 0x01, 0x96, 0x00}, {0x28, 0x01, 0x96, 0x01},
+ {0x02, 0x01, 0x97, 0x00}, {0x09, 0x01, 0x97, 0x00},
+ {0x17, 0x01, 0x97, 0x00}, {0x28, 0x01, 0x97, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x93, 0x00}, {0x06, 0x01, 0x93, 0x00},
+ {0x0a, 0x01, 0x93, 0x00}, {0x0f, 0x01, 0x93, 0x00},
+ {0x18, 0x01, 0x93, 0x00}, {0x1f, 0x01, 0x93, 0x00},
+ {0x29, 0x01, 0x93, 0x00}, {0x38, 0x01, 0x93, 0x01},
+ {0x03, 0x01, 0x95, 0x00}, {0x06, 0x01, 0x95, 0x00},
+ {0x0a, 0x01, 0x95, 0x00}, {0x0f, 0x01, 0x95, 0x00},
+ {0x18, 0x01, 0x95, 0x00}, {0x1f, 0x01, 0x95, 0x00},
+ {0x29, 0x01, 0x95, 0x00}, {0x38, 0x01, 0x95, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x96, 0x00}, {0x06, 0x01, 0x96, 0x00},
+ {0x0a, 0x01, 0x96, 0x00}, {0x0f, 0x01, 0x96, 0x00},
+ {0x18, 0x01, 0x96, 0x00}, {0x1f, 0x01, 0x96, 0x00},
+ {0x29, 0x01, 0x96, 0x00}, {0x38, 0x01, 0x96, 0x01},
+ {0x03, 0x01, 0x97, 0x00}, {0x06, 0x01, 0x97, 0x00},
+ {0x0a, 0x01, 0x97, 0x00}, {0x0f, 0x01, 0x97, 0x00},
+ {0x18, 0x01, 0x97, 0x00}, {0x1f, 0x01, 0x97, 0x00},
+ {0x29, 0x01, 0x97, 0x00}, {0x38, 0x01, 0x97, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x98, 0x00}, {0x09, 0x01, 0x98, 0x00},
+ {0x17, 0x01, 0x98, 0x00}, {0x28, 0x01, 0x98, 0x01},
+ {0x02, 0x01, 0x9b, 0x00}, {0x09, 0x01, 0x9b, 0x00},
+ {0x17, 0x01, 0x9b, 0x00}, {0x28, 0x01, 0x9b, 0x01},
+ {0x02, 0x01, 0x9d, 0x00}, {0x09, 0x01, 0x9d, 0x00},
+ {0x17, 0x01, 0x9d, 0x00}, {0x28, 0x01, 0x9d, 0x01},
+ {0x02, 0x01, 0x9e, 0x00}, {0x09, 0x01, 0x9e, 0x00},
+ {0x17, 0x01, 0x9e, 0x00}, {0x28, 0x01, 0x9e, 0x01}
+ },
+ /* 160 */
+ {
+ {0x03, 0x01, 0x98, 0x00}, {0x06, 0x01, 0x98, 0x00},
+ {0x0a, 0x01, 0x98, 0x00}, {0x0f, 0x01, 0x98, 0x00},
+ {0x18, 0x01, 0x98, 0x00}, {0x1f, 0x01, 0x98, 0x00},
+ {0x29, 0x01, 0x98, 0x00}, {0x38, 0x01, 0x98, 0x01},
+ {0x03, 0x01, 0x9b, 0x00}, {0x06, 0x01, 0x9b, 0x00},
+ {0x0a, 0x01, 0x9b, 0x00}, {0x0f, 0x01, 0x9b, 0x00},
+ {0x18, 0x01, 0x9b, 0x00}, {0x1f, 0x01, 0x9b, 0x00},
+ {0x29, 0x01, 0x9b, 0x00}, {0x38, 0x01, 0x9b, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x9d, 0x00}, {0x06, 0x01, 0x9d, 0x00},
+ {0x0a, 0x01, 0x9d, 0x00}, {0x0f, 0x01, 0x9d, 0x00},
+ {0x18, 0x01, 0x9d, 0x00}, {0x1f, 0x01, 0x9d, 0x00},
+ {0x29, 0x01, 0x9d, 0x00}, {0x38, 0x01, 0x9d, 0x01},
+ {0x03, 0x01, 0x9e, 0x00}, {0x06, 0x01, 0x9e, 0x00},
+ {0x0a, 0x01, 0x9e, 0x00}, {0x0f, 0x01, 0x9e, 0x00},
+ {0x18, 0x01, 0x9e, 0x00}, {0x1f, 0x01, 0x9e, 0x00},
+ {0x29, 0x01, 0x9e, 0x00}, {0x38, 0x01, 0x9e, 0x01}
+ },
+ {
+ {0x01, 0x01, 0xa5, 0x00}, {0x16, 0x01, 0xa5, 0x01},
+ {0x01, 0x01, 0xa6, 0x00}, {0x16, 0x01, 0xa6, 0x01},
+ {0x01, 0x01, 0xa8, 0x00}, {0x16, 0x01, 0xa8, 0x01},
+ {0x01, 0x01, 0xae, 0x00}, {0x16, 0x01, 0xae, 0x01},
+ {0x01, 0x01, 0xaf, 0x00}, {0x16, 0x01, 0xaf, 0x01},
+ {0x01, 0x01, 0xb4, 0x00}, {0x16, 0x01, 0xb4, 0x01},
+ {0x01, 0x01, 0xb6, 0x00}, {0x16, 0x01, 0xb6, 0x01},
+ {0x01, 0x01, 0xb7, 0x00}, {0x16, 0x01, 0xb7, 0x01}
+ },
+ {
+ {0x02, 0x01, 0xa5, 0x00}, {0x09, 0x01, 0xa5, 0x00},
+ {0x17, 0x01, 0xa5, 0x00}, {0x28, 0x01, 0xa5, 0x01},
+ {0x02, 0x01, 0xa6, 0x00}, {0x09, 0x01, 0xa6, 0x00},
+ {0x17, 0x01, 0xa6, 0x00}, {0x28, 0x01, 0xa6, 0x01},
+ {0x02, 0x01, 0xa8, 0x00}, {0x09, 0x01, 0xa8, 0x00},
+ {0x17, 0x01, 0xa8, 0x00}, {0x28, 0x01, 0xa8, 0x01},
+ {0x02, 0x01, 0xae, 0x00}, {0x09, 0x01, 0xae, 0x00},
+ {0x17, 0x01, 0xae, 0x00}, {0x28, 0x01, 0xae, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xa5, 0x00}, {0x06, 0x01, 0xa5, 0x00},
+ {0x0a, 0x01, 0xa5, 0x00}, {0x0f, 0x01, 0xa5, 0x00},
+ {0x18, 0x01, 0xa5, 0x00}, {0x1f, 0x01, 0xa5, 0x00},
+ {0x29, 0x01, 0xa5, 0x00}, {0x38, 0x01, 0xa5, 0x01},
+ {0x03, 0x01, 0xa6, 0x00}, {0x06, 0x01, 0xa6, 0x00},
+ {0x0a, 0x01, 0xa6, 0x00}, {0x0f, 0x01, 0xa6, 0x00},
+ {0x18, 0x01, 0xa6, 0x00}, {0x1f, 0x01, 0xa6, 0x00},
+ {0x29, 0x01, 0xa6, 0x00}, {0x38, 0x01, 0xa6, 0x01}
+ },
+ /* 165 */
+ {
+ {0x03, 0x01, 0xa8, 0x00}, {0x06, 0x01, 0xa8, 0x00},
+ {0x0a, 0x01, 0xa8, 0x00}, {0x0f, 0x01, 0xa8, 0x00},
+ {0x18, 0x01, 0xa8, 0x00}, {0x1f, 0x01, 0xa8, 0x00},
+ {0x29, 0x01, 0xa8, 0x00}, {0x38, 0x01, 0xa8, 0x01},
+ {0x03, 0x01, 0xae, 0x00}, {0x06, 0x01, 0xae, 0x00},
+ {0x0a, 0x01, 0xae, 0x00}, {0x0f, 0x01, 0xae, 0x00},
+ {0x18, 0x01, 0xae, 0x00}, {0x1f, 0x01, 0xae, 0x00},
+ {0x29, 0x01, 0xae, 0x00}, {0x38, 0x01, 0xae, 0x01}
+ },
+ {
+ {0x02, 0x01, 0xaf, 0x00}, {0x09, 0x01, 0xaf, 0x00},
+ {0x17, 0x01, 0xaf, 0x00}, {0x28, 0x01, 0xaf, 0x01},
+ {0x02, 0x01, 0xb4, 0x00}, {0x09, 0x01, 0xb4, 0x00},
+ {0x17, 0x01, 0xb4, 0x00}, {0x28, 0x01, 0xb4, 0x01},
+ {0x02, 0x01, 0xb6, 0x00}, {0x09, 0x01, 0xb6, 0x00},
+ {0x17, 0x01, 0xb6, 0x00}, {0x28, 0x01, 0xb6, 0x01},
+ {0x02, 0x01, 0xb7, 0x00}, {0x09, 0x01, 0xb7, 0x00},
+ {0x17, 0x01, 0xb7, 0x00}, {0x28, 0x01, 0xb7, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xaf, 0x00}, {0x06, 0x01, 0xaf, 0x00},
+ {0x0a, 0x01, 0xaf, 0x00}, {0x0f, 0x01, 0xaf, 0x00},
+ {0x18, 0x01, 0xaf, 0x00}, {0x1f, 0x01, 0xaf, 0x00},
+ {0x29, 0x01, 0xaf, 0x00}, {0x38, 0x01, 0xaf, 0x01},
+ {0x03, 0x01, 0xb4, 0x00}, {0x06, 0x01, 0xb4, 0x00},
+ {0x0a, 0x01, 0xb4, 0x00}, {0x0f, 0x01, 0xb4, 0x00},
+ {0x18, 0x01, 0xb4, 0x00}, {0x1f, 0x01, 0xb4, 0x00},
+ {0x29, 0x01, 0xb4, 0x00}, {0x38, 0x01, 0xb4, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xb6, 0x00}, {0x06, 0x01, 0xb6, 0x00},
+ {0x0a, 0x01, 0xb6, 0x00}, {0x0f, 0x01, 0xb6, 0x00},
+ {0x18, 0x01, 0xb6, 0x00}, {0x1f, 0x01, 0xb6, 0x00},
+ {0x29, 0x01, 0xb6, 0x00}, {0x38, 0x01, 0xb6, 0x01},
+ {0x03, 0x01, 0xb7, 0x00}, {0x06, 0x01, 0xb7, 0x00},
+ {0x0a, 0x01, 0xb7, 0x00}, {0x0f, 0x01, 0xb7, 0x00},
+ {0x18, 0x01, 0xb7, 0x00}, {0x1f, 0x01, 0xb7, 0x00},
+ {0x29, 0x01, 0xb7, 0x00}, {0x38, 0x01, 0xb7, 0x01}
+ },
+ {
+ {0x00, 0x01, 0xbc, 0x01}, {0x00, 0x01, 0xbf, 0x01},
+ {0x00, 0x01, 0xc5, 0x01}, {0x00, 0x01, 0xe7, 0x01},
+ {0x00, 0x01, 0xef, 0x01}, {0xb0, 0x00, 0x00, 0x00},
+ {0xb2, 0x00, 0x00, 0x00}, {0xb3, 0x00, 0x00, 0x00},
+ {0xb7, 0x00, 0x00, 0x00}, {0xb8, 0x00, 0x00, 0x00},
+ {0xba, 0x00, 0x00, 0x00}, {0xbb, 0x00, 0x00, 0x00},
+ {0xc0, 0x00, 0x00, 0x00}, {0xc7, 0x00, 0x00, 0x00},
+ {0xd0, 0x00, 0x00, 0x00}, {0xdf, 0x00, 0x00, 0x01}
+ },
+ /* 170 */
+ {
+ {0x01, 0x01, 0xbc, 0x00}, {0x16, 0x01, 0xbc, 0x01},
+ {0x01, 0x01, 0xbf, 0x00}, {0x16, 0x01, 0xbf, 0x01},
+ {0x01, 0x01, 0xc5, 0x00}, {0x16, 0x01, 0xc5, 0x01},
+ {0x01, 0x01, 0xe7, 0x00}, {0x16, 0x01, 0xe7, 0x01},
+ {0x01, 0x01, 0xef, 0x00}, {0x16, 0x01, 0xef, 0x01},
+ {0x00, 0x01, 0x09, 0x01}, {0x00, 0x01, 0x8e, 0x01},
+ {0x00, 0x01, 0x90, 0x01}, {0x00, 0x01, 0x91, 0x01},
+ {0x00, 0x01, 0x94, 0x01}, {0x00, 0x01, 0x9f, 0x01}
+ },
+ {
+ {0x02, 0x01, 0xbc, 0x00}, {0x09, 0x01, 0xbc, 0x00},
+ {0x17, 0x01, 0xbc, 0x00}, {0x28, 0x01, 0xbc, 0x01},
+ {0x02, 0x01, 0xbf, 0x00}, {0x09, 0x01, 0xbf, 0x00},
+ {0x17, 0x01, 0xbf, 0x00}, {0x28, 0x01, 0xbf, 0x01},
+ {0x02, 0x01, 0xc5, 0x00}, {0x09, 0x01, 0xc5, 0x00},
+ {0x17, 0x01, 0xc5, 0x00}, {0x28, 0x01, 0xc5, 0x01},
+ {0x02, 0x01, 0xe7, 0x00}, {0x09, 0x01, 0xe7, 0x00},
+ {0x17, 0x01, 0xe7, 0x00}, {0x28, 0x01, 0xe7, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xbc, 0x00}, {0x06, 0x01, 0xbc, 0x00},
+ {0x0a, 0x01, 0xbc, 0x00}, {0x0f, 0x01, 0xbc, 0x00},
+ {0x18, 0x01, 0xbc, 0x00}, {0x1f, 0x01, 0xbc, 0x00},
+ {0x29, 0x01, 0xbc, 0x00}, {0x38, 0x01, 0xbc, 0x01},
+ {0x03, 0x01, 0xbf, 0x00}, {0x06, 0x01, 0xbf, 0x00},
+ {0x0a, 0x01, 0xbf, 0x00}, {0x0f, 0x01, 0xbf, 0x00},
+ {0x18, 0x01, 0xbf, 0x00}, {0x1f, 0x01, 0xbf, 0x00},
+ {0x29, 0x01, 0xbf, 0x00}, {0x38, 0x01, 0xbf, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xc5, 0x00}, {0x06, 0x01, 0xc5, 0x00},
+ {0x0a, 0x01, 0xc5, 0x00}, {0x0f, 0x01, 0xc5, 0x00},
+ {0x18, 0x01, 0xc5, 0x00}, {0x1f, 0x01, 0xc5, 0x00},
+ {0x29, 0x01, 0xc5, 0x00}, {0x38, 0x01, 0xc5, 0x01},
+ {0x03, 0x01, 0xe7, 0x00}, {0x06, 0x01, 0xe7, 0x00},
+ {0x0a, 0x01, 0xe7, 0x00}, {0x0f, 0x01, 0xe7, 0x00},
+ {0x18, 0x01, 0xe7, 0x00}, {0x1f, 0x01, 0xe7, 0x00},
+ {0x29, 0x01, 0xe7, 0x00}, {0x38, 0x01, 0xe7, 0x01}
+ },
+ {
+ {0x02, 0x01, 0xef, 0x00}, {0x09, 0x01, 0xef, 0x00},
+ {0x17, 0x01, 0xef, 0x00}, {0x28, 0x01, 0xef, 0x01},
+ {0x01, 0x01, 0x09, 0x00}, {0x16, 0x01, 0x09, 0x01},
+ {0x01, 0x01, 0x8e, 0x00}, {0x16, 0x01, 0x8e, 0x01},
+ {0x01, 0x01, 0x90, 0x00}, {0x16, 0x01, 0x90, 0x01},
+ {0x01, 0x01, 0x91, 0x00}, {0x16, 0x01, 0x91, 0x01},
+ {0x01, 0x01, 0x94, 0x00}, {0x16, 0x01, 0x94, 0x01},
+ {0x01, 0x01, 0x9f, 0x00}, {0x16, 0x01, 0x9f, 0x01}
+ },
+ /* 175 */
+ {
+ {0x03, 0x01, 0xef, 0x00}, {0x06, 0x01, 0xef, 0x00},
+ {0x0a, 0x01, 0xef, 0x00}, {0x0f, 0x01, 0xef, 0x00},
+ {0x18, 0x01, 0xef, 0x00}, {0x1f, 0x01, 0xef, 0x00},
+ {0x29, 0x01, 0xef, 0x00}, {0x38, 0x01, 0xef, 0x01},
+ {0x02, 0x01, 0x09, 0x00}, {0x09, 0x01, 0x09, 0x00},
+ {0x17, 0x01, 0x09, 0x00}, {0x28, 0x01, 0x09, 0x01},
+ {0x02, 0x01, 0x8e, 0x00}, {0x09, 0x01, 0x8e, 0x00},
+ {0x17, 0x01, 0x8e, 0x00}, {0x28, 0x01, 0x8e, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x09, 0x00}, {0x06, 0x01, 0x09, 0x00},
+ {0x0a, 0x01, 0x09, 0x00}, {0x0f, 0x01, 0x09, 0x00},
+ {0x18, 0x01, 0x09, 0x00}, {0x1f, 0x01, 0x09, 0x00},
+ {0x29, 0x01, 0x09, 0x00}, {0x38, 0x01, 0x09, 0x01},
+ {0x03, 0x01, 0x8e, 0x00}, {0x06, 0x01, 0x8e, 0x00},
+ {0x0a, 0x01, 0x8e, 0x00}, {0x0f, 0x01, 0x8e, 0x00},
+ {0x18, 0x01, 0x8e, 0x00}, {0x1f, 0x01, 0x8e, 0x00},
+ {0x29, 0x01, 0x8e, 0x00}, {0x38, 0x01, 0x8e, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x90, 0x00}, {0x09, 0x01, 0x90, 0x00},
+ {0x17, 0x01, 0x90, 0x00}, {0x28, 0x01, 0x90, 0x01},
+ {0x02, 0x01, 0x91, 0x00}, {0x09, 0x01, 0x91, 0x00},
+ {0x17, 0x01, 0x91, 0x00}, {0x28, 0x01, 0x91, 0x01},
+ {0x02, 0x01, 0x94, 0x00}, {0x09, 0x01, 0x94, 0x00},
+ {0x17, 0x01, 0x94, 0x00}, {0x28, 0x01, 0x94, 0x01},
+ {0x02, 0x01, 0x9f, 0x00}, {0x09, 0x01, 0x9f, 0x00},
+ {0x17, 0x01, 0x9f, 0x00}, {0x28, 0x01, 0x9f, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x90, 0x00}, {0x06, 0x01, 0x90, 0x00},
+ {0x0a, 0x01, 0x90, 0x00}, {0x0f, 0x01, 0x90, 0x00},
+ {0x18, 0x01, 0x90, 0x00}, {0x1f, 0x01, 0x90, 0x00},
+ {0x29, 0x01, 0x90, 0x00}, {0x38, 0x01, 0x90, 0x01},
+ {0x03, 0x01, 0x91, 0x00}, {0x06, 0x01, 0x91, 0x00},
+ {0x0a, 0x01, 0x91, 0x00}, {0x0f, 0x01, 0x91, 0x00},
+ {0x18, 0x01, 0x91, 0x00}, {0x1f, 0x01, 0x91, 0x00},
+ {0x29, 0x01, 0x91, 0x00}, {0x38, 0x01, 0x91, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x94, 0x00}, {0x06, 0x01, 0x94, 0x00},
+ {0x0a, 0x01, 0x94, 0x00}, {0x0f, 0x01, 0x94, 0x00},
+ {0x18, 0x01, 0x94, 0x00}, {0x1f, 0x01, 0x94, 0x00},
+ {0x29, 0x01, 0x94, 0x00}, {0x38, 0x01, 0x94, 0x01},
+ {0x03, 0x01, 0x9f, 0x00}, {0x06, 0x01, 0x9f, 0x00},
+ {0x0a, 0x01, 0x9f, 0x00}, {0x0f, 0x01, 0x9f, 0x00},
+ {0x18, 0x01, 0x9f, 0x00}, {0x1f, 0x01, 0x9f, 0x00},
+ {0x29, 0x01, 0x9f, 0x00}, {0x38, 0x01, 0x9f, 0x01}
+ },
+ /* 180 */
+ {
+ {0x00, 0x01, 0xab, 0x01}, {0x00, 0x01, 0xce, 0x01},
+ {0x00, 0x01, 0xd7, 0x01}, {0x00, 0x01, 0xe1, 0x01},
+ {0x00, 0x01, 0xec, 0x01}, {0x00, 0x01, 0xed, 0x01},
+ {0xbc, 0x00, 0x00, 0x00}, {0xbd, 0x00, 0x00, 0x00},
+ {0xc1, 0x00, 0x00, 0x00}, {0xc4, 0x00, 0x00, 0x00},
+ {0xc8, 0x00, 0x00, 0x00}, {0xcb, 0x00, 0x00, 0x00},
+ {0xd1, 0x00, 0x00, 0x00}, {0xd8, 0x00, 0x00, 0x00},
+ {0xe0, 0x00, 0x00, 0x00}, {0xee, 0x00, 0x00, 0x01}
+ },
+ {
+ {0x01, 0x01, 0xab, 0x00}, {0x16, 0x01, 0xab, 0x01},
+ {0x01, 0x01, 0xce, 0x00}, {0x16, 0x01, 0xce, 0x01},
+ {0x01, 0x01, 0xd7, 0x00}, {0x16, 0x01, 0xd7, 0x01},
+ {0x01, 0x01, 0xe1, 0x00}, {0x16, 0x01, 0xe1, 0x01},
+ {0x01, 0x01, 0xec, 0x00}, {0x16, 0x01, 0xec, 0x01},
+ {0x01, 0x01, 0xed, 0x00}, {0x16, 0x01, 0xed, 0x01},
+ {0x00, 0x01, 0xc7, 0x01}, {0x00, 0x01, 0xcf, 0x01},
+ {0x00, 0x01, 0xea, 0x01}, {0x00, 0x01, 0xeb, 0x01}
+ },
+ {
+ {0x02, 0x01, 0xab, 0x00}, {0x09, 0x01, 0xab, 0x00},
+ {0x17, 0x01, 0xab, 0x00}, {0x28, 0x01, 0xab, 0x01},
+ {0x02, 0x01, 0xce, 0x00}, {0x09, 0x01, 0xce, 0x00},
+ {0x17, 0x01, 0xce, 0x00}, {0x28, 0x01, 0xce, 0x01},
+ {0x02, 0x01, 0xd7, 0x00}, {0x09, 0x01, 0xd7, 0x00},
+ {0x17, 0x01, 0xd7, 0x00}, {0x28, 0x01, 0xd7, 0x01},
+ {0x02, 0x01, 0xe1, 0x00}, {0x09, 0x01, 0xe1, 0x00},
+ {0x17, 0x01, 0xe1, 0x00}, {0x28, 0x01, 0xe1, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xab, 0x00}, {0x06, 0x01, 0xab, 0x00},
+ {0x0a, 0x01, 0xab, 0x00}, {0x0f, 0x01, 0xab, 0x00},
+ {0x18, 0x01, 0xab, 0x00}, {0x1f, 0x01, 0xab, 0x00},
+ {0x29, 0x01, 0xab, 0x00}, {0x38, 0x01, 0xab, 0x01},
+ {0x03, 0x01, 0xce, 0x00}, {0x06, 0x01, 0xce, 0x00},
+ {0x0a, 0x01, 0xce, 0x00}, {0x0f, 0x01, 0xce, 0x00},
+ {0x18, 0x01, 0xce, 0x00}, {0x1f, 0x01, 0xce, 0x00},
+ {0x29, 0x01, 0xce, 0x00}, {0x38, 0x01, 0xce, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xd7, 0x00}, {0x06, 0x01, 0xd7, 0x00},
+ {0x0a, 0x01, 0xd7, 0x00}, {0x0f, 0x01, 0xd7, 0x00},
+ {0x18, 0x01, 0xd7, 0x00}, {0x1f, 0x01, 0xd7, 0x00},
+ {0x29, 0x01, 0xd7, 0x00}, {0x38, 0x01, 0xd7, 0x01},
+ {0x03, 0x01, 0xe1, 0x00}, {0x06, 0x01, 0xe1, 0x00},
+ {0x0a, 0x01, 0xe1, 0x00}, {0x0f, 0x01, 0xe1, 0x00},
+ {0x18, 0x01, 0xe1, 0x00}, {0x1f, 0x01, 0xe1, 0x00},
+ {0x29, 0x01, 0xe1, 0x00}, {0x38, 0x01, 0xe1, 0x01}
+ },
+ /* 185 */
+ {
+ {0x02, 0x01, 0xec, 0x00}, {0x09, 0x01, 0xec, 0x00},
+ {0x17, 0x01, 0xec, 0x00}, {0x28, 0x01, 0xec, 0x01},
+ {0x02, 0x01, 0xed, 0x00}, {0x09, 0x01, 0xed, 0x00},
+ {0x17, 0x01, 0xed, 0x00}, {0x28, 0x01, 0xed, 0x01},
+ {0x01, 0x01, 0xc7, 0x00}, {0x16, 0x01, 0xc7, 0x01},
+ {0x01, 0x01, 0xcf, 0x00}, {0x16, 0x01, 0xcf, 0x01},
+ {0x01, 0x01, 0xea, 0x00}, {0x16, 0x01, 0xea, 0x01},
+ {0x01, 0x01, 0xeb, 0x00}, {0x16, 0x01, 0xeb, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xec, 0x00}, {0x06, 0x01, 0xec, 0x00},
+ {0x0a, 0x01, 0xec, 0x00}, {0x0f, 0x01, 0xec, 0x00},
+ {0x18, 0x01, 0xec, 0x00}, {0x1f, 0x01, 0xec, 0x00},
+ {0x29, 0x01, 0xec, 0x00}, {0x38, 0x01, 0xec, 0x01},
+ {0x03, 0x01, 0xed, 0x00}, {0x06, 0x01, 0xed, 0x00},
+ {0x0a, 0x01, 0xed, 0x00}, {0x0f, 0x01, 0xed, 0x00},
+ {0x18, 0x01, 0xed, 0x00}, {0x1f, 0x01, 0xed, 0x00},
+ {0x29, 0x01, 0xed, 0x00}, {0x38, 0x01, 0xed, 0x01}
+ },
+ {
+ {0x02, 0x01, 0xc7, 0x00}, {0x09, 0x01, 0xc7, 0x00},
+ {0x17, 0x01, 0xc7, 0x00}, {0x28, 0x01, 0xc7, 0x01},
+ {0x02, 0x01, 0xcf, 0x00}, {0x09, 0x01, 0xcf, 0x00},
+ {0x17, 0x01, 0xcf, 0x00}, {0x28, 0x01, 0xcf, 0x01},
+ {0x02, 0x01, 0xea, 0x00}, {0x09, 0x01, 0xea, 0x00},
+ {0x17, 0x01, 0xea, 0x00}, {0x28, 0x01, 0xea, 0x01},
+ {0x02, 0x01, 0xeb, 0x00}, {0x09, 0x01, 0xeb, 0x00},
+ {0x17, 0x01, 0xeb, 0x00}, {0x28, 0x01, 0xeb, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xc7, 0x00}, {0x06, 0x01, 0xc7, 0x00},
+ {0x0a, 0x01, 0xc7, 0x00}, {0x0f, 0x01, 0xc7, 0x00},
+ {0x18, 0x01, 0xc7, 0x00}, {0x1f, 0x01, 0xc7, 0x00},
+ {0x29, 0x01, 0xc7, 0x00}, {0x38, 0x01, 0xc7, 0x01},
+ {0x03, 0x01, 0xcf, 0x00}, {0x06, 0x01, 0xcf, 0x00},
+ {0x0a, 0x01, 0xcf, 0x00}, {0x0f, 0x01, 0xcf, 0x00},
+ {0x18, 0x01, 0xcf, 0x00}, {0x1f, 0x01, 0xcf, 0x00},
+ {0x29, 0x01, 0xcf, 0x00}, {0x38, 0x01, 0xcf, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xea, 0x00}, {0x06, 0x01, 0xea, 0x00},
+ {0x0a, 0x01, 0xea, 0x00}, {0x0f, 0x01, 0xea, 0x00},
+ {0x18, 0x01, 0xea, 0x00}, {0x1f, 0x01, 0xea, 0x00},
+ {0x29, 0x01, 0xea, 0x00}, {0x38, 0x01, 0xea, 0x01},
+ {0x03, 0x01, 0xeb, 0x00}, {0x06, 0x01, 0xeb, 0x00},
+ {0x0a, 0x01, 0xeb, 0x00}, {0x0f, 0x01, 0xeb, 0x00},
+ {0x18, 0x01, 0xeb, 0x00}, {0x1f, 0x01, 0xeb, 0x00},
+ {0x29, 0x01, 0xeb, 0x00}, {0x38, 0x01, 0xeb, 0x01}
+ },
+ /* 190 */
+ {
+ {0xc2, 0x00, 0x00, 0x00}, {0xc3, 0x00, 0x00, 0x00},
+ {0xc5, 0x00, 0x00, 0x00}, {0xc6, 0x00, 0x00, 0x00},
+ {0xc9, 0x00, 0x00, 0x00}, {0xca, 0x00, 0x00, 0x00},
+ {0xcc, 0x00, 0x00, 0x00}, {0xcd, 0x00, 0x00, 0x00},
+ {0xd2, 0x00, 0x00, 0x00}, {0xd5, 0x00, 0x00, 0x00},
+ {0xd9, 0x00, 0x00, 0x00}, {0xdc, 0x00, 0x00, 0x00},
+ {0xe1, 0x00, 0x00, 0x00}, {0xe7, 0x00, 0x00, 0x00},
+ {0xef, 0x00, 0x00, 0x00}, {0xf6, 0x00, 0x00, 0x01}
+ },
+ {
+ {0x00, 0x01, 0xc0, 0x01}, {0x00, 0x01, 0xc1, 0x01},
+ {0x00, 0x01, 0xc8, 0x01}, {0x00, 0x01, 0xc9, 0x01},
+ {0x00, 0x01, 0xca, 0x01}, {0x00, 0x01, 0xcd, 0x01},
+ {0x00, 0x01, 0xd2, 0x01}, {0x00, 0x01, 0xd5, 0x01},
+ {0x00, 0x01, 0xda, 0x01}, {0x00, 0x01, 0xdb, 0x01},
+ {0x00, 0x01, 0xee, 0x01}, {0x00, 0x01, 0xf0, 0x01},
+ {0x00, 0x01, 0xf2, 0x01}, {0x00, 0x01, 0xf3, 0x01},
+ {0x00, 0x01, 0xff, 0x01}, {0xce, 0x00, 0x00, 0x00}
+ },
+ {
+ {0x01, 0x01, 0xc0, 0x00}, {0x16, 0x01, 0xc0, 0x01},
+ {0x01, 0x01, 0xc1, 0x00}, {0x16, 0x01, 0xc1, 0x01},
+ {0x01, 0x01, 0xc8, 0x00}, {0x16, 0x01, 0xc8, 0x01},
+ {0x01, 0x01, 0xc9, 0x00}, {0x16, 0x01, 0xc9, 0x01},
+ {0x01, 0x01, 0xca, 0x00}, {0x16, 0x01, 0xca, 0x01},
+ {0x01, 0x01, 0xcd, 0x00}, {0x16, 0x01, 0xcd, 0x01},
+ {0x01, 0x01, 0xd2, 0x00}, {0x16, 0x01, 0xd2, 0x01},
+ {0x01, 0x01, 0xd5, 0x00}, {0x16, 0x01, 0xd5, 0x01}
+ },
+ {
+ {0x02, 0x01, 0xc0, 0x00}, {0x09, 0x01, 0xc0, 0x00},
+ {0x17, 0x01, 0xc0, 0x00}, {0x28, 0x01, 0xc0, 0x01},
+ {0x02, 0x01, 0xc1, 0x00}, {0x09, 0x01, 0xc1, 0x00},
+ {0x17, 0x01, 0xc1, 0x00}, {0x28, 0x01, 0xc1, 0x01},
+ {0x02, 0x01, 0xc8, 0x00}, {0x09, 0x01, 0xc8, 0x00},
+ {0x17, 0x01, 0xc8, 0x00}, {0x28, 0x01, 0xc8, 0x01},
+ {0x02, 0x01, 0xc9, 0x00}, {0x09, 0x01, 0xc9, 0x00},
+ {0x17, 0x01, 0xc9, 0x00}, {0x28, 0x01, 0xc9, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xc0, 0x00}, {0x06, 0x01, 0xc0, 0x00},
+ {0x0a, 0x01, 0xc0, 0x00}, {0x0f, 0x01, 0xc0, 0x00},
+ {0x18, 0x01, 0xc0, 0x00}, {0x1f, 0x01, 0xc0, 0x00},
+ {0x29, 0x01, 0xc0, 0x00}, {0x38, 0x01, 0xc0, 0x01},
+ {0x03, 0x01, 0xc1, 0x00}, {0x06, 0x01, 0xc1, 0x00},
+ {0x0a, 0x01, 0xc1, 0x00}, {0x0f, 0x01, 0xc1, 0x00},
+ {0x18, 0x01, 0xc1, 0x00}, {0x1f, 0x01, 0xc1, 0x00},
+ {0x29, 0x01, 0xc1, 0x00}, {0x38, 0x01, 0xc1, 0x01}
+ },
+ /* 195 */
+ {
+ {0x03, 0x01, 0xc8, 0x00}, {0x06, 0x01, 0xc8, 0x00},
+ {0x0a, 0x01, 0xc8, 0x00}, {0x0f, 0x01, 0xc8, 0x00},
+ {0x18, 0x01, 0xc8, 0x00}, {0x1f, 0x01, 0xc8, 0x00},
+ {0x29, 0x01, 0xc8, 0x00}, {0x38, 0x01, 0xc8, 0x01},
+ {0x03, 0x01, 0xc9, 0x00}, {0x06, 0x01, 0xc9, 0x00},
+ {0x0a, 0x01, 0xc9, 0x00}, {0x0f, 0x01, 0xc9, 0x00},
+ {0x18, 0x01, 0xc9, 0x00}, {0x1f, 0x01, 0xc9, 0x00},
+ {0x29, 0x01, 0xc9, 0x00}, {0x38, 0x01, 0xc9, 0x01}
+ },
+ {
+ {0x02, 0x01, 0xca, 0x00}, {0x09, 0x01, 0xca, 0x00},
+ {0x17, 0x01, 0xca, 0x00}, {0x28, 0x01, 0xca, 0x01},
+ {0x02, 0x01, 0xcd, 0x00}, {0x09, 0x01, 0xcd, 0x00},
+ {0x17, 0x01, 0xcd, 0x00}, {0x28, 0x01, 0xcd, 0x01},
+ {0x02, 0x01, 0xd2, 0x00}, {0x09, 0x01, 0xd2, 0x00},
+ {0x17, 0x01, 0xd2, 0x00}, {0x28, 0x01, 0xd2, 0x01},
+ {0x02, 0x01, 0xd5, 0x00}, {0x09, 0x01, 0xd5, 0x00},
+ {0x17, 0x01, 0xd5, 0x00}, {0x28, 0x01, 0xd5, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xca, 0x00}, {0x06, 0x01, 0xca, 0x00},
+ {0x0a, 0x01, 0xca, 0x00}, {0x0f, 0x01, 0xca, 0x00},
+ {0x18, 0x01, 0xca, 0x00}, {0x1f, 0x01, 0xca, 0x00},
+ {0x29, 0x01, 0xca, 0x00}, {0x38, 0x01, 0xca, 0x01},
+ {0x03, 0x01, 0xcd, 0x00}, {0x06, 0x01, 0xcd, 0x00},
+ {0x0a, 0x01, 0xcd, 0x00}, {0x0f, 0x01, 0xcd, 0x00},
+ {0x18, 0x01, 0xcd, 0x00}, {0x1f, 0x01, 0xcd, 0x00},
+ {0x29, 0x01, 0xcd, 0x00}, {0x38, 0x01, 0xcd, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xd2, 0x00}, {0x06, 0x01, 0xd2, 0x00},
+ {0x0a, 0x01, 0xd2, 0x00}, {0x0f, 0x01, 0xd2, 0x00},
+ {0x18, 0x01, 0xd2, 0x00}, {0x1f, 0x01, 0xd2, 0x00},
+ {0x29, 0x01, 0xd2, 0x00}, {0x38, 0x01, 0xd2, 0x01},
+ {0x03, 0x01, 0xd5, 0x00}, {0x06, 0x01, 0xd5, 0x00},
+ {0x0a, 0x01, 0xd5, 0x00}, {0x0f, 0x01, 0xd5, 0x00},
+ {0x18, 0x01, 0xd5, 0x00}, {0x1f, 0x01, 0xd5, 0x00},
+ {0x29, 0x01, 0xd5, 0x00}, {0x38, 0x01, 0xd5, 0x01}
+ },
+ {
+ {0x01, 0x01, 0xda, 0x00}, {0x16, 0x01, 0xda, 0x01},
+ {0x01, 0x01, 0xdb, 0x00}, {0x16, 0x01, 0xdb, 0x01},
+ {0x01, 0x01, 0xee, 0x00}, {0x16, 0x01, 0xee, 0x01},
+ {0x01, 0x01, 0xf0, 0x00}, {0x16, 0x01, 0xf0, 0x01},
+ {0x01, 0x01, 0xf2, 0x00}, {0x16, 0x01, 0xf2, 0x01},
+ {0x01, 0x01, 0xf3, 0x00}, {0x16, 0x01, 0xf3, 0x01},
+ {0x01, 0x01, 0xff, 0x00}, {0x16, 0x01, 0xff, 0x01},
+ {0x00, 0x01, 0xcb, 0x01}, {0x00, 0x01, 0xcc, 0x01}
+ },
+ /* 200 */
+ {
+ {0x02, 0x01, 0xda, 0x00}, {0x09, 0x01, 0xda, 0x00},
+ {0x17, 0x01, 0xda, 0x00}, {0x28, 0x01, 0xda, 0x01},
+ {0x02, 0x01, 0xdb, 0x00}, {0x09, 0x01, 0xdb, 0x00},
+ {0x17, 0x01, 0xdb, 0x00}, {0x28, 0x01, 0xdb, 0x01},
+ {0x02, 0x01, 0xee, 0x00}, {0x09, 0x01, 0xee, 0x00},
+ {0x17, 0x01, 0xee, 0x00}, {0x28, 0x01, 0xee, 0x01},
+ {0x02, 0x01, 0xf0, 0x00}, {0x09, 0x01, 0xf0, 0x00},
+ {0x17, 0x01, 0xf0, 0x00}, {0x28, 0x01, 0xf0, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xda, 0x00}, {0x06, 0x01, 0xda, 0x00},
+ {0x0a, 0x01, 0xda, 0x00}, {0x0f, 0x01, 0xda, 0x00},
+ {0x18, 0x01, 0xda, 0x00}, {0x1f, 0x01, 0xda, 0x00},
+ {0x29, 0x01, 0xda, 0x00}, {0x38, 0x01, 0xda, 0x01},
+ {0x03, 0x01, 0xdb, 0x00}, {0x06, 0x01, 0xdb, 0x00},
+ {0x0a, 0x01, 0xdb, 0x00}, {0x0f, 0x01, 0xdb, 0x00},
+ {0x18, 0x01, 0xdb, 0x00}, {0x1f, 0x01, 0xdb, 0x00},
+ {0x29, 0x01, 0xdb, 0x00}, {0x38, 0x01, 0xdb, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xee, 0x00}, {0x06, 0x01, 0xee, 0x00},
+ {0x0a, 0x01, 0xee, 0x00}, {0x0f, 0x01, 0xee, 0x00},
+ {0x18, 0x01, 0xee, 0x00}, {0x1f, 0x01, 0xee, 0x00},
+ {0x29, 0x01, 0xee, 0x00}, {0x38, 0x01, 0xee, 0x01},
+ {0x03, 0x01, 0xf0, 0x00}, {0x06, 0x01, 0xf0, 0x00},
+ {0x0a, 0x01, 0xf0, 0x00}, {0x0f, 0x01, 0xf0, 0x00},
+ {0x18, 0x01, 0xf0, 0x00}, {0x1f, 0x01, 0xf0, 0x00},
+ {0x29, 0x01, 0xf0, 0x00}, {0x38, 0x01, 0xf0, 0x01}
+ },
+ {
+ {0x02, 0x01, 0xf2, 0x00}, {0x09, 0x01, 0xf2, 0x00},
+ {0x17, 0x01, 0xf2, 0x00}, {0x28, 0x01, 0xf2, 0x01},
+ {0x02, 0x01, 0xf3, 0x00}, {0x09, 0x01, 0xf3, 0x00},
+ {0x17, 0x01, 0xf3, 0x00}, {0x28, 0x01, 0xf3, 0x01},
+ {0x02, 0x01, 0xff, 0x00}, {0x09, 0x01, 0xff, 0x00},
+ {0x17, 0x01, 0xff, 0x00}, {0x28, 0x01, 0xff, 0x01},
+ {0x01, 0x01, 0xcb, 0x00}, {0x16, 0x01, 0xcb, 0x01},
+ {0x01, 0x01, 0xcc, 0x00}, {0x16, 0x01, 0xcc, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xf2, 0x00}, {0x06, 0x01, 0xf2, 0x00},
+ {0x0a, 0x01, 0xf2, 0x00}, {0x0f, 0x01, 0xf2, 0x00},
+ {0x18, 0x01, 0xf2, 0x00}, {0x1f, 0x01, 0xf2, 0x00},
+ {0x29, 0x01, 0xf2, 0x00}, {0x38, 0x01, 0xf2, 0x01},
+ {0x03, 0x01, 0xf3, 0x00}, {0x06, 0x01, 0xf3, 0x00},
+ {0x0a, 0x01, 0xf3, 0x00}, {0x0f, 0x01, 0xf3, 0x00},
+ {0x18, 0x01, 0xf3, 0x00}, {0x1f, 0x01, 0xf3, 0x00},
+ {0x29, 0x01, 0xf3, 0x00}, {0x38, 0x01, 0xf3, 0x01}
+ },
+ /* 205 */
+ {
+ {0x03, 0x01, 0xff, 0x00}, {0x06, 0x01, 0xff, 0x00},
+ {0x0a, 0x01, 0xff, 0x00}, {0x0f, 0x01, 0xff, 0x00},
+ {0x18, 0x01, 0xff, 0x00}, {0x1f, 0x01, 0xff, 0x00},
+ {0x29, 0x01, 0xff, 0x00}, {0x38, 0x01, 0xff, 0x01},
+ {0x02, 0x01, 0xcb, 0x00}, {0x09, 0x01, 0xcb, 0x00},
+ {0x17, 0x01, 0xcb, 0x00}, {0x28, 0x01, 0xcb, 0x01},
+ {0x02, 0x01, 0xcc, 0x00}, {0x09, 0x01, 0xcc, 0x00},
+ {0x17, 0x01, 0xcc, 0x00}, {0x28, 0x01, 0xcc, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xcb, 0x00}, {0x06, 0x01, 0xcb, 0x00},
+ {0x0a, 0x01, 0xcb, 0x00}, {0x0f, 0x01, 0xcb, 0x00},
+ {0x18, 0x01, 0xcb, 0x00}, {0x1f, 0x01, 0xcb, 0x00},
+ {0x29, 0x01, 0xcb, 0x00}, {0x38, 0x01, 0xcb, 0x01},
+ {0x03, 0x01, 0xcc, 0x00}, {0x06, 0x01, 0xcc, 0x00},
+ {0x0a, 0x01, 0xcc, 0x00}, {0x0f, 0x01, 0xcc, 0x00},
+ {0x18, 0x01, 0xcc, 0x00}, {0x1f, 0x01, 0xcc, 0x00},
+ {0x29, 0x01, 0xcc, 0x00}, {0x38, 0x01, 0xcc, 0x01}
+ },
+ {
+ {0xd3, 0x00, 0x00, 0x00}, {0xd4, 0x00, 0x00, 0x00},
+ {0xd6, 0x00, 0x00, 0x00}, {0xd7, 0x00, 0x00, 0x00},
+ {0xda, 0x00, 0x00, 0x00}, {0xdb, 0x00, 0x00, 0x00},
+ {0xdd, 0x00, 0x00, 0x00}, {0xde, 0x00, 0x00, 0x00},
+ {0xe2, 0x00, 0x00, 0x00}, {0xe4, 0x00, 0x00, 0x00},
+ {0xe8, 0x00, 0x00, 0x00}, {0xeb, 0x00, 0x00, 0x00},
+ {0xf0, 0x00, 0x00, 0x00}, {0xf3, 0x00, 0x00, 0x00},
+ {0xf7, 0x00, 0x00, 0x00}, {0xfa, 0x00, 0x00, 0x01}
+ },
+ {
+ {0x00, 0x01, 0xd3, 0x01}, {0x00, 0x01, 0xd4, 0x01},
+ {0x00, 0x01, 0xd6, 0x01}, {0x00, 0x01, 0xdd, 0x01},
+ {0x00, 0x01, 0xde, 0x01}, {0x00, 0x01, 0xdf, 0x01},
+ {0x00, 0x01, 0xf1, 0x01}, {0x00, 0x01, 0xf4, 0x01},
+ {0x00, 0x01, 0xf5, 0x01}, {0x00, 0x01, 0xf6, 0x01},
+ {0x00, 0x01, 0xf7, 0x01}, {0x00, 0x01, 0xf8, 0x01},
+ {0x00, 0x01, 0xfa, 0x01}, {0x00, 0x01, 0xfb, 0x01},
+ {0x00, 0x01, 0xfc, 0x01}, {0x00, 0x01, 0xfd, 0x01}
+ },
+ {
+ {0x01, 0x01, 0xd3, 0x00}, {0x16, 0x01, 0xd3, 0x01},
+ {0x01, 0x01, 0xd4, 0x00}, {0x16, 0x01, 0xd4, 0x01},
+ {0x01, 0x01, 0xd6, 0x00}, {0x16, 0x01, 0xd6, 0x01},
+ {0x01, 0x01, 0xdd, 0x00}, {0x16, 0x01, 0xdd, 0x01},
+ {0x01, 0x01, 0xde, 0x00}, {0x16, 0x01, 0xde, 0x01},
+ {0x01, 0x01, 0xdf, 0x00}, {0x16, 0x01, 0xdf, 0x01},
+ {0x01, 0x01, 0xf1, 0x00}, {0x16, 0x01, 0xf1, 0x01},
+ {0x01, 0x01, 0xf4, 0x00}, {0x16, 0x01, 0xf4, 0x01}
+ },
+ /* 210 */
+ {
+ {0x02, 0x01, 0xd3, 0x00}, {0x09, 0x01, 0xd3, 0x00},
+ {0x17, 0x01, 0xd3, 0x00}, {0x28, 0x01, 0xd3, 0x01},
+ {0x02, 0x01, 0xd4, 0x00}, {0x09, 0x01, 0xd4, 0x00},
+ {0x17, 0x01, 0xd4, 0x00}, {0x28, 0x01, 0xd4, 0x01},
+ {0x02, 0x01, 0xd6, 0x00}, {0x09, 0x01, 0xd6, 0x00},
+ {0x17, 0x01, 0xd6, 0x00}, {0x28, 0x01, 0xd6, 0x01},
+ {0x02, 0x01, 0xdd, 0x00}, {0x09, 0x01, 0xdd, 0x00},
+ {0x17, 0x01, 0xdd, 0x00}, {0x28, 0x01, 0xdd, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xd3, 0x00}, {0x06, 0x01, 0xd3, 0x00},
+ {0x0a, 0x01, 0xd3, 0x00}, {0x0f, 0x01, 0xd3, 0x00},
+ {0x18, 0x01, 0xd3, 0x00}, {0x1f, 0x01, 0xd3, 0x00},
+ {0x29, 0x01, 0xd3, 0x00}, {0x38, 0x01, 0xd3, 0x01},
+ {0x03, 0x01, 0xd4, 0x00}, {0x06, 0x01, 0xd4, 0x00},
+ {0x0a, 0x01, 0xd4, 0x00}, {0x0f, 0x01, 0xd4, 0x00},
+ {0x18, 0x01, 0xd4, 0x00}, {0x1f, 0x01, 0xd4, 0x00},
+ {0x29, 0x01, 0xd4, 0x00}, {0x38, 0x01, 0xd4, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xd6, 0x00}, {0x06, 0x01, 0xd6, 0x00},
+ {0x0a, 0x01, 0xd6, 0x00}, {0x0f, 0x01, 0xd6, 0x00},
+ {0x18, 0x01, 0xd6, 0x00}, {0x1f, 0x01, 0xd6, 0x00},
+ {0x29, 0x01, 0xd6, 0x00}, {0x38, 0x01, 0xd6, 0x01},
+ {0x03, 0x01, 0xdd, 0x00}, {0x06, 0x01, 0xdd, 0x00},
+ {0x0a, 0x01, 0xdd, 0x00}, {0x0f, 0x01, 0xdd, 0x00},
+ {0x18, 0x01, 0xdd, 0x00}, {0x1f, 0x01, 0xdd, 0x00},
+ {0x29, 0x01, 0xdd, 0x00}, {0x38, 0x01, 0xdd, 0x01}
+ },
+ {
+ {0x02, 0x01, 0xde, 0x00}, {0x09, 0x01, 0xde, 0x00},
+ {0x17, 0x01, 0xde, 0x00}, {0x28, 0x01, 0xde, 0x01},
+ {0x02, 0x01, 0xdf, 0x00}, {0x09, 0x01, 0xdf, 0x00},
+ {0x17, 0x01, 0xdf, 0x00}, {0x28, 0x01, 0xdf, 0x01},
+ {0x02, 0x01, 0xf1, 0x00}, {0x09, 0x01, 0xf1, 0x00},
+ {0x17, 0x01, 0xf1, 0x00}, {0x28, 0x01, 0xf1, 0x01},
+ {0x02, 0x01, 0xf4, 0x00}, {0x09, 0x01, 0xf4, 0x00},
+ {0x17, 0x01, 0xf4, 0x00}, {0x28, 0x01, 0xf4, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xde, 0x00}, {0x06, 0x01, 0xde, 0x00},
+ {0x0a, 0x01, 0xde, 0x00}, {0x0f, 0x01, 0xde, 0x00},
+ {0x18, 0x01, 0xde, 0x00}, {0x1f, 0x01, 0xde, 0x00},
+ {0x29, 0x01, 0xde, 0x00}, {0x38, 0x01, 0xde, 0x01},
+ {0x03, 0x01, 0xdf, 0x00}, {0x06, 0x01, 0xdf, 0x00},
+ {0x0a, 0x01, 0xdf, 0x00}, {0x0f, 0x01, 0xdf, 0x00},
+ {0x18, 0x01, 0xdf, 0x00}, {0x1f, 0x01, 0xdf, 0x00},
+ {0x29, 0x01, 0xdf, 0x00}, {0x38, 0x01, 0xdf, 0x01}
+ },
+ /* 215 */
+ {
+ {0x03, 0x01, 0xf1, 0x00}, {0x06, 0x01, 0xf1, 0x00},
+ {0x0a, 0x01, 0xf1, 0x00}, {0x0f, 0x01, 0xf1, 0x00},
+ {0x18, 0x01, 0xf1, 0x00}, {0x1f, 0x01, 0xf1, 0x00},
+ {0x29, 0x01, 0xf1, 0x00}, {0x38, 0x01, 0xf1, 0x01},
+ {0x03, 0x01, 0xf4, 0x00}, {0x06, 0x01, 0xf4, 0x00},
+ {0x0a, 0x01, 0xf4, 0x00}, {0x0f, 0x01, 0xf4, 0x00},
+ {0x18, 0x01, 0xf4, 0x00}, {0x1f, 0x01, 0xf4, 0x00},
+ {0x29, 0x01, 0xf4, 0x00}, {0x38, 0x01, 0xf4, 0x01}
+ },
+ {
+ {0x01, 0x01, 0xf5, 0x00}, {0x16, 0x01, 0xf5, 0x01},
+ {0x01, 0x01, 0xf6, 0x00}, {0x16, 0x01, 0xf6, 0x01},
+ {0x01, 0x01, 0xf7, 0x00}, {0x16, 0x01, 0xf7, 0x01},
+ {0x01, 0x01, 0xf8, 0x00}, {0x16, 0x01, 0xf8, 0x01},
+ {0x01, 0x01, 0xfa, 0x00}, {0x16, 0x01, 0xfa, 0x01},
+ {0x01, 0x01, 0xfb, 0x00}, {0x16, 0x01, 0xfb, 0x01},
+ {0x01, 0x01, 0xfc, 0x00}, {0x16, 0x01, 0xfc, 0x01},
+ {0x01, 0x01, 0xfd, 0x00}, {0x16, 0x01, 0xfd, 0x01}
+ },
+ {
+ {0x02, 0x01, 0xf5, 0x00}, {0x09, 0x01, 0xf5, 0x00},
+ {0x17, 0x01, 0xf5, 0x00}, {0x28, 0x01, 0xf5, 0x01},
+ {0x02, 0x01, 0xf6, 0x00}, {0x09, 0x01, 0xf6, 0x00},
+ {0x17, 0x01, 0xf6, 0x00}, {0x28, 0x01, 0xf6, 0x01},
+ {0x02, 0x01, 0xf7, 0x00}, {0x09, 0x01, 0xf7, 0x00},
+ {0x17, 0x01, 0xf7, 0x00}, {0x28, 0x01, 0xf7, 0x01},
+ {0x02, 0x01, 0xf8, 0x00}, {0x09, 0x01, 0xf8, 0x00},
+ {0x17, 0x01, 0xf8, 0x00}, {0x28, 0x01, 0xf8, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xf5, 0x00}, {0x06, 0x01, 0xf5, 0x00},
+ {0x0a, 0x01, 0xf5, 0x00}, {0x0f, 0x01, 0xf5, 0x00},
+ {0x18, 0x01, 0xf5, 0x00}, {0x1f, 0x01, 0xf5, 0x00},
+ {0x29, 0x01, 0xf5, 0x00}, {0x38, 0x01, 0xf5, 0x01},
+ {0x03, 0x01, 0xf6, 0x00}, {0x06, 0x01, 0xf6, 0x00},
+ {0x0a, 0x01, 0xf6, 0x00}, {0x0f, 0x01, 0xf6, 0x00},
+ {0x18, 0x01, 0xf6, 0x00}, {0x1f, 0x01, 0xf6, 0x00},
+ {0x29, 0x01, 0xf6, 0x00}, {0x38, 0x01, 0xf6, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xf7, 0x00}, {0x06, 0x01, 0xf7, 0x00},
+ {0x0a, 0x01, 0xf7, 0x00}, {0x0f, 0x01, 0xf7, 0x00},
+ {0x18, 0x01, 0xf7, 0x00}, {0x1f, 0x01, 0xf7, 0x00},
+ {0x29, 0x01, 0xf7, 0x00}, {0x38, 0x01, 0xf7, 0x01},
+ {0x03, 0x01, 0xf8, 0x00}, {0x06, 0x01, 0xf8, 0x00},
+ {0x0a, 0x01, 0xf8, 0x00}, {0x0f, 0x01, 0xf8, 0x00},
+ {0x18, 0x01, 0xf8, 0x00}, {0x1f, 0x01, 0xf8, 0x00},
+ {0x29, 0x01, 0xf8, 0x00}, {0x38, 0x01, 0xf8, 0x01}
+ },
+ /* 220 */
+ {
+ {0x02, 0x01, 0xfa, 0x00}, {0x09, 0x01, 0xfa, 0x00},
+ {0x17, 0x01, 0xfa, 0x00}, {0x28, 0x01, 0xfa, 0x01},
+ {0x02, 0x01, 0xfb, 0x00}, {0x09, 0x01, 0xfb, 0x00},
+ {0x17, 0x01, 0xfb, 0x00}, {0x28, 0x01, 0xfb, 0x01},
+ {0x02, 0x01, 0xfc, 0x00}, {0x09, 0x01, 0xfc, 0x00},
+ {0x17, 0x01, 0xfc, 0x00}, {0x28, 0x01, 0xfc, 0x01},
+ {0x02, 0x01, 0xfd, 0x00}, {0x09, 0x01, 0xfd, 0x00},
+ {0x17, 0x01, 0xfd, 0x00}, {0x28, 0x01, 0xfd, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xfa, 0x00}, {0x06, 0x01, 0xfa, 0x00},
+ {0x0a, 0x01, 0xfa, 0x00}, {0x0f, 0x01, 0xfa, 0x00},
+ {0x18, 0x01, 0xfa, 0x00}, {0x1f, 0x01, 0xfa, 0x00},
+ {0x29, 0x01, 0xfa, 0x00}, {0x38, 0x01, 0xfa, 0x01},
+ {0x03, 0x01, 0xfb, 0x00}, {0x06, 0x01, 0xfb, 0x00},
+ {0x0a, 0x01, 0xfb, 0x00}, {0x0f, 0x01, 0xfb, 0x00},
+ {0x18, 0x01, 0xfb, 0x00}, {0x1f, 0x01, 0xfb, 0x00},
+ {0x29, 0x01, 0xfb, 0x00}, {0x38, 0x01, 0xfb, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xfc, 0x00}, {0x06, 0x01, 0xfc, 0x00},
+ {0x0a, 0x01, 0xfc, 0x00}, {0x0f, 0x01, 0xfc, 0x00},
+ {0x18, 0x01, 0xfc, 0x00}, {0x1f, 0x01, 0xfc, 0x00},
+ {0x29, 0x01, 0xfc, 0x00}, {0x38, 0x01, 0xfc, 0x01},
+ {0x03, 0x01, 0xfd, 0x00}, {0x06, 0x01, 0xfd, 0x00},
+ {0x0a, 0x01, 0xfd, 0x00}, {0x0f, 0x01, 0xfd, 0x00},
+ {0x18, 0x01, 0xfd, 0x00}, {0x1f, 0x01, 0xfd, 0x00},
+ {0x29, 0x01, 0xfd, 0x00}, {0x38, 0x01, 0xfd, 0x01}
+ },
+ {
+ {0x00, 0x01, 0xfe, 0x01}, {0xe3, 0x00, 0x00, 0x00},
+ {0xe5, 0x00, 0x00, 0x00}, {0xe6, 0x00, 0x00, 0x00},
+ {0xe9, 0x00, 0x00, 0x00}, {0xea, 0x00, 0x00, 0x00},
+ {0xec, 0x00, 0x00, 0x00}, {0xed, 0x00, 0x00, 0x00},
+ {0xf1, 0x00, 0x00, 0x00}, {0xf2, 0x00, 0x00, 0x00},
+ {0xf4, 0x00, 0x00, 0x00}, {0xf5, 0x00, 0x00, 0x00},
+ {0xf8, 0x00, 0x00, 0x00}, {0xf9, 0x00, 0x00, 0x00},
+ {0xfb, 0x00, 0x00, 0x00}, {0xfc, 0x00, 0x00, 0x01}
+ },
+ {
+ {0x01, 0x01, 0xfe, 0x00}, {0x16, 0x01, 0xfe, 0x01},
+ {0x00, 0x01, 0x02, 0x01}, {0x00, 0x01, 0x03, 0x01},
+ {0x00, 0x01, 0x04, 0x01}, {0x00, 0x01, 0x05, 0x01},
+ {0x00, 0x01, 0x06, 0x01}, {0x00, 0x01, 0x07, 0x01},
+ {0x00, 0x01, 0x08, 0x01}, {0x00, 0x01, 0x0b, 0x01},
+ {0x00, 0x01, 0x0c, 0x01}, {0x00, 0x01, 0x0e, 0x01},
+ {0x00, 0x01, 0x0f, 0x01}, {0x00, 0x01, 0x10, 0x01},
+ {0x00, 0x01, 0x11, 0x01}, {0x00, 0x01, 0x12, 0x01}
+ },
+ /* 225 */
+ {
+ {0x02, 0x01, 0xfe, 0x00}, {0x09, 0x01, 0xfe, 0x00},
+ {0x17, 0x01, 0xfe, 0x00}, {0x28, 0x01, 0xfe, 0x01},
+ {0x01, 0x01, 0x02, 0x00}, {0x16, 0x01, 0x02, 0x01},
+ {0x01, 0x01, 0x03, 0x00}, {0x16, 0x01, 0x03, 0x01},
+ {0x01, 0x01, 0x04, 0x00}, {0x16, 0x01, 0x04, 0x01},
+ {0x01, 0x01, 0x05, 0x00}, {0x16, 0x01, 0x05, 0x01},
+ {0x01, 0x01, 0x06, 0x00}, {0x16, 0x01, 0x06, 0x01},
+ {0x01, 0x01, 0x07, 0x00}, {0x16, 0x01, 0x07, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xfe, 0x00}, {0x06, 0x01, 0xfe, 0x00},
+ {0x0a, 0x01, 0xfe, 0x00}, {0x0f, 0x01, 0xfe, 0x00},
+ {0x18, 0x01, 0xfe, 0x00}, {0x1f, 0x01, 0xfe, 0x00},
+ {0x29, 0x01, 0xfe, 0x00}, {0x38, 0x01, 0xfe, 0x01},
+ {0x02, 0x01, 0x02, 0x00}, {0x09, 0x01, 0x02, 0x00},
+ {0x17, 0x01, 0x02, 0x00}, {0x28, 0x01, 0x02, 0x01},
+ {0x02, 0x01, 0x03, 0x00}, {0x09, 0x01, 0x03, 0x00},
+ {0x17, 0x01, 0x03, 0x00}, {0x28, 0x01, 0x03, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x02, 0x00}, {0x06, 0x01, 0x02, 0x00},
+ {0x0a, 0x01, 0x02, 0x00}, {0x0f, 0x01, 0x02, 0x00},
+ {0x18, 0x01, 0x02, 0x00}, {0x1f, 0x01, 0x02, 0x00},
+ {0x29, 0x01, 0x02, 0x00}, {0x38, 0x01, 0x02, 0x01},
+ {0x03, 0x01, 0x03, 0x00}, {0x06, 0x01, 0x03, 0x00},
+ {0x0a, 0x01, 0x03, 0x00}, {0x0f, 0x01, 0x03, 0x00},
+ {0x18, 0x01, 0x03, 0x00}, {0x1f, 0x01, 0x03, 0x00},
+ {0x29, 0x01, 0x03, 0x00}, {0x38, 0x01, 0x03, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x04, 0x00}, {0x09, 0x01, 0x04, 0x00},
+ {0x17, 0x01, 0x04, 0x00}, {0x28, 0x01, 0x04, 0x01},
+ {0x02, 0x01, 0x05, 0x00}, {0x09, 0x01, 0x05, 0x00},
+ {0x17, 0x01, 0x05, 0x00}, {0x28, 0x01, 0x05, 0x01},
+ {0x02, 0x01, 0x06, 0x00}, {0x09, 0x01, 0x06, 0x00},
+ {0x17, 0x01, 0x06, 0x00}, {0x28, 0x01, 0x06, 0x01},
+ {0x02, 0x01, 0x07, 0x00}, {0x09, 0x01, 0x07, 0x00},
+ {0x17, 0x01, 0x07, 0x00}, {0x28, 0x01, 0x07, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x04, 0x00}, {0x06, 0x01, 0x04, 0x00},
+ {0x0a, 0x01, 0x04, 0x00}, {0x0f, 0x01, 0x04, 0x00},
+ {0x18, 0x01, 0x04, 0x00}, {0x1f, 0x01, 0x04, 0x00},
+ {0x29, 0x01, 0x04, 0x00}, {0x38, 0x01, 0x04, 0x01},
+ {0x03, 0x01, 0x05, 0x00}, {0x06, 0x01, 0x05, 0x00},
+ {0x0a, 0x01, 0x05, 0x00}, {0x0f, 0x01, 0x05, 0x00},
+ {0x18, 0x01, 0x05, 0x00}, {0x1f, 0x01, 0x05, 0x00},
+ {0x29, 0x01, 0x05, 0x00}, {0x38, 0x01, 0x05, 0x01}
+ },
+ /* 230 */
+ {
+ {0x03, 0x01, 0x06, 0x00}, {0x06, 0x01, 0x06, 0x00},
+ {0x0a, 0x01, 0x06, 0x00}, {0x0f, 0x01, 0x06, 0x00},
+ {0x18, 0x01, 0x06, 0x00}, {0x1f, 0x01, 0x06, 0x00},
+ {0x29, 0x01, 0x06, 0x00}, {0x38, 0x01, 0x06, 0x01},
+ {0x03, 0x01, 0x07, 0x00}, {0x06, 0x01, 0x07, 0x00},
+ {0x0a, 0x01, 0x07, 0x00}, {0x0f, 0x01, 0x07, 0x00},
+ {0x18, 0x01, 0x07, 0x00}, {0x1f, 0x01, 0x07, 0x00},
+ {0x29, 0x01, 0x07, 0x00}, {0x38, 0x01, 0x07, 0x01}
+ },
+ {
+ {0x01, 0x01, 0x08, 0x00}, {0x16, 0x01, 0x08, 0x01},
+ {0x01, 0x01, 0x0b, 0x00}, {0x16, 0x01, 0x0b, 0x01},
+ {0x01, 0x01, 0x0c, 0x00}, {0x16, 0x01, 0x0c, 0x01},
+ {0x01, 0x01, 0x0e, 0x00}, {0x16, 0x01, 0x0e, 0x01},
+ {0x01, 0x01, 0x0f, 0x00}, {0x16, 0x01, 0x0f, 0x01},
+ {0x01, 0x01, 0x10, 0x00}, {0x16, 0x01, 0x10, 0x01},
+ {0x01, 0x01, 0x11, 0x00}, {0x16, 0x01, 0x11, 0x01},
+ {0x01, 0x01, 0x12, 0x00}, {0x16, 0x01, 0x12, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x08, 0x00}, {0x09, 0x01, 0x08, 0x00},
+ {0x17, 0x01, 0x08, 0x00}, {0x28, 0x01, 0x08, 0x01},
+ {0x02, 0x01, 0x0b, 0x00}, {0x09, 0x01, 0x0b, 0x00},
+ {0x17, 0x01, 0x0b, 0x00}, {0x28, 0x01, 0x0b, 0x01},
+ {0x02, 0x01, 0x0c, 0x00}, {0x09, 0x01, 0x0c, 0x00},
+ {0x17, 0x01, 0x0c, 0x00}, {0x28, 0x01, 0x0c, 0x01},
+ {0x02, 0x01, 0x0e, 0x00}, {0x09, 0x01, 0x0e, 0x00},
+ {0x17, 0x01, 0x0e, 0x00}, {0x28, 0x01, 0x0e, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x08, 0x00}, {0x06, 0x01, 0x08, 0x00},
+ {0x0a, 0x01, 0x08, 0x00}, {0x0f, 0x01, 0x08, 0x00},
+ {0x18, 0x01, 0x08, 0x00}, {0x1f, 0x01, 0x08, 0x00},
+ {0x29, 0x01, 0x08, 0x00}, {0x38, 0x01, 0x08, 0x01},
+ {0x03, 0x01, 0x0b, 0x00}, {0x06, 0x01, 0x0b, 0x00},
+ {0x0a, 0x01, 0x0b, 0x00}, {0x0f, 0x01, 0x0b, 0x00},
+ {0x18, 0x01, 0x0b, 0x00}, {0x1f, 0x01, 0x0b, 0x00},
+ {0x29, 0x01, 0x0b, 0x00}, {0x38, 0x01, 0x0b, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x0c, 0x00}, {0x06, 0x01, 0x0c, 0x00},
+ {0x0a, 0x01, 0x0c, 0x00}, {0x0f, 0x01, 0x0c, 0x00},
+ {0x18, 0x01, 0x0c, 0x00}, {0x1f, 0x01, 0x0c, 0x00},
+ {0x29, 0x01, 0x0c, 0x00}, {0x38, 0x01, 0x0c, 0x01},
+ {0x03, 0x01, 0x0e, 0x00}, {0x06, 0x01, 0x0e, 0x00},
+ {0x0a, 0x01, 0x0e, 0x00}, {0x0f, 0x01, 0x0e, 0x00},
+ {0x18, 0x01, 0x0e, 0x00}, {0x1f, 0x01, 0x0e, 0x00},
+ {0x29, 0x01, 0x0e, 0x00}, {0x38, 0x01, 0x0e, 0x01}
+ },
+ /* 235 */
+ {
+ {0x02, 0x01, 0x0f, 0x00}, {0x09, 0x01, 0x0f, 0x00},
+ {0x17, 0x01, 0x0f, 0x00}, {0x28, 0x01, 0x0f, 0x01},
+ {0x02, 0x01, 0x10, 0x00}, {0x09, 0x01, 0x10, 0x00},
+ {0x17, 0x01, 0x10, 0x00}, {0x28, 0x01, 0x10, 0x01},
+ {0x02, 0x01, 0x11, 0x00}, {0x09, 0x01, 0x11, 0x00},
+ {0x17, 0x01, 0x11, 0x00}, {0x28, 0x01, 0x11, 0x01},
+ {0x02, 0x01, 0x12, 0x00}, {0x09, 0x01, 0x12, 0x00},
+ {0x17, 0x01, 0x12, 0x00}, {0x28, 0x01, 0x12, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x0f, 0x00}, {0x06, 0x01, 0x0f, 0x00},
+ {0x0a, 0x01, 0x0f, 0x00}, {0x0f, 0x01, 0x0f, 0x00},
+ {0x18, 0x01, 0x0f, 0x00}, {0x1f, 0x01, 0x0f, 0x00},
+ {0x29, 0x01, 0x0f, 0x00}, {0x38, 0x01, 0x0f, 0x01},
+ {0x03, 0x01, 0x10, 0x00}, {0x06, 0x01, 0x10, 0x00},
+ {0x0a, 0x01, 0x10, 0x00}, {0x0f, 0x01, 0x10, 0x00},
+ {0x18, 0x01, 0x10, 0x00}, {0x1f, 0x01, 0x10, 0x00},
+ {0x29, 0x01, 0x10, 0x00}, {0x38, 0x01, 0x10, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x11, 0x00}, {0x06, 0x01, 0x11, 0x00},
+ {0x0a, 0x01, 0x11, 0x00}, {0x0f, 0x01, 0x11, 0x00},
+ {0x18, 0x01, 0x11, 0x00}, {0x1f, 0x01, 0x11, 0x00},
+ {0x29, 0x01, 0x11, 0x00}, {0x38, 0x01, 0x11, 0x01},
+ {0x03, 0x01, 0x12, 0x00}, {0x06, 0x01, 0x12, 0x00},
+ {0x0a, 0x01, 0x12, 0x00}, {0x0f, 0x01, 0x12, 0x00},
+ {0x18, 0x01, 0x12, 0x00}, {0x1f, 0x01, 0x12, 0x00},
+ {0x29, 0x01, 0x12, 0x00}, {0x38, 0x01, 0x12, 0x01}
+ },
+ {
+ {0x00, 0x01, 0x13, 0x01}, {0x00, 0x01, 0x14, 0x01},
+ {0x00, 0x01, 0x15, 0x01}, {0x00, 0x01, 0x17, 0x01},
+ {0x00, 0x01, 0x18, 0x01}, {0x00, 0x01, 0x19, 0x01},
+ {0x00, 0x01, 0x1a, 0x01}, {0x00, 0x01, 0x1b, 0x01},
+ {0x00, 0x01, 0x1c, 0x01}, {0x00, 0x01, 0x1d, 0x01},
+ {0x00, 0x01, 0x1e, 0x01}, {0x00, 0x01, 0x1f, 0x01},
+ {0x00, 0x01, 0x7f, 0x01}, {0x00, 0x01, 0xdc, 0x01},
+ {0x00, 0x01, 0xf9, 0x01}, {0xfd, 0x00, 0x00, 0x01}
+ },
+ {
+ {0x01, 0x01, 0x13, 0x00}, {0x16, 0x01, 0x13, 0x01},
+ {0x01, 0x01, 0x14, 0x00}, {0x16, 0x01, 0x14, 0x01},
+ {0x01, 0x01, 0x15, 0x00}, {0x16, 0x01, 0x15, 0x01},
+ {0x01, 0x01, 0x17, 0x00}, {0x16, 0x01, 0x17, 0x01},
+ {0x01, 0x01, 0x18, 0x00}, {0x16, 0x01, 0x18, 0x01},
+ {0x01, 0x01, 0x19, 0x00}, {0x16, 0x01, 0x19, 0x01},
+ {0x01, 0x01, 0x1a, 0x00}, {0x16, 0x01, 0x1a, 0x01},
+ {0x01, 0x01, 0x1b, 0x00}, {0x16, 0x01, 0x1b, 0x01}
+ },
+ /* 240 */
+ {
+ {0x02, 0x01, 0x13, 0x00}, {0x09, 0x01, 0x13, 0x00},
+ {0x17, 0x01, 0x13, 0x00}, {0x28, 0x01, 0x13, 0x01},
+ {0x02, 0x01, 0x14, 0x00}, {0x09, 0x01, 0x14, 0x00},
+ {0x17, 0x01, 0x14, 0x00}, {0x28, 0x01, 0x14, 0x01},
+ {0x02, 0x01, 0x15, 0x00}, {0x09, 0x01, 0x15, 0x00},
+ {0x17, 0x01, 0x15, 0x00}, {0x28, 0x01, 0x15, 0x01},
+ {0x02, 0x01, 0x17, 0x00}, {0x09, 0x01, 0x17, 0x00},
+ {0x17, 0x01, 0x17, 0x00}, {0x28, 0x01, 0x17, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x13, 0x00}, {0x06, 0x01, 0x13, 0x00},
+ {0x0a, 0x01, 0x13, 0x00}, {0x0f, 0x01, 0x13, 0x00},
+ {0x18, 0x01, 0x13, 0x00}, {0x1f, 0x01, 0x13, 0x00},
+ {0x29, 0x01, 0x13, 0x00}, {0x38, 0x01, 0x13, 0x01},
+ {0x03, 0x01, 0x14, 0x00}, {0x06, 0x01, 0x14, 0x00},
+ {0x0a, 0x01, 0x14, 0x00}, {0x0f, 0x01, 0x14, 0x00},
+ {0x18, 0x01, 0x14, 0x00}, {0x1f, 0x01, 0x14, 0x00},
+ {0x29, 0x01, 0x14, 0x00}, {0x38, 0x01, 0x14, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x15, 0x00}, {0x06, 0x01, 0x15, 0x00},
+ {0x0a, 0x01, 0x15, 0x00}, {0x0f, 0x01, 0x15, 0x00},
+ {0x18, 0x01, 0x15, 0x00}, {0x1f, 0x01, 0x15, 0x00},
+ {0x29, 0x01, 0x15, 0x00}, {0x38, 0x01, 0x15, 0x01},
+ {0x03, 0x01, 0x17, 0x00}, {0x06, 0x01, 0x17, 0x00},
+ {0x0a, 0x01, 0x17, 0x00}, {0x0f, 0x01, 0x17, 0x00},
+ {0x18, 0x01, 0x17, 0x00}, {0x1f, 0x01, 0x17, 0x00},
+ {0x29, 0x01, 0x17, 0x00}, {0x38, 0x01, 0x17, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x18, 0x00}, {0x09, 0x01, 0x18, 0x00},
+ {0x17, 0x01, 0x18, 0x00}, {0x28, 0x01, 0x18, 0x01},
+ {0x02, 0x01, 0x19, 0x00}, {0x09, 0x01, 0x19, 0x00},
+ {0x17, 0x01, 0x19, 0x00}, {0x28, 0x01, 0x19, 0x01},
+ {0x02, 0x01, 0x1a, 0x00}, {0x09, 0x01, 0x1a, 0x00},
+ {0x17, 0x01, 0x1a, 0x00}, {0x28, 0x01, 0x1a, 0x01},
+ {0x02, 0x01, 0x1b, 0x00}, {0x09, 0x01, 0x1b, 0x00},
+ {0x17, 0x01, 0x1b, 0x00}, {0x28, 0x01, 0x1b, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x18, 0x00}, {0x06, 0x01, 0x18, 0x00},
+ {0x0a, 0x01, 0x18, 0x00}, {0x0f, 0x01, 0x18, 0x00},
+ {0x18, 0x01, 0x18, 0x00}, {0x1f, 0x01, 0x18, 0x00},
+ {0x29, 0x01, 0x18, 0x00}, {0x38, 0x01, 0x18, 0x01},
+ {0x03, 0x01, 0x19, 0x00}, {0x06, 0x01, 0x19, 0x00},
+ {0x0a, 0x01, 0x19, 0x00}, {0x0f, 0x01, 0x19, 0x00},
+ {0x18, 0x01, 0x19, 0x00}, {0x1f, 0x01, 0x19, 0x00},
+ {0x29, 0x01, 0x19, 0x00}, {0x38, 0x01, 0x19, 0x01}
+ },
+ /* 245 */
+ {
+ {0x03, 0x01, 0x1a, 0x00}, {0x06, 0x01, 0x1a, 0x00},
+ {0x0a, 0x01, 0x1a, 0x00}, {0x0f, 0x01, 0x1a, 0x00},
+ {0x18, 0x01, 0x1a, 0x00}, {0x1f, 0x01, 0x1a, 0x00},
+ {0x29, 0x01, 0x1a, 0x00}, {0x38, 0x01, 0x1a, 0x01},
+ {0x03, 0x01, 0x1b, 0x00}, {0x06, 0x01, 0x1b, 0x00},
+ {0x0a, 0x01, 0x1b, 0x00}, {0x0f, 0x01, 0x1b, 0x00},
+ {0x18, 0x01, 0x1b, 0x00}, {0x1f, 0x01, 0x1b, 0x00},
+ {0x29, 0x01, 0x1b, 0x00}, {0x38, 0x01, 0x1b, 0x01}
+ },
+ {
+ {0x01, 0x01, 0x1c, 0x00}, {0x16, 0x01, 0x1c, 0x01},
+ {0x01, 0x01, 0x1d, 0x00}, {0x16, 0x01, 0x1d, 0x01},
+ {0x01, 0x01, 0x1e, 0x00}, {0x16, 0x01, 0x1e, 0x01},
+ {0x01, 0x01, 0x1f, 0x00}, {0x16, 0x01, 0x1f, 0x01},
+ {0x01, 0x01, 0x7f, 0x00}, {0x16, 0x01, 0x7f, 0x01},
+ {0x01, 0x01, 0xdc, 0x00}, {0x16, 0x01, 0xdc, 0x01},
+ {0x01, 0x01, 0xf9, 0x00}, {0x16, 0x01, 0xf9, 0x01},
+ {0xfe, 0x00, 0x00, 0x00}, {0xff, 0x00, 0x00, 0x01}
+ },
+ {
+ {0x02, 0x01, 0x1c, 0x00}, {0x09, 0x01, 0x1c, 0x00},
+ {0x17, 0x01, 0x1c, 0x00}, {0x28, 0x01, 0x1c, 0x01},
+ {0x02, 0x01, 0x1d, 0x00}, {0x09, 0x01, 0x1d, 0x00},
+ {0x17, 0x01, 0x1d, 0x00}, {0x28, 0x01, 0x1d, 0x01},
+ {0x02, 0x01, 0x1e, 0x00}, {0x09, 0x01, 0x1e, 0x00},
+ {0x17, 0x01, 0x1e, 0x00}, {0x28, 0x01, 0x1e, 0x01},
+ {0x02, 0x01, 0x1f, 0x00}, {0x09, 0x01, 0x1f, 0x00},
+ {0x17, 0x01, 0x1f, 0x00}, {0x28, 0x01, 0x1f, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x1c, 0x00}, {0x06, 0x01, 0x1c, 0x00},
+ {0x0a, 0x01, 0x1c, 0x00}, {0x0f, 0x01, 0x1c, 0x00},
+ {0x18, 0x01, 0x1c, 0x00}, {0x1f, 0x01, 0x1c, 0x00},
+ {0x29, 0x01, 0x1c, 0x00}, {0x38, 0x01, 0x1c, 0x01},
+ {0x03, 0x01, 0x1d, 0x00}, {0x06, 0x01, 0x1d, 0x00},
+ {0x0a, 0x01, 0x1d, 0x00}, {0x0f, 0x01, 0x1d, 0x00},
+ {0x18, 0x01, 0x1d, 0x00}, {0x1f, 0x01, 0x1d, 0x00},
+ {0x29, 0x01, 0x1d, 0x00}, {0x38, 0x01, 0x1d, 0x01}
+ },
+ {
+ {0x03, 0x01, 0x1e, 0x00}, {0x06, 0x01, 0x1e, 0x00},
+ {0x0a, 0x01, 0x1e, 0x00}, {0x0f, 0x01, 0x1e, 0x00},
+ {0x18, 0x01, 0x1e, 0x00}, {0x1f, 0x01, 0x1e, 0x00},
+ {0x29, 0x01, 0x1e, 0x00}, {0x38, 0x01, 0x1e, 0x01},
+ {0x03, 0x01, 0x1f, 0x00}, {0x06, 0x01, 0x1f, 0x00},
+ {0x0a, 0x01, 0x1f, 0x00}, {0x0f, 0x01, 0x1f, 0x00},
+ {0x18, 0x01, 0x1f, 0x00}, {0x1f, 0x01, 0x1f, 0x00},
+ {0x29, 0x01, 0x1f, 0x00}, {0x38, 0x01, 0x1f, 0x01}
+ },
+ /* 250 */
+ {
+ {0x02, 0x01, 0x7f, 0x00}, {0x09, 0x01, 0x7f, 0x00},
+ {0x17, 0x01, 0x7f, 0x00}, {0x28, 0x01, 0x7f, 0x01},
+ {0x02, 0x01, 0xdc, 0x00}, {0x09, 0x01, 0xdc, 0x00},
+ {0x17, 0x01, 0xdc, 0x00}, {0x28, 0x01, 0xdc, 0x01},
+ {0x02, 0x01, 0xf9, 0x00}, {0x09, 0x01, 0xf9, 0x00},
+ {0x17, 0x01, 0xf9, 0x00}, {0x28, 0x01, 0xf9, 0x01},
+ {0x00, 0x01, 0x0a, 0x01}, {0x00, 0x01, 0x0d, 0x01},
+ {0x00, 0x01, 0x16, 0x01}, {0xfa, 0x00, 0x00, 0x00}
+ },
+ {
+ {0x03, 0x01, 0x7f, 0x00}, {0x06, 0x01, 0x7f, 0x00},
+ {0x0a, 0x01, 0x7f, 0x00}, {0x0f, 0x01, 0x7f, 0x00},
+ {0x18, 0x01, 0x7f, 0x00}, {0x1f, 0x01, 0x7f, 0x00},
+ {0x29, 0x01, 0x7f, 0x00}, {0x38, 0x01, 0x7f, 0x01},
+ {0x03, 0x01, 0xdc, 0x00}, {0x06, 0x01, 0xdc, 0x00},
+ {0x0a, 0x01, 0xdc, 0x00}, {0x0f, 0x01, 0xdc, 0x00},
+ {0x18, 0x01, 0xdc, 0x00}, {0x1f, 0x01, 0xdc, 0x00},
+ {0x29, 0x01, 0xdc, 0x00}, {0x38, 0x01, 0xdc, 0x01}
+ },
+ {
+ {0x03, 0x01, 0xf9, 0x00}, {0x06, 0x01, 0xf9, 0x00},
+ {0x0a, 0x01, 0xf9, 0x00}, {0x0f, 0x01, 0xf9, 0x00},
+ {0x18, 0x01, 0xf9, 0x00}, {0x1f, 0x01, 0xf9, 0x00},
+ {0x29, 0x01, 0xf9, 0x00}, {0x38, 0x01, 0xf9, 0x01},
+ {0x01, 0x01, 0x0a, 0x00}, {0x16, 0x01, 0x0a, 0x01},
+ {0x01, 0x01, 0x0d, 0x00}, {0x16, 0x01, 0x0d, 0x01},
+ {0x01, 0x01, 0x16, 0x00}, {0x16, 0x01, 0x16, 0x01},
+ {0xfc, 0x00, 0x00, 0x00}, {0xfc, 0x00, 0x00, 0x00}
+ },
+ {
+ {0x02, 0x01, 0x0a, 0x00}, {0x09, 0x01, 0x0a, 0x00},
+ {0x17, 0x01, 0x0a, 0x00}, {0x28, 0x01, 0x0a, 0x01},
+ {0x02, 0x01, 0x0d, 0x00}, {0x09, 0x01, 0x0d, 0x00},
+ {0x17, 0x01, 0x0d, 0x00}, {0x28, 0x01, 0x0d, 0x01},
+ {0x02, 0x01, 0x16, 0x00}, {0x09, 0x01, 0x16, 0x00},
+ {0x17, 0x01, 0x16, 0x00}, {0x28, 0x01, 0x16, 0x01},
+ {0xfd, 0x00, 0x00, 0x00}, {0xfd, 0x00, 0x00, 0x00},
+ {0xfd, 0x00, 0x00, 0x00}, {0xfd, 0x00, 0x00, 0x00}
+ },
+ {
+ {0x03, 0x01, 0x0a, 0x00}, {0x06, 0x01, 0x0a, 0x00},
+ {0x0a, 0x01, 0x0a, 0x00}, {0x0f, 0x01, 0x0a, 0x00},
+ {0x18, 0x01, 0x0a, 0x00}, {0x1f, 0x01, 0x0a, 0x00},
+ {0x29, 0x01, 0x0a, 0x00}, {0x38, 0x01, 0x0a, 0x01},
+ {0x03, 0x01, 0x0d, 0x00}, {0x06, 0x01, 0x0d, 0x00},
+ {0x0a, 0x01, 0x0d, 0x00}, {0x0f, 0x01, 0x0d, 0x00},
+ {0x18, 0x01, 0x0d, 0x00}, {0x1f, 0x01, 0x0d, 0x00},
+ {0x29, 0x01, 0x0d, 0x00}, {0x38, 0x01, 0x0d, 0x01}
+ },
+ /* 255 */
+ {
+ {0x03, 0x01, 0x16, 0x00}, {0x06, 0x01, 0x16, 0x00},
+ {0x0a, 0x01, 0x16, 0x00}, {0x0f, 0x01, 0x16, 0x00},
+ {0x18, 0x01, 0x16, 0x00}, {0x1f, 0x01, 0x16, 0x00},
+ {0x29, 0x01, 0x16, 0x00}, {0x38, 0x01, 0x16, 0x01},
+ {0xff, 0x00, 0x00, 0x00}, {0xff, 0x00, 0x00, 0x00},
+ {0xff, 0x00, 0x00, 0x00}, {0xff, 0x00, 0x00, 0x00},
+ {0xff, 0x00, 0x00, 0x00}, {0xff, 0x00, 0x00, 0x00},
+ {0xff, 0x00, 0x00, 0x00}, {0xff, 0x00, 0x00, 0x00}
+ }
+};
+
+
+ngx_int_t
+ngx_http_v2_huff_decode(u_char *state, u_char *src, size_t len, u_char **dst,
+ ngx_uint_t last, ngx_log_t *log)
+{
+ u_char *end, ch, ending;
+
+ ch = 0;
+ ending = 1;
+
+ end = src + len;
+
+ while (src != end) {
+ ch = *src++;
+
+ if (ngx_http_v2_huff_decode_bits(state, &ending, ch >> 4, dst)
+ != NGX_OK)
+ {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,
+ "http2 huffman decoding error at state %d: "
+ "bad code 0x%Xd", *state, ch >> 4);
+
+ return NGX_ERROR;
+ }
+
+ if (ngx_http_v2_huff_decode_bits(state, &ending, ch & 0xf, dst)
+ != NGX_OK)
+ {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,
+ "http2 huffman decoding error at state %d: "
+ "bad code 0x%Xd", *state, ch & 0xf);
+
+ return NGX_ERROR;
+ }
+ }
+
+ if (last) {
+ if (!ending) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
+ "http2 huffman decoding error: "
+ "incomplete code 0x%Xd", ch);
+
+ return NGX_ERROR;
+ }
+
+ *state = 0;
+ }
+
+ return NGX_OK;
+}
+
+
+
+static ngx_inline ngx_int_t
+ngx_http_v2_huff_decode_bits(u_char *state, u_char *ending, ngx_uint_t bits,
+ u_char **dst)
+{
+ ngx_http_v2_huff_decode_code_t code;
+
+ code = ngx_http_v2_huff_decode_codes[*state][bits];
+
+ if (code.next == *state) {
+ return NGX_ERROR;
+ }
+
+ if (code.emit) {
+ *(*dst)++ = code.sym;
+ }
+
+ *ending = code.ending;
+ *state = code.next;
+
+ return NGX_OK;
+}
diff --git a/src/nginx-1.18.0/src/http/v2/ngx_http_v2_huff_encode.c b/src/nginx-1.18.0/src/http/v2/ngx_http_v2_huff_encode.c
new file mode 100644
index 0000000..3f822cd
--- /dev/null
+++ b/src/nginx-1.18.0/src/http/v2/ngx_http_v2_huff_encode.c
@@ -0,0 +1,254 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ * Copyright (C) Valentin V. Bartenev
+ * Copyright (C) 2015 Vlad Krasnov
+ */
+
+
+#include
+#include
+#include
+
+
+typedef struct {
+ uint32_t code;
+ uint32_t len;
+} ngx_http_v2_huff_encode_code_t;
+
+
+static ngx_http_v2_huff_encode_code_t ngx_http_v2_huff_encode_table[256] =
+{
+ {0x00001ff8, 13}, {0x007fffd8, 23}, {0x0fffffe2, 28}, {0x0fffffe3, 28},
+ {0x0fffffe4, 28}, {0x0fffffe5, 28}, {0x0fffffe6, 28}, {0x0fffffe7, 28},
+ {0x0fffffe8, 28}, {0x00ffffea, 24}, {0x3ffffffc, 30}, {0x0fffffe9, 28},
+ {0x0fffffea, 28}, {0x3ffffffd, 30}, {0x0fffffeb, 28}, {0x0fffffec, 28},
+ {0x0fffffed, 28}, {0x0fffffee, 28}, {0x0fffffef, 28}, {0x0ffffff0, 28},
+ {0x0ffffff1, 28}, {0x0ffffff2, 28}, {0x3ffffffe, 30}, {0x0ffffff3, 28},
+ {0x0ffffff4, 28}, {0x0ffffff5, 28}, {0x0ffffff6, 28}, {0x0ffffff7, 28},
+ {0x0ffffff8, 28}, {0x0ffffff9, 28}, {0x0ffffffa, 28}, {0x0ffffffb, 28},
+ {0x00000014, 6}, {0x000003f8, 10}, {0x000003f9, 10}, {0x00000ffa, 12},
+ {0x00001ff9, 13}, {0x00000015, 6}, {0x000000f8, 8}, {0x000007fa, 11},
+ {0x000003fa, 10}, {0x000003fb, 10}, {0x000000f9, 8}, {0x000007fb, 11},
+ {0x000000fa, 8}, {0x00000016, 6}, {0x00000017, 6}, {0x00000018, 6},
+ {0x00000000, 5}, {0x00000001, 5}, {0x00000002, 5}, {0x00000019, 6},
+ {0x0000001a, 6}, {0x0000001b, 6}, {0x0000001c, 6}, {0x0000001d, 6},
+ {0x0000001e, 6}, {0x0000001f, 6}, {0x0000005c, 7}, {0x000000fb, 8},
+ {0x00007ffc, 15}, {0x00000020, 6}, {0x00000ffb, 12}, {0x000003fc, 10},
+ {0x00001ffa, 13}, {0x00000021, 6}, {0x0000005d, 7}, {0x0000005e, 7},
+ {0x0000005f, 7}, {0x00000060, 7}, {0x00000061, 7}, {0x00000062, 7},
+ {0x00000063, 7}, {0x00000064, 7}, {0x00000065, 7}, {0x00000066, 7},
+ {0x00000067, 7}, {0x00000068, 7}, {0x00000069, 7}, {0x0000006a, 7},
+ {0x0000006b, 7}, {0x0000006c, 7}, {0x0000006d, 7}, {0x0000006e, 7},
+ {0x0000006f, 7}, {0x00000070, 7}, {0x00000071, 7}, {0x00000072, 7},
+ {0x000000fc, 8}, {0x00000073, 7}, {0x000000fd, 8}, {0x00001ffb, 13},
+ {0x0007fff0, 19}, {0x00001ffc, 13}, {0x00003ffc, 14}, {0x00000022, 6},
+ {0x00007ffd, 15}, {0x00000003, 5}, {0x00000023, 6}, {0x00000004, 5},
+ {0x00000024, 6}, {0x00000005, 5}, {0x00000025, 6}, {0x00000026, 6},
+ {0x00000027, 6}, {0x00000006, 5}, {0x00000074, 7}, {0x00000075, 7},
+ {0x00000028, 6}, {0x00000029, 6}, {0x0000002a, 6}, {0x00000007, 5},
+ {0x0000002b, 6}, {0x00000076, 7}, {0x0000002c, 6}, {0x00000008, 5},
+ {0x00000009, 5}, {0x0000002d, 6}, {0x00000077, 7}, {0x00000078, 7},
+ {0x00000079, 7}, {0x0000007a, 7}, {0x0000007b, 7}, {0x00007ffe, 15},
+ {0x000007fc, 11}, {0x00003ffd, 14}, {0x00001ffd, 13}, {0x0ffffffc, 28},
+ {0x000fffe6, 20}, {0x003fffd2, 22}, {0x000fffe7, 20}, {0x000fffe8, 20},
+ {0x003fffd3, 22}, {0x003fffd4, 22}, {0x003fffd5, 22}, {0x007fffd9, 23},
+ {0x003fffd6, 22}, {0x007fffda, 23}, {0x007fffdb, 23}, {0x007fffdc, 23},
+ {0x007fffdd, 23}, {0x007fffde, 23}, {0x00ffffeb, 24}, {0x007fffdf, 23},
+ {0x00ffffec, 24}, {0x00ffffed, 24}, {0x003fffd7, 22}, {0x007fffe0, 23},
+ {0x00ffffee, 24}, {0x007fffe1, 23}, {0x007fffe2, 23}, {0x007fffe3, 23},
+ {0x007fffe4, 23}, {0x001fffdc, 21}, {0x003fffd8, 22}, {0x007fffe5, 23},
+ {0x003fffd9, 22}, {0x007fffe6, 23}, {0x007fffe7, 23}, {0x00ffffef, 24},
+ {0x003fffda, 22}, {0x001fffdd, 21}, {0x000fffe9, 20}, {0x003fffdb, 22},
+ {0x003fffdc, 22}, {0x007fffe8, 23}, {0x007fffe9, 23}, {0x001fffde, 21},
+ {0x007fffea, 23}, {0x003fffdd, 22}, {0x003fffde, 22}, {0x00fffff0, 24},
+ {0x001fffdf, 21}, {0x003fffdf, 22}, {0x007fffeb, 23}, {0x007fffec, 23},
+ {0x001fffe0, 21}, {0x001fffe1, 21}, {0x003fffe0, 22}, {0x001fffe2, 21},
+ {0x007fffed, 23}, {0x003fffe1, 22}, {0x007fffee, 23}, {0x007fffef, 23},
+ {0x000fffea, 20}, {0x003fffe2, 22}, {0x003fffe3, 22}, {0x003fffe4, 22},
+ {0x007ffff0, 23}, {0x003fffe5, 22}, {0x003fffe6, 22}, {0x007ffff1, 23},
+ {0x03ffffe0, 26}, {0x03ffffe1, 26}, {0x000fffeb, 20}, {0x0007fff1, 19},
+ {0x003fffe7, 22}, {0x007ffff2, 23}, {0x003fffe8, 22}, {0x01ffffec, 25},
+ {0x03ffffe2, 26}, {0x03ffffe3, 26}, {0x03ffffe4, 26}, {0x07ffffde, 27},
+ {0x07ffffdf, 27}, {0x03ffffe5, 26}, {0x00fffff1, 24}, {0x01ffffed, 25},
+ {0x0007fff2, 19}, {0x001fffe3, 21}, {0x03ffffe6, 26}, {0x07ffffe0, 27},
+ {0x07ffffe1, 27}, {0x03ffffe7, 26}, {0x07ffffe2, 27}, {0x00fffff2, 24},
+ {0x001fffe4, 21}, {0x001fffe5, 21}, {0x03ffffe8, 26}, {0x03ffffe9, 26},
+ {0x0ffffffd, 28}, {0x07ffffe3, 27}, {0x07ffffe4, 27}, {0x07ffffe5, 27},
+ {0x000fffec, 20}, {0x00fffff3, 24}, {0x000fffed, 20}, {0x001fffe6, 21},
+ {0x003fffe9, 22}, {0x001fffe7, 21}, {0x001fffe8, 21}, {0x007ffff3, 23},
+ {0x003fffea, 22}, {0x003fffeb, 22}, {0x01ffffee, 25}, {0x01ffffef, 25},
+ {0x00fffff4, 24}, {0x00fffff5, 24}, {0x03ffffea, 26}, {0x007ffff4, 23},
+ {0x03ffffeb, 26}, {0x07ffffe6, 27}, {0x03ffffec, 26}, {0x03ffffed, 26},
+ {0x07ffffe7, 27}, {0x07ffffe8, 27}, {0x07ffffe9, 27}, {0x07ffffea, 27},
+ {0x07ffffeb, 27}, {0x0ffffffe, 28}, {0x07ffffec, 27}, {0x07ffffed, 27},
+ {0x07ffffee, 27}, {0x07ffffef, 27}, {0x07fffff0, 27}, {0x03ffffee, 26}
+};
+
+
+/* same as above, but embeds lowercase transformation */
+static ngx_http_v2_huff_encode_code_t ngx_http_v2_huff_encode_table_lc[256] =
+{
+ {0x00001ff8, 13}, {0x007fffd8, 23}, {0x0fffffe2, 28}, {0x0fffffe3, 28},
+ {0x0fffffe4, 28}, {0x0fffffe5, 28}, {0x0fffffe6, 28}, {0x0fffffe7, 28},
+ {0x0fffffe8, 28}, {0x00ffffea, 24}, {0x3ffffffc, 30}, {0x0fffffe9, 28},
+ {0x0fffffea, 28}, {0x3ffffffd, 30}, {0x0fffffeb, 28}, {0x0fffffec, 28},
+ {0x0fffffed, 28}, {0x0fffffee, 28}, {0x0fffffef, 28}, {0x0ffffff0, 28},
+ {0x0ffffff1, 28}, {0x0ffffff2, 28}, {0x3ffffffe, 30}, {0x0ffffff3, 28},
+ {0x0ffffff4, 28}, {0x0ffffff5, 28}, {0x0ffffff6, 28}, {0x0ffffff7, 28},
+ {0x0ffffff8, 28}, {0x0ffffff9, 28}, {0x0ffffffa, 28}, {0x0ffffffb, 28},
+ {0x00000014, 6}, {0x000003f8, 10}, {0x000003f9, 10}, {0x00000ffa, 12},
+ {0x00001ff9, 13}, {0x00000015, 6}, {0x000000f8, 8}, {0x000007fa, 11},
+ {0x000003fa, 10}, {0x000003fb, 10}, {0x000000f9, 8}, {0x000007fb, 11},
+ {0x000000fa, 8}, {0x00000016, 6}, {0x00000017, 6}, {0x00000018, 6},
+ {0x00000000, 5}, {0x00000001, 5}, {0x00000002, 5}, {0x00000019, 6},
+ {0x0000001a, 6}, {0x0000001b, 6}, {0x0000001c, 6}, {0x0000001d, 6},
+ {0x0000001e, 6}, {0x0000001f, 6}, {0x0000005c, 7}, {0x000000fb, 8},
+ {0x00007ffc, 15}, {0x00000020, 6}, {0x00000ffb, 12}, {0x000003fc, 10},
+ {0x00001ffa, 13}, {0x00000003, 5}, {0x00000023, 6}, {0x00000004, 5},
+ {0x00000024, 6}, {0x00000005, 5}, {0x00000025, 6}, {0x00000026, 6},
+ {0x00000027, 6}, {0x00000006, 5}, {0x00000074, 7}, {0x00000075, 7},
+ {0x00000028, 6}, {0x00000029, 6}, {0x0000002a, 6}, {0x00000007, 5},
+ {0x0000002b, 6}, {0x00000076, 7}, {0x0000002c, 6}, {0x00000008, 5},
+ {0x00000009, 5}, {0x0000002d, 6}, {0x00000077, 7}, {0x00000078, 7},
+ {0x00000079, 7}, {0x0000007a, 7}, {0x0000007b, 7}, {0x00001ffb, 13},
+ {0x0007fff0, 19}, {0x00001ffc, 13}, {0x00003ffc, 14}, {0x00000022, 6},
+ {0x00007ffd, 15}, {0x00000003, 5}, {0x00000023, 6}, {0x00000004, 5},
+ {0x00000024, 6}, {0x00000005, 5}, {0x00000025, 6}, {0x00000026, 6},
+ {0x00000027, 6}, {0x00000006, 5}, {0x00000074, 7}, {0x00000075, 7},
+ {0x00000028, 6}, {0x00000029, 6}, {0x0000002a, 6}, {0x00000007, 5},
+ {0x0000002b, 6}, {0x00000076, 7}, {0x0000002c, 6}, {0x00000008, 5},
+ {0x00000009, 5}, {0x0000002d, 6}, {0x00000077, 7}, {0x00000078, 7},
+ {0x00000079, 7}, {0x0000007a, 7}, {0x0000007b, 7}, {0x00007ffe, 15},
+ {0x000007fc, 11}, {0x00003ffd, 14}, {0x00001ffd, 13}, {0x0ffffffc, 28},
+ {0x000fffe6, 20}, {0x003fffd2, 22}, {0x000fffe7, 20}, {0x000fffe8, 20},
+ {0x003fffd3, 22}, {0x003fffd4, 22}, {0x003fffd5, 22}, {0x007fffd9, 23},
+ {0x003fffd6, 22}, {0x007fffda, 23}, {0x007fffdb, 23}, {0x007fffdc, 23},
+ {0x007fffdd, 23}, {0x007fffde, 23}, {0x00ffffeb, 24}, {0x007fffdf, 23},
+ {0x00ffffec, 24}, {0x00ffffed, 24}, {0x003fffd7, 22}, {0x007fffe0, 23},
+ {0x00ffffee, 24}, {0x007fffe1, 23}, {0x007fffe2, 23}, {0x007fffe3, 23},
+ {0x007fffe4, 23}, {0x001fffdc, 21}, {0x003fffd8, 22}, {0x007fffe5, 23},
+ {0x003fffd9, 22}, {0x007fffe6, 23}, {0x007fffe7, 23}, {0x00ffffef, 24},
+ {0x003fffda, 22}, {0x001fffdd, 21}, {0x000fffe9, 20}, {0x003fffdb, 22},
+ {0x003fffdc, 22}, {0x007fffe8, 23}, {0x007fffe9, 23}, {0x001fffde, 21},
+ {0x007fffea, 23}, {0x003fffdd, 22}, {0x003fffde, 22}, {0x00fffff0, 24},
+ {0x001fffdf, 21}, {0x003fffdf, 22}, {0x007fffeb, 23}, {0x007fffec, 23},
+ {0x001fffe0, 21}, {0x001fffe1, 21}, {0x003fffe0, 22}, {0x001fffe2, 21},
+ {0x007fffed, 23}, {0x003fffe1, 22}, {0x007fffee, 23}, {0x007fffef, 23},
+ {0x000fffea, 20}, {0x003fffe2, 22}, {0x003fffe3, 22}, {0x003fffe4, 22},
+ {0x007ffff0, 23}, {0x003fffe5, 22}, {0x003fffe6, 22}, {0x007ffff1, 23},
+ {0x03ffffe0, 26}, {0x03ffffe1, 26}, {0x000fffeb, 20}, {0x0007fff1, 19},
+ {0x003fffe7, 22}, {0x007ffff2, 23}, {0x003fffe8, 22}, {0x01ffffec, 25},
+ {0x03ffffe2, 26}, {0x03ffffe3, 26}, {0x03ffffe4, 26}, {0x07ffffde, 27},
+ {0x07ffffdf, 27}, {0x03ffffe5, 26}, {0x00fffff1, 24}, {0x01ffffed, 25},
+ {0x0007fff2, 19}, {0x001fffe3, 21}, {0x03ffffe6, 26}, {0x07ffffe0, 27},
+ {0x07ffffe1, 27}, {0x03ffffe7, 26}, {0x07ffffe2, 27}, {0x00fffff2, 24},
+ {0x001fffe4, 21}, {0x001fffe5, 21}, {0x03ffffe8, 26}, {0x03ffffe9, 26},
+ {0x0ffffffd, 28}, {0x07ffffe3, 27}, {0x07ffffe4, 27}, {0x07ffffe5, 27},
+ {0x000fffec, 20}, {0x00fffff3, 24}, {0x000fffed, 20}, {0x001fffe6, 21},
+ {0x003fffe9, 22}, {0x001fffe7, 21}, {0x001fffe8, 21}, {0x007ffff3, 23},
+ {0x003fffea, 22}, {0x003fffeb, 22}, {0x01ffffee, 25}, {0x01ffffef, 25},
+ {0x00fffff4, 24}, {0x00fffff5, 24}, {0x03ffffea, 26}, {0x007ffff4, 23},
+ {0x03ffffeb, 26}, {0x07ffffe6, 27}, {0x03ffffec, 26}, {0x03ffffed, 26},
+ {0x07ffffe7, 27}, {0x07ffffe8, 27}, {0x07ffffe9, 27}, {0x07ffffea, 27},
+ {0x07ffffeb, 27}, {0x0ffffffe, 28}, {0x07ffffec, 27}, {0x07ffffed, 27},
+ {0x07ffffee, 27}, {0x07ffffef, 27}, {0x07fffff0, 27}, {0x03ffffee, 26}
+};
+
+
+#if (NGX_PTR_SIZE == 8)
+
+#if (NGX_HAVE_LITTLE_ENDIAN)
+
+#if (NGX_HAVE_GCC_BSWAP64)
+#define ngx_http_v2_huff_encode_buf(dst, buf) \
+ (*(uint64_t *) (dst) = __builtin_bswap64(buf))
+#else
+#define ngx_http_v2_huff_encode_buf(dst, buf) \
+ ((dst)[0] = (u_char) ((buf) >> 56), \
+ (dst)[1] = (u_char) ((buf) >> 48), \
+ (dst)[2] = (u_char) ((buf) >> 40), \
+ (dst)[3] = (u_char) ((buf) >> 32), \
+ (dst)[4] = (u_char) ((buf) >> 24), \
+ (dst)[5] = (u_char) ((buf) >> 16), \
+ (dst)[6] = (u_char) ((buf) >> 8), \
+ (dst)[7] = (u_char) (buf))
+#endif
+
+#else /* !NGX_HAVE_LITTLE_ENDIAN */
+#define ngx_http_v2_huff_encode_buf(dst, buf) \
+ (*(uint64_t *) (dst) = (buf))
+#endif
+
+#else /* NGX_PTR_SIZE == 4 */
+
+#define ngx_http_v2_huff_encode_buf(dst, buf) \
+ (*(uint32_t *) (dst) = htonl(buf))
+
+#endif
+
+
+size_t
+ngx_http_v2_huff_encode(u_char *src, size_t len, u_char *dst, ngx_uint_t lower)
+{
+ u_char *end;
+ size_t hlen;
+ ngx_uint_t buf, pending, code;
+ ngx_http_v2_huff_encode_code_t *table, *next;
+
+ table = lower ? ngx_http_v2_huff_encode_table_lc
+ : ngx_http_v2_huff_encode_table;
+ hlen = 0;
+ buf = 0;
+ pending = 0;
+
+ end = src + len;
+
+ while (src != end) {
+ next = &table[*src++];
+
+ code = next->code;
+ pending += next->len;
+
+ /* accumulate bits */
+ if (pending < sizeof(buf) * 8) {
+ buf |= code << (sizeof(buf) * 8 - pending);
+ continue;
+ }
+
+ if (hlen + sizeof(buf) >= len) {
+ return 0;
+ }
+
+ pending -= sizeof(buf) * 8;
+
+ buf |= code >> pending;
+
+ ngx_http_v2_huff_encode_buf(&dst[hlen], buf);
+
+ hlen += sizeof(buf);
+
+ buf = pending ? code << (sizeof(buf) * 8 - pending) : 0;
+ }
+
+ if (pending == 0) {
+ return hlen;
+ }
+
+ buf |= (ngx_uint_t) -1 >> pending;
+
+ pending = ngx_align(pending, 8);
+
+ if (hlen + pending / 8 >= len) {
+ return 0;
+ }
+
+ buf >>= sizeof(buf) * 8 - pending;
+
+ do {
+ pending -= 8;
+ dst[hlen++] = (u_char) (buf >> pending);
+ } while (pending);
+
+ return hlen;
+}
diff --git a/src/nginx-1.18.0/src/http/v2/ngx_http_v2_module.c b/src/nginx-1.18.0/src/http/v2/ngx_http_v2_module.c
new file mode 100644
index 0000000..c54dc10
--- /dev/null
+++ b/src/nginx-1.18.0/src/http/v2/ngx_http_v2_module.c
@@ -0,0 +1,610 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ * Copyright (C) Valentin V. Bartenev
+ */
+
+
+#include
+#include
+#include
+#include
+
+
+static ngx_int_t ngx_http_v2_add_variables(ngx_conf_t *cf);
+
+static ngx_int_t ngx_http_v2_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+
+static ngx_int_t ngx_http_v2_module_init(ngx_cycle_t *cycle);
+
+static void *ngx_http_v2_create_main_conf(ngx_conf_t *cf);
+static char *ngx_http_v2_init_main_conf(ngx_conf_t *cf, void *conf);
+static void *ngx_http_v2_create_srv_conf(ngx_conf_t *cf);
+static char *ngx_http_v2_merge_srv_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static void *ngx_http_v2_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_v2_merge_loc_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+
+static char *ngx_http_v2_push(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+
+static char *ngx_http_v2_recv_buffer_size(ngx_conf_t *cf, void *post,
+ void *data);
+static char *ngx_http_v2_pool_size(ngx_conf_t *cf, void *post, void *data);
+static char *ngx_http_v2_preread_size(ngx_conf_t *cf, void *post, void *data);
+static char *ngx_http_v2_streams_index_mask(ngx_conf_t *cf, void *post,
+ void *data);
+static char *ngx_http_v2_chunk_size(ngx_conf_t *cf, void *post, void *data);
+static char *ngx_http_v2_spdy_deprecated(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_conf_post_t ngx_http_v2_recv_buffer_size_post =
+ { ngx_http_v2_recv_buffer_size };
+static ngx_conf_post_t ngx_http_v2_pool_size_post =
+ { ngx_http_v2_pool_size };
+static ngx_conf_post_t ngx_http_v2_preread_size_post =
+ { ngx_http_v2_preread_size };
+static ngx_conf_post_t ngx_http_v2_streams_index_mask_post =
+ { ngx_http_v2_streams_index_mask };
+static ngx_conf_post_t ngx_http_v2_chunk_size_post =
+ { ngx_http_v2_chunk_size };
+
+
+static ngx_command_t ngx_http_v2_commands[] = {
+
+ { ngx_string("http2_recv_buffer_size"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ offsetof(ngx_http_v2_main_conf_t, recv_buffer_size),
+ &ngx_http_v2_recv_buffer_size_post },
+
+ { ngx_string("http2_pool_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_v2_srv_conf_t, pool_size),
+ &ngx_http_v2_pool_size_post },
+
+ { ngx_string("http2_max_concurrent_streams"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_v2_srv_conf_t, concurrent_streams),
+ NULL },
+
+ { ngx_string("http2_max_concurrent_pushes"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_v2_srv_conf_t, concurrent_pushes),
+ NULL },
+
+ { ngx_string("http2_max_requests"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_v2_srv_conf_t, max_requests),
+ NULL },
+
+ { ngx_string("http2_max_field_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_v2_srv_conf_t, max_field_size),
+ NULL },
+
+ { ngx_string("http2_max_header_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_v2_srv_conf_t, max_header_size),
+ NULL },
+
+ { ngx_string("http2_body_preread_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_v2_srv_conf_t, preread_size),
+ &ngx_http_v2_preread_size_post },
+
+ { ngx_string("http2_streams_index_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_v2_srv_conf_t, streams_index_mask),
+ &ngx_http_v2_streams_index_mask_post },
+
+ { ngx_string("http2_recv_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_v2_srv_conf_t, recv_timeout),
+ NULL },
+
+ { ngx_string("http2_idle_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_v2_srv_conf_t, idle_timeout),
+ NULL },
+
+ { ngx_string("http2_chunk_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_v2_loc_conf_t, chunk_size),
+ &ngx_http_v2_chunk_size_post },
+
+ { ngx_string("http2_push_preload"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_v2_loc_conf_t, push_preload),
+ NULL },
+
+ { ngx_string("http2_push"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_v2_push,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("spdy_recv_buffer_size"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_http_v2_spdy_deprecated,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("spdy_pool_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_http_v2_spdy_deprecated,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("spdy_max_concurrent_streams"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_http_v2_spdy_deprecated,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("spdy_streams_index_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_http_v2_spdy_deprecated,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("spdy_recv_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_http_v2_spdy_deprecated,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("spdy_keepalive_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_http_v2_spdy_deprecated,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("spdy_headers_comp"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_http_v2_spdy_deprecated,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("spdy_chunk_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_v2_spdy_deprecated,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_v2_module_ctx = {
+ ngx_http_v2_add_variables, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ ngx_http_v2_create_main_conf, /* create main configuration */
+ ngx_http_v2_init_main_conf, /* init main configuration */
+
+ ngx_http_v2_create_srv_conf, /* create server configuration */
+ ngx_http_v2_merge_srv_conf, /* merge server configuration */
+
+ ngx_http_v2_create_loc_conf, /* create location configuration */
+ ngx_http_v2_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_v2_module = {
+ NGX_MODULE_V1,
+ &ngx_http_v2_module_ctx, /* module context */
+ ngx_http_v2_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ ngx_http_v2_module_init, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_variable_t ngx_http_v2_vars[] = {
+
+ { ngx_string("http2"), NULL,
+ ngx_http_v2_variable, 0, 0, 0 },
+
+ ngx_http_null_variable
+};
+
+
+static ngx_int_t
+ngx_http_v2_add_variables(ngx_conf_t *cf)
+{
+ ngx_http_variable_t *var, *v;
+
+ for (v = ngx_http_v2_vars; v->name.len; v++) {
+ var = ngx_http_add_variable(cf, &v->name, v->flags);
+ if (var == NULL) {
+ return NGX_ERROR;
+ }
+
+ var->get_handler = v->get_handler;
+ var->data = v->data;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_v2_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+
+ if (r->stream) {
+#if (NGX_HTTP_SSL)
+
+ if (r->connection->ssl) {
+ v->len = sizeof("h2") - 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) "h2";
+
+ return NGX_OK;
+ }
+
+#endif
+ v->len = sizeof("h2c") - 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) "h2c";
+
+ return NGX_OK;
+ }
+
+ *v = ngx_http_variable_null_value;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_v2_module_init(ngx_cycle_t *cycle)
+{
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_v2_create_main_conf(ngx_conf_t *cf)
+{
+ ngx_http_v2_main_conf_t *h2mcf;
+
+ h2mcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v2_main_conf_t));
+ if (h2mcf == NULL) {
+ return NULL;
+ }
+
+ h2mcf->recv_buffer_size = NGX_CONF_UNSET_SIZE;
+
+ return h2mcf;
+}
+
+
+static char *
+ngx_http_v2_init_main_conf(ngx_conf_t *cf, void *conf)
+{
+ ngx_http_v2_main_conf_t *h2mcf = conf;
+
+ ngx_conf_init_size_value(h2mcf->recv_buffer_size, 256 * 1024);
+
+ return NGX_CONF_OK;
+}
+
+
+static void *
+ngx_http_v2_create_srv_conf(ngx_conf_t *cf)
+{
+ ngx_http_v2_srv_conf_t *h2scf;
+
+ h2scf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v2_srv_conf_t));
+ if (h2scf == NULL) {
+ return NULL;
+ }
+
+ h2scf->pool_size = NGX_CONF_UNSET_SIZE;
+
+ h2scf->concurrent_streams = NGX_CONF_UNSET_UINT;
+ h2scf->concurrent_pushes = NGX_CONF_UNSET_UINT;
+ h2scf->max_requests = NGX_CONF_UNSET_UINT;
+
+ h2scf->max_field_size = NGX_CONF_UNSET_SIZE;
+ h2scf->max_header_size = NGX_CONF_UNSET_SIZE;
+
+ h2scf->preread_size = NGX_CONF_UNSET_SIZE;
+
+ h2scf->streams_index_mask = NGX_CONF_UNSET_UINT;
+
+ h2scf->recv_timeout = NGX_CONF_UNSET_MSEC;
+ h2scf->idle_timeout = NGX_CONF_UNSET_MSEC;
+
+ return h2scf;
+}
+
+
+static char *
+ngx_http_v2_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_v2_srv_conf_t *prev = parent;
+ ngx_http_v2_srv_conf_t *conf = child;
+
+ ngx_conf_merge_size_value(conf->pool_size, prev->pool_size, 4096);
+
+ ngx_conf_merge_uint_value(conf->concurrent_streams,
+ prev->concurrent_streams, 128);
+ ngx_conf_merge_uint_value(conf->concurrent_pushes,
+ prev->concurrent_pushes, 10);
+ ngx_conf_merge_uint_value(conf->max_requests, prev->max_requests, 1000);
+
+ ngx_conf_merge_size_value(conf->max_field_size, prev->max_field_size,
+ 4096);
+ ngx_conf_merge_size_value(conf->max_header_size, prev->max_header_size,
+ 16384);
+
+ ngx_conf_merge_size_value(conf->preread_size, prev->preread_size, 65536);
+
+ ngx_conf_merge_uint_value(conf->streams_index_mask,
+ prev->streams_index_mask, 32 - 1);
+
+ ngx_conf_merge_msec_value(conf->recv_timeout,
+ prev->recv_timeout, 30000);
+ ngx_conf_merge_msec_value(conf->idle_timeout,
+ prev->idle_timeout, 180000);
+
+ return NGX_CONF_OK;
+}
+
+
+static void *
+ngx_http_v2_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_v2_loc_conf_t *h2lcf;
+
+ h2lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v2_loc_conf_t));
+ if (h2lcf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * h2lcf->pushes = NULL;
+ */
+
+ h2lcf->chunk_size = NGX_CONF_UNSET_SIZE;
+
+ h2lcf->push_preload = NGX_CONF_UNSET;
+ h2lcf->push = NGX_CONF_UNSET;
+
+ return h2lcf;
+}
+
+
+static char *
+ngx_http_v2_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_v2_loc_conf_t *prev = parent;
+ ngx_http_v2_loc_conf_t *conf = child;
+
+ ngx_conf_merge_size_value(conf->chunk_size, prev->chunk_size, 8 * 1024);
+
+ ngx_conf_merge_value(conf->push, prev->push, 1);
+
+ if (conf->push && conf->pushes == NULL) {
+ conf->pushes = prev->pushes;
+ }
+
+ ngx_conf_merge_value(conf->push_preload, prev->push_preload, 0);
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_v2_push(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_v2_loc_conf_t *h2lcf = conf;
+
+ ngx_str_t *value;
+ ngx_http_complex_value_t *cv;
+ ngx_http_compile_complex_value_t ccv;
+
+ value = cf->args->elts;
+
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+
+ if (h2lcf->pushes) {
+ return "\"off\" parameter cannot be used with URI";
+ }
+
+ if (h2lcf->push == 0) {
+ return "is duplicate";
+ }
+
+ h2lcf->push = 0;
+ return NGX_CONF_OK;
+ }
+
+ if (h2lcf->push == 0) {
+ return "URI cannot be used with \"off\" parameter";
+ }
+
+ h2lcf->push = 1;
+
+ if (h2lcf->pushes == NULL) {
+ h2lcf->pushes = ngx_array_create(cf->pool, 1,
+ sizeof(ngx_http_complex_value_t));
+ if (h2lcf->pushes == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ cv = ngx_array_push(h2lcf->pushes);
+ if (cv == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = cv;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_v2_recv_buffer_size(ngx_conf_t *cf, void *post, void *data)
+{
+ size_t *sp = data;
+
+ if (*sp <= 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE) {
+ return "value is too small";
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_v2_pool_size(ngx_conf_t *cf, void *post, void *data)
+{
+ size_t *sp = data;
+
+ if (*sp < NGX_MIN_POOL_SIZE) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the pool size must be no less than %uz",
+ NGX_MIN_POOL_SIZE);
+
+ return NGX_CONF_ERROR;
+ }
+
+ if (*sp % NGX_POOL_ALIGNMENT) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the pool size must be a multiple of %uz",
+ NGX_POOL_ALIGNMENT);
+
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_v2_preread_size(ngx_conf_t *cf, void *post, void *data)
+{
+ size_t *sp = data;
+
+ if (*sp > NGX_HTTP_V2_MAX_WINDOW) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the maximum body preread buffer size is %uz",
+ NGX_HTTP_V2_MAX_WINDOW);
+
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_v2_streams_index_mask(ngx_conf_t *cf, void *post, void *data)
+{
+ ngx_uint_t *np = data;
+
+ ngx_uint_t mask;
+
+ mask = *np - 1;
+
+ if (*np == 0 || (*np & mask)) {
+ return "must be a power of two";
+ }
+
+ *np = mask;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_v2_chunk_size(ngx_conf_t *cf, void *post, void *data)
+{
+ size_t *sp = data;
+
+ if (*sp == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the http2 chunk size cannot be zero");
+
+ return NGX_CONF_ERROR;
+ }
+
+ if (*sp > NGX_HTTP_V2_MAX_FRAME_SIZE) {
+ *sp = NGX_HTTP_V2_MAX_FRAME_SIZE;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_v2_spdy_deprecated(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "invalid directive \"%V\": ngx_http_spdy_module "
+ "was superseded by ngx_http_v2_module", &cmd->name);
+
+ return NGX_CONF_OK;
+}
diff --git a/src/nginx-1.18.0/src/http/v2/ngx_http_v2_module.h b/src/nginx-1.18.0/src/http/v2/ngx_http_v2_module.h
new file mode 100644
index 0000000..cdd2921
--- /dev/null
+++ b/src/nginx-1.18.0/src/http/v2/ngx_http_v2_module.h
@@ -0,0 +1,50 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ * Copyright (C) Valentin V. Bartenev
+ */
+
+
+#ifndef _NGX_HTTP_V2_MODULE_H_INCLUDED_
+#define _NGX_HTTP_V2_MODULE_H_INCLUDED_
+
+
+#include
+#include
+#include
+
+
+typedef struct {
+ size_t recv_buffer_size;
+ u_char *recv_buffer;
+} ngx_http_v2_main_conf_t;
+
+
+typedef struct {
+ size_t pool_size;
+ ngx_uint_t concurrent_streams;
+ ngx_uint_t concurrent_pushes;
+ ngx_uint_t max_requests;
+ size_t max_field_size;
+ size_t max_header_size;
+ size_t preread_size;
+ ngx_uint_t streams_index_mask;
+ ngx_msec_t recv_timeout;
+ ngx_msec_t idle_timeout;
+} ngx_http_v2_srv_conf_t;
+
+
+typedef struct {
+ size_t chunk_size;
+
+ ngx_flag_t push_preload;
+
+ ngx_flag_t push;
+ ngx_array_t *pushes;
+} ngx_http_v2_loc_conf_t;
+
+
+extern ngx_module_t ngx_http_v2_module;
+
+
+#endif /* _NGX_HTTP_V2_MODULE_H_INCLUDED_ */
diff --git a/src/nginx-1.18.0/src/http/v2/ngx_http_v2_table.c b/src/nginx-1.18.0/src/http/v2/ngx_http_v2_table.c
new file mode 100644
index 0000000..7d49803
--- /dev/null
+++ b/src/nginx-1.18.0/src/http/v2/ngx_http_v2_table.c
@@ -0,0 +1,363 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ * Copyright (C) Valentin V. Bartenev
+ */
+
+
+#include
+#include
+#include
+
+
+#define NGX_HTTP_V2_TABLE_SIZE 4096
+
+
+static ngx_int_t ngx_http_v2_table_account(ngx_http_v2_connection_t *h2c,
+ size_t size);
+
+
+static ngx_http_v2_header_t ngx_http_v2_static_table[] = {
+ { ngx_string(":authority"), ngx_string("") },
+ { ngx_string(":method"), ngx_string("GET") },
+ { ngx_string(":method"), ngx_string("POST") },
+ { ngx_string(":path"), ngx_string("/") },
+ { ngx_string(":path"), ngx_string("/index.html") },
+ { ngx_string(":scheme"), ngx_string("http") },
+ { ngx_string(":scheme"), ngx_string("https") },
+ { ngx_string(":status"), ngx_string("200") },
+ { ngx_string(":status"), ngx_string("204") },
+ { ngx_string(":status"), ngx_string("206") },
+ { ngx_string(":status"), ngx_string("304") },
+ { ngx_string(":status"), ngx_string("400") },
+ { ngx_string(":status"), ngx_string("404") },
+ { ngx_string(":status"), ngx_string("500") },
+ { ngx_string("accept-charset"), ngx_string("") },
+ { ngx_string("accept-encoding"), ngx_string("gzip, deflate") },
+ { ngx_string("accept-language"), ngx_string("") },
+ { ngx_string("accept-ranges"), ngx_string("") },
+ { ngx_string("accept"), ngx_string("") },
+ { ngx_string("access-control-allow-origin"), ngx_string("") },
+ { ngx_string("age"), ngx_string("") },
+ { ngx_string("allow"), ngx_string("") },
+ { ngx_string("authorization"), ngx_string("") },
+ { ngx_string("cache-control"), ngx_string("") },
+ { ngx_string("content-disposition"), ngx_string("") },
+ { ngx_string("content-encoding"), ngx_string("") },
+ { ngx_string("content-language"), ngx_string("") },
+ { ngx_string("content-length"), ngx_string("") },
+ { ngx_string("content-location"), ngx_string("") },
+ { ngx_string("content-range"), ngx_string("") },
+ { ngx_string("content-type"), ngx_string("") },
+ { ngx_string("cookie"), ngx_string("") },
+ { ngx_string("date"), ngx_string("") },
+ { ngx_string("etag"), ngx_string("") },
+ { ngx_string("expect"), ngx_string("") },
+ { ngx_string("expires"), ngx_string("") },
+ { ngx_string("from"), ngx_string("") },
+ { ngx_string("host"), ngx_string("") },
+ { ngx_string("if-match"), ngx_string("") },
+ { ngx_string("if-modified-since"), ngx_string("") },
+ { ngx_string("if-none-match"), ngx_string("") },
+ { ngx_string("if-range"), ngx_string("") },
+ { ngx_string("if-unmodified-since"), ngx_string("") },
+ { ngx_string("last-modified"), ngx_string("") },
+ { ngx_string("link"), ngx_string("") },
+ { ngx_string("location"), ngx_string("") },
+ { ngx_string("max-forwards"), ngx_string("") },
+ { ngx_string("proxy-authenticate"), ngx_string("") },
+ { ngx_string("proxy-authorization"), ngx_string("") },
+ { ngx_string("range"), ngx_string("") },
+ { ngx_string("referer"), ngx_string("") },
+ { ngx_string("refresh"), ngx_string("") },
+ { ngx_string("retry-after"), ngx_string("") },
+ { ngx_string("server"), ngx_string("") },
+ { ngx_string("set-cookie"), ngx_string("") },
+ { ngx_string("strict-transport-security"), ngx_string("") },
+ { ngx_string("transfer-encoding"), ngx_string("") },
+ { ngx_string("user-agent"), ngx_string("") },
+ { ngx_string("vary"), ngx_string("") },
+ { ngx_string("via"), ngx_string("") },
+ { ngx_string("www-authenticate"), ngx_string("") },
+};
+
+#define NGX_HTTP_V2_STATIC_TABLE_ENTRIES \
+ (sizeof(ngx_http_v2_static_table) \
+ / sizeof(ngx_http_v2_header_t))
+
+
+ngx_str_t *
+ngx_http_v2_get_static_name(ngx_uint_t index)
+{
+ return &ngx_http_v2_static_table[index - 1].name;
+}
+
+
+ngx_str_t *
+ngx_http_v2_get_static_value(ngx_uint_t index)
+{
+ return &ngx_http_v2_static_table[index - 1].value;
+}
+
+
+ngx_int_t
+ngx_http_v2_get_indexed_header(ngx_http_v2_connection_t *h2c, ngx_uint_t index,
+ ngx_uint_t name_only)
+{
+ u_char *p;
+ size_t rest;
+ ngx_http_v2_header_t *entry;
+
+ if (index == 0) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent invalid hpack table index 0");
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 get indexed %s: %ui",
+ name_only ? "name" : "header", index);
+
+ index--;
+
+ if (index < NGX_HTTP_V2_STATIC_TABLE_ENTRIES) {
+ h2c->state.header = ngx_http_v2_static_table[index];
+ return NGX_OK;
+ }
+
+ index -= NGX_HTTP_V2_STATIC_TABLE_ENTRIES;
+
+ if (index < h2c->hpack.added - h2c->hpack.deleted) {
+ index = (h2c->hpack.added - index - 1) % h2c->hpack.allocated;
+ entry = h2c->hpack.entries[index];
+
+ p = ngx_pnalloc(h2c->state.pool, entry->name.len + 1);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ h2c->state.header.name.len = entry->name.len;
+ h2c->state.header.name.data = p;
+
+ rest = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - entry->name.data;
+
+ if (entry->name.len > rest) {
+ p = ngx_cpymem(p, entry->name.data, rest);
+ p = ngx_cpymem(p, h2c->hpack.storage, entry->name.len - rest);
+
+ } else {
+ p = ngx_cpymem(p, entry->name.data, entry->name.len);
+ }
+
+ *p = '\0';
+
+ if (name_only) {
+ return NGX_OK;
+ }
+
+ p = ngx_pnalloc(h2c->state.pool, entry->value.len + 1);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ h2c->state.header.value.len = entry->value.len;
+ h2c->state.header.value.data = p;
+
+ rest = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - entry->value.data;
+
+ if (entry->value.len > rest) {
+ p = ngx_cpymem(p, entry->value.data, rest);
+ p = ngx_cpymem(p, h2c->hpack.storage, entry->value.len - rest);
+
+ } else {
+ p = ngx_cpymem(p, entry->value.data, entry->value.len);
+ }
+
+ *p = '\0';
+
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent out of bound hpack table index: %ui", index);
+
+ return NGX_ERROR;
+}
+
+
+ngx_int_t
+ngx_http_v2_add_header(ngx_http_v2_connection_t *h2c,
+ ngx_http_v2_header_t *header)
+{
+ size_t avail;
+ ngx_uint_t index;
+ ngx_http_v2_header_t *entry, **entries;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 table add: \"%V: %V\"",
+ &header->name, &header->value);
+
+ if (h2c->hpack.entries == NULL) {
+ h2c->hpack.allocated = 64;
+ h2c->hpack.size = NGX_HTTP_V2_TABLE_SIZE;
+ h2c->hpack.free = NGX_HTTP_V2_TABLE_SIZE;
+
+ h2c->hpack.entries = ngx_palloc(h2c->connection->pool,
+ sizeof(ngx_http_v2_header_t *)
+ * h2c->hpack.allocated);
+ if (h2c->hpack.entries == NULL) {
+ return NGX_ERROR;
+ }
+
+ h2c->hpack.storage = ngx_palloc(h2c->connection->pool,
+ h2c->hpack.free);
+ if (h2c->hpack.storage == NULL) {
+ return NGX_ERROR;
+ }
+
+ h2c->hpack.pos = h2c->hpack.storage;
+ }
+
+ if (ngx_http_v2_table_account(h2c, header->name.len + header->value.len)
+ != NGX_OK)
+ {
+ return NGX_OK;
+ }
+
+ if (h2c->hpack.reused == h2c->hpack.deleted) {
+ entry = ngx_palloc(h2c->connection->pool, sizeof(ngx_http_v2_header_t));
+ if (entry == NULL) {
+ return NGX_ERROR;
+ }
+
+ } else {
+ entry = h2c->hpack.entries[h2c->hpack.reused++ % h2c->hpack.allocated];
+ }
+
+ avail = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - h2c->hpack.pos;
+
+ entry->name.len = header->name.len;
+ entry->name.data = h2c->hpack.pos;
+
+ if (avail >= header->name.len) {
+ h2c->hpack.pos = ngx_cpymem(h2c->hpack.pos, header->name.data,
+ header->name.len);
+ } else {
+ ngx_memcpy(h2c->hpack.pos, header->name.data, avail);
+ h2c->hpack.pos = ngx_cpymem(h2c->hpack.storage,
+ header->name.data + avail,
+ header->name.len - avail);
+ avail = NGX_HTTP_V2_TABLE_SIZE;
+ }
+
+ avail -= header->name.len;
+
+ entry->value.len = header->value.len;
+ entry->value.data = h2c->hpack.pos;
+
+ if (avail >= header->value.len) {
+ h2c->hpack.pos = ngx_cpymem(h2c->hpack.pos, header->value.data,
+ header->value.len);
+ } else {
+ ngx_memcpy(h2c->hpack.pos, header->value.data, avail);
+ h2c->hpack.pos = ngx_cpymem(h2c->hpack.storage,
+ header->value.data + avail,
+ header->value.len - avail);
+ }
+
+ if (h2c->hpack.allocated == h2c->hpack.added - h2c->hpack.deleted) {
+
+ entries = ngx_palloc(h2c->connection->pool,
+ sizeof(ngx_http_v2_header_t *)
+ * (h2c->hpack.allocated + 64));
+ if (entries == NULL) {
+ return NGX_ERROR;
+ }
+
+ index = h2c->hpack.deleted % h2c->hpack.allocated;
+
+ ngx_memcpy(entries, &h2c->hpack.entries[index],
+ (h2c->hpack.allocated - index)
+ * sizeof(ngx_http_v2_header_t *));
+
+ ngx_memcpy(&entries[h2c->hpack.allocated - index], h2c->hpack.entries,
+ index * sizeof(ngx_http_v2_header_t *));
+
+ (void) ngx_pfree(h2c->connection->pool, h2c->hpack.entries);
+
+ h2c->hpack.entries = entries;
+
+ h2c->hpack.added = h2c->hpack.allocated;
+ h2c->hpack.deleted = 0;
+ h2c->hpack.reused = 0;
+ h2c->hpack.allocated += 64;
+ }
+
+ h2c->hpack.entries[h2c->hpack.added++ % h2c->hpack.allocated] = entry;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_v2_table_account(ngx_http_v2_connection_t *h2c, size_t size)
+{
+ ngx_http_v2_header_t *entry;
+
+ size += 32;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 table account: %uz free:%uz",
+ size, h2c->hpack.free);
+
+ if (size <= h2c->hpack.free) {
+ h2c->hpack.free -= size;
+ return NGX_OK;
+ }
+
+ if (size > h2c->hpack.size) {
+ h2c->hpack.deleted = h2c->hpack.added;
+ h2c->hpack.free = h2c->hpack.size;
+ return NGX_DECLINED;
+ }
+
+ do {
+ entry = h2c->hpack.entries[h2c->hpack.deleted++ % h2c->hpack.allocated];
+ h2c->hpack.free += 32 + entry->name.len + entry->value.len;
+ } while (size > h2c->hpack.free);
+
+ h2c->hpack.free -= size;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_v2_table_size(ngx_http_v2_connection_t *h2c, size_t size)
+{
+ ssize_t needed;
+ ngx_http_v2_header_t *entry;
+
+ if (size > NGX_HTTP_V2_TABLE_SIZE) {
+ ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+ "client sent invalid table size update: %uz", size);
+
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+ "http2 new hpack table size: %uz was:%uz",
+ size, h2c->hpack.size);
+
+ needed = h2c->hpack.size - size;
+
+ while (needed > (ssize_t) h2c->hpack.free) {
+ entry = h2c->hpack.entries[h2c->hpack.deleted++ % h2c->hpack.allocated];
+ h2c->hpack.free += 32 + entry->name.len + entry->value.len;
+ }
+
+ h2c->hpack.size = size;
+ h2c->hpack.free -= needed;
+
+ return NGX_OK;
+}
diff --git a/src/nginx-1.18.0/src/mail/ngx_mail.c b/src/nginx-1.18.0/src/mail/ngx_mail.c
new file mode 100644
index 0000000..f17c2cc
--- /dev/null
+++ b/src/nginx-1.18.0/src/mail/ngx_mail.c
@@ -0,0 +1,482 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+#include
+#include
+
+
+static char *ngx_mail_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static ngx_int_t ngx_mail_add_ports(ngx_conf_t *cf, ngx_array_t *ports,
+ ngx_mail_listen_t *listen);
+static char *ngx_mail_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports);
+static ngx_int_t ngx_mail_add_addrs(ngx_conf_t *cf, ngx_mail_port_t *mport,
+ ngx_mail_conf_addr_t *addr);
+#if (NGX_HAVE_INET6)
+static ngx_int_t ngx_mail_add_addrs6(ngx_conf_t *cf, ngx_mail_port_t *mport,
+ ngx_mail_conf_addr_t *addr);
+#endif
+static ngx_int_t ngx_mail_cmp_conf_addrs(const void *one, const void *two);
+
+
+ngx_uint_t ngx_mail_max_module;
+
+
+static ngx_command_t ngx_mail_commands[] = {
+
+ { ngx_string("mail"),
+ NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+ ngx_mail_block,
+ 0,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_core_module_t ngx_mail_module_ctx = {
+ ngx_string("mail"),
+ NULL,
+ NULL
+};
+
+
+ngx_module_t ngx_mail_module = {
+ NGX_MODULE_V1,
+ &ngx_mail_module_ctx, /* module context */
+ ngx_mail_commands, /* module directives */
+ NGX_CORE_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static char *
+ngx_mail_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *rv;
+ ngx_uint_t i, m, mi, s;
+ ngx_conf_t pcf;
+ ngx_array_t ports;
+ ngx_mail_listen_t *listen;
+ ngx_mail_module_t *module;
+ ngx_mail_conf_ctx_t *ctx;
+ ngx_mail_core_srv_conf_t **cscfp;
+ ngx_mail_core_main_conf_t *cmcf;
+
+ if (*(ngx_mail_conf_ctx_t **) conf) {
+ return "is duplicate";
+ }
+
+ /* the main mail context */
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_mail_conf_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *(ngx_mail_conf_ctx_t **) conf = ctx;
+
+ /* count the number of the mail modules and set up their indices */
+
+ ngx_mail_max_module = ngx_count_modules(cf->cycle, NGX_MAIL_MODULE);
+
+
+ /* the mail main_conf context, it is the same in the all mail contexts */
+
+ ctx->main_conf = ngx_pcalloc(cf->pool,
+ sizeof(void *) * ngx_mail_max_module);
+ if (ctx->main_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+
+ /*
+ * the mail null srv_conf context, it is used to merge
+ * the server{}s' srv_conf's
+ */
+
+ ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_mail_max_module);
+ if (ctx->srv_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+
+ /*
+ * create the main_conf's and the null srv_conf's of the all mail modules
+ */
+
+ for (m = 0; cf->cycle->modules[m]; m++) {
+ if (cf->cycle->modules[m]->type != NGX_MAIL_MODULE) {
+ continue;
+ }
+
+ module = cf->cycle->modules[m]->ctx;
+ mi = cf->cycle->modules[m]->ctx_index;
+
+ if (module->create_main_conf) {
+ ctx->main_conf[mi] = module->create_main_conf(cf);
+ if (ctx->main_conf[mi] == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ if (module->create_srv_conf) {
+ ctx->srv_conf[mi] = module->create_srv_conf(cf);
+ if (ctx->srv_conf[mi] == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+ }
+
+
+ /* parse inside the mail{} block */
+
+ pcf = *cf;
+ cf->ctx = ctx;
+
+ cf->module_type = NGX_MAIL_MODULE;
+ cf->cmd_type = NGX_MAIL_MAIN_CONF;
+ rv = ngx_conf_parse(cf, NULL);
+
+ if (rv != NGX_CONF_OK) {
+ *cf = pcf;
+ return rv;
+ }
+
+
+ /* init mail{} main_conf's, merge the server{}s' srv_conf's */
+
+ cmcf = ctx->main_conf[ngx_mail_core_module.ctx_index];
+ cscfp = cmcf->servers.elts;
+
+ for (m = 0; cf->cycle->modules[m]; m++) {
+ if (cf->cycle->modules[m]->type != NGX_MAIL_MODULE) {
+ continue;
+ }
+
+ module = cf->cycle->modules[m]->ctx;
+ mi = cf->cycle->modules[m]->ctx_index;
+
+ /* init mail{} main_conf's */
+
+ cf->ctx = ctx;
+
+ if (module->init_main_conf) {
+ rv = module->init_main_conf(cf, ctx->main_conf[mi]);
+ if (rv != NGX_CONF_OK) {
+ *cf = pcf;
+ return rv;
+ }
+ }
+
+ for (s = 0; s < cmcf->servers.nelts; s++) {
+
+ /* merge the server{}s' srv_conf's */
+
+ cf->ctx = cscfp[s]->ctx;
+
+ if (module->merge_srv_conf) {
+ rv = module->merge_srv_conf(cf,
+ ctx->srv_conf[mi],
+ cscfp[s]->ctx->srv_conf[mi]);
+ if (rv != NGX_CONF_OK) {
+ *cf = pcf;
+ return rv;
+ }
+ }
+ }
+ }
+
+ *cf = pcf;
+
+
+ if (ngx_array_init(&ports, cf->temp_pool, 4, sizeof(ngx_mail_conf_port_t))
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ listen = cmcf->listen.elts;
+
+ for (i = 0; i < cmcf->listen.nelts; i++) {
+ if (ngx_mail_add_ports(cf, &ports, &listen[i]) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ return ngx_mail_optimize_servers(cf, &ports);
+}
+
+
+static ngx_int_t
+ngx_mail_add_ports(ngx_conf_t *cf, ngx_array_t *ports,
+ ngx_mail_listen_t *listen)
+{
+ in_port_t p;
+ ngx_uint_t i;
+ struct sockaddr *sa;
+ ngx_mail_conf_port_t *port;
+ ngx_mail_conf_addr_t *addr;
+
+ sa = listen->sockaddr;
+ p = ngx_inet_get_port(sa);
+
+ port = ports->elts;
+ for (i = 0; i < ports->nelts; i++) {
+ if (p == port[i].port && sa->sa_family == port[i].family) {
+
+ /* a port is already in the port list */
+
+ port = &port[i];
+ goto found;
+ }
+ }
+
+ /* add a port to the port list */
+
+ port = ngx_array_push(ports);
+ if (port == NULL) {
+ return NGX_ERROR;
+ }
+
+ port->family = sa->sa_family;
+ port->port = p;
+
+ if (ngx_array_init(&port->addrs, cf->temp_pool, 2,
+ sizeof(ngx_mail_conf_addr_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+found:
+
+ addr = ngx_array_push(&port->addrs);
+ if (addr == NULL) {
+ return NGX_ERROR;
+ }
+
+ addr->opt = *listen;
+
+ return NGX_OK;
+}
+
+
+static char *
+ngx_mail_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports)
+{
+ ngx_uint_t i, p, last, bind_wildcard;
+ ngx_listening_t *ls;
+ ngx_mail_port_t *mport;
+ ngx_mail_conf_port_t *port;
+ ngx_mail_conf_addr_t *addr;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ port = ports->elts;
+ for (p = 0; p < ports->nelts; p++) {
+
+ ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts,
+ sizeof(ngx_mail_conf_addr_t), ngx_mail_cmp_conf_addrs);
+
+ addr = port[p].addrs.elts;
+ last = port[p].addrs.nelts;
+
+ /*
+ * if there is the binding to the "*:port" then we need to bind()
+ * to the "*:port" only and ignore the other bindings
+ */
+
+ if (addr[last - 1].opt.wildcard) {
+ addr[last - 1].opt.bind = 1;
+ bind_wildcard = 1;
+
+ } else {
+ bind_wildcard = 0;
+ }
+
+ i = 0;
+
+ while (i < last) {
+
+ if (bind_wildcard && !addr[i].opt.bind) {
+ i++;
+ continue;
+ }
+
+ ls = ngx_create_listening(cf, addr[i].opt.sockaddr,
+ addr[i].opt.socklen);
+ if (ls == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ls->addr_ntop = 1;
+ ls->handler = ngx_mail_init_connection;
+ ls->pool_size = 256;
+
+ cscf = addr->opt.ctx->srv_conf[ngx_mail_core_module.ctx_index];
+
+ ls->logp = cscf->error_log;
+ ls->log.data = &ls->addr_text;
+ ls->log.handler = ngx_accept_log_error;
+
+ ls->backlog = addr[i].opt.backlog;
+ ls->rcvbuf = addr[i].opt.rcvbuf;
+ ls->sndbuf = addr[i].opt.sndbuf;
+
+ ls->keepalive = addr[i].opt.so_keepalive;
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+ ls->keepidle = addr[i].opt.tcp_keepidle;
+ ls->keepintvl = addr[i].opt.tcp_keepintvl;
+ ls->keepcnt = addr[i].opt.tcp_keepcnt;
+#endif
+
+#if (NGX_HAVE_INET6)
+ ls->ipv6only = addr[i].opt.ipv6only;
+#endif
+
+ mport = ngx_palloc(cf->pool, sizeof(ngx_mail_port_t));
+ if (mport == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ls->servers = mport;
+
+ mport->naddrs = i + 1;
+
+ switch (ls->sockaddr->sa_family) {
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ if (ngx_mail_add_addrs6(cf, mport, addr) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ break;
+#endif
+ default: /* AF_INET */
+ if (ngx_mail_add_addrs(cf, mport, addr) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ break;
+ }
+
+ addr++;
+ last--;
+ }
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_mail_add_addrs(ngx_conf_t *cf, ngx_mail_port_t *mport,
+ ngx_mail_conf_addr_t *addr)
+{
+ ngx_uint_t i;
+ ngx_mail_in_addr_t *addrs;
+ struct sockaddr_in *sin;
+
+ mport->addrs = ngx_pcalloc(cf->pool,
+ mport->naddrs * sizeof(ngx_mail_in_addr_t));
+ if (mport->addrs == NULL) {
+ return NGX_ERROR;
+ }
+
+ addrs = mport->addrs;
+
+ for (i = 0; i < mport->naddrs; i++) {
+
+ sin = (struct sockaddr_in *) addr[i].opt.sockaddr;
+ addrs[i].addr = sin->sin_addr.s_addr;
+
+ addrs[i].conf.ctx = addr[i].opt.ctx;
+#if (NGX_MAIL_SSL)
+ addrs[i].conf.ssl = addr[i].opt.ssl;
+#endif
+ addrs[i].conf.addr_text = addr[i].opt.addr_text;
+ }
+
+ return NGX_OK;
+}
+
+
+#if (NGX_HAVE_INET6)
+
+static ngx_int_t
+ngx_mail_add_addrs6(ngx_conf_t *cf, ngx_mail_port_t *mport,
+ ngx_mail_conf_addr_t *addr)
+{
+ ngx_uint_t i;
+ ngx_mail_in6_addr_t *addrs6;
+ struct sockaddr_in6 *sin6;
+
+ mport->addrs = ngx_pcalloc(cf->pool,
+ mport->naddrs * sizeof(ngx_mail_in6_addr_t));
+ if (mport->addrs == NULL) {
+ return NGX_ERROR;
+ }
+
+ addrs6 = mport->addrs;
+
+ for (i = 0; i < mport->naddrs; i++) {
+
+ sin6 = (struct sockaddr_in6 *) addr[i].opt.sockaddr;
+ addrs6[i].addr6 = sin6->sin6_addr;
+
+ addrs6[i].conf.ctx = addr[i].opt.ctx;
+#if (NGX_MAIL_SSL)
+ addrs6[i].conf.ssl = addr[i].opt.ssl;
+#endif
+ addrs6[i].conf.addr_text = addr[i].opt.addr_text;
+ }
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_mail_cmp_conf_addrs(const void *one, const void *two)
+{
+ ngx_mail_conf_addr_t *first, *second;
+
+ first = (ngx_mail_conf_addr_t *) one;
+ second = (ngx_mail_conf_addr_t *) two;
+
+ if (first->opt.wildcard) {
+ /* a wildcard must be the last resort, shift it to the end */
+ return 1;
+ }
+
+ if (second->opt.wildcard) {
+ /* a wildcard must be the last resort, shift it to the end */
+ return -1;
+ }
+
+ if (first->opt.bind && !second->opt.bind) {
+ /* shift explicit bind()ed addresses to the start */
+ return -1;
+ }
+
+ if (!first->opt.bind && second->opt.bind) {
+ /* shift explicit bind()ed addresses to the start */
+ return 1;
+ }
+
+ /* do not sort by default */
+
+ return 0;
+}
diff --git a/src/nginx-1.18.0/src/mail/ngx_mail.h b/src/nginx-1.18.0/src/mail/ngx_mail.h
new file mode 100644
index 0000000..d904f25
--- /dev/null
+++ b/src/nginx-1.18.0/src/mail/ngx_mail.h
@@ -0,0 +1,413 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_MAIL_H_INCLUDED_
+#define _NGX_MAIL_H_INCLUDED_
+
+
+#include
+#include
+#include
+#include
+
+#if (NGX_MAIL_SSL)
+#include
+#endif
+
+
+
+typedef struct {
+ void **main_conf;
+ void **srv_conf;
+} ngx_mail_conf_ctx_t;
+
+
+typedef struct {
+ struct sockaddr *sockaddr;
+ socklen_t socklen;
+ ngx_str_t addr_text;
+
+ /* server ctx */
+ ngx_mail_conf_ctx_t *ctx;
+
+ unsigned bind:1;
+ unsigned wildcard:1;
+ unsigned ssl:1;
+#if (NGX_HAVE_INET6)
+ unsigned ipv6only:1;
+#endif
+ unsigned so_keepalive:2;
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+ int tcp_keepidle;
+ int tcp_keepintvl;
+ int tcp_keepcnt;
+#endif
+ int backlog;
+ int rcvbuf;
+ int sndbuf;
+} ngx_mail_listen_t;
+
+
+typedef struct {
+ ngx_mail_conf_ctx_t *ctx;
+ ngx_str_t addr_text;
+ ngx_uint_t ssl; /* unsigned ssl:1; */
+} ngx_mail_addr_conf_t;
+
+typedef struct {
+ in_addr_t addr;
+ ngx_mail_addr_conf_t conf;
+} ngx_mail_in_addr_t;
+
+
+#if (NGX_HAVE_INET6)
+
+typedef struct {
+ struct in6_addr addr6;
+ ngx_mail_addr_conf_t conf;
+} ngx_mail_in6_addr_t;
+
+#endif
+
+
+typedef struct {
+ /* ngx_mail_in_addr_t or ngx_mail_in6_addr_t */
+ void *addrs;
+ ngx_uint_t naddrs;
+} ngx_mail_port_t;
+
+
+typedef struct {
+ int family;
+ in_port_t port;
+ ngx_array_t addrs; /* array of ngx_mail_conf_addr_t */
+} ngx_mail_conf_port_t;
+
+
+typedef struct {
+ ngx_mail_listen_t opt;
+} ngx_mail_conf_addr_t;
+
+
+typedef struct {
+ ngx_array_t servers; /* ngx_mail_core_srv_conf_t */
+ ngx_array_t listen; /* ngx_mail_listen_t */
+} ngx_mail_core_main_conf_t;
+
+
+#define NGX_MAIL_POP3_PROTOCOL 0
+#define NGX_MAIL_IMAP_PROTOCOL 1
+#define NGX_MAIL_SMTP_PROTOCOL 2
+
+
+typedef struct ngx_mail_protocol_s ngx_mail_protocol_t;
+
+
+typedef struct {
+ ngx_mail_protocol_t *protocol;
+
+ ngx_msec_t timeout;
+ ngx_msec_t resolver_timeout;
+
+ ngx_str_t server_name;
+
+ u_char *file_name;
+ ngx_uint_t line;
+
+ ngx_resolver_t *resolver;
+ ngx_log_t *error_log;
+
+ /* server ctx */
+ ngx_mail_conf_ctx_t *ctx;
+
+ ngx_uint_t listen; /* unsigned listen:1; */
+} ngx_mail_core_srv_conf_t;
+
+
+typedef enum {
+ ngx_pop3_start = 0,
+ ngx_pop3_user,
+ ngx_pop3_passwd,
+ ngx_pop3_auth_login_username,
+ ngx_pop3_auth_login_password,
+ ngx_pop3_auth_plain,
+ ngx_pop3_auth_cram_md5,
+ ngx_pop3_auth_external
+} ngx_pop3_state_e;
+
+
+typedef enum {
+ ngx_imap_start = 0,
+ ngx_imap_auth_login_username,
+ ngx_imap_auth_login_password,
+ ngx_imap_auth_plain,
+ ngx_imap_auth_cram_md5,
+ ngx_imap_auth_external,
+ ngx_imap_login,
+ ngx_imap_user,
+ ngx_imap_passwd
+} ngx_imap_state_e;
+
+
+typedef enum {
+ ngx_smtp_start = 0,
+ ngx_smtp_auth_login_username,
+ ngx_smtp_auth_login_password,
+ ngx_smtp_auth_plain,
+ ngx_smtp_auth_cram_md5,
+ ngx_smtp_auth_external,
+ ngx_smtp_helo,
+ ngx_smtp_helo_xclient,
+ ngx_smtp_helo_from,
+ ngx_smtp_xclient,
+ ngx_smtp_xclient_from,
+ ngx_smtp_xclient_helo,
+ ngx_smtp_from,
+ ngx_smtp_to
+} ngx_smtp_state_e;
+
+
+typedef struct {
+ ngx_peer_connection_t upstream;
+ ngx_buf_t *buffer;
+} ngx_mail_proxy_ctx_t;
+
+
+typedef struct {
+ uint32_t signature; /* "MAIL" */
+
+ ngx_connection_t *connection;
+
+ ngx_str_t out;
+ ngx_buf_t *buffer;
+
+ void **ctx;
+ void **main_conf;
+ void **srv_conf;
+
+ ngx_resolver_ctx_t *resolver_ctx;
+
+ ngx_mail_proxy_ctx_t *proxy;
+
+ ngx_uint_t mail_state;
+
+ unsigned protocol:3;
+ unsigned blocked:1;
+ unsigned quit:1;
+ unsigned quoted:1;
+ unsigned backslash:1;
+ unsigned no_sync_literal:1;
+ unsigned starttls:1;
+ unsigned esmtp:1;
+ unsigned auth_method:3;
+ unsigned auth_wait:1;
+
+ ngx_str_t login;
+ ngx_str_t passwd;
+
+ ngx_str_t salt;
+ ngx_str_t tag;
+ ngx_str_t tagged_line;
+ ngx_str_t text;
+
+ ngx_str_t *addr_text;
+ ngx_str_t host;
+ ngx_str_t smtp_helo;
+ ngx_str_t smtp_from;
+ ngx_str_t smtp_to;
+
+ ngx_str_t cmd;
+
+ ngx_uint_t command;
+ ngx_array_t args;
+
+ ngx_uint_t login_attempt;
+
+ /* used to parse POP3/IMAP/SMTP command */
+
+ ngx_uint_t state;
+ u_char *cmd_start;
+ u_char *arg_start;
+ u_char *arg_end;
+ ngx_uint_t literal_len;
+} ngx_mail_session_t;
+
+
+typedef struct {
+ ngx_str_t *client;
+ ngx_mail_session_t *session;
+} ngx_mail_log_ctx_t;
+
+
+#define NGX_POP3_USER 1
+#define NGX_POP3_PASS 2
+#define NGX_POP3_CAPA 3
+#define NGX_POP3_QUIT 4
+#define NGX_POP3_NOOP 5
+#define NGX_POP3_STLS 6
+#define NGX_POP3_APOP 7
+#define NGX_POP3_AUTH 8
+#define NGX_POP3_STAT 9
+#define NGX_POP3_LIST 10
+#define NGX_POP3_RETR 11
+#define NGX_POP3_DELE 12
+#define NGX_POP3_RSET 13
+#define NGX_POP3_TOP 14
+#define NGX_POP3_UIDL 15
+
+
+#define NGX_IMAP_LOGIN 1
+#define NGX_IMAP_LOGOUT 2
+#define NGX_IMAP_CAPABILITY 3
+#define NGX_IMAP_NOOP 4
+#define NGX_IMAP_STARTTLS 5
+
+#define NGX_IMAP_NEXT 6
+
+#define NGX_IMAP_AUTHENTICATE 7
+
+
+#define NGX_SMTP_HELO 1
+#define NGX_SMTP_EHLO 2
+#define NGX_SMTP_AUTH 3
+#define NGX_SMTP_QUIT 4
+#define NGX_SMTP_NOOP 5
+#define NGX_SMTP_MAIL 6
+#define NGX_SMTP_RSET 7
+#define NGX_SMTP_RCPT 8
+#define NGX_SMTP_DATA 9
+#define NGX_SMTP_VRFY 10
+#define NGX_SMTP_EXPN 11
+#define NGX_SMTP_HELP 12
+#define NGX_SMTP_STARTTLS 13
+
+
+#define NGX_MAIL_AUTH_PLAIN 0
+#define NGX_MAIL_AUTH_LOGIN 1
+#define NGX_MAIL_AUTH_LOGIN_USERNAME 2
+#define NGX_MAIL_AUTH_APOP 3
+#define NGX_MAIL_AUTH_CRAM_MD5 4
+#define NGX_MAIL_AUTH_EXTERNAL 5
+#define NGX_MAIL_AUTH_NONE 6
+
+
+#define NGX_MAIL_AUTH_PLAIN_ENABLED 0x0002
+#define NGX_MAIL_AUTH_LOGIN_ENABLED 0x0004
+#define NGX_MAIL_AUTH_APOP_ENABLED 0x0008
+#define NGX_MAIL_AUTH_CRAM_MD5_ENABLED 0x0010
+#define NGX_MAIL_AUTH_EXTERNAL_ENABLED 0x0020
+#define NGX_MAIL_AUTH_NONE_ENABLED 0x0040
+
+
+#define NGX_MAIL_PARSE_INVALID_COMMAND 20
+
+
+typedef void (*ngx_mail_init_session_pt)(ngx_mail_session_t *s,
+ ngx_connection_t *c);
+typedef void (*ngx_mail_init_protocol_pt)(ngx_event_t *rev);
+typedef void (*ngx_mail_auth_state_pt)(ngx_event_t *rev);
+typedef ngx_int_t (*ngx_mail_parse_command_pt)(ngx_mail_session_t *s);
+
+
+struct ngx_mail_protocol_s {
+ ngx_str_t name;
+ in_port_t port[4];
+ ngx_uint_t type;
+
+ ngx_mail_init_session_pt init_session;
+ ngx_mail_init_protocol_pt init_protocol;
+ ngx_mail_parse_command_pt parse_command;
+ ngx_mail_auth_state_pt auth_state;
+
+ ngx_str_t internal_server_error;
+ ngx_str_t cert_error;
+ ngx_str_t no_cert;
+};
+
+
+typedef struct {
+ ngx_mail_protocol_t *protocol;
+
+ void *(*create_main_conf)(ngx_conf_t *cf);
+ char *(*init_main_conf)(ngx_conf_t *cf, void *conf);
+
+ void *(*create_srv_conf)(ngx_conf_t *cf);
+ char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev,
+ void *conf);
+} ngx_mail_module_t;
+
+
+#define NGX_MAIL_MODULE 0x4C49414D /* "MAIL" */
+
+#define NGX_MAIL_MAIN_CONF 0x02000000
+#define NGX_MAIL_SRV_CONF 0x04000000
+
+
+#define NGX_MAIL_MAIN_CONF_OFFSET offsetof(ngx_mail_conf_ctx_t, main_conf)
+#define NGX_MAIL_SRV_CONF_OFFSET offsetof(ngx_mail_conf_ctx_t, srv_conf)
+
+
+#define ngx_mail_get_module_ctx(s, module) (s)->ctx[module.ctx_index]
+#define ngx_mail_set_ctx(s, c, module) s->ctx[module.ctx_index] = c;
+#define ngx_mail_delete_ctx(s, module) s->ctx[module.ctx_index] = NULL;
+
+
+#define ngx_mail_get_module_main_conf(s, module) \
+ (s)->main_conf[module.ctx_index]
+#define ngx_mail_get_module_srv_conf(s, module) (s)->srv_conf[module.ctx_index]
+
+#define ngx_mail_conf_get_module_main_conf(cf, module) \
+ ((ngx_mail_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index]
+#define ngx_mail_conf_get_module_srv_conf(cf, module) \
+ ((ngx_mail_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index]
+
+
+#if (NGX_MAIL_SSL)
+void ngx_mail_starttls_handler(ngx_event_t *rev);
+ngx_int_t ngx_mail_starttls_only(ngx_mail_session_t *s, ngx_connection_t *c);
+#endif
+
+
+void ngx_mail_init_connection(ngx_connection_t *c);
+
+ngx_int_t ngx_mail_salt(ngx_mail_session_t *s, ngx_connection_t *c,
+ ngx_mail_core_srv_conf_t *cscf);
+ngx_int_t ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c,
+ ngx_uint_t n);
+ngx_int_t ngx_mail_auth_login_username(ngx_mail_session_t *s,
+ ngx_connection_t *c, ngx_uint_t n);
+ngx_int_t ngx_mail_auth_login_password(ngx_mail_session_t *s,
+ ngx_connection_t *c);
+ngx_int_t ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s,
+ ngx_connection_t *c, char *prefix, size_t len);
+ngx_int_t ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c);
+ngx_int_t ngx_mail_auth_external(ngx_mail_session_t *s, ngx_connection_t *c,
+ ngx_uint_t n);
+ngx_int_t ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c);
+
+void ngx_mail_send(ngx_event_t *wev);
+ngx_int_t ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c);
+void ngx_mail_auth(ngx_mail_session_t *s, ngx_connection_t *c);
+void ngx_mail_close_connection(ngx_connection_t *c);
+void ngx_mail_session_internal_server_error(ngx_mail_session_t *s);
+u_char *ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len);
+
+
+char *ngx_mail_capabilities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+
+
+/* STUB */
+void ngx_mail_proxy_init(ngx_mail_session_t *s, ngx_addr_t *peer);
+void ngx_mail_auth_http_init(ngx_mail_session_t *s);
+/**/
+
+
+extern ngx_uint_t ngx_mail_max_module;
+extern ngx_module_t ngx_mail_core_module;
+
+
+#endif /* _NGX_MAIL_H_INCLUDED_ */
diff --git a/src/nginx-1.18.0/src/mail/ngx_mail_auth_http_module.c b/src/nginx-1.18.0/src/mail/ngx_mail_auth_http_module.c
new file mode 100644
index 0000000..6b57358
--- /dev/null
+++ b/src/nginx-1.18.0/src/mail/ngx_mail_auth_http_module.c
@@ -0,0 +1,1574 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+#include
+#include
+#include
+
+
+typedef struct {
+ ngx_addr_t *peer;
+
+ ngx_msec_t timeout;
+ ngx_flag_t pass_client_cert;
+
+ ngx_str_t host_header;
+ ngx_str_t uri;
+ ngx_str_t header;
+
+ ngx_array_t *headers;
+
+ u_char *file;
+ ngx_uint_t line;
+} ngx_mail_auth_http_conf_t;
+
+
+typedef struct ngx_mail_auth_http_ctx_s ngx_mail_auth_http_ctx_t;
+
+typedef void (*ngx_mail_auth_http_handler_pt)(ngx_mail_session_t *s,
+ ngx_mail_auth_http_ctx_t *ctx);
+
+struct ngx_mail_auth_http_ctx_s {
+ ngx_buf_t *request;
+ ngx_buf_t *response;
+ ngx_peer_connection_t peer;
+
+ ngx_mail_auth_http_handler_pt handler;
+
+ ngx_uint_t state;
+
+ u_char *header_name_start;
+ u_char *header_name_end;
+ u_char *header_start;
+ u_char *header_end;
+
+ ngx_str_t addr;
+ ngx_str_t port;
+ ngx_str_t err;
+ ngx_str_t errmsg;
+ ngx_str_t errcode;
+
+ time_t sleep;
+
+ ngx_pool_t *pool;
+};
+
+
+static void ngx_mail_auth_http_write_handler(ngx_event_t *wev);
+static void ngx_mail_auth_http_read_handler(ngx_event_t *rev);
+static void ngx_mail_auth_http_ignore_status_line(ngx_mail_session_t *s,
+ ngx_mail_auth_http_ctx_t *ctx);
+static void ngx_mail_auth_http_process_headers(ngx_mail_session_t *s,
+ ngx_mail_auth_http_ctx_t *ctx);
+static void ngx_mail_auth_sleep_handler(ngx_event_t *rev);
+static ngx_int_t ngx_mail_auth_http_parse_header_line(ngx_mail_session_t *s,
+ ngx_mail_auth_http_ctx_t *ctx);
+static void ngx_mail_auth_http_block_read(ngx_event_t *rev);
+static void ngx_mail_auth_http_dummy_handler(ngx_event_t *ev);
+static ngx_buf_t *ngx_mail_auth_http_create_request(ngx_mail_session_t *s,
+ ngx_pool_t *pool, ngx_mail_auth_http_conf_t *ahcf);
+static ngx_int_t ngx_mail_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text,
+ ngx_str_t *escaped);
+
+static void *ngx_mail_auth_http_create_conf(ngx_conf_t *cf);
+static char *ngx_mail_auth_http_merge_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static char *ngx_mail_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static char *ngx_mail_auth_http_header(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_command_t ngx_mail_auth_http_commands[] = {
+
+ { ngx_string("auth_http"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_mail_auth_http,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("auth_http_timeout"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_auth_http_conf_t, timeout),
+ NULL },
+
+ { ngx_string("auth_http_header"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE2,
+ ngx_mail_auth_http_header,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("auth_http_pass_client_cert"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_auth_http_conf_t, pass_client_cert),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_mail_module_t ngx_mail_auth_http_module_ctx = {
+ NULL, /* protocol */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_mail_auth_http_create_conf, /* create server configuration */
+ ngx_mail_auth_http_merge_conf /* merge server configuration */
+};
+
+
+ngx_module_t ngx_mail_auth_http_module = {
+ NGX_MODULE_V1,
+ &ngx_mail_auth_http_module_ctx, /* module context */
+ ngx_mail_auth_http_commands, /* module directives */
+ NGX_MAIL_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_str_t ngx_mail_auth_http_method[] = {
+ ngx_string("plain"),
+ ngx_string("plain"),
+ ngx_string("plain"),
+ ngx_string("apop"),
+ ngx_string("cram-md5"),
+ ngx_string("external"),
+ ngx_string("none")
+};
+
+static ngx_str_t ngx_mail_smtp_errcode = ngx_string("535 5.7.0");
+
+
+void
+ngx_mail_auth_http_init(ngx_mail_session_t *s)
+{
+ ngx_int_t rc;
+ ngx_pool_t *pool;
+ ngx_mail_auth_http_ctx_t *ctx;
+ ngx_mail_auth_http_conf_t *ahcf;
+
+ s->connection->log->action = "in http auth state";
+
+ pool = ngx_create_pool(2048, s->connection->log);
+ if (pool == NULL) {
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ ctx = ngx_pcalloc(pool, sizeof(ngx_mail_auth_http_ctx_t));
+ if (ctx == NULL) {
+ ngx_destroy_pool(pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ ctx->pool = pool;
+
+ ahcf = ngx_mail_get_module_srv_conf(s, ngx_mail_auth_http_module);
+
+ ctx->request = ngx_mail_auth_http_create_request(s, pool, ahcf);
+ if (ctx->request == NULL) {
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ ngx_mail_set_ctx(s, ctx, ngx_mail_auth_http_module);
+
+ ctx->peer.sockaddr = ahcf->peer->sockaddr;
+ ctx->peer.socklen = ahcf->peer->socklen;
+ ctx->peer.name = &ahcf->peer->name;
+ ctx->peer.get = ngx_event_get_peer;
+ ctx->peer.log = s->connection->log;
+ ctx->peer.log_error = NGX_ERROR_ERR;
+
+ rc = ngx_event_connect_peer(&ctx->peer);
+
+ if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
+ if (ctx->peer.connection) {
+ ngx_close_connection(ctx->peer.connection);
+ }
+
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ ctx->peer.connection->data = s;
+ ctx->peer.connection->pool = s->connection->pool;
+
+ s->connection->read->handler = ngx_mail_auth_http_block_read;
+ ctx->peer.connection->read->handler = ngx_mail_auth_http_read_handler;
+ ctx->peer.connection->write->handler = ngx_mail_auth_http_write_handler;
+
+ ctx->handler = ngx_mail_auth_http_ignore_status_line;
+
+ ngx_add_timer(ctx->peer.connection->read, ahcf->timeout);
+ ngx_add_timer(ctx->peer.connection->write, ahcf->timeout);
+
+ if (rc == NGX_OK) {
+ ngx_mail_auth_http_write_handler(ctx->peer.connection->write);
+ return;
+ }
+}
+
+
+static void
+ngx_mail_auth_http_write_handler(ngx_event_t *wev)
+{
+ ssize_t n, size;
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_auth_http_ctx_t *ctx;
+ ngx_mail_auth_http_conf_t *ahcf;
+
+ c = wev->data;
+ s = c->data;
+
+ ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0,
+ "mail auth http write handler");
+
+ if (wev->timedout) {
+ ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT,
+ "auth http server %V timed out", ctx->peer.name);
+ ngx_close_connection(c);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ size = ctx->request->last - ctx->request->pos;
+
+ n = ngx_send(c, ctx->request->pos, size);
+
+ if (n == NGX_ERROR) {
+ ngx_close_connection(c);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ if (n > 0) {
+ ctx->request->pos += n;
+
+ if (n == size) {
+ wev->handler = ngx_mail_auth_http_dummy_handler;
+
+ if (wev->timer_set) {
+ ngx_del_timer(wev);
+ }
+
+ if (ngx_handle_write_event(wev, 0) != NGX_OK) {
+ ngx_close_connection(c);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ }
+
+ return;
+ }
+ }
+
+ if (!wev->timer_set) {
+ ahcf = ngx_mail_get_module_srv_conf(s, ngx_mail_auth_http_module);
+ ngx_add_timer(wev, ahcf->timeout);
+ }
+}
+
+
+static void
+ngx_mail_auth_http_read_handler(ngx_event_t *rev)
+{
+ ssize_t n, size;
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_auth_http_ctx_t *ctx;
+
+ c = rev->data;
+ s = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+ "mail auth http read handler");
+
+ ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,
+ "auth http server %V timed out", ctx->peer.name);
+ ngx_close_connection(c);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ if (ctx->response == NULL) {
+ ctx->response = ngx_create_temp_buf(ctx->pool, 1024);
+ if (ctx->response == NULL) {
+ ngx_close_connection(c);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+ }
+
+ size = ctx->response->end - ctx->response->last;
+
+ n = ngx_recv(c, ctx->response->pos, size);
+
+ if (n > 0) {
+ ctx->response->last += n;
+
+ ctx->handler(s, ctx);
+ return;
+ }
+
+ if (n == NGX_AGAIN) {
+ return;
+ }
+
+ ngx_close_connection(c);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+}
+
+
+static void
+ngx_mail_auth_http_ignore_status_line(ngx_mail_session_t *s,
+ ngx_mail_auth_http_ctx_t *ctx)
+{
+ u_char *p, ch;
+ enum {
+ sw_start = 0,
+ sw_H,
+ sw_HT,
+ sw_HTT,
+ sw_HTTP,
+ sw_skip,
+ sw_almost_done
+ } state;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
+ "mail auth http process status line");
+
+ state = ctx->state;
+
+ for (p = ctx->response->pos; p < ctx->response->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* "HTTP/" */
+ case sw_start:
+ if (ch == 'H') {
+ state = sw_H;
+ break;
+ }
+ goto next;
+
+ case sw_H:
+ if (ch == 'T') {
+ state = sw_HT;
+ break;
+ }
+ goto next;
+
+ case sw_HT:
+ if (ch == 'T') {
+ state = sw_HTT;
+ break;
+ }
+ goto next;
+
+ case sw_HTT:
+ if (ch == 'P') {
+ state = sw_HTTP;
+ break;
+ }
+ goto next;
+
+ case sw_HTTP:
+ if (ch == '/') {
+ state = sw_skip;
+ break;
+ }
+ goto next;
+
+ /* any text until end of line */
+ case sw_skip:
+ switch (ch) {
+ case CR:
+ state = sw_almost_done;
+
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+
+ /* end of status line */
+ case sw_almost_done:
+ if (ch == LF) {
+ goto done;
+ }
+
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server %V sent invalid response",
+ ctx->peer.name);
+ ngx_close_connection(ctx->peer.connection);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+ }
+
+ ctx->response->pos = p;
+ ctx->state = state;
+
+ return;
+
+next:
+
+ p = ctx->response->start - 1;
+
+done:
+
+ ctx->response->pos = p + 1;
+ ctx->state = 0;
+ ctx->handler = ngx_mail_auth_http_process_headers;
+ ctx->handler(s, ctx);
+}
+
+
+static void
+ngx_mail_auth_http_process_headers(ngx_mail_session_t *s,
+ ngx_mail_auth_http_ctx_t *ctx)
+{
+ u_char *p;
+ time_t timer;
+ size_t len, size;
+ ngx_int_t rc, port, n;
+ ngx_addr_t *peer;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
+ "mail auth http process headers");
+
+ for ( ;; ) {
+ rc = ngx_mail_auth_http_parse_header_line(s, ctx);
+
+ if (rc == NGX_OK) {
+
+#if (NGX_DEBUG)
+ {
+ ngx_str_t key, value;
+
+ key.len = ctx->header_name_end - ctx->header_name_start;
+ key.data = ctx->header_name_start;
+ value.len = ctx->header_end - ctx->header_start;
+ value.data = ctx->header_start;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
+ "mail auth http header: \"%V: %V\"",
+ &key, &value);
+ }
+#endif
+
+ len = ctx->header_name_end - ctx->header_name_start;
+
+ if (len == sizeof("Auth-Status") - 1
+ && ngx_strncasecmp(ctx->header_name_start,
+ (u_char *) "Auth-Status",
+ sizeof("Auth-Status") - 1)
+ == 0)
+ {
+ len = ctx->header_end - ctx->header_start;
+
+ if (len == 2
+ && ctx->header_start[0] == 'O'
+ && ctx->header_start[1] == 'K')
+ {
+ continue;
+ }
+
+ if (len == 4
+ && ctx->header_start[0] == 'W'
+ && ctx->header_start[1] == 'A'
+ && ctx->header_start[2] == 'I'
+ && ctx->header_start[3] == 'T')
+ {
+ s->auth_wait = 1;
+ continue;
+ }
+
+ ctx->errmsg.len = len;
+ ctx->errmsg.data = ctx->header_start;
+
+ switch (s->protocol) {
+
+ case NGX_MAIL_POP3_PROTOCOL:
+ size = sizeof("-ERR ") - 1 + len + sizeof(CRLF) - 1;
+ break;
+
+ case NGX_MAIL_IMAP_PROTOCOL:
+ size = s->tag.len + sizeof("NO ") - 1 + len
+ + sizeof(CRLF) - 1;
+ break;
+
+ default: /* NGX_MAIL_SMTP_PROTOCOL */
+ ctx->err = ctx->errmsg;
+ continue;
+ }
+
+ p = ngx_pnalloc(s->connection->pool, size);
+ if (p == NULL) {
+ ngx_close_connection(ctx->peer.connection);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ ctx->err.data = p;
+
+ switch (s->protocol) {
+
+ case NGX_MAIL_POP3_PROTOCOL:
+ *p++ = '-'; *p++ = 'E'; *p++ = 'R'; *p++ = 'R'; *p++ = ' ';
+ break;
+
+ case NGX_MAIL_IMAP_PROTOCOL:
+ p = ngx_cpymem(p, s->tag.data, s->tag.len);
+ *p++ = 'N'; *p++ = 'O'; *p++ = ' ';
+ break;
+
+ default: /* NGX_MAIL_SMTP_PROTOCOL */
+ break;
+ }
+
+ p = ngx_cpymem(p, ctx->header_start, len);
+ *p++ = CR; *p++ = LF;
+
+ ctx->err.len = p - ctx->err.data;
+
+ continue;
+ }
+
+ if (len == sizeof("Auth-Server") - 1
+ && ngx_strncasecmp(ctx->header_name_start,
+ (u_char *) "Auth-Server",
+ sizeof("Auth-Server") - 1)
+ == 0)
+ {
+ ctx->addr.len = ctx->header_end - ctx->header_start;
+ ctx->addr.data = ctx->header_start;
+
+ continue;
+ }
+
+ if (len == sizeof("Auth-Port") - 1
+ && ngx_strncasecmp(ctx->header_name_start,
+ (u_char *) "Auth-Port",
+ sizeof("Auth-Port") - 1)
+ == 0)
+ {
+ ctx->port.len = ctx->header_end - ctx->header_start;
+ ctx->port.data = ctx->header_start;
+
+ continue;
+ }
+
+ if (len == sizeof("Auth-User") - 1
+ && ngx_strncasecmp(ctx->header_name_start,
+ (u_char *) "Auth-User",
+ sizeof("Auth-User") - 1)
+ == 0)
+ {
+ s->login.len = ctx->header_end - ctx->header_start;
+
+ s->login.data = ngx_pnalloc(s->connection->pool, s->login.len);
+ if (s->login.data == NULL) {
+ ngx_close_connection(ctx->peer.connection);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ ngx_memcpy(s->login.data, ctx->header_start, s->login.len);
+
+ continue;
+ }
+
+ if (len == sizeof("Auth-Pass") - 1
+ && ngx_strncasecmp(ctx->header_name_start,
+ (u_char *) "Auth-Pass",
+ sizeof("Auth-Pass") - 1)
+ == 0)
+ {
+ s->passwd.len = ctx->header_end - ctx->header_start;
+
+ s->passwd.data = ngx_pnalloc(s->connection->pool,
+ s->passwd.len);
+ if (s->passwd.data == NULL) {
+ ngx_close_connection(ctx->peer.connection);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ ngx_memcpy(s->passwd.data, ctx->header_start, s->passwd.len);
+
+ continue;
+ }
+
+ if (len == sizeof("Auth-Wait") - 1
+ && ngx_strncasecmp(ctx->header_name_start,
+ (u_char *) "Auth-Wait",
+ sizeof("Auth-Wait") - 1)
+ == 0)
+ {
+ n = ngx_atoi(ctx->header_start,
+ ctx->header_end - ctx->header_start);
+
+ if (n != NGX_ERROR) {
+ ctx->sleep = n;
+ }
+
+ continue;
+ }
+
+ if (len == sizeof("Auth-Error-Code") - 1
+ && ngx_strncasecmp(ctx->header_name_start,
+ (u_char *) "Auth-Error-Code",
+ sizeof("Auth-Error-Code") - 1)
+ == 0)
+ {
+ ctx->errcode.len = ctx->header_end - ctx->header_start;
+
+ ctx->errcode.data = ngx_pnalloc(s->connection->pool,
+ ctx->errcode.len);
+ if (ctx->errcode.data == NULL) {
+ ngx_close_connection(ctx->peer.connection);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ ngx_memcpy(ctx->errcode.data, ctx->header_start,
+ ctx->errcode.len);
+
+ continue;
+ }
+
+ /* ignore other headers */
+
+ continue;
+ }
+
+ if (rc == NGX_DONE) {
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
+ "mail auth http header done");
+
+ ngx_close_connection(ctx->peer.connection);
+
+ if (ctx->err.len) {
+
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "client login failed: \"%V\"", &ctx->errmsg);
+
+ if (s->protocol == NGX_MAIL_SMTP_PROTOCOL) {
+
+ if (ctx->errcode.len == 0) {
+ ctx->errcode = ngx_mail_smtp_errcode;
+ }
+
+ ctx->err.len = ctx->errcode.len + ctx->errmsg.len
+ + sizeof(" " CRLF) - 1;
+
+ p = ngx_pnalloc(s->connection->pool, ctx->err.len);
+ if (p == NULL) {
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ ctx->err.data = p;
+
+ p = ngx_cpymem(p, ctx->errcode.data, ctx->errcode.len);
+ *p++ = ' ';
+ p = ngx_cpymem(p, ctx->errmsg.data, ctx->errmsg.len);
+ *p++ = CR; *p = LF;
+ }
+
+ s->out = ctx->err;
+ timer = ctx->sleep;
+
+ ngx_destroy_pool(ctx->pool);
+
+ if (timer == 0) {
+ s->quit = 1;
+ ngx_mail_send(s->connection->write);
+ return;
+ }
+
+ ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000));
+
+ s->connection->read->handler = ngx_mail_auth_sleep_handler;
+
+ return;
+ }
+
+ if (s->auth_wait) {
+ timer = ctx->sleep;
+
+ ngx_destroy_pool(ctx->pool);
+
+ if (timer == 0) {
+ ngx_mail_auth_http_init(s);
+ return;
+ }
+
+ ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000));
+
+ s->connection->read->handler = ngx_mail_auth_sleep_handler;
+
+ return;
+ }
+
+ if (ctx->addr.len == 0 || ctx->port.len == 0) {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server %V did not send server or port",
+ ctx->peer.name);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ if (s->passwd.data == NULL
+ && s->protocol != NGX_MAIL_SMTP_PROTOCOL)
+ {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server %V did not send password",
+ ctx->peer.name);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ peer = ngx_pcalloc(s->connection->pool, sizeof(ngx_addr_t));
+ if (peer == NULL) {
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ rc = ngx_parse_addr(s->connection->pool, peer,
+ ctx->addr.data, ctx->addr.len);
+
+ switch (rc) {
+ case NGX_OK:
+ break;
+
+ case NGX_DECLINED:
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server %V sent invalid server "
+ "address:\"%V\"",
+ ctx->peer.name, &ctx->addr);
+ /* fall through */
+
+ default:
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ port = ngx_atoi(ctx->port.data, ctx->port.len);
+ if (port == NGX_ERROR || port < 1 || port > 65535) {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server %V sent invalid server "
+ "port:\"%V\"",
+ ctx->peer.name, &ctx->port);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ ngx_inet_set_port(peer->sockaddr, (in_port_t) port);
+
+ len = ctx->addr.len + 1 + ctx->port.len;
+
+ peer->name.len = len;
+
+ peer->name.data = ngx_pnalloc(s->connection->pool, len);
+ if (peer->name.data == NULL) {
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ len = ctx->addr.len;
+
+ ngx_memcpy(peer->name.data, ctx->addr.data, len);
+
+ peer->name.data[len++] = ':';
+
+ ngx_memcpy(peer->name.data + len, ctx->port.data, ctx->port.len);
+
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_proxy_init(s, peer);
+
+ return;
+ }
+
+ if (rc == NGX_AGAIN ) {
+ return;
+ }
+
+ /* rc == NGX_ERROR */
+
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server %V sent invalid header in response",
+ ctx->peer.name);
+ ngx_close_connection(ctx->peer.connection);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+
+ return;
+ }
+}
+
+
+static void
+ngx_mail_auth_sleep_handler(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail auth sleep handler");
+
+ c = rev->data;
+ s = c->data;
+
+ if (rev->timedout) {
+
+ rev->timedout = 0;
+
+ if (s->auth_wait) {
+ s->auth_wait = 0;
+ ngx_mail_auth_http_init(s);
+ return;
+ }
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ rev->handler = cscf->protocol->auth_state;
+
+ s->mail_state = 0;
+ s->auth_method = NGX_MAIL_AUTH_PLAIN;
+
+ c->log->action = "in auth state";
+
+ ngx_mail_send(c->write);
+
+ if (c->destroyed) {
+ return;
+ }
+
+ ngx_add_timer(rev, cscf->timeout);
+
+ if (rev->ready) {
+ rev->handler(rev);
+ return;
+ }
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ }
+
+ return;
+ }
+
+ if (rev->active) {
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ }
+ }
+}
+
+
+static ngx_int_t
+ngx_mail_auth_http_parse_header_line(ngx_mail_session_t *s,
+ ngx_mail_auth_http_ctx_t *ctx)
+{
+ u_char c, ch, *p;
+ enum {
+ sw_start = 0,
+ sw_name,
+ sw_space_before_value,
+ sw_value,
+ sw_space_after_value,
+ sw_almost_done,
+ sw_header_almost_done
+ } state;
+
+ state = ctx->state;
+
+ for (p = ctx->response->pos; p < ctx->response->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* first char */
+ case sw_start:
+
+ switch (ch) {
+ case CR:
+ ctx->header_end = p;
+ state = sw_header_almost_done;
+ break;
+ case LF:
+ ctx->header_end = p;
+ goto header_done;
+ default:
+ state = sw_name;
+ ctx->header_name_start = p;
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ break;
+ }
+
+ return NGX_ERROR;
+ }
+ break;
+
+ /* header name */
+ case sw_name:
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ break;
+ }
+
+ if (ch == ':') {
+ ctx->header_name_end = p;
+ state = sw_space_before_value;
+ break;
+ }
+
+ if (ch == '-') {
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ break;
+ }
+
+ if (ch == CR) {
+ ctx->header_name_end = p;
+ ctx->header_start = p;
+ ctx->header_end = p;
+ state = sw_almost_done;
+ break;
+ }
+
+ if (ch == LF) {
+ ctx->header_name_end = p;
+ ctx->header_start = p;
+ ctx->header_end = p;
+ goto done;
+ }
+
+ return NGX_ERROR;
+
+ /* space* before header value */
+ case sw_space_before_value:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ ctx->header_start = p;
+ ctx->header_end = p;
+ state = sw_almost_done;
+ break;
+ case LF:
+ ctx->header_start = p;
+ ctx->header_end = p;
+ goto done;
+ default:
+ ctx->header_start = p;
+ state = sw_value;
+ break;
+ }
+ break;
+
+ /* header value */
+ case sw_value:
+ switch (ch) {
+ case ' ':
+ ctx->header_end = p;
+ state = sw_space_after_value;
+ break;
+ case CR:
+ ctx->header_end = p;
+ state = sw_almost_done;
+ break;
+ case LF:
+ ctx->header_end = p;
+ goto done;
+ }
+ break;
+
+ /* space* before end of header line */
+ case sw_space_after_value:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ default:
+ state = sw_value;
+ break;
+ }
+ break;
+
+ /* end of header line */
+ case sw_almost_done:
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ return NGX_ERROR;
+ }
+
+ /* end of header */
+ case sw_header_almost_done:
+ switch (ch) {
+ case LF:
+ goto header_done;
+ default:
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ ctx->response->pos = p;
+ ctx->state = state;
+
+ return NGX_AGAIN;
+
+done:
+
+ ctx->response->pos = p + 1;
+ ctx->state = sw_start;
+
+ return NGX_OK;
+
+header_done:
+
+ ctx->response->pos = p + 1;
+ ctx->state = sw_start;
+
+ return NGX_DONE;
+}
+
+
+static void
+ngx_mail_auth_http_block_read(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_auth_http_ctx_t *ctx;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+ "mail auth http block read");
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ c = rev->data;
+ s = c->data;
+
+ ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);
+
+ ngx_close_connection(ctx->peer.connection);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ }
+}
+
+
+static void
+ngx_mail_auth_http_dummy_handler(ngx_event_t *ev)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, ev->log, 0,
+ "mail auth http dummy handler");
+}
+
+
+static ngx_buf_t *
+ngx_mail_auth_http_create_request(ngx_mail_session_t *s, ngx_pool_t *pool,
+ ngx_mail_auth_http_conf_t *ahcf)
+{
+ size_t len;
+ ngx_buf_t *b;
+ ngx_str_t login, passwd;
+#if (NGX_MAIL_SSL)
+ ngx_str_t verify, subject, issuer, serial, fingerprint,
+ raw_cert, cert;
+ ngx_connection_t *c;
+ ngx_mail_ssl_conf_t *sslcf;
+#endif
+ ngx_mail_core_srv_conf_t *cscf;
+
+ if (ngx_mail_auth_http_escape(pool, &s->login, &login) != NGX_OK) {
+ return NULL;
+ }
+
+ if (ngx_mail_auth_http_escape(pool, &s->passwd, &passwd) != NGX_OK) {
+ return NULL;
+ }
+
+#if (NGX_MAIL_SSL)
+
+ c = s->connection;
+ sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+
+ if (c->ssl && sslcf->verify) {
+
+ /* certificate details */
+
+ if (ngx_ssl_get_client_verify(c, pool, &verify) != NGX_OK) {
+ return NULL;
+ }
+
+ if (ngx_ssl_get_subject_dn(c, pool, &subject) != NGX_OK) {
+ return NULL;
+ }
+
+ if (ngx_ssl_get_issuer_dn(c, pool, &issuer) != NGX_OK) {
+ return NULL;
+ }
+
+ if (ngx_ssl_get_serial_number(c, pool, &serial) != NGX_OK) {
+ return NULL;
+ }
+
+ if (ngx_ssl_get_fingerprint(c, pool, &fingerprint) != NGX_OK) {
+ return NULL;
+ }
+
+ if (ahcf->pass_client_cert) {
+
+ /* certificate itself, if configured */
+
+ if (ngx_ssl_get_raw_certificate(c, pool, &raw_cert) != NGX_OK) {
+ return NULL;
+ }
+
+ if (ngx_mail_auth_http_escape(pool, &raw_cert, &cert) != NGX_OK) {
+ return NULL;
+ }
+
+ } else {
+ ngx_str_null(&cert);
+ }
+
+ } else {
+ ngx_str_null(&verify);
+ ngx_str_null(&subject);
+ ngx_str_null(&issuer);
+ ngx_str_null(&serial);
+ ngx_str_null(&fingerprint);
+ ngx_str_null(&cert);
+ }
+
+#endif
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ len = sizeof("GET ") - 1 + ahcf->uri.len + sizeof(" HTTP/1.0" CRLF) - 1
+ + sizeof("Host: ") - 1 + ahcf->host_header.len + sizeof(CRLF) - 1
+ + sizeof("Auth-Method: ") - 1
+ + ngx_mail_auth_http_method[s->auth_method].len
+ + sizeof(CRLF) - 1
+ + sizeof("Auth-User: ") - 1 + login.len + sizeof(CRLF) - 1
+ + sizeof("Auth-Pass: ") - 1 + passwd.len + sizeof(CRLF) - 1
+ + sizeof("Auth-Salt: ") - 1 + s->salt.len
+ + sizeof("Auth-Protocol: ") - 1 + cscf->protocol->name.len
+ + sizeof(CRLF) - 1
+ + sizeof("Auth-Login-Attempt: ") - 1 + NGX_INT_T_LEN
+ + sizeof(CRLF) - 1
+ + sizeof("Client-IP: ") - 1 + s->connection->addr_text.len
+ + sizeof(CRLF) - 1
+ + sizeof("Client-Host: ") - 1 + s->host.len + sizeof(CRLF) - 1
+ + sizeof("Auth-SMTP-Helo: ") - 1 + s->smtp_helo.len + sizeof(CRLF) - 1
+ + sizeof("Auth-SMTP-From: ") - 1 + s->smtp_from.len + sizeof(CRLF) - 1
+ + sizeof("Auth-SMTP-To: ") - 1 + s->smtp_to.len + sizeof(CRLF) - 1
+#if (NGX_MAIL_SSL)
+ + sizeof("Auth-SSL: on" CRLF) - 1
+ + sizeof("Auth-SSL-Verify: ") - 1 + verify.len + sizeof(CRLF) - 1
+ + sizeof("Auth-SSL-Subject: ") - 1 + subject.len + sizeof(CRLF) - 1
+ + sizeof("Auth-SSL-Issuer: ") - 1 + issuer.len + sizeof(CRLF) - 1
+ + sizeof("Auth-SSL-Serial: ") - 1 + serial.len + sizeof(CRLF) - 1
+ + sizeof("Auth-SSL-Fingerprint: ") - 1 + fingerprint.len
+ + sizeof(CRLF) - 1
+ + sizeof("Auth-SSL-Cert: ") - 1 + cert.len + sizeof(CRLF) - 1
+#endif
+ + ahcf->header.len
+ + sizeof(CRLF) - 1;
+
+ b = ngx_create_temp_buf(pool, len);
+ if (b == NULL) {
+ return NULL;
+ }
+
+ b->last = ngx_cpymem(b->last, "GET ", sizeof("GET ") - 1);
+ b->last = ngx_copy(b->last, ahcf->uri.data, ahcf->uri.len);
+ b->last = ngx_cpymem(b->last, " HTTP/1.0" CRLF,
+ sizeof(" HTTP/1.0" CRLF) - 1);
+
+ b->last = ngx_cpymem(b->last, "Host: ", sizeof("Host: ") - 1);
+ b->last = ngx_copy(b->last, ahcf->host_header.data,
+ ahcf->host_header.len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ b->last = ngx_cpymem(b->last, "Auth-Method: ",
+ sizeof("Auth-Method: ") - 1);
+ b->last = ngx_cpymem(b->last,
+ ngx_mail_auth_http_method[s->auth_method].data,
+ ngx_mail_auth_http_method[s->auth_method].len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ b->last = ngx_cpymem(b->last, "Auth-User: ", sizeof("Auth-User: ") - 1);
+ b->last = ngx_copy(b->last, login.data, login.len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ b->last = ngx_cpymem(b->last, "Auth-Pass: ", sizeof("Auth-Pass: ") - 1);
+ b->last = ngx_copy(b->last, passwd.data, passwd.len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ if (s->auth_method != NGX_MAIL_AUTH_PLAIN && s->salt.len) {
+ b->last = ngx_cpymem(b->last, "Auth-Salt: ", sizeof("Auth-Salt: ") - 1);
+ b->last = ngx_copy(b->last, s->salt.data, s->salt.len);
+
+ s->passwd.data = NULL;
+ }
+
+ b->last = ngx_cpymem(b->last, "Auth-Protocol: ",
+ sizeof("Auth-Protocol: ") - 1);
+ b->last = ngx_cpymem(b->last, cscf->protocol->name.data,
+ cscf->protocol->name.len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ b->last = ngx_sprintf(b->last, "Auth-Login-Attempt: %ui" CRLF,
+ s->login_attempt);
+
+ b->last = ngx_cpymem(b->last, "Client-IP: ", sizeof("Client-IP: ") - 1);
+ b->last = ngx_copy(b->last, s->connection->addr_text.data,
+ s->connection->addr_text.len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ if (s->host.len) {
+ b->last = ngx_cpymem(b->last, "Client-Host: ",
+ sizeof("Client-Host: ") - 1);
+ b->last = ngx_copy(b->last, s->host.data, s->host.len);
+ *b->last++ = CR; *b->last++ = LF;
+ }
+
+ if (s->auth_method == NGX_MAIL_AUTH_NONE) {
+
+ /* HELO, MAIL FROM, and RCPT TO can't contain CRLF, no need to escape */
+
+ b->last = ngx_cpymem(b->last, "Auth-SMTP-Helo: ",
+ sizeof("Auth-SMTP-Helo: ") - 1);
+ b->last = ngx_copy(b->last, s->smtp_helo.data, s->smtp_helo.len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ b->last = ngx_cpymem(b->last, "Auth-SMTP-From: ",
+ sizeof("Auth-SMTP-From: ") - 1);
+ b->last = ngx_copy(b->last, s->smtp_from.data, s->smtp_from.len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ b->last = ngx_cpymem(b->last, "Auth-SMTP-To: ",
+ sizeof("Auth-SMTP-To: ") - 1);
+ b->last = ngx_copy(b->last, s->smtp_to.data, s->smtp_to.len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ }
+
+#if (NGX_MAIL_SSL)
+
+ if (c->ssl) {
+ b->last = ngx_cpymem(b->last, "Auth-SSL: on" CRLF,
+ sizeof("Auth-SSL: on" CRLF) - 1);
+
+ if (verify.len) {
+ b->last = ngx_cpymem(b->last, "Auth-SSL-Verify: ",
+ sizeof("Auth-SSL-Verify: ") - 1);
+ b->last = ngx_copy(b->last, verify.data, verify.len);
+ *b->last++ = CR; *b->last++ = LF;
+ }
+
+ if (subject.len) {
+ b->last = ngx_cpymem(b->last, "Auth-SSL-Subject: ",
+ sizeof("Auth-SSL-Subject: ") - 1);
+ b->last = ngx_copy(b->last, subject.data, subject.len);
+ *b->last++ = CR; *b->last++ = LF;
+ }
+
+ if (issuer.len) {
+ b->last = ngx_cpymem(b->last, "Auth-SSL-Issuer: ",
+ sizeof("Auth-SSL-Issuer: ") - 1);
+ b->last = ngx_copy(b->last, issuer.data, issuer.len);
+ *b->last++ = CR; *b->last++ = LF;
+ }
+
+ if (serial.len) {
+ b->last = ngx_cpymem(b->last, "Auth-SSL-Serial: ",
+ sizeof("Auth-SSL-Serial: ") - 1);
+ b->last = ngx_copy(b->last, serial.data, serial.len);
+ *b->last++ = CR; *b->last++ = LF;
+ }
+
+ if (fingerprint.len) {
+ b->last = ngx_cpymem(b->last, "Auth-SSL-Fingerprint: ",
+ sizeof("Auth-SSL-Fingerprint: ") - 1);
+ b->last = ngx_copy(b->last, fingerprint.data, fingerprint.len);
+ *b->last++ = CR; *b->last++ = LF;
+ }
+
+ if (cert.len) {
+ b->last = ngx_cpymem(b->last, "Auth-SSL-Cert: ",
+ sizeof("Auth-SSL-Cert: ") - 1);
+ b->last = ngx_copy(b->last, cert.data, cert.len);
+ *b->last++ = CR; *b->last++ = LF;
+ }
+ }
+
+#endif
+
+ if (ahcf->header.len) {
+ b->last = ngx_copy(b->last, ahcf->header.data, ahcf->header.len);
+ }
+
+ /* add "\r\n" at the header end */
+ *b->last++ = CR; *b->last++ = LF;
+
+#if (NGX_DEBUG_MAIL_PASSWD)
+ ngx_log_debug2(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
+ "mail auth http header:%N\"%*s\"",
+ (size_t) (b->last - b->pos), b->pos);
+#endif
+
+ return b;
+}
+
+
+static ngx_int_t
+ngx_mail_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text, ngx_str_t *escaped)
+{
+ u_char *p;
+ uintptr_t n;
+
+ n = ngx_escape_uri(NULL, text->data, text->len, NGX_ESCAPE_MAIL_AUTH);
+
+ if (n == 0) {
+ *escaped = *text;
+ return NGX_OK;
+ }
+
+ escaped->len = text->len + n * 2;
+
+ p = ngx_pnalloc(pool, escaped->len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ (void) ngx_escape_uri(p, text->data, text->len, NGX_ESCAPE_MAIL_AUTH);
+
+ escaped->data = p;
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_mail_auth_http_create_conf(ngx_conf_t *cf)
+{
+ ngx_mail_auth_http_conf_t *ahcf;
+
+ ahcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_auth_http_conf_t));
+ if (ahcf == NULL) {
+ return NULL;
+ }
+
+ ahcf->timeout = NGX_CONF_UNSET_MSEC;
+ ahcf->pass_client_cert = NGX_CONF_UNSET;
+
+ ahcf->file = cf->conf_file->file.name.data;
+ ahcf->line = cf->conf_file->line;
+
+ return ahcf;
+}
+
+
+static char *
+ngx_mail_auth_http_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_mail_auth_http_conf_t *prev = parent;
+ ngx_mail_auth_http_conf_t *conf = child;
+
+ u_char *p;
+ size_t len;
+ ngx_uint_t i;
+ ngx_table_elt_t *header;
+
+ if (conf->peer == NULL) {
+ conf->peer = prev->peer;
+ conf->host_header = prev->host_header;
+ conf->uri = prev->uri;
+
+ if (conf->peer == NULL) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no \"auth_http\" is defined for server in %s:%ui",
+ conf->file, conf->line);
+
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);
+
+ ngx_conf_merge_value(conf->pass_client_cert, prev->pass_client_cert, 0);
+
+ if (conf->headers == NULL) {
+ conf->headers = prev->headers;
+ conf->header = prev->header;
+ }
+
+ if (conf->headers && conf->header.len == 0) {
+ len = 0;
+ header = conf->headers->elts;
+ for (i = 0; i < conf->headers->nelts; i++) {
+ len += header[i].key.len + 2 + header[i].value.len + 2;
+ }
+
+ p = ngx_pnalloc(cf->pool, len);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->header.len = len;
+ conf->header.data = p;
+
+ for (i = 0; i < conf->headers->nelts; i++) {
+ p = ngx_cpymem(p, header[i].key.data, header[i].key.len);
+ *p++ = ':'; *p++ = ' ';
+ p = ngx_cpymem(p, header[i].value.data, header[i].value.len);
+ *p++ = CR; *p++ = LF;
+ }
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_mail_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_mail_auth_http_conf_t *ahcf = conf;
+
+ ngx_str_t *value;
+ ngx_url_t u;
+
+ value = cf->args->elts;
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.url = value[1];
+ u.default_port = 80;
+ u.uri_part = 1;
+
+ if (ngx_strncmp(u.url.data, "http://", 7) == 0) {
+ u.url.len -= 7;
+ u.url.data += 7;
+ }
+
+ if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
+ if (u.err) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "%s in auth_http \"%V\"", u.err, &u.url);
+ }
+
+ return NGX_CONF_ERROR;
+ }
+
+ ahcf->peer = u.addrs;
+
+ if (u.family != AF_UNIX) {
+ ahcf->host_header = u.host;
+
+ } else {
+ ngx_str_set(&ahcf->host_header, "localhost");
+ }
+
+ ahcf->uri = u.uri;
+
+ if (ahcf->uri.len == 0) {
+ ngx_str_set(&ahcf->uri, "/");
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_mail_auth_http_header(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_mail_auth_http_conf_t *ahcf = conf;
+
+ ngx_str_t *value;
+ ngx_table_elt_t *header;
+
+ if (ahcf->headers == NULL) {
+ ahcf->headers = ngx_array_create(cf->pool, 1, sizeof(ngx_table_elt_t));
+ if (ahcf->headers == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ header = ngx_array_push(ahcf->headers);
+ if (header == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ value = cf->args->elts;
+
+ header->key = value[1];
+ header->value = value[2];
+
+ return NGX_CONF_OK;
+}
diff --git a/src/nginx-1.18.0/src/mail/ngx_mail_core_module.c b/src/nginx-1.18.0/src/mail/ngx_mail_core_module.c
new file mode 100644
index 0000000..e16d702
--- /dev/null
+++ b/src/nginx-1.18.0/src/mail/ngx_mail_core_module.c
@@ -0,0 +1,678 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+#include
+#include
+
+
+static void *ngx_mail_core_create_main_conf(ngx_conf_t *cf);
+static void *ngx_mail_core_create_srv_conf(ngx_conf_t *cf);
+static char *ngx_mail_core_merge_srv_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static char *ngx_mail_core_server(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_mail_core_protocol(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_mail_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_command_t ngx_mail_core_commands[] = {
+
+ { ngx_string("server"),
+ NGX_MAIL_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+ ngx_mail_core_server,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("listen"),
+ NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+ ngx_mail_core_listen,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("protocol"),
+ NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_mail_core_protocol,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("timeout"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_core_srv_conf_t, timeout),
+ NULL },
+
+ { ngx_string("server_name"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_core_srv_conf_t, server_name),
+ NULL },
+
+ { ngx_string("error_log"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+ ngx_mail_core_error_log,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("resolver"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+ ngx_mail_core_resolver,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("resolver_timeout"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_core_srv_conf_t, resolver_timeout),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_mail_module_t ngx_mail_core_module_ctx = {
+ NULL, /* protocol */
+
+ ngx_mail_core_create_main_conf, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_mail_core_create_srv_conf, /* create server configuration */
+ ngx_mail_core_merge_srv_conf /* merge server configuration */
+};
+
+
+ngx_module_t ngx_mail_core_module = {
+ NGX_MODULE_V1,
+ &ngx_mail_core_module_ctx, /* module context */
+ ngx_mail_core_commands, /* module directives */
+ NGX_MAIL_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static void *
+ngx_mail_core_create_main_conf(ngx_conf_t *cf)
+{
+ ngx_mail_core_main_conf_t *cmcf;
+
+ cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_core_main_conf_t));
+ if (cmcf == NULL) {
+ return NULL;
+ }
+
+ if (ngx_array_init(&cmcf->servers, cf->pool, 4,
+ sizeof(ngx_mail_core_srv_conf_t *))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ if (ngx_array_init(&cmcf->listen, cf->pool, 4, sizeof(ngx_mail_listen_t))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ return cmcf;
+}
+
+
+static void *
+ngx_mail_core_create_srv_conf(ngx_conf_t *cf)
+{
+ ngx_mail_core_srv_conf_t *cscf;
+
+ cscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_core_srv_conf_t));
+ if (cscf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * cscf->protocol = NULL;
+ * cscf->error_log = NULL;
+ */
+
+ cscf->timeout = NGX_CONF_UNSET_MSEC;
+ cscf->resolver_timeout = NGX_CONF_UNSET_MSEC;
+
+ cscf->resolver = NGX_CONF_UNSET_PTR;
+
+ cscf->file_name = cf->conf_file->file.name.data;
+ cscf->line = cf->conf_file->line;
+
+ return cscf;
+}
+
+
+static char *
+ngx_mail_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_mail_core_srv_conf_t *prev = parent;
+ ngx_mail_core_srv_conf_t *conf = child;
+
+ ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);
+ ngx_conf_merge_msec_value(conf->resolver_timeout, prev->resolver_timeout,
+ 30000);
+
+
+ ngx_conf_merge_str_value(conf->server_name, prev->server_name, "");
+
+ if (conf->server_name.len == 0) {
+ conf->server_name = cf->cycle->hostname;
+ }
+
+ if (conf->protocol == NULL) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "unknown mail protocol for server in %s:%ui",
+ conf->file_name, conf->line);
+ return NGX_CONF_ERROR;
+ }
+
+ if (conf->error_log == NULL) {
+ if (prev->error_log) {
+ conf->error_log = prev->error_log;
+ } else {
+ conf->error_log = &cf->cycle->new_log;
+ }
+ }
+
+ ngx_conf_merge_ptr_value(conf->resolver, prev->resolver, NULL);
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_mail_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *rv;
+ void *mconf;
+ ngx_uint_t m;
+ ngx_conf_t pcf;
+ ngx_mail_module_t *module;
+ ngx_mail_conf_ctx_t *ctx, *mail_ctx;
+ ngx_mail_core_srv_conf_t *cscf, **cscfp;
+ ngx_mail_core_main_conf_t *cmcf;
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_mail_conf_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ mail_ctx = cf->ctx;
+ ctx->main_conf = mail_ctx->main_conf;
+
+ /* the server{}'s srv_conf */
+
+ ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_mail_max_module);
+ if (ctx->srv_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ for (m = 0; cf->cycle->modules[m]; m++) {
+ if (cf->cycle->modules[m]->type != NGX_MAIL_MODULE) {
+ continue;
+ }
+
+ module = cf->cycle->modules[m]->ctx;
+
+ if (module->create_srv_conf) {
+ mconf = module->create_srv_conf(cf);
+ if (mconf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->srv_conf[cf->cycle->modules[m]->ctx_index] = mconf;
+ }
+ }
+
+ /* the server configuration context */
+
+ cscf = ctx->srv_conf[ngx_mail_core_module.ctx_index];
+ cscf->ctx = ctx;
+
+ cmcf = ctx->main_conf[ngx_mail_core_module.ctx_index];
+
+ cscfp = ngx_array_push(&cmcf->servers);
+ if (cscfp == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *cscfp = cscf;
+
+
+ /* parse inside server{} */
+
+ pcf = *cf;
+ cf->ctx = ctx;
+ cf->cmd_type = NGX_MAIL_SRV_CONF;
+
+ rv = ngx_conf_parse(cf, NULL);
+
+ *cf = pcf;
+
+ if (rv == NGX_CONF_OK && !cscf->listen) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no \"listen\" is defined for server in %s:%ui",
+ cscf->file_name, cscf->line);
+ return NGX_CONF_ERROR;
+ }
+
+ return rv;
+}
+
+
+static char *
+ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_mail_core_srv_conf_t *cscf = conf;
+
+ ngx_str_t *value, size;
+ ngx_url_t u;
+ ngx_uint_t i, n, m;
+ ngx_mail_listen_t *ls, *als;
+ ngx_mail_module_t *module;
+ ngx_mail_core_main_conf_t *cmcf;
+
+ cscf->listen = 1;
+
+ value = cf->args->elts;
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.url = value[1];
+ u.listen = 1;
+
+ if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
+ if (u.err) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "%s in \"%V\" of the \"listen\" directive",
+ u.err, &u.url);
+ }
+
+ return NGX_CONF_ERROR;
+ }
+
+ cmcf = ngx_mail_conf_get_module_main_conf(cf, ngx_mail_core_module);
+
+ ls = ngx_array_push_n(&cmcf->listen, u.naddrs);
+ if (ls == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(ls, sizeof(ngx_mail_listen_t));
+
+ ls->backlog = NGX_LISTEN_BACKLOG;
+ ls->rcvbuf = -1;
+ ls->sndbuf = -1;
+ ls->ctx = cf->ctx;
+
+#if (NGX_HAVE_INET6)
+ ls->ipv6only = 1;
+#endif
+
+ if (cscf->protocol == NULL) {
+ for (m = 0; cf->cycle->modules[m]; m++) {
+ if (cf->cycle->modules[m]->type != NGX_MAIL_MODULE) {
+ continue;
+ }
+
+ module = cf->cycle->modules[m]->ctx;
+
+ if (module->protocol == NULL) {
+ continue;
+ }
+
+ for (i = 0; module->protocol->port[i]; i++) {
+ if (module->protocol->port[i] == u.port) {
+ cscf->protocol = module->protocol;
+ break;
+ }
+ }
+ }
+ }
+
+ for (i = 2; i < cf->args->nelts; i++) {
+
+ if (ngx_strcmp(value[i].data, "bind") == 0) {
+ ls->bind = 1;
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "backlog=", 8) == 0) {
+ ls->backlog = ngx_atoi(value[i].data + 8, value[i].len - 8);
+ ls->bind = 1;
+
+ if (ls->backlog == NGX_ERROR || ls->backlog == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid backlog \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "rcvbuf=", 7) == 0) {
+ size.len = value[i].len - 7;
+ size.data = value[i].data + 7;
+
+ ls->rcvbuf = ngx_parse_size(&size);
+ ls->bind = 1;
+
+ if (ls->rcvbuf == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid rcvbuf \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "sndbuf=", 7) == 0) {
+ size.len = value[i].len - 7;
+ size.data = value[i].data + 7;
+
+ ls->sndbuf = ngx_parse_size(&size);
+ ls->bind = 1;
+
+ if (ls->sndbuf == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid sndbuf \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "ipv6only=o", 10) == 0) {
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ if (ngx_strcmp(&value[i].data[10], "n") == 0) {
+ ls->ipv6only = 1;
+
+ } else if (ngx_strcmp(&value[i].data[10], "ff") == 0) {
+ ls->ipv6only = 0;
+
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid ipv6only flags \"%s\"",
+ &value[i].data[9]);
+ return NGX_CONF_ERROR;
+ }
+
+ ls->bind = 1;
+ continue;
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "bind ipv6only is not supported "
+ "on this platform");
+ return NGX_CONF_ERROR;
+#endif
+ }
+
+ if (ngx_strcmp(value[i].data, "ssl") == 0) {
+#if (NGX_MAIL_SSL)
+ ngx_mail_ssl_conf_t *sslcf;
+
+ sslcf = ngx_mail_conf_get_module_srv_conf(cf, ngx_mail_ssl_module);
+
+ sslcf->listen = 1;
+ sslcf->file = cf->conf_file->file.name.data;
+ sslcf->line = cf->conf_file->line;
+
+ ls->ssl = 1;
+
+ continue;
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the \"ssl\" parameter requires "
+ "ngx_mail_ssl_module");
+ return NGX_CONF_ERROR;
+#endif
+ }
+
+ if (ngx_strncmp(value[i].data, "so_keepalive=", 13) == 0) {
+
+ if (ngx_strcmp(&value[i].data[13], "on") == 0) {
+ ls->so_keepalive = 1;
+
+ } else if (ngx_strcmp(&value[i].data[13], "off") == 0) {
+ ls->so_keepalive = 2;
+
+ } else {
+
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+ u_char *p, *end;
+ ngx_str_t s;
+
+ end = value[i].data + value[i].len;
+ s.data = value[i].data + 13;
+
+ p = ngx_strlchr(s.data, end, ':');
+ if (p == NULL) {
+ p = end;
+ }
+
+ if (p > s.data) {
+ s.len = p - s.data;
+
+ ls->tcp_keepidle = ngx_parse_time(&s, 1);
+ if (ls->tcp_keepidle == (time_t) NGX_ERROR) {
+ goto invalid_so_keepalive;
+ }
+ }
+
+ s.data = (p < end) ? (p + 1) : end;
+
+ p = ngx_strlchr(s.data, end, ':');
+ if (p == NULL) {
+ p = end;
+ }
+
+ if (p > s.data) {
+ s.len = p - s.data;
+
+ ls->tcp_keepintvl = ngx_parse_time(&s, 1);
+ if (ls->tcp_keepintvl == (time_t) NGX_ERROR) {
+ goto invalid_so_keepalive;
+ }
+ }
+
+ s.data = (p < end) ? (p + 1) : end;
+
+ if (s.data < end) {
+ s.len = end - s.data;
+
+ ls->tcp_keepcnt = ngx_atoi(s.data, s.len);
+ if (ls->tcp_keepcnt == NGX_ERROR) {
+ goto invalid_so_keepalive;
+ }
+ }
+
+ if (ls->tcp_keepidle == 0 && ls->tcp_keepintvl == 0
+ && ls->tcp_keepcnt == 0)
+ {
+ goto invalid_so_keepalive;
+ }
+
+ ls->so_keepalive = 1;
+
+#else
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the \"so_keepalive\" parameter accepts "
+ "only \"on\" or \"off\" on this platform");
+ return NGX_CONF_ERROR;
+
+#endif
+ }
+
+ ls->bind = 1;
+
+ continue;
+
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+ invalid_so_keepalive:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid so_keepalive value: \"%s\"",
+ &value[i].data[13]);
+ return NGX_CONF_ERROR;
+#endif
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the invalid \"%V\" parameter", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ als = cmcf->listen.elts;
+
+ for (n = 0; n < u.naddrs; n++) {
+ ls[n] = ls[0];
+
+ ls[n].sockaddr = u.addrs[n].sockaddr;
+ ls[n].socklen = u.addrs[n].socklen;
+ ls[n].addr_text = u.addrs[n].name;
+ ls[n].wildcard = ngx_inet_wildcard(ls[n].sockaddr);
+
+ for (i = 0; i < cmcf->listen.nelts - u.naddrs + n; i++) {
+
+ if (ngx_cmp_sockaddr(als[i].sockaddr, als[i].socklen,
+ ls[n].sockaddr, ls[n].socklen, 1)
+ != NGX_OK)
+ {
+ continue;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "duplicate \"%V\" address and port pair",
+ &ls[n].addr_text);
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_mail_core_protocol(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_mail_core_srv_conf_t *cscf = conf;
+
+ ngx_str_t *value;
+ ngx_uint_t m;
+ ngx_mail_module_t *module;
+
+ value = cf->args->elts;
+
+ for (m = 0; cf->cycle->modules[m]; m++) {
+ if (cf->cycle->modules[m]->type != NGX_MAIL_MODULE) {
+ continue;
+ }
+
+ module = cf->cycle->modules[m]->ctx;
+
+ if (module->protocol
+ && ngx_strcmp(module->protocol->name.data, value[1].data) == 0)
+ {
+ cscf->protocol = module->protocol;
+
+ return NGX_CONF_OK;
+ }
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "unknown protocol \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+}
+
+
+static char *
+ngx_mail_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_mail_core_srv_conf_t *cscf = conf;
+
+ return ngx_log_set_log(cf, &cscf->error_log);
+}
+
+
+static char *
+ngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_mail_core_srv_conf_t *cscf = conf;
+
+ ngx_str_t *value;
+
+ value = cf->args->elts;
+
+ if (cscf->resolver != NGX_CONF_UNSET_PTR) {
+ return "is duplicate";
+ }
+
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ cscf->resolver = NULL;
+ return NGX_CONF_OK;
+ }
+
+ cscf->resolver = ngx_resolver_create(cf, &value[1], cf->args->nelts - 1);
+ if (cscf->resolver == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_mail_capabilities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+
+ ngx_str_t *c, *value;
+ ngx_uint_t i;
+ ngx_array_t *a;
+
+ a = (ngx_array_t *) (p + cmd->offset);
+
+ value = cf->args->elts;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+ c = ngx_array_push(a);
+ if (c == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *c = value[i];
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/src/nginx-1.18.0/src/mail/ngx_mail_handler.c b/src/nginx-1.18.0/src/mail/ngx_mail_handler.c
new file mode 100644
index 0000000..803a247
--- /dev/null
+++ b/src/nginx-1.18.0/src/mail/ngx_mail_handler.c
@@ -0,0 +1,884 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+#include
+#include
+
+
+static void ngx_mail_init_session(ngx_connection_t *c);
+
+#if (NGX_MAIL_SSL)
+static void ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c);
+static void ngx_mail_ssl_handshake_handler(ngx_connection_t *c);
+static ngx_int_t ngx_mail_verify_cert(ngx_mail_session_t *s,
+ ngx_connection_t *c);
+#endif
+
+
+void
+ngx_mail_init_connection(ngx_connection_t *c)
+{
+ size_t len;
+ ngx_uint_t i;
+ ngx_mail_port_t *port;
+ struct sockaddr *sa;
+ struct sockaddr_in *sin;
+ ngx_mail_log_ctx_t *ctx;
+ ngx_mail_in_addr_t *addr;
+ ngx_mail_session_t *s;
+ ngx_mail_addr_conf_t *addr_conf;
+ ngx_mail_core_srv_conf_t *cscf;
+ u_char text[NGX_SOCKADDR_STRLEN];
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+ ngx_mail_in6_addr_t *addr6;
+#endif
+
+
+ /* find the server configuration for the address:port */
+
+ port = c->listening->servers;
+
+ if (port->naddrs > 1) {
+
+ /*
+ * There are several addresses on this port and one of them
+ * is the "*:port" wildcard so getsockname() is needed to determine
+ * the server address.
+ *
+ * AcceptEx() already gave this address.
+ */
+
+ if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ sa = c->local_sockaddr;
+
+ switch (sa->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) sa;
+
+ addr6 = port->addrs;
+
+ /* the last address is "*" */
+
+ for (i = 0; i < port->naddrs - 1; i++) {
+ if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {
+ break;
+ }
+ }
+
+ addr_conf = &addr6[i].conf;
+
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) sa;
+
+ addr = port->addrs;
+
+ /* the last address is "*" */
+
+ for (i = 0; i < port->naddrs - 1; i++) {
+ if (addr[i].addr == sin->sin_addr.s_addr) {
+ break;
+ }
+ }
+
+ addr_conf = &addr[i].conf;
+
+ break;
+ }
+
+ } else {
+ switch (c->local_sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ addr6 = port->addrs;
+ addr_conf = &addr6[0].conf;
+ break;
+#endif
+
+ default: /* AF_INET */
+ addr = port->addrs;
+ addr_conf = &addr[0].conf;
+ break;
+ }
+ }
+
+ s = ngx_pcalloc(c->pool, sizeof(ngx_mail_session_t));
+ if (s == NULL) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ s->signature = NGX_MAIL_MODULE;
+
+ s->main_conf = addr_conf->ctx->main_conf;
+ s->srv_conf = addr_conf->ctx->srv_conf;
+
+ s->addr_text = &addr_conf->addr_text;
+
+ c->data = s;
+ s->connection = c;
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ ngx_set_connection_log(c, cscf->error_log);
+
+ len = ngx_sock_ntop(c->sockaddr, c->socklen, text, NGX_SOCKADDR_STRLEN, 1);
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%uA client %*s connected to %V",
+ c->number, len, text, s->addr_text);
+
+ ctx = ngx_palloc(c->pool, sizeof(ngx_mail_log_ctx_t));
+ if (ctx == NULL) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ ctx->client = &c->addr_text;
+ ctx->session = s;
+
+ c->log->connection = c->number;
+ c->log->handler = ngx_mail_log_error;
+ c->log->data = ctx;
+ c->log->action = "sending client greeting line";
+
+ c->log_error = NGX_ERROR_INFO;
+
+#if (NGX_MAIL_SSL)
+ {
+ ngx_mail_ssl_conf_t *sslcf;
+
+ sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+
+ if (sslcf->enable || addr_conf->ssl) {
+ c->log->action = "SSL handshaking";
+
+ ngx_mail_ssl_init_connection(&sslcf->ssl, c);
+ return;
+ }
+
+ }
+#endif
+
+ ngx_mail_init_session(c);
+}
+
+
+#if (NGX_MAIL_SSL)
+
+void
+ngx_mail_starttls_handler(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_ssl_conf_t *sslcf;
+
+ c = rev->data;
+ s = c->data;
+ s->starttls = 1;
+
+ c->log->action = "in starttls state";
+
+ sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+
+ ngx_mail_ssl_init_connection(&sslcf->ssl, c);
+}
+
+
+static void
+ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c)
+{
+ ngx_mail_session_t *s;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ if (ngx_ssl_create_connection(ssl, c, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ if (ngx_ssl_handshake(c) == NGX_AGAIN) {
+
+ s = c->data;
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ ngx_add_timer(c->read, cscf->timeout);
+
+ c->ssl->handler = ngx_mail_ssl_handshake_handler;
+
+ return;
+ }
+
+ ngx_mail_ssl_handshake_handler(c);
+}
+
+
+static void
+ngx_mail_ssl_handshake_handler(ngx_connection_t *c)
+{
+ ngx_mail_session_t *s;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ if (c->ssl->handshaked) {
+
+ s = c->data;
+
+ if (ngx_mail_verify_cert(s, c) != NGX_OK) {
+ return;
+ }
+
+ if (s->starttls) {
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ c->read->handler = cscf->protocol->init_protocol;
+ c->write->handler = ngx_mail_send;
+
+ cscf->protocol->init_protocol(c->read);
+
+ return;
+ }
+
+ c->read->ready = 0;
+
+ ngx_mail_init_session(c);
+ return;
+ }
+
+ ngx_mail_close_connection(c);
+}
+
+
+static ngx_int_t
+ngx_mail_verify_cert(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ long rc;
+ X509 *cert;
+ ngx_mail_ssl_conf_t *sslcf;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+
+ if (!sslcf->verify) {
+ return NGX_OK;
+ }
+
+ rc = SSL_get_verify_result(c->ssl->connection);
+
+ if (rc != X509_V_OK
+ && (sslcf->verify != 3 || !ngx_ssl_verify_error_optional(rc)))
+ {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client SSL certificate verify error: (%l:%s)",
+ rc, X509_verify_cert_error_string(rc));
+
+ ngx_ssl_remove_cached_session(c->ssl->session_ctx,
+ (SSL_get0_session(c->ssl->connection)));
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ s->out = cscf->protocol->cert_error;
+ s->quit = 1;
+
+ c->write->handler = ngx_mail_send;
+
+ ngx_mail_send(s->connection->write);
+ return NGX_ERROR;
+ }
+
+ if (sslcf->verify == 1) {
+ cert = SSL_get_peer_certificate(c->ssl->connection);
+
+ if (cert == NULL) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent no required SSL certificate");
+
+ ngx_ssl_remove_cached_session(c->ssl->session_ctx,
+ (SSL_get0_session(c->ssl->connection)));
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ s->out = cscf->protocol->no_cert;
+ s->quit = 1;
+
+ c->write->handler = ngx_mail_send;
+
+ ngx_mail_send(s->connection->write);
+ return NGX_ERROR;
+ }
+
+ X509_free(cert);
+ }
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static void
+ngx_mail_init_session(ngx_connection_t *c)
+{
+ ngx_mail_session_t *s;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ s = c->data;
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ s->protocol = cscf->protocol->type;
+
+ s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_mail_max_module);
+ if (s->ctx == NULL) {
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ c->write->handler = ngx_mail_send;
+
+ cscf->protocol->init_session(s, c);
+}
+
+
+ngx_int_t
+ngx_mail_salt(ngx_mail_session_t *s, ngx_connection_t *c,
+ ngx_mail_core_srv_conf_t *cscf)
+{
+ s->salt.data = ngx_pnalloc(c->pool,
+ sizeof(" <18446744073709551616.@>" CRLF) - 1
+ + NGX_TIME_T_LEN
+ + cscf->server_name.len);
+ if (s->salt.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ s->salt.len = ngx_sprintf(s->salt.data, "<%ul.%T@%V>" CRLF,
+ ngx_random(), ngx_time(), &cscf->server_name)
+ - s->salt.data;
+
+ return NGX_OK;
+}
+
+
+#if (NGX_MAIL_SSL)
+
+ngx_int_t
+ngx_mail_starttls_only(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_mail_ssl_conf_t *sslcf;
+
+ if (c->ssl) {
+ return 0;
+ }
+
+ sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+
+ if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif
+
+
+ngx_int_t
+ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n)
+{
+ u_char *p, *last;
+ ngx_str_t *arg, plain;
+
+ arg = s->args.elts;
+
+#if (NGX_DEBUG_MAIL_PASSWD)
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth plain: \"%V\"", &arg[n]);
+#endif
+
+ plain.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
+ if (plain.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_decode_base64(&plain, &arg[n]) != NGX_OK) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid base64 encoding in AUTH PLAIN command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ p = plain.data;
+ last = p + plain.len;
+
+ while (p < last && *p++) { /* void */ }
+
+ if (p == last) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid login in AUTH PLAIN command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ s->login.data = p;
+
+ while (p < last && *p) { p++; }
+
+ if (p == last) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid password in AUTH PLAIN command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ s->login.len = p++ - s->login.data;
+
+ s->passwd.len = last - p;
+ s->passwd.data = p;
+
+#if (NGX_DEBUG_MAIL_PASSWD)
+ ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth plain: \"%V\" \"%V\"", &s->login, &s->passwd);
+#endif
+
+ return NGX_DONE;
+}
+
+
+ngx_int_t
+ngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c,
+ ngx_uint_t n)
+{
+ ngx_str_t *arg;
+
+ arg = s->args.elts;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth login username: \"%V\"", &arg[n]);
+
+ s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
+ if (s->login.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_decode_base64(&s->login, &arg[n]) != NGX_OK) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid base64 encoding in AUTH LOGIN command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth login username: \"%V\"", &s->login);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_mail_auth_login_password(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_str_t *arg;
+
+ arg = s->args.elts;
+
+#if (NGX_DEBUG_MAIL_PASSWD)
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth login password: \"%V\"", &arg[0]);
+#endif
+
+ s->passwd.data = ngx_pnalloc(c->pool,
+ ngx_base64_decoded_length(arg[0].len));
+ if (s->passwd.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_decode_base64(&s->passwd, &arg[0]) != NGX_OK) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid base64 encoding in AUTH LOGIN command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+#if (NGX_DEBUG_MAIL_PASSWD)
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth login password: \"%V\"", &s->passwd);
+#endif
+
+ return NGX_DONE;
+}
+
+
+ngx_int_t
+ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s, ngx_connection_t *c,
+ char *prefix, size_t len)
+{
+ u_char *p;
+ ngx_str_t salt;
+ ngx_uint_t n;
+
+ p = ngx_pnalloc(c->pool, len + ngx_base64_encoded_length(s->salt.len) + 2);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ salt.data = ngx_cpymem(p, prefix, len);
+ s->salt.len -= 2;
+
+ ngx_encode_base64(&salt, &s->salt);
+
+ s->salt.len += 2;
+ n = len + salt.len;
+ p[n++] = CR; p[n++] = LF;
+
+ s->out.len = n;
+ s->out.data = p;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ u_char *p, *last;
+ ngx_str_t *arg;
+
+ arg = s->args.elts;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth cram-md5: \"%V\"", &arg[0]);
+
+ s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len));
+ if (s->login.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid base64 encoding in AUTH CRAM-MD5 command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ p = s->login.data;
+ last = p + s->login.len;
+
+ while (p < last) {
+ if (*p++ == ' ') {
+ s->login.len = p - s->login.data - 1;
+ s->passwd.len = last - p;
+ s->passwd.data = p;
+ break;
+ }
+ }
+
+ if (s->passwd.len != 32) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid CRAM-MD5 hash in AUTH CRAM-MD5 command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth cram-md5: \"%V\" \"%V\"", &s->login, &s->passwd);
+
+ s->auth_method = NGX_MAIL_AUTH_CRAM_MD5;
+
+ return NGX_DONE;
+}
+
+
+ngx_int_t
+ngx_mail_auth_external(ngx_mail_session_t *s, ngx_connection_t *c,
+ ngx_uint_t n)
+{
+ ngx_str_t *arg, external;
+
+ arg = s->args.elts;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth external: \"%V\"", &arg[n]);
+
+ external.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
+ if (external.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_decode_base64(&external, &arg[n]) != NGX_OK) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid base64 encoding in AUTH EXTERNAL command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ s->login.len = external.len;
+ s->login.data = external.data;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth external: \"%V\"", &s->login);
+
+ s->auth_method = NGX_MAIL_AUTH_EXTERNAL;
+
+ return NGX_DONE;
+}
+
+
+void
+ngx_mail_send(ngx_event_t *wev)
+{
+ ngx_int_t n;
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ c = wev->data;
+ s = c->data;
+
+ if (wev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ c->timedout = 1;
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ if (s->out.len == 0) {
+ if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ }
+
+ return;
+ }
+
+ n = c->send(c, s->out.data, s->out.len);
+
+ if (n > 0) {
+ s->out.data += n;
+ s->out.len -= n;
+
+ if (s->out.len != 0) {
+ goto again;
+ }
+
+ if (wev->timer_set) {
+ ngx_del_timer(wev);
+ }
+
+ if (s->quit) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ if (s->blocked) {
+ c->read->handler(c->read);
+ }
+
+ return;
+ }
+
+ if (n == NGX_ERROR) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ /* n == NGX_AGAIN */
+
+again:
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ ngx_add_timer(c->write, cscf->timeout);
+
+ if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+}
+
+
+ngx_int_t
+ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ssize_t n;
+ ngx_int_t rc;
+ ngx_str_t l;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last);
+
+ if (n == NGX_ERROR || n == 0) {
+ ngx_mail_close_connection(c);
+ return NGX_ERROR;
+ }
+
+ if (n > 0) {
+ s->buffer->last += n;
+ }
+
+ if (n == NGX_AGAIN) {
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ ngx_mail_session_internal_server_error(s);
+ return NGX_ERROR;
+ }
+
+ if (s->buffer->pos == s->buffer->last) {
+ return NGX_AGAIN;
+ }
+ }
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ rc = cscf->protocol->parse_command(s);
+
+ if (rc == NGX_AGAIN) {
+
+ if (s->buffer->last < s->buffer->end) {
+ return rc;
+ }
+
+ l.len = s->buffer->last - s->buffer->start;
+ l.data = s->buffer->start;
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent too long command \"%V\"", &l);
+
+ s->quit = 1;
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ if (rc == NGX_IMAP_NEXT || rc == NGX_MAIL_PARSE_INVALID_COMMAND) {
+ return rc;
+ }
+
+ if (rc == NGX_ERROR) {
+ ngx_mail_close_connection(c);
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+void
+ngx_mail_auth(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ s->args.nelts = 0;
+
+ if (s->buffer->pos == s->buffer->last) {
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
+ }
+
+ s->state = 0;
+
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+
+ s->login_attempt++;
+
+ ngx_mail_auth_http_init(s);
+}
+
+
+void
+ngx_mail_session_internal_server_error(ngx_mail_session_t *s)
+{
+ ngx_mail_core_srv_conf_t *cscf;
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ s->out = cscf->protocol->internal_server_error;
+ s->quit = 1;
+
+ ngx_mail_send(s->connection->write);
+}
+
+
+void
+ngx_mail_close_connection(ngx_connection_t *c)
+{
+ ngx_pool_t *pool;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "close mail connection: %d", c->fd);
+
+#if (NGX_MAIL_SSL)
+
+ if (c->ssl) {
+ if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
+ c->ssl->handler = ngx_mail_close_connection;
+ return;
+ }
+ }
+
+#endif
+
+#if (NGX_STAT_STUB)
+ (void) ngx_atomic_fetch_add(ngx_stat_active, -1);
+#endif
+
+ c->destroyed = 1;
+
+ pool = c->pool;
+
+ ngx_close_connection(c);
+
+ ngx_destroy_pool(pool);
+}
+
+
+u_char *
+ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len)
+{
+ u_char *p;
+ ngx_mail_session_t *s;
+ ngx_mail_log_ctx_t *ctx;
+
+ if (log->action) {
+ p = ngx_snprintf(buf, len, " while %s", log->action);
+ len -= p - buf;
+ buf = p;
+ }
+
+ ctx = log->data;
+
+ p = ngx_snprintf(buf, len, ", client: %V", ctx->client);
+ len -= p - buf;
+ buf = p;
+
+ s = ctx->session;
+
+ if (s == NULL) {
+ return p;
+ }
+
+ p = ngx_snprintf(buf, len, "%s, server: %V",
+ s->starttls ? " using starttls" : "",
+ s->addr_text);
+ len -= p - buf;
+ buf = p;
+
+ if (s->login.len == 0) {
+ return p;
+ }
+
+ p = ngx_snprintf(buf, len, ", login: \"%V\"", &s->login);
+ len -= p - buf;
+ buf = p;
+
+ if (s->proxy == NULL) {
+ return p;
+ }
+
+ p = ngx_snprintf(buf, len, ", upstream: %V", s->proxy->upstream.name);
+
+ return p;
+}
diff --git a/src/nginx-1.18.0/src/mail/ngx_mail_imap_handler.c b/src/nginx-1.18.0/src/mail/ngx_mail_imap_handler.c
new file mode 100644
index 0000000..3bf09ec
--- /dev/null
+++ b/src/nginx-1.18.0/src/mail/ngx_mail_imap_handler.c
@@ -0,0 +1,472 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+#include
+#include
+#include
+
+
+static ngx_int_t ngx_mail_imap_login(ngx_mail_session_t *s,
+ ngx_connection_t *c);
+static ngx_int_t ngx_mail_imap_authenticate(ngx_mail_session_t *s,
+ ngx_connection_t *c);
+static ngx_int_t ngx_mail_imap_capability(ngx_mail_session_t *s,
+ ngx_connection_t *c);
+static ngx_int_t ngx_mail_imap_starttls(ngx_mail_session_t *s,
+ ngx_connection_t *c);
+
+
+static u_char imap_greeting[] = "* OK IMAP4 ready" CRLF;
+static u_char imap_star[] = "* ";
+static u_char imap_ok[] = "OK completed" CRLF;
+static u_char imap_next[] = "+ OK" CRLF;
+static u_char imap_plain_next[] = "+ " CRLF;
+static u_char imap_username[] = "+ VXNlcm5hbWU6" CRLF;
+static u_char imap_password[] = "+ UGFzc3dvcmQ6" CRLF;
+static u_char imap_bye[] = "* BYE" CRLF;
+static u_char imap_invalid_command[] = "BAD invalid command" CRLF;
+
+
+void
+ngx_mail_imap_init_session(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_mail_core_srv_conf_t *cscf;
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ ngx_str_set(&s->out, imap_greeting);
+
+ c->read->handler = ngx_mail_imap_init_protocol;
+
+ ngx_add_timer(c->read, cscf->timeout);
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ }
+
+ ngx_mail_send(c->write);
+}
+
+
+void
+ngx_mail_imap_init_protocol(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_imap_srv_conf_t *iscf;
+
+ c = rev->data;
+
+ c->log->action = "in auth state";
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ c->timedout = 1;
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ s = c->data;
+
+ if (s->buffer == NULL) {
+ if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t))
+ == NGX_ERROR)
+ {
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module);
+
+ s->buffer = ngx_create_temp_buf(c->pool, iscf->client_buffer_size);
+ if (s->buffer == NULL) {
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+ }
+
+ s->mail_state = ngx_imap_start;
+ c->read->handler = ngx_mail_imap_auth_state;
+
+ ngx_mail_imap_auth_state(rev);
+}
+
+
+void
+ngx_mail_imap_auth_state(ngx_event_t *rev)
+{
+ u_char *p, *dst, *src, *end;
+ ngx_str_t *arg;
+ ngx_int_t rc;
+ ngx_uint_t tag, i;
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+
+ c = rev->data;
+ s = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap auth state");
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ c->timedout = 1;
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ if (s->out.len) {
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap send handler busy");
+ s->blocked = 1;
+ return;
+ }
+
+ s->blocked = 0;
+
+ rc = ngx_mail_read_command(s, c);
+
+ if (rc == NGX_AGAIN || rc == NGX_ERROR) {
+ return;
+ }
+
+ tag = 1;
+ s->text.len = 0;
+ ngx_str_set(&s->out, imap_ok);
+
+ if (rc == NGX_OK) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap auth command: %i",
+ s->command);
+
+ if (s->backslash) {
+
+ arg = s->args.elts;
+
+ for (i = 0; i < s->args.nelts; i++) {
+ dst = arg[i].data;
+ end = dst + arg[i].len;
+
+ for (src = dst; src < end; dst++) {
+ *dst = *src;
+ if (*src++ == '\\') {
+ *dst = *src++;
+ }
+ }
+
+ arg[i].len = dst - arg[i].data;
+ }
+
+ s->backslash = 0;
+ }
+
+ switch (s->mail_state) {
+
+ case ngx_imap_start:
+
+ switch (s->command) {
+
+ case NGX_IMAP_LOGIN:
+ rc = ngx_mail_imap_login(s, c);
+ break;
+
+ case NGX_IMAP_AUTHENTICATE:
+ rc = ngx_mail_imap_authenticate(s, c);
+ tag = (rc != NGX_OK);
+ break;
+
+ case NGX_IMAP_CAPABILITY:
+ rc = ngx_mail_imap_capability(s, c);
+ break;
+
+ case NGX_IMAP_LOGOUT:
+ s->quit = 1;
+ ngx_str_set(&s->text, imap_bye);
+ break;
+
+ case NGX_IMAP_NOOP:
+ break;
+
+ case NGX_IMAP_STARTTLS:
+ rc = ngx_mail_imap_starttls(s, c);
+ break;
+
+ default:
+ rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+ break;
+ }
+
+ break;
+
+ case ngx_imap_auth_login_username:
+ rc = ngx_mail_auth_login_username(s, c, 0);
+
+ tag = 0;
+ ngx_str_set(&s->out, imap_password);
+ s->mail_state = ngx_imap_auth_login_password;
+
+ break;
+
+ case ngx_imap_auth_login_password:
+ rc = ngx_mail_auth_login_password(s, c);
+ break;
+
+ case ngx_imap_auth_plain:
+ rc = ngx_mail_auth_plain(s, c, 0);
+ break;
+
+ case ngx_imap_auth_cram_md5:
+ rc = ngx_mail_auth_cram_md5(s, c);
+ break;
+
+ case ngx_imap_auth_external:
+ rc = ngx_mail_auth_external(s, c, 0);
+ break;
+ }
+
+ } else if (rc == NGX_IMAP_NEXT) {
+ tag = 0;
+ ngx_str_set(&s->out, imap_next);
+ }
+
+ switch (rc) {
+
+ case NGX_DONE:
+ ngx_mail_auth(s, c);
+ return;
+
+ case NGX_ERROR:
+ ngx_mail_session_internal_server_error(s);
+ return;
+
+ case NGX_MAIL_PARSE_INVALID_COMMAND:
+ s->state = 0;
+ ngx_str_set(&s->out, imap_invalid_command);
+ s->mail_state = ngx_imap_start;
+ break;
+ }
+
+ if (tag) {
+ if (s->tag.len == 0) {
+ ngx_str_set(&s->tag, imap_star);
+ }
+
+ if (s->tagged_line.len < s->tag.len + s->text.len + s->out.len) {
+ s->tagged_line.len = s->tag.len + s->text.len + s->out.len;
+ s->tagged_line.data = ngx_pnalloc(c->pool, s->tagged_line.len);
+ if (s->tagged_line.data == NULL) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+ }
+
+ p = s->tagged_line.data;
+
+ if (s->text.len) {
+ p = ngx_cpymem(p, s->text.data, s->text.len);
+ }
+
+ p = ngx_cpymem(p, s->tag.data, s->tag.len);
+ ngx_memcpy(p, s->out.data, s->out.len);
+
+ s->out.len = s->text.len + s->tag.len + s->out.len;
+ s->out.data = s->tagged_line.data;
+ }
+
+ if (rc != NGX_IMAP_NEXT) {
+ s->args.nelts = 0;
+
+ if (s->state) {
+ /* preserve tag */
+ s->arg_start = s->buffer->start + s->tag.len;
+ s->buffer->pos = s->arg_start;
+ s->buffer->last = s->arg_start;
+
+ } else {
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
+ s->tag.len = 0;
+ }
+ }
+
+ ngx_mail_send(c->write);
+}
+
+
+static ngx_int_t
+ngx_mail_imap_login(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_str_t *arg;
+
+#if (NGX_MAIL_SSL)
+ if (ngx_mail_starttls_only(s, c)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+#endif
+
+ arg = s->args.elts;
+
+ if (s->args.nelts != 2 || arg[0].len == 0) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ s->login.len = arg[0].len;
+ s->login.data = ngx_pnalloc(c->pool, s->login.len);
+ if (s->login.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s->login.data, arg[0].data, s->login.len);
+
+ s->passwd.len = arg[1].len;
+ s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);
+ if (s->passwd.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len);
+
+#if (NGX_DEBUG_MAIL_PASSWD)
+ ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "imap login:\"%V\" passwd:\"%V\"",
+ &s->login, &s->passwd);
+#else
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "imap login:\"%V\"", &s->login);
+#endif
+
+ return NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_mail_imap_authenticate(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_int_t rc;
+ ngx_mail_core_srv_conf_t *cscf;
+ ngx_mail_imap_srv_conf_t *iscf;
+
+#if (NGX_MAIL_SSL)
+ if (ngx_mail_starttls_only(s, c)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+#endif
+
+ iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module);
+
+ rc = ngx_mail_auth_parse(s, c);
+
+ switch (rc) {
+
+ case NGX_MAIL_AUTH_LOGIN:
+
+ ngx_str_set(&s->out, imap_username);
+ s->mail_state = ngx_imap_auth_login_username;
+
+ return NGX_OK;
+
+ case NGX_MAIL_AUTH_LOGIN_USERNAME:
+
+ ngx_str_set(&s->out, imap_password);
+ s->mail_state = ngx_imap_auth_login_password;
+
+ return ngx_mail_auth_login_username(s, c, 1);
+
+ case NGX_MAIL_AUTH_PLAIN:
+
+ ngx_str_set(&s->out, imap_plain_next);
+ s->mail_state = ngx_imap_auth_plain;
+
+ return NGX_OK;
+
+ case NGX_MAIL_AUTH_CRAM_MD5:
+
+ if (!(iscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ if (s->salt.data == NULL) {
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ if (ngx_mail_salt(s, c, cscf) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) {
+ s->mail_state = ngx_imap_auth_cram_md5;
+ return NGX_OK;
+ }
+
+ return NGX_ERROR;
+
+ case NGX_MAIL_AUTH_EXTERNAL:
+
+ if (!(iscf->auth_methods & NGX_MAIL_AUTH_EXTERNAL_ENABLED)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ ngx_str_set(&s->out, imap_username);
+ s->mail_state = ngx_imap_auth_external;
+
+ return NGX_OK;
+ }
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_mail_imap_capability(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_mail_imap_srv_conf_t *iscf;
+
+ iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module);
+
+#if (NGX_MAIL_SSL)
+
+ if (c->ssl == NULL) {
+ ngx_mail_ssl_conf_t *sslcf;
+
+ sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+
+ if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {
+ s->text = iscf->starttls_capability;
+ return NGX_OK;
+ }
+
+ if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
+ s->text = iscf->starttls_only_capability;
+ return NGX_OK;
+ }
+ }
+#endif
+
+ s->text = iscf->capability;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_mail_imap_starttls(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+#if (NGX_MAIL_SSL)
+ ngx_mail_ssl_conf_t *sslcf;
+
+ if (c->ssl == NULL) {
+ sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+ if (sslcf->starttls) {
+ c->read->handler = ngx_mail_starttls_handler;
+ return NGX_OK;
+ }
+ }
+
+#endif
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+}
diff --git a/src/nginx-1.18.0/src/mail/ngx_mail_imap_module.c b/src/nginx-1.18.0/src/mail/ngx_mail_imap_module.c
new file mode 100644
index 0000000..1f187fd
--- /dev/null
+++ b/src/nginx-1.18.0/src/mail/ngx_mail_imap_module.c
@@ -0,0 +1,257 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+#include
+#include
+#include
+
+
+static void *ngx_mail_imap_create_srv_conf(ngx_conf_t *cf);
+static char *ngx_mail_imap_merge_srv_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+
+
+static ngx_str_t ngx_mail_imap_default_capabilities[] = {
+ ngx_string("IMAP4"),
+ ngx_string("IMAP4rev1"),
+ ngx_string("UIDPLUS"),
+ ngx_null_string
+};
+
+
+static ngx_conf_bitmask_t ngx_mail_imap_auth_methods[] = {
+ { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED },
+ { ngx_string("login"), NGX_MAIL_AUTH_LOGIN_ENABLED },
+ { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED },
+ { ngx_string("external"), NGX_MAIL_AUTH_EXTERNAL_ENABLED },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_str_t ngx_mail_imap_auth_methods_names[] = {
+ ngx_string("AUTH=PLAIN"),
+ ngx_string("AUTH=LOGIN"),
+ ngx_null_string, /* APOP */
+ ngx_string("AUTH=CRAM-MD5"),
+ ngx_string("AUTH=EXTERNAL"),
+ ngx_null_string /* NONE */
+};
+
+
+static ngx_mail_protocol_t ngx_mail_imap_protocol = {
+ ngx_string("imap"),
+ { 143, 993, 0, 0 },
+ NGX_MAIL_IMAP_PROTOCOL,
+
+ ngx_mail_imap_init_session,
+ ngx_mail_imap_init_protocol,
+ ngx_mail_imap_parse_command,
+ ngx_mail_imap_auth_state,
+
+ ngx_string("* BAD internal server error" CRLF),
+ ngx_string("* BYE SSL certificate error" CRLF),
+ ngx_string("* BYE No required SSL certificate" CRLF)
+};
+
+
+static ngx_command_t ngx_mail_imap_commands[] = {
+
+ { ngx_string("imap_client_buffer"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_imap_srv_conf_t, client_buffer_size),
+ NULL },
+
+ { ngx_string("imap_capabilities"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+ ngx_mail_capabilities,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_imap_srv_conf_t, capabilities),
+ NULL },
+
+ { ngx_string("imap_auth"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_imap_srv_conf_t, auth_methods),
+ &ngx_mail_imap_auth_methods },
+
+ ngx_null_command
+};
+
+
+static ngx_mail_module_t ngx_mail_imap_module_ctx = {
+ &ngx_mail_imap_protocol, /* protocol */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_mail_imap_create_srv_conf, /* create server configuration */
+ ngx_mail_imap_merge_srv_conf /* merge server configuration */
+};
+
+
+ngx_module_t ngx_mail_imap_module = {
+ NGX_MODULE_V1,
+ &ngx_mail_imap_module_ctx, /* module context */
+ ngx_mail_imap_commands, /* module directives */
+ NGX_MAIL_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static void *
+ngx_mail_imap_create_srv_conf(ngx_conf_t *cf)
+{
+ ngx_mail_imap_srv_conf_t *iscf;
+
+ iscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_imap_srv_conf_t));
+ if (iscf == NULL) {
+ return NULL;
+ }
+
+ iscf->client_buffer_size = NGX_CONF_UNSET_SIZE;
+
+ if (ngx_array_init(&iscf->capabilities, cf->pool, 4, sizeof(ngx_str_t))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ return iscf;
+}
+
+
+static char *
+ngx_mail_imap_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_mail_imap_srv_conf_t *prev = parent;
+ ngx_mail_imap_srv_conf_t *conf = child;
+
+ u_char *p, *auth;
+ size_t size;
+ ngx_str_t *c, *d;
+ ngx_uint_t i, m;
+
+ ngx_conf_merge_size_value(conf->client_buffer_size,
+ prev->client_buffer_size,
+ (size_t) ngx_pagesize);
+
+ ngx_conf_merge_bitmask_value(conf->auth_methods,
+ prev->auth_methods,
+ (NGX_CONF_BITMASK_SET
+ |NGX_MAIL_AUTH_PLAIN_ENABLED));
+
+
+ if (conf->capabilities.nelts == 0) {
+ conf->capabilities = prev->capabilities;
+ }
+
+ if (conf->capabilities.nelts == 0) {
+
+ for (d = ngx_mail_imap_default_capabilities; d->len; d++) {
+ c = ngx_array_push(&conf->capabilities);
+ if (c == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *c = *d;
+ }
+ }
+
+ size = sizeof("* CAPABILITY" CRLF) - 1;
+
+ c = conf->capabilities.elts;
+ for (i = 0; i < conf->capabilities.nelts; i++) {
+ size += 1 + c[i].len;
+ }
+
+ for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
+ m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
+ m <<= 1, i++)
+ {
+ if (m & conf->auth_methods) {
+ size += 1 + ngx_mail_imap_auth_methods_names[i].len;
+ }
+ }
+
+ p = ngx_pnalloc(cf->pool, size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->capability.len = size;
+ conf->capability.data = p;
+
+ p = ngx_cpymem(p, "* CAPABILITY", sizeof("* CAPABILITY") - 1);
+
+ for (i = 0; i < conf->capabilities.nelts; i++) {
+ *p++ = ' ';
+ p = ngx_cpymem(p, c[i].data, c[i].len);
+ }
+
+ auth = p;
+
+ for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
+ m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
+ m <<= 1, i++)
+ {
+ if (m & conf->auth_methods) {
+ *p++ = ' ';
+ p = ngx_cpymem(p, ngx_mail_imap_auth_methods_names[i].data,
+ ngx_mail_imap_auth_methods_names[i].len);
+ }
+ }
+
+ *p++ = CR; *p = LF;
+
+
+ size += sizeof(" STARTTLS") - 1;
+
+ p = ngx_pnalloc(cf->pool, size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->starttls_capability.len = size;
+ conf->starttls_capability.data = p;
+
+ p = ngx_cpymem(p, conf->capability.data,
+ conf->capability.len - (sizeof(CRLF) - 1));
+ p = ngx_cpymem(p, " STARTTLS", sizeof(" STARTTLS") - 1);
+ *p++ = CR; *p = LF;
+
+
+ size = (auth - conf->capability.data) + sizeof(CRLF) - 1
+ + sizeof(" STARTTLS LOGINDISABLED") - 1;
+
+ p = ngx_pnalloc(cf->pool, size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->starttls_only_capability.len = size;
+ conf->starttls_only_capability.data = p;
+
+ p = ngx_cpymem(p, conf->capability.data,
+ auth - conf->capability.data);
+ p = ngx_cpymem(p, " STARTTLS LOGINDISABLED",
+ sizeof(" STARTTLS LOGINDISABLED") - 1);
+ *p++ = CR; *p = LF;
+
+ return NGX_CONF_OK;
+}
diff --git a/src/nginx-1.18.0/src/mail/ngx_mail_imap_module.h b/src/nginx-1.18.0/src/mail/ngx_mail_imap_module.h
new file mode 100644
index 0000000..131b445
--- /dev/null
+++ b/src/nginx-1.18.0/src/mail/ngx_mail_imap_module.h
@@ -0,0 +1,39 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_MAIL_IMAP_MODULE_H_INCLUDED_
+#define _NGX_MAIL_IMAP_MODULE_H_INCLUDED_
+
+
+#include
+#include
+#include
+
+
+typedef struct {
+ size_t client_buffer_size;
+
+ ngx_str_t capability;
+ ngx_str_t starttls_capability;
+ ngx_str_t starttls_only_capability;
+
+ ngx_uint_t auth_methods;
+
+ ngx_array_t capabilities;
+} ngx_mail_imap_srv_conf_t;
+
+
+void ngx_mail_imap_init_session(ngx_mail_session_t *s, ngx_connection_t *c);
+void ngx_mail_imap_init_protocol(ngx_event_t *rev);
+void ngx_mail_imap_auth_state(ngx_event_t *rev);
+ngx_int_t ngx_mail_imap_parse_command(ngx_mail_session_t *s);
+
+
+extern ngx_module_t ngx_mail_imap_module;
+
+
+#endif /* _NGX_MAIL_IMAP_MODULE_H_INCLUDED_ */
diff --git a/src/nginx-1.18.0/src/mail/ngx_mail_parse.c b/src/nginx-1.18.0/src/mail/ngx_mail_parse.c
new file mode 100644
index 0000000..2c2cdff
--- /dev/null
+++ b/src/nginx-1.18.0/src/mail/ngx_mail_parse.c
@@ -0,0 +1,932 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+ngx_int_t
+ngx_mail_pop3_parse_command(ngx_mail_session_t *s)
+{
+ u_char ch, *p, *c, c0, c1, c2, c3;
+ ngx_str_t *arg;
+ enum {
+ sw_start = 0,
+ sw_spaces_before_argument,
+ sw_argument,
+ sw_almost_done
+ } state;
+
+ state = s->state;
+
+ for (p = s->buffer->pos; p < s->buffer->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* POP3 command */
+ case sw_start:
+ if (ch == ' ' || ch == CR || ch == LF) {
+ c = s->buffer->start;
+
+ if (p - c == 4) {
+
+ c0 = ngx_toupper(c[0]);
+ c1 = ngx_toupper(c[1]);
+ c2 = ngx_toupper(c[2]);
+ c3 = ngx_toupper(c[3]);
+
+ if (c0 == 'U' && c1 == 'S' && c2 == 'E' && c3 == 'R')
+ {
+ s->command = NGX_POP3_USER;
+
+ } else if (c0 == 'P' && c1 == 'A' && c2 == 'S' && c3 == 'S')
+ {
+ s->command = NGX_POP3_PASS;
+
+ } else if (c0 == 'A' && c1 == 'P' && c2 == 'O' && c3 == 'P')
+ {
+ s->command = NGX_POP3_APOP;
+
+ } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T')
+ {
+ s->command = NGX_POP3_QUIT;
+
+ } else if (c0 == 'C' && c1 == 'A' && c2 == 'P' && c3 == 'A')
+ {
+ s->command = NGX_POP3_CAPA;
+
+ } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H')
+ {
+ s->command = NGX_POP3_AUTH;
+
+ } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')
+ {
+ s->command = NGX_POP3_NOOP;
+#if (NGX_MAIL_SSL)
+ } else if (c0 == 'S' && c1 == 'T' && c2 == 'L' && c3 == 'S')
+ {
+ s->command = NGX_POP3_STLS;
+#endif
+ } else {
+ goto invalid;
+ }
+
+ } else {
+ goto invalid;
+ }
+
+ switch (ch) {
+ case ' ':
+ state = sw_spaces_before_argument;
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+ }
+
+ if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
+ goto invalid;
+ }
+
+ break;
+
+ case sw_spaces_before_argument:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ state = sw_almost_done;
+ s->arg_end = p;
+ break;
+ case LF:
+ s->arg_end = p;
+ goto done;
+ default:
+ if (s->args.nelts <= 2) {
+ state = sw_argument;
+ s->arg_start = p;
+ break;
+ }
+ goto invalid;
+ }
+ break;
+
+ case sw_argument:
+ switch (ch) {
+
+ case ' ':
+
+ /*
+ * the space should be considered as part of the at username
+ * or password, but not of argument in other commands
+ */
+
+ if (s->command == NGX_POP3_USER
+ || s->command == NGX_POP3_PASS)
+ {
+ break;
+ }
+
+ /* fall through */
+
+ case CR:
+ case LF:
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = p - s->arg_start;
+ arg->data = s->arg_start;
+ s->arg_start = NULL;
+
+ switch (ch) {
+ case ' ':
+ state = sw_spaces_before_argument;
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case sw_almost_done:
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ goto invalid;
+ }
+ }
+ }
+
+ s->buffer->pos = p;
+ s->state = state;
+
+ return NGX_AGAIN;
+
+done:
+
+ s->buffer->pos = p + 1;
+
+ if (s->arg_start) {
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = s->arg_end - s->arg_start;
+ arg->data = s->arg_start;
+ s->arg_start = NULL;
+ }
+
+ s->state = (s->command != NGX_POP3_AUTH) ? sw_start : sw_argument;
+
+ return NGX_OK;
+
+invalid:
+
+ s->state = sw_start;
+ s->arg_start = NULL;
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+}
+
+
+ngx_int_t
+ngx_mail_imap_parse_command(ngx_mail_session_t *s)
+{
+ u_char ch, *p, *c;
+ ngx_str_t *arg;
+ enum {
+ sw_start = 0,
+ sw_spaces_before_command,
+ sw_command,
+ sw_spaces_before_argument,
+ sw_argument,
+ sw_backslash,
+ sw_literal,
+ sw_no_sync_literal_argument,
+ sw_start_literal_argument,
+ sw_literal_argument,
+ sw_end_literal_argument,
+ sw_almost_done
+ } state;
+
+ state = s->state;
+
+ for (p = s->buffer->pos; p < s->buffer->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* IMAP tag */
+ case sw_start:
+ switch (ch) {
+ case ' ':
+ s->tag.len = p - s->buffer->start + 1;
+ s->tag.data = s->buffer->start;
+ state = sw_spaces_before_command;
+ break;
+ case CR:
+ s->state = sw_start;
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ case LF:
+ s->state = sw_start;
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+ break;
+
+ case sw_spaces_before_command:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ s->state = sw_start;
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ case LF:
+ s->state = sw_start;
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ default:
+ s->cmd_start = p;
+ state = sw_command;
+ break;
+ }
+ break;
+
+ case sw_command:
+ if (ch == ' ' || ch == CR || ch == LF) {
+
+ c = s->cmd_start;
+
+ switch (p - c) {
+
+ case 4:
+ if ((c[0] == 'N' || c[0] == 'n')
+ && (c[1] == 'O'|| c[1] == 'o')
+ && (c[2] == 'O'|| c[2] == 'o')
+ && (c[3] == 'P'|| c[3] == 'p'))
+ {
+ s->command = NGX_IMAP_NOOP;
+
+ } else {
+ goto invalid;
+ }
+ break;
+
+ case 5:
+ if ((c[0] == 'L'|| c[0] == 'l')
+ && (c[1] == 'O'|| c[1] == 'o')
+ && (c[2] == 'G'|| c[2] == 'g')
+ && (c[3] == 'I'|| c[3] == 'i')
+ && (c[4] == 'N'|| c[4] == 'n'))
+ {
+ s->command = NGX_IMAP_LOGIN;
+
+ } else {
+ goto invalid;
+ }
+ break;
+
+ case 6:
+ if ((c[0] == 'L'|| c[0] == 'l')
+ && (c[1] == 'O'|| c[1] == 'o')
+ && (c[2] == 'G'|| c[2] == 'g')
+ && (c[3] == 'O'|| c[3] == 'o')
+ && (c[4] == 'U'|| c[4] == 'u')
+ && (c[5] == 'T'|| c[5] == 't'))
+ {
+ s->command = NGX_IMAP_LOGOUT;
+
+ } else {
+ goto invalid;
+ }
+ break;
+
+#if (NGX_MAIL_SSL)
+ case 8:
+ if ((c[0] == 'S'|| c[0] == 's')
+ && (c[1] == 'T'|| c[1] == 't')
+ && (c[2] == 'A'|| c[2] == 'a')
+ && (c[3] == 'R'|| c[3] == 'r')
+ && (c[4] == 'T'|| c[4] == 't')
+ && (c[5] == 'T'|| c[5] == 't')
+ && (c[6] == 'L'|| c[6] == 'l')
+ && (c[7] == 'S'|| c[7] == 's'))
+ {
+ s->command = NGX_IMAP_STARTTLS;
+
+ } else {
+ goto invalid;
+ }
+ break;
+#endif
+
+ case 10:
+ if ((c[0] == 'C'|| c[0] == 'c')
+ && (c[1] == 'A'|| c[1] == 'a')
+ && (c[2] == 'P'|| c[2] == 'p')
+ && (c[3] == 'A'|| c[3] == 'a')
+ && (c[4] == 'B'|| c[4] == 'b')
+ && (c[5] == 'I'|| c[5] == 'i')
+ && (c[6] == 'L'|| c[6] == 'l')
+ && (c[7] == 'I'|| c[7] == 'i')
+ && (c[8] == 'T'|| c[8] == 't')
+ && (c[9] == 'Y'|| c[9] == 'y'))
+ {
+ s->command = NGX_IMAP_CAPABILITY;
+
+ } else {
+ goto invalid;
+ }
+ break;
+
+ case 12:
+ if ((c[0] == 'A'|| c[0] == 'a')
+ && (c[1] == 'U'|| c[1] == 'u')
+ && (c[2] == 'T'|| c[2] == 't')
+ && (c[3] == 'H'|| c[3] == 'h')
+ && (c[4] == 'E'|| c[4] == 'e')
+ && (c[5] == 'N'|| c[5] == 'n')
+ && (c[6] == 'T'|| c[6] == 't')
+ && (c[7] == 'I'|| c[7] == 'i')
+ && (c[8] == 'C'|| c[8] == 'c')
+ && (c[9] == 'A'|| c[9] == 'a')
+ && (c[10] == 'T'|| c[10] == 't')
+ && (c[11] == 'E'|| c[11] == 'e'))
+ {
+ s->command = NGX_IMAP_AUTHENTICATE;
+
+ } else {
+ goto invalid;
+ }
+ break;
+
+ default:
+ goto invalid;
+ }
+
+ switch (ch) {
+ case ' ':
+ state = sw_spaces_before_argument;
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+ }
+
+ if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
+ goto invalid;
+ }
+
+ break;
+
+ case sw_spaces_before_argument:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ state = sw_almost_done;
+ s->arg_end = p;
+ break;
+ case LF:
+ s->arg_end = p;
+ goto done;
+ case '"':
+ if (s->args.nelts <= 2) {
+ s->quoted = 1;
+ s->arg_start = p + 1;
+ state = sw_argument;
+ break;
+ }
+ goto invalid;
+ case '{':
+ if (s->args.nelts <= 2) {
+ state = sw_literal;
+ break;
+ }
+ goto invalid;
+ default:
+ if (s->args.nelts <= 2) {
+ s->arg_start = p;
+ state = sw_argument;
+ break;
+ }
+ goto invalid;
+ }
+ break;
+
+ case sw_argument:
+ if (ch == ' ' && s->quoted) {
+ break;
+ }
+
+ switch (ch) {
+ case '"':
+ if (!s->quoted) {
+ break;
+ }
+ s->quoted = 0;
+ /* fall through */
+ case ' ':
+ case CR:
+ case LF:
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = p - s->arg_start;
+ arg->data = s->arg_start;
+ s->arg_start = NULL;
+
+ switch (ch) {
+ case '"':
+ case ' ':
+ state = sw_spaces_before_argument;
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+ case '\\':
+ if (s->quoted) {
+ s->backslash = 1;
+ state = sw_backslash;
+ }
+ break;
+ }
+ break;
+
+ case sw_backslash:
+ switch (ch) {
+ case CR:
+ case LF:
+ goto invalid;
+ default:
+ state = sw_argument;
+ }
+ break;
+
+ case sw_literal:
+ if (ch >= '0' && ch <= '9') {
+ s->literal_len = s->literal_len * 10 + (ch - '0');
+ break;
+ }
+ if (ch == '}') {
+ state = sw_start_literal_argument;
+ break;
+ }
+ if (ch == '+') {
+ state = sw_no_sync_literal_argument;
+ break;
+ }
+ goto invalid;
+
+ case sw_no_sync_literal_argument:
+ if (ch == '}') {
+ s->no_sync_literal = 1;
+ state = sw_start_literal_argument;
+ break;
+ }
+ goto invalid;
+
+ case sw_start_literal_argument:
+ switch (ch) {
+ case CR:
+ break;
+ case LF:
+ s->buffer->pos = p + 1;
+ s->arg_start = p + 1;
+ if (s->no_sync_literal == 0) {
+ s->state = sw_literal_argument;
+ return NGX_IMAP_NEXT;
+ }
+ state = sw_literal_argument;
+ s->no_sync_literal = 0;
+ break;
+ default:
+ goto invalid;
+ }
+ break;
+
+ case sw_literal_argument:
+ if (s->literal_len && --s->literal_len) {
+ break;
+ }
+
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = p + 1 - s->arg_start;
+ arg->data = s->arg_start;
+ s->arg_start = NULL;
+ state = sw_end_literal_argument;
+
+ break;
+
+ case sw_end_literal_argument:
+ switch (ch) {
+ case '{':
+ if (s->args.nelts <= 2) {
+ state = sw_literal;
+ break;
+ }
+ goto invalid;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ default:
+ state = sw_spaces_before_argument;
+ break;
+ }
+ break;
+
+ case sw_almost_done:
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ goto invalid;
+ }
+ }
+ }
+
+ s->buffer->pos = p;
+ s->state = state;
+
+ return NGX_AGAIN;
+
+done:
+
+ s->buffer->pos = p + 1;
+
+ if (s->arg_start) {
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = s->arg_end - s->arg_start;
+ arg->data = s->arg_start;
+
+ s->arg_start = NULL;
+ s->cmd_start = NULL;
+ s->quoted = 0;
+ s->no_sync_literal = 0;
+ s->literal_len = 0;
+ }
+
+ s->state = (s->command != NGX_IMAP_AUTHENTICATE) ? sw_start : sw_argument;
+
+ return NGX_OK;
+
+invalid:
+
+ s->state = sw_start;
+ s->quoted = 0;
+ s->no_sync_literal = 0;
+ s->literal_len = 0;
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+}
+
+
+ngx_int_t
+ngx_mail_smtp_parse_command(ngx_mail_session_t *s)
+{
+ u_char ch, *p, *c, c0, c1, c2, c3;
+ ngx_str_t *arg;
+ enum {
+ sw_start = 0,
+ sw_command,
+ sw_invalid,
+ sw_spaces_before_argument,
+ sw_argument,
+ sw_almost_done
+ } state;
+
+ state = s->state;
+
+ for (p = s->buffer->pos; p < s->buffer->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* SMTP command */
+ case sw_start:
+ s->cmd_start = p;
+ state = sw_command;
+
+ /* fall through */
+
+ case sw_command:
+ if (ch == ' ' || ch == CR || ch == LF) {
+ c = s->cmd_start;
+
+ if (p - c == 4) {
+
+ c0 = ngx_toupper(c[0]);
+ c1 = ngx_toupper(c[1]);
+ c2 = ngx_toupper(c[2]);
+ c3 = ngx_toupper(c[3]);
+
+ if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'O')
+ {
+ s->command = NGX_SMTP_HELO;
+
+ } else if (c0 == 'E' && c1 == 'H' && c2 == 'L' && c3 == 'O')
+ {
+ s->command = NGX_SMTP_EHLO;
+
+ } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T')
+ {
+ s->command = NGX_SMTP_QUIT;
+
+ } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H')
+ {
+ s->command = NGX_SMTP_AUTH;
+
+ } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')
+ {
+ s->command = NGX_SMTP_NOOP;
+
+ } else if (c0 == 'M' && c1 == 'A' && c2 == 'I' && c3 == 'L')
+ {
+ s->command = NGX_SMTP_MAIL;
+
+ } else if (c0 == 'R' && c1 == 'S' && c2 == 'E' && c3 == 'T')
+ {
+ s->command = NGX_SMTP_RSET;
+
+ } else if (c0 == 'R' && c1 == 'C' && c2 == 'P' && c3 == 'T')
+ {
+ s->command = NGX_SMTP_RCPT;
+
+ } else if (c0 == 'V' && c1 == 'R' && c2 == 'F' && c3 == 'Y')
+ {
+ s->command = NGX_SMTP_VRFY;
+
+ } else if (c0 == 'E' && c1 == 'X' && c2 == 'P' && c3 == 'N')
+ {
+ s->command = NGX_SMTP_EXPN;
+
+ } else if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'P')
+ {
+ s->command = NGX_SMTP_HELP;
+
+ } else {
+ goto invalid;
+ }
+#if (NGX_MAIL_SSL)
+ } else if (p - c == 8) {
+
+ if ((c[0] == 'S'|| c[0] == 's')
+ && (c[1] == 'T'|| c[1] == 't')
+ && (c[2] == 'A'|| c[2] == 'a')
+ && (c[3] == 'R'|| c[3] == 'r')
+ && (c[4] == 'T'|| c[4] == 't')
+ && (c[5] == 'T'|| c[5] == 't')
+ && (c[6] == 'L'|| c[6] == 'l')
+ && (c[7] == 'S'|| c[7] == 's'))
+ {
+ s->command = NGX_SMTP_STARTTLS;
+
+ } else {
+ goto invalid;
+ }
+#endif
+ } else {
+ goto invalid;
+ }
+
+ s->cmd.data = s->cmd_start;
+ s->cmd.len = p - s->cmd_start;
+
+ switch (ch) {
+ case ' ':
+ state = sw_spaces_before_argument;
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+ }
+
+ if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
+ goto invalid;
+ }
+
+ break;
+
+ case sw_invalid:
+ goto invalid;
+
+ case sw_spaces_before_argument:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ state = sw_almost_done;
+ s->arg_end = p;
+ break;
+ case LF:
+ s->arg_end = p;
+ goto done;
+ default:
+ if (s->args.nelts <= 10) {
+ state = sw_argument;
+ s->arg_start = p;
+ break;
+ }
+ goto invalid;
+ }
+ break;
+
+ case sw_argument:
+ switch (ch) {
+ case ' ':
+ case CR:
+ case LF:
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = p - s->arg_start;
+ arg->data = s->arg_start;
+ s->arg_start = NULL;
+
+ switch (ch) {
+ case ' ':
+ state = sw_spaces_before_argument;
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case sw_almost_done:
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ goto invalid;
+ }
+ }
+ }
+
+ s->buffer->pos = p;
+ s->state = state;
+
+ return NGX_AGAIN;
+
+done:
+
+ s->buffer->pos = p + 1;
+
+ if (s->arg_start) {
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = s->arg_end - s->arg_start;
+ arg->data = s->arg_start;
+ s->arg_start = NULL;
+ }
+
+ s->state = (s->command != NGX_SMTP_AUTH) ? sw_start : sw_argument;
+
+ return NGX_OK;
+
+invalid:
+
+ s->state = sw_invalid;
+ s->arg_start = NULL;
+
+ /* skip invalid command till LF */
+
+ for (p = s->buffer->pos; p < s->buffer->last; p++) {
+ if (*p == LF) {
+ s->state = sw_start;
+ p++;
+ break;
+ }
+ }
+
+ s->buffer->pos = p;
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+}
+
+
+ngx_int_t
+ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_str_t *arg;
+
+#if (NGX_MAIL_SSL)
+ if (ngx_mail_starttls_only(s, c)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+#endif
+
+ if (s->args.nelts == 0) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ arg = s->args.elts;
+
+ if (arg[0].len == 5) {
+
+ if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5) == 0) {
+
+ if (s->args.nelts == 1) {
+ return NGX_MAIL_AUTH_LOGIN;
+ }
+
+ if (s->args.nelts == 2) {
+ return NGX_MAIL_AUTH_LOGIN_USERNAME;
+ }
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN", 5) == 0) {
+
+ if (s->args.nelts == 1) {
+ return NGX_MAIL_AUTH_PLAIN;
+ }
+
+ if (s->args.nelts == 2) {
+ return ngx_mail_auth_plain(s, c, 1);
+ }
+ }
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ if (arg[0].len == 8) {
+
+ if (ngx_strncasecmp(arg[0].data, (u_char *) "CRAM-MD5", 8) == 0) {
+
+ if (s->args.nelts != 1) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ return NGX_MAIL_AUTH_CRAM_MD5;
+ }
+
+ if (ngx_strncasecmp(arg[0].data, (u_char *) "EXTERNAL", 8) == 0) {
+
+ if (s->args.nelts == 1) {
+ return NGX_MAIL_AUTH_EXTERNAL;
+ }
+
+ if (s->args.nelts == 2) {
+ return ngx_mail_auth_external(s, c, 1);
+ }
+ }
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+}
diff --git a/src/nginx-1.18.0/src/mail/ngx_mail_pop3_handler.c b/src/nginx-1.18.0/src/mail/ngx_mail_pop3_handler.c
new file mode 100644
index 0000000..9310c27
--- /dev/null
+++ b/src/nginx-1.18.0/src/mail/ngx_mail_pop3_handler.c
@@ -0,0 +1,515 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+#include
+#include
+#include
+
+
+static ngx_int_t ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c);
+static ngx_int_t ngx_mail_pop3_pass(ngx_mail_session_t *s, ngx_connection_t *c);
+static ngx_int_t ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c,
+ ngx_int_t stls);
+static ngx_int_t ngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c);
+static ngx_int_t ngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c);
+static ngx_int_t ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c);
+
+
+static u_char pop3_greeting[] = "+OK POP3 ready" CRLF;
+static u_char pop3_ok[] = "+OK" CRLF;
+static u_char pop3_next[] = "+ " CRLF;
+static u_char pop3_username[] = "+ VXNlcm5hbWU6" CRLF;
+static u_char pop3_password[] = "+ UGFzc3dvcmQ6" CRLF;
+static u_char pop3_invalid_command[] = "-ERR invalid command" CRLF;
+
+
+void
+ngx_mail_pop3_init_session(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ u_char *p;
+ ngx_mail_core_srv_conf_t *cscf;
+ ngx_mail_pop3_srv_conf_t *pscf;
+
+ pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ if (pscf->auth_methods
+ & (NGX_MAIL_AUTH_APOP_ENABLED|NGX_MAIL_AUTH_CRAM_MD5_ENABLED))
+ {
+ if (ngx_mail_salt(s, c, cscf) != NGX_OK) {
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ s->out.data = ngx_pnalloc(c->pool, sizeof(pop3_greeting) + s->salt.len);
+ if (s->out.data == NULL) {
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ p = ngx_cpymem(s->out.data, pop3_greeting, sizeof(pop3_greeting) - 3);
+ *p++ = ' ';
+ p = ngx_cpymem(p, s->salt.data, s->salt.len);
+
+ s->out.len = p - s->out.data;
+
+ } else {
+ ngx_str_set(&s->out, pop3_greeting);
+ }
+
+ c->read->handler = ngx_mail_pop3_init_protocol;
+
+ ngx_add_timer(c->read, cscf->timeout);
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ }
+
+ ngx_mail_send(c->write);
+}
+
+
+void
+ngx_mail_pop3_init_protocol(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+
+ c = rev->data;
+
+ c->log->action = "in auth state";
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ c->timedout = 1;
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ s = c->data;
+
+ if (s->buffer == NULL) {
+ if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t))
+ == NGX_ERROR)
+ {
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ s->buffer = ngx_create_temp_buf(c->pool, 128);
+ if (s->buffer == NULL) {
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+ }
+
+ s->mail_state = ngx_pop3_start;
+ c->read->handler = ngx_mail_pop3_auth_state;
+
+ ngx_mail_pop3_auth_state(rev);
+}
+
+
+void
+ngx_mail_pop3_auth_state(ngx_event_t *rev)
+{
+ ngx_int_t rc;
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+
+ c = rev->data;
+ s = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 auth state");
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ c->timedout = 1;
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ if (s->out.len) {
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 send handler busy");
+ s->blocked = 1;
+ return;
+ }
+
+ s->blocked = 0;
+
+ rc = ngx_mail_read_command(s, c);
+
+ if (rc == NGX_AGAIN || rc == NGX_ERROR) {
+ return;
+ }
+
+ ngx_str_set(&s->out, pop3_ok);
+
+ if (rc == NGX_OK) {
+ switch (s->mail_state) {
+
+ case ngx_pop3_start:
+
+ switch (s->command) {
+
+ case NGX_POP3_USER:
+ rc = ngx_mail_pop3_user(s, c);
+ break;
+
+ case NGX_POP3_CAPA:
+ rc = ngx_mail_pop3_capa(s, c, 1);
+ break;
+
+ case NGX_POP3_APOP:
+ rc = ngx_mail_pop3_apop(s, c);
+ break;
+
+ case NGX_POP3_AUTH:
+ rc = ngx_mail_pop3_auth(s, c);
+ break;
+
+ case NGX_POP3_QUIT:
+ s->quit = 1;
+ break;
+
+ case NGX_POP3_NOOP:
+ break;
+
+ case NGX_POP3_STLS:
+ rc = ngx_mail_pop3_stls(s, c);
+ break;
+
+ default:
+ rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+ break;
+ }
+
+ break;
+
+ case ngx_pop3_user:
+
+ switch (s->command) {
+
+ case NGX_POP3_PASS:
+ rc = ngx_mail_pop3_pass(s, c);
+ break;
+
+ case NGX_POP3_CAPA:
+ rc = ngx_mail_pop3_capa(s, c, 0);
+ break;
+
+ case NGX_POP3_QUIT:
+ s->quit = 1;
+ break;
+
+ case NGX_POP3_NOOP:
+ break;
+
+ default:
+ rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+ break;
+ }
+
+ break;
+
+ /* suppress warnings */
+ case ngx_pop3_passwd:
+ break;
+
+ case ngx_pop3_auth_login_username:
+ rc = ngx_mail_auth_login_username(s, c, 0);
+
+ ngx_str_set(&s->out, pop3_password);
+ s->mail_state = ngx_pop3_auth_login_password;
+ break;
+
+ case ngx_pop3_auth_login_password:
+ rc = ngx_mail_auth_login_password(s, c);
+ break;
+
+ case ngx_pop3_auth_plain:
+ rc = ngx_mail_auth_plain(s, c, 0);
+ break;
+
+ case ngx_pop3_auth_cram_md5:
+ rc = ngx_mail_auth_cram_md5(s, c);
+ break;
+
+ case ngx_pop3_auth_external:
+ rc = ngx_mail_auth_external(s, c, 0);
+ break;
+ }
+ }
+
+ switch (rc) {
+
+ case NGX_DONE:
+ ngx_mail_auth(s, c);
+ return;
+
+ case NGX_ERROR:
+ ngx_mail_session_internal_server_error(s);
+ return;
+
+ case NGX_MAIL_PARSE_INVALID_COMMAND:
+ s->mail_state = ngx_pop3_start;
+ s->state = 0;
+
+ ngx_str_set(&s->out, pop3_invalid_command);
+
+ /* fall through */
+
+ case NGX_OK:
+
+ s->args.nelts = 0;
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
+
+ if (s->state) {
+ s->arg_start = s->buffer->start;
+ }
+
+ ngx_mail_send(c->write);
+ }
+}
+
+static ngx_int_t
+ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_str_t *arg;
+
+#if (NGX_MAIL_SSL)
+ if (ngx_mail_starttls_only(s, c)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+#endif
+
+ if (s->args.nelts != 1) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ arg = s->args.elts;
+ s->login.len = arg[0].len;
+ s->login.data = ngx_pnalloc(c->pool, s->login.len);
+ if (s->login.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s->login.data, arg[0].data, s->login.len);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "pop3 login: \"%V\"", &s->login);
+
+ s->mail_state = ngx_pop3_user;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_mail_pop3_pass(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_str_t *arg;
+
+ if (s->args.nelts != 1) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ arg = s->args.elts;
+ s->passwd.len = arg[0].len;
+ s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);
+ if (s->passwd.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s->passwd.data, arg[0].data, s->passwd.len);
+
+#if (NGX_DEBUG_MAIL_PASSWD)
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "pop3 passwd: \"%V\"", &s->passwd);
+#endif
+
+ return NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c, ngx_int_t stls)
+{
+ ngx_mail_pop3_srv_conf_t *pscf;
+
+ pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);
+
+#if (NGX_MAIL_SSL)
+
+ if (stls && c->ssl == NULL) {
+ ngx_mail_ssl_conf_t *sslcf;
+
+ sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+
+ if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {
+ s->out = pscf->starttls_capability;
+ return NGX_OK;
+ }
+
+ if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
+ s->out = pscf->starttls_only_capability;
+ return NGX_OK;
+ }
+ }
+
+#endif
+
+ s->out = pscf->capability;
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+#if (NGX_MAIL_SSL)
+ ngx_mail_ssl_conf_t *sslcf;
+
+ if (c->ssl == NULL) {
+ sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+ if (sslcf->starttls) {
+ c->read->handler = ngx_mail_starttls_handler;
+ return NGX_OK;
+ }
+ }
+
+#endif
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+}
+
+
+static ngx_int_t
+ngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_str_t *arg;
+ ngx_mail_pop3_srv_conf_t *pscf;
+
+#if (NGX_MAIL_SSL)
+ if (ngx_mail_starttls_only(s, c)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+#endif
+
+ if (s->args.nelts != 2) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);
+
+ if (!(pscf->auth_methods & NGX_MAIL_AUTH_APOP_ENABLED)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ arg = s->args.elts;
+
+ s->login.len = arg[0].len;
+ s->login.data = ngx_pnalloc(c->pool, s->login.len);
+ if (s->login.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s->login.data, arg[0].data, s->login.len);
+
+ s->passwd.len = arg[1].len;
+ s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);
+ if (s->passwd.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "pop3 apop: \"%V\" \"%V\"", &s->login, &s->passwd);
+
+ s->auth_method = NGX_MAIL_AUTH_APOP;
+
+ return NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_int_t rc;
+ ngx_mail_pop3_srv_conf_t *pscf;
+
+#if (NGX_MAIL_SSL)
+ if (ngx_mail_starttls_only(s, c)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+#endif
+
+ pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);
+
+ if (s->args.nelts == 0) {
+ s->out = pscf->auth_capability;
+ s->state = 0;
+
+ return NGX_OK;
+ }
+
+ rc = ngx_mail_auth_parse(s, c);
+
+ switch (rc) {
+
+ case NGX_MAIL_AUTH_LOGIN:
+
+ ngx_str_set(&s->out, pop3_username);
+ s->mail_state = ngx_pop3_auth_login_username;
+
+ return NGX_OK;
+
+ case NGX_MAIL_AUTH_LOGIN_USERNAME:
+
+ ngx_str_set(&s->out, pop3_password);
+ s->mail_state = ngx_pop3_auth_login_password;
+
+ return ngx_mail_auth_login_username(s, c, 1);
+
+ case NGX_MAIL_AUTH_PLAIN:
+
+ ngx_str_set(&s->out, pop3_next);
+ s->mail_state = ngx_pop3_auth_plain;
+
+ return NGX_OK;
+
+ case NGX_MAIL_AUTH_CRAM_MD5:
+
+ if (!(pscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) {
+ s->mail_state = ngx_pop3_auth_cram_md5;
+ return NGX_OK;
+ }
+
+ return NGX_ERROR;
+
+ case NGX_MAIL_AUTH_EXTERNAL:
+
+ if (!(pscf->auth_methods & NGX_MAIL_AUTH_EXTERNAL_ENABLED)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ ngx_str_set(&s->out, pop3_username);
+ s->mail_state = ngx_pop3_auth_external;
+
+ return NGX_OK;
+ }
+
+ return rc;
+}
diff --git a/src/nginx-1.18.0/src/mail/ngx_mail_pop3_module.c b/src/nginx-1.18.0/src/mail/ngx_mail_pop3_module.c
new file mode 100644
index 0000000..a673070
--- /dev/null
+++ b/src/nginx-1.18.0/src/mail/ngx_mail_pop3_module.c
@@ -0,0 +1,322 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+#include
+#include
+#include
+
+
+static void *ngx_mail_pop3_create_srv_conf(ngx_conf_t *cf);
+static char *ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+
+
+static ngx_str_t ngx_mail_pop3_default_capabilities[] = {
+ ngx_string("TOP"),
+ ngx_string("USER"),
+ ngx_string("UIDL"),
+ ngx_null_string
+};
+
+
+static ngx_conf_bitmask_t ngx_mail_pop3_auth_methods[] = {
+ { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED },
+ { ngx_string("apop"), NGX_MAIL_AUTH_APOP_ENABLED },
+ { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED },
+ { ngx_string("external"), NGX_MAIL_AUTH_EXTERNAL_ENABLED },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_str_t ngx_mail_pop3_auth_methods_names[] = {
+ ngx_string("PLAIN"),
+ ngx_string("LOGIN"),
+ ngx_null_string, /* APOP */
+ ngx_string("CRAM-MD5"),
+ ngx_string("EXTERNAL"),
+ ngx_null_string /* NONE */
+};
+
+
+static ngx_mail_protocol_t ngx_mail_pop3_protocol = {
+ ngx_string("pop3"),
+ { 110, 995, 0, 0 },
+ NGX_MAIL_POP3_PROTOCOL,
+
+ ngx_mail_pop3_init_session,
+ ngx_mail_pop3_init_protocol,
+ ngx_mail_pop3_parse_command,
+ ngx_mail_pop3_auth_state,
+
+ ngx_string("-ERR internal server error" CRLF),
+ ngx_string("-ERR SSL certificate error" CRLF),
+ ngx_string("-ERR No required SSL certificate" CRLF)
+};
+
+
+static ngx_command_t ngx_mail_pop3_commands[] = {
+
+ { ngx_string("pop3_capabilities"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+ ngx_mail_capabilities,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_pop3_srv_conf_t, capabilities),
+ NULL },
+
+ { ngx_string("pop3_auth"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_pop3_srv_conf_t, auth_methods),
+ &ngx_mail_pop3_auth_methods },
+
+ ngx_null_command
+};
+
+
+static ngx_mail_module_t ngx_mail_pop3_module_ctx = {
+ &ngx_mail_pop3_protocol, /* protocol */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_mail_pop3_create_srv_conf, /* create server configuration */
+ ngx_mail_pop3_merge_srv_conf /* merge server configuration */
+};
+
+
+ngx_module_t ngx_mail_pop3_module = {
+ NGX_MODULE_V1,
+ &ngx_mail_pop3_module_ctx, /* module context */
+ ngx_mail_pop3_commands, /* module directives */
+ NGX_MAIL_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static void *
+ngx_mail_pop3_create_srv_conf(ngx_conf_t *cf)
+{
+ ngx_mail_pop3_srv_conf_t *pscf;
+
+ pscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_pop3_srv_conf_t));
+ if (pscf == NULL) {
+ return NULL;
+ }
+
+ if (ngx_array_init(&pscf->capabilities, cf->pool, 4, sizeof(ngx_str_t))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ return pscf;
+}
+
+
+static char *
+ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_mail_pop3_srv_conf_t *prev = parent;
+ ngx_mail_pop3_srv_conf_t *conf = child;
+
+ u_char *p;
+ size_t size, stls_only_size;
+ ngx_str_t *c, *d;
+ ngx_uint_t i, m;
+
+ ngx_conf_merge_bitmask_value(conf->auth_methods,
+ prev->auth_methods,
+ (NGX_CONF_BITMASK_SET
+ |NGX_MAIL_AUTH_PLAIN_ENABLED));
+
+ if (conf->auth_methods & NGX_MAIL_AUTH_PLAIN_ENABLED) {
+ conf->auth_methods |= NGX_MAIL_AUTH_LOGIN_ENABLED;
+ }
+
+ if (conf->capabilities.nelts == 0) {
+ conf->capabilities = prev->capabilities;
+ }
+
+ if (conf->capabilities.nelts == 0) {
+
+ for (d = ngx_mail_pop3_default_capabilities; d->len; d++) {
+ c = ngx_array_push(&conf->capabilities);
+ if (c == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *c = *d;
+ }
+ }
+
+ size = sizeof("+OK Capability list follows" CRLF) - 1
+ + sizeof("." CRLF) - 1;
+
+ stls_only_size = size + sizeof("STLS" CRLF) - 1;
+
+ c = conf->capabilities.elts;
+ for (i = 0; i < conf->capabilities.nelts; i++) {
+ size += c[i].len + sizeof(CRLF) - 1;
+
+ if (ngx_strcasecmp(c[i].data, (u_char *) "USER") == 0) {
+ continue;
+ }
+
+ stls_only_size += c[i].len + sizeof(CRLF) - 1;
+ }
+
+ size += sizeof("SASL") - 1 + sizeof(CRLF) - 1;
+
+ for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
+ m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
+ m <<= 1, i++)
+ {
+ if (ngx_mail_pop3_auth_methods_names[i].len == 0) {
+ continue;
+ }
+
+ if (m & conf->auth_methods) {
+ size += 1 + ngx_mail_pop3_auth_methods_names[i].len;
+ }
+ }
+
+ p = ngx_pnalloc(cf->pool, size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->capability.len = size;
+ conf->capability.data = p;
+
+ p = ngx_cpymem(p, "+OK Capability list follows" CRLF,
+ sizeof("+OK Capability list follows" CRLF) - 1);
+
+ for (i = 0; i < conf->capabilities.nelts; i++) {
+ p = ngx_cpymem(p, c[i].data, c[i].len);
+ *p++ = CR; *p++ = LF;
+ }
+
+ p = ngx_cpymem(p, "SASL", sizeof("SASL") - 1);
+
+ for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
+ m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
+ m <<= 1, i++)
+ {
+ if (ngx_mail_pop3_auth_methods_names[i].len == 0) {
+ continue;
+ }
+
+ if (m & conf->auth_methods) {
+ *p++ = ' ';
+ p = ngx_cpymem(p, ngx_mail_pop3_auth_methods_names[i].data,
+ ngx_mail_pop3_auth_methods_names[i].len);
+ }
+ }
+
+ *p++ = CR; *p++ = LF;
+
+ *p++ = '.'; *p++ = CR; *p = LF;
+
+
+ size += sizeof("STLS" CRLF) - 1;
+
+ p = ngx_pnalloc(cf->pool, size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->starttls_capability.len = size;
+ conf->starttls_capability.data = p;
+
+ p = ngx_cpymem(p, conf->capability.data,
+ conf->capability.len - (sizeof("." CRLF) - 1));
+
+ p = ngx_cpymem(p, "STLS" CRLF, sizeof("STLS" CRLF) - 1);
+ *p++ = '.'; *p++ = CR; *p = LF;
+
+
+ size = sizeof("+OK methods supported:" CRLF) - 1
+ + sizeof("." CRLF) - 1;
+
+ for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
+ m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
+ m <<= 1, i++)
+ {
+ if (ngx_mail_pop3_auth_methods_names[i].len == 0) {
+ continue;
+ }
+
+ if (m & conf->auth_methods) {
+ size += ngx_mail_pop3_auth_methods_names[i].len
+ + sizeof(CRLF) - 1;
+ }
+ }
+
+ p = ngx_pnalloc(cf->pool, size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->auth_capability.data = p;
+ conf->auth_capability.len = size;
+
+ p = ngx_cpymem(p, "+OK methods supported:" CRLF,
+ sizeof("+OK methods supported:" CRLF) - 1);
+
+ for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
+ m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
+ m <<= 1, i++)
+ {
+ if (ngx_mail_pop3_auth_methods_names[i].len == 0) {
+ continue;
+ }
+
+ if (m & conf->auth_methods) {
+ p = ngx_cpymem(p, ngx_mail_pop3_auth_methods_names[i].data,
+ ngx_mail_pop3_auth_methods_names[i].len);
+ *p++ = CR; *p++ = LF;
+ }
+ }
+
+ *p++ = '.'; *p++ = CR; *p = LF;
+
+
+ p = ngx_pnalloc(cf->pool, stls_only_size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->starttls_only_capability.len = stls_only_size;
+ conf->starttls_only_capability.data = p;
+
+ p = ngx_cpymem(p, "+OK Capability list follows" CRLF,
+ sizeof("+OK Capability list follows" CRLF) - 1);
+
+ for (i = 0; i < conf->capabilities.nelts; i++) {
+ if (ngx_strcasecmp(c[i].data, (u_char *) "USER") == 0) {
+ continue;
+ }
+
+ p = ngx_cpymem(p, c[i].data, c[i].len);
+ *p++ = CR; *p++ = LF;
+ }
+
+ p = ngx_cpymem(p, "STLS" CRLF, sizeof("STLS" CRLF) - 1);
+ *p++ = '.'; *p++ = CR; *p = LF;
+
+ return NGX_CONF_OK;
+}
diff --git a/src/nginx-1.18.0/src/mail/ngx_mail_pop3_module.h b/src/nginx-1.18.0/src/mail/ngx_mail_pop3_module.h
new file mode 100644
index 0000000..86947a7
--- /dev/null
+++ b/src/nginx-1.18.0/src/mail/ngx_mail_pop3_module.h
@@ -0,0 +1,38 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_MAIL_POP3_MODULE_H_INCLUDED_
+#define _NGX_MAIL_POP3_MODULE_H_INCLUDED_
+
+
+#include
+#include
+#include
+
+
+typedef struct {
+ ngx_str_t capability;
+ ngx_str_t starttls_capability;
+ ngx_str_t starttls_only_capability;
+ ngx_str_t auth_capability;
+
+ ngx_uint_t auth_methods;
+
+ ngx_array_t capabilities;
+} ngx_mail_pop3_srv_conf_t;
+
+
+void ngx_mail_pop3_init_session(ngx_mail_session_t *s, ngx_connection_t *c);
+void ngx_mail_pop3_init_protocol(ngx_event_t *rev);
+void ngx_mail_pop3_auth_state(ngx_event_t *rev);
+ngx_int_t ngx_mail_pop3_parse_command(ngx_mail_session_t *s);
+
+
+extern ngx_module_t ngx_mail_pop3_module;
+
+
+#endif /* _NGX_MAIL_POP3_MODULE_H_INCLUDED_ */
diff --git a/src/nginx-1.18.0/src/mail/ngx_mail_proxy_module.c b/src/nginx-1.18.0/src/mail/ngx_mail_proxy_module.c
new file mode 100644
index 0000000..1c86e54
--- /dev/null
+++ b/src/nginx-1.18.0/src/mail/ngx_mail_proxy_module.c
@@ -0,0 +1,1126 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+#include
+#include
+#include
+
+
+typedef struct {
+ ngx_flag_t enable;
+ ngx_flag_t pass_error_message;
+ ngx_flag_t xclient;
+ size_t buffer_size;
+ ngx_msec_t timeout;
+} ngx_mail_proxy_conf_t;
+
+
+static void ngx_mail_proxy_block_read(ngx_event_t *rev);
+static void ngx_mail_proxy_pop3_handler(ngx_event_t *rev);
+static void ngx_mail_proxy_imap_handler(ngx_event_t *rev);
+static void ngx_mail_proxy_smtp_handler(ngx_event_t *rev);
+static void ngx_mail_proxy_dummy_handler(ngx_event_t *ev);
+static ngx_int_t ngx_mail_proxy_read_response(ngx_mail_session_t *s,
+ ngx_uint_t state);
+static void ngx_mail_proxy_handler(ngx_event_t *ev);
+static void ngx_mail_proxy_upstream_error(ngx_mail_session_t *s);
+static void ngx_mail_proxy_internal_server_error(ngx_mail_session_t *s);
+static void ngx_mail_proxy_close_session(ngx_mail_session_t *s);
+static void *ngx_mail_proxy_create_conf(ngx_conf_t *cf);
+static char *ngx_mail_proxy_merge_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+
+
+static ngx_command_t ngx_mail_proxy_commands[] = {
+
+ { ngx_string("proxy"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_proxy_conf_t, enable),
+ NULL },
+
+ { ngx_string("proxy_buffer"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_proxy_conf_t, buffer_size),
+ NULL },
+
+ { ngx_string("proxy_timeout"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_proxy_conf_t, timeout),
+ NULL },
+
+ { ngx_string("proxy_pass_error_message"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_proxy_conf_t, pass_error_message),
+ NULL },
+
+ { ngx_string("xclient"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_proxy_conf_t, xclient),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_mail_module_t ngx_mail_proxy_module_ctx = {
+ NULL, /* protocol */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_mail_proxy_create_conf, /* create server configuration */
+ ngx_mail_proxy_merge_conf /* merge server configuration */
+};
+
+
+ngx_module_t ngx_mail_proxy_module = {
+ NGX_MODULE_V1,
+ &ngx_mail_proxy_module_ctx, /* module context */
+ ngx_mail_proxy_commands, /* module directives */
+ NGX_MAIL_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static u_char smtp_auth_ok[] = "235 2.0.0 OK" CRLF;
+
+
+void
+ngx_mail_proxy_init(ngx_mail_session_t *s, ngx_addr_t *peer)
+{
+ ngx_int_t rc;
+ ngx_mail_proxy_ctx_t *p;
+ ngx_mail_proxy_conf_t *pcf;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ s->connection->log->action = "connecting to upstream";
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ p = ngx_pcalloc(s->connection->pool, sizeof(ngx_mail_proxy_ctx_t));
+ if (p == NULL) {
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ s->proxy = p;
+
+ p->upstream.sockaddr = peer->sockaddr;
+ p->upstream.socklen = peer->socklen;
+ p->upstream.name = &peer->name;
+ p->upstream.get = ngx_event_get_peer;
+ p->upstream.log = s->connection->log;
+ p->upstream.log_error = NGX_ERROR_ERR;
+
+ rc = ngx_event_connect_peer(&p->upstream);
+
+ if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ ngx_add_timer(p->upstream.connection->read, cscf->timeout);
+
+ p->upstream.connection->data = s;
+ p->upstream.connection->pool = s->connection->pool;
+
+ s->connection->read->handler = ngx_mail_proxy_block_read;
+ p->upstream.connection->write->handler = ngx_mail_proxy_dummy_handler;
+
+ pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
+
+ s->proxy->buffer = ngx_create_temp_buf(s->connection->pool,
+ pcf->buffer_size);
+ if (s->proxy->buffer == NULL) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ s->out.len = 0;
+
+ switch (s->protocol) {
+
+ case NGX_MAIL_POP3_PROTOCOL:
+ p->upstream.connection->read->handler = ngx_mail_proxy_pop3_handler;
+ s->mail_state = ngx_pop3_start;
+ break;
+
+ case NGX_MAIL_IMAP_PROTOCOL:
+ p->upstream.connection->read->handler = ngx_mail_proxy_imap_handler;
+ s->mail_state = ngx_imap_start;
+ break;
+
+ default: /* NGX_MAIL_SMTP_PROTOCOL */
+ p->upstream.connection->read->handler = ngx_mail_proxy_smtp_handler;
+ s->mail_state = ngx_smtp_start;
+ break;
+ }
+}
+
+
+static void
+ngx_mail_proxy_block_read(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy block read");
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ c = rev->data;
+ s = c->data;
+
+ ngx_mail_proxy_close_session(s);
+ }
+}
+
+
+static void
+ngx_mail_proxy_pop3_handler(ngx_event_t *rev)
+{
+ u_char *p;
+ ngx_int_t rc;
+ ngx_str_t line;
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_proxy_conf_t *pcf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+ "mail proxy pop3 auth handler");
+
+ c = rev->data;
+ s = c->data;
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "upstream timed out");
+ c->timedout = 1;
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ rc = ngx_mail_proxy_read_response(s, 0);
+
+ if (rc == NGX_AGAIN) {
+ return;
+ }
+
+ if (rc == NGX_ERROR) {
+ ngx_mail_proxy_upstream_error(s);
+ return;
+ }
+
+ switch (s->mail_state) {
+
+ case ngx_pop3_start:
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send user");
+
+ s->connection->log->action = "sending user name to upstream";
+
+ line.len = sizeof("USER ") - 1 + s->login.len + 2;
+ line.data = ngx_pnalloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ p = ngx_cpymem(line.data, "USER ", sizeof("USER ") - 1);
+ p = ngx_cpymem(p, s->login.data, s->login.len);
+ *p++ = CR; *p = LF;
+
+ s->mail_state = ngx_pop3_user;
+ break;
+
+ case ngx_pop3_user:
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send pass");
+
+ s->connection->log->action = "sending password to upstream";
+
+ line.len = sizeof("PASS ") - 1 + s->passwd.len + 2;
+ line.data = ngx_pnalloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ p = ngx_cpymem(line.data, "PASS ", sizeof("PASS ") - 1);
+ p = ngx_cpymem(p, s->passwd.data, s->passwd.len);
+ *p++ = CR; *p = LF;
+
+ s->mail_state = ngx_pop3_passwd;
+ break;
+
+ case ngx_pop3_passwd:
+ s->connection->read->handler = ngx_mail_proxy_handler;
+ s->connection->write->handler = ngx_mail_proxy_handler;
+ rev->handler = ngx_mail_proxy_handler;
+ c->write->handler = ngx_mail_proxy_handler;
+
+ pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
+ ngx_add_timer(s->connection->read, pcf->timeout);
+ ngx_del_timer(c->read);
+
+ c->log->action = NULL;
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");
+
+ ngx_mail_proxy_handler(s->connection->write);
+
+ return;
+
+ default:
+#if (NGX_SUPPRESS_WARN)
+ ngx_str_null(&line);
+#endif
+ break;
+ }
+
+ if (c->send(c, line.data, line.len) < (ssize_t) line.len) {
+ /*
+ * we treat the incomplete sending as NGX_ERROR
+ * because it is very strange here
+ */
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ s->proxy->buffer->pos = s->proxy->buffer->start;
+ s->proxy->buffer->last = s->proxy->buffer->start;
+}
+
+
+static void
+ngx_mail_proxy_imap_handler(ngx_event_t *rev)
+{
+ u_char *p;
+ ngx_int_t rc;
+ ngx_str_t line;
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_proxy_conf_t *pcf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+ "mail proxy imap auth handler");
+
+ c = rev->data;
+ s = c->data;
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "upstream timed out");
+ c->timedout = 1;
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ rc = ngx_mail_proxy_read_response(s, s->mail_state);
+
+ if (rc == NGX_AGAIN) {
+ return;
+ }
+
+ if (rc == NGX_ERROR) {
+ ngx_mail_proxy_upstream_error(s);
+ return;
+ }
+
+ switch (s->mail_state) {
+
+ case ngx_imap_start:
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+ "mail proxy send login");
+
+ s->connection->log->action = "sending LOGIN command to upstream";
+
+ line.len = s->tag.len + sizeof("LOGIN ") - 1
+ + 1 + NGX_SIZE_T_LEN + 1 + 2;
+ line.data = ngx_pnalloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ line.len = ngx_sprintf(line.data, "%VLOGIN {%uz}" CRLF,
+ &s->tag, s->login.len)
+ - line.data;
+
+ s->mail_state = ngx_imap_login;
+ break;
+
+ case ngx_imap_login:
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send user");
+
+ s->connection->log->action = "sending user name to upstream";
+
+ line.len = s->login.len + 1 + 1 + NGX_SIZE_T_LEN + 1 + 2;
+ line.data = ngx_pnalloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ line.len = ngx_sprintf(line.data, "%V {%uz}" CRLF,
+ &s->login, s->passwd.len)
+ - line.data;
+
+ s->mail_state = ngx_imap_user;
+ break;
+
+ case ngx_imap_user:
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+ "mail proxy send passwd");
+
+ s->connection->log->action = "sending password to upstream";
+
+ line.len = s->passwd.len + 2;
+ line.data = ngx_pnalloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ p = ngx_cpymem(line.data, s->passwd.data, s->passwd.len);
+ *p++ = CR; *p = LF;
+
+ s->mail_state = ngx_imap_passwd;
+ break;
+
+ case ngx_imap_passwd:
+ s->connection->read->handler = ngx_mail_proxy_handler;
+ s->connection->write->handler = ngx_mail_proxy_handler;
+ rev->handler = ngx_mail_proxy_handler;
+ c->write->handler = ngx_mail_proxy_handler;
+
+ pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
+ ngx_add_timer(s->connection->read, pcf->timeout);
+ ngx_del_timer(c->read);
+
+ c->log->action = NULL;
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");
+
+ ngx_mail_proxy_handler(s->connection->write);
+
+ return;
+
+ default:
+#if (NGX_SUPPRESS_WARN)
+ ngx_str_null(&line);
+#endif
+ break;
+ }
+
+ if (c->send(c, line.data, line.len) < (ssize_t) line.len) {
+ /*
+ * we treat the incomplete sending as NGX_ERROR
+ * because it is very strange here
+ */
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ s->proxy->buffer->pos = s->proxy->buffer->start;
+ s->proxy->buffer->last = s->proxy->buffer->start;
+}
+
+
+static void
+ngx_mail_proxy_smtp_handler(ngx_event_t *rev)
+{
+ u_char *p;
+ ngx_int_t rc;
+ ngx_str_t line;
+ ngx_buf_t *b;
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_proxy_conf_t *pcf;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+ "mail proxy smtp auth handler");
+
+ c = rev->data;
+ s = c->data;
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "upstream timed out");
+ c->timedout = 1;
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ rc = ngx_mail_proxy_read_response(s, s->mail_state);
+
+ if (rc == NGX_AGAIN) {
+ return;
+ }
+
+ if (rc == NGX_ERROR) {
+ ngx_mail_proxy_upstream_error(s);
+ return;
+ }
+
+ switch (s->mail_state) {
+
+ case ngx_smtp_start:
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send ehlo");
+
+ s->connection->log->action = "sending HELO/EHLO to upstream";
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ line.len = sizeof("HELO ") - 1 + cscf->server_name.len + 2;
+ line.data = ngx_pnalloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
+
+ p = ngx_cpymem(line.data,
+ ((s->esmtp || pcf->xclient) ? "EHLO " : "HELO "),
+ sizeof("HELO ") - 1);
+
+ p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);
+ *p++ = CR; *p = LF;
+
+ if (pcf->xclient) {
+ s->mail_state = ngx_smtp_helo_xclient;
+
+ } else if (s->auth_method == NGX_MAIL_AUTH_NONE) {
+ s->mail_state = ngx_smtp_helo_from;
+
+ } else {
+ s->mail_state = ngx_smtp_helo;
+ }
+
+ break;
+
+ case ngx_smtp_helo_xclient:
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+ "mail proxy send xclient");
+
+ s->connection->log->action = "sending XCLIENT to upstream";
+
+ line.len = sizeof("XCLIENT ADDR= LOGIN= NAME="
+ CRLF) - 1
+ + s->connection->addr_text.len + s->login.len + s->host.len;
+
+#if (NGX_HAVE_INET6)
+ if (s->connection->sockaddr->sa_family == AF_INET6) {
+ line.len += sizeof("IPV6:") - 1;
+ }
+#endif
+
+ line.data = ngx_pnalloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ p = ngx_cpymem(line.data, "XCLIENT ADDR=", sizeof("XCLIENT ADDR=") - 1);
+
+#if (NGX_HAVE_INET6)
+ if (s->connection->sockaddr->sa_family == AF_INET6) {
+ p = ngx_cpymem(p, "IPV6:", sizeof("IPV6:") - 1);
+ }
+#endif
+
+ p = ngx_copy(p, s->connection->addr_text.data,
+ s->connection->addr_text.len);
+
+ if (s->login.len) {
+ p = ngx_cpymem(p, " LOGIN=", sizeof(" LOGIN=") - 1);
+ p = ngx_copy(p, s->login.data, s->login.len);
+ }
+
+ p = ngx_cpymem(p, " NAME=", sizeof(" NAME=") - 1);
+ p = ngx_copy(p, s->host.data, s->host.len);
+
+ *p++ = CR; *p++ = LF;
+
+ line.len = p - line.data;
+
+ if (s->smtp_helo.len) {
+ s->mail_state = ngx_smtp_xclient_helo;
+
+ } else if (s->auth_method == NGX_MAIL_AUTH_NONE) {
+ s->mail_state = ngx_smtp_xclient_from;
+
+ } else {
+ s->mail_state = ngx_smtp_xclient;
+ }
+
+ break;
+
+ case ngx_smtp_xclient_helo:
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+ "mail proxy send client ehlo");
+
+ s->connection->log->action = "sending client HELO/EHLO to upstream";
+
+ line.len = sizeof("HELO " CRLF) - 1 + s->smtp_helo.len;
+
+ line.data = ngx_pnalloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ line.len = ngx_sprintf(line.data,
+ ((s->esmtp) ? "EHLO %V" CRLF : "HELO %V" CRLF),
+ &s->smtp_helo)
+ - line.data;
+
+ s->mail_state = (s->auth_method == NGX_MAIL_AUTH_NONE) ?
+ ngx_smtp_helo_from : ngx_smtp_helo;
+
+ break;
+
+ case ngx_smtp_helo_from:
+ case ngx_smtp_xclient_from:
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+ "mail proxy send mail from");
+
+ s->connection->log->action = "sending MAIL FROM to upstream";
+
+ line.len = s->smtp_from.len + sizeof(CRLF) - 1;
+ line.data = ngx_pnalloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ p = ngx_cpymem(line.data, s->smtp_from.data, s->smtp_from.len);
+ *p++ = CR; *p = LF;
+
+ s->mail_state = ngx_smtp_from;
+
+ break;
+
+ case ngx_smtp_from:
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+ "mail proxy send rcpt to");
+
+ s->connection->log->action = "sending RCPT TO to upstream";
+
+ line.len = s->smtp_to.len + sizeof(CRLF) - 1;
+ line.data = ngx_pnalloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ p = ngx_cpymem(line.data, s->smtp_to.data, s->smtp_to.len);
+ *p++ = CR; *p = LF;
+
+ s->mail_state = ngx_smtp_to;
+
+ break;
+
+ case ngx_smtp_helo:
+ case ngx_smtp_xclient:
+ case ngx_smtp_to:
+
+ b = s->proxy->buffer;
+
+ if (s->auth_method == NGX_MAIL_AUTH_NONE) {
+ b->pos = b->start;
+
+ } else {
+ ngx_memcpy(b->start, smtp_auth_ok, sizeof(smtp_auth_ok) - 1);
+ b->last = b->start + sizeof(smtp_auth_ok) - 1;
+ }
+
+ s->connection->read->handler = ngx_mail_proxy_handler;
+ s->connection->write->handler = ngx_mail_proxy_handler;
+ rev->handler = ngx_mail_proxy_handler;
+ c->write->handler = ngx_mail_proxy_handler;
+
+ pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
+ ngx_add_timer(s->connection->read, pcf->timeout);
+ ngx_del_timer(c->read);
+
+ c->log->action = NULL;
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");
+
+ if (s->buffer->pos == s->buffer->last) {
+ ngx_mail_proxy_handler(s->connection->write);
+
+ } else {
+ ngx_mail_proxy_handler(c->write);
+ }
+
+ return;
+
+ default:
+#if (NGX_SUPPRESS_WARN)
+ ngx_str_null(&line);
+#endif
+ break;
+ }
+
+ if (c->send(c, line.data, line.len) < (ssize_t) line.len) {
+ /*
+ * we treat the incomplete sending as NGX_ERROR
+ * because it is very strange here
+ */
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ s->proxy->buffer->pos = s->proxy->buffer->start;
+ s->proxy->buffer->last = s->proxy->buffer->start;
+}
+
+
+static void
+ngx_mail_proxy_dummy_handler(ngx_event_t *wev)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0, "mail proxy dummy handler");
+
+ if (ngx_handle_write_event(wev, 0) != NGX_OK) {
+ c = wev->data;
+ s = c->data;
+
+ ngx_mail_proxy_close_session(s);
+ }
+}
+
+
+static ngx_int_t
+ngx_mail_proxy_read_response(ngx_mail_session_t *s, ngx_uint_t state)
+{
+ u_char *p, *m;
+ ssize_t n;
+ ngx_buf_t *b;
+ ngx_mail_proxy_conf_t *pcf;
+
+ s->connection->log->action = "reading response from upstream";
+
+ b = s->proxy->buffer;
+
+ n = s->proxy->upstream.connection->recv(s->proxy->upstream.connection,
+ b->last, b->end - b->last);
+
+ if (n == NGX_ERROR || n == 0) {
+ return NGX_ERROR;
+ }
+
+ if (n == NGX_AGAIN) {
+ return NGX_AGAIN;
+ }
+
+ b->last += n;
+
+ if (b->last - b->pos < 4) {
+ return NGX_AGAIN;
+ }
+
+ if (*(b->last - 2) != CR || *(b->last - 1) != LF) {
+ if (b->last == b->end) {
+ *(b->last - 1) = '\0';
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "upstream sent too long response line: \"%s\"",
+ b->pos);
+ return NGX_ERROR;
+ }
+
+ return NGX_AGAIN;
+ }
+
+ p = b->pos;
+
+ switch (s->protocol) {
+
+ case NGX_MAIL_POP3_PROTOCOL:
+ if (p[0] == '+' && p[1] == 'O' && p[2] == 'K') {
+ return NGX_OK;
+ }
+ break;
+
+ case NGX_MAIL_IMAP_PROTOCOL:
+ switch (state) {
+
+ case ngx_imap_start:
+ if (p[0] == '*' && p[1] == ' ' && p[2] == 'O' && p[3] == 'K') {
+ return NGX_OK;
+ }
+ break;
+
+ case ngx_imap_login:
+ case ngx_imap_user:
+ if (p[0] == '+') {
+ return NGX_OK;
+ }
+ break;
+
+ case ngx_imap_passwd:
+ if (ngx_strncmp(p, s->tag.data, s->tag.len) == 0) {
+ p += s->tag.len;
+ if (p[0] == 'O' && p[1] == 'K') {
+ return NGX_OK;
+ }
+ }
+ break;
+ }
+
+ break;
+
+ default: /* NGX_MAIL_SMTP_PROTOCOL */
+
+ if (p[3] == '-') {
+ /* multiline reply, check if we got last line */
+
+ m = b->last - (sizeof(CRLF "200" CRLF) - 1);
+
+ while (m > p) {
+ if (m[0] == CR && m[1] == LF) {
+ break;
+ }
+
+ m--;
+ }
+
+ if (m <= p || m[5] == '-') {
+ return NGX_AGAIN;
+ }
+ }
+
+ switch (state) {
+
+ case ngx_smtp_start:
+ if (p[0] == '2' && p[1] == '2' && p[2] == '0') {
+ return NGX_OK;
+ }
+ break;
+
+ case ngx_smtp_helo:
+ case ngx_smtp_helo_xclient:
+ case ngx_smtp_helo_from:
+ case ngx_smtp_from:
+ if (p[0] == '2' && p[1] == '5' && p[2] == '0') {
+ return NGX_OK;
+ }
+ break;
+
+ case ngx_smtp_xclient:
+ case ngx_smtp_xclient_from:
+ case ngx_smtp_xclient_helo:
+ if (p[0] == '2' && (p[1] == '2' || p[1] == '5') && p[2] == '0') {
+ return NGX_OK;
+ }
+ break;
+
+ case ngx_smtp_to:
+ return NGX_OK;
+ }
+
+ break;
+ }
+
+ pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
+
+ if (pcf->pass_error_message == 0) {
+ *(b->last - 2) = '\0';
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "upstream sent invalid response: \"%s\"", p);
+ return NGX_ERROR;
+ }
+
+ s->out.len = b->last - p - 2;
+ s->out.data = p;
+
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "upstream sent invalid response: \"%V\"", &s->out);
+
+ s->out.len = b->last - b->pos;
+ s->out.data = b->pos;
+
+ return NGX_ERROR;
+}
+
+
+static void
+ngx_mail_proxy_handler(ngx_event_t *ev)
+{
+ char *action, *recv_action, *send_action;
+ size_t size;
+ ssize_t n;
+ ngx_buf_t *b;
+ ngx_uint_t do_write;
+ ngx_connection_t *c, *src, *dst;
+ ngx_mail_session_t *s;
+ ngx_mail_proxy_conf_t *pcf;
+
+ c = ev->data;
+ s = c->data;
+
+ if (ev->timedout || c->close) {
+ c->log->action = "proxying";
+
+ if (c->close) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, "shutdown timeout");
+
+ } else if (c == s->connection) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "client timed out");
+ c->timedout = 1;
+
+ } else {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "upstream timed out");
+ }
+
+ ngx_mail_proxy_close_session(s);
+ return;
+ }
+
+ if (c == s->connection) {
+ if (ev->write) {
+ recv_action = "proxying and reading from upstream";
+ send_action = "proxying and sending to client";
+ src = s->proxy->upstream.connection;
+ dst = c;
+ b = s->proxy->buffer;
+
+ } else {
+ recv_action = "proxying and reading from client";
+ send_action = "proxying and sending to upstream";
+ src = c;
+ dst = s->proxy->upstream.connection;
+ b = s->buffer;
+ }
+
+ } else {
+ if (ev->write) {
+ recv_action = "proxying and reading from client";
+ send_action = "proxying and sending to upstream";
+ src = s->connection;
+ dst = c;
+ b = s->buffer;
+
+ } else {
+ recv_action = "proxying and reading from upstream";
+ send_action = "proxying and sending to client";
+ src = c;
+ dst = s->connection;
+ b = s->proxy->buffer;
+ }
+ }
+
+ do_write = ev->write ? 1 : 0;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_MAIL, ev->log, 0,
+ "mail proxy handler: %ui, #%d > #%d",
+ do_write, src->fd, dst->fd);
+
+ for ( ;; ) {
+
+ if (do_write) {
+
+ size = b->last - b->pos;
+
+ if (size && dst->write->ready) {
+ c->log->action = send_action;
+
+ n = dst->send(dst, b->pos, size);
+
+ if (n == NGX_ERROR) {
+ ngx_mail_proxy_close_session(s);
+ return;
+ }
+
+ if (n > 0) {
+ b->pos += n;
+
+ if (b->pos == b->last) {
+ b->pos = b->start;
+ b->last = b->start;
+ }
+ }
+ }
+ }
+
+ size = b->end - b->last;
+
+ if (size && src->read->ready) {
+ c->log->action = recv_action;
+
+ n = src->recv(src, b->last, size);
+
+ if (n == NGX_AGAIN || n == 0) {
+ break;
+ }
+
+ if (n > 0) {
+ do_write = 1;
+ b->last += n;
+
+ continue;
+ }
+
+ if (n == NGX_ERROR) {
+ src->read->eof = 1;
+ }
+ }
+
+ break;
+ }
+
+ c->log->action = "proxying";
+
+ if ((s->connection->read->eof && s->buffer->pos == s->buffer->last)
+ || (s->proxy->upstream.connection->read->eof
+ && s->proxy->buffer->pos == s->proxy->buffer->last)
+ || (s->connection->read->eof
+ && s->proxy->upstream.connection->read->eof))
+ {
+ action = c->log->action;
+ c->log->action = NULL;
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, "proxied session done");
+ c->log->action = action;
+
+ ngx_mail_proxy_close_session(s);
+ return;
+ }
+
+ if (ngx_handle_write_event(dst->write, 0) != NGX_OK) {
+ ngx_mail_proxy_close_session(s);
+ return;
+ }
+
+ if (ngx_handle_read_event(dst->read, 0) != NGX_OK) {
+ ngx_mail_proxy_close_session(s);
+ return;
+ }
+
+ if (ngx_handle_write_event(src->write, 0) != NGX_OK) {
+ ngx_mail_proxy_close_session(s);
+ return;
+ }
+
+ if (ngx_handle_read_event(src->read, 0) != NGX_OK) {
+ ngx_mail_proxy_close_session(s);
+ return;
+ }
+
+ if (c == s->connection) {
+ pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
+ ngx_add_timer(c->read, pcf->timeout);
+ }
+}
+
+
+static void
+ngx_mail_proxy_upstream_error(ngx_mail_session_t *s)
+{
+ if (s->proxy->upstream.connection) {
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
+ "close mail proxy connection: %d",
+ s->proxy->upstream.connection->fd);
+
+ ngx_close_connection(s->proxy->upstream.connection);
+ }
+
+ if (s->out.len == 0) {
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ s->quit = 1;
+ ngx_mail_send(s->connection->write);
+}
+
+
+static void
+ngx_mail_proxy_internal_server_error(ngx_mail_session_t *s)
+{
+ if (s->proxy->upstream.connection) {
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
+ "close mail proxy connection: %d",
+ s->proxy->upstream.connection->fd);
+
+ ngx_close_connection(s->proxy->upstream.connection);
+ }
+
+ ngx_mail_session_internal_server_error(s);
+}
+
+
+static void
+ngx_mail_proxy_close_session(ngx_mail_session_t *s)
+{
+ if (s->proxy->upstream.connection) {
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
+ "close mail proxy connection: %d",
+ s->proxy->upstream.connection->fd);
+
+ ngx_close_connection(s->proxy->upstream.connection);
+ }
+
+ ngx_mail_close_connection(s->connection);
+}
+
+
+static void *
+ngx_mail_proxy_create_conf(ngx_conf_t *cf)
+{
+ ngx_mail_proxy_conf_t *pcf;
+
+ pcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_proxy_conf_t));
+ if (pcf == NULL) {
+ return NULL;
+ }
+
+ pcf->enable = NGX_CONF_UNSET;
+ pcf->pass_error_message = NGX_CONF_UNSET;
+ pcf->xclient = NGX_CONF_UNSET;
+ pcf->buffer_size = NGX_CONF_UNSET_SIZE;
+ pcf->timeout = NGX_CONF_UNSET_MSEC;
+
+ return pcf;
+}
+
+
+static char *
+ngx_mail_proxy_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_mail_proxy_conf_t *prev = parent;
+ ngx_mail_proxy_conf_t *conf = child;
+
+ ngx_conf_merge_value(conf->enable, prev->enable, 0);
+ ngx_conf_merge_value(conf->pass_error_message, prev->pass_error_message, 0);
+ ngx_conf_merge_value(conf->xclient, prev->xclient, 1);
+ ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
+ (size_t) ngx_pagesize);
+ ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 24 * 60 * 60000);
+
+ return NGX_CONF_OK;
+}
diff --git a/src/nginx-1.18.0/src/mail/ngx_mail_smtp_handler.c b/src/nginx-1.18.0/src/mail/ngx_mail_smtp_handler.c
new file mode 100644
index 0000000..f1017e0
--- /dev/null
+++ b/src/nginx-1.18.0/src/mail/ngx_mail_smtp_handler.c
@@ -0,0 +1,915 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+#include
+#include
+#include
+
+
+static void ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx);
+static void ngx_mail_smtp_resolve_name(ngx_event_t *rev);
+static void ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx);
+static void ngx_mail_smtp_block_reading(ngx_event_t *rev);
+static void ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c);
+static void ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev);
+static ngx_int_t ngx_mail_smtp_create_buffer(ngx_mail_session_t *s,
+ ngx_connection_t *c);
+
+static ngx_int_t ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c);
+static ngx_int_t ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c);
+static ngx_int_t ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c);
+static ngx_int_t ngx_mail_smtp_starttls(ngx_mail_session_t *s,
+ ngx_connection_t *c);
+static ngx_int_t ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c);
+static ngx_int_t ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c);
+
+static ngx_int_t ngx_mail_smtp_discard_command(ngx_mail_session_t *s,
+ ngx_connection_t *c, char *err);
+static void ngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s,
+ ngx_connection_t *c, char *err);
+
+
+static u_char smtp_ok[] = "250 2.0.0 OK" CRLF;
+static u_char smtp_bye[] = "221 2.0.0 Bye" CRLF;
+static u_char smtp_starttls[] = "220 2.0.0 Start TLS" CRLF;
+static u_char smtp_next[] = "334 " CRLF;
+static u_char smtp_username[] = "334 VXNlcm5hbWU6" CRLF;
+static u_char smtp_password[] = "334 UGFzc3dvcmQ6" CRLF;
+static u_char smtp_invalid_command[] = "500 5.5.1 Invalid command" CRLF;
+static u_char smtp_invalid_pipelining[] =
+ "503 5.5.0 Improper use of SMTP command pipelining" CRLF;
+static u_char smtp_invalid_argument[] = "501 5.5.4 Invalid argument" CRLF;
+static u_char smtp_auth_required[] = "530 5.7.1 Authentication required" CRLF;
+static u_char smtp_bad_sequence[] = "503 5.5.1 Bad sequence of commands" CRLF;
+
+
+static ngx_str_t smtp_unavailable = ngx_string("[UNAVAILABLE]");
+static ngx_str_t smtp_tempunavail = ngx_string("[TEMPUNAVAIL]");
+
+
+void
+ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_resolver_ctx_t *ctx;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ if (cscf->resolver == NULL) {
+ s->host = smtp_unavailable;
+ ngx_mail_smtp_greeting(s, c);
+ return;
+ }
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+ if (c->sockaddr->sa_family == AF_UNIX) {
+ s->host = smtp_tempunavail;
+ ngx_mail_smtp_greeting(s, c);
+ return;
+ }
+#endif
+
+ c->log->action = "in resolving client address";
+
+ ctx = ngx_resolve_start(cscf->resolver, NULL);
+ if (ctx == NULL) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ ctx->addr.sockaddr = c->sockaddr;
+ ctx->addr.socklen = c->socklen;
+ ctx->handler = ngx_mail_smtp_resolve_addr_handler;
+ ctx->data = s;
+ ctx->timeout = cscf->resolver_timeout;
+
+ s->resolver_ctx = ctx;
+ c->read->handler = ngx_mail_smtp_block_reading;
+
+ if (ngx_resolve_addr(ctx) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ }
+}
+
+
+static void
+ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+
+ s = ctx->data;
+ c = s->connection;
+
+ if (ctx->state) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "%V could not be resolved (%i: %s)",
+ &c->addr_text, ctx->state,
+ ngx_resolver_strerror(ctx->state));
+
+ if (ctx->state == NGX_RESOLVE_NXDOMAIN) {
+ s->host = smtp_unavailable;
+
+ } else {
+ s->host = smtp_tempunavail;
+ }
+
+ ngx_resolve_addr_done(ctx);
+
+ ngx_mail_smtp_greeting(s, s->connection);
+
+ return;
+ }
+
+ c->log->action = "in resolving client hostname";
+
+ s->host.data = ngx_pstrdup(c->pool, &ctx->name);
+ if (s->host.data == NULL) {
+ ngx_resolve_addr_done(ctx);
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ s->host.len = ctx->name.len;
+
+ ngx_resolve_addr_done(ctx);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "address resolved: %V", &s->host);
+
+ c->read->handler = ngx_mail_smtp_resolve_name;
+
+ ngx_post_event(c->read, &ngx_posted_events);
+}
+
+
+static void
+ngx_mail_smtp_resolve_name(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_resolver_ctx_t *ctx;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ c = rev->data;
+ s = c->data;
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ ctx = ngx_resolve_start(cscf->resolver, NULL);
+ if (ctx == NULL) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ ctx->name = s->host;
+ ctx->handler = ngx_mail_smtp_resolve_name_handler;
+ ctx->data = s;
+ ctx->timeout = cscf->resolver_timeout;
+
+ s->resolver_ctx = ctx;
+ c->read->handler = ngx_mail_smtp_block_reading;
+
+ if (ngx_resolve_name(ctx) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ }
+}
+
+
+static void
+ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx)
+{
+ ngx_uint_t i;
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+
+ s = ctx->data;
+ c = s->connection;
+
+ if (ctx->state) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "\"%V\" could not be resolved (%i: %s)",
+ &ctx->name, ctx->state,
+ ngx_resolver_strerror(ctx->state));
+
+ if (ctx->state == NGX_RESOLVE_NXDOMAIN) {
+ s->host = smtp_unavailable;
+
+ } else {
+ s->host = smtp_tempunavail;
+ }
+
+ } else {
+
+#if (NGX_DEBUG)
+ {
+ u_char text[NGX_SOCKADDR_STRLEN];
+ ngx_str_t addr;
+
+ addr.data = text;
+
+ for (i = 0; i < ctx->naddrs; i++) {
+ addr.len = ngx_sock_ntop(ctx->addrs[i].sockaddr,
+ ctx->addrs[i].socklen,
+ text, NGX_SOCKADDR_STRLEN, 0);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "name was resolved to %V", &addr);
+ }
+ }
+#endif
+
+ for (i = 0; i < ctx->naddrs; i++) {
+ if (ngx_cmp_sockaddr(ctx->addrs[i].sockaddr, ctx->addrs[i].socklen,
+ c->sockaddr, c->socklen, 0)
+ == NGX_OK)
+ {
+ goto found;
+ }
+ }
+
+ s->host = smtp_unavailable;
+ }
+
+found:
+
+ ngx_resolve_name_done(ctx);
+
+ ngx_mail_smtp_greeting(s, c);
+}
+
+
+static void
+ngx_mail_smtp_block_reading(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_resolver_ctx_t *ctx;
+
+ c = rev->data;
+ s = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp reading blocked");
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+
+ if (s->resolver_ctx) {
+ ctx = s->resolver_ctx;
+
+ if (ctx->handler == ngx_mail_smtp_resolve_addr_handler) {
+ ngx_resolve_addr_done(ctx);
+
+ } else if (ctx->handler == ngx_mail_smtp_resolve_name_handler) {
+ ngx_resolve_name_done(ctx);
+ }
+
+ s->resolver_ctx = NULL;
+ }
+
+ ngx_mail_close_connection(c);
+ }
+}
+
+
+static void
+ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_msec_t timeout;
+ ngx_mail_core_srv_conf_t *cscf;
+ ngx_mail_smtp_srv_conf_t *sscf;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "smtp greeting for \"%V\"", &s->host);
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+ sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);
+
+ timeout = sscf->greeting_delay ? sscf->greeting_delay : cscf->timeout;
+ ngx_add_timer(c->read, timeout);
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ }
+
+ if (c->read->ready) {
+ ngx_post_event(c->read, &ngx_posted_events);
+ }
+
+ if (sscf->greeting_delay) {
+ c->read->handler = ngx_mail_smtp_invalid_pipelining;
+ return;
+ }
+
+ c->read->handler = ngx_mail_smtp_init_protocol;
+
+ s->out = sscf->greeting;
+
+ ngx_mail_send(c->write);
+}
+
+
+static void
+ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_core_srv_conf_t *cscf;
+ ngx_mail_smtp_srv_conf_t *sscf;
+
+ c = rev->data;
+ s = c->data;
+
+ c->log->action = "in delay pipelining state";
+
+ if (rev->timedout) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "delay greeting");
+
+ rev->timedout = 0;
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ c->read->handler = ngx_mail_smtp_init_protocol;
+
+ ngx_add_timer(c->read, cscf->timeout);
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);
+
+ s->out = sscf->greeting;
+
+ } else {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "invalid pipelining");
+
+ if (s->buffer == NULL) {
+ if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) {
+ return;
+ }
+ }
+
+ if (ngx_mail_smtp_discard_command(s, c,
+ "client was rejected before greeting: \"%V\"")
+ != NGX_OK)
+ {
+ return;
+ }
+
+ ngx_str_set(&s->out, smtp_invalid_pipelining);
+ s->quit = 1;
+ }
+
+ ngx_mail_send(c->write);
+}
+
+
+void
+ngx_mail_smtp_init_protocol(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+
+ c = rev->data;
+
+ c->log->action = "in auth state";
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ c->timedout = 1;
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ s = c->data;
+
+ if (s->buffer == NULL) {
+ if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) {
+ return;
+ }
+ }
+
+ s->mail_state = ngx_smtp_start;
+ c->read->handler = ngx_mail_smtp_auth_state;
+
+ ngx_mail_smtp_auth_state(rev);
+}
+
+
+static ngx_int_t
+ngx_mail_smtp_create_buffer(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_mail_smtp_srv_conf_t *sscf;
+
+ if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) == NGX_ERROR) {
+ ngx_mail_session_internal_server_error(s);
+ return NGX_ERROR;
+ }
+
+ sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);
+
+ s->buffer = ngx_create_temp_buf(c->pool, sscf->client_buffer_size);
+ if (s->buffer == NULL) {
+ ngx_mail_session_internal_server_error(s);
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+void
+ngx_mail_smtp_auth_state(ngx_event_t *rev)
+{
+ ngx_int_t rc;
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+
+ c = rev->data;
+ s = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp auth state");
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ c->timedout = 1;
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ if (s->out.len) {
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp send handler busy");
+ s->blocked = 1;
+ return;
+ }
+
+ s->blocked = 0;
+
+ rc = ngx_mail_read_command(s, c);
+
+ if (rc == NGX_AGAIN || rc == NGX_ERROR) {
+ return;
+ }
+
+ ngx_str_set(&s->out, smtp_ok);
+
+ if (rc == NGX_OK) {
+ switch (s->mail_state) {
+
+ case ngx_smtp_start:
+
+ switch (s->command) {
+
+ case NGX_SMTP_HELO:
+ case NGX_SMTP_EHLO:
+ rc = ngx_mail_smtp_helo(s, c);
+ break;
+
+ case NGX_SMTP_AUTH:
+ rc = ngx_mail_smtp_auth(s, c);
+ break;
+
+ case NGX_SMTP_QUIT:
+ s->quit = 1;
+ ngx_str_set(&s->out, smtp_bye);
+ break;
+
+ case NGX_SMTP_MAIL:
+ rc = ngx_mail_smtp_mail(s, c);
+ break;
+
+ case NGX_SMTP_RCPT:
+ rc = ngx_mail_smtp_rcpt(s, c);
+ break;
+
+ case NGX_SMTP_RSET:
+ rc = ngx_mail_smtp_rset(s, c);
+ break;
+
+ case NGX_SMTP_NOOP:
+ break;
+
+ case NGX_SMTP_STARTTLS:
+ rc = ngx_mail_smtp_starttls(s, c);
+ ngx_str_set(&s->out, smtp_starttls);
+ break;
+
+ default:
+ rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+ break;
+ }
+
+ break;
+
+ case ngx_smtp_auth_login_username:
+ rc = ngx_mail_auth_login_username(s, c, 0);
+
+ ngx_str_set(&s->out, smtp_password);
+ s->mail_state = ngx_smtp_auth_login_password;
+ break;
+
+ case ngx_smtp_auth_login_password:
+ rc = ngx_mail_auth_login_password(s, c);
+ break;
+
+ case ngx_smtp_auth_plain:
+ rc = ngx_mail_auth_plain(s, c, 0);
+ break;
+
+ case ngx_smtp_auth_cram_md5:
+ rc = ngx_mail_auth_cram_md5(s, c);
+ break;
+
+ case ngx_smtp_auth_external:
+ rc = ngx_mail_auth_external(s, c, 0);
+ break;
+ }
+ }
+
+ if (s->buffer->pos < s->buffer->last) {
+ s->blocked = 1;
+ }
+
+ switch (rc) {
+
+ case NGX_DONE:
+ ngx_mail_auth(s, c);
+ return;
+
+ case NGX_ERROR:
+ ngx_mail_session_internal_server_error(s);
+ return;
+
+ case NGX_MAIL_PARSE_INVALID_COMMAND:
+ s->mail_state = ngx_smtp_start;
+ s->state = 0;
+ ngx_str_set(&s->out, smtp_invalid_command);
+
+ /* fall through */
+
+ case NGX_OK:
+ s->args.nelts = 0;
+
+ if (s->buffer->pos == s->buffer->last) {
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
+ }
+
+ if (s->state) {
+ s->arg_start = s->buffer->pos;
+ }
+
+ ngx_mail_send(c->write);
+ }
+}
+
+
+static ngx_int_t
+ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_str_t *arg;
+ ngx_mail_smtp_srv_conf_t *sscf;
+
+ if (s->args.nelts != 1) {
+ ngx_str_set(&s->out, smtp_invalid_argument);
+ s->state = 0;
+ return NGX_OK;
+ }
+
+ arg = s->args.elts;
+
+ s->smtp_helo.len = arg[0].len;
+
+ s->smtp_helo.data = ngx_pnalloc(c->pool, arg[0].len);
+ if (s->smtp_helo.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s->smtp_helo.data, arg[0].data, arg[0].len);
+
+ ngx_str_null(&s->smtp_from);
+ ngx_str_null(&s->smtp_to);
+
+ sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);
+
+ if (s->command == NGX_SMTP_HELO) {
+ s->out = sscf->server_name;
+
+ } else {
+ s->esmtp = 1;
+
+#if (NGX_MAIL_SSL)
+
+ if (c->ssl == NULL) {
+ ngx_mail_ssl_conf_t *sslcf;
+
+ sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+
+ if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {
+ s->out = sscf->starttls_capability;
+ return NGX_OK;
+ }
+
+ if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
+ s->out = sscf->starttls_only_capability;
+ return NGX_OK;
+ }
+ }
+#endif
+
+ s->out = sscf->capability;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_int_t rc;
+ ngx_mail_core_srv_conf_t *cscf;
+ ngx_mail_smtp_srv_conf_t *sscf;
+
+#if (NGX_MAIL_SSL)
+ if (ngx_mail_starttls_only(s, c)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+#endif
+
+ if (s->args.nelts == 0) {
+ ngx_str_set(&s->out, smtp_invalid_argument);
+ s->state = 0;
+ return NGX_OK;
+ }
+
+ sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);
+
+ rc = ngx_mail_auth_parse(s, c);
+
+ switch (rc) {
+
+ case NGX_MAIL_AUTH_LOGIN:
+
+ ngx_str_set(&s->out, smtp_username);
+ s->mail_state = ngx_smtp_auth_login_username;
+
+ return NGX_OK;
+
+ case NGX_MAIL_AUTH_LOGIN_USERNAME:
+
+ ngx_str_set(&s->out, smtp_password);
+ s->mail_state = ngx_smtp_auth_login_password;
+
+ return ngx_mail_auth_login_username(s, c, 1);
+
+ case NGX_MAIL_AUTH_PLAIN:
+
+ ngx_str_set(&s->out, smtp_next);
+ s->mail_state = ngx_smtp_auth_plain;
+
+ return NGX_OK;
+
+ case NGX_MAIL_AUTH_CRAM_MD5:
+
+ if (!(sscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ if (s->salt.data == NULL) {
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ if (ngx_mail_salt(s, c, cscf) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ if (ngx_mail_auth_cram_md5_salt(s, c, "334 ", 4) == NGX_OK) {
+ s->mail_state = ngx_smtp_auth_cram_md5;
+ return NGX_OK;
+ }
+
+ return NGX_ERROR;
+
+ case NGX_MAIL_AUTH_EXTERNAL:
+
+ if (!(sscf->auth_methods & NGX_MAIL_AUTH_EXTERNAL_ENABLED)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ ngx_str_set(&s->out, smtp_username);
+ s->mail_state = ngx_smtp_auth_external;
+
+ return NGX_OK;
+ }
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_str_t *arg, cmd;
+ ngx_mail_smtp_srv_conf_t *sscf;
+
+ sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);
+
+ if (!(sscf->auth_methods & NGX_MAIL_AUTH_NONE_ENABLED)) {
+ ngx_mail_smtp_log_rejected_command(s, c, "client was rejected: \"%V\"");
+ ngx_str_set(&s->out, smtp_auth_required);
+ return NGX_OK;
+ }
+
+ /* auth none */
+
+ if (s->smtp_from.len) {
+ ngx_str_set(&s->out, smtp_bad_sequence);
+ return NGX_OK;
+ }
+
+ if (s->args.nelts == 0) {
+ ngx_str_set(&s->out, smtp_invalid_argument);
+ return NGX_OK;
+ }
+
+ arg = s->args.elts;
+ arg += s->args.nelts - 1;
+
+ cmd.len = arg->data + arg->len - s->cmd.data;
+ cmd.data = s->cmd.data;
+
+ s->smtp_from.len = cmd.len;
+
+ s->smtp_from.data = ngx_pnalloc(c->pool, cmd.len);
+ if (s->smtp_from.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s->smtp_from.data, cmd.data, cmd.len);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "smtp mail from:\"%V\"", &s->smtp_from);
+
+ ngx_str_set(&s->out, smtp_ok);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_str_t *arg, cmd;
+
+ if (s->smtp_from.len == 0) {
+ ngx_str_set(&s->out, smtp_bad_sequence);
+ return NGX_OK;
+ }
+
+ if (s->args.nelts == 0) {
+ ngx_str_set(&s->out, smtp_invalid_argument);
+ return NGX_OK;
+ }
+
+ arg = s->args.elts;
+ arg += s->args.nelts - 1;
+
+ cmd.len = arg->data + arg->len - s->cmd.data;
+ cmd.data = s->cmd.data;
+
+ s->smtp_to.len = cmd.len;
+
+ s->smtp_to.data = ngx_pnalloc(c->pool, cmd.len);
+ if (s->smtp_to.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s->smtp_to.data, cmd.data, cmd.len);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "smtp rcpt to:\"%V\"", &s->smtp_to);
+
+ s->auth_method = NGX_MAIL_AUTH_NONE;
+
+ return NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_str_null(&s->smtp_from);
+ ngx_str_null(&s->smtp_to);
+ ngx_str_set(&s->out, smtp_ok);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_mail_smtp_starttls(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+#if (NGX_MAIL_SSL)
+ ngx_mail_ssl_conf_t *sslcf;
+
+ if (c->ssl == NULL) {
+ sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+ if (sslcf->starttls) {
+
+ /*
+ * RFC3207 requires us to discard any knowledge
+ * obtained from client before STARTTLS.
+ */
+
+ ngx_str_null(&s->smtp_helo);
+ ngx_str_null(&s->smtp_from);
+ ngx_str_null(&s->smtp_to);
+
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
+
+ c->read->handler = ngx_mail_starttls_handler;
+ return NGX_OK;
+ }
+ }
+
+#endif
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+}
+
+
+static ngx_int_t
+ngx_mail_smtp_discard_command(ngx_mail_session_t *s, ngx_connection_t *c,
+ char *err)
+{
+ ssize_t n;
+
+ n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last);
+
+ if (n == NGX_ERROR || n == 0) {
+ ngx_mail_close_connection(c);
+ return NGX_ERROR;
+ }
+
+ if (n > 0) {
+ s->buffer->last += n;
+ }
+
+ if (n == NGX_AGAIN) {
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ ngx_mail_session_internal_server_error(s);
+ return NGX_ERROR;
+ }
+
+ return NGX_AGAIN;
+ }
+
+ ngx_mail_smtp_log_rejected_command(s, c, err);
+
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s, ngx_connection_t *c,
+ char *err)
+{
+ u_char ch;
+ ngx_str_t cmd;
+ ngx_uint_t i;
+
+ if (c->log->log_level < NGX_LOG_INFO) {
+ return;
+ }
+
+ cmd.len = s->buffer->last - s->buffer->start;
+ cmd.data = s->buffer->start;
+
+ for (i = 0; i < cmd.len; i++) {
+ ch = cmd.data[i];
+
+ if (ch != CR && ch != LF) {
+ continue;
+ }
+
+ cmd.data[i] = '_';
+ }
+
+ cmd.len = i;
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, err, &cmd);
+}
diff --git a/src/nginx-1.18.0/src/mail/ngx_mail_smtp_module.c b/src/nginx-1.18.0/src/mail/ngx_mail_smtp_module.c
new file mode 100644
index 0000000..3b5a2d8
--- /dev/null
+++ b/src/nginx-1.18.0/src/mail/ngx_mail_smtp_module.c
@@ -0,0 +1,311 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+#include
+#include
+#include
+
+
+static void *ngx_mail_smtp_create_srv_conf(ngx_conf_t *cf);
+static char *ngx_mail_smtp_merge_srv_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+
+
+static ngx_conf_bitmask_t ngx_mail_smtp_auth_methods[] = {
+ { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED },
+ { ngx_string("login"), NGX_MAIL_AUTH_LOGIN_ENABLED },
+ { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED },
+ { ngx_string("external"), NGX_MAIL_AUTH_EXTERNAL_ENABLED },
+ { ngx_string("none"), NGX_MAIL_AUTH_NONE_ENABLED },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_str_t ngx_mail_smtp_auth_methods_names[] = {
+ ngx_string("PLAIN"),
+ ngx_string("LOGIN"),
+ ngx_null_string, /* APOP */
+ ngx_string("CRAM-MD5"),
+ ngx_string("EXTERNAL"),
+ ngx_null_string /* NONE */
+};
+
+
+static ngx_mail_protocol_t ngx_mail_smtp_protocol = {
+ ngx_string("smtp"),
+ { 25, 465, 587, 0 },
+ NGX_MAIL_SMTP_PROTOCOL,
+
+ ngx_mail_smtp_init_session,
+ ngx_mail_smtp_init_protocol,
+ ngx_mail_smtp_parse_command,
+ ngx_mail_smtp_auth_state,
+
+ ngx_string("451 4.3.2 Internal server error" CRLF),
+ ngx_string("421 4.7.1 SSL certificate error" CRLF),
+ ngx_string("421 4.7.1 No required SSL certificate" CRLF)
+};
+
+
+static ngx_command_t ngx_mail_smtp_commands[] = {
+
+ { ngx_string("smtp_client_buffer"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_smtp_srv_conf_t, client_buffer_size),
+ NULL },
+
+ { ngx_string("smtp_greeting_delay"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_smtp_srv_conf_t, greeting_delay),
+ NULL },
+
+ { ngx_string("smtp_capabilities"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+ ngx_mail_capabilities,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_smtp_srv_conf_t, capabilities),
+ NULL },
+
+ { ngx_string("smtp_auth"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_smtp_srv_conf_t, auth_methods),
+ &ngx_mail_smtp_auth_methods },
+
+ ngx_null_command
+};
+
+
+static ngx_mail_module_t ngx_mail_smtp_module_ctx = {
+ &ngx_mail_smtp_protocol, /* protocol */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_mail_smtp_create_srv_conf, /* create server configuration */
+ ngx_mail_smtp_merge_srv_conf /* merge server configuration */
+};
+
+
+ngx_module_t ngx_mail_smtp_module = {
+ NGX_MODULE_V1,
+ &ngx_mail_smtp_module_ctx, /* module context */
+ ngx_mail_smtp_commands, /* module directives */
+ NGX_MAIL_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static void *
+ngx_mail_smtp_create_srv_conf(ngx_conf_t *cf)
+{
+ ngx_mail_smtp_srv_conf_t *sscf;
+
+ sscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_smtp_srv_conf_t));
+ if (sscf == NULL) {
+ return NULL;
+ }
+
+ sscf->client_buffer_size = NGX_CONF_UNSET_SIZE;
+ sscf->greeting_delay = NGX_CONF_UNSET_MSEC;
+
+ if (ngx_array_init(&sscf->capabilities, cf->pool, 4, sizeof(ngx_str_t))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ return sscf;
+}
+
+
+static char *
+ngx_mail_smtp_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_mail_smtp_srv_conf_t *prev = parent;
+ ngx_mail_smtp_srv_conf_t *conf = child;
+
+ u_char *p, *auth, *last;
+ size_t size;
+ ngx_str_t *c;
+ ngx_uint_t i, m, auth_enabled;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ ngx_conf_merge_size_value(conf->client_buffer_size,
+ prev->client_buffer_size,
+ (size_t) ngx_pagesize);
+
+ ngx_conf_merge_msec_value(conf->greeting_delay,
+ prev->greeting_delay, 0);
+
+ ngx_conf_merge_bitmask_value(conf->auth_methods,
+ prev->auth_methods,
+ (NGX_CONF_BITMASK_SET
+ |NGX_MAIL_AUTH_PLAIN_ENABLED
+ |NGX_MAIL_AUTH_LOGIN_ENABLED));
+
+
+ cscf = ngx_mail_conf_get_module_srv_conf(cf, ngx_mail_core_module);
+
+ size = sizeof("220 ESMTP ready" CRLF) - 1 + cscf->server_name.len;
+
+ p = ngx_pnalloc(cf->pool, size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->greeting.len = size;
+ conf->greeting.data = p;
+
+ *p++ = '2'; *p++ = '2'; *p++ = '0'; *p++ = ' ';
+ p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);
+ ngx_memcpy(p, " ESMTP ready" CRLF, sizeof(" ESMTP ready" CRLF) - 1);
+
+
+ size = sizeof("250 " CRLF) - 1 + cscf->server_name.len;
+
+ p = ngx_pnalloc(cf->pool, size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->server_name.len = size;
+ conf->server_name.data = p;
+
+ *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' ';
+ p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);
+ *p++ = CR; *p = LF;
+
+
+ if (conf->capabilities.nelts == 0) {
+ conf->capabilities = prev->capabilities;
+ }
+
+ size = sizeof("250-") - 1 + cscf->server_name.len + sizeof(CRLF) - 1;
+
+ c = conf->capabilities.elts;
+ for (i = 0; i < conf->capabilities.nelts; i++) {
+ size += sizeof("250 ") - 1 + c[i].len + sizeof(CRLF) - 1;
+ }
+
+ auth_enabled = 0;
+
+ for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
+ m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
+ m <<= 1, i++)
+ {
+ if (m & conf->auth_methods) {
+ size += 1 + ngx_mail_smtp_auth_methods_names[i].len;
+ auth_enabled = 1;
+ }
+ }
+
+ if (auth_enabled) {
+ size += sizeof("250 AUTH") - 1 + sizeof(CRLF) - 1;
+ }
+
+ p = ngx_pnalloc(cf->pool, size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->capability.len = size;
+ conf->capability.data = p;
+
+ last = p;
+
+ *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-';
+ p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);
+ *p++ = CR; *p++ = LF;
+
+ for (i = 0; i < conf->capabilities.nelts; i++) {
+ last = p;
+ *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-';
+ p = ngx_cpymem(p, c[i].data, c[i].len);
+ *p++ = CR; *p++ = LF;
+ }
+
+ auth = p;
+
+ if (auth_enabled) {
+ last = p;
+
+ *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' ';
+ *p++ = 'A'; *p++ = 'U'; *p++ = 'T'; *p++ = 'H';
+
+ for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
+ m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
+ m <<= 1, i++)
+ {
+ if (m & conf->auth_methods) {
+ *p++ = ' ';
+ p = ngx_cpymem(p, ngx_mail_smtp_auth_methods_names[i].data,
+ ngx_mail_smtp_auth_methods_names[i].len);
+ }
+ }
+
+ *p++ = CR; *p = LF;
+
+ } else {
+ last[3] = ' ';
+ }
+
+ size += sizeof("250 STARTTLS" CRLF) - 1;
+
+ p = ngx_pnalloc(cf->pool, size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->starttls_capability.len = size;
+ conf->starttls_capability.data = p;
+
+ p = ngx_cpymem(p, conf->capability.data, conf->capability.len);
+
+ ngx_memcpy(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1);
+
+ p = conf->starttls_capability.data
+ + (last - conf->capability.data) + 3;
+ *p = '-';
+
+ size = (auth - conf->capability.data)
+ + sizeof("250 STARTTLS" CRLF) - 1;
+
+ p = ngx_pnalloc(cf->pool, size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->starttls_only_capability.len = size;
+ conf->starttls_only_capability.data = p;
+
+ p = ngx_cpymem(p, conf->capability.data, auth - conf->capability.data);
+
+ ngx_memcpy(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1);
+
+ if (last < auth) {
+ p = conf->starttls_only_capability.data
+ + (last - conf->capability.data) + 3;
+ *p = '-';
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/src/nginx-1.18.0/src/mail/ngx_mail_smtp_module.h b/src/nginx-1.18.0/src/mail/ngx_mail_smtp_module.h
new file mode 100644
index 0000000..04ffab6
--- /dev/null
+++ b/src/nginx-1.18.0/src/mail/ngx_mail_smtp_module.h
@@ -0,0 +1,45 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_MAIL_SMTP_MODULE_H_INCLUDED_
+#define _NGX_MAIL_SMTP_MODULE_H_INCLUDED_
+
+
+#include
+#include
+#include
+#include
+
+
+typedef struct {
+ ngx_msec_t greeting_delay;
+
+ size_t client_buffer_size;
+
+ ngx_str_t capability;
+ ngx_str_t starttls_capability;
+ ngx_str_t starttls_only_capability;
+
+ ngx_str_t server_name;
+ ngx_str_t greeting;
+
+ ngx_uint_t auth_methods;
+
+ ngx_array_t capabilities;
+} ngx_mail_smtp_srv_conf_t;
+
+
+void ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c);
+void ngx_mail_smtp_init_protocol(ngx_event_t *rev);
+void ngx_mail_smtp_auth_state(ngx_event_t *rev);
+ngx_int_t ngx_mail_smtp_parse_command(ngx_mail_session_t *s);
+
+
+extern ngx_module_t ngx_mail_smtp_module;
+
+
+#endif /* _NGX_MAIL_SMTP_MODULE_H_INCLUDED_ */
diff --git a/src/nginx-1.18.0/src/mail/ngx_mail_ssl_module.c b/src/nginx-1.18.0/src/mail/ngx_mail_ssl_module.c
new file mode 100644
index 0000000..e193b29
--- /dev/null
+++ b/src/nginx-1.18.0/src/mail/ngx_mail_ssl_module.c
@@ -0,0 +1,656 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+#include
+
+
+#define NGX_DEFAULT_CIPHERS "HIGH:!aNULL:!MD5"
+#define NGX_DEFAULT_ECDH_CURVE "auto"
+
+
+static void *ngx_mail_ssl_create_conf(ngx_conf_t *cf);
+static char *ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child);
+
+static char *ngx_mail_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_mail_ssl_starttls(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_mail_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_conf_enum_t ngx_mail_starttls_state[] = {
+ { ngx_string("off"), NGX_MAIL_STARTTLS_OFF },
+ { ngx_string("on"), NGX_MAIL_STARTTLS_ON },
+ { ngx_string("only"), NGX_MAIL_STARTTLS_ONLY },
+ { ngx_null_string, 0 }
+};
+
+
+
+static ngx_conf_bitmask_t ngx_mail_ssl_protocols[] = {
+ { ngx_string("SSLv2"), NGX_SSL_SSLv2 },
+ { ngx_string("SSLv3"), NGX_SSL_SSLv3 },
+ { ngx_string("TLSv1"), NGX_SSL_TLSv1 },
+ { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },
+ { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },
+ { ngx_string("TLSv1.3"), NGX_SSL_TLSv1_3 },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_conf_enum_t ngx_mail_ssl_verify[] = {
+ { ngx_string("off"), 0 },
+ { ngx_string("on"), 1 },
+ { ngx_string("optional"), 2 },
+ { ngx_string("optional_no_ca"), 3 },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_conf_deprecated_t ngx_mail_ssl_deprecated = {
+ ngx_conf_deprecated, "ssl", "listen ... ssl"
+};
+
+
+static ngx_command_t ngx_mail_ssl_commands[] = {
+
+ { ngx_string("ssl"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
+ ngx_mail_ssl_enable,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, enable),
+ &ngx_mail_ssl_deprecated },
+
+ { ngx_string("starttls"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_mail_ssl_starttls,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, starttls),
+ ngx_mail_starttls_state },
+
+ { ngx_string("ssl_certificate"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_array_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, certificates),
+ NULL },
+
+ { ngx_string("ssl_certificate_key"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_array_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, certificate_keys),
+ NULL },
+
+ { ngx_string("ssl_password_file"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_mail_ssl_password_file,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("ssl_dhparam"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, dhparam),
+ NULL },
+
+ { ngx_string("ssl_ecdh_curve"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, ecdh_curve),
+ NULL },
+
+ { ngx_string("ssl_protocols"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, protocols),
+ &ngx_mail_ssl_protocols },
+
+ { ngx_string("ssl_ciphers"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, ciphers),
+ NULL },
+
+ { ngx_string("ssl_prefer_server_ciphers"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, prefer_server_ciphers),
+ NULL },
+
+ { ngx_string("ssl_session_cache"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE12,
+ ngx_mail_ssl_session_cache,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("ssl_session_tickets"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, session_tickets),
+ NULL },
+
+ { ngx_string("ssl_session_ticket_key"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_array_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, session_ticket_keys),
+ NULL },
+
+ { ngx_string("ssl_session_timeout"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_sec_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, session_timeout),
+ NULL },
+
+ { ngx_string("ssl_verify_client"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_enum_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, verify),
+ &ngx_mail_ssl_verify },
+
+ { ngx_string("ssl_verify_depth"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, verify_depth),
+ NULL },
+
+ { ngx_string("ssl_client_certificate"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, client_certificate),
+ NULL },
+
+ { ngx_string("ssl_trusted_certificate"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, trusted_certificate),
+ NULL },
+
+ { ngx_string("ssl_crl"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, crl),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_mail_module_t ngx_mail_ssl_module_ctx = {
+ NULL, /* protocol */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_mail_ssl_create_conf, /* create server configuration */
+ ngx_mail_ssl_merge_conf /* merge server configuration */
+};
+
+
+ngx_module_t ngx_mail_ssl_module = {
+ NGX_MODULE_V1,
+ &ngx_mail_ssl_module_ctx, /* module context */
+ ngx_mail_ssl_commands, /* module directives */
+ NGX_MAIL_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_str_t ngx_mail_ssl_sess_id_ctx = ngx_string("MAIL");
+
+
+static void *
+ngx_mail_ssl_create_conf(ngx_conf_t *cf)
+{
+ ngx_mail_ssl_conf_t *scf;
+
+ scf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_ssl_conf_t));
+ if (scf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * scf->listen = 0;
+ * scf->protocols = 0;
+ * scf->dhparam = { 0, NULL };
+ * scf->ecdh_curve = { 0, NULL };
+ * scf->client_certificate = { 0, NULL };
+ * scf->trusted_certificate = { 0, NULL };
+ * scf->crl = { 0, NULL };
+ * scf->ciphers = { 0, NULL };
+ * scf->shm_zone = NULL;
+ */
+
+ scf->enable = NGX_CONF_UNSET;
+ scf->starttls = NGX_CONF_UNSET_UINT;
+ scf->certificates = NGX_CONF_UNSET_PTR;
+ scf->certificate_keys = NGX_CONF_UNSET_PTR;
+ scf->passwords = NGX_CONF_UNSET_PTR;
+ scf->prefer_server_ciphers = NGX_CONF_UNSET;
+ scf->verify = NGX_CONF_UNSET_UINT;
+ scf->verify_depth = NGX_CONF_UNSET_UINT;
+ scf->builtin_session_cache = NGX_CONF_UNSET;
+ scf->session_timeout = NGX_CONF_UNSET;
+ scf->session_tickets = NGX_CONF_UNSET;
+ scf->session_ticket_keys = NGX_CONF_UNSET_PTR;
+
+ return scf;
+}
+
+
+static char *
+ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_mail_ssl_conf_t *prev = parent;
+ ngx_mail_ssl_conf_t *conf = child;
+
+ char *mode;
+ ngx_pool_cleanup_t *cln;
+
+ ngx_conf_merge_value(conf->enable, prev->enable, 0);
+ ngx_conf_merge_uint_value(conf->starttls, prev->starttls,
+ NGX_MAIL_STARTTLS_OFF);
+
+ ngx_conf_merge_value(conf->session_timeout,
+ prev->session_timeout, 300);
+
+ ngx_conf_merge_value(conf->prefer_server_ciphers,
+ prev->prefer_server_ciphers, 0);
+
+ ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,
+ (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1
+ |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));
+
+ ngx_conf_merge_uint_value(conf->verify, prev->verify, 0);
+ ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1);
+
+ ngx_conf_merge_ptr_value(conf->certificates, prev->certificates, NULL);
+ ngx_conf_merge_ptr_value(conf->certificate_keys, prev->certificate_keys,
+ NULL);
+
+ ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);
+
+ ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
+
+ ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,
+ NGX_DEFAULT_ECDH_CURVE);
+
+ ngx_conf_merge_str_value(conf->client_certificate,
+ prev->client_certificate, "");
+ ngx_conf_merge_str_value(conf->trusted_certificate,
+ prev->trusted_certificate, "");
+ ngx_conf_merge_str_value(conf->crl, prev->crl, "");
+
+ ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
+
+
+ conf->ssl.log = cf->log;
+
+ if (conf->listen) {
+ mode = "listen ... ssl";
+
+ } else if (conf->enable) {
+ mode = "ssl";
+
+ } else if (conf->starttls != NGX_MAIL_STARTTLS_OFF) {
+ mode = "starttls";
+
+ } else {
+ return NGX_CONF_OK;
+ }
+
+ if (conf->file == NULL) {
+ conf->file = prev->file;
+ conf->line = prev->line;
+ }
+
+ if (conf->certificates == NULL) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no \"ssl_certificate\" is defined for "
+ "the \"%s\" directive in %s:%ui",
+ mode, conf->file, conf->line);
+ return NGX_CONF_ERROR;
+ }
+
+ if (conf->certificate_keys == NULL) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no \"ssl_certificate_key\" is defined for "
+ "the \"%s\" directive in %s:%ui",
+ mode, conf->file, conf->line);
+ return NGX_CONF_ERROR;
+ }
+
+ if (conf->certificate_keys->nelts < conf->certificates->nelts) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no \"ssl_certificate_key\" is defined "
+ "for certificate \"%V\" and "
+ "the \"%s\" directive in %s:%ui",
+ ((ngx_str_t *) conf->certificates->elts)
+ + conf->certificates->nelts - 1,
+ mode, conf->file, conf->line);
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_ssl_create(&conf->ssl, conf->protocols, NULL) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ cln = ngx_pool_cleanup_add(cf->pool, 0);
+ if (cln == NULL) {
+ ngx_ssl_cleanup_ctx(&conf->ssl);
+ return NGX_CONF_ERROR;
+ }
+
+ cln->handler = ngx_ssl_cleanup_ctx;
+ cln->data = &conf->ssl;
+
+ if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates,
+ conf->certificate_keys, conf->passwords)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ if (conf->verify) {
+
+ if (conf->client_certificate.len == 0 && conf->verify != 3) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no ssl_client_certificate for ssl_verify_client");
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_ssl_client_certificate(cf, &conf->ssl,
+ &conf->client_certificate,
+ conf->verify_depth)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_ssl_trusted_certificate(cf, &conf->ssl,
+ &conf->trusted_certificate,
+ conf->verify_depth)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers,
+ conf->prefer_server_ciphers)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_conf_merge_value(conf->builtin_session_cache,
+ prev->builtin_session_cache, NGX_SSL_NONE_SCACHE);
+
+ if (conf->shm_zone == NULL) {
+ conf->shm_zone = prev->shm_zone;
+ }
+
+ if (ngx_ssl_session_cache(&conf->ssl, &ngx_mail_ssl_sess_id_ctx,
+ conf->certificates, conf->builtin_session_cache,
+ conf->shm_zone, conf->session_timeout)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_conf_merge_value(conf->session_tickets,
+ prev->session_tickets, 1);
+
+#ifdef SSL_OP_NO_TICKET
+ if (!conf->session_tickets) {
+ SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_NO_TICKET);
+ }
+#endif
+
+ ngx_conf_merge_ptr_value(conf->session_ticket_keys,
+ prev->session_ticket_keys, NULL);
+
+ if (ngx_ssl_session_ticket_keys(cf, &conf->ssl, conf->session_ticket_keys)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_mail_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_mail_ssl_conf_t *scf = conf;
+
+ char *rv;
+
+ rv = ngx_conf_set_flag_slot(cf, cmd, conf);
+
+ if (rv != NGX_CONF_OK) {
+ return rv;
+ }
+
+ if (scf->enable && (ngx_int_t) scf->starttls > NGX_MAIL_STARTTLS_OFF) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"starttls\" directive conflicts with \"ssl on\"");
+ return NGX_CONF_ERROR;
+ }
+
+ if (!scf->listen) {
+ scf->file = cf->conf_file->file.name.data;
+ scf->line = cf->conf_file->line;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_mail_ssl_starttls(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_mail_ssl_conf_t *scf = conf;
+
+ char *rv;
+
+ rv = ngx_conf_set_enum_slot(cf, cmd, conf);
+
+ if (rv != NGX_CONF_OK) {
+ return rv;
+ }
+
+ if (scf->enable == 1 && (ngx_int_t) scf->starttls > NGX_MAIL_STARTTLS_OFF) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"ssl\" directive conflicts with \"starttls\"");
+ return NGX_CONF_ERROR;
+ }
+
+ if (!scf->listen) {
+ scf->file = cf->conf_file->file.name.data;
+ scf->line = cf->conf_file->line;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_mail_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_mail_ssl_conf_t *scf = conf;
+
+ ngx_str_t *value;
+
+ if (scf->passwords != NGX_CONF_UNSET_PTR) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ scf->passwords = ngx_ssl_read_password_file(cf, &value[1]);
+
+ if (scf->passwords == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_mail_ssl_conf_t *scf = conf;
+
+ size_t len;
+ ngx_str_t *value, name, size;
+ ngx_int_t n;
+ ngx_uint_t i, j;
+
+ value = cf->args->elts;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+
+ if (ngx_strcmp(value[i].data, "off") == 0) {
+ scf->builtin_session_cache = NGX_SSL_NO_SCACHE;
+ continue;
+ }
+
+ if (ngx_strcmp(value[i].data, "none") == 0) {
+ scf->builtin_session_cache = NGX_SSL_NONE_SCACHE;
+ continue;
+ }
+
+ if (ngx_strcmp(value[i].data, "builtin") == 0) {
+ scf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE;
+ continue;
+ }
+
+ if (value[i].len > sizeof("builtin:") - 1
+ && ngx_strncmp(value[i].data, "builtin:", sizeof("builtin:") - 1)
+ == 0)
+ {
+ n = ngx_atoi(value[i].data + sizeof("builtin:") - 1,
+ value[i].len - (sizeof("builtin:") - 1));
+
+ if (n == NGX_ERROR) {
+ goto invalid;
+ }
+
+ scf->builtin_session_cache = n;
+
+ continue;
+ }
+
+ if (value[i].len > sizeof("shared:") - 1
+ && ngx_strncmp(value[i].data, "shared:", sizeof("shared:") - 1)
+ == 0)
+ {
+ len = 0;
+
+ for (j = sizeof("shared:") - 1; j < value[i].len; j++) {
+ if (value[i].data[j] == ':') {
+ break;
+ }
+
+ len++;
+ }
+
+ if (len == 0) {
+ goto invalid;
+ }
+
+ name.len = len;
+ name.data = value[i].data + sizeof("shared:") - 1;
+
+ size.len = value[i].len - j - 1;
+ size.data = name.data + len + 1;
+
+ n = ngx_parse_size(&size);
+
+ if (n == NGX_ERROR) {
+ goto invalid;
+ }
+
+ if (n < (ngx_int_t) (8 * ngx_pagesize)) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "session cache \"%V\" is too small",
+ &value[i]);
+
+ return NGX_CONF_ERROR;
+ }
+
+ scf->shm_zone = ngx_shared_memory_add(cf, &name, n,
+ &ngx_mail_ssl_module);
+ if (scf->shm_zone == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ scf->shm_zone->init = ngx_ssl_session_cache_init;
+
+ continue;
+ }
+
+ goto invalid;
+ }
+
+ if (scf->shm_zone && scf->builtin_session_cache == NGX_CONF_UNSET) {
+ scf->builtin_session_cache = NGX_SSL_NO_BUILTIN_SCACHE;
+ }
+
+ return NGX_CONF_OK;
+
+invalid:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid session cache \"%V\"", &value[i]);
+
+ return NGX_CONF_ERROR;
+}
diff --git a/src/nginx-1.18.0/src/mail/ngx_mail_ssl_module.h b/src/nginx-1.18.0/src/mail/ngx_mail_ssl_module.h
new file mode 100644
index 0000000..d6b0b8e
--- /dev/null
+++ b/src/nginx-1.18.0/src/mail/ngx_mail_ssl_module.h
@@ -0,0 +1,65 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_MAIL_SSL_H_INCLUDED_
+#define _NGX_MAIL_SSL_H_INCLUDED_
+
+
+#include
+#include
+#include
+
+
+#define NGX_MAIL_STARTTLS_OFF 0
+#define NGX_MAIL_STARTTLS_ON 1
+#define NGX_MAIL_STARTTLS_ONLY 2
+
+
+typedef struct {
+ ngx_flag_t enable;
+ ngx_flag_t prefer_server_ciphers;
+
+ ngx_ssl_t ssl;
+
+ ngx_uint_t starttls;
+ ngx_uint_t listen;
+ ngx_uint_t protocols;
+
+ ngx_uint_t verify;
+ ngx_uint_t verify_depth;
+
+ ssize_t builtin_session_cache;
+
+ time_t session_timeout;
+
+ ngx_array_t *certificates;
+ ngx_array_t *certificate_keys;
+
+ ngx_str_t dhparam;
+ ngx_str_t ecdh_curve;
+ ngx_str_t client_certificate;
+ ngx_str_t trusted_certificate;
+ ngx_str_t crl;
+
+ ngx_str_t ciphers;
+
+ ngx_array_t *passwords;
+
+ ngx_shm_zone_t *shm_zone;
+
+ ngx_flag_t session_tickets;
+ ngx_array_t *session_ticket_keys;
+
+ u_char *file;
+ ngx_uint_t line;
+} ngx_mail_ssl_conf_t;
+
+
+extern ngx_module_t ngx_mail_ssl_module;
+
+
+#endif /* _NGX_MAIL_SSL_H_INCLUDED_ */
diff --git a/src/nginx-1.18.0/src/misc/ngx_cpp_test_module.cpp b/src/nginx-1.18.0/src/misc/ngx_cpp_test_module.cpp
new file mode 100644
index 0000000..5d2f08d
--- /dev/null
+++ b/src/nginx-1.18.0/src/misc/ngx_cpp_test_module.cpp
@@ -0,0 +1,29 @@
+
+// stub module to test header files' C++ compatibility
+
+extern "C" {
+ #include
+ #include
+ #include
+ #include
+ #include
+
+ #include
+
+ #include
+ #include
+ #include
+ #include
+}
+
+// nginx header files should go before other, because they define 64-bit off_t
+// #include
+
+
+void ngx_cpp_test_handler(void *data);
+
+void
+ngx_cpp_test_handler(void *data)
+{
+ return;
+}
diff --git a/src/nginx-1.18.0/src/misc/ngx_google_perftools_module.c b/src/nginx-1.18.0/src/misc/ngx_google_perftools_module.c
new file mode 100644
index 0000000..381f3d1
--- /dev/null
+++ b/src/nginx-1.18.0/src/misc/ngx_google_perftools_module.c
@@ -0,0 +1,126 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+
+/*
+ * declare Profiler interface here because
+ * is C++ header file
+ */
+
+int ProfilerStart(u_char* fname);
+void ProfilerStop(void);
+void ProfilerRegisterThread(void);
+
+
+static void *ngx_google_perftools_create_conf(ngx_cycle_t *cycle);
+static ngx_int_t ngx_google_perftools_worker(ngx_cycle_t *cycle);
+
+
+typedef struct {
+ ngx_str_t profiles;
+} ngx_google_perftools_conf_t;
+
+
+static ngx_command_t ngx_google_perftools_commands[] = {
+
+ { ngx_string("google_perftools_profiles"),
+ NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ 0,
+ offsetof(ngx_google_perftools_conf_t, profiles),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_core_module_t ngx_google_perftools_module_ctx = {
+ ngx_string("google_perftools"),
+ ngx_google_perftools_create_conf,
+ NULL
+};
+
+
+ngx_module_t ngx_google_perftools_module = {
+ NGX_MODULE_V1,
+ &ngx_google_perftools_module_ctx, /* module context */
+ ngx_google_perftools_commands, /* module directives */
+ NGX_CORE_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ ngx_google_perftools_worker, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static void *
+ngx_google_perftools_create_conf(ngx_cycle_t *cycle)
+{
+ ngx_google_perftools_conf_t *gptcf;
+
+ gptcf = ngx_pcalloc(cycle->pool, sizeof(ngx_google_perftools_conf_t));
+ if (gptcf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc()
+ *
+ * gptcf->profiles = { 0, NULL };
+ */
+
+ return gptcf;
+}
+
+
+static ngx_int_t
+ngx_google_perftools_worker(ngx_cycle_t *cycle)
+{
+ u_char *profile;
+ ngx_google_perftools_conf_t *gptcf;
+
+ gptcf = (ngx_google_perftools_conf_t *)
+ ngx_get_conf(cycle->conf_ctx, ngx_google_perftools_module);
+
+ if (gptcf->profiles.len == 0) {
+ return NGX_OK;
+ }
+
+ profile = ngx_alloc(gptcf->profiles.len + NGX_INT_T_LEN + 2, cycle->log);
+ if (profile == NULL) {
+ return NGX_OK;
+ }
+
+ if (getenv("CPUPROFILE")) {
+ /* disable inherited Profiler enabled in master process */
+ ProfilerStop();
+ }
+
+ ngx_sprintf(profile, "%V.%d%Z", &gptcf->profiles, ngx_pid);
+
+ if (ProfilerStart(profile)) {
+ /* start ITIMER_PROF timer */
+ ProfilerRegisterThread();
+
+ } else {
+ ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_errno,
+ "ProfilerStart(%s) failed", profile);
+ }
+
+ ngx_free(profile);
+
+ return NGX_OK;
+}
+
+
+/* ProfilerStop() is called on Profiler destruction */
diff --git a/src/nginx-1.18.0/src/os/unix/ngx_alloc.c b/src/nginx-1.18.0/src/os/unix/ngx_alloc.c
new file mode 100644
index 0000000..5c2f787
--- /dev/null
+++ b/src/nginx-1.18.0/src/os/unix/ngx_alloc.c
@@ -0,0 +1,90 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+
+
+ngx_uint_t ngx_pagesize;
+ngx_uint_t ngx_pagesize_shift;
+ngx_uint_t ngx_cacheline_size;
+
+
+void *
+ngx_alloc(size_t size, ngx_log_t *log)
+{
+ void *p;
+
+ p = malloc(size);
+ if (p == NULL) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+ "malloc(%uz) failed", size);
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, "malloc: %p:%uz", p, size);
+
+ return p;
+}
+
+
+void *
+ngx_calloc(size_t size, ngx_log_t *log)
+{
+ void *p;
+
+ p = ngx_alloc(size, log);
+
+ if (p) {
+ ngx_memzero(p, size);
+ }
+
+ return p;
+}
+
+
+#if (NGX_HAVE_POSIX_MEMALIGN)
+
+void *
+ngx_memalign(size_t alignment, size_t size, ngx_log_t *log)
+{
+ void *p;
+ int err;
+
+ err = posix_memalign(&p, alignment, size);
+
+ if (err) {
+ ngx_log_error(NGX_LOG_EMERG, log, err,
+ "posix_memalign(%uz, %uz) failed", alignment, size);
+ p = NULL;
+ }
+
+ ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, log, 0,
+ "posix_memalign: %p:%uz @%uz", p, size, alignment);
+
+ return p;
+}
+
+#elif (NGX_HAVE_MEMALIGN)
+
+void *
+ngx_memalign(size_t alignment, size_t size, ngx_log_t *log)
+{
+ void *p;
+
+ p = memalign(alignment, size);
+ if (p == NULL) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+ "memalign(%uz, %uz) failed", alignment, size);
+ }
+
+ ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, log, 0,
+ "memalign: %p:%uz @%uz", p, size, alignment);
+
+ return p;
+}
+
+#endif
diff --git a/src/nginx-1.18.0/src/os/unix/ngx_alloc.h b/src/nginx-1.18.0/src/os/unix/ngx_alloc.h
new file mode 100644
index 0000000..655db25
--- /dev/null
+++ b/src/nginx-1.18.0/src/os/unix/ngx_alloc.h
@@ -0,0 +1,45 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_ALLOC_H_INCLUDED_
+#define _NGX_ALLOC_H_INCLUDED_
+
+
+#include
+#include
+
+
+void *ngx_alloc(size_t size, ngx_log_t *log);
+void *ngx_calloc(size_t size, ngx_log_t *log);
+
+#define ngx_free free
+
+
+/*
+ * Linux has memalign() or posix_memalign()
+ * Solaris has memalign()
+ * FreeBSD 7.0 has posix_memalign(), besides, early version's malloc()
+ * aligns allocations bigger than page size at the page boundary
+ */
+
+#if (NGX_HAVE_POSIX_MEMALIGN || NGX_HAVE_MEMALIGN)
+
+void *ngx_memalign(size_t alignment, size_t size, ngx_log_t *log);
+
+#else
+
+#define ngx_memalign(alignment, size, log) ngx_alloc(size, log)
+
+#endif
+
+
+extern ngx_uint_t ngx_pagesize;
+extern ngx_uint_t ngx_pagesize_shift;
+extern ngx_uint_t ngx_cacheline_size;
+
+
+#endif /* _NGX_ALLOC_H_INCLUDED_ */
diff --git a/src/nginx-1.18.0/src/os/unix/ngx_atomic.h b/src/nginx-1.18.0/src/os/unix/ngx_atomic.h
new file mode 100644
index 0000000..74b8b7f
--- /dev/null
+++ b/src/nginx-1.18.0/src/os/unix/ngx_atomic.h
@@ -0,0 +1,313 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_ATOMIC_H_INCLUDED_
+#define _NGX_ATOMIC_H_INCLUDED_
+
+
+#include
+#include
+
+
+#if (NGX_HAVE_LIBATOMIC)
+
+#define AO_REQUIRE_CAS
+#include
+
+#define NGX_HAVE_ATOMIC_OPS 1
+
+typedef long ngx_atomic_int_t;
+typedef AO_t ngx_atomic_uint_t;
+typedef volatile ngx_atomic_uint_t ngx_atomic_t;
+
+#if (NGX_PTR_SIZE == 8)
+#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1)
+#else
+#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1)
+#endif
+
+#define ngx_atomic_cmp_set(lock, old, new) \
+ AO_compare_and_swap(lock, old, new)
+#define ngx_atomic_fetch_add(value, add) \
+ AO_fetch_and_add(value, add)
+#define ngx_memory_barrier() AO_nop()
+#define ngx_cpu_pause()
+
+
+#elif (NGX_DARWIN_ATOMIC)
+
+/*
+ * use Darwin 8 atomic(3) and barrier(3) operations
+ * optimized at run-time for UP and SMP
+ */
+
+#include
+
+/* "bool" conflicts with perl's CORE/handy.h */
+#if 0
+#undef bool
+#endif
+
+
+#define NGX_HAVE_ATOMIC_OPS 1
+
+#if (NGX_PTR_SIZE == 8)
+
+typedef int64_t ngx_atomic_int_t;
+typedef uint64_t ngx_atomic_uint_t;
+#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1)
+
+#define ngx_atomic_cmp_set(lock, old, new) \
+ OSAtomicCompareAndSwap64Barrier(old, new, (int64_t *) lock)
+
+#define ngx_atomic_fetch_add(value, add) \
+ (OSAtomicAdd64(add, (int64_t *) value) - add)
+
+#else
+
+typedef int32_t ngx_atomic_int_t;
+typedef uint32_t ngx_atomic_uint_t;
+#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1)
+
+#define ngx_atomic_cmp_set(lock, old, new) \
+ OSAtomicCompareAndSwap32Barrier(old, new, (int32_t *) lock)
+
+#define ngx_atomic_fetch_add(value, add) \
+ (OSAtomicAdd32(add, (int32_t *) value) - add)
+
+#endif
+
+#define ngx_memory_barrier() OSMemoryBarrier()
+
+#define ngx_cpu_pause()
+
+typedef volatile ngx_atomic_uint_t ngx_atomic_t;
+
+
+#elif (NGX_HAVE_GCC_ATOMIC)
+
+/* GCC 4.1 builtin atomic operations */
+
+#define NGX_HAVE_ATOMIC_OPS 1
+
+typedef long ngx_atomic_int_t;
+typedef unsigned long ngx_atomic_uint_t;
+
+#if (NGX_PTR_SIZE == 8)
+#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1)
+#else
+#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1)
+#endif
+
+typedef volatile ngx_atomic_uint_t ngx_atomic_t;
+
+
+#define ngx_atomic_cmp_set(lock, old, set) \
+ __sync_bool_compare_and_swap(lock, old, set)
+
+#define ngx_atomic_fetch_add(value, add) \
+ __sync_fetch_and_add(value, add)
+
+#define ngx_memory_barrier() __sync_synchronize()
+
+#if ( __i386__ || __i386 || __amd64__ || __amd64 )
+#define ngx_cpu_pause() __asm__ ("pause")
+#else
+#define ngx_cpu_pause()
+#endif
+
+
+#elif ( __i386__ || __i386 )
+
+typedef int32_t ngx_atomic_int_t;
+typedef uint32_t ngx_atomic_uint_t;
+typedef volatile ngx_atomic_uint_t ngx_atomic_t;
+#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1)
+
+
+#if ( __SUNPRO_C )
+
+#define NGX_HAVE_ATOMIC_OPS 1
+
+ngx_atomic_uint_t
+ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,
+ ngx_atomic_uint_t set);
+
+ngx_atomic_int_t
+ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add);
+
+/*
+ * Sun Studio 12 exits with segmentation fault on '__asm ("pause")',
+ * so ngx_cpu_pause is declared in src/os/unix/ngx_sunpro_x86.il
+ */
+
+void
+ngx_cpu_pause(void);
+
+/* the code in src/os/unix/ngx_sunpro_x86.il */
+
+#define ngx_memory_barrier() __asm (".volatile"); __asm (".nonvolatile")
+
+
+#else /* ( __GNUC__ || __INTEL_COMPILER ) */
+
+#define NGX_HAVE_ATOMIC_OPS 1
+
+#include "ngx_gcc_atomic_x86.h"
+
+#endif
+
+
+#elif ( __amd64__ || __amd64 )
+
+typedef int64_t ngx_atomic_int_t;
+typedef uint64_t ngx_atomic_uint_t;
+typedef volatile ngx_atomic_uint_t ngx_atomic_t;
+#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1)
+
+
+#if ( __SUNPRO_C )
+
+#define NGX_HAVE_ATOMIC_OPS 1
+
+ngx_atomic_uint_t
+ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,
+ ngx_atomic_uint_t set);
+
+ngx_atomic_int_t
+ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add);
+
+/*
+ * Sun Studio 12 exits with segmentation fault on '__asm ("pause")',
+ * so ngx_cpu_pause is declared in src/os/unix/ngx_sunpro_amd64.il
+ */
+
+void
+ngx_cpu_pause(void);
+
+/* the code in src/os/unix/ngx_sunpro_amd64.il */
+
+#define ngx_memory_barrier() __asm (".volatile"); __asm (".nonvolatile")
+
+
+#else /* ( __GNUC__ || __INTEL_COMPILER ) */
+
+#define NGX_HAVE_ATOMIC_OPS 1
+
+#include "ngx_gcc_atomic_amd64.h"
+
+#endif
+
+
+#elif ( __sparc__ || __sparc || __sparcv9 )
+
+#if (NGX_PTR_SIZE == 8)
+
+typedef int64_t ngx_atomic_int_t;
+typedef uint64_t ngx_atomic_uint_t;
+#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1)
+
+#else
+
+typedef int32_t ngx_atomic_int_t;
+typedef uint32_t ngx_atomic_uint_t;
+#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1)
+
+#endif
+
+typedef volatile ngx_atomic_uint_t ngx_atomic_t;
+
+
+#if ( __SUNPRO_C )
+
+#define NGX_HAVE_ATOMIC_OPS 1
+
+#include "ngx_sunpro_atomic_sparc64.h"
+
+
+#else /* ( __GNUC__ || __INTEL_COMPILER ) */
+
+#define NGX_HAVE_ATOMIC_OPS 1
+
+#include "ngx_gcc_atomic_sparc64.h"
+
+#endif
+
+
+#elif ( __powerpc__ || __POWERPC__ )
+
+#define NGX_HAVE_ATOMIC_OPS 1
+
+#if (NGX_PTR_SIZE == 8)
+
+typedef int64_t ngx_atomic_int_t;
+typedef uint64_t ngx_atomic_uint_t;
+#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1)
+
+#else
+
+typedef int32_t ngx_atomic_int_t;
+typedef uint32_t ngx_atomic_uint_t;
+#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1)
+
+#endif
+
+typedef volatile ngx_atomic_uint_t ngx_atomic_t;
+
+
+#include "ngx_gcc_atomic_ppc.h"
+
+#endif
+
+
+#if !(NGX_HAVE_ATOMIC_OPS)
+
+#define NGX_HAVE_ATOMIC_OPS 0
+
+typedef int32_t ngx_atomic_int_t;
+typedef uint32_t ngx_atomic_uint_t;
+typedef volatile ngx_atomic_uint_t ngx_atomic_t;
+#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1)
+
+
+static ngx_inline ngx_atomic_uint_t
+ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,
+ ngx_atomic_uint_t set)
+{
+ if (*lock == old) {
+ *lock = set;
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static ngx_inline ngx_atomic_int_t
+ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)
+{
+ ngx_atomic_int_t old;
+
+ old = *value;
+ *value += add;
+
+ return old;
+}
+
+#define ngx_memory_barrier()
+#define ngx_cpu_pause()
+
+#endif
+
+
+void ngx_spinlock(ngx_atomic_t *lock, ngx_atomic_int_t value, ngx_uint_t spin);
+
+#define ngx_trylock(lock) (*(lock) == 0 && ngx_atomic_cmp_set(lock, 0, 1))
+#define ngx_unlock(lock) *(lock) = 0
+
+
+#endif /* _NGX_ATOMIC_H_INCLUDED_ */
diff --git a/src/nginx-1.18.0/src/os/unix/ngx_channel.c b/src/nginx-1.18.0/src/os/unix/ngx_channel.c
new file mode 100644
index 0000000..1efa066
--- /dev/null
+++ b/src/nginx-1.18.0/src/os/unix/ngx_channel.c
@@ -0,0 +1,253 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+#include
+
+
+ngx_int_t
+ngx_write_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size,
+ ngx_log_t *log)
+{
+ ssize_t n;
+ ngx_err_t err;
+ struct iovec iov[1];
+ struct msghdr msg;
+
+#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
+
+ union {
+ struct cmsghdr cm;
+ char space[CMSG_SPACE(sizeof(int))];
+ } cmsg;
+
+ if (ch->fd == -1) {
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+
+ } else {
+ msg.msg_control = (caddr_t) &cmsg;
+ msg.msg_controllen = sizeof(cmsg);
+
+ ngx_memzero(&cmsg, sizeof(cmsg));
+
+ cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int));
+ cmsg.cm.cmsg_level = SOL_SOCKET;
+ cmsg.cm.cmsg_type = SCM_RIGHTS;
+
+ /*
+ * We have to use ngx_memcpy() instead of simple
+ * *(int *) CMSG_DATA(&cmsg.cm) = ch->fd;
+ * because some gcc 4.4 with -O2/3/s optimization issues the warning:
+ * dereferencing type-punned pointer will break strict-aliasing rules
+ *
+ * Fortunately, gcc with -O1 compiles this ngx_memcpy()
+ * in the same simple assignment as in the code above
+ */
+
+ ngx_memcpy(CMSG_DATA(&cmsg.cm), &ch->fd, sizeof(int));
+ }
+
+ msg.msg_flags = 0;
+
+#else
+
+ if (ch->fd == -1) {
+ msg.msg_accrights = NULL;
+ msg.msg_accrightslen = 0;
+
+ } else {
+ msg.msg_accrights = (caddr_t) &ch->fd;
+ msg.msg_accrightslen = sizeof(int);
+ }
+
+#endif
+
+ iov[0].iov_base = (char *) ch;
+ iov[0].iov_len = size;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ n = sendmsg(s, &msg, 0);
+
+ if (n == -1) {
+ err = ngx_errno;
+ if (err == NGX_EAGAIN) {
+ return NGX_AGAIN;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, log, err, "sendmsg() failed");
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_read_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size, ngx_log_t *log)
+{
+ ssize_t n;
+ ngx_err_t err;
+ struct iovec iov[1];
+ struct msghdr msg;
+
+#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
+ union {
+ struct cmsghdr cm;
+ char space[CMSG_SPACE(sizeof(int))];
+ } cmsg;
+#else
+ int fd;
+#endif
+
+ iov[0].iov_base = (char *) ch;
+ iov[0].iov_len = size;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
+ msg.msg_control = (caddr_t) &cmsg;
+ msg.msg_controllen = sizeof(cmsg);
+#else
+ msg.msg_accrights = (caddr_t) &fd;
+ msg.msg_accrightslen = sizeof(int);
+#endif
+
+ n = recvmsg(s, &msg, 0);
+
+ if (n == -1) {
+ err = ngx_errno;
+ if (err == NGX_EAGAIN) {
+ return NGX_AGAIN;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, log, err, "recvmsg() failed");
+ return NGX_ERROR;
+ }
+
+ if (n == 0) {
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, "recvmsg() returned zero");
+ return NGX_ERROR;
+ }
+
+ if ((size_t) n < sizeof(ngx_channel_t)) {
+ ngx_log_error(NGX_LOG_ALERT, log, 0,
+ "recvmsg() returned not enough data: %z", n);
+ return NGX_ERROR;
+ }
+
+#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
+
+ if (ch->command == NGX_CMD_OPEN_CHANNEL) {
+
+ if (cmsg.cm.cmsg_len < (socklen_t) CMSG_LEN(sizeof(int))) {
+ ngx_log_error(NGX_LOG_ALERT, log, 0,
+ "recvmsg() returned too small ancillary data");
+ return NGX_ERROR;
+ }
+
+ if (cmsg.cm.cmsg_level != SOL_SOCKET || cmsg.cm.cmsg_type != SCM_RIGHTS)
+ {
+ ngx_log_error(NGX_LOG_ALERT, log, 0,
+ "recvmsg() returned invalid ancillary data "
+ "level %d or type %d",
+ cmsg.cm.cmsg_level, cmsg.cm.cmsg_type);
+ return NGX_ERROR;
+ }
+
+ /* ch->fd = *(int *) CMSG_DATA(&cmsg.cm); */
+
+ ngx_memcpy(&ch->fd, CMSG_DATA(&cmsg.cm), sizeof(int));
+ }
+
+ if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
+ ngx_log_error(NGX_LOG_ALERT, log, 0,
+ "recvmsg() truncated data");
+ }
+
+#else
+
+ if (ch->command == NGX_CMD_OPEN_CHANNEL) {
+ if (msg.msg_accrightslen != sizeof(int)) {
+ ngx_log_error(NGX_LOG_ALERT, log, 0,
+ "recvmsg() returned no ancillary data");
+ return NGX_ERROR;
+ }
+
+ ch->fd = fd;
+ }
+
+#endif
+
+ return n;
+}
+
+
+ngx_int_t
+ngx_add_channel_event(ngx_cycle_t *cycle, ngx_fd_t fd, ngx_int_t event,
+ ngx_event_handler_pt handler)
+{
+ ngx_event_t *ev, *rev, *wev;
+ ngx_connection_t *c;
+
+ c = ngx_get_connection(fd, cycle->log);
+
+ if (c == NULL) {
+ return NGX_ERROR;
+ }
+
+ c->pool = cycle->pool;
+
+ rev = c->read;
+ wev = c->write;
+
+ rev->log = cycle->log;
+ wev->log = cycle->log;
+
+ rev->channel = 1;
+ wev->channel = 1;
+
+ ev = (event == NGX_READ_EVENT) ? rev : wev;
+
+ ev->handler = handler;
+
+ if (ngx_add_conn && (ngx_event_flags & NGX_USE_EPOLL_EVENT) == 0) {
+ if (ngx_add_conn(c) == NGX_ERROR) {
+ ngx_free_connection(c);
+ return NGX_ERROR;
+ }
+
+ } else {
+ if (ngx_add_event(ev, event, 0) == NGX_ERROR) {
+ ngx_free_connection(c);
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+void
+ngx_close_channel(ngx_fd_t *fd, ngx_log_t *log)
+{
+ if (close(fd[0]) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "close() channel failed");
+ }
+
+ if (close(fd[1]) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "close() channel failed");
+ }
+}
diff --git a/src/nginx-1.18.0/src/os/unix/ngx_channel.h b/src/nginx-1.18.0/src/os/unix/ngx_channel.h
new file mode 100644
index 0000000..362cc64
--- /dev/null
+++ b/src/nginx-1.18.0/src/os/unix/ngx_channel.h
@@ -0,0 +1,34 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_CHANNEL_H_INCLUDED_
+#define _NGX_CHANNEL_H_INCLUDED_
+
+
+#include
+#include
+#include
+
+
+typedef struct {
+ ngx_uint_t command;
+ ngx_pid_t pid;
+ ngx_int_t slot;
+ ngx_fd_t fd;
+} ngx_channel_t;
+
+
+ngx_int_t ngx_write_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size,
+ ngx_log_t *log);
+ngx_int_t ngx_read_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size,
+ ngx_log_t *log);
+ngx_int_t ngx_add_channel_event(ngx_cycle_t *cycle, ngx_fd_t fd,
+ ngx_int_t event, ngx_event_handler_pt handler);
+void ngx_close_channel(ngx_fd_t *fd, ngx_log_t *log);
+
+
+#endif /* _NGX_CHANNEL_H_INCLUDED_ */
diff --git a/src/nginx-1.18.0/src/os/unix/ngx_daemon.c b/src/nginx-1.18.0/src/os/unix/ngx_daemon.c
new file mode 100644
index 0000000..385c49b
--- /dev/null
+++ b/src/nginx-1.18.0/src/os/unix/ngx_daemon.c
@@ -0,0 +1,71 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+
+
+ngx_int_t
+ngx_daemon(ngx_log_t *log)
+{
+ int fd;
+
+ switch (fork()) {
+ case -1:
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "fork() failed");
+ return NGX_ERROR;
+
+ case 0:
+ break;
+
+ default:
+ exit(0);
+ }
+
+ ngx_parent = ngx_pid;
+ ngx_pid = ngx_getpid();
+
+ if (setsid() == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "setsid() failed");
+ return NGX_ERROR;
+ }
+
+ umask(0);
+
+ fd = open("/dev/null", O_RDWR);
+ if (fd == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+ "open(\"/dev/null\") failed");
+ return NGX_ERROR;
+ }
+
+ if (dup2(fd, STDIN_FILENO) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDIN) failed");
+ return NGX_ERROR;
+ }
+
+ if (dup2(fd, STDOUT_FILENO) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDOUT) failed");
+ return NGX_ERROR;
+ }
+
+#if 0
+ if (dup2(fd, STDERR_FILENO) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDERR) failed");
+ return NGX_ERROR;
+ }
+#endif
+
+ if (fd > STDERR_FILENO) {
+ if (close(fd) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "close() failed");
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
diff --git a/src/nginx-1.18.0/src/os/unix/ngx_darwin.h b/src/nginx-1.18.0/src/os/unix/ngx_darwin.h
new file mode 100644
index 0000000..4d01b26
--- /dev/null
+++ b/src/nginx-1.18.0/src/os/unix/ngx_darwin.h
@@ -0,0 +1,23 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_DARWIN_H_INCLUDED_
+#define _NGX_DARWIN_H_INCLUDED_
+
+
+void ngx_debug_init(void);
+ngx_chain_t *ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in,
+ off_t limit);
+
+extern int ngx_darwin_kern_osreldate;
+extern int ngx_darwin_hw_ncpu;
+extern u_long ngx_darwin_net_inet_tcp_sendspace;
+
+extern ngx_uint_t ngx_debug_malloc;
+
+
+#endif /* _NGX_DARWIN_H_INCLUDED_ */
diff --git a/src/nginx-1.18.0/src/os/unix/ngx_darwin_config.h b/src/nginx-1.18.0/src/os/unix/ngx_darwin_config.h
new file mode 100644
index 0000000..0dfe633
--- /dev/null
+++ b/src/nginx-1.18.0/src/os/unix/ngx_darwin_config.h
@@ -0,0 +1,100 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_DARWIN_CONFIG_H_INCLUDED_
+#define _NGX_DARWIN_CONFIG_H_INCLUDED_
+
+
+#define __APPLE_USE_RFC_3542 /* IPV6_PKTINFO */
+
+
+#include
+#include
+#include
+#include
+#include
+#include /* offsetof() */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include /* statfs() */
+
+#include /* FIONBIO */
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include /* TCP_NODELAY */
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+
+
+#ifndef IOV_MAX
+#define IOV_MAX 64
+#endif
+
+
+#include
+
+
+#if (NGX_HAVE_POSIX_SEM)
+#include
+#endif
+
+
+#if (NGX_HAVE_POLL)
+#include
+#endif
+
+
+#if (NGX_HAVE_KQUEUE)
+#include
+#endif
+
+
+#define NGX_LISTEN_BACKLOG -1
+
+
+#ifndef NGX_HAVE_INHERITED_NONBLOCK
+#define NGX_HAVE_INHERITED_NONBLOCK 1
+#endif
+
+
+#ifndef NGX_HAVE_CASELESS_FILESYSTEM
+#define NGX_HAVE_CASELESS_FILESYSTEM 1
+#endif
+
+
+#define NGX_HAVE_OS_SPECIFIC_INIT 1
+#define NGX_HAVE_DEBUG_MALLOC 1
+
+
+extern char **environ;
+
+
+#endif /* _NGX_DARWIN_CONFIG_H_INCLUDED_ */
diff --git a/src/nginx-1.18.0/src/os/unix/ngx_darwin_init.c b/src/nginx-1.18.0/src/os/unix/ngx_darwin_init.c
new file mode 100644
index 0000000..aabe02f
--- /dev/null
+++ b/src/nginx-1.18.0/src/os/unix/ngx_darwin_init.c
@@ -0,0 +1,198 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+
+
+char ngx_darwin_kern_ostype[16];
+char ngx_darwin_kern_osrelease[128];
+int ngx_darwin_hw_ncpu;
+int ngx_darwin_kern_ipc_somaxconn;
+u_long ngx_darwin_net_inet_tcp_sendspace;
+
+ngx_uint_t ngx_debug_malloc;
+
+
+static ngx_os_io_t ngx_darwin_io = {
+ ngx_unix_recv,
+ ngx_readv_chain,
+ ngx_udp_unix_recv,
+ ngx_unix_send,
+ ngx_udp_unix_send,
+ ngx_udp_unix_sendmsg_chain,
+#if (NGX_HAVE_SENDFILE)
+ ngx_darwin_sendfile_chain,
+ NGX_IO_SENDFILE
+#else
+ ngx_writev_chain,
+ 0
+#endif
+};
+
+
+typedef struct {
+ char *name;
+ void *value;
+ size_t size;
+ ngx_uint_t exists;
+} sysctl_t;
+
+
+sysctl_t sysctls[] = {
+ { "hw.ncpu",
+ &ngx_darwin_hw_ncpu,
+ sizeof(ngx_darwin_hw_ncpu), 0 },
+
+ { "net.inet.tcp.sendspace",
+ &ngx_darwin_net_inet_tcp_sendspace,
+ sizeof(ngx_darwin_net_inet_tcp_sendspace), 0 },
+
+ { "kern.ipc.somaxconn",
+ &ngx_darwin_kern_ipc_somaxconn,
+ sizeof(ngx_darwin_kern_ipc_somaxconn), 0 },
+
+ { NULL, NULL, 0, 0 }
+};
+
+
+void
+ngx_debug_init(void)
+{
+#if (NGX_DEBUG_MALLOC)
+
+ /*
+ * MacOSX 10.6, 10.7: MallocScribble fills freed memory with 0x55
+ * and fills allocated memory with 0xAA.
+ * MacOSX 10.4, 10.5: MallocScribble fills freed memory with 0x55,
+ * MallocPreScribble fills allocated memory with 0xAA.
+ * MacOSX 10.3: MallocScribble fills freed memory with 0x55,
+ * and no way to fill allocated memory.
+ */
+
+ setenv("MallocScribble", "1", 0);
+
+ ngx_debug_malloc = 1;
+
+#else
+
+ if (getenv("MallocScribble")) {
+ ngx_debug_malloc = 1;
+ }
+
+#endif
+}
+
+
+ngx_int_t
+ngx_os_specific_init(ngx_log_t *log)
+{
+ size_t size;
+ ngx_err_t err;
+ ngx_uint_t i;
+
+ size = sizeof(ngx_darwin_kern_ostype);
+ if (sysctlbyname("kern.ostype", ngx_darwin_kern_ostype, &size, NULL, 0)
+ == -1)
+ {
+ err = ngx_errno;
+
+ if (err != NGX_ENOENT) {
+
+ ngx_log_error(NGX_LOG_ALERT, log, err,
+ "sysctlbyname(kern.ostype) failed");
+
+ if (err != NGX_ENOMEM) {
+ return NGX_ERROR;
+ }
+
+ ngx_darwin_kern_ostype[size - 1] = '\0';
+ }
+ }
+
+ size = sizeof(ngx_darwin_kern_osrelease);
+ if (sysctlbyname("kern.osrelease", ngx_darwin_kern_osrelease, &size,
+ NULL, 0)
+ == -1)
+ {
+ err = ngx_errno;
+
+ if (err != NGX_ENOENT) {
+
+ ngx_log_error(NGX_LOG_ALERT, log, err,
+ "sysctlbyname(kern.osrelease) failed");
+
+ if (err != NGX_ENOMEM) {
+ return NGX_ERROR;
+ }
+
+ ngx_darwin_kern_osrelease[size - 1] = '\0';
+ }
+ }
+
+ for (i = 0; sysctls[i].name; i++) {
+ size = sysctls[i].size;
+
+ if (sysctlbyname(sysctls[i].name, sysctls[i].value, &size, NULL, 0)
+ == 0)
+ {
+ sysctls[i].exists = 1;
+ continue;
+ }
+
+ err = ngx_errno;
+
+ if (err == NGX_ENOENT) {
+ continue;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, log, err,
+ "sysctlbyname(%s) failed", sysctls[i].name);
+ return NGX_ERROR;
+ }
+
+ ngx_ncpu = ngx_darwin_hw_ncpu;
+
+ if (ngx_darwin_kern_ipc_somaxconn > 32767) {
+ ngx_log_error(NGX_LOG_ALERT, log, 0,
+ "sysctl kern.ipc.somaxconn must be less than 32768");
+ return NGX_ERROR;
+ }
+
+ ngx_tcp_nodelay_and_tcp_nopush = 1;
+
+ ngx_os_io = ngx_darwin_io;
+
+ return NGX_OK;
+}
+
+
+void
+ngx_os_specific_status(ngx_log_t *log)
+{
+ u_long value;
+ ngx_uint_t i;
+
+ if (ngx_darwin_kern_ostype[0]) {
+ ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s",
+ ngx_darwin_kern_ostype, ngx_darwin_kern_osrelease);
+ }
+
+ for (i = 0; sysctls[i].name; i++) {
+ if (sysctls[i].exists) {
+ if (sysctls[i].size == sizeof(long)) {
+ value = *(long *) sysctls[i].value;
+
+ } else {
+ value = *(int *) sysctls[i].value;
+ }
+
+ ngx_log_error(NGX_LOG_NOTICE, log, 0, "%s: %l",
+ sysctls[i].name, value);
+ }
+ }
+}
diff --git a/src/nginx-1.18.0/src/os/unix/ngx_darwin_sendfile_chain.c b/src/nginx-1.18.0/src/os/unix/ngx_darwin_sendfile_chain.c
new file mode 100644
index 0000000..2a76c15
--- /dev/null
+++ b/src/nginx-1.18.0/src/os/unix/ngx_darwin_sendfile_chain.c
@@ -0,0 +1,206 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+#include
+
+
+/*
+ * It seems that Darwin 9.4 (Mac OS X 1.5) sendfile() has the same
+ * old bug as early FreeBSD sendfile() syscall:
+ * http://bugs.freebsd.org/33771
+ *
+ * Besides sendfile() has another bug: if one calls sendfile()
+ * with both a header and a trailer, then sendfile() ignores a file part
+ * at all and sends only the header and the trailer together.
+ * For this reason we send a trailer only if there is no a header.
+ *
+ * Although sendfile() allows to pass a header or a trailer,
+ * it may send the header or the trailer and a part of the file
+ * in different packets. And FreeBSD workaround (TCP_NOPUSH option)
+ * does not help.
+ */
+
+
+ngx_chain_t *
+ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
+{
+ int rc;
+ off_t send, prev_send, sent;
+ off_t file_size;
+ ssize_t n;
+ ngx_uint_t eintr;
+ ngx_err_t err;
+ ngx_buf_t *file;
+ ngx_event_t *wev;
+ ngx_chain_t *cl;
+ ngx_iovec_t header, trailer;
+ struct sf_hdtr hdtr;
+ struct iovec headers[NGX_IOVS_PREALLOCATE];
+ struct iovec trailers[NGX_IOVS_PREALLOCATE];
+
+ wev = c->write;
+
+ if (!wev->ready) {
+ return in;
+ }
+
+#if (NGX_HAVE_KQUEUE)
+
+ if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {
+ (void) ngx_connection_error(c, wev->kq_errno,
+ "kevent() reported about an closed connection");
+ wev->error = 1;
+ return NGX_CHAIN_ERROR;
+ }
+
+#endif
+
+ /* the maximum limit size is the maximum size_t value - the page size */
+
+ if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {
+ limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;
+ }
+
+ send = 0;
+
+ header.iovs = headers;
+ header.nalloc = NGX_IOVS_PREALLOCATE;
+
+ trailer.iovs = trailers;
+ trailer.nalloc = NGX_IOVS_PREALLOCATE;
+
+ for ( ;; ) {
+ eintr = 0;
+ prev_send = send;
+
+ /* create the header iovec and coalesce the neighbouring bufs */
+
+ cl = ngx_output_chain_to_iovec(&header, in, limit - send, c->log);
+
+ if (cl == NGX_CHAIN_ERROR) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ send += header.size;
+
+ if (cl && cl->buf->in_file && send < limit) {
+ file = cl->buf;
+
+ /* coalesce the neighbouring file bufs */
+
+ file_size = ngx_chain_coalesce_file(&cl, limit - send);
+
+ send += file_size;
+
+ if (header.count == 0 && send < limit) {
+
+ /*
+ * create the trailer iovec and coalesce the neighbouring bufs
+ */
+
+ cl = ngx_output_chain_to_iovec(&trailer, cl, limit - send,
+ c->log);
+ if (cl == NGX_CHAIN_ERROR) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ send += trailer.size;
+
+ } else {
+ trailer.count = 0;
+ }
+
+ /*
+ * sendfile() returns EINVAL if sf_hdtr's count is 0,
+ * but corresponding pointer is not NULL
+ */
+
+ hdtr.headers = header.count ? header.iovs : NULL;
+ hdtr.hdr_cnt = header.count;
+ hdtr.trailers = trailer.count ? trailer.iovs : NULL;
+ hdtr.trl_cnt = trailer.count;
+
+ sent = header.size + file_size;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "sendfile: @%O %O h:%uz",
+ file->file_pos, sent, header.size);
+
+ rc = sendfile(file->file->fd, c->fd, file->file_pos,
+ &sent, &hdtr, 0);
+
+ if (rc == -1) {
+ err = ngx_errno;
+
+ switch (err) {
+ case NGX_EAGAIN:
+ break;
+
+ case NGX_EINTR:
+ eintr = 1;
+ break;
+
+ default:
+ wev->error = 1;
+ (void) ngx_connection_error(c, err, "sendfile() failed");
+ return NGX_CHAIN_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,
+ "sendfile() sent only %O bytes", sent);
+ }
+
+ if (rc == 0 && sent == 0) {
+
+ /*
+ * if rc and sent equal to zero, then someone
+ * has truncated the file, so the offset became beyond
+ * the end of the file
+ */
+
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "sendfile() reported that \"%s\" was truncated",
+ file->file->name.data);
+
+ return NGX_CHAIN_ERROR;
+ }
+
+ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "sendfile: %d, @%O %O:%O",
+ rc, file->file_pos, sent, file_size + header.size);
+
+ } else {
+ n = ngx_writev(c, &header);
+
+ if (n == NGX_ERROR) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ sent = (n == NGX_AGAIN) ? 0 : n;
+ }
+
+ c->sent += sent;
+
+ in = ngx_chain_update_sent(in, sent);
+
+ if (eintr) {
+ send = prev_send + sent;
+ continue;
+ }
+
+ if (send - prev_send != sent) {
+ wev->ready = 0;
+ return in;
+ }
+
+ if (send >= limit || in == NULL) {
+ return in;
+ }
+ }
+}
diff --git a/src/nginx-1.18.0/src/os/unix/ngx_dlopen.c b/src/nginx-1.18.0/src/os/unix/ngx_dlopen.c
new file mode 100644
index 0000000..a0efc69
--- /dev/null
+++ b/src/nginx-1.18.0/src/os/unix/ngx_dlopen.c
@@ -0,0 +1,28 @@
+
+/*
+ * Copyright (C) Maxim Dounin
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+
+
+#if (NGX_HAVE_DLOPEN)
+
+char *
+ngx_dlerror(void)
+{
+ char *err;
+
+ err = (char *) dlerror();
+
+ if (err == NULL) {
+ return "";
+ }
+
+ return err;
+}
+
+#endif
diff --git a/src/nginx-1.18.0/src/os/unix/ngx_dlopen.h b/src/nginx-1.18.0/src/os/unix/ngx_dlopen.h
new file mode 100644
index 0000000..7a3159f
--- /dev/null
+++ b/src/nginx-1.18.0/src/os/unix/ngx_dlopen.h
@@ -0,0 +1,31 @@
+
+/*
+ * Copyright (C) Maxim Dounin
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_DLOPEN_H_INCLUDED_
+#define _NGX_DLOPEN_H_INCLUDED_
+
+
+#include
+#include
+
+
+#define ngx_dlopen(path) dlopen((char *) path, RTLD_NOW | RTLD_GLOBAL)
+#define ngx_dlopen_n "dlopen()"
+
+#define ngx_dlsym(handle, symbol) dlsym(handle, symbol)
+#define ngx_dlsym_n "dlsym()"
+
+#define ngx_dlclose(handle) dlclose(handle)
+#define ngx_dlclose_n "dlclose()"
+
+
+#if (NGX_HAVE_DLOPEN)
+char *ngx_dlerror(void);
+#endif
+
+
+#endif /* _NGX_DLOPEN_H_INCLUDED_ */
diff --git a/src/nginx-1.18.0/src/os/unix/ngx_errno.c b/src/nginx-1.18.0/src/os/unix/ngx_errno.c
new file mode 100644
index 0000000..e787b23
--- /dev/null
+++ b/src/nginx-1.18.0/src/os/unix/ngx_errno.c
@@ -0,0 +1,87 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+
+
+/*
+ * The strerror() messages are copied because:
+ *
+ * 1) strerror() and strerror_r() functions are not Async-Signal-Safe,
+ * therefore, they cannot be used in signal handlers;
+ *
+ * 2) a direct sys_errlist[] array may be used instead of these functions,
+ * but Linux linker warns about its usage:
+ *
+ * warning: `sys_errlist' is deprecated; use `strerror' or `strerror_r' instead
+ * warning: `sys_nerr' is deprecated; use `strerror' or `strerror_r' instead
+ *
+ * causing false bug reports.
+ */
+
+
+static ngx_str_t *ngx_sys_errlist;
+static ngx_str_t ngx_unknown_error = ngx_string("Unknown error");
+
+
+u_char *
+ngx_strerror(ngx_err_t err, u_char *errstr, size_t size)
+{
+ ngx_str_t *msg;
+
+ msg = ((ngx_uint_t) err < NGX_SYS_NERR) ? &ngx_sys_errlist[err]:
+ &ngx_unknown_error;
+ size = ngx_min(size, msg->len);
+
+ return ngx_cpymem(errstr, msg->data, size);
+}
+
+
+ngx_int_t
+ngx_strerror_init(void)
+{
+ char *msg;
+ u_char *p;
+ size_t len;
+ ngx_err_t err;
+
+ /*
+ * ngx_strerror() is not ready to work at this stage, therefore,
+ * malloc() is used and possible errors are logged using strerror().
+ */
+
+ len = NGX_SYS_NERR * sizeof(ngx_str_t);
+
+ ngx_sys_errlist = malloc(len);
+ if (ngx_sys_errlist == NULL) {
+ goto failed;
+ }
+
+ for (err = 0; err < NGX_SYS_NERR; err++) {
+ msg = strerror(err);
+ len = ngx_strlen(msg);
+
+ p = malloc(len);
+ if (p == NULL) {
+ goto failed;
+ }
+
+ ngx_memcpy(p, msg, len);
+ ngx_sys_errlist[err].len = len;
+ ngx_sys_errlist[err].data = p;
+ }
+
+ return NGX_OK;
+
+failed:
+
+ err = errno;
+ ngx_log_stderr(0, "malloc(%uz) failed (%d: %s)", len, err, strerror(err));
+
+ return NGX_ERROR;
+}
diff --git a/src/nginx-1.18.0/src/os/unix/ngx_errno.h b/src/nginx-1.18.0/src/os/unix/ngx_errno.h
new file mode 100644
index 0000000..7d6ca76
--- /dev/null
+++ b/src/nginx-1.18.0/src/os/unix/ngx_errno.h
@@ -0,0 +1,79 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_ERRNO_H_INCLUDED_
+#define _NGX_ERRNO_H_INCLUDED_
+
+
+#include
+#include
+
+
+typedef int ngx_err_t;
+
+#define NGX_EPERM EPERM
+#define NGX_ENOENT ENOENT
+#define NGX_ENOPATH ENOENT
+#define NGX_ESRCH ESRCH
+#define NGX_EINTR EINTR
+#define NGX_ECHILD ECHILD
+#define NGX_ENOMEM ENOMEM
+#define NGX_EACCES EACCES
+#define NGX_EBUSY EBUSY
+#define NGX_EEXIST EEXIST
+#define NGX_EEXIST_FILE EEXIST
+#define NGX_EXDEV EXDEV
+#define NGX_ENOTDIR ENOTDIR
+#define NGX_EISDIR EISDIR
+#define NGX_EINVAL EINVAL
+#define NGX_ENFILE ENFILE
+#define NGX_EMFILE EMFILE
+#define NGX_ENOSPC ENOSPC
+#define NGX_EPIPE EPIPE
+#define NGX_EINPROGRESS EINPROGRESS
+#define NGX_ENOPROTOOPT ENOPROTOOPT
+#define NGX_EOPNOTSUPP EOPNOTSUPP
+#define NGX_EADDRINUSE EADDRINUSE
+#define NGX_ECONNABORTED ECONNABORTED
+#define NGX_ECONNRESET ECONNRESET
+#define NGX_ENOTCONN ENOTCONN
+#define NGX_ETIMEDOUT ETIMEDOUT
+#define NGX_ECONNREFUSED ECONNREFUSED
+#define NGX_ENAMETOOLONG ENAMETOOLONG
+#define NGX_ENETDOWN ENETDOWN
+#define NGX_ENETUNREACH ENETUNREACH
+#define NGX_EHOSTDOWN EHOSTDOWN
+#define NGX_EHOSTUNREACH EHOSTUNREACH
+#define NGX_ENOSYS ENOSYS
+#define NGX_ECANCELED ECANCELED
+#define NGX_EILSEQ EILSEQ
+#define NGX_ENOMOREFILES 0
+#define NGX_ELOOP ELOOP
+#define NGX_EBADF EBADF
+
+#if (NGX_HAVE_OPENAT)
+#define NGX_EMLINK EMLINK
+#endif
+
+#if (__hpux__)
+#define NGX_EAGAIN EWOULDBLOCK
+#else
+#define NGX_EAGAIN EAGAIN
+#endif
+
+
+#define ngx_errno errno
+#define ngx_socket_errno errno
+#define ngx_set_errno(err) errno = err
+#define ngx_set_socket_errno(err) errno = err
+
+
+u_char *ngx_strerror(ngx_err_t err, u_char *errstr, size_t size);
+ngx_int_t ngx_strerror_init(void);
+
+
+#endif /* _NGX_ERRNO_H_INCLUDED_ */
diff --git a/src/nginx-1.18.0/src/os/unix/ngx_file_aio_read.c b/src/nginx-1.18.0/src/os/unix/ngx_file_aio_read.c
new file mode 100644
index 0000000..bb60ee8
--- /dev/null
+++ b/src/nginx-1.18.0/src/os/unix/ngx_file_aio_read.c
@@ -0,0 +1,216 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+#include
+
+
+/*
+ * FreeBSD file AIO features and quirks:
+ *
+ * if an asked data are already in VM cache, then aio_error() returns 0,
+ * and the data are already copied in buffer;
+ *
+ * aio_read() preread in VM cache as minimum 16K (probably BKVASIZE);
+ * the first AIO preload may be up to 128K;
+ *
+ * aio_read/aio_error() may return EINPROGRESS for just written data;
+ *
+ * kqueue EVFILT_AIO filter is level triggered only: an event repeats
+ * until aio_return() will be called;
+ *
+ * aio_cancel() cannot cancel file AIO: it returns AIO_NOTCANCELED always.
+ */
+
+
+extern int ngx_kqueue;
+
+
+static ssize_t ngx_file_aio_result(ngx_file_t *file, ngx_event_aio_t *aio,
+ ngx_event_t *ev);
+static void ngx_file_aio_event_handler(ngx_event_t *ev);
+
+
+ngx_int_t
+ngx_file_aio_init(ngx_file_t *file, ngx_pool_t *pool)
+{
+ ngx_event_aio_t *aio;
+
+ aio = ngx_pcalloc(pool, sizeof(ngx_event_aio_t));
+ if (aio == NULL) {
+ return NGX_ERROR;
+ }
+
+ aio->file = file;
+ aio->fd = file->fd;
+ aio->event.data = aio;
+ aio->event.ready = 1;
+ aio->event.log = file->log;
+
+ file->aio = aio;
+
+ return NGX_OK;
+}
+
+
+ssize_t
+ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset,
+ ngx_pool_t *pool)
+{
+ int n;
+ ngx_event_t *ev;
+ ngx_event_aio_t *aio;
+
+ if (!ngx_file_aio) {
+ return ngx_read_file(file, buf, size, offset);
+ }
+
+ if (file->aio == NULL && ngx_file_aio_init(file, pool) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ aio = file->aio;
+ ev = &aio->event;
+
+ if (!ev->ready) {
+ ngx_log_error(NGX_LOG_ALERT, file->log, 0,
+ "second aio post for \"%V\"", &file->name);
+ return NGX_AGAIN;
+ }
+
+ ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0,
+ "aio complete:%d @%O:%uz %V",
+ ev->complete, offset, size, &file->name);
+
+ if (ev->complete) {
+ ev->complete = 0;
+ ngx_set_errno(aio->err);
+
+ if (aio->err == 0) {
+ return aio->nbytes;
+ }
+
+ ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,
+ "aio read \"%s\" failed", file->name.data);
+
+ return NGX_ERROR;
+ }
+
+ ngx_memzero(&aio->aiocb, sizeof(struct aiocb));
+
+ aio->aiocb.aio_fildes = file->fd;
+ aio->aiocb.aio_offset = offset;
+ aio->aiocb.aio_buf = buf;
+ aio->aiocb.aio_nbytes = size;
+#if (NGX_HAVE_KQUEUE)
+ aio->aiocb.aio_sigevent.sigev_notify_kqueue = ngx_kqueue;
+ aio->aiocb.aio_sigevent.sigev_notify = SIGEV_KEVENT;
+ aio->aiocb.aio_sigevent.sigev_value.sival_ptr = ev;
+#endif
+ ev->handler = ngx_file_aio_event_handler;
+
+ n = aio_read(&aio->aiocb);
+
+ if (n == -1) {
+ n = ngx_errno;
+
+ if (n == NGX_EAGAIN) {
+ return ngx_read_file(file, buf, size, offset);
+ }
+
+ ngx_log_error(NGX_LOG_CRIT, file->log, n,
+ "aio_read(\"%V\") failed", &file->name);
+
+ if (n == NGX_ENOSYS) {
+ ngx_file_aio = 0;
+ return ngx_read_file(file, buf, size, offset);
+ }
+
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0,
+ "aio_read: fd:%d %d", file->fd, n);
+
+ ev->active = 1;
+ ev->ready = 0;
+ ev->complete = 0;
+
+ return ngx_file_aio_result(aio->file, aio, ev);
+}
+
+
+static ssize_t
+ngx_file_aio_result(ngx_file_t *file, ngx_event_aio_t *aio, ngx_event_t *ev)
+{
+ int n;
+ ngx_err_t err;
+
+ n = aio_error(&aio->aiocb);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0,
+ "aio_error: fd:%d %d", file->fd, n);
+
+ if (n == -1) {
+ err = ngx_errno;
+ aio->err = err;
+
+ ngx_log_error(NGX_LOG_ALERT, file->log, err,
+ "aio_error(\"%V\") failed", &file->name);
+ return NGX_ERROR;
+ }
+
+ if (n == NGX_EINPROGRESS) {
+ if (ev->ready) {
+ ev->ready = 0;
+ ngx_log_error(NGX_LOG_ALERT, file->log, n,
+ "aio_read(\"%V\") still in progress",
+ &file->name);
+ }
+
+ return NGX_AGAIN;
+ }
+
+ n = aio_return(&aio->aiocb);
+
+ if (n == -1) {
+ err = ngx_errno;
+ aio->err = err;
+ ev->ready = 1;
+
+ ngx_log_error(NGX_LOG_CRIT, file->log, err,
+ "aio_return(\"%V\") failed", &file->name);
+ return NGX_ERROR;
+ }
+
+ aio->err = 0;
+ aio->nbytes = n;
+ ev->ready = 1;
+ ev->active = 0;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0,
+ "aio_return: fd:%d %d", file->fd, n);
+
+ return n;
+}
+
+
+static void
+ngx_file_aio_event_handler(ngx_event_t *ev)
+{
+ ngx_event_aio_t *aio;
+
+ aio = ev->data;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, ev->log, 0,
+ "aio event handler fd:%d %V", aio->fd, &aio->file->name);
+
+ if (ngx_file_aio_result(aio->file, aio, ev) != NGX_AGAIN) {
+ aio->handler(ev);
+ }
+}
diff --git a/src/nginx-1.18.0/src/os/unix/ngx_files.c b/src/nginx-1.18.0/src/os/unix/ngx_files.c
new file mode 100644
index 0000000..482d327
--- /dev/null
+++ b/src/nginx-1.18.0/src/os/unix/ngx_files.c
@@ -0,0 +1,907 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include
+#include
+
+
+#if (NGX_THREADS)
+#include
+static void ngx_thread_read_handler(void *data, ngx_log_t *log);
+static void ngx_thread_write_chain_to_file_handler(void *data, ngx_log_t *log);
+#endif
+
+static ngx_chain_t *ngx_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *cl);
+static ssize_t ngx_writev_file(ngx_file_t *file, ngx_iovec_t *vec,
+ off_t offset);
+
+
+#if (NGX_HAVE_FILE_AIO)
+
+ngx_uint_t ngx_file_aio = 1;
+
+#endif
+
+
+ssize_t
+ngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset)
+{
+ ssize_t n;
+
+ ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0,
+ "read: %d, %p, %uz, %O", file->fd, buf, size, offset);
+
+#if (NGX_HAVE_PREAD)
+
+ n = pread(file->fd, buf, size, offset);
+
+ if (n == -1) {
+ ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,
+ "pread() \"%s\" failed", file->name.data);
+ return NGX_ERROR;
+ }
+
+#else
+
+ if (file->sys_offset != offset) {
+ if (lseek(file->fd, offset, SEEK_SET) == -1) {
+ ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,
+ "lseek() \"%s\" failed", file->name.data);
+ return NGX_ERROR;
+ }
+
+ file->sys_offset = offset;
+ }
+
+ n = read(file->fd, buf, size);
+
+ if (n == -1) {
+ ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,
+ "read() \"%s\" failed", file->name.data);
+ return NGX_ERROR;
+ }
+
+ file->sys_offset += n;
+
+#endif
+
+ file->offset += n;
+
+ return n;
+}
+
+
+#if (NGX_THREADS)
+
+typedef struct {
+ ngx_fd_t fd;
+ ngx_uint_t write; /* unsigned write:1; */
+
+ u_char *buf;
+ size_t size;
+ ngx_chain_t *chain;
+ off_t offset;
+
+ size_t nbytes;
+ ngx_err_t err;
+} ngx_thread_file_ctx_t;
+
+
+ssize_t
+ngx_thread_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset,
+ ngx_pool_t *pool)
+{
+ ngx_thread_task_t *task;
+ ngx_thread_file_ctx_t *ctx;
+
+ ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0,
+ "thread read: %d, %p, %uz, %O",
+ file->fd, buf, size, offset);
+
+ task = file->thread_task;
+
+ if (task == NULL) {
+ task = ngx_thread_task_alloc(pool, sizeof(ngx_thread_file_ctx_t));
+ if (task == NULL) {
+ return NGX_ERROR;
+ }
+
+ file->thread_task = task;
+ }
+
+ ctx = task->ctx;
+
+ if (task->event.complete) {
+ task->event.complete = 0;
+
+ if (ctx->write) {
+ ngx_log_error(NGX_LOG_ALERT, file->log, 0,
+ "invalid thread call, read instead of write");
+ return NGX_ERROR;
+ }
+
+ if (ctx->err) {
+ ngx_log_error(NGX_LOG_CRIT, file->log, ctx->err,
+ "pread() \"%s\" failed", file->name.data);
+ return NGX_ERROR;
+ }
+
+ return ctx->nbytes;
+ }
+
+ task->handler = ngx_thread_read_handler;
+
+ ctx->write = 0;
+
+ ctx->fd = file->fd;
+ ctx->buf = buf;
+ ctx->size = size;
+ ctx->offset = offset;
+
+ if (file->thread_handler(task, file) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_AGAIN;
+}
+
+
+#if (NGX_HAVE_PREAD)
+
+static void
+ngx_thread_read_handler(void *data, ngx_log_t *log)
+{
+ ngx_thread_file_ctx_t *ctx = data;
+
+ ssize_t n;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, "thread read handler");
+
+ n = pread(ctx->fd, ctx->buf, ctx->size, ctx->offset);
+
+ if (n == -1) {
+ ctx->err = ngx_errno;
+
+ } else {
+ ctx->nbytes = n;
+ ctx->err = 0;
+ }
+
+#if 0
+ ngx_time_update();
+#endif
+
+ ngx_log_debug4(NGX_LOG_DEBUG_CORE, log, 0,
+ "pread: %z (err: %d) of %uz @%O",
+ n, ctx->err, ctx->size, ctx->offset);
+}
+
+#else
+
+#error pread() is required!
+
+#endif
+
+#endif /* NGX_THREADS */
+
+
+ssize_t
+ngx_write_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset)
+{
+ ssize_t n, written;
+ ngx_err_t err;
+
+ ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0,
+ "write: %d, %p, %uz, %O", file->fd, buf, size, offset);
+
+ written = 0;
+
+#if (NGX_HAVE_PWRITE)
+
+ for ( ;; ) {
+ n = pwrite(file->fd, buf + written, size, offset);
+
+ if (n == -1) {
+ err = ngx_errno;
+
+ if (err == NGX_EINTR) {
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, file->log, err,
+ "pwrite() was interrupted");
+ continue;
+ }
+
+ ngx_log_error(NGX_LOG_CRIT, file->log, err,
+ "pwrite() \"%s\" failed", file->name.data);
+ return NGX_ERROR;
+ }
+
+ file->offset += n;
+ written += n;
+
+ if ((size_t) n == size) {
+ return written;
+ }
+
+ offset += n;
+ size -= n;
+ }
+
+#else
+
+ if (file->sys_offset != offset) {
+ if (lseek(file->fd, offset, SEEK_SET) == -1) {
+ ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,
+ "lseek() \"%s\" failed", file->name.data);
+ return NGX_ERROR;
+ }
+
+ file->sys_offset = offset;
+ }
+
+ for ( ;; ) {
+ n = write(file->fd, buf + written, size);
+
+ if (n == -1) {
+ err = ngx_errno;
+
+ if (err == NGX_EINTR) {
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, file->log, err,
+ "write() was interrupted");
+ continue;
+ }
+
+ ngx_log_error(NGX_LOG_CRIT, file->log, err,
+ "write() \"%s\" failed", file->name.data);
+ return NGX_ERROR;
+ }
+
+ file->sys_offset += n;
+ file->offset += n;
+ written += n;
+
+ if ((size_t) n == size) {
+ return written;
+ }
+
+ size -= n;
+ }
+#endif
+}
+
+
+ngx_fd_t
+ngx_open_tempfile(u_char *name, ngx_uint_t persistent, ngx_uint_t access)
+{
+ ngx_fd_t fd;
+
+ fd = open((const char *) name, O_CREAT|O_EXCL|O_RDWR,
+ access ? access : 0600);
+
+ if (fd != -1 && !persistent) {
+ (void) unlink((const char *) name);
+ }
+
+ return fd;
+}
+
+
+ssize_t
+ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *cl, off_t offset,
+ ngx_pool_t *pool)
+{
+ ssize_t total, n;
+ ngx_iovec_t vec;
+ struct iovec iovs[NGX_IOVS_PREALLOCATE];
+
+ /* use pwrite() if there is the only buf in a chain */
+
+ if (cl->next == NULL) {
+ return ngx_write_file(file, cl->buf->pos,
+ (size_t) (cl->buf->last - cl->buf->pos),
+ offset);
+ }
+
+ total = 0;
+
+ vec.iovs = iovs;
+ vec.nalloc = NGX_IOVS_PREALLOCATE;
+
+ do {
+ /* create the iovec and coalesce the neighbouring bufs */
+ cl = ngx_chain_to_iovec(&vec, cl);
+
+ /* use pwrite() if there is the only iovec buffer */
+
+ if (vec.count == 1) {
+ n = ngx_write_file(file, (u_char *) iovs[0].iov_base,
+ iovs[0].iov_len, offset);
+
+ if (n == NGX_ERROR) {
+ return n;
+ }
+
+ return total + n;
+ }
+
+ n = ngx_writev_file(file, &vec, offset);
+
+ if (n == NGX_ERROR) {
+ return n;
+ }
+
+ offset += n;
+ total += n;
+
+ } while (cl);
+
+ return total;
+}
+
+
+static ngx_chain_t *
+ngx_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *cl)
+{
+ size_t total, size;
+ u_char *prev;
+ ngx_uint_t n;
+ struct iovec *iov;
+
+ iov = NULL;
+ prev = NULL;
+ total = 0;
+ n = 0;
+
+ for ( /* void */ ; cl; cl = cl->next) {
+
+ if (ngx_buf_special(cl->buf)) {
+ continue;
+ }
+
+ size = cl->buf->last - cl->buf->pos;
+
+ if (prev == cl->buf->pos) {
+ iov->iov_len += size;
+
+ } else {
+ if (n == vec->nalloc) {
+ break;
+ }
+
+ iov = &vec->iovs[n++];
+
+ iov->iov_base = (void *) cl->buf->pos;
+ iov->iov_len = size;
+ }
+
+ prev = cl->buf->pos + size;
+ total += size;
+ }
+
+ vec->count = n;
+ vec->size = total;
+
+ return cl;
+}
+
+
+static ssize_t
+ngx_writev_file(ngx_file_t *file, ngx_iovec_t *vec, off_t offset)
+{
+ ssize_t n;
+ ngx_err_t err;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_CORE, file->log, 0,
+ "writev: %d, %uz, %O", file->fd, vec->size, offset);
+
+#if (NGX_HAVE_PWRITEV)
+
+eintr:
+
+ n = pwritev(file->fd, vec->iovs, vec->count, offset);
+
+ if (n == -1) {
+ err = ngx_errno;
+
+ if (err == NGX_EINTR) {
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, file->log, err,
+ "pwritev() was interrupted");
+ goto eintr;
+ }
+
+ ngx_log_error(NGX_LOG_CRIT, file->log, err,
+ "pwritev() \"%s\" failed", file->name.data);
+ return NGX_ERROR;
+ }
+
+ if ((size_t) n != vec->size) {
+ ngx_log_error(NGX_LOG_CRIT, file->log, 0,
+ "pwritev() \"%s\" has written only %z of %uz",
+ file->name.data, n, vec->size);
+ return NGX_ERROR;
+ }
+
+#else
+
+ if (file->sys_offset != offset) {
+ if (lseek(file->fd, offset, SEEK_SET) == -1) {
+ ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,
+ "lseek() \"%s\" failed", file->name.data);
+ return NGX_ERROR;
+ }
+
+ file->sys_offset = offset;
+ }
+
+eintr:
+
+ n = writev(file->fd, vec->iovs, vec->count);
+
+ if (n == -1) {
+ err = ngx_errno;
+
+ if (err == NGX_EINTR) {
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, file->log, err,
+ "writev() was interrupted");
+ goto eintr;
+ }
+
+ ngx_log_error(NGX_LOG_CRIT, file->log, err,
+ "writev() \"%s\" failed", file->name.data);
+ return NGX_ERROR;
+ }
+
+ if ((size_t) n != vec->size) {
+ ngx_log_error(NGX_LOG_CRIT, file->log, 0,
+ "writev() \"%s\" has written only %z of %uz",
+ file->name.data, n, vec->size);
+ return NGX_ERROR;
+ }
+
+ file->sys_offset += n;
+
+#endif
+
+ file->offset += n;
+
+ return n;
+}
+
+
+#if (NGX_THREADS)
+
+ssize_t
+ngx_thread_write_chain_to_file(ngx_file_t *file, ngx_chain_t *cl, off_t offset,
+ ngx_pool_t *pool)
+{
+ ngx_thread_task_t *task;
+ ngx_thread_file_ctx_t *ctx;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_CORE, file->log, 0,
+ "thread write chain: %d, %p, %O",
+ file->fd, cl, offset);
+
+ task = file->thread_task;
+
+ if (task == NULL) {
+ task = ngx_thread_task_alloc(pool,
+ sizeof(ngx_thread_file_ctx_t));
+ if (task == NULL) {
+ return NGX_ERROR;
+ }
+
+ file->thread_task = task;
+ }
+
+ ctx = task->ctx;
+
+ if (task->event.complete) {
+ task->event.complete = 0;
+
+ if (!ctx->write) {
+ ngx_log_error(NGX_LOG_ALERT, file->log, 0,
+ "invalid thread call, write instead of read");
+ return NGX_ERROR;
+ }
+
+ if (ctx->err || ctx->nbytes == 0) {
+ ngx_log_error(NGX_LOG_CRIT, file->log, ctx->err,
+ "pwritev() \"%s\" failed", file->name.data);
+ return NGX_ERROR;
+ }
+
+ file->offset += ctx->nbytes;
+ return ctx->nbytes;
+ }
+
+ task->handler = ngx_thread_write_chain_to_file_handler;
+
+ ctx->write = 1;
+
+ ctx->fd = file->fd;
+ ctx->chain = cl;
+ ctx->offset = offset;
+
+ if (file->thread_handler(task, file) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_AGAIN;
+}
+
+
+static void
+ngx_thread_write_chain_to_file_handler(void *data, ngx_log_t *log)
+{
+ ngx_thread_file_ctx_t *ctx = data;
+
+#if (NGX_HAVE_PWRITEV)
+
+ off_t offset;
+ ssize_t n;
+ ngx_err_t err;
+ ngx_chain_t *cl;
+ ngx_iovec_t vec;
+ struct iovec iovs[NGX_IOVS_PREALLOCATE];
+
+ vec.iovs = iovs;
+ vec.nalloc = NGX_IOVS_PREALLOCATE;
+
+ cl = ctx->chain;
+ offset = ctx->offset;
+
+ ctx->nbytes = 0;
+ ctx->err = 0;
+
+ do {
+ /* create the iovec and coalesce the neighbouring bufs */
+ cl = ngx_chain_to_iovec(&vec, cl);
+
+eintr:
+
+ n = pwritev(ctx->fd, iovs, vec.count, offset);
+
+ if (n == -1) {
+ err = ngx_errno;
+
+ if (err == NGX_EINTR) {
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, err,
+ "pwritev() was interrupted");
+ goto eintr;
+ }
+
+ ctx->err = err;
+ return;
+ }
+
+ if ((size_t) n != vec.size) {
+ ctx->nbytes = 0;
+ return;
+ }
+
+ ctx->nbytes += n;
+ offset += n;
+ } while (cl);
+
+#else
+
+ ctx->err = NGX_ENOSYS;
+ return;
+
+#endif
+}
+
+#endif /* NGX_THREADS */
+
+
+ngx_int_t
+ngx_set_file_time(u_char *name, ngx_fd_t fd, time_t s)
+{
+ struct timeval tv[2];
+
+ tv[0].tv_sec = ngx_time();
+ tv[0].tv_usec = 0;
+ tv[1].tv_sec = s;
+ tv[1].tv_usec = 0;
+
+ if (utimes((char *) name, tv) != -1) {
+ return NGX_OK;
+ }
+
+ return NGX_ERROR;
+}
+
+
+ngx_int_t
+ngx_create_file_mapping(ngx_file_mapping_t *fm)
+{
+ fm->fd = ngx_open_file(fm->name, NGX_FILE_RDWR, NGX_FILE_TRUNCATE,
+ NGX_FILE_DEFAULT_ACCESS);
+
+ if (fm->fd == NGX_INVALID_FILE) {
+ ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,
+ ngx_open_file_n " \"%s\" failed", fm->name);
+ return NGX_ERROR;
+ }
+
+ if (ftruncate(fm->fd, fm->size) == -1) {
+ ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,
+ "ftruncate() \"%s\" failed", fm->name);
+ goto failed;
+ }
+
+ fm->addr = mmap(NULL, fm->size, PROT_READ|PROT_WRITE, MAP_SHARED,
+ fm->fd, 0);
+ if (fm->addr != MAP_FAILED) {
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,
+ "mmap(%uz) \"%s\" failed", fm->size, fm->name);
+
+failed:
+
+ if (ngx_close_file(fm->fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", fm->name);
+ }
+
+ return NGX_ERROR;
+}
+
+
+void
+ngx_close_file_mapping(ngx_file_mapping_t *fm)
+{
+ if (munmap(fm->addr, fm->size) == -1) {
+ ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,
+ "munmap(%uz) \"%s\" failed", fm->size, fm->name);
+ }
+
+ if (ngx_close_file(fm->fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", fm->name);
+ }
+}
+
+
+ngx_int_t
+ngx_open_dir(ngx_str_t *name, ngx_dir_t *dir)
+{
+ dir->dir = opendir((const char *) name->data);
+
+ if (dir->dir == NULL) {
+ return NGX_ERROR;
+ }
+
+ dir->valid_info = 0;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_read_dir(ngx_dir_t *dir)
+{
+ dir->de = readdir(dir->dir);
+
+ if (dir->de) {
+#if (NGX_HAVE_D_TYPE)
+ dir->type = dir->de->d_type;
+#else
+ dir->type = 0;
+#endif
+ return NGX_OK;
+ }
+
+ return NGX_ERROR;
+}
+
+
+ngx_int_t
+ngx_open_glob(ngx_glob_t *gl)
+{
+ int n;
+
+ n = glob((char *) gl->pattern, 0, NULL, &gl->pglob);
+
+ if (n == 0) {
+ return NGX_OK;
+ }
+
+#ifdef GLOB_NOMATCH
+
+ if (n == GLOB_NOMATCH && gl->test) {
+ return NGX_OK;
+ }
+
+#endif
+
+ return NGX_ERROR;
+}
+
+
+ngx_int_t
+ngx_read_glob(ngx_glob_t *gl, ngx_str_t *name)
+{
+ size_t count;
+
+#ifdef GLOB_NOMATCH
+ count = (size_t) gl->pglob.gl_pathc;
+#else
+ count = (size_t) gl->pglob.gl_matchc;
+#endif
+
+ if (gl->n < count) {
+
+ name->len = (size_t) ngx_strlen(gl->pglob.gl_pathv[gl->n]);
+ name->data = (u_char *) gl->pglob.gl_pathv[gl->n];
+ gl->n++;
+
+ return NGX_OK;
+ }
+
+ return NGX_DONE;
+}
+
+
+void
+ngx_close_glob(ngx_glob_t *gl)
+{
+ globfree(&gl->pglob);
+}
+
+
+ngx_err_t
+ngx_trylock_fd(ngx_fd_t fd)
+{
+ struct flock fl;
+
+ ngx_memzero(&fl, sizeof(struct flock));
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+
+ if (fcntl(fd, F_SETLK, &fl) == -1) {
+ return ngx_errno;
+ }
+
+ return 0;
+}
+
+
+ngx_err_t
+ngx_lock_fd(ngx_fd_t fd)
+{
+ struct flock fl;
+
+ ngx_memzero(&fl, sizeof(struct flock));
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+
+ if (fcntl(fd, F_SETLKW, &fl) == -1) {
+ return ngx_errno;
+ }
+
+ return 0;
+}
+
+
+ngx_err_t
+ngx_unlock_fd(ngx_fd_t fd)
+{
+ struct flock fl;
+
+ ngx_memzero(&fl, sizeof(struct flock));
+ fl.l_type = F_UNLCK;
+ fl.l_whence = SEEK_SET;
+
+ if (fcntl(fd, F_SETLK, &fl) == -1) {
+ return ngx_errno;
+ }
+
+ return 0;
+}
+
+
+#if (NGX_HAVE_POSIX_FADVISE) && !(NGX_HAVE_F_READAHEAD)
+
+ngx_int_t
+ngx_read_ahead(ngx_fd_t fd, size_t n)
+{
+ int err;
+
+ err = posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
+
+ if (err == 0) {
+ return 0;
+ }
+
+ ngx_set_errno(err);
+ return NGX_FILE_ERROR;
+}
+
+#endif
+
+
+#if (NGX_HAVE_O_DIRECT)
+
+ngx_int_t
+ngx_directio_on(ngx_fd_t fd)
+{
+ int flags;
+
+ flags = fcntl(fd, F_GETFL);
+
+ if (flags == -1) {
+ return NGX_FILE_ERROR;
+ }
+
+ return fcntl(fd, F_SETFL, flags | O_DIRECT);
+}
+
+
+ngx_int_t
+ngx_directio_off(ngx_fd_t fd)
+{
+ int flags;
+
+ flags = fcntl(fd, F_GETFL);
+
+ if (flags == -1) {
+ return NGX_FILE_ERROR;
+ }
+
+ return fcntl(fd, F_SETFL, flags & ~O_DIRECT);
+}
+
+#endif
+
+
+#if (NGX_HAVE_STATFS)
+
+size_t
+ngx_fs_bsize(u_char *name)
+{
+ struct statfs fs;
+
+ if (statfs((char *) name, &fs) == -1) {
+ return 512;
+ }
+
+ if ((fs.f_bsize % 512) != 0) {
+ return 512;
+ }
+
+ return (size_t) fs.f_bsize;
+}
+
+#elif (NGX_HAVE_STATVFS)
+
+size_t
+ngx_fs_bsize(u_char *name)
+{
+ struct statvfs fs;
+
+ if (statvfs((char *) name, &fs) == -1) {
+ return 512;
+ }
+
+ if ((fs.f_frsize % 512) != 0) {
+ return 512;
+ }
+
+ return (size_t) fs.f_frsize;
+}
+
+#else
+
+size_t
+ngx_fs_bsize(u_char *name)
+{
+ return 512;
+}
+
+#endif
diff --git a/src/nginx-1.18.0/src/os/unix/ngx_files.h b/src/nginx-1.18.0/src/os/unix/ngx_files.h
new file mode 100644
index 0000000..383e38e
--- /dev/null
+++ b/src/nginx-1.18.0/src/os/unix/ngx_files.h
@@ -0,0 +1,392 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_FILES_H_INCLUDED_
+#define _NGX_FILES_H_INCLUDED_
+
+
+#include
+#include
+
+
+typedef int ngx_fd_t;
+typedef struct stat ngx_file_info_t;
+typedef ino_t ngx_file_uniq_t;
+
+
+typedef struct {
+ u_char *name;
+ size_t size;
+ void *addr;
+ ngx_fd_t fd;
+ ngx_log_t *log;
+} ngx_file_mapping_t;
+
+
+typedef struct {
+ DIR *dir;
+ struct dirent *de;
+ struct stat info;
+
+ unsigned type:8;
+ unsigned valid_info:1;
+} ngx_dir_t;
+
+
+typedef struct {
+ size_t n;
+ glob_t pglob;
+ u_char *pattern;
+ ngx_log_t *log;
+ ngx_uint_t test;
+} ngx_glob_t;
+
+
+#define NGX_INVALID_FILE -1
+#define NGX_FILE_ERROR -1
+
+
+
+#ifdef __CYGWIN__
+
+#ifndef NGX_HAVE_CASELESS_FILESYSTEM
+#define NGX_HAVE_CASELESS_FILESYSTEM 1
+#endif
+
+#define ngx_open_file(name, mode, create, access) \
+ open((const char *) name, mode|create|O_BINARY, access)
+
+#else
+
+#define ngx_open_file(name, mode, create, access) \
+ open((const char *) name, mode|create, access)
+
+#endif
+
+#define ngx_open_file_n "open()"
+
+#define NGX_FILE_RDONLY O_RDONLY
+#define NGX_FILE_WRONLY O_WRONLY
+#define NGX_FILE_RDWR O_RDWR
+#define NGX_FILE_CREATE_OR_OPEN O_CREAT
+#define NGX_FILE_OPEN 0
+#define NGX_FILE_TRUNCATE (O_CREAT|O_TRUNC)
+#define NGX_FILE_APPEND (O_WRONLY|O_APPEND)
+#define NGX_FILE_NONBLOCK O_NONBLOCK
+
+#if (NGX_HAVE_OPENAT)
+#define NGX_FILE_NOFOLLOW O_NOFOLLOW
+
+#if defined(O_DIRECTORY)
+#define NGX_FILE_DIRECTORY O_DIRECTORY
+#else
+#define NGX_FILE_DIRECTORY 0
+#endif
+
+#if defined(O_SEARCH)
+#define NGX_FILE_SEARCH (O_SEARCH|NGX_FILE_DIRECTORY)
+
+#elif defined(O_EXEC)
+#define NGX_FILE_SEARCH (O_EXEC|NGX_FILE_DIRECTORY)
+
+#elif (NGX_HAVE_O_PATH)
+#define NGX_FILE_SEARCH (O_PATH|O_RDONLY|NGX_FILE_DIRECTORY)
+
+#else
+#define NGX_FILE_SEARCH (O_RDONLY|NGX_FILE_DIRECTORY)
+#endif
+
+#endif /* NGX_HAVE_OPENAT */
+
+#define NGX_FILE_DEFAULT_ACCESS 0644
+#define NGX_FILE_OWNER_ACCESS 0600
+
+
+#define ngx_close_file close
+#define ngx_close_file_n "close()"
+
+
+#define ngx_delete_file(name) unlink((const char *) name)
+#define ngx_delete_file_n "unlink()"
+
+
+ngx_fd_t ngx_open_tempfile(u_char *name, ngx_uint_t persistent,
+ ngx_uint_t access);
+#define ngx_open_tempfile_n "open()"
+
+
+ssize_t ngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset);
+#if (NGX_HAVE_PREAD)
+#define ngx_read_file_n "pread()"
+#else
+#define ngx_read_file_n "read()"
+#endif
+
+ssize_t ngx_write_file(ngx_file_t *file, u_char *buf, size_t size,
+ off_t offset);
+
+ssize_t ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *ce,
+ off_t offset, ngx_pool_t *pool);
+
+
+#define ngx_read_fd read
+#define ngx_read_fd_n "read()"
+
+/*
+ * we use inlined function instead of simple #define
+ * because glibc 2.3 sets warn_unused_result attribute for write()
+ * and in this case gcc 4.3 ignores (void) cast
+ */
+static ngx_inline ssize_t
+ngx_write_fd(ngx_fd_t fd, void *buf, size_t n)
+{
+ return write(fd, buf, n);
+}
+
+#define ngx_write_fd_n "write()"
+
+
+#define ngx_write_console ngx_write_fd
+
+
+#define ngx_linefeed(p) *p++ = LF;
+#define NGX_LINEFEED_SIZE 1
+#define NGX_LINEFEED "\x0a"
+
+
+#define ngx_rename_file(o, n) rename((const char *) o, (const char *) n)
+#define ngx_rename_file_n "rename()"
+
+
+#define ngx_change_file_access(n, a) chmod((const char *) n, a)
+#define ngx_change_file_access_n "chmod()"
+
+
+ngx_int_t ngx_set_file_time(u_char *name, ngx_fd_t fd, time_t s);
+#define ngx_set_file_time_n "utimes()"
+
+
+#define ngx_file_info(file, sb) stat((const char *) file, sb)
+#define ngx_file_info_n "stat()"
+
+#define ngx_fd_info(fd, sb) fstat(fd, sb)
+#define ngx_fd_info_n "fstat()"
+
+#define ngx_link_info(file, sb) lstat((const char *) file, sb)
+#define ngx_link_info_n "lstat()"
+
+#define ngx_is_dir(sb) (S_ISDIR((sb)->st_mode))
+#define ngx_is_file(sb) (S_ISREG((sb)->st_mode))
+#define ngx_is_link(sb) (S_ISLNK((sb)->st_mode))
+#define ngx_is_exec(sb) (((sb)->st_mode & S_IXUSR) == S_IXUSR)
+#define ngx_file_access(sb) ((sb)->st_mode & 0777)
+#define ngx_file_size(sb) (sb)->st_size
+#define ngx_file_fs_size(sb) ngx_max((sb)->st_size, (sb)->st_blocks * 512)
+#define ngx_file_mtime(sb) (sb)->st_mtime
+#define ngx_file_uniq(sb) (sb)->st_ino
+
+
+ngx_int_t ngx_create_file_mapping(ngx_file_mapping_t *fm);
+void ngx_close_file_mapping(ngx_file_mapping_t *fm);
+
+
+#define ngx_realpath(p, r) (u_char *) realpath((char *) p, (char *) r)
+#define ngx_realpath_n "realpath()"
+#define ngx_getcwd(buf, size) (getcwd((char *) buf, size) != NULL)
+#define ngx_getcwd_n "getcwd()"
+#define ngx_path_separator(c) ((c) == '/')
+
+
+#if defined(PATH_MAX)
+
+#define NGX_HAVE_MAX_PATH 1
+#define NGX_MAX_PATH PATH_MAX
+
+#else
+
+#define NGX_MAX_PATH 4096
+
+#endif
+
+
+ngx_int_t ngx_open_dir(ngx_str_t *name, ngx_dir_t *dir);
+#define ngx_open_dir_n "opendir()"
+
+
+#define ngx_close_dir(d) closedir((d)->dir)
+#define ngx_close_dir_n "closedir()"
+
+
+ngx_int_t ngx_read_dir(ngx_dir_t *dir);
+#define ngx_read_dir_n "readdir()"
+
+
+#define ngx_create_dir(name, access) mkdir((const char *) name, access)
+#define ngx_create_dir_n "mkdir()"
+
+
+#define ngx_delete_dir(name) rmdir((const char *) name)
+#define ngx_delete_dir_n "rmdir()"
+
+
+#define ngx_dir_access(a) (a | (a & 0444) >> 2)
+
+
+#define ngx_de_name(dir) ((u_char *) (dir)->de->d_name)
+#if (NGX_HAVE_D_NAMLEN)
+#define ngx_de_namelen(dir) (dir)->de->d_namlen
+#else
+#define ngx_de_namelen(dir) ngx_strlen((dir)->de->d_name)
+#endif
+
+static ngx_inline ngx_int_t
+ngx_de_info(u_char *name, ngx_dir_t *dir)
+{
+ dir->type = 0;
+ return stat((const char *) name, &dir->info);
+}
+
+#define ngx_de_info_n "stat()"
+#define ngx_de_link_info(name, dir) lstat((const char *) name, &(dir)->info)
+#define ngx_de_link_info_n "lstat()"
+
+#if (NGX_HAVE_D_TYPE)
+
+/*
+ * some file systems (e.g. XFS on Linux and CD9660 on FreeBSD)
+ * do not set dirent.d_type
+ */
+
+#define ngx_de_is_dir(dir) \
+ (((dir)->type) ? ((dir)->type == DT_DIR) : (S_ISDIR((dir)->info.st_mode)))
+#define ngx_de_is_file(dir) \
+ (((dir)->type) ? ((dir)->type == DT_REG) : (S_ISREG((dir)->info.st_mode)))
+#define ngx_de_is_link(dir) \
+ (((dir)->type) ? ((dir)->type == DT_LNK) : (S_ISLNK((dir)->info.st_mode)))
+
+#else
+
+#define ngx_de_is_dir(dir) (S_ISDIR((dir)->info.st_mode))
+#define ngx_de_is_file(dir) (S_ISREG((dir)->info.st_mode))
+#define ngx_de_is_link(dir) (S_ISLNK((dir)->info.st_mode))
+
+#endif
+
+#define ngx_de_access(dir) (((dir)->info.st_mode) & 0777)
+#define ngx_de_size(dir) (dir)->info.st_size
+#define ngx_de_fs_size(dir) \
+ ngx_max((dir)->info.st_size, (dir)->info.st_blocks * 512)
+#define ngx_de_mtime(dir) (dir)->info.st_mtime
+
+
+ngx_int_t ngx_open_glob(ngx_glob_t *gl);
+#define ngx_open_glob_n "glob()"
+ngx_int_t ngx_read_glob(ngx_glob_t *gl, ngx_str_t *name);
+void ngx_close_glob(ngx_glob_t *gl);
+
+
+ngx_err_t ngx_trylock_fd(ngx_fd_t fd);
+ngx_err_t ngx_lock_fd(ngx_fd_t fd);
+ngx_err_t ngx_unlock_fd(ngx_fd_t fd);
+
+#define ngx_trylock_fd_n "fcntl(F_SETLK, F_WRLCK)"
+#define ngx_lock_fd_n "fcntl(F_SETLKW, F_WRLCK)"
+#define ngx_unlock_fd_n "fcntl(F_SETLK, F_UNLCK)"
+
+
+#if (NGX_HAVE_F_READAHEAD)
+
+#define NGX_HAVE_READ_AHEAD 1
+
+#define ngx_read_ahead(fd, n) fcntl(fd, F_READAHEAD, (int) n)
+#define ngx_read_ahead_n "fcntl(fd, F_READAHEAD)"
+
+#elif (NGX_HAVE_POSIX_FADVISE)
+
+#define NGX_HAVE_READ_AHEAD 1
+
+ngx_int_t ngx_read_ahead(ngx_fd_t fd, size_t n);
+#define ngx_read_ahead_n "posix_fadvise(POSIX_FADV_SEQUENTIAL)"
+
+#else
+
+#define ngx_read_ahead(fd, n) 0
+#define ngx_read_ahead_n "ngx_read_ahead_n"
+
+#endif
+
+
+#if (NGX_HAVE_O_DIRECT)
+
+ngx_int_t ngx_directio_on(ngx_fd_t fd);
+#define ngx_directio_on_n "fcntl(O_DIRECT)"
+
+ngx_int_t ngx_directio_off(ngx_fd_t fd);
+#define ngx_directio_off_n "fcntl(!O_DIRECT)"
+
+#elif (NGX_HAVE_F_NOCACHE)
+
+#define ngx_directio_on(fd) fcntl(fd, F_NOCACHE, 1)
+#define ngx_directio_on_n "fcntl(F_NOCACHE, 1)"
+
+#elif (NGX_HAVE_DIRECTIO)
+
+#define ngx_directio_on(fd) directio(fd, DIRECTIO_ON)
+#define ngx_directio_on_n "directio(DIRECTIO_ON)"
+
+#else
+
+#define ngx_directio_on(fd) 0
+#define ngx_directio_on_n "ngx_directio_on_n"
+
+#endif
+
+size_t ngx_fs_bsize(u_char *name);
+
+
+#if (NGX_HAVE_OPENAT)
+
+#define ngx_openat_file(fd, name, mode, create, access) \
+ openat(fd, (const char *) name, mode|create, access)
+
+#define ngx_openat_file_n "openat()"
+
+#define ngx_file_at_info(fd, name, sb, flag) \
+ fstatat(fd, (const char *) name, sb, flag)
+
+#define ngx_file_at_info_n "fstatat()"
+
+#define NGX_AT_FDCWD (ngx_fd_t) AT_FDCWD
+
+#endif
+
+
+#define ngx_stdout STDOUT_FILENO
+#define ngx_stderr STDERR_FILENO
+#define ngx_set_stderr(fd) dup2(fd, STDERR_FILENO)
+#define ngx_set_stderr_n "dup2(STDERR_FILENO)"
+
+
+#if (NGX_HAVE_FILE_AIO)
+
+ngx_int_t ngx_file_aio_init(ngx_file_t *file, ngx_pool_t *pool);
+ssize_t ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size,
+ off_t offset, ngx_pool_t *pool);
+
+extern ngx_uint_t ngx_file_aio;
+
+#endif
+
+#if (NGX_THREADS)
+ssize_t ngx_thread_read(ngx_file_t *file, u_char *buf, size_t size,
+ off_t offset, ngx_pool_t *pool);
+ssize_t ngx_thread_write_chain_to_file(ngx_file_t *file, ngx_chain_t *cl,
+ off_t offset, ngx_pool_t *pool);
+#endif
+
+
+#endif /* _NGX_FILES_H_INCLUDED_ */
diff --git a/src/nginx-1.18.0/src/os/unix/ngx_freebsd.h b/src/nginx-1.18.0/src/os/unix/ngx_freebsd.h
new file mode 100644
index 0000000..4f93da5
--- /dev/null
+++ b/src/nginx-1.18.0/src/os/unix/ngx_freebsd.h
@@ -0,0 +1,25 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_FREEBSD_H_INCLUDED_
+#define _NGX_FREEBSD_H_INCLUDED_
+
+
+void ngx_debug_init(void);
+ngx_chain_t *ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in,
+ off_t limit);
+
+extern int ngx_freebsd_kern_osreldate;
+extern int ngx_freebsd_hw_ncpu;
+extern u_long ngx_freebsd_net_inet_tcp_sendspace;
+
+extern ngx_uint_t ngx_freebsd_sendfile_nbytes_bug;
+extern ngx_uint_t ngx_freebsd_use_tcp_nopush;
+extern ngx_uint_t ngx_debug_malloc;
+
+
+#endif /* _NGX_FREEBSD_H_INCLUDED_ */
diff --git a/src/nginx-1.18.0/src/os/unix/ngx_freebsd_config.h b/src/nginx-1.18.0/src/os/unix/ngx_freebsd_config.h
new file mode 100644
index 0000000..c641108
--- /dev/null
+++ b/src/nginx-1.18.0/src/os/unix/ngx_freebsd_config.h
@@ -0,0 +1,129 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_FREEBSD_CONFIG_H_INCLUDED_
+#define _NGX_FREEBSD_CONFIG_H_INCLUDED_
+
+
+#include
+#include
+#include
+#include
+#include /* offsetof() */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include /* ALIGN() */
+#include /* statfs() */
+
+#include /* FIONBIO */
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include