@velox0/cerver 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/runtime/server.c CHANGED
@@ -20,6 +20,7 @@
20
20
  #include <fcntl.h>
21
21
  #include <time.h>
22
22
  #include <sys/mman.h>
23
+ #include <sys/time.h>
23
24
  #include <sys/socket.h>
24
25
  #include <sys/types.h>
25
26
  #include <netinet/in.h>
@@ -55,7 +56,7 @@
55
56
  static void* cerver_memmem(const void* hay, size_t haylen, const void* needle, size_t nlen) {
56
57
  if (nlen == 0) return (void*)hay;
57
58
  if (nlen > haylen) return NULL;
58
- const char* p = (const char*)hay;
59
+ const char* p = (const char*)hay;
59
60
  const char* end = p + haylen - nlen;
60
61
  for (; p <= end; p++) {
61
62
  if (memcmp(p, needle, nlen) == 0) return (void*)p;
@@ -91,12 +92,12 @@ static int get_cpu_count(void) {
91
92
  /* ------------------------------------------------------------------ */
92
93
 
93
94
  typedef struct {
94
- int fds[CERVER_CONN_QUEUE_SIZE];
95
- int head;
96
- int tail;
97
- int count;
95
+ int fds[CERVER_CONN_QUEUE_SIZE];
96
+ int head;
97
+ int tail;
98
+ int count;
98
99
  pthread_mutex_t lock;
99
- pthread_cond_t not_empty;
100
+ pthread_cond_t not_empty;
100
101
  } conn_queue_t;
101
102
 
102
103
  static void cq_init(conn_queue_t* q) {
@@ -119,7 +120,7 @@ static int cq_push(conn_queue_t* q, int fd) {
119
120
  return -1;
120
121
  }
121
122
  q->fds[q->tail] = fd;
122
- q->tail = (q->tail + 1) % CERVER_CONN_QUEUE_SIZE;
123
+ q->tail = (q->tail + 1) % CERVER_CONN_QUEUE_SIZE;
123
124
  q->count++;
124
125
  pthread_cond_signal(&q->not_empty);
125
126
  pthread_mutex_unlock(&q->lock);
@@ -140,7 +141,7 @@ static int cq_pop(conn_queue_t* q, volatile int* running) {
140
141
  pthread_mutex_unlock(&q->lock);
141
142
  return -1;
142
143
  }
143
- int fd = q->fds[q->head];
144
+ int fd = q->fds[q->head];
144
145
  q->head = (q->head + 1) % CERVER_CONN_QUEUE_SIZE;
145
146
  q->count--;
146
147
  pthread_mutex_unlock(&q->lock);
@@ -152,31 +153,31 @@ static conn_queue_t g_conn_queue;
152
153
 
153
154
  /* Global worker readiness tracking */
154
155
  typedef struct {
155
- int workers_ready;
156
- int workers_expected;
156
+ int workers_ready;
157
+ int workers_expected;
157
158
  pthread_mutex_t lock;
158
- pthread_cond_t ready_cv;
159
+ pthread_cond_t ready_cv;
159
160
  } worker_readiness_t;
160
161
 
161
- static worker_readiness_t g_worker_readiness = {.workers_ready = 0,
162
+ static worker_readiness_t g_worker_readiness = {.workers_ready = 0,
162
163
  .workers_expected = 0,
163
- .lock = PTHREAD_MUTEX_INITIALIZER,
164
- .ready_cv = PTHREAD_COND_INITIALIZER};
164
+ .lock = PTHREAD_MUTEX_INITIALIZER,
165
+ .ready_cv = PTHREAD_COND_INITIALIZER};
165
166
 
166
167
  /* Global acceptor startup tracking */
167
168
  typedef struct {
168
- int acceptors_ready;
169
- int start_accepting;
169
+ int acceptors_ready;
170
+ int start_accepting;
170
171
  pthread_mutex_t lock;
171
- pthread_cond_t ready_cv;
172
- pthread_cond_t start_cv;
172
+ pthread_cond_t ready_cv;
173
+ pthread_cond_t start_cv;
173
174
  } acceptor_readiness_t;
174
175
 
175
176
  static acceptor_readiness_t g_acceptor_readiness = {.acceptors_ready = 0,
176
177
  .start_accepting = 0,
177
- .lock = PTHREAD_MUTEX_INITIALIZER,
178
- .ready_cv = PTHREAD_COND_INITIALIZER,
179
- .start_cv = PTHREAD_COND_INITIALIZER};
178
+ .lock = PTHREAD_MUTEX_INITIALIZER,
179
+ .ready_cv = PTHREAD_COND_INITIALIZER,
180
+ .start_cv = PTHREAD_COND_INITIALIZER};
180
181
 
181
182
  /* ------------------------------------------------------------------ */
182
183
  /* Buffered read */
@@ -185,7 +186,7 @@ static acceptor_readiness_t g_acceptor_readiness = {.acceptors_ready = 0,
185
186
  static char* read_full_request(int fd, size_t* out_len) {
186
187
  size_t cap = CERVER_READ_BUF;
187
188
  size_t len = 0;
188
- char* buf = malloc(cap + 1);
189
+ char* buf = malloc(cap + 1);
189
190
  if (!buf) return NULL;
190
191
 
191
192
  while (len < (size_t)CERVER_READ_BUF_MAX) {
@@ -227,16 +228,16 @@ static void handle_connection(cerver_server_t* srv, int client_fd) {
227
228
  setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay));
228
229
 
229
230
  int request_count = 0;
230
- int keepalive = 1;
231
+ int keepalive = 1;
231
232
 
232
233
  while (keepalive && srv->running && request_count < CERVER_KEEPALIVE_MAX) {
233
234
  struct timeval tv;
234
- tv.tv_sec = (request_count == 0) ? 5 : CERVER_KEEPALIVE_TIMEOUT;
235
+ tv.tv_sec = (request_count == 0) ? 5 : CERVER_KEEPALIVE_TIMEOUT;
235
236
  tv.tv_usec = 0;
236
237
  setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
237
238
 
238
239
  size_t req_len = 0;
239
- char* buf = read_full_request(client_fd, &req_len);
240
+ char* buf = read_full_request(client_fd, &req_len);
240
241
  if (!buf || req_len == 0) {
241
242
  if (buf) free(buf);
242
243
  break;
@@ -355,9 +356,9 @@ static int create_listener(int port, int reuseport) {
355
356
 
356
357
  struct sockaddr_in addr;
357
358
  memset(&addr, 0, sizeof(addr));
358
- addr.sin_family = AF_INET;
359
+ addr.sin_family = AF_INET;
359
360
  addr.sin_addr.s_addr = INADDR_ANY;
360
- addr.sin_port = htons((uint16_t)port);
361
+ addr.sin_port = htons((uint16_t)port);
361
362
 
362
363
  if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
363
364
  perror("cerver: bind");
@@ -380,7 +381,7 @@ static int create_listener(int port, int reuseport) {
380
381
 
381
382
  static int accept_connection(int listen_fd) {
382
383
  struct sockaddr_in ca;
383
- socklen_t cl = sizeof(ca);
384
+ socklen_t cl = sizeof(ca);
384
385
  #ifdef __linux__
385
386
  return accept4(listen_fd, (struct sockaddr*)&ca, &cl, SOCK_CLOEXEC);
386
387
  #else
@@ -394,7 +395,7 @@ static int accept_connection(int listen_fd) {
394
395
 
395
396
  #if CERVER_USE_KQUEUE
396
397
  static void* acceptor_loop(void* arg) {
397
- cerver_worker_t* w = (cerver_worker_t*)arg;
398
+ cerver_worker_t* w = (cerver_worker_t*)arg;
398
399
  cerver_server_t* srv = w->srv;
399
400
 
400
401
  int kq = kqueue();
@@ -419,8 +420,8 @@ static void* acceptor_loop(void* arg) {
419
420
  pthread_mutex_unlock(&g_acceptor_readiness.lock);
420
421
 
421
422
  while (srv->running) {
422
- struct timespec ts = {1, 0};
423
- int nev = kevent(kq, NULL, 0, events, CERVER_MAX_EVENTS, &ts);
423
+ struct timespec ts = {1, 0};
424
+ int nev = kevent(kq, NULL, 0, events, CERVER_MAX_EVENTS, &ts);
424
425
  if (nev < 0) {
425
426
  if (errno == EINTR) continue;
426
427
  break;
@@ -454,7 +455,7 @@ static void* acceptor_loop(void* arg) {
454
455
 
455
456
  #elif CERVER_USE_EPOLL
456
457
  static void* acceptor_loop(void* arg) {
457
- cerver_worker_t* w = (cerver_worker_t*)arg;
458
+ cerver_worker_t* w = (cerver_worker_t*)arg;
458
459
  cerver_server_t* srv = w->srv;
459
460
 
460
461
  int ep = epoll_create1(EPOLL_CLOEXEC);
@@ -511,7 +512,7 @@ static void* acceptor_loop(void* arg) {
511
512
 
512
513
  #else /* SELECT */
513
514
  static void* acceptor_loop(void* arg) {
514
- cerver_worker_t* w = (cerver_worker_t*)arg;
515
+ cerver_worker_t* w = (cerver_worker_t*)arg;
515
516
  cerver_server_t* srv = w->srv;
516
517
 
517
518
  pthread_mutex_lock(&g_acceptor_readiness.lock);
@@ -526,8 +527,8 @@ static void* acceptor_loop(void* arg) {
526
527
  fd_set rfds;
527
528
  FD_ZERO(&rfds);
528
529
  FD_SET(w->listen_fd, &rfds);
529
- struct timeval tv = {1, 0};
530
- int ret = select(w->listen_fd + 1, &rfds, NULL, NULL, &tv);
530
+ struct timeval tv = {1, 0};
531
+ int ret = select(w->listen_fd + 1, &rfds, NULL, NULL, &tv);
531
532
  if (ret < 0) {
532
533
  if (errno == EINTR) continue;
533
534
  break;
@@ -577,7 +578,7 @@ void cerver_stat_cache_store(cerver_stat_cache_t* cache, const char* path, size_
577
578
  time_t mtime) {
578
579
  time_t now = time(NULL);
579
580
  pthread_mutex_lock(&cache->lock);
580
- int best = 0;
581
+ int best = 0;
581
582
  time_t oldest = cache->entries[0].cached_at;
582
583
  for (int i = 0; i < CERVER_STAT_CACHE_SIZE; i++) {
583
584
  cerver_stat_entry_t* e = &cache->entries[i];
@@ -587,16 +588,16 @@ void cerver_stat_cache_store(cerver_stat_cache_t* cache, const char* path, size_
587
588
  }
588
589
  if (e->cached_at < oldest) {
589
590
  oldest = e->cached_at;
590
- best = i;
591
+ best = i;
591
592
  }
592
593
  }
593
594
  cerver_stat_entry_t* slot = &cache->entries[best];
594
595
  strncpy(slot->path, path, sizeof(slot->path) - 1);
595
596
  slot->path[sizeof(slot->path) - 1] = '\0';
596
- slot->file_size = file_size;
597
- slot->mtime = mtime;
598
- slot->cached_at = now;
599
- slot->valid = 1;
597
+ slot->file_size = file_size;
598
+ slot->mtime = mtime;
599
+ slot->cached_at = now;
600
+ slot->valid = 1;
600
601
  pthread_mutex_unlock(&cache->lock);
601
602
  }
602
603
 
@@ -606,25 +607,25 @@ void cerver_stat_cache_store(cerver_stat_cache_t* cache, const char* path, size_
606
607
 
607
608
  int cerver_init(cerver_server_t* srv, int port, int threads) {
608
609
  memset(srv, 0, sizeof(*srv));
609
- srv->port = port;
610
- srv->sock_fd = -1;
611
- srv->running = 0;
612
- srv->public_dir = NULL;
610
+ srv->port = port;
611
+ srv->sock_fd = -1;
612
+ srv->running = 0;
613
+ srv->public_dir = NULL;
613
614
  srv->dispatch_override = NULL;
614
- srv->worker_count = (threads > 0) ? threads : get_cpu_count();
615
- srv->workers = NULL;
615
+ srv->worker_count = (threads > 0) ? threads : get_cpu_count();
616
+ srv->workers = NULL;
616
617
  cerver_stat_cache_init(&srv->stat_cache);
617
618
  return 0;
618
619
  }
619
620
 
620
621
  int cerver_add_routes(cerver_server_t* srv, cerver_route_t* routes, int count) {
621
- srv->routes = routes;
622
+ srv->routes = routes;
622
623
  srv->route_count = count;
623
624
  return 0;
624
625
  }
625
626
 
626
627
  int cerver_set_assets(cerver_server_t* srv, cerver_asset_t* assets, int count) {
627
- srv->assets = assets;
628
+ srv->assets = assets;
628
629
  srv->asset_count = count;
629
630
  return 0;
630
631
  }
@@ -646,7 +647,7 @@ int cerver_listen(cerver_server_t* srv) {
646
647
  /* Determine pool and acceptor counts.
647
648
  * Acceptors: min(worker_count, cpu_count) — one per core for accept.
648
649
  * Pool workers: worker_count * 16 — enough to cover concurrent keep-alive. */
649
- int cpu_count = get_cpu_count();
650
+ int cpu_count = get_cpu_count();
650
651
  int acceptor_count = cpu_count;
651
652
  if (acceptor_count > srv->worker_count) acceptor_count = srv->worker_count;
652
653
  if (acceptor_count < 1) acceptor_count = 1;
@@ -660,7 +661,7 @@ int cerver_listen(cerver_server_t* srv) {
660
661
 
661
662
  /* Initialize worker readiness tracking */
662
663
  pthread_mutex_lock(&g_worker_readiness.lock);
663
- g_worker_readiness.workers_ready = 0;
664
+ g_worker_readiness.workers_ready = 0;
664
665
  g_worker_readiness.workers_expected = pool_size;
665
666
  pthread_mutex_unlock(&g_worker_readiness.lock);
666
667
 
@@ -711,7 +712,7 @@ int cerver_listen(cerver_server_t* srv) {
711
712
  }
712
713
 
713
714
  /* Start acceptor threads */
714
- srv->workers = calloc((size_t)acceptor_count, sizeof(cerver_worker_t));
715
+ srv->workers = calloc((size_t)acceptor_count, sizeof(cerver_worker_t));
715
716
  srv->worker_count = acceptor_count;
716
717
  if (!srv->workers) {
717
718
  perror("cerver: calloc acceptors");
@@ -726,9 +727,9 @@ int cerver_listen(cerver_server_t* srv) {
726
727
 
727
728
  for (int i = 0; i < acceptor_count; i++) {
728
729
  cerver_worker_t* w = &srv->workers[i];
729
- w->id = i;
730
- w->srv = srv;
731
- w->event_fd = -1;
730
+ w->id = i;
731
+ w->srv = srv;
732
+ w->event_fd = -1;
732
733
  #ifdef __linux__
733
734
  w->listen_fd = create_listener(srv->port, 1);
734
735
  if (w->listen_fd < 0) w->listen_fd = srv->sock_fd;
package/runtime/static.c CHANGED
@@ -62,7 +62,7 @@ typedef struct {
62
62
 
63
63
  static encoding_prefs_t parse_accept_encoding(const cerver_request_t* req) {
64
64
  encoding_prefs_t prefs = {0, 0};
65
- const char* ae = cerver_req_header(req, "Accept-Encoding");
65
+ const char* ae = cerver_req_header(req, "Accept-Encoding");
66
66
  if (!ae) return prefs;
67
67
 
68
68
  if (strstr(ae, "br")) prefs.accepts_br = 1;
@@ -92,7 +92,7 @@ static void add_cache_headers(cerver_response_t* res, const char* path) {
92
92
  static int serve_embedded(cerver_server_t* srv, cerver_request_t* req, cerver_response_t* res) {
93
93
  if (!srv->assets || srv->asset_count == 0) return -1;
94
94
 
95
- const char* path = req->path;
95
+ const char* path = req->path;
96
96
  const cerver_asset_t* found = NULL;
97
97
 
98
98
  /*
@@ -112,7 +112,7 @@ static int serve_embedded(cerver_server_t* srv, cerver_request_t* req, cerver_re
112
112
 
113
113
  /* Try with /index.html appended (for directory-like paths) */
114
114
  if (!found) {
115
- char index_path[CERVER_MAX_PATH];
115
+ char index_path[CERVER_MAX_PATH];
116
116
  size_t plen = strlen(path);
117
117
  if (plen > 0 && path[plen - 1] == '/') {
118
118
  snprintf(index_path, sizeof(index_path), "%sindex.html", path);
@@ -207,11 +207,11 @@ static int serve_filesystem(cerver_server_t* srv, cerver_request_t* req, cerver_
207
207
  /* Advise the kernel we'll read sequentially */
208
208
  madvise(mapped, file_size, MADV_SEQUENTIAL);
209
209
 
210
- res->status = 200;
210
+ res->status = 200;
211
211
  res->content_type = mime;
212
- res->body = (const char*)mapped;
213
- res->body_len = file_size;
214
- res->_body_owned = 2; /* Special flag: needs munmap, not free */
212
+ res->body = (const char*)mapped;
213
+ res->body_len = file_size;
214
+ res->_body_owned = 2; /* Special flag: needs munmap, not free */
215
215
  } else {
216
216
  /* Small files: read into buffer (avoids mmap overhead) */
217
217
  int fd = open(full_path, O_RDONLY);
@@ -236,11 +236,11 @@ static int serve_filesystem(cerver_server_t* srv, cerver_request_t* req, cerver_
236
236
  return -1;
237
237
  }
238
238
 
239
- res->status = 200;
239
+ res->status = 200;
240
240
  res->content_type = mime;
241
- res->body = file_data;
242
- res->body_len = file_size;
243
- res->_body_owned = 1; /* malloc'd */
241
+ res->body = file_data;
242
+ res->body_len = file_size;
243
+ res->_body_owned = 1; /* malloc'd */
244
244
  }
245
245
 
246
246
  add_cache_headers(res, path);
@@ -0,0 +1,76 @@
1
+ #include "minunit.h"
2
+
3
+ #include <stdio.h>
4
+ #include <string.h>
5
+ #include <unistd.h>
6
+
7
+ int mu_tests_run = 0;
8
+ int mu_tests_failed = 0;
9
+ int mu_test_failed = 0;
10
+
11
+ static int mu_is_tty(FILE* f) { return isatty(fileno(f)); }
12
+
13
+ static const char* mu_color(FILE* f, const char* code) { return mu_is_tty(f) ? code : ""; }
14
+
15
+ const char* mu_snip(const char* s, char* buf, size_t cap) {
16
+ if (s == NULL) {
17
+ return "(null)";
18
+ }
19
+ if (cap == 0) {
20
+ return "";
21
+ }
22
+
23
+ size_t len = strlen(s);
24
+ if (len < cap) {
25
+ snprintf(buf, cap, "%s", s);
26
+ return buf;
27
+ }
28
+
29
+ if (cap <= 4) {
30
+ size_t keep = cap - 1;
31
+ memcpy(buf, s, keep);
32
+ buf[keep] = '\0';
33
+ return buf;
34
+ }
35
+
36
+ size_t keep = cap - 4;
37
+ memcpy(buf, s, keep);
38
+ memcpy(buf + keep, "...", 3);
39
+ buf[keep + 3] = '\0';
40
+ return buf;
41
+ }
42
+
43
+ void mu_fail(const char* file, int line, const char* msg) {
44
+ const char* red = mu_color(stderr, "\x1b[31m");
45
+ const char* reset = mu_color(stderr, "\x1b[0m");
46
+
47
+ mu_test_failed = 1;
48
+ fprintf(stderr, "%sFAIL%s %s:%d: %s\n", red, reset, file, line, msg);
49
+ }
50
+
51
+ void mu_run(const char* name, mu_test_fn fn) {
52
+ mu_tests_run++;
53
+ mu_test_failed = 0;
54
+ fn();
55
+ if (mu_test_failed) {
56
+ mu_tests_failed++;
57
+ fprintf(stderr, " in %s\n", name);
58
+ } else {
59
+ const char* green = mu_color(stdout, "\x1b[32m");
60
+ const char* reset = mu_color(stdout, "\x1b[0m");
61
+ printf("%sok%s %s\n", green, reset, name);
62
+ }
63
+ }
64
+
65
+ int mu_report(void) {
66
+ const char* reset = mu_color(stdout, "\x1b[0m");
67
+ if (mu_tests_failed) {
68
+ const char* red = mu_color(stdout, "\x1b[31m");
69
+ printf("%sSummary:%s %d tests, %s%d failures%s\n", red, reset, mu_tests_run, red,
70
+ mu_tests_failed, reset);
71
+ } else {
72
+ const char* green = mu_color(stdout, "\x1b[32m");
73
+ printf("%sSummary:%s %d tests, %s0 failures%s\n", green, reset, mu_tests_run, green, reset);
74
+ }
75
+ return mu_tests_failed ? 1 : 0;
76
+ }
@@ -0,0 +1,64 @@
1
+ #ifndef CERVER_MINUNIT_H
2
+ #define CERVER_MINUNIT_H
3
+
4
+ #include <stddef.h>
5
+ #include <stdio.h>
6
+ #include <string.h>
7
+
8
+ typedef void (*mu_test_fn)(void);
9
+
10
+ #define MU_SNIP_MAX 80
11
+
12
+ extern int mu_tests_run;
13
+ extern int mu_tests_failed;
14
+ extern int mu_test_failed;
15
+
16
+ void mu_fail(const char* file, int line, const char* msg);
17
+ void mu_run(const char* name, mu_test_fn fn);
18
+ int mu_report(void);
19
+ const char* mu_snip(const char* s, char* buf, size_t cap);
20
+
21
+ #define MU_ASSERT(cond) \
22
+ do { \
23
+ if (!(cond)) { \
24
+ mu_fail(__FILE__, __LINE__, #cond); \
25
+ return; \
26
+ } \
27
+ } while (0)
28
+
29
+ #define MU_ASSERT_EQ_INT(a, b) \
30
+ do { \
31
+ if ((a) != (b)) { \
32
+ char _mu_buf[128]; \
33
+ snprintf(_mu_buf, sizeof(_mu_buf), "got %d expected %d", (int)(a), (int)(b)); \
34
+ mu_fail(__FILE__, __LINE__, _mu_buf); \
35
+ return; \
36
+ } \
37
+ } while (0)
38
+
39
+ #define MU_ASSERT_EQ_SIZE(a, b) \
40
+ do { \
41
+ if ((a) != (b)) { \
42
+ char _mu_buf[128]; \
43
+ snprintf(_mu_buf, sizeof(_mu_buf), "got %zu expected %zu", (size_t)(a), (size_t)(b)); \
44
+ mu_fail(__FILE__, __LINE__, _mu_buf); \
45
+ return; \
46
+ } \
47
+ } while (0)
48
+
49
+ #define MU_ASSERT_STREQ(a, b) \
50
+ do { \
51
+ if (((a) == NULL && (b) != NULL) || ((a) != NULL && (b) == NULL) || \
52
+ ((a) != NULL && (b) != NULL && strcmp((a), (b)) != 0)) { \
53
+ char _mu_buf[256]; \
54
+ char _mu_a[MU_SNIP_MAX]; \
55
+ char _mu_b[MU_SNIP_MAX]; \
56
+ const char* _mu_as = mu_snip((a), _mu_a, sizeof(_mu_a)); \
57
+ const char* _mu_bs = mu_snip((b), _mu_b, sizeof(_mu_b)); \
58
+ snprintf(_mu_buf, sizeof(_mu_buf), "got '%s' expected '%s'", _mu_as, _mu_bs); \
59
+ mu_fail(__FILE__, __LINE__, _mu_buf); \
60
+ return; \
61
+ } \
62
+ } while (0)
63
+
64
+ #endif