File Coverage

feersum_utils.c.inc
Criterion Covered Total %
statement 132 162 81.4
branch 54 78 69.2
condition n/a
subroutine n/a
pod n/a
total 186 240 77.5


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 16           str_case_eq_both(const char *a, const char *b, size_t len)
132             {
133 116 100         for (size_t i = 0; i < len; i++)
134 104 100         if (ascii_lower[(unsigned char)a[i]] != ascii_lower[(unsigned char)b[i]]) return 0;
135 12           return 1;
136             }
137              
138             /*
139             * Fixed-length case-insensitive comparison.
140             * First argument must be a pre-lowered literal; only `b` is lowered at runtime.
141             */
142             INLINE_UNLESS_DEBUG bool
143 1876           str_case_eq_fixed(const char *a, const char *b, size_t len)
144             {
145 16155 100         for (size_t i = 0; i < len; i++)
146 14477 100         if (a[i] != ascii_lower[(unsigned char)b[i]]) return 0;
147 1678           return 1;
148             }
149              
150             static void
151 323           uri_decode_sv (SV *sv)
152             {
153             STRLEN len;
154             char *ptr, *end, *decoded;
155              
156 323           ptr = SvPV(sv, len);
157 323           end = SvEND(sv);
158              
159             // quickly scan for % so we can ignore decoding that portion of the string
160 18299 100         while (ptr < end) {
161 17979 100         if (unlikely(*ptr == '%')) goto needs_decode;
162 17976           ptr++;
163             }
164 320           return;
165              
166 3           needs_decode:
167              
168             // Up until ptr have been "decoded" already by virtue of those chars not
169             // being encoded.
170 3           decoded = ptr;
171              
172 31 100         for (; ptr < end; ptr++) {
173 28 100         if (unlikely(*ptr == '%') && likely(end - ptr > 2)) {
    100          
174 8           int c1 = hex_decode(ptr[1]);
175 8           int c2 = hex_decode(ptr[2]);
176 8 100         if (likely(c1 != -1 && c2 != -1)) {
    50          
177 7           *decoded++ = (c1 << 4) + c2;
178 7           ptr += 2;
179 7           continue;
180             }
181             }
182 21           *decoded++ = *ptr;
183             }
184              
185 3           *decoded = '\0'; // play nice with C
186              
187 3           SvCUR_set(sv, decoded - SvPVX(sv));
188             }
189              
190             // populate connection-level addr/port cache (called once per connection)
191             static void
192 341           feersum_set_conn_remote_info(pTHX_ struct feer_conn *c)
193             {
194 341 100         if (c->remote_addr) return; // already cached
195 275           struct sockaddr *sa = (struct sockaddr *)&c->sa;
196 275           switch (sa->sa_family) {
197 271           case AF_INET: {
198 271           struct sockaddr_in *in = (struct sockaddr_in *)sa;
199             char buf[INET_ADDRSTRLEN];
200 271 50         if (inet_ntop(AF_INET, &in->sin_addr, buf, sizeof(buf))) {
201 271           c->remote_addr = newSVpv(buf, 0);
202             } else {
203 0           c->remote_addr = newSVpvs("0.0.0.0");
204             }
205 271           c->remote_port = newSViv(ntohs(in->sin_port));
206 271           break;
207             }
208             #ifdef AF_INET6
209 4           case AF_INET6: {
210 4           struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)sa;
211             char buf[INET6_ADDRSTRLEN];
212 4 50         if (inet_ntop(AF_INET6, &in6->sin6_addr, buf, sizeof(buf))) {
213 4           c->remote_addr = newSVpv(buf, 0);
214             } else {
215 0           c->remote_addr = newSVpvs("::");
216             }
217 4           c->remote_port = newSViv(ntohs(in6->sin6_port));
218 4           break;
219             }
220             #endif
221             #ifdef AF_UNIX
222 0           case AF_UNIX:
223 0           c->remote_addr = newSVpvs("unix");
224 0           c->remote_port = newSViv(0);
225 0           break;
226             #endif
227 0           default:
228 0           c->remote_addr = newSVpvs("unspec");
229 0           c->remote_port = newSViv(0);
230 0           break;
231             }
232             }
233              
234             static SV*
235 325           feersum_env_method(pTHX_ struct feer_req *r)
236             {
237             // Return cached SV for common HTTP methods (avoids newSVpvn per request)
238             // Use length-first dispatch with memcmp for consistency
239 325           switch (r->method_len) {
240 217           case 3:
241 217 50         if (likely(memcmp(r->method, "GET", 3) == 0))
242 217           return SvREFCNT_inc_simple_NN(method_GET);
243 0 0         if (memcmp(r->method, "PUT", 3) == 0)
244 0           return SvREFCNT_inc_simple_NN(method_PUT);
245 0           break;
246 105           case 4:
247 105 100         if (likely(memcmp(r->method, "POST", 4) == 0))
248 104           return SvREFCNT_inc_simple_NN(method_POST);
249 1 50         if (memcmp(r->method, "HEAD", 4) == 0)
250 1           return SvREFCNT_inc_simple_NN(method_HEAD);
251 0           break;
252 1           case 5:
253 1 50         if (memcmp(r->method, "PATCH", 5) == 0)
254 1           return SvREFCNT_inc_simple_NN(method_PATCH);
255 0           break;
256 1           case 6:
257 1 50         if (memcmp(r->method, "DELETE", 6) == 0)
258 1           return SvREFCNT_inc_simple_NN(method_DELETE);
259 0           break;
260 1           case 7:
261 1 50         if (memcmp(r->method, "OPTIONS", 7) == 0)
262 1           return SvREFCNT_inc_simple_NN(method_OPTIONS);
263 0           break;
264             }
265             // Uncommon method - create new SV
266 0           return newSVpvn(r->method, r->method_len);
267             }
268              
269             INLINE_UNLESS_DEBUG static SV*
270 603           feersum_env_uri(pTHX_ struct feer_req *r)
271             {
272 603           return newSVpvn(r->uri, r->uri_len);
273             }
274              
275             INLINE_UNLESS_DEBUG static SV*
276 598           feersum_env_protocol(pTHX_ struct feer_req *r)
277             {
278 598 100         return (r->minor_version == 1) ? psgi_serv11 : psgi_serv10;
279             }
280              
281             INLINE_UNLESS_DEBUG static void
282 323           feersum_set_path_and_query(pTHX_ struct feer_req *r)
283             {
284 323           const char *qpos = (const char *)memchr(r->uri, '?', r->uri_len);
285 323 100         if (qpos != NULL) {
286 19           r->path = newSVpvn(r->uri, (qpos - r->uri));
287 19           qpos++;
288 19           r->query = newSVpvn(qpos, r->uri_len - (qpos - r->uri));
289             } else {
290 304           r->path = feersum_env_uri(aTHX_ r);
291 304           r->query = SvREFCNT_inc_simple_NN(empty_query_sv);
292             }
293 323           uri_decode_sv(r->path);
294 323           }
295              
296             INLINE_UNLESS_DEBUG static SV*
297 54           feersum_env_path(pTHX_ struct feer_req *r)
298             {
299 54 100         if (unlikely(!r->path)) feersum_set_path_and_query(aTHX_ r);
300 54           return r->path;
301             }
302              
303             INLINE_UNLESS_DEBUG static SV*
304 28           feersum_env_query(pTHX_ struct feer_req *r)
305             {
306 28 50         if (unlikely(!r->query)) feersum_set_path_and_query(aTHX_ r);
307 28           return r->query;
308             }
309              
310             INLINE_UNLESS_DEBUG static SV*
311 38           feersum_env_addr(pTHX_ struct feer_conn *c)
312             {
313 38           feersum_set_conn_remote_info(aTHX_ c);
314 38           return c->remote_addr;
315             }
316              
317             INLINE_UNLESS_DEBUG static SV*
318 6           feersum_env_port(pTHX_ struct feer_conn *c)
319             {
320 6           feersum_set_conn_remote_info(aTHX_ c);
321 6           return c->remote_port;
322             }
323              
324             // Find header value by name (case-insensitive), returns NULL if not found
325             static const char*
326 35           find_header_value(struct feer_req *r, const char *name, size_t name_len, size_t *value_len)
327             {
328 152 100         for (size_t i = 0; i < r->num_headers; i++) {
329 139           struct phr_header *hdr = &r->headers[i];
330             // Skip NULL names (obs-fold continuation lines, rejected at parse but check anyway)
331 161 50         if (hdr->name && hdr->name_len == name_len &&
332 22           str_case_eq_fixed(name, hdr->name, name_len))
333             {
334 22           *value_len = hdr->value_len;
335 22           return hdr->value;
336             }
337             }
338 13           *value_len = 0;
339 13           return NULL;
340             }
341              
342             static int
343 9           feer_socketpair_nb(int sv[2])
344             {
345             #if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
346 9 50         if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, sv) == 0)
347 9           return 0;
348 0 0         if (errno != EINVAL) return -1;
349             /* fallthrough: kernel too old for SOCK_CLOEXEC/SOCK_NONBLOCK */
350             #endif
351 0 0         if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0)
352 0           return -1;
353 0           if (fcntl(sv[0], F_SETFL, O_NONBLOCK) < 0 ||
354 0 0         fcntl(sv[1], F_SETFL, O_NONBLOCK) < 0 ||
355 0 0         fcntl(sv[0], F_SETFD, FD_CLOEXEC) < 0 ||
356 0           fcntl(sv[1], F_SETFD, FD_CLOEXEC) < 0)
357             {
358 0           close(sv[0]);
359 0           close(sv[1]);
360 0           return -1;
361             }
362 0           return 0;
363             }
364              
365             static SV*
366 74           newSV_buf(STRLEN size)
367             {
368 74           SV *sv = newSV(size);
369 74           SvPOK_on(sv);
370 74           SvCUR_set(sv, 0);
371 74 50         if (size > 0)
372 74           *SvPVX(sv) = '\0';
373 74           return sv;
374             }
375              
376             static time_t last_generated_time = 0;
377              
378 365           static void date_timer_cb(EV_P_ ev_timer *w, int revents) {
379             PERL_UNUSED_VAR(EV_A);
380             PERL_UNUSED_VAR(revents);
381             PERL_UNUSED_VAR(w);
382              
383             time_t now;
384             #if defined(__linux__) && defined(CLOCK_REALTIME_COARSE)
385             struct timespec ts;
386 365 50         if (likely(clock_gettime(CLOCK_REALTIME_COARSE, &ts) == 0)) {
387 365           now = ts.tv_sec;
388             } else {
389 0           now = time(NULL);
390             }
391             #else
392             now = time(NULL);
393             #endif
394 365 100         if (now == last_generated_time) return;
395              
396 357           last_generated_time = now;
397             struct tm tm_buf;
398 357 50         if (unlikely(!gmtime_r(&now, &tm_buf))) return;
399              
400             /* RFC 7231 requires English day/month names; strftime is locale-dependent */
401             static const char *days[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
402             static const char *months[] = {"Jan","Feb","Mar","Apr","May","Jun",
403             "Jul","Aug","Sep","Oct","Nov","Dec"};
404 357           snprintf(DATE_BUF + 6, DATE_VALUE_LENGTH + 1,
405             "%s, %02d %s %04d %02d:%02d:%02d GMT",
406 357           days[tm_buf.tm_wday], tm_buf.tm_mday, months[tm_buf.tm_mon],
407 357           tm_buf.tm_year + 1900, tm_buf.tm_hour, tm_buf.tm_min, tm_buf.tm_sec);
408 357           DATE_BUF[DATE_HEADER_LENGTH-2] = '\r';
409 357           DATE_BUF[DATE_HEADER_LENGTH-1] = '\n';
410             }
411              
412             static int
413 449           format_content_length(char *buf, size_t len)
414             {
415             static const char prefix[] = "Content-Length: ";
416 449           memcpy(buf, prefix, 16);
417              
418 449           char *p = buf + 16;
419              
420 449 100         if (len == 0) {
421 1           *p++ = '0';
422             } else {
423             char tmp[20];
424 448           char *t = tmp;
425 1075 100         while (len > 0) {
426 627           *t++ = '0' + (len % 10);
427 627           len /= 10;
428             }
429 1075 100         while (t > tmp) {
430 627           *p++ = *--t;
431             }
432             }
433              
434 449           *p++ = '\r'; *p++ = '\n'; *p++ = '\r'; *p++ = '\n';
435 449           return p - buf;
436             }
437              
438             #ifdef FEERSUM_HAS_USDT
439             static void
440             feer_usdt_trace(int level, const char *fmt, ...)
441             {
442             if (FEERSUM_TRACE_ENABLED()) {
443             char buf[1024];
444             va_list ap;
445             va_start(ap, fmt);
446             vsnprintf(buf, sizeof(buf), fmt, ap);
447             va_end(ap);
448             FEERSUM_TRACE(level, buf);
449             }
450             }
451             #endif