Return to ngx_http_spdy.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / nginx / src / http |
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 <ngx_http_spdy_module.h> ! 12: ! 13: #include <zlib.h> ! 14: ! 15: ! 16: #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) ! 17: ! 18: #define ngx_str5cmp(m, c0, c1, c2, c3, c4) \ ! 19: *(uint32_t *) m == (c3 << 24 | c2 << 16 | c1 << 8 | c0) \ ! 20: && m[4] == c4 ! 21: ! 22: #else ! 23: ! 24: #define ngx_str5cmp(m, c0, c1, c2, c3, c4) \ ! 25: m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4 ! 26: ! 27: #endif ! 28: ! 29: ! 30: #if (NGX_HAVE_NONALIGNED) ! 31: ! 32: #define ngx_spdy_frame_parse_uint16(p) ntohs(*(uint16_t *) (p)) ! 33: #define ngx_spdy_frame_parse_uint32(p) ntohl(*(uint32_t *) (p)) ! 34: ! 35: #else ! 36: ! 37: #define ngx_spdy_frame_parse_uint16(p) ((p)[0] << 8 | (p)[1]) ! 38: #define ngx_spdy_frame_parse_uint32(p) \ ! 39: ((p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3]) ! 40: ! 41: #endif ! 42: ! 43: #define ngx_spdy_frame_parse_sid(p) \ ! 44: (ngx_spdy_frame_parse_uint32(p) & 0x7fffffff) ! 45: ! 46: ! 47: #define ngx_spdy_ctl_frame_check(h) \ ! 48: (((h) & 0xffffff00) == ngx_spdy_ctl_frame_head(0)) ! 49: #define ngx_spdy_data_frame_check(h) \ ! 50: (!((h) & (uint32_t) NGX_SPDY_CTL_BIT << 31)) ! 51: ! 52: #define ngx_spdy_ctl_frame_type(h) ((h) & 0x000000ff) ! 53: #define ngx_spdy_frame_flags(p) ((p) >> 24) ! 54: #define ngx_spdy_frame_length(p) ((p) & 0x00ffffff) ! 55: ! 56: ! 57: #define NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE 4096 ! 58: #define NGX_SPDY_CTL_FRAME_BUFFER_SIZE 16 ! 59: ! 60: #define NGX_SPDY_PROTOCOL_ERROR 1 ! 61: #define NGX_SPDY_INVALID_STREAM 2 ! 62: #define NGX_SPDY_REFUSED_STREAM 3 ! 63: #define NGX_SPDY_UNSUPPORTED_VERSION 4 ! 64: #define NGX_SPDY_CANCEL 5 ! 65: #define NGX_SPDY_INTERNAL_ERROR 6 ! 66: #define NGX_SPDY_FLOW_CONTROL_ERROR 7 ! 67: ! 68: #define NGX_SPDY_SETTINGS_MAX_STREAMS 4 ! 69: ! 70: #define NGX_SPDY_SETTINGS_FLAG_PERSIST 0x01 ! 71: ! 72: typedef struct { ! 73: ngx_uint_t hash; ! 74: u_char len; ! 75: u_char header[7]; ! 76: ngx_int_t (*handler)(ngx_http_request_t *r); ! 77: } ngx_http_spdy_request_header_t; ! 78: ! 79: ! 80: static void ngx_http_spdy_read_handler(ngx_event_t *rev); ! 81: static void ngx_http_spdy_write_handler(ngx_event_t *wev); ! 82: static void ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc); ! 83: ! 84: static u_char *ngx_http_spdy_state_detect_settings( ! 85: ngx_http_spdy_connection_t *sc, u_char *pos, u_char *end); ! 86: static u_char *ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc, ! 87: u_char *pos, u_char *end); ! 88: static u_char *ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc, ! 89: u_char *pos, u_char *end); ! 90: static u_char *ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc, ! 91: u_char *pos, u_char *end); ! 92: static u_char *ngx_http_spdy_state_headers_error(ngx_http_spdy_connection_t *sc, ! 93: u_char *pos, u_char *end); ! 94: static u_char *ngx_http_spdy_state_headers_skip(ngx_http_spdy_connection_t *sc, ! 95: u_char *pos, u_char *end); ! 96: static u_char *ngx_http_spdy_state_data(ngx_http_spdy_connection_t *sc, ! 97: u_char *pos, u_char *end); ! 98: static u_char *ngx_http_spdy_state_rst_stream(ngx_http_spdy_connection_t *sc, ! 99: u_char *pos, u_char *end); ! 100: static u_char *ngx_http_spdy_state_ping(ngx_http_spdy_connection_t *sc, ! 101: u_char *pos, u_char *end); ! 102: static u_char *ngx_http_spdy_state_skip(ngx_http_spdy_connection_t *sc, ! 103: u_char *pos, u_char *end); ! 104: static u_char *ngx_http_spdy_state_settings(ngx_http_spdy_connection_t *sc, ! 105: u_char *pos, u_char *end); ! 106: static u_char *ngx_http_spdy_state_noop(ngx_http_spdy_connection_t *sc, ! 107: u_char *pos, u_char *end); ! 108: static u_char *ngx_http_spdy_state_complete(ngx_http_spdy_connection_t *sc, ! 109: u_char *pos, u_char *end); ! 110: static u_char *ngx_http_spdy_state_save(ngx_http_spdy_connection_t *sc, ! 111: u_char *pos, u_char *end, ngx_http_spdy_handler_pt handler); ! 112: static u_char *ngx_http_spdy_state_protocol_error( ! 113: ngx_http_spdy_connection_t *sc); ! 114: static u_char *ngx_http_spdy_state_internal_error( ! 115: ngx_http_spdy_connection_t *sc); ! 116: ! 117: static ngx_int_t ngx_http_spdy_send_rst_stream(ngx_http_spdy_connection_t *sc, ! 118: ngx_uint_t sid, ngx_uint_t status, ngx_uint_t priority); ! 119: static ngx_int_t ngx_http_spdy_send_settings(ngx_http_spdy_connection_t *sc); ! 120: static ngx_int_t ngx_http_spdy_settings_frame_handler( ! 121: ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame); ! 122: static ngx_http_spdy_out_frame_t *ngx_http_spdy_get_ctl_frame( ! 123: ngx_http_spdy_connection_t *sc, size_t size, ngx_uint_t priority); ! 124: static ngx_int_t ngx_http_spdy_ctl_frame_handler( ! 125: ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame); ! 126: ! 127: static ngx_http_spdy_stream_t *ngx_http_spdy_create_stream( ! 128: ngx_http_spdy_connection_t *sc, ngx_uint_t id, ngx_uint_t priority); ! 129: static ngx_http_spdy_stream_t *ngx_http_spdy_get_stream_by_id( ! 130: ngx_http_spdy_connection_t *sc, ngx_uint_t sid); ! 131: #define ngx_http_spdy_streams_index_size(sscf) (sscf->streams_index_mask + 1) ! 132: #define ngx_http_spdy_stream_index(sscf, sid) \ ! 133: ((sid >> 1) & sscf->streams_index_mask) ! 134: ! 135: static ngx_int_t ngx_http_spdy_parse_header(ngx_http_request_t *r); ! 136: static ngx_int_t ngx_http_spdy_alloc_large_header_buffer(ngx_http_request_t *r); ! 137: ! 138: static ngx_int_t ngx_http_spdy_handle_request_header(ngx_http_request_t *r); ! 139: static ngx_int_t ngx_http_spdy_parse_method(ngx_http_request_t *r); ! 140: static ngx_int_t ngx_http_spdy_parse_scheme(ngx_http_request_t *r); ! 141: static ngx_int_t ngx_http_spdy_parse_url(ngx_http_request_t *r); ! 142: static ngx_int_t ngx_http_spdy_parse_version(ngx_http_request_t *r); ! 143: ! 144: static ngx_int_t ngx_http_spdy_construct_request_line(ngx_http_request_t *r); ! 145: static void ngx_http_spdy_run_request(ngx_http_request_t *r); ! 146: static ngx_int_t ngx_http_spdy_init_request_body(ngx_http_request_t *r); ! 147: ! 148: static void ngx_http_spdy_handle_connection_handler(ngx_event_t *rev); ! 149: static void ngx_http_spdy_keepalive_handler(ngx_event_t *rev); ! 150: static void ngx_http_spdy_finalize_connection(ngx_http_spdy_connection_t *sc, ! 151: ngx_int_t rc); ! 152: ! 153: static void ngx_http_spdy_pool_cleanup(void *data); ! 154: ! 155: static void *ngx_http_spdy_zalloc(void *opaque, u_int items, u_int size); ! 156: static void ngx_http_spdy_zfree(void *opaque, void *address); ! 157: ! 158: ! 159: static const u_char ngx_http_spdy_dict[] = ! 160: "options" "get" "head" "post" "put" "delete" "trace" ! 161: "accept" "accept-charset" "accept-encoding" "accept-language" ! 162: "authorization" "expect" "from" "host" ! 163: "if-modified-since" "if-match" "if-none-match" "if-range" ! 164: "if-unmodifiedsince" "max-forwards" "proxy-authorization" ! 165: "range" "referer" "te" "user-agent" ! 166: "100" "101" "200" "201" "202" "203" "204" "205" "206" ! 167: "300" "301" "302" "303" "304" "305" "306" "307" ! 168: "400" "401" "402" "403" "404" "405" "406" "407" "408" "409" "410" ! 169: "411" "412" "413" "414" "415" "416" "417" ! 170: "500" "501" "502" "503" "504" "505" ! 171: "accept-ranges" "age" "etag" "location" "proxy-authenticate" "public" ! 172: "retry-after" "server" "vary" "warning" "www-authenticate" "allow" ! 173: "content-base" "content-encoding" "cache-control" "connection" "date" ! 174: "trailer" "transfer-encoding" "upgrade" "via" "warning" ! 175: "content-language" "content-length" "content-location" ! 176: "content-md5" "content-range" "content-type" "etag" "expires" ! 177: "last-modified" "set-cookie" ! 178: "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday" ! 179: "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec" ! 180: "chunked" "text/html" "image/png" "image/jpg" "image/gif" ! 181: "application/xml" "application/xhtml" "text/plain" "public" "max-age" ! 182: "charset=iso-8859-1" "utf-8" "gzip" "deflate" "HTTP/1.1" "status" ! 183: "version" "url"; ! 184: ! 185: ! 186: static ngx_http_spdy_request_header_t ngx_http_spdy_request_headers[] = { ! 187: { 0, 6, "method", ngx_http_spdy_parse_method }, ! 188: { 0, 6, "scheme", ngx_http_spdy_parse_scheme }, ! 189: { 0, 3, "url", ngx_http_spdy_parse_url }, ! 190: { 0, 7, "version", ngx_http_spdy_parse_version }, ! 191: }; ! 192: ! 193: #define NGX_SPDY_REQUEST_HEADERS \ ! 194: (sizeof(ngx_http_spdy_request_headers) \ ! 195: / sizeof(ngx_http_spdy_request_header_t)) ! 196: ! 197: ! 198: void ! 199: ngx_http_spdy_init(ngx_event_t *rev) ! 200: { ! 201: int rc; ! 202: ngx_connection_t *c; ! 203: ngx_pool_cleanup_t *cln; ! 204: ngx_http_connection_t *hc; ! 205: ngx_http_spdy_srv_conf_t *sscf; ! 206: ngx_http_spdy_main_conf_t *smcf; ! 207: ngx_http_spdy_connection_t *sc; ! 208: ! 209: c = rev->data; ! 210: hc = c->data; ! 211: ! 212: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, ! 213: "init spdy request"); ! 214: ! 215: c->log->action = "processing SPDY"; ! 216: ! 217: smcf = ngx_http_get_module_main_conf(hc->conf_ctx, ngx_http_spdy_module); ! 218: ! 219: if (smcf->recv_buffer == NULL) { ! 220: smcf->recv_buffer = ngx_palloc(ngx_cycle->pool, smcf->recv_buffer_size); ! 221: if (smcf->recv_buffer == NULL) { ! 222: ngx_http_close_connection(c); ! 223: return; ! 224: } ! 225: } ! 226: ! 227: sc = ngx_pcalloc(c->pool, sizeof(ngx_http_spdy_connection_t)); ! 228: if (sc == NULL) { ! 229: ngx_http_close_connection(c); ! 230: return; ! 231: } ! 232: ! 233: sc->connection = c; ! 234: sc->http_connection = hc; ! 235: ! 236: sc->handler = ngx_http_spdy_state_detect_settings; ! 237: ! 238: sc->zstream_in.zalloc = ngx_http_spdy_zalloc; ! 239: sc->zstream_in.zfree = ngx_http_spdy_zfree; ! 240: sc->zstream_in.opaque = sc; ! 241: ! 242: rc = inflateInit(&sc->zstream_in); ! 243: if (rc != Z_OK) { ! 244: ngx_log_error(NGX_LOG_ALERT, c->log, 0, ! 245: "inflateInit() failed: %d", rc); ! 246: ngx_http_close_connection(c); ! 247: return; ! 248: } ! 249: ! 250: sc->zstream_out.zalloc = ngx_http_spdy_zalloc; ! 251: sc->zstream_out.zfree = ngx_http_spdy_zfree; ! 252: sc->zstream_out.opaque = sc; ! 253: ! 254: sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_spdy_module); ! 255: ! 256: rc = deflateInit2(&sc->zstream_out, (int) sscf->headers_comp, ! 257: Z_DEFLATED, 11, 4, Z_DEFAULT_STRATEGY); ! 258: ! 259: if (rc != Z_OK) { ! 260: ngx_log_error(NGX_LOG_ALERT, c->log, 0, ! 261: "deflateInit2() failed: %d", rc); ! 262: ngx_http_close_connection(c); ! 263: return; ! 264: } ! 265: ! 266: rc = deflateSetDictionary(&sc->zstream_out, ngx_http_spdy_dict, ! 267: sizeof(ngx_http_spdy_dict)); ! 268: if (rc != Z_OK) { ! 269: ngx_log_error(NGX_LOG_ALERT, c->log, 0, ! 270: "deflateSetDictionary() failed: %d", rc); ! 271: ngx_http_close_connection(c); ! 272: return; ! 273: } ! 274: ! 275: sc->pool = ngx_create_pool(sscf->pool_size, sc->connection->log); ! 276: if (sc->pool == NULL) { ! 277: ngx_http_close_connection(c); ! 278: return; ! 279: } ! 280: ! 281: cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_pool_cleanup_file_t)); ! 282: if (cln == NULL) { ! 283: ngx_http_close_connection(c); ! 284: return; ! 285: } ! 286: ! 287: cln->handler = ngx_http_spdy_pool_cleanup; ! 288: cln->data = sc; ! 289: ! 290: sc->streams_index = ngx_pcalloc(sc->pool, ! 291: ngx_http_spdy_streams_index_size(sscf) ! 292: * sizeof(ngx_http_spdy_stream_t *)); ! 293: if (sc->streams_index == NULL) { ! 294: ngx_http_close_connection(c); ! 295: return; ! 296: } ! 297: ! 298: c->data = sc; ! 299: ! 300: rev->handler = ngx_http_spdy_read_handler; ! 301: c->write->handler = ngx_http_spdy_write_handler; ! 302: ! 303: ngx_http_spdy_read_handler(rev); ! 304: } ! 305: ! 306: ! 307: static void ! 308: ngx_http_spdy_read_handler(ngx_event_t *rev) ! 309: { ! 310: u_char *p, *end; ! 311: size_t available; ! 312: ssize_t n; ! 313: ngx_connection_t *c; ! 314: ngx_http_spdy_main_conf_t *smcf; ! 315: ngx_http_spdy_connection_t *sc; ! 316: ! 317: c = rev->data; ! 318: sc = c->data; ! 319: ! 320: if (rev->timedout) { ! 321: ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); ! 322: ngx_http_spdy_finalize_connection(sc, NGX_HTTP_REQUEST_TIME_OUT); ! 323: return; ! 324: } ! 325: ! 326: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy read handler"); ! 327: ! 328: sc->blocked = 1; ! 329: ! 330: smcf = ngx_http_get_module_main_conf(sc->http_connection->conf_ctx, ! 331: ngx_http_spdy_module); ! 332: ! 333: available = smcf->recv_buffer_size - 2 * NGX_SPDY_STATE_BUFFER_SIZE; ! 334: ! 335: do { ! 336: p = smcf->recv_buffer; ! 337: ! 338: ngx_memcpy(p, sc->buffer, NGX_SPDY_STATE_BUFFER_SIZE); ! 339: end = p + sc->buffer_used; ! 340: ! 341: n = c->recv(c, end, available); ! 342: ! 343: if (n == NGX_AGAIN) { ! 344: break; ! 345: } ! 346: ! 347: if (n == 0 && (sc->waiting || sc->processing)) { ! 348: ngx_log_error(NGX_LOG_INFO, c->log, 0, ! 349: "client closed prematurely connection"); ! 350: } ! 351: ! 352: if (n == 0 || n == NGX_ERROR) { ! 353: ngx_http_spdy_finalize_connection(sc, ! 354: NGX_HTTP_CLIENT_CLOSED_REQUEST); ! 355: return; ! 356: } ! 357: ! 358: end += n; ! 359: ! 360: sc->buffer_used = 0; ! 361: sc->waiting = 0; ! 362: ! 363: do { ! 364: p = sc->handler(sc, p, end); ! 365: ! 366: if (p == NULL) { ! 367: return; ! 368: } ! 369: ! 370: } while (p != end); ! 371: ! 372: } while (rev->ready); ! 373: ! 374: if (ngx_handle_read_event(rev, 0) != NGX_OK) { ! 375: ngx_http_spdy_finalize_connection(sc, NGX_HTTP_INTERNAL_SERVER_ERROR); ! 376: return; ! 377: } ! 378: ! 379: sc->blocked = 0; ! 380: ! 381: if (sc->processing) { ! 382: if (rev->timer_set) { ! 383: ngx_del_timer(rev); ! 384: } ! 385: return; ! 386: } ! 387: ! 388: ngx_http_spdy_handle_connection(sc); ! 389: } ! 390: ! 391: ! 392: static void ! 393: ngx_http_spdy_write_handler(ngx_event_t *wev) ! 394: { ! 395: ngx_int_t rc; ! 396: ngx_connection_t *c; ! 397: ngx_http_spdy_stream_t *stream, *s, *sn; ! 398: ngx_http_spdy_connection_t *sc; ! 399: ! 400: c = wev->data; ! 401: sc = c->data; ! 402: ! 403: if (wev->timedout) { ! 404: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, ! 405: "spdy write event timed out"); ! 406: ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST); ! 407: return; ! 408: } ! 409: ! 410: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy write handler"); ! 411: ! 412: sc->blocked = 2; ! 413: ! 414: rc = ngx_http_spdy_send_output_queue(sc); ! 415: ! 416: if (rc == NGX_ERROR) { ! 417: ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST); ! 418: return; ! 419: } ! 420: ! 421: stream = NULL; ! 422: ! 423: for (s = sc->last_stream; s; s = sn) { ! 424: sn = s->next; ! 425: s->next = stream; ! 426: stream = s; ! 427: } ! 428: ! 429: sc->last_stream = NULL; ! 430: ! 431: sc->blocked = 1; ! 432: ! 433: for ( /* void */ ; stream; stream = sn) { ! 434: sn = stream->next; ! 435: stream->handled = 0; ! 436: ! 437: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, ! 438: "spdy run stream %ui", stream->id); ! 439: ! 440: wev = stream->request->connection->write; ! 441: wev->handler(wev); ! 442: } ! 443: ! 444: sc->blocked = 0; ! 445: ! 446: if (rc == NGX_AGAIN) { ! 447: return; ! 448: } ! 449: ! 450: ngx_http_spdy_handle_connection(sc); ! 451: } ! 452: ! 453: ! 454: ngx_int_t ! 455: ngx_http_spdy_send_output_queue(ngx_http_spdy_connection_t *sc) ! 456: { ! 457: ngx_chain_t *cl; ! 458: ngx_event_t *wev; ! 459: ngx_connection_t *c; ! 460: ngx_http_core_loc_conf_t *clcf; ! 461: ngx_http_spdy_out_frame_t *out, *frame, *fn; ! 462: ! 463: c = sc->connection; ! 464: ! 465: if (c->error) { ! 466: return NGX_ERROR; ! 467: } ! 468: ! 469: wev = c->write; ! 470: ! 471: if (!wev->ready) { ! 472: return NGX_OK; ! 473: } ! 474: ! 475: cl = NULL; ! 476: out = NULL; ! 477: ! 478: for (frame = sc->last_out; frame; frame = fn) { ! 479: frame->last->next = cl; ! 480: cl = frame->first; ! 481: ! 482: fn = frame->next; ! 483: frame->next = out; ! 484: out = frame; ! 485: ! 486: ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, ! 487: "spdy frame out: %p sid:%ui prio:%ui bl:%ui size:%uz", ! 488: out, out->stream ? out->stream->id : 0, out->priority, ! 489: out->blocked, out->size); ! 490: } ! 491: ! 492: cl = c->send_chain(c, cl, 0); ! 493: ! 494: if (cl == NGX_CHAIN_ERROR) { ! 495: c->error = 1; ! 496: ! 497: if (!sc->blocked) { ! 498: ngx_post_event(wev, &ngx_posted_events); ! 499: } ! 500: ! 501: return NGX_ERROR; ! 502: } ! 503: ! 504: clcf = ngx_http_get_module_loc_conf(sc->http_connection->conf_ctx, ! 505: ngx_http_core_module); ! 506: ! 507: if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { ! 508: return NGX_ERROR; /* FIXME */ ! 509: } ! 510: ! 511: if (cl) { ! 512: ngx_add_timer(wev, clcf->send_timeout); ! 513: ! 514: } else { ! 515: if (wev->timer_set) { ! 516: ngx_del_timer(wev); ! 517: } ! 518: } ! 519: ! 520: for ( /* void */ ; out; out = out->next) { ! 521: if (out->handler(sc, out) != NGX_OK) { ! 522: out->blocked = 1; ! 523: out->priority = NGX_SPDY_HIGHEST_PRIORITY; ! 524: break; ! 525: } ! 526: ! 527: ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, ! 528: "spdy frame sent: %p sid:%ui bl:%ui size:%uz", ! 529: out, out->stream ? out->stream->id : 0, ! 530: out->blocked, out->size); ! 531: } ! 532: ! 533: frame = NULL; ! 534: ! 535: for ( /* void */ ; out; out = fn) { ! 536: fn = out->next; ! 537: out->next = frame; ! 538: frame = out; ! 539: } ! 540: ! 541: sc->last_out = frame; ! 542: ! 543: return NGX_OK; ! 544: } ! 545: ! 546: ! 547: static void ! 548: ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc) ! 549: { ! 550: ngx_connection_t *c; ! 551: ngx_http_spdy_srv_conf_t *sscf; ! 552: ! 553: if (sc->last_out || sc->processing) { ! 554: return; ! 555: } ! 556: ! 557: c = sc->connection; ! 558: ! 559: if (c->error) { ! 560: ngx_http_close_connection(c); ! 561: return; ! 562: } ! 563: ! 564: if (c->buffered) { ! 565: return; ! 566: } ! 567: ! 568: sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, ! 569: ngx_http_spdy_module); ! 570: if (sc->waiting) { ! 571: ngx_add_timer(c->read, sscf->recv_timeout); ! 572: return; ! 573: } ! 574: ! 575: if (ngx_terminate || ngx_exiting) { ! 576: ngx_http_close_connection(c); ! 577: return; ! 578: } ! 579: ! 580: ngx_destroy_pool(sc->pool); ! 581: ! 582: sc->pool = NULL; ! 583: sc->free_ctl_frames = NULL; ! 584: sc->free_fake_connections = NULL; ! 585: ! 586: #if (NGX_HTTP_SSL) ! 587: if (c->ssl) { ! 588: ngx_ssl_free_buffer(c); ! 589: } ! 590: #endif ! 591: ! 592: c->destroyed = 1; ! 593: c->idle = 1; ! 594: ngx_reusable_connection(c, 1); ! 595: ! 596: c->write->handler = ngx_http_empty_handler; ! 597: c->read->handler = ngx_http_spdy_keepalive_handler; ! 598: ! 599: if (c->write->timer_set) { ! 600: ngx_del_timer(c->write); ! 601: } ! 602: ! 603: ngx_add_timer(c->read, sscf->keepalive_timeout); ! 604: } ! 605: ! 606: ! 607: static u_char * ! 608: ngx_http_spdy_state_detect_settings(ngx_http_spdy_connection_t *sc, ! 609: u_char *pos, u_char *end) ! 610: { ! 611: if (end - pos < NGX_SPDY_FRAME_HEADER_SIZE) { ! 612: return ngx_http_spdy_state_save(sc, pos, end, ! 613: ngx_http_spdy_state_detect_settings); ! 614: } ! 615: ! 616: /* ! 617: * Since this is the first frame in a buffer, ! 618: * then it is properly aligned ! 619: */ ! 620: ! 621: if (*(uint32_t *) pos == htonl(ngx_spdy_ctl_frame_head(NGX_SPDY_SETTINGS))) ! 622: { ! 623: sc->length = ngx_spdy_frame_length(htonl(((uint32_t *) pos)[1])); ! 624: ! 625: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ! 626: "spdy SETTINGS frame received, size: %uz", sc->length); ! 627: ! 628: pos += NGX_SPDY_FRAME_HEADER_SIZE; ! 629: ! 630: return ngx_http_spdy_state_settings(sc, pos, end); ! 631: } ! 632: ! 633: ngx_http_spdy_send_settings(sc); ! 634: ! 635: return ngx_http_spdy_state_head(sc, pos, end); ! 636: } ! 637: ! 638: ! 639: static u_char * ! 640: ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc, u_char *pos, ! 641: u_char *end) ! 642: { ! 643: uint32_t head, flen; ! 644: ! 645: if (end - pos < NGX_SPDY_FRAME_HEADER_SIZE) { ! 646: return ngx_http_spdy_state_save(sc, pos, end, ! 647: ngx_http_spdy_state_head); ! 648: } ! 649: ! 650: head = ngx_spdy_frame_parse_uint32(pos); ! 651: ! 652: pos += sizeof(uint32_t); ! 653: ! 654: flen = ngx_spdy_frame_parse_uint32(pos); ! 655: ! 656: sc->flags = ngx_spdy_frame_flags(flen); ! 657: sc->length = ngx_spdy_frame_length(flen); ! 658: ! 659: pos += sizeof(uint32_t); ! 660: ! 661: ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ! 662: "spdy process frame head:%08Xd f:%ui l:%ui", ! 663: head, sc->flags, sc->length); ! 664: ! 665: if (ngx_spdy_ctl_frame_check(head)) { ! 666: switch (ngx_spdy_ctl_frame_type(head)) { ! 667: ! 668: case NGX_SPDY_SYN_STREAM: ! 669: return ngx_http_spdy_state_syn_stream(sc, pos, end); ! 670: ! 671: case NGX_SPDY_SYN_REPLY: ! 672: return ngx_http_spdy_state_protocol_error(sc); ! 673: ! 674: case NGX_SPDY_RST_STREAM: ! 675: return ngx_http_spdy_state_rst_stream(sc, pos, end); ! 676: ! 677: case NGX_SPDY_SETTINGS: ! 678: return ngx_http_spdy_state_skip(sc, pos, end); ! 679: ! 680: case NGX_SPDY_NOOP: ! 681: return ngx_http_spdy_state_noop(sc, pos, end); ! 682: ! 683: case NGX_SPDY_PING: ! 684: return ngx_http_spdy_state_ping(sc, pos, end); ! 685: ! 686: case NGX_SPDY_GOAWAY: ! 687: return ngx_http_spdy_state_skip(sc, pos, end); /* TODO */ ! 688: ! 689: case NGX_SPDY_HEADERS: ! 690: return ngx_http_spdy_state_protocol_error(sc); ! 691: ! 692: default: /* TODO logging */ ! 693: return ngx_http_spdy_state_skip(sc, pos, end); ! 694: } ! 695: } ! 696: ! 697: if (ngx_spdy_data_frame_check(head)) { ! 698: sc->stream = ngx_http_spdy_get_stream_by_id(sc, head); ! 699: return ngx_http_spdy_state_data(sc, pos, end); ! 700: } ! 701: ! 702: ! 703: /* TODO version & type check */ ! 704: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ! 705: "spdy unknown frame"); ! 706: ! 707: return ngx_http_spdy_state_protocol_error(sc); ! 708: } ! 709: ! 710: ! 711: static u_char * ! 712: ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc, u_char *pos, ! 713: u_char *end) ! 714: { ! 715: ngx_uint_t sid, prio; ! 716: ngx_http_spdy_stream_t *stream; ! 717: ngx_http_spdy_srv_conf_t *sscf; ! 718: ! 719: if (end - pos < NGX_SPDY_SYN_STREAM_SIZE) { ! 720: return ngx_http_spdy_state_save(sc, pos, end, ! 721: ngx_http_spdy_state_syn_stream); ! 722: } ! 723: ! 724: if (sc->length <= NGX_SPDY_SYN_STREAM_SIZE) { ! 725: /* TODO logging */ ! 726: return ngx_http_spdy_state_protocol_error(sc); ! 727: } ! 728: ! 729: sc->length -= NGX_SPDY_SYN_STREAM_SIZE; ! 730: ! 731: sid = ngx_spdy_frame_parse_sid(pos); ! 732: prio = pos[8] >> 6; ! 733: ! 734: pos += NGX_SPDY_SYN_STREAM_SIZE; ! 735: ! 736: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ! 737: "spdy SYN_STREAM frame sid:%ui prio:%ui", sid, prio); ! 738: ! 739: sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, ! 740: ngx_http_spdy_module); ! 741: ! 742: if (sc->processing >= sscf->concurrent_streams) { ! 743: ! 744: ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0, ! 745: "spdy concurrent streams excessed %ui", sc->processing); ! 746: ! 747: if (ngx_http_spdy_send_rst_stream(sc, sid, NGX_SPDY_REFUSED_STREAM, ! 748: prio) ! 749: != NGX_OK) ! 750: { ! 751: return ngx_http_spdy_state_internal_error(sc); ! 752: } ! 753: ! 754: return ngx_http_spdy_state_headers_skip(sc, pos, end); ! 755: } ! 756: ! 757: stream = ngx_http_spdy_create_stream(sc, sid, prio); ! 758: if (stream == NULL) { ! 759: return ngx_http_spdy_state_internal_error(sc); ! 760: } ! 761: ! 762: stream->in_closed = (sc->flags & NGX_SPDY_FLAG_FIN) ? 1 : 0; ! 763: ! 764: stream->request->request_length = NGX_SPDY_FRAME_HEADER_SIZE ! 765: + NGX_SPDY_SYN_STREAM_SIZE ! 766: + sc->length; ! 767: ! 768: sc->stream = stream; ! 769: ! 770: sc->last_sid = sid; ! 771: ! 772: return ngx_http_spdy_state_headers(sc, pos, end); ! 773: } ! 774: ! 775: ! 776: static u_char * ! 777: ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc, u_char *pos, ! 778: u_char *end) ! 779: { ! 780: int z; ! 781: size_t size; ! 782: ngx_buf_t *buf; ! 783: ngx_int_t rc; ! 784: ngx_uint_t complete; ! 785: ngx_http_request_t *r; ! 786: ! 787: size = end - pos; ! 788: ! 789: if (size == 0) { ! 790: return ngx_http_spdy_state_save(sc, pos, end, ! 791: ngx_http_spdy_state_headers); ! 792: } ! 793: ! 794: if (size >= sc->length) { ! 795: size = sc->length; ! 796: complete = 1; ! 797: ! 798: } else { ! 799: complete = 0; ! 800: } ! 801: ! 802: r = sc->stream->request; ! 803: ! 804: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 805: "spdy process HEADERS %uz of %uz", size, sc->length); ! 806: ! 807: buf = r->header_in; ! 808: ! 809: sc->zstream_in.next_in = pos; ! 810: sc->zstream_in.avail_in = size; ! 811: sc->zstream_in.next_out = buf->last; ! 812: sc->zstream_in.avail_out = buf->end - buf->last - 1; ! 813: ! 814: z = inflate(&sc->zstream_in, Z_NO_FLUSH); ! 815: ! 816: if (z == Z_NEED_DICT) { ! 817: z = inflateSetDictionary(&sc->zstream_in, ngx_http_spdy_dict, ! 818: sizeof(ngx_http_spdy_dict)); ! 819: if (z != Z_OK) { ! 820: ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ! 821: "spdy inflateSetDictionary() failed: %d", z); ! 822: ngx_http_spdy_close_stream(sc->stream, 0); ! 823: return ngx_http_spdy_state_protocol_error(sc); ! 824: } ! 825: ! 826: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 827: "spdy inflateSetDictionary(): %d", z); ! 828: ! 829: z = sc->zstream_in.avail_in ? inflate(&sc->zstream_in, Z_NO_FLUSH) ! 830: : Z_OK; ! 831: } ! 832: ! 833: if (z != Z_OK) { ! 834: ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ! 835: "spdy inflate() failed: %d", z); ! 836: ngx_http_spdy_close_stream(sc->stream, 0); ! 837: return ngx_http_spdy_state_protocol_error(sc); ! 838: } ! 839: ! 840: ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 841: "spdy inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", ! 842: sc->zstream_in.next_in, sc->zstream_in.next_out, ! 843: sc->zstream_in.avail_in, sc->zstream_in.avail_out, ! 844: z); ! 845: ! 846: sc->length -= sc->zstream_in.next_in - pos; ! 847: pos = sc->zstream_in.next_in; ! 848: ! 849: buf->last = sc->zstream_in.next_out; ! 850: ! 851: if (r->headers_in.headers.part.elts == NULL) { ! 852: ! 853: if (buf->last - buf->pos < NGX_SPDY_NV_NUM_SIZE) { ! 854: return ngx_http_spdy_state_save(sc, pos, end, ! 855: ngx_http_spdy_state_headers); ! 856: } ! 857: ! 858: sc->headers = ngx_spdy_frame_parse_uint16(buf->pos); ! 859: ! 860: buf->pos += NGX_SPDY_NV_NUM_SIZE; ! 861: ! 862: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 863: "spdy headers count: %ui", sc->headers); ! 864: ! 865: if (ngx_list_init(&r->headers_in.headers, r->pool, sc->headers + 3, ! 866: sizeof(ngx_table_elt_t)) ! 867: != NGX_OK) ! 868: { ! 869: ngx_http_spdy_close_stream(sc->stream, ! 870: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 871: return ngx_http_spdy_state_headers_error(sc, pos, end); ! 872: } ! 873: ! 874: if (ngx_array_init(&r->headers_in.cookies, r->pool, 2, ! 875: sizeof(ngx_table_elt_t *)) ! 876: != NGX_OK) ! 877: { ! 878: ngx_http_spdy_close_stream(sc->stream, ! 879: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 880: return ngx_http_spdy_state_headers_error(sc, pos, end); ! 881: } ! 882: } ! 883: ! 884: while (sc->headers) { ! 885: ! 886: rc = ngx_http_spdy_parse_header(r); ! 887: ! 888: switch (rc) { ! 889: ! 890: case NGX_DONE: ! 891: sc->headers--; ! 892: ! 893: case NGX_OK: ! 894: break; ! 895: ! 896: case NGX_AGAIN: ! 897: ! 898: if (sc->zstream_in.avail_in) { ! 899: ! 900: rc = ngx_http_spdy_alloc_large_header_buffer(r); ! 901: ! 902: if (rc == NGX_DECLINED) { ! 903: /* TODO logging */ ! 904: ngx_http_finalize_request(r, ! 905: NGX_HTTP_REQUEST_HEADER_TOO_LARGE); ! 906: return ngx_http_spdy_state_headers_error(sc, pos, end); ! 907: } ! 908: ! 909: if (rc != NGX_OK) { ! 910: ngx_http_spdy_close_stream(sc->stream, ! 911: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 912: return ngx_http_spdy_state_headers_error(sc, pos, end); ! 913: } ! 914: ! 915: buf = r->header_in; ! 916: ! 917: sc->zstream_in.next_out = buf->last; ! 918: sc->zstream_in.avail_out = buf->end - buf->last - 1; ! 919: ! 920: z = inflate(&sc->zstream_in, Z_NO_FLUSH); ! 921: ! 922: if (z != Z_OK) { ! 923: ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ! 924: "spdy inflate() failed: %d", z); ! 925: ngx_http_spdy_close_stream(sc->stream, 0); ! 926: return ngx_http_spdy_state_protocol_error(sc); ! 927: } ! 928: ! 929: sc->length -= sc->zstream_in.next_in - pos; ! 930: pos = sc->zstream_in.next_in; ! 931: ! 932: buf->last = sc->zstream_in.next_out; ! 933: ! 934: continue; ! 935: } ! 936: ! 937: if (complete) { ! 938: /* TODO: improve error message */ ! 939: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 940: "spdy again while last chunk"); ! 941: ngx_http_spdy_close_stream(sc->stream, 0); ! 942: return ngx_http_spdy_state_protocol_error(sc); ! 943: } ! 944: ! 945: return ngx_http_spdy_state_save(sc, pos, end, ! 946: ngx_http_spdy_state_headers); ! 947: ! 948: case NGX_HTTP_PARSE_INVALID_REQUEST: ! 949: ! 950: /* TODO: improve error message */ ! 951: ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ! 952: "client sent invalid header line"); ! 953: ! 954: ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); ! 955: ! 956: return ngx_http_spdy_state_headers_error(sc, pos, end); ! 957: ! 958: default: /* NGX_HTTP_PARSE_INVALID_HEADER */ ! 959: ! 960: ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ! 961: "client sent invalid HEADERS spdy frame"); ! 962: ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_BAD_REQUEST); ! 963: return ngx_http_spdy_state_protocol_error(sc); ! 964: } ! 965: ! 966: /* a header line has been parsed successfully */ ! 967: ! 968: rc = ngx_http_spdy_handle_request_header(r); ! 969: ! 970: if (rc != NGX_OK) { ! 971: if (rc == NGX_HTTP_PARSE_INVALID_HEADER) { ! 972: ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ! 973: "client sent invalid HEADERS spdy frame"); ! 974: ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_BAD_REQUEST); ! 975: return ngx_http_spdy_state_protocol_error(sc); ! 976: } ! 977: ! 978: if (rc == NGX_HTTP_PARSE_INVALID_REQUEST) { ! 979: ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); ! 980: } ! 981: ! 982: return ngx_http_spdy_state_headers_error(sc, pos, end); ! 983: } ! 984: } ! 985: ! 986: if (buf->pos != buf->last) { ! 987: /* TODO: improve error message */ ! 988: ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 989: "end %ui %p %p", complete, buf->pos, buf->last); ! 990: ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_BAD_REQUEST); ! 991: return ngx_http_spdy_state_protocol_error(sc); ! 992: } ! 993: ! 994: if (!complete) { ! 995: return ngx_http_spdy_state_save(sc, pos, end, ! 996: ngx_http_spdy_state_headers); ! 997: } ! 998: ! 999: ngx_http_spdy_run_request(r); ! 1000: ! 1001: return ngx_http_spdy_state_complete(sc, pos, end); ! 1002: } ! 1003: ! 1004: ! 1005: static u_char * ! 1006: ngx_http_spdy_state_headers_error(ngx_http_spdy_connection_t *sc, u_char *pos, ! 1007: u_char *end) ! 1008: { ! 1009: if (sc->connection->error) { ! 1010: return ngx_http_spdy_state_internal_error(sc); ! 1011: } ! 1012: ! 1013: return ngx_http_spdy_state_headers_skip(sc, pos, end); ! 1014: } ! 1015: ! 1016: ! 1017: static u_char * ! 1018: ngx_http_spdy_state_headers_skip(ngx_http_spdy_connection_t *sc, u_char *pos, ! 1019: u_char *end) ! 1020: { ! 1021: int n; ! 1022: size_t size; ! 1023: u_char buffer[NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE]; ! 1024: ! 1025: if (sc->length == 0) { ! 1026: return ngx_http_spdy_state_complete(sc, pos, end); ! 1027: } ! 1028: ! 1029: size = end - pos; ! 1030: ! 1031: if (size == 0) { ! 1032: return ngx_http_spdy_state_save(sc, pos, end, ! 1033: ngx_http_spdy_state_headers_skip); ! 1034: } ! 1035: ! 1036: sc->zstream_in.next_in = pos; ! 1037: sc->zstream_in.avail_in = (size < sc->length) ? size : sc->length; ! 1038: ! 1039: while (sc->zstream_in.avail_in) { ! 1040: sc->zstream_in.next_out = buffer; ! 1041: sc->zstream_in.avail_out = NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE; ! 1042: ! 1043: n = inflate(&sc->zstream_in, Z_NO_FLUSH); ! 1044: ! 1045: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ! 1046: "spdy inflate(): %d", n); ! 1047: ! 1048: if (n != Z_OK) { ! 1049: /* TODO: logging */ ! 1050: return ngx_http_spdy_state_protocol_error(sc); ! 1051: } ! 1052: } ! 1053: ! 1054: pos = sc->zstream_in.next_in; ! 1055: ! 1056: if (size < sc->length) { ! 1057: sc->length -= size; ! 1058: return ngx_http_spdy_state_save(sc, pos, end, ! 1059: ngx_http_spdy_state_headers_skip); ! 1060: } ! 1061: ! 1062: return ngx_http_spdy_state_complete(sc, pos, end); ! 1063: } ! 1064: ! 1065: ! 1066: static u_char * ! 1067: ngx_http_spdy_state_data(ngx_http_spdy_connection_t *sc, u_char *pos, ! 1068: u_char *end) ! 1069: { ! 1070: size_t size; ! 1071: ssize_t n; ! 1072: ngx_buf_t *buf; ! 1073: ngx_int_t rc; ! 1074: ngx_uint_t complete; ! 1075: ngx_temp_file_t *tf; ! 1076: ngx_http_request_t *r; ! 1077: ngx_http_spdy_stream_t *stream; ! 1078: ngx_http_request_body_t *rb; ! 1079: ngx_http_core_loc_conf_t *clcf; ! 1080: ! 1081: stream = sc->stream; ! 1082: ! 1083: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ! 1084: "spdy DATA frame"); ! 1085: ! 1086: if (stream == NULL) { ! 1087: return ngx_http_spdy_state_skip(sc, pos, end); ! 1088: } ! 1089: ! 1090: if (stream->in_closed) { ! 1091: /* TODO log */ ! 1092: return ngx_http_spdy_state_protocol_error(sc); ! 1093: } ! 1094: ! 1095: if (stream->skip_data) { ! 1096: ! 1097: if (sc->flags & NGX_SPDY_FLAG_FIN) { ! 1098: stream->in_closed = 1; ! 1099: } ! 1100: ! 1101: /* TODO log and accounting */ ! 1102: return ngx_http_spdy_state_skip(sc, pos, end); ! 1103: } ! 1104: ! 1105: size = end - pos; ! 1106: ! 1107: if (size >= sc->length) { ! 1108: size = sc->length; ! 1109: complete = 1; ! 1110: ! 1111: } else { ! 1112: sc->length -= size; ! 1113: complete = 0; ! 1114: } ! 1115: ! 1116: r = stream->request; ! 1117: ! 1118: if (r->request_body == NULL ! 1119: && ngx_http_spdy_init_request_body(r) != NGX_OK) ! 1120: { ! 1121: stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR; ! 1122: return ngx_http_spdy_state_skip(sc, pos, end); ! 1123: } ! 1124: ! 1125: rb = r->request_body; ! 1126: tf = rb->temp_file; ! 1127: buf = rb->buf; ! 1128: ! 1129: if (size) { ! 1130: rb->rest += size; ! 1131: ! 1132: if (r->headers_in.content_length_n != -1 ! 1133: && r->headers_in.content_length_n < rb->rest) ! 1134: { ! 1135: /* TODO logging */ ! 1136: stream->skip_data = NGX_SPDY_DATA_ERROR; ! 1137: goto error; ! 1138: ! 1139: } else { ! 1140: clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ! 1141: ! 1142: if (clcf->client_max_body_size ! 1143: && clcf->client_max_body_size < rb->rest) ! 1144: { ! 1145: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ! 1146: "client intended to send too large chunked " ! 1147: "body: %O bytes", ! 1148: rb->rest); ! 1149: ! 1150: stream->skip_data = NGX_SPDY_DATA_ERROR; ! 1151: goto error; ! 1152: } ! 1153: } ! 1154: ! 1155: if (tf) { ! 1156: buf->start = pos; ! 1157: buf->pos = pos; ! 1158: ! 1159: pos += size; ! 1160: ! 1161: buf->end = pos; ! 1162: buf->last = pos; ! 1163: ! 1164: n = ngx_write_chain_to_temp_file(tf, rb->bufs); ! 1165: ! 1166: /* TODO: n == 0 or not complete and level event */ ! 1167: ! 1168: if (n == NGX_ERROR) { ! 1169: stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR; ! 1170: goto error; ! 1171: } ! 1172: ! 1173: tf->offset += n; ! 1174: ! 1175: } else { ! 1176: buf->last = ngx_cpymem(buf->last, pos, size); ! 1177: pos += size; ! 1178: } ! 1179: ! 1180: r->request_length += size; ! 1181: } ! 1182: ! 1183: if (!complete) { ! 1184: return ngx_http_spdy_state_save(sc, pos, end, ! 1185: ngx_http_spdy_state_data); ! 1186: } ! 1187: ! 1188: if (sc->flags & NGX_SPDY_FLAG_FIN) { ! 1189: ! 1190: stream->in_closed = 1; ! 1191: ! 1192: if (tf) { ! 1193: ngx_memzero(buf, sizeof(ngx_buf_t)); ! 1194: ! 1195: buf->in_file = 1; ! 1196: buf->file_last = tf->file.offset; ! 1197: buf->file = &tf->file; ! 1198: ! 1199: rb->buf = NULL; ! 1200: } ! 1201: ! 1202: if (r->headers_in.content_length_n < 0) { ! 1203: r->headers_in.content_length_n = rb->rest; ! 1204: } ! 1205: ! 1206: if (rb->post_handler) { ! 1207: rb->post_handler(r); ! 1208: } ! 1209: } ! 1210: ! 1211: return ngx_http_spdy_state_complete(sc, pos, end); ! 1212: ! 1213: error: ! 1214: ! 1215: if (rb->post_handler) { ! 1216: ! 1217: if (stream->skip_data == NGX_SPDY_DATA_ERROR) { ! 1218: rc = (r->headers_in.content_length_n == -1) ! 1219: ? NGX_HTTP_REQUEST_ENTITY_TOO_LARGE ! 1220: : NGX_HTTP_BAD_REQUEST; ! 1221: ! 1222: } else { ! 1223: rc = NGX_HTTP_INTERNAL_SERVER_ERROR; ! 1224: } ! 1225: ! 1226: ngx_http_finalize_request(r, rc); ! 1227: } ! 1228: ! 1229: return ngx_http_spdy_state_skip(sc, pos, end); ! 1230: } ! 1231: ! 1232: ! 1233: static u_char * ! 1234: ngx_http_spdy_state_rst_stream(ngx_http_spdy_connection_t *sc, u_char *pos, ! 1235: u_char *end) ! 1236: { ! 1237: ngx_uint_t sid, status; ! 1238: ngx_event_t *ev; ! 1239: ngx_connection_t *fc; ! 1240: ngx_http_request_t *r; ! 1241: ngx_http_spdy_stream_t *stream; ! 1242: ! 1243: if (end - pos < NGX_SPDY_RST_STREAM_SIZE) { ! 1244: return ngx_http_spdy_state_save(sc, pos, end, ! 1245: ngx_http_spdy_state_rst_stream); ! 1246: } ! 1247: ! 1248: if (sc->length != NGX_SPDY_RST_STREAM_SIZE) { ! 1249: /* TODO logging */ ! 1250: return ngx_http_spdy_state_protocol_error(sc); ! 1251: } ! 1252: ! 1253: sid = ngx_spdy_frame_parse_sid(pos); ! 1254: ! 1255: pos += NGX_SPDY_SID_SIZE; ! 1256: ! 1257: status = ngx_spdy_frame_parse_uint32(pos); ! 1258: ! 1259: pos += sizeof(uint32_t); ! 1260: ! 1261: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ! 1262: "spdy RST_STREAM sid:%ui st:%ui", sid, status); ! 1263: ! 1264: ! 1265: switch (status) { ! 1266: ! 1267: case NGX_SPDY_PROTOCOL_ERROR: ! 1268: /* TODO logging */ ! 1269: return ngx_http_spdy_state_protocol_error(sc); ! 1270: ! 1271: case NGX_SPDY_INVALID_STREAM: ! 1272: /* TODO */ ! 1273: break; ! 1274: ! 1275: case NGX_SPDY_REFUSED_STREAM: ! 1276: /* TODO */ ! 1277: break; ! 1278: ! 1279: case NGX_SPDY_UNSUPPORTED_VERSION: ! 1280: /* TODO logging */ ! 1281: return ngx_http_spdy_state_protocol_error(sc); ! 1282: ! 1283: case NGX_SPDY_CANCEL: ! 1284: case NGX_SPDY_INTERNAL_ERROR: ! 1285: stream = ngx_http_spdy_get_stream_by_id(sc, sid); ! 1286: if (stream == NULL) { ! 1287: /* TODO false cancel */ ! 1288: break; ! 1289: } ! 1290: ! 1291: stream->in_closed = 1; ! 1292: stream->out_closed = 1; ! 1293: ! 1294: r = stream->request; ! 1295: ! 1296: fc = r->connection; ! 1297: fc->error = 1; ! 1298: ! 1299: ev = fc->read; ! 1300: ev->handler(ev); ! 1301: ! 1302: break; ! 1303: ! 1304: case NGX_SPDY_FLOW_CONTROL_ERROR: ! 1305: /* TODO logging */ ! 1306: return ngx_http_spdy_state_protocol_error(sc); ! 1307: ! 1308: default: ! 1309: /* TODO */ ! 1310: return ngx_http_spdy_state_protocol_error(sc); ! 1311: } ! 1312: ! 1313: return ngx_http_spdy_state_complete(sc, pos, end); ! 1314: } ! 1315: ! 1316: ! 1317: static u_char * ! 1318: ngx_http_spdy_state_ping(ngx_http_spdy_connection_t *sc, u_char *pos, ! 1319: u_char *end) ! 1320: { ! 1321: u_char *p; ! 1322: ngx_buf_t *buf; ! 1323: ngx_http_spdy_out_frame_t *frame; ! 1324: ! 1325: if (end - pos < NGX_SPDY_PING_SIZE) { ! 1326: return ngx_http_spdy_state_save(sc, pos, end, ! 1327: ngx_http_spdy_state_ping); ! 1328: } ! 1329: ! 1330: if (sc->length != NGX_SPDY_PING_SIZE) { ! 1331: /* TODO logging */ ! 1332: return ngx_http_spdy_state_protocol_error(sc); ! 1333: } ! 1334: ! 1335: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ! 1336: "spdy PING frame"); ! 1337: ! 1338: frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_PING_SIZE, ! 1339: NGX_SPDY_HIGHEST_PRIORITY); ! 1340: if (frame == NULL) { ! 1341: return ngx_http_spdy_state_internal_error(sc); ! 1342: } ! 1343: ! 1344: buf = frame->first->buf; ! 1345: ! 1346: p = buf->pos; ! 1347: ! 1348: p = ngx_spdy_frame_write_head(p, NGX_SPDY_PING); ! 1349: p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_PING_SIZE); ! 1350: ! 1351: p = ngx_cpymem(p, pos, NGX_SPDY_PING_SIZE); ! 1352: ! 1353: buf->last = p; ! 1354: ! 1355: ngx_http_spdy_queue_frame(sc, frame); ! 1356: ! 1357: pos += NGX_SPDY_PING_SIZE; ! 1358: ! 1359: return ngx_http_spdy_state_complete(sc, pos, end); ! 1360: } ! 1361: ! 1362: ! 1363: static u_char * ! 1364: ngx_http_spdy_state_skip(ngx_http_spdy_connection_t *sc, u_char *pos, ! 1365: u_char *end) ! 1366: { ! 1367: size_t size; ! 1368: ! 1369: size = end - pos; ! 1370: ! 1371: if (size < sc->length) { ! 1372: sc->length -= size; ! 1373: return ngx_http_spdy_state_save(sc, end, end, ! 1374: ngx_http_spdy_state_skip); ! 1375: } ! 1376: ! 1377: return ngx_http_spdy_state_complete(sc, pos + sc->length, end); ! 1378: } ! 1379: ! 1380: ! 1381: static u_char * ! 1382: ngx_http_spdy_state_settings(ngx_http_spdy_connection_t *sc, u_char *pos, ! 1383: u_char *end) ! 1384: { ! 1385: ngx_uint_t v; ! 1386: ngx_http_spdy_srv_conf_t *sscf; ! 1387: ! 1388: if (sc->headers == 0) { ! 1389: ! 1390: if (end - pos < NGX_SPDY_SETTINGS_NUM_SIZE) { ! 1391: return ngx_http_spdy_state_save(sc, pos, end, ! 1392: ngx_http_spdy_state_settings); ! 1393: } ! 1394: ! 1395: sc->headers = ngx_spdy_frame_parse_uint32(pos); ! 1396: ! 1397: pos += NGX_SPDY_SETTINGS_NUM_SIZE; ! 1398: sc->length -= NGX_SPDY_SETTINGS_NUM_SIZE; ! 1399: ! 1400: if (sc->length < sc->headers * NGX_SPDY_SETTINGS_PAIR_SIZE) { ! 1401: /* TODO logging */ ! 1402: return ngx_http_spdy_state_protocol_error(sc); ! 1403: } ! 1404: ! 1405: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ! 1406: "spdy SETTINGS frame consists of %ui entries", ! 1407: sc->headers); ! 1408: } ! 1409: ! 1410: while (sc->headers) { ! 1411: if (end - pos < NGX_SPDY_SETTINGS_PAIR_SIZE) { ! 1412: return ngx_http_spdy_state_save(sc, pos, end, ! 1413: ngx_http_spdy_state_settings); ! 1414: } ! 1415: ! 1416: sc->headers--; ! 1417: ! 1418: if (pos[0] != NGX_SPDY_SETTINGS_MAX_STREAMS) { ! 1419: pos += NGX_SPDY_SETTINGS_PAIR_SIZE; ! 1420: sc->length -= NGX_SPDY_SETTINGS_PAIR_SIZE; ! 1421: continue; ! 1422: } ! 1423: ! 1424: v = ngx_spdy_frame_parse_uint32(pos + NGX_SPDY_SETTINGS_IDF_SIZE); ! 1425: ! 1426: sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, ! 1427: ngx_http_spdy_module); ! 1428: ! 1429: if (v != sscf->concurrent_streams) { ! 1430: ngx_http_spdy_send_settings(sc); ! 1431: } ! 1432: ! 1433: return ngx_http_spdy_state_skip(sc, pos, end); ! 1434: } ! 1435: ! 1436: ngx_http_spdy_send_settings(sc); ! 1437: ! 1438: return ngx_http_spdy_state_complete(sc, pos, end); ! 1439: } ! 1440: ! 1441: ! 1442: static u_char * ! 1443: ngx_http_spdy_state_noop(ngx_http_spdy_connection_t *sc, u_char *pos, ! 1444: u_char *end) ! 1445: { ! 1446: if (sc->length) { ! 1447: /* TODO logging */ ! 1448: return ngx_http_spdy_state_protocol_error(sc); ! 1449: } ! 1450: ! 1451: return ngx_http_spdy_state_complete(sc, pos, end); ! 1452: } ! 1453: ! 1454: ! 1455: static u_char * ! 1456: ngx_http_spdy_state_complete(ngx_http_spdy_connection_t *sc, u_char *pos, ! 1457: u_char *end) ! 1458: { ! 1459: sc->handler = ngx_http_spdy_state_head; ! 1460: return pos; ! 1461: } ! 1462: ! 1463: ! 1464: static u_char * ! 1465: ngx_http_spdy_state_save(ngx_http_spdy_connection_t *sc, ! 1466: u_char *pos, u_char *end, ngx_http_spdy_handler_pt handler) ! 1467: { ! 1468: #if (NGX_DEBUG) ! 1469: if (end - pos > NGX_SPDY_STATE_BUFFER_SIZE) { ! 1470: ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0, ! 1471: "spdy state buffer overflow: " ! 1472: "%i bytes required", end - pos); ! 1473: return ngx_http_spdy_state_internal_error(sc); ! 1474: } ! 1475: #endif ! 1476: ! 1477: ngx_memcpy(sc->buffer, pos, NGX_SPDY_STATE_BUFFER_SIZE); ! 1478: ! 1479: sc->buffer_used = end - pos; ! 1480: sc->handler = handler; ! 1481: sc->waiting = 1; ! 1482: ! 1483: return end; ! 1484: } ! 1485: ! 1486: ! 1487: static u_char * ! 1488: ngx_http_spdy_state_protocol_error(ngx_http_spdy_connection_t *sc) ! 1489: { ! 1490: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ! 1491: "spdy state protocol error"); ! 1492: ! 1493: /* TODO */ ! 1494: ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST); ! 1495: return NULL; ! 1496: } ! 1497: ! 1498: ! 1499: static u_char * ! 1500: ngx_http_spdy_state_internal_error(ngx_http_spdy_connection_t *sc) ! 1501: { ! 1502: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ! 1503: "spdy state internal error"); ! 1504: ! 1505: /* TODO */ ! 1506: ngx_http_spdy_finalize_connection(sc, NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1507: return NULL; ! 1508: } ! 1509: ! 1510: ! 1511: static ngx_int_t ! 1512: ngx_http_spdy_send_rst_stream(ngx_http_spdy_connection_t *sc, ngx_uint_t sid, ! 1513: ngx_uint_t status, ngx_uint_t priority) ! 1514: { ! 1515: u_char *p; ! 1516: ngx_buf_t *buf; ! 1517: ngx_http_spdy_out_frame_t *frame; ! 1518: ! 1519: if (sc->connection->error) { ! 1520: return NGX_OK; ! 1521: } ! 1522: ! 1523: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ! 1524: "spdy write RST_STREAM sid:%ui st:%ui", sid, status); ! 1525: ! 1526: frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_RST_STREAM_SIZE, ! 1527: priority); ! 1528: if (frame == NULL) { ! 1529: return NGX_ERROR; ! 1530: } ! 1531: ! 1532: buf = frame->first->buf; ! 1533: ! 1534: p = buf->pos; ! 1535: ! 1536: p = ngx_spdy_frame_write_head(p, NGX_SPDY_RST_STREAM); ! 1537: p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_RST_STREAM_SIZE); ! 1538: ! 1539: p = ngx_spdy_frame_write_sid(p, sid); ! 1540: p = ngx_spdy_frame_aligned_write_uint32(p, status); ! 1541: ! 1542: buf->last = p; ! 1543: ! 1544: ngx_http_spdy_queue_frame(sc, frame); ! 1545: ! 1546: return NGX_OK; ! 1547: } ! 1548: ! 1549: ! 1550: #if 0 ! 1551: static ngx_int_t ! 1552: ngx_http_spdy_send_goaway(ngx_http_spdy_connection_t *sc) ! 1553: { ! 1554: u_char *p; ! 1555: ngx_buf_t *buf; ! 1556: ngx_http_spdy_out_frame_t *frame; ! 1557: ! 1558: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ! 1559: "spdy create GOAWAY sid:%ui", sc->last_sid); ! 1560: ! 1561: frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_GOAWAY_SIZE, ! 1562: NGX_SPDY_HIGHEST_PRIORITY); ! 1563: if (frame == NULL) { ! 1564: return NGX_ERROR; ! 1565: } ! 1566: ! 1567: buf = frame->first->buf; ! 1568: ! 1569: p = buf->pos; ! 1570: ! 1571: p = ngx_spdy_frame_write_head(p, NGX_SPDY_GOAWAY); ! 1572: p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_GOAWAY_SIZE); ! 1573: ! 1574: p = ngx_spdy_frame_write_sid(p, sc->last_sid); ! 1575: ! 1576: buf->last = p; ! 1577: ! 1578: ngx_http_spdy_queue_frame(sc, frame); ! 1579: ! 1580: return NGX_OK; ! 1581: } ! 1582: #endif ! 1583: ! 1584: ! 1585: static ngx_int_t ! 1586: ngx_http_spdy_send_settings(ngx_http_spdy_connection_t *sc) ! 1587: { ! 1588: u_char *p; ! 1589: ngx_buf_t *buf; ! 1590: ngx_pool_t *pool; ! 1591: ngx_chain_t *cl; ! 1592: ngx_http_spdy_srv_conf_t *sscf; ! 1593: ngx_http_spdy_out_frame_t *frame; ! 1594: ! 1595: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ! 1596: "spdy create SETTINGS frame"); ! 1597: ! 1598: pool = sc->connection->pool; ! 1599: ! 1600: frame = ngx_palloc(pool, sizeof(ngx_http_spdy_out_frame_t)); ! 1601: if (frame == NULL) { ! 1602: return NGX_ERROR; ! 1603: } ! 1604: ! 1605: cl = ngx_alloc_chain_link(pool); ! 1606: if (cl == NULL) { ! 1607: return NGX_ERROR; ! 1608: } ! 1609: ! 1610: buf = ngx_create_temp_buf(pool, NGX_SPDY_FRAME_HEADER_SIZE ! 1611: + NGX_SPDY_SETTINGS_NUM_SIZE ! 1612: + NGX_SPDY_SETTINGS_PAIR_SIZE); ! 1613: if (buf == NULL) { ! 1614: return NGX_ERROR; ! 1615: } ! 1616: ! 1617: buf->last_buf = 1; ! 1618: ! 1619: cl->buf = buf; ! 1620: cl->next = NULL; ! 1621: ! 1622: frame->first = cl; ! 1623: frame->last = cl; ! 1624: frame->handler = ngx_http_spdy_settings_frame_handler; ! 1625: #if (NGX_DEBUG) ! 1626: frame->stream = NULL; ! 1627: frame->size = NGX_SPDY_FRAME_HEADER_SIZE ! 1628: + NGX_SPDY_SETTINGS_NUM_SIZE ! 1629: + NGX_SPDY_SETTINGS_PAIR_SIZE; ! 1630: #endif ! 1631: frame->priority = NGX_SPDY_HIGHEST_PRIORITY; ! 1632: frame->blocked = 0; ! 1633: ! 1634: p = buf->pos; ! 1635: ! 1636: p = ngx_spdy_frame_write_head(p, NGX_SPDY_SETTINGS); ! 1637: p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_CLEAR_SETTINGS, ! 1638: NGX_SPDY_SETTINGS_NUM_SIZE ! 1639: + NGX_SPDY_SETTINGS_PAIR_SIZE); ! 1640: ! 1641: p = ngx_spdy_frame_aligned_write_uint32(p, 1); ! 1642: p = ngx_spdy_frame_aligned_write_uint32(p, ! 1643: NGX_SPDY_SETTINGS_MAX_STREAMS << 24 ! 1644: | NGX_SPDY_SETTINGS_FLAG_PERSIST); ! 1645: ! 1646: sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, ! 1647: ngx_http_spdy_module); ! 1648: ! 1649: p = ngx_spdy_frame_aligned_write_uint32(p, sscf->concurrent_streams); ! 1650: ! 1651: buf->last = p; ! 1652: ! 1653: ngx_http_spdy_queue_frame(sc, frame); ! 1654: ! 1655: return NGX_OK; ! 1656: } ! 1657: ! 1658: ! 1659: ngx_int_t ! 1660: ngx_http_spdy_settings_frame_handler(ngx_http_spdy_connection_t *sc, ! 1661: ngx_http_spdy_out_frame_t *frame) ! 1662: { ! 1663: ngx_buf_t *buf; ! 1664: ! 1665: buf = frame->first->buf; ! 1666: ! 1667: if (buf->pos != buf->last) { ! 1668: return NGX_AGAIN; ! 1669: } ! 1670: ! 1671: ngx_free_chain(sc->pool, frame->first); ! 1672: ! 1673: return NGX_OK; ! 1674: } ! 1675: ! 1676: ! 1677: static ngx_http_spdy_out_frame_t * ! 1678: ngx_http_spdy_get_ctl_frame(ngx_http_spdy_connection_t *sc, size_t size, ! 1679: ngx_uint_t priority) ! 1680: { ! 1681: ngx_chain_t *cl; ! 1682: ngx_http_spdy_out_frame_t *frame; ! 1683: ! 1684: frame = sc->free_ctl_frames; ! 1685: ! 1686: if (frame) { ! 1687: sc->free_ctl_frames = frame->free; ! 1688: ! 1689: cl = frame->first; ! 1690: cl->buf->pos = cl->buf->start; ! 1691: ! 1692: } else { ! 1693: frame = ngx_palloc(sc->pool, sizeof(ngx_http_spdy_out_frame_t)); ! 1694: if (frame == NULL) { ! 1695: return NULL; ! 1696: } ! 1697: ! 1698: cl = ngx_alloc_chain_link(sc->pool); ! 1699: if (cl == NULL) { ! 1700: return NULL; ! 1701: } ! 1702: ! 1703: cl->buf = ngx_create_temp_buf(sc->pool, ! 1704: NGX_SPDY_CTL_FRAME_BUFFER_SIZE); ! 1705: if (cl->buf == NULL) { ! 1706: return NULL; ! 1707: } ! 1708: ! 1709: cl->buf->last_buf = 1; ! 1710: ! 1711: frame->first = cl; ! 1712: frame->last = cl; ! 1713: frame->handler = ngx_http_spdy_ctl_frame_handler; ! 1714: } ! 1715: ! 1716: frame->free = NULL; ! 1717: ! 1718: #if (NGX_DEBUG) ! 1719: if (size > NGX_SPDY_CTL_FRAME_BUFFER_SIZE - NGX_SPDY_FRAME_HEADER_SIZE) { ! 1720: ngx_log_error(NGX_LOG_ALERT, sc->pool->log, 0, ! 1721: "requested control frame is too big: %z", size); ! 1722: return NULL; ! 1723: } ! 1724: ! 1725: frame->stream = NULL; ! 1726: frame->size = size; ! 1727: #endif ! 1728: ! 1729: frame->priority = priority; ! 1730: frame->blocked = 0; ! 1731: ! 1732: return frame; ! 1733: } ! 1734: ! 1735: ! 1736: static ngx_int_t ! 1737: ngx_http_spdy_ctl_frame_handler(ngx_http_spdy_connection_t *sc, ! 1738: ngx_http_spdy_out_frame_t *frame) ! 1739: { ! 1740: ngx_buf_t *buf; ! 1741: ! 1742: buf = frame->first->buf; ! 1743: ! 1744: if (buf->pos != buf->last) { ! 1745: return NGX_AGAIN; ! 1746: } ! 1747: ! 1748: frame->free = sc->free_ctl_frames; ! 1749: sc->free_ctl_frames = frame; ! 1750: ! 1751: return NGX_OK; ! 1752: } ! 1753: ! 1754: ! 1755: static ngx_http_spdy_stream_t * ! 1756: ngx_http_spdy_create_stream(ngx_http_spdy_connection_t *sc, ngx_uint_t id, ! 1757: ngx_uint_t priority) ! 1758: { ! 1759: ngx_log_t *log; ! 1760: ngx_uint_t index; ! 1761: ngx_event_t *rev, *wev; ! 1762: ngx_connection_t *fc; ! 1763: ngx_http_log_ctx_t *ctx; ! 1764: ngx_http_request_t *r; ! 1765: ngx_http_spdy_stream_t *stream; ! 1766: ngx_http_core_srv_conf_t *cscf; ! 1767: ngx_http_spdy_srv_conf_t *sscf; ! 1768: ! 1769: fc = sc->free_fake_connections; ! 1770: ! 1771: if (fc) { ! 1772: sc->free_fake_connections = fc->data; ! 1773: ! 1774: rev = fc->read; ! 1775: wev = fc->write; ! 1776: log = fc->log; ! 1777: ctx = log->data; ! 1778: ! 1779: } else { ! 1780: fc = ngx_palloc(sc->pool, sizeof(ngx_connection_t)); ! 1781: if (fc == NULL) { ! 1782: return NULL; ! 1783: } ! 1784: ! 1785: rev = ngx_palloc(sc->pool, sizeof(ngx_event_t)); ! 1786: if (rev == NULL) { ! 1787: return NULL; ! 1788: } ! 1789: ! 1790: wev = ngx_palloc(sc->pool, sizeof(ngx_event_t)); ! 1791: if (wev == NULL) { ! 1792: return NULL; ! 1793: } ! 1794: ! 1795: log = ngx_palloc(sc->pool, sizeof(ngx_log_t)); ! 1796: if (log == NULL) { ! 1797: return NULL; ! 1798: } ! 1799: ! 1800: ctx = ngx_palloc(sc->pool, sizeof(ngx_http_log_ctx_t)); ! 1801: if (ctx == NULL) { ! 1802: return NULL; ! 1803: } ! 1804: ! 1805: ctx->connection = fc; ! 1806: ctx->request = NULL; ! 1807: } ! 1808: ! 1809: ngx_memcpy(log, sc->connection->log, sizeof(ngx_log_t)); ! 1810: ! 1811: log->data = ctx; ! 1812: ! 1813: ngx_memzero(rev, sizeof(ngx_event_t)); ! 1814: ! 1815: rev->data = fc; ! 1816: rev->ready = 1; ! 1817: rev->handler = ngx_http_empty_handler; ! 1818: rev->log = log; ! 1819: ! 1820: ngx_memcpy(wev, rev, sizeof(ngx_event_t)); ! 1821: ! 1822: wev->write = 1; ! 1823: ! 1824: ngx_memcpy(fc, sc->connection, sizeof(ngx_connection_t)); ! 1825: ! 1826: fc->data = sc->http_connection; ! 1827: fc->read = rev; ! 1828: fc->write = wev; ! 1829: fc->sent = 0; ! 1830: fc->log = log; ! 1831: fc->buffered = 0; ! 1832: fc->sndlowat = 1; ! 1833: fc->tcp_nodelay = NGX_TCP_NODELAY_DISABLED; ! 1834: ! 1835: r = ngx_http_create_request(fc); ! 1836: if (r == NULL) { ! 1837: return NULL; ! 1838: } ! 1839: ! 1840: r->valid_location = 1; ! 1841: ! 1842: fc->data = r; ! 1843: sc->connection->requests++; ! 1844: ! 1845: cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); ! 1846: ! 1847: r->header_in = ngx_create_temp_buf(r->pool, ! 1848: cscf->client_header_buffer_size); ! 1849: if (r->header_in == NULL) { ! 1850: ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1851: return NULL; ! 1852: } ! 1853: ! 1854: r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; ! 1855: ! 1856: stream = ngx_pcalloc(r->pool, sizeof(ngx_http_spdy_stream_t)); ! 1857: if (stream == NULL) { ! 1858: ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1859: return NULL; ! 1860: } ! 1861: ! 1862: r->spdy_stream = stream; ! 1863: ! 1864: stream->id = id; ! 1865: stream->request = r; ! 1866: stream->connection = sc; ! 1867: stream->priority = priority; ! 1868: ! 1869: sscf = ngx_http_get_module_srv_conf(r, ngx_http_spdy_module); ! 1870: ! 1871: index = ngx_http_spdy_stream_index(sscf, id); ! 1872: ! 1873: stream->index = sc->streams_index[index]; ! 1874: sc->streams_index[index] = stream; ! 1875: ! 1876: sc->processing++; ! 1877: ! 1878: return stream; ! 1879: } ! 1880: ! 1881: ! 1882: static ngx_http_spdy_stream_t * ! 1883: ngx_http_spdy_get_stream_by_id(ngx_http_spdy_connection_t *sc, ! 1884: ngx_uint_t sid) ! 1885: { ! 1886: ngx_http_spdy_stream_t *stream; ! 1887: ngx_http_spdy_srv_conf_t *sscf; ! 1888: ! 1889: sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, ! 1890: ngx_http_spdy_module); ! 1891: ! 1892: stream = sc->streams_index[ngx_http_spdy_stream_index(sscf, sid)]; ! 1893: ! 1894: while (stream) { ! 1895: if (stream->id == sid) { ! 1896: return stream; ! 1897: } ! 1898: ! 1899: stream = stream->index; ! 1900: } ! 1901: ! 1902: return NULL; ! 1903: } ! 1904: ! 1905: ! 1906: static ngx_int_t ! 1907: ngx_http_spdy_parse_header(ngx_http_request_t *r) ! 1908: { ! 1909: u_char *p, *end, ch; ! 1910: ngx_uint_t len, hash; ! 1911: ngx_http_core_srv_conf_t *cscf; ! 1912: ! 1913: enum { ! 1914: sw_name_len = 0, ! 1915: sw_name, ! 1916: sw_value_len, ! 1917: sw_value ! 1918: } state; ! 1919: ! 1920: state = r->state; ! 1921: ! 1922: p = r->header_in->pos; ! 1923: end = r->header_in->last; ! 1924: ! 1925: switch (state) { ! 1926: ! 1927: case sw_name_len: ! 1928: ! 1929: if (end - p < NGX_SPDY_NV_NLEN_SIZE) { ! 1930: return NGX_AGAIN; ! 1931: } ! 1932: ! 1933: len = ngx_spdy_frame_parse_uint16(p); ! 1934: ! 1935: if (!len) { ! 1936: return NGX_HTTP_PARSE_INVALID_HEADER; ! 1937: } ! 1938: ! 1939: p += NGX_SPDY_NV_NLEN_SIZE; ! 1940: ! 1941: r->header_name_end = p + len; ! 1942: r->lowcase_index = len; ! 1943: r->invalid_header = 0; ! 1944: ! 1945: state = sw_name; ! 1946: ! 1947: /* fall through */ ! 1948: ! 1949: case sw_name: ! 1950: ! 1951: if (r->header_name_end > end) { ! 1952: break; ! 1953: } ! 1954: ! 1955: cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); ! 1956: ! 1957: r->header_name_start = p; ! 1958: ! 1959: hash = 0; ! 1960: ! 1961: for ( /* void */ ; p != r->header_name_end; p++) { ! 1962: ! 1963: ch = *p; ! 1964: ! 1965: hash = ngx_hash(hash, ch); ! 1966: ! 1967: if ((ch >= 'a' && ch <= 'z') ! 1968: || (ch == '-') ! 1969: || (ch >= '0' && ch <= '9') ! 1970: || (ch == '_' && cscf->underscores_in_headers)) ! 1971: { ! 1972: continue; ! 1973: } ! 1974: ! 1975: switch (ch) { ! 1976: case '\0': ! 1977: case LF: ! 1978: case CR: ! 1979: case ':': ! 1980: return NGX_HTTP_PARSE_INVALID_REQUEST; ! 1981: } ! 1982: ! 1983: if (ch >= 'A' && ch <= 'Z') { ! 1984: return NGX_HTTP_PARSE_INVALID_HEADER; ! 1985: } ! 1986: ! 1987: r->invalid_header = 1; ! 1988: } ! 1989: ! 1990: r->header_hash = hash; ! 1991: ! 1992: state = sw_value_len; ! 1993: ! 1994: /* fall through */ ! 1995: ! 1996: case sw_value_len: ! 1997: ! 1998: if (end - p < NGX_SPDY_NV_VLEN_SIZE) { ! 1999: break; ! 2000: } ! 2001: ! 2002: len = ngx_spdy_frame_parse_uint16(p); ! 2003: ! 2004: if (!len) { ! 2005: return NGX_ERROR; ! 2006: } ! 2007: ! 2008: p += NGX_SPDY_NV_VLEN_SIZE; ! 2009: ! 2010: r->header_end = p + len; ! 2011: ! 2012: state = sw_value; ! 2013: ! 2014: /* fall through */ ! 2015: ! 2016: case sw_value: ! 2017: ! 2018: if (r->header_end > end) { ! 2019: break; ! 2020: } ! 2021: ! 2022: r->header_start = p; ! 2023: ! 2024: for ( /* void */ ; p != r->header_end; p++) { ! 2025: ! 2026: ch = *p; ! 2027: ! 2028: if (ch == '\0') { ! 2029: ! 2030: if (p == r->header_start) { ! 2031: return NGX_ERROR; ! 2032: } ! 2033: ! 2034: r->header_size = p - r->header_start; ! 2035: r->header_in->pos = p + 1; ! 2036: ! 2037: return NGX_OK; ! 2038: } ! 2039: ! 2040: if (ch == CR || ch == LF) { ! 2041: return NGX_HTTP_PARSE_INVALID_HEADER; ! 2042: } ! 2043: } ! 2044: ! 2045: r->header_size = p - r->header_start; ! 2046: r->header_in->pos = p; ! 2047: ! 2048: r->state = 0; ! 2049: ! 2050: return NGX_DONE; ! 2051: } ! 2052: ! 2053: r->header_in->pos = p; ! 2054: r->state = state; ! 2055: ! 2056: return NGX_AGAIN; ! 2057: } ! 2058: ! 2059: ! 2060: static ngx_int_t ! 2061: ngx_http_spdy_alloc_large_header_buffer(ngx_http_request_t *r) ! 2062: { ! 2063: u_char *old, *new; ! 2064: size_t rest; ! 2065: ngx_buf_t *buf; ! 2066: ngx_http_spdy_stream_t *stream; ! 2067: ngx_http_core_srv_conf_t *cscf; ! 2068: ! 2069: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 2070: "spdy alloc large header buffer"); ! 2071: ! 2072: stream = r->spdy_stream; ! 2073: ! 2074: cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); ! 2075: ! 2076: if (stream->header_buffers ! 2077: == (ngx_uint_t) cscf->large_client_header_buffers.num) ! 2078: { ! 2079: return NGX_DECLINED; ! 2080: } ! 2081: ! 2082: rest = r->header_in->last - r->header_in->pos; ! 2083: ! 2084: if (rest >= cscf->large_client_header_buffers.size) { ! 2085: return NGX_DECLINED; ! 2086: } ! 2087: ! 2088: buf = ngx_create_temp_buf(r->pool, cscf->large_client_header_buffers.size); ! 2089: if (buf == NULL) { ! 2090: return NGX_ERROR; ! 2091: } ! 2092: ! 2093: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 2094: "spdy large header alloc: %p %uz", ! 2095: buf->pos, buf->end - buf->last); ! 2096: ! 2097: old = r->header_in->pos; ! 2098: new = buf->pos; ! 2099: ! 2100: if (rest) { ! 2101: buf->last = ngx_cpymem(new, old, rest); ! 2102: } ! 2103: ! 2104: if (r->header_name_end > old) { ! 2105: r->header_name_end = new + (r->header_name_end - old); ! 2106: ! 2107: } else if (r->header_end > old) { ! 2108: r->header_end = new + (r->header_end - old); ! 2109: } ! 2110: ! 2111: r->header_in = buf; ! 2112: ! 2113: stream->header_buffers++; ! 2114: ! 2115: return NGX_OK; ! 2116: } ! 2117: ! 2118: ! 2119: static ngx_int_t ! 2120: ngx_http_spdy_handle_request_header(ngx_http_request_t *r) ! 2121: { ! 2122: ngx_uint_t i; ! 2123: ngx_table_elt_t *h; ! 2124: ngx_http_core_srv_conf_t *cscf; ! 2125: ngx_http_spdy_request_header_t *sh; ! 2126: ! 2127: if (r->invalid_header) { ! 2128: cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); ! 2129: ! 2130: if (cscf->ignore_invalid_headers) { ! 2131: ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ! 2132: "client sent invalid header: \"%*s\"", ! 2133: r->header_end - r->header_name_start, ! 2134: r->header_name_start); ! 2135: return NGX_OK; ! 2136: } ! 2137: ! 2138: } else { ! 2139: for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) { ! 2140: sh = &ngx_http_spdy_request_headers[i]; ! 2141: ! 2142: if (sh->hash != r->header_hash ! 2143: || sh->len != r->lowcase_index ! 2144: || ngx_strncmp(sh->header, r->header_name_start, ! 2145: r->lowcase_index) ! 2146: != 0) ! 2147: { ! 2148: continue; ! 2149: } ! 2150: ! 2151: return sh->handler(r); ! 2152: } ! 2153: } ! 2154: ! 2155: h = ngx_list_push(&r->headers_in.headers); ! 2156: if (h == NULL) { ! 2157: ngx_http_spdy_close_stream(r->spdy_stream, ! 2158: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 2159: return NGX_ERROR; ! 2160: } ! 2161: ! 2162: h->hash = r->header_hash; ! 2163: ! 2164: h->key.len = r->lowcase_index; ! 2165: h->key.data = r->header_name_start; ! 2166: h->key.data[h->key.len] = '\0'; ! 2167: ! 2168: h->value.len = r->header_size; ! 2169: h->value.data = r->header_start; ! 2170: h->value.data[h->value.len] = '\0'; ! 2171: ! 2172: h->lowcase_key = h->key.data; ! 2173: ! 2174: return NGX_OK; ! 2175: } ! 2176: ! 2177: ! 2178: void ! 2179: ngx_http_spdy_request_headers_init() ! 2180: { ! 2181: ngx_uint_t i; ! 2182: ngx_http_spdy_request_header_t *h; ! 2183: ! 2184: for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) { ! 2185: h = &ngx_http_spdy_request_headers[i]; ! 2186: h->hash = ngx_hash_key(h->header, h->len); ! 2187: } ! 2188: } ! 2189: ! 2190: ! 2191: static ngx_int_t ! 2192: ngx_http_spdy_parse_method(ngx_http_request_t *r) ! 2193: { ! 2194: size_t k, len; ! 2195: ngx_uint_t n; ! 2196: const u_char *p, *m; ! 2197: ! 2198: /* ! 2199: * This array takes less than 256 sequential bytes, ! 2200: * and if typical CPU cache line size is 64 bytes, ! 2201: * it is prefetched for 4 load operations. ! 2202: */ ! 2203: static const struct { ! 2204: u_char len; ! 2205: const u_char method[11]; ! 2206: uint32_t value; ! 2207: } tests[] = { ! 2208: { 3, "GET", NGX_HTTP_GET }, ! 2209: { 4, "POST", NGX_HTTP_POST }, ! 2210: { 4, "HEAD", NGX_HTTP_HEAD }, ! 2211: { 7, "OPTIONS", NGX_HTTP_OPTIONS }, ! 2212: { 8, "PROPFIND", NGX_HTTP_PROPFIND }, ! 2213: { 3, "PUT", NGX_HTTP_PUT }, ! 2214: { 5, "MKCOL", NGX_HTTP_MKCOL }, ! 2215: { 6, "DELETE", NGX_HTTP_DELETE }, ! 2216: { 4, "COPY", NGX_HTTP_COPY }, ! 2217: { 4, "MOVE", NGX_HTTP_MOVE }, ! 2218: { 9, "PROPPATCH", NGX_HTTP_PROPPATCH }, ! 2219: { 4, "LOCK", NGX_HTTP_LOCK }, ! 2220: { 6, "UNLOCK", NGX_HTTP_UNLOCK }, ! 2221: { 5, "PATCH", NGX_HTTP_PATCH }, ! 2222: { 5, "TRACE", NGX_HTTP_TRACE } ! 2223: }, *test; ! 2224: ! 2225: if (r->method_name.len) { ! 2226: return NGX_HTTP_PARSE_INVALID_HEADER; ! 2227: } ! 2228: ! 2229: len = r->header_size; ! 2230: ! 2231: r->method_name.len = len; ! 2232: r->method_name.data = r->header_start; ! 2233: ! 2234: test = tests; ! 2235: n = sizeof(tests) / sizeof(tests[0]); ! 2236: ! 2237: do { ! 2238: if (len == test->len) { ! 2239: p = r->method_name.data; ! 2240: m = test->method; ! 2241: k = len; ! 2242: ! 2243: do { ! 2244: if (*p++ != *m++) { ! 2245: goto next; ! 2246: } ! 2247: } while (--k); ! 2248: ! 2249: r->method = test->value; ! 2250: return NGX_OK; ! 2251: } ! 2252: ! 2253: next: ! 2254: test++; ! 2255: ! 2256: } while (--n); ! 2257: ! 2258: p = r->method_name.data; ! 2259: ! 2260: do { ! 2261: if ((*p < 'A' || *p > 'Z') && *p != '_') { ! 2262: ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, ! 2263: "client sent invalid method"); ! 2264: return NGX_HTTP_PARSE_INVALID_REQUEST; ! 2265: } ! 2266: ! 2267: p++; ! 2268: ! 2269: } while (--len); ! 2270: ! 2271: return NGX_OK; ! 2272: } ! 2273: ! 2274: ! 2275: static ngx_int_t ! 2276: ngx_http_spdy_parse_scheme(ngx_http_request_t *r) ! 2277: { ! 2278: if (r->schema_start) { ! 2279: return NGX_HTTP_PARSE_INVALID_HEADER; ! 2280: } ! 2281: ! 2282: r->schema_start = r->header_start; ! 2283: r->schema_end = r->header_end; ! 2284: ! 2285: return NGX_OK; ! 2286: } ! 2287: ! 2288: ! 2289: static ngx_int_t ! 2290: ngx_http_spdy_parse_url(ngx_http_request_t *r) ! 2291: { ! 2292: if (r->unparsed_uri.len) { ! 2293: return NGX_HTTP_PARSE_INVALID_HEADER; ! 2294: } ! 2295: ! 2296: r->uri_start = r->header_start; ! 2297: r->uri_end = r->header_end; ! 2298: ! 2299: if (ngx_http_parse_uri(r) != NGX_OK) { ! 2300: return NGX_HTTP_PARSE_INVALID_REQUEST; ! 2301: } ! 2302: ! 2303: if (ngx_http_process_request_uri(r) != NGX_OK) { ! 2304: return NGX_ERROR; ! 2305: } ! 2306: ! 2307: return NGX_OK; ! 2308: } ! 2309: ! 2310: ! 2311: static ngx_int_t ! 2312: ngx_http_spdy_parse_version(ngx_http_request_t *r) ! 2313: { ! 2314: u_char *p, ch; ! 2315: ! 2316: if (r->http_protocol.len) { ! 2317: return NGX_HTTP_PARSE_INVALID_HEADER; ! 2318: } ! 2319: ! 2320: p = r->header_start; ! 2321: ! 2322: if (r->header_size < 8 || !(ngx_str5cmp(p, 'H', 'T', 'T', 'P', '/'))) { ! 2323: return NGX_HTTP_PARSE_INVALID_REQUEST; ! 2324: } ! 2325: ! 2326: ch = *(p + 5); ! 2327: ! 2328: if (ch < '1' || ch > '9') { ! 2329: return NGX_HTTP_PARSE_INVALID_REQUEST; ! 2330: } ! 2331: ! 2332: r->http_major = ch - '0'; ! 2333: ! 2334: for (p += 6; p != r->header_end - 2; p++) { ! 2335: ! 2336: ch = *p; ! 2337: ! 2338: if (ch < '0' || ch > '9') { ! 2339: return NGX_HTTP_PARSE_INVALID_REQUEST; ! 2340: } ! 2341: ! 2342: r->http_major = r->http_major * 10 + ch - '0'; ! 2343: } ! 2344: ! 2345: if (*p != '.') { ! 2346: return NGX_HTTP_PARSE_INVALID_REQUEST; ! 2347: } ! 2348: ! 2349: ch = *(p + 1); ! 2350: ! 2351: if (ch < '0' || ch > '9') { ! 2352: return NGX_HTTP_PARSE_INVALID_REQUEST; ! 2353: } ! 2354: ! 2355: r->http_minor = ch - '0'; ! 2356: ! 2357: for (p += 2; p != r->header_end; p++) { ! 2358: ! 2359: ch = *p; ! 2360: ! 2361: if (ch < '0' || ch > '9') { ! 2362: return NGX_HTTP_PARSE_INVALID_REQUEST; ! 2363: } ! 2364: ! 2365: r->http_minor = r->http_minor * 10 + ch - '0'; ! 2366: } ! 2367: ! 2368: r->http_protocol.len = r->header_size; ! 2369: r->http_protocol.data = r->header_start; ! 2370: r->http_version = r->http_major * 1000 + r->http_minor; ! 2371: ! 2372: return NGX_OK; ! 2373: } ! 2374: ! 2375: ! 2376: static ngx_int_t ! 2377: ngx_http_spdy_construct_request_line(ngx_http_request_t *r) ! 2378: { ! 2379: u_char *p; ! 2380: ! 2381: if (r->method_name.len == 0 ! 2382: || r->unparsed_uri.len == 0 ! 2383: || r->http_protocol.len == 0) ! 2384: { ! 2385: ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); ! 2386: return NGX_ERROR; ! 2387: } ! 2388: ! 2389: r->request_line.len = r->method_name.len + 1 ! 2390: + r->unparsed_uri.len + 1 ! 2391: + r->http_protocol.len; ! 2392: ! 2393: p = ngx_pnalloc(r->pool, r->request_line.len + 1); ! 2394: if (p == NULL) { ! 2395: ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); ! 2396: return NGX_ERROR; ! 2397: } ! 2398: ! 2399: r->request_line.data = p; ! 2400: ! 2401: p = ngx_cpymem(p, r->method_name.data, r->method_name.len); ! 2402: ! 2403: *p++ = ' '; ! 2404: ! 2405: p = ngx_cpymem(p, r->unparsed_uri.data, r->unparsed_uri.len); ! 2406: ! 2407: *p++ = ' '; ! 2408: ! 2409: ngx_memcpy(p, r->http_protocol.data, r->http_protocol.len + 1); ! 2410: ! 2411: /* some modules expect the space character after method name */ ! 2412: r->method_name.data = r->request_line.data; ! 2413: ! 2414: return NGX_OK; ! 2415: } ! 2416: ! 2417: ! 2418: static void ! 2419: ngx_http_spdy_run_request(ngx_http_request_t *r) ! 2420: { ! 2421: ngx_uint_t i; ! 2422: ngx_list_part_t *part; ! 2423: ngx_table_elt_t *h; ! 2424: ngx_http_header_t *hh; ! 2425: ngx_http_core_main_conf_t *cmcf; ! 2426: ! 2427: if (ngx_http_spdy_construct_request_line(r) != NGX_OK) { ! 2428: return; ! 2429: } ! 2430: ! 2431: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 2432: "spdy http request line: \"%V\"", &r->request_line); ! 2433: ! 2434: cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); ! 2435: ! 2436: part = &r->headers_in.headers.part; ! 2437: h = part->elts; ! 2438: ! 2439: for (i = 0 ;; i++) { ! 2440: ! 2441: if (i >= part->nelts) { ! 2442: if (part->next == NULL) { ! 2443: break; ! 2444: } ! 2445: ! 2446: part = part->next; ! 2447: h = part->elts; ! 2448: i = 0; ! 2449: } ! 2450: ! 2451: hh = ngx_hash_find(&cmcf->headers_in_hash, h[i].hash, ! 2452: h[i].lowcase_key, h[i].key.len); ! 2453: ! 2454: if (hh && hh->handler(r, &h[i], hh->offset) != NGX_OK) { ! 2455: return; ! 2456: } ! 2457: ! 2458: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 2459: "http header: \"%V: %V\"", &h[i].key, &h[i].value); ! 2460: } ! 2461: ! 2462: r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE; ! 2463: ! 2464: if (ngx_http_process_request_header(r) != NGX_OK) { ! 2465: return; ! 2466: } ! 2467: ! 2468: ngx_http_process_request(r); ! 2469: } ! 2470: ! 2471: ! 2472: static ngx_int_t ! 2473: ngx_http_spdy_init_request_body(ngx_http_request_t *r) ! 2474: { ! 2475: ngx_buf_t *buf; ! 2476: ngx_temp_file_t *tf; ! 2477: ngx_http_request_body_t *rb; ! 2478: ngx_http_core_loc_conf_t *clcf; ! 2479: ! 2480: rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); ! 2481: if (rb == NULL) { ! 2482: return NGX_ERROR; ! 2483: } ! 2484: ! 2485: r->request_body = rb; ! 2486: ! 2487: if (r->spdy_stream->in_closed) { ! 2488: return NGX_OK; ! 2489: } ! 2490: ! 2491: rb->rest = r->headers_in.content_length_n; ! 2492: ! 2493: clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ! 2494: ! 2495: if (r->request_body_in_file_only ! 2496: || rb->rest > (off_t) clcf->client_body_buffer_size ! 2497: || rb->rest < 0) ! 2498: { ! 2499: tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); ! 2500: if (tf == NULL) { ! 2501: return NGX_ERROR; ! 2502: } ! 2503: ! 2504: tf->file.fd = NGX_INVALID_FILE; ! 2505: tf->file.log = r->connection->log; ! 2506: tf->path = clcf->client_body_temp_path; ! 2507: tf->pool = r->pool; ! 2508: tf->warn = "a client request body is buffered to a temporary file"; ! 2509: tf->log_level = r->request_body_file_log_level; ! 2510: tf->persistent = r->request_body_in_persistent_file; ! 2511: tf->clean = r->request_body_in_clean_file; ! 2512: ! 2513: if (r->request_body_file_group_access) { ! 2514: tf->access = 0660; ! 2515: } ! 2516: ! 2517: rb->temp_file = tf; ! 2518: ! 2519: if (r->spdy_stream->in_closed ! 2520: && ngx_create_temp_file(&tf->file, tf->path, tf->pool, ! 2521: tf->persistent, tf->clean, tf->access) ! 2522: != NGX_OK) ! 2523: { ! 2524: return NGX_ERROR; ! 2525: } ! 2526: ! 2527: buf = ngx_calloc_buf(r->pool); ! 2528: if (buf == NULL) { ! 2529: return NGX_ERROR; ! 2530: } ! 2531: ! 2532: } else { ! 2533: ! 2534: if (rb->rest == 0) { ! 2535: return NGX_OK; ! 2536: } ! 2537: ! 2538: buf = ngx_create_temp_buf(r->pool, (size_t) rb->rest); ! 2539: if (buf == NULL) { ! 2540: return NGX_ERROR; ! 2541: } ! 2542: } ! 2543: ! 2544: rb->buf = buf; ! 2545: ! 2546: rb->bufs = ngx_alloc_chain_link(r->pool); ! 2547: if (rb->bufs == NULL) { ! 2548: return NGX_ERROR; ! 2549: } ! 2550: ! 2551: rb->bufs->buf = buf; ! 2552: rb->bufs->next = NULL; ! 2553: ! 2554: rb->rest = 0; ! 2555: ! 2556: return NGX_OK; ! 2557: } ! 2558: ! 2559: ! 2560: ngx_int_t ! 2561: ngx_http_spdy_read_request_body(ngx_http_request_t *r, ! 2562: ngx_http_client_body_handler_pt post_handler) ! 2563: { ! 2564: ngx_http_spdy_stream_t *stream; ! 2565: ! 2566: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 2567: "spdy read request body"); ! 2568: ! 2569: stream = r->spdy_stream; ! 2570: ! 2571: switch (stream->skip_data) { ! 2572: ! 2573: case NGX_SPDY_DATA_DISCARD: ! 2574: post_handler(r); ! 2575: return NGX_OK; ! 2576: ! 2577: case NGX_SPDY_DATA_ERROR: ! 2578: if (r->headers_in.content_length_n == -1) { ! 2579: return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; ! 2580: } else { ! 2581: return NGX_HTTP_BAD_REQUEST; ! 2582: } ! 2583: ! 2584: case NGX_SPDY_DATA_INTERNAL_ERROR: ! 2585: return NGX_HTTP_INTERNAL_SERVER_ERROR; ! 2586: } ! 2587: ! 2588: if (!r->request_body && ngx_http_spdy_init_request_body(r) != NGX_OK) { ! 2589: stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR; ! 2590: return NGX_HTTP_INTERNAL_SERVER_ERROR; ! 2591: } ! 2592: ! 2593: if (stream->in_closed) { ! 2594: post_handler(r); ! 2595: return NGX_OK; ! 2596: } ! 2597: ! 2598: r->request_body->post_handler = post_handler; ! 2599: ! 2600: return NGX_AGAIN; ! 2601: } ! 2602: ! 2603: ! 2604: void ! 2605: ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc) ! 2606: { ! 2607: ngx_event_t *ev; ! 2608: ngx_connection_t *fc; ! 2609: ngx_http_spdy_stream_t **index, *s; ! 2610: ngx_http_spdy_srv_conf_t *sscf; ! 2611: ngx_http_spdy_connection_t *sc; ! 2612: ! 2613: sc = stream->connection; ! 2614: ! 2615: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ! 2616: "spdy close stream %ui, processing %ui", ! 2617: stream->id, sc->processing); ! 2618: ! 2619: if (!stream->out_closed) { ! 2620: if (ngx_http_spdy_send_rst_stream(sc, stream->id, ! 2621: NGX_SPDY_INTERNAL_ERROR, ! 2622: stream->priority) ! 2623: != NGX_OK) ! 2624: { ! 2625: sc->connection->error = 1; ! 2626: } ! 2627: } ! 2628: ! 2629: sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, ! 2630: ngx_http_spdy_module); ! 2631: ! 2632: index = sc->streams_index + ngx_http_spdy_stream_index(sscf, stream->id); ! 2633: ! 2634: for ( ;; ) { ! 2635: s = *index; ! 2636: ! 2637: if (s == NULL) { ! 2638: break; ! 2639: } ! 2640: ! 2641: if (s == stream) { ! 2642: *index = s->index; ! 2643: break; ! 2644: } ! 2645: ! 2646: index = &s->index; ! 2647: } ! 2648: ! 2649: fc = stream->request->connection; ! 2650: ! 2651: ngx_http_free_request(stream->request, rc); ! 2652: ! 2653: ev = fc->read; ! 2654: ! 2655: if (ev->active || ev->disabled) { ! 2656: ngx_del_event(ev, NGX_READ_EVENT, 0); ! 2657: } ! 2658: ! 2659: if (ev->timer_set) { ! 2660: ngx_del_timer(ev); ! 2661: } ! 2662: ! 2663: if (ev->prev) { ! 2664: ngx_delete_posted_event(ev); ! 2665: } ! 2666: ! 2667: ev = fc->write; ! 2668: ! 2669: if (ev->active || ev->disabled) { ! 2670: ngx_del_event(ev, NGX_WRITE_EVENT, 0); ! 2671: } ! 2672: ! 2673: if (ev->timer_set) { ! 2674: ngx_del_timer(ev); ! 2675: } ! 2676: ! 2677: if (ev->prev) { ! 2678: ngx_delete_posted_event(ev); ! 2679: } ! 2680: ! 2681: fc->data = sc->free_fake_connections; ! 2682: sc->free_fake_connections = fc; ! 2683: ! 2684: sc->processing--; ! 2685: ! 2686: if (sc->processing || sc->blocked) { ! 2687: return; ! 2688: } ! 2689: ! 2690: ev = sc->connection->read; ! 2691: ! 2692: ev->handler = ngx_http_spdy_handle_connection_handler; ! 2693: ngx_post_event(ev, &ngx_posted_events); ! 2694: } ! 2695: ! 2696: ! 2697: static void ! 2698: ngx_http_spdy_handle_connection_handler(ngx_event_t *rev) ! 2699: { ! 2700: ngx_connection_t *c; ! 2701: ! 2702: rev->handler = ngx_http_spdy_read_handler; ! 2703: ! 2704: if (rev->ready) { ! 2705: ngx_http_spdy_read_handler(rev); ! 2706: return; ! 2707: } ! 2708: ! 2709: c = rev->data; ! 2710: ! 2711: ngx_http_spdy_handle_connection(c->data); ! 2712: } ! 2713: ! 2714: ! 2715: static void ! 2716: ngx_http_spdy_keepalive_handler(ngx_event_t *rev) ! 2717: { ! 2718: ngx_connection_t *c; ! 2719: ngx_http_spdy_srv_conf_t *sscf; ! 2720: ngx_http_spdy_connection_t *sc; ! 2721: ! 2722: c = rev->data; ! 2723: ! 2724: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy keepalive handler"); ! 2725: ! 2726: if (rev->timedout || c->close) { ! 2727: ngx_http_close_connection(c); ! 2728: return; ! 2729: } ! 2730: ! 2731: #if (NGX_HAVE_KQUEUE) ! 2732: ! 2733: if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { ! 2734: if (rev->pending_eof) { ! 2735: c->log->handler = NULL; ! 2736: ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno, ! 2737: "kevent() reported that client %V closed " ! 2738: "keepalive connection", &c->addr_text); ! 2739: #if (NGX_HTTP_SSL) ! 2740: if (c->ssl) { ! 2741: c->ssl->no_send_shutdown = 1; ! 2742: } ! 2743: #endif ! 2744: ngx_http_close_connection(c); ! 2745: return; ! 2746: } ! 2747: } ! 2748: ! 2749: #endif ! 2750: ! 2751: c->destroyed = 0; ! 2752: c->idle = 0; ! 2753: ngx_reusable_connection(c, 0); ! 2754: ! 2755: sc = c->data; ! 2756: ! 2757: sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, ! 2758: ngx_http_spdy_module); ! 2759: ! 2760: sc->pool = ngx_create_pool(sscf->pool_size, sc->connection->log); ! 2761: if (sc->pool == NULL) { ! 2762: ngx_http_close_connection(c); ! 2763: return; ! 2764: } ! 2765: ! 2766: sc->streams_index = ngx_pcalloc(sc->pool, ! 2767: ngx_http_spdy_streams_index_size(sscf) ! 2768: * sizeof(ngx_http_spdy_stream_t *)); ! 2769: if (sc->streams_index == NULL) { ! 2770: ngx_http_close_connection(c); ! 2771: return; ! 2772: } ! 2773: ! 2774: c->write->handler = ngx_http_spdy_write_handler; ! 2775: ! 2776: rev->handler = ngx_http_spdy_read_handler; ! 2777: ngx_http_spdy_read_handler(rev); ! 2778: } ! 2779: ! 2780: ! 2781: static void ! 2782: ngx_http_spdy_finalize_connection(ngx_http_spdy_connection_t *sc, ! 2783: ngx_int_t rc) ! 2784: { ! 2785: ngx_uint_t i, size; ! 2786: ngx_event_t *ev; ! 2787: ngx_connection_t *c, *fc; ! 2788: ngx_http_request_t *r; ! 2789: ngx_http_spdy_stream_t *stream; ! 2790: ngx_http_spdy_srv_conf_t *sscf; ! 2791: ! 2792: c = sc->connection; ! 2793: ! 2794: if (!sc->processing) { ! 2795: ngx_http_close_connection(c); ! 2796: return; ! 2797: } ! 2798: ! 2799: c->error = 1; ! 2800: c->read->handler = ngx_http_empty_handler; ! 2801: ! 2802: sc->last_out = NULL; ! 2803: ! 2804: sc->blocked = 1; ! 2805: ! 2806: sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, ! 2807: ngx_http_spdy_module); ! 2808: ! 2809: size = ngx_http_spdy_streams_index_size(sscf); ! 2810: ! 2811: for (i = 0; i < size; i++) { ! 2812: stream = sc->streams_index[i]; ! 2813: ! 2814: while (stream) { ! 2815: r = stream->request; ! 2816: ! 2817: fc = r->connection; ! 2818: fc->error = 1; ! 2819: ! 2820: if (stream->waiting) { ! 2821: r->blocked -= stream->waiting; ! 2822: stream->waiting = 0; ! 2823: ev = fc->write; ! 2824: ! 2825: } else { ! 2826: ev = fc->read; ! 2827: } ! 2828: ! 2829: stream = stream->index; ! 2830: ! 2831: ev->eof = 1; ! 2832: ev->handler(ev); ! 2833: } ! 2834: } ! 2835: ! 2836: sc->blocked = 0; ! 2837: ! 2838: if (sc->processing) { ! 2839: return; ! 2840: } ! 2841: ! 2842: ngx_http_close_connection(c); ! 2843: } ! 2844: ! 2845: ! 2846: static void ! 2847: ngx_http_spdy_pool_cleanup(void *data) ! 2848: { ! 2849: ngx_http_spdy_connection_t *sc = data; ! 2850: ! 2851: if (sc->pool) { ! 2852: ngx_destroy_pool(sc->pool); ! 2853: } ! 2854: } ! 2855: ! 2856: ! 2857: static void * ! 2858: ngx_http_spdy_zalloc(void *opaque, u_int items, u_int size) ! 2859: { ! 2860: ngx_http_spdy_connection_t *sc = opaque; ! 2861: ! 2862: return ngx_palloc(sc->connection->pool, items * size); ! 2863: } ! 2864: ! 2865: ! 2866: static void ! 2867: ngx_http_spdy_zfree(void *opaque, void *address) ! 2868: { ! 2869: #if 0 ! 2870: ngx_http_spdy_connection_t *sc = opaque; ! 2871: ! 2872: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, ! 2873: "spdy zfree: %p", address); ! 2874: #endif ! 2875: }