@velox0/cerver 0.4.0 → 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/mime.c CHANGED
@@ -5,80 +5,80 @@
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 {
11
- const char *ext;
12
- const char *mime;
12
+ const char* ext;
13
+ const char* mime;
13
14
  } mime_entry_t;
14
15
 
15
16
  static const mime_entry_t mime_table[] = {
16
17
  /* Web essentials */
17
- { ".html", "text/html; charset=utf-8" },
18
- { ".htm", "text/html; charset=utf-8" },
19
- { ".css", "text/css; charset=utf-8" },
20
- { ".js", "application/javascript; charset=utf-8" },
21
- { ".mjs", "application/javascript; charset=utf-8" },
22
- { ".json", "application/json; charset=utf-8" },
23
- { ".xml", "application/xml; charset=utf-8" },
18
+ {".html", "text/html; charset=utf-8"},
19
+ {".htm", "text/html; charset=utf-8"},
20
+ {".css", "text/css; charset=utf-8"},
21
+ {".js", "application/javascript; charset=utf-8"},
22
+ {".mjs", "application/javascript; charset=utf-8"},
23
+ {".json", "application/json; charset=utf-8"},
24
+ {".xml", "application/xml; charset=utf-8"},
24
25
 
25
26
  /* Text */
26
- { ".txt", "text/plain; charset=utf-8" },
27
- { ".csv", "text/csv; charset=utf-8" },
28
- { ".md", "text/markdown; charset=utf-8" },
27
+ {".txt", "text/plain; charset=utf-8"},
28
+ {".csv", "text/csv; charset=utf-8"},
29
+ {".md", "text/markdown; charset=utf-8"},
29
30
 
30
31
  /* Images */
31
- { ".png", "image/png" },
32
- { ".jpg", "image/jpeg" },
33
- { ".jpeg", "image/jpeg" },
34
- { ".gif", "image/gif" },
35
- { ".svg", "image/svg+xml" },
36
- { ".ico", "image/x-icon" },
37
- { ".webp", "image/webp" },
38
- { ".avif", "image/avif" },
32
+ {".png", "image/png"},
33
+ {".jpg", "image/jpeg"},
34
+ {".jpeg", "image/jpeg"},
35
+ {".gif", "image/gif"},
36
+ {".svg", "image/svg+xml"},
37
+ {".ico", "image/x-icon"},
38
+ {".webp", "image/webp"},
39
+ {".avif", "image/avif"},
39
40
 
40
41
  /* Fonts */
41
- { ".woff", "font/woff" },
42
- { ".woff2", "font/woff2" },
43
- { ".ttf", "font/ttf" },
44
- { ".otf", "font/otf" },
45
- { ".eot", "application/vnd.ms-fontobject" },
42
+ {".woff", "font/woff"},
43
+ {".woff2", "font/woff2"},
44
+ {".ttf", "font/ttf"},
45
+ {".otf", "font/otf"},
46
+ {".eot", "application/vnd.ms-fontobject"},
46
47
 
47
48
  /* Media */
48
- { ".mp4", "video/mp4" },
49
- { ".webm", "video/webm" },
50
- { ".ogg", "audio/ogg" },
51
- { ".mp3", "audio/mpeg" },
52
- { ".wav", "audio/wav" },
49
+ {".mp4", "video/mp4"},
50
+ {".webm", "video/webm"},
51
+ {".ogg", "audio/ogg"},
52
+ {".mp3", "audio/mpeg"},
53
+ {".wav", "audio/wav"},
53
54
 
54
55
  /* Archives */
55
- { ".zip", "application/zip" },
56
- { ".gz", "application/gzip" },
57
- { ".tar", "application/x-tar" },
56
+ {".zip", "application/zip"},
57
+ {".gz", "application/gzip"},
58
+ {".tar", "application/x-tar"},
58
59
 
59
60
  /* Documents */
60
- { ".pdf", "application/pdf" },
61
+ {".pdf", "application/pdf"},
61
62
 
62
63
  /* Misc */
63
- { ".wasm", "application/wasm" },
64
- { ".map", "application/json" },
64
+ {".wasm", "application/wasm"},
65
+ {".map", "application/json"},
65
66
 
66
- { NULL, NULL }
67
- };
67
+ {NULL, NULL}};
68
68
 
69
- const char *cerver_mime_from_path(const char *path) {
70
- if (!path) return "application/octet-stream";
69
+ const char* cerver_mime_from_path(const char* path) {
70
+ if (!path) return "application/octet-stream";
71
71
 
72
- /* Find the last '.' in the path */
73
- const char *dot = strrchr(path, '.');
74
- if (!dot) return "application/octet-stream";
72
+ /* Find the last '.' in the path */
73
+ const char* dot = strrchr(path, '.');
74
+ if (!dot) return "application/octet-stream";
75
75
 
76
- /* Case-insensitive extension match */
77
- for (const mime_entry_t *entry = mime_table; entry->ext; entry++) {
78
- if (strcasecmp(dot, entry->ext) == 0) {
79
- return entry->mime;
80
- }
76
+ /* Case-insensitive extension match */
77
+ for (const mime_entry_t* entry = mime_table; entry->ext; entry++) {
78
+ if (strcasecmp(dot, entry->ext) == 0) {
79
+ return entry->mime;
81
80
  }
81
+ }
82
82
 
83
- return "application/octet-stream";
83
+ return "application/octet-stream";
84
84
  }
package/runtime/router.c CHANGED
@@ -11,36 +11,37 @@
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 */
17
18
  /* ------------------------------------------------------------------ */
18
19
 
19
- const char *cerver_req_param(const cerver_request_t *req, const char *key) {
20
- for (int i = 0; i < req->params_count; i++) {
21
- if (strcmp(req->params[i].key, key) == 0) {
22
- return req->params[i].value;
23
- }
20
+ const char* cerver_req_param(const cerver_request_t* req, const char* key) {
21
+ for (int i = 0; i < req->params_count; i++) {
22
+ if (strcmp(req->params[i].key, key) == 0) {
23
+ return req->params[i].value;
24
24
  }
25
- return "";
25
+ }
26
+ return "";
26
27
  }
27
28
 
28
- const char *cerver_req_query(const cerver_request_t *req, const char *key) {
29
- for (int i = 0; i < req->query_count; i++) {
30
- if (strcmp(req->query[i].key, key) == 0) {
31
- return req->query[i].value;
32
- }
29
+ const char* cerver_req_query(const cerver_request_t* req, const char* key) {
30
+ for (int i = 0; i < req->query_count; i++) {
31
+ if (strcmp(req->query[i].key, key) == 0) {
32
+ return req->query[i].value;
33
33
  }
34
- return "";
34
+ }
35
+ return "";
35
36
  }
36
37
 
37
- const char *cerver_req_header(const cerver_request_t *req, const char *key) {
38
- for (int i = 0; i < req->header_count; i++) {
39
- if (strcasecmp(req->headers[i].key, key) == 0) {
40
- return req->headers[i].value;
41
- }
38
+ const char* cerver_req_header(const cerver_request_t* req, const char* key) {
39
+ for (int i = 0; i < req->header_count; i++) {
40
+ if (strcasecmp(req->headers[i].key, key) == 0) {
41
+ return req->headers[i].value;
42
42
  }
43
- return NULL;
43
+ }
44
+ return NULL;
44
45
  }
45
46
 
46
47
  /* ------------------------------------------------------------------ */
@@ -51,20 +52,20 @@ const char *cerver_req_header(const cerver_request_t *req, const char *key) {
51
52
  * Returns 1 if the client sent "Connection: close" or is HTTP/1.0
52
53
  * without an explicit "Connection: keep-alive".
53
54
  */
54
- int cerver_req_wants_close(const cerver_request_t *req) {
55
- const char *conn = cerver_req_header(req, "Connection");
56
- if (conn && strcasecmp(conn, "close") == 0) return 1;
57
- /* HTTP/1.0 without explicit keep-alive → close */
58
- /* (We don't track HTTP version separately, so default keep-alive for 1.1) */
59
- return 0;
55
+ int cerver_req_wants_close(const cerver_request_t* req) {
56
+ const char* conn = cerver_req_header(req, "Connection");
57
+ if (conn && strcasecmp(conn, "close") == 0) return 1;
58
+ /* HTTP/1.0 without explicit keep-alive → close */
59
+ /* (We don't track HTTP version separately, so default keep-alive for 1.1) */
60
+ return 0;
60
61
  }
61
62
 
62
63
  /* ------------------------------------------------------------------ */
63
64
  /* Server configuration helpers */
64
65
  /* ------------------------------------------------------------------ */
65
66
 
66
- void cerver_set_dispatch(cerver_server_t *srv, cerver_dispatch_fn fn) {
67
- srv->dispatch_override = fn;
67
+ void cerver_set_dispatch(cerver_server_t* srv, cerver_dispatch_fn fn) {
68
+ srv->dispatch_override = fn;
68
69
  }
69
70
 
70
71
  /* ------------------------------------------------------------------ */
@@ -77,96 +78,96 @@ void cerver_set_dispatch(cerver_server_t *srv, cerver_dispatch_fn fn) {
77
78
  *
78
79
  * Uses manual segment iteration instead of strtok_r for speed.
79
80
  */
80
- int cerver_route_match(const cerver_route_t *route, cerver_request_t *req) {
81
- /* Method must match */
82
- if (strcmp(route->method, req->method) != 0) {
83
- return 0;
84
- }
81
+ int cerver_route_match(const cerver_route_t* route, cerver_request_t* req) {
82
+ /* Method must match */
83
+ if (strcmp(route->method, req->method) != 0) {
84
+ return 0;
85
+ }
85
86
 
86
- const char *pattern = route->pattern;
87
- const char *path = req->path;
87
+ const char* pattern = route->pattern;
88
+ const char* path = req->path;
88
89
 
89
- /* Fast path: exact match */
90
- if (strcmp(pattern, path) == 0) {
91
- return 1;
92
- }
90
+ /* Fast path: exact match */
91
+ if (strcmp(pattern, path) == 0) {
92
+ return 1;
93
+ }
93
94
 
94
- /* No dynamic segments? Then the strcmp above was definitive */
95
- if (!strchr(pattern, ':')) {
95
+ /* No dynamic segments? Then the strcmp above was definitive */
96
+ if (!strchr(pattern, ':')) {
97
+ return 0;
98
+ }
99
+
100
+ /* Segment-by-segment matching without strtok_r */
101
+ const char* pp = pattern; /* pattern pointer */
102
+ const char* rp = path; /* request path pointer */
103
+
104
+ int saved_params = req->params_count;
105
+
106
+ /* Skip leading '/' */
107
+ if (*pp == '/') pp++;
108
+ if (*rp == '/') rp++;
109
+
110
+ while (*pp && *rp) {
111
+ /* Extract pattern segment */
112
+ const char* pp_seg = pp;
113
+ while (*pp && *pp != '/') pp++;
114
+ size_t pp_len = (size_t)(pp - pp_seg);
115
+
116
+ /* Extract path segment */
117
+ const char* rp_seg = rp;
118
+ while (*rp && *rp != '/') rp++;
119
+ size_t rp_len = (size_t)(rp - rp_seg);
120
+
121
+ if (pp_seg[0] == ':') {
122
+ /* Dynamic segment — extract parameter */
123
+ if (req->params_count < CERVER_MAX_PARAMS) {
124
+ req->params[req->params_count].key = pp_seg + 1;
125
+ /* Temporarily NUL-terminate the key at the slash */
126
+ /* The key points into the route pattern (static/const) */
127
+ req->params[req->params_count].value = rp_seg;
128
+ req->params_count++;
129
+ }
130
+ } else {
131
+ /* Static segment — must match exactly */
132
+ if (pp_len != rp_len || memcmp(pp_seg, rp_seg, pp_len) != 0) {
133
+ req->params_count = saved_params;
96
134
  return 0;
135
+ }
97
136
  }
98
137
 
99
- /* Segment-by-segment matching without strtok_r */
100
- const char *pp = pattern; /* pattern pointer */
101
- const char *rp = path; /* request path pointer */
102
-
103
- int saved_params = req->params_count;
104
-
105
- /* Skip leading '/' */
138
+ /* Skip '/' separator */
106
139
  if (*pp == '/') pp++;
107
140
  if (*rp == '/') rp++;
141
+ }
108
142
 
109
- while (*pp && *rp) {
110
- /* Extract pattern segment */
111
- const char *pp_seg = pp;
112
- while (*pp && *pp != '/') pp++;
113
- size_t pp_len = (size_t)(pp - pp_seg);
114
-
115
- /* Extract path segment */
116
- const char *rp_seg = rp;
117
- while (*rp && *rp != '/') rp++;
118
- size_t rp_len = (size_t)(rp - rp_seg);
119
-
120
- if (pp_seg[0] == ':') {
121
- /* Dynamic segment — extract parameter */
122
- if (req->params_count < CERVER_MAX_PARAMS) {
123
- req->params[req->params_count].key = pp_seg + 1;
124
- /* Temporarily NUL-terminate the key at the slash */
125
- /* The key points into the route pattern (static/const) */
126
- req->params[req->params_count].value = rp_seg;
127
- req->params_count++;
128
- }
129
- } else {
130
- /* Static segment — must match exactly */
131
- if (pp_len != rp_len || memcmp(pp_seg, rp_seg, pp_len) != 0) {
132
- req->params_count = saved_params;
133
- return 0;
134
- }
135
- }
136
-
137
- /* Skip '/' separator */
138
- if (*pp == '/') pp++;
139
- if (*rp == '/') rp++;
140
- }
141
-
142
- /* Both must be consumed */
143
- if (*pp || *rp) {
144
- req->params_count = saved_params;
145
- return 0;
146
- }
143
+ /* Both must be consumed */
144
+ if (*pp || *rp) {
145
+ req->params_count = saved_params;
146
+ return 0;
147
+ }
147
148
 
148
- return 1;
149
+ return 1;
149
150
  }
150
151
 
151
152
  /* ------------------------------------------------------------------ */
152
153
  /* Dispatch: find and return the handler for a request */
153
154
  /* ------------------------------------------------------------------ */
154
155
 
155
- cerver_handler_fn cerver_dispatch(cerver_server_t *srv, cerver_request_t *req) {
156
- /* Try the generated compile-time dispatch first */
157
- if (srv->dispatch_override) {
158
- cerver_handler_fn h = srv->dispatch_override(req);
159
- if (h) return h;
160
- }
156
+ cerver_handler_fn cerver_dispatch(cerver_server_t* srv, cerver_request_t* req) {
157
+ /* Try the generated compile-time dispatch first */
158
+ if (srv->dispatch_override) {
159
+ cerver_handler_fn h = srv->dispatch_override(req);
160
+ if (h) return h;
161
+ }
161
162
 
162
- /* Fall back to generic route table scan */
163
- if (!srv->routes) return NULL;
163
+ /* Fall back to generic route table scan */
164
+ if (!srv->routes) return NULL;
164
165
 
165
- for (int i = 0; i < srv->route_count; i++) {
166
- if (cerver_route_match(&srv->routes[i], req)) {
167
- return srv->routes[i].handler;
168
- }
166
+ for (int i = 0; i < srv->route_count; i++) {
167
+ if (cerver_route_match(&srv->routes[i], req)) {
168
+ return srv->routes[i].handler;
169
169
  }
170
+ }
170
171
 
171
- return NULL;
172
+ return NULL;
172
173
  }