http-server-request.c revision 711e8e4c5c5d702dfa062f42a1ede5de14c151c9
7cb128dc4cae2a03a742f63ba7afee23c78e3af0Phil Carmody/* Copyright (c) 2013-2014 Dovecot authors, see the included COPYING file */
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen#include "lib.h"
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen#include "http-server-private.h"
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainenstruct http_server_request *
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainenhttp_server_request_new(struct http_server_connection *conn)
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen{
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen pool_t pool;
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen struct http_server_request *req;
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen pool = pool_alloconly_create("http_message", 4096);
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen req = p_new(pool, struct http_server_request, 1);
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen req->pool = pool;
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen req->refcount = 1;
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen req->conn = conn;
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen req->server = conn->server;
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen http_server_connection_add_request(conn, req);
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen return req;
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen}
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainenvoid http_server_request_ref(struct http_server_request *req)
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen{
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen req->refcount++;
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen}
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainenbool http_server_request_unref(struct http_server_request **_req)
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen{
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen struct http_server_request *req = *_req;
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen struct http_server_connection *conn = req->conn;
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen i_assert(req->refcount > 0);
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen if (--req->refcount > 0)
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen return TRUE;
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen if (req->state < HTTP_SERVER_REQUEST_STATE_FINISHED) {
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen req->state = HTTP_SERVER_REQUEST_STATE_ABORTED;
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen http_server_connection_remove_request(conn, req);
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen }
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen if (req->destroy_callback != NULL) {
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen req->destroy_callback(req->destroy_context);
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen req->destroy_callback = NULL;
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen }
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen if (req->response != NULL)
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen http_server_response_free(req->response);
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen pool_unref(&req->pool);
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen *_req = NULL;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen return FALSE;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen}
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainenvoid http_server_request_destroy(struct http_server_request **_req)
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen{
89b3a2a06a87e66798d21998c8ddf525c3946e7fTimo Sirainen struct http_server_request *req = *_req;
89b3a2a06a87e66798d21998c8ddf525c3946e7fTimo Sirainen
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen if (req->delay_destroy) {
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen req->destroy_pending = TRUE;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen } else if (req->destroy_callback != NULL) {
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen void (*callback)(void *) = req->destroy_callback;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen req->destroy_callback = NULL;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen callback(req->destroy_context);
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen }
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen http_server_request_unref(_req);
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen}
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainenvoid http_server_request_set_destroy_callback(struct http_server_request *req,
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen void (*callback)(void *),
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen void *context)
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen{
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen req->destroy_callback = callback;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen req->destroy_context = context;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen}
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainenvoid http_server_request_abort(struct http_server_request **_req)
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen{
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen struct http_server_request *req = *_req;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen struct http_server_connection *conn = req->conn;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen if (req->state < HTTP_SERVER_REQUEST_STATE_FINISHED) {
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen req->state = HTTP_SERVER_REQUEST_STATE_ABORTED;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen http_server_connection_remove_request(conn, req);
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen }
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen if (req->response != NULL)
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen http_server_response_free(req->response);
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen req->response = NULL;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen req->conn = conn;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen http_server_request_destroy(_req);
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen}
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainenconst struct http_request *
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainenhttp_server_request_get(struct http_server_request *req)
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen{
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen return &req->req;
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen}
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainenpool_t
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainenhttp_server_request_get_pool(struct http_server_request *req)
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen{
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen return req->pool;
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen}
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainenstruct http_server_response *
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainenhttp_server_request_get_response(struct http_server_request *req)
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen{
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen return req->response;
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen}
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainenint http_server_request_get_auth(struct http_server_request *req,
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen struct http_auth_credentials *credentials)
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen{
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen const char *auth;
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen 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)
{
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)) {
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;
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;
resp = http_server_response_create(req, status, reason);
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);
}