@velox0/cerver 0.4.0 → 0.4.1
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/.github/workflows/publish.yml +3 -4
- package/package.json +1 -1
- package/runtime/cerver.h +129 -129
- package/runtime/http_parser.c +152 -152
- package/runtime/http_writer.c +136 -126
- package/runtime/mime.c +48 -49
- package/runtime/router.c +98 -98
- package/runtime/server.c +593 -436
- package/runtime/static.c +174 -183
package/package.json
CHANGED
package/runtime/cerver.h
CHANGED
|
@@ -17,38 +17,40 @@
|
|
|
17
17
|
/* Limits */
|
|
18
18
|
/* ------------------------------------------------------------------ */
|
|
19
19
|
|
|
20
|
-
#define CERVER_MAX_HEADERS
|
|
21
|
-
#define CERVER_MAX_PARAMS
|
|
22
|
-
#define CERVER_MAX_QUERY
|
|
23
|
-
#define CERVER_MAX_PATH
|
|
24
|
-
#define CERVER_MAX_HEADER_VAL
|
|
25
|
-
#define CERVER_READ_BUF
|
|
26
|
-
#define CERVER_READ_BUF_MAX
|
|
27
|
-
#define CERVER_MAX_ROUTES
|
|
20
|
+
#define CERVER_MAX_HEADERS 64
|
|
21
|
+
#define CERVER_MAX_PARAMS 16
|
|
22
|
+
#define CERVER_MAX_QUERY 32
|
|
23
|
+
#define CERVER_MAX_PATH 2048
|
|
24
|
+
#define CERVER_MAX_HEADER_VAL 4096
|
|
25
|
+
#define CERVER_READ_BUF 8192
|
|
26
|
+
#define CERVER_READ_BUF_MAX (1 << 20) /* 1 MB hard limit */
|
|
27
|
+
#define CERVER_MAX_ROUTES 256
|
|
28
28
|
|
|
29
29
|
/* Keep-alive settings */
|
|
30
|
-
#define CERVER_KEEPALIVE_MAX
|
|
31
|
-
#define CERVER_KEEPALIVE_TIMEOUT 5
|
|
30
|
+
#define CERVER_KEEPALIVE_MAX 2000000000 /* max requests per connection */
|
|
31
|
+
#define CERVER_KEEPALIVE_TIMEOUT 5 /* seconds idle between requests */
|
|
32
|
+
#define CERVER_KEEPALIVE_MAX 2000000000 /* max requests per connection */
|
|
33
|
+
#define CERVER_KEEPALIVE_TIMEOUT 5 /* seconds idle between requests */
|
|
32
34
|
|
|
33
35
|
/* Event loop tuning */
|
|
34
|
-
#define CERVER_MAX_EVENTS
|
|
35
|
-
#define CERVER_LISTEN_BACKLOG
|
|
36
|
+
#define CERVER_MAX_EVENTS 256
|
|
37
|
+
#define CERVER_LISTEN_BACKLOG 4096
|
|
36
38
|
|
|
37
39
|
/* Worker architecture */
|
|
38
|
-
#define CERVER_THREAD_POOL_DEFAULT
|
|
39
|
-
#define CERVER_TASK_QUEUE_SIZE
|
|
40
|
+
#define CERVER_THREAD_POOL_DEFAULT 4
|
|
41
|
+
#define CERVER_TASK_QUEUE_SIZE 1024
|
|
40
42
|
|
|
41
43
|
/* Stat cache for filesystem serving */
|
|
42
|
-
#define CERVER_STAT_CACHE_SIZE
|
|
43
|
-
#define CERVER_STAT_CACHE_TTL
|
|
44
|
+
#define CERVER_STAT_CACHE_SIZE 256
|
|
45
|
+
#define CERVER_STAT_CACHE_TTL 60 /* seconds */
|
|
44
46
|
|
|
45
47
|
/* ------------------------------------------------------------------ */
|
|
46
48
|
/* Key-value pair (used for headers, query params, route params) */
|
|
47
49
|
/* ------------------------------------------------------------------ */
|
|
48
50
|
|
|
49
51
|
typedef struct {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
const char* key;
|
|
53
|
+
const char* value;
|
|
52
54
|
} cerver_kv_t;
|
|
53
55
|
|
|
54
56
|
/* ------------------------------------------------------------------ */
|
|
@@ -56,34 +58,34 @@ typedef struct {
|
|
|
56
58
|
/* ------------------------------------------------------------------ */
|
|
57
59
|
|
|
58
60
|
typedef struct {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
/* HTTP method: "GET", "POST", etc. */
|
|
62
|
+
char method[16];
|
|
61
63
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
/* Decoded path (no query string) */
|
|
65
|
+
char path[CERVER_MAX_PATH];
|
|
64
66
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
/* Raw query string (after '?') */
|
|
68
|
+
char query_string[CERVER_MAX_PATH];
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
/* Parsed query parameters */
|
|
71
|
+
cerver_kv_t query[CERVER_MAX_QUERY];
|
|
72
|
+
int query_count;
|
|
71
73
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
/* Route parameters (from dynamic segments like :key) */
|
|
75
|
+
cerver_kv_t params[CERVER_MAX_PARAMS];
|
|
76
|
+
int params_count;
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
/* Request headers */
|
|
79
|
+
cerver_kv_t headers[CERVER_MAX_HEADERS];
|
|
80
|
+
int header_count;
|
|
79
81
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
82
|
+
/* Request body (for POST) */
|
|
83
|
+
const char* body;
|
|
84
|
+
size_t body_len;
|
|
83
85
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
/* Internal: raw buffer ownership (NULL if in-place parsing used) */
|
|
87
|
+
char* _raw_buf;
|
|
88
|
+
size_t _raw_len;
|
|
87
89
|
} cerver_request_t;
|
|
88
90
|
|
|
89
91
|
/* ------------------------------------------------------------------ */
|
|
@@ -91,53 +93,53 @@ typedef struct {
|
|
|
91
93
|
/* ------------------------------------------------------------------ */
|
|
92
94
|
|
|
93
95
|
typedef struct {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
+
int status;
|
|
97
|
+
const char* content_type;
|
|
96
98
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
/* Response body — can be heap-allocated or static */
|
|
100
|
+
const char* body;
|
|
101
|
+
size_t body_len;
|
|
100
102
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
/* Extra headers */
|
|
104
|
+
cerver_kv_t headers[CERVER_MAX_HEADERS];
|
|
105
|
+
int header_count;
|
|
104
106
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
+
/* Internal flag: was body malloc'd? */
|
|
108
|
+
int _body_owned;
|
|
107
109
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
+
/* Keep-alive control: set to 1 to force close after response */
|
|
111
|
+
int _force_close;
|
|
110
112
|
} cerver_response_t;
|
|
111
113
|
|
|
112
114
|
/* Response helpers — called by generated handler code */
|
|
113
|
-
void cerver_res_text(cerver_response_t
|
|
114
|
-
void cerver_res_json(cerver_response_t
|
|
115
|
-
void cerver_res_html(cerver_response_t
|
|
116
|
-
void cerver_res_file(cerver_response_t
|
|
117
|
-
const unsigned char
|
|
118
|
-
void cerver_res_header(cerver_response_t
|
|
115
|
+
void cerver_res_text(cerver_response_t* res, int status, const char* text);
|
|
116
|
+
void cerver_res_json(cerver_response_t* res, int status, const char* json);
|
|
117
|
+
void cerver_res_html(cerver_response_t* res, int status, const char* html);
|
|
118
|
+
void cerver_res_file(cerver_response_t* res, int status, const char* mime,
|
|
119
|
+
const unsigned char* data, size_t len);
|
|
120
|
+
void cerver_res_header(cerver_response_t* res, const char* key, const char* val);
|
|
119
121
|
|
|
120
122
|
/* ------------------------------------------------------------------ */
|
|
121
123
|
/* Request helpers */
|
|
122
124
|
/* ------------------------------------------------------------------ */
|
|
123
125
|
|
|
124
|
-
const char
|
|
125
|
-
const char
|
|
126
|
-
const char
|
|
126
|
+
const char* cerver_req_param(const cerver_request_t* req, const char* key);
|
|
127
|
+
const char* cerver_req_query(const cerver_request_t* req, const char* key);
|
|
128
|
+
const char* cerver_req_header(const cerver_request_t* req, const char* key);
|
|
127
129
|
|
|
128
130
|
/* Check if client wants to close after this request */
|
|
129
|
-
int cerver_req_wants_close(const cerver_request_t
|
|
131
|
+
int cerver_req_wants_close(const cerver_request_t* req);
|
|
130
132
|
|
|
131
133
|
/* ------------------------------------------------------------------ */
|
|
132
134
|
/* Route definition */
|
|
133
135
|
/* ------------------------------------------------------------------ */
|
|
134
136
|
|
|
135
|
-
typedef void (*cerver_handler_fn)(cerver_request_t
|
|
137
|
+
typedef void (*cerver_handler_fn)(cerver_request_t* req, cerver_response_t* res);
|
|
136
138
|
|
|
137
139
|
typedef struct {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
140
|
+
const char* method; /* "GET", "POST" */
|
|
141
|
+
const char* pattern; /* "/", "/art/:key", "/api/projects" */
|
|
142
|
+
cerver_handler_fn handler;
|
|
141
143
|
} cerver_route_t;
|
|
142
144
|
|
|
143
145
|
/* ------------------------------------------------------------------ */
|
|
@@ -145,20 +147,20 @@ typedef struct {
|
|
|
145
147
|
/* ------------------------------------------------------------------ */
|
|
146
148
|
|
|
147
149
|
typedef struct {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
150
|
+
const char* path; /* e.g. "/index.html" */
|
|
151
|
+
const char* mime_type; /* e.g. "text/html" */
|
|
152
|
+
const unsigned char* data;
|
|
153
|
+
size_t data_len;
|
|
154
|
+
|
|
155
|
+
/* Pre-compressed variants (NULL if not available) */
|
|
156
|
+
const unsigned char* data_gz;
|
|
157
|
+
size_t data_gz_len;
|
|
158
|
+
const unsigned char* data_br;
|
|
159
|
+
size_t data_br_len;
|
|
160
|
+
|
|
161
|
+
/* Pre-computed response header (NULL if not generated) */
|
|
162
|
+
const char* prebuilt_header;
|
|
163
|
+
size_t prebuilt_header_len;
|
|
162
164
|
} cerver_asset_t;
|
|
163
165
|
|
|
164
166
|
/* ------------------------------------------------------------------ */
|
|
@@ -166,23 +168,23 @@ typedef struct {
|
|
|
166
168
|
/* ------------------------------------------------------------------ */
|
|
167
169
|
|
|
168
170
|
typedef struct {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
171
|
+
char path[CERVER_MAX_PATH];
|
|
172
|
+
size_t file_size;
|
|
173
|
+
time_t mtime;
|
|
174
|
+
time_t cached_at;
|
|
175
|
+
int valid;
|
|
174
176
|
} cerver_stat_entry_t;
|
|
175
177
|
|
|
176
178
|
typedef struct {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
+
cerver_stat_entry_t entries[CERVER_STAT_CACHE_SIZE];
|
|
180
|
+
pthread_mutex_t lock;
|
|
179
181
|
} cerver_stat_cache_t;
|
|
180
182
|
|
|
181
183
|
/* ------------------------------------------------------------------ */
|
|
182
184
|
/* Generated dispatch (compile-time route optimization) */
|
|
183
185
|
/* ------------------------------------------------------------------ */
|
|
184
186
|
|
|
185
|
-
typedef cerver_handler_fn (*cerver_dispatch_fn)(cerver_request_t
|
|
187
|
+
typedef cerver_handler_fn (*cerver_dispatch_fn)(cerver_request_t* req);
|
|
186
188
|
|
|
187
189
|
/* ------------------------------------------------------------------ */
|
|
188
190
|
/* Worker state (per-core event loop) */
|
|
@@ -191,11 +193,11 @@ typedef cerver_handler_fn (*cerver_dispatch_fn)(cerver_request_t *req);
|
|
|
191
193
|
typedef struct cerver_server cerver_server_t;
|
|
192
194
|
|
|
193
195
|
typedef struct {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
196
|
+
int id;
|
|
197
|
+
int event_fd; /* kqueue or epoll fd */
|
|
198
|
+
int listen_fd; /* per-worker on Linux, shared on macOS */
|
|
199
|
+
cerver_server_t* srv;
|
|
200
|
+
pthread_t thread;
|
|
199
201
|
} cerver_worker_t;
|
|
200
202
|
|
|
201
203
|
/* ------------------------------------------------------------------ */
|
|
@@ -203,75 +205,73 @@ typedef struct {
|
|
|
203
205
|
/* ------------------------------------------------------------------ */
|
|
204
206
|
|
|
205
207
|
struct cerver_server {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
208
|
+
int port;
|
|
209
|
+
int sock_fd;
|
|
210
|
+
cerver_route_t* routes;
|
|
211
|
+
int route_count;
|
|
212
|
+
cerver_asset_t* assets;
|
|
213
|
+
int asset_count;
|
|
214
|
+
const char* public_dir; /* NULL if embedded mode */
|
|
215
|
+
volatile int running;
|
|
216
|
+
|
|
217
|
+
/* Generated dispatch override (faster than generic router) */
|
|
218
|
+
cerver_dispatch_fn dispatch_override;
|
|
219
|
+
|
|
220
|
+
/* Stat cache for filesystem serving */
|
|
221
|
+
cerver_stat_cache_t stat_cache;
|
|
222
|
+
|
|
223
|
+
/* Worker pool */
|
|
224
|
+
int worker_count;
|
|
225
|
+
cerver_worker_t* workers;
|
|
224
226
|
};
|
|
225
227
|
|
|
226
228
|
/* Server lifecycle */
|
|
227
|
-
int
|
|
228
|
-
int
|
|
229
|
-
int
|
|
230
|
-
void cerver_set_public_dir(cerver_server_t
|
|
231
|
-
void cerver_set_dispatch(cerver_server_t
|
|
232
|
-
int
|
|
233
|
-
void cerver_shutdown(cerver_server_t
|
|
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);
|
|
232
|
+
void cerver_set_public_dir(cerver_server_t* srv, const char* dir);
|
|
233
|
+
void cerver_set_dispatch(cerver_server_t* srv, cerver_dispatch_fn fn);
|
|
234
|
+
int cerver_listen(cerver_server_t* srv);
|
|
235
|
+
void cerver_shutdown(cerver_server_t* srv);
|
|
234
236
|
|
|
235
237
|
/* ------------------------------------------------------------------ */
|
|
236
238
|
/* HTTP parser (internal) */
|
|
237
239
|
/* ------------------------------------------------------------------ */
|
|
238
240
|
|
|
239
|
-
int cerver_parse_request(const char
|
|
241
|
+
int cerver_parse_request(const char* raw, size_t len, cerver_request_t* req);
|
|
240
242
|
|
|
241
243
|
/* ------------------------------------------------------------------ */
|
|
242
244
|
/* HTTP writer (internal) */
|
|
243
245
|
/* ------------------------------------------------------------------ */
|
|
244
246
|
|
|
245
|
-
int cerver_write_response(int fd, const cerver_response_t
|
|
247
|
+
int cerver_write_response(int fd, const cerver_response_t* res, int keepalive);
|
|
246
248
|
|
|
247
249
|
/* ------------------------------------------------------------------ */
|
|
248
250
|
/* Router (internal) */
|
|
249
251
|
/* ------------------------------------------------------------------ */
|
|
250
252
|
|
|
251
|
-
int cerver_route_match(const cerver_route_t
|
|
252
|
-
cerver_handler_fn cerver_dispatch(cerver_server_t
|
|
253
|
+
int cerver_route_match(const cerver_route_t* route, cerver_request_t* req);
|
|
254
|
+
cerver_handler_fn cerver_dispatch(cerver_server_t* srv, cerver_request_t* req);
|
|
253
255
|
|
|
254
256
|
/* ------------------------------------------------------------------ */
|
|
255
257
|
/* MIME (internal) */
|
|
256
258
|
/* ------------------------------------------------------------------ */
|
|
257
259
|
|
|
258
|
-
const char
|
|
260
|
+
const char* cerver_mime_from_path(const char* path);
|
|
259
261
|
|
|
260
262
|
/* ------------------------------------------------------------------ */
|
|
261
263
|
/* Static file serving (internal) */
|
|
262
264
|
/* ------------------------------------------------------------------ */
|
|
263
265
|
|
|
264
|
-
int cerver_serve_static(cerver_server_t
|
|
265
|
-
cerver_response_t *res);
|
|
266
|
+
int cerver_serve_static(cerver_server_t* srv, cerver_request_t* req, cerver_response_t* res);
|
|
266
267
|
|
|
267
268
|
/* ------------------------------------------------------------------ */
|
|
268
269
|
/* Stat cache (internal) */
|
|
269
270
|
/* ------------------------------------------------------------------ */
|
|
270
271
|
|
|
271
|
-
void cerver_stat_cache_init(cerver_stat_cache_t
|
|
272
|
-
int
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
size_t file_size, time_t mtime);
|
|
272
|
+
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);
|
|
274
|
+
void cerver_stat_cache_store(cerver_stat_cache_t* cache, const char* path, size_t file_size,
|
|
275
|
+
time_t mtime);
|
|
276
276
|
|
|
277
277
|
#endif /* CERVER_H */
|