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);
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;
}
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;
}
/* 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;
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;
}
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;
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) {
}
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;
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 */
}
}
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;
}
parser->payload =
i_stream_create_limit(parser->input, (size_t)-1);
}
+ if (parser->error_code != HTTP_MESSAGE_PARSE_ERROR_NONE)
+ return -1;
return 0;
}
#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;
const unsigned char *cur, *end;
+ const char *error;
+ enum http_message_parse_error error_code;
+
struct http_header_parser *header_parser;
struct istream *payload;
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
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;
}
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;
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.
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;
}
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;
}
/* 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;
/* 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;
}
}
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;
_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);
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 )
[ 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
*/
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
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;
#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,
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
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;
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;
- }
- _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;
- }
+ 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_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;
_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);
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;
/* 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 )
[ 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:
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));
{
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);