http-date.c \
http-url.c \
http-parser.c \
+ http-header.c \
http-header-parser.c \
http-transfer-chunked.c \
http-message-parser.c \
+ http-request.c \
http-request-parser.c \
+ http-response.c \
http-response-parser.c \
http-client-request.c \
http-client-connection.c \
http-date.h \
http-url.h \
http-parser.h \
+ http-header.h \
http-header-parser.h \
http-transfer.h \
http-message-parser.h \
+ http-request.h \
http-request-parser.h \
http-response.h \
http-response-parser.h \
test_http_response_parser_LDADD = \
http-date.lo \
http-parser.lo \
+ http-header.lo \
http-header-parser.lo \
http-transfer-chunked.lo \
http-message-parser.lo \
if (callback != NULL) {
struct http_response response;
- memset(&response, 0, sizeof(response));
- response.status = status;
- response.reason = error;
+ http_response_init(&response, status, error);
(void)callback(&response, req->context);
}
}
--- /dev/null
+/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+
+#include "http-header.h"
+
+struct http_header {
+ ARRAY_TYPE(http_header_field) fields;
+ /* FIXME: ARRAY(struct http_header_field *) *btree; */
+};
+
+struct http_header *
+http_header_create(pool_t pool, unsigned int init_count)
+{
+ struct http_header *header;
+
+ header = p_new(pool, struct http_header, 1);
+ p_array_init(&header->fields, pool, init_count);
+
+ return header;
+}
+
+const struct http_header_field *
+http_header_field_add(struct http_header *header,
+ const char *name, const unsigned char *data, size_t size)
+{
+ struct http_header_field *hfield;
+ pool_t pool = array_get_pool(&header->fields);
+ void *value;
+
+ hfield = array_append_space(&header->fields);
+ hfield->key = p_strdup(pool, name);
+ hfield->size = size;
+
+ value = p_malloc(pool, size+1);
+ memcpy(value, data, size);
+ hfield->value = (const char *)value;
+
+ return hfield;
+}
+
+void http_header_field_delete(struct http_header *header, const char *name)
+{
+ ARRAY_TYPE(http_header_field) *hfields = &header->fields;
+ const struct http_header_field *hfield;
+
+ array_foreach(hfields, hfield) {
+ if (http_header_field_is(hfield, name)) {
+ array_delete(hfields, array_foreach_idx(hfields, hfield), 1);
+ }
+ }
+}
+
+const ARRAY_TYPE(http_header_field) *
+http_header_get_fields(const struct http_header *header)
+{
+ return &header->fields;
+}
+
+const struct http_header_field *
+http_header_field_find(const struct http_header *header, const char *name)
+{
+ const struct http_header_field *hfield;
+
+ array_foreach(&header->fields, hfield) {
+ if (http_header_field_is(hfield, name))
+ return hfield;
+ }
+
+ return NULL;
+}
+
+const char *
+http_header_field_get(const struct http_header *header, const char *name)
+{
+ const struct http_header_field *hfield =
+ http_header_field_find(header, name);
+ return (hfield == NULL ? NULL : hfield->value);
+}
+
+int http_header_field_find_unique(const struct http_header *header,
+ const char *name, const struct http_header_field **hfield_r)
+{
+ const struct http_header_field *hfield, *hfield_found = NULL;
+
+ array_foreach(&header->fields, hfield) {
+ if (http_header_field_is(hfield, name)) {
+ if (hfield_found != NULL)
+ return -1;
+ hfield_found = hfield;
+ }
+ }
+
+ *hfield_r = hfield_found;
+ return (hfield_found == NULL ? 0 : 1);
+}
+
--- /dev/null
+#ifndef HTTP_HEADER_H
+#define HTTP_HEADER_H
+
+struct http_header;
+
+struct http_header_field {
+ const char *key; /* FIXME: rename to 'name' for v2.3 */
+ const char *value;
+ size_t size;
+};
+ARRAY_DEFINE_TYPE(http_header_field, struct http_header_field);
+
+static inline bool http_header_field_is(const struct http_header_field *hfield,
+ const char *name)
+{
+ return (strcasecmp(hfield->key, name) == 0);
+}
+
+struct http_header *
+http_header_create(pool_t pool, unsigned int init_count);
+
+const struct http_header_field *
+http_header_field_add(struct http_header *header,
+ const char *name, const unsigned char *data, size_t size);
+void http_header_field_delete(struct http_header *header, const char *name);
+
+const ARRAY_TYPE(http_header_field) *
+http_header_get_fields(const struct http_header *header) ATTR_PURE;
+
+const struct http_header_field *
+http_header_field_find(const struct http_header *header, const char *name)
+ ATTR_PURE;
+const char *
+http_header_field_get(const struct http_header *header, const char *name)
+ ATTR_PURE;
+int http_header_field_find_unique(const struct http_header *header,
+ const char *name, const struct http_header_field **hfield_r) ATTR_PURE;
+
+#endif
#include "array.h"
#include "istream.h"
#include "http-parser.h"
+#include "http-header.h"
#include "http-header-parser.h"
#include "http-date.h"
#include "http-transfer.h"
pool_ref(pool);
}
parser->msg.date = (time_t)-1;
- p_array_init(&parser->msg.headers, parser->msg.pool, 32);
+ parser->msg.header = http_header_create(parser->msg.pool, 32);
p_array_init(&parser->msg.connection_options, parser->msg.pool, 4);
}
}
static int
-http_message_parse_header(struct http_message_parser *parser, const char *name,
- const unsigned char *data, size_t size,
+http_message_parse_header(struct http_message_parser *parser,
+ const char *name, const unsigned char *data, size_t size,
const char **error_r)
{
- struct http_response_header *hdr;
+ const struct http_header_field *hdr;
struct http_parser hparser;
- void *value;
- hdr = array_append_space(&parser->msg.headers);
- hdr->key = p_strdup(parser->msg.pool, name);
- hdr->value = value = p_malloc(parser->msg.pool, size+1);
- memcpy(value, data, size);
- hdr->size = size;
+ hdr = http_header_field_add(parser->msg.header, name, data, size);
/* https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-23
Section 3.2.2:
int http_message_parse_headers(struct http_message_parser *parser,
const char **error_r)
{
- const char *field_name, *error;
const unsigned char *field_data;
+ const char *field_name, *error;
size_t field_size;
int ret;
/* *( header-field CRLF ) CRLF */
- while ((ret=http_header_parse_next_field
- (parser->header_parser, &field_name, &field_data, &field_size, &error)) > 0) {
+ while ((ret=http_header_parse_next_field(parser->header_parser,
+ &field_name, &field_data, &field_size, &error)) > 0) {
if (field_name == NULL) {
/* EOH */
return 1;
}
- if (http_message_parse_header(parser, field_name, field_data,
- field_size, error_r) < 0)
+
+ if (http_message_parse_header(parser,
+ field_name, field_data, field_size, error_r) < 0)
return -1;
}
handled as an error. A sender MUST remove the received Content-
Length field prior to forwarding such a message downstream.
*/
- if (parser->msg.have_content_length) {
- ARRAY_TYPE(http_response_header) *headers = &parser->msg.headers;
- const struct http_response_header *hdr;
+ if (parser->msg.have_content_length)
+ http_header_field_delete(parser->msg.header, "Content-Length");
- array_foreach(headers, hdr) {
- if (strcasecmp(hdr->key, "Content-Length") == 0) {
- array_delete(headers, array_foreach_idx(headers, hdr), 1);
- break;
- }
- }
- }
} else if (parser->msg.content_length > 0) {
/* Got explicit message size from Content-Length: header */
parser->payload =
#include "http-response.h"
#include "http-transfer.h"
+struct http_header;
+
struct http_message {
pool_t pool;
unsigned int version_major;
unsigned int version_minor;
- ARRAY_TYPE(http_response_header) headers;
- time_t date;
+ struct http_header *header;
+ time_t date;
uoff_t content_length;
const char *location;
ARRAY_TYPE(http_transfer_coding) transfer_encoding;
request->version_minor = parser->parser.msg.version_minor;
request->date = parser->parser.msg.date;
request->payload = parser->parser.payload;
- request->headers = parser->parser.msg.headers;
+ request->header = parser->parser.msg.header;
request->connection_options = parser->parser.msg.connection_options;
request->connection_close = parser->parser.msg.connection_close;
return 1;
#ifndef HTTP_REQUEST_PARSER_H
#define HTTP_REQUEST_PARSER_H
-#include "http-response.h"
-
-struct http_request {
- const char *method;
- const char *target;
-
- unsigned char version_major;
- unsigned char version_minor;
-
- time_t date;
- struct istream *payload;
-
- ARRAY_TYPE(http_response_header) headers;
- ARRAY_TYPE(const_string) connection_options;
-
- unsigned int connection_close:1;
-};
+#include "http-request.h"
struct http_request_parser *
http_request_parser_init(struct istream *input);
--- /dev/null
+#include "lib.h"
+#include "array.h"
+#include "istream.h"
+
+#include "http-request.h"
+
+bool http_request_has_connection_option(const struct http_request *req,
+ const char *option)
+{
+ const char *const *opt_idx;
+
+ array_foreach(&req->connection_options, opt_idx) {
+ if (strcasecmp(*opt_idx, option) == 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+int http_request_get_payload_size(const struct http_request *req,
+ uoff_t *size_r)
+{
+ if (req->payload == NULL) {
+ *size_r = 0;
+ return 1;
+ }
+
+ return i_stream_get_size(req->payload, TRUE, size_r);
+}
--- /dev/null
+#ifndef HTTP_REQUEST_H
+#define HTTP_REQUEST_H
+
+#include "http-header.h"
+
+struct http_request {
+ const char *method;
+
+ const char *target;
+
+ unsigned char version_major;
+ unsigned char version_minor;
+
+ time_t date;
+ const struct http_header *header;
+ struct istream *payload;
+
+ ARRAY_TYPE(const_string) connection_options;
+
+ unsigned int connection_close:1;
+};
+
+static inline bool
+http_request_method_is(const struct http_request *req, const char *method)
+{
+ if (req->method == NULL)
+ return FALSE;
+
+ return (strcmp(req->method, method) == 0);
+}
+
+static inline const struct http_header_field *
+http_request_header_find(const struct http_request *req, const char *name)
+{
+ return http_header_field_find(req->header, name);
+}
+
+static inline const char *
+http_request_header_get(const struct http_request *req, const char *name)
+{
+ return http_header_field_get(req->header, name);
+}
+
+static inline const ARRAY_TYPE(http_header_field) *
+http_request_header_get_fields(const struct http_request *req)
+{
+ return http_header_get_fields(req->header);
+}
+
+bool http_request_has_connection_option(const struct http_request *req,
+ const char *option);
+int http_request_get_payload_size(const struct http_request *req,
+ uoff_t *size_r);
+
+#endif
response->location = parser->parser.msg.location;
response->date = parser->parser.msg.date;
response->payload = parser->parser.payload;
- response->headers = parser->parser.msg.headers;
+ response->header = parser->parser.msg.header;
+ response->headers = *http_header_get_fields(response->header); /* FIXME: remove in v2.3 */
response->connection_options = parser->parser.msg.connection_options;
response->connection_close = parser->parser.msg.connection_close;
return 1;
--- /dev/null
+#include "lib.h"
+#include "array.h"
+#include "istream.h"
+
+#include "http-response.h"
+
+bool http_response_has_connection_option(const struct http_response *resp,
+ const char *option)
+{
+ const char *const *opt_idx;
+
+ if (!array_is_created(&resp->connection_options))
+ return FALSE;
+ array_foreach(&resp->connection_options, opt_idx) {
+ if (strcasecmp(*opt_idx, option) == 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+int http_response_get_payload_size(const struct http_response *resp,
+ uoff_t *size_r)
+{
+ if (resp->payload == NULL) {
+ *size_r = 0;
+ return 1;
+ }
+
+ return i_stream_get_size(resp->payload, TRUE, size_r);
+}
+
#ifndef HTTP_RESPONSE_H
#define HTTP_RESPONSE_H
-struct http_response_header {
- const char *key;
- const char *value;
- size_t size;
-};
-ARRAY_DEFINE_TYPE(http_response_header, struct http_response_header);
+#include "array.h"
+
+#include "http-header.h"
+
+#define http_response_header http_header_field /* FIXME: remove in v2.3 */
struct http_response {
unsigned char version_major;
const char *location;
time_t date;
+ const struct http_header *header;
struct istream *payload;
- ARRAY_TYPE(http_response_header) headers;
+ /* FIXME: remove in v2.3 */
+ ARRAY_TYPE(http_header_field) headers;
+
ARRAY_TYPE(const_string) connection_options;
unsigned int connection_close:1;
};
+static inline void
+http_response_init(struct http_response *resp,
+ unsigned int status, const char *reason)
+{
+ memset(resp, 0, sizeof(*resp));
+ resp->status = status;
+ resp->reason = reason;
+}
+
+static inline const struct http_header_field *
+http_response_header_find(const struct http_response *resp, const char *name)
+{
+ if (resp->header == NULL)
+ return NULL;
+ return http_header_field_find(resp->header, name);
+}
+
+static inline const char *
+http_response_header_get(const struct http_response *resp, const char *name)
+{
+ if (resp->header == NULL)
+ return NULL;
+ return http_header_field_get(resp->header, name);
+}
+
+static inline const ARRAY_TYPE(http_header_field) *
+http_response_header_get_fields(const struct http_response *resp)
+{
+ if (resp->header == NULL)
+ return NULL;
+ return http_header_get_fields(resp->header);
+}
+
+bool http_response_has_connection_option(const struct http_response *resp,
+ const char *option);
+int http_response_get_payload_size(const struct http_response *resp,
+ uoff_t *size_r);
+
#endif