dovecot-2.1: Added "connection" API for handling client/server c...
dovecot at dovecot.org
dovecot at dovecot.org
Sun Jul 8 09:01:11 EEST 2012
details: http://hg.dovecot.org/dovecot-2.1/rev/43b841891c77
changeset: 14607:43b841891c77
user: Timo Sirainen <tss at iki.fi>
date: Sun Jul 08 08:59:52 2012 +0300
description:
Added "connection" API for handling client/server connections more easily.
diffstat:
src/lib/Makefile.am | 2 +
src/lib/connection.c | 341 +++++++++++++++++++++++++++++++++++++++++++++++++++
src/lib/connection.h | 118 +++++++++++++++++
3 files changed, 461 insertions(+), 0 deletions(-)
diffs (truncated from 486 to 300 lines):
diff -r 523c19238a8b -r 43b841891c77 src/lib/Makefile.am
--- a/src/lib/Makefile.am Sun Jul 08 07:45:17 2012 +0300
+++ b/src/lib/Makefile.am Sun Jul 08 08:59:52 2012 +0300
@@ -22,6 +22,7 @@
child-wait.c \
close-keep-errno.c \
compat.c \
+ connection.c \
crc32.c \
data-stack.c \
eacces-error.c \
@@ -140,6 +141,7 @@
child-wait.h \
close-keep-errno.h \
compat.h \
+ connection.h \
crc32.h \
data-stack.h \
eacces-error.h \
diff -r 523c19238a8b -r 43b841891c77 src/lib/connection.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/connection.c Sun Jul 08 08:59:52 2012 +0300
@@ -0,0 +1,341 @@
+/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "ostream.h"
+#include "network.h"
+#include "strescape.h"
+#include "llist.h"
+#include "connection.h"
+
+#include <unistd.h>
+
+static void connection_idle_timeout(struct connection *conn)
+{
+ conn->disconnect_reason = CONNECTION_DISCONNECT_IDLE_TIMEOUT;
+ conn->list->v.destroy(conn);
+}
+
+static void connection_connect_timeout(struct connection *conn)
+{
+ conn->disconnect_reason = CONNECTION_DISCONNECT_CONNECT_TIMEOUT;
+ conn->list->v.destroy(conn);
+}
+
+void connection_input_default(struct connection *conn)
+{
+ const char *line;
+ struct istream *input;
+ int ret = 0;
+
+ switch (connection_input_read(conn)) {
+ case -1:
+ case 0:
+ return;
+ case 1:
+ break;
+ default:
+ i_unreached();
+ }
+
+ input = conn->input;
+ i_stream_ref(input);
+ while (!input->closed && (line = i_stream_next_line(input)) != NULL) {
+ T_BEGIN {
+ ret = conn->list->v.input_line(conn, line);
+ } T_END;
+ if (ret <= 0)
+ break;
+ }
+ if (ret < 0 && !input->closed) {
+ conn->disconnect_reason = CONNECTION_DISCONNECT_DEINIT;
+ conn->list->v.destroy(conn);
+ }
+ i_stream_unref(&input);
+}
+
+int connection_verify_version(struct connection *conn,
+ const char *const *args)
+{
+ unsigned int recv_major_version;
+
+ /* VERSION <tab> service_name <tab> major version <tab> minor version */
+ if (str_array_length(args) != 4 ||
+ strcmp(args[0], "VERSION") != 0 ||
+ str_to_uint(args[2], &recv_major_version) < 0 ||
+ str_to_uint(args[3], &conn->minor_version) < 0) {
+ i_error("%s didn't reply with a valid VERSION line",
+ conn->name);
+ return -1;
+ }
+
+ if (strcmp(args[1], conn->list->set.service_name_in) != 0) {
+ i_error("%s: Connected to wrong socket type. "
+ "We want '%s', but received '%s'", conn->name,
+ conn->list->set.service_name_in, args[1]);
+ return -1;
+ }
+
+ if (recv_major_version != conn->list->set.major_version) {
+ i_error("%s: Socket supports major version %u, "
+ "but we support only %u (mixed old and new binaries?)",
+ conn->name, recv_major_version,
+ conn->list->set.major_version);
+ return -1;
+ }
+ return 0;
+}
+
+int connection_input_line_default(struct connection *conn, const char *line)
+{
+ const char *const *args;
+
+ args = t_strsplit_tabescaped(line);
+ if (!conn->version_received) {
+ if (connection_verify_version(conn, args) < 0)
+ return -1;
+ conn->version_received = TRUE;
+ }
+
+ return conn->list->v.input_args(conn, args);
+}
+
+static void connection_init_streams(struct connection *conn)
+{
+ const struct connection_settings *set = &conn->list->set;
+
+ i_assert(conn->input == NULL);
+ i_assert(conn->output == NULL);
+ i_assert(conn->to == NULL);
+
+ conn->version_received = set->major_version == 0;
+
+ if (set->input_max_size != 0) {
+ conn->input = i_stream_create_fd(conn->fd_in,
+ set->input_max_size, FALSE);
+ }
+ if (set->output_max_size != 0) {
+ conn->output = o_stream_create_fd(conn->fd_out,
+ set->output_max_size, FALSE);
+ }
+ if (set->input_idle_timeout_secs != 0) {
+ conn->to = timeout_add(set->input_idle_timeout_secs*1000,
+ connection_idle_timeout, conn);
+ }
+ if (set->major_version != 0) {
+ o_stream_send_str(conn->output, t_strdup_printf(
+ "VERSION\t%s\t%u\t%u\n", set->service_name_out,
+ set->major_version, set->minor_version));
+ }
+}
+
+static void connection_init_io(struct connection *conn)
+{
+ i_assert(conn->io == NULL);
+ conn->io = io_add(conn->fd_in, IO_READ, conn->list->v.input, conn);
+}
+
+void connection_init_server(struct connection_list *list,
+ struct connection *conn, const char *name,
+ int fd_in, int fd_out)
+{
+ i_assert(name != NULL);
+ i_assert(!list->set.client);
+
+ conn->list = list;
+ conn->name = i_strdup(name);
+ conn->fd_in = fd_in;
+ conn->fd_out = fd_out;
+ connection_init_io(conn);
+ connection_init_streams(conn);
+
+ DLLIST_PREPEND(&list->connections, conn);
+}
+
+void connection_init_client_ip(struct connection_list *list,
+ struct connection *conn,
+ const struct ip_addr *ip, unsigned int port)
+{
+ i_assert(list->set.client);
+
+ conn->fd_in = conn->fd_out = -1;
+ conn->list = list;
+ conn->name = i_strdup_printf("%s:%u", net_ip2addr(ip), port);
+
+ conn->ip = *ip;
+ conn->port = port;
+
+ DLLIST_PREPEND(&list->connections, conn);
+}
+
+void connection_init_client_unix(struct connection_list *list,
+ struct connection *conn, const char *path)
+{
+ i_assert(list->set.client);
+
+ conn->fd_in = conn->fd_out = -1;
+ conn->list = list;
+ conn->name = i_strdup(path);
+
+ DLLIST_PREPEND(&list->connections, conn);
+}
+
+static void connection_connected(struct connection *conn)
+{
+ io_remove(&conn->io);
+ if (conn->to != NULL)
+ timeout_remove(&conn->to);
+
+ connection_init_io(conn);
+}
+
+int connection_client_connect(struct connection *conn)
+{
+ const struct connection_settings *set = &conn->list->set;
+ int fd;
+
+ i_assert(conn->list->set.client);
+ i_assert(conn->fd_in == -1);
+
+ if (conn->port != 0)
+ fd = net_connect_ip(&conn->ip, conn->port, NULL);
+ else
+ fd = net_connect_unix(conn->name);
+ if (fd == -1)
+ return -1;
+ conn->fd_in = conn->fd_out = fd;
+
+ if (conn->port != 0) {
+ conn->io = io_add(conn->fd_out, IO_WRITE,
+ connection_connected, conn);
+ if (set->client_connect_timeout_msecs != 0) {
+ conn->to = timeout_add(set->client_connect_timeout_msecs,
+ connection_connect_timeout, conn);
+ }
+ } else {
+ connection_init_io(conn);
+ }
+ connection_init_streams(conn);
+ return 0;
+}
+
+void connection_disconnect(struct connection *conn)
+{
+ if (conn->to != NULL)
+ timeout_remove(&conn->to);
+ if (conn->io != NULL)
+ io_remove(&conn->io);
+ if (conn->input != NULL)
+ i_stream_destroy(&conn->input);
+ if (conn->output != NULL)
+ o_stream_destroy(&conn->output);
+ if (conn->fd_in != -1) {
+ if (close(conn->fd_in) < 0)
+ i_error("close(%s) failed: %m", conn->name);
+ if (conn->fd_in != conn->fd_out)
+ i_error("close(%s/out) failed: %m", conn->name);
+ conn->fd_in = conn->fd_out = -1;
+ }
+}
+
+void connection_deinit(struct connection *conn)
+{
+ DLLIST_REMOVE(&conn->list->connections, conn);
+
+ connection_disconnect(conn);
+ i_free(conn->name);
+}
+
+int connection_input_read(struct connection *conn)
+{
+ conn->last_input = ioloop_time;
+ if (conn->to != NULL)
+ timeout_reset(conn->to);
+
+ switch (i_stream_read(conn->input)) {
+ case -2:
+ /* buffer full */
+ switch (conn->list->set.input_full_behavior) {
+ case CONNECTION_BEHAVIOR_DESTROY:
+ conn->disconnect_reason =
+ CONNECTION_DISCONNECT_BUFFER_FULL;
+ conn->list->v.destroy(conn);
+ return -1;
+ case CONNECTION_BEHAVIOR_ALLOW:
+ return -2;
+ }
+ case -1:
+ /* disconnected */
+ conn->disconnect_reason =
+ CONNECTION_DISCONNECT_CONN_CLOSED;
+ conn->list->v.destroy(conn);
+ return -1;
+ case 0:
+ /* nothing new read */
+ return 0;
+ default:
More information about the dovecot-cvs
mailing list