dovecot-2.2: lib-http: Added initial support for server-side HTT...

dovecot at dovecot.org dovecot at dovecot.org
Mon Jul 21 07:56:04 UTC 2014


details:   http://hg.dovecot.org/dovecot-2.2/rev/edcbd75b76ba
changeset: 17629:edcbd75b76ba
user:      Stephan Bosch <stephan at rename-it.nl>
date:      Mon Jul 21 10:54:05 2014 +0300
description:
lib-http: Added initial support for server-side HTTP API.

diffstat:

 TODO                                  |   17 +
 src/lib-http/Makefile.am              |   13 +-
 src/lib-http/http-server-connection.c |  884 ++++++++++++++++++++++++++++++++++
 src/lib-http/http-server-private.h    |  202 +++++++
 src/lib-http/http-server-request.c    |  190 +++++++
 src/lib-http/http-server-response.c   |  376 ++++++++++++++
 src/lib-http/http-server.c            |   70 ++
 src/lib-http/http-server.h            |   93 +++
 8 files changed, 1842 insertions(+), 3 deletions(-)

diffs (truncated from 1900 to 300 lines):

diff -r fbf434ad2485 -r edcbd75b76ba TODO
--- a/TODO	Mon Jul 21 10:53:19 2014 +0300
+++ b/TODO	Mon Jul 21 10:54:05 2014 +0300
@@ -319,3 +319,20 @@
 
  - general
     - things break if next_uid gets to 2^32
+
+ - lib-http:
+    - Client:
+        - Handle HTTP/1.0 servers properly:
+            -> Transfer-Encoding is not allowed
+        - Implement support for priority/deadline-based scheduling.
+          Much like: https://httpd.apache.org/docs/2.2/mod/mod_proxy_balancer.html
+        - Allow handling non-idempotent requests specially
+          (no automatic retry, block pipeline)
+        - Implement support for `Range:' requests.
+        - Implement optional round-robin request scheduling for when
+          host has multiple IPs.
+    - Server:
+        - Implement API structure for virtual hosts and resources. This way,
+          multiple services can coexist independently on the same HTTP server.
+        - Implement support for `Range:' requests.
+    - Review compliance with RFC 7230 and RFC 7231
diff -r fbf434ad2485 -r edcbd75b76ba src/lib-http/Makefile.am
--- a/src/lib-http/Makefile.am	Mon Jul 21 10:53:19 2014 +0300
+++ b/src/lib-http/Makefile.am	Mon Jul 21 10:54:05 2014 +0300
@@ -4,7 +4,8 @@
 	-I$(top_srcdir)/src/lib \
 	-I$(top_srcdir)/src/lib-test \
 	-I$(top_srcdir)/src/lib-dns \
-	-I$(top_srcdir)/src/lib-ssl-iostream
+	-I$(top_srcdir)/src/lib-ssl-iostream \
+	-I$(top_srcdir)/src/lib-master
 
 libhttp_la_SOURCES = \
 	http-date.c \
@@ -23,7 +24,11 @@
 	http-client-peer.c \
 	http-client-queue.c \
 	http-client-host.c \
-	http-client.c
+	http-client.c \
+	http-server-response.c \
+	http-server-request.c \
+	http-server-connection.c \
+	http-server.c
 
 headers = \
 	http-date.h \
@@ -38,7 +43,9 @@
 	http-response.h \
 	http-response-parser.h \
 	http-client-private.h \
-	http-client.h
+	http-client.h \
+	http-server-private.h \
+	http-server.h
 
 pkginc_libdir=$(pkgincludedir)
 pkginc_lib_HEADERS = $(headers)
diff -r fbf434ad2485 -r edcbd75b76ba src/lib-http/http-server-connection.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-http/http-server-connection.c	Mon Jul 21 10:54:05 2014 +0300
@@ -0,0 +1,884 @@
+/* Copyright (c) 2013-2014 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "llist.h"
+#include "array.h"
+#include "str.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "ostream.h"
+#include "connection.h"
+#include "iostream-rawlog.h"
+#include "iostream-ssl.h"
+#include "master-service.h"
+#include "master-service-ssl.h"
+#include "http-date.h"
+#include "http-request-parser.h"
+
+#include "http-server-private.h"
+
+/*
+ * Logging
+ */
+
+static inline void
+http_server_connection_debug(struct http_server_connection *conn,
+	const char *format, ...) ATTR_FORMAT(2, 3);
+
+static inline void
+http_server_connection_debug(struct http_server_connection *conn,
+	const char *format, ...)
+{
+	va_list args;
+
+	if (conn->server->set.debug) {
+
+		va_start(args, format);	
+		i_debug("http-server: conn %s: %s",
+			http_server_connection_label(conn), t_strdup_vprintf(format, args));
+		va_end(args);
+	}
+}
+
+static inline void
+http_server_connection_error(struct http_server_connection *conn,
+	const char *format, ...) ATTR_FORMAT(2, 3);
+
+static inline void
+http_server_connection_error(struct http_server_connection *conn,
+	const char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);	
+	i_error("http-server: conn %s: %s",
+		http_server_connection_label(conn), t_strdup_vprintf(format, args));
+	va_end(args);
+}
+
+static inline void
+http_server_connection_client_error(struct http_server_connection *conn,
+	const char *format, ...) ATTR_FORMAT(2, 3);
+
+static inline void
+http_server_connection_client_error(struct http_server_connection *conn,
+	const char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);	
+	i_info("http-server: conn %s: %s",
+		http_server_connection_label(conn), t_strdup_vprintf(format, args));
+	va_end(args);
+}
+
+
+
+/*
+ * Connection
+ */
+
+static void http_server_connection_input(struct connection *_conn);
+
+static void
+http_server_connection_update_stats(struct http_server_connection *conn)
+{
+	if (conn->conn.input != NULL)
+		conn->stats.input = conn->conn.input->v_offset;
+	if (conn->conn.output != NULL)
+		conn->stats.output = conn->conn.output->offset;
+}
+
+const struct http_server_stats *
+http_server_connection_get_stats(struct http_server_connection *conn)
+{
+	http_server_connection_update_stats(conn);
+	return &conn->stats;
+}
+
+static void
+http_server_connection_input_halt(struct http_server_connection *conn)
+{
+	if (conn->conn.io != NULL)
+		io_remove(&conn->conn.io);
+}
+
+static void
+http_server_connection_input_resume(struct http_server_connection *conn)
+{
+	if (conn->conn.io == NULL && !conn->input_broken && !conn->close_indicated) {
+		conn->conn.io = io_add(conn->conn.fd_in, IO_READ,
+       http_server_connection_input, &conn->conn);
+	}
+}
+
+static void
+http_server_connection_idle_timeout(struct http_server_connection *conn)
+{
+	http_server_connection_client_error(conn, "Disconnected for inactivity");
+	http_server_connection_close(&conn, "Disconnected for inactivity");
+}
+
+static void
+http_server_connection_timeout_stop(struct http_server_connection *conn)
+{
+	if (conn->to_idle != NULL)
+		timeout_remove(&conn->to_idle);
+}
+
+static void
+http_server_connection_timeout_start(struct http_server_connection *conn)
+{
+	if (conn->to_idle == NULL && conn->server->set.max_client_idle_time_msecs > 0) {
+		conn->to_idle = timeout_add(conn->server->set.max_client_idle_time_msecs,
+				      http_server_connection_idle_timeout, conn);
+	}
+}
+
+static void
+http_server_connection_timeout_reset(struct http_server_connection *conn)
+{
+	if (conn->to_idle != NULL)
+		timeout_reset(conn->to_idle);
+}
+
+static void http_server_connection_ready(struct http_server_connection *conn)
+{
+	struct stat st;
+
+	if (conn->server->set.rawlog_dir != NULL &&
+		stat(conn->server->set.rawlog_dir, &st) == 0) {
+		iostream_rawlog_create(conn->server->set.rawlog_dir,
+				       &conn->conn.input, &conn->conn.output);
+	}
+
+	conn->http_parser = http_request_parser_init
+		(conn->conn.input, &conn->server->set.request_limits);
+	o_stream_set_flush_callback(conn->conn.output,
+    http_server_connection_output, conn);
+}
+
+static void http_server_connection_destroy(struct connection *_conn)
+{
+	struct http_server_connection *conn =
+		(struct http_server_connection *)_conn;
+
+	conn->closed = TRUE;
+	http_server_connection_unref(&conn);
+}
+
+static void http_server_payload_finished(struct http_server_connection *conn)
+{
+	timeout_remove(&conn->to_input);
+	http_server_connection_input_resume(conn);
+}
+
+static void
+http_server_payload_destroyed_timeout(struct http_server_connection *conn)
+{
+	http_server_connection_input(&conn->conn);
+}
+
+static void http_server_payload_destroyed(struct http_server_request *req)
+{
+	struct http_server_connection *conn = req->conn;
+	int stream_errno;
+
+	i_assert(conn != NULL);
+	i_assert(conn->request_queue_tail == req ||
+		req->state >= HTTP_SERVER_REQUEST_STATE_FINISHED);
+	i_assert(conn->conn.io == NULL);
+
+	http_server_connection_debug(conn, "Request payload stream destroyed");
+
+	/* caller is allowed to change the socket fd to blocking while reading
+	   the payload. make sure here that it's switched back. */
+	net_set_nonblock(conn->conn.fd_in, TRUE);
+
+	stream_errno = conn->incoming_payload->stream_errno;
+	conn->incoming_payload = NULL;
+
+	/* handle errors in transfer stream */
+	if (req->response == NULL && stream_errno != 0 &&
+		conn->conn.input->stream_errno == 0) {
+		switch (stream_errno) {
+		case EMSGSIZE:
+			conn->input_broken = TRUE;
+			http_server_connection_client_error(conn,
+				"Client sent excessively large request");
+			http_server_request_fail(req, 413, "Payload Too Large", TRUE);
+			return;
+		case EIO:
+			conn->input_broken = TRUE;
+			http_server_connection_client_error(conn,
+				"Client sent invalid request payload");
+			http_server_request_fail(req, 400, "Bad Request", conn->input_broken);
+			return;
+		default:
+			break;
+		}
+	}
+
+	if (req->state < HTTP_SERVER_REQUEST_STATE_PROCESSING) {
+		/* finished reading request */
+		req->state = HTTP_SERVER_REQUEST_STATE_PROCESSING;
+		if (req->response != NULL && req->response->submitted)
+			http_server_request_submit_response(req);
+	}
+
+	/* input stream may have pending input. make sure input handler
+	   gets called (but don't do it directly, since we get get here
+	   somewhere from the API user's code, which we can't really know what
+	   state it is in). this call also triggers sending the next response if
+	   necessary. */
+	if (!conn->input_broken) {
+		conn->to_input =


More information about the dovecot-cvs mailing list