Mercurial > hg > nginx
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 |