]> source.dussan.org Git - rspamd.git/commitdiff
New http_router interface.
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Mon, 27 Jan 2014 13:47:42 +0000 (13:47 +0000)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Mon, 27 Jan 2014 13:47:42 +0000 (13:47 +0000)
This interface is designed to emulate evhttp behaviour and route
requests among a set of functors regarding the requested path. It hides
some http_connection internals allowing to work on a pretty high level
implementing an HTTP server instance.

src/http.c
src/http.h

index a258ba8ca79afc2851b279ac90df1af7bdf41bdb..99bb2b8670c3011e874602f7a50c235cfb357d39 100644 (file)
@@ -24,6 +24,7 @@
 #include "config.h"
 #include "http.h"
 #include "utlist.h"
+#include "util.h"
 #include "printf.h"
 
 struct rspamd_http_connection_private {
@@ -595,9 +596,9 @@ rspamd_http_event_handler (int fd, short what, gpointer ud)
 }
 
 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,
+rspamd_http_connection_new (rspamd_http_body_handler_t body_handler,
+               rspamd_http_error_handler_t error_handler,
+               rspamd_http_finish_handler_t finish_handler,
                enum rspamd_http_options opts,
                enum rspamd_http_connection_type type)
 {
@@ -871,3 +872,160 @@ void rspamd_http_message_add_header (struct rspamd_http_message *msg,
                DL_APPEND (msg->headers, hdr);
        }
 }
+
+/*
+ * HTTP router functions
+ */
+
+static void
+rspamd_http_entry_free (struct rspamd_http_connection_entry *entry)
+{
+       if (entry != NULL) {
+               close (entry->conn->fd);
+               rspamd_http_connection_unref (entry->conn);
+               g_slice_free1 (sizeof (struct rspamd_http_connection_entry), entry);
+       }
+}
+
+static void
+rspamd_http_router_error_handler (struct rspamd_http_connection *conn, GError *err)
+{
+       struct rspamd_http_connection_entry *entry = conn->ud;
+       struct rspamd_http_message *msg;
+
+       if (entry->is_reply) {
+               /* At this point we need to finish this session and close owned socket */
+               if (entry->rt->error_handler != NULL) {
+                       entry->rt->error_handler (entry, err);
+               }
+               rspamd_http_entry_free (entry);
+       }
+       else {
+               /* Here we can write a reply to a client */
+               if (entry->rt->error_handler != NULL) {
+                       entry->rt->error_handler (entry, err);
+               }
+               msg = rspamd_http_new_message (HTTP_RESPONSE);
+               msg->date = time (NULL);
+               msg->code = err->code;
+               msg->body = g_string_new (err->message);
+               rspamd_http_connection_reset (entry->conn);
+               rspamd_http_connection_write_message (entry->conn, msg, NULL,
+                                       "text/plain", entry, entry->conn->fd, entry->rt->ptv, entry->rt->ev_base);
+               entry->is_reply = TRUE;
+       }
+}
+
+static int
+rspamd_http_router_finish_handler (struct rspamd_http_connection *conn,
+               struct rspamd_http_message *msg)
+{
+       struct rspamd_http_connection_entry *entry = conn->ud;
+       rspamd_http_router_handler_t handler = NULL;
+       gpointer found;
+       struct rspamd_http_message *err_msg;
+       GError *err;
+
+       G_STATIC_ASSERT (sizeof (rspamd_http_router_handler_t) == sizeof (gpointer));
+
+       if (entry->is_reply) {
+               /* Request is finished, it is safe to free a connection */
+               rspamd_http_entry_free (entry);
+       }
+       else {
+               /* Search for path */
+               if (msg->url != NULL && msg->url->len != 0) {
+                       found = g_hash_table_lookup (entry->rt->paths, msg->url->str);
+                       memcpy (&handler, &found, sizeof (found));
+               }
+               entry->is_reply = TRUE;
+               if (handler != NULL) {
+                       return handler (entry, msg);
+               }
+               else {
+                       err = g_error_new (HTTP_ERROR, 404,
+                                                       "Not found");
+                       if (entry->rt->error_handler != NULL) {
+                               entry->rt->error_handler (entry, err);
+                       }
+                       err_msg = rspamd_http_new_message (HTTP_RESPONSE);
+                       err_msg->date = time (NULL);
+                       err_msg->code = err->code;
+                       err_msg->body = g_string_new (err->message);
+                       rspamd_http_connection_reset (entry->conn);
+                       rspamd_http_connection_write_message (entry->conn, err_msg, NULL,
+                                       "text/plain", entry, entry->conn->fd, entry->rt->ptv, entry->rt->ev_base);
+                       g_error_free (err);
+               }
+       }
+
+       return 0;
+}
+
+struct rspamd_http_connection_router*
+rspamd_http_router_new (rspamd_http_router_error_handler_t eh,
+               struct timeval *timeout, struct event_base *base)
+{
+       struct rspamd_http_connection_router* new;
+
+       new = g_slice_alloc (sizeof (struct rspamd_http_connection_router));
+       new->paths = g_hash_table_new (rspamd_strcase_hash, rspamd_strcase_equal);
+       new->conns = NULL;
+       new->error_handler = eh;
+       new->ev_base = base;
+       if (timeout) {
+               new->tv = *timeout;
+               new->ptv = &new->tv;
+       }
+       else {
+               new->ptv = NULL;
+       }
+
+       return new;
+}
+
+void
+rspamd_http_router_add_path (struct rspamd_http_connection_router *router,
+               const gchar *path, rspamd_http_router_handler_t handler)
+{
+       gpointer ptr;
+       G_STATIC_ASSERT (sizeof (rspamd_http_router_handler_t) == sizeof (gpointer));
+
+       if (path != NULL && handler != NULL && router != NULL) {
+               memcpy (&ptr, &handler, sizeof (ptr));
+               g_hash_table_insert (router->paths, (gpointer)path, ptr);
+       }
+}
+
+void
+rspamd_http_router_handle_socket (struct rspamd_http_connection_router *router,
+               gint fd, gpointer ud)
+{
+       struct rspamd_http_connection_entry *conn;
+
+       conn = g_slice_alloc (sizeof (struct rspamd_http_connection_entry));
+       conn->rt = router;
+       conn->ud = ud;
+       conn->is_reply = FALSE;
+
+       conn->conn = rspamd_http_connection_new (NULL, rspamd_http_router_error_handler,
+                       rspamd_http_router_finish_handler, 0, RSPAMD_HTTP_SERVER);
+
+       rspamd_http_connection_read_message (conn->conn, conn, fd, router->ptv, router->ev_base);
+       LL_PREPEND (router->conns, conn);
+}
+
+void
+rspamd_http_router_free (struct rspamd_http_connection_router *router)
+{
+       struct rspamd_http_connection_entry *conn, *tmp;
+
+       if (router) {
+               LL_FOREACH_SAFE (router->conns, conn, tmp) {
+                       rspamd_http_entry_free (conn);
+               }
+
+               g_hash_table_unref (router->paths);
+               g_slice_free1 (sizeof (struct rspamd_http_connection_router), router);
+       }
+}
index cdac77a54c716302716a4db7b43e1f73ff9c0401..929e5412617f9e85dac61f3f5dac4eaba6e2d56e 100644 (file)
@@ -71,25 +71,31 @@ enum rspamd_http_options {
 
 struct rspamd_http_connection_private;
 struct rspamd_http_connection;
+struct rspamd_http_connection_router;
+struct rspamd_http_connection_entry;
 
-typedef int (*rspamd_http_body_handler) (struct rspamd_http_connection *conn,
+typedef int (*rspamd_http_body_handler_t) (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 *conn, GError *err);
+typedef void (*rspamd_http_error_handler_t) (struct rspamd_http_connection *conn, GError *err);
 
-typedef int (*rspamd_http_finish_handler) (struct rspamd_http_connection *conn,
+typedef int (*rspamd_http_finish_handler_t) (struct rspamd_http_connection *conn,
                struct rspamd_http_message *msg);
 
+typedef int (*rspamd_http_router_handler_t) (struct rspamd_http_connection_entry *conn_ent,
+               struct rspamd_http_message *msg);
+typedef void (*rspamd_http_router_error_handler_t) (struct rspamd_http_connection_entry *conn_ent, GError *err);
+
 /**
  * HTTP connection structure
  */
 struct rspamd_http_connection {
        struct rspamd_http_connection_private *priv;
-       rspamd_http_body_handler body_handler;
-       rspamd_http_error_handler error_handler;
-       rspamd_http_finish_handler finish_handler;
+       rspamd_http_body_handler_t body_handler;
+       rspamd_http_error_handler_t error_handler;
+       rspamd_http_finish_handler_t finish_handler;
        gpointer ud;
        enum rspamd_http_options opts;
        enum rspamd_http_connection_type type;
@@ -97,16 +103,33 @@ struct rspamd_http_connection {
        gint ref;
 };
 
+struct rspamd_http_connection_entry {
+       struct rspamd_http_connection_router *rt;
+       struct rspamd_http_connection *conn;
+       gpointer ud;
+       gboolean is_reply;
+       struct rspamd_http_connection_entry *next;
+};
+
+struct rspamd_http_connection_router {
+       struct rspamd_http_connection_entry *conns;
+       GHashTable *paths;
+       struct timeval tv;
+       struct timeval *ptv;
+       struct event_base *ev_base;
+       rspamd_http_router_error_handler_t error_handler;
+};
+
 /**
  * Create new http connection
- * @param handler handler for body
+ * @param handler_t handler_t for body
  * @param opts options
  * @return new connection structure
  */
 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,
+               rspamd_http_body_handler_t body_handler,
+               rspamd_http_error_handler_t error_handler,
+               rspamd_http_finish_handler_t finish_handler,
                enum rspamd_http_options opts,
                enum rspamd_http_connection_type type);
 
@@ -205,4 +228,34 @@ void rspamd_http_message_free (struct rspamd_http_message *msg);
  */
 time_t rspamd_http_parse_date (const gchar *header, gsize len);
 
+/**
+ * Create new http connection router and the associated HTTP connection
+ * @return
+ */
+struct rspamd_http_connection_router* rspamd_http_router_new (
+               rspamd_http_router_error_handler_t eh,
+               struct timeval *timeout,
+               struct event_base *base);
+
+/**
+ * Add new path to the router
+ */
+void rspamd_http_router_add_path (struct rspamd_http_connection_router *router,
+               const gchar *path, rspamd_http_router_handler_t handler);
+
+/**
+ * Handle new accepted socket
+ * @param router router object
+ * @param fd server socket
+ * @param ud opaque userdata
+ */
+void rspamd_http_router_handle_socket (struct rspamd_http_connection_router *router,
+               gint fd, gpointer ud);
+
+/**
+ * Free router and all connections associated
+ * @param router
+ */
+void rspamd_http_router_free (struct rspamd_http_connection_router *router);
+
 #endif /* HTTP_H_ */