diff --git a/ChangeLog b/ChangeLog index 127195b6..b9a9e2cd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -112,6 +112,7 @@ Changes in current version: o Do not use SO_REUSEADDR when connecting o Support 64-bit integers in RPC structs o Correct handling of trailing headers in chunked replies; from Scott Lamb. + o Support multi-line HTTP headers; based on a patch from Moshe Litvin Changes in 1.4.0: o allow \r or \n individually to separate HTTP headers instead of the standard "\r\n"; from Charles Kerr. diff --git a/http.c b/http.c index b6c697da..cb61608b 100644 --- a/http.c +++ b/http.c @@ -1412,6 +1412,29 @@ evhttp_parse_firstline(struct evhttp_request *req, struct evbuffer *buffer) return (status); } +static int +evhttp_append_to_last_header(struct evkeyvalq *headers, const char *line) +{ + struct evkeyval *header = TAILQ_LAST(headers, evkeyvalq); + char *newval; + size_t old_len, line_len; + + if (header == NULL) + return (-1); + + old_len = strlen(header->value); + line_len = strlen(line); + + newval = mm_realloc(header->value, old_len + line_len + 1); + if (newval == NULL) + return (-1); + + memcpy(newval + old_len, line, line_len + 1); + header->value = newval; + + return (0); +} + enum message_read_status evhttp_parse_headers(struct evhttp_request *req, struct evbuffer* buffer) { @@ -1429,6 +1452,13 @@ evhttp_parse_headers(struct evhttp_request *req, struct evbuffer* buffer) break; } + /* Check if this is a continuation line */ + if (*line == ' ' || *line == '\t') { + if (evhttp_append_to_last_header(headers, line) == -1) + goto error; + continue; + } + /* Processing of header lines */ svalue = line; skey = strsep(&svalue, ":"); diff --git a/test/regress_http.c b/test/regress_http.c index f45e070a..51c17ca8 100644 --- a/test/regress_http.c +++ b/test/regress_http.c @@ -231,7 +231,18 @@ http_basic_cb(struct evhttp_request *req, void *arg) int empty = evhttp_find_header(req->input_headers, "Empty") != NULL; event_debug(("%s: called\n", __func__)); evbuffer_add_printf(evb, "This is funny"); - + + /* For multi-line headers test */ + { + const char *multi = + evhttp_find_header(req->input_headers,"X-multi"); + if (multi) { + if (strcmp("END", multi + strlen(multi) - 3) == 0) + test_ok++; + if (evhttp_find_header(req->input_headers, "X-Last")) + test_ok++; + } + } /* allow sending of an empty reply */ evhttp_send_reply(req, HTTP_OK, "Everything is fine", !empty ? evb : NULL); @@ -2126,6 +2137,52 @@ failed: exit(1); } +static void +http_multi_line_header_test(void) +{ + struct bufferevent *bev; + int fd; + const char *http_start_request; + short port = -1; + + test_ok = 0; + fprintf(stdout, "Testing HTTP Server with multi line: "); + + http = http_setup(&port, NULL); + + fd = http_connect("127.0.0.1", port); + + /* Stupid thing to send a request */ + bev = bufferevent_new(fd, http_readcb, http_writecb, + http_errorcb, NULL); + + http_start_request = + "GET /test HTTP/1.1\r\n" + "Host: somehost\r\n" + "Connection: close\r\n" + "X-Multi: aaaaaaaa\r\n" + " a\r\n" + "\tEND\r\n" + "X-Last: last\r\n" + "\r\n"; + + bufferevent_write(bev, http_start_request, strlen(http_start_request)); + + event_dispatch(); + + bufferevent_free(bev); + close(fd); + + evhttp_free(http); + + if (test_ok != 4) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + fprintf(stdout, "OK\n"); +} + void http_suite(void) { @@ -2146,6 +2203,8 @@ http_suite(void) http_highport_test(); http_dispatcher_test(); + http_multi_line_header_test(); + http_incomplete_test(0 /* use_timeout */); http_incomplete_test(1 /* use_timeout */);