]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
Implement http server using http_parser.
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Wed, 8 Jan 2014 17:32:24 +0000 (17:32 +0000)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Wed, 8 Jan 2014 17:32:24 +0000 (17:32 +0000)
CMakeLists.txt
lib/CMakeLists.txt
src/http.c [new file with mode: 0644]
src/http.h [new file with mode: 0644]
src/lua/lua_http.c
src/map.c
src/util.c
src/util.h

index ffbc2497406bb9289a04e2401d3856105a2857f7..b3038ba5cf65fdeeeb4fe391fcc3f7da3d30559b 100644 (file)
@@ -875,7 +875,8 @@ ENDIF(HG)
 INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src" 
                                        "${CMAKE_BINARY_DIR}/src" 
                                        "${CMAKE_SOURCE_DIR}/src/ucl/include"
-                                       "${CMAKE_SOURCE_DIR}/contrib/uthash")
+                                       "${CMAKE_SOURCE_DIR}/contrib/uthash"
+                                       "${CMAKE_SOURCE_DIR}/contrib/http-parser")
 
 SET(RSPAMDSRC  src/modules.c
                                src/controller.c
@@ -917,6 +918,7 @@ ADD_SUBDIRECTORY(src/lua)
 ADD_SUBDIRECTORY(src/json)
 ADD_SUBDIRECTORY(src/cdb)
 ADD_SUBDIRECTORY(src/ucl)
+ADD_SUBDIRECTORY(contrib/http-parser)
 ADD_SUBDIRECTORY(lib)
 
 ADD_SUBDIRECTORY(src/client)
index b236be14a3a238725164adce3ed538351c96992c..0f3cba7b4f3522bd21564b67319848e268c3e7eb 100644 (file)
@@ -8,6 +8,7 @@ SET(LIBRSPAMDUTILSRC                    ../src/aio_event.c
                                                                ../src/fstring.c
                                                                ../src/fuzzy.c
                                                                ../src/hash.c
+                                                               ../src/http.c
                                                                ../src/logger.c
                                                                ../src/map.c
                                                                ../src/memcached.c
@@ -73,6 +74,7 @@ ENDIF(CMAKE_COMPILER_IS_GNUCC)
 TARGET_LINK_LIBRARIES(rspamd-util ${RSPAMD_REQUIRED_LIBRARIES})
 TARGET_LINK_LIBRARIES(rspamd-util pcre)
 TARGET_LINK_LIBRARIES(rspamd-util rspamd-ucl)
+TARGET_LINK_LIBRARIES(rspamd-util rspamd-http-parser)
 TARGET_LINK_LIBRARIES(rspamd-util event)
 
 IF(NOT DEBIAN_BUILD)
diff --git a/src/http.c b/src/http.c
new file mode 100644 (file)
index 0000000..4d03642
--- /dev/null
@@ -0,0 +1,591 @@
+/* Copyright (c) 2014, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *       * Redistributions of source code must retain the above copyright
+ *         notice, this list of conditions and the following disclaimer.
+ *       * Redistributions in binary form must reproduce the above copyright
+ *         notice, this list of conditions and the following disclaimer in the
+ *         documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "http.h"
+#include "utlist.h"
+
+struct rspamd_http_server_private {
+       GString *buf;
+       gboolean new_header;
+       struct rspamd_http_header *header;
+       struct http_parser parser;
+       struct http_parser_settings parser_cb;
+       struct event ev;
+       struct timeval tv;
+       struct timeval *ptv;
+       gboolean in_body;
+       struct rspamd_http_request *req;
+};
+
+#define HTTP_ERROR http_error_quark ()
+GQuark
+http_error_quark (void)
+{
+       return g_quark_from_static_string ("http-error-quark");
+}
+
+static inline void
+rspamd_http_check_date (struct rspamd_http_server_private *priv)
+{
+       if (g_ascii_strcasecmp (priv->header->name->str, "date") == 0) {
+               priv->req->date = rspamd_http_parse_date (priv->header->value->str,
+                               priv->header->value->len);
+       }
+}
+
+static gint
+rspamd_http_on_url (http_parser* parser, const gchar *at, size_t length)
+{
+       struct rspamd_http_server *serv = (struct rspamd_http_server *)parser->data;
+       struct rspamd_http_server_private *priv;
+
+       priv = serv->priv;
+
+       g_string_append_len (priv->req->url, at, length);
+
+       return 0;
+}
+
+static gint
+rspamd_http_on_header_field (http_parser* parser, const gchar *at, size_t length)
+{
+       struct rspamd_http_server *serv = (struct rspamd_http_server *)parser->data;
+       struct rspamd_http_server_private *priv;
+
+       priv = serv->priv;
+
+       if (priv->header == NULL) {
+               priv->header = g_slice_alloc (sizeof (struct rspamd_http_header));
+               priv->header->name = g_string_sized_new (32);
+               priv->header->value = g_string_sized_new (32);
+       }
+       else if (priv->new_header) {
+               LL_PREPEND (priv->req->headers, priv->header);
+               rspamd_http_check_date (priv);
+               priv->header = g_slice_alloc (sizeof (struct rspamd_http_header));
+               priv->header->name = g_string_sized_new (32);
+               priv->header->value = g_string_sized_new (32);
+       }
+
+       priv->new_header = FALSE;
+       g_string_append_len (priv->header->name, at, length);
+
+       return 0;
+}
+
+static gint
+rspamd_http_on_header_value (http_parser* parser, const gchar *at, size_t length)
+{
+       struct rspamd_http_server *serv = (struct rspamd_http_server *)parser->data;
+       struct rspamd_http_server_private *priv;
+
+       priv = serv->priv;
+
+       if (priv->header == NULL) {
+               /* Should not happen */
+               return -1;
+       }
+
+       priv->new_header = TRUE;
+       g_string_append_len (priv->header->value, at, length);
+
+       return 0;
+}
+
+static int
+rspamd_http_on_headers_complete (http_parser* parser)
+{
+       struct rspamd_http_server *serv = (struct rspamd_http_server *)parser->data;
+       struct rspamd_http_server_private *priv;
+
+       priv = serv->priv;
+
+       if (priv->header != NULL) {
+               LL_PREPEND (priv->req->headers, priv->header);
+               rspamd_http_check_date (priv);
+               priv->header = NULL;
+       }
+
+       priv->in_body = TRUE;
+       if (parser->content_length != 0 && parser->content_length != ULLONG_MAX) {
+               priv->req->body = g_string_sized_new (parser->content_length);
+       }
+       else {
+               priv->req->body = g_string_sized_new (BUFSIZ);
+       }
+
+       return 0;
+}
+
+static int
+rspamd_http_on_body (http_parser* parser, const gchar *at, size_t length)
+{
+       struct rspamd_http_server *serv = (struct rspamd_http_server *)parser->data;
+
+       if (serv->opts & RSPAMD_HTTP_BODY_PARTIAL) {
+               return (serv->body_handler (serv, serv->priv->req, at, length));
+       }
+
+       return 0;
+}
+
+static int
+rspamd_http_on_message_complete (http_parser* parser)
+{
+       struct rspamd_http_server *serv = (struct rspamd_http_server *)parser->data;
+       struct rspamd_http_server_private *priv;
+       int ret;
+
+       priv = serv->priv;
+
+       if (serv->opts & RSPAMD_HTTP_BODY_PARTIAL) {
+               ret = serv->body_handler (serv, priv->req, NULL, 0);
+       }
+       else {
+               ret = serv->body_handler (serv, priv->req, priv->req->body->str, priv->req->body->len);
+       }
+
+       return ret;
+}
+
+static void
+rspamd_http_event_handler (int fd, short what, gpointer ud)
+{
+       struct rspamd_http_server *serv = (struct rspamd_http_server *)ud;
+       struct rspamd_http_server_private *priv;
+       GString *buf;
+       gchar *start;
+       gssize r;
+       gint64 remain;
+       GError *err;
+
+       priv = serv->priv;
+       if (priv->in_body) {
+               buf = priv->req->body;
+       }
+       else {
+               priv->buf->len = 0;
+               buf = priv->buf;
+       }
+
+       remain = buf->allocated_len - buf->len;
+       if (remain <= 0) {
+               /* Expand string */
+               g_string_set_size (buf, buf->allocated_len * 2);
+               remain = buf->allocated_len - buf->len;
+       }
+       start = buf->str + buf->len;
+       r = read (fd, start, remain);
+       if (r == -1) {
+               err = g_error_new (HTTP_ERROR, errno, "IO read error: %s", strerror (errno));
+               serv->error_handler (serv, err);
+               g_error_free (err);
+       }
+       else {
+               buf->len += r;
+               if (http_parser_execute (&priv->parser, &priv->parser_cb, start, r) != (size_t)r) {
+                       err = g_error_new (HTTP_ERROR, priv->parser.http_errno,
+                                       "HTTP parser error: %s", http_errno_description (priv->parser.http_errno));
+                       serv->error_handler (serv, err);
+                       g_error_free (err);
+               }
+       }
+}
+
+struct rspamd_http_server*
+rspamd_http_server_new (rspamd_http_body_handler body_handler,
+               rspamd_http_error_handler error_handler, enum rspamd_http_options opts)
+{
+       struct rspamd_http_server *new;
+       struct rspamd_http_server_private *priv;
+
+       new = g_slice_alloc0 (sizeof (struct rspamd_http_server));
+       new->opts = opts;
+       new->body_handler = body_handler;
+       new->error_handler = error_handler;
+       new->fd = -1;
+
+       /* Init priv */
+       priv = g_slice_alloc0 (sizeof (struct rspamd_http_server_private));
+       http_parser_init (&priv->parser, HTTP_REQUEST);
+       priv->parser.data = new;
+       priv->parser_cb.on_url = rspamd_http_on_url;
+       priv->parser_cb.on_header_field = rspamd_http_on_header_field;
+       priv->parser_cb.on_header_value = rspamd_http_on_header_value;
+       priv->parser_cb.on_headers_complete = rspamd_http_on_headers_complete;
+       priv->parser_cb.on_body = rspamd_http_on_body;
+       priv->parser_cb.on_message_complete = rspamd_http_on_message_complete;
+
+       new->priv = priv;
+
+       return new;
+}
+
+void
+rspamd_http_server_reset (struct rspamd_http_server *server)
+{
+       struct rspamd_http_server_private *priv;
+       struct rspamd_http_request *req;
+       struct rspamd_http_header *hdr, *tmp_hdr;
+
+       priv = server->priv;
+       req = priv->req;
+
+       /* Clear request */
+       if (req != NULL) {
+               LL_FOREACH_SAFE(req->headers, hdr, tmp_hdr) {
+                       g_string_free (hdr->name, TRUE);
+                       g_string_free (hdr->value, TRUE);
+                       g_slice_free1 (sizeof (struct rspamd_http_header), hdr);
+               }
+               g_string_free (req->body, TRUE);
+               g_string_free (req->url, TRUE);
+               g_slice_free1 (sizeof (struct rspamd_http_request), req);
+               priv->req = NULL;
+       }
+
+       /* Clear priv */
+       event_del (&priv->ev);
+       if (priv->buf != NULL) {
+               g_string_free (priv->buf, TRUE);
+               priv->buf = NULL;
+       }
+
+       /* Clear server itself */
+       if (server->fd != -1) {
+               close (server->fd);
+       }
+}
+
+void
+rspamd_http_server_free (struct rspamd_http_server *server)
+{
+       struct rspamd_http_server_private *priv;
+
+       priv = server->priv;
+       rspamd_http_server_reset (server);
+       g_slice_free1 (sizeof (struct rspamd_http_server_private), priv);
+       g_slice_free1 (sizeof (struct rspamd_http_server), server);
+}
+
+void
+rspamd_http_server_handle_request (struct rspamd_http_server *server,
+               gpointer ud, gint fd, struct timeval *timeout, struct event_base *base)
+{
+       struct rspamd_http_server_private *priv = server->priv;
+       struct rspamd_http_request *req;
+
+       server->fd = fd;
+       server->ud = ud;
+       req = g_slice_alloc (sizeof (struct rspamd_http_request));
+       req->url = g_string_sized_new (32);
+       req->headers = NULL;
+       req->date = 0;
+       priv->req = req;
+
+       if (timeout == NULL) {
+               priv->ptv = NULL;
+       }
+       else {
+               memcpy (&priv->tv, timeout, sizeof (struct timeval));
+               priv->ptv = &priv->tv;
+       }
+       priv->header = NULL;
+       priv->buf = g_string_sized_new (BUFSIZ);
+       priv->in_body = FALSE;
+       priv->new_header = TRUE;
+
+       event_set (&priv->ev, fd, EV_READ | EV_PERSIST, rspamd_http_event_handler, server);
+       event_base_set (base, &priv->ev);
+       event_add (&priv->ev, priv->ptv);
+}
+
+
+/*
+ * Obtained from nginx
+ * Copyright (C) Igor Sysoev
+ */
+static guint mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+time_t
+rspamd_http_parse_date (const gchar *header, gsize len)
+{
+       const gchar *p, *end;
+       gint month;
+       guint day, year, hour, min, sec;
+       guint64 time;
+       enum {
+               no = 0, rfc822, /* Tue, 10 Nov 2002 23:50:13   */
+               rfc850, /* Tuesday, 10-Dec-02 23:50:13 */
+               isoc /* Tue Dec 10 23:50:13 2002    */
+       } fmt;
+
+       fmt = 0;
+       if (len > 0) {
+               end = header + len;
+       }
+       else {
+               end = header + strlen (header);
+       }
+
+#if (NGX_SUPPRESS_WARN)
+       day = 32;
+       year = 2038;
+#endif
+
+       for (p = header; p < end; p++) {
+               if (*p == ',') {
+                       break;
+               }
+
+               if (*p == ' ') {
+                       fmt = isoc;
+                       break;
+               }
+       }
+
+       for (p++; p < end; p++)
+               if (*p != ' ') {
+                       break;
+               }
+
+       if (end - p < 18) {
+               return (time_t)-1;
+       }
+
+       if (fmt != isoc) {
+               if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
+                       return (time_t)-1;
+               }
+
+               day = (*p - '0') * 10 + *(p + 1) - '0';
+               p += 2;
+
+               if (*p == ' ') {
+                       if (end - p < 18) {
+                               return (time_t)-1;
+                       }
+                       fmt = rfc822;
+
+               }
+               else if (*p == '-') {
+                       fmt = rfc850;
+
+               }
+               else {
+                       return (time_t)-1;
+               }
+
+               p++;
+       }
+
+       switch (*p) {
+
+       case 'J':
+               month = *(p + 1) == 'a' ? 0 : *(p + 2) == 'n' ? 5 : 6;
+               break;
+
+       case 'F':
+               month = 1;
+               break;
+
+       case 'M':
+               month = *(p + 2) == 'r' ? 2 : 4;
+               break;
+
+       case 'A':
+               month = *(p + 1) == 'p' ? 3 : 7;
+               break;
+
+       case 'S':
+               month = 8;
+               break;
+
+       case 'O':
+               month = 9;
+               break;
+
+       case 'N':
+               month = 10;
+               break;
+
+       case 'D':
+               month = 11;
+               break;
+
+       default:
+               return (time_t)-1;
+       }
+
+       p += 3;
+
+       if ((fmt == rfc822 && *p != ' ') || (fmt == rfc850 && *p != '-')) {
+               return (time_t)-1;
+       }
+
+       p++;
+
+       if (fmt == rfc822) {
+               if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9'
+               || *(p + 2) < '0' || *(p + 2) > '9' || *(p + 3) < '0'
+               || *(p + 3) > '9') {
+                       return (time_t)-1;
+               }
+
+               year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100
+                               + (*(p + 2) - '0') * 10 + *(p + 3) - '0';
+               p += 4;
+
+       }
+       else if (fmt == rfc850) {
+               if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
+                       return (time_t)-1;
+               }
+
+               year = (*p - '0') * 10 + *(p + 1) - '0';
+               year += (year < 70) ? 2000 : 1900;
+               p += 2;
+       }
+
+       if (fmt == isoc) {
+               if (*p == ' ') {
+                       p++;
+               }
+
+               if (*p < '0' || *p > '9') {
+                       return (time_t)-1;
+               }
+
+               day = *p++ - '0';
+
+               if (*p != ' ') {
+                       if (*p < '0' || *p > '9') {
+                               return (time_t)-1;
+                       }
+
+                       day = day * 10 + *p++ - '0';
+               }
+
+               if (end - p < 14) {
+                       return (time_t)-1;
+               }
+       }
+
+       if (*p++ != ' ') {
+               return (time_t)-1;
+       }
+
+       if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
+               return (time_t)-1;
+       }
+
+       hour = (*p - '0') * 10 + *(p + 1) - '0';
+       p += 2;
+
+       if (*p++ != ':') {
+               return (time_t)-1;
+       }
+
+       if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
+               return (time_t)-1;
+       }
+
+       min = (*p - '0') * 10 + *(p + 1) - '0';
+       p += 2;
+
+       if (*p++ != ':') {
+               return (time_t)-1;
+       }
+
+       if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
+               return (time_t)-1;
+       }
+
+       sec = (*p - '0') * 10 + *(p + 1) - '0';
+
+       if (fmt == isoc) {
+               p += 2;
+
+               if (*p++ != ' ') {
+                       return (time_t)-1;
+               }
+
+               if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9'
+               || *(p + 2) < '0' || *(p + 2) > '9' || *(p + 3) < '0'
+               || *(p + 3) > '9') {
+                       return (time_t)-1;
+               }
+
+               year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100
+                               + (*(p + 2) - '0') * 10 + *(p + 3) - '0';
+       }
+
+       if (hour > 23 || min > 59 || sec > 59) {
+               return (time_t)-1;
+       }
+
+       if (day == 29 && month == 1) {
+               if ((year & 3) || ((year % 100 == 0) && (year % 400) != 0)) {
+                       return (time_t)-1;
+               }
+
+       }
+       else if (day > mday[month]) {
+               return (time_t)-1;
+       }
+
+       /*
+        * shift new year to March 1 and start months from 1 (not 0),
+        * it is needed for Gauss' formula
+        */
+
+       if (--month <= 0) {
+               month += 12;
+               year -= 1;
+       }
+
+       /* Gauss' formula for Gregorian days since March 1, 1 BC */
+
+       time = (guint64) (
+                       /* days in years including leap years since March 1, 1 BC */
+
+                       365 * year + year / 4 - year / 100 + year / 400
+
+                       /* days before the month */
+
+                       + 367 * month / 12 - 30
+
+                       /* days before the day */
+
+                       + day - 1
+
+                       /*
+                        * 719527 days were between March 1, 1 BC and March 1, 1970,
+                        * 31 and 28 days were in January and February 1970
+                        */
+
+                       - 719527 + 31 + 28) * 86400 + hour * 3600 + min * 60 + sec;
+
+       return (time_t) time;
+}
diff --git a/src/http.h b/src/http.h
new file mode 100644 (file)
index 0000000..8506097
--- /dev/null
@@ -0,0 +1,164 @@
+/* Copyright (c) 2014, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *       * Redistributions of source code must retain the above copyright
+ *         notice, this list of conditions and the following disclaimer.
+ *       * Redistributions in binary form must reproduce the above copyright
+ *         notice, this list of conditions and the following disclaimer in the
+ *         documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef HTTP_H_
+#define HTTP_H_
+
+/**
+ * @file http.h
+ *
+ * This is an interface for HTTP client and server. This code uses HTTP parser written
+ * by Joyent Inc based on nginx code.
+ */
+
+#include "config.h"
+#include "http_parser.h"
+
+/**
+ * HTTP header structure
+ */
+struct rspamd_http_header {
+       GString *name;
+       GString *value;
+       struct rspamd_http_header *next;
+};
+
+/**
+ * HTTP request structure, used for requests
+ */
+struct rspamd_http_request {
+       GString *url;
+       struct rspamd_http_header *headers;
+       GString *body;
+       time_t date;
+       gint code;
+};
+
+struct rspamd_http_reply {
+       struct rspamd_http_header *headers;
+       GString *body;
+       gint code;
+};
+
+/**
+ * Options for HTTP client and server
+ */
+enum rspamd_http_options {
+       RSPAMD_HTTP_BODY_PARTIAL = 0x1//!< RSPAMD_HTTP_BODY_PARTIAL
+};
+
+struct rspamd_http_server_private;
+struct rspamd_http_server;
+
+typedef gboolean (*rspamd_http_body_handler) (struct rspamd_http_server *srv,
+               struct rspamd_http_request *req,
+               const gchar *chunk,
+               gsize len);
+
+typedef void (*rspamd_http_error_handler) (struct rspamd_http_server *srv, GError *err);
+
+typedef void (*rspamd_http_reply_handler) (struct rspamd_http_server *srv,
+               struct rspamd_http_reply *reply, GError *err);
+
+/**
+ * HTTP server structure
+ */
+struct rspamd_http_server {
+       gint fd;
+       struct rspamd_http_server_private *priv;
+       enum rspamd_http_options opts;
+       rspamd_http_body_handler body_handler;
+       rspamd_http_error_handler error_handler;
+       gpointer ud;
+};
+
+/**
+ * Create new http server
+ * @param handler handler for body
+ * @param opts options
+ * @return new server structure
+ */
+struct rspamd_http_server* rspamd_http_server_new (rspamd_http_body_handler body_handler,
+               rspamd_http_error_handler error_handler,
+               enum rspamd_http_options opts);
+
+/**
+ * Handle a request using socket fd and user data ud
+ * @param server server structure
+ * @param ud opaque user data
+ * @param fd fd to read/write
+ */
+void rspamd_http_server_handle_request (struct rspamd_http_server *server, gpointer ud, gint fd,
+               struct timeval *timeout, struct event_base *base);
+
+/**
+ * Send reply using initialised server
+ * @param server server structure
+ * @param reply HTTP reply
+ * @return TRUE if request can be sent
+ */
+gboolean rspamd_http_server_write_reply (struct rspamd_http_server *server, struct rspamd_http_reply *reply,
+               rspamd_http_reply_handler *handler);
+
+/**
+ * Free server structure
+ * @param server
+ */
+void rspamd_http_server_free (struct rspamd_http_server *server);
+
+/**
+ * Reset server for a new request
+ * @param server
+ */
+void rspamd_http_server_reset (struct rspamd_http_server *server);
+
+/**
+ * Create new HTTP reply
+ * @param code code to pass
+ * @return new reply object
+ */
+struct rspamd_http_reply * rspamd_http_new_reply (gint code);
+
+/**
+ * Append a header to reply
+ * @param rep
+ * @param name
+ * @param value
+ */
+void rspamd_http_reply_add_header (struct rspamd_http_reply *rep, const gchar *name, const gchar *value);
+
+/**
+ * Free HTTP reply
+ * @param rep
+ */
+void rspamd_http_reply_free (struct rspamd_http_reply *rep);
+
+/**
+ * Parse HTTP date header and return it as time_t
+ * @param header HTTP date header
+ * @param len length of header
+ * @return time_t or (time_t)-1 in case of error
+ */
+time_t rspamd_http_parse_date (const gchar *header, gsize len);
+
+#endif /* HTTP_H_ */
index f83c5f6f42eb419d6feeb9019310f9bb126b2535..26f699b785cd1c57b686985bf61296bc640335b8 100644 (file)
@@ -24,6 +24,7 @@
 #include "lua_common.h"
 #include "buffer.h"
 #include "dns.h"
+#include "http.h"
 
 #define MAX_HEADERS_SIZE 8192
 
@@ -229,7 +230,7 @@ lua_http_parse_header_line (struct lua_http_ud *ud, f_str_t *in)
 
        /* Check date */
        if (g_ascii_strcasecmp (new->name, "date") == 0) {
-               ud->date = parse_http_date (new->value, -1);
+               ud->date = rspamd_http_parse_date (new->value, -1);
        }
 
        /* Insert a header to the list */
index ea5b1a35a534fe78003980db7b6c70d996d384f6..9b17bd05edb729f675d2468c460123ba2e01f537 100644 (file)
--- a/src/map.c
+++ b/src/map.c
@@ -27,6 +27,7 @@
  */
 #include "config.h"
 #include "map.h"
+#include "http.h"
 #include "main.h"
 #include "util.h"
 #include "mem_pool.h"
@@ -349,7 +350,7 @@ read_http_common (struct rspamd_map *map, struct http_map_data *data, struct htt
                        /* Check for date */
                        date = g_hash_table_lookup (reply->headers, "Date");
                        if (date != NULL) {
-                               data->last_checked = parse_http_date (date, -1);
+                               data->last_checked = rspamd_http_parse_date (date, -1);
                        }
                        else {
                                data->last_checked = (time_t)-1;
index 14af4b2bd739e196c49888ee97c26aecd1dde9ee..938bb66cc0674074098f990046ba7f622ce09580 100644 (file)
@@ -2013,274 +2013,6 @@ parse_ipmask_v4 (const char *line, struct in_addr *ina, int *mask)
        return TRUE;
 }
 
-/*
- * Obtained from nginx
- * Copyright (C) Igor Sysoev
- */
-static guint mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
-
-time_t
-parse_http_date (const gchar *header, gsize len)
-{
-       const gchar *p, *end;
-       gint month;
-       guint day, year, hour, min, sec;
-       guint64 time;
-       enum {
-               no = 0, rfc822, /* Tue, 10 Nov 2002 23:50:13   */
-               rfc850, /* Tuesday, 10-Dec-02 23:50:13 */
-               isoc /* Tue Dec 10 23:50:13 2002    */
-       } fmt;
-
-       fmt = 0;
-       if (len > 0) {
-               end = header + len;
-       }
-       else {
-               end = header + strlen (header);
-       }
-
-#if (NGX_SUPPRESS_WARN)
-       day = 32;
-       year = 2038;
-#endif
-
-       for (p = header; p < end; p++) {
-               if (*p == ',') {
-                       break;
-               }
-
-               if (*p == ' ') {
-                       fmt = isoc;
-                       break;
-               }
-       }
-
-       for (p++; p < end; p++)
-               if (*p != ' ') {
-                       break;
-               }
-
-       if (end - p < 18) {
-               return (time_t)-1;
-       }
-
-       if (fmt != isoc) {
-               if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
-                       return (time_t)-1;
-               }
-
-               day = (*p - '0') * 10 + *(p + 1) - '0';
-               p += 2;
-
-               if (*p == ' ') {
-                       if (end - p < 18) {
-                               return (time_t)-1;
-                       }
-                       fmt = rfc822;
-
-               }
-               else if (*p == '-') {
-                       fmt = rfc850;
-
-               }
-               else {
-                       return (time_t)-1;
-               }
-
-               p++;
-       }
-
-       switch (*p) {
-
-       case 'J':
-               month = *(p + 1) == 'a' ? 0 : *(p + 2) == 'n' ? 5 : 6;
-               break;
-
-       case 'F':
-               month = 1;
-               break;
-
-       case 'M':
-               month = *(p + 2) == 'r' ? 2 : 4;
-               break;
-
-       case 'A':
-               month = *(p + 1) == 'p' ? 3 : 7;
-               break;
-
-       case 'S':
-               month = 8;
-               break;
-
-       case 'O':
-               month = 9;
-               break;
-
-       case 'N':
-               month = 10;
-               break;
-
-       case 'D':
-               month = 11;
-               break;
-
-       default:
-               return (time_t)-1;
-       }
-
-       p += 3;
-
-       if ((fmt == rfc822 && *p != ' ') || (fmt == rfc850 && *p != '-')) {
-               return (time_t)-1;
-       }
-
-       p++;
-
-       if (fmt == rfc822) {
-               if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9'
-               || *(p + 2) < '0' || *(p + 2) > '9' || *(p + 3) < '0'
-               || *(p + 3) > '9') {
-                       return (time_t)-1;
-               }
-
-               year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100
-                               + (*(p + 2) - '0') * 10 + *(p + 3) - '0';
-               p += 4;
-
-       }
-       else if (fmt == rfc850) {
-               if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
-                       return (time_t)-1;
-               }
-
-               year = (*p - '0') * 10 + *(p + 1) - '0';
-               year += (year < 70) ? 2000 : 1900;
-               p += 2;
-       }
-
-       if (fmt == isoc) {
-               if (*p == ' ') {
-                       p++;
-               }
-
-               if (*p < '0' || *p > '9') {
-                       return (time_t)-1;
-               }
-
-               day = *p++ - '0';
-
-               if (*p != ' ') {
-                       if (*p < '0' || *p > '9') {
-                               return (time_t)-1;
-                       }
-
-                       day = day * 10 + *p++ - '0';
-               }
-
-               if (end - p < 14) {
-                       return (time_t)-1;
-               }
-       }
-
-       if (*p++ != ' ') {
-               return (time_t)-1;
-       }
-
-       if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
-               return (time_t)-1;
-       }
-
-       hour = (*p - '0') * 10 + *(p + 1) - '0';
-       p += 2;
-
-       if (*p++ != ':') {
-               return (time_t)-1;
-       }
-
-       if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
-               return (time_t)-1;
-       }
-
-       min = (*p - '0') * 10 + *(p + 1) - '0';
-       p += 2;
-
-       if (*p++ != ':') {
-               return (time_t)-1;
-       }
-
-       if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
-               return (time_t)-1;
-       }
-
-       sec = (*p - '0') * 10 + *(p + 1) - '0';
-
-       if (fmt == isoc) {
-               p += 2;
-
-               if (*p++ != ' ') {
-                       return (time_t)-1;
-               }
-
-               if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9'
-               || *(p + 2) < '0' || *(p + 2) > '9' || *(p + 3) < '0'
-               || *(p + 3) > '9') {
-                       return (time_t)-1;
-               }
-
-               year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100
-                               + (*(p + 2) - '0') * 10 + *(p + 3) - '0';
-       }
-
-       if (hour > 23 || min > 59 || sec > 59) {
-               return (time_t)-1;
-       }
-
-       if (day == 29 && month == 1) {
-               if ((year & 3) || ((year % 100 == 0) && (year % 400) != 0)) {
-                       return (time_t)-1;
-               }
-
-       }
-       else if (day > mday[month]) {
-               return (time_t)-1;
-       }
-
-       /*
-        * shift new year to March 1 and start months from 1 (not 0),
-        * it is needed for Gauss' formula
-        */
-
-       if (--month <= 0) {
-               month += 12;
-               year -= 1;
-       }
-
-       /* Gauss' formula for Gregorian days since March 1, 1 BC */
-
-       time = (guint64) (
-                       /* days in years including leap years since March 1, 1 BC */
-
-                       365 * year + year / 4 - year / 100 + year / 400
-
-                       /* days before the month */
-
-                       + 367 * month / 12 - 30
-
-                       /* days before the day */
-
-                       + day - 1
-
-                       /*
-                        * 719527 days were between March 1, 1 BC and March 1, 1970,
-                        * 31 and 28 days were in January and February 1970
-                        */
-
-                       - 719527 + 31 + 28) * 86400 + hour * 3600 + min * 60 + sec;
-
-       return (time_t) time;
-}
-
 static volatile sig_atomic_t saved_signo[NSIG];
 
 static
index 9109819865c0ea7b6990fa066b4b52f5cf88765a..2951942e37a35cfb9293fc175464c9772461e5fd 100644 (file)
@@ -432,14 +432,6 @@ gpointer rspamd_str_pool_copy (gconstpointer data, gpointer ud);
  */
 gboolean parse_ipmask_v4 (const char *line, struct in_addr *ina, int *mask);
 
-/**
- * Parse HTTP date header and return it as time_t
- * @param header HTTP date header
- * @param len length of header
- * @return time_t or (time_t)-1 in case of error
- */
-time_t parse_http_date (const gchar *header, gsize len);
-
 /**
  * Read passphrase from tty
  * @param buf buffer to fill with a password