File Coverage

lib/Plack/Handler/Gazelle.xs
Criterion Covered Total %
statement 432 544 79.4
branch 216 342 63.1
condition n/a
subroutine n/a
pod n/a
total 648 886 73.1


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 147           status_message (int code) {
81 147           switch (code) {
82             case 100: return "Continue";
83 0           case 101: return "Switching Protocols";
84 0           case 102: return "Processing"; /* RFC 2518 (WebDAV) */
85 140           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 2           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 2           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 2           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 1560 100         for (i = 0; i != len; ++i, ++s)
    100          
166 1285 100         if (*s == ch)
    100          
167             break;
168             return i;
169             }
170              
171             static
172             int header_is(const struct phr_header* header, const char* name,
173             size_t len)
174             {
175             const char* x, * y;
176 778 100         if (header->name_len != len)
    100          
177             return 0;
178 395 100         for (x = header->name, y = name; len != 0; --len, ++x, ++y)
    100          
179 372 100         if (TOU(*x) != *y)
    100          
    100          
    50          
180             return 0;
181             return 1;
182             }
183              
184              
185              
186             STATIC_INLINE
187 150           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 150           dst = newSV(0);
194 150 50         (void)SvUPGRADE(dst, SVt_PV);
195 150 50         d = SvGROW(dst, src_len * 3 + 1);
    50          
196              
197 577 100         for (i = 0; i < src_len; i++ ) {
198 429 100         if ( src[i] == '%' ) {
199 18 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 16 100         s2 -= s2 <= '9' ? '0'
    50          
205             : s2 <= 'F' ? 'A' - 10
206             : 'a' - 10;
207 16 100         s3 -= s3 <= '9' ? '0'
    50          
208             : s3 <= 'F' ? 'A' - 10
209             : 'a' - 10;
210 16           d[dlen++] = s2 * 16 + s3;
211             i += 2;
212             }
213             else {
214 411           d[dlen++] = src[i];
215             }
216             }
217 148           SvCUR_set(dst, dlen);
218 148           *SvEND(dst) = '\0';
219 148           SvPOK_only(dst);
220 148           (void)hv_stores(env, "PATH_INFO", dst);
221 148           return 1;
222             }
223              
224              
225             STATIC_INLINE
226             int
227 151           _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 151           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 151           ret = phr_parse_request(
242             buf, buf_len,
243             &method, &method_len,
244             &path, &path_len,
245             &minor_version, headers, &num_headers, 0
246             );
247              
248 151 100         if (ret < 0)
249             goto done;
250 150 50         if (minor_version > 1 || minor_version < 0 ) {
251             ret = -1;
252             goto done;
253             }
254              
255 150           (void)hv_stores(env, "REQUEST_METHOD", newSVpvn(method, method_len));
256 150           (void)hv_stores(env, "REQUEST_URI", newSVpvn(path, path_len));
257 150           (void)hv_stores(env, "SCRIPT_NAME", newSVpvn("", 0));
258             strcpy(tmp, "HTTP/1.");
259 150           tmp[sizeof("HTTP/1.")-1] = '0' + minor_version;
260 150           (void)hv_stores(env, "SERVER_PROTOCOL", newSVpvn(tmp, sizeof("HTTP/1.1")-1));
261              
262             /* PATH_INFO QUERY_STRING */
263 300           path_len = find_ch(path, path_len, '#'); /* strip off all text after # after storing request_uri */
264             question_at = find_ch(path, path_len, '?');
265 150 100         if ( store_path_info(aTHX_ env, path, question_at) < 0 ) {
266 2           hv_clear(env);
267             ret = -1;
268 2           goto done;
269             }
270 148 100         if (question_at != path_len) ++question_at;
271 148           (void)hv_stores(env, "QUERY_STRING", newSVpvn(path + question_at, path_len - question_at));
272              
273             last_value = NULL;
274 544 100         for (i = 0; i < num_headers; ++i) {
275 397 100         if (headers[i].name != NULL) {
276             const char* name;
277             size_t name_len;
278             SV** slot;
279 395 100         if (header_is(headers + i, "CONTENT-TYPE", sizeof("CONTENT-TYPE") - 1)) {
280             name = "CONTENT_TYPE";
281             name_len = sizeof("CONTENT_TYPE") - 1;
282 383 100         } else if (header_is(headers + i, "CONTENT-LENGTH", sizeof("CONTENT-LENGTH") - 1)) {
283             name = "CONTENT_LENGTH";
284             name_len = sizeof("CONTENT_LENGTH") - 1;
285             } else {
286             const char* s;
287             char* d;
288             size_t n;
289 372 100         if (sizeof(tmp) - 5 < headers[i].name_len) {
290 1           hv_clear(env);
291             ret = -1;
292 1           goto done;
293             }
294             strcpy(tmp, "HTTP_");
295 4333 100         for (s = headers[i].name, n = headers[i].name_len, d = tmp + 5;
296             n != 0;
297 3962           s++, --n, d++) {
298 3962 100         *d = *s == '-' ? '_' : TOU(*s);
    100          
299             name = tmp;
300 3962           name_len = headers[i].name_len + 5;
301             }
302             }
303 394           slot = hv_fetch(env, name, name_len, 1);
304 394 50         if ( !slot ) croak("ERROR: failed to create hash entry");
305 394 100         if (SvOK(*slot)) {
    50          
    50          
306 4           sv_catpvn(*slot, ", ", 2);
307 4           sv_catpvn(*slot, headers[i].value, headers[i].value_len);
308             } else {
309 390           sv_setpvn(*slot, headers[i].value, headers[i].value_len);
310 390           last_value = *slot;
311             }
312             } else {
313             /* continuing lines of a mulitiline header */
314 2           sv_catpvn(last_value, headers[i].value, headers[i].value_len);
315             }
316             }
317             done:
318 151           return ret;
319             }
320              
321              
322             STATIC_INLINE
323             char *
324 498           svpv2char(pTHX_ SV *sv, STRLEN *lp)
325             {
326 498 50         if (SvGAMAGIC(sv))
    50          
    0          
    0          
327 0           sv = sv_2mortal(newSVsv(sv));
328 498 100         return SvPV(sv, *lp);
329             }
330              
331              
332             STATIC_INLINE
333             int
334 458           _accept(int fileno, struct sockaddr *addr, unsigned int addrlen) {
335             int fd;
336             #ifdef HAVE_ACCEPT4
337             fd = accept4(fileno, addr, &addrlen, SOCK_CLOEXEC|SOCK_NONBLOCK);
338             #else
339 458           fd = accept(fileno, addr, &addrlen);
340             #endif
341 458 100         if (fd < 0) {
342             return fd;
343             }
344             #ifndef HAVE_ACCEPT4
345 168           fcntl(fd, F_SETFD, FD_CLOEXEC);
346 168           fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
347             #endif
348 168           return fd;
349             }
350              
351              
352             STATIC_INLINE
353             ssize_t
354 157           _writev_timeout(const int fileno, const double timeout, struct iovec *iovec, const int iovcnt, const int do_select ) {
355             int rv;
356             int nfound;
357             struct pollfd wfds[1];
358 157 50         if ( do_select == 1) goto WAIT_WRITE;
359             DO_WRITE:
360 157           rv = writev(fileno, iovec, iovcnt);
361 157 50         if ( rv >= 0 ) {
362 157           return rv;
363             }
364 0 0         if ( rv < 0 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK ) {
    0          
    0          
    0          
365 0           return rv;
366             }
367             WAIT_WRITE:
368             while (1) {
369 0           wfds[0].fd = fileno;
370 0           wfds[0].events = POLLOUT;
371 0           nfound = poll(wfds, 1, (int)timeout*1000);
372 0 0         if ( nfound == 1 ) {
373             break;
374             }
375 0 0         if ( nfound == 0 && errno != EINTR ) {
    0          
376             return -1;
377             }
378             }
379             goto DO_WRITE;
380             }
381              
382             STATIC_INLINE
383             ssize_t
384 412           _read_timeout(const int fileno, const double timeout, char * read_buf, const int read_len ) {
385             int rv;
386             int nfound;
387             struct pollfd rfds[1];
388             DO_READ:
389 213           rfds[0].fd = fileno;
390 213           rfds[0].events = POLLIN;
391 426           rv = read(fileno, read_buf, read_len);
392 213 100         if ( rv >= 0 ) {
393 206           return rv;
394             }
395 7 50         if ( rv < 0 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK ) {
    50          
    50          
    0          
396 0           return rv;
397             }
398             WAIT_READ:
399             while (1) {
400 7           nfound = poll(rfds, 1, (int)timeout*1000);
401 7 50         if ( nfound == 1 ) {
402             break;
403             }
404 0 0         if ( nfound == 0 && errno != EINTR ) {
    0          
405             return -1;
406             }
407             }
408             goto DO_READ;
409             }
410              
411             STATIC_INLINE
412             ssize_t
413 42           _write_timeout(const int fileno, const double timeout, char * write_buf, const int write_len ) {
414             int rv;
415             int nfound;
416             struct pollfd wfds[1];
417             DO_WRITE:
418 21           rv = write(fileno, write_buf, write_len);
419 21 50         if ( rv >= 0 ) {
420 21           return rv;
421             }
422 0 0         if ( rv < 0 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK ) {
    0          
    0          
    0          
423 0           return rv;
424             }
425             WAIT_WRITE:
426             while (1) {
427 0           wfds[0].fd = fileno;
428 0           wfds[0].events = POLLOUT;
429 0           nfound = poll(wfds, 1, (int)timeout*1000);
430 0 0         if ( nfound == 1 ) {
431             break;
432             }
433 0 0         if ( nfound == 0 && errno != EINTR ) {
    0          
434             return -1;
435             }
436             }
437             goto DO_WRITE;
438             }
439              
440             STATIC_INLINE
441             void
442             str_s(char * dst, size_t *dst_len, const char * src, int src_len) {
443             int i;
444 145           int dlen = *dst_len;
445 1679 100         for ( i=0; i
    100          
    100          
    100          
446 1242           dst[dlen++] = src[i];
447             }
448 437           *dst_len = dlen;
449             }
450              
451              
452             STATIC_INLINE
453             void
454             str_i(char * dst, size_t * dst_len, int src, int fig) {
455 725           int dlen = *dst_len + fig - 1;
456             do {
457 2181           dst[dlen] = '0' + (src % 10);
458 2181           dlen--;
459 2181           src /= 10;
460 2181 100         } while( dlen >= *dst_len );
    100          
    100          
    100          
    100          
    100          
    100          
461 725           *dst_len += fig;
462             }
463              
464              
465             STATIC_INLINE
466 145           int _date_line(char * date_line) {
467             struct tm gtm;
468             time_t lt;
469             size_t i = 0;
470 145           time(<);
471 145           gmtime_r(<, >m);
472 145           date_line[i++] = 'D';
473 145           date_line[i++] = 'a';
474 145           date_line[i++] = 't';
475 145           date_line[i++] = 'e';
476 145           date_line[i++] = ':';
477 145           date_line[i++] = ' ';
478 145           str_s(date_line, &i, DoW[gtm.tm_wday], 3);
479 145           date_line[i++] = ',';
480 145           date_line[i++] = ' ';
481 145           str_i(date_line, &i, gtm.tm_mday, 2);
482 145           date_line[i++] = ' ';
483 145           str_s(date_line, &i, MoY[gtm.tm_mon], 3);
484 145           date_line[i++] = ' ';
485 145           str_i(date_line, &i, gtm.tm_year + 1900, 4);
486 145           date_line[i++] = ' ';
487 145           str_i(date_line, &i, gtm.tm_hour,2);
488 145           date_line[i++] = ':';
489 145           str_i(date_line, &i, gtm.tm_min,2);
490 145           date_line[i++] = ':';
491 145           str_i(date_line, &i, gtm.tm_sec,2);
492 145           date_line[i++] = ' ';
493 145           date_line[i++] = 'G';
494 145           date_line[i++] = 'M';
495 145           date_line[i++] = 'T';
496 145           date_line[i++] = 13;
497 145           date_line[i++] = 10;
498 145           return i;
499             }
500              
501             STATIC_INLINE
502 96           int _chunked_header(char *buf, ssize_t len) {
503             int dlen = 0, i;
504             ssize_t l = len;
505 213 100         while ( l > 0 ) {
506 117           dlen++;
507 117           l /= 16;
508             }
509             i = dlen;
510 96           buf[i++] = 13;
511 96           buf[i++] = 10;
512 96           buf[i+1] = 0;
513 213 100         while ( len > 0 ) {
514 117           buf[--dlen] = xdigit[len % 16];
515 117           len /= 16;
516             }
517 96           return i;
518             }
519              
520             MODULE = Plack::Handler::Gazelle PACKAGE = Plack::Handler::Gazelle
521              
522             PROTOTYPES: DISABLE
523              
524             BOOT:
525             {
526             AV * psgi_version;
527 205           psgi_version = newAV();
528 205           av_extend(psgi_version, 2);
529 205           (void)av_push(psgi_version,newSViv(1));
530 205           (void)av_push(psgi_version,newSViv(1));
531 205           SvREADONLY_on((SV*)psgi_version);
532              
533             HV *e;
534 205           e = newHV();
535 205           (void)hv_stores(e,"SCRIPT_NAME", newSVpvs(""));
536 205           (void)hv_stores(e,"psgi.version", newRV((SV*)psgi_version));
537 205           (void)hv_stores(e,"psgi.errors", newRV((SV*)PL_stderrgv));
538 205           (void)hv_stores(e,"psgi.url_scheme", newSVpvs("http"));
539 205           (void)hv_stores(e,"psgi.run_once", newSV(0));
540 205           (void)hv_stores(e,"psgi.multithread", newSV(0));
541 205           (void)hv_stores(e,"psgi.multiprocess", newSViv(1));
542 205           (void)hv_stores(e,"psgi.streaming", newSViv(1));
543 205           (void)hv_stores(e,"psgi.nonblocking", newSV(0));
544 205           (void)hv_stores(e,"psgix.input.buffered", newSViv(1));
545 205           (void)hv_stores(e,"psgix.harakiri", newSViv(1));
546              
547             /* stolenn from Feersum */
548             /* placeholders that get defined for every request */
549 205           (void)hv_stores(e, "SERVER_PROTOCOL", &PL_sv_undef);
550 205           (void)hv_stores(e, "SERVER_NAME", &PL_sv_undef);
551 205           (void)hv_stores(e, "SERVER_PORT", &PL_sv_undef);
552 205           (void)hv_stores(e, "REQUEST_URI", &PL_sv_undef);
553 205           (void)hv_stores(e, "REQUEST_METHOD", &PL_sv_undef);
554 205           (void)hv_stores(e, "PATH_INFO", &PL_sv_undef);
555 205           (void)hv_stores(e, "REMOTE_ADDR", &PL_sv_placeholder);
556 205           (void)hv_stores(e, "REMOTE_PORT", &PL_sv_placeholder);
557              
558             /* defaults that get changed for some requests */
559 205           (void)hv_stores(e, "psgi.input", &PL_sv_placeholder);
560 205           (void)hv_stores(e, "CONTENT_LENGTH", &PL_sv_placeholder);
561 205           (void)hv_stores(e, "QUERY_STRING", &PL_sv_placeholder);
562              
563             /* anticipated headers */
564 205           (void)hv_stores(e, "CONTENT_TYPE", &PL_sv_placeholder);
565 205           (void)hv_stores(e, "HTTP_HOST", &PL_sv_placeholder);
566 205           (void)hv_stores(e, "HTTP_USER_AGENT", &PL_sv_placeholder);
567 205           (void)hv_stores(e, "HTTP_ACCEPT", &PL_sv_placeholder);
568 205           (void)hv_stores(e, "HTTP_ACCEPT_LANGUAGE", &PL_sv_placeholder);
569 205           (void)hv_stores(e, "HTTP_ACCEPT_CHARSET", &PL_sv_placeholder);
570 205           (void)hv_stores(e, "HTTP_REFERER", &PL_sv_placeholder);
571 205           (void)hv_stores(e, "HTTP_COOKIE", &PL_sv_placeholder);
572 205           (void)hv_stores(e, "HTTP_IF_MODIFIED_SINCE", &PL_sv_placeholder);
573 205           (void)hv_stores(e, "HTTP_IF_NONE_MATCH", &PL_sv_placeholder);
574 205           (void)hv_stores(e, "HTTP_IF_MODIFIED_SINCE", &PL_sv_placeholder);
575 205           (void)hv_stores(e, "HTTP_IF_NONE_MATCH", &PL_sv_placeholder);
576 205           (void)hv_stores(e, "HTTP_CACHE_CONTROL", &PL_sv_placeholder);
577 205           (void)hv_stores(e, "HTTP_X_FORWARDED_FOR", &PL_sv_placeholder);
578              
579 205           env_template = e;
580             }
581              
582             SV *
583             accept_psgi(fileno, timeout, tcp, host, port)
584             int fileno
585             double timeout
586             int tcp
587             SV * host
588             SV * port
589             PREINIT:
590             int fd;
591             struct sockaddr_in cliaddr;
592             unsigned int len;
593             char read_buf[MAX_HEADER_SIZE];
594             HV * env;
595 458           int flag = 1;
596             ssize_t rv = 0;
597             ssize_t buf_len;
598             ssize_t reqlen;
599             PPCODE:
600             {
601             /* if ( my ($conn, $buf, $env) = accept_buffer(fileno($server),timeout,tcp,host,port) */
602              
603             len = sizeof(cliaddr);
604 458           fd = _accept(fileno, (struct sockaddr *)&cliaddr, len);
605             /* endif */
606 458 100         if (fd < 0) {
607             goto badexit;
608             }
609              
610 168           rv = _read_timeout(fd, timeout, &read_buf[0], MAX_HEADER_SIZE);
611             // printf("fd:%d rv:%ld %f %d\n",fd,rv,timeout);
612 168 100         if ( rv <= 0 ) {
613 17           close(fd);
614 17           goto badexit;
615             }
616              
617 151           env = newHVhv(env_template);
618              
619 151 50         if ( tcp == 1 ) {
620 151           setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(int));
621 151           (void)hv_stores(env,"REMOTE_ADDR",newSVpv(inet_ntoa(cliaddr.sin_addr),0));
622 151 50         (void)hv_stores(env,"REMOTE_PORT",newSViv(ntohs(cliaddr.sin_port)));
623             }
624             else {
625 0           (void)hv_stores(env,"REMOTE_ADDR",newSV(0));
626 0           (void)hv_stores(env,"REMOTE_PORT",newSViv(0));
627             }
628 151           (void)hv_stores(env,"SERVER_PORT",SvREFCNT_inc(port));
629 151           (void)hv_stores(env,"SERVER_NAME",SvREFCNT_inc(host));
630              
631             buf_len = rv;
632             while (1) {
633 151           reqlen = _parse_http_request(aTHX_ &read_buf[0],buf_len,env);
634 151 100         if ( reqlen >= 0 ) {
635             break;
636             }
637 4 50         else if ( reqlen == -1 ) {
638             /* error */
639 4           close(fd);
640 4           goto badexit_clear;
641             }
642 0 0         if ( MAX_HEADER_SIZE - buf_len == 0 ) {
643             /* too large header */
644             char* badreq;
645             badreq = BAD_REQUEST;
646 0           rv = _write_timeout(fd, timeout, badreq, sizeof(BAD_REQUEST) - 1);
647 0           close(fd);
648 0           goto badexit_clear;
649             }
650             /* request is incomplete */
651 0           rv = _read_timeout(fd, timeout, &read_buf[buf_len], MAX_HEADER_SIZE - buf_len);
652 0 0         if ( rv <= 0 ) {
653 0           close(fd);
654 0           goto badexit_clear;
655             }
656 0           buf_len += rv;
657 0           }
658              
659             /* expect */
660 147           SV **expect_val = hv_fetch(env, "HTTP_EXPECT" , sizeof("HTTP_EXPECT")-1, 0);
661 147 50         if (expect_val != NULL) {
662 0 0         if ( strncmp(SvPV_nolen(*expect_val), "100-continue", SvCUR(*expect_val)) == 0 ) {
    0          
    0          
    0          
    0          
663 0           rv = _write_timeout(fd, timeout, EXPECT_CONTINUE, sizeof(EXPECT_CONTINUE) - 1);
664 0 0         if ( rv <= 0 ) {
665 0           close(fd);
666 0           goto badexit;
667             }
668             } else {
669 0           rv = _write_timeout(fd, timeout, EXPECT_FAILED, sizeof(EXPECT_FAILED) - 1);
670 0           close(fd);
671 0           goto badexit;
672             }
673             }
674              
675 147           PUSHs(sv_2mortal(newSViv(fd)));
676 147           PUSHs(sv_2mortal(newSVpvn(&read_buf[reqlen], buf_len - reqlen)));
677 147           PUSHs(sv_2mortal(newRV_noinc((SV*)env)));
678 147           XSRETURN(3);
679              
680             badexit_clear:
681 4           sv_2mortal((SV*)env);
682             badexit:
683 311           XSRETURN(0);
684             }
685              
686             unsigned long
687             read_timeout(fileno, rbuf, len, offset, timeout)
688             int fileno
689             SV * rbuf
690             ssize_t len
691             ssize_t offset
692             double timeout
693             PREINIT:
694             SV * buf;
695             char * d;
696             ssize_t rv;
697             ssize_t buf_len;
698             CODE:
699 38 50         if (!SvROK(rbuf)) croak("ERROR: buf must be RV");
700 38           buf = SvRV(rbuf);
701 38 50         if (!SvOK(buf)) {
    0          
    0          
702 0           sv_setpvn(buf,"",0);
703             }
704 38 50         SvUPGRADE(buf, SVt_PV);
705 38 50         SvPV_nolen(buf);
706 38           buf_len = SvCUR(buf);
707 38 100         if ( len > READ_BUFSZ ) {
708             len = READ_BUFSZ;
709             }
710 38 100         d = SvGROW(buf, buf_len + len + 1);
    50          
711 38           rv = _read_timeout(fileno, timeout, &d[offset], len);
712 38 50         SvCUR_set(buf, (rv > 0) ? rv + buf_len : buf_len);
713 38           *SvEND(buf) = '\0';
714 38           SvPOK_only(buf);
715 38 50         if (rv < 0) XSRETURN_UNDEF;
716 38           RETVAL = (unsigned long)rv;
717             OUTPUT:
718             RETVAL
719              
720             unsigned long
721             write_timeout(fileno, buf, len, offset, timeout)
722             int fileno
723             SV * buf
724             ssize_t len
725             ssize_t offset
726             double timeout
727             PREINIT:
728             char * d;
729             ssize_t rv;
730             CODE:
731 0 0         SvUPGRADE(buf, SVt_PV);
732 0 0         d = SvPV_nolen(buf);
733 0           rv = _write_timeout(fileno, timeout, &d[offset], len);
734 0 0         if (rv < 0) XSRETURN_UNDEF;
735 0           RETVAL = (unsigned long)rv;
736             OUTPUT:
737             RETVAL
738              
739             unsigned long
740             write_chunk(fileno, buf, offset, timeout)
741             int fileno
742             SV * buf
743             ssize_t offset
744             double timeout
745             PREINIT:
746             char *d;
747             ssize_t buf_len;
748             ssize_t rv = 0;
749             ssize_t written = 0;
750             ssize_t vec_offset = 0;
751             int count =0;
752             int remain;
753             ssize_t iovcnt = 3;
754             char chunked_header_buf[18];
755             CODE:
756 12 100         if ( !SvOK(buf) ) {
    50          
    50          
757             RETVAL = 0;
758 2           return;
759             }
760 11 100         SvUPGRADE(buf, SVt_PV);
761 11 100         d = SvPV_nolen(buf);
762 11           buf_len = SvCUR(buf);
763 11 100         if ( buf_len == 0 ){
764             RETVAL = 0;
765             return;
766             }
767              
768 10           {
769 10           struct iovec v[iovcnt]; // Needs C99 compiler
770 10           v[0].iov_len = _chunked_header(chunked_header_buf,buf_len);
771 10           v[0].iov_base = chunked_header_buf;
772 10           v[1].iov_len = buf_len;
773 10           v[1].iov_base = d;
774 10           v[2].iov_base = "\r\n";
775 10           v[2].iov_len = sizeof("\r\n") -1;
776              
777             vec_offset = 0;
778             written = 0;
779             remain = iovcnt;
780 20 100         while ( remain > 0 ) {
781 10           count = (remain > IOV_MAX) ? IOV_MAX : remain;
782 10           rv = _writev_timeout(fileno, timeout, &v[vec_offset], count, (vec_offset == 0) ? 0 : 1);
783 10 50         if ( rv <= 0 ) {
784 0           warn("failed to writev: %zd errno:%d", rv, errno);
785             // error or disconnected
786 0           break;
787             }
788 10           written += rv;
789 40 100         while ( rv > 0 ) {
790 30 50         if ( (unsigned int)rv >= v[vec_offset].iov_len ) {
791 30           rv -= v[vec_offset].iov_len;
792 30           vec_offset++;
793 30           remain--;
794             }
795             else {
796 0           v[vec_offset].iov_base = (char*)v[vec_offset].iov_base + rv;
797 30           v[vec_offset].iov_len -= rv;
798             rv = 0;
799             }
800             }
801             }
802             }
803              
804 10 50         if (rv < 0) XSRETURN_UNDEF;
805 10           RETVAL = (unsigned long)written;
806             OUTPUT:
807             RETVAL
808              
809             unsigned long
810             write_all(fileno, buf, offset, timeout)
811             int fileno
812             SV * buf
813             ssize_t offset
814             double timeout
815             PREINIT:
816             char * d;
817             ssize_t buf_len;
818             ssize_t rv;
819             ssize_t written = 0;
820             CODE:
821 23 100         if ( !SvOK(buf) ) {
    50          
    50          
822             RETVAL = 0;
823             return;
824             }
825 22 100         SvUPGRADE(buf, SVt_PV);
826 22 100         d = SvPV_nolen(buf);
827 22           buf_len = SvCUR(buf);
828 22 100         if ( buf_len == 0 ) {
829             RETVAL = 0;
830             return;
831             }
832             written = 0;
833 42 100         while ( buf_len > written ) {
834 21           rv = _write_timeout(fileno, timeout, &d[written], buf_len - written);
835 21 50         if ( rv <= 0 ) {
836             break;
837             }
838 21           written += rv;
839             }
840 21 50         if (rv < 0) XSRETURN_UNDEF;
841 21           RETVAL = (unsigned long)written;
842             OUTPUT:
843             RETVAL
844              
845              
846             void
847             close_client(fileno)
848             int fileno
849             CODE:
850 147           close(fileno);
851              
852             unsigned long
853             write_informational_response(fileno, timeout, status_code, headers)
854             int fileno
855             double timeout
856             int status_code
857             AV * headers
858             PREINIT:
859             ssize_t rv;
860             ssize_t iovcnt;
861             ssize_t vec_offset;
862             ssize_t written;
863             int count;
864             int remain;
865             size_t i;
866             struct iovec * iv;
867             char status_line[512];
868             char * key;
869             char * val;
870 1           STRLEN key_len = 0;
871 1           STRLEN val_len = 0;
872             CODE:
873 1 50         if( (av_len(headers)+1) % 2 == 1 ) croak("ERROR: Odd number of element in header");
874 1           iovcnt = 10 + (av_len(headers)+2)*2;
875 1           {
876 1           struct iovec iv[iovcnt]; // Needs C99 compiler
877             /* status line */
878             iovcnt = 0;
879             i=0;
880 1           status_line[i++] = 'H';
881 1           status_line[i++] = 'T';
882 1           status_line[i++] = 'T';
883 1           status_line[i++] = 'P';
884 1           status_line[i++] = '/';
885 1           status_line[i++] = '1';
886 1           status_line[i++] = '.';
887 1           status_line[i++] = '1';
888 1           status_line[i++] = ' ';
889             str_i(status_line,&i,status_code,3);
890 1           status_line[i++] = ' ';
891 1           const char * message = status_message(status_code);
892 1           str_s(status_line,&i, message, strlen(message));
893 1           status_line[i++] = 13;
894 1           status_line[i++] = 10;
895 1           iv[iovcnt].iov_base = status_line;
896 1           iv[iovcnt].iov_len = i;
897             iovcnt++;
898              
899             i=0;
900 3 100         while (i < av_len(headers) + 1 ) {
901             /* key */
902 2           key = svpv2char(aTHX_ *av_fetch(headers,i,0), &key_len);
903 2           i++;
904 2           val = svpv2char(aTHX_ *av_fetch(headers,i,0), &val_len);
905 2           i++;
906 2           iv[iovcnt].iov_base = key;
907 2           iv[iovcnt].iov_len = key_len;
908 2           iovcnt++;
909 2           iv[iovcnt].iov_base = ": ";
910 2           iv[iovcnt].iov_len = sizeof(": ") - 1;
911 2           iovcnt++;
912             /* value */
913 2           iv[iovcnt].iov_base = val;
914 2           iv[iovcnt].iov_len = val_len;
915 2           iovcnt++;
916 2           iv[iovcnt].iov_base = "\r\n";
917 2           iv[iovcnt].iov_len = sizeof("\r\n") - 1;
918 2           iovcnt++;
919             }
920 1           iv[iovcnt].iov_base = "\r\n";
921 1           iv[iovcnt].iov_len = sizeof("\r\n") - 1;
922 1           iovcnt++;
923              
924             vec_offset = 0;
925             written = 0;
926 1           remain = iovcnt;
927 2 100         while ( remain > 0 ) {
928 1           count = (remain > IOV_MAX) ? IOV_MAX : remain;
929 1           rv = _writev_timeout(fileno, timeout, &iv[vec_offset], count, (vec_offset == 0) ? 0 : 1);
930 1 50         if ( rv <= 0 ) {
931 0           warn("failed to writev: %zd errno:%d", rv, errno);
932             // error or disconnected
933 0           break;
934             }
935 1           written += rv;
936 11 100         while ( rv > 0 ) {
937 10 50         if ( (unsigned int)rv >= iv[vec_offset].iov_len ) {
938 10           rv -= iv[vec_offset].iov_len;
939 10           vec_offset++;
940 10           remain--;
941             }
942             else {
943 0           iv[vec_offset].iov_base = (char*)iv[vec_offset].iov_base + rv;
944 10           iv[vec_offset].iov_len -= rv;
945             rv = 0;
946             }
947             }
948             }
949             }
950              
951 1 50         if (rv < 0) XSRETURN_UNDEF;
952 1           RETVAL = (unsigned long) written;
953             OUTPUT:
954             RETVAL
955              
956              
957             unsigned long
958             write_psgi_response(fileno, timeout, status_code, headers, body, use_chunkedv)
959             int fileno
960             double timeout
961             int status_code
962             AV * headers
963             AV * body
964             SV * use_chunkedv
965             ALIAS:
966             Plack::Handler::Gazelle::write_psgi_response = 0
967             Plack::Handler::Gazelle::write_psgi_response_header = 1
968             PREINIT:
969             ssize_t rv;
970             ssize_t iovcnt;
971             ssize_t vec_offset;
972             ssize_t written;
973             int count;
974             int remain;
975             size_t i;
976             struct iovec * v;
977             char status_line[512];
978             char date_line[512];
979             char server_line[1032];
980             char * key;
981             char * val;
982 146           STRLEN key_len = 0;
983             STRLEN val_len;
984             int date_pushed = 0;
985             const char * s;
986             char* d;
987             ssize_t n;
988             IV use_chunked;
989             char * chunked_header_buf;
990              
991             CODE:
992 146 50         if( (av_len(headers)+1) % 2 == 1 ) croak("ERROR: Odd number of element in header");
993 146 50         use_chunked = SvIV(use_chunkedv);
994 146           iovcnt = 10 + (av_len(headers)+2)*2 + (av_len(body) + 1);
995              
996             /* status_with_no_entity_body */
997 146 50         if ( status_code < 200 || status_code == 204 || status_code == 304 ) {
    100          
998             use_chunked = 0;
999             }
1000              
1001 146 100         if ( use_chunked > 0 ) {
1002 93           iovcnt += (av_len(body)+1)*2;
1003             }
1004 146           Newx(chunked_header_buf, 18 * (av_len(body)+2), char);
1005              
1006 146           {
1007 146           struct iovec v[iovcnt]; // Needs C99 compiler
1008             /* status line */
1009             iovcnt = 0;
1010             i=0;
1011 146           status_line[i++] = 'H';
1012 146           status_line[i++] = 'T';
1013 146           status_line[i++] = 'T';
1014 146           status_line[i++] = 'P';
1015 146           status_line[i++] = '/';
1016 146           status_line[i++] = '1';
1017 146           status_line[i++] = '.';
1018 146           status_line[i++] = '1';
1019 146           status_line[i++] = ' ';
1020             str_i(status_line,&i,status_code,3);
1021 146           status_line[i++] = ' ';
1022 146           const char * message = status_message(status_code);
1023 146           str_s(status_line,&i, message, strlen(message));
1024 146           status_line[i++] = 13;
1025 146           status_line[i++] = 10;
1026 146           v[iovcnt].iov_base = status_line;
1027 146           v[iovcnt].iov_len = i;
1028             iovcnt++;
1029              
1030             /* for date header */
1031             iovcnt++;
1032              
1033 146           v[iovcnt].iov_base = "Server: gazelle\r\n";
1034 146           v[iovcnt].iov_len = sizeof("Server: gazelle\r\n")-1;
1035             iovcnt++;
1036              
1037             i=0;
1038             date_pushed = 0;
1039 325 100         while ( i < av_len(headers) + 1 ) {
1040             /* key */
1041 179           key = svpv2char(aTHX_ *av_fetch(headers,i,0), &key_len);
1042 179           i++;
1043 179 50         if ( strncasecmp(key,"Connection",key_len) == 0 ) {
1044 0           i++;
1045 0           continue;
1046             }
1047              
1048 179           val = svpv2char(aTHX_ *av_fetch(headers,i,0), &val_len);
1049 179           i++;
1050              
1051 179 100         if ( strncasecmp(key,"Date",key_len) == 0 ) {
1052             strcpy(date_line, "Date: ");
1053 5 100         for ( s=val, n = val_len, d=date_line+sizeof("Date: ")-1; n !=0; s++, --n, d++) {
1054 4           *d = *s;
1055             }
1056 1           date_line[sizeof("Date: ") -1 + val_len] = 13;
1057 1           date_line[sizeof("Date: ") -1 + val_len + 1] = 10;
1058 1           v[1].iov_base = date_line;
1059 1           v[1].iov_len = sizeof("Date: ") -1 + val_len + 2;
1060             date_pushed = 1;
1061 1           continue;
1062 178 100         } else if ( strncasecmp(key,"Server",key_len) == 0 ) {
1063             strcpy(server_line, "Server: ");
1064 6 100         for ( s=val, n = val_len, d=server_line+sizeof("Server: ")-1; n !=0; s++, --n, d++) {
1065 5           *d = *s;
1066             }
1067 1           server_line[sizeof("Server: ") -1 + val_len] = 13;
1068 1           server_line[sizeof("Server: ") -1 + val_len + 1] = 10;
1069 1           v[2].iov_base = server_line;
1070 1           v[2].iov_len = sizeof("Server: ") -1 + val_len + 2;
1071 1           continue;
1072 177 100         } else if ( strncasecmp(key,"Content-Length",key_len) == 0 || strncasecmp(key,"Transfer-Encoding",key_len) == 0) {
    50          
1073             use_chunked = 0;
1074             }
1075              
1076 177           v[iovcnt].iov_base = key;
1077 177           v[iovcnt].iov_len = key_len;
1078 177           iovcnt++;
1079 177           v[iovcnt].iov_base = ": ";
1080 177           v[iovcnt].iov_len = sizeof(": ") - 1;
1081 177           iovcnt++;
1082             /* value */
1083 177           v[iovcnt].iov_base = val;
1084 177           v[iovcnt].iov_len = val_len;
1085 177           iovcnt++;
1086 177           v[iovcnt].iov_base = "\r\n";
1087 177           v[iovcnt].iov_len = sizeof("\r\n") - 1;
1088 179           iovcnt++;
1089             }
1090              
1091 146 100         if ( date_pushed == 0 ) {
1092 145           v[1].iov_len = _date_line(date_line);
1093 145           v[1].iov_base = date_line;
1094             }
1095              
1096 146 100         if ( use_chunked > 0 ) {
1097 89           v[iovcnt].iov_base = "Transfer-Encoding: chunked\r\n";
1098 89           v[iovcnt].iov_len = sizeof("Transfer-Encoding: chunked\r\n") - 1;
1099 89           iovcnt++;
1100             }
1101              
1102 146           v[iovcnt].iov_base = "Connection: close\r\n\r\n";
1103 146           v[iovcnt].iov_len = sizeof("Connection: close\r\n\r\n") - 1;
1104 146           iovcnt++;
1105              
1106             size_t chb_offset = 0;
1107 286 100         for (i=0; i < av_len(body) + 1; i++ ) {
1108 140           SV **b = av_fetch(body,i,0);
1109 140 100         if (!SvOK(*b)) {
    50          
    50          
1110 4           continue;
1111             }
1112 136           d = svpv2char(aTHX_ *b, &val_len);
1113 136 100         if ( val_len < 1 ) {
1114 4           continue;
1115             }
1116 132 100         if ( use_chunked ) {
1117 86           v[iovcnt].iov_len = _chunked_header(&chunked_header_buf[chb_offset],val_len);
1118 86           v[iovcnt].iov_base = &chunked_header_buf[chb_offset];
1119 86           chb_offset += v[iovcnt].iov_len;
1120 86           iovcnt++;
1121             }
1122 132           v[iovcnt].iov_base = d;
1123 132           v[iovcnt].iov_len = val_len;
1124 132           iovcnt++;
1125 132 100         if ( use_chunked ) {
1126 86           v[iovcnt].iov_base = "\r\n";
1127 86           v[iovcnt].iov_len = sizeof("\r\n") -1;
1128 86           iovcnt++;
1129             }
1130             }
1131              
1132 146 100         if ( use_chunked && ix == 0 ) {
1133 84           v[iovcnt].iov_base = "0\r\n\r\n";
1134 84           v[iovcnt].iov_len = sizeof("0\r\n\r\n") - 1;
1135 84           iovcnt++;
1136             }
1137              
1138             vec_offset = 0;
1139             written = 0;
1140 146           remain = iovcnt;
1141 292 100         while ( remain > 0 ) {
1142 146           count = (remain > IOV_MAX) ? IOV_MAX : remain;
1143 146           rv = _writev_timeout(fileno, timeout, &v[vec_offset], count, (vec_offset == 0) ? 0 : 1);
1144 146 50         if ( rv <= 0 ) {
1145 0           warn("failed to writev: %zd errno:%d", rv, errno);
1146             // error or disconnected
1147 0           break;
1148             }
1149 146           written += rv;
1150 1915 100         while ( rv > 0 ) {
1151 1769 50         if ( (unsigned int)rv >= v[vec_offset].iov_len ) {
1152 1769           rv -= v[vec_offset].iov_len;
1153 1769           vec_offset++;
1154 1769           remain--;
1155             }
1156             else {
1157 0           v[vec_offset].iov_base = (char*)v[vec_offset].iov_base + rv;
1158 1769           v[vec_offset].iov_len -= rv;
1159             rv = 0;
1160             }
1161             }
1162             }
1163             }
1164 146           sv_setiv(use_chunkedv, use_chunked);
1165 146           Safefree(chunked_header_buf);
1166 146 50         if (rv < 0) XSRETURN_UNDEF;
1167 146           RETVAL = (unsigned long) written;
1168             OUTPUT:
1169             RETVAL