File Coverage

feersum_utils.c.inc
Criterion Covered Total %
statement 131 162 80.8
branch 52 78 66.6
condition n/a
subroutine n/a
pod n/a
total 183 240 76.2


line stmt bran cond sub pod time code
1             char DATE_BUF[DATE_HEADER_LENGTH+1] = "Date: Thu, 01 Jan 1970 00:00:00 GMT\015\012";
2              
3             char header_key_buf[HEADER_KEY_BUFSZ] = "HTTP_";
4              
5             int FEERSUM_FREELIST_MAX = 32;
6              
7             struct iomatrix *iomatrix_freelist = NULL;
8             int iomatrix_freelist_count = 0;
9             struct feer_req *feer_req_freelist = NULL;
10             int feer_req_freelist_count = 0;
11              
12             HV *feer_stash, *feer_conn_stash;
13             HV *feer_conn_reader_stash = NULL, *feer_conn_writer_stash = NULL;
14             MGVTBL psgix_io_vtbl;
15             struct feer_server *default_server = NULL;
16             struct ev_loop *feersum_ev_loop = NULL;
17             ev_timer date_timer;
18             int date_timer_refs = 0;
19             AV *psgi_ver;
20             SV *psgi_serv10, *psgi_serv11;
21             SV *method_GET, *method_POST, *method_HEAD, *method_PUT, *method_PATCH, *method_DELETE, *method_OPTIONS;
22             SV *status_200, *status_201, *status_204, *status_301, *status_302, *status_304;
23             SV *status_400, *status_404, *status_500;
24             SV *empty_query_sv;
25             SV *psgi_env_version = NULL;
26             SV *psgi_env_errors = NULL;
27              
28             const unsigned char ascii_lower[256] = {
29             0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
30             16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
31             32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
32             48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
33             64,'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
34             'p','q','r','s','t','u','v','w','x','y','z',91,92,93,94,95,
35             96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
36             112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
37             128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
38             144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
39             160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
40             176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
41             192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
42             208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
43             224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
44             240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
45             };
46              
47             const unsigned char ascii_upper[256] = {
48             0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
49             16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
50             32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
51             48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
52             64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
53             80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
54             96,'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
55             'P','Q','R','S','T','U','V','W','X','Y','Z',123,124,125,126,127,
56             128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
57             144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
58             160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
59             176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
60             192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
61             208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
62             224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
63             240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
64             };
65              
66             const unsigned char ascii_upper_dash[256] = {
67             0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
68             16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
69             32,33,34,35,36,37,38,39,40,41,42,43,44,'_',46,47,
70             48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
71             64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
72             80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
73             96,'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
74             'P','Q','R','S','T','U','V','W','X','Y','Z',123,124,125,126,127,
75             128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
76             144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
77             160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
78             176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
79             192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
80             208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
81             224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
82             240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
83             };
84              
85             const unsigned char ascii_lower_dash[256] = {
86             0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
87             16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
88             32,33,34,35,36,37,38,39,40,41,42,43,44,'_',46,47,
89             48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
90             64,'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
91             'p','q','r','s','t','u','v','w','x','y','z',91,92,93,94,95,
92             96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
93             112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
94             128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
95             144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
96             160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
97             176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
98             192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
99             208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
100             224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
101             240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
102             };
103              
104             const unsigned char hex_decode_table[256] = {
105             0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
106             0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
107             0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
108             0,1,2,3,4,5,6,7,8,9,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
109             0xFF,10,11,12,13,14,15,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
110             0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
111             0xFF,10,11,12,13,14,15,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
112             0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
113             0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
114             0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
115             0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
116             0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
117             0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
118             0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
119             0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
120             0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
121             };
122              
123             // Returns 0-15 for valid hex digit, -1 for invalid
124             #define hex_decode(ch) ((int)(signed char)hex_decode_table[(unsigned char)(ch)])
125              
126             /*
127             * Case-insensitive comparison where neither string is pre-lowered.
128             * Lengths must already be verified to match.
129             */
130             INLINE_UNLESS_DEBUG bool
131 5           str_case_eq_both(const char *a, const char *b, size_t len)
132             {
133             size_t i;
134 57 100         for (i=0; i
135 52 50         if (ascii_lower[(unsigned char)a[i]] != ascii_lower[(unsigned char)b[i]]) return 0;
136             }
137 5           return 1;
138             }
139              
140             /*
141             * Fixed-length case-insensitive comparison.
142             * First argument must be a pre-lowered literal; only `b` is lowered at runtime.
143             */
144             INLINE_UNLESS_DEBUG bool
145 1585           str_case_eq_fixed(const char *a, const char *b, size_t len)
146             {
147             size_t i;
148 13153 100         for (i=0; i
149 11753 100         if (a[i] != ascii_lower[(unsigned char)b[i]]) return 0;
150             }
151 1400           return 1;
152             }
153              
154             static void
155 284           uri_decode_sv (SV *sv)
156             {
157             STRLEN len;
158             char *ptr, *end, *decoded;
159              
160 284           ptr = SvPV(sv, len);
161 284           end = SvEND(sv);
162              
163             // quickly scan for % so we can ignore decoding that portion of the string
164 17976 100         while (ptr < end) {
165 17695 100         if (unlikely(*ptr == '%')) goto needs_decode;
166 17692           ptr++;
167             }
168 281           return;
169              
170 3           needs_decode:
171              
172             // Up until ptr have been "decoded" already by virtue of those chars not
173             // being encoded.
174 3           decoded = ptr;
175              
176 31 100         for (; ptr < end; ptr++) {
177 28 100         if (unlikely(*ptr == '%') && likely(end - ptr > 2)) {
    100          
178 8           int c1 = hex_decode(ptr[1]);
179 8           int c2 = hex_decode(ptr[2]);
180 8 100         if (likely(c1 != -1 && c2 != -1)) {
    50          
181 7           *decoded++ = (c1 << 4) + c2;
182 7           ptr += 2;
183 7           continue;
184             }
185             }
186 21           *decoded++ = *ptr;
187             }
188              
189 3           *decoded = '\0'; // play nice with C
190              
191 3           SvCUR_set(sv, decoded - SvPVX(sv));
192             }
193              
194             // populate connection-level addr/port cache (called once per connection)
195             static void
196 304           feersum_set_conn_remote_info(pTHX_ struct feer_conn *c)
197             {
198 304 100         if (c->remote_addr) return; // already cached
199 246           struct sockaddr *sa = (struct sockaddr *)&c->sa;
200 246           switch (sa->sa_family) {
201 242           case AF_INET: {
202 242           struct sockaddr_in *in = (struct sockaddr_in *)sa;
203             char buf[INET_ADDRSTRLEN];
204 242 50         if (inet_ntop(AF_INET, &in->sin_addr, buf, sizeof(buf))) {
205 242           c->remote_addr = newSVpv(buf, 0);
206             } else {
207 0           c->remote_addr = newSVpvs("0.0.0.0");
208             }
209 242           c->remote_port = newSViv(ntohs(in->sin_port));
210 242           break;
211             }
212             #ifdef AF_INET6
213 4           case AF_INET6: {
214 4           struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)sa;
215             char buf[INET6_ADDRSTRLEN];
216 4 50         if (inet_ntop(AF_INET6, &in6->sin6_addr, buf, sizeof(buf))) {
217 4           c->remote_addr = newSVpv(buf, 0);
218             } else {
219 0           c->remote_addr = newSVpvs("::");
220             }
221 4           c->remote_port = newSViv(ntohs(in6->sin6_port));
222 4           break;
223             }
224             #endif
225             #ifdef AF_UNIX
226 0           case AF_UNIX:
227 0           c->remote_addr = newSVpvs("unix");
228 0           c->remote_port = newSViv(0);
229 0           break;
230             #endif
231 0           default:
232 0           c->remote_addr = newSVpvs("unspec");
233 0           c->remote_port = newSViv(0);
234 0           break;
235             }
236             }
237              
238             static SV*
239 286           feersum_env_method(pTHX_ struct feer_req *r)
240             {
241             // Return cached SV for common HTTP methods (avoids newSVpvn per request)
242             // Use length-first dispatch with memcmp for consistency
243 286           switch (r->method_len) {
244 189           case 3:
245 189 50         if (likely(memcmp(r->method, "GET", 3) == 0))
246 189           return SvREFCNT_inc_simple_NN(method_GET);
247 0 0         if (memcmp(r->method, "PUT", 3) == 0)
248 0           return SvREFCNT_inc_simple_NN(method_PUT);
249 0           break;
250 94           case 4:
251 94 100         if (likely(memcmp(r->method, "POST", 4) == 0))
252 93           return SvREFCNT_inc_simple_NN(method_POST);
253 1 50         if (memcmp(r->method, "HEAD", 4) == 0)
254 1           return SvREFCNT_inc_simple_NN(method_HEAD);
255 0           break;
256 1           case 5:
257 1 50         if (memcmp(r->method, "PATCH", 5) == 0)
258 1           return SvREFCNT_inc_simple_NN(method_PATCH);
259 0           break;
260 1           case 6:
261 1 50         if (memcmp(r->method, "DELETE", 6) == 0)
262 1           return SvREFCNT_inc_simple_NN(method_DELETE);
263 0           break;
264 1           case 7:
265 1 50         if (memcmp(r->method, "OPTIONS", 7) == 0)
266 1           return SvREFCNT_inc_simple_NN(method_OPTIONS);
267 0           break;
268             }
269             // Uncommon method - create new SV
270 0           return newSVpvn(r->method, r->method_len);
271             }
272              
273             INLINE_UNLESS_DEBUG static SV*
274 527           feersum_env_uri(pTHX_ struct feer_req *r)
275             {
276 527           return newSVpvn(r->uri, r->uri_len);
277             }
278              
279             INLINE_UNLESS_DEBUG static SV*
280 524           feersum_env_protocol(pTHX_ struct feer_req *r)
281             {
282 524 100         return (r->minor_version == 1) ? psgi_serv11 : psgi_serv10;
283             }
284              
285             INLINE_UNLESS_DEBUG static void
286 284           feersum_set_path_and_query(pTHX_ struct feer_req *r)
287             {
288 284           const char *qpos = (const char *)memchr(r->uri, '?', r->uri_len);
289 284 100         if (qpos != NULL) {
290 19           r->path = newSVpvn(r->uri, (qpos - r->uri));
291 19           qpos++;
292 19           r->query = newSVpvn(qpos, r->uri_len - (qpos - r->uri));
293             } else {
294 265           r->path = feersum_env_uri(aTHX_ r);
295 265           r->query = SvREFCNT_inc_simple_NN(empty_query_sv);
296             }
297 284           uri_decode_sv(r->path);
298 284           }
299              
300             INLINE_UNLESS_DEBUG static SV*
301 50           feersum_env_path(pTHX_ struct feer_req *r)
302             {
303 50 100         if (unlikely(!r->path)) feersum_set_path_and_query(aTHX_ r);
304 50           return r->path;
305             }
306              
307             INLINE_UNLESS_DEBUG static SV*
308 28           feersum_env_query(pTHX_ struct feer_req *r)
309             {
310 28 50         if (unlikely(!r->query)) feersum_set_path_and_query(aTHX_ r);
311 28           return r->query;
312             }
313              
314             INLINE_UNLESS_DEBUG static SV*
315 38           feersum_env_addr(pTHX_ struct feer_conn *c)
316             {
317 38           feersum_set_conn_remote_info(aTHX_ c);
318 38           return c->remote_addr;
319             }
320              
321             INLINE_UNLESS_DEBUG static SV*
322 6           feersum_env_port(pTHX_ struct feer_conn *c)
323             {
324 6           feersum_set_conn_remote_info(aTHX_ c);
325 6           return c->remote_port;
326             }
327              
328             // Find header value by name (case-insensitive), returns NULL if not found
329             static const char*
330 35           find_header_value(struct feer_req *r, const char *name, size_t name_len, size_t *value_len)
331             {
332             size_t i;
333 152 100         for (i = 0; i < r->num_headers; i++) {
334 139           struct phr_header *hdr = &r->headers[i];
335             // Skip NULL names (obs-fold continuation lines, rejected at parse but check anyway)
336 161 50         if (hdr->name && hdr->name_len == name_len &&
337 22           str_case_eq_fixed(name, hdr->name, name_len))
338             {
339 22           *value_len = hdr->value_len;
340 22           return hdr->value;
341             }
342             }
343 13           *value_len = 0;
344 13           return NULL;
345             }
346              
347             static int
348 3           feer_socketpair_nb(int sv[2])
349             {
350             #if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
351 3 50         if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, sv) == 0)
352 3           return 0;
353 0 0         if (errno != EINVAL) return -1;
354             /* fallthrough: kernel too old for SOCK_CLOEXEC/SOCK_NONBLOCK */
355             #endif
356 0 0         if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0)
357 0           return -1;
358 0           if (fcntl(sv[0], F_SETFL, O_NONBLOCK) < 0 ||
359 0 0         fcntl(sv[1], F_SETFL, O_NONBLOCK) < 0 ||
360 0 0         fcntl(sv[0], F_SETFD, FD_CLOEXEC) < 0 ||
361 0           fcntl(sv[1], F_SETFD, FD_CLOEXEC) < 0)
362             {
363 0           close(sv[0]);
364 0           close(sv[1]);
365 0           return -1;
366             }
367 0           return 0;
368             }
369              
370             static SV*
371 65           newSV_buf(STRLEN size)
372             {
373 65           SV *sv = newSV(size);
374 65           SvPOK_on(sv);
375 65           SvCUR_set(sv, 0);
376 65 50         if (size > 0)
377 65           *SvPVX(sv) = '\0';
378 65           return sv;
379             }
380              
381             static time_t last_generated_time = 0;
382              
383 284           static void date_timer_cb(EV_P_ ev_timer *w, int revents) {
384             PERL_UNUSED_VAR(EV_A);
385             PERL_UNUSED_VAR(revents);
386             PERL_UNUSED_VAR(w);
387              
388             time_t now;
389             #if defined(__linux__) && defined(CLOCK_REALTIME_COARSE)
390             struct timespec ts;
391 284 50         if (likely(clock_gettime(CLOCK_REALTIME_COARSE, &ts) == 0)) {
392 284           now = ts.tv_sec;
393             } else {
394 0           now = time(NULL);
395             }
396             #else
397             now = time(NULL);
398             #endif
399 284 100         if (now == last_generated_time) return;
400              
401 278           last_generated_time = now;
402             struct tm tm_buf;
403 278 50         if (unlikely(!gmtime_r(&now, &tm_buf))) return;
404              
405             /* RFC 7231 requires English day/month names; strftime is locale-dependent */
406             static const char *days[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
407             static const char *months[] = {"Jan","Feb","Mar","Apr","May","Jun",
408             "Jul","Aug","Sep","Oct","Nov","Dec"};
409 278           snprintf(DATE_BUF + 6, DATE_VALUE_LENGTH + 1,
410             "%s, %02d %s %04d %02d:%02d:%02d GMT",
411 278           days[tm_buf.tm_wday], tm_buf.tm_mday, months[tm_buf.tm_mon],
412 278           tm_buf.tm_year + 1900, tm_buf.tm_hour, tm_buf.tm_min, tm_buf.tm_sec);
413 278           DATE_BUF[DATE_HEADER_LENGTH-2] = '\r';
414 278           DATE_BUF[DATE_HEADER_LENGTH-1] = '\n';
415             }
416              
417             static int
418 405           format_content_length(char *buf, size_t len)
419             {
420             static const char prefix[] = "Content-Length: ";
421 405           memcpy(buf, prefix, 16);
422              
423 405           char *p = buf + 16;
424              
425 405 50         if (len == 0) {
426 0           *p++ = '0';
427             } else {
428             char tmp[20];
429 405           char *t = tmp;
430 971 100         while (len > 0) {
431 566           *t++ = '0' + (len % 10);
432 566           len /= 10;
433             }
434 971 100         while (t > tmp) {
435 566           *p++ = *--t;
436             }
437             }
438              
439 405           *p++ = '\r'; *p++ = '\n'; *p++ = '\r'; *p++ = '\n';
440 405           return p - buf;
441             }
442              
443             #ifdef FEERSUM_HAS_USDT
444             static void
445             feer_usdt_trace(int level, const char *fmt, ...)
446             {
447             if (FEERSUM_TRACE_ENABLED()) {
448             char buf[1024];
449             va_list ap;
450             va_start(ap, fmt);
451             vsnprintf(buf, sizeof(buf), fmt, ap);
452             va_end(ap);
453             FEERSUM_TRACE(level, buf);
454             }
455             }
456             #endif