src/http/ngx_http_spdy_filter_module.c - nginx-1.7.10

Global variables defined

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Nginx, Inc.
  3. * Copyright (C) Valentin V. Bartenev
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_http.h>
  8. #include <nginx.h>
  9. #include <ngx_http_spdy_module.h>

  10. #include <zlib.h>


  11. #define ngx_http_spdy_nv_nsize(h)  (NGX_SPDY_NV_NLEN_SIZE + sizeof(h) - 1)
  12. #define ngx_http_spdy_nv_vsize(h)  (NGX_SPDY_NV_VLEN_SIZE + sizeof(h) - 1)

  13. #define ngx_http_spdy_nv_write_num   ngx_spdy_frame_write_uint32
  14. #define ngx_http_spdy_nv_write_nlen  ngx_spdy_frame_write_uint32
  15. #define ngx_http_spdy_nv_write_vlen  ngx_spdy_frame_write_uint32

  16. #define ngx_http_spdy_nv_write_name(p, h)                                     \
  17.     ngx_cpymem(ngx_http_spdy_nv_write_nlen(p, sizeof(h) - 1), h, sizeof(h) - 1)

  18. #define ngx_http_spdy_nv_write_val(p, h)                                      \
  19.     ngx_cpymem(ngx_http_spdy_nv_write_vlen(p, sizeof(h) - 1), h, sizeof(h) - 1)


  20. static ngx_chain_t *ngx_http_spdy_send_chain(ngx_connection_t *fc,
  21.     ngx_chain_t *in, off_t limit);

  22. static ngx_inline ngx_int_t ngx_http_spdy_filter_send(
  23.     ngx_connection_t *fc, ngx_http_spdy_stream_t *stream);
  24. static ngx_inline ngx_int_t ngx_http_spdy_flow_control(
  25.     ngx_http_spdy_connection_t *sc, ngx_http_spdy_stream_t *stream);
  26. static void ngx_http_spdy_waiting_queue(ngx_http_spdy_connection_t *sc,
  27.     ngx_http_spdy_stream_t *stream);

  28. static ngx_chain_t *ngx_http_spdy_filter_get_shadow(
  29.     ngx_http_spdy_stream_t *stream, ngx_buf_t *buf, off_t offset, off_t size);
  30. static ngx_http_spdy_out_frame_t *ngx_http_spdy_filter_get_data_frame(
  31.     ngx_http_spdy_stream_t *stream, size_t len, ngx_chain_t *first,
  32.     ngx_chain_t *last);

  33. static ngx_int_t ngx_http_spdy_syn_frame_handler(
  34.     ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);
  35. static ngx_int_t ngx_http_spdy_data_frame_handler(
  36.     ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);
  37. static ngx_inline void ngx_http_spdy_handle_frame(
  38.     ngx_http_spdy_stream_t *stream, ngx_http_spdy_out_frame_t *frame);
  39. static ngx_inline void ngx_http_spdy_handle_stream(
  40.     ngx_http_spdy_connection_t *sc, ngx_http_spdy_stream_t *stream);

  41. static void ngx_http_spdy_filter_cleanup(void *data);

  42. static ngx_int_t ngx_http_spdy_filter_init(ngx_conf_t *cf);


  43. static ngx_http_module_t  ngx_http_spdy_filter_module_ctx = {
  44.     NULL,                                  /* preconfiguration */
  45.     ngx_http_spdy_filter_init,             /* postconfiguration */

  46.     NULL,                                  /* create main configuration */
  47.     NULL,                                  /* init main configuration */

  48.     NULL,                                  /* create server configuration */
  49.     NULL,                                  /* merge server configuration */

  50.     NULL,                                  /* create location configuration */
  51.     NULL                                   /* merge location configuration */
  52. };


  53. ngx_module_t  ngx_http_spdy_filter_module = {
  54.     NGX_MODULE_V1,
  55.     &ngx_http_spdy_filter_module_ctx,      /* module context */
  56.     NULL,                                  /* module directives */
  57.     NGX_HTTP_MODULE,                       /* module type */
  58.     NULL,                                  /* init master */
  59.     NULL,                                  /* init module */
  60.     NULL,                                  /* init process */
  61.     NULL,                                  /* init thread */
  62.     NULL,                                  /* exit thread */
  63.     NULL,                                  /* exit process */
  64.     NULL,                                  /* exit master */
  65.     NGX_MODULE_V1_PADDING
  66. };


  67. static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;


  68. static ngx_int_t
  69. ngx_http_spdy_header_filter(ngx_http_request_t *r)
  70. {
  71.     int                           rc;
  72.     size_t                        len;
  73.     u_char                       *p, *buf, *last;
  74.     ngx_buf_t                    *b;
  75.     ngx_str_t                     host;
  76.     ngx_uint_t                    i, j, count, port;
  77.     ngx_chain_t                  *cl;
  78.     ngx_list_part_t              *part, *pt;
  79.     ngx_table_elt_t              *header, *h;
  80.     ngx_connection_t             *c;
  81.     ngx_http_cleanup_t           *cln;
  82.     ngx_http_core_loc_conf_t     *clcf;
  83.     ngx_http_core_srv_conf_t     *cscf;
  84.     ngx_http_spdy_stream_t       *stream;
  85.     ngx_http_spdy_out_frame_t    *frame;
  86.     ngx_http_spdy_connection_t   *sc;
  87.     struct sockaddr_in           *sin;
  88. #if (NGX_HAVE_INET6)
  89.     struct sockaddr_in6          *sin6;
  90. #endif
  91.     u_char                        addr[NGX_SOCKADDR_STRLEN];

  92.     if (!r->spdy_stream) {
  93.         return ngx_http_next_header_filter(r);
  94.     }

  95.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  96.                    "spdy header filter");

  97.     if (r->header_sent) {
  98.         return NGX_OK;
  99.     }

  100.     r->header_sent = 1;

  101.     if (r != r->main) {
  102.         return NGX_OK;
  103.     }

  104.     c = r->connection;

  105.     if (r->method == NGX_HTTP_HEAD) {
  106.         r->header_only = 1;
  107.     }

  108.     switch (r->headers_out.status) {

  109.     case NGX_HTTP_OK:
  110.     case NGX_HTTP_PARTIAL_CONTENT:
  111.         break;

  112.     case NGX_HTTP_NOT_MODIFIED:
  113.         r->header_only = 1;
  114.         break;

  115.     case NGX_HTTP_NO_CONTENT:
  116.         r->header_only = 1;

  117.         ngx_str_null(&r->headers_out.content_type);

  118.         r->headers_out.content_length = NULL;
  119.         r->headers_out.content_length_n = -1;

  120.         /* fall through */

  121.     default:
  122.         r->headers_out.last_modified_time = -1;
  123.         r->headers_out.last_modified = NULL;
  124.     }

  125.     len = NGX_SPDY_NV_NUM_SIZE
  126.           + ngx_http_spdy_nv_nsize(":version")
  127.           + ngx_http_spdy_nv_vsize("HTTP/1.1")
  128.           + ngx_http_spdy_nv_nsize(":status")
  129.           + (r->headers_out.status_line.len
  130.              ? NGX_SPDY_NV_VLEN_SIZE + r->headers_out.status_line.len
  131.              : ngx_http_spdy_nv_vsize("418"));

  132.     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

  133.     if (r->headers_out.server == NULL) {
  134.         len += ngx_http_spdy_nv_nsize("server");
  135.         len += clcf->server_tokens ? ngx_http_spdy_nv_vsize(NGINX_VER)
  136.                                    : ngx_http_spdy_nv_vsize("nginx");
  137.     }

  138.     if (r->headers_out.date == NULL) {
  139.         len += ngx_http_spdy_nv_nsize("date")
  140.                + ngx_http_spdy_nv_vsize("Wed, 31 Dec 1986 10:00:00 GMT");
  141.     }

  142.     if (r->headers_out.content_type.len) {
  143.         len += ngx_http_spdy_nv_nsize("content-type")
  144.                + NGX_SPDY_NV_VLEN_SIZE + r->headers_out.content_type.len;

  145.         if (r->headers_out.content_type_len == r->headers_out.content_type.len
  146.             && r->headers_out.charset.len)
  147.         {
  148.             len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
  149.         }
  150.     }

  151.     if (r->headers_out.content_length == NULL
  152.         && r->headers_out.content_length_n >= 0)
  153.     {
  154.         len += ngx_http_spdy_nv_nsize("content-length")
  155.                + NGX_SPDY_NV_VLEN_SIZE + NGX_OFF_T_LEN;
  156.     }

  157.     if (r->headers_out.last_modified == NULL
  158.         && r->headers_out.last_modified_time != -1)
  159.     {
  160.         len += ngx_http_spdy_nv_nsize("last-modified")
  161.                + ngx_http_spdy_nv_vsize("Wed, 31 Dec 1986 10:00:00 GMT");
  162.     }

  163.     if (r->headers_out.location
  164.         && r->headers_out.location->value.len
  165.         && r->headers_out.location->value.data[0] == '/')
  166.     {
  167.         r->headers_out.location->hash = 0;

  168.         if (clcf->server_name_in_redirect) {
  169.             cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
  170.             host = cscf->server_name;

  171.         } else if (r->headers_in.server.len) {
  172.             host = r->headers_in.server;

  173.         } else {
  174.             host.len = NGX_SOCKADDR_STRLEN;
  175.             host.data = addr;

  176.             if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) {
  177.                 return NGX_ERROR;
  178.             }
  179.         }

  180.         switch (c->local_sockaddr->sa_family) {

  181. #if (NGX_HAVE_INET6)
  182.         case AF_INET6:
  183.             sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
  184.             port = ntohs(sin6->sin6_port);
  185.             break;
  186. #endif
  187. #if (NGX_HAVE_UNIX_DOMAIN)
  188.         case AF_UNIX:
  189.             port = 0;
  190.             break;
  191. #endif
  192.         default: /* AF_INET */
  193.             sin = (struct sockaddr_in *) c->local_sockaddr;
  194.             port = ntohs(sin->sin_port);
  195.             break;
  196.         }

  197.         len += ngx_http_spdy_nv_nsize("location")
  198.                + ngx_http_spdy_nv_vsize("https://")
  199.                + host.len
  200.                + r->headers_out.location->value.len;

  201.         if (clcf->port_in_redirect) {

  202. #if (NGX_HTTP_SSL)
  203.             if (c->ssl)
  204.                 port = (port == 443) ? 0 : port;
  205.             else
  206. #endif
  207.                 port = (port == 80) ? 0 : port;

  208.         } else {
  209.             port = 0;
  210.         }

  211.         if (port) {
  212.             len += sizeof(":65535") - 1;
  213.         }

  214.     } else {
  215.         ngx_str_null(&host);
  216.         port = 0;
  217.     }

  218.     part = &r->headers_out.headers.part;
  219.     header = part->elts;

  220.     for (i = 0; /* void */; i++) {

  221.         if (i >= part->nelts) {
  222.             if (part->next == NULL) {
  223.                 break;
  224.             }

  225.             part = part->next;
  226.             header = part->elts;
  227.             i = 0;
  228.         }

  229.         if (header[i].hash == 0) {
  230.             continue;
  231.         }

  232.         len += NGX_SPDY_NV_NLEN_SIZE + header[i].key.len
  233.                + NGX_SPDY_NV_VLEN_SIZE  + header[i].value.len;
  234.     }

  235.     buf = ngx_alloc(len, r->pool->log);
  236.     if (buf == NULL) {
  237.         return NGX_ERROR;
  238.     }

  239.     last = buf + NGX_SPDY_NV_NUM_SIZE;

  240.     last = ngx_http_spdy_nv_write_name(last, ":version");
  241.     last = ngx_http_spdy_nv_write_val(last, "HTTP/1.1");

  242.     last = ngx_http_spdy_nv_write_name(last, ":status");

  243.     if (r->headers_out.status_line.len) {
  244.         last = ngx_http_spdy_nv_write_vlen(last,
  245.                                            r->headers_out.status_line.len);
  246.         last = ngx_cpymem(last, r->headers_out.status_line.data,
  247.                           r->headers_out.status_line.len);
  248.     } else {
  249.         last = ngx_http_spdy_nv_write_vlen(last, 3);
  250.         last = ngx_sprintf(last, "%03ui", r->headers_out.status);
  251.     }

  252.     count = 2;

  253.     if (r->headers_out.server == NULL) {
  254.         last = ngx_http_spdy_nv_write_name(last, "server");
  255.         last = clcf->server_tokens
  256.                ? ngx_http_spdy_nv_write_val(last, NGINX_VER)
  257.                : ngx_http_spdy_nv_write_val(last, "nginx");

  258.         count++;
  259.     }

  260.     if (r->headers_out.date == NULL) {
  261.         last = ngx_http_spdy_nv_write_name(last, "date");

  262.         last = ngx_http_spdy_nv_write_vlen(last, ngx_cached_http_time.len);

  263.         last = ngx_cpymem(last, ngx_cached_http_time.data,
  264.                           ngx_cached_http_time.len);

  265.         count++;
  266.     }

  267.     if (r->headers_out.content_type.len) {

  268.         last = ngx_http_spdy_nv_write_name(last, "content-type");

  269.         p = last + NGX_SPDY_NV_VLEN_SIZE;

  270.         last = ngx_cpymem(p, r->headers_out.content_type.data,
  271.                           r->headers_out.content_type.len);

  272.         if (r->headers_out.content_type_len == r->headers_out.content_type.len
  273.             && r->headers_out.charset.len)
  274.         {
  275.             last = ngx_cpymem(last, "; charset=", sizeof("; charset=") - 1);

  276.             last = ngx_cpymem(last, r->headers_out.charset.data,
  277.                               r->headers_out.charset.len);

  278.             /* update r->headers_out.content_type for possible logging */

  279.             r->headers_out.content_type.len = last - p;
  280.             r->headers_out.content_type.data = p;
  281.         }

  282.         (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
  283.                                            r->headers_out.content_type.len);

  284.         count++;
  285.     }

  286.     if (r->headers_out.content_length == NULL
  287.         && r->headers_out.content_length_n >= 0)
  288.     {
  289.         last = ngx_http_spdy_nv_write_name(last, "content-length");

  290.         p = last + NGX_SPDY_NV_VLEN_SIZE;

  291.         last = ngx_sprintf(p, "%O", r->headers_out.content_length_n);

  292.         (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
  293.                                            last - p);

  294.         count++;
  295.     }

  296.     if (r->headers_out.last_modified == NULL
  297.         && r->headers_out.last_modified_time != -1)
  298.     {
  299.         last = ngx_http_spdy_nv_write_name(last, "last-modified");

  300.         p = last + NGX_SPDY_NV_VLEN_SIZE;

  301.         last = ngx_http_time(p, r->headers_out.last_modified_time);

  302.         (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
  303.                                            last - p);

  304.         count++;
  305.     }

  306.     if (host.data) {

  307.         last = ngx_http_spdy_nv_write_name(last, "location");

  308.         p = last + NGX_SPDY_NV_VLEN_SIZE;

  309.         last = ngx_cpymem(p, "http", sizeof("http") - 1);

  310. #if (NGX_HTTP_SSL)
  311.         if (c->ssl) {
  312.             *last++ ='s';
  313.         }
  314. #endif

  315.         *last++ = ':'; *last++ = '/'; *last++ = '/';

  316.         last = ngx_cpymem(last, host.data, host.len);

  317.         if (port) {
  318.             last = ngx_sprintf(last, ":%ui", port);
  319.         }

  320.         last = ngx_cpymem(last, r->headers_out.location->value.data,
  321.                           r->headers_out.location->value.len);

  322.         /* update r->headers_out.location->value for possible logging */

  323.         r->headers_out.location->value.len = last - p;
  324.         r->headers_out.location->value.data = p;
  325.         ngx_str_set(&r->headers_out.location->key, "location");

  326.         (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
  327.                                            r->headers_out.location->value.len);

  328.         count++;
  329.     }

  330.     part = &r->headers_out.headers.part;
  331.     header = part->elts;

  332.     for (i = 0; /* void */; i++) {

  333.         if (i >= part->nelts) {
  334.             if (part->next == NULL) {
  335.                 break;
  336.             }

  337.             part = part->next;
  338.             header = part->elts;
  339.             i = 0;
  340.         }

  341.         if (header[i].hash == 0 || header[i].hash == 2) {
  342.             continue;
  343.         }

  344.         last = ngx_http_spdy_nv_write_nlen(last, header[i].key.len);

  345.         ngx_strlow(last, header[i].key.data, header[i].key.len);
  346.         last += header[i].key.len;

  347.         p = last + NGX_SPDY_NV_VLEN_SIZE;

  348.         last = ngx_cpymem(p, header[i].value.data, header[i].value.len);

  349.         pt = part;
  350.         h = header;

  351.         for (j = i + 1; /* void */; j++) {

  352.             if (j >= pt->nelts) {
  353.                 if (pt->next == NULL) {
  354.                     break;
  355.                 }

  356.                 pt = pt->next;
  357.                 h = pt->elts;
  358.                 j = 0;
  359.             }

  360.             if (h[j].hash == 0 || h[j].hash == 2
  361.                 || h[j].key.len != header[i].key.len
  362.                 || ngx_strncasecmp(header[i].key.data, h[j].key.data,
  363.                                    header[i].key.len))
  364.             {
  365.                 continue;
  366.             }

  367.             if (h[j].value.len) {
  368.                 if (last != p) {
  369.                     *last++ = '\0';
  370.                 }

  371.                 last = ngx_cpymem(last, h[j].value.data, h[j].value.len);
  372.             }

  373.             h[j].hash = 2;
  374.         }

  375.         (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
  376.                                            last - p);

  377.         count++;
  378.     }

  379.     (void) ngx_http_spdy_nv_write_num(buf, count);

  380.     stream = r->spdy_stream;
  381.     sc = stream->connection;

  382.     len = last - buf;

  383.     b = ngx_create_temp_buf(r->pool, NGX_SPDY_FRAME_HEADER_SIZE
  384.                                      + NGX_SPDY_SYN_REPLY_SIZE
  385.                                      + deflateBound(&sc->zstream_out, len));
  386.     if (b == NULL) {
  387.         ngx_free(buf);
  388.         return NGX_ERROR;
  389.     }

  390.     b->last += NGX_SPDY_FRAME_HEADER_SIZE + NGX_SPDY_SYN_REPLY_SIZE;

  391.     sc->zstream_out.next_in = buf;
  392.     sc->zstream_out.avail_in = len;
  393.     sc->zstream_out.next_out = b->last;
  394.     sc->zstream_out.avail_out = b->end - b->last;

  395.     rc = deflate(&sc->zstream_out, Z_SYNC_FLUSH);

  396.     ngx_free(buf);

  397.     if (rc != Z_OK) {
  398.         ngx_log_error(NGX_LOG_ALERT, c->log, 0, "deflate() failed: %d", rc);
  399.         return NGX_ERROR;
  400.     }

  401.     ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,
  402.                    "spdy deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
  403.                    sc->zstream_out.next_in, sc->zstream_out.next_out,
  404.                    sc->zstream_out.avail_in, sc->zstream_out.avail_out,
  405.                    rc);

  406.     b->last = sc->zstream_out.next_out;

  407.     p = b->pos;
  408.     p = ngx_spdy_frame_write_head(p, NGX_SPDY_SYN_REPLY);

  409.     len = b->last - b->pos;

  410.     r->header_size = len;

  411.     len -= NGX_SPDY_FRAME_HEADER_SIZE;

  412.     if (r->header_only) {
  413.         b->last_buf = 1;
  414.         p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_FIN, len);

  415.     } else {
  416.         p = ngx_spdy_frame_write_flags_and_len(p, 0, len);
  417.     }

  418.     (void) ngx_spdy_frame_write_sid(p, stream->id);

  419.     cl = ngx_alloc_chain_link(r->pool);
  420.     if (cl == NULL) {
  421.         return NGX_ERROR;
  422.     }

  423.     cl->buf = b;
  424.     cl->next = NULL;

  425.     frame = ngx_palloc(r->pool, sizeof(ngx_http_spdy_out_frame_t));
  426.     if (frame == NULL) {
  427.         return NGX_ERROR;
  428.     }

  429.     frame->first = cl;
  430.     frame->last = cl;
  431.     frame->handler = ngx_http_spdy_syn_frame_handler;
  432.     frame->stream = stream;
  433.     frame->length = len;
  434.     frame->priority = stream->priority;
  435.     frame->blocked = 1;
  436.     frame->fin = r->header_only;

  437.     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,
  438.                    "spdy:%ui create SYN_REPLY frame %p: len:%uz",
  439.                    stream->id, frame, frame->length);

  440.     ngx_http_spdy_queue_blocked_frame(sc, frame);

  441.     cln = ngx_http_cleanup_add(r, 0);
  442.     if (cln == NULL) {
  443.         return NGX_ERROR;
  444.     }

  445.     cln->handler = ngx_http_spdy_filter_cleanup;
  446.     cln->data = stream;

  447.     stream->queued = 1;

  448.     c->send_chain = ngx_http_spdy_send_chain;
  449.     c->need_last_buf = 1;

  450.     return ngx_http_spdy_filter_send(c, stream);
  451. }


  452. static ngx_chain_t *
  453. ngx_http_spdy_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)
  454. {
  455.     off_t                        size, offset;
  456.     size_t                       rest, frame_size;
  457.     ngx_chain_t                 *cl, *out, **ln;
  458.     ngx_http_request_t          *r;
  459.     ngx_http_spdy_stream_t      *stream;
  460.     ngx_http_spdy_loc_conf_t    *slcf;
  461.     ngx_http_spdy_out_frame_t   *frame;
  462.     ngx_http_spdy_connection_t  *sc;

  463.     r = fc->data;
  464.     stream = r->spdy_stream;

  465. #if (NGX_SUPPRESS_WARN)
  466.     size = 0;
  467. #endif

  468.     while (in) {
  469.         size = ngx_buf_size(in->buf);

  470.         if (size || in->buf->last_buf) {
  471.             break;
  472.         }

  473.         in = in->next;
  474.     }

  475.     if (in == NULL) {

  476.         if (stream->queued) {
  477.             fc->write->delayed = 1;
  478.         } else {
  479.             fc->buffered &= ~NGX_SPDY_BUFFERED;
  480.         }

  481.         return NULL;
  482.     }

  483.     sc = stream->connection;

  484.     if (size && ngx_http_spdy_flow_control(sc, stream) == NGX_DECLINED) {
  485.         fc->write->delayed = 1;
  486.         return in;
  487.     }

  488.     if (limit == 0 || limit > (off_t) sc->send_window) {
  489.         limit = sc->send_window;
  490.     }

  491.     if (limit > stream->send_window) {
  492.         limit = (stream->send_window > 0) ? stream->send_window : 0;
  493.     }

  494.     if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) {
  495.         cl = ngx_alloc_chain_link(r->pool);
  496.         if (cl == NULL) {
  497.             return NGX_CHAIN_ERROR;
  498.         }

  499.         cl->buf = in->buf;
  500.         in->buf = cl->buf->shadow;

  501.         offset = ngx_buf_in_memory(in->buf)
  502.                  ? (cl->buf->pos - in->buf->pos)
  503.                  : (cl->buf->file_pos - in->buf->file_pos);

  504.         cl->next = stream->free_bufs;
  505.         stream->free_bufs = cl;

  506.     } else {
  507.         offset = 0;
  508.     }

  509. #if (NGX_SUPPRESS_WARN)
  510.     cl = NULL;
  511. #endif

  512.     slcf = ngx_http_get_module_loc_conf(r, ngx_http_spdy_module);

  513.     frame_size = (limit <= (off_t) slcf->chunk_size) ? (size_t) limit
  514.                                                      : slcf->chunk_size;

  515.     for ( ;; ) {
  516.         ln = &out;
  517.         rest = frame_size;

  518.         while ((off_t) rest >= size) {

  519.             if (offset) {
  520.                 cl = ngx_http_spdy_filter_get_shadow(stream, in->buf,
  521.                                                      offset, size);
  522.                 if (cl == NULL) {
  523.                     return NGX_CHAIN_ERROR;
  524.                 }

  525.                 offset = 0;

  526.             } else {
  527.                 cl = ngx_alloc_chain_link(r->pool);
  528.                 if (cl == NULL) {
  529.                     return NGX_CHAIN_ERROR;
  530.                 }

  531.                 cl->buf = in->buf;
  532.             }

  533.             *ln = cl;
  534.             ln = &cl->next;

  535.             rest -= (size_t) size;
  536.             in = in->next;

  537.             if (in == NULL) {
  538.                 frame_size -= rest;
  539.                 rest = 0;
  540.                 break;
  541.             }

  542.             size = ngx_buf_size(in->buf);
  543.         }

  544.         if (rest) {
  545.             cl = ngx_http_spdy_filter_get_shadow(stream, in->buf,
  546.                                                  offset, rest);
  547.             if (cl == NULL) {
  548.                 return NGX_CHAIN_ERROR;
  549.             }

  550.             cl->buf->flush = 0;
  551.             cl->buf->last_buf = 0;

  552.             *ln = cl;

  553.             offset += rest;
  554.             size -= rest;
  555.         }

  556.         frame = ngx_http_spdy_filter_get_data_frame(stream, frame_size,
  557.                                                     out, cl);
  558.         if (frame == NULL) {
  559.             return NGX_CHAIN_ERROR;
  560.         }

  561.         ngx_http_spdy_queue_frame(sc, frame);

  562.         sc->send_window -= frame_size;

  563.         stream->send_window -= frame_size;
  564.         stream->queued++;

  565.         if (in == NULL) {
  566.             break;
  567.         }

  568.         limit -= frame_size;

  569.         if (limit == 0) {
  570.             break;
  571.         }

  572.         if (limit < (off_t) slcf->chunk_size) {
  573.             frame_size = (size_t) limit;
  574.         }
  575.     }

  576.     if (offset) {
  577.         cl = ngx_http_spdy_filter_get_shadow(stream, in->buf, offset, size);
  578.         if (cl == NULL) {
  579.             return NGX_CHAIN_ERROR;
  580.         }

  581.         in->buf = cl->buf;
  582.         ngx_free_chain(r->pool, cl);
  583.     }

  584.     if (ngx_http_spdy_filter_send(fc, stream) == NGX_ERROR) {
  585.         return NGX_CHAIN_ERROR;
  586.     }

  587.     if (in && ngx_http_spdy_flow_control(sc, stream) == NGX_DECLINED) {
  588.         fc->write->delayed = 1;
  589.     }

  590.     return in;
  591. }


  592. static ngx_chain_t *
  593. ngx_http_spdy_filter_get_shadow(ngx_http_spdy_stream_t *stream, ngx_buf_t *buf,
  594.     off_t offset, off_t size)
  595. {
  596.     ngx_buf_t    *chunk;
  597.     ngx_chain_t  *cl;

  598.     cl = ngx_chain_get_free_buf(stream->request->pool, &stream->free_bufs);
  599.     if (cl == NULL) {
  600.         return NULL;
  601.     }

  602.     chunk = cl->buf;

  603.     ngx_memcpy(chunk, buf, sizeof(ngx_buf_t));

  604.     chunk->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow;
  605.     chunk->shadow = buf;

  606.     if (ngx_buf_in_memory(chunk)) {
  607.         chunk->pos += offset;
  608.         chunk->last = chunk->pos + size;
  609.     }

  610.     if (chunk->in_file) {
  611.         chunk->file_pos += offset;
  612.         chunk->file_last = chunk->file_pos + size;
  613.     }

  614.     return cl;
  615. }


  616. static ngx_http_spdy_out_frame_t *
  617. ngx_http_spdy_filter_get_data_frame(ngx_http_spdy_stream_t *stream,
  618.     size_t len, ngx_chain_t *first, ngx_chain_t *last)
  619. {
  620.     u_char                     *p;
  621.     ngx_buf_t                  *buf;
  622.     ngx_uint_t                  flags;
  623.     ngx_chain_t                *cl;
  624.     ngx_http_spdy_out_frame_t  *frame;


  625.     frame = stream->free_frames;

  626.     if (frame) {
  627.         stream->free_frames = frame->next;

  628.     } else {
  629.         frame = ngx_palloc(stream->request->pool,
  630.                            sizeof(ngx_http_spdy_out_frame_t));
  631.         if (frame == NULL) {
  632.             return NULL;
  633.         }
  634.     }

  635.     flags = last->buf->last_buf ? NGX_SPDY_FLAG_FIN : 0;

  636.     ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,
  637.                    "spdy:%ui create DATA frame %p: len:%uz flags:%ui",
  638.                    stream->id, frame, len, flags);

  639.     cl = ngx_chain_get_free_buf(stream->request->pool,
  640.                                 &stream->free_data_headers);
  641.     if (cl == NULL) {
  642.         return NULL;
  643.     }

  644.     buf = cl->buf;

  645.     if (buf->start) {
  646.         p = buf->start;
  647.         buf->pos = p;

  648.         p += NGX_SPDY_SID_SIZE;

  649.         (void) ngx_spdy_frame_write_flags_and_len(p, flags, len);

  650.     } else {
  651.         p = ngx_palloc(stream->request->pool, NGX_SPDY_FRAME_HEADER_SIZE);
  652.         if (p == NULL) {
  653.             return NULL;
  654.         }

  655.         buf->pos = p;
  656.         buf->start = p;

  657.         p = ngx_spdy_frame_write_sid(p, stream->id);
  658.         p = ngx_spdy_frame_write_flags_and_len(p, flags, len);

  659.         buf->last = p;
  660.         buf->end = p;

  661.         buf->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_get_data_frame;
  662.         buf->memory = 1;
  663.     }

  664.     cl->next = first;
  665.     first = cl;

  666.     last->buf->flush = 1;

  667.     frame->first = first;
  668.     frame->last = last;
  669.     frame->handler = ngx_http_spdy_data_frame_handler;
  670.     frame->stream = stream;
  671.     frame->length = len;
  672.     frame->priority = stream->priority;
  673.     frame->blocked = 0;
  674.     frame->fin = last->buf->last_buf;

  675.     return frame;
  676. }


  677. static ngx_inline ngx_int_t
  678. ngx_http_spdy_filter_send(ngx_connection_t *fc, ngx_http_spdy_stream_t *stream)
  679. {
  680.     stream->blocked = 1;

  681.     if (ngx_http_spdy_send_output_queue(stream->connection) == NGX_ERROR) {
  682.         fc->error = 1;
  683.         return NGX_ERROR;
  684.     }

  685.     stream->blocked = 0;

  686.     if (stream->queued) {
  687.         fc->buffered |= NGX_SPDY_BUFFERED;
  688.         fc->write->delayed = 1;
  689.         return NGX_AGAIN;
  690.     }

  691.     fc->buffered &= ~NGX_SPDY_BUFFERED;

  692.     return NGX_OK;
  693. }


  694. static ngx_inline ngx_int_t
  695. ngx_http_spdy_flow_control(ngx_http_spdy_connection_t *sc,
  696.     ngx_http_spdy_stream_t *stream)
  697. {
  698.     if (stream->send_window <= 0) {
  699.         stream->exhausted = 1;
  700.         return NGX_DECLINED;
  701.     }

  702.     if (sc->send_window == 0) {
  703.         ngx_http_spdy_waiting_queue(sc, stream);
  704.         return NGX_DECLINED;
  705.     }

  706.     return NGX_OK;
  707. }


  708. static void
  709. ngx_http_spdy_waiting_queue(ngx_http_spdy_connection_t *sc,
  710.     ngx_http_spdy_stream_t *stream)
  711. {
  712.     ngx_queue_t             *q;
  713.     ngx_http_spdy_stream_t  *s;

  714.     if (stream->handled) {
  715.         return;
  716.     }

  717.     stream->handled = 1;

  718.     for (q = ngx_queue_last(&sc->waiting);
  719.          q != ngx_queue_sentinel(&sc->waiting);
  720.          q = ngx_queue_prev(q))
  721.     {
  722.         s = ngx_queue_data(q, ngx_http_spdy_stream_t, queue);

  723.         /*
  724.          * NB: higher values represent lower priorities.
  725.          */
  726.         if (stream->priority >= s->priority) {
  727.             break;
  728.         }
  729.     }

  730.     ngx_queue_insert_after(q, &stream->queue);
  731. }


  732. static ngx_int_t
  733. ngx_http_spdy_syn_frame_handler(ngx_http_spdy_connection_t *sc,
  734.     ngx_http_spdy_out_frame_t *frame)
  735. {
  736.     ngx_buf_t               *buf;
  737.     ngx_http_spdy_stream_t  *stream;

  738.     buf = frame->first->buf;

  739.     if (buf->pos != buf->last) {
  740.         return NGX_AGAIN;
  741.     }

  742.     stream = frame->stream;

  743.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
  744.                    "spdy:%ui SYN_REPLY frame %p was sent", stream->id, frame);

  745.     ngx_free_chain(stream->request->pool, frame->first);

  746.     ngx_http_spdy_handle_frame(stream, frame);

  747.     ngx_http_spdy_handle_stream(sc, stream);

  748.     return NGX_OK;
  749. }


  750. static ngx_int_t
  751. ngx_http_spdy_data_frame_handler(ngx_http_spdy_connection_t *sc,
  752.     ngx_http_spdy_out_frame_t *frame)
  753. {
  754.     ngx_buf_t               *buf;
  755.     ngx_chain_t             *cl, *ln;
  756.     ngx_http_spdy_stream_t  *stream;

  757.     stream = frame->stream;

  758.     cl = frame->first;

  759.     if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_data_frame) {

  760.         if (cl->buf->pos != cl->buf->last) {
  761.             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
  762.                            "spdy:%ui DATA frame %p was sent partially",
  763.                            stream->id, frame);

  764.             return NGX_AGAIN;
  765.         }

  766.         ln = cl->next;

  767.         cl->next = stream->free_data_headers;
  768.         stream->free_data_headers = cl;

  769.         if (cl == frame->last) {
  770.             goto done;
  771.         }

  772.         cl = ln;
  773.     }

  774.     for ( ;; ) {
  775.         if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) {
  776.             buf = cl->buf->shadow;

  777.             if (ngx_buf_in_memory(buf)) {
  778.                 buf->pos = cl->buf->pos;
  779.             }

  780.             if (buf->in_file) {
  781.                 buf->file_pos = cl->buf->file_pos;
  782.             }
  783.         }

  784.         if (ngx_buf_size(cl->buf) != 0) {

  785.             if (cl != frame->first) {
  786.                 frame->first = cl;
  787.                 ngx_http_spdy_handle_stream(sc, stream);
  788.             }

  789.             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
  790.                            "spdy:%ui DATA frame %p was sent partially",
  791.                            stream->id, frame);

  792.             return NGX_AGAIN;
  793.         }

  794.         ln = cl->next;

  795.         if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) {
  796.             cl->next = stream->free_bufs;
  797.             stream->free_bufs = cl;

  798.         } else {
  799.             ngx_free_chain(stream->request->pool, cl);
  800.         }

  801.         if (cl == frame->last) {
  802.             goto done;
  803.         }

  804.         cl = ln;
  805.     }

  806. done:

  807.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
  808.                    "spdy:%ui DATA frame %p was sent", stream->id, frame);

  809.     stream->request->header_size += NGX_SPDY_FRAME_HEADER_SIZE;

  810.     ngx_http_spdy_handle_frame(stream, frame);

  811.     ngx_http_spdy_handle_stream(sc, stream);

  812.     return NGX_OK;
  813. }


  814. static ngx_inline void
  815. ngx_http_spdy_handle_frame(ngx_http_spdy_stream_t *stream,
  816.     ngx_http_spdy_out_frame_t *frame)
  817. {
  818.     ngx_http_request_t  *r;

  819.     r = stream->request;

  820.     r->connection->sent += NGX_SPDY_FRAME_HEADER_SIZE + frame->length;

  821.     if (frame->fin) {
  822.         stream->out_closed = 1;
  823.     }

  824.     frame->next = stream->free_frames;
  825.     stream->free_frames = frame;

  826.     stream->queued--;
  827. }


  828. static ngx_inline void
  829. ngx_http_spdy_handle_stream(ngx_http_spdy_connection_t *sc,
  830.     ngx_http_spdy_stream_t *stream)
  831. {
  832.     ngx_event_t  *wev;

  833.     if (stream->handled || stream->blocked || stream->exhausted) {
  834.         return;
  835.     }

  836.     wev = stream->request->connection->write;

  837.     /*
  838.      * This timer can only be set if the stream was delayed because of rate
  839.      * limit.  In that case the event should be triggered by the timer.
  840.      */

  841.     if (!wev->timer_set) {
  842.         wev->delayed = 0;

  843.         stream->handled = 1;
  844.         ngx_queue_insert_tail(&sc->posted, &stream->queue);
  845.     }
  846. }


  847. static void
  848. ngx_http_spdy_filter_cleanup(void *data)
  849. {
  850.     ngx_http_spdy_stream_t *stream = data;

  851.     size_t                       delta;
  852.     ngx_http_spdy_out_frame_t   *frame, **fn;
  853.     ngx_http_spdy_connection_t  *sc;

  854.     if (stream->handled) {
  855.         stream->handled = 0;
  856.         ngx_queue_remove(&stream->queue);
  857.     }

  858.     if (stream->queued == 0) {
  859.         return;
  860.     }

  861.     delta = 0;
  862.     sc = stream->connection;
  863.     fn = &sc->last_out;

  864.     for ( ;; ) {
  865.         frame = *fn;

  866.         if (frame == NULL) {
  867.             break;
  868.         }

  869.         if (frame->stream == stream && !frame->blocked) {
  870.             *fn = frame->next;

  871.             delta += frame->length;

  872.             if (--stream->queued == 0) {
  873.                 break;
  874.             }

  875.             continue;
  876.         }

  877.         fn = &frame->next;
  878.     }

  879.     if (sc->send_window == 0 && delta && !ngx_queue_empty(&sc->waiting)) {
  880.         ngx_queue_add(&sc->posted, &sc->waiting);
  881.         ngx_queue_init(&sc->waiting);
  882.     }

  883.     sc->send_window += delta;
  884. }


  885. static ngx_int_t
  886. ngx_http_spdy_filter_init(ngx_conf_t *cf)
  887. {
  888.     ngx_http_next_header_filter = ngx_http_top_header_filter;
  889.     ngx_http_top_header_filter = ngx_http_spdy_header_filter;

  890.     return NGX_OK;
  891. }