File Coverage

lib/Plack/Handler/Gazelle.xs
Criterion Covered Total %
statement 365 570 64.0
branch 132 292 45.2
condition n/a
subroutine n/a
pod n/a
total 497 862 57.6


line stmt bran cond sub pod time code
1             #ifdef __cplusplus
2             extern "C" {
3             #endif
4              
5             #define PERL_NO_GET_CONTEXT /* we want efficiency */
6             #include
7             #include
8             #include
9             #include
10             #include
11              
12             #ifdef __cplusplus
13             } /* extern "C" */
14             #endif
15              
16             #define NEED_newSVpvn_flags
17              
18             #include "ppport.h"
19             #ifndef __need_IOV_MAX
20             #define __need_IOV_MAX
21             #endif
22              
23             #include
24             #include
25             #include
26              
27             #include
28             #define _GNU_SOURCE /* See feature_test_macros(7) */
29             #include
30             #include
31             #include
32             #include "picohttpparser/picohttpparser.c"
33              
34             #ifndef STATIC_INLINE /* a public perl API from 5.13.4 */
35             # if defined(__GNUC__) || defined(__cplusplus) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L))
36             # define STATIC_INLINE static inline
37             # else
38             # define STATIC_INLINE static
39             # endif
40             #endif /* STATIC_INLINE */
41              
42             #ifndef IOV_MAX
43             #if defined(__FreeBSD__) || defined(__APPLE__)
44             # define IOV_MAX 128
45             #endif
46             #endif
47              
48             #ifndef IOV_MAX
49             # error "Unable to determine IOV_MAX from system headers"
50             #endif
51              
52              
53             #define MAX_HEADER_SIZE 16384
54             #define MAX_HEADER_NAME_LEN 1024
55             #define MAX_HEADERS 128
56             #if defined(__OpenBSD__)
57             #define READ_BUFSZ 16383
58             #else
59             #define READ_BUFSZ 16384
60             #endif
61             #define BAD_REQUEST "HTTP/1.0 400 Bad Request\r\nConnection: close\r\n\r\n400 Bad Request\r\n"
62             #define EXPECT_CONTINUE "HTTP/1.1 100 Continue\r\n\r\n"
63             #define EXPECT_FAILED "HTTP/1.1 417 Expectation Failed\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nExpectation Failed\r\n"
64             #define TOU(ch) (('a' <= ch && ch <= 'z') ? ch - ('a' - 'A') : ch)
65              
66             static const char *DoW[] = {
67             "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
68             };
69             static const char *MoY[] = {
70             "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
71             };
72             static const char xdigit[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
73              
74             static HV *env_template;
75              
76             /* stolen from HTTP::Status and Feersum */
77             /* Unmarked codes are from RFC 2616 */
78             /* See also: http://en.wikipedia.org/wiki/List_of_HTTP_status_codes */
79             static const char *
80 38           status_message (int code) {
81 38           switch (code) {
82             case 100: return "Continue";
83 0           case 101: return "Switching Protocols";
84 0           case 102: return "Processing"; /* RFC 2518 (WebDAV) */
85 37           case 200: return "OK";
86 0           case 201: return "Created";
87 0           case 202: return "Accepted";
88 0           case 203: return "Non-Authoritative Information";
89 0           case 204: return "No Content";
90 0           case 205: return "Reset Content";
91 0           case 206: return "Partial Content";
92 0           case 207: return "Multi-Status"; /* RFC 2518 (WebDAV) */
93 0           case 208: return "Already Reported"; /* RFC 5842 */
94 0           case 300: return "Multiple Choices";
95 0           case 301: return "Moved Permanently";
96 0           case 302: return "Found";
97 0           case 303: return "See Other";
98 0           case 304: return "Not Modified";
99 0           case 305: return "Use Proxy";
100 0           case 307: return "Temporary Redirect";
101 0           case 400: return "Bad Request";
102 0           case 401: return "Unauthorized";
103 0           case 402: return "Payment Required";
104 0           case 403: return "Forbidden";
105 0           case 404: return "Not Found";
106 0           case 405: return "Method Not Allowed";
107 0           case 406: return "Not Acceptable";
108 0           case 407: return "Proxy Authentication Required";
109 0           case 408: return "Request Timeout";
110 0           case 409: return "Conflict";
111 0           case 410: return "Gone";
112 0           case 411: return "Length Required";
113 0           case 412: return "Precondition Failed";
114 0           case 413: return "Request Entity Too Large";
115 0           case 414: return "Request-URI Too Large";
116 0           case 415: return "Unsupported Media Type";
117 0           case 416: return "Request Range Not Satisfiable";
118 0           case 417: return "Expectation Failed";
119 0           case 418: return "I'm a teapot"; /* RFC 2324 */
120 0           case 422: return "Unprocessable Entity"; /* RFC 2518 (WebDAV) */
121 0           case 423: return "Locked"; /* RFC 2518 (WebDAV) */
122 0           case 424: return "Failed Dependency"; /* RFC 2518 (WebDAV) */
123 0           case 425: return "No code"; /* WebDAV Advanced Collections */
124 0           case 426: return "Upgrade Required"; /* RFC 2817 */
125 0           case 428: return "Precondition Required";
126 0           case 429: return "Too Many Requests";
127 0           case 431: return "Request Header Fields Too Large";
128 0           case 449: return "Retry with"; /* unofficial Microsoft */
129 0           case 500: return "Internal Server Error";
130 0           case 501: return "Not Implemented";
131 0           case 502: return "Bad Gateway";
132 0           case 503: return "Service Unavailable";
133 0           case 504: return "Gateway Timeout";
134 0           case 505: return "HTTP Version Not Supported";
135 0           case 506: return "Variant Also Negotiates"; /* RFC 2295 */
136 0           case 507: return "Insufficient Storage"; /* RFC 2518 (WebDAV) */
137 0           case 509: return "Bandwidth Limit Exceeded"; /* unofficial */
138 0           case 510: return "Not Extended"; /* RFC 2774 */
139 0           case 511: return "Network Authentication Required";
140             default: break;
141             }
142             /* default to the Nxx group names in RFC 2616 */
143 0 0         if (100 <= code && code <= 199) {
144             return "Informational";
145             }
146 0 0         else if (200 <= code && code <= 299) {
147             return "Success";
148             }
149 0 0         else if (300 <= code && code <= 399) {
150             return "Redirection";
151             }
152 0 0         else if (400 <= code && code <= 499) {
153             return "Client Error";
154             }
155             else {
156 0           return "Error";
157             }
158             }
159              
160             /* stolen from HTTP::Parser::XS */
161             static
162             size_t find_ch(const char* s, size_t len, char ch)
163             {
164             size_t i;
165 240 100         for (i = 0; i != len; ++i, ++s)
    100          
166 162 100         if (*s == ch)
    100          
167             break;
168             return i;
169             }
170              
171             static
172 198           int header_is(const struct phr_header* header, const char* name,
173             size_t len)
174             {
175             const char* x, * y;
176 198 100         if (header->name_len != len)
177             return 0;
178 28 100         for (x = header->name, y = name; len != 0; --len, ++x, ++y)
179 26 100         if (TOU(*x) != *y)
    50          
180             return 0;
181             return 1;
182             }
183              
184              
185              
186             STATIC_INLINE
187 41           int store_path_info(pTHX_ HV* env, const char* src, size_t src_len) {
188             size_t dlen = 0, i = 0;
189             char *d;
190             char s2, s3;
191             SV * dst;
192              
193 41           dst = newSV(0);
194 41 50         (void)SvUPGRADE(dst, SVt_PV);
195 41 50         d = SvGROW(dst, src_len * 3 + 1);
    50          
196              
197 105 100         for (i = 0; i < src_len; i++ ) {
198 66 100         if ( src[i] == '%' ) {
199 4 50         if ( !isxdigit(src[i+1]) || !isxdigit(src[i+2]) ) {
    100          
200             return -1;
201             }
202             s2 = src[i+1];
203             s3 = src[i+2];
204 2 50         s2 -= s2 <= '9' ? '0'
    0          
205             : s2 <= 'F' ? 'A' - 10
206             : 'a' - 10;
207 2 50         s3 -= s3 <= '9' ? '0'
    0          
208             : s3 <= 'F' ? 'A' - 10
209             : 'a' - 10;
210 2           d[dlen++] = s2 * 16 + s3;
211             i += 2;
212             }
213             else {
214 62           d[dlen++] = src[i];
215             }
216             }
217 39           SvCUR_set(dst, dlen);
218 39           *SvEND(dst) = '\0';
219 39           SvPOK_only(dst);
220 39           (void)hv_stores(env, "PATH_INFO", dst);
221 39           return 1;
222             }
223              
224              
225             STATIC_INLINE
226             int
227 42           _parse_http_request(pTHX_ char *buf, ssize_t buf_len, HV *env) {
228             const char* method;
229             size_t method_len;
230             const char* path;
231             size_t path_len;
232             int minor_version;
233             struct phr_header headers[MAX_HEADERS];
234 42           size_t num_headers = MAX_HEADERS;
235             size_t question_at;
236             size_t i;
237             int ret;
238             SV* last_value;
239             char tmp[MAX_HEADER_NAME_LEN + sizeof("HTTP_") - 1];
240              
241             int seen_content_length = 0;
242             int seen_transfer_encoding = 0;
243              
244 42           ret = phr_parse_request(
245             buf, buf_len,
246             &method, &method_len,
247             &path, &path_len,
248             &minor_version, headers, &num_headers, 0
249             );
250              
251 42 100         if (ret < 0)
252 1           goto done;
253 41 50         if (minor_version > 1 || minor_version < 0 ) {
254             ret = -1;
255 0           goto done;
256             }
257              
258 41           (void)hv_stores(env, "REQUEST_METHOD", newSVpvn(method, method_len));
259 41           (void)hv_stores(env, "REQUEST_URI", newSVpvn(path, path_len));
260 41           (void)hv_stores(env, "SCRIPT_NAME", newSVpvn("", 0));
261             strcpy(tmp, "HTTP/1.");
262 41           tmp[sizeof("HTTP/1.")-1] = '0' + minor_version;
263 41           (void)hv_stores(env, "SERVER_PROTOCOL", newSVpvn(tmp, sizeof("HTTP/1.1")-1));
264              
265             /* PATH_INFO QUERY_STRING */
266 82           path_len = find_ch(path, path_len, '#'); /* strip off all text after # after storing request_uri */
267             question_at = find_ch(path, path_len, '?');
268 41 100         if ( store_path_info(aTHX_ env, path, question_at) < 0 ) {
269 2           hv_clear(env);
270             ret = -1;
271 2           goto done;
272             }
273 39 100         if (question_at != path_len) ++question_at;
274 39           (void)hv_stores(env, "QUERY_STRING", newSVpvn(path + question_at, path_len - question_at));
275              
276             last_value = NULL;
277 107 100         for (i = 0; i < num_headers; ++i) {
278 69 100         if (headers[i].name != NULL) {
279             const char* name;
280             size_t name_len;
281             SV** slot;
282 67 100         if (header_is(headers + i, "CONTENT-TYPE", sizeof("CONTENT-TYPE") - 1)) {
283             name = "CONTENT_TYPE";
284             name_len = sizeof("CONTENT_TYPE") - 1;
285 66 100         } else if (header_is(headers + i, "CONTENT-LENGTH", sizeof("CONTENT-LENGTH") - 1)) {
286 1 50         if (seen_content_length) {
287             /* duplicated Content_Length headers is
288             * a potential request-smuggling attack. Reject */
289             ret = -1;
290 0           goto done;
291             }
292             seen_content_length = 1;
293 1 50         if (seen_transfer_encoding) {
294             /* RFC 7230 §3.3.3: Transfer-Encoding overrides Content-Length;
295             * receiving both is a potential request-smuggling attack. Reject.
296             * env will be freed via sv_2mortal in the badexit_clear path. */
297             ret = -1;
298 0           goto done;
299             }
300             name = "CONTENT_LENGTH";
301             name_len = sizeof("CONTENT_LENGTH") - 1;
302 65 50         } else if (header_is(headers + i, "TRANSFER-ENCODING", sizeof("TRANSFER-ENCODING") - 1)) {
303 0 0         if (seen_transfer_encoding ) {
304             /* duplicated transfer-encoding headers is
305             * a potential request-smuggling attack. Reject */
306             ret = -1;
307 0           goto done;
308             }
309             seen_transfer_encoding = 1;
310 0 0         if (seen_content_length) {
311             /* RFC 7230 §3.3.3: Transfer-Encoding overrides Content-Length;
312             * receiving both is a potential request-smuggling attack. Reject.
313             * env will be freed via sv_2mortal in the badexit_clear path. */
314             ret = -1;
315 0           goto done;
316             }
317             name = tmp;
318             strcpy(tmp, "HTTP_TRANSFER_ENCODING");
319             name_len = sizeof("HTTP_TRANSFER_ENCODING") - 1;
320             } else {
321             const char* s;
322             char* d;
323             size_t n;
324 65 100         if (sizeof(tmp) - 5 < headers[i].name_len) {
325 1           hv_clear(env);
326             ret = -1;
327 1           goto done;
328             }
329             strcpy(tmp, "HTTP_");
330 64           for (s = headers[i].name, n = headers[i].name_len, d = tmp + 5;
331 1517 100         n != 0;
332 1453           s++, --n, d++) {
333 1453 100         *d = *s == '-' ? '_' : TOU(*s);
    100          
334             name = tmp;
335 1453           name_len = headers[i].name_len + 5;
336             }
337             }
338 66           slot = hv_fetch(env, name, name_len, 1);
339 66 50         if ( !slot ) croak("ERROR: failed to create hash entry");
340 66 100         if (SvOK(*slot)) {
341 2           sv_catpvn(*slot, ", ", 2);
342 2           sv_catpvn(*slot, headers[i].value, headers[i].value_len);
343             } else {
344 64           sv_setpvn(*slot, headers[i].value, headers[i].value_len);
345 64           last_value = *slot;
346             }
347             } else {
348             /* continuing lines of a mulitiline header */
349 2           sv_catpvn(last_value, headers[i].value, headers[i].value_len);
350             }
351             }
352 38           done:
353 42           return ret;
354             }
355              
356              
357             STATIC_INLINE
358             char *
359 115           svpv2char(pTHX_ SV *sv, STRLEN *lp)
360             {
361 115 50         if (SvGAMAGIC(sv))
    50          
    0          
    0          
362 0           sv = sv_2mortal(newSVsv(sv));
363 115           return SvPV(sv, *lp);
364             }
365              
366              
367             STATIC_INLINE
368             int
369             _accept(int fileno, struct sockaddr *addr, unsigned int addrlen) {
370             int fd;
371             #ifdef HAVE_ACCEPT4
372 13526           fd = accept4(fileno, addr, &addrlen, SOCK_CLOEXEC|SOCK_NONBLOCK);
373             #else
374             fd = accept(fileno, addr, &addrlen);
375             #endif
376             if (fd < 0) {
377             return fd;
378             }
379             #ifndef HAVE_ACCEPT4
380             fcntl(fd, F_SETFD, FD_CLOEXEC);
381             fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
382             #endif
383             return fd;
384             }
385              
386              
387             STATIC_INLINE
388             ssize_t
389 38           _writev_timeout(const int fileno, const double timeout, struct iovec *iovec, const int iovcnt, const int do_select ) {
390             int rv;
391             int nfound;
392             struct pollfd wfds[1];
393 38 50         if ( do_select == 1) goto WAIT_WRITE;
394 38           DO_WRITE:
395 38           rv = writev(fileno, iovec, iovcnt);
396 38 50         if ( rv >= 0 ) {
397 38           return rv;
398             }
399 0 0         if ( rv < 0 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK ) {
    0          
400 0           return rv;
401             }
402 0           WAIT_WRITE:
403             while (1) {
404 0           wfds[0].fd = fileno;
405 0           wfds[0].events = POLLOUT;
406 0           nfound = poll(wfds, 1, (int)timeout*1000);
407 0 0         if ( nfound == 1 ) {
408             break;
409             }
410 0 0         if ( nfound == 0 && errno != EINTR ) {
    0          
411             return -1;
412             }
413             }
414 0           goto DO_WRITE;
415             }
416              
417             STATIC_INLINE
418             ssize_t
419 46           _read_timeout(const int fileno, const double timeout, char * read_buf, const int read_len ) {
420             int rv;
421             int nfound;
422             struct pollfd rfds[1];
423 46           DO_READ:
424 46           rfds[0].fd = fileno;
425 46           rfds[0].events = POLLIN;
426 46 50         rv = read(fileno, read_buf, read_len);
427 46 50         if ( rv >= 0 ) {
428 46           return rv;
429             }
430 0 0         if ( rv < 0 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK ) {
    0          
431 0           return rv;
432             }
433 0           WAIT_READ:
434             while (1) {
435 0           nfound = poll(rfds, 1, (int)timeout*1000);
436 0 0         if ( nfound == 1 ) {
437             break;
438             }
439 0 0         if ( nfound == 0 && errno != EINTR ) {
    0          
440             return -1;
441             }
442             }
443 0           goto DO_READ;
444             }
445              
446             STATIC_INLINE
447             ssize_t
448 0           _write_timeout(const int fileno, const double timeout, char * write_buf, const int write_len ) {
449             int rv;
450             int nfound;
451             struct pollfd wfds[1];
452 0           DO_WRITE:
453 0           rv = write(fileno, write_buf, write_len);
454 0 0         if ( rv >= 0 ) {
455 0           return rv;
456             }
457 0 0         if ( rv < 0 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK ) {
    0          
458 0           return rv;
459             }
460 0           WAIT_WRITE:
461             while (1) {
462 0           wfds[0].fd = fileno;
463 0           wfds[0].events = POLLOUT;
464 0           nfound = poll(wfds, 1, (int)timeout*1000);
465 0 0         if ( nfound == 1 ) {
466             break;
467             }
468 0 0         if ( nfound == 0 && errno != EINTR ) {
    0          
469             return -1;
470             }
471             }
472 0           goto DO_WRITE;
473             }
474              
475             STATIC_INLINE
476             void
477             str_s(char * dst, size_t *dst_len, const char * src, int src_len) {
478             int i;
479 75           int dlen = *dst_len;
480 416 100         for ( i=0; i
    100          
    100          
    100          
481 304           dst[dlen++] = src[i];
482             }
483 112           *dst_len = dlen;
484             }
485              
486              
487             STATIC_INLINE
488             void
489 223           str_i(char * dst, size_t * dst_len, int src, int fig) {
490 223           int dlen = *dst_len + fig - 1;
491             do {
492 558           dst[dlen] = '0' + (src % 10);
493 558           dlen--;
494 558           src /= 10;
495 558 100         } while( dlen >= *dst_len );
496 223           *dst_len += fig;
497 223           }
498              
499              
500             STATIC_INLINE
501 37           int _date_line(char * date_line) {
502             struct tm gtm;
503             time_t lt;
504             size_t i = 0;
505 37           time(<);
506 37           gmtime_r(<, >m);
507 37           date_line[i++] = 'D';
508 37           date_line[i++] = 'a';
509 37           date_line[i++] = 't';
510 37           date_line[i++] = 'e';
511 37           date_line[i++] = ':';
512 37           date_line[i++] = ' ';
513 37           str_s(date_line, &i, DoW[gtm.tm_wday], 3);
514 37           date_line[i++] = ',';
515 37           date_line[i++] = ' ';
516 37           str_i(date_line, &i, gtm.tm_mday, 2);
517 37           date_line[i++] = ' ';
518 37           str_s(date_line, &i, MoY[gtm.tm_mon], 3);
519 37           date_line[i++] = ' ';
520 37           str_i(date_line, &i, gtm.tm_year + 1900, 4);
521 37           date_line[i++] = ' ';
522 37           str_i(date_line, &i, gtm.tm_hour,2);
523 37           date_line[i++] = ':';
524 37           str_i(date_line, &i, gtm.tm_min,2);
525 37           date_line[i++] = ':';
526 37           str_i(date_line, &i, gtm.tm_sec,2);
527 37           date_line[i++] = ' ';
528 37           date_line[i++] = 'G';
529 37           date_line[i++] = 'M';
530 37           date_line[i++] = 'T';
531 37           date_line[i++] = 13;
532 37           date_line[i++] = 10;
533 37           return i;
534             }
535              
536             STATIC_INLINE
537 29           int _chunked_header(char *buf, ssize_t len) {
538             int dlen = 0, i;
539             ssize_t l = len;
540 58 100         while ( l > 0 ) {
541 29           dlen++;
542 29           l /= 16;
543             }
544             i = dlen;
545 29           buf[i++] = 13;
546 29           buf[i++] = 10;
547 29           buf[i+1] = 0;
548 58 100         while ( len > 0 ) {
549 29           buf[--dlen] = xdigit[len % 16];
550 29           len /= 16;
551             }
552 29           return i;
553             }
554              
555             MODULE = Plack::Handler::Gazelle PACKAGE = Plack::Handler::Gazelle
556              
557             PROTOTYPES: DISABLE
558              
559             BOOT:
560             {
561             AV * psgi_version;
562 68           psgi_version = newAV();
563 68           av_extend(psgi_version, 2);
564 68           (void)av_push(psgi_version,newSViv(1));
565 68           (void)av_push(psgi_version,newSViv(1));
566 68           SvREADONLY_on((SV*)psgi_version);
567              
568             HV *e;
569 68           e = newHV();
570 68           (void)hv_stores(e,"SCRIPT_NAME", newSVpvs(""));
571 68           (void)hv_stores(e,"psgi.version", newRV((SV*)psgi_version));
572 68           (void)hv_stores(e,"psgi.errors", newRV((SV*)PL_stderrgv));
573 68           (void)hv_stores(e,"psgi.url_scheme", newSVpvs("http"));
574 68           (void)hv_stores(e,"psgi.run_once", newSV(0));
575 68           (void)hv_stores(e,"psgi.multithread", newSV(0));
576 68           (void)hv_stores(e,"psgi.multiprocess", newSViv(1));
577 68           (void)hv_stores(e,"psgi.streaming", newSViv(1));
578 68           (void)hv_stores(e,"psgi.nonblocking", newSV(0));
579 68           (void)hv_stores(e,"psgix.input.buffered", newSViv(1));
580 68           (void)hv_stores(e,"psgix.harakiri", newSViv(1));
581              
582             /* stolenn from Feersum */
583             /* placeholders that get defined for every request */
584 68           (void)hv_stores(e, "SERVER_PROTOCOL", &PL_sv_undef);
585 68           (void)hv_stores(e, "SERVER_NAME", &PL_sv_undef);
586 68           (void)hv_stores(e, "SERVER_PORT", &PL_sv_undef);
587 68           (void)hv_stores(e, "REQUEST_URI", &PL_sv_undef);
588 68           (void)hv_stores(e, "REQUEST_METHOD", &PL_sv_undef);
589 68           (void)hv_stores(e, "PATH_INFO", &PL_sv_undef);
590 68           (void)hv_stores(e, "REMOTE_ADDR", &PL_sv_placeholder);
591 68           (void)hv_stores(e, "REMOTE_PORT", &PL_sv_placeholder);
592              
593             /* defaults that get changed for some requests */
594 68           (void)hv_stores(e, "psgi.input", &PL_sv_placeholder);
595 68           (void)hv_stores(e, "CONTENT_LENGTH", &PL_sv_placeholder);
596 68           (void)hv_stores(e, "QUERY_STRING", &PL_sv_placeholder);
597              
598             /* anticipated headers */
599 68           (void)hv_stores(e, "CONTENT_TYPE", &PL_sv_placeholder);
600 68           (void)hv_stores(e, "HTTP_HOST", &PL_sv_placeholder);
601 68           (void)hv_stores(e, "HTTP_USER_AGENT", &PL_sv_placeholder);
602 68           (void)hv_stores(e, "HTTP_ACCEPT", &PL_sv_placeholder);
603 68           (void)hv_stores(e, "HTTP_ACCEPT_LANGUAGE", &PL_sv_placeholder);
604 68           (void)hv_stores(e, "HTTP_ACCEPT_CHARSET", &PL_sv_placeholder);
605 68           (void)hv_stores(e, "HTTP_REFERER", &PL_sv_placeholder);
606 68           (void)hv_stores(e, "HTTP_COOKIE", &PL_sv_placeholder);
607 68           (void)hv_stores(e, "HTTP_IF_MODIFIED_SINCE", &PL_sv_placeholder);
608 68           (void)hv_stores(e, "HTTP_IF_NONE_MATCH", &PL_sv_placeholder);
609 68           (void)hv_stores(e, "HTTP_IF_MODIFIED_SINCE", &PL_sv_placeholder);
610 68           (void)hv_stores(e, "HTTP_IF_NONE_MATCH", &PL_sv_placeholder);
611 68           (void)hv_stores(e, "HTTP_CACHE_CONTROL", &PL_sv_placeholder);
612 68           (void)hv_stores(e, "HTTP_X_FORWARDED_FOR", &PL_sv_placeholder);
613              
614 68           env_template = e;
615             }
616              
617             SV *
618             accept_psgi(fileno, timeout, tcp, host, port)
619             int fileno
620             double timeout
621             int tcp
622             SV * host
623             SV * port
624             PREINIT:
625             int fd;
626             struct sockaddr_in cliaddr;
627             unsigned int len;
628             char read_buf[MAX_HEADER_SIZE];
629             HV * env;
630 13526           int flag = 1;
631             ssize_t rv = 0;
632             ssize_t buf_len;
633             ssize_t reqlen;
634             PPCODE:
635             {
636             /* if ( my ($conn, $buf, $env) = accept_buffer(fileno($server),timeout,tcp,host,port) */
637              
638             len = sizeof(cliaddr);
639 13526           fd = _accept(fileno, (struct sockaddr *)&cliaddr, len);
640             /* endif */
641 13526 100         if (fd < 0) {
642 13480           goto badexit;
643             }
644              
645 46           rv = _read_timeout(fd, timeout, &read_buf[0], MAX_HEADER_SIZE);
646             // printf("fd:%d rv:%ld %f %d\n",fd,rv,timeout);
647 46 100         if ( rv <= 0 ) {
648 4           close(fd);
649 4           goto badexit;
650             }
651              
652 42           env = newHVhv(env_template);
653              
654 42 50         if ( tcp == 1 ) {
655 42           setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(int));
656 42           (void)hv_stores(env,"REMOTE_ADDR",newSVpv(inet_ntoa(cliaddr.sin_addr),0));
657 42           (void)hv_stores(env,"REMOTE_PORT",newSViv(ntohs(cliaddr.sin_port)));
658             }
659             else {
660 0           (void)hv_stores(env,"REMOTE_ADDR",newSV(0));
661 0           (void)hv_stores(env,"REMOTE_PORT",newSViv(0));
662             }
663 42           (void)hv_stores(env,"SERVER_PORT",SvREFCNT_inc(port));
664 42           (void)hv_stores(env,"SERVER_NAME",SvREFCNT_inc(host));
665              
666             buf_len = rv;
667             while (1) {
668 42           reqlen = _parse_http_request(aTHX_ &read_buf[0],buf_len,env);
669 42 100         if ( reqlen >= 0 ) {
670             break;
671             }
672 4 50         else if ( reqlen == -1 ) {
673             /* error */
674 4           close(fd);
675 4           goto badexit_clear;
676             }
677 0 0         if ( MAX_HEADER_SIZE - buf_len == 0 ) {
678             /* too large header */
679             char* badreq;
680             badreq = BAD_REQUEST;
681 0           rv = _write_timeout(fd, timeout, badreq, sizeof(BAD_REQUEST) - 1);
682 0           close(fd);
683 0           goto badexit_clear;
684             }
685             /* request is incomplete */
686 0           rv = _read_timeout(fd, timeout, &read_buf[buf_len], MAX_HEADER_SIZE - buf_len);
687 0 0         if ( rv <= 0 ) {
688 0           close(fd);
689 0           goto badexit_clear;
690             }
691 0           buf_len += rv;
692             }
693              
694             /* expect */
695 38           SV **expect_val = hv_fetch(env, "HTTP_EXPECT" , sizeof("HTTP_EXPECT")-1, 0);
696 38 50         if (expect_val != NULL) {
697 0 0         if ( strncmp(SvPV_nolen(*expect_val), "100-continue", SvCUR(*expect_val)) == 0 ) {
698 0           rv = _write_timeout(fd, timeout, EXPECT_CONTINUE, sizeof(EXPECT_CONTINUE) - 1);
699 0 0         if ( rv <= 0 ) {
700 0           close(fd);
701 0           goto badexit;
702             }
703             } else {
704 0           rv = _write_timeout(fd, timeout, EXPECT_FAILED, sizeof(EXPECT_FAILED) - 1);
705 0           close(fd);
706 0           goto badexit;
707             }
708             }
709              
710 38           PUSHs(sv_2mortal(newSViv(fd)));
711 38           PUSHs(sv_2mortal(newSVpvn(&read_buf[reqlen], buf_len - reqlen)));
712 38           PUSHs(sv_2mortal(newRV_noinc((SV*)env)));
713 38           XSRETURN(3);
714              
715 4           badexit_clear:
716 4           sv_2mortal((SV*)env);
717 13488           badexit:
718 13488           XSRETURN(0);
719             }
720              
721             unsigned long
722             read_timeout(fileno, rbuf, len, offset, timeout)
723             int fileno
724             SV * rbuf
725             ssize_t len
726             ssize_t offset
727             double timeout
728             PREINIT:
729             SV * buf;
730             char * d;
731             ssize_t rv;
732             ssize_t buf_len;
733             CODE:
734 0 0         if (!SvROK(rbuf)) croak("ERROR: buf must be RV");
735 0           buf = SvRV(rbuf);
736 0 0         if (!SvOK(buf)) {
737 0           sv_setpvn(buf,"",0);
738             }
739 0 0         SvUPGRADE(buf, SVt_PV);
740 0           SvPV_nolen(buf);
741 0           buf_len = SvCUR(buf);
742 0           if ( len > READ_BUFSZ ) {
743             len = READ_BUFSZ;
744             }
745 0 0         d = SvGROW(buf, buf_len + len + 1);
    0          
746 0           rv = _read_timeout(fileno, timeout, &d[offset], len);
747 0 0         SvCUR_set(buf, (rv > 0) ? rv + buf_len : buf_len);
748 0           *SvEND(buf) = '\0';
749 0           SvPOK_only(buf);
750 0 0         if (rv < 0) XSRETURN_UNDEF;
751 0 0         RETVAL = (unsigned long)rv;
752             OUTPUT:
753             RETVAL
754              
755             unsigned long
756             write_timeout(fileno, buf, len, offset, timeout)
757             int fileno
758             SV * buf
759             ssize_t len
760             ssize_t offset
761             double timeout
762             PREINIT:
763             char * d;
764             ssize_t rv;
765             CODE:
766 0 0         SvUPGRADE(buf, SVt_PV);
767 0           d = SvPV_nolen(buf);
768 0           rv = _write_timeout(fileno, timeout, &d[offset], len);
769 0 0         if (rv < 0) XSRETURN_UNDEF;
770 0 0         RETVAL = (unsigned long)rv;
771             OUTPUT:
772             RETVAL
773              
774             unsigned long
775             write_chunk(fileno, buf, offset, timeout)
776             int fileno
777             SV * buf
778             ssize_t offset
779             double timeout
780             PREINIT:
781             char *d;
782             ssize_t buf_len;
783             ssize_t rv = 0;
784             ssize_t written = 0;
785             ssize_t vec_offset = 0;
786             int count =0;
787             int remain;
788             ssize_t iovcnt = 3;
789             char chunked_header_buf[18];
790             CODE:
791 0 0         if ( !SvOK(buf) ) {
792             RETVAL = 0;
793 0           return;
794             }
795 0 0         SvUPGRADE(buf, SVt_PV);
796 0           d = SvPV_nolen(buf);
797 0           buf_len = SvCUR(buf);
798 0 0         if ( buf_len == 0 ){
799             RETVAL = 0;
800             return;
801             }
802              
803 0           {
804 0           struct iovec v[iovcnt]; // Needs C99 compiler
805 0           v[0].iov_len = _chunked_header(chunked_header_buf,buf_len);
806 0           v[0].iov_base = chunked_header_buf;
807 0           v[1].iov_len = buf_len;
808 0           v[1].iov_base = d;
809 0           v[2].iov_base = "\r\n";
810 0           v[2].iov_len = sizeof("\r\n") -1;
811              
812             vec_offset = 0;
813             written = 0;
814             remain = iovcnt;
815 0 0         while ( remain > 0 ) {
816 0           count = (remain > IOV_MAX) ? IOV_MAX : remain;
817 0           rv = _writev_timeout(fileno, timeout, &v[vec_offset], count, (vec_offset == 0) ? 0 : 1);
818 0 0         if ( rv <= 0 ) {
819 0           warn("failed to writev: %zd errno:%d", rv, errno);
820             // error or disconnected
821 0           break;
822             }
823 0           written += rv;
824 0 0         while ( rv > 0 ) {
825 0 0         if ( (unsigned int)rv >= v[vec_offset].iov_len ) {
826 0           rv -= v[vec_offset].iov_len;
827 0           vec_offset++;
828 0           remain--;
829             }
830             else {
831 0           v[vec_offset].iov_base = (char*)v[vec_offset].iov_base + rv;
832 0           v[vec_offset].iov_len -= rv;
833             rv = 0;
834             }
835             }
836             }
837             }
838              
839 0 0         if (rv < 0) XSRETURN_UNDEF;
840 0 0         RETVAL = (unsigned long)written;
841             OUTPUT:
842             RETVAL
843              
844             unsigned long
845             write_all(fileno, buf, offset, timeout)
846             int fileno
847             SV * buf
848             ssize_t offset
849             double timeout
850             PREINIT:
851             char * d;
852             ssize_t buf_len;
853             ssize_t rv;
854             ssize_t written = 0;
855             CODE:
856 0 0         if ( !SvOK(buf) ) {
857             RETVAL = 0;
858             return;
859             }
860 0 0         SvUPGRADE(buf, SVt_PV);
861 0           d = SvPV_nolen(buf);
862 0           buf_len = SvCUR(buf);
863 0 0         if ( buf_len == 0 ) {
864             RETVAL = 0;
865             return;
866             }
867             written = 0;
868 0 0         while ( buf_len > written ) {
869 0           rv = _write_timeout(fileno, timeout, &d[written], buf_len - written);
870 0 0         if ( rv <= 0 ) {
871             break;
872             }
873 0           written += rv;
874             }
875 0 0         if (rv < 0) XSRETURN_UNDEF;
876 0 0         RETVAL = (unsigned long)written;
877             OUTPUT:
878             RETVAL
879              
880              
881             void
882             close_client(fileno)
883             int fileno
884             CODE:
885 38           close(fileno);
886              
887             unsigned long
888             write_informational_response(fileno, timeout, status_code, headers)
889             int fileno
890             double timeout
891             int status_code
892             AV * headers
893             PREINIT:
894             ssize_t rv;
895             ssize_t iovcnt;
896             ssize_t vec_offset;
897             ssize_t written;
898             int count;
899             int remain;
900             size_t i;
901             struct iovec * iv;
902             char status_line[512];
903             char * key;
904             char * val;
905 1           STRLEN key_len = 0;
906 1 50         STRLEN val_len = 0;
907             CODE:
908 1 50         if( (av_len(headers)+1) % 2 == 1 ) croak("ERROR: Odd number of element in header");
909 1           iovcnt = 10 + (av_len(headers)+2)*2;
910 1           {
911 1           struct iovec iv[iovcnt]; // Needs C99 compiler
912             /* status line */
913             iovcnt = 0;
914             i=0;
915 1           status_line[i++] = 'H';
916 1           status_line[i++] = 'T';
917 1           status_line[i++] = 'T';
918 1           status_line[i++] = 'P';
919 1           status_line[i++] = '/';
920 1           status_line[i++] = '1';
921 1           status_line[i++] = '.';
922 1           status_line[i++] = '1';
923 1           status_line[i++] = ' ';
924 1           str_i(status_line,&i,status_code,3);
925 1           status_line[i++] = ' ';
926 1           const char * message = status_message(status_code);
927 1           str_s(status_line,&i, message, strlen(message));
928 1           status_line[i++] = 13;
929 1           status_line[i++] = 10;
930 1           iv[iovcnt].iov_base = status_line;
931 1           iv[iovcnt].iov_len = i;
932             iovcnt++;
933              
934 1           i=0;
935 3 100         while (i < av_len(headers) + 1 ) {
936             /* key */
937 2           key = svpv2char(aTHX_ *av_fetch(headers,i,0), &key_len);
938 2           i++;
939 2           val = svpv2char(aTHX_ *av_fetch(headers,i,0), &val_len);
940 2           i++;
941 2           iv[iovcnt].iov_base = key;
942 2           iv[iovcnt].iov_len = key_len;
943 2           iovcnt++;
944 2           iv[iovcnt].iov_base = ": ";
945 2           iv[iovcnt].iov_len = sizeof(": ") - 1;
946 2           iovcnt++;
947             /* value */
948 2           iv[iovcnt].iov_base = val;
949 2           iv[iovcnt].iov_len = val_len;
950 2           iovcnt++;
951 2           iv[iovcnt].iov_base = "\r\n";
952 2           iv[iovcnt].iov_len = sizeof("\r\n") - 1;
953 2           iovcnt++;
954             }
955 1           iv[iovcnt].iov_base = "\r\n";
956 1           iv[iovcnt].iov_len = sizeof("\r\n") - 1;
957 1           iovcnt++;
958              
959             vec_offset = 0;
960             written = 0;
961 1           remain = iovcnt;
962 2 100         while ( remain > 0 ) {
963 1           count = (remain > IOV_MAX) ? IOV_MAX : remain;
964 1           rv = _writev_timeout(fileno, timeout, &iv[vec_offset], count, (vec_offset == 0) ? 0 : 1);
965 1 50         if ( rv <= 0 ) {
966 0           warn("failed to writev: %zd errno:%d", rv, errno);
967             // error or disconnected
968 0           break;
969             }
970 1           written += rv;
971 11 100         while ( rv > 0 ) {
972 10 50         if ( (unsigned int)rv >= iv[vec_offset].iov_len ) {
973 10           rv -= iv[vec_offset].iov_len;
974 10           vec_offset++;
975 10           remain--;
976             }
977             else {
978 0           iv[vec_offset].iov_base = (char*)iv[vec_offset].iov_base + rv;
979 0           iv[vec_offset].iov_len -= rv;
980             rv = 0;
981             }
982             }
983             }
984             }
985              
986 1 50         if (rv < 0) XSRETURN_UNDEF;
987 1 50         RETVAL = (unsigned long) written;
988             OUTPUT:
989             RETVAL
990              
991              
992             unsigned long
993             write_psgi_response(fileno, timeout, status_code, headers, body, use_chunkedv)
994             int fileno
995             double timeout
996             int status_code
997             AV * headers
998             AV * body
999             SV * use_chunkedv
1000             ALIAS:
1001             Plack::Handler::Gazelle::write_psgi_response = 0
1002             Plack::Handler::Gazelle::write_psgi_response_header = 1
1003             PREINIT:
1004             ssize_t rv;
1005             ssize_t iovcnt;
1006             ssize_t vec_offset;
1007             ssize_t written;
1008             int count;
1009             int remain;
1010             size_t i;
1011             struct iovec * v;
1012             char status_line[512];
1013             char date_line[512];
1014             char server_line[1032];
1015             char * key;
1016             char * val;
1017 37 50         STRLEN key_len = 0;
1018             STRLEN val_len;
1019             int date_pushed = 0;
1020             const char * s;
1021             char* d;
1022             ssize_t n;
1023             IV use_chunked;
1024             char * chunked_header_buf;
1025              
1026             CODE:
1027 37 50         if( (av_len(headers)+1) % 2 == 1 ) croak("ERROR: Odd number of element in header");
1028 37           use_chunked = SvIV(use_chunkedv);
1029 37           iovcnt = 10 + (av_len(headers)+2)*2 + (av_len(body) + 1);
1030              
1031             /* status_with_no_entity_body */
1032 37 50         if ( status_code < 200 || status_code == 204 || status_code == 304 ) {
    50          
1033             use_chunked = 0;
1034             }
1035              
1036 37 100         if ( use_chunked > 0 ) {
1037 29           iovcnt += (av_len(body)+1)*2;
1038             }
1039 37           Newx(chunked_header_buf, 18 * (av_len(body)+2), char);
1040              
1041 37           {
1042 37           struct iovec v[iovcnt]; // Needs C99 compiler
1043             /* status line */
1044             iovcnt = 0;
1045             i=0;
1046 37           status_line[i++] = 'H';
1047 37           status_line[i++] = 'T';
1048 37           status_line[i++] = 'T';
1049 37           status_line[i++] = 'P';
1050 37           status_line[i++] = '/';
1051 37           status_line[i++] = '1';
1052 37           status_line[i++] = '.';
1053 37           status_line[i++] = '1';
1054 37           status_line[i++] = ' ';
1055 37           str_i(status_line,&i,status_code,3);
1056 37           status_line[i++] = ' ';
1057 37           const char * message = status_message(status_code);
1058 37           str_s(status_line,&i, message, strlen(message));
1059 37           status_line[i++] = 13;
1060 37           status_line[i++] = 10;
1061 37           v[iovcnt].iov_base = status_line;
1062 37           v[iovcnt].iov_len = i;
1063             iovcnt++;
1064              
1065             /* for date header */
1066             iovcnt++;
1067              
1068 37           v[iovcnt].iov_base = "Server: gazelle\r\n";
1069 37           v[iovcnt].iov_len = sizeof("Server: gazelle\r\n")-1;
1070             iovcnt++;
1071              
1072 37           i=0;
1073             date_pushed = 0;
1074 74 100         while ( i < av_len(headers) + 1 ) {
1075             /* key */
1076 37           key = svpv2char(aTHX_ *av_fetch(headers,i,0), &key_len);
1077 37           i++;
1078 37 50         if ( strncasecmp(key,"Connection",key_len) == 0 ) {
1079 0           i++;
1080 0           continue;
1081             }
1082              
1083 37           val = svpv2char(aTHX_ *av_fetch(headers,i,0), &val_len);
1084 37           i++;
1085              
1086 37 50         if ( strncasecmp(key,"Date",key_len) == 0 ) {
1087             strcpy(date_line, "Date: ");
1088 0 0         for ( s=val, n = val_len, d=date_line+sizeof("Date: ")-1; n !=0; s++, --n, d++) {
1089 0           *d = *s;
1090             }
1091 0           date_line[sizeof("Date: ") -1 + val_len] = 13;
1092 0           date_line[sizeof("Date: ") -1 + val_len + 1] = 10;
1093 0           v[1].iov_base = date_line;
1094 0           v[1].iov_len = sizeof("Date: ") -1 + val_len + 2;
1095             date_pushed = 1;
1096 0           continue;
1097 37 50         } else if ( strncasecmp(key,"Server",key_len) == 0 ) {
1098             strcpy(server_line, "Server: ");
1099 0 0         for ( s=val, n = val_len, d=server_line+sizeof("Server: ")-1; n !=0; s++, --n, d++) {
1100 0           *d = *s;
1101             }
1102 0           server_line[sizeof("Server: ") -1 + val_len] = 13;
1103 0           server_line[sizeof("Server: ") -1 + val_len + 1] = 10;
1104 0           v[2].iov_base = server_line;
1105 0           v[2].iov_len = sizeof("Server: ") -1 + val_len + 2;
1106 0           continue;
1107 37 50         } else if ( strncasecmp(key,"Content-Length",key_len) == 0 || strncasecmp(key,"Transfer-Encoding",key_len) == 0) {
    50          
1108             use_chunked = 0;
1109             }
1110              
1111 37           v[iovcnt].iov_base = key;
1112 37           v[iovcnt].iov_len = key_len;
1113 37           iovcnt++;
1114 37           v[iovcnt].iov_base = ": ";
1115 37           v[iovcnt].iov_len = sizeof(": ") - 1;
1116 37           iovcnt++;
1117             /* value */
1118 37           v[iovcnt].iov_base = val;
1119 37           v[iovcnt].iov_len = val_len;
1120 37           iovcnt++;
1121 37           v[iovcnt].iov_base = "\r\n";
1122 37           v[iovcnt].iov_len = sizeof("\r\n") - 1;
1123 37           iovcnt++;
1124             }
1125              
1126 37 50         if ( date_pushed == 0 ) {
1127 37           v[1].iov_len = _date_line(date_line);
1128 37           v[1].iov_base = date_line;
1129             }
1130              
1131 37 100         if ( use_chunked > 0 ) {
1132 29           v[iovcnt].iov_base = "Transfer-Encoding: chunked\r\n";
1133 29           v[iovcnt].iov_len = sizeof("Transfer-Encoding: chunked\r\n") - 1;
1134 29           iovcnt++;
1135             }
1136              
1137 37           v[iovcnt].iov_base = "Connection: close\r\n\r\n";
1138 37           v[iovcnt].iov_len = sizeof("Connection: close\r\n\r\n") - 1;
1139 37           iovcnt++;
1140              
1141             size_t chb_offset = 0;
1142 74 100         for (i=0; i < av_len(body) + 1; i++ ) {
1143 37           SV **b = av_fetch(body,i,0);
1144 37 50         if (!SvOK(*b)) {
1145 0           continue;
1146             }
1147 37           d = svpv2char(aTHX_ *b, &val_len);
1148 37 50         if ( val_len < 1 ) {
1149 0           continue;
1150             }
1151 37 100         if ( use_chunked ) {
1152 29           v[iovcnt].iov_len = _chunked_header(&chunked_header_buf[chb_offset],val_len);
1153 29           v[iovcnt].iov_base = &chunked_header_buf[chb_offset];
1154 29           chb_offset += v[iovcnt].iov_len;
1155 29           iovcnt++;
1156             }
1157 37           v[iovcnt].iov_base = d;
1158 37           v[iovcnt].iov_len = val_len;
1159 37           iovcnt++;
1160 37 100         if ( use_chunked ) {
1161 29           v[iovcnt].iov_base = "\r\n";
1162 29           v[iovcnt].iov_len = sizeof("\r\n") -1;
1163 29           iovcnt++;
1164             }
1165             }
1166              
1167 37 100         if ( use_chunked && ix == 0 ) {
1168 29           v[iovcnt].iov_base = "0\r\n\r\n";
1169 29           v[iovcnt].iov_len = sizeof("0\r\n\r\n") - 1;
1170 29           iovcnt++;
1171             }
1172              
1173             vec_offset = 0;
1174             written = 0;
1175 37           remain = iovcnt;
1176 74 100         while ( remain > 0 ) {
1177 37           count = (remain > IOV_MAX) ? IOV_MAX : remain;
1178 37           rv = _writev_timeout(fileno, timeout, &v[vec_offset], count, (vec_offset == 0) ? 0 : 1);
1179 37 50         if ( rv <= 0 ) {
1180 0           warn("failed to writev: %zd errno:%d", rv, errno);
1181             // error or disconnected
1182 0           break;
1183             }
1184 37           written += rv;
1185 486 100         while ( rv > 0 ) {
1186 449 50         if ( (unsigned int)rv >= v[vec_offset].iov_len ) {
1187 449           rv -= v[vec_offset].iov_len;
1188 449           vec_offset++;
1189 449           remain--;
1190             }
1191             else {
1192 0           v[vec_offset].iov_base = (char*)v[vec_offset].iov_base + rv;
1193 0           v[vec_offset].iov_len -= rv;
1194             rv = 0;
1195             }
1196             }
1197             }
1198             }
1199 37           sv_setiv(use_chunkedv, use_chunked);
1200 37           Safefree(chunked_header_buf);
1201 37 50         if (rv < 0) XSRETURN_UNDEF;
1202 37 100         RETVAL = (unsigned long) written;
1203             OUTPUT:
1204             RETVAL