SPDY: split big data frames

Yury Kirpichev ykirpichev at gmail.com
Mon Jun 24 15:04:52 UTC 2013


Hi,

It is limitation of current nginx spdy implementation that whole data chain
is sent by single spdy data frame.
Below are changes where I tried to change this behavior and use 32kb data
frames at most in order to send outgoing spdy data.
Any comments are welcome.

# HG changeset patch
# User ykirpichev at gmail.com
# Date 1372085474 -14400
# Branch spdy_split_big_frame_default
# Node ID c8660bcdd8d3fb2ef12fa1edc1d0f39b771de51e
# Parent  982678c5c270f93a0c21ab6eb23cb123c0dc3df0
SPDY: split big frames

diff -r 982678c5c270 -r c8660bcdd8d3 src/core/ngx_buf.h
--- a/src/core/ngx_buf.h Wed Jun 12 00:41:24 2013 +0900
+++ b/src/core/ngx_buf.h Mon Jun 24 18:51:14 2013 +0400
@@ -158,5 +158,8 @@
 void ngx_chain_update_chains(ngx_pool_t *p, ngx_chain_t **free,
     ngx_chain_t **busy, ngx_chain_t **out, ngx_buf_tag_t tag);

+    ngx_int_t
+ngx_split_buf_in_chain(ngx_pool_t *pool, ngx_chain_t **chain,
+                       ngx_chain_t *in, off_t buf_size);

 #endif /* _NGX_BUF_H_INCLUDED_ */
diff -r 982678c5c270 -r c8660bcdd8d3 src/core/ngx_output_chain.c
--- a/src/core/ngx_output_chain.c Wed Jun 12 00:41:24 2013 +0900
+++ b/src/core/ngx_output_chain.c Mon Jun 24 18:51:14 2013 +0400
@@ -672,3 +672,59 @@

     return NGX_AGAIN;
 }
+
+
+    ngx_int_t
+ngx_split_buf_in_chain(ngx_pool_t *pool, ngx_chain_t **chain,
+                       ngx_chain_t *in, off_t buf_size)
+{
+    ngx_chain_t  *cl, **il;
+    ngx_buf_t    *b, *buf;
+
+#if 0
+    if (!in->buf->in_file) {
+        return NGX_OK;
+    }
+#endif
+
+    il = chain;
+
+    while (ngx_buf_size(in->buf) > buf_size) {
+        cl = ngx_alloc_chain_link(pool);
+        if (cl == NULL) {
+            return NGX_ERROR;
+        }
+
+        buf = in->buf;
+
+        /* split a file buf on bufs by the buf_size limit */
+
+        b = ngx_calloc_buf(pool);
+        if (b == NULL) {
+            return NGX_ERROR;
+        }
+
+        ngx_memcpy(b, buf, sizeof(ngx_buf_t));
+        b->start = NULL;
+        b->end = NULL;
+        b->last_buf = 0;
+        b->last_in_chain = 0;
+
+        if (ngx_buf_in_memory(buf)) {
+            buf->pos += buf_size;
+            b->last = buf->pos;
+        }
+
+        buf->file_pos += buf_size;
+        b->file_last = buf->file_pos;
+
+        cl->buf = b;
+
+        cl->next = in;
+        *il = cl;
+        il = &cl->next;
+    }
+
+    return NGX_OK;
+}
+
diff -r 982678c5c270 -r c8660bcdd8d3 src/http/ngx_http_spdy_filter_module.c
--- a/src/http/ngx_http_spdy_filter_module.c Wed Jun 12 00:41:24 2013 +0900
+++ b/src/http/ngx_http_spdy_filter_module.c Mon Jun 24 18:51:14 2013 +0400
@@ -15,6 +15,9 @@


 #define NGX_SPDY_WRITE_BUFFERED  NGX_HTTP_WRITE_BUFFERED
+/* it is subject for change */
+/* consider to use NGX_SPDY_MAX_FRAME_SIZE instead ??? */
+#define NGX_SPDY_MAX_FRAME_LENGTH  (1024 * 32)

 #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)
@@ -676,6 +679,10 @@
         cl->buf = b;

         *ln = cl;
+        if (ngx_buf_size(b) > NGX_SPDY_MAX_FRAME_LENGTH) {
+            ngx_split_buf_in_chain(r->pool, ln, cl,
NGX_SPDY_MAX_FRAME_LENGTH);
+        }
+
         ln = &cl->next;

         if (ll->next == NULL) {
@@ -685,24 +692,51 @@
         ll = ll->next;
     }

-    if (size > NGX_SPDY_MAX_FRAME_SIZE) {
-        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                      "FIXME: chain too big in spdy filter: %O", size);
-        return NGX_ERROR;
+    cl->next = NULL;
+    ll = cl = out;
+    size = 0;
+
+    for ( ;; ) {
+        if (size + ngx_buf_size(cl->buf) > NGX_SPDY_MAX_FRAME_LENGTH) {
+            ll->buf->last_in_chain = 1;
+            frame = ngx_http_spdy_filter_get_data_frame(stream,
(size_t)size,
+                                                        ll->buf->last_buf,
out, ll);
+
+            ngx_http_spdy_queue_frame(stream->connection, frame);
+            stream->waiting++;
+            r->main->blocked++;
+
+
+            size = ngx_buf_size(cl->buf);
+            out = cl;
+            ll->next = NULL;
+            ll = cl;
+            if (cl->next == NULL) {
+                break;
+            }
+            cl = cl->next;
+        }
+        else {
+            size += ngx_buf_size(cl->buf);
+            ll = cl;
+            if (cl->next == NULL) {
+                break;
+            }
+            cl = cl->next;
+
+        }
     }

-    frame = ngx_http_spdy_filter_get_data_frame(stream, (size_t) size,
-                                                b->last_buf, out, cl);
-    if (frame == NULL) {
-        return NGX_ERROR;
+    if (size > 0) {
+        ll->buf->last_in_chain = 1;
+
+        frame = ngx_http_spdy_filter_get_data_frame(stream, (size_t)size,
+                                                    ll->buf->last_buf,
out, ll);
+        ngx_http_spdy_queue_frame(stream->connection, frame);
+        stream->waiting++;
+        r->main->blocked++;
     }

-    ngx_http_spdy_queue_frame(stream->connection, frame);
-
-    stream->waiting++;
-
-    r->main->blocked++;
-
     return ngx_http_spdy_filter_send(r->connection, stream);
 }


BR/ Yury
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.nginx.org/pipermail/nginx-devel/attachments/20130624/8286e0cc/attachment.html>


More information about the nginx-devel mailing list