http-response-parser.c revision feba5e502b2131c9a1c766b7ef9ff041dbf71d1d
a8c5a86d183db25a57bf193c06b41e092ec2e151Timo Sirainen/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenhttp_response_parser_init(struct istream *input,
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* FIXME: implement status line limit */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen parser = i_new(struct http_response_parser, 1);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen http_message_parser_init(&parser->parser, input, hdr_limits);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenvoid http_response_parser_deinit(struct http_response_parser **_parser)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen struct http_response_parser *parser = *_parser;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenhttp_response_parser_restart(struct http_response_parser *parser)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen http_message_parser_restart(&parser->parser, NULL);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic int http_response_parse_status(struct http_response_parser *parser)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen const size_t size = parser->parser.end - parser->parser.cur;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* status-code = 3DIGIT
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (!i_isdigit(p[0]) || !i_isdigit(p[1]) || !i_isdigit(p[2]))
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen (p[0] - '0')*100 + (p[1] - '0')*10 + (p[2] - '0');
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic int http_response_parse_reason(struct http_response_parser *parser)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* reason-phrase = *( HTAB / SP / VCHAR / obs-text )
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen // FIXME: limit length
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen while (p < parser->parser.end && http_char_is_text(*p))
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen p_strdup_until(parser->parser.msg.pool, parser->parser.cur, p);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic inline const char *_chr_sanitize(unsigned char c)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic int http_response_parse(struct http_response_parser *parser,
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen const char **error_r)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen struct http_message_parser *_parser = &parser->parser;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* status-line = HTTP-version SP status-code SP reason-phrase CRLF
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen status-code = 3DIGIT
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen reason-phrase = *( HTAB / SP / VCHAR / obs-text )
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen parser->state = HTTP_RESPONSE_PARSE_STATE_VERSION;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* fall through */
d48e40d6c77d673ad402d96571198d1cce4da225Timo Sirainen if ((ret=http_message_parse_version(_parser)) <= 0) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen *error_r = "Invalid HTTP version in response";
a327d9301f593433c228c4cc8cca05c95b37f6fbTimo Sirainen parser->state = HTTP_RESPONSE_PARSE_STATE_SP1;
27a44fcfd8d19bffe0f267f20a2b5d3fe7600fddTimo Sirainen /* fall through */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen ("Expected ' ' after response version, but found %s",
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen parser->state = HTTP_RESPONSE_PARSE_STATE_STATUS;
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen /* fall through */
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen if ((ret=http_response_parse_status(parser)) <= 0) {
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen *error_r = "Invalid HTTP status code in response";
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen parser->state = HTTP_RESPONSE_PARSE_STATE_SP2;
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen /* fall through */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen ("Expected ' ' after response status code, but found %s",
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen parser->state = HTTP_RESPONSE_PARSE_STATE_REASON;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* fall through */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if ((ret=http_response_parse_reason(parser)) <= 0) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* fall through */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* fall through */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen ("Expected line end after response, but found %s",
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen parser->state = HTTP_RESPONSE_PARSE_STATE_HEADER;
a618726eb3eb09a3866fe93208baf923d593f4d3Timo Sirainenstatic int http_response_parse_status_line(struct http_response_parser *parser,
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen const char **error_r)
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch struct http_message_parser *_parser = &parser->parser;
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch const unsigned char *begin;
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch while ((ret = i_stream_read_data(_parser->input, &begin, &size,
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch if ((ret = http_response_parse(parser, error_r)) < 0)
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch i_stream_skip(_parser->input, _parser->cur - begin);
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch old_bytes = i_stream_get_data_size(_parser->input);
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch parser->state == HTTP_RESPONSE_PARSE_STATE_INIT)
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Boschint http_response_parse_next(struct http_response_parser *parser,
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch bool no_payload, struct http_response *response,
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch const char **error_r)
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen /* make sure we finished streaming payload from previous response
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen before we continue. */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if ((ret = http_message_parse_finish_payload(&parser->parser, error_r)) <= 0)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* HTTP-message = start-line
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen *( header-field CRLF )
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen [ message-body ]
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (parser->state != HTTP_RESPONSE_PARSE_STATE_HEADER) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if ((ret = http_response_parse_status_line(parser, error_r)) <= 0)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if ((ret = http_message_parse_headers(&parser->parser, error_r)) <= 0)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-21
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen Section 3.3.2:
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen A server MUST NOT send a Content-Length header field in any response
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen with a status code of 1xx (Informational) or 204 (No Content). [...]
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if ((parser->response_status / 100 == 1 || parser->response_status == 204) &&
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen "Unexpected Content-Length header field for %u response "
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen "(length=%"PRIuUOFF_T")", parser->response_status,
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-21
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen Section 3.3.3:
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen Any response to a HEAD request and any response with a 1xx
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen (Informational), 204 (No Content), or 304 (Not Modified) status
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen code is always terminated by the first empty line after the
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen header fields, regardless of the header fields present in the
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen message, and thus cannot contain a message body.
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (parser->response_status / 100 == 1 || parser->response_status == 204
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen || parser->response_status == 304) { // HEAD is handled in caller
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* [ message-body ] */
e25c9a57d651456a5f446a98e677c8c472c4ce98Timo Sirainen if (http_message_parse_body(&parser->parser, FALSE, error_r) < 0)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen parser->state = HTTP_RESPONSE_PARSE_STATE_INIT;
88df77efd9ffdfd9a62bca0a9c8f403ecdbea3ffTimo Sirainen response->version_major = parser->parser.msg.version_major;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen response->version_minor = parser->parser.msg.version_minor;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen response->location = parser->parser.msg.location;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen response->headers = *http_header_get_fields(response->header); /* FIXME: remove in v2.3 */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen response->connection_options = parser->parser.msg.connection_options;