# HG changeset patch # User Stephan Bosch # Date 1379206557 -10800 # Node ID 9da90cf29d91f8c84bd316c5d1c4cc629af02860 # Parent e53f5173fa2d71e01f4ce5eb9e55b250fea1002f lib-http: Added support for parsing Expect: header (currently only accepts `100-continue'). diff -r e53f5173fa2d -r 9da90cf29d91 src/lib-http/http-request-parser.c --- a/src/lib-http/http-request-parser.c Sun Sep 15 03:55:11 2013 +0300 +++ b/src/lib-http/http-request-parser.c Sun Sep 15 03:55:57 2013 +0300 @@ -334,6 +334,159 @@ return i_stream_have_bytes_left(parser->parser.payload); } +static int +http_request_parse_expect_header(struct http_request_parser *parser, + struct http_request *request, const struct http_header_field *hdr) +{ + struct http_message_parser *_parser = &parser->parser; + struct http_parser hparser; + bool parse_error = FALSE; + unsigned int num_expectations = 0; + + /* Expect = 1#expectation + expectation = expect-name [ BWS "=" BWS expect-value ] + *( OWS ";" [ OWS expect-param ] ) + expect-param = expect-name [ BWS "=" BWS expect-value ] + expect-name = token + expect-value = token / quoted-string + */ + http_parser_init(&hparser, (const unsigned char *)hdr->value, hdr->size); + while (!parse_error) { + const char *expect_name, *expect_value; + + /* expect-name */ + if (http_parse_token(&hparser, &expect_name) > 0) { + num_expectations++; + if (strcasecmp(expect_name, "100-continue") == 0) { + request->expect_100_continue = TRUE; + } else { + /* http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-23 + Section 5.1.1: + + If all received Expect header field(s) are syntactically valid but + contain an expectation that the recipient does not understand or + cannot comply with, the recipient MUST respond with a 417 + (Expectation Failed) status code. A recipient of a syntactically + invalid Expectation header field MUST respond with a 4xx status code + other than 417. + + --> Must check rest of expect header syntax before returning error. + */ + if (parser->error_code == HTTP_REQUEST_PARSE_ERROR_NONE) { + parser->error_code = HTTP_REQUEST_PARSE_ERROR_EXPECTATION_FAILED; + _parser->error = t_strdup_printf + ("Unknown Expectation `%s'", expect_name); + } + } + + /* BWS "=" BWS */ + http_parse_ows(&hparser); + if (hparser.cur >= hparser.end) + break; + + if (*hparser.cur == '=') { + hparser.cur++; + http_parse_ows(&hparser); + + /* value */ + if (http_parse_word(&hparser, &expect_value) <= 0) { + parse_error = TRUE; + break; + } + + if (parser->error_code == HTTP_REQUEST_PARSE_ERROR_NONE) { + parser->error_code = HTTP_REQUEST_PARSE_ERROR_EXPECTATION_FAILED; + _parser->error = t_strdup_printf + ("Expectation `%s' has unexpected value", expect_name); + } + } + + /* *( OWS ";" [ OWS expect-param ] ) */ + while (!parse_error) { + const char *attribute, *value; + + /* OWS ";" */ + http_parse_ows(&hparser); + if (hparser.cur >= hparser.end || *hparser.cur != ';') + break; + hparser.cur++; + http_parse_ows(&hparser); + + /* expect-param */ + if (http_parse_token(&hparser, &attribute) <= 0) { + parse_error = TRUE; + break; + } + + /* BWS "=" BWS */ + http_parse_ows(&hparser); + if (hparser.cur >= hparser.end || *hparser.cur != '=') { + parse_error = TRUE; + break; + } + hparser.cur++; + http_parse_ows(&hparser); + + /* value */ + if (http_parse_word(&hparser, &value) <= 0) { + parse_error = TRUE; + break; + } + + if (parser->error_code == HTTP_REQUEST_PARSE_ERROR_NONE) { + parser->error_code = HTTP_REQUEST_PARSE_ERROR_EXPECTATION_FAILED; + _parser->error = t_strdup_printf + ("Expectation `%s' has unknown parameter `'%s'", + expect_name, attribute); + } + } + if (parse_error) + break; + } + http_parse_ows(&hparser); + if (hparser.cur >= hparser.end || *hparser.cur != ',') + break; + hparser.cur++; + http_parse_ows(&hparser); + } + + if (parse_error || hparser.cur < hparser.end) { + parser->error_code = HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST; + _parser->error = "Invalid Expect header"; + return -1; + } + + if (parser->error_code != HTTP_REQUEST_PARSE_ERROR_NONE) + return -1; + + if (num_expectations == 0) { + parser->error_code = HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST; + _parser->error = "Empty Expect header"; + return -1; + } + return 0; +} + +static int +http_request_parse_headers(struct http_request_parser *parser, + struct http_request *request) +{ + const ARRAY_TYPE(http_header_field) *hdrs; + const struct http_header_field *hdr; + + hdrs = http_header_get_fields(parser->parser.msg.header); + array_foreach(hdrs, hdr) { + int ret = 0; + + if (http_header_field_is(hdr, "Expect")) + ret = http_request_parse_expect_header(parser, request, hdr); + + if (ret < 0) + return -1; + } + return 0; +} + int http_request_parse_next(struct http_request_parser *parser, pool_t pool, struct http_request *request, enum http_request_parse_error *error_code_r, const char **error_r) @@ -421,6 +574,13 @@ return -1; } + /* parse request-specific headers */ + if (http_request_parse_headers(parser, request) < 0) { + *error_code_r = parser->error_code; + *error_r = parser->parser.error; + return -1; + } + request->method = parser->request_method; request->target_raw = parser->request_target; request->version_major = parser->parser.msg.version_major; diff -r e53f5173fa2d -r 9da90cf29d91 src/lib-http/http-request-parser.h --- a/src/lib-http/http-request-parser.h Sun Sep 15 03:55:11 2013 +0300 +++ b/src/lib-http/http-request-parser.h Sun Sep 15 03:55:57 2013 +0300 @@ -10,6 +10,8 @@ HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST, /* recoverable generic error */ HTTP_REQUEST_PARSE_ERROR_NOT_IMPLEMENTED, /* used unimplemented feature (recoverable) */ + HTTP_REQUEST_PARSE_ERROR_EXPECTATION_FAILED, /* unknown item in Expect: + header (recoverable) */ HTTP_REQUEST_PARSE_ERROR_METHOD_TOO_LONG, /* method too long (fatal) */ HTTP_REQUEST_PARSE_ERROR_TARGET_TOO_LONG, /* target too long (fatal) */ HTTP_REQUEST_PARSE_ERROR_PAYLOAD_TOO_LARGE /* payload too large (fatal) */ diff -r e53f5173fa2d -r 9da90cf29d91 src/lib-http/http-request.h --- a/src/lib-http/http-request.h Sun Sep 15 03:55:11 2013 +0300 +++ b/src/lib-http/http-request.h Sun Sep 15 03:55:57 2013 +0300 @@ -46,6 +46,7 @@ ARRAY_TYPE(const_string) connection_options; unsigned int connection_close:1; + unsigned int expect_100_continue:1; }; static inline bool