@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/cerver.h CHANGED
@@ -6,6 +6,16 @@
6
6
  * the route dispatch interface.
7
7
  */
8
8
 
9
+ /*
10
+ * Feature test macros for POSIX/GNU APIs used by the runtime.
11
+ * Must be defined before any system headers.
12
+ */
13
+ #if defined(__linux__)
14
+ #ifndef _GNU_SOURCE
15
+ #define _GNU_SOURCE
16
+ #endif
17
+ #endif
18
+
9
19
  #ifndef CERVER_H
10
20
  #define CERVER_H
11
21
 
@@ -69,22 +79,22 @@ typedef struct {
69
79
 
70
80
  /* Parsed query parameters */
71
81
  cerver_kv_t query[CERVER_MAX_QUERY];
72
- int query_count;
82
+ int query_count;
73
83
 
74
84
  /* Route parameters (from dynamic segments like :key) */
75
85
  cerver_kv_t params[CERVER_MAX_PARAMS];
76
- int params_count;
86
+ int params_count;
77
87
 
78
88
  /* Request headers */
79
89
  cerver_kv_t headers[CERVER_MAX_HEADERS];
80
- int header_count;
90
+ int header_count;
81
91
 
82
92
  /* Request body (for POST) */
83
93
  const char* body;
84
- size_t body_len;
94
+ size_t body_len;
85
95
 
86
96
  /* Internal: raw buffer ownership (NULL if in-place parsing used) */
87
- char* _raw_buf;
97
+ char* _raw_buf;
88
98
  size_t _raw_len;
89
99
  } cerver_request_t;
90
100
 
@@ -93,16 +103,16 @@ typedef struct {
93
103
  /* ------------------------------------------------------------------ */
94
104
 
95
105
  typedef struct {
96
- int status;
106
+ int status;
97
107
  const char* content_type;
98
108
 
99
109
  /* Response body — can be heap-allocated or static */
100
110
  const char* body;
101
- size_t body_len;
111
+ size_t body_len;
102
112
 
103
113
  /* Extra headers */
104
114
  cerver_kv_t headers[CERVER_MAX_HEADERS];
105
- int header_count;
115
+ int header_count;
106
116
 
107
117
  /* Internal flag: was body malloc'd? */
108
118
  int _body_owned;
@@ -137,8 +147,8 @@ int cerver_req_wants_close(const cerver_request_t* req);
137
147
  typedef void (*cerver_handler_fn)(cerver_request_t* req, cerver_response_t* res);
138
148
 
139
149
  typedef struct {
140
- const char* method; /* "GET", "POST" */
141
- const char* pattern; /* "/", "/art/:key", "/api/projects" */
150
+ const char* method; /* "GET", "POST" */
151
+ const char* pattern; /* "/", "/groups/:group_id", "/api/items" */
142
152
  cerver_handler_fn handler;
143
153
  } cerver_route_t;
144
154
 
@@ -147,20 +157,20 @@ typedef struct {
147
157
  /* ------------------------------------------------------------------ */
148
158
 
149
159
  typedef struct {
150
- const char* path; /* e.g. "/index.html" */
151
- const char* mime_type; /* e.g. "text/html" */
160
+ const char* path; /* e.g. "/index.html" */
161
+ const char* mime_type; /* e.g. "text/html" */
152
162
  const unsigned char* data;
153
- size_t data_len;
163
+ size_t data_len;
154
164
 
155
165
  /* Pre-compressed variants (NULL if not available) */
156
166
  const unsigned char* data_gz;
157
- size_t data_gz_len;
167
+ size_t data_gz_len;
158
168
  const unsigned char* data_br;
159
- size_t data_br_len;
169
+ size_t data_br_len;
160
170
 
161
171
  /* Pre-computed response header (NULL if not generated) */
162
172
  const char* prebuilt_header;
163
- size_t prebuilt_header_len;
173
+ size_t prebuilt_header_len;
164
174
  } cerver_asset_t;
165
175
 
166
176
  /* ------------------------------------------------------------------ */
@@ -168,16 +178,16 @@ typedef struct {
168
178
  /* ------------------------------------------------------------------ */
169
179
 
170
180
  typedef struct {
171
- char path[CERVER_MAX_PATH];
181
+ char path[CERVER_MAX_PATH];
172
182
  size_t file_size;
173
183
  time_t mtime;
174
184
  time_t cached_at;
175
- int valid;
185
+ int valid;
176
186
  } cerver_stat_entry_t;
177
187
 
178
188
  typedef struct {
179
189
  cerver_stat_entry_t entries[CERVER_STAT_CACHE_SIZE];
180
- pthread_mutex_t lock;
190
+ pthread_mutex_t lock;
181
191
  } cerver_stat_cache_t;
182
192
 
183
193
  /* ------------------------------------------------------------------ */
@@ -193,11 +203,11 @@ typedef cerver_handler_fn (*cerver_dispatch_fn)(cerver_request_t* req);
193
203
  typedef struct cerver_server cerver_server_t;
194
204
 
195
205
  typedef struct {
196
- int id;
197
- int event_fd; /* kqueue or epoll fd */
198
- int listen_fd; /* per-worker on Linux, shared on macOS */
206
+ int id;
207
+ int event_fd; /* kqueue or epoll fd */
208
+ int listen_fd; /* per-worker on Linux, shared on macOS */
199
209
  cerver_server_t* srv;
200
- pthread_t thread;
210
+ pthread_t thread;
201
211
  } cerver_worker_t;
202
212
 
203
213
  /* ------------------------------------------------------------------ */
@@ -205,14 +215,14 @@ typedef struct {
205
215
  /* ------------------------------------------------------------------ */
206
216
 
207
217
  struct cerver_server {
208
- int port;
209
- int sock_fd;
218
+ int port;
219
+ int sock_fd;
210
220
  cerver_route_t* routes;
211
- int route_count;
221
+ int route_count;
212
222
  cerver_asset_t* assets;
213
- int asset_count;
214
- const char* public_dir; /* NULL if embedded mode */
215
- volatile int running;
223
+ int asset_count;
224
+ const char* public_dir; /* NULL if embedded mode */
225
+ volatile int running;
216
226
 
217
227
  /* Generated dispatch override (faster than generic router) */
218
228
  cerver_dispatch_fn dispatch_override;
@@ -221,17 +231,17 @@ struct cerver_server {
221
231
  cerver_stat_cache_t stat_cache;
222
232
 
223
233
  /* Worker pool */
224
- int worker_count;
234
+ int worker_count;
225
235
  cerver_worker_t* workers;
226
236
  };
227
237
 
228
238
  /* Server lifecycle */
229
- int cerver_init(cerver_server_t* srv, int port, int threads);
230
- int cerver_add_routes(cerver_server_t* srv, cerver_route_t* routes, int count);
231
- int cerver_set_assets(cerver_server_t* srv, cerver_asset_t* assets, int count);
239
+ int cerver_init(cerver_server_t* srv, int port, int threads);
240
+ int cerver_add_routes(cerver_server_t* srv, cerver_route_t* routes, int count);
241
+ int cerver_set_assets(cerver_server_t* srv, cerver_asset_t* assets, int count);
232
242
  void cerver_set_public_dir(cerver_server_t* srv, const char* dir);
233
243
  void cerver_set_dispatch(cerver_server_t* srv, cerver_dispatch_fn fn);
234
- int cerver_listen(cerver_server_t* srv);
244
+ int cerver_listen(cerver_server_t* srv);
235
245
  void cerver_shutdown(cerver_server_t* srv);
236
246
 
237
247
  /* ------------------------------------------------------------------ */
@@ -250,7 +260,7 @@ int cerver_write_response(int fd, const cerver_response_t* res, int keepalive);
250
260
  /* Router (internal) */
251
261
  /* ------------------------------------------------------------------ */
252
262
 
253
- int cerver_route_match(const cerver_route_t* route, cerver_request_t* req);
263
+ int cerver_route_match(const cerver_route_t* route, cerver_request_t* req);
254
264
  cerver_handler_fn cerver_dispatch(cerver_server_t* srv, cerver_request_t* req);
255
265
 
256
266
  /* ------------------------------------------------------------------ */
@@ -270,7 +280,7 @@ int cerver_serve_static(cerver_server_t* srv, cerver_request_t* req, cerver_resp
270
280
  /* ------------------------------------------------------------------ */
271
281
 
272
282
  void cerver_stat_cache_init(cerver_stat_cache_t* cache);
273
- int cerver_stat_cache_lookup(cerver_stat_cache_t* cache, const char* path, size_t* file_size);
283
+ int cerver_stat_cache_lookup(cerver_stat_cache_t* cache, const char* path, size_t* file_size);
274
284
  void cerver_stat_cache_store(cerver_stat_cache_t* cache, const char* path, size_t file_size,
275
285
  time_t mtime);
276
286
 
@@ -11,6 +11,7 @@
11
11
  #include <stdio.h>
12
12
  #include <stdlib.h>
13
13
  #include <string.h>
14
+ #include <strings.h>
14
15
  #include <ctype.h>
15
16
 
16
17
  /* ------------------------------------------------------------------ */
@@ -67,13 +68,13 @@ static void parse_query_string(char* qs, cerver_request_t* req) {
67
68
 
68
69
  char* eq = strchr(pair_start, '=');
69
70
  if (eq) {
70
- *eq = '\0';
71
- req->query[req->query_count].key = pair_start;
71
+ *eq = '\0';
72
+ req->query[req->query_count].key = pair_start;
72
73
  req->query[req->query_count].value = eq + 1;
73
74
  url_decode((char*)req->query[req->query_count].key);
74
75
  url_decode((char*)req->query[req->query_count].value);
75
76
  } else {
76
- req->query[req->query_count].key = pair_start;
77
+ req->query[req->query_count].key = pair_start;
77
78
  req->query[req->query_count].value = "";
78
79
  }
79
80
  req->query_count++;
@@ -94,7 +95,7 @@ int cerver_parse_request(const char* raw, size_t len, cerver_request_t* req) {
94
95
  * The caller must keep it alive for the request's lifetime.
95
96
  */
96
97
  char* buf = (char*)raw;
97
- buf[len] = '\0'; /* caller ensures buf has capacity for len+1 */
98
+ buf[len] = '\0'; /* caller ensures buf has capacity for len+1 */
98
99
 
99
100
  /* We no longer allocate _raw_buf — the read buffer IS the raw buffer */
100
101
  req->_raw_buf = NULL;
@@ -117,7 +118,7 @@ int cerver_parse_request(const char* raw, size_t len, cerver_request_t* req) {
117
118
 
118
119
  /* Path (and maybe query string) */
119
120
  char* path_start = sp1 + 1;
120
- char* sp2 = strchr(path_start, ' ');
121
+ char* sp2 = strchr(path_start, ' ');
121
122
  if (sp2) *sp2 = '\0';
122
123
 
123
124
  /* Split path and query string */
@@ -125,8 +126,8 @@ int cerver_parse_request(const char* raw, size_t len, cerver_request_t* req) {
125
126
  if (qmark) {
126
127
  *qmark = '\0';
127
128
  /* Point query_string directly into the buffer */
128
- char* qs_start = qmark + 1;
129
- size_t qs_len = strlen(qs_start);
129
+ char* qs_start = qmark + 1;
130
+ size_t qs_len = strlen(qs_start);
130
131
  if (qs_len >= sizeof(req->query_string)) qs_len = sizeof(req->query_string) - 1;
131
132
  memcpy(req->query_string, qs_start, qs_len);
132
133
  req->query_string[qs_len] = '\0';
@@ -148,7 +149,7 @@ int cerver_parse_request(const char* raw, size_t len, cerver_request_t* req) {
148
149
  }
149
150
 
150
151
  /* ---- Headers ---- */
151
- char* hdr_start = line_end + 2; /* skip \r\n */
152
+ char* hdr_start = line_end + 2; /* skip \r\n */
152
153
  size_t content_length = 0;
153
154
 
154
155
  while (hdr_start < buf + len) {
@@ -166,11 +167,11 @@ int cerver_parse_request(const char* raw, size_t len, cerver_request_t* req) {
166
167
  if (req->header_count < CERVER_MAX_HEADERS) {
167
168
  char* colon = strchr(hdr_start, ':');
168
169
  if (colon) {
169
- *colon = '\0';
170
+ *colon = '\0';
170
171
  char* val = colon + 1;
171
172
  while (*val == ' ') val++;
172
173
 
173
- req->headers[req->header_count].key = hdr_start;
174
+ req->headers[req->header_count].key = hdr_start;
174
175
  req->headers[req->header_count].value = val;
175
176
  req->header_count++;
176
177
 
@@ -186,7 +187,7 @@ int cerver_parse_request(const char* raw, size_t len, cerver_request_t* req) {
186
187
 
187
188
  /* ---- Body (for POST etc.) ---- */
188
189
  if (content_length > 0 && hdr_start < buf + len) {
189
- req->body = hdr_start;
190
+ req->body = hdr_start;
190
191
  req->body_len = content_length;
191
192
  /* Ensure we don't read past the buffer */
192
193
  size_t remaining = (size_t)(buf + len - hdr_start);
@@ -57,7 +57,7 @@ static const char* status_text(int code) {
57
57
  int cerver_write_response(int fd, const cerver_response_t* res, int keepalive) {
58
58
  /* Build the response header */
59
59
  char header[4096];
60
- int hlen = 0;
60
+ int hlen = 0;
61
61
 
62
62
  /* Status line */
63
63
  hlen += snprintf(header + hlen, sizeof(header) - (size_t)hlen, "HTTP/1.1 %d %s\r\n", res->status,
@@ -99,11 +99,11 @@ int cerver_write_response(int fd, const cerver_response_t* res, int keepalive) {
99
99
  if (res->body && res->body_len > 0) {
100
100
  struct iovec iov[2];
101
101
  iov[0].iov_base = header;
102
- iov[0].iov_len = (size_t)hlen;
102
+ iov[0].iov_len = (size_t)hlen;
103
103
  iov[1].iov_base = (void*)res->body;
104
- iov[1].iov_len = res->body_len;
104
+ iov[1].iov_len = res->body_len;
105
105
 
106
- size_t total = iov[0].iov_len + iov[1].iov_len;
106
+ size_t total = iov[0].iov_len + iov[1].iov_len;
107
107
  size_t written = 0;
108
108
 
109
109
  while (written < total) {
@@ -118,9 +118,9 @@ int cerver_write_response(int fd, const cerver_response_t* res, int keepalive) {
118
118
  } else {
119
119
  /* Header fully sent, adjust body iov */
120
120
  size_t body_sent = written - (size_t)hlen;
121
- iov[0].iov_len = 0;
122
- iov[1].iov_base = (void*)(res->body + body_sent);
123
- iov[1].iov_len = res->body_len - body_sent;
121
+ iov[0].iov_len = 0;
122
+ iov[1].iov_base = (void*)(res->body + body_sent);
123
+ iov[1].iov_len = res->body_len - body_sent;
124
124
  }
125
125
  }
126
126
  } else {
@@ -137,41 +137,41 @@ int cerver_write_response(int fd, const cerver_response_t* res, int keepalive) {
137
137
  /* ------------------------------------------------------------------ */
138
138
 
139
139
  void cerver_res_text(cerver_response_t* res, int status, const char* text) {
140
- res->status = status;
140
+ res->status = status;
141
141
  res->content_type = "text/plain; charset=utf-8";
142
- res->body = text;
143
- res->body_len = strlen(text);
144
- res->_body_owned = 0;
142
+ res->body = text;
143
+ res->body_len = strlen(text);
144
+ res->_body_owned = 0;
145
145
  }
146
146
 
147
147
  void cerver_res_json(cerver_response_t* res, int status, const char* json) {
148
- res->status = status;
148
+ res->status = status;
149
149
  res->content_type = "application/json; charset=utf-8";
150
- res->body = json;
151
- res->body_len = strlen(json);
152
- res->_body_owned = 0;
150
+ res->body = json;
151
+ res->body_len = strlen(json);
152
+ res->_body_owned = 0;
153
153
  }
154
154
 
155
155
  void cerver_res_html(cerver_response_t* res, int status, const char* html) {
156
- res->status = status;
156
+ res->status = status;
157
157
  res->content_type = "text/html; charset=utf-8";
158
- res->body = html;
159
- res->body_len = strlen(html);
160
- res->_body_owned = 0;
158
+ res->body = html;
159
+ res->body_len = strlen(html);
160
+ res->_body_owned = 0;
161
161
  }
162
162
 
163
163
  void cerver_res_file(cerver_response_t* res, int status, const char* mime,
164
164
  const unsigned char* data, size_t len) {
165
- res->status = status;
165
+ res->status = status;
166
166
  res->content_type = mime;
167
- res->body = (const char*)data;
168
- res->body_len = len;
169
- res->_body_owned = 0;
167
+ res->body = (const char*)data;
168
+ res->body_len = len;
169
+ res->_body_owned = 0;
170
170
  }
171
171
 
172
172
  void cerver_res_header(cerver_response_t* res, const char* key, const char* val) {
173
173
  if (res->header_count < CERVER_MAX_HEADERS) {
174
- res->headers[res->header_count].key = key;
174
+ res->headers[res->header_count].key = key;
175
175
  res->headers[res->header_count].value = val;
176
176
  res->header_count++;
177
177
  }
package/runtime/mime.c CHANGED
@@ -5,6 +5,7 @@
5
5
  #include "cerver.h"
6
6
 
7
7
  #include <string.h>
8
+ #include <strings.h>
8
9
  #include <ctype.h>
9
10
 
10
11
  typedef struct {
package/runtime/router.c CHANGED
@@ -11,6 +11,7 @@
11
11
  #include <stdio.h>
12
12
  #include <stdlib.h>
13
13
  #include <string.h>
14
+ #include <strings.h>
14
15
 
15
16
  /* ------------------------------------------------------------------ */
16
17
  /* Request accessor helpers */
@@ -84,7 +85,7 @@ int cerver_route_match(const cerver_route_t* route, cerver_request_t* req) {
84
85
  }
85
86
 
86
87
  const char* pattern = route->pattern;
87
- const char* path = req->path;
88
+ const char* path = req->path;
88
89
 
89
90
  /* Fast path: exact match */
90
91
  if (strcmp(pattern, path) == 0) {