# HG changeset patch # User Valentin V. Bartenev # Date 1359252145 -14400 # Node ID af5e29261674cdd97b2f15371ff4ce7bf3ff17ec # Parent 13c4c155f26f772b0bc1074a05298088d6499218 OpenSSL: removed conditions those always hold true. diff -r 13c4c155f26f -r af5e29261674 src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Thu Jan 10 13:17:29 2013 +0000 +++ b/src/event/ngx_event_openssl.c Sun Jan 27 06:02:25 2013 +0400 @@ -1210,7 +1210,7 @@ ngx_ssl_send_chain(ngx_connection_t *c, size = buf->last - buf->pos; - if (!flush && buf->last < buf->end && c->ssl->buffer) { + if (!flush && buf->last < buf->end) { break; } @@ -1232,10 +1232,8 @@ ngx_ssl_send_chain(ngx_connection_t *c, break; } - if (buf->pos == buf->last) { - buf->pos = buf->start; - buf->last = buf->start; - } + buf->pos = buf->start; + buf->last = buf->start; if (in == NULL || send == limit) { break; # HG changeset patch # User Valentin V. Bartenev # Date 1359252145 -14400 # Node ID b438cea5ed5f1641fa831a369132568a9f12003e # Parent af5e29261674cdd97b2f15371ff4ce7bf3ff17ec OpenSSL: resetting flush flag after the data was written. diff -r af5e29261674 -r b438cea5ed5f src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Sun Jan 27 06:02:25 2013 +0400 +++ b/src/event/ngx_event_openssl.c Sun Jan 27 06:02:25 2013 +0400 @@ -1232,6 +1232,8 @@ ngx_ssl_send_chain(ngx_connection_t *c, break; } + flush = 0; + buf->pos = buf->start; buf->last = buf->start; # HG changeset patch # User Valentin V. Bartenev # Date 1359252145 -14400 # Node ID 24fd03b99a2a011f08311718da16d98e0e91063e # Parent b438cea5ed5f1641fa831a369132568a9f12003e OpenSSL: preservation of flush flag for buffered data. diff -r b438cea5ed5f -r 24fd03b99a2a src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Sun Jan 27 06:02:25 2013 +0400 +++ b/src/event/ngx_event_openssl.c Sun Jan 27 06:02:25 2013 +0400 @@ -1169,7 +1169,7 @@ ngx_ssl_send_chain(ngx_connection_t *c, } send = 0; - flush = (in == NULL) ? 1 : 0; + flush = (in == NULL) ? 1 : buf->flush; for ( ;; ) { @@ -1191,7 +1191,6 @@ ngx_ssl_send_chain(ngx_connection_t *c, if (send + size > limit) { size = (ssize_t) (limit - send); - flush = 1; } ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, @@ -1210,7 +1209,7 @@ ngx_ssl_send_chain(ngx_connection_t *c, size = buf->last - buf->pos; - if (!flush && buf->last < buf->end) { + if (!flush && send < limit && buf->last < buf->end) { break; } @@ -1221,8 +1220,7 @@ ngx_ssl_send_chain(ngx_connection_t *c, } if (n == NGX_AGAIN) { - c->buffered |= NGX_SSL_BUFFERED; - return in; + break; } buf->pos += n; @@ -1242,6 +1240,8 @@ ngx_ssl_send_chain(ngx_connection_t *c, } } + buf->flush = flush; + if (buf->pos < buf->last) { c->buffered |= NGX_SSL_BUFFERED; # HG changeset patch # User Valentin V. Bartenev # Date 1359252145 -14400 # Node ID 0f475b05da84f2fc07f45b9ca433438c8ba2e982 # Parent 24fd03b99a2a011f08311718da16d98e0e91063e OpenSSL: calculation of buffer size moved closer to its usage. No functional changes. diff -r 24fd03b99a2a -r 0f475b05da84 src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Sun Jan 27 06:02:25 2013 +0400 +++ b/src/event/ngx_event_openssl.c Sun Jan 27 06:02:25 2013 +0400 @@ -1207,12 +1207,12 @@ ngx_ssl_send_chain(ngx_connection_t *c, } } - size = buf->last - buf->pos; - if (!flush && send < limit && buf->last < buf->end) { break; } + size = buf->last - buf->pos; + n = ngx_ssl_write(c, buf->pos, size); if (n == NGX_ERROR) { # HG changeset patch # User Valentin V. Bartenev # Date 1359252145 -14400 # Node ID 844adbae308a240045874d12351868f010027f6e # Parent 0f475b05da84f2fc07f45b9ca433438c8ba2e982 OpenSSL: avoid calling SSL_write() with zero data size. According to documentation, calling SSL_write() with num=0 bytes to be sent results in undefined behavior. We don't currently call ngx_ssl_send_chain() with empty chain and buffer. This check handles the case of a chain with total data size that is a multiple of NGX_SSL_BUFSIZE, and with the special buffer at the end. diff -r 0f475b05da84 -r 844adbae308a src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Sun Jan 27 06:02:25 2013 +0400 +++ b/src/event/ngx_event_openssl.c Sun Jan 27 06:02:25 2013 +0400 @@ -1213,6 +1213,12 @@ ngx_ssl_send_chain(ngx_connection_t *c, size = buf->last - buf->pos; + if (size == 0) { + buf->flush = 0; + c->buffered &= ~NGX_SSL_BUFFERED; + return in; + } + n = ngx_ssl_write(c, buf->pos, size); if (n == NGX_ERROR) { # HG changeset patch # User Valentin V. Bartenev # Date 1351713336 -14400 # Node ID 6d4895af0a03e9dbf832d13892f1d588956b8039 # Parent 844adbae308a240045874d12351868f010027f6e The default server lookup moved to ngx_http_init_connection(). No functional changes. diff -r 844adbae308a -r 6d4895af0a03 src/http/ngx_http_core_module.h --- a/src/http/ngx_http_core_module.h Sun Jan 27 06:02:25 2013 +0400 +++ b/src/http/ngx_http_core_module.h Wed Oct 31 23:55:36 2012 +0400 @@ -209,6 +209,23 @@ typedef struct { typedef struct { +#if (NGX_PCRE) + ngx_http_regex_t *regex; +#endif + ngx_http_core_srv_conf_t *server; /* virtual name server conf */ + ngx_str_t name; +} ngx_http_server_name_t; + + +typedef struct { + ngx_hash_combined_t names; + + ngx_uint_t nregex; + ngx_http_server_name_t *regex; +} ngx_http_virtual_names_t; + + +struct ngx_http_addr_conf_s { /* the default server configuration for this address:port */ ngx_http_core_srv_conf_t *default_server; @@ -217,7 +234,7 @@ typedef struct { #if (NGX_HTTP_SSL) ngx_uint_t ssl; /* unsigned ssl:1; */ #endif -} ngx_http_addr_conf_t; +}; typedef struct { @@ -268,15 +285,6 @@ typedef struct { } ngx_http_conf_addr_t; -struct ngx_http_server_name_s { -#if (NGX_PCRE) - ngx_http_regex_t *regex; -#endif - ngx_http_core_srv_conf_t *server; /* virtual name server conf */ - ngx_str_t name; -}; - - typedef struct { ngx_int_t status; ngx_int_t overwrite; diff -r 844adbae308a -r 6d4895af0a03 src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Sun Jan 27 06:02:25 2013 +0400 +++ b/src/http/ngx_http_request.c Wed Oct 31 23:55:36 2012 +0400 @@ -190,8 +190,99 @@ ngx_http_header_t ngx_http_headers_in[] void ngx_http_init_connection(ngx_connection_t *c) { - ngx_event_t *rev; - ngx_http_log_ctx_t *ctx; + ngx_uint_t i; + ngx_event_t *rev; + struct sockaddr_in *sin; + ngx_http_port_t *port; + ngx_http_in_addr_t *addr; + ngx_http_log_ctx_t *ctx; + ngx_http_connection_t *hc; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; + ngx_http_in6_addr_t *addr6; +#endif + + hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t)); + if (hc == NULL) { + ngx_http_close_connection(c); + return; + } + + c->data = hc; + + /* find the server configuration for the address:port */ + + port = c->listening->servers; + + if (port->naddrs > 1) { + + /* + * there are several addresses on this port and one of them + * is an "*:port" wildcard so getsockname() in ngx_http_server_addr() + * is required to determine a server address + */ + + if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { + ngx_http_close_connection(c); + return; + } + + switch (c->local_sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) c->local_sockaddr; + + addr6 = port->addrs; + + /* the last address is "*" */ + + for (i = 0; i < port->naddrs - 1; i++) { + if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) { + break; + } + } + + hc->addr_conf = &addr6[i].conf; + + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) c->local_sockaddr; + + addr = port->addrs; + + /* the last address is "*" */ + + for (i = 0; i < port->naddrs - 1; i++) { + if (addr[i].addr == sin->sin_addr.s_addr) { + break; + } + } + + hc->addr_conf = &addr[i].conf; + + break; + } + + } else { + + switch (c->local_sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + addr6 = port->addrs; + hc->addr_conf = &addr6[0].conf; + break; +#endif + + default: /* AF_INET */ + addr = port->addrs; + hc->addr_conf = &addr[0].conf; + break; + } + } ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t)); if (ctx == NULL) { @@ -246,22 +337,13 @@ static void ngx_http_init_request(ngx_event_t *rev) { ngx_time_t *tp; - ngx_uint_t i; ngx_connection_t *c; ngx_http_request_t *r; - struct sockaddr_in *sin; - ngx_http_port_t *port; - ngx_http_in_addr_t *addr; ngx_http_log_ctx_t *ctx; - ngx_http_addr_conf_t *addr_conf; ngx_http_connection_t *hc; ngx_http_core_srv_conf_t *cscf; ngx_http_core_loc_conf_t *clcf; ngx_http_core_main_conf_t *cmcf; -#if (NGX_HAVE_INET6) - struct sockaddr_in6 *sin6; - ngx_http_in6_addr_t *addr6; -#endif #if (NGX_STAT_STUB) (void) ngx_atomic_fetch_add(ngx_stat_reading, -1); @@ -280,14 +362,6 @@ ngx_http_init_request(ngx_event_t *rev) hc = c->data; - if (hc == NULL) { - hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t)); - if (hc == NULL) { - ngx_http_close_connection(c); - return; - } - } - r = hc->request; if (r) { @@ -315,86 +389,10 @@ ngx_http_init_request(ngx_event_t *rev) c->sent = 0; r->signature = NGX_HTTP_MODULE; - /* find the server configuration for the address:port */ - - port = c->listening->servers; - r->connection = c; - if (port->naddrs > 1) { - - /* - * there are several addresses on this port and one of them - * is an "*:port" wildcard so getsockname() in ngx_http_server_addr() - * is required to determine a server address - */ - - if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { - ngx_http_close_connection(c); - return; - } - - switch (c->local_sockaddr->sa_family) { - -#if (NGX_HAVE_INET6) - case AF_INET6: - sin6 = (struct sockaddr_in6 *) c->local_sockaddr; - - addr6 = port->addrs; - - /* the last address is "*" */ - - for (i = 0; i < port->naddrs - 1; i++) { - if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) { - break; - } - } - - addr_conf = &addr6[i].conf; - - break; -#endif - - default: /* AF_INET */ - sin = (struct sockaddr_in *) c->local_sockaddr; - - addr = port->addrs; - - /* the last address is "*" */ - - for (i = 0; i < port->naddrs - 1; i++) { - if (addr[i].addr == sin->sin_addr.s_addr) { - break; - } - } - - addr_conf = &addr[i].conf; - - break; - } - - } else { - - switch (c->local_sockaddr->sa_family) { - -#if (NGX_HAVE_INET6) - case AF_INET6: - addr6 = port->addrs; - addr_conf = &addr6[0].conf; - break; -#endif - - default: /* AF_INET */ - addr = port->addrs; - addr_conf = &addr[0].conf; - break; - } - } - - r->virtual_names = addr_conf->virtual_names; - /* the default server configuration for the address:port */ - cscf = addr_conf->default_server; + cscf = hc->addr_conf->default_server; r->main_conf = cscf->ctx->main_conf; r->srv_conf = cscf->ctx->srv_conf; @@ -409,13 +407,13 @@ ngx_http_init_request(ngx_event_t *rev) ngx_http_ssl_srv_conf_t *sscf; sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module); - if (sscf->enable || addr_conf->ssl) { + if (sscf->enable || hc->addr_conf->ssl) { if (c->ssl == NULL) { c->log->action = "SSL handshaking"; - if (addr_conf->ssl && sscf->ssl.ctx == NULL) { + if (hc->addr_conf->ssl && sscf->ssl.ctx == NULL) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "no \"ssl_certificate\" is defined " "in server listening on SSL port"); @@ -1792,12 +1790,15 @@ ngx_http_find_virtual_server(ngx_http_re { ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; - - if (r->virtual_names == NULL) { + ngx_http_virtual_names_t *virtual_names; + + virtual_names = r->http_connection->addr_conf->virtual_names; + + if (virtual_names == NULL) { return NGX_DECLINED; } - cscf = ngx_hash_find_combined(&r->virtual_names->names, + cscf = ngx_hash_find_combined(&virtual_names->names, ngx_hash_key(host, len), host, len); if (cscf) { @@ -1806,7 +1807,7 @@ ngx_http_find_virtual_server(ngx_http_re #if (NGX_PCRE) - if (len && r->virtual_names->nregex) { + if (len && virtual_names->nregex) { ngx_int_t n; ngx_uint_t i; ngx_str_t name; @@ -1815,9 +1816,9 @@ ngx_http_find_virtual_server(ngx_http_re name.len = len; name.data = host; - sn = r->virtual_names->regex; - - for (i = 0; i < r->virtual_names->nregex; i++) { + sn = virtual_names->regex; + + for (i = 0; i < virtual_names->nregex; i++) { n = ngx_http_regex_exec(r, sn[i].regex, &name); diff -r 844adbae308a -r 6d4895af0a03 src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h Sun Jan 27 06:02:25 2013 +0400 +++ b/src/http/ngx_http_request.h Wed Oct 31 23:55:36 2012 +0400 @@ -284,7 +284,11 @@ typedef struct { } ngx_http_request_body_t; +typedef struct ngx_http_addr_conf_s ngx_http_addr_conf_t; + typedef struct { + ngx_http_addr_conf_t *addr_conf; + ngx_http_request_t *request; ngx_buf_t **busy; @@ -297,17 +301,6 @@ typedef struct { } ngx_http_connection_t; -typedef struct ngx_http_server_name_s ngx_http_server_name_t; - - -typedef struct { - ngx_hash_combined_t names; - - ngx_uint_t nregex; - ngx_http_server_name_t *regex; -} ngx_http_virtual_names_t; - - typedef void (*ngx_http_cleanup_pt)(void *data); typedef struct ngx_http_cleanup_s ngx_http_cleanup_t; @@ -401,8 +394,6 @@ struct ngx_http_request_s { ngx_http_post_subrequest_t *post_subrequest; ngx_http_posted_request_t *posted_requests; - ngx_http_virtual_names_t *virtual_names; - ngx_int_t phase_handler; ngx_http_handler_pt content_handler; ngx_uint_t access_code; # HG changeset patch # User Valentin V. Bartenev # Date 1359252146 -14400 # Node ID 79d3a9317f5f11cfc338b6bdc819b98fb10fa105 # Parent 6d4895af0a03e9dbf832d13892f1d588956b8039 imported patch set_log diff -r 6d4895af0a03 -r 79d3a9317f5f src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c Wed Oct 31 23:55:36 2012 +0400 +++ b/src/http/ngx_http_core_module.c Sun Jan 27 06:02:26 2013 +0400 @@ -1458,11 +1458,7 @@ ngx_http_update_location_config(ngx_http } if (r == r->main) { - r->connection->log->file = clcf->error_log->file; - - if (!(r->connection->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { - r->connection->log->log_level = clcf->error_log->log_level; - } + ngx_http_set_log(r->connection, clcf->error_log); } if ((ngx_io.flags & NGX_IO_SENDFILE) && clcf->sendfile) { diff -r 6d4895af0a03 -r 79d3a9317f5f src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Wed Oct 31 23:55:36 2012 +0400 +++ b/src/http/ngx_http_request.c Sun Jan 27 06:02:26 2013 +0400 @@ -438,10 +438,8 @@ ngx_http_init_request(ngx_event_t *rev) #endif clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - c->log->file = clcf->error_log->file; - if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { - c->log->log_level = clcf->error_log->log_level; - } + + ngx_http_set_log(r->connection, clcf->error_log); if (c->buffer == NULL) { c->buffer = ngx_create_temp_buf(c->pool, @@ -1845,11 +1843,8 @@ found: r->loc_conf = cscf->ctx->loc_conf; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - r->connection->log->file = clcf->error_log->file; - - if (!(r->connection->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { - r->connection->log->log_level = clcf->error_log->log_level; - } + + ngx_http_set_log(r->connection, clcf->error_log); return NGX_OK; } diff -r 6d4895af0a03 -r 79d3a9317f5f src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h Wed Oct 31 23:55:36 2012 +0400 +++ b/src/http/ngx_http_request.h Sun Jan 27 06:02:26 2013 +0400 @@ -566,4 +566,11 @@ extern ngx_http_header_t ngx_http_ extern ngx_http_header_out_t ngx_http_headers_out[]; +#define ngx_http_set_log(c, l) \ + c->log->file = l->file; \ + if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { \ + c->log->log_level = l->log_level; \ + } + + #endif /* _NGX_HTTP_REQUEST_H_INCLUDED_ */ # HG changeset patch # User Valentin V. Bartenev # Date 1351864093 -14400 # Node ID a63744b685e8cd210d13d5f7905042567000d846 # Parent 79d3a9317f5f11cfc338b6bdc819b98fb10fa105 Do not process regex captures while looking for SNI server. This change helps to decouple ngx_http_ssl_servername() from the request object. Since the new code is similar to ngx_http_find_virtual_server(), the whole function was moved closer to it. We also simplified SNI hostname validation by replacing ngx_http_validate_host() to ngx_strlow(). This is made possible by elimination of user access to the data through captures, as well as we don't care about port presence according to RFC 6066. diff -r 79d3a9317f5f -r a63744b685e8 src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Sun Jan 27 06:02:26 2013 +0400 +++ b/src/http/ngx_http_request.c Fri Nov 02 17:48:13 2012 +0400 @@ -33,7 +33,7 @@ static ngx_int_t ngx_http_process_cookie 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 ssize_t ngx_http_validate_host(ngx_http_request_t *r, u_char **host, - size_t len, ngx_uint_t alloc); + size_t len); static ngx_int_t ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, size_t len); @@ -633,78 +633,6 @@ ngx_http_ssl_handshake_handler(ngx_conne return; } -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - -int -ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg) -{ - size_t len; - u_char *host; - const char *servername; - ngx_connection_t *c; - ngx_http_request_t *r; - ngx_http_ssl_srv_conf_t *sscf; - - servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name); - - if (servername == NULL) { - return SSL_TLSEXT_ERR_NOACK; - } - - c = ngx_ssl_get_connection(ssl_conn); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "SSL server name: \"%s\"", servername); - - len = ngx_strlen(servername); - - if (len == 0) { - return SSL_TLSEXT_ERR_NOACK; - } - - r = c->data; - - host = (u_char *) servername; - - len = ngx_http_validate_host(r, &host, len, 1); - - if (len <= 0) { - return SSL_TLSEXT_ERR_NOACK; - } - - if (ngx_http_find_virtual_server(r, host, len) != NGX_OK) { - return SSL_TLSEXT_ERR_NOACK; - } - - sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module); - - if (sscf->ssl.ctx) { - SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx); - - /* - * SSL_set_SSL_CTX() only changes certs as of 1.0.0d - * adjust other things we care about - */ - - SSL_set_verify(ssl_conn, SSL_CTX_get_verify_mode(sscf->ssl.ctx), - SSL_CTX_get_verify_callback(sscf->ssl.ctx)); - - SSL_set_verify_depth(ssl_conn, SSL_CTX_get_verify_depth(sscf->ssl.ctx)); - -#ifdef SSL_CTRL_CLEAR_OPTIONS - /* only in 0.9.8m+ */ - SSL_clear_options(ssl_conn, SSL_get_options(ssl_conn) & - ~SSL_CTX_get_options(sscf->ssl.ctx)); -#endif - - SSL_set_options(ssl_conn, SSL_CTX_get_options(sscf->ssl.ctx)); - } - - return SSL_TLSEXT_ERR_OK; -} - -#endif - #endif @@ -881,7 +809,7 @@ ngx_http_process_request_line(ngx_event_ host = r->host_start; n = ngx_http_validate_host(r, &host, - r->host_end - r->host_start, 0); + r->host_end - r->host_start); if (n == 0) { ngx_log_error(NGX_LOG_INFO, c->log, 0, @@ -1413,7 +1341,7 @@ ngx_http_process_host(ngx_http_request_t } host = h->value.data; - len = ngx_http_validate_host(r, &host, h->value.len, 0); + len = ngx_http_validate_host(r, &host, h->value.len); if (len == 0) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, @@ -1698,11 +1626,11 @@ ngx_http_process_request(ngx_http_reques static ssize_t -ngx_http_validate_host(ngx_http_request_t *r, u_char **host, size_t len, - ngx_uint_t alloc) +ngx_http_validate_host(ngx_http_request_t *r, u_char **host, size_t len) { - u_char *h, ch; - size_t i, dot_pos, host_len; + u_char *h, ch; + size_t i, dot_pos, host_len; + ngx_uint_t alloc; enum { sw_usual = 0, @@ -1715,6 +1643,7 @@ ngx_http_validate_host(ngx_http_request_ h = *host; + alloc = 0; state = sw_usual; for (i = 0; i < len; i++) { @@ -1850,6 +1779,137 @@ found: } +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + +int +ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg) +{ + size_t len; + u_char *host; + const char *servername; + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_ssl_srv_conf_t *sscf; + ngx_http_core_loc_conf_t *clcf; + ngx_http_core_srv_conf_t *cscf; + ngx_http_virtual_names_t *virtual_names; + + servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name); + + if (servername == NULL) { + return SSL_TLSEXT_ERR_NOACK; + } + + c = ngx_ssl_get_connection(ssl_conn); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "SSL server name: \"%s\"", servername); + + len = ngx_strlen(servername); + + if (len == 0) { + return SSL_TLSEXT_ERR_NOACK; + } + + host = ngx_pnalloc(c->pool, len); + if (host == NULL) { + return SSL_TLSEXT_ERR_NOACK; + } + + ngx_strlow(host, (u_char *) servername, len); + + r = c->data; + + virtual_names = r->http_connection->addr_conf->virtual_names; + + if (virtual_names == NULL) { + return SSL_TLSEXT_ERR_NOACK; + } + + cscf = ngx_hash_find_combined(&virtual_names->names, + ngx_hash_key(host, len), host, len); + + if (cscf) { + goto found; + } + +#if (NGX_PCRE) + + if (len && virtual_names->nregex) { + ngx_int_t n; + ngx_uint_t i; + ngx_str_t name; + ngx_http_server_name_t *sn; + + name.len = len; + name.data = host; + + sn = virtual_names->regex; + + for (i = 0; i < virtual_names->nregex; i++) { + + n = ngx_regex_exec(sn[i].regex->regex, &name, NULL, 0); + + if (n == NGX_REGEX_NO_MATCHED) { + continue; + } + + if (n < 0) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, ngx_regex_exec_n + " failed: %i on \"%V\" using \"%V\"", + n, &name, &sn[i].regex->name); + return SSL_TLSEXT_ERR_NOACK; + } + + cscf = sn[i].server; + + goto found; + } + } + +#endif + + return SSL_TLSEXT_ERR_NOACK; + +found: + + r->srv_conf = cscf->ctx->srv_conf; + r->loc_conf = cscf->ctx->loc_conf; + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + ngx_http_set_log(c, clcf->error_log); + + sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module); + + if (sscf->ssl.ctx) { + SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx); + + /* + * SSL_set_SSL_CTX() only changes certs as of 1.0.0d + * adjust other things we care about + */ + + SSL_set_verify(ssl_conn, SSL_CTX_get_verify_mode(sscf->ssl.ctx), + SSL_CTX_get_verify_callback(sscf->ssl.ctx)); + + SSL_set_verify_depth(ssl_conn, SSL_CTX_get_verify_depth(sscf->ssl.ctx)); + +#ifdef SSL_CTRL_CLEAR_OPTIONS + /* only in 0.9.8m+ */ + SSL_clear_options(ssl_conn, SSL_get_options(ssl_conn) & + ~SSL_CTX_get_options(sscf->ssl.ctx)); +#endif + + SSL_set_options(ssl_conn, SSL_CTX_get_options(sscf->ssl.ctx)); + } + + return SSL_TLSEXT_ERR_OK; +} + +#endif + + static void ngx_http_request_handler(ngx_event_t *ev) { # HG changeset patch # User Valentin V. Bartenev # Date 1351866846 -14400 # Node ID b86e10b202c9685c1d084bc69687e48f445387e8 # Parent a63744b685e8cd210d13d5f7905042567000d846 Server configuration found by SNI must be used not only for the first request in connection. Now we store configuration context in the http connection object so we can use it as the default and change it in the ngx_http_ssl_servername(). This also made it possible a bit more quicker access to the configuration without the request object, so we did not make it specific to SNI. diff -r a63744b685e8 -r b86e10b202c9 src/http/ngx_http_config.h --- a/src/http/ngx_http_config.h Fri Nov 02 17:48:13 2012 +0400 +++ b/src/http/ngx_http_config.h Fri Nov 02 18:34:06 2012 +0400 @@ -14,11 +14,11 @@ #include -typedef struct { +struct ngx_http_conf_ctx_s { void **main_conf; void **srv_conf; void **loc_conf; -} ngx_http_conf_ctx_t; +}; typedef struct { diff -r a63744b685e8 -r b86e10b202c9 src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Fri Nov 02 17:48:13 2012 +0400 +++ b/src/http/ngx_http_request.c Fri Nov 02 18:34:06 2012 +0400 @@ -284,6 +284,8 @@ ngx_http_init_connection(ngx_connection_ } } + hc->ctx = hc->addr_conf->default_server->ctx; + ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t)); if (ctx == NULL) { ngx_http_close_connection(c); @@ -391,12 +393,9 @@ ngx_http_init_request(ngx_event_t *rev) r->connection = c; - /* the default server configuration for the address:port */ - cscf = hc->addr_conf->default_server; - - r->main_conf = cscf->ctx->main_conf; - r->srv_conf = cscf->ctx->srv_conf; - r->loc_conf = cscf->ctx->loc_conf; + r->main_conf = hc->ctx->main_conf; + r->srv_conf = hc->ctx->srv_conf; + r->loc_conf = hc->ctx->loc_conf; rev->handler = ngx_http_process_request_line; r->read_event_handler = ngx_http_block_reading; @@ -441,6 +440,8 @@ ngx_http_init_request(ngx_event_t *rev) ngx_http_set_log(r->connection, clcf->error_log); + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + if (c->buffer == NULL) { c->buffer = ngx_create_temp_buf(c->pool, cscf->client_header_buffer_size); @@ -1873,6 +1874,8 @@ ngx_http_ssl_servername(ngx_ssl_conn_t * found: + r->http_connection->ctx = cscf->ctx; + r->srv_conf = cscf->ctx->srv_conf; r->loc_conf = cscf->ctx->loc_conf; diff -r a63744b685e8 -r b86e10b202c9 src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h Fri Nov 02 17:48:13 2012 +0400 +++ b/src/http/ngx_http_request.h Fri Nov 02 18:34:06 2012 +0400 @@ -286,8 +286,11 @@ typedef struct { typedef struct ngx_http_addr_conf_s ngx_http_addr_conf_t; +typedef struct ngx_http_conf_ctx_s ngx_http_conf_ctx_t; + typedef struct { ngx_http_addr_conf_t *addr_conf; + ngx_http_conf_ctx_t *ctx; ngx_http_request_t *request; # HG changeset patch # User Valentin V. Bartenev # Date 1359252146 -14400 # Node ID d9704385dde4bc2104cd0889010503087a74f883 # Parent b86e10b202c9685c1d084bc69687e48f445387e8 imported patch stat diff -r b86e10b202c9 -r d9704385dde4 src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Fri Nov 02 18:34:06 2012 +0400 +++ b/src/http/ngx_http_request.c Sun Jan 27 06:02:26 2013 +0400 @@ -307,10 +307,6 @@ ngx_http_init_connection(ngx_connection_ rev->handler = ngx_http_init_request; c->write->handler = ngx_http_empty_handler; -#if (NGX_STAT_STUB) - (void) ngx_atomic_fetch_add(ngx_stat_reading, 1); -#endif - if (rev->ready) { /* the deferred accept(), rtsig, aio, iocp */ @@ -326,9 +322,6 @@ ngx_http_init_connection(ngx_connection_ ngx_add_timer(rev, c->listening->post_accept_timeout); if (ngx_handle_read_event(rev, 0) != NGX_OK) { -#if (NGX_STAT_STUB) - (void) ngx_atomic_fetch_add(ngx_stat_reading, -1); -#endif ngx_http_close_connection(c); return; } @@ -347,10 +340,6 @@ ngx_http_init_request(ngx_event_t *rev) ngx_http_core_loc_conf_t *clcf; ngx_http_core_main_conf_t *cmcf; -#if (NGX_STAT_STUB) - (void) ngx_atomic_fetch_add(ngx_stat_reading, -1); -#endif - c = rev->data; if (rev->timedout) { @@ -2603,10 +2592,6 @@ ngx_http_set_keepalive(ngx_http_request_ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "pipelined request"); -#if (NGX_STAT_STUB) - (void) ngx_atomic_fetch_add(ngx_stat_reading, 1); -#endif - hc->pipeline = 1; c->log->action = "reading client pipelined request line"; @@ -2848,10 +2833,6 @@ ngx_http_keepalive_handler(ngx_event_t * b->last += n; -#if (NGX_STAT_STUB) - (void) ngx_atomic_fetch_add(ngx_stat_reading, 1); -#endif - c->log->handler = ngx_http_log_error; c->log->action = "reading client request line"; # HG changeset patch # User Valentin V. Bartenev # Date 1351880578 -14400 # Node ID d3ad9832575ed08a51d7d069818d0a4883cd3fdb # Parent d9704385dde4bc2104cd0889010503087a74f883 Do not create the request object until the TLS/SSL handshake is completed. This will simplify adding another connection handler which does not need the request object (e.g. SPDY) after the TLS/SSL handshake. One more noticeable improvement is using "client_header_buffer_size" configuration from the server selected by SNI, not the default one. The two additional changes were made to simplify the code: we do not log the TLS/SSL connection errors into the access log, and do not count idle connection and TLS/SSL handshake as the reading request state. Apart from that we do not call ngx_ssl_create_connection() until we got the first byte of the ClientHello message. This also decrease memory consumption in case of plain HTTP request to TLS/SSL socket. diff -r d9704385dde4 -r d3ad9832575e src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Sun Jan 27 06:02:26 2013 +0400 +++ b/src/http/ngx_http_request.c Fri Nov 02 22:22:58 2012 +0400 @@ -307,6 +307,29 @@ ngx_http_init_connection(ngx_connection_ rev->handler = ngx_http_init_request; c->write->handler = ngx_http_empty_handler; +#if (NGX_HTTP_SSL) + { + ngx_http_ssl_srv_conf_t *sscf; + + sscf = ngx_http_get_module_srv_conf(hc->ctx, ngx_http_ssl_module); + + if (sscf->enable || hc->addr_conf->ssl) { + + c->log->action = "SSL handshaking"; + + if (hc->addr_conf->ssl && sscf->ssl.ctx == NULL) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "no \"ssl_certificate\" is defined " + "in server listening on SSL port"); + ngx_http_close_connection(c); + return; + } + + rev->handler = ngx_http_ssl_handshake; + } + } +#endif + if (rev->ready) { /* the deferred accept(), rtsig, aio, iocp */ @@ -315,7 +338,7 @@ ngx_http_init_connection(ngx_connection_ return; } - ngx_http_init_request(rev); + rev->handler(rev); return; } @@ -386,45 +409,8 @@ ngx_http_init_request(ngx_event_t *rev) r->srv_conf = hc->ctx->srv_conf; r->loc_conf = hc->ctx->loc_conf; - rev->handler = ngx_http_process_request_line; r->read_event_handler = ngx_http_block_reading; -#if (NGX_HTTP_SSL) - - { - ngx_http_ssl_srv_conf_t *sscf; - - sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module); - if (sscf->enable || hc->addr_conf->ssl) { - - if (c->ssl == NULL) { - - c->log->action = "SSL handshaking"; - - if (hc->addr_conf->ssl && sscf->ssl.ctx == NULL) { - ngx_log_error(NGX_LOG_ERR, c->log, 0, - "no \"ssl_certificate\" is defined " - "in server listening on SSL port"); - ngx_http_close_connection(c); - return; - } - - if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER) - != NGX_OK) - { - ngx_http_close_connection(c); - return; - } - - rev->handler = ngx_http_ssl_handshake; - } - - r->main_filter_need_in_memory = 1; - } - } - -#endif - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ngx_http_set_log(r->connection, clcf->error_log); @@ -480,6 +466,21 @@ ngx_http_init_request(ngx_event_t *rev) c->single_connection = 1; c->destroyed = 0; +#if (NGX_HTTP_SSL) + { + ngx_http_ssl_srv_conf_t *sscf; + + if (c->ssl) { + r->main_filter_need_in_memory = 1; + + } else { + sscf = ngx_http_get_module_srv_conf(hc->ctx, ngx_http_ssl_module); + + r->plain_http = sscf->enable || hc->addr_conf->ssl; + } + } +#endif + r->main = r; r->count = 1; @@ -510,7 +511,8 @@ ngx_http_init_request(ngx_event_t *rev) (void) ngx_atomic_fetch_add(ngx_stat_requests, 1); #endif - rev->handler(rev); + rev->handler = ngx_http_process_request_line; + ngx_http_process_request_line(rev); } @@ -519,81 +521,93 @@ ngx_http_init_request(ngx_event_t *rev) static void ngx_http_ssl_handshake(ngx_event_t *rev) { - u_char buf[1]; - ssize_t n; - ngx_int_t rc; - ngx_connection_t *c; - ngx_http_request_t *r; + u_char buf[1]; + ssize_t n; + ngx_int_t rc; + ngx_connection_t *c; + ngx_http_connection_t *hc; + ngx_http_ssl_srv_conf_t *sscf; c = rev->data; - r = c->data; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http check ssl handshake"); if (rev->timedout) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); - c->timedout = 1; - ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT); + ngx_http_close_connection(c); return; } n = recv(c->fd, (char *) buf, 1, MSG_PEEK); - if (n == -1 && ngx_socket_errno == NGX_EAGAIN) { - - if (!rev->timer_set) { - ngx_add_timer(rev, c->listening->post_accept_timeout); + if (n == -1) { + if (ngx_socket_errno == NGX_EAGAIN) { + + 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; } - if (ngx_handle_read_event(rev, 0) != NGX_OK) { - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - } + ngx_connection_error(c, ngx_socket_errno, "recv() failed"); + ngx_http_close_connection(c); return; } - if (n == 1) { - if (buf[0] & 0x80 /* SSLv2 */ || buf[0] == 0x16 /* SSLv3/TLSv1 */) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "https ssl handshake: 0x%02Xd", buf[0]); - - rc = ngx_ssl_handshake(c); - - if (rc == NGX_AGAIN) { - - if (!rev->timer_set) { - ngx_add_timer(rev, c->listening->post_accept_timeout); - } - - c->ssl->handler = ngx_http_ssl_handshake_handler; - return; + if (n == 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "client closed connection"); + ngx_http_close_connection(c); + return; + } + + if (buf[0] & 0x80 /* SSLv2 */ || buf[0] == 0x16 /* SSLv3/TLSv1 */) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "https ssl handshake: 0x%02Xd", buf[0]); + + hc = c->data; + sscf = ngx_http_get_module_srv_conf(hc->ctx, ngx_http_ssl_module); + + if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER) + != NGX_OK) + { + ngx_http_close_connection(c); + return; + } + + rc = ngx_ssl_handshake(c); + + if (rc == NGX_AGAIN) { + + if (!rev->timer_set) { + ngx_add_timer(rev, c->listening->post_accept_timeout); } - ngx_http_ssl_handshake_handler(c); - + c->ssl->handler = ngx_http_ssl_handshake_handler; return; - - } else { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "plain http"); - - r->plain_http = 1; } + + ngx_http_ssl_handshake_handler(c); + + return; } c->log->action = "reading client request line"; - rev->handler = ngx_http_process_request_line; - ngx_http_process_request_line(rev); + rev->handler = ngx_http_init_request; + ngx_http_init_request(rev); } static void ngx_http_ssl_handshake_handler(ngx_connection_t *c) { - ngx_http_request_t *r; - if (c->ssl->handshaked) { /* @@ -608,17 +622,19 @@ ngx_http_ssl_handshake_handler(ngx_conne c->log->action = "reading client request line"; - c->read->handler = ngx_http_process_request_line; + c->read->handler = ngx_http_init_request; /* STUB: epoll edge */ c->write->handler = ngx_http_empty_handler; - ngx_http_process_request_line(c->read); + ngx_http_init_request(c->read); return; } - r = c->data; - - ngx_http_close_request(r, NGX_HTTP_BAD_REQUEST); + if (c->read->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + } + + ngx_http_close_connection(c); return; } @@ -1778,7 +1794,7 @@ ngx_http_ssl_servername(ngx_ssl_conn_t * u_char *host; const char *servername; ngx_connection_t *c; - ngx_http_request_t *r; + ngx_http_connection_t *hc; ngx_http_ssl_srv_conf_t *sscf; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; @@ -1808,9 +1824,9 @@ ngx_http_ssl_servername(ngx_ssl_conn_t * ngx_strlow(host, (u_char *) servername, len); - r = c->data; - - virtual_names = r->http_connection->addr_conf->virtual_names; + hc = c->data; + + virtual_names = hc->addr_conf->virtual_names; if (virtual_names == NULL) { return SSL_TLSEXT_ERR_NOACK; @@ -1863,16 +1879,13 @@ ngx_http_ssl_servername(ngx_ssl_conn_t * found: - r->http_connection->ctx = cscf->ctx; - - r->srv_conf = cscf->ctx->srv_conf; - r->loc_conf = cscf->ctx->loc_conf; - - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + hc->ctx = cscf->ctx; + + clcf = ngx_http_get_module_loc_conf(hc->ctx, ngx_http_core_module); ngx_http_set_log(c, clcf->error_log); - sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module); + sscf = ngx_http_get_module_srv_conf(hc->ctx, ngx_http_ssl_module); if (sscf->ssl.ctx) { SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx); # HG changeset patch # User Valentin V. Bartenev # Date 1351880664 -14400 # Node ID 13a27a477f3f6484f8ccb269d729dde306f3fc9c # Parent d3ad9832575ed08a51d7d069818d0a4883cd3fdb Applying the new server configuration as soon as the host header was parsed. This allows to use virtual server specific values of "underscores_in_headers" and "large_client_header_buffers" for the HTTP requests and the HTTPS requests without SNI. diff -r d3ad9832575e -r 13a27a477f3f src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Fri Nov 02 22:22:58 2012 +0400 +++ b/src/http/ngx_http_request.c Fri Nov 02 22:24:24 2012 +0400 @@ -829,6 +829,11 @@ ngx_http_process_request_line(ngx_event_ return; } + if (ngx_http_find_virtual_server(r, host, n) == NGX_ERROR) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + r->headers_in.server.len = n; r->headers_in.server.data = host; } @@ -936,7 +941,6 @@ ngx_http_process_request_headers(ngx_eve } cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); - cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); rc = NGX_AGAIN; @@ -990,6 +994,9 @@ ngx_http_process_request_headers(ngx_eve } } + /* the host header could change the server configuration context */ + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + rc = ngx_http_parse_header_line(r, r->header_in, cscf->underscores_in_headers); @@ -1365,6 +1372,11 @@ ngx_http_process_host(ngx_http_request_t return NGX_OK; } + if (ngx_http_find_virtual_server(r, host, len) == NGX_ERROR) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + r->headers_in.server.len = len; r->headers_in.server.data = host; @@ -1481,12 +1493,11 @@ ngx_http_process_cookie(ngx_http_request static ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r) { - if (ngx_http_find_virtual_server(r, r->headers_in.server.data, - r->headers_in.server.len) - == NGX_ERROR) - { - ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return NGX_ERROR; + if (r->headers_in.server.len == 0) { + if (ngx_http_find_virtual_server(r, NULL, 0) == NGX_ERROR) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } } if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10) { # HG changeset patch # User Valentin V. Bartenev # Date 1359254212 -14400 # Node ID 2b0eb170257b9f6577c02029cb33f42031af4a47 # Parent 13a27a477f3f6484f8ccb269d729dde306f3fc9c [mq]: restriction_new diff -r 13a27a477f3f -r 2b0eb170257b src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Fri Nov 02 22:24:24 2012 +0400 +++ b/src/http/ngx_http_request.c Sun Jan 27 06:36:52 2013 +0400 @@ -830,7 +830,6 @@ ngx_http_process_request_line(ngx_event_ } if (ngx_http_find_virtual_server(r, host, n) == NGX_ERROR) { - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } @@ -840,11 +839,7 @@ ngx_http_process_request_line(ngx_event_ if (r->http_version < NGX_HTTP_VERSION_10) { - if (ngx_http_find_virtual_server(r, r->headers_in.server.data, - r->headers_in.server.len) - == NGX_ERROR) - { - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + if (ngx_http_find_virtual_server(r, NULL, 0) == NGX_ERROR) { return; } @@ -1373,7 +1368,6 @@ ngx_http_process_host(ngx_http_request_t } if (ngx_http_find_virtual_server(r, host, len) == NGX_ERROR) { - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_ERROR; } @@ -1495,7 +1489,6 @@ ngx_http_process_request_header(ngx_http { if (r->headers_in.server.len == 0) { if (ngx_http_find_virtual_server(r, NULL, 0) == NGX_ERROR) { - ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_ERROR; } } @@ -1732,16 +1725,29 @@ ngx_http_validate_host(ngx_http_request_ static ngx_int_t ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, size_t len) { + ngx_http_connection_t *hc; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; ngx_http_virtual_names_t *virtual_names; - - virtual_names = r->http_connection->addr_conf->virtual_names; +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + ngx_uint_t sni_mismatched; + ngx_http_ssl_srv_conf_t *sscf; +#endif + + hc = r->http_connection; + + virtual_names = hc->addr_conf->virtual_names; if (virtual_names == NULL) { return NGX_DECLINED; } +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + sni_mismatched = (hc->sni_host + && (hc->sni_host->len != len + || ngx_strncmp(hc->sni_host->data, host, len) != 0)); +#endif + cscf = ngx_hash_find_combined(&virtual_names->names, ngx_hash_key(host, len), host, len); @@ -1775,12 +1781,20 @@ ngx_http_find_virtual_server(ngx_http_re continue; } + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_ERROR; } } #endif +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + if (sni_mismatched) { + cscf = hc->addr_conf->default_server; + goto found; + } +#endif + return NGX_DECLINED; found: @@ -1792,6 +1806,20 @@ found: ngx_http_set_log(r->connection, clcf->error_log); +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + if (sni_mismatched) { + sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module); + + if (sscf->verify) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client has attempted to request the server name " + "different from that one was negotiated"); + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; + } + } +#endif + return NGX_OK; } @@ -1892,6 +1920,14 @@ found: hc->ctx = cscf->ctx; + hc->sni_host = ngx_palloc(c->pool, sizeof(ngx_str_t)); + if (hc->sni_host == NULL) { + return SSL_TLSEXT_ERR_NOACK; + } + + hc->sni_host->len = len; + hc->sni_host->data = host; + clcf = ngx_http_get_module_loc_conf(hc->ctx, ngx_http_core_module); ngx_http_set_log(c, clcf->error_log); diff -r 13a27a477f3f -r 2b0eb170257b src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h Fri Nov 02 22:24:24 2012 +0400 +++ b/src/http/ngx_http_request.h Sun Jan 27 06:36:52 2013 +0400 @@ -292,6 +292,10 @@ typedef struct { ngx_http_addr_conf_t *addr_conf; ngx_http_conf_ctx_t *ctx; +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + ngx_str_t *sni_host; +#endif + ngx_http_request_t *request; ngx_buf_t **busy; # HG changeset patch # User Valentin V. Bartenev # Date 1351882955 -14400 # Node ID 92af4dbf4a04e998bd23745478805ee1f16d0b57 # Parent 2b0eb170257b9f6577c02029cb33f42031af4a47 Allocate the request object from its pool. No functional changes. diff -r 2b0eb170257b -r 92af4dbf4a04 src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Sun Jan 27 06:36:52 2013 +0400 +++ b/src/http/ngx_http_request.c Fri Nov 02 23:02:35 2012 +0400 @@ -354,6 +354,7 @@ ngx_http_init_connection(ngx_connection_ static void ngx_http_init_request(ngx_event_t *rev) { + ngx_pool_t *pool; ngx_time_t *tp; ngx_connection_t *c; ngx_http_request_t *r; @@ -376,27 +377,25 @@ ngx_http_init_request(ngx_event_t *rev) hc = c->data; - r = hc->request; - - if (r) { - ngx_memzero(r, sizeof(ngx_http_request_t)); - - r->pipeline = hc->pipeline; - - if (hc->nbusy) { - r->header_in = hc->busy[0]; - } - - } else { - r = ngx_pcalloc(c->pool, sizeof(ngx_http_request_t)); - if (r == NULL) { - ngx_http_close_connection(c); - return; - } - - hc->request = r; + cscf = ngx_http_get_module_srv_conf(hc->ctx, ngx_http_core_module); + + pool = ngx_create_pool(cscf->request_pool_size, c->log); + if (pool == NULL) { + ngx_http_close_connection(c); + return; } + r = ngx_pcalloc(pool, sizeof(ngx_http_request_t)); + if (r == NULL) { + ngx_destroy_pool(pool); + ngx_http_close_connection(c); + return; + } + + r->pool = pool; + + r->pipeline = hc->pipeline; + c->data = r; r->http_connection = hc; @@ -415,27 +414,17 @@ ngx_http_init_request(ngx_event_t *rev) ngx_http_set_log(r->connection, clcf->error_log); - cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); - 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; } } - if (r->header_in == NULL) { - r->header_in = c->buffer; - } - - r->pool = ngx_create_pool(cscf->request_pool_size, c->log); - if (r->pool == NULL) { - 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, sizeof(ngx_table_elt_t)) @@ -2664,16 +2653,12 @@ ngx_http_set_keepalive(ngx_http_request_ /* * To keep a memory footprint as small as possible for an idle - * keepalive connection we try to free the ngx_http_request_t and - * c->buffer's memory if they were allocated outside the c->pool. + * keepalive connection we try to free c->buffer's memory if it + * was allocated outside the c->pool. * The large header buffers are always allocated outside the c->pool and * are freed too. */ - if (ngx_pfree(c->pool, r) == NGX_OK) { - hc->request = NULL; - } - b = c->buffer; if (ngx_pfree(c->pool, b->start) == NGX_OK) { @@ -3123,6 +3108,7 @@ static void ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc) { ngx_log_t *log; + ngx_pool_t *pool; struct linger linger; ngx_http_cleanup_t *cln; ngx_http_log_ctx_t *ctx; @@ -3189,7 +3175,9 @@ ngx_http_free_request(ngx_http_request_t r->connection->destroyed = 1; - ngx_destroy_pool(r->pool); + pool = r->pool; + r->pool = NULL; + ngx_destroy_pool(pool); } diff -r 2b0eb170257b -r 92af4dbf4a04 src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h Sun Jan 27 06:36:52 2013 +0400 +++ b/src/http/ngx_http_request.h Fri Nov 02 23:02:35 2012 +0400 @@ -296,8 +296,6 @@ typedef struct { ngx_str_t *sni_host; #endif - ngx_http_request_t *request; - ngx_buf_t **busy; ngx_int_t nbusy; # HG changeset patch # User Valentin V. Bartenev # Date 1351882955 -14400 # Node ID b5d8fbef90291853e331a58334028e7bbacc541a # Parent 92af4dbf4a04e998bd23745478805ee1f16d0b57 Do not create the request object until the first byte of request is received. Previously, we create the request object and log the 400 "Bad request" error in access log if the client closed the connection without passing a single byte of the request. Since it's a common behavior of modern browsers (like Chrome), it would be more reasonable to consider such connections as idle and do not process them as a bad request. diff -r 92af4dbf4a04 -r b5d8fbef9029 src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Fri Nov 02 23:02:35 2012 +0400 +++ b/src/http/ngx_http_request.c Fri Nov 02 23:02:35 2012 +0400 @@ -299,12 +299,20 @@ 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 request"; c->log_error = NGX_ERROR_INFO; + c->buffer = ngx_calloc_buf(c->pool); + if (c->buffer == NULL) { + ngx_http_close_connection(c); + return; + } + + c->buffer->temporary = 1; + rev = c->read; - rev->handler = ngx_http_init_request; + rev->handler = ngx_http_keepalive_handler; c->write->handler = ngx_http_empty_handler; #if (NGX_HTTP_SSL) @@ -330,6 +338,8 @@ ngx_http_init_connection(ngx_connection_ } #endif + ngx_add_timer(rev, c->listening->post_accept_timeout); + if (rev->ready) { /* the deferred accept(), rtsig, aio, iocp */ @@ -342,8 +352,6 @@ ngx_http_init_connection(ngx_connection_ return; } - ngx_add_timer(rev, c->listening->post_accept_timeout); - if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_connection(c); return; @@ -366,13 +374,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; @@ -414,16 +415,6 @@ ngx_http_init_request(ngx_event_t *rev) ngx_http_set_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, @@ -533,10 +524,6 @@ ngx_http_ssl_handshake(ngx_event_t *rev) if (n == -1) { if (ngx_socket_errno == NGX_EAGAIN) { - 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); } @@ -573,11 +560,6 @@ ngx_http_ssl_handshake(ngx_event_t *rev) rc = ngx_ssl_handshake(c); if (rc == NGX_AGAIN) { - - if (!rev->timer_set) { - ngx_add_timer(rev, c->listening->post_accept_timeout); - } - c->ssl->handler = ngx_http_ssl_handshake_handler; return; } @@ -587,10 +569,10 @@ ngx_http_ssl_handshake(ngx_event_t *rev) return; } - c->log->action = "reading client request line"; - - rev->handler = ngx_http_init_request; - ngx_http_init_request(rev); + c->log->action = "waiting request"; + + rev->handler = ngx_http_keepalive_handler; + ngx_http_keepalive_handler(rev); } @@ -609,12 +591,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 request"; + + c->read->handler = ngx_http_keepalive_handler; /* STUB: epoll edge */ c->write->handler = ngx_http_empty_handler; - ngx_http_init_request(c->read); + ngx_http_keepalive_handler(c->read); return; } @@ -2773,16 +2755,24 @@ ngx_http_set_keepalive(ngx_http_request_ static void ngx_http_keepalive_handler(ngx_event_t *rev) { - size_t size; - ssize_t n; - ngx_buf_t *b; - ngx_connection_t *c; + 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 keepalive handler"); if (rev->timedout || c->close) { + + if (!c->idle) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, + "client timed out"); + } + ngx_http_close_connection(c); return; } @@ -2791,10 +2781,8 @@ ngx_http_keepalive_handler(ngx_event_t * 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); + "kevent() reported that client closed connection"); #if (NGX_HTTP_SSL) if (c->ssl) { c->ssl->no_send_shutdown = 1; @@ -2807,17 +2795,14 @@ ngx_http_keepalive_handler(ngx_event_t * #endif + hc = c->data; + cscf = ngx_http_get_module_srv_conf(hc->ctx, ngx_http_core_module); + + size = cscf->client_header_buffer_size; b = c->buffer; - size = b->end - b->start; if (b->pos == NULL) { - /* - * The c->buffer's memory was freed by ngx_http_set_keepalive(). - * However, the c->buffer->start and c->buffer->end were not changed - * to keep the buffer size. - */ - b->pos = ngx_palloc(c->pool, size); if (b->pos == NULL) { ngx_http_close_connection(c); @@ -2847,7 +2832,7 @@ ngx_http_keepalive_handler(ngx_event_t * /* * Like ngx_http_set_keepalive() we are trying to not hold - * c->buffer's memory for a keepalive connection. + * c->buffer's memory for an idle connection. */ if (ngx_pfree(c->pool, b->start) == NGX_OK) { @@ -2867,18 +2852,15 @@ ngx_http_keepalive_handler(ngx_event_t * return; } - c->log->handler = NULL; - if (n == 0) { ngx_log_error(NGX_LOG_INFO, c->log, ngx_socket_errno, - "client %V closed keepalive connection", &c->addr_text); + "client closed connection"); ngx_http_close_connection(c); return; } b->last += n; - c->log->handler = ngx_http_log_error; c->log->action = "reading client request line"; c->idle = 0; # HG changeset patch # User Valentin V. Bartenev # Date 1351883308 -14400 # Node ID 3f34b24787d7c261b450f65dc4412207b8f9f3c4 # Parent b5d8fbef90291853e331a58334028e7bbacc541a Fixed: "client_header_timeout" was used only for the first request in connection. diff -r b5d8fbef9029 -r 3f34b24787d7 src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Fri Nov 02 23:02:35 2012 +0400 +++ b/src/http/ngx_http_request.c Fri Nov 02 23:08:28 2012 +0400 @@ -2609,8 +2609,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; @@ -2626,6 +2624,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; @@ -2746,6 +2748,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); } @@ -2863,7 +2867,11 @@ ngx_http_keepalive_handler(ngx_event_t * c->log->action = "reading client request line"; - c->idle = 0; + if (c->idle) { + c->idle = 0; + ngx_del_timer(rev); + } + ngx_reusable_connection(c, 0); ngx_http_init_request(rev); # HG changeset patch # User Valentin V. Bartenev # Date 1351883906 -14400 # Node ID 1d70831e6a362d5f19085a3db02c4767a1cbe515 # Parent 3f34b24787d7c261b450f65dc4412207b8f9f3c4 Make connections reusable after the accept and till the first byte of request. diff -r 3f34b24787d7 -r 1d70831e6a36 src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Fri Nov 02 23:08:28 2012 +0400 +++ b/src/event/ngx_event_openssl.c Fri Nov 02 23:18:26 2012 +0400 @@ -854,7 +854,7 @@ ngx_ssl_handshake_handler(ngx_event_t *e ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL handshake handler: %d", ev->write); - if (ev->timedout) { + if (ev->timedout || c->close) { c->ssl->handler(c); return; } diff -r 3f34b24787d7 -r 1d70831e6a36 src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Fri Nov 02 23:08:28 2012 +0400 +++ b/src/http/ngx_http_request.c Fri Nov 02 23:18:26 2012 +0400 @@ -338,6 +338,8 @@ ngx_http_init_connection(ngx_connection_ } #endif + ngx_reusable_connection(c, 1); + ngx_add_timer(rev, c->listening->post_accept_timeout); if (rev->ready) { @@ -519,6 +521,12 @@ ngx_http_ssl_handshake(ngx_event_t *rev) return; } + if (c->close) { + ngx_log_error(NGX_LOG_WARN, c->log, 0, "abort connection"); + ngx_http_close_connection(c); + return; + } + n = recv(c->fd, (char *) buf, 1, MSG_PEEK); if (n == -1) { @@ -603,6 +611,9 @@ ngx_http_ssl_handshake_handler(ngx_conne if (c->read->timedout) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + + } else if (c->close) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "abort connection"); } ngx_http_close_connection(c); @@ -2773,8 +2784,12 @@ ngx_http_keepalive_handler(ngx_event_t * if (rev->timedout || c->close) { if (!c->idle) { - ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, - "client timed out"); + if (c->close) { + ngx_log_error(NGX_LOG_WARN, c->log, 0, "abort connection"); + } else { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, + "client timed out"); + } } ngx_http_close_connection(c); # HG changeset patch # User Valentin V. Bartenev # Date 1351885445 -14400 # Node ID 0d603bf04baddb03f149c554d43590f682715b97 # Parent 1d70831e6a362d5f19085a3db02c4767a1cbe515 Respect the new behavior of TCP_DEFER_ACCEPT. The behavior of TCP_DEFER_ACCEPT was changed in Linux kernel 2.6.32. Now it accepts connection after the timeout even if there is no data was received from the client. In nginx the TCP_DEFER_ACCEPT timeout is equal to "post_accept_timeout", so we do not want to wait for another one if we have got EAGAIN after the deferred accept. diff -r 1d70831e6a36 -r 0d603bf04bad src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Fri Nov 02 23:18:26 2012 +0400 +++ b/src/http/ngx_http_request.c Fri Nov 02 23:44:05 2012 +0400 @@ -532,6 +532,15 @@ ngx_http_ssl_handshake(ngx_event_t *rev) if (n == -1) { if (ngx_socket_errno == 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, + "deferred accept timed out"); + ngx_http_close_connection(c); + return; + } +#endif + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_connection(c); } @@ -2845,6 +2854,23 @@ ngx_http_keepalive_handler(ngx_event_t * c->log_error = NGX_ERROR_INFO; if (n == NGX_AGAIN) { + +#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) + if (c->listening->deferred_accept) { + if (!c->idle +#if (NGX_HTTP_SSL) + && c->ssl == NULL +#endif + ) + { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, + "deferred accept timed out"); + ngx_http_close_connection(c); + return; + } + } +#endif + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_connection(c); } # HG changeset patch # User Valentin V. Bartenev # Date 1351599261 -14400 # Node ID e467074d48d3ba807bdb29394b9de0e8db9208e2 # Parent 0d603bf04baddb03f149c554d43590f682715b97 Removed unused c->single_connection flag. The "single_connection" flag of ngx_connection_t is intended to be used as lock mechanism to prevent simultaneous modifying request object from different threads working with client and upstream connections. The flag is redundant since threads in nginx have never been used this way. diff -r 0d603bf04bad -r e467074d48d3 src/core/ngx_connection.c --- a/src/core/ngx_connection.c Fri Nov 02 23:44:05 2012 +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 0d603bf04bad -r e467074d48d3 src/core/ngx_connection.h --- a/src/core/ngx_connection.h Fri Nov 02 23:44:05 2012 +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 0d603bf04bad -r e467074d48d3 src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Fri Nov 02 23:44:05 2012 +0400 +++ b/src/http/ngx_http_request.c Tue Oct 30 16:14:21 2012 +0400 @@ -445,7 +445,6 @@ ngx_http_init_request(ngx_event_t *rev) return; } - c->single_connection = 1; c->destroyed = 0; #if (NGX_HTTP_SSL) diff -r 0d603bf04bad -r e467074d48d3 src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c Fri Nov 02 23:44:05 2012 +0400 +++ b/src/http/ngx_http_upstream.c Tue Oct 30 16:14:21 2012 +0400 @@ -1105,8 +1105,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 V. Bartenev # Date 1351886318 -14400 # Node ID 937db9e84fd7b9a7069f1fc7d8cf4bedb7036aa6 # Parent e467074d48d3ba807bdb29394b9de0e8db9208e2 The ngx_http_init_request() function was refactored. Now this function can be used as the request object factory with minimal impact to the connection object. diff -r e467074d48d3 -r 937db9e84fd7 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 Fri Nov 02 23:58:38 2012 +0400 @@ -10,7 +10,7 @@ #include -static void ngx_http_init_request(ngx_event_t *ev); +static ngx_http_request_t *ngx_http_init_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); @@ -361,12 +361,11 @@ ngx_http_init_connection(ngx_connection_ } -static void -ngx_http_init_request(ngx_event_t *rev) +static ngx_http_request_t * +ngx_http_init_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; @@ -374,8 +373,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; @@ -384,27 +381,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->ctx->main_conf; @@ -424,15 +413,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); @@ -441,12 +428,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) { ngx_http_ssl_srv_conf_t *sscf; @@ -492,8 +476,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; } @@ -2640,20 +2623,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_init_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 @@ -2907,6 +2900,15 @@ ngx_http_keepalive_handler(ngx_event_t * c->log->action = "reading client request line"; + c->data = ngx_http_init_request(c); + if (c->data == NULL) { + ngx_http_close_connection(c); + return; + } + + c->sent = 0; + c->destroyed = 0; + if (c->idle) { c->idle = 0; ngx_del_timer(rev); @@ -2914,7 +2916,8 @@ ngx_http_keepalive_handler(ngx_event_t * ngx_reusable_connection(c, 0); - ngx_http_init_request(rev); + rev->handler = ngx_http_process_request_line; + ngx_http_process_request_line(rev); } diff -r e467074d48d3 -r 937db9e84fd7 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 Fri Nov 02 23:58:38 2012 +0400 @@ -301,8 +301,6 @@ typedef struct { ngx_buf_t **free; ngx_int_t nfree; - - ngx_uint_t pipeline; /* unsigned pipeline:1; */ } ngx_http_connection_t; # HG changeset patch # User Valentin V. Bartenev # Date 1351887542 -14400 # Node ID 17fe3b6dc916f65e1ef72b899b2444e5936edf7f # Parent 937db9e84fd7b9a7069f1fc7d8cf4bedb7036aa6 The "post_accept_timeout" was separated from the "client_header_timeout". Since the connections in "post_accept_timeout" are cheap and reusable, there is not much sense in including it into the "client_header_timeout" as well as that they are being equal. The new directive introduced. diff -r 937db9e84fd7 -r 17fe3b6dc916 src/http/ngx_http.c --- a/src/http/ngx_http.c Fri Nov 02 23:58:38 2012 +0400 +++ b/src/http/ngx_http.c Sat Nov 03 00:19:02 2012 +0400 @@ -1743,7 +1743,7 @@ ngx_http_add_listening(ngx_conf_t *cf, n cscf = addr->default_server; ls->pool_size = cscf->connection_pool_size; - ls->post_accept_timeout = cscf->client_header_timeout; + ls->post_accept_timeout = cscf->post_accept_timeout; clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index]; diff -r 937db9e84fd7 -r 17fe3b6dc916 src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c Fri Nov 02 23:58:38 2012 +0400 +++ b/src/http/ngx_http_core_module.c Sat Nov 03 00:19:02 2012 +0400 @@ -242,6 +242,13 @@ static ngx_command_t ngx_http_core_comm offsetof(ngx_http_core_srv_conf_t, request_pool_size), &ngx_http_core_pool_size_p }, + { ngx_string("post_accept_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_core_srv_conf_t, post_accept_timeout), + NULL }, + { ngx_string("client_header_timeout"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_msec_slot, @@ -3398,6 +3405,7 @@ ngx_http_core_create_srv_conf(ngx_conf_t cscf->connection_pool_size = NGX_CONF_UNSET_SIZE; cscf->request_pool_size = NGX_CONF_UNSET_SIZE; + cscf->post_accept_timeout = NGX_CONF_UNSET_MSEC; cscf->client_header_timeout = NGX_CONF_UNSET_MSEC; cscf->client_header_buffer_size = NGX_CONF_UNSET_SIZE; cscf->ignore_invalid_headers = NGX_CONF_UNSET; @@ -3423,6 +3431,8 @@ ngx_http_core_merge_srv_conf(ngx_conf_t prev->connection_pool_size, 256); ngx_conf_merge_size_value(conf->request_pool_size, prev->request_pool_size, 4096); + ngx_conf_merge_msec_value(conf->post_accept_timeout, + prev->post_accept_timeout, 75000); ngx_conf_merge_msec_value(conf->client_header_timeout, prev->client_header_timeout, 60000); ngx_conf_merge_size_value(conf->client_header_buffer_size, diff -r 937db9e84fd7 -r 17fe3b6dc916 src/http/ngx_http_core_module.h --- a/src/http/ngx_http_core_module.h Fri Nov 02 23:58:38 2012 +0400 +++ b/src/http/ngx_http_core_module.h Sat Nov 03 00:19:02 2012 +0400 @@ -190,6 +190,7 @@ typedef struct { ngx_bufs_t large_client_header_buffers; + ngx_msec_t post_accept_timeout; ngx_msec_t client_header_timeout; ngx_flag_t ignore_invalid_headers; diff -r 937db9e84fd7 -r 17fe3b6dc916 src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Fri Nov 02 23:58:38 2012 +0400 +++ b/src/http/ngx_http_request.c Sat Nov 03 00:19:02 2012 +0400 @@ -2909,13 +2909,11 @@ ngx_http_keepalive_handler(ngx_event_t * c->sent = 0; c->destroyed = 0; - if (c->idle) { - c->idle = 0; - ngx_del_timer(rev); - } - + c->idle = 0; ngx_reusable_connection(c, 0); + ngx_del_timer(rev); + rev->handler = ngx_http_process_request_line; ngx_http_process_request_line(rev); } # HG changeset patch # User Valentin V. Bartenev # Date 1359254231 -14400 # Node ID fc6074ec1162ef5fd8ca96489b964911e495f3a9 # Parent 17fe3b6dc916f65e1ef72b899b2444e5936edf7f imported patch npn diff -r 17fe3b6dc916 -r fc6074ec1162 src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c Sat Nov 03 00:19:02 2012 +0400 +++ b/src/http/modules/ngx_http_ssl_module.c Sun Jan 27 06:37:11 2013 +0400 @@ -35,6 +35,15 @@ static char *ngx_http_ssl_session_cache( static ngx_int_t ngx_http_ssl_init(ngx_conf_t *cf); +#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); + +#endif + static ngx_conf_bitmask_t ngx_http_ssl_protocols[] = { { ngx_string("SSLv2"), NGX_SSL_SSLv2 }, @@ -490,6 +499,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; @@ -753,3 +767,25 @@ ngx_http_ssl_init(ngx_conf_t *cf) return NGX_OK; } + + +#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) +{ +#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 # HG changeset patch # User Valentin V. Bartenev # Date 1359254232 -14400 # Node ID c56301ef63c4e64d803560d2973484561c7341b9 # Parent fc6074ec1162ef5fd8ca96489b964911e495f3a9 imported patch limit_req diff -r fc6074ec1162 -r c56301ef63c4 src/http/modules/ngx_http_limit_req_module.c --- a/src/http/modules/ngx_http_limit_req_module.c Sun Jan 27 06:37:11 2013 +0400 +++ b/src/http/modules/ngx_http_limit_req_module.c Sun Jan 27 06:37:12 2013 +0400 @@ -56,7 +56,8 @@ typedef struct { } ngx_http_limit_req_conf_t; -static void ngx_http_limit_req_delay(ngx_http_request_t *r); +static void ngx_http_limit_req_delay(ngx_event_t *ev); +static void ngx_http_limit_req_delay_cleanup(void *data); static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit, ngx_uint_t hash, u_char *data, size_t len, ngx_uint_t *ep, ngx_uint_t account); @@ -150,6 +151,8 @@ ngx_http_limit_req_handler(ngx_http_requ ngx_int_t rc; ngx_uint_t n, excess; ngx_msec_t delay; + ngx_event_t *ev; + ngx_http_cleanup_t *cln; ngx_http_variable_value_t *vv; ngx_http_limit_req_ctx_t *ctx; ngx_http_limit_req_conf_t *lrcf; @@ -264,43 +267,38 @@ ngx_http_limit_req_handler(ngx_http_requ "delaying request, excess: %ui.%03ui, by zone \"%V\"", excess / 1000, excess % 1000, &limit->shm_zone->shm.name); - if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { + cln = ngx_http_cleanup_add(r, sizeof(ngx_event_t)); + if (cln == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } + cln->handler = ngx_http_limit_req_delay_cleanup; + + ev = cln->data; + + ngx_memzero(ev, sizeof(ngx_event_t)); + + ev->handler = ngx_http_limit_req_delay; + ev->data = r; + ev->log = r->connection->log; + r->read_event_handler = ngx_http_test_reading; - r->write_event_handler = ngx_http_limit_req_delay; - ngx_add_timer(r->connection->write, delay); + r->write_event_handler = ngx_http_request_empty_handler; + + ngx_add_timer(ev, delay); return NGX_AGAIN; } static void -ngx_http_limit_req_delay(ngx_http_request_t *r) +ngx_http_limit_req_delay(ngx_event_t *ev) { - ngx_event_t *wev; + ngx_http_request_t *r; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "limit_req delay"); + r = ev->data; - wev = r->connection->write; - - if (!wev->timedout) { - - if (ngx_handle_write_event(wev, 0) != NGX_OK) { - ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - } - - return; - } - - wev->timedout = 0; - - if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { - ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return; - } + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, "limit_req delay"); r->read_event_handler = ngx_http_block_reading; r->write_event_handler = ngx_http_core_run_phases; @@ -310,6 +308,17 @@ ngx_http_limit_req_delay(ngx_http_reques static void +ngx_http_limit_req_delay_cleanup(void *data) +{ + ngx_event_t *ev = data; + + if (ev->timer_set) { + ngx_del_timer(ev); + } +} + + +static void ngx_http_limit_req_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) { # HG changeset patch # User Valentin V. Bartenev # Date 1359254234 -14400 # Node ID 87a4e0a9d241b4920f02730fe6d2833a82eb9146 # Parent c56301ef63c4e64d803560d2973484561c7341b9 imported patch stat_keepalive diff -r c56301ef63c4 -r 87a4e0a9d241 src/core/ngx_connection.c --- a/src/core/ngx_connection.c Sun Jan 27 06:37:12 2013 +0400 +++ b/src/core/ngx_connection.c Sun Jan 27 06:37:14 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_idle, -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_idle, 1); +#endif } } diff -r c56301ef63c4 -r 87a4e0a9d241 src/event/ngx_event.c --- a/src/event/ngx_event.c Sun Jan 27 06:37:12 2013 +0400 +++ b/src/event/ngx_event.c Sun Jan 27 06:37:14 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_idle0; +ngx_atomic_t *ngx_stat_idle = &ngx_stat_idle0; #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_idle */ #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_idle = (ngx_atomic_t *) (shared + 9 * cl); #endif diff -r c56301ef63c4 -r 87a4e0a9d241 src/event/ngx_event.h --- a/src/event/ngx_event.h Sun Jan 27 06:37:12 2013 +0400 +++ b/src/event/ngx_event.h Sun Jan 27 06:37:14 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_idle; #endif diff -r c56301ef63c4 -r 87a4e0a9d241 src/http/modules/ngx_http_stub_status_module.c --- a/src/http/modules/ngx_http_stub_status_module.c Sun Jan 27 06:37:12 2013 +0400 +++ b/src/http/modules/ngx_http_stub_status_module.c Sun Jan 27 06:37:14 2013 +0400 @@ -64,7 +64,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, id; if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) { return NGX_HTTP_NOT_ALLOWED; @@ -107,6 +107,7 @@ static ngx_int_t ngx_http_status_handler rq = *ngx_stat_requests; rd = *ngx_stat_reading; wr = *ngx_stat_writing; + id = *ngx_stat_idle; b->last = ngx_sprintf(b->last, "Active connections: %uA \n", ac); @@ -116,7 +117,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, id); r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = b->last - b->pos; # HG changeset patch # User Valentin V. Bartenev # Date 1359254235 -14400 # Node ID aba1fef8b06039146d7b2ea68179bc25f917d904 # Parent 87a4e0a9d241b4920f02730fe6d2833a82eb9146 imported patch spdy diff -r 87a4e0a9d241 -r aba1fef8b060 auto/modules --- a/auto/modules Sun Jan 27 06:37:14 2013 +0400 +++ b/auto/modules Sun Jan 27 06:37:15 2013 +0400 @@ -118,8 +118,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 +184,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 87a4e0a9d241 -r aba1fef8b060 auto/options --- a/auto/options Sun Jan 27 06:37:14 2013 +0400 +++ b/auto/options Sun Jan 27 06:37:15 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 ;; diff -r 87a4e0a9d241 -r aba1fef8b060 auto/sources --- a/auto/sources Sun Jan 27 06:37:14 2013 +0400 +++ b/auto/sources Sun Jan 27 06:37:15 2013 +0400 @@ -324,6 +324,16 @@ 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_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_SPDY_FILTER_MODULE="ngx_http_spdy_filter_module" + + HTTP_CHARSET_FILTER_MODULE=ngx_http_charset_filter_module HTTP_CHARSET_SRCS=src/http/modules/ngx_http_charset_filter_module.c diff -r 87a4e0a9d241 -r aba1fef8b060 src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c Sun Jan 27 06:37:14 2013 +0400 +++ b/src/http/modules/ngx_http_ssl_module.c Sun Jan 27 06:37:15 2013 +0400 @@ -775,11 +775,27 @@ 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); +#endif + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "SSL NPN advertised"); + +#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; diff -r 87a4e0a9d241 -r aba1fef8b060 src/http/ngx_http.c --- a/src/http/ngx_http.c Sun Jan 27 06:37:14 2013 +0400 +++ b/src/http/ngx_http.c Sun Jan 27 06:37:15 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 @@ -1278,6 +1281,18 @@ ngx_http_add_addresses(ngx_conf_t *cf, n ssl = lsopt->ssl || addr[i].opt.ssl; #endif +#if (NGX_HTTP_SPDY) + spdy = lsopt->spdy || addr[i].opt.spdy; + +#if (NGX_HTTP_SSL && !defined TLSEXT_TYPE_next_proto_neg) + if (spdy && ssl) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "nginx was built without OpenSSL NPN support, " + "SPDY is not enabled for %s", addr[i].opt.addr); + } +#endif +#endif + if (lsopt->set) { if (addr[i].opt.set) { @@ -1307,6 +1322,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; } @@ -1820,6 +1838,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 +1902,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 87a4e0a9d241 -r aba1fef8b060 src/http/ngx_http.h --- a/src/http/ngx_http.h Sun Jan 27 06:37:14 2013 +0400 +++ b/src/http/ngx_http.h Sun Jan 27 06:37:15 2013 +0400 @@ -13,6 +13,7 @@ #include +typedef struct ngx_http_connection_s ngx_http_connection_t; typedef struct ngx_http_request_s ngx_http_request_t; typedef struct ngx_http_upstream_s ngx_http_upstream_t; typedef struct ngx_http_cache_s ngx_http_cache_t; @@ -25,6 +26,9 @@ typedef ngx_int_t (*ngx_http_header_hand typedef u_char *(*ngx_http_log_handler_pt)(ngx_http_request_t *r, ngx_http_request_t *sr, u_char *buf, size_t len); +#if (NGX_HTTP_SPDY) +#include +#endif #include #include @@ -35,6 +39,10 @@ typedef u_char *(*ngx_http_log_handler_p #include #include +#if (NGX_HTTP_SPDY) +#include +#endif + #if (NGX_HTTP_CACHE) #include #endif @@ -80,12 +88,16 @@ 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_http_request_t *ngx_http_init_request(ngx_connection_t *c); + 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,6 +116,7 @@ ngx_int_t ngx_http_parse_chunked(ngx_htt ngx_http_chunked_t *ctx); +ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r); 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); @@ -111,6 +124,7 @@ void ngx_http_run_posted_requests(ngx_co 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_log_request(ngx_http_request_t *r); void ngx_http_empty_handler(ngx_event_t *wev); void ngx_http_request_empty_handler(ngx_http_request_t *r); diff -r 87a4e0a9d241 -r aba1fef8b060 src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c Sun Jan 27 06:37:14 2013 +0400 +++ b/src/http/ngx_http_core_module.c Sun Jan 27 06:37:15 2013 +0400 @@ -82,7 +82,6 @@ static char *ngx_http_disable_symlinks(n #endif static char *ngx_http_core_lowat_check(ngx_conf_t *cf, void *post, void *data); -static char *ngx_http_core_pool_size(ngx_conf_t *cf, void *post, void *data); static ngx_conf_post_t ngx_http_core_lowat_post = { ngx_http_core_lowat_check }; @@ -2134,6 +2133,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; @@ -4082,6 +4088,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) { @@ -5141,7 +5159,7 @@ ngx_http_core_lowat_check(ngx_conf_t *cf } -static char * +char * ngx_http_core_pool_size(ngx_conf_t *cf, void *post, void *data) { size_t *sp = data; diff -r 87a4e0a9d241 -r aba1fef8b060 src/http/ngx_http_core_module.h --- a/src/http/ngx_http_core_module.h Sun Jan 27 06:37:14 2013 +0400 +++ b/src/http/ngx_http_core_module.h Sun Jan 27 06:37:15 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 @@ -233,7 +236,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 }; @@ -528,6 +534,9 @@ ngx_int_t ngx_http_get_forwarded_addr(ng u_char *xff, size_t xfflen, ngx_array_t *proxies, int recursive); +char *ngx_http_core_pool_size(ngx_conf_t *cf, void *post, void *data); + + extern ngx_module_t ngx_http_core_module; extern ngx_uint_t ngx_http_max_module; diff -r 87a4e0a9d241 -r aba1fef8b060 src/http/ngx_http_parse.c --- a/src/http/ngx_http_parse.c Sun Jan 27 06:37:14 2013 +0400 +++ b/src/http/ngx_http_parse.c Sun Jan 27 06:37:15 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 87a4e0a9d241 -r aba1fef8b060 src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Sun Jan 27 06:37:14 2013 +0400 +++ b/src/http/ngx_http_request.c Sun Jan 27 06:37:15 2013 +0400 @@ -10,7 +10,6 @@ #include -static ngx_http_request_t *ngx_http_init_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); @@ -30,7 +29,6 @@ static ngx_int_t ngx_http_process_user_a static ngx_int_t ngx_http_process_cookie(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); -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 ssize_t ngx_http_validate_host(ngx_http_request_t *r, u_char **host, size_t len); @@ -52,8 +50,6 @@ static void ngx_http_lingering_close_han 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, @@ -315,6 +311,12 @@ ngx_http_init_connection(ngx_connection_ rev->handler = ngx_http_keepalive_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; @@ -361,7 +363,7 @@ ngx_http_init_connection(ngx_connection_ } -static ngx_http_request_t * +ngx_http_request_t * ngx_http_init_request(ngx_connection_t *c) { ngx_pool_t *pool; @@ -590,6 +592,22 @@ ngx_http_ssl_handshake_handler(ngx_conne c->ssl->no_wait_shutdown = 1; +#if (NGX_HTTP_SPDY && defined TLSEXT_TYPE_next_proto_neg) + { + const u_char *data; + unsigned int len; + + SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &len); + + if (len == sizeof("spdy/2") - 1 + && ngx_strncmp(data, "spdy/2", sizeof("spdy/2") - 1) == 0) + { + ngx_http_spdy_init(c->read); + return; + } + } +#endif + c->log->action = "waiting request"; c->read->handler = ngx_http_keepalive_handler; @@ -1457,7 +1475,7 @@ ngx_http_process_cookie(ngx_http_request } -static ngx_int_t +ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r) { if (r->headers_in.server.len == 0) { @@ -2027,6 +2045,13 @@ ngx_http_finalize_request(ngx_http_reque ngx_http_request_t *pr; ngx_http_core_loc_conf_t *clcf; +#if (NGX_HTTP_SPDY) + if (r->spdy_stream) { + ngx_http_spdy_finalize_request(r, rc); + return; + } +#endif + c = r->connection; ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, @@ -3212,7 +3237,7 @@ ngx_http_free_request(ngx_http_request_t } -static void +void ngx_http_log_request(ngx_http_request_t *r) { ngx_uint_t i, n; @@ -3230,7 +3255,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 87a4e0a9d241 -r aba1fef8b060 src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h Sun Jan 27 06:37:14 2013 +0400 +++ b/src/http/ngx_http_request.h Sun Jan 27 06:37:15 2013 +0400 @@ -288,7 +288,7 @@ typedef struct ngx_http_addr_conf_s ngx typedef struct ngx_http_conf_ctx_s ngx_http_conf_ctx_t; -typedef struct { +struct ngx_http_connection_s { ngx_http_addr_conf_t *addr_conf; ngx_http_conf_ctx_t *ctx; @@ -301,7 +301,7 @@ typedef struct { ngx_buf_t **free; ngx_int_t nfree; -} ngx_http_connection_t; +}; typedef void (*ngx_http_cleanup_pt)(void *data); @@ -419,6 +419,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 87a4e0a9d241 -r aba1fef8b060 src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c Sun Jan 27 06:37:14 2013 +0400 +++ b/src/http/ngx_http_request_body.c Sun Jan 27 06:37:15 2013 +0400 @@ -41,7 +41,33 @@ ngx_http_read_client_request_body(ngx_ht r->main->count++; - if (r->request_body || r->discard_body) { + if (r->discard_body) { + post_handler(r); + return NGX_OK; + } + +#if (NGX_HTTP_SPDY) + if (r->spdy_stream) { + if (!r->request_body) { + if (ngx_http_spdy_init_request_body(r) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + } + + rb = r->request_body; + + if (r->spdy_stream->half_closed) { + post_handler(r); + return NGX_OK; + } + + rb->post_handler = post_handler; + + return NGX_AGAIN; + } +#endif + + if (r->request_body) { post_handler(r); return NGX_OK; } @@ -450,6 +476,13 @@ ngx_http_discard_request_body(ngx_http_r return NGX_OK; } +#if (NGX_HTTP_SPDY) + if (r->spdy_stream) { + r->discard_body = 1; + return NGX_OK; + } +#endif + if (ngx_http_test_expect(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } diff -r 87a4e0a9d241 -r aba1fef8b060 src/http/ngx_http_spdy.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/http/ngx_http_spdy.c Sun Jan 27 06:37:15 2013 +0400 @@ -0,0 +1,3197 @@ + +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Valentin V. Bartenev + */ + + +#include +#include +#include + +#include + + +#if (NGX_HAVE_GCC_ATTRIBUTE_ALIGNED) +#define ngx_aligned(x) __attribute__(( aligned (x) )) +#else +#define ngx_aligned(x) +#endif + + +#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)) +#define ngx_spdy_frame_parse_len(p) (ntohl(*(uint32_t *) (p)) >> 8) +#define ngx_spdy_frame_parse_sid(p) (ntohl(*(uint32_t *) (p)) & 0x7fffffff) + +#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]) +#define ngx_spdy_frame_parse_len(p) ((p)[0] << 16 | (p)[1] << 8 | (p)[2]) +#define ngx_spdy_frame_parse_sid(p) \ + (((p)[0] & 0x7f) << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3]) + +#endif + + +#define NGX_SPDY_HEADER_SIZE 8 + +#define NGX_SPDY_CTRL_BYTE 0x80 + +#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 + + +#if (NGX_HAVE_LITTLE_ENDIAN) + +#define NGX_SPDY_SYN_STREAM_HEAD \ + (NGX_SPDY_SYN_STREAM << 24 | NGX_SPDY_VERSION << 8 | NGX_SPDY_CTRL_BYTE) + +#define NGX_SPDY_SYN_REPLY_HEAD \ + (NGX_SPDY_SYN_REPLY << 24 | NGX_SPDY_VERSION << 8 | NGX_SPDY_CTRL_BYTE) + +#define NGX_SPDY_RST_STREAM_HEAD \ + (NGX_SPDY_RST_STREAM << 24 | NGX_SPDY_VERSION << 8 | NGX_SPDY_CTRL_BYTE) + +#define NGX_SPDY_SETTINGS_HEAD \ + (NGX_SPDY_SETTINGS << 24 | NGX_SPDY_VERSION << 8 | NGX_SPDY_CTRL_BYTE) + +#define NGX_SPDY_NOOP_HEAD \ + (NGX_SPDY_NOOP << 24 | NGX_SPDY_VERSION << 8 | NGX_SPDY_CTRL_BYTE) + +#define NGX_SPDY_PING_HEAD \ + (NGX_SPDY_PING << 24 | NGX_SPDY_VERSION << 8 | NGX_SPDY_CTRL_BYTE) + +#define NGX_SPDY_GOAWAY_HEAD \ + (NGX_SPDY_GOAWAY << 24 | NGX_SPDY_VERSION << 8 | NGX_SPDY_CTRL_BYTE) + +#define NGX_SPDY_HEADERS_HEAD \ + (NGX_SPDY_HEADERS << 24 | NGX_SPDY_VERSION << 8 | NGX_SPDY_CTRL_BYTE) + +#else + +#define NGX_SPDY_SYN_STREAM_HEAD \ + (NGX_SPDY_CTRL_BYTE << 24 | NGX_SPDY_VERSION << 16 | NGX_SPDY_SYN_STREAM) + +#define NGX_SPDY_SYN_REPLY_HEAD \ + (NGX_SPDY_CTRL_BYTE << 24 | NGX_SPDY_VERSION << 16 | NGX_SPDY_SYN_REPLY) + +#define NGX_SPDY_RST_STREAM_HEAD \ + (NGX_SPDY_CTRL_BYTE << 24 | NGX_SPDY_VERSION << 16 | NGX_SPDY_RST_STREAM) + +#define NGX_SPDY_SETTINGS_HEAD \ + (NGX_SPDY_CTRL_BYTE << 24 | NGX_SPDY_VERSION << 16 | NGX_SPDY_SETTINGS) + +#define NGX_SPDY_NOOP_HEAD \ + (NGX_SPDY_CTRL_BYTE << 24 | NGX_SPDY_VERSION << 16 | NGX_SPDY_NOOP) + +#define NGX_SPDY_PING_HEAD \ + (NGX_SPDY_CTRL_BYTE << 24 | NGX_SPDY_VERSION << 16 | NGX_SPDY_PING) + +#define NGX_SPDY_GOAWAY_HEAD \ + (NGX_SPDY_CTRL_BYTE << 24 | NGX_SPDY_VERSION << 16 | NGX_SPDY_GOAWAY) + +#define NGX_SPDY_HEADERS_HEAD \ + (NGX_SPDY_CTRL_BYTE << 24 | NGX_SPDY_VERSION << 16 | NGX_SPDY_HEADERS) + +#endif + + +#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_VERSION_HEADER_HASH (ngx_uint_t) 107725790424ull +#define NGX_SPDY_SCHEME_HEADER_HASH 3386979749u +#define NGX_SPDY_METHOD_HEADER_HASH 3217412321u +#define NGX_SPDY_URL_HEADER_HASH 116079u + +#define NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE 4096 + +#define NGX_SPDY_CTRL_FRAME_BUFFER_SIZE 16 + + +typedef struct { + u_char len; + u_char method[11]; + uint32_t value; +} ngx_http_spdy_method_test_t; + + +static 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 void *ngx_http_spdy_zalloc(void *opaque, u_int items, u_int size); +static void ngx_http_spdy_zfree(void *opaque, void *address); + +static void ngx_http_spdy_pool_cleanup(void *data); + +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_keepalive_handler(ngx_event_t *rev); + +#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_http_spdy_stream_t *ngx_http_spdy_get_stream_by_id( + ngx_http_spdy_connection_t *sc, ngx_uint_t sid); +static void ngx_http_spdy_stream_index_cleanup(void *data); + +static u_char *ngx_http_spdy_log_error_handler(ngx_http_request_t *r, + ngx_http_request_t *sr, u_char *buf, size_t len); +static void ngx_http_spdy_writer(ngx_http_request_t *r); + +static ngx_http_request_t *ngx_http_spdy_create_request( + ngx_http_spdy_connection_t *sc); +static void ngx_http_spdy_run_request(ngx_http_request_t *r); +static void ngx_http_spdy_terminate_request(ngx_http_request_t *r, + ngx_int_t rc); +static void ngx_http_spdy_terminate_handler(ngx_http_request_t *r); +static void ngx_http_spdy_request_finalizer(ngx_http_request_t *r); +static void ngx_http_spdy_close_request(ngx_http_request_t *r, ngx_int_t rc); +static void ngx_http_spdy_free_request(ngx_http_request_t *r, ngx_int_t rc); + +static void ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc); +static void ngx_http_spdy_finalize_connection(ngx_http_spdy_connection_t *sc, + ngx_int_t rc); + +static ngx_int_t ngx_http_spdy_process_frame(ngx_http_spdy_connection_t *sc, + u_char **pos, size_t size); +static ngx_int_t ngx_http_spdy_detect_settings_frame( + ngx_http_spdy_connection_t *sc, u_char **pos, size_t size); +static ngx_int_t ngx_http_spdy_process_settings_frame( + ngx_http_spdy_connection_t *sc, u_char **pos, size_t size); +static ngx_int_t ngx_http_spdy_skip_frame(ngx_http_spdy_connection_t *sc, + u_char **pos, size_t size); +static ngx_int_t ngx_http_spdy_process_syn_stream( + ngx_http_spdy_connection_t *sc, u_char **pos, size_t size); +static ngx_int_t ngx_http_spdy_process_data_frame( + ngx_http_spdy_connection_t *sc, u_char **pos, size_t size); +static ngx_int_t ngx_http_spdy_process_rst_stream( + ngx_http_spdy_connection_t *sc, u_char **pos, size_t size); +static ngx_int_t ngx_http_spdy_process_ping(ngx_http_spdy_connection_t *sc, + u_char **pos, size_t size); +static ngx_int_t ngx_http_spdy_process_headers(ngx_http_spdy_connection_t *sc, + u_char **pos, size_t size); +static ngx_int_t ngx_http_spdy_skip_headers(ngx_http_spdy_connection_t *sc, + u_char **pos, size_t size); + +static ngx_int_t ngx_http_spdy_parse_header(ngx_http_request_t *r, + ngx_uint_t allow_underscores); +static ngx_int_t ngx_http_spdy_parse_version(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_uri(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_construct_request_line(ngx_http_request_t *r); + +static ngx_http_spdy_out_frame_t *ngx_http_spdy_get_ctrl_frame( + ngx_http_spdy_connection_t *sc, size_t size, ngx_uint_t priority); +static ngx_int_t ngx_http_spdy_ctrl_frame_handler( + ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame); + +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 u_char *ngx_http_spdy_recv_buffer; + + +void +ngx_http_spdy_init(ngx_event_t *rev) +{ + int rc; + ngx_connection_t *c; + ngx_pool_cleanup_t *cln; + 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, + "init spdy request"); + + c->log->action = "SPDY processing"; + + 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 = c->data; + + sc->handler = ngx_http_spdy_detect_settings_frame; + + 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(sc->http_connection->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_pool_cleanup(void *data) +{ + ngx_http_spdy_connection_t *sc = data; + + if (sc->pool) { + ngx_destroy_pool(sc->pool); + } +} + + +ngx_int_t +ngx_http_spdy_alloc_recv_buffer(ngx_cycle_t *cycle) +{ + ngx_http_spdy_main_conf_t *smcf; + + smcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_spdy_module); + + if (smcf) { + ngx_http_spdy_recv_buffer = ngx_palloc(cycle->pool, + smcf->recv_buffer_size); + if (ngx_http_spdy_recv_buffer == NULL) { + return NGX_ERROR; + } + } + + return NGX_OK; +} + + +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 +} + + +static ngx_http_request_t * +ngx_http_spdy_create_request(ngx_http_spdy_connection_t *sc) +{ + ngx_log_t *log; + ngx_event_t *ev; + ngx_connection_t *fc; + ngx_http_log_ctx_t *ctx; + ngx_http_request_t *r; + ngx_http_core_srv_conf_t *cscf; + + fc = sc->free_fake_connections; + + if (fc) { + sc->free_fake_connections = fc->data; + + ev = fc->read; + log = fc->log; + ctx = log->data; + + } else { + fc = ngx_palloc(sc->pool, sizeof(ngx_connection_t)); + if (fc == NULL) { + return NULL; + } + + ev = ngx_palloc(sc->pool, sizeof(ngx_event_t)); + if (ev == 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_memzero(ev, sizeof(ngx_event_t)); + + ev->ready = 1; + ev->handler = ngx_http_empty_handler; + + ngx_memcpy(log, sc->connection->log, sizeof(ngx_log_t)); + + log->data = ctx; + + ngx_memcpy(fc, sc->connection, sizeof(ngx_connection_t)); + + fc->data = sc->http_connection; + fc->read = ev; + fc->write = ev; + fc->sent = 0; + fc->log = log; + fc->buffered = 0; + fc->sndlowat = 1; + + r = ngx_http_init_request(fc); + if (r == NULL) { + return NULL; + } + + 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) { + return NULL; + } + + sc->connection->requests++; + + fc->data = r; + + r->valid_location = 1; + r->log_handler = ngx_http_spdy_log_error_handler; + + r->gzip_tested = 1; + r->gzip_ok = 1; + + return r; +} + + +static void +ngx_http_spdy_read_handler(ngx_event_t *rev) +{ + u_char *p, *end; + size_t available; + ssize_t n; + ngx_int_t rc; + ngx_uint_t rest; + 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->ctx, + ngx_http_spdy_module); + + available = smcf->recv_buffer_size - NGX_SPDY_STATE_BUFFER_SIZE + 1; + + rc = sc->waiting ? NGX_AGAIN : NGX_DONE; + rest = sc->buffer_used; + + do { + p = ngx_http_spdy_recv_buffer; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "SPDY rest %ui", rest); + + ngx_memcpy(p, sc->buffer, NGX_SPDY_STATE_BUFFER_SIZE); + + n = c->recv(c, p + rest, available - rest); + + 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; + } + + n += rest; + end = p + n; + + do { + rc = sc->handler(sc, &p, n); + + n = end - p; + + if (rc == NGX_AGAIN) { + ngx_memcpy(sc->buffer, p, NGX_SPDY_STATE_BUFFER_SIZE); + break; + } + + if (rc == NGX_ERROR) { + ngx_log_error(NGX_LOG_WARN, c->log, 0, "SPDY ERROR"); + ngx_http_spdy_finalize_connection(sc, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + } while (n); + + rest = n; + +#if (NGX_DEBUG) + if (rest > NGX_SPDY_STATE_BUFFER_SIZE) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "spdy state buffer overflow: " + "%i bytes required", n); + ngx_http_spdy_finalize_connection(sc, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } +#endif + + } while (rev->ready); + + sc->buffer_used = rest; + + 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 (rc == NGX_AGAIN) { + sc->waiting = 1; + } + + 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, *fc; + ngx_http_log_ctx_t *ctx; + ngx_http_request_t *r; + ngx_http_spdy_stream_t *stream, *s, *sn; + ngx_http_spdy_connection_t *sc; + + c = wev->data; + + if (wev->timedout) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "SPDY DEBUG: write event timed out"); + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy write handler"); + + sc = c->data; + + 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; + + fc = stream->request->connection; + r = fc->data; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "spdy:%ui run request: \"%V?%V\"", + stream->id, &r->uri, &r->args); + + ctx = fc->log->data; + ctx->current_request = r; + + r->write_event_handler(r); + ngx_http_run_posted_requests(fc); + } + + 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_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; + } + + if (!c->write->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(c->write, &ngx_posted_events); + } + + return NGX_ERROR; + } + + clcf = ngx_http_get_module_loc_conf(sc->http_connection->ctx, + ngx_http_core_module); + + if (ngx_handle_write_event(c->write, clcf->send_lowat) != NGX_OK) { + return NGX_ERROR; + } + + 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->size, out->blocked); + } + + frame = NULL; + + for ( /* void */ ; out; out = fn) { + fn = out->next; + out->next = frame; + frame = out; + } + + sc->last_out = frame; + + return NGX_OK; +} + + +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->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 void +ngx_http_spdy_stream_index_cleanup(void *data) +{ + ngx_http_request_t *r = data; + + ngx_http_spdy_stream_t **index, *stream, *cs; + ngx_http_spdy_srv_conf_t *sscf; + ngx_http_spdy_connection_t *sc; + + stream = r->spdy_stream; + sc = stream->connection; + + sscf = ngx_http_get_module_srv_conf(sc->http_connection->ctx, + ngx_http_spdy_module); + + index = sc->streams_index + ngx_http_spdy_stream_index(sscf, stream->id); + + for ( ;; ) { + cs = *index; + + if (cs == NULL) { + return; + } + + if (cs == stream) { + *index = cs->index; + return; + } + + index = &cs->index; + } +} + + +static ngx_int_t +ngx_http_spdy_process_frame(ngx_http_spdy_connection_t *sc, u_char **pos, + size_t size) +{ + u_char *p, flags; + size_t length; + uint32_t head; + ngx_http_spdy_stream_t *stream; + + if (size < 8) { + return NGX_AGAIN; + } + + p = *pos; + +#if (NGX_HAVE_NONALIGNED) + head = *(uint32_t *) p; +#else + +#if (NGX_HAVE_LITTLE_ENDIAN) + head = p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; +#else + head = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; +#endif + +#endif + + flags = p[4]; + length = ngx_spdy_frame_parse_len(p + 5); + + sc->length = length; + sc->flags = flags; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy process frame head:%08Xd f:%ui l:%ui", + head, flags, length); + + *pos += 8; + + switch (head) { + + case NGX_SPDY_SYN_STREAM_HEAD: + sc->handler = ngx_http_spdy_process_syn_stream; + return NGX_OK; + + case NGX_SPDY_SYN_REPLY_HEAD: + //TODO log + return NGX_ERROR; + + case NGX_SPDY_RST_STREAM_HEAD: + sc->handler = ngx_http_spdy_process_rst_stream; + return NGX_OK; + + case NGX_SPDY_SETTINGS_HEAD: + //TODO + sc->handler = ngx_http_spdy_skip_frame; + return NGX_OK; + + case NGX_SPDY_NOOP_HEAD: + if (flags != 0 || length != 0) { + //TODO log + return NGX_ERROR; + } + return NGX_OK; + + case NGX_SPDY_PING_HEAD: + sc->handler = ngx_http_spdy_process_ping; + return NGX_OK; + + case NGX_SPDY_GOAWAY_HEAD: + //TODO + sc->handler = ngx_http_spdy_skip_frame; + return NGX_OK; + + case NGX_SPDY_HEADERS_HEAD: + //TODO log + return NGX_ERROR; + } + + head = ntohl(head); + + if (head >> 31) { + //TODO version & type check + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy unknown frame %ui", head); + + sc->handler = ngx_http_spdy_skip_frame; + + return NGX_OK; + } + + stream = ngx_http_spdy_get_stream_by_id(sc, head); + + if (stream == NULL || stream->request->discard_body) { + sc->handler = ngx_http_spdy_skip_frame; + return NGX_OK; + } + + if (stream->half_closed) { + //TODO log && error handling + return NGX_ERROR; + } + + sc->stream = stream; + sc->handler = ngx_http_spdy_process_data_frame; + + return ngx_http_spdy_process_data_frame(sc, pos, size - 8); +} + + +static ngx_int_t +ngx_http_spdy_detect_settings_frame(ngx_http_spdy_connection_t *sc, u_char **pos, + size_t size) +{ + u_char *p; + + if (size < 8) { + return NGX_AGAIN; + } + + p = *pos; + +#if (NGX_HAVE_NONALIGNED) + if (*(uint32_t *) p != NGX_SPDY_SETTINGS_HEAD) { +#else + if (p[0] != 0x80 + || p[1] != NGX_SPDY_VERSION + || p[2] != 0x00 + || p[3] != NGX_SPDY_SETTINGS) + { +#endif + ngx_http_spdy_send_settings(sc); + + sc->handler = ngx_http_spdy_process_frame; + return NGX_OK; + } + + sc->length = ngx_spdy_frame_parse_len(p + 5); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy SETTINGS frame received, size: %ui", + sc->length); + + *pos += 8; + + sc->handler = ngx_http_spdy_process_settings_frame; + return NGX_OK; +} + + +static ngx_int_t +ngx_http_spdy_process_settings_frame(ngx_http_spdy_connection_t *sc, u_char **pos, + size_t size) +{ + u_char *p; + ngx_uint_t v; + ngx_http_spdy_srv_conf_t *sscf; + + if (sc->headers == 0) { + + if (size < 4) { + return NGX_AGAIN; + } + + sc->headers = ngx_spdy_frame_parse_uint32(*pos); + + *pos += 4; + size -= 4; + sc->length -= 4; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy SETTINGS frame consists of %ui entries", + sc->headers); + } + + p = *pos; + + do { + if (size < 8) { + *pos = p; + return NGX_AGAIN; + } + + if (p[0] != 0x04) { + p += 8; + size -= 8; + sc->length -= 8; + continue; + } + + v = ngx_spdy_frame_parse_uint32(p + 4); + + sscf = ngx_http_get_module_srv_conf(sc->http_connection->ctx, + ngx_http_spdy_module); + + if (v != sscf->concurrent_streams) { + ngx_http_spdy_send_settings(sc); + } + + sc->handler = ngx_http_spdy_skip_frame; + return NGX_OK; + + } while (--sc->headers); + + ngx_http_spdy_send_settings(sc); + + sc->handler = ngx_http_spdy_process_frame; + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_spdy_skip_frame(ngx_http_spdy_connection_t *sc, u_char **pos, + size_t size) +{ + if (size < sc->length) { + *pos += size; + sc->length -= size; + return NGX_AGAIN; + } + + *pos += sc->length; + sc->handler = ngx_http_spdy_process_frame; + + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_spdy_process_syn_stream(ngx_http_spdy_connection_t *sc, u_char **pos, + size_t size) +{ + u_char *p; + ngx_uint_t sid, prio, index; + ngx_http_cleanup_t *cln; + ngx_http_request_t *r; + ngx_http_spdy_stream_t *stream; + ngx_http_spdy_srv_conf_t *sscf; + + if (size < 10) { + return NGX_AGAIN; + } + + p = *pos; + + sc->length -= 10; + *pos += 10; + + sid = ngx_spdy_frame_parse_sid(p); + prio = p[8] >> 6; + + 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->ctx, + ngx_http_spdy_module); + if (sc->processing == sscf->concurrent_streams) { + ngx_http_spdy_send_rst_stream(sc, sid, NGX_SPDY_REFUSED_STREAM, prio); + + sc->handler = ngx_http_spdy_skip_headers; + return NGX_OK; + } + + r = ngx_http_spdy_create_request(sc); + if (r == NULL) { + return NGX_ERROR; + } + + stream = ngx_pcalloc(r->pool, sizeof(ngx_http_spdy_stream_t)); + if (stream == NULL) { + return NGX_ERROR; + } + + r->spdy_stream = stream; + + stream->id = sid; + stream->request = r; + stream->connection = sc; + stream->priority = prio; + stream->half_closed = (sc->flags & NGX_SPDY_FLAG_FIN) ? 1 : 0; + + index = ngx_http_spdy_stream_index(sscf, sid); + + stream->index = sc->streams_index[index]; + sc->streams_index[index] = stream; + + cln = ngx_http_cleanup_add(r, 0); + if (cln == NULL) { + return NGX_ERROR; + } + + cln->handler = ngx_http_spdy_stream_index_cleanup; + cln->data = r; + + sc->processing++; + + sc->stream = stream; + + sc->handler = ngx_http_spdy_process_headers; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_spdy_process_data_frame(ngx_http_spdy_connection_t *sc, u_char **pos, + size_t size) +{ + u_char *p; + ssize_t n; + ngx_buf_t *buf; + ngx_uint_t complete; + ngx_temp_file_t *tf; + ngx_http_request_t *r; + ngx_http_request_body_t *rb; + + if (size >= sc->length) { + complete = 1; + size = sc->length; + + } else { + complete = 0; + sc->length -= size; + } + + r = sc->stream->request; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy DATA frame"); + + if (!r->request_body) { + if (ngx_http_spdy_init_request_body(r) != NGX_OK) { + return NGX_ERROR; + } + } + + rb = r->request_body; + tf = rb->temp_file; + buf = rb->buf; + + if (size) { + if (size > (size_t) rb->rest) { + return NGX_ERROR; + } + + rb->rest -= size; + p = *pos; + + if (tf) { + buf->start = p; + buf->pos = p; + + p += size; + + buf->end = p; + buf->last = p; + + n = ngx_write_chain_to_temp_file(tf, rb->bufs); + + /* TODO: n == 0 or not complete and level event */ + + if (n == NGX_ERROR) { + return NGX_ERROR; + } + + tf->offset += n; + + } else { + buf->last = ngx_cpymem(buf->last, p, size); + p += size; + } + + *pos = p; + } + + if (!complete) { + return NGX_AGAIN; + } + + sc->handler = ngx_http_spdy_process_frame; + + if (sc->flags & NGX_SPDY_FLAG_FIN) { + + sc->stream->half_closed = 1; + + if (tf) { + ngx_memzero(buf, sizeof(ngx_buf_t)); + + buf->in_file = 1; + buf->file_pos = 0; + buf->file_last = tf->file.offset; + buf->file = &tf->file; + + rb->buf = NULL; + } + + if (rb->post_handler) { + rb->post_handler(r); + } + } + + return NGX_DONE; +} + + +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; + + 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 + || (size_t) rb->rest > clcf->client_body_buffer_size) + { + 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->half_closed) { + if (ngx_create_temp_file(&tf->file, tf->path, tf->pool, + tf->persistent, tf->clean, tf->access) + != NGX_OK) + { + return NGX_ERROR; + } + + return NGX_OK; + } + + buf = ngx_calloc_buf(r->pool); + if (buf == NULL) { + return NGX_ERROR; + } + + } else { + + if (rb->rest == 0) { + return NGX_OK; + } + + buf = ngx_create_temp_buf(r->pool, 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; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_spdy_process_rst_stream(ngx_http_spdy_connection_t *sc, u_char **pos, + size_t size) +{ + u_char *p; + ngx_uint_t sid, status; + ngx_connection_t *fc; + ngx_http_request_t *r; + ngx_http_spdy_stream_t *stream; + + if (size < 8) { + return NGX_AGAIN; + } + + p = *pos; + + if (sc->length != 8 || sc->flags) { + return NGX_ERROR; + } + + sid = ngx_spdy_frame_parse_sid(p); + status = p[7]; + + 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 */ + break; + + case NGX_SPDY_INVALID_STREAM: + /* TODO */ + break; + + case NGX_SPDY_REFUSED_STREAM: + /* TODO */ + break; + + case NGX_SPDY_UNSUPPORTED_VERSION: + /* TODO */ + break; + + case NGX_SPDY_CANCEL: + stream = ngx_http_spdy_get_stream_by_id(sc, sid); + if (stream == NULL) { + /* TODO false cancel */ + break; + } + + r = stream->request; + r->main->count++; + + fc = r->connection; + + ngx_http_spdy_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST); + ngx_http_run_posted_requests(fc); + break; + + case NGX_SPDY_INTERNAL_ERROR: + /* TODO */ + break; + + case NGX_SPDY_FLOW_CONTROL_ERROR: + /* TODO */ + break; + + default: + return NGX_ERROR; + } + + *pos += 8; + sc->handler = ngx_http_spdy_process_frame; + + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_spdy_process_ping(ngx_http_spdy_connection_t *sc, u_char **pos, + size_t size) +{ + u_char *p, *d; + ngx_buf_t *buf; + ngx_http_spdy_out_frame_t *frame; + + static u_char ping_header[] = { 0x80, 0x02, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x04 }; + + if (size < 4) { + return NGX_AGAIN; + } + + if (sc->length != 4) { + return NGX_ERROR; + } + + p = *pos; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, "spdy PING frame"); + + frame = ngx_http_spdy_get_ctrl_frame(sc, 12, NGX_SPDY_HIGHEST_PRIORITY); + if (frame == NULL) { + return NGX_ERROR; + } + + buf = frame->first->buf; + + d = buf->pos; + d = ngx_cpymem(d, ping_header, 8); + d = ngx_cpymem(d, p, 4); + + buf->last = d; + + ngx_http_spdy_queue_frame(sc, frame); + + sc->handler = ngx_http_spdy_process_frame; + + *pos += 4; + + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_spdy_process_headers(ngx_http_spdy_connection_t *sc, u_char **pos, + size_t size) +{ + int z; + ngx_buf_t *buf; + ngx_int_t rc; + ngx_uint_t last; + ngx_table_elt_t *h; + ngx_connection_t *c; + ngx_http_request_t *r; + + c = sc->connection; + r = sc->stream->request; + buf = r->header_in; + + if (size >= sc->length) { + last = 1; + size = sc->length; + + } else { + last = 0; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "spdy process headers %d of %d", size, sc->length); + + 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_ALERT, c->log, 0, + "spdy inflateSetDictionary() failed: %d", z); + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->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_ALERT, c->log, 0, + "spdy inflate() failed: %d", z); + return NGX_ERROR; + } + + ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->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); + + *pos = sc->zstream_in.next_in; + + sc->length -= (size - sc->zstream_in.avail_in); + size = sc->zstream_in.avail_in; + + buf->last = sc->zstream_in.next_out; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "spdy headers decompressed: \"%*s\"", + buf->last - buf->pos, buf->pos); + + if (r->headers_in.headers.part.elts == NULL) { + + if (buf->last - buf->pos < 2) { + return NGX_AGAIN; + } + + sc->headers = ngx_spdy_frame_parse_uint16(buf->pos); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy headers count: %i", sc->headers); + + buf->pos += 2; + + if (ngx_list_init(&r->headers_in.headers, r->pool, sc->headers + 3, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_array_init(&r->headers_in.cookies, r->pool, 2, + sizeof(ngx_table_elt_t *)) + != NGX_OK) + { + return NGX_ERROR; + } + } + + while (sc->headers) { + + rc = ngx_http_spdy_parse_header(r, 0); + + 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_spdy_finalize_request(r, + NGX_HTTP_REQUEST_HEADER_TOO_LARGE); + + sc->handler = ngx_http_spdy_skip_headers; + return NGX_OK; + } + + if (rc != NGX_OK) { + return NGX_ERROR; + } + + *buf->pos = '\0'; + + 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_ALERT, c->log, 0, + "spdy inflate() failed: %d", z); + return NGX_ERROR; + } + + *pos = sc->zstream_in.next_in; + + buf->last = sc->zstream_in.next_out; + + sc->length -= (size - sc->zstream_in.avail_in); + size = sc->zstream_in.avail_in; + + continue; + } + + if (last) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "again while last"); + return NGX_ERROR; + } + + return NGX_AGAIN; + + default: + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "NGX_ERROR"); + return NGX_ERROR; + } + + if (r->invalid_header) { + + /* there was error while a header line parsing */ + + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid header line: \"%*s\"", + r->header_end - r->header_name_start, + r->header_name_start); + continue; + } + + /* a header line has been parsed successfully */ + + switch (r->header_hash) { + + case NGX_SPDY_URL_HEADER_HASH: + + if (r->lowcase_index == 3) { + + if (ngx_http_spdy_parse_uri(r) != NGX_OK) { + return NGX_ERROR; + } + + 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); + + continue; + } + + break; + + case NGX_SPDY_METHOD_HEADER_HASH: + + if (r->lowcase_index == 6) { + + if (ngx_http_spdy_parse_method(r) != NGX_OK) { + return NGX_ERROR; + } + + continue; + } + + break; + + case NGX_SPDY_SCHEME_HEADER_HASH: + + if (r->lowcase_index == 6) { + r->schema_start = r->header_start; + r->schema_end = r->header_end; + continue; + } + + break; + + case NGX_SPDY_VERSION_HEADER_HASH: + + if (r->lowcase_index == 7) { + + if (ngx_http_spdy_parse_version(r) != NGX_OK) { + return NGX_ERROR; + } + + continue; + } + + break; + } + + h = ngx_list_push(&r->headers_in.headers); + if (h == NULL) { + return NGX_ERROR; + } + + h->hash = r->header_hash; + + h->key.len = r->header_name_end - r->header_name_start; + h->key.data = r->header_name_start; + + h->value.len = r->header_size; + h->value.data = r->header_start; + + h->lowcase_key = h->key.data; + } + + if (!last) { + return NGX_AGAIN; + } + + if (buf->pos != buf->last) { + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "end %i %d %d", last, buf->pos, buf->last); + return NGX_ERROR; + } + + *buf->pos = '\0'; + + ngx_http_spdy_run_request(r); + + sc->handler = ngx_http_spdy_process_frame; + + return NGX_DONE; +} + + +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 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_connection_t *fc; + ngx_http_header_t *hh; + ngx_http_core_main_conf_t *cmcf; + + if (ngx_http_spdy_construct_request_line(r) != NGX_OK) { + ngx_http_spdy_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + fc = r->connection; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->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, fc->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; + } + + if (r->plain_http) { + ngx_log_error(NGX_LOG_INFO, fc->log, 0, + "client sent plain HTTP request to HTTPS port"); + ngx_http_spdy_finalize_request(r, NGX_HTTP_TO_HTTPS); + return; + } + +#if (NGX_STAT_STUB) + (void) ngx_atomic_fetch_add(ngx_stat_reading, -1); + r->stat_reading = 0; + (void) ngx_atomic_fetch_add(ngx_stat_writing, 1); + r->stat_writing = 1; +#endif + + r->write_event_handler = ngx_http_core_run_phases; + + ngx_http_core_run_phases(r); + ngx_http_run_posted_requests(fc); +} + + +static ngx_int_t +ngx_http_spdy_skip_headers(ngx_http_spdy_connection_t *sc, u_char **pos, + size_t size) +{ + int n; + u_char buffer[NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE]; + + 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) { + return NGX_ERROR; + } + } + + *pos = sc->zstream_in.next_in; + + if (size < sc->length) { + sc->length -= size; + return NGX_AGAIN; + } + + sc->handler = ngx_http_spdy_process_frame; + + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_spdy_parse_header(ngx_http_request_t *r, + ngx_uint_t allow_underscores) +{ + u_char *p, *end, ch; + ngx_uint_t len, hash; + 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 < 2) { + return NGX_AGAIN; + } + + len = ngx_spdy_frame_parse_uint16(p); + + if (!len) { + return NGX_ERROR; + } + + *p = '\0'; + + p += 2; + + 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; + } + + r->header_name_start = p; + + hash = 0; + + for ( /* void */ ; p != r->header_name_end; p++) { + + ch = *p; + + if ((ch >= 'a' && ch <= 'z') + || (ch == '-') + || (ch >= '0' && ch <= '9') + || (ch == '_' && allow_underscores)) + { + hash = ngx_hash(hash, ch); + continue; + } + + return NGX_ERROR; + } + + r->header_hash = hash; + + state = sw_value_len; + + /* fall through */ + + case sw_value_len: + + if (end - p < 2) { + break; + } + + len = ngx_spdy_frame_parse_uint16(p); + + if (!len) { + return NGX_ERROR; + } + + *p = '\0'; + + p += 2; + + 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_ERROR; + } + } + + 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_parse_version(ngx_http_request_t *r) +{ + u_char *p, ch; + + if (r->http_protocol.len) { + return NGX_ERROR; + } + + p = r->header_start; + + if (r->header_size < 8 || !(ngx_str5cmp(p, 'H', 'T', 'T', 'P', '/'))) { + return NGX_ERROR; + } + + ch = *(p + 5); + + if (ch < '1' || ch > '9') { + return NGX_ERROR; + } + + r->http_major = ch - '0'; + + for (p += 6; p != r->header_end - 2; p++) { + + ch = *p; + + if (ch < '0' || ch > '9') { + return NGX_ERROR; + } + + r->http_major = r->http_major * 10 + ch - '0'; + } + + if (*p != '.') { + return NGX_ERROR; + } + + ch = *(p + 1); + + if (ch < '0' || ch > '9') { + return NGX_ERROR; + } + + r->http_minor = ch - '0'; + + for (p += 2; p != r->header_end; p++) { + + ch = *p; + + if (ch < '0' || ch > '9') { + return NGX_ERROR; + } + + 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_parse_method(ngx_http_request_t *r) +{ + u_char *p, *m; + size_t k, len; + ngx_uint_t n; + ngx_http_spdy_method_test_t *test; + + /* + * 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 ngx_http_spdy_method_test_t tests[] ngx_aligned(64) = { + { 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 } + }; + + if (r->method_name.len) { + return NGX_ERROR; + } + + len = r->header_size; + + r->method_name.len = len; + r->method_name.data = r->header_start; + + test = tests; + n = sizeof(tests) / sizeof(ngx_http_spdy_method_test_t); + + 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); + + return NGX_ERROR; +} + + +static ngx_int_t +ngx_http_spdy_parse_uri(ngx_http_request_t *r) +{ + ngx_http_core_srv_conf_t *cscf; + + if (r->unparsed_uri.len) { + return NGX_ERROR; + } + + r->uri_start = r->header_start; + r->uri_end = r->header_end; + + if (ngx_http_parse_uri(r) != NGX_OK) { + return NGX_ERROR; + } + + if (r->args_start) { + r->uri.len = r->args_start - 1 - r->uri_start; + } else { + r->uri.len = r->header_size; + } + + if (r->complex_uri || r->quoted_uri) { + + r->uri.data = ngx_pnalloc(r->pool, r->uri.len + 1); + if (r->uri.data == NULL) { + 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) { + return NGX_ERROR; + } + + } else { + r->uri.data = r->uri_start; + } + + r->unparsed_uri.len = r->header_size; + 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"); + 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 + + 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) + { + 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) { + 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 ngx_http_spdy_out_frame_t * +ngx_http_spdy_get_ctrl_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_ctrl_frames; + + if (frame) { + sc->free_ctrl_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_CTRL_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_ctrl_frame_handler; + } + + frame->free = NULL; + +#if (NGX_DEBUG) + if (size > NGX_SPDY_CTRL_FRAME_BUFFER_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->blocked = 0; + frame->priority = priority; + + return frame; +} + + +static ngx_int_t +ngx_http_spdy_ctrl_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_ctrl_frames; + sc->free_ctrl_frames = frame; + + return NGX_OK; +} + + +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; + + static u_char rst_stream_header[] = { 0x80, 0x02, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x08 }; + + 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_ctrl_frame(sc, 16, priority); + if (frame == NULL) { + return NGX_ERROR; + } + + buf = frame->first->buf; + + p = buf->pos; + p = ngx_cpymem(p, rst_stream_header, 8); + + p = ngx_spdy_frame_aligned_write_uint32(p, sid); + p = ngx_spdy_frame_aligned_write_uint32(p, status); + + buf->last = p; + + ngx_http_spdy_queue_frame(sc, frame); + + return NGX_OK; +} + + +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; + + static u_char settings_header[] = { 0x80, 0x02, 0x00, 0x04, + 0x01, 0x00, 0x00, 0x0c }; + + 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, 20); + 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 = 20; +#endif + frame->blocked = 0; + frame->priority = NGX_SPDY_HIGHEST_PRIORITY; + + p = buf->pos; + + p = ngx_cpymem(p, settings_header, 8); + + p = ngx_spdy_frame_aligned_write_uint32(p, 1); + + p = ngx_spdy_frame_aligned_write_uint32(p, 0x04000001); + + sscf = ngx_http_get_module_srv_conf(sc->http_connection->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 void +ngx_http_spdy_writer(ngx_http_request_t *r) +{ + ngx_int_t rc; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "spdy writer handler: \"%V?%V\"", &r->uri, &r->args); + + rc = ngx_http_output_filter(r, NULL); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "spdy writer output filter: %d, \"%V?%V\"", + rc, &r->uri, &r->args); + + if (rc == NGX_ERROR) { + ngx_http_spdy_finalize_request(r, rc); + return; + } + + if (r->buffered || r->postponed + || (r == r->main && r->connection->buffered)) + { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "SPDY DEBUG: %i %i", r->buffered, r->postponed); + return; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "spdy writer done: \"%V?%V\"", &r->uri, &r->args); + + r->write_event_handler = ngx_http_request_empty_handler; + + ngx_http_spdy_finalize_request(r, rc); +} + + +static u_char * +ngx_http_spdy_log_error_handler(ngx_http_request_t *r, ngx_http_request_t *sr, + u_char *buf, size_t len) +{ + char *uri_separator; + u_char *p; + ngx_http_upstream_t *u; + ngx_http_core_srv_conf_t *cscf; + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + p = ngx_snprintf(buf, len, ", server: %V", &cscf->server_name); + len -= p - buf; + buf = p; + + if (r->request_line.data == NULL && r->request_start) { + for (p = r->request_start; p < r->header_in->last; p++) { + if (*p == CR || *p == LF) { + break; + } + } + + r->request_line.len = p - r->request_start; + r->request_line.data = r->request_start; + } + + if (r->request_line.len) { + p = ngx_snprintf(buf, len, ", request: \"%V\"", &r->request_line); + len -= p - buf; + buf = p; + } + + if (r != sr) { + p = ngx_snprintf(buf, len, ", subrequest: \"%V\"", &sr->uri); + len -= p - buf; + buf = p; + } + + u = sr->upstream; + + if (u && u->peer.name) { + + uri_separator = ""; + +#if (NGX_HAVE_UNIX_DOMAIN) + if (u->peer.sockaddr && u->peer.sockaddr->sa_family == AF_UNIX) { + uri_separator = ":"; + } +#endif + + p = ngx_snprintf(buf, len, ", upstream: \"%V%V%s%V\"", + &u->schema, u->peer.name, + uri_separator, &u->uri); + len -= p - buf; + buf = p; + } + + if (r->headers_in.host) { + p = ngx_snprintf(buf, len, ", host: \"%V\"", + &r->headers_in.host->value); + len -= p - buf; + buf = p; + } + + if (r->headers_in.referer) { + p = ngx_snprintf(buf, len, ", referrer: \"%V\"", + &r->headers_in.referer->value); + buf = p; + } + + return buf; +} + + +void +ngx_http_spdy_finalize_request(ngx_http_request_t *r, ngx_int_t rc) +{ + ngx_connection_t *fc; + ngx_http_request_t *mr, *pr; + ngx_http_core_loc_conf_t *clcf; + + fc = r->connection; + mr = r->main; + + ngx_log_debug5(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "spdy finalize request: %d, \"%V?%V\" a:%d, c:%d", + rc, &r->uri, &r->args, r == fc->data, mr->count); + + if (rc == NGX_DONE) { + ngx_http_spdy_close_request(r, rc); + return; + } + + if (rc == NGX_OK && r->filter_finalize) { + fc->error = 1; + } + + if (rc == NGX_DECLINED) { + r->content_handler = NULL; + r->write_event_handler = ngx_http_core_run_phases; + ngx_http_core_run_phases(r); + return; + } + + if (r != mr && r->post_subrequest) { + rc = r->post_subrequest->handler(r, r->post_subrequest->data, rc); + } + + if (rc == NGX_ERROR + || rc == NGX_HTTP_REQUEST_TIME_OUT + || rc == NGX_HTTP_CLIENT_CLOSED_REQUEST + || fc->error) + { + if (mr->blocked) { + r->write_event_handler = ngx_http_spdy_request_finalizer; + } + + ngx_http_spdy_terminate_request(r, rc); + return; + } + + if (rc >= NGX_HTTP_SPECIAL_RESPONSE + || rc == NGX_HTTP_CREATED + || rc == NGX_HTTP_NO_CONTENT) + { + if (rc == NGX_HTTP_CLOSE) { + ngx_http_spdy_terminate_request(r, rc); + return; + } + + ngx_http_spdy_finalize_request(r, + ngx_http_special_response_handler(r, rc)); + return; + } + + if (r != mr) { + + if (r->buffered || r->postponed) { + + r->http_state = NGX_HTTP_WRITING_REQUEST_STATE; + r->write_event_handler = ngx_http_spdy_writer; + + return; + } + + pr = r->parent; + + if (r == fc->data) { + + mr->count--; + mr->subrequests++; + + if (!r->logged) { + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->log_subrequest) { + ngx_http_log_request(r); + } + + r->logged = 1; + + } else { + ngx_log_error(NGX_LOG_ALERT, fc->log, 0, + "subrequest: \"%V?%V\" logged again", + &r->uri, &r->args); + } + + r->done = 1; + + if (pr->postponed && pr->postponed->request == r) { + pr->postponed = pr->postponed->next; + } + + fc->data = pr; + + } else { + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "spdy finalize non-active request: \"%V?%V\"", + &r->uri, &r->args); + + r->write_event_handler = ngx_http_spdy_request_finalizer; + + if (r->waited) { + r->done = 1; + } + } + + if (ngx_http_post_request(pr, NULL) != NGX_OK) { + mr->count++; + ngx_http_spdy_terminate_request(r, 0); + return; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "spdy wake parent request: \"%V?%V\"", + &pr->uri, &pr->args); + + return; + } + + if (r->buffered || fc->buffered || r->postponed || r->blocked) { + + r->http_state = NGX_HTTP_WRITING_REQUEST_STATE; + r->write_event_handler = ngx_http_spdy_writer; + + return; + } + + if (r != fc->data) { + ngx_log_error(NGX_LOG_ALERT, fc->log, 0, + "spdy finalize non-active request: \"%V?%V\"", + &r->uri, &r->args); + return; + } + + r->done = 1; + r->request_complete = 1; + + r->write_event_handler = ngx_http_request_empty_handler; + + ngx_http_spdy_close_request(r, 0); +} + + +static void +ngx_http_spdy_request_finalizer(ngx_http_request_t *r) +{ + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "spdy finalizer done: \"%V?%V\"", &r->uri, &r->args); + + ngx_http_spdy_finalize_request(r, 0); +} + + +static void +ngx_http_spdy_terminate_request(ngx_http_request_t *r, ngx_int_t rc) +{ + ngx_uint_t blocked; + ngx_http_cleanup_t *cln; + ngx_http_ephemeral_t *e; + ngx_http_spdy_stream_t *stream; + ngx_http_spdy_out_frame_t *frame, **fn; + ngx_http_spdy_connection_t *sc; + + r = r->main; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "spdy terminate request count:%d", r->count); + + if (rc > 0 && r->headers_out.status == 0) { + r->headers_out.status = rc; + } + + cln = r->cleanup; + r->cleanup = NULL; + + while (cln) { + if (cln->handler) { + cln->handler(cln->data); + } + + cln = cln->next; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "spdy terminate cleanup count:%d blk:%d", + r->count, r->blocked); + + if (r->write_event_handler) { + + stream = r->spdy_stream; + sc = stream->connection; + + fn = &sc->last_out; + + blocked = 0; + + for ( ;; ) { + frame = *fn; + + if (frame == NULL) { + break; + } + + if (frame->stream == stream) { + + if (!frame->blocked) { + stream->waiting--; + *fn = frame->next; + continue; + } + + blocked = 1; + } + + fn = &frame->next; + } + + if (r->blocked) { + return; + } + + r->posted_requests = NULL; + r->write_event_handler = ngx_http_spdy_terminate_handler; + + if (blocked) { + return; + } + + e = ngx_http_ephemeral(r); + (void) ngx_http_post_request(r, &e->terminal_posted_request); + return; + } + + r->count = 1; + + ngx_http_spdy_close_request(r, rc); +} + + +static void +ngx_http_spdy_terminate_handler(ngx_http_request_t *r) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "spdy terminate handler count:%d", r->count); + + r->count = 1; + + ngx_http_spdy_close_request(r, 0); +} + + +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_close_request(ngx_http_request_t *r, ngx_int_t rc) +{ + ngx_event_t *rev; + ngx_http_spdy_connection_t *sc; + + r = r->main; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "spdy request count:%d blk:%d", r->count, r->blocked); + + if (r->count == 0) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "spdy request count is zero"); + } + + r->count--; + + if (r->count || r->blocked) { + return; + } + + sc = r->spdy_stream->connection; + sc->processing--; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "spdy processing:%d", sc->processing); + + ngx_http_spdy_free_request(r, rc); + + if (sc->processing || sc->blocked || sc->last_out) { + return; + } + + rev = sc->connection->read; + + rev->handler = ngx_http_spdy_handle_connection_handler; + ngx_post_event(rev, &ngx_posted_events); +} + + +static void +ngx_http_spdy_free_request(ngx_http_request_t *r, ngx_int_t rc) +{ + ngx_connection_t *fc; + ngx_http_cleanup_t *cln; + ngx_http_spdy_connection_t *sc; + + fc = r->connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, "spdy free request"); + + for (cln = r->cleanup; cln; cln = cln->next) { + if (cln->handler) { + cln->handler(cln->data); + } + } + + sc = r->main->spdy_stream->connection; + +#if (NGX_STAT_STUB) + + if (r->stat_reading) { + (void) ngx_atomic_fetch_add(ngx_stat_reading, -1); + } + + if (r->stat_writing) { + (void) ngx_atomic_fetch_add(ngx_stat_writing, -1); + } + +#endif + + if (rc > 0 && (r->headers_out.status == 0 || fc->sent == 0)) { + r->headers_out.status = rc; + } + + fc->log->action = "logging request"; + + ngx_http_log_request(r); + + fc->log->action = "closing request"; + + if (fc->write->timer_set) { + ngx_del_timer(fc->write); + } + + if (fc->write->prev) { + ngx_delete_posted_event(fc->write); + } + + fc->destroyed = 1; + + fc->data = sc->free_fake_connections; + sc->free_fake_connections = fc; + + ngx_destroy_pool(r->pool); +} + + +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->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_ctrl_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; + + ngx_add_timer(c->read, sscf->keepalive_timeout); +} + + +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->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; + + sc->last_out = NULL; + + sc->blocked = 1; + + sscf = ngx_http_get_module_srv_conf(sc->http_connection->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; + + stream = stream->index; + + fc = r->connection; + fc->error = 1; + + r->main->count++; + + ngx_http_spdy_finalize_request(r, rc); + ngx_http_run_posted_requests(fc); + } + } + + sc->blocked = 0; + + if (!sc->processing) { + ngx_http_close_connection(c); + return; + } + + ev = c->read; + + if (ev->timer_set) { + ngx_del_timer(ev); + } + + if (ev->prev) { + ngx_delete_posted_event(ev); + } + + if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) { + ngx_del_event(ev, NGX_READ_EVENT, 0); + } + + ev = c->write; + + if (ev->timer_set) { + ngx_del_timer(ev); + } + + if (ev->prev) { + ngx_delete_posted_event(ev); + } + + if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) { + ngx_del_event(ev, NGX_WRITE_EVENT, 0); + } +} diff -r 87a4e0a9d241 -r aba1fef8b060 src/http/ngx_http_spdy.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/http/ngx_http_spdy.h Sun Jan 27 06:37:15 2013 +0400 @@ -0,0 +1,182 @@ +/* + * 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" +#endif + +#define NGX_SPDY_STATE_BUFFER_SIZE 9 + + +typedef struct ngx_http_spdy_connection_s ngx_http_spdy_connection_t; +typedef struct ngx_http_spdy_stream_s ngx_http_spdy_stream_t; +typedef struct ngx_http_spdy_out_frame_s ngx_http_spdy_out_frame_t; + + +typedef ngx_int_t (*ngx_http_spdy_handler_pt) (ngx_http_spdy_connection_t *sc, + u_char **pos, size_t size); + +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_ctrl_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; + + unsigned blocked:2; + unsigned waiting:1; +}; + + +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 half_closed:1; +}; + + +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 blocked; + ngx_uint_t priority; +}; + + +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->blocked && (*out)->blocked) + || frame->priority >= (*out)->priority) + { + break; + } + } + + frame->next = *out; + *out = frame; +} + + +void ngx_http_spdy_init(ngx_event_t *rev); +void ngx_http_spdy_finalize_request(ngx_http_request_t *r, ngx_int_t rc); + +ngx_int_t ngx_http_spdy_alloc_recv_buffer(ngx_cycle_t *cycle); + +ngx_int_t ngx_http_spdy_init_request_body(ngx_http_request_t *r); + +ngx_int_t ngx_http_spdy_send_output_queue(ngx_http_spdy_connection_t *sc); + +#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_frame_aligned_write_uint16(p, s) \ + (*(uint16_t *) (p) = htons(s), (p) + sizeof(uint16_t)) + +#define ngx_spdy_frame_aligned_write_uint32(p, s) \ + (*(uint32_t *) (p) = htonl(s), (p) + sizeof(uint32_t)) + +#define ngx_spdy_frame_aligned_write_flags_and_len(p, f, s) \ + (*(uint32_t *) (p) = htonl((f) << 24 | (s)), (p) + sizeof(uint32_t)) + +#if (NGX_HAVE_NONALIGNED) + +#if (NGX_HAVE_LITTLE_ENDIAN) +#define ngx_http_spdy_detect(p) ((*(uint32_t *) p << 8) == 0x00028000) +#else +#define ngx_http_spdy_detect(p) ((*(uint32_t *) p >> 8) == 0x00800200) +#endif + +#define ngx_spdy_frame_write_uint16 ngx_spdy_frame_aligned_write_uint16 +#define ngx_spdy_frame_write_uint32 ngx_spdy_frame_aligned_write_uint32 +#define ngx_spdy_frame_write_flags_and_len \ + ngx_spdy_frame_aligned_write_flags_and_len + +#else + +#define ngx_http_spdy_detect(p) (p[0] == 0x80 && p[1] == 0x02 && p[2] == 0x00) + +#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)) + +#define ngx_spdy_frame_write_flags_and_len(p, f, s) \ + ((p)[0] = (u_char) (f), \ + (p)[1] = (u_char) ((s) >> 16), \ + (p)[2] = (u_char) ((s) >> 8), \ + (p)[3] = (u_char) (s), (p) + sizeof(uint32_t)) + +#endif + +#endif /* _NGX_HTTP_SPDY_H_INCLUDED_ */ diff -r 87a4e0a9d241 -r aba1fef8b060 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 Sun Jan 27 06:37:15 2013 +0400 @@ -0,0 +1,902 @@ + +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Valentin V. Bartenev + */ + + +#include +#include +#include +#include + +#include + + +#define NGX_SPDY_WRITE_BUFFERED NGX_HTTP_WRITE_BUFFERED + +#define ngx_http_spdy_header_sizeof(h) (2 + sizeof(h) - 1) + +#define ngx_http_spdy_header_write(p, h) \ + ngx_cpymem(ngx_spdy_frame_write_uint16(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_inline void ngx_http_spdy_handle_stream( + ngx_http_spdy_connection_t *sc, 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, ngx_uint_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_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_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 = 8 + 6 + 2 + + ngx_http_spdy_header_sizeof("version") + + ngx_http_spdy_header_sizeof("HTTP/1.x") + + ngx_http_spdy_header_sizeof("status") + + ngx_http_spdy_header_sizeof("xxx"); + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (r->headers_out.server == NULL) { + len += ngx_http_spdy_header_sizeof("server"); + len += clcf->server_tokens ? ngx_http_spdy_header_sizeof(NGINX_VER): + ngx_http_spdy_header_sizeof("nginx"); + } + + if (r->headers_out.date == NULL) { + len += ngx_http_spdy_header_sizeof("date") + + ngx_http_spdy_header_sizeof("Wed, 31 Dec 1986 10:00:00 GMT"); + } + + if (r->headers_out.content_type.len) { + len += ngx_http_spdy_header_sizeof("content-type") + + 2 + 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_header_sizeof("content-length") + + 2 + NGX_OFF_T_LEN; + } + + if (r->headers_out.last_modified == NULL + && r->headers_out.last_modified_time != -1) + { + len += ngx_http_spdy_header_sizeof("last-modified") + + ngx_http_spdy_header_sizeof("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_header_sizeof("location") + + ngx_http_spdy_header_sizeof("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 += sizeof(uint16_t) + header[i].key.len + + sizeof(uint16_t) + header[i].value.len; + } + + buf = ngx_alloc(len, r->pool->log); + if (buf == NULL) { + return NGX_ERROR; + } + + last = buf + sizeof(uint16_t); + + last = ngx_http_spdy_header_write(last, "version"); + last = ngx_http_spdy_header_write(last, "HTTP/1.1"); + + last = ngx_http_spdy_header_write(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_header_write(last, "server"); + last = clcf->server_tokens ? + ngx_http_spdy_header_write(last, NGINX_VER): + ngx_http_spdy_header_write(last, "nginx"); + count++; + } + + if (r->headers_out.date == NULL) { + last = ngx_http_spdy_header_write(last, "date"); + + last = ngx_spdy_frame_write_uint16(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_header_write(last, "content-type"); + + p = last + sizeof(uint16_t); + + 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_spdy_frame_write_uint16(p - sizeof(uint16_t), + 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_header_write(last, "content-length"); + + p = last + sizeof(uint16_t); + + last = ngx_sprintf(p, "%O", r->headers_out.content_length_n); + + (void) ngx_spdy_frame_write_uint16(p - sizeof(uint16_t), last - p); + + count++; + } + + if (r->headers_out.last_modified == NULL + && r->headers_out.last_modified_time != -1) + { + last = ngx_http_spdy_header_write(last, "last-modified"); + + p = last + sizeof(uint16_t); + + last = ngx_http_time(p, r->headers_out.last_modified_time); + + (void) ngx_spdy_frame_write_uint16(p - sizeof(uint16_t), last - p); + + count++; + } + + if (host.data) { + + last = ngx_http_spdy_header_write(last, "location"); + + p = last + sizeof(uint16_t); + + 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_spdy_frame_write_uint16(p - sizeof(uint16_t), + 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_spdy_frame_write_uint16(last, header[i].key.len); + + ngx_strlow(last, header[i].key.data, header[i].key.len); + last += header[i].key.len; + + p = last + sizeof(uint16_t); + + 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_spdy_frame_write_uint16(p - sizeof(uint16_t), 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, 14 + deflateBound(&sc->zstream_out, len)); + if (b == NULL) { + ngx_free(buf); + return NGX_ERROR; + } + + b->last += 14; + + 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_aligned_write_uint32(p, 0x80020002); + + len = b->last - b->pos; + + r->header_size = len; + + if (r->header_only) { + b->last_buf = 1; + p = ngx_spdy_frame_aligned_write_flags_and_len(p, NGX_SPDY_FLAG_FIN, + len - 8); + } else { + p = ngx_spdy_frame_aligned_write_flags_and_len(p, 0, len - 8); + } + + (void) ngx_spdy_frame_aligned_write_uint32(p, r->spdy_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->blocked = 1; + frame->priority = stream->priority; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0, + "spdy:%ui create SYN_REPLY frame %p: size:%ui", + stream->id, frame, frame->size); + + ngx_http_spdy_queue_frame(sc, frame); + + 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_uint_t flags; + ngx_chain_t *cl, *ll, *out, **ln; + ngx_http_spdy_stream_t *stream; + ngx_http_spdy_out_frame_t *frame; + + stream = r->main->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 || stream->next) { + return NGX_AGAIN; + } + + 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; + } + + flags = b->last_buf ? NGX_SPDY_FLAG_FIN : 0; + + frame = ngx_http_spdy_filter_get_data_frame(stream, size, flags, out, cl); + if (frame == NULL) { + return NGX_ERROR; + } + + ngx_http_spdy_queue_frame(stream->connection, frame); + + stream->waiting++; + + 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, + ngx_uint_t len, ngx_uint_t flags, ngx_chain_t *first, ngx_chain_t *last) +{ + u_char *p; + ngx_buf_t *buf; + 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:%i flags:%ui", + stream->id, frame, len, flags); + + if (len || flags) { + 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_aligned_write_flags_and_len(p, flags, len); + + } else { + p = ngx_palloc(stream->request->pool, 8); + if (p == NULL) { + return NULL; + } + + buf->pos = p; + buf->start = p; + + p = ngx_spdy_frame_aligned_write_uint32(p, stream->id); + + p = ngx_spdy_frame_aligned_write_flags_and_len(p, flags, len); + + buf->last = p; + buf->end = p; + + buf->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_module; + } + + 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 = 8 + len; + frame->blocked = 0; + frame->priority = stream->priority; + + 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; + } + + return NGX_OK; +} + + +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->waiting == 0) { + fc->buffered &= ~NGX_HTTP_WRITE_BUFFERED; + } + + if (stream->handled) { + return; + } + + if (sc->blocked == 2) { + stream->handled = 1; + + stream->next = sc->last_stream; + sc->last_stream = stream; + } +} + + +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); + + stream->request->connection->sent += frame->size; + + frame->free = stream->free_frames; + stream->free_frames = frame; + + stream->waiting--; + + 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_request_t *r; + 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) { + 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); + } + + 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); + + r = stream->request; + + r->header_size += 8; + r->connection->sent += frame->size; + + frame->free = stream->free_frames; + stream->free_frames = frame; + + stream->waiting--; + + ngx_http_spdy_handle_stream(sc, stream); + + return NGX_OK; +} + + +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 87a4e0a9d241 -r aba1fef8b060 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 Sun Jan 27 06:37:15 2013 +0400 @@ -0,0 +1,299 @@ + +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Valentin V. Bartenev + */ + + +#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 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_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_handler_pt ngx_http_core_pool_size_p = + ngx_http_core_pool_size; +static ngx_conf_post_handler_pt + ngx_http_spdy_streams_index_mask_p = 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), + NULL }, + + { 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_core_pool_size_p }, + + { 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_p }, + + { 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 */ + NULL, /* init module */ + ngx_http_spdy_alloc_recv_buffer, /* 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] = r->spdy_stream->priority | 0x30; + + return NGX_OK; + } + + *v = ngx_http_variable_null_value; + + 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 = 1024 * 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, + Z_NO_COMPRESSION); + + 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 & mask) { + return "must be a power of two"; + } + + *np = mask; + + return NGX_CONF_OK; +} diff -r 87a4e0a9d241 -r aba1fef8b060 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 Sun Jan 27 06:37:15 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 { + ngx_flag_t enable; + size_t recv_buffer_size; +} 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 87a4e0a9d241 -r aba1fef8b060 src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c Sun Jan 27 06:37:14 2013 +0400 +++ b/src/http/ngx_http_upstream.c Sun Jan 27 06:37:15 2013 +0400 @@ -427,6 +427,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); }