|
|
@@ -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); |
|
|
|
} |