From: Stephan Bosch Date: Sun, 15 Sep 2013 00:44:42 +0000 (+0300) Subject: lib-http: Unified http-request.h and http-response.h headers. X-Git-Tag: 2.2.6~80 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e8f1e510df3ab051a816715c2056f0d10aee929e;p=thirdparty%2Fdovecot%2Fcore.git lib-http: Unified http-request.h and http-response.h headers. Renamed struct http_response_header to struct http_header_field, encapsulated the array in struct http_header and put it all in http-header.h/c Added inline utility functions for header querying and getting response/request payload size. --- diff --git a/src/lib-http/Makefile.am b/src/lib-http/Makefile.am index d0ae216421..dc267161ee 100644 --- a/src/lib-http/Makefile.am +++ b/src/lib-http/Makefile.am @@ -10,10 +10,13 @@ libhttp_la_SOURCES = \ 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 \ @@ -25,9 +28,11 @@ headers = \ 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 \ @@ -84,6 +89,7 @@ test_http_response_parser_SOURCES = test-http-response-parser.c 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 \ diff --git a/src/lib-http/http-client-request.c b/src/lib-http/http-client-request.c index bcc1decf19..1289b6f7ed 100644 --- a/src/lib-http/http-client-request.c +++ b/src/lib-http/http-client-request.c @@ -513,9 +513,7 @@ http_client_request_send_error(struct http_client_request *req, 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); } } diff --git a/src/lib-http/http-header.c b/src/lib-http/http-header.c new file mode 100644 index 0000000000..e620790b4e --- /dev/null +++ b/src/lib-http/http-header.c @@ -0,0 +1,98 @@ +/* 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); +} + diff --git a/src/lib-http/http-header.h b/src/lib-http/http-header.h new file mode 100644 index 0000000000..91b8ebe06a --- /dev/null +++ b/src/lib-http/http-header.h @@ -0,0 +1,39 @@ +#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 diff --git a/src/lib-http/http-message-parser.c b/src/lib-http/http-message-parser.c index c0996c138f..270e841c62 100644 --- a/src/lib-http/http-message-parser.c +++ b/src/lib-http/http-message-parser.c @@ -4,6 +4,7 @@ #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" @@ -48,7 +49,7 @@ void http_message_parser_restart(struct http_message_parser *parser, 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); } @@ -93,19 +94,14 @@ int http_message_parse_finish_payload(struct http_message_parser *parser, } 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: @@ -284,20 +280,21 @@ http_message_parse_header(struct http_message_parser *parser, const char *name, 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; } @@ -378,17 +375,9 @@ int http_message_parse_body(struct http_message_parser *parser, bool request, 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 = diff --git a/src/lib-http/http-message-parser.h b/src/lib-http/http-message-parser.h index 1b41dd34e4..79e90a2a2c 100644 --- a/src/lib-http/http-message-parser.h +++ b/src/lib-http/http-message-parser.h @@ -4,15 +4,17 @@ #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; diff --git a/src/lib-http/http-request-parser.c b/src/lib-http/http-request-parser.c index cb3fb546a6..c9f5c39dae 100644 --- a/src/lib-http/http-request-parser.c +++ b/src/lib-http/http-request-parser.c @@ -281,7 +281,7 @@ int http_request_parse_next(struct http_request_parser *parser, 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; diff --git a/src/lib-http/http-request-parser.h b/src/lib-http/http-request-parser.h index 676e1570c8..450aafaf61 100644 --- a/src/lib-http/http-request-parser.h +++ b/src/lib-http/http-request-parser.h @@ -1,23 +1,7 @@ #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); diff --git a/src/lib-http/http-request.c b/src/lib-http/http-request.c new file mode 100644 index 0000000000..51d8c0841a --- /dev/null +++ b/src/lib-http/http-request.c @@ -0,0 +1,28 @@ +#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); +} diff --git a/src/lib-http/http-request.h b/src/lib-http/http-request.h new file mode 100644 index 0000000000..5f1d064ccc --- /dev/null +++ b/src/lib-http/http-request.h @@ -0,0 +1,55 @@ +#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 diff --git a/src/lib-http/http-response-parser.c b/src/lib-http/http-response-parser.c index 77b7fddf7d..a088980ae2 100644 --- a/src/lib-http/http-response-parser.c +++ b/src/lib-http/http-response-parser.c @@ -292,7 +292,8 @@ int http_response_parse_next(struct http_response_parser *parser, 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; diff --git a/src/lib-http/http-response.c b/src/lib-http/http-response.c new file mode 100644 index 0000000000..abafb865e0 --- /dev/null +++ b/src/lib-http/http-response.c @@ -0,0 +1,31 @@ +#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); +} + diff --git a/src/lib-http/http-response.h b/src/lib-http/http-response.h index 16002ae4e3..80fce334b4 100644 --- a/src/lib-http/http-response.h +++ b/src/lib-http/http-response.h @@ -1,12 +1,11 @@ #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; @@ -18,12 +17,53 @@ struct http_response { 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