blob: ff04770d66dc284bc02efb4d27542a9434ab5b50 [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file http/server.c HTTP Server
3 *
4 * Copyright (C) 2011 Creytiv.com
5 */
6
7#include <re_types.h>
8#include <re_mem.h>
9#include <re_mbuf.h>
10#include <re_sa.h>
11#include <re_list.h>
12#include <re_fmt.h>
13#include <re_tmr.h>
14#include <re_srtp.h>
15#include <re_tcp.h>
16#include <re_tls.h>
17#include <re_msg.h>
18#include <re_http.h>
19
20
21enum {
22 TIMEOUT_IDLE = 600000,
23 TIMEOUT_INIT = 10000,
24 BUFSIZE_MAX = 524288,
25};
26
27struct http_sock {
28 struct list connl;
29 struct tcp_sock *ts;
30 struct tls *tls;
31 http_req_h *reqh;
32 void *arg;
33};
34
35struct http_conn {
36 struct le le;
37 struct tmr tmr;
38 struct sa peer;
39 struct http_sock *sock;
40 struct tcp_conn *tc;
41 struct tls_conn *sc;
42 struct mbuf *mb;
43};
44
45
46static void conn_close(struct http_conn *conn);
47
48
49static void sock_destructor(void *arg)
50{
51 struct http_sock *sock = arg;
52 struct le *le;
53
54 for (le=sock->connl.head; le;) {
55
56 struct http_conn *conn = le->data;
57
58 le = le->next;
59
60 conn_close(conn);
61 mem_deref(conn);
62 }
63
64 mem_deref(sock->tls);
65 mem_deref(sock->ts);
66}
67
68
69static void conn_destructor(void *arg)
70{
71 struct http_conn *conn = arg;
72
73 list_unlink(&conn->le);
74 tmr_cancel(&conn->tmr);
75 mem_deref(conn->sc);
76 mem_deref(conn->tc);
77 mem_deref(conn->mb);
78}
79
80
81static void conn_close(struct http_conn *conn)
82{
83 list_unlink(&conn->le);
84 tmr_cancel(&conn->tmr);
85 conn->sc = mem_deref(conn->sc);
86 conn->tc = mem_deref(conn->tc);
87 conn->sock = NULL;
88}
89
90
91static void timeout_handler(void *arg)
92{
93 struct http_conn *conn = arg;
94
95 conn_close(conn);
96 mem_deref(conn);
97}
98
99
100static void recv_handler(struct mbuf *mb, void *arg)
101{
102 struct http_conn *conn = arg;
103 int err = 0;
104
105 if (conn->mb) {
106
107 const size_t len = mbuf_get_left(mb), pos = conn->mb->pos;
108
109 if ((mbuf_get_left(conn->mb) + len) > BUFSIZE_MAX) {
110 err = EOVERFLOW;
111 goto out;
112 }
113
114 conn->mb->pos = conn->mb->end;
115
116 err = mbuf_write_mem(conn->mb, mbuf_buf(mb), len);
117 if (err)
118 goto out;
119
120 conn->mb->pos = pos;
121 }
122 else {
123 conn->mb = mem_ref(mb);
124 }
125
126 while (conn->mb) {
127 size_t end, pos = conn->mb->pos;
128 struct http_msg *msg;
129
130 err = http_msg_decode(&msg, conn->mb, true);
131 if (err) {
132 if (err == ENODATA) {
133 conn->mb->pos = pos;
134 err = 0;
135 break;
136 }
137
138 goto out;
139 }
140
141 if (mbuf_get_left(conn->mb) < msg->clen) {
142 conn->mb->pos = pos;
143 mem_deref(msg);
144 break;
145 }
146
147 mem_deref(msg->mb);
148 msg->mb = mem_ref(msg->_mb);
149
150 mb = conn->mb;
151
152 end = mb->end;
153 mb->end = mb->pos + msg->clen;
154
155 if (end > mb->end) {
156 struct mbuf *mbn = mbuf_alloc(end - mb->end);
157 if (!mbn) {
158 mem_deref(msg);
159 err = ENOMEM;
160 goto out;
161 }
162
163 (void)mbuf_write_mem(mbn, mb->buf + mb->end,
164 end - mb->end);
165 mbn->pos = 0;
166
167 mem_deref(conn->mb);
168 conn->mb = mbn;
169 }
170 else {
171 conn->mb = mem_deref(conn->mb);
172 }
173
174 if (conn->sock)
175 conn->sock->reqh(conn, msg, conn->sock->arg);
176
177 mem_deref(msg);
178
179 if (!conn->tc) {
180 err = ENOTCONN;
181 goto out;
182 }
183
184 tmr_start(&conn->tmr, TIMEOUT_IDLE, timeout_handler, conn);
185 }
186
187 out:
188 if (err) {
189 conn_close(conn);
190 mem_deref(conn);
191 }
192}
193
194
195static void close_handler(int err, void *arg)
196{
197 struct http_conn *conn = arg;
198 (void)err;
199
200 conn_close(conn);
201 mem_deref(conn);
202}
203
204
205static void connect_handler(const struct sa *peer, void *arg)
206{
207 struct http_sock *sock = arg;
208 struct http_conn *conn;
209 int err;
210
211 conn = mem_zalloc(sizeof(*conn), conn_destructor);
212 if (!conn) {
213 err = ENOMEM;
214 goto out;
215 }
216
217 list_append(&sock->connl, &conn->le, conn);
218 conn->peer = *peer;
219 conn->sock = sock;
220
221 err = tcp_accept(&conn->tc, sock->ts, NULL, recv_handler,
222 close_handler, conn);
223 if (err)
224 goto out;
225
226#ifdef USE_TLS
227 if (sock->tls) {
228 err = tls_start_tcp(&conn->sc, sock->tls, conn->tc, 0);
229 if (err)
230 goto out;
231 }
232#endif
233
234 tmr_start(&conn->tmr, TIMEOUT_INIT, timeout_handler, conn);
235
236 out:
237 if (err) {
238 mem_deref(conn);
239 tcp_reject(sock->ts);
240 }
241}
242
243
244/**
245 * Create an HTTP socket
246 *
247 * @param sockp Pointer to returned HTTP Socket
248 * @param laddr Network address to listen on
249 * @param reqh Request handler
250 * @param arg Handler argument
251 *
252 * @return 0 if success, otherwise errorcode
253 */
254int http_listen(struct http_sock **sockp, const struct sa *laddr,
255 http_req_h *reqh, void *arg)
256{
257 struct http_sock *sock;
258 int err;
259
260 if (!sockp || !laddr || !reqh)
261 return EINVAL;
262
263 sock = mem_zalloc(sizeof(*sock), sock_destructor);
264 if (!sock)
265 return ENOMEM;
266
267 err = tcp_listen(&sock->ts, laddr, connect_handler, sock);
268 if (err)
269 goto out;
270
271 sock->reqh = reqh;
272 sock->arg = arg;
273
274 out:
275 if (err)
276 mem_deref(sock);
277 else
278 *sockp = sock;
279
280 return err;
281}
282
283
284/**
285 * Create an HTTP secure socket
286 *
287 * @param sockp Pointer to returned HTTP Socket
288 * @param laddr Network address to listen on
289 * @param cert File path of TLS certificate
290 * @param reqh Request handler
291 * @param arg Handler argument
292 *
293 * @return 0 if success, otherwise errorcode
294 */
295int https_listen(struct http_sock **sockp, const struct sa *laddr,
296 const char *cert, http_req_h *reqh, void *arg)
297{
298 struct http_sock *sock;
299 int err;
300
301 if (!sockp || !laddr || !cert || !reqh)
302 return EINVAL;
303
304 err = http_listen(&sock, laddr, reqh, arg);
305 if (err)
306 return err;
307
308#ifdef USE_TLS
309 err = tls_alloc(&sock->tls, TLS_METHOD_SSLV23, cert, NULL);
310#else
311 err = EPROTONOSUPPORT;
312#endif
313 if (err)
314 goto out;
315
316 out:
317 if (err)
318 mem_deref(sock);
319 else
320 *sockp = sock;
321
322 return err;
323}
324
325
326/**
327 * Get the TCP socket of an HTTP socket
328 *
329 * @param sock HTTP socket
330 *
331 * @return TCP socket
332 */
333struct tcp_sock *http_sock_tcp(struct http_sock *sock)
334{
335 return sock ? sock->ts : NULL;
336}
337
338
339/**
340 * Get the peer address of an HTTP connection
341 *
342 * @param conn HTTP connection
343 *
344 * @return Peer address
345 */
346const struct sa *http_conn_peer(const struct http_conn *conn)
347{
348 return conn ? &conn->peer : NULL;
349}
350
351
352/**
353 * Get the TCP connection of an HTTP connection
354 *
355 * @param conn HTTP connection
356 *
357 * @return TCP connection
358 */
359struct tcp_conn *http_conn_tcp(struct http_conn *conn)
360{
361 return conn ? conn->tc : NULL;
362}
363
364
365/**
366 * Get the TLS connection of an HTTP connection
367 *
368 * @param conn HTTP connection
369 *
370 * @return TLS connection
371 */
372struct tls_conn *http_conn_tls(struct http_conn *conn)
373{
374 return conn ? conn->sc : NULL;
375}
376
377
378/**
379 * Close the HTTP connection
380 *
381 * @param conn HTTP connection
382 */
383void http_conn_close(struct http_conn *conn)
384{
385 if (!conn)
386 return;
387
388 conn->sc = mem_deref(conn->sc);
389 conn->tc = mem_deref(conn->tc);
390}
391
392
393static int http_vreply(struct http_conn *conn, uint16_t scode,
394 const char *reason, const char *fmt, va_list ap)
395{
396 struct mbuf *mb;
397 int err;
398
399 if (!conn || !scode || !reason)
400 return EINVAL;
401
402 if (!conn->tc)
403 return ENOTCONN;
404
405 mb = mbuf_alloc(8192);
406 if (!mb)
407 return ENOMEM;
408
409 err = mbuf_printf(mb, "HTTP/1.1 %u %s\r\n", scode, reason);
410 if (fmt)
411 err |= mbuf_vprintf(mb, fmt, ap);
412 else
413 err |= mbuf_write_str(mb, "Content-Length: 0\r\n\r\n");
414 if (err)
415 goto out;
416
417 mb->pos = 0;
418
419 err = tcp_send(conn->tc, mb);
420 if (err)
421 goto out;
422
423 out:
424 mem_deref(mb);
425
426 return err;
427}
428
429
430/**
431 * Send an HTTP response
432 *
433 * @param conn HTTP connection
434 * @param scode Response status code
435 * @param reason Response reason phrase
436 * @param fmt Formatted HTTP message
437 *
438 * @return 0 if success, otherwise errorcode
439 */
440int http_reply(struct http_conn *conn, uint16_t scode, const char *reason,
441 const char *fmt, ...)
442{
443 va_list ap;
444 int err;
445
446 va_start(ap, fmt);
447 err = http_vreply(conn, scode, reason, fmt, ap);
448 va_end(ap);
449
450 return err;
451}
452
453
454/**
455 * Send an HTTP response with content formatting
456 *
457 * @param conn HTTP connection
458 * @param scode Response status code
459 * @param reason Response reason phrase
460 * @param ctype Content type
461 * @param fmt Formatted HTTP content
462 *
463 * @return 0 if success, otherwise errorcode
464 */
465int http_creply(struct http_conn *conn, uint16_t scode, const char *reason,
466 const char *ctype, const char *fmt, ...)
467{
468 struct mbuf *mb;
469 va_list ap;
470 int err;
471
472 if (!ctype || !fmt)
473 return EINVAL;
474
475 mb = mbuf_alloc(8192);
476 if (!mb)
477 return ENOMEM;
478
479 va_start(ap, fmt);
480 err = mbuf_vprintf(mb, fmt, ap);
481 va_end(ap);
482 if (err)
483 goto out;
484
485 err = http_reply(conn, scode, reason,
486 "Content-Type: %s\r\n"
487 "Content-Length: %zu\r\n"
488 "\r\n"
489 "%b",
490 ctype,
491 mb->end,
492 mb->buf, mb->end);
493 if (err)
494 goto out;
495
496 out:
497 mem_deref(mb);
498
499 return err;
500}
501
502
503/**
504 * Send an HTTP error response
505 *
506 * @param conn HTTP connection
507 * @param scode Response status code
508 * @param reason Response reason phrase
509 *
510 * @return 0 if success, otherwise errorcode
511 */
512int http_ereply(struct http_conn *conn, uint16_t scode, const char *reason)
513{
514 return http_creply(conn, scode, reason, "text/html",
515 "<!DOCTYPE html>\n"
516 "<html>\n"
517 "<head><title>%u %s</title></head>\n"
518 "<body><h2>%u %s</h2></body>\n"
519 "</html>\n",
520 scode, reason,
521 scode, reason);
522}