struct rspamd_http_connection_private *priv;
gpointer ssl;
gint request_method;
+ rspamd_fstring_t *prev_host;
priv = conn->priv;
ssl = priv->ssl;
priv->ssl = NULL;
request_method = priv->msg->method;
+ /* Preserve host for keepalive */
+ prev_host = priv->msg->host;
+ priv->msg->host = NULL;
rspamd_http_connection_reset (conn);
priv->ssl = ssl;
+
/* Plan read message */
if (conn->opts & RSPAMD_HTTP_CLIENT_SHARED) {
conn->priv->ptv);
}
- priv->msg->method = request_method;
+ if (priv->msg) {
+ priv->msg->method = request_method;
+ priv->msg->host = prev_host;
+ }
+ else {
+ if (prev_host) {
+ rspamd_fstring_free (prev_host);
+ }
+ }
}
static void
conn->max_size = sz;
}
-void
-rspamd_http_message_free (struct rspamd_http_message *msg)
-{
- struct rspamd_http_header *hdr, *htmp, *hcur, *hcurtmp;
-
-
- HASH_ITER (hh, msg->headers, hdr, htmp) {
- HASH_DEL (msg->headers, hdr);
-
- DL_FOREACH_SAFE (hdr, hcur, hcurtmp) {
- rspamd_fstring_free (hcur->combined);
- g_free (hcur);
- }
- }
-
- rspamd_http_message_storage_cleanup (msg);
-
- if (msg->url != NULL) {
- rspamd_fstring_free (msg->url);
- }
- if (msg->status != NULL) {
- rspamd_fstring_free (msg->status);
- }
- if (msg->host != NULL) {
- rspamd_fstring_free (msg->host);
- }
- if (msg->peer_key != NULL) {
- rspamd_pubkey_unref (msg->peer_key);
- }
-
- g_free (msg);
-}
-
-void
-rspamd_http_message_set_peer_key (struct rspamd_http_message *msg,
- struct rspamd_cryptobox_pubkey *pk)
-{
- if (msg->peer_key != NULL) {
- rspamd_pubkey_unref (msg->peer_key);
- }
-
- if (pk) {
- msg->peer_key = rspamd_pubkey_ref (pk);
- }
- else {
- msg->peer_key = NULL;
- }
-}
-
-void
-rspamd_http_message_add_header_len (struct rspamd_http_message *msg,
- const gchar *name,
- const gchar *value,
- gsize len)
-{
- struct rspamd_http_header *hdr, *found = NULL;
- guint nlen, vlen;
-
- if (msg != NULL && name != NULL && value != NULL) {
- hdr = g_malloc0 (sizeof (struct rspamd_http_header));
- nlen = strlen (name);
- vlen = len;
- hdr->combined = rspamd_fstring_sized_new (nlen + vlen + 4);
- rspamd_printf_fstring (&hdr->combined, "%s: %*s\r\n", name, (gint)vlen,
- value);
- hdr->name.begin = hdr->combined->str;
- hdr->name.len = nlen;
- hdr->value.begin = hdr->combined->str + nlen + 2;
- hdr->value.len = vlen;
-
- HASH_FIND (hh, msg->headers, hdr->name.begin,
- hdr->name.len, found);
-
- if (found == NULL) {
- HASH_ADD_KEYPTR (hh, msg->headers, hdr->name.begin,
- hdr->name.len, hdr);
- }
-
- DL_APPEND (found, hdr);
- }
-}
-
-void
-rspamd_http_message_add_header (struct rspamd_http_message *msg,
- const gchar *name,
- const gchar *value)
-{
- if (value) {
- rspamd_http_message_add_header_len (msg, name, value, strlen (value));
- }
-}
-
-void
-rspamd_http_message_add_header_fstr (struct rspamd_http_message *msg,
- const gchar *name,
- rspamd_fstring_t *value)
-{
- struct rspamd_http_header *hdr, *found = NULL;
- guint nlen, vlen;
-
- if (msg != NULL && name != NULL && value != NULL) {
- hdr = g_malloc0 (sizeof (struct rspamd_http_header));
- nlen = strlen (name);
- vlen = value->len;
- hdr->combined = rspamd_fstring_sized_new (nlen + vlen + 4);
- rspamd_printf_fstring (&hdr->combined, "%s: %V\r\n", name, value);
- hdr->name.begin = hdr->combined->str;
- hdr->name.len = nlen;
- hdr->value.begin = hdr->combined->str + nlen + 2;
- hdr->value.len = vlen;
-
- HASH_FIND (hh, msg->headers, hdr->name.begin,
- hdr->name.len, found);
-
- if (found == NULL) {
- HASH_ADD_KEYPTR (hh, msg->headers, hdr->name.begin,
- hdr->name.len, hdr);
- }
-
- DL_APPEND (found, hdr);
- }
-}
-
-const rspamd_ftok_t *
-rspamd_http_message_find_header (struct rspamd_http_message *msg,
- const gchar *name)
-{
- struct rspamd_http_header *hdr;
- const rspamd_ftok_t *res = NULL;
- guint slen = strlen (name);
-
- if (msg != NULL) {
- HASH_FIND (hh, msg->headers, name, slen, hdr);
-
- if (hdr) {
- res = &hdr->value;
- }
- }
-
- return res;
-}
-
-GPtrArray*
-rspamd_http_message_find_header_multiple (
- struct rspamd_http_message *msg,
- const gchar *name)
-{
- GPtrArray *res = NULL;
- struct rspamd_http_header *hdr, *cur;
-
- guint slen = strlen (name);
-
- if (msg != NULL) {
- HASH_FIND (hh, msg->headers, name, slen, hdr);
-
- if (hdr) {
- res = g_ptr_array_sized_new (4);
-
- LL_FOREACH (hdr, cur) {
- g_ptr_array_add (res, &cur->value);
- }
- }
- }
-
-
- return res;
-}
-
-
-gboolean
-rspamd_http_message_remove_header (struct rspamd_http_message *msg,
- const gchar *name)
-{
- struct rspamd_http_header *hdr, *hcur, *hcurtmp;
- gboolean res = FALSE;
- guint slen = strlen (name);
-
- if (msg != NULL) {
- HASH_FIND (hh, msg->headers, name, slen, hdr);
-
- if (hdr) {
- HASH_DEL (msg->headers, hdr);
- res = TRUE;
-
- DL_FOREACH_SAFE (hdr, hcur, hcurtmp) {
- rspamd_fstring_free (hcur->combined);
- g_free (hcur);
- }
- }
- }
-
- return res;
-}
-
void
rspamd_http_connection_set_key (struct rspamd_http_connection *conn,
struct rspamd_cryptobox_keypair *key)
struct rspamd_http_connection;
struct rspamd_http_connection_router;
struct rspamd_http_connection_entry;
+struct rspamd_keepalive_hash_key;
struct rspamd_storage_shmem {
gchar *shm_name;
rspamd_http_error_handler_t error_handler;
rspamd_http_finish_handler_t finish_handler;
gpointer ud;
+ /* Used for keepalive */
+ struct rspamd_keepalive_hash_key *keepalive_hash_key;
gsize max_size;
unsigned opts;
enum rspamd_http_connection_type type;
static struct rspamd_http_context *default_ctx = NULL;
+struct rspamd_http_keepalive_cbdata {
+ struct rspamd_http_connection *conn;
+ GQueue *queue;
+ GList *link;
+ struct event ev;
+};
+
+static void
+rspamd_http_keepalive_queue_cleanup (GQueue *conns)
+{
+ GList *cur;
+
+ cur = conns->head;
+
+ while (cur) {
+ struct rspamd_http_keepalive_cbdata *cbd;
+
+ cbd = (struct rspamd_http_keepalive_cbdata *)cur->data;
+ rspamd_http_connection_unref (cbd->conn);
+ event_del (&cbd->ev);
+ g_free (cbd);
+
+ cur = cur->next;
+ }
+
+ g_queue_clear (conns);
+}
+
static void
rspamd_http_context_client_rotate_ev (gint fd, short what, void *arg)
{
ctx->ev_base = ev_base;
+ ctx->keep_alive_hash = kh_init (rspamd_keep_alive_hash);
+
return ctx;
}
return ctx;
}
+
void
rspamd_http_context_free (struct rspamd_http_context *ctx)
{
}
}
+ struct rspamd_keepalive_hash_key *hk;
+
+ kh_foreach_key (ctx->keep_alive_hash, hk, {
+ if (hk->host) {
+ g_free (hk->host);
+ }
+
+ rspamd_inet_address_free (hk->addr);
+ rspamd_http_keepalive_queue_cleanup (&hk->conns);
+ g_free (hk);
+ });
+
+ kh_destroy (rspamd_keep_alive_hash, ctx->keep_alive_hash);
+
g_free (ctx);
}
}
gint32
-rspamd_keep_alive_key_hash (struct rspamd_keepalive_hash_key k)
+rspamd_keep_alive_key_hash (struct rspamd_keepalive_hash_key *k)
{
gint32 h;
- h = rspamd_inet_address_port_hash (k.addr);
+ h = rspamd_inet_address_port_hash (k->addr);
- if (k.host) {
- h = rspamd_cryptobox_fast_hash (k.host, strlen (k.host), h);
+ if (k->host) {
+ h = rspamd_cryptobox_fast_hash (k->host, strlen (k->host), h);
}
return h;
}
bool
-rspamd_keep_alive_key_equal (struct rspamd_keepalive_hash_key k1,
- struct rspamd_keepalive_hash_key k2)
+rspamd_keep_alive_key_equal (struct rspamd_keepalive_hash_key *k1,
+ struct rspamd_keepalive_hash_key *k2)
{
- if (k1.host && k2.host) {
- if (rspamd_inet_address_port_equal (k1.addr, k2.addr)) {
- return strcmp (k1.host, k2.host);
+ if (k1->host && k2->host) {
+ if (rspamd_inet_address_port_equal (k1->addr, k2->addr)) {
+ return strcmp (k1->host, k2->host);
}
}
- else if (!k1.host && !k2.host) {
- return rspamd_inet_address_port_equal (k1.addr, k2.addr);
+ else if (!k1->host && !k2->host) {
+ return rspamd_inet_address_port_equal (k1->addr, k2->addr);
}
/* One has host and another has no host */
return false;
+}
+
+struct rspamd_http_connection*
+rspamd_http_context_check_keepalive (struct rspamd_http_context *ctx,
+ const rspamd_inet_addr_t *addr,
+ const gchar *host)
+{
+ struct rspamd_keepalive_hash_key hk, *phk;
+ khiter_t k;
+
+ hk.addr = (rspamd_inet_addr_t *)addr;
+ hk.host = (gchar *)host;
+
+ k = kh_get (rspamd_keep_alive_hash, ctx->keep_alive_hash, &hk);
+
+ if (k != kh_end (ctx->keep_alive_hash)) {
+ phk = kh_key (ctx->keep_alive_hash, k);
+ GQueue *conns = &phk->conns;
+
+ /* Use stack based approach */
+
+ if (g_queue_get_length (conns) > 0) {
+ struct rspamd_http_keepalive_cbdata *cbd;
+ struct rspamd_http_connection *conn;
+
+ cbd = g_queue_pop_head (conns);
+ event_del (&cbd->ev);
+ conn = cbd->conn;
+ g_free (cbd);
+
+ /* We transfer refcount here! */
+ return conn;
+ }
+ }
+
+ return NULL;
+}
+
+void
+rspamd_http_context_prepare_keepalive (struct rspamd_http_context *ctx,
+ struct rspamd_http_connection *conn,
+ const rspamd_inet_addr_t *addr,
+ const gchar *host)
+{
+ struct rspamd_keepalive_hash_key hk, *phk;
+ khiter_t k;
+
+ hk.addr = (rspamd_inet_addr_t *)addr;
+ hk.host = (gchar *)host;
+
+ k = kh_get (rspamd_keep_alive_hash, ctx->keep_alive_hash, &hk);
+
+ if (k != kh_end (ctx->keep_alive_hash)) {
+ /* Reuse existing */
+ conn->keepalive_hash_key = kh_key (ctx->keep_alive_hash, k);
+ }
+ else {
+ /* Create new one */
+ GQueue empty_init = G_QUEUE_INIT;
+ gint r;
+
+ phk = g_malloc (sizeof (*phk));
+ phk->conns = empty_init;
+ phk->host = g_strdup (host);
+ phk->addr = rspamd_inet_address_copy (addr);
+
+ kh_put (rspamd_keep_alive_hash, ctx->keep_alive_hash, phk, &r);
+ conn->keepalive_hash_key = phk;
+ }
+}
+
+static void
+rspamd_http_keepalive_handler (gint fd, short what, gpointer ud)
+{
+ struct rspamd_http_keepalive_cbdata *cbdata =
+ (struct rspamd_http_keepalive_cbdata *)ud;
+ /*
+ * We can get here if a remote side reported something or it has
+ * timed out. In both cases we just terminate keepalive connection.
+ */
+
+ g_queue_delete_link (cbdata->queue, cbdata->link);
+ rspamd_http_connection_unref (cbdata->conn);
+ g_free (cbdata);
+}
+
+void
+rspamd_http_context_push_keepalive (struct rspamd_http_context *ctx,
+ struct rspamd_http_connection *conn,
+ struct rspamd_http_message *msg,
+ struct event_base *ev_base)
+{
+ struct rspamd_http_keepalive_cbdata *cbdata;
+ struct timeval tv;
+ gdouble timeout = ctx->config.keepalive_interval;
+
+ g_assert (conn->keepalive_hash_key != NULL);
+
+ /* Move connection to the keepalive pool */
+ cbdata = g_malloc0 (sizeof (*cbdata));
+
+ cbdata->conn = rspamd_http_connection_ref (conn);
+ g_queue_push_tail (&conn->keepalive_hash_key->conns, cbdata);
+ cbdata->link = conn->keepalive_hash_key->conns.tail;
+ cbdata->queue = &conn->keepalive_hash_key->conns;
+
+ event_set (&cbdata->ev, conn->fd, EV_READ|EV_TIMEOUT,
+ rspamd_http_keepalive_handler,
+ &cbdata);
+
+ if (msg) {
+ const rspamd_ftok_t *tok;
+
+ tok = rspamd_http_message_find_header (msg, "Keep-Alive");
+
+ if (tok) {
+ goffset pos = rspamd_substring_search_caseless (tok->begin,
+ tok->len, "timeout=", sizeof ("timeout=") - 1);
+
+ if (pos != -1) {
+ pos += sizeof ("timeout=");
+
+ gchar *end_pos = memchr (tok->begin + pos, ',', tok->len - pos);
+ glong real_timeout;
+
+ if (end_pos) {
+ if (rspamd_strtol (tok->begin + pos + 1,
+ (end_pos - tok->begin) - pos - 1, &real_timeout) &&
+ real_timeout > 0) {
+ timeout = real_timeout;
+ }
+ }
+ else {
+ if (rspamd_strtol (tok->begin + pos + 1,
+ tok->len - pos - 1, &real_timeout) &&
+ real_timeout > 0) {
+ timeout = real_timeout;
+ }
+ }
+ }
+ }
+ }
+
+ double_to_tv (timeout, &tv);
+ event_base_set (ev_base, &cbdata->ev);
+ event_add (&cbdata->ev, &tv);
}
\ No newline at end of file
#include "config.h"
#include "ucl.h"
+#include "addr.h"
#include <event.h>
struct rspamd_http_context;
struct rspamd_config;
+struct rspamd_http_message;
struct rspamd_http_context_cfg {
guint kp_cache_size_client;
guint kp_cache_size_server;
guint ssl_cache_size;
+ gdouble keepalive_interval;
gdouble client_key_rotate_time;
const gchar *user_agent;
};
struct rspamd_http_context* rspamd_http_context_default (void);
+/**
+ * Returns preserved keepalive connection if it's available.
+ * Refcount is transferred to caller!
+ * @param ctx
+ * @param addr
+ * @param host
+ * @return
+ */
+struct rspamd_http_connection* rspamd_http_context_check_keepalive (
+ struct rspamd_http_context *ctx, const rspamd_inet_addr_t *addr,
+ const gchar *host);
+
+/**
+ * Prepares keepalive key for a connection by creating a new entry or by reusing existent
+ * Bear in mind, that keepalive pool has currently no cleanup methods!
+ * @param ctx
+ * @param conn
+ * @param addr
+ * @param host
+ */
+void rspamd_http_context_prepare_keepalive (struct rspamd_http_context *ctx,
+ struct rspamd_http_connection *conn,
+ const rspamd_inet_addr_t *addr,
+ const gchar *host);
+/**
+ * Pushes a connection to keepalive pool after client request is finished,
+ * keepalive key *must* be prepared before using of this function
+ * @param ctx
+ * @param conn
+ * @param msg
+ */
+void rspamd_http_context_push_keepalive (struct rspamd_http_context *ctx,
+ struct rspamd_http_connection *conn,
+ struct rspamd_http_message *msg,
+ struct event_base *ev_base);
+
#endif
#include "libutil/http_private.h"
#include "libutil/printf.h"
#include "libutil/logger.h"
+#include "utlist.h"
#include "unix-std.h"
struct rspamd_http_message *
msg->body_buf.len = 0;
}
+
+void
+rspamd_http_message_free (struct rspamd_http_message *msg)
+{
+ struct rspamd_http_header *hdr, *htmp, *hcur, *hcurtmp;
+
+
+ HASH_ITER (hh, msg->headers, hdr, htmp) {
+ HASH_DEL (msg->headers, hdr);
+
+ DL_FOREACH_SAFE (hdr, hcur, hcurtmp) {
+ rspamd_fstring_free (hcur->combined);
+ g_free (hcur);
+ }
+ }
+
+ rspamd_http_message_storage_cleanup (msg);
+
+ if (msg->url != NULL) {
+ rspamd_fstring_free (msg->url);
+ }
+ if (msg->status != NULL) {
+ rspamd_fstring_free (msg->status);
+ }
+ if (msg->host != NULL) {
+ rspamd_fstring_free (msg->host);
+ }
+ if (msg->peer_key != NULL) {
+ rspamd_pubkey_unref (msg->peer_key);
+ }
+
+ g_free (msg);
+}
+
+void
+rspamd_http_message_set_peer_key (struct rspamd_http_message *msg,
+ struct rspamd_cryptobox_pubkey *pk)
+{
+ if (msg->peer_key != NULL) {
+ rspamd_pubkey_unref (msg->peer_key);
+ }
+
+ if (pk) {
+ msg->peer_key = rspamd_pubkey_ref (pk);
+ }
+ else {
+ msg->peer_key = NULL;
+ }
+}
+
+void
+rspamd_http_message_add_header_len (struct rspamd_http_message *msg,
+ const gchar *name,
+ const gchar *value,
+ gsize len)
+{
+ struct rspamd_http_header *hdr, *found = NULL;
+ guint nlen, vlen;
+
+ if (msg != NULL && name != NULL && value != NULL) {
+ hdr = g_malloc0 (sizeof (struct rspamd_http_header));
+ nlen = strlen (name);
+ vlen = len;
+ hdr->combined = rspamd_fstring_sized_new (nlen + vlen + 4);
+ rspamd_printf_fstring (&hdr->combined, "%s: %*s\r\n", name, (gint)vlen,
+ value);
+ hdr->name.begin = hdr->combined->str;
+ hdr->name.len = nlen;
+ hdr->value.begin = hdr->combined->str + nlen + 2;
+ hdr->value.len = vlen;
+
+ HASH_FIND (hh, msg->headers, hdr->name.begin,
+ hdr->name.len, found);
+
+ if (found == NULL) {
+ HASH_ADD_KEYPTR (hh, msg->headers, hdr->name.begin,
+ hdr->name.len, hdr);
+ }
+
+ DL_APPEND (found, hdr);
+ }
+}
+
+void
+rspamd_http_message_add_header (struct rspamd_http_message *msg,
+ const gchar *name,
+ const gchar *value)
+{
+ if (value) {
+ rspamd_http_message_add_header_len (msg, name, value, strlen (value));
+ }
+}
+
+void
+rspamd_http_message_add_header_fstr (struct rspamd_http_message *msg,
+ const gchar *name,
+ rspamd_fstring_t *value)
+{
+ struct rspamd_http_header *hdr, *found = NULL;
+ guint nlen, vlen;
+
+ if (msg != NULL && name != NULL && value != NULL) {
+ hdr = g_malloc0 (sizeof (struct rspamd_http_header));
+ nlen = strlen (name);
+ vlen = value->len;
+ hdr->combined = rspamd_fstring_sized_new (nlen + vlen + 4);
+ rspamd_printf_fstring (&hdr->combined, "%s: %V\r\n", name, value);
+ hdr->name.begin = hdr->combined->str;
+ hdr->name.len = nlen;
+ hdr->value.begin = hdr->combined->str + nlen + 2;
+ hdr->value.len = vlen;
+
+ HASH_FIND (hh, msg->headers, hdr->name.begin,
+ hdr->name.len, found);
+
+ if (found == NULL) {
+ HASH_ADD_KEYPTR (hh, msg->headers, hdr->name.begin,
+ hdr->name.len, hdr);
+ }
+
+ DL_APPEND (found, hdr);
+ }
+}
+
+const rspamd_ftok_t *
+rspamd_http_message_find_header (struct rspamd_http_message *msg,
+ const gchar *name)
+{
+ struct rspamd_http_header *hdr;
+ const rspamd_ftok_t *res = NULL;
+ guint slen = strlen (name);
+
+ if (msg != NULL) {
+ HASH_FIND (hh, msg->headers, name, slen, hdr);
+
+ if (hdr) {
+ res = &hdr->value;
+ }
+ }
+
+ return res;
+}
+
+GPtrArray*
+rspamd_http_message_find_header_multiple (
+ struct rspamd_http_message *msg,
+ const gchar *name)
+{
+ GPtrArray *res = NULL;
+ struct rspamd_http_header *hdr, *cur;
+
+ guint slen = strlen (name);
+
+ if (msg != NULL) {
+ HASH_FIND (hh, msg->headers, name, slen, hdr);
+
+ if (hdr) {
+ res = g_ptr_array_sized_new (4);
+
+ LL_FOREACH (hdr, cur) {
+ g_ptr_array_add (res, &cur->value);
+ }
+ }
+ }
+
+
+ return res;
+}
+
+
+gboolean
+rspamd_http_message_remove_header (struct rspamd_http_message *msg,
+ const gchar *name)
+{
+ struct rspamd_http_header *hdr, *hcur, *hcurtmp;
+ gboolean res = FALSE;
+ guint slen = strlen (name);
+
+ if (msg != NULL) {
+ HASH_FIND (hh, msg->headers, name, slen, hdr);
+
+ if (hdr) {
+ HASH_DEL (msg->headers, hdr);
+ res = TRUE;
+
+ DL_FOREACH_SAFE (hdr, hcur, hcurtmp) {
+ rspamd_fstring_free (hcur->combined);
+ g_free (hcur);
+ }
+ }
+ }
+
+ return res;
+}
\ No newline at end of file
struct rspamd_keepalive_hash_key {
rspamd_inet_addr_t *addr;
gchar *host;
+ GQueue conns;
};
-gint32 rspamd_keep_alive_key_hash (struct rspamd_keepalive_hash_key k);
-bool rspamd_keep_alive_key_equal (struct rspamd_keepalive_hash_key k1,
- struct rspamd_keepalive_hash_key k2);
+gint32 rspamd_keep_alive_key_hash (struct rspamd_keepalive_hash_key* k);
+bool rspamd_keep_alive_key_equal (struct rspamd_keepalive_hash_key* k1,
+ struct rspamd_keepalive_hash_key* k2);
-KHASH_INIT (rspamd_keep_alive_hash, struct rspamd_keepalive_hash_key,
- GQueue, true, rspamd_keep_alive_key_hash, rspamd_keep_alive_key_equal);
+KHASH_INIT (rspamd_keep_alive_hash, struct rspamd_keepalive_hash_key *,
+ char, 0, rspamd_keep_alive_key_hash, rspamd_keep_alive_key_equal);
struct rspamd_http_context {
struct rspamd_http_context_cfg config;