aboutsummaryrefslogtreecommitdiffstats
path: root/src/client/rspamdclient.c
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2014-01-20 14:29:02 +0000
committerVsevolod Stakhov <vsevolod@highsecure.ru>2014-01-20 14:29:02 +0000
commit43a44c7faf94133305a7afd627b3d009a60aa371 (patch)
tree10cc5a0da61ecd5d2e273abedc43decc1d43fbfb /src/client/rspamdclient.c
parentadc6e8a159ce05cc65f7b4d6f4a6b006300877b0 (diff)
downloadrspamd-43a44c7faf94133305a7afd627b3d009a60aa371.tar.gz
rspamd-43a44c7faf94133305a7afd627b3d009a60aa371.zip
Write the new client library.
Diffstat (limited to 'src/client/rspamdclient.c')
-rw-r--r--src/client/rspamdclient.c180
1 files changed, 177 insertions, 3 deletions
diff --git a/src/client/rspamdclient.c b/src/client/rspamdclient.c
index d7f1c77dd..c612901e1 100644
--- a/src/client/rspamdclient.c
+++ b/src/client/rspamdclient.c
@@ -23,11 +23,185 @@
#include "rspamdclient.h"
#include "util.h"
+#include "http.h"
+
+struct rspamd_client_request;
+
+/*
+ * Since rspamd uses untagged HTTP we can pass a single message per socket
+ */
+struct rspamd_client_connection {
+ gint fd;
+ GString *server_name;
+ struct event_base *ev_base;
+ struct timeval timeout;
+ struct rspamd_http_connection *http_conn;
+ gboolean connected;
+ struct rspamd_client_request *req;
+};
+
+struct rspamd_client_request {
+ struct rspamd_client_connection *conn;
+ struct rspamd_http_message *msg;
+ rspamd_client_callback cb;
+ gpointer ud;
+};
+
+#define RCLIENT_ERROR rspamd_client_error_quark ()
+GQuark
+rspamd_client_error_quark (void)
+{
+ return g_quark_from_static_string ("rspamd-client-error");
+}
+
+static gint
+rspamd_client_body_handler (struct rspamd_http_connection *conn,
+ struct rspamd_http_message *msg,
+ const gchar *chunk, gsize len)
+{
+ /* Do nothing here */
+ return 0;
+}
+
+static void
+rspamd_client_error_handler (struct rspamd_http_connection *conn, GError *err)
+{
+ struct rspamd_client_request *req = (struct rspamd_client_request *)conn->ud;
+ struct rspamd_client_connection *c;
+
+ c = req->conn;
+ req->cb (c->server_name->str, NULL, req->ud, err);
+}
+
+static void
+rspamd_client_finish_handler (struct rspamd_http_connection *conn,
+ struct rspamd_http_message *msg)
+{
+ struct rspamd_client_request *req = (struct rspamd_client_request *)conn->ud;
+ struct rspamd_client_connection *c;
+ struct ucl_parser *parser;
+ GError *err;
+
+ c = req->conn;
+ if (msg->body == NULL || msg->body->len == 0 || msg->code != 200) {
+ err = g_error_new (RCLIENT_ERROR, msg->code, "HTTP error occurred: %d", msg->code);
+ req->cb (c->server_name->str, NULL, req->ud, err);
+ g_error_free (err);
+ return;
+ }
+
+ parser = ucl_parser_new (0);
+ if (!ucl_parser_add_chunk (parser, msg->body->str, msg->body->len)) {
+ err = g_error_new (RCLIENT_ERROR, msg->code, "Cannot parse UCL: %s",
+ ucl_parser_get_error (parser));
+ ucl_parser_free (parser);
+ req->cb (c->server_name->str, NULL, req->ud, err);
+ g_error_free (err);
+ return;
+ }
+
+ req->cb (c->server_name->str, ucl_parser_get_object (parser), req->ud, NULL);
+ ucl_parser_free (parser);
+}
+
+struct rspamd_client_connection *
+rspamd_client_init (struct event_base *ev_base, const gchar *name,
+ guint16 port, gdouble timeout)
+{
+ struct rspamd_client_connection *conn;
+ gint fd;
+
+ fd = make_universal_socket (name, port, SOCK_STREAM, TRUE, FALSE, TRUE);
+ if (fd == -1) {
+ return NULL;
+ }
+
+ conn = g_slice_alloc (sizeof (struct rspamd_client_connection));
+ conn->ev_base = ev_base;
+ conn->fd = fd;
+ conn->connected = FALSE;
+ conn->http_conn = rspamd_http_connection_new (rspamd_client_body_handler,
+ rspamd_client_error_handler, rspamd_client_finish_handler, 0, RSPAMD_HTTP_CLIENT);
+ conn->server_name = g_string_new (name);
+ if (port != 0) {
+ rspamd_printf_gstring (conn->server_name, ":%d", (int)port);
+ }
+
+ double_to_tv (timeout, &conn->timeout);
+
+ return conn;
+}
gboolean
-rspamd_client_command (struct event_base *ev_base, const gchar *name,
- guint16 port, const gchar *command, GHashTable *attrs,
- gdouble timeout, FILE *in, gpointer ud)
+rspamd_client_command (struct rspamd_client_connection *conn,
+ const gchar *command, GHashTable *attrs,
+ FILE *in, rspamd_client_callback cb,
+ gpointer ud, GError **err)
{
+ struct rspamd_client_request *req;
+ gchar *p, *hn, *hv;
+ gsize remain;
+ GHashTableIter it;
+ req = g_slice_alloc (sizeof (struct rspamd_client_request));
+ req->conn = conn;
+ req->cb = cb;
+ req->ud = ud;
+
+ req->msg = rspamd_http_new_message (HTTP_REQUEST);
+ if (in != NULL) {
+ /* Read input stream */
+ req->msg->body = g_string_sized_new (BUFSIZ);
+ while (!feof (in)) {
+ p = req->msg->body->str + req->msg->body->len;
+ remain = req->msg->body->allocated_len - req->msg->body->len - 1;
+ if (remain == 0) {
+ g_string_set_size (req->msg->body, req->msg->body->len * 2);
+ }
+ remain = fread (p, 1, remain, in);
+ if (remain > 0) {
+ req->msg->body->len += remain;
+ req->msg->body->str[req->msg->body->len] = '\0';
+ }
+ }
+ if (ferror (in) != 0) {
+ g_set_error (err, RCLIENT_ERROR, ferror (in), "input IO error: %s", strerror (ferror (in)));
+ g_slice_free1 (sizeof (struct rspamd_client_request), req);
+ return FALSE;
+ }
+ }
+ else {
+ req->msg->body = NULL;
+ }
+
+ /* Convert headers */
+ g_hash_table_iter_init (&it, attrs);
+ while (g_hash_table_iter_next (&it, (gpointer *)&hn, (gpointer *)&hv)) {
+ rspamd_http_message_add_header (req->msg, hn, hv);
+ }
+
+ req->msg->url = g_string_new ("/");
+ g_string_append (req->msg->url, command);
+
+ conn->req = req;
+
+ rspamd_http_connection_write_message (conn->http_conn, req->msg, NULL,
+ "text/plain", req, conn->fd, &conn->timeout, conn->ev_base);
+
+ return TRUE;
+}
+
+void
+rspamd_client_destroy (struct rspamd_client_connection *conn)
+{
+ if (conn != NULL) {
+ rspamd_http_connection_free (conn->http_conn);
+ if (conn->req != NULL) {
+ rspamd_http_message_free (conn->req->msg);
+ g_slice_free1 (sizeof (struct rspamd_client_request), conn->req);
+ }
+ close (conn->fd);
+ g_string_free (conn->server_name, TRUE);
+ g_slice_free1 (sizeof (struct rspamd_client_connection), conn);
+ }
}