# HG changeset patch # User Valentin Bartenev # Date 1363794516 -14400 # Node ID d3269427a19c02d93bc2681fcbd10941a462775d # Parent e0553ed7d9097bba14c70bbe33f847721fffbcfa Create request object only after the first byte was received. Previously, we always created an object and logged 400 (Bad Request) in access log if a client closed connection without sending any data. Such a connection was counted as "reading". Since it's common for modern browsers to behave like this, it's no longer considered an error if a client closes connection without sending any data, and such a connection will be counted as "waiting". Now, we do not log 400 (Bad Request) and keep memory footprint as small as possible. diff -r e0553ed7d909 -r d3269427a19c src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Tue Mar 05 14:36:20 2013 +0000 +++ b/src/http/ngx_http_request.c Wed Mar 20 19:48:36 2013 +0400 @@ -10,6 +10,7 @@ #include +static void ngx_http_wait_request_handler(ngx_event_t *ev); static void ngx_http_init_request(ngx_event_t *ev); static void ngx_http_process_request_line(ngx_event_t *rev); static void ngx_http_process_request_headers(ngx_event_t *rev); @@ -308,12 +309,12 @@ ngx_http_init_connection(ngx_connection_ c->log->connection = c->number; c->log->handler = ngx_http_log_error; c->log->data = ctx; - c->log->action = "reading client request line"; + c->log->action = "waiting for request"; c->log_error = NGX_ERROR_INFO; rev = c->read; - rev->handler = ngx_http_init_request; + rev->handler = ngx_http_wait_request_handler; c->write->handler = ngx_http_empty_handler; #if (NGX_HTTP_SSL) @@ -363,6 +364,99 @@ ngx_http_init_connection(ngx_connection_ static void +ngx_http_wait_request_handler(ngx_event_t *rev) +{ + size_t size; + ssize_t n; + ngx_buf_t *b; + ngx_connection_t *c; + ngx_http_connection_t *hc; + ngx_http_core_srv_conf_t *cscf; + + c = rev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http wait request handler"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + ngx_http_close_connection(c); + return; + } + + hc = c->data; + cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); + + size = cscf->client_header_buffer_size; + + b = c->buffer; + + if (b == NULL) { + b = ngx_create_temp_buf(c->pool, size); + if (b == NULL) { + ngx_http_close_connection(c); + return; + } + + c->buffer = b; + + } else if (b->start == NULL) { + + b->start = ngx_palloc(c->pool, size); + if (b->start == NULL) { + ngx_http_close_connection(c); + return; + } + + b->pos = b->start; + b->last = b->start; + b->end = b->last + size; + } + + n = c->recv(c, b->last, size); + + if (n == NGX_AGAIN) { + + if (!rev->timer_set) { + ngx_add_timer(rev, c->listening->post_accept_timeout); + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_connection(c); + return; + } + + /* + * We are trying to not hold c->buffer's memory for an idle connection. + */ + + if (ngx_pfree(c->pool, b->start) == NGX_OK) { + b->start = NULL; + } + + return; + } + + if (n == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } + + if (n == 0) { + ngx_log_error(NGX_LOG_INFO, c->log, ngx_socket_errno, + "client closed connection"); + ngx_http_close_connection(c); + return; + } + + b->last += n; + + c->log->action = "reading client request line"; + + ngx_http_init_request(rev); +} + + +static void ngx_http_init_request(ngx_event_t *rev) { ngx_pool_t *pool; @@ -377,13 +471,6 @@ ngx_http_init_request(ngx_event_t *rev) c = rev->data; - if (rev->timedout) { - ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); - - ngx_http_close_connection(c); - return; - } - c->requests++; hc = c->data; @@ -425,16 +512,6 @@ ngx_http_init_request(ngx_event_t *rev) ngx_http_set_connection_log(r->connection, clcf->error_log); - if (c->buffer == NULL) { - c->buffer = ngx_create_temp_buf(c->pool, - cscf->client_header_buffer_size); - if (c->buffer == NULL) { - ngx_destroy_pool(r->pool); - ngx_http_close_connection(c); - return; - } - } - r->header_in = hc->nbusy ? hc->busy[0] : c->buffer; if (ngx_list_init(&r->headers_out.headers, r->pool, 20, @@ -592,10 +669,10 @@ ngx_http_ssl_handshake(ngx_event_t *rev) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "plain http"); - c->log->action = "reading client request line"; - - rev->handler = ngx_http_init_request; - ngx_http_init_request(rev); + c->log->action = "waiting for request"; + + rev->handler = ngx_http_wait_request_handler; + ngx_http_wait_request_handler(rev); return; } @@ -620,12 +697,12 @@ ngx_http_ssl_handshake_handler(ngx_conne c->ssl->no_wait_shutdown = 1; - c->log->action = "reading client request line"; - - c->read->handler = ngx_http_init_request; + c->log->action = "waiting for request"; + + c->read->handler = ngx_http_wait_request_handler; /* STUB: epoll edge */ c->write->handler = ngx_http_empty_handler; - ngx_http_init_request(c->read); + ngx_http_wait_request_handler(c->read); return; } # HG changeset patch # User Valentin Bartenev # Date 1363794516 -14400 # Node ID c9c1959dee9f191084be5c8b646dc026169675f7 # Parent d3269427a19c02d93bc2681fcbd10941a462775d Use "client_header_timeout" for all requests in a connection. Previously, only the first request in a connection used timeout value from the "client_header_timeout" directive while reading header. All subsequent requests used "keepalive_timeout" for that. It happened because timeout of the read event was set to the value of "keepalive_timeout" in ngx_http_set_keepalive(), but was not removed when the next request arrived. diff -r d3269427a19c -r c9c1959dee9f src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Wed Mar 20 19:48:36 2013 +0400 +++ b/src/http/ngx_http_request.c Wed Mar 20 19:48:36 2013 +0400 @@ -2736,8 +2736,6 @@ ngx_http_set_keepalive(ngx_http_request_ c->data = hc; - ngx_add_timer(rev, clcf->keepalive_timeout); - if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_connection(c); return; @@ -2753,6 +2751,10 @@ ngx_http_set_keepalive(ngx_http_request_ hc->pipeline = 1; c->log->action = "reading client pipelined request line"; + if (rev->timer_set) { + ngx_del_timer(rev); + } + rev->handler = ngx_http_init_request; ngx_post_event(rev, &ngx_posted_events); return; @@ -2872,6 +2874,8 @@ ngx_http_set_keepalive(ngx_http_request_ c->idle = 1; ngx_reusable_connection(c, 1); + ngx_add_timer(rev, clcf->keepalive_timeout); + if (rev->ready) { ngx_post_event(rev, &ngx_posted_events); } @@ -2993,6 +2997,8 @@ ngx_http_keepalive_handler(ngx_event_t * c->idle = 0; ngx_reusable_connection(c, 0); + ngx_del_timer(rev); + ngx_http_init_request(rev); } # HG changeset patch # User Valentin Bartenev # Date 1363794516 -14400 # Node ID b1e43369703d5e64b56b5d2ce0077047c785e3be # Parent c9c1959dee9f191084be5c8b646dc026169675f7 Respect the new behavior of TCP_DEFER_ACCEPT. In Linux 2.6.32, TCP_DEFER_ACCEPT was changed to accept connections after the deferring period is finished without any data available. (Reading from the socket returns EAGAIN in this case.) Since in nginx TCP_DEFER_ACCEPT is set to "post_accept_timeout", we do not need to wait longer if deferred accept returns with no data. diff -r c9c1959dee9f -r b1e43369703d src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Wed Mar 20 19:48:36 2013 +0400 +++ b/src/http/ngx_http_request.c Wed Mar 20 19:48:36 2013 +0400 @@ -416,6 +416,20 @@ ngx_http_wait_request_handler(ngx_event_ if (n == NGX_AGAIN) { +#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) + if (c->listening->deferred_accept +#if (NGX_HTTP_SSL) + && c->ssl == NULL +#endif + ) + { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, + "client timed out in deferred accept"); + ngx_http_close_connection(c); + return; + } +#endif + if (!rev->timer_set) { ngx_add_timer(rev, c->listening->post_accept_timeout); } @@ -617,6 +631,15 @@ ngx_http_ssl_handshake(ngx_event_t *rev) if (n == -1) { if (err == NGX_EAGAIN) { +#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) + if (c->listening->deferred_accept) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, + "client timed out in deferred accept"); + ngx_http_close_connection(c); + return; + } +#endif + if (!rev->timer_set) { ngx_add_timer(rev, c->listening->post_accept_timeout); } # HG changeset patch # User Valentin V. Bartenev # Date 1351599261 -14400 # Node ID a96ef493684ccde5887e2d5694d66672133e55d8 # Parent b1e43369703d5e64b56b5d2ce0077047c785e3be Removed c->single_connection flag. The c->single_connection was intended to be used as lock mechanism to serialize modifications of request object from several threads working with client and upstream connections. The flag is redundant since threads in nginx have never been used that way. diff -r b1e43369703d -r a96ef493684c src/core/ngx_connection.c --- a/src/core/ngx_connection.c Wed Mar 20 19:48:36 2013 +0400 +++ b/src/core/ngx_connection.c Tue Oct 30 16:14:21 2012 +0400 @@ -900,11 +900,9 @@ ngx_close_connection(ngx_connection_t *c c->read->closed = 1; c->write->closed = 1; - if (c->single_connection) { - ngx_unlock(&c->lock); - c->read->locked = 0; - c->write->locked = 0; - } + ngx_unlock(&c->lock); + c->read->locked = 0; + c->write->locked = 0; ngx_mutex_unlock(ngx_posted_events_mutex); diff -r b1e43369703d -r a96ef493684c src/core/ngx_connection.h --- a/src/core/ngx_connection.h Wed Mar 20 19:48:36 2013 +0400 +++ b/src/core/ngx_connection.h Tue Oct 30 16:14:21 2012 +0400 @@ -152,7 +152,6 @@ struct ngx_connection_s { unsigned log_error:3; /* ngx_connection_log_error_e */ - unsigned single_connection:1; unsigned unexpected_eof:1; unsigned timedout:1; unsigned error:1; diff -r b1e43369703d -r a96ef493684c src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Wed Mar 20 19:48:36 2013 +0400 +++ b/src/http/ngx_http_request.c Tue Oct 30 16:14:21 2012 +0400 @@ -554,7 +554,6 @@ ngx_http_init_request(ngx_event_t *rev) return; } - c->single_connection = 1; c->destroyed = 0; #if (NGX_HTTP_SSL) diff -r b1e43369703d -r a96ef493684c src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c Wed Mar 20 19:48:36 2013 +0400 +++ b/src/http/ngx_http_upstream.c Tue Oct 30 16:14:21 2012 +0400 @@ -1118,8 +1118,6 @@ ngx_http_upstream_connect(ngx_http_reque r->connection->log->action = "connecting to upstream"; - r->connection->single_connection = 0; - if (u->state && u->state->response_sec) { tp = ngx_timeofday(); u->state->response_sec = tp->sec - u->state->response_sec; # HG changeset patch # User Valentin Bartenev # Date 1363794516 -14400 # Node ID a0ffc076b080e6e9723eed2ce29c6f0bcc01f9fe # Parent a96ef493684ccde5887e2d5694d66672133e55d8 Refactored ngx_http_init_request(). Now it can be used as the request object factory with minimal impact on the connection object. Therefore it was renamed to ngx_http_create_request(). diff -r a96ef493684c -r a0ffc076b080 src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Tue Oct 30 16:14:21 2012 +0400 +++ b/src/http/ngx_http_request.c Wed Mar 20 19:48:36 2013 +0400 @@ -11,7 +11,7 @@ static void ngx_http_wait_request_handler(ngx_event_t *ev); -static void ngx_http_init_request(ngx_event_t *ev); +static ngx_http_request_t *ngx_http_create_request(ngx_connection_t *c); static void ngx_http_process_request_line(ngx_event_t *rev); static void ngx_http_process_request_headers(ngx_event_t *rev); static ssize_t ngx_http_read_request_header(ngx_http_request_t *r); @@ -466,16 +466,22 @@ ngx_http_wait_request_handler(ngx_event_ c->log->action = "reading client request line"; - ngx_http_init_request(rev); + c->data = ngx_http_create_request(c); + if (c->data == NULL) { + ngx_http_close_connection(c); + return; + } + + rev->handler = ngx_http_process_request_line; + ngx_http_process_request_line(rev); } -static void -ngx_http_init_request(ngx_event_t *rev) +static ngx_http_request_t * +ngx_http_create_request(ngx_connection_t *c) { ngx_pool_t *pool; ngx_time_t *tp; - ngx_connection_t *c; ngx_http_request_t *r; ngx_http_log_ctx_t *ctx; ngx_http_connection_t *hc; @@ -483,8 +489,6 @@ ngx_http_init_request(ngx_event_t *rev) ngx_http_core_loc_conf_t *clcf; ngx_http_core_main_conf_t *cmcf; - c = rev->data; - c->requests++; hc = c->data; @@ -493,27 +497,19 @@ ngx_http_init_request(ngx_event_t *rev) pool = ngx_create_pool(cscf->request_pool_size, c->log); if (pool == NULL) { - ngx_http_close_connection(c); - return; + return NULL; } r = ngx_pcalloc(pool, sizeof(ngx_http_request_t)); if (r == NULL) { ngx_destroy_pool(pool); - ngx_http_close_connection(c); - return; + return NULL; } r->pool = pool; - r->pipeline = hc->pipeline; - - c->data = r; r->http_connection = hc; - - c->sent = 0; r->signature = NGX_HTTP_MODULE; - r->connection = c; r->main_conf = hc->conf_ctx->main_conf; @@ -533,15 +529,13 @@ ngx_http_init_request(ngx_event_t *rev) != NGX_OK) { ngx_destroy_pool(r->pool); - ngx_http_close_connection(c); - return; + return NULL; } r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module); if (r->ctx == NULL) { ngx_destroy_pool(r->pool); - ngx_http_close_connection(c); - return; + return NULL; } cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); @@ -550,12 +544,9 @@ ngx_http_init_request(ngx_event_t *rev) * sizeof(ngx_http_variable_value_t)); if (r->variables == NULL) { ngx_destroy_pool(r->pool); - ngx_http_close_connection(c); - return; + return NULL; } - c->destroyed = 0; - #if (NGX_HTTP_SSL) if (c->ssl) { r->main_filter_need_in_memory = 1; @@ -592,8 +583,7 @@ ngx_http_init_request(ngx_event_t *rev) (void) ngx_atomic_fetch_add(ngx_stat_requests, 1); #endif - rev->handler = ngx_http_process_request_line; - ngx_http_process_request_line(rev); + return r; } @@ -2722,7 +2712,7 @@ ngx_http_set_keepalive(ngx_http_request_ /* * If the large header buffers were allocated while the previous * request processing then we do not use c->buffer for - * the pipelined request (see ngx_http_init_request()). + * the pipelined request (see ngx_http_create_request()). * * Now we would move the large header buffers to the free list. */ @@ -2770,20 +2760,30 @@ ngx_http_set_keepalive(ngx_http_request_ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "pipelined request"); - hc->pipeline = 1; c->log->action = "reading client pipelined request line"; + r = ngx_http_create_request(c); + if (r == NULL) { + ngx_http_close_connection(c); + return; + } + + r->pipeline = 1; + + c->data = r; + + c->sent = 0; + c->destroyed = 0; + if (rev->timer_set) { ngx_del_timer(rev); } - rev->handler = ngx_http_init_request; + rev->handler = ngx_http_process_request_line; ngx_post_event(rev, &ngx_posted_events); return; } - hc->pipeline = 0; - /* * To keep a memory footprint as small as possible for an idle keepalive * connection we try to free c->buffer's memory if it was allocated outside @@ -3019,9 +3019,19 @@ ngx_http_keepalive_handler(ngx_event_t * c->idle = 0; ngx_reusable_connection(c, 0); + c->data = ngx_http_create_request(c); + if (c->data == NULL) { + ngx_http_close_connection(c); + return; + } + + c->sent = 0; + c->destroyed = 0; + ngx_del_timer(rev); - ngx_http_init_request(rev); + rev->handler = ngx_http_process_request_line; + ngx_http_process_request_line(rev); } diff -r a96ef493684c -r a0ffc076b080 src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h Tue Oct 30 16:14:21 2012 +0400 +++ b/src/http/ngx_http_request.h Wed Mar 20 19:48:36 2013 +0400 @@ -308,8 +308,9 @@ typedef struct { ngx_buf_t **free; ngx_int_t nfree; - unsigned pipeline:1; - unsigned ssl:1; +#if (NGX_HTTP_SSL) + ngx_uint_t ssl; /* unsigned ssl:1; */ +#endif } ngx_http_connection_t; # HG changeset patch # User Valentin Bartenev # Date 1363794516 -14400 # Node ID e9a6d8aad0239030dd2a68ea1dac1ba9b01e0cdd # Parent a0ffc076b080e6e9723eed2ce29c6f0bcc01f9fe SSL: Next Protocol Negotiation extension support. Not only this is useful for the upcoming SPDY support, but it can also help to improve HTTPS performance by enabling TLS False Start in Chrome/Chromium browsers [1]. So, we always enable NPN for HTTPS if it is supported by OpenSSL. [1] http://www.imperialviolet.org/2012/04/11/falsestart.html diff -r a0ffc076b080 -r e9a6d8aad023 src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c Wed Mar 20 19:48:36 2013 +0400 +++ b/src/http/modules/ngx_http_ssl_module.c Wed Mar 20 19:48:36 2013 +0400 @@ -18,6 +18,11 @@ typedef ngx_int_t (*ngx_ssl_variable_han #define NGX_DEFAULT_ECDH_CURVE "prime256v1" +#ifdef TLSEXT_TYPE_next_proto_neg +static int ngx_http_ssl_npn_advertised(ngx_ssl_conn_t *ssl_conn, + const unsigned char **out, unsigned int *outlen, void *arg); +#endif + static ngx_int_t ngx_http_ssl_static_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_ssl_variable(ngx_http_request_t *r, @@ -262,6 +267,30 @@ static ngx_http_variable_t ngx_http_ssl static ngx_str_t ngx_http_ssl_sess_id_ctx = ngx_string("HTTP"); +#ifdef TLSEXT_TYPE_next_proto_neg + +#define NGX_HTTP_NPN_ADVERTISE "\x08http/1.1" + +static int +ngx_http_ssl_npn_advertised(ngx_ssl_conn_t *ssl_conn, + const unsigned char **out, unsigned int *outlen, void *arg) +{ +#if (NGX_DEBUG) + ngx_connection_t *c; + + c = ngx_ssl_get_connection(ssl_conn); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "SSL NPN advertised"); +#endif + + *out = (unsigned char *) NGX_HTTP_NPN_ADVERTISE; + *outlen = sizeof(NGX_HTTP_NPN_ADVERTISE) - 1; + + return SSL_TLSEXT_ERR_OK; +} + +#endif + + static ngx_int_t ngx_http_ssl_static_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) @@ -490,6 +519,11 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t * #endif +#ifdef TLSEXT_TYPE_next_proto_neg + SSL_CTX_set_next_protos_advertised_cb(conf->ssl.ctx, + ngx_http_ssl_npn_advertised, NULL); +#endif + cln = ngx_pool_cleanup_add(cf->pool, 0); if (cln == NULL) { return NGX_CONF_ERROR; # HG changeset patch # User Valentin Bartenev # Date 1363794516 -14400 # Node ID c8c95da704ba326088af580195c07a0c35d83549 # Parent e9a6d8aad0239030dd2a68ea1dac1ba9b01e0cdd Gzip: fixed setting of NGX_HTTP_GZIP_BUFFERED. In r2411 setting of NGX_HTTP_GZIP_BUFFERED in c->buffered was moved from ngx_http_gzip_filter_deflate_start() to ngx_http_gzip_filter_buffer() since it was always called first. But in r2543 the "postpone_gzipping" directive was introduced, and if postponed gzipping is disabled (the default setting), ngx_http_gzip_filter_buffer() is not called at all. We must always set NGX_HTTP_GZIP_BUFFERED after the start of compression since there is always a trailer that is buffered. There are no known cases when it leads to any problem with current code. But we already had troubles in upcoming SPDY implementation. diff -r e9a6d8aad023 -r c8c95da704ba src/http/modules/ngx_http_gzip_filter_module.c --- a/src/http/modules/ngx_http_gzip_filter_module.c Wed Mar 20 19:48:36 2013 +0400 +++ b/src/http/modules/ngx_http_gzip_filter_module.c Wed Mar 20 19:48:36 2013 +0400 @@ -620,6 +620,8 @@ ngx_http_gzip_filter_deflate_start(ngx_h return NGX_ERROR; } + r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED; + ctx->last_out = &ctx->out; ctx->crc32 = crc32(0L, Z_NULL, 0); ctx->flush = Z_NO_FLUSH; # HG changeset patch # User Valentin Bartenev # Date 1363794516 -14400 # Node ID 23d8e3d51b90d4a02cc40ec9e06d3d2149f20a2a # Parent c8c95da704ba326088af580195c07a0c35d83549 Removed unused prototype of ngx_http_find_server_conf(). This function prototype and its implementation was added in r90, but the implementation was removed in r97. diff -r c8c95da704ba -r 23d8e3d51b90 src/http/ngx_http.h --- a/src/http/ngx_http.h Wed Mar 20 19:48:36 2013 +0400 +++ b/src/http/ngx_http.h Wed Mar 20 19:48:36 2013 +0400 @@ -104,7 +104,6 @@ ngx_int_t ngx_http_parse_chunked(ngx_htt ngx_http_chunked_t *ctx); -ngx_int_t ngx_http_find_server_conf(ngx_http_request_t *r); void ngx_http_update_location_config(ngx_http_request_t *r); void ngx_http_handler(ngx_http_request_t *r); void ngx_http_run_posted_requests(ngx_connection_t *c); # HG changeset patch # User Valentin Bartenev # Date 1363794516 -14400 # Node ID 42ddcd747b9aa502f425129aae225ecef1b6f9dd # Parent 23d8e3d51b90d4a02cc40ec9e06d3d2149f20a2a Allow to reuse connections that wait their first request. This should improve behavior under deficiency of connections. Since SSL handshake usually takes significant amount of time, we exclude connections from reusable queue during this period to avoid premature flush of them. diff -r 23d8e3d51b90 -r 42ddcd747b9a src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Wed Mar 20 19:48:36 2013 +0400 +++ b/src/http/ngx_http_request.c Wed Mar 20 19:48:36 2013 +0400 @@ -355,6 +355,7 @@ ngx_http_init_connection(ngx_connection_ } ngx_add_timer(rev, c->listening->post_accept_timeout); + ngx_reusable_connection(c, 1); if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_connection(c); @@ -383,6 +384,11 @@ ngx_http_wait_request_handler(ngx_event_ return; } + if (c->close) { + ngx_http_close_connection(c); + return; + } + hc = c->data; cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); @@ -432,6 +438,7 @@ ngx_http_wait_request_handler(ngx_event_ if (!rev->timer_set) { ngx_add_timer(rev, c->listening->post_accept_timeout); + ngx_reusable_connection(c, 1); } if (ngx_handle_read_event(rev, 0) != NGX_OK) { @@ -466,6 +473,8 @@ ngx_http_wait_request_handler(ngx_event_ c->log->action = "reading client request line"; + ngx_reusable_connection(c, 0); + c->data = ngx_http_create_request(c); if (c->data == NULL) { ngx_http_close_connection(c); @@ -611,6 +620,11 @@ ngx_http_ssl_handshake(ngx_event_t *rev) return; } + if (c->close) { + ngx_http_close_connection(c); + return; + } + n = recv(c->fd, (char *) buf, 1, MSG_PEEK); err = ngx_socket_errno; @@ -631,6 +645,7 @@ ngx_http_ssl_handshake(ngx_event_t *rev) if (!rev->timer_set) { ngx_add_timer(rev, c->listening->post_accept_timeout); + ngx_reusable_connection(c, 1); } if (ngx_handle_read_event(rev, 0) != NGX_OK) { @@ -670,6 +685,8 @@ ngx_http_ssl_handshake(ngx_event_t *rev) ngx_add_timer(rev, c->listening->post_accept_timeout); } + ngx_reusable_connection(c, 0); + c->ssl->handler = ngx_http_ssl_handshake_handler; return; } @@ -714,6 +731,8 @@ ngx_http_ssl_handshake_handler(ngx_conne c->read->handler = ngx_http_wait_request_handler; /* STUB: epoll edge */ c->write->handler = ngx_http_empty_handler; + ngx_reusable_connection(c, 1); + ngx_http_wait_request_handler(c->read); return; # HG changeset patch # User Valentin Bartenev # Date 1363794516 -14400 # Node ID 9aadbbcb88235000e3fa14b88316f1218be9653c # Parent 42ddcd747b9aa502f425129aae225ecef1b6f9dd Status: introduced the "ngx_stat_waiting" counter. And corresponding variable $connections_waiting was added. Previously, waiting connections were counted as the difference between active connections and the sum of reading and writing connections. That made it impossible to count more than one request in one connection as reading or writing (as is the case for SPDY). Also, we no longer count connections in handshake state as waiting. diff -r 42ddcd747b9a -r 9aadbbcb8823 src/core/ngx_connection.c --- a/src/core/ngx_connection.c Wed Mar 20 19:48:36 2013 +0400 +++ b/src/core/ngx_connection.c Wed Mar 20 19:48:36 2013 +0400 @@ -970,6 +970,10 @@ ngx_reusable_connection(ngx_connection_t if (c->reusable) { ngx_queue_remove(&c->queue); + +#if (NGX_STAT_STUB) + (void) ngx_atomic_fetch_add(ngx_stat_waiting, -1); +#endif } c->reusable = reusable; @@ -979,6 +983,10 @@ ngx_reusable_connection(ngx_connection_t ngx_queue_insert_head( (ngx_queue_t *) &ngx_cycle->reusable_connections_queue, &c->queue); + +#if (NGX_STAT_STUB) + (void) ngx_atomic_fetch_add(ngx_stat_waiting, 1); +#endif } } diff -r 42ddcd747b9a -r 9aadbbcb8823 src/event/ngx_event.c --- a/src/event/ngx_event.c Wed Mar 20 19:48:36 2013 +0400 +++ b/src/event/ngx_event.c Wed Mar 20 19:48:36 2013 +0400 @@ -73,6 +73,8 @@ ngx_atomic_t ngx_stat_reading0; ngx_atomic_t *ngx_stat_reading = &ngx_stat_reading0; ngx_atomic_t ngx_stat_writing0; ngx_atomic_t *ngx_stat_writing = &ngx_stat_writing0; +ngx_atomic_t ngx_stat_waiting0; +ngx_atomic_t *ngx_stat_waiting = &ngx_stat_waiting0; #endif @@ -511,7 +513,8 @@ ngx_event_module_init(ngx_cycle_t *cycle + cl /* ngx_stat_requests */ + cl /* ngx_stat_active */ + cl /* ngx_stat_reading */ - + cl; /* ngx_stat_writing */ + + cl /* ngx_stat_writing */ + + cl; /* ngx_stat_waiting */ #endif @@ -558,6 +561,7 @@ ngx_event_module_init(ngx_cycle_t *cycle ngx_stat_active = (ngx_atomic_t *) (shared + 6 * cl); ngx_stat_reading = (ngx_atomic_t *) (shared + 7 * cl); ngx_stat_writing = (ngx_atomic_t *) (shared + 8 * cl); + ngx_stat_waiting = (ngx_atomic_t *) (shared + 9 * cl); #endif diff -r 42ddcd747b9a -r 9aadbbcb8823 src/event/ngx_event.h --- a/src/event/ngx_event.h Wed Mar 20 19:48:36 2013 +0400 +++ b/src/event/ngx_event.h Wed Mar 20 19:48:36 2013 +0400 @@ -511,6 +511,7 @@ extern ngx_atomic_t *ngx_stat_requests; extern ngx_atomic_t *ngx_stat_active; extern ngx_atomic_t *ngx_stat_reading; extern ngx_atomic_t *ngx_stat_writing; +extern ngx_atomic_t *ngx_stat_waiting; #endif diff -r 42ddcd747b9a -r 9aadbbcb8823 src/http/modules/ngx_http_stub_status_module.c --- a/src/http/modules/ngx_http_stub_status_module.c Wed Mar 20 19:48:36 2013 +0400 +++ b/src/http/modules/ngx_http_stub_status_module.c Wed Mar 20 19:48:36 2013 +0400 @@ -73,6 +73,9 @@ static ngx_http_variable_t ngx_http_stu { ngx_string("connections_writing"), NULL, ngx_http_stub_status_variable, 2, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + { ngx_string("connections_waiting"), NULL, ngx_http_stub_status_variable, + 3, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + { ngx_null_string, NULL, NULL, 0, 0, 0 } }; @@ -83,7 +86,7 @@ static ngx_int_t ngx_http_status_handler ngx_int_t rc; ngx_buf_t *b; ngx_chain_t out; - ngx_atomic_int_t ap, hn, ac, rq, rd, wr; + ngx_atomic_int_t ap, hn, ac, rq, rd, wr, wa; if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) { return NGX_HTTP_NOT_ALLOWED; @@ -126,6 +129,7 @@ static ngx_int_t ngx_http_status_handler rq = *ngx_stat_requests; rd = *ngx_stat_reading; wr = *ngx_stat_writing; + wa = *ngx_stat_waiting; b->last = ngx_sprintf(b->last, "Active connections: %uA \n", ac); @@ -135,7 +139,7 @@ static ngx_int_t ngx_http_status_handler b->last = ngx_sprintf(b->last, " %uA %uA %uA \n", ap, hn, rq); b->last = ngx_sprintf(b->last, "Reading: %uA Writing: %uA Waiting: %uA \n", - rd, wr, ac - (rd + wr)); + rd, wr, wa); r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = b->last - b->pos; @@ -177,6 +181,10 @@ ngx_http_stub_status_variable(ngx_http_r value = *ngx_stat_writing; break; + case 3: + value = *ngx_stat_waiting; + break; + /* suppress warning */ default: value = 0; # HG changeset patch # User Valentin Bartenev # Date 1363794516 -14400 # Node ID ed600b1e18083c077430e6d4ba5c5420d738e4b2 # Parent 9aadbbcb88235000e3fa14b88316f1218be9653c URI processing code moved to a separate function. This allows to reuse it in the upcoming SPDY module. diff -r 9aadbbcb8823 -r ed600b1e1808 src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Wed Mar 20 19:48:36 2013 +0400 +++ b/src/http/ngx_http_request.c Wed Mar 20 19:48:36 2013 +0400 @@ -31,6 +31,7 @@ static ngx_int_t ngx_http_process_connec static ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_process_request_uri(ngx_http_request_t *r); static ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r); static void ngx_http_process_request(ngx_http_request_t *r); static ngx_int_t ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool, @@ -838,12 +839,11 @@ ngx_http_ssl_servername(ngx_ssl_conn_t * static void ngx_http_process_request_line(ngx_event_t *rev) { - ssize_t n; - ngx_int_t rc, rv; - ngx_str_t host; - ngx_connection_t *c; - ngx_http_request_t *r; - ngx_http_core_srv_conf_t *cscf; + ssize_t n; + ngx_int_t rc, rv; + ngx_str_t host; + ngx_connection_t *c; + ngx_http_request_t *r; c = rev->data; r = c->data; @@ -880,130 +880,20 @@ ngx_http_process_request_line(ngx_event_ r->request_line.data = r->request_start; r->request_length = r->header_in->pos - r->request_start; - - if (r->args_start) { - r->uri.len = r->args_start - 1 - r->uri_start; - } else { - r->uri.len = r->uri_end - r->uri_start; - } - - - if (r->complex_uri || r->quoted_uri) { - - r->uri.data = ngx_pnalloc(r->pool, r->uri.len + 1); - if (r->uri.data == NULL) { - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return; - } - - cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); - - rc = ngx_http_parse_complex_uri(r, cscf->merge_slashes); - - if (rc == NGX_HTTP_PARSE_INVALID_REQUEST) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid request"); - ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); - return; - } - - } else { - r->uri.data = r->uri_start; - } - - - r->unparsed_uri.len = r->uri_end - r->uri_start; - r->unparsed_uri.data = r->uri_start; - - r->valid_unparsed_uri = r->space_in_uri ? 0 : 1; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http request line: \"%V\"", &r->request_line); r->method_name.len = r->method_end - r->request_start + 1; r->method_name.data = r->request_line.data; - if (r->http_protocol.data) { r->http_protocol.len = r->request_end - r->http_protocol.data; } - - if (r->uri_ext) { - if (r->args_start) { - r->exten.len = r->args_start - 1 - r->uri_ext; - } else { - r->exten.len = r->uri_end - r->uri_ext; - } - - r->exten.data = r->uri_ext; + if (ngx_http_process_request_uri(r) != NGX_OK) { + return; } - - if (r->args_start && r->uri_end > r->args_start) { - r->args.len = r->uri_end - r->args_start; - r->args.data = r->args_start; - } - -#if (NGX_WIN32) - { - u_char *p, *last; - - p = r->uri.data; - last = r->uri.data + r->uri.len; - - while (p < last) { - - if (*p++ == ':') { - - /* - * this check covers "::$data", "::$index_allocation" and - * ":$i30:$index_allocation" - */ - - if (p < last && *p == '$') { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent unsafe win32 URI"); - ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); - return; - } - } - } - - p = r->uri.data + r->uri.len - 1; - - while (p > r->uri.data) { - - if (*p == ' ') { - p--; - continue; - } - - if (*p == '.') { - p--; - continue; - } - - break; - } - - if (p != r->uri.data + r->uri.len - 1) { - r->uri.len = p + 1 - r->uri.data; - ngx_http_set_exten(r); - } - - } -#endif - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http request line: \"%V\"", &r->request_line); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http uri: \"%V\"", &r->uri); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http args: \"%V\"", &r->args); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http exten: \"%V\"", &r->exten); - if (r->host_start && r->host_end) { host.len = r->host_end - r->host_start; @@ -1095,6 +985,121 @@ ngx_http_process_request_line(ngx_event_ } +static ngx_int_t +ngx_http_process_request_uri(ngx_http_request_t *r) +{ + ngx_http_core_srv_conf_t *cscf; + + if (r->args_start) { + r->uri.len = r->args_start - 1 - r->uri_start; + } else { + r->uri.len = r->uri_end - r->uri_start; + } + + if (r->complex_uri || r->quoted_uri) { + + r->uri.data = ngx_pnalloc(r->pool, r->uri.len + 1); + if (r->uri.data == NULL) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + if (ngx_http_parse_complex_uri(r, cscf->merge_slashes) != NGX_OK) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid request"); + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; + } + + } else { + r->uri.data = r->uri_start; + } + + r->unparsed_uri.len = r->uri_end - r->uri_start; + r->unparsed_uri.data = r->uri_start; + + r->valid_unparsed_uri = r->space_in_uri ? 0 : 1; + + if (r->uri_ext) { + if (r->args_start) { + r->exten.len = r->args_start - 1 - r->uri_ext; + } else { + r->exten.len = r->uri_end - r->uri_ext; + } + + r->exten.data = r->uri_ext; + } + + if (r->args_start && r->uri_end > r->args_start) { + r->args.len = r->uri_end - r->args_start; + r->args.data = r->args_start; + } + +#if (NGX_WIN32) + { + u_char *p, *last; + + p = r->uri.data; + last = r->uri.data + r->uri.len; + + while (p < last) { + + if (*p++ == ':') { + + /* + * this check covers "::$data", "::$index_allocation" and + * ":$i30:$index_allocation" + */ + + if (p < last && *p == '$') { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent unsafe win32 URI"); + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; + } + } + } + + p = r->uri.data + r->uri.len - 1; + + while (p > r->uri.data) { + + if (*p == ' ') { + p--; + continue; + } + + if (*p == '.') { + p--; + continue; + } + + break; + } + + if (p != r->uri.data + r->uri.len - 1) { + r->uri.len = p + 1 - r->uri.data; + ngx_http_set_exten(r); + } + + } +#endif + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http uri: \"%V\"", &r->uri); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http args: \"%V\"", &r->args); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http exten: \"%V\"", &r->exten); + + return NGX_OK; +} + + static void ngx_http_process_request_headers(ngx_event_t *rev) { # HG changeset patch # User Valentin Bartenev # Date 1363794516 -14400 # Node ID a10cc9106dc7521e3b0b4f64f8e028907deaa8bd # Parent ed600b1e18083c077430e6d4ba5c5420d738e4b2 Win32: disable MSVC warning diff -r ed600b1e1808 -r a10cc9106dc7 src/os/win32/ngx_win32_config.h --- a/src/os/win32/ngx_win32_config.h Wed Mar 20 19:48:36 2013 +0400 +++ b/src/os/win32/ngx_win32_config.h Wed Mar 20 19:48:36 2013 +0400 @@ -70,6 +70,9 @@ typedef long time_t; /* FD_SET() and FD_CLR(): conditional expression is constant */ #pragma warning(disable:4127) +/* array is too small to include a terminating null character */ +#pragma warning(disable:4295) + #endif # HG changeset patch # User Valentin Bartenev # Date 1363794580 -14400 # Node ID 7271c16e52cc6b833de11cc87b7e5c1147c471c8 # Parent a10cc9106dc7521e3b0b4f64f8e028907deaa8bd Preliminary experimental support for SPDY draft 2. diff -r a10cc9106dc7 -r 7271c16e52cc auto/modules --- a/auto/modules Wed Mar 20 19:48:36 2013 +0400 +++ b/auto/modules Wed Mar 20 19:49:40 2013 +0400 @@ -100,6 +100,7 @@ fi # ngx_http_write_filter # ngx_http_header_filter # ngx_http_chunked_filter +# ngx_http_spdy_filter # ngx_http_range_header_filter # ngx_http_gzip_filter # ngx_http_postpone_filter @@ -118,8 +119,13 @@ fi HTTP_FILTER_MODULES="$HTTP_WRITE_FILTER_MODULE \ $HTTP_HEADER_FILTER_MODULE \ - $HTTP_CHUNKED_FILTER_MODULE \ - $HTTP_RANGE_HEADER_FILTER_MODULE" + $HTTP_CHUNKED_FILTER_MODULE" + +if [ $HTTP_SPDY = YES ]; then + HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_SPDY_FILTER_MODULE" +fi + +HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_RANGE_HEADER_FILTER_MODULE" if [ $HTTP_GZIP = YES ]; then have=NGX_HTTP_GZIP . auto/have @@ -179,6 +185,15 @@ if [ $HTTP_USERID = YES ]; then HTTP_SRCS="$HTTP_SRCS $HTTP_USERID_SRCS" fi + +if [ $HTTP_SPDY = YES ]; then + have=NGX_HTTP_SPDY . auto/have + USE_ZLIB=YES + HTTP_MODULES="$HTTP_MODULES $HTTP_SPDY_MODULE" + HTTP_DEPS="$HTTP_DEPS $HTTP_SPDY_DEPS" + HTTP_SRCS="$HTTP_SRCS $HTTP_SPDY_SRCS" +fi + HTTP_MODULES="$HTTP_MODULES $HTTP_STATIC_MODULE" if [ $HTTP_GZIP_STATIC = YES ]; then diff -r a10cc9106dc7 -r 7271c16e52cc auto/options --- a/auto/options Wed Mar 20 19:48:36 2013 +0400 +++ b/auto/options Wed Mar 20 19:49:40 2013 +0400 @@ -60,6 +60,7 @@ HTTP_CACHE=YES HTTP_CHARSET=YES HTTP_GZIP=YES HTTP_SSL=NO +HTTP_SPDY=NO HTTP_SSI=YES HTTP_POSTPONE=NO HTTP_REALIP=NO @@ -202,6 +203,7 @@ do --http-scgi-temp-path=*) NGX_HTTP_SCGI_TEMP_PATH="$value" ;; --with-http_ssl_module) HTTP_SSL=YES ;; + --with-http_spdy_module) HTTP_SPDY=YES ;; --with-http_realip_module) HTTP_REALIP=YES ;; --with-http_addition_module) HTTP_ADDITION=YES ;; --with-http_xslt_module) HTTP_XSLT=YES ;; @@ -349,6 +351,7 @@ cat << END --with-ipv6 enable IPv6 support --with-http_ssl_module enable ngx_http_ssl_module + --with-http_spdy_module enable ngx_http_spdy_module --with-http_realip_module enable ngx_http_realip_module --with-http_addition_module enable ngx_http_addition_module --with-http_xslt_module enable ngx_http_xslt_module diff -r a10cc9106dc7 -r 7271c16e52cc auto/sources --- a/auto/sources Wed Mar 20 19:48:36 2013 +0400 +++ b/auto/sources Wed Mar 20 19:49:40 2013 +0400 @@ -324,6 +324,15 @@ HTTP_POSTPONE_FILTER_SRCS=src/http/ngx_h HTTP_FILE_CACHE_SRCS=src/http/ngx_http_file_cache.c +HTTP_SPDY_MODULE=ngx_http_spdy_module +HTTP_SPDY_FILTER_MODULE=ngx_http_spdy_filter_module +HTTP_SPDY_DEPS="src/http/ngx_http_spdy.h \ + src/http/ngx_http_spdy_module.h" +HTTP_SPDY_SRCS="src/http/ngx_http_spdy.c \ + src/http/ngx_http_spdy_module.c \ + src/http/ngx_http_spdy_filter_module.c" + + HTTP_CHARSET_FILTER_MODULE=ngx_http_charset_filter_module HTTP_CHARSET_SRCS=src/http/modules/ngx_http_charset_filter_module.c diff -r a10cc9106dc7 -r 7271c16e52cc src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c Wed Mar 20 19:48:36 2013 +0400 +++ b/src/http/modules/ngx_http_ssl_module.c Wed Mar 20 19:49:40 2013 +0400 @@ -275,13 +275,28 @@ static int ngx_http_ssl_npn_advertised(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, unsigned int *outlen, void *arg) { -#if (NGX_DEBUG) +#if (NGX_HTTP_SPDY || NGX_DEBUG) ngx_connection_t *c; c = ngx_ssl_get_connection(ssl_conn); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "SSL NPN advertised"); #endif +#if (NGX_HTTP_SPDY) + { + ngx_http_connection_t *hc; + + hc = c->data; + + if (hc->addr_conf->spdy) { + *out = (unsigned char *) NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE; + *outlen = sizeof(NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1; + + return SSL_TLSEXT_ERR_OK; + } + } +#endif + *out = (unsigned char *) NGX_HTTP_NPN_ADVERTISE; *outlen = sizeof(NGX_HTTP_NPN_ADVERTISE) - 1; diff -r a10cc9106dc7 -r 7271c16e52cc src/http/ngx_http.c --- a/src/http/ngx_http.c Wed Mar 20 19:48:36 2013 +0400 +++ b/src/http/ngx_http.c Wed Mar 20 19:49:40 2013 +0400 @@ -1225,6 +1225,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, n #if (NGX_HTTP_SSL) ngx_uint_t ssl; #endif +#if (NGX_HTTP_SPDY) + ngx_uint_t spdy; +#endif /* * we cannot compare whole sockaddr struct's as kernel @@ -1277,6 +1280,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, n #if (NGX_HTTP_SSL) ssl = lsopt->ssl || addr[i].opt.ssl; #endif +#if (NGX_HTTP_SPDY) + spdy = lsopt->spdy || addr[i].opt.spdy; +#endif if (lsopt->set) { @@ -1307,6 +1313,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, n #if (NGX_HTTP_SSL) addr[i].opt.ssl = ssl; #endif +#if (NGX_HTTP_SPDY) + addr[i].opt.spdy = spdy; +#endif return NGX_OK; } @@ -1337,6 +1346,14 @@ ngx_http_add_address(ngx_conf_t *cf, ngx } } +#if (NGX_HTTP_SPDY && NGX_HTTP_SSL && !defined TLSEXT_TYPE_next_proto_neg) + if (lsopt->spdy && lsopt->ssl) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "nginx was built without OpenSSL NPN support, " + "SPDY is not enabled for %s", lsopt->addr); + } +#endif + addr = ngx_array_push(&port->addrs); if (addr == NULL) { return NGX_ERROR; @@ -1820,6 +1837,9 @@ ngx_http_add_addrs(ngx_conf_t *cf, ngx_h #if (NGX_HTTP_SSL) addrs[i].conf.ssl = addr[i].opt.ssl; #endif +#if (NGX_HTTP_SPDY) + addrs[i].conf.spdy = addr[i].opt.spdy; +#endif if (addr[i].hash.buckets == NULL && (addr[i].wc_head == NULL @@ -1881,6 +1901,9 @@ ngx_http_add_addrs6(ngx_conf_t *cf, ngx_ #if (NGX_HTTP_SSL) addrs6[i].conf.ssl = addr[i].opt.ssl; #endif +#if (NGX_HTTP_SPDY) + addrs6[i].conf.spdy = addr[i].opt.spdy; +#endif if (addr[i].hash.buckets == NULL && (addr[i].wc_head == NULL diff -r a10cc9106dc7 -r 7271c16e52cc src/http/ngx_http.h --- a/src/http/ngx_http.h Wed Mar 20 19:48:36 2013 +0400 +++ b/src/http/ngx_http.h Wed Mar 20 19:49:40 2013 +0400 @@ -20,6 +20,10 @@ typedef struct ngx_http_file_cache_s ng typedef struct ngx_http_log_ctx_s ngx_http_log_ctx_t; typedef struct ngx_http_chunked_s ngx_http_chunked_t; +#if (NGX_HTTP_SPDY) +typedef struct ngx_http_spdy_stream_s ngx_http_spdy_stream_t; +#endif + typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); typedef u_char *(*ngx_http_log_handler_pt)(ngx_http_request_t *r, @@ -35,6 +39,9 @@ typedef u_char *(*ngx_http_log_handler_p #include #include +#if (NGX_HTTP_SPDY) +#include +#endif #if (NGX_HTTP_CACHE) #include #endif @@ -80,12 +87,14 @@ ngx_int_t ngx_http_add_listen(ngx_conf_t void ngx_http_init_connection(ngx_connection_t *c); +void ngx_http_close_connection(ngx_connection_t *c); #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME int ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg); #endif ngx_int_t ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b); +ngx_int_t ngx_http_parse_uri(ngx_http_request_t *r); ngx_int_t ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes); ngx_int_t ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b, @@ -104,12 +113,17 @@ ngx_int_t ngx_http_parse_chunked(ngx_htt ngx_http_chunked_t *ctx); +ngx_http_request_t *ngx_http_create_request(ngx_connection_t *c); +ngx_int_t ngx_http_process_request_uri(ngx_http_request_t *r); +ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r); +void ngx_http_process_request(ngx_http_request_t *r); void ngx_http_update_location_config(ngx_http_request_t *r); void ngx_http_handler(ngx_http_request_t *r); void ngx_http_run_posted_requests(ngx_connection_t *c); ngx_int_t ngx_http_post_request(ngx_http_request_t *r, ngx_http_posted_request_t *pr); void ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc); +void ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc); void ngx_http_empty_handler(ngx_event_t *wev); void ngx_http_request_empty_handler(ngx_http_request_t *r); diff -r a10cc9106dc7 -r 7271c16e52cc src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c Wed Mar 20 19:48:36 2013 +0400 +++ b/src/http/ngx_http_core_module.c Wed Mar 20 19:49:40 2013 +0400 @@ -2130,6 +2130,13 @@ ngx_http_gzip_ok(ngx_http_request_t *r) return NGX_DECLINED; } +#if (NGX_HTTP_SPDY) + if (r->spdy_stream) { + r->gzip_ok = 1; + return NGX_OK; + } +#endif + ae = r->headers_in.accept_encoding; if (ae == NULL) { return NGX_DECLINED; @@ -2464,6 +2471,10 @@ ngx_http_subrequest(ngx_http_request_t * sr->request_body = r->request_body; +#if (NGX_HTTP_SPDY) + sr->spdy_stream = r->spdy_stream; +#endif + sr->method = NGX_HTTP_GET; sr->http_version = r->http_version; @@ -4130,6 +4141,18 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx #endif } + if (ngx_strcmp(value[n].data, "spdy") == 0) { +#if (NGX_HTTP_SPDY) + lsopt.spdy = 1; + continue; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"spdy\" parameter requires " + "ngx_http_spdy_module"); + return NGX_CONF_ERROR; +#endif + } + if (ngx_strncmp(value[n].data, "so_keepalive=", 13) == 0) { if (ngx_strcmp(&value[n].data[13], "on") == 0) { diff -r a10cc9106dc7 -r 7271c16e52cc src/http/ngx_http_core_module.h --- a/src/http/ngx_http_core_module.h Wed Mar 20 19:48:36 2013 +0400 +++ b/src/http/ngx_http_core_module.h Wed Mar 20 19:49:40 2013 +0400 @@ -75,6 +75,9 @@ typedef struct { #if (NGX_HTTP_SSL) unsigned ssl:1; #endif +#if (NGX_HTTP_SPDY) + unsigned spdy:1; +#endif #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) unsigned ipv6only:1; #endif @@ -232,7 +235,10 @@ struct ngx_http_addr_conf_s { ngx_http_virtual_names_t *virtual_names; #if (NGX_HTTP_SSL) - ngx_uint_t ssl; /* unsigned ssl:1; */ + unsigned ssl:1; +#endif +#if (NGX_HTTP_SPDY) + unsigned spdy:1; #endif }; diff -r a10cc9106dc7 -r 7271c16e52cc src/http/ngx_http_parse.c --- a/src/http/ngx_http_parse.c Wed Mar 20 19:48:36 2013 +0400 +++ b/src/http/ngx_http_parse.c Wed Mar 20 19:49:40 2013 +0400 @@ -1075,6 +1075,154 @@ header_done: ngx_int_t +ngx_http_parse_uri(ngx_http_request_t *r) +{ + u_char *p, ch; + enum { + sw_start = 0, + sw_after_slash_in_uri, + sw_check_uri, + sw_uri + } state; + + state = sw_start; + + for (p = r->uri_start; p != r->uri_end; p++) { + + ch = *p; + + switch (state) { + + case sw_start: + + if (ch != '/') { + return NGX_ERROR; + } + + state = sw_after_slash_in_uri; + break; + + /* check "/.", "//", "%", and "\" (Win32) in URI */ + case sw_after_slash_in_uri: + + if (usual[ch >> 5] & (1 << (ch & 0x1f))) { + state = sw_check_uri; + break; + } + + switch (ch) { + case ' ': + r->space_in_uri = 1; + state = sw_check_uri; + break; + case '.': + r->complex_uri = 1; + state = sw_uri; + break; + case '%': + r->quoted_uri = 1; + state = sw_uri; + break; + case '/': + r->complex_uri = 1; + state = sw_uri; + break; +#if (NGX_WIN32) + case '\\': + r->complex_uri = 1; + state = sw_uri; + break; +#endif + case '?': + r->args_start = p + 1; + state = sw_uri; + break; + case '#': + r->complex_uri = 1; + state = sw_uri; + break; + case '+': + r->plus_in_uri = 1; + break; + default: + state = sw_check_uri; + break; + } + break; + + /* check "/", "%" and "\" (Win32) in URI */ + case sw_check_uri: + + if (usual[ch >> 5] & (1 << (ch & 0x1f))) { + break; + } + + switch (ch) { + case '/': +#if (NGX_WIN32) + if (r->uri_ext == p) { + r->complex_uri = 1; + state = sw_uri; + break; + } +#endif + r->uri_ext = NULL; + state = sw_after_slash_in_uri; + break; + case '.': + r->uri_ext = p + 1; + break; + case ' ': + r->space_in_uri = 1; + break; +#if (NGX_WIN32) + case '\\': + r->complex_uri = 1; + state = sw_after_slash_in_uri; + break; +#endif + case '%': + r->quoted_uri = 1; + state = sw_uri; + break; + case '?': + r->args_start = p + 1; + state = sw_uri; + break; + case '#': + r->complex_uri = 1; + state = sw_uri; + break; + case '+': + r->plus_in_uri = 1; + break; + } + break; + + /* URI */ + case sw_uri: + + if (usual[ch >> 5] & (1 << (ch & 0x1f))) { + break; + } + + switch (ch) { + case ' ': + r->space_in_uri = 1; + break; + case '#': + r->complex_uri = 1; + break; + } + break; + } + } + + return NGX_OK; +} + + +ngx_int_t ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes) { u_char c, ch, decoded, *p, *u; diff -r a10cc9106dc7 -r 7271c16e52cc src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Wed Mar 20 19:48:36 2013 +0400 +++ b/src/http/ngx_http_request.c Wed Mar 20 19:49:40 2013 +0400 @@ -11,7 +11,6 @@ static void ngx_http_wait_request_handler(ngx_event_t *ev); -static ngx_http_request_t *ngx_http_create_request(ngx_connection_t *c); static void ngx_http_process_request_line(ngx_event_t *rev); static void ngx_http_process_request_headers(ngx_event_t *rev); static ssize_t ngx_http_read_request_header(ngx_http_request_t *r); @@ -31,9 +30,6 @@ static ngx_int_t ngx_http_process_connec static ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); -static ngx_int_t ngx_http_process_request_uri(ngx_http_request_t *r); -static ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r); -static void ngx_http_process_request(ngx_http_request_t *r); static ngx_int_t ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc); static ngx_int_t ngx_http_set_virtual_server(ngx_http_request_t *r, @@ -56,9 +52,7 @@ static void ngx_http_set_lingering_close static void ngx_http_lingering_close_handler(ngx_event_t *ev); static ngx_int_t ngx_http_post_action(ngx_http_request_t *r); static void ngx_http_close_request(ngx_http_request_t *r, ngx_int_t error); -static void ngx_http_free_request(ngx_http_request_t *r, ngx_int_t error); static void ngx_http_log_request(ngx_http_request_t *r); -static void ngx_http_close_connection(ngx_connection_t *c); static u_char *ngx_http_log_error(ngx_log_t *log, u_char *buf, size_t len); static u_char *ngx_http_log_error_handler(ngx_http_request_t *r, @@ -318,6 +312,12 @@ ngx_http_init_connection(ngx_connection_ rev->handler = ngx_http_wait_request_handler; c->write->handler = ngx_http_empty_handler; +#if (NGX_HTTP_SPDY) + if (hc->addr_conf->spdy) { + rev->handler = ngx_http_spdy_init; + } +#endif + #if (NGX_HTTP_SSL) { ngx_http_ssl_srv_conf_t *sscf; @@ -487,7 +487,7 @@ ngx_http_wait_request_handler(ngx_event_ } -static ngx_http_request_t * +ngx_http_request_t * ngx_http_create_request(ngx_connection_t *c) { ngx_pool_t *pool; @@ -727,6 +727,21 @@ ngx_http_ssl_handshake_handler(ngx_conne c->ssl->no_wait_shutdown = 1; +#if (NGX_HTTP_SPDY && defined TLSEXT_TYPE_next_proto_neg) + { + unsigned int len; + const unsigned char *data; + static const ngx_str_t spdy = ngx_string(NGX_SPDY_NPN_NEGOTIATED); + + SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &len); + + if (len == spdy.len && ngx_strncmp(data, spdy.data, spdy.len) == 0) { + ngx_http_spdy_init(c->read); + return; + } + } +#endif + c->log->action = "waiting for request"; c->read->handler = ngx_http_wait_request_handler; @@ -985,7 +1000,7 @@ ngx_http_process_request_line(ngx_event_ } -static ngx_int_t +ngx_int_t ngx_http_process_request_uri(ngx_http_request_t *r) { ngx_http_core_srv_conf_t *cscf; @@ -1687,7 +1702,7 @@ ngx_http_process_multi_header_lines(ngx_ } -static ngx_int_t +ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r) { if (r->headers_in.server.len == 0 @@ -1757,7 +1772,7 @@ ngx_http_process_request_header(ngx_http } -static void +void ngx_http_process_request(ngx_http_request_t *r) { ngx_connection_t *c; @@ -2434,6 +2449,13 @@ ngx_http_finalize_connection(ngx_http_re { ngx_http_core_loc_conf_t *clcf; +#if (NGX_HTTP_SPDY) + if (r->spdy_stream) { + ngx_http_close_request(r, 0); + return; + } +#endif + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (r->main->count != 1) { @@ -2488,6 +2510,12 @@ ngx_http_set_write_handler(ngx_http_requ ngx_http_test_reading; r->write_event_handler = ngx_http_writer; +#if (NGX_HTTP_SPDY) + if (r->spdy_stream) { + return NGX_OK; + } +#endif + wev = r->connection->write; if (wev->ready && wev->delayed) { @@ -2635,6 +2663,19 @@ ngx_http_test_reading(ngx_http_request_t ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http test reading"); +#if (NGX_HTTP_SPDY) + + if (r->spdy_stream) { + if (c->error) { + err = 0; + goto closed; + } + + return; + } + +#endif + #if (NGX_HAVE_KQUEUE) if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { @@ -3270,12 +3311,19 @@ ngx_http_close_request(ngx_http_request_ return; } +#if (NGX_HTTP_SPDY) + if (r->spdy_stream) { + ngx_http_spdy_close_stream(r->spdy_stream, rc); + return; + } +#endif + ngx_http_free_request(r, rc); ngx_http_close_connection(c); } -static void +void ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc) { ngx_log_t *log; @@ -3376,7 +3424,7 @@ ngx_http_log_request(ngx_http_request_t } -static void +void ngx_http_close_connection(ngx_connection_t *c) { ngx_pool_t *pool; diff -r a10cc9106dc7 -r 7271c16e52cc src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h Wed Mar 20 19:48:36 2013 +0400 +++ b/src/http/ngx_http_request.h Wed Mar 20 19:49:40 2013 +0400 @@ -429,6 +429,9 @@ struct ngx_http_request_s { ngx_uint_t err_status; ngx_http_connection_t *http_connection; +#if (NGX_HTTP_SPDY) + ngx_http_spdy_stream_t *spdy_stream; +#endif ngx_http_log_handler_pt log_handler; diff -r a10cc9106dc7 -r 7271c16e52cc src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c Wed Mar 20 19:48:36 2013 +0400 +++ b/src/http/ngx_http_request_body.c Wed Mar 20 19:49:40 2013 +0400 @@ -42,6 +42,13 @@ ngx_http_read_client_request_body(ngx_ht r->main->count++; +#if (NGX_HTTP_SPDY) + if (r->spdy_stream) { + rc = ngx_http_spdy_read_request_body(r, post_handler); + goto done; + } +#endif + if (r->request_body || r->discard_body) { post_handler(r); return NGX_OK; @@ -462,6 +469,13 @@ ngx_http_discard_request_body(ngx_http_r ngx_int_t rc; ngx_event_t *rev; +#if (NGX_HTTP_SPDY) + if (r->spdy_stream && r == r->main) { + r->spdy_stream->skip_data = NGX_SPDY_DATA_DISCARD; + return NGX_OK; + } +#endif + if (r != r->main || r->discard_body || r->request_body) { return NGX_OK; } diff -r a10cc9106dc7 -r 7271c16e52cc src/http/ngx_http_spdy.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/http/ngx_http_spdy.c Wed Mar 20 19:49:40 2013 +0400 @@ -0,0 +1,2881 @@ + +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Valentin V. Bartenev + */ + + +#include +#include +#include +#include + +#include + + +#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) + +#define ngx_str5cmp(m, c0, c1, c2, c3, c4) \ + *(uint32_t *) m == (c3 << 24 | c2 << 16 | c1 << 8 | c0) \ + && m[4] == c4 + +#else + +#define ngx_str5cmp(m, c0, c1, c2, c3, c4) \ + m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4 + +#endif + + +#if (NGX_HAVE_NONALIGNED) + +#define ngx_spdy_frame_parse_uint16(p) ntohs(*(uint16_t *) (p)) +#define ngx_spdy_frame_parse_uint32(p) ntohl(*(uint32_t *) (p)) + +#else + +#define ngx_spdy_frame_parse_uint16(p) ((p)[0] << 8 | (p)[1]) +#define ngx_spdy_frame_parse_uint32(p) \ + ((p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3]) + +#endif + +#define ngx_spdy_frame_parse_sid(p) \ + (ngx_spdy_frame_parse_uint32(p) & 0x7fffffff) + + +#define ngx_spdy_ctl_frame_check(h) \ + (((h) & 0xffffff00) == ngx_spdy_ctl_frame_head(0)) +#define ngx_spdy_data_frame_check(h) \ + (!((h) & (uint32_t) NGX_SPDY_CTL_BIT << 31)) + +#define ngx_spdy_ctl_frame_type(h) ((h) & 0x000000ff) +#define ngx_spdy_frame_flags(p) ((p) >> 24) +#define ngx_spdy_frame_length(p) ((p) & 0x00ffffff) + + +#define NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE 4096 +#define NGX_SPDY_CTL_FRAME_BUFFER_SIZE 16 + +#define NGX_SPDY_PROTOCOL_ERROR 1 +#define NGX_SPDY_INVALID_STREAM 2 +#define NGX_SPDY_REFUSED_STREAM 3 +#define NGX_SPDY_UNSUPPORTED_VERSION 4 +#define NGX_SPDY_CANCEL 5 +#define NGX_SPDY_INTERNAL_ERROR 6 +#define NGX_SPDY_FLOW_CONTROL_ERROR 7 + +#define NGX_SPDY_SETTINGS_MAX_STREAMS 4 + +#define NGX_SPDY_SETTINGS_FLAG_PERSIST 0x01 + +typedef struct { + ngx_uint_t hash; + u_char len; + u_char header[7]; + ngx_int_t (*handler)(ngx_http_request_t *r); +} ngx_http_spdy_request_header_t; + + +static void ngx_http_spdy_read_handler(ngx_event_t *rev); +static void ngx_http_spdy_write_handler(ngx_event_t *wev); +static void ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc); + +static u_char *ngx_http_spdy_state_detect_settings( + ngx_http_spdy_connection_t *sc, u_char *pos, u_char *end); +static u_char *ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc, + u_char *pos, u_char *end); +static u_char *ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc, + u_char *pos, u_char *end); +static u_char *ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc, + u_char *pos, u_char *end); +static u_char *ngx_http_spdy_state_headers_error(ngx_http_spdy_connection_t *sc, + u_char *pos, u_char *end); +static u_char *ngx_http_spdy_state_headers_skip(ngx_http_spdy_connection_t *sc, + u_char *pos, u_char *end); +static u_char *ngx_http_spdy_state_data(ngx_http_spdy_connection_t *sc, + u_char *pos, u_char *end); +static u_char *ngx_http_spdy_state_rst_stream(ngx_http_spdy_connection_t *sc, + u_char *pos, u_char *end); +static u_char *ngx_http_spdy_state_ping(ngx_http_spdy_connection_t *sc, + u_char *pos, u_char *end); +static u_char *ngx_http_spdy_state_skip(ngx_http_spdy_connection_t *sc, + u_char *pos, u_char *end); +static u_char *ngx_http_spdy_state_settings(ngx_http_spdy_connection_t *sc, + u_char *pos, u_char *end); +static u_char *ngx_http_spdy_state_noop(ngx_http_spdy_connection_t *sc, + u_char *pos, u_char *end); +static u_char *ngx_http_spdy_state_complete(ngx_http_spdy_connection_t *sc, + u_char *pos, u_char *end); +static u_char *ngx_http_spdy_state_save(ngx_http_spdy_connection_t *sc, + u_char *pos, u_char *end, ngx_http_spdy_handler_pt handler); +static u_char *ngx_http_spdy_state_protocol_error( + ngx_http_spdy_connection_t *sc); +static u_char *ngx_http_spdy_state_internal_error( + ngx_http_spdy_connection_t *sc); + +static ngx_int_t ngx_http_spdy_send_rst_stream(ngx_http_spdy_connection_t *sc, + ngx_uint_t sid, ngx_uint_t status, ngx_uint_t priority); +static ngx_int_t ngx_http_spdy_send_settings(ngx_http_spdy_connection_t *sc); +static ngx_int_t ngx_http_spdy_settings_frame_handler( + ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame); +static ngx_http_spdy_out_frame_t *ngx_http_spdy_get_ctl_frame( + ngx_http_spdy_connection_t *sc, size_t size, ngx_uint_t priority); +static ngx_int_t ngx_http_spdy_ctl_frame_handler( + ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame); + +static ngx_http_spdy_stream_t *ngx_http_spdy_create_stream( + ngx_http_spdy_connection_t *sc, ngx_uint_t id, ngx_uint_t priority); +static ngx_http_spdy_stream_t *ngx_http_spdy_get_stream_by_id( + ngx_http_spdy_connection_t *sc, ngx_uint_t sid); +#define ngx_http_spdy_streams_index_size(sscf) (sscf->streams_index_mask + 1) +#define ngx_http_spdy_stream_index(sscf, sid) \ + ((sid >> 1) & sscf->streams_index_mask) + +static ngx_int_t ngx_http_spdy_parse_header(ngx_http_request_t *r); +static ngx_int_t ngx_http_spdy_alloc_large_header_buffer(ngx_http_request_t *r); + +static ngx_int_t ngx_http_spdy_handle_request_header(ngx_http_request_t *r); +static ngx_int_t ngx_http_spdy_parse_method(ngx_http_request_t *r); +static ngx_int_t ngx_http_spdy_parse_scheme(ngx_http_request_t *r); +static ngx_int_t ngx_http_spdy_parse_url(ngx_http_request_t *r); +static ngx_int_t ngx_http_spdy_parse_version(ngx_http_request_t *r); + +static ngx_int_t ngx_http_spdy_construct_request_line(ngx_http_request_t *r); +static void ngx_http_spdy_run_request(ngx_http_request_t *r); +static ngx_int_t ngx_http_spdy_init_request_body(ngx_http_request_t *r); + +static void ngx_http_spdy_handle_connection_handler(ngx_event_t *rev); +static void ngx_http_spdy_keepalive_handler(ngx_event_t *rev); +static void ngx_http_spdy_finalize_connection(ngx_http_spdy_connection_t *sc, + ngx_int_t rc); + +static void ngx_http_spdy_pool_cleanup(void *data); + +static void *ngx_http_spdy_zalloc(void *opaque, u_int items, u_int size); +static void ngx_http_spdy_zfree(void *opaque, void *address); + + +static const u_char ngx_http_spdy_dict[] = + "options" "get" "head" "post" "put" "delete" "trace" + "accept" "accept-charset" "accept-encoding" "accept-language" + "authorization" "expect" "from" "host" + "if-modified-since" "if-match" "if-none-match" "if-range" + "if-unmodifiedsince" "max-forwards" "proxy-authorization" + "range" "referer" "te" "user-agent" + "100" "101" "200" "201" "202" "203" "204" "205" "206" + "300" "301" "302" "303" "304" "305" "306" "307" + "400" "401" "402" "403" "404" "405" "406" "407" "408" "409" "410" + "411" "412" "413" "414" "415" "416" "417" + "500" "501" "502" "503" "504" "505" + "accept-ranges" "age" "etag" "location" "proxy-authenticate" "public" + "retry-after" "server" "vary" "warning" "www-authenticate" "allow" + "content-base" "content-encoding" "cache-control" "connection" "date" + "trailer" "transfer-encoding" "upgrade" "via" "warning" + "content-language" "content-length" "content-location" + "content-md5" "content-range" "content-type" "etag" "expires" + "last-modified" "set-cookie" + "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday" + "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec" + "chunked" "text/html" "image/png" "image/jpg" "image/gif" + "application/xml" "application/xhtml" "text/plain" "public" "max-age" + "charset=iso-8859-1" "utf-8" "gzip" "deflate" "HTTP/1.1" "status" + "version" "url"; + + +static ngx_http_spdy_request_header_t ngx_http_spdy_request_headers[] = { + { 0, 6, "method", ngx_http_spdy_parse_method }, + { 0, 6, "scheme", ngx_http_spdy_parse_scheme }, + { 0, 3, "url", ngx_http_spdy_parse_url }, + { 0, 7, "version", ngx_http_spdy_parse_version }, +}; + +#define NGX_SPDY_REQUEST_HEADERS \ + (sizeof(ngx_http_spdy_request_headers) \ + / sizeof(ngx_http_spdy_request_header_t)) + + +void +ngx_http_spdy_init(ngx_event_t *rev) +{ + int rc; + ngx_connection_t *c; + ngx_pool_cleanup_t *cln; + ngx_http_connection_t *hc; + ngx_http_spdy_srv_conf_t *sscf; + ngx_http_spdy_main_conf_t *smcf; + ngx_http_spdy_connection_t *sc; + + c = rev->data; + hc = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "init spdy request"); + + c->log->action = "processing SPDY"; + + smcf = ngx_http_get_module_main_conf(hc->conf_ctx, ngx_http_spdy_module); + + if (smcf->recv_buffer == NULL) { + smcf->recv_buffer = ngx_palloc(ngx_cycle->pool, smcf->recv_buffer_size); + if (smcf->recv_buffer == NULL) { + ngx_http_close_connection(c); + return; + } + } + + sc = ngx_pcalloc(c->pool, sizeof(ngx_http_spdy_connection_t)); + if (sc == NULL) { + ngx_http_close_connection(c); + return; + } + + sc->connection = c; + sc->http_connection = hc; + + sc->handler = ngx_http_spdy_state_detect_settings; + + sc->zstream_in.zalloc = ngx_http_spdy_zalloc; + sc->zstream_in.zfree = ngx_http_spdy_zfree; + sc->zstream_in.opaque = sc; + + rc = inflateInit(&sc->zstream_in); + if (rc != Z_OK) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "inflateInit() failed: %d", rc); + ngx_http_close_connection(c); + return; + } + + sc->zstream_out.zalloc = ngx_http_spdy_zalloc; + sc->zstream_out.zfree = ngx_http_spdy_zfree; + sc->zstream_out.opaque = sc; + + sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_spdy_module); + + rc = deflateInit2(&sc->zstream_out, (int) sscf->headers_comp, + Z_DEFLATED, 11, 4, Z_DEFAULT_STRATEGY); + + if (rc != Z_OK) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "deflateInit2() failed: %d", rc); + ngx_http_close_connection(c); + return; + } + + rc = deflateSetDictionary(&sc->zstream_out, ngx_http_spdy_dict, + sizeof(ngx_http_spdy_dict)); + if (rc != Z_OK) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "deflateSetDictionary() failed: %d", rc); + ngx_http_close_connection(c); + return; + } + + sc->pool = ngx_create_pool(sscf->pool_size, sc->connection->log); + if (sc->pool == NULL) { + ngx_http_close_connection(c); + return; + } + + cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_pool_cleanup_file_t)); + if (cln == NULL) { + ngx_http_close_connection(c); + return; + } + + cln->handler = ngx_http_spdy_pool_cleanup; + cln->data = sc; + + sc->streams_index = ngx_pcalloc(sc->pool, + ngx_http_spdy_streams_index_size(sscf) + * sizeof(ngx_http_spdy_stream_t *)); + if (sc->streams_index == NULL) { + ngx_http_close_connection(c); + return; + } + + c->data = sc; + + rev->handler = ngx_http_spdy_read_handler; + c->write->handler = ngx_http_spdy_write_handler; + + ngx_http_spdy_read_handler(rev); +} + + +static void +ngx_http_spdy_read_handler(ngx_event_t *rev) +{ + u_char *p, *end; + size_t available; + ssize_t n; + ngx_connection_t *c; + ngx_http_spdy_main_conf_t *smcf; + ngx_http_spdy_connection_t *sc; + + c = rev->data; + sc = c->data; + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + ngx_http_spdy_finalize_connection(sc, NGX_HTTP_REQUEST_TIME_OUT); + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy read handler"); + + sc->blocked = 1; + + smcf = ngx_http_get_module_main_conf(sc->http_connection->conf_ctx, + ngx_http_spdy_module); + + available = smcf->recv_buffer_size - 2 * NGX_SPDY_STATE_BUFFER_SIZE; + + do { + p = smcf->recv_buffer; + + ngx_memcpy(p, sc->buffer, NGX_SPDY_STATE_BUFFER_SIZE); + end = p + sc->buffer_used; + + n = c->recv(c, end, available); + + if (n == NGX_AGAIN) { + break; + } + + if (n == 0 && (sc->waiting || sc->processing)) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client closed prematurely connection"); + } + + if (n == 0 || n == NGX_ERROR) { + ngx_http_spdy_finalize_connection(sc, + NGX_HTTP_CLIENT_CLOSED_REQUEST); + return; + } + + end += n; + + sc->buffer_used = 0; + sc->waiting = 0; + + do { + p = sc->handler(sc, p, end); + + if (p == NULL) { + return; + } + + } while (p != end); + + } while (rev->ready); + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_spdy_finalize_connection(sc, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + sc->blocked = 0; + + if (sc->processing) { + if (rev->timer_set) { + ngx_del_timer(rev); + } + return; + } + + ngx_http_spdy_handle_connection(sc); +} + + +static void +ngx_http_spdy_write_handler(ngx_event_t *wev) +{ + ngx_int_t rc; + ngx_connection_t *c; + ngx_http_spdy_stream_t *stream, *s, *sn; + ngx_http_spdy_connection_t *sc; + + c = wev->data; + sc = c->data; + + if (wev->timedout) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "spdy write event timed out"); + ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST); + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy write handler"); + + sc->blocked = 2; + + rc = ngx_http_spdy_send_output_queue(sc); + + if (rc == NGX_ERROR) { + ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST); + return; + } + + stream = NULL; + + for (s = sc->last_stream; s; s = sn) { + sn = s->next; + s->next = stream; + stream = s; + } + + sc->last_stream = NULL; + + sc->blocked = 1; + + for ( /* void */ ; stream; stream = sn) { + sn = stream->next; + stream->handled = 0; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "spdy run stream %ui", stream->id); + + wev = stream->request->connection->write; + wev->handler(wev); + } + + sc->blocked = 0; + + if (rc == NGX_AGAIN) { + return; + } + + ngx_http_spdy_handle_connection(sc); +} + + +ngx_int_t +ngx_http_spdy_send_output_queue(ngx_http_spdy_connection_t *sc) +{ + ngx_chain_t *cl; + ngx_event_t *wev; + ngx_connection_t *c; + ngx_http_core_loc_conf_t *clcf; + ngx_http_spdy_out_frame_t *out, *frame, *fn; + + c = sc->connection; + + if (c->error) { + return NGX_ERROR; + } + + wev = c->write; + + if (!wev->ready) { + return NGX_OK; + } + + cl = NULL; + out = NULL; + + for (frame = sc->last_out; frame; frame = fn) { + frame->last->next = cl; + cl = frame->first; + + fn = frame->next; + frame->next = out; + out = frame; + + ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, + "spdy frame out: %p sid:%ui prio:%ui bl:%ui size:%uz", + out, out->stream ? out->stream->id : 0, out->priority, + out->blocked, out->size); + } + + cl = c->send_chain(c, cl, 0); + + if (cl == NGX_CHAIN_ERROR) { + c->error = 1; + + if (!sc->blocked) { + ngx_post_event(wev, &ngx_posted_events); + } + + return NGX_ERROR; + } + + clcf = ngx_http_get_module_loc_conf(sc->http_connection->conf_ctx, + ngx_http_core_module); + + if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { + return NGX_ERROR; /* FIXME */ + } + + if (cl) { + ngx_add_timer(wev, clcf->send_timeout); + + } else { + if (wev->timer_set) { + ngx_del_timer(wev); + } + } + + for ( /* void */ ; out; out = out->next) { + if (out->handler(sc, out) != NGX_OK) { + out->blocked = 1; + out->priority = NGX_SPDY_HIGHEST_PRIORITY; + break; + } + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "spdy frame sent: %p sid:%ui bl:%ui size:%uz", + out, out->stream ? out->stream->id : 0, + out->blocked, out->size); + } + + frame = NULL; + + for ( /* void */ ; out; out = fn) { + fn = out->next; + out->next = frame; + frame = out; + } + + sc->last_out = frame; + + return NGX_OK; +} + + +static void +ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc) +{ + ngx_connection_t *c; + ngx_http_spdy_srv_conf_t *sscf; + + if (sc->last_out || sc->processing) { + return; + } + + c = sc->connection; + + if (c->error) { + ngx_http_close_connection(c); + return; + } + + if (c->buffered) { + return; + } + + sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, + ngx_http_spdy_module); + if (sc->waiting) { + ngx_add_timer(c->read, sscf->recv_timeout); + return; + } + + if (ngx_terminate || ngx_exiting) { + ngx_http_close_connection(c); + return; + } + + ngx_destroy_pool(sc->pool); + + sc->pool = NULL; + sc->free_ctl_frames = NULL; + sc->free_fake_connections = NULL; + +#if (NGX_HTTP_SSL) + if (c->ssl) { + ngx_ssl_free_buffer(c); + } +#endif + + c->destroyed = 1; + c->idle = 1; + ngx_reusable_connection(c, 1); + + c->write->handler = ngx_http_empty_handler; + c->read->handler = ngx_http_spdy_keepalive_handler; + + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + ngx_add_timer(c->read, sscf->keepalive_timeout); +} + + +static u_char * +ngx_http_spdy_state_detect_settings(ngx_http_spdy_connection_t *sc, + u_char *pos, u_char *end) +{ + if (end - pos < NGX_SPDY_FRAME_HEADER_SIZE) { + return ngx_http_spdy_state_save(sc, pos, end, + ngx_http_spdy_state_detect_settings); + } + + /* + * Since this is the first frame in a buffer, + * then it is properly aligned + */ + + if (*(uint32_t *) pos == htonl(ngx_spdy_ctl_frame_head(NGX_SPDY_SETTINGS))) + { + sc->length = ngx_spdy_frame_length(htonl(((uint32_t *) pos)[1])); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy SETTINGS frame received, size: %uz", sc->length); + + pos += NGX_SPDY_FRAME_HEADER_SIZE; + + return ngx_http_spdy_state_settings(sc, pos, end); + } + + ngx_http_spdy_send_settings(sc); + + return ngx_http_spdy_state_head(sc, pos, end); +} + + +static u_char * +ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc, u_char *pos, + u_char *end) +{ + uint32_t head, flen; + + if (end - pos < NGX_SPDY_FRAME_HEADER_SIZE) { + return ngx_http_spdy_state_save(sc, pos, end, + ngx_http_spdy_state_head); + } + + head = ngx_spdy_frame_parse_uint32(pos); + + pos += sizeof(uint32_t); + + flen = ngx_spdy_frame_parse_uint32(pos); + + sc->flags = ngx_spdy_frame_flags(flen); + sc->length = ngx_spdy_frame_length(flen); + + pos += sizeof(uint32_t); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy process frame head:%08Xd f:%ui l:%ui", + head, sc->flags, sc->length); + + if (ngx_spdy_ctl_frame_check(head)) { + switch (ngx_spdy_ctl_frame_type(head)) { + + case NGX_SPDY_SYN_STREAM: + return ngx_http_spdy_state_syn_stream(sc, pos, end); + + case NGX_SPDY_SYN_REPLY: + return ngx_http_spdy_state_protocol_error(sc); + + case NGX_SPDY_RST_STREAM: + return ngx_http_spdy_state_rst_stream(sc, pos, end); + + case NGX_SPDY_SETTINGS: + return ngx_http_spdy_state_skip(sc, pos, end); + + case NGX_SPDY_NOOP: + return ngx_http_spdy_state_noop(sc, pos, end); + + case NGX_SPDY_PING: + return ngx_http_spdy_state_ping(sc, pos, end); + + case NGX_SPDY_GOAWAY: + return ngx_http_spdy_state_skip(sc, pos, end); /* TODO */ + + case NGX_SPDY_HEADERS: + return ngx_http_spdy_state_protocol_error(sc); + + default: /* TODO logging */ + return ngx_http_spdy_state_skip(sc, pos, end); + } + } + + if (ngx_spdy_data_frame_check(head)) { + sc->stream = ngx_http_spdy_get_stream_by_id(sc, head); + return ngx_http_spdy_state_data(sc, pos, end); + } + + + /* TODO version & type check */ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy unknown frame"); + + return ngx_http_spdy_state_protocol_error(sc); +} + + +static u_char * +ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc, u_char *pos, + u_char *end) +{ + ngx_uint_t sid, prio; + ngx_http_spdy_stream_t *stream; + ngx_http_spdy_srv_conf_t *sscf; + + if (end - pos < NGX_SPDY_SYN_STREAM_SIZE) { + return ngx_http_spdy_state_save(sc, pos, end, + ngx_http_spdy_state_syn_stream); + } + + if (sc->length <= NGX_SPDY_SYN_STREAM_SIZE) { + /* TODO logging */ + return ngx_http_spdy_state_protocol_error(sc); + } + + sc->length -= NGX_SPDY_SYN_STREAM_SIZE; + + sid = ngx_spdy_frame_parse_sid(pos); + prio = pos[8] >> 6; + + pos += NGX_SPDY_SYN_STREAM_SIZE; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy SYN_STREAM frame sid:%ui prio:%ui", sid, prio); + + sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, + ngx_http_spdy_module); + + if (sc->processing >= sscf->concurrent_streams) { + + ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0, + "spdy concurrent streams excessed %ui", sc->processing); + + if (ngx_http_spdy_send_rst_stream(sc, sid, NGX_SPDY_REFUSED_STREAM, + prio) + != NGX_OK) + { + return ngx_http_spdy_state_internal_error(sc); + } + + return ngx_http_spdy_state_headers_skip(sc, pos, end); + } + + stream = ngx_http_spdy_create_stream(sc, sid, prio); + if (stream == NULL) { + return ngx_http_spdy_state_internal_error(sc); + } + + stream->in_closed = (sc->flags & NGX_SPDY_FLAG_FIN) ? 1 : 0; + + stream->request->request_length = NGX_SPDY_FRAME_HEADER_SIZE + + NGX_SPDY_SYN_STREAM_SIZE + + sc->length; + + sc->stream = stream; + + sc->last_sid = sid; + + return ngx_http_spdy_state_headers(sc, pos, end); +} + + +static u_char * +ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc, u_char *pos, + u_char *end) +{ + int z; + size_t size; + ngx_buf_t *buf; + ngx_int_t rc; + ngx_uint_t complete; + ngx_http_request_t *r; + + size = end - pos; + + if (size == 0) { + return ngx_http_spdy_state_save(sc, pos, end, + ngx_http_spdy_state_headers); + } + + if (size >= sc->length) { + size = sc->length; + complete = 1; + + } else { + complete = 0; + } + + r = sc->stream->request; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "spdy process HEADERS %uz of %uz", size, sc->length); + + buf = r->header_in; + + sc->zstream_in.next_in = pos; + sc->zstream_in.avail_in = size; + sc->zstream_in.next_out = buf->last; + sc->zstream_in.avail_out = buf->end - buf->last - 1; + + z = inflate(&sc->zstream_in, Z_NO_FLUSH); + + if (z == Z_NEED_DICT) { + z = inflateSetDictionary(&sc->zstream_in, ngx_http_spdy_dict, + sizeof(ngx_http_spdy_dict)); + if (z != Z_OK) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "spdy inflateSetDictionary() failed: %d", z); + ngx_http_spdy_close_stream(sc->stream, 0); + return ngx_http_spdy_state_protocol_error(sc); + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "spdy inflateSetDictionary(): %d", z); + + z = sc->zstream_in.avail_in ? inflate(&sc->zstream_in, Z_NO_FLUSH) + : Z_OK; + } + + if (z != Z_OK) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "spdy inflate() failed: %d", z); + ngx_http_spdy_close_stream(sc->stream, 0); + return ngx_http_spdy_state_protocol_error(sc); + } + + ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "spdy inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", + sc->zstream_in.next_in, sc->zstream_in.next_out, + sc->zstream_in.avail_in, sc->zstream_in.avail_out, + z); + + sc->length -= sc->zstream_in.next_in - pos; + pos = sc->zstream_in.next_in; + + buf->last = sc->zstream_in.next_out; + + if (r->headers_in.headers.part.elts == NULL) { + + if (buf->last - buf->pos < NGX_SPDY_NV_NUM_SIZE) { + return ngx_http_spdy_state_save(sc, pos, end, + ngx_http_spdy_state_headers); + } + + sc->headers = ngx_spdy_frame_parse_uint16(buf->pos); + + buf->pos += NGX_SPDY_NV_NUM_SIZE; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "spdy headers count: %ui", sc->headers); + + if (ngx_list_init(&r->headers_in.headers, r->pool, sc->headers + 3, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + ngx_http_spdy_close_stream(sc->stream, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return ngx_http_spdy_state_headers_error(sc, pos, end); + } + + if (ngx_array_init(&r->headers_in.cookies, r->pool, 2, + sizeof(ngx_table_elt_t *)) + != NGX_OK) + { + ngx_http_spdy_close_stream(sc->stream, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return ngx_http_spdy_state_headers_error(sc, pos, end); + } + } + + while (sc->headers) { + + rc = ngx_http_spdy_parse_header(r); + + switch (rc) { + + case NGX_DONE: + sc->headers--; + + case NGX_OK: + break; + + case NGX_AGAIN: + + if (sc->zstream_in.avail_in) { + + rc = ngx_http_spdy_alloc_large_header_buffer(r); + + if (rc == NGX_DECLINED) { + /* TODO logging */ + ngx_http_finalize_request(r, + NGX_HTTP_REQUEST_HEADER_TOO_LARGE); + return ngx_http_spdy_state_headers_error(sc, pos, end); + } + + if (rc != NGX_OK) { + ngx_http_spdy_close_stream(sc->stream, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return ngx_http_spdy_state_headers_error(sc, pos, end); + } + + buf = r->header_in; + + sc->zstream_in.next_out = buf->last; + sc->zstream_in.avail_out = buf->end - buf->last - 1; + + z = inflate(&sc->zstream_in, Z_NO_FLUSH); + + if (z != Z_OK) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "spdy inflate() failed: %d", z); + ngx_http_spdy_close_stream(sc->stream, 0); + return ngx_http_spdy_state_protocol_error(sc); + } + + sc->length -= sc->zstream_in.next_in - pos; + pos = sc->zstream_in.next_in; + + buf->last = sc->zstream_in.next_out; + + continue; + } + + if (complete) { + /* TODO: improve error message */ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "spdy again while last chunk"); + ngx_http_spdy_close_stream(sc->stream, 0); + return ngx_http_spdy_state_protocol_error(sc); + } + + return ngx_http_spdy_state_save(sc, pos, end, + ngx_http_spdy_state_headers); + + case NGX_HTTP_PARSE_INVALID_REQUEST: + + /* TODO: improve error message */ + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid header line"); + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + + return ngx_http_spdy_state_headers_error(sc, pos, end); + + default: /* NGX_HTTP_PARSE_INVALID_HEADER */ + + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid HEADERS spdy frame"); + ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_BAD_REQUEST); + return ngx_http_spdy_state_protocol_error(sc); + } + + /* a header line has been parsed successfully */ + + rc = ngx_http_spdy_handle_request_header(r); + + if (rc != NGX_OK) { + if (rc == NGX_HTTP_PARSE_INVALID_HEADER) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid HEADERS spdy frame"); + ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_BAD_REQUEST); + return ngx_http_spdy_state_protocol_error(sc); + } + + if (rc == NGX_HTTP_PARSE_INVALID_REQUEST) { + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + } + + return ngx_http_spdy_state_headers_error(sc, pos, end); + } + } + + if (buf->pos != buf->last) { + /* TODO: improve error message */ + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "end %ui %p %p", complete, buf->pos, buf->last); + ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_BAD_REQUEST); + return ngx_http_spdy_state_protocol_error(sc); + } + + if (!complete) { + return ngx_http_spdy_state_save(sc, pos, end, + ngx_http_spdy_state_headers); + } + + ngx_http_spdy_run_request(r); + + return ngx_http_spdy_state_complete(sc, pos, end); +} + + +static u_char * +ngx_http_spdy_state_headers_error(ngx_http_spdy_connection_t *sc, u_char *pos, + u_char *end) +{ + if (sc->connection->error) { + return ngx_http_spdy_state_internal_error(sc); + } + + return ngx_http_spdy_state_headers_skip(sc, pos, end); +} + + +static u_char * +ngx_http_spdy_state_headers_skip(ngx_http_spdy_connection_t *sc, u_char *pos, + u_char *end) +{ + int n; + size_t size; + u_char buffer[NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE]; + + if (sc->length == 0) { + return ngx_http_spdy_state_complete(sc, pos, end); + } + + size = end - pos; + + if (size == 0) { + return ngx_http_spdy_state_save(sc, pos, end, + ngx_http_spdy_state_headers_skip); + } + + sc->zstream_in.next_in = pos; + sc->zstream_in.avail_in = (size < sc->length) ? size : sc->length; + + while (sc->zstream_in.avail_in) { + sc->zstream_in.next_out = buffer; + sc->zstream_in.avail_out = NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE; + + n = inflate(&sc->zstream_in, Z_NO_FLUSH); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy inflate(): %d", n); + + if (n != Z_OK) { + /* TODO: logging */ + return ngx_http_spdy_state_protocol_error(sc); + } + } + + pos = sc->zstream_in.next_in; + + if (size < sc->length) { + sc->length -= size; + return ngx_http_spdy_state_save(sc, pos, end, + ngx_http_spdy_state_headers_skip); + } + + return ngx_http_spdy_state_complete(sc, pos, end); +} + + +static u_char * +ngx_http_spdy_state_data(ngx_http_spdy_connection_t *sc, u_char *pos, + u_char *end) +{ + size_t size; + ssize_t n; + ngx_buf_t *buf; + ngx_int_t rc; + ngx_uint_t complete; + ngx_temp_file_t *tf; + ngx_http_request_t *r; + ngx_http_spdy_stream_t *stream; + ngx_http_request_body_t *rb; + ngx_http_core_loc_conf_t *clcf; + + stream = sc->stream; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy DATA frame"); + + if (stream == NULL) { + return ngx_http_spdy_state_skip(sc, pos, end); + } + + if (stream->in_closed) { + /* TODO log */ + return ngx_http_spdy_state_protocol_error(sc); + } + + if (stream->skip_data) { + + if (sc->flags & NGX_SPDY_FLAG_FIN) { + stream->in_closed = 1; + } + + /* TODO log and accounting */ + return ngx_http_spdy_state_skip(sc, pos, end); + } + + size = end - pos; + + if (size >= sc->length) { + size = sc->length; + complete = 1; + + } else { + sc->length -= size; + complete = 0; + } + + r = stream->request; + + if (r->request_body == NULL + && ngx_http_spdy_init_request_body(r) != NGX_OK) + { + stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR; + return ngx_http_spdy_state_skip(sc, pos, end); + } + + rb = r->request_body; + tf = rb->temp_file; + buf = rb->buf; + + if (size) { + rb->rest += size; + + if (r->headers_in.content_length_n != -1 + && r->headers_in.content_length_n < rb->rest) + { + /* TODO logging */ + stream->skip_data = NGX_SPDY_DATA_ERROR; + goto error; + + } else { + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->client_max_body_size + && clcf->client_max_body_size < rb->rest) + { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "client intended to send too large chunked " + "body: %O bytes", + rb->rest); + + stream->skip_data = NGX_SPDY_DATA_ERROR; + goto error; + } + } + + if (tf) { + buf->start = pos; + buf->pos = pos; + + pos += size; + + buf->end = pos; + buf->last = pos; + + n = ngx_write_chain_to_temp_file(tf, rb->bufs); + + /* TODO: n == 0 or not complete and level event */ + + if (n == NGX_ERROR) { + stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR; + goto error; + } + + tf->offset += n; + + } else { + buf->last = ngx_cpymem(buf->last, pos, size); + pos += size; + } + + r->request_length += size; + } + + if (!complete) { + return ngx_http_spdy_state_save(sc, pos, end, + ngx_http_spdy_state_data); + } + + if (sc->flags & NGX_SPDY_FLAG_FIN) { + + stream->in_closed = 1; + + if (tf) { + ngx_memzero(buf, sizeof(ngx_buf_t)); + + buf->in_file = 1; + buf->file_last = tf->file.offset; + buf->file = &tf->file; + + rb->buf = NULL; + } + + if (r->headers_in.content_length_n < 0) { + r->headers_in.content_length_n = rb->rest; + } + + if (rb->post_handler) { + rb->post_handler(r); + } + } + + return ngx_http_spdy_state_complete(sc, pos, end); + +error: + + if (rb->post_handler) { + + if (stream->skip_data == NGX_SPDY_DATA_ERROR) { + rc = (r->headers_in.content_length_n == -1) + ? NGX_HTTP_REQUEST_ENTITY_TOO_LARGE + : NGX_HTTP_BAD_REQUEST; + + } else { + rc = NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_http_finalize_request(r, rc); + } + + return ngx_http_spdy_state_skip(sc, pos, end); +} + + +static u_char * +ngx_http_spdy_state_rst_stream(ngx_http_spdy_connection_t *sc, u_char *pos, + u_char *end) +{ + ngx_uint_t sid, status; + ngx_event_t *ev; + ngx_connection_t *fc; + ngx_http_request_t *r; + ngx_http_spdy_stream_t *stream; + + if (end - pos < NGX_SPDY_RST_STREAM_SIZE) { + return ngx_http_spdy_state_save(sc, pos, end, + ngx_http_spdy_state_rst_stream); + } + + if (sc->length != NGX_SPDY_RST_STREAM_SIZE) { + /* TODO logging */ + return ngx_http_spdy_state_protocol_error(sc); + } + + sid = ngx_spdy_frame_parse_sid(pos); + + pos += NGX_SPDY_SID_SIZE; + + status = ngx_spdy_frame_parse_uint32(pos); + + pos += sizeof(uint32_t); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy RST_STREAM sid:%ui st:%ui", sid, status); + + + switch (status) { + + case NGX_SPDY_PROTOCOL_ERROR: + /* TODO logging */ + return ngx_http_spdy_state_protocol_error(sc); + + case NGX_SPDY_INVALID_STREAM: + /* TODO */ + break; + + case NGX_SPDY_REFUSED_STREAM: + /* TODO */ + break; + + case NGX_SPDY_UNSUPPORTED_VERSION: + /* TODO logging */ + return ngx_http_spdy_state_protocol_error(sc); + + case NGX_SPDY_CANCEL: + case NGX_SPDY_INTERNAL_ERROR: + stream = ngx_http_spdy_get_stream_by_id(sc, sid); + if (stream == NULL) { + /* TODO false cancel */ + break; + } + + stream->in_closed = 1; + stream->out_closed = 1; + + r = stream->request; + + fc = r->connection; + fc->error = 1; + + ev = fc->read; + ev->handler(ev); + + break; + + case NGX_SPDY_FLOW_CONTROL_ERROR: + /* TODO logging */ + return ngx_http_spdy_state_protocol_error(sc); + + default: + /* TODO */ + return ngx_http_spdy_state_protocol_error(sc); + } + + return ngx_http_spdy_state_complete(sc, pos, end); +} + + +static u_char * +ngx_http_spdy_state_ping(ngx_http_spdy_connection_t *sc, u_char *pos, + u_char *end) +{ + u_char *p; + ngx_buf_t *buf; + ngx_http_spdy_out_frame_t *frame; + + if (end - pos < NGX_SPDY_PING_SIZE) { + return ngx_http_spdy_state_save(sc, pos, end, + ngx_http_spdy_state_ping); + } + + if (sc->length != NGX_SPDY_PING_SIZE) { + /* TODO logging */ + return ngx_http_spdy_state_protocol_error(sc); + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy PING frame"); + + frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_PING_SIZE, + NGX_SPDY_HIGHEST_PRIORITY); + if (frame == NULL) { + return ngx_http_spdy_state_internal_error(sc); + } + + buf = frame->first->buf; + + p = buf->pos; + + p = ngx_spdy_frame_write_head(p, NGX_SPDY_PING); + p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_PING_SIZE); + + p = ngx_cpymem(p, pos, NGX_SPDY_PING_SIZE); + + buf->last = p; + + ngx_http_spdy_queue_frame(sc, frame); + + pos += NGX_SPDY_PING_SIZE; + + return ngx_http_spdy_state_complete(sc, pos, end); +} + + +static u_char * +ngx_http_spdy_state_skip(ngx_http_spdy_connection_t *sc, u_char *pos, + u_char *end) +{ + size_t size; + + size = end - pos; + + if (size < sc->length) { + sc->length -= size; + return ngx_http_spdy_state_save(sc, end, end, + ngx_http_spdy_state_skip); + } + + return ngx_http_spdy_state_complete(sc, pos + sc->length, end); +} + + +static u_char * +ngx_http_spdy_state_settings(ngx_http_spdy_connection_t *sc, u_char *pos, + u_char *end) +{ + ngx_uint_t v; + ngx_http_spdy_srv_conf_t *sscf; + + if (sc->headers == 0) { + + if (end - pos < NGX_SPDY_SETTINGS_NUM_SIZE) { + return ngx_http_spdy_state_save(sc, pos, end, + ngx_http_spdy_state_settings); + } + + sc->headers = ngx_spdy_frame_parse_uint32(pos); + + pos += NGX_SPDY_SETTINGS_NUM_SIZE; + sc->length -= NGX_SPDY_SETTINGS_NUM_SIZE; + + if (sc->length < sc->headers * NGX_SPDY_SETTINGS_PAIR_SIZE) { + /* TODO logging */ + return ngx_http_spdy_state_protocol_error(sc); + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy SETTINGS frame consists of %ui entries", + sc->headers); + } + + while (sc->headers) { + if (end - pos < NGX_SPDY_SETTINGS_PAIR_SIZE) { + return ngx_http_spdy_state_save(sc, pos, end, + ngx_http_spdy_state_settings); + } + + sc->headers--; + + if (pos[0] != NGX_SPDY_SETTINGS_MAX_STREAMS) { + pos += NGX_SPDY_SETTINGS_PAIR_SIZE; + sc->length -= NGX_SPDY_SETTINGS_PAIR_SIZE; + continue; + } + + v = ngx_spdy_frame_parse_uint32(pos + NGX_SPDY_SETTINGS_IDF_SIZE); + + sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, + ngx_http_spdy_module); + + if (v != sscf->concurrent_streams) { + ngx_http_spdy_send_settings(sc); + } + + return ngx_http_spdy_state_skip(sc, pos, end); + } + + ngx_http_spdy_send_settings(sc); + + return ngx_http_spdy_state_complete(sc, pos, end); +} + + +static u_char * +ngx_http_spdy_state_noop(ngx_http_spdy_connection_t *sc, u_char *pos, + u_char *end) +{ + if (sc->length) { + /* TODO logging */ + return ngx_http_spdy_state_protocol_error(sc); + } + + return ngx_http_spdy_state_complete(sc, pos, end); +} + + +static u_char * +ngx_http_spdy_state_complete(ngx_http_spdy_connection_t *sc, u_char *pos, + u_char *end) +{ + sc->handler = ngx_http_spdy_state_head; + return pos; +} + + +static u_char * +ngx_http_spdy_state_save(ngx_http_spdy_connection_t *sc, + u_char *pos, u_char *end, ngx_http_spdy_handler_pt handler) +{ +#if (NGX_DEBUG) + if (end - pos > NGX_SPDY_STATE_BUFFER_SIZE) { + ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0, + "spdy state buffer overflow: " + "%i bytes required", end - pos); + return ngx_http_spdy_state_internal_error(sc); + } +#endif + + ngx_memcpy(sc->buffer, pos, NGX_SPDY_STATE_BUFFER_SIZE); + + sc->buffer_used = end - pos; + sc->handler = handler; + sc->waiting = 1; + + return end; +} + + +static u_char * +ngx_http_spdy_state_protocol_error(ngx_http_spdy_connection_t *sc) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy state protocol error"); + + /* TODO */ + ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST); + return NULL; +} + + +static u_char * +ngx_http_spdy_state_internal_error(ngx_http_spdy_connection_t *sc) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy state internal error"); + + /* TODO */ + ngx_http_spdy_finalize_connection(sc, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NULL; +} + + +static ngx_int_t +ngx_http_spdy_send_rst_stream(ngx_http_spdy_connection_t *sc, ngx_uint_t sid, + ngx_uint_t status, ngx_uint_t priority) +{ + u_char *p; + ngx_buf_t *buf; + ngx_http_spdy_out_frame_t *frame; + + if (sc->connection->error) { + return NGX_OK; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy write RST_STREAM sid:%ui st:%ui", sid, status); + + frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_RST_STREAM_SIZE, + priority); + if (frame == NULL) { + return NGX_ERROR; + } + + buf = frame->first->buf; + + p = buf->pos; + + p = ngx_spdy_frame_write_head(p, NGX_SPDY_RST_STREAM); + p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_RST_STREAM_SIZE); + + p = ngx_spdy_frame_write_sid(p, sid); + p = ngx_spdy_frame_aligned_write_uint32(p, status); + + buf->last = p; + + ngx_http_spdy_queue_frame(sc, frame); + + return NGX_OK; +} + + +#if 0 +static ngx_int_t +ngx_http_spdy_send_goaway(ngx_http_spdy_connection_t *sc) +{ + u_char *p; + ngx_buf_t *buf; + ngx_http_spdy_out_frame_t *frame; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy create GOAWAY sid:%ui", sc->last_sid); + + frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_GOAWAY_SIZE, + NGX_SPDY_HIGHEST_PRIORITY); + if (frame == NULL) { + return NGX_ERROR; + } + + buf = frame->first->buf; + + p = buf->pos; + + p = ngx_spdy_frame_write_head(p, NGX_SPDY_GOAWAY); + p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_GOAWAY_SIZE); + + p = ngx_spdy_frame_write_sid(p, sc->last_sid); + + buf->last = p; + + ngx_http_spdy_queue_frame(sc, frame); + + return NGX_OK; +} +#endif + + +static ngx_int_t +ngx_http_spdy_send_settings(ngx_http_spdy_connection_t *sc) +{ + u_char *p; + ngx_buf_t *buf; + ngx_pool_t *pool; + ngx_chain_t *cl; + ngx_http_spdy_srv_conf_t *sscf; + ngx_http_spdy_out_frame_t *frame; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy create SETTINGS frame"); + + pool = sc->connection->pool; + + frame = ngx_palloc(pool, sizeof(ngx_http_spdy_out_frame_t)); + if (frame == NULL) { + return NGX_ERROR; + } + + cl = ngx_alloc_chain_link(pool); + if (cl == NULL) { + return NGX_ERROR; + } + + buf = ngx_create_temp_buf(pool, NGX_SPDY_FRAME_HEADER_SIZE + + NGX_SPDY_SETTINGS_NUM_SIZE + + NGX_SPDY_SETTINGS_PAIR_SIZE); + if (buf == NULL) { + return NGX_ERROR; + } + + buf->last_buf = 1; + + cl->buf = buf; + cl->next = NULL; + + frame->first = cl; + frame->last = cl; + frame->handler = ngx_http_spdy_settings_frame_handler; +#if (NGX_DEBUG) + frame->stream = NULL; + frame->size = NGX_SPDY_FRAME_HEADER_SIZE + + NGX_SPDY_SETTINGS_NUM_SIZE + + NGX_SPDY_SETTINGS_PAIR_SIZE; +#endif + frame->priority = NGX_SPDY_HIGHEST_PRIORITY; + frame->blocked = 0; + + p = buf->pos; + + p = ngx_spdy_frame_write_head(p, NGX_SPDY_SETTINGS); + p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_CLEAR_SETTINGS, + NGX_SPDY_SETTINGS_NUM_SIZE + + NGX_SPDY_SETTINGS_PAIR_SIZE); + + p = ngx_spdy_frame_aligned_write_uint32(p, 1); + p = ngx_spdy_frame_aligned_write_uint32(p, + NGX_SPDY_SETTINGS_MAX_STREAMS << 24 + | NGX_SPDY_SETTINGS_FLAG_PERSIST); + + sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, + ngx_http_spdy_module); + + p = ngx_spdy_frame_aligned_write_uint32(p, sscf->concurrent_streams); + + buf->last = p; + + ngx_http_spdy_queue_frame(sc, frame); + + return NGX_OK; +} + + +ngx_int_t +ngx_http_spdy_settings_frame_handler(ngx_http_spdy_connection_t *sc, + ngx_http_spdy_out_frame_t *frame) +{ + ngx_buf_t *buf; + + buf = frame->first->buf; + + if (buf->pos != buf->last) { + return NGX_AGAIN; + } + + ngx_free_chain(sc->pool, frame->first); + + return NGX_OK; +} + + +static ngx_http_spdy_out_frame_t * +ngx_http_spdy_get_ctl_frame(ngx_http_spdy_connection_t *sc, size_t size, + ngx_uint_t priority) +{ + ngx_chain_t *cl; + ngx_http_spdy_out_frame_t *frame; + + frame = sc->free_ctl_frames; + + if (frame) { + sc->free_ctl_frames = frame->free; + + cl = frame->first; + cl->buf->pos = cl->buf->start; + + } else { + frame = ngx_palloc(sc->pool, sizeof(ngx_http_spdy_out_frame_t)); + if (frame == NULL) { + return NULL; + } + + cl = ngx_alloc_chain_link(sc->pool); + if (cl == NULL) { + return NULL; + } + + cl->buf = ngx_create_temp_buf(sc->pool, + NGX_SPDY_CTL_FRAME_BUFFER_SIZE); + if (cl->buf == NULL) { + return NULL; + } + + cl->buf->last_buf = 1; + + frame->first = cl; + frame->last = cl; + frame->handler = ngx_http_spdy_ctl_frame_handler; + } + + frame->free = NULL; + +#if (NGX_DEBUG) + if (size > NGX_SPDY_CTL_FRAME_BUFFER_SIZE - NGX_SPDY_FRAME_HEADER_SIZE) { + ngx_log_error(NGX_LOG_ALERT, sc->pool->log, 0, + "requested control frame is too big: %z", size); + return NULL; + } + + frame->stream = NULL; + frame->size = size; +#endif + + frame->priority = priority; + frame->blocked = 0; + + return frame; +} + + +static ngx_int_t +ngx_http_spdy_ctl_frame_handler(ngx_http_spdy_connection_t *sc, + ngx_http_spdy_out_frame_t *frame) +{ + ngx_buf_t *buf; + + buf = frame->first->buf; + + if (buf->pos != buf->last) { + return NGX_AGAIN; + } + + frame->free = sc->free_ctl_frames; + sc->free_ctl_frames = frame; + + return NGX_OK; +} + + +static ngx_http_spdy_stream_t * +ngx_http_spdy_create_stream(ngx_http_spdy_connection_t *sc, ngx_uint_t id, + ngx_uint_t priority) +{ + ngx_log_t *log; + ngx_uint_t index; + ngx_event_t *rev, *wev; + ngx_connection_t *fc; + ngx_http_log_ctx_t *ctx; + ngx_http_request_t *r; + ngx_http_spdy_stream_t *stream; + ngx_http_core_srv_conf_t *cscf; + ngx_http_spdy_srv_conf_t *sscf; + + fc = sc->free_fake_connections; + + if (fc) { + sc->free_fake_connections = fc->data; + + rev = fc->read; + wev = fc->write; + log = fc->log; + ctx = log->data; + + } else { + fc = ngx_palloc(sc->pool, sizeof(ngx_connection_t)); + if (fc == NULL) { + return NULL; + } + + rev = ngx_palloc(sc->pool, sizeof(ngx_event_t)); + if (rev == NULL) { + return NULL; + } + + wev = ngx_palloc(sc->pool, sizeof(ngx_event_t)); + if (wev == NULL) { + return NULL; + } + + log = ngx_palloc(sc->pool, sizeof(ngx_log_t)); + if (log == NULL) { + return NULL; + } + + ctx = ngx_palloc(sc->pool, sizeof(ngx_http_log_ctx_t)); + if (ctx == NULL) { + return NULL; + } + + ctx->connection = fc; + ctx->request = NULL; + } + + ngx_memcpy(log, sc->connection->log, sizeof(ngx_log_t)); + + log->data = ctx; + + ngx_memzero(rev, sizeof(ngx_event_t)); + + rev->data = fc; + rev->ready = 1; + rev->handler = ngx_http_empty_handler; + rev->log = log; + + ngx_memcpy(wev, rev, sizeof(ngx_event_t)); + + wev->write = 1; + + ngx_memcpy(fc, sc->connection, sizeof(ngx_connection_t)); + + fc->data = sc->http_connection; + fc->read = rev; + fc->write = wev; + fc->sent = 0; + fc->log = log; + fc->buffered = 0; + fc->sndlowat = 1; + + r = ngx_http_create_request(fc); + if (r == NULL) { + return NULL; + } + + r->valid_location = 1; + + fc->data = r; + sc->connection->requests++; + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + r->header_in = ngx_create_temp_buf(r->pool, + cscf->client_header_buffer_size); + if (r->header_in == NULL) { + ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NULL; + } + + r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; + + stream = ngx_pcalloc(r->pool, sizeof(ngx_http_spdy_stream_t)); + if (stream == NULL) { + ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NULL; + } + + r->spdy_stream = stream; + + stream->id = id; + stream->request = r; + stream->connection = sc; + stream->priority = priority; + + sscf = ngx_http_get_module_srv_conf(r, ngx_http_spdy_module); + + index = ngx_http_spdy_stream_index(sscf, id); + + stream->index = sc->streams_index[index]; + sc->streams_index[index] = stream; + + sc->processing++; + + return stream; +} + + +static ngx_http_spdy_stream_t * +ngx_http_spdy_get_stream_by_id(ngx_http_spdy_connection_t *sc, + ngx_uint_t sid) +{ + ngx_http_spdy_stream_t *stream; + ngx_http_spdy_srv_conf_t *sscf; + + sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, + ngx_http_spdy_module); + + stream = sc->streams_index[ngx_http_spdy_stream_index(sscf, sid)]; + + while (stream) { + if (stream->id == sid) { + return stream; + } + + stream = stream->index; + } + + return NULL; +} + + +static ngx_int_t +ngx_http_spdy_parse_header(ngx_http_request_t *r) +{ + u_char *p, *end, ch; + ngx_uint_t len, hash; + ngx_http_core_srv_conf_t *cscf; + + enum { + sw_name_len = 0, + sw_name, + sw_value_len, + sw_value + } state; + + state = r->state; + + p = r->header_in->pos; + end = r->header_in->last; + + switch (state) { + + case sw_name_len: + + if (end - p < NGX_SPDY_NV_NLEN_SIZE) { + return NGX_AGAIN; + } + + len = ngx_spdy_frame_parse_uint16(p); + + if (!len) { + return NGX_HTTP_PARSE_INVALID_HEADER; + } + + p += NGX_SPDY_NV_NLEN_SIZE; + + r->header_name_end = p + len; + r->lowcase_index = len; + r->invalid_header = 0; + + state = sw_name; + + /* fall through */ + + case sw_name: + + if (r->header_name_end > end) { + break; + } + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + r->header_name_start = p; + + hash = 0; + + for ( /* void */ ; p != r->header_name_end; p++) { + + ch = *p; + + hash = ngx_hash(hash, ch); + + if ((ch >= 'a' && ch <= 'z') + || (ch == '-') + || (ch >= '0' && ch <= '9') + || (ch == '_' && cscf->underscores_in_headers)) + { + continue; + } + + switch (ch) { + case '\0': + case LF: + case CR: + case ':': + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + + if (ch >= 'A' && ch <= 'Z') { + return NGX_HTTP_PARSE_INVALID_HEADER; + } + + r->invalid_header = 1; + } + + r->header_hash = hash; + + state = sw_value_len; + + /* fall through */ + + case sw_value_len: + + if (end - p < NGX_SPDY_NV_VLEN_SIZE) { + break; + } + + len = ngx_spdy_frame_parse_uint16(p); + + if (!len) { + return NGX_ERROR; + } + + p += NGX_SPDY_NV_VLEN_SIZE; + + r->header_end = p + len; + + state = sw_value; + + /* fall through */ + + case sw_value: + + if (r->header_end > end) { + break; + } + + r->header_start = p; + + for ( /* void */ ; p != r->header_end; p++) { + + ch = *p; + + if (ch == '\0') { + + if (p == r->header_start) { + return NGX_ERROR; + } + + r->header_size = p - r->header_start; + r->header_in->pos = p + 1; + + return NGX_OK; + } + + if (ch == CR || ch == LF) { + return NGX_HTTP_PARSE_INVALID_HEADER; + } + } + + r->header_size = p - r->header_start; + r->header_in->pos = p; + + r->state = 0; + + return NGX_DONE; + } + + r->header_in->pos = p; + r->state = state; + + return NGX_AGAIN; +} + + +static ngx_int_t +ngx_http_spdy_alloc_large_header_buffer(ngx_http_request_t *r) +{ + u_char *old, *new; + size_t rest; + ngx_buf_t *buf; + ngx_http_spdy_stream_t *stream; + ngx_http_core_srv_conf_t *cscf; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "spdy alloc large header buffer"); + + stream = r->spdy_stream; + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + if (stream->header_buffers + == (ngx_uint_t) cscf->large_client_header_buffers.num) + { + return NGX_DECLINED; + } + + rest = r->header_in->last - r->header_in->pos; + + if (rest >= cscf->large_client_header_buffers.size) { + return NGX_DECLINED; + } + + buf = ngx_create_temp_buf(r->pool, cscf->large_client_header_buffers.size); + if (buf == NULL) { + return NGX_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "spdy large header alloc: %p %uz", + buf->pos, buf->end - buf->last); + + old = r->header_in->pos; + new = buf->pos; + + if (rest) { + buf->last = ngx_cpymem(new, old, rest); + } + + if (r->header_name_end > old) { + r->header_name_end = new + (r->header_name_end - old); + + } else if (r->header_end > old) { + r->header_end = new + (r->header_end - old); + } + + r->header_in = buf; + + stream->header_buffers++; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_spdy_handle_request_header(ngx_http_request_t *r) +{ + ngx_uint_t i; + ngx_table_elt_t *h; + ngx_http_core_srv_conf_t *cscf; + ngx_http_spdy_request_header_t *sh; + + if (r->invalid_header) { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + if (cscf->ignore_invalid_headers) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid header: \"%*s\"", + r->header_end - r->header_name_start, + r->header_name_start); + return NGX_OK; + } + + } else { + for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) { + sh = &ngx_http_spdy_request_headers[i]; + + if (sh->hash != r->header_hash + || sh->len != r->lowcase_index + || ngx_strncmp(sh->header, r->header_name_start, + r->lowcase_index) + != 0) + { + continue; + } + + return sh->handler(r); + } + } + + h = ngx_list_push(&r->headers_in.headers); + if (h == NULL) { + ngx_http_spdy_close_stream(r->spdy_stream, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + h->hash = r->header_hash; + + h->key.len = r->lowcase_index; + h->key.data = r->header_name_start; + h->key.data[h->key.len] = '\0'; + + h->value.len = r->header_size; + h->value.data = r->header_start; + h->value.data[h->value.len] = '\0'; + + h->lowcase_key = h->key.data; + + return NGX_OK; +} + + +void +ngx_http_spdy_request_headers_init() +{ + ngx_uint_t i; + ngx_http_spdy_request_header_t *h; + + for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) { + h = &ngx_http_spdy_request_headers[i]; + h->hash = ngx_hash_key(h->header, h->len); + } +} + + +static ngx_int_t +ngx_http_spdy_parse_method(ngx_http_request_t *r) +{ + size_t k, len; + ngx_uint_t n; + const u_char *p, *m; + + /* + * This array takes less than 256 sequential bytes, + * and if typical CPU cache line size is 64 bytes, + * it is prefetched for 4 load operations. + */ + static const struct { + u_char len; + const u_char method[11]; + uint32_t value; + } tests[] = { + { 3, "GET", NGX_HTTP_GET }, + { 4, "POST", NGX_HTTP_POST }, + { 4, "HEAD", NGX_HTTP_HEAD }, + { 7, "OPTIONS", NGX_HTTP_OPTIONS }, + { 8, "PROPFIND", NGX_HTTP_PROPFIND }, + { 3, "PUT", NGX_HTTP_PUT }, + { 5, "MKCOL", NGX_HTTP_MKCOL }, + { 6, "DELETE", NGX_HTTP_DELETE }, + { 4, "COPY", NGX_HTTP_COPY }, + { 4, "MOVE", NGX_HTTP_MOVE }, + { 9, "PROPPATCH", NGX_HTTP_PROPPATCH }, + { 4, "LOCK", NGX_HTTP_LOCK }, + { 6, "UNLOCK", NGX_HTTP_UNLOCK }, + { 5, "PATCH", NGX_HTTP_PATCH }, + { 5, "TRACE", NGX_HTTP_TRACE } + }, *test; + + if (r->method_name.len) { + return NGX_HTTP_PARSE_INVALID_HEADER; + } + + len = r->header_size; + + r->method_name.len = len; + r->method_name.data = r->header_start; + + test = tests; + n = sizeof(tests) / sizeof(tests[0]); + + do { + if (len == test->len) { + p = r->method_name.data; + m = test->method; + k = len; + + do { + if (*p++ != *m++) { + goto next; + } + } while (--k); + + r->method = test->value; + return NGX_OK; + } + + next: + test++; + + } while (--n); + + p = r->method_name.data; + + do { + if ((*p < 'A' || *p > 'Z') && *p != '_') { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid method"); + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + + p++; + + } while (--len); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_spdy_parse_scheme(ngx_http_request_t *r) +{ + if (r->schema_start) { + return NGX_HTTP_PARSE_INVALID_HEADER; + } + + r->schema_start = r->header_start; + r->schema_end = r->header_end; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_spdy_parse_url(ngx_http_request_t *r) +{ + if (r->unparsed_uri.len) { + return NGX_HTTP_PARSE_INVALID_HEADER; + } + + r->uri_start = r->header_start; + r->uri_end = r->header_end; + + if (ngx_http_parse_uri(r) != NGX_OK) { + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + + if (ngx_http_process_request_uri(r) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_spdy_parse_version(ngx_http_request_t *r) +{ + u_char *p, ch; + + if (r->http_protocol.len) { + return NGX_HTTP_PARSE_INVALID_HEADER; + } + + p = r->header_start; + + if (r->header_size < 8 || !(ngx_str5cmp(p, 'H', 'T', 'T', 'P', '/'))) { + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + + ch = *(p + 5); + + if (ch < '1' || ch > '9') { + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + + r->http_major = ch - '0'; + + for (p += 6; p != r->header_end - 2; p++) { + + ch = *p; + + if (ch < '0' || ch > '9') { + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + + r->http_major = r->http_major * 10 + ch - '0'; + } + + if (*p != '.') { + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + + ch = *(p + 1); + + if (ch < '0' || ch > '9') { + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + + r->http_minor = ch - '0'; + + for (p += 2; p != r->header_end; p++) { + + ch = *p; + + if (ch < '0' || ch > '9') { + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + + r->http_minor = r->http_minor * 10 + ch - '0'; + } + + r->http_protocol.len = r->header_size; + r->http_protocol.data = r->header_start; + r->http_version = r->http_major * 1000 + r->http_minor; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_spdy_construct_request_line(ngx_http_request_t *r) +{ + u_char *p; + + if (r->method_name.len == 0 + || r->unparsed_uri.len == 0 + || r->http_protocol.len == 0) + { + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; + } + + r->request_line.len = r->method_name.len + 1 + + r->unparsed_uri.len + 1 + + r->http_protocol.len; + + p = ngx_pnalloc(r->pool, r->request_line.len + 1); + if (p == NULL) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + r->request_line.data = p; + + p = ngx_cpymem(p, r->method_name.data, r->method_name.len); + + *p++ = ' '; + + p = ngx_cpymem(p, r->unparsed_uri.data, r->unparsed_uri.len); + + *p++ = ' '; + + ngx_memcpy(p, r->http_protocol.data, r->http_protocol.len + 1); + + /* some modules expect the space character after method name */ + r->method_name.data = r->request_line.data; + + return NGX_OK; +} + + +static void +ngx_http_spdy_run_request(ngx_http_request_t *r) +{ + ngx_uint_t i; + ngx_list_part_t *part; + ngx_table_elt_t *h; + ngx_http_header_t *hh; + ngx_http_core_main_conf_t *cmcf; + + if (ngx_http_spdy_construct_request_line(r) != NGX_OK) { + return; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "spdy http request line: \"%V\"", &r->request_line); + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + part = &r->headers_in.headers.part; + h = part->elts; + + for (i = 0 ;; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + h = part->elts; + i = 0; + } + + hh = ngx_hash_find(&cmcf->headers_in_hash, h[i].hash, + h[i].lowcase_key, h[i].key.len); + + if (hh && hh->handler(r, &h[i], hh->offset) != NGX_OK) { + return; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http header: \"%V: %V\"", &h[i].key, &h[i].value); + } + + r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE; + + if (ngx_http_process_request_header(r) != NGX_OK) { + return; + } + + ngx_http_process_request(r); +} + + +static ngx_int_t +ngx_http_spdy_init_request_body(ngx_http_request_t *r) +{ + ngx_buf_t *buf; + ngx_temp_file_t *tf; + ngx_http_request_body_t *rb; + ngx_http_core_loc_conf_t *clcf; + + rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); + if (rb == NULL) { + return NGX_ERROR; + } + + r->request_body = rb; + + if (r->spdy_stream->in_closed) { + return NGX_OK; + } + + rb->rest = r->headers_in.content_length_n; + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (r->request_body_in_file_only + || rb->rest > (off_t) clcf->client_body_buffer_size + || rb->rest < 0) + { + tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); + if (tf == NULL) { + return NGX_ERROR; + } + + tf->file.fd = NGX_INVALID_FILE; + tf->file.log = r->connection->log; + tf->path = clcf->client_body_temp_path; + tf->pool = r->pool; + tf->warn = "a client request body is buffered to a temporary file"; + tf->log_level = r->request_body_file_log_level; + tf->persistent = r->request_body_in_persistent_file; + tf->clean = r->request_body_in_clean_file; + + if (r->request_body_file_group_access) { + tf->access = 0660; + } + + rb->temp_file = tf; + + if (r->spdy_stream->in_closed + && ngx_create_temp_file(&tf->file, tf->path, tf->pool, + tf->persistent, tf->clean, tf->access) + != NGX_OK) + { + return NGX_ERROR; + } + + buf = ngx_calloc_buf(r->pool); + if (buf == NULL) { + return NGX_ERROR; + } + + if (rb->rest == 0) { + buf->in_file = 1; + buf->file = &tf->file; + } else { + rb->buf = buf; + } + + } else { + + if (rb->rest == 0) { + return NGX_OK; + } + + buf = ngx_create_temp_buf(r->pool, (size_t) rb->rest); + if (buf == NULL) { + return NGX_ERROR; + } + + rb->buf = buf; + } + + rb->bufs = ngx_alloc_chain_link(r->pool); + if (rb->bufs == NULL) { + return NGX_ERROR; + } + + rb->bufs->buf = buf; + rb->bufs->next = NULL; + + rb->rest = 0; + + return NGX_OK; +} + + +ngx_int_t +ngx_http_spdy_read_request_body(ngx_http_request_t *r, + ngx_http_client_body_handler_pt post_handler) +{ + ngx_http_spdy_stream_t *stream; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "spdy read request body"); + + stream = r->spdy_stream; + + switch (stream->skip_data) { + + case NGX_SPDY_DATA_DISCARD: + post_handler(r); + return NGX_OK; + + case NGX_SPDY_DATA_ERROR: + if (r->headers_in.content_length_n == -1) { + return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; + } else { + return NGX_HTTP_BAD_REQUEST; + } + + case NGX_SPDY_DATA_INTERNAL_ERROR: + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (!r->request_body && ngx_http_spdy_init_request_body(r) != NGX_OK) { + stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR; + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (stream->in_closed) { + post_handler(r); + return NGX_OK; + } + + r->request_body->post_handler = post_handler; + + return NGX_AGAIN; +} + + +void +ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc) +{ + ngx_event_t *ev; + ngx_connection_t *fc; + ngx_http_spdy_stream_t **index, *s; + ngx_http_spdy_srv_conf_t *sscf; + ngx_http_spdy_connection_t *sc; + + sc = stream->connection; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy close stream %ui, processing %ui", + stream->id, sc->processing); + + if (!stream->out_closed) { + if (ngx_http_spdy_send_rst_stream(sc, stream->id, + NGX_SPDY_INTERNAL_ERROR, + stream->priority) + != NGX_OK) + { + sc->connection->error = 1; + } + } + + sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, + ngx_http_spdy_module); + + index = sc->streams_index + ngx_http_spdy_stream_index(sscf, stream->id); + + for ( ;; ) { + s = *index; + + if (s == NULL) { + break; + } + + if (s == stream) { + *index = s->index; + break; + } + + index = &s->index; + } + + fc = stream->request->connection; + + ngx_http_free_request(stream->request, rc); + + ev = fc->read; + + if (ev->active || ev->disabled) { + ngx_del_event(ev, NGX_READ_EVENT, 0); + } + + if (ev->timer_set) { + ngx_del_timer(ev); + } + + if (ev->prev) { + ngx_delete_posted_event(ev); + } + + ev = fc->write; + + if (ev->active || ev->disabled) { + ngx_del_event(ev, NGX_WRITE_EVENT, 0); + } + + if (ev->timer_set) { + ngx_del_timer(ev); + } + + if (ev->prev) { + ngx_delete_posted_event(ev); + } + + fc->data = sc->free_fake_connections; + sc->free_fake_connections = fc; + + sc->processing--; + + if (sc->processing || sc->blocked) { + return; + } + + ev = sc->connection->read; + + ev->handler = ngx_http_spdy_handle_connection_handler; + ngx_post_event(ev, &ngx_posted_events); +} + + +static void +ngx_http_spdy_handle_connection_handler(ngx_event_t *rev) +{ + ngx_connection_t *c; + + rev->handler = ngx_http_spdy_read_handler; + + if (rev->ready) { + ngx_http_spdy_read_handler(rev); + return; + } + + c = rev->data; + + ngx_http_spdy_handle_connection(c->data); +} + + +static void +ngx_http_spdy_keepalive_handler(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_http_spdy_srv_conf_t *sscf; + ngx_http_spdy_connection_t *sc; + + c = rev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy keepalive handler"); + + if (rev->timedout || c->close) { + ngx_http_close_connection(c); + return; + } + +#if (NGX_HAVE_KQUEUE) + + if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { + if (rev->pending_eof) { + c->log->handler = NULL; + ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno, + "kevent() reported that client %V closed " + "keepalive connection", &c->addr_text); +#if (NGX_HTTP_SSL) + if (c->ssl) { + c->ssl->no_send_shutdown = 1; + } +#endif + ngx_http_close_connection(c); + return; + } + } + +#endif + + c->destroyed = 0; + c->idle = 0; + ngx_reusable_connection(c, 0); + + sc = c->data; + + sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, + ngx_http_spdy_module); + + sc->pool = ngx_create_pool(sscf->pool_size, sc->connection->log); + if (sc->pool == NULL) { + ngx_http_close_connection(c); + return; + } + + sc->streams_index = ngx_pcalloc(sc->pool, + ngx_http_spdy_streams_index_size(sscf) + * sizeof(ngx_http_spdy_stream_t *)); + if (sc->streams_index == NULL) { + ngx_http_close_connection(c); + return; + } + + c->write->handler = ngx_http_spdy_write_handler; + + rev->handler = ngx_http_spdy_read_handler; + ngx_http_spdy_read_handler(rev); +} + + +static void +ngx_http_spdy_finalize_connection(ngx_http_spdy_connection_t *sc, + ngx_int_t rc) +{ + ngx_uint_t i, size; + ngx_event_t *ev; + ngx_connection_t *c, *fc; + ngx_http_request_t *r; + ngx_http_spdy_stream_t *stream; + ngx_http_spdy_srv_conf_t *sscf; + + c = sc->connection; + + if (!sc->processing) { + ngx_http_close_connection(c); + return; + } + + c->error = 1; + c->read->handler = ngx_http_empty_handler; + + sc->last_out = NULL; + + sc->blocked = 1; + + sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, + ngx_http_spdy_module); + + size = ngx_http_spdy_streams_index_size(sscf); + + for (i = 0; i < size; i++) { + stream = sc->streams_index[i]; + + while (stream) { + r = stream->request; + + fc = r->connection; + fc->error = 1; + + if (stream->waiting) { + r->blocked -= stream->waiting; + stream->waiting = 0; + ev = fc->write; + + } else { + ev = fc->read; + } + + stream = stream->index; + + ev->eof = 1; + ev->handler(ev); + } + } + + sc->blocked = 0; + + if (sc->processing) { + return; + } + + ngx_http_close_connection(c); +} + + +static void +ngx_http_spdy_pool_cleanup(void *data) +{ + ngx_http_spdy_connection_t *sc = data; + + if (sc->pool) { + ngx_destroy_pool(sc->pool); + } +} + + +static void * +ngx_http_spdy_zalloc(void *opaque, u_int items, u_int size) +{ + ngx_http_spdy_connection_t *sc = opaque; + + return ngx_palloc(sc->connection->pool, items * size); +} + + +static void +ngx_http_spdy_zfree(void *opaque, void *address) +{ +#if 0 + ngx_http_spdy_connection_t *sc = opaque; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy zfree: %p", address); +#endif +} diff -r a10cc9106dc7 -r 7271c16e52cc src/http/ngx_http_spdy.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/http/ngx_http_spdy.h Wed Mar 20 19:49:40 2013 +0400 @@ -0,0 +1,235 @@ +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Valentin V. Bartenev + */ + + +#ifndef _NGX_HTTP_SPDY_H_INCLUDED_ +#define _NGX_HTTP_SPDY_H_INCLUDED_ + + +#include +#include +#include + +#include + + +#define NGX_SPDY_VERSION 2 + +#ifdef TLSEXT_TYPE_next_proto_neg +#define NGX_SPDY_NPN_ADVERTISE "\x06spdy/2" +#define NGX_SPDY_NPN_NEGOTIATED "spdy/2" +#endif + +#define NGX_SPDY_STATE_BUFFER_SIZE 16 + +#define NGX_SPDY_CTL_BIT 1 + +#define NGX_SPDY_SYN_STREAM 1 +#define NGX_SPDY_SYN_REPLY 2 +#define NGX_SPDY_RST_STREAM 3 +#define NGX_SPDY_SETTINGS 4 +#define NGX_SPDY_NOOP 5 +#define NGX_SPDY_PING 6 +#define NGX_SPDY_GOAWAY 7 +#define NGX_SPDY_HEADERS 8 + +#define NGX_SPDY_FRAME_HEADER_SIZE 8 + +#define NGX_SPDY_SID_SIZE 4 + +#define NGX_SPDY_SYN_STREAM_SIZE 10 +#define NGX_SPDY_SYN_REPLY_SIZE 6 +#define NGX_SPDY_RST_STREAM_SIZE 8 +#define NGX_SPDY_PING_SIZE 4 +#define NGX_SPDY_GOAWAY_SIZE 4 +#define NGX_SPDY_NV_NUM_SIZE 2 +#define NGX_SPDY_NV_NLEN_SIZE 2 +#define NGX_SPDY_NV_VLEN_SIZE 2 +#define NGX_SPDY_SETTINGS_NUM_SIZE 4 +#define NGX_SPDY_SETTINGS_IDF_SIZE 4 +#define NGX_SPDY_SETTINGS_VAL_SIZE 4 + +#define NGX_SPDY_SETTINGS_PAIR_SIZE \ + (NGX_SPDY_SETTINGS_IDF_SIZE + NGX_SPDY_SETTINGS_VAL_SIZE) + +#define NGX_SPDY_HIGHEST_PRIORITY 0 +#define NGX_SPDY_LOWEST_PRIORITY 3 + +#define NGX_SPDY_FLAG_FIN 0x01 +#define NGX_SPDY_FLAG_UNIDIRECTIONAL 0x02 +#define NGX_SPDY_FLAG_CLEAR_SETTINGS 0x01 + +#define NGX_SPDY_MAX_FRAME_SIZE ((1 << 24) - 1) + +#define NGX_SPDY_DATA_DISCARD 1 +#define NGX_SPDY_DATA_ERROR 2 +#define NGX_SPDY_DATA_INTERNAL_ERROR 3 + + +typedef struct ngx_http_spdy_connection_s ngx_http_spdy_connection_t; +typedef struct ngx_http_spdy_out_frame_s ngx_http_spdy_out_frame_t; + + +typedef u_char *(*ngx_http_spdy_handler_pt) (ngx_http_spdy_connection_t *sc, + u_char *pos, u_char *end); + +struct ngx_http_spdy_connection_s { + ngx_connection_t *connection; + ngx_http_connection_t *http_connection; + + ngx_uint_t processing; + + u_char buffer[NGX_SPDY_STATE_BUFFER_SIZE]; + size_t buffer_used; + ngx_http_spdy_handler_pt handler; + + z_stream zstream_in; + z_stream zstream_out; + + ngx_pool_t *pool; + + ngx_http_spdy_out_frame_t *free_ctl_frames; + ngx_connection_t *free_fake_connections; + + ngx_http_spdy_stream_t **streams_index; + + ngx_http_spdy_out_frame_t *last_out; + ngx_http_spdy_stream_t *last_stream; + + ngx_http_spdy_stream_t *stream; + + ngx_uint_t headers; + size_t length; + u_char flags; + + ngx_uint_t last_sid; + + unsigned blocked:2; + unsigned waiting:1; /* FIXME better name */ +}; + + +struct ngx_http_spdy_stream_s { + ngx_uint_t id; + ngx_http_request_t *request; + ngx_http_spdy_connection_t *connection; + ngx_http_spdy_stream_t *index; + ngx_http_spdy_stream_t *next; + + ngx_uint_t header_buffers; + ngx_uint_t waiting; + ngx_http_spdy_out_frame_t *free_frames; + ngx_chain_t *free_data_headers; + + unsigned priority:2; + unsigned handled:1; + unsigned in_closed:1; + unsigned out_closed:1; + unsigned skip_data:2; +}; + + +struct ngx_http_spdy_out_frame_s { + ngx_http_spdy_out_frame_t *next; + ngx_chain_t *first; + ngx_chain_t *last; + ngx_int_t (*handler)(ngx_http_spdy_connection_t *sc, + ngx_http_spdy_out_frame_t *frame); + + ngx_http_spdy_out_frame_t *free; + + ngx_http_spdy_stream_t *stream; + size_t size; + + ngx_uint_t priority; + unsigned blocked:1; + unsigned fin:1; +}; + + +static ngx_inline void +ngx_http_spdy_queue_frame(ngx_http_spdy_connection_t *sc, + ngx_http_spdy_out_frame_t *frame) +{ + ngx_http_spdy_out_frame_t **out; + + for (out = &sc->last_out; *out; out = &(*out)->next) + { + if (frame->priority >= (*out)->priority) { + break; + } + } + + frame->next = *out; + *out = frame; +} + + +static ngx_inline void +ngx_http_spdy_queue_blocked_frame(ngx_http_spdy_connection_t *sc, + ngx_http_spdy_out_frame_t *frame) +{ + ngx_http_spdy_out_frame_t **out; + + for (out = &sc->last_out; *out && !(*out)->blocked; out = &(*out)->next) + { + if (frame->priority >= (*out)->priority) { + break; + } + } + + frame->next = *out; + *out = frame; +} + + +void ngx_http_spdy_init(ngx_event_t *rev); +void ngx_http_spdy_request_headers_init(); + +ngx_int_t ngx_http_spdy_read_request_body(ngx_http_request_t *r, + ngx_http_client_body_handler_pt post_handler); + +void ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc); + +ngx_int_t ngx_http_spdy_send_output_queue(ngx_http_spdy_connection_t *sc); + + +#define ngx_spdy_frame_aligned_write_uint16(p, s) \ + (*(uint16_t *) (p) = htons((uint16_t) (s)), (p) + sizeof(uint16_t)) + +#define ngx_spdy_frame_aligned_write_uint32(p, s) \ + (*(uint32_t *) (p) = htonl((uint32_t) (s)), (p) + sizeof(uint32_t)) + +#if (NGX_HAVE_NONALIGNED) + +#define ngx_spdy_frame_write_uint16 ngx_spdy_frame_aligned_write_uint16 +#define ngx_spdy_frame_write_uint32 ngx_spdy_frame_aligned_write_uint32 + +#else + +#define ngx_spdy_frame_write_uint16(p, s) \ + ((p)[0] = (u_char) (s) >> 8, (p)[1] = (u_char) (s), (p) + sizeof(uint16_t)) + +#define ngx_spdy_frame_write_uint32(p, s) \ + ((p)[0] = (u_char) (s) >> 24, \ + (p)[1] = (u_char) (s) >> 16, \ + (p)[2] = (u_char) (s) >> 8, \ + (p)[3] = (u_char) (s), (p) + sizeof(uint32_t)) + +#endif + + +#define ngx_spdy_ctl_frame_head(t) \ + ((uint32_t) NGX_SPDY_CTL_BIT << 31 | NGX_SPDY_VERSION << 16 | (t)) + +#define ngx_spdy_frame_write_head(p, t) \ + ngx_spdy_frame_aligned_write_uint32(p, ngx_spdy_ctl_frame_head(t)) + +#define ngx_spdy_frame_write_flags_and_len(p, f, l) \ + ngx_spdy_frame_aligned_write_uint32(p, (f) << 24 | (l)) + +#define ngx_spdy_frame_write_sid ngx_spdy_frame_aligned_write_uint32 + +#endif /* _NGX_HTTP_SPDY_H_INCLUDED_ */ diff -r a10cc9106dc7 -r 7271c16e52cc src/http/ngx_http_spdy_filter_module.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/http/ngx_http_spdy_filter_module.c Wed Mar 20 19:49:40 2013 +0400 @@ -0,0 +1,999 @@ + +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Valentin V. Bartenev + */ + + +#include +#include +#include +#include +#include + +#include + + +#define NGX_SPDY_WRITE_BUFFERED NGX_HTTP_WRITE_BUFFERED + +#define ngx_http_spdy_nv_nsize(h) (NGX_SPDY_NV_NLEN_SIZE + sizeof(h) - 1) +#define ngx_http_spdy_nv_vsize(h) (NGX_SPDY_NV_VLEN_SIZE + sizeof(h) - 1) + +#define ngx_http_spdy_nv_write_num ngx_spdy_frame_write_uint16 +#define ngx_http_spdy_nv_write_nlen ngx_spdy_frame_write_uint16 +#define ngx_http_spdy_nv_write_vlen ngx_spdy_frame_write_uint16 + +#define ngx_http_spdy_nv_write_name(p, h) \ + ngx_cpymem(ngx_http_spdy_nv_write_nlen(p, sizeof(h) - 1), h, sizeof(h) - 1) + +#define ngx_http_spdy_nv_write_val(p, h) \ + ngx_cpymem(ngx_http_spdy_nv_write_vlen(p, sizeof(h) - 1), h, sizeof(h) - 1) + +static ngx_inline ngx_int_t ngx_http_spdy_filter_send( + ngx_connection_t *fc, ngx_http_spdy_stream_t *stream); + +static ngx_http_spdy_out_frame_t *ngx_http_spdy_filter_get_data_frame( + ngx_http_spdy_stream_t *stream, size_t len, ngx_uint_t flags, + ngx_chain_t *first, ngx_chain_t *last); + +static ngx_int_t ngx_http_spdy_syn_frame_handler( + ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame); +static ngx_int_t ngx_http_spdy_data_frame_handler( + ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame); +static ngx_inline void ngx_http_spdy_handle_frame( + ngx_http_spdy_stream_t *stream, ngx_http_spdy_out_frame_t *frame); +static ngx_inline void ngx_http_spdy_handle_stream( + ngx_http_spdy_connection_t *sc, ngx_http_spdy_stream_t *stream); + +static void ngx_http_spdy_filter_cleanup(void *data); + +static ngx_int_t ngx_http_spdy_filter_init(ngx_conf_t *cf); + + +static ngx_http_module_t ngx_http_spdy_filter_module_ctx = { + NULL, /* preconfiguration */ + ngx_http_spdy_filter_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + NULL, /* create location configuration */ + NULL /* merge location configuration */ +}; + + +ngx_module_t ngx_http_spdy_filter_module = { + NGX_MODULE_V1, + &ngx_http_spdy_filter_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_http_output_header_filter_pt ngx_http_next_header_filter; +static ngx_http_output_body_filter_pt ngx_http_next_body_filter; + + +static ngx_int_t +ngx_http_spdy_header_filter(ngx_http_request_t *r) +{ + int rc; + size_t len; + u_char *p, *buf, *last; + ngx_buf_t *b; + ngx_str_t host; + ngx_uint_t i, j, count, port; + ngx_chain_t *cl; + ngx_list_part_t *part, *pt; + ngx_table_elt_t *header, *h; + ngx_connection_t *c; + ngx_http_cleanup_t *cln; + ngx_http_core_loc_conf_t *clcf; + ngx_http_core_srv_conf_t *cscf; + ngx_http_spdy_stream_t *stream; + ngx_http_spdy_out_frame_t *frame; + ngx_http_spdy_connection_t *sc; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + u_char addr[NGX_SOCKADDR_STRLEN]; + + if (!r->spdy_stream) { + return ngx_http_next_header_filter(r); + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "spdy header filter"); + + if (r->header_sent) { + return NGX_OK; + } + + r->header_sent = 1; + + if (r != r->main) { + return NGX_OK; + } + + c = r->connection; + + if (r->method == NGX_HTTP_HEAD) { + r->header_only = 1; + } + + switch (r->headers_out.status) { + + case NGX_HTTP_OK: + case NGX_HTTP_PARTIAL_CONTENT: + break; + + case NGX_HTTP_NOT_MODIFIED: + r->header_only = 1; + break; + + case NGX_HTTP_NO_CONTENT: + r->header_only = 1; + + ngx_str_null(&r->headers_out.content_type); + + r->headers_out.content_length = NULL; + r->headers_out.content_length_n = -1; + + /* fall through */ + + default: + r->headers_out.last_modified_time = -1; + r->headers_out.last_modified = NULL; + } + + len = NGX_SPDY_NV_NUM_SIZE + + ngx_http_spdy_nv_nsize("version") + + ngx_http_spdy_nv_vsize("HTTP/1.1") + + ngx_http_spdy_nv_nsize("status") + + ngx_http_spdy_nv_vsize("418"); + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (r->headers_out.server == NULL) { + len += ngx_http_spdy_nv_nsize("server"); + len += clcf->server_tokens ? ngx_http_spdy_nv_vsize(NGINX_VER) + : ngx_http_spdy_nv_vsize("nginx"); + } + + if (r->headers_out.date == NULL) { + len += ngx_http_spdy_nv_nsize("date") + + ngx_http_spdy_nv_vsize("Wed, 31 Dec 1986 10:00:00 GMT"); + } + + if (r->headers_out.content_type.len) { + len += ngx_http_spdy_nv_nsize("content-type") + + NGX_SPDY_NV_VLEN_SIZE + r->headers_out.content_type.len; + + if (r->headers_out.content_type_len == r->headers_out.content_type.len + && r->headers_out.charset.len) + { + len += sizeof("; charset=") - 1 + r->headers_out.charset.len; + } + } + + if (r->headers_out.content_length == NULL + && r->headers_out.content_length_n >= 0) + { + len += ngx_http_spdy_nv_nsize("content-length") + + NGX_SPDY_NV_VLEN_SIZE + NGX_OFF_T_LEN; + } + + if (r->headers_out.last_modified == NULL + && r->headers_out.last_modified_time != -1) + { + len += ngx_http_spdy_nv_nsize("last-modified") + + ngx_http_spdy_nv_vsize("Wed, 31 Dec 1986 10:00:00 GMT"); + } + + if (r->headers_out.location + && r->headers_out.location->value.len + && r->headers_out.location->value.data[0] == '/') + { + r->headers_out.location->hash = 0; + + if (clcf->server_name_in_redirect) { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + host = cscf->server_name; + + } else if (r->headers_in.server.len) { + host = r->headers_in.server; + + } else { + host.len = NGX_SOCKADDR_STRLEN; + host.data = addr; + + if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) { + return NGX_ERROR; + } + } + + switch (c->local_sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) c->local_sockaddr; + port = ntohs(sin6->sin6_port); + break; +#endif +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + port = 0; + break; +#endif + default: /* AF_INET */ + sin = (struct sockaddr_in *) c->local_sockaddr; + port = ntohs(sin->sin_port); + break; + } + + len += ngx_http_spdy_nv_nsize("location") + + ngx_http_spdy_nv_vsize("https://") + + host.len + + r->headers_out.location->value.len; + + if (clcf->port_in_redirect) { + +#if (NGX_HTTP_SSL) + if (c->ssl) + port = (port == 443) ? 0 : port; + else +#endif + port = (port == 80) ? 0 : port; + + } else { + port = 0; + } + + if (port) { + len += sizeof(":65535") - 1; + } + + } else { + ngx_str_null(&host); + port = 0; + } + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + len += NGX_SPDY_NV_NLEN_SIZE + header[i].key.len + + NGX_SPDY_NV_VLEN_SIZE + header[i].value.len; + } + + buf = ngx_alloc(len, r->pool->log); + if (buf == NULL) { + return NGX_ERROR; + } + + last = buf + NGX_SPDY_NV_NUM_SIZE; + + last = ngx_http_spdy_nv_write_name(last, "version"); + last = ngx_http_spdy_nv_write_val(last, "HTTP/1.1"); + + last = ngx_http_spdy_nv_write_name(last, "status"); + last = ngx_spdy_frame_write_uint16(last, 3); + last = ngx_sprintf(last, "%03ui", r->headers_out.status); + + count = 2; + + if (r->headers_out.server == NULL) { + last = ngx_http_spdy_nv_write_name(last, "server"); + last = clcf->server_tokens + ? ngx_http_spdy_nv_write_val(last, NGINX_VER) + : ngx_http_spdy_nv_write_val(last, "nginx"); + + count++; + } + + if (r->headers_out.date == NULL) { + last = ngx_http_spdy_nv_write_name(last, "date"); + + last = ngx_http_spdy_nv_write_vlen(last, ngx_cached_http_time.len); + + last = ngx_cpymem(last, ngx_cached_http_time.data, + ngx_cached_http_time.len); + + count++; + } + + if (r->headers_out.content_type.len) { + + last = ngx_http_spdy_nv_write_name(last, "content-type"); + + p = last + NGX_SPDY_NV_VLEN_SIZE; + + last = ngx_cpymem(p, r->headers_out.content_type.data, + r->headers_out.content_type.len); + + if (r->headers_out.content_type_len == r->headers_out.content_type.len + && r->headers_out.charset.len) + { + last = ngx_cpymem(last, "; charset=", sizeof("; charset=") - 1); + + last = ngx_cpymem(last, r->headers_out.charset.data, + r->headers_out.charset.len); + + /* update r->headers_out.content_type for possible logging */ + + r->headers_out.content_type.len = last - p; + r->headers_out.content_type.data = p; + } + + (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE, + r->headers_out.content_type.len); + + count++; + } + + if (r->headers_out.content_length == NULL + && r->headers_out.content_length_n >= 0) + { + last = ngx_http_spdy_nv_write_name(last, "content-length"); + + p = last + NGX_SPDY_NV_VLEN_SIZE; + + last = ngx_sprintf(p, "%O", r->headers_out.content_length_n); + + (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE, + last - p); + + count++; + } + + if (r->headers_out.last_modified == NULL + && r->headers_out.last_modified_time != -1) + { + last = ngx_http_spdy_nv_write_name(last, "last-modified"); + + p = last + NGX_SPDY_NV_VLEN_SIZE; + + last = ngx_http_time(p, r->headers_out.last_modified_time); + + (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE, + last - p); + + count++; + } + + if (host.data) { + + last = ngx_http_spdy_nv_write_name(last, "location"); + + p = last + NGX_SPDY_NV_VLEN_SIZE; + + last = ngx_cpymem(p, "http", sizeof("http") - 1); + +#if (NGX_HTTP_SSL) + if (c->ssl) { + *last++ ='s'; + } +#endif + + *last++ = ':'; *last++ = '/'; *last++ = '/'; + + last = ngx_cpymem(last, host.data, host.len); + + if (port) { + last = ngx_sprintf(last, ":%ui", port); + } + + last = ngx_cpymem(last, r->headers_out.location->value.data, + r->headers_out.location->value.len); + + /* update r->headers_out.location->value for possible logging */ + + r->headers_out.location->value.len = last - p; + r->headers_out.location->value.data = p; + ngx_str_set(&r->headers_out.location->key, "location"); + + (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE, + r->headers_out.location->value.len); + + count++; + } + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0 || header[i].hash == 2) { + continue; + } + + if ((header[i].key.len == 6 + && ngx_strncasecmp(header[i].key.data, + (u_char *) "status", 6) == 0) + || (header[i].key.len == 7 + && ngx_strncasecmp(header[i].key.data, + (u_char *) "version", 7) == 0)) + { + header[i].hash = 0; + continue; + } + + last = ngx_http_spdy_nv_write_nlen(last, header[i].key.len); + + ngx_strlow(last, header[i].key.data, header[i].key.len); + last += header[i].key.len; + + p = last + NGX_SPDY_NV_VLEN_SIZE; + + last = ngx_cpymem(p, header[i].value.data, header[i].value.len); + + pt = part; + h = header; + + for (j = i + 1; /* void */; j++) { + + if (j >= pt->nelts) { + if (pt->next == NULL) { + break; + } + + pt = pt->next; + h = pt->elts; + j = 0; + } + + if (h[j].hash == 0 || h[j].hash == 2 + || h[j].key.len != header[i].key.len + || ngx_strncasecmp(header[i].key.data, h[j].key.data, + header[i].key.len)) + { + continue; + } + + *last++ = '\0'; + + last = ngx_cpymem(last, h[j].value.data, h[j].value.len); + + h[j].hash = 2; + } + + (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE, + last - p); + + count++; + } + + (void) ngx_spdy_frame_write_uint16(buf, count); + + stream = r->spdy_stream; + sc = stream->connection; + + len = last - buf; + + b = ngx_create_temp_buf(r->pool, NGX_SPDY_FRAME_HEADER_SIZE + + NGX_SPDY_SYN_REPLY_SIZE + + deflateBound(&sc->zstream_out, len)); + if (b == NULL) { + ngx_free(buf); + return NGX_ERROR; + } + + b->last += NGX_SPDY_FRAME_HEADER_SIZE + NGX_SPDY_SYN_REPLY_SIZE; + + sc->zstream_out.next_in = buf; + sc->zstream_out.avail_in = len; + sc->zstream_out.next_out = b->last; + sc->zstream_out.avail_out = b->end - b->last; + + rc = deflate(&sc->zstream_out, Z_SYNC_FLUSH); + + ngx_free(buf); + + if (rc != Z_OK) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "spdy deflate() failed: %d", rc); + return NGX_ERROR; + } + + ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, + "spdy deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", + sc->zstream_out.next_in, sc->zstream_out.next_out, + sc->zstream_out.avail_in, sc->zstream_out.avail_out, + rc); + + b->last = sc->zstream_out.next_out; + + p = b->pos; + p = ngx_spdy_frame_write_head(p, NGX_SPDY_SYN_REPLY); + + len = b->last - b->pos; + + r->header_size = len; + + if (r->header_only) { + b->last_buf = 1; + p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_FIN, + len - NGX_SPDY_FRAME_HEADER_SIZE); + } else { + p = ngx_spdy_frame_write_flags_and_len(p, 0, + len - NGX_SPDY_FRAME_HEADER_SIZE); + } + + (void) ngx_spdy_frame_write_sid(p, stream->id); + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + cl->next = NULL; + + frame = ngx_palloc(r->pool, sizeof(ngx_http_spdy_out_frame_t)); + if (frame == NULL) { + return NGX_ERROR; + } + + frame->first = cl; + frame->last = cl; + frame->handler = ngx_http_spdy_syn_frame_handler; + frame->free = NULL; + frame->stream = stream; + frame->size = len; + frame->priority = stream->priority; + frame->blocked = 1; + frame->fin = r->header_only; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0, + "spdy:%ui create SYN_REPLY frame %p: size:%uz", + stream->id, frame, frame->size); + + ngx_http_spdy_queue_blocked_frame(sc, frame); + + r->blocked++; + + cln = ngx_http_cleanup_add(r, 0); + if (cln == NULL) { + return NGX_ERROR; + } + + cln->handler = ngx_http_spdy_filter_cleanup; + cln->data = stream; + + stream->waiting = 1; + + return ngx_http_spdy_filter_send(c, stream); +} + + +static ngx_int_t +ngx_http_spdy_body_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + off_t size; + ngx_buf_t *b; + ngx_chain_t *cl, *ll, *out, **ln; + ngx_http_spdy_stream_t *stream; + ngx_http_spdy_out_frame_t *frame; + + stream = r->spdy_stream; + + if (stream == NULL) { + return ngx_http_next_body_filter(r, in); + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "spdy body filter \"%V?%V\"", &r->uri, &r->args); + + if (in == NULL || r->header_only) { + + if (stream->waiting) { + return NGX_AGAIN; + } + + r->connection->buffered &= ~NGX_SPDY_WRITE_BUFFERED; + + return NGX_OK; + } + + size = 0; + ln = &out; + ll = in; + + for ( ;; ) { + b = ll->buf; +#if 1 + if (ngx_buf_size(b) == 0 && !ngx_buf_special(b)) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "zero size buf in spdy body filter " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + b->temporary, + b->recycled, + b->in_file, + b->start, + b->pos, + b->last, + b->file, + b->file_pos, + b->file_last); + + ngx_debug_point(); + return NGX_ERROR; + } +#endif + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + size += ngx_buf_size(b); + cl->buf = b; + + *ln = cl; + ln = &cl->next; + + if (ll->next == NULL) { + break; + } + + ll = ll->next; + } + + if (size > NGX_SPDY_MAX_FRAME_SIZE) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "FIXME: chain too big in spdy filter: %O", size); + return NGX_ERROR; + } + + frame = ngx_http_spdy_filter_get_data_frame(stream, (size_t) size, + b->last_buf, out, cl); + if (frame == NULL) { + return NGX_ERROR; + } + + ngx_http_spdy_queue_frame(stream->connection, frame); + + stream->waiting++; + + r->main->blocked++; + + return ngx_http_spdy_filter_send(r->connection, stream); +} + + +static ngx_http_spdy_out_frame_t * +ngx_http_spdy_filter_get_data_frame(ngx_http_spdy_stream_t *stream, + size_t len, ngx_uint_t fin, ngx_chain_t *first, ngx_chain_t *last) +{ + u_char *p; + ngx_buf_t *buf; + ngx_uint_t flags; + ngx_chain_t *cl; + ngx_http_spdy_out_frame_t *frame; + + + frame = stream->free_frames; + + if (frame) { + stream->free_frames = frame->free; + + } else { + frame = ngx_palloc(stream->request->pool, + sizeof(ngx_http_spdy_out_frame_t)); + if (frame == NULL) { + return NULL; + } + } + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0, + "spdy:%ui create DATA frame %p: len:%uz fin:%ui", + stream->id, frame, len, fin); + + if (len || fin) { + + flags = fin ? NGX_SPDY_FLAG_FIN : 0; + + cl = ngx_chain_get_free_buf(stream->request->pool, + &stream->free_data_headers); + if (cl == NULL) { + return NULL; + } + + buf = cl->buf; + + if (buf->start) { + p = buf->start; + buf->pos = p; + + p += sizeof(uint32_t); + + (void) ngx_spdy_frame_write_flags_and_len(p, flags, len); + + } else { + p = ngx_palloc(stream->request->pool, NGX_SPDY_FRAME_HEADER_SIZE); + if (p == NULL) { + return NULL; + } + + buf->pos = p; + buf->start = p; + + p = ngx_spdy_frame_write_sid(p, stream->id); + p = ngx_spdy_frame_write_flags_and_len(p, flags, len); + + buf->last = p; + buf->end = p; + + buf->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_module; + buf->memory = 1; + } + + cl->next = first; + first = cl; + } + + frame->first = first; + frame->last = last; + frame->handler = ngx_http_spdy_data_frame_handler; + frame->free = NULL; + frame->stream = stream; + frame->size = NGX_SPDY_FRAME_HEADER_SIZE + len; + frame->priority = stream->priority; + frame->blocked = 0; + frame->fin = fin; + + return frame; +} + + +static ngx_inline ngx_int_t +ngx_http_spdy_filter_send(ngx_connection_t *fc, ngx_http_spdy_stream_t *stream) +{ + if (ngx_http_spdy_send_output_queue(stream->connection) == NGX_ERROR) { + fc->error = 1; + return NGX_ERROR; + } + + if (stream->waiting) { + fc->buffered |= NGX_SPDY_WRITE_BUFFERED; + fc->write->delayed = 1; + return NGX_AGAIN; + } + + fc->buffered &= ~NGX_SPDY_WRITE_BUFFERED; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_spdy_syn_frame_handler(ngx_http_spdy_connection_t *sc, + ngx_http_spdy_out_frame_t *frame) +{ + ngx_buf_t *buf; + ngx_http_spdy_stream_t *stream; + + buf = frame->first->buf; + + if (buf->pos != buf->last) { + return NGX_AGAIN; + } + + stream = frame->stream; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy:%ui SYN_REPLY frame %p was sent", stream->id, frame); + + ngx_free_chain(stream->request->pool, frame->first); + + ngx_http_spdy_handle_frame(stream, frame); + + ngx_http_spdy_handle_stream(sc, stream); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_spdy_data_frame_handler(ngx_http_spdy_connection_t *sc, + ngx_http_spdy_out_frame_t *frame) +{ + ngx_chain_t *cl, *ln; + ngx_http_spdy_stream_t *stream; + + stream = frame->stream; + + cl = frame->first; + + if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_module) { + + if (cl->buf->pos != cl->buf->last) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy:%ui DATA frame %p was sent partially", + stream->id, frame); + + return NGX_AGAIN; + } + + ln = cl->next; + + cl->next = stream->free_data_headers; + stream->free_data_headers = cl; + + if (cl == frame->last) { + goto done; + } + + cl = ln; + } + + for ( ;; ) { + if (ngx_buf_size(cl->buf) != 0) { + + if (cl != frame->first) { + frame->first = cl; + ngx_http_spdy_handle_stream(sc, stream); + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy:%ui DATA frame %p was sent partially", + stream->id, frame); + + return NGX_AGAIN; + } + + ln = cl->next; + + ngx_free_chain(stream->request->pool, cl); + + if (cl == frame->last) { + goto done; + } + + cl = ln; + } + +done: + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy:%ui DATA frame %p was sent", stream->id, frame); + + stream->request->header_size += NGX_SPDY_FRAME_HEADER_SIZE; + + ngx_http_spdy_handle_frame(stream, frame); + + ngx_http_spdy_handle_stream(sc, stream); + + return NGX_OK; +} + + +static ngx_inline void +ngx_http_spdy_handle_frame(ngx_http_spdy_stream_t *stream, + ngx_http_spdy_out_frame_t *frame) +{ + ngx_http_request_t *r; + + r = stream->request; + + r->connection->sent += frame->size; + r->blocked--; + + if (frame->fin) { + stream->out_closed = 1; + } + + frame->free = stream->free_frames; + stream->free_frames = frame; + + stream->waiting--; +} + + +static ngx_inline void +ngx_http_spdy_handle_stream(ngx_http_spdy_connection_t *sc, + ngx_http_spdy_stream_t *stream) +{ + ngx_connection_t *fc; + + fc = stream->request->connection; + + fc->write->delayed = 0; + + if (stream->handled) { + return; + } + + if (sc->blocked == 2) { + stream->handled = 1; + + stream->next = sc->last_stream; + sc->last_stream = stream; + } +} + + +static void +ngx_http_spdy_filter_cleanup(void *data) +{ + ngx_http_spdy_stream_t *stream = data; + + ngx_http_request_t *r; + ngx_http_spdy_out_frame_t *frame, **fn; + + if (stream->waiting == 0) { + return; + } + + r = stream->request; + + fn = &stream->connection->last_out; + + for ( ;; ) { + frame = *fn; + + if (frame == NULL) { + break; + } + + if (frame->stream == stream && !frame->blocked) { + + stream->waiting--; + r->blocked--; + + *fn = frame->next; + continue; + } + + fn = &frame->next; + } +} + + +static ngx_int_t +ngx_http_spdy_filter_init(ngx_conf_t *cf) +{ + ngx_http_next_header_filter = ngx_http_top_header_filter; + ngx_http_top_header_filter = ngx_http_spdy_header_filter; + + ngx_http_next_body_filter = ngx_http_top_body_filter; + ngx_http_top_body_filter = ngx_http_spdy_body_filter; + + return NGX_OK; +} diff -r a10cc9106dc7 -r 7271c16e52cc src/http/ngx_http_spdy_module.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/http/ngx_http_spdy_module.c Wed Mar 20 19:49:40 2013 +0400 @@ -0,0 +1,351 @@ + +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Valentin V. Bartenev + */ + + +#include +#include +#include +#include + + +static ngx_int_t ngx_http_spdy_add_variables(ngx_conf_t *cf); + +static ngx_int_t ngx_http_spdy_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_spdy_request_priority_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + +static ngx_int_t ngx_http_spdy_module_init(ngx_cycle_t *cycle); + +static void *ngx_http_spdy_create_main_conf(ngx_conf_t *cf); +static char *ngx_http_spdy_init_main_conf(ngx_conf_t *cf, void *conf); + +static void *ngx_http_spdy_create_srv_conf(ngx_conf_t *cf); +static char *ngx_http_spdy_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); + +static char *ngx_http_spdy_recv_buffer_size(ngx_conf_t *cf, void *post, + void *data); +static char *ngx_http_spdy_pool_size(ngx_conf_t *cf, void *post, void *data); +static char *ngx_http_spdy_streams_index_mask(ngx_conf_t *cf, void *post, + void *data); + + +static ngx_conf_num_bounds_t ngx_http_spdy_headers_comp_bounds = { + ngx_conf_check_num_bounds, 0, 9 +}; + +static ngx_conf_post_t ngx_http_spdy_recv_buffer_size_post = + { ngx_http_spdy_recv_buffer_size }; +static ngx_conf_post_t ngx_http_spdy_pool_size_post = + { ngx_http_spdy_pool_size }; +static ngx_conf_post_t ngx_http_spdy_streams_index_mask_post = + { ngx_http_spdy_streams_index_mask }; + + +static ngx_command_t ngx_http_spdy_commands[] = { + + { ngx_string("spdy_recv_buffer_size"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_MAIN_CONF_OFFSET, + offsetof(ngx_http_spdy_main_conf_t, recv_buffer_size), + &ngx_http_spdy_recv_buffer_size_post }, + + { ngx_string("spdy_pool_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_spdy_srv_conf_t, pool_size), + &ngx_http_spdy_pool_size_post }, + + { ngx_string("spdy_max_concurrent_streams"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_spdy_srv_conf_t, concurrent_streams), + NULL }, + + { ngx_string("spdy_streams_index_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_spdy_srv_conf_t, streams_index_mask), + &ngx_http_spdy_streams_index_mask_post }, + + { ngx_string("spdy_recv_timeout"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_spdy_srv_conf_t, recv_timeout), + NULL }, + + { ngx_string("spdy_keepalive_timeout"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_spdy_srv_conf_t, keepalive_timeout), + NULL }, + + { ngx_string("spdy_headers_comp"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_spdy_srv_conf_t, headers_comp), + &ngx_http_spdy_headers_comp_bounds }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_spdy_module_ctx = { + ngx_http_spdy_add_variables, /* preconfiguration */ + NULL, /* postconfiguration */ + + ngx_http_spdy_create_main_conf, /* create main configuration */ + ngx_http_spdy_init_main_conf, /* init main configuration */ + + ngx_http_spdy_create_srv_conf, /* create server configuration */ + ngx_http_spdy_merge_srv_conf, /* merge server configuration */ + + NULL, /* create location configuration */ + NULL /* merge location configuration */ +}; + + +ngx_module_t ngx_http_spdy_module = { + NGX_MODULE_V1, + &ngx_http_spdy_module_ctx, /* module context */ + ngx_http_spdy_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + ngx_http_spdy_module_init, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_http_variable_t ngx_http_spdy_vars[] = { + + { ngx_string("spdy"), NULL, + ngx_http_spdy_variable, 0, 0, 0 }, + + { ngx_string("spdy_request_priority"), NULL, + ngx_http_spdy_request_priority_variable, 0, 0, 0 }, + + { ngx_null_string, NULL, NULL, 0, 0, 0 } +}; + + +static ngx_int_t +ngx_http_spdy_add_variables(ngx_conf_t *cf) +{ + ngx_http_variable_t *var, *v; + + for (v = ngx_http_spdy_vars; v->name.len; v++) { + var = ngx_http_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_spdy_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->spdy_stream) { + v->len = 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) "2"; + + return NGX_OK; + } + + *v = ngx_http_variable_null_value; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_spdy_request_priority_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->spdy_stream) { + v->len = 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + v->data = ngx_pnalloc(r->pool, 1); + if (v->data == NULL) { + return NGX_ERROR; + } + + v->data[0] = '0' + (u_char) r->spdy_stream->priority; + + return NGX_OK; + } + + *v = ngx_http_variable_null_value; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_spdy_module_init(ngx_cycle_t *cycle) +{ + ngx_http_spdy_request_headers_init(); + + return NGX_OK; +} + + +static void * +ngx_http_spdy_create_main_conf(ngx_conf_t *cf) +{ + ngx_http_spdy_main_conf_t *smcf; + + smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_main_conf_t)); + if (smcf == NULL) { + return NULL; + } + + smcf->recv_buffer_size = NGX_CONF_UNSET_SIZE; + + return smcf; +} + + +static char * +ngx_http_spdy_init_main_conf(ngx_conf_t *cf, void *conf) +{ + ngx_http_spdy_main_conf_t *smcf = conf; + + if (smcf->recv_buffer_size == NGX_CONF_UNSET_SIZE) { + smcf->recv_buffer_size = 256 * 1024; + } + + return NGX_CONF_OK; +} + + +static void * +ngx_http_spdy_create_srv_conf(ngx_conf_t *cf) +{ + ngx_http_spdy_srv_conf_t *sscf; + + sscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_srv_conf_t)); + if (sscf == NULL) { + return NULL; + } + + sscf->pool_size = NGX_CONF_UNSET_SIZE; + + sscf->concurrent_streams = NGX_CONF_UNSET_UINT; + sscf->streams_index_mask = NGX_CONF_UNSET_UINT; + + sscf->recv_timeout = NGX_CONF_UNSET_MSEC; + sscf->keepalive_timeout = NGX_CONF_UNSET_MSEC; + + sscf->headers_comp = NGX_CONF_UNSET; + + return sscf; +} + + +static char * +ngx_http_spdy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_http_spdy_srv_conf_t *prev = parent; + ngx_http_spdy_srv_conf_t *conf = child; + + ngx_conf_merge_size_value(conf->pool_size, prev->pool_size, 4096); + + ngx_conf_merge_uint_value(conf->concurrent_streams, + prev->concurrent_streams, 100); + + ngx_conf_merge_uint_value(conf->streams_index_mask, + prev->streams_index_mask, 32 - 1); + + ngx_conf_merge_msec_value(conf->recv_timeout, + prev->recv_timeout, 30000); + ngx_conf_merge_msec_value(conf->keepalive_timeout, + prev->keepalive_timeout, 180000); + + ngx_conf_merge_value(conf->headers_comp, prev->headers_comp, 0); + + return NGX_CONF_OK; +} + + +static char * +ngx_http_spdy_recv_buffer_size(ngx_conf_t *cf, void *post, void *data) +{ + size_t *sp = data; + + if (*sp <= 2 * NGX_SPDY_STATE_BUFFER_SIZE) { + return "value is too small"; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_spdy_pool_size(ngx_conf_t *cf, void *post, void *data) +{ + size_t *sp = data; + + if (*sp < NGX_MIN_POOL_SIZE) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the pool size must be no less than %uz", + NGX_MIN_POOL_SIZE); + return NGX_CONF_ERROR; + } + + if (*sp % NGX_POOL_ALIGNMENT) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the pool size must be a multiple of %uz", + NGX_POOL_ALIGNMENT); + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_spdy_streams_index_mask(ngx_conf_t *cf, void *post, void *data) +{ + ngx_uint_t *np = data; + + ngx_uint_t mask; + + mask = *np - 1; + + if (*np == 0 || (*np & mask)) { + return "must be a power of two"; + } + + *np = mask; + + return NGX_CONF_OK; +} diff -r a10cc9106dc7 -r 7271c16e52cc src/http/ngx_http_spdy_module.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/http/ngx_http_spdy_module.h Wed Mar 20 19:49:40 2013 +0400 @@ -0,0 +1,36 @@ + +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Valentin V. Bartenev + */ + + +#ifndef _NGX_HTTP_SPDY_MODULE_H_INCLUDED_ +#define _NGX_HTTP_SPDY_MODULE_H_INCLUDED_ + + +#include +#include +#include + + +typedef struct { + size_t recv_buffer_size; + u_char *recv_buffer; +} ngx_http_spdy_main_conf_t; + + +typedef struct { + size_t pool_size; + ngx_uint_t concurrent_streams; + ngx_uint_t streams_index_mask; + ngx_msec_t recv_timeout; + ngx_msec_t keepalive_timeout; + ngx_int_t headers_comp; +} ngx_http_spdy_srv_conf_t; + + +extern ngx_module_t ngx_http_spdy_module; + + +#endif /* _NGX_HTTP_SPDY_MODULE_H_INCLUDED_ */ diff -r a10cc9106dc7 -r 7271c16e52cc src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c Wed Mar 20 19:48:36 2013 +0400 +++ b/src/http/ngx_http_upstream.c Wed Mar 20 19:49:40 2013 +0400 @@ -440,6 +440,13 @@ ngx_http_upstream_init(ngx_http_request_ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http init upstream, client timer: %d", c->read->timer_set); +#if (NGX_HTTP_SPDY) + if (r->spdy_stream) { + ngx_http_upstream_init_request(r); + return; + } +#endif + if (c->read->timer_set) { ngx_del_timer(c->read); } @@ -1012,6 +1019,12 @@ ngx_http_upstream_check_broken_connectio return; } +#if (NGX_HTTP_SPDY) + if (r->spdy_stream) { + return; + } +#endif + #if (NGX_HAVE_KQUEUE) if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {