diff --git a/patches/nginx__http2_spdy.patch b/patches/nginx__http2_spdy.patch new file mode 100644 index 0000000..c22401e --- /dev/null +++ b/patches/nginx__http2_spdy.patch @@ -0,0 +1,6263 @@ +From 4bb4f986c696fa4ca267481a55de461980760b75 Mon Sep 17 00:00:00 2001 +From: Jiale Zhi +Date: Wed, 14 Oct 2015 17:51:06 -0700 +Subject: [PATCH] Add SPDY support back to Nginx with HTTP/2 + +--- + auto/modules | 13 + + auto/options | 3 + + auto/sources | 8 + + src/core/ngx_connection.h | 1 + + src/http/modules/ngx_http_ssl_module.c | 52 +- + src/http/ngx_http.c | 27 + + src/http/ngx_http.h | 6 + + src/http/ngx_http_core_module.c | 22 +- + src/http/ngx_http_core_module.h | 6 + + src/http/ngx_http_request.c | 67 + + src/http/ngx_http_request.h | 3 + + src/http/ngx_http_request_body.c | 13 + + src/http/ngx_http_spdy.c | 3701 ++++++++++++++++++++++++++++++++ + src/http/ngx_http_spdy.h | 261 +++ + src/http/ngx_http_spdy_filter_module.c | 1222 +++++++++++ + src/http/ngx_http_spdy_module.c | 408 ++++ + src/http/ngx_http_spdy_module.h | 41 + + src/http/ngx_http_upstream.c | 11 + + 18 files changed, 5856 insertions(+), 9 deletions(-) + create mode 100644 src/http/ngx_http_spdy.c + create mode 100644 src/http/ngx_http_spdy.h + create mode 100644 src/http/ngx_http_spdy_filter_module.c + create mode 100644 src/http/ngx_http_spdy_module.c + create mode 100644 src/http/ngx_http_spdy_module.h + +diff --git a/auto/modules b/auto/modules +index 5c734e1..ef18f20 100644 +--- a/auto/modules ++++ b/auto/modules +@@ -95,6 +95,7 @@ fi + # ngx_http_header_filter + # ngx_http_chunked_filter + # ngx_http_v2_filter ++# ngx_http_spdy_filter + # ngx_http_range_header_filter + # ngx_http_gzip_filter + # ngx_http_postpone_filter +@@ -119,6 +120,10 @@ if [ $HTTP_V2 = YES ]; then + HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_V2_FILTER_MODULE" + fi + ++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 +@@ -188,6 +193,14 @@ if [ $HTTP_V2 = YES ]; then + HTTP_SRCS="$HTTP_SRCS $HTTP_V2_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 --git a/auto/options b/auto/options +index efc8943..75e5c03 100644 +--- a/auto/options ++++ b/auto/options +@@ -59,6 +59,7 @@ HTTP_CHARSET=YES + HTTP_GZIP=YES + HTTP_SSL=NO + HTTP_V2=NO ++HTTP_SPDY=NO + HTTP_SSI=YES + HTTP_POSTPONE=NO + HTTP_REALIP=NO +@@ -211,6 +212,7 @@ do + + --with-http_ssl_module) HTTP_SSL=YES ;; + --with-http_v2_module) HTTP_V2=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 ;; +@@ -379,6 +381,7 @@ cat << END + + --with-http_ssl_module enable ngx_http_ssl_module + --with-http_v2_module enable ngx_http_v2_module ++ --with-http_spdy_module enable ngx_http_spdy_module + --with-http_realip_module enable ngx_http_realip_module + --with-http_addition_module enable ngx_http_addition_module + --with-http_xslt_module enable ngx_http_xslt_module +diff --git a/auto/sources b/auto/sources +index 2abbc60..281bb92 100644 +--- a/auto/sources ++++ b/auto/sources +@@ -329,6 +329,14 @@ HTTP_V2_SRCS="src/http/v2/ngx_http_v2.c \ + src/http/v2/ngx_http_v2_module.c \ + src/http/v2/ngx_http_v2_filter_module.c" + ++HTTP_SPDY_MODULE=ngx_http_spdy_module ++HTTP_SPDY_FILTER_MODULE=ngx_http_spdy_filter_module ++HTTP_SPDY_DEPS="src/http/ngx_http_spdy.h \ ++ src/http/ngx_http_spdy_module.h" ++HTTP_SPDY_SRCS="src/http/ngx_http_spdy.c \ ++ src/http/ngx_http_spdy_module.c \ ++ src/http/ngx_http_spdy_filter_module.c" ++ + + HTTP_CHARSET_FILTER_MODULE=ngx_http_charset_filter_module + HTTP_CHARSET_SRCS=src/http/modules/ngx_http_charset_filter_module.c +diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h +index 977f028..ee0e62a 100644 +--- a/src/core/ngx_connection.h ++++ b/src/core/ngx_connection.h +@@ -119,6 +119,7 @@ typedef enum { + #define NGX_LOWLEVEL_BUFFERED 0x0f + #define NGX_SSL_BUFFERED 0x01 + #define NGX_HTTP_V2_BUFFERED 0x02 ++#define NGX_SPDY_BUFFERED 0x04 + + + struct ngx_connection_s { +diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c +index 7b051ea..97b71e7 100644 +--- a/src/http/modules/ngx_http_ssl_module.c ++++ b/src/http/modules/ngx_http_ssl_module.c +@@ -326,10 +326,10 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, + #if (NGX_DEBUG) + unsigned int i; + #endif +-#if (NGX_HTTP_V2) ++#if (NGX_HTTP_V2 || NGX_HTTP_SPDY) + ngx_http_connection_t *hc; + #endif +-#if (NGX_HTTP_V2 || NGX_DEBUG) ++#if (NGX_HTTP_V2 || NGX_HTTP_SPDY || NGX_DEBUG) + ngx_connection_t *c; + + c = ngx_ssl_get_connection(ssl_conn); +@@ -342,9 +342,20 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, + } + #endif + +-#if (NGX_HTTP_V2) ++#if (NGX_HTTP_V2 || NGX_HTTP_SPDY) + hc = c->data; ++#endif + ++#if (NGX_HTTP_V2 && NGX_HTTP_SPDY) ++ if (hc->addr_conf->http2 && hc->addr_conf->spdy) { ++ srv = (unsigned char *) NGX_HTTP_V2_ALPN_ADVERTISE ++ NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE; ++ srvlen = sizeof(NGX_HTTP_V2_ALPN_ADVERTISE NGX_SPDY_NPN_ADVERTISE ++ NGX_HTTP_NPN_ADVERTISE) - 1; ++ ++ } else ++#endif ++#if (NGX_HTTP_V2) + if (hc->addr_conf->http2) { + srv = + (unsigned char *) NGX_HTTP_V2_ALPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE; +@@ -352,6 +363,13 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, + + } else + #endif ++#if (NGX_HTTP_SPDY) ++ if (hc->addr_conf->spdy) { ++ srv = (unsigned char *) NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE; ++ srvlen = sizeof(NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1; ++ ++ } else ++#endif + { + srv = (unsigned char *) NGX_HTTP_NPN_ADVERTISE; + srvlen = sizeof(NGX_HTTP_NPN_ADVERTISE) - 1; +@@ -379,26 +397,50 @@ static int + ngx_http_ssl_npn_advertised(ngx_ssl_conn_t *ssl_conn, + const unsigned char **out, unsigned int *outlen, void *arg) + { +-#if (NGX_HTTP_V2 || NGX_DEBUG) ++#if (NGX_HTTP_V2 || NGX_HTTP_SPDY || NGX_DEBUG) + ngx_connection_t *c; + + c = ngx_ssl_get_connection(ssl_conn); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "SSL NPN advertised"); + #endif + +-#if (NGX_HTTP_V2) ++#if (NGX_HTTP_V2 || NGX_HTTP_SPDY) + { + ngx_http_connection_t *hc; + + hc = c->data; ++#endif + ++#if (NGX_HTTP_V2 && NGX_HTTP_SPDY) ++ if (hc->addr_conf->http2 && hc->addr_conf->spdy) { ++ *out = (unsigned char *) NGX_HTTP_V2_NPN_ADVERTISE ++ NGX_SPDY_NPN_ADVERTISE ++ NGX_HTTP_NPN_ADVERTISE; ++ *outlen = sizeof(NGX_HTTP_V2_NPN_ADVERTISE NGX_SPDY_NPN_ADVERTISE ++ NGX_HTTP_NPN_ADVERTISE) - 1; ++ ++ return SSL_TLSEXT_ERR_OK; ++ } else ++#endif ++#if (NGX_HTTP_V2) + if (hc->addr_conf->http2) { + *out = + (unsigned char *) NGX_HTTP_V2_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE; + *outlen = sizeof(NGX_HTTP_V2_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1; + + return SSL_TLSEXT_ERR_OK; ++ } else ++#endif ++#if (NGX_HTTP_SPDY) ++ 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 ++ ++#if (NGX_HTTP_V2 || NGX_HTTP_SPDY) + } + #endif + +diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c +index d09e3f0..caa5ee4 100644 +--- a/src/http/ngx_http.c ++++ b/src/http/ngx_http.c +@@ -1236,6 +1236,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, + #if (NGX_HTTP_V2) + ngx_uint_t http2; + #endif ++#if (NGX_HTTP_SPDY) ++ ngx_uint_t spdy; ++#endif + + /* + * we cannot compare whole sockaddr struct's as kernel +@@ -1293,6 +1296,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, + #if (NGX_HTTP_V2) + http2 = lsopt->http2 || addr[i].opt.http2; + #endif ++#if (NGX_HTTP_SPDY) ++ spdy = lsopt->spdy || addr[i].opt.spdy; ++#endif + + if (lsopt->set) { + +@@ -1327,6 +1333,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, + #if (NGX_HTTP_V2) + addr[i].opt.http2 = http2; + #endif ++#if (NGX_HTTP_SPDY) ++ addr[i].opt.spdy = spdy; ++#endif + + return NGX_OK; + } +@@ -1370,6 +1379,18 @@ ngx_http_add_address(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, + + #endif + ++#if (NGX_HTTP_SPDY && NGX_HTTP_SSL \ ++ && !defined TLSEXT_TYPE_application_layer_protocol_negotiation \ ++ && !defined TLSEXT_TYPE_next_proto_neg) ++ if (lsopt->spdy && lsopt->ssl) { ++ ngx_conf_log_error(NGX_LOG_WARN, cf, 0, ++ "nginx was built with OpenSSL that lacks ALPN " ++ "and NPN support, SPDY is not enabled for %s", ++ lsopt->addr); ++ } ++ ++#endif ++ + addr = ngx_array_push(&port->addrs); + if (addr == NULL) { + return NGX_ERROR; +@@ -1862,6 +1883,9 @@ ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport, + #if (NGX_HTTP_V2) + addrs[i].conf.http2 = addr[i].opt.http2; + #endif ++#if (NGX_HTTP_SPDY) ++ addrs[i].conf.spdy = addr[i].opt.spdy; ++#endif + addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; + + if (addr[i].hash.buckets == NULL +@@ -1927,6 +1951,9 @@ ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport, + #if (NGX_HTTP_V2) + addrs6[i].conf.http2 = addr[i].opt.http2; + #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 --git a/src/http/ngx_http.h b/src/http/ngx_http.h +index 19cb680..7d443c1 100644 +--- a/src/http/ngx_http.h ++++ b/src/http/ngx_http.h +@@ -23,6 +23,9 @@ typedef struct ngx_http_chunked_s ngx_http_chunked_t; + #if (NGX_HTTP_V2) + typedef struct ngx_http_v2_stream_s ngx_http_v2_stream_t; + #endif ++#if (NGX_HTTP_SPDY) ++typedef struct ngx_http_spdy_stream_s ngx_http_spdy_stream_t; ++#endif + + typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +@@ -41,6 +44,9 @@ typedef u_char *(*ngx_http_log_handler_pt)(ngx_http_request_t *r, + #if (NGX_HTTP_V2) + #include + #endif ++#if (NGX_HTTP_SPDY) ++#include ++#endif + #if (NGX_HTTP_CACHE) + #include + #endif +diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c +index 7a29608..39de2a8 100644 +--- a/src/http/ngx_http_core_module.c ++++ b/src/http/ngx_http_core_module.c +@@ -2132,6 +2132,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; +@@ -2476,6 +2483,9 @@ ngx_http_subrequest(ngx_http_request_t *r, + #if (NGX_HTTP_V2) + sr->stream = r->stream; + #endif ++#if (NGX_HTTP_SPDY) ++ sr->spdy_stream = r->spdy_stream; ++#endif + + sr->method = NGX_HTTP_GET; + sr->http_version = r->http_version; +@@ -4209,11 +4219,15 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) + } + + if (ngx_strcmp(value[n].data, "spdy") == 0) { +- ngx_conf_log_error(NGX_LOG_WARN, cf, 0, +- "invalid parameter \"spdy\": " +- "ngx_http_spdy_module was superseded " +- "by ngx_http_v2_module"); ++#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) { +diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h +index 7dec9e1..01110d3 100644 +--- a/src/http/ngx_http_core_module.h ++++ b/src/http/ngx_http_core_module.h +@@ -82,6 +82,9 @@ typedef struct { + #if (NGX_HTTP_V2) + unsigned http2:1; + #endif ++#if (NGX_HTTP_SPDY) ++ unsigned spdy:1; ++#endif + #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + unsigned ipv6only:1; + #endif +@@ -251,6 +254,9 @@ struct ngx_http_addr_conf_s { + #if (NGX_HTTP_V2) + unsigned http2:1; + #endif ++#if (NGX_HTTP_SPDY) ++ unsigned spdy:1; ++#endif + unsigned proxy_protocol:1; + }; + +diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c +index ab7c15f..0115630 100644 +--- a/src/http/ngx_http_request.c ++++ b/src/http/ngx_http_request.c +@@ -312,6 +312,11 @@ ngx_http_init_connection(ngx_connection_t *c) + rev->handler = ngx_http_wait_request_handler; + c->write->handler = ngx_http_empty_handler; + ++#if (NGX_HTTP_SPDY) ++ if (hc->addr_conf->spdy) { ++ rev->handler = ngx_http_spdy_init; ++ } ++#endif + #if (NGX_HTTP_V2) + if (hc->addr_conf->http2) { + rev->handler = ngx_http_v2_init; +@@ -797,6 +802,34 @@ ngx_http_ssl_handshake_handler(ngx_connection_t *c) + } + #endif + ++#if (NGX_HTTP_SPDY \ ++ && (defined TLSEXT_TYPE_application_layer_protocol_negotiation \ ++ || defined TLSEXT_TYPE_next_proto_neg)) ++ { ++ unsigned int len; ++ const unsigned char *data; ++ static const ngx_str_t spdy = ngx_string(NGX_SPDY_NPN_NEGOTIATED); ++ ++#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation ++ SSL_get0_alpn_selected(c->ssl->connection, &data, &len); ++ ++#ifdef TLSEXT_TYPE_next_proto_neg ++ if (len == 0) { ++ SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &len); ++ } ++#endif ++ ++#else /* TLSEXT_TYPE_next_proto_neg */ ++ SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &len); ++#endif ++ ++ if (len == spdy.len && ngx_strncmp(data, spdy.data, spdy.len) == 0) { ++ ngx_http_spdy_init(c->read); ++ return; ++ } ++ } ++#endif ++ + c->log->action = "waiting for request"; + + c->read->handler = ngx_http_wait_request_handler; +@@ -2510,6 +2543,12 @@ ngx_http_finalize_connection(ngx_http_request_t *r) + return; + } + #endif ++#if (NGX_HTTP_SPDY) ++ if (r->spdy_stream) { ++ ngx_http_close_request(r, 0); ++ return; ++ } ++#endif + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + +@@ -2575,6 +2614,11 @@ ngx_http_set_write_handler(ngx_http_request_t *r) + return NGX_OK; + } + #endif ++#if (NGX_HTTP_SPDY) ++ if (r->spdy_stream) { ++ return NGX_OK; ++ } ++#endif + + wev = r->connection->write; + +@@ -2666,6 +2710,11 @@ ngx_http_writer(ngx_http_request_t *r) + return; + } + #endif ++#if (NGX_HTTP_SPDY) ++ if (r->spdy_stream) { ++ return; ++ } ++#endif + + if (!wev->delayed) { + ngx_add_timer(wev, clcf->send_timeout); +@@ -2741,6 +2790,18 @@ ngx_http_test_reading(ngx_http_request_t *r) + } + + #endif ++#if (NGX_HTTP_SPDY) ++ ++ if (r->spdy_stream) { ++ if (c->error) { ++ err = 0; ++ goto closed; ++ } ++ ++ return; ++ } ++ ++#endif + + #if (NGX_HAVE_KQUEUE) + +@@ -3410,6 +3471,12 @@ ngx_http_close_request(ngx_http_request_t *r, ngx_int_t rc) + return; + } + #endif ++#if (NGX_HTTP_SPDY) ++ if (r->spdy_stream) { ++ ngx_http_spdy_close_stream(r->spdy_stream, rc); ++ return; ++ } ++#endif + + ngx_http_free_request(r, rc); + ngx_http_close_connection(c); +diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h +index 967032a..2cccd0f 100644 +--- a/src/http/ngx_http_request.h ++++ b/src/http/ngx_http_request.h +@@ -435,6 +435,9 @@ struct ngx_http_request_s { + #if (NGX_HTTP_V2) + ngx_http_v2_stream_t *stream; + #endif ++#if (NGX_HTTP_SPDY) ++ ngx_http_spdy_stream_t *spdy_stream; ++#endif + + ngx_http_log_handler_pt log_handler; + +diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c +index 77e92e3..4be35c8 100644 +--- a/src/http/ngx_http_request_body.c ++++ b/src/http/ngx_http_request_body.c +@@ -47,6 +47,13 @@ ngx_http_read_client_request_body(ngx_http_request_t *r, + goto done; + } + #endif ++#if (NGX_HTTP_SPDY) ++ if (r->spdy_stream && r == r->main) { ++ r->request_body_no_buffering = 0; ++ rc = ngx_http_spdy_read_request_body(r, post_handler); ++ goto done; ++ } ++#endif + + if (r != r->main || r->request_body || r->discard_body) { + r->request_body_no_buffering = 0; +@@ -576,6 +583,12 @@ ngx_http_discard_request_body(ngx_http_request_t *r) + return NGX_OK; + } + #endif ++#if (NGX_HTTP_SPDY) ++ if (r->spdy_stream && r == r->main) { ++ r->spdy_stream->skip_data = NGX_SPDY_DATA_DISCARD; ++ return NGX_OK; ++ } ++#endif + + if (r != r->main || r->discard_body || r->request_body) { + return NGX_OK; +diff --git a/src/http/ngx_http_spdy.c b/src/http/ngx_http_spdy.c +new file mode 100644 +index 0000000..21c5217 +--- /dev/null ++++ b/src/http/ngx_http_spdy.c +@@ -0,0 +1,3701 @@ ++ ++/* ++ * Copyright (C) Nginx, Inc. ++ * Copyright (C) Valentin V. Bartenev ++ */ ++ ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++ ++#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) ++ ++#define ngx_str5cmp(m, c0, c1, c2, c3, c4) \ ++ *(uint32_t *) m == (c3 << 24 | c2 << 16 | c1 << 8 | c0) \ ++ && m[4] == c4 ++ ++#else ++ ++#define ngx_str5cmp(m, c0, c1, c2, c3, c4) \ ++ m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4 ++ ++#endif ++ ++ ++#if (NGX_HAVE_NONALIGNED) ++ ++#define ngx_spdy_frame_parse_uint16(p) ntohs(*(uint16_t *) (p)) ++#define ngx_spdy_frame_parse_uint32(p) ntohl(*(uint32_t *) (p)) ++ ++#else ++ ++#define ngx_spdy_frame_parse_uint16(p) ((p)[0] << 8 | (p)[1]) ++#define ngx_spdy_frame_parse_uint32(p) \ ++ ((p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3]) ++ ++#endif ++ ++#define ngx_spdy_frame_parse_sid(p) \ ++ (ngx_spdy_frame_parse_uint32(p) & 0x7fffffff) ++#define ngx_spdy_frame_parse_delta(p) \ ++ (ngx_spdy_frame_parse_uint32(p) & 0x7fffffff) ++ ++ ++#define ngx_spdy_ctl_frame_check(h) \ ++ (((h) & 0xffff0000) == ngx_spdy_ctl_frame_head(0)) ++#define ngx_spdy_data_frame_check(h) \ ++ (!((h) & (uint32_t) NGX_SPDY_CTL_BIT << 31)) ++ ++#define ngx_spdy_ctl_frame_type(h) ((h) & 0x0000ffff) ++#define ngx_spdy_frame_flags(p) ((p) >> 24) ++#define ngx_spdy_frame_length(p) ((p) & 0x00ffffff) ++#define ngx_spdy_frame_id(p) ((p) & 0x00ffffff) ++ ++ ++#define NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE 4096 ++#define NGX_SPDY_CTL_FRAME_BUFFER_SIZE 16 ++ ++#define NGX_SPDY_PROTOCOL_ERROR 1 ++#define NGX_SPDY_INVALID_STREAM 2 ++#define NGX_SPDY_REFUSED_STREAM 3 ++#define NGX_SPDY_UNSUPPORTED_VERSION 4 ++#define NGX_SPDY_CANCEL 5 ++#define NGX_SPDY_INTERNAL_ERROR 6 ++#define NGX_SPDY_FLOW_CONTROL_ERROR 7 ++#define NGX_SPDY_STREAM_IN_USE 8 ++#define NGX_SPDY_STREAM_ALREADY_CLOSED 9 ++/* deprecated 10 */ ++#define NGX_SPDY_FRAME_TOO_LARGE 11 ++ ++#define NGX_SPDY_SETTINGS_MAX_STREAMS 4 ++#define NGX_SPDY_SETTINGS_INIT_WINDOW 7 ++ ++#define NGX_SPDY_SETTINGS_FLAG_PERSIST 0x01 ++#define NGX_SPDY_SETTINGS_FLAG_PERSISTED 0x02 ++ ++#define NGX_SPDY_MAX_WINDOW NGX_MAX_INT32_VALUE ++#define NGX_SPDY_CONNECTION_WINDOW 65536 ++#define NGX_SPDY_INIT_STREAM_WINDOW 65536 ++#define NGX_SPDY_STREAM_WINDOW NGX_SPDY_MAX_WINDOW ++ ++typedef struct { ++ ngx_uint_t hash; ++ u_char len; ++ u_char header[7]; ++ ngx_int_t (*handler)(ngx_http_request_t *r); ++} ngx_http_spdy_request_header_t; ++ ++ ++static void ngx_http_spdy_read_handler(ngx_event_t *rev); ++static void ngx_http_spdy_write_handler(ngx_event_t *wev); ++static void ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc); ++ ++static u_char *ngx_http_spdy_proxy_protocol(ngx_http_spdy_connection_t *sc, ++ u_char *pos, u_char *end); ++static u_char *ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc, ++ u_char *pos, u_char *end); ++static u_char *ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc, ++ u_char *pos, u_char *end); ++static u_char *ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc, ++ u_char *pos, u_char *end); ++static u_char *ngx_http_spdy_state_headers_skip(ngx_http_spdy_connection_t *sc, ++ u_char *pos, u_char *end); ++static u_char *ngx_http_spdy_state_headers_error(ngx_http_spdy_connection_t *sc, ++ u_char *pos, u_char *end); ++static u_char *ngx_http_spdy_state_window_update(ngx_http_spdy_connection_t *sc, ++ u_char *pos, u_char *end); ++static u_char *ngx_http_spdy_state_data(ngx_http_spdy_connection_t *sc, ++ u_char *pos, u_char *end); ++static u_char *ngx_http_spdy_state_read_data(ngx_http_spdy_connection_t *sc, ++ u_char *pos, u_char *end); ++static u_char *ngx_http_spdy_state_rst_stream(ngx_http_spdy_connection_t *sc, ++ u_char *pos, u_char *end); ++static u_char *ngx_http_spdy_state_ping(ngx_http_spdy_connection_t *sc, ++ u_char *pos, u_char *end); ++static u_char *ngx_http_spdy_state_skip(ngx_http_spdy_connection_t *sc, ++ u_char *pos, u_char *end); ++static u_char *ngx_http_spdy_state_settings(ngx_http_spdy_connection_t *sc, ++ u_char *pos, u_char *end); ++static u_char *ngx_http_spdy_state_complete(ngx_http_spdy_connection_t *sc, ++ u_char *pos, u_char *end); ++static u_char *ngx_http_spdy_state_save(ngx_http_spdy_connection_t *sc, ++ u_char *pos, u_char *end, ngx_http_spdy_handler_pt handler); ++ ++static u_char *ngx_http_spdy_state_inflate_error( ++ ngx_http_spdy_connection_t *sc, int rc); ++static u_char *ngx_http_spdy_state_protocol_error( ++ ngx_http_spdy_connection_t *sc); ++static u_char *ngx_http_spdy_state_internal_error( ++ ngx_http_spdy_connection_t *sc); ++ ++static ngx_int_t ngx_http_spdy_send_window_update( ++ ngx_http_spdy_connection_t *sc, ngx_uint_t sid, ngx_uint_t delta); ++static ngx_int_t ngx_http_spdy_send_rst_stream(ngx_http_spdy_connection_t *sc, ++ ngx_uint_t sid, ngx_uint_t status, ngx_uint_t priority); ++static ngx_int_t ngx_http_spdy_send_settings(ngx_http_spdy_connection_t *sc); ++static ngx_int_t ngx_http_spdy_settings_frame_handler( ++ ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame); ++static ngx_http_spdy_out_frame_t *ngx_http_spdy_get_ctl_frame( ++ ngx_http_spdy_connection_t *sc, size_t size, ngx_uint_t priority); ++static ngx_int_t ngx_http_spdy_ctl_frame_handler( ++ ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame); ++ ++static ngx_http_spdy_stream_t *ngx_http_spdy_create_stream( ++ ngx_http_spdy_connection_t *sc, ngx_uint_t id, ngx_uint_t priority); ++static ngx_http_spdy_stream_t *ngx_http_spdy_get_stream_by_id( ++ ngx_http_spdy_connection_t *sc, ngx_uint_t sid); ++#define ngx_http_spdy_streams_index_size(sscf) (sscf->streams_index_mask + 1) ++#define ngx_http_spdy_stream_index(sscf, sid) \ ++ ((sid >> 1) & sscf->streams_index_mask) ++ ++static ngx_int_t ngx_http_spdy_parse_header(ngx_http_request_t *r); ++static ngx_int_t ngx_http_spdy_alloc_large_header_buffer(ngx_http_request_t *r); ++ ++static ngx_int_t ngx_http_spdy_handle_request_header(ngx_http_request_t *r); ++static ngx_int_t ngx_http_spdy_parse_method(ngx_http_request_t *r); ++static ngx_int_t ngx_http_spdy_parse_scheme(ngx_http_request_t *r); ++static ngx_int_t ngx_http_spdy_parse_host(ngx_http_request_t *r); ++static ngx_int_t ngx_http_spdy_parse_path(ngx_http_request_t *r); ++static ngx_int_t ngx_http_spdy_parse_version(ngx_http_request_t *r); ++ ++static ngx_int_t ngx_http_spdy_construct_request_line(ngx_http_request_t *r); ++static void ngx_http_spdy_run_request(ngx_http_request_t *r); ++static ngx_int_t ngx_http_spdy_init_request_body(ngx_http_request_t *r); ++ ++static ngx_int_t ngx_http_spdy_terminate_stream(ngx_http_spdy_connection_t *sc, ++ ngx_http_spdy_stream_t *stream, ngx_uint_t status); ++ ++static void ngx_http_spdy_close_stream_handler(ngx_event_t *ev); ++ ++static void ngx_http_spdy_handle_connection_handler(ngx_event_t *rev); ++static void ngx_http_spdy_keepalive_handler(ngx_event_t *rev); ++static void ngx_http_spdy_finalize_connection(ngx_http_spdy_connection_t *sc, ++ ngx_int_t rc); ++ ++static ngx_int_t ngx_http_spdy_adjust_windows(ngx_http_spdy_connection_t *sc, ++ ssize_t delta); ++ ++static void ngx_http_spdy_pool_cleanup(void *data); ++ ++static void *ngx_http_spdy_zalloc(void *opaque, u_int items, u_int size); ++static void ngx_http_spdy_zfree(void *opaque, void *address); ++ ++ ++static const u_char ngx_http_spdy_dict[] = { ++ 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, /* - - - - o p t i */ ++ 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, /* o n s - - - - h */ ++ 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, /* e a d - - - - p */ ++ 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, /* o s t - - - - p */ ++ 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, /* u t - - - - d e */ ++ 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, /* l e t e - - - - */ ++ 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, /* t r a c e - - - */ ++ 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, /* - a c c e p t - */ ++ 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, /* - - - a c c e p */ ++ 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, /* t - c h a r s e */ ++ 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, /* t - - - - a c c */ ++ 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, /* e p t - e n c o */ ++ 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, /* d i n g - - - - */ ++ 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, /* a c c e p t - l */ ++ 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, /* a n g u a g e - */ ++ 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, /* - - - a c c e p */ ++ 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, /* t - r a n g e s */ ++ 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, /* - - - - a g e - */ ++ 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, /* - - - a l l o w */ ++ 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, /* - - - - a u t h */ ++ 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, /* o r i z a t i o */ ++ 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, /* n - - - - c a c */ ++ 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, /* h e - c o n t r */ ++ 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, /* o l - - - - c o */ ++ 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, /* n n e c t i o n */ ++ 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, /* - - - - c o n t */ ++ 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, /* e n t - b a s e */ ++ 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, /* - - - - c o n t */ ++ 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, /* e n t - e n c o */ ++ 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, /* d i n g - - - - */ ++ 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, /* c o n t e n t - */ ++ 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, /* l a n g u a g e */ ++ 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, /* - - - - c o n t */ ++ 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, /* e n t - l e n g */ ++ 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, /* t h - - - - c o */ ++ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, /* n t e n t - l o */ ++ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, /* c a t i o n - - */ ++ 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, /* - - c o n t e n */ ++ 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, /* t - m d 5 - - - */ ++ 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, /* - c o n t e n t */ ++ 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, /* - r a n g e - - */ ++ 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, /* - - c o n t e n */ ++ 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, /* t - t y p e - - */ ++ 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, /* - - d a t e - - */ ++ 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, /* - - e t a g - - */ ++ 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, /* - - e x p e c t */ ++ 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, /* - - - - e x p i */ ++ 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, /* r e s - - - - f */ ++ 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, /* r o m - - - - h */ ++ 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, /* o s t - - - - i */ ++ 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, /* f - m a t c h - */ ++ 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, /* - - - i f - m o */ ++ 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, /* d i f i e d - s */ ++ 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, /* i n c e - - - - */ ++ 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, /* i f - n o n e - */ ++ 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, /* m a t c h - - - */ ++ 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, /* - i f - r a n g */ ++ 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, /* e - - - - i f - */ ++ 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, /* u n m o d i f i */ ++ 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, /* e d - s i n c e */ ++ 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, /* - - - - l a s t */ ++ 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, /* - m o d i f i e */ ++ 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, /* d - - - - l o c */ ++ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, /* a t i o n - - - */ ++ 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, /* - m a x - f o r */ ++ 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, /* w a r d s - - - */ ++ 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, /* - p r a g m a - */ ++ 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, /* - - - p r o x y */ ++ 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, /* - a u t h e n t */ ++ 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, /* i c a t e - - - */ ++ 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, /* - p r o x y - a */ ++ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, /* u t h o r i z a */ ++ 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, /* t i o n - - - - */ ++ 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, /* r a n g e - - - */ ++ 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, /* - r e f e r e r */ ++ 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, /* - - - - r e t r */ ++ 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, /* y - a f t e r - */ ++ 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, /* - - - s e r v e */ ++ 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, /* r - - - - t e - */ ++ 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, /* - - - t r a i l */ ++ 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, /* e r - - - - t r */ ++ 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, /* a n s f e r - e */ ++ 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, /* n c o d i n g - */ ++ 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, /* - - - u p g r a */ ++ 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, /* d e - - - - u s */ ++ 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, /* e r - a g e n t */ ++ 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, /* - - - - v a r y */ ++ 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, /* - - - - v i a - */ ++ 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, /* - - - w a r n i */ ++ 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, /* n g - - - - w w */ ++ 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, /* w - a u t h e n */ ++ 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, /* t i c a t e - - */ ++ 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, /* - - m e t h o d */ ++ 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, /* - - - - g e t - */ ++ 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, /* - - - s t a t u */ ++ 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, /* s - - - - 2 0 0 */ ++ 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, /* - O K - - - - v */ ++ 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, /* e r s i o n - - */ ++ 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, /* - - H T T P - 1 */ ++ 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, /* - 1 - - - - u r */ ++ 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, /* l - - - - p u b */ ++ 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, /* l i c - - - - s */ ++ 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, /* e t - c o o k i */ ++ 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, /* e - - - - k e e */ ++ 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, /* p - a l i v e - */ ++ 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, /* - - - o r i g i */ ++ 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, /* n 1 0 0 1 0 1 2 */ ++ 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, /* 0 1 2 0 2 2 0 5 */ ++ 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, /* 2 0 6 3 0 0 3 0 */ ++ 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, /* 2 3 0 3 3 0 4 3 */ ++ 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, /* 0 5 3 0 6 3 0 7 */ ++ 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, /* 4 0 2 4 0 5 4 0 */ ++ 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, /* 6 4 0 7 4 0 8 4 */ ++ 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, /* 0 9 4 1 0 4 1 1 */ ++ 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, /* 4 1 2 4 1 3 4 1 */ ++ 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, /* 4 4 1 5 4 1 6 4 */ ++ 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, /* 1 7 5 0 2 5 0 4 */ ++ 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, /* 5 0 5 2 0 3 - N */ ++ 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, /* o n - A u t h o */ ++ 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, /* r i t a t i v e */ ++ 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, /* - I n f o r m a */ ++ 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, /* t i o n 2 0 4 - */ ++ 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, /* N o - C o n t e */ ++ 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, /* n t 3 0 1 - M o */ ++ 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, /* v e d - P e r m */ ++ 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, /* a n e n t l y 4 */ ++ 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, /* 0 0 - B a d - R */ ++ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, /* e q u e s t 4 0 */ ++ 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, /* 1 - U n a u t h */ ++ 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, /* o r i z e d 4 0 */ ++ 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, /* 3 - F o r b i d */ ++ 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, /* d e n 4 0 4 - N */ ++ 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, /* o t - F o u n d */ ++ 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, /* 5 0 0 - I n t e */ ++ 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, /* r n a l - S e r */ ++ 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, /* v e r - E r r o */ ++ 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, /* r 5 0 1 - N o t */ ++ 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, /* - I m p l e m e */ ++ 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, /* n t e d 5 0 3 - */ ++ 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, /* S e r v i c e - */ ++ 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, /* U n a v a i l a */ ++ 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, /* b l e J a n - F */ ++ 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, /* e b - M a r - A */ ++ 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, /* p r - M a y - J */ ++ 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, /* u n - J u l - A */ ++ 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, /* u g - S e p t - */ ++ 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, /* O c t - N o v - */ ++ 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, /* D e c - 0 0 - 0 */ ++ 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, /* 0 - 0 0 - M o n */ ++ 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, /* - - T u e - - W */ ++ 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, /* e d - - T h u - */ ++ 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, /* - F r i - - S a */ ++ 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, /* t - - S u n - - */ ++ 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, /* G M T c h u n k */ ++ 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, /* e d - t e x t - */ ++ 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, /* h t m l - i m a */ ++ 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, /* g e - p n g - i */ ++ 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, /* m a g e - j p g */ ++ 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, /* - i m a g e - g */ ++ 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, /* i f - a p p l i */ ++ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, /* c a t i o n - x */ ++ 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, /* m l - a p p l i */ ++ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, /* c a t i o n - x */ ++ 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, /* h t m l - x m l */ ++ 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, /* - t e x t - p l */ ++ 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, /* a i n - t e x t */ ++ 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, /* - j a v a s c r */ ++ 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, /* i p t - p u b l */ ++ 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, /* i c p r i v a t */ ++ 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, /* e m a x - a g e */ ++ 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, /* - g z i p - d e */ ++ 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, /* f l a t e - s d */ ++ 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, /* c h c h a r s e */ ++ 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, /* t - u t f - 8 c */ ++ 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, /* h a r s e t - i */ ++ 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, /* s o - 8 8 5 9 - */ ++ 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, /* 1 - u t f - - - */ ++ 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e /* - e n q - 0 - */ ++}; ++ ++ ++static ngx_http_spdy_request_header_t ngx_http_spdy_request_headers[] = { ++ { 0, 6, "method", ngx_http_spdy_parse_method }, ++ { 0, 6, "scheme", ngx_http_spdy_parse_scheme }, ++ { 0, 4, "host", ngx_http_spdy_parse_host }, ++ { 0, 4, "path", ngx_http_spdy_parse_path }, ++ { 0, 7, "version", ngx_http_spdy_parse_version }, ++}; ++ ++#define NGX_SPDY_REQUEST_HEADERS \ ++ (sizeof(ngx_http_spdy_request_headers) \ ++ / sizeof(ngx_http_spdy_request_header_t)) ++ ++ ++void ++ngx_http_spdy_init(ngx_event_t *rev) ++{ ++ int rc; ++ ngx_connection_t *c; ++ ngx_pool_cleanup_t *cln; ++ ngx_http_connection_t *hc; ++ ngx_http_spdy_srv_conf_t *sscf; ++ ngx_http_spdy_main_conf_t *smcf; ++ ngx_http_spdy_connection_t *sc; ++ ++ c = rev->data; ++ hc = c->data; ++ ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "init spdy request"); ++ ++ c->log->action = "processing SPDY"; ++ ++ smcf = ngx_http_get_module_main_conf(hc->conf_ctx, ngx_http_spdy_module); ++ ++ if (smcf->recv_buffer == NULL) { ++ smcf->recv_buffer = ngx_palloc(ngx_cycle->pool, smcf->recv_buffer_size); ++ if (smcf->recv_buffer == NULL) { ++ ngx_http_close_connection(c); ++ return; ++ } ++ } ++ ++ sc = ngx_pcalloc(c->pool, sizeof(ngx_http_spdy_connection_t)); ++ if (sc == NULL) { ++ ngx_http_close_connection(c); ++ return; ++ } ++ ++ sc->connection = c; ++ sc->http_connection = hc; ++ ++ sc->send_window = NGX_SPDY_CONNECTION_WINDOW; ++ sc->recv_window = NGX_SPDY_CONNECTION_WINDOW; ++ ++ sc->init_window = NGX_SPDY_INIT_STREAM_WINDOW; ++ ++ sc->handler = hc->proxy_protocol ? ngx_http_spdy_proxy_protocol ++ : ngx_http_spdy_state_head; ++ ++ sc->zstream_in.zalloc = ngx_http_spdy_zalloc; ++ sc->zstream_in.zfree = ngx_http_spdy_zfree; ++ sc->zstream_in.opaque = sc; ++ ++ rc = inflateInit(&sc->zstream_in); ++ if (rc != Z_OK) { ++ ngx_log_error(NGX_LOG_ALERT, c->log, 0, ++ "inflateInit() failed: %d", rc); ++ ngx_http_close_connection(c); ++ return; ++ } ++ ++ sc->zstream_out.zalloc = ngx_http_spdy_zalloc; ++ sc->zstream_out.zfree = ngx_http_spdy_zfree; ++ sc->zstream_out.opaque = sc; ++ ++ sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_spdy_module); ++ ++ rc = deflateInit2(&sc->zstream_out, (int) sscf->headers_comp, ++ Z_DEFLATED, 11, 4, Z_DEFAULT_STRATEGY); ++ ++ if (rc != Z_OK) { ++ ngx_log_error(NGX_LOG_ALERT, c->log, 0, ++ "deflateInit2() failed: %d", rc); ++ ngx_http_close_connection(c); ++ return; ++ } ++ ++ rc = deflateSetDictionary(&sc->zstream_out, ngx_http_spdy_dict, ++ sizeof(ngx_http_spdy_dict)); ++ if (rc != Z_OK) { ++ ngx_log_error(NGX_LOG_ALERT, c->log, 0, ++ "deflateSetDictionary() failed: %d", rc); ++ ngx_http_close_connection(c); ++ return; ++ } ++ ++ sc->pool = ngx_create_pool(sscf->pool_size, sc->connection->log); ++ if (sc->pool == NULL) { ++ ngx_http_close_connection(c); ++ return; ++ } ++ ++ cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_pool_cleanup_file_t)); ++ if (cln == NULL) { ++ ngx_http_close_connection(c); ++ return; ++ } ++ ++ cln->handler = ngx_http_spdy_pool_cleanup; ++ cln->data = sc; ++ ++ sc->streams_index = ngx_pcalloc(sc->pool, ++ ngx_http_spdy_streams_index_size(sscf) ++ * sizeof(ngx_http_spdy_stream_t *)); ++ if (sc->streams_index == NULL) { ++ ngx_http_close_connection(c); ++ return; ++ } ++ ++ if (ngx_http_spdy_send_settings(sc) == NGX_ERROR) { ++ ngx_http_close_connection(c); ++ return; ++ } ++ ++ if (ngx_http_spdy_send_window_update(sc, 0, NGX_SPDY_MAX_WINDOW ++ - sc->recv_window) ++ == NGX_ERROR) ++ { ++ ngx_http_close_connection(c); ++ return; ++ } ++ ++ sc->recv_window = NGX_SPDY_MAX_WINDOW; ++ ++ ngx_queue_init(&sc->waiting); ++ ngx_queue_init(&sc->posted); ++ ++ c->data = sc; ++ ++ rev->handler = ngx_http_spdy_read_handler; ++ c->write->handler = ngx_http_spdy_write_handler; ++ ++ ngx_http_spdy_read_handler(rev); ++} ++ ++ ++static void ++ngx_http_spdy_read_handler(ngx_event_t *rev) ++{ ++ u_char *p, *end; ++ size_t available; ++ ssize_t n; ++ ngx_connection_t *c; ++ ngx_http_spdy_main_conf_t *smcf; ++ ngx_http_spdy_connection_t *sc; ++ ++ c = rev->data; ++ sc = c->data; ++ ++ if (rev->timedout) { ++ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); ++ ngx_http_spdy_finalize_connection(sc, NGX_HTTP_REQUEST_TIME_OUT); ++ return; ++ } ++ ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy read handler"); ++ ++ sc->blocked = 1; ++ ++ smcf = ngx_http_get_module_main_conf(sc->http_connection->conf_ctx, ++ ngx_http_spdy_module); ++ ++ available = smcf->recv_buffer_size - 2 * NGX_SPDY_STATE_BUFFER_SIZE; ++ ++ do { ++ p = smcf->recv_buffer; ++ ++ ngx_memcpy(p, sc->buffer, NGX_SPDY_STATE_BUFFER_SIZE); ++ end = p + sc->buffer_used; ++ ++ n = c->recv(c, end, available); ++ ++ if (n == NGX_AGAIN) { ++ break; ++ } ++ ++ if (n == 0 && (sc->incomplete || sc->processing)) { ++ ngx_log_error(NGX_LOG_INFO, c->log, 0, ++ "client prematurely closed connection"); ++ } ++ ++ if (n == 0 || n == NGX_ERROR) { ++ ngx_http_spdy_finalize_connection(sc, ++ NGX_HTTP_CLIENT_CLOSED_REQUEST); ++ return; ++ } ++ ++ end += n; ++ ++ sc->buffer_used = 0; ++ sc->incomplete = 0; ++ ++ do { ++ p = sc->handler(sc, p, end); ++ ++ if (p == NULL) { ++ return; ++ } ++ ++ } while (p != end); ++ ++ } while (rev->ready); ++ ++ if (ngx_handle_read_event(rev, 0) != NGX_OK) { ++ ngx_http_spdy_finalize_connection(sc, NGX_HTTP_INTERNAL_SERVER_ERROR); ++ return; ++ } ++ ++ if (sc->last_out && ngx_http_spdy_send_output_queue(sc) == NGX_ERROR) { ++ ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST); ++ return; ++ } ++ ++ sc->blocked = 0; ++ ++ if (sc->processing) { ++ if (rev->timer_set) { ++ ngx_del_timer(rev); ++ } ++ return; ++ } ++ ++ ngx_http_spdy_handle_connection(sc); ++} ++ ++ ++static void ++ngx_http_spdy_write_handler(ngx_event_t *wev) ++{ ++ ngx_int_t rc; ++ ngx_queue_t *q; ++ ngx_connection_t *c; ++ ngx_http_spdy_stream_t *stream; ++ ngx_http_spdy_connection_t *sc; ++ ++ c = wev->data; ++ sc = c->data; ++ ++ if (wev->timedout) { ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, ++ "spdy write event timed out"); ++ ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST); ++ return; ++ } ++ ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy write handler"); ++ ++ sc->blocked = 1; ++ ++ rc = ngx_http_spdy_send_output_queue(sc); ++ ++ if (rc == NGX_ERROR) { ++ ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST); ++ return; ++ } ++ ++ while (!ngx_queue_empty(&sc->posted)) { ++ q = ngx_queue_head(&sc->posted); ++ ++ ngx_queue_remove(q); ++ ++ stream = ngx_queue_data(q, ngx_http_spdy_stream_t, queue); ++ ++ stream->handled = 0; ++ ++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, ++ "run spdy stream %ui", stream->id); ++ ++ wev = stream->request->connection->write; ++ wev->handler(wev); ++ } ++ ++ sc->blocked = 0; ++ ++ if (rc == NGX_AGAIN) { ++ return; ++ } ++ ++ ngx_http_spdy_handle_connection(sc); ++} ++ ++ ++ngx_int_t ++ngx_http_spdy_send_output_queue(ngx_http_spdy_connection_t *sc) ++{ ++ int tcp_nodelay; ++ ngx_chain_t *cl; ++ ngx_event_t *wev; ++ ngx_connection_t *c; ++ ngx_http_core_loc_conf_t *clcf; ++ ngx_http_spdy_out_frame_t *out, *frame, *fn; ++ ++ c = sc->connection; ++ ++ if (c->error) { ++ return NGX_ERROR; ++ } ++ ++ wev = c->write; ++ ++ if (!wev->ready) { ++ return NGX_OK; ++ } ++ ++ cl = NULL; ++ out = NULL; ++ ++ for (frame = sc->last_out; frame; frame = fn) { ++ frame->last->next = cl; ++ cl = frame->first; ++ ++ fn = frame->next; ++ frame->next = out; ++ out = frame; ++ ++ ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, ++ "spdy frame out: %p sid:%ui prio:%ui bl:%d len:%uz", ++ out, out->stream ? out->stream->id : 0, out->priority, ++ out->blocked, out->length); ++ } ++ ++ cl = c->send_chain(c, cl, 0); ++ ++ if (cl == NGX_CHAIN_ERROR) { ++ goto error; ++ } ++ ++ clcf = ngx_http_get_module_loc_conf(sc->http_connection->conf_ctx, ++ ngx_http_core_module); ++ ++ if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { ++ goto error; ++ } ++ ++ if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) { ++ if (ngx_tcp_push(c->fd) == -1) { ++ ngx_connection_error(c, ngx_socket_errno, ngx_tcp_push_n " failed"); ++ goto error; ++ } ++ ++ c->tcp_nopush = NGX_TCP_NOPUSH_UNSET; ++ tcp_nodelay = ngx_tcp_nodelay_and_tcp_nopush ? 1 : 0; ++ ++ } else { ++ tcp_nodelay = 1; ++ } ++ ++ if (tcp_nodelay ++ && clcf->tcp_nodelay ++ && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) ++ { ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "tcp_nodelay"); ++ ++ if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, ++ (const void *) &tcp_nodelay, sizeof(int)) ++ == -1) ++ { ++#if (NGX_SOLARIS) ++ /* Solaris returns EINVAL if a socket has been shut down */ ++ c->log_error = NGX_ERROR_IGNORE_EINVAL; ++#endif ++ ++ ngx_connection_error(c, ngx_socket_errno, ++ "setsockopt(TCP_NODELAY) failed"); ++ ++ c->log_error = NGX_ERROR_INFO; ++ goto error; ++ } ++ ++ c->tcp_nodelay = NGX_TCP_NODELAY_SET; ++ } ++ ++ if (cl) { ++ ngx_add_timer(wev, clcf->send_timeout); ++ ++ } else { ++ if (wev->timer_set) { ++ ngx_del_timer(wev); ++ } ++ } ++ ++ for ( /* void */ ; out; out = fn) { ++ fn = 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:%d len:%uz", ++ out, out->stream ? out->stream->id : 0, ++ out->blocked, out->length); ++ } ++ ++ frame = NULL; ++ ++ for ( /* void */ ; out; out = fn) { ++ fn = out->next; ++ out->next = frame; ++ frame = out; ++ } ++ ++ sc->last_out = frame; ++ ++ return NGX_OK; ++ ++error: ++ ++ c->error = 1; ++ ++ if (!sc->blocked) { ++ ngx_post_event(wev, &ngx_posted_events); ++ } ++ ++ return NGX_ERROR; ++} ++ ++ ++static void ++ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc) ++{ ++ ngx_connection_t *c; ++ ngx_http_spdy_srv_conf_t *sscf; ++ ++ if (sc->last_out || sc->processing) { ++ return; ++ } ++ ++ c = sc->connection; ++ ++ if (c->error) { ++ ngx_http_close_connection(c); ++ return; ++ } ++ ++ if (c->buffered) { ++ return; ++ } ++ ++ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, ++ ngx_http_spdy_module); ++ if (sc->incomplete) { ++ ngx_add_timer(c->read, sscf->recv_timeout); ++ return; ++ } ++ ++ if (ngx_terminate || ngx_exiting) { ++ ngx_http_close_connection(c); ++ return; ++ } ++ ++ ngx_destroy_pool(sc->pool); ++ ++ sc->pool = NULL; ++ sc->free_ctl_frames = NULL; ++ sc->free_fake_connections = NULL; ++ ++#if (NGX_HTTP_SSL) ++ if (c->ssl) { ++ ngx_ssl_free_buffer(c); ++ } ++#endif ++ ++ c->destroyed = 1; ++ c->idle = 1; ++ ngx_reusable_connection(c, 1); ++ ++ c->write->handler = ngx_http_empty_handler; ++ c->read->handler = ngx_http_spdy_keepalive_handler; ++ ++ if (c->write->timer_set) { ++ ngx_del_timer(c->write); ++ } ++ ++ ngx_add_timer(c->read, sscf->keepalive_timeout); ++} ++ ++ ++static u_char * ++ngx_http_spdy_proxy_protocol(ngx_http_spdy_connection_t *sc, u_char *pos, ++ u_char *end) ++{ ++ ngx_log_t *log; ++ ++ log = sc->connection->log; ++ log->action = "reading PROXY protocol"; ++ ++ pos = ngx_proxy_protocol_read(sc->connection, pos, end); ++ ++ log->action = "processing SPDY"; ++ ++ if (pos == NULL) { ++ return ngx_http_spdy_state_protocol_error(sc); ++ } ++ ++ return ngx_http_spdy_state_complete(sc, pos, end); ++} ++ ++ ++static u_char * ++ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc, u_char *pos, ++ u_char *end) ++{ ++ uint32_t head, flen; ++ ngx_uint_t type; ++ ++ if (end - pos < NGX_SPDY_FRAME_HEADER_SIZE) { ++ return ngx_http_spdy_state_save(sc, pos, end, ++ ngx_http_spdy_state_head); ++ } ++ ++ head = ngx_spdy_frame_parse_uint32(pos); ++ ++ pos += sizeof(uint32_t); ++ ++ flen = ngx_spdy_frame_parse_uint32(pos); ++ ++ sc->flags = ngx_spdy_frame_flags(flen); ++ sc->length = ngx_spdy_frame_length(flen); ++ ++ pos += sizeof(uint32_t); ++ ++ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "process spdy frame head:%08XD f:%Xd l:%uz", ++ head, sc->flags, sc->length); ++ ++ if (ngx_spdy_ctl_frame_check(head)) { ++ type = ngx_spdy_ctl_frame_type(head); ++ ++ switch (type) { ++ ++ case NGX_SPDY_SYN_STREAM: ++ return ngx_http_spdy_state_syn_stream(sc, pos, end); ++ ++ case NGX_SPDY_SYN_REPLY: ++ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0, ++ "client sent unexpected SYN_REPLY frame"); ++ return ngx_http_spdy_state_protocol_error(sc); ++ ++ case NGX_SPDY_RST_STREAM: ++ return ngx_http_spdy_state_rst_stream(sc, pos, end); ++ ++ case NGX_SPDY_SETTINGS: ++ return ngx_http_spdy_state_settings(sc, pos, end); ++ ++ case NGX_SPDY_PING: ++ return ngx_http_spdy_state_ping(sc, pos, end); ++ ++ case NGX_SPDY_GOAWAY: ++ return ngx_http_spdy_state_skip(sc, pos, end); /* TODO */ ++ ++ case NGX_SPDY_HEADERS: ++ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0, ++ "client sent unexpected HEADERS frame"); ++ return ngx_http_spdy_state_protocol_error(sc); ++ ++ case NGX_SPDY_WINDOW_UPDATE: ++ return ngx_http_spdy_state_window_update(sc, pos, end); ++ ++ default: ++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy control frame with unknown type %ui", type); ++ return ngx_http_spdy_state_skip(sc, pos, end); ++ } ++ } ++ ++ if (ngx_spdy_data_frame_check(head)) { ++ sc->stream = ngx_http_spdy_get_stream_by_id(sc, head); ++ return ngx_http_spdy_state_data(sc, pos, end); ++ } ++ ++ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0, ++ "client sent invalid frame"); ++ ++ return ngx_http_spdy_state_protocol_error(sc); ++} ++ ++ ++static u_char * ++ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc, u_char *pos, ++ u_char *end) ++{ ++ ngx_uint_t sid, prio; ++ ngx_http_spdy_stream_t *stream; ++ ngx_http_spdy_srv_conf_t *sscf; ++ ++ if (end - pos < NGX_SPDY_SYN_STREAM_SIZE) { ++ return ngx_http_spdy_state_save(sc, pos, end, ++ ngx_http_spdy_state_syn_stream); ++ } ++ ++ if (sc->length <= NGX_SPDY_SYN_STREAM_SIZE) { ++ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0, ++ "client sent SYN_STREAM frame with incorrect length %uz", ++ sc->length); ++ ++ return ngx_http_spdy_state_protocol_error(sc); ++ } ++ ++ sc->length -= NGX_SPDY_SYN_STREAM_SIZE; ++ ++ sid = ngx_spdy_frame_parse_sid(pos); ++ prio = pos[8] >> 5; ++ ++ pos += NGX_SPDY_SYN_STREAM_SIZE; ++ ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy SYN_STREAM frame sid:%ui prio:%ui", sid, prio); ++ ++ if (sid % 2 == 0 || sid <= sc->last_sid) { ++ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0, ++ "client sent SYN_STREAM frame " ++ "with invalid Stream-ID %ui", sid); ++ ++ stream = ngx_http_spdy_get_stream_by_id(sc, sid); ++ ++ if (stream) { ++ if (ngx_http_spdy_terminate_stream(sc, stream, ++ NGX_SPDY_PROTOCOL_ERROR) ++ != NGX_OK) ++ { ++ return ngx_http_spdy_state_internal_error(sc); ++ } ++ } ++ ++ return ngx_http_spdy_state_protocol_error(sc); ++ } ++ ++ sc->last_sid = sid; ++ ++ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, ++ ngx_http_spdy_module); ++ ++ if (sc->processing >= sscf->concurrent_streams) { ++ ++ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0, ++ "concurrent streams exceeded %ui", sc->processing); ++ ++ if (ngx_http_spdy_send_rst_stream(sc, sid, NGX_SPDY_REFUSED_STREAM, ++ prio) ++ != NGX_OK) ++ { ++ return ngx_http_spdy_state_internal_error(sc); ++ } ++ ++ return ngx_http_spdy_state_headers_skip(sc, pos, end); ++ } ++ ++ stream = ngx_http_spdy_create_stream(sc, sid, prio); ++ if (stream == NULL) { ++ return ngx_http_spdy_state_internal_error(sc); ++ } ++ ++ stream->in_closed = (sc->flags & NGX_SPDY_FLAG_FIN) ? 1 : 0; ++ ++ stream->request->request_length = NGX_SPDY_FRAME_HEADER_SIZE ++ + NGX_SPDY_SYN_STREAM_SIZE ++ + sc->length; ++ ++ sc->stream = stream; ++ ++ return ngx_http_spdy_state_headers(sc, pos, end); ++} ++ ++ ++static u_char * ++ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc, u_char *pos, ++ u_char *end) ++{ ++ int z; ++ size_t size; ++ ngx_buf_t *buf; ++ ngx_int_t rc; ++ ngx_http_request_t *r; ++ ++ size = end - pos; ++ ++ if (size == 0) { ++ return ngx_http_spdy_state_save(sc, pos, end, ++ ngx_http_spdy_state_headers); ++ } ++ ++ if (size > sc->length) { ++ size = sc->length; ++ } ++ ++ r = sc->stream->request; ++ ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ++ "process spdy header block %uz of %uz", size, sc->length); ++ ++ buf = r->header_in; ++ ++ sc->zstream_in.next_in = pos; ++ sc->zstream_in.avail_in = size; ++ sc->zstream_in.next_out = buf->last; ++ ++ /* one byte is reserved for null-termination of the last header value */ ++ 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) { ++ if (z == Z_DATA_ERROR) { ++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ++ "client sent SYN_STREAM frame with header " ++ "block encoded using wrong dictionary: %ul", ++ (u_long) sc->zstream_in.adler); ++ ++ return ngx_http_spdy_state_protocol_error(sc); ++ } ++ ++ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ++ "inflateSetDictionary() failed: %d", z); ++ ++ return ngx_http_spdy_state_internal_error(sc); ++ } ++ ++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ++ "spdy inflateSetDictionary(): %d", z); ++ ++ z = sc->zstream_in.avail_in ? inflate(&sc->zstream_in, Z_NO_FLUSH) ++ : Z_OK; ++ } ++ ++ ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ++ "spdy inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", ++ sc->zstream_in.next_in, sc->zstream_in.next_out, ++ sc->zstream_in.avail_in, sc->zstream_in.avail_out, ++ z); ++ ++ if (z != Z_OK) { ++ return ngx_http_spdy_state_inflate_error(sc, z); ++ } ++ ++ sc->length -= sc->zstream_in.next_in - pos; ++ pos = sc->zstream_in.next_in; ++ ++ buf->last = sc->zstream_in.next_out; ++ ++ if (r->headers_in.headers.part.elts == NULL) { ++ ++ if (buf->last - buf->pos < NGX_SPDY_NV_NUM_SIZE) { ++ ++ if (sc->length == 0) { ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ++ "premature end of spdy header block"); ++ ++ return ngx_http_spdy_state_headers_error(sc, pos, end); ++ } ++ ++ return ngx_http_spdy_state_save(sc, pos, end, ++ ngx_http_spdy_state_headers); ++ } ++ ++ sc->entries = ngx_spdy_frame_parse_uint32(buf->pos); ++ ++ buf->pos += NGX_SPDY_NV_NUM_SIZE; ++ ++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ++ "spdy header block has %ui entries", ++ sc->entries); ++ ++ if (ngx_list_init(&r->headers_in.headers, r->pool, 20, ++ sizeof(ngx_table_elt_t)) ++ != NGX_OK) ++ { ++ ngx_http_spdy_close_stream(sc->stream, ++ NGX_HTTP_INTERNAL_SERVER_ERROR); ++ return ngx_http_spdy_state_headers_skip(sc, pos, end); ++ } ++ ++ if (ngx_array_init(&r->headers_in.cookies, r->pool, 2, ++ sizeof(ngx_table_elt_t *)) ++ != NGX_OK) ++ { ++ ngx_http_spdy_close_stream(sc->stream, ++ NGX_HTTP_INTERNAL_SERVER_ERROR); ++ return ngx_http_spdy_state_headers_skip(sc, pos, end); ++ } ++ } ++ ++ while (sc->entries) { ++ ++ rc = ngx_http_spdy_parse_header(r); ++ ++ switch (rc) { ++ ++ case NGX_DONE: ++ sc->entries--; ++ ++ 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) { ++ ngx_http_finalize_request(r, ++ NGX_HTTP_REQUEST_HEADER_TOO_LARGE); ++ return ngx_http_spdy_state_headers_skip(sc, pos, end); ++ } ++ ++ if (rc != NGX_OK) { ++ ngx_http_spdy_close_stream(sc->stream, ++ NGX_HTTP_INTERNAL_SERVER_ERROR); ++ return ngx_http_spdy_state_headers_skip(sc, pos, end); ++ } ++ ++ /* null-terminate the last processed header name or value */ ++ *buf->pos = '\0'; ++ ++ buf = r->header_in; ++ ++ sc->zstream_in.next_out = buf->last; ++ ++ /* one byte is reserved for null-termination */ ++ sc->zstream_in.avail_out = buf->end - buf->last - 1; ++ ++ z = inflate(&sc->zstream_in, Z_NO_FLUSH); ++ ++ ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ++ "spdy inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", ++ sc->zstream_in.next_in, sc->zstream_in.next_out, ++ sc->zstream_in.avail_in, sc->zstream_in.avail_out, ++ z); ++ ++ if (z != Z_OK) { ++ return ngx_http_spdy_state_inflate_error(sc, z); ++ } ++ ++ sc->length -= sc->zstream_in.next_in - pos; ++ pos = sc->zstream_in.next_in; ++ ++ buf->last = sc->zstream_in.next_out; ++ ++ continue; ++ } ++ ++ if (sc->length == 0) { ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ++ "premature end of spdy header block"); ++ ++ return ngx_http_spdy_state_headers_error(sc, pos, end); ++ } ++ ++ return ngx_http_spdy_state_save(sc, pos, end, ++ ngx_http_spdy_state_headers); ++ ++ case NGX_HTTP_PARSE_INVALID_HEADER: ++ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); ++ return ngx_http_spdy_state_headers_skip(sc, pos, end); ++ ++ default: /* NGX_ERROR */ ++ return ngx_http_spdy_state_headers_error(sc, pos, end); ++ } ++ ++ /* a header line has been parsed successfully */ ++ ++ rc = ngx_http_spdy_handle_request_header(r); ++ ++ if (rc != NGX_OK) { ++ if (rc == NGX_HTTP_PARSE_INVALID_HEADER) { ++ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); ++ return ngx_http_spdy_state_headers_skip(sc, pos, end); ++ } ++ ++ if (rc != NGX_ABORT) { ++ ngx_http_spdy_close_stream(sc->stream, ++ NGX_HTTP_INTERNAL_SERVER_ERROR); ++ } ++ ++ return ngx_http_spdy_state_headers_skip(sc, pos, end); ++ } ++ } ++ ++ if (buf->pos != buf->last || sc->zstream_in.avail_in) { ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ++ "incorrect number of spdy header block entries"); ++ ++ return ngx_http_spdy_state_headers_error(sc, pos, end); ++ } ++ ++ if (sc->length) { ++ return ngx_http_spdy_state_save(sc, pos, end, ++ ngx_http_spdy_state_headers); ++ } ++ ++ /* null-terminate the last header value */ ++ *buf->pos = '\0'; ++ ++ ngx_http_spdy_run_request(r); ++ ++ return ngx_http_spdy_state_complete(sc, pos, end); ++} ++ ++ ++static u_char * ++ngx_http_spdy_state_headers_skip(ngx_http_spdy_connection_t *sc, u_char *pos, ++ u_char *end) ++{ ++ int n; ++ size_t size; ++ u_char buffer[NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE]; ++ ++ if (sc->length == 0) { ++ return ngx_http_spdy_state_complete(sc, pos, end); ++ } ++ ++ size = end - pos; ++ ++ if (size == 0) { ++ return ngx_http_spdy_state_save(sc, pos, end, ++ ngx_http_spdy_state_headers_skip); ++ } ++ ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy header block skip %uz of %uz", size, sc->length); ++ ++ 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_debug5(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", ++ sc->zstream_in.next_in, sc->zstream_in.next_out, ++ sc->zstream_in.avail_in, sc->zstream_in.avail_out, ++ n); ++ ++ if (n != Z_OK) { ++ return ngx_http_spdy_state_inflate_error(sc, n); ++ } ++ } ++ ++ pos = sc->zstream_in.next_in; ++ ++ if (size < sc->length) { ++ sc->length -= size; ++ return ngx_http_spdy_state_save(sc, pos, end, ++ ngx_http_spdy_state_headers_skip); ++ } ++ ++ return ngx_http_spdy_state_complete(sc, pos, end); ++} ++ ++ ++static u_char * ++ngx_http_spdy_state_headers_error(ngx_http_spdy_connection_t *sc, u_char *pos, ++ u_char *end) ++{ ++ ngx_http_spdy_stream_t *stream; ++ ++ stream = sc->stream; ++ ++ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0, ++ "client sent SYN_STREAM frame for stream %ui " ++ "with invalid header block", stream->id); ++ ++ if (ngx_http_spdy_send_rst_stream(sc, stream->id, NGX_SPDY_PROTOCOL_ERROR, ++ stream->priority) ++ != NGX_OK) ++ { ++ return ngx_http_spdy_state_internal_error(sc); ++ } ++ ++ stream->out_closed = 1; ++ ++ ngx_http_spdy_close_stream(stream, NGX_HTTP_BAD_REQUEST); ++ ++ return ngx_http_spdy_state_headers_skip(sc, pos, end); ++} ++ ++ ++static u_char * ++ngx_http_spdy_state_window_update(ngx_http_spdy_connection_t *sc, u_char *pos, ++ u_char *end) ++{ ++ size_t delta; ++ ngx_uint_t sid; ++ ngx_event_t *wev; ++ ngx_queue_t *q; ++ ngx_http_spdy_stream_t *stream; ++ ++ if (end - pos < NGX_SPDY_WINDOW_UPDATE_SIZE) { ++ return ngx_http_spdy_state_save(sc, pos, end, ++ ngx_http_spdy_state_window_update); ++ } ++ ++ if (sc->length != NGX_SPDY_WINDOW_UPDATE_SIZE) { ++ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0, ++ "client sent WINDOW_UPDATE frame " ++ "with incorrect length %uz", sc->length); ++ ++ return ngx_http_spdy_state_protocol_error(sc); ++ } ++ ++ sid = ngx_spdy_frame_parse_sid(pos); ++ ++ pos += NGX_SPDY_SID_SIZE; ++ ++ delta = ngx_spdy_frame_parse_delta(pos); ++ ++ pos += NGX_SPDY_DELTA_SIZE; ++ ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy WINDOW_UPDATE sid:%ui delta:%uz", sid, delta); ++ ++ if (sid) { ++ stream = ngx_http_spdy_get_stream_by_id(sc, sid); ++ ++ if (stream == NULL) { ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "unknown spdy stream"); ++ ++ return ngx_http_spdy_state_complete(sc, pos, end); ++ } ++ ++ if (stream->send_window > (ssize_t) (NGX_SPDY_MAX_WINDOW - delta)) { ++ ++ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0, ++ "client violated flow control for stream %ui: " ++ "received WINDOW_UPDATE frame with delta %uz " ++ "not allowed for window %z", ++ sid, delta, stream->send_window); ++ ++ if (ngx_http_spdy_terminate_stream(sc, stream, ++ NGX_SPDY_FLOW_CONTROL_ERROR) ++ == NGX_ERROR) ++ { ++ return ngx_http_spdy_state_internal_error(sc); ++ } ++ ++ return ngx_http_spdy_state_complete(sc, pos, end); ++ } ++ ++ stream->send_window += delta; ++ ++ if (stream->exhausted) { ++ stream->exhausted = 0; ++ ++ wev = stream->request->connection->write; ++ ++ if (!wev->timer_set) { ++ wev->delayed = 0; ++ wev->handler(wev); ++ } ++ } ++ ++ } else { ++ sc->send_window += delta; ++ ++ if (sc->send_window > NGX_SPDY_MAX_WINDOW) { ++ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0, ++ "client violated connection flow control: " ++ "received WINDOW_UPDATE frame with delta %uz " ++ "not allowed for window %uz", ++ delta, sc->send_window); ++ ++ return ngx_http_spdy_state_protocol_error(sc); ++ } ++ ++ while (!ngx_queue_empty(&sc->waiting)) { ++ q = ngx_queue_head(&sc->waiting); ++ ++ ngx_queue_remove(q); ++ ++ stream = ngx_queue_data(q, ngx_http_spdy_stream_t, queue); ++ ++ stream->handled = 0; ++ ++ wev = stream->request->connection->write; ++ ++ if (!wev->timer_set) { ++ wev->delayed = 0; ++ wev->handler(wev); ++ ++ if (sc->send_window == 0) { ++ break; ++ } ++ } ++ } ++ } ++ ++ return ngx_http_spdy_state_complete(sc, pos, end); ++} ++ ++ ++static u_char * ++ngx_http_spdy_state_data(ngx_http_spdy_connection_t *sc, u_char *pos, ++ u_char *end) ++{ ++ ngx_http_spdy_stream_t *stream; ++ ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy DATA frame"); ++ ++ if (sc->length > sc->recv_window) { ++ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0, ++ "client violated connection flow control: " ++ "received DATA frame length %uz, available window %uz", ++ sc->length, sc->recv_window); ++ ++ return ngx_http_spdy_state_protocol_error(sc); ++ } ++ ++ sc->recv_window -= sc->length; ++ ++ if (sc->recv_window < NGX_SPDY_MAX_WINDOW / 4) { ++ ++ if (ngx_http_spdy_send_window_update(sc, 0, ++ NGX_SPDY_MAX_WINDOW ++ - sc->recv_window) ++ == NGX_ERROR) ++ { ++ return ngx_http_spdy_state_internal_error(sc); ++ } ++ ++ sc->recv_window = NGX_SPDY_MAX_WINDOW; ++ } ++ ++ stream = sc->stream; ++ ++ if (stream == NULL) { ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "unknown spdy stream"); ++ ++ return ngx_http_spdy_state_skip(sc, pos, end); ++ } ++ ++ if (sc->length > stream->recv_window) { ++ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0, ++ "client violated flow control for stream %ui: " ++ "received DATA frame length %uz, available window %uz", ++ stream->id, sc->length, stream->recv_window); ++ ++ if (ngx_http_spdy_terminate_stream(sc, stream, ++ NGX_SPDY_FLOW_CONTROL_ERROR) ++ == NGX_ERROR) ++ { ++ return ngx_http_spdy_state_internal_error(sc); ++ } ++ ++ return ngx_http_spdy_state_skip(sc, pos, end); ++ } ++ ++ stream->recv_window -= sc->length; ++ ++ if (stream->recv_window < NGX_SPDY_STREAM_WINDOW / 4) { ++ ++ if (ngx_http_spdy_send_window_update(sc, stream->id, ++ NGX_SPDY_STREAM_WINDOW ++ - stream->recv_window) ++ == NGX_ERROR) ++ { ++ return ngx_http_spdy_state_internal_error(sc); ++ } ++ ++ stream->recv_window = NGX_SPDY_STREAM_WINDOW; ++ } ++ ++ if (stream->in_closed) { ++ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0, ++ "client sent DATA frame for half-closed stream %ui", ++ stream->id); ++ ++ if (ngx_http_spdy_terminate_stream(sc, stream, ++ NGX_SPDY_STREAM_ALREADY_CLOSED) ++ == NGX_ERROR) ++ { ++ return ngx_http_spdy_state_internal_error(sc); ++ } ++ ++ return ngx_http_spdy_state_skip(sc, pos, end); ++ } ++ ++ return ngx_http_spdy_state_read_data(sc, pos, end); ++} ++ ++ ++static u_char * ++ngx_http_spdy_state_read_data(ngx_http_spdy_connection_t *sc, u_char *pos, ++ u_char *end) ++{ ++ size_t size; ++ ssize_t n; ++ ngx_buf_t *buf; ++ ngx_int_t rc; ++ ngx_temp_file_t *tf; ++ ngx_http_request_t *r; ++ ngx_http_spdy_stream_t *stream; ++ ngx_http_request_body_t *rb; ++ ngx_http_core_loc_conf_t *clcf; ++ ++ stream = sc->stream; ++ ++ if (stream == NULL) { ++ return ngx_http_spdy_state_skip(sc, pos, end); ++ } ++ ++ if (stream->skip_data) { ++ ++ if (sc->flags & NGX_SPDY_FLAG_FIN) { ++ stream->in_closed = 1; ++ } ++ ++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "skipping spdy DATA frame, reason: %d", ++ stream->skip_data); ++ ++ return ngx_http_spdy_state_skip(sc, pos, end); ++ } ++ ++ size = end - pos; ++ ++ if (size > sc->length) { ++ size = sc->length; ++ } ++ ++ r = stream->request; ++ ++ if (r->request_body == NULL ++ && ngx_http_spdy_init_request_body(r) != NGX_OK) ++ { ++ stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR; ++ return ngx_http_spdy_state_skip(sc, pos, end); ++ } ++ ++ rb = r->request_body; ++ tf = rb->temp_file; ++ buf = rb->buf; ++ ++ if (size) { ++ rb->rest += size; ++ ++ if (r->headers_in.content_length_n != -1 ++ && r->headers_in.content_length_n < rb->rest) ++ { ++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ++ "client intended to send body data " ++ "larger than declared"); ++ ++ stream->skip_data = NGX_SPDY_DATA_ERROR; ++ goto error; ++ ++ } else { ++ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ++ ++ if (clcf->client_max_body_size ++ && clcf->client_max_body_size < rb->rest) ++ { ++ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ++ "client intended to send " ++ "too large chunked body: %O bytes", rb->rest); ++ ++ stream->skip_data = NGX_SPDY_DATA_ERROR; ++ goto error; ++ } ++ } ++ ++ sc->length -= size; ++ ++ if (tf) { ++ buf->start = pos; ++ buf->pos = pos; ++ ++ pos += size; ++ ++ buf->end = pos; ++ buf->last = pos; ++ ++ n = ngx_write_chain_to_temp_file(tf, rb->bufs); ++ ++ /* TODO: n == 0 or not complete and level event */ ++ ++ if (n == NGX_ERROR) { ++ stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR; ++ goto error; ++ } ++ ++ tf->offset += n; ++ ++ } else { ++ buf->last = ngx_cpymem(buf->last, pos, size); ++ pos += size; ++ } ++ ++ r->request_length += size; ++ } ++ ++ if (sc->length) { ++ return ngx_http_spdy_state_save(sc, pos, end, ++ ngx_http_spdy_state_read_data); ++ } ++ ++ if (sc->flags & NGX_SPDY_FLAG_FIN) { ++ ++ stream->in_closed = 1; ++ ++ if (r->headers_in.content_length_n < 0) { ++ r->headers_in.content_length_n = rb->rest; ++ ++ } else if (r->headers_in.content_length_n != rb->rest) { ++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ++ "client prematurely closed stream: " ++ "only %O out of %O bytes of request body received", ++ rb->rest, r->headers_in.content_length_n); ++ ++ stream->skip_data = NGX_SPDY_DATA_ERROR; ++ goto error; ++ } ++ ++ if (tf) { ++ ngx_memzero(buf, sizeof(ngx_buf_t)); ++ ++ buf->in_file = 1; ++ buf->file_last = tf->file.offset; ++ buf->file = &tf->file; ++ ++ rb->buf = NULL; ++ } ++ ++ if (rb->post_handler) { ++ r->read_event_handler = ngx_http_block_reading; ++ rb->post_handler(r); ++ } ++ } ++ ++ return ngx_http_spdy_state_complete(sc, pos, end); ++ ++error: ++ ++ if (rb->post_handler) { ++ ++ if (stream->skip_data == NGX_SPDY_DATA_ERROR) { ++ rc = (r->headers_in.content_length_n == -1) ++ ? NGX_HTTP_REQUEST_ENTITY_TOO_LARGE ++ : NGX_HTTP_BAD_REQUEST; ++ ++ } else { ++ rc = NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ ngx_http_finalize_request(r, rc); ++ } ++ ++ return ngx_http_spdy_state_skip(sc, pos, end); ++} ++ ++ ++static u_char * ++ngx_http_spdy_state_rst_stream(ngx_http_spdy_connection_t *sc, u_char *pos, ++ u_char *end) ++{ ++ ngx_uint_t sid, status; ++ ngx_event_t *ev; ++ ngx_connection_t *fc; ++ ngx_http_spdy_stream_t *stream; ++ ++ if (end - pos < NGX_SPDY_RST_STREAM_SIZE) { ++ return ngx_http_spdy_state_save(sc, pos, end, ++ ngx_http_spdy_state_rst_stream); ++ } ++ ++ if (sc->length != NGX_SPDY_RST_STREAM_SIZE) { ++ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0, ++ "client sent RST_STREAM frame with incorrect length %uz", ++ sc->length); ++ ++ return ngx_http_spdy_state_protocol_error(sc); ++ } ++ ++ sid = ngx_spdy_frame_parse_sid(pos); ++ ++ pos += NGX_SPDY_SID_SIZE; ++ ++ status = ngx_spdy_frame_parse_uint32(pos); ++ ++ pos += sizeof(uint32_t); ++ ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy RST_STREAM sid:%ui st:%ui", sid, status); ++ ++ stream = ngx_http_spdy_get_stream_by_id(sc, sid); ++ ++ if (stream == NULL) { ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "unknown spdy stream"); ++ ++ return ngx_http_spdy_state_complete(sc, pos, end); ++ } ++ ++ stream->in_closed = 1; ++ stream->out_closed = 1; ++ ++ fc = stream->request->connection; ++ fc->error = 1; ++ ++ switch (status) { ++ ++ case NGX_SPDY_CANCEL: ++ ngx_log_error(NGX_LOG_INFO, fc->log, 0, ++ "client canceled stream %ui", sid); ++ break; ++ ++ case NGX_SPDY_INTERNAL_ERROR: ++ ngx_log_error(NGX_LOG_INFO, fc->log, 0, ++ "client terminated stream %ui due to internal error", ++ sid); ++ break; ++ ++ default: ++ ngx_log_error(NGX_LOG_INFO, fc->log, 0, ++ "client terminated stream %ui with status %ui", ++ sid, status); ++ break; ++ } ++ ++ ev = fc->read; ++ ev->handler(ev); ++ ++ return ngx_http_spdy_state_complete(sc, pos, end); ++} ++ ++ ++static u_char * ++ngx_http_spdy_state_ping(ngx_http_spdy_connection_t *sc, u_char *pos, ++ u_char *end) ++{ ++ u_char *p; ++ ngx_buf_t *buf; ++ ngx_http_spdy_out_frame_t *frame; ++ ++ if (end - pos < NGX_SPDY_PING_SIZE) { ++ return ngx_http_spdy_state_save(sc, pos, end, ++ ngx_http_spdy_state_ping); ++ } ++ ++ if (sc->length != NGX_SPDY_PING_SIZE) { ++ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0, ++ "client sent PING frame with incorrect length %uz", ++ sc->length); ++ ++ return ngx_http_spdy_state_protocol_error(sc); ++ } ++ ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy PING frame"); ++ ++ frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_PING_SIZE, ++ NGX_SPDY_HIGHEST_PRIORITY); ++ if (frame == NULL) { ++ return ngx_http_spdy_state_internal_error(sc); ++ } ++ ++ buf = frame->first->buf; ++ ++ p = buf->pos; ++ ++ p = ngx_spdy_frame_write_head(p, NGX_SPDY_PING); ++ p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_PING_SIZE); ++ ++ p = ngx_cpymem(p, pos, NGX_SPDY_PING_SIZE); ++ ++ buf->last = p; ++ ++ ngx_http_spdy_queue_frame(sc, frame); ++ ++ pos += NGX_SPDY_PING_SIZE; ++ ++ return ngx_http_spdy_state_complete(sc, pos, end); ++} ++ ++ ++static u_char * ++ngx_http_spdy_state_skip(ngx_http_spdy_connection_t *sc, u_char *pos, ++ u_char *end) ++{ ++ size_t size; ++ ++ size = end - pos; ++ ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy frame skip %uz of %uz", size, sc->length); ++ ++ if (size < sc->length) { ++ sc->length -= size; ++ return ngx_http_spdy_state_save(sc, end, end, ++ ngx_http_spdy_state_skip); ++ } ++ ++ return ngx_http_spdy_state_complete(sc, pos + sc->length, end); ++} ++ ++ ++static u_char * ++ngx_http_spdy_state_settings(ngx_http_spdy_connection_t *sc, u_char *pos, ++ u_char *end) ++{ ++ ngx_uint_t fid, val; ++ ++ if (sc->entries == 0) { ++ ++ if (end - pos < NGX_SPDY_SETTINGS_NUM_SIZE) { ++ return ngx_http_spdy_state_save(sc, pos, end, ++ ngx_http_spdy_state_settings); ++ } ++ ++ sc->entries = ngx_spdy_frame_parse_uint32(pos); ++ ++ pos += NGX_SPDY_SETTINGS_NUM_SIZE; ++ sc->length -= NGX_SPDY_SETTINGS_NUM_SIZE; ++ ++ if (sc->length < sc->entries * NGX_SPDY_SETTINGS_PAIR_SIZE) { ++ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0, ++ "client sent SETTINGS frame with incorrect " ++ "length %uz or number of entries %ui", ++ sc->length, sc->entries); ++ ++ return ngx_http_spdy_state_protocol_error(sc); ++ } ++ ++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy SETTINGS frame has %ui entries", sc->entries); ++ } ++ ++ while (sc->entries) { ++ if (end - pos < NGX_SPDY_SETTINGS_PAIR_SIZE) { ++ return ngx_http_spdy_state_save(sc, pos, end, ++ ngx_http_spdy_state_settings); ++ } ++ ++ sc->entries--; ++ sc->length -= NGX_SPDY_SETTINGS_PAIR_SIZE; ++ ++ fid = ngx_spdy_frame_parse_uint32(pos); ++ ++ pos += NGX_SPDY_SETTINGS_FID_SIZE; ++ ++ val = ngx_spdy_frame_parse_uint32(pos); ++ ++ pos += NGX_SPDY_SETTINGS_VAL_SIZE; ++ ++ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy SETTINGS entry fl:%ui id:%ui val:%ui", ++ ngx_spdy_frame_flags(fid), ngx_spdy_frame_id(fid), val); ++ ++ if (ngx_spdy_frame_flags(fid) == NGX_SPDY_SETTINGS_FLAG_PERSISTED) { ++ continue; ++ } ++ ++ switch (ngx_spdy_frame_id(fid)) { ++ ++ case NGX_SPDY_SETTINGS_INIT_WINDOW: ++ ++ if (val > NGX_SPDY_MAX_WINDOW) { ++ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0, ++ "client sent SETTINGS frame with " ++ "incorrect INIT_WINDOW value: %ui", val); ++ ++ return ngx_http_spdy_state_protocol_error(sc); ++ } ++ ++ if (ngx_http_spdy_adjust_windows(sc, val - sc->init_window) ++ != NGX_OK) ++ { ++ return ngx_http_spdy_state_internal_error(sc); ++ } ++ ++ sc->init_window = val; ++ ++ continue; ++ } ++ } ++ ++ return ngx_http_spdy_state_complete(sc, pos, end); ++} ++ ++ ++static u_char * ++ngx_http_spdy_state_complete(ngx_http_spdy_connection_t *sc, u_char *pos, ++ u_char *end) ++{ ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy frame complete pos:%p end:%p", pos, end); ++ ++ if (pos > end) { ++ ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0, ++ "receive buffer overrun"); ++ ++ ngx_debug_point(); ++ return ngx_http_spdy_state_internal_error(sc); ++ } ++ ++ sc->handler = ngx_http_spdy_state_head; ++ sc->stream = NULL; ++ ++ return pos; ++} ++ ++ ++static u_char * ++ngx_http_spdy_state_save(ngx_http_spdy_connection_t *sc, ++ u_char *pos, u_char *end, ngx_http_spdy_handler_pt handler) ++{ ++ size_t size; ++ ++ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy frame state save pos:%p end:%p handler:%p", ++ pos, end, handler); ++ ++ size = end - pos; ++ ++ if (size > NGX_SPDY_STATE_BUFFER_SIZE) { ++ ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0, ++ "state buffer overflow: %uz bytes required", size); ++ ++ ngx_debug_point(); ++ return ngx_http_spdy_state_internal_error(sc); ++ } ++ ++ ngx_memcpy(sc->buffer, pos, NGX_SPDY_STATE_BUFFER_SIZE); ++ ++ sc->buffer_used = size; ++ sc->handler = handler; ++ sc->incomplete = 1; ++ ++ return end; ++} ++ ++ ++static u_char * ++ngx_http_spdy_state_inflate_error(ngx_http_spdy_connection_t *sc, int rc) ++{ ++ if (rc == Z_DATA_ERROR || rc == Z_STREAM_END) { ++ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0, ++ "client sent SYN_STREAM frame with " ++ "corrupted header block, inflate() failed: %d", rc); ++ ++ return ngx_http_spdy_state_protocol_error(sc); ++ } ++ ++ ngx_log_error(NGX_LOG_ERR, sc->connection->log, 0, ++ "inflate() failed: %d", rc); ++ ++ return ngx_http_spdy_state_internal_error(sc); ++} ++ ++ ++static u_char * ++ngx_http_spdy_state_protocol_error(ngx_http_spdy_connection_t *sc) ++{ ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy state protocol error"); ++ ++ if (sc->stream) { ++ sc->stream->out_closed = 1; ++ ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_BAD_REQUEST); ++ } ++ ++ ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST); ++ ++ return NULL; ++} ++ ++ ++static u_char * ++ngx_http_spdy_state_internal_error(ngx_http_spdy_connection_t *sc) ++{ ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy state internal error"); ++ ++ if (sc->stream) { ++ sc->stream->out_closed = 1; ++ ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_INTERNAL_SERVER_ERROR); ++ } ++ ++ ngx_http_spdy_finalize_connection(sc, NGX_HTTP_INTERNAL_SERVER_ERROR); ++ ++ return NULL; ++} ++ ++ ++static ngx_int_t ++ngx_http_spdy_send_window_update(ngx_http_spdy_connection_t *sc, ngx_uint_t sid, ++ ngx_uint_t delta) ++{ ++ u_char *p; ++ ngx_buf_t *buf; ++ ngx_http_spdy_out_frame_t *frame; ++ ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy send WINDOW_UPDATE sid:%ui delta:%ui", sid, delta); ++ ++ frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_WINDOW_UPDATE_SIZE, ++ NGX_SPDY_HIGHEST_PRIORITY); ++ if (frame == NULL) { ++ return NGX_ERROR; ++ } ++ ++ buf = frame->first->buf; ++ ++ p = buf->pos; ++ ++ p = ngx_spdy_frame_write_head(p, NGX_SPDY_WINDOW_UPDATE); ++ p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_WINDOW_UPDATE_SIZE); ++ ++ p = ngx_spdy_frame_write_sid(p, sid); ++ p = ngx_spdy_frame_aligned_write_uint32(p, delta); ++ ++ buf->last = p; ++ ++ ngx_http_spdy_queue_frame(sc, 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; ++ ++ if (sc->connection->error) { ++ return NGX_OK; ++ } ++ ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy send RST_STREAM sid:%ui st:%ui", sid, status); ++ ++ frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_RST_STREAM_SIZE, ++ priority); ++ if (frame == NULL) { ++ return NGX_ERROR; ++ } ++ ++ buf = frame->first->buf; ++ ++ p = buf->pos; ++ ++ p = ngx_spdy_frame_write_head(p, NGX_SPDY_RST_STREAM); ++ p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_RST_STREAM_SIZE); ++ ++ p = ngx_spdy_frame_write_sid(p, sid); ++ p = ngx_spdy_frame_aligned_write_uint32(p, status); ++ ++ buf->last = p; ++ ++ ngx_http_spdy_queue_frame(sc, frame); ++ ++ return NGX_OK; ++} ++ ++ ++#if 0 ++static ngx_int_t ++ngx_http_spdy_send_goaway(ngx_http_spdy_connection_t *sc) ++{ ++ u_char *p; ++ ngx_buf_t *buf; ++ ngx_http_spdy_out_frame_t *frame; ++ ++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy send GOAWAY sid:%ui", sc->last_sid); ++ ++ frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_GOAWAY_SIZE, ++ NGX_SPDY_HIGHEST_PRIORITY); ++ if (frame == NULL) { ++ return NGX_ERROR; ++ } ++ ++ buf = frame->first->buf; ++ ++ p = buf->pos; ++ ++ p = ngx_spdy_frame_write_head(p, NGX_SPDY_GOAWAY); ++ p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_GOAWAY_SIZE); ++ ++ p = ngx_spdy_frame_write_sid(p, sc->last_sid); ++ ++ buf->last = p; ++ ++ ngx_http_spdy_queue_frame(sc, frame); ++ ++ return NGX_OK; ++} ++#endif ++ ++ ++static ngx_int_t ++ngx_http_spdy_send_settings(ngx_http_spdy_connection_t *sc) ++{ ++ u_char *p; ++ ngx_buf_t *buf; ++ ngx_chain_t *cl; ++ ngx_http_spdy_srv_conf_t *sscf; ++ ngx_http_spdy_out_frame_t *frame; ++ ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy send SETTINGS frame"); ++ ++ frame = ngx_palloc(sc->pool, sizeof(ngx_http_spdy_out_frame_t)); ++ if (frame == NULL) { ++ return NGX_ERROR; ++ } ++ ++ cl = ngx_alloc_chain_link(sc->pool); ++ if (cl == NULL) { ++ return NGX_ERROR; ++ } ++ ++ buf = ngx_create_temp_buf(sc->pool, NGX_SPDY_FRAME_HEADER_SIZE ++ + NGX_SPDY_SETTINGS_NUM_SIZE ++ + 2 * NGX_SPDY_SETTINGS_PAIR_SIZE); ++ if (buf == NULL) { ++ return NGX_ERROR; ++ } ++ ++ buf->last_buf = 1; ++ ++ cl->buf = buf; ++ cl->next = NULL; ++ ++ frame->first = cl; ++ frame->last = cl; ++ frame->handler = ngx_http_spdy_settings_frame_handler; ++ frame->stream = NULL; ++#if (NGX_DEBUG) ++ frame->length = NGX_SPDY_SETTINGS_NUM_SIZE ++ + 2 * NGX_SPDY_SETTINGS_PAIR_SIZE; ++#endif ++ frame->priority = NGX_SPDY_HIGHEST_PRIORITY; ++ frame->blocked = 0; ++ ++ p = buf->pos; ++ ++ p = ngx_spdy_frame_write_head(p, NGX_SPDY_SETTINGS); ++ p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_CLEAR_SETTINGS, ++ NGX_SPDY_SETTINGS_NUM_SIZE ++ + 2 * NGX_SPDY_SETTINGS_PAIR_SIZE); ++ ++ p = ngx_spdy_frame_aligned_write_uint32(p, 2); ++ ++ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, ++ ngx_http_spdy_module); ++ ++ p = ngx_spdy_frame_write_flags_and_id(p, 0, NGX_SPDY_SETTINGS_MAX_STREAMS); ++ p = ngx_spdy_frame_aligned_write_uint32(p, sscf->concurrent_streams); ++ ++ p = ngx_spdy_frame_write_flags_and_id(p, 0, NGX_SPDY_SETTINGS_INIT_WINDOW); ++ p = ngx_spdy_frame_aligned_write_uint32(p, NGX_SPDY_STREAM_WINDOW); ++ ++ buf->last = p; ++ ++ ngx_http_spdy_queue_frame(sc, frame); ++ ++ return NGX_OK; ++} ++ ++ ++ngx_int_t ++ngx_http_spdy_settings_frame_handler(ngx_http_spdy_connection_t *sc, ++ ngx_http_spdy_out_frame_t *frame) ++{ ++ ngx_buf_t *buf; ++ ++ buf = frame->first->buf; ++ ++ if (buf->pos != buf->last) { ++ return NGX_AGAIN; ++ } ++ ++ ngx_free_chain(sc->pool, frame->first); ++ ++ return NGX_OK; ++} ++ ++ ++static ngx_http_spdy_out_frame_t * ++ngx_http_spdy_get_ctl_frame(ngx_http_spdy_connection_t *sc, size_t length, ++ ngx_uint_t priority) ++{ ++ ngx_chain_t *cl; ++ ngx_http_spdy_out_frame_t *frame; ++ ++ frame = sc->free_ctl_frames; ++ ++ if (frame) { ++ sc->free_ctl_frames = frame->next; ++ ++ cl = frame->first; ++ cl->buf->pos = cl->buf->start; ++ ++ } else { ++ frame = ngx_palloc(sc->pool, sizeof(ngx_http_spdy_out_frame_t)); ++ if (frame == NULL) { ++ return NULL; ++ } ++ ++ cl = ngx_alloc_chain_link(sc->pool); ++ if (cl == NULL) { ++ return NULL; ++ } ++ ++ cl->buf = ngx_create_temp_buf(sc->pool, ++ NGX_SPDY_CTL_FRAME_BUFFER_SIZE); ++ if (cl->buf == NULL) { ++ return NULL; ++ } ++ ++ cl->buf->last_buf = 1; ++ ++ frame->first = cl; ++ frame->last = cl; ++ frame->handler = ngx_http_spdy_ctl_frame_handler; ++ frame->stream = NULL; ++ } ++ ++#if (NGX_DEBUG) ++ if (length > NGX_SPDY_CTL_FRAME_BUFFER_SIZE - NGX_SPDY_FRAME_HEADER_SIZE) { ++ ngx_log_error(NGX_LOG_ALERT, sc->pool->log, 0, ++ "requested control frame is too large: %uz", length); ++ return NULL; ++ } ++ ++ frame->length = length; ++#endif ++ ++ frame->priority = priority; ++ frame->blocked = 0; ++ ++ return frame; ++} ++ ++ ++static ngx_int_t ++ngx_http_spdy_ctl_frame_handler(ngx_http_spdy_connection_t *sc, ++ ngx_http_spdy_out_frame_t *frame) ++{ ++ ngx_buf_t *buf; ++ ++ buf = frame->first->buf; ++ ++ if (buf->pos != buf->last) { ++ return NGX_AGAIN; ++ } ++ ++ frame->next = sc->free_ctl_frames; ++ sc->free_ctl_frames = frame; ++ ++ return NGX_OK; ++} ++ ++ ++static ngx_http_spdy_stream_t * ++ngx_http_spdy_create_stream(ngx_http_spdy_connection_t *sc, ngx_uint_t id, ++ ngx_uint_t priority) ++{ ++ ngx_log_t *log; ++ ngx_uint_t index; ++ ngx_event_t *rev, *wev; ++ ngx_connection_t *fc; ++ ngx_http_log_ctx_t *ctx; ++ ngx_http_request_t *r; ++ ngx_http_spdy_stream_t *stream; ++ ngx_http_core_srv_conf_t *cscf; ++ ngx_http_spdy_srv_conf_t *sscf; ++ ++ fc = sc->free_fake_connections; ++ ++ if (fc) { ++ sc->free_fake_connections = fc->data; ++ ++ rev = fc->read; ++ wev = fc->write; ++ log = fc->log; ++ ctx = log->data; ++ ++ } else { ++ fc = ngx_palloc(sc->pool, sizeof(ngx_connection_t)); ++ if (fc == NULL) { ++ return NULL; ++ } ++ ++ rev = ngx_palloc(sc->pool, sizeof(ngx_event_t)); ++ if (rev == NULL) { ++ return NULL; ++ } ++ ++ wev = ngx_palloc(sc->pool, sizeof(ngx_event_t)); ++ if (wev == NULL) { ++ return NULL; ++ } ++ ++ log = ngx_palloc(sc->pool, sizeof(ngx_log_t)); ++ if (log == NULL) { ++ return NULL; ++ } ++ ++ ctx = ngx_palloc(sc->pool, sizeof(ngx_http_log_ctx_t)); ++ if (ctx == NULL) { ++ return NULL; ++ } ++ ++ ctx->connection = fc; ++ ctx->request = NULL; ++ } ++ ++ ngx_memcpy(log, sc->connection->log, sizeof(ngx_log_t)); ++ ++ log->data = ctx; ++ ++ ngx_memzero(rev, sizeof(ngx_event_t)); ++ ++ rev->data = fc; ++ rev->ready = 1; ++ rev->handler = ngx_http_spdy_close_stream_handler; ++ rev->log = log; ++ ++ ngx_memcpy(wev, rev, sizeof(ngx_event_t)); ++ ++ wev->write = 1; ++ ++ ngx_memcpy(fc, sc->connection, sizeof(ngx_connection_t)); ++ ++ fc->data = sc->http_connection; ++ fc->read = rev; ++ fc->write = wev; ++ fc->sent = 0; ++ fc->log = log; ++ fc->buffered = 0; ++ fc->sndlowat = 1; ++ fc->tcp_nodelay = NGX_TCP_NODELAY_DISABLED; ++ ++ r = ngx_http_create_request(fc); ++ if (r == NULL) { ++ return NULL; ++ } ++ ++ r->valid_location = 1; ++ ++ fc->data = r; ++ sc->connection->requests++; ++ ++ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); ++ ++ r->header_in = ngx_create_temp_buf(r->pool, ++ cscf->client_header_buffer_size); ++ if (r->header_in == NULL) { ++ ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); ++ return NULL; ++ } ++ ++ r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; ++ ++ stream = ngx_pcalloc(r->pool, sizeof(ngx_http_spdy_stream_t)); ++ if (stream == NULL) { ++ ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); ++ return NULL; ++ } ++ ++ r->spdy_stream = stream; ++ ++ stream->id = id; ++ stream->request = r; ++ stream->connection = sc; ++ ++ stream->send_window = sc->init_window; ++ stream->recv_window = NGX_SPDY_STREAM_WINDOW; ++ ++ stream->priority = priority; ++ ++ sscf = ngx_http_get_module_srv_conf(r, ngx_http_spdy_module); ++ ++ index = ngx_http_spdy_stream_index(sscf, id); ++ ++ stream->index = sc->streams_index[index]; ++ sc->streams_index[index] = stream; ++ ++ sc->processing++; ++ ++ return stream; ++} ++ ++ ++static ngx_http_spdy_stream_t * ++ngx_http_spdy_get_stream_by_id(ngx_http_spdy_connection_t *sc, ++ ngx_uint_t sid) ++{ ++ ngx_http_spdy_stream_t *stream; ++ ngx_http_spdy_srv_conf_t *sscf; ++ ++ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, ++ ngx_http_spdy_module); ++ ++ stream = sc->streams_index[ngx_http_spdy_stream_index(sscf, sid)]; ++ ++ while (stream) { ++ if (stream->id == sid) { ++ return stream; ++ } ++ ++ stream = stream->index; ++ } ++ ++ return NULL; ++} ++ ++ ++static ngx_int_t ++ngx_http_spdy_parse_header(ngx_http_request_t *r) ++{ ++ u_char *p, *end, ch; ++ ngx_uint_t hash; ++ ngx_http_core_srv_conf_t *cscf; ++ ++ enum { ++ sw_name_len = 0, ++ sw_name, ++ sw_value_len, ++ sw_value ++ } state; ++ ++ state = r->state; ++ ++ p = r->header_in->pos; ++ end = r->header_in->last; ++ ++ switch (state) { ++ ++ case sw_name_len: ++ ++ if (end - p < NGX_SPDY_NV_NLEN_SIZE) { ++ return NGX_AGAIN; ++ } ++ ++ r->lowcase_index = ngx_spdy_frame_parse_uint32(p); ++ ++ if (r->lowcase_index == 0) { ++ return NGX_ERROR; ++ } ++ ++ /* null-terminate the previous header value */ ++ *p = '\0'; ++ ++ p += NGX_SPDY_NV_NLEN_SIZE; ++ ++ r->invalid_header = 0; ++ ++ state = sw_name; ++ ++ /* fall through */ ++ ++ case sw_name: ++ ++ if ((ngx_uint_t) (end - p) < r->lowcase_index) { ++ break; ++ } ++ ++ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); ++ ++ r->header_name_start = p; ++ r->header_name_end = p + r->lowcase_index; ++ ++ if (p[0] == ':') { ++ p++; ++ } ++ ++ hash = 0; ++ ++ for ( /* void */ ; p != r->header_name_end; p++) { ++ ++ ch = *p; ++ ++ hash = ngx_hash(hash, ch); ++ ++ if ((ch >= 'a' && ch <= 'z') ++ || (ch == '-') ++ || (ch >= '0' && ch <= '9') ++ || (ch == '_' && cscf->underscores_in_headers)) ++ { ++ continue; ++ } ++ ++ switch (ch) { ++ case '\0': ++ case LF: ++ case CR: ++ case ':': ++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ++ "client sent invalid header name: \"%*s\"", ++ r->lowcase_index, r->header_name_start); ++ ++ return NGX_HTTP_PARSE_INVALID_HEADER; ++ } ++ ++ if (ch >= 'A' && ch <= 'Z') { ++ return NGX_ERROR; ++ } ++ ++ r->invalid_header = 1; ++ } ++ ++ r->header_hash = hash; ++ ++ state = sw_value_len; ++ ++ /* fall through */ ++ ++ case sw_value_len: ++ ++ if (end - p < NGX_SPDY_NV_VLEN_SIZE) { ++ break; ++ } ++ ++ r->lowcase_index = ngx_spdy_frame_parse_uint32(p); ++ ++ /* null-terminate header name */ ++ *p = '\0'; ++ ++ p += NGX_SPDY_NV_VLEN_SIZE; ++ ++ state = sw_value; ++ ++ /* fall through */ ++ ++ case sw_value: ++ ++ if ((ngx_uint_t) (end - p) < r->lowcase_index) { ++ break; ++ } ++ ++ r->header_start = p; ++ ++ while (r->lowcase_index--) { ++ ch = *p; ++ ++ if (ch == '\0') { ++ ++ if (p == r->header_start) { ++ return NGX_ERROR; ++ } ++ ++ r->header_end = p; ++ r->header_in->pos = p + 1; ++ ++ r->state = sw_value; ++ ++ return NGX_OK; ++ } ++ ++ if (ch == CR || ch == LF) { ++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ++ "client sent header \"%*s\" with " ++ "invalid value: \"%*s\\%c...\"", ++ r->header_name_end - r->header_name_start, ++ r->header_name_start, ++ p - r->header_start, ++ r->header_start, ++ ch == CR ? 'r' : 'n'); ++ ++ return NGX_HTTP_PARSE_INVALID_HEADER; ++ } ++ ++ p++; ++ } ++ ++ r->header_end = p; ++ r->header_in->pos = p; ++ ++ r->state = 0; ++ ++ return NGX_DONE; ++ } ++ ++ r->header_in->pos = p; ++ r->state = state; ++ ++ return NGX_AGAIN; ++} ++ ++ ++static ngx_int_t ++ngx_http_spdy_alloc_large_header_buffer(ngx_http_request_t *r) ++{ ++ u_char *old, *new, *p; ++ 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) ++ { ++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ++ "client sent too large request"); ++ ++ return NGX_DECLINED; ++ } ++ ++ rest = r->header_in->last - r->header_in->pos; ++ ++ /* ++ * One more byte is needed for null-termination ++ * and another one for further progress. ++ */ ++ if (rest > cscf->large_client_header_buffers.size - 2) { ++ p = r->header_in->pos; ++ ++ if (rest > NGX_MAX_ERROR_STR - 300) { ++ rest = NGX_MAX_ERROR_STR - 300; ++ } ++ ++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ++ "client sent too long header name or value: \"%*s...\"", ++ rest, p); ++ ++ 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); ++ } ++ ++ r->header_in = buf; ++ ++ stream->header_buffers++; ++ ++ return NGX_OK; ++} ++ ++ ++static ngx_int_t ++ngx_http_spdy_handle_request_header(ngx_http_request_t *r) ++{ ++ ngx_uint_t i; ++ ngx_table_elt_t *h; ++ ngx_http_core_srv_conf_t *cscf; ++ ngx_http_spdy_request_header_t *sh; ++ ++ if (r->invalid_header) { ++ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); ++ ++ if (cscf->ignore_invalid_headers) { ++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ++ "client sent invalid header: \"%*s\"", ++ r->header_end - r->header_name_start, ++ r->header_name_start); ++ return NGX_OK; ++ } ++ ++ } ++ ++ if (r->header_name_start[0] == ':') { ++ r->header_name_start++; ++ ++ for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) { ++ sh = &ngx_http_spdy_request_headers[i]; ++ ++ if (sh->hash != r->header_hash ++ || sh->len != r->header_name_end - r->header_name_start ++ || ngx_strncmp(sh->header, r->header_name_start, sh->len) != 0) ++ { ++ continue; ++ } ++ ++ return sh->handler(r); ++ } ++ ++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ++ "client sent invalid header name: \":%*s\"", ++ r->header_end - r->header_name_start, ++ r->header_name_start); ++ ++ return NGX_HTTP_PARSE_INVALID_HEADER; ++ } ++ ++ 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_end - r->header_start; ++ h->value.data = r->header_start; ++ ++ h->lowcase_key = h->key.data; ++ ++ return NGX_OK; ++} ++ ++ ++void ++ngx_http_spdy_request_headers_init(void) ++{ ++ ngx_uint_t i; ++ ngx_http_spdy_request_header_t *h; ++ ++ for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) { ++ h = &ngx_http_spdy_request_headers[i]; ++ h->hash = ngx_hash_key(h->header, h->len); ++ } ++} ++ ++ ++static ngx_int_t ++ngx_http_spdy_parse_method(ngx_http_request_t *r) ++{ ++ size_t k, len; ++ ngx_uint_t n; ++ const u_char *p, *m; ++ ++ /* ++ * This array takes less than 256 sequential bytes, ++ * and if typical CPU cache line size is 64 bytes, ++ * it is prefetched for 4 load operations. ++ */ ++ static const struct { ++ u_char len; ++ const u_char method[11]; ++ uint32_t value; ++ } tests[] = { ++ { 3, "GET", NGX_HTTP_GET }, ++ { 4, "POST", NGX_HTTP_POST }, ++ { 4, "HEAD", NGX_HTTP_HEAD }, ++ { 7, "OPTIONS", NGX_HTTP_OPTIONS }, ++ { 8, "PROPFIND", NGX_HTTP_PROPFIND }, ++ { 3, "PUT", NGX_HTTP_PUT }, ++ { 5, "MKCOL", NGX_HTTP_MKCOL }, ++ { 6, "DELETE", NGX_HTTP_DELETE }, ++ { 4, "COPY", NGX_HTTP_COPY }, ++ { 4, "MOVE", NGX_HTTP_MOVE }, ++ { 9, "PROPPATCH", NGX_HTTP_PROPPATCH }, ++ { 4, "LOCK", NGX_HTTP_LOCK }, ++ { 6, "UNLOCK", NGX_HTTP_UNLOCK }, ++ { 5, "PATCH", NGX_HTTP_PATCH }, ++ { 5, "TRACE", NGX_HTTP_TRACE } ++ }, *test; ++ ++ if (r->method_name.len) { ++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ++ "client sent duplicate :method header"); ++ ++ return NGX_HTTP_PARSE_INVALID_HEADER; ++ } ++ ++ len = r->header_end - r->header_start; ++ ++ r->method_name.len = len; ++ r->method_name.data = r->header_start; ++ ++ test = tests; ++ n = sizeof(tests) / sizeof(tests[0]); ++ ++ do { ++ if (len == test->len) { ++ p = r->method_name.data; ++ m = test->method; ++ k = len; ++ ++ do { ++ if (*p++ != *m++) { ++ goto next; ++ } ++ } while (--k); ++ ++ r->method = test->value; ++ return NGX_OK; ++ } ++ ++ next: ++ test++; ++ ++ } while (--n); ++ ++ p = r->method_name.data; ++ ++ do { ++ if ((*p < 'A' || *p > 'Z') && *p != '_') { ++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ++ "client sent invalid method: \"%V\"", ++ &r->method_name); ++ ++ return NGX_HTTP_PARSE_INVALID_HEADER; ++ } ++ ++ p++; ++ ++ } while (--len); ++ ++ return NGX_OK; ++} ++ ++ ++static ngx_int_t ++ngx_http_spdy_parse_scheme(ngx_http_request_t *r) ++{ ++ if (r->schema_start) { ++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ++ "client sent duplicate :schema header"); ++ ++ return NGX_HTTP_PARSE_INVALID_HEADER; ++ } ++ ++ r->schema_start = r->header_start; ++ r->schema_end = r->header_end; ++ ++ return NGX_OK; ++} ++ ++ ++static ngx_int_t ++ngx_http_spdy_parse_host(ngx_http_request_t *r) ++{ ++ ngx_table_elt_t *h; ++ ++ if (r->headers_in.host) { ++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ++ "client sent duplicate :host header"); ++ ++ return NGX_HTTP_PARSE_INVALID_HEADER; ++ } ++ ++ h = ngx_list_push(&r->headers_in.headers); ++ if (h == NULL) { ++ return NGX_ERROR; ++ } ++ ++ r->headers_in.host = h; ++ ++ 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_end - r->header_start; ++ h->value.data = r->header_start; ++ ++ h->lowcase_key = h->key.data; ++ ++ return NGX_OK; ++} ++ ++ ++static ngx_int_t ++ngx_http_spdy_parse_path(ngx_http_request_t *r) ++{ ++ if (r->unparsed_uri.len) { ++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ++ "client sent duplicate :path header"); ++ ++ return NGX_HTTP_PARSE_INVALID_HEADER; ++ } ++ ++ r->uri_start = r->header_start; ++ r->uri_end = r->header_end; ++ ++ if (ngx_http_parse_uri(r) != NGX_OK) { ++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ++ "client sent invalid URI: \"%*s\"", ++ r->uri_end - r->uri_start, r->uri_start); ++ ++ return NGX_HTTP_PARSE_INVALID_HEADER; ++ } ++ ++ if (ngx_http_process_request_uri(r) != NGX_OK) { ++ /* ++ * request has been finalized already ++ * in ngx_http_process_request_uri() ++ */ ++ return NGX_ABORT; ++ } ++ ++ return NGX_OK; ++} ++ ++ ++static ngx_int_t ++ngx_http_spdy_parse_version(ngx_http_request_t *r) ++{ ++ u_char *p, ch; ++ ++ if (r->http_protocol.len) { ++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ++ "client sent duplicate :version header"); ++ ++ return NGX_HTTP_PARSE_INVALID_HEADER; ++ } ++ ++ p = r->header_start; ++ ++ if (r->header_end - p < 8 || !(ngx_str5cmp(p, 'H', 'T', 'T', 'P', '/'))) { ++ goto invalid; ++ } ++ ++ ch = *(p + 5); ++ ++ if (ch < '1' || ch > '9') { ++ goto invalid; ++ } ++ ++ r->http_major = ch - '0'; ++ ++ for (p += 6; p != r->header_end - 2; p++) { ++ ++ ch = *p; ++ ++ if (ch == '.') { ++ break; ++ } ++ ++ if (ch < '0' || ch > '9') { ++ goto invalid; ++ } ++ ++ r->http_major = r->http_major * 10 + ch - '0'; ++ } ++ ++ if (*p != '.') { ++ goto invalid; ++ } ++ ++ ch = *(p + 1); ++ ++ if (ch < '0' || ch > '9') { ++ goto invalid; ++ } ++ ++ r->http_minor = ch - '0'; ++ ++ for (p += 2; p != r->header_end; p++) { ++ ++ ch = *p; ++ ++ if (ch < '0' || ch > '9') { ++ goto invalid; ++ } ++ ++ r->http_minor = r->http_minor * 10 + ch - '0'; ++ } ++ ++ r->http_protocol.len = r->header_end - r->header_start; ++ r->http_protocol.data = r->header_start; ++ r->http_version = r->http_major * 1000 + r->http_minor; ++ ++ return NGX_OK; ++ ++invalid: ++ ++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ++ "client sent invalid http version: \"%*s\"", ++ r->header_end - r->header_start, r->header_start); ++ ++ return NGX_HTTP_PARSE_INVALID_HEADER; ++} ++ ++ ++static ngx_int_t ++ngx_http_spdy_construct_request_line(ngx_http_request_t *r) ++{ ++ u_char *p; ++ ++ if (r->method_name.len == 0 ++ || r->unparsed_uri.len == 0 ++ || r->http_protocol.len == 0) ++ { ++ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); ++ return NGX_ERROR; ++ } ++ ++ r->request_line.len = r->method_name.len + 1 ++ + r->unparsed_uri.len + 1 ++ + r->http_protocol.len; ++ ++ p = ngx_pnalloc(r->pool, r->request_line.len + 1); ++ if (p == NULL) { ++ ngx_http_spdy_close_stream(r->spdy_stream, ++ NGX_HTTP_INTERNAL_SERVER_ERROR); ++ return NGX_ERROR; ++ } ++ ++ r->request_line.data = p; ++ ++ p = ngx_cpymem(p, r->method_name.data, r->method_name.len); ++ ++ *p++ = ' '; ++ ++ p = ngx_cpymem(p, r->unparsed_uri.data, r->unparsed_uri.len); ++ ++ *p++ = ' '; ++ ++ ngx_memcpy(p, r->http_protocol.data, r->http_protocol.len + 1); ++ ++ /* some modules expect the space character after method name */ ++ r->method_name.data = r->request_line.data; ++ ++ return NGX_OK; ++} ++ ++ ++static void ++ngx_http_spdy_run_request(ngx_http_request_t *r) ++{ ++ ngx_uint_t i; ++ ngx_list_part_t *part; ++ ngx_table_elt_t *h; ++ ngx_http_header_t *hh; ++ ngx_http_core_main_conf_t *cmcf; ++ ++ if (ngx_http_spdy_construct_request_line(r) != NGX_OK) { ++ return; ++ } ++ ++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ++ "spdy http request line: \"%V\"", &r->request_line); ++ ++ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); ++ ++ part = &r->headers_in.headers.part; ++ h = part->elts; ++ ++ for (i = 0 ;; i++) { ++ ++ if (i >= part->nelts) { ++ if (part->next == NULL) { ++ break; ++ } ++ ++ part = part->next; ++ h = part->elts; ++ i = 0; ++ } ++ ++ hh = ngx_hash_find(&cmcf->headers_in_hash, h[i].hash, ++ h[i].lowcase_key, h[i].key.len); ++ ++ if (hh && hh->handler(r, &h[i], hh->offset) != NGX_OK) { ++ return; ++ } ++ ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ++ "spdy 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->headers_in.content_length_n > 0 && r->spdy_stream->in_closed) { ++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ++ "client prematurely closed stream"); ++ ++ r->spdy_stream->skip_data = NGX_SPDY_DATA_ERROR; ++ ++ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); ++ return; ++ } ++ ++ ngx_http_process_request(r); ++} ++ ++ ++static ngx_int_t ++ngx_http_spdy_init_request_body(ngx_http_request_t *r) ++{ ++ ngx_buf_t *buf; ++ ngx_temp_file_t *tf; ++ ngx_http_request_body_t *rb; ++ ngx_http_core_loc_conf_t *clcf; ++ ++ rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); ++ if (rb == NULL) { ++ return NGX_ERROR; ++ } ++ ++ r->request_body = rb; ++ ++ if (r->spdy_stream->in_closed) { ++ return NGX_OK; ++ } ++ ++ rb->rest = r->headers_in.content_length_n; ++ ++ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ++ ++ if (r->request_body_in_file_only ++ || rb->rest > (off_t) clcf->client_body_buffer_size ++ || rb->rest < 0) ++ { ++ tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); ++ if (tf == NULL) { ++ return NGX_ERROR; ++ } ++ ++ tf->file.fd = NGX_INVALID_FILE; ++ tf->file.log = r->connection->log; ++ tf->path = clcf->client_body_temp_path; ++ tf->pool = r->pool; ++ tf->warn = "a client request body is buffered to a temporary file"; ++ tf->log_level = r->request_body_file_log_level; ++ tf->persistent = r->request_body_in_persistent_file; ++ tf->clean = r->request_body_in_clean_file; ++ ++ if (r->request_body_file_group_access) { ++ tf->access = 0660; ++ } ++ ++ rb->temp_file = tf; ++ ++ if (r->spdy_stream->in_closed ++ && ngx_create_temp_file(&tf->file, tf->path, tf->pool, ++ tf->persistent, tf->clean, tf->access) ++ != NGX_OK) ++ { ++ return NGX_ERROR; ++ } ++ ++ buf = ngx_calloc_buf(r->pool); ++ if (buf == NULL) { ++ return NGX_ERROR; ++ } ++ ++ } else { ++ ++ if (rb->rest == 0) { ++ return NGX_OK; ++ } ++ ++ buf = ngx_create_temp_buf(r->pool, (size_t) rb->rest); ++ if (buf == NULL) { ++ return NGX_ERROR; ++ } ++ } ++ ++ rb->buf = buf; ++ ++ rb->bufs = ngx_alloc_chain_link(r->pool); ++ if (rb->bufs == NULL) { ++ return NGX_ERROR; ++ } ++ ++ rb->bufs->buf = buf; ++ rb->bufs->next = NULL; ++ ++ rb->rest = 0; ++ ++ return NGX_OK; ++} ++ ++ ++ngx_int_t ++ngx_http_spdy_read_request_body(ngx_http_request_t *r, ++ ngx_http_client_body_handler_pt post_handler) ++{ ++ ngx_http_spdy_stream_t *stream; ++ ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ++ "spdy read request body"); ++ ++ stream = r->spdy_stream; ++ ++ switch (stream->skip_data) { ++ ++ case NGX_SPDY_DATA_DISCARD: ++ post_handler(r); ++ return NGX_OK; ++ ++ case NGX_SPDY_DATA_ERROR: ++ if (r->headers_in.content_length_n == -1) { ++ return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; ++ } else { ++ return NGX_HTTP_BAD_REQUEST; ++ } ++ ++ case NGX_SPDY_DATA_INTERNAL_ERROR: ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ if (!r->request_body && ngx_http_spdy_init_request_body(r) != NGX_OK) { ++ stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR; ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ if (stream->in_closed) { ++ post_handler(r); ++ return NGX_OK; ++ } ++ ++ r->request_body->post_handler = post_handler; ++ ++ r->read_event_handler = ngx_http_test_reading; ++ r->write_event_handler = ngx_http_request_empty_handler; ++ ++ return NGX_AGAIN; ++} ++ ++ ++static ngx_int_t ++ngx_http_spdy_terminate_stream(ngx_http_spdy_connection_t *sc, ++ ngx_http_spdy_stream_t *stream, ngx_uint_t status) ++{ ++ ngx_event_t *rev; ++ ngx_connection_t *fc; ++ ++ if (ngx_http_spdy_send_rst_stream(sc, stream->id, status, ++ NGX_SPDY_HIGHEST_PRIORITY) ++ == NGX_ERROR) ++ { ++ return NGX_ERROR; ++ } ++ ++ stream->out_closed = 1; ++ ++ fc = stream->request->connection; ++ fc->error = 1; ++ ++ rev = fc->read; ++ rev->handler(rev); ++ ++ return NGX_OK; ++} ++ ++ ++static void ++ngx_http_spdy_close_stream_handler(ngx_event_t *ev) ++{ ++ ngx_connection_t *fc; ++ ngx_http_request_t *r; ++ ++ fc = ev->data; ++ r = fc->data; ++ ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ++ "spdy close stream handler"); ++ ++ ngx_http_spdy_close_stream(r->spdy_stream, 0); ++} ++ ++ ++void ++ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc) ++{ ++ ngx_event_t *ev; ++ ngx_connection_t *fc; ++ ngx_http_spdy_stream_t **index, *s; ++ ngx_http_spdy_srv_conf_t *sscf; ++ ngx_http_spdy_connection_t *sc; ++ ++ sc = stream->connection; ++ ++ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy close stream %ui, queued %ui, processing %ui", ++ stream->id, stream->queued, sc->processing); ++ ++ fc = stream->request->connection; ++ ++ if (stream->queued) { ++ fc->write->handler = ngx_http_spdy_close_stream_handler; ++ return; ++ } ++ ++ if (!stream->out_closed) { ++ if (ngx_http_spdy_send_rst_stream(sc, stream->id, ++ NGX_SPDY_INTERNAL_ERROR, ++ stream->priority) ++ != NGX_OK) ++ { ++ sc->connection->error = 1; ++ } ++ } ++ ++ if (sc->stream == stream) { ++ sc->stream = NULL; ++ } ++ ++ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, ++ ngx_http_spdy_module); ++ ++ index = sc->streams_index + ngx_http_spdy_stream_index(sscf, stream->id); ++ ++ for ( ;; ) { ++ s = *index; ++ ++ if (s == NULL) { ++ break; ++ } ++ ++ if (s == stream) { ++ *index = s->index; ++ break; ++ } ++ ++ index = &s->index; ++ } ++ ++ ngx_http_free_request(stream->request, rc); ++ ++ ev = fc->read; ++ ++ if (ev->active || ev->disabled) { ++ ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0, ++ "fake read event was activated"); ++ } ++ ++ if (ev->timer_set) { ++ ngx_del_timer(ev); ++ } ++ ++ if (ev->posted) { ++ ngx_delete_posted_event(ev); ++ } ++ ++ ev = fc->write; ++ ++ if (ev->active || ev->disabled) { ++ ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0, ++ "fake write event was activated"); ++ } ++ ++ if (ev->timer_set) { ++ ngx_del_timer(ev); ++ } ++ ++ if (ev->posted) { ++ ngx_delete_posted_event(ev); ++ } ++ ++ fc->data = sc->free_fake_connections; ++ sc->free_fake_connections = fc; ++ ++ sc->processing--; ++ ++ if (sc->processing || sc->blocked) { ++ return; ++ } ++ ++ ev = sc->connection->read; ++ ++ ev->handler = ngx_http_spdy_handle_connection_handler; ++ ngx_post_event(ev, &ngx_posted_events); ++} ++ ++ ++static void ++ngx_http_spdy_handle_connection_handler(ngx_event_t *rev) ++{ ++ ngx_connection_t *c; ++ ++ rev->handler = ngx_http_spdy_read_handler; ++ ++ if (rev->ready) { ++ ngx_http_spdy_read_handler(rev); ++ return; ++ } ++ ++ c = rev->data; ++ ++ ngx_http_spdy_handle_connection(c->data); ++} ++ ++ ++static void ++ngx_http_spdy_keepalive_handler(ngx_event_t *rev) ++{ ++ ngx_connection_t *c; ++ ngx_http_spdy_srv_conf_t *sscf; ++ ngx_http_spdy_connection_t *sc; ++ ++ c = rev->data; ++ ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy keepalive handler"); ++ ++ if (rev->timedout || c->close) { ++ ngx_http_close_connection(c); ++ return; ++ } ++ ++#if (NGX_HAVE_KQUEUE) ++ ++ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { ++ if (rev->pending_eof) { ++ c->log->handler = NULL; ++ ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno, ++ "kevent() reported that client %V closed " ++ "keepalive connection", &c->addr_text); ++#if (NGX_HTTP_SSL) ++ if (c->ssl) { ++ c->ssl->no_send_shutdown = 1; ++ } ++#endif ++ ngx_http_close_connection(c); ++ return; ++ } ++ } ++ ++#endif ++ ++ c->destroyed = 0; ++ c->idle = 0; ++ ngx_reusable_connection(c, 0); ++ ++ sc = c->data; ++ ++ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, ++ ngx_http_spdy_module); ++ ++ sc->pool = ngx_create_pool(sscf->pool_size, sc->connection->log); ++ if (sc->pool == NULL) { ++ ngx_http_close_connection(c); ++ return; ++ } ++ ++ sc->streams_index = ngx_pcalloc(sc->pool, ++ ngx_http_spdy_streams_index_size(sscf) ++ * sizeof(ngx_http_spdy_stream_t *)); ++ if (sc->streams_index == NULL) { ++ ngx_http_close_connection(c); ++ return; ++ } ++ ++ c->write->handler = ngx_http_spdy_write_handler; ++ ++ rev->handler = ngx_http_spdy_read_handler; ++ ngx_http_spdy_read_handler(rev); ++} ++ ++ ++static void ++ngx_http_spdy_finalize_connection(ngx_http_spdy_connection_t *sc, ++ ngx_int_t rc) ++{ ++ ngx_uint_t i, size; ++ ngx_event_t *ev; ++ ngx_connection_t *c, *fc; ++ ngx_http_request_t *r; ++ ngx_http_spdy_stream_t *stream; ++ ngx_http_spdy_srv_conf_t *sscf; ++ ++ c = sc->connection; ++ ++ if (!sc->processing) { ++ ngx_http_close_connection(c); ++ return; ++ } ++ ++ c->error = 1; ++ c->read->handler = ngx_http_empty_handler; ++ c->write->handler = ngx_http_empty_handler; ++ ++ sc->last_out = NULL; ++ ++ sc->blocked = 1; ++ ++ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, ++ ngx_http_spdy_module); ++ ++ size = ngx_http_spdy_streams_index_size(sscf); ++ ++ for (i = 0; i < size; i++) { ++ stream = sc->streams_index[i]; ++ ++ while (stream) { ++ stream->handled = 0; ++ ++ r = stream->request; ++ fc = r->connection; ++ ++ fc->error = 1; ++ ++ if (stream->queued) { ++ stream->queued = 0; ++ ++ ev = fc->write; ++ ev->delayed = 0; ++ ++ } else { ++ ev = fc->read; ++ } ++ ++ stream = stream->index; ++ ++ ev->eof = 1; ++ ev->handler(ev); ++ } ++ } ++ ++ sc->blocked = 0; ++ ++ if (sc->processing) { ++ return; ++ } ++ ++ ngx_http_close_connection(c); ++} ++ ++ ++static ngx_int_t ++ngx_http_spdy_adjust_windows(ngx_http_spdy_connection_t *sc, ssize_t delta) ++{ ++ ngx_uint_t i, size; ++ ngx_event_t *wev; ++ ngx_http_spdy_stream_t *stream, *sn; ++ ngx_http_spdy_srv_conf_t *sscf; ++ ++ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, ++ ngx_http_spdy_module); ++ ++ size = ngx_http_spdy_streams_index_size(sscf); ++ ++ for (i = 0; i < size; i++) { ++ ++ for (stream = sc->streams_index[i]; stream; stream = sn) { ++ sn = stream->index; ++ ++ if (delta > 0 ++ && stream->send_window ++ > (ssize_t) (NGX_SPDY_MAX_WINDOW - delta)) ++ { ++ if (ngx_http_spdy_terminate_stream(sc, stream, ++ NGX_SPDY_FLOW_CONTROL_ERROR) ++ == NGX_ERROR) ++ { ++ return NGX_ERROR; ++ } ++ ++ continue; ++ } ++ ++ stream->send_window += delta; ++ ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy:%ui adjust window:%z", ++ stream->id, stream->send_window); ++ ++ if (stream->send_window > 0 && stream->exhausted) { ++ stream->exhausted = 0; ++ ++ wev = stream->request->connection->write; ++ ++ if (!wev->timer_set) { ++ wev->delayed = 0; ++ wev->handler(wev); ++ } ++ } ++ } ++ } ++ ++ return NGX_OK; ++} ++ ++ ++static void ++ngx_http_spdy_pool_cleanup(void *data) ++{ ++ ngx_http_spdy_connection_t *sc = data; ++ ++ if (sc->pool) { ++ ngx_destroy_pool(sc->pool); ++ } ++} ++ ++ ++static void * ++ngx_http_spdy_zalloc(void *opaque, u_int items, u_int size) ++{ ++ ngx_http_spdy_connection_t *sc = opaque; ++ ++ return ngx_palloc(sc->connection->pool, items * size); ++} ++ ++ ++static void ++ngx_http_spdy_zfree(void *opaque, void *address) ++{ ++#if 0 ++ ngx_http_spdy_connection_t *sc = opaque; ++ ++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy zfree: %p", address); ++#endif ++} +diff --git a/src/http/ngx_http_spdy.h b/src/http/ngx_http_spdy.h +new file mode 100644 +index 0000000..df24495 +--- /dev/null ++++ b/src/http/ngx_http_spdy.h +@@ -0,0 +1,261 @@ ++/* ++ * 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 3 ++ ++#define NGX_SPDY_NPN_ADVERTISE "\x08spdy/3.1" ++#define NGX_SPDY_NPN_NEGOTIATED "spdy/3.1" ++ ++#define NGX_SPDY_STATE_BUFFER_SIZE 16 ++ ++#define NGX_SPDY_CTL_BIT 1 ++ ++#define NGX_SPDY_SYN_STREAM 1 ++#define NGX_SPDY_SYN_REPLY 2 ++#define NGX_SPDY_RST_STREAM 3 ++#define NGX_SPDY_SETTINGS 4 ++#define NGX_SPDY_PING 6 ++#define NGX_SPDY_GOAWAY 7 ++#define NGX_SPDY_HEADERS 8 ++#define NGX_SPDY_WINDOW_UPDATE 9 ++ ++#define NGX_SPDY_FRAME_HEADER_SIZE 8 ++ ++#define NGX_SPDY_SID_SIZE 4 ++#define NGX_SPDY_DELTA_SIZE 4 ++ ++#define NGX_SPDY_SYN_STREAM_SIZE 10 ++#define NGX_SPDY_SYN_REPLY_SIZE 4 ++#define NGX_SPDY_RST_STREAM_SIZE 8 ++#define NGX_SPDY_PING_SIZE 4 ++#define NGX_SPDY_GOAWAY_SIZE 8 ++#define NGX_SPDY_WINDOW_UPDATE_SIZE 8 ++#define NGX_SPDY_NV_NUM_SIZE 4 ++#define NGX_SPDY_NV_NLEN_SIZE 4 ++#define NGX_SPDY_NV_VLEN_SIZE 4 ++#define NGX_SPDY_SETTINGS_NUM_SIZE 4 ++#define NGX_SPDY_SETTINGS_FID_SIZE 4 ++#define NGX_SPDY_SETTINGS_VAL_SIZE 4 ++ ++#define NGX_SPDY_SETTINGS_PAIR_SIZE \ ++ (NGX_SPDY_SETTINGS_FID_SIZE + NGX_SPDY_SETTINGS_VAL_SIZE) ++ ++#define NGX_SPDY_HIGHEST_PRIORITY 0 ++#define NGX_SPDY_LOWEST_PRIORITY 7 ++ ++#define NGX_SPDY_FLAG_FIN 0x01 ++#define NGX_SPDY_FLAG_UNIDIRECTIONAL 0x02 ++#define NGX_SPDY_FLAG_CLEAR_SETTINGS 0x01 ++ ++#define NGX_SPDY_MAX_FRAME_SIZE ((1 << 24) - 1) ++ ++#define NGX_SPDY_DATA_DISCARD 1 ++#define NGX_SPDY_DATA_ERROR 2 ++#define NGX_SPDY_DATA_INTERNAL_ERROR 3 ++ ++ ++typedef struct ngx_http_spdy_connection_s ngx_http_spdy_connection_t; ++typedef struct ngx_http_spdy_out_frame_s ngx_http_spdy_out_frame_t; ++ ++ ++typedef u_char *(*ngx_http_spdy_handler_pt) (ngx_http_spdy_connection_t *sc, ++ u_char *pos, u_char *end); ++ ++struct ngx_http_spdy_connection_s { ++ ngx_connection_t *connection; ++ ngx_http_connection_t *http_connection; ++ ++ ngx_uint_t processing; ++ ++ size_t send_window; ++ size_t recv_window; ++ size_t init_window; ++ ++ ngx_queue_t waiting; ++ ++ u_char buffer[NGX_SPDY_STATE_BUFFER_SIZE]; ++ size_t buffer_used; ++ ngx_http_spdy_handler_pt handler; ++ ++ z_stream zstream_in; ++ z_stream zstream_out; ++ ++ ngx_pool_t *pool; ++ ++ ngx_http_spdy_out_frame_t *free_ctl_frames; ++ ngx_connection_t *free_fake_connections; ++ ++ ngx_http_spdy_stream_t **streams_index; ++ ++ ngx_http_spdy_out_frame_t *last_out; ++ ++ ngx_queue_t posted; ++ ++ ngx_http_spdy_stream_t *stream; ++ ++ ngx_uint_t entries; ++ size_t length; ++ u_char flags; ++ ++ ngx_uint_t last_sid; ++ ++ unsigned blocked:1; ++ unsigned incomplete: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_uint_t header_buffers; ++ ngx_uint_t queued; ++ ++ /* ++ * A change to SETTINGS_INITIAL_WINDOW_SIZE could cause the ++ * send_window to become negative, hence it's signed. ++ */ ++ ssize_t send_window; ++ size_t recv_window; ++ ++ ngx_http_spdy_out_frame_t *free_frames; ++ ngx_chain_t *free_data_headers; ++ ngx_chain_t *free_bufs; ++ ++ ngx_queue_t queue; ++ ++ unsigned priority:3; ++ unsigned handled:1; ++ unsigned blocked:1; ++ unsigned exhausted:1; ++ unsigned in_closed:1; ++ unsigned out_closed:1; ++ unsigned skip_data:2; ++}; ++ ++ ++struct ngx_http_spdy_out_frame_s { ++ ngx_http_spdy_out_frame_t *next; ++ ngx_chain_t *first; ++ ngx_chain_t *last; ++ ngx_int_t (*handler)(ngx_http_spdy_connection_t *sc, ++ ngx_http_spdy_out_frame_t *frame); ++ ++ ngx_http_spdy_stream_t *stream; ++ size_t length; ++ ++ ngx_uint_t priority; ++ unsigned blocked:1; ++ unsigned fin:1; ++}; ++ ++ ++static ngx_inline void ++ngx_http_spdy_queue_frame(ngx_http_spdy_connection_t *sc, ++ ngx_http_spdy_out_frame_t *frame) ++{ ++ ngx_http_spdy_out_frame_t **out; ++ ++ for (out = &sc->last_out; *out; out = &(*out)->next) ++ { ++ /* ++ * NB: higher values represent lower priorities. ++ */ ++ if (frame->priority >= (*out)->priority) { ++ break; ++ } ++ } ++ ++ frame->next = *out; ++ *out = frame; ++} ++ ++ ++static ngx_inline void ++ngx_http_spdy_queue_blocked_frame(ngx_http_spdy_connection_t *sc, ++ ngx_http_spdy_out_frame_t *frame) ++{ ++ ngx_http_spdy_out_frame_t **out; ++ ++ for (out = &sc->last_out; *out; out = &(*out)->next) ++ { ++ if ((*out)->blocked) { ++ break; ++ } ++ } ++ ++ frame->next = *out; ++ *out = frame; ++} ++ ++ ++void ngx_http_spdy_init(ngx_event_t *rev); ++void ngx_http_spdy_request_headers_init(void); ++ ++ngx_int_t ngx_http_spdy_read_request_body(ngx_http_request_t *r, ++ ngx_http_client_body_handler_pt post_handler); ++ ++void ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc); ++ ++ngx_int_t ngx_http_spdy_send_output_queue(ngx_http_spdy_connection_t *sc); ++ ++ ++#define ngx_spdy_frame_aligned_write_uint16(p, s) \ ++ (*(uint16_t *) (p) = htons((uint16_t) (s)), (p) + sizeof(uint16_t)) ++ ++#define ngx_spdy_frame_aligned_write_uint32(p, s) \ ++ (*(uint32_t *) (p) = htonl((uint32_t) (s)), (p) + sizeof(uint32_t)) ++ ++#if (NGX_HAVE_NONALIGNED) ++ ++#define ngx_spdy_frame_write_uint16 ngx_spdy_frame_aligned_write_uint16 ++#define ngx_spdy_frame_write_uint32 ngx_spdy_frame_aligned_write_uint32 ++ ++#else ++ ++#define ngx_spdy_frame_write_uint16(p, s) \ ++ ((p)[0] = (u_char) ((s) >> 8), \ ++ (p)[1] = (u_char) (s), \ ++ (p) + sizeof(uint16_t)) ++ ++#define ngx_spdy_frame_write_uint32(p, s) \ ++ ((p)[0] = (u_char) ((s) >> 24), \ ++ (p)[1] = (u_char) ((s) >> 16), \ ++ (p)[2] = (u_char) ((s) >> 8), \ ++ (p)[3] = (u_char) (s), \ ++ (p) + sizeof(uint32_t)) ++ ++#endif ++ ++ ++#define ngx_spdy_ctl_frame_head(t) \ ++ ((uint32_t) NGX_SPDY_CTL_BIT << 31 | NGX_SPDY_VERSION << 16 | (t)) ++ ++#define ngx_spdy_frame_write_head(p, t) \ ++ ngx_spdy_frame_aligned_write_uint32(p, ngx_spdy_ctl_frame_head(t)) ++ ++#define ngx_spdy_frame_write_flags_and_len(p, f, l) \ ++ ngx_spdy_frame_aligned_write_uint32(p, (f) << 24 | (l)) ++#define ngx_spdy_frame_write_flags_and_id(p, f, i) \ ++ ngx_spdy_frame_aligned_write_uint32(p, (f) << 24 | (i)) ++ ++#define ngx_spdy_frame_write_sid ngx_spdy_frame_aligned_write_uint32 ++#define ngx_spdy_frame_write_window ngx_spdy_frame_aligned_write_uint32 ++ ++#endif /* _NGX_HTTP_SPDY_H_INCLUDED_ */ +diff --git a/src/http/ngx_http_spdy_filter_module.c b/src/http/ngx_http_spdy_filter_module.c +new file mode 100644 +index 0000000..377e935 +--- /dev/null ++++ b/src/http/ngx_http_spdy_filter_module.c +@@ -0,0 +1,1222 @@ ++ ++/* ++ * Copyright (C) Nginx, Inc. ++ * Copyright (C) Valentin V. Bartenev ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++ ++#define ngx_http_spdy_nv_nsize(h) (NGX_SPDY_NV_NLEN_SIZE + sizeof(h) - 1) ++#define ngx_http_spdy_nv_vsize(h) (NGX_SPDY_NV_VLEN_SIZE + sizeof(h) - 1) ++ ++#define ngx_http_spdy_nv_write_num ngx_spdy_frame_write_uint32 ++#define ngx_http_spdy_nv_write_nlen ngx_spdy_frame_write_uint32 ++#define ngx_http_spdy_nv_write_vlen ngx_spdy_frame_write_uint32 ++ ++#define ngx_http_spdy_nv_write_name(p, h) \ ++ ngx_cpymem(ngx_http_spdy_nv_write_nlen(p, sizeof(h) - 1), h, sizeof(h) - 1) ++ ++#define ngx_http_spdy_nv_write_val(p, h) \ ++ ngx_cpymem(ngx_http_spdy_nv_write_vlen(p, sizeof(h) - 1), h, sizeof(h) - 1) ++ ++ ++static ngx_chain_t *ngx_http_spdy_send_chain(ngx_connection_t *fc, ++ ngx_chain_t *in, off_t limit); ++ ++static ngx_inline ngx_int_t ngx_http_spdy_filter_send( ++ ngx_connection_t *fc, ngx_http_spdy_stream_t *stream); ++static ngx_inline ngx_int_t ngx_http_spdy_flow_control( ++ ngx_http_spdy_connection_t *sc, ngx_http_spdy_stream_t *stream); ++static void ngx_http_spdy_waiting_queue(ngx_http_spdy_connection_t *sc, ++ ngx_http_spdy_stream_t *stream); ++ ++static ngx_chain_t *ngx_http_spdy_filter_get_shadow( ++ ngx_http_spdy_stream_t *stream, ngx_buf_t *buf, off_t offset, off_t size); ++static ngx_http_spdy_out_frame_t *ngx_http_spdy_filter_get_data_frame( ++ ngx_http_spdy_stream_t *stream, size_t len, ngx_chain_t *first, ++ ngx_chain_t *last); ++ ++static ngx_int_t ngx_http_spdy_syn_frame_handler( ++ ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame); ++static ngx_int_t ngx_http_spdy_data_frame_handler( ++ ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame); ++static ngx_inline void ngx_http_spdy_handle_frame( ++ ngx_http_spdy_stream_t *stream, ngx_http_spdy_out_frame_t *frame); ++static ngx_inline void ngx_http_spdy_handle_stream( ++ ngx_http_spdy_connection_t *sc, ngx_http_spdy_stream_t *stream); ++ ++static void ngx_http_spdy_filter_cleanup(void *data); ++ ++static ngx_int_t ngx_http_spdy_filter_init(ngx_conf_t *cf); ++ ++ ++static ngx_http_module_t ngx_http_spdy_filter_module_ctx = { ++ NULL, /* preconfiguration */ ++ ngx_http_spdy_filter_init, /* postconfiguration */ ++ ++ NULL, /* create main configuration */ ++ NULL, /* init main configuration */ ++ ++ NULL, /* create server configuration */ ++ NULL, /* merge server configuration */ ++ ++ NULL, /* create location configuration */ ++ NULL /* merge location configuration */ ++}; ++ ++ ++ngx_module_t ngx_http_spdy_filter_module = { ++ NGX_MODULE_V1, ++ &ngx_http_spdy_filter_module_ctx, /* module context */ ++ NULL, /* module directives */ ++ NGX_HTTP_MODULE, /* module type */ ++ NULL, /* init master */ ++ NULL, /* init module */ ++ NULL, /* init process */ ++ NULL, /* init thread */ ++ NULL, /* exit thread */ ++ NULL, /* exit process */ ++ NULL, /* exit master */ ++ NGX_MODULE_V1_PADDING ++}; ++ ++ ++static ngx_http_output_header_filter_pt ngx_http_next_header_filter; ++ ++ ++static ngx_int_t ++ngx_http_spdy_header_filter(ngx_http_request_t *r) ++{ ++ int rc; ++ size_t len; ++ u_char *p, *buf, *last; ++ ngx_buf_t *b; ++ ngx_str_t host; ++ ngx_uint_t i, j, count, port; ++ ngx_chain_t *cl; ++ ngx_list_part_t *part, *pt; ++ ngx_table_elt_t *header, *h; ++ ngx_connection_t *c; ++ ngx_http_cleanup_t *cln; ++ ngx_http_core_loc_conf_t *clcf; ++ ngx_http_core_srv_conf_t *cscf; ++ ngx_http_spdy_stream_t *stream; ++ ngx_http_spdy_out_frame_t *frame; ++ ngx_http_spdy_connection_t *sc; ++ struct sockaddr_in *sin; ++#if (NGX_HAVE_INET6) ++ struct sockaddr_in6 *sin6; ++#endif ++ u_char addr[NGX_SOCKADDR_STRLEN]; ++ ++ if (!r->spdy_stream) { ++ return ngx_http_next_header_filter(r); ++ } ++ ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ++ "spdy header filter"); ++ ++ if (r->header_sent) { ++ return NGX_OK; ++ } ++ ++ r->header_sent = 1; ++ ++ if (r != r->main) { ++ return NGX_OK; ++ } ++ ++ c = r->connection; ++ ++ if (r->method == NGX_HTTP_HEAD) { ++ r->header_only = 1; ++ } ++ ++ switch (r->headers_out.status) { ++ ++ case NGX_HTTP_OK: ++ case NGX_HTTP_PARTIAL_CONTENT: ++ break; ++ ++ case NGX_HTTP_NOT_MODIFIED: ++ r->header_only = 1; ++ break; ++ ++ case NGX_HTTP_NO_CONTENT: ++ r->header_only = 1; ++ ++ ngx_str_null(&r->headers_out.content_type); ++ ++ r->headers_out.content_length = NULL; ++ r->headers_out.content_length_n = -1; ++ ++ /* fall through */ ++ ++ default: ++ r->headers_out.last_modified_time = -1; ++ r->headers_out.last_modified = NULL; ++ } ++ ++ len = NGX_SPDY_NV_NUM_SIZE ++ + ngx_http_spdy_nv_nsize(":version") ++ + ngx_http_spdy_nv_vsize("HTTP/1.1") ++ + ngx_http_spdy_nv_nsize(":status") ++ + (r->headers_out.status_line.len ++ ? NGX_SPDY_NV_VLEN_SIZE + r->headers_out.status_line.len ++ : ngx_http_spdy_nv_vsize("418")); ++ ++ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ++ ++ if (r->headers_out.server == NULL) { ++ len += ngx_http_spdy_nv_nsize("server"); ++ len += clcf->server_tokens ? ngx_http_spdy_nv_vsize(NGINX_VER) ++ : ngx_http_spdy_nv_vsize("nginx"); ++ } ++ ++ if (r->headers_out.date == NULL) { ++ len += ngx_http_spdy_nv_nsize("date") ++ + ngx_http_spdy_nv_vsize("Wed, 31 Dec 1986 10:00:00 GMT"); ++ } ++ ++ if (r->headers_out.content_type.len) { ++ len += ngx_http_spdy_nv_nsize("content-type") ++ + NGX_SPDY_NV_VLEN_SIZE + r->headers_out.content_type.len; ++ ++ if (r->headers_out.content_type_len == r->headers_out.content_type.len ++ && r->headers_out.charset.len) ++ { ++ len += sizeof("; charset=") - 1 + r->headers_out.charset.len; ++ } ++ } ++ ++ if (r->headers_out.content_length == NULL ++ && r->headers_out.content_length_n >= 0) ++ { ++ len += ngx_http_spdy_nv_nsize("content-length") ++ + NGX_SPDY_NV_VLEN_SIZE + NGX_OFF_T_LEN; ++ } ++ ++ if (r->headers_out.last_modified == NULL ++ && r->headers_out.last_modified_time != -1) ++ { ++ len += ngx_http_spdy_nv_nsize("last-modified") ++ + ngx_http_spdy_nv_vsize("Wed, 31 Dec 1986 10:00:00 GMT"); ++ } ++ ++ if (r->headers_out.location ++ && r->headers_out.location->value.len ++ && r->headers_out.location->value.data[0] == '/') ++ { ++ r->headers_out.location->hash = 0; ++ ++ if (clcf->server_name_in_redirect) { ++ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); ++ host = cscf->server_name; ++ ++ } else if (r->headers_in.server.len) { ++ host = r->headers_in.server; ++ ++ } else { ++ host.len = NGX_SOCKADDR_STRLEN; ++ host.data = addr; ++ ++ if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) { ++ return NGX_ERROR; ++ } ++ } ++ ++ switch (c->local_sockaddr->sa_family) { ++ ++#if (NGX_HAVE_INET6) ++ case AF_INET6: ++ sin6 = (struct sockaddr_in6 *) c->local_sockaddr; ++ port = ntohs(sin6->sin6_port); ++ break; ++#endif ++#if (NGX_HAVE_UNIX_DOMAIN) ++ case AF_UNIX: ++ port = 0; ++ break; ++#endif ++ default: /* AF_INET */ ++ sin = (struct sockaddr_in *) c->local_sockaddr; ++ port = ntohs(sin->sin_port); ++ break; ++ } ++ ++ len += ngx_http_spdy_nv_nsize("location") ++ + ngx_http_spdy_nv_vsize("https://") ++ + host.len ++ + r->headers_out.location->value.len; ++ ++ if (clcf->port_in_redirect) { ++ ++#if (NGX_HTTP_SSL) ++ if (c->ssl) ++ port = (port == 443) ? 0 : port; ++ else ++#endif ++ port = (port == 80) ? 0 : port; ++ ++ } else { ++ port = 0; ++ } ++ ++ if (port) { ++ len += sizeof(":65535") - 1; ++ } ++ ++ } else { ++ ngx_str_null(&host); ++ port = 0; ++ } ++ ++ part = &r->headers_out.headers.part; ++ header = part->elts; ++ ++ for (i = 0; /* void */; i++) { ++ ++ if (i >= part->nelts) { ++ if (part->next == NULL) { ++ break; ++ } ++ ++ part = part->next; ++ header = part->elts; ++ i = 0; ++ } ++ ++ if (header[i].hash == 0) { ++ continue; ++ } ++ ++ len += NGX_SPDY_NV_NLEN_SIZE + header[i].key.len ++ + NGX_SPDY_NV_VLEN_SIZE + header[i].value.len; ++ } ++ ++ buf = ngx_alloc(len, r->pool->log); ++ if (buf == NULL) { ++ return NGX_ERROR; ++ } ++ ++ last = buf + NGX_SPDY_NV_NUM_SIZE; ++ ++ last = ngx_http_spdy_nv_write_name(last, ":version"); ++ last = ngx_http_spdy_nv_write_val(last, "HTTP/1.1"); ++ ++ last = ngx_http_spdy_nv_write_name(last, ":status"); ++ ++ if (r->headers_out.status_line.len) { ++ last = ngx_http_spdy_nv_write_vlen(last, ++ r->headers_out.status_line.len); ++ last = ngx_cpymem(last, r->headers_out.status_line.data, ++ r->headers_out.status_line.len); ++ } else { ++ last = ngx_http_spdy_nv_write_vlen(last, 3); ++ last = ngx_sprintf(last, "%03ui", r->headers_out.status); ++ } ++ ++ count = 2; ++ ++ if (r->headers_out.server == NULL) { ++ last = ngx_http_spdy_nv_write_name(last, "server"); ++ last = clcf->server_tokens ++ ? ngx_http_spdy_nv_write_val(last, NGINX_VER) ++ : ngx_http_spdy_nv_write_val(last, "nginx"); ++ ++ count++; ++ } ++ ++ if (r->headers_out.date == NULL) { ++ last = ngx_http_spdy_nv_write_name(last, "date"); ++ ++ last = ngx_http_spdy_nv_write_vlen(last, ngx_cached_http_time.len); ++ ++ last = ngx_cpymem(last, ngx_cached_http_time.data, ++ ngx_cached_http_time.len); ++ ++ count++; ++ } ++ ++ if (r->headers_out.content_type.len) { ++ ++ last = ngx_http_spdy_nv_write_name(last, "content-type"); ++ ++ p = last + NGX_SPDY_NV_VLEN_SIZE; ++ ++ last = ngx_cpymem(p, r->headers_out.content_type.data, ++ r->headers_out.content_type.len); ++ ++ if (r->headers_out.content_type_len == r->headers_out.content_type.len ++ && r->headers_out.charset.len) ++ { ++ last = ngx_cpymem(last, "; charset=", sizeof("; charset=") - 1); ++ ++ last = ngx_cpymem(last, r->headers_out.charset.data, ++ r->headers_out.charset.len); ++ ++ /* update r->headers_out.content_type for possible logging */ ++ ++ r->headers_out.content_type.len = last - p; ++ r->headers_out.content_type.data = p; ++ } ++ ++ (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE, ++ r->headers_out.content_type.len); ++ ++ count++; ++ } ++ ++ if (r->headers_out.content_length == NULL ++ && r->headers_out.content_length_n >= 0) ++ { ++ last = ngx_http_spdy_nv_write_name(last, "content-length"); ++ ++ p = last + NGX_SPDY_NV_VLEN_SIZE; ++ ++ last = ngx_sprintf(p, "%O", r->headers_out.content_length_n); ++ ++ (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE, ++ last - p); ++ ++ count++; ++ } ++ ++ if (r->headers_out.last_modified == NULL ++ && r->headers_out.last_modified_time != -1) ++ { ++ last = ngx_http_spdy_nv_write_name(last, "last-modified"); ++ ++ p = last + NGX_SPDY_NV_VLEN_SIZE; ++ ++ last = ngx_http_time(p, r->headers_out.last_modified_time); ++ ++ (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE, ++ last - p); ++ ++ count++; ++ } ++ ++ if (host.data) { ++ ++ last = ngx_http_spdy_nv_write_name(last, "location"); ++ ++ p = last + NGX_SPDY_NV_VLEN_SIZE; ++ ++ last = ngx_cpymem(p, "http", sizeof("http") - 1); ++ ++#if (NGX_HTTP_SSL) ++ if (c->ssl) { ++ *last++ ='s'; ++ } ++#endif ++ ++ *last++ = ':'; *last++ = '/'; *last++ = '/'; ++ ++ last = ngx_cpymem(last, host.data, host.len); ++ ++ if (port) { ++ last = ngx_sprintf(last, ":%ui", port); ++ } ++ ++ last = ngx_cpymem(last, r->headers_out.location->value.data, ++ r->headers_out.location->value.len); ++ ++ /* update r->headers_out.location->value for possible logging */ ++ ++ r->headers_out.location->value.len = last - p; ++ r->headers_out.location->value.data = p; ++ ngx_str_set(&r->headers_out.location->key, "location"); ++ ++ (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE, ++ r->headers_out.location->value.len); ++ ++ count++; ++ } ++ ++ part = &r->headers_out.headers.part; ++ header = part->elts; ++ ++ for (i = 0; /* void */; i++) { ++ ++ if (i >= part->nelts) { ++ if (part->next == NULL) { ++ break; ++ } ++ ++ part = part->next; ++ header = part->elts; ++ i = 0; ++ } ++ ++ if (header[i].hash == 0 || header[i].hash == 2) { ++ continue; ++ } ++ ++ last = ngx_http_spdy_nv_write_nlen(last, header[i].key.len); ++ ++ ngx_strlow(last, header[i].key.data, header[i].key.len); ++ last += header[i].key.len; ++ ++ p = last + NGX_SPDY_NV_VLEN_SIZE; ++ ++ last = ngx_cpymem(p, header[i].value.data, header[i].value.len); ++ ++ pt = part; ++ h = header; ++ ++ for (j = i + 1; /* void */; j++) { ++ ++ if (j >= pt->nelts) { ++ if (pt->next == NULL) { ++ break; ++ } ++ ++ pt = pt->next; ++ h = pt->elts; ++ j = 0; ++ } ++ ++ if (h[j].hash == 0 || h[j].hash == 2 ++ || h[j].key.len != header[i].key.len ++ || ngx_strncasecmp(header[i].key.data, h[j].key.data, ++ header[i].key.len)) ++ { ++ continue; ++ } ++ ++ if (h[j].value.len) { ++ if (last != p) { ++ *last++ = '\0'; ++ } ++ ++ last = ngx_cpymem(last, h[j].value.data, h[j].value.len); ++ } ++ ++ h[j].hash = 2; ++ } ++ ++ (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE, ++ last - p); ++ ++ count++; ++ } ++ ++ (void) ngx_http_spdy_nv_write_num(buf, count); ++ ++ stream = r->spdy_stream; ++ sc = stream->connection; ++ ++ len = last - buf; ++ ++ b = ngx_create_temp_buf(r->pool, NGX_SPDY_FRAME_HEADER_SIZE ++ + NGX_SPDY_SYN_REPLY_SIZE ++ + deflateBound(&sc->zstream_out, len)); ++ if (b == NULL) { ++ ngx_free(buf); ++ return NGX_ERROR; ++ } ++ ++ b->last += NGX_SPDY_FRAME_HEADER_SIZE + NGX_SPDY_SYN_REPLY_SIZE; ++ ++ sc->zstream_out.next_in = buf; ++ sc->zstream_out.avail_in = len; ++ sc->zstream_out.next_out = b->last; ++ sc->zstream_out.avail_out = b->end - b->last; ++ ++ rc = deflate(&sc->zstream_out, Z_SYNC_FLUSH); ++ ++ ngx_free(buf); ++ ++ if (rc != Z_OK) { ++ ngx_log_error(NGX_LOG_ALERT, c->log, 0, "deflate() failed: %d", rc); ++ return NGX_ERROR; ++ } ++ ++ ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, ++ "spdy deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", ++ sc->zstream_out.next_in, sc->zstream_out.next_out, ++ sc->zstream_out.avail_in, sc->zstream_out.avail_out, ++ rc); ++ ++ b->last = sc->zstream_out.next_out; ++ ++ p = b->pos; ++ p = ngx_spdy_frame_write_head(p, NGX_SPDY_SYN_REPLY); ++ ++ len = b->last - b->pos; ++ ++ r->header_size = len; ++ ++ len -= NGX_SPDY_FRAME_HEADER_SIZE; ++ ++ if (r->header_only) { ++ b->last_buf = 1; ++ p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_FIN, len); ++ ++ } else { ++ p = ngx_spdy_frame_write_flags_and_len(p, 0, len); ++ } ++ ++ (void) ngx_spdy_frame_write_sid(p, stream->id); ++ ++ cl = ngx_alloc_chain_link(r->pool); ++ if (cl == NULL) { ++ return NGX_ERROR; ++ } ++ ++ cl->buf = b; ++ cl->next = NULL; ++ ++ frame = ngx_palloc(r->pool, sizeof(ngx_http_spdy_out_frame_t)); ++ if (frame == NULL) { ++ return NGX_ERROR; ++ } ++ ++ frame->first = cl; ++ frame->last = cl; ++ frame->handler = ngx_http_spdy_syn_frame_handler; ++ frame->stream = stream; ++ frame->length = len; ++ frame->priority = stream->priority; ++ frame->blocked = 1; ++ frame->fin = r->header_only; ++ ++ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0, ++ "spdy:%ui create SYN_REPLY frame %p: len:%uz", ++ stream->id, frame, frame->length); ++ ++ ngx_http_spdy_queue_blocked_frame(sc, frame); ++ ++ cln = ngx_http_cleanup_add(r, 0); ++ if (cln == NULL) { ++ return NGX_ERROR; ++ } ++ ++ cln->handler = ngx_http_spdy_filter_cleanup; ++ cln->data = stream; ++ ++ stream->queued = 1; ++ ++ c->send_chain = ngx_http_spdy_send_chain; ++ c->need_last_buf = 1; ++ ++ return ngx_http_spdy_filter_send(c, stream); ++} ++ ++ ++static ngx_chain_t * ++ngx_http_spdy_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit) ++{ ++ off_t size, offset; ++ size_t rest, frame_size; ++ ngx_chain_t *cl, *out, **ln; ++ ngx_http_request_t *r; ++ ngx_http_spdy_stream_t *stream; ++ ngx_http_spdy_loc_conf_t *slcf; ++ ngx_http_spdy_out_frame_t *frame; ++ ngx_http_spdy_connection_t *sc; ++ ++ r = fc->data; ++ stream = r->spdy_stream; ++ ++#if (NGX_SUPPRESS_WARN) ++ size = 0; ++#endif ++ ++ while (in) { ++ size = ngx_buf_size(in->buf); ++ ++ if (size || in->buf->last_buf) { ++ break; ++ } ++ ++ in = in->next; ++ } ++ ++ if (in == NULL) { ++ ++ if (stream->queued) { ++ fc->write->delayed = 1; ++ } else { ++ fc->buffered &= ~NGX_SPDY_BUFFERED; ++ } ++ ++ return NULL; ++ } ++ ++ sc = stream->connection; ++ ++ if (size && ngx_http_spdy_flow_control(sc, stream) == NGX_DECLINED) { ++ fc->write->delayed = 1; ++ return in; ++ } ++ ++ if (limit == 0 || limit > (off_t) sc->send_window) { ++ limit = sc->send_window; ++ } ++ ++ if (limit > stream->send_window) { ++ limit = (stream->send_window > 0) ? stream->send_window : 0; ++ } ++ ++ if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) { ++ cl = ngx_alloc_chain_link(r->pool); ++ if (cl == NULL) { ++ return NGX_CHAIN_ERROR; ++ } ++ ++ cl->buf = in->buf; ++ in->buf = cl->buf->shadow; ++ ++ offset = ngx_buf_in_memory(in->buf) ++ ? (cl->buf->pos - in->buf->pos) ++ : (cl->buf->file_pos - in->buf->file_pos); ++ ++ cl->next = stream->free_bufs; ++ stream->free_bufs = cl; ++ ++ } else { ++ offset = 0; ++ } ++ ++#if (NGX_SUPPRESS_WARN) ++ cl = NULL; ++#endif ++ ++ slcf = ngx_http_get_module_loc_conf(r, ngx_http_spdy_module); ++ ++ frame_size = (limit <= (off_t) slcf->chunk_size) ? (size_t) limit ++ : slcf->chunk_size; ++ ++ for ( ;; ) { ++ ln = &out; ++ rest = frame_size; ++ ++ while ((off_t) rest >= size) { ++ ++ if (offset) { ++ cl = ngx_http_spdy_filter_get_shadow(stream, in->buf, ++ offset, size); ++ if (cl == NULL) { ++ return NGX_CHAIN_ERROR; ++ } ++ ++ offset = 0; ++ ++ } else { ++ cl = ngx_alloc_chain_link(r->pool); ++ if (cl == NULL) { ++ return NGX_CHAIN_ERROR; ++ } ++ ++ cl->buf = in->buf; ++ } ++ ++ *ln = cl; ++ ln = &cl->next; ++ ++ rest -= (size_t) size; ++ in = in->next; ++ ++ if (in == NULL) { ++ frame_size -= rest; ++ rest = 0; ++ break; ++ } ++ ++ size = ngx_buf_size(in->buf); ++ } ++ ++ if (rest) { ++ cl = ngx_http_spdy_filter_get_shadow(stream, in->buf, ++ offset, rest); ++ if (cl == NULL) { ++ return NGX_CHAIN_ERROR; ++ } ++ ++ cl->buf->flush = 0; ++ cl->buf->last_buf = 0; ++ ++ *ln = cl; ++ ++ offset += rest; ++ size -= rest; ++ } ++ ++ frame = ngx_http_spdy_filter_get_data_frame(stream, frame_size, ++ out, cl); ++ if (frame == NULL) { ++ return NGX_CHAIN_ERROR; ++ } ++ ++ ngx_http_spdy_queue_frame(sc, frame); ++ ++ sc->send_window -= frame_size; ++ ++ stream->send_window -= frame_size; ++ stream->queued++; ++ ++ if (in == NULL) { ++ break; ++ } ++ ++ limit -= frame_size; ++ ++ if (limit == 0) { ++ break; ++ } ++ ++ if (limit < (off_t) slcf->chunk_size) { ++ frame_size = (size_t) limit; ++ } ++ } ++ ++ if (offset) { ++ cl = ngx_http_spdy_filter_get_shadow(stream, in->buf, offset, size); ++ if (cl == NULL) { ++ return NGX_CHAIN_ERROR; ++ } ++ ++ in->buf = cl->buf; ++ ngx_free_chain(r->pool, cl); ++ } ++ ++ if (ngx_http_spdy_filter_send(fc, stream) == NGX_ERROR) { ++ return NGX_CHAIN_ERROR; ++ } ++ ++ if (in && ngx_http_spdy_flow_control(sc, stream) == NGX_DECLINED) { ++ fc->write->delayed = 1; ++ } ++ ++ return in; ++} ++ ++ ++static ngx_chain_t * ++ngx_http_spdy_filter_get_shadow(ngx_http_spdy_stream_t *stream, ngx_buf_t *buf, ++ off_t offset, off_t size) ++{ ++ ngx_buf_t *chunk; ++ ngx_chain_t *cl; ++ ++ cl = ngx_chain_get_free_buf(stream->request->pool, &stream->free_bufs); ++ if (cl == NULL) { ++ return NULL; ++ } ++ ++ chunk = cl->buf; ++ ++ ngx_memcpy(chunk, buf, sizeof(ngx_buf_t)); ++ ++ chunk->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow; ++ chunk->shadow = buf; ++ ++ if (ngx_buf_in_memory(chunk)) { ++ chunk->pos += offset; ++ chunk->last = chunk->pos + size; ++ } ++ ++ if (chunk->in_file) { ++ chunk->file_pos += offset; ++ chunk->file_last = chunk->file_pos + size; ++ } ++ ++ return cl; ++} ++ ++ ++static ngx_http_spdy_out_frame_t * ++ngx_http_spdy_filter_get_data_frame(ngx_http_spdy_stream_t *stream, ++ size_t len, ngx_chain_t *first, ngx_chain_t *last) ++{ ++ u_char *p; ++ ngx_buf_t *buf; ++ ngx_uint_t flags; ++ ngx_chain_t *cl; ++ ngx_http_spdy_out_frame_t *frame; ++ ++ ++ frame = stream->free_frames; ++ ++ if (frame) { ++ stream->free_frames = frame->next; ++ ++ } else { ++ frame = ngx_palloc(stream->request->pool, ++ sizeof(ngx_http_spdy_out_frame_t)); ++ if (frame == NULL) { ++ return NULL; ++ } ++ } ++ ++ flags = last->buf->last_buf ? NGX_SPDY_FLAG_FIN : 0; ++ ++ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0, ++ "spdy:%ui create DATA frame %p: len:%uz flags:%ui", ++ stream->id, frame, 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 += NGX_SPDY_SID_SIZE; ++ ++ (void) ngx_spdy_frame_write_flags_and_len(p, flags, len); ++ ++ } else { ++ p = ngx_palloc(stream->request->pool, NGX_SPDY_FRAME_HEADER_SIZE); ++ if (p == NULL) { ++ return NULL; ++ } ++ ++ buf->pos = p; ++ buf->start = p; ++ ++ p = ngx_spdy_frame_write_sid(p, stream->id); ++ p = ngx_spdy_frame_write_flags_and_len(p, flags, len); ++ ++ buf->last = p; ++ buf->end = p; ++ ++ buf->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_get_data_frame; ++ buf->memory = 1; ++ } ++ ++ cl->next = first; ++ first = cl; ++ ++ last->buf->flush = 1; ++ ++ frame->first = first; ++ frame->last = last; ++ frame->handler = ngx_http_spdy_data_frame_handler; ++ frame->stream = stream; ++ frame->length = len; ++ frame->priority = stream->priority; ++ frame->blocked = 0; ++ frame->fin = last->buf->last_buf; ++ ++ return frame; ++} ++ ++ ++static ngx_inline ngx_int_t ++ngx_http_spdy_filter_send(ngx_connection_t *fc, ngx_http_spdy_stream_t *stream) ++{ ++ stream->blocked = 1; ++ ++ if (ngx_http_spdy_send_output_queue(stream->connection) == NGX_ERROR) { ++ fc->error = 1; ++ return NGX_ERROR; ++ } ++ ++ stream->blocked = 0; ++ ++ if (stream->queued) { ++ fc->buffered |= NGX_SPDY_BUFFERED; ++ fc->write->delayed = 1; ++ return NGX_AGAIN; ++ } ++ ++ fc->buffered &= ~NGX_SPDY_BUFFERED; ++ ++ return NGX_OK; ++} ++ ++ ++static ngx_inline ngx_int_t ++ngx_http_spdy_flow_control(ngx_http_spdy_connection_t *sc, ++ ngx_http_spdy_stream_t *stream) ++{ ++ if (stream->send_window <= 0) { ++ stream->exhausted = 1; ++ return NGX_DECLINED; ++ } ++ ++ if (sc->send_window == 0) { ++ ngx_http_spdy_waiting_queue(sc, stream); ++ return NGX_DECLINED; ++ } ++ ++ return NGX_OK; ++} ++ ++ ++static void ++ngx_http_spdy_waiting_queue(ngx_http_spdy_connection_t *sc, ++ ngx_http_spdy_stream_t *stream) ++{ ++ ngx_queue_t *q; ++ ngx_http_spdy_stream_t *s; ++ ++ if (stream->handled) { ++ return; ++ } ++ ++ stream->handled = 1; ++ ++ for (q = ngx_queue_last(&sc->waiting); ++ q != ngx_queue_sentinel(&sc->waiting); ++ q = ngx_queue_prev(q)) ++ { ++ s = ngx_queue_data(q, ngx_http_spdy_stream_t, queue); ++ ++ /* ++ * NB: higher values represent lower priorities. ++ */ ++ if (stream->priority >= s->priority) { ++ break; ++ } ++ } ++ ++ ngx_queue_insert_after(q, &stream->queue); ++} ++ ++ ++static ngx_int_t ++ngx_http_spdy_syn_frame_handler(ngx_http_spdy_connection_t *sc, ++ ngx_http_spdy_out_frame_t *frame) ++{ ++ ngx_buf_t *buf; ++ ngx_http_spdy_stream_t *stream; ++ ++ buf = frame->first->buf; ++ ++ if (buf->pos != buf->last) { ++ return NGX_AGAIN; ++ } ++ ++ stream = frame->stream; ++ ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy:%ui SYN_REPLY frame %p was sent", stream->id, frame); ++ ++ ngx_free_chain(stream->request->pool, frame->first); ++ ++ ngx_http_spdy_handle_frame(stream, frame); ++ ++ ngx_http_spdy_handle_stream(sc, stream); ++ ++ return NGX_OK; ++} ++ ++ ++static ngx_int_t ++ngx_http_spdy_data_frame_handler(ngx_http_spdy_connection_t *sc, ++ ngx_http_spdy_out_frame_t *frame) ++{ ++ ngx_buf_t *buf; ++ ngx_chain_t *cl, *ln; ++ ngx_http_spdy_stream_t *stream; ++ ++ stream = frame->stream; ++ ++ cl = frame->first; ++ ++ if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_data_frame) { ++ ++ if (cl->buf->pos != cl->buf->last) { ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy:%ui DATA frame %p was sent partially", ++ stream->id, frame); ++ ++ return NGX_AGAIN; ++ } ++ ++ ln = cl->next; ++ ++ cl->next = stream->free_data_headers; ++ stream->free_data_headers = cl; ++ ++ if (cl == frame->last) { ++ goto done; ++ } ++ ++ cl = ln; ++ } ++ ++ for ( ;; ) { ++ if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) { ++ buf = cl->buf->shadow; ++ ++ if (ngx_buf_in_memory(buf)) { ++ buf->pos = cl->buf->pos; ++ } ++ ++ if (buf->in_file) { ++ buf->file_pos = cl->buf->file_pos; ++ } ++ } ++ ++ if (ngx_buf_size(cl->buf) != 0) { ++ ++ if (cl != frame->first) { ++ frame->first = cl; ++ ngx_http_spdy_handle_stream(sc, stream); ++ } ++ ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy:%ui DATA frame %p was sent partially", ++ stream->id, frame); ++ ++ return NGX_AGAIN; ++ } ++ ++ ln = cl->next; ++ ++ if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) { ++ cl->next = stream->free_bufs; ++ stream->free_bufs = cl; ++ ++ } else { ++ ngx_free_chain(stream->request->pool, cl); ++ } ++ ++ if (cl == frame->last) { ++ goto done; ++ } ++ ++ cl = ln; ++ } ++ ++done: ++ ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ++ "spdy:%ui DATA frame %p was sent", stream->id, frame); ++ ++ stream->request->header_size += NGX_SPDY_FRAME_HEADER_SIZE; ++ ++ ngx_http_spdy_handle_frame(stream, frame); ++ ++ ngx_http_spdy_handle_stream(sc, stream); ++ ++ return NGX_OK; ++} ++ ++ ++static ngx_inline void ++ngx_http_spdy_handle_frame(ngx_http_spdy_stream_t *stream, ++ ngx_http_spdy_out_frame_t *frame) ++{ ++ ngx_http_request_t *r; ++ ++ r = stream->request; ++ ++ r->connection->sent += NGX_SPDY_FRAME_HEADER_SIZE + frame->length; ++ ++ if (frame->fin) { ++ stream->out_closed = 1; ++ } ++ ++ frame->next = stream->free_frames; ++ stream->free_frames = frame; ++ ++ stream->queued--; ++} ++ ++ ++static ngx_inline void ++ngx_http_spdy_handle_stream(ngx_http_spdy_connection_t *sc, ++ ngx_http_spdy_stream_t *stream) ++{ ++ ngx_event_t *wev; ++ ++ if (stream->handled || stream->blocked || stream->exhausted) { ++ return; ++ } ++ ++ wev = stream->request->connection->write; ++ ++ /* ++ * This timer can only be set if the stream was delayed because of rate ++ * limit. In that case the event should be triggered by the timer. ++ */ ++ ++ if (!wev->timer_set) { ++ wev->delayed = 0; ++ ++ stream->handled = 1; ++ ngx_queue_insert_tail(&sc->posted, &stream->queue); ++ } ++} ++ ++ ++static void ++ngx_http_spdy_filter_cleanup(void *data) ++{ ++ ngx_http_spdy_stream_t *stream = data; ++ ++ size_t delta; ++ ngx_http_spdy_out_frame_t *frame, **fn; ++ ngx_http_spdy_connection_t *sc; ++ ++ if (stream->handled) { ++ stream->handled = 0; ++ ngx_queue_remove(&stream->queue); ++ } ++ ++ if (stream->queued == 0) { ++ return; ++ } ++ ++ delta = 0; ++ sc = stream->connection; ++ fn = &sc->last_out; ++ ++ for ( ;; ) { ++ frame = *fn; ++ ++ if (frame == NULL) { ++ break; ++ } ++ ++ if (frame->stream == stream && !frame->blocked) { ++ *fn = frame->next; ++ ++ delta += frame->length; ++ ++ if (--stream->queued == 0) { ++ break; ++ } ++ ++ continue; ++ } ++ ++ fn = &frame->next; ++ } ++ ++ if (sc->send_window == 0 && delta && !ngx_queue_empty(&sc->waiting)) { ++ ngx_queue_add(&sc->posted, &sc->waiting); ++ ngx_queue_init(&sc->waiting); ++ } ++ ++ sc->send_window += delta; ++} ++ ++ ++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; ++ ++ return NGX_OK; ++} +diff --git a/src/http/ngx_http_spdy_module.c b/src/http/ngx_http_spdy_module.c +new file mode 100644 +index 0000000..5178a36 +--- /dev/null ++++ b/src/http/ngx_http_spdy_module.c +@@ -0,0 +1,408 @@ ++ ++/* ++ * Copyright (C) Nginx, Inc. ++ * Copyright (C) Valentin V. Bartenev ++ */ ++ ++ ++#include ++#include ++#include ++#include ++ ++ ++static ngx_int_t ngx_http_spdy_add_variables(ngx_conf_t *cf); ++ ++static ngx_int_t ngx_http_spdy_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data); ++static ngx_int_t ngx_http_spdy_request_priority_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data); ++ ++static ngx_int_t ngx_http_spdy_module_init(ngx_cycle_t *cycle); ++ ++static void *ngx_http_spdy_create_main_conf(ngx_conf_t *cf); ++static char *ngx_http_spdy_init_main_conf(ngx_conf_t *cf, void *conf); ++static void *ngx_http_spdy_create_srv_conf(ngx_conf_t *cf); ++static char *ngx_http_spdy_merge_srv_conf(ngx_conf_t *cf, void *parent, ++ void *child); ++static void *ngx_http_spdy_create_loc_conf(ngx_conf_t *cf); ++static char *ngx_http_spdy_merge_loc_conf(ngx_conf_t *cf, void *parent, ++ void *child); ++ ++static char *ngx_http_spdy_recv_buffer_size(ngx_conf_t *cf, void *post, ++ void *data); ++static char *ngx_http_spdy_pool_size(ngx_conf_t *cf, void *post, void *data); ++static char *ngx_http_spdy_streams_index_mask(ngx_conf_t *cf, void *post, ++ void *data); ++static char *ngx_http_spdy_chunk_size(ngx_conf_t *cf, void *post, void *data); ++ ++ ++static ngx_conf_num_bounds_t ngx_http_spdy_headers_comp_bounds = { ++ ngx_conf_check_num_bounds, 0, 9 ++}; ++ ++static ngx_conf_post_t ngx_http_spdy_recv_buffer_size_post = ++ { ngx_http_spdy_recv_buffer_size }; ++static ngx_conf_post_t ngx_http_spdy_pool_size_post = ++ { ngx_http_spdy_pool_size }; ++static ngx_conf_post_t ngx_http_spdy_streams_index_mask_post = ++ { ngx_http_spdy_streams_index_mask }; ++static ngx_conf_post_t ngx_http_spdy_chunk_size_post = ++ { ngx_http_spdy_chunk_size }; ++ ++ ++static ngx_command_t ngx_http_spdy_commands[] = { ++ ++ { ngx_string("spdy_recv_buffer_size"), ++ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_size_slot, ++ NGX_HTTP_MAIN_CONF_OFFSET, ++ offsetof(ngx_http_spdy_main_conf_t, recv_buffer_size), ++ &ngx_http_spdy_recv_buffer_size_post }, ++ ++ { ngx_string("spdy_pool_size"), ++ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_size_slot, ++ NGX_HTTP_SRV_CONF_OFFSET, ++ offsetof(ngx_http_spdy_srv_conf_t, pool_size), ++ &ngx_http_spdy_pool_size_post }, ++ ++ { ngx_string("spdy_max_concurrent_streams"), ++ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_num_slot, ++ NGX_HTTP_SRV_CONF_OFFSET, ++ offsetof(ngx_http_spdy_srv_conf_t, concurrent_streams), ++ NULL }, ++ ++ { ngx_string("spdy_streams_index_size"), ++ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_num_slot, ++ NGX_HTTP_SRV_CONF_OFFSET, ++ offsetof(ngx_http_spdy_srv_conf_t, streams_index_mask), ++ &ngx_http_spdy_streams_index_mask_post }, ++ ++ { ngx_string("spdy_recv_timeout"), ++ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_msec_slot, ++ NGX_HTTP_SRV_CONF_OFFSET, ++ offsetof(ngx_http_spdy_srv_conf_t, recv_timeout), ++ NULL }, ++ ++ { ngx_string("spdy_keepalive_timeout"), ++ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_msec_slot, ++ NGX_HTTP_SRV_CONF_OFFSET, ++ offsetof(ngx_http_spdy_srv_conf_t, keepalive_timeout), ++ NULL }, ++ ++ { ngx_string("spdy_headers_comp"), ++ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_num_slot, ++ NGX_HTTP_SRV_CONF_OFFSET, ++ offsetof(ngx_http_spdy_srv_conf_t, headers_comp), ++ &ngx_http_spdy_headers_comp_bounds }, ++ ++ { ngx_string("spdy_chunk_size"), ++ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_size_slot, ++ NGX_HTTP_LOC_CONF_OFFSET, ++ offsetof(ngx_http_spdy_loc_conf_t, chunk_size), ++ &ngx_http_spdy_chunk_size_post }, ++ ++ 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 */ ++ ++ ngx_http_spdy_create_loc_conf, /* create location configuration */ ++ ngx_http_spdy_merge_loc_conf /* merge location configuration */ ++}; ++ ++ ++ngx_module_t ngx_http_spdy_module = { ++ NGX_MODULE_V1, ++ &ngx_http_spdy_module_ctx, /* module context */ ++ ngx_http_spdy_commands, /* module directives */ ++ NGX_HTTP_MODULE, /* module type */ ++ NULL, /* init master */ ++ ngx_http_spdy_module_init, /* init module */ ++ NULL, /* init process */ ++ NULL, /* init thread */ ++ NULL, /* exit thread */ ++ NULL, /* exit process */ ++ NULL, /* exit master */ ++ NGX_MODULE_V1_PADDING ++}; ++ ++ ++static ngx_http_variable_t ngx_http_spdy_vars[] = { ++ ++ { ngx_string("spdy"), NULL, ++ ngx_http_spdy_variable, 0, 0, 0 }, ++ ++ { ngx_string("spdy_request_priority"), NULL, ++ ngx_http_spdy_request_priority_variable, 0, 0, 0 }, ++ ++ { ngx_null_string, NULL, NULL, 0, 0, 0 } ++}; ++ ++ ++static ngx_int_t ++ngx_http_spdy_add_variables(ngx_conf_t *cf) ++{ ++ ngx_http_variable_t *var, *v; ++ ++ for (v = ngx_http_spdy_vars; v->name.len; v++) { ++ var = ngx_http_add_variable(cf, &v->name, v->flags); ++ if (var == NULL) { ++ return NGX_ERROR; ++ } ++ ++ var->get_handler = v->get_handler; ++ var->data = v->data; ++ } ++ ++ return NGX_OK; ++} ++ ++ ++static ngx_int_t ++ngx_http_spdy_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data) ++{ ++ if (r->spdy_stream) { ++ v->len = sizeof("3.1") - 1; ++ v->valid = 1; ++ v->no_cacheable = 0; ++ v->not_found = 0; ++ v->data = (u_char *) "3.1"; ++ ++ return NGX_OK; ++ } ++ ++ *v = ngx_http_variable_null_value; ++ ++ return NGX_OK; ++} ++ ++ ++static ngx_int_t ++ngx_http_spdy_request_priority_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data) ++{ ++ if (r->spdy_stream) { ++ v->len = 1; ++ v->valid = 1; ++ v->no_cacheable = 0; ++ v->not_found = 0; ++ ++ v->data = ngx_pnalloc(r->pool, 1); ++ if (v->data == NULL) { ++ return NGX_ERROR; ++ } ++ ++ v->data[0] = '0' + (u_char) r->spdy_stream->priority; ++ ++ return NGX_OK; ++ } ++ ++ *v = ngx_http_variable_null_value; ++ ++ return NGX_OK; ++} ++ ++ ++static ngx_int_t ++ngx_http_spdy_module_init(ngx_cycle_t *cycle) ++{ ++ ngx_http_spdy_request_headers_init(); ++ ++ return NGX_OK; ++} ++ ++ ++static void * ++ngx_http_spdy_create_main_conf(ngx_conf_t *cf) ++{ ++ ngx_http_spdy_main_conf_t *smcf; ++ ++ smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_main_conf_t)); ++ if (smcf == NULL) { ++ return NULL; ++ } ++ ++ smcf->recv_buffer_size = NGX_CONF_UNSET_SIZE; ++ ++ return smcf; ++} ++ ++ ++static char * ++ngx_http_spdy_init_main_conf(ngx_conf_t *cf, void *conf) ++{ ++ ngx_http_spdy_main_conf_t *smcf = conf; ++ ++ ngx_conf_init_size_value(smcf->recv_buffer_size, 256 * 1024); ++ ++ return NGX_CONF_OK; ++} ++ ++ ++static void * ++ngx_http_spdy_create_srv_conf(ngx_conf_t *cf) ++{ ++ ngx_http_spdy_srv_conf_t *sscf; ++ ++ sscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_srv_conf_t)); ++ if (sscf == NULL) { ++ return NULL; ++ } ++ ++ sscf->pool_size = NGX_CONF_UNSET_SIZE; ++ ++ sscf->concurrent_streams = NGX_CONF_UNSET_UINT; ++ sscf->streams_index_mask = NGX_CONF_UNSET_UINT; ++ ++ sscf->recv_timeout = NGX_CONF_UNSET_MSEC; ++ sscf->keepalive_timeout = NGX_CONF_UNSET_MSEC; ++ ++ sscf->headers_comp = NGX_CONF_UNSET; ++ ++ return sscf; ++} ++ ++ ++static char * ++ngx_http_spdy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ++{ ++ ngx_http_spdy_srv_conf_t *prev = parent; ++ ngx_http_spdy_srv_conf_t *conf = child; ++ ++ ngx_conf_merge_size_value(conf->pool_size, prev->pool_size, 4096); ++ ++ ngx_conf_merge_uint_value(conf->concurrent_streams, ++ prev->concurrent_streams, 100); ++ ++ ngx_conf_merge_uint_value(conf->streams_index_mask, ++ prev->streams_index_mask, 32 - 1); ++ ++ ngx_conf_merge_msec_value(conf->recv_timeout, ++ prev->recv_timeout, 30000); ++ ngx_conf_merge_msec_value(conf->keepalive_timeout, ++ prev->keepalive_timeout, 180000); ++ ++ ngx_conf_merge_value(conf->headers_comp, prev->headers_comp, 0); ++ ++ return NGX_CONF_OK; ++} ++ ++ ++static void * ++ngx_http_spdy_create_loc_conf(ngx_conf_t *cf) ++{ ++ ngx_http_spdy_loc_conf_t *slcf; ++ ++ slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_loc_conf_t)); ++ if (slcf == NULL) { ++ return NULL; ++ } ++ ++ slcf->chunk_size = NGX_CONF_UNSET_SIZE; ++ ++ return slcf; ++} ++ ++ ++static char * ++ngx_http_spdy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ++{ ++ ngx_http_spdy_loc_conf_t *prev = parent; ++ ngx_http_spdy_loc_conf_t *conf = child; ++ ++ ngx_conf_merge_size_value(conf->chunk_size, prev->chunk_size, 8 * 1024); ++ ++ return NGX_CONF_OK; ++} ++ ++ ++static char * ++ngx_http_spdy_recv_buffer_size(ngx_conf_t *cf, void *post, void *data) ++{ ++ size_t *sp = data; ++ ++ if (*sp <= 2 * NGX_SPDY_STATE_BUFFER_SIZE) { ++ return "value is too small"; ++ } ++ ++ return NGX_CONF_OK; ++} ++ ++ ++static char * ++ngx_http_spdy_pool_size(ngx_conf_t *cf, void *post, void *data) ++{ ++ size_t *sp = data; ++ ++ if (*sp < NGX_MIN_POOL_SIZE) { ++ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ++ "the pool size must be no less than %uz", ++ NGX_MIN_POOL_SIZE); ++ return NGX_CONF_ERROR; ++ } ++ ++ if (*sp % NGX_POOL_ALIGNMENT) { ++ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ++ "the pool size must be a multiple of %uz", ++ NGX_POOL_ALIGNMENT); ++ return NGX_CONF_ERROR; ++ } ++ ++ return NGX_CONF_OK; ++} ++ ++ ++static char * ++ngx_http_spdy_streams_index_mask(ngx_conf_t *cf, void *post, void *data) ++{ ++ ngx_uint_t *np = data; ++ ++ ngx_uint_t mask; ++ ++ mask = *np - 1; ++ ++ if (*np == 0 || (*np & mask)) { ++ return "must be a power of two"; ++ } ++ ++ *np = mask; ++ ++ return NGX_CONF_OK; ++} ++ ++ ++static char * ++ngx_http_spdy_chunk_size(ngx_conf_t *cf, void *post, void *data) ++{ ++ size_t *sp = data; ++ ++ if (*sp == 0) { ++ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ++ "the spdy chunk size cannot be zero"); ++ return NGX_CONF_ERROR; ++ } ++ ++ if (*sp > NGX_SPDY_MAX_FRAME_SIZE) { ++ *sp = NGX_SPDY_MAX_FRAME_SIZE; ++ } ++ ++ return NGX_CONF_OK; ++} +diff --git a/src/http/ngx_http_spdy_module.h b/src/http/ngx_http_spdy_module.h +new file mode 100644 +index 0000000..5242322 +--- /dev/null ++++ b/src/http/ngx_http_spdy_module.h +@@ -0,0 +1,41 @@ ++ ++/* ++ * Copyright (C) Nginx, Inc. ++ * Copyright (C) Valentin V. Bartenev ++ */ ++ ++ ++#ifndef _NGX_HTTP_SPDY_MODULE_H_INCLUDED_ ++#define _NGX_HTTP_SPDY_MODULE_H_INCLUDED_ ++ ++ ++#include ++#include ++#include ++ ++ ++typedef struct { ++ size_t recv_buffer_size; ++ u_char *recv_buffer; ++} ngx_http_spdy_main_conf_t; ++ ++ ++typedef struct { ++ size_t pool_size; ++ ngx_uint_t concurrent_streams; ++ ngx_uint_t streams_index_mask; ++ ngx_msec_t recv_timeout; ++ ngx_msec_t keepalive_timeout; ++ ngx_int_t headers_comp; ++} ngx_http_spdy_srv_conf_t; ++ ++ ++typedef struct { ++ size_t chunk_size; ++} ngx_http_spdy_loc_conf_t; ++ ++ ++extern ngx_module_t ngx_http_spdy_module; ++ ++ ++#endif /* _NGX_HTTP_SPDY_MODULE_H_INCLUDED_ */ +diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c +index 74fffd5..aa75284 100644 +--- a/src/http/ngx_http_upstream.c ++++ b/src/http/ngx_http_upstream.c +@@ -481,6 +481,12 @@ ngx_http_upstream_init(ngx_http_request_t *r) + return; + } + #endif ++#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); +@@ -1156,6 +1162,11 @@ ngx_http_upstream_check_broken_connection(ngx_http_request_t *r, + return; + } + #endif ++#if (NGX_HTTP_SPDY) ++ if (r->spdy_stream) { ++ return; ++ } ++#endif + + #if (NGX_HAVE_KQUEUE) + +-- +2.7.4 (Apple Git-66) +