Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
n_http_server.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
27
29
30#include "nilorea/n_common.h"
31#include "nilorea/n_log.h"
32#include "nilorea/n_network.h"
33#include "nilorea/n_str.h"
34
35#include <errno.h>
36#include <pthread.h>
37#include <signal.h>
38#include <stdatomic.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <strings.h>
43#include <time.h>
44#include <unistd.h>
45
46#ifndef __windows__
47#include <poll.h>
48#include <sys/socket.h>
49#endif
50
51#ifdef HAVE_OPENSSL
52#include <openssl/err.h>
53#include <openssl/ssl.h>
54#endif
55
56/* bytes the connection buffer holds: a full head plus room for pipelining */
57#define N_HTTP_SERVER_BUF (N_HTTP_REQ_HEAD_MAX + 16384u)
58/* longest single wait slice, so a stop request is noticed quickly */
59#define N_HTTP_SERVER_SLICE_MS 250
60/* accept loop timeout, ms */
61#define N_HTTP_SERVER_ACCEPT_MS 250
62
63/* Socket error codes: Winsock does not set errno. */
64#ifdef __windows__
65#define _SOCK_ERR() WSAGetLastError()
66#define _SOCK_AGAIN(e) ((e) == WSAEWOULDBLOCK)
67#define _SOCK_INTR(e) ((e) == WSAEINTR)
68#define _SOCK_LEN(n) ((int)(n))
69#else
70#define _SOCK_ERR() errno
71#define _SOCK_AGAIN(e) ((e) == EAGAIN || (e) == EWOULDBLOCK)
72#define _SOCK_INTR(e) ((e) == EINTR)
73#define _SOCK_LEN(n) (n)
74#endif
75
76/* I/O results below 0 */
77#define _IO_CLOSED 0
78#define _IO_ERROR (-1)
79#define _IO_TIMEOUT (-2)
80#define _IO_STOPPED (-3)
81
82/* one peer address and its open connections */
83typedef struct {
84 char ip[64];
85 size_t count;
86} _ip_slot;
87
91#ifdef HAVE_OPENSSL
92 SSL_CTX* ctx;
93#endif
94 atomic_int stop;
95 pthread_mutex_t lock;
96 pthread_cond_t cond;
97 NETWORK** queue; /* accepted connections waiting for a worker */
98 size_t qhead, qlen;
99 pthread_t* threads;
101 /* peer addresses with their open connection count (per-IP cap) */
104};
105
106typedef struct {
109#ifdef HAVE_OPENSSL
110 SSL* ssl;
111#endif
112 char* buf;
113 size_t len;
114 long long deadline; /* current operation's absolute deadline, ms */
115 int idle_phase; /* a stop may interrupt the current wait */
116 /* the request being served */
117 size_t head_len;
118 long long body_left; /* body bytes not read yet */
119 int body_read; /* the handler asked for the body */
120} _conn;
121
122/* Monotonic clock in ms. */
123static long long _now_ms(void) {
124#ifdef __windows__
125 return (long long)GetTickCount64();
126#else
127 struct timespec ts;
128 clock_gettime(CLOCK_MONOTONIC, &ts);
129 return (long long)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
130#endif
131}
132
133static void _stat_add(N_HTTP_SERVER* srv, unsigned long long* field, long long delta) {
134 pthread_mutex_lock(&srv->lock);
135 *field = (unsigned long long)((long long)*field + delta);
136 pthread_mutex_unlock(&srv->lock);
137}
138
139/* Wait until the socket is readable (or writable), the deadline passes, or a
140 * stop is requested during an idle wait. Returns 1, _IO_TIMEOUT, _IO_STOPPED
141 * or _IO_ERROR. */
142static int _wait(_conn* c, int for_write) {
143 for (;;) {
144 long long left = c->deadline - _now_ms();
145 int slice, rc;
146 if (c->idle_phase && atomic_load(&c->srv->stop))
147 return _IO_STOPPED;
148 if (left <= 0)
149 return _IO_TIMEOUT;
150 slice = left > N_HTTP_SERVER_SLICE_MS ? N_HTTP_SERVER_SLICE_MS : (int)left;
151#ifdef __windows__
152 {
153 WSAPOLLFD p;
154 p.fd = c->netw->link.sock;
155 p.events = for_write ? POLLWRNORM : POLLRDNORM;
156 p.revents = 0;
157 rc = WSAPoll(&p, 1, slice);
158 }
159#else
160 {
161 struct pollfd p;
162 p.fd = (int)c->netw->link.sock;
163 p.events = for_write ? POLLOUT : POLLIN;
164 p.revents = 0;
165 rc = poll(&p, 1, slice);
166 }
167#endif
168 if (rc > 0)
169 return 1;
170 if (rc < 0 && !_SOCK_INTR(_SOCK_ERR()))
171 return _IO_ERROR;
172 }
173}
174
175/* Read at most cap bytes. Returns > 0, _IO_CLOSED, or a negative _IO_ code. */
176static long long _recv(_conn* c, char* dst, size_t cap) {
177 if (cap > 1 << 30)
178 cap = 1 << 30;
179 for (;;) {
180#ifdef HAVE_OPENSSL
181 if (c->ssl) {
182 int n = SSL_read(c->ssl, dst, (int)cap), err;
183 if (n > 0)
184 return n;
185 err = SSL_get_error(c->ssl, n);
186 if (err == SSL_ERROR_ZERO_RETURN)
187 return _IO_CLOSED;
188 if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
189 int w = _wait(c, err == SSL_ERROR_WANT_WRITE);
190 if (w != 1)
191 return w;
192 continue;
193 }
194 ERR_clear_error();
195 return err == SSL_ERROR_SYSCALL && n == 0 ? _IO_CLOSED : _IO_ERROR;
196 }
197#endif
198 {
199 ssize_t n = (ssize_t)recv(c->netw->link.sock, dst, _SOCK_LEN(cap), 0);
200 int w, e;
201 if (n > 0)
202 return n;
203 if (n == 0)
204 return _IO_CLOSED;
205 e = _SOCK_ERR();
206 if (_SOCK_INTR(e))
207 continue;
208 if (!_SOCK_AGAIN(e))
209 return _IO_ERROR;
210 w = _wait(c, 0);
211 if (w != 1)
212 return w;
213 }
214 }
215}
216
217/* Discard a SIGPIPE raised by our own write on a closed socket (it is
218 * blocked in worker threads, so it stays pending in this thread). */
219static void _drop_sigpipe(void) {
220#if !defined(__windows__) && defined(__linux__)
221 sigset_t set;
222 struct timespec zero = {0, 0};
223 sigemptyset(&set);
224 sigaddset(&set, SIGPIPE);
225 while (sigtimedwait(&set, NULL, &zero) > 0);
226#endif
227}
228
229/* Write function for n_http_response_write(). */
230static ssize_t _write_fn(void* ctx, const void* buf, size_t len) {
231 _conn* c = (_conn*)ctx;
232 if (len > 1 << 30)
233 len = 1 << 30;
234 for (;;) {
235#ifdef HAVE_OPENSSL
236 if (c->ssl) {
237 int n = SSL_write(c->ssl, buf, (int)len), err;
238 if (n > 0)
239 return n;
240 err = SSL_get_error(c->ssl, n);
241 if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
242 int w = _wait(c, err == SSL_ERROR_WANT_WRITE);
243 if (w != 1)
244 return -1;
245 continue;
246 }
247 ERR_clear_error();
249 return -1;
250 }
251#endif
252 {
253#ifdef MSG_NOSIGNAL
254 ssize_t n = (ssize_t)send(c->netw->link.sock, buf, _SOCK_LEN(len), MSG_NOSIGNAL);
255#else
256 ssize_t n = (ssize_t)send(c->netw->link.sock, (const char*)buf, _SOCK_LEN(len), 0);
257#endif
258 int e = n < 0 ? _SOCK_ERR() : 0;
259 if (n > 0)
260 return n;
261 if (n < 0 && _SOCK_INTR(e))
262 continue;
263 if (n < 0 && _SOCK_AGAIN(e)) {
264 if (_wait(c, 1) != 1)
265 return -1;
266 continue;
267 }
269 return -1;
270 }
271 }
272}
273
274/* Send a short server-generated response and mark the connection closed. */
275static void _send_error(_conn* c, int status) {
276 N_HTTP_RESP* r = n_http_response_new(status);
277 char body[96];
278 if (!r)
279 return;
280 snprintf(body, sizeof(body), "%d %s\n", status, n_http_status_reason(status));
281 n_http_response_set_header(r, "Content-Type", "text/plain; charset=utf-8");
282 n_http_response_set_header(r, "Cache-Control", "no-store");
283 n_http_response_set_header(r, "X-Content-Type-Options", "nosniff");
284 n_http_response_append(r, body, strlen(body));
287 c->idle_phase = 0;
290}
291
292#ifdef HAVE_OPENSSL
293/* Non-blocking TLS handshake under the handshake deadline. */
294static int _handshake(_conn* c) {
295 c->ssl = SSL_new(c->srv->ctx);
296 if (!c->ssl)
297 return FALSE;
298 SSL_set_fd(c->ssl, (int)c->netw->link.sock);
300 c->idle_phase = 1;
301 for (;;) {
302 int rc = SSL_accept(c->ssl), err, w;
303 if (rc == 1)
304 return TRUE;
305 err = SSL_get_error(c->ssl, rc);
306 if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) {
307 ERR_clear_error();
309 return FALSE;
310 }
311 w = _wait(c, err == SSL_ERROR_WANT_WRITE);
312 if (w != 1) {
313 if (w == _IO_TIMEOUT)
314 _stat_add(c->srv, &c->srv->stats.timeouts, 1);
315 return FALSE;
316 }
317 }
318}
319#endif
320
321/* Header value equals token, ASCII case-insensitive (Connection, Expect). */
322static int _value_is(const char* v, size_t n, const char* token) {
323 size_t i, t = strlen(token);
324 if (!v || n != t)
325 return 0;
326 for (i = 0; i < n; i++) {
327 char a = v[i], b = token[i];
328 if (a >= 'A' && a <= 'Z')
329 a = (char)(a - 'A' + 'a');
330 if (a != b)
331 return 0;
332 }
333 return 1;
334}
335
336/* Read up to want body bytes into dst: first the bytes already buffered after
337 * the head, then from the socket. Returns the count or a negative _IO_ code. */
338static long long _body_chunk(_conn* c, char* dst, size_t want) {
339 size_t avail = c->len - c->head_len;
340 if (avail > 0) {
341 size_t take = avail < want ? avail : want;
342 memcpy(dst, c->buf + c->head_len, take);
343 memmove(c->buf + c->head_len, c->buf + c->head_len + take, avail - take);
344 c->len -= take;
345 return (long long)take;
346 }
347 return _recv(c, dst, want);
348}
349
350/* Common checks of the two body readers. Returns TRUE to go on. */
351static int _body_start(N_HTTP_SERVER_REQUEST* req, long long max) {
352 _conn* c = (_conn*)req->conn;
353 req->body_status = 0;
354 if (c->body_read) {
355 req->body_status = 500;
356 return FALSE;
357 }
358 c->body_read = 1;
359 if (req->head.content_length > max || req->head.content_length > c->srv->cfg.max_body) {
360 req->body_status = 413;
361 return FALSE;
362 }
363 {
364 size_t elen = 0;
365 const char* e = n_http_server_header(req, "expect", &elen);
366 if (e && c->body_left > 0) {
367 static const char cont[] = "HTTP/1.1 100 Continue\r\n\r\n";
369 c->idle_phase = 0;
370 if (_write_fn(c, cont, sizeof(cont) - 1) != (ssize_t)(sizeof(cont) - 1)) {
371 req->body_status = 400;
372 return FALSE;
373 }
374 }
375 }
377 c->idle_phase = 0;
378 return TRUE;
379}
380
381static int _body_fail(N_HTTP_SERVER_REQUEST* req, long long rc) {
382 _conn* c = (_conn*)req->conn;
383 if (rc == _IO_TIMEOUT) {
384 req->body_status = 408;
385 _stat_add(c->srv, &c->srv->stats.timeouts, 1);
386 } else {
387 req->body_status = 400;
388 }
389 return FALSE;
390}
391
399int n_http_server_read_body(N_HTTP_SERVER_REQUEST* req, long long max, N_STR** out) {
400 _conn* c;
401 __n_assert(req, return FALSE);
402 __n_assert(out, return FALSE);
403 c = (_conn*)req->conn;
404 if (!_body_start(req, max))
405 return FALSE;
406 if (!*out) {
407 *out = new_nstr((size_t)req->head.content_length + 1);
408 if (!*out) {
409 req->body_status = 500;
410 return FALSE;
411 }
412 } else if (resize_nstr(*out, (*out)->written + (size_t)req->head.content_length + 1) != TRUE) {
413 req->body_status = 500;
414 return FALSE;
415 }
416 while (c->body_left > 0) {
417 long long n = _body_chunk(c, (*out)->data + (*out)->written, (size_t)c->body_left);
418 if (n <= 0)
419 return _body_fail(req, n);
420 (*out)->written += (size_t)n;
421 (*out)->data[(*out)->written] = '\0';
422 c->body_left -= n;
423 }
424 return TRUE;
425}
426
434int n_http_server_read_body_to_fd(N_HTTP_SERVER_REQUEST* req, long long max, int fd) {
435 _conn* c;
436 char chunk[16384];
437 __n_assert(req, return FALSE);
438 c = (_conn*)req->conn;
439 if (fd < 0) {
440 req->body_status = 500;
441 return FALSE;
442 }
443 if (!_body_start(req, max))
444 return FALSE;
445 while (c->body_left > 0) {
446 size_t want = c->body_left < (long long)sizeof(chunk) ? (size_t)c->body_left : sizeof(chunk);
447 long long n = _body_chunk(c, chunk, want);
448 size_t done = 0;
449 if (n <= 0)
450 return _body_fail(req, n);
451 while (done < (size_t)n) {
452 ssize_t w = write(fd, chunk + done, (size_t)n - done);
453 if (w < 0 && errno == EINTR)
454 continue;
455 if (w <= 0) {
456 req->body_status = 500;
457 return FALSE;
458 }
459 done += (size_t)w;
460 }
461 c->body_left -= n;
462 }
463 return TRUE;
464}
465
473const char* n_http_server_header(const N_HTTP_SERVER_REQUEST* req, const char* name, size_t* len) {
474 if (!req)
475 return NULL;
476 return n_http_request_header(&req->head, req->raw, name, len);
477}
478
479/* Is there a CRLFCRLF in the first n bytes? (a head too long ends without one) */
480static int _has_head_end(const char* b, size_t n) {
481 size_t i;
482 for (i = 0; i + 3 < n; i++)
483 if (b[i] == '\r' && b[i + 1] == '\n' && b[i + 2] == '\r' && b[i + 3] == '\n')
484 return 1;
485 return 0;
486}
487
488/* Does the buffer start with a well-formed method other than GET, HEAD and
489 * POST? RFC 9110 section 9.1: such a method is answered 501, not 400. */
490static int _unsupported_method(const char* b, size_t n) {
491 size_t i;
492 for (i = 0; i < n && i <= 20; i++) {
493 if (b[i] == ' ')
494 break;
495 if (!((b[i] >= 'A' && b[i] <= 'Z') || b[i] == '-' || b[i] == '_'))
496 return 0;
497 }
498 if (i == 0 || i >= n || b[i] != ' ')
499 return 0;
500 return !((i == 3 && memcmp(b, "GET", 3) == 0) || (i == 4 && (memcmp(b, "HEAD", 4) == 0 || memcmp(b, "POST", 4) == 0)));
501}
502
503/* Is the head complete, with exactly one Content-Length, a well-formed
504 * number above every body cap (N_HTTP_REQ_BODY_LIMIT_MAX)? RFC 9110 section
505 * 15.5.14: too large a body is a 413, not a malformed request. */
506static int _content_too_large(const char* b, size_t n) {
507 size_t i = 0, found = 0, end;
508 int too_large = 0;
509 for (end = 0; end + 3 < n; end++)
510 if (b[end] == '\r' && b[end + 1] == '\n' && b[end + 2] == '\r' && b[end + 3] == '\n')
511 break;
512 if (end + 3 >= n)
513 return 0;
514 /* skip the request line */
515 while (i + 1 < end && !(b[i] == '\r' && b[i + 1] == '\n'))
516 i++;
517 while (i + 2 <= end) {
518 size_t ls = i + 2, le = ls, v, ve, digits = 0;
519 unsigned long long val = 0;
520 while (le + 1 < end + 2 && !(b[le] == '\r' && b[le + 1] == '\n'))
521 le++;
522 i = le;
523 if (le - ls < 15 || strncasecmp(b + ls, "content-length:", 15) != 0)
524 continue;
525 found++;
526 v = ls + 15;
527 ve = le;
528 while (v < ve && (b[v] == ' ' || b[v] == '\t'))
529 v++;
530 while (ve > v && (b[ve - 1] == ' ' || b[ve - 1] == '\t'))
531 ve--;
532 if (v == ve)
533 return 0;
534 for (; v < ve; v++) {
535 if (b[v] < '0' || b[v] > '9')
536 return 0;
537 if (++digits > 19) {
538 too_large = 1;
539 continue;
540 }
541 val = val * 10u + (unsigned)(b[v] - '0');
542 }
543 if (val > (unsigned long long)N_HTTP_REQ_BODY_LIMIT_MAX)
544 too_large = 1;
545 if (i >= end)
546 break;
547 }
548 return found == 1 && too_large;
549}
550
551/* Serve every request of one connection. */
552static void _serve(_conn* c) {
553 N_HTTP_SERVER* srv = c->srv;
555 size_t served = 0;
556
559 lim.require_host = srv->cfg.require_host;
560
561 while (served < srv->cfg.max_requests_per_conn) {
562 N_HTTP_SERVER_REQUEST* req = NULL;
563 N_HTTP_RESP* resp = NULL;
565 int keep, got_first = c->len > 0;
566 size_t clen = 0;
567 const char* conn_hdr;
568
569 /* wait for, then read, the whole head */
570 c->idle_phase = !got_first;
571 c->deadline = _now_ms() + (got_first ? srv->cfg.header_timeout_ms : (served == 0 ? srv->cfg.header_timeout_ms : srv->cfg.idle_timeout_ms));
573 if (!req)
574 return;
575 for (;;) {
576 long long n;
577 st = n_http_request_parse_ex(c->buf, c->len, &lim, &req->head);
578 if (st != N_HTTP_REQ_INCOMPLETE)
579 break;
580 n = _recv(c, c->buf + c->len, N_HTTP_SERVER_BUF - c->len);
581 if (n <= 0) {
582 if (n == _IO_TIMEOUT && got_first) {
583 _stat_add(srv, &srv->stats.timeouts, 1);
584 _send_error(c, 408);
585 } else if (n == _IO_TIMEOUT) {
586 _stat_add(srv, &srv->stats.timeouts, 1);
587 }
588 Free(req);
589 return;
590 }
591 if (!got_first) {
592 got_first = 1;
593 c->idle_phase = 0;
595 }
596 c->len += (size_t)n;
597 }
598 if (st == N_HTTP_REQ_ERROR) {
599 int status = 400;
601 status = 431;
602 else if (_unsupported_method(c->buf, c->len))
603 status = 501;
604 else if (_content_too_large(c->buf, c->len))
605 status = 413;
606 _stat_add(srv, &srv->stats.bad_requests, 1);
607 _send_error(c, status);
608 Free(req);
609 return;
610 }
611
612 /* the request as the handler sees it */
613 c->head_len = req->head.head_len;
614 c->body_left = req->head.content_length;
615 c->body_read = 0;
616 req->raw = c->buf;
617 req->peer_ip = c->netw->link.ip ? c->netw->link.ip : "";
618#ifdef HAVE_OPENSSL
619 req->tls = c->ssl != NULL;
620#endif
621 req->conn = c;
622 {
623 const char* q = strchr(req->head.path, '?');
624 size_t plen = q ? (size_t)(q - req->head.path) : strlen(req->head.path);
625 memcpy(req->target, req->head.path, plen);
626 req->target[plen] = '\0';
627 req->path = req->target;
628 req->path_len = plen;
629 if (q) {
630 size_t qlen = strlen(q + 1);
631 memcpy(req->target + plen + 1, q + 1, qlen + 1);
632 req->query = req->target + plen + 1;
633 req->query_len = qlen;
634 } else {
635 req->target[plen + 1] = '\0';
636 req->query = req->target + plen + 1;
637 req->query_len = 0;
638 }
639 }
640 if (req->head.content_length > srv->cfg.max_body) {
641 _stat_add(srv, &srv->stats.bad_requests, 1);
642 _send_error(c, 413);
643 Free(req);
644 return;
645 }
646 {
647 size_t elen = 0;
648 const char* e = n_http_server_header(req, "expect", &elen);
649 if (e && !_value_is(e, elen, "100-continue")) {
650 _stat_add(srv, &srv->stats.bad_requests, 1);
651 _send_error(c, 417);
652 Free(req);
653 return;
654 }
655 }
656
657 resp = srv->cfg.handler ? srv->cfg.handler(req, srv->cfg.user) : NULL;
658 if (!resp)
659 resp = n_http_response_new(500);
660 if (!resp) {
661 Free(req);
662 return;
663 }
664
665 /* keep the connection only when the request was fully consumed */
666 conn_hdr = n_http_server_header(req, "connection", &clen);
667 if (req->head.version_minor == 1)
668 keep = !_value_is(conn_hdr, clen, "close");
669 else
670 keep = _value_is(conn_hdr, clen, "keep-alive");
671 if (c->body_left > 0 || atomic_load(&srv->stop) || served + 1 >= srv->cfg.max_requests_per_conn || resp->status == 400 ||
672 resp->status == 408 || resp->status == 413)
673 keep = 0;
675 c->deadline = _now_ms() + srv->cfg.write_timeout_ms;
676 c->idle_phase = 0;
677 if (!n_http_response_write(resp, _write_fn, c))
678 keep = 0;
679 _stat_add(srv, &srv->stats.requests, 1);
681 Free(req);
682 served++;
683 if (!keep)
684 return;
685 /* drop this request's head; any bytes after it are the next request */
686 memmove(c->buf, c->buf + c->head_len, c->len - c->head_len);
687 c->len -= c->head_len;
688 }
689}
690
691/* Per-IP accounting, under srv->lock. Returns TRUE when the address may open
692 * one more connection. There are 2 * workers slots, more than the connections
693 * that can be open or queued at once. */
694static int _ip_take(N_HTTP_SERVER* srv, const char* ip) {
695 size_t i, slots = srv->cfg.workers * 2, free_slot = slots;
696 if (!srv->cfg.max_per_ip)
697 return TRUE;
698 for (i = 0; i < slots; i++) {
699 if (srv->ips[i].count && strcmp(srv->ips[i].ip, ip) == 0) {
700 if (srv->ips[i].count >= srv->cfg.max_per_ip)
701 return FALSE;
702 srv->ips[i].count++;
703 return TRUE;
704 }
705 if (!srv->ips[i].count && free_slot == slots)
706 free_slot = i;
707 }
708 if (free_slot == slots)
709 return FALSE;
710 snprintf(srv->ips[free_slot].ip, sizeof(srv->ips[free_slot].ip), "%s", ip);
711 srv->ips[free_slot].count = 1;
712 return TRUE;
713}
714
715static void _ip_release(N_HTTP_SERVER* srv, const char* ip) {
716 size_t i;
717 if (!srv->cfg.max_per_ip)
718 return;
719 for (i = 0; i < srv->cfg.workers * 2; i++)
720 if (srv->ips[i].count && strcmp(srv->ips[i].ip, ip) == 0) {
721 srv->ips[i].count--;
722 return;
723 }
724}
725
726static void* _worker(void* arg) {
727 N_HTTP_SERVER* srv = (N_HTTP_SERVER*)arg;
728 char* buf = NULL;
729#if !defined(__windows__)
730 sigset_t set;
731 sigemptyset(&set);
732 sigaddset(&set, SIGPIPE);
733 pthread_sigmask(SIG_BLOCK, &set, NULL);
734#endif
735 Malloc(buf, char, N_HTTP_SERVER_BUF + 1);
736 if (!buf)
737 return NULL;
738 for (;;) {
739 NETWORK* netw = NULL;
740 _conn c;
741 pthread_mutex_lock(&srv->lock);
742 while (srv->qlen == 0 && !atomic_load(&srv->stop))
743 pthread_cond_wait(&srv->cond, &srv->lock);
744 if (srv->qlen == 0) {
745 pthread_mutex_unlock(&srv->lock);
746 break;
747 }
748 netw = srv->queue[srv->qhead];
749 srv->qhead = (srv->qhead + 1) % srv->cfg.workers;
750 srv->qlen--;
751 srv->stats.active++;
752 pthread_mutex_unlock(&srv->lock);
753
754 memset(&c, 0, sizeof(c));
755 c.srv = srv;
756 c.netw = netw;
757 c.buf = buf;
759#ifdef HAVE_OPENSSL
760 if (srv->ctx && !_handshake(&c)) {
761 _stat_add(srv, &srv->stats.tls_failures, 1);
762 } else
763#endif
764 {
765 _serve(&c);
766 }
767#ifdef HAVE_OPENSSL
768 if (c.ssl) {
769 SSL_shutdown(c.ssl); /* one try: the socket is non-blocking */
770 ERR_clear_error();
772 SSL_free(c.ssl);
773 }
774#endif
775 pthread_mutex_lock(&srv->lock);
776 _ip_release(srv, netw->link.ip ? netw->link.ip : "");
777 srv->stats.active--;
778 pthread_mutex_unlock(&srv->lock);
780 }
781 Free(buf);
782 return NULL;
783}
784
790 __n_assert(cfg, return);
791 memset(cfg, 0, sizeof(*cfg));
792 cfg->addr = "127.0.0.1";
793 cfg->port = "8080";
794 cfg->workers = 32;
795 cfg->handshake_timeout_ms = 10000;
796 cfg->header_timeout_ms = 10000;
797 cfg->idle_timeout_ms = 15000;
798 cfg->body_timeout_ms = 60000;
799 cfg->write_timeout_ms = 60000;
800 cfg->max_requests_per_conn = 1000;
801 cfg->max_body = 32LL * 1024 * 1024;
802 cfg->require_host = 1;
803}
804
811 N_HTTP_SERVER* srv = NULL;
812 __n_assert(cfg, return NULL);
813 if (!cfg->addr || !cfg->port || !cfg->handler || cfg->workers == 0 || cfg->workers > 4096 || cfg->handshake_timeout_ms <= 0 ||
814 cfg->header_timeout_ms <= 0 || cfg->idle_timeout_ms <= 0 || cfg->body_timeout_ms <= 0 || cfg->write_timeout_ms <= 0 ||
815 cfg->max_requests_per_conn == 0 || cfg->max_body < 0 || cfg->max_body > N_HTTP_REQ_BODY_LIMIT_MAX ||
816 (!cfg->tls_key_file) != (!cfg->tls_cert_file)) {
817 n_log(LOG_ERR, "http server: invalid configuration");
818 return NULL;
819 }
820 Malloc(srv, N_HTTP_SERVER, 1);
821 __n_assert(srv, return NULL);
822 srv->cfg = *cfg;
823 atomic_init(&srv->stop, 0);
824 pthread_mutex_init(&srv->lock, NULL);
825 pthread_cond_init(&srv->cond, NULL);
826 Malloc(srv->queue, NETWORK*, cfg->workers);
827 Malloc(srv->threads, pthread_t, cfg->workers);
828 Malloc(srv->ips, _ip_slot, cfg->workers * 2);
829 if (!srv->queue || !srv->threads || !srv->ips) {
830 n_http_server_free(&srv);
831 return NULL;
832 }
833 if (cfg->tls_key_file) {
834#ifdef HAVE_OPENSSL
836 if (!srv->ctx) {
837 n_http_server_free(&srv);
838 return NULL;
839 }
840#else
841 n_log(LOG_ERR, "http server: TLS requested but the library was built without OpenSSL");
842 n_http_server_free(&srv);
843 return NULL;
844#endif
845 }
846 if (netw_make_listening(&srv->listen, (char*)cfg->addr, (char*)cfg->port, 128, NETWORK_IPALL) != TRUE || !srv->listen) {
847 n_log(LOG_ERR, "http server: cannot listen on %s:%s", cfg->addr, cfg->port);
848 n_http_server_free(&srv);
849 return NULL;
850 }
851 return srv;
852}
853
859 if (srv)
860 atomic_store(&srv->stop, 1);
861}
862
869 size_t i;
870 __n_assert(srv, return FALSE);
871 __n_assert(srv->listen, return FALSE);
872 for (i = 0; i < srv->cfg.workers; i++) {
873 if (pthread_create(&srv->threads[i], NULL, _worker, srv) != 0) {
874 n_log(LOG_ERR, "http server: cannot start worker %zu", i);
875 atomic_store(&srv->stop, 1);
876 break;
877 }
878 srv->nb_threads++;
879 }
880 while (!atomic_load(&srv->stop)) {
881 int rv = 0;
883 int queued = 0;
884 if (!netw)
885 continue;
886 pthread_mutex_lock(&srv->lock);
887 srv->stats.accepted++;
888 if (srv->qlen < srv->cfg.workers && !atomic_load(&srv->stop) && _ip_take(srv, netw->link.ip ? netw->link.ip : "")) {
889 srv->queue[(srv->qhead + srv->qlen) % srv->cfg.workers] = netw;
890 srv->qlen++;
891 queued = 1;
892 pthread_cond_signal(&srv->cond);
893 } else {
894 srv->stats.refused++;
895 }
896 pthread_mutex_unlock(&srv->lock);
897 if (!queued) {
898 /* over capacity: a plain-HTTP peer gets a 503, a TLS one a reset */
899 if (
900#ifdef HAVE_OPENSSL
901 !srv->ctx
902#else
903 1
904#endif
905 ) {
906 static const char busy[] = "HTTP/1.1 503 Service Unavailable\r\nContent-Length: 0\r\nConnection: close\r\nRetry-After: 1\r\n\r\n";
907#ifdef MSG_NOSIGNAL
908 send(netw->link.sock, busy, sizeof(busy) - 1, MSG_NOSIGNAL | MSG_DONTWAIT);
909#else
910 send(netw->link.sock, busy, sizeof(busy) - 1, 0);
911#endif
912 }
914 }
915 }
916 pthread_mutex_lock(&srv->lock);
917 pthread_cond_broadcast(&srv->cond);
918 pthread_mutex_unlock(&srv->lock);
919 for (i = 0; i < srv->nb_threads; i++)
920 pthread_join(srv->threads[i], NULL);
921 srv->nb_threads = 0;
922 /* connections still queued when the workers left */
923 while (srv->qlen > 0) {
924 netw_close(&srv->queue[srv->qhead]);
925 srv->qhead = (srv->qhead + 1) % srv->cfg.workers;
926 srv->qlen--;
927 }
928 return TRUE;
929}
930
936 if (!srv || !*srv)
937 return;
938 if ((*srv)->listen)
939 netw_close(&(*srv)->listen);
940#ifdef HAVE_OPENSSL
941 if ((*srv)->ctx)
942 SSL_CTX_free((*srv)->ctx);
943#endif
944 FreeNoLog((*srv)->queue);
945 FreeNoLog((*srv)->threads);
946 FreeNoLog((*srv)->ips);
947 pthread_mutex_destroy(&(*srv)->lock);
948 pthread_cond_destroy(&(*srv)->cond);
949 FreeNoLog(*srv);
950}
951
958 __n_assert(srv, return);
959 __n_assert(out, return);
960 pthread_mutex_lock(&srv->lock);
961 *out = srv->stats;
962 pthread_mutex_unlock(&srv->lock);
963}
NETWORK * netw
Network for server mode, accepting incomming.
Definition ex_network.c:39
volatile sig_atomic_t done
#define FreeNoLog(__ptr)
Free Handler without log.
Definition n_common.h:272
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
Definition n_common.h:204
#define __n_assert(__ptr, __ret)
macro to assert things
Definition n_common.h:279
#define endif
close a ifwhatever block
Definition n_common.h:325
#define Free(__ptr)
Free Handler to get errors.
Definition n_common.h:263
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:113
#define LOG_ERR
error conditions
Definition n_log.h:100
int require_host
non-zero: an HTTP/1.1 request must carry exactly one Host header and an HTTP/1.0 one at most one (RFC...
Definition n_http_req.h:105
long long body_max
largest accepted Content-Length, 0 to N_HTTP_REQ_BODY_LIMIT_MAX
Definition n_http_req.h:102
unsigned methods
accepted methods, an OR of N_HTTP_REQ_METHOD_BIT(); any other method is an error
Definition n_http_req.h:100
char path[2048u+1]
Definition n_http_req.h:119
long long content_length
Definition n_http_req.h:120
n_http_req_method method
Definition n_http_req.h:118
#define N_HTTP_REQ_METHOD_BIT(m)
Bit of a method in n_http_req_limits.methods.
Definition n_http_req.h:84
n_http_req_status n_http_request_parse_ex(const char *buf, size_t len, const n_http_req_limits *limits, n_http_request *out)
parse an HTTP/1.1 request head with caller-chosen limits
Definition n_http_req.c:110
#define N_HTTP_REQ_HEAD_MAX
Largest request head (request line + headers, through CRLFCRLF) accepted.
Definition n_http_req.h:63
n_http_req_status
Result of n_http_request_parse.
Definition n_http_req.h:130
const char * n_http_request_header(const n_http_request *req, const char *buf, const char *name, size_t *value_len)
find a header of a parsed request
Definition n_http_req.c:253
#define N_HTTP_REQ_BODY_LIMIT_MAX
Highest body cap n_http_request_parse_ex() accepts (1 GiB).
Definition n_http_req.h:72
@ N_HTTP_REQ_INCOMPLETE
no CRLFCRLF yet, still under the cap
Definition n_http_req.h:132
@ N_HTTP_REQ_ERROR
malformed / oversized / unsupported
Definition n_http_req.h:133
@ N_HTTP_REQ_POST
Definition n_http_req.h:79
@ N_HTTP_REQ_GET
Definition n_http_req.h:78
@ N_HTTP_REQ_HEAD
Definition n_http_req.h:80
Limits for n_http_request_parse_ex().
Definition n_http_req.h:97
int status
status code, 100..599
int n_http_response_append(N_HTTP_RESP *r, const void *data, size_t len)
append bytes to the memory body (drops a file body)
const char * n_http_status_reason(int status)
reason phrase of a status code, a class phrase for an unlisted one
int n_http_response_set_header(N_HTTP_RESP *r, const char *name, const char *value)
set a header: remove every header of that name, then add it
int n_http_response_write(const N_HTTP_RESP *r, n_http_write_fn fn, void *ctx)
write the whole response (head, then body unless HEAD or bodiless)
int n_http_response_set_connection(N_HTTP_RESP *r, int keep_alive, int version_minor, int head_only)
connection handling for this response
void n_http_response_free(N_HTTP_RESP **r)
free a response (closing its file body) and set *r to NULL
N_HTTP_RESP * n_http_response_new(int status)
new response with a status, keep-alive off, HTTP/1.1
a response under construction (fields are private, use the functions)
int body_timeout_ms
body deadline, ms (default 60000)
int header_timeout_ms
request head deadline from its first byte, ms (default 10000)
unsigned long long requests
requests answered
int tls
non-zero over TLS
unsigned long long timeouts
connections ended by a deadline
int body_status
after a failed body read: the status to answer (408, 413...)
char target[2048u+2]
private: storage for path and query
const char * raw
the raw head, for n_http_server_header()
size_t max_requests_per_conn
requests served on one connection before it is closed (default 1000)
unsigned long long active
connections being served now
size_t workers
worker threads = most concurrent connections (default 32)
const char * port
port to bind ("7430")
int write_timeout_ms
deadline to write one response, ms (default 60000)
unsigned long long accepted
connections accepted
size_t query_len
length of query
long long max_body
largest Content-Length accepted at all (default 32 MiB); handlers pass their own, lower cap to the bo...
const char * tls_key_file
PEM private key, NULL for plain HTTP.
const char * tls_cert_file
PEM certificate chain, NULL for plain HTTP.
const char * path
the path, without the query string, still percent-encoded
n_http_server_handler handler
the application
const char * addr
address to bind ("127.0.0.1")
size_t max_per_ip
most connections from one peer address, 0 for no limit (default 0: behind a reverse proxy every conne...
n_http_request head
the parsed head (method, version_minor, content_length, headers)
size_t path_len
length of path
void * conn
private: the connection
int require_host
refuse HTTP/1.1 requests without exactly one Host (default 1)
unsigned long long refused
connections refused (queue full, per-IP cap)
const char * peer_ip
peer address as text
const char * query
the query string without '?
void * user
passed to the handler
int idle_timeout_ms
wait for the next request on a kept-alive connection, ms (default 15000)
unsigned long long tls_failures
failed TLS handshakes
unsigned long long bad_requests
400 / 413 / 417 / 431 answered by the server
int handshake_timeout_ms
TLS handshake deadline, ms (default 10000).
void n_http_server_stats(N_HTTP_SERVER *srv, N_HTTP_SERVER_STATS *out)
copy of the counters
int n_http_server_read_body_to_fd(N_HTTP_SERVER_REQUEST *req, long long max, int fd)
read the whole body into an open file descriptor, refusing more than max
int n_http_server_run(N_HTTP_SERVER *srv)
serve until n_http_server_stop()
const char * n_http_server_header(const N_HTTP_SERVER_REQUEST *req, const char *name, size_t *len)
a request header, see n_http_request_header()
N_HTTP_SERVER * n_http_server_new(const N_HTTP_SERVER_CONFIG *cfg)
bind the listening socket and load the TLS context
void n_http_server_stop(N_HTTP_SERVER *srv)
ask the server to stop (async-signal-safe)
int n_http_server_read_body(N_HTTP_SERVER_REQUEST *req, long long max, N_STR **out)
read the whole body into memory, refusing more than max
void n_http_server_free(N_HTTP_SERVER **srv)
free a stopped server and set *srv to NULL
void n_http_server_config_defaults(N_HTTP_SERVER_CONFIG *cfg)
fill a config with the documented defaults
server settings; start from n_http_server_config_defaults()
a request as the handler sees it (fields are read-only)
counters, see n_http_server_stats()
size_t written
number of meaningful bytes in data, excluding the null terminator; the size including the null termin...
Definition n_str.h:68
int resize_nstr(N_STR *nstr, size_t size)
reallocate a nstr internal buffer.
Definition n_str.c:1628
N_STR * new_nstr(NSTRBYTE size)
create a new N_STR string
Definition n_str.c:209
A box including a string and his lenght.
Definition n_str.h:61
char * ip
ip of the connected socket
Definition n_network.h:358
N_SOCKET link
networking socket
Definition n_network.h:454
SOCKET sock
a normal socket
Definition n_network.h:356
int netw_make_listening(NETWORK **netw, char *addr, char *port, int nbpending, int ip_version)
Make a NETWORK be a Listening network.
Definition n_network.c:3602
NETWORK * netw_accept_from_ex(NETWORK *from, size_t send_list_limit, size_t recv_list_limit, int blocking, int *retval)
make a normal 'accept' .
Definition n_network.c:4101
#define NETWORK_IPALL
Flag for auto detection by OS of ip version to use.
Definition n_network.h:48
SSL_CTX * netw_ssl_server_ctx_new(const char *key_file, const char *cert_chain_file)
new standalone server SSL_CTX with the library's secure defaults
Definition n_network.c:1449
int netw_close(NETWORK **netw)
Closing a specified Network, destroy queues, free the structure.
Definition n_network.c:3367
int netw_set_blocking(NETWORK *netw, unsigned long int is_blocking)
Modify blocking socket mode.
Definition n_network.c:911
Structure of a NETWORK.
Definition n_network.h:375
Common headers and low-level functions & define.
pthread_mutex_t lock
char * buf
#define N_HTTP_SERVER_BUF
long long body_left
static void _drop_sigpipe(void)
static int _handshake(_conn *c)
int body_read
static ssize_t _write_fn(void *ctx, const void *buf, size_t len)
long long deadline
#define _SOCK_AGAIN(e)
static int _ip_take(N_HTTP_SERVER *srv, const char *ip)
#define _SOCK_LEN(n)
size_t head_len
pthread_t * threads
char ip[64]
pthread_cond_t cond
static int _content_too_large(const char *b, size_t n)
#define N_HTTP_SERVER_SLICE_MS
size_t count
#define _SOCK_ERR()
SSL_CTX * ctx
static int _value_is(const char *v, size_t n, const char *token)
static void _send_error(_conn *c, int status)
#define _IO_STOPPED
static void _stat_add(N_HTTP_SERVER *srv, unsigned long long *field, long long delta)
N_HTTP_SERVER * srv
static long long _body_chunk(_conn *c, char *dst, size_t want)
static int _body_fail(N_HTTP_SERVER_REQUEST *req, long long rc)
#define _IO_ERROR
static void * _worker(void *arg)
NETWORK * listen
static int _unsupported_method(const char *b, size_t n)
static long long _recv(_conn *c, char *dst, size_t cap)
N_HTTP_SERVER_STATS stats
#define _IO_TIMEOUT
static int _wait(_conn *c, int for_write)
_ip_slot * ips
#define _SOCK_INTR(e)
atomic_int stop
SSL * ssl
NETWORK * netw
#define _IO_CLOSED
N_HTTP_SERVER_CONFIG cfg
#define N_HTTP_SERVER_ACCEPT_MS
NETWORK ** queue
size_t len
static void _serve(_conn *c)
static long long _now_ms(void)
static void _ip_release(N_HTTP_SERVER *srv, const char *ip)
static int _body_start(N_HTTP_SERVER_REQUEST *req, long long max)
static int _has_head_end(const char *b, size_t n)
int idle_phase
a server (opaque)
HTTP/1.1 server: TLS or plain, deadlines, limits, keep-alive.
Generic log system.
Network Engine.
static uint64_t le(const unsigned char *p, int n)
read a little endian unsigned value
Definition n_pth.c:233
N_STR and string function declaration.