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