48#include <sys/socket.h>
52#include <openssl/err.h>
53#include <openssl/ssl.h>
57#define N_HTTP_SERVER_BUF (N_HTTP_REQ_HEAD_MAX + 16384u)
59#define N_HTTP_SERVER_SLICE_MS 250
61#define N_HTTP_SERVER_ACCEPT_MS 250
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))
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)
79#define _IO_TIMEOUT (-2)
80#define _IO_STOPPED (-3)
125 return (
long long)GetTickCount64();
128 clock_gettime(CLOCK_MONOTONIC, &ts);
129 return (
long long)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
134 pthread_mutex_lock(&srv->
lock);
135 *field = (
unsigned long long)((
long long)*field + delta);
136 pthread_mutex_unlock(&srv->
lock);
155 p.events = for_write ? POLLWRNORM : POLLRDNORM;
157 rc = WSAPoll(&p, 1, slice);
163 p.events = for_write ? POLLOUT : POLLIN;
165 rc = poll(&p, 1, slice);
182 int n = SSL_read(c->
ssl, dst, (
int)cap), err;
185 err = SSL_get_error(c->
ssl, n);
186 if (err == SSL_ERROR_ZERO_RETURN)
188 if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
189 int w =
_wait(c, err == SSL_ERROR_WANT_WRITE);
220#if !defined(__windows__) && defined(__linux__)
222 struct timespec zero = {0, 0};
224 sigaddset(&set, SIGPIPE);
225 while (sigtimedwait(&set, NULL, &zero) > 0);
230static ssize_t
_write_fn(
void* ctx,
const void* buf,
size_t len) {
237 int n = SSL_write(c->
ssl, buf, (
int)len), err;
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);
264 if (
_wait(c, 1) != 1)
302 int rc = SSL_accept(c->
ssl), err, w;
305 err = SSL_get_error(c->
ssl, rc);
306 if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) {
311 w =
_wait(c, err == SSL_ERROR_WANT_WRITE);
322static int _value_is(
const char* v,
size_t n,
const char* token) {
323 size_t i, t = strlen(token);
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');
341 size_t take = avail < want ? avail : want;
345 return (
long long)take;
347 return _recv(c, dst, want);
367 static const char cont[] =
"HTTP/1.1 100 Continue\r\n\r\n";
370 if (
_write_fn(c, cont,
sizeof(cont) - 1) != (ssize_t)(
sizeof(cont) - 1)) {
420 (*out)->written += (size_t)n;
421 (*out)->data[(*out)->written] =
'\0';
446 size_t want = c->
body_left < (
long long)
sizeof(chunk) ? (size_t)c->
body_left :
sizeof(chunk);
451 while (
done < (
size_t)n) {
452 ssize_t w = write(fd, chunk +
done, (
size_t)n -
done);
453 if (w < 0 && errno == EINTR)
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')
492 for (i = 0; i < n && i <= 20; i++) {
495 if (!((b[i] >=
'A' && b[i] <=
'Z') || b[i] ==
'-' || b[i] ==
'_'))
498 if (i == 0 || i >= n || b[i] !=
' ')
500 return !((i == 3 && memcmp(b,
"GET", 3) == 0) || (i == 4 && (memcmp(b,
"HEAD", 4) == 0 || memcmp(b,
"POST", 4) == 0)));
507 size_t i = 0, found = 0, end;
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')
515 while (i + 1 < end && !(b[i] ==
'\r' && b[i + 1] ==
'\n'))
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'))
523 if (
le - ls < 15 || strncasecmp(b + ls,
"content-length:", 15) != 0)
528 while (v < ve && (b[v] ==
' ' || b[v] ==
'\t'))
530 while (ve > v && (b[ve - 1] ==
' ' || b[ve - 1] ==
'\t'))
534 for (; v < ve; v++) {
535 if (b[v] <
'0' || b[v] >
'9')
541 val = val * 10u + (unsigned)(b[v] -
'0');
548 return found == 1 && too_large;
561 while (served < srv->cfg.max_requests_per_conn) {
565 int keep, got_first = c->
len > 0;
567 const char* conn_hdr;
619 req->
tls = c->
ssl != NULL;
623 const char* q = strchr(req->
head.
path,
'?');
630 size_t qlen = strlen(q + 1);
631 memcpy(req->
target + plen + 1, q + 1, qlen + 1);
635 req->
target[plen + 1] =
'\0';
649 if (e && !
_value_is(e, elen,
"100-continue")) {
668 keep = !
_value_is(conn_hdr, clen,
"close");
670 keep =
_value_is(conn_hdr, clen,
"keep-alive");
695 size_t i, slots = srv->
cfg.
workers * 2, free_slot = slots;
698 for (i = 0; i < slots; i++) {
705 if (!srv->
ips[i].
count && free_slot == slots)
708 if (free_slot == slots)
710 snprintf(srv->
ips[free_slot].
ip,
sizeof(srv->
ips[free_slot].
ip),
"%s", ip);
729#if !defined(__windows__)
732 sigaddset(&set, SIGPIPE);
733 pthread_sigmask(SIG_BLOCK, &set, NULL);
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);
752 pthread_mutex_unlock(&srv->
lock);
754 memset(&c, 0,
sizeof(c));
775 pthread_mutex_lock(&srv->
lock);
778 pthread_mutex_unlock(&srv->
lock);
791 memset(cfg, 0,
sizeof(*cfg));
792 cfg->
addr =
"127.0.0.1";
823 atomic_init(&srv->
stop, 0);
824 pthread_mutex_init(&srv->
lock, NULL);
825 pthread_cond_init(&srv->
cond, NULL);
841 n_log(
LOG_ERR,
"http server: TLS requested but the library was built without OpenSSL");
860 atomic_store(&srv->
stop, 1);
874 n_log(
LOG_ERR,
"http server: cannot start worker %zu", i);
875 atomic_store(&srv->
stop, 1);
880 while (!atomic_load(&srv->
stop)) {
886 pthread_mutex_lock(&srv->
lock);
892 pthread_cond_signal(&srv->
cond);
896 pthread_mutex_unlock(&srv->
lock);
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";
908 send(
netw->link.sock, busy,
sizeof(busy) - 1, MSG_NOSIGNAL | MSG_DONTWAIT);
910 send(
netw->link.sock, busy,
sizeof(busy) - 1, 0);
916 pthread_mutex_lock(&srv->
lock);
917 pthread_cond_broadcast(&srv->
cond);
918 pthread_mutex_unlock(&srv->
lock);
920 pthread_join(srv->
threads[i], NULL);
923 while (srv->
qlen > 0) {
942 SSL_CTX_free((*srv)->ctx);
947 pthread_mutex_destroy(&(*srv)->lock);
948 pthread_cond_destroy(&(*srv)->cond);
960 pthread_mutex_lock(&srv->
lock);
962 pthread_mutex_unlock(&srv->
lock);
NETWORK * netw
Network for server mode, accepting incomming.
volatile sig_atomic_t done
#define FreeNoLog(__ptr)
Free Handler without log.
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
#define __n_assert(__ptr, __ret)
macro to assert things
#define endif
close a ifwhatever block
#define Free(__ptr)
Free Handler to get errors.
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_ERR
error conditions
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...
long long body_max
largest accepted Content-Length, 0 to N_HTTP_REQ_BODY_LIMIT_MAX
unsigned methods
accepted methods, an OR of N_HTTP_REQ_METHOD_BIT(); any other method is an error
#define N_HTTP_REQ_METHOD_BIT(m)
Bit of a method in n_http_req_limits.methods.
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
#define N_HTTP_REQ_HEAD_MAX
Largest request head (request line + headers, through CRLFCRLF) accepted.
n_http_req_status
Result of n_http_request_parse.
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
#define N_HTTP_REQ_BODY_LIMIT_MAX
Highest body cap n_http_request_parse_ex() accepts (1 GiB).
@ N_HTTP_REQ_INCOMPLETE
no CRLFCRLF yet, still under the cap
@ N_HTTP_REQ_ERROR
malformed / oversized / unsupported
Limits for n_http_request_parse_ex().
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
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...
int resize_nstr(N_STR *nstr, size_t size)
reallocate a nstr internal buffer.
N_STR * new_nstr(NSTRBYTE size)
create a new N_STR string
A box including a string and his lenght.
char * ip
ip of the connected socket
N_SOCKET link
networking socket
SOCKET sock
a normal socket
int netw_make_listening(NETWORK **netw, char *addr, char *port, int nbpending, int ip_version)
Make a NETWORK be a Listening network.
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' .
#define NETWORK_IPALL
Flag for auto detection by OS of ip version to use.
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
int netw_close(NETWORK **netw)
Closing a specified Network, destroy queues, free the structure.
int netw_set_blocking(NETWORK *netw, unsigned long int is_blocking)
Modify blocking socket mode.
Common headers and low-level functions & define.
#define N_HTTP_SERVER_BUF
static void _drop_sigpipe(void)
static int _handshake(_conn *c)
static ssize_t _write_fn(void *ctx, const void *buf, size_t len)
static int _ip_take(N_HTTP_SERVER *srv, const char *ip)
static int _content_too_large(const char *b, size_t n)
#define N_HTTP_SERVER_SLICE_MS
static int _value_is(const char *v, size_t n, const char *token)
static void _send_error(_conn *c, int status)
static void _stat_add(N_HTTP_SERVER *srv, unsigned long long *field, long long delta)
static long long _body_chunk(_conn *c, char *dst, size_t want)
static int _body_fail(N_HTTP_SERVER_REQUEST *req, long long rc)
static void * _worker(void *arg)
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
static int _wait(_conn *c, int for_write)
#define N_HTTP_SERVER_ACCEPT_MS
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)
HTTP/1.1 server: TLS or plain, deadlines, limits, keep-alive.
static uint64_t le(const unsigned char *p, int n)
read a little endian unsigned value
N_STR and string function declaration.