aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2014-01-08 17:32:24 +0000
committerVsevolod Stakhov <vsevolod@highsecure.ru>2014-01-08 17:32:24 +0000
commit4e4ae88f0e2093f0a354e396169694d40eb08d6d (patch)
treec0f62f49a1843c88bac671c713dc3fe2c2eb0409
parent0894d44904c188cc1e62abe147267e593fdb369c (diff)
downloadrspamd-4e4ae88f0e2093f0a354e396169694d40eb08d6d.tar.gz
rspamd-4e4ae88f0e2093f0a354e396169694d40eb08d6d.zip
Implement http server using http_parser.
-rw-r--r--CMakeLists.txt4
-rw-r--r--lib/CMakeLists.txt2
-rw-r--r--src/http.c591
-rw-r--r--src/http.h164
-rw-r--r--src/lua/lua_http.c3
-rw-r--r--src/map.c3
-rw-r--r--src/util.c268
-rw-r--r--src/util.h8
8 files changed, 764 insertions, 279 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ffbc24974..b3038ba5c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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)
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index b236be14a..0f3cba7b4 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -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
index 000000000..4d036428b
--- /dev/null
+++ b/src/http.c
@@ -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
index 000000000..8506097a5
--- /dev/null
+++ b/src/http.h
@@ -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_ */
diff --git a/src/lua/lua_http.c b/src/lua/lua_http.c
index f83c5f6f4..26f699b78 100644
--- a/src/lua/lua_http.c
+++ b/src/lua/lua_http.c
@@ -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 */
diff --git a/src/map.c b/src/map.c
index ea5b1a35a..9b17bd05e 100644
--- 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;
diff --git a/src/util.c b/src/util.c
index 14af4b2bd..938bb66cc 100644
--- a/src/util.c
+++ b/src/util.c
@@ -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
diff --git a/src/util.h b/src/util.h
index 910981986..2951942e3 100644
--- a/src/util.h
+++ b/src/util.h
@@ -433,14 +433,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
* @param size size of the buffer