Annotation of embedaddon/nginx/src/http/ngx_http_spdy_filter_module.c, revision 1.1

1.1     ! misho       1: 
        !             2: /*
        !             3:  * Copyright (C) Nginx, Inc.
        !             4:  * Copyright (C) Valentin V. Bartenev
        !             5:  */
        !             6: 
        !             7: 
        !             8: #include <ngx_config.h>
        !             9: #include <ngx_core.h>
        !            10: #include <ngx_http.h>
        !            11: #include <nginx.h>
        !            12: #include <ngx_http_spdy_module.h>
        !            13: 
        !            14: #include <zlib.h>
        !            15: 
        !            16: 
        !            17: #define NGX_SPDY_WRITE_BUFFERED  NGX_HTTP_WRITE_BUFFERED
        !            18: 
        !            19: #define ngx_http_spdy_nv_nsize(h)  (NGX_SPDY_NV_NLEN_SIZE + sizeof(h) - 1)
        !            20: #define ngx_http_spdy_nv_vsize(h)  (NGX_SPDY_NV_VLEN_SIZE + sizeof(h) - 1)
        !            21: 
        !            22: #define ngx_http_spdy_nv_write_num   ngx_spdy_frame_write_uint16
        !            23: #define ngx_http_spdy_nv_write_nlen  ngx_spdy_frame_write_uint16
        !            24: #define ngx_http_spdy_nv_write_vlen  ngx_spdy_frame_write_uint16
        !            25: 
        !            26: #define ngx_http_spdy_nv_write_name(p, h)                                     \
        !            27:     ngx_cpymem(ngx_http_spdy_nv_write_nlen(p, sizeof(h) - 1), h, sizeof(h) - 1)
        !            28: 
        !            29: #define ngx_http_spdy_nv_write_val(p, h)                                      \
        !            30:     ngx_cpymem(ngx_http_spdy_nv_write_vlen(p, sizeof(h) - 1), h, sizeof(h) - 1)
        !            31: 
        !            32: static ngx_inline ngx_int_t ngx_http_spdy_filter_send(
        !            33:     ngx_connection_t *fc, ngx_http_spdy_stream_t *stream);
        !            34: 
        !            35: static ngx_http_spdy_out_frame_t *ngx_http_spdy_filter_get_data_frame(
        !            36:     ngx_http_spdy_stream_t *stream, size_t len, ngx_uint_t flags,
        !            37:     ngx_chain_t *first, ngx_chain_t *last);
        !            38: 
        !            39: static ngx_int_t ngx_http_spdy_syn_frame_handler(
        !            40:     ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);
        !            41: static ngx_int_t ngx_http_spdy_data_frame_handler(
        !            42:     ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);
        !            43: static ngx_inline void ngx_http_spdy_handle_frame(
        !            44:     ngx_http_spdy_stream_t *stream, ngx_http_spdy_out_frame_t *frame);
        !            45: static ngx_inline void ngx_http_spdy_handle_stream(
        !            46:     ngx_http_spdy_connection_t *sc, ngx_http_spdy_stream_t *stream);
        !            47: 
        !            48: static void ngx_http_spdy_filter_cleanup(void *data);
        !            49: 
        !            50: static ngx_int_t ngx_http_spdy_filter_init(ngx_conf_t *cf);
        !            51: 
        !            52: 
        !            53: static ngx_http_module_t  ngx_http_spdy_filter_module_ctx = {
        !            54:     NULL,                                  /* preconfiguration */
        !            55:     ngx_http_spdy_filter_init,             /* postconfiguration */
        !            56: 
        !            57:     NULL,                                  /* create main configuration */
        !            58:     NULL,                                  /* init main configuration */
        !            59: 
        !            60:     NULL,                                  /* create server configuration */
        !            61:     NULL,                                  /* merge server configuration */
        !            62: 
        !            63:     NULL,                                  /* create location configuration */
        !            64:     NULL                                   /* merge location configuration */
        !            65: };
        !            66: 
        !            67: 
        !            68: ngx_module_t  ngx_http_spdy_filter_module = {
        !            69:     NGX_MODULE_V1,
        !            70:     &ngx_http_spdy_filter_module_ctx,      /* module context */
        !            71:     NULL,                                  /* module directives */
        !            72:     NGX_HTTP_MODULE,                       /* module type */
        !            73:     NULL,                                  /* init master */
        !            74:     NULL,                                  /* init module */
        !            75:     NULL,                                  /* init process */
        !            76:     NULL,                                  /* init thread */
        !            77:     NULL,                                  /* exit thread */
        !            78:     NULL,                                  /* exit process */
        !            79:     NULL,                                  /* exit master */
        !            80:     NGX_MODULE_V1_PADDING
        !            81: };
        !            82: 
        !            83: 
        !            84: static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
        !            85: static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
        !            86: 
        !            87: 
        !            88: static ngx_int_t
        !            89: ngx_http_spdy_header_filter(ngx_http_request_t *r)
        !            90: {
        !            91:     int                           rc;
        !            92:     size_t                        len;
        !            93:     u_char                       *p, *buf, *last;
        !            94:     ngx_buf_t                    *b;
        !            95:     ngx_str_t                     host;
        !            96:     ngx_uint_t                    i, j, count, port;
        !            97:     ngx_chain_t                  *cl;
        !            98:     ngx_list_part_t              *part, *pt;
        !            99:     ngx_table_elt_t              *header, *h;
        !           100:     ngx_connection_t             *c;
        !           101:     ngx_http_cleanup_t           *cln;
        !           102:     ngx_http_core_loc_conf_t     *clcf;
        !           103:     ngx_http_core_srv_conf_t     *cscf;
        !           104:     ngx_http_spdy_stream_t       *stream;
        !           105:     ngx_http_spdy_out_frame_t    *frame;
        !           106:     ngx_http_spdy_connection_t   *sc;
        !           107:     struct sockaddr_in           *sin;
        !           108: #if (NGX_HAVE_INET6)
        !           109:     struct sockaddr_in6          *sin6;
        !           110: #endif
        !           111:     u_char                        addr[NGX_SOCKADDR_STRLEN];
        !           112: 
        !           113:     if (!r->spdy_stream) {
        !           114:         return ngx_http_next_header_filter(r);
        !           115:     }
        !           116: 
        !           117:     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
        !           118:                    "spdy header filter");
        !           119: 
        !           120:     if (r->header_sent) {
        !           121:         return NGX_OK;
        !           122:     }
        !           123: 
        !           124:     r->header_sent = 1;
        !           125: 
        !           126:     if (r != r->main) {
        !           127:         return NGX_OK;
        !           128:     }
        !           129: 
        !           130:     c = r->connection;
        !           131: 
        !           132:     if (r->method == NGX_HTTP_HEAD) {
        !           133:         r->header_only = 1;
        !           134:     }
        !           135: 
        !           136:     switch (r->headers_out.status) {
        !           137: 
        !           138:     case NGX_HTTP_OK:
        !           139:     case NGX_HTTP_PARTIAL_CONTENT:
        !           140:         break;
        !           141: 
        !           142:     case NGX_HTTP_NOT_MODIFIED:
        !           143:         r->header_only = 1;
        !           144:         break;
        !           145: 
        !           146:     case NGX_HTTP_NO_CONTENT:
        !           147:         r->header_only = 1;
        !           148: 
        !           149:         ngx_str_null(&r->headers_out.content_type);
        !           150: 
        !           151:         r->headers_out.content_length = NULL;
        !           152:         r->headers_out.content_length_n = -1;
        !           153: 
        !           154:         /* fall through */
        !           155: 
        !           156:     default:
        !           157:         r->headers_out.last_modified_time = -1;
        !           158:         r->headers_out.last_modified = NULL;
        !           159:     }
        !           160: 
        !           161:     len = NGX_SPDY_NV_NUM_SIZE
        !           162:           + ngx_http_spdy_nv_nsize("version")
        !           163:           + ngx_http_spdy_nv_vsize("HTTP/1.1")
        !           164:           + ngx_http_spdy_nv_nsize("status")
        !           165:           + ngx_http_spdy_nv_vsize("418");
        !           166: 
        !           167:     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
        !           168: 
        !           169:     if (r->headers_out.server == NULL) {
        !           170:         len += ngx_http_spdy_nv_nsize("server");
        !           171:         len += clcf->server_tokens ? ngx_http_spdy_nv_vsize(NGINX_VER)
        !           172:                                    : ngx_http_spdy_nv_vsize("nginx");
        !           173:     }
        !           174: 
        !           175:     if (r->headers_out.date == NULL) {
        !           176:         len += ngx_http_spdy_nv_nsize("date")
        !           177:                + ngx_http_spdy_nv_vsize("Wed, 31 Dec 1986 10:00:00 GMT");
        !           178:     }
        !           179: 
        !           180:     if (r->headers_out.content_type.len) {
        !           181:         len += ngx_http_spdy_nv_nsize("content-type")
        !           182:                + NGX_SPDY_NV_VLEN_SIZE + r->headers_out.content_type.len;
        !           183: 
        !           184:         if (r->headers_out.content_type_len == r->headers_out.content_type.len
        !           185:             && r->headers_out.charset.len)
        !           186:         {
        !           187:             len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
        !           188:         }
        !           189:     }
        !           190: 
        !           191:     if (r->headers_out.content_length == NULL
        !           192:         && r->headers_out.content_length_n >= 0)
        !           193:     {
        !           194:         len += ngx_http_spdy_nv_nsize("content-length")
        !           195:                + NGX_SPDY_NV_VLEN_SIZE + NGX_OFF_T_LEN;
        !           196:     }
        !           197: 
        !           198:     if (r->headers_out.last_modified == NULL
        !           199:         && r->headers_out.last_modified_time != -1)
        !           200:     {
        !           201:         len += ngx_http_spdy_nv_nsize("last-modified")
        !           202:                + ngx_http_spdy_nv_vsize("Wed, 31 Dec 1986 10:00:00 GMT");
        !           203:     }
        !           204: 
        !           205:     if (r->headers_out.location
        !           206:         && r->headers_out.location->value.len
        !           207:         && r->headers_out.location->value.data[0] == '/')
        !           208:     {
        !           209:         r->headers_out.location->hash = 0;
        !           210: 
        !           211:         if (clcf->server_name_in_redirect) {
        !           212:             cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
        !           213:             host = cscf->server_name;
        !           214: 
        !           215:         } else if (r->headers_in.server.len) {
        !           216:             host = r->headers_in.server;
        !           217: 
        !           218:         } else {
        !           219:             host.len = NGX_SOCKADDR_STRLEN;
        !           220:             host.data = addr;
        !           221: 
        !           222:             if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) {
        !           223:                 return NGX_ERROR;
        !           224:             }
        !           225:         }
        !           226: 
        !           227:         switch (c->local_sockaddr->sa_family) {
        !           228: 
        !           229: #if (NGX_HAVE_INET6)
        !           230:         case AF_INET6:
        !           231:             sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
        !           232:             port = ntohs(sin6->sin6_port);
        !           233:             break;
        !           234: #endif
        !           235: #if (NGX_HAVE_UNIX_DOMAIN)
        !           236:         case AF_UNIX:
        !           237:             port = 0;
        !           238:             break;
        !           239: #endif
        !           240:         default: /* AF_INET */
        !           241:             sin = (struct sockaddr_in *) c->local_sockaddr;
        !           242:             port = ntohs(sin->sin_port);
        !           243:             break;
        !           244:         }
        !           245: 
        !           246:         len += ngx_http_spdy_nv_nsize("location")
        !           247:                + ngx_http_spdy_nv_vsize("https://")
        !           248:                + host.len
        !           249:                + r->headers_out.location->value.len;
        !           250: 
        !           251:         if (clcf->port_in_redirect) {
        !           252: 
        !           253: #if (NGX_HTTP_SSL)
        !           254:             if (c->ssl)
        !           255:                 port = (port == 443) ? 0 : port;
        !           256:             else
        !           257: #endif
        !           258:                 port = (port == 80) ? 0 : port;
        !           259: 
        !           260:         } else {
        !           261:             port = 0;
        !           262:         }
        !           263: 
        !           264:         if (port) {
        !           265:             len += sizeof(":65535") - 1;
        !           266:         }
        !           267: 
        !           268:     } else {
        !           269:         ngx_str_null(&host);
        !           270:         port = 0;
        !           271:     }
        !           272: 
        !           273:     part = &r->headers_out.headers.part;
        !           274:     header = part->elts;
        !           275: 
        !           276:     for (i = 0; /* void */; i++) {
        !           277: 
        !           278:         if (i >= part->nelts) {
        !           279:             if (part->next == NULL) {
        !           280:                 break;
        !           281:             }
        !           282: 
        !           283:             part = part->next;
        !           284:             header = part->elts;
        !           285:             i = 0;
        !           286:         }
        !           287: 
        !           288:         if (header[i].hash == 0) {
        !           289:             continue;
        !           290:         }
        !           291: 
        !           292:         len += NGX_SPDY_NV_NLEN_SIZE + header[i].key.len
        !           293:                + NGX_SPDY_NV_VLEN_SIZE  + header[i].value.len;
        !           294:     }
        !           295: 
        !           296:     buf = ngx_alloc(len, r->pool->log);
        !           297:     if (buf == NULL) {
        !           298:         return NGX_ERROR;
        !           299:     }
        !           300: 
        !           301:     last = buf + NGX_SPDY_NV_NUM_SIZE;
        !           302: 
        !           303:     last = ngx_http_spdy_nv_write_name(last, "version");
        !           304:     last = ngx_http_spdy_nv_write_val(last, "HTTP/1.1");
        !           305: 
        !           306:     last = ngx_http_spdy_nv_write_name(last, "status");
        !           307:     last = ngx_spdy_frame_write_uint16(last, 3);
        !           308:     last = ngx_sprintf(last, "%03ui", r->headers_out.status);
        !           309: 
        !           310:     count = 2;
        !           311: 
        !           312:     if (r->headers_out.server == NULL) {
        !           313:         last = ngx_http_spdy_nv_write_name(last, "server");
        !           314:         last = clcf->server_tokens
        !           315:                ? ngx_http_spdy_nv_write_val(last, NGINX_VER)
        !           316:                : ngx_http_spdy_nv_write_val(last, "nginx");
        !           317: 
        !           318:         count++;
        !           319:     }
        !           320: 
        !           321:     if (r->headers_out.date == NULL) {
        !           322:         last = ngx_http_spdy_nv_write_name(last, "date");
        !           323: 
        !           324:         last = ngx_http_spdy_nv_write_vlen(last, ngx_cached_http_time.len);
        !           325: 
        !           326:         last = ngx_cpymem(last, ngx_cached_http_time.data,
        !           327:                           ngx_cached_http_time.len);
        !           328: 
        !           329:         count++;
        !           330:     }
        !           331: 
        !           332:     if (r->headers_out.content_type.len) {
        !           333: 
        !           334:         last = ngx_http_spdy_nv_write_name(last, "content-type");
        !           335: 
        !           336:         p = last + NGX_SPDY_NV_VLEN_SIZE;
        !           337: 
        !           338:         last = ngx_cpymem(p, r->headers_out.content_type.data,
        !           339:                           r->headers_out.content_type.len);
        !           340: 
        !           341:         if (r->headers_out.content_type_len == r->headers_out.content_type.len
        !           342:             && r->headers_out.charset.len)
        !           343:         {
        !           344:             last = ngx_cpymem(last, "; charset=", sizeof("; charset=") - 1);
        !           345: 
        !           346:             last = ngx_cpymem(last, r->headers_out.charset.data,
        !           347:                               r->headers_out.charset.len);
        !           348: 
        !           349:             /* update r->headers_out.content_type for possible logging */
        !           350: 
        !           351:             r->headers_out.content_type.len = last - p;
        !           352:             r->headers_out.content_type.data = p;
        !           353:         }
        !           354: 
        !           355:         (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
        !           356:                                            r->headers_out.content_type.len);
        !           357: 
        !           358:         count++;
        !           359:     }
        !           360: 
        !           361:     if (r->headers_out.content_length == NULL
        !           362:         && r->headers_out.content_length_n >= 0)
        !           363:     {
        !           364:         last = ngx_http_spdy_nv_write_name(last, "content-length");
        !           365: 
        !           366:         p = last + NGX_SPDY_NV_VLEN_SIZE;
        !           367: 
        !           368:         last = ngx_sprintf(p, "%O", r->headers_out.content_length_n);
        !           369: 
        !           370:         (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
        !           371:                                            last - p);
        !           372: 
        !           373:         count++;
        !           374:     }
        !           375: 
        !           376:     if (r->headers_out.last_modified == NULL
        !           377:         && r->headers_out.last_modified_time != -1)
        !           378:     {
        !           379:         last = ngx_http_spdy_nv_write_name(last, "last-modified");
        !           380: 
        !           381:         p = last + NGX_SPDY_NV_VLEN_SIZE;
        !           382: 
        !           383:         last = ngx_http_time(p, r->headers_out.last_modified_time);
        !           384: 
        !           385:         (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
        !           386:                                            last - p);
        !           387: 
        !           388:         count++;
        !           389:     }
        !           390: 
        !           391:     if (host.data) {
        !           392: 
        !           393:         last = ngx_http_spdy_nv_write_name(last, "location");
        !           394: 
        !           395:         p = last + NGX_SPDY_NV_VLEN_SIZE;
        !           396: 
        !           397:         last = ngx_cpymem(p, "http", sizeof("http") - 1);
        !           398: 
        !           399: #if (NGX_HTTP_SSL)
        !           400:         if (c->ssl) {
        !           401:             *last++ ='s';
        !           402:         }
        !           403: #endif
        !           404: 
        !           405:         *last++ = ':'; *last++ = '/'; *last++ = '/';
        !           406: 
        !           407:         last = ngx_cpymem(last, host.data, host.len);
        !           408: 
        !           409:         if (port) {
        !           410:             last = ngx_sprintf(last, ":%ui", port);
        !           411:         }
        !           412: 
        !           413:         last = ngx_cpymem(last, r->headers_out.location->value.data,
        !           414:                           r->headers_out.location->value.len);
        !           415: 
        !           416:         /* update r->headers_out.location->value for possible logging */
        !           417: 
        !           418:         r->headers_out.location->value.len = last - p;
        !           419:         r->headers_out.location->value.data = p;
        !           420:         ngx_str_set(&r->headers_out.location->key, "location");
        !           421: 
        !           422:         (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
        !           423:                                            r->headers_out.location->value.len);
        !           424: 
        !           425:         count++;
        !           426:     }
        !           427: 
        !           428:     part = &r->headers_out.headers.part;
        !           429:     header = part->elts;
        !           430: 
        !           431:     for (i = 0; /* void */; i++) {
        !           432: 
        !           433:         if (i >= part->nelts) {
        !           434:             if (part->next == NULL) {
        !           435:                 break;
        !           436:             }
        !           437: 
        !           438:             part = part->next;
        !           439:             header = part->elts;
        !           440:             i = 0;
        !           441:         }
        !           442: 
        !           443:         if (header[i].hash == 0 || header[i].hash == 2) {
        !           444:             continue;
        !           445:         }
        !           446: 
        !           447:         if ((header[i].key.len == 6
        !           448:              && ngx_strncasecmp(header[i].key.data,
        !           449:                                 (u_char *) "status", 6) == 0)
        !           450:             || (header[i].key.len == 7
        !           451:                 && ngx_strncasecmp(header[i].key.data,
        !           452:                                    (u_char *) "version", 7) == 0))
        !           453:         {
        !           454:             header[i].hash = 0;
        !           455:             continue;
        !           456:         }
        !           457: 
        !           458:         last = ngx_http_spdy_nv_write_nlen(last, header[i].key.len);
        !           459: 
        !           460:         ngx_strlow(last, header[i].key.data, header[i].key.len);
        !           461:         last += header[i].key.len;
        !           462: 
        !           463:         p = last + NGX_SPDY_NV_VLEN_SIZE;
        !           464: 
        !           465:         last = ngx_cpymem(p, header[i].value.data, header[i].value.len);
        !           466: 
        !           467:         pt = part;
        !           468:         h = header;
        !           469: 
        !           470:         for (j = i + 1; /* void */; j++) {
        !           471: 
        !           472:             if (j >= pt->nelts) {
        !           473:                 if (pt->next == NULL) {
        !           474:                     break;
        !           475:                 }
        !           476: 
        !           477:                 pt = pt->next;
        !           478:                 h = pt->elts;
        !           479:                 j = 0;
        !           480:             }
        !           481: 
        !           482:             if (h[j].hash == 0 || h[j].hash == 2
        !           483:                 || h[j].key.len != header[i].key.len
        !           484:                 || ngx_strncasecmp(header[i].key.data, h[j].key.data,
        !           485:                                    header[i].key.len))
        !           486:             {
        !           487:                 continue;
        !           488:             }
        !           489: 
        !           490:             *last++ = '\0';
        !           491: 
        !           492:             last = ngx_cpymem(last, h[j].value.data, h[j].value.len);
        !           493: 
        !           494:             h[j].hash = 2;
        !           495:         }
        !           496: 
        !           497:         (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
        !           498:                                            last - p);
        !           499: 
        !           500:         count++;
        !           501:     }
        !           502: 
        !           503:     (void) ngx_spdy_frame_write_uint16(buf, count);
        !           504: 
        !           505:     stream = r->spdy_stream;
        !           506:     sc = stream->connection;
        !           507: 
        !           508:     len = last - buf;
        !           509: 
        !           510:     b = ngx_create_temp_buf(r->pool, NGX_SPDY_FRAME_HEADER_SIZE
        !           511:                                      + NGX_SPDY_SYN_REPLY_SIZE
        !           512:                                      + deflateBound(&sc->zstream_out, len));
        !           513:     if (b == NULL) {
        !           514:         ngx_free(buf);
        !           515:         return NGX_ERROR;
        !           516:     }
        !           517: 
        !           518:     b->last += NGX_SPDY_FRAME_HEADER_SIZE + NGX_SPDY_SYN_REPLY_SIZE;
        !           519: 
        !           520:     sc->zstream_out.next_in = buf;
        !           521:     sc->zstream_out.avail_in = len;
        !           522:     sc->zstream_out.next_out = b->last;
        !           523:     sc->zstream_out.avail_out = b->end - b->last;
        !           524: 
        !           525:     rc = deflate(&sc->zstream_out, Z_SYNC_FLUSH);
        !           526: 
        !           527:     ngx_free(buf);
        !           528: 
        !           529:     if (rc != Z_OK) {
        !           530:         ngx_log_error(NGX_LOG_ALERT, c->log, 0,
        !           531:                       "spdy deflate() failed: %d", rc);
        !           532:         return NGX_ERROR;
        !           533:     }
        !           534: 
        !           535:     ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,
        !           536:                    "spdy deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
        !           537:                    sc->zstream_out.next_in, sc->zstream_out.next_out,
        !           538:                    sc->zstream_out.avail_in, sc->zstream_out.avail_out,
        !           539:                    rc);
        !           540: 
        !           541:     b->last = sc->zstream_out.next_out;
        !           542: 
        !           543:     p = b->pos;
        !           544:     p = ngx_spdy_frame_write_head(p, NGX_SPDY_SYN_REPLY);
        !           545: 
        !           546:     len = b->last - b->pos;
        !           547: 
        !           548:     r->header_size = len;
        !           549: 
        !           550:     if (r->header_only) {
        !           551:         b->last_buf = 1;
        !           552:         p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_FIN,
        !           553:                                              len - NGX_SPDY_FRAME_HEADER_SIZE);
        !           554:     } else {
        !           555:         p = ngx_spdy_frame_write_flags_and_len(p, 0,
        !           556:                                              len - NGX_SPDY_FRAME_HEADER_SIZE);
        !           557:     }
        !           558: 
        !           559:     (void) ngx_spdy_frame_write_sid(p, stream->id);
        !           560: 
        !           561:     cl = ngx_alloc_chain_link(r->pool);
        !           562:     if (cl == NULL) {
        !           563:         return NGX_ERROR;
        !           564:     }
        !           565: 
        !           566:     cl->buf = b;
        !           567:     cl->next = NULL;
        !           568: 
        !           569:     frame = ngx_palloc(r->pool, sizeof(ngx_http_spdy_out_frame_t));
        !           570:     if (frame == NULL) {
        !           571:         return NGX_ERROR;
        !           572:     }
        !           573: 
        !           574:     frame->first = cl;
        !           575:     frame->last = cl;
        !           576:     frame->handler = ngx_http_spdy_syn_frame_handler;
        !           577:     frame->free = NULL;
        !           578:     frame->stream = stream;
        !           579:     frame->size = len;
        !           580:     frame->priority = stream->priority;
        !           581:     frame->blocked = 1;
        !           582:     frame->fin = r->header_only;
        !           583: 
        !           584:     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,
        !           585:                    "spdy:%ui create SYN_REPLY frame %p: size:%uz",
        !           586:                    stream->id, frame, frame->size);
        !           587: 
        !           588:     ngx_http_spdy_queue_blocked_frame(sc, frame);
        !           589: 
        !           590:     r->blocked++;
        !           591: 
        !           592:     cln = ngx_http_cleanup_add(r, 0);
        !           593:     if (cln == NULL) {
        !           594:         return NGX_ERROR;
        !           595:     }
        !           596: 
        !           597:     cln->handler = ngx_http_spdy_filter_cleanup;
        !           598:     cln->data = stream;
        !           599: 
        !           600:     stream->waiting = 1;
        !           601: 
        !           602:     return ngx_http_spdy_filter_send(c, stream);
        !           603: }
        !           604: 
        !           605: 
        !           606: static ngx_int_t
        !           607: ngx_http_spdy_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
        !           608: {
        !           609:     off_t                       size;
        !           610:     ngx_buf_t                  *b;
        !           611:     ngx_chain_t                *cl, *ll, *out, **ln;
        !           612:     ngx_http_spdy_stream_t     *stream;
        !           613:     ngx_http_spdy_out_frame_t  *frame;
        !           614: 
        !           615:     stream = r->spdy_stream;
        !           616: 
        !           617:     if (stream == NULL) {
        !           618:         return ngx_http_next_body_filter(r, in);
        !           619:     }
        !           620: 
        !           621:     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
        !           622:                    "spdy body filter \"%V?%V\"", &r->uri, &r->args);
        !           623: 
        !           624:     if (in == NULL || r->header_only) {
        !           625: 
        !           626:         if (stream->waiting) {
        !           627:             return NGX_AGAIN;
        !           628:         }
        !           629: 
        !           630:         r->connection->buffered &= ~NGX_SPDY_WRITE_BUFFERED;
        !           631: 
        !           632:         return NGX_OK;
        !           633:     }
        !           634: 
        !           635:     size = 0;
        !           636:     ln = &out;
        !           637:     ll = in;
        !           638: 
        !           639:     for ( ;; ) {
        !           640:         b = ll->buf;
        !           641: #if 1
        !           642:         if (ngx_buf_size(b) == 0 && !ngx_buf_special(b)) {
        !           643:             ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
        !           644:                           "zero size buf in spdy body filter "
        !           645:                           "t:%d r:%d f:%d %p %p-%p %p %O-%O",
        !           646:                           b->temporary,
        !           647:                           b->recycled,
        !           648:                           b->in_file,
        !           649:                           b->start,
        !           650:                           b->pos,
        !           651:                           b->last,
        !           652:                           b->file,
        !           653:                           b->file_pos,
        !           654:                           b->file_last);
        !           655: 
        !           656:             ngx_debug_point();
        !           657:             return NGX_ERROR;
        !           658:         }
        !           659: #endif
        !           660:         cl = ngx_alloc_chain_link(r->pool);
        !           661:         if (cl == NULL) {
        !           662:             return NGX_ERROR;
        !           663:         }
        !           664: 
        !           665:         size += ngx_buf_size(b);
        !           666:         cl->buf = b;
        !           667: 
        !           668:         *ln = cl;
        !           669:         ln = &cl->next;
        !           670: 
        !           671:         if (ll->next == NULL) {
        !           672:             break;
        !           673:         }
        !           674: 
        !           675:         ll = ll->next;
        !           676:     }
        !           677: 
        !           678:     if (size > NGX_SPDY_MAX_FRAME_SIZE) {
        !           679:         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
        !           680:                       "FIXME: chain too big in spdy filter: %O", size);
        !           681:         return NGX_ERROR;
        !           682:     }
        !           683: 
        !           684:     frame = ngx_http_spdy_filter_get_data_frame(stream, (size_t) size,
        !           685:                                                 b->last_buf, out, cl);
        !           686:     if (frame == NULL) {
        !           687:         return NGX_ERROR;
        !           688:     }
        !           689: 
        !           690:     ngx_http_spdy_queue_frame(stream->connection, frame);
        !           691: 
        !           692:     stream->waiting++;
        !           693: 
        !           694:     r->main->blocked++;
        !           695: 
        !           696:     return ngx_http_spdy_filter_send(r->connection, stream);
        !           697: }
        !           698: 
        !           699: 
        !           700: static ngx_http_spdy_out_frame_t *
        !           701: ngx_http_spdy_filter_get_data_frame(ngx_http_spdy_stream_t *stream,
        !           702:     size_t len, ngx_uint_t fin, ngx_chain_t *first, ngx_chain_t *last)
        !           703: {
        !           704:     u_char                     *p;
        !           705:     ngx_buf_t                  *buf;
        !           706:     ngx_uint_t                  flags;
        !           707:     ngx_chain_t                *cl;
        !           708:     ngx_http_spdy_out_frame_t  *frame;
        !           709: 
        !           710: 
        !           711:     frame = stream->free_frames;
        !           712: 
        !           713:     if (frame) {
        !           714:         stream->free_frames = frame->free;
        !           715: 
        !           716:     } else {
        !           717:         frame = ngx_palloc(stream->request->pool,
        !           718:                            sizeof(ngx_http_spdy_out_frame_t));
        !           719:         if (frame == NULL) {
        !           720:             return NULL;
        !           721:         }
        !           722:     }
        !           723: 
        !           724:     ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,
        !           725:                    "spdy:%ui create DATA frame %p: len:%uz fin:%ui",
        !           726:                    stream->id, frame, len, fin);
        !           727: 
        !           728:     if (len || fin) {
        !           729: 
        !           730:         flags = fin ? NGX_SPDY_FLAG_FIN : 0;
        !           731: 
        !           732:         cl = ngx_chain_get_free_buf(stream->request->pool,
        !           733:                                     &stream->free_data_headers);
        !           734:         if (cl == NULL) {
        !           735:             return NULL;
        !           736:         }
        !           737: 
        !           738:         buf = cl->buf;
        !           739: 
        !           740:         if (buf->start) {
        !           741:             p = buf->start;
        !           742:             buf->pos = p;
        !           743: 
        !           744:             p += sizeof(uint32_t);
        !           745: 
        !           746:             (void) ngx_spdy_frame_write_flags_and_len(p, flags, len);
        !           747: 
        !           748:         } else {
        !           749:             p = ngx_palloc(stream->request->pool, NGX_SPDY_FRAME_HEADER_SIZE);
        !           750:             if (p == NULL) {
        !           751:                 return NULL;
        !           752:             }
        !           753: 
        !           754:             buf->pos = p;
        !           755:             buf->start = p;
        !           756: 
        !           757:             p = ngx_spdy_frame_write_sid(p, stream->id);
        !           758:             p = ngx_spdy_frame_write_flags_and_len(p, flags, len);
        !           759: 
        !           760:             buf->last = p;
        !           761:             buf->end = p;
        !           762: 
        !           763:             buf->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_module;
        !           764:             buf->memory = 1;
        !           765:         }
        !           766: 
        !           767:         cl->next = first;
        !           768:         first = cl;
        !           769:     }
        !           770: 
        !           771:     frame->first = first;
        !           772:     frame->last = last;
        !           773:     frame->handler = ngx_http_spdy_data_frame_handler;
        !           774:     frame->free = NULL;
        !           775:     frame->stream = stream;
        !           776:     frame->size = NGX_SPDY_FRAME_HEADER_SIZE + len;
        !           777:     frame->priority = stream->priority;
        !           778:     frame->blocked = 0;
        !           779:     frame->fin = fin;
        !           780: 
        !           781:     return frame;
        !           782: }
        !           783: 
        !           784: 
        !           785: static ngx_inline ngx_int_t
        !           786: ngx_http_spdy_filter_send(ngx_connection_t *fc, ngx_http_spdy_stream_t *stream)
        !           787: {
        !           788:     if (ngx_http_spdy_send_output_queue(stream->connection) == NGX_ERROR) {
        !           789:         fc->error = 1;
        !           790:         return NGX_ERROR;
        !           791:     }
        !           792: 
        !           793:     if (stream->waiting) {
        !           794:         fc->buffered |= NGX_SPDY_WRITE_BUFFERED;
        !           795:         fc->write->delayed = 1;
        !           796:         return NGX_AGAIN;
        !           797:     }
        !           798: 
        !           799:     fc->buffered &= ~NGX_SPDY_WRITE_BUFFERED;
        !           800: 
        !           801:     return NGX_OK;
        !           802: }
        !           803: 
        !           804: 
        !           805: static ngx_int_t
        !           806: ngx_http_spdy_syn_frame_handler(ngx_http_spdy_connection_t *sc,
        !           807:     ngx_http_spdy_out_frame_t *frame)
        !           808: {
        !           809:     ngx_buf_t               *buf;
        !           810:     ngx_http_spdy_stream_t  *stream;
        !           811: 
        !           812:     buf = frame->first->buf;
        !           813: 
        !           814:     if (buf->pos != buf->last) {
        !           815:         return NGX_AGAIN;
        !           816:     }
        !           817: 
        !           818:     stream = frame->stream;
        !           819: 
        !           820:     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
        !           821:                    "spdy:%ui SYN_REPLY frame %p was sent", stream->id, frame);
        !           822: 
        !           823:     ngx_free_chain(stream->request->pool, frame->first);
        !           824: 
        !           825:     ngx_http_spdy_handle_frame(stream, frame);
        !           826: 
        !           827:     ngx_http_spdy_handle_stream(sc, stream);
        !           828: 
        !           829:     return NGX_OK;
        !           830: }
        !           831: 
        !           832: 
        !           833: static ngx_int_t
        !           834: ngx_http_spdy_data_frame_handler(ngx_http_spdy_connection_t *sc,
        !           835:     ngx_http_spdy_out_frame_t *frame)
        !           836: {
        !           837:     ngx_chain_t             *cl, *ln;
        !           838:     ngx_http_spdy_stream_t  *stream;
        !           839: 
        !           840:     stream = frame->stream;
        !           841: 
        !           842:     cl = frame->first;
        !           843: 
        !           844:     if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_module) {
        !           845: 
        !           846:         if (cl->buf->pos != cl->buf->last) {
        !           847:             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
        !           848:                            "spdy:%ui DATA frame %p was sent partially",
        !           849:                            stream->id, frame);
        !           850: 
        !           851:             return NGX_AGAIN;
        !           852:         }
        !           853: 
        !           854:         ln = cl->next;
        !           855: 
        !           856:         cl->next = stream->free_data_headers;
        !           857:         stream->free_data_headers = cl;
        !           858: 
        !           859:         if (cl == frame->last) {
        !           860:             goto done;
        !           861:         }
        !           862: 
        !           863:         cl = ln;
        !           864:     }
        !           865: 
        !           866:     for ( ;; ) {
        !           867:         if (ngx_buf_size(cl->buf) != 0) {
        !           868: 
        !           869:             if (cl != frame->first) {
        !           870:                 frame->first = cl;
        !           871:                 ngx_http_spdy_handle_stream(sc, stream);
        !           872:             }
        !           873: 
        !           874:             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
        !           875:                            "spdy:%ui DATA frame %p was sent partially",
        !           876:                            stream->id, frame);
        !           877: 
        !           878:             return NGX_AGAIN;
        !           879:         }
        !           880: 
        !           881:         ln = cl->next;
        !           882: 
        !           883:         ngx_free_chain(stream->request->pool, cl);
        !           884: 
        !           885:         if (cl == frame->last) {
        !           886:             goto done;
        !           887:         }
        !           888: 
        !           889:         cl = ln;
        !           890:     }
        !           891: 
        !           892: done:
        !           893: 
        !           894:     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
        !           895:                    "spdy:%ui DATA frame %p was sent", stream->id, frame);
        !           896: 
        !           897:     stream->request->header_size += NGX_SPDY_FRAME_HEADER_SIZE;
        !           898: 
        !           899:     ngx_http_spdy_handle_frame(stream, frame);
        !           900: 
        !           901:     ngx_http_spdy_handle_stream(sc, stream);
        !           902: 
        !           903:     return NGX_OK;
        !           904: }
        !           905: 
        !           906: 
        !           907: static ngx_inline void
        !           908: ngx_http_spdy_handle_frame(ngx_http_spdy_stream_t *stream,
        !           909:     ngx_http_spdy_out_frame_t *frame)
        !           910: {
        !           911:     ngx_http_request_t  *r;
        !           912: 
        !           913:     r = stream->request;
        !           914: 
        !           915:     r->connection->sent += frame->size;
        !           916:     r->blocked--;
        !           917: 
        !           918:     if (frame->fin) {
        !           919:         stream->out_closed = 1;
        !           920:     }
        !           921: 
        !           922:     frame->free = stream->free_frames;
        !           923:     stream->free_frames = frame;
        !           924: 
        !           925:     stream->waiting--;
        !           926: }
        !           927: 
        !           928: 
        !           929: static ngx_inline void
        !           930: ngx_http_spdy_handle_stream(ngx_http_spdy_connection_t *sc,
        !           931:     ngx_http_spdy_stream_t *stream)
        !           932: {
        !           933:     ngx_connection_t  *fc;
        !           934: 
        !           935:     fc = stream->request->connection;
        !           936: 
        !           937:     fc->write->delayed = 0;
        !           938: 
        !           939:     if (stream->handled) {
        !           940:         return;
        !           941:     }
        !           942: 
        !           943:     if (sc->blocked == 2) {
        !           944:         stream->handled = 1;
        !           945: 
        !           946:         stream->next = sc->last_stream;
        !           947:         sc->last_stream = stream;
        !           948:     }
        !           949: }
        !           950: 
        !           951: 
        !           952: static void
        !           953: ngx_http_spdy_filter_cleanup(void *data)
        !           954: {
        !           955:     ngx_http_spdy_stream_t *stream = data;
        !           956: 
        !           957:     ngx_http_request_t         *r;
        !           958:     ngx_http_spdy_out_frame_t  *frame, **fn;
        !           959: 
        !           960:     if (stream->waiting == 0) {
        !           961:         return;
        !           962:     }
        !           963: 
        !           964:     r = stream->request;
        !           965: 
        !           966:     fn = &stream->connection->last_out;
        !           967: 
        !           968:     for ( ;; ) {
        !           969:         frame = *fn;
        !           970: 
        !           971:         if (frame == NULL) {
        !           972:             break;
        !           973:         }
        !           974: 
        !           975:         if (frame->stream == stream && !frame->blocked) {
        !           976: 
        !           977:             stream->waiting--;
        !           978:             r->blocked--;
        !           979: 
        !           980:             *fn = frame->next;
        !           981:             continue;
        !           982:         }
        !           983: 
        !           984:         fn = &frame->next;
        !           985:     }
        !           986: }
        !           987: 
        !           988: 
        !           989: static ngx_int_t
        !           990: ngx_http_spdy_filter_init(ngx_conf_t *cf)
        !           991: {
        !           992:     ngx_http_next_header_filter = ngx_http_top_header_filter;
        !           993:     ngx_http_top_header_filter = ngx_http_spdy_header_filter;
        !           994: 
        !           995:     ngx_http_next_body_filter = ngx_http_top_body_filter;
        !           996:     ngx_http_top_body_filter = ngx_http_spdy_body_filter;
        !           997: 
        !           998:     return NGX_OK;
        !           999: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>