Last active
November 20, 2024 13:00
-
-
Save felixbuenemann/44d53b911ebfc2a4ff2b951e49923da8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 3a8fe43e9e073f26e44f62ad072079acd8b78de3 Mon Sep 17 00:00:00 2001 | |
From: =?UTF-8?q?Felix=20Bu=CC=88nemann?= <buenemann@louis.info> | |
Date: Mon, 30 May 2016 00:00:29 +0200 | |
Subject: [PATCH] Add SPDY support back to Nginx with HTTP/2 | |
Ported to 1.9.15 from 1.9.7 patch by Jiale Zhi from CloudFlare with | |
additional fixes to re-enable deprecated spdy directives and fix | |
compilation with http_v2 module enabled and spdy disabled. | |
--- | |
auto/modules | 31 + | |
auto/options | 3 + | |
src/core/ngx_connection.h | 1 + | |
src/http/modules/ngx_http_ssl_module.c | 55 +- | |
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 | 57 + | |
src/http/ngx_http_request.h | 3 + | |
src/http/ngx_http_request_body.c | 12 + | |
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 + | |
src/http/v2/ngx_http_v2_module.c | 69 - | |
18 files changed, 5858 insertions(+), 78 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 22ff6d9..42966dd 100644 | |
--- a/auto/modules | |
+++ b/auto/modules | |
@@ -139,6 +139,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 | |
@@ -171,6 +172,7 @@ ngx_module_order="ngx_http_static_module \ | |
ngx_http_header_filter_module \ | |
ngx_http_chunked_filter_module \ | |
ngx_http_v2_filter_module \ | |
+ ngx_http_spdy_filter_module \ | |
ngx_http_range_header_filter_module \ | |
ngx_http_gzip_filter_module \ | |
ngx_http_postpone_filter_module \ | |
@@ -232,6 +234,21 @@ if [ $HTTP_V2 = YES ]; then | |
. auto/module | |
fi | |
+if [ $HTTP_SPDY = YES ]; then | |
+ have=NGX_HTTP_SPDY . auto/have | |
+ USE_ZLIB=YES | |
+ | |
+ ngx_module_name=ngx_http_spdy_filter_module | |
+ ngx_module_incs= | |
+ ngx_module_deps= | |
+ ngx_module_srcs=src/http/ngx_http_spdy_filter_module.c | |
+ ngx_module_libs= | |
+ ngx_module_link=$HTTP_SPDY | |
+ | |
+ . auto/module | |
+fi | |
+ | |
+ | |
if :; then | |
ngx_module_name=ngx_http_range_header_filter_module | |
ngx_module_incs= | |
@@ -440,6 +457,20 @@ if [ $HTTP_V2 = YES ]; then | |
. auto/module | |
fi | |
+if [ $HTTP_SPDY = YES ]; then | |
+ have=NGX_HTTP_SPDY . auto/have | |
+ | |
+ ngx_module_name=ngx_http_spdy_module | |
+ ngx_module_incs=src/http | |
+ ngx_module_deps="src/http/ngx_http_spdy.h src/http/ngx_http_spdy_module.h" | |
+ ngx_module_srcs="src/http/ngx_http_spdy.c \ | |
+ src/http/ngx_http_spdy_module.c" | |
+ ngx_module_libs= | |
+ ngx_module_link=$HTTP_SPDY | |
+ | |
+ . auto/module | |
+fi | |
+ | |
if :; then | |
ngx_module_name=ngx_http_static_module | |
ngx_module_incs= | |
diff --git a/auto/options b/auto/options | |
index ac8beb1..a8d8c6e 100644 | |
--- a/auto/options | |
+++ b/auto/options | |
@@ -60,6 +60,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 | |
@@ -219,6 +220,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 ;; | |
@@ -398,6 +400,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/src/core/ngx_connection.h b/src/core/ngx_connection.h | |
index b0d162a..bee16de 100644 | |
--- a/src/core/ngx_connection.h | |
+++ b/src/core/ngx_connection.h | |
@@ -120,6 +120,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 6a4108c..0016991 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); | |
@@ -343,9 +343,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; | |
@@ -353,6 +364,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; | |
@@ -380,19 +398,32 @@ 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; | |
@@ -400,6 +431,20 @@ ngx_http_ssl_npn_advertised(ngx_ssl_conn_t *ssl_conn, | |
return SSL_TLSEXT_ERR_OK; | |
} | |
+#endif | |
+#if (NGX_HTTP_V2 && NGX_HTTP_SPDY) | |
+ 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 0ceb613..8a0e98f 100644 | |
--- a/src/http/ngx_http.c | |
+++ b/src/http/ngx_http.c | |
@@ -1229,6 +1229,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 | |
@@ -1286,6 +1289,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) { | |
@@ -1320,6 +1326,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; | |
} | |
@@ -1363,6 +1372,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; | |
@@ -1855,6 +1876,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 | |
@@ -1920,6 +1944,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 | |
addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; | |
if (addr[i].hash.buckets == 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 <ngx_http_v2.h> | |
#endif | |
+#if (NGX_HTTP_SPDY) | |
+#include <ngx_http_spdy.h> | |
+#endif | |
#if (NGX_HTTP_CACHE) | |
#include <ngx_http_cache.h> | |
#endif | |
diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c | |
index bd36aec..6351c9f 100644 | |
--- a/src/http/ngx_http_core_module.c | |
+++ b/src/http/ngx_http_core_module.c | |
@@ -2140,6 +2140,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; | |
@@ -2484,6 +2491,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; | |
@@ -4219,11 +4229,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 6c446a0..434d33e 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 7d6cada..90c14b9 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; | |
@@ -2513,6 +2546,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); | |
@@ -2732,6 +2771,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) | |
@@ -3401,6 +3452,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 cfde7dc..c7412be 100644 | |
--- a/src/http/ngx_http_request.h | |
+++ b/src/http/ngx_http_request.h | |
@@ -439,6 +439,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 0641329..a40507d 100644 | |
--- a/src/http/ngx_http_request_body.c | |
+++ b/src/http/ngx_http_request_body.c | |
@@ -52,6 +52,12 @@ ngx_http_read_client_request_body(ngx_http_request_t *r, | |
goto done; | |
} | |
#endif | |
+#if (NGX_HTTP_SPDY) | |
+ if (r->spdy_stream) { | |
+ rc = ngx_http_spdy_read_request_body(r, post_handler); | |
+ goto done; | |
+ } | |
+#endif | |
if (ngx_http_test_expect(r) != NGX_OK) { | |
rc = NGX_HTTP_INTERNAL_SERVER_ERROR; | |
@@ -524,6 +530,12 @@ ngx_http_discard_request_body(ngx_http_request_t *r) | |
return NGX_OK; | |
} | |
#endif | |
+#if (NGX_HTTP_SPDY) | |
+ if (r->spdy_stream) { | |
+ r->spdy_stream->skip_data = 1; | |
+ return NGX_OK; | |
+ } | |
+#endif | |
if (ngx_http_test_expect(r) != NGX_OK) { | |
return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
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 <ngx_config.h> | |
+#include <ngx_core.h> | |
+#include <ngx_http.h> | |
+#include <ngx_http_spdy_module.h> | |
+ | |
+#include <zlib.h> | |
+ | |
+ | |
+#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) | |
+ { |