1
/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
6
#include "http-parser.h"
7
#include "http-header-parser.h"
9
#include "http-transfer.h"
10
#include "http-message-parser.h"
14
void http_message_parser_init(struct http_message_parser *parser,
15
struct istream *input)
17
memset(parser, 0, sizeof(*parser));
18
parser->input = input;
21
void http_message_parser_deinit(struct http_message_parser *parser)
23
if (parser->header_parser != NULL)
24
http_header_parser_deinit(&parser->header_parser);
25
if (parser->msg_pool != NULL)
26
pool_unref(&parser->msg_pool);
27
if (parser->payload != NULL)
28
i_stream_unref(&parser->payload);
31
void http_message_parser_restart(struct http_message_parser *parser)
33
i_assert(parser->payload == NULL);
35
if (parser->header_parser == NULL)
36
parser->header_parser = http_header_parser_init(parser->input);
38
http_header_parser_reset(parser->header_parser);
40
if (parser->msg_pool != NULL)
41
pool_unref(&parser->msg_pool);
42
parser->msg_pool = pool_alloconly_create("http_message", 4096);
43
memset(&parser->msg, 0, sizeof(parser->msg));
44
parser->msg.date = (time_t)-1;
45
p_array_init(&parser->msg.headers, parser->msg_pool, 32);
48
int http_message_parse_version(struct http_message_parser *parser)
50
const unsigned char *p = parser->cur;
51
const size_t size = parser->end - parser->cur;
53
/* HTTP-version = HTTP-name "/" DIGIT "." DIGIT
54
HTTP-name = %x48.54.54.50 ; "HTTP", case-sensitive
58
if (memcmp(p, "HTTP/", 5) != 0 ||
59
!i_isdigit(p[5]) || p[6] != '.' || !i_isdigit(p[7]))
61
parser->msg.version_major = p[5] - '0';
62
parser->msg.version_minor = p[7] - '0';
67
int http_message_parse_finish_payload(struct http_message_parser *parser,
70
const unsigned char *data;
74
if (parser->payload == NULL)
77
while ((ret = i_stream_read_data(parser->payload, &data, &size, 0)) > 0)
78
i_stream_skip(parser->payload, size);
79
if (ret == 0 || parser->payload->stream_errno != 0) {
81
*error_r = "Stream error while skipping payload";
84
i_stream_unref(&parser->payload);
89
http_message_parse_header(struct http_message_parser *parser, const char *name,
90
const unsigned char *data, size_t size,
93
struct http_response_header *hdr;
94
struct http_parser hparser;
97
hdr = array_append_space(&parser->msg.headers);
98
hdr->key = p_strdup(parser->msg_pool, name);
99
hdr->value = value = p_malloc(parser->msg_pool, size+1);
100
memcpy(value, data, size);
105
if (strcasecmp(name, "Connection") == 0) {
108
/* Connection = 1#connection-option
109
connection-option = token
111
http_parser_init(&hparser, data, size);
113
if (http_parse_token_list_next(&hparser, &option) <= 0)
115
if (strcasecmp(option, "close") == 0) {
116
parser->msg.connection_close = TRUE;
117
break; // not interested in any other options
122
if (strcasecmp(name, "Content-Length") == 0) {
123
/* Content-Length = 1*DIGIT */
124
if (str_to_uoff(hdr->value, &parser->msg.content_length) < 0) {
125
*error_r = "Invalid Content-Length header";
132
if (strcasecmp(name, "Date") == 0) {
133
/* Date = HTTP-date */
134
(void)http_date_parse(data, size, &parser->msg.date);
139
if (strcasecmp(name, "Location") == 0) {
140
/* Location = URI-reference (not parsed here) */
141
parser->msg.location = hdr->value;
146
if (strcasecmp(name, "Transfer-Encoding") == 0) {
147
/* Transfer-Encoding = 1#transfer-coding */
148
parser->msg.transfer_encoding = hdr->value;
158
int http_message_parse_headers(struct http_message_parser *parser,
159
const char **error_r)
161
const char *field_name, *error;
162
const unsigned char *field_data;
166
/* *( header-field CRLF ) CRLF */
167
while ((ret=http_header_parse_next_field
168
(parser->header_parser, &field_name, &field_data, &field_size, &error)) > 0) {
169
if (field_name == NULL) {
173
if (http_message_parse_header(parser, field_name, field_data,
174
field_size, error_r) < 0)
179
*error_r = t_strdup_printf(
180
"Failed to parse response header: %s", error);
185
int http_message_parse_body(struct http_message_parser *parser,
186
const char **error_r)
188
struct http_parser hparser;
190
if (parser->msg.content_length > 0) {
191
/* Got explicit message size from Content-Length: header */
193
i_stream_create_limit(parser->input,
194
parser->msg.content_length);
195
} else if (parser->msg.transfer_encoding != NULL) {
198
/* Transfer-Encoding = 1#transfer-coding
199
transfer-coding = "chunked" / "compress" / "deflate" / "gzip"
200
/ transfer-extension ; [FIXME]
201
transfer-extension = token *( OWS ";" OWS transfer-parameter )
203
http_parser_init(&hparser,
204
(const unsigned char *)parser->msg.transfer_encoding,
205
strlen(parser->msg.transfer_encoding));
207
if (http_parse_token_list_next(&hparser, &tenc) <= 0)
209
if (strcasecmp(tenc, "chunked") == 0) {
211
http_transfer_chunked_istream_create(parser->input);
214
*error_r = t_strdup_printf(
215
"Unknown Transfer-Encoding `%s'", tenc);