[nginx] SPDY: implemented buffers chain splitting.
Valentin Bartenev
vbart at nginx.com
Tue Jan 14 12:58:07 UTC 2014
details: http://hg.nginx.org/nginx/rev/b7ee1bae0ffa
branches:
changeset: 5514:b7ee1bae0ffa
user: Valentin Bartenev <vbart at nginx.com>
date: Tue Jan 14 16:24:45 2014 +0400
description:
SPDY: implemented buffers chain splitting.
It fixes "chain too big in spdy filter" alerts, and adds full support for rate
limiting of SPDY streams.
diffstat:
src/http/ngx_http_spdy.h | 1 +
src/http/ngx_http_spdy_filter_module.c | 191 ++++++++++++++++++++++++++++----
2 files changed, 164 insertions(+), 28 deletions(-)
diffs (280 lines):
diff -r 311803b21504 -r b7ee1bae0ffa src/http/ngx_http_spdy.h
--- a/src/http/ngx_http_spdy.h Tue Jan 14 16:24:45 2014 +0400
+++ b/src/http/ngx_http_spdy.h Tue Jan 14 16:24:45 2014 +0400
@@ -123,6 +123,7 @@ struct ngx_http_spdy_stream_s {
ngx_http_spdy_out_frame_t *free_frames;
ngx_chain_t *free_data_headers;
+ ngx_chain_t *free_bufs;
unsigned priority:2;
unsigned handled:1;
diff -r 311803b21504 -r b7ee1bae0ffa src/http/ngx_http_spdy_filter_module.c
--- a/src/http/ngx_http_spdy_filter_module.c Tue Jan 14 16:24:45 2014 +0400
+++ b/src/http/ngx_http_spdy_filter_module.c Tue Jan 14 16:24:45 2014 +0400
@@ -34,6 +34,9 @@ static ngx_chain_t *ngx_http_spdy_send_c
static ngx_inline ngx_int_t ngx_http_spdy_filter_send(
ngx_connection_t *fc, 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, size_t offset,
+ size_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);
@@ -618,8 +621,8 @@ ngx_http_spdy_header_filter(ngx_http_req
static ngx_chain_t *
ngx_http_spdy_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)
{
- off_t size;
- ngx_buf_t *b;
+ 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;
@@ -639,48 +642,161 @@ ngx_http_spdy_send_chain(ngx_connection_
return NULL;
}
- size = 0;
- ln = &out;
+ size = ngx_buf_size(in->buf);
- do {
- b = in->buf;
-
+ 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;
}
- size += ngx_buf_size(b);
- cl->buf = b;
+ cl->buf = in->buf;
+ in->buf = cl->buf->shadow;
- *ln = cl;
- ln = &cl->next;
+ offset = ngx_buf_in_memory(in->buf)
+ ? (cl->buf->pos - in->buf->pos)
+ : (cl->buf->file_pos - in->buf->file_pos);
- in = in->next;
+ cl->next = stream->free_bufs;
+ stream->free_bufs = cl;
- } while (in);
-
- if (size > NGX_SPDY_MAX_FRAME_SIZE) {
- ngx_log_error(NGX_LOG_ALERT, fc->log, 0,
- "FIXME: chain too big in spdy filter: %O", size);
- return NGX_CHAIN_ERROR;
+ } else {
+ offset = 0;
}
- frame = ngx_http_spdy_filter_get_data_frame(stream, (size_t) size,
- out, cl);
- if (frame == NULL) {
- return NGX_CHAIN_ERROR;
+ frame_size = (limit && limit < NGX_SPDY_MAX_FRAME_SIZE)
+ ? limit : NGX_SPDY_MAX_FRAME_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;
+ 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(stream->connection, frame);
+
+ stream->queued++;
+
+ if (in == NULL) {
+ break;
+ }
+
+ if (limit) {
+ limit -= frame_size;
+
+ if (limit == 0) {
+ break;
+ }
+
+ if (limit < NGX_SPDY_MAX_FRAME_SIZE) {
+ frame_size = limit;
+ }
+ }
}
- ngx_http_spdy_queue_frame(stream->connection, frame);
+ if (offset) {
+ cl = ngx_http_spdy_filter_get_shadow(stream, in->buf, offset, size);
+ if (cl == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
- stream->queued++;
+ in->buf = cl->buf;
+ ngx_free_chain(r->pool, cl);
+ }
if (ngx_http_spdy_filter_send(fc, stream) == NGX_ERROR) {
return NGX_CHAIN_ERROR;
}
- return NULL;
+ return in;
+}
+
+
+static ngx_chain_t *
+ngx_http_spdy_filter_get_shadow(ngx_http_spdy_stream_t *stream, ngx_buf_t *buf,
+ size_t offset, size_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;
}
@@ -747,7 +863,7 @@ ngx_http_spdy_filter_get_data_frame(ngx_
buf->last = p;
buf->end = p;
- buf->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_module;
+ buf->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_get_data_frame;
buf->memory = 1;
}
@@ -825,6 +941,7 @@ 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;
@@ -832,7 +949,7 @@ ngx_http_spdy_data_frame_handler(ngx_htt
cl = frame->first;
- if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_module) {
+ 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,
@@ -855,6 +972,18 @@ ngx_http_spdy_data_frame_handler(ngx_htt
}
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) {
@@ -871,7 +1000,13 @@ ngx_http_spdy_data_frame_handler(ngx_htt
ln = cl->next;
- ngx_free_chain(stream->request->pool, cl);
+ 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;
More information about the nginx-devel
mailing list