Mercurial > dovecot > core-2.2
view src/lib-http/http-server-request.c @ 22711:25d4771ad0fd
lib-storage: mailbox_list_index - indentation cleanup
author | Timo Sirainen <timo.sirainen@dovecot.fi> |
---|---|
date | Thu, 14 Dec 2017 02:10:27 +0200 |
parents | 669845036f77 |
children | cb108f786fb4 |
line wrap: on
line source
/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "ostream.h" #include "istream-private.h" #include "http-server-private.h" /* * Logging */ static inline void http_server_request_debug(struct http_server_request *req, const char *format, ...) ATTR_FORMAT(2, 3); static inline void http_server_request_debug(struct http_server_request *req, const char *format, ...) { struct http_server *server = req->server; va_list args; if (server->set.debug) { va_start(args, format); i_debug("http-server: request %s: %s", http_server_request_label(req), t_strdup_vprintf(format, args)); va_end(args); } } /* * Request */ struct http_server_request * http_server_request_new(struct http_server_connection *conn) { static unsigned int id_counter = 0; pool_t pool; struct http_server_request *req; pool = pool_alloconly_create(MEMPOOL_GROWING"http_server_request", 4096); req = p_new(pool, struct http_server_request, 1); req->pool = pool; req->refcount = 1; req->conn = conn; req->server = conn->server; req->id = ++id_counter; http_server_connection_add_request(conn, req); return req; } void http_server_request_ref(struct http_server_request *req) { i_assert(req->refcount > 0); req->refcount++; } bool http_server_request_unref(struct http_server_request **_req) { struct http_server_request *req = *_req; struct http_server_connection *conn = req->conn; i_assert(req->refcount > 0); *_req = NULL; if (--req->refcount > 0) return TRUE; http_server_request_debug(req, "Free"); if (req->state < HTTP_SERVER_REQUEST_STATE_FINISHED) { req->state = HTTP_SERVER_REQUEST_STATE_ABORTED; http_server_connection_remove_request(conn, req); } if (req->destroy_callback != NULL) { req->destroy_callback(req->destroy_context); req->destroy_callback = NULL; } if (req->response != NULL) http_server_response_free(req->response); pool_unref(&req->pool); return FALSE; } void http_server_request_destroy(struct http_server_request **_req) { struct http_server_request *req = *_req; struct http_server *server = req->server; http_server_request_debug(req, "Destroy"); /* just make sure the request ends in a proper state */ if (req->state < HTTP_SERVER_REQUEST_STATE_FINISHED) req->state = HTTP_SERVER_REQUEST_STATE_ABORTED; if (server->ioloop) io_loop_stop(server->ioloop); if (req->delay_destroy) { req->destroy_pending = TRUE; } else if (req->destroy_callback != NULL) { void (*callback)(void *) = req->destroy_callback; req->destroy_callback = NULL; callback(req->destroy_context); } http_server_request_unref(_req); } void http_server_request_set_destroy_callback(struct http_server_request *req, void (*callback)(void *), void *context) { req->destroy_callback = callback; req->destroy_context = context; } void http_server_request_abort(struct http_server_request **_req, const char *reason) { struct http_server_request *req = *_req; struct http_server_connection *conn = req->conn; if (req->state >= HTTP_SERVER_REQUEST_STATE_FINISHED) return; http_server_request_debug(req, "Abort"); req->conn = NULL; if (req->state < HTTP_SERVER_REQUEST_STATE_FINISHED) { if (conn != NULL) { http_server_connection_remove_request(conn, req); if (!conn->closed) { /* send best-effort response if appropriate */ if (!conn->output_locked && req->state >= HTTP_SERVER_REQUEST_STATE_PROCESSING && req->state < HTTP_SERVER_REQUEST_STATE_SENT_RESPONSE) { static const char *response = "HTTP/1.1 500 Internal Server Error\r\n" "Content-Length: 0\r\n" "\r\n"; (void)o_stream_send(conn->conn.output, response, strlen(response)); } /* close the connection */ http_server_connection_close(&conn, reason); } } req->state = HTTP_SERVER_REQUEST_STATE_ABORTED; } if (req->response != NULL && !req->response->payload_blocking) { http_server_response_free(req->response); req->response = NULL; } http_server_request_destroy(_req); } const struct http_request * http_server_request_get(struct http_server_request *req) { return &req->req; } pool_t http_server_request_get_pool(struct http_server_request *req) { return req->pool; } struct http_server_response * http_server_request_get_response(struct http_server_request *req) { return req->response; } int http_server_request_get_auth(struct http_server_request *req, struct http_auth_credentials *credentials) { const char *auth; auth = http_request_header_get(&req->req, "Authorization"); if (auth == NULL) return 0; if (http_auth_parse_credentials ((const unsigned char *)auth, strlen(auth), credentials) < 0) return -1; return 1; } bool http_server_request_is_finished(struct http_server_request *req) { return req->response != NULL || req->state == HTTP_SERVER_REQUEST_STATE_ABORTED; } void http_server_request_halt_payload(struct http_server_request *req) { i_assert(req->state <= HTTP_SERVER_REQUEST_STATE_QUEUED); req->payload_halted = TRUE; } void http_server_request_continue_payload(struct http_server_request *req) { i_assert(req->state <= HTTP_SERVER_REQUEST_STATE_QUEUED); req->payload_halted = FALSE; if (req->req.expect_100_continue && !req->sent_100_continue) http_server_connection_trigger_responses(req->conn); } void http_server_request_ready_to_respond(struct http_server_request *req) { http_server_request_debug(req, "Ready to respond"); req->state = HTTP_SERVER_REQUEST_STATE_READY_TO_RESPOND; http_server_connection_trigger_responses(req->conn); } void http_server_request_submit_response(struct http_server_request *req) { struct http_server_connection *conn = req->conn; i_assert(conn != NULL && req->response != NULL && req->response->submitted); switch (req->state) { case HTTP_SERVER_REQUEST_STATE_NEW: case HTTP_SERVER_REQUEST_STATE_QUEUED: case HTTP_SERVER_REQUEST_STATE_PAYLOAD_IN: case HTTP_SERVER_REQUEST_STATE_PROCESSING: if (!http_server_request_is_complete(req)) { http_server_request_debug(req, "Not ready to respond"); req->state = HTTP_SERVER_REQUEST_STATE_SUBMITTED_RESPONSE; break; } http_server_request_ready_to_respond(req); break; case HTTP_SERVER_REQUEST_STATE_ABORTED: break; default: i_unreached(); } } void http_server_request_finished(struct http_server_request *req) { struct http_server_connection *conn = req->conn; struct http_server_response *resp = req->response; http_server_tunnel_callback_t tunnel_callback = resp->tunnel_callback; void *tunnel_context = resp->tunnel_context; http_server_request_debug(req, "Finished"); i_assert(req->state < HTTP_SERVER_REQUEST_STATE_FINISHED); req->state = HTTP_SERVER_REQUEST_STATE_FINISHED; http_server_connection_remove_request(conn, req); conn->stats.response_count++; if (tunnel_callback == NULL && (req->req.connection_close || resp->close)) { if (resp->close) { http_server_connection_close(&conn, t_strdup_printf("Server closed connection: %u %s", resp->status, resp->reason)); } else { http_server_connection_close(&conn, "Client requested connection close"); } http_server_request_destroy(&req); return; } http_server_request_destroy(&req); if (tunnel_callback != NULL) { http_server_connection_tunnel(&conn, tunnel_callback, tunnel_context); return; } http_server_connection_trigger_responses(conn); } static struct http_server_response * http_server_request_create_fail_response(struct http_server_request *req, unsigned int status, const char *reason) { struct http_server_response *resp; req->failed = TRUE; i_assert(status / 100 != 1 && status != 204 && status != 304); resp = http_server_response_create(req, status, reason); if (!http_request_method_is(&req->req, "HEAD")) { http_server_response_add_header (resp, "Content-Type", "text/plain; charset=utf-8"); reason = t_strconcat(reason, "\r\n", NULL); http_server_response_set_payload_data (resp, (const unsigned char *)reason, strlen(reason)); } return resp; } static void http_server_request_fail_full(struct http_server_request *req, unsigned int status, const char *reason, bool close) { struct http_server_response *resp; req->failed = TRUE; resp = http_server_request_create_fail_response(req, status, reason); if (close) http_server_response_submit_close(resp); else http_server_response_submit(resp); } void http_server_request_fail(struct http_server_request *req, unsigned int status, const char *reason) { http_server_request_fail_full(req, status, reason, req->conn->input_broken); } void http_server_request_fail_close(struct http_server_request *req, unsigned int status, const char *reason) { http_server_request_fail_full(req, status, reason, TRUE); } void http_server_request_fail_auth(struct http_server_request *req, const char *reason, const struct http_auth_challenge *chlng) { struct http_server_response *resp; req->failed = TRUE; if (reason == NULL) reason = "Unauthenticated"; resp = http_server_request_create_fail_response(req, 401, reason); http_server_response_add_auth(resp, chlng); http_server_response_submit(resp); } void http_server_request_fail_auth_basic(struct http_server_request *req, const char *reason, const char *realm) { struct http_auth_challenge chlng; http_auth_basic_challenge_init(&chlng, realm); 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; return -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; if (!http_server_request_unref(&req)) 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; }