comparison src/http/ngx_http_spdy.c @ 5691:31349361558e

SPDY: ngx_http_spdy_state_headers() error handling cleanup. - Specification-friendly handling of invalid header block or special headers. Such errors are not fatal for session and shouldn't lead to connection close; - Avoid mix of NGX_HTTP_PARSE_INVALID_REQUEST/NGX_HTTP_PARSE_INVALID_HEADER.
author Valentin Bartenev <vbart@nginx.com>
date Wed, 30 Apr 2014 20:34:20 +0400
parents fb56f5d612a0
children ed5fb7d22d46
comparison
equal deleted inserted replaced
5690:fb56f5d612a0 5691:31349361558e
102 static u_char *ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc, 102 static u_char *ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc,
103 u_char *pos, u_char *end); 103 u_char *pos, u_char *end);
104 static u_char *ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc, 104 static u_char *ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc,
105 u_char *pos, u_char *end); 105 u_char *pos, u_char *end);
106 static u_char *ngx_http_spdy_state_headers_skip(ngx_http_spdy_connection_t *sc, 106 static u_char *ngx_http_spdy_state_headers_skip(ngx_http_spdy_connection_t *sc,
107 u_char *pos, u_char *end);
108 static u_char *ngx_http_spdy_state_headers_error(ngx_http_spdy_connection_t *sc,
107 u_char *pos, u_char *end); 109 u_char *pos, u_char *end);
108 static u_char *ngx_http_spdy_state_window_update(ngx_http_spdy_connection_t *sc, 110 static u_char *ngx_http_spdy_state_window_update(ngx_http_spdy_connection_t *sc,
109 u_char *pos, u_char *end); 111 u_char *pos, u_char *end);
110 static u_char *ngx_http_spdy_state_data(ngx_http_spdy_connection_t *sc, 112 static u_char *ngx_http_spdy_state_data(ngx_http_spdy_connection_t *sc,
111 u_char *pos, u_char *end); 113 u_char *pos, u_char *end);
1081 if (r->headers_in.headers.part.elts == NULL) { 1083 if (r->headers_in.headers.part.elts == NULL) {
1082 1084
1083 if (buf->last - buf->pos < NGX_SPDY_NV_NUM_SIZE) { 1085 if (buf->last - buf->pos < NGX_SPDY_NV_NUM_SIZE) {
1084 1086
1085 if (complete) { 1087 if (complete) {
1086 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, 1088 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1087 "client sent SYN_STREAM frame " 1089 "premature end of spdy header block");
1088 "with invalid HEADERS block"); 1090
1089 1091 return ngx_http_spdy_state_headers_error(sc, pos, end);
1090 return ngx_http_spdy_state_protocol_error(sc);
1091 } 1092 }
1092 1093
1093 return ngx_http_spdy_state_save(sc, pos, end, 1094 return ngx_http_spdy_state_save(sc, pos, end,
1094 ngx_http_spdy_state_headers); 1095 ngx_http_spdy_state_headers);
1095 } 1096 }
1179 if (complete) { 1180 if (complete) {
1180 /* TODO: improve error message */ 1181 /* TODO: improve error message */
1181 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1182 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1182 "spdy again while last chunk"); 1183 "spdy again while last chunk");
1183 1184
1184 return ngx_http_spdy_state_protocol_error(sc); 1185 return ngx_http_spdy_state_headers_error(sc, pos, end);
1185 } 1186 }
1186 1187
1187 return ngx_http_spdy_state_save(sc, pos, end, 1188 return ngx_http_spdy_state_save(sc, pos, end,
1188 ngx_http_spdy_state_headers); 1189 ngx_http_spdy_state_headers);
1189 1190
1190 case NGX_HTTP_PARSE_INVALID_REQUEST: 1191 case NGX_HTTP_PARSE_INVALID_HEADER:
1191 1192
1192 /* TODO: improve error message */ 1193 /* TODO: improve error message */
1193 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, 1194 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
1194 "client sent invalid header line"); 1195 "client sent invalid header line");
1195 1196
1196 ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); 1197 ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
1197 1198
1198 return ngx_http_spdy_state_headers_skip(sc, pos, end); 1199 return ngx_http_spdy_state_headers_skip(sc, pos, end);
1199 1200
1200 default: /* NGX_HTTP_PARSE_INVALID_HEADER */ 1201 default: /* NGX_ERROR */
1201 1202 return ngx_http_spdy_state_headers_error(sc, pos, end);
1202 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
1203 "client sent invalid HEADERS spdy frame");
1204
1205 return ngx_http_spdy_state_protocol_error(sc);
1206 } 1203 }
1207 1204
1208 /* a header line has been parsed successfully */ 1205 /* a header line has been parsed successfully */
1209 1206
1210 rc = ngx_http_spdy_handle_request_header(r); 1207 rc = ngx_http_spdy_handle_request_header(r);
1211 1208
1212 if (rc != NGX_OK) { 1209 if (rc != NGX_OK) {
1213 if (rc == NGX_HTTP_PARSE_INVALID_HEADER) { 1210 if (rc == NGX_HTTP_PARSE_INVALID_HEADER) {
1214 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, 1211 ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
1215 "client sent invalid HEADERS spdy frame"); 1212 return ngx_http_spdy_state_headers_skip(sc, pos, end);
1216
1217 return ngx_http_spdy_state_protocol_error(sc);
1218 } 1213 }
1219 1214
1220 if (rc == NGX_HTTP_PARSE_INVALID_REQUEST) { 1215 if (rc != NGX_ABORT) {
1221 ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); 1216 ngx_http_spdy_close_stream(sc->stream,
1217 NGX_HTTP_INTERNAL_SERVER_ERROR);
1222 } 1218 }
1223 1219
1224 return ngx_http_spdy_state_headers_skip(sc, pos, end); 1220 return ngx_http_spdy_state_headers_skip(sc, pos, end);
1225 } 1221 }
1226 } 1222 }
1227 1223
1228 if (buf->pos != buf->last || sc->zstream_in.avail_in) { 1224 if (buf->pos != buf->last || sc->zstream_in.avail_in) {
1229 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, 1225 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1230 "client sent SYN_STREAM frame " 1226 "incorrect number of spdy header block entries");
1231 "with invalid HEADERS block"); 1227
1232 1228 return ngx_http_spdy_state_headers_error(sc, pos, end);
1233 return ngx_http_spdy_state_protocol_error(sc);
1234 } 1229 }
1235 1230
1236 if (!complete) { 1231 if (!complete) {
1237 return ngx_http_spdy_state_save(sc, pos, end, 1232 return ngx_http_spdy_state_save(sc, pos, end,
1238 ngx_http_spdy_state_headers); 1233 ngx_http_spdy_state_headers);
1287 return ngx_http_spdy_state_save(sc, pos, end, 1282 return ngx_http_spdy_state_save(sc, pos, end,
1288 ngx_http_spdy_state_headers_skip); 1283 ngx_http_spdy_state_headers_skip);
1289 } 1284 }
1290 1285
1291 return ngx_http_spdy_state_complete(sc, pos, end); 1286 return ngx_http_spdy_state_complete(sc, pos, end);
1287 }
1288
1289
1290 static u_char *
1291 ngx_http_spdy_state_headers_error(ngx_http_spdy_connection_t *sc, u_char *pos,
1292 u_char *end)
1293 {
1294 ngx_http_spdy_stream_t *stream;
1295
1296 stream = sc->stream;
1297
1298 ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
1299 "client sent SYN_STREAM frame for stream %ui "
1300 "with invalid header block", stream->id);
1301
1302 if (ngx_http_spdy_send_rst_stream(sc, stream->id, NGX_SPDY_PROTOCOL_ERROR,
1303 stream->priority)
1304 != NGX_OK)
1305 {
1306 return ngx_http_spdy_state_internal_error(sc);
1307 }
1308
1309 stream->out_closed = 1;
1310
1311 ngx_http_spdy_close_stream(stream, NGX_HTTP_BAD_REQUEST);
1312
1313 return ngx_http_spdy_state_headers_skip(sc, pos, end);
1292 } 1314 }
1293 1315
1294 1316
1295 static u_char * 1317 static u_char *
1296 ngx_http_spdy_state_window_update(ngx_http_spdy_connection_t *sc, u_char *pos, 1318 ngx_http_spdy_state_window_update(ngx_http_spdy_connection_t *sc, u_char *pos,
2423 } 2445 }
2424 2446
2425 r->lowcase_index = ngx_spdy_frame_parse_uint32(p); 2447 r->lowcase_index = ngx_spdy_frame_parse_uint32(p);
2426 2448
2427 if (r->lowcase_index == 0) { 2449 if (r->lowcase_index == 0) {
2428 return NGX_HTTP_PARSE_INVALID_HEADER; 2450 return NGX_ERROR;
2429 } 2451 }
2430 2452
2431 /* null-terminate the previous header value */ 2453 /* null-terminate the previous header value */
2432 *p = '\0'; 2454 *p = '\0';
2433 2455
2473 switch (ch) { 2495 switch (ch) {
2474 case '\0': 2496 case '\0':
2475 case LF: 2497 case LF:
2476 case CR: 2498 case CR:
2477 case ':': 2499 case ':':
2478 return NGX_HTTP_PARSE_INVALID_REQUEST; 2500 return NGX_HTTP_PARSE_INVALID_HEADER;
2479 } 2501 }
2480 2502
2481 if (ch >= 'A' && ch <= 'Z') { 2503 if (ch >= 'A' && ch <= 'Z') {
2482 return NGX_HTTP_PARSE_INVALID_HEADER; 2504 return NGX_ERROR;
2483 } 2505 }
2484 2506
2485 r->invalid_header = 1; 2507 r->invalid_header = 1;
2486 } 2508 }
2487 2509
2640 } 2662 }
2641 2663
2642 return sh->handler(r); 2664 return sh->handler(r);
2643 } 2665 }
2644 2666
2645 return NGX_HTTP_PARSE_INVALID_REQUEST; 2667 return NGX_HTTP_PARSE_INVALID_HEADER;
2646 } 2668 }
2647 2669
2648 h = ngx_list_push(&r->headers_in.headers); 2670 h = ngx_list_push(&r->headers_in.headers);
2649 if (h == NULL) { 2671 if (h == NULL) {
2650 ngx_http_spdy_close_stream(r->spdy_stream,
2651 NGX_HTTP_INTERNAL_SERVER_ERROR);
2652 return NGX_ERROR; 2672 return NGX_ERROR;
2653 } 2673 }
2654 2674
2655 h->hash = r->header_hash; 2675 h->hash = r->header_hash;
2656 2676
2750 2770
2751 do { 2771 do {
2752 if ((*p < 'A' || *p > 'Z') && *p != '_') { 2772 if ((*p < 'A' || *p > 'Z') && *p != '_') {
2753 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, 2773 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
2754 "client sent invalid method"); 2774 "client sent invalid method");
2755 return NGX_HTTP_PARSE_INVALID_REQUEST; 2775 return NGX_HTTP_PARSE_INVALID_HEADER;
2756 } 2776 }
2757 2777
2758 p++; 2778 p++;
2759 2779
2760 } while (--len); 2780 } while (--len);
2786 return NGX_HTTP_PARSE_INVALID_HEADER; 2806 return NGX_HTTP_PARSE_INVALID_HEADER;
2787 } 2807 }
2788 2808
2789 h = ngx_list_push(&r->headers_in.headers); 2809 h = ngx_list_push(&r->headers_in.headers);
2790 if (h == NULL) { 2810 if (h == NULL) {
2791 ngx_http_spdy_close_stream(r->spdy_stream,
2792 NGX_HTTP_INTERNAL_SERVER_ERROR);
2793 return NGX_ERROR; 2811 return NGX_ERROR;
2794 } 2812 }
2795 2813
2796 r->headers_in.host = h; 2814 r->headers_in.host = h;
2797 2815
2818 2836
2819 r->uri_start = r->header_start; 2837 r->uri_start = r->header_start;
2820 r->uri_end = r->header_end; 2838 r->uri_end = r->header_end;
2821 2839
2822 if (ngx_http_parse_uri(r) != NGX_OK) { 2840 if (ngx_http_parse_uri(r) != NGX_OK) {
2823 return NGX_HTTP_PARSE_INVALID_REQUEST; 2841 return NGX_HTTP_PARSE_INVALID_HEADER;
2824 } 2842 }
2825 2843
2826 if (ngx_http_process_request_uri(r) != NGX_OK) { 2844 if (ngx_http_process_request_uri(r) != NGX_OK) {
2827 return NGX_ERROR; 2845 /*
2846 * request has been finalized already
2847 * in ngx_http_process_request_uri()
2848 */
2849 return NGX_ABORT;
2828 } 2850 }
2829 2851
2830 return NGX_OK; 2852 return NGX_OK;
2831 } 2853 }
2832 2854
2841 } 2863 }
2842 2864
2843 p = r->header_start; 2865 p = r->header_start;
2844 2866
2845 if (r->header_end - p < 8 || !(ngx_str5cmp(p, 'H', 'T', 'T', 'P', '/'))) { 2867 if (r->header_end - p < 8 || !(ngx_str5cmp(p, 'H', 'T', 'T', 'P', '/'))) {
2846 return NGX_HTTP_PARSE_INVALID_REQUEST; 2868 return NGX_HTTP_PARSE_INVALID_HEADER;
2847 } 2869 }
2848 2870
2849 ch = *(p + 5); 2871 ch = *(p + 5);
2850 2872
2851 if (ch < '1' || ch > '9') { 2873 if (ch < '1' || ch > '9') {
2852 return NGX_HTTP_PARSE_INVALID_REQUEST; 2874 return NGX_HTTP_PARSE_INVALID_HEADER;
2853 } 2875 }
2854 2876
2855 r->http_major = ch - '0'; 2877 r->http_major = ch - '0';
2856 2878
2857 for (p += 6; p != r->header_end - 2; p++) { 2879 for (p += 6; p != r->header_end - 2; p++) {
2861 if (ch == '.') { 2883 if (ch == '.') {
2862 break; 2884 break;
2863 } 2885 }
2864 2886
2865 if (ch < '0' || ch > '9') { 2887 if (ch < '0' || ch > '9') {
2866 return NGX_HTTP_PARSE_INVALID_REQUEST; 2888 return NGX_HTTP_PARSE_INVALID_HEADER;
2867 } 2889 }
2868 2890
2869 r->http_major = r->http_major * 10 + ch - '0'; 2891 r->http_major = r->http_major * 10 + ch - '0';
2870 } 2892 }
2871 2893
2872 if (*p != '.') { 2894 if (*p != '.') {
2873 return NGX_HTTP_PARSE_INVALID_REQUEST; 2895 return NGX_HTTP_PARSE_INVALID_HEADER;
2874 } 2896 }
2875 2897
2876 ch = *(p + 1); 2898 ch = *(p + 1);
2877 2899
2878 if (ch < '0' || ch > '9') { 2900 if (ch < '0' || ch > '9') {
2879 return NGX_HTTP_PARSE_INVALID_REQUEST; 2901 return NGX_HTTP_PARSE_INVALID_HEADER;
2880 } 2902 }
2881 2903
2882 r->http_minor = ch - '0'; 2904 r->http_minor = ch - '0';
2883 2905
2884 for (p += 2; p != r->header_end; p++) { 2906 for (p += 2; p != r->header_end; p++) {
2885 2907
2886 ch = *p; 2908 ch = *p;
2887 2909
2888 if (ch < '0' || ch > '9') { 2910 if (ch < '0' || ch > '9') {
2889 return NGX_HTTP_PARSE_INVALID_REQUEST; 2911 return NGX_HTTP_PARSE_INVALID_HEADER;
2890 } 2912 }
2891 2913
2892 r->http_minor = r->http_minor * 10 + ch - '0'; 2914 r->http_minor = r->http_minor * 10 + ch - '0';
2893 } 2915 }
2894 2916