├── .gitignore ├── LICENSE ├── README.md ├── nginx ├── LICENSE ├── conf │ └── nginx.conf └── src │ └── http │ └── modules │ └── ngx_http_fastcgi_module.c └── servers └── afcgitest ├── Makefile ├── afcgitest.c ├── sockutil.c └── sockutil.h /.gitignore: -------------------------------------------------------------------------------- 1 | servers/afcgitest/afcgitest 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 Rasmus Andersson 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # afcgi 2 | 3 | Asynchronous FastCGI. 4 | 5 | Primarily a modified version of the [Nginx FastCGI module](http://wiki.nginx.org/NginxHttpFcgiModule) which implements multiplexing of connections, allowing a single FastCGI server to handle many concurrent requests. 6 | 7 | This paves the way for long-lived connections in web apps without wasting resources -- i.e. optimally you only need to run one server process per CPU (or one server with one thread per CPU) instead of one process per request. 8 | 9 | ## Files 10 | 11 | - `nginx/` contains the modified `ngx_http_fastcgi_module.c` along with a "prefix" (conf, logs, etc) for running a test server. 12 | 13 | - `servers/` contains a set of asynchronous FastCGI server implementations, most notably the original libevent-based server written with the purpose to test afcgi. 14 | 15 | [](http://hunch.se/s/cx/e1rg8i8bkgwks.png) 16 | 17 | ## Building 18 | 19 | ### Nginx module 20 | 21 | The nginx module is built by *replacing* the original `ngx_http_fastcgi_module.c`: 22 | 23 | wget http://sysoev.ru/nginx/nginx-0.8.29.tar.gz 24 | tar xzf nginx-0.8.29.tar.gz 25 | cd nginx-0.8.29/src/http/modules 26 | mv ngx_http_fastcgi_module.c ngx_http_fastcgi_module.c.dist 27 | cp /path/to/afcgi/nginx/src/http/modules/ngx_http_fastcgi_module.c . 28 | cd ../../.. 29 | ./configure 30 | make 31 | 32 | This module has been developed for and tested with nginx version 0.8.29 and is still in an experimental stage. No warranties, no guarantees -- use at own risk ;) 33 | 34 | ### Server reference implementation 35 | 36 | The FCGI server reference implementation in `servers/afcgitest` is built by using regular make and linking against [libevent](http://monkey.org/~provos/libevent/): 37 | 38 | cd /path/to/afcgi/servers/afcgitest 39 | make 40 | ./afcgitest 127.0.0.1:5000 41 | 42 | > The last line assumes a FastCGI client (i.e. nginx) is connecting to port `5000` on `127.0.0.1`. 43 | 44 | At the moment the `Makefile` is prepared for Mac OS X and contains a few lines which will not work in other environments. Simply remove or comment-out these lines when building on another platform: 45 | 46 | # If Mac OS X: 47 | CFLAGS += -arch i386 48 | LDFLAGS += -arch i386 49 | 50 | becomes: 51 | 52 | # If Mac OS X: 53 | #CFLAGS += -arch i386 54 | #LDFLAGS += -arch i386 55 | 56 | > **OS X users:** This assumes libevent is built for the i386 architecture. Your libevent library might use another architecture and then you should change the `-arch` argument value to match the architecture of libevent. 57 | 58 | ## License & redistribution 59 | 60 | Most parts of this software is licensed under the MIT license (see the `LICENSE` file for details) while the nginx module is licensed under the nginx license (details in the file `nginx/LICENSE`). 61 | 62 | In short; you are free to use this software for commercial and non-commercial applictions as long as the license(s) and copyright notice(s) are properly reproduced. Read the LICENSEs files for details. 63 | 64 | ## Authors 65 | 66 | - Rasmus Andersson 67 | 68 | - Igor Sysoev (author of the original, synchronous fastcgi module) 69 | -------------------------------------------------------------------------------- /nginx/LICENSE: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2002-2009 Igor Sysoev 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | -------------------------------------------------------------------------------- /nginx/conf/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 1; 2 | daemon off; 3 | error_log nginx.log debug; 4 | 5 | events { 6 | worker_connections 256; 7 | } 8 | 9 | http { 10 | default_type application/octet-stream; 11 | sendfile on; 12 | keepalive_timeout 30; 13 | 14 | server { 15 | listen 8888; 16 | location /fcgi { 17 | fastcgi_pass 127.0.0.1:5000; 18 | fastcgi_param SCRIPT_FILENAME /tmp$fastcgi_script_name; 19 | fastcgi_param QUERY_STRING $query_string; 20 | fastcgi_param REQUEST_METHOD $request_method; 21 | fastcgi_param CONTENT_TYPE $content_type; 22 | fastcgi_param CONTENT_LENGTH $content_length; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /nginx/src/http/modules/ngx_http_fastcgi_module.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Igor Sysoev 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #define NGX_FASTCGI_MPX_REQLIMIT __SHRT_MAX__+__SHRT_MAX__ 12 | #define NGX_HTTP_FASTCGI_KEEP_CONN 1 13 | //#define NGX_FASTCGI_MPX_DEBUG 1 14 | 15 | #ifndef NGX_FASTCGI_MPX_DEBUG 16 | #define NGX_FASTCGI_MPX_DEBUG 0 17 | #endif 18 | 19 | #if ((NGX_DEBUG) || (NGX_FASTCGI_MPX_DEBUG)) 20 | #define DLOG(r, fmt, ...) \ 21 | ngx_log_error(NGX_LOG_DEBUG, (r)->connection->log, 0, \ 22 | "[ngx_http_fastcgi_module:%d] " fmt, __LINE__, ##__VA_ARGS__) 23 | #else 24 | #define DLOG(...) 25 | #endif 26 | 27 | 28 | typedef struct { 29 | ngx_http_upstream_conf_t upstream; 30 | 31 | ngx_str_t index; 32 | 33 | ngx_array_t *flushes; 34 | ngx_array_t *params_len; 35 | ngx_array_t *params; 36 | ngx_array_t *params_source; 37 | ngx_array_t *catch_stderr; 38 | 39 | ngx_array_t *fastcgi_lengths; 40 | ngx_array_t *fastcgi_values; 41 | 42 | #if (NGX_HTTP_CACHE) 43 | ngx_http_complex_value_t cache_key; 44 | #endif 45 | 46 | #if (NGX_PCRE) 47 | ngx_regex_t *split_regex; 48 | ngx_str_t split_name; 49 | #endif 50 | 51 | ngx_http_request_t *requests[NGX_FASTCGI_MPX_REQLIMIT]; 52 | } ngx_http_fastcgi_loc_conf_t; 53 | 54 | 55 | typedef enum { 56 | ngx_http_fastcgi_st_version = 0, 57 | ngx_http_fastcgi_st_type, 58 | ngx_http_fastcgi_st_request_id_hi, 59 | ngx_http_fastcgi_st_request_id_lo, 60 | ngx_http_fastcgi_st_content_length_hi, 61 | ngx_http_fastcgi_st_content_length_lo, 62 | ngx_http_fastcgi_st_padding_length, 63 | ngx_http_fastcgi_st_reserved, 64 | ngx_http_fastcgi_st_data, 65 | ngx_http_fastcgi_st_padding 66 | } ngx_http_fastcgi_state_e; 67 | 68 | 69 | typedef struct { 70 | u_char *start; 71 | u_char *end; 72 | } ngx_http_fastcgi_split_part_t; 73 | 74 | 75 | typedef struct { 76 | ngx_http_fastcgi_state_e state; 77 | u_char *pos; 78 | u_char *last; 79 | ngx_uint_t type; 80 | size_t length; 81 | size_t padding; 82 | 83 | ngx_uint_t fastcgi_stdout; /* unsigned :1 */ 84 | 85 | ngx_array_t *split_parts; 86 | 87 | ngx_str_t script_name; 88 | ngx_str_t path_info; 89 | short rid; 90 | } ngx_http_fastcgi_ctx_t; 91 | 92 | 93 | #define NGX_HTTP_FASTCGI_RESPONDER 1 94 | 95 | #define NGX_HTTP_FASTCGI_BEGIN_REQUEST 1 96 | #define NGX_HTTP_FASTCGI_ABORT_REQUEST 2 97 | #define NGX_HTTP_FASTCGI_END_REQUEST 3 98 | #define NGX_HTTP_FASTCGI_PARAMS 4 99 | #define NGX_HTTP_FASTCGI_STDIN 5 100 | #define NGX_HTTP_FASTCGI_STDOUT 6 101 | #define NGX_HTTP_FASTCGI_STDERR 7 102 | #define NGX_HTTP_FASTCGI_DATA 8 103 | 104 | 105 | typedef struct { 106 | u_char version; 107 | u_char type; 108 | u_char request_id_hi; 109 | u_char request_id_lo; 110 | u_char content_length_hi; 111 | u_char content_length_lo; 112 | u_char padding_length; 113 | u_char reserved; 114 | } ngx_http_fastcgi_header_t; 115 | 116 | 117 | typedef struct { 118 | u_char role_hi; 119 | u_char role_lo; 120 | u_char flags; 121 | u_char reserved[5]; 122 | } ngx_http_fastcgi_begin_request_t; 123 | 124 | 125 | typedef struct { 126 | u_char version; 127 | u_char type; 128 | u_char request_id_hi; 129 | u_char request_id_lo; 130 | } ngx_http_fastcgi_header_small_t; 131 | 132 | 133 | typedef struct { 134 | ngx_http_fastcgi_header_t h0; 135 | ngx_http_fastcgi_begin_request_t br; 136 | ngx_http_fastcgi_header_small_t h1; 137 | } ngx_http_fastcgi_request_start_t; 138 | 139 | 140 | static ngx_int_t ngx_http_fastcgi_eval(ngx_http_request_t *r, 141 | ngx_http_fastcgi_loc_conf_t *flcf); 142 | #if (NGX_HTTP_CACHE) 143 | static ngx_int_t ngx_http_fastcgi_create_key(ngx_http_request_t *r); 144 | #endif 145 | static ngx_int_t ngx_http_fastcgi_create_request(ngx_http_request_t *r); 146 | static ngx_int_t ngx_http_fastcgi_reinit_request(ngx_http_request_t *r); 147 | static ngx_int_t ngx_http_fastcgi_process_header(ngx_http_request_t *r); 148 | static ngx_int_t ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, 149 | ngx_buf_t *buf); 150 | static ngx_int_t ngx_http_fastcgi_process_record(ngx_http_request_t *r, 151 | ngx_http_fastcgi_ctx_t *f); 152 | static void ngx_http_fastcgi_abort_request(ngx_http_request_t *r); 153 | static void ngx_http_fastcgi_finalize_request(ngx_http_request_t *r, 154 | ngx_int_t rc); 155 | 156 | static ngx_int_t ngx_http_fastcgi_add_variables(ngx_conf_t *cf); 157 | static void *ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf); 158 | static char *ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, 159 | void *parent, void *child); 160 | static ngx_int_t ngx_http_fastcgi_script_name_variable(ngx_http_request_t *r, 161 | ngx_http_variable_value_t *v, uintptr_t data); 162 | static ngx_int_t ngx_http_fastcgi_path_info_variable(ngx_http_request_t *r, 163 | ngx_http_variable_value_t *v, uintptr_t data); 164 | static ngx_http_fastcgi_ctx_t *ngx_http_fastcgi_split(ngx_http_request_t *r, 165 | ngx_http_fastcgi_loc_conf_t *flcf); 166 | 167 | static char *ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, 168 | void *conf); 169 | static char *ngx_http_fastcgi_split_path_info(ngx_conf_t *cf, 170 | ngx_command_t *cmd, void *conf); 171 | static char *ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd, 172 | void *conf); 173 | #if (NGX_HTTP_CACHE) 174 | static char *ngx_http_fastcgi_cache(ngx_conf_t *cf, ngx_command_t *cmd, 175 | void *conf); 176 | static char *ngx_http_fastcgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, 177 | void *conf); 178 | #endif 179 | 180 | static char *ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post, 181 | void *data); 182 | 183 | static char *ngx_http_fastcgi_upstream_max_fails_unsupported(ngx_conf_t *cf, 184 | ngx_command_t *cmd, void *conf); 185 | static char *ngx_http_fastcgi_upstream_fail_timeout_unsupported(ngx_conf_t *cf, 186 | ngx_command_t *cmd, void *conf); 187 | 188 | 189 | static ngx_conf_post_t ngx_http_fastcgi_lowat_post = 190 | { ngx_http_fastcgi_lowat_check }; 191 | 192 | 193 | static ngx_conf_bitmask_t ngx_http_fastcgi_next_upstream_masks[] = { 194 | { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR }, 195 | { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT }, 196 | { ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER }, 197 | { ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 }, 198 | { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 }, 199 | { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 }, 200 | { ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING }, 201 | { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF }, 202 | { ngx_null_string, 0 } 203 | }; 204 | 205 | 206 | static ngx_conf_bitmask_t ngx_http_fastcgi_ignore_headers_masks[] = { 207 | { ngx_string("X-Accel-Redirect"), NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT }, 208 | { ngx_string("X-Accel-Expires"), NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES }, 209 | { ngx_string("Expires"), NGX_HTTP_UPSTREAM_IGN_EXPIRES }, 210 | { ngx_string("Cache-Control"), NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL }, 211 | { ngx_null_string, 0 } 212 | }; 213 | 214 | 215 | ngx_module_t ngx_http_fastcgi_module; 216 | 217 | 218 | static ngx_command_t ngx_http_fastcgi_commands[] = { 219 | 220 | { ngx_string("fastcgi_pass"), 221 | NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, 222 | ngx_http_fastcgi_pass, 223 | NGX_HTTP_LOC_CONF_OFFSET, 224 | 0, 225 | NULL }, 226 | 227 | { ngx_string("fastcgi_index"), 228 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 229 | ngx_conf_set_str_slot, 230 | NGX_HTTP_LOC_CONF_OFFSET, 231 | offsetof(ngx_http_fastcgi_loc_conf_t, index), 232 | NULL }, 233 | 234 | { ngx_string("fastcgi_split_path_info"), 235 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 236 | ngx_http_fastcgi_split_path_info, 237 | NGX_HTTP_LOC_CONF_OFFSET, 238 | 0, 239 | NULL }, 240 | 241 | { ngx_string("fastcgi_store"), 242 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 243 | ngx_http_fastcgi_store, 244 | NGX_HTTP_LOC_CONF_OFFSET, 245 | 0, 246 | NULL }, 247 | 248 | { ngx_string("fastcgi_store_access"), 249 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123, 250 | ngx_conf_set_access_slot, 251 | NGX_HTTP_LOC_CONF_OFFSET, 252 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.store_access), 253 | NULL }, 254 | 255 | { ngx_string("fastcgi_ignore_client_abort"), 256 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 257 | ngx_conf_set_flag_slot, 258 | NGX_HTTP_LOC_CONF_OFFSET, 259 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.ignore_client_abort), 260 | NULL }, 261 | 262 | { ngx_string("fastcgi_bind"), 263 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 264 | ngx_http_upsteam_bind_set_slot, 265 | NGX_HTTP_LOC_CONF_OFFSET, 266 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.local), 267 | NULL }, 268 | 269 | { ngx_string("fastcgi_connect_timeout"), 270 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 271 | ngx_conf_set_msec_slot, 272 | NGX_HTTP_LOC_CONF_OFFSET, 273 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.connect_timeout), 274 | NULL }, 275 | 276 | { ngx_string("fastcgi_send_timeout"), 277 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 278 | ngx_conf_set_msec_slot, 279 | NGX_HTTP_LOC_CONF_OFFSET, 280 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.send_timeout), 281 | NULL }, 282 | 283 | { ngx_string("fastcgi_send_lowat"), 284 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 285 | ngx_conf_set_size_slot, 286 | NGX_HTTP_LOC_CONF_OFFSET, 287 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.send_lowat), 288 | &ngx_http_fastcgi_lowat_post }, 289 | 290 | { ngx_string("fastcgi_buffer_size"), 291 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 292 | ngx_conf_set_size_slot, 293 | NGX_HTTP_LOC_CONF_OFFSET, 294 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.buffer_size), 295 | NULL }, 296 | 297 | { ngx_string("fastcgi_pass_request_headers"), 298 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 299 | ngx_conf_set_flag_slot, 300 | NGX_HTTP_LOC_CONF_OFFSET, 301 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_request_headers), 302 | NULL }, 303 | 304 | { ngx_string("fastcgi_pass_request_body"), 305 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 306 | ngx_conf_set_flag_slot, 307 | NGX_HTTP_LOC_CONF_OFFSET, 308 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_request_body), 309 | NULL }, 310 | 311 | { ngx_string("fastcgi_intercept_errors"), 312 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 313 | ngx_conf_set_flag_slot, 314 | NGX_HTTP_LOC_CONF_OFFSET, 315 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.intercept_errors), 316 | NULL }, 317 | 318 | { ngx_string("fastcgi_read_timeout"), 319 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 320 | ngx_conf_set_msec_slot, 321 | NGX_HTTP_LOC_CONF_OFFSET, 322 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.read_timeout), 323 | NULL }, 324 | 325 | { ngx_string("fastcgi_buffers"), 326 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, 327 | ngx_conf_set_bufs_slot, 328 | NGX_HTTP_LOC_CONF_OFFSET, 329 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.bufs), 330 | NULL }, 331 | 332 | { ngx_string("fastcgi_busy_buffers_size"), 333 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 334 | ngx_conf_set_size_slot, 335 | NGX_HTTP_LOC_CONF_OFFSET, 336 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.busy_buffers_size_conf), 337 | NULL }, 338 | 339 | #if (NGX_HTTP_CACHE) 340 | 341 | { ngx_string("fastcgi_cache"), 342 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 343 | ngx_http_fastcgi_cache, 344 | NGX_HTTP_LOC_CONF_OFFSET, 345 | 0, 346 | NULL }, 347 | 348 | { ngx_string("fastcgi_cache_key"), 349 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 350 | ngx_http_fastcgi_cache_key, 351 | NGX_HTTP_LOC_CONF_OFFSET, 352 | 0, 353 | NULL }, 354 | 355 | { ngx_string("fastcgi_cache_path"), 356 | NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE, 357 | ngx_http_file_cache_set_slot, 358 | 0, 359 | 0, 360 | &ngx_http_fastcgi_module }, 361 | 362 | { ngx_string("fastcgi_cache_valid"), 363 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, 364 | ngx_http_file_cache_valid_set_slot, 365 | NGX_HTTP_LOC_CONF_OFFSET, 366 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_valid), 367 | NULL }, 368 | 369 | { ngx_string("fastcgi_cache_min_uses"), 370 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 371 | ngx_conf_set_num_slot, 372 | NGX_HTTP_LOC_CONF_OFFSET, 373 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_min_uses), 374 | NULL }, 375 | 376 | { ngx_string("fastcgi_cache_use_stale"), 377 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, 378 | ngx_conf_set_bitmask_slot, 379 | NGX_HTTP_LOC_CONF_OFFSET, 380 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_use_stale), 381 | &ngx_http_fastcgi_next_upstream_masks }, 382 | 383 | { ngx_string("fastcgi_cache_methods"), 384 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, 385 | ngx_conf_set_bitmask_slot, 386 | NGX_HTTP_LOC_CONF_OFFSET, 387 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_methods), 388 | &ngx_http_upstream_cache_method_mask }, 389 | 390 | #endif 391 | 392 | { ngx_string("fastcgi_temp_path"), 393 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234, 394 | ngx_conf_set_path_slot, 395 | NGX_HTTP_LOC_CONF_OFFSET, 396 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.temp_path), 397 | NULL }, 398 | 399 | { ngx_string("fastcgi_max_temp_file_size"), 400 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 401 | ngx_conf_set_size_slot, 402 | NGX_HTTP_LOC_CONF_OFFSET, 403 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.max_temp_file_size_conf), 404 | NULL }, 405 | 406 | { ngx_string("fastcgi_temp_file_write_size"), 407 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 408 | ngx_conf_set_size_slot, 409 | NGX_HTTP_LOC_CONF_OFFSET, 410 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.temp_file_write_size_conf), 411 | NULL }, 412 | 413 | { ngx_string("fastcgi_next_upstream"), 414 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, 415 | ngx_conf_set_bitmask_slot, 416 | NGX_HTTP_LOC_CONF_OFFSET, 417 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.next_upstream), 418 | &ngx_http_fastcgi_next_upstream_masks }, 419 | 420 | { ngx_string("fastcgi_upstream_max_fails"), 421 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 422 | ngx_http_fastcgi_upstream_max_fails_unsupported, 423 | 0, 424 | 0, 425 | NULL }, 426 | 427 | { ngx_string("fastcgi_upstream_fail_timeout"), 428 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 429 | ngx_http_fastcgi_upstream_fail_timeout_unsupported, 430 | 0, 431 | 0, 432 | NULL }, 433 | 434 | { ngx_string("fastcgi_param"), 435 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, 436 | ngx_conf_set_keyval_slot, 437 | NGX_HTTP_LOC_CONF_OFFSET, 438 | offsetof(ngx_http_fastcgi_loc_conf_t, params_source), 439 | NULL }, 440 | 441 | { ngx_string("fastcgi_pass_header"), 442 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 443 | ngx_conf_set_str_array_slot, 444 | NGX_HTTP_LOC_CONF_OFFSET, 445 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_headers), 446 | NULL }, 447 | 448 | { ngx_string("fastcgi_hide_header"), 449 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 450 | ngx_conf_set_str_array_slot, 451 | NGX_HTTP_LOC_CONF_OFFSET, 452 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.hide_headers), 453 | NULL }, 454 | 455 | { ngx_string("fastcgi_ignore_headers"), 456 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, 457 | ngx_conf_set_bitmask_slot, 458 | NGX_HTTP_LOC_CONF_OFFSET, 459 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.ignore_headers), 460 | &ngx_http_fastcgi_ignore_headers_masks }, 461 | 462 | { ngx_string("fastcgi_catch_stderr"), 463 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 464 | ngx_conf_set_str_array_slot, 465 | NGX_HTTP_LOC_CONF_OFFSET, 466 | offsetof(ngx_http_fastcgi_loc_conf_t, catch_stderr), 467 | NULL }, 468 | 469 | ngx_null_command 470 | }; 471 | 472 | 473 | static ngx_http_module_t ngx_http_fastcgi_module_ctx = { 474 | ngx_http_fastcgi_add_variables, /* preconfiguration */ 475 | NULL, /* postconfiguration */ 476 | 477 | NULL, /* create main configuration */ 478 | NULL, /* init main configuration */ 479 | 480 | NULL, /* create server configuration */ 481 | NULL, /* merge server configuration */ 482 | 483 | ngx_http_fastcgi_create_loc_conf, /* create location configuration */ 484 | ngx_http_fastcgi_merge_loc_conf /* merge location configuration */ 485 | }; 486 | 487 | 488 | ngx_module_t ngx_http_fastcgi_module = { 489 | NGX_MODULE_V1, 490 | &ngx_http_fastcgi_module_ctx, /* module context */ 491 | ngx_http_fastcgi_commands, /* module directives */ 492 | NGX_HTTP_MODULE, /* module type */ 493 | NULL, /* init master */ 494 | NULL, /* init module */ 495 | NULL, /* init process */ 496 | NULL, /* init thread */ 497 | NULL, /* exit thread */ 498 | NULL, /* exit process */ 499 | NULL, /* exit master */ 500 | NGX_MODULE_V1_PADDING 501 | }; 502 | 503 | 504 | static ngx_http_fastcgi_request_start_t ngx_http_fastcgi_request_start = { 505 | { 1, /* version */ 506 | NGX_HTTP_FASTCGI_BEGIN_REQUEST, /* type */ 507 | 0, /* request_id_hi */ 508 | 1, /* request_id_lo */ 509 | 0, /* content_length_hi */ 510 | sizeof(ngx_http_fastcgi_begin_request_t), /* content_length_lo */ 511 | 0, /* padding_length */ 512 | 0 }, /* reserved */ 513 | 514 | { 0, /* role_hi */ 515 | NGX_HTTP_FASTCGI_RESPONDER, /* role_lo */ 516 | NGX_HTTP_FASTCGI_KEEP_CONN, /* flags */ 517 | { 0, 0, 0, 0, 0 } }, /* reserved[5] */ 518 | 519 | { 1, /* version */ 520 | NGX_HTTP_FASTCGI_PARAMS, /* type */ 521 | 0, /* request_id_hi */ 522 | 1 }, /* request_id_lo */ 523 | 524 | }; 525 | 526 | 527 | static ngx_http_variable_t ngx_http_fastcgi_vars[] = { 528 | 529 | { ngx_string("fastcgi_script_name"), NULL, 530 | ngx_http_fastcgi_script_name_variable, 0, 531 | NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, 532 | 533 | { ngx_string("fastcgi_path_info"), NULL, 534 | ngx_http_fastcgi_path_info_variable, 0, 535 | NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, 536 | 537 | { ngx_null_string, NULL, NULL, 0, 0, 0 } 538 | }; 539 | 540 | 541 | static ngx_str_t ngx_http_fastcgi_hide_headers[] = { 542 | ngx_string("Status"), 543 | ngx_string("X-Accel-Expires"), 544 | ngx_string("X-Accel-Redirect"), 545 | ngx_string("X-Accel-Limit-Rate"), 546 | ngx_string("X-Accel-Buffering"), 547 | ngx_string("X-Accel-Charset"), 548 | ngx_null_string 549 | }; 550 | 551 | 552 | #if (NGX_HTTP_CACHE) 553 | 554 | static ngx_str_t ngx_http_fastcgi_hide_cache_headers[] = { 555 | ngx_string("Status"), 556 | ngx_string("X-Accel-Expires"), 557 | ngx_string("X-Accel-Redirect"), 558 | ngx_string("X-Accel-Limit-Rate"), 559 | ngx_string("X-Accel-Buffering"), 560 | ngx_string("X-Accel-Charset"), 561 | ngx_string("Set-Cookie"), 562 | ngx_string("P3P"), 563 | ngx_null_string 564 | }; 565 | 566 | #endif 567 | 568 | 569 | static ngx_path_init_t ngx_http_fastcgi_temp_path = { 570 | ngx_string(NGX_HTTP_FASTCGI_TEMP_PATH), { 1, 2, 0 } 571 | }; 572 | 573 | 574 | static ngx_inline short 575 | ngx_http_fastcgi_retain_request(ngx_http_request_t *r, 576 | ngx_http_fastcgi_ctx_t *f, ngx_http_fastcgi_loc_conf_t *c) 577 | { 578 | if (f == NULL) { 579 | f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module); 580 | } 581 | if (f->rid != 0) { 582 | // should assert this? 583 | return 0; 584 | } 585 | if (c == NULL) { 586 | c = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); 587 | } 588 | 589 | int i; 590 | 591 | f->rid = 0; 592 | 593 | for (i=0; irequests[i] == NULL) { 595 | f->rid = (short)i+1; 596 | DLOG(r, "RETAIN rid %d", f->rid); 597 | c->requests[i] = r; 598 | break; 599 | } 600 | } 601 | 602 | return f->rid; 603 | } 604 | 605 | 606 | static ngx_inline void 607 | ngx_http_fastcgi_release_request(ngx_http_request_t *r, 608 | ngx_http_fastcgi_ctx_t *f, ngx_http_fastcgi_loc_conf_t *c) 609 | { 610 | if (f == NULL) { 611 | f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module); 612 | } 613 | if (f->rid < 1) 614 | return; 615 | if (c == NULL) { 616 | c = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); 617 | } 618 | 619 | if (f != NULL) { 620 | DLOG(r, "RELEASE rid %d", f->rid); 621 | if (c != NULL) { 622 | c->requests[f->rid-1] = NULL; 623 | } 624 | f->rid = 0; 625 | } 626 | } 627 | 628 | 629 | static ngx_int_t 630 | ngx_http_fastcgi_handler(ngx_http_request_t *r) 631 | { 632 | ngx_int_t rc; 633 | ngx_http_upstream_t *u; 634 | ngx_http_fastcgi_ctx_t *f; 635 | ngx_http_fastcgi_loc_conf_t *flcf; 636 | 637 | if (r->subrequest_in_memory) { 638 | ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, 639 | "ngx_http_fastcgi_module does not support " 640 | "subrequest in memory"); 641 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 642 | } 643 | 644 | if (ngx_http_upstream_create(r) != NGX_OK) { 645 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 646 | } 647 | 648 | f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t)); 649 | if (f == NULL) { 650 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 651 | } 652 | 653 | flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); 654 | 655 | u = r->upstream; 656 | 657 | /* todo: store requests[] map per upstream peer, not in location scope */ 658 | 659 | if (ngx_http_fastcgi_retain_request(r, f, flcf) == 0) { 660 | ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, 661 | "ngx_http_fastcgi_module: max number of concurrent " 662 | "requests hit (%d requests)", NGX_FASTCGI_MPX_REQLIMIT); 663 | return NGX_HTTP_SERVICE_UNAVAILABLE; 664 | } 665 | 666 | ngx_http_set_ctx(r, f, ngx_http_fastcgi_module); 667 | 668 | if (flcf->fastcgi_lengths) { 669 | if (ngx_http_fastcgi_eval(r, flcf) != NGX_OK) { 670 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 671 | } 672 | } 673 | 674 | u->schema.len = sizeof("fastcgi://") - 1; 675 | u->schema.data = (u_char *) "fastcgi://"; 676 | 677 | u->output.tag = (ngx_buf_tag_t) &ngx_http_fastcgi_module; 678 | 679 | u->conf = &flcf->upstream; 680 | 681 | #if (NGX_HTTP_CACHE) 682 | u->create_key = ngx_http_fastcgi_create_key; 683 | #endif 684 | u->create_request = ngx_http_fastcgi_create_request; 685 | u->reinit_request = ngx_http_fastcgi_reinit_request; 686 | u->process_header = ngx_http_fastcgi_process_header; 687 | u->abort_request = ngx_http_fastcgi_abort_request; 688 | u->finalize_request = ngx_http_fastcgi_finalize_request; 689 | 690 | u->buffering = 1; 691 | 692 | u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t)); 693 | if (u->pipe == NULL) { 694 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 695 | } 696 | 697 | u->pipe->input_filter = ngx_http_fastcgi_input_filter; 698 | u->pipe->input_ctx = r; 699 | // end 700 | 701 | rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init); 702 | 703 | if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { 704 | return rc; 705 | } 706 | 707 | return NGX_DONE; 708 | } 709 | 710 | 711 | static ngx_int_t 712 | ngx_http_fastcgi_eval(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf) 713 | { 714 | ngx_url_t u; 715 | 716 | ngx_memzero(&u, sizeof(ngx_url_t)); 717 | 718 | if (ngx_http_script_run(r, &u.url, flcf->fastcgi_lengths->elts, 0, 719 | flcf->fastcgi_values->elts) 720 | == NULL) 721 | { 722 | return NGX_ERROR; 723 | } 724 | 725 | u.no_resolve = 1; 726 | 727 | if (ngx_parse_url(r->pool, &u) != NGX_OK) { 728 | if (u.err) { 729 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 730 | "%s in upstream \"%V\"", u.err, &u.url); 731 | } 732 | 733 | return NGX_ERROR; 734 | } 735 | 736 | if (u.no_port) { 737 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 738 | "no port in upstream \"%V\"", &u.url); 739 | return NGX_ERROR; 740 | } 741 | 742 | r->upstream->resolved = ngx_pcalloc(r->pool, 743 | sizeof(ngx_http_upstream_resolved_t)); 744 | if (r->upstream->resolved == NULL) { 745 | return NGX_ERROR; 746 | } 747 | 748 | if (u.addrs && u.addrs[0].sockaddr) { 749 | r->upstream->resolved->sockaddr = u.addrs[0].sockaddr; 750 | r->upstream->resolved->socklen = u.addrs[0].socklen; 751 | r->upstream->resolved->naddrs = 1; 752 | r->upstream->resolved->host = u.addrs[0].name; 753 | 754 | } else { 755 | r->upstream->resolved->host = u.host; 756 | r->upstream->resolved->port = u.port; 757 | } 758 | 759 | return NGX_OK; 760 | } 761 | 762 | 763 | #if (NGX_HTTP_CACHE) 764 | 765 | static ngx_int_t 766 | ngx_http_fastcgi_create_key(ngx_http_request_t *r) 767 | { 768 | ngx_str_t *key; 769 | ngx_http_fastcgi_loc_conf_t *flcf; 770 | 771 | key = ngx_array_push(&r->cache->keys); 772 | if (key == NULL) { 773 | return NGX_ERROR; 774 | } 775 | 776 | flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); 777 | 778 | if (ngx_http_complex_value(r, &flcf->cache_key, key) != NGX_OK) { 779 | return NGX_ERROR; 780 | } 781 | 782 | return NGX_OK; 783 | } 784 | 785 | #endif 786 | 787 | 788 | static ngx_int_t 789 | ngx_http_fastcgi_create_request(ngx_http_request_t *r) 790 | { 791 | off_t file_pos; 792 | u_char ch, *pos; 793 | size_t size, len, key_len, val_len, padding; 794 | ngx_uint_t i, n, next; 795 | ngx_buf_t *b; 796 | ngx_chain_t *cl, *body; 797 | ngx_list_part_t *part; 798 | ngx_table_elt_t *header; 799 | ngx_http_script_code_pt code; 800 | ngx_http_script_engine_t e, le; 801 | ngx_http_fastcgi_header_t *h; 802 | ngx_http_fastcgi_loc_conf_t *flcf; 803 | ngx_http_script_len_code_pt lcode; 804 | ngx_http_fastcgi_ctx_t *f; 805 | 806 | f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module); 807 | //DLOG(r, "create rid %d", f->rid); 808 | 809 | len = 0; 810 | 811 | flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); 812 | 813 | if (flcf->params_len) { 814 | ngx_memzero(&le, sizeof(ngx_http_script_engine_t)); 815 | 816 | ngx_http_script_flush_no_cacheable_variables(r, flcf->flushes); 817 | le.flushed = 1; 818 | 819 | le.ip = flcf->params_len->elts; 820 | le.request = r; 821 | 822 | while (*(uintptr_t *) le.ip) { 823 | 824 | lcode = *(ngx_http_script_len_code_pt *) le.ip; 825 | key_len = lcode(&le); 826 | 827 | for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { 828 | lcode = *(ngx_http_script_len_code_pt *) le.ip; 829 | } 830 | le.ip += sizeof(uintptr_t); 831 | 832 | len += 1 + key_len + ((val_len > 127) ? 4 : 1) + val_len; 833 | } 834 | } 835 | 836 | if (flcf->upstream.pass_request_headers) { 837 | 838 | part = &r->headers_in.headers.part; 839 | header = part->elts; 840 | 841 | for (i = 0; /* void */; i++) { 842 | 843 | if (i >= part->nelts) { 844 | if (part->next == NULL) { 845 | break; 846 | } 847 | 848 | part = part->next; 849 | header = part->elts; 850 | i = 0; 851 | } 852 | 853 | len += ((sizeof("HTTP_") - 1 + header[i].key.len > 127) ? 4 : 1) 854 | + ((header[i].value.len > 127) ? 4 : 1) 855 | + sizeof("HTTP_") - 1 + header[i].key.len + header[i].value.len; 856 | } 857 | } 858 | 859 | 860 | if (len > 65535) { 861 | ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, 862 | "fastcgi request record is too big: %uz", len); 863 | return NGX_ERROR; 864 | } 865 | 866 | 867 | padding = 8 - len % 8; 868 | padding = (padding == 8) ? 0 : padding; 869 | 870 | 871 | size = sizeof(ngx_http_fastcgi_header_t) 872 | + sizeof(ngx_http_fastcgi_begin_request_t) 873 | 874 | + sizeof(ngx_http_fastcgi_header_t) /* NGX_HTTP_FASTCGI_PARAMS */ 875 | + len + padding 876 | + sizeof(ngx_http_fastcgi_header_t) /* NGX_HTTP_FASTCGI_PARAMS */ 877 | 878 | + sizeof(ngx_http_fastcgi_header_t); /* NGX_HTTP_FASTCGI_STDIN */ 879 | 880 | 881 | b = ngx_create_temp_buf(r->pool, size); 882 | if (b == NULL) { 883 | return NGX_ERROR; 884 | } 885 | 886 | cl = ngx_alloc_chain_link(r->pool); 887 | if (cl == NULL) { 888 | return NGX_ERROR; 889 | } 890 | 891 | cl->buf = b; 892 | 893 | ngx_memcpy(b->pos, &ngx_http_fastcgi_request_start, 894 | sizeof(ngx_http_fastcgi_request_start_t)); 895 | 896 | ((ngx_http_fastcgi_request_start_t *)b->pos)->h0.request_id_hi = 897 | (u_char) ((f->rid >> 8) & 0xff); 898 | ((ngx_http_fastcgi_request_start_t *)b->pos)->h0.request_id_lo = 899 | (u_char) (f->rid & 0xff); 900 | 901 | h = (ngx_http_fastcgi_header_t *) 902 | (b->pos + sizeof(ngx_http_fastcgi_header_t) 903 | + sizeof(ngx_http_fastcgi_begin_request_t)); 904 | 905 | h->content_length_hi = (u_char) ((len >> 8) & 0xff); 906 | h->content_length_lo = (u_char) (len & 0xff); 907 | h->padding_length = (u_char) padding; 908 | h->reserved = 0; 909 | 910 | b->last = b->pos + sizeof(ngx_http_fastcgi_header_t) 911 | + sizeof(ngx_http_fastcgi_begin_request_t) 912 | + sizeof(ngx_http_fastcgi_header_t); 913 | 914 | 915 | if (flcf->params_len) { 916 | ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); 917 | 918 | e.ip = flcf->params->elts; 919 | e.pos = b->last; 920 | e.request = r; 921 | e.flushed = 1; 922 | 923 | le.ip = flcf->params_len->elts; 924 | 925 | while (*(uintptr_t *) le.ip) { 926 | 927 | lcode = *(ngx_http_script_len_code_pt *) le.ip; 928 | key_len = (u_char) lcode(&le); 929 | 930 | for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { 931 | lcode = *(ngx_http_script_len_code_pt *) le.ip; 932 | } 933 | le.ip += sizeof(uintptr_t); 934 | 935 | *e.pos++ = (u_char) key_len; 936 | 937 | if (val_len > 127) { 938 | *e.pos++ = (u_char) (((val_len >> 24) & 0x7f) | 0x80); 939 | *e.pos++ = (u_char) ((val_len >> 16) & 0xff); 940 | *e.pos++ = (u_char) ((val_len >> 8) & 0xff); 941 | *e.pos++ = (u_char) (val_len & 0xff); 942 | 943 | } else { 944 | *e.pos++ = (u_char) val_len; 945 | } 946 | 947 | while (*(uintptr_t *) e.ip) { 948 | code = *(ngx_http_script_code_pt *) e.ip; 949 | code((ngx_http_script_engine_t *) &e); 950 | } 951 | e.ip += sizeof(uintptr_t); 952 | 953 | ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 954 | "fastcgi param: \"%*s: %*s\"", 955 | key_len, e.pos - (key_len + val_len), 956 | val_len, e.pos - val_len); 957 | } 958 | 959 | b->last = e.pos; 960 | } 961 | 962 | 963 | if (flcf->upstream.pass_request_headers) { 964 | 965 | part = &r->headers_in.headers.part; 966 | header = part->elts; 967 | 968 | for (i = 0; /* void */; i++) { 969 | 970 | if (i >= part->nelts) { 971 | if (part->next == NULL) { 972 | break; 973 | } 974 | 975 | part = part->next; 976 | header = part->elts; 977 | i = 0; 978 | } 979 | 980 | len = sizeof("HTTP_") - 1 + header[i].key.len; 981 | if (len > 127) { 982 | *b->last++ = (u_char) (((len >> 24) & 0x7f) | 0x80); 983 | *b->last++ = (u_char) ((len >> 16) & 0xff); 984 | *b->last++ = (u_char) ((len >> 8) & 0xff); 985 | *b->last++ = (u_char) (len & 0xff); 986 | 987 | } else { 988 | *b->last++ = (u_char) len; 989 | } 990 | 991 | len = header[i].value.len; 992 | if (len > 127) { 993 | *b->last++ = (u_char) (((len >> 24) & 0x7f) | 0x80); 994 | *b->last++ = (u_char) ((len >> 16) & 0xff); 995 | *b->last++ = (u_char) ((len >> 8) & 0xff); 996 | *b->last++ = (u_char) (len & 0xff); 997 | 998 | } else { 999 | *b->last++ = (u_char) len; 1000 | } 1001 | 1002 | b->last = ngx_cpymem(b->last, "HTTP_", sizeof("HTTP_") - 1); 1003 | 1004 | for (n = 0; n < header[i].key.len; n++) { 1005 | ch = header[i].key.data[n]; 1006 | 1007 | if (ch >= 'a' && ch <= 'z') { 1008 | ch &= ~0x20; 1009 | 1010 | } else if (ch == '-') { 1011 | ch = '_'; 1012 | } 1013 | 1014 | *b->last++ = ch; 1015 | } 1016 | 1017 | b->last = ngx_copy(b->last, header[i].value.data, 1018 | header[i].value.len); 1019 | } 1020 | } 1021 | 1022 | 1023 | if (padding) { 1024 | ngx_memzero(b->last, padding); 1025 | b->last += padding; 1026 | } 1027 | 1028 | 1029 | h = (ngx_http_fastcgi_header_t *) b->last; 1030 | b->last += sizeof(ngx_http_fastcgi_header_t); 1031 | 1032 | h->version = 1; 1033 | h->type = NGX_HTTP_FASTCGI_PARAMS; 1034 | 1035 | h->request_id_hi = (u_char) ((f->rid >> 8) & 0xff); 1036 | h->request_id_lo = (u_char) (f->rid & 0xff); 1037 | 1038 | h->content_length_hi = 0; 1039 | h->content_length_lo = 0; 1040 | h->padding_length = 0; 1041 | h->reserved = 0; 1042 | 1043 | h = (ngx_http_fastcgi_header_t *) b->last; 1044 | b->last += sizeof(ngx_http_fastcgi_header_t); 1045 | 1046 | if (flcf->upstream.pass_request_body) { 1047 | body = r->upstream->request_bufs; 1048 | r->upstream->request_bufs = cl; 1049 | 1050 | #if (NGX_SUPPRESS_WARN) 1051 | file_pos = 0; 1052 | pos = NULL; 1053 | #endif 1054 | 1055 | while (body) { 1056 | 1057 | if (body->buf->in_file) { 1058 | file_pos = body->buf->file_pos; 1059 | 1060 | } else { 1061 | pos = body->buf->pos; 1062 | } 1063 | 1064 | next = 0; 1065 | 1066 | do { 1067 | b = ngx_alloc_buf(r->pool); 1068 | if (b == NULL) { 1069 | return NGX_ERROR; 1070 | } 1071 | 1072 | ngx_memcpy(b, body->buf, sizeof(ngx_buf_t)); 1073 | 1074 | if (body->buf->in_file) { 1075 | b->file_pos = file_pos; 1076 | file_pos += 32 * 1024; 1077 | 1078 | if (file_pos >= body->buf->file_last) { 1079 | file_pos = body->buf->file_last; 1080 | next = 1; 1081 | } 1082 | 1083 | b->file_last = file_pos; 1084 | len = (ngx_uint_t) (file_pos - b->file_pos); 1085 | 1086 | } else { 1087 | b->pos = pos; 1088 | pos += 32 * 1024; 1089 | 1090 | if (pos >= body->buf->last) { 1091 | pos = body->buf->last; 1092 | next = 1; 1093 | } 1094 | 1095 | b->last = pos; 1096 | len = (ngx_uint_t) (pos - b->pos); 1097 | } 1098 | 1099 | padding = 8 - len % 8; 1100 | padding = (padding == 8) ? 0 : padding; 1101 | 1102 | h->version = 1; 1103 | h->type = NGX_HTTP_FASTCGI_STDIN; 1104 | h->request_id_hi = (u_char) ((f->rid >> 8) & 0xff); 1105 | h->request_id_lo = (u_char) (f->rid & 0xff); 1106 | h->content_length_hi = (u_char) ((len >> 8) & 0xff); 1107 | h->content_length_lo = (u_char) (len & 0xff); 1108 | h->padding_length = (u_char) padding; 1109 | h->reserved = 0; 1110 | 1111 | cl->next = ngx_alloc_chain_link(r->pool); 1112 | if (cl->next == NULL) { 1113 | return NGX_ERROR; 1114 | } 1115 | 1116 | cl = cl->next; 1117 | cl->buf = b; 1118 | 1119 | b = ngx_create_temp_buf(r->pool, 1120 | sizeof(ngx_http_fastcgi_header_t) 1121 | + padding); 1122 | if (b == NULL) { 1123 | return NGX_ERROR; 1124 | } 1125 | 1126 | if (padding) { 1127 | ngx_memzero(b->last, padding); 1128 | b->last += padding; 1129 | } 1130 | 1131 | h = (ngx_http_fastcgi_header_t *) b->last; 1132 | b->last += sizeof(ngx_http_fastcgi_header_t); 1133 | 1134 | cl->next = ngx_alloc_chain_link(r->pool); 1135 | if (cl->next == NULL) { 1136 | return NGX_ERROR; 1137 | } 1138 | 1139 | cl = cl->next; 1140 | cl->buf = b; 1141 | 1142 | } while (!next); 1143 | 1144 | body = body->next; 1145 | } 1146 | 1147 | } else { 1148 | r->upstream->request_bufs = cl; 1149 | } 1150 | 1151 | h->version = 1; 1152 | h->type = NGX_HTTP_FASTCGI_STDIN; 1153 | h->request_id_hi = (u_char) ((f->rid >> 8) & 0xff); 1154 | h->request_id_lo = (u_char) (f->rid & 0xff); 1155 | h->content_length_hi = 0; 1156 | h->content_length_lo = 0; 1157 | h->padding_length = 0; 1158 | h->reserved = 0; 1159 | 1160 | cl->next = NULL; 1161 | 1162 | return NGX_OK; 1163 | } 1164 | 1165 | 1166 | static ngx_int_t 1167 | ngx_http_fastcgi_reinit_request(ngx_http_request_t *r) 1168 | { 1169 | ngx_http_fastcgi_ctx_t *f; 1170 | 1171 | f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module); 1172 | 1173 | if (f == NULL) { 1174 | return NGX_OK; 1175 | } 1176 | 1177 | DLOG(r, "reinit rid %d", f->rid); 1178 | 1179 | f->state = ngx_http_fastcgi_st_version; 1180 | f->fastcgi_stdout = 0; 1181 | f->rid = 0; 1182 | 1183 | return NGX_OK; 1184 | } 1185 | 1186 | 1187 | static ngx_int_t 1188 | ngx_http_fastcgi_process_header(ngx_http_request_t *r) 1189 | { 1190 | u_char *p, *msg, *start, *last, 1191 | *part_start, *part_end; 1192 | size_t size; 1193 | ngx_str_t *status_line, *pattern; 1194 | ngx_int_t rc, status; 1195 | ngx_buf_t buf; 1196 | ngx_uint_t i; 1197 | ngx_table_elt_t *h; 1198 | ngx_http_upstream_t *u; 1199 | ngx_http_fastcgi_ctx_t *f; 1200 | ngx_http_upstream_header_t *hh; 1201 | ngx_http_fastcgi_loc_conf_t *flcf; 1202 | ngx_http_fastcgi_split_part_t *part; 1203 | ngx_http_upstream_main_conf_t *umcf; 1204 | 1205 | f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module); 1206 | 1207 | umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); 1208 | 1209 | u = r->upstream; 1210 | 1211 | for ( ;; ) { 1212 | 1213 | if (f->state < ngx_http_fastcgi_st_data) { 1214 | 1215 | f->pos = u->buffer.pos; 1216 | f->last = u->buffer.last; 1217 | 1218 | rc = ngx_http_fastcgi_process_record(r, f); 1219 | 1220 | u->buffer.pos = f->pos; 1221 | u->buffer.last = f->last; 1222 | 1223 | if (rc == NGX_AGAIN) { 1224 | return NGX_AGAIN; 1225 | } 1226 | 1227 | if (rc == NGX_ERROR) { 1228 | return NGX_HTTP_UPSTREAM_INVALID_HEADER; 1229 | } 1230 | 1231 | if (f->type != NGX_HTTP_FASTCGI_STDOUT 1232 | && f->type != NGX_HTTP_FASTCGI_STDERR) 1233 | { 1234 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 1235 | "upstream sent unexpected FastCGI record: %d", 1236 | f->type); 1237 | 1238 | return NGX_HTTP_UPSTREAM_INVALID_HEADER; 1239 | } 1240 | 1241 | if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) { 1242 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 1243 | "upstream closed prematurely FastCGI stdout"); 1244 | 1245 | return NGX_HTTP_UPSTREAM_INVALID_HEADER; 1246 | } 1247 | } 1248 | 1249 | if (f->state == ngx_http_fastcgi_st_padding) { 1250 | 1251 | if (u->buffer.pos + f->padding < u->buffer.last) { 1252 | f->state = ngx_http_fastcgi_st_version; 1253 | u->buffer.pos += f->padding; 1254 | 1255 | continue; 1256 | } 1257 | 1258 | if (u->buffer.pos + f->padding == u->buffer.last) { 1259 | f->state = ngx_http_fastcgi_st_version; 1260 | u->buffer.pos = u->buffer.last; 1261 | 1262 | return NGX_AGAIN; 1263 | } 1264 | 1265 | f->padding -= u->buffer.last - u->buffer.pos; 1266 | u->buffer.pos = u->buffer.last; 1267 | 1268 | return NGX_AGAIN; 1269 | } 1270 | 1271 | 1272 | /* f->state == ngx_http_fastcgi_st_data */ 1273 | 1274 | if (f->type == NGX_HTTP_FASTCGI_STDERR) { 1275 | 1276 | if (f->length) { 1277 | msg = u->buffer.pos; 1278 | 1279 | if (u->buffer.pos + f->length <= u->buffer.last) { 1280 | u->buffer.pos += f->length; 1281 | f->length = 0; 1282 | f->state = ngx_http_fastcgi_st_padding; 1283 | 1284 | } else { 1285 | f->length -= u->buffer.last - u->buffer.pos; 1286 | u->buffer.pos = u->buffer.last; 1287 | } 1288 | 1289 | for (p = u->buffer.pos - 1; msg < p; p--) { 1290 | if (*p != LF && *p != CR && *p != '.' && *p != ' ') { 1291 | break; 1292 | } 1293 | } 1294 | 1295 | p++; 1296 | 1297 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 1298 | "FastCGI sent in stderr: \"%*s\"", p - msg, msg); 1299 | 1300 | flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); 1301 | 1302 | if (flcf->catch_stderr) { 1303 | pattern = flcf->catch_stderr->elts; 1304 | 1305 | for (i = 0; i < flcf->catch_stderr->nelts; i++) { 1306 | if (ngx_strnstr(msg, (char *) pattern[i].data, 1307 | p - msg) 1308 | != NULL) 1309 | { 1310 | return NGX_HTTP_UPSTREAM_INVALID_HEADER; 1311 | } 1312 | } 1313 | } 1314 | 1315 | if (u->buffer.pos == u->buffer.last) { 1316 | 1317 | if (!f->fastcgi_stdout) { 1318 | 1319 | /* 1320 | * the special handling the large number 1321 | * of the PHP warnings to not allocate memory 1322 | */ 1323 | 1324 | u->buffer.pos = u->buffer.start; 1325 | u->buffer.last = u->buffer.start; 1326 | } 1327 | 1328 | return NGX_AGAIN; 1329 | } 1330 | 1331 | } else { 1332 | f->state = ngx_http_fastcgi_st_version; 1333 | } 1334 | 1335 | continue; 1336 | } 1337 | 1338 | 1339 | /* f->type == NGX_HTTP_FASTCGI_STDOUT */ 1340 | 1341 | f->fastcgi_stdout = 1; 1342 | 1343 | start = u->buffer.pos; 1344 | 1345 | if (u->buffer.pos + f->length < u->buffer.last) { 1346 | 1347 | /* 1348 | * set u->buffer.last to the end of the FastCGI record data 1349 | * for ngx_http_parse_header_line() 1350 | */ 1351 | 1352 | last = u->buffer.last; 1353 | u->buffer.last = u->buffer.pos + f->length; 1354 | 1355 | } else { 1356 | last = NULL; 1357 | } 1358 | 1359 | for ( ;; ) { 1360 | 1361 | part_start = u->buffer.pos; 1362 | part_end = u->buffer.last; 1363 | 1364 | rc = ngx_http_parse_header_line(r, &u->buffer, 1); 1365 | 1366 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1367 | "http fastcgi parser: %d", rc); 1368 | 1369 | if (rc == NGX_AGAIN) { 1370 | break; 1371 | } 1372 | 1373 | if (rc == NGX_OK) { 1374 | 1375 | /* a header line has been parsed successfully */ 1376 | 1377 | h = ngx_list_push(&u->headers_in.headers); 1378 | if (h == NULL) { 1379 | return NGX_ERROR; 1380 | } 1381 | 1382 | if (f->split_parts && f->split_parts->nelts) { 1383 | 1384 | part = f->split_parts->elts; 1385 | size = u->buffer.pos - part_start; 1386 | 1387 | for (i = 0; i < f->split_parts->nelts; i++) { 1388 | size += part[i].end - part[i].start; 1389 | } 1390 | 1391 | p = ngx_pnalloc(r->pool, size); 1392 | if (p == NULL) { 1393 | return NGX_ERROR; 1394 | } 1395 | 1396 | buf.pos = p; 1397 | 1398 | for (i = 0; i < f->split_parts->nelts; i++) { 1399 | p = ngx_cpymem(p, part[i].start, 1400 | part[i].end - part[i].start); 1401 | } 1402 | 1403 | p = ngx_cpymem(p, part_start, u->buffer.pos - part_start); 1404 | 1405 | buf.last = p; 1406 | 1407 | f->split_parts->nelts = 0; 1408 | 1409 | rc = ngx_http_parse_header_line(r, &buf, 1); 1410 | 1411 | h->key.len = r->header_name_end - r->header_name_start; 1412 | h->key.data = r->header_name_start; 1413 | h->key.data[h->key.len] = '\0'; 1414 | 1415 | h->value.len = r->header_end - r->header_start; 1416 | h->value.data = r->header_start; 1417 | h->value.data[h->value.len] = '\0'; 1418 | 1419 | h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); 1420 | if (h->lowcase_key == NULL) { 1421 | return NGX_ERROR; 1422 | } 1423 | 1424 | } else { 1425 | 1426 | h->key.len = r->header_name_end - r->header_name_start; 1427 | h->value.len = r->header_end - r->header_start; 1428 | 1429 | h->key.data = ngx_pnalloc(r->pool, 1430 | h->key.len + 1 + h->value.len + 1 1431 | + h->key.len); 1432 | if (h->key.data == NULL) { 1433 | return NGX_ERROR; 1434 | } 1435 | 1436 | h->value.data = h->key.data + h->key.len + 1; 1437 | h->lowcase_key = h->key.data + h->key.len + 1 1438 | + h->value.len + 1; 1439 | 1440 | ngx_cpystrn(h->key.data, r->header_name_start, 1441 | h->key.len + 1); 1442 | ngx_cpystrn(h->value.data, r->header_start, 1443 | h->value.len + 1); 1444 | } 1445 | 1446 | h->hash = r->header_hash; 1447 | 1448 | if (h->key.len == r->lowcase_index) { 1449 | ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len); 1450 | 1451 | } else { 1452 | ngx_strlow(h->lowcase_key, h->key.data, h->key.len); 1453 | } 1454 | 1455 | hh = ngx_hash_find(&umcf->headers_in_hash, h->hash, 1456 | h->lowcase_key, h->key.len); 1457 | 1458 | if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { 1459 | return NGX_ERROR; 1460 | } 1461 | 1462 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1463 | "http fastcgi header: \"%V: %V\"", 1464 | &h->key, &h->value); 1465 | 1466 | if (u->buffer.pos < u->buffer.last) { 1467 | continue; 1468 | } 1469 | 1470 | /* the end of the FastCGI record */ 1471 | 1472 | break; 1473 | } 1474 | 1475 | if (rc == NGX_HTTP_PARSE_HEADER_DONE) { 1476 | 1477 | /* a whole header has been parsed successfully */ 1478 | 1479 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1480 | "http fastcgi header done"); 1481 | 1482 | if (u->headers_in.status) { 1483 | status_line = &u->headers_in.status->value; 1484 | 1485 | status = ngx_atoi(status_line->data, 3); 1486 | 1487 | if (status == NGX_ERROR) { 1488 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 1489 | "upstream sent invalid status \"%V\"", 1490 | status_line); 1491 | return NGX_HTTP_UPSTREAM_INVALID_HEADER; 1492 | } 1493 | 1494 | u->headers_in.status_n = status; 1495 | u->headers_in.status_line = *status_line; 1496 | 1497 | } else if (u->headers_in.location) { 1498 | u->headers_in.status_n = 302; 1499 | u->headers_in.status_line.len = 1500 | sizeof("302 Moved Temporarily") - 1; 1501 | u->headers_in.status_line.data = 1502 | (u_char *) "302 Moved Temporarily"; 1503 | 1504 | } else { 1505 | u->headers_in.status_n = 200; 1506 | u->headers_in.status_line.len = sizeof("200 OK") - 1; 1507 | u->headers_in.status_line.data = (u_char *) "200 OK"; 1508 | } 1509 | 1510 | if (u->state) { 1511 | u->state->status = u->headers_in.status_n; 1512 | } 1513 | 1514 | break; 1515 | } 1516 | 1517 | /* there was error while a header line parsing */ 1518 | 1519 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 1520 | "upstream sent invalid header"); 1521 | 1522 | return NGX_HTTP_UPSTREAM_INVALID_HEADER; 1523 | } 1524 | 1525 | if (last) { 1526 | u->buffer.last = last; 1527 | } 1528 | 1529 | f->length -= u->buffer.pos - start; 1530 | 1531 | if (f->length == 0) { 1532 | if (f->padding) { 1533 | f->state = ngx_http_fastcgi_st_padding; 1534 | } else { 1535 | f->state = ngx_http_fastcgi_st_version; 1536 | } 1537 | } 1538 | 1539 | if (rc == NGX_HTTP_PARSE_HEADER_DONE) { 1540 | return NGX_OK; 1541 | } 1542 | 1543 | if (rc == NGX_OK) { 1544 | continue; 1545 | } 1546 | 1547 | /* rc == NGX_AGAIN */ 1548 | 1549 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1550 | "upstream split a header line in FastCGI records"); 1551 | 1552 | if (f->split_parts == NULL) { 1553 | f->split_parts = ngx_array_create(r->pool, 1, 1554 | sizeof(ngx_http_fastcgi_split_part_t)); 1555 | if (f->split_parts == NULL) { 1556 | return NGX_ERROR; 1557 | } 1558 | } 1559 | 1560 | part = ngx_array_push(f->split_parts); 1561 | 1562 | part->start = part_start; 1563 | part->end = part_end; 1564 | 1565 | if (u->buffer.pos < u->buffer.last) { 1566 | continue; 1567 | } 1568 | 1569 | return NGX_AGAIN; 1570 | } 1571 | } 1572 | 1573 | 1574 | static ngx_int_t 1575 | ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) 1576 | { 1577 | u_char *m, *msg; 1578 | ngx_int_t rc; 1579 | ngx_buf_t *b, **prev; 1580 | ngx_chain_t *cl; 1581 | ngx_http_request_t *r; 1582 | ngx_http_fastcgi_ctx_t *f; 1583 | 1584 | if (buf->pos == buf->last) { 1585 | return NGX_OK; 1586 | } 1587 | 1588 | r = p->input_ctx; 1589 | f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module); 1590 | 1591 | b = NULL; 1592 | prev = &buf->shadow; 1593 | 1594 | f->pos = buf->pos; 1595 | f->last = buf->last; 1596 | 1597 | for ( ;; ) { 1598 | if (f->state < ngx_http_fastcgi_st_data) { 1599 | 1600 | rc = ngx_http_fastcgi_process_record(r, f); 1601 | 1602 | if (rc == NGX_AGAIN) { 1603 | break; 1604 | } 1605 | 1606 | if (rc == NGX_ERROR) { 1607 | return NGX_ERROR; 1608 | } 1609 | 1610 | if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) { 1611 | f->state = ngx_http_fastcgi_st_version; 1612 | p->upstream_done = 1; 1613 | // only done in finalize_request: 1614 | //ngx_http_fastcgi_release_request(r, f, NULL); 1615 | 1616 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0, 1617 | "http fastcgi closed stdout"); 1618 | 1619 | continue; 1620 | } 1621 | 1622 | if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) { 1623 | f->state = ngx_http_fastcgi_st_version; 1624 | p->upstream_done = 1; 1625 | // only done in finalize_request: 1626 | //ngx_http_fastcgi_release_request(r, f, NULL); 1627 | 1628 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0, 1629 | "http fastcgi sent end request"); 1630 | 1631 | break; 1632 | } 1633 | } 1634 | 1635 | 1636 | if (f->state == ngx_http_fastcgi_st_padding) { 1637 | 1638 | if (f->pos + f->padding < f->last) { 1639 | f->state = ngx_http_fastcgi_st_version; 1640 | f->pos += f->padding; 1641 | 1642 | continue; 1643 | } 1644 | 1645 | if (f->pos + f->padding == f->last) { 1646 | f->state = ngx_http_fastcgi_st_version; 1647 | 1648 | break; 1649 | } 1650 | 1651 | f->padding -= f->last - f->pos; 1652 | 1653 | break; 1654 | } 1655 | 1656 | 1657 | /* f->state == ngx_http_fastcgi_st_data */ 1658 | 1659 | if (f->type == NGX_HTTP_FASTCGI_STDERR) { 1660 | 1661 | if (f->length) { 1662 | 1663 | if (f->pos == f->last) { 1664 | break; 1665 | } 1666 | 1667 | msg = f->pos; 1668 | 1669 | if (f->pos + f->length <= f->last) { 1670 | f->pos += f->length; 1671 | f->length = 0; 1672 | f->state = ngx_http_fastcgi_st_padding; 1673 | 1674 | } else { 1675 | f->length -= f->last - f->pos; 1676 | f->pos = f->last; 1677 | } 1678 | 1679 | for (m = f->pos - 1; msg < m; m--) { 1680 | if (*m != LF && *m != CR && *m != '.' && *m != ' ') { 1681 | break; 1682 | } 1683 | } 1684 | 1685 | ngx_log_error(NGX_LOG_ERR, p->log, 0, 1686 | "FastCGI sent in stderr: \"%*s\"", 1687 | m + 1 - msg, msg); 1688 | 1689 | if (f->pos == f->last) { 1690 | break; 1691 | } 1692 | 1693 | } else { 1694 | f->state = ngx_http_fastcgi_st_version; 1695 | } 1696 | 1697 | continue; 1698 | } 1699 | 1700 | 1701 | /* f->type == NGX_HTTP_FASTCGI_STDOUT */ 1702 | 1703 | if (f->pos == f->last) { 1704 | break; 1705 | } 1706 | 1707 | if (p->free) { 1708 | b = p->free->buf; 1709 | p->free = p->free->next; 1710 | 1711 | } else { 1712 | b = ngx_alloc_buf(p->pool); 1713 | if (b == NULL) { 1714 | return NGX_ERROR; 1715 | } 1716 | } 1717 | 1718 | ngx_memzero(b, sizeof(ngx_buf_t)); 1719 | 1720 | b->pos = f->pos; 1721 | b->start = buf->start; 1722 | b->end = buf->end; 1723 | b->tag = p->tag; 1724 | b->temporary = 1; 1725 | b->recycled = 1; 1726 | 1727 | *prev = b; 1728 | prev = &b->shadow; 1729 | 1730 | cl = ngx_alloc_chain_link(p->pool); 1731 | if (cl == NULL) { 1732 | return NGX_ERROR; 1733 | } 1734 | 1735 | cl->buf = b; 1736 | cl->next = NULL; 1737 | 1738 | if (p->in) { 1739 | *p->last_in = cl; 1740 | } else { 1741 | p->in = cl; 1742 | } 1743 | p->last_in = &cl->next; 1744 | 1745 | 1746 | /* STUB */ b->num = buf->num; 1747 | 1748 | ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0, 1749 | "input buf #%d %p", b->num, b->pos); 1750 | 1751 | if (f->pos + f->length < f->last) { 1752 | 1753 | if (f->padding) { 1754 | f->state = ngx_http_fastcgi_st_padding; 1755 | } else { 1756 | f->state = ngx_http_fastcgi_st_version; 1757 | } 1758 | 1759 | f->pos += f->length; 1760 | b->last = f->pos; 1761 | 1762 | continue; 1763 | } 1764 | 1765 | if (f->pos + f->length == f->last) { 1766 | 1767 | if (f->padding) { 1768 | f->state = ngx_http_fastcgi_st_padding; 1769 | } else { 1770 | f->state = ngx_http_fastcgi_st_version; 1771 | } 1772 | 1773 | b->last = f->last; 1774 | 1775 | break; 1776 | } 1777 | 1778 | f->length -= f->last - f->pos; 1779 | 1780 | b->last = f->last; 1781 | 1782 | break; 1783 | 1784 | } 1785 | 1786 | if (b) { 1787 | b->shadow = buf; 1788 | b->last_shadow = 1; 1789 | 1790 | ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0, 1791 | "input buf %p %z", b->pos, b->last - b->pos); 1792 | 1793 | return NGX_OK; 1794 | } 1795 | 1796 | /* there is no data record in the buf, add it to free chain */ 1797 | 1798 | if (ngx_event_pipe_add_free_buf(p, buf) != NGX_OK) { 1799 | return NGX_ERROR; 1800 | } 1801 | 1802 | return NGX_OK; 1803 | } 1804 | 1805 | 1806 | static ngx_int_t 1807 | ngx_http_fastcgi_process_record(ngx_http_request_t *r, 1808 | ngx_http_fastcgi_ctx_t *f) 1809 | { 1810 | u_char ch, *p; 1811 | //u_char rid_hi; 1812 | ngx_http_fastcgi_state_e state; 1813 | 1814 | state = f->state; 1815 | 1816 | for (p = f->pos; p < f->last; p++) { 1817 | 1818 | ch = *p; 1819 | 1820 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1821 | "http fastcgi record byte: %02Xd", ch); 1822 | 1823 | switch (state) { 1824 | 1825 | case ngx_http_fastcgi_st_version: 1826 | if (ch != 1) { 1827 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 1828 | "upstream sent unsupported FastCGI " 1829 | "protocol version: %d", ch); 1830 | return NGX_ERROR; 1831 | } 1832 | state = ngx_http_fastcgi_st_type; 1833 | break; 1834 | 1835 | case ngx_http_fastcgi_st_type: 1836 | switch (ch) { 1837 | case NGX_HTTP_FASTCGI_STDOUT: 1838 | case NGX_HTTP_FASTCGI_STDERR: 1839 | case NGX_HTTP_FASTCGI_END_REQUEST: 1840 | f->type = (ngx_uint_t) ch; 1841 | break; 1842 | default: 1843 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 1844 | "upstream sent invalid FastCGI " 1845 | "record type: %d", ch); 1846 | return NGX_ERROR; 1847 | 1848 | } 1849 | state = ngx_http_fastcgi_st_request_id_hi; 1850 | break; 1851 | 1852 | /* we support the single request per connection */ 1853 | 1854 | case ngx_http_fastcgi_st_request_id_hi: 1855 | //rid_hi = ch; 1856 | state = ngx_http_fastcgi_st_request_id_lo; 1857 | break; 1858 | 1859 | case ngx_http_fastcgi_st_request_id_lo: 1860 | //DLOG(r, "r rid %d, rec rid %d", f->rid, (rid_hi << 8) + ch); 1861 | state = ngx_http_fastcgi_st_content_length_hi; 1862 | break; 1863 | 1864 | case ngx_http_fastcgi_st_content_length_hi: 1865 | f->length = ch << 8; 1866 | state = ngx_http_fastcgi_st_content_length_lo; 1867 | break; 1868 | 1869 | case ngx_http_fastcgi_st_content_length_lo: 1870 | f->length |= (size_t) ch; 1871 | state = ngx_http_fastcgi_st_padding_length; 1872 | break; 1873 | 1874 | case ngx_http_fastcgi_st_padding_length: 1875 | f->padding = (size_t) ch; 1876 | state = ngx_http_fastcgi_st_reserved; 1877 | break; 1878 | 1879 | case ngx_http_fastcgi_st_reserved: 1880 | state = ngx_http_fastcgi_st_data; 1881 | 1882 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1883 | "http fastcgi record length: %z", f->length); 1884 | 1885 | f->pos = p + 1; 1886 | f->state = state; 1887 | 1888 | return NGX_OK; 1889 | 1890 | /* suppress warning */ 1891 | case ngx_http_fastcgi_st_data: 1892 | case ngx_http_fastcgi_st_padding: 1893 | break; 1894 | } 1895 | } 1896 | 1897 | f->state = state; 1898 | 1899 | return NGX_AGAIN; 1900 | } 1901 | 1902 | 1903 | static void 1904 | ngx_http_fastcgi_abort_request(ngx_http_request_t *r) 1905 | { 1906 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1907 | "abort http fastcgi request"); 1908 | return; 1909 | } 1910 | 1911 | 1912 | static void 1913 | ngx_http_fastcgi_finalize_request(ngx_http_request_t *r, ngx_int_t status) 1914 | { 1915 | if (status == NGX_HTTP_CLIENT_CLOSED_REQUEST) { 1916 | DLOG(r, "ABORTED"); 1917 | ngx_http_fastcgi_header_t *h; 1918 | ngx_buf_t *b; 1919 | ngx_chain_t *cl; 1920 | size_t size; 1921 | 1922 | size = sizeof(ngx_http_fastcgi_header_t); 1923 | 1924 | b = ngx_create_temp_buf(r->pool, size); 1925 | if (b == NULL) { 1926 | return; 1927 | } 1928 | 1929 | cl = ngx_alloc_chain_link(r->pool); 1930 | if (cl == NULL) { 1931 | return; 1932 | } 1933 | 1934 | cl->buf = b; 1935 | cl->next = NULL; 1936 | 1937 | r->upstream->request_bufs = cl; 1938 | b->last = b->pos + size; 1939 | 1940 | ngx_http_fastcgi_ctx_t *f; 1941 | f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module); 1942 | 1943 | h = ((ngx_http_fastcgi_header_t *)b->pos); 1944 | h->version = 1; 1945 | h->type = NGX_HTTP_FASTCGI_ABORT_REQUEST; 1946 | h->request_id_hi = (u_char) ((f->rid >> 8) & 0xff); 1947 | h->request_id_lo = (u_char) (f->rid & 0xff); 1948 | h->content_length_hi = 0; 1949 | h->content_length_lo = 0; 1950 | h->padding_length = 0; 1951 | h->reserved = 0; 1952 | } 1953 | ngx_http_fastcgi_release_request(r, NULL, NULL); 1954 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1955 | "finalize http fastcgi request"); 1956 | 1957 | return; 1958 | } 1959 | 1960 | 1961 | static ngx_int_t 1962 | ngx_http_fastcgi_add_variables(ngx_conf_t *cf) 1963 | { 1964 | ngx_http_variable_t *var, *v; 1965 | 1966 | for (v = ngx_http_fastcgi_vars; v->name.len; v++) { 1967 | var = ngx_http_add_variable(cf, &v->name, v->flags); 1968 | if (var == NULL) { 1969 | return NGX_ERROR; 1970 | } 1971 | 1972 | var->get_handler = v->get_handler; 1973 | var->data = v->data; 1974 | } 1975 | 1976 | return NGX_OK; 1977 | } 1978 | 1979 | 1980 | static void * 1981 | ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf) 1982 | { 1983 | ngx_http_fastcgi_loc_conf_t *conf; 1984 | 1985 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fastcgi_loc_conf_t)); 1986 | if (conf == NULL) { 1987 | return NULL; 1988 | } 1989 | 1990 | int i; 1991 | for (i=0; irequests[i] = NULL; 1993 | 1994 | /* 1995 | * set by ngx_pcalloc(): 1996 | * 1997 | * conf->upstream.bufs.num = 0; 1998 | * conf->upstream.ignore_headers = 0; 1999 | * conf->upstream.next_upstream = 0; 2000 | * conf->upstream.cache_use_stale = 0; 2001 | * conf->upstream.cache_methods = 0; 2002 | * conf->upstream.temp_path = NULL; 2003 | * conf->upstream.hide_headers_hash = { NULL, 0 }; 2004 | * conf->upstream.uri = { 0, NULL }; 2005 | * conf->upstream.location = NULL; 2006 | * conf->upstream.store_lengths = NULL; 2007 | * conf->upstream.store_values = NULL; 2008 | * 2009 | * conf->index.len = 0; 2010 | * conf->index.data = NULL; 2011 | */ 2012 | 2013 | conf->upstream.store = NGX_CONF_UNSET; 2014 | conf->upstream.store_access = NGX_CONF_UNSET_UINT; 2015 | conf->upstream.buffering = NGX_CONF_UNSET; 2016 | conf->upstream.ignore_client_abort = NGX_CONF_UNSET; 2017 | 2018 | conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC; 2019 | conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC; 2020 | conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC; 2021 | 2022 | conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE; 2023 | conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE; 2024 | 2025 | conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE; 2026 | conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE; 2027 | conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE; 2028 | 2029 | conf->upstream.pass_request_headers = NGX_CONF_UNSET; 2030 | conf->upstream.pass_request_body = NGX_CONF_UNSET; 2031 | 2032 | #if (NGX_HTTP_CACHE) 2033 | conf->upstream.cache = NGX_CONF_UNSET_PTR; 2034 | conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT; 2035 | conf->upstream.cache_valid = NGX_CONF_UNSET_PTR; 2036 | #endif 2037 | 2038 | conf->upstream.hide_headers = NGX_CONF_UNSET_PTR; 2039 | conf->upstream.pass_headers = NGX_CONF_UNSET_PTR; 2040 | 2041 | conf->upstream.intercept_errors = NGX_CONF_UNSET; 2042 | 2043 | /* "fastcgi_cyclic_temp_file" is disabled */ 2044 | conf->upstream.cyclic_temp_file = 0; 2045 | 2046 | conf->catch_stderr = NGX_CONF_UNSET_PTR; 2047 | 2048 | return conf; 2049 | } 2050 | 2051 | 2052 | static char * 2053 | ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) 2054 | { 2055 | ngx_http_fastcgi_loc_conf_t *prev = parent; 2056 | ngx_http_fastcgi_loc_conf_t *conf = child; 2057 | 2058 | u_char *p; 2059 | size_t size; 2060 | uintptr_t *code; 2061 | ngx_str_t *h; 2062 | ngx_uint_t i; 2063 | ngx_keyval_t *src; 2064 | ngx_hash_init_t hash; 2065 | ngx_http_script_compile_t sc; 2066 | ngx_http_script_copy_code_t *copy; 2067 | 2068 | if (conf->upstream.store != 0) { 2069 | ngx_conf_merge_value(conf->upstream.store, 2070 | prev->upstream.store, 0); 2071 | 2072 | if (conf->upstream.store_lengths == NULL) { 2073 | conf->upstream.store_lengths = prev->upstream.store_lengths; 2074 | conf->upstream.store_values = prev->upstream.store_values; 2075 | } 2076 | } 2077 | 2078 | ngx_conf_merge_uint_value(conf->upstream.store_access, 2079 | prev->upstream.store_access, 0600); 2080 | 2081 | ngx_conf_merge_value(conf->upstream.buffering, 2082 | prev->upstream.buffering, 1); 2083 | 2084 | ngx_conf_merge_value(conf->upstream.ignore_client_abort, 2085 | prev->upstream.ignore_client_abort, 0); 2086 | 2087 | ngx_conf_merge_msec_value(conf->upstream.connect_timeout, 2088 | prev->upstream.connect_timeout, 60000); 2089 | 2090 | ngx_conf_merge_msec_value(conf->upstream.send_timeout, 2091 | prev->upstream.send_timeout, 60000); 2092 | 2093 | ngx_conf_merge_msec_value(conf->upstream.read_timeout, 2094 | prev->upstream.read_timeout, 60000); 2095 | 2096 | ngx_conf_merge_size_value(conf->upstream.send_lowat, 2097 | prev->upstream.send_lowat, 0); 2098 | 2099 | ngx_conf_merge_size_value(conf->upstream.buffer_size, 2100 | prev->upstream.buffer_size, 2101 | (size_t) ngx_pagesize); 2102 | 2103 | 2104 | ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs, 2105 | 8, ngx_pagesize); 2106 | 2107 | if (conf->upstream.bufs.num < 2) { 2108 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 2109 | "there must be at least 2 \"fastcgi_buffers\""); 2110 | return NGX_CONF_ERROR; 2111 | } 2112 | 2113 | 2114 | size = conf->upstream.buffer_size; 2115 | if (size < conf->upstream.bufs.size) { 2116 | size = conf->upstream.bufs.size; 2117 | } 2118 | 2119 | 2120 | ngx_conf_merge_size_value(conf->upstream.busy_buffers_size_conf, 2121 | prev->upstream.busy_buffers_size_conf, 2122 | NGX_CONF_UNSET_SIZE); 2123 | 2124 | if (conf->upstream.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) { 2125 | conf->upstream.busy_buffers_size = 2 * size; 2126 | } else { 2127 | conf->upstream.busy_buffers_size = 2128 | conf->upstream.busy_buffers_size_conf; 2129 | } 2130 | 2131 | if (conf->upstream.busy_buffers_size < size) { 2132 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 2133 | "\"fastcgi_busy_buffers_size\" must be equal or bigger than " 2134 | "maximum of the value of \"fastcgi_buffer_size\" and " 2135 | "one of the \"fastcgi_buffers\""); 2136 | 2137 | return NGX_CONF_ERROR; 2138 | } 2139 | 2140 | if (conf->upstream.busy_buffers_size 2141 | > (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size) 2142 | { 2143 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 2144 | "\"fastcgi_busy_buffers_size\" must be less than " 2145 | "the size of all \"fastcgi_buffers\" minus one buffer"); 2146 | 2147 | return NGX_CONF_ERROR; 2148 | } 2149 | 2150 | 2151 | ngx_conf_merge_size_value(conf->upstream.temp_file_write_size_conf, 2152 | prev->upstream.temp_file_write_size_conf, 2153 | NGX_CONF_UNSET_SIZE); 2154 | 2155 | if (conf->upstream.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) { 2156 | conf->upstream.temp_file_write_size = 2 * size; 2157 | } else { 2158 | conf->upstream.temp_file_write_size = 2159 | conf->upstream.temp_file_write_size_conf; 2160 | } 2161 | 2162 | if (conf->upstream.temp_file_write_size < size) { 2163 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 2164 | "\"fastcgi_temp_file_write_size\" must be equal or bigger than " 2165 | "maximum of the value of \"fastcgi_buffer_size\" and " 2166 | "one of the \"fastcgi_buffers\""); 2167 | 2168 | return NGX_CONF_ERROR; 2169 | } 2170 | 2171 | 2172 | ngx_conf_merge_size_value(conf->upstream.max_temp_file_size_conf, 2173 | prev->upstream.max_temp_file_size_conf, 2174 | NGX_CONF_UNSET_SIZE); 2175 | 2176 | if (conf->upstream.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) { 2177 | conf->upstream.max_temp_file_size = 1024 * 1024 * 1024; 2178 | } else { 2179 | conf->upstream.max_temp_file_size = 2180 | conf->upstream.max_temp_file_size_conf; 2181 | } 2182 | 2183 | if (conf->upstream.max_temp_file_size != 0 2184 | && conf->upstream.max_temp_file_size < size) 2185 | { 2186 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 2187 | "\"fastcgi_max_temp_file_size\" must be equal to zero to disable " 2188 | "the temporary files usage or must be equal or bigger than " 2189 | "maximum of the value of \"fastcgi_buffer_size\" and " 2190 | "one of the \"fastcgi_buffers\""); 2191 | 2192 | return NGX_CONF_ERROR; 2193 | } 2194 | 2195 | 2196 | ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers, 2197 | prev->upstream.ignore_headers, 2198 | NGX_CONF_BITMASK_SET); 2199 | 2200 | 2201 | ngx_conf_merge_bitmask_value(conf->upstream.next_upstream, 2202 | prev->upstream.next_upstream, 2203 | (NGX_CONF_BITMASK_SET 2204 | |NGX_HTTP_UPSTREAM_FT_ERROR 2205 | |NGX_HTTP_UPSTREAM_FT_TIMEOUT)); 2206 | 2207 | if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) { 2208 | conf->upstream.next_upstream = NGX_CONF_BITMASK_SET 2209 | |NGX_HTTP_UPSTREAM_FT_OFF; 2210 | } 2211 | 2212 | if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path, 2213 | prev->upstream.temp_path, 2214 | &ngx_http_fastcgi_temp_path) 2215 | != NGX_OK) 2216 | { 2217 | return NGX_CONF_ERROR; 2218 | } 2219 | 2220 | #if (NGX_HTTP_CACHE) 2221 | 2222 | ngx_conf_merge_ptr_value(conf->upstream.cache, 2223 | prev->upstream.cache, NULL); 2224 | 2225 | if (conf->upstream.cache && conf->upstream.cache->data == NULL) { 2226 | ngx_shm_zone_t *shm_zone; 2227 | 2228 | shm_zone = conf->upstream.cache; 2229 | 2230 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 2231 | "\"fastcgi_cache\" zone \"%V\" is unknown", 2232 | &shm_zone->shm.name); 2233 | 2234 | return NGX_CONF_ERROR; 2235 | } 2236 | 2237 | ngx_conf_merge_uint_value(conf->upstream.cache_min_uses, 2238 | prev->upstream.cache_min_uses, 1); 2239 | 2240 | ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale, 2241 | prev->upstream.cache_use_stale, 2242 | (NGX_CONF_BITMASK_SET 2243 | |NGX_HTTP_UPSTREAM_FT_OFF)); 2244 | 2245 | if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) { 2246 | conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET 2247 | |NGX_HTTP_UPSTREAM_FT_OFF; 2248 | } 2249 | 2250 | if (conf->upstream.cache_methods == 0) { 2251 | conf->upstream.cache_methods = prev->upstream.cache_methods; 2252 | } 2253 | 2254 | conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD; 2255 | 2256 | ngx_conf_merge_ptr_value(conf->upstream.cache_valid, 2257 | prev->upstream.cache_valid, NULL); 2258 | 2259 | if (conf->cache_key.value.data == NULL) { 2260 | conf->cache_key = prev->cache_key; 2261 | } 2262 | 2263 | #endif 2264 | 2265 | ngx_conf_merge_value(conf->upstream.pass_request_headers, 2266 | prev->upstream.pass_request_headers, 1); 2267 | ngx_conf_merge_value(conf->upstream.pass_request_body, 2268 | prev->upstream.pass_request_body, 1); 2269 | 2270 | ngx_conf_merge_value(conf->upstream.intercept_errors, 2271 | prev->upstream.intercept_errors, 0); 2272 | 2273 | ngx_conf_merge_ptr_value(conf->catch_stderr, prev->catch_stderr, NULL); 2274 | 2275 | 2276 | ngx_conf_merge_str_value(conf->index, prev->index, ""); 2277 | 2278 | hash.max_size = 512; 2279 | hash.bucket_size = ngx_align(64, ngx_cacheline_size); 2280 | hash.name = "fastcgi_hide_headers_hash"; 2281 | 2282 | #if (NGX_HTTP_CACHE) 2283 | 2284 | h = conf->upstream.cache ? ngx_http_fastcgi_hide_cache_headers: 2285 | ngx_http_fastcgi_hide_headers; 2286 | #else 2287 | 2288 | h = ngx_http_fastcgi_hide_headers; 2289 | 2290 | #endif 2291 | 2292 | if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream, 2293 | &prev->upstream, h, &hash) 2294 | != NGX_OK) 2295 | { 2296 | return NGX_CONF_ERROR; 2297 | } 2298 | 2299 | if (conf->upstream.upstream == NULL) { 2300 | conf->upstream.upstream = prev->upstream.upstream; 2301 | } 2302 | 2303 | if (conf->fastcgi_lengths == NULL) { 2304 | conf->fastcgi_lengths = prev->fastcgi_lengths; 2305 | conf->fastcgi_values = prev->fastcgi_values; 2306 | } 2307 | 2308 | #if (NGX_PCRE) 2309 | if (conf->split_regex == NULL) { 2310 | conf->split_regex = prev->split_regex; 2311 | conf->split_name = prev->split_name; 2312 | } 2313 | #endif 2314 | 2315 | if (conf->params_source == NULL) { 2316 | conf->flushes = prev->flushes; 2317 | conf->params_len = prev->params_len; 2318 | conf->params = prev->params; 2319 | conf->params_source = prev->params_source; 2320 | 2321 | if (conf->params_source == NULL) { 2322 | return NGX_CONF_OK; 2323 | } 2324 | } 2325 | 2326 | conf->params_len = ngx_array_create(cf->pool, 64, 1); 2327 | if (conf->params_len == NULL) { 2328 | return NGX_CONF_ERROR; 2329 | } 2330 | 2331 | conf->params = ngx_array_create(cf->pool, 512, 1); 2332 | if (conf->params == NULL) { 2333 | return NGX_CONF_ERROR; 2334 | } 2335 | 2336 | src = conf->params_source->elts; 2337 | for (i = 0; i < conf->params_source->nelts; i++) { 2338 | 2339 | if (ngx_http_script_variables_count(&src[i].value) == 0) { 2340 | copy = ngx_array_push_n(conf->params_len, 2341 | sizeof(ngx_http_script_copy_code_t)); 2342 | if (copy == NULL) { 2343 | return NGX_CONF_ERROR; 2344 | } 2345 | 2346 | copy->code = (ngx_http_script_code_pt) 2347 | ngx_http_script_copy_len_code; 2348 | copy->len = src[i].key.len; 2349 | 2350 | 2351 | copy = ngx_array_push_n(conf->params_len, 2352 | sizeof(ngx_http_script_copy_code_t)); 2353 | if (copy == NULL) { 2354 | return NGX_CONF_ERROR; 2355 | } 2356 | 2357 | copy->code = (ngx_http_script_code_pt) 2358 | ngx_http_script_copy_len_code; 2359 | copy->len = src[i].value.len; 2360 | 2361 | 2362 | size = (sizeof(ngx_http_script_copy_code_t) 2363 | + src[i].key.len + src[i].value.len 2364 | + sizeof(uintptr_t) - 1) 2365 | & ~(sizeof(uintptr_t) - 1); 2366 | 2367 | copy = ngx_array_push_n(conf->params, size); 2368 | if (copy == NULL) { 2369 | return NGX_CONF_ERROR; 2370 | } 2371 | 2372 | copy->code = ngx_http_script_copy_code; 2373 | copy->len = src[i].key.len + src[i].value.len; 2374 | 2375 | p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); 2376 | 2377 | p = ngx_cpymem(p, src[i].key.data, src[i].key.len); 2378 | ngx_memcpy(p, src[i].value.data, src[i].value.len); 2379 | 2380 | } else { 2381 | copy = ngx_array_push_n(conf->params_len, 2382 | sizeof(ngx_http_script_copy_code_t)); 2383 | if (copy == NULL) { 2384 | return NGX_CONF_ERROR; 2385 | } 2386 | 2387 | copy->code = (ngx_http_script_code_pt) 2388 | ngx_http_script_copy_len_code; 2389 | copy->len = src[i].key.len; 2390 | 2391 | 2392 | size = (sizeof(ngx_http_script_copy_code_t) 2393 | + src[i].key.len + sizeof(uintptr_t) - 1) 2394 | & ~(sizeof(uintptr_t) - 1); 2395 | 2396 | copy = ngx_array_push_n(conf->params, size); 2397 | if (copy == NULL) { 2398 | return NGX_CONF_ERROR; 2399 | } 2400 | 2401 | copy->code = ngx_http_script_copy_code; 2402 | copy->len = src[i].key.len; 2403 | 2404 | p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); 2405 | ngx_memcpy(p, src[i].key.data, src[i].key.len); 2406 | 2407 | 2408 | ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); 2409 | 2410 | sc.cf = cf; 2411 | sc.source = &src[i].value; 2412 | sc.flushes = &conf->flushes; 2413 | sc.lengths = &conf->params_len; 2414 | sc.values = &conf->params; 2415 | 2416 | if (ngx_http_script_compile(&sc) != NGX_OK) { 2417 | return NGX_CONF_ERROR; 2418 | } 2419 | } 2420 | 2421 | code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t)); 2422 | if (code == NULL) { 2423 | return NGX_CONF_ERROR; 2424 | } 2425 | 2426 | *code = (uintptr_t) NULL; 2427 | 2428 | 2429 | code = ngx_array_push_n(conf->params, sizeof(uintptr_t)); 2430 | if (code == NULL) { 2431 | return NGX_CONF_ERROR; 2432 | } 2433 | 2434 | *code = (uintptr_t) NULL; 2435 | } 2436 | 2437 | code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t)); 2438 | if (code == NULL) { 2439 | return NGX_CONF_ERROR; 2440 | } 2441 | 2442 | *code = (uintptr_t) NULL; 2443 | 2444 | return NGX_CONF_OK; 2445 | } 2446 | 2447 | 2448 | static ngx_int_t 2449 | ngx_http_fastcgi_script_name_variable(ngx_http_request_t *r, 2450 | ngx_http_variable_value_t *v, uintptr_t data) 2451 | { 2452 | u_char *p; 2453 | ngx_http_fastcgi_ctx_t *f; 2454 | ngx_http_fastcgi_loc_conf_t *flcf; 2455 | 2456 | flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); 2457 | 2458 | f = ngx_http_fastcgi_split(r, flcf); 2459 | 2460 | if (f == NULL) { 2461 | return NGX_ERROR; 2462 | } 2463 | 2464 | if (f->script_name.len == 0 2465 | || f->script_name.data[f->script_name.len - 1] != '/') 2466 | { 2467 | v->len = f->script_name.len; 2468 | v->valid = 1; 2469 | v->no_cacheable = 0; 2470 | v->not_found = 0; 2471 | v->data = f->script_name.data; 2472 | 2473 | return NGX_OK; 2474 | } 2475 | 2476 | v->len = f->script_name.len + flcf->index.len; 2477 | 2478 | v->data = ngx_pnalloc(r->pool, v->len); 2479 | if (v->data == NULL) { 2480 | return NGX_ERROR; 2481 | } 2482 | 2483 | p = ngx_copy(v->data, f->script_name.data, f->script_name.len); 2484 | ngx_memcpy(p, flcf->index.data, flcf->index.len); 2485 | 2486 | return NGX_OK; 2487 | } 2488 | 2489 | 2490 | static ngx_int_t 2491 | ngx_http_fastcgi_path_info_variable(ngx_http_request_t *r, 2492 | ngx_http_variable_value_t *v, uintptr_t data) 2493 | { 2494 | ngx_http_fastcgi_ctx_t *f; 2495 | ngx_http_fastcgi_loc_conf_t *flcf; 2496 | 2497 | flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); 2498 | 2499 | f = ngx_http_fastcgi_split(r, flcf); 2500 | 2501 | if (f == NULL) { 2502 | return NGX_ERROR; 2503 | } 2504 | 2505 | v->len = f->path_info.len; 2506 | v->valid = 1; 2507 | v->no_cacheable = 0; 2508 | v->not_found = 0; 2509 | v->data = f->path_info.data; 2510 | 2511 | return NGX_OK; 2512 | } 2513 | 2514 | 2515 | static ngx_http_fastcgi_ctx_t * 2516 | ngx_http_fastcgi_split(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf) 2517 | { 2518 | ngx_http_fastcgi_ctx_t *f; 2519 | #if (NGX_PCRE) 2520 | ngx_int_t n; 2521 | int captures[(1 + 2) * 3]; 2522 | 2523 | f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module); 2524 | 2525 | if (f == NULL) { 2526 | f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t)); 2527 | if (f == NULL) { 2528 | return NULL; 2529 | } 2530 | 2531 | ngx_http_set_ctx(r, f, ngx_http_fastcgi_module); 2532 | } 2533 | 2534 | if (f->script_name.len) { 2535 | return f; 2536 | } 2537 | 2538 | if (flcf->split_regex == NULL) { 2539 | f->script_name = r->uri; 2540 | return f; 2541 | } 2542 | 2543 | n = ngx_regex_exec(flcf->split_regex, &r->uri, captures, (1 + 2) * 3); 2544 | 2545 | if (n >= 0) { /* match */ 2546 | f->script_name.len = captures[3] - captures[2]; 2547 | f->script_name.data = r->uri.data; 2548 | 2549 | f->path_info.len = captures[5] - captures[4]; 2550 | f->path_info.data = r->uri.data + f->script_name.len; 2551 | 2552 | return f; 2553 | } 2554 | 2555 | if (n == NGX_REGEX_NO_MATCHED) { 2556 | f->script_name = r->uri; 2557 | return f; 2558 | } 2559 | 2560 | ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, 2561 | ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"", 2562 | n, &r->uri, &flcf->split_name); 2563 | return NULL; 2564 | 2565 | #else 2566 | 2567 | f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module); 2568 | 2569 | if (f == NULL) { 2570 | f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t)); 2571 | if (f == NULL) { 2572 | return NULL; 2573 | } 2574 | 2575 | ngx_http_set_ctx(r, f, ngx_http_fastcgi_module); 2576 | } 2577 | 2578 | f->script_name = r->uri; 2579 | 2580 | return f; 2581 | 2582 | #endif 2583 | } 2584 | 2585 | 2586 | static char * 2587 | ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 2588 | { 2589 | ngx_http_fastcgi_loc_conf_t *flcf = conf; 2590 | 2591 | ngx_url_t u; 2592 | ngx_str_t *value, *url; 2593 | ngx_uint_t n; 2594 | ngx_http_core_loc_conf_t *clcf; 2595 | ngx_http_script_compile_t sc; 2596 | 2597 | if (flcf->upstream.upstream || flcf->fastcgi_lengths) { 2598 | return "is duplicate"; 2599 | } 2600 | 2601 | clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); 2602 | 2603 | clcf->handler = ngx_http_fastcgi_handler; 2604 | 2605 | if (clcf->name.data[clcf->name.len - 1] == '/') { 2606 | clcf->auto_redirect = 1; 2607 | } 2608 | 2609 | value = cf->args->elts; 2610 | 2611 | url = &value[1]; 2612 | 2613 | n = ngx_http_script_variables_count(url); 2614 | 2615 | if (n) { 2616 | 2617 | ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); 2618 | 2619 | sc.cf = cf; 2620 | sc.source = url; 2621 | sc.lengths = &flcf->fastcgi_lengths; 2622 | sc.values = &flcf->fastcgi_values; 2623 | sc.variables = n; 2624 | sc.complete_lengths = 1; 2625 | sc.complete_values = 1; 2626 | 2627 | if (ngx_http_script_compile(&sc) != NGX_OK) { 2628 | return NGX_CONF_ERROR; 2629 | } 2630 | 2631 | return NGX_CONF_OK; 2632 | } 2633 | 2634 | ngx_memzero(&u, sizeof(ngx_url_t)); 2635 | 2636 | u.url = value[1]; 2637 | u.no_resolve = 1; 2638 | 2639 | flcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); 2640 | if (flcf->upstream.upstream == NULL) { 2641 | return NGX_CONF_ERROR; 2642 | } 2643 | 2644 | return NGX_CONF_OK; 2645 | } 2646 | 2647 | 2648 | static char * 2649 | ngx_http_fastcgi_split_path_info(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 2650 | { 2651 | #if (NGX_PCRE) 2652 | ngx_http_fastcgi_loc_conf_t *flcf = conf; 2653 | 2654 | ngx_str_t *value; 2655 | ngx_regex_compile_t rc; 2656 | u_char errstr[NGX_MAX_CONF_ERRSTR]; 2657 | 2658 | value = cf->args->elts; 2659 | 2660 | flcf->split_name = value[1]; 2661 | 2662 | ngx_memzero(&rc, sizeof(ngx_regex_compile_t)); 2663 | 2664 | rc.pattern = value[1]; 2665 | rc.pool = cf->pool; 2666 | rc.err.len = NGX_MAX_CONF_ERRSTR; 2667 | rc.err.data = errstr; 2668 | 2669 | if (ngx_regex_compile(&rc) != NGX_OK) { 2670 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err); 2671 | return NGX_CONF_ERROR; 2672 | } 2673 | 2674 | if (rc.captures != 2) { 2675 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 2676 | "pattern \"%V\" must have 2 captures", &value[1]); 2677 | return NGX_CONF_ERROR; 2678 | } 2679 | 2680 | flcf->split_regex = rc.regex; 2681 | 2682 | return NGX_CONF_OK; 2683 | 2684 | #else 2685 | 2686 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 2687 | "\"%V\" requires PCRE library", &cmd->name); 2688 | return NGX_CONF_ERROR; 2689 | 2690 | #endif 2691 | } 2692 | 2693 | 2694 | static char * 2695 | ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 2696 | { 2697 | ngx_http_fastcgi_loc_conf_t *flcf = conf; 2698 | 2699 | ngx_str_t *value; 2700 | ngx_http_script_compile_t sc; 2701 | 2702 | if (flcf->upstream.store != NGX_CONF_UNSET 2703 | || flcf->upstream.store_lengths) 2704 | { 2705 | return "is duplicate"; 2706 | } 2707 | 2708 | value = cf->args->elts; 2709 | 2710 | if (ngx_strcmp(value[1].data, "off") == 0) { 2711 | flcf->upstream.store = 0; 2712 | return NGX_CONF_OK; 2713 | } 2714 | 2715 | #if (NGX_HTTP_CACHE) 2716 | 2717 | if (flcf->upstream.cache != NGX_CONF_UNSET_PTR 2718 | && flcf->upstream.cache != NULL) 2719 | { 2720 | return "is incompatible with \"fastcgi_cache\""; 2721 | } 2722 | 2723 | #endif 2724 | 2725 | if (ngx_strcmp(value[1].data, "on") == 0) { 2726 | flcf->upstream.store = 1; 2727 | return NGX_CONF_OK; 2728 | } 2729 | 2730 | /* include the terminating '\0' into script */ 2731 | value[1].len++; 2732 | 2733 | ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); 2734 | 2735 | sc.cf = cf; 2736 | sc.source = &value[1]; 2737 | sc.lengths = &flcf->upstream.store_lengths; 2738 | sc.values = &flcf->upstream.store_values; 2739 | sc.variables = ngx_http_script_variables_count(&value[1]); 2740 | sc.complete_lengths = 1; 2741 | sc.complete_values = 1; 2742 | 2743 | if (ngx_http_script_compile(&sc) != NGX_OK) { 2744 | return NGX_CONF_ERROR; 2745 | } 2746 | 2747 | return NGX_CONF_OK; 2748 | } 2749 | 2750 | 2751 | #if (NGX_HTTP_CACHE) 2752 | 2753 | static char * 2754 | ngx_http_fastcgi_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 2755 | { 2756 | ngx_http_fastcgi_loc_conf_t *flcf = conf; 2757 | 2758 | ngx_str_t *value; 2759 | 2760 | value = cf->args->elts; 2761 | 2762 | if (flcf->upstream.cache != NGX_CONF_UNSET_PTR) { 2763 | return "is duplicate"; 2764 | } 2765 | 2766 | if (ngx_strcmp(value[1].data, "off") == 0) { 2767 | flcf->upstream.cache = NULL; 2768 | return NGX_CONF_OK; 2769 | } 2770 | 2771 | if (flcf->upstream.store > 0 || flcf->upstream.store_lengths) { 2772 | return "is incompatible with \"fastcgi_store\""; 2773 | } 2774 | 2775 | flcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, 2776 | &ngx_http_fastcgi_module); 2777 | if (flcf->upstream.cache == NULL) { 2778 | return NGX_CONF_ERROR; 2779 | } 2780 | 2781 | return NGX_CONF_OK; 2782 | } 2783 | 2784 | 2785 | static char * 2786 | ngx_http_fastcgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 2787 | { 2788 | ngx_http_fastcgi_loc_conf_t *flcf = conf; 2789 | 2790 | ngx_str_t *value; 2791 | ngx_http_compile_complex_value_t ccv; 2792 | 2793 | value = cf->args->elts; 2794 | 2795 | if (flcf->cache_key.value.len) { 2796 | return "is duplicate"; 2797 | } 2798 | 2799 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 2800 | 2801 | ccv.cf = cf; 2802 | ccv.value = &value[1]; 2803 | ccv.complex_value = &flcf->cache_key; 2804 | 2805 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 2806 | return NGX_CONF_ERROR; 2807 | } 2808 | 2809 | return NGX_CONF_OK; 2810 | } 2811 | 2812 | #endif 2813 | 2814 | 2815 | static char * 2816 | ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post, void *data) 2817 | { 2818 | #if (NGX_FREEBSD) 2819 | ssize_t *np = data; 2820 | 2821 | if ((u_long) *np >= ngx_freebsd_net_inet_tcp_sendspace) { 2822 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 2823 | "\"fastcgi_send_lowat\" must be less than %d " 2824 | "(sysctl net.inet.tcp.sendspace)", 2825 | ngx_freebsd_net_inet_tcp_sendspace); 2826 | 2827 | return NGX_CONF_ERROR; 2828 | } 2829 | 2830 | #elif !(NGX_HAVE_SO_SNDLOWAT) 2831 | ssize_t *np = data; 2832 | 2833 | ngx_conf_log_error(NGX_LOG_WARN, cf, 0, 2834 | "\"fastcgi_send_lowat\" is not supported, ignored"); 2835 | 2836 | *np = 0; 2837 | 2838 | #endif 2839 | 2840 | return NGX_CONF_OK; 2841 | } 2842 | 2843 | 2844 | static char * 2845 | ngx_http_fastcgi_upstream_max_fails_unsupported(ngx_conf_t *cf, 2846 | ngx_command_t *cmd, void *conf) 2847 | { 2848 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 2849 | "\"fastcgi_upstream_max_fails\" is not supported, " 2850 | "use the \"max_fails\" parameter of the \"server\" directive ", 2851 | "inside the \"upstream\" block"); 2852 | 2853 | return NGX_CONF_ERROR; 2854 | } 2855 | 2856 | 2857 | static char * 2858 | ngx_http_fastcgi_upstream_fail_timeout_unsupported(ngx_conf_t *cf, 2859 | ngx_command_t *cmd, void *conf) 2860 | { 2861 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 2862 | "\"fastcgi_upstream_fail_timeout\" is not supported, " 2863 | "use the \"fail_timeout\" parameter of the \"server\" directive ", 2864 | "inside the \"upstream\" block"); 2865 | 2866 | return NGX_CONF_ERROR; 2867 | } 2868 | -------------------------------------------------------------------------------- /servers/afcgitest/Makefile: -------------------------------------------------------------------------------- 1 | INCDIRS = /opt/local/include 2 | LIBDIRS = /opt/local/lib 3 | LIBS = event 4 | SOURCES = sockutil.c afcgitest.c 5 | EXECUTABLE = afcgitest 6 | 7 | CFLAGS = -Wall $(addprefix -I, $(INCDIRS)) 8 | LDLIBS = $(addprefix -l, $(LIBS) $(LIBS_$(notdir $*))) 9 | LDFLAGS = $(addprefix -L, $(LIBDIRS)) $(LDLIBS) 10 | OBJECTS = $(SOURCES:.c=.o) 11 | 12 | #----------------------------------------------------- 13 | # Options 14 | 15 | # Optimize 16 | #CFLAGS += -O3 -DNDEBUG 17 | 18 | # If Mac OS X: 19 | CFLAGS += -arch i386 20 | LDFLAGS += -arch i386 21 | 22 | #----------------------------------------------------- 23 | 24 | all: $(SOURCES) $(EXECUTABLE) 25 | 26 | $(EXECUTABLE): $(OBJECTS) 27 | $(CC) $(LDFLAGS) $(OBJECTS) -o $@ 28 | 29 | .c.o: 30 | $(CC) $(CFLAGS) -c $< -o $@ 31 | 32 | clean: 33 | rm -rf *.o $(EXECUTABLE) 34 | 35 | .PHONY: all clean 36 | -------------------------------------------------------------------------------- /servers/afcgitest/afcgitest.c: -------------------------------------------------------------------------------- 1 | #include "sockutil.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include /* for inet_ntop */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #define DEBUG_RESPONSE_DELAY 5 23 | 24 | #ifndef DEBUG_RESPONSE_DELAY 25 | #define DEBUG_RESPONSE_DELAY 0 26 | #endif 27 | 28 | #define AZ(foo) assert((foo) == 0) 29 | #define AN(foo) assert((foo) != 0) 30 | 31 | #define FCGI_LISTENSOCK_FILENO 0 32 | #define FLAG_KEEP_CONN 1 33 | #define REQUESTS_MAX __SHRT_MAX__+__SHRT_MAX__ 34 | 35 | #define ROLE_RESPONDER 1 36 | #define ROLE_AUTHORIZER 2 37 | #define ROLE_FILTER 3 38 | 39 | 40 | typedef struct { 41 | struct event ev; 42 | } server_t; 43 | 44 | 45 | typedef struct { 46 | uint16_t id; 47 | uint16_t role; 48 | uint8_t keepconn; 49 | uint8_t stdin_eof; 50 | uint8_t terminate; 51 | uint8_t padding; 52 | struct bufferevent *bev; 53 | } request_t; 54 | 55 | 56 | request_t *_requests[REQUESTS_MAX]; 57 | 58 | 59 | enum { 60 | TYPE_BEGIN_REQUEST = 1, 61 | TYPE_ABORT_REQUEST = 2, 62 | TYPE_END_REQUEST = 3, 63 | TYPE_PARAMS = 4, 64 | TYPE_STDIN = 5, 65 | TYPE_STDOUT = 6, 66 | TYPE_STDERR = 7, 67 | TYPE_DATA = 8, 68 | TYPE_GET_VALUES = 9, 69 | TYPE_GET_VALUES_RESULT = 10, 70 | TYPE_UNKNOWN = 11 71 | }; 72 | 73 | 74 | enum { 75 | PROTOST_REQUEST_COMPLETE = 0, 76 | PROTOST_CANT_MPX_CONN = 1, 77 | PROTOST_OVERLOADED = 2, 78 | PROTOST_UNKNOWN_ROLE = 3 79 | }; 80 | 81 | 82 | typedef struct { 83 | uint8_t version; 84 | uint8_t type; 85 | uint8_t requestIdB1; 86 | uint8_t requestIdB0; 87 | uint8_t contentLengthB1; 88 | uint8_t contentLengthB0; 89 | uint8_t paddingLength; 90 | uint8_t reserved; 91 | } header_t; // 8 92 | 93 | 94 | typedef struct { 95 | uint8_t roleB1; 96 | uint8_t roleB0; 97 | uint8_t flags; 98 | uint8_t reserved[5]; 99 | } begin_request_t; // 8 100 | 101 | 102 | typedef struct { 103 | header_t header; 104 | uint8_t appStatusB3; 105 | uint8_t appStatusB2; 106 | uint8_t appStatusB1; 107 | uint8_t appStatusB0; 108 | uint8_t protocolStatus; 109 | uint8_t reserved[3]; 110 | } end_request_t; // 16 111 | 112 | 113 | typedef struct { 114 | header_t header; 115 | uint8_t type; 116 | uint8_t reserved[7]; 117 | } unknown_type_t; // 16 118 | 119 | 120 | inline static void header_init(header_t *self, uint8_t t, uint16_t id, uint16_t len) { 121 | self->version = '\1'; 122 | self->type = t; 123 | self->requestIdB1 = id >> 8; 124 | self->requestIdB0 = id & 0xff; 125 | self->contentLengthB1 = len >> 8; 126 | self->contentLengthB0 = len & 0xff; 127 | self->paddingLength = '\0'; 128 | self->reserved = '\0'; 129 | } 130 | 131 | 132 | inline static void end_request_init(end_request_t *self, uint16_t id, uint32_t ast, uint8_t protostatus) { 133 | header_init((header_t *)self, TYPE_END_REQUEST, id, sizeof(end_request_t)-sizeof(header_t)); 134 | self->appStatusB3 = (ast >> 24) & 0xff; 135 | self->appStatusB3 = (ast >> 16) & 0xff; 136 | self->appStatusB3 = (ast >> 8) & 0xff; 137 | self->appStatusB3 = ast & 0xff; 138 | self->protocolStatus = protostatus; 139 | memset(self->reserved, 0, sizeof(self->reserved)); 140 | } 141 | 142 | 143 | inline static void unknown_type_init(unknown_type_t *self, uint8_t unknown_type) { 144 | header_init((header_t *)self, TYPE_UNKNOWN, 0, sizeof(unknown_type_t)-sizeof(header_t)); 145 | self->type = unknown_type; 146 | memset(self->reserved, 0, sizeof(self->reserved)); 147 | } 148 | 149 | 150 | /////////// bev 151 | 152 | 153 | #define BEV_FD(p) ((p)->ev_read.ev_fd) 154 | #define BEV_FD_SET(p, v) ((p)->ev_read.ev_fd = (v)) 155 | 156 | 157 | inline static void bev_disable(struct bufferevent *bev) { 158 | int events = 0; 159 | if (bev->readcb) 160 | events |= EV_READ; 161 | if (bev->writecb) 162 | events |= EV_WRITE; 163 | bufferevent_disable(bev, events); 164 | } 165 | 166 | 167 | inline static void bev_close(struct bufferevent *bev) { 168 | bev_disable(bev); 169 | close(BEV_FD(bev)); 170 | BEV_FD_SET(bev, -1); 171 | } 172 | 173 | 174 | inline static void bev_drain(struct bufferevent *bev) { 175 | evbuffer_drain(bev->input, EVBUFFER_LENGTH(bev->input)); 176 | evbuffer_drain(bev->output, EVBUFFER_LENGTH(bev->output)); 177 | } 178 | 179 | 180 | void bev_abort(struct bufferevent *bev) { 181 | struct linger lingeropt; 182 | lingeropt.l_onoff = 1; 183 | lingeropt.l_linger = 0; 184 | AZ(setsockopt(BEV_FD(bev), SOL_SOCKET, SO_LINGER, (char *)&lingeropt, sizeof(lingeropt))); 185 | shutdown(BEV_FD(bev), SHUT_RDWR); 186 | bev_drain(bev); 187 | bev_close(bev); 188 | } 189 | 190 | 191 | inline static int bev_add(struct event *ev, int timeout) { 192 | struct timeval tv, *ptv = NULL; 193 | if (timeout) { 194 | evutil_timerclear(&tv); 195 | tv.tv_sec = timeout; 196 | ptv = &tv; 197 | } 198 | return event_add(ev, ptv); 199 | } 200 | 201 | 202 | /////////// request 203 | 204 | void app_handle_beginrequest(request_t *r); 205 | void app_handle_input(request_t *r, uint16_t length); 206 | void app_handle_requestaborted(request_t *r); 207 | 208 | 209 | // get a request by request id 210 | inline static request_t *request_get(uint16_t id) { 211 | request_t *r; 212 | r = _requests[id]; 213 | if (r == NULL) { 214 | r = (request_t *)calloc(1, sizeof(request_t)); 215 | _requests[id] = r; 216 | } 217 | printf("** get req %p\n", r); 218 | return r; 219 | } 220 | 221 | // restore a request object 222 | inline static void request_put(request_t *r) { 223 | printf("** put req %p\n", r); 224 | r->bev->cbarg = NULL; 225 | r->bev = NULL; 226 | } 227 | 228 | static inline bool request_is_active(request_t *r) { 229 | return ((r != NULL) && (r->bev != NULL)); 230 | } 231 | 232 | 233 | void request_write(request_t *r, const char *buf, uint16_t len, uint8_t tostdout) { 234 | if (len == 0) 235 | return; 236 | 237 | if (!request_is_active(r)) { 238 | //warn("request_write(): request is not active"); 239 | return; 240 | } 241 | 242 | header_t h; 243 | header_init(&h, tostdout ? TYPE_STDOUT : TYPE_STDERR, r->id, len); 244 | 245 | if (evbuffer_add(r->bev->output, (const void *)&h, sizeof(header_t)) != -1) 246 | evbuffer_add(r->bev->output, (const void *)buf, len); 247 | 248 | // schedule write 249 | if (r->bev->enabled & EV_WRITE) 250 | bev_add(&r->bev->ev_write, r->bev->timeout_write); 251 | } 252 | 253 | 254 | void request_end(request_t *r, uint32_t appstatus, uint8_t protostatus) { 255 | if (!request_is_active(r)) { 256 | //warn("request_end(): request is not active"); 257 | return; 258 | } 259 | 260 | uint8_t buf[32]; // header + header + end_request_t 261 | uint8_t *p = buf; 262 | 263 | //assert(EVBUFFER_LENGTH(r->bev->output) == 0); 264 | 265 | // Terminate the stdout and stderr stream, and send the end-request message. 266 | header_init((header_t *)p, TYPE_STDOUT, r->id, 0); 267 | p += sizeof(header_t); 268 | header_init((header_t *)p, TYPE_STDERR, r->id, 0); 269 | p += sizeof(header_t); 270 | end_request_init((end_request_t *)p, r->id, appstatus, protostatus); 271 | p += sizeof(end_request_t); 272 | 273 | printf("sending END_REQUEST for id %d\n", r->id); 274 | 275 | bufferevent_write(r->bev, (const void *)buf, sizeof(buf)); 276 | 277 | r->terminate = true; 278 | } 279 | 280 | 281 | inline static void process_abort_request(request_t *r) { 282 | assert(r->bev != NULL); 283 | printf("request %p aborted by client\n", r); 284 | 285 | app_handle_requestaborted(r); 286 | 287 | r->terminate = 1; // can we trust fcgiproto_writecb to be called? 288 | } 289 | 290 | 291 | void fcgiproto_errorcb(struct bufferevent *bev, short what, request_t *r) { 292 | if (what & EVBUFFER_EOF) { 293 | printf("request %p EOF\n", r); 294 | // we treat abrupt disconnect as abort 295 | process_abort_request(r); 296 | } 297 | else if (what & EVBUFFER_TIMEOUT) 298 | printf("request %p timeout\n", r); 299 | else 300 | printf("request %p error\n", r); 301 | 302 | bev_close(bev); 303 | if (r) 304 | request_put(r); 305 | } 306 | 307 | 308 | inline static void process_unknown(struct bufferevent *bev, uint8_t type, uint16_t len) { 309 | printf("process_unknown(%p, %d, %d)\n", bev, type, len); 310 | unknown_type_t msg; 311 | unknown_type_init(&msg, type); 312 | bufferevent_write(bev, (const void *)&msg, sizeof(unknown_type_t)); 313 | evbuffer_drain(bev->input, sizeof(header_t) + len); 314 | } 315 | 316 | 317 | inline static void process_begin_request(struct bufferevent *bev, uint16_t id, const begin_request_t *br) { 318 | request_t *r; 319 | 320 | r = request_get(id); 321 | //assert(r->bev == NULL); 322 | if ((r->bev != NULL) && (EVBUFFER_LENGTH(r->bev->input) != 0)) { 323 | printf("warn: client sent already used req id %d -- skipping", id); 324 | // todo: respond with error 325 | bev_close(r->bev); 326 | return; 327 | } 328 | 329 | 330 | r->bev = bev; 331 | r->id = id; 332 | r->keepconn = (br->flags & FLAG_KEEP_CONN) == 1; 333 | r->role = (br->roleB1 << 8) + br->roleB0; 334 | r->stdin_eof = false; 335 | r->terminate = false; 336 | bev->cbarg = (void *)r; 337 | 338 | evbuffer_drain(bev->input, sizeof(header_t)+sizeof(begin_request_t)); 339 | } 340 | 341 | 342 | inline static void process_params(struct bufferevent *bev, uint16_t id, const uint8_t *buf, uint16_t len) { 343 | request_t *r; 344 | 345 | r = request_get(id); 346 | //assert(r->bev != NULL); // this can actually happen and it's ok 347 | 348 | // Is this the last message to come? Then queue the request for the user. 349 | if (len == 0) { 350 | evbuffer_drain(bev->input, sizeof(header_t)); 351 | app_handle_beginrequest(r); 352 | return; 353 | } 354 | 355 | // Process message. 356 | 357 | uint8_t const * const bufend = buf + len; 358 | uint32_t name_len; 359 | uint32_t data_len; 360 | 361 | while(buf != bufend) { 362 | 363 | if (*buf >> 7 == 0) { 364 | name_len = *(buf++); 365 | } 366 | else { 367 | name_len = ((buf[0] & 0x7F) << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3]; 368 | buf += 4; 369 | } 370 | 371 | if (*buf >> 7 == 0) { 372 | data_len = *(buf++); 373 | } 374 | else { 375 | data_len = ((buf[0] & 0x7F) << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3]; 376 | buf += 4; 377 | } 378 | 379 | assert(buf + name_len + data_len <= bufend); 380 | 381 | // todo replace with actual adding to req: 382 | char k[255], v[8192]; 383 | strncpy(k, (const char *)buf, name_len); k[name_len] = '\0'; 384 | buf += name_len; 385 | strncpy(v, (const char *)buf, data_len); v[data_len] = '\0'; 386 | buf += data_len; 387 | printf("fcgiproto>> param>> '%s' => '%s'\n", k, v); 388 | // todo: req->second->params[name] = data; 389 | } 390 | 391 | evbuffer_drain(bev->input, sizeof(header_t) + len); 392 | } 393 | 394 | 395 | inline static void process_stdin(struct bufferevent *bev, uint16_t id, const uint8_t *buf, uint16_t len) { 396 | request_t *r; 397 | 398 | r = request_get(id); 399 | 400 | // left-over stdin on inactive request is drained and forgotten 401 | if (r->bev == NULL) { 402 | bev_drain(bev); 403 | return; 404 | } 405 | 406 | assert(r->bev != NULL); 407 | evbuffer_drain(bev->input, sizeof(header_t)); 408 | 409 | // Is this the last message to come? Then set the eof flag. 410 | // Otherwise, add the data to the buffer in the request structure. 411 | if (len == 0) { 412 | r->stdin_eof = true; 413 | return; 414 | } 415 | 416 | app_handle_input(r, len); 417 | } 418 | 419 | 420 | void fcgiproto_readcb(struct bufferevent *bev, request_t *r) { 421 | printf("fcgiproto_readcb(%p, %p)\n", bev, r); 422 | //bufferevent_write_buffer(bev, bev->input); 423 | 424 | while(EVBUFFER_LENGTH(bev->input) >= sizeof(header_t)) { 425 | const header_t *hp = (const header_t *)EVBUFFER_DATA(bev->input); 426 | 427 | // Check whether our peer speaks the correct protocol version. 428 | if (hp->version != 1) { 429 | warnx("fcgiev: cannot handle protocol version %u", hp->version); 430 | bev_abort(bev); 431 | break; 432 | } 433 | 434 | // Check whether we have the whole message that follows the 435 | // headers in our buffer already. If not, we can't process it 436 | // yet. 437 | uint16_t msg_len = (hp->contentLengthB1 << 8) + hp->contentLengthB0; 438 | uint16_t msg_id = (hp->requestIdB1 << 8) + hp->requestIdB0; 439 | 440 | if (EVBUFFER_LENGTH(bev->input) < sizeof(header_t) + msg_len + hp->paddingLength) 441 | return; 442 | 443 | // Process the message. 444 | printf("fcgiproto>> received message: id: %d, bodylen: %d, padding: %d, type: %d\n", 445 | msg_id, msg_len, hp->paddingLength, (int)hp->type); 446 | 447 | switch (hp->type) { 448 | case TYPE_BEGIN_REQUEST: 449 | process_begin_request(bev, msg_id, 450 | (const begin_request_t *)(EVBUFFER_DATA(bev->input) + sizeof(header_t)) ); 451 | break; 452 | case TYPE_ABORT_REQUEST: 453 | process_abort_request(request_get(msg_id)); 454 | break; 455 | case TYPE_PARAMS: 456 | process_params(bev, msg_id, (const uint8_t *)EVBUFFER_DATA(bev->input) + sizeof(header_t), msg_len); 457 | break; 458 | case TYPE_STDIN: 459 | process_stdin(bev, msg_id, (const uint8_t *)EVBUFFER_DATA(bev->input) + sizeof(header_t), msg_len); 460 | break; 461 | //case TYPE_END_REQUEST: 462 | //case TYPE_STDOUT: 463 | //case TYPE_STDERR: 464 | //case TYPE_DATA: 465 | //case TYPE_GET_VALUES: 466 | //case TYPE_GET_VALUES_RESULT: 467 | //case TYPE_UNKNOWN: 468 | default: 469 | process_unknown(bev, hp->type, msg_len); 470 | }/* switch(hp->type) */ 471 | 472 | if (hp->paddingLength) 473 | evbuffer_drain(bev->input, hp->paddingLength); 474 | } 475 | } 476 | 477 | 478 | void fcgiproto_writecb(struct bufferevent *bev, request_t *r) { 479 | // Invoked if bev->output is drained or below the low watermark. 480 | printf("fcgiproto_writecb(%p, %p)\n", bev, r); 481 | 482 | if (r != NULL && r->terminate) { 483 | bev_disable(r->bev); 484 | bev_drain(r->bev); 485 | bev_close(r->bev); 486 | request_put(r); 487 | if (r->keepconn == false) { 488 | printf("PUT connection (r->keepconn == false, in fcgiproto_writecb)\n"); 489 | } 490 | } 491 | } 492 | 493 | 494 | /*void conn_init(request_t *r, int fd) { 495 | r->ident = 0; 496 | r->bev.readcb = (evbuffercb)fcgiproto_readcb; 497 | r->bev.writecb = (evbuffercb)fcgiproto_writecb; 498 | r->bev.errorcb = (everrorcb)fcgiproto_errorcb; 499 | r->bev.cbarg = (void *)r; 500 | event_set(&r->bev.ev_read, fd, EV_READ, bufferevent_readcb, (void *)&r->bev); 501 | event_set(&r->bev.ev_write, fd, EV_WRITE, bufferevent_writecb, (void *)&r->bev); 502 | r->bev.enabled = EV_WRITE; 503 | }*/ 504 | 505 | 506 | void server_init(server_t *server) { 507 | server->ev.ev_fd = FCGI_LISTENSOCK_FILENO; 508 | } 509 | 510 | 511 | const sau_t *server_bind(server_t *server, const char *addrorpath) { 512 | static sau_t sa; 513 | if ((server->ev.ev_fd = sockutil_bind(addrorpath, SOMAXCONN, &sa)) < 0) 514 | return NULL; 515 | return (const sau_t *)(&sa); 516 | } 517 | 518 | 519 | static const char *sockaddr_host(const sau_t *sa) { 520 | static char *buf[SOCK_MAXADDRLEN+1]; 521 | buf[0] = '\0'; 522 | struct sockaddr_in *sk = (struct sockaddr_in *)sa; 523 | return inet_ntop(AF_INET, &(sk->sin_addr), (char *)buf, SOCK_MAXADDRLEN); 524 | } 525 | 526 | 527 | void server_accept(int fd, short ev, server_t *server) { 528 | struct bufferevent *bev; 529 | socklen_t saz; 530 | int on = 1, events, connfd; 531 | struct timeval *timeout; 532 | sau_t sa; 533 | 534 | saz = sizeof(sa); 535 | connfd = accept(fd, (struct sockaddr *)&sa, &saz); 536 | timeout = NULL; 537 | 538 | if (connfd < 0) { 539 | warn("accept failed"); 540 | return; 541 | } 542 | 543 | // Disable Nagle -- better response times at the cost of more packets being sent. 544 | setsockopt(connfd, IPPROTO_TCP, TCP_NODELAY, (char *)&on, sizeof(on)); 545 | // Set nonblocking 546 | AZ(ioctl(connfd, FIONBIO, (int *)&on)); 547 | 548 | bev = bufferevent_new(connfd, (evbuffercb)fcgiproto_readcb, 549 | (evbuffercb)fcgiproto_writecb, (everrorcb)fcgiproto_errorcb, NULL); 550 | 551 | events = EV_READ; 552 | if (bev->writecb) 553 | events |= EV_WRITE; 554 | bufferevent_enable(bev, events); 555 | 556 | printf("GET connection\n"); 557 | printf("fcgi client %s connected on fd %d\n", sockaddr_host(&sa), connfd); 558 | } 559 | 560 | 561 | void server_enable(server_t *server) { 562 | int on = 1; 563 | AZ(ioctl(server->ev.ev_fd, FIONBIO, (int *)&on)); 564 | event_set(&server->ev, server->ev.ev_fd, EV_READ|EV_PERSIST, 565 | (void (*)(int,short,void*))server_accept, (void *)server); 566 | event_add(&server->ev, NULL/* no timeout */); 567 | } 568 | 569 | 570 | void fcgiev_init() { 571 | memset((void *)_requests, 0, sizeof(_requests)); 572 | } 573 | 574 | 575 | static void app_test_delayed_finalize_request(int fd, short event, void *arg) { 576 | request_t *r = (request_t *)arg; 577 | if (request_is_active(r)) { 578 | printf("app_test_delayed_finalize_request %p\n", r); 579 | static const char hello[] = "Content-type: text/plain\r\n\r\nHello world\n"; 580 | request_write(r, hello, sizeof(hello)-1, 1); 581 | request_end(r, 0, PROTOST_REQUEST_COMPLETE); 582 | } 583 | } 584 | 585 | 586 | void app_handle_beginrequest(request_t *r) { 587 | printf("app_handle_beginrequest %p\n", r); 588 | 589 | if (r->role != ROLE_RESPONDER) { 590 | request_write(r, "We can't handle any role but RESPONDER.", 39, 0); 591 | request_end(r, 1, PROTOST_UNKNOWN_ROLE); 592 | return; 593 | } 594 | 595 | #if DEBUG_RESPONSE_DELAY 596 | // DELAYED 597 | struct event *timer_ev; 598 | struct timeval tv; 599 | timer_ev = calloc(1,sizeof(struct event)); 600 | tv.tv_sec = DEBUG_RESPONSE_DELAY; 601 | tv.tv_usec = 0; 602 | evtimer_set(timer_ev, app_test_delayed_finalize_request, (void *)r); 603 | evtimer_add(timer_ev, &tv); 604 | #else 605 | // DIRECT 606 | static const char hello[] = "Content-type: text/plain\r\n\r\nHello world\n"; 607 | request_write(r, hello, sizeof(hello)-1, 1); 608 | request_end(r, 0, PROTOST_REQUEST_COMPLETE); 609 | #endif 610 | } 611 | 612 | 613 | void app_handle_input(request_t *r, uint16_t length) { 614 | printf("app_handle_input %p -- %d bytes\n", r, length); 615 | // simply drain it for now 616 | evbuffer_drain(r->bev->input, length); 617 | } 618 | 619 | 620 | void app_handle_requestaborted(request_t *r) { 621 | printf("app_handle_requestaborted %p\n", r); 622 | } 623 | 624 | 625 | int main(int argc, const char * const *argv) { 626 | server_t *server; 627 | int i; 628 | 629 | // initialize libraries 630 | event_init(); 631 | fcgiev_init(); 632 | 633 | // no argmuents: bind to stdin 634 | if (argc <= 1) { 635 | server = calloc(1,sizeof(server_t)); 636 | server_init(server); 637 | server_enable(server); 638 | } 639 | // bind with every argument 640 | else { 641 | for (i=1; i 4 | #include 5 | #include 6 | #include 7 | #include /* for fcntl */ 8 | #include 9 | #include /* for memchr() */ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #ifndef PATH_MAX 24 | #ifdef MAXPATHLEN 25 | #define PATH_MAX MAXPATHLEN 26 | #else 27 | #define PATH_MAX 1024 28 | #endif 29 | #endif 30 | 31 | inline static int _build_un(const char *bindPath, struct sockaddr_un *servAddrPtr, int *servAddrLen) { 32 | int bindPathLen = strlen(bindPath); 33 | if(bindPathLen > sizeof(servAddrPtr->sun_path)) 34 | return -1; 35 | memset((char *) servAddrPtr, 0, sizeof(*servAddrPtr)); 36 | servAddrPtr->sun_family = AF_UNIX; 37 | memcpy(servAddrPtr->sun_path, bindPath, bindPathLen); 38 | *servAddrLen = sizeof(servAddrPtr->sun_family) + bindPathLen; 39 | return 0; 40 | } 41 | 42 | 43 | int sockutil_bind(const char *bindPath, int backlog, sau_t *sa) { 44 | int fd, servLen; 45 | bool tcp = false; 46 | unsigned long tcp_ia = 0; 47 | char *tp; 48 | short port = 0; 49 | char host[PATH_MAX]; 50 | 51 | strcpy(host, bindPath); 52 | 53 | if((tp = strchr(host, ':')) != 0) { 54 | *tp++ = 0; 55 | if((port = atoi(tp)) == 0) 56 | *--tp = ':'; 57 | else 58 | tcp = true; 59 | } 60 | 61 | if(tcp) { 62 | if (!*host || !strcmp(host,"*")) { 63 | tcp_ia = htonl(INADDR_ANY); 64 | } 65 | else { 66 | tcp_ia = inet_addr(host); 67 | if (tcp_ia == INADDR_NONE) { 68 | struct hostent * hep; 69 | hep = gethostbyname(host); 70 | if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) { 71 | warn("fcgiev: cannot resolve host name %s", host); 72 | return -1; 73 | } 74 | if (hep->h_addr_list[1]) { 75 | warn("fcgiev: host %s has multiple addresses -- choose one explicitly", host); 76 | return -1; 77 | } 78 | tcp_ia = ((struct in_addr *) (hep->h_addr))->s_addr; 79 | } 80 | } 81 | 82 | fd = socket(AF_INET, SOCK_STREAM, 0); 83 | 84 | if(fd >= 0) { 85 | int flag = 1; 86 | if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag)) < 0) { 87 | warn("fcgiev: can't set SO_REUSEADDR."); 88 | return -1; 89 | } 90 | } 91 | } 92 | else { /* tcp == FALSE */ 93 | fd = socket(AF_UNIX, SOCK_STREAM, 0); 94 | } 95 | 96 | if(fd < 0) 97 | return -1; 98 | 99 | // Bind the listening socket. 100 | if(tcp) { 101 | memset((char *)&sa->in, 0, sizeof(sa->in)); 102 | sa->in.sin_family = AF_INET; 103 | sa->in.sin_addr.s_addr = tcp_ia; 104 | sa->in.sin_port = htons(port); 105 | servLen = sizeof(sa->in); 106 | } 107 | else { 108 | unlink(bindPath); 109 | if(_build_un(bindPath, &sa->un, &servLen)) { 110 | warn("fcgiev: listening socket's path name is too long."); 111 | return -1; 112 | } 113 | } 114 | 115 | if(bind(fd, (struct sockaddr *) &sa->un, servLen) < 0) { 116 | perror("fcgiev: bind"); 117 | return -1; 118 | } 119 | 120 | if(listen(fd, backlog) < 0) { 121 | perror("fcgiev: listen"); 122 | return -1; 123 | } 124 | 125 | return fd; 126 | } 127 | -------------------------------------------------------------------------------- /servers/afcgitest/sockutil.h: -------------------------------------------------------------------------------- 1 | #ifndef _FCGIEV_SOCKUTIL_H_ 2 | #define _FCGIEV_SOCKUTIL_H_ 3 | 4 | #include 5 | #include 6 | 7 | typedef union { 8 | struct sockaddr_un un; 9 | struct sockaddr_in in; 10 | } sau_t; 11 | 12 | int sockutil_bind(const char *bindPath, int backlog, sau_t *sa); 13 | 14 | #endif 15 | --------------------------------------------------------------------------------