]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-http: server: Implemented blocking request input stream.
authorStephan Bosch <stephan@rename-it.nl>
Wed, 10 Feb 2016 21:32:46 +0000 (22:32 +0100)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Thu, 11 Feb 2016 10:00:42 +0000 (12:00 +0200)
src/lib-http/http-server-connection.c
src/lib-http/http-server-private.h
src/lib-http/http-server-request.c
src/lib-http/http-server.h

index 1f2570fb3545bc945798171d9bbe67b1e6d2c00c..847f80c62e9be99cefdd5237c72809f7c054a6e2 100644 (file)
@@ -1086,11 +1086,18 @@ void http_server_connection_tunnel(struct http_server_connection **_conn,
 
 void http_server_connection_switch_ioloop(struct http_server_connection *conn)
 {
+       if (conn->switching_ioloop)
+               return;
+
+       conn->switching_ioloop = TRUE;
        if (conn->to_input != NULL)
                conn->to_input = io_loop_move_timeout(&conn->to_input);
        if (conn->to_idle != NULL)
                conn->to_idle = io_loop_move_timeout(&conn->to_idle);
        if (conn->io_resp_payload != NULL)
                conn->io_resp_payload = io_loop_move_io(&conn->io_resp_payload);
+       if (conn->incoming_payload != NULL)
+               i_stream_switch_ioloop(conn->incoming_payload);
        connection_switch_ioloop(&conn->conn);
+       conn->switching_ioloop = FALSE;
 }
index 65a0503972cb818c1b4c199a053be96fdceae22c..73788bef68c85c6b25b3bf1bc461079c828ef7d9 100644 (file)
@@ -83,6 +83,8 @@ struct http_server_request {
        struct http_server *server;
        struct http_server_connection *conn;
 
+       struct istream *payload_input;
+
        struct http_server_response *response;
 
        void (*destroy_callback)(void *);
@@ -125,6 +127,7 @@ struct http_server_connection {
        unsigned int input_broken:1;
        unsigned int output_locked:1;
        unsigned int in_req_callback:1;  /* performing request callback (busy) */
+       unsigned int switching_ioloop:1; /* in the middle of switching ioloop */
 };
 
 struct http_server {
index 9232a9473ddb27f7367ee4464fa25d33c7d57d7f..c380217e1089b02c5881eddac21adc8e591eb1c4 100644 (file)
@@ -3,6 +3,7 @@
 #include "lib.h"
 #include "ioloop.h"
 #include "ostream.h"
+#include "istream-private.h"
 
 #include "http-server-private.h"
 
@@ -355,3 +356,151 @@ void http_server_request_fail_auth_basic(struct http_server_request *req,
        http_server_request_fail_auth(req, reason, &chlng);
 }
 
+/*
+ * Payload input stream
+ */
+
+struct http_server_istream {
+       struct istream_private istream;
+
+       struct http_server_request *req;
+
+       ssize_t read_status;
+};
+
+static void
+http_server_istream_switch_ioloop(struct istream_private *stream)
+{
+       struct http_server_istream *hsristream =
+               (struct http_server_istream *)stream;
+
+       if (hsristream->istream.istream.blocking)
+               return;
+
+       http_server_connection_switch_ioloop(hsristream->req->conn);
+}
+
+static void
+http_server_istream_read_any(struct http_server_istream *hsristream)
+{
+       struct istream_private *stream = &hsristream->istream;
+       struct http_server *server = hsristream->req->server;
+       ssize_t ret;
+
+       if ((ret=i_stream_read_copy_from_parent
+               (&stream->istream)) > 0) {
+               hsristream->read_status = ret;
+               io_loop_stop(server->ioloop);
+       }
+}
+
+static ssize_t
+http_server_istream_read(struct istream_private *stream)
+{
+       struct http_server_istream *hsristream =
+               (struct http_server_istream *)stream;
+       struct http_server_request *req = hsristream->req;
+       struct http_server *server;
+       struct http_server_connection *conn;
+       bool blocking = stream->istream.blocking;
+       ssize_t ret;
+
+       if (req == NULL) {
+               /* request already gone (we shouldn't get here) */
+               stream->istream.stream_errno = EINVAL;
+               ret = -1;
+       }
+
+       i_stream_seek(stream->parent, stream->parent_start_offset +
+                     stream->istream.v_offset);
+
+       server = hsristream->req->server;
+       conn = hsristream->req->conn;
+
+       ret = i_stream_read_copy_from_parent(&stream->istream);
+       if (ret == 0 && blocking) {
+               struct ioloop *prev_ioloop = current_ioloop;
+               struct io *io;
+
+               http_server_connection_ref(conn);
+               http_server_request_ref(req);
+
+               i_assert(server->ioloop == NULL);
+               server->ioloop = io_loop_create();
+               http_server_connection_switch_ioloop(conn);
+
+               if (blocking && req->req.expect_100_continue &&
+                       !req->sent_100_continue)
+                       http_server_connection_trigger_responses(conn);
+
+               hsristream->read_status = 0;
+               io = io_add_istream(&stream->istream,
+                       http_server_istream_read_any, hsristream);
+               while (req->state < HTTP_SERVER_REQUEST_STATE_FINISHED &&
+                       hsristream->read_status == 0) {
+                       io_loop_run(server->ioloop);
+               }
+               io_remove(&io);
+
+               io_loop_set_current(prev_ioloop);
+               http_server_connection_switch_ioloop(conn);
+               io_loop_set_current(server->ioloop);
+               io_loop_destroy(&server->ioloop);
+
+               ret = hsristream->read_status;
+
+               http_server_request_unref(&req);
+               if (req == NULL)
+                       hsristream->req = NULL;
+               http_server_connection_unref(&conn);
+       }
+
+       return ret;
+}
+
+static void
+http_server_istream_destroy(struct iostream_private *stream)
+{
+       struct http_server_istream *hsristream =
+               (struct http_server_istream *)stream;
+       uoff_t v_offset;
+
+       v_offset = hsristream->istream.parent_start_offset +
+               hsristream->istream.istream.v_offset;
+       if (hsristream->istream.parent->seekable ||
+               v_offset > hsristream->istream.parent->v_offset) {
+               /* get to same position in parent stream */
+               i_stream_seek(hsristream->istream.parent, v_offset);
+       }
+
+       i_stream_unref(&hsristream->istream.parent);
+}
+
+struct istream *
+http_server_request_get_payload_input(struct http_server_request *req,
+       bool blocking)
+{
+       struct http_server_istream *hsristream;
+       struct istream *payload = req->req.payload;
+
+       i_assert(req->payload_input == NULL);
+
+       hsristream = i_new(struct http_server_istream, 1);
+       hsristream->req = req;
+       hsristream->istream.max_buffer_size =
+               payload->real_stream->max_buffer_size;
+       hsristream->istream.stream_size_passthrough = TRUE;
+
+       hsristream->istream.read = http_server_istream_read;
+       hsristream->istream.switch_ioloop = http_server_istream_switch_ioloop;
+       hsristream->istream.iostream.destroy = http_server_istream_destroy;
+
+       hsristream->istream.istream.readable_fd = FALSE;
+       hsristream->istream.istream.blocking = blocking;
+       hsristream->istream.istream.seekable = FALSE;
+
+       req->payload_input = i_stream_create
+               (&hsristream->istream, payload, i_stream_get_fd(payload));
+       i_stream_unref(&req->req.payload);
+       return req->payload_input;
+}
index 552d2c0da399dfb543c737250fba8dd81c9a552d..82f8197c03b503a45243c368be7577f6a0b4c71a 100644 (file)
@@ -91,6 +91,14 @@ http_server_request_get_response(struct http_server_request *req);
    or because the request was aborted. */
 bool http_server_request_is_finished(struct http_server_request *req);
 
+/* Return input stream for the request's payload. Optionally, this stream
+   can be made blocking. Do *NOT* meddle with the FD of the http_request
+   payload to achieve the same, because protocol violations will result.
+ */
+struct istream *
+http_server_request_get_payload_input(struct http_server_request *req,
+       bool blocking);
+
 /* Get the authentication credentials provided in this request. Returns 0 if
    the Authorization header is absent, returns -1 when that header cannot be
    parsed, and returns 1 otherwise */