Created
September 10, 2017 04:25
-
-
Save CarterLi/88d34b3f56ba75d688c4daeec6409416 to your computer and use it in GitHub Desktop.
Sslconfig patches for nginx 1.13.5 ( Apply them in order )
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 28e518f73665baa867e5a0627cffdad0ab4f09f0 Mon Sep 17 00:00:00 2001 | |
From: Carter Li <carter.li@eoitek.com> | |
Date: Wed, 6 Sep 2017 10:07:12 +0800 | |
Subject: [PATCH 1/3] Dynamic tls records | |
--- | |
src/event/ngx_event_openssl.c | 39 +++++++++++++++++ | |
src/event/ngx_event_openssl.h | 15 ++++++- | |
src/http/modules/ngx_http_ssl_module.c | 76 ++++++++++++++++++++++++++++++++++ | |
src/http/modules/ngx_http_ssl_module.h | 6 +++ | |
4 files changed, 135 insertions(+), 1 deletion(-) | |
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c | |
index 88a6dbe..b2b7a5f 100644 | |
--- a/src/event/ngx_event_openssl.c | |
+++ b/src/event/ngx_event_openssl.c | |
@@ -1174,6 +1174,7 @@ ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags) | |
sc->buffer = ((flags & NGX_SSL_BUFFER) != 0); | |
sc->buffer_size = ssl->buffer_size; | |
+ sc->dyn_rec = ssl->dyn_rec; | |
sc->session_ctx = ssl->ctx; | |
@@ -1712,6 +1713,41 @@ ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) | |
for ( ;; ) { | |
+ /* Dynamic record resizing: | |
+ We want the initial records to fit into one TCP segment | |
+ so we don't get TCP HoL blocking due to TCP Slow Start. | |
+ A connection always starts with small records, but after | |
+ a given amount of records sent, we make the records larger | |
+ to reduce header overhead. | |
+ After a connection has idled for a given timeout, begin | |
+ the process from the start. The actual parameters are | |
+ configurable. If dyn_rec_timeout is 0, we assume dyn_rec is off. */ | |
+ | |
+ if (c->ssl->dyn_rec.timeout > 0 ) { | |
+ | |
+ if (ngx_current_msec - c->ssl->dyn_rec_last_write > | |
+ c->ssl->dyn_rec.timeout) | |
+ { | |
+ buf->end = buf->start + c->ssl->dyn_rec.size_lo; | |
+ c->ssl->dyn_rec_records_sent = 0; | |
+ | |
+ } else { | |
+ if (c->ssl->dyn_rec_records_sent > | |
+ c->ssl->dyn_rec.threshold * 2) | |
+ { | |
+ buf->end = buf->start + c->ssl->buffer_size; | |
+ | |
+ } else if (c->ssl->dyn_rec_records_sent > | |
+ c->ssl->dyn_rec.threshold) | |
+ { | |
+ buf->end = buf->start + c->ssl->dyn_rec.size_hi; | |
+ | |
+ } else { | |
+ buf->end = buf->start + c->ssl->dyn_rec.size_lo; | |
+ } | |
+ } | |
+ } | |
+ | |
while (in && buf->last < buf->end && send < limit) { | |
if (in->buf->last_buf || in->buf->flush) { | |
flush = 1; | |
@@ -1813,6 +1849,9 @@ ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size) | |
if (n > 0) { | |
+ c->ssl->dyn_rec_records_sent++; | |
+ c->ssl->dyn_rec_last_write = ngx_current_msec; | |
+ | |
if (c->ssl->saved_read_handler) { | |
c->read->handler = c->ssl->saved_read_handler; | |
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h | |
index b9a3a96..93e45ad 100644 | |
--- a/src/event/ngx_event_openssl.h | |
+++ b/src/event/ngx_event_openssl.h | |
@@ -59,10 +59,19 @@ | |
#endif | |
+typedef struct { | |
+ ngx_msec_t timeout; | |
+ ngx_uint_t threshold; | |
+ size_t size_lo; | |
+ size_t size_hi; | |
+} ngx_ssl_dyn_rec_t; | |
+ | |
+ | |
struct ngx_ssl_s { | |
SSL_CTX *ctx; | |
ngx_log_t *log; | |
size_t buffer_size; | |
+ ngx_ssl_dyn_rec_t dyn_rec; | |
}; | |
@@ -85,6 +94,10 @@ struct ngx_ssl_connection_s { | |
unsigned no_wait_shutdown:1; | |
unsigned no_send_shutdown:1; | |
unsigned handshake_buffer_set:1; | |
+ | |
+ ngx_ssl_dyn_rec_t dyn_rec; | |
+ ngx_msec_t dyn_rec_last_write; | |
+ ngx_uint_t dyn_rec_records_sent; | |
}; | |
@@ -94,7 +107,7 @@ struct ngx_ssl_connection_s { | |
#define NGX_SSL_DFLT_BUILTIN_SCACHE -5 | |
-#define NGX_SSL_MAX_SESSION_SIZE 4096 | |
+#define NGX_SSL_MAX_SESSION_SIZE 16384 | |
typedef struct ngx_ssl_sess_id_s ngx_ssl_sess_id_t; | |
diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c | |
index 7d62176..1e016eb 100644 | |
--- a/src/http/modules/ngx_http_ssl_module.c | |
+++ b/src/http/modules/ngx_http_ssl_module.c | |
@@ -234,6 +234,41 @@ static ngx_command_t ngx_http_ssl_commands[] = { | |
offsetof(ngx_http_ssl_srv_conf_t, stapling_verify), | |
NULL }, | |
+ { ngx_string("ssl_dyn_rec_enable"), | |
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, | |
+ ngx_conf_set_flag_slot, | |
+ NGX_HTTP_SRV_CONF_OFFSET, | |
+ offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_enable), | |
+ NULL }, | |
+ | |
+ { ngx_string("ssl_dyn_rec_timeout"), | |
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, | |
+ ngx_conf_set_msec_slot, | |
+ NGX_HTTP_SRV_CONF_OFFSET, | |
+ offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_timeout), | |
+ NULL }, | |
+ | |
+ { ngx_string("ssl_dyn_rec_size_lo"), | |
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, | |
+ ngx_conf_set_size_slot, | |
+ NGX_HTTP_SRV_CONF_OFFSET, | |
+ offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_size_lo), | |
+ NULL }, | |
+ | |
+ { ngx_string("ssl_dyn_rec_size_hi"), | |
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, | |
+ ngx_conf_set_size_slot, | |
+ NGX_HTTP_SRV_CONF_OFFSET, | |
+ offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_size_hi), | |
+ NULL }, | |
+ | |
+ { ngx_string("ssl_dyn_rec_threshold"), | |
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, | |
+ ngx_conf_set_num_slot, | |
+ NGX_HTTP_SRV_CONF_OFFSET, | |
+ offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_threshold), | |
+ NULL }, | |
+ | |
ngx_null_command | |
}; | |
@@ -559,6 +594,11 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf) | |
sscf->session_ticket_keys = NGX_CONF_UNSET_PTR; | |
sscf->stapling = NGX_CONF_UNSET; | |
sscf->stapling_verify = NGX_CONF_UNSET; | |
+ sscf->dyn_rec_enable = NGX_CONF_UNSET; | |
+ sscf->dyn_rec_timeout = NGX_CONF_UNSET_MSEC; | |
+ sscf->dyn_rec_size_lo = NGX_CONF_UNSET_SIZE; | |
+ sscf->dyn_rec_size_hi = NGX_CONF_UNSET_SIZE; | |
+ sscf->dyn_rec_threshold = NGX_CONF_UNSET_UINT; | |
return sscf; | |
} | |
@@ -624,6 +664,20 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) | |
ngx_conf_merge_str_value(conf->stapling_responder, | |
prev->stapling_responder, ""); | |
+ ngx_conf_merge_value(conf->dyn_rec_enable, prev->dyn_rec_enable, 0); | |
+ ngx_conf_merge_msec_value(conf->dyn_rec_timeout, prev->dyn_rec_timeout, | |
+ 1000); | |
+ /* Default sizes for the dynamic record sizes are defined to fit maximal | |
+ TLS + IPv6 overhead in a single TCP segment for lo and 3 segments for hi: | |
+ 1369 = 1500 - 40 (IP) - 20 (TCP) - 10 (Time) - 61 (Max TLS overhead) */ | |
+ ngx_conf_merge_size_value(conf->dyn_rec_size_lo, prev->dyn_rec_size_lo, | |
+ 1369); | |
+ /* 4229 = (1500 - 40 - 20 - 10) * 3 - 61 */ | |
+ ngx_conf_merge_size_value(conf->dyn_rec_size_hi, prev->dyn_rec_size_hi, | |
+ 4229); | |
+ ngx_conf_merge_uint_value(conf->dyn_rec_threshold, prev->dyn_rec_threshold, | |
+ 40); | |
+ | |
conf->ssl.log = cf->log; | |
if (conf->enable) { | |
@@ -804,6 +858,28 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) | |
} | |
+ if (conf->dyn_rec_enable) { | |
+ conf->ssl.dyn_rec.timeout = conf->dyn_rec_timeout; | |
+ conf->ssl.dyn_rec.threshold = conf->dyn_rec_threshold; | |
+ | |
+ if (conf->buffer_size > conf->dyn_rec_size_lo) { | |
+ conf->ssl.dyn_rec.size_lo = conf->dyn_rec_size_lo; | |
+ | |
+ } else { | |
+ conf->ssl.dyn_rec.size_lo = conf->buffer_size; | |
+ } | |
+ | |
+ if (conf->buffer_size > conf->dyn_rec_size_hi) { | |
+ conf->ssl.dyn_rec.size_hi = conf->dyn_rec_size_hi; | |
+ | |
+ } else { | |
+ conf->ssl.dyn_rec.size_hi = conf->buffer_size; | |
+ } | |
+ | |
+ } else { | |
+ conf->ssl.dyn_rec.timeout = 0; | |
+ } | |
+ | |
return NGX_CONF_OK; | |
} | |
diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h | |
index 57f5941..309ac00 100644 | |
--- a/src/http/modules/ngx_http_ssl_module.h | |
+++ b/src/http/modules/ngx_http_ssl_module.h | |
@@ -57,6 +57,12 @@ typedef struct { | |
u_char *file; | |
ngx_uint_t line; | |
+ | |
+ ngx_flag_t dyn_rec_enable; | |
+ ngx_msec_t dyn_rec_timeout; | |
+ size_t dyn_rec_size_lo; | |
+ size_t dyn_rec_size_hi; | |
+ ngx_uint_t dyn_rec_threshold; | |
} ngx_http_ssl_srv_conf_t; | |
-- | |
1.8.3.1 |
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 ccbb1acb3f99a2ac155734bc2be5b9ca2d1b641c Mon Sep 17 00:00:00 2001 | |
From: Carter Li <carter.li@eoitek.com> | |
Date: Wed, 6 Sep 2017 10:13:20 +0800 | |
Subject: [PATCH 2/3] Http2 hpack | |
--- | |
auto/modules | 4 + | |
auto/options | 3 + | |
src/core/ngx_murmurhash.c | 60 +++++ | |
src/core/ngx_murmurhash.h | 2 + | |
src/http/v2/ngx_http_v2.c | 13 + | |
src/http/v2/ngx_http_v2.h | 81 ++++++ | |
src/http/v2/ngx_http_v2_filter_module.c | 154 +++--------- | |
src/http/v2/ngx_http_v2_table.c | 431 ++++++++++++++++++++++++++++++++ | |
8 files changed, 632 insertions(+), 116 deletions(-) | |
diff --git a/auto/modules b/auto/modules | |
index 26b05bd..4c5d5b1 100644 | |
--- a/auto/modules | |
+++ b/auto/modules | |
@@ -436,6 +436,10 @@ if [ $HTTP = YES ]; then | |
. auto/module | |
fi | |
+ if [ $HTTP_V2_HPACK_ENC = YES ]; then | |
+ have=NGX_HTTP_V2_HPACK_ENC . auto/have | |
+ fi | |
+ | |
if :; then | |
ngx_module_name=ngx_http_static_module | |
ngx_module_incs= | |
diff --git a/auto/options b/auto/options | |
index 24c2618..edcf9ef 100644 | |
--- a/auto/options | |
+++ b/auto/options | |
@@ -59,6 +59,7 @@ HTTP_CHARSET=YES | |
HTTP_GZIP=YES | |
HTTP_SSL=NO | |
HTTP_V2=NO | |
+HTTP_V2_HPACK_ENC=NO | |
HTTP_SSI=YES | |
HTTP_POSTPONE=NO | |
HTTP_REALIP=NO | |
@@ -222,6 +223,7 @@ $0: warning: the \"--with-ipv6\" option is deprecated" | |
--with-http_ssl_module) HTTP_SSL=YES ;; | |
--with-http_v2_module) HTTP_V2=YES ;; | |
+ --with-http_v2_hpack_enc) HTTP_V2_HPACK_ENC=YES ;; | |
--with-http_realip_module) HTTP_REALIP=YES ;; | |
--with-http_addition_module) HTTP_ADDITION=YES ;; | |
--with-http_xslt_module) HTTP_XSLT=YES ;; | |
@@ -432,6 +434,7 @@ cat << END | |
--with-http_ssl_module enable ngx_http_ssl_module | |
--with-http_v2_module enable ngx_http_v2_module | |
+ --with-http_v2_hpack_enc enable ngx_http_v2_hpack_enc | |
--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_murmurhash.c b/src/core/ngx_murmurhash.c | |
index 5ade658..4932f20 100644 | |
--- a/src/core/ngx_murmurhash.c | |
+++ b/src/core/ngx_murmurhash.c | |
@@ -50,3 +50,63 @@ ngx_murmur_hash2(u_char *data, size_t len) | |
return h; | |
} | |
+ | |
+ | |
+uint64_t | |
+ngx_murmur_hash2_64(u_char *data, size_t len, uint64_t seed) | |
+{ | |
+ uint64_t h, k; | |
+ | |
+ h = seed ^ len; | |
+ | |
+ while (len >= 8) { | |
+ k = data[0]; | |
+ k |= data[1] << 8; | |
+ k |= data[2] << 16; | |
+ k |= data[3] << 24; | |
+ k |= (uint64_t)data[4] << 32; | |
+ k |= (uint64_t)data[5] << 40; | |
+ k |= (uint64_t)data[6] << 48; | |
+ k |= (uint64_t)data[7] << 56; | |
+ | |
+ k *= 0xc6a4a7935bd1e995ull; | |
+ k ^= k >> 47; | |
+ k *= 0xc6a4a7935bd1e995ull; | |
+ | |
+ h ^= k; | |
+ h *= 0xc6a4a7935bd1e995ull; | |
+ | |
+ data += 8; | |
+ len -= 8; | |
+ } | |
+ | |
+ switch (len) { | |
+ case 7: | |
+ h ^= (uint64_t)data[6] << 48; | |
+ /* fall through */ | |
+ case 6: | |
+ h ^= (uint64_t)data[5] << 40; | |
+ /* fall through */ | |
+ case 5: | |
+ h ^= (uint64_t)data[4] << 32; | |
+ /* fall through */ | |
+ case 4: | |
+ h ^= data[3] << 24; | |
+ /* fall through */ | |
+ case 3: | |
+ h ^= data[2] << 16; | |
+ /* fall through */ | |
+ case 2: | |
+ h ^= data[1] << 8; | |
+ /* fall through */ | |
+ case 1: | |
+ h ^= data[0]; | |
+ h *= 0xc6a4a7935bd1e995ull; | |
+ } | |
+ | |
+ h ^= h >> 47; | |
+ h *= 0xc6a4a7935bd1e995ull; | |
+ h ^= h >> 47; | |
+ | |
+ return h; | |
+} | |
diff --git a/src/core/ngx_murmurhash.h b/src/core/ngx_murmurhash.h | |
index 54e867d..322b3df 100644 | |
--- a/src/core/ngx_murmurhash.h | |
+++ b/src/core/ngx_murmurhash.h | |
@@ -15,5 +15,7 @@ | |
uint32_t ngx_murmur_hash2(u_char *data, size_t len); | |
+uint64_t ngx_murmur_hash2_64(u_char *data, size_t len, uint64_t seed); | |
+ | |
#endif /* _NGX_MURMURHASH_H_INCLUDED_ */ | |
diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c | |
index 7725616..db5e46b 100644 | |
--- a/src/http/v2/ngx_http_v2.c | |
+++ b/src/http/v2/ngx_http_v2.c | |
@@ -245,6 +245,8 @@ ngx_http_v2_init(ngx_event_t *rev) | |
h2c->frame_size = NGX_HTTP_V2_DEFAULT_FRAME_SIZE; | |
+ h2c->max_hpack_table_size = NGX_HTTP_V2_DEFAULT_HPACK_TABLE_SIZE; | |
+ | |
h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module); | |
h2c->pool = ngx_create_pool(h2scf->pool_size, h2c->connection->log); | |
@@ -2018,6 +2020,17 @@ ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos, | |
h2c->frame_size = value; | |
break; | |
+ case NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING: | |
+ | |
+ if (value > NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE) { | |
+ h2c->max_hpack_table_size = NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE; | |
+ } else { | |
+ h2c->max_hpack_table_size = value; | |
+ } | |
+ | |
+ h2c->indicate_resize = 1; | |
+ break; | |
+ | |
default: | |
break; | |
} | |
diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h | |
index 4804658..774abe8 100644 | |
--- a/src/http/v2/ngx_http_v2.h | |
+++ b/src/http/v2/ngx_http_v2.h | |
@@ -49,6 +49,13 @@ | |
#define NGX_HTTP_V2_MAX_WINDOW ((1U << 31) - 1) | |
#define NGX_HTTP_V2_DEFAULT_WINDOW 65535 | |
+#define HPACK_ENC_HTABLE_SZ 128 /* better to keep a PoT < 64k */ | |
+#define HPACK_ENC_HTABLE_ENTRIES ((HPACK_ENC_HTABLE_SZ * 100) / 128) | |
+#define HPACK_ENC_DYNAMIC_KEY_TBL_SZ 10 /* 10 is sufficient for most */ | |
+#define HPACK_ENC_MAX_ENTRY 512 /* longest header size to match */ | |
+ | |
+#define NGX_HTTP_V2_DEFAULT_HPACK_TABLE_SIZE 4096 | |
+#define NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE 16384 /* < 64k */ | |
typedef struct ngx_http_v2_connection_s ngx_http_v2_connection_t; | |
typedef struct ngx_http_v2_node_s ngx_http_v2_node_t; | |
@@ -110,6 +117,46 @@ typedef struct { | |
} ngx_http_v2_hpack_t; | |
+#if (NGX_HTTP_V2_HPACK_ENC) | |
+typedef struct { | |
+ uint64_t hash_val; | |
+ uint32_t index; | |
+ uint16_t pos; | |
+ uint16_t klen, vlen; | |
+ uint16_t size; | |
+ uint16_t next; | |
+} ngx_http_v2_hpack_enc_entry_t; | |
+ | |
+ | |
+typedef struct { | |
+ uint64_t hash_val; | |
+ uint32_t index; | |
+ uint16_t pos; | |
+ uint16_t klen; | |
+} ngx_http_v2_hpack_name_entry_t; | |
+ | |
+ | |
+typedef struct { | |
+ size_t size; /* size as defined in RFC 7541 */ | |
+ uint32_t top; /* the last entry */ | |
+ uint32_t pos; | |
+ uint16_t n_elems; /* number of elements */ | |
+ uint16_t base; /* index of the oldest entry */ | |
+ uint16_t last; /* index of the newest entry */ | |
+ | |
+ /* hash table for dynamic entries, instead using a generic hash table, | |
+ which would be too slow to process a significant amount of headers, | |
+ this table is not determenistic, and might ocasionally fail to insert | |
+ a value, at the cost of slightly worse compression, but significantly | |
+ faster performance */ | |
+ ngx_http_v2_hpack_enc_entry_t htable[HPACK_ENC_HTABLE_SZ]; | |
+ ngx_http_v2_hpack_name_entry_t heads[HPACK_ENC_DYNAMIC_KEY_TBL_SZ]; | |
+ u_char storage[NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE + | |
+ HPACK_ENC_MAX_ENTRY]; | |
+} ngx_http_v2_hpack_enc_t; | |
+#endif | |
+ | |
+ | |
struct ngx_http_v2_connection_s { | |
ngx_connection_t *connection; | |
ngx_http_connection_t *http_connection; | |
@@ -122,6 +169,8 @@ struct ngx_http_v2_connection_s { | |
size_t frame_size; | |
+ size_t max_hpack_table_size; | |
+ | |
ngx_queue_t waiting; | |
ngx_http_v2_state_t state; | |
@@ -146,6 +195,11 @@ struct ngx_http_v2_connection_s { | |
unsigned settings_ack:1; | |
unsigned blocked:1; | |
unsigned goaway:1; | |
+ unsigned indicate_resize:1; | |
+ | |
+#if (NGX_HTTP_V2_HPACK_ENC) | |
+ ngx_http_v2_hpack_enc_t hpack_enc; | |
+#endif | |
}; | |
@@ -347,4 +401,31 @@ size_t ngx_http_v2_huff_encode(u_char *src, size_t len, u_char *dst, | |
#define ngx_http_v2_write_sid ngx_http_v2_write_uint32 | |
+u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, | |
+ u_char *tmp, ngx_uint_t lower); | |
+ | |
+u_char * | |
+ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value); | |
+ | |
+#define ngx_http_v2_write_name(dst, src, len, tmp) \ | |
+ ngx_http_v2_string_encode(dst, src, len, tmp, 1) | |
+#define ngx_http_v2_write_value(dst, src, len, tmp) \ | |
+ ngx_http_v2_string_encode(dst, src, len, tmp, 0) | |
+ | |
+u_char * | |
+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos, | |
+ u_char *key, size_t key_len, u_char *value, size_t value_len, | |
+ u_char *tmp); | |
+ | |
+void | |
+ngx_http_v2_table_resize(ngx_http_v2_connection_t *h2c); | |
+ | |
+#define ngx_http_v2_write_header_str(key, value) \ | |
+ ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \ | |
+ (u_char *) value, sizeof(value) - 1, tmp); | |
+ | |
+#define ngx_http_v2_write_header_tbl(key, val) \ | |
+ ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \ | |
+ val.data, val.len, tmp); | |
+ | |
#endif /* _NGX_HTTP_V2_H_INCLUDED_ */ | |
diff --git a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c | |
index 8621e7a..6ce17dd 100644 | |
--- a/src/http/v2/ngx_http_v2_filter_module.c | |
+++ b/src/http/v2/ngx_http_v2_filter_module.c | |
@@ -25,11 +25,6 @@ | |
#define ngx_http_v2_indexed(i) (128 + (i)) | |
#define ngx_http_v2_inc_indexed(i) (64 + (i)) | |
-#define ngx_http_v2_write_name(dst, src, len, tmp) \ | |
- ngx_http_v2_string_encode(dst, src, len, tmp, 1) | |
-#define ngx_http_v2_write_value(dst, src, len, tmp) \ | |
- ngx_http_v2_string_encode(dst, src, len, tmp, 0) | |
- | |
#define NGX_HTTP_V2_ENCODE_RAW 0 | |
#define NGX_HTTP_V2_ENCODE_HUFF 0x80 | |
@@ -53,10 +48,6 @@ | |
#define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1 | |
-static u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, | |
- u_char *tmp, ngx_uint_t lower); | |
-static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, | |
- ngx_uint_t value); | |
static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame( | |
ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin); | |
static ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame( | |
@@ -142,6 +133,7 @@ ngx_http_v2_header_filter(ngx_http_request_t *r) | |
ngx_http_core_loc_conf_t *clcf; | |
ngx_http_core_srv_conf_t *cscf; | |
u_char addr[NGX_SOCKADDR_STRLEN]; | |
+ ngx_http_v2_connection_t *h2c; | |
static const u_char nginx[5] = "\x84\xaa\x63\x55\xe7"; | |
#if (NGX_HTTP_GZIP) | |
@@ -150,11 +142,9 @@ ngx_http_v2_header_filter(ngx_http_request_t *r) | |
#endif | |
static size_t nginx_ver_len = ngx_http_v2_literal_size(NGINX_VER); | |
- static u_char nginx_ver[ngx_http_v2_literal_size(NGINX_VER)]; | |
static size_t nginx_ver_build_len = | |
ngx_http_v2_literal_size(NGINX_VER_BUILD); | |
- static u_char nginx_ver_build[ngx_http_v2_literal_size(NGINX_VER_BUILD)]; | |
if (!r->stream) { | |
return ngx_http_next_header_filter(r); | |
@@ -415,7 +405,7 @@ ngx_http_v2_header_filter(ngx_http_request_t *r) | |
} | |
tmp = ngx_palloc(r->pool, tmp_len); | |
- pos = ngx_pnalloc(r->pool, len); | |
+ pos = ngx_pnalloc(r->pool, len + 15 + 1); | |
if (pos == NULL || tmp == NULL) { | |
return NGX_ERROR; | |
@@ -423,6 +413,18 @@ ngx_http_v2_header_filter(ngx_http_request_t *r) | |
start = pos; | |
+ h2c = r->stream->connection; | |
+ | |
+ if (h2c->indicate_resize) { | |
+ *pos = 32; | |
+ pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(5), | |
+ h2c->max_hpack_table_size); | |
+ h2c->indicate_resize = 0; | |
+#if (NGX_HTTP_V2_HPACK_ENC) | |
+ ngx_http_v2_table_resize(h2c); | |
+#endif | |
+ } | |
+ | |
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, | |
"http2 output header: \":status: %03ui\"", | |
r->headers_out.status); | |
@@ -431,67 +433,28 @@ ngx_http_v2_header_filter(ngx_http_request_t *r) | |
*pos++ = status; | |
} else { | |
- *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX); | |
- *pos++ = NGX_HTTP_V2_ENCODE_RAW | 3; | |
- pos = ngx_sprintf(pos, "%03ui", r->headers_out.status); | |
+ ngx_sprintf(pos + 8, "%O3ui", r->headers_out.status); | |
+ pos = ngx_http_v2_write_header(h2c, pos, (u_char *)":status", | |
+ sizeof(":status") - 1, pos + 8, 3, tmp); | |
} | |
if (r->headers_out.server == NULL) { | |
- | |
- if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { | |
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, | |
- "http2 output header: \"server: %s\"", | |
- NGINX_VER); | |
- | |
- } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { | |
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, | |
- "http2 output header: \"server: %s\"", | |
- NGINX_VER_BUILD); | |
- | |
- } else { | |
- ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, | |
- "http2 output header: \"server: nginx\""); | |
- } | |
- | |
- *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX); | |
- | |
if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { | |
- if (nginx_ver[0] == '\0') { | |
- p = ngx_http_v2_write_value(nginx_ver, (u_char *) NGINX_VER, | |
- sizeof(NGINX_VER) - 1, tmp); | |
- nginx_ver_len = p - nginx_ver; | |
- } | |
- | |
- pos = ngx_cpymem(pos, nginx_ver, nginx_ver_len); | |
+ pos = ngx_http_v2_write_header_str("server", NGINX_VER); | |
} else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { | |
- if (nginx_ver_build[0] == '\0') { | |
- p = ngx_http_v2_write_value(nginx_ver_build, | |
- (u_char *) NGINX_VER_BUILD, | |
- sizeof(NGINX_VER_BUILD) - 1, tmp); | |
- nginx_ver_build_len = p - nginx_ver_build; | |
- } | |
- | |
- pos = ngx_cpymem(pos, nginx_ver_build, nginx_ver_build_len); | |
+ pos = ngx_http_v2_write_header_str("server", NGINX_VER_BUILD); | |
} else { | |
- pos = ngx_cpymem(pos, nginx, sizeof(nginx)); | |
+ pos = ngx_http_v2_write_header_str("server", "nginx"); | |
} | |
} | |
if (r->headers_out.date == NULL) { | |
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, | |
- "http2 output header: \"date: %V\"", | |
- &ngx_cached_http_time); | |
- | |
- *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX); | |
- pos = ngx_http_v2_write_value(pos, ngx_cached_http_time.data, | |
- ngx_cached_http_time.len, tmp); | |
+ pos = ngx_http_v2_write_header_tbl("date", ngx_cached_http_time); | |
} | |
if (r->headers_out.content_type.len) { | |
- *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_TYPE_INDEX); | |
- | |
if (r->headers_out.content_type_len == r->headers_out.content_type.len | |
&& r->headers_out.charset.len) | |
{ | |
@@ -517,64 +480,36 @@ ngx_http_v2_header_filter(ngx_http_request_t *r) | |
r->headers_out.content_type.data = p - len; | |
} | |
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, | |
- "http2 output header: \"content-type: %V\"", | |
- &r->headers_out.content_type); | |
- | |
- pos = ngx_http_v2_write_value(pos, r->headers_out.content_type.data, | |
- r->headers_out.content_type.len, tmp); | |
+ pos = ngx_http_v2_write_header_tbl("content-type", | |
+ r->headers_out.content_type); | |
} | |
if (r->headers_out.content_length == NULL | |
&& r->headers_out.content_length_n >= 0) | |
{ | |
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, | |
- "http2 output header: \"content-length: %O\"", | |
- r->headers_out.content_length_n); | |
- | |
- *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_LENGTH_INDEX); | |
- | |
- p = pos; | |
- pos = ngx_sprintf(pos + 1, "%O", r->headers_out.content_length_n); | |
- *p = NGX_HTTP_V2_ENCODE_RAW | (u_char) (pos - p - 1); | |
+ p = ngx_sprintf(pos + 15, "%O", r->headers_out.content_length_n); | |
+ pos = ngx_http_v2_write_header(h2c, pos, (u_char *)"content-length", | |
+ sizeof("content-length") - 1, pos + 15, | |
+ p - (pos + 15), tmp); | |
} | |
if (r->headers_out.last_modified == NULL | |
&& r->headers_out.last_modified_time != -1) | |
{ | |
- *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX); | |
- | |
- ngx_http_time(pos, r->headers_out.last_modified_time); | |
+ ngx_http_time(pos + 14, r->headers_out.last_modified_time); | |
len = sizeof("Wed, 31 Dec 1986 18:00:00 GMT") - 1; | |
- | |
- ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0, | |
- "http2 output header: \"last-modified: %*s\"", | |
- len, pos); | |
- | |
- /* | |
- * Date will always be encoded using huffman in the temporary buffer, | |
- * so it's safe here to use src and dst pointing to the same address. | |
- */ | |
- pos = ngx_http_v2_write_value(pos, pos, len, tmp); | |
+ pos = ngx_http_v2_write_header(h2c, pos, (u_char *)"last-modified", | |
+ sizeof("last-modified") - 1, pos + 14, | |
+ len, tmp); | |
} | |
if (r->headers_out.location && r->headers_out.location->value.len) { | |
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, | |
- "http2 output header: \"location: %V\"", | |
- &r->headers_out.location->value); | |
- | |
- *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX); | |
- pos = ngx_http_v2_write_value(pos, r->headers_out.location->value.data, | |
- r->headers_out.location->value.len, tmp); | |
+ pos = ngx_http_v2_write_header_tbl("location", r->headers_out.location->value); | |
} | |
#if (NGX_HTTP_GZIP) | |
if (r->gzip_vary) { | |
- ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, | |
- "http2 output header: \"vary: Accept-Encoding\""); | |
- | |
- *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX); | |
- pos = ngx_cpymem(pos, accept_encoding, sizeof(accept_encoding)); | |
+ pos = ngx_http_v2_write_header_str("vary", "Accept-Encoding"); | |
} | |
#endif | |
@@ -597,23 +532,10 @@ ngx_http_v2_header_filter(ngx_http_request_t *r) | |
continue; | |
} | |
-#if (NGX_DEBUG) | |
- if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) { | |
- ngx_strlow(tmp, header[i].key.data, header[i].key.len); | |
+ pos = ngx_http_v2_write_header(h2c, pos, header[i].key.data, | |
+ header[i].key.len, header[i].value.data, | |
+ header[i].value.len, tmp); | |
- ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0, | |
- "http2 output header: \"%*s: %V\"", | |
- header[i].key.len, tmp, &header[i].value); | |
- } | |
-#endif | |
- | |
- *pos++ = 0; | |
- | |
- pos = ngx_http_v2_write_name(pos, header[i].key.data, | |
- header[i].key.len, tmp); | |
- | |
- pos = ngx_http_v2_write_value(pos, header[i].value.data, | |
- header[i].value.len, tmp); | |
} | |
frame = ngx_http_v2_create_headers_frame(r, start, pos, r->header_only); | |
@@ -752,7 +674,7 @@ ngx_http_v2_create_trailers_frame(ngx_http_request_t *r) | |
} | |
-static u_char * | |
+u_char * | |
ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp, | |
ngx_uint_t lower) | |
{ | |
@@ -778,7 +700,7 @@ ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp, | |
} | |
-static u_char * | |
+u_char * | |
ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value) | |
{ | |
if (value < prefix) { | |
diff --git a/src/http/v2/ngx_http_v2_table.c b/src/http/v2/ngx_http_v2_table.c | |
index a73748a..ed64d40 100644 | |
--- a/src/http/v2/ngx_http_v2_table.c | |
+++ b/src/http/v2/ngx_http_v2_table.c | |
@@ -347,3 +347,434 @@ ngx_http_v2_table_size(ngx_http_v2_connection_t *h2c, size_t size) | |
return NGX_OK; | |
} | |
+ | |
+ | |
+#if (NGX_HTTP_V2_HPACK_ENC) | |
+ | |
+static ngx_int_t | |
+hpack_get_static_index(ngx_http_v2_connection_t *h2c, u_char *val, size_t len); | |
+ | |
+static ngx_int_t | |
+hpack_get_dynamic_index(ngx_http_v2_connection_t *h2c, uint64_t key_hash, | |
+ uint8_t *key, size_t key_len); | |
+ | |
+ | |
+void | |
+ngx_http_v2_table_resize(ngx_http_v2_connection_t *h2c) | |
+{ | |
+ ngx_http_v2_hpack_enc_entry_t *table; | |
+ uint64_t idx; | |
+ | |
+ table = h2c->hpack_enc.htable; | |
+ | |
+ while (h2c->hpack_enc.size > h2c->max_hpack_table_size) { | |
+ idx = h2c->hpack_enc.base; | |
+ h2c->hpack_enc.base = table[idx].next; | |
+ h2c->hpack_enc.size -= table[idx].size; | |
+ table[idx].hash_val = 0; | |
+ h2c->hpack_enc.n_elems--; | |
+ } | |
+} | |
+ | |
+ | |
+/* checks if a header is in the hpack table - if so returns the table entry, | |
+ otherwise encodes and inserts into the table and returns 0, | |
+ if failed to insert into table, returns -1 */ | |
+static ngx_int_t | |
+ngx_http_v2_table_encode_strings(ngx_http_v2_connection_t *h2c, | |
+ size_t key_len, size_t val_len, uint8_t *key, uint8_t *val, | |
+ ngx_int_t *header_idx) | |
+{ | |
+ uint64_t hash_val, key_hash, idx, lru; | |
+ int i; | |
+ size_t size = key_len + val_len + 32; | |
+ uint8_t *storage = h2c->hpack_enc.storage; | |
+ | |
+ ngx_http_v2_hpack_enc_entry_t *table; | |
+ ngx_http_v2_hpack_name_entry_t *name; | |
+ | |
+ *header_idx = NGX_ERROR; | |
+ /* step 1: compute the hash value of header */ | |
+ if (size > HPACK_ENC_MAX_ENTRY || size > h2c->max_hpack_table_size) { | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ key_hash = ngx_murmur_hash2_64(key, key_len, 0x01234); | |
+ hash_val = ngx_murmur_hash2_64(val, val_len, key_hash); | |
+ | |
+ if (hash_val == 0) { | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ /* step 2: check if full header in the table */ | |
+ idx = hash_val; | |
+ i = -1; | |
+ while (idx) { | |
+ /* at most 8 locations are checked, but most will be done in 1 or 2 */ | |
+ table = &h2c->hpack_enc.htable[idx % HPACK_ENC_HTABLE_SZ]; | |
+ if (table->hash_val == hash_val | |
+ && table->klen == key_len | |
+ && table->vlen == val_len | |
+ && ngx_memcmp(key, storage + table->pos, key_len) == 0 | |
+ && ngx_memcmp(val, storage + table->pos + key_len, val_len) == 0) | |
+ { | |
+ return (h2c->hpack_enc.top - table->index) + 61; | |
+ } | |
+ | |
+ if (table->hash_val == 0 && i == -1) { | |
+ i = idx % HPACK_ENC_HTABLE_SZ; | |
+ break; | |
+ } | |
+ | |
+ idx >>= 8; | |
+ } | |
+ | |
+ /* step 3: check if key is in one of the tables */ | |
+ *header_idx = hpack_get_static_index(h2c, key, key_len); | |
+ | |
+ if (i == -1) { | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ if (*header_idx == NGX_ERROR) { | |
+ *header_idx = hpack_get_dynamic_index(h2c, key_hash, key, key_len); | |
+ } | |
+ | |
+ /* step 4: store the new entry */ | |
+ table = h2c->hpack_enc.htable; | |
+ | |
+ if (h2c->hpack_enc.top == 0xffffffff) { | |
+ /* just to be on the safe side, avoid overflow */ | |
+ ngx_memset(&h2c->hpack_enc, 0, sizeof(ngx_http_v2_hpack_enc_t)); | |
+ } | |
+ | |
+ while ((h2c->hpack_enc.size + size > h2c->max_hpack_table_size) | |
+ || h2c->hpack_enc.n_elems == HPACK_ENC_HTABLE_ENTRIES) { | |
+ /* make space for the new entry first */ | |
+ idx = h2c->hpack_enc.base; | |
+ h2c->hpack_enc.base = table[idx].next; | |
+ h2c->hpack_enc.size -= table[idx].size; | |
+ table[idx].hash_val = 0; | |
+ h2c->hpack_enc.n_elems--; | |
+ } | |
+ | |
+ table[i] = (ngx_http_v2_hpack_enc_entry_t){.hash_val = hash_val, | |
+ .index = h2c->hpack_enc.top, | |
+ .pos = h2c->hpack_enc.pos, | |
+ .klen = key_len, | |
+ .vlen = val_len, | |
+ .size = size, | |
+ .next = 0}; | |
+ | |
+ table[h2c->hpack_enc.last].next = i; | |
+ if (h2c->hpack_enc.n_elems == 0) { | |
+ h2c->hpack_enc.base = i; | |
+ } | |
+ | |
+ h2c->hpack_enc.last = i; | |
+ h2c->hpack_enc.top++; | |
+ h2c->hpack_enc.size += size; | |
+ h2c->hpack_enc.n_elems++; | |
+ | |
+ /* update header name lookup */ | |
+ if (*header_idx == NGX_ERROR ) { | |
+ lru = h2c->hpack_enc.top; | |
+ | |
+ for (i=0; i<HPACK_ENC_DYNAMIC_KEY_TBL_SZ; i++) { | |
+ | |
+ name = &h2c->hpack_enc.heads[i]; | |
+ | |
+ if ( name->hash_val == 0 || (name->hash_val == key_hash | |
+ && ngx_memcmp(storage + name->pos, key, key_len) == 0) ) | |
+ { | |
+ name->hash_val = key_hash; | |
+ name->pos = h2c->hpack_enc.pos; | |
+ name->index = h2c->hpack_enc.top - 1; | |
+ break; | |
+ } | |
+ | |
+ if (lru > name->index) { | |
+ lru = name->index; | |
+ idx = i; | |
+ } | |
+ } | |
+ | |
+ if (i == HPACK_ENC_DYNAMIC_KEY_TBL_SZ) { | |
+ name = &h2c->hpack_enc.heads[idx]; | |
+ name->hash_val = hash_val; | |
+ name->pos = h2c->hpack_enc.pos; | |
+ name->index = h2c->hpack_enc.top - 1; | |
+ } | |
+ } | |
+ | |
+ ngx_memcpy(storage + h2c->hpack_enc.pos, key, key_len); | |
+ ngx_memcpy(storage + h2c->hpack_enc.pos + key_len, val, val_len); | |
+ | |
+ h2c->hpack_enc.pos += size; | |
+ if (h2c->hpack_enc.pos > NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE) { | |
+ h2c->hpack_enc.pos = 0; | |
+ } | |
+ | |
+ return NGX_OK; | |
+} | |
+ | |
+ | |
+u_char * | |
+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos, | |
+ u_char *key, size_t key_len, | |
+ u_char *value, size_t value_len, | |
+ u_char *tmp) | |
+{ | |
+ ngx_int_t idx, header_idx; | |
+ | |
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, | |
+ "http2 output header: %*s: %*s", key_len, key, value_len, | |
+ value); | |
+ | |
+ /* attempt to find the value in the dynamic table */ | |
+ idx = ngx_http_v2_table_encode_strings(h2c, key_len, value_len, key, value, | |
+ &header_idx); | |
+ | |
+ if (idx > 0) { | |
+ /* positive index indicates success */ | |
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, | |
+ "http2 hpack encode: Indexed Header Field: %ud", idx); | |
+ | |
+ *pos = 128; | |
+ pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), idx); | |
+ | |
+ } else { | |
+ | |
+ if (header_idx == NGX_ERROR) { /* if key is not present */ | |
+ | |
+ if (idx == NGX_ERROR) { /* if header was not added */ | |
+ *pos++ = 0; | |
+ | |
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, | |
+ "http2 hpack encode: Literal Header Field without" | |
+ " Indexing — New Name"); | |
+ } else { /* if header was added */ | |
+ *pos++ = 64; | |
+ | |
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, | |
+ "http2 hpack encode: Literal Header Field with " | |
+ "Incremental Indexing — New Name"); | |
+ } | |
+ | |
+ pos = ngx_http_v2_write_name(pos, key, key_len, tmp); | |
+ | |
+ } else { /* if key is present */ | |
+ | |
+ if (idx == NGX_ERROR) { | |
+ *pos = 0; | |
+ pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(4), header_idx); | |
+ | |
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, | |
+ "http2 hpack encode: Literal Header Field without" | |
+ " Indexing — Indexed Name: %ud", header_idx); | |
+ } else { | |
+ *pos = 64; | |
+ pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(6), header_idx); | |
+ | |
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, | |
+ "http2 hpack encode: Literal Header Field with " | |
+ "Incremental Indexing — Indexed Name: %ud", header_idx); | |
+ } | |
+ } | |
+ | |
+ pos = ngx_http_v2_write_value(pos, value, value_len, tmp); | |
+ } | |
+ | |
+ return pos; | |
+} | |
+ | |
+ | |
+static ngx_int_t | |
+hpack_get_dynamic_index(ngx_http_v2_connection_t *h2c, uint64_t key_hash, | |
+ uint8_t *key, size_t key_len) | |
+{ | |
+ ngx_http_v2_hpack_name_entry_t *name; | |
+ int i; | |
+ | |
+ for (i=0; i<HPACK_ENC_DYNAMIC_KEY_TBL_SZ; i++) { | |
+ name = &h2c->hpack_enc.heads[i]; | |
+ | |
+ if (name->hash_val == key_hash | |
+ && ngx_memcmp(h2c->hpack_enc.storage + name->pos, key, key_len) == 0) | |
+ { | |
+ if (name->index >= h2c->hpack_enc.top - h2c->hpack_enc.n_elems) { | |
+ return (h2c->hpack_enc.top - name->index) + 61; | |
+ } | |
+ break; | |
+ } | |
+ } | |
+ | |
+ return NGX_ERROR; | |
+} | |
+ | |
+ | |
+/* decide if a given header is present in the static dictionary, this could be | |
+ done in several ways, but it seems the fastest one is "exhaustive" search */ | |
+static ngx_int_t | |
+hpack_get_static_index(ngx_http_v2_connection_t *h2c, u_char *val, size_t len) | |
+{ | |
+ /* the static dictionary of response only headers, | |
+ although response headers can be put by origin, | |
+ that would be rare */ | |
+ static const struct { | |
+ u_char len; | |
+ const u_char val[28]; | |
+ u_char idx; | |
+ } server_headers[] = { | |
+ { 3, "age", 21},//0 | |
+ { 3, "via", 60}, | |
+ { 4, "date", 33},//2 | |
+ { 4, "etag", 34}, | |
+ { 4, "link", 45}, | |
+ { 4, "vary", 59}, | |
+ { 5, "allow", 22},//6 | |
+ { 6, "server", 54},//7 | |
+ { 7, "expires", 36},//8 | |
+ { 7, "refresh", 52}, | |
+ { 8, "location", 46},//10 | |
+ {10, "set-cookie", 55},//11 | |
+ {11, "retry-after", 53},//12 | |
+ {12, "content-type", 31},//13 | |
+ {13, "content-range", 30},//14 | |
+ {13, "accept-ranges", 18}, | |
+ {13, "cache-control", 24}, | |
+ {13, "last-modified", 44}, | |
+ {14, "content-length", 28},//18 | |
+ {16, "content-encoding", 26},//19 | |
+ {16, "content-language", 27}, | |
+ {16, "content-location", 29}, | |
+ {16, "www-authenticate", 61}, | |
+ {17, "transfer-encoding", 57},//23 | |
+ {18, "proxy-authenticate", 48},//24 | |
+ {19, "content-disposition", 25},//25 | |
+ {25, "strict-transport-security", 56},//26 | |
+ {27, "access-control-allow-origin", 20},//27 | |
+ {99, "", 99}, | |
+ }, *header; | |
+ | |
+ /* for a given length, where to start the search | |
+ since minimal length is 3, the table has a -3 | |
+ offset */ | |
+ static const int8_t start_at[] = { | |
+ [3-3] = 0, | |
+ [4-3] = 2, | |
+ [5-3] = 6, | |
+ [6-3] = 7, | |
+ [7-3] = 8, | |
+ [8-3] = 10, | |
+ [9-3] = -1, | |
+ [10-3] = 11, | |
+ [11-3] = 12, | |
+ [12-3] = 13, | |
+ [13-3] = 14, | |
+ [14-3] = 18, | |
+ [15-3] = -1, | |
+ [16-3] = 19, | |
+ [17-3] = 23, | |
+ [18-3] = 24, | |
+ [19-3] = 25, | |
+ [20-3] = -1, | |
+ [21-3] = -1, | |
+ [22-3] = -1, | |
+ [23-3] = -1, | |
+ [24-3] = -1, | |
+ [25-3] = 26, | |
+ [26-3] = -1, | |
+ [27-3] = 27, | |
+ }; | |
+ | |
+ uint64_t pref; | |
+ size_t save_len = len, i; | |
+ int8_t start; | |
+ | |
+ /* early exit for out of bounds lengths */ | |
+ if (len < 3 || len > 27) { | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ start = start_at[len - 3]; | |
+ if (start == -1) { | |
+ /* exit for non existent lengths */ | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ header = &server_headers[start_at[len - 3]]; | |
+ | |
+ /* load first 8 bytes of key, for fast comparison */ | |
+ if (len < 8) { | |
+ pref = 0; | |
+ if (len >= 4) { | |
+ pref = *(uint32_t *)(val + len - 4) | 0x20202020; | |
+ len -= 4; | |
+ } | |
+ while (len > 0) { /* 3 iterations at most */ | |
+ pref = (pref << 8) ^ (val[len - 1] | 0x20); | |
+ len--; | |
+ } | |
+ } else { | |
+ pref = *(uint64_t *)val | 0x2020202020202020; | |
+ len -= 8; | |
+ } | |
+ | |
+ /* iterate over headers with the right length */ | |
+ while (header->len == save_len) { | |
+ /* quickly compare the first 8 bytes, most tests will end here */ | |
+ if (pref != *(uint64_t *) header->val) { | |
+ header++; | |
+ continue; | |
+ } | |
+ | |
+ if (len == 0) { | |
+ /* len == 0, indicates prefix held the entire key */ | |
+ return header->idx; | |
+ } | |
+ /* for longer keys compare the rest */ | |
+ i = 1 + (save_len + 7) % 8; /* align so we can compare in quadwords */ | |
+ | |
+ while (i + 8 <= save_len) { /* 3 iterations at most */ | |
+ if ( *(uint64_t *)&header->val[i] | |
+ != (*(uint64_t *) &val[i]| 0x2020202020202020) ) | |
+ { | |
+ header++; | |
+ i = 0; | |
+ break; | |
+ } | |
+ i += 8; | |
+ } | |
+ | |
+ if (i == 0) { | |
+ continue; | |
+ } | |
+ | |
+ /* found the corresponding entry in the static dictionary */ | |
+ return header->idx; | |
+ } | |
+ | |
+ return NGX_ERROR; | |
+} | |
+ | |
+#else | |
+ | |
+u_char * | |
+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos, | |
+ u_char *key, size_t key_len, | |
+ u_char *value, size_t value_len, | |
+ u_char *tmp) | |
+{ | |
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, | |
+ "http2 output header: %*s: %*s", key_len, key, value_len, | |
+ value); | |
+ | |
+ *pos++ = 64; | |
+ pos = ngx_http_v2_write_name(pos, key, key_len, tmp); | |
+ pos = ngx_http_v2_write_value(pos, value, value_len, tmp); | |
+ | |
+ return pos; | |
+} | |
+ | |
+#endif | |
-- | |
1.8.3.1 |
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 0adefe1744911ec9b670893fc78bc79663f4d5c0 Mon Sep 17 00:00:00 2001 | |
From: Carter Li <carter.li@eoitek.com> | |
Date: Wed, 6 Sep 2017 10:23:53 +0800 | |
Subject: [PATCH 3/3] Http2 spdy | |
--- | |
auto/modules | 28 + | |
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 | 7 + | |
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, 5856 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 4c5d5b1..5d00d45 100644 | |
--- a/auto/modules | |
+++ b/auto/modules | |
@@ -134,6 +134,7 @@ if [ $HTTP = YES ]; then | |
# 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 | |
@@ -166,6 +167,7 @@ if [ $HTTP = YES ]; then | |
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 \ | |
@@ -227,6 +229,19 @@ if [ $HTTP = 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 +455,19 @@ if [ $HTTP = YES ]; then | |
have=NGX_HTTP_V2_HPACK_ENC . auto/have | |
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 edcf9ef..5aed35b 100644 | |
--- a/auto/options | |
+++ b/auto/options | |
@@ -60,6 +60,7 @@ HTTP_GZIP=YES | |
HTTP_SSL=NO | |
HTTP_V2=NO | |
HTTP_V2_HPACK_ENC=NO | |
+HTTP_SPDY=NO | |
HTTP_SSI=YES | |
HTTP_POSTPONE=NO | |
HTTP_REALIP=NO | |
@@ -224,6 +225,7 @@ $0: warning: the \"--with-ipv6\" option is deprecated" | |
--with-http_ssl_module) HTTP_SSL=YES ;; | |
--with-http_v2_module) HTTP_V2=YES ;; | |
--with-http_v2_hpack_enc) HTTP_V2_HPACK_ENC=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 ;; | |
@@ -435,6 +437,7 @@ cat << END | |
--with-http_ssl_module enable ngx_http_ssl_module | |
--with-http_v2_module enable ngx_http_v2_module | |
--with-http_v2_hpack_enc enable ngx_http_v2_hpack_enc | |
+ --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 e4dfe58..3afb2ac 100644 | |
--- a/src/core/ngx_connection.h | |
+++ b/src/core/ngx_connection.h | |
@@ -116,6 +116,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 1e016eb..b5bb81a 100644 | |
--- a/src/http/modules/ngx_http_ssl_module.c | |
+++ b/src/http/modules/ngx_http_ssl_module.c | |
@@ -387,10 +387,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); | |
@@ -404,9 +404,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; | |
@@ -414,6 +425,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; | |
@@ -441,19 +459,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; | |
@@ -461,6 +492,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 9d8b6d7..c572691 100644 | |
--- a/src/http/ngx_http.c | |
+++ b/src/http/ngx_http.c | |
@@ -1199,6 +1199,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 | |
@@ -1234,6 +1237,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) { | |
@@ -1268,6 +1274,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; | |
} | |
@@ -1311,6 +1320,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; | |
@@ -1804,6 +1825,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 | |
@@ -1869,6 +1893,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 afab4f6..7b70426 100644 | |
--- a/src/http/ngx_http.h | |
+++ b/src/http/ngx_http.h | |
@@ -19,6 +19,9 @@ typedef struct ngx_http_cache_s ngx_http_cache_t; | |
typedef struct ngx_http_file_cache_s ngx_http_file_cache_t; | |
typedef struct ngx_http_log_ctx_s ngx_http_log_ctx_t; | |
typedef struct ngx_http_chunked_s ngx_http_chunked_t; | |
+#if (NGX_HTTP_SPDY) | |
+typedef struct ngx_http_spdy_stream_s ngx_http_spdy_stream_t; | |
+#endif | |
typedef struct ngx_http_v2_stream_s ngx_http_v2_stream_t; | |
typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r, | |
@@ -35,9 +38,13 @@ typedef u_char *(*ngx_http_log_handler_pt)(ngx_http_request_t *r, | |
#include <ngx_http_upstream_round_robin.h> | |
#include <ngx_http_core_module.h> | |
+ | |
#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 57a4742..9ae0a06 100644 | |
--- a/src/http/ngx_http_core_module.c | |
+++ b/src/http/ngx_http_core_module.c | |
@@ -1933,6 +1933,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; | |
@@ -2284,6 +2291,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; | |
@@ -3964,11 +3974,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 a6128b5..5eba4f9 100644 | |
--- a/src/http/ngx_http_core_module.h | |
+++ b/src/http/ngx_http_core_module.h | |
@@ -74,6 +74,9 @@ typedef struct { | |
unsigned wildcard:1; | |
unsigned ssl:1; | |
unsigned http2:1; | |
+#if (NGX_HTTP_SPDY) | |
+ unsigned spdy:1; | |
+#endif | |
#if (NGX_HAVE_INET6) | |
unsigned ipv6only:1; | |
#endif | |
@@ -235,6 +238,9 @@ struct ngx_http_addr_conf_s { | |
unsigned ssl:1; | |
unsigned http2:1; | |
+#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 de1b202..322b30c 100644 | |
--- a/src/http/ngx_http_request.c | |
+++ b/src/http/ngx_http_request.c | |
@@ -319,6 +319,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; | |
@@ -821,6 +826,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; | |
@@ -2573,6 +2606,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); | |
@@ -2782,6 +2821,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) | |
@@ -3445,6 +3496,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 421004a..21077cf 100644 | |
--- a/src/http/ngx_http_request.h | |
+++ b/src/http/ngx_http_request.h | |
@@ -429,6 +429,9 @@ struct ngx_http_request_s { | |
int *captures; | |
u_char *captures_data; | |
#endif | |
+#if (NGX_HTTP_SPDY) | |
+ ngx_http_spdy_stream_t *spdy_stream; | |
+#endif | |
size_t limit_rate; | |
size_t limit_rate_after; | |
diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c | |
index c4f092e..2a2970f 100644 | |
--- a/src/http/ngx_http_request_body.c | |
+++ b/src/http/ngx_http_request_body.c | |
@@ -84,6 +84,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 | |
preread = r->header_in->last - r->header_in->pos; | |
@@ -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) | |
+ { | |
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
+ "client sent too large request"); | |
+ | |
+ return NGX_DECLINED; | |
+ } | |
+ | |
+ rest = r->header_in->last - r->header_in->pos; | |
+ | |
+ /* | |
+ * One more byte is needed for null-termination | |
+ * and another one for further progress. | |
+ */ | |
+ if (rest > cscf->large_client_header_buffers.size - 2) { | |
+ p = r->header_in->pos; | |
+ | |
+ if (rest > NGX_MAX_ERROR_STR - 300) { | |
+ rest = NGX_MAX_ERROR_STR - 300; | |
+ } | |
+ | |
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
+ "client sent too long header name or value: \"%*s...\"", | |
+ rest, p); | |
+ | |
+ return NGX_DECLINED; | |
+ } | |
+ | |
+ buf = ngx_create_temp_buf(r->pool, cscf->large_client_header_buffers.size); | |
+ if (buf == NULL) { | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
+ "spdy large header alloc: %p %uz", | |
+ buf->pos, buf->end - buf->last); | |
+ | |
+ old = r->header_in->pos; | |
+ new = buf->pos; | |
+ | |
+ if (rest) { | |
+ buf->last = ngx_cpymem(new, old, rest); | |
+ } | |
+ | |
+ r->header_in = buf; | |
+ | |
+ stream->header_buffers++; | |
+ | |
+ return NGX_OK; | |
+} | |
+ | |
+ | |
+static ngx_int_t | |
+ngx_http_spdy_handle_request_header(ngx_http_request_t *r) | |
+{ | |
+ ngx_uint_t i; | |
+ ngx_table_elt_t *h; | |
+ ngx_http_core_srv_conf_t *cscf; | |
+ ngx_http_spdy_request_header_t *sh; | |
+ | |
+ if (r->invalid_header) { | |
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); | |
+ | |
+ if (cscf->ignore_invalid_headers) { | |
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
+ "client sent invalid header: \"%*s\"", | |
+ r->header_end - r->header_name_start, | |
+ r->header_name_start); | |
+ return NGX_OK; | |
+ } | |
+ | |
+ } | |
+ | |
+ if (r->header_name_start[0] == ':') { | |
+ r->header_name_start++; | |
+ | |
+ for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) { | |
+ sh = &ngx_http_spdy_request_headers[i]; | |
+ | |
+ if (sh->hash != r->header_hash | |
+ || sh->len != r->header_name_end - r->header_name_start | |
+ || ngx_strncmp(sh->header, r->header_name_start, sh->len) != 0) | |
+ { | |
+ continue; | |
+ } | |
+ | |
+ return sh->handler(r); | |
+ } | |
+ | |
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
+ "client sent invalid header name: \":%*s\"", | |
+ r->header_end - r->header_name_start, | |
+ r->header_name_start); | |
+ | |
+ return NGX_HTTP_PARSE_INVALID_HEADER; | |
+ } | |
+ | |
+ h = ngx_list_push(&r->headers_in.headers); | |
+ if (h == NULL) { | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ h->hash = r->header_hash; | |
+ | |
+ h->key.len = r->header_name_end - r->header_name_start; | |
+ h->key.data = r->header_name_start; | |
+ | |
+ h->value.len = r->header_end - r->header_start; | |
+ h->value.data = r->header_start; | |
+ | |
+ h->lowcase_key = h->key.data; | |
+ | |
+ return NGX_OK; | |
+} | |
+ | |
+ | |
+void | |
+ngx_http_spdy_request_headers_init(void) | |
+{ | |
+ ngx_uint_t i; | |
+ ngx_http_spdy_request_header_t *h; | |
+ | |
+ for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) { | |
+ h = &ngx_http_spdy_request_headers[i]; | |
+ h->hash = ngx_hash_key(h->header, h->len); | |
+ } | |
+} | |
+ | |
+ | |
+static ngx_int_t | |
+ngx_http_spdy_parse_method(ngx_http_request_t *r) | |
+{ | |
+ size_t k, len; | |
+ ngx_uint_t n; | |
+ const u_char *p, *m; | |
+ | |
+ /* | |
+ * This array takes less than 256 sequential bytes, | |
+ * and if typical CPU cache line size is 64 bytes, | |
+ * it is prefetched for 4 load operations. | |
+ */ | |
+ static const struct { | |
+ u_char len; | |
+ const u_char method[11]; | |
+ uint32_t value; | |
+ } tests[] = { | |
+ { 3, "GET", NGX_HTTP_GET }, | |
+ { 4, "POST", NGX_HTTP_POST }, | |
+ { 4, "HEAD", NGX_HTTP_HEAD }, | |
+ { 7, "OPTIONS", NGX_HTTP_OPTIONS }, | |
+ { 8, "PROPFIND", NGX_HTTP_PROPFIND }, | |
+ { 3, "PUT", NGX_HTTP_PUT }, | |
+ { 5, "MKCOL", NGX_HTTP_MKCOL }, | |
+ { 6, "DELETE", NGX_HTTP_DELETE }, | |
+ { 4, "COPY", NGX_HTTP_COPY }, | |
+ { 4, "MOVE", NGX_HTTP_MOVE }, | |
+ { 9, "PROPPATCH", NGX_HTTP_PROPPATCH }, | |
+ { 4, "LOCK", NGX_HTTP_LOCK }, | |
+ { 6, "UNLOCK", NGX_HTTP_UNLOCK }, | |
+ { 5, "PATCH", NGX_HTTP_PATCH }, | |
+ { 5, "TRACE", NGX_HTTP_TRACE } | |
+ }, *test; | |
+ | |
+ if (r->method_name.len) { | |
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
+ "client sent duplicate :method header"); | |
+ | |
+ return NGX_HTTP_PARSE_INVALID_HEADER; | |
+ } | |
+ | |
+ len = r->header_end - r->header_start; | |
+ | |
+ r->method_name.len = len; | |
+ r->method_name.data = r->header_start; | |
+ | |
+ test = tests; | |
+ n = sizeof(tests) / sizeof(tests[0]); | |
+ | |
+ do { | |
+ if (len == test->len) { | |
+ p = r->method_name.data; | |
+ m = test->method; | |
+ k = len; | |
+ | |
+ do { | |
+ if (*p++ != *m++) { | |
+ goto next; | |
+ } | |
+ } while (--k); | |
+ | |
+ r->method = test->value; | |
+ return NGX_OK; | |
+ } | |
+ | |
+ next: | |
+ test++; | |
+ | |
+ } while (--n); | |
+ | |
+ p = r->method_name.data; | |
+ | |
+ do { | |
+ if ((*p < 'A' || *p > 'Z') && *p != '_') { | |
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
+ "client sent invalid method: \"%V\"", | |
+ &r->method_name); | |
+ | |
+ return NGX_HTTP_PARSE_INVALID_HEADER; | |
+ } | |
+ | |
+ p++; | |
+ | |
+ } while (--len); | |
+ | |
+ return NGX_OK; | |
+} | |
+ | |
+ | |
+static ngx_int_t | |
+ngx_http_spdy_parse_scheme(ngx_http_request_t *r) | |
+{ | |
+ if (r->schema_start) { | |
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
+ "client sent duplicate :schema header"); | |
+ | |
+ return NGX_HTTP_PARSE_INVALID_HEADER; | |
+ } | |
+ | |
+ r->schema_start = r->header_start; | |
+ r->schema_end = r->header_end; | |
+ | |
+ return NGX_OK; | |
+} | |
+ | |
+ | |
+static ngx_int_t | |
+ngx_http_spdy_parse_host(ngx_http_request_t *r) | |
+{ | |
+ ngx_table_elt_t *h; | |
+ | |
+ if (r->headers_in.host) { | |
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
+ "client sent duplicate :host header"); | |
+ | |
+ return NGX_HTTP_PARSE_INVALID_HEADER; | |
+ } | |
+ | |
+ h = ngx_list_push(&r->headers_in.headers); | |
+ if (h == NULL) { | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ r->headers_in.host = h; | |
+ | |
+ h->hash = r->header_hash; | |
+ | |
+ h->key.len = r->header_name_end - r->header_name_start; | |
+ h->key.data = r->header_name_start; | |
+ | |
+ h->value.len = r->header_end - r->header_start; | |
+ h->value.data = r->header_start; | |
+ | |
+ h->lowcase_key = h->key.data; | |
+ | |
+ return NGX_OK; | |
+} | |
+ | |
+ | |
+static ngx_int_t | |
+ngx_http_spdy_parse_path(ngx_http_request_t *r) | |
+{ | |
+ if (r->unparsed_uri.len) { | |
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
+ "client sent duplicate :path header"); | |
+ | |
+ return NGX_HTTP_PARSE_INVALID_HEADER; | |
+ } | |
+ | |
+ r->uri_start = r->header_start; | |
+ r->uri_end = r->header_end; | |
+ | |
+ if (ngx_http_parse_uri(r) != NGX_OK) { | |
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
+ "client sent invalid URI: \"%*s\"", | |
+ r->uri_end - r->uri_start, r->uri_start); | |
+ | |
+ return NGX_HTTP_PARSE_INVALID_HEADER; | |
+ } | |
+ | |
+ if (ngx_http_process_request_uri(r) != NGX_OK) { | |
+ /* | |
+ * request has been finalized already | |
+ * in ngx_http_process_request_uri() | |
+ */ | |
+ return NGX_ABORT; | |
+ } | |
+ | |
+ return NGX_OK; | |
+} | |
+ | |
+ | |
+static ngx_int_t | |
+ngx_http_spdy_parse_version(ngx_http_request_t *r) | |
+{ | |
+ u_char *p, ch; | |
+ | |
+ if (r->http_protocol.len) { | |
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
+ "client sent duplicate :version header"); | |
+ | |
+ return NGX_HTTP_PARSE_INVALID_HEADER; | |
+ } | |
+ | |
+ p = r->header_start; | |
+ | |
+ if (r->header_end - p < 8 || !(ngx_str5cmp(p, 'H', 'T', 'T', 'P', '/'))) { | |
+ goto invalid; | |
+ } | |
+ | |
+ ch = *(p + 5); | |
+ | |
+ if (ch < '1' || ch > '9') { | |
+ goto invalid; | |
+ } | |
+ | |
+ r->http_major = ch - '0'; | |
+ | |
+ for (p += 6; p != r->header_end - 2; p++) { | |
+ | |
+ ch = *p; | |
+ | |
+ if (ch == '.') { | |
+ break; | |
+ } | |
+ | |
+ if (ch < '0' || ch > '9') { | |
+ goto invalid; | |
+ } | |
+ | |
+ r->http_major = r->http_major * 10 + ch - '0'; | |
+ } | |
+ | |
+ if (*p != '.') { | |
+ goto invalid; | |
+ } | |
+ | |
+ ch = *(p + 1); | |
+ | |
+ if (ch < '0' || ch > '9') { | |
+ goto invalid; | |
+ } | |
+ | |
+ r->http_minor = ch - '0'; | |
+ | |
+ for (p += 2; p != r->header_end; p++) { | |
+ | |
+ ch = *p; | |
+ | |
+ if (ch < '0' || ch > '9') { | |
+ goto invalid; | |
+ } | |
+ | |
+ r->http_minor = r->http_minor * 10 + ch - '0'; | |
+ } | |
+ | |
+ r->http_protocol.len = r->header_end - r->header_start; | |
+ r->http_protocol.data = r->header_start; | |
+ r->http_version = r->http_major * 1000 + r->http_minor; | |
+ | |
+ return NGX_OK; | |
+ | |
+invalid: | |
+ | |
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
+ "client sent invalid http version: \"%*s\"", | |
+ r->header_end - r->header_start, r->header_start); | |
+ | |
+ return NGX_HTTP_PARSE_INVALID_HEADER; | |
+} | |
+ | |
+ | |
+static ngx_int_t | |
+ngx_http_spdy_construct_request_line(ngx_http_request_t *r) | |
+{ | |
+ u_char *p; | |
+ | |
+ if (r->method_name.len == 0 | |
+ || r->unparsed_uri.len == 0 | |
+ || r->http_protocol.len == 0) | |
+ { | |
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ r->request_line.len = r->method_name.len + 1 | |
+ + r->unparsed_uri.len + 1 | |
+ + r->http_protocol.len; | |
+ | |
+ p = ngx_pnalloc(r->pool, r->request_line.len + 1); | |
+ if (p == NULL) { | |
+ ngx_http_spdy_close_stream(r->spdy_stream, | |
+ NGX_HTTP_INTERNAL_SERVER_ERROR); | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ r->request_line.data = p; | |
+ | |
+ p = ngx_cpymem(p, r->method_name.data, r->method_name.len); | |
+ | |
+ *p++ = ' '; | |
+ | |
+ p = ngx_cpymem(p, r->unparsed_uri.data, r->unparsed_uri.len); | |
+ | |
+ *p++ = ' '; | |
+ | |
+ ngx_memcpy(p, r->http_protocol.data, r->http_protocol.len + 1); | |
+ | |
+ /* some modules expect the space character after method name */ | |
+ r->method_name.data = r->request_line.data; | |
+ | |
+ return NGX_OK; | |
+} | |
+ | |
+ | |
+static void | |
+ngx_http_spdy_run_request(ngx_http_request_t *r) | |
+{ | |
+ ngx_uint_t i; | |
+ ngx_list_part_t *part; | |
+ ngx_table_elt_t *h; | |
+ ngx_http_header_t *hh; | |
+ ngx_http_core_main_conf_t *cmcf; | |
+ | |
+ if (ngx_http_spdy_construct_request_line(r) != NGX_OK) { | |
+ return; | |
+ } | |
+ | |
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
+ "spdy http request line: \"%V\"", &r->request_line); | |
+ | |
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); | |
+ | |
+ part = &r->headers_in.headers.part; | |
+ h = part->elts; | |
+ | |
+ for (i = 0 ;; i++) { | |
+ | |
+ if (i >= part->nelts) { | |
+ if (part->next == NULL) { | |
+ break; | |
+ } | |
+ | |
+ part = part->next; | |
+ h = part->elts; | |
+ i = 0; | |
+ } | |
+ | |
+ hh = ngx_hash_find(&cmcf->headers_in_hash, h[i].hash, | |
+ h[i].lowcase_key, h[i].key.len); | |
+ | |
+ if (hh && hh->handler(r, &h[i], hh->offset) != NGX_OK) { | |
+ return; | |
+ } | |
+ | |
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
+ "spdy http header: \"%V: %V\"", &h[i].key, &h[i].value); | |
+ } | |
+ | |
+ r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE; | |
+ | |
+ if (ngx_http_process_request_header(r) != NGX_OK) { | |
+ return; | |
+ } | |
+ | |
+ if (r->headers_in.content_length_n > 0 && r->spdy_stream->in_closed) { | |
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
+ "client prematurely closed stream"); | |
+ | |
+ r->spdy_stream->skip_data = NGX_SPDY_DATA_ERROR; | |
+ | |
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); | |
+ return; | |
+ } | |
+ | |
+ ngx_http_process_request(r); | |
+} | |
+ | |
+ | |
+static ngx_int_t | |
+ngx_http_spdy_init_request_body(ngx_http_request_t *r) | |
+{ | |
+ ngx_buf_t *buf; | |
+ ngx_temp_file_t *tf; | |
+ ngx_http_request_body_t *rb; | |
+ ngx_http_core_loc_conf_t *clcf; | |
+ | |
+ rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); | |
+ if (rb == NULL) { | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ r->request_body = rb; | |
+ | |
+ if (r->spdy_stream->in_closed) { | |
+ return NGX_OK; | |
+ } | |
+ | |
+ rb->rest = r->headers_in.content_length_n; | |
+ | |
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | |
+ | |
+ if (r->request_body_in_file_only | |
+ || rb->rest > (off_t) clcf->client_body_buffer_size | |
+ || rb->rest < 0) | |
+ { | |
+ tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); | |
+ if (tf == NULL) { | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ tf->file.fd = NGX_INVALID_FILE; | |
+ tf->file.log = r->connection->log; | |
+ tf->path = clcf->client_body_temp_path; | |
+ tf->pool = r->pool; | |
+ tf->warn = "a client request body is buffered to a temporary file"; | |
+ tf->log_level = r->request_body_file_log_level; | |
+ tf->persistent = r->request_body_in_persistent_file; | |
+ tf->clean = r->request_body_in_clean_file; | |
+ | |
+ if (r->request_body_file_group_access) { | |
+ tf->access = 0660; | |
+ } | |
+ | |
+ rb->temp_file = tf; | |
+ | |
+ if (r->spdy_stream->in_closed | |
+ && ngx_create_temp_file(&tf->file, tf->path, tf->pool, | |
+ tf->persistent, tf->clean, tf->access) | |
+ != NGX_OK) | |
+ { | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ buf = ngx_calloc_buf(r->pool); | |
+ if (buf == NULL) { | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ } else { | |
+ | |
+ if (rb->rest == 0) { | |
+ return NGX_OK; | |
+ } | |
+ | |
+ buf = ngx_create_temp_buf(r->pool, (size_t) rb->rest); | |
+ if (buf == NULL) { | |
+ return NGX_ERROR; | |
+ } | |
+ } | |
+ | |
+ rb->buf = buf; | |
+ | |
+ rb->bufs = ngx_alloc_chain_link(r->pool); | |
+ if (rb->bufs == NULL) { | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ rb->bufs->buf = buf; | |
+ rb->bufs->next = NULL; | |
+ | |
+ rb->rest = 0; | |
+ | |
+ return NGX_OK; | |
+} | |
+ | |
+ | |
+ngx_int_t | |
+ngx_http_spdy_read_request_body(ngx_http_request_t *r, | |
+ ngx_http_client_body_handler_pt post_handler) | |
+{ | |
+ ngx_http_spdy_stream_t *stream; | |
+ | |
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
+ "spdy read request body"); | |
+ | |
+ stream = r->spdy_stream; | |
+ | |
+ switch (stream->skip_data) { | |
+ | |
+ case NGX_SPDY_DATA_DISCARD: | |
+ post_handler(r); | |
+ return NGX_OK; | |
+ | |
+ case NGX_SPDY_DATA_ERROR: | |
+ if (r->headers_in.content_length_n == -1) { | |
+ return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; | |
+ } else { | |
+ return NGX_HTTP_BAD_REQUEST; | |
+ } | |
+ | |
+ case NGX_SPDY_DATA_INTERNAL_ERROR: | |
+ return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
+ } | |
+ | |
+ if (!r->request_body && ngx_http_spdy_init_request_body(r) != NGX_OK) { | |
+ stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR; | |
+ return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
+ } | |
+ | |
+ if (stream->in_closed) { | |
+ post_handler(r); | |
+ return NGX_OK; | |
+ } | |
+ | |
+ r->request_body->post_handler = post_handler; | |
+ | |
+ r->read_event_handler = ngx_http_test_reading; | |
+ r->write_event_handler = ngx_http_request_empty_handler; | |
+ | |
+ return NGX_AGAIN; | |
+} | |
+ | |
+ | |
+static ngx_int_t | |
+ngx_http_spdy_terminate_stream(ngx_http_spdy_connection_t *sc, | |
+ ngx_http_spdy_stream_t *stream, ngx_uint_t status) | |
+{ | |
+ ngx_event_t *rev; | |
+ ngx_connection_t *fc; | |
+ | |
+ if (ngx_http_spdy_send_rst_stream(sc, stream->id, status, | |
+ NGX_SPDY_HIGHEST_PRIORITY) | |
+ == NGX_ERROR) | |
+ { | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ stream->out_closed = 1; | |
+ | |
+ fc = stream->request->connection; | |
+ fc->error = 1; | |
+ | |
+ rev = fc->read; | |
+ rev->handler(rev); | |
+ | |
+ return NGX_OK; | |
+} | |
+ | |
+ | |
+static void | |
+ngx_http_spdy_close_stream_handler(ngx_event_t *ev) | |
+{ | |
+ ngx_connection_t *fc; | |
+ ngx_http_request_t *r; | |
+ | |
+ fc = ev->data; | |
+ r = fc->data; | |
+ | |
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
+ "spdy close stream handler"); | |
+ | |
+ ngx_http_spdy_close_stream(r->spdy_stream, 0); | |
+} | |
+ | |
+ | |
+void | |
+ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc) | |
+{ | |
+ ngx_event_t *ev; | |
+ ngx_connection_t *fc; | |
+ ngx_http_spdy_stream_t **index, *s; | |
+ ngx_http_spdy_srv_conf_t *sscf; | |
+ ngx_http_spdy_connection_t *sc; | |
+ | |
+ sc = stream->connection; | |
+ | |
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
+ "spdy close stream %ui, queued %ui, processing %ui", | |
+ stream->id, stream->queued, sc->processing); | |
+ | |
+ fc = stream->request->connection; | |
+ | |
+ if (stream->queued) { | |
+ fc->write->handler = ngx_http_spdy_close_stream_handler; | |
+ return; | |
+ } | |
+ | |
+ if (!stream->out_closed) { | |
+ if (ngx_http_spdy_send_rst_stream(sc, stream->id, | |
+ NGX_SPDY_INTERNAL_ERROR, | |
+ stream->priority) | |
+ != NGX_OK) | |
+ { | |
+ sc->connection->error = 1; | |
+ } | |
+ } | |
+ | |
+ if (sc->stream == stream) { | |
+ sc->stream = NULL; | |
+ } | |
+ | |
+ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, | |
+ ngx_http_spdy_module); | |
+ | |
+ index = sc->streams_index + ngx_http_spdy_stream_index(sscf, stream->id); | |
+ | |
+ for ( ;; ) { | |
+ s = *index; | |
+ | |
+ if (s == NULL) { | |
+ break; | |
+ } | |
+ | |
+ if (s == stream) { | |
+ *index = s->index; | |
+ break; | |
+ } | |
+ | |
+ index = &s->index; | |
+ } | |
+ | |
+ ngx_http_free_request(stream->request, rc); | |
+ | |
+ ev = fc->read; | |
+ | |
+ if (ev->active || ev->disabled) { | |
+ ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0, | |
+ "fake read event was activated"); | |
+ } | |
+ | |
+ if (ev->timer_set) { | |
+ ngx_del_timer(ev); | |
+ } | |
+ | |
+ if (ev->posted) { | |
+ ngx_delete_posted_event(ev); | |
+ } | |
+ | |
+ ev = fc->write; | |
+ | |
+ if (ev->active || ev->disabled) { | |
+ ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0, | |
+ "fake write event was activated"); | |
+ } | |
+ | |
+ if (ev->timer_set) { | |
+ ngx_del_timer(ev); | |
+ } | |
+ | |
+ if (ev->posted) { | |
+ ngx_delete_posted_event(ev); | |
+ } | |
+ | |
+ fc->data = sc->free_fake_connections; | |
+ sc->free_fake_connections = fc; | |
+ | |
+ sc->processing--; | |
+ | |
+ if (sc->processing || sc->blocked) { | |
+ return; | |
+ } | |
+ | |
+ ev = sc->connection->read; | |
+ | |
+ ev->handler = ngx_http_spdy_handle_connection_handler; | |
+ ngx_post_event(ev, &ngx_posted_events); | |
+} | |
+ | |
+ | |
+static void | |
+ngx_http_spdy_handle_connection_handler(ngx_event_t *rev) | |
+{ | |
+ ngx_connection_t *c; | |
+ | |
+ rev->handler = ngx_http_spdy_read_handler; | |
+ | |
+ if (rev->ready) { | |
+ ngx_http_spdy_read_handler(rev); | |
+ return; | |
+ } | |
+ | |
+ c = rev->data; | |
+ | |
+ ngx_http_spdy_handle_connection(c->data); | |
+} | |
+ | |
+ | |
+static void | |
+ngx_http_spdy_keepalive_handler(ngx_event_t *rev) | |
+{ | |
+ ngx_connection_t *c; | |
+ ngx_http_spdy_srv_conf_t *sscf; | |
+ ngx_http_spdy_connection_t *sc; | |
+ | |
+ c = rev->data; | |
+ | |
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy keepalive handler"); | |
+ | |
+ if (rev->timedout || c->close) { | |
+ ngx_http_close_connection(c); | |
+ return; | |
+ } | |
+ | |
+#if (NGX_HAVE_KQUEUE) | |
+ | |
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { | |
+ if (rev->pending_eof) { | |
+ c->log->handler = NULL; | |
+ ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno, | |
+ "kevent() reported that client %V closed " | |
+ "keepalive connection", &c->addr_text); | |
+#if (NGX_HTTP_SSL) | |
+ if (c->ssl) { | |
+ c->ssl->no_send_shutdown = 1; | |
+ } | |
+#endif | |
+ ngx_http_close_connection(c); | |
+ return; | |
+ } | |
+ } | |
+ | |
+#endif | |
+ | |
+ c->destroyed = 0; | |
+ c->idle = 0; | |
+ ngx_reusable_connection(c, 0); | |
+ | |
+ sc = c->data; | |
+ | |
+ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, | |
+ ngx_http_spdy_module); | |
+ | |
+ sc->pool = ngx_create_pool(sscf->pool_size, sc->connection->log); | |
+ if (sc->pool == NULL) { | |
+ ngx_http_close_connection(c); | |
+ return; | |
+ } | |
+ | |
+ sc->streams_index = ngx_pcalloc(sc->pool, | |
+ ngx_http_spdy_streams_index_size(sscf) | |
+ * sizeof(ngx_http_spdy_stream_t *)); | |
+ if (sc->streams_index == NULL) { | |
+ ngx_http_close_connection(c); | |
+ return; | |
+ } | |
+ | |
+ c->write->handler = ngx_http_spdy_write_handler; | |
+ | |
+ rev->handler = ngx_http_spdy_read_handler; | |
+ ngx_http_spdy_read_handler(rev); | |
+} | |
+ | |
+ | |
+static void | |
+ngx_http_spdy_finalize_connection(ngx_http_spdy_connection_t *sc, | |
+ ngx_int_t rc) | |
+{ | |
+ ngx_uint_t i, size; | |
+ ngx_event_t *ev; | |
+ ngx_connection_t *c, *fc; | |
+ ngx_http_request_t *r; | |
+ ngx_http_spdy_stream_t *stream; | |
+ ngx_http_spdy_srv_conf_t *sscf; | |
+ | |
+ c = sc->connection; | |
+ | |
+ if (!sc->processing) { | |
+ ngx_http_close_connection(c); | |
+ return; | |
+ } | |
+ | |
+ c->error = 1; | |
+ c->read->handler = ngx_http_empty_handler; | |
+ c->write->handler = ngx_http_empty_handler; | |
+ | |
+ sc->last_out = NULL; | |
+ | |
+ sc->blocked = 1; | |
+ | |
+ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, | |
+ ngx_http_spdy_module); | |
+ | |
+ size = ngx_http_spdy_streams_index_size(sscf); | |
+ | |
+ for (i = 0; i < size; i++) { | |
+ stream = sc->streams_index[i]; | |
+ | |
+ while (stream) { | |
+ stream->handled = 0; | |
+ | |
+ r = stream->request; | |
+ fc = r->connection; | |
+ | |
+ fc->error = 1; | |
+ | |
+ if (stream->queued) { | |
+ stream->queued = 0; | |
+ | |
+ ev = fc->write; | |
+ ev->delayed = 0; | |
+ | |
+ } else { | |
+ ev = fc->read; | |
+ } | |
+ | |
+ stream = stream->index; | |
+ | |
+ ev->eof = 1; | |
+ ev->handler(ev); | |
+ } | |
+ } | |
+ | |
+ sc->blocked = 0; | |
+ | |
+ if (sc->processing) { | |
+ return; | |
+ } | |
+ | |
+ ngx_http_close_connection(c); | |
+} | |
+ | |
+ | |
+static ngx_int_t | |
+ngx_http_spdy_adjust_windows(ngx_http_spdy_connection_t *sc, ssize_t delta) | |
+{ | |
+ ngx_uint_t i, size; | |
+ ngx_event_t *wev; | |
+ ngx_http_spdy_stream_t *stream, *sn; | |
+ ngx_http_spdy_srv_conf_t *sscf; | |
+ | |
+ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, | |
+ ngx_http_spdy_module); | |
+ | |
+ size = ngx_http_spdy_streams_index_size(sscf); | |
+ | |
+ for (i = 0; i < size; i++) { | |
+ | |
+ for (stream = sc->streams_index[i]; stream; stream = sn) { | |
+ sn = stream->index; | |
+ | |
+ if (delta > 0 | |
+ && stream->send_window | |
+ > (ssize_t) (NGX_SPDY_MAX_WINDOW - delta)) | |
+ { | |
+ if (ngx_http_spdy_terminate_stream(sc, stream, | |
+ NGX_SPDY_FLOW_CONTROL_ERROR) | |
+ == NGX_ERROR) | |
+ { | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ continue; | |
+ } | |
+ | |
+ stream->send_window += delta; | |
+ | |
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
+ "spdy:%ui adjust window:%z", | |
+ stream->id, stream->send_window); | |
+ | |
+ if (stream->send_window > 0 && stream->exhausted) { | |
+ stream->exhausted = 0; | |
+ | |
+ wev = stream->request->connection->write; | |
+ | |
+ if (!wev->timer_set) { | |
+ wev->delayed = 0; | |
+ wev->handler(wev); | |
+ } | |
+ } | |
+ } | |
+ } | |
+ | |
+ return NGX_OK; | |
+} | |
+ | |
+ | |
+static void | |
+ngx_http_spdy_pool_cleanup(void *data) | |
+{ | |
+ ngx_http_spdy_connection_t *sc = data; | |
+ | |
+ if (sc->pool) { | |
+ ngx_destroy_pool(sc->pool); | |
+ } | |
+} | |
+ | |
+ | |
+static void * | |
+ngx_http_spdy_zalloc(void *opaque, u_int items, u_int size) | |
+{ | |
+ ngx_http_spdy_connection_t *sc = opaque; | |
+ | |
+ return ngx_palloc(sc->connection->pool, items * size); | |
+} | |
+ | |
+ | |
+static void | |
+ngx_http_spdy_zfree(void *opaque, void *address) | |
+{ | |
+#if 0 | |
+ ngx_http_spdy_connection_t *sc = opaque; | |
+ | |
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
+ "spdy zfree: %p", address); | |
+#endif | |
+} | |
diff --git a/src/http/ngx_http_spdy.h b/src/http/ngx_http_spdy.h | |
new file mode 100644 | |
index 0000000..df24495 | |
--- /dev/null | |
+++ b/src/http/ngx_http_spdy.h | |
@@ -0,0 +1,261 @@ | |
+/* | |
+ * Copyright (C) Nginx, Inc. | |
+ * Copyright (C) Valentin V. Bartenev | |
+ */ | |
+ | |
+ | |
+#ifndef _NGX_HTTP_SPDY_H_INCLUDED_ | |
+#define _NGX_HTTP_SPDY_H_INCLUDED_ | |
+ | |
+ | |
+#include <ngx_config.h> | |
+#include <ngx_core.h> | |
+#include <ngx_http.h> | |
+ | |
+#include <zlib.h> | |
+ | |
+ | |
+#define NGX_SPDY_VERSION 3 | |
+ | |
+#define NGX_SPDY_NPN_ADVERTISE "\x08spdy/3.1" | |
+#define NGX_SPDY_NPN_NEGOTIATED "spdy/3.1" | |
+ | |
+#define NGX_SPDY_STATE_BUFFER_SIZE 16 | |
+ | |
+#define NGX_SPDY_CTL_BIT 1 | |
+ | |
+#define NGX_SPDY_SYN_STREAM 1 | |
+#define NGX_SPDY_SYN_REPLY 2 | |
+#define NGX_SPDY_RST_STREAM 3 | |
+#define NGX_SPDY_SETTINGS 4 | |
+#define NGX_SPDY_PING 6 | |
+#define NGX_SPDY_GOAWAY 7 | |
+#define NGX_SPDY_HEADERS 8 | |
+#define NGX_SPDY_WINDOW_UPDATE 9 | |
+ | |
+#define NGX_SPDY_FRAME_HEADER_SIZE 8 | |
+ | |
+#define NGX_SPDY_SID_SIZE 4 | |
+#define NGX_SPDY_DELTA_SIZE 4 | |
+ | |
+#define NGX_SPDY_SYN_STREAM_SIZE 10 | |
+#define NGX_SPDY_SYN_REPLY_SIZE 4 | |
+#define NGX_SPDY_RST_STREAM_SIZE 8 | |
+#define NGX_SPDY_PING_SIZE 4 | |
+#define NGX_SPDY_GOAWAY_SIZE 8 | |
+#define NGX_SPDY_WINDOW_UPDATE_SIZE 8 | |
+#define NGX_SPDY_NV_NUM_SIZE 4 | |
+#define NGX_SPDY_NV_NLEN_SIZE 4 | |
+#define NGX_SPDY_NV_VLEN_SIZE 4 | |
+#define NGX_SPDY_SETTINGS_NUM_SIZE 4 | |
+#define NGX_SPDY_SETTINGS_FID_SIZE 4 | |
+#define NGX_SPDY_SETTINGS_VAL_SIZE 4 | |
+ | |
+#define NGX_SPDY_SETTINGS_PAIR_SIZE \ | |
+ (NGX_SPDY_SETTINGS_FID_SIZE + NGX_SPDY_SETTINGS_VAL_SIZE) | |
+ | |
+#define NGX_SPDY_HIGHEST_PRIORITY 0 | |
+#define NGX_SPDY_LOWEST_PRIORITY 7 | |
+ | |
+#define NGX_SPDY_FLAG_FIN 0x01 | |
+#define NGX_SPDY_FLAG_UNIDIRECTIONAL 0x02 | |
+#define NGX_SPDY_FLAG_CLEAR_SETTINGS 0x01 | |
+ | |
+#define NGX_SPDY_MAX_FRAME_SIZE ((1 << 24) - 1) | |
+ | |
+#define NGX_SPDY_DATA_DISCARD 1 | |
+#define NGX_SPDY_DATA_ERROR 2 | |
+#define NGX_SPDY_DATA_INTERNAL_ERROR 3 | |
+ | |
+ | |
+typedef struct ngx_http_spdy_connection_s ngx_http_spdy_connection_t; | |
+typedef struct ngx_http_spdy_out_frame_s ngx_http_spdy_out_frame_t; | |
+ | |
+ | |
+typedef u_char *(*ngx_http_spdy_handler_pt) (ngx_http_spdy_connection_t *sc, | |
+ u_char *pos, u_char *end); | |
+ | |
+struct ngx_http_spdy_connection_s { | |
+ ngx_connection_t *connection; | |
+ ngx_http_connection_t *http_connection; | |
+ | |
+ ngx_uint_t processing; | |
+ | |
+ size_t send_window; | |
+ size_t recv_window; | |
+ size_t init_window; | |
+ | |
+ ngx_queue_t waiting; | |
+ | |
+ u_char buffer[NGX_SPDY_STATE_BUFFER_SIZE]; | |
+ size_t buffer_used; | |
+ ngx_http_spdy_handler_pt handler; | |
+ | |
+ z_stream zstream_in; | |
+ z_stream zstream_out; | |
+ | |
+ ngx_pool_t *pool; | |
+ | |
+ ngx_http_spdy_out_frame_t *free_ctl_frames; | |
+ ngx_connection_t *free_fake_connections; | |
+ | |
+ ngx_http_spdy_stream_t **streams_index; | |
+ | |
+ ngx_http_spdy_out_frame_t *last_out; | |
+ | |
+ ngx_queue_t posted; | |
+ | |
+ ngx_http_spdy_stream_t *stream; | |
+ | |
+ ngx_uint_t entries; | |
+ size_t length; | |
+ u_char flags; | |
+ | |
+ ngx_uint_t last_sid; | |
+ | |
+ unsigned blocked:1; | |
+ unsigned incomplete:1; | |
+}; | |
+ | |
+ | |
+struct ngx_http_spdy_stream_s { | |
+ ngx_uint_t id; | |
+ ngx_http_request_t *request; | |
+ ngx_http_spdy_connection_t *connection; | |
+ ngx_http_spdy_stream_t *index; | |
+ | |
+ ngx_uint_t header_buffers; | |
+ ngx_uint_t queued; | |
+ | |
+ /* | |
+ * A change to SETTINGS_INITIAL_WINDOW_SIZE could cause the | |
+ * send_window to become negative, hence it's signed. | |
+ */ | |
+ ssize_t send_window; | |
+ size_t recv_window; | |
+ | |
+ ngx_http_spdy_out_frame_t *free_frames; | |
+ ngx_chain_t *free_data_headers; | |
+ ngx_chain_t *free_bufs; | |
+ | |
+ ngx_queue_t queue; | |
+ | |
+ unsigned priority:3; | |
+ unsigned handled:1; | |
+ unsigned blocked:1; | |
+ unsigned exhausted:1; | |
+ unsigned in_closed:1; | |
+ unsigned out_closed:1; | |
+ unsigned skip_data:2; | |
+}; | |
+ | |
+ | |
+struct ngx_http_spdy_out_frame_s { | |
+ ngx_http_spdy_out_frame_t *next; | |
+ ngx_chain_t *first; | |
+ ngx_chain_t *last; | |
+ ngx_int_t (*handler)(ngx_http_spdy_connection_t *sc, | |
+ ngx_http_spdy_out_frame_t *frame); | |
+ | |
+ ngx_http_spdy_stream_t *stream; | |
+ size_t length; | |
+ | |
+ ngx_uint_t priority; | |
+ unsigned blocked:1; | |
+ unsigned fin:1; | |
+}; | |
+ | |
+ | |
+static ngx_inline void | |
+ngx_http_spdy_queue_frame(ngx_http_spdy_connection_t *sc, | |
+ ngx_http_spdy_out_frame_t *frame) | |
+{ | |
+ ngx_http_spdy_out_frame_t **out; | |
+ | |
+ for (out = &sc->last_out; *out; out = &(*out)->next) | |
+ { | |
+ /* | |
+ * NB: higher values represent lower priorities. | |
+ */ | |
+ if (frame->priority >= (*out)->priority) { | |
+ break; | |
+ } | |
+ } | |
+ | |
+ frame->next = *out; | |
+ *out = frame; | |
+} | |
+ | |
+ | |
+static ngx_inline void | |
+ngx_http_spdy_queue_blocked_frame(ngx_http_spdy_connection_t *sc, | |
+ ngx_http_spdy_out_frame_t *frame) | |
+{ | |
+ ngx_http_spdy_out_frame_t **out; | |
+ | |
+ for (out = &sc->last_out; *out; out = &(*out)->next) | |
+ { | |
+ if ((*out)->blocked) { | |
+ break; | |
+ } | |
+ } | |
+ | |
+ frame->next = *out; | |
+ *out = frame; | |
+} | |
+ | |
+ | |
+void ngx_http_spdy_init(ngx_event_t *rev); | |
+void ngx_http_spdy_request_headers_init(void); | |
+ | |
+ngx_int_t ngx_http_spdy_read_request_body(ngx_http_request_t *r, | |
+ ngx_http_client_body_handler_pt post_handler); | |
+ | |
+void ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc); | |
+ | |
+ngx_int_t ngx_http_spdy_send_output_queue(ngx_http_spdy_connection_t *sc); | |
+ | |
+ | |
+#define ngx_spdy_frame_aligned_write_uint16(p, s) \ | |
+ (*(uint16_t *) (p) = htons((uint16_t) (s)), (p) + sizeof(uint16_t)) | |
+ | |
+#define ngx_spdy_frame_aligned_write_uint32(p, s) \ | |
+ (*(uint32_t *) (p) = htonl((uint32_t) (s)), (p) + sizeof(uint32_t)) | |
+ | |
+#if (NGX_HAVE_NONALIGNED) | |
+ | |
+#define ngx_spdy_frame_write_uint16 ngx_spdy_frame_aligned_write_uint16 | |
+#define ngx_spdy_frame_write_uint32 ngx_spdy_frame_aligned_write_uint32 | |
+ | |
+#else | |
+ | |
+#define ngx_spdy_frame_write_uint16(p, s) \ | |
+ ((p)[0] = (u_char) ((s) >> 8), \ | |
+ (p)[1] = (u_char) (s), \ | |
+ (p) + sizeof(uint16_t)) | |
+ | |
+#define ngx_spdy_frame_write_uint32(p, s) \ | |
+ ((p)[0] = (u_char) ((s) >> 24), \ | |
+ (p)[1] = (u_char) ((s) >> 16), \ | |
+ (p)[2] = (u_char) ((s) >> 8), \ | |
+ (p)[3] = (u_char) (s), \ | |
+ (p) + sizeof(uint32_t)) | |
+ | |
+#endif | |
+ | |
+ | |
+#define ngx_spdy_ctl_frame_head(t) \ | |
+ ((uint32_t) NGX_SPDY_CTL_BIT << 31 | NGX_SPDY_VERSION << 16 | (t)) | |
+ | |
+#define ngx_spdy_frame_write_head(p, t) \ | |
+ ngx_spdy_frame_aligned_write_uint32(p, ngx_spdy_ctl_frame_head(t)) | |
+ | |
+#define ngx_spdy_frame_write_flags_and_len(p, f, l) \ | |
+ ngx_spdy_frame_aligned_write_uint32(p, (f) << 24 | (l)) | |
+#define ngx_spdy_frame_write_flags_and_id(p, f, i) \ | |
+ ngx_spdy_frame_aligned_write_uint32(p, (f) << 24 | (i)) | |
+ | |
+#define ngx_spdy_frame_write_sid ngx_spdy_frame_aligned_write_uint32 | |
+#define ngx_spdy_frame_write_window ngx_spdy_frame_aligned_write_uint32 | |
+ | |
+#endif /* _NGX_HTTP_SPDY_H_INCLUDED_ */ | |
diff --git a/src/http/ngx_http_spdy_filter_module.c b/src/http/ngx_http_spdy_filter_module.c | |
new file mode 100644 | |
index 0000000..377e935 | |
--- /dev/null | |
+++ b/src/http/ngx_http_spdy_filter_module.c | |
@@ -0,0 +1,1222 @@ | |
+ | |
+/* | |
+ * Copyright (C) Nginx, Inc. | |
+ * Copyright (C) Valentin V. Bartenev | |
+ */ | |
+ | |
+ | |
+#include <ngx_config.h> | |
+#include <ngx_core.h> | |
+#include <ngx_http.h> | |
+#include <nginx.h> | |
+#include <ngx_http_spdy_module.h> | |
+ | |
+#include <zlib.h> | |
+ | |
+ | |
+#define ngx_http_spdy_nv_nsize(h) (NGX_SPDY_NV_NLEN_SIZE + sizeof(h) - 1) | |
+#define ngx_http_spdy_nv_vsize(h) (NGX_SPDY_NV_VLEN_SIZE + sizeof(h) - 1) | |
+ | |
+#define ngx_http_spdy_nv_write_num ngx_spdy_frame_write_uint32 | |
+#define ngx_http_spdy_nv_write_nlen ngx_spdy_frame_write_uint32 | |
+#define ngx_http_spdy_nv_write_vlen ngx_spdy_frame_write_uint32 | |
+ | |
+#define ngx_http_spdy_nv_write_name(p, h) \ | |
+ ngx_cpymem(ngx_http_spdy_nv_write_nlen(p, sizeof(h) - 1), h, sizeof(h) - 1) | |
+ | |
+#define ngx_http_spdy_nv_write_val(p, h) \ | |
+ ngx_cpymem(ngx_http_spdy_nv_write_vlen(p, sizeof(h) - 1), h, sizeof(h) - 1) | |
+ | |
+ | |
+static ngx_chain_t *ngx_http_spdy_send_chain(ngx_connection_t *fc, | |
+ ngx_chain_t *in, off_t limit); | |
+ | |
+static ngx_inline ngx_int_t ngx_http_spdy_filter_send( | |
+ ngx_connection_t *fc, ngx_http_spdy_stream_t *stream); | |
+static ngx_inline ngx_int_t ngx_http_spdy_flow_control( | |
+ ngx_http_spdy_connection_t *sc, ngx_http_spdy_stream_t *stream); | |
+static void ngx_http_spdy_waiting_queue(ngx_http_spdy_connection_t *sc, | |
+ ngx_http_spdy_stream_t *stream); | |
+ | |
+static ngx_chain_t *ngx_http_spdy_filter_get_shadow( | |
+ ngx_http_spdy_stream_t *stream, ngx_buf_t *buf, off_t offset, off_t size); | |
+static ngx_http_spdy_out_frame_t *ngx_http_spdy_filter_get_data_frame( | |
+ ngx_http_spdy_stream_t *stream, size_t len, ngx_chain_t *first, | |
+ ngx_chain_t *last); | |
+ | |
+static ngx_int_t ngx_http_spdy_syn_frame_handler( | |
+ ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame); | |
+static ngx_int_t ngx_http_spdy_data_frame_handler( | |
+ ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame); | |
+static ngx_inline void ngx_http_spdy_handle_frame( | |
+ ngx_http_spdy_stream_t *stream, ngx_http_spdy_out_frame_t *frame); | |
+static ngx_inline void ngx_http_spdy_handle_stream( | |
+ ngx_http_spdy_connection_t *sc, ngx_http_spdy_stream_t *stream); | |
+ | |
+static void ngx_http_spdy_filter_cleanup(void *data); | |
+ | |
+static ngx_int_t ngx_http_spdy_filter_init(ngx_conf_t *cf); | |
+ | |
+ | |
+static ngx_http_module_t ngx_http_spdy_filter_module_ctx = { | |
+ NULL, /* preconfiguration */ | |
+ ngx_http_spdy_filter_init, /* postconfiguration */ | |
+ | |
+ NULL, /* create main configuration */ | |
+ NULL, /* init main configuration */ | |
+ | |
+ NULL, /* create server configuration */ | |
+ NULL, /* merge server configuration */ | |
+ | |
+ NULL, /* create location configuration */ | |
+ NULL /* merge location configuration */ | |
+}; | |
+ | |
+ | |
+ngx_module_t ngx_http_spdy_filter_module = { | |
+ NGX_MODULE_V1, | |
+ &ngx_http_spdy_filter_module_ctx, /* module context */ | |
+ NULL, /* module directives */ | |
+ NGX_HTTP_MODULE, /* module type */ | |
+ NULL, /* init master */ | |
+ NULL, /* init module */ | |
+ NULL, /* init process */ | |
+ NULL, /* init thread */ | |
+ NULL, /* exit thread */ | |
+ NULL, /* exit process */ | |
+ NULL, /* exit master */ | |
+ NGX_MODULE_V1_PADDING | |
+}; | |
+ | |
+ | |
+static ngx_http_output_header_filter_pt ngx_http_next_header_filter; | |
+ | |
+ | |
+static ngx_int_t | |
+ngx_http_spdy_header_filter(ngx_http_request_t *r) | |
+{ | |
+ int rc; | |
+ size_t len; | |
+ u_char *p, *buf, *last; | |
+ ngx_buf_t *b; | |
+ ngx_str_t host; | |
+ ngx_uint_t i, j, count, port; | |
+ ngx_chain_t *cl; | |
+ ngx_list_part_t *part, *pt; | |
+ ngx_table_elt_t *header, *h; | |
+ ngx_connection_t *c; | |
+ ngx_http_cleanup_t *cln; | |
+ ngx_http_core_loc_conf_t *clcf; | |
+ ngx_http_core_srv_conf_t *cscf; | |
+ ngx_http_spdy_stream_t *stream; | |
+ ngx_http_spdy_out_frame_t *frame; | |
+ ngx_http_spdy_connection_t *sc; | |
+ struct sockaddr_in *sin; | |
+#if (NGX_HAVE_INET6) | |
+ struct sockaddr_in6 *sin6; | |
+#endif | |
+ u_char addr[NGX_SOCKADDR_STRLEN]; | |
+ | |
+ if (!r->spdy_stream) { | |
+ return ngx_http_next_header_filter(r); | |
+ } | |
+ | |
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
+ "spdy header filter"); | |
+ | |
+ if (r->header_sent) { | |
+ return NGX_OK; | |
+ } | |
+ | |
+ r->header_sent = 1; | |
+ | |
+ if (r != r->main) { | |
+ return NGX_OK; | |
+ } | |
+ | |
+ c = r->connection; | |
+ | |
+ if (r->method == NGX_HTTP_HEAD) { | |
+ r->header_only = 1; | |
+ } | |
+ | |
+ switch (r->headers_out.status) { | |
+ | |
+ case NGX_HTTP_OK: | |
+ case NGX_HTTP_PARTIAL_CONTENT: | |
+ break; | |
+ | |
+ case NGX_HTTP_NOT_MODIFIED: | |
+ r->header_only = 1; | |
+ break; | |
+ | |
+ case NGX_HTTP_NO_CONTENT: | |
+ r->header_only = 1; | |
+ | |
+ ngx_str_null(&r->headers_out.content_type); | |
+ | |
+ r->headers_out.content_length = NULL; | |
+ r->headers_out.content_length_n = -1; | |
+ | |
+ /* fall through */ | |
+ | |
+ default: | |
+ r->headers_out.last_modified_time = -1; | |
+ r->headers_out.last_modified = NULL; | |
+ } | |
+ | |
+ len = NGX_SPDY_NV_NUM_SIZE | |
+ + ngx_http_spdy_nv_nsize(":version") | |
+ + ngx_http_spdy_nv_vsize("HTTP/1.1") | |
+ + ngx_http_spdy_nv_nsize(":status") | |
+ + (r->headers_out.status_line.len | |
+ ? NGX_SPDY_NV_VLEN_SIZE + r->headers_out.status_line.len | |
+ : ngx_http_spdy_nv_vsize("418")); | |
+ | |
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | |
+ | |
+ if (r->headers_out.server == NULL) { | |
+ len += ngx_http_spdy_nv_nsize("server"); | |
+ len += clcf->server_tokens ? ngx_http_spdy_nv_vsize(NGINX_VER) | |
+ : ngx_http_spdy_nv_vsize("nginx"); | |
+ } | |
+ | |
+ if (r->headers_out.date == NULL) { | |
+ len += ngx_http_spdy_nv_nsize("date") | |
+ + ngx_http_spdy_nv_vsize("Wed, 31 Dec 1986 10:00:00 GMT"); | |
+ } | |
+ | |
+ if (r->headers_out.content_type.len) { | |
+ len += ngx_http_spdy_nv_nsize("content-type") | |
+ + NGX_SPDY_NV_VLEN_SIZE + r->headers_out.content_type.len; | |
+ | |
+ if (r->headers_out.content_type_len == r->headers_out.content_type.len | |
+ && r->headers_out.charset.len) | |
+ { | |
+ len += sizeof("; charset=") - 1 + r->headers_out.charset.len; | |
+ } | |
+ } | |
+ | |
+ if (r->headers_out.content_length == NULL | |
+ && r->headers_out.content_length_n >= 0) | |
+ { | |
+ len += ngx_http_spdy_nv_nsize("content-length") | |
+ + NGX_SPDY_NV_VLEN_SIZE + NGX_OFF_T_LEN; | |
+ } | |
+ | |
+ if (r->headers_out.last_modified == NULL | |
+ && r->headers_out.last_modified_time != -1) | |
+ { | |
+ len += ngx_http_spdy_nv_nsize("last-modified") | |
+ + ngx_http_spdy_nv_vsize("Wed, 31 Dec 1986 10:00:00 GMT"); | |
+ } | |
+ | |
+ if (r->headers_out.location | |
+ && r->headers_out.location->value.len | |
+ && r->headers_out.location->value.data[0] == '/') | |
+ { | |
+ r->headers_out.location->hash = 0; | |
+ | |
+ if (clcf->server_name_in_redirect) { | |
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); | |
+ host = cscf->server_name; | |
+ | |
+ } else if (r->headers_in.server.len) { | |
+ host = r->headers_in.server; | |
+ | |
+ } else { | |
+ host.len = NGX_SOCKADDR_STRLEN; | |
+ host.data = addr; | |
+ | |
+ if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) { | |
+ return NGX_ERROR; | |
+ } | |
+ } | |
+ | |
+ switch (c->local_sockaddr->sa_family) { | |
+ | |
+#if (NGX_HAVE_INET6) | |
+ case AF_INET6: | |
+ sin6 = (struct sockaddr_in6 *) c->local_sockaddr; | |
+ port = ntohs(sin6->sin6_port); | |
+ break; | |
+#endif | |
+#if (NGX_HAVE_UNIX_DOMAIN) | |
+ case AF_UNIX: | |
+ port = 0; | |
+ break; | |
+#endif | |
+ default: /* AF_INET */ | |
+ sin = (struct sockaddr_in *) c->local_sockaddr; | |
+ port = ntohs(sin->sin_port); | |
+ break; | |
+ } | |
+ | |
+ len += ngx_http_spdy_nv_nsize("location") | |
+ + ngx_http_spdy_nv_vsize("https://") | |
+ + host.len | |
+ + r->headers_out.location->value.len; | |
+ | |
+ if (clcf->port_in_redirect) { | |
+ | |
+#if (NGX_HTTP_SSL) | |
+ if (c->ssl) | |
+ port = (port == 443) ? 0 : port; | |
+ else | |
+#endif | |
+ port = (port == 80) ? 0 : port; | |
+ | |
+ } else { | |
+ port = 0; | |
+ } | |
+ | |
+ if (port) { | |
+ len += sizeof(":65535") - 1; | |
+ } | |
+ | |
+ } else { | |
+ ngx_str_null(&host); | |
+ port = 0; | |
+ } | |
+ | |
+ part = &r->headers_out.headers.part; | |
+ header = part->elts; | |
+ | |
+ for (i = 0; /* void */; i++) { | |
+ | |
+ if (i >= part->nelts) { | |
+ if (part->next == NULL) { | |
+ break; | |
+ } | |
+ | |
+ part = part->next; | |
+ header = part->elts; | |
+ i = 0; | |
+ } | |
+ | |
+ if (header[i].hash == 0) { | |
+ continue; | |
+ } | |
+ | |
+ len += NGX_SPDY_NV_NLEN_SIZE + header[i].key.len | |
+ + NGX_SPDY_NV_VLEN_SIZE + header[i].value.len; | |
+ } | |
+ | |
+ buf = ngx_alloc(len, r->pool->log); | |
+ if (buf == NULL) { | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ last = buf + NGX_SPDY_NV_NUM_SIZE; | |
+ | |
+ last = ngx_http_spdy_nv_write_name(last, ":version"); | |
+ last = ngx_http_spdy_nv_write_val(last, "HTTP/1.1"); | |
+ | |
+ last = ngx_http_spdy_nv_write_name(last, ":status"); | |
+ | |
+ if (r->headers_out.status_line.len) { | |
+ last = ngx_http_spdy_nv_write_vlen(last, | |
+ r->headers_out.status_line.len); | |
+ last = ngx_cpymem(last, r->headers_out.status_line.data, | |
+ r->headers_out.status_line.len); | |
+ } else { | |
+ last = ngx_http_spdy_nv_write_vlen(last, 3); | |
+ last = ngx_sprintf(last, "%03ui", r->headers_out.status); | |
+ } | |
+ | |
+ count = 2; | |
+ | |
+ if (r->headers_out.server == NULL) { | |
+ last = ngx_http_spdy_nv_write_name(last, "server"); | |
+ last = clcf->server_tokens | |
+ ? ngx_http_spdy_nv_write_val(last, NGINX_VER) | |
+ : ngx_http_spdy_nv_write_val(last, "nginx"); | |
+ | |
+ count++; | |
+ } | |
+ | |
+ if (r->headers_out.date == NULL) { | |
+ last = ngx_http_spdy_nv_write_name(last, "date"); | |
+ | |
+ last = ngx_http_spdy_nv_write_vlen(last, ngx_cached_http_time.len); | |
+ | |
+ last = ngx_cpymem(last, ngx_cached_http_time.data, | |
+ ngx_cached_http_time.len); | |
+ | |
+ count++; | |
+ } | |
+ | |
+ if (r->headers_out.content_type.len) { | |
+ | |
+ last = ngx_http_spdy_nv_write_name(last, "content-type"); | |
+ | |
+ p = last + NGX_SPDY_NV_VLEN_SIZE; | |
+ | |
+ last = ngx_cpymem(p, r->headers_out.content_type.data, | |
+ r->headers_out.content_type.len); | |
+ | |
+ if (r->headers_out.content_type_len == r->headers_out.content_type.len | |
+ && r->headers_out.charset.len) | |
+ { | |
+ last = ngx_cpymem(last, "; charset=", sizeof("; charset=") - 1); | |
+ | |
+ last = ngx_cpymem(last, r->headers_out.charset.data, | |
+ r->headers_out.charset.len); | |
+ | |
+ /* update r->headers_out.content_type for possible logging */ | |
+ | |
+ r->headers_out.content_type.len = last - p; | |
+ r->headers_out.content_type.data = p; | |
+ } | |
+ | |
+ (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE, | |
+ r->headers_out.content_type.len); | |
+ | |
+ count++; | |
+ } | |
+ | |
+ if (r->headers_out.content_length == NULL | |
+ && r->headers_out.content_length_n >= 0) | |
+ { | |
+ last = ngx_http_spdy_nv_write_name(last, "content-length"); | |
+ | |
+ p = last + NGX_SPDY_NV_VLEN_SIZE; | |
+ | |
+ last = ngx_sprintf(p, "%O", r->headers_out.content_length_n); | |
+ | |
+ (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE, | |
+ last - p); | |
+ | |
+ count++; | |
+ } | |
+ | |
+ if (r->headers_out.last_modified == NULL | |
+ && r->headers_out.last_modified_time != -1) | |
+ { | |
+ last = ngx_http_spdy_nv_write_name(last, "last-modified"); | |
+ | |
+ p = last + NGX_SPDY_NV_VLEN_SIZE; | |
+ | |
+ last = ngx_http_time(p, r->headers_out.last_modified_time); | |
+ | |
+ (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE, | |
+ last - p); | |
+ | |
+ count++; | |
+ } | |
+ | |
+ if (host.data) { | |
+ | |
+ last = ngx_http_spdy_nv_write_name(last, "location"); | |
+ | |
+ p = last + NGX_SPDY_NV_VLEN_SIZE; | |
+ | |
+ last = ngx_cpymem(p, "http", sizeof("http") - 1); | |
+ | |
+#if (NGX_HTTP_SSL) | |
+ if (c->ssl) { | |
+ *last++ ='s'; | |
+ } | |
+#endif | |
+ | |
+ *last++ = ':'; *last++ = '/'; *last++ = '/'; | |
+ | |
+ last = ngx_cpymem(last, host.data, host.len); | |
+ | |
+ if (port) { | |
+ last = ngx_sprintf(last, ":%ui", port); | |
+ } | |
+ | |
+ last = ngx_cpymem(last, r->headers_out.location->value.data, | |
+ r->headers_out.location->value.len); | |
+ | |
+ /* update r->headers_out.location->value for possible logging */ | |
+ | |
+ r->headers_out.location->value.len = last - p; | |
+ r->headers_out.location->value.data = p; | |
+ ngx_str_set(&r->headers_out.location->key, "location"); | |
+ | |
+ (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE, | |
+ r->headers_out.location->value.len); | |
+ | |
+ count++; | |
+ } | |
+ | |
+ part = &r->headers_out.headers.part; | |
+ header = part->elts; | |
+ | |
+ for (i = 0; /* void */; i++) { | |
+ | |
+ if (i >= part->nelts) { | |
+ if (part->next == NULL) { | |
+ break; | |
+ } | |
+ | |
+ part = part->next; | |
+ header = part->elts; | |
+ i = 0; | |
+ } | |
+ | |
+ if (header[i].hash == 0 || header[i].hash == 2) { | |
+ continue; | |
+ } | |
+ | |
+ last = ngx_http_spdy_nv_write_nlen(last, header[i].key.len); | |
+ | |
+ ngx_strlow(last, header[i].key.data, header[i].key.len); | |
+ last += header[i].key.len; | |
+ | |
+ p = last + NGX_SPDY_NV_VLEN_SIZE; | |
+ | |
+ last = ngx_cpymem(p, header[i].value.data, header[i].value.len); | |
+ | |
+ pt = part; | |
+ h = header; | |
+ | |
+ for (j = i + 1; /* void */; j++) { | |
+ | |
+ if (j >= pt->nelts) { | |
+ if (pt->next == NULL) { | |
+ break; | |
+ } | |
+ | |
+ pt = pt->next; | |
+ h = pt->elts; | |
+ j = 0; | |
+ } | |
+ | |
+ if (h[j].hash == 0 || h[j].hash == 2 | |
+ || h[j].key.len != header[i].key.len | |
+ || ngx_strncasecmp(header[i].key.data, h[j].key.data, | |
+ header[i].key.len)) | |
+ { | |
+ continue; | |
+ } | |
+ | |
+ if (h[j].value.len) { | |
+ if (last != p) { | |
+ *last++ = '\0'; | |
+ } | |
+ | |
+ last = ngx_cpymem(last, h[j].value.data, h[j].value.len); | |
+ } | |
+ | |
+ h[j].hash = 2; | |
+ } | |
+ | |
+ (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE, | |
+ last - p); | |
+ | |
+ count++; | |
+ } | |
+ | |
+ (void) ngx_http_spdy_nv_write_num(buf, count); | |
+ | |
+ stream = r->spdy_stream; | |
+ sc = stream->connection; | |
+ | |
+ len = last - buf; | |
+ | |
+ b = ngx_create_temp_buf(r->pool, NGX_SPDY_FRAME_HEADER_SIZE | |
+ + NGX_SPDY_SYN_REPLY_SIZE | |
+ + deflateBound(&sc->zstream_out, len)); | |
+ if (b == NULL) { | |
+ ngx_free(buf); | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ b->last += NGX_SPDY_FRAME_HEADER_SIZE + NGX_SPDY_SYN_REPLY_SIZE; | |
+ | |
+ sc->zstream_out.next_in = buf; | |
+ sc->zstream_out.avail_in = len; | |
+ sc->zstream_out.next_out = b->last; | |
+ sc->zstream_out.avail_out = b->end - b->last; | |
+ | |
+ rc = deflate(&sc->zstream_out, Z_SYNC_FLUSH); | |
+ | |
+ ngx_free(buf); | |
+ | |
+ if (rc != Z_OK) { | |
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0, "deflate() failed: %d", rc); | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
+ "spdy deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", | |
+ sc->zstream_out.next_in, sc->zstream_out.next_out, | |
+ sc->zstream_out.avail_in, sc->zstream_out.avail_out, | |
+ rc); | |
+ | |
+ b->last = sc->zstream_out.next_out; | |
+ | |
+ p = b->pos; | |
+ p = ngx_spdy_frame_write_head(p, NGX_SPDY_SYN_REPLY); | |
+ | |
+ len = b->last - b->pos; | |
+ | |
+ r->header_size = len; | |
+ | |
+ len -= NGX_SPDY_FRAME_HEADER_SIZE; | |
+ | |
+ if (r->header_only) { | |
+ b->last_buf = 1; | |
+ p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_FIN, len); | |
+ | |
+ } else { | |
+ p = ngx_spdy_frame_write_flags_and_len(p, 0, len); | |
+ } | |
+ | |
+ (void) ngx_spdy_frame_write_sid(p, stream->id); | |
+ | |
+ cl = ngx_alloc_chain_link(r->pool); | |
+ if (cl == NULL) { | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ cl->buf = b; | |
+ cl->next = NULL; | |
+ | |
+ frame = ngx_palloc(r->pool, sizeof(ngx_http_spdy_out_frame_t)); | |
+ if (frame == NULL) { | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ frame->first = cl; | |
+ frame->last = cl; | |
+ frame->handler = ngx_http_spdy_syn_frame_handler; | |
+ frame->stream = stream; | |
+ frame->length = len; | |
+ frame->priority = stream->priority; | |
+ frame->blocked = 1; | |
+ frame->fin = r->header_only; | |
+ | |
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0, | |
+ "spdy:%ui create SYN_REPLY frame %p: len:%uz", | |
+ stream->id, frame, frame->length); | |
+ | |
+ ngx_http_spdy_queue_blocked_frame(sc, frame); | |
+ | |
+ cln = ngx_http_cleanup_add(r, 0); | |
+ if (cln == NULL) { | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ cln->handler = ngx_http_spdy_filter_cleanup; | |
+ cln->data = stream; | |
+ | |
+ stream->queued = 1; | |
+ | |
+ c->send_chain = ngx_http_spdy_send_chain; | |
+ c->need_last_buf = 1; | |
+ | |
+ return ngx_http_spdy_filter_send(c, stream); | |
+} | |
+ | |
+ | |
+static ngx_chain_t * | |
+ngx_http_spdy_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit) | |
+{ | |
+ off_t size, offset; | |
+ size_t rest, frame_size; | |
+ ngx_chain_t *cl, *out, **ln; | |
+ ngx_http_request_t *r; | |
+ ngx_http_spdy_stream_t *stream; | |
+ ngx_http_spdy_loc_conf_t *slcf; | |
+ ngx_http_spdy_out_frame_t *frame; | |
+ ngx_http_spdy_connection_t *sc; | |
+ | |
+ r = fc->data; | |
+ stream = r->spdy_stream; | |
+ | |
+#if (NGX_SUPPRESS_WARN) | |
+ size = 0; | |
+#endif | |
+ | |
+ while (in) { | |
+ size = ngx_buf_size(in->buf); | |
+ | |
+ if (size || in->buf->last_buf) { | |
+ break; | |
+ } | |
+ | |
+ in = in->next; | |
+ } | |
+ | |
+ if (in == NULL) { | |
+ | |
+ if (stream->queued) { | |
+ fc->write->delayed = 1; | |
+ } else { | |
+ fc->buffered &= ~NGX_SPDY_BUFFERED; | |
+ } | |
+ | |
+ return NULL; | |
+ } | |
+ | |
+ sc = stream->connection; | |
+ | |
+ if (size && ngx_http_spdy_flow_control(sc, stream) == NGX_DECLINED) { | |
+ fc->write->delayed = 1; | |
+ return in; | |
+ } | |
+ | |
+ if (limit == 0 || limit > (off_t) sc->send_window) { | |
+ limit = sc->send_window; | |
+ } | |
+ | |
+ if (limit > stream->send_window) { | |
+ limit = (stream->send_window > 0) ? stream->send_window : 0; | |
+ } | |
+ | |
+ if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) { | |
+ cl = ngx_alloc_chain_link(r->pool); | |
+ if (cl == NULL) { | |
+ return NGX_CHAIN_ERROR; | |
+ } | |
+ | |
+ cl->buf = in->buf; | |
+ in->buf = cl->buf->shadow; | |
+ | |
+ offset = ngx_buf_in_memory(in->buf) | |
+ ? (cl->buf->pos - in->buf->pos) | |
+ : (cl->buf->file_pos - in->buf->file_pos); | |
+ | |
+ cl->next = stream->free_bufs; | |
+ stream->free_bufs = cl; | |
+ | |
+ } else { | |
+ offset = 0; | |
+ } | |
+ | |
+#if (NGX_SUPPRESS_WARN) | |
+ cl = NULL; | |
+#endif | |
+ | |
+ slcf = ngx_http_get_module_loc_conf(r, ngx_http_spdy_module); | |
+ | |
+ frame_size = (limit <= (off_t) slcf->chunk_size) ? (size_t) limit | |
+ : slcf->chunk_size; | |
+ | |
+ for ( ;; ) { | |
+ ln = &out; | |
+ rest = frame_size; | |
+ | |
+ while ((off_t) rest >= size) { | |
+ | |
+ if (offset) { | |
+ cl = ngx_http_spdy_filter_get_shadow(stream, in->buf, | |
+ offset, size); | |
+ if (cl == NULL) { | |
+ return NGX_CHAIN_ERROR; | |
+ } | |
+ | |
+ offset = 0; | |
+ | |
+ } else { | |
+ cl = ngx_alloc_chain_link(r->pool); | |
+ if (cl == NULL) { | |
+ return NGX_CHAIN_ERROR; | |
+ } | |
+ | |
+ cl->buf = in->buf; | |
+ } | |
+ | |
+ *ln = cl; | |
+ ln = &cl->next; | |
+ | |
+ rest -= (size_t) size; | |
+ in = in->next; | |
+ | |
+ if (in == NULL) { | |
+ frame_size -= rest; | |
+ rest = 0; | |
+ break; | |
+ } | |
+ | |
+ size = ngx_buf_size(in->buf); | |
+ } | |
+ | |
+ if (rest) { | |
+ cl = ngx_http_spdy_filter_get_shadow(stream, in->buf, | |
+ offset, rest); | |
+ if (cl == NULL) { | |
+ return NGX_CHAIN_ERROR; | |
+ } | |
+ | |
+ cl->buf->flush = 0; | |
+ cl->buf->last_buf = 0; | |
+ | |
+ *ln = cl; | |
+ | |
+ offset += rest; | |
+ size -= rest; | |
+ } | |
+ | |
+ frame = ngx_http_spdy_filter_get_data_frame(stream, frame_size, | |
+ out, cl); | |
+ if (frame == NULL) { | |
+ return NGX_CHAIN_ERROR; | |
+ } | |
+ | |
+ ngx_http_spdy_queue_frame(sc, frame); | |
+ | |
+ sc->send_window -= frame_size; | |
+ | |
+ stream->send_window -= frame_size; | |
+ stream->queued++; | |
+ | |
+ if (in == NULL) { | |
+ break; | |
+ } | |
+ | |
+ limit -= frame_size; | |
+ | |
+ if (limit == 0) { | |
+ break; | |
+ } | |
+ | |
+ if (limit < (off_t) slcf->chunk_size) { | |
+ frame_size = (size_t) limit; | |
+ } | |
+ } | |
+ | |
+ if (offset) { | |
+ cl = ngx_http_spdy_filter_get_shadow(stream, in->buf, offset, size); | |
+ if (cl == NULL) { | |
+ return NGX_CHAIN_ERROR; | |
+ } | |
+ | |
+ in->buf = cl->buf; | |
+ ngx_free_chain(r->pool, cl); | |
+ } | |
+ | |
+ if (ngx_http_spdy_filter_send(fc, stream) == NGX_ERROR) { | |
+ return NGX_CHAIN_ERROR; | |
+ } | |
+ | |
+ if (in && ngx_http_spdy_flow_control(sc, stream) == NGX_DECLINED) { | |
+ fc->write->delayed = 1; | |
+ } | |
+ | |
+ return in; | |
+} | |
+ | |
+ | |
+static ngx_chain_t * | |
+ngx_http_spdy_filter_get_shadow(ngx_http_spdy_stream_t *stream, ngx_buf_t *buf, | |
+ off_t offset, off_t size) | |
+{ | |
+ ngx_buf_t *chunk; | |
+ ngx_chain_t *cl; | |
+ | |
+ cl = ngx_chain_get_free_buf(stream->request->pool, &stream->free_bufs); | |
+ if (cl == NULL) { | |
+ return NULL; | |
+ } | |
+ | |
+ chunk = cl->buf; | |
+ | |
+ ngx_memcpy(chunk, buf, sizeof(ngx_buf_t)); | |
+ | |
+ chunk->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow; | |
+ chunk->shadow = buf; | |
+ | |
+ if (ngx_buf_in_memory(chunk)) { | |
+ chunk->pos += offset; | |
+ chunk->last = chunk->pos + size; | |
+ } | |
+ | |
+ if (chunk->in_file) { | |
+ chunk->file_pos += offset; | |
+ chunk->file_last = chunk->file_pos + size; | |
+ } | |
+ | |
+ return cl; | |
+} | |
+ | |
+ | |
+static ngx_http_spdy_out_frame_t * | |
+ngx_http_spdy_filter_get_data_frame(ngx_http_spdy_stream_t *stream, | |
+ size_t len, ngx_chain_t *first, ngx_chain_t *last) | |
+{ | |
+ u_char *p; | |
+ ngx_buf_t *buf; | |
+ ngx_uint_t flags; | |
+ ngx_chain_t *cl; | |
+ ngx_http_spdy_out_frame_t *frame; | |
+ | |
+ | |
+ frame = stream->free_frames; | |
+ | |
+ if (frame) { | |
+ stream->free_frames = frame->next; | |
+ | |
+ } else { | |
+ frame = ngx_palloc(stream->request->pool, | |
+ sizeof(ngx_http_spdy_out_frame_t)); | |
+ if (frame == NULL) { | |
+ return NULL; | |
+ } | |
+ } | |
+ | |
+ flags = last->buf->last_buf ? NGX_SPDY_FLAG_FIN : 0; | |
+ | |
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0, | |
+ "spdy:%ui create DATA frame %p: len:%uz flags:%ui", | |
+ stream->id, frame, len, flags); | |
+ | |
+ cl = ngx_chain_get_free_buf(stream->request->pool, | |
+ &stream->free_data_headers); | |
+ if (cl == NULL) { | |
+ return NULL; | |
+ } | |
+ | |
+ buf = cl->buf; | |
+ | |
+ if (buf->start) { | |
+ p = buf->start; | |
+ buf->pos = p; | |
+ | |
+ p += NGX_SPDY_SID_SIZE; | |
+ | |
+ (void) ngx_spdy_frame_write_flags_and_len(p, flags, len); | |
+ | |
+ } else { | |
+ p = ngx_palloc(stream->request->pool, NGX_SPDY_FRAME_HEADER_SIZE); | |
+ if (p == NULL) { | |
+ return NULL; | |
+ } | |
+ | |
+ buf->pos = p; | |
+ buf->start = p; | |
+ | |
+ p = ngx_spdy_frame_write_sid(p, stream->id); | |
+ p = ngx_spdy_frame_write_flags_and_len(p, flags, len); | |
+ | |
+ buf->last = p; | |
+ buf->end = p; | |
+ | |
+ buf->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_get_data_frame; | |
+ buf->memory = 1; | |
+ } | |
+ | |
+ cl->next = first; | |
+ first = cl; | |
+ | |
+ last->buf->flush = 1; | |
+ | |
+ frame->first = first; | |
+ frame->last = last; | |
+ frame->handler = ngx_http_spdy_data_frame_handler; | |
+ frame->stream = stream; | |
+ frame->length = len; | |
+ frame->priority = stream->priority; | |
+ frame->blocked = 0; | |
+ frame->fin = last->buf->last_buf; | |
+ | |
+ return frame; | |
+} | |
+ | |
+ | |
+static ngx_inline ngx_int_t | |
+ngx_http_spdy_filter_send(ngx_connection_t *fc, ngx_http_spdy_stream_t *stream) | |
+{ | |
+ stream->blocked = 1; | |
+ | |
+ if (ngx_http_spdy_send_output_queue(stream->connection) == NGX_ERROR) { | |
+ fc->error = 1; | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ stream->blocked = 0; | |
+ | |
+ if (stream->queued) { | |
+ fc->buffered |= NGX_SPDY_BUFFERED; | |
+ fc->write->delayed = 1; | |
+ return NGX_AGAIN; | |
+ } | |
+ | |
+ fc->buffered &= ~NGX_SPDY_BUFFERED; | |
+ | |
+ return NGX_OK; | |
+} | |
+ | |
+ | |
+static ngx_inline ngx_int_t | |
+ngx_http_spdy_flow_control(ngx_http_spdy_connection_t *sc, | |
+ ngx_http_spdy_stream_t *stream) | |
+{ | |
+ if (stream->send_window <= 0) { | |
+ stream->exhausted = 1; | |
+ return NGX_DECLINED; | |
+ } | |
+ | |
+ if (sc->send_window == 0) { | |
+ ngx_http_spdy_waiting_queue(sc, stream); | |
+ return NGX_DECLINED; | |
+ } | |
+ | |
+ return NGX_OK; | |
+} | |
+ | |
+ | |
+static void | |
+ngx_http_spdy_waiting_queue(ngx_http_spdy_connection_t *sc, | |
+ ngx_http_spdy_stream_t *stream) | |
+{ | |
+ ngx_queue_t *q; | |
+ ngx_http_spdy_stream_t *s; | |
+ | |
+ if (stream->handled) { | |
+ return; | |
+ } | |
+ | |
+ stream->handled = 1; | |
+ | |
+ for (q = ngx_queue_last(&sc->waiting); | |
+ q != ngx_queue_sentinel(&sc->waiting); | |
+ q = ngx_queue_prev(q)) | |
+ { | |
+ s = ngx_queue_data(q, ngx_http_spdy_stream_t, queue); | |
+ | |
+ /* | |
+ * NB: higher values represent lower priorities. | |
+ */ | |
+ if (stream->priority >= s->priority) { | |
+ break; | |
+ } | |
+ } | |
+ | |
+ ngx_queue_insert_after(q, &stream->queue); | |
+} | |
+ | |
+ | |
+static ngx_int_t | |
+ngx_http_spdy_syn_frame_handler(ngx_http_spdy_connection_t *sc, | |
+ ngx_http_spdy_out_frame_t *frame) | |
+{ | |
+ ngx_buf_t *buf; | |
+ ngx_http_spdy_stream_t *stream; | |
+ | |
+ buf = frame->first->buf; | |
+ | |
+ if (buf->pos != buf->last) { | |
+ return NGX_AGAIN; | |
+ } | |
+ | |
+ stream = frame->stream; | |
+ | |
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
+ "spdy:%ui SYN_REPLY frame %p was sent", stream->id, frame); | |
+ | |
+ ngx_free_chain(stream->request->pool, frame->first); | |
+ | |
+ ngx_http_spdy_handle_frame(stream, frame); | |
+ | |
+ ngx_http_spdy_handle_stream(sc, stream); | |
+ | |
+ return NGX_OK; | |
+} | |
+ | |
+ | |
+static ngx_int_t | |
+ngx_http_spdy_data_frame_handler(ngx_http_spdy_connection_t *sc, | |
+ ngx_http_spdy_out_frame_t *frame) | |
+{ | |
+ ngx_buf_t *buf; | |
+ ngx_chain_t *cl, *ln; | |
+ ngx_http_spdy_stream_t *stream; | |
+ | |
+ stream = frame->stream; | |
+ | |
+ cl = frame->first; | |
+ | |
+ if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_data_frame) { | |
+ | |
+ if (cl->buf->pos != cl->buf->last) { | |
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
+ "spdy:%ui DATA frame %p was sent partially", | |
+ stream->id, frame); | |
+ | |
+ return NGX_AGAIN; | |
+ } | |
+ | |
+ ln = cl->next; | |
+ | |
+ cl->next = stream->free_data_headers; | |
+ stream->free_data_headers = cl; | |
+ | |
+ if (cl == frame->last) { | |
+ goto done; | |
+ } | |
+ | |
+ cl = ln; | |
+ } | |
+ | |
+ for ( ;; ) { | |
+ if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) { | |
+ buf = cl->buf->shadow; | |
+ | |
+ if (ngx_buf_in_memory(buf)) { | |
+ buf->pos = cl->buf->pos; | |
+ } | |
+ | |
+ if (buf->in_file) { | |
+ buf->file_pos = cl->buf->file_pos; | |
+ } | |
+ } | |
+ | |
+ if (ngx_buf_size(cl->buf) != 0) { | |
+ | |
+ if (cl != frame->first) { | |
+ frame->first = cl; | |
+ ngx_http_spdy_handle_stream(sc, stream); | |
+ } | |
+ | |
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
+ "spdy:%ui DATA frame %p was sent partially", | |
+ stream->id, frame); | |
+ | |
+ return NGX_AGAIN; | |
+ } | |
+ | |
+ ln = cl->next; | |
+ | |
+ if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) { | |
+ cl->next = stream->free_bufs; | |
+ stream->free_bufs = cl; | |
+ | |
+ } else { | |
+ ngx_free_chain(stream->request->pool, cl); | |
+ } | |
+ | |
+ if (cl == frame->last) { | |
+ goto done; | |
+ } | |
+ | |
+ cl = ln; | |
+ } | |
+ | |
+done: | |
+ | |
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
+ "spdy:%ui DATA frame %p was sent", stream->id, frame); | |
+ | |
+ stream->request->header_size += NGX_SPDY_FRAME_HEADER_SIZE; | |
+ | |
+ ngx_http_spdy_handle_frame(stream, frame); | |
+ | |
+ ngx_http_spdy_handle_stream(sc, stream); | |
+ | |
+ return NGX_OK; | |
+} | |
+ | |
+ | |
+static ngx_inline void | |
+ngx_http_spdy_handle_frame(ngx_http_spdy_stream_t *stream, | |
+ ngx_http_spdy_out_frame_t *frame) | |
+{ | |
+ ngx_http_request_t *r; | |
+ | |
+ r = stream->request; | |
+ | |
+ r->connection->sent += NGX_SPDY_FRAME_HEADER_SIZE + frame->length; | |
+ | |
+ if (frame->fin) { | |
+ stream->out_closed = 1; | |
+ } | |
+ | |
+ frame->next = stream->free_frames; | |
+ stream->free_frames = frame; | |
+ | |
+ stream->queued--; | |
+} | |
+ | |
+ | |
+static ngx_inline void | |
+ngx_http_spdy_handle_stream(ngx_http_spdy_connection_t *sc, | |
+ ngx_http_spdy_stream_t *stream) | |
+{ | |
+ ngx_event_t *wev; | |
+ | |
+ if (stream->handled || stream->blocked || stream->exhausted) { | |
+ return; | |
+ } | |
+ | |
+ wev = stream->request->connection->write; | |
+ | |
+ /* | |
+ * This timer can only be set if the stream was delayed because of rate | |
+ * limit. In that case the event should be triggered by the timer. | |
+ */ | |
+ | |
+ if (!wev->timer_set) { | |
+ wev->delayed = 0; | |
+ | |
+ stream->handled = 1; | |
+ ngx_queue_insert_tail(&sc->posted, &stream->queue); | |
+ } | |
+} | |
+ | |
+ | |
+static void | |
+ngx_http_spdy_filter_cleanup(void *data) | |
+{ | |
+ ngx_http_spdy_stream_t *stream = data; | |
+ | |
+ size_t delta; | |
+ ngx_http_spdy_out_frame_t *frame, **fn; | |
+ ngx_http_spdy_connection_t *sc; | |
+ | |
+ if (stream->handled) { | |
+ stream->handled = 0; | |
+ ngx_queue_remove(&stream->queue); | |
+ } | |
+ | |
+ if (stream->queued == 0) { | |
+ return; | |
+ } | |
+ | |
+ delta = 0; | |
+ sc = stream->connection; | |
+ fn = &sc->last_out; | |
+ | |
+ for ( ;; ) { | |
+ frame = *fn; | |
+ | |
+ if (frame == NULL) { | |
+ break; | |
+ } | |
+ | |
+ if (frame->stream == stream && !frame->blocked) { | |
+ *fn = frame->next; | |
+ | |
+ delta += frame->length; | |
+ | |
+ if (--stream->queued == 0) { | |
+ break; | |
+ } | |
+ | |
+ continue; | |
+ } | |
+ | |
+ fn = &frame->next; | |
+ } | |
+ | |
+ if (sc->send_window == 0 && delta && !ngx_queue_empty(&sc->waiting)) { | |
+ ngx_queue_add(&sc->posted, &sc->waiting); | |
+ ngx_queue_init(&sc->waiting); | |
+ } | |
+ | |
+ sc->send_window += delta; | |
+} | |
+ | |
+ | |
+static ngx_int_t | |
+ngx_http_spdy_filter_init(ngx_conf_t *cf) | |
+{ | |
+ ngx_http_next_header_filter = ngx_http_top_header_filter; | |
+ ngx_http_top_header_filter = ngx_http_spdy_header_filter; | |
+ | |
+ return NGX_OK; | |
+} | |
diff --git a/src/http/ngx_http_spdy_module.c b/src/http/ngx_http_spdy_module.c | |
new file mode 100644 | |
index 0000000..5178a36 | |
--- /dev/null | |
+++ b/src/http/ngx_http_spdy_module.c | |
@@ -0,0 +1,408 @@ | |
+ | |
+/* | |
+ * Copyright (C) Nginx, Inc. | |
+ * Copyright (C) Valentin V. Bartenev | |
+ */ | |
+ | |
+ | |
+#include <ngx_config.h> | |
+#include <ngx_core.h> | |
+#include <ngx_http.h> | |
+#include <ngx_http_spdy_module.h> | |
+ | |
+ | |
+static ngx_int_t ngx_http_spdy_add_variables(ngx_conf_t *cf); | |
+ | |
+static ngx_int_t ngx_http_spdy_variable(ngx_http_request_t *r, | |
+ ngx_http_variable_value_t *v, uintptr_t data); | |
+static ngx_int_t ngx_http_spdy_request_priority_variable(ngx_http_request_t *r, | |
+ ngx_http_variable_value_t *v, uintptr_t data); | |
+ | |
+static ngx_int_t ngx_http_spdy_module_init(ngx_cycle_t *cycle); | |
+ | |
+static void *ngx_http_spdy_create_main_conf(ngx_conf_t *cf); | |
+static char *ngx_http_spdy_init_main_conf(ngx_conf_t *cf, void *conf); | |
+static void *ngx_http_spdy_create_srv_conf(ngx_conf_t *cf); | |
+static char *ngx_http_spdy_merge_srv_conf(ngx_conf_t *cf, void *parent, | |
+ void *child); | |
+static void *ngx_http_spdy_create_loc_conf(ngx_conf_t *cf); | |
+static char *ngx_http_spdy_merge_loc_conf(ngx_conf_t *cf, void *parent, | |
+ void *child); | |
+ | |
+static char *ngx_http_spdy_recv_buffer_size(ngx_conf_t *cf, void *post, | |
+ void *data); | |
+static char *ngx_http_spdy_pool_size(ngx_conf_t *cf, void *post, void *data); | |
+static char *ngx_http_spdy_streams_index_mask(ngx_conf_t *cf, void *post, | |
+ void *data); | |
+static char *ngx_http_spdy_chunk_size(ngx_conf_t *cf, void *post, void *data); | |
+ | |
+ | |
+static ngx_conf_num_bounds_t ngx_http_spdy_headers_comp_bounds = { | |
+ ngx_conf_check_num_bounds, 0, 9 | |
+}; | |
+ | |
+static ngx_conf_post_t ngx_http_spdy_recv_buffer_size_post = | |
+ { ngx_http_spdy_recv_buffer_size }; | |
+static ngx_conf_post_t ngx_http_spdy_pool_size_post = | |
+ { ngx_http_spdy_pool_size }; | |
+static ngx_conf_post_t ngx_http_spdy_streams_index_mask_post = | |
+ { ngx_http_spdy_streams_index_mask }; | |
+static ngx_conf_post_t ngx_http_spdy_chunk_size_post = | |
+ { ngx_http_spdy_chunk_size }; | |
+ | |
+ | |
+static ngx_command_t ngx_http_spdy_commands[] = { | |
+ | |
+ { ngx_string("spdy_recv_buffer_size"), | |
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, | |
+ ngx_conf_set_size_slot, | |
+ NGX_HTTP_MAIN_CONF_OFFSET, | |
+ offsetof(ngx_http_spdy_main_conf_t, recv_buffer_size), | |
+ &ngx_http_spdy_recv_buffer_size_post }, | |
+ | |
+ { ngx_string("spdy_pool_size"), | |
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, | |
+ ngx_conf_set_size_slot, | |
+ NGX_HTTP_SRV_CONF_OFFSET, | |
+ offsetof(ngx_http_spdy_srv_conf_t, pool_size), | |
+ &ngx_http_spdy_pool_size_post }, | |
+ | |
+ { ngx_string("spdy_max_concurrent_streams"), | |
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, | |
+ ngx_conf_set_num_slot, | |
+ NGX_HTTP_SRV_CONF_OFFSET, | |
+ offsetof(ngx_http_spdy_srv_conf_t, concurrent_streams), | |
+ NULL }, | |
+ | |
+ { ngx_string("spdy_streams_index_size"), | |
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, | |
+ ngx_conf_set_num_slot, | |
+ NGX_HTTP_SRV_CONF_OFFSET, | |
+ offsetof(ngx_http_spdy_srv_conf_t, streams_index_mask), | |
+ &ngx_http_spdy_streams_index_mask_post }, | |
+ | |
+ { ngx_string("spdy_recv_timeout"), | |
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, | |
+ ngx_conf_set_msec_slot, | |
+ NGX_HTTP_SRV_CONF_OFFSET, | |
+ offsetof(ngx_http_spdy_srv_conf_t, recv_timeout), | |
+ NULL }, | |
+ | |
+ { ngx_string("spdy_keepalive_timeout"), | |
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, | |
+ ngx_conf_set_msec_slot, | |
+ NGX_HTTP_SRV_CONF_OFFSET, | |
+ offsetof(ngx_http_spdy_srv_conf_t, keepalive_timeout), | |
+ NULL }, | |
+ | |
+ { ngx_string("spdy_headers_comp"), | |
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, | |
+ ngx_conf_set_num_slot, | |
+ NGX_HTTP_SRV_CONF_OFFSET, | |
+ offsetof(ngx_http_spdy_srv_conf_t, headers_comp), | |
+ &ngx_http_spdy_headers_comp_bounds }, | |
+ | |
+ { ngx_string("spdy_chunk_size"), | |
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
+ ngx_conf_set_size_slot, | |
+ NGX_HTTP_LOC_CONF_OFFSET, | |
+ offsetof(ngx_http_spdy_loc_conf_t, chunk_size), | |
+ &ngx_http_spdy_chunk_size_post }, | |
+ | |
+ ngx_null_command | |
+}; | |
+ | |
+ | |
+static ngx_http_module_t ngx_http_spdy_module_ctx = { | |
+ ngx_http_spdy_add_variables, /* preconfiguration */ | |
+ NULL, /* postconfiguration */ | |
+ | |
+ ngx_http_spdy_create_main_conf, /* create main configuration */ | |
+ ngx_http_spdy_init_main_conf, /* init main configuration */ | |
+ | |
+ ngx_http_spdy_create_srv_conf, /* create server configuration */ | |
+ ngx_http_spdy_merge_srv_conf, /* merge server configuration */ | |
+ | |
+ ngx_http_spdy_create_loc_conf, /* create location configuration */ | |
+ ngx_http_spdy_merge_loc_conf /* merge location configuration */ | |
+}; | |
+ | |
+ | |
+ngx_module_t ngx_http_spdy_module = { | |
+ NGX_MODULE_V1, | |
+ &ngx_http_spdy_module_ctx, /* module context */ | |
+ ngx_http_spdy_commands, /* module directives */ | |
+ NGX_HTTP_MODULE, /* module type */ | |
+ NULL, /* init master */ | |
+ ngx_http_spdy_module_init, /* init module */ | |
+ NULL, /* init process */ | |
+ NULL, /* init thread */ | |
+ NULL, /* exit thread */ | |
+ NULL, /* exit process */ | |
+ NULL, /* exit master */ | |
+ NGX_MODULE_V1_PADDING | |
+}; | |
+ | |
+ | |
+static ngx_http_variable_t ngx_http_spdy_vars[] = { | |
+ | |
+ { ngx_string("spdy"), NULL, | |
+ ngx_http_spdy_variable, 0, 0, 0 }, | |
+ | |
+ { ngx_string("spdy_request_priority"), NULL, | |
+ ngx_http_spdy_request_priority_variable, 0, 0, 0 }, | |
+ | |
+ { ngx_null_string, NULL, NULL, 0, 0, 0 } | |
+}; | |
+ | |
+ | |
+static ngx_int_t | |
+ngx_http_spdy_add_variables(ngx_conf_t *cf) | |
+{ | |
+ ngx_http_variable_t *var, *v; | |
+ | |
+ for (v = ngx_http_spdy_vars; v->name.len; v++) { | |
+ var = ngx_http_add_variable(cf, &v->name, v->flags); | |
+ if (var == NULL) { | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ var->get_handler = v->get_handler; | |
+ var->data = v->data; | |
+ } | |
+ | |
+ return NGX_OK; | |
+} | |
+ | |
+ | |
+static ngx_int_t | |
+ngx_http_spdy_variable(ngx_http_request_t *r, | |
+ ngx_http_variable_value_t *v, uintptr_t data) | |
+{ | |
+ if (r->spdy_stream) { | |
+ v->len = sizeof("3.1") - 1; | |
+ v->valid = 1; | |
+ v->no_cacheable = 0; | |
+ v->not_found = 0; | |
+ v->data = (u_char *) "3.1"; | |
+ | |
+ return NGX_OK; | |
+ } | |
+ | |
+ *v = ngx_http_variable_null_value; | |
+ | |
+ return NGX_OK; | |
+} | |
+ | |
+ | |
+static ngx_int_t | |
+ngx_http_spdy_request_priority_variable(ngx_http_request_t *r, | |
+ ngx_http_variable_value_t *v, uintptr_t data) | |
+{ | |
+ if (r->spdy_stream) { | |
+ v->len = 1; | |
+ v->valid = 1; | |
+ v->no_cacheable = 0; | |
+ v->not_found = 0; | |
+ | |
+ v->data = ngx_pnalloc(r->pool, 1); | |
+ if (v->data == NULL) { | |
+ return NGX_ERROR; | |
+ } | |
+ | |
+ v->data[0] = '0' + (u_char) r->spdy_stream->priority; | |
+ | |
+ return NGX_OK; | |
+ } | |
+ | |
+ *v = ngx_http_variable_null_value; | |
+ | |
+ return NGX_OK; | |
+} | |
+ | |
+ | |
+static ngx_int_t | |
+ngx_http_spdy_module_init(ngx_cycle_t *cycle) | |
+{ | |
+ ngx_http_spdy_request_headers_init(); | |
+ | |
+ return NGX_OK; | |
+} | |
+ | |
+ | |
+static void * | |
+ngx_http_spdy_create_main_conf(ngx_conf_t *cf) | |
+{ | |
+ ngx_http_spdy_main_conf_t *smcf; | |
+ | |
+ smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_main_conf_t)); | |
+ if (smcf == NULL) { | |
+ return NULL; | |
+ } | |
+ | |
+ smcf->recv_buffer_size = NGX_CONF_UNSET_SIZE; | |
+ | |
+ return smcf; | |
+} | |
+ | |
+ | |
+static char * | |
+ngx_http_spdy_init_main_conf(ngx_conf_t *cf, void *conf) | |
+{ | |
+ ngx_http_spdy_main_conf_t *smcf = conf; | |
+ | |
+ ngx_conf_init_size_value(smcf->recv_buffer_size, 256 * 1024); | |
+ | |
+ return NGX_CONF_OK; | |
+} | |
+ | |
+ | |
+static void * | |
+ngx_http_spdy_create_srv_conf(ngx_conf_t *cf) | |
+{ | |
+ ngx_http_spdy_srv_conf_t *sscf; | |
+ | |
+ sscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_srv_conf_t)); | |
+ if (sscf == NULL) { | |
+ return NULL; | |
+ } | |
+ | |
+ sscf->pool_size = NGX_CONF_UNSET_SIZE; | |
+ | |
+ sscf->concurrent_streams = NGX_CONF_UNSET_UINT; | |
+ sscf->streams_index_mask = NGX_CONF_UNSET_UINT; | |
+ | |
+ sscf->recv_timeout = NGX_CONF_UNSET_MSEC; | |
+ sscf->keepalive_timeout = NGX_CONF_UNSET_MSEC; | |
+ | |
+ sscf->headers_comp = NGX_CONF_UNSET; | |
+ | |
+ return sscf; | |
+} | |
+ | |
+ | |
+static char * | |
+ngx_http_spdy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) | |
+{ | |
+ ngx_http_spdy_srv_conf_t *prev = parent; | |
+ ngx_http_spdy_srv_conf_t *conf = child; | |
+ | |
+ ngx_conf_merge_size_value(conf->pool_size, prev->pool_size, 4096); | |
+ | |
+ ngx_conf_merge_uint_value(conf->concurrent_streams, | |
+ prev->concurrent_streams, 100); | |
+ | |
+ ngx_conf_merge_uint_value(conf->streams_index_mask, | |
+ prev->streams_index_mask, 32 - 1); | |
+ | |
+ ngx_conf_merge_msec_value(conf->recv_timeout, | |
+ prev->recv_timeout, 30000); | |
+ ngx_conf_merge_msec_value(conf->keepalive_timeout, | |
+ prev->keepalive_timeout, 180000); | |
+ | |
+ ngx_conf_merge_value(conf->headers_comp, prev->headers_comp, 0); | |
+ | |
+ return NGX_CONF_OK; | |
+} | |
+ | |
+ | |
+static void * | |
+ngx_http_spdy_create_loc_conf(ngx_conf_t *cf) | |
+{ | |
+ ngx_http_spdy_loc_conf_t *slcf; | |
+ | |
+ slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_loc_conf_t)); | |
+ if (slcf == NULL) { | |
+ return NULL; | |
+ } | |
+ | |
+ slcf->chunk_size = NGX_CONF_UNSET_SIZE; | |
+ | |
+ return slcf; | |
+} | |
+ | |
+ | |
+static char * | |
+ngx_http_spdy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) | |
+{ | |
+ ngx_http_spdy_loc_conf_t *prev = parent; | |
+ ngx_http_spdy_loc_conf_t *conf = child; | |
+ | |
+ ngx_conf_merge_size_value(conf->chunk_size, prev->chunk_size, 8 * 1024); | |
+ | |
+ return NGX_CONF_OK; | |
+} | |
+ | |
+ | |
+static char * | |
+ngx_http_spdy_recv_buffer_size(ngx_conf_t *cf, void *post, void *data) | |
+{ | |
+ size_t *sp = data; | |
+ | |
+ if (*sp <= 2 * NGX_SPDY_STATE_BUFFER_SIZE) { | |
+ return "value is too small"; | |
+ } | |
+ | |
+ return NGX_CONF_OK; | |
+} | |
+ | |
+ | |
+static char * | |
+ngx_http_spdy_pool_size(ngx_conf_t *cf, void *post, void *data) | |
+{ | |
+ size_t *sp = data; | |
+ | |
+ if (*sp < NGX_MIN_POOL_SIZE) { | |
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
+ "the pool size must be no less than %uz", | |
+ NGX_MIN_POOL_SIZE); | |
+ return NGX_CONF_ERROR; | |
+ } | |
+ | |
+ if (*sp % NGX_POOL_ALIGNMENT) { | |
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
+ "the pool size must be a multiple of %uz", | |
+ NGX_POOL_ALIGNMENT); | |
+ return NGX_CONF_ERROR; | |
+ } | |
+ | |
+ return NGX_CONF_OK; | |
+} | |
+ | |
+ | |
+static char * | |
+ngx_http_spdy_streams_index_mask(ngx_conf_t *cf, void *post, void *data) | |
+{ | |
+ ngx_uint_t *np = data; | |
+ | |
+ ngx_uint_t mask; | |
+ | |
+ mask = *np - 1; | |
+ | |
+ if (*np == 0 || (*np & mask)) { | |
+ return "must be a power of two"; | |
+ } | |
+ | |
+ *np = mask; | |
+ | |
+ return NGX_CONF_OK; | |
+} | |
+ | |
+ | |
+static char * | |
+ngx_http_spdy_chunk_size(ngx_conf_t *cf, void *post, void *data) | |
+{ | |
+ size_t *sp = data; | |
+ | |
+ if (*sp == 0) { | |
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
+ "the spdy chunk size cannot be zero"); | |
+ return NGX_CONF_ERROR; | |
+ } | |
+ | |
+ if (*sp > NGX_SPDY_MAX_FRAME_SIZE) { | |
+ *sp = NGX_SPDY_MAX_FRAME_SIZE; | |
+ } | |
+ | |
+ return NGX_CONF_OK; | |
+} | |
diff --git a/src/http/ngx_http_spdy_module.h b/src/http/ngx_http_spdy_module.h | |
new file mode 100644 | |
index 0000000..5242322 | |
--- /dev/null | |
+++ b/src/http/ngx_http_spdy_module.h | |
@@ -0,0 +1,41 @@ | |
+ | |
+/* | |
+ * Copyright (C) Nginx, Inc. | |
+ * Copyright (C) Valentin V. Bartenev | |
+ */ | |
+ | |
+ | |
+#ifndef _NGX_HTTP_SPDY_MODULE_H_INCLUDED_ | |
+#define _NGX_HTTP_SPDY_MODULE_H_INCLUDED_ | |
+ | |
+ | |
+#include <ngx_config.h> | |
+#include <ngx_core.h> | |
+#include <ngx_http.h> | |
+ | |
+ | |
+typedef struct { | |
+ size_t recv_buffer_size; | |
+ u_char *recv_buffer; | |
+} ngx_http_spdy_main_conf_t; | |
+ | |
+ | |
+typedef struct { | |
+ size_t pool_size; | |
+ ngx_uint_t concurrent_streams; | |
+ ngx_uint_t streams_index_mask; | |
+ ngx_msec_t recv_timeout; | |
+ ngx_msec_t keepalive_timeout; | |
+ ngx_int_t headers_comp; | |
+} ngx_http_spdy_srv_conf_t; | |
+ | |
+ | |
+typedef struct { | |
+ size_t chunk_size; | |
+} ngx_http_spdy_loc_conf_t; | |
+ | |
+ | |
+extern ngx_module_t ngx_http_spdy_module; | |
+ | |
+ | |
+#endif /* _NGX_HTTP_SPDY_MODULE_H_INCLUDED_ */ | |
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c | |
index 73a5882..4384a6c 100644 | |
--- a/src/http/ngx_http_upstream.c | |
+++ b/src/http/ngx_http_upstream.c | |
@@ -514,6 +514,12 @@ ngx_http_upstream_init(ngx_http_request_t *r) | |
return; | |
} | |
#endif | |
+#if (NGX_HTTP_SPDY) | |
+ if (r->spdy_stream) { | |
+ ngx_http_upstream_init_request(r); | |
+ return; | |
+ } | |
+#endif | |
if (c->read->timer_set) { | |
ngx_del_timer(c->read); | |
@@ -1317,6 +1323,11 @@ ngx_http_upstream_check_broken_connection(ngx_http_request_t *r, | |
return; | |
} | |
#endif | |
+#if (NGX_HTTP_SPDY) | |
+ if (r->spdy_stream) { | |
+ return; | |
+ } | |
+#endif | |
#if (NGX_HAVE_KQUEUE) | |
diff --git a/src/http/v2/ngx_http_v2_module.c b/src/http/v2/ngx_http_v2_module.c | |
index 7f7dab2..1abf0c2 100644 | |
--- a/src/http/v2/ngx_http_v2_module.c | |
+++ b/src/http/v2/ngx_http_v2_module.c | |
@@ -34,8 +34,6 @@ static char *ngx_http_v2_preread_size(ngx_conf_t *cf, void *post, void *data); | |
static char *ngx_http_v2_streams_index_mask(ngx_conf_t *cf, void *post, | |
void *data); | |
static char *ngx_http_v2_chunk_size(ngx_conf_t *cf, void *post, void *data); | |
-static char *ngx_http_v2_spdy_deprecated(ngx_conf_t *cf, ngx_command_t *cmd, | |
- void *conf); | |
static ngx_conf_post_t ngx_http_v2_recv_buffer_size_post = | |
@@ -129,62 +127,6 @@ static ngx_command_t ngx_http_v2_commands[] = { | |
offsetof(ngx_http_v2_loc_conf_t, chunk_size), | |
&ngx_http_v2_chunk_size_post }, | |
- { ngx_string("spdy_recv_buffer_size"), | |
- NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, | |
- ngx_http_v2_spdy_deprecated, | |
- NGX_HTTP_MAIN_CONF_OFFSET, | |
- 0, | |
- NULL }, | |
- | |
- { ngx_string("spdy_pool_size"), | |
- NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, | |
- ngx_http_v2_spdy_deprecated, | |
- NGX_HTTP_SRV_CONF_OFFSET, | |
- 0, | |
- NULL }, | |
- | |
- { ngx_string("spdy_max_concurrent_streams"), | |
- NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, | |
- ngx_http_v2_spdy_deprecated, | |
- NGX_HTTP_SRV_CONF_OFFSET, | |
- 0, | |
- NULL }, | |
- | |
- { ngx_string("spdy_streams_index_size"), | |
- NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, | |
- ngx_http_v2_spdy_deprecated, | |
- NGX_HTTP_SRV_CONF_OFFSET, | |
- 0, | |
- NULL }, | |
- | |
- { ngx_string("spdy_recv_timeout"), | |
- NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, | |
- ngx_http_v2_spdy_deprecated, | |
- NGX_HTTP_SRV_CONF_OFFSET, | |
- 0, | |
- NULL }, | |
- | |
- { ngx_string("spdy_keepalive_timeout"), | |
- NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, | |
- ngx_http_v2_spdy_deprecated, | |
- NGX_HTTP_SRV_CONF_OFFSET, | |
- 0, | |
- NULL }, | |
- | |
- { ngx_string("spdy_headers_comp"), | |
- NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, | |
- ngx_http_v2_spdy_deprecated, | |
- NGX_HTTP_SRV_CONF_OFFSET, | |
- 0, | |
- NULL }, | |
- | |
- { ngx_string("spdy_chunk_size"), | |
- NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
- ngx_http_v2_spdy_deprecated, | |
- NGX_HTTP_LOC_CONF_OFFSET, | |
- 0, | |
- NULL }, | |
- | |
ngx_null_command | |
}; | |
@@ -496,14 +438,3 @@ ngx_http_v2_chunk_size(ngx_conf_t *cf, void *post, void *data) | |
return NGX_CONF_OK; | |
} | |
- | |
- | |
-static char * | |
-ngx_http_v2_spdy_deprecated(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
-{ | |
- ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | |
- "invalid directive \"%V\": ngx_http_spdy_module " | |
- "was superseded by ngx_http_v2_module", &cmd->name); | |
- | |
- return NGX_CONF_OK; | |
-} | |
-- | |
1.8.3.1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment