bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Boschhttp_response_parser_init(struct istream *input,
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch /* FIXME: implement status line limit */
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch if ((flags & HTTP_RESPONSE_PARSE_FLAG_STRICT) != 0)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser = i_new(struct http_response_parser, 1);
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_response_parser_deinit(struct http_response_parser **_parser)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_response_parser *parser = *_parser;
7384b4e78eaab44693c985192276e31322155e32Stephan Boschhttp_response_parser_restart(struct http_response_parser *parser)
6dad0888fcec8372f230941c70d8940b8c203b32Stephan Bosch http_message_parser_restart(&parser->parser, NULL);
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic int http_response_parse_status(struct http_response_parser *parser)
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen const size_t size = parser->parser.end - parser->parser.cur;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* status-code = 3DIGIT
1bc12a53ddc6696bb209fb79d7cc66262d2ea621Timo Sirainen if (!i_isdigit(p[0]) || !i_isdigit(p[1]) || !i_isdigit(p[2]))
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch (p[0] - '0')*100 + (p[1] - '0')*10 + (p[2] - '0');
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic int http_response_parse_reason(struct http_response_parser *parser)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* reason-phrase = *( HTAB / SP / VCHAR / obs-text )
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch // FIXME: limit length
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen while (p < parser->parser.end && http_char_is_text(*p))
1ec26e0b70ac7f8a4e3dfbc59aa77f572651d5aeStephan Bosch pool = http_message_parser_get_pool(&parser->parser);
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainenstatic const char *_reply_sanitize(struct http_message_parser *parser)
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen const unsigned char *p;
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen unsigned int i;
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen for (p = parser->cur, i = 0; p < parser->end && i < 20; p++, i++) {
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen if (*p == 0x0a)
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen else if (*p == 0x0d)
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Boschstatic int http_response_parse(struct http_response_parser *parser)
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen struct http_message_parser *_parser = &parser->parser;
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* RFC 7230, Section 3.1.2: Status Line
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch status-line = HTTP-version SP status-code SP reason-phrase CRLF
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch status-code = 3DIGIT
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch reason-phrase = *( HTAB / SP / VCHAR / obs-text )
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch parser->state = HTTP_RESPONSE_PARSE_STATE_VERSION;
be90d5399e65511d3161fa5f2063b048a2ee8264Stephan Bosch parser->response_offset = _parser->input->v_offset +
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch /* fall through */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if ((ret=http_message_parse_version(_parser)) <= 0) {
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen "Invalid HTTP version in response: %s",
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch parser->state = HTTP_RESPONSE_PARSE_STATE_SP1;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch /* fall through */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch ("Expected ' ' after response version, but found %s",
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch parser->state = HTTP_RESPONSE_PARSE_STATE_STATUS;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch /* fall through */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if ((ret=http_response_parse_status(parser)) <= 0) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch _parser->error = "Invalid HTTP status code in response";
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch parser->state = HTTP_RESPONSE_PARSE_STATE_SP2;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch /* fall through */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch ("Expected ' ' after response status code, but found %s",
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch parser->state = HTTP_RESPONSE_PARSE_STATE_REASON;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch /* fall through */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if ((ret=http_response_parse_reason(parser)) <= 0) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch /* fall through */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch /* fall through */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch ("Expected line end after response, but found %s",
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch parser->state = HTTP_RESPONSE_PARSE_STATE_HEADER;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Boschhttp_response_parse_status_line(struct http_response_parser *parser)
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen struct http_message_parser *_parser = &parser->parser;
651f981ca065d2365bf2ea07b04318a5402f047aTimo Sirainen while ((ret = i_stream_read_bytes(_parser->input, &begin, &size,
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen i_stream_skip(_parser->input, _parser->cur - begin);
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen old_bytes = i_stream_get_data_size(_parser->input);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch _parser->error = "HTTP status line is too long";
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen parser->state == HTTP_RESPONSE_PARSE_STATE_INIT)
2a8454fe986d4a881d3612eebb39101be1d6db94Timo Sirainen _parser->error = t_strdup_printf("Stream error: %s",
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Boschhttp_response_parse_retry_after(const char *hdrval, time_t resp_time,
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* RFC 7231, Section 7.1.3: Retry-After
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch The value of this field can be either an HTTP-date or a number of
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch seconds to delay after the response is received.
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch Retry-After = HTTP-date / delta-seconds
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch A delay-seconds value is a non-negative decimal integer, representing
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch time in seconds.
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch delta-seconds = 1*DIGIT
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch ((unsigned char *)hdrval, strlen(hdrval), retry_after_r) ? 0 : -1);
be90d5399e65511d3161fa5f2063b048a2ee8264Stephan Boschuoff_t http_response_parser_get_last_offset(struct http_response_parser *parser)
7384b4e78eaab44693c985192276e31322155e32Stephan Boschint http_response_parse_next(struct http_response_parser *parser,
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch struct http_response *response, const char **error_r)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* make sure we finished streaming payload from previous response
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch before we continue. */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if ((ret = http_message_parse_finish_payload(&parser->parser)) <= 0) {
c177bc7c153dd4d00dfa719f8a93c81129c4235eStephan Bosch if (parser->state == HTTP_RESPONSE_PARSE_STATE_INIT)
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* RFC 7230, Section 3:
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch HTTP-message = start-line
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch *( header-field CRLF )
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch [ message-body ]
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (parser->state != HTTP_RESPONSE_PARSE_STATE_HEADER) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if ((ret = http_response_parse_status_line(parser)) <= 0) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if ((ret = http_message_parse_headers(&parser->parser)) <= 0) {
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* RFC 7230, Section 3.3.2: Content-Length
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch A server MUST NOT send a Content-Length header field in any response
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch with a status code of 1xx (Informational) or 204 (No Content).
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch if ((parser->response_status / 100 == 1 || parser->response_status == 204) &&
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch "Unexpected Content-Length header field for %u response "
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch "(length=%"PRIuUOFF_T")", parser->response_status,
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* RFC 7230, Section 3.3.3: Message Body Length
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch 1. Any response to a HEAD request and any response with a 1xx
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch (Informational), 204 (No Content), or 304 (Not Modified) status
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch code is always terminated by the first empty line after the
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch header fields, regardless of the header fields present in the
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch message, and thus cannot contain a message body.
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch if (parser->response_status / 100 == 1 || parser->response_status == 204
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch || parser->response_status == 304) { // HEAD is handled in caller
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch payload_type = HTTP_RESPONSE_PAYLOAD_TYPE_NOT_PRESENT;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch if ((payload_type == HTTP_RESPONSE_PAYLOAD_TYPE_ALLOWED) ||
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch (payload_type == HTTP_RESPONSE_PAYLOAD_TYPE_ONLY_UNSUCCESSFUL &&
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* [ message-body ] */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (http_message_parse_body(&parser->parser, FALSE) < 0) {
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* RFC 7231, Section 7.1.3: Retry-After
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch Servers send the "Retry-After" header field to indicate how long the
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch user agent ought to wait before making a follow-up request. When
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch sent with a 503 (Service Unavailable) response, Retry-After indicates
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch how long the service is expected to be unavailable to the client.
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch When sent with any 3xx (Redirection) response, Retry-After indicates
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch the minimum time that the user agent is asked to wait before issuing
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch the redirected request.
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch if (parser->response_status == 503 || (parser->response_status / 100) == 3) {
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch hdrval = http_header_field_get(parser->parser.msg.header, "Retry-After");
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch (hdrval, parser->parser.msg.date, &retry_after);
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch /* broken Retry-After header is ignored */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->state = HTTP_RESPONSE_PARSE_STATE_INIT;
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch response->version_major = parser->parser.msg.version_major;
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch response->version_minor = parser->parser.msg.version_minor;
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch response->location = parser->parser.msg.location;
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch response->connection_options = parser->parser.msg.connection_options;