]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
Unify http code for client and server.
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Thu, 9 Jan 2014 18:44:34 +0000 (18:44 +0000)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Thu, 9 Jan 2014 18:44:34 +0000 (18:44 +0000)
src/cfg_utils.c
src/http.c
src/http.h

index 56310669eafe0eccd632d97eb75757649dc88fd6..9216fb6ef3f2940b12863bc919c0d4361704ab95 100644 (file)
@@ -125,7 +125,7 @@ parse_host_port_priority_strv (memory_pool_t *pool, gchar **tokens,
                }
        }
        else if (default_port != 0) {
-               rspamd_snprintf (portbuf, sizeof (portbuf), "%u", default_port);
+               rspamd_snprintf (portbuf, sizeof (portbuf), "%ud", default_port);
                cur_port = portbuf;
        }
        else {
index 6c37d069e96cdea6766717bf4c394f684c4ffac3..7d9b93469f58aa25be73d834b84e6c2e27a2fd50 100644 (file)
@@ -24,6 +24,7 @@
 #include "config.h"
 #include "http.h"
 #include "utlist.h"
+#include "printf.h"
 
 struct rspamd_http_connection_private {
        GString *buf;
@@ -34,8 +35,11 @@ struct rspamd_http_connection_private {
        struct event ev;
        struct timeval tv;
        struct timeval *ptv;
-       gboolean in_body;
-       struct rspamd_http_message *req;
+       struct rspamd_http_message *msg;
+       struct iovec *out;
+       guint outlen;
+       gsize wr_pos;
+       gsize wr_total;
 };
 
 #define HTTP_ERROR http_error_quark ()
@@ -45,274 +49,31 @@ http_error_quark (void)
        return g_quark_from_static_string ("http-error-quark");
 }
 
-static inline void
-rspamd_http_check_date (struct rspamd_http_connection_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_connection *conn = (struct rspamd_http_connection *)parser->data;
-       struct rspamd_http_connection_private *priv;
-
-       priv = conn->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_connection *conn = (struct rspamd_http_connection *)parser->data;
-       struct rspamd_http_connection_private *priv;
-
-       priv = conn->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_connection *conn = (struct rspamd_http_connection *)parser->data;
-       struct rspamd_http_connection_private *priv;
-
-       priv = conn->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_connection *conn = (struct rspamd_http_connection *)parser->data;
-       struct rspamd_http_connection_private *priv;
-
-       priv = conn->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 + 1);
-       }
-       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_connection *conn = (struct rspamd_http_connection *)parser->data;
-       struct rspamd_http_connection_private *priv;
-
-       priv = conn->priv;
-
-       g_string_append_len (priv->req->body, at, length);
-
-       if (conn->opts & RSPAMD_HTTP_BODY_PARTIAL) {
-               return (conn->body_handler (conn, priv->req, at, length));
-       }
-
-       return 0;
-}
-
-static int
-rspamd_http_on_message_complete (http_parser* parser)
-{
-       struct rspamd_http_connection *conn = (struct rspamd_http_connection *)parser->data;
-       struct rspamd_http_connection_private *priv;
-       int ret;
-
-       priv = conn->priv;
-
-       if (conn->opts & RSPAMD_HTTP_BODY_PARTIAL) {
-               ret = conn->body_handler (conn, priv->req, NULL, 0);
-       }
-       else {
-               ret = conn->body_handler (conn, priv->req, priv->req->body->str, priv->req->body->len);
-       }
-
-       return ret;
-}
-
-static void
-rspamd_http_event_handler (int fd, short what, gpointer ud)
+static const gchar *
+rspamd_http_code_to_str (gint code)
 {
-       struct rspamd_http_connection *conn = (struct rspamd_http_connection *)ud;
-       struct rspamd_http_connection_private *priv;
-       GString *buf;
-       gssize r;
-       GError *err;
-
-       priv = conn->priv;
-       buf = priv->buf;
-
-       r = read (fd, buf->str, buf->allocated_len);
-       if (r == -1) {
-               err = g_error_new (HTTP_ERROR, errno, "IO read error: %s", strerror (errno));
-               conn->error_handler (conn, err);
-               g_error_free (err);
-       }
-       else {
-               buf->len = r;
-               if (http_parser_execute (&priv->parser, &priv->parser_cb, buf->str, 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));
-                       conn->error_handler (conn, err);
-                       g_error_free (err);
-               }
-               /* TODO: handle EOF */
+       if (code == 200) {
+               return "OK";
        }
-}
-
-struct rspamd_http_connection*
-rspamd_http_connection_new (rspamd_http_body_handler body_handler,
-               rspamd_http_error_handler error_handler, enum rspamd_http_options opts)
-{
-       struct rspamd_http_connection *new;
-       struct rspamd_http_connection_private *priv;
-
-       new = g_slice_alloc0 (sizeof (struct rspamd_http_connection));
-       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_connection_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_connection_reset (struct rspamd_http_connection *conn)
-{
-       struct rspamd_http_connection_private *priv;
-       struct rspamd_http_message *req;
-       struct rspamd_http_header *hdr, *tmp_hdr;
-
-       priv = conn->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_message), req);
-               priv->req = NULL;
+       else if (code == 404) {
+               return "Not found";
        }
-
-       /* Clear priv */
-       event_del (&priv->ev);
-       if (priv->buf != NULL) {
-               g_string_free (priv->buf, TRUE);
-               priv->buf = NULL;
+       else if (code == 403 || code == 401) {
+               return "Not authorized";
        }
-
-       /* Clear conn itself */
-       if (conn->fd != -1) {
-               close (conn->fd);
+       else if (code >= 400 && code < 500) {
+               return "Bad request";
        }
-}
-
-void
-rspamd_http_connection_free (struct rspamd_http_connection *conn)
-{
-       struct rspamd_http_connection_private *priv;
-
-       priv = conn->priv;
-       rspamd_http_connection_reset (conn);
-       g_slice_free1 (sizeof (struct rspamd_http_connection_private), priv);
-       g_slice_free1 (sizeof (struct rspamd_http_connection), conn);
-}
-
-void
-rspamd_http_connection_handle_request (struct rspamd_http_connection *conn,
-               gpointer ud, gint fd, struct timeval *timeout, struct event_base *base)
-{
-       struct rspamd_http_connection_private *priv = conn->priv;
-       struct rspamd_http_message *req;
-
-       conn->fd = fd;
-       conn->ud = ud;
-       req = g_slice_alloc (sizeof (struct rspamd_http_message));
-       req->url = g_string_sized_new (32);
-       req->headers = NULL;
-       req->date = 0;
-       priv->req = req;
-
-       if (timeout == NULL) {
-               priv->ptv = NULL;
+       else if (code >= 300 && code < 400) {
+               return "See Other";
        }
-       else {
-               memcpy (&priv->tv, timeout, sizeof (struct timeval));
-               priv->ptv = &priv->tv;
+       else if (code >= 500 && code < 600) {
+               return "Internal server error";
        }
-       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, conn);
-       event_base_set (base, &priv->ev);
-       event_add (&priv->ev, priv->ptv);
+       return "Unknown error";
 }
 
-
 /*
  * Obtained from nginx
  * Copyright (C) Igor Sysoev
@@ -580,3 +341,457 @@ rspamd_http_parse_date (const gchar *header, gsize len)
 
        return (time_t) time;
 }
+
+static inline void
+rspamd_http_check_date (struct rspamd_http_connection_private *priv)
+{
+       if (g_ascii_strcasecmp (priv->header->name->str, "date") == 0) {
+               priv->msg->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_connection *conn = (struct rspamd_http_connection *)parser->data;
+       struct rspamd_http_connection_private *priv;
+
+       priv = conn->priv;
+
+       g_string_append_len (priv->msg->url, at, length);
+
+       return 0;
+}
+
+static gint
+rspamd_http_on_header_field (http_parser* parser, const gchar *at, size_t length)
+{
+       struct rspamd_http_connection *conn = (struct rspamd_http_connection *)parser->data;
+       struct rspamd_http_connection_private *priv;
+
+       priv = conn->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->msg->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_connection *conn = (struct rspamd_http_connection *)parser->data;
+       struct rspamd_http_connection_private *priv;
+
+       priv = conn->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_connection *conn = (struct rspamd_http_connection *)parser->data;
+       struct rspamd_http_connection_private *priv;
+
+       priv = conn->priv;
+
+       if (priv->header != NULL) {
+               LL_PREPEND (priv->msg->headers, priv->header);
+               rspamd_http_check_date (priv);
+               priv->header = NULL;
+       }
+
+       if (parser->content_length != 0 && parser->content_length != ULLONG_MAX) {
+               priv->msg->body = g_string_sized_new (parser->content_length + 1);
+       }
+       else {
+               priv->msg->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_connection *conn = (struct rspamd_http_connection *)parser->data;
+       struct rspamd_http_connection_private *priv;
+
+       priv = conn->priv;
+
+       g_string_append_len (priv->msg->body, at, length);
+
+       if (conn->opts & RSPAMD_HTTP_BODY_PARTIAL) {
+               return (conn->body_handler (conn, priv->msg, at, length));
+       }
+
+       return 0;
+}
+
+static int
+rspamd_http_on_message_complete (http_parser* parser)
+{
+       struct rspamd_http_connection *conn = (struct rspamd_http_connection *)parser->data;
+       struct rspamd_http_connection_private *priv;
+       int ret;
+
+       priv = conn->priv;
+
+       if (conn->body_handler != NULL) {
+               if (conn->opts & RSPAMD_HTTP_BODY_PARTIAL) {
+                       ret = conn->body_handler (conn, priv->msg, NULL, 0);
+               }
+               else {
+                       ret = conn->body_handler (conn, priv->msg, priv->msg->body->str, priv->msg->body->len);
+               }
+       }
+       conn->finish_handler (conn, priv->msg);
+
+       return ret;
+}
+
+static void
+rspamd_http_write_helper (struct rspamd_http_connection *conn)
+{
+       struct rspamd_http_connection_private *priv;
+       struct iovec *start;
+       guint niov, i;
+       gsize remain;
+       gssize r;
+       GError *err;
+
+       priv = conn->priv;
+
+       if (priv->wr_pos == priv->wr_total) {
+               conn->finish_handler (conn, priv->msg);
+               return;
+       }
+
+       start = &priv->out[0];
+       niov = priv->outlen;
+       remain = priv->wr_pos;
+       for (i = 0; i < priv->outlen && remain > 0; i ++) {
+               /* Find out the first iov required */
+               start = &priv->out[i];
+               if (start->iov_len <= remain) {
+                       remain -= start->iov_len;
+                       start = &priv->out[i + 1];
+                       niov --;
+               }
+               else {
+                       start->iov_base = (void *)((char *)start->iov_base + remain);
+                       start->iov_len -= remain;
+                       remain = 0;
+               }
+       }
+
+       r = writev (conn->fd, start, MIN (IOV_MAX, niov));
+
+       if (r == -1) {
+               err = g_error_new (HTTP_ERROR, errno, "IO write error: %s", strerror (errno));
+               conn->error_handler (conn, err);
+               g_error_free (err);
+       }
+       else {
+               priv->wr_pos += r;
+       }
+
+       if (priv->wr_pos >= priv->wr_total) {
+               conn->finish_handler (conn, priv->msg);
+       }
+}
+
+static void
+rspamd_http_event_handler (int fd, short what, gpointer ud)
+{
+       struct rspamd_http_connection *conn = (struct rspamd_http_connection *)ud;
+       struct rspamd_http_connection_private *priv;
+       GString *buf;
+       gssize r;
+       GError *err;
+
+       priv = conn->priv;
+       buf = priv->buf;
+
+       if (what == EV_READ) {
+               r = read (fd, buf->str, buf->allocated_len);
+               if (r == -1) {
+                       err = g_error_new (HTTP_ERROR, errno, "IO read error: %s", strerror (errno));
+                       conn->error_handler (conn, err);
+                       g_error_free (err);
+               }
+               else {
+                       buf->len = r;
+                       if (http_parser_execute (&priv->parser, &priv->parser_cb, buf->str, 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));
+                               conn->error_handler (conn, err);
+                               g_error_free (err);
+                       }
+               }
+       }
+       else if (what == EV_TIMEOUT) {
+               err = g_error_new (HTTP_ERROR, ETIMEDOUT,
+                               "IO timeout");
+               conn->error_handler (conn, err);
+               g_error_free (err);
+       }
+       else if (what == EV_WRITE) {
+               rspamd_http_write_helper (conn);
+       }
+}
+
+struct rspamd_http_connection*
+rspamd_http_connection_new (rspamd_http_body_handler body_handler,
+               rspamd_http_error_handler error_handler,
+               rspamd_http_finish_handler finish_handler,
+               enum rspamd_http_options opts,
+               enum rspamd_http_connection_type type)
+{
+       struct rspamd_http_connection *new;
+       struct rspamd_http_connection_private *priv;
+
+       if (error_handler == NULL || finish_handler == NULL) {
+               return NULL;
+       }
+
+       new = g_slice_alloc0 (sizeof (struct rspamd_http_connection));
+       new->opts = opts;
+       new->type = type;
+       new->body_handler = body_handler;
+       new->error_handler = error_handler;
+       new->finish_handler = finish_handler;
+       new->fd = -1;
+
+       /* Init priv */
+       priv = g_slice_alloc0 (sizeof (struct rspamd_http_connection_private));
+       http_parser_init (&priv->parser, type == RSPAMD_HTTP_SERVER ? HTTP_REQUEST : HTTP_RESPONSE);
+       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_connection_reset (struct rspamd_http_connection *conn)
+{
+       struct rspamd_http_connection_private *priv;
+       struct rspamd_http_message *msg;
+
+       priv = conn->priv;
+       msg = priv->msg;
+
+       /* Clear request */
+       if (msg != NULL) {
+               rspamd_http_message_free (msg);
+               priv->msg = NULL;
+       }
+
+       /* Clear priv */
+       event_del (&priv->ev);
+       if (priv->buf != NULL) {
+               g_string_free (priv->buf, TRUE);
+               priv->buf = NULL;
+       }
+       if (priv->out != NULL) {
+               g_slice_free1 (sizeof (struct iovec) * priv->outlen, priv->out);
+               priv->out = NULL;
+       }
+
+       /* Clear conn itself */
+       if (conn->fd != -1) {
+               close (conn->fd);
+       }
+}
+
+void
+rspamd_http_connection_free (struct rspamd_http_connection *conn)
+{
+       struct rspamd_http_connection_private *priv;
+
+       priv = conn->priv;
+       rspamd_http_connection_reset (conn);
+       g_slice_free1 (sizeof (struct rspamd_http_connection_private), priv);
+       g_slice_free1 (sizeof (struct rspamd_http_connection), conn);
+}
+
+void
+rspamd_http_connection_read_message (struct rspamd_http_connection *conn,
+               gpointer ud, gint fd, struct timeval *timeout, struct event_base *base)
+{
+       struct rspamd_http_connection_private *priv = conn->priv;
+       struct rspamd_http_message *req;
+
+       conn->fd = fd;
+       conn->ud = ud;
+       req = rspamd_http_new_message (conn->type == RSPAMD_HTTP_SERVER ? HTTP_REQUEST : HTTP_RESPONSE);
+       priv->msg = 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->new_header = TRUE;
+
+       event_set (&priv->ev, fd, EV_READ | EV_PERSIST, rspamd_http_event_handler, conn);
+       event_base_set (base, &priv->ev);
+       event_add (&priv->ev, priv->ptv);
+}
+
+void
+rspamd_http_connection_write_message (struct rspamd_http_connection *conn,
+               struct rspamd_http_message *msg, const gchar *host,
+               gpointer ud, gint fd, struct timeval *timeout, struct event_base *base)
+{
+       struct rspamd_http_connection_private *priv = conn->priv;
+       struct rspamd_http_header *hdr;
+       gint i;
+
+       conn->fd = fd;
+       conn->ud = ud;
+       priv->msg = msg;
+
+       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 (64);
+
+       if (conn->type == RSPAMD_HTTP_SERVER) {
+               /* Format reply */
+               rspamd_printf_gstring (priv->buf, "HTTP/1.1 %d %s\r\n"
+                               "Connection: close\r\n"
+                               "Content-Length: %z\r\n",
+                               msg->code, rspamd_http_code_to_str (msg->code),
+                               msg->body->len);
+       }
+       else {
+               /* Format request */
+               if (host != NULL) {
+                       rspamd_printf_gstring (priv->buf, "%s %v HTTP/1.1\r\n"
+                                       "Connection: close\r\n"
+                                       "Host: %s\r\n"
+                                       "Content-Length: %z\r\n",
+                               http_method_str (msg->method), msg->url, host, msg->body->len);
+               }
+               else {
+                       /* Fallback to HTTP/1.0 */
+                       rspamd_printf_gstring (priv->buf, "%s %v HTTP/1.0\r\n"
+                                       "Content-Length: %z\r\n",
+                               http_method_str (msg->method), msg->url, msg->body->len);
+               }
+       }
+
+       /* Allocate iov */
+       priv->outlen = 3;
+       priv->wr_total = msg->body->len + priv->buf->len + 2;
+       LL_FOREACH (msg->headers, hdr) {
+               /* <name><: ><value><\r\n> */
+               priv->wr_total += hdr->name->len + hdr->value->len + 4;
+               priv->outlen += 4;
+       }
+       priv->out = g_slice_alloc (sizeof (struct iovec) * priv->outlen);
+       priv->wr_pos = 0;
+
+       /* Now set up all iov */
+       priv->out[0].iov_base = priv->buf->str;
+       priv->out[0].iov_len = priv->buf->len;
+       i = 1;
+       LL_FOREACH (msg->headers, hdr) {
+               priv->out[i].iov_base = hdr->name->str;
+               priv->out[i++].iov_len = hdr->name->len;
+               priv->out[i].iov_base = ": ";
+               priv->out[i++].iov_len = 2;
+               priv->out[i].iov_base = hdr->value->str;
+               priv->out[i++].iov_len = hdr->value->len;
+               priv->out[i].iov_base = "\r\n";
+               priv->out[i++].iov_len = 2;
+       }
+       priv->out[i].iov_base = "\r\n";
+       priv->out[i++].iov_len = 2;
+       priv->out[i].iov_base = msg->body->str;
+       priv->out[i++].iov_len = msg->body->len;
+
+       event_set (&priv->ev, fd, EV_WRITE, rspamd_http_event_handler, conn);
+       event_base_set (base, &priv->ev);
+       event_add (&priv->ev, priv->ptv);
+}
+
+struct rspamd_http_message*
+rspamd_http_new_message (enum http_parser_type type)
+{
+       struct rspamd_http_message *new;
+
+       new = g_slice_alloc (sizeof (struct rspamd_http_message));
+       if (type == HTTP_REQUEST) {
+               new->url = g_string_sized_new (32);
+       }
+       else {
+               new->url = NULL;
+               new->code = 200;
+       }
+       new->headers = NULL;
+       new->date = 0;
+       new->body = NULL;
+       new->type = type;
+
+       return new;
+}
+
+void
+rspamd_http_message_free (struct rspamd_http_message *msg)
+{
+       struct rspamd_http_header *hdr, *tmp_hdr;
+
+       LL_FOREACH_SAFE (msg->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);
+       }
+       if (msg->body != NULL) {
+               g_string_free (msg->body, TRUE);
+       }
+       if (msg->url != NULL) {
+               g_string_free (msg->url, TRUE);
+       }
+       g_slice_free1 (sizeof (struct rspamd_http_message), msg);
+}
index fe9c8d643c688033a0cceed4f164b3e3b0682c11..d26a99d95b81dc7fa9fcb7d109a9f7997a74644f 100644 (file)
@@ -34,9 +34,9 @@
 #include "config.h"
 #include "http_parser.h"
 
-enum rspamd_http_message_type {
-       RSPAMD_HTTP_REQUEST,
-       RSPAMD_HTTP_REPLY
+enum rspamd_http_connection_type {
+       RSPAMD_HTTP_SERVER,
+       RSPAMD_HTTP_CLIENT
 };
 
 /**
@@ -55,9 +55,10 @@ struct rspamd_http_message {
        GString *url;
        struct rspamd_http_header *headers;
        GString *body;
-       enum rspamd_http_message_type type;
+       enum http_parser_type type;
        time_t date;
        gint code;
+       enum http_method method;
 };
 
 
@@ -71,65 +72,80 @@ enum rspamd_http_options {
 struct rspamd_http_connection_private;
 struct rspamd_http_connection;
 
-typedef gboolean (*rspamd_http_body_handler) (struct rspamd_http_connection *srv,
-               struct rspamd_http_message *req,
+typedef gboolean (*rspamd_http_body_handler) (struct rspamd_http_connection *conn,
+               struct rspamd_http_message *msg,
                const gchar *chunk,
                gsize len);
 
-typedef void (*rspamd_http_error_handler) (struct rspamd_http_connection *srv, GError *err);
+typedef void (*rspamd_http_error_handler) (struct rspamd_http_connection *conn, GError *err);
 
-typedef void (*rspamd_http_reply_handler) (struct rspamd_http_connection *srv,
-               struct rspamd_http_message *reply, GError *err);
+typedef void (*rspamd_http_finish_handler) (struct rspamd_http_connection *conn,
+               struct rspamd_http_message *msg);
 
 /**
- * HTTP conn structure
+ * HTTP connection structure
  */
 struct rspamd_http_connection {
-       gint fd;
        struct rspamd_http_connection_private *priv;
-       enum rspamd_http_options opts;
        rspamd_http_body_handler body_handler;
        rspamd_http_error_handler error_handler;
+       rspamd_http_finish_handler finish_handler;
        gpointer ud;
+       enum rspamd_http_options opts;
+       enum rspamd_http_connection_type type;
+       gint fd;
 };
 
 /**
- * Create new http conn
+ * Create new http connection
  * @param handler handler for body
  * @param opts options
- * @return new conn structure
+ * @return new connection structure
  */
-struct rspamd_http_connection* rspamd_http_connection_new (rspamd_http_body_handler body_handler,
+struct rspamd_http_connection* rspamd_http_connection_new (
+               rspamd_http_body_handler body_handler,
                rspamd_http_error_handler error_handler,
-               enum rspamd_http_options opts);
+               rspamd_http_finish_handler finish_handler,
+               enum rspamd_http_options opts,
+               enum rspamd_http_connection_type type);
 
 /**
  * Handle a request using socket fd and user data ud
- * @param conn conn structure
+ * @param conn connection structure
  * @param ud opaque user data
  * @param fd fd to read/write
  */
-void rspamd_http_connection_handle_request (struct rspamd_http_connection *conn, gpointer ud, gint fd,
-               struct timeval *timeout, struct event_base *base);
+void rspamd_http_connection_read_message (
+               struct rspamd_http_connection *conn,
+               gpointer ud,
+               gint fd,
+               struct timeval *timeout,
+               struct event_base *base);
 
 /**
- * Send reply using initialised conn
- * @param conn conn structure
- * @param reply HTTP reply
- * @return TRUE if request can be sent
+ * Send reply using initialised connection
+ * @param conn connection structure
+ * @param msg HTTP message
+ * @param ud opaque user data
+ * @param fd fd to read/write
  */
-gboolean rspamd_http_connection_write_reply (struct rspamd_http_connection *conn,
-               struct rspamd_http_message *reply,
-               rspamd_http_reply_handler *handler);
+void rspamd_http_connection_write_message (
+               struct rspamd_http_connection *conn,
+               struct rspamd_http_message *msg,
+               const gchar *host,
+               gpointer ud,
+               gint fd,
+               struct timeval *timeout,
+               struct event_base *base);
 
 /**
- * Free conn structure
+ * Free connection structure
  * @param conn
  */
 void rspamd_http_connection_free (struct rspamd_http_connection *conn);
 
 /**
- * Reset conn for a new request
+ * Reset connection for a new request
  * @param conn
  */
 void rspamd_http_connection_reset (struct rspamd_http_connection *conn);
@@ -139,7 +155,7 @@ void rspamd_http_connection_reset (struct rspamd_http_connection *conn);
  * @param code code to pass
  * @return new reply object
  */
-struct rspamd_http_message* rspamd_http_new_message (enum rspamd_http_message_type);
+struct rspamd_http_message* rspamd_http_new_message (enum http_parser_type type);
 
 /**
  * Append a header to reply