summaryrefslogtreecommitdiffstats
path: root/src/lib-http/http-server-private.h
blob: c07d873e736cd83c92d314401dc78c20291139ac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
#ifndef HTTP_SERVER_PRIVATE_H
#define HTTP_SERVER_PRIVATE_H

#include "connection.h"

#include "iostream-pump.h"
#include "http-server.h"
#include "llist.h"

struct http_server_ostream;
struct http_server_payload_handler;
struct http_server_request;
struct http_server_connection;

/*
 * Defaults
 */

#define HTTP_SERVER_REQUEST_MAX_TARGET_LENGTH 4096

/*
 * Types
 */

enum http_server_request_state {
	/* New request; request header is still being parsed. */
	HTTP_SERVER_REQUEST_STATE_NEW = 0,
	/* Queued request; callback to request handler executing. */
	HTTP_SERVER_REQUEST_STATE_QUEUED,
	/* Reading request payload; request handler still needs to read more
	   payload. */
	HTTP_SERVER_REQUEST_STATE_PAYLOAD_IN,
	/* This request is being processed; request payload is fully read, but
	   no response is yet submitted */
	HTTP_SERVER_REQUEST_STATE_PROCESSING,
	/* A response is submitted for this request. If not all request payload
	   was read by the handler, it is first skipped on the input. */
	HTTP_SERVER_REQUEST_STATE_SUBMITTED_RESPONSE,
	/* Request is ready for response; a response is submitted and the
	   request payload is fully read */
	HTTP_SERVER_REQUEST_STATE_READY_TO_RESPOND,
	/* The response for the request is sent (apart from payload) */
	HTTP_SERVER_REQUEST_STATE_SENT_RESPONSE,
	/* Sending response payload to client */
	HTTP_SERVER_REQUEST_STATE_PAYLOAD_OUT,
	/* Request is finished; still lingering due to references */
	HTTP_SERVER_REQUEST_STATE_FINISHED,
	/* Request is aborted; still lingering due to references */
	HTTP_SERVER_REQUEST_STATE_ABORTED
};

/*
 * Objects
 */

struct http_server_payload_handler {
	struct http_server_request *req;

	void (*switch_ioloop)(struct http_server_payload_handler *handler,
			      struct ioloop *ioloop);
	void (*destroy)(struct http_server_payload_handler *handler);

	bool in_callback:1;
};

struct http_server_response {
	struct http_server_request *request;
	struct event *event;

	unsigned int status;
	const char *reason;

	string_t *headers;
	ARRAY_TYPE(string) perm_headers;
	time_t date;
	ARRAY_TYPE(http_auth_challenge) auth_challenges;

	struct istream *payload_input;
	uoff_t payload_size, payload_offset;
	struct ostream *payload_output;
	struct http_server_ostream *payload_stream;

	http_server_tunnel_callback_t tunnel_callback;
	void *tunnel_context;

	bool have_hdr_connection:1;
	bool have_hdr_date:1;
	bool have_hdr_body_spec:1;

	bool payload_chunked:1;
	bool payload_finished:1;
	bool payload_corked:1;
	bool submitted:1;
};

struct http_server_request {
	struct http_request req;
	pool_t pool;
	unsigned int refcount, immune_refcount;
	unsigned int id;
	int callback_refcount;
	struct event *event;
	uoff_t input_start_offset, output_start_offset;

	enum http_server_request_state state;

	struct http_server_request *prev, *next;

	struct http_server *server;
	struct http_server_connection *conn;

	struct istream *payload_input;

	struct http_server_response *response;

	void (*destroy_callback)(void *);
	void *destroy_context;

	bool payload_halted:1;
	bool sent_100_continue:1;
	bool destroy_pending:1;
	bool failed:1;
	bool connection_close:1;
};

struct http_server_connection {
	struct connection conn;
	struct http_server *server;
	struct ioloop *ioloop, *ioloop_switching;
	struct event *event;
	unsigned int refcount;

	const struct http_server_callbacks *callbacks;
	void *context;

	struct timeout *to_input, *to_idle;
	struct ssl_iostream *ssl_iostream;
	struct http_request_parser *http_parser;

	struct http_server_request *request_queue_head, *request_queue_tail;
	unsigned int request_queue_count;

	struct istream *incoming_payload;
	struct http_server_payload_handler *payload_handler;

	struct io *io_resp_payload;

	char *disconnect_reason;

	struct http_server_stats stats;

	bool ssl:1;
	bool closed:1;
	bool close_indicated:1;
	bool input_broken:1;
	bool output_locked:1;
	bool output_halted:1;
	bool in_req_callback:1;  /* performing request callback (busy) */
};

struct http_server_location {
	const char *path;

	struct http_server_resource *resource;
};

struct http_server_resource {
	pool_t pool;
	struct http_server *server;
	struct event *event;

	http_server_resource_callback_t *callback;
	void *context;

	void (*destroy_callback)(void *);
	void *destroy_context;

	ARRAY(struct http_server_location *) locations;
};

struct http_server {
	pool_t pool;

	struct http_server_settings set;

	struct ioloop *ioloop;
	struct event *event;
	struct ssl_iostream_context *ssl_ctx;

	struct connection_list *conn_list;

	ARRAY(struct http_server_resource *) resources;
	ARRAY(struct http_server_location *) locations;

	bool shutting_down:1;    /* shutting down server */
};

/*
 * Response output stream
 */

struct ostream *
http_server_ostream_create(struct http_server_response *resp,
			   size_t max_buffer_size, bool blocking);
bool http_server_ostream_get_size(struct http_server_ostream *hsostream,
				  uoff_t *size_r);
int http_server_ostream_continue(struct http_server_ostream *hsostream);

void http_server_ostream_output_available(
	struct http_server_ostream *hsostream);
void http_server_ostream_response_finished(
	struct http_server_ostream *hsostream);
void http_server_ostream_response_destroyed(
	struct http_server_ostream *hsostream);

struct ostream *
http_server_ostream_get_output(struct http_server_ostream *hsostream);

void http_server_ostream_set_error(struct http_server_ostream *hsostream,
				   int stream_errno, const char *stream_error);

/*
 * Response
 */

void http_server_response_request_free(struct http_server_response *resp);
void http_server_response_request_destroy(struct http_server_response *resp);
void http_server_response_request_abort(struct http_server_response *resp,
					const char *reason);
void http_server_response_request_finished(struct http_server_response *resp);

int http_server_response_send(struct http_server_response *resp);
int http_server_response_send_more(struct http_server_response *resp);
int http_server_response_finish_payload_out(struct http_server_response *resp);

/*
 * Request
 */

static inline bool
http_server_request_is_new(struct http_server_request *req)
{
	return (req->state == HTTP_SERVER_REQUEST_STATE_NEW);
}

static inline bool
http_server_request_version_equals(struct http_server_request *req,
				   unsigned int major, unsigned int minor)
{
	return (req->req.version_major == major &&
		req->req.version_minor == minor);
}

const char *http_server_request_label(struct http_server_request *req);

void http_server_request_update_event(struct http_server_request *req);

struct http_server_request *
http_server_request_new(struct http_server_connection *conn);
void http_server_request_destroy(struct http_server_request **_req);
void http_server_request_abort(struct http_server_request **_req,
			       const char *reason) ATTR_NULL(2);

void http_server_request_immune_ref(struct http_server_request *req);
void http_server_request_immune_unref(struct http_server_request **_req);

bool http_server_request_is_complete(struct http_server_request *req);

void http_server_request_received(struct http_server_request *req);
void http_server_request_callback(struct http_server_request *req);

void http_server_request_halt_payload(struct http_server_request *req);
void http_server_request_continue_payload(struct http_server_request *req);

void http_server_request_submit_response(struct http_server_request *req);

void http_server_request_ready_to_respond(struct http_server_request *req);
void http_server_request_finished(struct http_server_request *req);

/* Payload handler */

void http_server_payload_handler_destroy(
	struct http_server_payload_handler **_handler);
void http_server_payload_handler_switch_ioloop(
	struct http_server_payload_handler *handler, struct ioloop *ioloop);

/*
 * Connection
 */

static inline void
http_server_connection_add_request(struct http_server_connection *conn,
				   struct http_server_request *sreq)
{
	DLLIST2_APPEND(&conn->request_queue_head, &conn->request_queue_tail,
		       sreq);
	conn->request_queue_count++;
}
static inline void
http_server_connection_remove_request(struct http_server_connection *conn,
				      struct http_server_request *sreq)
{
	DLLIST2_REMOVE(&conn->request_queue_head, &conn->request_queue_tail,
		       sreq);
	conn->request_queue_count--;
}

struct connection_list *http_server_connection_list_init(void);

bool http_server_connection_shut_down(struct http_server_connection *conn);

void http_server_connection_input_set_pending(
	struct http_server_connection *conn);
void http_server_connection_input_halt(struct http_server_connection *conn);
void http_server_connection_input_resume(struct http_server_connection *conn);

void http_server_connection_start_idle_timeout(
	struct http_server_connection *conn);
void http_server_connection_reset_idle_timeout(
	struct http_server_connection *conn);
void http_server_connection_stop_idle_timeout(
	struct http_server_connection *conn);

void http_server_connection_handle_output_error(
	struct http_server_connection *conn);

void http_server_connection_output_trigger(struct http_server_connection *conn);
void http_server_connection_output_halt(struct http_server_connection *conn);
void http_server_connection_output_resume(struct http_server_connection *conn);

int http_server_connection_flush(struct http_server_connection *conn);
int http_server_connection_output(struct http_server_connection *conn);

void http_server_connection_tunnel(struct http_server_connection **_conn,
				   http_server_tunnel_callback_t callback,
				   void *context);

bool http_server_connection_pending_payload(
	struct http_server_connection *conn);

/*
 * Resource
 */

int http_server_resource_find(struct http_server *server, const char *path,
			      struct http_server_resource **res_r,
			      const char **sub_path_r) ATTR_NULL(2);

bool http_server_resource_callback(struct http_server_request *req);

/*
 * Server
 */

int http_server_init_ssl_ctx(struct http_server *server, const char **error_r);

#endif