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>