Mercurial > dovecot > original-hg > dovecot-2.2
changeset 16749:28e5d58e49dc
lib-http: Adjusted message and request parsers to return an error code.
author | Stephan Bosch <stephan@rename-it.nl> |
---|---|
date | Sun, 15 Sep 2013 03:50:08 +0300 |
parents | eeaa68773f73 |
children | a335db9dca6a |
files | src/lib-http/http-message-parser.c src/lib-http/http-message-parser.h src/lib-http/http-request-parser.c src/lib-http/http-request-parser.h src/lib-http/http-response-parser.c src/lib-http/test-http-server.c |
diffstat | 6 files changed, 277 insertions(+), 157 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib-http/http-message-parser.c Sun Sep 15 03:47:54 2013 +0300 +++ b/src/lib-http/http-message-parser.c Sun Sep 15 03:50:08 2013 +0300 @@ -62,35 +62,45 @@ const unsigned char *p = parser->cur; const size_t size = parser->end - parser->cur; + parser->error_code = HTTP_MESSAGE_PARSE_ERROR_NONE; + parser->error = NULL; + /* HTTP-version = HTTP-name "/" DIGIT "." DIGIT HTTP-name = %x48.54.54.50 ; "HTTP", case-sensitive */ if (size < 8) return 0; if (memcmp(p, "HTTP/", 5) != 0 || - !i_isdigit(p[5]) || p[6] != '.' || !i_isdigit(p[7])) + !i_isdigit(p[5]) || p[6] != '.' || !i_isdigit(p[7])) { + parser->error = "Bad HTTP version"; + parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE; return -1; + } parser->msg.version_major = p[5] - '0'; parser->msg.version_minor = p[7] - '0'; parser->cur += 8; return 1; } -int http_message_parse_finish_payload(struct http_message_parser *parser, - const char **error_r) +int http_message_parse_finish_payload(struct http_message_parser *parser) { const unsigned char *data; size_t size; int ret; + parser->error_code = HTTP_MESSAGE_PARSE_ERROR_NONE; + parser->error = NULL; + if (parser->payload == NULL) return 1; while ((ret = i_stream_read_data(parser->payload, &data, &size, 0)) > 0) i_stream_skip(parser->payload, size); if (ret == 0 || parser->payload->stream_errno != 0) { - if (ret < 0) - *error_r = "Stream error while skipping payload"; + if (ret < 0) { + parser->error = "Stream error while skipping payload"; + parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_STREAM; + } return ret; } i_stream_unref(&parser->payload); @@ -99,8 +109,7 @@ static int http_message_parse_header(struct http_message_parser *parser, - const char *name, const unsigned char *data, size_t size, - const char **error_r) + const char *name, const unsigned char *data, size_t size) { const struct http_header_field *hdr; struct http_parser hparser; @@ -141,7 +150,8 @@ } if (hparser.cur < hparser.end || num_tokens == 0) { - *error_r = "Invalid Connection header"; + parser->error = "Invalid Connection header"; + parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE; return -1; } @@ -150,12 +160,14 @@ /* Content-Length: */ if (strcasecmp(name, "Content-Length") == 0) { if (parser->msg.have_content_length) { - *error_r = "Duplicate Content-Length header"; + parser->error = "Duplicate Content-Length header"; + parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE; return -1; } /* Content-Length = 1*DIGIT */ if (str_to_uoff(hdr->value, &parser->msg.content_length) < 0) { - *error_r = "Invalid Content-Length header"; + parser->error= "Invalid Content-Length header"; + parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE; return -1; } parser->msg.have_content_length = TRUE; @@ -165,7 +177,8 @@ case 'D': case 'd': if (strcasecmp(name, "Date") == 0) { if (parser->msg.date != (time_t)-1) { - *error_r = "Duplicate Date header"; + parser->error = "Duplicate Date header"; + parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE; return -1; } @@ -269,7 +282,8 @@ if (hparser.cur < hparser.end || array_count(&parser->msg.transfer_encoding) == 0) { - *error_r = "Invalid Transfer-Encoding header"; + parser->error = "Invalid Transfer-Encoding header"; + parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE; return -1; } return 0; @@ -281,14 +295,16 @@ return 0; } -int http_message_parse_headers(struct http_message_parser *parser, - const char **error_r) +int http_message_parse_headers(struct http_message_parser *parser) { const unsigned char *field_data; const char *field_name, *error; size_t field_size; int ret; + parser->error_code = HTTP_MESSAGE_PARSE_ERROR_NONE; + parser->error = NULL; + /* *( header-field CRLF ) CRLF */ while ((ret=http_header_parse_next_field(parser->header_parser, &field_name, &field_data, &field_size, &error)) > 0) { @@ -298,23 +314,28 @@ } if (http_message_parse_header(parser, - field_name, field_data, field_size, error_r) < 0) + field_name, field_data, field_size) < 0) return -1; } if (ret < 0) { - *error_r = t_strdup_printf( - "Failed to parse response header: %s", error); + if (parser->input->eof || parser->input->stream_errno != 0) { + parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_STREAM; + parser->error = "Broken stream"; + } else { + parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE; + parser->error = t_strdup_printf("Failed to parse header: %s", error); + } + } return ret; } - -int http_message_parse_body(struct http_message_parser *parser, bool request, - const char **error_r) +int http_message_parse_body(struct http_message_parser *parser, bool request) { - *error_r = NULL; - + parser->error_code = HTTP_MESSAGE_PARSE_ERROR_NONE; + parser->error = NULL; + if (array_is_created(&parser->msg.transfer_encoding)) { const struct http_transfer_coding *coding; @@ -324,20 +345,27 @@ if (strcasecmp(coding->name, "chunked") == 0) { chunked_last = TRUE; - if (*error_r == NULL && array_is_created(&coding->parameters) && - array_count(&coding->parameters) > 0) { + if ((parser->error_code == HTTP_MESSAGE_PARSE_ERROR_NONE) + && array_is_created(&coding->parameters) + && array_count(&coding->parameters) > 0) { const struct http_transfer_param *param = array_idx(&coding->parameters, 0); - - *error_r = t_strdup_printf("Unexpected parameter `%s' specified" + + parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BAD_MESSAGE; + parser->error = t_strdup_printf( + "Unexpected parameter `%s' specified" "for the `%s' transfer coding", param->attribute, coding->name); + /* recoverable */ } } else if (chunked_last) { - *error_r = "Chunked Transfer-Encoding must be last"; + parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE; + parser->error = "Chunked Transfer-Encoding must be last"; return -1; - } else if (*error_r == NULL) { - *error_r = t_strdup_printf( + } else if (parser->error_code == HTTP_MESSAGE_PARSE_ERROR_NONE) { + parser->error_code = HTTP_MESSAGE_PARSE_ERROR_NOT_IMPLEMENTED; + parser->error = t_strdup_printf( "Unknown transfer coding `%s'", coding->name); + /* recoverable */ } } @@ -364,7 +392,8 @@ length cannot be determined reliably; the server MUST respond with the 400 (Bad Request) status code and then close the connection. */ - *error_r = "Final Transfer-Encoding in request is not `chunked'"; + parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE; + parser->error = "Final Transfer-Encoding in request is not chunked"; return -1; } @@ -401,5 +430,7 @@ parser->payload = i_stream_create_limit(parser->input, (size_t)-1); } + if (parser->error_code != HTTP_MESSAGE_PARSE_ERROR_NONE) + return -1; return 0; }
--- a/src/lib-http/http-message-parser.h Sun Sep 15 03:47:54 2013 +0300 +++ b/src/lib-http/http-message-parser.h Sun Sep 15 03:50:08 2013 +0300 @@ -6,6 +6,15 @@ #include "http-header.h" +enum http_message_parse_error { + HTTP_MESSAGE_PARSE_ERROR_NONE = 0, /* no error */ + HTTP_MESSAGE_PARSE_ERROR_BROKEN_STREAM, /* stream error */ + HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE, /* unrecoverable generic error */ + HTTP_MESSAGE_PARSE_ERROR_BAD_MESSAGE, /* recoverable generic error */ + HTTP_MESSAGE_PARSE_ERROR_NOT_IMPLEMENTED, /* used unimplemented feature + (recoverable) */ +}; + struct http_message { pool_t pool; @@ -30,6 +39,9 @@ const unsigned char *cur, *end; + const char *error; + enum http_message_parse_error error_code; + struct http_header_parser *header_parser; struct istream *payload; @@ -44,12 +56,9 @@ void http_message_parser_restart(struct http_message_parser *parser, pool_t pool); -int http_message_parse_finish_payload(struct http_message_parser *parser, - const char **error_r); +int http_message_parse_finish_payload(struct http_message_parser *parser); int http_message_parse_version(struct http_message_parser *parser); -int http_message_parse_headers(struct http_message_parser *parser, - const char **error_r); -int http_message_parse_body(struct http_message_parser *parser, bool request, - const char **error_r); +int http_message_parse_headers(struct http_message_parser *parser); +int http_message_parse_body(struct http_message_parser *parser, bool request); #endif
--- a/src/lib-http/http-request-parser.c Sun Sep 15 03:47:54 2013 +0300 +++ b/src/lib-http/http-request-parser.c Sun Sep 15 03:50:08 2013 +0300 @@ -24,6 +24,8 @@ struct http_message_parser parser; enum http_request_parser_state state; + enum http_request_parse_error error_code; + const char *request_method; const char *request_target; @@ -106,7 +108,7 @@ } static int http_request_parse(struct http_request_parser *parser, - pool_t pool, const char **error_r) + pool_t pool) { struct http_message_parser *_parser = &parser->parser; int ret; @@ -125,7 +127,8 @@ if (*_parser->cur == '\r' || *_parser->cur == '\n') { if (parser->skipping_line) { /* second extra CRLF; not allowed */ - *error_r = "Empty request line"; + parser->error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST; + _parser->error = "Empty request line"; return -1; } /* HTTP/1.0 client sent one extra CRLF after body. @@ -138,19 +141,17 @@ parser->skipping_line = FALSE; /* fall through */ case HTTP_REQUEST_PARSE_STATE_METHOD: - if ((ret=http_request_parse_method(parser)) <= 0) { - if (ret < 0) - *error_r = "Invalid HTTP method in request"; + if ((ret=http_request_parse_method(parser)) <= 0) return ret; - } parser->state = HTTP_REQUEST_PARSE_STATE_SP1; if (_parser->cur == _parser->end) return 0; /* fall through */ case HTTP_REQUEST_PARSE_STATE_SP1: if (*_parser->cur != ' ') { - *error_r = t_strdup_printf - ("Expected ' ' after request method, but found %s", + parser->error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST; + _parser->error = t_strdup_printf + ("Unexpected character %s in request method", _chr_sanitize(*_parser->cur)); return -1; } @@ -160,19 +161,17 @@ return 0; /* fall through */ case HTTP_REQUEST_PARSE_STATE_TARGET: - if ((ret=http_request_parse_target(parser)) <= 0) { - if (ret < 0) - *error_r = "Invalid HTTP target in request"; + if ((ret=http_request_parse_target(parser)) <= 0) return ret; - } parser->state = HTTP_REQUEST_PARSE_STATE_SP2; if (_parser->cur == _parser->end) return 0; /* fall through */ case HTTP_REQUEST_PARSE_STATE_SP2: if (*_parser->cur != ' ') { - *error_r = t_strdup_printf - ("Expected ' ' after request target, but found %s", + parser->error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST; + _parser->error = t_strdup_printf + ("Unexpected character %s in request target", _chr_sanitize(*_parser->cur)); return -1; } @@ -183,8 +182,10 @@ /* fall through */ case HTTP_REQUEST_PARSE_STATE_VERSION: if ((ret=http_message_parse_version(&parser->parser)) <= 0) { - if (ret < 0) - *error_r = "Invalid HTTP version in request"; + if (ret < 0) { + parser->error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST; + _parser->error = "Invalid HTTP version in request"; + } return ret; } parser->state = HTTP_REQUEST_PARSE_STATE_CR; @@ -200,8 +201,9 @@ /* fall through */ case HTTP_REQUEST_PARSE_STATE_LF: if (*_parser->cur != '\n') { - *error_r = t_strdup_printf - ("Expected line end after request, but found %s", + parser->error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST; + _parser->error = t_strdup_printf + ("Unexpected character %s at end of request line", _chr_sanitize(*_parser->cur)); return -1; } @@ -223,7 +225,7 @@ } static int http_request_parse_request_line(struct http_request_parser *parser, - pool_t pool, const char **error_r) + pool_t pool) { struct http_message_parser *_parser = &parser->parser; const unsigned char *begin; @@ -235,7 +237,7 @@ _parser->cur = begin; _parser->end = _parser->cur + size; - if ((ret = http_request_parse(parser, pool, error_r)) < 0) + if ((ret = http_request_parse(parser, pool)) < 0) return -1; i_stream_skip(_parser->input, _parser->cur - begin); @@ -244,29 +246,61 @@ old_bytes = i_stream_get_data_size(_parser->input); } - i_assert(ret != -2); + if (ret == -2) { + parser->error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST; + _parser->error = "HTTP request line is too long"; + return -1; + } if (ret < 0) { if (_parser->input->eof && - parser->state == HTTP_REQUEST_PARSE_STATE_INIT) + parser->state == HTTP_REQUEST_PARSE_STATE_INIT) return 0; - *error_r = "Stream error"; + parser->error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_STREAM; + _parser->error = "Broken stream"; return -1; } return 0; } +static inline enum http_request_parse_error +http_request_parser_message_error(struct http_request_parser *parser) +{ + switch (parser->parser.error_code) { + case HTTP_MESSAGE_PARSE_ERROR_BROKEN_STREAM: + return HTTP_REQUEST_PARSE_ERROR_BROKEN_STREAM; + case HTTP_MESSAGE_PARSE_ERROR_BAD_MESSAGE: + return HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST; + case HTTP_MESSAGE_PARSE_ERROR_NOT_IMPLEMENTED: + return HTTP_REQUEST_PARSE_ERROR_NOT_IMPLEMENTED; + case HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE: + return HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST; + default: + break; + } + i_unreached(); + return HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST; +} + int http_request_parse_next(struct http_request_parser *parser, pool_t pool, struct http_request *request, - const char **error_r) + enum http_request_parse_error *error_code_r, const char **error_r) { const struct http_header_field *hdr; const char *error; int ret; + *error_code_r = parser->error_code = HTTP_REQUEST_PARSE_ERROR_NONE; + *error_r = parser->parser.error = NULL; + /* make sure we finished streaming payload from previous request before we continue. */ - if ((ret = http_message_parse_finish_payload(&parser->parser, error_r)) <= 0) + if ((ret = http_message_parse_finish_payload(&parser->parser)) <= 0) { + if (ret < 0) { + *error_code_r = http_request_parser_message_error(parser); + *error_r = parser->parser.error; + } return ret; + } /* HTTP-message = start-line *( header-field CRLF ) @@ -274,13 +308,36 @@ [ message-body ] */ if (parser->state != HTTP_REQUEST_PARSE_STATE_HEADER) { - if ((ret = http_request_parse_request_line(parser, pool, error_r)) <= 0) + ret = http_request_parse_request_line(parser, pool); + + /* assign early for error reporting */ + request->method = parser->request_method; + request->target_raw = parser->request_target; + request->version_major = parser->parser.msg.version_major; + request->version_minor = parser->parser.msg.version_minor; + + if (ret <= 0) { + if (ret < 0) { + *error_code_r = parser->error_code; + *error_r = parser->parser.error; + } return ret; - } - if ((ret = http_message_parse_headers(&parser->parser, error_r)) <= 0) + } + } + + if ((ret = http_message_parse_headers(&parser->parser)) <= 0) { + if (ret < 0) { + *error_code_r = http_request_parser_message_error(parser); + *error_r = parser->parser.error; + } return ret; - if (http_message_parse_body(&parser->parser, TRUE, error_r) < 0) + } + + if (http_message_parse_body(&parser->parser, TRUE) < 0) { + *error_code_r = http_request_parser_message_error(parser); + *error_r = parser->parser.error; return -1; + } parser->state = HTTP_REQUEST_PARSE_STATE_INIT; /* https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-23 @@ -293,6 +350,7 @@ */ if ((ret=http_header_field_find_unique (parser->parser.msg.header, "Host", &hdr)) <= 0) { + *error_code_r = HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST; if (ret == 0) *error_r = "Missing Host header"; else @@ -304,6 +362,7 @@ if (http_url_request_target_parse(parser->request_target, hdr->value, parser->parser.msg.pool, &request->target, &error) < 0) { + *error_code_r = HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST; *error_r = t_strdup_printf("Bad request target `%s': %s", parser->request_target, error); return -1;
--- a/src/lib-http/http-request-parser.h Sun Sep 15 03:47:54 2013 +0300 +++ b/src/lib-http/http-request-parser.h Sun Sep 15 03:50:08 2013 +0300 @@ -2,6 +2,17 @@ #define HTTP_REQUEST_PARSER_H #include "http-request.h" + +enum http_request_parse_error { + HTTP_REQUEST_PARSE_ERROR_NONE = 0, /* no error */ + HTTP_REQUEST_PARSE_ERROR_BROKEN_STREAM, /* stream error */ + HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST, /* unrecoverable generic error */ + HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST, /* recoverable generic error */ + HTTP_REQUEST_PARSE_ERROR_NOT_IMPLEMENTED, /* used unimplemented feature + (recoverable) */ + HTTP_REQUEST_PARSE_ERROR_METHOD_TOO_LONG, /* method too long (fatal) */ + HTTP_REQUEST_PARSE_ERROR_TARGET_TOO_LONG /* target too long (fatal) */ +}; struct http_request_parser * http_request_parser_init(struct istream *input, @@ -10,6 +21,6 @@ int http_request_parse_next(struct http_request_parser *parser, pool_t pool, struct http_request *request, - const char **error_r); + enum http_request_parse_error *error_code_r, const char **error_r); #endif
--- a/src/lib-http/http-response-parser.c Sun Sep 15 03:47:54 2013 +0300 +++ b/src/lib-http/http-response-parser.c Sun Sep 15 03:50:08 2013 +0300 @@ -102,8 +102,7 @@ return t_strdup_printf("<0x%02x>", c); } -static int http_response_parse(struct http_response_parser *parser, - const char **error_r) +static int http_response_parse(struct http_response_parser *parser) { struct http_message_parser *_parser = &parser->parser; int ret; @@ -113,94 +112,92 @@ reason-phrase = *( HTAB / SP / VCHAR / obs-text ) */ - for (;;) { - switch (parser->state) { - case HTTP_RESPONSE_PARSE_STATE_INIT: - http_response_parser_restart(parser); - parser->state = HTTP_RESPONSE_PARSE_STATE_VERSION; - /* fall through */ - case HTTP_RESPONSE_PARSE_STATE_VERSION: - if ((ret=http_message_parse_version(_parser)) <= 0) { - if (ret < 0) - *error_r = "Invalid HTTP version in response"; - return ret; - } - parser->state = HTTP_RESPONSE_PARSE_STATE_SP1; - if (_parser->cur == _parser->end) - return 0; - /* fall through */ - case HTTP_RESPONSE_PARSE_STATE_SP1: - if (*_parser->cur != ' ') { - *error_r = t_strdup_printf - ("Expected ' ' after response version, but found %s", - _chr_sanitize(*_parser->cur)); - return -1; - } + switch (parser->state) { + case HTTP_RESPONSE_PARSE_STATE_INIT: + http_response_parser_restart(parser); + parser->state = HTTP_RESPONSE_PARSE_STATE_VERSION; + /* fall through */ + case HTTP_RESPONSE_PARSE_STATE_VERSION: + if ((ret=http_message_parse_version(_parser)) <= 0) { + if (ret < 0) + _parser->error = "Invalid HTTP version in response"; + return ret; + } + parser->state = HTTP_RESPONSE_PARSE_STATE_SP1; + if (_parser->cur == _parser->end) + return 0; + /* fall through */ + case HTTP_RESPONSE_PARSE_STATE_SP1: + if (*_parser->cur != ' ') { + _parser->error = t_strdup_printf + ("Expected ' ' after response version, but found %s", + _chr_sanitize(*_parser->cur)); + return -1; + } + _parser->cur++; + parser->state = HTTP_RESPONSE_PARSE_STATE_STATUS; + if (_parser->cur >= _parser->end) + return 0; + /* fall through */ + case HTTP_RESPONSE_PARSE_STATE_STATUS: + if ((ret=http_response_parse_status(parser)) <= 0) { + if (ret < 0) + _parser->error = "Invalid HTTP status code in response"; + return ret; + } + parser->state = HTTP_RESPONSE_PARSE_STATE_SP2; + if (_parser->cur == _parser->end) + return 0; + /* fall through */ + case HTTP_RESPONSE_PARSE_STATE_SP2: + if (*_parser->cur != ' ') { + _parser->error = t_strdup_printf + ("Expected ' ' after response status code, but found %s", + _chr_sanitize(*_parser->cur)); + return -1; + } + _parser->cur++; + parser->state = HTTP_RESPONSE_PARSE_STATE_REASON; + if (_parser->cur >= _parser->end) + return 0; + /* fall through */ + case HTTP_RESPONSE_PARSE_STATE_REASON: + if ((ret=http_response_parse_reason(parser)) <= 0) { + i_assert(ret == 0); + return 0; + } + parser->state = HTTP_RESPONSE_PARSE_STATE_CR; + if (_parser->cur == _parser->end) + return 0; + /* fall through */ + case HTTP_RESPONSE_PARSE_STATE_CR: + if (*_parser->cur == '\r') _parser->cur++; - parser->state = HTTP_RESPONSE_PARSE_STATE_STATUS; - if (_parser->cur >= _parser->end) - return 0; - /* fall through */ - case HTTP_RESPONSE_PARSE_STATE_STATUS: - if ((ret=http_response_parse_status(parser)) <= 0) { - if (ret < 0) - *error_r = "Invalid HTTP status code in response"; - return ret; - } - parser->state = HTTP_RESPONSE_PARSE_STATE_SP2; - if (_parser->cur == _parser->end) - return 0; - /* fall through */ - case HTTP_RESPONSE_PARSE_STATE_SP2: - if (*_parser->cur != ' ') { - *error_r = t_strdup_printf - ("Expected ' ' after response status code, but found %s", - _chr_sanitize(*_parser->cur)); - return -1; - } - _parser->cur++; - parser->state = HTTP_RESPONSE_PARSE_STATE_REASON; - if (_parser->cur >= _parser->end) - return 0; - /* fall through */ - case HTTP_RESPONSE_PARSE_STATE_REASON: - if ((ret=http_response_parse_reason(parser)) <= 0) { - i_assert(ret == 0); - return 0; - } - parser->state = HTTP_RESPONSE_PARSE_STATE_CR; - if (_parser->cur == _parser->end) - return 0; - /* fall through */ - case HTTP_RESPONSE_PARSE_STATE_CR: - if (*_parser->cur == '\r') - _parser->cur++; - parser->state = HTTP_RESPONSE_PARSE_STATE_LF; - if (_parser->cur == _parser->end) - return 0; - /* fall through */ - case HTTP_RESPONSE_PARSE_STATE_LF: - if (*_parser->cur != '\n') { - *error_r = t_strdup_printf - ("Expected line end after response, but found %s", - _chr_sanitize(*_parser->cur)); - return -1; - } - _parser->cur++; - parser->state = HTTP_RESPONSE_PARSE_STATE_HEADER; - return 1; - case HTTP_RESPONSE_PARSE_STATE_HEADER: - default: - i_unreached(); + parser->state = HTTP_RESPONSE_PARSE_STATE_LF; + if (_parser->cur == _parser->end) + return 0; + /* fall through */ + case HTTP_RESPONSE_PARSE_STATE_LF: + if (*_parser->cur != '\n') { + _parser->error = t_strdup_printf + ("Expected line end after response, but found %s", + _chr_sanitize(*_parser->cur)); + return -1; } + _parser->cur++; + parser->state = HTTP_RESPONSE_PARSE_STATE_HEADER; + return 1; + case HTTP_RESPONSE_PARSE_STATE_HEADER: + default: + break; } i_unreached(); return -1; } -static int http_response_parse_status_line(struct http_response_parser *parser, - const char **error_r) +static int +http_response_parse_status_line(struct http_response_parser *parser) { struct http_message_parser *_parser = &parser->parser; const unsigned char *begin; @@ -212,7 +209,7 @@ _parser->cur = begin; _parser->end = _parser->cur + size; - if ((ret = http_response_parse(parser, error_r)) < 0) + if ((ret = http_response_parse(parser)) < 0) return -1; i_stream_skip(_parser->input, _parser->cur - begin); @@ -221,12 +218,15 @@ old_bytes = i_stream_get_data_size(_parser->input); } - i_assert(ret != -2); + if (ret == -2) { + _parser->error = "HTTP status line is too long"; + return -1; + } if (ret < 0) { if (_parser->input->eof && parser->state == HTTP_RESPONSE_PARSE_STATE_INIT) return 0; - *error_r = "Stream error"; + _parser->error = "Stream error"; return -1; } return 0; @@ -240,8 +240,10 @@ /* make sure we finished streaming payload from previous response before we continue. */ - if ((ret = http_message_parse_finish_payload(&parser->parser, error_r)) <= 0) + if ((ret = http_message_parse_finish_payload(&parser->parser)) <= 0) { + *error_r = parser->parser.error; return ret; + } /* HTTP-message = start-line *( header-field CRLF ) @@ -249,11 +251,15 @@ [ message-body ] */ if (parser->state != HTTP_RESPONSE_PARSE_STATE_HEADER) { - if ((ret = http_response_parse_status_line(parser, error_r)) <= 0) + if ((ret = http_response_parse_status_line(parser)) <= 0) { + *error_r = parser->parser.error; return ret; + } } - if ((ret = http_message_parse_headers(&parser->parser, error_r)) <= 0) + if ((ret = http_message_parse_headers(&parser->parser)) <= 0) { + *error_r = parser->parser.error; return ret; + } /* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-21 Section 3.3.2: @@ -286,9 +292,12 @@ if (!no_payload) { /* [ message-body ] */ - if (http_message_parse_body(&parser->parser, FALSE, error_r) < 0) - return -1; + if (http_message_parse_body(&parser->parser, FALSE) < 0) { + *error_r = parser->parser.error; + return -1; + } } + parser->state = HTTP_RESPONSE_PARSE_STATE_INIT; memset(response, 0, sizeof(*response));
--- a/src/lib-http/test-http-server.c Sun Sep 15 03:47:54 2013 +0300 +++ b/src/lib-http/test-http-server.c Sun Sep 15 03:50:08 2013 +0300 @@ -49,11 +49,12 @@ { struct client *client = (struct client *)conn; struct http_request request; + enum http_request_parse_error error_code; const char *error; int ret; while ((ret = http_request_parse_next - (client->parser, NULL, &request, &error)) > 0) { + (client->parser, NULL, &request, &error_code, &error)) > 0) { if (client_handle_request(client, &request) < 0 || request.connection_close) { client_destroy(conn);