http-response-parser.c revision feba5e502b2131c9a1c766b7ef9ff041dbf71d1d
a8c5a86d183db25a57bf193c06b41e092ec2e151Timo Sirainen/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen#include "lib.h"
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen#include "istream.h"
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen#include "http-parser.h"
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen#include "http-message-parser.h"
bdd36cfdba3ff66d25570a9ff568d69e1eb543cfTimo Sirainen#include "http-response-parser.h"
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen#include <ctype.h>
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenenum http_response_parser_state {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen HTTP_RESPONSE_PARSE_STATE_INIT = 0,
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen HTTP_RESPONSE_PARSE_STATE_VERSION,
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen HTTP_RESPONSE_PARSE_STATE_SP1,
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen HTTP_RESPONSE_PARSE_STATE_STATUS,
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen HTTP_RESPONSE_PARSE_STATE_SP2,
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen HTTP_RESPONSE_PARSE_STATE_REASON,
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen HTTP_RESPONSE_PARSE_STATE_CR,
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen HTTP_RESPONSE_PARSE_STATE_LF,
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen HTTP_RESPONSE_PARSE_STATE_HEADER
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen};
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstruct http_response_parser {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen struct http_message_parser parser;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen enum http_response_parser_state state;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen unsigned int response_status;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen const char *response_reason;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen};
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstruct http_response_parser *
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenhttp_response_parser_init(struct istream *input,
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen const struct http_header_limits *hdr_limits)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen{
a21f618de284dc22a480af1371d5f5cea50a39dfTimo Sirainen struct http_response_parser *parser;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
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 Sirainen return parser;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen}
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenvoid http_response_parser_deinit(struct http_response_parser **_parser)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen{
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen struct http_response_parser *parser = *_parser;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen http_message_parser_deinit(&parser->parser);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen i_free(parser);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen}
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic void
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenhttp_response_parser_restart(struct http_response_parser *parser)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen{
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen http_message_parser_restart(&parser->parser, NULL);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen parser->response_status = 0;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen parser->response_reason = NULL;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen}
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic int http_response_parse_status(struct http_response_parser *parser)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen{
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen const unsigned char *p = parser->parser.cur;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen const size_t size = parser->parser.end - parser->parser.cur;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* status-code = 3DIGIT
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (size < 3)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return 0;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (!i_isdigit(p[0]) || !i_isdigit(p[1]) || !i_isdigit(p[2]))
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return -1;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen parser->response_status =
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen (p[0] - '0')*100 + (p[1] - '0')*10 + (p[2] - '0');
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen parser->parser.cur += 3;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return 1;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen}
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic int http_response_parse_reason(struct http_response_parser *parser)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen{
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen const unsigned char *p = parser->parser.cur;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* reason-phrase = *( HTAB / SP / VCHAR / obs-text )
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen // FIXME: limit length
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen while (p < parser->parser.end && http_char_is_text(*p))
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen p++;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (p == parser->parser.end)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return 0;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen parser->response_reason =
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen p_strdup_until(parser->parser.msg.pool, parser->parser.cur, p);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen parser->parser.cur = p;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return 1;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen}
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic inline const char *_chr_sanitize(unsigned char c)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen{
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (c >= 0x20 && c < 0x7F)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return t_strdup_printf("'%c'", c);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return t_strdup_printf("0x%02x", c);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen}
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic int http_response_parse(struct http_response_parser *parser,
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen const char **error_r)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen{
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen struct http_message_parser *_parser = &parser->parser;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen int ret;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
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 */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen for (;;) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen switch (parser->state) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen case HTTP_RESPONSE_PARSE_STATE_INIT:
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen http_response_parser_restart(parser);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen parser->state = HTTP_RESPONSE_PARSE_STATE_VERSION;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* fall through */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen case HTTP_RESPONSE_PARSE_STATE_VERSION:
d48e40d6c77d673ad402d96571198d1cce4da225Timo Sirainen if ((ret=http_message_parse_version(_parser)) <= 0) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (ret < 0)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen *error_r = "Invalid HTTP version in response";
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return ret;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
a327d9301f593433c228c4cc8cca05c95b37f6fbTimo Sirainen parser->state = HTTP_RESPONSE_PARSE_STATE_SP1;
d48e40d6c77d673ad402d96571198d1cce4da225Timo Sirainen if (_parser->cur == _parser->end)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return 0;
27a44fcfd8d19bffe0f267f20a2b5d3fe7600fddTimo Sirainen /* fall through */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen case HTTP_RESPONSE_PARSE_STATE_SP1:
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (*_parser->cur != ' ') {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen *error_r = t_strdup_printf
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen ("Expected ' ' after response version, but found %s",
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen _chr_sanitize(*_parser->cur));
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return -1;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen _parser->cur++;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen parser->state = HTTP_RESPONSE_PARSE_STATE_STATUS;
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen if (_parser->cur >= _parser->end)
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen return 0;
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen /* fall through */
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen case HTTP_RESPONSE_PARSE_STATE_STATUS:
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen if ((ret=http_response_parse_status(parser)) <= 0) {
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen if (ret < 0)
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen *error_r = "Invalid HTTP status code in response";
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen return ret;
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen }
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen parser->state = HTTP_RESPONSE_PARSE_STATE_SP2;
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen if (_parser->cur == _parser->end)
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen return 0;
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen /* fall through */
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen case HTTP_RESPONSE_PARSE_STATE_SP2:
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen if (*_parser->cur != ' ') {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen *error_r = t_strdup_printf
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen ("Expected ' ' after response status code, but found %s",
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen _chr_sanitize(*_parser->cur));
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return -1;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen _parser->cur++;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen parser->state = HTTP_RESPONSE_PARSE_STATE_REASON;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (_parser->cur >= _parser->end)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return 0;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* fall through */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen case HTTP_RESPONSE_PARSE_STATE_REASON:
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if ((ret=http_response_parse_reason(parser)) <= 0) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen i_assert(ret == 0);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return 0;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen parser->state = HTTP_RESPONSE_PARSE_STATE_CR;
a618726eb3eb09a3866fe93208baf923d593f4d3Timo Sirainen if (_parser->cur == _parser->end)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return 0;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* fall through */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen case HTTP_RESPONSE_PARSE_STATE_CR:
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (*_parser->cur == '\r')
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen _parser->cur++;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen parser->state = HTTP_RESPONSE_PARSE_STATE_LF;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (_parser->cur == _parser->end)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return 0;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* fall through */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen case HTTP_RESPONSE_PARSE_STATE_LF:
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (*_parser->cur != '\n') {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen *error_r = t_strdup_printf
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen ("Expected line end after response, but found %s",
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen _chr_sanitize(*_parser->cur));
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return -1;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
a618726eb3eb09a3866fe93208baf923d593f4d3Timo Sirainen _parser->cur++;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen parser->state = HTTP_RESPONSE_PARSE_STATE_HEADER;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return 1;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen case HTTP_RESPONSE_PARSE_STATE_HEADER:
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen default:
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen i_unreached();
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen i_unreached();
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return -1;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen}
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
a618726eb3eb09a3866fe93208baf923d593f4d3Timo Sirainenstatic int http_response_parse_status_line(struct http_response_parser *parser,
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen const char **error_r)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen{
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch struct http_message_parser *_parser = &parser->parser;
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch const unsigned char *begin;
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch size_t size, old_bytes = 0;
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch int ret;
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch while ((ret = i_stream_read_data(_parser->input, &begin, &size,
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch old_bytes)) > 0) {
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch _parser->cur = begin;
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch _parser->end = _parser->cur + size;
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch if ((ret = http_response_parse(parser, error_r)) < 0)
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch return -1;
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch i_stream_skip(_parser->input, _parser->cur - begin);
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch if (ret > 0)
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch return 1;
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch old_bytes = i_stream_get_data_size(_parser->input);
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch }
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch i_assert(ret != -2);
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch if (ret < 0) {
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch if (_parser->input->eof &&
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch parser->state == HTTP_RESPONSE_PARSE_STATE_INIT)
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch return 0;
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch *error_r = "Stream error";
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch return -1;
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch }
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch return 0;
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch}
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch
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)
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch{
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch int ret;
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch
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 return ret;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* HTTP-message = start-line
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen *( header-field CRLF )
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen CRLF
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen [ message-body ]
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen */
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 return ret;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if ((ret = http_message_parse_headers(&parser->parser, error_r)) <= 0)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return ret;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-21
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen Section 3.3.2:
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
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 */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if ((parser->response_status / 100 == 1 || parser->response_status == 204) &&
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen parser->parser.msg.content_length > 0) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen *error_r = t_strdup_printf(
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen "Unexpected Content-Length header field for %u response "
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen "(length=%"PRIuUOFF_T")", parser->response_status,
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen parser->parser.msg.content_length);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return -1;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-21
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen Section 3.3.3:
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen
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 */
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 no_payload = TRUE;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
e25c9a57d651456a5f446a98e677c8c472c4ce98Timo Sirainen
e25c9a57d651456a5f446a98e677c8c472c4ce98Timo Sirainen if (!no_payload) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* [ message-body ] */
e25c9a57d651456a5f446a98e677c8c472c4ce98Timo Sirainen if (http_message_parse_body(&parser->parser, FALSE, error_r) < 0)
e25c9a57d651456a5f446a98e677c8c472c4ce98Timo Sirainen return -1;
e25c9a57d651456a5f446a98e677c8c472c4ce98Timo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen parser->state = HTTP_RESPONSE_PARSE_STATE_INIT;
e25c9a57d651456a5f446a98e677c8c472c4ce98Timo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen memset(response, 0, sizeof(*response));
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen response->status = parser->response_status;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen response->reason = parser->response_reason;
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->date = parser->parser.msg.date;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen response->payload = parser->parser.payload;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen response->header = parser->parser.msg.header;
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;
a618726eb3eb09a3866fe93208baf923d593f4d3Timo Sirainen response->connection_close = parser->parser.msg.connection_close;
a618726eb3eb09a3866fe93208baf923d593f4d3Timo Sirainen return 1;
a618726eb3eb09a3866fe93208baf923d593f4d3Timo Sirainen}
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen