]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-http: Unified http-request.h and http-response.h headers.
authorStephan Bosch <stephan@rename-it.nl>
Sun, 15 Sep 2013 00:44:42 +0000 (03:44 +0300)
committerStephan Bosch <stephan@rename-it.nl>
Sun, 15 Sep 2013 00:44:42 +0000 (03:44 +0300)
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.

13 files changed:
src/lib-http/Makefile.am
src/lib-http/http-client-request.c
src/lib-http/http-header.c [new file with mode: 0644]
src/lib-http/http-header.h [new file with mode: 0644]
src/lib-http/http-message-parser.c
src/lib-http/http-message-parser.h
src/lib-http/http-request-parser.c
src/lib-http/http-request-parser.h
src/lib-http/http-request.c [new file with mode: 0644]
src/lib-http/http-request.h [new file with mode: 0644]
src/lib-http/http-response-parser.c
src/lib-http/http-response.c [new file with mode: 0644]
src/lib-http/http-response.h

index d0ae216421f0f70ec737f022336ef6b98bc623dd..dc267161ee0b563cf0079307faa49025a0887d21 100644 (file)
@@ -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 \
index bcc1decf1947cd867b00a7cfcf14c5ea5ebd5140..1289b6f7ede4f619097e40ec38919c3587b1b5c3 100644 (file)
@@ -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 (file)
index 0000000..e620790
--- /dev/null
@@ -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 (file)
index 0000000..91b8ebe
--- /dev/null
@@ -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
index c0996c138f9dfae81e2c7282e79cfc35c8464480..270e841c62a6a14845c365b474e59ab0cc226528 100644 (file)
@@ -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 =
index 1b41dd34e4c5a7c0401ce06ba264160115cc1b52..79e90a2a2c228e632e45c7311e3efbaa02eb47b3 100644 (file)
@@ -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;
index cb3fb546a6ff308a156687547c9925c3e371005a..c9f5c39daec85b8c364fb37c7890b068e415d705 100644 (file)
@@ -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;
index 676e1570c8b6f74afeb3e645b01fd797df9cbec1..450aafaf61fe7ff993af1783e72867aff58c2287 100644 (file)
@@ -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 (file)
index 0000000..51d8c08
--- /dev/null
@@ -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 (file)
index 0000000..5f1d064
--- /dev/null
@@ -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
index 77b7fddf7de9dcc1df9b35e483931c98b30a2904..a088980ae2c24d35b7a063d97fc95fe02e8b73ff 100644 (file)
@@ -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 (file)
index 0000000..abafb86
--- /dev/null
@@ -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);
+}
+
index 16002ae4e3d218bb7a80aeb3591801f1291431a5..80fce334b4b176d3526b5562c5a3abae1417c015 100644 (file)
@@ -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