# HG changeset patch # User Maxim Dounin # Date 1247514984 -14400 # Node ID 8fbdd980b527b10fabb241deb537131f97f8003a # Parent 44a61c599bb261acf2a233ff269eab593f8ed03e# Parent 0001f4fa05017605d814ce2f6e2012b87d8d75d2 Merge with current. diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -206,3 +206,49 @@ a2a3905c04abe7ff22d33176682cdba5eb6cf186 a8e3f1441eec1b608b77f39110e16f0b82b983d5 NGINX_0_7_17 b246022ef4543cf779e3a0e040922d069c109171 NGINX_0_7_18 4b19c15c95eb9f49f48295d139dcdfd77bb22f38 NGINX_0_7_19 +b4f69f2ef02c188a31125d0774191becd3c3a10d NGINX_0_7_20 +ff86d646f9df4cf9ec14662fe09269d57d9598ea NGINX_0_7_21 +ad0a34a8efa6b727cfb4bbc7d94cab0e882fd87b NGINX_0_7_22 +88d3e895bdf94af51c38b5da5a986ff942dd52c5 NGINX_0_7_23 +9da1d9d94d1860d5a209110197fe4826766faa60 NGINX_0_7_24 +e7dbea1ee115c746298309e05501fc13ce942547 NGINX_0_7_25 +21aff1b3da48c501503d5e47d373c6382413c5e4 NGINX_0_7_26 +dac47e9ef0d5fc6ec8cd1e0ff433c86e96d1a358 NGINX_0_7_27 +fd759445d8a890a9db4ab645581358fdce277908 NGINX_0_7_28 +49a0eb7ce20c1114dd25a73373b9ad5f77d02ed7 NGINX_0_7_29 +dc98ed169c03366ef89869d49da3b21b4b6663fe NGINX_0_7_30 +ce4f9ff90bfa58834c5b0db35395fd980c8c4aa0 NGINX_0_7_31 +6281966854a55e092674b01b6861bd025fe158ee NGINX_0_7_32 +670af56a1158749e82d7b1fce1ce348f8e10472a NGINX_0_7_33 +33394d1255b058295bc596287d249ccd612fb41e NGINX_0_7_34 +15a022ee809bb6234f9ca4993714d9820ef954dd NGINX_0_7_35 +76a79816b77167fb3fc7591b21de449d9cd71322 NGINX_0_7_36 +20962db0117cbc0996dc82ff31f8abc6c3598451 NGINX_0_7_37 +fc5ebf0e5f98c4a7c1c370234e3f9be09b94a2a1 NGINX_0_7_38 +a8424ffa495cf3a21769c567df89bef2a9c0c3fd NGINX_0_7_39 +ca8f7f6cab16b29a7be5b2b789a743bad2d8f0a8 NGINX_0_7_40 +2e2b57743e871f3e1aa5994b693da5d248df5800 NGINX_0_7_41 +bb941a2996a6026f35c76f60ec2cd56c23f34ae5 NGINX_0_7_42 +dcb6b5f9d526e677c2860164d9c03b33caa0bd35 NGINX_0_7_43 +c8cfb6c462efa1a49e51fd193e4c645a5cfb212c NGINX_0_7_44 +9eda3153223b42932fec477da26be67a8a33b781 NGINX_0_7_45 +56baf312c1b57b425c331a325b7875fd92aff5b8 NGINX_0_7_46 +6866b490272ef912cd285ce6edc2c393546ea947 NGINX_0_7_47 +09f0ef15d5446d925a7b556925235563ef7932f7 NGINX_0_7_48 +3a4102b00f9753c6b851becebe0d04996b7ff50e NGINX_0_7_49 +09b45263c8178db911e60992a21df9278736dfe4 NGINX_0_7_50 +f2c6a737327477de6844021269e95333e03eba19 NGINX_0_7_51 +549994537f15f7212b5f09f1f4d6e00f2dc562f9 NGINX_0_7_52 +392c16f2d85815c32add2715607598013ce58014 NGINX_0_7_53 +ed5e10fb40fc02b64d6c5719141e33f74cccd02a NGINX_0_7_54 +6484cbba022236c1cd094abfa117dd3aa1b70181 NGINX_0_7_55 +829f9a66a65923d1765961bf1c5075869d421633 NGINX_0_7_56 +e66f886a83052c6a09dfbd77bfc78310dd7fd2d9 NGINX_0_7_57 +98143f74eb3d9d87babc0934c9cd7c40b7af277a NGINX_0_7_58 +499474178a112350895b502f16a2d1abe1b5b532 NGINX_0_7_59 +f39b9e29530de0464264608c33d84abf2e10b9f2 NGINX_0_8_0 +207ae3ff044452b1fb8ad28951ff566615a51939 NGINX_0_8_1 +13b908d5338a1db8365c33efc6189569bb9d58a7 NGINX_0_8_2 +f0cac61857ae53b080bb44fa899762c901079dee NGINX_0_8_3 +c178dbab489a192d9e40f7747777a0890e35e93b NGINX_0_8_4 +e8b686f230a83ac654d963d1d8e0f96893e0c372 NGINX_0_8_5 diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,693 @@ +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. the bug had + appeared in 0.8.2. + + *) 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 is the 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. @@ -169,7 +858,7 @@ Changes with nginx 0.7.9 *) 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 bugs had + set, then the exact name was matched by the wildcard; the bug had appeared in 0.3.18. @@ -1716,7 +2405,7 @@ Changes with nginx 0.3.55 *) Bugfix: if the request contained "//" or "/./" and escaped symbols after them, then the proxied request was sent unescaped. - *) Bugfix: the $r->headers_in("Cookie") of the ngx_http_perl_module now + *) Bugfix: the $r->header_in("Cookie") of the ngx_http_perl_module now returns all "Cookie" header lines. *) Bugfix: a segmentation fault occurred if @@ -3230,8 +3919,8 @@ Changes with nginx 0.1.18 *) Bugfix: the proxy_set_x_var and fastcgi_set_var directives were not inherited. - *) Bugfix: in the redirect rewrite directive the arguments were - concatenated with URI by the "&" rather than the "?". + *) Bugfix: in a redirect rewrite directive arguments were concatenated + with URI by an "&" rather than a "?". *) Bugfix: the lines without trailing ";" in the file being included by the ngx_http_geo_module were silently ignored. diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,695 @@ +Изменения в nginx 0.8.5 13.07.2009 + + *) Исправление: теперь nginx разрешает подчёркивания в методе запроса. + + *) Исправление: при использовании HTTP Basic-аутентификации на Windows + для неверных имени/пароля возвращалась 500-ая ошибка. + + *) Исправление: ответы модуля ngx_http_perl_module не работали в + подзапросах. + + *) Исправление: в модуле ngx_http_limit_req_module. + Спасибо Максиму Дунину. + + +Изменения в nginx 0.8.4 22.06.2009 + + *) Исправление: nginx не собирался с параметром --without-http-cache; + ошибка появилась в 0.8.3. + + +Изменения в nginx 0.8.3 19.06.2009 + + *) Добавление: переменная $upstream_cache_status. + + *) Исправление: nginx не собирался на MacOSX 10.6. + + *) Исправление: nginx не собирался с параметром --without-http-cache; + ошибка появилась в 0.8.2. + + *) Исправление: если использовался перехват 401 ошибки от бэкенда и + бэкенд не возвращал строку "WWW-Authenticate" в заголовке ответа, то + в рабочем процессе происходил segmentation fault. + Спасибо Евгению Мычло. + + +Изменения в nginx 0.8.2 15.06.2009 + + *) Исправление: во взаимодействии open_file_cache и proxy/fastcgi кэша + на старте. + + *) Исправление: open_file_cache мог кэшировать открытые файлы очень + долго; ошибка появилась в 0.7.4. + + +Изменения в nginx 0.8.1 08.06.2009 + + *) Добавление: параметр updating в директивах proxy_cache_use_stale и + fastcgi_cache_use_stale. + + *) Исправление: строки "If-Modified-Since", "If-Range" и им подобные в + заголовке запроса клиента передавались бэкенду при кэшировании, если + не использовалась директива proxy_set_header с любыми параметрами. + + *) Исправление: строки "Set-Cookie" и "P3P" в заголовке ответа бэкенда + не скрывались при кэшировании, если не использовались директивы + proxy_hide_header/fastcgi_hide_header с любыми параметрами. + + *) Исправление: модуль ngx_http_image_filter_module не понимал формат + GIF87a. + Спасибо Денису Ильиных. + + *) Исправление: nginx не собирался на Solaris 10 и более ранних; ошибка + появилась в 0.7.56. + + +Изменения в nginx 0.8.0 02.06.2009 + + *) Добавление: директива keepalive_requests. + + *) Добавление: директива limit_rate_after. + Спасибо Ivan Debnar. + + *) Исправление: XSLT-фильтр не работал в подзапросах. + + *) Исправление: обработке относительных путей в nginx/Windows. + + *) Исправление: в proxy_store, fastcgi_store, proxy_cache и + fastcgi_cache в nginx/Windows. + + *) Исправление: в обработке ошибок выделения памяти. + Спасибо Максиму Дунину и Кириллу Коринскому. + + +Изменения в nginx 0.7.59 25.05.2009 + + *) Добавление: директивы proxy_cache_methods и fastcgi_cache_methods. + + *) Исправление: утечки сокетов; ошибка появилась в 0.7.25. + Спасибо Максиму Дунину. + + *) Исправление: при использовании переменной $request_body в рабочем + процессе происходил segmentation fault, если в запросе не было тела; + ошибка появилась в 0.7.58. + + *) Исправление: SSL-модули могли не собираться на Solaris и Linux; + ошибка появилась в 0.7.56. + + *) Исправление: ответы модуля ngx_http_xslt_filter_module не + обрабатывались SSI-, charset- и gzip-фильтрами. + + *) Исправление: директива charset не ставила кодировку для ответов + модуля ngx_http_gzip_static_module. + + +Изменения в nginx 0.7.58 18.05.2009 + + *) Добавление: директива listen почтового прокси-сервера поддерживает + IPv6. + + *) Добавление: директива image_filter_jpeg_quality. + + *) Добавление: директива client_body_in_single_buffer. + + *) Добавление: переменная $request_body. + + *) Исправление: в модуле ngx_http_autoindex_module в ссылках на имена + файлов, содержащих символ ":". + + *) Исправление: процедура "make upgrade" не работала; ошибка появилась + в 0.7.53. + Спасибо Денису Латыпову. + + +Изменения в nginx 0.7.57 12.05.2009 + + *) Исправление: при перенаправлении ошибок модуля + ngx_http_image_filter_module в именованный location в рабочем + процессе происходил floating-point fault; ошибка появилась в 0.7.56. + + +Изменения в nginx 0.7.56 11.05.2009 + + *) Добавление: nginx/Windows поддерживает IPv6 в директиве listen + модуля HTTP. + + *) Исправление: в модуле ngx_http_image_filter_module. + + +Изменения в nginx 0.7.55 06.05.2009 + + *) Исправление: параметры http_XXX в директивах proxy_cache_use_stale и + fastcgi_cache_use_stale не работали. + + *) Исправление: fastcgi кэш не кэшировал ответы, состоящие только из + заголовка. + + *) Исправление: ошибки "select() failed (9: Bad file descriptor)" в + nginx/Unix и "select() failed (10038: ...)" в nginx/Windows. + + *) Исправление: при использовании директивы debug_connection в рабочем + процессе мог произойти segmentation fault; ошибка появилась в 0.7.54. + + *) Исправление: в сборке модуля ngx_http_image_filter_module. + + *) Исправление: файлы больше 2G не передавались с использованием + $r->sendfile. + Спасибо Максиму Дунину. + + +Изменения в nginx 0.7.54 01.05.2009 + + *) Добавление: модуль ngx_http_image_filter_module. + + *) Добавление: директивы proxy_ignore_headers и fastcgi_ignore_headers. + + *) Исправление: при использовании переменных "open_file_cache_errors + on" в рабочем процессе мог произойти segmentation fault; ошибка + появилась в 0.7.53. + + *) Исправление: директива "port_in_redirect off" не работала; ошибка + появилась в 0.7.39. + + *) Исправление: улучшение обработки ошибок метода select. + + *) Исправление: ошибки "select() failed (10022: ...)" в nginx/Windows. + + *) Исправление: в текстовых сообщениях об ошибках в nginx/Windows; + ошибка появилась в 0.7.53. + + +Изменения в nginx 0.7.53 27.04.2009 + + *) Изменение: теперь лог, указанный в --error-log-path, создаётся с + самого начала работы. + + *) Добавление: теперь ошибки и предупреждения при старте записываются в + error_log и выводятся на stderr. + + *) Добавление: при сборке с пустым параметром --prefix= nginx + использует как префикс каталог, в котором он был запущен. + + *) Добавление: ключ -p. + + *) Добавление: ключ -s на Unix-платформах. + + *) Добавление: ключи -? и -h. + Спасибо Jerome Loyet. + + *) Добавление: теперь ключи можно задавать в сжатой форме. + + *) Исправление: nginx/Windows не работал, если файл конфигурации был + задан ключом -c. + + *) Исправление: при использовании директив proxy_store, fastcgi_store, + proxy_cache или fastcgi_cache временные файлы могли не удаляться. + Спасибо Максиму Дунину. + + *) Исправление: в заголовке Auth-Method запроса серверу аутентификации + почтового прокси-сервера передавалось неверное значение; ошибка + появилась в 0.7.34. + Спасибо Simon Lecaille. + + *) Исправление: при логгировании на Linux не писались текстовые + описания системных ошибок; ошибка появилась в 0.7.45. + + *) Исправление: директива fastcgi_cache_min_uses не работала. + Спасибо Андрею Воробьёву. + + +Изменения в nginx 0.7.52 20.04.2009 + + *) Добавление: первая бинарная версия под Windows. + + *) Исправление: корректная обработка метода HEAD при кэшировании. + + *) Исправление: корректная обработка строк "If-Modified-Since", + "If-Range" и им подобных в заголовке запроса клиента при кэшировании. + + *) Исправление: теперь строки "Set-Cookie" и "P3P" скрываются в + заголовке ответа для закэшированных ответов. + + *) Исправление: если nginx был собран с модулем ngx_http_perl_module и + perl поддерживал потоки, то при выходе основного процесса могла + выдаваться ошибка "panic: MUTEX_LOCK". + + *) Исправление: nginx не собирался с параметром --without-http-cache; + ошибка появилась в 0.7.48. + + *) Исправление: nginx не собирался на платформах, отличных от i386, + amd64, sparc и ppc; ошибка появилась в 0.7.42. + + +Изменения в nginx 0.7.51 12.04.2009 + + *) Добавление: директива try_files поддерживает код ответа в последнем + параметре. + + *) Добавление: теперь в директиве return можно использовать любой код + ответа. + + *) Исправление: директива error_page делала внешний редирект без строки + запроса; ошибка появилась в 0.7.44. + + *) Исправление: если сервера слушали на нескольких явно описанных + адресах, то виртуальные сервера могли не работать; ошибка появилась + в 0.7.39. + + +Изменения в nginx 0.7.50 06.04.2009 + + *) Исправление: переменные $arg_... не работали; ошибка появилась в + 0.7.49. + + +Изменения в nginx 0.7.49 06.04.2009 + + *) Исправление: при использовании переменных $arg_... в рабочем + процессе мог произойти segmentation fault; ошибка появилась в 0.7.48. + + +Изменения в nginx 0.7.48 06.04.2009 + + *) Добавление: директива proxy_cache_key. + + *) Исправление: теперь nginx учитывает при кэшировании строки + "X-Accel-Expires", "Expires" и "Cache-Control" в заголовке ответа + бэкенда. + + *) Исправление: теперь nginx кэширует только ответы на запросы GET. + + *) Исправление: директива fastcgi_cache_key не наследовалась. + + *) Исправление: переменные $arg_... не работали с SSI-подзапросами. + Спасибо Максиму Дунину. + + *) Исправление: nginx не собирался с библиотекой uclibc. + Спасибо Timothy Redaelli. + + *) Исправление: nginx не собирался на OpenBSD; ошибка появилась + в 0.7.46. + + +Изменения в nginx 0.7.47 01.04.2009 + + *) Исправление: nginx не собирался на FreeBSD 6 и более ранних версиях; + ошибка появилась в 0.7.46. + + *) Исправление: nginx не собирался на MacOSX; ошибка появилась в 0.7.46. + + *) Исправление: если использовался параметр max_size, то cache manager + мог удалить весь кэш; ошибка появилась в 0.7.46. + + *) Изменение: в рабочем процессе мог произойти segmentation fault, если + директивы proxy_cache/fastcgi_cache и proxy_cache_valid/ + fastcgi_cache_valid не были заданы на одном уровне; ошибка появилась + в 0.7.46. + + *) Исправление: в рабочем процессе мог произойти segmentation fault при + перенаправлении запроса проксированному или FastCGI-серверу с + помощью error_page или try_files; ошибка появилась в 0.7.44. + + +Изменения в nginx 0.7.46 30.03.2009 + + *) Исправление: архив предыдущего релиза был неверным. + + +Изменения в nginx 0.7.45 30.03.2009 + + *) Изменение: теперь директивы proxy_cache и proxy_cache_valid можно + задавать на разных уровнях. + + *) Изменение: параметр clean_time в директиве proxy_cache_path удалён. + + *) Добавление: параметр max_size в директиве proxy_cache_path. + + *) Добавление: предварительная поддержка кэширования в модуле + ngx_http_fastcgi_module. + + *) Добавление: теперь при ошибках выделения в разделяемой памяти в логе + указываются названия директивы и зоны. + + *) Исправление: директива "add_header last-modified ''" не удаляла в + заголовке ответа строку "Last-Modified"; ошибка появилась в 0.7.44. + + *) Исправление: в директиве auth_basic_user_file не работал + относительный путь, заданный строкой без переменных; ошибка + появилась в 0.7.44. + Спасибо Jerome Loyet. + + *) Исправление: в директиве alias, заданной переменными без ссылок на + выделения в регулярных выражениях; ошибка появилась в 0.7.42. + + +Изменения в nginx 0.7.44 23.03.2009 + + *) Добавление: предварительная поддержка кэширования в модуле + ngx_http_proxy_module. + + *) Добавление: параметр --with-pcre в configure. + + *) Добавление: теперь директива try_files может быть использована на + уровне server. + + *) Исправление: директива try_files неправильно обрабатывала строку + запроса в последнем параметре. + + *) Исправление: директива try_files могла неверно тестировать каталоги. + + *) Исправление: если для пары адрес:порт описан только один сервер, то + выделения в регулярных выражениях в директиве server_name не + работали. + + +Изменения в nginx 0.7.43 18.03.2009 + + *) Исправление: запрос обрабатывался неверно, если директива root + использовала переменные; ошибка появилась в 0.7.42. + + *) Исправление: если сервер слушал на адресах типа "*", то значение + переменной $server_addr было "0.0.0.0"; ошибка появилась в 0.7.36. + + +Изменения в nginx 0.7.42 16.03.2009 + + *) Изменение: ошибка "Invalid argument", возвращаемая + setsockopt(TCP_NODELAY) на Solaris, теперь игнорируется. + + *) Изменение: при отсутствии файла, указанного в директиве + auth_basic_user_file, теперь возвращается ошибка 403 вместо 500. + + *) Добавление: директива auth_basic_user_file поддерживает переменные. + Спасибо Кириллу Коринскому. + + *) Добавление: директива listen поддерживает параметр ipv6only. + Спасибо Zhang Hua. + + *) Исправление: в директиве alias со ссылками на выделения в регулярных + выражениях; ошибка появилась в 0.7.40. + + *) Исправление: совместимость с Tru64 UNIX. + Спасибо Dustin Marquess. + + *) Исправление: nginx не собирался без библиотеки PCRE; ошибка + появилась в 0.7.41. + + +Изменения в nginx 0.7.41 11.03.2009 + + *) Исправление: в рабочем процессе мог произойти segmentation fault, + если в server_name или location были выделения в регулярных + выражениях; ошибка появилась в 0.7.40. + Спасибо Владимиру Сопоту. + + +Изменения в nginx 0.7.40 09.03.2009 + + *) Добавление: директива location поддерживает выделения в регулярных + выражениях. + + *) Добавление: директиву alias с ссылками на выделения в регулярных + выражениях можно использовать внутри location'а, заданного + регулярным выражением с выделениями. + + *) Добавление: директива server_name поддерживает выделения в + регулярных выражениях. + + *) Изменение: модуль ngx_http_autoindex_module не показывал последний + слэш для каталогов на файловой системе XFS; ошибка появилась в + 0.7.15. + Спасибо Дмитрию Кузьменко. + + +Изменения в nginx 0.7.39 02.03.2009 + + *) Исправление: при включённом сжатии большие ответы с использованием + SSI могли зависать; ошибка появилась в 0.7.28. + Спасибо Артёму Бохану. + + *) Исправление: при использовании коротких статических вариантов в + директиве try_files в рабочем процессе мог произойти segmentation + fault. + + +Изменения в nginx 0.7.38 23.02.2009 + + *) Добавление: логгирование ошибок аутентификации. + + *) Исправление: имя/пароль, заданные в auth_basic_user_file, + игнорировались после нечётного числа пустых строк. + Спасибо Александру Загребину. + + *) Исправление: при использовании длинного пути в unix domain сокете в + главном процессе происходил segmentation fault; ошибка появилась в + 0.7.36. + + +Изменения в nginx 0.7.37 21.02.2009 + + *) Исправление: директивы, использующие upstream'ы, не работали; ошибка + появилась в 0.7.36. + + +Изменения в nginx 0.7.36 21.02.2009 + + *) Добавление: предварительная поддержка IPv6; директива listen модуля + HTTP поддерживает IPv6. + + *) Исправление: переменная $ancient_browser не работала для браузеров, + заданных директивами modern_browser. + + +Изменения в nginx 0.7.35 16.02.2009 + + *) Исправление: директива ssl_engine не использовала SSL-акселератор + для асимметричных шифров. + Спасибо Marcin Gozdalik. + + *) Исправление: директива try_files выставляла MIME-type, исходя из + расширения первоначального запроса. + + *) Исправление: в директивах server_name, valid_referers и map + неправильно обрабатывались имена вида "*domain.tld", если + использовались маски вида ".domain.tld" и ".subdomain.domain.tld"; + ошибка появилась в 0.7.9. + + +Изменения в nginx 0.7.34 10.02.2009 + + *) Добавление: параметр off в директиве if_modified_since. + + *) Добавление: теперь после команды XCLIENT nginx посылает команду + HELO/EHLO. + Спасибо Максиму Дунину. + + *) Добавление: поддержка Microsoft-специфичного режима + "AUTH LOGIN with User Name" в почтовом прокси-сервере. + Спасибо Максиму Дунину. + + *) Исправление: в директиве rewrite, возвращающей редирект, старые + аргументы присоединялись к новым через символ "?" вместо "&"; + ошибка появилась в 0.1.18. + Спасибо Максиму Дунину. + + *) Исправление: nginx не собирался на AIX. + + +Изменения в nginx 0.7.33 02.02.2009 + + *) Исправление: если на запрос с телом возвращался редирект, то ответ + мог быть двойным при использовании методов epoll или rtsig. + Спасибо Eden Li. + + *) Исправление: для некоторых типов редиректов в переменной + $sent_http_location было пустое значение. + + *) Исправление: при использовании директивы resolver в SMTP + прокси-сервере в рабочем процессе мог произойти segmentation fault. + + +Изменения в nginx 0.7.32 26.01.2009 + + *) Добавление: теперь в директиве try_files можно явно указать проверку + каталога. + + *) Исправление: fastcgi_store не всегда сохранял файлы. + + *) Исправление: в гео-диапазонах. + + *) Исправление: ошибки выделения больших блоков в разделяемой памяти, + если nginx был собран без отладки. + Спасибо Андрею Квасову. + + +Изменения в nginx 0.7.31 19.01.2009 + + *) Изменение: теперь директива try_files проверяет только файлы, + игнорируя каталоги. + + *) Добавление: директива fastcgi_split_path_info. + + *) Исправления в поддержке строки "Expect" в заголовке запроса. + + *) Исправления в гео-диапазонах. + + *) Исправление: при отсутствии ответа ngx_http_memcached_module + возвращал в теле ответа строку "END" вместо 404-ой страницы по + умолчанию; ошибка появилась в 0.7.18. + Спасибо Максиму Дунину. + + *) Исправление: при проксировании SMPT nginx выдавал сообщение + "250 2.0.0 OK" вместо "235 2.0.0 OK"; ошибка появилась в 0.7.22. + Спасибо Максиму Дунину. + + +Изменения в nginx 0.7.30 24.12.2008 + + *) Исправление: в рабочем процессе происходил segmentation fault, если + в директивах fastcgi_pass или proxy_pass использовались переменные и + имя хоста должно было резолвиться; ошибка появилась в 0.7.29. + + +Изменения в nginx 0.7.29 24.12.2008 + + *) Исправление: директивы fastcgi_pass и proxy_pass не поддерживали + переменные при использовании unix domain сокетов. + + *) Исправления в обработке подзапросов; ошибки появились в 0.7.25. + + *) Исправление: ответ "100 Continue" выдавался для запросов версии + HTTP/1.0; + Спасибо Максиму Дунину. + + *) Исправление: в выделении памяти в модуле ngx_http_gzip_filter_module + под Cygwin. + + +Изменения в nginx 0.7.28 22.12.2008 + + *) Изменение: в выделении памяти в модуле ngx_http_gzip_filter_module. + + *) Изменение: значения по умолчанию для директивы gzip_buffers изменены + с 4 4k/8k на 32 4k или 16 8k. + + +Изменения в nginx 0.7.27 15.12.2008 + + *) Добавление: директива try_files. + + *) Добавление: директива fastcgi_pass поддерживает переменные. + + *) Добавление: теперь директива geo может брать адрес из переменной. + Спасибо Андрею Нигматулину. + + *) Добавление: теперь модификатор location'а можно указывать без + пробела перед названием. + + *) Добавление: переменная $upstream_response_length. + + *) Исправление: теперь директива add_header не добавляет пустое + значение. + + *) Исправление: при запросе файла нулевой длины nginx закрывал + соединение, ничего не передав; ошибка появилась в 0.7.25. + + *) Исправление: метод MOVE не мог перемещать файл в несуществующий + каталог. + + *) Исправление: если в сервере не был описан ни один именованный + location, но такой location использовался в директиве error_page, то + в рабочем процессе происходил segmentation fault. + Спасибо Сергею Боченкову. + + +Изменения в nginx 0.7.26 08.12.2008 + + *) Исправление: в обработке подзапросов; ошибка появилась в 0.7.25. + + +Изменения в nginx 0.7.25 08.12.2008 + + *) Изменение: в обработке подзапросов. + + *) Изменение: теперь разрешаются POST'ы без строки "Content-Length" в + заголовке запроса. + + *) Исправление: теперь директивы limit_req и limit_conn указывают + причину запрета запроса. + + *) Исправление: в параметре delete директивы geo. + + +Изменения в nginx 0.7.24 01.12.2008 + + *) Добавление: директива if_modified_since. + + *) Исправление: nginx не обрабатывал ответ FastCGI-сервера, если перед + ответом сервер передавал много сообщений в stderr. + + *) Исправление: переменные "$cookie_..." не работали в SSI and в + перловом модуле. + + +Изменения в nginx 0.7.23 27.11.2008 + + *) Добавление: параметры delete и ranges в директиве geo. + + *) Добавление: ускорение загрузки geo-базы с большим числом значений. + + *) Добавление: уменьшение памяти, необходимой для загрузки geo-базы. + + +Изменения в nginx 0.7.22 20.11.2008 + + *) Добавление: параметр none в директиве smtp_auth. + Спасибо Максиму Дунину. + + *) Добавление: переменные "$cookie_...". + + *) Исправление: директива directio не работала с файловой системой XFS. + + *) Исправление: resolver не понимал большие DNS-ответы. + Спасибо Zyb. + + +Изменения в nginx 0.7.21 11.11.2008 + + *) Изменения в модуле ngx_http_limit_req_module. + + *) Добавление: поддержка EXSLT в модуле ngx_http_xslt_module. + Спасибо Денису Латыпову. + + *) Изменение: совместимость с glibc 2.3. + Спасибо Eric Benson и Максиму Дунину. + + *) Исправление: nginx не запускался на MacOSX 10.4 и более ранних; + ошибка появилась в 0.7.6. + + +Изменения в nginx 0.7.20 10.11.2008 + + *) Изменения в модуле ngx_http_gzip_filter_module. + + *) Добавление: модуль ngx_http_limit_req_module. + + *) Исправление: на платформах sparc и ppc рабочие процессы могли + выходить по сигналу SIGBUS; ошибка появилась в 0.7.3. + Спасибо Максиму Дунину. + + *) Исправление: директивы вида "proxy_pass http://host/some:uri" не + работали; ошибка появилась в 0.7.12. + + *) Исправление: при использовании HTTPS запросы могли завершаться с + ошибкой "bad write retry". + + *) Исправление: модуль ngx_http_secure_link_module не работал внутри + location'ов с именами меньше 3 символов. + + *) Исправление: переменная $server_addr могла не иметь значения. + + Изменения в nginx 0.7.19 13.10.2008 *) Исправление: обновление номера версии. @@ -794,7 +1485,7 @@ *) Добавление: директивы open_file_cache, open_file_cache_retest и open_file_cache_errors. - *) Исправление: утечка сокетов; ошибка появилась в 0.6.7. + *) Исправление: утечки сокетов; ошибка появилась в 0.6.7. *) Исправление: В строку заголовка ответа "Content-Type", указанную в методе $r->send_http_header(), не добавлялась кодировка, указанная в @@ -1763,7 +2454,7 @@ закодированные символы в виде "%XX", то проксируемый запрос передавался незакодированным. - *) Исправление: метод $r->headers_in("Cookie") модуля + *) Исправление: метод $r->header_in("Cookie") модуля ngx_http_perl_module теперь возвращает все строки "Cookie" в заголовке запроса. diff --git a/LICENSE b/LICENSE --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2008 Igor Sysoev + * Copyright (C) 2002-2009 Igor Sysoev * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -21,5 +21,4 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * */ diff --git a/auto/cc/bcc b/auto/cc/bcc --- a/auto/cc/bcc +++ b/auto/cc/bcc @@ -24,7 +24,7 @@ esac # __stdcall #CPU_OPT="$CPU_OPT -ps" # __fastcall -CPU_OPT="$CPU_OPT -pr" +#CPU_OPT="$CPU_OPT -pr" CFLAGS="$CFLAGS $CPU_OPT" @@ -46,7 +46,7 @@ NGX_USE_PCH="-Hu -H=$NGX_OBJS/ngx_config # Win32 GUI mode application -LINK="\$(CC) -laa" +#LINK="\$(CC) -laa" # the resource file diff --git a/auto/cc/gcc b/auto/cc/gcc --- a/auto/cc/gcc +++ b/auto/cc/gcc @@ -158,9 +158,10 @@ case "$NGX_GCC_VER" in 3.* | 4.* ) # we have a lot of the unused function arguments CFLAGS="$CFLAGS -Wno-unused-parameter" - CFLAGS="$CFLAGS -Wno-unused-function" + CFLAGS="$CFLAGS -Wunused-function" CFLAGS="$CFLAGS -Wunused-variable" CFLAGS="$CFLAGS -Wunused-value" + # 4.2.1 shows the warning in wrong places #CFLAGS="$CFLAGS -Wunreachable-code" ;; diff --git a/auto/cc/msvc b/auto/cc/msvc --- a/auto/cc/msvc +++ b/auto/cc/msvc @@ -2,7 +2,9 @@ # Copyright (C) Igor Sysoev -# MSVC 6.0 SP2, MSVC Toolkit 2003 (7.1), MSVC 2005 Express Edition SP1 (8.0) +# MSVC 6.0 SP2 +# MSVC Toolkit 2003 (7.1) +# MSVC 2005 Express Edition SP1 (8.0) # optimizations @@ -49,12 +51,12 @@ case $CPU in ;; esac -# __cdecl, use with OpenSSL, md5 asm, and sha1 asm +# __cdecl, default, must be used with OpenSSL, md5 asm, and sha1 asm #CPU_OPT="$CPU_OPT -Gd" # __stdcall #CPU_OPT="$CPU_OPT -Gz" # __fastcall -CPU_OPT="$CPU_OPT -Gr" +#CPU_OPT="$CPU_OPT -Gr" CFLAGS="$CFLAGS $CPU_OPT" @@ -76,27 +78,34 @@ LINK="\$(CC)" # the link flags CORE_LINK="$CORE_LINK -link -verbose:lib" -if [ $NGX_CC_NAME = msvc7 ]; then - # link with libcmt.lib, multithreaded - LIBC="-MT" -else - # link with msvcrt.dll - LIBC="-MD" -fi +# link with libcmt.lib, multithreaded +LIBC="-MT" +# link with msvcrt.dll +# however, MSVC Toolkit 2003 has no MSVCRT.LIB +#LIBC="-MD" CFLAGS="$CFLAGS $LIBC" +CORE_LIBS="$CORE_LIBS kernel32.lib user32.lib" + # Win32 GUI mode application -CORE_LIBS="$CORE_LIBS kernel32.lib user32.lib" -CORE_LINK="$CORE_LINK -subsystem:windows -entry:mainCRTStartup" +#CORE_LINK="$CORE_LINK -subsystem:windows -entry:mainCRTStartup" # debug +# msvc8 under Wine issues +# Program database manager mismatch; please check your installation if [ $NGX_CC_NAME != msvc8 ]; then CFLAGS="$CFLAGS -Zi" CORE_LINK="$CORE_LINK -debug" fi +# MSVC 2005 supports C99 variadic macros +if [ $NGX_CC_NAME = msvc8 ]; then + have=NGX_HAVE_C99_VARIADIC_MACROS . auto/have +fi + + # precompiled headers CORE_DEPS="$CORE_DEPS $NGX_OBJS/ngx_config.pch" NGX_PCH="$NGX_OBJS/ngx_config.pch" @@ -115,24 +124,13 @@ ngx_binout="-Fe" ngx_objext="obj" ngx_binext=".exe" -if [ "$BMAKE" = nmake ]; then - # MS nmake - - ngx_long_start='@<< - ' - ngx_long_end='<<' - ngx_long_regex_cont=' \ +ngx_long_start='@<< ' - ngx_long_cont=' +ngx_long_end='<<' +ngx_long_regex_cont=' \ ' - -else - # Borland make - - ngx_long_start='@&&| - ' - ngx_long_end='|' -fi +ngx_long_cont=' + ' # MSVC understand / in path #ngx_regex_dirsep='\\' diff --git a/auto/cc/owc b/auto/cc/owc --- a/auto/cc/owc +++ b/auto/cc/owc @@ -71,7 +71,7 @@ NGX_USE_PCH="-fh=$NGX_OBJS/ngx_config.pc # the link flags, built target is NT GUI mode application -CORE_LINK="$CORE_LINK -l=nt_win" +#CORE_LINK="$CORE_LINK -l=nt_win" # the resource file @@ -87,3 +87,17 @@ ngx_binext=".exe" ngx_regex_dirsep='\\' ngx_dirsep="\\" + +ngx_long_start=' ' +ngx_long_end=' ' +ngx_long_regex_cont=' \&\ + ' +ngx_long_cont=' & + ' + +ngx_regex_cont=' \&\ + ' +ngx_cont=' & + ' +ngx_tab=' & + ' diff --git a/auto/cc/sunc b/auto/cc/sunc --- a/auto/cc/sunc +++ b/auto/cc/sunc @@ -45,21 +45,6 @@ fi case "$NGX_MACHINE" in i86pc) - ngx_feature="PAUSE hardware capability bug" - ngx_feature_name= - ngx_feature_run=bug - ngx_feature_incs= - ngx_feature_path= - ngx_feature_libs= - ngx_feature_test='__asm ("pause")' - - . auto/feature - - if [ $ngx_found = yes ]; then - # disable [ PAUSE ] hwcap for Sun Studio 11 - CORE_LINK="$CORE_LINK -Msrc/os/unix/ngx_sunpro_x86.map" - fi - NGX_AUX=" src/os/unix/ngx_sunpro_x86.il" ;; diff --git a/auto/headers b/auto/headers --- a/auto/headers +++ b/auto/headers @@ -2,8 +2,11 @@ # Copyright (C) Igor Sysoev -ngx_include="unistd.h"; . auto/include -ngx_include="inttypes.h"; . auto/include -ngx_include="limits.h"; . auto/include -ngx_include="sys/filio.h"; . auto/include -ngx_include="crypt.h"; . auto/include +ngx_include="unistd.h"; . auto/include +ngx_include="inttypes.h"; . auto/include +ngx_include="limits.h"; . auto/include +ngx_include="sys/filio.h"; . auto/include +ngx_include="sys/param.h"; . auto/include +ngx_include="sys/mount.h"; . auto/include +ngx_include="sys/statvfs.h"; . auto/include +ngx_include="crypt.h"; . auto/include diff --git a/auto/include b/auto/include --- a/auto/include +++ b/auto/include @@ -16,6 +16,7 @@ ngx_found=no cat << END > $NGX_AUTOTEST.c +$NGX_INCLUDE_SYS_PARAM_H #include <$ngx_include> int main() { diff --git a/auto/init b/auto/init --- a/auto/init +++ b/auto/init @@ -43,36 +43,8 @@ fi cat << END > Makefile -build: - \$(MAKE) -f $NGX_MAKEFILE - -install: - \$(MAKE) -f $NGX_MAKEFILE install +default: build clean: rm -rf Makefile $NGX_OBJS - -upgrade: - $NGX_SBIN_PATH -t - - kill -USR2 \`cat $NGX_PID_PATH\` - sleep 1 - test -f $NGX_PID_PATH.oldbin - - kill -QUIT \`cat $NGX_PID_PATH.oldbin\` - -upgrade1: - # upgrade 0.1.x to 0.2+ - - $NGX_SBIN_PATH -t - - cp $NGX_PID_PATH $NGX_PID_PATH.oldbin - - kill -USR2 \`cat $NGX_PID_PATH\` - sleep 1 - test -f $NGX_PID_PATH.oldbin - - cp $NGX_PID_PATH $NGX_PID_PATH.newbin - - kill -QUIT \`cat $NGX_PID_PATH.oldbin\` END diff --git a/auto/install b/auto/install --- a/auto/install +++ b/auto/install @@ -15,6 +15,63 @@ END fi +case ".$NGX_SBIN_PATH" in + ./*) + ;; + + .) + NGX_SBIN_PATH=$NGX_PREFIX/sbin/nginx + ;; + + *) + NGX_SBIN_PATH=$NGX_PREFIX/$NGX_SBIN_PATH + ;; +esac + + +case ".$NGX_CONF_PATH" in + ./*) + ;; + + *) + NGX_CONF_PATH=$NGX_PREFIX/$NGX_CONF_PATH + ;; +esac + + +NGX_CONF_PREFIX=`dirname $NGX_CONF_PATH` + + +case ".$NGX_PID_PATH" in + ./*) + ;; + + *) + NGX_PID_PATH=$NGX_PREFIX/$NGX_PID_PATH + ;; +esac + + +case ".$NGX_ERROR_LOG_PATH" in + ./*) + ;; + + *) + NGX_ERROR_LOG_PATH=$NGX_PREFIX/$NGX_ERROR_LOG_PATH + ;; +esac + + +case ".$NGX_HTTP_LOG_PATH" in + ./*) + ;; + + *) + NGX_HTTP_LOG_PATH=$NGX_PREFIX/$NGX_HTTP_LOG_PATH + ;; +esac + + cat << END >> $NGX_MAKEFILE install: $NGX_OBJS${ngx_dirsep}nginx${ngx_binext} \ @@ -67,3 +124,39 @@ if test -n "\$(DESTDIR)$NGX_ERROR_LOG_PA END fi + + +# create Makefile + +cat << END >> Makefile + +build: + \$(MAKE) -f $NGX_MAKEFILE + +install: + \$(MAKE) -f $NGX_MAKEFILE install + +upgrade: + $NGX_SBIN_PATH -t + + kill -USR2 \`cat $NGX_PID_PATH\` + sleep 1 + test -f $NGX_PID_PATH.oldbin + + kill -QUIT \`cat $NGX_PID_PATH.oldbin\` + +upgrade1: + # upgrade 0.1.x to 0.2+ + + $NGX_SBIN_PATH -t + + cp $NGX_PID_PATH $NGX_PID_PATH.oldbin + + kill -USR2 \`cat $NGX_PID_PATH\` + sleep 1 + test -f $NGX_PID_PATH.oldbin + + cp $NGX_PID_PATH $NGX_PID_PATH.newbin + + kill -QUIT \`cat $NGX_PID_PATH.oldbin\` +END diff --git a/auto/lib/conf b/auto/lib/conf --- a/auto/lib/conf +++ b/auto/lib/conf @@ -4,15 +4,29 @@ if [ $USE_PCRE = YES -o $PCRE != NONE ]; then . auto/lib/pcre/conf + +else + if [ $USE_PCRE = DISABLED -a $HTTP_REWRITE = YES ]; then + +cat << END + +$0: error: the HTTP rewrite module requires the PCRE library. +You can either disable the module by using --without-http_rewrite_module +option or you have to enable the PCRE support. + +END + exit 1 + fi fi + if [ $USE_OPENSSL = YES ]; then . auto/lib/openssl/conf fi if [ $USE_MD5 = YES ]; then - if [ $OPENSSL != NONE -a $OPENSSL != NO ]; then + if [ $USE_OPENSSL = YES ]; then have=NGX_HAVE_OPENSSL_MD5_H . auto/have have=NGX_OPENSSL_MD5 . auto/have MD5=YES @@ -26,7 +40,7 @@ fi if [ $USE_SHA1 = YES ]; then - if [ $OPENSSL != NONE -a $OPENSSL != NO ]; then + if [ $USE_OPENSSL = YES ]; then have=NGX_HAVE_OPENSSL_SHA1_H . auto/have SHA1=YES SHA1_LIB=OpenSSL @@ -45,6 +59,10 @@ if [ $USE_LIBXSLT = YES ]; then . auto/lib/libxslt/conf fi +if [ $USE_LIBGD = YES ]; then + . auto/lib/libgd/conf +fi + if [ $USE_PERL = YES ]; then . auto/lib/perl/conf fi diff --git a/auto/lib/google-perftools/conf b/auto/lib/google-perftools/conf --- a/auto/lib/google-perftools/conf +++ b/auto/lib/google-perftools/conf @@ -30,4 +30,15 @@ fi if [ $ngx_found = yes ]; then CORE_LIBS="$CORE_LIBS $ngx_feature_libs" + +else + +cat << END + +$0: error: the Google perftool module requires the Google perftools +library. You can either do not enable the module or install the library. + +END + + exit 1 fi diff --git a/auto/lib/libgd/conf b/auto/lib/libgd/conf new file mode 100644 --- /dev/null +++ b/auto/lib/libgd/conf @@ -0,0 +1,82 @@ + +# Copyright (C) Igor Sysoev + + + ngx_feature="GD library" + ngx_feature_name= + ngx_feature_run=no + ngx_feature_incs="#include " + ngx_feature_path= + ngx_feature_libs="-lgd" + ngx_feature_test="gdImagePtr img = gdImageCreateFromGifPtr(1, NULL);" + . auto/feature + + +if [ $ngx_found = no ]; then + + # FreeBSD port + + ngx_feature="GD library in /usr/local/" + ngx_feature_path="/usr/local/include" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lgd" + else + ngx_feature_libs="-L/usr/local/lib -lgd" + fi + + . auto/feature +fi + + +if [ $ngx_found = no ]; then + + # NetBSD port + + ngx_feature="GD library in /usr/pkg/" + ngx_feature_path="/usr/pkg/include/" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lgd" + else + ngx_feature_libs="-L/usr/pkg/lib -lgd" + fi + + . auto/feature +fi + + +if [ $ngx_found = no ]; then + + # MacPorts + + ngx_feature="GD library in /opt/local/" + ngx_feature_path="/opt/local/include" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lgd" + else + ngx_feature_libs="-L/opt/local/lib -lgd" + fi + + . auto/feature +fi + + +if [ $ngx_found = yes ]; then + + CORE_INCS="$CORE_INCS $ngx_feature_path" + CORE_LIBS="$CORE_LIBS $ngx_feature_libs" + +else + +cat << END + +$0: error: the HTTP image filter module requires the GD library. +You can either do not enable the module or install the libraries. + +END + + exit 1 + +fi diff --git a/auto/lib/libxslt/conf b/auto/lib/libxslt/conf --- a/auto/lib/libxslt/conf +++ b/auto/lib/libxslt/conf @@ -88,3 +88,68 @@ END exit 1 fi + + + ngx_feature="libexslt" + ngx_feature_name=NGX_HAVE_EXSLT + ngx_feature_run=no + ngx_feature_incs="#include " + ngx_feature_path="/usr/include/libxml2" + ngx_feature_libs="-lexslt" + ngx_feature_test="exsltRegisterAll();" + . auto/feature + +if [ $ngx_found = no ]; then + + # FreeBSD port + + ngx_feature="libexslt in /usr/local/" + ngx_feature_path="/usr/local/include/libxml2 /usr/local/include" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lexslt" + else + ngx_feature_libs="-L/usr/local/lib -lexslt" + fi + + . auto/feature +fi + + +if [ $ngx_found = no ]; then + + # NetBSD port + + ngx_feature="libexslt in /usr/pkg/" + ngx_feature_path="/usr/pkg/include/libxml2 /usr/local/include" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lexslt" + else + ngx_feature_libs="-L/usr/pkg/lib -lexslt" + fi + + . auto/feature +fi + + +if [ $ngx_found = no ]; then + + # MacPorts + + ngx_feature="libexslt in /opt/local/" + ngx_feature_path="/opt/local/include/libxml2 /opt/local/include" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lexslt" + else + ngx_feature_libs="-L/opt/local/lib -lexslt" + fi + + . auto/feature +fi + + +if [ $ngx_found = yes ]; then + CORE_LIBS="$CORE_LIBS -lexslt" +fi diff --git a/auto/lib/md5/conf b/auto/lib/md5/conf --- a/auto/lib/md5/conf +++ b/auto/lib/md5/conf @@ -4,7 +4,7 @@ if [ $MD5 != NONE ]; then - if grep MD5_Init $MD5/md5.h >/dev/null; then + if grep MD5_Init $MD5/md5.h 2>&1 >/dev/null; then # OpenSSL md5 OPENSSL_MD5=YES have=NGX_HAVE_OPENSSL_MD5 . auto/have @@ -46,30 +46,30 @@ else if [ "$NGX_PLATFORM" != win32 ]; then - MD5=NO + MD5=NO - # Solaris 8/9 + # FreeBSD, Solaris 10 - ngx_feature="rsaref md5 library" - ngx_feature_name= - ngx_feature_run=no - ngx_feature_incs="#include " - ngx_feature_path= - ngx_feature_libs="-lmd5" - ngx_feature_test="MD5_CTX md5; MD5Init(&md5)" - . auto/feature + ngx_feature="system md library" + ngx_feature_name= + ngx_feature_run=no + ngx_feature_incs="#include " + ngx_feature_path= + ngx_feature_libs="-lmd" + ngx_feature_test="MD5_CTX md5; MD5Init(&md5)" + . auto/feature - ngx_md5_lib="system md5" + ngx_md5_lib="system md" if [ $ngx_found = no ]; then - # FreeBSD + # Solaris 8/9 - ngx_feature="rsaref md library" - ngx_feature_libs="-lmd" + ngx_feature="system md5 library" + ngx_feature_libs="-lmd5" . auto/feature - ngx_md5_lib="system md" + ngx_md5_lib="system md5" fi if [ $ngx_found = no ]; then @@ -94,6 +94,18 @@ else CORE_LIBS="$CORE_LIBS $ngx_feature_libs" MD5=YES MD5_LIB=$ngx_md5_lib + else + +cat << END + +$0: error: the HTTP cache module requires md5 functions +from OpenSSL library. You can either disable the module by using +--without-http-cache option, or install the OpenSSL library into the system, +or build the OpenSSL library statically from the source with nginx by using +--with-http_ssl_module --with-openssl= options. + +END + exit 1 fi fi diff --git a/auto/lib/md5/make b/auto/lib/md5/make --- a/auto/lib/md5/make +++ b/auto/lib/md5/make @@ -7,16 +7,19 @@ case "$NGX_CC_NAME" in msvc*) ngx_makefile=makefile.msvc ngx_opt="CPU_OPT=\"$CPU_OPT\" LIBC=$LIBC MD5_ASM=$MD5_ASM" + ngx_md5="MD5=\"$MD5\"" ;; owc*) ngx_makefile=makefile.owc ngx_opt="CPU_OPT=\"$CPU_OPT\"" + ngx_md5=`echo MD5=\"$MD5\" | sed -e "s/\//$ngx_regex_dirsep/g"` ;; bcc) ngx_makefile=makefile.bcc ngx_opt="-DCPU_OPT=\"$CPU_OPT\" -DMD5_ASM=$MD5_ASM" + ngx_md5=`echo \-DMD5=\"$MD5\" | sed -e "s/\//$ngx_regex_dirsep/g"` ;; esac @@ -28,14 +31,10 @@ done=NO case "$NGX_PLATFORM" in win32) - cp auto/lib/md5/$ngx_makefile $MD5 - cat << END >> $NGX_MAKEFILE `echo "$MD5/md5.lib: $NGX_MAKEFILE" | sed -e "s/\//$ngx_regex_dirsep/g"` - cd `echo $MD5 | sed -e "s/\//$ngx_regex_dirsep/g"` - \$(MAKE) -f $ngx_makefile $ngx_opt - cd ..\\..\\.. + \$(MAKE) -f auto/lib/md5/$ngx_makefile $ngx_opt $ngx_md5 END diff --git a/auto/lib/md5/makefile.bcc b/auto/lib/md5/makefile.bcc --- a/auto/lib/md5/makefile.bcc +++ b/auto/lib/md5/makefile.bcc @@ -7,12 +7,14 @@ CFLAGS = -q -O2 -tWM $(CPU_OPT) -DL_ENDI !if "$(MD5_ASM)" == "YES" md5.lib: + cd $(MD5) bcc32 -c $(CFLAGS) -DMD5_ASM md5_dgst.c tlib md5.lib +md5_dgst.obj +"asm\m-win32.obj" !else md5.lib: + cd $(MD5) bcc32 -c $(CFLAGS) md5_dgst.c tlib md5.lib +md5_dgst.obj diff --git a/auto/lib/md5/makefile.msvc b/auto/lib/md5/makefile.msvc --- a/auto/lib/md5/makefile.msvc +++ b/auto/lib/md5/makefile.msvc @@ -7,12 +7,14 @@ CFLAGS = -nologo -O2 -Ob1 -Oi -Gs $(LIBC !IF "$(MD5_ASM)" == "YES" md5.lib: + cd $(MD5) cl -c $(CFLAGS) -D MD5_ASM md5_dgst.c link -lib -out:md5.lib md5_dgst.obj asm/m-win32.obj !ELSE md5.lib: + cd $(MD5) cl -c $(CFLAGS) md5_dgst.c link -lib -out:md5.lib md5_dgst.obj diff --git a/auto/lib/md5/makefile.owc b/auto/lib/md5/makefile.owc --- a/auto/lib/md5/makefile.owc +++ b/auto/lib/md5/makefile.owc @@ -5,5 +5,6 @@ CFLAGS = -zq -bt=nt -bm -ot -op -oi -oe -s $(CPU_OPT) md5.lib: + cd $(MD5) wcl386 -c $(CFLAGS) -dL_ENDIAN md5_dgst.c wlib -n md5.lib md5_dgst.obj diff --git a/auto/lib/openssl/conf b/auto/lib/openssl/conf --- a/auto/lib/openssl/conf +++ b/auto/lib/openssl/conf @@ -3,24 +3,36 @@ if [ $OPENSSL != NONE ]; then - CORE_INCS="$CORE_INCS $OPENSSL/include" case "$CC" in + + cl | bcc32) + have=NGX_OPENSSL . auto/have + have=NGX_SSL . auto/have + + CFLAGS="$CFLAGS -DNO_SYS_TYPES_H" + + CORE_INCS="$CORE_INCS $OPENSSL/openssl/include" + CORE_DEPS="$CORE_DEPS $OPENSSL/openssl/include/openssl/ssl.h" + CORE_LIBS="$CORE_LIBS $OPENSSL/openssl/lib/ssleay32.lib" + CORE_LIBS="$CORE_LIBS $OPENSSL/openssl/lib/libeay32.lib" + + # libeay32.lib requires gdi32.lib + CORE_LIBS="$CORE_LIBS gdi32.lib" + ;; + *) have=NGX_OPENSSL . auto/have have=NGX_SSL . auto/have - LINK_DEPS="$LINK_DEPS $OPENSSL/libssl.a $OPENSSL/libcrypto.a" - CORE_LIBS="$CORE_LIBS $OPENSSL/libssl.a $OPENSSL/libcrypto.a" + + CORE_INCS="$CORE_INCS $OPENSSL/openssl/include" + CORE_DEPS="$CORE_DEPS $OPENSSL/openssl/include/openssl/ssl.h" + CORE_LIBS="$CORE_LIBS $OPENSSL/openssl/lib/libssl.a" + CORE_LIBS="$CORE_LIBS $OPENSSL/openssl/lib/libcrypto.a" + CORE_LIBS="$CORE_LIBS $NGX_LIBDL" ;; esac - case "$NGX_SYSTEM" in - SunOS|Linux) - CORE_LIBS="$CORE_LIBS -ldl" - ;; - esac - - else case "$NGX_PLATFORM" in @@ -36,8 +48,6 @@ else # libeay32.lib requires gdi32.lib CORE_LIBS="$CORE_LIBS gdi32.lib" - # OpenSSL 0.8's libeay32.lib requires advapi32.lib - CORE_LIBS="$CORE_LIBS advapi32.lib" ;; *) @@ -54,14 +64,19 @@ else if [ $ngx_found = yes ]; then have=NGX_SSL . auto/have - CORE_LIBS="$CORE_LIBS $ngx_feature_libs" + CORE_LIBS="$CORE_LIBS $ngx_feature_libs $NGX_LIBDL" OPENSSL=YES + else + +cat << END - case "$NGX_SYSTEM" in - SunOS) - CORE_LIBS="$CORE_LIBS -ldl" - ;; - esac +$0: error: SSL modules require the OpenSSL library. +You can either do not enable the modules, or install the OpenSSL library +into the system, or build the OpenSSL library statically from the source +with nginx by using --with-openssl= option. + +END + exit 1 fi ;; diff --git a/auto/lib/openssl/make b/auto/lib/openssl/make --- a/auto/lib/openssl/make +++ b/auto/lib/openssl/make @@ -2,26 +2,62 @@ # Copyright (C) Igor Sysoev -if test -n "$OPENSSL_OPT"; then - NGX_OPENSSL_CONFIG="./Configure \"$OPENSSL_OPT\"" -else - NGX_OPENSSL_CONFIG="./config" -fi +case "$CC" in + + cl) + + cat << END >> $NGX_MAKEFILE + +$OPENSSL/openssl/include/openssl/ssl.h: $NGX_MAKEFILE + \$(MAKE) -f auto/lib/openssl/makefile.msvc \ + OPENSSL="$OPENSSL" OPENSSL_OPT="$OPENSSL_OPT" -case $USE_THREADS in - NO) NGX_OPENSSL_CONFIG="$NGX_OPENSSL_CONFIG no-threads" ;; - *) NGX_OPENSSL_CONFIG="$NGX_OPENSSL_CONFIG threads" ;; -esac +END + + ;; -case "$NGX_PLATFORM" in - *) + bcc32) + + ngx_opt=`echo "-DOPENSSL=\"$OPENSSL\" -DOPENSSL_OPT=\"$OPENSSL_OPT\"" \ + | sed -e "s/\//$ngx_regex_dirsep/g"` + cat << END >> $NGX_MAKEFILE -$OPENSSL/libssl.a: +`echo "$OPENSSL\\openssl\\lib\\libeay32.lib: \ + $OPENSSL\\openssl\\include\\openssl\\ssl.h" \ + | sed -e "s/\//$ngx_regex_dirsep/g"` + +`echo "$OPENSSL\\openssl\\lib\\ssleay32.lib: \ + $OPENSSL\\openssl\\include\\openssl\\ssl.h" \ + | sed -e "s/\//$ngx_regex_dirsep/g"` + +`echo "$OPENSSL\\openssl\\include\\openssl\\ssl.h: $NGX_MAKEFILE" \ + | sed -e "s/\//$ngx_regex_dirsep/g"` + \$(MAKE) -f auto/lib/openssl/makefile.bcc $ngx_opt + +END + + ;; + + *) + case $USE_THREADS in + NO) OPENSSL_OPT="$OPENSSL_OPT no-threads" ;; + *) OPENSSL_OPT="$OPENSSL_OPT threads" ;; + esac + + case $OPENSSL in + /*) ngx_prefix="$OPENSSL/openssl" ;; + *) ngx_prefix="$PWD/$OPENSSL/openssl" ;; + esac + + cat << END >> $NGX_MAKEFILE + +$OPENSSL/openssl/include/openssl/ssl.h: $NGX_MAKEFILE cd $OPENSSL \\ && \$(MAKE) clean \\ - && $NGX_OPENSSL_CONFIG no-shared \\ - && \$(MAKE) + && ./config --prefix=$ngx_prefix no-shared $OPENSSL_OPT \\ + && \$(MAKE) \\ + && \$(MAKE) install END diff --git a/auto/lib/openssl/makefile.bcc b/auto/lib/openssl/makefile.bcc new file mode 100644 --- /dev/null +++ b/auto/lib/openssl/makefile.bcc @@ -0,0 +1,18 @@ + +# Copyright (C) Igor Sysoev + + +all: + cd $(OPENSSL) + + perl Configure BC-32 no-shared --prefix=openssl -DNO_SYS_TYPES_H \ + $(OPENSSL_OPT) + + ms\do_nasm + + $(MAKE) -f ms\bcb.mak + $(MAKE) -f ms\bcb.mak install + + # Borland's make does not expand "[ch]" in + # copy "inc32\openssl\*.[ch]" "openssl\include\openssl" + copy inc32\openssl\*.h openssl\include\openssl diff --git a/auto/lib/openssl/makefile.msvc b/auto/lib/openssl/makefile.msvc new file mode 100644 --- /dev/null +++ b/auto/lib/openssl/makefile.msvc @@ -0,0 +1,14 @@ + +# Copyright (C) Igor Sysoev + + +all: + cd $(OPENSSL) + + perl Configure VC-WIN32 no-shared --prefix=openssl -DNO_SYS_TYPES_H \ + $(OPENSSL_OPT) + + ms\do_ms + + $(MAKE) -f ms\nt.mak + $(MAKE) -f ms\nt.mak install diff --git a/auto/lib/pcre/conf b/auto/lib/pcre/conf --- a/auto/lib/pcre/conf +++ b/auto/lib/pcre/conf @@ -161,6 +161,18 @@ else CORE_INCS="$CORE_INCS $ngx_feature_path" CORE_LIBS="$CORE_LIBS $ngx_feature_libs" PCRE=YES + else + +cat << END + +$0: error: the HTTP rewrite module requires the PCRE library. +You can either disable the module by using --without-http_rewrite_module +option, or install the PCRE library into the system, or build the PCRE library +statically from the source with nginx by using --with-pcre= option. + +END + exit 1 + fi fi diff --git a/auto/lib/pcre/make b/auto/lib/pcre/make --- a/auto/lib/pcre/make +++ b/auto/lib/pcre/make @@ -7,16 +7,19 @@ case "$NGX_CC_NAME" in msvc*) ngx_makefile=makefile.msvc ngx_opt="CPU_OPT=\"$CPU_OPT\" LIBC=$LIBC" + ngx_pcre="PCRE=\"$PCRE\"" ;; owc*) ngx_makefile=makefile.owc ngx_opt="CPU_OPT=\"$CPU_OPT\"" + ngx_pcre=`echo PCRE=\"$PCRE\" | sed -e "s/\//$ngx_regex_dirsep/g"` ;; bcc) ngx_makefile=makefile.bcc ngx_opt="-DCPU_OPT=\"$CPU_OPT\"" + ngx_pcre=`echo \-DPCRE=\"$PCRE\" | sed -e "s/\//$ngx_regex_dirsep/g"` ;; esac @@ -25,26 +28,14 @@ esac case "$NGX_PLATFORM" in win32) - cp auto/lib/pcre/patch.pcre.in $PCRE - cp auto/lib/pcre/patch.pcre.in.owc $PCRE - cp auto/lib/pcre/patch.config.in $PCRE - cp auto/lib/pcre/patch.pcre.c $PCRE - cp auto/lib/pcre/$ngx_makefile $PCRE - - ngx_pcre=`echo $PCRE | sed -e "s/\//$ngx_regex_dirsep/g"` cat << END >> $NGX_MAKEFILE -`echo "$PCRE/pcre.h: $NGX_MAKEFILE" | sed -e "s/\//$ngx_regex_dirsep/g"` - cd $ngx_pcre - \$(MAKE) -f $ngx_makefile pcre.h - cd ..\\..\\.. +`echo "$PCRE/pcre.lib: $NGX_MAKEFILE" | sed -e "s/\//$ngx_regex_dirsep/g"` + \$(MAKE) -f auto/lib/pcre/$ngx_makefile $ngx_pcre $ngx_opt - -`echo "$PCRE/pcre.lib: $PCRE/pcre.h" | sed -e "s/\//$ngx_regex_dirsep/g"` - cd $ngx_pcre - \$(MAKE) -f $ngx_makefile $ngx_opt - cd ..\\..\\.. +`echo "$PCRE/pcre.h:" | sed -e "s/\//$ngx_regex_dirsep/g"` + \$(MAKE) -f auto/lib/pcre/$ngx_makefile $ngx_pcre pcre.h END @@ -61,7 +52,6 @@ END && CC="\$(CC)" CFLAGS="$PCRE_OPT" \\ ./configure --disable-shared - $PCRE/.libs/libpcre.a: $PCRE/Makefile cd $PCRE \\ && \$(MAKE) libpcre.la diff --git a/auto/lib/pcre/makefile.bcc b/auto/lib/pcre/makefile.bcc --- a/auto/lib/pcre/makefile.bcc +++ b/auto/lib/pcre/makefile.bcc @@ -3,19 +3,23 @@ CFLAGS = -q -O2 -tWM -w-8004 $(CPU_OPT) -PCREFLAGS = -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10 +PCREFLAGS = -DHAVE_CONFIG_H -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10 -pcre.lib: pcre.h - bcc32 -q -edftables dftables.c +pcre.lib: + cd $(PCRE) + + bcc32 -c $(CFLAGS) -I. $(PCREFLAGS) pcre_*.c - dftables > chartables.c + > pcre.lst + for %n in (*.obj) do @echo +%n & >> pcre.lst + echo + >> pcre.lst - bcc32 -c $(CFLAGS) $(PCREFLAGS) maketables.c get.c study.c pcre.c - - tlib pcre.lib +maketables.obj +get.obj +study.obj +pcre.obj + tlib pcre.lib @pcre.lst pcre.h: - patch -o pcre.h pcre.in patch.pcre.in - patch -o config.h config.in patch.config.in - patch -o pcre.c pcre.c patch.pcre.c + cd $(PCRE) + + copy /y pcre.h.generic pcre.h + copy /y config.h.generic config.h + copy /y pcre_chartables.c.dist pcre_chartables.c diff --git a/auto/lib/pcre/makefile.msvc b/auto/lib/pcre/makefile.msvc --- a/auto/lib/pcre/makefile.msvc +++ b/auto/lib/pcre/makefile.msvc @@ -3,21 +3,19 @@ CFLAGS = -O2 -Ob1 -Oi -Gs $(LIBC) $(CPU_OPT) -PCREFLAGS = -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10 +PCREFLAGS = -DHAVE_CONFIG_H -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10 -pcre.lib: pcre.h - cl -Fedftables dftables.c - - dftables > chartables.c +pcre.lib: + cd $(PCRE) - cl -nologo -c $(CFLAGS) $(PCREFLAGS) \ - maketables.c get.c study.c pcre.c + cl -nologo -c $(CFLAGS) -I . $(PCREFLAGS) pcre_*.c - link -lib -out:pcre.lib -verbose:lib \ - maketables.obj get.obj study.obj pcre.obj + link -lib -out:pcre.lib -verbose:lib pcre_*.obj pcre.h: - patch -o pcre.h pcre.in patch.pcre.in - patch -o config.h config.in patch.config.in - patch -o pcre.c pcre.c patch.pcre.c + cd $(PCRE) + + copy /y pcre.h.generic pcre.h + copy /y config.h.generic config.h + copy /y pcre_chartables.c.dist pcre_chartables.c diff --git a/auto/lib/pcre/makefile.owc b/auto/lib/pcre/makefile.owc --- a/auto/lib/pcre/makefile.owc +++ b/auto/lib/pcre/makefile.owc @@ -3,17 +3,21 @@ CFLAGS = -c -zq -bt=nt -ot -op -oi -oe -s -bm $(CPU_OPT) -PCREFLAGS = -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10 +PCREFLAGS = -DHAVE_CONFIG_H -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10 -pcre.lib: pcre.h - wcl386 -zq -bt=nt -l=nt -fe=dftables dftables.c - dftables > chartables.c +pcre.lib: + cd $(PCRE) - wcl386 $(CFLAGS) $(PCREFLAGS) maketables.c get.c study.c pcre.c - wlib -n pcre.lib maketables.obj get.obj study.obj pcre.obj + wcl386 $(CFLAGS) -i=. $(PCREFLAGS) pcre_*.c + dir /b *.obj > pcre.lst + + wlib -n pcre.lib @pcre.lst pcre.h: - patch -o pcre.h pcre.in patch.pcre.in.owc - patch -o config.h config.in patch.config.in + cd $(PCRE) + + copy /y pcre.h.generic pcre.h + copy /y config.h.generic config.h + copy /y pcre_chartables.c.dist pcre_chartables.c diff --git a/auto/lib/pcre/patch.config.in b/auto/lib/pcre/patch.config.in deleted file mode 100644 --- a/auto/lib/pcre/patch.config.in +++ /dev/null @@ -1,11 +0,0 @@ ---- config.in Thu Aug 21 14:43:07 2003 -+++ config.in Sun Mar 7 02:37:24 2004 -@@ -28,7 +28,7 @@ - found. */ - - #define HAVE_STRERROR 0 --#define HAVE_MEMMOVE 0 -+#define HAVE_MEMMOVE 1 - - /* There are some non-Unix systems that don't even have bcopy(). If this macro - is false, an emulation is used. If HAVE_MEMMOVE is set to 1, the value of diff --git a/auto/lib/pcre/patch.pcre.c b/auto/lib/pcre/patch.pcre.c deleted file mode 100644 --- a/auto/lib/pcre/patch.pcre.c +++ /dev/null @@ -1,13 +0,0 @@ ---- pcre.c Thu Aug 21 14:43:07 2003 -+++ pcre.c Tue Mar 22 12:56:59 2005 -@@ -246,8 +246,8 @@ - extern "C" void (*pcre_free)(void *) = free; - extern "C" int (*pcre_callout)(pcre_callout_block *) = NULL; - #else --void *(*pcre_malloc)(size_t) = malloc; --void (*pcre_free)(void *) = free; -+void *(__cdecl *pcre_malloc)(size_t) = malloc; -+void (__cdecl *pcre_free)(void *) = free; - int (*pcre_callout)(pcre_callout_block *) = NULL; - #endif - #endif diff --git a/auto/lib/pcre/patch.pcre.in b/auto/lib/pcre/patch.pcre.in deleted file mode 100644 --- a/auto/lib/pcre/patch.pcre.in +++ /dev/null @@ -1,26 +0,0 @@ ---- pcre.in Thu Aug 21 14:43:07 2003 -+++ pcre.h Tue Mar 22 12:56:59 2005 -@@ -10,9 +10,9 @@ - /* The file pcre.h is build by "configure". Do not edit it; instead - make changes to pcre.in. */ - --#define PCRE_MAJOR @PCRE_MAJOR@ --#define PCRE_MINOR @PCRE_MINOR@ --#define PCRE_DATE @PCRE_DATE@ -+#define PCRE_MAJOR 4 -+#define PCRE_MINOR 4 -+#define PCRE_DATE 21-August-2003 - - /* Win32 uses DLL by default */ - -@@ -143,8 +143,8 @@ - have to be different again. */ - - #ifndef VPCOMPAT --PCRE_DATA_SCOPE void *(*pcre_malloc)(size_t); --PCRE_DATA_SCOPE void (*pcre_free)(void *); -+PCRE_DATA_SCOPE void *(__cdecl *pcre_malloc)(size_t); -+PCRE_DATA_SCOPE void (__cdecl *pcre_free)(void *); - PCRE_DATA_SCOPE int (*pcre_callout)(pcre_callout_block *); - #else /* VPCOMPAT */ - extern void *pcre_malloc(size_t); diff --git a/auto/lib/pcre/patch.pcre.in.owc b/auto/lib/pcre/patch.pcre.in.owc deleted file mode 100644 --- a/auto/lib/pcre/patch.pcre.in.owc +++ /dev/null @@ -1,15 +0,0 @@ ---- pcre.in Thu Aug 21 14:43:07 2003 -+++ pcre.h Tue Mar 22 12:56:59 2005 -@@ -10,9 +10,9 @@ - /* The file pcre.h is build by "configure". Do not edit it; instead - make changes to pcre.in. */ - --#define PCRE_MAJOR @PCRE_MAJOR@ --#define PCRE_MINOR @PCRE_MINOR@ --#define PCRE_DATE @PCRE_DATE@ -+#define PCRE_MAJOR 4 -+#define PCRE_MINOR 4 -+#define PCRE_DATE 21-August-2003 - - /* Win32 uses DLL by default */ - diff --git a/auto/lib/sha1/make b/auto/lib/sha1/make --- a/auto/lib/sha1/make +++ b/auto/lib/sha1/make @@ -7,16 +7,19 @@ case "$NGX_CC_NAME" in msvc*) ngx_makefile=makefile.msvc ngx_opt="CPU_OPT=\"$CPU_OPT\" LIBC=$LIBC SHA1_ASM=$SHA1_ASM" + ngx_sha1="SHA1=\"$SHA1\"" ;; owc*) ngx_makefile=makefile.owc ngx_opt="CPU_OPT=\"$CPU_OPT\"" + ngx_sha1=`echo SHA1=\"$SHA1\" | sed -e "s/\//$ngx_regex_dirsep/g"` ;; bcc) ngx_makefile=makefile.bcc ngx_opt="-DCPU_OPT=\"$CPU_OPT\" -DSHA1_ASM=$SHA1_ASM" + ngx_sha1=`echo \-DSHA1=\"$SHA1\" | sed -e "s/\//$ngx_regex_dirsep/g"` ;; esac @@ -28,14 +31,10 @@ done=NO case "$NGX_PLATFORM" in win32) - cp auto/lib/sha1/$ngx_makefile $SHA1 - cat << END >> $NGX_MAKEFILE `echo "$SHA1/sha1.lib: $NGX_MAKEFILE" | sed -e "s/\//$ngx_regex_dirsep/g"` - cd `echo $SHA1 | sed -e "s/\//$ngx_regex_dirsep/g"` - \$(MAKE) -f $ngx_makefile $ngx_opt - cd ..\\..\\.. + \$(MAKE) -f auto/lib/sha1/$ngx_makefile $ngx_opt $ngx_sha1 END diff --git a/auto/lib/sha1/makefile.bcc b/auto/lib/sha1/makefile.bcc --- a/auto/lib/sha1/makefile.bcc +++ b/auto/lib/sha1/makefile.bcc @@ -7,12 +7,14 @@ CFLAGS = -q -O2 -tWM $(CPU_OPT) -DL_ENDI !if "$(SHA1_ASM)" == "YES" sha1.lib: + cd $(SHA1) bcc32 -c $(CFLAGS) -DSHA1_ASM sha1dgst.c tlib sha1.lib +sha1dgst.obj +"asm\s-win32.obj" !else sha1.lib: + cd $(SHA1) bcc32 -c $(CFLAGS) sha1dgst.c tlib sha1.lib +sha1dgst.obj diff --git a/auto/lib/sha1/makefile.msvc b/auto/lib/sha1/makefile.msvc --- a/auto/lib/sha1/makefile.msvc +++ b/auto/lib/sha1/makefile.msvc @@ -7,12 +7,14 @@ CFLAGS = -nologo -O2 -Ob1 -Oi -Gs $(LIBC !IF "$(SHA1_ASM)" == "YES" sha1.lib: + cd $(SHA1) cl -c $(CFLAGS) -D SHA1_ASM sha1dgst.c link -lib -out:sha1.lib sha1dgst.obj asm/s-win32.obj !ELSE sha1.lib: + cd $(SHA1) cl -c $(CFLAGS) sha1dgst.c link -lib -out:sha1.lib sha1dgst.obj diff --git a/auto/lib/sha1/makefile.owc b/auto/lib/sha1/makefile.owc --- a/auto/lib/sha1/makefile.owc +++ b/auto/lib/sha1/makefile.owc @@ -5,5 +5,6 @@ CFLAGS = -zq -bt=nt -bm -ot -op -oi -oe -s $(CPU_OPT) sha1.lib: + cd $(SHA1) wcl386 -c $(CFLAGS) -dL_ENDIAN sha1dgst.c wlib -n sha1.lib sha1dgst.obj diff --git a/auto/lib/zlib/conf b/auto/lib/zlib/conf --- a/auto/lib/zlib/conf +++ b/auto/lib/zlib/conf @@ -57,6 +57,17 @@ else CORE_LIBS="$CORE_LIBS $ngx_feature_libs" ZLIB=YES ngx_found=no + else + +cat << END + +$0: error: the HTTP gzip module requires the zlib library. +You can either disable the module by using --without-http_gzip_module +option, or install the zlib library into the system, or build the zlib library +statically from the source with nginx by using --with-zlib= option. + +END + exit 1 fi fi diff --git a/auto/lib/zlib/make b/auto/lib/zlib/make --- a/auto/lib/zlib/make +++ b/auto/lib/zlib/make @@ -7,17 +7,20 @@ case "$NGX_CC_NAME" in msvc*) ngx_makefile=makefile.msvc ngx_opt="CPU_OPT=\"$CPU_OPT\" LIBC=$LIBC" + ngx_zlib="ZLIB=\"$ZLIB\"" ;; owc*) ngx_makefile=makefile.owc ngx_opt="CPU_OPT=\"$CPU_OPT\"" + ngx_zlib=`echo ZLIB=\"$ZLIB\" | sed -e "s/\//$ngx_regex_dirsep/g"` ;; bcc) ngx_makefile=makefile.bcc ngx_opt="-DCPU_OPT=\"$CPU_OPT\"" + ngx_zlib=`echo \-DZLIB=\"$ZLIB\" | sed -e "s/\//$ngx_regex_dirsep/g"` ;; esac @@ -29,14 +32,10 @@ done=NO case "$NGX_PLATFORM" in win32) - cp auto/lib/zlib/$ngx_makefile $ZLIB - cat << END >> $NGX_MAKEFILE `echo "$ZLIB/zlib.lib: $NGX_MAKEFILE" | sed -e "s/\//$ngx_regex_dirsep/g"` - cd `echo $ZLIB | sed -e "s/\//$ngx_regex_dirsep/g"` - \$(MAKE) -f $ngx_makefile $ngx_opt - cd ..\\..\\.. + \$(MAKE) -f auto/lib/zlib/$ngx_makefile $ngx_opt $ngx_zlib END diff --git a/auto/lib/zlib/makefile.bcc b/auto/lib/zlib/makefile.bcc --- a/auto/lib/zlib/makefile.bcc +++ b/auto/lib/zlib/makefile.bcc @@ -5,6 +5,8 @@ CFLAGS = -q -O2 -tWM -w-8004 -w-8012 $(CPU_OPT) zlib.lib: + cd $(ZLIB) + bcc32 -c $(CFLAGS) adler32.c crc32.c deflate.c trees.c zutil.c \ compress.c diff --git a/auto/lib/zlib/makefile.msvc b/auto/lib/zlib/makefile.msvc --- a/auto/lib/zlib/makefile.msvc +++ b/auto/lib/zlib/makefile.msvc @@ -5,6 +5,8 @@ CFLAGS = -nologo -O2 -Ob1 -Oi -Gs $(LIBC) $(CPU_OPT) zlib.lib: + cd $(ZLIB) + cl -c $(CFLAGS) adler32.c crc32.c deflate.c trees.c zutil.c compress.c link -lib -out:zlib.lib adler32.obj crc32.obj deflate.obj \ diff --git a/auto/lib/zlib/makefile.owc b/auto/lib/zlib/makefile.owc --- a/auto/lib/zlib/makefile.owc +++ b/auto/lib/zlib/makefile.owc @@ -5,5 +5,9 @@ CFLAGS = -zq -bt=nt -ot -op -oi -oe -s -bm $(CPU_OPT) zlib.lib: - wcl386 -c $(CFLAGS) adler32.c crc32.c deflate.c trees.c zutil.c compress.c - wlib -n zlib.lib adler32.obj crc32.obj deflate.obj trees.obj zutil.obj compress.obj + cd $(ZLIB) + + wcl386 -c $(CFLAGS) adler32.c crc32.c deflate.c trees.c zutil.c & + compress.c + wlib -n zlib.lib adler32.obj crc32.obj deflate.obj trees.obj & + zutil.obj compress.obj diff --git a/auto/make b/auto/make --- a/auto/make +++ b/auto/make @@ -2,6 +2,8 @@ # Copyright (C) Igor Sysoev +echo "creating $NGX_MAKEFILE" + mkdir -p $NGX_OBJS/src/core $NGX_OBJS/src/event $NGX_OBJS/src/event/modules \ $NGX_OBJS/src/os/unix $NGX_OBJS/src/os/win32 \ $NGX_OBJS/src/http $NGX_OBJS/src/http/modules \ @@ -23,22 +25,12 @@ LINK = $LINK END + if test -n "$NGX_PERL_CFLAGS"; then echo NGX_PERL_CFLAGS = $NGX_PERL_CFLAGS >> $NGX_MAKEFILE echo NGX_PM_CFLAGS = $NGX_PM_CFLAGS >> $NGX_MAKEFILE fi -if [ "$BMAKE" = wmake ]; then - echo MAKE = wmake >> $NGX_MAKEFILE - - ngx_regex_cont=' ' - ngx_long_regex_cont=' ' - ngx_cont=' ' - ngx_long_cont=' ' - ngx_tab=' ' - -fi - # ALL_INCS, required by the addons and by OpenWatcom C precompiled headers diff --git a/auto/modules b/auto/modules --- a/auto/modules +++ b/auto/modules @@ -65,6 +65,13 @@ if [ $HTTP != YES ]; then fi +if [ $HTTP_CACHE = YES ]; then + USE_MD5=YES + have=NGX_HTTP_CACHE . auto/have + HTTP_SRCS="$HTTP_SRCS $HTTP_FILE_CACHE_SRCS" +fi + + if [ $HTTP_SSI = YES ]; then HTTP_POSTPONE=YES fi @@ -97,6 +104,7 @@ fi # ngx_http_charset_filter # ngx_http_ssi_filter # ngx_http_xslt_filter +# ngx_http_image_filter_filter # ngx_http_sub_filter # ngx_http_addition_filter # ngx_http_userid_filter @@ -143,6 +151,12 @@ if [ $HTTP_XSLT = YES ]; then HTTP_SRCS="$HTTP_SRCS $HTTP_XSLT_SRCS" fi +if [ $HTTP_IMAGE_FILTER = YES ]; then + USE_LIBGD=YES + HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_IMAGE_FILTER_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_IMAGE_SRCS" +fi + if [ $HTTP_SUB = YES ]; then HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_SUB_FILTER_MODULE" HTTP_SRCS="$HTTP_SRCS $HTTP_SUB_SRCS" @@ -205,6 +219,11 @@ if [ $HTTP_LIMIT_ZONE = YES ]; then HTTP_SRCS="$HTTP_SRCS $HTTP_LIMIT_ZONE_SRCS" fi +if [ $HTTP_LIMIT_REQ = YES ]; then + HTTP_MODULES="$HTTP_MODULES $HTTP_LIMIT_REQ_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_LIMIT_REQ_SRCS" +fi + if [ $HTTP_REALIP = YES ]; then have=NGX_HTTP_REALIP . auto/have HTTP_MODULES="$HTTP_MODULES $HTTP_REALIP_MODULE" @@ -302,11 +321,6 @@ if [ $HTTP_UPSTREAM_IP_HASH = YES ]; the HTTP_SRCS="$HTTP_SRCS $HTTP_UPSTREAM_IP_HASH_SRCS" fi -# STUB -#USE_MD5=YES -#HTTP_SRCS="$HTTP_SRCS $HTTP_CACHE_SRCS" -#HTTP_SRCS="$HTTP_SRCS $HTTP_FILE_CACHE_SRCS" - if [ $HTTP_STUB_STATUS = YES ]; then have=NGX_STAT_STUB . auto/have HTTP_MODULES="$HTTP_MODULES ngx_http_stub_status_module" diff --git a/auto/options b/auto/options --- a/auto/options +++ b/auto/options @@ -43,6 +43,8 @@ EVENT_AIO=NO USE_THREADS=NO +NGX_IPV6=NO + HTTP=YES NGX_HTTP_LOG_PATH= @@ -50,6 +52,7 @@ NGX_HTTP_CLIENT_TEMP_PATH= NGX_HTTP_PROXY_TEMP_PATH= NGX_HTTP_FASTCGI_TEMP_PATH= +HTTP_CACHE=YES HTTP_CHARSET=YES HTTP_GZIP=YES HTTP_SSL=NO @@ -57,6 +60,7 @@ HTTP_SSI=YES HTTP_POSTPONE=NO HTTP_REALIP=NO HTTP_XSLT=NO +HTTP_IMAGE_FILTER=NO HTTP_SUB=NO HTTP_ADDITION=NO HTTP_DAV=NO @@ -75,6 +79,7 @@ HTTP_FASTCGI=YES HTTP_PERL=NO HTTP_MEMCACHED=YES HTTP_LIMIT_ZONE=YES +HTTP_LIMIT_REQ=YES HTTP_EMPTY_GIF=YES HTTP_BROWSER=YES HTTP_SECURE_LINK=NO @@ -119,15 +124,19 @@ USE_PERL=NO NGX_PERL=perl USE_LIBXSLT=NO +USE_LIBGD=NO NGX_GOOGLE_PERFTOOLS=NO NGX_CPP_TEST=NO NGX_CPU_CACHE_LINE= +opt= for option do + opt="$opt `echo $option | sed -e \"s/\(--[^=]*=\)\(.* .*\)/\1'\2'/\"`" + case "$option" in -*=*) value=`echo "$option" | sed -e 's/[-_a-zA-Z0-9]*=//'` ;; *) value="" ;; @@ -136,6 +145,7 @@ do case "$option" in --help) help=yes ;; + --prefix=) NGX_PREFIX="!" ;; --prefix=*) NGX_PREFIX="$value" ;; --sbin-path=*) NGX_SBIN_PATH="$value" ;; --conf-path=*) NGX_CONF_PATH="$value" ;; @@ -159,7 +169,11 @@ do #--with-threads=*) USE_THREADS="$value" ;; #--with-threads) USE_THREADS="pthreads" ;; + --with-ipv6) NGX_IPV6=YES ;; + --without-http) HTTP=NO ;; + --without-http-cache) HTTP_CACHE=NO ;; + --http-log-path=*) NGX_HTTP_LOG_PATH="$value" ;; --http-client-body-temp-path=*) NGX_HTTP_CLIENT_TEMP_PATH="$value" ;; --http-proxy-temp-path=*) NGX_HTTP_PROXY_TEMP_PATH="$value" ;; @@ -169,6 +183,7 @@ do --with-http_realip_module) HTTP_REALIP=YES ;; --with-http_addition_module) HTTP_ADDITION=YES ;; --with-http_xslt_module) HTTP_XSLT=YES ;; + --with-http_image_filter_module) HTTP_IMAGE_FILTER=YES ;; --with-http_sub_module) HTTP_SUB=YES ;; --with-http_dav_module) HTTP_DAV=YES ;; --with-http_flv_module) HTTP_FLV=YES ;; @@ -192,6 +207,7 @@ do --without-http_fastcgi_module) HTTP_FASTCGI=NO ;; --without-http_memcached_module) HTTP_MEMCACHED=NO ;; --without-http_limit_zone_module) HTTP_LIMIT_ZONE=NO ;; + --without-http_limit_req_module) HTTP_LIMIT_REQ=NO ;; --without-http_empty_gif_module) HTTP_EMPTY_GIF=NO ;; --without-http_browser_module) HTTP_BROWSER=NO ;; --without-http_upstream_ip_hash_module) HTTP_UPSTREAM_IP_HASH=NO ;; @@ -225,6 +241,7 @@ do --with-debug) NGX_DEBUG=YES ;; --without-pcre) USE_PCRE=DISABLED ;; + --with-pcre) USE_PCRE=YES ;; --with-pcre=*) PCRE="$value" ;; --with-pcre-opt=*) PCRE_OPT="$value" ;; @@ -257,6 +274,9 @@ do done +NGX_CONFIGURE="$opt" + + if [ $help = yes ]; then cat << END @@ -283,10 +303,13 @@ cat << END --with-poll_module enable poll module --without-poll_module disable poll module + --with-ipv6 enable ipv6 support + --with-http_ssl_module enable ngx_http_ssl_module --with-http_realip_module enable ngx_http_realip_module --with-http_addition_module enable ngx_http_addition_module --with-http_xslt_module enable ngx_http_xslt_module + --with-http_image_filter_module enable ngx_http_image_filter_module --with-http_sub_module enable ngx_http_sub_module --with-http_dav_module enable ngx_http_dav_module --with-http_flv_module enable ngx_http_flv_module @@ -310,6 +333,7 @@ cat << END --without-http_fastcgi_module disable ngx_http_fastcgi_module --without-http_memcached_module disable ngx_http_memcached_module --without-http_limit_zone_module disable ngx_http_limit_zone_module + --without-http_limit_req_module disable ngx_http_limit_req_module --without-http_empty_gif_module disable ngx_http_empty_gif_module --without-http_browser_module disable ngx_http_browser_module --without-http_upstream_ip_hash_module @@ -327,6 +351,7 @@ cat << END files --without-http disable HTTP server + --without-http-cache disable HTTP cache --with-mail enable POP3/IMAP4/SMTP proxy module --with-mail_ssl_module enable ngx_mail_ssl_module @@ -347,7 +372,8 @@ cat << END pentium, pentiumpro, pentium3, pentium4, athlon, opteron, sparc32, sparc64, ppc64 - --without-pcre disable PCRE libarary usage + --without-pcre disable PCRE library usage + --with-pcre force PCRE library usage --with-pcre=DIR set path to PCRE library sources --with-pcre-opt=OPTIONS set additional options for PCRE building @@ -394,141 +420,21 @@ if [ ".$NGX_PLATFORM" = ".win32" ]; then fi -NGX_PREFIX=${NGX_PREFIX:-/usr/local/nginx} - - -case ".$NGX_SBIN_PATH" in - ./*) - ;; - - .) - NGX_SBIN_PATH=$NGX_PREFIX/sbin/nginx - ;; - - *) - NGX_SBIN_PATH=$NGX_PREFIX/$NGX_SBIN_PATH - ;; -esac - - -case ".$NGX_CONF_PATH" in - ./*) - ;; - - .) - NGX_CONF_PATH=$NGX_PREFIX/conf/nginx.conf - ;; - - *) - NGX_CONF_PATH=$NGX_PREFIX/$NGX_CONF_PATH - ;; -esac - - +NGX_CONF_PATH=${NGX_CONF_PATH:-conf/nginx.conf} NGX_CONF_PREFIX=`dirname $NGX_CONF_PATH` - - -case ".$NGX_PID_PATH" in - ./*) - ;; - - .) - NGX_PID_PATH=$NGX_PREFIX/logs/nginx.pid - ;; - - *) - NGX_PID_PATH=$NGX_PREFIX/$NGX_PID_PATH - ;; -esac - - -case ".$NGX_LOCK_PATH" in - ./*) - ;; - - .) - NGX_LOCK_PATH=$NGX_PREFIX/logs/nginx.lock - ;; - - *) - NGX_LOCK_PATH=$NGX_PREFIX/$NGX_LOCK_PATH - ;; -esac - - -case ".$NGX_ERROR_LOG_PATH" in - ./*) - ;; +NGX_PID_PATH=${NGX_PID_PATH:-logs/nginx.pid} +NGX_LOCK_PATH=${NGX_LOCK_PATH:-logs/nginx.lock} - .) - NGX_ERROR_LOG_PATH=$NGX_PREFIX/logs/error.log - ;; - - .stderr) - NGX_ERROR_LOG_PATH= - ;; - - *) - NGX_ERROR_LOG_PATH=$NGX_PREFIX/$NGX_ERROR_LOG_PATH - ;; -esac - - -case ".$NGX_HTTP_LOG_PATH" in - ./*) - ;; - - .) - NGX_HTTP_LOG_PATH=$NGX_PREFIX/logs/access.log - ;; - - *) - NGX_HTTP_LOG_PATH=$NGX_PREFIX/$NGX_HTTP_LOG_PATH - ;; -esac - - -case ".$NGX_HTTP_CLIENT_TEMP_PATH" in - ./*) - ;; +if [ ".$NGX_ERROR_LOG_PATH" = ".stderr" ]; then + NGX_ERROR_LOG_PATH= +else + NGX_ERROR_LOG_PATH=${NGX_ERROR_LOG_PATH:-logs/error.log} +fi - .) - NGX_HTTP_CLIENT_TEMP_PATH=$NGX_PREFIX/client_body_temp - ;; - - *) - NGX_HTTP_CLIENT_TEMP_PATH=$NGX_PREFIX/$NGX_HTTP_CLIENT_TEMP_PATH - ;; -esac - - -case ".$NGX_HTTP_PROXY_TEMP_PATH" in - ./*) - ;; - - .) - NGX_HTTP_PROXY_TEMP_PATH=$NGX_PREFIX/proxy_temp - ;; - - *) - NGX_HTTP_PROXY_TEMP_PATH=$NGX_PREFIX/$NGX_HTTP_PROXY_TEMP_PATH - ;; -esac - - -case ".$NGX_HTTP_FASTCGI_TEMP_PATH" in - ./*) - ;; - - .) - NGX_HTTP_FASTCGI_TEMP_PATH=$NGX_PREFIX/fastcgi_temp - ;; - - *) - NGX_HTTP_FASTCGI_TEMP_PATH=$NGX_PREFIX/$NGX_HTTP_FASTCGI_TEMP_PATH - ;; -esac - +NGX_HTTP_LOG_PATH=${NGX_HTTP_LOG_PATH:-logs/access.log} +NGX_HTTP_CLIENT_TEMP_PATH=${NGX_HTTP_CLIENT_TEMP_PATH:-client_body_temp} +NGX_HTTP_PROXY_TEMP_PATH=${NGX_HTTP_PROXY_TEMP_PATH:-proxy_temp} +NGX_HTTP_FASTCGI_TEMP_PATH=${NGX_HTTP_FASTCGI_TEMP_PATH:-fastcgi_temp} case ".$NGX_PERL_MODULES" in ./*) diff --git a/auto/os/features b/auto/os/features --- a/auto/os/features +++ b/auto/os/features @@ -98,7 +98,7 @@ if test -z "$NGX_KQUEUE_CHECKED"; then ngx_feature_name="NGX_HAVE_TIMER_EVENT" ngx_feature_run=yes ngx_feature_incs="#include -#include " + #include " ngx_feature_path= ngx_feature_libs= ngx_feature_test="int kq; @@ -205,3 +205,72 @@ ngx_feature_path= ngx_feature_libs= ngx_feature_test="directio(0, DIRECTIO_ON);" . auto/feature + + +ngx_feature="statfs()" +ngx_feature_name="NGX_HAVE_STATFS" +ngx_feature_run=no +ngx_feature_incs="$NGX_INCLUDE_SYS_PARAM_H + $NGX_INCLUDE_SYS_MOUNT_H + $NGX_INCLUDE_SYS_VFS_H" +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="struct statfs fs; + statfs(NULL, &fs);" +. auto/feature + + +ngx_feature="statvfs()" +ngx_feature_name="NGX_HAVE_STATVFS" +ngx_feature_run=no +ngx_feature_incs="#include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="struct statvfs fs; + statvfs(NULL, &fs);" +. auto/feature + + +ngx_feature="dlopen()" +ngx_feature_name= +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="dlopen(NULL, 0)" +. auto/feature + + +if [ $ngx_found != yes ]; then + + ngx_feature="dlopen() in libdl" + ngx_feature_libs="-ldl" + . auto/feature + + if [ $ngx_found = yes ]; then + NGX_LIBDL="-ldl" + fi +fi + + +ngx_feature="sched_yield()" +ngx_feature_name="NGX_HAVE_SCHED_YIELD" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="sched_yield()" +. auto/feature + + +if [ $ngx_found != yes ]; then + + ngx_feature="sched_yield() in librt" + ngx_feature_libs="-lrt" + . auto/feature + + if [ $ngx_found = yes ]; then + CORE_LIBS="$CORE_LIBS -lrt" + fi +fi diff --git a/auto/os/linux b/auto/os/linux --- a/auto/os/linux +++ b/auto/os/linux @@ -11,7 +11,8 @@ CORE_SRCS="$UNIX_SRCS $LINUX_SRCS" ngx_spacer=' ' -CC_AUX_FLAGS="$CC_AUX_FLAGS -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64" +cc_aux_flags="$CC_AUX_FLAGS" +CC_AUX_FLAGS="$cc_aux_flags -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64" # Linux kernel version @@ -60,12 +61,12 @@ fi # sendfile() -CC_AUX_FLAGS="$CC_AUX_FLAGS -D_GNU_SOURCE" +CC_AUX_FLAGS="$cc_aux_flags -D_GNU_SOURCE" ngx_feature="sendfile()" ngx_feature_name="NGX_HAVE_SENDFILE" ngx_feature_run=yes ngx_feature_incs="#include -#include " + #include " ngx_feature_path= ngx_feature_libs= ngx_feature_test="int s = 0, fd = 1; @@ -81,12 +82,12 @@ fi # sendfile64() -CC_AUX_FLAGS="$CC_AUX_FLAGS -D_FILE_OFFSET_BITS=64" +CC_AUX_FLAGS="$cc_aux_flags -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64" ngx_feature="sendfile64()" ngx_feature_name="NGX_HAVE_SENDFILE64" ngx_feature_run=yes ngx_feature_incs="#include -#include " + #include " ngx_feature_path= ngx_feature_libs= ngx_feature_test="int s = 0, fd = 1; @@ -121,3 +122,22 @@ ngx_feature_libs= ngx_feature_test="long mask = 0; sched_setaffinity(0, 32, (cpu_set_t *) &mask)" . auto/feature + + +# crypt_r() + +ngx_feature="crypt_r()" +ngx_feature_name="NGX_HAVE_GNU_CRYPT_R" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs=-lcrypt +ngx_feature_test="struct crypt_data cd; + crypt_r(NULL, NULL, &cd);" +. auto/feature + + +ngx_include="sys/vfs.h"; . auto/include + + +CC_AUX_FLAGS="$cc_aux_flags -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64" diff --git a/auto/os/solaris b/auto/os/solaris --- a/auto/os/solaris +++ b/auto/os/solaris @@ -7,14 +7,14 @@ have=NGX_SOLARIS . auto/have_headers CORE_INCS="$UNIX_INCS" CORE_DEPS="$UNIX_DEPS $SOLARIS_DEPS" CORE_SRCS="$UNIX_SRCS $SOLARIS_SRCS " -CORE_LIBS="$CORE_LIBS -lsocket -lnsl -lrt" +CORE_LIBS="$CORE_LIBS -lsocket -lnsl" NGX_RPATH=YES # Solaris's make does not support a blank line between target and rules ngx_spacer= -CC_AUX_FLAGS="$CC_AUX_FLAGS -D_FILE_OFFSET_BITS=64 -lsocket -lnsl -lrt" +CC_AUX_FLAGS="$CC_AUX_FLAGS -D_FILE_OFFSET_BITS=64 -lsocket -lnsl" if [ $ZLIB_ASM != NO ]; then diff --git a/auto/os/win32 b/auto/os/win32 --- a/auto/os/win32 +++ b/auto/os/win32 @@ -8,8 +8,9 @@ CORE_INCS="$WIN32_INCS" CORE_DEPS="$WIN32_DEPS" CORE_SRCS="$WIN32_SRCS $IOCP_SRCS" OS_CONFIG="$WIN32_CONFIG" -CORE_LIBS="$CORE_LIBS shell32.lib ws2_32.lib" +CORE_LIBS="$CORE_LIBS advapi32.lib ws2_32.lib" NGX_ICONS="$NGX_WIN32_ICONS" +SELECT_SRCS=$WIN32_SELECT_SRCS EVENT_MODULES="$EVENT_MODULES $IOCP_MODULE" EVENT_FOUND=YES @@ -19,5 +20,9 @@ if [ $EVENT_SELECT = NO ]; then EVENT_MODULES="$EVENT_MODULES $SELECT_MODULE" fi +if [ $NGX_IPV6 = YES ]; then + have=NGX_HAVE_INET6 . auto/have +fi + have=NGX_HAVE_AIO . auto/have have=NGX_HAVE_IOCP . auto/have diff --git a/auto/sources b/auto/sources --- a/auto/sources +++ b/auto/sources @@ -33,8 +33,7 @@ CORE_DEPS="src/core/nginx.h \ src/core/ngx_cycle.h \ src/core/ngx_conf_file.h \ src/core/ngx_resolver.h \ - src/core/ngx_open_file_cache.h \ - src/core/ngx_garbage_collector.h" + src/core/ngx_open_file_cache.h" CORE_SRCS="src/core/nginx.c \ @@ -62,8 +61,7 @@ CORE_SRCS="src/core/nginx.c \ src/core/ngx_cpuinfo.c \ src/core/ngx_conf_file.c \ src/core/ngx_resolver.c \ - src/core/ngx_open_file_cache.c \ - src/core/ngx_garbage_collector.c" + src/core/ngx_open_file_cache.c" REGEX_DEPS=src/core/ngx_regex.h @@ -97,6 +95,7 @@ EVENT_SRCS="src/event/ngx_event.c \ SELECT_MODULE=ngx_select_module SELECT_SRCS=src/event/modules/ngx_select_module.c +WIN32_SELECT_SRCS=src/event/modules/ngx_win32_select_module.c POLL_MODULE=ngx_poll_module POLL_SRCS=src/event/modules/ngx_poll_module.c @@ -217,8 +216,6 @@ WIN32_DEPS="$CORE_DEPS $EVENT_DEPS \ src/os/win32/ngx_socket.h \ src/os/win32/ngx_os.h \ src/os/win32/ngx_user.h \ - src/os/win32/ngx_gui.h \ - src/os/win32/ngx_gui_resources.h \ src/os/win32/ngx_process_cycle.h" WIN32_CONFIG=src/os/win32/ngx_win32_config.h @@ -235,14 +232,15 @@ WIN32_SRCS="$CORE_SRCS $EVENT_SRCS \ src/os/win32/ngx_wsarecv.c \ src/os/win32/ngx_wsarecv_chain.c \ src/os/win32/ngx_udp_wsarecv.c \ + src/os/win32/ngx_wsasend.c \ src/os/win32/ngx_wsasend_chain.c \ src/os/win32/ngx_win32_init.c \ src/os/win32/ngx_user.c \ - src/os/win32/ngx_gui.c \ + src/os/win32/ngx_event_log.c \ src/os/win32/ngx_process_cycle.c \ src/event/ngx_event_acceptex.c" -NGX_WIN32_ICONS="src/os/win32/nginx.ico src/os/win32/nginx_tray.ico" +NGX_WIN32_ICONS="src/os/win32/nginx.ico" NGX_WIN32_RC="src/os/win32/nginx.rc" @@ -254,8 +252,6 @@ HTTP_MODULES="ngx_http_module \ ngx_http_log_module \ ngx_http_upstream_module" -HTTP_CACHE_MODULE=ngx_http_cache_module - HTTP_WRITE_FILTER_MODULE="ngx_http_write_filter_module" HTTP_HEADER_FILTER_MODULE="ngx_http_header_filter_module" @@ -314,7 +310,6 @@ HTTP_SRCS="$HTTP_SRCS src/http/ngx_http_ HTTP_POSTPONE_FILTER_SRCS=src/http/ngx_http_postpone_filter_module.c -HTTP_CACHE_SRCS=src/http/ngx_http_cache.c HTTP_FILE_CACHE_SRCS=src/http/ngx_http_file_cache.c @@ -335,6 +330,10 @@ HTTP_XSLT_FILTER_MODULE=ngx_http_xslt_fi HTTP_XSLT_SRCS=src/http/modules/ngx_http_xslt_filter_module.c +HTTP_IMAGE_FILTER_MODULE=ngx_http_image_filter_module +HTTP_IMAGE_SRCS=src/http/modules/ngx_http_image_filter_module.c + + HTTP_SUB_FILTER_MODULE=ngx_http_sub_filter_module HTTP_SUB_SRCS=src/http/modules/ngx_http_sub_filter_module.c @@ -418,6 +417,10 @@ HTTP_LIMIT_ZONE_MODULE=ngx_http_limit_zo HTTP_LIMIT_ZONE_SRCS=src/http/modules/ngx_http_limit_zone_module.c +HTTP_LIMIT_REQ_MODULE=ngx_http_limit_req_module +HTTP_LIMIT_REQ_SRCS=src/http/modules/ngx_http_limit_req_module.c + + HTTP_EMPTY_GIF_MODULE=ngx_http_empty_gif_module HTTP_EMPTY_GIF_SRCS=src/http/modules/ngx_http_empty_gif_module.c diff --git a/auto/stubs b/auto/stubs --- a/auto/stubs +++ b/auto/stubs @@ -2,7 +2,6 @@ # Copyright (C) Igor Sysoev -have=NGX_USE_HTTP_FILE_CACHE_UNIQ . auto/have have=NGX_SUPPRESS_WARN . auto/have have=NGX_SMP . auto/have diff --git a/auto/summary b/auto/summary --- a/auto/summary +++ b/auto/summary @@ -74,65 +74,6 @@ esac echo -if [ $HTTP_REWRITE = YES ]; then - if [ $USE_PCRE = DISABLED ]; then - -cat << END -$0: error: the HTTP rewrite module requires the PCRE library. -You can either disable the module by using --without-http_rewrite_module -option or you have to enable the PCRE support. - -END - exit 1 - fi - - if [ $PCRE = NONE -o $PCRE = NO ]; then - -cat << END -$0: error: the HTTP rewrite module requires the PCRE library. -You can either disable the module by using --without-http_rewrite_module -option, or install the PCRE library into the system, or build the PCRE library -statically from the source with nginx by using --with-pcre= option. - -END - - exit 1 - fi -fi - - -if [ $HTTP_GZIP = YES ]; then - if [ $ZLIB = NONE -o $ZLIB = NO ]; then - -cat << END -$0: error: the HTTP gzip module requires the zlib library. -You can either disable the module by using --without-http_gzip_module -option, or install the zlib library into the system, or build the zlib library -statically from the source with nginx by using --with-zlib= option. - -END - - exit 1 - fi -fi - - -if [ $HTTP_SSL = YES ]; then - if [ $OPENSSL = NONE -o $OPENSSL = NO ]; then - -cat << END -$0: error: the HTTP SSL module requires the OpenSSL library. -You can either do not enable the module, or install the OpenSSL library -into the system, or build the OpenSSL library statically from the source -with nginx by using --with-openssl= option. - -END - - exit 1 - fi -fi - - cat << END nginx path prefix: "$NGX_PREFIX" nginx binary file: "$NGX_SBIN_PATH" diff --git a/auto/unix b/auto/unix --- a/auto/unix +++ b/auto/unix @@ -64,6 +64,21 @@ ngx_param=NGX_TIME_T_LEN; ngx_value=$ngx # syscalls, libc calls and some features +if [ $NGX_IPV6 = YES ]; then + ngx_feature="AF_INET6" + ngx_feature_name="NGX_HAVE_INET6" + ngx_feature_run=no + ngx_feature_incs="#include + #include + #include " + ngx_feature_path= + ngx_feature_libs= + ngx_feature_test="struct sockaddr_in6 sin6; + sin6.sin6_family = AF_INET6;" + . auto/feature +fi + + ngx_feature="setproctitle()" ngx_feature_name="NGX_HAVE_SETPROCTITLE" ngx_feature_run=no @@ -148,16 +163,6 @@ ngx_feature_test="void *p; p = memalign( . auto/feature -ngx_feature="sched_yield()" -ngx_feature_name="NGX_HAVE_SCHED_YIELD" -ngx_feature_run=no -ngx_feature_incs="#include " -ngx_feature_path= -ngx_feature_libs= -ngx_feature_test="sched_yield()" -. auto/feature - - ngx_feature="mmap(MAP_ANON|MAP_SHARED)" ngx_feature_name="NGX_HAVE_MAP_ANON" ngx_feature_run=yes @@ -175,8 +180,8 @@ ngx_feature='mmap("/dev/zero", MAP_SHARE ngx_feature_name="NGX_HAVE_MAP_DEVZERO" ngx_feature_run=yes ngx_feature_incs="#include -#include -#include " + #include + #include " ngx_feature_path= ngx_feature_libs= ngx_feature_test='void *p; int fd; @@ -190,7 +195,7 @@ ngx_feature="System V shared memory" ngx_feature_name="NGX_HAVE_SYSVSHM" ngx_feature_run=yes ngx_feature_incs="#include -#include " + #include " ngx_feature_path= ngx_feature_libs= ngx_feature_test="int id; @@ -214,7 +219,7 @@ ngx_feature="ioctl(FIONBIO)" ngx_feature_name="NGX_HAVE_FIONBIO" ngx_feature_run=no ngx_feature_incs="#include -$NGX_INCLUDE_SYS_FILIO_H" + $NGX_INCLUDE_SYS_FILIO_H" ngx_feature_path= ngx_feature_libs= ngx_feature_test="int i; i = FIONBIO" diff --git a/conf/nginx.conf b/conf/nginx.conf --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -18,8 +18,8 @@ http { include mime.types; default_type application/octet-stream; - #log_format main '$remote_addr - $remote_user [$time_local] $request ' - # '"$status" $body_bytes_sent "$http_referer" ' + #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; diff --git a/configure b/configure --- a/configure +++ b/configure @@ -3,8 +3,6 @@ # Copyright (C) Igor Sysoev -NGX_CONFIGURE=`echo $@ | sed 's/"/\\\\"/g'` - . auto/options . auto/init . auto/sources @@ -33,6 +31,12 @@ if test -z "$NGX_PLATFORM"; then NGX_PLATFORM="$NGX_SYSTEM:$NGX_RELEASE:$NGX_MACHINE"; + case "$NGX_SYSTEM" in + MINGW32_*) + NGX_PLATFORM=win32 + ;; + esac + else echo "building for $NGX_PLATFORM" NGX_SYSTEM=$NGX_PLATFORM @@ -54,6 +58,39 @@ fi . auto/modules . auto/lib/conf +case ".$NGX_PREFIX" in + .) + NGX_PREFIX=${NGX_PREFIX:-/usr/local/nginx} + have=NGX_PREFIX value="\"$NGX_PREFIX/\"" . auto/define + ;; + + .!) + NGX_PREFIX= + ;; + + *) + have=NGX_PREFIX value="\"$NGX_PREFIX/\"" . auto/define + ;; +esac + +if [ ".$NGX_CONF_PREFIX" != "." ]; then + have=NGX_CONF_PREFIX value="\"$NGX_CONF_PREFIX/\"" . auto/define +fi + +have=NGX_SBIN_PATH value="\"$NGX_SBIN_PATH\"" . auto/define +have=NGX_CONF_PATH value="\"$NGX_CONF_PATH\"" . auto/define +have=NGX_PID_PATH value="\"$NGX_PID_PATH\"" . auto/define +have=NGX_LOCK_PATH value="\"$NGX_LOCK_PATH\"" . auto/define +have=NGX_ERROR_LOG_PATH value="\"$NGX_ERROR_LOG_PATH\"" . auto/define + +have=NGX_HTTP_LOG_PATH value="\"$NGX_HTTP_LOG_PATH\"" . auto/define +have=NGX_HTTP_CLIENT_TEMP_PATH value="\"$NGX_HTTP_CLIENT_TEMP_PATH\"" +. auto/define +have=NGX_HTTP_PROXY_TEMP_PATH value="\"$NGX_HTTP_PROXY_TEMP_PATH\"" +. auto/define +have=NGX_HTTP_FASTCGI_TEMP_PATH value="\"$NGX_HTTP_FASTCGI_TEMP_PATH\"" +. auto/define + . auto/make . auto/lib/make . auto/install @@ -65,24 +102,6 @@ fi # STUB . auto/stubs -have=NGX_PREFIX value="\"$NGX_PREFIX/\"" . auto/define -have=NGX_SBIN_PATH value="\"$NGX_SBIN_PATH\"" . auto/define -have=NGX_CONF_PREFIX value="\"$NGX_CONF_PREFIX/\"" . auto/define -have=NGX_CONF_PATH value="\"$NGX_CONF_PATH\"" . auto/define -have=NGX_PID_PATH value="\"$NGX_PID_PATH\"" . auto/define -have=NGX_LOCK_PATH value="\"$NGX_LOCK_PATH\"" . auto/define -if test -n "$NGX_ERROR_LOG_PATH"; then - have=NGX_ERROR_LOG_PATH value="\"$NGX_ERROR_LOG_PATH\"" . auto/define -fi - -have=NGX_HTTP_LOG_PATH value="\"$NGX_HTTP_LOG_PATH\"" . auto/define -have=NGX_HTTP_CLIENT_TEMP_PATH value="\"$NGX_HTTP_CLIENT_TEMP_PATH\"" -. auto/define -have=NGX_HTTP_PROXY_TEMP_PATH value="\"$NGX_HTTP_PROXY_TEMP_PATH\"" -. auto/define -have=NGX_HTTP_FASTCGI_TEMP_PATH value="\"$NGX_HTTP_FASTCGI_TEMP_PATH\"" -. auto/define - have=NGX_USER value="\"$NGX_USER\"" . auto/define have=NGX_GROUP value="\"$NGX_GROUP\"" . auto/define diff --git a/src/core/nginx.c b/src/core/nginx.c --- a/src/core/nginx.c +++ b/src/core/nginx.c @@ -10,7 +10,8 @@ static ngx_int_t ngx_add_inherited_sockets(ngx_cycle_t *cycle); -static ngx_int_t ngx_getopt(ngx_cycle_t *cycle, int argc, char *const *argv); +static ngx_int_t ngx_get_options(int argc, char *const *argv); +static ngx_int_t ngx_process_options(ngx_cycle_t *cycle); static ngx_int_t ngx_save_argv(ngx_cycle_t *cycle, int argc, char *const *argv); static void *ngx_core_module_create_conf(ngx_cycle_t *cycle); static char *ngx_core_module_init_conf(ngx_cycle_t *cycle, void *conf); @@ -180,10 +181,16 @@ ngx_module_t ngx_core_module = { }; -ngx_uint_t ngx_max_module; +ngx_uint_t ngx_max_module; -static ngx_uint_t ngx_show_version; -static ngx_uint_t ngx_show_configure; +static ngx_uint_t ngx_show_help; +static ngx_uint_t ngx_show_version; +static ngx_uint_t ngx_show_configure; +static u_char *ngx_prefix; +static u_char *ngx_conf_file; +static u_char *ngx_conf_params; +static char *ngx_signal; + static char **ngx_os_environ; @@ -196,6 +203,50 @@ main(int argc, char *const *argv) ngx_cycle_t *cycle, init_cycle; ngx_core_conf_t *ccf; + if (ngx_get_options(argc, argv) != NGX_OK) { + return 1; + } + + if (ngx_show_version) { + ngx_log_stderr(0, "nginx version: " NGINX_VER); + + if (ngx_show_help) { + ngx_log_stderr(0, + "Usage: nginx [-?hvVt] [-s signal] [-c filename] " + "[-p prefix] [-g directives]" CRLF CRLF + "Options:" CRLF + " -?,-h : this help" CRLF + " -v : show version and exit" CRLF + " -V : show version and configure options then exit" + CRLF + " -t : test configuration and exit" CRLF + " -s signal : send signal to a master process: " + "stop, quit, reopen, reload" CRLF +#ifdef NGX_PREFIX + " -p prefix : set prefix path (default: " + NGX_PREFIX ")" CRLF +#else + " -p prefix : set prefix path (default: NONE)" CRLF +#endif + " -c filename : set configuration file (default: " + NGX_CONF_PATH ")" CRLF + " -g directives : set global directives out of configuration " + "file" CRLF + ); + } + + if (ngx_show_configure) { +#ifdef NGX_COMPILER + ngx_log_stderr(0, "built by " NGX_COMPILER); +#endif + ngx_log_stderr(0, "configure arguments:" NGX_CONFIGURE); + } + + if (!ngx_test_config) { + return 0; + } + } + #if (NGX_FREEBSD) ngx_debug_init(); #endif @@ -210,7 +261,7 @@ main(int argc, char *const *argv) ngx_pid = ngx_getpid(); - log = ngx_log_init(); + log = ngx_log_init(ngx_prefix); if (log == NULL) { return 1; } @@ -220,7 +271,10 @@ main(int argc, char *const *argv) ngx_ssl_init(log); #endif - /* init_cycle->log is required for signal handlers and ngx_getopt() */ + /* + * init_cycle->log is required for signal handlers and + * ngx_process_options() + */ ngx_memzero(&init_cycle, sizeof(ngx_cycle_t)); init_cycle.log = log; @@ -235,39 +289,10 @@ main(int argc, char *const *argv) return 1; } - if (ngx_getopt(&init_cycle, argc, ngx_argv) != NGX_OK) { + if (ngx_process_options(&init_cycle) != NGX_OK) { return 1; } - if (ngx_show_version) { - ngx_write_fd(ngx_stderr_fileno, "nginx version: " NGINX_VER CRLF, - sizeof("nginx version: " NGINX_VER CRLF) - 1); - - if (ngx_show_configure) { -#ifdef NGX_COMPILER - ngx_write_fd(ngx_stderr_fileno, "built by " NGX_COMPILER CRLF, - sizeof("built by " NGX_COMPILER CRLF) - 1); -#endif - -#ifndef __WATCOMC__ - - /* OpenWatcomC could not build the long NGX_CONFIGURE string */ - - ngx_write_fd(ngx_stderr_fileno, - "configure arguments: " NGX_CONFIGURE CRLF, - sizeof("configure arguments :" NGX_CONFIGURE CRLF) - 1); -#endif - } - - if (!ngx_test_config) { - return 0; - } - } - - if (ngx_test_config) { - log->log_level = NGX_LOG_INFO; - } - if (ngx_os_init(log) != NGX_OK) { return 1; } @@ -292,45 +317,34 @@ main(int argc, char *const *argv) cycle = ngx_init_cycle(&init_cycle); if (cycle == NULL) { if (ngx_test_config) { - ngx_log_error(NGX_LOG_EMERG, log, 0, - "the configuration file %s test failed", - init_cycle.conf_file.data); + ngx_log_stderr(0, "configuration file %s test failed", + init_cycle.conf_file.data); } return 1; } if (ngx_test_config) { - ngx_log_error(NGX_LOG_INFO, log, 0, - "the configuration file %s was tested successfully", - cycle->conf_file.data); + ngx_log_stderr(0, "configuration file %s test is successful", + cycle->conf_file.data); return 0; } + if (ngx_signal) { + return ngx_signal_process(cycle, ngx_signal); + } + ngx_os_status(cycle->log); ngx_cycle = cycle; ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); - ngx_process = ccf->master ? NGX_PROCESS_MASTER : NGX_PROCESS_SINGLE; - -#if (NGX_WIN32) - -#if 0 - - TODO: + if (ccf->master && ngx_process == NGX_PROCESS_SINGLE) { + ngx_process = NGX_PROCESS_MASTER; + } - if (ccf->run_as_service) { - if (ngx_service(cycle->log) != NGX_OK) { - return 1; - } - - return 0; - } -#endif - -#else +#if !(NGX_WIN32) if (ngx_init_signals(cycle->log) != NGX_OK) { return 1; @@ -344,17 +358,28 @@ main(int argc, char *const *argv) ngx_daemonized = 1; } +#endif + if (ngx_create_pidfile(&ccf->pid, cycle->log) != NGX_OK) { return 1; } -#endif + if (cycle->log->file->fd != ngx_stderr) { - if (ngx_process == NGX_PROCESS_MASTER) { - ngx_master_process_cycle(cycle); + if (ngx_set_stderr(cycle->log->file->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + ngx_set_stderr_n " failed"); + return 1; + } + } + + ngx_use_stderr = 0; + + if (ngx_process == NGX_PROCESS_SINGLE) { + ngx_single_process_cycle(cycle); } else { - ngx_single_process_cycle(cycle); + ngx_master_process_cycle(cycle); } return 0; @@ -379,7 +404,7 @@ ngx_add_inherited_sockets(ngx_cycle_t *c if (ngx_array_init(&cycle->listening, cycle->pool, 10, sizeof(ngx_listening_t)) - == NGX_ERROR) + != NGX_OK) { return NGX_ERROR; } @@ -524,6 +549,8 @@ ngx_exec_new_binary(ngx_cycle_t *cycle, ngx_core_conf_t *ccf; ngx_listening_t *ls; + ngx_memzero(&ctx, sizeof(ngx_exec_ctx_t)); + ctx.path = argv[0]; ctx.name = "new binary process"; ctx.argv = argv; @@ -607,64 +634,118 @@ ngx_exec_new_binary(ngx_cycle_t *cycle, static ngx_int_t -ngx_getopt(ngx_cycle_t *cycle, int argc, char *const *argv) +ngx_get_options(int argc, char *const *argv) { - ngx_int_t i; + u_char *p; + ngx_int_t i; for (i = 1; i < argc; i++) { - if (argv[i][0] != '-') { - ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, - "invalid option: \"%s\"", argv[i]); + + p = (u_char *) argv[i]; + + if (*p++ != '-') { + ngx_log_stderr(0, "invalid option: \"%s\"", argv[i]); return NGX_ERROR; } - switch (argv[i][1]) { + while (*p) { + + switch (*p++) { + + case '?': + case 'h': + ngx_show_version = 1; + ngx_show_help = 1; + break; + + case 'v': + ngx_show_version = 1; + break; + + case 'V': + ngx_show_version = 1; + ngx_show_configure = 1; + break; - case 'v': - ngx_show_version = 1; - break; + case 't': + ngx_test_config = 1; + break; + + case 'p': + if (*p) { + ngx_prefix = p; + goto next; + } + + if (argv[++i]) { + ngx_prefix = (u_char *) argv[i]; + goto next; + } + + ngx_log_stderr(0, "option \"-p\" requires directory name"); + return NGX_ERROR; + + case 'c': + if (*p) { + ngx_conf_file = p; + goto next; + } - case 'V': - ngx_show_version = 1; - ngx_show_configure = 1; - break; + if (argv[++i]) { + ngx_conf_file = (u_char *) argv[i]; + goto next; + } + + ngx_log_stderr(0, "option \"-c\" requires file name"); + return NGX_ERROR; + + case 'g': + if (*p) { + ngx_conf_params = p; + goto next; + } + + if (argv[++i]) { + ngx_conf_params = (u_char *) argv[i]; + goto next; + } + + ngx_log_stderr(0, "option \"-g\" requires parameter"); + return NGX_ERROR; - case 't': - ngx_test_config = 1; - break; + case 's': + if (*p) { + ngx_signal = (char *) p; + + } else if (argv[++i]) { + ngx_signal = argv[i]; + + } else { + ngx_log_stderr(0, "option \"-s\" requires parameter"); + return NGX_ERROR; + } - case 'c': - if (argv[i + 1] == NULL) { - ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, - "the option \"-c\" requires file name"); + if (ngx_strcmp(ngx_signal, "stop") == 0 + || ngx_strcmp(ngx_signal, "quit") == 0 + || ngx_strcmp(ngx_signal, "reopen") == 0 + || ngx_strcmp(ngx_signal, "reload") == 0) + { + ngx_process = NGX_PROCESS_SIGNALLER; + goto next; + } + + ngx_log_stderr(0, "invalid option: \"-s %s\"", ngx_signal); + return NGX_ERROR; + + default: + ngx_log_stderr(0, "invalid option: \"%c\"", *(p - 1)); return NGX_ERROR; } - - cycle->conf_file.data = (u_char *) argv[++i]; - cycle->conf_file.len = ngx_strlen(cycle->conf_file.data); - break; - - case 'g': - if (argv[i + 1] == NULL) { - ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, - "the option \"-g\" requires parameter"); - return NGX_ERROR; - } + } - cycle->conf_param.data = (u_char *) argv[++i]; - cycle->conf_param.len = ngx_strlen(cycle->conf_param.data); - break; + next: - default: - ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, - "invalid option: \"%s\"", argv[i]); - return NGX_ERROR; - } - } - - if (cycle->conf_file.data == NULL) { - cycle->conf_file.len = sizeof(NGX_CONF_PATH) - 1; - cycle->conf_file.data = (u_char *) NGX_CONF_PATH; + continue; } return NGX_OK; @@ -713,6 +794,106 @@ ngx_save_argv(ngx_cycle_t *cycle, int ar } +static ngx_int_t +ngx_process_options(ngx_cycle_t *cycle) +{ + u_char *p; + size_t len; + + if (ngx_prefix) { + len = ngx_strlen(ngx_prefix); + p = ngx_prefix; + + if (!ngx_path_separator(*p)) { + p = ngx_pnalloc(cycle->pool, len + 1); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(p, ngx_prefix, len); + p[len++] = '/'; + } + + cycle->conf_prefix.len = len; + cycle->conf_prefix.data = p; + cycle->prefix.len = len; + cycle->prefix.data = p; + + } else { + +#ifndef NGX_PREFIX + + p = ngx_pnalloc(cycle->pool, NGX_MAX_PATH); + if (p == NULL) { + return NGX_ERROR; + } + + if (ngx_getcwd(p, NGX_MAX_PATH) == 0) { + ngx_log_stderr(ngx_errno, "[emerg]: " ngx_getcwd_n " failed"); + return NGX_ERROR; + } + + len = ngx_strlen(p); + + p[len++] = '/'; + + cycle->conf_prefix.len = len; + cycle->conf_prefix.data = p; + cycle->prefix.len = len; + cycle->prefix.data = p; + +#else + +#ifdef NGX_CONF_PREFIX + cycle->conf_prefix.len = sizeof(NGX_CONF_PREFIX) - 1; + cycle->conf_prefix.data = (u_char *) NGX_CONF_PREFIX; +#else + cycle->conf_prefix.len = sizeof(NGX_PREFIX) - 1; + cycle->conf_prefix.data = (u_char *) NGX_PREFIX; +#endif + cycle->prefix.len = sizeof(NGX_PREFIX) - 1; + cycle->prefix.data = (u_char *) NGX_PREFIX; + +#endif + } + + if (ngx_conf_file) { + cycle->conf_file.len = ngx_strlen(ngx_conf_file); + cycle->conf_file.data = ngx_conf_file; + + } else { + cycle->conf_file.len = sizeof(NGX_CONF_PATH) - 1; + cycle->conf_file.data = (u_char *) NGX_CONF_PATH; + } + + if (ngx_conf_full_name(cycle, &cycle->conf_file, 0) != NGX_OK) { + return NGX_ERROR; + } + + for (p = cycle->conf_file.data + cycle->conf_file.len - 1; + p > cycle->conf_file.data; + p--) + { + if (ngx_path_separator(*p)) { + cycle->conf_prefix.len = p - ngx_cycle->conf_file.data + 1; + cycle->conf_prefix.data = ngx_cycle->conf_file.data; + break; + } + } + + if (ngx_conf_params) { + cycle->conf_param.len = ngx_strlen(ngx_conf_params); + cycle->conf_param.data = ngx_conf_params; + } + + if (ngx_test_config) { + cycle->log->log_level = NGX_LOG_INFO; + } + + return NGX_OK; +} + + static void * ngx_core_module_create_conf(ngx_cycle_t *cycle) { @@ -796,6 +977,27 @@ ngx_core_module_init_conf(ngx_cycle_t *c #endif + + if (ccf->pid.len == 0) { + ccf->pid.len = sizeof(NGX_PID_PATH) - 1; + ccf->pid.data = (u_char *) NGX_PID_PATH; + } + + if (ngx_conf_full_name(cycle, &ccf->pid, 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + + ccf->oldpid.len = ccf->pid.len + sizeof(NGX_OLDPID_EXT); + + ccf->oldpid.data = ngx_pnalloc(cycle->pool, ccf->oldpid.len); + if (ccf->oldpid.data == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memcpy(ngx_cpymem(ccf->oldpid.data, ccf->pid.data, ccf->pid.len), + NGX_OLDPID_EXT, sizeof(NGX_OLDPID_EXT)); + + #if !(NGX_WIN32) if (ccf->user == (uid_t) NGX_CONF_UNSET_UINT && geteuid() == 0) { @@ -824,32 +1026,13 @@ ngx_core_module_init_conf(ngx_cycle_t *c ccf->group = grp->gr_gid; } - if (ccf->pid.len == 0) { - ccf->pid.len = sizeof(NGX_PID_PATH) - 1; - ccf->pid.data = (u_char *) NGX_PID_PATH; - } - - if (ngx_conf_full_name(cycle, &ccf->pid, 0) == NGX_ERROR) { - return NGX_CONF_ERROR; - } - - ccf->oldpid.len = ccf->pid.len + sizeof(NGX_OLDPID_EXT); - - ccf->oldpid.data = ngx_pnalloc(cycle->pool, ccf->oldpid.len); - if (ccf->oldpid.data == NULL) { - return NGX_CONF_ERROR; - } - - ngx_memcpy(ngx_cpymem(ccf->oldpid.data, ccf->pid.data, ccf->pid.len), - NGX_OLDPID_EXT, sizeof(NGX_OLDPID_EXT)); - if (ccf->lock_file.len == 0) { ccf->lock_file.len = sizeof(NGX_LOCK_PATH) - 1; ccf->lock_file.data = (u_char *) NGX_LOCK_PATH; } - if (ngx_conf_full_name(cycle, &ccf->lock_file, 0) == NGX_ERROR) { + if (ngx_conf_full_name(cycle, &ccf->lock_file, 0) != NGX_OK) { return NGX_CONF_ERROR; } diff --git a/src/core/nginx.h b/src/core/nginx.h --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -8,7 +8,8 @@ #define _NGINX_H_INCLUDED_ -#define NGINX_VERSION "0.7.19" +#define nginx_version 8005 +#define NGINX_VERSION "0.8.5" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff --git a/src/core/ngx_buf.c b/src/core/ngx_buf.c --- a/src/core/ngx_buf.c +++ b/src/core/ngx_buf.c @@ -201,12 +201,6 @@ ngx_chain_update_chains(ngx_chain_t **fr break; } -#if (NGX_HAVE_WRITE_ZEROCOPY) - if ((*busy)->buf->zerocopy_busy) { - break; - } -#endif - if ((*busy)->buf->tag != tag) { *busy = (*busy)->next; continue; diff --git a/src/core/ngx_buf.h b/src/core/ngx_buf.h --- a/src/core/ngx_buf.h +++ b/src/core/ngx_buf.h @@ -51,8 +51,6 @@ struct ngx_buf_s { unsigned last_shadow:1; unsigned temp_file:1; - unsigned zerocopy_busy:1; - /* STUB */ int num; }; diff --git a/src/core/ngx_conf_file.c b/src/core/ngx_conf_file.c --- a/src/core/ngx_conf_file.c +++ b/src/core/ngx_conf_file.c @@ -12,6 +12,7 @@ static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last); static ngx_int_t ngx_conf_read_token(ngx_conf_t *cf); static char *ngx_conf_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static ngx_int_t ngx_conf_test_full_name(ngx_str_t *name); static void ngx_conf_flush_files(ngx_cycle_t *cycle); @@ -61,6 +62,7 @@ static ngx_uint_t argument_number[] = { char * ngx_conf_param(ngx_conf_t *cf) { + char *rv; ngx_str_t *param; ngx_buf_t b; ngx_conf_file_t conf_file; @@ -82,13 +84,17 @@ ngx_conf_param(ngx_conf_t *cf) b.temporary = 1; conf_file.file.fd = NGX_INVALID_FILE; - conf_file.file.name.data = (u_char *) "command line"; - conf_file.line = 1; + conf_file.file.name.data = NULL; + conf_file.line = 0; cf->conf_file = &conf_file; cf->conf_file->buffer = &b; - return ngx_conf_parse(cf, NULL); + rv = ngx_conf_parse(cf, NULL); + + cf->conf_file = NULL; + + return rv; } @@ -98,8 +104,8 @@ ngx_conf_parse(ngx_conf_t *cf, ngx_str_t char *rv; ngx_fd_t fd; ngx_int_t rc; - ngx_buf_t *b; - ngx_conf_file_t *prev; + ngx_buf_t buf; + ngx_conf_file_t *prev, conf_file; enum { parse_file = 0, parse_block, @@ -125,32 +131,24 @@ ngx_conf_parse(ngx_conf_t *cf, ngx_str_t prev = cf->conf_file; - cf->conf_file = ngx_palloc(cf->pool, sizeof(ngx_conf_file_t)); - if (cf->conf_file == NULL) { - return NGX_CONF_ERROR; - } + cf->conf_file = &conf_file; if (ngx_fd_info(fd, &cf->conf_file->file.info) == -1) { ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, ngx_fd_info_n " \"%s\" failed", filename->data); } - b = ngx_calloc_buf(cf->pool); - if (b == NULL) { - return NGX_CONF_ERROR; + cf->conf_file->buffer = &buf; + + buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log); + if (buf.start == NULL) { + goto failed; } - cf->conf_file->buffer = b; - - b->start = ngx_alloc(NGX_CONF_BUFFER, cf->log); - if (b->start == NULL) { - return NGX_CONF_ERROR; - } - - b->pos = b->start; - b->last = b->start; - b->end = b->last + NGX_CONF_BUFFER; - b->temporary = 1; + buf.pos = buf.start; + buf.last = buf.start; + buf.end = buf.last + NGX_CONF_BUFFER; + buf.temporary = 1; cf->conf_file->file.fd = fd; cf->conf_file->file.name.len = filename->len; @@ -256,9 +254,9 @@ failed: done: if (filename) { - ngx_free(cf->conf_file->buffer->start); - - cf->conf_file = prev; + if (cf->conf_file->buffer->start) { + ngx_free(cf->conf_file->buffer->start); + } if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, @@ -266,6 +264,8 @@ done: cf->conf_file->file.name.data); return NGX_CONF_ERROR; } + + cf->conf_file = prev; } if (rc == NGX_ERROR) { @@ -447,7 +447,9 @@ ngx_conf_read_token(ngx_conf_t *cf) last_space = 1; sharp_comment = 0; variable = 0; - quoted = s_quoted = d_quoted = 0; + quoted = 0; + s_quoted = 0; + d_quoted = 0; cf->args->nelts = 0; b = cf->conf_file->buffer; @@ -747,7 +749,7 @@ ngx_conf_include(ngx_conf_t *cf, ngx_com ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); - if (ngx_conf_full_name(cf->cycle, &file, 1) == NGX_ERROR) { + if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) { return NGX_CONF_ERROR; } @@ -801,46 +803,94 @@ ngx_int_t ngx_conf_full_name(ngx_cycle_t *cycle, ngx_str_t *name, ngx_uint_t conf_prefix) { size_t len; - u_char *p, *prefix; - ngx_str_t old; + u_char *p, *n, *prefix; + ngx_int_t rc; + + rc = ngx_conf_test_full_name(name); + + if (rc == NGX_OK) { + return rc; + } + + if (conf_prefix) { + len = cycle->conf_prefix.len; + prefix = cycle->conf_prefix.data; + + } else { + len = cycle->prefix.len; + prefix = cycle->prefix.data; + } + +#if (NGX_WIN32) + + if (rc == 2) { + len = rc; + } + +#endif + + n = ngx_pnalloc(cycle->pool, len + name->len + 1); + if (n == NULL) { + return NGX_ERROR; + } + + p = ngx_cpymem(n, prefix, len); + ngx_cpystrn(p, name->data, name->len + 1); + + name->len += len; + name->data = n; + + return NGX_OK; +} + + +static ngx_int_t +ngx_conf_test_full_name(ngx_str_t *name) +{ +#if (NGX_WIN32) + u_char c0, c1; + + c0 = name->data[0]; + + if (name->len < 2) { + if (c0 == '/') { + return 2; + } + + return NGX_DECLINED; + } + + c1 = name->data[1]; + + if (c1 == ':') { + c0 |= 0x20; + + if ((c0 >= 'a' && c0 <= 'z')) { + return NGX_OK; + } + + return NGX_DECLINED; + } + + if (c1 == '/') { + return NGX_OK; + } + + if (c0 == '/') { + return 2; + } + + return NGX_DECLINED; + +#else if (name->data[0] == '/') { return NGX_OK; } -#if (NGX_WIN32) - - if (name->len > 2 - && name->data[1] == ':' - && ((name->data[0] >= 'a' && name->data[0] <= 'z') - || (name->data[0] >= 'A' && name->data[0] <= 'Z'))) - { - return NGX_OK; - } + return NGX_DECLINED; #endif - - old = *name; - - if (conf_prefix) { - len = sizeof(NGX_CONF_PREFIX) - 1; - prefix = (u_char *) NGX_CONF_PREFIX; - - } else { - len = cycle->root.len; - prefix = cycle->root.data; - } - - name->len = len + old.len; - name->data = ngx_pnalloc(cycle->pool, name->len + 1); - if (name->data == NULL) { - return NGX_ERROR; - } - - p = ngx_cpymem(name->data, prefix, len); - ngx_cpystrn(p, old.data, old.len + 1); - - return NGX_OK; } @@ -857,10 +907,10 @@ ngx_conf_open_file(ngx_cycle_t *cycle, n full.data = NULL; #endif - if (name) { + if (name->len) { full = *name; - if (ngx_conf_full_name(cycle, &full, 0) == NGX_ERROR) { + if (ngx_conf_full_name(cycle, &full, 0) != NGX_OK) { return NULL; } @@ -893,14 +943,13 @@ ngx_conf_open_file(ngx_cycle_t *cycle, n return NULL; } - if (name) { + if (name->len) { file->fd = NGX_INVALID_FILE; file->name = full; } else { - file->fd = ngx_stderr_fileno; - file->name.len = 0; - file->name.data = NULL; + file->fd = ngx_stderr; + file->name = *name; } file->buffer = NULL; @@ -912,6 +961,7 @@ ngx_conf_open_file(ngx_cycle_t *cycle, n static void ngx_conf_flush_files(ngx_cycle_t *cycle) { + ssize_t n, len; ngx_uint_t i; ngx_list_part_t *part; ngx_open_file_t *file; @@ -932,11 +982,24 @@ ngx_conf_flush_files(ngx_cycle_t *cycle) i = 0; } - if (file[i].buffer == NULL || file[i].pos - file[i].buffer == 0) { + len = file[i].pos - file[i].buffer; + + if (file[i].buffer == NULL || len == 0) { continue; } - ngx_write_fd(file[i].fd, file[i].buffer, file[i].pos - file[i].buffer); + n = ngx_write_fd(file[i].fd, file[i].buffer, len); + + if (n == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_write_fd_n " to \"%s\" failed", + file[i].name.data); + + } else if (n != len) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz", + file[i].name.data, n, len); + } } } @@ -945,31 +1008,33 @@ void ngx_cdecl ngx_conf_log_error(ngx_uint_t level, ngx_conf_t *cf, ngx_err_t err, char *fmt, ...) { - u_char errstr[NGX_MAX_CONF_ERRSTR], *buf, *last; + u_char errstr[NGX_MAX_CONF_ERRSTR], *p, *last; va_list args; last = errstr + NGX_MAX_CONF_ERRSTR; va_start(args, fmt); - buf = ngx_vsnprintf(errstr, last - errstr, fmt, args); + p = ngx_vslprintf(errstr, last, fmt, args); va_end(args); - *buf = '\0'; - if (err) { - buf = ngx_snprintf(buf, last - buf - 1, " (%d: ", err); - buf = ngx_strerror_r(err, buf, last - buf - 1); - *buf++ = ')'; - *buf = '\0'; + p = ngx_log_errno(p, last, err); } if (cf->conf_file == NULL) { - ngx_log_error(level, cf->log, 0, "%s", errstr); + ngx_log_error(level, cf->log, 0, "%*s", p - errstr, errstr); return; } - ngx_log_error(level, cf->log, 0, "%s in %s:%ui", - errstr, cf->conf_file->file.name.data, cf->conf_file->line); + if (cf->conf_file->file.fd == NGX_INVALID_FILE) { + ngx_log_error(level, cf->log, 0, "%*s in command line", + p - errstr, errstr); + return; + } + + ngx_log_error(level, cf->log, 0, "%*s in %s:%ui", + p - errstr, errstr, + cf->conf_file->file.name.data, cf->conf_file->line); } diff --git a/src/core/ngx_conf_file.h b/src/core/ngx_conf_file.h --- a/src/core/ngx_conf_file.h +++ b/src/core/ngx_conf_file.h @@ -71,7 +71,7 @@ #define NGX_CONF_MODULE 0x464E4F43 /* "CONF" */ -#define NGX_MAX_CONF_ERRSTR 256 +#define NGX_MAX_CONF_ERRSTR 1024 struct ngx_command_s { diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c --- a/src/core/ngx_connection.c +++ b/src/core/ngx_connection.c @@ -13,11 +13,11 @@ ngx_os_io_t ngx_io; ngx_listening_t * -ngx_listening_inet_stream_socket(ngx_conf_t *cf, in_addr_t addr, in_port_t port) +ngx_create_listening(ngx_conf_t *cf, void *sockaddr, socklen_t socklen) { - size_t len; - ngx_listening_t *ls; - struct sockaddr_in *sin; + ngx_listening_t *ls; + struct sockaddr *sa; + u_char text[NGX_SOCKADDR_STRLEN]; ls = ngx_array_push(&cf->cycle->listening); if (ls == NULL) { @@ -26,34 +26,45 @@ ngx_listening_inet_stream_socket(ngx_con ngx_memzero(ls, sizeof(ngx_listening_t)); - sin = ngx_pcalloc(cf->pool, sizeof(struct sockaddr_in)); - if (sin == NULL) { + sa = ngx_palloc(cf->pool, socklen); + if (sa == NULL) { return NULL; } - sin->sin_family = AF_INET; - sin->sin_addr.s_addr = addr; - sin->sin_port = htons(port); + ngx_memcpy(sa, sockaddr, socklen); + ls->sockaddr = sa; + ls->socklen = socklen; - ls->addr_text.data = ngx_pnalloc(cf->pool, - NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1); + ls->addr_text.len = ngx_sock_ntop(sa, text, NGX_SOCKADDR_STRLEN, 1); + + ls->addr_text.data = ngx_pnalloc(cf->pool, ls->addr_text.len); if (ls->addr_text.data == NULL) { return NULL; } - len = ngx_inet_ntop(AF_INET, &addr, ls->addr_text.data, - NGX_INET_ADDRSTRLEN); - - ls->addr_text.len = ngx_sprintf(ls->addr_text.data + len, ":%d", port) - - ls->addr_text.data; + ngx_memcpy(ls->addr_text.data, text, ls->addr_text.len); ls->fd = (ngx_socket_t) -1; ls->type = SOCK_STREAM; - ls->sockaddr = (struct sockaddr *) sin; - ls->socklen = sizeof(struct sockaddr_in); - ls->addr = offsetof(struct sockaddr_in, sin_addr); - ls->addr_text_max_len = NGX_INET_ADDRSTRLEN; + + switch (ls->sockaddr->sa_family) { +#if (NGX_HAVE_INET6) + case AF_INET6: + ls->addr_text_max_len = NGX_INET6_ADDRSTRLEN; + break; +#endif + case AF_INET: + ls->addr_text_max_len = NGX_INET_ADDRSTRLEN; + break; + default: + ls->addr_text_max_len = NGX_SOCKADDR_STRLEN; + break; + } + + ls->backlog = NGX_LISTEN_BACKLOG; + ls->rcvbuf = -1; + ls->sndbuf = -1; return ls; } @@ -65,7 +76,6 @@ ngx_set_inherited_sockets(ngx_cycle_t *c size_t len; ngx_uint_t i; ngx_listening_t *ls; - struct sockaddr_in *sin; socklen_t olen; #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) ngx_err_t err; @@ -94,33 +104,39 @@ ngx_set_inherited_sockets(ngx_cycle_t *c continue; } - sin = (struct sockaddr_in *) ls[i].sockaddr; + switch (ls[i].sockaddr->sa_family) { - if (sin->sin_family != AF_INET) { +#if (NGX_HAVE_INET6) + case AF_INET6: + ls[i].addr_text_max_len = NGX_INET6_ADDRSTRLEN; + break; +#endif + + case AF_INET: + ls[i].addr_text_max_len = NGX_INET_ADDRSTRLEN; + break; + + default: ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno, "the inherited socket #%d has " - "unsupported family", ls[i].fd); + "an unsupported protocol family", ls[i].fd); ls[i].ignore = 1; continue; } - ls[i].addr_text_max_len = NGX_INET_ADDRSTRLEN; + len = ls[i].addr_text_max_len + sizeof(":65535") - 1; - ls[i].addr_text.data = ngx_pnalloc(cycle->pool, - NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1); + ls[i].addr_text.data = ngx_pnalloc(cycle->pool, len); if (ls[i].addr_text.data == NULL) { return NGX_ERROR; } - len = ngx_sock_ntop(ls[i].sockaddr, ls[i].addr_text.data, - NGX_INET_ADDRSTRLEN); + len = ngx_sock_ntop(ls[i].sockaddr, ls[i].addr_text.data, len, 1); if (len == 0) { return NGX_ERROR; } - ls[i].addr_text.len = ngx_sprintf(ls[i].addr_text.data + len, ":%d", - ntohs(sin->sin_port)) - - ls[i].addr_text.data; + ls[i].addr_text.len = len; ls[i].backlog = NGX_LISTEN_BACKLOG; @@ -278,6 +294,23 @@ ngx_open_listening_sockets(ngx_cycle_t * return NGX_ERROR; } +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + + if (ls[i].sockaddr->sa_family == AF_INET6 && ls[i].ipv6only) { + int ipv6only; + + ipv6only = (ls[i].ipv6only == 1); + + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, + (const void *) &ipv6only, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + "setsockopt(IPV6_V6ONLY) %V failed, ignored", + &ls[i].addr_text); + } + } +#endif /* TODO: close on exit */ if (!(ngx_event_flags & NGX_USE_AIO_EVENT)) { @@ -296,7 +329,7 @@ ngx_open_listening_sockets(ngx_cycle_t * } } - ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0, + ngx_log_debug2(NGX_LOG_DEBUG_CORE, log, 0, "bind() %V #%d ", &ls[i].addr_text, s); if (bind(s, ls[i].sockaddr, ls[i].socklen) == -1) { @@ -365,7 +398,7 @@ ngx_open_listening_sockets(ngx_cycle_t * void -ngx_configure_listening_socket(ngx_cycle_t *cycle) +ngx_configure_listening_sockets(ngx_cycle_t *cycle) { ngx_uint_t i; ngx_listening_t *ls; @@ -380,6 +413,8 @@ ngx_configure_listening_socket(ngx_cycle ls = cycle->listening.elts; for (i = 0; i < cycle->listening.nelts; i++) { + ls[i].log = *ls[i].logp; + if (ls[i].rcvbuf != -1) { if (setsockopt(ls[i].fd, SOL_SOCKET, SO_RCVBUF, (const void *) &ls[i].rcvbuf, sizeof(int)) @@ -536,29 +571,31 @@ ngx_close_listening_sockets(ngx_cycle_t c = ls[i].connection; - if (c->read->active) { - if (ngx_event_flags & NGX_USE_RTSIG_EVENT) { - ngx_del_conn(c, NGX_CLOSE_EVENT); + if (c) { + if (c->read->active) { + if (ngx_event_flags & NGX_USE_RTSIG_EVENT) { + ngx_del_conn(c, NGX_CLOSE_EVENT); - } else if (ngx_event_flags & NGX_USE_EPOLL_EVENT) { + } else if (ngx_event_flags & NGX_USE_EPOLL_EVENT) { - /* - * it seems that Linux-2.6.x OpenVZ sends events - * for closed shared listening sockets unless - * the events was explicity deleted - */ + /* + * it seems that Linux-2.6.x OpenVZ sends events + * for closed shared listening sockets unless + * the events was explicity deleted + */ - ngx_del_event(c->read, NGX_READ_EVENT, 0); + ngx_del_event(c->read, NGX_READ_EVENT, 0); - } else { - ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT); + } else { + ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT); + } } + + ngx_free_connection(c); + + c->fd = (ngx_socket_t) -1; } - ngx_free_connection(c); - - c->fd = (ngx_socket_t) -1; - ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0, "close listening %V #%d ", &ls[i].addr_text, ls[i].fd); @@ -566,6 +603,8 @@ ngx_close_listening_sockets(ngx_cycle_t ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno, ngx_close_socket_n " %V failed", &ls[i].addr_text); } + + ls[i].fd = (ngx_socket_t) -1; } } @@ -774,19 +813,92 @@ ngx_close_connection(ngx_connection_t *c ngx_int_t +ngx_connection_local_sockaddr(ngx_connection_t *c, ngx_str_t *s, + ngx_uint_t port) +{ + socklen_t len; + ngx_uint_t addr; + u_char sa[NGX_SOCKADDRLEN]; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + ngx_uint_t i; + struct sockaddr_in6 *sin6; +#endif + + switch (c->local_sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) c->local_sockaddr; + + for (addr = 0, i = 0; addr == 0 && i < 16; i++) { + addr |= sin6->sin6_addr.s6_addr[i]; + } + + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) c->local_sockaddr; + addr = sin->sin_addr.s_addr; + break; + } + + if (addr == 0) { + + len = NGX_SOCKADDRLEN; + + if (getsockname(c->fd, (struct sockaddr *) &sa, &len) == -1) { + ngx_connection_error(c, ngx_socket_errno, "getsockname() failed"); + return NGX_ERROR; + } + + c->local_sockaddr = ngx_palloc(c->pool, len); + if (c->local_sockaddr == NULL) { + return NGX_ERROR; + } + + c->local_socklen = len; + ngx_memcpy(c->local_sockaddr, &sa, len); + } + + if (s == NULL) { + return NGX_OK; + } + + s->len = ngx_sock_ntop(c->local_sockaddr, s->data, s->len, port); + + return NGX_OK; +} + + +ngx_int_t ngx_connection_error(ngx_connection_t *c, ngx_err_t err, char *text) { ngx_uint_t level; - if (err == NGX_ECONNRESET - && c->log_error == NGX_ERROR_IGNORE_ECONNRESET) + /* Winsock may return NGX_ECONNABORTED instead of NGX_ECONNRESET */ + + if ((err == NGX_ECONNRESET +#if (NGX_WIN32) + || err == NGX_ECONNABORTED +#endif + ) && c->log_error == NGX_ERROR_IGNORE_ECONNRESET) { return 0; } +#if (NGX_SOLARIS) + if (err == NGX_EINVAL && c->log_error == NGX_ERROR_IGNORE_EINVAL) { + return 0; + } +#endif + if (err == 0 || err == NGX_ECONNRESET -#if !(NGX_WIN32) +#if (NGX_WIN32) + || err == NGX_ECONNABORTED +#else || err == NGX_EPIPE #endif || err == NGX_ENOTCONN @@ -799,6 +911,7 @@ ngx_connection_error(ngx_connection_t *c { switch (c->log_error) { + case NGX_ERROR_IGNORE_EINVAL: case NGX_ERROR_IGNORE_ECONNRESET: case NGX_ERROR_INFO: level = NGX_LOG_INFO; diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h --- a/src/core/ngx_connection.h +++ b/src/core/ngx_connection.h @@ -19,7 +19,6 @@ struct ngx_listening_s { struct sockaddr *sockaddr; socklen_t socklen; /* size of sockaddr */ - size_t addr; /* offset to address in sockaddr */ size_t addr_text_max_len; ngx_str_t addr_text; @@ -35,6 +34,7 @@ struct ngx_listening_s { void *servers; /* array of ngx_http_in_addr_t, for example */ ngx_log_t log; + ngx_log_t *logp; size_t pool_size; /* should be here because of the AcceptEx() preread */ @@ -57,6 +57,10 @@ struct ngx_listening_s { unsigned shared:1; /* shared between threads or processes */ unsigned addr_ntop:1; +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + unsigned ipv6only:2; +#endif + #if (NGX_HAVE_DEFERRED_ACCEPT) unsigned deferred_accept:1; unsigned delete_deferred:1; @@ -70,10 +74,11 @@ struct ngx_listening_s { typedef enum { - NGX_ERROR_CRIT = 0, + NGX_ERROR_ALERT = 0, NGX_ERROR_ERR, NGX_ERROR_INFO, - NGX_ERROR_IGNORE_ECONNRESET + NGX_ERROR_IGNORE_ECONNRESET, + NGX_ERROR_IGNORE_EINVAL } ngx_connection_log_error_e; @@ -123,18 +128,18 @@ struct ngx_connection_s { ngx_ssl_connection_t *ssl; #endif -#if (NGX_HAVE_IOCP) struct sockaddr *local_sockaddr; socklen_t local_socklen; -#endif ngx_buf_t *buffer; ngx_atomic_uint_t number; + ngx_uint_t requests; + unsigned buffered:8; - unsigned log_error:2; /* ngx_connection_log_error_e */ + unsigned log_error:3; /* ngx_connection_log_error_e */ unsigned single_connection:1; unsigned unexpected_eof:1; @@ -160,18 +165,15 @@ struct ngx_connection_s { }; -#ifndef ngx_ssl_set_nosendshut -#define ngx_ssl_set_nosendshut(ssl) -#endif - - -ngx_listening_t *ngx_listening_inet_stream_socket(ngx_conf_t *cf, - in_addr_t addr, in_port_t port); +ngx_listening_t *ngx_create_listening(ngx_conf_t *cf, void *sockaddr, + socklen_t socklen); ngx_int_t ngx_set_inherited_sockets(ngx_cycle_t *cycle); ngx_int_t ngx_open_listening_sockets(ngx_cycle_t *cycle); -void ngx_configure_listening_socket(ngx_cycle_t *cycle); +void ngx_configure_listening_sockets(ngx_cycle_t *cycle); void ngx_close_listening_sockets(ngx_cycle_t *cycle); void ngx_close_connection(ngx_connection_t *c); +ngx_int_t ngx_connection_local_sockaddr(ngx_connection_t *c, ngx_str_t *s, + ngx_uint_t port); ngx_int_t ngx_connection_error(ngx_connection_t *c, ngx_err_t err, char *text); ngx_connection_t *ngx_get_connection(ngx_socket_t s, ngx_log_t *log); diff --git a/src/core/ngx_cpuinfo.c b/src/core/ngx_cpuinfo.c --- a/src/core/ngx_cpuinfo.c +++ b/src/core/ngx_cpuinfo.c @@ -72,7 +72,7 @@ void ngx_cpuinfo(void) { u_char *vendor; - uint32_t vbuf[5], cpu[4]; + uint32_t vbuf[5], cpu[4], model; vbuf[0] = 0; vbuf[1] = 0; @@ -103,8 +103,10 @@ ngx_cpuinfo(void) case 6: ngx_cacheline_size = 32; - if ((cpu[0] & 0xf0) >= 0xd0) { - /* Intel Core */ + model = ((cpu[0] & 0xf0000) >> 8) | (cpu[0] & 0xf0); + + if (model >= 0xd0) { + /* Intel Core, Core 2, Atom */ ngx_cacheline_size = 64; } diff --git a/src/core/ngx_cycle.c b/src/core/ngx_cycle.c --- a/src/core/ngx_cycle.c +++ b/src/core/ngx_cycle.c @@ -9,9 +9,11 @@ #include -static ngx_int_t ngx_test_lockfile(u_char *file, ngx_log_t *log); static void ngx_destroy_cycle_pools(ngx_conf_t *conf); static ngx_int_t ngx_cmp_sockaddr(struct sockaddr *sa1, struct sockaddr *sa2); +static ngx_int_t ngx_init_zone_pool(ngx_cycle_t *cycle, + ngx_shm_zone_t *shm_zone); +static ngx_int_t ngx_test_lockfile(u_char *file, ngx_log_t *log); static void ngx_clean_old_cycles(ngx_event_t *ev); @@ -32,11 +34,7 @@ ngx_tls_key_t ngx_core_tls_key; static ngx_connection_t dumb; /* STUB */ -#ifdef NGX_ERROR_LOG_PATH static ngx_str_t error_log = ngx_string(NGX_ERROR_LOG_PATH); -#else -static ngx_str_t error_log = ngx_null_string; -#endif ngx_cycle_t * @@ -44,7 +42,6 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) { void *rv; char **senv, **env; - u_char *lock_file; ngx_uint_t i, n; ngx_log_t *log; ngx_time_t *tp; @@ -52,7 +49,6 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) ngx_pool_t *pool; ngx_cycle_t *cycle, **old; ngx_shm_zone_t *shm_zone, *oshm_zone; - ngx_slab_pool_t *shpool; ngx_list_part_t *part, *opart; ngx_open_file_t *file; ngx_listening_t *ls, *nls; @@ -86,10 +82,22 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) cycle->pool = pool; cycle->log = log; + cycle->new_log.log_level = NGX_LOG_ERR; cycle->old_cycle = old_cycle; - cycle->root.len = sizeof(NGX_PREFIX) - 1; - cycle->root.data = (u_char *) NGX_PREFIX; + cycle->conf_prefix.len = old_cycle->conf_prefix.len; + cycle->conf_prefix.data = ngx_pstrdup(pool, &old_cycle->conf_prefix); + if (cycle->conf_prefix.data == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + cycle->prefix.len = old_cycle->prefix.len; + cycle->prefix.data = ngx_pstrdup(pool, &old_cycle->prefix); + if (cycle->prefix.data == NULL) { + ngx_destroy_pool(pool); + return NULL; + } cycle->conf_file.len = old_cycle->conf_file.len; cycle->conf_file.data = ngx_pnalloc(pool, old_cycle->conf_file.len + 1); @@ -100,15 +108,12 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) ngx_cpystrn(cycle->conf_file.data, old_cycle->conf_file.data, old_cycle->conf_file.len + 1); - cycle->conf_param.len = old_cycle->conf_param.len; - cycle->conf_param.data = ngx_pnalloc(pool, old_cycle->conf_param.len); + cycle->conf_param.data = ngx_pstrdup(pool, &old_cycle->conf_param); if (cycle->conf_param.data == NULL) { ngx_destroy_pool(pool); return NULL; } - ngx_memcpy(cycle->conf_param.data, old_cycle->conf_param.data, - old_cycle->conf_param.len); n = old_cycle->pathes.nelts ? old_cycle->pathes.nelts : 10; @@ -136,7 +141,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) } if (ngx_list_init(&cycle->open_files, pool, n, sizeof(ngx_open_file_t)) - == NGX_ERROR) + != NGX_OK) { ngx_destroy_pool(pool); return NULL; @@ -155,22 +160,12 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) } if (ngx_list_init(&cycle->shared_memory, pool, n, sizeof(ngx_shm_zone_t)) - == NGX_ERROR) + != NGX_OK) { ngx_destroy_pool(pool); return NULL; } - - cycle->new_log = ngx_log_create_errlog(cycle, NULL); - if (cycle->new_log == NULL) { - ngx_destroy_pool(pool); - return NULL; - } - - cycle->new_log->file->name = error_log; - - n = old_cycle->listening.nelts ? old_cycle->listening.nelts : 10; cycle->listening.elts = ngx_pcalloc(pool, n * sizeof(ngx_listening_t)); @@ -221,7 +216,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) if (module->create_conf) { rv = module->create_conf(cycle); - if (rv == NGX_CONF_ERROR) { + if (rv == NULL) { ngx_destroy_pool(pool); return NULL; } @@ -270,12 +265,10 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) } if (ngx_test_config) { - ngx_log_error(NGX_LOG_INFO, log, 0, - "the configuration file %s syntax is ok", - cycle->conf_file.data); + ngx_log_stderr(0, "the configuration file %s syntax is ok", + cycle->conf_file.data); } - for (i = 0; ngx_modules[i]; i++) { if (ngx_modules[i]->type != NGX_CORE_MODULE) { continue; @@ -293,11 +286,12 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) } } + if (ngx_process == NGX_PROCESS_SIGNALLER) { + return cycle; + } ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); -#if !(NGX_WIN32) - if (ngx_test_config) { if (ngx_create_pidfile(&ccf->pid, log) != NGX_OK) { @@ -326,8 +320,6 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) } } -#endif - if (ngx_test_lockfile(cycle->lock_file.data, log) != NGX_OK) { goto failed; @@ -339,6 +331,13 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) } + if (cycle->new_log.file == NULL) { + cycle->new_log.file = ngx_conf_open_file(cycle, &error_log); + if (cycle->new_log.file == NULL) { + goto failed; + } + } + /* open the new files */ part = &cycle->open_files.part; @@ -355,12 +354,13 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) i = 0; } - if (file[i].name.data == NULL) { + if (file[i].name.len == 0) { continue; } - file[i].fd = ngx_open_file(file[i].name.data, NGX_FILE_RDWR, - NGX_FILE_CREATE_OR_OPEN|NGX_FILE_APPEND, + file[i].fd = ngx_open_file(file[i].name.data, + NGX_FILE_APPEND, + NGX_FILE_CREATE_OR_OPEN, NGX_FILE_DEFAULT_ACCESS); ngx_log_debug3(NGX_LOG_DEBUG_CORE, log, 0, @@ -374,14 +374,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) goto failed; } -#if (NGX_WIN32) - if (ngx_file_append_mode(file[i].fd) != NGX_OK) { - ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, - ngx_file_append_mode_n " \"%s\" failed", - file[i].name.data); - goto failed; - } -#else +#if !(NGX_WIN32) if (fcntl(file[i].fd, F_SETFD, FD_CLOEXEC) == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "fcntl(FD_CLOEXEC) \"%s\" failed", @@ -391,12 +384,8 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) #endif } - cycle->log = cycle->new_log; - pool->log = cycle->new_log; - - if (cycle->log->log_level == 0) { - cycle->log->log_level = NGX_LOG_ERR; - } + cycle->log = &cycle->new_log; + pool->log = &cycle->new_log; /* create shared memory */ @@ -418,7 +407,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) if (shm_zone[i].shm.size == 0) { ngx_log_error(NGX_LOG_EMERG, log, 0, "zero size shared memory zone \"%V\"", - &shm_zone[i].name); + &shm_zone[i].shm.name); goto failed; } @@ -443,12 +432,13 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) n = 0; } - if (shm_zone[i].name.len != oshm_zone[n].name.len) { + if (shm_zone[i].shm.name.len != oshm_zone[n].shm.name.len) { continue; } - if (ngx_strncmp(shm_zone[i].name.data, oshm_zone[n].name.data, - shm_zone[i].name.len) + if (ngx_strncmp(shm_zone[i].shm.name.data, + oshm_zone[n].shm.name.data, + shm_zone[i].shm.name.len) != 0) { continue; @@ -475,38 +465,10 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) goto failed; } - shpool = (ngx_slab_pool_t *) shm_zone[i].shm.addr; - - shpool->end = shm_zone[i].shm.addr + shm_zone[i].shm.size; - shpool->min_shift = 3; - -#if (NGX_HAVE_ATOMIC_OPS) - - lock_file = NULL; - -#else - - lock_file = ngx_pnalloc(cycle->pool, - cycle->lock_file.len + shm_zone[i].name.len); - - if (lock_file == NULL) { + if (ngx_init_zone_pool(cycle, &shm_zone[i]) != NGX_OK) { goto failed; } - (void) ngx_cpystrn(ngx_cpymem(lock_file, cycle->lock_file.data, - cycle->lock_file.len), - shm_zone[i].name.data, shm_zone[i].name.len + 1); - -#endif - - if (ngx_shmtx_create(&shpool->mutex, (void *) &shpool->lock, lock_file) - != NGX_OK) - { - goto failed; - } - - ngx_slab_init(shpool); - if (shm_zone[i].init(&shm_zone[i], NULL) != NGX_OK) { goto failed; } @@ -609,31 +571,20 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) } if (!ngx_test_config) { - ngx_configure_listening_socket(cycle); + ngx_configure_listening_sockets(cycle); } /* commit the new cycle configuration */ -#if !(NGX_WIN32) - - if (!ngx_test_config && cycle->log->file->fd != STDERR_FILENO) { + if (!ngx_use_stderr && cycle->log->file->fd != ngx_stderr) { - ngx_log_debug3(NGX_LOG_DEBUG_CORE, log, 0, - "dup2: %p %d \"%s\"", - cycle->log->file, - cycle->log->file->fd, cycle->log->file->name.data); - - if (dup2(cycle->log->file->fd, STDERR_FILENO) == -1) { - ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, - "dup2(STDERR) failed"); - /* fatal */ - exit(1); + if (ngx_set_stderr(cycle->log->file->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_set_stderr_n " failed"); } } -#endif - pool->log = cycle->log; for (i = 0; ngx_modules[i]; i++) { @@ -678,10 +629,10 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) n = 0; } - if (oshm_zone[i].name.len == shm_zone[n].name.len - && ngx_strncmp(oshm_zone[i].name.data, - shm_zone[n].name.data, - oshm_zone[i].name.len) + if (oshm_zone[i].shm.name.len == shm_zone[n].shm.name.len + && ngx_strncmp(oshm_zone[i].shm.name.data, + shm_zone[n].shm.name.data, + oshm_zone[i].shm.name.len) == 0) { goto live_shm_zone; @@ -702,7 +653,8 @@ old_shm_zone_done: ls = old_cycle->listening.elts; for (i = 0; i < old_cycle->listening.nelts; i++) { - if (ls[i].remain) { + + if (ls[i].remain || ls[i].fd == -1) { continue; } @@ -730,7 +682,7 @@ old_shm_zone_done: i = 0; } - if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr_fileno) { + if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr) { continue; } @@ -830,7 +782,7 @@ failed: i = 0; } - if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr_fileno) { + if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr) { continue; } @@ -876,49 +828,123 @@ ngx_destroy_cycle_pools(ngx_conf_t *conf static ngx_int_t ngx_cmp_sockaddr(struct sockaddr *sa1, struct sockaddr *sa2) { - struct sockaddr_in *sin1, *sin2; + struct sockaddr_in *sin1, *sin2; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin61, *sin62; +#endif - /* AF_INET only */ - - if (sa1->sa_family != AF_INET || sa2->sa_family != AF_INET) { + if (sa1->sa_family != sa2->sa_family) { return NGX_DECLINED; } - sin1 = (struct sockaddr_in *) sa1; - sin2 = (struct sockaddr_in *) sa2; + switch (sa1->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin61 = (struct sockaddr_in6 *) sa1; + sin62 = (struct sockaddr_in6 *) sa2; + + if (sin61->sin6_port != sin61->sin6_port) { + return NGX_DECLINED; + } + + if (ngx_memcmp(&sin61->sin6_addr, &sin62->sin6_addr, 16) != 0) { + return NGX_DECLINED; + } - if (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr) { - return NGX_DECLINED; - } + break; +#endif + + default: /* AF_INET */ + + sin1 = (struct sockaddr_in *) sa1; + sin2 = (struct sockaddr_in *) sa2; - if (sin1->sin_port != sin2->sin_port) { - return NGX_DECLINED; + if (sin1->sin_port != sin2->sin_port) { + return NGX_DECLINED; + } + + if (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr) { + return NGX_DECLINED; + } + + break; } return NGX_OK; } -#if !(NGX_WIN32) +static ngx_int_t +ngx_init_zone_pool(ngx_cycle_t *cycle, ngx_shm_zone_t *zn) +{ + u_char *file; + ngx_slab_pool_t *sp; + + sp = (ngx_slab_pool_t *) zn->shm.addr; + + if (zn->shm.exists) { + + if (sp == sp->addr) { + return NGX_OK; + } + + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, + "shared zone \"%V\" has no equal addresses: %p vs %p", + &zn->shm.name, sp->addr, sp); + return NGX_ERROR; + } + + sp->end = zn->shm.addr + zn->shm.size; + sp->min_shift = 3; + sp->addr = zn->shm.addr; + +#if (NGX_HAVE_ATOMIC_OPS) + + file = NULL; + +#else + + file = ngx_pnalloc(cycle->pool, cycle->lock_file.len + zn->shm.name.len); + if (file == NULL) { + return NGX_ERROR; + } + + (void) ngx_sprintf(file, "%V%V%Z", &cycle->lock_file, &zn->shm.name); + +#endif + + if (ngx_shmtx_create(&sp->mutex, (void *) &sp->lock, file) != NGX_OK) { + return NGX_ERROR; + } + + ngx_slab_init(sp); + + return NGX_OK; +} + ngx_int_t ngx_create_pidfile(ngx_str_t *name, ngx_log_t *log) { - size_t len; - ngx_uint_t trunc; - ngx_file_t file; - u_char pid[NGX_INT64_LEN + 2]; + size_t len; + ngx_uint_t create; + ngx_file_t file; + u_char pid[NGX_INT64_LEN + 2]; + + if (ngx_process > NGX_PROCESS_MASTER) { + return NGX_OK; + } ngx_memzero(&file, sizeof(ngx_file_t)); file.name = *name; file.log = log; - trunc = ngx_test_config ? 0 : NGX_FILE_TRUNCATE; + create = ngx_test_config ? NGX_FILE_CREATE_OR_OPEN : NGX_FILE_TRUNCATE; file.fd = ngx_open_file(file.name.data, NGX_FILE_RDWR, - NGX_FILE_CREATE_OR_OPEN|trunc, - NGX_FILE_DEFAULT_ACCESS); + create, NGX_FILE_DEFAULT_ACCESS); if (file.fd == NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, @@ -959,7 +985,57 @@ ngx_delete_pidfile(ngx_cycle_t *cycle) } } -#endif + +ngx_int_t +ngx_signal_process(ngx_cycle_t *cycle, char *sig) +{ + ssize_t n; + ngx_int_t pid; + ngx_file_t file; + ngx_core_conf_t *ccf; + u_char buf[NGX_INT64_LEN + 2]; + + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "signal process started"); + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + file.name = ccf->pid; + file.log = cycle->log; + + file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, + NGX_FILE_OPEN, NGX_FILE_DEFAULT_ACCESS); + + if (file.fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, + ngx_open_file_n " \"%s\" failed", file.name.data); + return 1; + } + + n = ngx_read_file(&file, buf, NGX_INT64_LEN + 2, 0); + + if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", file.name.data); + } + + if (n == NGX_ERROR) { + return 1; + } + + while (n-- && (buf[n] == CR || buf[n] == LF)) { /* void */ } + + pid = ngx_atoi(buf, ++n); + + if (pid == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, cycle->log, 0, + "invalid PID number \"%*s\" in \"%s\"", + n, buf, file.name.data); + return 1; + } + + return ngx_os_signal_process(cycle, sig, pid); + +} static ngx_int_t @@ -996,6 +1072,7 @@ ngx_test_lockfile(u_char *file, ngx_log_ void ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user) { + ssize_t n, len; ngx_fd_t fd; ngx_uint_t i; ngx_list_part_t *part; @@ -1015,19 +1092,32 @@ ngx_reopen_files(ngx_cycle_t *cycle, ngx i = 0; } - if (file[i].name.data == NULL) { + if (file[i].name.len == 0) { continue; } - if (file[i].buffer && file[i].pos - file[i].buffer != 0) { - ngx_write_fd(file[i].fd, file[i].buffer, - file[i].pos - file[i].buffer); + len = file[i].pos - file[i].buffer; + + if (file[i].buffer && len != 0) { + + n = ngx_write_fd(file[i].fd, file[i].buffer, len); + + if (n == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_write_fd_n " to \"%s\" failed", + file[i].name.data); + + } else if (n != len) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz", + file[i].name.data, n, len); + } + file[i].pos = file[i].buffer; } - fd = ngx_open_file(file[i].name.data, NGX_FILE_RDWR, - NGX_FILE_CREATE_OR_OPEN|NGX_FILE_APPEND, - NGX_FILE_DEFAULT_ACCESS); + fd = ngx_open_file(file[i].name.data, NGX_FILE_APPEND, + NGX_FILE_CREATE_OR_OPEN, NGX_FILE_DEFAULT_ACCESS); ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reopen file \"%s\", old:%d new:%d", @@ -1039,25 +1129,13 @@ ngx_reopen_files(ngx_cycle_t *cycle, ngx continue; } -#if (NGX_WIN32) - if (ngx_file_append_mode(fd) == NGX_ERROR) { - ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, - ngx_file_append_mode_n " \"%s\" failed", - file[i].name.data); - - if (ngx_close_file(fd) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, - ngx_close_file_n " \"%s\" failed", - file[i].name.data); - } - - continue; - } -#else +#if !(NGX_WIN32) if (user != (ngx_uid_t) NGX_CONF_UNSET_UINT) { ngx_file_info_t fi; - if (ngx_file_info((const char *) file[i].name.data, &fi) == -1) { + if (ngx_file_info((const char *) file[i].name.data, &fi) + == NGX_FILE_ERROR) + { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, ngx_file_info_n " \"%s\" failed", file[i].name.data); @@ -1158,27 +1236,29 @@ ngx_shared_memory_add(ngx_conf_t *cf, ng i = 0; } - if (name->len != shm_zone[i].name.len) { + if (name->len != shm_zone[i].shm.name.len) { continue; } - if (ngx_strncmp(name->data, shm_zone[i].name.data, name->len) != 0) { + if (ngx_strncmp(name->data, shm_zone[i].shm.name.data, name->len) + != 0) + { continue; } if (size && size != shm_zone[i].shm.size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "the size %uz of shared memory zone \"%V\" " - "conflicts with already declared size %uz", - size, &shm_zone[i].name, shm_zone[i].shm.size); + "the size %uz of shared memory zone \"%V\" " + "conflicts with already declared size %uz", + size, &shm_zone[i].shm.name, shm_zone[i].shm.size); return NULL; } if (tag != shm_zone[i].tag) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "the shared memory zone \"%V\" is " - "already declared for a different use", - &shm_zone[i].name); + "the shared memory zone \"%V\" is " + "already declared for a different use", + &shm_zone[i].shm.name); return NULL; } @@ -1194,8 +1274,9 @@ ngx_shared_memory_add(ngx_conf_t *cf, ng shm_zone->data = NULL; shm_zone->shm.log = cf->cycle->log; shm_zone->shm.size = size; + shm_zone->shm.name = *name; + shm_zone->shm.exists = 0; shm_zone->init = NULL; - shm_zone->name = *name; shm_zone->tag = tag; return shm_zone; diff --git a/src/core/ngx_cycle.h b/src/core/ngx_cycle.h --- a/src/core/ngx_cycle.h +++ b/src/core/ngx_cycle.h @@ -29,7 +29,6 @@ struct ngx_shm_zone_s { void *data; ngx_shm_t shm; ngx_shm_zone_init_pt init; - ngx_str_t name; void *tag; }; @@ -39,7 +38,7 @@ struct ngx_cycle_s { ngx_pool_t *pool; ngx_log_t *log; - ngx_log_t *new_log; + ngx_log_t new_log; ngx_connection_t **files; ngx_connection_t *free_connections; @@ -61,7 +60,8 @@ struct ngx_cycle_s { ngx_str_t conf_file; ngx_str_t conf_param; - ngx_str_t root; + ngx_str_t conf_prefix; + ngx_str_t prefix; ngx_str_t lock_file; ngx_str_t hostname; }; @@ -117,6 +117,7 @@ typedef struct { ngx_cycle_t *ngx_init_cycle(ngx_cycle_t *old_cycle); ngx_int_t ngx_create_pidfile(ngx_str_t *name, ngx_log_t *log); void ngx_delete_pidfile(ngx_cycle_t *cycle); +ngx_int_t ngx_signal_process(ngx_cycle_t *cycle, char *sig); void ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user); char **ngx_set_environment(ngx_cycle_t *cycle, ngx_uint_t *last); ngx_pid_t ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv); diff --git a/src/core/ngx_file.c b/src/core/ngx_file.c --- a/src/core/ngx_file.c +++ b/src/core/ngx_file.c @@ -259,12 +259,12 @@ ngx_conf_set_path_slot(ngx_conf_t *cf, n path->name.len--; } - if (ngx_conf_full_name(cf->cycle, &path->name, 0) == NGX_ERROR) { + if (ngx_conf_full_name(cf->cycle, &path->name, 0) != NGX_OK) { return NULL; } path->len = 0; - path->cleaner = (ngx_gc_handler_pt) cmd->post; + path->manager = (ngx_path_manager_pt) cmd->post; path->conf_file = cf->conf_file->file.name.data; path->line = cf->conf_file->line; @@ -293,6 +293,49 @@ ngx_conf_set_path_slot(ngx_conf_t *cf, n char * +ngx_conf_merge_path_value(ngx_conf_t *cf, ngx_path_t **path, ngx_path_t *prev, + ngx_path_init_t *init) +{ + if (*path) { + return NGX_CONF_OK; + } + + if (prev) { + *path = prev; + return NGX_CONF_OK; + } + + *path = ngx_palloc(cf->pool, sizeof(ngx_path_t)); + if (*path == NULL) { + return NGX_CONF_ERROR; + } + + (*path)->name = init->name; + + if (ngx_conf_full_name(cf->cycle, &(*path)->name, 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + + (*path)->level[0] = init->level[0]; + (*path)->level[1] = init->level[1]; + (*path)->level[2] = init->level[2]; + + (*path)->len = init->level[0] + (init->level[0] ? 1 : 0) + + init->level[1] + (init->level[1] ? 1 : 0) + + init->level[2] + (init->level[2] ? 1 : 0); + + (*path)->manager = NULL; + (*path)->conf_file = NULL; + + if (ngx_add_path(cf, path) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +char * ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *confp = conf; @@ -446,7 +489,9 @@ ngx_create_pathes(ngx_cycle_t *cycle, ng { ngx_file_info_t fi; - if (ngx_file_info((const char *) path[i]->name.data, &fi) == -1) { + if (ngx_file_info((const char *) path[i]->name.data, &fi) + == NGX_FILE_ERROR) + { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, ngx_file_info_n " \"%s\" failed", path[i]->name.data); return NGX_ERROR; @@ -487,11 +532,13 @@ ngx_ext_rename_file(ngx_str_t *src, ngx_ #if !(NGX_WIN32) - if (ngx_change_file_access(src->data, ext->access) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno, - ngx_change_file_access_n " \"%s\" failed", src->data); - err = 0; - goto failed; + if (ext->access) { + if (ngx_change_file_access(src->data, ext->access) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno, + ngx_change_file_access_n " \"%s\" failed", src->data); + err = 0; + goto failed; + } } #endif @@ -511,13 +558,19 @@ ngx_ext_rename_file(ngx_str_t *src, ngx_ err = ngx_errno; - if (err == NGX_ENOENT) { - + if (err +#if (NGX_WIN32) + == ERROR_PATH_NOT_FOUND +#else + == NGX_ENOENT +#endif + ) + { if (!ext->create_path) { goto failed; } - err = ngx_create_full_path(to->data, ngx_dir_access(ext->access)); + err = ngx_create_full_path(to->data, ngx_dir_access(ext->path_access)); if (err) { ngx_log_error(NGX_LOG_CRIT, ext->log, err, @@ -561,12 +614,14 @@ failed: } } - if (err) { + if (err && ext->log_rename_error) { ngx_log_error(NGX_LOG_CRIT, ext->log, err, ngx_rename_file_n " \"%s\" to \"%s\" failed", src->data, to->data); } + ext->rename_error = err; + return NGX_ERROR; } diff --git a/src/core/ngx_file.h b/src/core/ngx_file.h --- a/src/core/ngx_file.h +++ b/src/core/ngx_file.h @@ -11,62 +11,73 @@ #include #include -typedef struct ngx_path_s ngx_path_t; - -#include - struct ngx_file_s { - ngx_fd_t fd; - ngx_str_t name; - ngx_file_info_t info; + ngx_fd_t fd; + ngx_str_t name; + ngx_file_info_t info; - off_t offset; - off_t sys_offset; + off_t offset; + off_t sys_offset; - ngx_log_t *log; + ngx_log_t *log; - unsigned valid_info:1; - unsigned directio:1; + unsigned valid_info:1; + unsigned directio:1; }; #define NGX_MAX_PATH_LEVEL 3 -struct ngx_path_s { - ngx_str_t name; - size_t len; - size_t level[3]; - ngx_gc_handler_pt cleaner; + +typedef time_t (*ngx_path_manager_pt) (void *data); + - u_char *conf_file; - ngx_uint_t line; -}; +typedef struct { + ngx_str_t name; + size_t len; + size_t level[3]; + + ngx_path_manager_pt manager; + void *data; + + u_char *conf_file; + ngx_uint_t line; +} ngx_path_t; typedef struct { - ngx_file_t file; - off_t offset; - ngx_path_t *path; - ngx_pool_t *pool; - char *warn; + ngx_str_t name; + size_t level[3]; +} ngx_path_init_t; + - ngx_uint_t access; +typedef struct { + ngx_file_t file; + off_t offset; + ngx_path_t *path; + ngx_pool_t *pool; + char *warn; - unsigned log_level:8; - unsigned persistent:1; - unsigned clean:1; + ngx_uint_t access; + + unsigned log_level:8; + unsigned persistent:1; + unsigned clean:1; } ngx_temp_file_t; typedef struct { - ngx_uint_t access; - time_t time; - ngx_fd_t fd; + ngx_uint_t access; + ngx_uint_t path_access; + time_t time; + ngx_fd_t fd; + ngx_err_t rename_error; - unsigned create_path:1; - unsigned delete_file:1; + unsigned create_path:1; + unsigned delete_file:1; + unsigned log_rename_error:1; - ngx_log_t *log; + ngx_log_t *log; } ngx_ext_rename_file_t; @@ -110,40 +121,9 @@ void ngx_init_temp_number(void); ngx_atomic_uint_t ngx_next_temp_number(ngx_uint_t collision); char *ngx_conf_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_conf_merge_path_value(ngx_conf_t *cf, ngx_path_t **path, + ngx_path_t *prev, ngx_path_init_t *init); char *ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -#define ngx_conf_merge_path_value(curr, prev, path, l1, l2, l3, clean, cf) \ - if (curr == NULL) { \ - if (prev == NULL) { \ - curr = ngx_palloc(cf->pool, sizeof(ngx_path_t)); \ - if (curr == NULL) { \ - return NGX_CONF_ERROR; \ - } \ - \ - curr->name.len = sizeof(path) - 1; \ - curr->name.data = (u_char *) path; \ - \ - if (ngx_conf_full_name(cf->cycle, &curr->name, 0) == NGX_ERROR) { \ - return NGX_CONF_ERROR; \ - } \ - \ - curr->level[0] = l1; \ - curr->level[1] = l2; \ - curr->level[2] = l3; \ - curr->len = l1 + l2 + l3 + (l1 ? 1:0) + (l2 ? 1:0) + (l3 ? 1:0); \ - curr->cleaner = clean; \ - curr->conf_file = NULL; \ - \ - if (ngx_add_path(cf, &curr) == NGX_ERROR) { \ - return NGX_CONF_ERROR; \ - } \ - \ - } else { \ - curr = prev; \ - } \ - } - - - #endif /* _NGX_FILE_H_INCLUDED_ */ diff --git a/src/core/ngx_garbage_collector.c b/src/core/ngx_garbage_collector.c deleted file mode 100644 --- a/src/core/ngx_garbage_collector.c +++ /dev/null @@ -1,217 +0,0 @@ - -/* - * Copyright (C) Igor Sysoev - */ - - -#include -#include - - - -ngx_int_t ngx_collect_garbage(ngx_gc_t *ctx, ngx_str_t *dname, ngx_int_t level) -{ - int rc; - u_char *last; - size_t len; - ngx_err_t err; - ngx_str_t fname, buf; - ngx_dir_t dir; - - buf.len = 0; -#if (NGX_SUPPRESS_WARN) - buf.data = NULL; - fname.data = NULL; -#endif - - ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->log, 0, - "gc dir \"%s\":%d", dname->data, dname->len); - - if (ngx_open_dir(dname, &dir) == NGX_ERROR) { - ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, - ngx_open_dir_n " \"%s\" failed", dname->data); - return NGX_ERROR; - } - - for ( ;; ) { - ngx_set_errno(0); - if (ngx_read_dir(&dir) == NGX_ERROR) { - err = ngx_errno; - - if (err != NGX_ENOMOREFILES) { - ngx_log_error(NGX_LOG_CRIT, ctx->log, err, - ngx_read_dir_n " \"%s\" failed", dname->data); - rc = NGX_ERROR; - - } else { - rc = NGX_OK; - } - - break; - } - - len = ngx_de_namelen(&dir); - - ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->log, 0, - "gc name \"%s\":%d", ngx_de_name(&dir), len); - - if (len == 1 && ngx_de_name(&dir)[0] == '.') { - continue; - } - - if (len == 2 - && ngx_de_name(&dir)[0] == '.' - && ngx_de_name(&dir)[1] == '.') - { - continue; - } - - fname.len = dname->len + 1+ len; - - if (fname.len + NGX_DIR_MASK_LEN > buf.len) { - - if (buf.len) { - ngx_free(buf.data); - } - - buf.len = dname->len + 1 + len + NGX_DIR_MASK_LEN; - - buf.data = ngx_alloc(buf.len + 1, ctx->log); - if (buf.data == NULL) { - return NGX_ABORT; - } - } - - last = ngx_cpymem(buf.data, dname->data, dname->len); - *last++ = '/'; - ngx_memcpy(last, ngx_de_name(&dir), len + 1); - fname.data = buf.data; - - ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0, - "gc path: \"%s\"", fname.data); - - if (!dir.valid_info) { - if (ngx_de_info(fname.data, &dir) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, - ngx_de_info_n " \"%s\" failed", fname.data); - continue; - } - } - - if (ngx_de_is_dir(&dir)) { - - ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0, - "gc enter dir \"%s\"", fname.data); - - if (level == -1 - /* there can not be directory on the last level */ - || level == NGX_MAX_PATH_LEVEL - /* an directory from the old path hierarchy */ - || len != ctx->path->level[level]) - { - if (ngx_collect_garbage(ctx, &fname, -1) == NGX_ABORT) { - return NGX_ABORT; - } - - fname.data[fname.len] = '\0'; - - ngx_log_error(NGX_LOG_NOTICE, ctx->log, 0, - "delete old hierachy directory \"%s\"", - fname.data); - - if (ngx_delete_dir(fname.data) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, - ngx_delete_dir_n " \"%s\" failed", - fname.data); - } else { - ctx->deleted++; - ctx->freed += ngx_de_size(&dir); - } - - continue; - } - - if (ngx_collect_garbage(ctx, &fname, level + 1) == NGX_ABORT) { - return NGX_ABORT; - } - - } else if (ngx_de_is_file(&dir)) { - - ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0, - "gc file \"%s\"", fname.data); - - if (level == -1 - || (level < NGX_MAX_PATH_LEVEL && ctx->path->level[level] != 0)) - { - if (ngx_delete_file(fname.data) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, - ngx_delete_file_n " \"%s\" failed", - fname.data); - } else { - ctx->deleted++; - ctx->freed += ngx_de_size(&dir); - } - - continue; - } - - if (ctx->handler(ctx, &fname, &dir) == NGX_ABORT) { - return NGX_ABORT; - } - - } else { - ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, - "the file \"%s\" has unknown type, deleting", - fname.data); - - if (ngx_delete_file(fname.data) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, - ngx_delete_file_n " \"%s\" failed", fname.data); - } else { - ctx->deleted++; - ctx->freed += ngx_de_size(&dir); - } - } - } - - if (buf.len) { - ngx_free(buf.data); - } - - if (ngx_close_dir(&dir) == NGX_ERROR) { - ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, - ngx_close_dir_n " \"%s\" failed", fname.data); - } - - return rc; -} - - -ngx_int_t ngx_garbage_collector_temp_handler(ngx_gc_t *ctx, ngx_str_t *name, - ngx_dir_t *dir) -{ - /* - * We use mtime only and do not use atime because: - * on NTFS access time has a resolution of 1 hour, - * on NT FAT access time has a resolution of 1 day, - * Unices have the mount option "noatime". - */ - - if (ngx_time() - ngx_de_mtime(dir) < 3600) { - return NGX_OK; - } - - ngx_log_error(NGX_LOG_NOTICE, ctx->log, 0, - "delete the stale temporary file \"%s\"", name->data); - - if (ngx_delete_file(name->data) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, - ngx_delete_file_n " \"%s\" failed", name->data); - return NGX_ERROR; - } - - ctx->deleted++; - ctx->freed += ngx_de_size(dir); - - return NGX_OK; -} diff --git a/src/core/ngx_garbage_collector.h b/src/core/ngx_garbage_collector.h deleted file mode 100644 --- a/src/core/ngx_garbage_collector.h +++ /dev/null @@ -1,31 +0,0 @@ - -/* - * Copyright (C) Igor Sysoev - */ - - -#ifndef _NGX_GARBAGE_COLLECTOR_H_INCLUDED_ -#define _NGX_GARBAGE_COLLECTOR_H_INCLUDED_ - - -typedef struct ngx_gc_s ngx_gc_t; - -typedef ngx_int_t (*ngx_gc_handler_pt) (ngx_gc_t *ctx, ngx_str_t *name, - ngx_dir_t *dir); - - -struct ngx_gc_s { - ngx_path_t *path; - u_int deleted; - off_t freed; - ngx_gc_handler_pt handler; - ngx_log_t *log; -}; - - -ngx_int_t ngx_collect_garbage(ngx_gc_t *ctx, ngx_str_t *dname, ngx_int_t level); -ngx_int_t ngx_garbage_collector_temp_handler(ngx_gc_t *ctx, ngx_str_t *name, - ngx_dir_t *dir); - - -#endif /* _NGX_GARBAGE_COLLECTOR_H_INCLUDED_ */ diff --git a/src/core/ngx_hash.c b/src/core/ngx_hash.c --- a/src/core/ngx_hash.c +++ b/src/core/ngx_hash.c @@ -589,7 +589,7 @@ ngx_hash_wildcard_init(ngx_hash_init_t * wdc->value = names[n].value; } - name->value = (void *) ((uintptr_t) wdc | (dot ? 3 : 1)); + name->value = (void *) ((uintptr_t) wdc | (dot ? 3 : 2)); } else if (dot) { name->value = (void *) ((uintptr_t) name->value | 1); diff --git a/src/core/ngx_inet.c b/src/core/ngx_inet.c --- a/src/core/ngx_inet.c +++ b/src/core/ngx_inet.c @@ -8,11 +8,13 @@ #include +#if (NGX_HAVE_INET6) +static size_t ngx_inet6_ntop(u_char *p, u_char *text, size_t len); +#endif static ngx_int_t ngx_parse_unix_domain_url(ngx_pool_t *pool, ngx_url_t *u); static ngx_int_t ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u); - +static ngx_int_t ngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u); -/* AF_INET only */ in_addr_t ngx_inet_addr(u_char *text, size_t len) @@ -57,25 +59,58 @@ ngx_inet_addr(u_char *text, size_t len) } -/* AF_INET only */ - size_t -ngx_sock_ntop(struct sockaddr *sa, u_char *text, size_t len) +ngx_sock_ntop(struct sockaddr *sa, u_char *text, size_t len, ngx_uint_t port) { - u_char *p; - struct sockaddr_in *sin; + u_char *p; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + size_t n; + struct sockaddr_in6 *sin6; +#endif - if (sa->sa_family == AF_INET) { + switch (sa->sa_family) { + + case AF_INET: sin = (struct sockaddr_in *) sa; p = (u_char *) &sin->sin_addr; - return ngx_snprintf(text, len, "%ud.%ud.%ud.%ud", - p[0], p[1], p[2], p[3]) - - text; + if (port) { + p = ngx_snprintf(text, len, "%ud.%ud.%ud.%ud:%d", + p[0], p[1], p[2], p[3], ntohs(sin->sin_port)); + } else { + p = ngx_snprintf(text, len, "%ud.%ud.%ud.%ud", + p[0], p[1], p[2], p[3]); + } + + return (p - text); + +#if (NGX_HAVE_INET6) + + case AF_INET6: + + sin6 = (struct sockaddr_in6 *) sa; + + n = 0; + + if (port) { + text[n++] = '['; + } + + n = ngx_inet6_ntop((u_char *) &sin6->sin6_addr, &text[n], len); + + if (port) { + n = ngx_sprintf(&text[1 + n], "]:%d", + ntohs(sin6->sin6_port)) - text; + } + + return n; +#endif + + default: + return 0; } - - return 0; } @@ -84,42 +119,132 @@ ngx_inet_ntop(int family, void *addr, u_ { u_char *p; - if (family == AF_INET) { + switch (family) { - p = (u_char *) addr; + case AF_INET: + + p = addr; return ngx_snprintf(text, len, "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]) - text; + +#if (NGX_HAVE_INET6) + + case AF_INET6: + return ngx_inet6_ntop(addr, text, len); + +#endif + + default: + return 0; + } +} + + +#if (NGX_HAVE_INET6) + +static size_t +ngx_inet6_ntop(u_char *p, u_char *text, size_t len) +{ + u_char *dst; + size_t max, n; + ngx_uint_t i, zero, last; + + if (len < NGX_INET6_ADDRSTRLEN) { + return 0; } - return 0; + zero = (ngx_uint_t) -1; + last = (ngx_uint_t) -1; + max = 1; + n = 0; + + for (i = 0; i < 16; i += 2) { + + if (p[i] || p[i + 1]) { + + if (max < n) { + zero = last; + max = n; + } + + n = 0; + continue; + } + + if (n++ == 0) { + last = i; + } + } + + if (max < n) { + zero = last; + max = n; + } + + dst = text; + n = 16; + + if (zero == 0) { + + if ((max == 5 && p[10] == 0xff && p[11] == 0xff) + || (max == 6) + || (max == 7 && p[14] != 0 && p[15] != 1)) + { + n = 12; + } + + *dst++ = ':'; + } + + for (i = 0; i < n; i += 2) { + + if (i == zero) { + *dst++ = ':'; + i += (max - 1) * 2; + continue; + } + + dst = ngx_sprintf(dst, "%uxi", p[i] * 256 + p[i + 1]); + + if (i < 14) { + *dst++ = ':'; + } + } + + if (n == 12) { + dst = ngx_sprintf(dst, "%ud.%ud.%ud.%ud", p[12], p[13], p[14], p[15]); + } + + return dst - text; } +#endif + /* AF_INET only */ ngx_int_t -ngx_ptocidr(ngx_str_t *text, void *cidr) +ngx_ptocidr(ngx_str_t *text, ngx_cidr_t *cidr) { - u_char *addr, *mask, *last; - ngx_int_t shift; - ngx_inet_cidr_t *in_cidr; + u_char *addr, *mask, *last; + ngx_int_t shift; - in_cidr = cidr; addr = text->data; last = addr + text->len; mask = ngx_strlchr(addr, last, '/'); - in_cidr->addr = ngx_inet_addr(addr, (mask ? mask : last) - addr); + cidr->u.in.addr = ngx_inet_addr(addr, (mask ? mask : last) - addr); - if (in_cidr->addr == INADDR_NONE) { + if (cidr->u.in.addr == INADDR_NONE) { return NGX_ERROR; } if (mask == NULL) { - in_cidr->mask = 0xffffffff; + cidr->family = AF_INET; + cidr->u.in.mask = 0xffffffff; return NGX_OK; } @@ -130,26 +255,28 @@ ngx_ptocidr(ngx_str_t *text, void *cidr) return NGX_ERROR; } + cidr->family = AF_INET; + if (shift == 0) { /* the x86 compilers use the shl instruction that shifts by modulo 32 */ - in_cidr->mask = 0; + cidr->u.in.mask = 0; - if (in_cidr->addr == 0) { + if (cidr->u.in.addr == 0) { return NGX_OK; } return NGX_DONE; } - in_cidr->mask = htonl((ngx_uint_t) (0 - (1 << (32 - shift)))); + cidr->u.in.mask = htonl((ngx_uint_t) (0 - (1 << (32 - shift)))); - if (in_cidr->addr == (in_cidr->addr & in_cidr->mask)) { + if (cidr->u.in.addr == (cidr->u.in.addr & cidr->u.in.mask)) { return NGX_OK; } - in_cidr->addr &= in_cidr->mask; + cidr->u.in.addr &= cidr->u.in.mask; return NGX_DONE; } @@ -171,6 +298,10 @@ ngx_parse_url(ngx_pool_t *pool, ngx_url_ return NGX_ERROR; } + if (p[0] == '[') { + return ngx_parse_inet6_url(pool, u); + } + return ngx_parse_inet_url(pool, u); } @@ -209,13 +340,17 @@ ngx_parse_unix_domain_url(ngx_pool_t *po u->host.len = len++; u->host.data = path; - u->family = AF_UNIX; if (len > sizeof(saun->sun_path)) { u->err = "too long path in the unix domain socket"; return NGX_ERROR; } + u->socklen = sizeof(struct sockaddr_un); + saun = (struct sockaddr_un *) &u->sockaddr; + saun->sun_family = AF_UNIX; + (void) ngx_cpystrn((u_char *) saun->sun_path, path, len); + u->addrs = ngx_pcalloc(pool, sizeof(ngx_peer_addr_t)); if (u->addrs == NULL) { return NGX_ERROR; @@ -226,6 +361,7 @@ ngx_parse_unix_domain_url(ngx_pool_t *po return NGX_ERROR; } + u->family = AF_UNIX; u->naddrs = 1; saun->sun_family = AF_UNIX; @@ -251,10 +387,15 @@ ngx_parse_unix_domain_url(ngx_pool_t *po static ngx_int_t ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u) { - u_char *p, *host, *port, *last, *uri; - size_t len; - ngx_int_t n; - struct hostent *h; + u_char *p, *host, *port, *last, *uri, *args; + size_t len; + ngx_int_t n; + struct hostent *h; + struct sockaddr_in *sin; + + u->socklen = sizeof(struct sockaddr_in); + sin = (struct sockaddr_in *) &u->sockaddr; + sin->sin_family = AF_INET; u->family = AF_INET; @@ -264,7 +405,18 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx port = ngx_strlchr(host, last, ':'); - uri = ngx_strlchr(port ? port : host, last, '/'); + uri = ngx_strlchr(host, last, '/'); + + args = ngx_strlchr(host, last, '?'); + + if (args) { + if (uri == NULL) { + uri = args; + + } else if (args < uri) { + uri = args; + } + } if (uri) { if (u->listen || !u->uri_part) { @@ -276,6 +428,10 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx u->uri.data = uri; last = uri; + + if (uri < port) { + port = NULL; + } } if (port) { @@ -296,6 +452,7 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx } u->port = (in_port_t) n; + sin->sin_port = htons((in_port_t) n); u->port_text.len = len; u->port_text.data = port; @@ -319,10 +476,13 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx } u->port = (in_port_t) n; + sin->sin_port = htons((in_port_t) n); u->port_text.len = last - host; u->port_text.data = host; + u->wildcard = 1; + return NGX_OK; } } @@ -358,9 +518,9 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx (void) ngx_cpystrn(p, host, len); - u->addr.in_addr = inet_addr((const char *) p); + sin->sin_addr.s_addr = inet_addr((const char *) p); - if (u->addr.in_addr == INADDR_NONE) { + if (sin->sin_addr.s_addr == INADDR_NONE) { h = gethostbyname((const char *) p); if (h == NULL || h->h_addr_list[0] == NULL) { @@ -369,17 +529,23 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx return NGX_ERROR; } - u->addr.in_addr = *(in_addr_t *) (h->h_addr_list[0]); + sin->sin_addr.s_addr = *(in_addr_t *) (h->h_addr_list[0]); + } + + if (sin->sin_addr.s_addr == INADDR_ANY) { + u->wildcard = 1; } ngx_free(p); } else { - u->addr.in_addr = INADDR_ANY; + sin->sin_addr.s_addr = INADDR_ANY; + u->wildcard = 1; } if (u->no_port) { u->port = u->default_port; + sin->sin_port = htons(u->default_port); } if (u->listen) { @@ -394,11 +560,148 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx } +static ngx_int_t +ngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u) +{ +#if (NGX_HAVE_INET6) + int rc; + u_char *p, *host, *port, *last, *uri; + size_t len; + ngx_int_t n; + struct sockaddr_in6 *sin6; + + u->socklen = sizeof(struct sockaddr_in6); + sin6 = (struct sockaddr_in6 *) &u->sockaddr; + sin6->sin6_family = AF_INET6; + + host = u->url.data + 1; + + last = u->url.data + u->url.len; + + p = ngx_strlchr(host, last, ']'); + + if (p == NULL) { + u->err = "invalid host"; + return NGX_ERROR; + } + + if (last - p) { + + port = p + 1; + + uri = ngx_strlchr(port, last, '/'); + + if (uri) { + if (u->listen || !u->uri_part) { + u->err = "invalid host"; + return NGX_ERROR; + } + + u->uri.len = last - uri; + u->uri.data = uri; + } + + if (*port == ':') { + port++; + + len = last - port; + + if (len == 0) { + u->err = "invalid port"; + return NGX_ERROR; + } + + n = ngx_atoi(port, len); + + if (n < 1 || n > 65536) { + u->err = "invalid port"; + return NGX_ERROR; + } + + u->port = (in_port_t) n; + sin6->sin6_port = htons((in_port_t) n); + + u->port_text.len = len; + u->port_text.data = port; + + } else { + u->no_port = 1; + } + } + + len = p - host; + + if (len == 0) { + u->err = "no host"; + return NGX_ERROR; + } + + u->host.len = len++; + u->host.data = host; + + p = ngx_alloc(len, pool->log); + if (p == NULL) { + return NGX_ERROR; + } + + (void) ngx_cpystrn(p, host, len); + +#if (NGX_WIN32) + + rc = WSAStringToAddress((char *) p, AF_INET6, NULL, + (SOCKADDR *) sin6, &u->socklen); + rc = !rc; + + if (u->port) { + sin6->sin6_port = htons(u->port); + } + +#else + + rc = inet_pton(AF_INET6, (const char *) p, &sin6->sin6_addr); + +#endif + + ngx_free(p); + + if (rc == 0) { + u->err = "invalid IPv6 address"; + return NGX_ERROR; + } + + if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { + u->wildcard = 1; + } + + u->family = AF_INET6; + + if (u->no_resolve) { + return NGX_OK; + } + + if (u->no_port) { + u->port = u->default_port; + sin6->sin6_port = htons(u->default_port); + } + + return NGX_OK; + +#else + + u->err = "the INET6 sockets are not supported on this platform"; + + return NGX_ERROR; + +#endif +} + + ngx_int_t ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u) { u_char *p, *host; size_t len; + in_port_t port; in_addr_t in_addr; ngx_uint_t i; struct hostent *h; @@ -413,6 +716,8 @@ ngx_inet_resolve_host(ngx_pool_t *pool, /* AF_INET only */ + port = htons(u->port); + in_addr = inet_addr((char *) host); if (in_addr == INADDR_NONE) { @@ -449,22 +754,22 @@ ngx_inet_resolve_host(ngx_pool_t *pool, } sin->sin_family = AF_INET; - sin->sin_port = htons(u->port); + sin->sin_port = port; sin->sin_addr.s_addr = *(in_addr_t *) (h->h_addr_list[i]); u->addrs[i].sockaddr = (struct sockaddr *) sin; u->addrs[i].socklen = sizeof(struct sockaddr_in); - len = NGX_INET_ADDRSTRLEN + sizeof(":65536") - 1; + len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1; p = ngx_pnalloc(pool, len); if (p == NULL) { return NGX_ERROR; } - len = ngx_sock_ntop((struct sockaddr *) sin, p, len); + len = ngx_sock_ntop((struct sockaddr *) sin, p, len, 1); - u->addrs[i].name.len = ngx_sprintf(&p[len], ":%d", u->port) - p; + u->addrs[i].name.len = len; u->addrs[i].name.data = p; } @@ -487,18 +792,19 @@ ngx_inet_resolve_host(ngx_pool_t *pool, u->naddrs = 1; sin->sin_family = AF_INET; - sin->sin_port = htons(u->port); + sin->sin_port = port; sin->sin_addr.s_addr = in_addr; u->addrs[0].sockaddr = (struct sockaddr *) sin; u->addrs[0].socklen = sizeof(struct sockaddr_in); - p = ngx_pnalloc(pool, u->host.len + sizeof(":65536") - 1); + p = ngx_pnalloc(pool, u->host.len + sizeof(":65535") - 1); if (p == NULL) { return NGX_ERROR; } - u->addrs[0].name.len = ngx_sprintf(p, "%V:%d", &u->host, u->port) - p; + u->addrs[0].name.len = ngx_sprintf(p, "%V:%d", + &u->host, ntohs(port)) - p; u->addrs[0].name.data = p; } diff --git a/src/core/ngx_inet.h b/src/core/ngx_inet.h --- a/src/core/ngx_inet.h +++ b/src/core/ngx_inet.h @@ -12,57 +12,95 @@ #include -#define NGX_INET_ADDRSTRLEN (sizeof("255.255.255.255") - 1) +#define NGX_INET_ADDRSTRLEN (sizeof("255.255.255.255") - 1) +#define NGX_INET6_ADDRSTRLEN \ + (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") - 1) + +#define NGX_SOCKADDR_STRLEN (NGX_INET6_ADDRSTRLEN + sizeof(":65535") - 1) + + +/* + * TODO: autoconfigure NGX_SOCKADDRLEN as + * sizeof(struct sockaddr_storage) + * sizeof(struct sockaddr_un) + * sizeof(struct sockaddr_in6) + * sizeof(struct sockaddr_in) + */ + +#if (NGX_HAVE_UNIX_DOMAIN) +#define NGX_SOCKADDRLEN sizeof(struct sockaddr_un) +#else +#define NGX_SOCKADDRLEN 512 +#endif typedef struct { - in_addr_t addr; - in_addr_t mask; -} ngx_inet_cidr_t; + in_addr_t addr; + in_addr_t mask; +} ngx_in_cidr_t; -typedef union { - in_addr_t in_addr; -} ngx_url_addr_t; +#if (NGX_HAVE_INET6) + +typedef struct { + struct in6_addr addr; + struct in6_addr mask; +} ngx_in6_cidr_t; + +#endif typedef struct { - struct sockaddr *sockaddr; - socklen_t socklen; - ngx_str_t name; + ngx_uint_t family; + union { + ngx_in_cidr_t in; +#if (NGX_HAVE_INET6) + ngx_in6_cidr_t in6; +#endif + } u; +} ngx_cidr_t; + + +typedef struct { + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_str_t name; } ngx_peer_addr_t; typedef struct { - ngx_str_t url; - ngx_str_t host; - ngx_str_t port_text; - ngx_str_t uri; + ngx_str_t url; + ngx_str_t host; + ngx_str_t port_text; + ngx_str_t uri; - in_port_t port; - in_port_t default_port; - int family; + in_port_t port; + in_port_t default_port; + int family; - unsigned listen:1; - unsigned uri_part:1; - unsigned no_resolve:1; - unsigned one_addr:1; + unsigned listen:1; + unsigned uri_part:1; + unsigned no_resolve:1; + unsigned one_addr:1; - unsigned no_port:1; + unsigned no_port:1; + unsigned wildcard:1; - ngx_url_addr_t addr; + socklen_t socklen; + u_char sockaddr[NGX_SOCKADDRLEN]; - ngx_peer_addr_t *addrs; - ngx_uint_t naddrs; + ngx_peer_addr_t *addrs; + ngx_uint_t naddrs; - char *err; + char *err; } ngx_url_t; in_addr_t ngx_inet_addr(u_char *text, size_t len); -size_t ngx_sock_ntop(struct sockaddr *sa, u_char *text, size_t len); +size_t ngx_sock_ntop(struct sockaddr *sa, u_char *text, size_t len, + ngx_uint_t port); size_t ngx_inet_ntop(int family, void *addr, u_char *text, size_t len); -ngx_int_t ngx_ptocidr(ngx_str_t *text, void *cidr); +ngx_int_t ngx_ptocidr(ngx_str_t *text, ngx_cidr_t *cidr); ngx_int_t ngx_parse_url(ngx_pool_t *pool, ngx_url_t *u); ngx_int_t ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u); diff --git a/src/core/ngx_log.c b/src/core/ngx_log.c --- a/src/core/ngx_log.c +++ b/src/core/ngx_log.c @@ -8,14 +8,14 @@ #include -static char *ngx_set_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_command_t ngx_errlog_commands[] = { {ngx_string("error_log"), NGX_MAIN_CONF|NGX_CONF_1MORE, - ngx_set_error_log, + ngx_error_log, 0, 0, NULL}, @@ -48,12 +48,20 @@ ngx_module_t ngx_errlog_module = { static ngx_log_t ngx_log; -static ngx_open_file_t ngx_stderr; +static ngx_open_file_t ngx_log_file; +ngx_uint_t ngx_use_stderr = 1; -static const char *err_levels[] = { - "stderr", "emerg", "alert", "crit", "error", - "warn", "notice", "info", "debug" +static ngx_str_t err_levels[] = { + ngx_null_string, + ngx_string("emerg"), + ngx_string("alert"), + ngx_string("crit"), + ngx_string("error"), + ngx_string("warn"), + ngx_string("notice"), + ngx_string("info"), + ngx_string("debug") }; static const char *debug_levels[] = { @@ -79,7 +87,8 @@ ngx_log_error_core(ngx_uint_t level, ngx #if (NGX_HAVE_VARIADIC_MACROS) va_list args; #endif - u_char errstr[NGX_MAX_ERROR_STR], *p, *last; + u_char *p, *last, *msg; + u_char errstr[NGX_MAX_ERROR_STR]; if (log->file->fd == NGX_INVALID_FILE) { return; @@ -92,60 +101,32 @@ ngx_log_error_core(ngx_uint_t level, ngx p = errstr + ngx_cached_err_log_time.len; - p = ngx_snprintf(p, last - p, " [%s] ", err_levels[level]); + p = ngx_slprintf(p, last, " [%V] ", &err_levels[level]); /* pid#tid */ - p = ngx_snprintf(p, last - p, "%P#" NGX_TID_T_FMT ": ", + p = ngx_slprintf(p, last, "%P#" NGX_TID_T_FMT ": ", ngx_log_pid, ngx_log_tid); if (log->connection) { - p = ngx_snprintf(p, last - p, "*%uA ", log->connection); + p = ngx_slprintf(p, last, "*%uA ", log->connection); } + msg = p; + #if (NGX_HAVE_VARIADIC_MACROS) va_start(args, fmt); - p = ngx_vsnprintf(p, last - p, fmt, args); + p = ngx_vslprintf(p, last, fmt, args); va_end(args); #else - p = ngx_vsnprintf(p, last - p, fmt, args); + p = ngx_vslprintf(p, last, fmt, args); #endif if (err) { - - if (p > last - 50) { - - /* leave a space for an error code */ - - p = last - 50; - *p++ = '.'; - *p++ = '.'; - *p++ = '.'; - } - -#if (NGX_WIN32) - - if ((unsigned) err >= 0x80000000) { - p = ngx_snprintf(p, last - p, " (%Xd: ", err); - - } else { - p = ngx_snprintf(p, last - p, " (%d: ", err); - } - -#else - - p = ngx_snprintf(p, last - p, " (%d: ", err); - -#endif - - p = ngx_strerror_r(err, p, last - p); - - if (p < last) { - *p++ = ')'; - } + p = ngx_log_errno(p, last, err); } if (level != NGX_LOG_DEBUG && log->handler) { @@ -158,7 +139,20 @@ ngx_log_error_core(ngx_uint_t level, ngx ngx_linefeed(p); - ngx_write_fd(log->file->fd, errstr, p - errstr); + (void) ngx_write_fd(log->file->fd, errstr, p - errstr); + + if (!ngx_use_stderr + || level > NGX_LOG_WARN + || log->file->fd == ngx_stderr) + { + return; + } + + msg -= (err_levels[level].len + 4); + + (void) ngx_sprintf(msg, "[%V]: ", &err_levels[level]); + + (void) ngx_write_console(ngx_stderr, msg, p - msg); } @@ -191,64 +185,169 @@ ngx_log_debug_core(ngx_log_t *log, ngx_e #endif -void -ngx_log_abort(ngx_err_t err, const char *text) +void ngx_cdecl +ngx_log_abort(ngx_err_t err, const char *fmt, ...) +{ + u_char *p; + va_list args; + u_char errstr[NGX_MAX_CONF_ERRSTR]; + + va_start(args, fmt); + p = ngx_vsnprintf(errstr, sizeof(errstr) - 1, fmt, args); + va_end(args); + + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err, + "%*s", p - errstr, errstr); +} + + +void ngx_cdecl +ngx_log_stderr(ngx_err_t err, const char *fmt, ...) { - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err, text); + u_char *p, *last; + va_list args; + u_char errstr[NGX_MAX_ERROR_STR]; + + last = errstr + NGX_MAX_ERROR_STR; + + va_start(args, fmt); + p = ngx_vslprintf(errstr, last, fmt, args); + va_end(args); + + if (err) { + p = ngx_log_errno(p, last, err); + } + + if (p > last - NGX_LINEFEED_SIZE) { + p = last - NGX_LINEFEED_SIZE; + } + + ngx_linefeed(p); + + (void) ngx_write_console(ngx_stderr, errstr, p - errstr); +} + + +u_char * +ngx_log_errno(u_char *buf, u_char *last, ngx_err_t err) +{ + if (buf > last - 50) { + + /* leave a space for an error code */ + + buf = last - 50; + *buf++ = '.'; + *buf++ = '.'; + *buf++ = '.'; + } + +#if (NGX_WIN32) + buf = ngx_slprintf(buf, last, ((unsigned) err < 0x80000000) + ? " (%d: " : " (%Xd: ", err); +#else + buf = ngx_slprintf(buf, last, " (%d: ", err); +#endif + + buf = ngx_strerror_r(err, buf, last - buf); + + if (buf < last) { + *buf++ = ')'; + } + + return buf; } ngx_log_t * -ngx_log_init(void) +ngx_log_init(u_char *prefix) { - ngx_log.file = &ngx_stderr; + u_char *p, *name; + size_t nlen, plen; + + ngx_log.file = &ngx_log_file; ngx_log.log_level = NGX_LOG_NOTICE; + name = (u_char *) NGX_ERROR_LOG_PATH; + + /* + * we use ngx_strlen() here since BCC warns about + * condition is always false and unreachable code + */ + + nlen = ngx_strlen(name); + + if (nlen == 0) { + ngx_log_file.fd = ngx_stderr; + return &ngx_log; + } + + p = NULL; + #if (NGX_WIN32) + if (name[1] != ':') { +#else + if (name[0] != '/') { +#endif - ngx_stderr_fileno = GetStdHandle(STD_ERROR_HANDLE); + if (prefix) { + plen = ngx_strlen(prefix); - ngx_stderr.fd = ngx_open_file(NGX_ERROR_LOG_PATH, NGX_FILE_RDWR, - NGX_FILE_CREATE_OR_OPEN|NGX_FILE_APPEND, 0); + } else { +#ifdef NGX_PREFIX + prefix = (u_char *) NGX_PREFIX; + plen = ngx_strlen(prefix); +#else + plen = 0; +#endif + } - if (ngx_stderr.fd == NGX_INVALID_FILE) { - ngx_message_box("nginx", MB_OK, ngx_errno, - "Could not open error log file: " - ngx_open_file_n " \"" NGX_ERROR_LOG_PATH "\" failed"); - return NULL; + if (plen) { + name = malloc(plen + nlen + 2); + if (name == NULL) { + return NULL; + } + + p = ngx_cpymem(name, prefix, plen); + + if (!ngx_path_separator(*(p - 1))) { + *p++ = '/'; + } + + ngx_cpystrn(p, (u_char *) NGX_ERROR_LOG_PATH, nlen + 1); + + p = name; + } } - if (ngx_file_append_mode(ngx_stderr.fd) == NGX_ERROR) { - ngx_message_box("nginx", MB_OK, ngx_errno, - "Could not open error log file: " - ngx_file_append_mode_n " \"" NGX_ERROR_LOG_PATH - "\" failed"); - return NULL; + ngx_log_file.fd = ngx_open_file(name, NGX_FILE_APPEND, + NGX_FILE_CREATE_OR_OPEN, + NGX_FILE_DEFAULT_ACCESS); + + if (ngx_log_file.fd == NGX_INVALID_FILE) { + ngx_log_stderr(ngx_errno, + "[alert]: could not open error log file: " + ngx_open_file_n " \"%s\" failed", name); +#if (NGX_WIN32) + ngx_event_log(ngx_errno, + "could not open error log file: " + ngx_open_file_n " \"%s\" failed", name); +#endif + + ngx_log_file.fd = ngx_stderr; } -#else - - ngx_stderr.fd = STDERR_FILENO; - -#endif + if (p) { + ngx_free(p); + } return &ngx_log; } ngx_log_t * -ngx_log_create_errlog(ngx_cycle_t *cycle, ngx_array_t *args) +ngx_log_create(ngx_cycle_t *cycle, ngx_str_t *name) { ngx_log_t *log; - ngx_str_t *value, *name; - - if (args) { - value = args->elts; - name = &value[1]; - - } else { - name = NULL; - } log = ngx_pcalloc(cycle->pool, sizeof(ngx_log_t)); if (log == NULL) { @@ -265,7 +364,7 @@ ngx_log_create_errlog(ngx_cycle_t *cycle char * -ngx_set_error_log_levels(ngx_conf_t *cf, ngx_log_t *log) +ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log) { ngx_uint_t i, n, d; ngx_str_t *value; @@ -275,12 +374,12 @@ ngx_set_error_log_levels(ngx_conf_t *cf, for (i = 2; i < cf->args->nelts; i++) { for (n = 1; n <= NGX_LOG_DEBUG; n++) { - if (ngx_strcmp(value[i].data, err_levels[n]) == 0) { + if (ngx_strcmp(value[i].data, err_levels[n].data) == 0) { if (log->log_level != 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "duplicate log level \"%s\"", - value[i].data); + "duplicate log level \"%V\"", + &value[i]); return NGX_CONF_ERROR; } @@ -293,8 +392,8 @@ ngx_set_error_log_levels(ngx_conf_t *cf, if (ngx_strcmp(value[i].data, debug_levels[n++]) == 0) { if (log->log_level & ~NGX_LOG_DEBUG_ALL) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid log level \"%s\"", - value[i].data); + "invalid log level \"%V\"", + &value[i]); return NGX_CONF_ERROR; } @@ -305,7 +404,7 @@ ngx_set_error_log_levels(ngx_conf_t *cf, if (log->log_level == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid log level \"%s\"", value[i].data); + "invalid log level \"%V\"", &value[i]); return NGX_CONF_ERROR; } } @@ -319,26 +418,35 @@ ngx_set_error_log_levels(ngx_conf_t *cf, static char * -ngx_set_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - ngx_str_t *value; + ngx_str_t *value, name; + + if (cf->cycle->new_log.file) { + return "is duplicate"; + } value = cf->args->elts; - if (value[1].len == 6 && ngx_strcmp(value[1].data, "stderr") == 0) { - cf->cycle->new_log->file->fd = ngx_stderr.fd; - cf->cycle->new_log->file->name.len = 0; - cf->cycle->new_log->file->name.data = NULL; + if (ngx_strcmp(value[1].data, "stderr") == 0) { + name.len = 0; + name.data = NULL; } else { - cf->cycle->new_log->file->name = value[1]; + name = value[1]; + } - if (ngx_conf_full_name(cf->cycle, &cf->cycle->new_log->file->name, 0) - == NGX_ERROR) - { - return NGX_CONF_ERROR; - } + cf->cycle->new_log.file = ngx_conf_open_file(cf->cycle, &name); + if (cf->cycle->new_log.file == NULL) { + return NULL; } - return ngx_set_error_log_levels(cf, cf->cycle->new_log); + if (cf->args->nelts == 2) { + cf->cycle->new_log.log_level = NGX_LOG_ERR; + return NGX_CONF_OK; + } + + cf->cycle->new_log.log_level = 0; + + return ngx_log_set_levels(cf, &cf->cycle->new_log); } diff --git a/src/core/ngx_log.h b/src/core/ngx_log.h --- a/src/core/ngx_log.h +++ b/src/core/ngx_log.h @@ -195,13 +195,16 @@ void ngx_cdecl ngx_log_debug_core(ngx_lo /*********************************/ -ngx_log_t *ngx_log_init(void); -ngx_log_t *ngx_log_create_errlog(ngx_cycle_t *cycle, ngx_array_t *args); -char *ngx_set_error_log_levels(ngx_conf_t *cf, ngx_log_t *log); -void ngx_log_abort(ngx_err_t err, const char *text); +ngx_log_t *ngx_log_init(u_char *prefix); +ngx_log_t *ngx_log_create(ngx_cycle_t *cycle, ngx_str_t *name); +char *ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log); +void ngx_cdecl ngx_log_abort(ngx_err_t err, const char *fmt, ...); +void ngx_cdecl ngx_log_stderr(ngx_err_t err, const char *fmt, ...); +u_char *ngx_log_errno(u_char *buf, u_char *last, ngx_err_t err); extern ngx_module_t ngx_errlog_module; +extern ngx_uint_t ngx_use_stderr; #endif /* _NGX_LOG_H_INCLUDED_ */ diff --git a/src/core/ngx_open_file_cache.c b/src/core/ngx_open_file_cache.c --- a/src/core/ngx_open_file_cache.c +++ b/src/core/ngx_open_file_cache.c @@ -130,6 +130,7 @@ ngx_open_cached_file(ngx_open_file_cache time_t now; uint32_t hash; ngx_int_t rc; + ngx_file_info_t fi; ngx_pool_cleanup_t *cln; ngx_cached_open_file_t *file; ngx_pool_cleanup_file_t *clnf; @@ -140,6 +141,25 @@ ngx_open_cached_file(ngx_open_file_cache if (cache == NULL) { + if (of->test_only) { + + if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) { + of->err = ngx_errno; + of->failed = ngx_file_info_n; + return NGX_ERROR; + } + + of->uniq = ngx_file_uniq(&fi); + of->mtime = ngx_file_mtime(&fi); + of->size = ngx_file_size(&fi); + of->is_dir = ngx_is_dir(&fi); + of->is_file = ngx_is_file(&fi); + of->is_link = ngx_is_link(&fi); + of->is_exec = ngx_is_exec(&fi); + + return NGX_OK; + } + cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t)); if (cln == NULL) { return NGX_ERROR; @@ -214,6 +234,7 @@ ngx_open_cached_file(ngx_open_file_cache } else { of->err = file->err; + of->failed = ngx_open_file_n; } goto found; @@ -339,6 +360,7 @@ create: file->uses = 1; file->count = 0; + file->use_event = 0; file->event = NULL; add_event: @@ -443,7 +465,8 @@ ngx_open_and_stat_file(u_char *name, ngx if (of->fd != NGX_INVALID_FILE) { - if (ngx_file_info(name, &fi) == -1) { + if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) { + of->failed = ngx_file_info_n; goto failed; } @@ -453,11 +476,12 @@ ngx_open_and_stat_file(u_char *name, ngx } else if (of->test_dir) { - if (ngx_file_info(name, &fi) == -1) { + if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) { + of->failed = ngx_file_info_n; goto failed; } - if (of->is_dir) { + if (ngx_is_dir(&fi)) { goto done; } } @@ -466,12 +490,12 @@ ngx_open_and_stat_file(u_char *name, ngx fd = ngx_open_file(name, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); } else { - fd = ngx_open_file(name, NGX_FILE_RDWR, - NGX_FILE_CREATE_OR_OPEN|NGX_FILE_APPEND, + fd = ngx_open_file(name, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN, NGX_FILE_DEFAULT_ACCESS); } if (fd == NGX_INVALID_FILE) { + of->failed = ngx_open_file_n; goto failed; } diff --git a/src/core/ngx_open_file_cache.h b/src/core/ngx_open_file_cache.h --- a/src/core/ngx_open_file_cache.h +++ b/src/core/ngx_open_file_cache.h @@ -12,19 +12,25 @@ #define _NGX_OPEN_FILE_CACHE_H_INCLUDED_ +#define NGX_OPEN_FILE_DIRECTIO_OFF NGX_MAX_OFF_T_VALUE + + typedef struct { ngx_fd_t fd; ngx_file_uniq_t uniq; time_t mtime; off_t size; off_t directio; + ngx_err_t err; + char *failed; time_t valid; ngx_uint_t min_uses; unsigned test_dir:1; + unsigned test_only:1; unsigned log:1; unsigned errors:1; unsigned events:1; diff --git a/src/core/ngx_output_chain.c b/src/core/ngx_output_chain.c --- a/src/core/ngx_output_chain.c +++ b/src/core/ngx_output_chain.c @@ -13,6 +13,17 @@ #define NGX_SENDFILE_LIMIT 4096 #endif +/* + * When DIRECTIO is enabled FreeBSD, Solaris, and MacOSX read directly + * to an application memory from a device if parameters are aligned + * to device sector boundary(512 bytes). They fallback to usual read + * operation if the parameters are not aligned. + * Linux allows DIRECTIO only if the parameters are aligned to a filesystem + * sector boundary, otherwise it returns EINVAL. The sector size is + * usually 512 bytes, however, on XFS it may be 4096 bytes. + */ +#define NGX_DIRECTIO_BLOCK 4096 + #define NGX_NONE 1 @@ -303,12 +314,11 @@ ngx_output_chain_add_copy(ngx_pool_t *po #endif + cl->next = NULL; *ll = cl; ll = &cl->next; } - *ll = NULL; - return NGX_OK; } @@ -327,7 +337,7 @@ ngx_output_chain_align_file_buf(ngx_outp ctx->directio = 1; - size = (size_t) (in->file_pos - (in->file_pos & ~511)); + size = (size_t) (in->file_pos - (in->file_pos & ~(NGX_DIRECTIO_BLOCK - 1))); if (size == 0) { @@ -338,7 +348,7 @@ ngx_output_chain_align_file_buf(ngx_outp size = (size_t) bsize; } else { - size = 512 - size; + size = NGX_DIRECTIO_BLOCK - size; if ((off_t) size > bsize) { size = (size_t) bsize; @@ -413,7 +423,7 @@ ngx_output_chain_get_buf(ngx_output_chai * userland buffer direct usage conjunctly with directio */ - b->start = ngx_pmemalign(ctx->pool, size, 512); + b->start = ngx_pmemalign(ctx->pool, size, NGX_DIRECTIO_BLOCK); if (b->start == NULL) { return NGX_ERROR; } @@ -492,6 +502,7 @@ ngx_output_chain_copy_buf(ngx_output_cha if (src->pos == src->last) { dst->flush = src->flush; dst->last_buf = src->last_buf; + dst->last_in_chain = src->last_in_chain; } } else { @@ -566,6 +577,7 @@ ngx_output_chain_copy_buf(ngx_output_cha if (src->file_pos == src->file_last) { dst->flush = src->flush; dst->last_buf = src->last_buf; + dst->last_in_chain = src->last_in_chain; } } diff --git a/src/core/ngx_palloc.c b/src/core/ngx_palloc.c --- a/src/core/ngx_palloc.c +++ b/src/core/ngx_palloc.c @@ -25,6 +25,7 @@ ngx_create_pool(size_t size, ngx_log_t * p->d.last = (u_char *) p + sizeof(ngx_pool_t); p->d.end = (u_char *) p + size; p->d.next = NULL; + p->d.failed = 0; size = size - sizeof(ngx_pool_t); p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL; @@ -91,6 +92,26 @@ ngx_destroy_pool(ngx_pool_t *pool) } +void +ngx_reset_pool(ngx_pool_t *pool) +{ + ngx_pool_t *p; + ngx_pool_large_t *l; + + for (l = pool->large; l; l = l->next) { + if (l->alloc) { + ngx_free(l->alloc); + } + } + + pool->large = NULL; + + for (p = pool; p; p = p->d.next) { + p->d.last = (u_char *) p + sizeof(ngx_pool_t); + } +} + + void * ngx_palloc(ngx_pool_t *pool, size_t size) { @@ -169,14 +190,16 @@ ngx_palloc_block(ngx_pool_t *pool, size_ new->d.end = m + psize; new->d.next = NULL; + new->d.failed = 0; m += sizeof(ngx_pool_data_t); + m = ngx_align_ptr(m, NGX_ALIGNMENT); new->d.last = m + size; current = pool->current; for (p = current; p->d.next; p = p->d.next) { - if ((size_t) (p->d.end - p->d.last) < NGX_ALIGNMENT) { + if (p->d.failed++ > 4) { current = p->d.next; } } @@ -193,6 +216,7 @@ static void * ngx_palloc_large(ngx_pool_t *pool, size_t size) { void *p; + ngx_uint_t n; ngx_pool_large_t *large; p = ngx_alloc(size, pool->log); @@ -200,6 +224,19 @@ ngx_palloc_large(ngx_pool_t *pool, size_ return NULL; } + n = 0; + + for (large = pool->large; large; large = large->next) { + if (large->alloc == NULL) { + large->alloc = p; + return p; + } + + if (n++ > 3) { + break; + } + } + large = ngx_palloc(pool, sizeof(ngx_pool_large_t)); if (large == NULL) { ngx_free(p); @@ -305,6 +342,27 @@ ngx_pool_cleanup_add(ngx_pool_t *p, size void +ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd) +{ + ngx_pool_cleanup_t *c; + ngx_pool_cleanup_file_t *cf; + + for (c = p->cleanup; c; c = c->next) { + if (c->handler == ngx_pool_cleanup_file) { + + cf = c->data; + + if (cf->fd == fd) { + c->handler(cf); + c->handler = NULL; + return; + } + } + } +} + + +void ngx_pool_cleanup_file(void *data) { ngx_pool_cleanup_file_t *c = data; diff --git a/src/core/ngx_palloc.h b/src/core/ngx_palloc.h --- a/src/core/ngx_palloc.h +++ b/src/core/ngx_palloc.h @@ -14,7 +14,6 @@ /* * NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86. - * On FreeBSD 5.x it allows to use the zero copy sending. * On Windows NT it decreases a number of locked pages in a kernel. */ #define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1) @@ -47,6 +46,7 @@ typedef struct { u_char *last; u_char *end; ngx_pool_t *next; + ngx_uint_t failed; } ngx_pool_data_t; @@ -73,6 +73,7 @@ void *ngx_calloc(size_t size, ngx_log_t ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log); void ngx_destroy_pool(ngx_pool_t *pool); +void ngx_reset_pool(ngx_pool_t *pool); void *ngx_palloc(ngx_pool_t *pool, size_t size); void *ngx_pnalloc(ngx_pool_t *pool, size_t size); @@ -82,6 +83,7 @@ ngx_int_t ngx_pfree(ngx_pool_t *pool, vo ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size); +void ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd); void ngx_pool_cleanup_file(void *data); void ngx_pool_delete_file(void *data); diff --git a/src/core/ngx_radix_tree.c b/src/core/ngx_radix_tree.c --- a/src/core/ngx_radix_tree.c +++ b/src/core/ngx_radix_tree.c @@ -42,13 +42,13 @@ ngx_radix_tree_create(ngx_pool_t *pool, } /* - * The preallocation the first nodes: 0, 1, 00, 01, 10, 11, 000, 001, etc. - * increases the TLB hits even if for the first lookup iterations. - * On the 32-bit platforms the 7 preallocated bits takes continuous 4K, - * 8 - 8K, 9 - 16K, etc. On the 64-bit platforms the 6 preallocated bits + * Preallocation of first nodes : 0, 1, 00, 01, 10, 11, 000, 001, etc. + * increases TLB hits even if for first lookup iterations. + * On 32-bit platforms the 7 preallocated bits takes continuous 4K, + * 8 - 8K, 9 - 16K, etc. On 64-bit platforms the 6 preallocated bits * takes continuous 4K, 7 - 8K, 8 - 16K, etc. There is no sense to * to preallocate more than one page, because further preallocation - * distributes the only bit per page. Instead, the random insertion + * distributes the only bit per page. Instead, a random insertion * may distribute several bits per page. * * Thus, by default we preallocate maximum diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c --- a/src/core/ngx_resolver.c +++ b/src/core/ngx_resolver.c @@ -131,14 +131,14 @@ ngx_resolver_create(ngx_conf_t *cf, ngx_ r->event->handler = ngx_resolver_resend_handler; r->event->data = r; - r->event->log = cf->cycle->new_log; + r->event->log = &cf->cycle->new_log; r->ident = -1; r->resend_timeout = 5; r->expire = 30; r->valid = 300; - r->log = cf->cycle->new_log; + r->log = &cf->cycle->new_log; r->log_level = NGX_LOG_ALERT; if (addr) { @@ -152,7 +152,7 @@ ngx_resolver_create(ngx_conf_t *cf, ngx_ uc->sockaddr = addr->sockaddr; uc->socklen = addr->socklen; uc->server = addr->name; - uc->log = cf->cycle->new_log; + uc->log = &cf->cycle->new_log; } return r; @@ -578,6 +578,7 @@ failed: ngx_int_t ngx_resolve_addr(ngx_resolver_ctx_t *ctx) { + u_char *name; ngx_resolver_t *r; ngx_resolver_node_t *rn; @@ -601,19 +602,21 @@ ngx_resolve_addr(ngx_resolver_ctx_t *ctx ngx_queue_insert_head(&r->addr_expire_queue, &rn->queue); - ctx->name.len = rn->nlen; - ctx->name.data = ngx_resolver_dup(r, rn->name, rn->nlen); - if (ctx->name.data == NULL) { + name = ngx_resolver_dup(r, rn->name, rn->nlen); + if (name == NULL) { goto failed; } + ctx->name.len = rn->nlen; + ctx->name.data = name; + /* unlock addr mutex */ ctx->state = NGX_OK; ctx->handler(ctx); - ngx_resolver_free(r, ctx->name.data); + ngx_resolver_free(r, name); return NGX_OK; } @@ -623,7 +626,9 @@ ngx_resolve_addr(ngx_resolver_ctx_t *ctx ctx->next = rn->waiting; rn->waiting = ctx; - return NGX_AGAIN; + /* unlock addr mutex */ + + return NGX_OK; } ngx_queue_remove(&rn->queue); @@ -956,11 +961,14 @@ ngx_resolver_process_response(ngx_resolv { char *err; size_t len; - ngx_uint_t i, ident, flags, code, nqs, nan, qtype, qclass; + ngx_uint_t i, times, ident, qident, flags, code, nqs, nan, + qtype, qclass; + ngx_queue_t *q; ngx_resolver_qs_t *qs; + ngx_resolver_node_t *rn; ngx_resolver_query_t *query; - if ((size_t) n < sizeof(ngx_resolver_query_t) + 1) { + if ((size_t) n < sizeof(ngx_resolver_query_t)) { goto short_response; } @@ -985,11 +993,31 @@ ngx_resolver_process_response(ngx_resolv code = flags & 0x7f; - if (code == NGX_RESOLVE_FORMERR || code > NGX_RESOLVE_REFUSED) { - ngx_log_error(r->log_level, r->log, 0, - "DNS error (%ui: %s), query id:%ui", - code, ngx_resolver_strerror(code), ident); - return; + if (code == NGX_RESOLVE_FORMERR) { + + times = 0; + + for (q = ngx_queue_head(&r->name_resend_queue); + q != ngx_queue_sentinel(&r->name_resend_queue) || times++ < 100; + q = ngx_queue_next(q)) + { + rn = ngx_queue_data(q, ngx_resolver_node_t, queue); + qident = (rn->query[0] << 8) + rn->query[1]; + + if (qident == ident) { + ngx_log_error(r->log_level, r->log, 0, + "DNS error (%ui: %s), query id:%ui, name:\"%*s\"", + code, ngx_resolver_strerror(code), ident, + rn->nlen, rn->name); + return; + } + } + + goto dns_error; + } + + if (code > NGX_RESOLVE_REFUSED) { + goto dns_error; } if (nqs != 1) { @@ -1069,6 +1097,13 @@ done: ngx_log_error(r->log_level, r->log, 0, err); return; + +dns_error: + + ngx_log_error(r->log_level, r->log, 0, + "DNS error (%ui: %s), query id:%ui", + code, ngx_resolver_strerror(code), ident); + return; } @@ -1306,7 +1341,7 @@ ngx_resolver_process_a(ngx_resolver_t *r ctx->handler(ctx); } - if (naddrs) { + if (naddrs > 1) { ngx_resolver_free(r, addrs); } @@ -1483,20 +1518,23 @@ ngx_resolver_process_ptr(ngx_resolver_t goto short_response; } - len -= 2; - if (ngx_resolver_copy(r, &name, buf, &buf[i], &buf[n]) != NGX_OK) { return; } ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver an:%V", &name); - if (len != (size_t) rn->nlen || ngx_strncmp(name.data, rn->name, len) != 0) + if (name.len != (size_t) rn->nlen + || ngx_strncmp(name.data, rn->name, name.len) != 0) { - ngx_resolver_free(r, rn->name); + if (rn->nlen) { + ngx_resolver_free(r, rn->name); + } + + rn->nlen = (u_short) name.len; rn->name = name.data; - name.data = ngx_resolver_dup(r, rn->name, len); + name.data = ngx_resolver_dup(r, rn->name, name.len); if (name.data == NULL) { goto failed; } @@ -1836,7 +1874,7 @@ ngx_resolver_copy(ngx_resolver_t *r, ngx } if (n & 0xc0) { - n = (n & 0x3f << 8) + *p; + n = ((n & 0x3f) << 8) + *p; p = &buf[n]; } else { @@ -1886,7 +1924,7 @@ done: } } else { - n = (n & 0x3f << 8) + *src; + n = ((n & 0x3f) << 8) + *src; src = &buf[n]; n = *src++; diff --git a/src/core/ngx_shmtx.h b/src/core/ngx_shmtx.h --- a/src/core/ngx_shmtx.h +++ b/src/core/ngx_shmtx.h @@ -57,7 +57,15 @@ ngx_shmtx_trylock(ngx_shmtx_t *mtx) return 0; } - ngx_log_abort(err, ngx_trylock_fd_n " failed"); +#if __osf__ /* Tru64 UNIX */ + + if (err == NGX_EACCESS) { + return 0; + } + +#endif + + ngx_log_abort(err, ngx_trylock_fd_n " %s failed", mtx->name); return 0; } @@ -74,7 +82,7 @@ ngx_shmtx_lock(ngx_shmtx_t *mtx) return; } - ngx_log_abort(err, ngx_lock_fd_n " failed"); + ngx_log_abort(err, ngx_lock_fd_n " %s failed", mtx->name); } @@ -89,7 +97,7 @@ ngx_shmtx_unlock(ngx_shmtx_t *mtx) return; } - ngx_log_abort(err, ngx_unlock_fd_n " failed"); + ngx_log_abort(err, ngx_unlock_fd_n " %s failed", mtx->name); } diff --git a/src/core/ngx_slab.c b/src/core/ngx_slab.c --- a/src/core/ngx_slab.c +++ b/src/core/ngx_slab.c @@ -6,23 +6,6 @@ #include #include -/* - - 12 - 2048 2 11 - 1024 4 10 - 512 8 9 - 256 16 8 - - 128 32 4 32 7 - - 64 64 8 63 6 1 - 32 128 16 127 5 1 - 16 256 32 254 4 2 - 8 512 64 504 3 8 - - */ - #define NGX_SLAB_PAGE_MASK 3 #define NGX_SLAB_PAGE 0 @@ -80,6 +63,8 @@ static ngx_slab_page_t *ngx_slab_alloc_p ngx_uint_t pages); static void ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page, ngx_uint_t pages); +static void ngx_slab_error(ngx_slab_pool_t *pool, ngx_uint_t level, + char *text); static ngx_uint_t ngx_slab_max_size; @@ -147,11 +132,8 @@ ngx_slab_init(ngx_slab_pool_t *pool) pool->pages->slab = pages; } -#if 0 - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "slab: %p, %p, %ui, %d", - pool, pool->start, pages, - (pool->end - pool->start) / ngx_pagesize - pages); -#endif + pool->log_ctx = &pool->zero; + pool->zero = '\0'; } @@ -438,8 +420,7 @@ ngx_slab_free_locked(ngx_slab_pool_t *po ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, "slab free: %p", p); if ((u_char *) p < pool->start || (u_char *) p > pool->end) { - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, - "ngx_slab_free(): outside of pool"); + ngx_slab_error(pool, NGX_LOG_ALERT, "ngx_slab_free(): outside of pool"); goto fail; } @@ -587,14 +568,14 @@ ngx_slab_free_locked(ngx_slab_pool_t *po } if (slab == NGX_SLAB_PAGE_FREE) { - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, - "ngx_slab_free(): page is already free"); + ngx_slab_error(pool, NGX_LOG_ALERT, + "ngx_slab_free(): page is already free"); goto fail; } if (slab == NGX_SLAB_PAGE_BUSY) { - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, - "ngx_slab_free(): pointer to wrong page"); + ngx_slab_error(pool, NGX_LOG_ALERT, + "ngx_slab_free(): pointer to wrong page"); goto fail; } @@ -603,9 +584,9 @@ ngx_slab_free_locked(ngx_slab_pool_t *po ngx_slab_free_pages(pool, &pool->pages[n], size); - size <<= ngx_pagesize_shift; + ngx_slab_junk(p, size << ngx_pagesize_shift); - goto done; + return; } /* not reached */ @@ -620,15 +601,15 @@ done: wrong_chunk: - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, - "ngx_slab_free(): pointer to wrong chunk"); + ngx_slab_error(pool, NGX_LOG_ALERT, + "ngx_slab_free(): pointer to wrong chunk"); goto fail; chunk_already_free: - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, - "ngx_slab_free(): chunk is already free"); + ngx_slab_error(pool, NGX_LOG_ALERT, + "ngx_slab_free(): chunk is already free"); fail: @@ -661,11 +642,8 @@ ngx_slab_alloc_pages(ngx_slab_pool_t *po } page->slab = pages | NGX_SLAB_PAGE_START; - -#if (NGX_DEBUG) page->next = NULL; page->prev = NGX_SLAB_PAGE; -#endif if (--pages == 0) { return page; @@ -673,10 +651,8 @@ ngx_slab_alloc_pages(ngx_slab_pool_t *po for (p = page + 1; pages; pages--) { p->slab = NGX_SLAB_PAGE_BUSY; -#if (NGX_DEBUG) p->next = NULL; p->prev = NGX_SLAB_PAGE; -#endif p++; } @@ -684,8 +660,7 @@ ngx_slab_alloc_pages(ngx_slab_pool_t *po } } - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, NGX_ENOMEM, - "ngx_slab_alloc(): failed"); + ngx_slab_error(pool, NGX_LOG_CRIT, "ngx_slab_alloc() failed: no memory"); return NULL; } @@ -716,3 +691,10 @@ ngx_slab_free_pages(ngx_slab_pool_t *poo pool->free.next = page; } + + +static void +ngx_slab_error(ngx_slab_pool_t *pool, ngx_uint_t level, char *text) +{ + ngx_log_error(level, ngx_cycle->log, 0, "%s%s", text, pool->log_ctx); +} diff --git a/src/core/ngx_slab.h b/src/core/ngx_slab.h --- a/src/core/ngx_slab.h +++ b/src/core/ngx_slab.h @@ -34,6 +34,12 @@ typedef struct { u_char *end; ngx_shmtx_t mutex; + + u_char *log_ctx; + u_char zero; + + void *data; + void *addr; } ngx_slab_pool_t; diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c --- a/src/core/ngx_string.c +++ b/src/core/ngx_string.c @@ -8,6 +8,10 @@ #include +static u_char *ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64, + u_char zero, ngx_uint_t hexadecimal, ngx_uint_t width); + + void ngx_strlow(u_char *dst, u_char *src, size_t n) { @@ -26,12 +30,15 @@ ngx_cpystrn(u_char *dst, u_char *src, si return dst; } - for ( /* void */ ; --n; dst++, src++) { + while (--n) { *dst = *src; if (*dst == '\0') { return dst; } + + dst++; + src++; } *dst = '\0'; @@ -67,6 +74,7 @@ ngx_pstrdup(ngx_pool_t *pool, ngx_str_t * %[0][width][u][x|X]D int32_t/uint32_t * %[0][width][u][x|X]L int64_t/uint64_t * %[0][width|m][u][x|X]A ngx_atomic_int_t/ngx_atomic_uint_t + * %[0][width][.width]f float * %P ngx_pid_t * %M ngx_msec_t * %r rlim_t @@ -94,7 +102,7 @@ ngx_sprintf(u_char *buf, const char *fmt va_list args; va_start(args, fmt); - p = ngx_vsnprintf(buf, /* STUB */ 65536, fmt, args); + p = ngx_vslprintf(buf, (void *) -1, fmt, args); va_end(args); return p; @@ -108,7 +116,21 @@ ngx_snprintf(u_char *buf, size_t max, co va_list args; va_start(args, fmt); - p = ngx_vsnprintf(buf, max, fmt, args); + p = ngx_vslprintf(buf, buf + max, fmt, args); + va_end(args); + + return p; +} + + +u_char * ngx_cdecl +ngx_slprintf(u_char *buf, u_char *last, const char *fmt, ...) +{ + u_char *p; + va_list args; + + va_start(args, fmt); + p = ngx_vslprintf(buf, last, fmt, args); va_end(args); return p; @@ -116,30 +138,18 @@ ngx_snprintf(u_char *buf, size_t max, co u_char * -ngx_vsnprintf(u_char *buf, size_t max, const char *fmt, va_list args) +ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args) { - u_char *p, zero, *last, temp[NGX_INT64_LEN + 1]; - /* - * really we need temp[NGX_INT64_LEN] only, - * but icc issues the warning - */ + u_char *p, zero; int d; + float f, scale; size_t len, slen; - uint32_t ui32; int64_t i64; uint64_t ui64; ngx_msec_t ms; - ngx_uint_t width, sign, hexadecimal, max_width; + ngx_uint_t width, sign, hex, max_width, frac_width, i; ngx_str_t *v; ngx_variable_value_t *vv; - static u_char hex[] = "0123456789abcdef"; - static u_char HEX[] = "0123456789ABCDEF"; - - if (max == 0) { - return buf; - } - - last = buf + max; while (*fmt && buf < last) { @@ -156,12 +166,11 @@ ngx_vsnprintf(u_char *buf, size_t max, c zero = (u_char) ((*++fmt == '0') ? '0' : ' '); width = 0; sign = 1; - hexadecimal = 0; + hex = 0; max_width = 0; + frac_width = 0; slen = (size_t) -1; - p = temp + NGX_INT64_LEN; - while (*fmt >= '0' && *fmt <= '9') { width = width * 10 + *fmt++ - '0'; } @@ -181,17 +190,26 @@ ngx_vsnprintf(u_char *buf, size_t max, c continue; case 'X': - hexadecimal = 2; + hex = 2; sign = 0; fmt++; continue; case 'x': - hexadecimal = 1; + hex = 1; sign = 0; fmt++; continue; + case '.': + fmt++; + + while (*fmt >= '0' && *fmt <= '9') { + frac_width = frac_width * 10 + *fmt++ - '0'; + } + + break; + case '*': slen = va_arg(args, size_t); fmt++; @@ -339,6 +357,43 @@ ngx_vsnprintf(u_char *buf, size_t max, c break; + case 'f': + f = (float) va_arg(args, double); + + if (f < 0) { + *buf++ = '-'; + f = -f; + } + + ui64 = (int64_t) f; + + buf = ngx_sprintf_num(buf, last, ui64, zero, 0, width); + + if (frac_width) { + + if (buf < last) { + *buf++ = '.'; + } + + scale = 1.0; + + for (i = 0; i < frac_width; i++) { + scale *= 10.0; + } + + /* + * (int64_t) cast is required for msvc6: + * it can not convert uint64_t to double + */ + ui64 = (uint64_t) ((f - (int64_t) ui64) * scale); + + buf = ngx_sprintf_num(buf, last, ui64, '0', 0, frac_width); + } + + fmt++; + + continue; + #if !(NGX_WIN32) case 'r': i64 = (int64_t) va_arg(args, rlim_t); @@ -348,7 +403,7 @@ ngx_vsnprintf(u_char *buf, size_t max, c case 'p': ui64 = (uintptr_t) va_arg(args, void *); - hexadecimal = 2; + hex = 2; sign = 0; zero = '0'; width = NGX_PTR_SIZE * 2; @@ -398,63 +453,7 @@ ngx_vsnprintf(u_char *buf, size_t max, c } } - if (hexadecimal == 1) { - do { - - /* the "(uint32_t)" cast disables the BCC's warning */ - *--p = hex[(uint32_t) (ui64 & 0xf)]; - - } while (ui64 >>= 4); - - } else if (hexadecimal == 2) { - do { - - /* the "(uint32_t)" cast disables the BCC's warning */ - *--p = HEX[(uint32_t) (ui64 & 0xf)]; - - } while (ui64 >>= 4); - - } else if (ui64 <= NGX_MAX_UINT32_VALUE) { - - /* - * To divide 64-bit number and to find the remainder - * on the x86 platform gcc and icc call the libc functions - * [u]divdi3() and [u]moddi3(), they call another function - * in its turn. On FreeBSD it is the qdivrem() function, - * its source code is about 170 lines of the code. - * The glibc counterpart is about 150 lines of the code. - * - * For 32-bit numbers and some divisors gcc and icc use - * the inlined multiplication and shifts. For example, - * unsigned "i32 / 10" is compiled to - * - * (i32 * 0xCCCCCCCD) >> 35 - */ - - ui32 = (uint32_t) ui64; - - do { - *--p = (u_char) (ui32 % 10 + '0'); - } while (ui32 /= 10); - - } else { - do { - *--p = (u_char) (ui64 % 10 + '0'); - } while (ui64 /= 10); - } - - len = (temp + NGX_INT64_LEN) - p; - - while (len++ < width && buf < last) { - *buf++ = zero; - } - - len = (temp + NGX_INT64_LEN) - p; - if (buf + len > last) { - len = last - buf; - } - - buf = ngx_cpymem(buf, p, len); + buf = ngx_sprintf_num(buf, last, ui64, zero, hex, width); fmt++; @@ -467,6 +466,92 @@ ngx_vsnprintf(u_char *buf, size_t max, c } +static u_char * +ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64, u_char zero, + ngx_uint_t hexadecimal, ngx_uint_t width) +{ + u_char *p, temp[NGX_INT64_LEN + 1]; + /* + * we need temp[NGX_INT64_LEN] only, + * but icc issues the warning + */ + size_t len; + uint32_t ui32; + static u_char hex[] = "0123456789abcdef"; + static u_char HEX[] = "0123456789ABCDEF"; + + p = temp + NGX_INT64_LEN; + + if (hexadecimal == 0) { + + if (ui64 <= NGX_MAX_UINT32_VALUE) { + + /* + * To divide 64-bit numbers and to find remainders + * on the x86 platform gcc and icc call the libc functions + * [u]divdi3() and [u]moddi3(), they call another function + * in its turn. On FreeBSD it is the qdivrem() function, + * its source code is about 170 lines of the code. + * The glibc counterpart is about 150 lines of the code. + * + * For 32-bit numbers and some divisors gcc and icc use + * a inlined multiplication and shifts. For example, + * unsigned "i32 / 10" is compiled to + * + * (i32 * 0xCCCCCCCD) >> 35 + */ + + ui32 = (uint32_t) ui64; + + do { + *--p = (u_char) (ui32 % 10 + '0'); + } while (ui32 /= 10); + + } else { + do { + *--p = (u_char) (ui64 % 10 + '0'); + } while (ui64 /= 10); + } + + } else if (hexadecimal == 1) { + + do { + + /* the "(uint32_t)" cast disables the BCC's warning */ + *--p = hex[(uint32_t) (ui64 & 0xf)]; + + } while (ui64 >>= 4); + + } else { /* hexadecimal == 2 */ + + do { + + /* the "(uint32_t)" cast disables the BCC's warning */ + *--p = HEX[(uint32_t) (ui64 & 0xf)]; + + } while (ui64 >>= 4); + } + + /* zero or space padding */ + + len = (temp + NGX_INT64_LEN) - p; + + while (len++ < width && buf < last) { + *buf++ = zero; + } + + /* number safe copy */ + + len = (temp + NGX_INT64_LEN) - p; + + if (buf + len > last) { + len = last - buf; + } + + return ngx_cpymem(buf, p, len); +} + + /* * We use ngx_strcasecmp()/ngx_strncasecmp() for 7-bit ASCII strings only, * and implement our own ngx_strcasecmp()/ngx_strncasecmp() @@ -618,6 +703,39 @@ ngx_strcasestrn(u_char *s1, char *s2, si } +/* + * ngx_strlcasestrn() is intended to search for static substring + * with known length in string until the argument last. The argument n + * must be length of the second substring - 1. + */ + +u_char * +ngx_strlcasestrn(u_char *s1, u_char *last, u_char *s2, size_t n) +{ + ngx_uint_t c1, c2; + + c2 = (ngx_uint_t) *s2++; + c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2; + last -= n; + + do { + do { + if (s1 >= last) { + return NULL; + } + + c1 = (ngx_uint_t) *s1++; + + c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1; + + } while (c1 != c2); + + } while (ngx_strncasecmp(s1, s2, n) != 0); + + return --s1; +} + + ngx_int_t ngx_rstrncmp(u_char *s1, u_char *s2, size_t n) { diff --git a/src/core/ngx_string.h b/src/core/ngx_string.h --- a/src/core/ngx_string.h +++ b/src/core/ngx_string.h @@ -140,7 +140,11 @@ u_char *ngx_cpystrn(u_char *dst, u_char u_char *ngx_pstrdup(ngx_pool_t *pool, ngx_str_t *src); u_char * ngx_cdecl ngx_sprintf(u_char *buf, const char *fmt, ...); u_char * ngx_cdecl ngx_snprintf(u_char *buf, size_t max, const char *fmt, ...); -u_char *ngx_vsnprintf(u_char *buf, size_t max, const char *fmt, va_list args); +u_char * ngx_cdecl ngx_slprintf(u_char *buf, u_char *last, const char *fmt, + ...); +u_char *ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args); +#define ngx_vsnprintf(buf, max, fmt, args) \ + ngx_vslprintf(buf, buf + (max), fmt, args) ngx_int_t ngx_strcasecmp(u_char *s1, u_char *s2); ngx_int_t ngx_strncasecmp(u_char *s1, u_char *s2, size_t n); @@ -149,6 +153,7 @@ u_char *ngx_strnstr(u_char *s1, char *s2 u_char *ngx_strstrn(u_char *s1, char *s2, size_t n); u_char *ngx_strcasestrn(u_char *s1, char *s2, size_t n); +u_char *ngx_strlcasestrn(u_char *s1, u_char *last, u_char *s2, size_t n); ngx_int_t ngx_rstrncmp(u_char *s1, u_char *s2, size_t n); ngx_int_t ngx_rstrncasecmp(u_char *s1, u_char *s2, size_t n); diff --git a/src/event/modules/ngx_aio_module.c b/src/event/modules/ngx_aio_module.c --- a/src/event/modules/ngx_aio_module.c +++ b/src/event/modules/ngx_aio_module.c @@ -134,12 +134,14 @@ ngx_aio_del_connection(ngx_connection_t ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "aio_cancel: %d", rc); if (rc == AIO_CANCELED) { - c->read->active = c->write->active = 0; + c->read->active = 0; + c->write->active = 0; return NGX_OK; } if (rc == AIO_ALLDONE) { - c->read->active = c->write->active = 0; + c->read->active = 0; + c->write->active = 0; ngx_log_error(NGX_LOG_ALERT, c->log, 0, "aio_cancel() returned AIO_ALLDONE"); return NGX_OK; diff --git a/src/event/modules/ngx_devpoll_module.c b/src/event/modules/ngx_devpoll_module.c --- a/src/event/modules/ngx_devpoll_module.c +++ b/src/event/modules/ngx_devpoll_module.c @@ -550,7 +550,7 @@ ngx_devpoll_create_conf(ngx_cycle_t *cyc dpcf = ngx_palloc(cycle->pool, sizeof(ngx_devpoll_conf_t)); if (dpcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } dpcf->changes = NGX_CONF_UNSET; diff --git a/src/event/modules/ngx_epoll_module.c b/src/event/modules/ngx_epoll_module.c --- a/src/event/modules/ngx_epoll_module.c +++ b/src/event/modules/ngx_epoll_module.c @@ -552,7 +552,7 @@ ngx_epoll_create_conf(ngx_cycle_t *cycle epcf = ngx_palloc(cycle->pool, sizeof(ngx_epoll_conf_t)); if (epcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } epcf->events = NGX_CONF_UNSET; diff --git a/src/event/modules/ngx_eventport_module.c b/src/event/modules/ngx_eventport_module.c --- a/src/event/modules/ngx_eventport_module.c +++ b/src/event/modules/ngx_eventport_module.c @@ -581,7 +581,7 @@ ngx_eventport_create_conf(ngx_cycle_t *c epcf = ngx_palloc(cycle->pool, sizeof(ngx_eventport_conf_t)); if (epcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } epcf->events = NGX_CONF_UNSET; diff --git a/src/event/modules/ngx_kqueue_module.c b/src/event/modules/ngx_kqueue_module.c --- a/src/event/modules/ngx_kqueue_module.c +++ b/src/event/modules/ngx_kqueue_module.c @@ -768,7 +768,7 @@ ngx_kqueue_create_conf(ngx_cycle_t *cycl kcf = ngx_palloc(cycle->pool, sizeof(ngx_kqueue_conf_t)); if (kcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } kcf->changes = NGX_CONF_UNSET; diff --git a/src/event/modules/ngx_rtsig_module.c b/src/event/modules/ngx_rtsig_module.c --- a/src/event/modules/ngx_rtsig_module.c +++ b/src/event/modules/ngx_rtsig_module.c @@ -691,7 +691,7 @@ ngx_rtsig_create_conf(ngx_cycle_t *cycle rtscf = ngx_palloc(cycle->pool, sizeof(ngx_rtsig_conf_t)); if (rtscf == NULL) { - return NGX_CONF_ERROR; + return NULL; } rtscf->signo = NGX_CONF_UNSET; diff --git a/src/event/modules/ngx_select_module.c b/src/event/modules/ngx_select_module.c --- a/src/event/modules/ngx_select_module.c +++ b/src/event/modules/ngx_select_module.c @@ -9,7 +9,6 @@ #include - static ngx_int_t ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer); static void ngx_select_done(ngx_cycle_t *cycle); static ngx_int_t ngx_select_add_event(ngx_event_t *ev, ngx_int_t event, @@ -18,6 +17,7 @@ static ngx_int_t ngx_select_del_event(ng ngx_uint_t flags); static ngx_int_t ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags); +static void ngx_select_repair_fd_sets(ngx_cycle_t *cycle); static char *ngx_select_init_conf(ngx_cycle_t *cycle, void *conf); @@ -26,13 +26,7 @@ static fd_set master_write_fd_se static fd_set work_read_fd_set; static fd_set work_write_fd_set; -#if (NGX_WIN32) -static ngx_uint_t max_read; -static ngx_uint_t max_write; -#else static ngx_int_t max_fd; -#endif - static ngx_uint_t nevents; static ngx_event_t **event_index; @@ -111,11 +105,7 @@ ngx_select_init(ngx_cycle_t *cycle, ngx_ ngx_event_flags = NGX_USE_LEVEL_EVENT; -#if (NGX_WIN32) - max_read = max_write = 0; -#else max_fd = -1; -#endif return NGX_OK; } @@ -146,30 +136,17 @@ ngx_select_add_event(ngx_event_t *ev, ng return NGX_OK; } -#if (NGX_WIN32) - - if ((event == NGX_READ_EVENT) && (max_read >= FD_SETSIZE) - || (event == NGX_WRITE_EVENT) && (max_write >= FD_SETSIZE)) + if ((event == NGX_READ_EVENT && ev->write) + || (event == NGX_WRITE_EVENT && !ev->write)) { - ngx_log_error(NGX_LOG_ERR, ev->log, 0, - "maximum number of descriptors " - "supported by select() is %d", FD_SETSIZE); + ngx_log_error(NGX_LOG_ALERT, ev->log, 0, + "invalid select %s event fd:%d ev:%i", + ev->write ? "write" : "read", c->fd, event); return NGX_ERROR; } if (event == NGX_READ_EVENT) { FD_SET(c->fd, &master_read_fd_set); - max_read++; - - } else if (event == NGX_WRITE_EVENT) { - FD_SET(c->fd, &master_write_fd_set); - max_write++; - } - -#else - - if (event == NGX_READ_EVENT) { - FD_SET(c->fd, &master_read_fd_set); } else if (event == NGX_WRITE_EVENT) { FD_SET(c->fd, &master_write_fd_set); @@ -179,8 +156,6 @@ ngx_select_add_event(ngx_event_t *ev, ng max_fd = c->fd; } -#endif - ev->active = 1; event_index[nevents] = ev; @@ -194,6 +169,7 @@ ngx_select_add_event(ngx_event_t *ev, ng static ngx_int_t ngx_select_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) { + ngx_event_t *e; ngx_connection_t *c; c = ev->data; @@ -207,19 +183,6 @@ ngx_select_del_event(ngx_event_t *ev, ng ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, "select del event fd:%d ev:%i", c->fd, event); -#if (NGX_WIN32) - - if (event == NGX_READ_EVENT) { - FD_CLR(c->fd, &master_read_fd_set); - max_read--; - - } else if (event == NGX_WRITE_EVENT) { - FD_CLR(c->fd, &master_write_fd_set); - max_write--; - } - -#else - if (event == NGX_READ_EVENT) { FD_CLR(c->fd, &master_read_fd_set); @@ -231,11 +194,10 @@ ngx_select_del_event(ngx_event_t *ev, ng max_fd = -1; } -#endif - if (ev->index < --nevents) { - event_index[ev->index] = event_index[nevents]; - event_index[ev->index]->index = ev->index; + e = event_index[nevents]; + event_index[ev->index] = e; + e->index = ev->index; } ev->index = NGX_INVALID_INDEX; @@ -248,14 +210,12 @@ static ngx_int_t ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) { - int ready, nready; - ngx_uint_t i, found; - ngx_err_t err; - ngx_event_t *ev, **queue; - ngx_connection_t *c; - struct timeval tv, *tp; - -#if !(NGX_WIN32) + int ready, nready; + ngx_err_t err; + ngx_uint_t i, found; + ngx_event_t *ev, **queue; + struct timeval tv, *tp; + ngx_connection_t *c; if (max_fd == -1) { for (i = 0; i < nevents; i++) { @@ -269,8 +229,6 @@ ngx_select_process_events(ngx_cycle_t *c "change max_fd: %d", max_fd); } -#endif - #if (NGX_DEBUG) if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) { for (i = 0; i < nevents; i++) { @@ -280,10 +238,8 @@ ngx_select_process_events(ngx_cycle_t *c "select event: fd:%d wr:%d", c->fd, ev->write); } -#if !(NGX_WIN32) ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "max_fd: %d", max_fd); -#endif } #endif @@ -302,26 +258,8 @@ ngx_select_process_events(ngx_cycle_t *c work_read_fd_set = master_read_fd_set; work_write_fd_set = master_write_fd_set; -#if 1 - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, - /* - * (void *) disables "dereferencing type-punned - * pointer will break strict-aliasing rules - */ - "select read fd_set: %08Xd", - *(int *) (void *) &work_read_fd_set); -#endif - -#if (NGX_WIN32) - - ready = select(0, &work_read_fd_set, &work_write_fd_set, NULL, tp); - -#else - ready = select(max_fd + 1, &work_read_fd_set, &work_write_fd_set, NULL, tp); -#endif - if (ready == -1) { err = ngx_socket_errno; } else { @@ -335,15 +273,6 @@ ngx_select_process_events(ngx_cycle_t *c ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "select ready %d", ready); -#if (NGX_WIN32) - - if (err) { - ngx_log_error(NGX_LOG_ALERT, cycle->log, err, "select() failed"); - return NGX_ERROR; - } - -#else - if (err) { ngx_uint_t level; @@ -361,11 +290,14 @@ ngx_select_process_events(ngx_cycle_t *c } ngx_log_error(level, cycle->log, err, "select() failed"); + + if (err == EBADF) { + ngx_select_repair_fd_sets(cycle); + } + return NGX_ERROR; } -#endif - if (ready == 0) { if (timer != NGX_TIMER_INFINITE) { return NGX_OK; @@ -414,13 +346,64 @@ ngx_select_process_events(ngx_cycle_t *c ngx_mutex_unlock(ngx_posted_events_mutex); if (ready != nready) { - ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "select ready != events"); + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "select ready != events: %d:%d", ready, nready); + + ngx_select_repair_fd_sets(cycle); } return NGX_OK; } +static void +ngx_select_repair_fd_sets(ngx_cycle_t *cycle) +{ + int n; + socklen_t len; + ngx_err_t err; + ngx_socket_t s; + + for (s = 0; s <= max_fd; s++) { + + if (FD_ISSET(s, &master_read_fd_set) == 0) { + continue; + } + + len = sizeof(int); + + if (getsockopt(s, SOL_SOCKET, SO_TYPE, &n, &len) == -1) { + err = ngx_socket_errno; + + ngx_log_error(NGX_LOG_ALERT, cycle->log, err, + "invalid descriptor #%d in read fd_set", s); + + FD_CLR(s, &master_read_fd_set); + } + } + + for (s = 0; s <= max_fd; s++) { + + if (FD_ISSET(s, &master_write_fd_set) == 0) { + continue; + } + + len = sizeof(int); + + if (getsockopt(s, SOL_SOCKET, SO_TYPE, &n, &len) == -1) { + err = ngx_socket_errno; + + ngx_log_error(NGX_LOG_ALERT, cycle->log, err, + "invalid descriptor #%d in write fd_set", s); + + FD_CLR(s, &master_write_fd_set); + } + } + + max_fd = -1; +} + + static char * ngx_select_init_conf(ngx_cycle_t *cycle, void *conf) { @@ -434,8 +417,6 @@ ngx_select_init_conf(ngx_cycle_t *cycle, /* disable warning: the default FD_SETSIZE is 1024U in FreeBSD 5.x */ -#if !(NGX_WIN32) - if (cycle->connection_n > FD_SETSIZE) { ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "the maximum number of files " @@ -443,9 +424,7 @@ ngx_select_init_conf(ngx_cycle_t *cycle, return NGX_CONF_ERROR; } -#endif - -#if (NGX_THREADS) && !(NGX_WIN32) +#if (NGX_THREADS) ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "select() is not supported in the threaded mode"); diff --git a/src/event/modules/ngx_win32_select_module.c b/src/event/modules/ngx_win32_select_module.c new file mode 100644 --- /dev/null +++ b/src/event/modules/ngx_win32_select_module.c @@ -0,0 +1,403 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +static ngx_int_t ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer); +static void ngx_select_done(ngx_cycle_t *cycle); +static ngx_int_t ngx_select_add_event(ngx_event_t *ev, ngx_int_t event, + ngx_uint_t flags); +static ngx_int_t ngx_select_del_event(ngx_event_t *ev, ngx_int_t event, + ngx_uint_t flags); +static ngx_int_t ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, + ngx_uint_t flags); +static void ngx_select_repair_fd_sets(ngx_cycle_t *cycle); +static char *ngx_select_init_conf(ngx_cycle_t *cycle, void *conf); + + +static fd_set master_read_fd_set; +static fd_set master_write_fd_set; +static fd_set work_read_fd_set; +static fd_set work_write_fd_set; + +static ngx_uint_t max_read; +static ngx_uint_t max_write; +static ngx_uint_t nevents; + +static ngx_event_t **event_index; + + +static ngx_str_t select_name = ngx_string("select"); + +ngx_event_module_t ngx_select_module_ctx = { + &select_name, + NULL, /* create configuration */ + ngx_select_init_conf, /* init configuration */ + + { + ngx_select_add_event, /* add an event */ + ngx_select_del_event, /* delete an event */ + ngx_select_add_event, /* enable an event */ + ngx_select_del_event, /* disable an event */ + NULL, /* add an connection */ + NULL, /* delete an connection */ + NULL, /* process the changes */ + ngx_select_process_events, /* process the events */ + ngx_select_init, /* init the events */ + ngx_select_done /* done the events */ + } + +}; + +ngx_module_t ngx_select_module = { + NGX_MODULE_V1, + &ngx_select_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_EVENT_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_int_t +ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer) +{ + ngx_event_t **index; + + if (event_index == NULL) { + FD_ZERO(&master_read_fd_set); + FD_ZERO(&master_write_fd_set); + nevents = 0; + } + + if (ngx_process == NGX_PROCESS_WORKER + || cycle->old_cycle == NULL + || cycle->old_cycle->connection_n < cycle->connection_n) + { + index = ngx_alloc(sizeof(ngx_event_t *) * 2 * cycle->connection_n, + cycle->log); + if (index == NULL) { + return NGX_ERROR; + } + + if (event_index) { + ngx_memcpy(index, event_index, sizeof(ngx_event_t *) * nevents); + ngx_free(event_index); + } + + event_index = index; + } + + ngx_io = ngx_os_io; + + ngx_event_actions = ngx_select_module_ctx.actions; + + ngx_event_flags = NGX_USE_LEVEL_EVENT; + + max_read = 0; + max_write = 0; + + return NGX_OK; +} + + +static void +ngx_select_done(ngx_cycle_t *cycle) +{ + ngx_free(event_index); + + event_index = NULL; +} + + +static ngx_int_t +ngx_select_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) +{ + ngx_connection_t *c; + + c = ev->data; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "select add event fd:%d ev:%i", c->fd, event); + + if (ev->index != NGX_INVALID_INDEX) { + ngx_log_error(NGX_LOG_ALERT, ev->log, 0, + "select event fd:%d ev:%i is already set", c->fd, event); + return NGX_OK; + } + + if ((event == NGX_READ_EVENT && ev->write) + || (event == NGX_WRITE_EVENT && !ev->write)) + { + ngx_log_error(NGX_LOG_ALERT, ev->log, 0, + "invalid select %s event fd:%d ev:%i", + ev->write ? "write" : "read", c->fd, event); + return NGX_ERROR; + } + + if ((event == NGX_READ_EVENT) && (max_read >= FD_SETSIZE) + || (event == NGX_WRITE_EVENT) && (max_write >= FD_SETSIZE)) + { + ngx_log_error(NGX_LOG_ERR, ev->log, 0, + "maximum number of descriptors " + "supported by select() is %d", FD_SETSIZE); + return NGX_ERROR; + } + + if (event == NGX_READ_EVENT) { + FD_SET(c->fd, &master_read_fd_set); + max_read++; + + } else if (event == NGX_WRITE_EVENT) { + FD_SET(c->fd, &master_write_fd_set); + max_write++; + } + + ev->active = 1; + + event_index[nevents] = ev; + ev->index = nevents; + nevents++; + + return NGX_OK; +} + + +static ngx_int_t +ngx_select_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) +{ + ngx_event_t *e; + ngx_connection_t *c; + + c = ev->data; + + ev->active = 0; + + if (ev->index == NGX_INVALID_INDEX) { + return NGX_OK; + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "select del event fd:%d ev:%i", c->fd, event); + + if (event == NGX_READ_EVENT) { + FD_CLR(c->fd, &master_read_fd_set); + max_read--; + + } else if (event == NGX_WRITE_EVENT) { + FD_CLR(c->fd, &master_write_fd_set); + max_write--; + } + + if (ev->index < --nevents) { + e = event_index[nevents]; + event_index[ev->index] = e; + e->index = ev->index; + } + + ev->index = NGX_INVALID_INDEX; + + return NGX_OK; +} + + +static ngx_int_t +ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, + ngx_uint_t flags) +{ + int ready, nready; + ngx_err_t err; + ngx_uint_t i, found; + ngx_event_t *ev, **queue; + struct timeval tv, *tp; + ngx_connection_t *c; + +#if (NGX_DEBUG) + if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) { + for (i = 0; i < nevents; i++) { + ev = event_index[i]; + c = ev->data; + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "select event: fd:%d wr:%d", c->fd, ev->write); + } + } +#endif + + if (timer == NGX_TIMER_INFINITE) { + tp = NULL; + + } else { + tv.tv_sec = (long) (timer / 1000); + tv.tv_usec = (long) ((timer % 1000) * 1000); + tp = &tv; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "select timer: %M", timer); + + work_read_fd_set = master_read_fd_set; + work_write_fd_set = master_write_fd_set; + + if (max_read || max_write) { + ready = select(0, &work_read_fd_set, &work_write_fd_set, NULL, tp); + + } else { + + /* + * Winsock select() requires that at least one descriptor set must be + * be non-null, and any non-null descriptor set must contain at least + * one handle to a socket. Otherwise select() returns WSAEINVAL. + */ + + ngx_msleep(timer); + + ready = 0; + } + + if (ready == -1) { + err = ngx_socket_errno; + } else { + err = 0; + } + + if (flags & NGX_UPDATE_TIME) { + ngx_time_update(0, 0); + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "select ready %d", ready); + + if (err) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, err, "select() failed"); + + if (err == WSAENOTSOCK) { + ngx_select_repair_fd_sets(cycle); + } + + return NGX_ERROR; + } + + if (ready == 0) { + if (timer != NGX_TIMER_INFINITE) { + return NGX_OK; + } + + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "select() returned no events without timeout"); + return NGX_ERROR; + } + + ngx_mutex_lock(ngx_posted_events_mutex); + + nready = 0; + + for (i = 0; i < nevents; i++) { + ev = event_index[i]; + c = ev->data; + found = 0; + + if (ev->write) { + if (FD_ISSET(c->fd, &work_write_fd_set)) { + found = 1; + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "select write %d", c->fd); + } + + } else { + if (FD_ISSET(c->fd, &work_read_fd_set)) { + found = 1; + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "select read %d", c->fd); + } + } + + if (found) { + ev->ready = 1; + + queue = (ngx_event_t **) (ev->accept ? &ngx_posted_accept_events: + &ngx_posted_events); + ngx_locked_post_event(ev, queue); + + nready++; + } + } + + ngx_mutex_unlock(ngx_posted_events_mutex); + + if (ready != nready) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "select ready != events: %d:%d", ready, nready); + + ngx_select_repair_fd_sets(cycle); + } + + return NGX_OK; +} + + +static void +ngx_select_repair_fd_sets(ngx_cycle_t *cycle) +{ + int n; + u_int i; + socklen_t len; + ngx_err_t err; + ngx_socket_t s; + + for (i = 0; i < master_read_fd_set.fd_count; i++) { + + s = master_read_fd_set.fd_array[i]; + len = sizeof(int); + + if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *) &n, &len) == -1) { + err = ngx_socket_errno; + + ngx_log_error(NGX_LOG_ALERT, cycle->log, err, + "invalid descriptor #%d in read fd_set", s); + + FD_CLR(s, &master_read_fd_set); + } + } + + for (i = 0; i < master_write_fd_set.fd_count; i++) { + + s = master_write_fd_set.fd_array[i]; + len = sizeof(int); + + if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *) &n, &len) == -1) { + err = ngx_socket_errno; + + ngx_log_error(NGX_LOG_ALERT, cycle->log, err, + "invalid descriptor #%d in write fd_set", s); + + FD_CLR(s, &master_write_fd_set); + } + } +} + + +static char * +ngx_select_init_conf(ngx_cycle_t *cycle, void *conf) +{ + ngx_event_conf_t *ecf; + + ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module); + + if (ecf->use != ngx_select_module.ctx_index) { + return NGX_CONF_OK; + } + + return NGX_CONF_OK; +} diff --git a/src/event/ngx_event.c b/src/event/ngx_event.c --- a/src/event/ngx_event.c +++ b/src/event/ngx_event.c @@ -442,7 +442,7 @@ ngx_event_module_init(ngx_cycle_t *cycle ecf = (*cf)[ngx_event_core_module.ctx_index]; - if (!ngx_test_config) { + if (!ngx_test_config && ngx_process <= NGX_PROCESS_MASTER) { ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "using the \"%s\" event method", ecf->name); } @@ -506,6 +506,8 @@ ngx_event_module_init(ngx_cycle_t *cycle #endif shm.size = size; + shm.name.len = sizeof("nginx_shared_zone"); + shm.name.data = (u_char *) "nginx_shared_zone"; shm.log = cycle->log; if (ngx_shm_alloc(&shm) != NGX_OK) { @@ -535,7 +537,7 @@ ngx_event_module_init(ngx_cycle_t *cycle #endif - *ngx_connection_counter = 1; + (void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1); ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "counter: %p, %d", @@ -774,6 +776,10 @@ ngx_event_process_init(ngx_cycle_t *cycl rev->handler = ngx_event_acceptex; + if (ngx_use_accept_mutex) { + continue; + } + if (ngx_add_event(rev, 0, NGX_IOCP_ACCEPT) == NGX_ERROR) { return NGX_ERROR; } @@ -790,6 +796,10 @@ ngx_event_process_init(ngx_cycle_t *cycl } else { rev->handler = ngx_event_accept; + if (ngx_use_accept_mutex) { + continue; + } + if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { return NGX_ERROR; } @@ -1040,18 +1050,16 @@ ngx_event_debug_connection(ngx_conf_t *c ngx_str_t *value; ngx_event_debug_t *dc; struct hostent *h; - ngx_inet_cidr_t in_cidr; + ngx_cidr_t cidr; value = cf->args->elts; - /* AF_INET only */ - dc = ngx_array_push(&ecf->debug_connection); if (dc == NULL) { return NGX_CONF_ERROR; } - rc = ngx_ptocidr(&value[1], &in_cidr); + rc = ngx_ptocidr(&value[1], &cidr); if (rc == NGX_DONE) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, @@ -1060,8 +1068,18 @@ ngx_event_debug_connection(ngx_conf_t *c } if (rc == NGX_OK) { - dc->mask = in_cidr.mask; - dc->addr = in_cidr.addr; + + /* AF_INET only */ + + if (cidr.family != AF_INET) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"debug_connection\" supports IPv4 only"); + return NGX_CONF_ERROR; + } + + dc->mask = cidr.u.in.mask; + dc->addr = cidr.u.in.addr; + return NGX_CONF_OK; } @@ -1095,7 +1113,7 @@ ngx_event_create_conf(ngx_cycle_t *cycle ecf = ngx_palloc(cycle->pool, sizeof(ngx_event_conf_t)); if (ecf == NULL) { - return NGX_CONF_ERROR; + return NULL; } ecf->connections = NGX_CONF_UNSET_UINT; @@ -1110,7 +1128,7 @@ ngx_event_create_conf(ngx_cycle_t *cycle if (ngx_array_init(&ecf->debug_connection, cycle->pool, 4, sizeof(ngx_event_debug_t)) == NGX_ERROR) { - return NGX_CONF_ERROR; + return NULL; } #endif diff --git a/src/event/ngx_event_accept.c b/src/event/ngx_event_accept.c --- a/src/event/ngx_event_accept.c +++ b/src/event/ngx_event_accept.c @@ -9,10 +9,6 @@ #include -/* the buffer size is enough to hold "struct sockaddr_un" */ -#define NGX_SOCKLEN 512 - - static ngx_int_t ngx_enable_accept_events(ngx_cycle_t *cycle); static ngx_int_t ngx_disable_accept_events(ngx_cycle_t *cycle); static void ngx_close_accepted_connection(ngx_connection_t *c); @@ -29,7 +25,7 @@ ngx_event_accept(ngx_event_t *ev) ngx_listening_t *ls; ngx_connection_t *c, *lc; ngx_event_conf_t *ecf; - char sa[NGX_SOCKLEN]; + u_char sa[NGX_SOCKADDRLEN]; ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module); @@ -48,7 +44,7 @@ ngx_event_accept(ngx_event_t *ev) "accept on %V, ready: %d", &ls->addr_text, ev->available); do { - socklen = NGX_SOCKLEN; + socklen = NGX_SOCKADDRLEN; s = accept(lc->fd, (struct sockaddr *) sa, &socklen); @@ -61,8 +57,8 @@ ngx_event_accept(ngx_event_t *ev) return; } - ngx_log_error((err == NGX_ECONNABORTED) ? NGX_LOG_ERR: - NGX_LOG_ALERT, + ngx_log_error((ngx_uint_t) ((err == NGX_ECONNABORTED) ? + NGX_LOG_ERR : NGX_LOG_ALERT), ev->log, err, "accept() failed"); if (err == NGX_ECONNABORTED) { @@ -79,7 +75,7 @@ ngx_event_accept(ngx_event_t *ev) } #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_accepted, 1); + (void) ngx_atomic_fetch_add(ngx_stat_accepted, 1); #endif ngx_accept_disabled = ngx_cycle->connection_n / 8 @@ -97,7 +93,7 @@ ngx_event_accept(ngx_event_t *ev) } #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_active, 1); + (void) ngx_atomic_fetch_add(ngx_stat_active, 1); #endif c->pool = ngx_create_pool(ls->pool_size, ev->log); @@ -153,8 +149,10 @@ ngx_event_accept(ngx_event_t *ev) c->log = log; c->pool->log = log; + c->socklen = socklen; c->listening = ls; - c->socklen = socklen; + c->local_sockaddr = ls->sockaddr; + c->local_socklen = ls->socklen; c->unexpected_eof = 1; @@ -190,7 +188,7 @@ ngx_event_accept(ngx_event_t *ev) c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1); #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_handled, 1); + (void) ngx_atomic_fetch_add(ngx_stat_handled, 1); #endif #if (NGX_THREADS) @@ -208,7 +206,7 @@ ngx_event_accept(ngx_event_t *ev) } c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->addr_text.data, - ls->addr_text_max_len); + ls->addr_text_max_len, 0); if (c->addr_text.len == 0) { ngx_close_accepted_connection(c); return; @@ -381,7 +379,7 @@ ngx_close_accepted_connection(ngx_connec } #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_active, -1); + (void) ngx_atomic_fetch_add(ngx_stat_active, -1); #endif } diff --git a/src/event/ngx_event_connect.c b/src/event/ngx_event_connect.c --- a/src/event/ngx_event_connect.c +++ b/src/event/ngx_event_connect.c @@ -155,6 +155,7 @@ ngx_event_connect_peer(ngx_peer_connecti */ || err == NGX_EAGAIN #endif + || err == NGX_ECONNRESET || err == NGX_ENETDOWN || err == NGX_ENETUNREACH || err == NGX_EHOSTDOWN diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -10,7 +10,7 @@ typedef struct { - ngx_str_t engine; + ngx_uint_t engine; /* unsigned engine:1; */ } ngx_openssl_conf_t; @@ -37,26 +37,17 @@ static void ngx_ssl_session_rbtree_inser ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); static void *ngx_openssl_create_conf(ngx_cycle_t *cycle); -static char *ngx_openssl_init_conf(ngx_cycle_t *cycle, void *conf); +static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static void ngx_openssl_exit(ngx_cycle_t *cycle); -#if !(NGX_SSL_ENGINE) -static char *ngx_openssl_noengine(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf); -#endif - static ngx_command_t ngx_openssl_commands[] = { { ngx_string("ssl_engine"), NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, -#if (NGX_SSL_ENGINE) - ngx_conf_set_str_slot, -#else - ngx_openssl_noengine, -#endif + ngx_openssl_engine, 0, - offsetof(ngx_openssl_conf_t, engine), + 0, NULL }, ngx_null_command @@ -66,7 +57,7 @@ static ngx_command_t ngx_openssl_comman static ngx_core_module_t ngx_openssl_module_ctx = { ngx_string("openssl"), ngx_openssl_create_conf, - ngx_openssl_init_conf + NULL }; @@ -188,13 +179,6 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_ SSL_CTX_set_options(ssl->ctx, ngx_ssl_protocols[protocols >> 1]); } - /* - * we need this option because in ngx_ssl_send_chain() - * we may switch to a buffered write and may copy leftover part of - * previously unbuffered data to our internal buffer - */ - SSL_CTX_set_mode(ssl->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - SSL_CTX_set_read_ahead(ssl->ctx, 1); return NGX_OK; @@ -205,7 +189,7 @@ ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_str_t *key) { - if (ngx_conf_full_name(cf->cycle, cert, 1) == NGX_ERROR) { + if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) { return NGX_ERROR; } @@ -218,7 +202,7 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ return NGX_ERROR; } - if (ngx_conf_full_name(cf->cycle, key, 1) == NGX_ERROR) { + if (ngx_conf_full_name(cf->cycle, key, 1) != NGX_OK) { return NGX_ERROR; } @@ -249,7 +233,7 @@ ngx_ssl_client_certificate(ngx_conf_t *c return NGX_OK; } - if (ngx_conf_full_name(cf->cycle, cert, 1) == NGX_ERROR) { + if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) { return NGX_ERROR; } @@ -407,7 +391,7 @@ ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_ return NGX_OK; } - if (ngx_conf_full_name(cf->cycle, file, 1) == NGX_ERROR) { + if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) { return NGX_ERROR; } @@ -505,11 +489,11 @@ ngx_ssl_handshake(ngx_connection_t *c) if (n == 1) { - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_ERROR; } - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { return NGX_ERROR; } @@ -576,7 +560,7 @@ ngx_ssl_handshake(ngx_connection_t *c) c->read->handler = ngx_ssl_handshake_handler; c->write->handler = ngx_ssl_handshake_handler; - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_ERROR; } @@ -588,7 +572,7 @@ ngx_ssl_handshake(ngx_connection_t *c) c->read->handler = ngx_ssl_handshake_handler; c->write->handler = ngx_ssl_handshake_handler; - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { return NGX_ERROR; } @@ -774,7 +758,7 @@ ngx_ssl_handle_recv(ngx_connection_t *c, c->ssl->saved_write_handler = NULL; c->write->ready = 1; - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { return NGX_ERROR; } @@ -802,7 +786,7 @@ ngx_ssl_handle_recv(ngx_connection_t *c, c->write->ready = 0; - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { return NGX_ERROR; } @@ -860,14 +844,7 @@ ngx_ssl_send_chain(ngx_connection_t *c, ssize_t send, size; ngx_buf_t *buf; - if (!c->ssl->buffer - || (in && in->next == NULL && !(c->buffered & NGX_SSL_BUFFERED))) - { - /* - * we avoid a buffer copy if - * we do not need to buffer the output - * or the incoming buf is a single and our buffer is empty - */ + if (!c->ssl->buffer) { while (in) { if (ngx_buf_special(in->buf)) { @@ -1033,7 +1010,7 @@ ngx_ssl_write(ngx_connection_t *c, u_cha c->ssl->saved_read_handler = NULL; c->read->ready = 1; - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_ERROR; } @@ -1061,7 +1038,7 @@ ngx_ssl_write(ngx_connection_t *c, u_cha c->read->ready = 0; - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_ERROR; } @@ -1161,11 +1138,11 @@ ngx_ssl_shutdown(ngx_connection_t *c) c->read->handler = ngx_ssl_shutdown_handler; c->write->handler = ngx_ssl_shutdown_handler; - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_ERROR; } - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { return NGX_ERROR; } @@ -1254,23 +1231,37 @@ ngx_ssl_connection_error(ngx_connection_ n = ERR_GET_REASON(ERR_peek_error()); /* handshake failures */ - if (n == SSL_R_DIGEST_CHECK_FAILED - || n == SSL_R_NO_SHARED_CIPHER - || n == SSL_R_UNEXPECTED_MESSAGE - || n == SSL_R_WRONG_VERSION_NUMBER - || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC + if (n == SSL_R_DIGEST_CHECK_FAILED /* 149 */ + || n == SSL_R_NO_CIPHERS_PASSED /* 182 */ + || n == SSL_R_NO_SHARED_CIPHER /* 193 */ + || n == SSL_R_UNEXPECTED_MESSAGE /* 244 */ + || n == SSL_R_UNEXPECTED_RECORD /* 245 */ + || n == SSL_R_WRONG_VERSION_NUMBER /* 267 */ + || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC /* 281 */ || n == 1000 /* SSL_R_SSLV3_ALERT_CLOSE_NOTIFY */ - || n == SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE - || n == SSL_R_SSLV3_ALERT_BAD_RECORD_MAC - || n == SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE - || n == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE - || n == SSL_R_SSLV3_ALERT_BAD_CERTIFICATE - || n == SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE - || n == SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED - || n == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED - || n == SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN - || n == SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER - || n == SSL_R_TLSV1_ALERT_UNKNOWN_CA) + || n == SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE /* 1010 */ + || n == SSL_R_SSLV3_ALERT_BAD_RECORD_MAC /* 1020 */ + || n == SSL_R_TLSV1_ALERT_DECRYPTION_FAILED /* 1021 */ + || n == SSL_R_TLSV1_ALERT_RECORD_OVERFLOW /* 1022 */ + || n == SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE /* 1030 */ + || n == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE /* 1040 */ + || n == SSL_R_SSLV3_ALERT_NO_CERTIFICATE /* 1041 */ + || n == SSL_R_SSLV3_ALERT_BAD_CERTIFICATE /* 1042 */ + || n == SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE /* 1043 */ + || n == SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED /* 1044 */ + || n == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED /* 1045 */ + || n == SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN /* 1046 */ + || n == SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER /* 1047 */ + || n == SSL_R_TLSV1_ALERT_UNKNOWN_CA /* 1048 */ + || n == SSL_R_TLSV1_ALERT_ACCESS_DENIED /* 1049 */ + || n == SSL_R_TLSV1_ALERT_DECODE_ERROR /* 1050 */ + || n == SSL_R_TLSV1_ALERT_DECRYPT_ERROR /* 1051 */ + || n == SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION /* 1060 */ + || n == SSL_R_TLSV1_ALERT_PROTOCOL_VERSION /* 1070 */ + || n == SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY /* 1071 */ + || n == SSL_R_TLSV1_ALERT_INTERNAL_ERROR /* 1080 */ + || n == SSL_R_TLSV1_ALERT_USER_CANCELLED /* 1090 */ + || n == SSL_R_TLSV1_ALERT_NO_RENEGOTIATION) /* 1100 */ { switch (c->log_error) { @@ -1315,7 +1306,7 @@ ngx_ssl_error(ngx_uint_t level, ngx_log_ last = errstr + NGX_MAX_CONF_ERRSTR; va_start(args, fmt); - p = ngx_vsnprintf(errstr, sizeof(errstr) - 1, fmt, args); + p = ngx_vslprintf(errstr, last - 1, fmt, args); va_end(args); p = ngx_cpystrn(p, (u_char *) " (SSL:", last - p); @@ -1396,7 +1387,7 @@ ngx_ssl_session_cache(ngx_ssl_t *ssl, ng } } - SSL_CTX_set_timeout(ssl->ctx, timeout); + SSL_CTX_set_timeout(ssl->ctx, (long) timeout); if (shm_zone) { shm_zone->init = ngx_ssl_session_cache_init; @@ -1421,6 +1412,7 @@ ngx_ssl_session_cache(ngx_ssl_t *ssl, ng static ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data) { + size_t len; ngx_slab_pool_t *shpool; ngx_ssl_session_cache_t *cache; @@ -1429,6 +1421,11 @@ ngx_ssl_session_cache_init(ngx_shm_zone_ return NGX_OK; } + if (shm_zone->shm.exists) { + shm_zone->data = data; + return NGX_OK; + } + shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; cache = ngx_slab_alloc(shpool, sizeof(ngx_ssl_session_cache_t)); @@ -1436,12 +1433,23 @@ ngx_ssl_session_cache_init(ngx_shm_zone_ return NGX_ERROR; } + shpool->data = cache; + shm_zone->data = cache; + ngx_rbtree_init(&cache->session_rbtree, &cache->sentinel, ngx_ssl_session_rbtree_insert_value); ngx_queue_init(&cache->expire_queue); - shm_zone->data = cache; + len = sizeof(" in SSL session shared cache \"\"") + shm_zone->shm.name.len; + + shpool->log_ctx = ngx_slab_alloc(shpool, len); + if (shpool->log_ctx == NULL) { + return NGX_ERROR; + } + + ngx_sprintf(shpool->log_ctx, " in SSL session shared cache \"%V\"%Z", + &shm_zone->shm.name); return NGX_OK; } @@ -2107,14 +2115,13 @@ ngx_openssl_create_conf(ngx_cycle_t *cyc oscf = ngx_pcalloc(cycle->pool, sizeof(ngx_openssl_conf_t)); if (oscf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* * set by ngx_pcalloc(): * - * oscf->engine.len = 0; - * oscf->engine.data = NULL; + * oscf->engine = 0; */ return oscf; @@ -2122,53 +2129,54 @@ ngx_openssl_create_conf(ngx_cycle_t *cyc static char * -ngx_openssl_init_conf(ngx_cycle_t *cycle, void *conf) +ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { #if (NGX_SSL_ENGINE) ngx_openssl_conf_t *oscf = conf; - ENGINE *engine; - - if (oscf->engine.len == 0) { - return NGX_CONF_OK; + ENGINE *engine; + ngx_str_t *value; + + if (oscf->engine) { + return "is duplicate"; } - engine = ENGINE_by_id((const char *) oscf->engine.data); + oscf->engine = 1; + + value = cf->args->elts; + + engine = ENGINE_by_id((const char *) value[1].data); if (engine == NULL) { - ngx_ssl_error(NGX_LOG_WARN, cycle->log, 0, - "ENGINE_by_id(\"%V\") failed", &oscf->engine); + ngx_ssl_error(NGX_LOG_WARN, cf->log, 0, + "ENGINE_by_id(\"%V\") failed", &value[1]); return NGX_CONF_ERROR; } if (ENGINE_set_default(engine, ENGINE_METHOD_ALL) == 0) { - ngx_ssl_error(NGX_LOG_WARN, cycle->log, 0, + ngx_ssl_error(NGX_LOG_WARN, cf->log, 0, "ENGINE_set_default(\"%V\", ENGINE_METHOD_ALL) failed", - &oscf->engine); + &value[1]); + + ENGINE_free(engine); + return NGX_CONF_ERROR; } ENGINE_free(engine); -#endif - return NGX_CONF_OK; -} - - -#if !(NGX_SSL_ENGINE) - -static char * -ngx_openssl_noengine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ + +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"ssl_engine\" directive is available only in " "OpenSSL 0.9.7 and higher,"); return NGX_CONF_ERROR; -} #endif +} static void diff --git a/src/event/ngx_event_pipe.c b/src/event/ngx_event_pipe.c --- a/src/event/ngx_event_pipe.c +++ b/src/event/ngx_event_pipe.c @@ -56,7 +56,7 @@ ngx_event_pipe(ngx_event_pipe_t *p, ngx_ flags = (rev->eof || rev->error) ? NGX_CLOSE_EVENT : 0; - if (ngx_handle_read_event(rev, flags) == NGX_ERROR) { + if (ngx_handle_read_event(rev, flags) != NGX_OK) { return NGX_ABORT; } @@ -70,7 +70,7 @@ ngx_event_pipe(ngx_event_pipe_t *p, ngx_ if (p->downstream->fd != -1 && p->downstream->data == p->output_ctx) { wev = p->downstream->write; - if (ngx_handle_write_event(wev, p->send_lowat) == NGX_ERROR) { + if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) { return NGX_ABORT; } @@ -397,7 +397,7 @@ ngx_event_pipe_read_upstream(ngx_event_p p->free_raw_bufs = p->free_raw_bufs->next; - if (p->free_bufs) { + if (p->free_bufs && p->buf_to_file == NULL) { for (cl = p->free_raw_bufs; cl; cl = cl->next) { if (cl->buf->shadow == NULL) { ngx_pfree(p->pool, cl->buf->start); @@ -423,7 +423,7 @@ ngx_event_pipe_write_to_downstream(ngx_e size_t bsize; ngx_int_t rc; ngx_uint_t flush, prev_last_shadow; - ngx_chain_t *out, **ll, *cl; + ngx_chain_t *out, **ll, *cl, file; ngx_connection_t *downstream; downstream = p->downstream; @@ -488,6 +488,18 @@ ngx_event_pipe_write_to_downstream(ngx_e p->in = NULL; } + if (p->cacheable && p->buf_to_file) { + + file.buf = p->buf_to_file; + file.next = NULL; + + if (ngx_write_chain_to_temp_file(p->temp_file, &file) + == NGX_ERROR) + { + return NGX_ABORT; + } + } + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0, "pipe write downstream done"); diff --git a/src/http/modules/ngx_http_access_module.c b/src/http/modules/ngx_http_access_module.c --- a/src/http/modules/ngx_http_access_module.c +++ b/src/http/modules/ngx_http_access_module.c @@ -9,8 +9,6 @@ #include -/* AF_INET only */ - typedef struct { in_addr_t mask; in_addr_t addr; @@ -103,6 +101,10 @@ ngx_http_access_handler(ngx_http_request /* AF_INET only */ + if (r->connection->sockaddr->sa_family != AF_INET) { + return NGX_DECLINED; + } + sin = (struct sockaddr_in *) r->connection->sockaddr; rule = alcf->rules->elts; @@ -139,7 +141,7 @@ ngx_http_access_rule(ngx_conf_t *cf, ngx ngx_int_t rc; ngx_str_t *value; - ngx_inet_cidr_t in_cidr; + ngx_cidr_t cidr; ngx_http_access_rule_t *rule; if (alcf->rules == NULL) { @@ -166,7 +168,7 @@ ngx_http_access_rule(ngx_conf_t *cf, ngx return NGX_CONF_OK; } - rc = ngx_ptocidr(&value[1], &in_cidr); + rc = ngx_ptocidr(&value[1], &cidr); if (rc == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", @@ -174,13 +176,19 @@ ngx_http_access_rule(ngx_conf_t *cf, ngx return NGX_CONF_ERROR; } + if (cidr.family != AF_INET) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"allow\" supports IPv4 only"); + return NGX_CONF_ERROR; + } + if (rc == NGX_DONE) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "low address bits of %V are meaningless", &value[1]); } - rule->mask = in_cidr.mask; - rule->addr = in_cidr.addr; + rule->mask = cidr.u.in.mask; + rule->addr = cidr.u.in.addr; return NGX_CONF_OK; } @@ -193,7 +201,7 @@ ngx_http_access_create_loc_conf(ngx_conf conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_access_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } return conf; diff --git a/src/http/modules/ngx_http_addition_filter_module.c b/src/http/modules/ngx_http_addition_filter_module.c --- a/src/http/modules/ngx_http_addition_filter_module.c +++ b/src/http/modules/ngx_http_addition_filter_module.c @@ -151,10 +151,10 @@ ngx_http_addition_body_filter(ngx_http_r ctx->before_body_sent = 1; if (conf->before_body.len) { - rc = ngx_http_subrequest(r, &conf->before_body, NULL, &sr, NULL, 0); - - if (rc == NGX_ERROR || rc == NGX_DONE) { - return rc; + if (ngx_http_subrequest(r, &conf->before_body, NULL, &sr, NULL, 0) + != NGX_OK) + { + return NGX_ERROR; } } } @@ -180,10 +180,10 @@ ngx_http_addition_body_filter(ngx_http_r return rc; } - rc = ngx_http_subrequest(r, &conf->after_body, NULL, &sr, NULL, 0); - - if (rc == NGX_ERROR || rc == NGX_DONE) { - return rc; + if (ngx_http_subrequest(r, &conf->after_body, NULL, &sr, NULL, 0) + != NGX_OK) + { + return NGX_ERROR; } ngx_http_set_ctx(r, NULL, ngx_http_addition_filter_module); @@ -212,7 +212,7 @@ ngx_http_addition_create_conf(ngx_conf_t conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_addition_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* diff --git a/src/http/modules/ngx_http_auth_basic_module.c b/src/http/modules/ngx_http_auth_basic_module.c --- a/src/http/modules/ngx_http_auth_basic_module.c +++ b/src/http/modules/ngx_http_auth_basic_module.c @@ -13,13 +13,13 @@ typedef struct { - ngx_str_t passwd; + ngx_str_t passwd; } ngx_http_auth_basic_ctx_t; typedef struct { - ngx_str_t realm; - ngx_str_t user_file; + ngx_str_t realm; + ngx_http_complex_value_t user_file; } ngx_http_auth_basic_loc_conf_t; @@ -34,6 +34,8 @@ static char *ngx_http_auth_basic_merge_l void *parent, void *child); static ngx_int_t ngx_http_auth_basic_init(ngx_conf_t *cf); static char *ngx_http_auth_basic(ngx_conf_t *cf, void *post, void *data); +static char *ngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static ngx_conf_post_handler_pt ngx_http_auth_basic_p = ngx_http_auth_basic; @@ -51,7 +53,7 @@ static ngx_command_t ngx_http_auth_basi { ngx_string("auth_basic_user_file"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF |NGX_CONF_TAKE1, - ngx_conf_set_str_slot, + ngx_http_auth_basic_user_file, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_auth_basic_loc_conf_t, user_file), NULL }, @@ -98,8 +100,9 @@ ngx_http_auth_basic_handler(ngx_http_req ssize_t n; ngx_fd_t fd; ngx_int_t rc; - ngx_str_t pwd; - ngx_uint_t i, login, left, passwd; + ngx_err_t err; + ngx_str_t pwd, user_file; + ngx_uint_t i, level, login, left, passwd; ngx_file_t file; ngx_http_auth_basic_ctx_t *ctx; ngx_http_auth_basic_loc_conf_t *alcf; @@ -112,7 +115,7 @@ ngx_http_auth_basic_handler(ngx_http_req alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_basic_module); - if (alcf->realm.len == 0 || alcf->user_file.len == 0) { + if (alcf->realm.len == 0 || alcf->user_file.value.len == 0) { return NGX_DECLINED; } @@ -126,6 +129,10 @@ ngx_http_auth_basic_handler(ngx_http_req rc = ngx_http_auth_basic_user(r); if (rc == NGX_DECLINED) { + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "no user/password was provided for basic authentication"); + return ngx_http_auth_basic_set_realm(r, &alcf->realm); } @@ -133,18 +140,34 @@ ngx_http_auth_basic_handler(ngx_http_req return NGX_HTTP_INTERNAL_SERVER_ERROR; } - fd = ngx_open_file(alcf->user_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); + if (ngx_http_complex_value(r, &alcf->user_file, &user_file) != NGX_OK) { + return NGX_ERROR; + } + + fd = ngx_open_file(user_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); if (fd == NGX_INVALID_FILE) { - ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, - ngx_open_file_n " \"%s\" failed", alcf->user_file.data); - return NGX_HTTP_INTERNAL_SERVER_ERROR; + err = ngx_errno; + + if (err == NGX_ENOENT) { + level = NGX_LOG_ERR; + rc = NGX_HTTP_FORBIDDEN; + + } else { + level = NGX_LOG_CRIT; + rc = NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_log_error(level, r->connection->log, err, + ngx_open_file_n " \"%s\" failed", user_file.data); + + return rc; } ngx_memzero(&file, sizeof(ngx_file_t)); file.fd = fd; - file.name = alcf->user_file; + file.name = user_file; file.log = r->connection->log; state = sw_login; @@ -172,9 +195,16 @@ ngx_http_auth_basic_handler(ngx_http_req switch (state) { case sw_login: - if (login == 0 && buf[i] == '#') { - state = sw_skip; - break; + if (login == 0) { + + if (buf[i] == '#' || buf[i] == CR) { + state = sw_skip; + break; + } + + if (buf[i] == LF) { + break; + } } if (buf[i] != r->headers_in.user.data[login]) { @@ -242,6 +272,10 @@ ngx_http_auth_basic_handler(ngx_http_req return ngx_http_auth_basic_crypt_handler(r, NULL, &pwd, &alcf->realm); } + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "user \"%V\" was not found in \"%V\"", + &r->headers_in.user, &user_file); + return ngx_http_auth_basic_set_realm(r, &alcf->realm); } @@ -257,8 +291,8 @@ ngx_http_auth_basic_crypt_handler(ngx_ht &encrypted); ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "rc: %d user: \"%V\" salt: \"%s\"", - rc, &r->headers_in.user, passwd->data); + "rc: %d user: \"%V\" salt: \"%s\"", + rc, &r->headers_in.user, passwd->data); if (rc == NGX_OK) { if (ngx_strcmp(encrypted, passwd->data) == 0) { @@ -268,6 +302,10 @@ ngx_http_auth_basic_crypt_handler(ngx_ht ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "encrypted: \"%s\"", encrypted); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "user \"%V\": password mismatch", + &r->headers_in.user); + return ngx_http_auth_basic_set_realm(r, realm); } @@ -334,7 +372,7 @@ ngx_http_auth_basic_create_loc_conf(ngx_ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_basic_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } return conf; @@ -351,12 +389,7 @@ ngx_http_auth_basic_merge_loc_conf(ngx_c conf->realm = prev->realm; } - if (conf->user_file.data) { - if (ngx_conf_full_name(cf->cycle, &conf->user_file, 1) != NGX_OK) { - return NGX_CONF_ERROR; - } - - } else { + if (conf->user_file.value.len == 0) { conf->user_file = prev->user_file; } @@ -414,3 +447,33 @@ ngx_http_auth_basic(ngx_conf_t *cf, void return NGX_CONF_OK; } + + +static char * +ngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_auth_basic_loc_conf_t *alcf = conf; + + ngx_str_t *value; + ngx_http_compile_complex_value_t ccv; + + if (alcf->user_file.value.len) { + return "is duplicate"; + } + + value = cf->args->elts; + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &alcf->user_file; + ccv.zero = 1; + ccv.conf_prefix = 1; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} diff --git a/src/http/modules/ngx_http_autoindex_module.c b/src/http/modules/ngx_http_autoindex_module.c --- a/src/http/modules/ngx_http_autoindex_module.c +++ b/src/http/modules/ngx_http_autoindex_module.c @@ -25,8 +25,11 @@ typedef struct { typedef struct { ngx_str_t name; size_t utf_len; - ngx_uint_t escape; - ngx_uint_t dir; + size_t escape; + + unsigned dir:1; + unsigned colon:1; + time_t mtime; off_t size; } ngx_http_autoindex_entry_t; @@ -142,7 +145,7 @@ ngx_http_autoindex_handler(ngx_http_requ ngx_int_t rc, size; ngx_str_t path; ngx_dir_t dir; - ngx_uint_t i, level; + ngx_uint_t i, level, utf8; ngx_pool_t *pool; ngx_time_t *tp; ngx_chain_t out; @@ -157,7 +160,6 @@ ngx_http_autoindex_handler(ngx_http_requ return NGX_DECLINED; } - /* TODO: Win32 */ if (r->zero_in_uri) { return NGX_DECLINED; } @@ -250,6 +252,16 @@ ngx_http_autoindex_handler(ngx_http_requ filename = path.data; filename[path.len] = '/'; + if (r->headers_out.charset.len == 5 + && ngx_strncasecmp(r->headers_out.charset.data, (u_char *) "utf-8", 5) + == 0) + { + utf8 = 1; + + } else { + utf8 = 0; + } + for ( ;; ) { ngx_set_errno(0); @@ -299,6 +311,11 @@ ngx_http_autoindex_handler(ngx_http_requ if (err != NGX_ENOENT) { ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, ngx_de_info_n " \"%s\" failed", filename); + + if (err == NGX_EACCES) { + continue; + } + return ngx_http_autoindex_error(r, &dir, &path); } @@ -328,12 +345,14 @@ ngx_http_autoindex_handler(ngx_http_requ entry->escape = 2 * ngx_escape_uri(NULL, ngx_de_name(&dir), len, NGX_ESCAPE_HTML); - if (r->utf8) { + if (utf8) { entry->utf_len = ngx_utf8_length(entry->name.data, entry->name.len); } else { entry->utf_len = len; } + entry->colon = (ngx_strchr(entry->name.data, ':') != NULL); + entry->dir = ngx_de_is_dir(&dir); entry->mtime = ngx_de_mtime(&dir); entry->size = ngx_de_size(&dir); @@ -359,7 +378,7 @@ ngx_http_autoindex_handler(ngx_http_requ + entry[i].name.len + entry[i].escape + 1 /* 1 is for "/" */ + sizeof("\">") - 1 - + entry[i].name.len - entry[i].utf_len + + entry[i].name.len - entry[i].utf_len + entry[i].colon * 2 + NGX_HTTP_AUTOINDEX_NAME_LEN + sizeof(">") - 2 + sizeof("") - 1 + sizeof(" 28-Sep-1970 12:00 ") - 1 @@ -392,6 +411,11 @@ ngx_http_autoindex_handler(ngx_http_requ for (i = 0; i < entries.nelts; i++) { b->last = ngx_cpymem(b->last, "last++ = '.'; + *b->last++ = '/'; + } + if (entry[i].escape) { ngx_escape_uri(b->last, entry[i].name.data, entry[i].name.len, NGX_ESCAPE_HTML); @@ -608,7 +632,7 @@ ngx_http_autoindex_create_loc_conf(ngx_c conf = ngx_palloc(cf->pool, sizeof(ngx_http_autoindex_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } conf->enable = NGX_CONF_UNSET; diff --git a/src/http/modules/ngx_http_browser_module.c b/src/http/modules/ngx_http_browser_module.c --- a/src/http/modules/ngx_http_browser_module.c +++ b/src/http/modules/ngx_http_browser_module.c @@ -318,6 +318,10 @@ ngx_http_browser(ngx_http_request_t *r, if (c == '.') { version += ver * scale; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "version: \"%ui\" \"%ui\"", + modern[i].version, version); + if (version > modern[i].version) { return NGX_HTTP_MODERN_BROWSER; } @@ -339,6 +343,8 @@ ngx_http_browser(ngx_http_request_t *r, if (version >= modern[i].version) { return NGX_HTTP_MODERN_BROWSER; } + + return NGX_HTTP_ANCIENT_BROWSER; } if (!cf->modern_unlisted_browsers) { @@ -417,7 +423,7 @@ ngx_http_browser_create_conf(ngx_conf_t conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_browser_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* diff --git a/src/http/modules/ngx_http_charset_filter_module.c b/src/http/modules/ngx_http_charset_filter_module.c --- a/src/http/modules/ngx_http_charset_filter_module.c +++ b/src/http/modules/ngx_http_charset_filter_module.c @@ -9,8 +9,9 @@ #include -#define NGX_HTTP_NO_CHARSET -2 -#define NGX_HTTP_CHARSET_VAR 0x10000 +#define NGX_HTTP_CHARSET_OFF -2 +#define NGX_HTTP_NO_CHARSET -3 +#define NGX_HTTP_CHARSET_VAR 0x10000 /* 1 byte length and up to 3 bytes for the UTF-8 encoding of the UCS-2 */ #define NGX_UTF_LEN 4 @@ -61,6 +62,7 @@ typedef struct { typedef struct { u_char *table; ngx_int_t charset; + ngx_str_t charset_name; ngx_chain_t *busy; ngx_chain_t *free_bufs; @@ -82,9 +84,16 @@ typedef struct { } ngx_http_charset_conf_ctx_t; -static ngx_int_t ngx_http_charset_get_charset(ngx_http_charset_t *charsets, - ngx_uint_t n, ngx_str_t *charset); -static ngx_int_t ngx_http_charset_set_charset(ngx_http_request_t *r, +static ngx_int_t ngx_http_destination_charset(ngx_http_request_t *r, + ngx_str_t *name); +static ngx_int_t ngx_http_main_request_charset(ngx_http_request_t *r, + ngx_str_t *name); +static ngx_int_t ngx_http_source_charset(ngx_http_request_t *r, + ngx_str_t *name); +static ngx_int_t ngx_http_get_charset(ngx_http_request_t *r, ngx_str_t *name); +static ngx_inline void ngx_http_set_charset(ngx_http_request_t *r, + ngx_str_t *charset); +static ngx_int_t ngx_http_charset_ctx(ngx_http_request_t *r, ngx_http_charset_t *charsets, ngx_int_t charset, ngx_int_t source_charset); static ngx_uint_t ngx_http_charset_recode(ngx_buf_t *b, u_char *table); static ngx_chain_t *ngx_http_charset_recode_from_utf8(ngx_pool_t *pool, @@ -207,206 +216,264 @@ static ngx_int_t ngx_http_charset_header_filter(ngx_http_request_t *r) { ngx_int_t charset, source_charset; - ngx_str_t *mc, *from, *to, s; - ngx_uint_t n; + ngx_str_t dst, src; ngx_http_charset_t *charsets; - ngx_http_charset_ctx_t *ctx; - ngx_http_variable_value_t *vv; - ngx_http_charset_loc_conf_t *lcf, *mlcf; ngx_http_charset_main_conf_t *mcf; - mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module); - - charsets = mcf->charsets.elts; - n = mcf->charsets.nelts; - - /* destination charset */ - if (r == r->main) { - - if (r->headers_out.content_encoding - && r->headers_out.content_encoding->value.len) - { - return ngx_http_next_header_filter(r); - } - - if (r->headers_out.content_type.len == 0) { - return ngx_http_next_header_filter(r); - } - - if (r->headers_out.override_charset - && r->headers_out.override_charset->len) - { - charset = ngx_http_charset_get_charset(charsets, n, - r->headers_out.override_charset); - - if (charset == NGX_HTTP_NO_CHARSET) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "unknown charset \"%V\" to override", - r->headers_out.override_charset); - - return ngx_http_next_header_filter(r); - } - - } else { - mlcf = ngx_http_get_module_loc_conf(r, - ngx_http_charset_filter_module); - charset = mlcf->charset; - - if (charset == NGX_HTTP_NO_CHARSET) { - return ngx_http_next_header_filter(r); - } - - if (r->headers_out.charset.len) { - if (mlcf->override_charset == 0) { - return ngx_http_next_header_filter(r); - } - - } else { - if (ngx_http_test_content_type(r, &mlcf->types) == NULL) { - return ngx_http_next_header_filter(r); - } - } - - if (charset >= NGX_HTTP_CHARSET_VAR) { - vv = ngx_http_get_indexed_variable(r, - charset - NGX_HTTP_CHARSET_VAR); - - if (vv == NULL || vv->not_found) { - return NGX_ERROR; - } - - s.len = vv->len; - s.data = vv->data; - - charset = ngx_http_charset_get_charset(charsets, n, &s); - } - } + charset = ngx_http_destination_charset(r, &dst); } else { - ctx = ngx_http_get_module_ctx(r->main, ngx_http_charset_filter_module); - - if (ctx == NULL) { - - mc = &r->main->headers_out.charset; - - if (mc->len == 0) { - return ngx_http_next_header_filter(r); - } + charset = ngx_http_main_request_charset(r, &dst); + } - ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t)); - if (ctx == NULL) { - return NGX_ERROR; - } - - ngx_http_set_ctx(r->main, ctx, ngx_http_charset_filter_module); + if (charset == NGX_ERROR) { + return NGX_ERROR; + } - charset = ngx_http_charset_get_charset(charsets, n, mc); - - ctx->charset = charset; - - } else { - charset = ctx->charset; - } + if (charset == NGX_DECLINED) { + return ngx_http_next_header_filter(r); } - /* source charset */ - - if (r->headers_out.charset.len == 0) { - lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module); - - source_charset = lcf->source_charset; - - if (source_charset >= NGX_HTTP_CHARSET_VAR) { - vv = ngx_http_get_indexed_variable(r, - source_charset - NGX_HTTP_CHARSET_VAR); - - if (vv == NULL || vv->not_found) { - return NGX_ERROR; - } + /* charset: charset index or NGX_HTTP_NO_CHARSET */ - s.len = vv->len; - s.data = vv->data; - - source_charset = ngx_http_charset_get_charset(charsets, n, &s); - } + source_charset = ngx_http_source_charset(r, &src); - if (charset != NGX_HTTP_NO_CHARSET) { - return ngx_http_charset_set_charset(r, mcf->charsets.elts, charset, - source_charset); - } - - if (source_charset == NGX_CONF_UNSET) { - return ngx_http_next_header_filter(r); - } - - from = &charsets[source_charset].name; - to = &r->main->headers_out.charset; - - goto no_charset_map; + if (source_charset == NGX_ERROR) { + return NGX_ERROR; } - source_charset = ngx_http_charset_get_charset(charsets, n, - &r->headers_out.charset); + /* + * source_charset: charset index, NGX_HTTP_NO_CHARSET, + * or NGX_HTTP_CHARSET_OFF + */ + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "charset: \"%V\" > \"%V\"", &src, &dst); + + if (source_charset == NGX_HTTP_CHARSET_OFF) { + ngx_http_set_charset(r, &dst); + + return ngx_http_next_header_filter(r); + } if (charset == NGX_HTTP_NO_CHARSET || source_charset == NGX_HTTP_NO_CHARSET) { - if (charset != source_charset - || ngx_strcasecmp(r->main->headers_out.charset.data, - r->headers_out.charset.data) - != 0) + if (source_charset != charset + || ngx_strncasecmp(dst.data, src.data, dst.len) != 0) { - from = &r->headers_out.charset; - to = (charset == NGX_HTTP_NO_CHARSET) ? - &r->main->headers_out.charset: - &charsets[charset].name; - goto no_charset_map; } + ngx_http_set_charset(r, &dst); + return ngx_http_next_header_filter(r); } + mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module); + charsets = mcf->charsets.elts; + if (source_charset != charset && (charsets[source_charset].tables == NULL || charsets[source_charset].tables[charset] == NULL)) { - from = &charsets[source_charset].name; - to = &charsets[charset].name; - goto no_charset_map; } r->headers_out.content_type.len = r->headers_out.content_type_len; - return ngx_http_charset_set_charset(r, mcf->charsets.elts, charset, - source_charset); + ngx_http_set_charset(r, &dst); + + if (source_charset != charset) { + return ngx_http_charset_ctx(r, charsets, charset, source_charset); + } + + return ngx_http_next_header_filter(r); no_charset_map: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no \"charset_map\" between the charsets \"%V\" and \"%V\"", - from, to); + &src, &dst); return ngx_http_next_header_filter(r); } static ngx_int_t -ngx_http_charset_get_charset(ngx_http_charset_t *charsets, ngx_uint_t n, - ngx_str_t *charset) +ngx_http_destination_charset(ngx_http_request_t *r, ngx_str_t *name) +{ + ngx_int_t charset; + ngx_http_charset_t *charsets; + ngx_http_variable_value_t *vv; + ngx_http_charset_loc_conf_t *mlcf; + ngx_http_charset_main_conf_t *mcf; + + if (!r->ignore_content_encoding + && r->headers_out.content_encoding + && r->headers_out.content_encoding->value.len) + { + return NGX_DECLINED; + } + + if (r->headers_out.content_type.len == 0) { + return NGX_DECLINED; + } + + if (r->headers_out.override_charset + && r->headers_out.override_charset->len) + { + *name = *r->headers_out.override_charset; + + charset = ngx_http_get_charset(r, name); + + if (charset != NGX_HTTP_NO_CHARSET) { + return charset; + } + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "unknown charset \"%V\" to override", name); + + return NGX_DECLINED; + } + + mlcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module); + charset = mlcf->charset; + + if (charset == NGX_HTTP_CHARSET_OFF) { + return NGX_DECLINED; + } + + if (r->headers_out.charset.len) { + if (mlcf->override_charset == 0) { + return NGX_DECLINED; + } + + } else { + if (ngx_http_test_content_type(r, &mlcf->types) == NULL) { + return NGX_DECLINED; + } + } + + if (charset < NGX_HTTP_CHARSET_VAR) { + mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module); + charsets = mcf->charsets.elts; + *name = charsets[charset].name; + return charset; + } + + vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR); + + if (vv == NULL || vv->not_found) { + return NGX_ERROR; + } + + name->len = vv->len; + name->data = vv->data; + + return ngx_http_get_charset(r, name); +} + + +static ngx_int_t +ngx_http_main_request_charset(ngx_http_request_t *r, ngx_str_t *src) { - ngx_uint_t i; + ngx_int_t charset; + ngx_str_t *main_charset; + ngx_http_charset_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r->main, ngx_http_charset_filter_module); + + if (ctx) { + *src = ctx->charset_name; + return ctx->charset; + } + + main_charset = &r->main->headers_out.charset; + + if (main_charset->len == 0) { + return NGX_DECLINED; + } + + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_http_set_ctx(r->main, ctx, ngx_http_charset_filter_module); + + charset = ngx_http_get_charset(r, main_charset); + + ctx->charset = charset; + ctx->charset_name = *main_charset; + *src = *main_charset; + + return charset; +} + + +static ngx_int_t +ngx_http_source_charset(ngx_http_request_t *r, ngx_str_t *name) +{ + ngx_int_t charset; + ngx_http_charset_t *charsets; + ngx_http_variable_value_t *vv; + ngx_http_charset_loc_conf_t *lcf; + ngx_http_charset_main_conf_t *mcf; + + if (r->headers_out.charset.len) { + *name = r->headers_out.charset; + return ngx_http_get_charset(r, name); + } + + lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module); + + charset = lcf->source_charset; + + if (charset == NGX_HTTP_CHARSET_OFF) { + name->len = 0; + return charset; + } + + if (charset < NGX_HTTP_CHARSET_VAR) { + mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module); + charsets = mcf->charsets.elts; + *name = charsets[charset].name; + return charset; + } + + vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR); + + if (vv == NULL || vv->not_found) { + return NGX_ERROR; + } + + name->len = vv->len; + name->data = vv->data; + + return ngx_http_get_charset(r, name); +} + + +static ngx_int_t +ngx_http_get_charset(ngx_http_request_t *r, ngx_str_t *name) +{ + ngx_uint_t i, n; + ngx_http_charset_t *charset; + ngx_http_charset_main_conf_t *mcf; + + mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module); + + charset = mcf->charsets.elts; + n = mcf->charsets.nelts; for (i = 0; i < n; i++) { - if (charsets[i].name.len != charset->len) { + if (charset[i].name.len != name->len) { continue; } - if (ngx_strncasecmp(charsets[i].name.data, charset->data, charset->len) - == 0) - { + if (ngx_strncasecmp(charset[i].name.data, name->data, name->len) == 0) { return i; } } @@ -415,11 +482,12 @@ ngx_http_charset_get_charset(ngx_http_ch } -static ngx_int_t -ngx_http_charset_set_charset(ngx_http_request_t *r, - ngx_http_charset_t *charsets, ngx_int_t charset, ngx_int_t source_charset) +static ngx_inline void +ngx_http_set_charset(ngx_http_request_t *r, ngx_str_t *charset) { - ngx_http_charset_ctx_t *ctx; + if (r != r->main) { + return; + } if (r->headers_out.status == NGX_HTTP_MOVED_PERMANENTLY || r->headers_out.status == NGX_HTTP_MOVED_TEMPORARILY) @@ -430,16 +498,18 @@ ngx_http_charset_set_charset(ngx_http_re */ r->headers_out.charset.len = 0; - - return ngx_http_next_header_filter(r); + return; } - r->headers_out.charset = charsets[charset].name; - r->utf8 = charsets[charset].utf8; + r->headers_out.charset = *charset; +} + - if (source_charset == NGX_CONF_UNSET || source_charset == charset) { - return ngx_http_next_header_filter(r); - } +static ngx_int_t +ngx_http_charset_ctx(ngx_http_request_t *r, ngx_http_charset_t *charsets, + ngx_int_t charset, ngx_int_t source_charset) +{ + ngx_http_charset_ctx_t *ctx; ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t)); if (ctx == NULL) { @@ -450,6 +520,7 @@ ngx_http_charset_set_charset(ngx_http_re ctx->table = charsets[source_charset].tables[charset]; ctx->charset = charset; + ctx->charset_name = charsets[charset].name; ctx->length = charsets[charset].length; ctx->from_utf8 = charsets[source_charset].utf8; ctx->to_utf8 = charsets[charset].utf8; @@ -541,12 +612,6 @@ ngx_http_charset_body_filter(ngx_http_re break; } -#if (NGX_HAVE_WRITE_ZEROCOPY) - if (b->zerocopy_busy) { - break; - } -#endif - ctx->busy = cl->next; if (b->tag != (ngx_buf_tag_t) &ngx_http_charset_filter_module) { @@ -1343,7 +1408,7 @@ ngx_http_set_charset_slot(ngx_conf_t *cf if (cmd->offset == offsetof(ngx_http_charset_loc_conf_t, charset) && ngx_strcmp(value[1].data, "off") == 0) { - *cp = NGX_HTTP_NO_CHARSET; + *cp = NGX_HTTP_CHARSET_OFF; return NGX_CONF_OK; } @@ -1423,25 +1488,27 @@ ngx_http_charset_create_main_conf(ngx_co mcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_main_conf_t)); if (mcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } if (ngx_array_init(&mcf->charsets, cf->pool, 2, sizeof(ngx_http_charset_t)) - == NGX_ERROR) + != NGX_OK) { - return NGX_CONF_ERROR; + return NULL; } if (ngx_array_init(&mcf->tables, cf->pool, 1, - sizeof(ngx_http_charset_tables_t)) == NGX_ERROR) + sizeof(ngx_http_charset_tables_t)) + != NGX_OK) { - return NGX_CONF_ERROR; + return NULL; } if (ngx_array_init(&mcf->recodes, cf->pool, 2, - sizeof(ngx_http_charset_recode_t)) == NGX_ERROR) + sizeof(ngx_http_charset_recode_t)) + != NGX_OK) { - return NGX_CONF_ERROR; + return NULL; } return mcf; @@ -1455,7 +1522,7 @@ ngx_http_charset_create_loc_conf(ngx_con lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_loc_conf_t)); if (lcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* @@ -1492,14 +1559,12 @@ ngx_http_charset_merge_loc_conf(ngx_conf } ngx_conf_merge_value(conf->override_charset, prev->override_charset, 0); - ngx_conf_merge_value(conf->charset, prev->charset, NGX_HTTP_NO_CHARSET); + ngx_conf_merge_value(conf->charset, prev->charset, NGX_HTTP_CHARSET_OFF); + ngx_conf_merge_value(conf->source_charset, prev->source_charset, + NGX_HTTP_CHARSET_OFF); - if (conf->source_charset == NGX_CONF_UNSET) { - conf->source_charset = prev->source_charset; - } - - if (conf->charset == NGX_HTTP_NO_CHARSET - || conf->source_charset == NGX_CONF_UNSET + if (conf->charset == NGX_HTTP_CHARSET_OFF + || conf->source_charset == NGX_HTTP_CHARSET_OFF || conf->charset == conf->source_charset) { return NGX_CONF_OK; diff --git a/src/http/modules/ngx_http_dav_module.c b/src/http/modules/ngx_http_dav_module.c --- a/src/http/modules/ngx_http_dav_module.c +++ b/src/http/modules/ngx_http_dav_module.c @@ -148,7 +148,6 @@ ngx_http_dav_handler(ngx_http_request_t ngx_int_t rc; ngx_http_dav_loc_conf_t *dlcf; - /* TODO: Win32 */ if (r->zero_in_uri) { return NGX_DECLINED; } @@ -222,7 +221,7 @@ ngx_http_dav_put_handler(ngx_http_reques temp = &r->request_body->temp_file->file.name; - if (ngx_file_info(path.data, &fi) == -1) { + if (ngx_file_info(path.data, &fi) == NGX_FILE_ERROR) { status = NGX_HTTP_CREATED; } else { @@ -246,9 +245,11 @@ ngx_http_dav_put_handler(ngx_http_reques dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module); ext.access = dlcf->access; + ext.path_access = dlcf->access; ext.time = -1; ext.create_path = dlcf->create_full_put_path; ext.delete_file = 1; + ext.log_rename_error = 1; ext.log = r->connection->log; if (r->headers_in.date) { @@ -325,7 +326,7 @@ ok: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http delete filename: \"%s\"", path.data); - if (ngx_file_info(path.data, &fi) == -1) { + if (ngx_file_info(path.data, &fi) == NGX_FILE_ERROR) { err = ngx_errno; rc = (err == NGX_ENOTDIR) ? NGX_HTTP_CONFLICT : NGX_HTTP_NOT_FOUND; @@ -521,6 +522,7 @@ ngx_http_dav_copy_move_handler(ngx_http_ ngx_tree_ctx_t tree; ngx_file_info_t fi; ngx_table_elt_t *dest, *over; + ngx_ext_rename_file_t ext; ngx_http_dav_copy_ctx_t copy; ngx_http_dav_loc_conf_t *dlcf; @@ -676,7 +678,7 @@ overwrite_done: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http copy to: \"%s\"", copy.path.data); - if (ngx_file_info(copy.path.data, &fi) == -1) { + if (ngx_file_info(copy.path.data, &fi) == NGX_FILE_ERROR) { err = ngx_errno; if (err != NGX_ENOENT) { @@ -710,7 +712,7 @@ overwrite_done: dir = ngx_is_dir(&fi); } - if (ngx_file_info(path.data, &fi) == -1) { + if (ngx_file_info(path.data, &fi) == NGX_FILE_ERROR) { return ngx_http_dav_error(r->connection->log, ngx_errno, NGX_HTTP_NOT_FOUND, ngx_file_info_n, path.data); @@ -781,9 +783,32 @@ overwrite_done: } else { if (r->method == NGX_HTTP_MOVE) { - if (ngx_rename_file(path.data, copy.path.data) != NGX_FILE_ERROR) { + + dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module); + + ext.access = 0; + ext.path_access = dlcf->access; + ext.time = -1; + ext.create_path = 1; + ext.delete_file = 0; + ext.log_rename_error = 0; + ext.log = r->connection->log; + + if (ngx_ext_rename_file(&path, ©.path, &ext) == NGX_OK) { return NGX_HTTP_NO_CONTENT; } + + if (ext.rename_error != NGX_EXDEV) { + + if (ext.rename_error) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, + ext.rename_error, + ngx_rename_file_n " \"%s\" to \"%s\" failed", + path.data, copy.path.data); + } + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } } dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module); @@ -1129,7 +1154,7 @@ ngx_http_dav_create_loc_conf(ngx_conf_t conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dav_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* diff --git a/src/http/modules/ngx_http_empty_gif_module.c b/src/http/modules/ngx_http_empty_gif_module.c --- a/src/http/modules/ngx_http_empty_gif_module.c +++ b/src/http/modules/ngx_http_empty_gif_module.c @@ -122,6 +122,7 @@ ngx_http_empty_gif_handler(ngx_http_requ return rc; } + r->headers_out.content_type_len = sizeof("image/gif") - 1; r->headers_out.content_type.len = sizeof("image/gif") - 1; r->headers_out.content_type.data = (u_char *) "image/gif"; diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c --- a/src/http/modules/ngx_http_fastcgi_module.c +++ b/src/http/modules/ngx_http_fastcgi_module.c @@ -19,6 +19,18 @@ typedef struct { ngx_array_t *params; ngx_array_t *params_source; ngx_array_t *catch_stderr; + + ngx_array_t *fastcgi_lengths; + ngx_array_t *fastcgi_values; + +#if (NGX_HTTP_CACHE) + ngx_http_complex_value_t cache_key; +#endif + +#if (NGX_PCRE) + ngx_regex_t *split_regex; + ngx_str_t split_name; +#endif } ngx_http_fastcgi_loc_conf_t; @@ -53,6 +65,9 @@ typedef struct { ngx_uint_t fastcgi_stdout; /* unsigned :1 */ ngx_array_t *split_parts; + + ngx_str_t script_name; + ngx_str_t path_info; } ngx_http_fastcgi_ctx_t; @@ -103,6 +118,11 @@ typedef struct { } ngx_http_fastcgi_request_start_t; +static ngx_int_t ngx_http_fastcgi_eval(ngx_http_request_t *r, + ngx_http_fastcgi_loc_conf_t *flcf); +#if (NGX_HTTP_CACHE) +static ngx_int_t ngx_http_fastcgi_create_key(ngx_http_request_t *r); +#endif static ngx_int_t ngx_http_fastcgi_create_request(ngx_http_request_t *r); static ngx_int_t ngx_http_fastcgi_reinit_request(ngx_http_request_t *r); static ngx_int_t ngx_http_fastcgi_process_header(ngx_http_request_t *r); @@ -120,11 +140,24 @@ static char *ngx_http_fastcgi_merge_loc_ void *parent, void *child); static ngx_int_t ngx_http_fastcgi_script_name_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_fastcgi_path_info_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_http_fastcgi_ctx_t *ngx_http_fastcgi_split(ngx_http_request_t *r, + ngx_http_fastcgi_loc_conf_t *flcf); static char *ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_fastcgi_split_path_info(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); static char *ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +#if (NGX_HTTP_CACHE) +static char *ngx_http_fastcgi_cache(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_http_fastcgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +#endif + static char *ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post, void *data); @@ -145,11 +178,24 @@ static ngx_conf_bitmask_t ngx_http_fast { ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 }, { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 }, { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 }, + { ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING }, { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF }, { ngx_null_string, 0 } }; +static ngx_conf_bitmask_t ngx_http_fastcgi_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("Expires"), NGX_HTTP_UPSTREAM_IGN_EXPIRES }, + { ngx_string("Cache-Control"), NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL }, + { ngx_null_string, 0 } +}; + + +ngx_module_t ngx_http_fastcgi_module; + + static ngx_command_t ngx_http_fastcgi_commands[] = { { ngx_string("fastcgi_pass"), @@ -166,6 +212,13 @@ static ngx_command_t ngx_http_fastcgi_c offsetof(ngx_http_fastcgi_loc_conf_t, index), NULL }, + { ngx_string("fastcgi_split_path_info"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_fastcgi_split_path_info, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + { ngx_string("fastcgi_store"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_http_fastcgi_store, @@ -257,12 +310,65 @@ static ngx_command_t ngx_http_fastcgi_c offsetof(ngx_http_fastcgi_loc_conf_t, upstream.busy_buffers_size_conf), NULL }, +#if (NGX_HTTP_CACHE) + + { ngx_string("fastcgi_cache"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_http_fastcgi_cache, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("fastcgi_cache_key"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_http_fastcgi_cache_key, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("fastcgi_cache_path"), + NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE, + ngx_http_file_cache_set_slot, + 0, + 0, + &ngx_http_fastcgi_module }, + + { ngx_string("fastcgi_cache_valid"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, + ngx_http_file_cache_valid_set_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_valid), + NULL }, + + { ngx_string("fastcgi_cache_min_uses"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_min_uses), + NULL }, + + { ngx_string("fastcgi_cache_use_stale"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, + ngx_conf_set_bitmask_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_use_stale), + &ngx_http_fastcgi_next_upstream_masks }, + + { ngx_string("fastcgi_cache_methods"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, + ngx_conf_set_bitmask_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_methods), + &ngx_http_upstream_cache_method_mask }, + +#endif + { ngx_string("fastcgi_temp_path"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234, ngx_conf_set_path_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_fastcgi_loc_conf_t, upstream.temp_path), - (void *) ngx_garbage_collector_temp_handler }, + NULL }, { ngx_string("fastcgi_max_temp_file_size"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, @@ -320,6 +426,13 @@ static ngx_command_t ngx_http_fastcgi_c offsetof(ngx_http_fastcgi_loc_conf_t, upstream.hide_headers), NULL }, + { ngx_string("fastcgi_ignore_headers"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, + ngx_conf_set_bitmask_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_fastcgi_loc_conf_t, upstream.ignore_headers), + &ngx_http_fastcgi_ignore_headers_masks }, + { ngx_string("fastcgi_catch_stderr"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_conf_set_str_array_slot, @@ -385,8 +498,18 @@ static ngx_http_fastcgi_request_start_t }; -static ngx_str_t ngx_http_fastcgi_script_name = - ngx_string("fastcgi_script_name"); +static ngx_http_variable_t ngx_http_fastcgi_vars[] = { + + { ngx_string("fastcgi_script_name"), NULL, + ngx_http_fastcgi_script_name_variable, 0, + NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, + + { ngx_string("fastcgi_path_info"), NULL, + ngx_http_fastcgi_path_info_variable, 0, + NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, + + { ngx_null_string, NULL, NULL, 0, 0, 0 } +}; static ngx_str_t ngx_http_fastcgi_hide_headers[] = { @@ -400,11 +523,17 @@ static ngx_str_t ngx_http_fastcgi_hide_ }; +static ngx_path_init_t ngx_http_fastcgi_temp_path = { + ngx_string(NGX_HTTP_FASTCGI_TEMP_PATH), { 1, 2, 0 } +}; + + static ngx_int_t ngx_http_fastcgi_handler(ngx_http_request_t *r) { ngx_int_t rc; ngx_http_upstream_t *u; + ngx_http_fastcgi_ctx_t *f; ngx_http_fastcgi_loc_conf_t *flcf; if (r->subrequest_in_memory) { @@ -414,14 +543,30 @@ ngx_http_fastcgi_handler(ngx_http_reques return NGX_HTTP_INTERNAL_SERVER_ERROR; } - flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); + f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t)); + if (f == NULL) { + return NGX_ERROR; + } + + ngx_http_set_ctx(r, f, ngx_http_fastcgi_module); u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t)); if (u == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } - u->schema = flcf->upstream.schema; + r->upstream = u; + + flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); + + if (flcf->fastcgi_lengths) { + if (ngx_http_fastcgi_eval(r, flcf) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + } + + u->schema.len = sizeof("fastcgi://") - 1; + u->schema.data = (u_char *) "fastcgi://"; u->peer.log = r->connection->log; u->peer.log_error = NGX_ERROR_ERR; @@ -433,6 +578,9 @@ ngx_http_fastcgi_handler(ngx_http_reques u->conf = &flcf->upstream; +#if (NGX_HTTP_CACHE) + u->create_key = ngx_http_fastcgi_create_key; +#endif u->create_request = ngx_http_fastcgi_create_request; u->reinit_request = ngx_http_fastcgi_reinit_request; u->process_header = ngx_http_fastcgi_process_header; @@ -449,8 +597,6 @@ ngx_http_fastcgi_handler(ngx_http_reques u->pipe->input_filter = ngx_http_fastcgi_input_filter; u->pipe->input_ctx = r; - r->upstream = u; - rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { @@ -462,6 +608,83 @@ ngx_http_fastcgi_handler(ngx_http_reques static ngx_int_t +ngx_http_fastcgi_eval(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf) +{ + ngx_url_t u; + + ngx_memzero(&u, sizeof(ngx_url_t)); + + if (ngx_http_script_run(r, &u.url, flcf->fastcgi_lengths->elts, 0, + flcf->fastcgi_values->elts) + == NULL) + { + return NGX_ERROR; + } + + u.no_resolve = 1; + + if (ngx_parse_url(r->pool, &u) != NGX_OK) { + if (u.err) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "%s in upstream \"%V\"", u.err, &u.url); + } + + return NGX_ERROR; + } + + if (u.no_port) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "no port in upstream \"%V\"", &u.url); + return NGX_ERROR; + } + + r->upstream->resolved = ngx_pcalloc(r->pool, + sizeof(ngx_http_upstream_resolved_t)); + if (r->upstream->resolved == NULL) { + return NGX_ERROR; + } + + if (u.addrs && u.addrs[0].sockaddr) { + r->upstream->resolved->sockaddr = u.addrs[0].sockaddr; + r->upstream->resolved->socklen = u.addrs[0].socklen; + r->upstream->resolved->naddrs = 1; + r->upstream->resolved->host = u.addrs[0].name; + + } else { + r->upstream->resolved->host = u.host; + r->upstream->resolved->port = u.port; + } + + return NGX_OK; +} + + +#if (NGX_HTTP_CACHE) + +static ngx_int_t +ngx_http_fastcgi_create_key(ngx_http_request_t *r) +{ + ngx_str_t *key; + ngx_http_fastcgi_loc_conf_t *flcf; + + key = ngx_array_push(&r->cache->keys); + if (key == NULL) { + return NGX_ERROR; + } + + flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); + + if (ngx_http_complex_value(r, &flcf->cache_key, key) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; +} + +#endif + + +static ngx_int_t ngx_http_fastcgi_create_request(ngx_http_request_t *r) { off_t file_pos; @@ -867,15 +1090,6 @@ ngx_http_fastcgi_process_header(ngx_http umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); - if (f == NULL) { - f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t)); - if (f == NULL) { - return NGX_ERROR; - } - - ngx_http_set_ctx(r, f, ngx_http_fastcgi_module); - } - u = r->upstream; for ( ;; ) { @@ -1177,12 +1391,9 @@ ngx_http_fastcgi_process_header(ngx_http u->headers_in.status_line.data = (u_char *) "200 OK"; } - u->state->status = u->headers_in.status_n; -#if 0 - if (u->cacheable) { - u->cacheable = ngx_http_upstream_is_cacheable(r); + if (u->state) { + u->state->status = u->headers_in.status_n; } -#endif break; } @@ -1600,16 +1811,18 @@ ngx_http_fastcgi_finalize_request(ngx_ht static ngx_int_t ngx_http_fastcgi_add_variables(ngx_conf_t *cf) { - ngx_http_variable_t *var; - - var = ngx_http_add_variable(cf, &ngx_http_fastcgi_script_name, - NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE); - if (var == NULL) { - return NGX_ERROR; + ngx_http_variable_t *var, *v; + + for (v = ngx_http_fastcgi_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; } - var->get_handler = ngx_http_fastcgi_script_name_variable; - return NGX_OK; } @@ -1621,17 +1834,19 @@ ngx_http_fastcgi_create_loc_conf(ngx_con conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fastcgi_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* * set by ngx_pcalloc(): * * conf->upstream.bufs.num = 0; + * conf->upstream.ignore_headers = 0; * conf->upstream.next_upstream = 0; + * conf->upstream.cache_use_stale = 0; + * conf->upstream.cache_methods = 0; * conf->upstream.temp_path = NULL; * conf->upstream.hide_headers_hash = { NULL, 0 }; - * conf->upstream.schema = { 0, NULL }; * conf->upstream.uri = { 0, NULL }; * conf->upstream.location = NULL; * conf->upstream.store_lengths = NULL; @@ -1660,6 +1875,12 @@ ngx_http_fastcgi_create_loc_conf(ngx_con conf->upstream.pass_request_headers = NGX_CONF_UNSET; conf->upstream.pass_request_body = NGX_CONF_UNSET; +#if (NGX_HTTP_CACHE) + conf->upstream.cache = NGX_CONF_UNSET_PTR; + conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT; + conf->upstream.cache_valid = NGX_CONF_UNSET_PTR; +#endif + conf->upstream.hide_headers = NGX_CONF_UNSET_PTR; conf->upstream.pass_headers = NGX_CONF_UNSET_PTR; @@ -1691,7 +1912,7 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf if (conf->upstream.store != 0) { ngx_conf_merge_value(conf->upstream.store, - prev->upstream.store, 0); + prev->upstream.store, 0); if (conf->upstream.store_lengths == NULL) { conf->upstream.store_lengths = prev->upstream.store_lengths; @@ -1817,6 +2038,11 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf } + ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers, + prev->upstream.ignore_headers, + NGX_CONF_BITMASK_SET); + + ngx_conf_merge_bitmask_value(conf->upstream.next_upstream, prev->upstream.next_upstream, (NGX_CONF_BITMASK_SET @@ -1828,10 +2054,58 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf |NGX_HTTP_UPSTREAM_FT_OFF; } - ngx_conf_merge_path_value(conf->upstream.temp_path, + if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path, prev->upstream.temp_path, - NGX_HTTP_FASTCGI_TEMP_PATH, 1, 2, 0, - ngx_garbage_collector_temp_handler, cf); + &ngx_http_fastcgi_temp_path) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + +#if (NGX_HTTP_CACHE) + + ngx_conf_merge_ptr_value(conf->upstream.cache, + prev->upstream.cache, NULL); + + if (conf->upstream.cache && conf->upstream.cache->data == NULL) { + ngx_shm_zone_t *shm_zone; + + shm_zone = conf->upstream.cache; + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"fastcgi_cache\" zone \"%V\" is unknown", + &shm_zone->shm.name); + + return NGX_CONF_ERROR; + } + + ngx_conf_merge_uint_value(conf->upstream.cache_min_uses, + prev->upstream.cache_min_uses, 1); + + ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale, + prev->upstream.cache_use_stale, + (NGX_CONF_BITMASK_SET + |NGX_HTTP_UPSTREAM_FT_OFF)); + + if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) { + conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET + |NGX_HTTP_UPSTREAM_FT_OFF; + } + + if (conf->upstream.cache_methods == 0) { + conf->upstream.cache_methods = prev->upstream.cache_methods; + } + + conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD; + + ngx_conf_merge_ptr_value(conf->upstream.cache_valid, + prev->upstream.cache_valid, NULL); + + if (conf->cache_key.value.data == NULL) { + conf->cache_key = prev->cache_key; + } + +#endif ngx_conf_merge_value(conf->upstream.pass_request_headers, prev->upstream.pass_request_headers, 1); @@ -1861,9 +2135,20 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf if (conf->upstream.upstream == NULL) { conf->upstream.upstream = prev->upstream.upstream; - conf->upstream.schema = prev->upstream.schema; + } + + if (conf->fastcgi_lengths == NULL) { + conf->fastcgi_lengths = prev->fastcgi_lengths; + conf->fastcgi_values = prev->fastcgi_values; } +#if (NGX_PCRE) + if (conf->split_regex == NULL) { + conf->split_regex = prev->split_regex; + conf->split_name = prev->split_name; + } +#endif + if (conf->params_source == NULL) { conf->flushes = prev->flushes; conf->params_len = prev->params_len; @@ -2002,77 +2287,194 @@ ngx_http_fastcgi_script_name_variable(ng ngx_http_variable_value_t *v, uintptr_t data) { u_char *p; + ngx_http_fastcgi_ctx_t *f; ngx_http_fastcgi_loc_conf_t *flcf; - if (r->uri.len) { + flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); + + f = ngx_http_fastcgi_split(r, flcf); + + if (f == NULL) { + return NGX_ERROR; + } + + if (f->script_name.len == 0 + || f->script_name.data[f->script_name.len - 1] != '/') + { + v->len = f->script_name.len; v->valid = 1; v->no_cacheable = 0; v->not_found = 0; - - flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); - - if (r->uri.data[r->uri.len - 1] != '/') { - v->len = r->uri.len; - v->data = r->uri.data; - return NGX_OK; - } - - v->len = r->uri.len + flcf->index.len; - - v->data = ngx_pnalloc(r->pool, v->len); - if (v->data == NULL) { - return NGX_ERROR; - } - - p = ngx_copy(v->data, r->uri.data, r->uri.len); - ngx_memcpy(p, flcf->index.data, flcf->index.len); - - } else { - v->len = 0; - v->valid = 1; - v->no_cacheable = 0; - v->not_found = 0; - v->data = NULL; + v->data = f->script_name.data; return NGX_OK; } + v->len = f->script_name.len + flcf->index.len; + + v->data = ngx_pnalloc(r->pool, v->len); + if (v->data == NULL) { + return NGX_ERROR; + } + + p = ngx_copy(v->data, f->script_name.data, f->script_name.len); + ngx_memcpy(p, flcf->index.data, flcf->index.len); + return NGX_OK; } +static ngx_int_t +ngx_http_fastcgi_path_info_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_http_fastcgi_ctx_t *f; + ngx_http_fastcgi_loc_conf_t *flcf; + + flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); + + f = ngx_http_fastcgi_split(r, flcf); + + if (f == NULL) { + return NGX_ERROR; + } + + v->len = f->path_info.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = f->path_info.data; + + return NGX_OK; +} + + +static ngx_http_fastcgi_ctx_t * +ngx_http_fastcgi_split(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf) +{ + ngx_http_fastcgi_ctx_t *f; +#if (NGX_PCRE) + ngx_int_t n; + int captures[(1 + 2) * 3]; + + f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module); + + if (f == NULL) { + f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t)); + if (f == NULL) { + return NULL; + } + + ngx_http_set_ctx(r, f, ngx_http_fastcgi_module); + } + + if (f->script_name.len) { + return f; + } + + if (flcf->split_regex == NULL) { + f->script_name = r->uri; + return f; + } + + n = ngx_regex_exec(flcf->split_regex, &r->uri, captures, (1 + 2) * 3); + + if (n == NGX_REGEX_NO_MATCHED) { + f->script_name = r->uri; + return f; + } + + if (n < 0) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + ngx_regex_exec_n " failed: %d on \"%V\" using \"%V\"", + n, &r->uri, &flcf->split_name); + return NULL; + } + + /* match */ + + f->script_name.len = captures[3] - captures[2]; + f->script_name.data = r->uri.data; + + f->path_info.len = captures[5] - captures[4]; + f->path_info.data = r->uri.data + f->script_name.len; + + return f; + +#else + + f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module); + + if (f == NULL) { + f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t)); + if (f == NULL) { + return NULL; + } + + ngx_http_set_ctx(r, f, ngx_http_fastcgi_module); + } + + f->script_name = r->uri; + + return f; + +#endif +} + + static char * ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - ngx_http_fastcgi_loc_conf_t *lcf = conf; - - ngx_url_t u; - ngx_str_t *value; - ngx_http_core_loc_conf_t *clcf; - - if (lcf->upstream.schema.len) { + ngx_http_fastcgi_loc_conf_t *flcf = conf; + + ngx_url_t u; + ngx_str_t *value, *url; + ngx_uint_t n; + ngx_http_core_loc_conf_t *clcf; + ngx_http_script_compile_t sc; + + if (flcf->upstream.upstream || flcf->fastcgi_lengths) { return "is duplicate"; } + clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); + clcf->handler = ngx_http_fastcgi_handler; + value = cf->args->elts; + url = &value[1]; + + n = ngx_http_script_variables_count(url); + + if (n) { + + ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + + sc.cf = cf; + sc.source = url; + sc.lengths = &flcf->fastcgi_lengths; + sc.values = &flcf->fastcgi_values; + sc.variables = n; + sc.complete_lengths = 1; + sc.complete_values = 1; + + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; + } + ngx_memzero(&u, sizeof(ngx_url_t)); u.url = value[1]; u.no_resolve = 1; - lcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); - if (lcf->upstream.upstream == NULL) { + flcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); + if (flcf->upstream.upstream == NULL) { return NGX_CONF_ERROR; } - lcf->upstream.schema.len = sizeof("fastcgi://") - 1; - lcf->upstream.schema.data = (u_char *) "fastcgi://"; - - clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); - - clcf->handler = ngx_http_fastcgi_handler; - if (clcf->name.data[clcf->name.len - 1] == '/') { clcf->auto_redirect = 1; } @@ -2082,6 +2484,57 @@ ngx_http_fastcgi_pass(ngx_conf_t *cf, ng static char * +ngx_http_fastcgi_split_path_info(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ +#if (NGX_PCRE) + ngx_http_fastcgi_loc_conf_t *flcf = conf; + + ngx_int_t n; + ngx_str_t *value, err; + u_char errstr[NGX_MAX_CONF_ERRSTR]; + + value = cf->args->elts; + + flcf->split_name = value[1]; + + err.len = NGX_MAX_CONF_ERRSTR; + err.data = errstr; + + flcf->split_regex = ngx_regex_compile(&value[1], 0, cf->pool, &err); + + if (flcf->split_regex == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data); + return NGX_CONF_ERROR; + } + + n = ngx_regex_capture_count(flcf->split_regex); + + if (n < 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + ngx_regex_capture_count_n " failed for " + "pattern \"%V\"", &value[1]); + return NGX_CONF_ERROR; + } + + if (n != 2) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "pattern \"%V\" must have 2 captures", &value[1]); + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; + +#else + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%V\" requires PCRE library", &cmd->name); + return NGX_CONF_ERROR; + +#endif +} + + +static char * ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_fastcgi_loc_conf_t *flcf = conf; @@ -2089,20 +2542,31 @@ ngx_http_fastcgi_store(ngx_conf_t *cf, n ngx_str_t *value; ngx_http_script_compile_t sc; - if (flcf->upstream.store != NGX_CONF_UNSET || flcf->upstream.store_lengths) + if (flcf->upstream.store != NGX_CONF_UNSET + || flcf->upstream.store_lengths) { return "is duplicate"; } value = cf->args->elts; - if (ngx_strcmp(value[1].data, "on") == 0) { - flcf->upstream.store = 1; + if (ngx_strcmp(value[1].data, "off") == 0) { + flcf->upstream.store = 0; return NGX_CONF_OK; } - if (ngx_strcmp(value[1].data, "off") == 0) { - flcf->upstream.store = 0; +#if (NGX_HTTP_CACHE) + + if (flcf->upstream.cache != NGX_CONF_UNSET_PTR + && flcf->upstream.cache != NULL) + { + return "is incompatible with \"fastcgi_cache\""; + } + +#endif + + if (ngx_strcmp(value[1].data, "on") == 0) { + flcf->upstream.store = 1; return NGX_CONF_OK; } @@ -2127,6 +2591,70 @@ ngx_http_fastcgi_store(ngx_conf_t *cf, n } +#if (NGX_HTTP_CACHE) + +static char * +ngx_http_fastcgi_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_fastcgi_loc_conf_t *flcf = conf; + + ngx_str_t *value; + + value = cf->args->elts; + + if (flcf->upstream.cache != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + if (ngx_strcmp(value[1].data, "off") == 0) { + flcf->upstream.cache = NULL; + return NGX_CONF_OK; + } + + if (flcf->upstream.store > 0 || flcf->upstream.store_lengths) { + return "is incompatible with \"fastcgi_store\""; + } + + flcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, + &ngx_http_fastcgi_module); + if (flcf->upstream.cache == NULL) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_fastcgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_fastcgi_loc_conf_t *flcf = conf; + + ngx_str_t *value; + ngx_http_compile_complex_value_t ccv; + + value = cf->args->elts; + + if (flcf->cache_key.value.len) { + return "is duplicate"; + } + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &flcf->cache_key; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + +#endif + + static char * ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post, void *data) { diff --git a/src/http/modules/ngx_http_flv_module.c b/src/http/modules/ngx_http_flv_module.c --- a/src/http/modules/ngx_http_flv_module.c +++ b/src/http/modules/ngx_http_flv_module.c @@ -60,12 +60,12 @@ ngx_module_t ngx_http_flv_module = { static ngx_int_t ngx_http_flv_handler(ngx_http_request_t *r) { - u_char *p, *n, *last; + u_char *last; off_t start, len; size_t root; ngx_int_t rc; ngx_uint_t level, i; - ngx_str_t path; + ngx_str_t path, value; ngx_log_t *log; ngx_buf_t *b; ngx_chain_t out[2]; @@ -80,7 +80,6 @@ ngx_http_flv_handler(ngx_http_request_t return NGX_DECLINED; } - /* TODO: Win32 */ if (r->zero_in_uri) { return NGX_DECLINED; } @@ -144,7 +143,7 @@ ngx_http_flv_handler(ngx_http_request_t if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) { ngx_log_error(level, log, of.err, - ngx_open_file_n " \"%s\" failed", path.data); + "%s \"%s\" failed", of.failed, path.data); } return rc; @@ -160,25 +159,17 @@ ngx_http_flv_handler(ngx_http_request_t return NGX_DECLINED; } - r->root_tested = 1; + r->root_tested = !r->error_page; start = 0; len = of.size; i = 1; if (r->args.len) { - p = (u_char *) ngx_strnstr(r->args.data, "start=", r->args.len); - - if (p) { - p += 6; - for (n = p; n < r->args.data + r->args.len; n++) { - if (*n == '&') { - break; - } - } + if (ngx_http_arg(r, (u_char *) "start", 5, &value) == NGX_OK) { - start = ngx_atoof(p, n - p); + start = ngx_atoof(value.data, value.len); if (start == NGX_ERROR || start >= len) { start = 0; diff --git a/src/http/modules/ngx_http_geo_module.c b/src/http/modules/ngx_http_geo_module.c --- a/src/http/modules/ngx_http_geo_module.c +++ b/src/http/modules/ngx_http_geo_module.c @@ -10,20 +10,66 @@ typedef struct { - ngx_radix_tree_t *tree; - ngx_pool_t *pool; - ngx_array_t values; + u_short start; + u_short end; + ngx_http_variable_value_t *value; +} ngx_http_geo_range_t; + + +typedef struct { + ngx_http_geo_range_t *ranges; + ngx_uint_t n; +} ngx_http_geo_low_ranges_t; + + +typedef struct { + ngx_http_geo_low_ranges_t low[0x10000]; + ngx_http_variable_value_t *default_value; +} ngx_http_geo_high_ranges_t; + + +typedef struct { + ngx_http_variable_value_t *value; + ngx_str_t *net; + ngx_http_geo_high_ranges_t *high; + ngx_radix_tree_t *tree; + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; + ngx_pool_t *pool; + ngx_pool_t *temp_pool; } ngx_http_geo_conf_ctx_t; +typedef struct { + union { + ngx_radix_tree_t *tree; + ngx_http_geo_high_ranges_t *high; + } u; + + ngx_int_t index; +} ngx_http_geo_ctx_t; + + +static in_addr_t ngx_http_geo_addr(ngx_http_request_t *r, + ngx_http_geo_ctx_t *ctx); static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); +static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + ngx_str_t *value); +static char *ngx_http_geo_add_range(ngx_conf_t *cf, + ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end); +static ngx_uint_t ngx_http_geo_delete_range(ngx_conf_t *cf, + ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end); +static char *ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + ngx_str_t *value); +static ngx_http_variable_value_t *ngx_http_geo_value(ngx_conf_t *cf, + ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value); static ngx_command_t ngx_http_geo_commands[] = { { ngx_string("geo"), - NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1, + NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12, ngx_http_geo_block, NGX_HTTP_MAIN_CONF_OFFSET, 0, @@ -67,53 +113,130 @@ ngx_module_t ngx_http_geo_module = { /* AF_INET only */ static ngx_int_t -ngx_http_geo_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, +ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - ngx_radix_tree_t *tree = (ngx_radix_tree_t *) data; + ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data; - struct sockaddr_in *sin; ngx_http_variable_value_t *vv; - sin = (struct sockaddr_in *) r->connection->sockaddr; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http geo started"); - vv = (ngx_http_variable_value_t *) - ngx_radix32tree_find(tree, ntohl(sin->sin_addr.s_addr)); + ngx_radix32tree_find(ctx->u.tree, ngx_http_geo_addr(r, ctx)); *v = *vv; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http geo: %V %v", &r->connection->addr_text, v); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http geo: %v", v); return NGX_OK; } +static ngx_int_t +ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data; + + in_addr_t addr; + ngx_uint_t i, n; + ngx_http_geo_range_t *range; + + *v = *ctx->u.high->default_value; + + addr = ngx_http_geo_addr(r, ctx); + + range = ctx->u.high->low[addr >> 16].ranges; + + n = addr & 0xffff; + + for (i = 0; i < ctx->u.high->low[addr >> 16].n; i++) { + if (n >= (ngx_uint_t) range[i].start + && n <= (ngx_uint_t) range[i].end) + { + *v = *range[i].value; + } + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http geo: %v", v); + + return NGX_OK; +} + + +static in_addr_t +ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx) +{ + struct sockaddr_in *sin; + ngx_http_variable_value_t *v; + + if (ctx->index == -1) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http geo started: %V", &r->connection->addr_text); + + if (r->connection->sockaddr->sa_family != AF_INET) { + return 0; + } + + sin = (struct sockaddr_in *) r->connection->sockaddr; + return ntohl(sin->sin_addr.s_addr); + } + + v = ngx_http_get_flushed_variable(r, ctx->index); + + if (v == NULL || v->not_found) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http geo not found"); + + return 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http geo started: %v", v); + + return ntohl(ngx_inet_addr(v->data, v->len)); +} + + static char * ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *rv; + size_t len; ngx_str_t *value, name; + ngx_uint_t i; ngx_conf_t save; ngx_pool_t *pool; - ngx_radix_tree_t *tree; + ngx_array_t *a; ngx_http_variable_t *var; + ngx_http_geo_ctx_t *geo; ngx_http_geo_conf_ctx_t ctx; value = cf->args->elts; + geo = ngx_palloc(cf->pool, sizeof(ngx_http_geo_ctx_t)); + if (geo == NULL) { + return NGX_CONF_ERROR; + } + name = value[1]; + name.len--; + name.data++; - if (name.data[0] != '$') { - ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "\"%V\" variable name should start with '$'", - &value[1]); - } else { + if (cf->args->nelts == 3) { + + geo->index = ngx_http_get_variable_index(cf, &name); + if (geo->index == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + name = value[2]; name.len--; name.data++; + + } else { + geo->index = -1; } var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE); @@ -121,29 +244,21 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c return NGX_CONF_ERROR; } - tree = ngx_radix_tree_create(cf->pool, -1); - - if (tree == NULL) { - return NGX_CONF_ERROR; - } - - var->get_handler = ngx_http_geo_variable; - var->data = (uintptr_t) tree; - pool = ngx_create_pool(16384, cf->log); if (pool == NULL) { return NGX_CONF_ERROR; } - if (ngx_array_init(&ctx.values, pool, 512, - sizeof(ngx_http_variable_value_t *)) - != NGX_OK) - { - ngx_destroy_pool(pool); + ctx.temp_pool = ngx_create_pool(16384, cf->log); + if (ctx.temp_pool == NULL) { return NGX_CONF_ERROR; } - ctx.tree = tree; + ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, + ngx_http_variable_value_rbtree_insert); + + ctx.high = NULL; + ctx.tree = NULL; ctx.pool = cf->pool; save = *cf; @@ -156,121 +271,589 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c *cf = save; - ngx_destroy_pool(pool); + if (ctx.high) { + + for (i = 0; i < 0x10000; i++) { + a = (ngx_array_t *) ctx.high->low[i].ranges; + + if (a == NULL || a->nelts == 0) { + continue; + } + + ctx.high->low[i].n = a->nelts; + + len = a->nelts * sizeof(ngx_http_geo_range_t); - if (ngx_radix32tree_find(tree, 0) != NGX_RADIX_NO_VALUE) { - return rv; - } + ctx.high->low[i].ranges = ngx_palloc(cf->pool, len); + if (ctx.high->low[i].ranges == NULL ){ + return NGX_CONF_ERROR; + } + + ngx_memcpy(ctx.high->low[i].ranges, a->elts, len); + } + + geo->u.high = ctx.high; + + var->get_handler = ngx_http_geo_range_variable; + var->data = (uintptr_t) geo; + + ngx_destroy_pool(ctx.temp_pool); + ngx_destroy_pool(pool); - if (ngx_radix32tree_insert(tree, 0, 0, - (uintptr_t) &ngx_http_variable_null_value) - == NGX_ERROR) - { - return NGX_CONF_ERROR; + if (ctx.high->default_value == NULL) { + ctx.high->default_value = &ngx_http_variable_null_value; + } + + } else { + if (ctx.tree == NULL) { + ctx.tree = ngx_radix_tree_create(cf->pool, -1); + if (ctx.tree == NULL) { + return NGX_CONF_ERROR; + } + } + + geo->u.tree = ctx.tree; + + var->get_handler = ngx_http_geo_cidr_variable; + var->data = (uintptr_t) geo; + + ngx_destroy_pool(ctx.temp_pool); + ngx_destroy_pool(pool); + + if (ngx_radix32tree_find(ctx.tree, 0) != NGX_RADIX_NO_VALUE) { + return rv; + } + + if (ngx_radix32tree_insert(ctx.tree, 0, 0, + (uintptr_t) &ngx_http_variable_null_value) + == NGX_ERROR) + { + return NGX_CONF_ERROR; + } } return rv; } -/* AF_INET only */ - static char * ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) { - ngx_int_t rc; - ngx_str_t *value, file; - ngx_uint_t i; - ngx_inet_cidr_t cidrin; - ngx_http_geo_conf_ctx_t *ctx; - ngx_http_variable_value_t *var, *old, **v; + char *rv; + ngx_str_t *value, file; + ngx_http_geo_conf_ctx_t *ctx; ctx = cf->ctx; + value = cf->args->elts; + + if (cf->args->nelts == 1) { + + if (ngx_strcmp(value[0].data, "ranges") == 0) { + + if (ctx->tree) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"ranges\" directive must be " + "the first directive inside \"geo\" block"); + goto failed; + } + + ctx->high = ngx_pcalloc(ctx->pool, + sizeof(ngx_http_geo_high_ranges_t)); + if (ctx->high == NULL) { + goto failed; + } + + rv = NGX_CONF_OK; + + goto done; + } + } + if (cf->args->nelts != 2) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid number of the geo parameters"); - return NGX_CONF_ERROR; + goto failed; } - value = cf->args->elts; + if (ngx_strcmp(value[0].data, "include") == 0) { + + file.len = value[1].len++; - if (ngx_strcmp(value[0].data, "include") == 0) { - file = value[1]; + file.data = ngx_pstrdup(ctx->temp_pool, &value[1]); + if (file.data == NULL) { + goto failed; + } - if (ngx_conf_full_name(cf->cycle, &file, 1) == NGX_ERROR){ - return NGX_CONF_ERROR; + if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK){ + goto failed; } ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); - return ngx_conf_parse(cf, &file); + rv = ngx_conf_parse(cf, &file); + + goto done; + } + + if (ctx->high) { + rv = ngx_http_geo_range(cf, ctx, value); + + } else { + rv = ngx_http_geo_cidr(cf, ctx, value); } +done: + + ngx_reset_pool(cf->pool); + + return rv; + +failed: + + ngx_reset_pool(cf->pool); + + return NGX_CONF_ERROR; +} + + +static char * +ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + ngx_str_t *value) +{ + u_char *p, *last; + in_addr_t start, end; + ngx_str_t *net; + ngx_uint_t del; + ngx_http_variable_value_t *old; + if (ngx_strcmp(value[0].data, "default") == 0) { - cidrin.addr = 0; - cidrin.mask = 0; + + old = ctx->high->default_value; - } else { - rc = ngx_ptocidr(&value[0], &cidrin); - - if (rc == NGX_ERROR) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid parameter \"%V\"", &value[0]); + ctx->high->default_value = ngx_http_geo_value(cf, ctx, &value[1]); + if (ctx->high->default_value == NULL) { return NGX_CONF_ERROR; } - if (rc == NGX_DONE) { + if (old) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "duplicate range \"%V\", value: \"%v\", old value: \"%v\"", + &value[0], ctx->high->default_value, old); + } + + return NGX_CONF_OK; + } + + if (ngx_strcmp(value[0].data, "delete") == 0) { + net = &value[1]; + del = 1; + + } else { + net = &value[0]; + del = 0; + } + + last = net->data + net->len; + + p = ngx_strlchr(net->data, last, '-'); + + if (p == NULL) { + goto invalid; + } + + start = ngx_inet_addr(net->data, p - net->data); + + if (start == INADDR_NONE) { + goto invalid; + } + + start = ntohl(start); + + p++; + + end = ngx_inet_addr(p, last - p); + + if (end == INADDR_NONE) { + goto invalid; + } + + end = ntohl(end); + + if (start > end) { + goto invalid; + } + + if (del) { + if (ngx_http_geo_delete_range(cf, ctx, start, end)) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "low address bits of %V are meaningless", - &value[0]); + "no address range \"%V\" to delete", net); + } + + return NGX_CONF_OK; + } + + ctx->value = ngx_http_geo_value(cf, ctx, &value[1]); + + if (ctx->value == NULL) { + return NGX_CONF_ERROR; + } + + ctx->net = net; + + return ngx_http_geo_add_range(cf, ctx, start, end); + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid range \"%V\"", net); + + return NGX_CONF_ERROR; +} + + +/* the add procedure is optimized to add a growing up sequence */ + +static char * +ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + in_addr_t start, in_addr_t end) +{ + in_addr_t n; + ngx_uint_t h, i, s, e; + ngx_array_t *a; + ngx_http_geo_range_t *range; + + for (n = start; n <= end; n += 0x10000) { + + h = n >> 16; + + if (n == start) { + s = n & 0xffff; + } else { + s = 0; + } + + if ((n | 0xffff) > end) { + e = end & 0xffff; + + } else { + e = 0xffff; + } + + a = (ngx_array_t *) ctx->high->low[h].ranges; + + if (a == NULL) { + a = ngx_array_create(ctx->temp_pool, 64, + sizeof(ngx_http_geo_range_t)); + if (a == NULL) { + return NGX_CONF_ERROR; + } + + ctx->high->low[h].ranges = (ngx_http_geo_range_t *) a; } - cidrin.addr = ntohl(cidrin.addr); - cidrin.mask = ntohl(cidrin.mask); + i = a->nelts; + range = a->elts; + + while (i) { + + i--; + + if (e < (ngx_uint_t) range[i].start) { + continue; + } + + if (s > (ngx_uint_t) range[i].end) { + + /* add after the range */ + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; + } + + range = a->elts; + + ngx_memcpy(&range[i + 2], &range[i + 1], + (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t)); + + range[i + 1].start = (u_short) s; + range[i + 1].end = (u_short) e; + range[i + 1].value = ctx->value; + + goto next; + } + + if (s == (ngx_uint_t) range[i].start + && e == (ngx_uint_t) range[i].end) + { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "duplicate range \"%V\", value: \"%v\", old value: \"%v\"", + ctx->net, ctx->value, range[i].value); + + range[i].value = ctx->value; + + goto next; + } + + if (s > (ngx_uint_t) range[i].start + && e < (ngx_uint_t) range[i].end) + { + /* split the range and insert the new one */ + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; + } + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; + } + + range = a->elts; + + ngx_memcpy(&range[i + 3], &range[i + 1], + (a->nelts - 3 - i) * sizeof(ngx_http_geo_range_t)); + + range[i + 2].start = (u_short) (e + 1); + range[i + 2].end = range[i].end; + range[i + 2].value = range[i].value; + + range[i + 1].start = (u_short) s; + range[i + 1].end = (u_short) e; + range[i + 1].value = ctx->value; + + range[i].end = (u_short) (s - 1); + + goto next; + } + + if (s == (ngx_uint_t) range[i].start + && e < (ngx_uint_t) range[i].end) + { + /* shift the range start and insert the new range */ + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; + } + + range = a->elts; + + ngx_memcpy(&range[i + 1], &range[i], + (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t)); + + range[i + 1].start = (u_short) (e + 1); + + range[i].start = (u_short) s; + range[i].end = (u_short) e; + range[i].value = ctx->value; + + goto next; + } + + if (s > (ngx_uint_t) range[i].start + && e == (ngx_uint_t) range[i].end) + { + /* shift the range end and insert the new range */ + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; + } + + range = a->elts; + + ngx_memcpy(&range[i + 2], &range[i + 1], + (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t)); + + range[i + 1].start = (u_short) s; + range[i + 1].end = (u_short) e; + range[i + 1].value = ctx->value; + + range[i].end = (u_short) (s - 1); + + goto next; + } + + s = (ngx_uint_t) range[i].start; + e = (ngx_uint_t) range[i].end; + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "range \"%V\" overlaps \"%d.%d.%d.%d-%d.%d.%d.%d\"", + ctx->net, + h >> 8, h & 0xff, s >> 8, s & 0xff, + h >> 8, h & 0xff, e >> 8, e & 0xff); + + return NGX_CONF_ERROR; + } + + /* add the first range */ + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; + } + + range->start = (u_short) s; + range->end = (u_short) e; + range->value = ctx->value; + + next: + + continue; } - var = NULL; - v = ctx->values.elts; + return NGX_CONF_OK; +} + + +static ngx_uint_t +ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + in_addr_t start, in_addr_t end) +{ + in_addr_t n; + ngx_uint_t h, i, s, e, warn; + ngx_array_t *a; + ngx_http_geo_range_t *range; + + warn = 0; + + for (n = start; n <= end; n += 0x10000) { - for (i = 0; i < ctx->values.nelts; i++) { - if ((size_t) v[i]->len != value[1].len) { + h = n >> 16; + + if (n == start) { + s = n & 0xffff; + } else { + s = 0; + } + + if ((n | 0xffff) > end) { + e = end & 0xffff; + + } else { + e = 0xffff; + } + + a = (ngx_array_t *) ctx->high->low[h].ranges; + + if (a == NULL) { + warn = 1; continue; } - if (ngx_strncmp(value[1].data, v[i]->data, value[1].len) == 0) { - var = v[i]; - break; + range = a->elts; + for (i = 0; i < a->nelts; i++) { + + if (s == (ngx_uint_t) range[i].start + && e == (ngx_uint_t) range[i].end) + { + ngx_memcpy(&range[i], &range[i + 1], + (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t)); + + a->nelts--; + + break; + } + + if (s != (ngx_uint_t) range[i].start + && e != (ngx_uint_t) range[i].end) + { + continue; + } + + warn = 1; + } + } + + return warn; +} + + +static char * +ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + ngx_str_t *value) +{ + ngx_int_t rc, del; + ngx_str_t *net; + ngx_uint_t i; + ngx_cidr_t cidr; + ngx_http_variable_value_t *val, *old; + + if (ctx->tree == NULL) { + ctx->tree = ngx_radix_tree_create(ctx->pool, -1); + if (ctx->tree == NULL) { + return NGX_CONF_ERROR; } } - if (var == NULL) { - var = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t)); - if (var == NULL) { - return NGX_CONF_ERROR; + if (ngx_strcmp(value[0].data, "default") == 0) { + cidr.u.in.addr = 0; + cidr.u.in.mask = 0; + net = &value[0]; + + } else { + if (ngx_strcmp(value[0].data, "delete") == 0) { + net = &value[1]; + del = 1; + + } else { + net = &value[0]; + del = 0; } - var->len = value[1].len; - var->data = ngx_pstrdup(ctx->pool, &value[1]); - if (var->data == NULL) { - return NGX_CONF_ERROR; + if (ngx_strcmp(net->data, "255.255.255.255") == 0) { + cidr.u.in.addr = 0xffffffff; + cidr.u.in.mask = 0xffffffff; + + } else { + rc = ngx_ptocidr(net, &cidr); + + if (rc == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid network \"%V\"", net); + return NGX_CONF_ERROR; + } + + if (cidr.family != AF_INET) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"geo\" supports IPv4 only"); + return NGX_CONF_ERROR; + } + + if (rc == NGX_DONE) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "low address bits of %V are meaningless", + net); + } + + cidr.u.in.addr = ntohl(cidr.u.in.addr); + cidr.u.in.mask = ntohl(cidr.u.in.mask); } - var->valid = 1; - var->no_cacheable = 0; - var->not_found = 0; + if (del) { + if (ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr, + cidr.u.in.mask) + != NGX_OK) + { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "no network \"%V\" to delete", net); + } - v = ngx_array_push(&ctx->values); - if (v == NULL) { - return NGX_CONF_ERROR; + return NGX_CONF_OK; } + } - *v = var; + val = ngx_http_geo_value(cf, ctx, &value[1]); + + if (val == NULL) { + return NGX_CONF_ERROR; } for (i = 2; i; i--) { - rc = ngx_radix32tree_insert(ctx->tree, cidrin.addr, cidrin.mask, - (uintptr_t) var); + rc = ngx_radix32tree_insert(ctx->tree, cidr.u.in.addr, cidr.u.in.mask, + (uintptr_t) val); if (rc == NGX_OK) { return NGX_CONF_OK; } @@ -282,18 +865,65 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command /* rc == NGX_BUSY */ old = (ngx_http_variable_value_t *) - ngx_radix32tree_find(ctx->tree, cidrin.addr & cidrin.mask); + ngx_radix32tree_find(ctx->tree, cidr.u.in.addr & cidr.u.in.mask); ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "duplicate parameter \"%V\", value: \"%v\", old value: \"%v\"", - &value[0], var, old); + "duplicate network \"%V\", value: \"%v\", old value: \"%v\"", + net, val, old); - rc = ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask); + rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr, cidr.u.in.mask); if (rc == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree"); return NGX_CONF_ERROR; } } return NGX_CONF_ERROR; } + + +static ngx_http_variable_value_t * +ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + ngx_str_t *value) +{ + uint32_t hash; + ngx_http_variable_value_t *val; + ngx_http_variable_value_node_t *vvn; + + hash = ngx_crc32_long(value->data, value->len); + + val = ngx_http_variable_value_lookup(&ctx->rbtree, value, hash); + + if (val) { + return val; + } + + val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t)); + if (val == NULL) { + return NULL; + } + + val->len = value->len; + val->data = ngx_pstrdup(ctx->pool, value); + if (val->data == NULL) { + return NULL; + } + + val->valid = 1; + val->no_cacheable = 0; + val->not_found = 0; + + vvn = ngx_palloc(ctx->temp_pool, sizeof(ngx_http_variable_value_node_t)); + if (vvn == NULL) { + return NULL; + } + + vvn->node.key = hash; + vvn->len = val->len; + vvn->value = val; + + ngx_rbtree_insert(&ctx->rbtree, &vvn->node); + + return val; +} diff --git a/src/http/modules/ngx_http_gzip_filter_module.c b/src/http/modules/ngx_http_gzip_filter_module.c --- a/src/http/modules/ngx_http_gzip_filter_module.c +++ b/src/http/modules/ngx_http_gzip_filter_module.c @@ -19,6 +19,7 @@ typedef struct { ngx_bufs_t bufs; + size_t postpone_gzipping; ngx_int_t level; size_t wbits; size_t memlevel; @@ -34,19 +35,27 @@ typedef struct { ngx_chain_t *busy; ngx_chain_t *out; ngx_chain_t **last_out; + + ngx_chain_t *copied; + ngx_chain_t *copy_buf; + ngx_buf_t *in_buf; ngx_buf_t *out_buf; ngx_int_t bufs; - off_t length; - void *preallocated; char *free_mem; ngx_uint_t allocated; + int wbits; + int memlevel; + unsigned flush:4; unsigned redo:1; unsigned done:1; + unsigned nomem:1; + unsigned gzheader:1; + unsigned buffering:1; size_t zin; size_t zout; @@ -57,10 +66,45 @@ typedef struct { } ngx_http_gzip_ctx_t; +#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) + +struct gztrailer { + uint32_t crc32; + uint32_t zlen; +}; + +#else /* NGX_HAVE_BIG_ENDIAN || !NGX_HAVE_NONALIGNED */ + +struct gztrailer { + u_char crc32[4]; + u_char zlen[4]; +}; + +#endif + + +static void ngx_http_gzip_filter_memory(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx); +static ngx_int_t ngx_http_gzip_filter_buffer(ngx_http_gzip_ctx_t *ctx, + ngx_chain_t *in); +static ngx_int_t ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx); +static ngx_int_t ngx_http_gzip_filter_gzheader(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx); +static ngx_int_t ngx_http_gzip_filter_add_data(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx); +static ngx_int_t ngx_http_gzip_filter_get_buf(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx); +static ngx_int_t ngx_http_gzip_filter_deflate(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx); +static ngx_int_t ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx); + static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items, u_int size); static void ngx_http_gzip_filter_free(void *opaque, void *address); -static void ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx); +static void ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx); static ngx_int_t ngx_http_gzip_add_variables(ngx_conf_t *cf); static ngx_int_t ngx_http_gzip_ratio_variable(ngx_http_request_t *r, @@ -127,6 +171,13 @@ static ngx_command_t ngx_http_gzip_filt offsetof(ngx_http_gzip_conf_t, memlevel), &ngx_http_gzip_hash_p }, + { ngx_string("postpone_gzipping"), + 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_gzip_conf_t, postpone_gzipping), + NULL }, + { ngx_string("gzip_no_buffer"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, @@ -176,25 +227,6 @@ ngx_module_t ngx_http_gzip_filter_modul }; -static u_char gzheader[10] = { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 }; - -#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) - -struct gztrailer { - uint32_t crc32; - uint32_t zlen; -}; - -#else /* NGX_HAVE_BIG_ENDIAN || !NGX_HAVE_NONALIGNED */ - -struct gztrailer { - u_char crc32[4]; - u_char zlen[4]; -}; - -#endif - - static ngx_str_t ngx_http_gzip_ratio = ngx_string("gzip_ratio"); static ngx_http_output_header_filter_pt ngx_http_next_header_filter; @@ -233,6 +265,9 @@ ngx_http_gzip_header_filter(ngx_http_req ngx_http_set_ctx(r, ctx, ngx_http_gzip_filter_module); ctx->request = r; + ctx->buffering = (conf->postpone_gzipping != 0); + + ngx_http_gzip_filter_memory(r, ctx); h = ngx_list_push(&r->headers_out.headers); if (h == NULL) { @@ -247,8 +282,6 @@ ngx_http_gzip_header_filter(ngx_http_req r->headers_out.content_encoding = h; - ctx->length = r->headers_out.content_length_n; - r->main_filter_need_in_memory = 1; ngx_http_clear_content_length(r); @@ -261,13 +294,9 @@ ngx_http_gzip_header_filter(ngx_http_req static ngx_int_t ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { - int rc, wbits, memlevel; - ngx_int_t last; - struct gztrailer *trailer; - ngx_buf_t *b; - ngx_chain_t *cl, out; - ngx_http_gzip_ctx_t *ctx; - ngx_http_gzip_conf_t *conf; + int rc; + ngx_chain_t *cl; + ngx_http_gzip_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module); @@ -275,392 +304,633 @@ ngx_http_gzip_body_filter(ngx_http_reque return ngx_http_next_body_filter(r, in); } - conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); - - if (ctx->preallocated == NULL) { - wbits = conf->wbits; - memlevel = conf->memlevel; - - if (ctx->length > 0) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http gzip filter"); - /* the actual zlib window size is smaller by 262 bytes */ - - while (ctx->length < ((1 << (wbits - 1)) - 262)) { - wbits--; - memlevel--; - } - } + if (ctx->buffering) { /* - * We preallocate a memory for zlib in one buffer (200K-400K), this - * decreases a number of malloc() and free() calls and also probably - * decreases a number of syscalls (sbrk() and so on). - * Besides we free this memory as soon as the gzipping will complete - * and do not wait while a whole response will be sent to a client. - * - * 8K is for zlib deflate_state, it takes - * *) 5816 bytes on i386 and sparc64 (32-bit mode) - * *) 5920 bytes on amd64 and sparc64 + * With default memory settings zlib starts to output gzipped data + * only after it has got about 90K, so it makes sense to allocate + * zlib memory (200-400K) only after we have enough data to compress. + * Although we copy buffers, nevertheless for not big responses + * this allows to allocate zlib memory, to compress and to output + * the response in one step using hot CPU cache. */ - ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9)); - - ctx->preallocated = ngx_palloc(r->pool, ctx->allocated); - if (ctx->preallocated == NULL) { - return NGX_ERROR; - } - - ctx->free_mem = ctx->preallocated; + if (in) { + switch (ngx_http_gzip_filter_buffer(ctx, in)) { - ctx->zstream.zalloc = ngx_http_gzip_filter_alloc; - ctx->zstream.zfree = ngx_http_gzip_filter_free; - ctx->zstream.opaque = ctx; - - rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED, - -wbits, memlevel, Z_DEFAULT_STRATEGY); + case NGX_OK: + return NGX_OK; - if (rc != Z_OK) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "deflateInit2() failed: %d", rc); - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } + case NGX_DONE: + in = NULL; + break; - b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); - if (b == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - b->memory = 1; - b->pos = gzheader; - b->last = b->pos + 10; - - out.buf = b; - out.next = NULL; + default: /* NGX_ERROR */ + goto failed; + } - /* - * We pass the gzheader to the next filter now to avoid its linking - * to the ctx->busy chain. zlib does not usually output the compressed - * data in the initial iterations, so the gzheader that was linked - * to the ctx->busy chain would be flushed by ngx_http_write_filter(). - */ + } else { + ctx->buffering = 0; + } + } - if (ngx_http_next_body_filter(r, &out) == NGX_ERROR) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; + if (ctx->preallocated == NULL) { + if (ngx_http_gzip_filter_deflate_start(r, ctx) != NGX_OK) { + goto failed; } - - r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED; - - ctx->last_out = &ctx->out; - - ctx->crc32 = crc32(0L, Z_NULL, 0); - ctx->flush = Z_NO_FLUSH; } if (in) { - if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; + if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) { + goto failed; } } - last = NGX_NONE; + if (ctx->nomem) { + + /* flush busy buffers */ + + if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) { + goto failed; + } + + cl = NULL; + + ngx_chain_update_chains(&ctx->free, &ctx->busy, &cl, + (ngx_buf_tag_t) &ngx_http_gzip_filter_module); + ctx->nomem = 0; + } for ( ;; ) { + /* cycle while we can write to a client */ + for ( ;; ) { - /* does zlib need a new data ? */ - - if (ctx->zstream.avail_in == 0 - && ctx->flush == Z_NO_FLUSH - && !ctx->redo) - { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "gzip in: %p", ctx->in); + /* cycle while there is data to feed zlib and ... */ - if (ctx->in == NULL) { - break; - } - - ctx->in_buf = ctx->in->buf; - ctx->in = ctx->in->next; - - ctx->zstream.next_in = ctx->in_buf->pos; - ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos; - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "gzip in_buf:%p ni:%p ai:%ud", - ctx->in_buf, - ctx->zstream.next_in, ctx->zstream.avail_in); + rc = ngx_http_gzip_filter_add_data(r, ctx); - /* STUB */ - if (ctx->in_buf->last < ctx->in_buf->pos) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "zstream.avail_in is huge"); - ctx->done = 1; - return NGX_ERROR; - } - /**/ - - if (ctx->in_buf->last_buf) { - ctx->flush = Z_FINISH; + if (rc == NGX_DECLINED) { + break; + } - } else if (ctx->in_buf->flush) { - ctx->flush = Z_SYNC_FLUSH; - } - - if (ctx->zstream.avail_in == 0) { - if (ctx->flush == Z_NO_FLUSH) { - continue; - } - - } else { - ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in, - ctx->zstream.avail_in); - } + if (rc == NGX_AGAIN) { + continue; } - /* is there a space for the gzipped data ? */ - - if (ctx->zstream.avail_out == 0) { - - if (ctx->free) { - ctx->out_buf = ctx->free->buf; - ctx->free = ctx->free->next; - - } else if (ctx->bufs < conf->bufs.num) { - ctx->out_buf = ngx_create_temp_buf(r->pool, - conf->bufs.size); - if (ctx->out_buf == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - ctx->out_buf->tag = (ngx_buf_tag_t) - &ngx_http_gzip_filter_module; - ctx->out_buf->recycled = 1; - ctx->bufs++; + /* ... there are buffers to write zlib output */ - } else { - break; - } - - ctx->zstream.next_out = ctx->out_buf->pos; - ctx->zstream.avail_out = conf->bufs.size; - } + rc = ngx_http_gzip_filter_get_buf(r, ctx); - ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d", - ctx->zstream.next_in, ctx->zstream.next_out, - ctx->zstream.avail_in, ctx->zstream.avail_out, - ctx->flush, ctx->redo); - - rc = deflate(&ctx->zstream, ctx->flush); - - if (rc != Z_OK && rc != Z_STREAM_END) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "deflate() failed: %d, %d", ctx->flush, rc); - ngx_http_gzip_error(ctx); - return NGX_ERROR; + if (rc == NGX_DECLINED) { + break; } - ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", - ctx->zstream.next_in, ctx->zstream.next_out, - ctx->zstream.avail_in, ctx->zstream.avail_out, - rc); - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "gzip in_buf:%p pos:%p", - ctx->in_buf, ctx->in_buf->pos); - - - if (ctx->zstream.next_in) { - ctx->in_buf->pos = ctx->zstream.next_in; - - if (ctx->zstream.avail_in == 0) { - ctx->zstream.next_in = NULL; - } + if (rc == NGX_ERROR) { + goto failed; } - ctx->out_buf->last = ctx->zstream.next_out; - - if (ctx->zstream.avail_out == 0) { - - /* zlib wants to output some more gzipped data */ - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - cl->buf = ctx->out_buf; - cl->next = NULL; - *ctx->last_out = cl; - ctx->last_out = &cl->next; - - ctx->redo = 1; - continue; - } - - ctx->redo = 0; - - if (ctx->flush == Z_SYNC_FLUSH) { - - ctx->zstream.avail_out = 0; - ctx->out_buf->flush = 1; - ctx->flush = Z_NO_FLUSH; + rc = ngx_http_gzip_filter_deflate(r, ctx); - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - cl->buf = ctx->out_buf; - cl->next = NULL; - *ctx->last_out = cl; - ctx->last_out = &cl->next; - + if (rc == NGX_OK) { break; } - if (rc == Z_STREAM_END) { - - ctx->zin = ctx->zstream.total_in; - ctx->zout = 10 + ctx->zstream.total_out + 8; - - rc = deflateEnd(&ctx->zstream); - - if (rc != Z_OK) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "deflateEnd() failed: %d", rc); - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - ngx_pfree(r->pool, ctx->preallocated); - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - cl->buf = ctx->out_buf; - cl->next = NULL; - *ctx->last_out = cl; - ctx->last_out = &cl->next; - - if (ctx->zstream.avail_out >= 8) { - trailer = (struct gztrailer *) ctx->out_buf->last; - ctx->out_buf->last += 8; - ctx->out_buf->last_buf = 1; - - } else { - b = ngx_create_temp_buf(r->pool, 8); - if (b == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - b->last_buf = 1; - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - cl->buf = b; - cl->next = NULL; - *ctx->last_out = cl; - ctx->last_out = &cl->next; - trailer = (struct gztrailer *) b->pos; - b->last += 8; - } - -#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) - - trailer->crc32 = ctx->crc32; - trailer->zlen = ctx->zin; - -#else - trailer->crc32[0] = (u_char) (ctx->crc32 & 0xff); - trailer->crc32[1] = (u_char) ((ctx->crc32 >> 8) & 0xff); - trailer->crc32[2] = (u_char) ((ctx->crc32 >> 16) & 0xff); - trailer->crc32[3] = (u_char) ((ctx->crc32 >> 24) & 0xff); - - trailer->zlen[0] = (u_char) (ctx->zin & 0xff); - trailer->zlen[1] = (u_char) ((ctx->zin >> 8) & 0xff); - trailer->zlen[2] = (u_char) ((ctx->zin >> 16) & 0xff); - trailer->zlen[3] = (u_char) ((ctx->zin >> 24) & 0xff); -#endif - - ctx->zstream.avail_in = 0; - ctx->zstream.avail_out = 0; - - ctx->done = 1; - - r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED; - - break; + if (rc == NGX_ERROR) { + goto failed; } - if (conf->no_buffer && ctx->in == NULL) { + /* rc == NGX_AGAIN */ + } + + if (ctx->out == NULL) { + ngx_http_gzip_filter_free_copy_buf(r, ctx); - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } + return ctx->busy ? NGX_AGAIN : NGX_OK; + } - cl->buf = ctx->out_buf; - cl->next = NULL; - *ctx->last_out = cl; - ctx->last_out = &cl->next; - - break; + if (!ctx->gzheader) { + if (ngx_http_gzip_filter_gzheader(r, ctx) != NGX_OK) { + goto failed; } } - if (ctx->out == NULL) { + rc = ngx_http_next_body_filter(r, ctx->out); - if (last == NGX_AGAIN) { - return NGX_AGAIN; - } - - if (ctx->busy == NULL) { - return NGX_OK; - } + if (rc == NGX_ERROR) { + goto failed; } - last = ngx_http_next_body_filter(r, ctx->out); - - /* - * we do not check NGX_AGAIN here because the downstream filters - * may free some buffers and zlib may compress some data into them - */ - - if (last == NGX_ERROR) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } + ngx_http_gzip_filter_free_copy_buf(r, ctx); ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out, (ngx_buf_tag_t) &ngx_http_gzip_filter_module); ctx->last_out = &ctx->out; + ctx->nomem = 0; + if (ctx->done) { - return last; + return rc; + } + } + + /* unreachable */ + +failed: + + ctx->done = 1; + + if (ctx->preallocated) { + deflateEnd(&ctx->zstream); + + ngx_pfree(r->pool, ctx->preallocated); + } + + ngx_http_gzip_filter_free_copy_buf(r, ctx); + + return NGX_ERROR; +} + + +static void +ngx_http_gzip_filter_memory(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) +{ + int wbits, memlevel; + ngx_http_gzip_conf_t *conf; + + conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); + + wbits = conf->wbits; + memlevel = conf->memlevel; + + if (r->headers_out.content_length_n > 0) { + + /* the actual zlib window size is smaller by 262 bytes */ + + while (r->headers_out.content_length_n < ((1 << (wbits - 1)) - 262)) { + wbits--; + memlevel--; } } + + ctx->wbits = wbits; + ctx->memlevel = memlevel; + + /* + * We preallocate a memory for zlib in one buffer (200K-400K), this + * decreases a number of malloc() and free() calls and also probably + * decreases a number of syscalls (sbrk()/mmap() and so on). + * Besides we free the memory as soon as a gzipping will complete + * and do not wait while a whole response will be sent to a client. + * + * 8K is for zlib deflate_state, it takes + * *) 5816 bytes on i386 and sparc64 (32-bit mode) + * *) 5920 bytes on amd64 and sparc64 + */ + + ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9)); +} + + +static ngx_int_t +ngx_http_gzip_filter_buffer(ngx_http_gzip_ctx_t *ctx, ngx_chain_t *in) +{ + size_t size, buffered; + ngx_buf_t *b, *buf; + ngx_chain_t *cl, **ll; + ngx_http_request_t *r; + ngx_http_gzip_conf_t *conf; + + r = ctx->request; + + r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED; + + buffered = 0; + ll = &ctx->in; + + for (cl = ctx->in; cl; cl = cl->next) { + buffered += cl->buf->last - cl->buf->pos; + ll = &cl->next; + } + + conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); + + while (in) { + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + b = in->buf; + + size = b->last - b->pos; + buffered += size; + + if (b->flush || b->last_buf || buffered > conf->postpone_gzipping) { + ctx->buffering = 0; + } + + if (ctx->buffering && size) { + + buf = ngx_create_temp_buf(r->pool, size); + if (buf == NULL) { + return NGX_ERROR; + } + + buf->last = ngx_cpymem(buf->pos, b->pos, size); + b->pos = b->last; + + buf->last_buf = b->last_buf; + buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module; + + cl->buf = buf; + + } else { + cl->buf = b; + } + + *ll = cl; + ll = &cl->next; + in = in->next; + } + + *ll = NULL; + + return ctx->buffering ? NGX_OK : NGX_DONE; +} + + +static ngx_int_t +ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx) +{ + int rc; + ngx_http_gzip_conf_t *conf; + + conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); + + ctx->preallocated = ngx_palloc(r->pool, ctx->allocated); + if (ctx->preallocated == NULL) { + return NGX_ERROR; + } + + ctx->free_mem = ctx->preallocated; + + ctx->zstream.zalloc = ngx_http_gzip_filter_alloc; + ctx->zstream.zfree = ngx_http_gzip_filter_free; + ctx->zstream.opaque = ctx; + + rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED, + - ctx->wbits, ctx->memlevel, Z_DEFAULT_STRATEGY); + + if (rc != Z_OK) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "deflateInit2() failed: %d", rc); + return NGX_ERROR; + } + + ctx->last_out = &ctx->out; + ctx->crc32 = crc32(0L, Z_NULL, 0); + ctx->flush = Z_NO_FLUSH; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_gzip_filter_gzheader(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) +{ + ngx_buf_t *b; + ngx_chain_t *cl; + static u_char gzheader[10] = + { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 }; + + b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); + if (b == NULL) { + return NGX_ERROR; + } + + b->memory = 1; + b->pos = gzheader; + b->last = b->pos + 10; + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + cl->next = ctx->out; + ctx->out = cl; + + ctx->gzheader = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_gzip_filter_add_data(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) +{ + if (ctx->zstream.avail_in || ctx->flush != Z_NO_FLUSH || ctx->redo) { + return NGX_OK; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "gzip in: %p", ctx->in); + + if (ctx->in == NULL) { + return NGX_DECLINED; + } + + if (ctx->copy_buf) { + + /* + * to avoid CPU cache trashing we do not free() just quit buf, + * but postpone free()ing after zlib compressing and data output + */ + + ctx->copy_buf->next = ctx->copied; + ctx->copied = ctx->copy_buf; + ctx->copy_buf = NULL; + } + + ctx->in_buf = ctx->in->buf; + + if (ctx->in_buf->tag == (ngx_buf_tag_t) &ngx_http_gzip_filter_module) { + ctx->copy_buf = ctx->in; + } + + ctx->in = ctx->in->next; + + ctx->zstream.next_in = ctx->in_buf->pos; + ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "gzip in_buf:%p ni:%p ai:%ud", + ctx->in_buf, + ctx->zstream.next_in, ctx->zstream.avail_in); + + if (ctx->in_buf->last_buf) { + ctx->flush = Z_FINISH; + + } else if (ctx->in_buf->flush) { + ctx->flush = Z_SYNC_FLUSH; + } + + if (ctx->zstream.avail_in) { + + ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in, + ctx->zstream.avail_in); + + } else if (ctx->flush == Z_NO_FLUSH) { + return NGX_AGAIN; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_gzip_filter_get_buf(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) +{ + ngx_http_gzip_conf_t *conf; + + if (ctx->zstream.avail_out) { + return NGX_OK; + } + + conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); + + if (ctx->free) { + ctx->out_buf = ctx->free->buf; + ctx->free = ctx->free->next; + + } else if (ctx->bufs < conf->bufs.num) { + + ctx->out_buf = ngx_create_temp_buf(r->pool, conf->bufs.size); + if (ctx->out_buf == NULL) { + return NGX_ERROR; + } + + ctx->out_buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module; + ctx->out_buf->recycled = 1; + ctx->bufs++; + + } else { + ctx->nomem = 1; + return NGX_DECLINED; + } + + ctx->zstream.next_out = ctx->out_buf->pos; + ctx->zstream.avail_out = conf->bufs.size; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_gzip_filter_deflate(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) +{ + int rc; + ngx_chain_t *cl; + ngx_http_gzip_conf_t *conf; + + ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d", + ctx->zstream.next_in, ctx->zstream.next_out, + ctx->zstream.avail_in, ctx->zstream.avail_out, + ctx->flush, ctx->redo); + + rc = deflate(&ctx->zstream, ctx->flush); + + if (rc != Z_OK && rc != Z_STREAM_END) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "deflate() failed: %d, %d", ctx->flush, rc); + return NGX_ERROR; + } + + ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", + ctx->zstream.next_in, ctx->zstream.next_out, + ctx->zstream.avail_in, ctx->zstream.avail_out, + rc); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "gzip in_buf:%p pos:%p", + ctx->in_buf, ctx->in_buf->pos); + + if (ctx->zstream.next_in) { + ctx->in_buf->pos = ctx->zstream.next_in; + + if (ctx->zstream.avail_in == 0) { + ctx->zstream.next_in = NULL; + } + } + + ctx->out_buf->last = ctx->zstream.next_out; + + if (ctx->zstream.avail_out == 0) { + + /* zlib wants to output some more gzipped data */ + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = ctx->out_buf; + cl->next = NULL; + *ctx->last_out = cl; + ctx->last_out = &cl->next; + + ctx->redo = 1; + + return NGX_AGAIN; + } + + ctx->redo = 0; + + if (ctx->flush == Z_SYNC_FLUSH) { + + ctx->zstream.avail_out = 0; + ctx->out_buf->flush = 1; + ctx->flush = Z_NO_FLUSH; + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = ctx->out_buf; + cl->next = NULL; + *ctx->last_out = cl; + ctx->last_out = &cl->next; + + return NGX_OK; + } + + if (rc == Z_STREAM_END) { + + if (ngx_http_gzip_filter_deflate_end(r, ctx) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; + } + + conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); + + if (conf->no_buffer && ctx->in == NULL) { + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = ctx->out_buf; + cl->next = NULL; + *ctx->last_out = cl; + ctx->last_out = &cl->next; + + return NGX_OK; + } + + return NGX_AGAIN; +} + + +static ngx_int_t +ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx) +{ + int rc; + ngx_buf_t *b; + ngx_chain_t *cl; + struct gztrailer *trailer; + + ctx->zin = ctx->zstream.total_in; + ctx->zout = 10 + ctx->zstream.total_out + 8; + + rc = deflateEnd(&ctx->zstream); + + if (rc != Z_OK) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "deflateEnd() failed: %d", rc); + return NGX_ERROR; + } + + ngx_pfree(r->pool, ctx->preallocated); + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = ctx->out_buf; + cl->next = NULL; + *ctx->last_out = cl; + ctx->last_out = &cl->next; + + if (ctx->zstream.avail_out >= 8) { + trailer = (struct gztrailer *) ctx->out_buf->last; + ctx->out_buf->last += 8; + ctx->out_buf->last_buf = 1; + + } else { + b = ngx_create_temp_buf(r->pool, 8); + if (b == NULL) { + return NGX_ERROR; + } + + b->last_buf = 1; + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + cl->next = NULL; + *ctx->last_out = cl; + ctx->last_out = &cl->next; + trailer = (struct gztrailer *) b->pos; + b->last += 8; + } + +#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) + + trailer->crc32 = ctx->crc32; + trailer->zlen = ctx->zin; + +#else + + trailer->crc32[0] = (u_char) (ctx->crc32 & 0xff); + trailer->crc32[1] = (u_char) ((ctx->crc32 >> 8) & 0xff); + trailer->crc32[2] = (u_char) ((ctx->crc32 >> 16) & 0xff); + trailer->crc32[3] = (u_char) ((ctx->crc32 >> 24) & 0xff); + + trailer->zlen[0] = (u_char) (ctx->zin & 0xff); + trailer->zlen[1] = (u_char) ((ctx->zin >> 8) & 0xff); + trailer->zlen[2] = (u_char) ((ctx->zin >> 16) & 0xff); + trailer->zlen[3] = (u_char) ((ctx->zin >> 24) & 0xff); + +#endif + + ctx->zstream.avail_in = 0; + ctx->zstream.avail_out = 0; + + ctx->done = 1; + + r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED; + + return NGX_OK; } @@ -674,14 +944,14 @@ ngx_http_gzip_filter_alloc(void *opaque, alloc = items * size; - if (alloc % 512 != 0) { + if (alloc % 512 != 0 && alloc < 8192) { /* * The zlib deflate_state allocation, it takes about 6K, * we allocate 8K. Other allocations are divisible by 512. */ - alloc = (alloc + ngx_pagesize - 1) & ~(ngx_pagesize - 1); + alloc = 8192; } if (alloc <= ctx->allocated) { @@ -719,20 +989,16 @@ ngx_http_gzip_filter_free(void *opaque, static void -ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx) +ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx) { - deflateEnd(&ctx->zstream); + ngx_chain_t *cl; - if (ctx->preallocated) { - ngx_pfree(ctx->request->pool, ctx->preallocated); + for (cl = ctx->copied; cl; cl = cl->next) { + ngx_pfree(r->pool, cl->buf->start); } - ctx->zstream.avail_in = 0; - ctx->zstream.avail_out = 0; - - ctx->done = 1; - - return; + ctx->copied = NULL; } @@ -803,7 +1069,7 @@ ngx_http_gzip_create_conf(ngx_conf_t *cf conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gzip_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* @@ -817,9 +1083,10 @@ ngx_http_gzip_create_conf(ngx_conf_t *cf conf->enable = NGX_CONF_UNSET; conf->no_buffer = NGX_CONF_UNSET; + conf->postpone_gzipping = NGX_CONF_UNSET_SIZE; conf->level = NGX_CONF_UNSET; - conf->wbits = (size_t) NGX_CONF_UNSET; - conf->memlevel = (size_t) NGX_CONF_UNSET; + conf->wbits = NGX_CONF_UNSET_SIZE; + conf->memlevel = NGX_CONF_UNSET_SIZE; conf->min_length = NGX_CONF_UNSET; return conf; @@ -833,15 +1100,18 @@ ngx_http_gzip_merge_conf(ngx_conf_t *cf, ngx_http_gzip_conf_t *conf = child; ngx_conf_merge_value(conf->enable, prev->enable, 0); + ngx_conf_merge_value(conf->no_buffer, prev->no_buffer, 0); - ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 4, ngx_pagesize); + ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, + (128 * 1024) / ngx_pagesize, ngx_pagesize); + ngx_conf_merge_size_value(conf->postpone_gzipping, prev->postpone_gzipping, + 0); ngx_conf_merge_value(conf->level, prev->level, 1); ngx_conf_merge_size_value(conf->wbits, prev->wbits, MAX_WBITS); ngx_conf_merge_size_value(conf->memlevel, prev->memlevel, MAX_MEM_LEVEL - 1); ngx_conf_merge_value(conf->min_length, prev->min_length, 20); - ngx_conf_merge_value(conf->no_buffer, prev->no_buffer, 0); if (ngx_http_merge_types(cf, conf->types_keys, &conf->types, prev->types_keys, &prev->types, @@ -871,9 +1141,9 @@ ngx_http_gzip_filter_init(ngx_conf_t *cf static char * ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data) { - int *np = data; + size_t *np = data; - int wbits, wsize; + size_t wbits, wsize; wbits = 15; @@ -895,9 +1165,9 @@ ngx_http_gzip_window(ngx_conf_t *cf, voi static char * ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data) { - int *np = data; + size_t *np = data; - int memlevel, hsize; + size_t memlevel, hsize; memlevel = 9; diff --git a/src/http/modules/ngx_http_gzip_static_module.c b/src/http/modules/ngx_http_gzip_static_module.c --- a/src/http/modules/ngx_http_gzip_static_module.c +++ b/src/http/modules/ngx_http_gzip_static_module.c @@ -89,7 +89,6 @@ ngx_http_gzip_static_handler(ngx_http_re return NGX_DECLINED; } - /* TODO: Win32 */ if (r->zero_in_uri) { return NGX_DECLINED; } @@ -153,7 +152,7 @@ ngx_http_gzip_static_handler(ngx_http_re } ngx_log_error(level, log, of.err, - ngx_open_file_n " \"%s\" failed", path.data); + "%s \"%s\" failed", of.failed, path.data); return NGX_DECLINED; } @@ -176,7 +175,7 @@ ngx_http_gzip_static_handler(ngx_http_re #endif - r->root_tested = 1; + r->root_tested = !r->error_page; rc = ngx_http_discard_request_body(r); @@ -206,6 +205,7 @@ ngx_http_gzip_static_handler(ngx_http_re h->value.data = (u_char *) "gzip"; r->headers_out.content_encoding = h; + r->ignore_content_encoding = 1; /* we need to allocate all before the header would be sent */ @@ -251,7 +251,7 @@ ngx_http_gzip_static_create_conf(ngx_con conf = ngx_palloc(cf->pool, sizeof(ngx_http_gzip_static_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } conf->enable = NGX_CONF_UNSET; diff --git a/src/http/modules/ngx_http_headers_filter_module.c b/src/http/modules/ngx_http_headers_filter_module.c --- a/src/http/modules/ngx_http_headers_filter_module.c +++ b/src/http/modules/ngx_http_headers_filter_module.c @@ -16,18 +16,18 @@ typedef ngx_int_t (*ngx_http_set_header_ typedef struct { - ngx_str_t name; - ngx_uint_t offset; - ngx_http_set_header_pt handler; + ngx_str_t name; + ngx_uint_t offset; + ngx_http_set_header_pt handler; } ngx_http_set_header_t; struct ngx_http_header_val_s { - ngx_table_elt_t value; - ngx_uint_t offset; - ngx_http_set_header_pt handler; - ngx_array_t *lengths; - ngx_array_t *values; + ngx_http_complex_value_t value; + ngx_uint_t hash; + ngx_str_t key; + ngx_http_set_header_pt handler; + ngx_uint_t offset; }; @@ -162,16 +162,8 @@ ngx_http_headers_filter(ngx_http_request h = conf->headers->elts; for (i = 0; i < conf->headers->nelts; i++) { - if (h[i].lengths == NULL) { - value = h[i].value.value; - - } else { - if (ngx_http_script_run(r, &value, h[i].lengths->elts, 0, - h[i].values->elts) - == NULL) - { - return NGX_ERROR; - } + if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) { + return NGX_ERROR; } if (h[i].handler(r, &h[i], &value) != NGX_OK) { @@ -325,15 +317,17 @@ ngx_http_add_header(ngx_http_request_t * { ngx_table_elt_t *h; - h = ngx_list_push(&r->headers_out.headers); - if (h == NULL) { - return NGX_ERROR; + if (value->len) { + h = ngx_list_push(&r->headers_out.headers); + if (h == NULL) { + return NGX_ERROR; + } + + h->hash = hv->hash; + h->key = hv->key; + h->value = *value; } - h->hash = hv->value.hash; - h->key = hv->value.key; - h->value = *value; - return NGX_OK; } @@ -412,8 +406,8 @@ ngx_http_set_last_modified(ngx_http_requ } } - h->hash = hv->value.hash; - h->key = hv->value.key; + h->hash = hv->hash; + h->key = hv->key; h->value = *value; return NGX_OK; @@ -427,7 +421,7 @@ ngx_http_headers_create_conf(ngx_conf_t conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_headers_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* @@ -576,12 +570,11 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx { ngx_http_headers_conf_t *hcf = conf; - ngx_int_t n; - ngx_str_t *value; - ngx_uint_t i; - ngx_http_header_val_t *h; - ngx_http_set_header_t *sh; - ngx_http_script_compile_t sc; + ngx_str_t *value; + ngx_uint_t i; + ngx_http_header_val_t *hv; + ngx_http_set_header_t *set; + ngx_http_compile_complex_value_t ccv; value = cf->args->elts; @@ -593,47 +586,40 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx } } - h = ngx_array_push(hcf->headers); - if (h == NULL) { + hv = ngx_array_push(hcf->headers); + if (hv == NULL) { return NGX_CONF_ERROR; } - h->value.hash = 1; - h->value.key = value[1]; - h->value.value = value[2]; - h->offset = 0; - h->handler = ngx_http_add_header; - h->lengths = NULL; - h->values = NULL; + hv->hash = 1; + hv->key = value[1]; + hv->handler = ngx_http_add_header; + hv->offset = 0; - sh = ngx_http_set_headers; - for (i = 0; sh[i].name.len; i++) { - if (ngx_strcasecmp(value[1].data, sh[i].name.data) != 0) { + set = ngx_http_set_headers; + for (i = 0; set[i].name.len; i++) { + if (ngx_strcasecmp(value[1].data, set[i].name.data) != 0) { continue; } - h->offset = sh[i].offset; - h->handler = sh[i].handler; + hv->offset = set[i].offset; + hv->handler = set[i].handler; + break; } - n = ngx_http_script_variables_count(&value[2]); - - if (n == 0) { + if (value[2].len == 0) { + ngx_memzero(&hv->value, sizeof(ngx_http_complex_value_t)); return NGX_CONF_OK; } - ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); - sc.cf = cf; - sc.source = &value[2]; - sc.lengths = &h->lengths; - sc.values = &h->values; - sc.variables = n; - sc.complete_lengths = 1; - sc.complete_values = 1; + ccv.cf = cf; + ccv.value = &value[2]; + ccv.complex_value = &hv->value; - if (ngx_http_script_compile(&sc) != NGX_OK) { + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } diff --git a/src/http/modules/ngx_http_image_filter_module.c b/src/http/modules/ngx_http_image_filter_module.c new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_image_filter_module.c @@ -0,0 +1,1079 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + +#include + + +#define NGX_HTTP_IMAGE_OFF 0 +#define NGX_HTTP_IMAGE_TEST 1 +#define NGX_HTTP_IMAGE_SIZE 2 +#define NGX_HTTP_IMAGE_RESIZE 3 +#define NGX_HTTP_IMAGE_CROP 4 + + +#define NGX_HTTP_IMAGE_START 0 +#define NGX_HTTP_IMAGE_READ 1 +#define NGX_HTTP_IMAGE_PROCESS 2 +#define NGX_HTTP_IMAGE_PASS 3 +#define NGX_HTTP_IMAGE_DONE 4 + + +#define NGX_HTTP_IMAGE_NONE 0 +#define NGX_HTTP_IMAGE_JPEG 1 +#define NGX_HTTP_IMAGE_GIF 2 +#define NGX_HTTP_IMAGE_PNG 3 + + +#define NGX_HTTP_IMAGE_BUFFERED 0x08 + + +typedef struct { + ngx_uint_t filter; + ngx_uint_t width; + ngx_uint_t height; + ngx_int_t jpeg_quality; + + size_t buffer_size; +} ngx_http_image_filter_conf_t; + + +typedef struct { + u_char *image; + u_char *last; + + size_t length; + + ngx_uint_t width; + ngx_uint_t height; + + ngx_uint_t phase; + ngx_uint_t type; +} ngx_http_image_filter_ctx_t; + + +static ngx_int_t ngx_http_image_send(ngx_http_request_t *r, + ngx_http_image_filter_ctx_t *ctx, ngx_chain_t *in); +static ngx_uint_t ngx_http_image_test(ngx_http_request_t *r, ngx_chain_t *in); +static ngx_int_t ngx_http_image_read(ngx_http_request_t *r, ngx_chain_t *in); +static ngx_buf_t *ngx_http_image_process(ngx_http_request_t *r); +static ngx_buf_t *ngx_http_image_json(ngx_http_request_t *r, + ngx_http_image_filter_ctx_t *ctx); +static ngx_buf_t *ngx_http_image_asis(ngx_http_request_t *r, + ngx_http_image_filter_ctx_t *ctx); +static void ngx_http_image_length(ngx_http_request_t *r, ngx_buf_t *b); +static ngx_int_t ngx_http_image_size(ngx_http_request_t *r, + ngx_http_image_filter_ctx_t *ctx); + +static ngx_buf_t *ngx_http_image_resize(ngx_http_request_t *r, + ngx_http_image_filter_ctx_t *ctx); +static gdImagePtr ngx_http_image_source(ngx_http_request_t *r, + ngx_http_image_filter_ctx_t *ctx); +static gdImagePtr ngx_http_image_new(ngx_http_request_t *r, int w, int h, + int colors); +static u_char *ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type, + gdImagePtr img, int *size); +static void ngx_http_image_cleanup(void *data); + + +static void *ngx_http_image_filter_create_conf(ngx_conf_t *cf); +static char *ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent, + void *child); +static char *ngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static ngx_int_t ngx_http_image_filter_init(ngx_conf_t *cf); + + +static ngx_command_t ngx_http_image_filter_commands[] = { + + { ngx_string("image_filter"), + NGX_HTTP_LOC_CONF|NGX_CONF_TAKE13, + ngx_http_image_filter, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("image_filter_jpeg_quality"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_image_filter_conf_t, jpeg_quality), + NULL }, + + { ngx_string("image_filter_buffer"), + 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_image_filter_conf_t, buffer_size), + NULL }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_image_filter_module_ctx = { + NULL, /* preconfiguration */ + ngx_http_image_filter_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_image_filter_create_conf, /* create location configuration */ + ngx_http_image_filter_merge_conf /* merge location configuration */ +}; + + +ngx_module_t ngx_http_image_filter_module = { + NGX_MODULE_V1, + &ngx_http_image_filter_module_ctx, /* module context */ + ngx_http_image_filter_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_output_header_filter_pt ngx_http_next_header_filter; +static ngx_http_output_body_filter_pt ngx_http_next_body_filter; + + +static ngx_str_t ngx_http_image_types[] = { + ngx_string("image/jpeg"), + ngx_string("image/gif"), + ngx_string("image/png") +}; + + +static ngx_int_t +ngx_http_image_header_filter(ngx_http_request_t *r) +{ + off_t len; + ngx_http_image_filter_ctx_t *ctx; + ngx_http_image_filter_conf_t *conf; + + if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) { + return ngx_http_next_header_filter(r); + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module); + + if (ctx) { + ngx_http_set_ctx(r, NULL, ngx_http_image_filter_module); + return ngx_http_next_header_filter(r); + } + + conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module); + + if (conf->filter == NGX_HTTP_IMAGE_OFF) { + return ngx_http_next_header_filter(r); + } + + if (r->headers_out.content_type.len + >= sizeof("multipart/x-mixed-replace") - 1 + && ngx_strncasecmp(r->headers_out.content_type.data, + (u_char *) "multipart/x-mixed-replace", + sizeof("multipart/x-mixed-replace") - 1) + == 0) + { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "image filter: multipart/x-mixed-replace response"); + + return NGX_ERROR; + } + + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_image_filter_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_http_set_ctx(r, ctx, ngx_http_image_filter_module); + + len = r->headers_out.content_length_n; + + if (len != -1 && len > (off_t) conf->buffer_size) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "image filter: too big response: %O", len); + + return NGX_ERROR; + } + + if (len == -1) { + ctx->length = conf->buffer_size; + + } else { + ctx->length = (size_t) len; + } + + if (r->headers_out.refresh) { + r->headers_out.refresh->hash = 0; + } + + r->main_filter_need_in_memory = 1; + r->allow_ranges = 0; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_image_body_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + ngx_int_t rc; + ngx_str_t *ct; + ngx_chain_t out; + ngx_http_image_filter_ctx_t *ctx; + ngx_http_image_filter_conf_t *conf; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "image filter"); + + if (in == NULL) { + return ngx_http_next_body_filter(r, in); + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module); + + if (ctx == NULL) { + return ngx_http_next_body_filter(r, in); + } + + switch (ctx->phase) { + + case NGX_HTTP_IMAGE_START: + + ctx->type = ngx_http_image_test(r, in); + + conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module); + + if (ctx->type == NGX_HTTP_IMAGE_NONE) { + + if (conf->filter == NGX_HTTP_IMAGE_SIZE) { + out.buf = ngx_http_image_json(r, NULL); + + if (out.buf) { + out.next = NULL; + ctx->phase = NGX_HTTP_IMAGE_DONE; + + return ngx_http_image_send(r, ctx, &out); + } + } + + return ngx_http_filter_finalize_request(r, + &ngx_http_image_filter_module, + NGX_HTTP_UNSUPPORTED_MEDIA_TYPE); + } + + /* override content type */ + + ct = &ngx_http_image_types[ctx->type - 1]; + r->headers_out.content_type_len = ct->len; + r->headers_out.content_type = *ct; + r->headers_out.content_type_lowcase = NULL; + + if (conf->filter == NGX_HTTP_IMAGE_TEST) { + ctx->phase = NGX_HTTP_IMAGE_PASS; + + return ngx_http_image_send(r, ctx, in); + } + + ctx->phase = NGX_HTTP_IMAGE_READ; + + /* fall through */ + + case NGX_HTTP_IMAGE_READ: + + rc = ngx_http_image_read(r, in); + + if (rc == NGX_AGAIN) { + return NGX_OK; + } + + if (rc == NGX_ERROR) { + return ngx_http_filter_finalize_request(r, + &ngx_http_image_filter_module, + NGX_HTTP_UNSUPPORTED_MEDIA_TYPE); + } + + /* fall through */ + + case NGX_HTTP_IMAGE_PROCESS: + + out.buf = ngx_http_image_process(r); + + if (out.buf == NULL) { + return ngx_http_filter_finalize_request(r, + &ngx_http_image_filter_module, + NGX_HTTP_UNSUPPORTED_MEDIA_TYPE); + } + + out.next = NULL; + ctx->phase = NGX_HTTP_IMAGE_PASS; + + return ngx_http_image_send(r, ctx, &out); + + case NGX_HTTP_IMAGE_PASS: + + return ngx_http_next_body_filter(r, in); + + default: /* NGX_HTTP_IMAGE_DONE */ + + rc = ngx_http_next_body_filter(r, NULL); + + /* NGX_ERROR resets any pending data */ + return (rc == NGX_OK) ? NGX_ERROR : rc; + } +} + + +static ngx_int_t +ngx_http_image_send(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx, + ngx_chain_t *in) +{ + ngx_int_t rc; + + rc = ngx_http_next_header_filter(r); + + if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { + return NGX_ERROR; + } + + rc = ngx_http_next_body_filter(r, in); + + if (ctx->phase == NGX_HTTP_IMAGE_DONE) { + /* NGX_ERROR resets any pending data */ + return (rc == NGX_OK) ? NGX_ERROR : rc; + } + + return rc; +} + + +static ngx_uint_t +ngx_http_image_test(ngx_http_request_t *r, ngx_chain_t *in) +{ + u_char *p; + + p = in->buf->pos; + + if (in->buf->last - p < 16) { + return NGX_HTTP_IMAGE_NONE; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "image filter: \"%c%c\"", p[0], p[1]); + + if (p[0] == 0xff && p[1] == 0xd8) { + + /* JPEG */ + + return NGX_HTTP_IMAGE_JPEG; + + } else if (p[0] == 'G' && p[1] == 'I' && p[2] == 'F' && p[3] == '8' + && p[5] == 'a') + { + if (p[4] == '9' || p[4] == '7') { + /* GIF */ + return NGX_HTTP_IMAGE_GIF; + } + + } else if (p[0] == 0x89 && p[1] == 'P' && p[2] == 'N' && p[3] == 'G' + && p[4] == 0x0d && p[5] == 0x0a && p[6] == 0x1a && p[7] == 0x0a) + { + /* PNG */ + + return NGX_HTTP_IMAGE_PNG; + } + + return NGX_HTTP_IMAGE_NONE; +} + + +static ngx_int_t +ngx_http_image_read(ngx_http_request_t *r, ngx_chain_t *in) +{ + u_char *p; + size_t size, rest; + ngx_buf_t *b; + ngx_chain_t *cl; + ngx_http_image_filter_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module); + + if (ctx->image == NULL) { + ctx->image = ngx_palloc(r->pool, ctx->length); + if (ctx->image == NULL) { + return NGX_ERROR; + } + + ctx->last = ctx->image; + } + + p = ctx->last; + + for (cl = in; cl; cl = cl->next) { + + b = cl->buf; + size = b->last - b->pos; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "image buf: %uz", size); + + rest = ctx->image + ctx->length - p; + size = (rest < size) ? rest : size; + + p = ngx_cpymem(p, b->pos, size); + b->pos += size; + + if (b->last_buf) { + ctx->last = p; + return NGX_OK; + } + } + + ctx->last = p; + r->connection->buffered |= NGX_HTTP_IMAGE_BUFFERED; + + return NGX_AGAIN; +} + + +static ngx_buf_t * +ngx_http_image_process(ngx_http_request_t *r) +{ + ngx_buf_t *b; + ngx_int_t rc; + ngx_http_image_filter_ctx_t *ctx; + ngx_http_image_filter_conf_t *conf; + + r->connection->buffered &= ~NGX_HTTP_IMAGE_BUFFERED; + + ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module); + + rc = ngx_http_image_size(r, ctx); + + conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module); + + if (conf->filter == NGX_HTTP_IMAGE_SIZE) { + + b = ngx_http_image_json(r, rc == NGX_OK ? ctx : NULL); + + } else if (rc == NGX_OK + && ctx->width <= conf->width + && ctx->height <= conf->height) + { + b = ngx_http_image_asis(r, ctx); + + } else { + b = ngx_http_image_resize(r, ctx); + } + + return b; +} + + +static ngx_buf_t * +ngx_http_image_json(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx) +{ + size_t len; + ngx_buf_t *b; + + b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); + if (b == NULL) { + return NULL; + } + + b->memory = 1; + b->last_buf = 1; + + ngx_http_clean_header(r); + + r->headers_out.status = NGX_HTTP_OK; + r->headers_out.content_type.len = sizeof("text/plain") - 1; + r->headers_out.content_type.data = (u_char *) "text/plain"; + r->headers_out.content_type_lowcase = NULL; + + if (ctx == NULL) { + b->pos = (u_char *) "{}" CRLF; + b->last = b->pos + sizeof("{}" CRLF) - 1; + + ngx_http_image_length(r, b); + + return b; + } + + len = sizeof("{ \"img\" : " + "{ \"width\": , \"height\": , \"type\": \"jpeg\" } }" CRLF) - 1 + + 2 * NGX_SIZE_T_LEN; + + b->pos = ngx_pnalloc(r->pool, len); + if (b->pos == NULL) { + return NULL; + } + + b->last = ngx_sprintf(b->pos, + "{ \"img\" : " + "{ \"width\": %uz," + " \"height\": %uz," + " \"type\": \"%s\" } }" CRLF, + ctx->width, ctx->height, + ngx_http_image_types[ctx->type - 1].data + 6); + + ngx_http_image_length(r, b); + + return b; +} + + +static ngx_buf_t * +ngx_http_image_asis(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx) +{ + ngx_buf_t *b; + + b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); + if (b == NULL) { + return NULL; + } + + b->pos = ctx->image; + b->last = ctx->last; + b->memory = 1; + b->last_buf = 1; + + ngx_http_image_length(r, b); + + return b; +} + + +static void +ngx_http_image_length(ngx_http_request_t *r, ngx_buf_t *b) +{ + r->headers_out.content_length_n = b->last - b->pos; + + if (r->headers_out.content_length) { + r->headers_out.content_length->hash = 0; + } + + r->headers_out.content_length = NULL; +} + + +static ngx_int_t +ngx_http_image_size(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx) +{ + u_char *p, *last; + ngx_uint_t width, height; + + p = ctx->image; + + switch (ctx->type) { + + case NGX_HTTP_IMAGE_JPEG: + + p += 2; + last = ctx->image + ctx->length - 10; + + while (p < last) { + + if (p[0] == 0xff && p[1] != 0xff) { + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "JPEG: %02xd %02xd", *p, *(p + 1)); + + p++; + + if (*p == 0xc0 || *p == 0xc1 || *p == 0xc2 || *p == 0xc3 + || *p == 0xc9 || *p == 0xca || *p == 0xcb) + { + goto found; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "JPEG: %02xd %02xd", p[1], p[2]); + + p += p[1] * 256 + p[2]; + + continue; + } + + p++; + } + + return NGX_DECLINED; + + found: + + width = p[6] * 256 + p[7]; + height = p[4] * 256 + p[5]; + + break; + + case NGX_HTTP_IMAGE_GIF: + + if (ctx->length < 10) { + return NGX_DECLINED; + } + + width = p[7] * 256 + p[6]; + height = p[9] * 256 + p[8]; + + break; + + case NGX_HTTP_IMAGE_PNG: + + if (ctx->length < 24) { + return NGX_DECLINED; + } + + width = p[18] * 256 + p[19]; + height = p[22] * 256 + p[23]; + + break; + + default: + + return NGX_DECLINED; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "image size: %d x %d", width, height); + + ctx->width = width; + ctx->height = height; + + return NGX_OK; +} + + +static ngx_buf_t * +ngx_http_image_resize(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx) +{ + int sx, sy, dx, dy, ox, oy, + colors, transparent, size; + u_char *out; + ngx_buf_t *b; + ngx_uint_t resize; + gdImagePtr src, dst; + ngx_pool_cleanup_t *cln; + ngx_http_image_filter_conf_t *conf; + + src = ngx_http_image_source(r, ctx); + + if (src == NULL) { + return NULL; + } + + sx = gdImageSX(src); + sy = gdImageSY(src); + + conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module); + + if ((ngx_uint_t) sx <= conf->width && (ngx_uint_t) sy <= conf->height) { + gdImageDestroy(src); + return ngx_http_image_asis(r, ctx); + } + + colors = gdImageColorsTotal(src); + transparent = gdImageGetTransparent(src); + + dx = sx; + dy = sy; + + if (conf->filter == NGX_HTTP_IMAGE_RESIZE) { + + if ((ngx_uint_t) dx > conf->width) { + dy = dy * conf->width / dx; + dy = dy ? dy : 1; + dx = conf->width; + } + + if ((ngx_uint_t) dy > conf->height) { + dx = dx * conf->height / dy; + dx = dx ? dx : 1; + dy = conf->height; + } + + resize = 1; + + } else { /* NGX_HTTP_IMAGE_CROP */ + + resize = 0; + + if ((ngx_uint_t) (dx * 100 / dy) < conf->width * 100 / conf->height) { + + if ((ngx_uint_t) dx > conf->width) { + dy = dy * conf->width / dx; + dy = dy ? dy : 1; + dx = conf->width; + resize = 1; + } + + } else { + if ((ngx_uint_t) dy > conf->height) { + dx = dx * conf->height / dy; + dx = dx ? dx : 1; + dy = conf->height; + resize = 1; + } + } + } + + if (resize) { + dst = ngx_http_image_new(r, dx, dy, colors); + if (dst == NULL) { + gdImageDestroy(src); + return NULL; + } + + gdImageCopyResampled(dst, src, 0, 0, 0, 0, dx, dy, sx, sy); + + gdImageDestroy(src); + + } else { + dst = src; + } + + if (conf->filter == NGX_HTTP_IMAGE_CROP) { + + src = dst; + + if ((ngx_uint_t) dx > conf->width) { + ox = dx - conf->width; + + } else { + ox = 0; + } + + if ((ngx_uint_t) dy > conf->height) { + oy = dy - conf->height; + + } else { + oy = 0; + } + + if (ox || oy) { + + dst = ngx_http_image_new(r, dx - ox, dy - oy, colors); + + if (dst == NULL) { + gdImageDestroy(src); + return NULL; + } + + ox /= 2; + oy /= 2; + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "image crop: %d x %d @ %d x %d", + dx, dy, ox, oy); + + gdImageCopy(dst, src, 0, 0, ox, oy, dx - ox, dy - oy); + + gdImageDestroy(src); + } + } + + gdImageColorTransparent(dst, transparent); + + out = ngx_http_image_out(r, ctx->type, dst, &size); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "image: %d x %d %d", sx, sy, colors); + + gdImageDestroy(dst); + ngx_pfree(r->pool, ctx->image); + + if (out == NULL) { + return NULL; + } + + cln = ngx_pool_cleanup_add(r->pool, 0); + if (cln == NULL) { + gdFree(out); + return NULL; + } + + b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); + if (b == NULL) { + gdFree(out); + return NULL; + } + + cln->handler = ngx_http_image_cleanup; + cln->data = out; + + b->pos = out; + b->last = out + size; + b->memory = 1; + b->last_buf = 1; + + ngx_http_image_length(r, b); + + return b; +} + + +static gdImagePtr +ngx_http_image_source(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx) +{ + char *failed; + gdImagePtr img; + + img = NULL; + + switch (ctx->type) { + + case NGX_HTTP_IMAGE_JPEG: + img = gdImageCreateFromJpegPtr(ctx->length, ctx->image); + failed = "gdImageCreateFromJpegPtr() failed"; + break; + + case NGX_HTTP_IMAGE_GIF: + img = gdImageCreateFromGifPtr(ctx->length, ctx->image); + failed = "gdImageCreateFromGifPtr() failed"; + break; + + case NGX_HTTP_IMAGE_PNG: + img = gdImageCreateFromPngPtr(ctx->length, ctx->image); + failed = "gdImageCreateFromPngPtr() failed"; + break; + + default: + failed = "unknown image type"; + break; + } + + if (img == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, failed); + } + + return img; +} + + +static gdImagePtr +ngx_http_image_new(ngx_http_request_t *r, int w, int h, int colors) +{ + gdImagePtr img; + + if (colors == 0) { + img = gdImageCreateTrueColor(w, h); + + if (img == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "gdImageCreateTrueColor() failed"); + return NULL; + } + + } else { + img = gdImageCreate(w, h); + + if (img == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "gdImageCreate() failed"); + return NULL; + } + } + + return img; +} + + +static u_char * +ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type, gdImagePtr img, + int *size) +{ + char *failed; + u_char *out; + ngx_http_image_filter_conf_t *conf; + + out = NULL; + + switch (type) { + + case NGX_HTTP_IMAGE_JPEG: + conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module); + out = gdImageJpegPtr(img, size, conf->jpeg_quality); + failed = "gdImageJpegPtr() failed"; + break; + + case NGX_HTTP_IMAGE_GIF: + out = gdImageGifPtr(img, size); + failed = "gdImageGifPtr() failed"; + break; + + case NGX_HTTP_IMAGE_PNG: + out = gdImagePngPtr(img, size); + failed = "gdImagePngPtr() failed"; + break; + + default: + failed = "unknown image type"; + break; + } + + if (out == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, failed); + } + + return out; +} + + +static void +ngx_http_image_cleanup(void *data) +{ + gdFree(data); +} + + +static void * +ngx_http_image_filter_create_conf(ngx_conf_t *cf) +{ + ngx_http_image_filter_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_image_filter_conf_t)); + if (conf == NULL) { + return NULL; + } + + conf->filter = NGX_CONF_UNSET_UINT; + conf->jpeg_quality = NGX_CONF_UNSET; + conf->buffer_size = NGX_CONF_UNSET_SIZE; + + return conf; +} + + +static char * +ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_http_image_filter_conf_t *prev = parent; + ngx_http_image_filter_conf_t *conf = child; + + if (conf->filter == NGX_CONF_UNSET_UINT) { + + if (prev->filter == NGX_CONF_UNSET_UINT) { + conf->filter = NGX_HTTP_IMAGE_OFF; + + } else { + conf->filter = prev->filter; + conf->width = prev->width; + conf->height = prev->height; + } + } + + /* 75 is libjpeg default quality */ + ngx_conf_merge_value(conf->jpeg_quality, prev->jpeg_quality, 75); + + ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, + 1 * 1024 * 1024); + + return NGX_CONF_OK; +} + + +static char * +ngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_image_filter_conf_t *imcf = conf; + + ngx_str_t *value; + ngx_int_t n; + ngx_uint_t i; + + value = cf->args->elts; + + i = 1; + + if (cf->args->nelts == 2) { + if (ngx_strcmp(value[i].data, "off") == 0) { + imcf->filter = NGX_HTTP_IMAGE_OFF; + + } else if (ngx_strcmp(value[i].data, "test") == 0) { + imcf->filter = NGX_HTTP_IMAGE_TEST; + + } else if (ngx_strcmp(value[i].data, "size") == 0) { + imcf->filter = NGX_HTTP_IMAGE_SIZE; + + } else { + goto failed; + } + + return NGX_CONF_OK; + } + + if (ngx_strcmp(value[i].data, "resize") == 0) { + imcf->filter = NGX_HTTP_IMAGE_RESIZE; + + } else if (ngx_strcmp(value[i].data, "crop") == 0) { + imcf->filter = NGX_HTTP_IMAGE_CROP; + + } else { + goto failed; + } + + i++; + + if (value[i].len == 1 && value[i].data[0] == '-') { + imcf->width = (ngx_uint_t) -1; + + } else { + n = ngx_atoi(value[i].data, value[i].len); + if (n == NGX_ERROR) { + goto failed; + } + + imcf->width = (ngx_uint_t) n; + } + + i++; + + if (value[i].len == 1 && value[i].data[0] == '-') { + imcf->height = (ngx_uint_t) -1; + + } else { + n = ngx_atoi(value[i].data, value[i].len); + if (n == NGX_ERROR) { + goto failed; + } + + imcf->height = (ngx_uint_t) n; + } + + return NGX_CONF_OK; + +failed: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", + &value[i]); + + return NGX_CONF_ERROR; +} + + +static ngx_int_t +ngx_http_image_filter_init(ngx_conf_t *cf) +{ + ngx_http_next_header_filter = ngx_http_top_header_filter; + ngx_http_top_header_filter = ngx_http_image_header_filter; + + ngx_http_next_body_filter = ngx_http_top_body_filter; + ngx_http_top_body_filter = ngx_http_image_body_filter; + + return NGX_OK; +} diff --git a/src/http/modules/ngx_http_index_module.c b/src/http/modules/ngx_http_index_module.c --- a/src/http/modules/ngx_http_index_module.c +++ b/src/http/modules/ngx_http_index_module.c @@ -83,23 +83,22 @@ ngx_module_t ngx_http_index_module = { /* - * Try to open the first index file before the test of the directory existence - * because the valid requests should be many more than invalid ones. - * If open() would fail, then stat() should be more quickly because some data - * is already cached in the kernel. - * Besides, Win32 has ERROR_PATH_NOT_FOUND (NGX_ENOTDIR). - * Unix has ENOTDIR error, although it less helpfull - it points only - * that path contains the usual file in place of the directory. + * Try to open/test the first index file before the test of directory + * existence because valid requests should be much more than invalid ones. + * If the file open()/stat() would fail, then the directory stat() should + * be more quickly because some data is already cached in the kernel. + * Besides, Win32 may return ERROR_PATH_NOT_FOUND (NGX_ENOTDIR) at once. + * Unix has ENOTDIR error, however, it's less helpful than Win32's one: + * it only indicates that path contains an usual file in place of directory. */ static ngx_int_t ngx_http_index_handler(ngx_http_request_t *r) { u_char *p, *name; - size_t len, nlen, root, allocated; + size_t len, root, reserve, allocated; ngx_int_t rc; ngx_str_t path, uri; - ngx_log_t *log; ngx_uint_t i, dir_tested; ngx_http_index_t *index; ngx_open_file_info_t of; @@ -117,13 +116,10 @@ ngx_http_index_handler(ngx_http_request_ return NGX_DECLINED; } - /* TODO: Win32 */ if (r->zero_in_uri) { return NGX_DECLINED; } - log = r->connection->log; - ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); @@ -131,6 +127,7 @@ ngx_http_index_handler(ngx_http_request_ root = 0; dir_tested = 0; name = NULL; + /* suppress MSVC warning */ path.data = NULL; index = ilcf->indices->elts; @@ -142,8 +139,8 @@ ngx_http_index_handler(ngx_http_request_ return ngx_http_internal_redirect(r, &index[i].name, &r->args); } - len = ilcf->max_index_len; - nlen = index[i].name.len; + reserve = ilcf->max_index_len; + len = index[i].name.len; } else { ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); @@ -152,8 +149,7 @@ ngx_http_index_handler(ngx_http_request_ e.request = r; e.flushed = 1; - /* 1 byte for terminating '\0' */ - + /* 1 is for terminating '\0' as in static names */ len = 1; while (*(uintptr_t *) e.ip) { @@ -161,21 +157,19 @@ ngx_http_index_handler(ngx_http_request_ len += lcode(&e); } - nlen = len; - /* 16 bytes are preallocation */ - len += 16; + reserve = len + 16; } - if (len > (size_t) (path.data + allocated - name)) { + if (reserve > allocated) { - name = ngx_http_map_uri_to_path(r, &path, &root, len); + name = ngx_http_map_uri_to_path(r, &path, &root, reserve); if (name == NULL) { return NGX_ERROR; } - allocated = path.len; + allocated = path.data + path.len - name; } if (index[i].values == NULL) { @@ -196,31 +190,33 @@ ngx_http_index_handler(ngx_http_request_ } if (*name == '/') { - uri.len = nlen - 1; + uri.len = len - 1; uri.data = name; return ngx_http_internal_redirect(r, &uri, &r->args); } path.len = e.pos - path.data; - *e.pos++ = '\0'; + *e.pos = '\0'; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "open index \"%V\"", &path); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "open index \"%V\"", &path); ngx_memzero(&of, sizeof(ngx_open_file_info_t)); of.directio = clcf->directio; of.valid = clcf->open_file_cache_valid; of.min_uses = clcf->open_file_cache_min_uses; + of.test_only = 1; of.errors = clcf->open_file_cache_errors; of.events = clcf->open_file_cache_events; if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, of.err, - ngx_open_file_n " \"%s\" failed", path.data); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err, + "%s \"%s\" failed", of.failed, path.data); if (of.err == 0) { return NGX_HTTP_INTERNAL_SERVER_ERROR; @@ -244,13 +240,13 @@ ngx_http_index_handler(ngx_http_request_ continue; } - ngx_log_error(NGX_LOG_ERR, log, of.err, - ngx_open_file_n " \"%s\" failed", path.data); + ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err, + "%s \"%s\" failed", of.failed, path.data); return NGX_HTTP_INTERNAL_SERVER_ERROR; } - uri.len = r->uri.len + nlen - 1; + uri.len = r->uri.len + len - 1; if (!clcf->alias) { uri.data = path.data + root; @@ -262,7 +258,7 @@ ngx_http_index_handler(ngx_http_request_ } p = ngx_copy(uri.data, r->uri.data, r->uri.len); - ngx_memcpy(p, name, nlen - 1); + ngx_memcpy(p, name, len - 1); } return ngx_http_internal_redirect(r, &uri, &r->args); @@ -296,6 +292,7 @@ ngx_http_index_test_dir(ngx_http_request ngx_memzero(&of, sizeof(ngx_open_file_info_t)); of.test_dir = 1; + of.test_only = 1; of.valid = clcf->open_file_cache_valid; of.errors = clcf->open_file_cache_errors; @@ -323,7 +320,7 @@ ngx_http_index_test_dir(ngx_http_request } ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err, - ngx_open_file_n " \"%s\" failed", dir.data); + "%s \"%s\" failed", of.failed, dir.data); } return NGX_HTTP_INTERNAL_SERVER_ERROR; @@ -369,7 +366,7 @@ ngx_http_index_create_loc_conf(ngx_conf_ conf = ngx_palloc(cf->pool, sizeof(ngx_http_index_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } conf->indices = NULL; @@ -493,7 +490,7 @@ ngx_http_index_set_index(ngx_conf_t *cf, continue; } - /* include the terminating '\0' to the length to use ngx_copy() */ + /* include the terminating '\0' to the length to use ngx_memcpy() */ index->name.len++; continue; diff --git a/src/http/modules/ngx_http_limit_req_module.c b/src/http/modules/ngx_http_limit_req_module.c new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_limit_req_module.c @@ -0,0 +1,795 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +typedef struct { + u_char color; + u_char dummy; + u_short len; + ngx_queue_t queue; + ngx_msec_t last; + /* integer value, 1 corresponds to 0.001 r/s */ + ngx_uint_t excess; + u_char data[1]; +} ngx_http_limit_req_node_t; + + +typedef struct { + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; + ngx_queue_t queue; +} ngx_http_limit_req_shctx_t; + + +typedef struct { + ngx_http_limit_req_shctx_t *sh; + ngx_slab_pool_t *shpool; + /* integer value, 1 corresponds to 0.001 r/s */ + ngx_uint_t rate; + ngx_int_t index; + ngx_str_t var; +} ngx_http_limit_req_ctx_t; + + +typedef struct { + ngx_shm_zone_t *shm_zone; + /* integer value, 1 corresponds to 0.001 r/s */ + ngx_uint_t burst; + ngx_uint_t nodelay;/* unsigned nodelay:1 */ +} ngx_http_limit_req_conf_t; + + +static void ngx_http_limit_req_delay(ngx_http_request_t *r); +static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf, + ngx_uint_t hash, u_char *data, size_t len, ngx_http_limit_req_node_t **lrp); +static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, + ngx_uint_t n); + +static void *ngx_http_limit_req_create_conf(ngx_conf_t *cf); +static char *ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, + void *child); +static char *ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static ngx_int_t ngx_http_limit_req_init(ngx_conf_t *cf); + + +static ngx_command_t ngx_http_limit_req_commands[] = { + + { ngx_string("limit_req_zone"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3, + ngx_http_limit_req_zone, + 0, + 0, + NULL }, + + { ngx_string("limit_req"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123, + ngx_http_limit_req, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_limit_req_module_ctx = { + NULL, /* preconfiguration */ + ngx_http_limit_req_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_limit_req_create_conf, /* create location configration */ + ngx_http_limit_req_merge_conf /* merge location configration */ +}; + + +ngx_module_t ngx_http_limit_req_module = { + NGX_MODULE_V1, + &ngx_http_limit_req_module_ctx, /* module context */ + ngx_http_limit_req_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_int_t +ngx_http_limit_req_handler(ngx_http_request_t *r) +{ + size_t len, n; + uint32_t hash; + ngx_int_t rc; + ngx_uint_t excess; + ngx_time_t *tp; + ngx_rbtree_node_t *node; + ngx_http_variable_value_t *vv; + ngx_http_limit_req_ctx_t *ctx; + ngx_http_limit_req_node_t *lr; + ngx_http_limit_req_conf_t *lrcf; + + if (r->main->limit_req_set) { + return NGX_DECLINED; + } + + lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module); + + if (lrcf->shm_zone == NULL) { + return NGX_DECLINED; + } + + ctx = lrcf->shm_zone->data; + + vv = ngx_http_get_indexed_variable(r, ctx->index); + + if (vv == NULL || vv->not_found) { + return NGX_DECLINED; + } + + len = vv->len; + + if (len == 0) { + return NGX_DECLINED; + } + + if (len > 65535) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "the value of the \"%V\" variable " + "is more than 65535 bytes: \"%v\"", + &ctx->var, vv); + return NGX_DECLINED; + } + + r->main->limit_req_set = 1; + + hash = ngx_crc32_short(vv->data, len); + + ngx_shmtx_lock(&ctx->shpool->mutex); + + ngx_http_limit_req_expire(ctx, 1); + + rc = ngx_http_limit_req_lookup(lrcf, hash, vv->data, len, &lr); + + if (lr) { + ngx_queue_remove(&lr->queue); + + ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); + + excess = lr->excess; + + } else { + excess = 0; + } + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "limit_req: %i %ui.%03ui", rc, excess / 1000, excess % 1000); + + if (rc == NGX_BUSY) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "limiting requests, excess: %ui.%03ui by zone \"%V\"", + excess / 1000, excess % 1000, &lrcf->shm_zone->shm.name); + + return NGX_HTTP_SERVICE_UNAVAILABLE; + } + + if (rc == NGX_AGAIN) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + if (lrcf->nodelay) { + return NGX_DECLINED; + } + + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "delaying request, excess: %ui.%03ui, by zone \"%V\"", + excess / 1000, excess % 1000, &lrcf->shm_zone->shm.name); + + if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + r->read_event_handler = ngx_http_test_reading; + r->write_event_handler = ngx_http_limit_req_delay; + ngx_add_timer(r->connection->write, (ngx_msec_t) excess); + + return NGX_AGAIN; + } + + if (rc == NGX_OK) { + goto done; + } + + /* rc == NGX_DECLINED */ + + n = offsetof(ngx_rbtree_node_t, color) + + offsetof(ngx_http_limit_req_node_t, data) + + len; + + node = ngx_slab_alloc_locked(ctx->shpool, n); + if (node == NULL) { + + ngx_http_limit_req_expire(ctx, 0); + + node = ngx_slab_alloc_locked(ctx->shpool, n); + if (node == NULL) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + return NGX_HTTP_SERVICE_UNAVAILABLE; + } + } + + lr = (ngx_http_limit_req_node_t *) &node->color; + + node->key = hash; + lr->len = (u_char) len; + + tp = ngx_timeofday(); + lr->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec); + + lr->excess = 0; + ngx_memcpy(lr->data, vv->data, len); + + ngx_rbtree_insert(&ctx->sh->rbtree, node); + + ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); + +done: + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return NGX_DECLINED; +} + + +static void +ngx_http_limit_req_delay(ngx_http_request_t *r) +{ + ngx_event_t *wev; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "limit_req delay"); + + wev = r->connection->write; + + if (!wev->timedout) { + + if (ngx_handle_write_event(wev, 0) != NGX_OK) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + return; + } + + wev->timedout = 0; + + if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + r->read_event_handler = ngx_http_block_reading; + r->write_event_handler = ngx_http_core_run_phases; + + ngx_http_core_run_phases(r); +} + + +static void +ngx_http_limit_req_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) +{ + ngx_rbtree_node_t **p; + ngx_http_limit_req_node_t *lrn, *lrnt; + + for ( ;; ) { + + if (node->key < temp->key) { + + p = &temp->left; + + } else if (node->key > temp->key) { + + p = &temp->right; + + } else { /* node->key == temp->key */ + + lrn = (ngx_http_limit_req_node_t *) &node->color; + lrnt = (ngx_http_limit_req_node_t *) &temp->color; + + p = (ngx_memn2cmp(lrn->data, lrnt->data, lrn->len, lrnt->len) < 0) + ? &temp->left : &temp->right; + } + + if (*p == sentinel) { + break; + } + + temp = *p; + } + + *p = node; + node->parent = temp; + node->left = sentinel; + node->right = sentinel; + ngx_rbt_red(node); +} + + +static ngx_int_t +ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf, ngx_uint_t hash, + u_char *data, size_t len, ngx_http_limit_req_node_t **lrp) +{ + ngx_int_t rc, excess; + ngx_time_t *tp; + ngx_msec_t now; + ngx_msec_int_t ms; + ngx_rbtree_node_t *node, *sentinel; + ngx_http_limit_req_ctx_t *ctx; + ngx_http_limit_req_node_t *lr; + + ctx = lrcf->shm_zone->data; + + node = ctx->sh->rbtree.root; + sentinel = ctx->sh->rbtree.sentinel; + + while (node != sentinel) { + + if (hash < node->key) { + node = node->left; + continue; + } + + if (hash > node->key) { + node = node->right; + continue; + } + + /* hash == node->key */ + + do { + lr = (ngx_http_limit_req_node_t *) &node->color; + + rc = ngx_memn2cmp(data, lr->data, len, (size_t) lr->len); + + if (rc == 0) { + + tp = ngx_timeofday(); + + now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); + ms = (ngx_msec_int_t) (now - lr->last); + + excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; + + if (excess < 0) { + excess = 0; + } + + lr->excess = excess; + lr->last = now; + + *lrp = lr; + + if ((ngx_uint_t) excess > lrcf->burst) { + return NGX_BUSY; + } + + if (excess) { + return NGX_AGAIN; + } + + return NGX_OK; + } + + node = (rc < 0) ? node->left : node->right; + + } while (node != sentinel && hash == node->key); + + break; + } + + *lrp = NULL; + + return NGX_DECLINED; +} + + +static void +ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n) +{ + ngx_int_t excess; + ngx_time_t *tp; + ngx_msec_t now; + ngx_queue_t *q; + ngx_msec_int_t ms; + ngx_rbtree_node_t *node; + ngx_http_limit_req_node_t *lr; + + tp = ngx_timeofday(); + + now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); + + /* + * n == 1 deletes one or two zero rate entries + * n == 0 deletes oldest entry by force + * and one or two zero rate entries + */ + + while (n < 3) { + + if (ngx_queue_empty(&ctx->sh->queue)) { + return; + } + + q = ngx_queue_last(&ctx->sh->queue); + + lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue); + + if (n++ != 0) { + + ms = (ngx_msec_int_t) (now - lr->last); + ms = ngx_abs(ms); + + if (ms < 60000) { + return; + } + + excess = lr->excess - ctx->rate * ms / 1000; + + if (excess > 0) { + return; + } + } + + ngx_queue_remove(q); + + node = (ngx_rbtree_node_t *) + ((u_char *) lr - offsetof(ngx_rbtree_node_t, color)); + + ngx_rbtree_delete(&ctx->sh->rbtree, node); + + ngx_slab_free_locked(ctx->shpool, node); + } +} + + +static ngx_int_t +ngx_http_limit_req_init_zone(ngx_shm_zone_t *shm_zone, void *data) +{ + ngx_http_limit_req_ctx_t *octx = data; + + size_t len; + ngx_http_limit_req_ctx_t *ctx; + + ctx = shm_zone->data; + + if (octx) { + if (ngx_strcmp(ctx->var.data, octx->var.data) != 0) { + ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, + "limit_req \"%V\" uses the \"%V\" variable " + "while previously it used the \"%V\" variable", + &shm_zone->shm.name, &ctx->var, &octx->var); + return NGX_ERROR; + } + + ctx->sh = octx->sh; + ctx->shpool = octx->shpool; + + return NGX_OK; + } + + ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + + if (shm_zone->shm.exists) { + ctx->sh = ctx->shpool->data; + + return NGX_OK; + } + + ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_limit_req_shctx_t)); + if (ctx->sh == NULL) { + return NGX_ERROR; + } + + ctx->shpool->data = ctx->sh; + + ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel, + ngx_http_limit_req_rbtree_insert_value); + + ngx_queue_init(&ctx->sh->queue); + + len = sizeof(" in limit_req zone \"\"") + shm_zone->shm.name.len; + + ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len); + if (ctx->shpool->log_ctx == NULL) { + return NGX_ERROR; + } + + ngx_sprintf(ctx->shpool->log_ctx, " in limit_req zone \"%V\"%Z", + &shm_zone->shm.name); + + return NGX_OK; +} + + +static void * +ngx_http_limit_req_create_conf(ngx_conf_t *cf) +{ + ngx_http_limit_req_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_conf_t)); + if (conf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * conf->shm_zone = NULL; + * conf->burst = 0; + * conf->nodelay = 0; + */ + + return conf; +} + + +static char * +ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_http_limit_req_conf_t *prev = parent; + ngx_http_limit_req_conf_t *conf = child; + + if (conf->shm_zone == NULL) { + *conf = *prev; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + u_char *p; + size_t size, len; + ngx_str_t *value, name, s; + ngx_int_t rate, scale; + ngx_uint_t i; + ngx_shm_zone_t *shm_zone; + ngx_http_limit_req_ctx_t *ctx; + + value = cf->args->elts; + + ctx = NULL; + size = 0; + rate = 1; + scale = 1; + name.len = 0; + + for (i = 1; i < cf->args->nelts; i++) { + + if (ngx_strncmp(value[i].data, "zone=", 5) == 0) { + + name.data = value[i].data + 5; + + p = (u_char *) ngx_strchr(name.data, ':'); + + if (p) { + *p = '\0'; + + name.len = p - name.data; + + p++; + + s.len = value[i].data + value[i].len - p; + s.data = p; + + size = ngx_parse_size(&s); + if (size > 8191) { + continue; + } + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid zone size \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (ngx_strncmp(value[i].data, "rate=", 5) == 0) { + + len = value[i].len; + p = value[i].data + len - 3; + + if (ngx_strncmp(p, "r/s", 3) == 0) { + scale = 1; + len -= 3; + + } else if (ngx_strncmp(p, "r/m", 3) == 0) { + scale = 60; + len -= 3; + } + + rate = ngx_atoi(value[i].data + 5, len - 5); + if (rate <= NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid rate \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + + if (value[i].data[0] == '$') { + + value[i].len--; + value[i].data++; + + ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_ctx_t)); + if (ctx == NULL) { + return NGX_CONF_ERROR; + } + + ctx->index = ngx_http_get_variable_index(cf, &value[i]); + if (ctx->index == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + ctx->var = value[i]; + + continue; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (name.len == 0 || size == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%V\" must have \"zone\" parameter", + &cmd->name); + return NGX_CONF_ERROR; + } + + if (ctx == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "no variable is defined for limit_req_zone \"%V\"", + &cmd->name); + return NGX_CONF_ERROR; + } + + ctx->rate = rate * 1000 / scale; + + shm_zone = ngx_shared_memory_add(cf, &name, size, + &ngx_http_limit_req_module); + if (shm_zone == NULL) { + return NGX_CONF_ERROR; + } + + if (shm_zone->data) { + ctx = shm_zone->data; + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "limit_req_zone \"%V\" is already bound to variable \"%V\"", + &value[1], &ctx->var); + return NGX_CONF_ERROR; + } + + shm_zone->init = ngx_http_limit_req_init_zone; + shm_zone->data = ctx; + + return NGX_CONF_OK; +} + + +static char * +ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_limit_req_conf_t *lrcf = conf; + + ngx_int_t burst; + ngx_str_t *value, s; + ngx_uint_t i; + + if (lrcf->shm_zone) { + return "is duplicate"; + } + + value = cf->args->elts; + + burst = 0; + + for (i = 1; i < cf->args->nelts; i++) { + + if (ngx_strncmp(value[i].data, "zone=", 5) == 0) { + + s.len = value[i].len - 5; + s.data = value[i].data + 5; + + lrcf->shm_zone = ngx_shared_memory_add(cf, &s, 0, + &ngx_http_limit_req_module); + if (lrcf->shm_zone == NULL) { + return NGX_CONF_ERROR; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "burst=", 6) == 0) { + + burst = ngx_atoi(value[i].data + 6, value[i].len - 6); + if (burst <= 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid burst rate \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "nodelay", 7) == 0) { + lrcf->nodelay = 1; + continue; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (lrcf->shm_zone == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%V\" must have \"zone\" parameter", + &cmd->name); + return NGX_CONF_ERROR; + } + + if (lrcf->shm_zone->data == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unknown limit_req_zone \"%V\"", + &lrcf->shm_zone->shm.name); + return NGX_CONF_ERROR; + } + + lrcf->burst = burst * 1000; + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_http_limit_req_init(ngx_conf_t *cf) +{ + ngx_http_handler_pt *h; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + + h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_http_limit_req_handler; + + return NGX_OK; +} diff --git a/src/http/modules/ngx_http_limit_zone_module.c b/src/http/modules/ngx_http_limit_zone_module.c --- a/src/http/modules/ngx_http_limit_zone_module.c +++ b/src/http/modules/ngx_http_limit_zone_module.c @@ -189,6 +189,10 @@ ngx_http_limit_zone_handler(ngx_http_req ngx_shmtx_unlock(&shpool->mutex); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "limiting connections by zone \"%V\"", + &lzcf->shm_zone->shm.name); + return NGX_HTTP_SERVICE_UNAVAILABLE; } @@ -312,6 +316,7 @@ ngx_http_limit_zone_init_zone(ngx_shm_zo { ngx_http_limit_zone_ctx_t *octx = data; + size_t len; ngx_slab_pool_t *shpool; ngx_rbtree_node_t *sentinel; ngx_http_limit_zone_ctx_t *ctx; @@ -323,7 +328,7 @@ ngx_http_limit_zone_init_zone(ngx_shm_zo ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, "limit_zone \"%V\" uses the \"%V\" variable " "while previously it used the \"%V\" variable", - &shm_zone->name, &ctx->var, &octx->var); + &shm_zone->shm.name, &ctx->var, &octx->var); return NGX_ERROR; } @@ -334,11 +339,19 @@ ngx_http_limit_zone_init_zone(ngx_shm_zo shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + if (shm_zone->shm.exists) { + ctx->rbtree = shpool->data; + + return NGX_OK; + } + ctx->rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t)); if (ctx->rbtree == NULL) { return NGX_ERROR; } + shpool->data = ctx->rbtree; + sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t)); if (sentinel == NULL) { return NGX_ERROR; @@ -347,6 +360,16 @@ ngx_http_limit_zone_init_zone(ngx_shm_zo ngx_rbtree_init(ctx->rbtree, sentinel, ngx_http_limit_zone_rbtree_insert_value); + len = sizeof(" in limit_zone \"\"") + shm_zone->shm.name.len; + + shpool->log_ctx = ngx_slab_alloc(shpool, len); + if (shpool->log_ctx == NULL) { + return NGX_ERROR; + } + + ngx_sprintf(shpool->log_ctx, " in limit_zone \"%V\"%Z", + &shm_zone->shm.name); + return NGX_OK; } @@ -358,7 +381,7 @@ ngx_http_limit_zone_create_conf(ngx_conf conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_zone_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* @@ -462,6 +485,10 @@ ngx_http_limit_conn(ngx_conf_t *cf, ngx_ ngx_int_t n; ngx_str_t *value; + if (lzcf->shm_zone) { + return "is duplicate"; + } + value = cf->args->elts; lzcf->shm_zone = ngx_shared_memory_add(cf, &value[1], 0, diff --git a/src/http/modules/ngx_http_log_module.c b/src/http/modules/ngx_http_log_module.c --- a/src/http/modules/ngx_http_log_module.c +++ b/src/http/modules/ngx_http_log_module.c @@ -385,6 +385,8 @@ ngx_http_log_script_write(ngx_http_reque of.valid = clcf->open_file_cache_valid; of.min_uses = clcf->open_file_cache_min_uses; + of.test_dir = 1; + of.test_only = 1; of.errors = clcf->open_file_cache_errors; of.events = clcf->open_file_cache_events; @@ -433,13 +435,13 @@ ngx_http_log_script_write(ngx_http_reque of.log = 1; of.valid = llcf->open_file_cache_valid; of.min_uses = llcf->open_file_cache_min_uses; - of.directio = NGX_MAX_OFF_T_VALUE; + of.directio = NGX_OPEN_FILE_DIRECTIO_OFF; if (ngx_open_cached_file(llcf->open_file_cache, &log, &of, r->pool) != NGX_OK) { ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, - ngx_open_file_n " \"%s\" failed", log.data); + "%s \"%s\" failed", of.failed, log.data); /* simulate successfull logging */ return len; } @@ -712,18 +714,18 @@ ngx_http_log_create_main_conf(ngx_conf_t conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_main_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } if (ngx_array_init(&conf->formats, cf->pool, 4, sizeof(ngx_http_log_fmt_t)) != NGX_OK) { - return NGX_CONF_ERROR; + return NULL; } fmt = ngx_array_push(&conf->formats); if (fmt == NULL) { - return NGX_CONF_ERROR; + return NULL; } fmt->name.len = sizeof("combined") - 1; @@ -733,7 +735,7 @@ ngx_http_log_create_main_conf(ngx_conf_t fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_http_log_op_t)); if (fmt->ops == NULL) { - return NGX_CONF_ERROR; + return NULL; } return conf; @@ -747,7 +749,7 @@ ngx_http_log_create_loc_conf(ngx_conf_t conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } conf->open_file_cache = NGX_CONF_UNSET_PTR; @@ -863,7 +865,7 @@ ngx_http_log_set_log(ngx_conf_t *cf, ngx } } else { - if (ngx_conf_full_name(cf->cycle, &value[1], 0) == NGX_ERROR) { + if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { return NGX_CONF_ERROR; } diff --git a/src/http/modules/ngx_http_map_module.c b/src/http/modules/ngx_http_map_module.c --- a/src/http/modules/ngx_http_map_module.c +++ b/src/http/modules/ngx_http_map_module.c @@ -160,7 +160,7 @@ ngx_http_map_create_conf(ngx_conf_t *cf) mcf = ngx_palloc(cf->pool, sizeof(ngx_http_map_conf_t)); if (mcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } mcf->hash_max_size = NGX_CONF_UNSET_UINT; @@ -374,7 +374,7 @@ ngx_http_map(ngx_conf_t *cf, ngx_command if (ngx_strcmp(value[0].data, "include") == 0) { file = value[1]; - if (ngx_conf_full_name(cf->cycle, &file, 1) == NGX_ERROR){ + if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK){ return NGX_CONF_ERROR; } diff --git a/src/http/modules/ngx_http_memcached_module.c b/src/http/modules/ngx_http_memcached_module.c --- a/src/http/modules/ngx_http_memcached_module.c +++ b/src/http/modules/ngx_http_memcached_module.c @@ -183,7 +183,8 @@ ngx_http_memcached_handler(ngx_http_requ return NGX_HTTP_INTERNAL_SERVER_ERROR; } - u->schema = mlcf->upstream.schema; + u->schema.len = sizeof("memcached://") - 1; + u->schema.data = (u_char *) "memcached://"; u->peer.log = r->connection->log; u->peer.log_error = NGX_ERROR_ERR; @@ -512,7 +513,7 @@ ngx_http_memcached_create_loc_conf(ngx_c conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_memcached_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* @@ -521,7 +522,6 @@ ngx_http_memcached_create_loc_conf(ngx_c * conf->upstream.bufs.num = 0; * conf->upstream.next_upstream = 0; * conf->upstream.temp_path = NULL; - * conf->upstream.schema = { 0, NULL }; * conf->upstream.uri = { 0, NULL }; * conf->upstream.location = NULL; */ @@ -584,7 +584,6 @@ ngx_http_memcached_merge_loc_conf(ngx_co if (conf->upstream.upstream == NULL) { conf->upstream.upstream = prev->upstream.upstream; - conf->upstream.schema = prev->upstream.schema; } if (conf->index == NGX_CONF_UNSET) { @@ -598,13 +597,13 @@ ngx_http_memcached_merge_loc_conf(ngx_co static char * ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - ngx_http_memcached_loc_conf_t *lcf = conf; + ngx_http_memcached_loc_conf_t *mlcf = conf; ngx_str_t *value; ngx_url_t u; ngx_http_core_loc_conf_t *clcf; - if (lcf->upstream.schema.len) { + if (mlcf->upstream.upstream) { return "is duplicate"; } @@ -615,14 +614,11 @@ ngx_http_memcached_pass(ngx_conf_t *cf, u.url = value[1]; u.no_resolve = 1; - lcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); - if (lcf->upstream.upstream == NULL) { + mlcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); + if (mlcf->upstream.upstream == NULL) { return NGX_CONF_ERROR; } - lcf->upstream.schema.len = sizeof("memcached://") - 1; - lcf->upstream.schema.data = (u_char *) "memcached://"; - clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); clcf->handler = ngx_http_memcached_handler; @@ -631,9 +627,9 @@ ngx_http_memcached_pass(ngx_conf_t *cf, clcf->auto_redirect = 1; } - lcf->index = ngx_http_get_variable_index(cf, &ngx_http_memcached_key); + mlcf->index = ngx_http_get_variable_index(cf, &ngx_http_memcached_key); - if (lcf->index == NGX_ERROR) { + if (mlcf->index == NGX_ERROR) { return NGX_CONF_ERROR; } diff --git a/src/http/modules/ngx_http_not_modified_filter_module.c b/src/http/modules/ngx_http_not_modified_filter_module.c --- a/src/http/modules/ngx_http_not_modified_filter_module.c +++ b/src/http/modules/ngx_http_not_modified_filter_module.c @@ -47,10 +47,11 @@ ngx_module_t ngx_http_not_modified_filt static ngx_http_output_header_filter_pt ngx_http_next_header_filter; -static -ngx_int_t ngx_http_not_modified_header_filter(ngx_http_request_t *r) +static ngx_int_t +ngx_http_not_modified_header_filter(ngx_http_request_t *r) { - time_t ims; + time_t ims; + ngx_http_core_loc_conf_t *clcf; if (r->headers_out.status != NGX_HTTP_OK || r != r->main @@ -60,29 +61,39 @@ ngx_int_t ngx_http_not_modified_header_f return ngx_http_next_header_filter(r); } + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->if_modified_since == NGX_HTTP_IMS_OFF) { + return ngx_http_next_header_filter(r); + } + ims = ngx_http_parse_time(r->headers_in.if_modified_since->value.data, r->headers_in.if_modified_since->value.len); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http ims:%d lm:%d", ims, r->headers_out.last_modified_time); - /* - * I think that the equality of the dates is correcter - */ + if (ims != r->headers_out.last_modified_time) { - if (ims == r->headers_out.last_modified_time) { - r->headers_out.status = NGX_HTTP_NOT_MODIFIED; - r->headers_out.content_type.len = 0; - ngx_http_clear_content_length(r); - ngx_http_clear_accept_ranges(r); + if (clcf->if_modified_since == NGX_HTTP_IMS_EXACT + || ims < r->headers_out.last_modified_time) + { + return ngx_http_next_header_filter(r); + } } + r->headers_out.status = NGX_HTTP_NOT_MODIFIED; + r->headers_out.status_line.len = 0; + r->headers_out.content_type.len = 0; + ngx_http_clear_content_length(r); + ngx_http_clear_accept_ranges(r); + return ngx_http_next_header_filter(r); } -static -ngx_int_t ngx_http_not_modified_filter_init(ngx_conf_t *cf) +static ngx_int_t +ngx_http_not_modified_filter_init(ngx_conf_t *cf) { ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_not_modified_header_filter; diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -32,6 +32,8 @@ struct ngx_http_proxy_redirect_s { typedef struct { + ngx_str_t key_start; + ngx_str_t schema; ngx_str_t host_header; ngx_str_t port; ngx_str_t uri; @@ -62,6 +64,10 @@ typedef struct { ngx_str_t location; ngx_str_t url; +#if (NGX_HTTP_CACHE) + ngx_http_complex_value_t cache_key; +#endif + ngx_http_proxy_vars_t vars; ngx_flag_t redirect; @@ -88,6 +94,9 @@ typedef struct { static ngx_int_t ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx, ngx_http_proxy_loc_conf_t *plcf); +#if (NGX_HTTP_CACHE) +static ngx_int_t ngx_http_proxy_create_key(ngx_http_request_t *r); +#endif static ngx_int_t ngx_http_proxy_create_request(ngx_http_request_t *r); static ngx_int_t ngx_http_proxy_reinit_request(ngx_http_request_t *r); static ngx_int_t ngx_http_proxy_process_status_line(ngx_http_request_t *r); @@ -115,6 +124,8 @@ static ngx_int_t ngx_http_proxy_add_vari static void *ngx_http_proxy_create_loc_conf(ngx_conf_t *cf); static char *ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); +static ngx_int_t ngx_http_proxy_merge_headers(ngx_conf_t *cf, + ngx_http_proxy_loc_conf_t *conf, ngx_http_proxy_loc_conf_t *prev); static char *ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -122,6 +133,12 @@ static char *ngx_http_proxy_redirect(ngx void *conf); static char *ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +#if (NGX_HTTP_CACHE) +static char *ngx_http_proxy_cache(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_http_proxy_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +#endif static char *ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data); @@ -134,8 +151,7 @@ static char *ngx_http_proxy_upstream_fai static ngx_int_t ngx_http_proxy_set_ssl(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *plcf); #endif -static ngx_int_t ngx_http_proxy_set_vars(ngx_pool_t *pool, ngx_url_t *u, - ngx_http_proxy_vars_t *v); +static void ngx_http_proxy_set_vars(ngx_url_t *u, ngx_http_proxy_vars_t *v); static ngx_conf_post_t ngx_http_proxy_lowat_post = @@ -151,11 +167,24 @@ static ngx_conf_bitmask_t ngx_http_prox { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 }, { ngx_string("http_504"), NGX_HTTP_UPSTREAM_FT_HTTP_504 }, { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 }, + { ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING }, { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF }, { ngx_null_string, 0 } }; +static ngx_conf_bitmask_t ngx_http_proxy_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("Expires"), NGX_HTTP_UPSTREAM_IGN_EXPIRES }, + { ngx_string("Cache-Control"), NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL }, + { ngx_null_string, 0 } +}; + + +ngx_module_t ngx_http_proxy_module; + + static ngx_command_t ngx_http_proxy_commands[] = { { ngx_string("proxy_pass"), @@ -305,12 +334,65 @@ static ngx_command_t ngx_http_proxy_com offsetof(ngx_http_proxy_loc_conf_t, upstream.busy_buffers_size_conf), NULL }, +#if (NGX_HTTP_CACHE) + + { ngx_string("proxy_cache"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_http_proxy_cache, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("proxy_cache_key"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_http_proxy_cache_key, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("proxy_cache_path"), + NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE, + ngx_http_file_cache_set_slot, + 0, + 0, + &ngx_http_proxy_module }, + + { ngx_string("proxy_cache_valid"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, + ngx_http_file_cache_valid_set_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_valid), + NULL }, + + { ngx_string("proxy_cache_min_uses"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_min_uses), + NULL }, + + { ngx_string("proxy_cache_use_stale"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, + ngx_conf_set_bitmask_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_use_stale), + &ngx_http_proxy_next_upstream_masks }, + + { ngx_string("proxy_cache_methods"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, + ngx_conf_set_bitmask_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_methods), + &ngx_http_upstream_cache_method_mask }, + +#endif + { ngx_string("proxy_temp_path"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234, ngx_conf_set_path_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_proxy_loc_conf_t, upstream.temp_path), - (void *) ngx_garbage_collector_temp_handler }, + NULL }, { ngx_string("proxy_max_temp_file_size"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, @@ -361,6 +443,13 @@ static ngx_command_t ngx_http_proxy_com offsetof(ngx_http_proxy_loc_conf_t, upstream.hide_headers), NULL }, + { ngx_string("proxy_ignore_headers"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, + ngx_conf_set_bitmask_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, upstream.ignore_headers), + &ngx_http_proxy_ignore_headers_masks }, + #if (NGX_HTTP_SSL) { ngx_string("proxy_ssl_session_reuse"), @@ -432,6 +521,40 @@ static ngx_str_t ngx_http_proxy_hide_he }; +#if (NGX_HTTP_CACHE) + +static ngx_keyval_t ngx_http_proxy_cache_headers[] = { + { ngx_string("Host"), ngx_string("$proxy_host") }, + { ngx_string("Connection"), ngx_string("close") }, + { ngx_string("Keep-Alive"), ngx_string("") }, + { ngx_string("Expect"), ngx_string("") }, + { ngx_string("If-Modified-Since"), ngx_string("") }, + { ngx_string("If-Unmodified-Since"), ngx_string("") }, + { ngx_string("If-Match-None"), ngx_string("") }, + { ngx_string("If-Match"), ngx_string("") }, + { ngx_string("Range"), ngx_string("") }, + { ngx_string("If-Range"), ngx_string("") }, + { ngx_null_string, ngx_null_string } +}; + + +static ngx_str_t ngx_http_proxy_hide_cache_headers[] = { + ngx_string("Date"), + ngx_string("Server"), + ngx_string("X-Pad"), + ngx_string("X-Accel-Expires"), + ngx_string("X-Accel-Redirect"), + ngx_string("X-Accel-Limit-Rate"), + ngx_string("X-Accel-Buffering"), + ngx_string("X-Accel-Charset"), + ngx_string("Set-Cookie"), + ngx_string("P3P"), + ngx_null_string +}; + +#endif + + static ngx_http_variable_t ngx_http_proxy_vars[] = { { ngx_string("proxy_host"), NULL, ngx_http_proxy_host_variable, 0, @@ -454,6 +577,11 @@ static ngx_http_variable_t ngx_http_pro }; +static ngx_path_init_t ngx_http_proxy_temp_path = { + ngx_string(NGX_HTTP_PROXY_TEMP_PATH), { 1, 2, 0 } +}; + + static ngx_int_t ngx_http_proxy_handler(ngx_http_request_t *r) { @@ -480,7 +608,7 @@ ngx_http_proxy_handler(ngx_http_request_ if (plcf->proxy_lengths == 0) { ctx->vars = plcf->vars; - u->schema = plcf->upstream.schema; + u->schema = plcf->vars.schema; #if (NGX_HTTP_SSL) u->ssl = (plcf->upstream.ssl != NULL); #endif @@ -501,6 +629,9 @@ ngx_http_proxy_handler(ngx_http_request_ u->conf = &plcf->upstream; +#if (NGX_HTTP_CACHE) + u->create_key = ngx_http_proxy_create_key; +#endif u->create_request = ngx_http_proxy_create_request; u->reinit_request = ngx_http_proxy_reinit_request; u->process_header = ngx_http_proxy_process_status_line; @@ -536,10 +667,12 @@ static ngx_int_t ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx, ngx_http_proxy_loc_conf_t *plcf) { - size_t add; - u_short port; - ngx_str_t proxy; - ngx_url_t u; + u_char *p; + size_t add; + u_short port; + ngx_str_t proxy; + ngx_url_t url; + ngx_http_upstream_t *u; if (ngx_http_script_run(r, &proxy, plcf->proxy_lengths->elts, 0, plcf->proxy_values->elts) @@ -569,49 +702,171 @@ ngx_http_proxy_eval(ngx_http_request_t * return NGX_ERROR; } - r->upstream->schema.len = add; - r->upstream->schema.data = proxy.data; - - ngx_memzero(&u, sizeof(ngx_url_t)); - - u.url.len = proxy.len - add; - u.url.data = proxy.data + add; - u.default_port = port; - u.uri_part = 1; - u.no_resolve = 1; - - if (ngx_parse_url(r->pool, &u) != NGX_OK) { - if (u.err) { + u = r->upstream; + + u->schema.len = add; + u->schema.data = proxy.data; + + ngx_memzero(&url, sizeof(ngx_url_t)); + + url.url.len = proxy.len - add; + url.url.data = proxy.data + add; + url.default_port = port; + url.uri_part = 1; + url.no_resolve = 1; + + if (ngx_parse_url(r->pool, &url) != NGX_OK) { + if (url.err) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "%s in upstream \"%V\"", u.err, &u.url); + "%s in upstream \"%V\"", url.err, &url.url); } return NGX_ERROR; } - if (ngx_http_proxy_set_vars(r->pool, &u, &ctx->vars) != NGX_OK) { + if (url.uri.len && url.uri.data[0] == '?') { + p = ngx_pnalloc(r->pool, url.uri.len + 1); + if (p == NULL) { + return NGX_ERROR; + } + + *p++ = '/'; + ngx_memcpy(p, url.uri.data, url.uri.len); + + url.uri.len++; + url.uri.data = p - 1; + } + + ctx->vars.key_start = u->schema; + + ngx_http_proxy_set_vars(&url, &ctx->vars); + + u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t)); + if (u->resolved == NULL) { return NGX_ERROR; } - r->upstream->resolved = ngx_pcalloc(r->pool, - sizeof(ngx_http_upstream_resolved_t)); - if (r->upstream->resolved == NULL) { - return NGX_ERROR; + if (url.addrs && url.addrs[0].sockaddr) { + u->resolved->sockaddr = url.addrs[0].sockaddr; + u->resolved->socklen = url.addrs[0].socklen; + u->resolved->naddrs = 1; + u->resolved->host = url.addrs[0].name; + + } else { + u->resolved->host = url.host; + u->resolved->port = (in_port_t) (url.no_port ? port : url.port); + u->resolved->no_port = url.no_port; } - r->upstream->resolved->host = u.host; - r->upstream->resolved->port = (in_port_t) (u.no_port ? u.default_port: - u.port); - r->upstream->resolved->default_port = u.default_port; - return NGX_OK; } +#if (NGX_HTTP_CACHE) + +static ngx_int_t +ngx_http_proxy_create_key(ngx_http_request_t *r) +{ + size_t len, loc_len; + u_char *p; + uintptr_t escape; + ngx_str_t *key; + ngx_http_upstream_t *u; + ngx_http_proxy_ctx_t *ctx; + ngx_http_proxy_loc_conf_t *plcf; + + u = r->upstream; + + plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); + + ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + + key = ngx_array_push(&r->cache->keys); + if (key == NULL) { + return NGX_ERROR; + } + + if (plcf->cache_key.value.len) { + + if (ngx_http_complex_value(r, &plcf->cache_key, key) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; + } + + *key = ctx->vars.key_start; + + key = ngx_array_push(&r->cache->keys); + if (key == NULL) { + return NGX_ERROR; + } + + if (plcf->proxy_lengths) { + + *key = ctx->vars.uri; + u->uri = ctx->vars.uri; + + return NGX_OK; + + } else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri && r == r->main) + { + *key = r->unparsed_uri; + u->uri = r->unparsed_uri; + + return NGX_OK; + } + + loc_len = (r->valid_location && ctx->vars.uri.len) ? plcf->location.len : 0; + + if (r->quoted_uri || r->internal) { + escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len, + r->uri.len - loc_len, NGX_ESCAPE_URI); + } else { + escape = 0; + } + + len = ctx->vars.uri.len + r->uri.len - loc_len + escape + + sizeof("?") - 1 + r->args.len; + + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + key->data = p; + + if (r->valid_location) { + p = ngx_copy(p, ctx->vars.uri.data, ctx->vars.uri.len); + } + + if (escape) { + ngx_escape_uri(p, r->uri.data + loc_len, + r->uri.len - loc_len, NGX_ESCAPE_URI); + p += r->uri.len - loc_len + escape; + + } else { + p = ngx_copy(p, r->uri.data + loc_len, r->uri.len - loc_len); + } + + if (r->args.len > 0) { + *p++ = '?'; + p = ngx_copy(p, r->args.data, r->args.len); + } + + key->len = p - key->data; + u->uri = *key; + + return NGX_OK; +} + +#endif + + static ngx_int_t ngx_http_proxy_create_request(ngx_http_request_t *r) { - size_t len, loc_len, body_len; + size_t len, uri_len, loc_len, body_len; uintptr_t escape; ngx_buf_t *b; ngx_str_t method; @@ -652,12 +907,12 @@ ngx_http_proxy_create_request(ngx_http_r ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); if (plcf->proxy_lengths) { - len += ctx->vars.uri.len; + uri_len = ctx->vars.uri.len; } else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri && r == r->main) { unparsed_uri = 1; - len += r->unparsed_uri.len; + uri_len = r->unparsed_uri.len; } else { loc_len = (r->valid_location && ctx->vars.uri.len) ? @@ -668,10 +923,18 @@ ngx_http_proxy_create_request(ngx_http_r r->uri.len - loc_len, NGX_ESCAPE_URI); } - len += ctx->vars.uri.len + r->uri.len - loc_len + escape - + sizeof("?") - 1 + r->args.len; + uri_len = ctx->vars.uri.len + r->uri.len - loc_len + escape + + sizeof("?") - 1 + r->args.len; } + if (uri_len == 0) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "zero length URI to proxy"); + return NGX_ERROR; + } + + len += uri_len; + ngx_http_script_flush_no_cacheable_variables(r, plcf->flushes); if (plcf->body_set_len) { @@ -957,6 +1220,17 @@ ngx_http_proxy_process_status_line(ngx_h u = r->upstream; if (rc == NGX_HTTP_PROXY_PARSE_NO_HEADER) { + +#if (NGX_HTTP_CACHE) + + if (r->cache) { + r->http_version = NGX_HTTP_VERSION_9; + u->headers_in.status_n = NGX_HTTP_OK; + return NGX_OK; + } + +#endif + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "upstream sent no valid HTTP/1.0 header"); @@ -973,8 +1247,11 @@ ngx_http_proxy_process_status_line(ngx_h return NGX_OK; } + if (u->state) { + u->state->status = ctx->status; + } + u->headers_in.status_n = ctx->status; - u->state->status = ctx->status; u->headers_in.status_line.len = ctx->status_end - ctx->status_start; u->headers_in.status_line.data = ngx_pnalloc(r->pool, @@ -1607,17 +1884,19 @@ ngx_http_proxy_create_loc_conf(ngx_conf_ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_proxy_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* * set by ngx_pcalloc(): * * conf->upstream.bufs.num = 0; + * conf->upstream.ignore_headers = 0; * conf->upstream.next_upstream = 0; + * conf->upstream.cache_use_stale = 0; + * conf->upstream.cache_methods = 0; * conf->upstream.temp_path = NULL; * conf->upstream.hide_headers_hash = { NULL, 0 }; - * conf->upstream.schema = { 0, NULL }; * conf->upstream.uri = { 0, NULL }; * conf->upstream.location = NULL; * conf->upstream.store_lengths = NULL; @@ -1653,6 +1932,12 @@ ngx_http_proxy_create_loc_conf(ngx_conf_ conf->upstream.pass_request_headers = NGX_CONF_UNSET; conf->upstream.pass_request_body = NGX_CONF_UNSET; +#if (NGX_HTTP_CACHE) + conf->upstream.cache = NGX_CONF_UNSET_PTR; + conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT; + conf->upstream.cache_valid = NGX_CONF_UNSET_PTR; +#endif + conf->upstream.hide_headers = NGX_CONF_UNSET_PTR; conf->upstream.pass_headers = NGX_CONF_UNSET_PTR; @@ -1680,20 +1965,16 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t ngx_http_proxy_loc_conf_t *prev = parent; ngx_http_proxy_loc_conf_t *conf = child; - u_char *p; - size_t size; - uintptr_t *code; - ngx_uint_t i; - ngx_keyval_t *src, *s, *h; - ngx_hash_key_t *hk; - ngx_hash_init_t hash; - ngx_http_proxy_redirect_t *pr; - ngx_http_script_compile_t sc; - ngx_http_script_copy_code_t *copy; + size_t size; + ngx_str_t *h; + ngx_keyval_t *s; + ngx_hash_init_t hash; + ngx_http_proxy_redirect_t *pr; + ngx_http_script_compile_t sc; if (conf->upstream.store != 0) { ngx_conf_merge_value(conf->upstream.store, - prev->upstream.store, 0); + prev->upstream.store, 0); if (conf->upstream.store_lengths == NULL) { conf->upstream.store_lengths = prev->upstream.store_lengths; @@ -1817,6 +2098,11 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t } + ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers, + prev->upstream.ignore_headers, + NGX_CONF_BITMASK_SET); + + ngx_conf_merge_bitmask_value(conf->upstream.next_upstream, prev->upstream.next_upstream, (NGX_CONF_BITMASK_SET @@ -1828,10 +2114,59 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t |NGX_HTTP_UPSTREAM_FT_OFF; } - ngx_conf_merge_path_value(conf->upstream.temp_path, + if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path, prev->upstream.temp_path, - NGX_HTTP_PROXY_TEMP_PATH, 1, 2, 0, - ngx_garbage_collector_temp_handler, cf); + &ngx_http_proxy_temp_path) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + +#if (NGX_HTTP_CACHE) + + ngx_conf_merge_ptr_value(conf->upstream.cache, + prev->upstream.cache, NULL); + + if (conf->upstream.cache && conf->upstream.cache->data == NULL) { + ngx_shm_zone_t *shm_zone; + + shm_zone = conf->upstream.cache; + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"proxy_cache\" zone \"%V\" is unknown", + &shm_zone->shm.name); + + return NGX_CONF_ERROR; + } + + ngx_conf_merge_uint_value(conf->upstream.cache_min_uses, + prev->upstream.cache_min_uses, 1); + + ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale, + prev->upstream.cache_use_stale, + (NGX_CONF_BITMASK_SET + |NGX_HTTP_UPSTREAM_FT_OFF)); + + if (conf->upstream.cache_methods == 0) { + conf->upstream.cache_methods = prev->upstream.cache_methods; + } + + conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD; + + if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) { + conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET + |NGX_HTTP_UPSTREAM_FT_OFF; + } + + ngx_conf_merge_ptr_value(conf->upstream.cache_valid, + prev->upstream.cache_valid, NULL); + + if (conf->cache_key.value.data == NULL) { + conf->cache_key = prev->cache_key; + } + +#endif if (conf->method.len == 0) { conf->method = prev->method; @@ -1894,6 +2229,12 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t conf->proxy_values = prev->proxy_values; } +#if (NGX_HTTP_SSL) + if (conf->upstream.ssl == NULL) { + conf->upstream.ssl = prev->upstream.ssl; + } +#endif + ngx_conf_merge_uint_value(conf->headers_hash_max_size, prev->headers_hash_max_size, 512); @@ -1907,9 +2248,18 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t hash.bucket_size = conf->headers_hash_bucket_size; hash.name = "proxy_headers_hash"; +#if (NGX_HTTP_CACHE) + + h = conf->upstream.cache ? ngx_http_proxy_hide_cache_headers: + ngx_http_proxy_hide_headers; +#else + + h = ngx_http_proxy_hide_headers; + +#endif + if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream, - &prev->upstream, - ngx_http_proxy_hide_headers, &hash) + &prev->upstream, h, &hash) != NGX_OK) { return NGX_CONF_ERROR; @@ -1917,9 +2267,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t if (conf->upstream.upstream == NULL) { conf->upstream.upstream = prev->upstream.upstream; - conf->vars = prev->vars; - conf->upstream.schema = prev->upstream.schema; } @@ -1964,6 +2312,27 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t s->value.data = (u_char *) "$proxy_internal_body_length"; } + if (ngx_http_proxy_merge_headers(cf, conf, prev) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_http_proxy_merge_headers(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *conf, + ngx_http_proxy_loc_conf_t *prev) +{ + u_char *p; + size_t size; + uintptr_t *code; + ngx_uint_t i; + ngx_keyval_t *src, *s, *h; + ngx_hash_key_t *hk; + ngx_hash_init_t hash; + ngx_http_script_compile_t sc; + ngx_http_script_copy_code_t *copy; if (conf->headers_source == NULL) { conf->flushes = prev->flushes; @@ -1973,38 +2342,53 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t conf->headers_source = prev->headers_source; } - if (conf->headers_set_hash.buckets) { - return NGX_CONF_OK; + if (conf->headers_set_hash.buckets +#if (NGX_HTTP_CACHE) + && ((conf->upstream.cache == NULL) == (prev->upstream.cache == NULL)) +#endif + ) + { + return NGX_OK; } conf->headers_names = ngx_array_create(cf->pool, 4, sizeof(ngx_hash_key_t)); if (conf->headers_names == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } if (conf->headers_source == NULL) { conf->headers_source = ngx_array_create(cf->pool, 4, sizeof(ngx_keyval_t)); if (conf->headers_source == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } } conf->headers_set_len = ngx_array_create(cf->pool, 64, 1); if (conf->headers_set_len == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } conf->headers_set = ngx_array_create(cf->pool, 512, 1); if (conf->headers_set == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } src = conf->headers_source->elts; - for (h = ngx_http_proxy_headers; h->key.len; h++) { +#if (NGX_HTTP_CACHE) + + h = conf->upstream.cache ? ngx_http_proxy_cache_headers: + ngx_http_proxy_headers; +#else + + h = ngx_http_proxy_headers; + +#endif + + while (h->key.len) { for (i = 0; i < conf->headers_source->nelts; i++) { if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) { @@ -2014,7 +2398,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t s = ngx_array_push(conf->headers_source); if (s == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } *s = *h; @@ -2023,7 +2407,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t next: - continue; + h++; } @@ -2032,7 +2416,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t hk = ngx_array_push(conf->headers_names); if (hk == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } hk->key = src[i].key; @@ -2047,7 +2431,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t copy = ngx_array_push_n(conf->headers_set_len, sizeof(ngx_http_script_copy_code_t)); if (copy == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } copy->code = (ngx_http_script_code_pt) @@ -2064,7 +2448,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t copy = ngx_array_push_n(conf->headers_set, size); if (copy == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } copy->code = ngx_http_script_copy_code; @@ -2082,7 +2466,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t copy = ngx_array_push_n(conf->headers_set_len, sizeof(ngx_http_script_copy_code_t)); if (copy == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } copy->code = (ngx_http_script_code_pt) @@ -2096,7 +2480,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t copy = ngx_array_push_n(conf->headers_set, size); if (copy == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } copy->code = ngx_http_script_copy_code; @@ -2116,14 +2500,14 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t sc.values = &conf->headers_set; if (ngx_http_script_compile(&sc) != NGX_OK) { - return NGX_CONF_ERROR; + return NGX_ERROR; } copy = ngx_array_push_n(conf->headers_set_len, sizeof(ngx_http_script_copy_code_t)); if (copy == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } copy->code = (ngx_http_script_code_pt) @@ -2137,7 +2521,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t copy = ngx_array_push_n(conf->headers_set, size); if (copy == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } copy->code = ngx_http_script_copy_code; @@ -2149,14 +2533,14 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t code = ngx_array_push_n(conf->headers_set_len, sizeof(uintptr_t)); if (code == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } *code = (uintptr_t) NULL; code = ngx_array_push_n(conf->headers_set, sizeof(uintptr_t)); if (code == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } *code = (uintptr_t) NULL; @@ -2164,7 +2548,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t code = ngx_array_push_n(conf->headers_set_len, sizeof(uintptr_t)); if (code == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } *code = (uintptr_t) NULL; @@ -2182,10 +2566,10 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t conf->headers_names->nelts) != NGX_OK) { - return NGX_CONF_ERROR; + return NGX_ERROR; } - return NGX_CONF_OK; + return NGX_OK; } @@ -2202,7 +2586,7 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_ ngx_http_core_loc_conf_t *clcf; ngx_http_script_compile_t sc; - if (plcf->upstream.schema.len) { + if (plcf->upstream.upstream || plcf->proxy_lengths) { return "is duplicate"; } @@ -2278,16 +2662,16 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_ return NGX_CONF_ERROR; } - if (ngx_http_proxy_set_vars(cf->pool, &u, &plcf->vars) != NGX_OK) { - return NGX_CONF_ERROR; - } - - plcf->upstream.schema.len = add; - plcf->upstream.schema.data = url->data; - plcf->location = clcf->name; + plcf->vars.schema.len = add; + plcf->vars.schema.data = url->data; + plcf->vars.key_start = plcf->vars.schema; + + ngx_http_proxy_set_vars(&u, &plcf->vars); clcf->handler = ngx_http_proxy_handler; + plcf->location = clcf->name; + if (clcf->named #if (NGX_PCRE) || clcf->regex @@ -2333,10 +2717,26 @@ ngx_http_proxy_redirect(ngx_conf_t *cf, value = cf->args->elts; - if (ngx_strcmp(value[1].data, "off") == 0) { - plcf->redirect = 0; - plcf->redirects = NULL; - return NGX_CONF_OK; + if (cf->args->nelts == 2) { + if (ngx_strcmp(value[1].data, "off") == 0) { + plcf->redirect = 0; + plcf->redirects = NULL; + return NGX_CONF_OK; + } + + if (ngx_strcmp(value[1].data, "false") == 0) { + ngx_conf_log_error(NGX_LOG_ERR, cf, 0, + "invalid parameter \"false\", use \"off\" instead"); + plcf->redirect = 0; + plcf->redirects = NULL; + return NGX_CONF_OK; + } + + if (ngx_strcmp(value[1].data, "default") != 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[1]); + return NGX_CONF_ERROR; + } } if (plcf->redirects == NULL) { @@ -2352,7 +2752,7 @@ ngx_http_proxy_redirect(ngx_conf_t *cf, return NGX_CONF_ERROR; } - if (cf->args->nelts == 2 && ngx_strcmp(value[1].data, "default") == 0) { + if (ngx_strcmp(value[1].data, "default") == 0) { if (plcf->url.data == NULL) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"proxy_rewrite_location default\" must go " @@ -2415,20 +2815,31 @@ ngx_http_proxy_store(ngx_conf_t *cf, ngx ngx_str_t *value; ngx_http_script_compile_t sc; - if (plcf->upstream.store != NGX_CONF_UNSET || plcf->upstream.store_lengths) + if (plcf->upstream.store != NGX_CONF_UNSET + || plcf->upstream.store_lengths) { return "is duplicate"; } value = cf->args->elts; - if (ngx_strcmp(value[1].data, "on") == 0) { - plcf->upstream.store = 1; + if (ngx_strcmp(value[1].data, "off") == 0) { + plcf->upstream.store = 0; return NGX_CONF_OK; } - if (ngx_strcmp(value[1].data, "off") == 0) { - plcf->upstream.store = 0; +#if (NGX_HTTP_CACHE) + + if (plcf->upstream.cache != NGX_CONF_UNSET_PTR + && plcf->upstream.cache != NULL) + { + return "is incompatible with \"proxy_cache\""; + } + +#endif + + if (ngx_strcmp(value[1].data, "on") == 0) { + plcf->upstream.store = 1; return NGX_CONF_OK; } @@ -2453,6 +2864,70 @@ ngx_http_proxy_store(ngx_conf_t *cf, ngx } +#if (NGX_HTTP_CACHE) + +static char * +ngx_http_proxy_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_proxy_loc_conf_t *plcf = conf; + + ngx_str_t *value; + + value = cf->args->elts; + + if (plcf->upstream.cache != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + if (ngx_strcmp(value[1].data, "off") == 0) { + plcf->upstream.cache = NULL; + return NGX_CONF_OK; + } + + if (plcf->upstream.store > 0 || plcf->upstream.store_lengths) { + return "is incompatible with \"proxy_store\""; + } + + plcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, + &ngx_http_proxy_module); + if (plcf->upstream.cache == NULL) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_proxy_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_proxy_loc_conf_t *plcf = conf; + + ngx_str_t *value; + ngx_http_compile_complex_value_t ccv; + + value = cf->args->elts; + + if (plcf->cache_key.value.len) { + return "is duplicate"; + } + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &plcf->cache_key; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + +#endif + + static char * ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data) { @@ -2543,12 +3018,13 @@ ngx_http_proxy_set_ssl(ngx_conf_t *cf, n #endif -static ngx_int_t -ngx_http_proxy_set_vars(ngx_pool_t *pool, ngx_url_t *u, - ngx_http_proxy_vars_t *v) +static void +ngx_http_proxy_set_vars(ngx_url_t *u, ngx_http_proxy_vars_t *v) { if (u->family != AF_UNIX) { + if (u->no_port || u->port == u->default_port) { + v->host_header = u->host; if (u->default_port == 80) { @@ -2566,14 +3042,15 @@ ngx_http_proxy_set_vars(ngx_pool_t *pool v->port = u->port_text; } + v->key_start.len += v->host_header.len; + } else { v->host_header.len = sizeof("localhost") - 1; v->host_header.data = (u_char *) "localhost"; v->port.len = 0; v->port.data = (u_char *) ""; + v->key_start.len += sizeof("unix:") - 1 + u->host.len + 1; } v->uri = u->uri; - - return NGX_OK; } diff --git a/src/http/modules/ngx_http_random_index_module.c b/src/http/modules/ngx_http_random_index_module.c --- a/src/http/modules/ngx_http_random_index_module.c +++ b/src/http/modules/ngx_http_random_index_module.c @@ -86,7 +86,6 @@ ngx_http_random_index_handler(ngx_http_r return NGX_DECLINED; } - /* TODO: Win32 */ if (r->zero_in_uri) { return NGX_DECLINED; } @@ -281,7 +280,7 @@ ngx_http_random_index_create_loc_conf(ng conf = ngx_palloc(cf->pool, sizeof(ngx_http_random_index_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } conf->enable = NGX_CONF_UNSET; diff --git a/src/http/modules/ngx_http_range_filter_module.c b/src/http/modules/ngx_http_range_filter_module.c --- a/src/http/modules/ngx_http_range_filter_module.c +++ b/src/http/modules/ngx_http_range_filter_module.c @@ -222,7 +222,7 @@ ngx_http_range_header_filter(ngx_http_re } if (ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_range_t)) - == NGX_ERROR) + != NGX_OK) { return NGX_ERROR; } @@ -234,6 +234,7 @@ ngx_http_range_header_filter(ngx_http_re ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module); r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT; + r->headers_out.status_line.len = 0; if (ctx->ranges.nelts == 1) { return ngx_http_range_singlepart_header(r, ctx); @@ -490,6 +491,8 @@ ngx_http_range_multipart_header(ngx_http return NGX_ERROR; } + r->headers_out.content_type_lowcase = NULL; + /* "Content-Type: multipart/byteranges; boundary=0123456789" */ r->headers_out.content_type.len = @@ -498,6 +501,7 @@ ngx_http_range_multipart_header(ngx_http boundary) - r->headers_out.content_type.data; + r->headers_out.content_type_len = r->headers_out.content_type.len; /* the size of the last boundary CRLF "--0123456789--" CRLF */ @@ -748,6 +752,7 @@ ngx_http_range_multipart_body(ngx_http_r ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in, ngx_http_output_body_filter_pt ngx_http_next_body_filter) { + off_t body_start; ngx_buf_t *b, *buf; ngx_uint_t i; ngx_chain_t *out, *hcl, *rcl, *dcl, **ll; @@ -757,6 +762,12 @@ ngx_http_range_multipart_body(ngx_http_r buf = in->buf; range = ctx->ranges.elts; +#if (NGX_HTTP_CACHE) + body_start = r->cached ? r->cache->body_start : 0; +#else + body_start = 0; +#endif + for (i = 0; i < ctx->ranges.nelts; i++) { /* @@ -817,8 +828,8 @@ ngx_http_range_multipart_body(ngx_http_r b->file = buf->file; if (buf->in_file) { - b->file_pos = range[i].start; - b->file_last = range[i].end; + b->file_pos = body_start + range[i].start; + b->file_last = body_start + range[i].end; } if (ngx_buf_in_memory(buf)) { diff --git a/src/http/modules/ngx_http_realip_module.c b/src/http/modules/ngx_http_realip_module.c --- a/src/http/modules/ngx_http_realip_module.c +++ b/src/http/modules/ngx_http_realip_module.c @@ -14,8 +14,6 @@ #define NGX_HTTP_REALIP_HEADER 2 -/* AF_INET only */ - typedef struct { in_addr_t mask; in_addr_t addr; @@ -209,6 +207,10 @@ found: /* AF_INET only */ + if (r->connection->sockaddr->sa_family != AF_INET) { + return NGX_DECLINED; + } + sin = (struct sockaddr_in *) c->sockaddr; from = rlcf->from->elts; @@ -280,7 +282,7 @@ ngx_http_realip_from(ngx_conf_t *cf, ngx ngx_int_t rc; ngx_str_t *value; - ngx_inet_cidr_t in_cidr; + ngx_cidr_t cidr; ngx_http_realip_from_t *from; if (rlcf->from == NULL) { @@ -298,7 +300,7 @@ ngx_http_realip_from(ngx_conf_t *cf, ngx value = cf->args->elts; - rc = ngx_ptocidr(&value[1], &in_cidr); + rc = ngx_ptocidr(&value[1], &cidr); if (rc == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", @@ -306,13 +308,19 @@ ngx_http_realip_from(ngx_conf_t *cf, ngx return NGX_CONF_ERROR; } + if (cidr.family != AF_INET) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"realip_from\" supports IPv4 only"); + return NGX_CONF_ERROR; + } + if (rc == NGX_DONE) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "low address bits of %V are meaningless", &value[1]); } - from->mask = in_cidr.mask; - from->addr = in_cidr.addr; + from->mask = cidr.u.in.mask; + from->addr = cidr.u.in.addr; return NGX_CONF_OK; } @@ -352,7 +360,7 @@ ngx_http_realip_create_loc_conf(ngx_conf conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_realip_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* diff --git a/src/http/modules/ngx_http_referer_module.c b/src/http/modules/ngx_http_referer_module.c --- a/src/http/modules/ngx_http_referer_module.c +++ b/src/http/modules/ngx_http_referer_module.c @@ -221,7 +221,7 @@ ngx_http_referer_create_conf(ngx_conf_t conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_referer_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } #if (NGX_PCRE) diff --git a/src/http/modules/ngx_http_rewrite_module.c b/src/http/modules/ngx_http_rewrite_module.c --- a/src/http/modules/ngx_http_rewrite_module.c +++ b/src/http/modules/ngx_http_rewrite_module.c @@ -12,7 +12,6 @@ typedef struct { ngx_array_t *codes; /* uintptr_t */ - ngx_uint_t captures; ngx_uint_t stack_size; ngx_flag_t log; @@ -157,16 +156,6 @@ ngx_http_rewrite_handler(ngx_http_reques return NGX_HTTP_INTERNAL_SERVER_ERROR; } - if (rlcf->captures) { - e->captures = ngx_palloc(r->pool, rlcf->captures * sizeof(int)); - if (e->captures == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - } else { - e->captures = NULL; - } - e->ip = rlcf->codes->elts; e->request = r; e->quote = 1; @@ -231,7 +220,7 @@ ngx_http_rewrite_create_loc_conf(ngx_con conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_rewrite_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } conf->stack_size = NGX_CONF_UNSET_UINT; @@ -436,10 +425,6 @@ ngx_http_rewrite(ngx_conf_t *cf, ngx_com if (regex->ncaptures) { regex->ncaptures = (regex->ncaptures + 1) * 3; - - if (lcf->captures < regex->ncaptures) { - lcf->captures = regex->ncaptures; - } } regex_end = ngx_http_script_add_code(lcf->codes, @@ -618,11 +603,6 @@ ngx_http_rewrite_if(ngx_conf_t *cf, ngx_ } - if (lcf->captures < nlcf->captures) { - lcf->captures = nlcf->captures; - } - - if (elts != lcf->codes->elts) { if_code = (ngx_http_script_if_code_t *) ((u_char *) if_code + ((u_char *) lcf->codes->elts - elts)); @@ -777,10 +757,6 @@ ngx_http_rewrite_if_condition(ngx_conf_t if (n) { regex->ncaptures = (n + 1) * 3; - - if (lcf->captures < regex->ncaptures) { - lcf->captures = regex->ncaptures; - } } return NGX_CONF_OK; diff --git a/src/http/modules/ngx_http_secure_link_module.c b/src/http/modules/ngx_http_secure_link_module.c --- a/src/http/modules/ngx_http_secure_link_module.c +++ b/src/http/modules/ngx_http_secure_link_module.c @@ -113,7 +113,7 @@ url_start: len = last - p; - if (end - start != 32 || len < 3) { + if (end - start != 32 || len == 0) { goto not_found; } @@ -152,7 +152,7 @@ ngx_http_secure_link_create_conf(ngx_con conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_secure_link_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* diff --git a/src/http/modules/ngx_http_ssi_filter_module.c b/src/http/modules/ngx_http_ssi_filter_module.c --- a/src/http/modules/ngx_http_ssi_filter_module.c +++ b/src/http/modules/ngx_http_ssi_filter_module.c @@ -375,7 +375,6 @@ ngx_http_ssi_body_filter(ngx_http_reques ngx_uint_t i, index; ngx_chain_t *cl, **ll; ngx_table_elt_t *param; - ngx_http_request_t *pr; ngx_http_ssi_ctx_t *ctx, *mctx; ngx_http_ssi_block_t *bl; ngx_http_ssi_param_t *prm; @@ -398,49 +397,42 @@ ngx_http_ssi_body_filter(ngx_http_reques /* add the incoming chain to the chain ctx->in */ if (in) { - if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) { + if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) { return NGX_ERROR; } } + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http ssi filter \"%V?%V\"", &r->uri, &r->args); + if (ctx->wait) { - if (r->connection->data != r) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http ssi filter \"%V\" wait", &r->uri); + + if (r != r->connection->data) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http ssi filter wait \"%V?%V\" non-active", + &ctx->wait->uri, &ctx->wait->args); + return NGX_AGAIN; } - for (pr = ctx->wait->parent; pr; pr = pr->parent) { - if (pr == r) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http ssi filter \"%V\" flush", &r->uri); - - rc = ngx_http_next_body_filter(r, NULL); - - if (ctx->wait->done) { - ctx->wait = NULL; - } - - if (rc == NGX_ERROR || rc == NGX_AGAIN) { - return rc; - } - - break; - } - } - - if (ctx->wait == r) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http ssi filter \"%V\" continue", &r->uri); + if (ctx->wait->done) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http ssi filter wait \"%V?%V\" done", + &ctx->wait->uri, &ctx->wait->args); + ctx->wait = NULL; + + } else { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http ssi filter wait \"%V?%V\"", + &ctx->wait->uri, &ctx->wait->args); + + return ngx_http_next_body_filter(r, NULL); } } slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http ssi filter \"%V\"", &r->uri); - while (ctx->in || ctx->buf) { if (ctx->buf == NULL ){ @@ -788,16 +780,12 @@ ngx_http_ssi_body_filter(ngx_http_reques } } - if (cmd->flush) { - - if (ctx->out) { - rc = ngx_http_ssi_output(r, ctx); - - } else { - rc = ngx_http_next_body_filter(r, NULL); - } - - if (rc == NGX_ERROR) { + if (cmd->flush && ctx->out) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ssi flush"); + + if (ngx_http_ssi_output(r, ctx) == NGX_ERROR) { return NGX_ERROR; } } @@ -947,12 +935,6 @@ ngx_http_ssi_output(ngx_http_request_t * break; } -#if (NGX_HAVE_WRITE_ZEROCOPY) - if (b->zerocopy_busy) { - break; - } -#endif - if (b->shadow) { b->shadow->pos = b->shadow->last; } @@ -1899,6 +1881,7 @@ ngx_http_ssi_include(ngx_http_request_t if (uri == NULL) { uri = file; + wait = (ngx_str_t *) -1; } rc = ngx_http_ssi_evaluate_string(r, ctx, uri, NGX_HTTP_SSI_ADD_PREFIX); @@ -2001,6 +1984,10 @@ ngx_http_ssi_include(ngx_http_request_t } } + if (wait) { + flags |= NGX_HTTP_SUBREQUEST_WAITED; + } + if (set) { key = ngx_hash_strlow(set->data, set->data, set->len); @@ -2033,16 +2020,10 @@ ngx_http_ssi_include(ngx_http_request_t psr->data = &var->value; } - flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY; + flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY|NGX_HTTP_SUBREQUEST_WAITED; } - rc = ngx_http_subrequest(r, uri, &args, &sr, psr, flags); - - if (rc == NGX_DONE) { - return NGX_DONE; - } - - if (rc == NGX_ERROR) { + if (ngx_http_subrequest(r, uri, &args, &sr, psr, flags) != NGX_OK) { return NGX_HTTP_SSI_ERROR; } @@ -2050,17 +2031,17 @@ ngx_http_ssi_include(ngx_http_request_t return NGX_OK; } - if (rc == NGX_AGAIN) { - if (ctx->wait == NULL) { - ctx->wait = sr; - - } else { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "only one subrequest may be waited at the same time"); - } + if (ctx->wait == NULL) { + ctx->wait = sr; + + return NGX_AGAIN; + + } else { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "only one subrequest may be waited at the same time"); } - return rc; + return NGX_OK; } @@ -2079,7 +2060,7 @@ ngx_http_ssi_stub_output(ngx_http_reques out = data; if (!r->header_sent) { - if (ngx_http_set_content_type(r) == NGX_ERROR) { + if (ngx_http_set_content_type(r) != NGX_OK) { return NGX_ERROR; } @@ -2708,14 +2689,14 @@ ngx_http_ssi_create_main_conf(ngx_conf_t smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_main_conf_t)); if (smcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } smcf->commands.pool = cf->pool; smcf->commands.temp_pool = cf->temp_pool; if (ngx_hash_keys_array_init(&smcf->commands, NGX_HASH_SMALL) != NGX_OK) { - return NGX_CONF_ERROR; + return NULL; } return smcf; @@ -2755,7 +2736,7 @@ ngx_http_ssi_create_loc_conf(ngx_conf_t slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_loc_conf_t)); if (slcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -302,7 +302,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t sscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssl_srv_conf_t)); if (sscf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* @@ -320,8 +320,8 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t sscf->enable = NGX_CONF_UNSET; sscf->prefer_server_ciphers = NGX_CONF_UNSET; - sscf->verify = NGX_CONF_UNSET; - sscf->verify_depth = NGX_CONF_UNSET; + sscf->verify = NGX_CONF_UNSET_UINT; + sscf->verify_depth = NGX_CONF_UNSET_UINT; sscf->builtin_session_cache = NGX_CONF_UNSET; sscf->session_timeout = NGX_CONF_UNSET; @@ -564,6 +564,7 @@ ngx_http_ssl_session_cache(ngx_conf_t *c for (j = sizeof("shared:") - 1; j < value[i].len; j++) { if (value[i].data[j] == ':') { + value[i].data[j] = '\0'; break; } diff --git a/src/http/modules/ngx_http_static_module.c b/src/http/modules/ngx_http_static_module.c --- a/src/http/modules/ngx_http_static_module.c +++ b/src/http/modules/ngx_http_static_module.c @@ -66,7 +66,6 @@ ngx_http_static_handler(ngx_http_request return NGX_DECLINED; } - /* TODO: Win32 */ if (r->zero_in_uri) { return NGX_DECLINED; } @@ -129,13 +128,13 @@ ngx_http_static_handler(ngx_http_request if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) { ngx_log_error(level, log, of.err, - ngx_open_file_n " \"%s\" failed", path.data); + "%s \"%s\" failed", of.failed, path.data); } return rc; } - r->root_tested = 1; + r->root_tested = !r->error_page; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd); diff --git a/src/http/modules/ngx_http_sub_filter_module.c b/src/http/modules/ngx_http_sub_filter_module.c --- a/src/http/modules/ngx_http_sub_filter_module.c +++ b/src/http/modules/ngx_http_sub_filter_module.c @@ -10,17 +10,14 @@ typedef struct { - ngx_str_t match; - ngx_str_t sub; - - ngx_hash_t types; + ngx_str_t match; + ngx_http_complex_value_t value; - ngx_array_t *sub_lengths; - ngx_array_t *sub_values; + ngx_hash_t types; - ngx_flag_t once; + ngx_flag_t once; - ngx_array_t *types_keys; + ngx_array_t *types_keys; } ngx_http_sub_loc_conf_t; @@ -31,27 +28,27 @@ typedef enum { typedef struct { - ngx_str_t match; + ngx_str_t match; - ngx_uint_t once; /* unsigned once:1 */ + ngx_uint_t once; /* unsigned once:1 */ - ngx_buf_t *buf; + ngx_buf_t *buf; - u_char *pos; - u_char *copy_start; - u_char *copy_end; + u_char *pos; + u_char *copy_start; + u_char *copy_end; - ngx_chain_t *in; - ngx_chain_t *out; - ngx_chain_t **last_out; - ngx_chain_t *busy; - ngx_chain_t *free; + ngx_chain_t *in; + ngx_chain_t *out; + ngx_chain_t **last_out; + ngx_chain_t *busy; + ngx_chain_t *free; - ngx_str_t sub; + ngx_str_t sub; - ngx_uint_t state; - size_t saved; - size_t looked; + ngx_uint_t state; + size_t saved; + size_t looked; } ngx_http_sub_ctx_t; @@ -154,7 +151,6 @@ ngx_http_sub_header_filter(ngx_http_requ ctx->match = slcf->match; ctx->last_out = &ctx->out; - ctx->sub = slcf->sub; r->filter_need_in_memory = 1; @@ -204,7 +200,7 @@ ngx_http_sub_body_filter(ngx_http_reques /* add the incoming chain to the chain ctx->in */ if (in) { - if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) { + if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) { return NGX_ERROR; } } @@ -346,9 +342,8 @@ ngx_http_sub_body_filter(ngx_http_reques if (ctx->sub.data == NULL) { - if (ngx_http_script_run(r, &ctx->sub, slcf->sub_lengths->elts, - 0, slcf->sub_values->elts) - == NULL) + if (ngx_http_complex_value(r, &slcf->value, &ctx->sub) + != NGX_OK) { return NGX_ERROR; } @@ -465,12 +460,6 @@ ngx_http_sub_output(ngx_http_request_t * break; } -#if (NGX_HAVE_WRITE_ZEROCOPY) - if (b->zerocopy_busy) { - break; - } -#endif - if (b->shadow) { b->shadow->pos = b->shadow->last; } @@ -615,9 +604,8 @@ ngx_http_sub_filter(ngx_conf_t *cf, ngx_ { ngx_http_sub_loc_conf_t *slcf = conf; - ngx_str_t *value; - ngx_int_t n; - ngx_http_script_compile_t sc; + ngx_str_t *value; + ngx_http_compile_complex_value_t ccv; if (slcf->match.len) { return "is duplicate"; @@ -629,24 +617,13 @@ ngx_http_sub_filter(ngx_conf_t *cf, ngx_ slcf->match = value[1]; - n = ngx_http_script_variables_count(&value[2]); - - if (n == 0) { - slcf->sub = value[2]; - return NGX_CONF_OK; - } - - ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); - sc.cf = cf; - sc.source = &value[2]; - sc.lengths = &slcf->sub_lengths; - sc.values = &slcf->sub_values; - sc.variables = n; - sc.complete_lengths = 1; - sc.complete_values = 1; + ccv.cf = cf; + ccv.value = &value[2]; + ccv.complex_value = &slcf->value; - if (ngx_http_script_compile(&sc) != NGX_OK) { + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } @@ -661,7 +638,7 @@ ngx_http_sub_create_conf(ngx_conf_t *cf) slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_sub_loc_conf_t)); if (slcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* @@ -690,10 +667,8 @@ ngx_http_sub_merge_conf(ngx_conf_t *cf, ngx_conf_merge_value(conf->once, prev->once, 1); ngx_conf_merge_str_value(conf->match, prev->match, ""); - if (conf->sub.data == NULL && conf->sub_lengths == NULL) { - conf->sub = prev->sub; - conf->sub_lengths = prev->sub_lengths; - conf->sub_values = prev->sub_values; + if (conf->value.value.len == 0) { + conf->value = prev->value; } if (ngx_http_merge_types(cf, conf->types_keys, &conf->types, diff --git a/src/http/modules/ngx_http_upstream_ip_hash_module.c b/src/http/modules/ngx_http_upstream_ip_hash_module.c --- a/src/http/modules/ngx_http_upstream_ip_hash_module.c +++ b/src/http/modules/ngx_http_upstream_ip_hash_module.c @@ -15,7 +15,6 @@ typedef struct { ngx_uint_t hash; - /* AF_INET only */ u_char addr[3]; u_char tries; @@ -111,11 +110,20 @@ ngx_http_upstream_init_ip_hash_peer(ngx_ r->upstream->peer.get = ngx_http_upstream_get_ip_hash_peer; /* AF_INET only */ - sin = (struct sockaddr_in *) r->connection->sockaddr; - p = (u_char *) &sin->sin_addr.s_addr; - iphp->addr[0] = p[0]; - iphp->addr[1] = p[1]; - iphp->addr[2] = p[2]; + + if (r->connection->sockaddr->sa_family == AF_INET) { + + sin = (struct sockaddr_in *) r->connection->sockaddr; + p = (u_char *) &sin->sin_addr.s_addr; + iphp->addr[0] = p[0]; + iphp->addr[1] = p[1]; + iphp->addr[2] = p[2]; + + } else { + iphp->addr[0] = 0; + iphp->addr[1] = 0; + iphp->addr[2] = 0; + } iphp->hash = 89; iphp->tries = 0; diff --git a/src/http/modules/ngx_http_userid_filter_module.c b/src/http/modules/ngx_http_userid_filter_module.c --- a/src/http/modules/ngx_http_userid_filter_module.c +++ b/src/http/modules/ngx_http_userid_filter_module.c @@ -360,10 +360,16 @@ static ngx_int_t ngx_http_userid_set_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf) { - u_char *cookie, *p; - size_t len; - ngx_str_t src, dst; - ngx_table_elt_t *set_cookie, *p3p; + u_char *cookie, *p; + size_t len; + ngx_str_t src, dst; + ngx_table_elt_t *set_cookie, *p3p; + ngx_connection_t *c; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + /* * TODO: in the threaded mode the sequencers should be in TLS and their * ranges should be divided between threads @@ -384,11 +390,33 @@ ngx_http_userid_set_uid(ngx_http_request } else { if (conf->service == NGX_CONF_UNSET) { - if (ngx_http_server_addr(r, NULL) != NGX_OK) { + + c = r->connection; + + if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { return NGX_ERROR; } - ctx->uid_set[0] = htonl(r->in_addr); + switch (c->local_sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) c->local_sockaddr; + + p = (u_char *) &ctx->uid_set[0]; + + *p++ = sin6->sin6_addr.s6_addr[12]; + *p++ = sin6->sin6_addr.s6_addr[13]; + *p++ = sin6->sin6_addr.s6_addr[14]; + *p = sin6->sin6_addr.s6_addr[15]; + + break; +#endif + default: /* AF_INET */ + sin = (struct sockaddr_in *) c->local_sockaddr; + ctx->uid_set[0] = sin->sin_addr.s_addr; + break; + } } else { ctx->uid_set[0] = htonl(conf->service); @@ -542,7 +570,7 @@ ngx_http_userid_create_conf(ngx_conf_t * conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_userid_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* diff --git a/src/http/modules/ngx_http_xslt_filter_module.c b/src/http/modules/ngx_http_xslt_filter_module.c --- a/src/http/modules/ngx_http_xslt_filter_module.c +++ b/src/http/modules/ngx_http_xslt_filter_module.c @@ -15,6 +15,10 @@ #include #include +#if (NGX_HAVE_EXSLT) +#include +#endif + #ifndef NGX_HTTP_XSLT_REUSE_DTD #define NGX_HTTP_XSLT_REUSE_DTD 1 @@ -34,14 +38,8 @@ typedef struct { typedef struct { - ngx_array_t *lengths; - ngx_array_t *values; -} ngx_http_xslt_param_t; - - -typedef struct { xsltStylesheetPtr stylesheet; - ngx_array_t params; /* ngx_http_xslt_param_t */ + ngx_array_t params; /* ngx_http_complex_value_t */ } ngx_http_xslt_sheet_t; @@ -66,7 +64,6 @@ typedef struct { static ngx_int_t ngx_http_xslt_send(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b); -static ngx_int_t ngx_http_xslt_filter_internal_error(ngx_http_request_t *r); static ngx_int_t ngx_http_xslt_add_chunk(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b); @@ -283,7 +280,7 @@ ngx_http_xslt_body_filter(ngx_http_reque return ngx_http_xslt_send(r, ctx, NULL); } - if (cl->buf->last_buf) { + if (cl->buf->last_buf || cl->buf->last_in_chain) { ctx->doc = ctx->ctxt->myDoc; @@ -322,14 +319,15 @@ ngx_http_xslt_send(ngx_http_request_t *r ctx->done = 1; if (b == NULL) { - return ngx_http_xslt_filter_internal_error(r); + return ngx_http_filter_finalize_request(r, NULL, + NGX_HTTP_INTERNAL_SERVER_ERROR); } cln = ngx_pool_cleanup_add(r->pool, 0); if (cln == NULL) { ngx_free(b->pos); - return ngx_http_special_response_handler(r, + return ngx_http_filter_finalize_request(r, NULL, NGX_HTTP_INTERNAL_SERVER_ERROR); } @@ -362,22 +360,6 @@ ngx_http_xslt_send(ngx_http_request_t *r static ngx_int_t -ngx_http_xslt_filter_internal_error(ngx_http_request_t *r) -{ - ngx_int_t rc; - - /* clear the modules contexts */ - ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module); - - rc = ngx_http_special_response_handler(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - - /* NGX_ERROR resets any pending data */ - - return (rc == NGX_OK) ? NGX_ERROR : rc; -} - - -static ngx_int_t ngx_http_xslt_add_chunk(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b) { @@ -445,8 +427,8 @@ ngx_http_xslt_add_chunk(ngx_http_request ctx->request = r; } - err = xmlParseChunk(ctx->ctxt, (char *) b->pos, - (int) (b->last - b->pos), b->last_buf); + err = xmlParseChunk(ctx->ctxt, (char *) b->pos, (int) (b->last - b->pos), + (b->last_buf) || (b->last_in_chain)); if (err == 0) { b->pos = b->last; @@ -830,7 +812,6 @@ ngx_http_xslt_apply_stylesheet(ngx_http_ b->pos = buf; b->last = buf + len; b->memory = 1; - b->last_buf = 1; if (encoding) { r->headers_out.charset.len = ngx_strlen(encoding); @@ -841,6 +822,8 @@ ngx_http_xslt_apply_stylesheet(ngx_http_ return b; } + b->last_buf = 1; + if (type) { len = ngx_strlen(type); @@ -855,6 +838,8 @@ ngx_http_xslt_apply_stylesheet(ngx_http_ r->headers_out.content_type.data = (u_char *) "text/html"; } + r->headers_out.content_type_lowcase = NULL; + return b; } @@ -863,30 +848,25 @@ static ngx_int_t ngx_http_xslt_params(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx, ngx_array_t *params) { - u_char *p, *last, *value, *dst, *src, **s; - size_t len; - ngx_uint_t i; - ngx_str_t string; - ngx_http_xslt_param_t *param; + u_char *p, *last, *value, *dst, *src, **s; + size_t len; + ngx_uint_t i; + ngx_str_t string; + ngx_http_complex_value_t *param; param = params->elts; for (i = 0; i < params->nelts; i++) { - if (ngx_http_script_run(r, &string, param[i].lengths->elts, 1, - param[i].values->elts) - == NULL) - { + if (ngx_http_complex_value(r, ¶m[i], &string) != NGX_OK) { return NGX_ERROR; } - last = string.data + string.len - 1; - *last = '\0'; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "xslt filter param: \"%s\"", string.data); p = string.data; + last = string.data + string.len - 1; while (p && *p) { @@ -1069,8 +1049,8 @@ ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_pool_cleanup_t *cln; ngx_http_xslt_file_t *file; ngx_http_xslt_sheet_t *sheet; - ngx_http_xslt_param_t *param; - ngx_http_script_compile_t sc; + ngx_http_complex_value_t *param; + ngx_http_compile_complex_value_t ccv; ngx_http_xslt_filter_main_conf_t *xmcf; value = cf->args->elts; @@ -1138,7 +1118,7 @@ found: } if (ngx_array_init(&sheet->params, cf->pool, n - 2, - sizeof(ngx_http_xslt_param_t)) + sizeof(ngx_http_complex_value_t)) != NGX_OK) { return NGX_CONF_ERROR; @@ -1151,22 +1131,17 @@ found: return NGX_CONF_ERROR; } - param->lengths = NULL; - param->values = NULL; - - ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); - sc.cf = cf; - sc.source = &value[i]; - sc.lengths = ¶m->lengths; - sc.values = ¶m->values; - sc.variables = ngx_http_script_variables_count(&value[i]); - sc.complete_lengths = 1; - sc.complete_values = 1; + ccv.cf = cf; + ccv.value = &value[i]; + ccv.complex_value = param; + ccv.zero = 1; - if (ngx_http_script_compile(&sc) != NGX_OK) { + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } + } return NGX_CONF_OK; @@ -1194,7 +1169,7 @@ ngx_http_xslt_filter_create_main_conf(ng conf = ngx_palloc(cf->pool, sizeof(ngx_http_xslt_filter_main_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } if (ngx_array_init(&conf->dtd_files, cf->pool, 1, @@ -1222,7 +1197,7 @@ ngx_http_xslt_filter_create_conf(ngx_con conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_xslt_filter_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* @@ -1269,6 +1244,10 @@ ngx_http_xslt_filter_init(ngx_conf_t *cf { xmlInitParser(); +#if (NGX_HAVE_EXSLT) + exsltRegisterAll(); +#endif + ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_xslt_header_filter; diff --git a/src/http/modules/perl/nginx.pm b/src/http/modules/perl/nginx.pm --- a/src/http/modules/perl/nginx.pm +++ b/src/http/modules/perl/nginx.pm @@ -47,7 +47,7 @@ our @EXPORT = qw( HTTP_INSUFFICIENT_STORAGE ); -our $VERSION = '0.7.19'; +our $VERSION = '0.8.5'; require XSLoader; XSLoader::load('nginx', $VERSION); diff --git a/src/http/modules/perl/nginx.xs b/src/http/modules/perl/nginx.xs --- a/src/http/modules/perl/nginx.xs +++ b/src/http/modules/perl/nginx.xs @@ -607,7 +607,7 @@ sendfile(r, filename, offset = -1, bytes ngx_http_request_t *r; char *filename; - int offset; + off_t offset; size_t bytes; ngx_str_t path; ngx_buf_t *b; @@ -662,7 +662,7 @@ sendfile(r, filename, offset = -1, bytes } ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, - ngx_open_file_n " \"%s\" failed", filename); + "%s \"%s\" failed", of.failed, filename); XSRETURN_EMPTY; } diff --git a/src/http/modules/perl/ngx_http_perl_module.c b/src/http/modules/perl/ngx_http_perl_module.c --- a/src/http/modules/perl/ngx_http_perl_module.c +++ b/src/http/modules/perl/ngx_http_perl_module.c @@ -154,10 +154,14 @@ static ngx_http_ssi_command_t ngx_http_ #endif -static ngx_str_t ngx_null_name = ngx_null_string; +static ngx_str_t ngx_null_name = ngx_null_string; + +static HV *nginx_stash; +#if (NGX_HAVE_PERL_MULTIPLICITY) +static ngx_uint_t ngx_perl_term; +#endif -static HV *nginx_stash; static void ngx_http_perl_xs_init(pTHX) @@ -171,7 +175,6 @@ ngx_http_perl_xs_init(pTHX) static ngx_int_t ngx_http_perl_handler(ngx_http_request_t *r) { - /* TODO: Win32 */ if (r->zero_in_uri) { return NGX_HTTP_NOT_FOUND; } @@ -285,7 +288,7 @@ ngx_http_perl_sleep_handler(ngx_http_req return; } - if (ngx_handle_write_event(wev, 0) == NGX_ERROR) { + if (ngx_handle_write_event(wev, 0) != NGX_OK) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); } } @@ -781,7 +784,7 @@ ngx_http_perl_create_main_conf(ngx_conf_ pmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_perl_main_conf_t)); if (pmcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } if (ngx_array_init(&pmcf->requires, cf->pool, 1, sizeof(u_char *)) @@ -821,6 +824,12 @@ ngx_http_perl_cleanup_perl(void *data) (void) perl_destruct(perl); perl_free(perl); + + if (ngx_perl_term) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "perl term"); + + PERL_SYS_TERM(); + } } #endif @@ -860,7 +869,7 @@ ngx_http_perl_create_loc_conf(ngx_conf_t plcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_perl_loc_conf_t)); if (plcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* @@ -1041,15 +1050,13 @@ ngx_http_perl_init_worker(ngx_cycle_t *c pmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_perl_module); - { - - dTHXa(pmcf->perl); - PERL_SET_CONTEXT(pmcf->perl); + if (pmcf) { + dTHXa(pmcf->perl); + PERL_SET_CONTEXT(pmcf->perl); - /* set worker's $$ */ + /* set worker's $$ */ - sv_setiv(GvSV(gv_fetchpv("$", TRUE, SVt_PV)), (I32) ngx_pid); - + sv_setiv(GvSV(gv_fetchpv("$", TRUE, SVt_PV)), (I32) ngx_pid); } return NGX_OK; @@ -1059,16 +1066,24 @@ ngx_http_perl_init_worker(ngx_cycle_t *c static void ngx_http_perl_exit(ngx_cycle_t *cycle) { +#if (NGX_HAVE_PERL_MULTIPLICITY) + + ngx_perl_term = 1; + +#else ngx_http_perl_main_conf_t *pmcf; pmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_perl_module); - { + if (pmcf && nginx_stash) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cycle->log, 0, "perl term"); - dTHXa(pmcf->perl); - PERL_SET_CONTEXT(pmcf->perl); + (void) perl_destruct(pmcf->perl); - PERL_SYS_TERM(); + perl_free(pmcf->perl); + PERL_SYS_TERM(); } + +#endif } diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -18,12 +18,18 @@ static ngx_int_t ngx_http_init_phase_han ngx_http_core_main_conf_t *cmcf); static ngx_int_t ngx_http_init_server_lists(ngx_conf_t *cf, - ngx_array_t *servers, ngx_array_t *in_ports); + ngx_array_t *servers, ngx_array_t *ports); +static ngx_int_t ngx_http_add_ports(ngx_conf_t *cf, + ngx_http_core_srv_conf_t *cscf, ngx_array_t *ports, + ngx_http_listen_t *listen); +static ngx_int_t ngx_http_add_addresses(ngx_conf_t *cf, + ngx_http_core_srv_conf_t *cscf, ngx_http_conf_port_t *port, + ngx_http_listen_t *listen); static ngx_int_t ngx_http_add_address(ngx_conf_t *cf, - ngx_http_core_srv_conf_t *cscf, ngx_http_conf_in_port_t *in_port, + ngx_http_core_srv_conf_t *cscf, ngx_http_conf_port_t *port, ngx_http_listen_t *listen); static ngx_int_t ngx_http_add_names(ngx_conf_t *cf, - ngx_http_core_srv_conf_t *cscf, ngx_http_conf_in_addr_t *in_addr); + ngx_http_core_srv_conf_t *cscf, ngx_http_conf_addr_t *addr); static char *ngx_http_merge_locations(ngx_conf_t *cf, ngx_queue_t *locations, void **loc_conf, ngx_http_module_t *module, @@ -43,13 +49,23 @@ static ngx_http_location_tree_node_t * size_t prefix); static ngx_int_t ngx_http_optimize_servers(ngx_conf_t *cf, - ngx_http_core_main_conf_t *cmcf, ngx_array_t *in_ports); -static ngx_int_t ngx_http_cmp_conf_in_addrs(const void *one, const void *two); + ngx_http_core_main_conf_t *cmcf, ngx_array_t *ports); +static ngx_int_t ngx_http_server_names(ngx_conf_t *cf, + ngx_http_core_main_conf_t *cmcf, ngx_http_conf_addr_t *addr); +static ngx_int_t ngx_http_cmp_conf_addrs(const void *one, const void *two); static int ngx_libc_cdecl ngx_http_cmp_dns_wildcards(const void *one, const void *two); static ngx_int_t ngx_http_init_listening(ngx_conf_t *cf, - ngx_http_conf_in_port_t *in_port); + ngx_http_conf_port_t *port); +static ngx_listening_t *ngx_http_add_listening(ngx_conf_t *cf, + ngx_http_conf_addr_t *addr); +static ngx_int_t ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport, + ngx_http_conf_addr_t *addr); +#if (NGX_HAVE_INET6) +static ngx_int_t ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport, + ngx_http_conf_addr_t *addr); +#endif ngx_uint_t ngx_http_max_module; @@ -106,7 +122,7 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma char *rv; ngx_uint_t mi, m, s; ngx_conf_t pcf; - ngx_array_t in_ports; + ngx_array_t ports; ngx_http_module_t *module; ngx_http_conf_ctx_t *ctx; ngx_http_core_loc_conf_t *clcf; @@ -351,18 +367,14 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma * to find quickly the server core module configuration at run-time */ - /* AF_INET only */ - - if (ngx_http_init_server_lists(cf, &cmcf->servers, &in_ports) != NGX_OK) { + if (ngx_http_init_server_lists(cf, &cmcf->servers, &ports) != NGX_OK) { return NGX_CONF_ERROR; } /* optimize the lists of ports, addresses and server names */ - /* AF_INET only */ - - if (ngx_http_optimize_servers(cf, cmcf, &in_ports) != NGX_OK) { + if (ngx_http_optimize_servers(cf, cmcf, &ports) != NGX_OK) { return NGX_CONF_ERROR; } @@ -489,7 +501,7 @@ ngx_http_init_phase_handlers(ngx_conf_t use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0; use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0; - n = use_rewrite + use_access + 1; /* find config phase */ + n = use_rewrite + use_access + cmcf->try_files + 1 /* find config phase */; for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) { n += cmcf->phases[i].handlers.nelts; @@ -558,6 +570,15 @@ ngx_http_init_phase_handlers(ngx_conf_t continue; + case NGX_HTTP_TRY_FILES_PHASE: + if (cmcf->try_files) { + ph->checker = ngx_http_core_try_files_phase; + n++; + ph++; + } + + continue; + case NGX_HTTP_CONTENT_PHASE: checker = ngx_http_core_content_phase; break; @@ -1090,16 +1111,13 @@ inclusive: static ngx_int_t ngx_http_init_server_lists(ngx_conf_t *cf, ngx_array_t *servers, - ngx_array_t *in_ports) + ngx_array_t *ports) { - ngx_uint_t s, l, p, a; + ngx_uint_t s, i; ngx_http_listen_t *listen; - ngx_http_conf_in_port_t *in_port; - ngx_http_conf_in_addr_t *in_addr; ngx_http_core_srv_conf_t **cscfp; - if (ngx_array_init(in_ports, cf->temp_pool, 2, - sizeof(ngx_http_conf_in_port_t)) + if (ngx_array_init(ports, cf->temp_pool, 2, sizeof(ngx_http_conf_port_t)) != NGX_OK) { return NGX_ERROR; @@ -1113,92 +1131,11 @@ ngx_http_init_server_lists(ngx_conf_t *c /* "listen" directives */ listen = cscfp[s]->listen.elts; - for (l = 0; l < cscfp[s]->listen.nelts; l++) { - - /* AF_INET only */ - - in_port = in_ports->elts; - for (p = 0; p < in_ports->nelts; p++) { - - if (listen[l].port != in_port[p].port) { - continue; - } - - /* the port is already in the port list */ - - in_addr = in_port[p].addrs.elts; - for (a = 0; a < in_port[p].addrs.nelts; a++) { - - if (listen[l].addr != in_addr[a].addr) { - continue; - } - - /* the address is already in the address list */ - - if (ngx_http_add_names(cf, cscfp[s], &in_addr[a]) != NGX_OK) - { - return NGX_ERROR; - } - - /* - * check the duplicate "default" server - * for this address:port - */ - - if (listen[l].conf.default_server) { + for (i = 0; i < cscfp[s]->listen.nelts; i++) { - if (in_addr[a].default_server) { - ngx_log_error(NGX_LOG_ERR, cf->log, 0, - "the duplicate default server in %s:%ui", - listen[l].file_name, listen[l].line); - - return NGX_ERROR; - } - - in_addr[a].core_srv_conf = cscfp[s]; - in_addr[a].default_server = 1; -#if (NGX_HTTP_SSL) - in_addr[a].ssl = listen[l].conf.ssl; -#endif - in_addr[a].listen_conf = &listen[l].conf; - } - - goto found; - } - - /* - * add the address to the addresses list that - * bound to this port - */ - - if (ngx_http_add_address(cf, cscfp[s], &in_port[p], &listen[l]) - != NGX_OK) - { - return NGX_ERROR; - } - - goto found; - } - - /* add the port to the in_port list */ - - in_port = ngx_array_push(in_ports); - if (in_port == NULL) { + if (ngx_http_add_ports(cf, cscfp[s], ports, &listen[i]) != NGX_OK) { return NGX_ERROR; } - - in_port->port = listen[l].port; - in_port->addrs.elts = NULL; - - if (ngx_http_add_address(cf, cscfp[s], in_port, &listen[l]) - != NGX_OK) - { - return NGX_ERROR; - } - - found: - - continue; } } @@ -1206,67 +1143,203 @@ ngx_http_init_server_lists(ngx_conf_t *c } +static ngx_int_t +ngx_http_add_ports(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, + ngx_array_t *ports, ngx_http_listen_t *listen) +{ + in_port_t p; + ngx_uint_t i; + struct sockaddr *sa; + struct sockaddr_in *sin; + ngx_http_conf_port_t *port; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + sa = (struct sockaddr *) &listen->sockaddr; + + switch (sa->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) sa; + p = sin6->sin6_port; + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) sa; + p = sin->sin_port; + break; + } + + port = ports->elts; + for (i = 0; i < ports->nelts; i++) { + + if (p != port[i].port || sa->sa_family != port[i].family) { + continue; + } + + /* a port is already in the port list */ + + return ngx_http_add_addresses(cf, cscf, &port[i], listen); + } + + /* 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; + port->addrs.elts = NULL; + + return ngx_http_add_address(cf, cscf, port, listen); +} + + +static ngx_int_t +ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, + ngx_http_conf_port_t *port, ngx_http_listen_t *listen) +{ + u_char *p; + size_t len, off; + ngx_uint_t i; + struct sockaddr *sa; + ngx_http_conf_addr_t *addr; + + /* + * we can not compare whole sockaddr struct's as kernel + * may fill some fields in inherited sockaddr struct's + */ + + sa = (struct sockaddr *) &listen->sockaddr; + + switch (sa->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + off = offsetof(struct sockaddr_in6, sin6_addr); + len = 16; + break; +#endif + + default: /* AF_INET */ + off = offsetof(struct sockaddr_in, sin_addr); + len = 4; + break; + } + + p = listen->sockaddr + off; + + addr = port->addrs.elts; + + for (i = 0; i < port->addrs.nelts; i++) { + + if (ngx_memcmp(p, (u_char *) addr[i].sockaddr + off, len) != 0) { + continue; + } + + /* the address is already in the address list */ + + if (ngx_http_add_names(cf, cscf, &addr[i]) != NGX_OK) { + return NGX_ERROR; + } + + /* check the duplicate "default" server for this address:port */ + + if (listen->conf.default_server) { + + if (addr[i].default_server) { + ngx_log_error(NGX_LOG_ERR, cf->log, 0, + "the duplicate default server in %s:%ui", + listen->file_name, listen->line); + + return NGX_ERROR; + } + + addr[i].core_srv_conf = cscf; + addr[i].default_server = 1; +#if (NGX_HTTP_SSL) + addr[i].ssl = listen->conf.ssl; +#endif + addr[i].listen_conf = &listen->conf; + } + + return NGX_OK; + } + + /* add the address to the addresses list that bound to this port */ + + return ngx_http_add_address(cf, cscf, port, listen); +} + + /* * add the server address, the server names and the server core module - * configurations to the port (in_port) + * configurations to the port list */ static ngx_int_t ngx_http_add_address(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, - ngx_http_conf_in_port_t *in_port, ngx_http_listen_t *listen) + ngx_http_conf_port_t *port, ngx_http_listen_t *listen) { - ngx_http_conf_in_addr_t *in_addr; + ngx_http_conf_addr_t *addr; - if (in_port->addrs.elts == NULL) { - if (ngx_array_init(&in_port->addrs, cf->temp_pool, 4, - sizeof(ngx_http_conf_in_addr_t)) + if (port->addrs.elts == NULL) { + if (ngx_array_init(&port->addrs, cf->temp_pool, 4, + sizeof(ngx_http_conf_addr_t)) != NGX_OK) { return NGX_ERROR; } } - in_addr = ngx_array_push(&in_port->addrs); - if (in_addr == NULL) { + addr = ngx_array_push(&port->addrs); + if (addr == NULL) { return NGX_ERROR; } - in_addr->addr = listen->addr; - in_addr->hash.buckets = NULL; - in_addr->hash.size = 0; - in_addr->wc_head = NULL; - in_addr->wc_tail = NULL; - in_addr->names.elts = NULL; + addr->sockaddr = (struct sockaddr *) &listen->sockaddr; + addr->socklen = listen->socklen; + addr->hash.buckets = NULL; + addr->hash.size = 0; + addr->wc_head = NULL; + addr->wc_tail = NULL; + addr->names.elts = NULL; #if (NGX_PCRE) - in_addr->nregex = 0; - in_addr->regex = NULL; + addr->nregex = 0; + addr->regex = NULL; #endif - in_addr->core_srv_conf = cscf; - in_addr->default_server = listen->conf.default_server; - in_addr->bind = listen->conf.bind; + addr->core_srv_conf = cscf; + addr->default_server = listen->conf.default_server; + addr->bind = listen->conf.bind; + addr->wildcard = listen->conf.wildcard; #if (NGX_HTTP_SSL) - in_addr->ssl = listen->conf.ssl; + addr->ssl = listen->conf.ssl; #endif - in_addr->listen_conf = &listen->conf; + addr->listen_conf = &listen->conf; - return ngx_http_add_names(cf, cscf, in_addr); + return ngx_http_add_names(cf, cscf, addr); } /* * add the server names and the server core module - * configurations to the address:port (in_addr) + * configurations to the address:port */ static ngx_int_t ngx_http_add_names(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, - ngx_http_conf_in_addr_t *in_addr) + ngx_http_conf_addr_t *addr) { ngx_uint_t i; ngx_http_server_name_t *server_names, *name; - if (in_addr->names.elts == NULL) { - if (ngx_array_init(&in_addr->names, cf->temp_pool, 4, + if (addr->names.elts == NULL) { + if (ngx_array_init(&addr->names, cf->temp_pool, 4, sizeof(ngx_http_server_name_t)) != NGX_OK) { @@ -1284,7 +1357,7 @@ ngx_http_add_names(ngx_conf_t *cf, ngx_h ngx_log_debug1(NGX_LOG_DEBUG_HTTP, cf->log, 0, "name: %V", &server_names[i].name); - name = ngx_array_push(&in_addr->names); + name = ngx_array_push(&addr->names); if (name == NULL) { return NGX_ERROR; } @@ -1298,187 +1371,189 @@ ngx_http_add_names(ngx_conf_t *cf, ngx_h static ngx_int_t ngx_http_optimize_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf, - ngx_array_t *in_ports) + ngx_array_t *ports) { - ngx_int_t rc; - ngx_uint_t s, p, a; - ngx_hash_init_t hash; - ngx_http_server_name_t *name; - ngx_hash_keys_arrays_t ha; - ngx_http_conf_in_port_t *in_port; - ngx_http_conf_in_addr_t *in_addr; -#if (NGX_PCRE) - ngx_uint_t regex, i; -#endif + ngx_uint_t s, p, a; + ngx_http_conf_port_t *port; + ngx_http_conf_addr_t *addr; + ngx_http_server_name_t *name; - in_port = in_ports->elts; - for (p = 0; p < in_ports->nelts; p++) { + port = ports->elts; + for (p = 0; p < ports->nelts; p++) { - ngx_sort(in_port[p].addrs.elts, (size_t) in_port[p].addrs.nelts, - sizeof(ngx_http_conf_in_addr_t), ngx_http_cmp_conf_in_addrs); + ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts, + sizeof(ngx_http_conf_addr_t), ngx_http_cmp_conf_addrs); /* - * check whether all name-based servers have - * the same configuraiton as the default server + * check whether all name-based servers have the same + * configuraiton as a default server for given address:port */ - in_addr = in_port[p].addrs.elts; - for (a = 0; a < in_port[p].addrs.nelts; a++) { - - name = in_addr[a].names.elts; - for (s = 0; s < in_addr[a].names.nelts; s++) { - - if (in_addr[a].core_srv_conf != name[s].core_srv_conf) { - goto virtual_names; - } - } + addr = port[p].addrs.elts; + for (a = 0; a < port[p].addrs.nelts; a++) { - /* - * if all name-based servers have the same configuration - * as the default server, then we do not need to check - * them at run-time at all - */ - - in_addr[a].names.nelts = 0; - - continue; - - virtual_names: + name = addr[a].names.elts; + for (s = 0; s < addr[a].names.nelts; s++) { - ngx_memzero(&ha, sizeof(ngx_hash_keys_arrays_t)); - - ha.temp_pool = ngx_create_pool(16384, cf->log); - if (ha.temp_pool == NULL) { - return NGX_ERROR; - } - - ha.pool = cf->pool; - - if (ngx_hash_keys_array_init(&ha, NGX_HASH_LARGE) != NGX_OK) { - goto failed; - } - + if (addr[a].core_srv_conf == name[s].core_srv_conf #if (NGX_PCRE) - regex = 0; + && name[s].captures == 0 #endif - - name = in_addr[a].names.elts; - - for (s = 0; s < in_addr[a].names.nelts; s++) { - -#if (NGX_PCRE) - if (name[s].regex) { - regex++; + ) + { continue; } -#endif - rc = ngx_hash_add_key(&ha, &name[s].name, name[s].core_srv_conf, - NGX_HASH_WILDCARD_KEY); - - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - - if (rc == NGX_DECLINED) { - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "invalid server name or wildcard \"%V\" on %s", - &name[s].name, in_addr[a].listen_conf->addr); + if (ngx_http_server_names(cf, cmcf, &addr[a]) != NGX_OK) { return NGX_ERROR; } - if (rc == NGX_BUSY) { - ngx_log_error(NGX_LOG_WARN, cf->log, 0, - "conflicting server name \"%V\" on %s, ignored", - &name[s].name, in_addr[a].listen_conf->addr); - } + break; } + } - hash.key = ngx_hash_key_lc; - hash.max_size = cmcf->server_names_hash_max_size; - hash.bucket_size = cmcf->server_names_hash_bucket_size; - hash.name = "server_names_hash"; - hash.pool = cf->pool; + if (ngx_http_init_listening(cf, &port[p]) != NGX_OK) { + return NGX_ERROR; + } + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_server_names(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf, + ngx_http_conf_addr_t *addr) +{ + ngx_int_t rc; + ngx_uint_t s; + ngx_hash_init_t hash; + ngx_hash_keys_arrays_t ha; + ngx_http_server_name_t *name; +#if (NGX_PCRE) + ngx_uint_t regex, i; + + regex = 0; +#endif - if (ha.keys.nelts) { - hash.hash = &in_addr[a].hash; - hash.temp_pool = NULL; + ngx_memzero(&ha, sizeof(ngx_hash_keys_arrays_t)); + + ha.temp_pool = ngx_create_pool(16384, cf->log); + if (ha.temp_pool == NULL) { + return NGX_ERROR; + } + + ha.pool = cf->pool; + + if (ngx_hash_keys_array_init(&ha, NGX_HASH_LARGE) != NGX_OK) { + goto failed; + } + + name = addr->names.elts; - if (ngx_hash_init(&hash, ha.keys.elts, ha.keys.nelts) != NGX_OK) - { - goto failed; - } - } + for (s = 0; s < addr->names.nelts; s++) { - if (ha.dns_wc_head.nelts) { +#if (NGX_PCRE) + if (name[s].regex) { + regex++; + continue; + } +#endif - ngx_qsort(ha.dns_wc_head.elts, - (size_t) ha.dns_wc_head.nelts, - sizeof(ngx_hash_key_t), - ngx_http_cmp_dns_wildcards); + rc = ngx_hash_add_key(&ha, &name[s].name, name[s].core_srv_conf, + NGX_HASH_WILDCARD_KEY); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } - hash.hash = NULL; - hash.temp_pool = ha.temp_pool; + if (rc == NGX_DECLINED) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "invalid server name or wildcard \"%V\" on %s", + &name[s].name, addr->listen_conf->addr); + return NGX_ERROR; + } + + if (rc == NGX_BUSY) { + ngx_log_error(NGX_LOG_WARN, cf->log, 0, + "conflicting server name \"%V\" on %s, ignored", + &name[s].name, addr->listen_conf->addr); + } + } - if (ngx_hash_wildcard_init(&hash, ha.dns_wc_head.elts, - ha.dns_wc_head.nelts) - != NGX_OK) - { - goto failed; - } + hash.key = ngx_hash_key_lc; + hash.max_size = cmcf->server_names_hash_max_size; + hash.bucket_size = cmcf->server_names_hash_bucket_size; + hash.name = "server_names_hash"; + hash.pool = cf->pool; - in_addr[a].wc_head = (ngx_hash_wildcard_t *) hash.hash; - } + if (ha.keys.nelts) { + hash.hash = &addr->hash; + hash.temp_pool = NULL; - if (ha.dns_wc_tail.nelts) { + if (ngx_hash_init(&hash, ha.keys.elts, ha.keys.nelts) != NGX_OK) { + goto failed; + } + } + + if (ha.dns_wc_head.nelts) { - ngx_qsort(ha.dns_wc_tail.elts, - (size_t) ha.dns_wc_tail.nelts, - sizeof(ngx_hash_key_t), - ngx_http_cmp_dns_wildcards); + ngx_qsort(ha.dns_wc_head.elts, (size_t) ha.dns_wc_head.nelts, + sizeof(ngx_hash_key_t), ngx_http_cmp_dns_wildcards); + + hash.hash = NULL; + hash.temp_pool = ha.temp_pool; - hash.hash = NULL; - hash.temp_pool = ha.temp_pool; + if (ngx_hash_wildcard_init(&hash, ha.dns_wc_head.elts, + ha.dns_wc_head.nelts) + != NGX_OK) + { + goto failed; + } + + addr->wc_head = (ngx_hash_wildcard_t *) hash.hash; + } + + if (ha.dns_wc_tail.nelts) { - if (ngx_hash_wildcard_init(&hash, ha.dns_wc_tail.elts, - ha.dns_wc_tail.nelts) - != NGX_OK) - { - goto failed; - } + ngx_qsort(ha.dns_wc_tail.elts, (size_t) ha.dns_wc_tail.nelts, + sizeof(ngx_hash_key_t), ngx_http_cmp_dns_wildcards); + + hash.hash = NULL; + hash.temp_pool = ha.temp_pool; - in_addr[a].wc_tail = (ngx_hash_wildcard_t *) hash.hash; - } + if (ngx_hash_wildcard_init(&hash, ha.dns_wc_tail.elts, + ha.dns_wc_tail.nelts) + != NGX_OK) + { + goto failed; + } - ngx_destroy_pool(ha.temp_pool); + addr->wc_tail = (ngx_hash_wildcard_t *) hash.hash; + } + + ngx_destroy_pool(ha.temp_pool); #if (NGX_PCRE) - if (regex == 0) { - continue; - } - - in_addr[a].nregex = regex; - in_addr[a].regex = ngx_palloc(cf->pool, - regex * sizeof(ngx_http_server_name_t)); + if (regex == 0) { + return NGX_OK; + } - if (in_addr[a].regex == NULL) { - return NGX_ERROR; - } + addr->nregex = regex; + addr->regex = ngx_palloc(cf->pool, regex * sizeof(ngx_http_server_name_t)); + if (addr->regex == NULL) { + return NGX_ERROR; + } - for (i = 0, s = 0; s < in_addr[a].names.nelts; s++) { - if (name[s].regex) { - in_addr[a].regex[i++] = name[s]; - } - } -#endif - } - - if (ngx_http_init_listening(cf, &in_port[p]) != NGX_OK) { - return NGX_ERROR; + for (i = 0, s = 0; s < addr->names.nelts; s++) { + if (name[s].regex) { + addr->regex[i++] = name[s]; } } +#endif + return NGX_OK; failed: @@ -1490,15 +1565,15 @@ failed: static ngx_int_t -ngx_http_cmp_conf_in_addrs(const void *one, const void *two) +ngx_http_cmp_conf_addrs(const void *one, const void *two) { - ngx_http_conf_in_addr_t *first, *second; + ngx_http_conf_addr_t *first, *second; - first = (ngx_http_conf_in_addr_t *) one; - second = (ngx_http_conf_in_addr_t *) two; + first = (ngx_http_conf_addr_t *) one; + second = (ngx_http_conf_addr_t *) two; - if (first->addr == INADDR_ANY) { - /* the INADDR_ANY must be the last resort, shift it to the end */ + if (first->wildcard) { + /* a wildcard address must be the last resort, shift it to the end */ return 1; } @@ -1531,171 +1606,259 @@ ngx_http_cmp_dns_wildcards(const void *o static ngx_int_t -ngx_http_init_listening(ngx_conf_t *cf, ngx_http_conf_in_port_t *in_port) +ngx_http_init_listening(ngx_conf_t *cf, ngx_http_conf_port_t *port) { - ngx_uint_t i, a, last, bind_all, done; + ngx_uint_t i, last, bind_wildcard; ngx_listening_t *ls; - ngx_http_in_port_t *hip; - ngx_http_conf_in_addr_t *in_addr; - ngx_http_virtual_names_t *vn; - ngx_http_core_loc_conf_t *clcf; - ngx_http_core_srv_conf_t *cscf; + ngx_http_port_t *hport; + ngx_http_conf_addr_t *addr; - in_addr = in_port->addrs.elts; - last = in_port->addrs.nelts; + addr = port->addrs.elts; + last = port->addrs.nelts; /* - * if there is a binding to a "*:port" then we need to bind() - * to the "*:port" only and ignore other bindings + * If there is a binding to an "*:port" then we need to bind() to + * the "*:port" only and ignore other implicit bindings. The bindings + * have been already sorted: explicit bindings are on the start, then + * implicit bindings go, and wildcard binding is in the end. */ - if (in_addr[last - 1].addr == INADDR_ANY) { - in_addr[last - 1].bind = 1; - bind_all = 0; + if (addr[last - 1].wildcard) { + addr[last - 1].bind = 1; + bind_wildcard = 1; } else { - bind_all = 1; + bind_wildcard = 0; } - a = 0; + i = 0; - while (a < last) { + while (i < last) { - if (!bind_all && !in_addr[a].bind) { - a++; + if (bind_wildcard && !addr[i].bind) { + i++; continue; } - ls = ngx_listening_inet_stream_socket(cf, in_addr[a].addr, - in_port->port); + ls = ngx_http_add_listening(cf, &addr[i]); if (ls == NULL) { return NGX_ERROR; } - ls->addr_ntop = 1; - - ls->handler = ngx_http_init_connection; - - cscf = in_addr[a].core_srv_conf; - ls->pool_size = cscf->connection_pool_size; - ls->post_accept_timeout = cscf->client_header_timeout; - - clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index]; - - ls->log = *clcf->err_log; - ls->log.data = &ls->addr_text; - ls->log.handler = ngx_accept_log_error; - -#if (NGX_WIN32) - { - ngx_iocp_conf_t *iocpcf; - - iocpcf = ngx_event_get_conf(cf->cycle->conf_ctx, ngx_iocp_module); - if (iocpcf->acceptex_read) { - ls->post_accept_buffer_size = cscf->client_header_buffer_size; - } - } -#endif - - ls->backlog = in_addr[a].listen_conf->backlog; - ls->rcvbuf = in_addr[a].listen_conf->rcvbuf; - ls->sndbuf = in_addr[a].listen_conf->sndbuf; - -#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) - ls->accept_filter = in_addr[a].listen_conf->accept_filter; -#endif - -#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) - ls->deferred_accept = in_addr[a].listen_conf->deferred_accept; -#endif - - hip = ngx_palloc(cf->pool, sizeof(ngx_http_in_port_t)); - if (hip == NULL) { - return NGX_ERROR; - } - - hip->port = in_port->port; - - hip->port_text.data = ngx_pnalloc(cf->pool, 7); - if (hip->port_text.data == NULL) { + hport = ngx_pcalloc(cf->pool, sizeof(ngx_http_port_t)); + if (hport == NULL) { return NGX_ERROR; } - ls->servers = hip; - - hip->port_text.len = ngx_sprintf(hip->port_text.data, ":%d", hip->port) - - hip->port_text.data; - - in_addr = in_port->addrs.elts; + ls->servers = hport; - if (in_addr[a].bind && in_addr[a].addr != INADDR_ANY) { - hip->naddrs = 1; - done = 0; - - } else if (in_port->addrs.nelts > 1 - && in_addr[last - 1].addr == INADDR_ANY) - { - hip->naddrs = last; - done = 1; + if (i == last - 1) { + hport->naddrs = last; } else { - hip->naddrs = 1; - done = 0; - } - - hip->addrs = ngx_pcalloc(cf->pool, - hip->naddrs * sizeof(ngx_http_in_addr_t)); - if (hip->addrs == NULL) { - return NGX_ERROR; + hport->naddrs = 1; + i = 0; } - for (i = 0; i < hip->naddrs; i++) { - hip->addrs[i].addr = in_addr[i].addr; - hip->addrs[i].core_srv_conf = in_addr[i].core_srv_conf; - -#if (NGX_HTTP_SSL) - hip->addrs[i].ssl = in_addr[i].ssl; -#endif + switch (ls->sockaddr->sa_family) { - if (in_addr[i].hash.buckets == NULL - && (in_addr[i].wc_head == NULL - || in_addr[i].wc_head->hash.buckets == NULL) - && (in_addr[i].wc_head == NULL - || in_addr[i].wc_head->hash.buckets == NULL)) - { - continue; - } - - vn = ngx_palloc(cf->pool, sizeof(ngx_http_virtual_names_t)); - if (vn == NULL) { +#if (NGX_HAVE_INET6) + case AF_INET6: + if (ngx_http_add_addrs6(cf, hport, addr) != NGX_OK) { return NGX_ERROR; } - hip->addrs[i].virtual_names = vn; - - vn->names.hash = in_addr[i].hash; - vn->names.wc_head = in_addr[i].wc_head; - vn->names.wc_tail = in_addr[i].wc_tail; -#if (NGX_PCRE) - vn->nregex = in_addr[i].nregex; - vn->regex = in_addr[i].regex; + break; #endif + default: /* AF_INET */ + if (ngx_http_add_addrs(cf, hport, addr) != NGX_OK) { + return NGX_ERROR; + } + break; } - if (done) { - return NGX_OK; - } - - in_addr++; - in_port->addrs.elts = in_addr; + addr++; last--; - - a = 0; } return NGX_OK; } +static ngx_listening_t * +ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr) +{ + ngx_listening_t *ls; + ngx_http_core_loc_conf_t *clcf; + ngx_http_core_srv_conf_t *cscf; + + ls = ngx_create_listening(cf, addr->sockaddr, addr->socklen); + if (ls == NULL) { + return NULL; + } + + ls->addr_ntop = 1; + + ls->handler = ngx_http_init_connection; + + cscf = addr->core_srv_conf; + ls->pool_size = cscf->connection_pool_size; + ls->post_accept_timeout = cscf->client_header_timeout; + + clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index]; + + ls->logp = clcf->error_log; + ls->log.data = &ls->addr_text; + ls->log.handler = ngx_accept_log_error; + +#if (NGX_WIN32) + { + ngx_iocp_conf_t *iocpcf; + + iocpcf = ngx_event_get_conf(cf->cycle->conf_ctx, ngx_iocp_module); + if (iocpcf->acceptex_read) { + ls->post_accept_buffer_size = cscf->client_header_buffer_size; + } + } +#endif + + ls->backlog = addr->listen_conf->backlog; + ls->rcvbuf = addr->listen_conf->rcvbuf; + ls->sndbuf = addr->listen_conf->sndbuf; + +#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) + ls->accept_filter = addr->listen_conf->accept_filter; +#endif + +#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) + ls->deferred_accept = addr->listen_conf->deferred_accept; +#endif + +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + ls->ipv6only = addr->listen_conf->ipv6only; +#endif + + return ls; +} + + +static ngx_int_t +ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport, + ngx_http_conf_addr_t *addr) +{ + ngx_uint_t i; + ngx_http_in_addr_t *addrs; + struct sockaddr_in *sin; + ngx_http_virtual_names_t *vn; + + hport->addrs = ngx_pcalloc(cf->pool, + hport->naddrs * sizeof(ngx_http_in_addr_t)); + if (hport->addrs == NULL) { + return NGX_ERROR; + } + + addrs = hport->addrs; + + for (i = 0; i < hport->naddrs; i++) { + + sin = (struct sockaddr_in *) addr[i].sockaddr; + addrs[i].addr = sin->sin_addr.s_addr; + addrs[i].conf.core_srv_conf = addr[i].core_srv_conf; +#if (NGX_HTTP_SSL) + addrs[i].conf.ssl = addr[i].ssl; +#endif + + if (addr[i].hash.buckets == NULL + && (addr[i].wc_head == NULL + || addr[i].wc_head->hash.buckets == NULL) + && (addr[i].wc_tail == NULL + || addr[i].wc_tail->hash.buckets == NULL) +#if (NGX_PCRE) + && addr[i].nregex == 0 +#endif + ) + { + continue; + } + + vn = ngx_palloc(cf->pool, sizeof(ngx_http_virtual_names_t)); + if (vn == NULL) { + return NGX_ERROR; + } + + addrs[i].conf.virtual_names = vn; + + vn->names.hash = addr[i].hash; + vn->names.wc_head = addr[i].wc_head; + vn->names.wc_tail = addr[i].wc_tail; +#if (NGX_PCRE) + vn->nregex = addr[i].nregex; + vn->regex = addr[i].regex; +#endif + } + + return NGX_OK; +} + + +#if (NGX_HAVE_INET6) + +static ngx_int_t +ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport, + ngx_http_conf_addr_t *addr) +{ + ngx_uint_t i; + ngx_http_in6_addr_t *addrs6; + struct sockaddr_in6 *sin6; + ngx_http_virtual_names_t *vn; + + hport->addrs = ngx_pcalloc(cf->pool, + hport->naddrs * sizeof(ngx_http_in6_addr_t)); + if (hport->addrs == NULL) { + return NGX_ERROR; + } + + addrs6 = hport->addrs; + + for (i = 0; i < hport->naddrs; i++) { + + sin6 = (struct sockaddr_in6 *) addr[i].sockaddr; + addrs6[i].addr6 = sin6->sin6_addr; + addrs6[i].conf.core_srv_conf = addr[i].core_srv_conf; +#if (NGX_HTTP_SSL) + addrs6[i].conf.ssl = addr[i].ssl; +#endif + + if (addr[i].hash.buckets == NULL + && (addr[i].wc_head == NULL + || addr[i].wc_head->hash.buckets == NULL) + && (addr[i].wc_head == NULL + || addr[i].wc_head->hash.buckets == NULL)) + { + continue; + } + + vn = ngx_palloc(cf->pool, sizeof(ngx_http_virtual_names_t)); + if (vn == NULL) { + return NGX_ERROR; + } + + addrs6[i].conf.virtual_names = vn; + + vn->names.hash = addr[i].hash; + vn->names.wc_head = addr[i].wc_head; + vn->names.wc_tail = addr[i].wc_tail; +#if (NGX_PCRE) + vn->nregex = addr[i].nregex; + vn->regex = addr[i].regex; +#endif + } + + return NGX_OK; +} + +#endif + + char * ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -10,12 +10,13 @@ #include #include -#include -typedef struct ngx_http_request_s ngx_http_request_t; -typedef struct ngx_http_upstream_s ngx_http_upstream_t; -typedef struct ngx_http_log_ctx_s ngx_http_log_ctx_t; +typedef struct ngx_http_request_s ngx_http_request_t; +typedef struct ngx_http_upstream_s ngx_http_upstream_t; +typedef struct ngx_http_cache_s ngx_http_cache_t; +typedef struct ngx_http_file_cache_s ngx_http_file_cache_t; +typedef struct ngx_http_log_ctx_s ngx_http_log_ctx_t; typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); @@ -23,21 +24,18 @@ typedef u_char *(*ngx_http_log_handler_p ngx_http_request_t *sr, u_char *buf, size_t len); -#if (NGX_HTTP_CACHE) -#include -#endif -/* STUB */ -#include - #include #include #include #include #include #include +#include #include -#include +#if (NGX_HTTP_CACHE) +#include +#endif #if (NGX_HTTP_SSI) #include #endif @@ -76,15 +74,23 @@ ngx_int_t ngx_http_parse_header_line(ngx ngx_uint_t allow_underscores); ngx_int_t ngx_http_parse_multi_header_lines(ngx_array_t *headers, ngx_str_t *name, ngx_str_t *value); +ngx_int_t ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, + ngx_str_t *value); +void ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, + ngx_str_t *args); + ngx_int_t ngx_http_find_server_conf(ngx_http_request_t *r); void ngx_http_update_location_config(ngx_http_request_t *r); void ngx_http_handler(ngx_http_request_t *r); +void ngx_http_run_posted_requests(ngx_connection_t *c); +ngx_int_t ngx_http_post_request(ngx_http_request_t *r); void ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc); void ngx_http_empty_handler(ngx_event_t *wev); void ngx_http_request_empty_handler(ngx_http_request_t *r); + #define NGX_HTTP_LAST 1 #define NGX_HTTP_FLUSH 2 @@ -97,6 +103,9 @@ ngx_int_t ngx_http_read_client_request_b ngx_int_t ngx_http_send_header(ngx_http_request_t *r); ngx_int_t ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error); +ngx_int_t ngx_http_filter_finalize_request(ngx_http_request_t *r, + ngx_module_t *m, ngx_int_t error); +void ngx_http_clean_header(ngx_http_request_t *r); time_t ngx_http_parse_time(u_char *value, size_t len); @@ -106,6 +115,7 @@ size_t ngx_http_get_time(char *buf, time ngx_int_t ngx_http_discard_request_body(ngx_http_request_t *r); void ngx_http_block_reading(ngx_http_request_t *r); +void ngx_http_test_reading(ngx_http_request_t *r); char *ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); diff --git a/src/http/ngx_http_cache.c b/src/http/ngx_http_cache.c deleted file mode 100644 --- a/src/http/ngx_http_cache.c +++ /dev/null @@ -1,576 +0,0 @@ - -/* - * Copyright (C) Igor Sysoev - */ - - -#include -#include -#include - - -#if 0 - -static ngx_http_module_t ngx_http_cache_module_ctx = { - NULL, /* pre conf */ - - 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_cache_module = { - NGX_MODULE, - &ngx_http_cache_module_ctx, /* module context */ - NULL, /* module directives */ - NGX_HTTP_MODULE, /* module type */ - NULL, /* init module */ - NULL /* init process */ -}; - -#endif - - -static ngx_int_t ngx_http_cache_create(ngx_http_request_t *r) -{ - ngx_str_t *key; - - if (!(r->cache = ngx_pcalloc(r->pool, sizeof(ngx_http_cache_t)))) { - return NGX_ERROR; - } - - if (ngx_array_init(&r->cache->key, r->pool, 5, sizeof(ngx_str_t)) - == NGX_ERROR) - { - return NGX_ERROR; - } - - /* preallocate the primary key */ - - if (!(key = ngx_array_push(&r->cache->key))) { - return NGX_ERROR; - } - - key->len = 0; - key->data = NULL; - - /* - * we use offsetof() because sizeof() pads the struct size to the int size - */ - - r->cache->header_size = offsetof(ngx_http_cache_header_t, key); - - r->cache->log = r->connection->log; - r->cache->file.log = r->connection->log; - - return NGX_OK; -} - - -ngx_int_t ngx_http_cache_get(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx) -{ - ngx_str_t *key; - ngx_http_cache_t *c; - - if (r->cache == NULL) { - if (ngx_http_cache_create(r) == NGX_ERROR) { - return NGX_ABORT; - } - } - - c = r->cache; - key = c->key.elts; - - if (ctx->primary) { - key[0] = ctx->key; - c->header_size += ctx->key.len; - c->key_len += ctx->key.len; - c->buf = ctx->buf; - - } else { - if (key[0].len == 0) { - key[0] = r->uri; - c->header_size += r->uri.len; - c->key_len += ctx->key.len; - } - - if (!(key = ngx_array_push(&r->cache->key))) { - return NGX_ABORT; - } - - c->header_size += ctx->key.len; - c->key_len += ctx->key.len; - } - -#if 0 - - if (ctx->memory) { - ngx_http_memory_cache_get(r, ctx); - } - -#endif - - if (ctx->file) { - return ngx_http_file_cache_get(r, ctx); - } - - return NGX_DECLINED; -} - - -#if 0 - - -ngx_http_cache_t *ngx_http_cache_get(ngx_http_cache_hash_t *hash, - ngx_http_cleanup_t *cleanup, - ngx_str_t *key, uint32_t *crc) -{ - ngx_uint_t i; - ngx_http_cache_t *c; - - *crc = ngx_crc(key->data, key->len); - - c = hash->elts + *crc % hash->hash * hash->nelts; - - if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) { - return (void *) NGX_ERROR; - } - - for (i = 0; i < hash->nelts; i++) { - if (c[i].crc == *crc - && c[i].key.len == key->len - && ngx_rstrncmp(c[i].key.data, key->data, key->len) == 0) - { -#if 0 - if (c[i].expired) { - ngx_mutex_unlock(&hash->mutex); - return (void *) NGX_AGAIN; - } -#endif - - c[i].refs++; - - if ((!(c[i].notify && (ngx_event_flags & NGX_USE_KQUEUE_EVENT))) - && (ngx_cached_time - c[i].updated >= hash->update)) - { - c[i].expired = 1; - } - - ngx_mutex_unlock(&hash->mutex); - - if (cleanup) { - cleanup->data.cache.hash = hash; - cleanup->data.cache.cache = &c[i]; - cleanup->valid = 1; - cleanup->cache = 1; - } - - return &c[i]; - } - } - - ngx_mutex_unlock(&hash->mutex); - - return NULL; -} - - -ngx_http_cache_t *ngx_http_cache_alloc(ngx_http_cache_hash_t *hash, - ngx_http_cache_t *cache, - ngx_http_cleanup_t *cleanup, - ngx_str_t *key, uint32_t crc, - ngx_str_t *value, ngx_log_t *log) -{ - time_t old; - ngx_uint_t i; - ngx_http_cache_t *c; - - old = ngx_cached_time + 1; - - c = hash->elts + crc % hash->hash * hash->nelts; - - if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) { - return (void *) NGX_ERROR; - } - - if (cache == NULL) { - - /* allocate a new entry */ - - for (i = 0; i < hash->nelts; i++) { - if (c[i].refs > 0) { - /* a busy entry */ - continue; - } - - if (c[i].key.len == 0) { - /* a free entry is found */ - cache = &c[i]; - break; - } - - /* looking for the oldest cache entry */ - - if (old > c[i].accessed) { - - old = c[i].accessed; - cache = &c[i]; - } - } - - if (cache == NULL) { - ngx_mutex_unlock(&hash->mutex); - return NULL; - } - - ngx_http_cache_free(cache, key, value, log); - - if (cache->key.data == NULL) { - cache->key.data = ngx_alloc(key->len, log); - if (cache->key.data == NULL) { - ngx_http_cache_free(cache, NULL, NULL, log); - ngx_mutex_unlock(&hash->mutex); - return NULL; - } - } - - cache->key.len = key->len; - ngx_memcpy(cache->key.data, key->data, key->len); - - } else if (value) { - ngx_http_cache_free(cache, key, value, log); - } - - if (value) { - if (cache->data.value.data == NULL) { - cache->data.value.data = ngx_alloc(value->len, log); - if (cache->data.value.data == NULL) { - ngx_http_cache_free(cache, NULL, NULL, log); - ngx_mutex_unlock(&hash->mutex); - return NULL; - } - } - - cache->data.value.len = value->len; - ngx_memcpy(cache->data.value.data, value->data, value->len); - } - - cache->crc = crc; - cache->key.len = key->len; - - cache->refs = 1; - cache->count = 0; - - cache->deleted = 0; - cache->expired = 0; - cache->memory = 0; - cache->mmap = 0; - cache->notify = 0; - - if (cleanup) { - cleanup->data.cache.hash = hash; - cleanup->data.cache.cache = cache; - cleanup->valid = 1; - cleanup->cache = 1; - } - - ngx_mutex_unlock(&hash->mutex); - - return cache; -} - - -void ngx_http_cache_free(ngx_http_cache_t *cache, - ngx_str_t *key, ngx_str_t *value, ngx_log_t *log) -{ - if (cache->memory) { - if (cache->data.value.data - && (value == NULL || value->len > cache->data.value.len)) - { - ngx_free(cache->data.value.data); - cache->data.value.data = NULL; - } - } - - /* TODO: mmap */ - - cache->data.value.len = 0; - - if (cache->fd != NGX_INVALID_FILE) { - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, - "http cache close fd: %d", cache->fd); - - if (ngx_close_file(cache->fd) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, - ngx_close_file_n " \"%s\" failed", - cache->key.data); - } - - cache->fd = NGX_INVALID_FILE; - } - - if (cache->key.data && (key == NULL || key->len > cache->key.len)) { - ngx_free(cache->key.data); - cache->key.data = NULL; - } - - cache->key.len = 0; - - cache->refs = 0; -} - - -void ngx_http_cache_lock(ngx_http_cache_hash_t *hash, ngx_http_cache_t *cache) -{ - if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) { - return; - } -} - - -void ngx_http_cache_unlock(ngx_http_cache_hash_t *hash, - ngx_http_cache_t *cache, ngx_log_t *log) -{ - if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) { - return; - } - - cache->refs--; - - if (cache->refs == 0 && cache->deleted) { - ngx_http_cache_free(cache, NULL, NULL, log); - } - - ngx_mutex_unlock(&hash->mutex); -} - - -#if 0 - -ngx_http_cache_add_file_event(ngx_http_cache_hash_t *hash, - ngx_http_cache_t *cache) -{ - ngx_event_t *ev; - ngx_http_cache_event_ctx_t *ctx; - - ev = &ngx_cycle->read_events[fd]; - ngx_memzero(ev, sizeof(ngx_event_t); - - ev->data = data; - ev->event_handler = ngx_http_cache_invalidate; - - return ngx_add_event(ev, NGX_VNODE_EVENT, 0); -} - - -void ngx_http_cache_invalidate(ngx_event_t *ev) -{ - ngx_http_cache_event_ctx_t *ctx; - - ctx = ev->data; - - ngx_http_cache_lock(&ctx->hash->mutex); - - if (ctx->cache->refs == 0) - ngx_http_cache_free(ctx->cache, NULL, NULL, ctx->log); - - } else { - ctx->cache->deleted = 1; - } - - ngx_http_cache_unlock(&ctx->hash->mutex); -} - -#endif - - -/* TODO: currently fd only */ - -ngx_int_t ngx_http_send_cached(ngx_http_request_t *r) -{ - ngx_int_t rc; - ngx_hunk_t *h; - ngx_chain_t out; - ngx_http_log_ctx_t *ctx; - - ctx = r->connection->log->data; - ctx->action = "sending response to client"; - - r->headers_out.status = NGX_HTTP_OK; - r->headers_out.content_length_n = r->cache->data.size; - r->headers_out.last_modified_time = r->cache->last_modified; - - if (ngx_http_set_content_type(r) != NGX_OK) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - /* we need to allocate all before the header would be sent */ - - if (!(h = ngx_pcalloc(r->pool, sizeof(ngx_hunk_t)))) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - if (!(h->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t)))) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - rc = ngx_http_send_header(r); - - if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { - return rc; - } - - h->type = r->main ? NGX_HUNK_FILE : NGX_HUNK_FILE|NGX_HUNK_LAST; - - h->file_pos = 0; - h->file_last = r->cache->data.size; - - h->file->fd = r->cache->fd; - h->file->log = r->connection->log; - - out.hunk = h; - out.next = NULL; - - return ngx_http_output_filter(r, &out); -} - - -char *ngx_http_set_cache_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - char *p = conf; - - ngx_int_t i, j, dup, invalid; - ngx_str_t *value, line; - ngx_http_cache_t *c; - ngx_http_cache_hash_t *ch, **chp; - - chp = (ngx_http_cache_hash_t **) (p + cmd->offset); - if (*chp) { - return "is duplicate"; - } - - if (!(ch = ngx_pcalloc(cf->pool, sizeof(ngx_http_cache_hash_t)))) { - return NGX_CONF_ERROR; - } - *chp = ch; - - dup = 0; - invalid = 0; - - value = cf->args->elts; - - for (i = 1; i < cf->args->nelts; i++) { - - if (value[i].data[1] != '=') { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid value \"%s\"", value[i].data); - return NGX_CONF_ERROR; - } - - switch (value[i].data[0]) { - - case 'h': - if (ch->hash) { - dup = 1; - break; - } - - ch->hash = ngx_atoi(value[i].data + 2, value[i].len - 2); - if (ch->hash == (size_t) NGX_ERROR || ch->hash == 0) { - invalid = 1; - break; - } - - continue; - - case 'n': - if (ch->nelts) { - dup = 1; - break; - } - - ch->nelts = ngx_atoi(value[i].data + 2, value[i].len - 2); - if (ch->nelts == (size_t) NGX_ERROR || ch->nelts == 0) { - invalid = 1; - break; - } - - continue; - - case 'l': - if (ch->life) { - dup = 1; - break; - } - - line.len = value[i].len - 2; - line.data = value[i].data + 2; - - ch->life = ngx_parse_time(&line, 1); - if (ch->life == NGX_ERROR || ch->life == 0) { - invalid = 1; - break; - } - - continue; - - case 'u': - if (ch->update) { - dup = 1; - break; - } - - line.len = value[i].len - 2; - line.data = value[i].data + 2; - - ch->update = ngx_parse_time(&line, 1); - if (ch->update == NGX_ERROR || ch->update == 0) { - invalid = 1; - break; - } - - continue; - - default: - invalid = 1; - } - - if (dup) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "duplicate value \"%s\"", value[i].data); - return NGX_CONF_ERROR; - } - - if (invalid) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid value \"%s\"", value[i].data); - return NGX_CONF_ERROR; - } - } - - ch->elts = ngx_pcalloc(cf->pool, - ch->hash * ch->nelts * sizeof(ngx_http_cache_t)); - if (ch->elts == NULL) { - return NGX_CONF_ERROR; - } - - for (i = 0; i < (ngx_int_t) ch->hash; i++) { - c = ch->elts + i * ch->nelts; - - for (j = 0; j < (ngx_int_t) ch->nelts; j++) { - c[j].fd = NGX_INVALID_FILE; - } - } - - return NGX_CONF_OK; -} - - -#endif diff --git a/src/http/ngx_http_cache.h b/src/http/ngx_http_cache.h --- a/src/http/ngx_http_cache.h +++ b/src/http/ngx_http_cache.h @@ -13,145 +13,126 @@ #include -/* - * The 3 bits allows the 7 uses before the cache entry allocation. - * We can use maximum 7 bits, i.e up to the 127 uses. - */ -#define NGX_HTTP_CACHE_LAZY_ALLOCATION_BITS 3 +#define NGX_HTTP_CACHE_MISS 1 +#define NGX_HTTP_CACHE_EXPIRED 2 +#define NGX_HTTP_CACHE_STALE 3 +#define NGX_HTTP_CACHE_UPDATING 4 +#define NGX_HTTP_CACHE_HIT 5 + +#define NGX_HTTP_CACHE_KEY_LEN 16 typedef struct { - uint32_t crc; - ngx_str_t key; - time_t accessed; - - unsigned refs:20; /* 1048576 references */ - - unsigned count:NGX_HTTP_CACHE_LAZY_ALLOCATION_BITS; - - unsigned deleted:1; - unsigned expired:1; - unsigned memory:1; - unsigned mmap:1; - unsigned notify:1; - - ngx_fd_t fd; -#if (NGX_USE_HTTP_FILE_CACHE_UNIQ) - ngx_file_uniq_t uniq; /* no needed with kqueue */ -#endif - time_t last_modified; - time_t updated; - - union { - off_t size; - ngx_str_t value; - } data; -} ngx_http_cache_entry_t; - - -typedef struct { - time_t expires; - time_t last_modified; - time_t date; - off_t length; - size_t key_len; - char key[1]; -} ngx_http_cache_header_t; - - -#define NGX_HTTP_CACHE_HASH 7 -#define NGX_HTTP_CACHE_NELTS 4 - -typedef struct { - ngx_http_cache_entry_t *elts; - size_t hash; - size_t nelts; - time_t life; - time_t update; -#if (NGX_THREADS) - ngx_mutex_t mutex; -#endif - ngx_pool_t *pool; -} ngx_http_cache_hash_t; + ngx_uint_t status; + time_t valid; +} ngx_http_cache_valid_t; typedef struct { - ngx_http_cache_hash_t *hash; - ngx_http_cache_entry_t *cache; - ngx_file_t file; - ngx_array_t key; - uint32_t crc; - u_char md5[16]; - ngx_path_t *path; - ngx_buf_t *buf; - time_t expires; - time_t last_modified; - time_t date; - off_t length; - size_t key_len; - size_t file_start; - ngx_file_uniq_t uniq; - ngx_log_t *log; + ngx_rbtree_node_t node; + ngx_queue_t queue; + + u_char key[NGX_HTTP_CACHE_KEY_LEN + - sizeof(ngx_rbtree_key_t)]; + + unsigned count:20; + unsigned uses:10; + unsigned valid_msec:10; + unsigned error:10; + unsigned exists:1; + unsigned updating:1; + /* 12 unused bits */ + + ngx_file_uniq_t uniq; + time_t expire; + time_t valid_sec; + size_t body_start; + off_t length; +} ngx_http_file_cache_node_t; + - /* STUB */ - ssize_t header_size; - ngx_str_t key0; -} ngx_http_cache_t; +struct ngx_http_cache_s { + ngx_file_t file; + ngx_array_t keys; + uint32_t crc32; + u_char key[NGX_HTTP_CACHE_KEY_LEN]; + + ngx_file_uniq_t uniq; + time_t valid_sec; + time_t last_modified; + time_t date; + + size_t header_start; + size_t body_start; + off_t length; + + ngx_uint_t min_uses; + ngx_uint_t error; + ngx_uint_t valid_msec; + + ngx_buf_t *buf; + + ngx_http_file_cache_t *file_cache; + ngx_http_file_cache_node_t *node; + + unsigned updated:1; + unsigned exists:1; + unsigned temp_file:1; +}; typedef struct { - ngx_path_t *path; - ngx_str_t key; - ngx_buf_t *buf; - - unsigned file:1; - unsigned memory:1; - unsigned primary:1; -} ngx_http_cache_ctx_t; + time_t valid_sec; + time_t last_modified; + time_t date; + uint32_t crc32; + u_short valid_msec; + u_short header_start; + u_short body_start; +} ngx_http_file_cache_header_t; -#define NGX_HTTP_CACHE_STALE 1 -#define NGX_HTTP_CACHE_AGED 2 -#define NGX_HTTP_CACHE_THE_SAME 3 - - -ngx_int_t ngx_http_cache_get(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx); - -ngx_int_t ngx_http_file_cache_get(ngx_http_request_t *r, - ngx_http_cache_ctx_t *ctx); - -ngx_int_t ngx_http_file_cache_open(ngx_http_cache_t *c); - -ngx_int_t ngx_http_cache_cleaner_handler(ngx_gc_t *gc, ngx_str_t *name, - ngx_dir_t *dir); +typedef struct { + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; + ngx_queue_t queue; + ngx_atomic_t cold; + off_t size; +} ngx_http_file_cache_sh_t; -#if 0 +struct ngx_http_file_cache_s { + ngx_http_file_cache_sh_t *sh; + ngx_slab_pool_t *shpool; -ngx_http_cache_t *ngx_http_cache_get(ngx_http_cache_hash_t *cache, - ngx_http_cleanup_t *cleanup, - ngx_str_t *key, uint32_t *crc); + ngx_path_t *path; -ngx_http_cache_t *ngx_http_cache_alloc(ngx_http_cache_hash_t *hash, - ngx_http_cache_t *cache, - ngx_http_cleanup_t *cleanup, - ngx_str_t *key, uint32_t crc, - ngx_str_t *value, ngx_log_t *log); -void ngx_http_cache_free(ngx_http_cache_t *cache, - ngx_str_t *key, ngx_str_t *value, ngx_log_t *log); -void ngx_http_cache_lock(ngx_http_cache_hash_t *hash, ngx_http_cache_t *cache); -void ngx_http_cache_unlock(ngx_http_cache_hash_t *hash, - ngx_http_cache_t *cache, ngx_log_t *log); + off_t max_size; + size_t bsize; + + time_t inactive; -int ngx_http_cache_update_file(ngx_http_request_t *r,ngx_http_cache_ctx_t *ctx, - ngx_str_t *temp_file); + ngx_msec_t last; + ngx_uint_t files; -int ngx_http_send_cached(ngx_http_request_t *r); + ngx_shm_zone_t *shm_zone; +}; -char *ngx_http_set_cache_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +void ngx_http_file_cache_create_key(ngx_http_request_t *r); +ngx_int_t ngx_http_file_cache_open(ngx_http_request_t *r); +void ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf); +void ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf); +ngx_int_t ngx_http_cache_send(ngx_http_request_t *); +void ngx_http_file_cache_free(ngx_http_request_t *r, ngx_temp_file_t *tf); +time_t ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status); -#endif +char *ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +char *ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +extern ngx_str_t ngx_http_cache_status[]; #endif /* _NGX_HTTP_CACHE_H_INCLUDED_ */ diff --git a/src/http/ngx_http_config.h b/src/http/ngx_http_config.h --- a/src/http/ngx_http_config.h +++ b/src/http/ngx_http_config.h @@ -69,8 +69,10 @@ typedef struct { ((ngx_http_conf_ctx_t *) cf->ctx)->loc_conf[module.ctx_index] #define ngx_http_cycle_get_module_main_conf(cycle, module) \ - ((ngx_http_conf_ctx_t *) \ - cycle->conf_ctx[ngx_http_module.index])->main_conf[module.ctx_index] + (cycle->conf_ctx[ngx_http_module.index] ? \ + ((ngx_http_conf_ctx_t *) cycle->conf_ctx[ngx_http_module.index]) \ + ->main_conf[module.ctx_index]: \ + NULL) #endif /* _NGX_HTTP_CONFIG_H_INCLUDED_ */ diff --git a/src/http/ngx_http_copy_filter_module.c b/src/http/ngx_http_copy_filter_module.c --- a/src/http/ngx_http_copy_filter_module.c +++ b/src/http/ngx_http_copy_filter_module.c @@ -117,10 +117,6 @@ ngx_http_copy_filter(ngx_http_request_t r->buffered |= NGX_HTTP_COPY_BUFFERED; } - if (r != r->main) { - r->out = ctx->in; - } - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "copy filter: %i \"%V?%V\"", rc, &r->uri, &r->args); } diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -23,7 +23,6 @@ typedef struct { static ngx_int_t ngx_http_core_find_location(ngx_http_request_t *r); static ngx_int_t ngx_http_core_find_static_location(ngx_http_request_t *r, ngx_http_location_tree_node_t *node); -static ngx_int_t ngx_http_core_send_continue(ngx_http_request_t *r); static ngx_int_t ngx_http_core_preconfiguration(ngx_conf_t *cf); static void *ngx_http_core_create_main_conf(ngx_conf_t *cf); @@ -39,6 +38,8 @@ static char *ngx_http_core_server(ngx_co void *dummy); static char *ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy); +static ngx_int_t ngx_http_core_regex_location(ngx_conf_t *cf, + ngx_http_core_loc_conf_t *clcf, ngx_str_t *regex, ngx_uint_t caseless); static char *ngx_http_core_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -56,6 +57,8 @@ static char *ngx_http_core_directio(ngx_ void *conf); static char *ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_core_try_files(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static char *ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, @@ -108,6 +111,19 @@ static ngx_conf_enum_t ngx_http_core_sa }; +static ngx_conf_enum_t ngx_http_core_if_modified_since[] = { + { ngx_string("off"), NGX_HTTP_IMS_OFF }, + { ngx_string("exact"), NGX_HTTP_IMS_EXACT }, + { ngx_string("before"), NGX_HTTP_IMS_BEFORE }, + { ngx_null_string, 0 } +}; + + +static ngx_path_init_t ngx_http_client_temp_path = { + ngx_string(NGX_HTTP_CLIENT_TEMP_PATH), { 0, 0, 0 } +}; + + #if (NGX_HTTP_GZIP) static ngx_conf_enum_t ngx_http_gzip_http_version[] = { @@ -336,15 +352,22 @@ static ngx_command_t ngx_http_core_comm ngx_conf_set_path_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_core_loc_conf_t, client_body_temp_path), - (void *) ngx_garbage_collector_temp_handler }, + NULL }, { ngx_string("client_body_in_file_only"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_enum_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_core_loc_conf_t, client_body_in_file_only), &ngx_http_core_request_body_in_file }, + { ngx_string("client_body_in_single_buffer"), + 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_core_loc_conf_t, client_body_in_single_buffer), + NULL }, + { ngx_string("sendfile"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE1, @@ -410,6 +433,14 @@ static ngx_command_t ngx_http_core_comm offsetof(ngx_http_core_loc_conf_t, limit_rate), NULL }, + { ngx_string("limit_rate_after"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF + |NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, limit_rate_after), + NULL }, + { ngx_string("keepalive_timeout"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, ngx_http_core_keepalive, @@ -417,6 +448,13 @@ static ngx_command_t ngx_http_core_comm 0, NULL }, + { ngx_string("keepalive_requests"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, keepalive_requests), + NULL }, + { ngx_string("satisfy"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_enum_slot, @@ -515,6 +553,13 @@ static ngx_command_t ngx_http_core_comm offsetof(ngx_http_core_loc_conf_t, server_tokens), NULL }, + { ngx_string("if_modified_since"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_enum_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, if_modified_since), + &ngx_http_core_if_modified_since }, + { ngx_string("error_page"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_2MORE, @@ -523,6 +568,13 @@ static ngx_command_t ngx_http_core_comm 0, NULL }, + { ngx_string("try_files"), + NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_2MORE, + ngx_http_core_try_files, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + { ngx_string("post_action"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE1, @@ -661,7 +713,7 @@ ngx_module_t ngx_http_core_module = { }; -static ngx_str_t ngx_http_core_get_method = { 3, (u_char *) "GET " }; +ngx_str_t ngx_http_core_get_method = { 3, (u_char *) "GET " }; void @@ -793,7 +845,7 @@ ngx_http_core_find_config_phase(ngx_http { u_char *p; size_t len; - ngx_int_t rc, expect; + ngx_int_t rc; ngx_http_core_loc_conf_t *clcf; r->content_handler = NULL; @@ -837,15 +889,6 @@ ngx_http_core_find_config_phase(ngx_http return NGX_OK; } - if (r->headers_in.expect) { - expect = ngx_http_core_send_continue(r); - - if (expect != NGX_OK) { - ngx_http_finalize_request(r, expect); - return NGX_OK; - } - } - if (rc == NGX_DONE) { r->headers_out.location = ngx_list_push(&r->headers_out.headers); if (r->headers_out.location == NULL) { @@ -1016,6 +1059,192 @@ ngx_http_core_post_access_phase(ngx_http ngx_int_t +ngx_http_core_try_files_phase(ngx_http_request_t *r, + ngx_http_phase_handler_t *ph) +{ + size_t len, root, alias, reserve, allocated; + u_char *p, *name; + ngx_str_t path, args; + ngx_uint_t test_dir; + ngx_http_try_file_t *tf; + ngx_open_file_info_t of; + ngx_http_script_code_pt code; + ngx_http_script_engine_t e; + ngx_http_core_loc_conf_t *clcf; + ngx_http_script_len_code_pt lcode; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "try files phase: %ui", r->phase_handler); + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->try_files == NULL) { + r->phase_handler++; + return NGX_AGAIN; + } + + allocated = 0; + root = 0; + name = NULL; + /* suppress MSVC warning */ + path.data = NULL; + + tf = clcf->try_files; + + alias = clcf->alias ? clcf->name.len : 0; + + for ( ;; ) { + + if (tf->lengths) { + ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); + + e.ip = tf->lengths->elts; + e.request = r; + + /* 1 is for terminating '\0' as in static names */ + len = 1; + + while (*(uintptr_t *) e.ip) { + lcode = *(ngx_http_script_len_code_pt *) e.ip; + len += lcode(&e); + } + + } else { + len = tf->name.len; + } + + /* 16 bytes are preallocation */ + reserve = ngx_abs((ssize_t) (len - r->uri.len)) + alias + 16; + + if (reserve > allocated) { + + /* we just need to allocate path and to copy a root */ + + if (ngx_http_map_uri_to_path(r, &path, &root, reserve) == NULL) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_OK; + } + + name = path.data + root; + allocated = path.len - root - (r->uri.len - alias); + } + + if (tf->values == NULL) { + + /* tf->name.len includes the terminating '\0' */ + + ngx_memcpy(name, tf->name.data, tf->name.len); + + path.len = (name + tf->name.len - 1) - path.data; + + } else { + e.ip = tf->values->elts; + e.pos = name; + e.flushed = 1; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + } + + path.len = e.pos - path.data; + + *e.pos = '\0'; + + if (alias && ngx_strncmp(name, clcf->name.data, alias) == 0) { + ngx_memcpy(name, name + alias, len - alias); + path.len -= alias; + } + } + + test_dir = tf->test_dir; + + tf++; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "try to use file: \"%s\" \"%s\"", name, path.data); + + if (tf->lengths == NULL && tf->name.len == 0) { + + if (tf->code) { + ngx_http_finalize_request(r, tf->code); + return NGX_OK; + } + + path.len -= root; + path.data += root; + + if (path.data[0] == '@') { + (void) ngx_http_named_location(r, &path); + + } else { + ngx_http_split_args(r, &path, &args); + + (void) ngx_http_internal_redirect(r, &path, &args); + } + + return NGX_OK; + } + + ngx_memzero(&of, sizeof(ngx_open_file_info_t)); + + of.directio = clcf->directio; + of.valid = clcf->open_file_cache_valid; + of.min_uses = clcf->open_file_cache_min_uses; + of.test_only = 1; + of.errors = clcf->open_file_cache_errors; + of.events = clcf->open_file_cache_events; + + if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) + != NGX_OK) + { + if (of.err != NGX_ENOENT && of.err != NGX_ENOTDIR) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err, + "%s \"%s\" failed", of.failed, path.data); + } + + continue; + } + + if (of.is_dir && !test_dir) { + continue; + } + + path.len -= root; + path.data += root; + + if (!alias) { + r->uri = path; + + } else { + r->uri.len = alias + path.len; + r->uri.data = ngx_pnalloc(r->pool, r->uri.len); + if (r->uri.data == NULL) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_OK; + } + + p = ngx_copy(r->uri.data, clcf->name.data, alias); + ngx_memcpy(p, name, path.len); + } + + if (ngx_http_set_exten(r) != NGX_OK) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_OK; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "try file uri: \"%V\"", &r->uri); + + r->phase_handler++; + return NGX_AGAIN; + } + + /* not reached */ +} + + +ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) { @@ -1085,10 +1314,10 @@ ngx_http_update_location_config(ngx_http } if (r == r->main) { - r->connection->log->file = clcf->err_log->file; + r->connection->log->file = clcf->error_log->file; if (!(r->connection->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { - r->connection->log->log_level = clcf->err_log->log_level; + r->connection->log->log_level = clcf->error_log->log_level; } } @@ -1110,8 +1339,15 @@ ngx_http_update_location_config(ngx_http r->request_body_file_log_level = NGX_LOG_WARN; } - if (r->keepalive && clcf->keepalive_timeout == 0) { - r->keepalive = 0; + r->request_body_in_single_buf = clcf->client_body_in_single_buffer; + + if (r->keepalive) { + if (clcf->keepalive_timeout == 0) { + r->keepalive = 0; + + } else if (r->connection->requests >= clcf->keepalive_requests) { + r->keepalive = 0; + } } if (!clcf->tcp_nopush) { @@ -1143,7 +1379,7 @@ ngx_http_core_find_location(ngx_http_req ngx_int_t rc; ngx_http_core_loc_conf_t *pclcf; #if (NGX_PCRE) - ngx_int_t n; + ngx_int_t n, len; ngx_uint_t noregex; ngx_http_core_loc_conf_t *clcf, **clcfp; @@ -1177,12 +1413,26 @@ ngx_http_core_find_location(ngx_http_req if (noregex == 0 && pclcf->regex_locations) { + len = 0; + for (clcfp = pclcf->regex_locations; *clcfp; clcfp++) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "test location: ~ \"%V\"", &(*clcfp)->name); - n = ngx_regex_exec((*clcfp)->regex, &r->uri, NULL, 0); + if ((*clcfp)->captures) { + + len = (NGX_HTTP_MAX_CAPTURES + 1) * 3; + + if (r->captures == NULL) { + r->captures = ngx_palloc(r->pool, len * sizeof(int)); + if (r->captures == NULL) { + return NGX_ERROR; + } + } + } + + n = ngx_regex_exec((*clcfp)->regex, &r->uri, r->captures, len); if (n == NGX_REGEX_NO_MATCHED) { continue; @@ -1200,6 +1450,9 @@ ngx_http_core_find_location(ngx_http_req r->loc_conf = (*clcfp)->loc_conf; + r->ncaptures = len; + r->captures_data = r->uri.data; + /* look up nested locations */ rc = ngx_http_core_find_location(r); @@ -1294,80 +1547,41 @@ ngx_http_core_find_static_location(ngx_h } -static ngx_int_t -ngx_http_core_send_continue(ngx_http_request_t *r) -{ - ngx_int_t n; - ngx_str_t *expect; - - if (r->expect_tested) { - return NGX_OK; - } - - r->expect_tested = 1; - - expect = &r->headers_in.expect->value; - - if (expect->len != sizeof("100-continue") - 1 - || ngx_strncasecmp(expect->data, (u_char *) "100-continue", - sizeof("100-continue") - 1) - != 0) - { - return NGX_OK; - } - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "send 100 Continue"); - - n = r->connection->send(r->connection, - (u_char *) "HTTP/1.1 100 Continue" CRLF CRLF, - sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1); - - if (n == sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1) { - return NGX_OK; - } - - /* we assume that such small packet should be send successfully */ - - return NGX_HTTP_INTERNAL_SERVER_ERROR; -} - - void * ngx_http_test_content_type(ngx_http_request_t *r, ngx_hash_t *types_hash) { - u_char c, *p; - ngx_uint_t i, hash; + u_char c, *lowcase; + size_t len; + ngx_uint_t i, hash; if (r->headers_out.content_type.len == 0) { return NULL; } + len = r->headers_out.content_type_len; + if (r->headers_out.content_type_lowcase == NULL) { - p = ngx_pnalloc(r->pool, r->headers_out.content_type_len); - - if (p == NULL) { + lowcase = ngx_pnalloc(r->pool, len); + if (lowcase == NULL) { return NULL; } - r->headers_out.content_type_lowcase = p; + r->headers_out.content_type_lowcase = lowcase; hash = 0; - for (i = 0; i < r->headers_out.content_type_len; i++) { + for (i = 0; i < len; i++) { c = ngx_tolower(r->headers_out.content_type.data[i]); hash = ngx_hash(hash, c); - *p++ = c; + lowcase[i] = c; } r->headers_out.content_type_hash = hash; } - return ngx_hash_find(types_hash, - r->headers_out.content_type_hash, - r->headers_out.content_type_lowcase, - r->headers_out.content_type_len); + return ngx_hash_find(types_hash, r->headers_out.content_type_hash, + r->headers_out.content_type_lowcase, len); } @@ -1396,7 +1610,7 @@ ngx_http_set_content_type(ngx_http_reque exten = ngx_pnalloc(r->pool, r->exten.len); if (exten == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; + return NGX_ERROR; } hash = ngx_hash_strlow(exten, r->exten.data, r->exten.len); @@ -1467,16 +1681,24 @@ ngx_http_send_header(ngx_http_request_t ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in) { - ngx_int_t rc; - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + ngx_int_t rc; + ngx_connection_t *c; + + c = r->connection; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http output filter \"%V?%V\"", &r->uri, &r->args); rc = ngx_http_top_body_filter(r, in); if (rc == NGX_ERROR) { + + if (c->destroyed) { + return NGX_DONE; + } + /* NGX_ERROR may be returned by any filter */ - r->connection->error = 1; + c->error = 1; } return rc; @@ -1502,13 +1724,11 @@ ngx_http_map_uri_to_path(ngx_http_reques return NULL; } - reserved += r->uri.len - alias + 1; - if (clcf->root_lengths == NULL) { *root_length = clcf->root.len; - path->len = clcf->root.len + reserved; + path->len = clcf->root.len + reserved + r->uri.len - alias + 1; path->data = ngx_pnalloc(r->pool, path->len); if (path->data == NULL) { @@ -1518,6 +1738,16 @@ ngx_http_map_uri_to_path(ngx_http_reques last = ngx_copy(path->data, clcf->root.data, clcf->root.len); } else { + +#if (NGX_PCRE) + ngx_uint_t captures; + + captures = alias && clcf->captures; + reserved += captures ? 1 : r->uri.len - alias + 1; +#else + reserved += r->uri.len - alias + 1; +#endif + if (ngx_http_script_run(r, path, clcf->root_lengths->elts, reserved, clcf->root_values->elts) == NULL) @@ -1525,13 +1755,19 @@ ngx_http_map_uri_to_path(ngx_http_reques return NULL; } - if (ngx_conf_full_name((ngx_cycle_t *) ngx_cycle, path, 0)== NGX_ERROR) - { + if (ngx_conf_full_name((ngx_cycle_t *) ngx_cycle, path, 0) != NGX_OK) { return NULL; } *root_length = path->len - reserved; last = path->data + *root_length; + +#if (NGX_PCRE) + if (captures) { + *last = '\0'; + return last; + } +#endif } last = ngx_cpystrn(last, r->uri.data + alias, r->uri.len - alias + 1); @@ -1612,38 +1848,6 @@ ngx_http_auth_basic_user(ngx_http_reques } -ngx_int_t -ngx_http_server_addr(ngx_http_request_t *r, ngx_str_t *s) -{ - socklen_t len; - ngx_connection_t *c; - struct sockaddr_in sin; - - /* AF_INET only */ - - c = r->connection; - - if (r->in_addr == 0) { - len = sizeof(struct sockaddr_in); - if (getsockname(c->fd, (struct sockaddr *) &sin, &len) == -1) { - ngx_connection_error(c, ngx_socket_errno, "getsockname() failed"); - return NGX_ERROR; - } - - r->in_addr = sin.sin_addr.s_addr; - } - - if (s == NULL) { - return NGX_OK; - } - - s->len = ngx_sock_ntop((struct sockaddr *) &sin, s->data, - NGX_INET_ADDRSTRLEN); - - return NGX_OK; -} - - #if (NGX_HTTP_GZIP) ngx_int_t @@ -1813,7 +2017,6 @@ ngx_http_subrequest(ngx_http_request_t * { ngx_connection_t *c; ngx_http_request_t *sr; - ngx_http_log_ctx_t *ctx; ngx_http_core_srv_conf_t *cscf; ngx_http_postponed_request_t *pr, *p; @@ -1843,7 +2046,7 @@ ngx_http_subrequest(ngx_http_request_t * if (ngx_list_init(&sr->headers_out.headers, r->pool, 20, sizeof(ngx_table_elt_t)) - == NGX_ERROR) + != NGX_OK) { return NGX_ERROR; } @@ -1878,6 +2081,7 @@ ngx_http_subrequest(ngx_http_request_t * sr->zero_in_uri = (flags & NGX_HTTP_ZERO_IN_URI) != 0; sr->subrequest_in_memory = (flags & NGX_HTTP_SUBREQUEST_IN_MEMORY) != 0; + sr->waited = (flags & NGX_HTTP_SUBREQUEST_WAITED) != 0; sr->unparsed_uri = r->unparsed_uri; sr->method_name = ngx_http_core_get_method; @@ -1891,16 +2095,12 @@ ngx_http_subrequest(ngx_http_request_t * sr->parent = r; sr->post_subrequest = ps; sr->read_event_handler = ngx_http_request_empty_handler; - sr->write_event_handler = ngx_http_request_empty_handler; - - if (c->data == r) { + sr->write_event_handler = ngx_http_handler; + + if (c->data == r && r->postponed == NULL) { c->data = sr; } - sr->in_addr = r->in_addr; - sr->port = r->port; - sr->port_text = r->port_text; - sr->variables = r->variables; sr->log_handler = r->log_handler; @@ -1922,39 +2122,19 @@ ngx_http_subrequest(ngx_http_request_t * r->postponed = pr; } - ctx = c->log->data; - ctx->current_request = sr; - sr->internal = 1; - sr->fast_subrequest = 1; sr->discard_body = r->discard_body; + sr->expect_tested = 1; sr->main_filter_need_in_memory = r->main_filter_need_in_memory; sr->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1; - ngx_http_handler(sr); - - if (!c->destroyed) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http subrequest done \"%V?%V\"", uri, &sr->args); - - r->main->subrequests++; - - *psr = sr; - - if (sr->fast_subrequest) { - sr->fast_subrequest = 0; - - if (sr->done) { - return NGX_OK; - } - } - - return NGX_AGAIN; - } - - return NGX_DONE; + r->main->subrequests++; + + *psr = sr; + + return ngx_http_post_request(sr); } @@ -2001,6 +2181,10 @@ ngx_http_internal_redirect(ngx_http_requ ngx_http_update_location_config(r); +#if (NGX_HTTP_CACHE) + r->cache = NULL; +#endif + r->internal = 1; ngx_http_handler(r); @@ -2018,33 +2202,37 @@ ngx_http_named_location(ngx_http_request cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); - for (clcfp = cscf->named_locations; *clcfp; clcfp++) { - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "test location: \"%V\"", &(*clcfp)->name); - - if (name->len != (*clcfp)->name.len - || ngx_strncmp(name->data, (*clcfp)->name.data, name->len) != 0) - { - continue; + if (cscf->named_locations) { + + for (clcfp = cscf->named_locations; *clcfp; clcfp++) { + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "test location: \"%V\"", &(*clcfp)->name); + + if (name->len != (*clcfp)->name.len + || ngx_strncmp(name->data, (*clcfp)->name.data, name->len) != 0) + { + continue; + } + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "using location: %V \"%V?%V\"", + name, &r->uri, &r->args); + + r->internal = 1; + r->content_handler = NULL; + r->loc_conf = (*clcfp)->loc_conf; + + ngx_http_update_location_config(r); + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + r->phase_handler = cmcf->phase_engine.location_rewrite_index; + + ngx_http_core_run_phases(r); + + return NGX_DONE; } - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "using location: %V \"%V?%V\"", name, &r->uri, &r->args); - - r->internal = 1; - r->content_handler = NULL; - r->loc_conf = (*clcfp)->loc_conf; - - ngx_http_update_location_config(r); - - cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); - - r->phase_handler = cmcf->phase_engine.location_rewrite_index; - - ngx_http_core_run_phases(r); - - return NGX_DONE; } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, @@ -2185,8 +2373,10 @@ static char * ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) { char *rv; + u_char *mod; + size_t len; + ngx_str_t *value, *name; ngx_uint_t i; - ngx_str_t *value; ngx_conf_t save; ngx_http_module_t *module; ngx_http_conf_ctx_t *ctx, *pctx; @@ -2228,45 +2418,32 @@ ngx_http_core_location(ngx_conf_t *cf, n value = cf->args->elts; if (cf->args->nelts == 3) { - if (value[1].len == 1 && value[1].data[0] == '=') { - clcf->name = value[2]; + + len = value[1].len; + mod = value[1].data; + name = &value[2]; + + if (len == 1 && mod[0] == '=') { + + clcf->name = *name; clcf->exact_match = 1; - } else if (value[1].len == 2 - && value[1].data[0] == '^' - && value[1].data[1] == '~') - { - clcf->name = value[2]; + } else if (len == 2 && mod[0] == '^' && mod[1] == '~') { + + clcf->name = *name; clcf->noregex = 1; - } else if ((value[1].len == 1 && value[1].data[0] == '~') - || (value[1].len == 2 - && value[1].data[0] == '~' - && value[1].data[1] == '*')) - { -#if (NGX_PCRE) - ngx_str_t err; - u_char errstr[NGX_MAX_CONF_ERRSTR]; - - err.len = NGX_MAX_CONF_ERRSTR; - err.data = errstr; - - clcf->regex = ngx_regex_compile(&value[2], - value[1].len == 2 ? NGX_REGEX_CASELESS: 0, - cf->pool, &err); - - if (clcf->regex == NULL) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data); + } else if (len == 1 && mod[0] == '~') { + + if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) { return NGX_CONF_ERROR; } - clcf->name = value[2]; -#else - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "the using of the regex \"%V\" " - "requires PCRE library", &value[2]); - return NGX_CONF_ERROR; -#endif + } else if (len == 2 && mod[0] == '~' && mod[1] == '*') { + + if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) { + return NGX_CONF_ERROR; + } } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, @@ -2276,10 +2453,47 @@ ngx_http_core_location(ngx_conf_t *cf, n } else { - clcf->name = value[1]; - - if (value[1].data[0] == '@') { - clcf->named = 1; + name = &value[1]; + + if (name->data[0] == '=') { + + clcf->name.len = name->len - 1; + clcf->name.data = name->data + 1; + clcf->exact_match = 1; + + } else if (name->data[0] == '^' && name->data[1] == '~') { + + clcf->name.len = name->len - 2; + clcf->name.data = name->data + 2; + clcf->noregex = 1; + + } else if (name->data[0] == '~') { + + name->len--; + name->data++; + + if (name->data[0] == '*') { + + name->len--; + name->data++; + + if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) { + return NGX_CONF_ERROR; + } + + } else { + if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + + } else { + + clcf->name = *name; + + if (name->data[0] == '@') { + clcf->named = 1; + } } } @@ -2317,13 +2531,13 @@ ngx_http_core_location(ngx_conf_t *cf, n return NGX_CONF_ERROR; } + len = pclcf->name.len; + #if (NGX_PCRE) if (clcf->regex == NULL - && ngx_strncmp(clcf->name.data, pclcf->name.data, pclcf->name.len) - != 0) + && ngx_strncmp(clcf->name.data, pclcf->name.data, len) != 0) #else - if (ngx_strncmp(clcf->name.data, pclcf->name.data, pclcf->name.len) - != 0) + if (ngx_strncmp(clcf->name.data, pclcf->name.data, len) != 0) #endif { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, @@ -2349,6 +2563,41 @@ ngx_http_core_location(ngx_conf_t *cf, n } +static ngx_int_t +ngx_http_core_regex_location(ngx_conf_t *cf, ngx_http_core_loc_conf_t *clcf, + ngx_str_t *regex, ngx_uint_t caseless) +{ +#if (NGX_PCRE) + ngx_str_t err; + u_char errstr[NGX_MAX_CONF_ERRSTR]; + + err.len = NGX_MAX_CONF_ERRSTR; + err.data = errstr; + + clcf->regex = ngx_regex_compile(regex, caseless ? NGX_REGEX_CASELESS: 0, + cf->pool, &err); + + if (clcf->regex == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data); + return NGX_ERROR; + } + + clcf->name = *regex; + clcf->captures = (ngx_regex_capture_count(clcf->regex) > 0); + + return NGX_OK; + +#else + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the using of the regex \"%V\" requires PCRE library", + regex); + return NGX_ERROR; + +#endif +} + + static char * ngx_http_core_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { @@ -2390,7 +2639,7 @@ ngx_http_core_type(ngx_conf_t *cf, ngx_c if (ngx_strcmp(value[0].data, "include") == 0) { file = value[1]; - if (ngx_conf_full_name(cf->cycle, &file, 1) == NGX_ERROR){ + if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) { return NGX_CONF_ERROR; } @@ -2454,14 +2703,14 @@ ngx_http_core_create_main_conf(ngx_conf_ cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_main_conf_t)); if (cmcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } if (ngx_array_init(&cmcf->servers, cf->pool, 4, sizeof(ngx_http_core_srv_conf_t *)) != NGX_OK) { - return NGX_CONF_ERROR; + return NULL; } cmcf->server_names_hash_max_size = NGX_CONF_UNSET_UINT; @@ -2513,7 +2762,7 @@ ngx_http_core_create_srv_conf(ngx_conf_t cscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_srv_conf_t)); if (cscf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* @@ -2522,17 +2771,18 @@ ngx_http_core_create_srv_conf(ngx_conf_t * conf->client_large_buffers.num = 0; */ - if (ngx_array_init(&cscf->listen, cf->pool, 4, sizeof(ngx_http_listen_t)) - == NGX_ERROR) + if (ngx_array_init(&cscf->listen, cf->temp_pool, 4, + sizeof(ngx_http_listen_t)) + != NGX_OK) { - return NGX_CONF_ERROR; + return NULL; } if (ngx_array_init(&cscf->server_names, cf->temp_pool, 4, sizeof(ngx_http_server_name_t)) - == NGX_ERROR) + != NGX_OK) { - return NGX_CONF_ERROR; + return NULL; } cscf->connection_pool_size = NGX_CONF_UNSET_SIZE; @@ -2554,6 +2804,7 @@ ngx_http_core_merge_srv_conf(ngx_conf_t ngx_http_core_srv_conf_t *conf = child; ngx_http_listen_t *ls; + struct sockaddr_in *sin; ngx_http_server_name_t *sn; /* TODO: it does not merge, it inits only */ @@ -2566,18 +2817,25 @@ ngx_http_core_merge_srv_conf(ngx_conf_t ngx_memzero(ls, sizeof(ngx_http_listen_t)); - ls->addr = INADDR_ANY; + sin = (struct sockaddr_in *) &ls->sockaddr; + + sin->sin_family = AF_INET; #if (NGX_WIN32) - ls->port = 80; + sin->sin_port = htons(80); #else - /* STUB: getuid() should be cached */ - ls->port = (getuid() == 0) ? 80 : 8000; + sin->sin_port = htons((getuid() == 0) ? 80 : 8000); #endif - ls->family = AF_INET; + sin->sin_addr.s_addr = INADDR_ANY; + + ls->socklen = sizeof(struct sockaddr_in); ls->conf.backlog = NGX_LISTEN_BACKLOG; ls->conf.rcvbuf = -1; ls->conf.sndbuf = -1; + ls->conf.wildcard = 1; + + (void) ngx_sock_ntop((struct sockaddr *) &ls->sockaddr, ls->conf.addr, + NGX_SOCKADDR_STRLEN, 1); } if (conf->server_name.data == NULL) { @@ -2590,6 +2848,7 @@ ngx_http_core_merge_srv_conf(ngx_conf_t #if (NGX_PCRE) sn->regex = NULL; + sn->captures = 0; #endif sn->core_srv_conf = conf; sn->name.len = conf->server_name.len; @@ -2634,7 +2893,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_loc_conf_t)); if (lcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* @@ -2645,8 +2904,9 @@ ngx_http_core_create_loc_conf(ngx_conf_t * lcf->post_action = { 0, NULL }; * lcf->types = NULL; * lcf->default_type = { 0, NULL }; - * lcf->err_log = NULL; + * lcf->error_log = NULL; * lcf->error_pages = NULL; + * lcf->try_files = NULL; * lcf->client_body_path = NULL; * lcf->regex = NULL; * lcf->exact_match = 0; @@ -2659,8 +2919,10 @@ ngx_http_core_create_loc_conf(ngx_conf_t lcf->client_body_buffer_size = NGX_CONF_UNSET_SIZE; lcf->client_body_timeout = NGX_CONF_UNSET_MSEC; lcf->satisfy = NGX_CONF_UNSET_UINT; + lcf->if_modified_since = NGX_CONF_UNSET_UINT; + lcf->client_body_in_file_only = NGX_CONF_UNSET_UINT; + lcf->client_body_in_single_buffer = NGX_CONF_UNSET; lcf->internal = NGX_CONF_UNSET; - lcf->client_body_in_file_only = NGX_CONF_UNSET; lcf->sendfile = NGX_CONF_UNSET; lcf->sendfile_max_chunk = NGX_CONF_UNSET_SIZE; lcf->directio = NGX_CONF_UNSET; @@ -2670,8 +2932,10 @@ ngx_http_core_create_loc_conf(ngx_conf_t lcf->send_lowat = NGX_CONF_UNSET_SIZE; lcf->postpone_output = NGX_CONF_UNSET_SIZE; lcf->limit_rate = NGX_CONF_UNSET_SIZE; + lcf->limit_rate_after = NGX_CONF_UNSET_SIZE; lcf->keepalive_timeout = NGX_CONF_UNSET_MSEC; lcf->keepalive_header = NGX_CONF_UNSET; + lcf->keepalive_requests = NGX_CONF_UNSET_UINT; lcf->lingering_time = NGX_CONF_UNSET_MSEC; lcf->lingering_timeout = NGX_CONF_UNSET_MSEC; lcf->resolver_timeout = NGX_CONF_UNSET_MSEC; @@ -2739,7 +3003,7 @@ ngx_http_core_merge_loc_conf(ngx_conf_t conf->root.len = sizeof("html") - 1; conf->root.data = (u_char *) "html"; - if (ngx_conf_full_name(cf->cycle, &conf->root, 0) == NGX_ERROR) { + if (ngx_conf_full_name(cf->cycle, &conf->root, 0) != NGX_OK) { return NGX_CONF_ERROR; } } @@ -2823,11 +3087,11 @@ ngx_http_core_merge_loc_conf(ngx_conf_t } } - if (conf->err_log == NULL) { - if (prev->err_log) { - conf->err_log = prev->err_log; + if (conf->error_log == NULL) { + if (prev->error_log) { + conf->error_log = prev->error_log; } else { - conf->err_log = cf->cycle->new_log; + conf->error_log = &cf->cycle->new_log; } } @@ -2848,9 +3112,13 @@ ngx_http_core_merge_loc_conf(ngx_conf_t ngx_conf_merge_uint_value(conf->satisfy, prev->satisfy, NGX_HTTP_SATISFY_ALL); + ngx_conf_merge_uint_value(conf->if_modified_since, prev->if_modified_since, + NGX_HTTP_IMS_EXACT); + ngx_conf_merge_uint_value(conf->client_body_in_file_only, + prev->client_body_in_file_only, 0); + ngx_conf_merge_value(conf->client_body_in_single_buffer, + prev->client_body_in_single_buffer, 0); ngx_conf_merge_value(conf->internal, prev->internal, 0); - ngx_conf_merge_value(conf->client_body_in_file_only, - prev->client_body_in_file_only, 0); ngx_conf_merge_value(conf->sendfile, prev->sendfile, 0); ngx_conf_merge_size_value(conf->sendfile_max_chunk, prev->sendfile_max_chunk, 0); @@ -2864,10 +3132,14 @@ ngx_http_core_merge_loc_conf(ngx_conf_t ngx_conf_merge_size_value(conf->postpone_output, prev->postpone_output, 1460); ngx_conf_merge_size_value(conf->limit_rate, prev->limit_rate, 0); + ngx_conf_merge_size_value(conf->limit_rate_after, prev->limit_rate_after, + 0); ngx_conf_merge_msec_value(conf->keepalive_timeout, prev->keepalive_timeout, 75000); ngx_conf_merge_sec_value(conf->keepalive_header, prev->keepalive_header, 0); + ngx_conf_merge_uint_value(conf->keepalive_requests, + prev->keepalive_requests, 100); ngx_conf_merge_msec_value(conf->lingering_time, prev->lingering_time, 30000); ngx_conf_merge_msec_value(conf->lingering_timeout, @@ -2893,10 +3165,13 @@ ngx_http_core_merge_loc_conf(ngx_conf_t conf->resolver = prev->resolver; } - ngx_conf_merge_path_value(conf->client_body_temp_path, + if (ngx_conf_merge_path_value(cf, &conf->client_body_temp_path, prev->client_body_temp_path, - NGX_HTTP_CLIENT_TEMP_PATH, 0, 0, 0, - ngx_garbage_collector_temp_handler, cf); + &ngx_http_client_temp_path) + != NGX_OK) + { + return NGX_CONF_ERROR; + } ngx_conf_merge_value(conf->reset_timedout_connection, prev->reset_timedout_connection, 0); @@ -2948,8 +3223,6 @@ ngx_http_core_merge_loc_conf(ngx_conf_t } -/* AF_INET only */ - static char * ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { @@ -2990,17 +3263,18 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx ngx_memzero(ls, sizeof(ngx_http_listen_t)); - ls->family = u.family; - ls->addr = u.addr.in_addr; - ls->port = u.port; + ngx_memcpy(ls->sockaddr, u.sockaddr, u.socklen); + + ls->socklen = u.socklen; ls->file_name = cf->conf_file->file.name.data; ls->line = cf->conf_file->line; ls->conf.backlog = NGX_LISTEN_BACKLOG; ls->conf.rcvbuf = -1; ls->conf.sndbuf = -1; - - n = ngx_inet_ntop(AF_INET, &ls->addr, ls->conf.addr, NGX_INET_ADDRSTRLEN); - ngx_sprintf(&ls->conf.addr[n], ":%ui", ls->port); + ls->conf.wildcard = u.wildcard; + + (void) ngx_sock_ntop((struct sockaddr *) &ls->sockaddr, ls->conf.addr, + NGX_SOCKADDR_STRLEN, 1); if (cf->args->nelts == 2) { return NGX_CONF_OK; @@ -3099,6 +3373,45 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx continue; } + if (ngx_strncmp(value[n].data, "ipv6only=o", 10) == 0) { +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + struct sockaddr *sa; + + sa = (struct sockaddr *) ls->sockaddr; + + if (sa->sa_family == AF_INET6) { + + if (ngx_strcmp(&value[n].data[10], "n") == 0) { + ls->conf.ipv6only = 1; + + } else if (ngx_strcmp(&value[n].data[10], "ff") == 0) { + ls->conf.ipv6only = 2; + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid ipv6only flags \"%s\"", + &value[n].data[9]); + return NGX_CONF_ERROR; + } + + ls->conf.bind = 1; + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "ipv6only is not supported " + "on addr \"%s\", ignored", + ls->conf.addr); + } + + 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[n].data, "ssl") == 0) { #if (NGX_HTTP_SSL) ls->conf.ssl = 1; @@ -3188,6 +3501,7 @@ ngx_http_core_server_name(ngx_conf_t *cf #if (NGX_PCRE) sn->regex = NULL; + sn->captures = 0; #endif sn->core_srv_conf = cscf; sn->name = value[i]; @@ -3214,6 +3528,7 @@ ngx_http_core_server_name(ngx_conf_t *cf return NGX_CONF_ERROR; } + sn->captures = (ngx_regex_capture_count(sn->regex) > 0); sn->name = value[i]; } #else @@ -3266,18 +3581,6 @@ ngx_http_core_root(ngx_conf_t *cf, ngx_c return NGX_CONF_ERROR; } -#if (NGX_PCRE) - - if (lcf->regex && alias) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "the \"alias\" directive may not be used " - "inside location given by regular expression"); - - return NGX_CONF_ERROR; - } - -#endif - value = cf->args->elts; if (ngx_strstr(value[1].data, "$document_root") @@ -3310,31 +3613,43 @@ ngx_http_core_root(ngx_conf_t *cf, ngx_c } if (lcf->root.data[0] != '$') { - if (ngx_conf_full_name(cf->cycle, &lcf->root, 0) == NGX_ERROR) { + if (ngx_conf_full_name(cf->cycle, &lcf->root, 0) != NGX_OK) { return NGX_CONF_ERROR; } } n = ngx_http_script_variables_count(&lcf->root); - if (n == 0) { - return NGX_CONF_OK; + ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + + if (n) { + sc.cf = cf; + sc.source = &lcf->root; + sc.lengths = &lcf->root_lengths; + sc.values = &lcf->root_values; + sc.variables = n; + sc.complete_lengths = 1; + sc.complete_values = 1; + + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; + } } - ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); - - sc.cf = cf; - sc.source = &lcf->root; - sc.lengths = &lcf->root_lengths; - sc.values = &lcf->root_values; - sc.variables = n; - sc.complete_lengths = 1; - sc.complete_values = 1; - - if (ngx_http_script_compile(&sc) != NGX_OK) { +#if (NGX_PCRE) + + if (alias && lcf->regex + && (ngx_regex_capture_count(lcf->regex) <= 0 || sc.ncaptures == 0)) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"alias\" directive must use captures " + "inside location given by regular expression"); + return NGX_CONF_ERROR; } +#endif + return NGX_CONF_OK; } @@ -3470,7 +3785,7 @@ ngx_http_core_directio(ngx_conf_t *cf, n value = cf->args->elts; if (ngx_strcmp(value[1].data, "off") == 0) { - clcf->directio = NGX_MAX_OFF_T_VALUE; + clcf->directio = NGX_OPEN_FILE_DIRECTIO_OFF; return NGX_CONF_OK; } @@ -3488,13 +3803,13 @@ ngx_http_core_error_page(ngx_conf_t *cf, { ngx_http_core_loc_conf_t *lcf = conf; - u_char *args; - ngx_int_t overwrite; - ngx_str_t *value, uri; - ngx_uint_t i, n, nvar; - ngx_array_t *uri_lengths, *uri_values; - ngx_http_err_page_t *err; - ngx_http_script_compile_t sc; + u_char *p; + ngx_int_t overwrite; + ngx_str_t *value, uri, args; + ngx_uint_t i, n; + ngx_http_err_page_t *err; + ngx_http_complex_value_t cv; + ngx_http_compile_complex_value_t ccv; if (lcf->error_pages == NULL) { lcf->error_pages = ngx_array_create(cf->pool, 4, @@ -3536,29 +3851,32 @@ ngx_http_core_error_page(ngx_conf_t *cf, } uri = value[cf->args->nelts - 1]; - uri_lengths = NULL; - uri_values = NULL; - - nvar = ngx_http_script_variables_count(&uri); - - if (nvar) { - ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); - - sc.cf = cf; - sc.source = &uri; - sc.lengths = &uri_lengths; - sc.values = &uri_values; - sc.variables = nvar; - sc.complete_lengths = 1; - sc.complete_values = 1; - - if (ngx_http_script_compile(&sc) != NGX_OK) { - return NGX_CONF_ERROR; + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &uri; + ccv.complex_value = &cv; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + args.len = 0; + args.data = NULL; + + if (cv.lengths == NULL && uri.data[0] == '/') { + p = (u_char *) ngx_strchr(uri.data, '?'); + + if (p) { + cv.value.len = p - uri.data; + cv.value.data = uri.data; + p++; + args.len = (uri.data + uri.len) - p; + args.data = p; } } - args = (u_char *) ngx_strchr(uri.data, '?'); - for (i = 1; i < cf->args->nelts - n; i++) { err = ngx_array_push(lcf->error_pages); if (err == NULL) { @@ -3597,21 +3915,88 @@ ngx_http_core_error_page(ngx_conf_t *cf, } } - if (args) { - err->uri.len = args - uri.data; - err->uri.data = uri.data; - args++; - err->args.len = (uri.data + uri.len) - args; - err->args.data = args; + err->value = cv; + err->args = args; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_core_try_files(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_core_loc_conf_t *clcf = conf; + + ngx_str_t *value; + ngx_int_t code; + ngx_uint_t i, n; + ngx_http_try_file_t *tf; + ngx_http_script_compile_t sc; + ngx_http_core_main_conf_t *cmcf; + + if (clcf->try_files) { + return "is duplicate"; + } + + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + + cmcf->try_files = 1; + + tf = ngx_pcalloc(cf->pool, cf->args->nelts * sizeof(ngx_http_try_file_t)); + if (tf == NULL) { + return NGX_CONF_ERROR; + } + + clcf->try_files = tf; + + value = cf->args->elts; + + for (i = 0; i < cf->args->nelts - 1; i++) { + + tf[i].name = value[i + 1]; + + if (tf[i].name.data[tf[i].name.len - 1] == '/') { + tf[i].test_dir = 1; + tf[i].name.len--; + tf[i].name.data[tf[i].name.len] = '\0'; + } + + n = ngx_http_script_variables_count(&tf[i].name); + + if (n) { + ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + + sc.cf = cf; + sc.source = &tf[i].name; + sc.lengths = &tf[i].lengths; + sc.values = &tf[i].values; + sc.variables = n; + sc.complete_lengths = 1; + sc.complete_values = 1; + + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; + } } else { - err->uri = uri; - err->args.len = 0; - err->args.data = NULL; + /* add trailing '\0' to length */ + tf[i].name.len++; } - - err->uri_lengths = uri_lengths; - err->uri_values = uri_values; + } + + if (tf[i - 1].name.data[0] == '=') { + + code = ngx_atoi(tf[i - 1].name.data + 1, tf[i - 1].name.len - 2); + + if (code == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid code \"%*s\"", + tf[i - 1].name.len - 1, tf[i - 1].name.data); + return NGX_CONF_ERROR; + } + + tf[i].code = code; } return NGX_CONF_OK; @@ -3701,12 +4086,25 @@ ngx_http_core_error_log(ngx_conf_t *cf, { ngx_http_core_loc_conf_t *lcf = conf; - lcf->err_log = ngx_log_create_errlog(cf->cycle, cf->args); - if (lcf->err_log == NULL) { + ngx_str_t *value; + + if (lcf->error_log) { + return "is duplicate"; + } + + value = cf->args->elts; + + lcf->error_log = ngx_log_create(cf->cycle, &value[1]); + if (lcf->error_log == NULL) { return NGX_CONF_ERROR; } - return ngx_set_error_log_levels(cf, lcf->err_log); + if (cf->args->nelts == 2) { + lcf->error_log->log_level = NGX_LOG_ERR; + return NGX_CONF_OK; + } + + return ngx_log_set_levels(cf, lcf->error_log); } diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -28,6 +28,11 @@ #define NGX_HTTP_SATISFY_ANY 1 +#define NGX_HTTP_IMS_OFF 0 +#define NGX_HTTP_IMS_EXACT 1 +#define NGX_HTTP_IMS_BEFORE 2 + + typedef struct ngx_http_location_tree_node_s ngx_http_location_tree_node_t; typedef struct ngx_http_core_loc_conf_s ngx_http_core_loc_conf_t; @@ -35,9 +40,13 @@ typedef struct ngx_http_core_loc_conf_s typedef struct { unsigned default_server:1; unsigned bind:1; + unsigned wildcard:1; #if (NGX_HTTP_SSL) unsigned ssl:1; #endif +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + unsigned ipv6only:2; +#endif int backlog; int rcvbuf; @@ -50,15 +59,13 @@ typedef struct { ngx_uint_t deferred_accept; #endif - u_char addr[NGX_INET_ADDRSTRLEN + sizeof(":65535")]; - + u_char addr[NGX_SOCKADDR_STRLEN + 1]; } ngx_http_listen_conf_t; typedef struct { - in_addr_t addr; - in_port_t port; - int family; + u_char sockaddr[NGX_SOCKADDRLEN]; + socklen_t socklen; u_char *file_name; ngx_uint_t line; @@ -81,6 +88,7 @@ typedef enum { NGX_HTTP_ACCESS_PHASE, NGX_HTTP_POST_ACCESS_PHASE, + NGX_HTTP_TRY_FILES_PHASE, NGX_HTTP_CONTENT_PHASE, NGX_HTTP_LOG_PHASE @@ -129,6 +137,8 @@ typedef struct { ngx_hash_keys_arrays_t *variables_keys; + ngx_uint_t try_files; /* unsigned try_files:1 */ + ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1]; } ngx_http_core_main_conf_t; @@ -165,8 +175,6 @@ typedef struct { typedef struct { - in_addr_t addr; - /* the default server configuration for this address:port */ ngx_http_core_srv_conf_t *core_srv_conf; @@ -175,25 +183,42 @@ typedef struct { #if (NGX_HTTP_SSL) ngx_uint_t ssl; /* unsigned ssl:1; */ #endif -} ngx_http_in_addr_t; - - -typedef struct { - in_port_t port; - ngx_str_t port_text; - ngx_http_in_addr_t *addrs; - ngx_uint_t naddrs; -} ngx_http_in_port_t; - - -typedef struct { - in_port_t port; - ngx_array_t addrs; /* array of ngx_http_conf_in_addr_t */ -} ngx_http_conf_in_port_t; +} ngx_http_addr_conf_t; typedef struct { in_addr_t addr; + ngx_http_addr_conf_t conf; +} ngx_http_in_addr_t; + + +#if (NGX_HAVE_INET6) + +typedef struct { + struct in6_addr addr6; + ngx_http_addr_conf_t conf; +} ngx_http_in6_addr_t; + +#endif + + +typedef struct { + /* ngx_http_in_addr_t or ngx_http_in6_addr_t */ + void *addrs; + ngx_uint_t naddrs; +} ngx_http_port_t; + + +typedef struct { + ngx_int_t family; + in_port_t port; + ngx_array_t addrs; /* array of ngx_http_conf_addr_t */ +} ngx_http_conf_port_t; + + +typedef struct { + struct sockaddr *sockaddr; + socklen_t socklen; ngx_hash_t hash; ngx_hash_wildcard_t *wc_head; @@ -211,17 +236,19 @@ typedef struct { unsigned default_server:1; unsigned bind:1; + unsigned wildcard:1; #if (NGX_HTTP_SSL) unsigned ssl:1; #endif ngx_http_listen_conf_t *listen_conf; -} ngx_http_conf_in_addr_t; +} ngx_http_conf_addr_t; struct ngx_http_server_name_s { #if (NGX_PCRE) ngx_regex_t *regex; + ngx_uint_t captures; /* unsigned captures:1; */ #endif ngx_http_core_srv_conf_t *core_srv_conf; /* virtual name server conf */ ngx_str_t name; @@ -231,18 +258,28 @@ struct ngx_http_server_name_s { typedef struct { ngx_int_t status; ngx_int_t overwrite; - ngx_str_t uri; + ngx_http_complex_value_t value; ngx_str_t args; - ngx_array_t *uri_lengths; - ngx_array_t *uri_values; } ngx_http_err_page_t; +typedef struct { + ngx_array_t *lengths; + ngx_array_t *values; + ngx_str_t name; + + unsigned code:10; + unsigned test_dir:1; +} ngx_http_try_file_t; + + struct ngx_http_core_loc_conf_s { ngx_str_t name; /* location name */ #if (NGX_PCRE) ngx_regex_t *regex; + + unsigned captures:1; #endif unsigned noname:1; /* "if () {}" block or limit_except */ @@ -258,7 +295,9 @@ struct ngx_http_core_loc_conf_s { #endif ngx_http_location_tree_node_t *static_locations; +#if (NGX_PCRE) ngx_http_core_loc_conf_t **regex_locations; +#endif /* pointer to the modules' loc_conf */ void **loc_conf; @@ -285,6 +324,7 @@ struct ngx_http_core_loc_conf_s { size_t send_lowat; /* send_lowat */ size_t postpone_output; /* postpone_output */ size_t limit_rate; /* limit_rate */ + size_t limit_rate_after; /* limit_rate_after */ size_t sendfile_max_chunk; /* sendfile_max_chunk */ ngx_msec_t client_body_timeout; /* client_body_timeout */ @@ -298,10 +338,14 @@ struct ngx_http_core_loc_conf_s { time_t keepalive_header; /* keepalive_timeout */ + ngx_uint_t keepalive_requests; /* keepalive_requests */ ngx_uint_t satisfy; /* satisfy */ + ngx_uint_t if_modified_since; /* if_modified_since */ + ngx_uint_t client_body_in_file_only; /* client_body_in_file_only */ + ngx_flag_t client_body_in_single_buffer; + /* client_body_in_singe_buffer */ ngx_flag_t internal; /* internal */ - ngx_flag_t client_body_in_file_only; /* client_body_in_file_only */ ngx_flag_t sendfile; /* sendfile */ ngx_flag_t tcp_nopush; /* tcp_nopush */ ngx_flag_t tcp_nodelay; /* tcp_nodelay */ @@ -327,6 +371,7 @@ struct ngx_http_core_loc_conf_s { #endif ngx_array_t *error_pages; /* error_page */ + ngx_http_try_file_t *try_files; /* try_files */ ngx_path_t *client_body_temp_path; /* client_body_temp_path */ @@ -336,7 +381,7 @@ struct ngx_http_core_loc_conf_s { ngx_flag_t open_file_cache_errors; ngx_flag_t open_file_cache_events; - ngx_log_t *err_log; + ngx_log_t *error_log; ngx_uint_t types_hash_max_size; ngx_uint_t types_hash_bucket_size; @@ -385,6 +430,8 @@ ngx_int_t ngx_http_core_access_phase(ngx ngx_http_phase_handler_t *ph); ngx_int_t ngx_http_core_post_access_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph); +ngx_int_t ngx_http_core_try_files_phase(ngx_http_request_t *r, + ngx_http_phase_handler_t *ph); ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph); @@ -395,7 +442,6 @@ ngx_int_t ngx_http_set_exten(ngx_http_re u_char *ngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *name, size_t *root_length, size_t reserved); ngx_int_t ngx_http_auth_basic_user(ngx_http_request_t *r); -ngx_int_t ngx_http_server_addr(ngx_http_request_t *r, ngx_str_t *s); #if (NGX_HTTP_GZIP) ngx_int_t ngx_http_gzip_ok(ngx_http_request_t *r); #endif @@ -425,6 +471,8 @@ extern ngx_module_t ngx_http_core_modul extern ngx_uint_t ngx_http_max_module; +extern ngx_str_t ngx_http_core_get_method; + #define ngx_http_clear_content_length(r) \ \ diff --git a/src/http/ngx_http_file_cache.c b/src/http/ngx_http_file_cache.c --- a/src/http/ngx_http_file_cache.c +++ b/src/http/ngx_http_file_cache.c @@ -7,253 +7,1566 @@ #include #include #include +#include -#if (NGX_HAVE_OPENSSL_MD5_H) -#include -#else -#include -#endif - -#if (NGX_OPENSSL_MD5) -#define MD5Init MD5_Init -#define MD5Update MD5_Update -#define MD5Final MD5_Final -#endif +static ngx_int_t ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, + ngx_http_cache_t *c); +static ngx_http_file_cache_node_t * + ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key); +static void ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); +static void ngx_http_file_cache_cleanup(void *data); +static time_t ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache); +static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache); +static void ngx_http_file_cache_delete(ngx_http_file_cache_t *cache, + ngx_queue_t *q, u_char *name); +static ngx_int_t + ngx_http_file_cache_manager_sleep(ngx_http_file_cache_t *cache); +static ngx_int_t ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx, + ngx_str_t *path); +static ngx_int_t ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, + ngx_str_t *path); +static ngx_int_t ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx, + ngx_str_t *path); +static ngx_int_t ngx_http_file_cache_add(ngx_http_file_cache_t *cache, + ngx_http_cache_t *c); +static ngx_int_t ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx, + ngx_str_t *path); -ngx_int_t ngx_http_file_cache_get(ngx_http_request_t *r, - ngx_http_cache_ctx_t *ctx) -{ - ngx_uint_t i; - ngx_str_t *key; - ngx_http_cache_t *c; - MD5_CTX md5; - - c = r->cache; - - c->file.name.len = ctx->path->name.len + 1 + ctx->path->len + 32; - if (!(c->file.name.data = ngx_palloc(r->pool, c->file.name.len + 1))) { - return NGX_ABORT; - } - - MD5Init(&md5); +ngx_str_t ngx_http_cache_status[] = { + ngx_string("MISS"), + ngx_string("EXPIRED"), + ngx_string("STALE"), + ngx_string("UPDATING"), + ngx_string("HIT") +}; - key = c->key.elts; - for (i = 0; i < c->key.nelts; i++) { - MD5Update(&md5, key[i].data, key[i].len); - } - MD5Update(&md5, ctx->key.data, ctx->key.len); - - MD5Final(c->md5, &md5); - - ngx_memcpy(c->file.name.data, ctx->path->name.data, ctx->path->name.len); - - ngx_md5_text(c->file.name.data + ctx->path->name.len + 1 + ctx->path->len, - c->md5); - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "file cache key: %V, md5: %s", &ctx->key, - c->file.name.data + ctx->path->name.len + 1 + ctx->path->len); - - ngx_create_hashed_filename(&c->file, ctx->path); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "file cache name: %s", c->file.name.data); - - return ngx_http_file_cache_open(r->cache); -} +static u_char ngx_http_file_cache_key[] = { LF, 'K', 'E', 'Y', ':', ' ' }; -ngx_int_t ngx_http_file_cache_open(ngx_http_cache_t *c) +static ngx_int_t +ngx_http_file_cache_init(ngx_shm_zone_t *shm_zone, void *data) { - ssize_t n; - ngx_err_t err; - ngx_http_cache_header_t *h; + ngx_http_file_cache_t *ocache = data; - c->file.fd = ngx_open_file(c->file.name.data, - NGX_FILE_RDONLY, NGX_FILE_OPEN); - - if (c->file.fd == NGX_INVALID_FILE) { - err = ngx_errno; + size_t len; + ngx_http_file_cache_t *cache; - if (err == NGX_ENOENT || err == NGX_ENOTDIR) { - return NGX_DECLINED; - } + cache = shm_zone->data; - ngx_log_error(NGX_LOG_CRIT, c->log, ngx_errno, - ngx_open_file_n " \"%s\" failed", c->file.name.data); - return NGX_ERROR; - } - - if (c->uniq) { - if (ngx_fd_info(c->file.fd, &c->file.info) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_CRIT, c->log, ngx_errno, - ngx_fd_info_n " \"%s\" failed", c->file.name.data); + if (ocache) { + if (ngx_strcmp(cache->path->name.data, ocache->path->name.data) != 0) { + ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, + "cache \"%V\" uses the \"%V\" cache path " + "while previously it used the \"%V\" cache path", + &shm_zone->shm.name, &cache->path->name, + &ocache->path->name); return NGX_ERROR; } - if (ngx_file_uniq(&c->file.info) == c->uniq) { - if (ngx_close_file(c->file.fd) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, - ngx_close_file_n " \"%s\" failed", - c->file.name.data); + cache->sh = ocache->sh; + + cache->shpool = ocache->shpool; + cache->bsize = ocache->bsize; + + cache->max_size /= cache->bsize; + + return NGX_OK; + } + + cache->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + + if (shm_zone->shm.exists) { + cache->sh = cache->shpool->data; + cache->bsize = ngx_fs_bsize(cache->path->name.data); + + return NGX_OK; + } + + cache->sh = ngx_slab_alloc(cache->shpool, sizeof(ngx_http_file_cache_sh_t)); + if (cache->sh == NULL) { + return NGX_ERROR; + } + + cache->shpool->data = cache->sh; + + ngx_rbtree_init(&cache->sh->rbtree, &cache->sh->sentinel, + ngx_http_file_cache_rbtree_insert_value); + + ngx_queue_init(&cache->sh->queue); + + cache->sh->cold = 1; + cache->sh->size = 0; + + cache->bsize = ngx_fs_bsize(cache->path->name.data); + + cache->max_size /= cache->bsize; + + len = sizeof(" in cache keys zone \"\"") + shm_zone->shm.name.len; + + cache->shpool->log_ctx = ngx_slab_alloc(cache->shpool, len); + if (cache->shpool->log_ctx == NULL) { + return NGX_ERROR; + } + + ngx_sprintf(cache->shpool->log_ctx, " in cache keys zone \"%V\"%Z", + &shm_zone->shm.name); + + return NGX_OK; +} + + +void +ngx_http_file_cache_create_key(ngx_http_request_t *r) +{ + size_t len; + ngx_str_t *key; + ngx_uint_t i; + ngx_md5_t md5; + ngx_http_cache_t *c; + + c = r->cache; + + len = 0; + + ngx_crc32_init(c->crc32); + ngx_md5_init(&md5); + + key = c->keys.elts; + for (i = 0; i < c->keys.nelts; i++) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http cache key: \"%V\"", &key[i]); + + len += key[i].len; + + ngx_crc32_update(&c->crc32, key[i].data, key[i].len); + ngx_md5_update(&md5, key[i].data, key[i].len); + } + + c->header_start = sizeof(ngx_http_file_cache_header_t) + + sizeof(ngx_http_file_cache_key) + len + 1; + + ngx_crc32_final(c->crc32); + ngx_md5_final(c->key, &md5); +} + + +ngx_int_t +ngx_http_file_cache_open(ngx_http_request_t *r) +{ + u_char *p; + time_t now; + ssize_t n; + ngx_int_t rc, rv; + ngx_uint_t cold, test; + ngx_path_t *path; + ngx_http_cache_t *c; + ngx_pool_cleanup_t *cln; + ngx_open_file_info_t of; + ngx_http_file_cache_t *cache; + ngx_http_core_loc_conf_t *clcf; + ngx_http_file_cache_header_t *h; + + c = r->cache; + cache = c->file_cache; + + cln = ngx_pool_cleanup_add(r->pool, 0); + if (cln == NULL) { + return NGX_ERROR; + } + + rc = ngx_http_file_cache_exists(cache, c); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache exists: %i e:%d", rc, c->exists); + + if (rc == NGX_ERROR) { + return rc; + } + + cln->handler = ngx_http_file_cache_cleanup; + cln->data = c; + + if (rc == NGX_AGAIN) { + return rc; + } + + cold = cache->sh->cold; + + if (rc == NGX_OK) { + + if (c->error) { + return c->error; + } + + c->temp_file = 1; + test = c->exists ? 1 : 0; + rv = NGX_DECLINED; + + } else { /* rc == NGX_DECLINED */ + + if (c->min_uses > 1) { + + if (!cold) { + return NGX_AGAIN; } - return NGX_HTTP_CACHE_THE_SAME; + test = 1; + rv = NGX_AGAIN; + + } else { + c->temp_file = 1; + test = cold ? 1 : 0; + rv = NGX_DECLINED; } } - n = ngx_read_file(&c->file, c->buf->pos, c->buf->end - c->buf->last, 0); + path = cache->path; + + c->file.name.len = path->name.len + 1 + path->len + + 2 * NGX_HTTP_CACHE_KEY_LEN; + + c->file.name.data = ngx_pnalloc(r->pool, c->file.name.len + 1); + if (c->file.name.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(c->file.name.data, path->name.data, path->name.len); + + p = c->file.name.data + path->name.len + 1 + path->len; + p = ngx_hex_dump(p, c->key, NGX_HTTP_CACHE_KEY_LEN); + *p = '\0'; + + ngx_create_hashed_filename(path, c->file.name.data, c->file.name.len); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "cache file: \"%s\"", c->file.name.data); + + if (!test) { + return NGX_DECLINED; + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + ngx_memzero(&of, sizeof(ngx_open_file_info_t)); - if (n == NGX_ERROR || n == NGX_AGAIN) { + of.uniq = c->uniq; + of.valid = clcf->open_file_cache_valid; + of.min_uses = clcf->open_file_cache_min_uses; + of.events = clcf->open_file_cache_events; + of.directio = NGX_OPEN_FILE_DIRECTIO_OFF; + + if (ngx_open_cached_file(clcf->open_file_cache, &c->file.name, &of, r->pool) + != NGX_OK) + { + switch (of.err) { + + case 0: + return NGX_ERROR; + + case NGX_ENOENT: + case NGX_ENOTDIR: + return rv; + + default: + ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err, + ngx_open_file_n " \"%s\" failed", c->file.name.data); + return NGX_ERROR; + } + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache fd: %d", of.fd); + + c->file.fd = of.fd; + c->file.log = r->connection->log; + + c->buf = ngx_create_temp_buf(r->pool, c->body_start); + if (c->buf == NULL) { + return NGX_ERROR; + } + + n = ngx_read_file(&c->file, c->buf->pos, c->body_start, 0); + + if (n == NGX_ERROR) { return n; } - if (n <= c->header_size) { - ngx_log_error(NGX_LOG_CRIT, c->log, 0, + if ((size_t) n <= c->header_start) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, "cache file \"%s\" is too small", c->file.name.data); return NGX_ERROR; } - h = (ngx_http_cache_header_t *) c->buf->pos; - c->expires = h->expires; - c->last_modified= h->last_modified; - c->date = h->date; - c->length = h->length; + h = (ngx_http_file_cache_header_t *) c->buf->pos; - if (h->key_len > (size_t) (c->buf->end - c->buf->pos)) { - ngx_log_error(NGX_LOG_ALERT, c->log, 0, - "cache file \"%s\" is probably invalid", - c->file.name.data); + if (h->crc32 != c->crc32 || (size_t) h->header_start != c->header_start) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, + "cache file \"%s\" has md5 collision", c->file.name.data); return NGX_DECLINED; } -#if 0 + c->buf->last += n; - /* TODO */ + c->valid_sec = h->valid_sec; + c->last_modified = h->last_modified; + c->date = h->date; + c->valid_msec = h->valid_msec; + c->length = of.size; + c->body_start = h->body_start; - if (c->key_len && h->key_len != c->key_len) { + r->cached = 1; + + if (cold) { - ngx_strncmp(h->key, c->key_data, h->key_len) != 0)) + ngx_shmtx_lock(&cache->shpool->mutex); - h->key[h->key_len] = '\0'; - ngx_log_error(NGX_LOG_ALERT, c->log, 0, - "md5 collision: \"%s\" and \"%s\"", - h->key, c->key.data); - return NGX_DECLINED; + if (!c->node->exists) { + c->node->uses = 1; + c->node->body_start = c->body_start; + c->node->exists = 1; + c->node->uniq = of.uniq; + + cache->sh->size += (c->length + cache->bsize - 1) / cache->bsize; + } + + ngx_shmtx_unlock(&cache->shpool->mutex); } -#endif + now = ngx_time(); + + if (c->valid_sec < now) { - c->buf->last += n; + ngx_shmtx_lock(&cache->shpool->mutex); + + if (c->node->updating) { + rc = NGX_HTTP_CACHE_UPDATING; - if (c->expires < ngx_time()) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http file cache expired"); - return NGX_HTTP_CACHE_STALE; + } else { + c->node->updating = 1; + rc = NGX_HTTP_CACHE_STALE; + } + + ngx_shmtx_unlock(&cache->shpool->mutex); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache expired: %i %T %T", + rc, c->valid_sec, now); + + return rc; } - /* TODO: NGX_HTTP_CACHE_AGED */ - - /* STUB */ return NGX_DECLINED; - return NGX_OK; } -#if 0 +static ngx_int_t +ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c) +{ + ngx_int_t rc; + ngx_http_file_cache_node_t *fcn; + + ngx_shmtx_lock(&cache->shpool->mutex); + + fcn = ngx_http_file_cache_lookup(cache, c->key); + + if (fcn) { + ngx_queue_remove(&fcn->queue); + + if (fcn->error) { + + if (fcn->valid_sec < ngx_time()) { + goto renew; + } + + rc = NGX_OK; + + goto done; + } + + fcn->uses++; + fcn->count++; + + if (fcn->exists) { + + c->exists = fcn->exists; + c->body_start = fcn->body_start; + + rc = NGX_OK; + + goto done; + } + + if (fcn->uses >= c->min_uses) { + + c->exists = fcn->exists; + c->body_start = fcn->body_start; + + rc = NGX_OK; + + } else { + rc = NGX_AGAIN; + } + + goto done; + } + + fcn = ngx_slab_alloc_locked(cache->shpool, + sizeof(ngx_http_file_cache_node_t)); + if (fcn == NULL) { + ngx_shmtx_unlock(&cache->shpool->mutex); + + (void) ngx_http_file_cache_forced_expire(cache); + + ngx_shmtx_lock(&cache->shpool->mutex); + + fcn = ngx_slab_alloc_locked(cache->shpool, + sizeof(ngx_http_file_cache_node_t)); + if (fcn == NULL) { + rc = NGX_ERROR; + goto failed; + } + } + + ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t)); + + ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)], + NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)); + + ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node); + +renew: + + rc = NGX_DECLINED; + + fcn->uses = 1; + fcn->count = 1; + fcn->valid_msec = 0; + fcn->error = 0; + fcn->exists = 0; + fcn->valid_sec = 0; + fcn->uniq = 0; + fcn->body_start = 0; + fcn->length = 0; + +done: + + fcn->expire = ngx_time() + cache->inactive; + + ngx_queue_insert_head(&cache->sh->queue, &fcn->queue); + + c->uniq = fcn->uniq; + c->error = fcn->error; + c->node = fcn; + +failed: + + ngx_shmtx_unlock(&cache->shpool->mutex); + + return rc; +} -int ngx_http_cache_update_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx, - ngx_str_t *temp_file) +static ngx_http_file_cache_node_t * +ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key) { - int retry; - ngx_err_t err; + ngx_int_t rc; + ngx_rbtree_key_t node_key; + ngx_rbtree_node_t *node, *sentinel; + ngx_http_file_cache_node_t *fcn; + + ngx_memcpy((u_char *) &node_key, key, sizeof(ngx_rbtree_key_t)); + + node = cache->sh->rbtree.root; + sentinel = cache->sh->rbtree.sentinel; + + while (node != sentinel) { + + if (node_key < node->key) { + node = node->left; + continue; + } + + if (node_key > node->key) { + node = node->right; + continue; + } + + /* node_key == node->key */ + + do { + fcn = (ngx_http_file_cache_node_t *) node; - retry = 0; + rc = ngx_memcmp(&key[sizeof(ngx_rbtree_key_t)], fcn->key, + NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)); + + if (rc == 0) { + return fcn; + } + + node = (rc < 0) ? node->left : node->right; + + } while (node != sentinel && node_key == node->key); + + break; + } + + /* not found */ + + return NULL; +} + + +static void +ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) +{ + ngx_rbtree_node_t **p; + ngx_http_file_cache_node_t *cn, *cnt; for ( ;; ) { - if (ngx_rename_file(temp_file->data, ctx->file.name.data) == NGX_OK) { - return NGX_OK; + + if (node->key < temp->key) { + + p = &temp->left; + + } else if (node->key > temp->key) { + + p = &temp->right; + + } else { /* node->key == temp->key */ + + cn = (ngx_http_file_cache_node_t *) node; + cnt = (ngx_http_file_cache_node_t *) temp; + + p = (ngx_memcmp(cn->key, cnt->key, + NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)) + < 0) + ? &temp->left : &temp->right; + } + + if (*p == sentinel) { + break; } - err = ngx_errno; + temp = *p; + } + + *p = node; + node->parent = temp; + node->left = sentinel; + node->right = sentinel; + ngx_rbt_red(node); +} + + +void +ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf) +{ + ngx_http_file_cache_header_t *h = (ngx_http_file_cache_header_t *) buf; + + u_char *p; + ngx_str_t *key; + ngx_uint_t i; + ngx_http_cache_t *c; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache set header"); + + c = r->cache; + + h->valid_sec = c->valid_sec; + h->last_modified = c->last_modified; + h->date = c->date; + h->crc32 = c->crc32; + h->valid_msec = (u_short) c->valid_msec; + h->header_start = (u_short) c->header_start; + h->body_start = (u_short) c->body_start; + + p = buf + sizeof(ngx_http_file_cache_header_t); + + p = ngx_cpymem(p, ngx_http_file_cache_key, sizeof(ngx_http_file_cache_key)); + + key = c->keys.elts; + for (i = 0; i < c->keys.nelts; i++) { + p = ngx_copy(p, key[i].data, key[i].len); + } + + *p = LF; +} + + +void +ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf) +{ + off_t size; + ngx_int_t rc; + ngx_file_uniq_t uniq; + ngx_file_info_t fi; + ngx_http_cache_t *c; + ngx_ext_rename_file_t ext; + ngx_http_file_cache_t *cache; + + c = r->cache; + + if (c->updated) { + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache update"); + + c->updated = 1; + + cache = c->file_cache; + + uniq = 0; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache rename: \"%s\" to \"%s\"", + tf->file.name.data, c->file.name.data); + + ext.access = NGX_FILE_OWNER_ACCESS; + ext.path_access = NGX_FILE_OWNER_ACCESS; + ext.time = -1; + ext.create_path = 1; + ext.delete_file = 1; + ext.log_rename_error = 1; + ext.log = r->connection->log; + + rc = ngx_ext_rename_file(&tf->file.name, &c->file.name, &ext); + + if (rc == NGX_OK) { + + if (ngx_fd_info(tf->file.fd, &fi) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, + ngx_fd_info_n " \"%s\" failed", tf->file.name.data); + + rc = NGX_ERROR; + + } else { + uniq = ngx_file_uniq(&fi); + } + } + + size = (c->length + cache->bsize - 1) / cache->bsize; + + ngx_shmtx_lock(&cache->shpool->mutex); + + c->node->count--; + c->node->uniq = uniq; + c->node->body_start = c->body_start; -#if (NGX_WIN32) - if (err == NGX_EEXIST) { - if (ngx_win32_rename_file(temp_file, &ctx->file.name, r->pool) - == NGX_ERROR) - { - return NGX_ERROR; + size = size - (c->node->length + cache->bsize - 1) / cache->bsize; + + c->node->length = c->length; + + cache->sh->size += size; + + if (rc == NGX_OK) { + c->node->exists = 1; + } + + c->node->updating = 0; + + ngx_shmtx_unlock(&cache->shpool->mutex); +} + + +ngx_int_t +ngx_http_cache_send(ngx_http_request_t *r) +{ + ngx_int_t rc; + ngx_buf_t *b; + ngx_chain_t out; + ngx_http_cache_t *c; + + c = r->cache; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache send: %s", c->file.name.data); + + /* we need to allocate all before the header would be sent */ + + b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); + if (b == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t)); + if (b->file == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + rc = ngx_http_send_header(r); + + if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { + return rc; + } + + b->file_pos = c->body_start; + b->file_last = c->length; + + b->in_file = (c->length - c->body_start) ? 1: 0; + b->last_buf = (r == r->main) ? 1: 0; + b->last_in_chain = 1; + + b->file->fd = c->file.fd; + b->file->name = c->file.name; + b->file->log = r->connection->log; + + out.buf = b; + out.next = NULL; + + return ngx_http_output_filter(r, &out); +} + + +void +ngx_http_file_cache_free(ngx_http_request_t *r, ngx_temp_file_t *tf) +{ + ngx_http_cache_t *c; + ngx_http_file_cache_t *cache; + + c = r->cache; + + if (c->updated) { + return; + } + + c->updated = 1; + + cache = c->file_cache; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache free"); + + ngx_shmtx_lock(&cache->shpool->mutex); + + c->node->count--; + + if (c->error) { + c->node->valid_sec = c->valid_sec; + c->node->valid_msec = c->valid_msec; + c->node->error = c->error; + } + + c->node->updating = 0; + + ngx_shmtx_unlock(&cache->shpool->mutex); + + if (c->temp_file) { + if (tf && tf->file.fd != NGX_INVALID_FILE) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache incomplete: \"%s\"", + tf->file.name.data); + + if (ngx_delete_file(tf->file.name.data) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", + tf->file.name.data); } } -#endif - - if (retry || (err != NGX_ENOENT && err != NGX_ENOTDIR)) { - ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, - ngx_rename_file_n "(\"%s\", \"%s\") failed", - temp_file->data, ctx->file.name.data); - - return NGX_ERROR; - } - - if (ngx_create_path(&ctx->file, ctx->path) == NGX_ERROR) { - return NGX_ERROR; - } - - retry = 1; } } -#endif +static void +ngx_http_file_cache_cleanup(void *data) +{ + ngx_http_cache_t *c = data; + + ngx_http_file_cache_t *cache; + + if (c->updated) { + return; + } + + c->updated = 1; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->file.log, 0, + "http file cache cleanup"); + + if (c->error) { + return; + } + + cache = c->file_cache; + + ngx_shmtx_lock(&cache->shpool->mutex); + + c->node->count--; + + ngx_shmtx_unlock(&cache->shpool->mutex); +} + + +static time_t +ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache) +{ + u_char *name; + size_t len; + time_t wait; + ngx_uint_t tries; + ngx_path_t *path; + ngx_queue_t *q; + ngx_http_file_cache_node_t *fcn; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "http file cache forced expire"); + + path = cache->path; + len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN; + + name = ngx_alloc(len + 1, ngx_cycle->log); + if (name == NULL) { + return 10; + } + + ngx_memcpy(name, path->name.data, path->name.len); + + wait = 10; + tries = 0; + + ngx_shmtx_lock(&cache->shpool->mutex); + + for (q = ngx_queue_last(&cache->sh->queue); + q != ngx_queue_sentinel(&cache->sh->queue); + q = ngx_queue_prev(q)) + { + fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue); + + ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "http file cache forced expire: #%d %d %02xd%02xd%02xd%02xd", + fcn->count, fcn->exists, + fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]); + + if (fcn->count) { + + if (tries++ < 20) { + continue; + } + + wait = 1; + + break; + } + + if (!fcn->exists) { + + ngx_queue_remove(q); + ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node); + ngx_slab_free_locked(cache->shpool, fcn); + + break; + } + + ngx_http_file_cache_delete(cache, q, name); + + break; + } + + ngx_shmtx_unlock(&cache->shpool->mutex); + + ngx_free(name); + + return wait; +} -ngx_int_t ngx_http_cache_cleaner_handler(ngx_gc_t *gc, ngx_str_t *name, - ngx_dir_t *dir) +static time_t +ngx_http_file_cache_expire(ngx_http_file_cache_t *cache) +{ + u_char *name, *p; + size_t len; + time_t now, wait; + ngx_path_t *path; + ngx_queue_t *q; + ngx_http_file_cache_node_t *fcn; + u_char key[2 * NGX_HTTP_CACHE_KEY_LEN]; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "http file cache expire"); + + path = cache->path; + len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN; + + name = ngx_alloc(len + 1, ngx_cycle->log); + if (name == NULL) { + return 10; + } + + ngx_memcpy(name, path->name.data, path->name.len); + + now = ngx_time(); + + ngx_shmtx_lock(&cache->shpool->mutex); + + for ( ;; ) { + + if (ngx_queue_empty(&cache->sh->queue)) { + wait = 10; + break; + } + + q = ngx_queue_last(&cache->sh->queue); + + fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue); + + wait = fcn->expire - now; + + if (wait > 0) { + wait = wait > 10 ? 10 : wait; + break; + } + + ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "http file cache expire: #%d %d %02xd%02xd%02xd%02xd", + fcn->count, fcn->exists, + fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]); + + if (fcn->count) { + + p = ngx_hex_dump(key, (u_char *) &fcn->node.key, + sizeof(ngx_rbtree_key_t)); + + len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t); + (void) ngx_hex_dump(p, fcn->key, len); + + /* + * abnormally exited workers may leave locked cache entries, + * and although it may be safe to remove them completely, + * we prefer to remove them from inactive queue and rbtree + * only, and to allow other leaks + */ + + ngx_queue_remove(q); + ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node); + + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "ignore long locked inactive cache entry %*s, count:%d", + 2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count); + + continue; + } + + if (!fcn->exists) { + + ngx_queue_remove(q); + ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node); + ngx_slab_free_locked(cache->shpool, fcn); + + continue; + } + + ngx_http_file_cache_delete(cache, q, name); + } + + ngx_shmtx_unlock(&cache->shpool->mutex); + + ngx_free(name); + + return wait; +} + + +static void +ngx_http_file_cache_delete(ngx_http_file_cache_t *cache, ngx_queue_t *q, + u_char *name) { - int rc; - ngx_buf_t buf; - ngx_http_cache_t c; - u_char data[sizeof(ngx_http_cache_header_t)]; + u_char *p; + size_t len; + ngx_path_t *path; + ngx_http_file_cache_node_t *fcn; + + fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue); + + cache->sh->size -= (fcn->length + cache->bsize - 1) / cache->bsize; + + path = cache->path; + + p = name + path->name.len + 1 + path->len; + + p = ngx_hex_dump(p, (u_char *) &fcn->node.key, sizeof(ngx_rbtree_key_t)); + + len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t); + p = ngx_hex_dump(p, fcn->key, len); + *p = '\0'; + + ngx_queue_remove(q); + + ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node); + + ngx_slab_free_locked(cache->shpool, fcn); + + ngx_shmtx_unlock(&cache->shpool->mutex); + + len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN; + + ngx_create_hashed_filename(path, name, len); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "http file cache expire: \"%s\"", name); + + if (ngx_delete_file(name) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", name); + } + + ngx_shmtx_lock(&cache->shpool->mutex); +} + + +static time_t +ngx_http_file_cache_manager(void *data) +{ + ngx_http_file_cache_t *cache = data; + + off_t size; + time_t next; + ngx_tree_ctx_t tree; + + if (cache->sh->cold) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "http file cache manager update"); + + tree.init_handler = NULL; + tree.file_handler = ngx_http_file_cache_manage_file; + tree.pre_tree_handler = ngx_http_file_cache_noop; + tree.post_tree_handler = ngx_http_file_cache_noop; + tree.spec_handler = ngx_http_file_cache_delete_file; + tree.data = cache; + tree.alloc = 0; + tree.log = ngx_cycle->log; + + cache->last = ngx_current_msec; + cache->files = 0; + + if (ngx_walk_tree(&tree, &cache->path->name) == NGX_ABORT) { + return 10; + } + + cache->sh->cold = 0; + + ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, + "http file cache: %V %.3fM, bsize: %uz", + &cache->path->name, + ((double) cache->sh->size * cache->bsize) / (1024 * 1024), + cache->bsize); + } + + next = ngx_http_file_cache_expire(cache); + + cache->last = ngx_current_msec; + cache->files = 0; + + for ( ;; ) { + ngx_shmtx_lock(&cache->shpool->mutex); + + size = cache->sh->size; + + ngx_shmtx_unlock(&cache->shpool->mutex); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "http file cache size: %O", size); + + if (size < cache->max_size) { + return next; + } + + next = ngx_http_file_cache_forced_expire(cache); + + if (ngx_http_file_cache_manager_sleep(cache) != NGX_OK) { + return next; + } + } +} + + +static ngx_int_t +ngx_http_file_cache_manager_sleep(ngx_http_file_cache_t *cache) +{ + ngx_msec_t elapsed; + + if (cache->files++ > 100) { + + ngx_time_update(0, 0); + + elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last)); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "http file cache manager time: %M", elapsed); + + if (elapsed > 200) { + + /* + * if processing 100 files takes more than 200ms, + * it seems that many operations require disk i/o, + * therefore sleep 200ms + */ + + ngx_msleep(200); + + ngx_time_update(0, 0); + } + + cache->last = ngx_current_msec; + cache->files = 0; + } + + return (ngx_quit || ngx_terminate) ? NGX_ABORT : NGX_OK; +} + + +static ngx_int_t +ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path) +{ + return NGX_OK; +} + + +static ngx_int_t +ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, ngx_str_t *path) +{ + ngx_http_file_cache_t *cache; + + cache = ctx->data; + + if (ngx_http_file_cache_add_file(ctx, path) != NGX_OK) { + (void) ngx_http_file_cache_delete_file(ctx, path); + } + + return ngx_http_file_cache_manager_sleep(cache); +} + + +static ngx_int_t +ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx, ngx_str_t *name) +{ + u_char *p; + ngx_fd_t fd; + ngx_int_t n; + ngx_uint_t i; + ngx_file_info_t fi; + ngx_http_cache_t c; + ngx_http_file_cache_t *cache; + ngx_http_file_cache_header_t h; + + if (name->len < 2 * NGX_HTTP_CACHE_KEY_LEN) { + return NGX_ERROR; + } ngx_memzero(&c, sizeof(ngx_http_cache_t)); - c.file.fd = NGX_INVALID_FILE; - c.file.name = *name; - c.file.log = gc->log; + fd = ngx_open_file(name->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); - c.header_size = sizeof(ngx_http_cache_header_t); - c.buf = &buf; - c.log = gc->log; - c.key_len = 0; + if (fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, + ngx_open_file_n " \"%s\" failed", name->data); + return NGX_ERROR; + } - buf.memory = 1; - buf.temporary = 1; - buf.pos = data; - buf.last = data; - buf.start = data; - buf.end = data + sizeof(ngx_http_cache_header_t); + c.file.fd = fd; + c.file.name = *name; + c.file.log = ctx->log; - rc = ngx_http_file_cache_open(&c); - - /* TODO: NGX_AGAIN */ - - if (rc != NGX_ERROR&& rc != NGX_DECLINED && rc != NGX_HTTP_CACHE_STALE) { - return NGX_OK; + n = ngx_read_file(&c.file, (u_char *) &h, + sizeof(ngx_http_file_cache_header_t), 0); + if (n == NGX_ERROR) { + return NGX_ERROR; } - if (ngx_delete_file(name->data) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_CRIT, c.log, ngx_errno, - ngx_delete_file_n " \"%s\" failed", name->data); + if ((size_t) n < sizeof(ngx_http_file_cache_header_t)) { + ngx_log_error(NGX_LOG_CRIT, ctx->log, 0, + "cache file \"%s\" is too small", name->data); + return NGX_ERROR; + } + + if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, + ngx_fd_info_n " \"%s\" failed", name->data); + + } else { + c.uniq = ngx_file_uniq(&fi); + c.valid_sec = h.valid_sec; + c.valid_msec = h.valid_msec; + c.body_start = h.body_start; + c.length = ngx_file_size(&fi); + } + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", name->data); + } + + if (c.body_start == 0) { return NGX_ERROR; } - gc->deleted++; - gc->freed += ngx_de_size(dir); + p = &name->data[name->len - 2 * NGX_HTTP_CACHE_KEY_LEN]; + + for (i = 0; i < NGX_HTTP_CACHE_KEY_LEN; i++) { + n = ngx_hextoi(p, 2); + + if (n == NGX_ERROR) { + return NGX_ERROR; + } + + p += 2; + + c.key[i] = (u_char) n; + } + + cache = ctx->data; + + return ngx_http_file_cache_add(cache, &c); +} + + +static ngx_int_t +ngx_http_file_cache_add(ngx_http_file_cache_t *cache, ngx_http_cache_t *c) +{ + ngx_http_file_cache_node_t *fcn; + + ngx_shmtx_lock(&cache->shpool->mutex); + + fcn = ngx_http_file_cache_lookup(cache, c->key); + + if (fcn == NULL) { + + fcn = ngx_slab_alloc_locked(cache->shpool, + sizeof(ngx_http_file_cache_node_t)); + if (fcn == NULL) { + ngx_shmtx_unlock(&cache->shpool->mutex); + return NGX_ERROR; + } + + ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t)); + + ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)], + NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)); + + ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node); + + fcn->uses = 1; + fcn->count = 0; + fcn->valid_msec = c->valid_msec; + fcn->error = 0; + fcn->exists = 1; + fcn->uniq = c->uniq; + fcn->valid_sec = c->valid_sec; + fcn->body_start = c->body_start; + fcn->length = c->length; + + cache->sh->size += (c->length + cache->bsize - 1) / cache->bsize; + + } else { + ngx_queue_remove(&fcn->queue); + } + + fcn->expire = ngx_time() + cache->inactive; + + ngx_queue_insert_head(&cache->sh->queue, &fcn->queue); + + ngx_shmtx_unlock(&cache->shpool->mutex); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0, + "http file cache delete: \"%s\"", path->data); + + if (ngx_delete_file(path->data) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", path->data); + } return NGX_OK; } + + +time_t +ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status) +{ + ngx_uint_t i; + ngx_http_cache_valid_t *valid; + + if (cache_valid == NULL) { + return 0; + } + + valid = cache_valid->elts; + for (i = 0; i < cache_valid->nelts; i++) { + + if (valid[i].status == 0) { + return valid[i].valid; + } + + if (valid[i].status == status) { + return valid[i].valid; + } + } + + return 0; +} + + +char * +ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + off_t max_size; + u_char *last, *p; + time_t inactive; + ssize_t size; + ngx_str_t s, name, *value; + ngx_uint_t i, n; + ngx_http_file_cache_t *cache; + + cache = ngx_pcalloc(cf->pool, sizeof(ngx_http_file_cache_t)); + if (cache == NULL) { + return NGX_CONF_ERROR; + } + + cache->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t)); + if (cache->path == NULL) { + return NGX_CONF_ERROR; + } + + inactive = 600; + + name.len = 0; + size = 0; + max_size = NGX_MAX_OFF_T_VALUE; + + value = cf->args->elts; + + cache->path->name = value[1]; + + if (cache->path->name.data[cache->path->name.len - 1] == '/') { + cache->path->name.len--; + } + + if (ngx_conf_full_name(cf->cycle, &cache->path->name, 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + + for (i = 2; i < cf->args->nelts; i++) { + + if (ngx_strncmp(value[i].data, "levels=", 7) == 0) { + + p = value[i].data + 7; + last = value[i].data + value[i].len; + + for (n = 0; n < 3 && p < last; n++) { + + if (*p > '0' && *p < '3') { + + cache->path->level[n] = *p++ - '0'; + cache->path->len += cache->path->level[n] + 1; + + if (p == last) { + break; + } + + if (*p++ == ':' && n < 2 && p != last) { + continue; + } + + goto invalid_levels; + } + + goto invalid_levels; + } + + if (cache->path->len < 10 + 3) { + continue; + } + + invalid_levels: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid \"levels\" \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (ngx_strncmp(value[i].data, "keys_zone=", 10) == 0) { + + name.data = value[i].data + 10; + + p = (u_char *) ngx_strchr(name.data, ':'); + + if (p) { + *p = '\0'; + + name.len = p - name.data; + + p++; + + s.len = value[i].data + value[i].len - p; + s.data = p; + + size = ngx_parse_size(&s); + if (size > 8191) { + continue; + } + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid keys zone size \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) { + + s.len = value[i].len - 9; + s.data = value[i].data + 9; + + inactive = ngx_parse_time(&s, 1); + if (inactive < 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid inactive value \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "max_size=", 9) == 0) { + + s.len = value[i].len - 9; + s.data = value[i].data + 9; + + max_size = ngx_parse_offset(&s); + if (max_size < 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid max_size value \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (name.len == 0 || size == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%V\" must have \"keys_zone\" parameter", + &cmd->name); + return NGX_CONF_ERROR; + } + + cache->path->manager = ngx_http_file_cache_manager; + cache->path->data = cache; + + if (ngx_add_path(cf, &cache->path) != NGX_OK) { + return NGX_CONF_ERROR; + } + + cache->shm_zone = ngx_shared_memory_add(cf, &name, size, cmd->post); + if (cache->shm_zone == NULL) { + return NGX_CONF_ERROR; + } + + if (cache->shm_zone->data) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate zone \"%V\"", &name); + return NGX_CONF_ERROR; + } + + + cache->shm_zone->init = ngx_http_file_cache_init; + cache->shm_zone->data = cache; + + cache->inactive = inactive; + cache->max_size = max_size; + + return NGX_CONF_OK; +} + + +char * +ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *p = conf; + + time_t valid; + ngx_str_t *value; + ngx_uint_t i, n, status; + ngx_array_t **a; + ngx_http_cache_valid_t *v; + static ngx_uint_t statuses[] = { 200, 301, 302 }; + + a = (ngx_array_t **) (p + cmd->offset); + + if (*a == NGX_CONF_UNSET_PTR) { + *a = ngx_array_create(cf->pool, 1, sizeof(ngx_http_cache_valid_t)); + if (*a == NULL) { + return NGX_CONF_ERROR; + } + } + + value = cf->args->elts; + n = cf->args->nelts - 1; + + valid = ngx_parse_time(&value[n], 1); + if (valid < 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid time value \"%V\"", &value[n]); + return NGX_CONF_ERROR; + } + + if (n == 1) { + + for (i = 0; i < 3; i++) { + v = ngx_array_push(*a); + if (v == NULL) { + return NGX_CONF_ERROR; + } + + v->status = statuses[i]; + v->valid = valid; + } + + return NGX_CONF_OK; + } + + for (i = 1; i < n; i++) { + + if (ngx_strcmp(value[i].data, "any") == 0) { + + status = 0; + + } else { + + status = ngx_atoi(value[i].data, value[i].len); + if (status < 100) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid status \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + } + + v = ngx_array_push(*a); + if (v == NULL) { + return NGX_CONF_ERROR; + } + + v->status = status; + v->valid = valid; + } + + return NGX_CONF_OK; +} diff --git a/src/http/ngx_http_header_filter_module.c b/src/http/ngx_http_header_filter_module.c --- a/src/http/ngx_http_header_filter_module.c +++ b/src/http/ngx_http_header_filter_module.c @@ -61,7 +61,8 @@ static ngx_str_t ngx_http_status_lines[] /* ngx_null_string, */ /* "207 Multi-Status" */ -#define NGX_HTTP_LEVEL_200 7 +#define NGX_HTTP_LAST_LEVEL_200 207 +#define NGX_HTTP_LEVEL_200 (NGX_HTTP_LAST_LEVEL_200 - 200) /* ngx_null_string, */ /* "300 Multiple Choices" */ @@ -74,7 +75,8 @@ static ngx_str_t ngx_http_status_lines[] /* ngx_null_string, */ /* "306 unused" */ /* ngx_null_string, */ /* "307 Temporary Redirect" */ -#define NGX_HTTP_LEVEL_300 4 +#define NGX_HTTP_LAST_LEVEL_300 305 +#define NGX_HTTP_LEVEL_300 (NGX_HTTP_LAST_LEVEL_300 - 301) ngx_string("400 Bad Request"), ngx_string("401 Unauthorized"), @@ -106,7 +108,8 @@ static ngx_str_t ngx_http_status_lines[] /* ngx_null_string, */ /* "423 Locked" */ /* ngx_null_string, */ /* "424 Failed Dependency" */ -#define NGX_HTTP_LEVEL_400 17 +#define NGX_HTTP_LAST_LEVEL_400 417 +#define NGX_HTTP_LEVEL_400 (NGX_HTTP_LAST_LEVEL_400 - 400) ngx_string("500 Internal Server Error"), ngx_string("501 Method Not Implemented"), @@ -120,6 +123,9 @@ static ngx_str_t ngx_http_status_lines[] /* ngx_null_string, */ /* "508 unused" */ /* ngx_null_string, */ /* "509 unused" */ /* ngx_null_string, */ /* "510 Not Extended" */ + +#define NGX_HTTP_LAST_LEVEL_500 508 + }; @@ -153,16 +159,20 @@ ngx_http_header_filter(ngx_http_request_ { u_char *p; size_t len; - ngx_str_t host; + ngx_str_t host, *status_line; ngx_buf_t *b; - ngx_uint_t status, i; + ngx_uint_t status, i, port; ngx_chain_t out; ngx_list_part_t *part; ngx_table_elt_t *header; + ngx_connection_t *c; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; - /* AF_INET only */ - u_char addr[NGX_INET_ADDRSTRLEN]; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + u_char addr[NGX_SOCKADDR_STRLEN]; r->header_sent = 1; @@ -196,17 +206,21 @@ ngx_http_header_filter(ngx_http_request_ if (r->headers_out.status_line.len) { len += r->headers_out.status_line.len; + status_line = &r->headers_out.status_line; #if (NGX_SUPPRESS_WARN) - status = NGX_INVALID_ARRAY_INDEX; + status = 0; #endif } else { - if (r->headers_out.status < NGX_HTTP_MOVED_PERMANENTLY) { + status = r->headers_out.status; + + if (status >= NGX_HTTP_OK + && status < NGX_HTTP_LAST_LEVEL_200) + { /* 2XX */ - status = r->headers_out.status - NGX_HTTP_OK; - if (r->headers_out.status == NGX_HTTP_NO_CONTENT) { + if (status == NGX_HTTP_NO_CONTENT) { r->header_only = 1; r->headers_out.content_type.len = 0; r->headers_out.content_type.data = NULL; @@ -216,30 +230,50 @@ ngx_http_header_filter(ngx_http_request_ r->headers_out.content_length_n = -1; } - } else if (r->headers_out.status < NGX_HTTP_BAD_REQUEST) { + status -= NGX_HTTP_OK; + status_line = &ngx_http_status_lines[status]; + len += ngx_http_status_lines[status].len; + + } else if (status >= NGX_HTTP_MOVED_PERMANENTLY + && status < NGX_HTTP_LAST_LEVEL_300) + { /* 3XX */ - status = r->headers_out.status - NGX_HTTP_MOVED_PERMANENTLY - + NGX_HTTP_LEVEL_200; - if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) { + if (status == NGX_HTTP_NOT_MODIFIED) { r->header_only = 1; } - } else if (r->headers_out.status < NGX_HTTP_INTERNAL_SERVER_ERROR) { + status = status - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_LEVEL_200; + status_line = &ngx_http_status_lines[status]; + len += ngx_http_status_lines[status].len; + + } else if (status >= NGX_HTTP_BAD_REQUEST + && status < NGX_HTTP_LAST_LEVEL_400) + { /* 4XX */ - status = r->headers_out.status - NGX_HTTP_BAD_REQUEST - + NGX_HTTP_LEVEL_200 - + NGX_HTTP_LEVEL_300; + status = status - NGX_HTTP_BAD_REQUEST + + NGX_HTTP_LEVEL_200 + + NGX_HTTP_LEVEL_300; + + status_line = &ngx_http_status_lines[status]; + len += ngx_http_status_lines[status].len; + + } else if (status >= NGX_HTTP_INTERNAL_SERVER_ERROR + && status < NGX_HTTP_LAST_LEVEL_500) + { + /* 5XX */ + status = status - NGX_HTTP_INTERNAL_SERVER_ERROR + + NGX_HTTP_LEVEL_200 + + NGX_HTTP_LEVEL_300 + + NGX_HTTP_LEVEL_400; + + status_line = &ngx_http_status_lines[status]; + len += ngx_http_status_lines[status].len; } else { - /* 5XX */ - status = r->headers_out.status - NGX_HTTP_INTERNAL_SERVER_ERROR - + NGX_HTTP_LEVEL_200 - + NGX_HTTP_LEVEL_300 - + NGX_HTTP_LEVEL_400; + len += NGX_INT_T_LEN; + status_line = NULL; } - - len += ngx_http_status_lines[status].len; } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); @@ -276,6 +310,8 @@ ngx_http_header_filter(ngx_http_request_ len += sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1; } + c = r->connection; + if (r->headers_out.location && r->headers_out.location->value.len && r->headers_out.location->value.data[0] == '/') @@ -290,38 +326,53 @@ ngx_http_header_filter(ngx_http_request_ host = r->headers_in.server; } else { + host.len = NGX_SOCKADDR_STRLEN; host.data = addr; - if (ngx_http_server_addr(r, &host) != NGX_OK) { + if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) { return NGX_ERROR; } } -#if (NGX_HTTP_SSL) - if (r->connection->ssl) { - len += sizeof("Location: https://") - 1 - + host.len - + r->headers_out.location->value.len + 2; + switch (c->local_sockaddr->sa_family) { - if (clcf->port_in_redirect && r->port != 443) { - len += r->port_text->len; - } +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) c->local_sockaddr; + port = ntohs(sin6->sin6_port); + break; +#endif + default: /* AF_INET */ + sin = (struct sockaddr_in *) c->local_sockaddr; + port = ntohs(sin->sin_port); + break; + } - } else + len += sizeof("Location: https://") - 1 + + host.len + + r->headers_out.location->value.len + 2; + + if (clcf->port_in_redirect) { + +#if (NGX_HTTP_SSL) + if (c->ssl) + port = (port == 443) ? 0 : port; + else #endif - { - len += sizeof("Location: http://") - 1 - + host.len - + r->headers_out.location->value.len + 2; + port = (port == 80) ? 0 : port; - if (clcf->port_in_redirect && r->port != 80) { - len += r->port_text->len; - } + } else { + port = 0; + } + + if (port) { + len += sizeof(":65535") - 1; } } else { host.len = 0; host.data = NULL; + port = 0; } if (r->chunked) { @@ -385,13 +436,11 @@ ngx_http_header_filter(ngx_http_request_ b->last = ngx_cpymem(b->last, "HTTP/1.1 ", sizeof("HTTP/1.x ") - 1); /* status line */ - if (r->headers_out.status_line.len) { - b->last = ngx_copy(b->last, r->headers_out.status_line.data, - r->headers_out.status_line.len); + if (status_line) { + b->last = ngx_copy(b->last, status_line->data, status_line->len); } else { - b->last = ngx_copy(b->last, ngx_http_status_lines[status].data, - ngx_http_status_lines[status].len); + b->last = ngx_sprintf(b->last, "%ui", status); } *b->last++ = CR; *b->last++ = LF; @@ -465,7 +514,7 @@ ngx_http_header_filter(ngx_http_request_ sizeof("Location: http") - 1); #if (NGX_HTTP_SSL) - if (r->connection->ssl) { + if (c->ssl) { *b->last++ ='s'; } #endif @@ -473,21 +522,8 @@ ngx_http_header_filter(ngx_http_request_ *b->last++ = ':'; *b->last++ = '/'; *b->last++ = '/'; b->last = ngx_copy(b->last, host.data, host.len); - if (clcf->port_in_redirect) { -#if (NGX_HTTP_SSL) - if (r->connection->ssl) { - if (r->port != 443) { - b->last = ngx_copy(b->last, r->port_text->data, - r->port_text->len); - } - } else -#endif - { - if (r->port != 80) { - b->last = ngx_copy(b->last, r->port_text->data, - r->port_text->len); - } - } + if (port) { + b->last = ngx_sprintf(b->last, ":%ui", port); } b->last = ngx_copy(b->last, r->headers_out.location->value.data, @@ -555,8 +591,8 @@ ngx_http_header_filter(ngx_http_request_ *b->last++ = CR; *b->last++ = LF; } - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "%*s\n", (size_t) (b->last - b->pos), b->pos); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "%*s", (size_t) (b->last - b->pos), b->pos); /* the end of HTTP header */ *b->last++ = CR; *b->last++ = LF; diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -143,7 +143,7 @@ ngx_http_parse_request_line(ngx_http_req break; } - if (ch < 'A' || ch > 'Z') { + if ((ch < 'A' || ch > 'Z') && ch != '_') { return NGX_HTTP_PARSE_INVALID_METHOD; } @@ -257,7 +257,7 @@ ngx_http_parse_request_line(ngx_http_req break; } - if (ch < 'A' || ch > 'Z') { + if ((ch < 'A' || ch > 'Z') && ch != '_') { return NGX_HTTP_PARSE_INVALID_METHOD; } @@ -1337,12 +1337,7 @@ ngx_http_parse_unsafe_uri(ngx_http_reque goto unsafe; } - if (p[0] == '.' && len == 3 && p[1] == '.' && (p[2] == '/' -#if (NGX_WIN32) - || p[2] == '\\' -#endif - )) - { + if (p[0] == '.' && len == 3 && p[1] == '.' && (ngx_path_separator(p[2]))) { goto unsafe; } @@ -1367,30 +1362,22 @@ ngx_http_parse_unsafe_uri(ngx_http_reque continue; } - if ((ch == '/' -#if (NGX_WIN32) - || ch == '\\' -#endif - ) && len > 2) - { + if (ngx_path_separator(ch) && len > 2) { + /* detect "/../" */ - if (p[0] == '.' && p[1] == '.' && p[2] == '/') { + if (p[0] == '.' && p[1] == '.' && ngx_path_separator(p[2])) { goto unsafe; } #if (NGX_WIN32) - if (p[2] == '\\') { - goto unsafe; - } - if (len > 3) { /* detect "/.../" */ if (p[0] == '.' && p[1] == '.' && p[2] == '.' - && (p[3] == '/' || p[3] == '\\')) + && ngx_path_separator(p[3])) { goto unsafe; } @@ -1481,3 +1468,81 @@ ngx_http_parse_multi_header_lines(ngx_ar return NGX_DECLINED; } + + +ngx_int_t +ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value) +{ + u_char *p, *last; + + if (r->args.len == 0) { + return NGX_DECLINED; + } + + p = r->args.data; + last = p + r->args.len; + + for ( /* void */ ; p < last; p++) { + + /* we need '=' after name, so drop one char from last */ + + p = ngx_strlcasestrn(p, last - 1, name, len - 1); + + if (p == NULL) { + return NGX_DECLINED; + } + + if ((p == r->args.data || *(p - 1) == '&') && *(p + len) == '=') { + + value->data = p + len + 1; + + p = ngx_strlchr(p, last, '&'); + + if (p == NULL) { + p = r->args.data + r->args.len; + } + + value->len = p - value->data; + + return NGX_OK; + } + } + + return NGX_DECLINED; +} + + +void +ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args) +{ + u_char ch, *p, *last; + + p = uri->data; + + last = p + uri->len; + + args->len = 0; + + while (p < last) { + + ch = *p++; + + if (ch == '?') { + args->len = last - p; + args->data = p; + + uri->len = p - 1 - uri->data; + + if (ngx_strlchr(p, last, '\0') != NULL) { + r->zero_in_uri = 1; + } + + return; + } + + if (ch == '\0') { + r->zero_in_uri = 1; + continue; + } + } +} diff --git a/src/http/ngx_http_postpone_filter_module.c b/src/http/ngx_http_postpone_filter_module.c --- a/src/http/ngx_http_postpone_filter_module.c +++ b/src/http/ngx_http_postpone_filter_module.c @@ -9,8 +9,8 @@ #include -static ngx_int_t - ngx_http_postpone_filter_output_postponed_request(ngx_http_request_t *r); +static ngx_int_t ngx_http_postpone_filter_add(ngx_http_request_t *r, + ngx_chain_t *in); static ngx_int_t ngx_http_postpone_filter_init(ngx_conf_t *cf); @@ -51,174 +51,119 @@ static ngx_http_output_body_filter_pt static ngx_int_t ngx_http_postpone_filter(ngx_http_request_t *r, ngx_chain_t *in) { - ngx_int_t rc; - ngx_chain_t *out; - ngx_http_postponed_request_t *pr, **ppr; + ngx_connection_t *c; + ngx_http_postponed_request_t *pr; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + c = r->connection; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http postpone filter \"%V?%V\" %p", &r->uri, &r->args, in); - if (r != r->connection->data || (r->postponed && in)) { - - if (r->postponed) { - for (pr = r->postponed; pr->next; pr = pr->next) { /* void */ } - - ppr = pr->request ? &pr->next : NULL; - - } else { - ppr = &r->postponed; -#if (NGX_SUPPRESS_WARN) - pr = NULL; -#endif - } + if (r != c->data) { - if (ppr) { - pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t)); - if (pr == NULL) { - return NGX_ERROR; - } - - *ppr = pr; - - pr->request = NULL; - pr->out = NULL; - pr->next = NULL; - } - - if (ngx_chain_add_copy(r->pool, &pr->out, in) == NGX_ERROR) { - return NGX_ERROR; + if (in) { + ngx_http_postpone_filter_add(r, in); + return NGX_OK; } -#if 1 - { - ngx_chain_t *cl; - ngx_buf_t *b = NULL; - for (cl = pr->out; cl; cl = cl->next) { - if (cl->buf == b) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "the same buf was used in postponed %p %p", - b, b->pos); - ngx_debug_point(); - return NGX_ERROR; - } - b = cl->buf; - } - } +#if 0 + /* TODO: SSI may pass NULL */ + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "http postpone filter NULL inactive request", + &r->uri, &r->args); #endif - if (r != r->connection->data || r->postponed->request) { - return NGX_AGAIN; - } - } - - if (r->postponed) { - out = r->postponed->out; - if (out) { - r->postponed = r->postponed->next; - } - - } else { - out = in; - } - - rc = NGX_OK; - - if (out - || (r->connection->buffered - & (NGX_HTTP_LOWLEVEL_BUFFERED|NGX_LOWLEVEL_BUFFERED))) - { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http postpone filter out \"%V?%V\"", &r->uri, &r->args); - - if (!(out && out->next == NULL && ngx_buf_sync_only(out->buf))) { - - rc = ngx_http_next_filter(r->main, out); - - if (rc == NGX_ERROR) { - /* NGX_ERROR may be returned by any filter */ - r->connection->error = 1; - } - } + return NGX_OK; } if (r->postponed == NULL) { - return rc; + + if (in || c->buffered) { + return ngx_http_next_filter(r->main, in); + } + + return NGX_OK; + } + + if (in) { + ngx_http_postpone_filter_add(r, in); } - rc = ngx_http_postpone_filter_output_postponed_request(r); + do { + pr = r->postponed; + + if (pr->request) { + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http postpone filter wake \"%V?%V\"", + &pr->request->uri, &pr->request->args); + + r->postponed = pr->next; + + c->data = pr->request; + + return ngx_http_post_request(pr->request); + } - if (rc == NGX_ERROR) { - /* NGX_ERROR may be returned by any filter */ - r->connection->error = 1; - } + if (pr->out == NULL) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "http postpone filter NULL output", + &r->uri, &r->args); + + } else { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http postpone filter output \"%V?%V\"", + &r->uri, &r->args); - return rc; + if (ngx_http_next_filter(r->main, pr->out) == NGX_ERROR) { + return NGX_ERROR; + } + } + + r->postponed = pr->next; + + } while (r->postponed); + + return NGX_OK; } static ngx_int_t -ngx_http_postpone_filter_output_postponed_request(ngx_http_request_t *r) +ngx_http_postpone_filter_add(ngx_http_request_t *r, ngx_chain_t *in) { - ngx_int_t rc; - ngx_chain_t *out; - ngx_http_log_ctx_t *ctx; - ngx_http_postponed_request_t *pr; + ngx_http_postponed_request_t *pr, **ppr; - for ( ;; ) { - pr = r->postponed; + if (r->postponed) { + for (pr = r->postponed; pr->next; pr = pr->next) { /* void */ } - if (pr == NULL) { - break; + if (pr->request == NULL) { + goto found; } - if (pr->request) { - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http postpone filter handle \"%V?%V\"", - &pr->request->uri, &pr->request->args); - - ctx = r->connection->log->data; - ctx->current_request = pr->request; - - if (!pr->request->done) { - r->connection->data = pr->request; - return NGX_AGAIN; - } - - rc = ngx_http_postpone_filter_output_postponed_request(pr->request); + ppr = &pr->next; - if (rc == NGX_AGAIN || rc == NGX_ERROR) { - return rc; - } - } - - out = pr->out; + } else { + ppr = &r->postponed; + } - if (out) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http postpone filter out postponed \"%V?%V\"", - &r->uri, &r->args); - - if (!(out && out->next == NULL && ngx_buf_sync_only(out->buf))) { - if (ngx_http_next_filter(r->main, out) == NGX_ERROR) { - return NGX_ERROR; - } - } - } - - r->postponed = r->postponed->next; + pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t)); + if (pr == NULL) { + return NGX_ERROR; } - if (r->out) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http postpone filter out again \"%V?%V\"", - &r->uri, &r->args); + *ppr = pr; - r->connection->data = r; - return NGX_AGAIN; + pr->request = NULL; + pr->out = NULL; + pr->next = NULL; + +found: + + if (ngx_chain_add_copy(r->pool, &pr->out, in) == NGX_OK) { + return NGX_OK; } - return NGX_OK; + return NGX_ERROR; } diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -38,8 +38,8 @@ static ngx_int_t ngx_http_find_virtual_s static void ngx_http_request_handler(ngx_event_t *ev); static ngx_int_t ngx_http_set_write_handler(ngx_http_request_t *r); static void ngx_http_writer(ngx_http_request_t *r); - -static void ngx_http_test_reading(ngx_http_request_t *r); +static void ngx_http_request_finalizer(ngx_http_request_t *r); + static void ngx_http_set_keepalive(ngx_http_request_t *r); static void ngx_http_keepalive_handler(ngx_event_t *ev); static void ngx_http_set_lingering_close(ngx_http_request_t *r); @@ -198,7 +198,7 @@ ngx_http_init_connection(ngx_connection_ c->write->handler = ngx_http_empty_handler; #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_reading, 1); + (void) ngx_atomic_fetch_add(ngx_stat_reading, 1); #endif if (rev->ready) { @@ -215,9 +215,9 @@ ngx_http_init_connection(ngx_connection_ ngx_add_timer(rev, c->listening->post_accept_timeout); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_reading, -1); + (void) ngx_atomic_fetch_add(ngx_stat_reading, -1); #endif ngx_http_close_connection(c); return; @@ -232,16 +232,22 @@ ngx_http_init_request(ngx_event_t *rev) ngx_uint_t i; ngx_connection_t *c; ngx_http_request_t *r; - ngx_http_in_port_t *hip; - ngx_http_in_addr_t *hia; + struct sockaddr_in *sin; + ngx_http_port_t *port; + ngx_http_in_addr_t *addr; ngx_http_log_ctx_t *ctx; + ngx_http_addr_conf_t *addr_conf; ngx_http_connection_t *hc; ngx_http_core_srv_conf_t *cscf; ngx_http_core_loc_conf_t *clcf; ngx_http_core_main_conf_t *cmcf; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; + ngx_http_in6_addr_t *addr6; +#endif #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_reading, -1); + (void) ngx_atomic_fetch_add(ngx_stat_reading, -1); #endif c = rev->data; @@ -253,6 +259,8 @@ ngx_http_init_request(ngx_event_t *rev) return; } + c->requests++; + hc = c->data; if (hc == NULL) { @@ -292,58 +300,84 @@ ngx_http_init_request(ngx_event_t *rev) /* find the server configuration for the address:port */ - /* AF_INET only */ - - hip = c->listening->servers; - hia = hip->addrs; - - r->port = hip->port; - r->port_text = &hip->port_text; - - i = 0; + port = c->listening->servers; r->connection = c; - if (hip->naddrs > 1) { + 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 has given this address. + * there are several addresses on this port and one of them + * is an "*:port" wildcard so getsockname() in ngx_http_server_addr() + * is required to determine a server address */ -#if (NGX_WIN32) - if (c->local_sockaddr) { - r->in_addr = - ((struct sockaddr_in *) c->local_sockaddr)->sin_addr.s_addr; - - } else + if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { + ngx_http_close_connection(c); + return; + } + + switch (c->local_sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) c->local_sockaddr; + + 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 - { - if (ngx_http_server_addr(r, NULL) != NGX_OK) { - ngx_http_close_connection(c); - return; + + default: /* AF_INET */ + sin = (struct sockaddr_in *) c->local_sockaddr; + + 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; + } } - } - - /* the last address is "*" */ - - for ( /* void */ ; i < hip->naddrs - 1; i++) { - if (hia[i].addr == r->in_addr) { - break; - } + + addr_conf = &addr[i].conf; + + break; } } else { - r->in_addr = hia[0].addr; + + 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; + } } - r->virtual_names = hia[i].virtual_names; + r->virtual_names = addr_conf->virtual_names; /* the default server configuration for the address:port */ - cscf = hia[i].core_srv_conf; + cscf = addr_conf->core_srv_conf; r->main_conf = cscf->ctx->main_conf; r->srv_conf = cscf->ctx->srv_conf; @@ -357,13 +391,13 @@ ngx_http_init_request(ngx_event_t *rev) ngx_http_ssl_srv_conf_t *sscf; sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module); - if (sscf->enable || hia[i].ssl) { + if (sscf->enable || addr_conf->ssl) { if (c->ssl == NULL) { c->log->action = "SSL handshaking"; - if (hia[i].ssl && sscf->ssl.ctx == NULL) { + if (addr_conf->ssl && sscf->ssl.ctx == NULL) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "no \"ssl_certificate\" is defined " "in server listening on SSL port"); @@ -372,7 +406,7 @@ ngx_http_init_request(ngx_event_t *rev) } if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER) - == NGX_ERROR) + != NGX_OK) { ngx_http_close_connection(c); return; @@ -388,9 +422,9 @@ ngx_http_init_request(ngx_event_t *rev) #endif clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - c->log->file = clcf->err_log->file; + c->log->file = clcf->error_log->file; if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { - c->log->log_level = clcf->err_log->log_level; + c->log->log_level = clcf->error_log->log_level; } if (c->buffer == NULL) { @@ -415,15 +449,17 @@ ngx_http_init_request(ngx_event_t *rev) if (ngx_list_init(&r->headers_out.headers, r->pool, 20, sizeof(ngx_table_elt_t)) - == NGX_ERROR) + != NGX_OK) { - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + ngx_destroy_pool(r->pool); + ngx_http_close_connection(c); return; } r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module); if (r->ctx == NULL) { - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + ngx_destroy_pool(r->pool); + ngx_http_close_connection(c); return; } @@ -432,7 +468,8 @@ ngx_http_init_request(ngx_event_t *rev) r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts * sizeof(ngx_http_variable_value_t)); if (r->variables == NULL) { - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + ngx_destroy_pool(r->pool); + ngx_http_close_connection(c); return; } @@ -463,9 +500,9 @@ ngx_http_init_request(ngx_event_t *rev) r->log_handler = ngx_http_log_error_handler; #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_reading, 1); + (void) ngx_atomic_fetch_add(ngx_stat_reading, 1); r->stat_reading = 1; - ngx_atomic_fetch_add(ngx_stat_requests, 1); + (void) ngx_atomic_fetch_add(ngx_stat_requests, 1); #endif rev->handler(rev); @@ -504,7 +541,7 @@ ngx_http_ssl_handshake(ngx_event_t *rev) ngx_add_timer(rev, c->listening->post_accept_timeout); } - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); } @@ -667,7 +704,6 @@ ngx_http_process_request_line(ngx_event_ r->request_line.len = r->request_end - r->request_start; r->request_line.data = r->request_start; - *r->request_end = '\0'; if (r->args_start) { @@ -775,7 +811,7 @@ ngx_http_process_request_line(ngx_event_ if (ngx_list_init(&r->headers_in.headers, r->pool, 20, sizeof(ngx_table_elt_t)) - == NGX_ERROR) + != NGX_OK) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; @@ -784,7 +820,7 @@ ngx_http_process_request_line(ngx_event_ if (ngx_array_init(&r->headers_in.cookies, r->pool, 2, sizeof(ngx_table_elt_t *)) - == NGX_ERROR) + != NGX_OK) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; @@ -1038,7 +1074,7 @@ ngx_http_read_request_header(ngx_http_re ngx_add_timer(rev, cscf->client_header_timeout); } - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_ERROR; } @@ -1412,9 +1448,7 @@ ngx_http_process_request_header(ngx_http } } - if (r->method & (NGX_HTTP_POST|NGX_HTTP_PUT) - && r->headers_in.content_length_n == -1) - { + if (r->method & NGX_HTTP_PUT && r->headers_in.content_length_n == -1) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "client sent %V method without \"Content-Length\" header", &r->method_name); @@ -1513,9 +1547,9 @@ ngx_http_process_request(ngx_http_reques } #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_reading, -1); + (void) ngx_atomic_fetch_add(ngx_stat_reading, -1); r->stat_reading = 0; - ngx_atomic_fetch_add(ngx_stat_writing, 1); + (void) ngx_atomic_fetch_add(ngx_stat_writing, 1); r->stat_writing = 1; #endif @@ -1525,7 +1559,7 @@ ngx_http_process_request(ngx_http_reques ngx_http_handler(r); - return; + ngx_http_run_posted_requests(c); } @@ -1558,15 +1592,9 @@ ngx_http_validate_host(u_char *host, siz continue; } - if (ch == '/' || ch == '\0') { + if (ngx_path_separator(ch) || ch == '\0') { return -1; } - -#if (NGX_WIN32) - if (ch == '\\') { - return -1; - } -#endif } if (dot) { @@ -1611,6 +1639,7 @@ ngx_http_find_virtual_server(ngx_http_re #if (NGX_PCRE) if (r->virtual_names->nregex) { + size_t ncaptures; ngx_int_t n; ngx_uint_t i; ngx_str_t name; @@ -1619,11 +1648,33 @@ ngx_http_find_virtual_server(ngx_http_re name.len = len; name.data = server; + ncaptures = 0; + sn = r->virtual_names->regex; for (i = 0; i < r->virtual_names->nregex; i++) { - n = ngx_regex_exec(sn[i].regex, &name, NULL, 0); + if (sn[i].captures && r->captures == NULL) { + + ncaptures = (NGX_HTTP_MAX_CAPTURES + 1) * 3; + + r->captures = ngx_palloc(r->pool, ncaptures * sizeof(int)); + if (r->captures == NULL) { + return NGX_ERROR; + } + + if (server == buf) { + server = ngx_pnalloc(r->pool, len); + if (server == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(server, buf, len); + name.data = server; + } + } + + n = ngx_regex_exec(sn[i].regex, &name, r->captures, ncaptures); if (n == NGX_REGEX_NO_MATCHED) { continue; @@ -1641,6 +1692,9 @@ ngx_http_find_virtual_server(ngx_http_re cscf = sn[i].core_srv_conf; + r->ncaptures = ncaptures; + r->captures_data = server; + goto found; } } @@ -1655,10 +1709,10 @@ found: r->loc_conf = cscf->ctx->loc_conf; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - r->connection->log->file = clcf->err_log->file; + r->connection->log->file = clcf->error_log->file; if (!(r->connection->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { - r->connection->log->log_level = clcf->err_log->log_level; + r->connection->log->log_level = clcf->error_log->log_level; } return NGX_OK; @@ -1678,12 +1732,73 @@ ngx_http_request_handler(ngx_event_t *ev ctx = c->log->data; ctx->current_request = r; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http run request: \"%V?%V\"", &r->uri, &r->args); + if (ev->write) { r->write_event_handler(r); } else { r->read_event_handler(r); } + + ngx_http_run_posted_requests(c); +} + + +void +ngx_http_run_posted_requests(ngx_connection_t *c) +{ + ngx_http_request_t *r; + ngx_http_log_ctx_t *ctx; + ngx_http_posted_request_t *pr; + + for ( ;; ) { + + if (c->destroyed) { + return; + } + + r = c->data; + pr = r->main->posted_requests; + + if (pr == NULL) { + return; + } + + r->main->posted_requests = pr->next; + + r = pr->request; + + ctx = c->log->data; + ctx->current_request = r; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http posted request: \"%V?%V\"", &r->uri, &r->args); + + r->write_event_handler(r); + } +} + + +ngx_int_t +ngx_http_post_request(ngx_http_request_t *r) +{ + ngx_http_posted_request_t *pr, **p; + + pr = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t)); + if (pr == NULL) { + return NGX_ERROR; + } + + pr->request = r; + pr->next = NULL; + + for (p = &r->main->posted_requests; *p; p = &(*p)->next) { /* void */ } + + *p = pr; + + return NGX_OK; } @@ -1692,7 +1807,6 @@ ngx_http_finalize_request(ngx_http_reque { ngx_connection_t *c; ngx_http_request_t *pr; - ngx_http_log_ctx_t *ctx; ngx_http_core_loc_conf_t *clcf; if (rc == NGX_DONE) { @@ -1702,9 +1816,14 @@ ngx_http_finalize_request(ngx_http_reque c = r->connection; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http finalize request: %d, \"%V?%V\"", - rc, &r->uri, &r->args); + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http finalize request: %d, \"%V?%V\" %d", + rc, &r->uri, &r->args, r == c->data); + + if (rc == NGX_OK && r->filter_finalize) { + c->error = 1; + return; + } if (rc == NGX_DECLINED) { r->content_handler = NULL; @@ -1760,88 +1879,91 @@ ngx_http_finalize_request(ngx_http_reque return; } - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - - if (r != r->main && !r->logged) { - - if (clcf->log_subrequest) { - ngx_http_log_request(r); - } - - r->logged = 1; - } - - if (r != r->main || rc == NGX_AGAIN) { - if (ngx_http_set_write_handler(r) != NGX_OK) { + if (r != r->main) { + + if (r->buffered || r->postponed) { + + if (ngx_http_set_write_handler(r) != NGX_OK) { + ngx_http_close_request(r->main, 0); + } + return; } - } - - r->done = 1; - - if (r != c->data) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http finalize non-active request: \"%V?%V\"", - &r->uri, &r->args); - return; - } - - if (r != r->main) { + +#if (NGX_DEBUG) + if (r != c->data) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http finalize non-active request: \"%V?%V\"", + &r->uri, &r->args); + } +#endif pr = r->parent; + if (r == c->data) { + + if (!r->logged) { + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->log_subrequest) { + ngx_http_log_request(r); + } + + r->logged = 1; + + } else { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "subrequest: \"%V?%V\" logged again", + &r->uri, &r->args); + } + + r->done = 1; + + if (pr->postponed && pr->postponed->request == r) { + pr->postponed = pr->postponed->next; + } + + c->data = pr; + + } else { + + r->write_event_handler = ngx_http_request_finalizer; + + if (r->waited) { + r->done = 1; + } + } + + if (ngx_http_post_request(pr) != NGX_OK) { + ngx_http_close_request(r->main, 0); + return; + } + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http parent request: \"%V?%V\"", &pr->uri, &pr->args); - - if (rc != NGX_AGAIN) { - c->data = pr; - } - - ctx = c->log->data; - ctx->current_request = pr; - - if (pr->postponed) { - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http request: \"%V?%V\" has postponed", - &pr->uri, &pr->args); - - if (rc != NGX_AGAIN && pr->postponed->request == r) { - pr->postponed = pr->postponed->next; - } - - if (r->fast_subrequest) { - - if (rc == NGX_AGAIN) { - r->fast_subrequest = 0; - } - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http fast subrequest: \"%V?%V\" done", - &r->uri, &r->args); - return; - } - - if (rc != NGX_AGAIN) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http wake parent request: \"%V?%V\"", - &pr->uri, &pr->args); - - pr->write_event_handler(pr); - } + "http wake parent request: \"%V?%V\"", + &pr->uri, &pr->args); + + return; + } + + if (r->buffered || c->buffered || r->postponed) { + + if (ngx_http_set_write_handler(r) != NGX_OK) { + ngx_http_close_request(r, 0); } return; } - if (rc == NGX_AGAIN) { + if (r != c->data) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "http finalize non-active request: \"%V?%V\"", + &r->uri, &r->args); return; } - if (c->buffered) { - (void) ngx_http_set_write_handler(r); - return; - } + r->done = 1; if (!r->post_action) { r->request_complete = 1; @@ -1869,6 +1991,8 @@ ngx_http_finalize_request(ngx_http_reque return; } + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + if (!ngx_terminate && !ngx_exiting && r->keepalive @@ -1908,7 +2032,7 @@ ngx_http_set_write_handler(ngx_http_requ ngx_add_timer(wev, clcf->send_timeout); } - if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) { + if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { ngx_http_close_request(r, 0); return NGX_ERROR; } @@ -1931,6 +2055,8 @@ ngx_http_writer(ngx_http_request_t *r) ngx_log_debug2(NGX_LOG_DEBUG_HTTP, wev->log, 0, "http writer handler: \"%V?%V\"", &r->uri, &r->args); + clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module); + if (wev->timedout) { if (!wev->delayed) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, @@ -1945,10 +2071,9 @@ ngx_http_writer(ngx_http_request_t *r) wev->delayed = 0; if (!wev->ready) { - clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module); ngx_add_timer(wev, clcf->send_timeout); - if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) { + if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { ngx_http_close_request(r, 0); } @@ -1960,9 +2085,7 @@ ngx_http_writer(ngx_http_request_t *r) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, "http writer delayed"); - clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module); - - if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) { + if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { ngx_http_close_request(r, 0); } @@ -1980,21 +2103,22 @@ ngx_http_writer(ngx_http_request_t *r) "http writer output filter: %d, \"%V?%V\"", rc, &r->uri, &r->args); - if (rc == NGX_AGAIN) { - clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module); + if (rc == NGX_ERROR) { + ngx_http_finalize_request(r, rc); + return; + } + + if (r->buffered || r->postponed || (r == r->main && c->buffered)) { + if (!wev->ready && !wev->delayed) { ngx_add_timer(wev, clcf->send_timeout); } - if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) { + if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { ngx_http_close_request(r, 0); } - if (r == r->main || r->buffered) { - return; - } - - rc = NGX_OK; + return; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, wev->log, 0, @@ -2004,6 +2128,16 @@ ngx_http_writer(ngx_http_request_t *r) } +static void +ngx_http_request_finalizer(ngx_http_request_t *r) +{ + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http finalizer done: \"%V?%V\"", &r->uri, &r->args); + + ngx_http_finalize_request(r, 0); +} + + void ngx_http_block_reading(ngx_http_request_t *r) { @@ -2015,16 +2149,14 @@ ngx_http_block_reading(ngx_http_request_ if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && r->connection->read->active) { - if (ngx_del_event(r->connection->read, NGX_READ_EVENT, 0) - == NGX_ERROR) - { + if (ngx_del_event(r->connection->read, NGX_READ_EVENT, 0) != NGX_OK) { ngx_http_close_request(r, 0); } } } -static void +void ngx_http_test_reading(ngx_http_request_t *r) { int n; @@ -2079,7 +2211,7 @@ ngx_http_test_reading(ngx_http_request_t if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && rev->active) { - if (ngx_del_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { + if (ngx_del_event(rev, NGX_READ_EVENT, 0) != NGX_OK) { ngx_http_close_request(r, 0); } } @@ -2119,6 +2251,7 @@ ngx_http_set_keepalive(ngx_http_request_ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "set http keepalive handler"); if (r->discard_body) { + r->write_event_handler = ngx_http_request_empty_handler; r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000); ngx_add_timer(rev, clcf->lingering_timeout); return; @@ -2173,7 +2306,7 @@ ngx_http_set_keepalive(ngx_http_request_ ngx_add_timer(rev, clcf->keepalive_timeout); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_connection(c); return; } @@ -2186,7 +2319,7 @@ ngx_http_set_keepalive(ngx_http_request_ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "pipelined request"); #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_reading, 1); + (void) ngx_atomic_fetch_add(ngx_stat_reading, 1); #endif hc->pipeline = 1; @@ -2260,7 +2393,7 @@ ngx_http_set_keepalive(ngx_http_request_ rev->handler = ngx_http_keepalive_handler; if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) { - if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) { + if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) { ngx_http_close_connection(c); return; } @@ -2292,8 +2425,15 @@ ngx_http_set_keepalive(ngx_http_request_ (const void *) &tcp_nodelay, sizeof(int)) == -1) { +#if (NGX_SOLARIS) + /* Solaris returns EINVAL if a socket has been shut down */ + c->log_error = NGX_ERROR_IGNORE_EINVAL; +#endif + ngx_connection_error(c, ngx_socket_errno, "setsockopt(TCP_NODELAY) failed"); + + c->log_error = NGX_ERROR_INFO; ngx_http_close_connection(c); return; } @@ -2385,7 +2525,7 @@ ngx_http_keepalive_handler(ngx_event_t * c->log_error = NGX_ERROR_INFO; if (n == NGX_AGAIN) { - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_connection(c); } @@ -2409,7 +2549,7 @@ ngx_http_keepalive_handler(ngx_event_t * b->last += n; #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_reading, 1); + (void) ngx_atomic_fetch_add(ngx_stat_reading, 1); #endif c->log->handler = ngx_http_log_error; @@ -2438,7 +2578,7 @@ ngx_http_set_lingering_close(ngx_http_re r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000); ngx_add_timer(rev, clcf->lingering_timeout); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_request(r, 0); return; } @@ -2447,7 +2587,7 @@ ngx_http_set_lingering_close(ngx_http_re wev->handler = ngx_http_empty_handler; if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) { - if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) { + if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) { ngx_http_close_request(r, 0); return; } @@ -2506,7 +2646,7 @@ ngx_http_lingering_close_handler(ngx_eve } while (rev->ready); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_request(r, 0); return; } @@ -2554,7 +2694,13 @@ ngx_http_send_special(ngx_http_request_t } if (flags & NGX_HTTP_LAST) { - b->last_buf = 1; + + if (r == r->main && !r->post_action) { + b->last_buf = 1; + + } else { + b->last_in_chain = 1; + } } if (flags & NGX_HTTP_FLUSH) { @@ -2638,11 +2784,11 @@ ngx_http_request_done(ngx_http_request_t #if (NGX_STAT_STUB) if (r->stat_reading) { - ngx_atomic_fetch_add(ngx_stat_reading, -1); + (void) ngx_atomic_fetch_add(ngx_stat_reading, -1); } if (r->stat_writing) { - ngx_atomic_fetch_add(ngx_stat_writing, -1); + (void) ngx_atomic_fetch_add(ngx_stat_writing, -1); } #endif @@ -2723,7 +2869,7 @@ ngx_http_close_connection(ngx_connection #endif #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_active, -1); + (void) ngx_atomic_fetch_add(ngx_stat_active, -1); #endif c->destroyed = 1; diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -10,6 +10,7 @@ #define NGX_HTTP_MAX_URI_CHANGES 10 #define NGX_HTTP_MAX_SUBREQUESTS 50 +#define NGX_HTTP_MAX_CAPTURES 9 /* must be 2^n */ #define NGX_HTTP_LC_HEADER_LEN 32 @@ -58,6 +59,7 @@ #define NGX_HTTP_ZERO_IN_URI 1 #define NGX_HTTP_SUBREQUEST_IN_MEMORY 2 +#define NGX_HTTP_SUBREQUEST_WAITED 4 #define NGX_HTTP_OK 200 @@ -321,6 +323,14 @@ struct ngx_http_postponed_request_s { }; +typedef struct ngx_http_posted_request_s ngx_http_posted_request_t; + +struct ngx_http_posted_request_s { + ngx_http_request_t *request; + ngx_http_posted_request_t *next; +}; + + typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r); typedef void (*ngx_http_event_handler_pt)(ngx_http_request_t *r); @@ -338,7 +348,9 @@ struct ngx_http_request_s { ngx_http_event_handler_pt read_event_handler; ngx_http_event_handler_pt write_event_handler; +#if (NGX_HTTP_CACHE) ngx_http_cache_t *cache; +#endif ngx_http_upstream_t *upstream; ngx_array_t *upstream_states; @@ -373,10 +385,8 @@ struct ngx_http_request_s { ngx_http_request_t *parent; ngx_http_postponed_request_t *postponed; ngx_http_post_subrequest_t *post_subrequest; + ngx_http_posted_request_t *posted_requests; - uint32_t in_addr; - ngx_uint_t port; - ngx_str_t *port_text; /* ":80" */ ngx_http_virtual_names_t *virtual_names; ngx_int_t phase_handler; @@ -385,6 +395,12 @@ struct ngx_http_request_s { ngx_http_variable_value_t *variables; +#if (NGX_PCRE) + ngx_uint_t ncaptures; + int *captures; + u_char *captures_data; +#endif + size_t limit_rate; /* used to learn the Apache compatible response length without a header */ @@ -428,9 +444,12 @@ struct ngx_http_request_s { unsigned request_body_file_group_access:1; unsigned request_body_file_log_level:3; - unsigned fast_subrequest:1; unsigned subrequest_in_memory:1; + unsigned waited:1; +#if (NGX_HTTP_CACHE) + unsigned cached:1; +#endif unsigned gzip:2; unsigned proxy:1; @@ -438,10 +457,12 @@ struct ngx_http_request_s { unsigned no_cache:1; /* - * instead of using the request context data in ngx_http_limit_zone_module - * we use the single bit in the request structure + * instead of using the request context data in + * ngx_http_limit_zone_module and ngx_http_limit_req_module + * we use the single bits in the request structure */ unsigned limit_zone_set:1; + unsigned limit_req_set:1; #if 0 unsigned cacheable:1; @@ -457,6 +478,8 @@ struct ngx_http_request_s { unsigned discard_body:1; unsigned internal:1; unsigned error_page:1; + unsigned ignore_content_encoding:1; + unsigned filter_finalize:1; unsigned post_action:1; unsigned request_complete:1; unsigned request_output:1; @@ -465,7 +488,6 @@ struct ngx_http_request_s { unsigned root_tested:1; unsigned done:1; unsigned logged:1; - unsigned utf8:1; unsigned buffered:4; diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -15,6 +15,7 @@ static ngx_int_t ngx_http_write_request_ ngx_chain_t *body); static void ngx_http_read_discarded_request_body_handler(ngx_http_request_t *r); static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r); +static ngx_int_t ngx_http_test_expect(ngx_http_request_t *r); /* @@ -41,6 +42,10 @@ ngx_http_read_client_request_body(ngx_ht return NGX_OK; } + if (ngx_http_test_expect(r) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); if (rb == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; @@ -327,7 +332,7 @@ ngx_http_do_read_client_request_body(ngx clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ngx_add_timer(c->read, clcf->client_body_timeout); - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -433,6 +438,10 @@ ngx_http_discard_request_body(ngx_http_r return NGX_OK; } + if (ngx_http_test_expect(r) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + rev = r->connection->read; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body"); @@ -462,7 +471,7 @@ ngx_http_discard_request_body(ngx_http_r r->read_event_handler = ngx_http_read_discarded_request_body_handler; - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -519,7 +528,7 @@ ngx_http_read_discarded_request_body_han /* rc == NGX_AGAIN */ - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { c->error = 1; ngx_http_finalize_request(r, rc); return; @@ -581,3 +590,45 @@ ngx_http_read_discarded_request_body(ngx return NGX_AGAIN; } + + +static ngx_int_t +ngx_http_test_expect(ngx_http_request_t *r) +{ + ngx_int_t n; + ngx_str_t *expect; + + if (r->expect_tested + || r->headers_in.expect == NULL + || r->http_version < NGX_HTTP_VERSION_11) + { + return NGX_OK; + } + + r->expect_tested = 1; + + expect = &r->headers_in.expect->value; + + if (expect->len != sizeof("100-continue") - 1 + || ngx_strncasecmp(expect->data, (u_char *) "100-continue", + sizeof("100-continue") - 1) + != 0) + { + return NGX_OK; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "send 100 Continue"); + + n = r->connection->send(r->connection, + (u_char *) "HTTP/1.1 100 Continue" CRLF CRLF, + sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1); + + if (n == sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1) { + return NGX_OK; + } + + /* we assume that such small packet should be send successfully */ + + return NGX_ERROR; +} diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c --- a/src/http/ngx_http_script.c +++ b/src/http/ngx_http_script.c @@ -9,11 +9,208 @@ #include +static ngx_int_t ngx_http_script_init_arrays(ngx_http_script_compile_t *sc); +static ngx_int_t ngx_http_script_done(ngx_http_script_compile_t *sc); +static ngx_int_t ngx_http_script_add_copy_code(ngx_http_script_compile_t *sc, + ngx_str_t *value, ngx_uint_t last); +static ngx_int_t ngx_http_script_add_var_code(ngx_http_script_compile_t *sc, + ngx_str_t *name); +static ngx_int_t ngx_http_script_add_args_code(ngx_http_script_compile_t *sc); +#if (NGX_PCRE) +static ngx_int_t ngx_http_script_add_capture_code(ngx_http_script_compile_t *sc, + ngx_uint_t n); +#endif +static ngx_int_t + ngx_http_script_add_full_name_code(ngx_http_script_compile_t *sc); +static size_t ngx_http_script_full_name_len_code(ngx_http_script_engine_t *e); +static void ngx_http_script_full_name_code(ngx_http_script_engine_t *e); + + #define ngx_http_script_exit (u_char *) &ngx_http_script_exit_code static uintptr_t ngx_http_script_exit_code = (uintptr_t) NULL; +void +ngx_http_script_flush_complex_value(ngx_http_request_t *r, + ngx_http_complex_value_t *val) +{ + ngx_uint_t *index; + + index = val->flushes; + + if (index) { + while (*index != (ngx_uint_t) -1) { + + if (r->variables[*index].no_cacheable) { + r->variables[*index].valid = 0; + r->variables[*index].not_found = 0; + } + + index++; + } + } +} + + +ngx_int_t +ngx_http_complex_value(ngx_http_request_t *r, ngx_http_complex_value_t *val, + ngx_str_t *value) +{ + size_t len; + ngx_http_script_code_pt code; + ngx_http_script_len_code_pt lcode; + ngx_http_script_engine_t e; + + if (val->lengths == NULL) { + *value = val->value; + return NGX_OK; + } + + ngx_http_script_flush_complex_value(r, val); + + ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); + + e.ip = val->lengths; + e.request = r; + e.flushed = 1; + + len = 0; + + while (*(uintptr_t *) e.ip) { + lcode = *(ngx_http_script_len_code_pt *) e.ip; + len += lcode(&e); + } + + value->len = len; + value->data = ngx_pnalloc(r->pool, len); + if (value->data == NULL) { + return NGX_ERROR; + } + + e.ip = val->values; + e.pos = value->data; + e.buf = *value; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + } + + *value = e.buf; + + return NGX_OK; +} + + +ngx_int_t +ngx_http_compile_complex_value(ngx_http_compile_complex_value_t *ccv) +{ + ngx_str_t *v; + ngx_uint_t i, n, nv, nc; + ngx_array_t flushes, lengths, values, *pf, *pl, *pv; + ngx_http_script_compile_t sc; + + v = ccv->value; + + if (v->len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, ccv->cf, 0, "empty parameter"); + return NGX_ERROR; + } + + nv = 0; + nc = 0; + + for (i = 0; i < v->len; i++) { + if (v->data[i] == '$') { + if (v->data[i + 1] >= '1' && v->data[i + 1] <= '9') { + nc++; + + } else { + nv++; + } + } + } + + if (v->data[0] != '$' && (ccv->conf_prefix || ccv->root_prefix)) { + + if (ngx_conf_full_name(ccv->cf->cycle, v, ccv->conf_prefix) != NGX_OK) { + return NGX_ERROR; + } + + ccv->conf_prefix = 0; + ccv->root_prefix = 0; + } + + ccv->complex_value->value = *v; + ccv->complex_value->flushes = NULL; + ccv->complex_value->lengths = NULL; + ccv->complex_value->values = NULL; + + if (nv == 0 && nc == 0) { + return NGX_OK; + } + + n = nv + 1; + + if (ngx_array_init(&flushes, ccv->cf->pool, n, sizeof(ngx_uint_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + n = nv * (2 * sizeof(ngx_http_script_copy_code_t) + + sizeof(ngx_http_script_var_code_t)) + + sizeof(uintptr_t); + + if (ngx_array_init(&lengths, ccv->cf->pool, n, 1) != NGX_OK) { + return NGX_ERROR; + } + + n = (nv * (2 * sizeof(ngx_http_script_copy_code_t) + + sizeof(ngx_http_script_var_code_t)) + + sizeof(uintptr_t) + + v->len + + sizeof(uintptr_t) - 1) + & ~(sizeof(uintptr_t) - 1); + + if (ngx_array_init(&values, ccv->cf->pool, n, 1) != NGX_OK) { + return NGX_ERROR; + } + + pf = &flushes; + pl = &lengths; + pv = &values; + + ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + + sc.cf = ccv->cf; + sc.source = v; + sc.flushes = &pf; + sc.lengths = &pl; + sc.values = &pv; + sc.complete_lengths = 1; + sc.complete_values = 1; + sc.zero = ccv->zero; + sc.conf_prefix = ccv->conf_prefix; + sc.root_prefix = ccv->root_prefix; + + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_ERROR; + } + + if (flushes.nelts) { + ccv->complex_value->flushes = flushes.elts; + ccv->complex_value->flushes[flushes.nelts] = (ngx_uint_t) -1; + } + + ccv->complex_value->lengths = lengths.elts; + ccv->complex_value->values = values.elts; + + return NGX_OK; +} + + ngx_uint_t ngx_http_script_variables_count(ngx_str_t *value) { @@ -32,53 +229,14 @@ ngx_http_script_variables_count(ngx_str_ ngx_int_t ngx_http_script_compile(ngx_http_script_compile_t *sc) { - u_char ch; - size_t size; - ngx_int_t index, *p; - ngx_str_t name; - uintptr_t *code; - ngx_uint_t i, n, bracket; - ngx_http_script_var_code_t *var_code; - ngx_http_script_copy_code_t *copy; - ngx_http_script_copy_capture_code_t *copy_capture; - - if (sc->flushes && *sc->flushes == NULL) { - n = sc->variables ? sc->variables : 1; - *sc->flushes = ngx_array_create(sc->cf->pool, n, sizeof(ngx_uint_t)); - if (*sc->flushes == NULL) { - return NGX_ERROR; - } - } - + u_char ch; + ngx_str_t name; + ngx_uint_t i, bracket; - if (*sc->lengths == NULL) { - n = sc->variables * (2 * sizeof(ngx_http_script_copy_code_t) - + sizeof(ngx_http_script_var_code_t)) - + sizeof(uintptr_t); - - *sc->lengths = ngx_array_create(sc->cf->pool, n, 1); - if (*sc->lengths == NULL) { - return NGX_ERROR; - } + if (ngx_http_script_init_arrays(sc) != NGX_OK) { + return NGX_ERROR; } - - if (*sc->values == NULL) { - n = (sc->variables * (2 * sizeof(ngx_http_script_copy_code_t) - + sizeof(ngx_http_script_var_code_t)) - + sizeof(uintptr_t) - + sc->source->len - + sizeof(uintptr_t) - 1) - & ~(sizeof(uintptr_t) - 1); - - *sc->values = ngx_array_create(sc->cf->pool, n, 1); - if (*sc->values == NULL) { - return NGX_ERROR; - } - } - - sc->variables = 0; - for (i = 0; i < sc->source->len; /* void */ ) { name.len = 0; @@ -89,6 +247,12 @@ ngx_http_script_compile(ngx_http_script_ goto invalid_variable; } +#if (NGX_PCRE) + { + ngx_uint_t n; + + /* NGX_HTTP_MAX_CAPTURES is 9 */ + if (sc->source->data[i] >= '1' && sc->source->data[i] <= '9') { n = sc->source->data[i] - '0'; @@ -99,36 +263,16 @@ ngx_http_script_compile(ngx_http_script_ sc->captures_mask |= 1 << n; - copy_capture = ngx_http_script_add_code(*sc->lengths, - sizeof(ngx_http_script_copy_capture_code_t), - NULL); - if (copy_capture == NULL) { + if (ngx_http_script_add_capture_code(sc, n) != NGX_OK) { return NGX_ERROR; } - copy_capture->code = (ngx_http_script_code_pt) - ngx_http_script_copy_capture_len_code; - copy_capture->n = 2 * n; - - - copy_capture = ngx_http_script_add_code(*sc->values, - sizeof(ngx_http_script_copy_capture_code_t), - &sc->main); - if (copy_capture == NULL) { - return NGX_ERROR; - } - - copy_capture->code = ngx_http_script_copy_capture_code; - copy_capture->n = 2 * n; - - if (sc->ncaptures < n) { - sc->ncaptures = n; - } - i++; continue; } + } +#endif if (sc->source->data[i] == '{') { bracket = 1; @@ -177,43 +321,10 @@ ngx_http_script_compile(ngx_http_script_ sc->variables++; - index = ngx_http_get_variable_index(sc->cf, &name); - - if (index == NGX_ERROR) { + if (ngx_http_script_add_var_code(sc, &name) != NGX_OK) { return NGX_ERROR; } - if (sc->flushes) { - p = ngx_array_push(*sc->flushes); - if (p == NULL) { - return NGX_ERROR; - } - - *p = index; - } - - var_code = ngx_http_script_add_code(*sc->lengths, - sizeof(ngx_http_script_var_code_t), - NULL); - if (var_code == NULL) { - return NGX_ERROR; - } - - var_code->code = (ngx_http_script_code_pt) - ngx_http_script_copy_var_len_code; - var_code->index = (uintptr_t) index; - - - var_code = ngx_http_script_add_code(*sc->values, - sizeof(ngx_http_script_var_code_t), - &sc->main); - if (var_code == NULL) { - return NGX_ERROR; - } - - var_code->code = ngx_http_script_copy_var_code; - var_code->index = (uintptr_t) index; - continue; } @@ -221,22 +332,10 @@ ngx_http_script_compile(ngx_http_script_ sc->args = 1; sc->compile_args = 0; - code = ngx_http_script_add_code(*sc->lengths, sizeof(uintptr_t), - NULL); - if (code == NULL) { + if (ngx_http_script_add_args_code(sc) != NGX_OK) { return NGX_ERROR; } - *code = (uintptr_t) ngx_http_script_mark_args_code; - - code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t), - &sc->main); - if (code == NULL) { - return NGX_ERROR; - } - - *code = (uintptr_t) ngx_http_script_start_args_code; - i++; continue; @@ -244,62 +343,35 @@ ngx_http_script_compile(ngx_http_script_ name.data = &sc->source->data[i]; - while (i < sc->source->len - && sc->source->data[i] != '$' - && !(sc->source->data[i] == '?' && sc->compile_args)) - { + while (i < sc->source->len) { + + if (sc->source->data[i] == '$') { + break; + } + + if (sc->source->data[i] == '?') { + + sc->args = 1; + + if (sc->compile_args) { + break; + } + } + i++; name.len++; } sc->size += name.len; - copy = ngx_http_script_add_code(*sc->lengths, - sizeof(ngx_http_script_copy_code_t), - NULL); - if (copy == NULL) { - return NGX_ERROR; - } - - copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code; - copy->len = name.len; - - size = (sizeof(ngx_http_script_copy_code_t) + name.len - + sizeof(uintptr_t) - 1) - & ~(sizeof(uintptr_t) - 1); - - copy = ngx_http_script_add_code(*sc->values, size, &sc->main); - if (copy == NULL) { + if (ngx_http_script_add_copy_code(sc, &name, (i == sc->source->len)) + != NGX_OK) + { return NGX_ERROR; } - - copy->code = ngx_http_script_copy_code; - copy->len = name.len; - - ngx_memcpy((u_char *) copy + sizeof(ngx_http_script_copy_code_t), - name.data, name.len); } - if (sc->complete_lengths) { - code = ngx_http_script_add_code(*sc->lengths, sizeof(uintptr_t), NULL); - if (code == NULL) { - return NGX_ERROR; - } - - *code = (uintptr_t) NULL; - } - - if (sc->complete_values) { - code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t), - &sc->main); - if (code == NULL) { - return NGX_ERROR; - } - - *code = (uintptr_t) NULL; - } - - return NGX_OK; + return ngx_http_script_done(sc); invalid_variable: @@ -376,6 +448,95 @@ ngx_http_script_flush_no_cacheable_varia } +static ngx_int_t +ngx_http_script_init_arrays(ngx_http_script_compile_t *sc) +{ + ngx_uint_t n; + + if (sc->flushes && *sc->flushes == NULL) { + n = sc->variables ? sc->variables : 1; + *sc->flushes = ngx_array_create(sc->cf->pool, n, sizeof(ngx_uint_t)); + if (*sc->flushes == NULL) { + return NGX_ERROR; + } + } + + if (*sc->lengths == NULL) { + n = sc->variables * (2 * sizeof(ngx_http_script_copy_code_t) + + sizeof(ngx_http_script_var_code_t)) + + sizeof(uintptr_t); + + *sc->lengths = ngx_array_create(sc->cf->pool, n, 1); + if (*sc->lengths == NULL) { + return NGX_ERROR; + } + } + + if (*sc->values == NULL) { + n = (sc->variables * (2 * sizeof(ngx_http_script_copy_code_t) + + sizeof(ngx_http_script_var_code_t)) + + sizeof(uintptr_t) + + sc->source->len + + sizeof(uintptr_t) - 1) + & ~(sizeof(uintptr_t) - 1); + + *sc->values = ngx_array_create(sc->cf->pool, n, 1); + if (*sc->values == NULL) { + return NGX_ERROR; + } + } + + sc->variables = 0; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_script_done(ngx_http_script_compile_t *sc) +{ + ngx_str_t zero; + uintptr_t *code; + + if (sc->zero) { + + zero.len = 1; + zero.data = (u_char *) "\0"; + + if (ngx_http_script_add_copy_code(sc, &zero, 0) != NGX_OK) { + return NGX_ERROR; + } + } + + if (sc->conf_prefix || sc->root_prefix) { + if (ngx_http_script_add_full_name_code(sc) != NGX_OK) { + return NGX_ERROR; + } + } + + if (sc->complete_lengths) { + code = ngx_http_script_add_code(*sc->lengths, sizeof(uintptr_t), NULL); + if (code == NULL) { + return NGX_ERROR; + } + + *code = (uintptr_t) NULL; + } + + if (sc->complete_values) { + code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t), + &sc->main); + if (code == NULL) { + return NGX_ERROR; + } + + *code = (uintptr_t) NULL; + } + + return NGX_OK; +} + + void * ngx_http_script_start_code(ngx_pool_t *pool, ngx_array_t **codes, size_t size) { @@ -400,7 +561,7 @@ ngx_http_script_add_code(ngx_array_t *co new = ngx_array_push_n(codes, size); if (new == NULL) { - return NGX_CONF_ERROR; + return NULL; } if (code) { @@ -414,6 +575,49 @@ ngx_http_script_add_code(ngx_array_t *co } +static ngx_int_t +ngx_http_script_add_copy_code(ngx_http_script_compile_t *sc, ngx_str_t *value, + ngx_uint_t last) +{ + u_char *p; + size_t size, len, zero; + ngx_http_script_copy_code_t *code; + + zero = (sc->zero && last); + len = value->len + zero; + + code = ngx_http_script_add_code(*sc->lengths, + sizeof(ngx_http_script_copy_code_t), NULL); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code; + code->len = len; + + size = (sizeof(ngx_http_script_copy_code_t) + len + sizeof(uintptr_t) - 1) + & ~(sizeof(uintptr_t) - 1); + + code = ngx_http_script_add_code(*sc->values, size, &sc->main); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = ngx_http_script_copy_code; + code->len = len; + + p = ngx_cpymem((u_char *) code + sizeof(ngx_http_script_copy_code_t), + value->data, value->len); + + if (zero) { + *p = '\0'; + sc->zero = 0; + } + + return NGX_OK; +} + + size_t ngx_http_script_copy_len_code(ngx_http_script_engine_t *e) { @@ -430,20 +634,67 @@ ngx_http_script_copy_len_code(ngx_http_s void ngx_http_script_copy_code(ngx_http_script_engine_t *e) { + u_char *p; ngx_http_script_copy_code_t *code; code = (ngx_http_script_copy_code_t *) e->ip; + p = e->pos; + if (!e->skip) { - e->pos = ngx_copy(e->pos, e->ip + sizeof(ngx_http_script_copy_code_t), + e->pos = ngx_copy(p, e->ip + sizeof(ngx_http_script_copy_code_t), code->len); } e->ip += sizeof(ngx_http_script_copy_code_t) + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1)); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, - "http script copy: \"%V\"", &e->buf); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, + "http script copy: \"%*s\"", e->pos - p, p); +} + + +static ngx_int_t +ngx_http_script_add_var_code(ngx_http_script_compile_t *sc, ngx_str_t *name) +{ + ngx_int_t index, *p; + ngx_http_script_var_code_t *code; + + index = ngx_http_get_variable_index(sc->cf, name); + + if (index == NGX_ERROR) { + return NGX_ERROR; + } + + if (sc->flushes) { + p = ngx_array_push(*sc->flushes); + if (p == NULL) { + return NGX_ERROR; + } + + *p = index; + } + + code = ngx_http_script_add_code(*sc->lengths, + sizeof(ngx_http_script_var_code_t), NULL); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = (ngx_http_script_code_pt) ngx_http_script_copy_var_len_code; + code->index = (uintptr_t) index; + + code = ngx_http_script_add_code(*sc->values, + sizeof(ngx_http_script_var_code_t), + &sc->main); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = ngx_http_script_copy_var_code; + code->index = (uintptr_t) index; + + return NGX_OK; } @@ -475,6 +726,7 @@ ngx_http_script_copy_var_len_code(ngx_ht void ngx_http_script_copy_var_code(ngx_http_script_engine_t *e) { + u_char *p; ngx_http_variable_value_t *value; ngx_http_script_var_code_t *code; @@ -492,69 +744,37 @@ ngx_http_script_copy_var_code(ngx_http_s } if (value && !value->not_found) { - e->pos = ngx_copy(e->pos, value->data, value->len); + p = e->pos; + e->pos = ngx_copy(p, value->data, value->len); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, - "http script var: \"%V\"", &e->buf); + "http script var: \"%*s\"", e->pos - p, p); } } } -size_t -ngx_http_script_copy_capture_len_code(ngx_http_script_engine_t *e) +static ngx_int_t +ngx_http_script_add_args_code(ngx_http_script_compile_t *sc) { - ngx_http_script_copy_capture_code_t *code; - - code = (ngx_http_script_copy_capture_code_t *) e->ip; - - e->ip += sizeof(ngx_http_script_copy_capture_code_t); + uintptr_t *code; - if (code->n < e->ncaptures) { - if ((e->is_args || e->quote) - && (e->request->quoted_uri || e->request->plus_in_uri)) - { - return e->captures[code->n + 1] - e->captures[code->n] - + 2 * ngx_escape_uri(NULL, - &e->line.data[e->captures[code->n]], - e->captures[code->n + 1] - e->captures[code->n], - NGX_ESCAPE_ARGS); - } else { - return e->captures[code->n + 1] - e->captures[code->n]; - } + code = ngx_http_script_add_code(*sc->lengths, sizeof(uintptr_t), NULL); + if (code == NULL) { + return NGX_ERROR; } - return 0; -} - - -void -ngx_http_script_copy_capture_code(ngx_http_script_engine_t *e) -{ - ngx_http_script_copy_capture_code_t *code; - - code = (ngx_http_script_copy_capture_code_t *) e->ip; - - e->ip += sizeof(ngx_http_script_copy_capture_code_t); + *code = (uintptr_t) ngx_http_script_mark_args_code; - if (code->n < e->ncaptures) { - if ((e->is_args || e->quote) - && (e->request->quoted_uri || e->request->plus_in_uri)) - { - e->pos = (u_char *) ngx_escape_uri(e->pos, - &e->line.data[e->captures[code->n]], - e->captures[code->n + 1] - e->captures[code->n], - NGX_ESCAPE_ARGS); - } else { - e->pos = ngx_copy(e->pos, - &e->line.data[e->captures[code->n]], - e->captures[code->n + 1] - e->captures[code->n]); - } + code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t), &sc->main); + if (code == NULL) { + return NGX_ERROR; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, - "http script capture: \"%V\"", &e->buf); + *code = (uintptr_t) ngx_http_script_start_args_code; + + return NGX_OK; } @@ -580,7 +800,6 @@ ngx_http_script_start_args_code(ngx_http } - #if (NGX_PCRE) void @@ -609,16 +828,27 @@ ngx_http_script_regex_start_code(ngx_htt e->line.data = e->sp->data; } - rc = ngx_regex_exec(code->regex, &e->line, e->captures, code->ncaptures); + if (code->ncaptures && r->captures == NULL) { + + r->captures = ngx_palloc(r->pool, + (NGX_HTTP_MAX_CAPTURES + 1) * 3 * sizeof(int)); + if (r->captures == NULL) { + e->ip = ngx_http_script_exit; + e->status = NGX_HTTP_INTERNAL_SERVER_ERROR; + return; + } + } + + rc = ngx_regex_exec(code->regex, &e->line, r->captures, code->ncaptures); if (rc == NGX_REGEX_NO_MATCHED) { - if (e->log) { + if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) { ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, "\"%V\" does not match \"%V\"", &code->name, &e->line); } - e->ncaptures = 0; + r->ncaptures = 0; if (code->test) { if (code->negative_test) { @@ -650,12 +880,13 @@ ngx_http_script_regex_start_code(ngx_htt return; } - if (e->log) { + if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) { ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, "\"%V\" matches \"%V\"", &code->name, &e->line); } - e->ncaptures = code->ncaptures; + r->ncaptures = code->ncaptures; + r->captures_data = e->line.data; if (code->test) { if (code->negative_test) { @@ -706,7 +937,7 @@ ngx_http_script_regex_start_code(ngx_htt } for (n = 1; n < (ngx_uint_t) rc; n++) { - e->buf.len += e->captures[2 * n + 1] - e->captures[2 * n]; + e->buf.len += r->captures[2 * n + 1] - r->captures[2 * n]; } } else { @@ -715,8 +946,6 @@ ngx_http_script_regex_start_code(ngx_htt le.ip = code->lengths->elts; le.line = e->line; le.request = r; - le.captures = e->captures; - le.ncaptures = e->ncaptures; le.quote = code->redirect; len = 0; @@ -786,7 +1015,7 @@ ngx_http_script_regex_end_code(ngx_http_ e->buf.len = e->pos - e->buf.data; - if (e->log) { + if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) { ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, "rewritten redirect: \"%V\"", &e->buf); } @@ -828,7 +1057,7 @@ ngx_http_script_regex_end_code(ngx_http_ } } - if (e->log) { + if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) { ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, "rewritten data: \"%V\", args: \"%V\"", &e->buf, &r->args); @@ -855,9 +1084,194 @@ ngx_http_script_regex_end_code(ngx_http_ e->ip += sizeof(ngx_http_script_regex_end_code_t); } + +static ngx_int_t +ngx_http_script_add_capture_code(ngx_http_script_compile_t *sc, ngx_uint_t n) +{ + ngx_http_script_copy_capture_code_t *code; + + code = ngx_http_script_add_code(*sc->lengths, + sizeof(ngx_http_script_copy_capture_code_t), + NULL); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = (ngx_http_script_code_pt) + ngx_http_script_copy_capture_len_code; + code->n = 2 * n; + + + code = ngx_http_script_add_code(*sc->values, + sizeof(ngx_http_script_copy_capture_code_t), + &sc->main); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = ngx_http_script_copy_capture_code; + code->n = 2 * n; + + if (sc->ncaptures < n) { + sc->ncaptures = n; + } + + return NGX_OK; +} + + +size_t +ngx_http_script_copy_capture_len_code(ngx_http_script_engine_t *e) +{ + int *cap; + u_char *p; + ngx_uint_t n; + ngx_http_request_t *r; + ngx_http_script_copy_capture_code_t *code; + + r = e->request; + + code = (ngx_http_script_copy_capture_code_t *) e->ip; + + e->ip += sizeof(ngx_http_script_copy_capture_code_t); + + n = code->n; + + if (n < r->ncaptures) { + + cap = r->captures; + + if ((e->is_args || e->quote) + && (e->request->quoted_uri || e->request->plus_in_uri)) + { + p = r->captures_data; + + return cap[n + 1] - cap[n] + + 2 * ngx_escape_uri(NULL, &p[cap[n]], cap[n + 1] - cap[n], + NGX_ESCAPE_ARGS); + } else { + return cap[n + 1] - cap[n]; + } + } + + return 0; +} + + +void +ngx_http_script_copy_capture_code(ngx_http_script_engine_t *e) +{ + int *cap; + u_char *p, *pos; + ngx_uint_t n; + ngx_http_request_t *r; + ngx_http_script_copy_capture_code_t *code; + + r = e->request; + + code = (ngx_http_script_copy_capture_code_t *) e->ip; + + e->ip += sizeof(ngx_http_script_copy_capture_code_t); + + n = code->n; + + pos = e->pos; + + if (n < r->ncaptures) { + + cap = r->captures; + p = r->captures_data; + + if ((e->is_args || e->quote) + && (e->request->quoted_uri || e->request->plus_in_uri)) + { + e->pos = (u_char *) ngx_escape_uri(pos, &p[cap[n]], + cap[n + 1] - cap[n], + NGX_ESCAPE_ARGS); + } else { + e->pos = ngx_copy(pos, &p[cap[n]], cap[n + 1] - cap[n]); + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, + "http script capture: \"%*s\"", e->pos - pos, pos); +} + #endif +static ngx_int_t +ngx_http_script_add_full_name_code(ngx_http_script_compile_t *sc) +{ + ngx_http_script_full_name_code_t *code; + + code = ngx_http_script_add_code(*sc->lengths, + sizeof(ngx_http_script_full_name_code_t), + NULL); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = (ngx_http_script_code_pt) ngx_http_script_full_name_len_code; + code->conf_prefix = sc->conf_prefix; + + code = ngx_http_script_add_code(*sc->values, + sizeof(ngx_http_script_full_name_code_t), + &sc->main); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = ngx_http_script_full_name_code; + code->conf_prefix = sc->conf_prefix; + + return NGX_OK; +} + + +static size_t +ngx_http_script_full_name_len_code(ngx_http_script_engine_t *e) +{ + ngx_http_script_full_name_code_t *code; + + code = (ngx_http_script_full_name_code_t *) e->ip; + + e->ip += sizeof(ngx_http_script_full_name_code_t); + + return code->conf_prefix ? ngx_cycle->conf_prefix.len: + ngx_cycle->prefix.len; +} + + +static void +ngx_http_script_full_name_code(ngx_http_script_engine_t *e) +{ + ngx_http_script_full_name_code_t *code; + + ngx_str_t value; + + code = (ngx_http_script_full_name_code_t *) e->ip; + + value.data = e->buf.data; + value.len = e->pos - e->buf.data; + + if (ngx_conf_full_name((ngx_cycle_t *) ngx_cycle, &value, code->conf_prefix) + != NGX_OK) + { + e->ip = ngx_http_script_exit; + e->status = NGX_HTTP_INTERNAL_SERVER_ERROR; + return; + } + + e->buf = value; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, + "http script fullname: \"%V\"", &value); + + e->ip += sizeof(ngx_http_script_full_name_code_t); +} + + void ngx_http_script_return_code(ngx_http_script_engine_t *e) { @@ -928,8 +1342,8 @@ ngx_http_script_equal_code(ngx_http_scri e->ip += sizeof(uintptr_t); - if (val->len == res->len && ngx_strncmp(val->data, res->data, res->len) - == 0) + if (val->len == res->len + && ngx_strncmp(val->data, res->data, res->len) == 0) { *res = ngx_http_variable_true_value; return; @@ -956,8 +1370,8 @@ ngx_http_script_not_equal_code(ngx_http_ e->ip += sizeof(uintptr_t); - if (val->len == res->len && ngx_strncmp(val->data, res->data, res->len) - == 0) + if (val->len == res->len + && ngx_strncmp(val->data, res->data, res->len) == 0) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, "http script not equal: no"); @@ -1000,6 +1414,7 @@ ngx_http_script_file_code(ngx_http_scrip of.directio = clcf->directio; of.valid = clcf->open_file_cache_valid; of.min_uses = clcf->open_file_cache_min_uses; + of.test_only = 1; of.errors = clcf->open_file_cache_errors; of.events = clcf->open_file_cache_events; @@ -1008,7 +1423,7 @@ ngx_http_script_file_code(ngx_http_scrip { if (of.err != NGX_ENOENT && of.err != NGX_ENOTDIR) { ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err, - ngx_file_info_n " \"%s\" failed", value->data); + "%s \"%s\" failed", of.failed, value->data); } switch (code->op) { @@ -1114,8 +1529,6 @@ ngx_http_script_complex_value_code(ngx_h le.ip = code->lengths->elts; le.line = e->line; le.request = e->request; - le.captures = e->captures; - le.ncaptures = e->ncaptures; le.quote = e->quote; for (len = 0; *(uintptr_t *) le.ip; len += lcode(&le)) { @@ -1163,9 +1576,6 @@ ngx_http_script_set_var_code(ngx_http_sc ngx_http_request_t *r; ngx_http_script_var_code_t *code; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, - "http script set var"); - code = (ngx_http_script_var_code_t *) e->ip; e->ip += sizeof(ngx_http_script_var_code_t); @@ -1179,6 +1589,20 @@ ngx_http_script_set_var_code(ngx_http_sc r->variables[code->index].no_cacheable = 0; r->variables[code->index].not_found = 0; r->variables[code->index].data = e->sp->data; + +#if (NGX_DEBUG) + { + ngx_http_variable_t *v; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + v = cmcf->variables.elts; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, + "http script set $%V", &v[code->index].name); + } +#endif } diff --git a/src/http/ngx_http_script.h b/src/http/ngx_http_script.h --- a/src/http/ngx_http_script.h +++ b/src/http/ngx_http_script.h @@ -30,9 +30,6 @@ typedef struct { unsigned is_args:1; unsigned log:1; - int *captures; - ngx_uint_t ncaptures; - ngx_int_t status; ngx_http_request_t *request; } ngx_http_script_engine_t; @@ -56,86 +53,114 @@ typedef struct { unsigned compile_args:1; unsigned complete_lengths:1; unsigned complete_values:1; + unsigned zero:1; + unsigned conf_prefix:1; + unsigned root_prefix:1; + unsigned dup_capture:1; - unsigned args:1; } ngx_http_script_compile_t; +typedef struct { + ngx_str_t value; + ngx_uint_t *flushes; + void *lengths; + void *values; +} ngx_http_complex_value_t; + + +typedef struct { + ngx_conf_t *cf; + ngx_str_t *value; + ngx_http_complex_value_t *complex_value; + + unsigned zero:1; + unsigned conf_prefix:1; + unsigned root_prefix:1; +} ngx_http_compile_complex_value_t; + + typedef void (*ngx_http_script_code_pt) (ngx_http_script_engine_t *e); typedef size_t (*ngx_http_script_len_code_pt) (ngx_http_script_engine_t *e); typedef struct { - ngx_http_script_code_pt code; - uintptr_t len; + ngx_http_script_code_pt code; + uintptr_t len; } ngx_http_script_copy_code_t; typedef struct { - ngx_http_script_code_pt code; - uintptr_t index; + ngx_http_script_code_pt code; + uintptr_t index; } ngx_http_script_var_code_t; typedef struct { - ngx_http_script_code_pt code; - ngx_http_set_variable_pt handler; - uintptr_t data; + ngx_http_script_code_pt code; + ngx_http_set_variable_pt handler; + uintptr_t data; } ngx_http_script_var_handler_code_t; typedef struct { - ngx_http_script_code_pt code; - uintptr_t n; + ngx_http_script_code_pt code; + uintptr_t n; } ngx_http_script_copy_capture_code_t; #if (NGX_PCRE) typedef struct { - ngx_http_script_code_pt code; - ngx_regex_t *regex; - ngx_array_t *lengths; - uintptr_t size; - uintptr_t ncaptures; - uintptr_t status; - uintptr_t next; + ngx_http_script_code_pt code; + ngx_regex_t *regex; + ngx_array_t *lengths; + uintptr_t size; + uintptr_t ncaptures; + uintptr_t status; + uintptr_t next; - uintptr_t test:1; - uintptr_t negative_test:1; - uintptr_t uri:1; - uintptr_t args:1; + uintptr_t test:1; + uintptr_t negative_test:1; + uintptr_t uri:1; + uintptr_t args:1; /* add the r->args to the new arguments */ - uintptr_t add_args:1; + uintptr_t add_args:1; - uintptr_t redirect:1; - uintptr_t break_cycle:1; + uintptr_t redirect:1; + uintptr_t break_cycle:1; - ngx_str_t name; + ngx_str_t name; } ngx_http_script_regex_code_t; typedef struct { - ngx_http_script_code_pt code; + ngx_http_script_code_pt code; - uintptr_t uri:1; - uintptr_t args:1; + uintptr_t uri:1; + uintptr_t args:1; /* add the r->args to the new arguments */ - uintptr_t add_args:1; + uintptr_t add_args:1; - uintptr_t redirect:1; + uintptr_t redirect:1; } ngx_http_script_regex_end_code_t; #endif typedef struct { - ngx_http_script_code_pt code; - uintptr_t status; - uintptr_t null; + ngx_http_script_code_pt code; + uintptr_t conf_prefix; +} ngx_http_script_full_name_code_t; + + +typedef struct { + ngx_http_script_code_pt code; + uintptr_t status; + uintptr_t null; } ngx_http_script_return_code_t; @@ -152,32 +177,38 @@ typedef enum { typedef struct { - ngx_http_script_code_pt code; - uintptr_t op; + ngx_http_script_code_pt code; + uintptr_t op; } ngx_http_script_file_code_t; typedef struct { - ngx_http_script_code_pt code; - uintptr_t next; - void **loc_conf; + ngx_http_script_code_pt code; + uintptr_t next; + void **loc_conf; } ngx_http_script_if_code_t; typedef struct { - ngx_http_script_code_pt code; - ngx_array_t *lengths; + ngx_http_script_code_pt code; + ngx_array_t *lengths; } ngx_http_script_complex_value_code_t; typedef struct { - ngx_http_script_code_pt code; - uintptr_t value; - uintptr_t text_len; - uintptr_t text_data; + ngx_http_script_code_pt code; + uintptr_t value; + uintptr_t text_len; + uintptr_t text_data; } ngx_http_script_value_code_t; +void ngx_http_script_flush_complex_value(ngx_http_request_t *r, + ngx_http_complex_value_t *val); +ngx_int_t ngx_http_complex_value(ngx_http_request_t *r, + ngx_http_complex_value_t *val, ngx_str_t *value); +ngx_int_t ngx_http_compile_complex_value(ngx_http_compile_complex_value_t *ccv); + ngx_uint_t ngx_http_script_variables_count(ngx_str_t *value); ngx_int_t ngx_http_script_compile(ngx_http_script_compile_t *sc); u_char *ngx_http_script_run(ngx_http_request_t *r, ngx_str_t *value, diff --git a/src/http/ngx_http_special_response.c b/src/http/ngx_http_special_response.c --- a/src/http/ngx_http_special_response.c +++ b/src/http/ngx_http_special_response.c @@ -275,14 +275,16 @@ static ngx_str_t ngx_http_error_pages[] ngx_null_string, /* 201, 204 */ -#define NGX_HTTP_LEVEL_200 1 +#define NGX_HTTP_LAST_LEVEL_200 202 +#define NGX_HTTP_LEVEL_200 (NGX_HTTP_LAST_LEVEL_200 - 201) /* ngx_null_string, */ /* 300 */ ngx_string(ngx_http_error_301_page), ngx_string(ngx_http_error_302_page), ngx_null_string, /* 303 */ -#define NGX_HTTP_LEVEL_300 3 +#define NGX_HTTP_LAST_LEVEL_300 304 +#define NGX_HTTP_LEVEL_300 (NGX_HTTP_LAST_LEVEL_300 - 301) ngx_string(ngx_http_error_400_page), ngx_string(ngx_http_error_401_page), @@ -302,7 +304,8 @@ static ngx_str_t ngx_http_error_pages[] ngx_string(ngx_http_error_415_page), ngx_string(ngx_http_error_416_page), -#define NGX_HTTP_LEVEL_400 17 +#define NGX_HTTP_LAST_LEVEL_400 417 +#define NGX_HTTP_LEVEL_400 (NGX_HTTP_LAST_LEVEL_400 - 400) ngx_string(ngx_http_error_495_page), /* 495, https certificate error */ ngx_string(ngx_http_error_496_page), /* 496, https no certificate */ @@ -318,6 +321,9 @@ static ngx_str_t ngx_http_error_pages[] ngx_null_string, /* 505 */ ngx_null_string, /* 506 */ ngx_string(ngx_http_error_507_page) + +#define NGX_HTTP_LAST_LEVEL_500 508 + }; @@ -379,6 +385,8 @@ ngx_http_special_response_handler(ngx_ht } } + r->expect_tested = 1; + if (ngx_http_discard_request_body(r) != NGX_OK) { error = NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -400,16 +408,22 @@ ngx_http_special_response_handler(ngx_ht /* 204 */ err = 0; - } else if (error < NGX_HTTP_BAD_REQUEST) { + } else if (error >= NGX_HTTP_MOVED_PERMANENTLY + && error < NGX_HTTP_LAST_LEVEL_300) + { /* 3XX */ err = error - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_LEVEL_200; - } else if (error < NGX_HTTP_OWN_CODES) { + } else if (error >= NGX_HTTP_BAD_REQUEST + && error < NGX_HTTP_LAST_LEVEL_400) + { /* 4XX */ err = error - NGX_HTTP_BAD_REQUEST + NGX_HTTP_LEVEL_200 + NGX_HTTP_LEVEL_300; - } else { + } else if (error >= NGX_HTTP_OWN_CODES + && error < NGX_HTTP_LAST_LEVEL_500) + { /* 49X, 5XX */ err = error - NGX_HTTP_OWN_CODES + NGX_HTTP_LEVEL_200 + NGX_HTTP_LEVEL_300 @@ -421,85 +435,113 @@ ngx_http_special_response_handler(ngx_ht r->err_status = NGX_HTTP_BAD_REQUEST; break; } + + } 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) { - u_char ch, *p, *last; - ngx_str_t *uri, *args, u, a; + ngx_int_t overwrite; + ngx_str_t uri, args; ngx_table_elt_t *location; ngx_http_core_loc_conf_t *clcf; - r->err_status = err_page->overwrite; + overwrite = err_page->overwrite; + + if (overwrite && overwrite != NGX_HTTP_OK) { + r->expect_tested = 1; + } + + r->err_status = overwrite; r->zero_in_uri = 0; - if (err_page->uri_lengths) { - if (ngx_http_script_run(r, &u, err_page->uri_lengths->elts, 0, - err_page->uri_values->elts) - == NULL) - { - return NGX_ERROR; - } - - p = u.data; - uri = &u; - args = NULL; - - if (*p == '/') { - - last = p + uri->len; - - while (p < last) { - - ch = *p++; - - if (ch == '?') { - a.len = last - p; - a.data = p; - args = &a; - - u.len = p - 1 - u.data; - - while (p < last) { - if (*p++ == '\0') { - r->zero_in_uri = 1; - break; - } - } - - break; - } - - if (ch == '\0') { - r->zero_in_uri = 1; - continue; - } - } - } - - } else { - uri = &err_page->uri; - args = &err_page->args; + if (ngx_http_complex_value(r, &err_page->value, &uri) != NGX_OK) { + return NGX_ERROR; } - if (uri->data[0] == '/') { + if (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_get_name; } - return ngx_http_internal_redirect(r, uri, args); + return ngx_http_internal_redirect(r, &uri, &args); } - if (uri->data[0] == '@') { - return ngx_http_named_location(r, uri); + if (uri.data[0] == '@') { + return ngx_http_named_location(r, &uri); } location = ngx_list_push(&r->headers_out.headers); @@ -513,7 +555,7 @@ ngx_http_send_error_page(ngx_http_reques location->hash = 1; location->key.len = sizeof("Location") - 1; location->key.data = (u_char *) "Location"; - location->value = *uri; + location->value = uri; r->headers_out.location = location; @@ -568,6 +610,7 @@ ngx_http_send_special_response(ngx_http_ r->headers_out.content_type_len = sizeof("text/html") - 1; r->headers_out.content_type.len = sizeof("text/html") - 1; r->headers_out.content_type.data = (u_char *) "text/html"; + r->headers_out.content_type_lowcase = NULL; } else { r->headers_out.content_length_n = -1; @@ -670,6 +713,7 @@ ngx_http_send_refresh(ngx_http_request_t r->headers_out.content_type_len = sizeof("text/html") - 1; r->headers_out.content_type.len = sizeof("text/html") - 1; r->headers_out.content_type.data = (u_char *) "text/html"; + r->headers_out.content_type_lowcase = NULL; r->headers_out.location->hash = 0; r->headers_out.location = NULL; diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -9,6 +9,15 @@ #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_send(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); +#endif + 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); @@ -20,27 +29,40 @@ static ngx_int_t ngx_http_upstream_reini ngx_http_upstream_t *u); static void ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t *u); -static void ngx_http_upstream_send_request_handler(ngx_event_t *wev); -static void ngx_http_upstream_process_header(ngx_event_t *rev); +static void ngx_http_upstream_send_request_handler(ngx_http_request_t *r, + ngx_http_upstream_t *u); +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 void ngx_http_upstream_process_body_in_memory(ngx_event_t *rev); +static ngx_int_t ngx_http_upstream_process_headers(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static void ngx_http_upstream_process_body_in_memory(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_process_non_buffered_downstream(ngx_http_request_t *r); -static void ngx_http_upstream_process_non_buffered_body(ngx_event_t *ev); +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); static void ngx_http_upstream_process_downstream(ngx_http_request_t *r); -static void ngx_http_upstream_process_body(ngx_event_t *ev); +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); static void ngx_http_upstream_store(ngx_http_request_t *r, ngx_http_upstream_t *u); -static void ngx_http_upstream_dummy_handler(ngx_event_t *wev); +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); @@ -50,10 +72,14 @@ static void ngx_http_upstream_finalize_r 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_multi_header_lines(ngx_http_request_t *r, + ngx_http_upstream_process_cache_control(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); 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, @@ -69,10 +95,15 @@ static ngx_int_t ngx_http_upstream_copy_ ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_copy_content_length(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_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); @@ -85,6 +116,8 @@ static ngx_int_t ngx_http_upstream_statu 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 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, @@ -126,8 +159,7 @@ ngx_http_upstream_header_t ngx_http_ups { ngx_string("Last-Modified"), ngx_http_upstream_process_header_line, offsetof(ngx_http_upstream_headers_in_t, last_modified), - ngx_http_upstream_copy_header_line, - offsetof(ngx_http_headers_out_t, last_modified), 0 }, + ngx_http_upstream_copy_last_modified, 0, 0 }, { ngx_string("Server"), ngx_http_upstream_process_header_line, @@ -158,21 +190,19 @@ ngx_http_upstream_header_t ngx_http_ups ngx_http_upstream_copy_header_line, 0, 1 }, { ngx_string("Cache-Control"), - ngx_http_upstream_process_multi_header_lines, - offsetof(ngx_http_upstream_headers_in_t, 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_header_line, - offsetof(ngx_http_upstream_headers_in_t, 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_header_line, + ngx_http_upstream_copy_allow_ranges, offsetof(ngx_http_headers_out_t, accept_ranges), 1 }, { ngx_string("Connection"), @@ -188,8 +218,7 @@ ngx_http_upstream_header_t ngx_http_ups ngx_http_upstream_copy_header_line, 0, 0 }, { ngx_string("X-Accel-Expires"), - ngx_http_upstream_process_header_line, - offsetof(ngx_http_upstream_headers_in_t, x_accel_expires), + ngx_http_upstream_process_accel_expires, 0, ngx_http_upstream_copy_header_line, 0, 0 }, { ngx_string("X-Accel-Redirect"), @@ -274,13 +303,28 @@ ngx_module_t ngx_http_upstream_module = static ngx_http_variable_t ngx_http_upstream_vars[] = { { ngx_string("upstream_addr"), NULL, - ngx_http_upstream_addr_variable, 0, NGX_HTTP_VAR_NOHASH, 0 }, + ngx_http_upstream_addr_variable, 0, + NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 }, { ngx_string("upstream_status"), NULL, - ngx_http_upstream_status_variable, 0, NGX_HTTP_VAR_NOHASH, 0 }, + ngx_http_upstream_status_variable, 0, + NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 }, { ngx_string("upstream_response_time"), NULL, - ngx_http_upstream_response_time_variable, 0, NGX_HTTP_VAR_NOHASH, 0 }, + ngx_http_upstream_response_time_variable, 0, + NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("upstream_response_length"), NULL, + ngx_http_upstream_response_length_variable, 0, + NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 }, + +#if (NGX_HTTP_CACHE) + + { ngx_string("upstream_cache_status"), NULL, + ngx_http_upstream_cache_status, 0, + NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 }, + +#endif { ngx_null_string, NULL, NULL, 0, 0, 0 } }; @@ -295,6 +339,15 @@ static ngx_http_upstream_next_t ngx_htt { 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 } +}; + + void ngx_http_upstream_init(ngx_http_request_t *r) { @@ -319,7 +372,9 @@ ngx_http_upstream_init(ngx_http_request_ u = r->upstream; - if (!r->post_action && !u->conf->ignore_client_abort) { + u->store = (u->conf->store || u->conf->store_lengths); + + 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; } @@ -340,6 +395,25 @@ ngx_http_upstream_init(ngx_http_request_ u->request_bufs = r->request_body->bufs; } +#if (NGX_HTTP_CACHE) + + if (u->conf->cache) { + ngx_int_t rc; + + rc = ngx_http_upstream_cache(r, u); + + if (rc == NGX_DONE) { + return; + } + + if (rc != NGX_DECLINED) { + ngx_http_finalize_request(r, rc); + return; + } + } + +#endif + if (u->create_request(r) != NGX_OK) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; @@ -386,14 +460,26 @@ ngx_http_upstream_init(ngx_http_request_ cln->data = r; u->cleanup = &cln->handler; - u->store = (u->conf->store || u->conf->store_lengths); - if (u->resolved == NULL) { uscf = u->conf->upstream; } else { + if (u->resolved->sockaddr) { + + if (ngx_http_upstream_create_round_robin_peer(r, u->resolved) + != NGX_OK) + { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + ngx_http_upstream_connect(r, u); + + return; + } + host = &u->resolved->host; umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); @@ -405,7 +491,7 @@ ngx_http_upstream_init(ngx_http_request_ uscf = uscfp[i]; if (uscf->host.len == host->len - && ((uscf->port == 0 && u->resolved->default_port) + && ((uscf->port == 0 && u->resolved->no_port) || uscf->port == u->resolved->port) && ngx_memcmp(uscf->host.data, host->data, host->len) == 0) { @@ -457,6 +543,178 @@ found: } +#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; + + if (!(r->method & u->conf->cache_methods)) { + return NGX_DECLINED; + } + + if (r->method & NGX_HTTP_HEAD) { + u->method = ngx_http_core_get_method; + } + + c = ngx_pcalloc(r->pool, sizeof(ngx_http_cache_t)); + if (c == NULL) { + return NGX_ERROR; + } + + if (ngx_array_init(&c->keys, r->pool, 4, sizeof(ngx_str_t)) != NGX_OK) { + return NGX_ERROR; + } + + r->cache = c; + c->file.log = r->connection->log; + + if (u->create_key(r) != NGX_OK) { + return NGX_ERROR; + } + + /* TODO: add keys */ + + ngx_http_file_cache_create_key(r); + + u->cacheable = 1; + + c->min_uses = u->conf->cache_min_uses; + c->body_start = u->conf->buffer_size; + c->file_cache = u->conf->cache->data; + + 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_UPDATING: + + if (u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING) { + 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: + + rc = ngx_http_upstream_cache_send(r, u); + + if (rc != NGX_HTTP_UPSTREAM_INVALID_HEADER) { + return rc; + } + + break; + + case NGX_ERROR: + + return NGX_ERROR; + + case NGX_HTTP_CACHE_STALE: + + c->valid_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_AGAIN: + + u->cacheable = 0; + + break; + + default: + + /* cached NGX_HTTP_BAD_GATEWAY, NGX_HTTP_GATEWAY_TIME_OUT, etc. */ + + u->cache_status = NGX_HTTP_CACHE_HIT; + + return rc; + } + + r->cached = 0; + + return NGX_DECLINED; +} + + +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; + + /* 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)); + + if (ngx_list_init(&u->headers_in.headers, r->pool, 8, + 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; + } + + /* rc == NGX_HTTP_UPSTREAM_INVALID_HEADER */ + + /* TODO: delete file */ + + return rc; +} + +#endif + + static void ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx) { @@ -511,6 +769,37 @@ ngx_http_upstream_resolve_handler(ngx_re static void +ngx_http_upstream_handler(ngx_event_t *ev) +{ + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_log_ctx_t *ctx; + ngx_http_upstream_t *u; + + c = ev->data; + r = c->data; + + u = r->upstream; + c = r->connection; + + ctx = c->log->data; + ctx->current_request = r; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream request: \"%V?%V\"", &r->uri, &r->args); + + 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); @@ -531,6 +820,7 @@ ngx_http_upstream_check_broken_connectio int n; char buf[1]; ngx_err_t err; + ngx_int_t event; ngx_connection_t *c; ngx_http_upstream_t *u; @@ -542,8 +832,22 @@ ngx_http_upstream_check_broken_connectio u = r->upstream; if (c->error) { - ngx_http_upstream_finalize_request(r, u, - NGX_HTTP_CLIENT_CLOSED_REQUEST); + 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; } @@ -566,7 +870,7 @@ ngx_http_upstream_check_broken_connectio ev->error = 1; } - if (!u->cacheable && !u->store && u->peer.connection) { + if (!u->cacheable && u->peer.connection) { ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno, "kevent() reported that client closed prematurely " "connection, so upstream connection is closed too"); @@ -597,17 +901,15 @@ ngx_http_upstream_check_broken_connectio ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, err, "http upstream recv(): %d", n); - /* - * we do not need to disable the write event because - * that event has NGX_USE_CLEAR_EVENT type - */ - if (ev->write && (n >= 0 || err == NGX_EAGAIN)) { return; } if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) { - if (ngx_del_event(ev, NGX_READ_EVENT, 0) == NGX_ERROR) { + + 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; @@ -632,7 +934,7 @@ ngx_http_upstream_check_broken_connectio ev->eof = 1; c->error = 1; - if (!u->cacheable && !u->store && u->peer.connection) { + if (!u->cacheable && u->peer.connection) { ngx_log_error(NGX_LOG_INFO, ev->log, err, "client closed prematurely connection, " "so upstream connection is closed too"); @@ -712,14 +1014,19 @@ ngx_http_upstream_connect(ngx_http_reque c->data = r; - c->write->handler = ngx_http_upstream_send_request_handler; - c->read->handler = ngx_http_upstream_process_header; + 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; c->pool = r->pool; - c->read->log = c->write->log = c->log = r->connection->log; + c->log = r->connection->log; + c->read->log = c->log; + c->write->log = c->log; /* init or reinit the ngx_output_chain() and ngx_chain_writer() contexts */ @@ -792,7 +1099,7 @@ ngx_http_upstream_ssl_init_connection(ng if (ngx_ssl_create_connection(u->conf->ssl, c, NGX_SSL_BUFFER|NGX_SSL_CLIENT) - == NGX_ERROR) + != NGX_OK) { ngx_http_upstream_finalize_request(r, u, NGX_HTTP_INTERNAL_SERVER_ERROR); @@ -838,8 +1145,8 @@ ngx_http_upstream_ssl_handshake(ngx_conn u->peer.save_session(&u->peer, u->peer.data); } - c->write->handler = ngx_http_upstream_send_request_handler; - c->read->handler = ngx_http_upstream_process_header; + c->write->handler = ngx_http_upstream_handler; + c->read->handler = ngx_http_upstream_handler; ngx_http_upstream_send_request(r, u); @@ -901,22 +1208,18 @@ ngx_http_upstream_reinit(ngx_http_reques /* reinit u->buffer */ -#if 0 - if (u->cache) { - u->buffer.pos = u->buffer.start + u->cache->ctx.header_size; - u->buffer.last = u->buffer.pos; - - } else { - u->buffer.pos = u->buffer.start; - u->buffer.last = u->buffer.start; + u->buffer.pos = u->buffer.start; + +#if (NGX_HTTP_CACHE) + + if (r->cache) { + u->buffer.pos += r->cache->header_start; } -#else - - u->buffer.pos = u->buffer.start; - u->buffer.last = u->buffer.start; #endif + u->buffer.last = u->buffer.pos; + return NGX_OK; } @@ -955,8 +1258,7 @@ ngx_http_upstream_send_request(ngx_http_ if (rc == NGX_AGAIN) { ngx_add_timer(c->write, u->conf->send_timeout); - if (ngx_handle_write_event(c->write, u->conf->send_lowat) == NGX_ERROR) - { + 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; @@ -993,14 +1295,14 @@ ngx_http_upstream_send_request(ngx_http_ * it's better to do here because we postpone header buffer allocation */ - ngx_http_upstream_process_header(c->read); + ngx_http_upstream_process_header(r, u); return; } #endif - c->write->handler = ngx_http_upstream_dummy_handler; - - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + 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; @@ -1009,20 +1311,17 @@ ngx_http_upstream_send_request(ngx_http_ static void -ngx_http_upstream_send_request_handler(ngx_event_t *wev) +ngx_http_upstream_send_request_handler(ngx_http_request_t *r, + ngx_http_upstream_t *u) { - ngx_connection_t *c; - ngx_http_request_t *r; - ngx_http_upstream_t *u; - - c = wev->data; - r = c->data; - u = r->upstream; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, + 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 (wev->timedout) { + if (c->write->timedout) { ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT); return; } @@ -1037,9 +1336,9 @@ ngx_http_upstream_send_request_handler(n #endif if (u->header_sent) { - wev->handler = ngx_http_upstream_dummy_handler; - - (void) ngx_handle_write_event(wev, 0); + u->write_event_handler = ngx_http_upstream_dummy_handler; + + (void) ngx_handle_write_event(c->write, 0); return; } @@ -1049,30 +1348,20 @@ ngx_http_upstream_send_request_handler(n static void -ngx_http_upstream_process_header(ngx_event_t *rev) +ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u) { - ssize_t n; - ngx_int_t rc; - ngx_str_t *uri, args; - ngx_uint_t i, flags; - ngx_list_part_t *part; - ngx_table_elt_t *h; - ngx_connection_t *c; - ngx_http_request_t *r; - ngx_http_upstream_t *u; - ngx_http_upstream_header_t *hh; - ngx_http_upstream_main_conf_t *umcf; - - c = rev->data; - r = c->data; - u = r->upstream; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, + 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 (rev->timedout) { + if (c->read->timedout) { ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT); return; } @@ -1106,70 +1395,68 @@ ngx_http_upstream_process_header(ngx_eve return; } -#if 0 - if (u->cache) { - u->buffer.pos += u->cache->ctx.header_size; +#if (NGX_HTTP_CACHE) + + if (r->cache) { + u->buffer.pos += r->cache->header_start; u->buffer.last = u->buffer.pos; } #endif } - n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last); - - if (n == NGX_AGAIN) { + 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); + ngx_add_timer(rev, u->read_timeout); #endif - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { - ngx_http_upstream_finalize_request(r, u, + 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; } - return; - } - - if (n == 0) { - ngx_log_error(NGX_LOG_ERR, rev->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->buffer.last += 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 0 - ngx_add_timer(rev, u->read_timeout); + u->valid_header_in = 0; + + u->peer.cached = 0; #endif - if (u->buffer.pos == u->buffer.end) { - ngx_log_error(NGX_LOG_ERR, rev->log, 0, - "upstream sent too big header"); - - ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_INVALID_HEADER); - return; + rc = u->process_header(r); + + if (rc == NGX_AGAIN) { + + if (u->buffer.pos == 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; } - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { - ngx_http_upstream_finalize_request(r, u, - NGX_HTTP_INTERNAL_SERVER_ERROR); - return; - } - - return; + break; } if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) { @@ -1200,124 +1487,10 @@ ngx_http_upstream_process_header(ngx_eve } } - umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); - - if (u->headers_in.x_accel_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; - } - } - } - - uri = &u->headers_in.x_accel_redirect->value; - args.len = 0; - args.data = NULL; - flags = 0; - - if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) { - ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND); - return; - } - - if (flags & NGX_HTTP_ZERO_IN_URI) { - r->zero_in_uri = 1; - } - - if (r->method != NGX_HTTP_HEAD) { - r->method = NGX_HTTP_GET; - } - - r->valid_unparsed_uri = 0; - - ngx_http_internal_redirect(r, uri, &args); + if (ngx_http_upstream_process_headers(r, u) != NGX_OK) { return; } - 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; - } - - 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; - } - } - - 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; - - u->headers_in.content_length_n = r->headers_out.content_length_n; - - if (r->headers_out.content_length_n != -1) { - u->length = (size_t) r->headers_out.content_length_n; - - } else { - u->length = NGX_MAX_SIZE_T_VALUE; - } - if (!r->subrequest_in_memory) { ngx_http_upstream_send_response(r, u); return; @@ -1337,19 +1510,27 @@ ngx_http_upstream_process_header(ngx_eve return; } - if (u->buffer.last - u->buffer.pos >= (ssize_t) u->length) { - if (u->input_filter(u->input_filter_ctx, 0) == NGX_ERROR) { + n = u->buffer.last - u->buffer.pos; + + if (n) { + u->buffer.last -= 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; } - ngx_http_upstream_finalize_request(r, u, 0); - return; + if (u->length == 0) { + ngx_http_upstream_finalize_request(r, u, 0); + return; + } } - rev->handler = ngx_http_upstream_process_body_in_memory; - - ngx_http_upstream_process_body_in_memory(rev); + u->read_event_handler = ngx_http_upstream_process_body_in_memory; + + ngx_http_upstream_process_body_in_memory(r, u); } @@ -1359,10 +1540,6 @@ ngx_http_upstream_test_next(ngx_http_req ngx_uint_t status; ngx_http_upstream_next_t *un; - if (!(u->conf->next_upstream & NGX_HTTP_UPSTREAM_FT_STATUS)) { - return NGX_DECLINED; - } - status = u->headers_in.status_n; for (un = ngx_http_upstream_next_errors; un->status; un++) { @@ -1376,16 +1553,21 @@ ngx_http_upstream_test_next(ngx_http_req return NGX_OK; } - 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 (NGX_HTTP_CACHE) - if (u->peer.tries == 0 && u->stale && (u->conf->use_stale & un->mask)) { - ngx_http_upstream_finalize_request(r, u, - ngx_http_send_cached_response(r)); + if (u->cache_status == NGX_HTTP_CACHE_EXPIRED + && (u->conf->cache_use_stale & un->mask)) + { + ngx_int_t rc; + + rc = u->reinit_request(r); + + if (rc == NGX_OK) { + u->cache_status = NGX_HTTP_CACHE_STALE; + rc = ngx_http_upstream_cache_send(r, u); + } + + ngx_http_upstream_finalize_request(r, u, rc); return NGX_OK; } @@ -1406,6 +1588,13 @@ ngx_http_upstream_intercept_errors(ngx_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; } @@ -1416,15 +1605,14 @@ ngx_http_upstream_intercept_errors(ngx_h return NGX_DECLINED; } - status = u->headers_in.status_n; - 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) { - + if (status == NGX_HTTP_UNAUTHORIZED + && u->headers_in.www_authenticate) + { h = ngx_list_push(&r->headers_out.headers); if (h == NULL) { @@ -1492,19 +1680,151 @@ ngx_http_upstream_test_connect(ngx_conne } -static void -ngx_http_upstream_process_body_in_memory(ngx_event_t *rev) +static ngx_int_t +ngx_http_upstream_process_headers(ngx_http_request_t *r, ngx_http_upstream_t *u) { - size_t size; - ssize_t n; - ngx_buf_t *b; - ngx_connection_t *c; - ngx_http_request_t *r; - ngx_http_upstream_t *u; - - c = rev->data; - r = c->data; - u = r->upstream; + 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; + args.len = 0; + args.data = NULL; + flags = 0; + + 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 (flags & NGX_HTTP_ZERO_IN_URI) { + r->zero_in_uri = 1; + } + + if (r->method != NGX_HTTP_HEAD) { + r->method = NGX_HTTP_GET; + } + + r->valid_unparsed_uri = 0; + + ngx_http_internal_redirect(r, uri, &args); + 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; + + u->headers_in.content_length_n = r->headers_out.content_length_n; + + if (r->headers_out.content_length_n != -1) { + u->length = (size_t) r->headers_out.content_length_n; + + } else { + u->length = NGX_MAX_SIZE_T_VALUE; + } + + return NGX_OK; +} + + +static void +ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r, + ngx_http_upstream_t *u) +{ + size_t size; + ssize_t n; + ngx_buf_t *b; + ngx_event_t *rev; + ngx_connection_t *c; + + c = u->peer.connection; + rev = c->read; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http upstream process body on memory"); @@ -1539,6 +1859,8 @@ ngx_http_upstream_process_body_in_memory return; } + 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; @@ -1549,7 +1871,7 @@ ngx_http_upstream_process_body_in_memory } } - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_upstream_finalize_request(r, u, NGX_ERROR); return; } @@ -1567,40 +1889,47 @@ static void ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u) { int tcp_nodelay; - ssize_t size; + ssize_t n; ngx_int_t rc; ngx_event_pipe_t *p; ngx_connection_t *c; - ngx_pool_cleanup_t *cl; - ngx_pool_cleanup_file_t *clf; ngx_http_core_loc_conf_t *clcf; rc = ngx_http_send_header(r); - if (rc == NGX_ERROR || rc > NGX_OK || r->post_action || r->header_only) { + if (rc == NGX_ERROR || rc > NGX_OK || r->post_action) { ngx_http_upstream_finalize_request(r, u, rc); return; } + c = r->connection; + + if (r->header_only) { + + if (u->cacheable || u->store) { + + if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) { + ngx_connection_error(c, ngx_socket_errno, + ngx_shutdown_socket_n " failed"); + } + + r->read_event_handler = ngx_http_request_empty_handler; + r->write_event_handler = ngx_http_request_empty_handler; + c->error = 1; + + } else { + ngx_http_upstream_finalize_request(r, u, rc); + return; + } + } + u->header_sent = 1; if (r->request_body && r->request_body->temp_file) { - for (cl = r->pool->cleanup; cl; cl = cl->next) { - if (cl->handler == ngx_pool_cleanup_file) { - clf = cl->data; - - if (clf->fd == r->request_body->temp_file->file.fd) { - cl->handler(clf); - cl->handler = NULL; - r->request_body->temp_file->file.fd = NGX_INVALID_FILE; - break; - } - } - } + ngx_pool_run_cleanup_file(r->pool, r->request_body->temp_file->file.fd); + r->request_body->temp_file->file.fd = NGX_INVALID_FILE; } - c = r->connection; - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (!u->buffering) { @@ -1611,8 +1940,7 @@ ngx_http_upstream_send_response(ngx_http u->input_filter_ctx = r; } - u->peer.connection->read->handler = - ngx_http_upstream_process_non_buffered_body; + u->read_event_handler = ngx_http_upstream_process_non_buffered_upstream; r->write_event_handler = ngx_http_upstream_process_non_buffered_downstream; @@ -1640,17 +1968,19 @@ ngx_http_upstream_send_response(ngx_http c->tcp_nodelay = NGX_TCP_NODELAY_SET; } - size = u->buffer.last - u->buffer.pos; - - if (size) { + n = u->buffer.last - u->buffer.pos; + + if (n) { u->buffer.last = u->buffer.pos; - if (u->input_filter(u->input_filter_ctx, size) == NGX_ERROR) { + u->state->response_length += n; + + if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) { ngx_http_upstream_finalize_request(r, u, 0); return; } - ngx_http_upstream_process_non_buffered_body(c->write); + ngx_http_upstream_process_non_buffered_downstream(r); } else { u->buffer.pos = u->buffer.start; @@ -1662,8 +1992,7 @@ ngx_http_upstream_send_response(ngx_http } if (u->peer.connection->read->ready) { - ngx_http_upstream_process_non_buffered_body( - u->peer.connection->read); + ngx_http_upstream_process_non_buffered_upstream(r, u); } } @@ -1672,30 +2001,49 @@ ngx_http_upstream_send_response(ngx_http /* TODO: preallocate event_pipe bufs, look "Content-Length" */ -#if 0 - - if (u->cache && u->cache->ctx.file.fd != NGX_INVALID_FILE) { - if (ngx_close_file(u->cache->ctx.file.fd) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, - ngx_close_file_n " \"%s\" failed", - u->cache->ctx.file.name.data); - } +#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; } if (u->cacheable) { - header = (ngx_http_cache_header_t *) u->buffer->start; - - header->expires = u->cache->ctx.expires; - header->last_modified = u->cache->ctx.last_modified; - header->date = u->cache->ctx.date; - header->length = r->headers_out.content_length_n; - u->cache->ctx.length = r->headers_out.content_length_n; - - header->key_len = u->cache->ctx.key0.len; - ngx_memcpy(&header->key, u->cache->ctx.key0.data, header->key_len); - header->key[header->key_len] = LF; + 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->last_modified = r->headers_out.last_modified_time; + r->cache->date = now; + r->cache->body_start = (u_short) (u->buffer.pos - u->buffer.start); + + if (r->headers_out.content_length_n != -1) { + r->cache->length = r->cache->body_start + + r->headers_out.content_length_n; + } + + ngx_http_file_cache_set_header(r, u->buffer.start); + + } else { + u->cacheable = 0; + r->headers_out.last_modified_time = -1; + } } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http cacheable: %d", u->cacheable); + #endif p = u->pipe; @@ -1723,7 +2071,7 @@ ngx_http_upstream_send_response(ngx_http p->temp_file->path = u->conf->temp_path; p->temp_file->pool = r->pool; - if (u->cacheable || u->store) { + if (p->cacheable) { p->temp_file->persistent = 1; } else { @@ -1793,69 +2141,82 @@ ngx_http_upstream_send_response(ngx_http p->send_timeout = clcf->send_timeout; p->send_lowat = clcf->send_lowat; - u->peer.connection->read->handler = ngx_http_upstream_process_body; + u->read_event_handler = ngx_http_upstream_process_upstream; r->write_event_handler = ngx_http_upstream_process_downstream; - ngx_http_upstream_process_body(u->peer.connection->read); + ngx_http_upstream_process_upstream(r, u); } static void ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r) { - ngx_http_upstream_process_non_buffered_body(r->connection->write); + 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, 0); + return; + } + + ngx_http_upstream_process_non_buffered_request(r, 1); } static void -ngx_http_upstream_process_non_buffered_body(ngx_event_t *ev) +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, 0); + 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 do_write; - ngx_connection_t *c, *downstream, *upstream; - ngx_http_request_t *r; + ngx_connection_t *downstream, *upstream; ngx_http_upstream_t *u; ngx_http_core_loc_conf_t *clcf; - c = ev->data; - r = c->data; u = r->upstream; - - if (ev->write) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http upstream process non buffered downstream"); - c->log->action = "sending to client"; - - } else { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http upstream process non buffered upstream"); - c->log->action = "reading upstream"; - } - - if (ev->timedout) { - if (ev->write) { - c->timedout = 1; - ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); - - } else { - ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); - } - - ngx_http_upstream_finalize_request(r, u, 0); - return; - } - downstream = r->connection; upstream = u->peer.connection; b = &u->buffer; - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - - do_write = ev->write || u->length == 0; + do_write = do_write || u->length == 0; for ( ;; ) { @@ -1907,6 +2268,8 @@ ngx_http_upstream_process_non_buffered_b } if (n > 0) { + u->state->response_length += n; + if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) { ngx_http_upstream_finalize_request(r, u, 0); return; @@ -1921,9 +2284,11 @@ ngx_http_upstream_process_non_buffered_b 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_ERROR) + != NGX_OK) { ngx_http_upstream_finalize_request(r, u, 0); return; @@ -1937,7 +2302,7 @@ ngx_http_upstream_process_non_buffered_b ngx_del_timer(downstream->write); } - if (ngx_handle_read_event(upstream->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(upstream->read, 0) != NGX_OK) { ngx_http_upstream_finalize_request(r, u, 0); return; } @@ -2003,96 +2368,71 @@ ngx_http_upstream_non_buffered_filter(vo static void ngx_http_upstream_process_downstream(ngx_http_request_t *r) { - ngx_http_upstream_process_body(r->connection->write); -} - - -static void -ngx_http_upstream_process_body(ngx_event_t *ev) -{ - ngx_temp_file_t *tf; + ngx_event_t *wev; + ngx_connection_t *c; ngx_event_pipe_t *p; - ngx_connection_t *c, *downstream; - ngx_http_log_ctx_t *ctx; - ngx_http_request_t *r; ngx_http_upstream_t *u; - c = ev->data; - r = c->data; + c = r->connection; u = r->upstream; - downstream = r->connection; - - if (ev->write) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http upstream process downstream"); - c->log->action = "sending to client"; - - } else { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http upstream process upstream"); - c->log->action = "reading upstream"; - - ctx = c->log->data; - ctx->current_request = r; - } - p = u->pipe; - - if (ev->timedout) { - if (ev->write) { - if (ev->delayed) { - - ev->timedout = 0; - ev->delayed = 0; - - if (!ev->ready) { - ngx_add_timer(ev, p->send_timeout); - - if (ngx_handle_write_event(ev, p->send_lowat) == NGX_ERROR) - { - ngx_http_upstream_finalize_request(r, u, 0); - return; - } - + wev = c->write; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream process downstream"); + + c->log->action = "sending to client"; + + if (wev->timedout) { + + if (wev->delayed) { + + wev->timedout = 0; + wev->delayed = 0; + + if (!wev->ready) { + ngx_add_timer(wev, p->send_timeout); + + if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, 0); + } + + return; + } + + if (ngx_event_pipe(p, wev->write) == NGX_ABORT) { + + if (c->destroyed) { return; } - if (ngx_event_pipe(p, ev->write) == NGX_ABORT) { - - if (downstream->destroyed) { - return; - } - - ngx_http_upstream_finalize_request(r, u, 0); - return; - } - - } else { - p->downstream_error = 1; - c->timedout = 1; - ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); + ngx_http_upstream_finalize_request(r, u, 0); + return; } } else { - p->upstream_error = 1; - ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); + p->downstream_error = 1; + c->timedout = 1; + ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); } } else { - if (ev->write && ev->delayed) { + + if (wev->delayed) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http downstream delayed"); - if (ngx_handle_write_event(ev, p->send_lowat) == NGX_ERROR) { - return; + if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, 0); } return; } - if (ngx_event_pipe(p, ev->write) == NGX_ABORT) { - - if (downstream->destroyed) { + if (ngx_event_pipe(p, 1) == NGX_ABORT) { + + if (c->destroyed) { return; } @@ -2101,24 +2441,79 @@ ngx_http_upstream_process_body(ngx_event } } + ngx_http_upstream_process_request(r); +} + + +static void +ngx_http_upstream_process_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 upstream"); + + c->log->action = "reading upstream"; + + if (c->read->timedout) { + u->pipe->upstream_error = 1; + ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); + + } else { + c = r->connection; + + if (ngx_event_pipe(u->pipe, 0) == NGX_ABORT) { + + if (c->destroyed) { + return; + } + + ngx_http_upstream_finalize_request(r, u, 0); + return; + } + } + + ngx_http_upstream_process_request(r); +} + + +static void +ngx_http_upstream_process_request(ngx_http_request_t *r) +{ + ngx_uint_t del; + ngx_temp_file_t *tf; + ngx_event_pipe_t *p; + ngx_http_upstream_t *u; + + u = r->upstream; + p = u->pipe; + if (u->peer.connection) { if (u->store) { + del = p->upstream_error; + tf = u->pipe->temp_file; - if (p->upstream_eof - && u->headers_in.status_n == NGX_HTTP_OK - && (u->headers_in.content_length_n == -1 - || (u->headers_in.content_length_n == tf->offset))) - { - ngx_http_upstream_store(r, u); - - } else if ((p->upstream_error - || (p->upstream_eof - && u->headers_in.status_n != NGX_HTTP_OK)) - && tf->file.fd != NGX_INVALID_FILE) - { + if (p->upstream_eof || p->upstream_done) { + + if (u->headers_in.status_n == NGX_HTTP_OK + && (u->headers_in.content_length_n == -1 + || (u->headers_in.content_length_n == tf->offset))) + { + ngx_http_upstream_store(r, u); + + } else { + del = 1; + } + } + + if (del && tf->file.fd != NGX_INVALID_FILE) { + if (ngx_delete_file(tf->file.name.data) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, @@ -2128,30 +2523,28 @@ ngx_http_upstream_process_body(ngx_event } } -#if (NGX_HTTP_FILE_CACHE) - - if (p->upstream_done && u->cacheable) { - if (ngx_http_cache_update(r) == NGX_ERROR) { - ngx_http_busy_unlock(u->conf->busy_lock, &u->busy_lock); - ngx_http_upstream_finalize_request(r, u, 0); - return; - } - - } else if (p->upstream_eof && u->cacheable) { - - /* TODO: check length & update cache */ - - if (ngx_http_cache_update(r) == NGX_ERROR) { - ngx_http_busy_unlock(u->conf->busy_lock, &u->busy_lock); - ngx_http_upstream_finalize_request(r, u, 0); - return; +#if (NGX_HTTP_CACHE) + + if (u->cacheable) { + + if (p->upstream_done) { + ngx_http_file_cache_update(r, u->pipe->temp_file); + + } else if (p->upstream_eof) { + + /* TODO: check length & update cache */ + + ngx_http_file_cache_update(r, u->pipe->temp_file); + + } else if (p->upstream_error) { + ngx_http_file_cache_free(r, u->pipe->temp_file); } } #endif if (p->upstream_done || p->upstream_eof || p->upstream_error) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http upstream exit: %p", p->out); #if 0 ngx_http_busy_unlock(u->conf->busy_lock, &u->busy_lock); @@ -2162,10 +2555,10 @@ ngx_http_upstream_process_body(ngx_event } if (p->downstream_error) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http upstream downstream error"); - if (!u->cacheable && u->peer.connection) { + if (!u->cacheable && !u->store && u->peer.connection) { ngx_http_upstream_finalize_request(r, u, 0); } } @@ -2209,9 +2602,11 @@ ngx_http_upstream_store(ngx_http_request } 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_rename_error = 1; ext.log = r->connection->log; if (u->headers_in.last_modified) { @@ -2247,9 +2642,9 @@ ngx_http_upstream_store(ngx_http_request static void -ngx_http_upstream_dummy_handler(ngx_event_t *wev) +ngx_http_upstream_dummy_handler(ngx_http_request_t *r, ngx_http_upstream_t *u) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http upstream dummy handler"); } @@ -2323,12 +2718,21 @@ ngx_http_upstream_next(ngx_http_request_ #if (NGX_HTTP_CACHE) - if (u->stale && (u->conf->use_stale & ft_type)) { - ngx_http_upstream_finalize_request(r, u, - ngx_http_send_cached_response(r)); + if (u->cache_status == NGX_HTTP_CACHE_EXPIRED + && (u->conf->cache_use_stale & ft_type)) + { + ngx_int_t rc; + + rc = u->reinit_request(r); + + if (rc == NGX_OK) { + u->cache_status = NGX_HTTP_CACHE_STALE; + rc = ngx_http_upstream_cache_send(r, u); + } + + ngx_http_upstream_finalize_request(r, u, rc); return; } - #endif ngx_http_upstream_finalize_request(r, u, status); @@ -2393,12 +2797,18 @@ ngx_http_upstream_finalize_request(ngx_h ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "finalize http upstream request: %i", rc); - *u->cleanup = NULL; + if (u->cleanup) { + *u->cleanup = NULL; + } if (u->state && u->state->response_sec) { tp = ngx_timeofday(); u->state->response_sec = tp->sec - u->state->response_sec; u->state->response_msec = tp->msec - u->state->response_msec; + + if (u->pipe) { + u->state->response_length = u->pipe->read_length; + } } u->finalize_request(r, rc); @@ -2436,25 +2846,42 @@ ngx_http_upstream_finalize_request(ngx_h u->peer.connection = NULL; - if (u->header_sent && (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE)) - { - rc = 0; - } - 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 0 - if (u->cache) { +#if (NGX_HTTP_CACHE) + + if (u->cacheable) { + time_t valid; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http upstream cache fd: %d", - u->cache->ctx.file.fd); + r->cache->file.fd); + + if (rc == NGX_HTTP_BAD_GATEWAY || rc == NGX_HTTP_GATEWAY_TIME_OUT) { + + 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, u->pipe->temp_file); } + #endif + if (u->header_sent + && (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE)) + { + rc = 0; + } + if (rc == NGX_DECLINED) { return; } @@ -2462,16 +2889,7 @@ ngx_http_upstream_finalize_request(ngx_h r->connection->log->action = "sending to client"; if (rc == 0) { - if (r == r->main) { - if (!r->post_action) { - rc = ngx_http_send_special(r, NGX_HTTP_LAST); - } - - } else { - if (r->out) { - rc = NGX_AGAIN; - } - } + rc = ngx_http_send_special(r, NGX_HTTP_LAST); } ngx_http_finalize_request(r, rc); @@ -2495,13 +2913,23 @@ ngx_http_upstream_process_header_line(ng static ngx_int_t -ngx_http_upstream_process_multi_header_lines(ngx_http_request_t *r, +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_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; - - pa = (ngx_array_t *) ((char *) &r->upstream->headers_in + 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) @@ -2517,14 +2945,159 @@ ngx_http_upstream_process_multi_header_l *ph = h; +#if (NGX_HTTP_CACHE) + { + u_char *p, *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) { + return NGX_OK; + } + + last = h->value.data + h->value.len; + + if (ngx_strlcasestrn(h->value.data, last, (u_char *) "no-cache", 8 - 1) + != NULL) + { + u->cacheable = 0; + return NGX_OK; + } + + p = ngx_strlcasestrn(h->value.data, last, (u_char *) "max-age=", 8 - 1); + + if (p == NULL) { + return NGX_OK; + } + + n = 0; + + for (p += 8; p < last; p++) { + if (*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; + } +#endif + return NGX_OK; } static ngx_int_t -ngx_http_upstream_ignore_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, +ngx_http_upstream_process_expires(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { + ngx_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_http_parse_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; + 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; } @@ -2651,6 +3224,7 @@ ngx_http_upstream_copy_content_type(ngx_ 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++) { @@ -2715,6 +3289,33 @@ ngx_http_upstream_copy_content_length(ng 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; + +#if (NGX_HTTP_CACHE) + + if (r->upstream->cacheable) { + r->headers_out.last_modified = ho; + r->headers_out.last_modified_time = ngx_http_parse_time(h->value.data, + h->value.len); + } + +#endif + + 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) { @@ -2804,6 +3405,33 @@ ngx_http_upstream_rewrite_refresh(ngx_ht } +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 (NGX_HTTP_CACHE) + + if (r->cached) { + r->allow_ranges = 1; + return NGX_OK; + + } + +#endif + + ho = ngx_list_push(&r->headers_out.headers); + if (ho == NULL) { + return NGX_ERROR; + } + + *ho = *h; + + return NGX_OK; +} + + #if (NGX_HTTP_GZIP) static ngx_int_t @@ -3051,6 +3679,66 @@ ngx_http_upstream_response_time_variable } +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 ( ;; ) { + 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; +} + + ngx_int_t ngx_http_upstream_header_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) @@ -3066,6 +3754,33 @@ ngx_http_upstream_header_variable(ngx_ht } +#if (NGX_HTTP_CACHE) + +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; +} + +#endif + + static char * ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) { @@ -3438,7 +4153,12 @@ ngx_http_upstream_hide_headers_hash(ngx_ { conf->hide_headers_hash = prev->hide_headers_hash; - if (conf->hide_headers_hash.buckets) { + if (conf->hide_headers_hash.buckets +#if (NGX_HTTP_CACHE) + && ((conf->cache == NULL) == (prev->cache == NULL)) +#endif + ) + { return NGX_OK; } @@ -3544,7 +4264,7 @@ ngx_http_upstream_create_main_conf(ngx_c sizeof(ngx_http_upstream_srv_conf_t *)) != NGX_OK) { - return NGX_CONF_ERROR; + return NULL; } return umcf; diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -24,8 +24,9 @@ #define NGX_HTTP_UPSTREAM_FT_HTTP_503 0x00000040 #define NGX_HTTP_UPSTREAM_FT_HTTP_504 0x00000080 #define NGX_HTTP_UPSTREAM_FT_HTTP_404 0x00000100 -#define NGX_HTTP_UPSTREAM_FT_BUSY_LOCK 0x00000200 -#define NGX_HTTP_UPSTREAM_FT_MAX_WAITING 0x00000400 +#define NGX_HTTP_UPSTREAM_FT_UPDATING 0x00000200 +#define NGX_HTTP_UPSTREAM_FT_BUSY_LOCK 0x00000400 +#define NGX_HTTP_UPSTREAM_FT_MAX_WAITING 0x00000800 #define NGX_HTTP_UPSTREAM_FT_NOLIVE 0x40000000 #define NGX_HTTP_UPSTREAM_FT_OFF 0x80000000 @@ -38,21 +39,28 @@ #define NGX_HTTP_UPSTREAM_INVALID_HEADER 40 -typedef struct { - ngx_msec_t bl_time; - ngx_uint_t bl_state; +#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 + - ngx_uint_t status; - time_t response_sec; - ngx_uint_t response_msec; +typedef struct { + ngx_msec_t bl_time; + ngx_uint_t bl_state; - ngx_str_t *peer; + ngx_uint_t status; + time_t response_sec; + ngx_uint_t response_msec; + off_t response_length; + + ngx_str_t *peer; } ngx_http_upstream_state_t; typedef struct { - ngx_hash_t headers_in_hash; - ngx_array_t upstreams; + ngx_hash_t headers_in_hash; + ngx_array_t upstreams; /* ngx_http_upstream_srv_conf_t */ } ngx_http_upstream_main_conf_t; @@ -65,21 +73,21 @@ typedef ngx_int_t (*ngx_http_upstream_in typedef struct { - ngx_http_upstream_init_pt init_upstream; - ngx_http_upstream_init_peer_pt init; - void *data; + ngx_http_upstream_init_pt init_upstream; + ngx_http_upstream_init_peer_pt init; + void *data; } ngx_http_upstream_peer_t; typedef struct { - ngx_peer_addr_t *addrs; - ngx_uint_t naddrs; - ngx_uint_t weight; - ngx_uint_t max_fails; - time_t fail_timeout; + ngx_peer_addr_t *addrs; + ngx_uint_t naddrs; + ngx_uint_t weight; + ngx_uint_t max_fails; + time_t fail_timeout; - unsigned down:1; - unsigned backup:1; + unsigned down:1; + unsigned backup:1; } ngx_http_upstream_server_t; @@ -92,185 +100,211 @@ typedef struct { struct ngx_http_upstream_srv_conf_s { - ngx_http_upstream_peer_t peer; - void **srv_conf; + ngx_http_upstream_peer_t peer; + void **srv_conf; - ngx_array_t *servers; /* ngx_http_upstream_server_t */ + 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; - in_port_t default_port; + ngx_uint_t flags; + ngx_str_t host; + u_char *file_name; + ngx_uint_t line; + in_port_t port; + in_port_t default_port; }; typedef struct { - ngx_http_upstream_srv_conf_t *upstream; + 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 timeout; + ngx_msec_t connect_timeout; + ngx_msec_t send_timeout; + ngx_msec_t read_timeout; + ngx_msec_t timeout; - size_t send_lowat; - size_t buffer_size; + size_t send_lowat; + size_t buffer_size; - size_t busy_buffers_size; - size_t max_temp_file_size; - size_t temp_file_write_size; + 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; - 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 next_upstream; - ngx_uint_t store_access; - - ngx_bufs_t bufs; + ngx_uint_t ignore_headers; + ngx_uint_t next_upstream; + ngx_uint_t store_access; + ngx_flag_t buffering; + ngx_flag_t pass_request_headers; + ngx_flag_t pass_request_body; - ngx_flag_t 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_path_t *temp_path; - ngx_flag_t ignore_client_abort; - ngx_flag_t intercept_errors; - ngx_flag_t cyclic_temp_file; + ngx_hash_t hide_headers_hash; + ngx_array_t *hide_headers; + ngx_array_t *pass_headers; - ngx_path_t *temp_path; +#if (NGX_HTTP_CACHE) + ngx_shm_zone_t *cache; - ngx_hash_t hide_headers_hash; - ngx_array_t *hide_headers; - ngx_array_t *pass_headers; + ngx_uint_t cache_min_uses; + ngx_uint_t cache_use_stale; + ngx_uint_t cache_methods; - ngx_str_t schema; + ngx_array_t *cache_valid; +#endif - ngx_array_t *store_lengths; - ngx_array_t *store_values; + ngx_array_t *store_lengths; + ngx_array_t *store_values; - signed store:2; - unsigned intercept_404:1; - unsigned change_buffering:1; + signed store:2; + unsigned intercept_404:1; + unsigned change_buffering:1; #if (NGX_HTTP_SSL) - ngx_ssl_t *ssl; - ngx_flag_t ssl_session_reuse; + ngx_ssl_t *ssl; + ngx_flag_t ssl_session_reuse; #endif } 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_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 headers; - ngx_uint_t status_n; - ngx_str_t status_line; + 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 *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 *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 *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 *last_modified; + ngx_table_elt_t *location; + ngx_table_elt_t *accept_ranges; + ngx_table_elt_t *www_authenticate; #if (NGX_HTTP_GZIP) - ngx_table_elt_t *content_encoding; + ngx_table_elt_t *content_encoding; #endif - off_t content_length_n; + off_t content_length_n; - ngx_array_t cache_control; + ngx_array_t cache_control; } ngx_http_upstream_headers_in_t; typedef struct { - ngx_str_t host; - in_port_t port; - ngx_uint_t default_port; /* unsigned default_port:1 */ - ngx_uint_t naddrs; - in_addr_t *addrs; - ngx_resolver_ctx_t *ctx; + ngx_str_t host; + in_port_t port; + ngx_uint_t no_port; /* unsigned no_port:1 */ + + ngx_uint_t naddrs; + in_addr_t *addrs; + + struct sockaddr *sockaddr; + socklen_t socklen; + + 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_peer_connection_t peer; + ngx_http_upstream_handler_pt read_event_handler; + ngx_http_upstream_handler_pt write_event_handler; - ngx_event_pipe_t *pipe; + ngx_peer_connection_t peer; - ngx_chain_t *request_bufs; + ngx_event_pipe_t *pipe; - ngx_output_chain_ctx_t output; - ngx_chain_writer_ctx_t writer; + ngx_chain_t *request_bufs; - ngx_http_upstream_conf_t *conf; + ngx_output_chain_ctx_t output; + ngx_chain_writer_ctx_t writer; - ngx_http_upstream_headers_in_t headers_in; + ngx_http_upstream_conf_t *conf; - ngx_http_upstream_resolved_t *resolved; + ngx_http_upstream_headers_in_t headers_in; - ngx_buf_t buffer; - size_t length; + ngx_http_upstream_resolved_t *resolved; + + ngx_buf_t buffer; + size_t length; - ngx_chain_t *out_bufs; - ngx_chain_t *busy_bufs; - ngx_chain_t *free_bufs; + 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; + ngx_int_t (*input_filter_init)(void *data); + ngx_int_t (*input_filter)(void *data, ssize_t bytes); + void *input_filter_ctx; - 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); +#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_msec_t timeout; + ngx_msec_t timeout; - ngx_http_upstream_state_t *state; + ngx_http_upstream_state_t *state; - ngx_str_t method; - ngx_str_t schema; - ngx_str_t uri; + ngx_str_t method; + ngx_str_t schema; + ngx_str_t uri; - ngx_http_cleanup_pt *cleanup; + ngx_http_cleanup_pt *cleanup; - unsigned store:1; - unsigned cacheable:1; - unsigned accel:1; - unsigned ssl:1; + 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 buffering:1; - unsigned request_sent:1; - unsigned header_sent:1; + unsigned request_sent:1; + unsigned header_sent:1; }; @@ -295,7 +329,9 @@ ngx_int_t ngx_http_upstream_hide_headers uscf->srv_conf[module.ctx_index] -extern ngx_module_t ngx_http_upstream_module; +extern ngx_module_t ngx_http_upstream_module; +extern ngx_conf_bitmask_t ngx_http_upstream_cache_method_mask[]; + #endif /* _NGX_HTTP_UPSTREAM_H_INCLUDED_ */ diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c --- a/src/http/ngx_http_upstream_round_robin.c +++ b/src/http/ngx_http_upstream_round_robin.c @@ -279,35 +279,47 @@ ngx_http_upstream_create_round_robin_pee peers->number = ur->naddrs; peers->name = &ur->host; - for (i = 0; i < ur->naddrs; i++) { - - len = NGX_INET_ADDRSTRLEN + sizeof(":65536") - 1; + if (ur->sockaddr) { + peers->peer[0].sockaddr = ur->sockaddr; + peers->peer[0].socklen = ur->socklen; + peers->peer[0].name = ur->host; + peers->peer[0].weight = 1; + peers->peer[0].current_weight = 1; + peers->peer[0].max_fails = 1; + peers->peer[0].fail_timeout = 10; - p = ngx_pnalloc(r->pool, len); - if (p == NULL) { - return NGX_ERROR; - } + } else { + + for (i = 0; i < ur->naddrs; i++) { - len = ngx_inet_ntop(AF_INET, &ur->addrs[i], p, NGX_INET_ADDRSTRLEN); - len = ngx_sprintf(&p[len], ":%d", ur->port) - p; + len = NGX_INET_ADDRSTRLEN + sizeof(":65536") - 1; + + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + return NGX_ERROR; + } - sin = ngx_pcalloc(r->pool, sizeof(struct sockaddr_in)); - if (sin == NULL) { - return NGX_ERROR; - } + len = ngx_inet_ntop(AF_INET, &ur->addrs[i], p, NGX_INET_ADDRSTRLEN); + len = ngx_sprintf(&p[len], ":%d", ur->port) - p; - sin->sin_family = AF_INET; - sin->sin_port = htons(ur->port); - sin->sin_addr.s_addr = ur->addrs[i]; + sin = ngx_pcalloc(r->pool, sizeof(struct sockaddr_in)); + if (sin == NULL) { + return NGX_ERROR; + } - peers->peer[i].sockaddr = (struct sockaddr *) sin; - peers->peer[i].socklen = sizeof(struct sockaddr_in); - peers->peer[i].name.len = len; - peers->peer[i].name.data = p; - peers->peer[i].weight = 1; - peers->peer[i].current_weight = 1; - peers->peer[i].max_fails = 1; - peers->peer[i].fail_timeout = 10; + sin->sin_family = AF_INET; + sin->sin_port = htons(ur->port); + sin->sin_addr.s_addr = ur->addrs[i]; + + peers->peer[i].sockaddr = (struct sockaddr *) sin; + peers->peer[i].socklen = sizeof(struct sockaddr_in); + peers->peer[i].name.len = len; + peers->peer[i].name.data = p; + peers->peer[i].weight = 1; + peers->peer[i].current_weight = 1; + peers->peer[i].max_fails = 1; + peers->peer[i].fail_timeout = 10; + } } rrp->peers = peers; diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -25,6 +25,8 @@ static ngx_int_t ngx_http_variable_unkno 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_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); @@ -60,6 +62,8 @@ static ngx_int_t ngx_http_variable_body_ 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); @@ -67,6 +71,8 @@ static ngx_int_t ngx_http_variable_sent_ 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, @@ -198,6 +204,10 @@ static ngx_http_variable_t ngx_http_cor 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 }, @@ -208,6 +218,9 @@ static ngx_http_variable_t ngx_http_cor { 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 }, @@ -325,7 +338,8 @@ ngx_http_get_variable_index(ngx_conf_t * if (v == NULL) { if (ngx_array_init(&cmcf->variables, cf->pool, 4, - sizeof(ngx_http_variable_t)) == NGX_ERROR) + sizeof(ngx_http_variable_t)) + != NGX_OK) { return NGX_ERROR; } @@ -488,6 +502,15 @@ ngx_http_get_variable(ngx_http_request_t return NULL; } + if (ngx_strncmp(name->data, "cookie_", 7) == 0) { + + if (ngx_http_variable_cookie(r, vv, (uintptr_t) name) == NGX_OK) { + return vv; + } + + return NULL; + } + if (ngx_strncmp(name->data, "arg_", 4) == 0) { if (ngx_http_variable_argument(r, vv, (uintptr_t) name) == NGX_OK) { @@ -728,56 +751,56 @@ ngx_http_variable_unknown_header(ngx_htt 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 *p, *arg; - size_t len; + u_char *arg; + size_t len; + ngx_str_t value; - if (r->args.len == 0) { + 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; } - len = name->len - 1 - (sizeof("arg_") - 1); - arg = name->data + sizeof("arg_") - 1; - - for (p = r->args.data; *p && *p != ' '; p++) { - - /* - * although r->args.data is not null-terminated by itself, - * however, there is null in the end of request line - */ - - p = ngx_strcasestrn(p, (char *) arg, len); - - if (p == NULL) { - v->not_found = 1; - return NGX_OK; - } - - if ((p == r->args.data || *(p - 1) == '&') && *(p + len + 1) == '=') { - - v->data = p + len + 2; - - p = (u_char *) ngx_strchr(p, '&'); - - if (p == NULL) { - p = r->args.data + r->args.len; - } - - v->len = p - v->data; - v->valid = 1; - v->no_cacheable = 0; - v->not_found = 0; - - return NGX_OK; - } - } - - v->not_found = 1; + v->data = value.data; + v->len = value.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; return NGX_OK; } @@ -812,17 +835,37 @@ 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; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif - /* AF_INET only */ + switch (r->connection->sockaddr->sa_family) { - sin = (struct sockaddr_in *) r->connection->sockaddr; +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) 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.s_addr; + v->len = sizeof(struct in6_addr); + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) &sin6->sin6_addr; + + 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; } @@ -846,8 +889,11 @@ 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; - struct sockaddr_in *sin; + ngx_uint_t port; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif v->len = 0; v->valid = 1; @@ -859,16 +905,23 @@ ngx_http_variable_remote_port(ngx_http_r return NGX_ERROR; } - /* AF_INET only */ + switch (r->connection->sockaddr->sa_family) { - if (r->connection->sockaddr->sa_family == AF_INET) { - sin = (struct sockaddr_in *) r->connection->sockaddr; +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) r->connection->sockaddr; + port = ntohs(sin6->sin6_port); + break; +#endif + default: /* AF_INET */ + sin = (struct sockaddr_in *) r->connection->sockaddr; port = ntohs(sin->sin_port); + break; + } - if (port > 0 && port < 65536) { - v->len = ngx_sprintf(v->data, "%ui", port) - v->data; - } + if (port > 0 && port < 65536) { + v->len = ngx_sprintf(v->data, "%ui", port) - v->data; } return NGX_OK; @@ -880,15 +933,21 @@ ngx_http_variable_server_addr(ngx_http_r ngx_http_variable_value_t *v, uintptr_t data) { ngx_str_t s; + u_char addr[NGX_SOCKADDR_STRLEN]; - s.data = ngx_pnalloc(r->pool, NGX_INET_ADDRSTRLEN); + 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; } - if (ngx_http_server_addr(r, &s) != NGX_OK) { - return NGX_ERROR; - } + ngx_memcpy(s.data, addr, s.len); v->len = s.len; v->valid = 1; @@ -904,11 +963,44 @@ static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - v->len = r->port_text->len - 1; + ngx_uint_t port; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + v->len = 0; v->valid = 1; v->no_cacheable = 0; v->not_found = 0; - v->data = r->port_text->data + 1; + + 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; + } + + switch (r->connection->local_sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) r->connection->local_sockaddr; + port = ntohs(sin6->sin6_port); + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) r->connection->local_sockaddr; + port = ntohs(sin->sin_port); + break; + } + + if (port > 0 && port < 65536) { + v->len = ngx_sprintf(v->data, "%ui", port) - v->data; + } return NGX_OK; } @@ -987,9 +1079,7 @@ ngx_http_variable_document_root(ngx_http return NGX_ERROR; } - if (ngx_conf_full_name((ngx_cycle_t *) ngx_cycle, &path, 0) - == NGX_ERROR) - { + if (ngx_conf_full_name((ngx_cycle_t *) ngx_cycle, &path, 0) != NGX_OK) { return NGX_ERROR; } @@ -1028,9 +1118,7 @@ ngx_http_variable_realpath_root(ngx_http path.data[path.len - 1] = '\0'; - if (ngx_conf_full_name((ngx_cycle_t *) ngx_cycle, &path, 0) - == NGX_ERROR) - { + if (ngx_conf_full_name((ngx_cycle_t *) ngx_cycle, &path, 0) != NGX_OK) { return NGX_ERROR; } } @@ -1231,6 +1319,31 @@ ngx_http_variable_sent_content_length(ng 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; + } + + name.len = sizeof("sent_http_location") - 1; + name.data = (u_char *) "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) { @@ -1371,6 +1484,59 @@ ngx_http_variable_request_completion(ngx 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, *next; + 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; + } + + next = cl->next->buf; + len = (buf->last - buf->pos) + (next->last - next->pos); + + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + v->data = p; + + p = ngx_cpymem(p, buf->pos, buf->last - buf->pos); + ngx_memcpy(p, next->pos, next->last - next->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) { @@ -1544,6 +1710,13 @@ ngx_http_variables_init_vars(ngx_conf_t continue; } + if (ngx_strncmp(v[i].name.data, "cookie_", 7) == 0) { + v[i].get_handler = ngx_http_variable_cookie; + v[i].data = (uintptr_t) &v[i].name; + + continue; + } + if (ngx_strncmp(v[i].name.data, "arg_", 4) == 0) { v[i].get_handler = ngx_http_variable_argument; v[i].data = (uintptr_t) &v[i].name; @@ -1589,3 +1762,87 @@ ngx_http_variables_init_vars(ngx_conf_t return NGX_OK; } + + +void +ngx_http_variable_value_rbtree_insert(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) +{ + ngx_rbtree_node_t **p; + ngx_http_variable_value_node_t *vvn, *vvt; + + for ( ;; ) { + + vvn = (ngx_http_variable_value_node_t *) node; + vvt = (ngx_http_variable_value_node_t *) temp; + + if (node->key != temp->key) { + + p = (node->key < temp->key) ? &temp->left : &temp->right; + + } else if (vvn->len != vvt->len) { + + p = (vvn->len < vvt->len) ? &temp->left : &temp->right; + + } else { + p = (ngx_memcmp(vvn->value->data, vvt->value->data, vvn->len) < 0) + ? &temp->left : &temp->right; + } + + if (*p == sentinel) { + break; + } + + temp = *p; + } + + *p = node; + node->parent = temp; + node->left = sentinel; + node->right = sentinel; + ngx_rbt_red(node); +} + + +ngx_http_variable_value_t * +ngx_http_variable_value_lookup(ngx_rbtree_t *rbtree, ngx_str_t *val, + uint32_t hash) +{ + ngx_int_t rc; + ngx_rbtree_node_t *node, *sentinel; + ngx_http_variable_value_node_t *vvn; + + node = rbtree->root; + sentinel = rbtree->sentinel; + + while (node != sentinel) { + + vvn = (ngx_http_variable_value_node_t *) node; + + if (hash != node->key) { + node = (hash < node->key) ? node->left : node->right; + continue; + } + + if (val->len != vvn->len) { + node = (val->len < vvn->len) ? node->left : node->right; + continue; + } + + rc = ngx_memcmp(val->data, vvn->value->data, val->len); + + if (rc < 0) { + node = node->left; + continue; + } + + if (rc > 0) { + node = node->right; + continue; + } + + return vvn->value; + } + + return NULL; +} diff --git a/src/http/ngx_http_variables.h b/src/http/ngx_http_variables.h --- a/src/http/ngx_http_variables.h +++ b/src/http/ngx_http_variables.h @@ -63,6 +63,19 @@ ngx_int_t ngx_http_variables_add_core_va ngx_int_t ngx_http_variables_init_vars(ngx_conf_t *cf); +typedef struct { + ngx_rbtree_node_t node; + size_t len; + ngx_http_variable_value_t *value; +} ngx_http_variable_value_node_t; + + +void ngx_http_variable_value_rbtree_insert(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); +ngx_http_variable_value_t *ngx_http_variable_value_lookup(ngx_rbtree_t *rbtree, + ngx_str_t *name, uint32_t hash); + + extern ngx_http_variable_value_t ngx_http_variable_null_value; extern ngx_http_variable_value_t ngx_http_variable_true_value; diff --git a/src/http/ngx_http_write_filter_module.c b/src/http/ngx_http_write_filter_module.c --- a/src/http/ngx_http_write_filter_module.c +++ b/src/http/ngx_http_write_filter_module.c @@ -46,7 +46,7 @@ ngx_module_t ngx_http_write_filter_modu ngx_int_t ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in) { - off_t size, sent, limit; + off_t size, sent, nsent, limit; ngx_uint_t last, flush; ngx_msec_t delay; ngx_chain_t *cl, *ln, **ll, *chain; @@ -210,7 +210,8 @@ ngx_http_write_filter(ngx_http_request_t } if (r->limit_rate) { - limit = r->limit_rate * (ngx_time() - r->start_sec + 1) - c->sent; + limit = r->limit_rate * (ngx_time() - r->start_sec + 1) + - (c->sent - clcf->limit_rate_after); if (limit <= 0) { c->write->delayed = 1; @@ -245,7 +246,23 @@ ngx_http_write_filter(ngx_http_request_t } if (r->limit_rate) { - delay = (ngx_msec_t) ((c->sent - sent) * 1000 / r->limit_rate + 1); + + nsent = c->sent; + + if (clcf->limit_rate_after) { + + sent -= clcf->limit_rate_after; + if (sent < 0) { + sent = 0; + } + + nsent -= clcf->limit_rate_after; + if (nsent < 0) { + nsent = 0; + } + } + + delay = (ngx_msec_t) ((nsent - sent) * 1000 / r->limit_rate + 1); if (delay > 0) { c->write->delayed = 1; diff --git a/src/mail/ngx_mail.c b/src/mail/ngx_mail.c --- a/src/mail/ngx_mail.c +++ b/src/mail/ngx_mail.c @@ -11,7 +11,16 @@ static char *ngx_mail_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static ngx_int_t ngx_mail_cmp_conf_in_addrs(const void *one, const void *two); +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; @@ -64,18 +73,12 @@ static char * ngx_mail_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *rv; - u_char *text; - size_t len; - ngx_uint_t i, a, l, m, mi, s, p, last, bind_all, done; + ngx_uint_t i, m, mi, s; ngx_conf_t pcf; - ngx_array_t in_ports; - ngx_listening_t *ls; - ngx_mail_listen_t *imls; + ngx_array_t ports; + ngx_mail_listen_t *listen; ngx_mail_module_t *module; - ngx_mail_in_port_t *imip; ngx_mail_conf_ctx_t *ctx; - ngx_mail_conf_in_port_t *in_port; - ngx_mail_conf_in_addr_t *in_addr; ngx_mail_core_srv_conf_t **cscfp; ngx_mail_core_main_conf_t *cmcf; @@ -216,178 +219,196 @@ ngx_mail_block(ngx_conf_t *cf, ngx_comma *cf = pcf; - if (ngx_array_init(&in_ports, cf->temp_pool, 4, - sizeof(ngx_mail_conf_in_port_t)) + if (ngx_array_init(&ports, cf->temp_pool, 4, sizeof(ngx_mail_conf_port_t)) != NGX_OK) { return NGX_CONF_ERROR; } - imls = cmcf->listen.elts; - - for (l = 0; l < cmcf->listen.nelts; l++) { - - /* AF_INET only */ + listen = cmcf->listen.elts; - in_port = in_ports.elts; - for (p = 0; p < in_ports.nelts; p++) { - if (in_port[p].port == imls[l].port) { - in_port = &in_port[p]; - goto found; - } - } - - in_port = ngx_array_push(&in_ports); - if (in_port == NULL) { - return NGX_CONF_ERROR; - } - - in_port->port = imls[l].port; - - if (ngx_array_init(&in_port->addrs, cf->temp_pool, 2, - sizeof(ngx_mail_conf_in_addr_t)) - != NGX_OK) - { + for (i = 0; i < cmcf->listen.nelts; i++) { + if (ngx_mail_add_ports(cf, &ports, &listen[i]) != NGX_OK) { return NGX_CONF_ERROR; } + } - found: + 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; + struct sockaddr_in *sin; + ngx_mail_conf_port_t *port; + ngx_mail_conf_addr_t *addr; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + sa = (struct sockaddr *) &listen->sockaddr; - in_addr = ngx_array_push(&in_port->addrs); - if (in_addr == NULL) { - return NGX_CONF_ERROR; + switch (sa->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) sa; + p = sin6->sin6_port; + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) sa; + p = sin->sin_port; + break; + } + + 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; } - - in_addr->addr = imls[l].addr; - in_addr->ctx = imls[l].ctx; - in_addr->bind = imls[l].bind; -#if (NGX_MAIL_SSL) - in_addr->ssl = imls[l].ssl; -#endif } - /* optimize the lists of ports and addresses */ + /* 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; - /* AF_INET only */ + 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; + } - in_port = in_ports.elts; - for (p = 0; p < in_ports.nelts; p++) { + addr->sockaddr = (struct sockaddr *) &listen->sockaddr; + addr->socklen = listen->socklen; + addr->ctx = listen->ctx; + addr->bind = listen->bind; + addr->wildcard = listen->wildcard; +#if (NGX_MAIL_SSL) + addr->ssl = listen->ssl; +#endif +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + addr->ipv6only = listen->ipv6only; +#endif + + return NGX_OK; +} + - ngx_sort(in_port[p].addrs.elts, (size_t) in_port[p].addrs.nelts, - sizeof(ngx_mail_conf_in_addr_t), ngx_mail_cmp_conf_in_addrs); +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; - in_addr = in_port[p].addrs.elts; - last = in_port[p].addrs.nelts; + 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 (in_addr[last - 1].addr == INADDR_ANY) { - in_addr[last - 1].bind = 1; - bind_all = 0; + if (addr[last - 1].wildcard) { + addr[last - 1].bind = 1; + bind_wildcard = 1; } else { - bind_all = 1; + bind_wildcard = 0; } - for (a = 0; a < last; /* void */ ) { + i = 0; - if (!bind_all && !in_addr[a].bind) { - a++; + while (i < last) { + + if (bind_wildcard && !addr[i].bind) { + i++; continue; } - ls = ngx_listening_inet_stream_socket(cf, in_addr[a].addr, - in_port[p].port); + ls = ngx_create_listening(cf, addr[i].sockaddr, addr[i].socklen); if (ls == NULL) { return NGX_CONF_ERROR; } - ls->backlog = NGX_LISTEN_BACKLOG; - ls->rcvbuf = -1; - ls->sndbuf = -1; - ls->addr_ntop = 1; ls->handler = ngx_mail_init_connection; ls->pool_size = 256; - /* STUB */ - ls->log = *cf->cycle->new_log; + /* TODO: error_log directive */ + ls->logp = &cf->cycle->new_log; ls->log.data = &ls->addr_text; ls->log.handler = ngx_accept_log_error; - /**/ - imip = ngx_palloc(cf->pool, sizeof(ngx_mail_in_port_t)); - if (imip == NULL) { +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + ls->ipv6only = addr[i].ipv6only; +#endif + + mport = ngx_palloc(cf->pool, sizeof(ngx_mail_port_t)); + if (mport == NULL) { return NGX_CONF_ERROR; } - ls->servers = imip; - - in_addr = in_port[p].addrs.elts; + ls->servers = mport; - if (in_addr[a].bind && in_addr[a].addr != INADDR_ANY) { - imip->naddrs = 1; - done = 0; - - } else if (in_port[p].addrs.nelts > 1 - && in_addr[last - 1].addr == INADDR_ANY) - { - imip->naddrs = last; - done = 1; + if (i == last - 1) { + mport->naddrs = last; } else { - imip->naddrs = 1; - done = 0; + mport->naddrs = 1; + i = 0; } -#if 0 - ngx_log_error(NGX_LOG_ALERT, cf->log, 0, - "%ui: %V %d %ui %ui", - a, &ls->addr_text, in_addr[a].bind, - imip->naddrs, last); -#endif - - imip->addrs = ngx_pcalloc(cf->pool, - imip->naddrs * sizeof(ngx_mail_in_addr_t)); - if (imip->addrs == NULL) { - return NGX_CONF_ERROR; - } - - for (i = 0; i < imip->naddrs; i++) { - imip->addrs[i].addr = in_addr[i].addr; - imip->addrs[i].ctx = in_addr[i].ctx; - - text = ngx_pnalloc(cf->pool, - NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1); - if (text == NULL) { + 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; } - - len = ngx_inet_ntop(AF_INET, &in_addr[i].addr, text, - NGX_INET_ADDRSTRLEN); - - len = ngx_sprintf(text + len, ":%d", in_port[p].port) - text; - - imip->addrs[i].addr_text.len = len; - imip->addrs[i].addr_text.data = text; - -#if (NGX_MAIL_SSL) - imip->addrs[i].ssl = in_addr[i].ssl; + break; #endif - } - - if (done) { + default: /* AF_INET */ + if (ngx_mail_add_addrs(cf, mport, addr) != NGX_OK) { + return NGX_CONF_ERROR; + } break; } - in_addr++; - in_port[p].addrs.elts = in_addr; + addr++; last--; - - a = 0; } } @@ -396,15 +417,111 @@ ngx_mail_block(ngx_conf_t *cf, ngx_comma static ngx_int_t -ngx_mail_cmp_conf_in_addrs(const void *one, const void *two) +ngx_mail_add_addrs(ngx_conf_t *cf, ngx_mail_port_t *mport, + ngx_mail_conf_addr_t *addr) { - ngx_mail_conf_in_addr_t *first, *second; + u_char *p; + size_t len; + ngx_uint_t i; + ngx_mail_in_addr_t *addrs; + struct sockaddr_in *sin; + u_char buf[NGX_SOCKADDR_STRLEN]; + + 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].sockaddr; + addrs[i].addr = sin->sin_addr.s_addr; + + addrs[i].conf.ctx = addr[i].ctx; +#if (NGX_MAIL_SSL) + addrs[i].conf.ssl = addr[i].ssl; +#endif + + len = ngx_sock_ntop(addr[i].sockaddr, buf, NGX_SOCKADDR_STRLEN, 1); + + p = ngx_pnalloc(cf->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(p, buf, len); + + addrs[i].conf.addr_text.len = len; + addrs[i].conf.addr_text.data = p; + } + + return NGX_OK; +} + + +#if (NGX_HAVE_INET6) - first = (ngx_mail_conf_in_addr_t *) one; - second = (ngx_mail_conf_in_addr_t *) two; +static ngx_int_t +ngx_mail_add_addrs6(ngx_conf_t *cf, ngx_mail_port_t *mport, + ngx_mail_conf_addr_t *addr) +{ + u_char *p; + size_t len; + ngx_uint_t i; + ngx_mail_in6_addr_t *addrs6; + struct sockaddr_in6 *sin6; + u_char buf[NGX_SOCKADDR_STRLEN]; + + 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].sockaddr; + addrs6[i].addr6 = sin6->sin6_addr; + + addrs6[i].conf.ctx = addr[i].ctx; +#if (NGX_MAIL_SSL) + addrs6[i].conf.ssl = addr[i].ssl; +#endif - if (first->addr == INADDR_ANY) { - /* the INADDR_ANY must be the last resort, shift it to the end */ + len = ngx_sock_ntop(addr[i].sockaddr, buf, NGX_SOCKADDR_STRLEN, 1); + + p = ngx_pnalloc(cf->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(p, buf, len); + + addrs6[i].conf.addr_text.len = len; + addrs6[i].conf.addr_text.data = p; + } + + 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->wildcard) { + /* a wildcard must be the last resort, shift it to the end */ return 1; } diff --git a/src/mail/ngx_mail.h b/src/mail/ngx_mail.h --- a/src/mail/ngx_mail.h +++ b/src/mail/ngx_mail.h @@ -26,50 +26,76 @@ typedef struct { typedef struct { - in_addr_t addr; - in_port_t port; - int family; + u_char sockaddr[NGX_SOCKADDRLEN]; + socklen_t socklen; /* server ctx */ ngx_mail_conf_ctx_t *ctx; unsigned bind:1; + unsigned wildcard:1; #if (NGX_MAIL_SSL) unsigned ssl:1; #endif +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + unsigned ipv6only:2; +#endif } ngx_mail_listen_t; typedef struct { - in_addr_t addr; ngx_mail_conf_ctx_t *ctx; ngx_str_t addr_text; #if (NGX_MAIL_SSL) ngx_uint_t ssl; /* unsigned ssl:1; */ #endif +} 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 { - ngx_mail_in_addr_t *addrs; /* array of ngx_mail_in_addr_t */ - ngx_uint_t naddrs; -} ngx_mail_in_port_t; + 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_in_addr_t */ -} ngx_mail_conf_in_port_t; + ngx_array_t addrs; /* array of ngx_mail_conf_addr_t */ +} ngx_mail_conf_port_t; typedef struct { - in_addr_t addr; + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_mail_conf_ctx_t *ctx; + unsigned bind:1; + unsigned wildcard:1; #if (NGX_MAIL_SSL) unsigned ssl:1; #endif -} ngx_mail_conf_in_addr_t; +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + unsigned ipv6only:2; +#endif +} ngx_mail_conf_addr_t; typedef struct { @@ -136,8 +162,13 @@ typedef enum { ngx_smtp_auth_plain, ngx_smtp_auth_cram_md5, ngx_smtp_helo, - ngx_smtp_noxclient, - ngx_smtp_xclient + 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; @@ -173,7 +204,7 @@ typedef struct { unsigned no_sync_literal:1; unsigned starttls:1; unsigned esmtp:1; - unsigned auth_method:2; + unsigned auth_method:3; unsigned auth_wait:1; ngx_str_t login; @@ -187,6 +218,8 @@ typedef struct { 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_uint_t command; ngx_array_t args; @@ -252,16 +285,19 @@ typedef struct { #define NGX_SMTP_STARTTLS 13 -#define NGX_MAIL_AUTH_PLAIN 0 -#define NGX_MAIL_AUTH_LOGIN 1 -#define NGX_MAIL_AUTH_APOP 2 -#define NGX_MAIL_AUTH_CRAM_MD5 3 +#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_NONE 5 #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_NONE_ENABLED 0x0020 #define NGX_MAIL_PARSE_INVALID_COMMAND 20 @@ -338,7 +374,7 @@ ngx_int_t ngx_mail_salt(ngx_mail_session 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_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, diff --git a/src/mail/ngx_mail_auth_http_module.c b/src/mail/ngx_mail_auth_http_module.c --- a/src/mail/ngx_mail_auth_http_module.c +++ b/src/mail/ngx_mail_auth_http_module.c @@ -40,7 +40,6 @@ struct ngx_mail_auth_http_ctx_s { ngx_mail_auth_http_handler_pt handler; ngx_uint_t state; - ngx_uint_t hash; /* no needed ? */ u_char *header_name_start; u_char *header_name_end; @@ -140,8 +139,10 @@ ngx_module_t ngx_mail_auth_http_module 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("cram-md5"), + ngx_string("none") }; static ngx_str_t ngx_mail_smtp_errcode = ngx_string("535 5.7.0"); @@ -268,7 +269,7 @@ ngx_mail_auth_http_write_handler(ngx_eve ngx_del_timer(wev); } - if (ngx_handle_write_event(wev, 0) == NGX_ERROR) { + 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); @@ -770,6 +771,8 @@ ngx_mail_auth_http_process_headers(ngx_m return; } + /* AF_INET only */ + sin = ngx_pcalloc(s->connection->pool, sizeof(struct sockaddr_in)); if (sin == NULL) { ngx_destroy_pool(ctx->pool); @@ -894,7 +897,7 @@ ngx_mail_auth_sleep_handler(ngx_event_t return; } - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_mail_close_connection(c); } @@ -902,7 +905,7 @@ ngx_mail_auth_sleep_handler(ngx_event_t } if (rev->active) { - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_mail_close_connection(c); } } @@ -914,7 +917,6 @@ ngx_mail_auth_http_parse_header_line(ngx ngx_mail_auth_http_ctx_t *ctx) { u_char c, ch, *p; - ngx_uint_t hash; enum { sw_start = 0, sw_name, @@ -926,7 +928,6 @@ ngx_mail_auth_http_parse_header_line(ngx } state; state = ctx->state; - hash = ctx->hash; for (p = ctx->response->pos; p < ctx->response->last; p++) { ch = *p; @@ -950,12 +951,10 @@ ngx_mail_auth_http_parse_header_line(ngx c = (u_char) (ch | 0x20); if (c >= 'a' && c <= 'z') { - hash = c; break; } if (ch >= '0' && ch <= '9') { - hash = ch; break; } @@ -967,7 +966,6 @@ ngx_mail_auth_http_parse_header_line(ngx case sw_name: c = (u_char) (ch | 0x20); if (c >= 'a' && c <= 'z') { - hash += c; break; } @@ -978,12 +976,10 @@ ngx_mail_auth_http_parse_header_line(ngx } if (ch == '-') { - hash += ch; break; } if (ch >= '0' && ch <= '9') { - hash += ch; break; } @@ -1080,7 +1076,6 @@ ngx_mail_auth_http_parse_header_line(ngx ctx->response->pos = p; ctx->state = state; - ctx->hash = hash; return NGX_AGAIN; @@ -1088,7 +1083,6 @@ done: ctx->response->pos = p + 1; ctx->state = sw_start; - ctx->hash = hash; return NGX_OK; @@ -1111,7 +1105,7 @@ ngx_mail_auth_http_block_read(ngx_event_ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail auth http block read"); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { c = rev->data; s = c->data; @@ -1165,6 +1159,10 @@ ngx_mail_auth_http_create_request(ngx_ma + 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("Auth-SMTP-From: ") - 1 + s->smtp_from.len + + sizeof("Auth-SMTP-To: ") - 1 + s->smtp_to.len + ahcf->header.len + sizeof(CRLF) - 1; @@ -1216,9 +1214,37 @@ ngx_mail_auth_http_create_request(ngx_ma 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); + 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 (ahcf->header.len) { b->last = ngx_copy(b->last, ahcf->header.data, ahcf->header.len); } @@ -1276,7 +1302,7 @@ ngx_mail_auth_http_create_conf(ngx_conf_ ahcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_auth_http_conf_t)); if (ahcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } ahcf->timeout = NGX_CONF_UNSET_MSEC; diff --git a/src/mail/ngx_mail_core_module.c b/src/mail/ngx_mail_core_module.c --- a/src/mail/ngx_mail_core_module.c +++ b/src/mail/ngx_mail_core_module.c @@ -120,20 +120,20 @@ ngx_mail_core_create_main_conf(ngx_conf_ cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_core_main_conf_t)); if (cmcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } if (ngx_array_init(&cmcf->servers, cf->pool, 4, sizeof(ngx_mail_core_srv_conf_t *)) != NGX_OK) { - return NGX_CONF_ERROR; + return NULL; } if (ngx_array_init(&cmcf->listen, cf->pool, 4, sizeof(ngx_mail_listen_t)) != NGX_OK) { - return NGX_CONF_ERROR; + return NULL; } return cmcf; @@ -274,19 +274,24 @@ ngx_mail_core_server(ngx_conf_t *cf, ngx } -/* AF_INET only */ - static char * ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_mail_core_srv_conf_t *cscf = conf; + size_t len, off; + in_port_t port; ngx_str_t *value; ngx_url_t u; ngx_uint_t i, m; - ngx_mail_listen_t *imls; + struct sockaddr *sa; + ngx_mail_listen_t *ls; ngx_mail_module_t *module; + struct sockaddr_in *sin; ngx_mail_core_main_conf_t *cmcf; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif value = cf->args->elts; @@ -307,11 +312,40 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx cmcf = ngx_mail_conf_get_module_main_conf(cf, ngx_mail_core_module); - imls = cmcf->listen.elts; + ls = cmcf->listen.elts; for (i = 0; i < cmcf->listen.nelts; i++) { - if (imls[i].addr != u.addr.in_addr || imls[i].port != u.port) { + sa = (struct sockaddr *) ls[i].sockaddr; + + if (sa->sa_family != u.family) { + continue; + } + + switch (sa->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + off = offsetof(struct sockaddr_in6, sin6_addr); + len = 16; + sin6 = (struct sockaddr_in6 *) sa; + port = sin6->sin6_port; + break; +#endif + + default: /* AF_INET */ + off = offsetof(struct sockaddr_in, sin_addr); + len = 4; + sin = (struct sockaddr_in *) sa; + port = sin->sin_port; + break; + } + + if (ngx_memcmp(ls[i].sockaddr + off, u.sockaddr + off, len) != 0) { + continue; + } + + if (port != u.port) { continue; } @@ -320,17 +354,18 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx return NGX_CONF_ERROR; } - imls = ngx_array_push(&cmcf->listen); - if (imls == NULL) { + ls = ngx_array_push(&cmcf->listen); + if (ls == NULL) { return NGX_CONF_ERROR; } - ngx_memzero(imls, sizeof(ngx_mail_listen_t)); + ngx_memzero(ls, sizeof(ngx_mail_listen_t)); + + ngx_memcpy(ls->sockaddr, u.sockaddr, u.socklen); - imls->addr = u.addr.in_addr; - imls->port = u.port; - imls->family = u.family; - imls->ctx = cf->ctx; + ls->socklen = u.socklen; + ls->wildcard = u.wildcard; + ls->ctx = cf->ctx; for (m = 0; ngx_modules[m]; m++) { if (ngx_modules[m]->type != NGX_MAIL_MODULE) { @@ -354,13 +389,54 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx for (i = 2; i < cf->args->nelts; i++) { if (ngx_strcmp(value[i].data, "bind") == 0) { - imls->bind = 1; + ls->bind = 1; continue; } + if (ngx_strncmp(value[i].data, "ipv6only=o", 10) == 0) { +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + struct sockaddr *sa; + u_char buf[NGX_SOCKADDR_STRLEN]; + + sa = (struct sockaddr *) ls->sockaddr; + + if (sa->sa_family == AF_INET6) { + + if (ngx_strcmp(&value[i].data[10], "n") == 0) { + ls->ipv6only = 1; + + } else if (ngx_strcmp(&value[i].data[10], "ff") == 0) { + ls->ipv6only = 2; + + } 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; + + } else { + len = ngx_sock_ntop(sa, buf, NGX_SOCKADDR_STRLEN, 1); + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "ipv6only is not supported " + "on addr \"%*s\", ignored", len, buf); + } + + 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) - imls->ssl = 1; + ls->ssl = 1; continue; #else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, diff --git a/src/mail/ngx_mail_handler.c b/src/mail/ngx_mail_handler.c --- a/src/mail/ngx_mail_handler.c +++ b/src/mail/ngx_mail_handler.c @@ -21,25 +21,27 @@ static void ngx_mail_ssl_handshake_handl void ngx_mail_init_connection(ngx_connection_t *c) { - in_addr_t in_addr; - socklen_t len; - ngx_uint_t i; - struct sockaddr_in sin; - ngx_mail_log_ctx_t *ctx; - ngx_mail_in_port_t *imip; - ngx_mail_in_addr_t *imia; - ngx_mail_session_t *s; + 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; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; + ngx_mail_in6_addr_t *addr6; +#endif + /* find the server configuration for the address:port */ /* AF_INET only */ - imip = c->listening->servers; - imia = imip->addrs; + port = c->listening->servers; - i = 0; - - if (imip->naddrs > 1) { + if (port->naddrs > 1) { /* * There are several addresses on this port and one of them @@ -49,45 +51,79 @@ ngx_mail_init_connection(ngx_connection_ * AcceptEx() already gave this address. */ -#if (NGX_WIN32) - if (c->local_sockaddr) { - in_addr = - ((struct sockaddr_in *) c->local_sockaddr)->sin_addr.s_addr; + if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { + ngx_mail_close_connection(c); + return; + } + + sa = c->local_sockaddr; + + switch (sa->sa_family) { - } else -#endif - { - len = sizeof(struct sockaddr_in); - if (getsockname(c->fd, (struct sockaddr *) &sin, &len) == -1) { - ngx_connection_error(c, ngx_socket_errno, - "getsockname() failed"); - ngx_mail_close_connection(c); - return; +#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; + } } - in_addr = sin.sin_addr.s_addr; + 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; } - /* the last address is "*" */ + } else { + switch (c->local_sockaddr->sa_family) { - for ( /* void */ ; i < imip->naddrs - 1; i++) { - if (in_addr == imia[i].addr) { - break; - } +#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->main_conf = imia[i].ctx->main_conf; - s->srv_conf = imia[i].ctx->srv_conf; + s->main_conf = addr_conf->ctx->main_conf; + s->srv_conf = addr_conf->ctx->srv_conf; - s->addr_text = &imia[i].addr_text; + s->addr_text = &addr_conf->addr_text; c->data = s; s->connection = c; @@ -124,7 +160,7 @@ ngx_mail_init_connection(ngx_connection_ return; } - if (imia[i].ssl) { + if (addr_conf->ssl) { c->log->action = "SSL handshaking"; @@ -356,21 +392,22 @@ ngx_mail_auth_plain(ngx_mail_session_t * ngx_int_t -ngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c) +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[0]); + "mail auth login username: \"%V\"", &arg[n]); - s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len)); + 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[0]) != NGX_OK) { + 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; @@ -513,7 +550,7 @@ ngx_mail_send(ngx_event_t *wev) } if (s->out.len == 0) { - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { ngx_mail_close_connection(c); } @@ -552,7 +589,7 @@ ngx_mail_send(ngx_event_t *wev) ngx_add_timer(c->write, cscf->timeout); - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { ngx_mail_close_connection(c); return; } @@ -579,7 +616,7 @@ ngx_mail_read_command(ngx_mail_session_t } if (n == NGX_AGAIN) { - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_session_internal_server_error(s); return NGX_ERROR; } @@ -673,7 +710,7 @@ ngx_mail_close_connection(ngx_connection #endif #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_active, -1); + (void) ngx_atomic_fetch_add(ngx_stat_active, -1); #endif c->destroyed = 1; diff --git a/src/mail/ngx_mail_imap_handler.c b/src/mail/ngx_mail_imap_handler.c --- a/src/mail/ngx_mail_imap_handler.c +++ b/src/mail/ngx_mail_imap_handler.c @@ -46,7 +46,7 @@ ngx_mail_imap_init_session(ngx_mail_sess ngx_add_timer(c->read, cscf->timeout); - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_close_connection(c); } @@ -205,7 +205,7 @@ ngx_mail_imap_auth_state(ngx_event_t *re break; case ngx_imap_auth_login_username: - rc = ngx_mail_auth_login_username(s, c); + rc = ngx_mail_auth_login_username(s, c, 0); tag = 0; s->out.len = sizeof(imap_password) - 1; @@ -370,6 +370,14 @@ ngx_mail_imap_authenticate(ngx_mail_sess return NGX_OK; + case NGX_MAIL_AUTH_LOGIN_USERNAME: + + s->out.len = sizeof(imap_password) - 1; + s->out.data = imap_password; + s->mail_state = ngx_imap_auth_login_password; + + return ngx_mail_auth_login_username(s, c, 1); + case NGX_MAIL_AUTH_PLAIN: s->out.len = sizeof(imap_plain_next) - 1; diff --git a/src/mail/ngx_mail_imap_module.c b/src/mail/ngx_mail_imap_module.c --- a/src/mail/ngx_mail_imap_module.c +++ b/src/mail/ngx_mail_imap_module.c @@ -36,7 +36,8 @@ static ngx_str_t ngx_mail_imap_auth_met ngx_string("AUTH=PLAIN"), ngx_string("AUTH=LOGIN"), ngx_null_string, /* APOP */ - ngx_string("AUTH=CRAM-MD5") + ngx_string("AUTH=CRAM-MD5"), + ngx_null_string /* NONE */ }; diff --git a/src/mail/ngx_mail_parse.c b/src/mail/ngx_mail_parse.c --- a/src/mail/ngx_mail_parse.c +++ b/src/mail/ngx_mail_parse.c @@ -746,7 +746,7 @@ ngx_mail_smtp_parse_command(ngx_mail_ses s->arg_end = p; goto done; default: - if (s->args.nelts <= 2) { + if (s->args.nelts <= 10) { state = sw_argument; s->arg_start = p; break; @@ -848,6 +848,10 @@ ngx_mail_auth_parse(ngx_mail_session_t * return NGX_MAIL_AUTH_LOGIN; } + if (s->args.nelts == 2) { + return NGX_MAIL_AUTH_LOGIN_USERNAME; + } + return NGX_MAIL_PARSE_INVALID_COMMAND; } diff --git a/src/mail/ngx_mail_pop3_handler.c b/src/mail/ngx_mail_pop3_handler.c --- a/src/mail/ngx_mail_pop3_handler.c +++ b/src/mail/ngx_mail_pop3_handler.c @@ -67,7 +67,7 @@ ngx_mail_pop3_init_session(ngx_mail_sess ngx_add_timer(c->read, cscf->timeout); - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_close_connection(c); } @@ -226,7 +226,7 @@ ngx_mail_pop3_auth_state(ngx_event_t *re break; case ngx_pop3_auth_login_username: - rc = ngx_mail_auth_login_username(s, c); + rc = ngx_mail_auth_login_username(s, c, 0); s->out.len = sizeof(pop3_password) - 1; s->out.data = pop3_password; @@ -474,6 +474,14 @@ ngx_mail_pop3_auth(ngx_mail_session_t *s return NGX_OK; + case NGX_MAIL_AUTH_LOGIN_USERNAME: + + s->out.len = sizeof(pop3_password) - 1; + s->out.data = pop3_password; + s->mail_state = ngx_pop3_auth_login_password; + + return ngx_mail_auth_login_username(s, c, 1); + case NGX_MAIL_AUTH_PLAIN: s->out.len = sizeof(pop3_next) - 1; diff --git a/src/mail/ngx_mail_proxy_module.c b/src/mail/ngx_mail_proxy_module.c --- a/src/mail/ngx_mail_proxy_module.c +++ b/src/mail/ngx_mail_proxy_module.c @@ -104,7 +104,7 @@ ngx_module_t ngx_mail_proxy_module = { }; -static u_char smtp_ok[] = "235 2.0.0 OK" CRLF; +static u_char smtp_auth_ok[] = "235 2.0.0 OK" CRLF; void @@ -201,7 +201,7 @@ ngx_mail_proxy_block_read(ngx_event_t *r ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy block read"); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { c = rev->data; s = c->data; @@ -465,6 +465,7 @@ ngx_mail_proxy_smtp_handler(ngx_event_t 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; @@ -520,19 +521,26 @@ ngx_mail_proxy_smtp_handler(ngx_event_t p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len); *p++ = CR; *p = LF; - s->mail_state = pcf->xclient ? ngx_smtp_helo: ngx_smtp_noxclient; + 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: + 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 PROTO=SMTP HELO= ADDR= LOGIN= NAME=" + line.len = sizeof("XCLIENT ADDR= LOGIN= NAME=" CRLF) - 1 - + s->esmtp + s->smtp_helo.len + s->connection->addr_text.len + s->login.len + s->host.len; line.data = ngx_pnalloc(c->pool, line.len); @@ -541,30 +549,102 @@ ngx_mail_proxy_smtp_handler(ngx_event_t return; } + line.len = ngx_sprintf(line.data, + "XCLIENT ADDR=%V%s%V NAME=%V" CRLF, + &s->connection->addr_text, + (s->login.len ? " LOGIN=" : ""), &s->login, &s->host) + - line.data; + if (s->smtp_helo.len) { - line.len = ngx_sprintf(line.data, - "XCLIENT PROTO=%sSMTP HELO=%V ADDR=%V LOGIN=%V " - "NAME=%V" CRLF, - (s->esmtp ? "E" : ""), &s->smtp_helo, - &s->connection->addr_text, &s->login, &s->host) - - line.data; + s->mail_state = ngx_smtp_xclient_helo; + + } else if (s->auth_method == NGX_MAIL_AUTH_NONE) { + s->mail_state = ngx_smtp_xclient_from; + } else { - line.len = ngx_sprintf(line.data, - "XCLIENT PROTO=SMTP ADDR=%V LOGIN=%V NAME=%V" CRLF, - &s->connection->addr_text, &s->login, &s->host) - - line.data; + 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; } - s->mail_state = ngx_smtp_xclient; + 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_noxclient: - case ngx_smtp_xclient: + 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; - ngx_memcpy(s->proxy->buffer->start, smtp_ok, sizeof(smtp_ok) - 1); + s->mail_state = ngx_smtp_to; + + break; + + case ngx_smtp_helo: + case ngx_smtp_xclient: + case ngx_smtp_to: - s->proxy->buffer->pos = s->proxy->buffer->start; - s->proxy->buffer->last = s->proxy->buffer->start + sizeof(smtp_ok) - 1; + 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; @@ -612,7 +692,7 @@ ngx_mail_proxy_dummy_handler(ngx_event_t ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0, "mail proxy dummy handler"); - if (ngx_handle_write_event(wev, 0) == NGX_ERROR) { + if (ngx_handle_write_event(wev, 0) != NGX_OK) { c = wev->data; s = c->data; @@ -703,19 +783,31 @@ ngx_mail_proxy_read_response(ngx_mail_se default: /* NGX_MAIL_SMTP_PROTOCOL */ 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_noxclient: + 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_start: case ngx_smtp_xclient: - if (p[0] == '2' && p[1] == '2' && p[2] == '0') { + 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; @@ -884,22 +976,22 @@ ngx_mail_proxy_handler(ngx_event_t *ev) return; } - if (ngx_handle_write_event(dst->write, 0) == NGX_ERROR) { + 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_ERROR) { + 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_ERROR) { + 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_ERROR) { + if (ngx_handle_read_event(src->read, 0) != NGX_OK) { ngx_mail_proxy_close_session(s); return; } @@ -969,7 +1061,7 @@ ngx_mail_proxy_create_conf(ngx_conf_t *c pcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_proxy_conf_t)); if (pcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } pcf->enable = NGX_CONF_UNSET; diff --git a/src/mail/ngx_mail_smtp_handler.c b/src/mail/ngx_mail_smtp_handler.c --- a/src/mail/ngx_mail_smtp_handler.c +++ b/src/mail/ngx_mail_smtp_handler.c @@ -12,6 +12,7 @@ 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_greeting(ngx_mail_session_t *s, ngx_connection_t *c); static void ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev); @@ -23,6 +24,8 @@ static ngx_int_t ngx_mail_smtp_auth(ngx_ 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); @@ -41,6 +44,7 @@ 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]"); @@ -62,6 +66,12 @@ ngx_mail_smtp_init_session(ngx_mail_sess return; } + if (c->sockaddr->sa_family != AF_INET) { + s->host = smtp_tempunavail; + ngx_mail_smtp_greeting(s, c); + return; + } + c->log->action = "in resolving client address"; ctx = ngx_resolve_start(cscf->resolver, NULL); @@ -88,9 +98,8 @@ ngx_mail_smtp_init_session(ngx_mail_sess static void ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx) { - ngx_connection_t *c; - ngx_mail_session_t *s; - ngx_mail_core_srv_conf_t *cscf; + ngx_connection_t *c; + ngx_mail_session_t *s; s = ctx->data; c = s->connection; @@ -131,6 +140,23 @@ ngx_mail_smtp_resolve_addr_handler(ngx_r 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); @@ -225,7 +251,7 @@ ngx_mail_smtp_greeting(ngx_mail_session_ timeout = sscf->greeting_delay ? sscf->greeting_delay : cscf->timeout; ngx_add_timer(c->read, timeout); - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_close_connection(c); } @@ -267,7 +293,7 @@ ngx_mail_smtp_invalid_pipelining(ngx_eve ngx_add_timer(c->read, cscf->timeout); - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_close_connection(c); return; } @@ -417,8 +443,15 @@ ngx_mail_smtp_auth_state(ngx_event_t *re 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: - case NGX_SMTP_RSET: break; case NGX_SMTP_STARTTLS: @@ -435,7 +468,7 @@ ngx_mail_smtp_auth_state(ngx_event_t *re break; case ngx_smtp_auth_login_username: - rc = ngx_mail_auth_login_username(s, c); + rc = ngx_mail_auth_login_username(s, c, 0); s->out.len = sizeof(smtp_password) - 1; s->out.data = smtp_password; @@ -513,6 +546,11 @@ ngx_mail_smtp_helo(ngx_mail_session_t *s ngx_memcpy(s->smtp_helo.data, arg[0].data, arg[0].len); + s->smtp_from.len = 0; + s->smtp_from.data = NULL; + s->smtp_to.len = 0; + s->smtp_to.data = NULL; + sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); if (s->command == NGX_SMTP_HELO) { @@ -579,6 +617,14 @@ ngx_mail_smtp_auth(ngx_mail_session_t *s return NGX_OK; + case NGX_MAIL_AUTH_LOGIN_USERNAME: + + s->out.len = sizeof(smtp_password) - 1; + s->out.data = smtp_password; + s->mail_state = ngx_smtp_auth_login_password; + + return ngx_mail_auth_login_username(s, c, 1); + case NGX_MAIL_AUTH_PLAIN: s->out.len = sizeof(smtp_next) - 1; @@ -618,10 +664,136 @@ ngx_mail_smtp_auth(ngx_mail_session_t *s static ngx_int_t ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c) { - ngx_mail_smtp_log_rejected_command(s, c, "client was rejected: \"%V\""); + u_char ch; + ngx_str_t l; + ngx_uint_t i; + 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\""); + + s->out.len = sizeof(smtp_auth_required) - 1; + s->out.data = smtp_auth_required; + + return NGX_OK; + } + + /* auth none */ + + if (s->smtp_from.len) { + s->out.len = sizeof(smtp_bad_sequence) - 1; + s->out.data = smtp_bad_sequence; + return NGX_OK; + } + + l.len = s->buffer->last - s->buffer->start; + l.data = s->buffer->start; + + for (i = 0; i < l.len; i++) { + ch = l.data[i]; + + if (ch != CR && ch != LF) { + continue; + } + + l.data[i] = ' '; + } + + while (i) { + if (l.data[i - 1] != ' ') { + break; + } + + i--; + } + + l.len = i; + + s->smtp_from.len = l.len; + + s->smtp_from.data = ngx_pnalloc(c->pool, l.len); + if (s->smtp_from.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->smtp_from.data, l.data, l.len); + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "smtp mail from:\"%V\"", &s->smtp_from); + + s->out.len = sizeof(smtp_ok) - 1; + s->out.data = smtp_ok; + + return NGX_OK; +} - s->out.len = sizeof(smtp_auth_required) - 1; - s->out.data = smtp_auth_required; + +static ngx_int_t +ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c) +{ + u_char ch; + ngx_str_t l; + ngx_uint_t i; + + if (s->smtp_from.len == 0) { + s->out.len = sizeof(smtp_bad_sequence) - 1; + s->out.data = smtp_bad_sequence; + return NGX_OK; + } + + l.len = s->buffer->last - s->buffer->start; + l.data = s->buffer->start; + + for (i = 0; i < l.len; i++) { + ch = l.data[i]; + + if (ch != CR && ch != LF) { + continue; + } + + l.data[i] = ' '; + } + + while (i) { + if (l.data[i - 1] != ' ') { + break; + } + + i--; + } + + l.len = i; + + s->smtp_to.len = l.len; + + s->smtp_to.data = ngx_pnalloc(c->pool, l.len); + if (s->smtp_to.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->smtp_to.data, l.data, l.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) +{ + s->smtp_from.len = 0; + s->smtp_from.data = NULL; + s->smtp_to.len = 0; + s->smtp_to.data = NULL; + + s->out.len = sizeof(smtp_ok) - 1; + s->out.data = smtp_ok; return NGX_OK; } @@ -644,6 +816,10 @@ ngx_mail_smtp_starttls(ngx_mail_session_ s->smtp_helo.len = 0; s->smtp_helo.data = NULL; + s->smtp_from.len = 0; + s->smtp_from.data = NULL; + s->smtp_to.len = 0; + s->smtp_to.data = NULL; c->read->handler = ngx_mail_starttls_handler; return NGX_OK; @@ -674,7 +850,7 @@ ngx_mail_smtp_discard_command(ngx_mail_s } if (n == NGX_AGAIN) { - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_session_internal_server_error(s); return NGX_ERROR; } diff --git a/src/mail/ngx_mail_smtp_module.c b/src/mail/ngx_mail_smtp_module.c --- a/src/mail/ngx_mail_smtp_module.c +++ b/src/mail/ngx_mail_smtp_module.c @@ -20,6 +20,7 @@ static ngx_conf_bitmask_t ngx_mail_smtp { 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("none"), NGX_MAIL_AUTH_NONE_ENABLED }, { ngx_null_string, 0 } }; @@ -28,7 +29,8 @@ static ngx_str_t ngx_mail_smtp_auth_met ngx_string("PLAIN"), ngx_string("LOGIN"), ngx_null_string, /* APOP */ - ngx_string("CRAM-MD5") + ngx_string("CRAM-MD5"), + ngx_null_string /* NONE */ }; @@ -136,10 +138,10 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t ngx_mail_smtp_srv_conf_t *prev = parent; ngx_mail_smtp_srv_conf_t *conf = child; - u_char *p, *auth; + u_char *p, *auth, *last; size_t size; ngx_str_t *c; - ngx_uint_t i, m; + ngx_uint_t i, m, auth_enabled; ngx_mail_core_srv_conf_t *cscf; ngx_conf_merge_size_value(conf->client_buffer_size, @@ -192,23 +194,29 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t conf->capabilities = prev->capabilities; } - size = sizeof("250-") - 1 + cscf->server_name.len + sizeof(CRLF) - 1 - + sizeof("250 AUTH") - 1 + sizeof(CRLF) - 1; + 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_CRAM_MD5_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; @@ -217,11 +225,14 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 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; @@ -229,21 +240,28 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t auth = p; - *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' '; - *p++ = 'A'; *p++ = 'U'; *p++ = 'T'; *p++ = 'H'; + 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_CRAM_MD5_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); + for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; + m <= NGX_MAIL_AUTH_CRAM_MD5_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; - *p++ = CR; *p = LF; + } else { + last[3] = ' '; + } size += sizeof("250 STARTTLS" CRLF) - 1; @@ -255,14 +273,13 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t conf->starttls_capability.len = size; conf->starttls_capability.data = p; - p = ngx_cpymem(p, conf->capability.data, - conf->capability.len); + p = ngx_cpymem(p, conf->capability.data, conf->capability.len); p = ngx_cpymem(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1); *p++ = CR; *p = LF; p = conf->starttls_capability.data - + (auth - conf->capability.data) + 3; + + (last - conf->capability.data) + 3; *p = '-'; size = (auth - conf->capability.data) @@ -276,10 +293,15 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 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, 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/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c --- a/src/mail/ngx_mail_ssl_module.c +++ b/src/mail/ngx_mail_ssl_module.c @@ -166,7 +166,7 @@ ngx_mail_ssl_create_conf(ngx_conf_t *cf) scf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_ssl_conf_t)); if (scf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* @@ -182,7 +182,7 @@ ngx_mail_ssl_create_conf(ngx_conf_t *cf) */ scf->enable = NGX_CONF_UNSET; - scf->starttls = NGX_CONF_UNSET; + scf->starttls = NGX_CONF_UNSET_UINT; scf->prefer_server_ciphers = NGX_CONF_UNSET; scf->builtin_session_cache = NGX_CONF_UNSET; scf->session_timeout = NGX_CONF_UNSET; diff --git a/src/os/unix/ngx_darwin_config.h b/src/os/unix/ngx_darwin_config.h --- a/src/os/unix/ngx_darwin_config.h +++ b/src/os/unix/ngx_darwin_config.h @@ -24,6 +24,7 @@ #include #include #include +#include /* statfs() */ #include /* FIONBIO */ #include diff --git a/src/os/unix/ngx_darwin_init.c b/src/os/unix/ngx_darwin_init.c --- a/src/os/unix/ngx_darwin_init.c +++ b/src/os/unix/ngx_darwin_init.c @@ -64,32 +64,44 @@ ngx_os_specific_init(ngx_log_t *log) ngx_uint_t i; size = sizeof(ngx_darwin_kern_ostype); - if (sysctlbyname("kern.ostype", - ngx_darwin_kern_ostype, &size, NULL, 0) == -1) { - ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, - "sysctlbyname(kern.ostype) failed"); + if (sysctlbyname("kern.ostype", ngx_darwin_kern_ostype, &size, NULL, 0) + == -1) + { + err = ngx_errno; + + if (err != NGX_ENOENT) { - if (ngx_errno != NGX_ENOMEM) { - return NGX_ERROR; + 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'; } - - 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) { - ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, - "sysctlbyname(kern.osrelease) failed"); + if (sysctlbyname("kern.osrelease", ngx_darwin_kern_osrelease, &size, + NULL, 0) + == -1) + { + err = ngx_errno; + + if (err != NGX_ENOENT) { - if (ngx_errno != NGX_ENOMEM) { - return NGX_ERROR; - } + ngx_log_error(NGX_LOG_ALERT, log, err, + "sysctlbyname(kern.osrelease) failed"); - ngx_darwin_kern_osrelease[size - 1] = '\0'; + 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; @@ -136,8 +148,10 @@ ngx_os_specific_status(ngx_log_t *log) u_long value; ngx_uint_t i; - ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s", - ngx_darwin_kern_ostype, ngx_darwin_kern_osrelease); + 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) { diff --git a/src/os/unix/ngx_errno.c b/src/os/unix/ngx_errno.c --- a/src/os/unix/ngx_errno.c +++ b/src/os/unix/ngx_errno.c @@ -10,10 +10,11 @@ #if (NGX_HAVE_STRERROR_R) -u_char *ngx_strerror_r(int err, u_char *errstr, size_t size) +u_char * +ngx_strerror_r(int err, u_char *errstr, size_t size) { if (size == 0) { - return 0; + return errstr; } errstr[0] = '\0'; @@ -32,12 +33,13 @@ u_char *ngx_strerror_r(int err, u_char * /* Linux strerror_r() */ -u_char *ngx_strerror_r(int err, u_char *errstr, size_t size) +u_char * +ngx_strerror_r(int err, u_char *errstr, size_t size) { char *str; if (size == 0) { - return 0; + return errstr; } errstr[0] = '\0'; diff --git a/src/os/unix/ngx_errno.h b/src/os/unix/ngx_errno.h --- a/src/os/unix/ngx_errno.h +++ b/src/os/unix/ngx_errno.h @@ -23,6 +23,7 @@ typedef int ngx_err_t; #define NGX_EACCES EACCES #define NGX_EBUSY EBUSY #define NGX_EEXIST EEXIST +#define NGX_EXDEV EXDEV #define NGX_ENOTDIR ENOTDIR #define NGX_EISDIR EISDIR #define NGX_EINVAL EINVAL @@ -43,6 +44,7 @@ typedef int ngx_err_t; #define NGX_EHOSTUNREACH EHOSTUNREACH #define NGX_ENOSYS ENOSYS #define NGX_ECANCELED ECANCELED +#define NGX_EILSEQ EILSEQ #define NGX_ENOMOREFILES 0 diff --git a/src/os/unix/ngx_files.c b/src/os/unix/ngx_files.c --- a/src/os/unix/ngx_files.c +++ b/src/os/unix/ngx_files.c @@ -249,11 +249,6 @@ ngx_open_dir(ngx_str_t *name, ngx_dir_t } dir->valid_info = 0; -#if (NGX_HAVE_D_TYPE) - dir->valid_type = 1; -#else - dir->valid_type = 0; -#endif return NGX_OK; } @@ -267,6 +262,9 @@ ngx_read_dir(ngx_dir_t *dir) if (dir->de) { #if (NGX_HAVE_D_TYPE) dir->type = dir->de->d_type; + dir->valid_type = dir->type ? 1 : 0; +#else + dir->valid_type = 0; #endif return NGX_OK; } @@ -418,3 +416,50 @@ ngx_directio_off(ngx_fd_t fd) } #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/os/unix/ngx_files.h b/src/os/unix/ngx_files.h --- a/src/os/unix/ngx_files.h +++ b/src/os/unix/ngx_files.h @@ -18,13 +18,13 @@ typedef ino_t ngx_fil typedef struct { - DIR *dir; - struct dirent *de; - struct stat info; + DIR *dir; + struct dirent *de; + struct stat info; - unsigned type:8; - unsigned valid_info:1; - unsigned valid_type:1; + unsigned type:8; + unsigned valid_info:1; + unsigned valid_type:1; } ngx_dir_t; @@ -63,10 +63,11 @@ typedef struct { #define NGX_FILE_RDWR O_RDWR #define NGX_FILE_CREATE_OR_OPEN O_CREAT #define NGX_FILE_OPEN 0 -#define NGX_FILE_TRUNCATE O_TRUNC -#define NGX_FILE_APPEND O_APPEND +#define NGX_FILE_TRUNCATE O_CREAT|O_TRUNC +#define NGX_FILE_APPEND O_WRONLY|O_APPEND #define NGX_FILE_DEFAULT_ACCESS 0644 +#define NGX_FILE_OWNER_ACCESS 0600 #define ngx_close_file close @@ -99,9 +100,23 @@ ssize_t ngx_write_chain_to_file(ngx_file #define ngx_read_fd read #define ngx_read_fd_n "read()" -#define ngx_write_fd write +/* + * 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 @@ -147,8 +162,10 @@ ngx_int_t ngx_set_file_time(u_char *name #define ngx_realpath(p, r) realpath((char *) p, (char *) r) #define ngx_realpath_n "realpath()" -#define ngx_getcwd(buf, size) (getcwd(buf, size) != NULL) +#define ngx_getcwd(buf, size) (getcwd((char *) buf, size) != NULL) #define ngx_getcwd_n "getcwd()" +#define ngx_path_separator(c) ((c) == '/') + #define NGX_MAX_PATH PATH_MAX #define NGX_DIR_MASK_LEN 0 @@ -190,10 +207,25 @@ ngx_int_t ngx_read_dir(ngx_dir_t *dir); #if (NGX_HAVE_D_TYPE) +#if (NGX_LINUX) + +/* XFS on Linux does 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_LINK) : (S_ISLNK((dir)->info.st_mode))) + +#else + #define ngx_de_is_dir(dir) ((dir)->type == DT_DIR) #define ngx_de_is_file(dir) ((dir)->type == DT_REG) #define ngx_de_is_link(dir) ((dir)->type == DT_LINK) +#endif /* NGX_LINUX */ + #else #define ngx_de_is_dir(dir) (S_ISDIR((dir)->info.st_mode)) @@ -247,5 +279,12 @@ ngx_int_t ngx_directio_off(ngx_fd_t fd); #endif +size_t ngx_fs_bsize(u_char *name); + + +#define ngx_stderr STDERR_FILENO +#define ngx_set_stderr(fd) dup2(fd, STDERR_FILENO) +#define ngx_set_stderr_n "dup2(STDERR_FILENO)" + #endif /* _NGX_FILES_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_freebsd.h b/src/os/unix/ngx_freebsd.h --- a/src/os/unix/ngx_freebsd.h +++ b/src/os/unix/ngx_freebsd.h @@ -14,7 +14,6 @@ ngx_chain_t *ngx_freebsd_sendfile_chain( extern int ngx_freebsd_kern_osreldate; extern int ngx_freebsd_hw_ncpu; extern u_long ngx_freebsd_net_inet_tcp_sendspace; -extern int ngx_freebsd_kern_ipc_zero_copy_send; extern ngx_uint_t ngx_freebsd_sendfile_nbytes_bug; extern ngx_uint_t ngx_freebsd_use_tcp_nopush; diff --git a/src/os/unix/ngx_freebsd_config.h b/src/os/unix/ngx_freebsd_config.h --- a/src/os/unix/ngx_freebsd_config.h +++ b/src/os/unix/ngx_freebsd_config.h @@ -22,6 +22,8 @@ #include #include #include +#include /* ALIGN() */ +#include /* statfs() */ #include /* FIONBIO */ #include @@ -43,7 +45,6 @@ #include /* setproctitle() before 4.1 */ #include #include -#include /* ALIGN() */ #if __FreeBSD_version < 400017 diff --git a/src/os/unix/ngx_freebsd_init.c b/src/os/unix/ngx_freebsd_init.c --- a/src/os/unix/ngx_freebsd_init.c +++ b/src/os/unix/ngx_freebsd_init.c @@ -19,9 +19,6 @@ u_long ngx_freebsd_net_inet_tcp_sendspa /* FreeBSD 4.9 */ int ngx_freebsd_machdep_hlt_logical_cpus; -/* FreeBSD 5.0 */ -int ngx_freebsd_kern_ipc_zero_copy_send; - ngx_uint_t ngx_freebsd_sendfile_nbytes_bug; ngx_uint_t ngx_freebsd_use_tcp_nopush; @@ -68,10 +65,6 @@ sysctl_t sysctls[] = { &ngx_freebsd_kern_ipc_somaxconn, sizeof(ngx_freebsd_kern_ipc_somaxconn), 0 }, - { "kern.ipc.zero_copy.send", - &ngx_freebsd_kern_ipc_zero_copy_send, - sizeof(ngx_freebsd_kern_ipc_zero_copy_send), 0 }, - { NULL, NULL, 0, 0 } }; diff --git a/src/os/unix/ngx_linux_config.h b/src/os/unix/ngx_linux_config.h --- a/src/os/unix/ngx_linux_config.h +++ b/src/os/unix/ngx_linux_config.h @@ -28,6 +28,7 @@ #include #include #include +#include /* statfs() */ #include #include @@ -94,11 +95,6 @@ extern ssize_t sendfile(int s, int fd, i #endif -#ifndef NGX_HAVE_GNU_CRYPT_R -#define NGX_HAVE_GNU_CRYPT_R 1 -#endif - - #ifndef NGX_HAVE_INHERITED_NONBLOCK #define NGX_HAVE_INHERITED_NONBLOCK 0 #endif diff --git a/src/os/unix/ngx_os.h b/src/os/unix/ngx_os.h --- a/src/os/unix/ngx_os.h +++ b/src/os/unix/ngx_os.h @@ -13,7 +13,6 @@ #define NGX_IO_SENDFILE 1 -#define NGX_IO_ZEROCOPY 2 typedef ssize_t (*ngx_recv_pt)(ngx_connection_t *c, u_char *buf, size_t size); @@ -38,6 +37,7 @@ void ngx_os_status(ngx_log_t *log); ngx_int_t ngx_os_specific_init(ngx_log_t *log); void ngx_os_specific_status(ngx_log_t *log); ngx_int_t ngx_daemon(ngx_log_t *log); +ngx_int_t ngx_os_signal_process(ngx_cycle_t *cycle, char *sig, ngx_int_t pid); ssize_t ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size); @@ -54,7 +54,6 @@ extern ngx_int_t ngx_max_sockets; extern ngx_uint_t ngx_inherited_nonblocking; extern ngx_uint_t ngx_tcp_nodelay_and_tcp_nopush; -#define ngx_stderr_fileno STDERR_FILENO #if (NGX_FREEBSD) #include diff --git a/src/os/unix/ngx_posix_config.h b/src/os/unix/ngx_posix_config.h --- a/src/os/unix/ngx_posix_config.h +++ b/src/os/unix/ngx_posix_config.h @@ -44,6 +44,15 @@ #include #include #include +#if (NGX_HAVE_SYS_PARAM_H) +#include /* statfs() */ +#endif +#if (NGX_HAVE_SYS_MOUNT_H) +#include /* statfs() */ +#endif +#if (NGX_HAVE_SYS_STATVFS_H) +#include /* statvfs() */ +#endif #if (NGX_HAVE_SYS_FILIO_H) #include /* FIONBIO */ diff --git a/src/os/unix/ngx_posix_init.c b/src/os/unix/ngx_posix_init.c --- a/src/os/unix/ngx_posix_init.c +++ b/src/os/unix/ngx_posix_init.c @@ -22,7 +22,7 @@ ngx_os_io_t ngx_os_io = { ngx_unix_recv, ngx_readv_chain, ngx_udp_unix_recv, - NULL, + ngx_unix_send, ngx_writev_chain, 0 }; diff --git a/src/os/unix/ngx_process.c b/src/os/unix/ngx_process.c --- a/src/os/unix/ngx_process.c +++ b/src/os/unix/ngx_process.c @@ -13,6 +13,7 @@ typedef struct { int signo; char *signame; + char *name; void (*handler)(int signo); } ngx_signal_t; @@ -36,39 +37,45 @@ ngx_process_t ngx_processes[NGX_MAX_P ngx_signal_t signals[] = { { ngx_signal_value(NGX_RECONFIGURE_SIGNAL), "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL), + "reload", ngx_signal_handler }, { ngx_signal_value(NGX_REOPEN_SIGNAL), "SIG" ngx_value(NGX_REOPEN_SIGNAL), + "reopen", ngx_signal_handler }, { ngx_signal_value(NGX_NOACCEPT_SIGNAL), "SIG" ngx_value(NGX_NOACCEPT_SIGNAL), + "", ngx_signal_handler }, { ngx_signal_value(NGX_TERMINATE_SIGNAL), "SIG" ngx_value(NGX_TERMINATE_SIGNAL), + "stop", ngx_signal_handler }, { ngx_signal_value(NGX_SHUTDOWN_SIGNAL), "SIG" ngx_value(NGX_SHUTDOWN_SIGNAL), + "quit", ngx_signal_handler }, { ngx_signal_value(NGX_CHANGEBIN_SIGNAL), "SIG" ngx_value(NGX_CHANGEBIN_SIGNAL), + "", ngx_signal_handler }, - { SIGALRM, "SIGALRM", ngx_signal_handler }, + { SIGALRM, "SIGALRM", "", ngx_signal_handler }, - { SIGINT, "SIGINT", ngx_signal_handler }, + { SIGINT, "SIGINT", "", ngx_signal_handler }, - { SIGIO, "SIGIO", ngx_signal_handler }, + { SIGIO, "SIGIO", "", ngx_signal_handler }, - { SIGCHLD, "SIGCHLD", ngx_signal_handler }, + { SIGCHLD, "SIGCHLD", "", ngx_signal_handler }, - { SIGPIPE, "SIGPIPE, SIG_IGN", SIG_IGN }, + { SIGPIPE, "SIGPIPE, SIG_IGN", "", SIG_IGN }, - { 0, NULL, NULL } + { 0, NULL, "", NULL } }; @@ -494,10 +501,16 @@ ngx_process_get_status(void) } if (WTERMSIG(status)) { +#ifdef WCOREDUMP ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "%s %P exited on signal %d%s", process, pid, WTERMSIG(status), WCOREDUMP(status) ? " (core dumped)" : ""); +#else + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "%s %P exited on signal %d", + process, pid, WTERMSIG(status)); +#endif } else { ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, @@ -534,3 +547,23 @@ ngx_debug_point(void) ngx_abort(); } } + + +ngx_int_t +ngx_os_signal_process(ngx_cycle_t *cycle, char *name, ngx_int_t pid) +{ + ngx_signal_t *sig; + + for (sig = signals; sig->signo != 0; sig++) { + if (ngx_strcmp(name, sig->name) == 0) { + if (kill(pid, sig->signo) != -1) { + return 0; + } + + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "kill(%P, %d) failed", pid, sig->signo); + } + } + + return 1; +} diff --git a/src/os/unix/ngx_process_cycle.c b/src/os/unix/ngx_process_cycle.c --- a/src/os/unix/ngx_process_cycle.c +++ b/src/os/unix/ngx_process_cycle.c @@ -12,7 +12,7 @@ static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type); -static void ngx_start_garbage_collector(ngx_cycle_t *cycle, ngx_int_t type); +static void ngx_start_cache_manager_process(ngx_cycle_t *cycle, ngx_int_t type); static void ngx_signal_worker_processes(ngx_cycle_t *cycle, int signo); static ngx_uint_t ngx_reap_children(ngx_cycle_t *cycle); static void ngx_master_process_exit(ngx_cycle_t *cycle); @@ -24,9 +24,8 @@ static void ngx_channel_handler(ngx_even static void ngx_wakeup_worker_threads(ngx_cycle_t *cycle); static ngx_thread_value_t ngx_worker_thread_cycle(void *data); #endif -#if 0 -static void ngx_garbage_collector_cycle(ngx_cycle_t *cycle, void *data); -#endif +static void ngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data); +static void ngx_cache_manager_process_handler(ngx_event_t *ev); ngx_uint_t ngx_process; @@ -123,7 +122,7 @@ ngx_master_process_cycle(ngx_cycle_t *cy ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_RESPAWN); - ngx_start_garbage_collector(cycle, NGX_PROCESS_RESPAWN); + ngx_start_cache_manager_process(cycle, NGX_PROCESS_RESPAWN); ngx_new_binary = 0; delay = 0; @@ -204,7 +203,7 @@ ngx_master_process_cycle(ngx_cycle_t *cy if (ngx_new_binary) { ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_RESPAWN); - ngx_start_garbage_collector(cycle, NGX_PROCESS_RESPAWN); + ngx_start_cache_manager_process(cycle, NGX_PROCESS_RESPAWN); ngx_noaccepting = 0; continue; @@ -223,7 +222,7 @@ ngx_master_process_cycle(ngx_cycle_t *cy ngx_core_module); ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_JUST_RESPAWN); - ngx_start_garbage_collector(cycle, NGX_PROCESS_JUST_RESPAWN); + ngx_start_cache_manager_process(cycle, NGX_PROCESS_JUST_RESPAWN); live = 1; ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); @@ -233,7 +232,7 @@ ngx_master_process_cycle(ngx_cycle_t *cy ngx_restart = 0; ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_RESPAWN); - ngx_start_garbage_collector(cycle, NGX_PROCESS_RESPAWN); + ngx_start_cache_manager_process(cycle, NGX_PROCESS_RESPAWN); live = 1; } @@ -361,18 +360,28 @@ ngx_start_worker_processes(ngx_cycle_t * static void -ngx_start_garbage_collector(ngx_cycle_t *cycle, ngx_int_t type) +ngx_start_cache_manager_process(ngx_cycle_t *cycle, ngx_int_t type) { -#if 0 - ngx_int_t i; - ngx_channel_t ch; + ngx_int_t i; + ngx_uint_t n; + ngx_path_t **path; + ngx_channel_t ch; - ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start garbage collector"); + path = ngx_cycle->pathes.elts; + for (n = 0; n < ngx_cycle->pathes.nelts; n++) { + if (path[n]->manager) { + goto start; + } + } + + return; + +start: ch.command = NGX_CMD_OPEN_CHANNEL; - ngx_spawn_process(cycle, ngx_garbage_collector_cycle, NULL, - "garbage collector", type); + ngx_spawn_process(cycle, ngx_cache_manager_process_cycle, NULL, + "cache manager process", type); ch.pid = ngx_processes[ngx_process_slot].pid; ch.slot = ngx_process_slot; @@ -398,7 +407,6 @@ ngx_start_garbage_collector(ngx_cycle_t ngx_write_channel(ngx_processes[i].channel[0], &ch, sizeof(ngx_channel_t), cycle->log); } -#endif } @@ -1004,7 +1012,7 @@ ngx_worker_process_exit(ngx_cycle_t *cyc && !c[i].read->resolver) { ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, - "open socket #%d left in %ui connection %s", + "open socket #%d left in connection %ui%s", c[i].fd, i, ngx_debug_quit ? ", aborting" : ""); ngx_debug_point(); } @@ -1254,27 +1262,29 @@ ngx_worker_thread_cycle(void *data) #endif -#if 0 - static void -ngx_garbage_collector_cycle(ngx_cycle_t *cycle, void *data) +ngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data) { - ngx_uint_t i; - ngx_gc_t ctx; - ngx_path_t **path; - ngx_event_t *ev; + void *ident[4]; + ngx_event_t ev; + + cycle->connection_n = 512; ngx_worker_process_init(cycle, 0); - ev = &cycle->read_events0[ngx_channel]; - - ngx_accept_mutex = NULL; + ngx_close_listening_sockets(cycle); - ngx_setproctitle("garbage collector"); + ngx_memzero(&ev, sizeof(ngx_event_t)); + ev.handler = ngx_cache_manager_process_handler; + ev.data = ident; + ev.log = cycle->log; + ident[3] = (void *) -1; -#if 0 - ngx_add_timer(ev, 60 * 1000); -#endif + ngx_use_accept_mutex = 0; + + ngx_setproctitle("cache manager process"); + + ngx_add_timer(&ev, 0); for ( ;; ) { @@ -1289,19 +1299,35 @@ ngx_garbage_collector_cycle(ngx_cycle_t ngx_reopen_files(cycle, -1); } - path = cycle->pathes.elts; - for (i = 0; i < cycle->pathes.nelts; i++) { - ctx.path = path[i]; - ctx.log = cycle->log; - ctx.handler = path[i]->cleaner; - - ngx_collect_garbage(&ctx, &path[i]->name, 0); - } - - ngx_add_timer(ev, 60 * 60 * 1000); - ngx_process_events_and_timers(cycle); } } -#endif + +static void +ngx_cache_manager_process_handler(ngx_event_t *ev) +{ + time_t next, n; + ngx_uint_t i; + ngx_path_t **path; + + next = 60 * 60; + + path = ngx_cycle->pathes.elts; + for (i = 0; i < ngx_cycle->pathes.nelts; i++) { + + if (path[i]->manager) { + n = path[i]->manager(path[i]->data); + + next = (n <= next) ? n : next; + + ngx_time_update(0, 0); + } + } + + if (next == 0) { + next = 1; + } + + ngx_add_timer(ev, next * 1000); +} diff --git a/src/os/unix/ngx_process_cycle.h b/src/os/unix/ngx_process_cycle.h --- a/src/os/unix/ngx_process_cycle.h +++ b/src/os/unix/ngx_process_cycle.h @@ -19,9 +19,10 @@ #define NGX_CMD_REOPEN 5 -#define NGX_PROCESS_SINGLE 0 -#define NGX_PROCESS_MASTER 1 -#define NGX_PROCESS_WORKER 2 +#define NGX_PROCESS_SINGLE 0 +#define NGX_PROCESS_MASTER 1 +#define NGX_PROCESS_WORKER 2 +#define NGX_PROCESS_SIGNALLER 3 void ngx_master_process_cycle(ngx_cycle_t *cycle); diff --git a/src/os/unix/ngx_send.c b/src/os/unix/ngx_send.c --- a/src/os/unix/ngx_send.c +++ b/src/os/unix/ngx_send.c @@ -40,6 +40,8 @@ ngx_unix_send(ngx_connection_t *c, u_cha wev->ready = 0; } + c->sent += n; + return n; } diff --git a/src/os/unix/ngx_shmem.h b/src/os/unix/ngx_shmem.h --- a/src/os/unix/ngx_shmem.h +++ b/src/os/unix/ngx_shmem.h @@ -15,7 +15,9 @@ typedef struct { u_char *addr; size_t size; + ngx_str_t name; ngx_log_t *log; + ngx_uint_t exists; /* unsigned exists:1; */ } ngx_shm_t; diff --git a/src/os/unix/ngx_solaris_config.h b/src/os/unix/ngx_solaris_config.h --- a/src/os/unix/ngx_solaris_config.h +++ b/src/os/unix/ngx_solaris_config.h @@ -28,6 +28,7 @@ #include #include #include +#include /* statvfs() */ #include /* FIONBIO */ #include diff --git a/src/os/unix/ngx_sunpro_x86.il b/src/os/unix/ngx_sunpro_x86.il --- a/src/os/unix/ngx_sunpro_x86.il +++ b/src/os/unix/ngx_sunpro_x86.il @@ -5,7 +5,7 @@ / ngx_atomic_uint_t ngx_atomic_cmp_set(ngx_atomic_t *lock, / ngx_atomic_uint_t old, ngx_atomic_uint_t set); / -/ the arguments are passed on the stack (%esp), 4(%esp), 8(%esp) +/ the arguments are passed on stack (%esp), 4(%esp), 8(%esp) .inline ngx_atomic_cmp_set,0 movl (%esp), %ecx @@ -21,7 +21,7 @@ / ngx_atomic_int_t ngx_atomic_fetch_add(ngx_atomic_t *value, / ngx_atomic_int_t add); / -/ the arguments are passed on the stack (%esp), 4(%esp) +/ the arguments are passed on stack (%esp), 4(%esp) .inline ngx_atomic_fetch_add,0 movl (%esp), %ecx @@ -32,7 +32,12 @@ / ngx_cpu_pause() +/ +/ the "rep; nop" is used instead of "pause" to avoid the "[ PAUSE ]" hardware +/ capability added by linker because Solaris/i386 does not know about it: +/ +/ ld.so.1: nginx: fatal: hardware capability unsupported: 0x2000 [ PAUSE ] .inline ngx_cpu_pause,0 - pause + rep; nop .end diff --git a/src/os/unix/ngx_sunpro_x86.map b/src/os/unix/ngx_sunpro_x86.map deleted file mode 100644 --- a/src/os/unix/ngx_sunpro_x86.map +++ /dev/null @@ -1,2 +0,0 @@ -# disable { PAUSE ] hwcap for Sun Studio 11 -hwcap_1 = OVERRIDE;