Annotation of embedaddon/php/sapi/cli/php_cli_server.c, revision 1.1

1.1     ! misho       1: /*
        !             2:    +----------------------------------------------------------------------+
        !             3:    | PHP Version 5                                                        |
        !             4:    +----------------------------------------------------------------------+
        !             5:    | Copyright (c) 1997-2012 The PHP Group                                |
        !             6:    +----------------------------------------------------------------------+
        !             7:    | This source file is subject to version 3.01 of the PHP license,      |
        !             8:    | that is bundled with this package in the file LICENSE, and is        |
        !             9:    | available through the world-wide-web at the following url:           |
        !            10:    | http://www.php.net/license/3_01.txt                                  |
        !            11:    | If you did not receive a copy of the PHP license and are unable to   |
        !            12:    | obtain it through the world-wide-web, please send a note to          |
        !            13:    | license@php.net so we can mail you a copy immediately.               |
        !            14:    +----------------------------------------------------------------------+
        !            15:    | Author: Moriyoshi Koizumi <moriyoshi@php.net>                        |
        !            16:    |         Xinchen Hui       <laruence@php.net>                         |
        !            17:    +----------------------------------------------------------------------+
        !            18: */
        !            19: 
        !            20: /* $Id: php_cli.c 306938 2011-01-01 02:17:06Z felipe $ */
        !            21: 
        !            22: #include <stdio.h>
        !            23: #include <fcntl.h>
        !            24: #include <assert.h>
        !            25: 
        !            26: #ifdef PHP_WIN32
        !            27: #include <process.h>
        !            28: #include <io.h>
        !            29: #include "win32/time.h"
        !            30: #include "win32/signal.h"
        !            31: #include "win32/php_registry.h"
        !            32: #else
        !            33: # include "php_config.h"
        !            34: #endif
        !            35: 
        !            36: #ifdef __riscos__
        !            37: #include <unixlib/local.h>
        !            38: #endif
        !            39: 
        !            40: 
        !            41: #if HAVE_TIME_H
        !            42: #include <time.h>
        !            43: #endif
        !            44: #if HAVE_SYS_TIME_H
        !            45: #include <sys/time.h>
        !            46: #endif
        !            47: #if HAVE_UNISTD_H
        !            48: #include <unistd.h>
        !            49: #endif
        !            50: #if HAVE_SIGNAL_H
        !            51: #include <signal.h>
        !            52: #endif
        !            53: #if HAVE_SETLOCALE
        !            54: #include <locale.h>
        !            55: #endif
        !            56: #if HAVE_DLFCN_H
        !            57: #include <dlfcn.h>
        !            58: #endif
        !            59: 
        !            60: #include "SAPI.h"
        !            61: #include "php.h"
        !            62: #include "php_ini.h"
        !            63: #include "php_main.h"
        !            64: #include "php_globals.h"
        !            65: #include "php_variables.h"
        !            66: #include "zend_hash.h"
        !            67: #include "zend_modules.h"
        !            68: #include "fopen_wrappers.h"
        !            69: 
        !            70: #include "zend_compile.h"
        !            71: #include "zend_execute.h"
        !            72: #include "zend_highlight.h"
        !            73: #include "zend_indent.h"
        !            74: #include "zend_exceptions.h"
        !            75: 
        !            76: #include "php_getopt.h"
        !            77: 
        !            78: #ifndef PHP_WIN32
        !            79: # define php_select(m, r, w, e, t)     select(m, r, w, e, t)
        !            80: # define SOCK_EINVAL EINVAL
        !            81: # define SOCK_EAGAIN EAGAIN
        !            82: # define SOCK_EINTR EINTR
        !            83: # define SOCK_EADDRINUSE EADDRINUSE
        !            84: #else
        !            85: # include "win32/select.h"
        !            86: # define SOCK_EINVAL WSAEINVAL
        !            87: # define SOCK_EAGAIN WSAEWOULDBLOCK
        !            88: # define SOCK_EINTR WSAEINTR
        !            89: # define SOCK_EADDRINUSE WSAEADDRINUSE
        !            90: #endif
        !            91: 
        !            92: #ifndef S_ISDIR
        !            93: #define S_ISDIR(mode)  (((mode)&S_IFMT) == S_IFDIR)
        !            94: #endif
        !            95: 
        !            96: #include "ext/standard/file.h" /* for php_set_sock_blocking() :-( */
        !            97: #include "ext/standard/php_smart_str.h"
        !            98: #include "ext/standard/html.h"
        !            99: #include "ext/standard/url.h" /* for php_url_decode() */
        !           100: #include "ext/standard/php_string.h" /* for php_dirname() */
        !           101: #include "ext/standard/info.h" /* for php_info_print_style() */
        !           102: #include "php_network.h"
        !           103: 
        !           104: #include "php_http_parser.h"
        !           105: #include "php_cli_server.h"
        !           106: 
        !           107: #define OUTPUT_NOT_CHECKED -1
        !           108: #define OUTPUT_IS_TTY 1
        !           109: #define OUTPUT_NOT_TTY 0
        !           110: 
        !           111: typedef struct php_cli_server_poller {
        !           112:        fd_set rfds, wfds;
        !           113:        struct {
        !           114:                fd_set rfds, wfds;
        !           115:        } active;
        !           116:        php_socket_t max_fd;
        !           117: } php_cli_server_poller;
        !           118: 
        !           119: typedef struct php_cli_server_request {
        !           120:        enum php_http_method request_method;    
        !           121:        int protocol_version;
        !           122:        char *request_uri;
        !           123:        size_t request_uri_len;
        !           124:        char *vpath;
        !           125:        size_t vpath_len;
        !           126:        char *path_translated;
        !           127:        size_t path_translated_len;
        !           128:        char *path_info;
        !           129:        size_t path_info_len;
        !           130:        char *query_string;
        !           131:        size_t query_string_len;
        !           132:        HashTable headers;
        !           133:        char *content;
        !           134:        size_t content_len;
        !           135:        const char *ext;
        !           136:        size_t ext_len;
        !           137:        struct stat sb;
        !           138: } php_cli_server_request;
        !           139: 
        !           140: typedef struct php_cli_server_chunk {
        !           141:        struct php_cli_server_chunk *next;
        !           142:        enum php_cli_server_chunk_type {
        !           143:                PHP_CLI_SERVER_CHUNK_HEAP,
        !           144:                PHP_CLI_SERVER_CHUNK_IMMORTAL
        !           145:        } type;
        !           146:        union {
        !           147:                struct { void *block; char *p; size_t len; } heap;
        !           148:                struct { const char *p; size_t len; } immortal;
        !           149:        } data;
        !           150: } php_cli_server_chunk;
        !           151: 
        !           152: typedef struct php_cli_server_buffer {
        !           153:        php_cli_server_chunk *first;
        !           154:        php_cli_server_chunk *last;
        !           155: } php_cli_server_buffer;
        !           156: 
        !           157: typedef struct php_cli_server_content_sender {
        !           158:        php_cli_server_buffer buffer;
        !           159: } php_cli_server_content_sender;
        !           160: 
        !           161: typedef struct php_cli_server_client {
        !           162:        struct php_cli_server *server;
        !           163:        php_socket_t sock;
        !           164:        struct sockaddr *addr;
        !           165:        socklen_t addr_len;
        !           166:        char *addr_str;
        !           167:        size_t addr_str_len;
        !           168:        php_http_parser parser;
        !           169:        unsigned int request_read:1;
        !           170:        char *current_header_name;
        !           171:        size_t current_header_name_len;
        !           172:        unsigned int current_header_name_allocated:1;
        !           173:        size_t post_read_offset;
        !           174:        php_cli_server_request request;
        !           175:        unsigned int content_sender_initialized:1;
        !           176:        php_cli_server_content_sender content_sender;
        !           177:        php_cli_server_buffer capture_buffer;
        !           178:        unsigned int capturing:1;
        !           179:        int file_fd;
        !           180: } php_cli_server_client;
        !           181: 
        !           182: typedef struct php_cli_server {
        !           183:        php_socket_t server_sock;
        !           184:        php_cli_server_poller poller;
        !           185:        int is_running;
        !           186:        char *host;
        !           187:        int port;
        !           188:        int address_family;
        !           189:        char *document_root;
        !           190:        size_t document_root_len;
        !           191:        char *router;
        !           192:        size_t router_len;
        !           193:        socklen_t socklen;
        !           194:        HashTable clients;
        !           195: } php_cli_server;
        !           196: 
        !           197: typedef struct php_cli_server_http_reponse_status_code_pair {
        !           198:        int code;
        !           199:        const char *str;
        !           200: } php_cli_server_http_reponse_status_code_pair;
        !           201: 
        !           202: typedef struct php_cli_server_ext_mime_type_pair {
        !           203:        const char *ext;
        !           204:        const char *mime_type;
        !           205: } php_cli_server_ext_mime_type_pair;
        !           206: 
        !           207: static php_cli_server_http_reponse_status_code_pair status_map[] = {
        !           208:        { 100, "Continue" },
        !           209:        { 101, "Switching Protocols" },
        !           210:        { 200, "OK" },
        !           211:        { 201, "Created" },
        !           212:        { 202, "Accepted" },
        !           213:        { 203, "Non-Authoritative Information" },
        !           214:        { 204, "No Content" },
        !           215:        { 205, "Reset Content" },
        !           216:        { 206, "Partial Content" },
        !           217:        { 300, "Multiple Choices" },
        !           218:        { 301, "Moved Permanently" },
        !           219:        { 302, "Found" },
        !           220:        { 303, "See Other" },
        !           221:        { 304, "Not Modified" },
        !           222:        { 305, "Use Proxy" },
        !           223:        { 307, "Temporary Redirect" },
        !           224:        { 400, "Bad Request" },
        !           225:        { 401, "Unauthorized" },
        !           226:        { 402, "Payment Required" },
        !           227:        { 403, "Forbidden" },
        !           228:        { 404, "Not Found" },
        !           229:        { 405, "Method Not Allowed" },
        !           230:        { 406, "Not Acceptable" },
        !           231:        { 407, "Proxy Authentication Required" },
        !           232:        { 408, "Request Timeout" },
        !           233:        { 409, "Conflict" },
        !           234:        { 410, "Gone" },
        !           235:        { 411, "Length Required" },
        !           236:        { 412, "Precondition Failed" },
        !           237:        { 413, "Request Entity Too Large" },
        !           238:        { 414, "Request-URI Too Long" },
        !           239:        { 415, "Unsupported Media Type" },
        !           240:        { 416, "Requested Range Not Satisfiable" },
        !           241:        { 417, "Expectation Failed" },
        !           242:        { 500, "Internal Server Error" },
        !           243:        { 501, "Not Implemented" },
        !           244:        { 502, "Bad Gateway" },
        !           245:        { 503, "Service Unavailable" },
        !           246:        { 504, "Gateway Timeout" },
        !           247:        { 505, "HTTP Version Not Supported" },
        !           248: };
        !           249: 
        !           250: static php_cli_server_http_reponse_status_code_pair template_map[] = {
        !           251:        { 400, "<h1 class=\"h\">%s</h1><p>Your browser sent a request that this server could not understand.</p>" },
        !           252:        { 404, "<h1 class=\"h\">%s</h1><p>The requested resource %s was not found on this server.</p>" },
        !           253:        { 500, "<h1 class=\"h\">%s</h1><p>The server is temporality unavaiable.</p>" }
        !           254: };
        !           255: 
        !           256: static php_cli_server_ext_mime_type_pair mime_type_map[] = {
        !           257:        { "gif", "image/gif" },
        !           258:        { "png", "image/png" },
        !           259:        { "jpe", "image/jpeg" },
        !           260:        { "jpg", "image/jpeg" },
        !           261:        { "jpeg", "image/jpeg" },
        !           262:        { "css", "text/css" },
        !           263:        { "html", "text/html" },
        !           264:        { "txt", "text/plain" },
        !           265:        { "js", "text/javascript" },
        !           266:        { NULL, NULL }
        !           267: };
        !           268: 
        !           269: static int php_cli_output_is_tty = OUTPUT_NOT_CHECKED;
        !           270: 
        !           271: static size_t php_cli_server_client_send_through(php_cli_server_client *client, const char *str, size_t str_len);
        !           272: static php_cli_server_chunk *php_cli_server_chunk_heap_new_self_contained(size_t len);
        !           273: static void php_cli_server_buffer_append(php_cli_server_buffer *buffer, php_cli_server_chunk *chunk);
        !           274: static void php_cli_server_logf(const char *format TSRMLS_DC, ...);
        !           275: static void php_cli_server_log_response(php_cli_server_client *client, int status, const char *message TSRMLS_DC);
        !           276: 
        !           277: ZEND_DECLARE_MODULE_GLOBALS(cli_server);
        !           278: 
        !           279: static void char_ptr_dtor_p(char **p) /* {{{ */
        !           280: {
        !           281:        pefree(*p, 1);
        !           282: } /* }}} */
        !           283: 
        !           284: static char *get_last_error() /* {{{ */
        !           285: {
        !           286:        return pestrdup(strerror(errno), 1);
        !           287: } /* }}} */
        !           288: 
        !           289: static const char *get_status_string(int code) /* {{{ */
        !           290: {
        !           291:        size_t e = (sizeof(status_map) / sizeof(php_cli_server_http_reponse_status_code_pair));
        !           292:        size_t s = 0;
        !           293: 
        !           294:        while (e != s) {
        !           295:                size_t c = MIN((e + s + 1) / 2, e - 1);
        !           296:                int d = status_map[c].code;
        !           297:                if (d > code) {
        !           298:                        e = c;
        !           299:                } else if (d < code) {
        !           300:                        s = c;
        !           301:                } else {
        !           302:                        return status_map[c].str;
        !           303:                }
        !           304:        }
        !           305:        return NULL;
        !           306: } /* }}} */
        !           307: 
        !           308: static const char *get_template_string(int code) /* {{{ */
        !           309: {
        !           310:        size_t e = (sizeof(template_map) / sizeof(php_cli_server_http_reponse_status_code_pair));
        !           311:        size_t s = 0;
        !           312: 
        !           313:        while (e != s) {
        !           314:                size_t c = MIN((e + s + 1) / 2, e - 1);
        !           315:                int d = template_map[c].code;
        !           316:                if (d > code) {
        !           317:                        e = c;
        !           318:                } else if (d < code) {
        !           319:                        s = c;
        !           320:                } else {
        !           321:                        return template_map[c].str;
        !           322:                }
        !           323:        }
        !           324:        return NULL;
        !           325: } /* }}} */
        !           326: 
        !           327: static void append_http_status_line(smart_str *buffer, int protocol_version, int response_code, int persistent) /* {{{ */
        !           328: {
        !           329:        if (!response_code) {
        !           330:                response_code = 200;
        !           331:        }
        !           332:        smart_str_appendl_ex(buffer, "HTTP", 4, persistent);
        !           333:        smart_str_appendc_ex(buffer, '/', persistent);
        !           334:        smart_str_append_generic_ex(buffer, protocol_version / 100, persistent, int, _unsigned);
        !           335:        smart_str_appendc_ex(buffer, '.', persistent);
        !           336:        smart_str_append_generic_ex(buffer, protocol_version % 100, persistent, int, _unsigned);
        !           337:        smart_str_appendc_ex(buffer, ' ', persistent);
        !           338:        smart_str_append_generic_ex(buffer, response_code, persistent, int, _unsigned);
        !           339:        smart_str_appendc_ex(buffer, ' ', persistent);
        !           340:        smart_str_appends_ex(buffer, get_status_string(response_code), persistent);
        !           341:        smart_str_appendl_ex(buffer, "\r\n", 2, persistent);
        !           342: } /* }}} */
        !           343: 
        !           344: static void append_essential_headers(smart_str* buffer, php_cli_server_client *client, int persistent) /* {{{ */
        !           345: {
        !           346:        {
        !           347:                char **val;
        !           348:                if (SUCCESS == zend_hash_find(&client->request.headers, "Host", sizeof("Host"), (void**)&val)) {
        !           349:                        smart_str_appendl_ex(buffer, "Host", sizeof("Host") - 1, persistent);
        !           350:                        smart_str_appendl_ex(buffer, ": ", sizeof(": ") - 1, persistent);
        !           351:                        smart_str_appends_ex(buffer, *val, persistent);
        !           352:                        smart_str_appendl_ex(buffer, "\r\n", 2, persistent);
        !           353:                }
        !           354:        }
        !           355:        smart_str_appendl_ex(buffer, "Connection: close\r\n", sizeof("Connection: close\r\n") - 1, persistent);
        !           356: } /* }}} */
        !           357: 
        !           358: static const char *get_mime_type(const char *ext, size_t ext_len) /* {{{ */
        !           359: {
        !           360:        php_cli_server_ext_mime_type_pair *pair;
        !           361:        for (pair = mime_type_map; pair->ext; pair++) {
        !           362:                size_t len = strlen(pair->ext);
        !           363:                if (len == ext_len && memcmp(pair->ext, ext, len) == 0) {
        !           364:                        return pair->mime_type;
        !           365:                }
        !           366:        }
        !           367:        return NULL;
        !           368: } /* }}} */
        !           369: 
        !           370: /* {{{ cli_server module
        !           371:  */
        !           372: 
        !           373: static void cli_server_init_globals(zend_cli_server_globals *cg TSRMLS_DC)
        !           374: {
        !           375:        cg->color = 0;
        !           376: }
        !           377: 
        !           378: PHP_INI_BEGIN()
        !           379:        STD_PHP_INI_BOOLEAN("cli_server.color", "0", PHP_INI_ALL, OnUpdateBool, color, zend_cli_server_globals, cli_server_globals)
        !           380: PHP_INI_END()
        !           381: 
        !           382: static PHP_MINIT_FUNCTION(cli_server)
        !           383: {
        !           384:        ZEND_INIT_MODULE_GLOBALS(cli_server, cli_server_init_globals, NULL);
        !           385:        REGISTER_INI_ENTRIES();
        !           386:        return SUCCESS;
        !           387: }
        !           388: 
        !           389: static PHP_MSHUTDOWN_FUNCTION(cli_server)
        !           390: {
        !           391:        UNREGISTER_INI_ENTRIES();
        !           392:        return SUCCESS;
        !           393: }
        !           394: 
        !           395: static PHP_MINFO_FUNCTION(cli_server)
        !           396: {
        !           397:        DISPLAY_INI_ENTRIES();
        !           398: }
        !           399: 
        !           400: zend_module_entry cli_server_module_entry = {
        !           401:        STANDARD_MODULE_HEADER,
        !           402:        "cli_server",
        !           403:        NULL,
        !           404:        PHP_MINIT(cli_server),
        !           405:        PHP_MSHUTDOWN(cli_server),
        !           406:        NULL,
        !           407:        NULL,
        !           408:        PHP_MINFO(cli_server),
        !           409:        PHP_VERSION,
        !           410:        STANDARD_MODULE_PROPERTIES
        !           411: };
        !           412: /* }}} */
        !           413: 
        !           414: static int sapi_cli_server_startup(sapi_module_struct *sapi_module) /* {{{ */
        !           415: {
        !           416:        if (php_module_startup(sapi_module, &cli_server_module_entry, 1) == FAILURE) {
        !           417:                return FAILURE;
        !           418:        }
        !           419:        return SUCCESS;
        !           420: } /* }}} */
        !           421: 
        !           422: static int sapi_cli_server_ub_write(const char *str, uint str_length TSRMLS_DC) /* {{{ */
        !           423: {
        !           424:        php_cli_server_client *client = SG(server_context);
        !           425:        if (!client) {
        !           426:                return 0;
        !           427:        }
        !           428:        if (client->capturing) {
        !           429:                php_cli_server_chunk *chunk = php_cli_server_chunk_heap_new_self_contained(str_length);
        !           430:                if (!chunk) {
        !           431:                        zend_bailout();
        !           432:                }
        !           433:                memmove(chunk->data.heap.p, str, str_length);
        !           434:                php_cli_server_buffer_append(&client->capture_buffer, chunk);
        !           435:                return str_length;
        !           436:        } else {
        !           437:                return php_cli_server_client_send_through(client, str, str_length);
        !           438:        }
        !           439: } /* }}} */
        !           440: 
        !           441: static void sapi_cli_server_flush(void *server_context) /* {{{ */
        !           442: {
        !           443:        php_cli_server_client *client = server_context;
        !           444:        TSRMLS_FETCH();
        !           445: 
        !           446:        if (!client) {
        !           447:                return;
        !           448:        }
        !           449: 
        !           450:        if (client->sock < 0) {
        !           451:                php_handle_aborted_connection();
        !           452:                return;
        !           453:        }
        !           454: 
        !           455:        if (!SG(headers_sent)) {
        !           456:                sapi_send_headers(TSRMLS_C);
        !           457:                SG(headers_sent) = 1;
        !           458:        }
        !           459: } /* }}} */
        !           460: 
        !           461: static int sapi_cli_server_discard_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) /* {{{ */{
        !           462:        return SAPI_HEADER_SENT_SUCCESSFULLY;
        !           463: }
        !           464: /* }}} */
        !           465: 
        !           466: static int sapi_cli_server_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) /* {{{ */
        !           467: {
        !           468:        php_cli_server_client *client = SG(server_context);
        !           469:        smart_str buffer = { 0 };
        !           470:        sapi_header_struct *h;
        !           471:        zend_llist_position pos;
        !           472: 
        !           473:        if (client == NULL || client->capturing || SG(request_info).no_headers) {
        !           474:                return SAPI_HEADER_SENT_SUCCESSFULLY;
        !           475:        }
        !           476: 
        !           477:        if (SG(sapi_headers).http_status_line) {
        !           478:                smart_str_appends(&buffer, SG(sapi_headers).http_status_line);
        !           479:                smart_str_appendl(&buffer, "\r\n", 2);
        !           480:        } else {
        !           481:                append_http_status_line(&buffer, client->request.protocol_version, SG(sapi_headers).http_response_code, 0);
        !           482:        }
        !           483: 
        !           484:        append_essential_headers(&buffer, client, 0);
        !           485: 
        !           486:        h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
        !           487:        while (h) {
        !           488:                if (!h->header_len) {
        !           489:                        continue;
        !           490:                }
        !           491:                smart_str_appendl(&buffer, h->header, h->header_len);
        !           492:                smart_str_appendl(&buffer, "\r\n", 2);
        !           493:                h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
        !           494:        }
        !           495:        smart_str_appendl(&buffer, "\r\n", 2);
        !           496: 
        !           497:        php_cli_server_client_send_through(client, buffer.c, buffer.len);
        !           498: 
        !           499:        smart_str_free(&buffer);
        !           500:        return SAPI_HEADER_SENT_SUCCESSFULLY;
        !           501: }
        !           502: /* }}} */
        !           503: 
        !           504: static char *sapi_cli_server_read_cookies(TSRMLS_D) /* {{{ */
        !           505: {
        !           506:        php_cli_server_client *client = SG(server_context);
        !           507:        char **val;
        !           508:        if (FAILURE == zend_hash_find(&client->request.headers, "Cookie", sizeof("Cookie"), (void**)&val)) {
        !           509:                return NULL;
        !           510:        }
        !           511:        return *val;
        !           512: } /* }}} */
        !           513: 
        !           514: static int sapi_cli_server_read_post(char *buf, uint count_bytes TSRMLS_DC) /* {{{ */
        !           515: {
        !           516:        php_cli_server_client *client = SG(server_context);
        !           517:        if (client->request.content) {
        !           518:                size_t content_len = client->request.content_len;
        !           519:                size_t nbytes_copied = MIN(client->post_read_offset + count_bytes, content_len) - client->post_read_offset;
        !           520:                memmove(buf, client->request.content + client->post_read_offset, nbytes_copied);
        !           521:                client->post_read_offset += nbytes_copied;
        !           522:                return nbytes_copied;
        !           523:        }
        !           524:        return 0;
        !           525: } /* }}} */
        !           526: 
        !           527: static void sapi_cli_server_register_variable(zval *track_vars_array, const char *key, const char *val TSRMLS_DC) /* {{{ */
        !           528: {
        !           529:        char *new_val = (char *)val;
        !           530:        uint new_val_len;
        !           531:        if (sapi_module.input_filter(PARSE_SERVER, (char*)key, &new_val, strlen(val), &new_val_len TSRMLS_CC)) {
        !           532:                php_register_variable_safe((char *)key, new_val, new_val_len, track_vars_array TSRMLS_CC);
        !           533:        }
        !           534: } /* }}} */
        !           535: 
        !           536: static int sapi_cli_server_register_entry_cb(char **entry TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ { 
        !           537:        zval *track_vars_array = va_arg(args, zval *);
        !           538:        if (hash_key->nKeyLength) {
        !           539:                char *real_key, *key;
        !           540:                uint i;
        !           541:                key = estrndup(hash_key->arKey, hash_key->nKeyLength);
        !           542:                for(i=0; i<hash_key->nKeyLength; i++) {
        !           543:                        if (key[i] == '-') {
        !           544:                                key[i] = '_';
        !           545:                        } else {
        !           546:                                key[i] = toupper(key[i]);
        !           547:                        }
        !           548:                }
        !           549:                spprintf(&real_key, 0, "%s_%s", "HTTP", key);
        !           550:                sapi_cli_server_register_variable(track_vars_array, real_key, *entry TSRMLS_CC);
        !           551:                efree(key);
        !           552:                efree(real_key);
        !           553:        }
        !           554: 
        !           555:        return ZEND_HASH_APPLY_KEEP;
        !           556: }
        !           557: /* }}} */
        !           558: 
        !           559: static void sapi_cli_server_register_variables(zval *track_vars_array TSRMLS_DC) /* {{{ */
        !           560: {
        !           561:        php_cli_server_client *client = SG(server_context);
        !           562:        sapi_cli_server_register_variable(track_vars_array, "DOCUMENT_ROOT", client->server->document_root TSRMLS_CC);
        !           563:        {
        !           564:                char *tmp;
        !           565:                if ((tmp = strrchr(client->addr_str, ':'))) {
        !           566:                        char addr[64], port[8];
        !           567:                        strncpy(port, tmp + 1, 8);
        !           568:                        port[7] = '\0';
        !           569:                        strncpy(addr, client->addr_str, tmp - client->addr_str);
        !           570:                        addr[tmp - client->addr_str] = '\0';
        !           571:                        sapi_cli_server_register_variable(track_vars_array, "REMOTE_ADDR", addr TSRMLS_CC);
        !           572:                        sapi_cli_server_register_variable(track_vars_array, "REMOTE_PORT", port TSRMLS_CC);
        !           573:                } else {
        !           574:                        sapi_cli_server_register_variable(track_vars_array, "REMOTE_ADDR", client->addr_str TSRMLS_CC);
        !           575:                }
        !           576:        } 
        !           577:        {
        !           578:                char *tmp;
        !           579:                spprintf(&tmp, 0, "PHP %s Development Server", PHP_VERSION);
        !           580:                sapi_cli_server_register_variable(track_vars_array, "SERVER_SOFTWARE", tmp TSRMLS_CC);
        !           581:                efree(tmp);
        !           582:        }
        !           583:        {
        !           584:                char *tmp;
        !           585:                spprintf(&tmp, 0, "HTTP/%d.%d", client->request.protocol_version / 100, client->request.protocol_version % 100);
        !           586:                sapi_cli_server_register_variable(track_vars_array, "SERVER_PROTOCOL", tmp TSRMLS_CC);
        !           587:                efree(tmp);
        !           588:        }
        !           589:        sapi_cli_server_register_variable(track_vars_array, "SERVER_NAME", client->server->host TSRMLS_CC);
        !           590:        {
        !           591:                char *tmp;
        !           592:                spprintf(&tmp, 0, "%i",  client->server->port);
        !           593:                sapi_cli_server_register_variable(track_vars_array, "SERVER_PORT", tmp TSRMLS_CC);
        !           594:                efree(tmp);
        !           595:        }
        !           596: 
        !           597:        sapi_cli_server_register_variable(track_vars_array, "REQUEST_URI", client->request.request_uri TSRMLS_CC);
        !           598:        sapi_cli_server_register_variable(track_vars_array, "REQUEST_METHOD", SG(request_info).request_method TSRMLS_CC);
        !           599:        sapi_cli_server_register_variable(track_vars_array, "SCRIPT_NAME", client->request.vpath TSRMLS_CC);
        !           600:        if (SG(request_info).path_translated) {
        !           601:                sapi_cli_server_register_variable(track_vars_array, "SCRIPT_FILENAME", SG(request_info).path_translated TSRMLS_CC);
        !           602:        } else if (client->server->router) {
        !           603:                char *temp;
        !           604:                spprintf(&temp, 0, "%s/%s", client->server->document_root, client->server->router);
        !           605:                sapi_cli_server_register_variable(track_vars_array, "SCRIPT_FILENAME", temp TSRMLS_CC);
        !           606:                efree(temp);
        !           607:        }
        !           608:        if (client->request.path_info) {
        !           609:                sapi_cli_server_register_variable(track_vars_array, "PATH_INFO", client->request.path_info TSRMLS_CC);
        !           610:        }
        !           611:        if (client->request.path_info_len) {
        !           612:                char *tmp;
        !           613:                spprintf(&tmp, 0, "%s%s", client->request.vpath, client->request.path_info);
        !           614:                sapi_cli_server_register_variable(track_vars_array, "PHP_SELF", tmp TSRMLS_CC);
        !           615:                efree(tmp);
        !           616:        } else {
        !           617:                sapi_cli_server_register_variable(track_vars_array, "PHP_SELF", client->request.vpath TSRMLS_CC);
        !           618:        }
        !           619:        if (client->request.query_string) {
        !           620:                sapi_cli_server_register_variable(track_vars_array, "QUERY_STRING", client->request.query_string TSRMLS_CC);
        !           621:        }
        !           622:        zend_hash_apply_with_arguments(&client->request.headers TSRMLS_CC, (apply_func_args_t)sapi_cli_server_register_entry_cb, 1, track_vars_array);
        !           623: } /* }}} */
        !           624: 
        !           625: static void sapi_cli_server_log_message(char *msg TSRMLS_DC) /* {{{ */
        !           626: {
        !           627:        struct timeval tv;
        !           628:        struct tm tm;
        !           629:        char buf[52];
        !           630:        gettimeofday(&tv, NULL);
        !           631:        php_localtime_r(&tv.tv_sec, &tm);
        !           632:        php_asctime_r(&tm, buf);
        !           633:        {
        !           634:                size_t l = strlen(buf);
        !           635:                if (l > 0) {
        !           636:                        buf[l - 1] = '\0';
        !           637:                } else {
        !           638:                        memmove(buf, "unknown", sizeof("unknown"));
        !           639:                }
        !           640:        }
        !           641:        fprintf(stderr, "[%s] %s\n", buf, msg);
        !           642: } /* }}} */
        !           643: 
        !           644: /* {{{ sapi_module_struct cli_server_sapi_module
        !           645:  */
        !           646: sapi_module_struct cli_server_sapi_module = {
        !           647:        "cli-server",                                                   /* name */
        !           648:        "Built-in HTTP server",         /* pretty name */
        !           649: 
        !           650:        sapi_cli_server_startup,                                /* startup */
        !           651:        php_module_shutdown_wrapper,    /* shutdown */
        !           652: 
        !           653:        NULL,                                                   /* activate */
        !           654:        NULL,                                                   /* deactivate */
        !           655: 
        !           656:        sapi_cli_server_ub_write,               /* unbuffered write */
        !           657:        sapi_cli_server_flush,                  /* flush */
        !           658:        NULL,                                                   /* get uid */
        !           659:        NULL,                                                   /* getenv */
        !           660: 
        !           661:        php_error,                                              /* error handler */
        !           662: 
        !           663:        NULL,                                                   /* header handler */
        !           664:        sapi_cli_server_send_headers,   /* send headers handler */
        !           665:        NULL,                                                   /* send header handler */
        !           666: 
        !           667:        sapi_cli_server_read_post,              /* read POST data */
        !           668:        sapi_cli_server_read_cookies,   /* read Cookies */
        !           669: 
        !           670:        sapi_cli_server_register_variables,     /* register server variables */
        !           671:        sapi_cli_server_log_message,    /* Log message */
        !           672:        NULL,                                                   /* Get request time */
        !           673:        NULL,                                                   /* Child terminate */
        !           674:        
        !           675:        STANDARD_SAPI_MODULE_PROPERTIES
        !           676: }; /* }}} */
        !           677: 
        !           678: static int php_cli_server_poller_ctor(php_cli_server_poller *poller) /* {{{ */
        !           679: {
        !           680:        FD_ZERO(&poller->rfds);
        !           681:        FD_ZERO(&poller->wfds);
        !           682:        poller->max_fd = -1;
        !           683:        return SUCCESS;
        !           684: } /* }}} */
        !           685: 
        !           686: static void php_cli_server_poller_add(php_cli_server_poller *poller, int mode, int fd) /* {{{ */
        !           687: {
        !           688:        if (mode & POLLIN) {
        !           689:                PHP_SAFE_FD_SET(fd, &poller->rfds);
        !           690:        }
        !           691:        if (mode & POLLOUT) {
        !           692:                PHP_SAFE_FD_SET(fd, &poller->wfds);
        !           693:        }
        !           694:        if (fd > poller->max_fd) {
        !           695:                poller->max_fd = fd;
        !           696:        }
        !           697: } /* }}} */
        !           698: 
        !           699: static void php_cli_server_poller_remove(php_cli_server_poller *poller, int mode, int fd) /* {{{ */
        !           700: {
        !           701:        if (mode & POLLIN) {
        !           702:                PHP_SAFE_FD_CLR(fd, &poller->rfds);
        !           703:        }
        !           704:        if (mode & POLLOUT) {
        !           705:                PHP_SAFE_FD_CLR(fd, &poller->wfds);
        !           706:        }
        !           707: #ifndef PHP_WIN32
        !           708:        if (fd == poller->max_fd) {
        !           709:                while (fd > 0) {
        !           710:                        fd--;
        !           711:                        if (((unsigned int *)&poller->rfds)[fd / (8 * sizeof(unsigned int))] || ((unsigned int *)&poller->wfds)[fd / (8 * sizeof(unsigned int))]) {
        !           712:                                break;
        !           713:                        }
        !           714:                        fd -= fd % (8 * sizeof(unsigned int));
        !           715:                }
        !           716:                poller->max_fd = fd;
        !           717:        }
        !           718: #endif
        !           719: } /* }}} */
        !           720: 
        !           721: static int php_cli_server_poller_poll(php_cli_server_poller *poller, const struct timeval *tv) /* {{{ */
        !           722: {
        !           723:        memmove(&poller->active.rfds, &poller->rfds, sizeof(poller->rfds));
        !           724:        memmove(&poller->active.wfds, &poller->wfds, sizeof(poller->wfds));
        !           725:        return php_select(poller->max_fd + 1, &poller->active.rfds, &poller->active.wfds, NULL, (struct timeval *)tv);
        !           726: } /* }}} */
        !           727: 
        !           728: static int php_cli_server_poller_iter_on_active(php_cli_server_poller *poller, void *opaque, int(*callback)(void *, int fd, int events)) /* {{{ */
        !           729: {
        !           730:        int retval = SUCCESS;
        !           731: #ifdef PHP_WIN32
        !           732:        struct socket_entry {
        !           733:                SOCKET fd;
        !           734:                int events;
        !           735:        } entries[FD_SETSIZE * 2];
        !           736:        php_socket_t fd = 0;
        !           737:        size_t i;
        !           738:        struct socket_entry *n = entries, *m;
        !           739: 
        !           740:        for (i = 0; i < poller->active.rfds.fd_count; i++) {
        !           741:                n->events = POLLIN;
        !           742:                n->fd = poller->active.rfds.fd_array[i];
        !           743:                n++;
        !           744:        }
        !           745: 
        !           746:        m = n;
        !           747:        for (i = 0; i < poller->active.wfds.fd_count; i++) {
        !           748:                struct socket_entry *e;
        !           749:                SOCKET fd = poller->active.wfds.fd_array[i];
        !           750:                for (e = entries; e < m; e++) {
        !           751:                        if (e->fd == fd) {
        !           752:                                e->events |= POLLOUT;
        !           753:                        }
        !           754:                }
        !           755:                if (e == m) {
        !           756:                        assert(n < entries + FD_SETSIZE * 2);
        !           757:                        n->events = POLLOUT;
        !           758:                        n->fd = fd;
        !           759:                        n++;
        !           760:                }
        !           761:        }
        !           762: 
        !           763:        {
        !           764:                struct socket_entry *e = entries;
        !           765:                for (; e < n; e++) {
        !           766:                        if (SUCCESS != callback(opaque, e->fd, e->events)) {
        !           767:                                retval = FAILURE;
        !           768:                        }
        !           769:                }
        !           770:        }
        !           771:        
        !           772: #else
        !           773:        php_socket_t fd = 0;
        !           774:        const php_socket_t max_fd = poller->max_fd;
        !           775:        const unsigned int *pr = (unsigned int *)&poller->active.rfds,
        !           776:                           *pw = (unsigned int *)&poller->active.wfds,
        !           777:                           *e = pr + (max_fd + (8 * sizeof(unsigned int)) - 1) / (8 * sizeof(unsigned int));
        !           778:        unsigned int mask;
        !           779:        while (pr < e && fd <= max_fd) {
        !           780:                for (mask = 1; mask; mask <<= 1, fd++) {
        !           781:                        int events = (*pr & mask ? POLLIN: 0) | (*pw & mask ? POLLOUT: 0);
        !           782:                        if (events) {
        !           783:                                if (SUCCESS != callback(opaque, fd, events)) {
        !           784:                                        retval = FAILURE;
        !           785:                                }
        !           786:                        }
        !           787:                }
        !           788:                pr++;
        !           789:                pw++;
        !           790:        }
        !           791: #endif
        !           792:        return retval;
        !           793: } /* }}} */
        !           794: 
        !           795: static size_t php_cli_server_chunk_size(const php_cli_server_chunk *chunk) /* {{{ */
        !           796: {
        !           797:        switch (chunk->type) {
        !           798:        case PHP_CLI_SERVER_CHUNK_HEAP:
        !           799:                return chunk->data.heap.len;
        !           800:        case PHP_CLI_SERVER_CHUNK_IMMORTAL:
        !           801:                return chunk->data.immortal.len;
        !           802:        }
        !           803:        return 0;
        !           804: } /* }}} */
        !           805: 
        !           806: static void php_cli_server_chunk_dtor(php_cli_server_chunk *chunk) /* {{{ */
        !           807: {
        !           808:        switch (chunk->type) {
        !           809:        case PHP_CLI_SERVER_CHUNK_HEAP:
        !           810:                if (chunk->data.heap.block != chunk) {
        !           811:                        pefree(chunk->data.heap.block, 1);
        !           812:                }
        !           813:                break;
        !           814:        case PHP_CLI_SERVER_CHUNK_IMMORTAL:
        !           815:                break;
        !           816:        }
        !           817: } /* }}} */
        !           818: 
        !           819: static void php_cli_server_buffer_dtor(php_cli_server_buffer *buffer) /* {{{ */
        !           820: {
        !           821:        php_cli_server_chunk *chunk, *next;
        !           822:        for (chunk = buffer->first; chunk; chunk = next) {
        !           823:                next = chunk->next;
        !           824:                php_cli_server_chunk_dtor(chunk);
        !           825:                pefree(chunk, 1);
        !           826:        }
        !           827: } /* }}} */
        !           828: 
        !           829: static void php_cli_server_buffer_ctor(php_cli_server_buffer *buffer) /* {{{ */
        !           830: {
        !           831:        buffer->first = NULL;
        !           832:        buffer->last = NULL;
        !           833: } /* }}} */
        !           834: 
        !           835: static void php_cli_server_buffer_append(php_cli_server_buffer *buffer, php_cli_server_chunk *chunk) /* {{{ */
        !           836: {
        !           837:        php_cli_server_chunk *last;
        !           838:        for (last = chunk; last->next; last = last->next);
        !           839:        if (!buffer->last) {
        !           840:                buffer->first = chunk;
        !           841:        } else {
        !           842:                buffer->last->next = chunk;
        !           843:        }
        !           844:        buffer->last = last;
        !           845: } /* }}} */
        !           846: 
        !           847: static void php_cli_server_buffer_prepend(php_cli_server_buffer *buffer, php_cli_server_chunk *chunk) /* {{{ */
        !           848: {
        !           849:        php_cli_server_chunk *last;
        !           850:        for (last = chunk; last->next; last = last->next);
        !           851:        last->next = buffer->first;
        !           852:        if (!buffer->last) {
        !           853:                buffer->last = last;
        !           854:        }
        !           855:        buffer->first = chunk;
        !           856: } /* }}} */
        !           857: 
        !           858: static size_t php_cli_server_buffer_size(const php_cli_server_buffer *buffer) /* {{{ */
        !           859: {
        !           860:        php_cli_server_chunk *chunk;
        !           861:        size_t retval = 0;
        !           862:        for (chunk = buffer->first; chunk; chunk = chunk->next) {
        !           863:                retval += php_cli_server_chunk_size(chunk);
        !           864:        }
        !           865:        return retval;
        !           866: } /* }}} */
        !           867: 
        !           868: static php_cli_server_chunk *php_cli_server_chunk_immortal_new(const char *buf, size_t len) /* {{{ */
        !           869: {
        !           870:        php_cli_server_chunk *chunk = pemalloc(sizeof(php_cli_server_chunk), 1);
        !           871:        if (!chunk) {
        !           872:                return NULL;
        !           873:        }
        !           874: 
        !           875:        chunk->type = PHP_CLI_SERVER_CHUNK_IMMORTAL;
        !           876:        chunk->next = NULL;
        !           877:        chunk->data.immortal.p = buf;
        !           878:        chunk->data.immortal.len = len;
        !           879:        return chunk;
        !           880: } /* }}} */
        !           881: 
        !           882: static php_cli_server_chunk *php_cli_server_chunk_heap_new(char *block, char *buf, size_t len) /* {{{ */
        !           883: {
        !           884:        php_cli_server_chunk *chunk = pemalloc(sizeof(php_cli_server_chunk), 1);
        !           885:        if (!chunk) {
        !           886:                return NULL;
        !           887:        }
        !           888: 
        !           889:        chunk->type = PHP_CLI_SERVER_CHUNK_HEAP;
        !           890:        chunk->next = NULL;
        !           891:        chunk->data.heap.block = block;
        !           892:        chunk->data.heap.p = buf;
        !           893:        chunk->data.heap.len = len;
        !           894:        return chunk;
        !           895: } /* }}} */
        !           896: 
        !           897: static php_cli_server_chunk *php_cli_server_chunk_heap_new_self_contained(size_t len) /* {{{ */
        !           898: {
        !           899:        php_cli_server_chunk *chunk = pemalloc(sizeof(php_cli_server_chunk) + len, 1);
        !           900:        if (!chunk) {
        !           901:                return NULL;
        !           902:        }
        !           903: 
        !           904:        chunk->type = PHP_CLI_SERVER_CHUNK_HEAP;
        !           905:        chunk->next = NULL;
        !           906:        chunk->data.heap.block = chunk;
        !           907:        chunk->data.heap.p = (char *)(chunk + 1);
        !           908:        chunk->data.heap.len = len;
        !           909:        return chunk;
        !           910: } /* }}} */
        !           911: 
        !           912: static void php_cli_server_content_sender_dtor(php_cli_server_content_sender *sender) /* {{{ */
        !           913: {
        !           914:        php_cli_server_buffer_dtor(&sender->buffer);
        !           915: } /* }}} */
        !           916: 
        !           917: static void php_cli_server_content_sender_ctor(php_cli_server_content_sender *sender) /* {{{ */
        !           918: {
        !           919:        php_cli_server_buffer_ctor(&sender->buffer);
        !           920: } /* }}} */
        !           921: 
        !           922: static int php_cli_server_content_sender_send(php_cli_server_content_sender *sender, php_socket_t fd, size_t *nbytes_sent_total) /* {{{ */
        !           923: {
        !           924:        php_cli_server_chunk *chunk, *next;
        !           925:        size_t _nbytes_sent_total = 0;
        !           926: 
        !           927:        for (chunk = sender->buffer.first; chunk; chunk = next) {
        !           928:                ssize_t nbytes_sent;
        !           929:                next = chunk->next;
        !           930: 
        !           931:                switch (chunk->type) {
        !           932:                case PHP_CLI_SERVER_CHUNK_HEAP:
        !           933:                        nbytes_sent = send(fd, chunk->data.heap.p, chunk->data.heap.len, 0);
        !           934:                        if (nbytes_sent < 0) {
        !           935:                                *nbytes_sent_total = _nbytes_sent_total;
        !           936:                                return php_socket_errno();
        !           937:                        } else if (nbytes_sent == chunk->data.heap.len) {
        !           938:                                php_cli_server_chunk_dtor(chunk);
        !           939:                                pefree(chunk, 1);
        !           940:                                sender->buffer.first = next;
        !           941:                                if (!next) {
        !           942:                                        sender->buffer.last = NULL;
        !           943:                                }
        !           944:                        } else {
        !           945:                                chunk->data.heap.p += nbytes_sent;
        !           946:                                chunk->data.heap.len -= nbytes_sent;
        !           947:                        }
        !           948:                        _nbytes_sent_total += nbytes_sent;
        !           949:                        break;
        !           950: 
        !           951:                case PHP_CLI_SERVER_CHUNK_IMMORTAL:
        !           952:                        nbytes_sent = send(fd, chunk->data.immortal.p, chunk->data.immortal.len, 0);
        !           953:                        if (nbytes_sent < 0) {
        !           954:                                *nbytes_sent_total = _nbytes_sent_total;
        !           955:                                return php_socket_errno();
        !           956:                        } else if (nbytes_sent == chunk->data.immortal.len) {
        !           957:                                php_cli_server_chunk_dtor(chunk);
        !           958:                                pefree(chunk, 1);
        !           959:                                sender->buffer.first = next; 
        !           960:                                if (!next) {
        !           961:                                        sender->buffer.last = NULL;
        !           962:                                }
        !           963:                        } else {
        !           964:                                chunk->data.immortal.p += nbytes_sent;
        !           965:                                chunk->data.immortal.len -= nbytes_sent;
        !           966:                        }
        !           967:                        _nbytes_sent_total += nbytes_sent;
        !           968:                        break;
        !           969:                }
        !           970:        }
        !           971:        *nbytes_sent_total = _nbytes_sent_total;
        !           972:        return 0;
        !           973: } /* }}} */
        !           974: 
        !           975: static int php_cli_server_content_sender_pull(php_cli_server_content_sender *sender, int fd, size_t *nbytes_read) /* {{{ */
        !           976: {
        !           977:        ssize_t _nbytes_read;
        !           978:        php_cli_server_chunk *chunk = php_cli_server_chunk_heap_new_self_contained(131072);
        !           979: 
        !           980:        _nbytes_read = read(fd, chunk->data.heap.p, chunk->data.heap.len);
        !           981:        if (_nbytes_read < 0) {
        !           982:                char *errstr = get_last_error();
        !           983:                TSRMLS_FETCH();
        !           984:                php_cli_server_logf("%s" TSRMLS_CC, errstr);
        !           985:                pefree(errstr, 1);
        !           986:                php_cli_server_chunk_dtor(chunk);
        !           987:                pefree(chunk, 1);
        !           988:                return 1;
        !           989:        }
        !           990:        chunk->data.heap.len = _nbytes_read;
        !           991:        php_cli_server_buffer_append(&sender->buffer, chunk);
        !           992:        *nbytes_read = _nbytes_read;
        !           993:        return 0;
        !           994: } /* }}} */
        !           995: 
        !           996: #if HAVE_UNISTD_H
        !           997: static int php_cli_is_output_tty() /* {{{ */
        !           998: {
        !           999:        if (php_cli_output_is_tty == OUTPUT_NOT_CHECKED) {
        !          1000:                php_cli_output_is_tty = isatty(STDOUT_FILENO);
        !          1001:        }
        !          1002:        return php_cli_output_is_tty;
        !          1003: } /* }}} */
        !          1004: #endif
        !          1005: 
        !          1006: static void php_cli_server_log_response(php_cli_server_client *client, int status, const char *message TSRMLS_DC) /* {{{ */
        !          1007: {
        !          1008:        int color = 0, effective_status = status;
        !          1009:        char *basic_buf, *message_buf = "", *error_buf = "";
        !          1010:        zend_bool append_error_message = 0;
        !          1011: 
        !          1012:        if (PG(last_error_message)) {
        !          1013:                switch (PG(last_error_type)) {
        !          1014:                        case E_ERROR:
        !          1015:                        case E_CORE_ERROR:
        !          1016:                        case E_COMPILE_ERROR:
        !          1017:                        case E_USER_ERROR:
        !          1018:                        case E_PARSE:
        !          1019:                                if (status == 200) {
        !          1020:                                        /* the status code isn't changed by a fatal error, so fake it */
        !          1021:                                        effective_status = 500;
        !          1022:                                }
        !          1023: 
        !          1024:                                append_error_message = 1;
        !          1025:                                break;
        !          1026:                }
        !          1027:        }
        !          1028: 
        !          1029: #if HAVE_UNISTD_H
        !          1030:        if (CLI_SERVER_G(color) && php_cli_is_output_tty() == OUTPUT_IS_TTY) {
        !          1031:                if (effective_status >= 500) {
        !          1032:                        /* server error: red */
        !          1033:                        color = 1;
        !          1034:                } else if (effective_status >= 400) {
        !          1035:                        /* client error: yellow */
        !          1036:                        color = 3;
        !          1037:                } else if (effective_status >= 200) {
        !          1038:                        /* success: green */
        !          1039:                        color = 2;
        !          1040:                }
        !          1041:        }
        !          1042: #endif
        !          1043: 
        !          1044:        /* basic */
        !          1045:        spprintf(&basic_buf, 0, "%s [%d]: %s", client->addr_str, status, client->request.request_uri);
        !          1046:        if (!basic_buf) {
        !          1047:                return;
        !          1048:        }
        !          1049: 
        !          1050:        /* message */
        !          1051:        if (message) {
        !          1052:                spprintf(&message_buf, 0, " - %s", message);
        !          1053:                if (!message_buf) {
        !          1054:                        efree(basic_buf);
        !          1055:                        return;
        !          1056:                }
        !          1057:        }
        !          1058: 
        !          1059:        /* error */
        !          1060:        if (append_error_message) {
        !          1061:                spprintf(&error_buf, 0, " - %s in %s on line %d", PG(last_error_message), PG(last_error_file), PG(last_error_lineno));
        !          1062:                if (!error_buf) {
        !          1063:                        efree(basic_buf);
        !          1064:                        if (message) {
        !          1065:                                efree(message_buf);
        !          1066:                        }
        !          1067:                        return;
        !          1068:                }
        !          1069:        }
        !          1070: 
        !          1071:        if (color) {
        !          1072:                php_cli_server_logf("\x1b[3%dm%s%s%s\x1b[0m" TSRMLS_CC, color, basic_buf, message_buf, error_buf);
        !          1073:        } else {
        !          1074:                php_cli_server_logf("%s%s%s" TSRMLS_CC, basic_buf, message_buf, error_buf);
        !          1075:        }
        !          1076: 
        !          1077:        efree(basic_buf);
        !          1078:        if (message) {
        !          1079:                efree(message_buf);
        !          1080:        }
        !          1081:        if (append_error_message) {
        !          1082:                efree(error_buf);
        !          1083:        }
        !          1084: } /* }}} */
        !          1085: 
        !          1086: static void php_cli_server_logf(const char *format TSRMLS_DC, ...) /* {{{ */
        !          1087: {
        !          1088:        char *buf = NULL;
        !          1089:        va_list ap;
        !          1090: #ifdef ZTS
        !          1091:        va_start(ap, tsrm_ls);
        !          1092: #else
        !          1093:        va_start(ap, format);
        !          1094: #endif
        !          1095:        vspprintf(&buf, 0, format, ap);
        !          1096:        va_end(ap);
        !          1097: 
        !          1098:        if (!buf) {
        !          1099:                return;
        !          1100:        }
        !          1101: 
        !          1102:        if (sapi_module.log_message) {
        !          1103:                sapi_module.log_message(buf TSRMLS_CC);
        !          1104:        }
        !          1105: 
        !          1106:        efree(buf);
        !          1107: } /* }}} */
        !          1108: 
        !          1109: static int php_network_listen_socket(const char *host, int *port, int socktype, int *af, socklen_t *socklen, char **errstr TSRMLS_DC) /* {{{ */
        !          1110: {
        !          1111:        int retval = SOCK_ERR;
        !          1112:        int err = 0;
        !          1113:        struct sockaddr *sa = NULL, **p, **sal;
        !          1114: 
        !          1115:        int num_addrs = php_network_getaddresses(host, socktype, &sal, errstr TSRMLS_CC);
        !          1116:        if (num_addrs == 0) {
        !          1117:                return -1;
        !          1118:        }
        !          1119:        for (p = sal; *p; p++) {
        !          1120:                if (sa) {
        !          1121:                        pefree(sa, 1);
        !          1122:                        sa = NULL;
        !          1123:                }
        !          1124: 
        !          1125:                retval = socket((*p)->sa_family, socktype, 0);
        !          1126:                if (retval == SOCK_ERR) {
        !          1127:                        continue;
        !          1128:                }
        !          1129: 
        !          1130:                switch ((*p)->sa_family) {
        !          1131: #if HAVE_GETADDRINFO && HAVE_IPV6
        !          1132:                case AF_INET6:
        !          1133:                        sa = pemalloc(sizeof(struct sockaddr_in6), 1);
        !          1134:                        if (!sa) {
        !          1135:                                closesocket(retval);
        !          1136:                                retval = SOCK_ERR;
        !          1137:                                *errstr = NULL;
        !          1138:                                goto out;
        !          1139:                        }
        !          1140:                        *(struct sockaddr_in6 *)sa = *(struct sockaddr_in6 *)*p;
        !          1141:                        ((struct sockaddr_in6 *)sa)->sin6_port = htons(*port);
        !          1142:                        *socklen = sizeof(struct sockaddr_in6);
        !          1143:                        break;
        !          1144: #endif
        !          1145:                case AF_INET:
        !          1146:                        sa = pemalloc(sizeof(struct sockaddr_in), 1);
        !          1147:                        if (!sa) {
        !          1148:                                closesocket(retval);
        !          1149:                                retval = SOCK_ERR;
        !          1150:                                *errstr = NULL;
        !          1151:                                goto out;
        !          1152:                        }
        !          1153:                        *(struct sockaddr_in *)sa = *(struct sockaddr_in *)*p;
        !          1154:                        ((struct sockaddr_in *)sa)->sin_port = htons(*port);
        !          1155:                        *socklen = sizeof(struct sockaddr_in);
        !          1156:                        break;
        !          1157:                default:
        !          1158:                        /* Unknown family */
        !          1159:                        *socklen = 0;
        !          1160:                        closesocket(retval);
        !          1161:                        continue;
        !          1162:                }
        !          1163: 
        !          1164: #ifdef SO_REUSEADDR
        !          1165:                {
        !          1166:                        int val = 1;
        !          1167:                        setsockopt(retval, SOL_SOCKET, SO_REUSEADDR, (char*)&val, sizeof(val));
        !          1168:                }
        !          1169: #endif
        !          1170: 
        !          1171:                if (bind(retval, sa, *socklen) == SOCK_CONN_ERR) {
        !          1172:                        err = php_socket_errno();
        !          1173:                        if (err == SOCK_EINVAL || err == SOCK_EADDRINUSE) {
        !          1174:                                goto out;
        !          1175:                        }
        !          1176:                        closesocket(retval);
        !          1177:                        retval = SOCK_ERR;
        !          1178:                        continue;
        !          1179:                }
        !          1180:                err = 0;
        !          1181: 
        !          1182:                *af = sa->sa_family;
        !          1183:                if (*port == 0) {
        !          1184:                        if (getsockname(retval, sa, socklen)) {
        !          1185:                                err = php_socket_errno();
        !          1186:                                goto out;
        !          1187:                        }
        !          1188:                        switch (sa->sa_family) {
        !          1189: #if HAVE_GETADDRINFO && HAVE_IPV6
        !          1190:                        case AF_INET6:
        !          1191:                                *port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
        !          1192:                                break;
        !          1193: #endif
        !          1194:                        case AF_INET:
        !          1195:                                *port = ntohs(((struct sockaddr_in *)sa)->sin_port);
        !          1196:                                break;
        !          1197:                        }
        !          1198:                }
        !          1199: 
        !          1200:                break;
        !          1201:        }
        !          1202: 
        !          1203:        if (retval == SOCK_ERR) {
        !          1204:                goto out;
        !          1205:        }
        !          1206: 
        !          1207:        if (listen(retval, SOMAXCONN)) {
        !          1208:                err = php_socket_errno();
        !          1209:                goto out;
        !          1210:        }
        !          1211: 
        !          1212: out:
        !          1213:        if (sa) {
        !          1214:                pefree(sa, 1);
        !          1215:        }
        !          1216:        if (sal) {
        !          1217:                php_network_freeaddresses(sal);
        !          1218:        }
        !          1219:        if (err) {
        !          1220:                if (retval >= 0) {
        !          1221:                        closesocket(retval);
        !          1222:                }
        !          1223:                if (errstr) {
        !          1224:                        *errstr = php_socket_strerror(err, NULL, 0);
        !          1225:                }
        !          1226:                return SOCK_ERR;
        !          1227:        }
        !          1228:        return retval;
        !          1229: } /* }}} */
        !          1230: 
        !          1231: static int php_cli_server_request_ctor(php_cli_server_request *req) /* {{{ */
        !          1232: {
        !          1233:        req->protocol_version = 0;
        !          1234:        req->request_uri = NULL;
        !          1235:        req->request_uri_len = 0;
        !          1236:        req->vpath = NULL;
        !          1237:        req->vpath_len = 0;
        !          1238:        req->path_translated = NULL;
        !          1239:        req->path_translated_len = 0;
        !          1240:        req->path_info = NULL;
        !          1241:        req->path_info_len = 0;
        !          1242:        req->query_string = NULL;
        !          1243:        req->query_string_len = 0;
        !          1244:        zend_hash_init(&req->headers, 0, NULL, (void(*)(void*))char_ptr_dtor_p, 1);
        !          1245:        req->content = NULL;
        !          1246:        req->content_len = 0;
        !          1247:        req->ext = NULL;
        !          1248:        req->ext_len = 0;
        !          1249:        return SUCCESS;
        !          1250: } /* }}} */
        !          1251: 
        !          1252: static void php_cli_server_request_dtor(php_cli_server_request *req) /* {{{ */
        !          1253: {
        !          1254:        if (req->request_uri) {
        !          1255:                pefree(req->request_uri, 1);
        !          1256:        }
        !          1257:        if (req->vpath) {
        !          1258:                pefree(req->vpath, 1);
        !          1259:        }
        !          1260:        if (req->path_translated) {
        !          1261:                pefree(req->path_translated, 1);
        !          1262:        }
        !          1263:        if (req->path_info) {
        !          1264:                pefree(req->path_info, 1);
        !          1265:        }
        !          1266:        if (req->query_string) {
        !          1267:                pefree(req->query_string, 1);
        !          1268:        }
        !          1269:        zend_hash_destroy(&req->headers);
        !          1270:        if (req->content) {
        !          1271:                pefree(req->content, 1);
        !          1272:        }
        !          1273: } /* }}} */
        !          1274: 
        !          1275: static void php_cli_server_request_translate_vpath(php_cli_server_request *request, const char *document_root, size_t document_root_len) /* {{{ */
        !          1276: {
        !          1277:        struct stat sb;
        !          1278:        static const char *index_files[] = { "index.php", "index.html", NULL };
        !          1279:        char *buf = safe_pemalloc(1, request->vpath_len, 1 + document_root_len + 1 + sizeof("index.html"), 1);
        !          1280:        char *p = buf, *prev_patch = 0, *q, *vpath;
        !          1281:        size_t prev_patch_len;
        !          1282:        int  is_static_file = 0;
        !          1283: 
        !          1284:        if (!buf) {
        !          1285:                return;
        !          1286:        }
        !          1287: 
        !          1288:        memmove(p, document_root, document_root_len);
        !          1289:        p += document_root_len;
        !          1290:        vpath = p;
        !          1291:        if (request->vpath_len > 0 && request->vpath[0] != '/') {
        !          1292:                *p++ = DEFAULT_SLASH;
        !          1293:        }
        !          1294:        q = request->vpath + request->vpath_len;
        !          1295:        while (q > request->vpath) {
        !          1296:                if (*q-- == '.') {
        !          1297:                        is_static_file = 1;
        !          1298:                        break;
        !          1299:                }
        !          1300:        }
        !          1301:        memmove(p, request->vpath, request->vpath_len);
        !          1302: #ifdef PHP_WIN32
        !          1303:        q = p + request->vpath_len;
        !          1304:        do {
        !          1305:                if (*q == '/') {
        !          1306:                        *q = '\\';
        !          1307:                }
        !          1308:        } while (q-- > p);
        !          1309: #endif
        !          1310:        p += request->vpath_len;
        !          1311:        *p = '\0';
        !          1312:        q = p;
        !          1313:        while (q > buf) {
        !          1314:                if (!stat(buf, &sb)) {
        !          1315:                        if (sb.st_mode & S_IFDIR) {
        !          1316:                                const char **file = index_files;
        !          1317:                                if (q[-1] != DEFAULT_SLASH) {
        !          1318:                                        *q++ = DEFAULT_SLASH;
        !          1319:                                }
        !          1320:                                while (*file) {
        !          1321:                                        size_t l = strlen(*file);
        !          1322:                                        memmove(q, *file, l + 1);
        !          1323:                                        if (!stat(buf, &sb) && (sb.st_mode & S_IFREG)) {
        !          1324:                                                q += l;
        !          1325:                                                break;
        !          1326:                                        }
        !          1327:                                        file++;
        !          1328:                                }
        !          1329:                                if (!*file || is_static_file) {
        !          1330:                                        if (prev_patch) {
        !          1331:                                                pefree(prev_patch, 1);
        !          1332:                                        }
        !          1333:                                        pefree(buf, 1);
        !          1334:                                        return;
        !          1335:                                }
        !          1336:                        }
        !          1337:                        break; /* regular file */
        !          1338:                } 
        !          1339:                if (prev_patch) {
        !          1340:                        pefree(prev_patch, 1);
        !          1341:                        *q = DEFAULT_SLASH;
        !          1342:                }
        !          1343:                while (q > buf && *(--q) != DEFAULT_SLASH);
        !          1344:                prev_patch_len = p - q;
        !          1345:                prev_patch = pestrndup(q, prev_patch_len, 1);
        !          1346:                *q = '\0';
        !          1347:        }
        !          1348:        if (prev_patch) {
        !          1349:                request->path_info_len = prev_patch_len;
        !          1350: #ifdef PHP_WIN32
        !          1351:                while (prev_patch_len--) {
        !          1352:                        if (prev_patch[prev_patch_len] == '\\') {
        !          1353:                                prev_patch[prev_patch_len] = '/';
        !          1354:                        }
        !          1355:                }
        !          1356: #endif
        !          1357:                request->path_info = prev_patch;
        !          1358:                pefree(request->vpath, 1);
        !          1359:                request->vpath = pestrndup(vpath, q - vpath, 1);
        !          1360:                request->vpath_len = q - vpath;
        !          1361:                request->path_translated = buf;
        !          1362:                request->path_translated_len = q - buf;
        !          1363:        } else {
        !          1364:                pefree(request->vpath, 1);
        !          1365:                request->vpath = pestrndup(vpath, q - vpath, 1);
        !          1366:                request->vpath_len = q - vpath;
        !          1367:                request->path_translated = buf;
        !          1368:                request->path_translated_len = q - buf;
        !          1369:        }
        !          1370: #ifdef PHP_WIN32
        !          1371:        {
        !          1372:                uint i = 0;
        !          1373:                for (;i<request->vpath_len;i++) {
        !          1374:                        if (request->vpath[i] == '\\') {
        !          1375:                                request->vpath[i] = '/';
        !          1376:                        }
        !          1377:                }       
        !          1378:        }
        !          1379: #endif
        !          1380:        request->sb = sb;
        !          1381: } /* }}} */
        !          1382: 
        !          1383: static void normalize_vpath(char **retval, size_t *retval_len, const char *vpath, size_t vpath_len, int persistent) /* {{{ */
        !          1384: {
        !          1385:        char *decoded_vpath = NULL;
        !          1386:        char *decoded_vpath_end;
        !          1387:        char *p;
        !          1388: 
        !          1389:        *retval = NULL;
        !          1390: 
        !          1391:        decoded_vpath = pestrndup(vpath, vpath_len, persistent);
        !          1392:        if (!decoded_vpath) {
        !          1393:                return;
        !          1394:        }
        !          1395: 
        !          1396:        decoded_vpath_end = decoded_vpath + php_url_decode(decoded_vpath, vpath_len);
        !          1397: 
        !          1398:        p = decoded_vpath;
        !          1399: 
        !          1400:        if (p < decoded_vpath_end && *p == '/') {
        !          1401:                char *n = p;
        !          1402:                while (n < decoded_vpath_end && *n == '/') n++;
        !          1403:                memmove(++p, n, decoded_vpath_end - n);
        !          1404:                decoded_vpath_end -= n - p;
        !          1405:        }
        !          1406: 
        !          1407:        while (p < decoded_vpath_end) {
        !          1408:                char *n = p;
        !          1409:                while (n < decoded_vpath_end && *n != '/') n++;
        !          1410:                if (n - p == 2 && p[0] == '.' && p[1] == '.') {
        !          1411:                        if (p > decoded_vpath) {
        !          1412:                                --p;
        !          1413:                                for (;;) {
        !          1414:                                        if (p == decoded_vpath) {
        !          1415:                                                if (*p == '/') {
        !          1416:                                                        p++;
        !          1417:                                                }
        !          1418:                                                break;
        !          1419:                                        }
        !          1420:                                        if (*(--p) == '/') {
        !          1421:                                                p++;
        !          1422:                                                break;
        !          1423:                                        }
        !          1424:                                }
        !          1425:                        }
        !          1426:                        while (n < decoded_vpath_end && *n == '/') n++;
        !          1427:                        memmove(p, n, decoded_vpath_end - n);
        !          1428:                        decoded_vpath_end -= n - p;
        !          1429:                } else if (n - p == 1 && p[0] == '.') {
        !          1430:                        while (n < decoded_vpath_end && *n == '/') n++;
        !          1431:                        memmove(p, n, decoded_vpath_end - n);
        !          1432:                        decoded_vpath_end -= n - p;
        !          1433:                } else {
        !          1434:                        if (n < decoded_vpath_end) {
        !          1435:                                char *nn = n;
        !          1436:                                while (nn < decoded_vpath_end && *nn == '/') nn++;
        !          1437:                                p = n + 1;
        !          1438:                                memmove(p, nn, decoded_vpath_end - nn);
        !          1439:                                decoded_vpath_end -= nn - p;
        !          1440:                        } else {
        !          1441:                                p = n;
        !          1442:                        }
        !          1443:                }
        !          1444:        }
        !          1445:        
        !          1446:        *decoded_vpath_end = '\0';
        !          1447:        *retval = decoded_vpath;
        !          1448:        *retval_len = decoded_vpath_end - decoded_vpath;
        !          1449: } /* }}} */
        !          1450: 
        !          1451: /* {{{ php_cli_server_client_read_request */
        !          1452: static int php_cli_server_client_read_request_on_message_begin(php_http_parser *parser)
        !          1453: {
        !          1454:        return 0;
        !          1455: }
        !          1456: 
        !          1457: static int php_cli_server_client_read_request_on_path(php_http_parser *parser, const char *at, size_t length)
        !          1458: {
        !          1459:        php_cli_server_client *client = parser->data;
        !          1460:        {
        !          1461:                char *vpath;
        !          1462:                size_t vpath_len;
        !          1463:                normalize_vpath(&vpath, &vpath_len, at, length, 1);
        !          1464:                client->request.vpath = vpath;
        !          1465:                client->request.vpath_len = vpath_len;
        !          1466:        }
        !          1467:        return 0;
        !          1468: }
        !          1469: 
        !          1470: static int php_cli_server_client_read_request_on_query_string(php_http_parser *parser, const char *at, size_t length)
        !          1471: {
        !          1472:        php_cli_server_client *client = parser->data;
        !          1473:        client->request.query_string = pestrndup(at, length, 1);
        !          1474:        client->request.query_string_len = length;
        !          1475:        return 0;
        !          1476: }
        !          1477: 
        !          1478: static int php_cli_server_client_read_request_on_url(php_http_parser *parser, const char *at, size_t length)
        !          1479: {
        !          1480:        php_cli_server_client *client = parser->data;
        !          1481:        client->request.request_method = parser->method;
        !          1482:        client->request.request_uri = pestrndup(at, length, 1);
        !          1483:        client->request.request_uri_len = length;
        !          1484:        return 0;
        !          1485: }
        !          1486: 
        !          1487: static int php_cli_server_client_read_request_on_fragment(php_http_parser *parser, const char *at, size_t length)
        !          1488: {
        !          1489:        return 0;
        !          1490: }
        !          1491: 
        !          1492: static int php_cli_server_client_read_request_on_header_field(php_http_parser *parser, const char *at, size_t length)
        !          1493: {
        !          1494:        php_cli_server_client *client = parser->data;
        !          1495:        if (client->current_header_name_allocated) {
        !          1496:                pefree(client->current_header_name, 1);
        !          1497:                client->current_header_name_allocated = 0;
        !          1498:        }
        !          1499:        client->current_header_name = (char *)at;
        !          1500:        client->current_header_name_len = length;
        !          1501:        return 0;
        !          1502: }
        !          1503: 
        !          1504: static int php_cli_server_client_read_request_on_header_value(php_http_parser *parser, const char *at, size_t length)
        !          1505: {
        !          1506:        php_cli_server_client *client = parser->data;
        !          1507:        char *value = pestrndup(at, length, 1);
        !          1508:        if (!value) {
        !          1509:                return 1;
        !          1510:        }
        !          1511:        {
        !          1512:                char *header_name = client->current_header_name;
        !          1513:                size_t header_name_len = client->current_header_name_len;
        !          1514:                char c = header_name[header_name_len];
        !          1515:                header_name[header_name_len] = '\0';
        !          1516:                zend_hash_add(&client->request.headers, header_name, header_name_len + 1, &value, sizeof(char *), NULL);
        !          1517:                header_name[header_name_len] = c;
        !          1518:        }
        !          1519: 
        !          1520:        if (client->current_header_name_allocated) {
        !          1521:                pefree(client->current_header_name, 1);
        !          1522:                client->current_header_name_allocated = 0;
        !          1523:        }
        !          1524:        return 0;
        !          1525: }
        !          1526: 
        !          1527: static int php_cli_server_client_read_request_on_headers_complete(php_http_parser *parser)
        !          1528: {
        !          1529:        php_cli_server_client *client = parser->data;
        !          1530:        if (client->current_header_name_allocated) {
        !          1531:                pefree(client->current_header_name, 1);
        !          1532:                client->current_header_name_allocated = 0;
        !          1533:        }
        !          1534:        client->current_header_name = NULL;
        !          1535:        return 0;
        !          1536: }
        !          1537: 
        !          1538: static int php_cli_server_client_read_request_on_body(php_http_parser *parser, const char *at, size_t length)
        !          1539: {
        !          1540:        php_cli_server_client *client = parser->data;
        !          1541:        if (!client->request.content) {
        !          1542:                client->request.content = pemalloc(parser->content_length, 1);
        !          1543:                if (!client->request.content) {
        !          1544:                        return -1;
        !          1545:                }
        !          1546:                client->request.content_len = 0;
        !          1547:        }
        !          1548:        memmove(client->request.content + client->request.content_len, at, length);
        !          1549:        client->request.content_len += length;
        !          1550:        return 0;
        !          1551: }
        !          1552: 
        !          1553: static int php_cli_server_client_read_request_on_message_complete(php_http_parser *parser)
        !          1554: {
        !          1555:        php_cli_server_client *client = parser->data;
        !          1556:        client->request.protocol_version = parser->http_major * 100 + parser->http_minor;
        !          1557:        php_cli_server_request_translate_vpath(&client->request, client->server->document_root, client->server->document_root_len);
        !          1558:        {
        !          1559:                const char *vpath = client->request.vpath, *end = vpath + client->request.vpath_len, *p = end;
        !          1560:                client->request.ext = end;
        !          1561:                client->request.ext_len = 0;
        !          1562:                while (p > vpath) {
        !          1563:                        --p;
        !          1564:                        if (*p == '.') {
        !          1565:                                ++p;
        !          1566:                                client->request.ext = p;
        !          1567:                                client->request.ext_len = end - p;
        !          1568:                                break;
        !          1569:                        }
        !          1570:                }
        !          1571:        }
        !          1572:        client->request_read = 1;
        !          1573:        return 0;
        !          1574: }
        !          1575: 
        !          1576: static int php_cli_server_client_read_request(php_cli_server_client *client, char **errstr TSRMLS_DC)
        !          1577: {
        !          1578:        char buf[16384];
        !          1579:        static const php_http_parser_settings settings = {
        !          1580:                php_cli_server_client_read_request_on_message_begin,
        !          1581:                php_cli_server_client_read_request_on_path,
        !          1582:                php_cli_server_client_read_request_on_query_string,
        !          1583:                php_cli_server_client_read_request_on_url,
        !          1584:                php_cli_server_client_read_request_on_fragment,
        !          1585:                php_cli_server_client_read_request_on_header_field,
        !          1586:                php_cli_server_client_read_request_on_header_value,
        !          1587:                php_cli_server_client_read_request_on_headers_complete,
        !          1588:                php_cli_server_client_read_request_on_body,
        !          1589:                php_cli_server_client_read_request_on_message_complete
        !          1590:        };
        !          1591:        size_t nbytes_consumed;
        !          1592:        int nbytes_read;
        !          1593:        if (client->request_read) {
        !          1594:                return 1;
        !          1595:        }
        !          1596:        nbytes_read = recv(client->sock, buf, sizeof(buf) - 1, 0);
        !          1597:        if (nbytes_read < 0) {
        !          1598:                int err = php_socket_errno();
        !          1599:                if (err == SOCK_EAGAIN) {
        !          1600:                        return 0;
        !          1601:                }
        !          1602:                *errstr = php_socket_strerror(err, NULL, 0);
        !          1603:                return -1;
        !          1604:        } else if (nbytes_read == 0) {
        !          1605:                *errstr = estrdup("Unexpected EOF");
        !          1606:                return -1;
        !          1607:        }
        !          1608:        client->parser.data = client;
        !          1609:        nbytes_consumed = php_http_parser_execute(&client->parser, &settings, buf, nbytes_read);
        !          1610:        if (nbytes_consumed != nbytes_read) {
        !          1611:                *errstr = estrdup("Malformed HTTP request");
        !          1612:                return -1;
        !          1613:        }
        !          1614:        if (client->current_header_name) {
        !          1615:                char *header_name = safe_pemalloc(client->current_header_name_len, 1, 1, 1);
        !          1616:                if (!header_name) {
        !          1617:                        return -1;
        !          1618:                }
        !          1619:                memmove(header_name, client->current_header_name, client->current_header_name_len);
        !          1620:                client->current_header_name = header_name;
        !          1621:                client->current_header_name_allocated = 1;
        !          1622:        }
        !          1623:        return client->request_read ? 1: 0;
        !          1624: }
        !          1625: /* }}} */
        !          1626: 
        !          1627: static size_t php_cli_server_client_send_through(php_cli_server_client *client, const char *str, size_t str_len) /* {{{ */
        !          1628: {
        !          1629:        struct timeval tv = { 10, 0 };
        !          1630:        ssize_t nbytes_left = str_len;
        !          1631:        do {
        !          1632:                ssize_t nbytes_sent = send(client->sock, str + str_len - nbytes_left, nbytes_left, 0);
        !          1633:                if (nbytes_sent < 0) {
        !          1634:                        int err = php_socket_errno();
        !          1635:                        if (err == SOCK_EAGAIN) {
        !          1636:                                int nfds = php_pollfd_for(client->sock, POLLOUT, &tv);
        !          1637:                                if (nfds > 0) {
        !          1638:                                        continue;
        !          1639:                                } else if (nfds < 0) {
        !          1640:                                        /* error */
        !          1641:                                        php_handle_aborted_connection();
        !          1642:                                        return nbytes_left;
        !          1643:                                } else {
        !          1644:                                        /* timeout */
        !          1645:                                        php_handle_aborted_connection();
        !          1646:                                        return nbytes_left;
        !          1647:                                }
        !          1648:                        } else {
        !          1649:                                php_handle_aborted_connection();
        !          1650:                                return nbytes_left;
        !          1651:                        }
        !          1652:                }
        !          1653:                nbytes_left -= nbytes_sent;
        !          1654:        } while (nbytes_left > 0);
        !          1655: 
        !          1656:        return str_len;
        !          1657: } /* }}} */
        !          1658: 
        !          1659: static void php_cli_server_client_populate_request_info(const php_cli_server_client *client, sapi_request_info *request_info) /* {{{ */
        !          1660: {
        !          1661:        char **val;
        !          1662: 
        !          1663:        request_info->request_method = php_http_method_str(client->request.request_method);
        !          1664:        request_info->proto_num = client->request.protocol_version;
        !          1665:        request_info->request_uri = client->request.request_uri;
        !          1666:        request_info->path_translated = client->request.path_translated;
        !          1667:        request_info->query_string = client->request.query_string;
        !          1668:        request_info->post_data = client->request.content;
        !          1669:        request_info->content_length = request_info->post_data_length = client->request.content_len;
        !          1670:        request_info->auth_user = request_info->auth_password = request_info->auth_digest = NULL;
        !          1671:        if (SUCCESS == zend_hash_find(&client->request.headers, "Content-Type", sizeof("Content-Type"), (void**)&val)) {
        !          1672:                request_info->content_type = *val;
        !          1673:        }
        !          1674: } /* }}} */
        !          1675: 
        !          1676: static void destroy_request_info(sapi_request_info *request_info) /* {{{ */
        !          1677: {
        !          1678: } /* }}} */
        !          1679: 
        !          1680: static void php_cli_server_client_begin_capture(php_cli_server_client *client) /* {{{ */
        !          1681: {
        !          1682:        php_cli_server_buffer_ctor(&client->capture_buffer);
        !          1683:        client->capturing = 1;
        !          1684: } /* }}} */
        !          1685: 
        !          1686: static void php_cli_server_client_end_capture(php_cli_server_client *client) /* {{{ */
        !          1687: {
        !          1688:        client->capturing = 0;
        !          1689:        php_cli_server_buffer_dtor(&client->capture_buffer);
        !          1690: } /* }}} */
        !          1691: 
        !          1692: static int php_cli_server_client_ctor(php_cli_server_client *client, php_cli_server *server, int client_sock, struct sockaddr *addr, socklen_t addr_len TSRMLS_DC) /* {{{ */
        !          1693: {
        !          1694:        client->server = server;
        !          1695:        client->sock = client_sock;
        !          1696:        client->addr = addr;
        !          1697:        client->addr_len = addr_len;
        !          1698:        {
        !          1699:                char *addr_str = 0;
        !          1700:                long addr_str_len = 0;
        !          1701:                php_network_populate_name_from_sockaddr(addr, addr_len, &addr_str, &addr_str_len, NULL, 0 TSRMLS_CC);
        !          1702:                client->addr_str = pestrndup(addr_str, addr_str_len, 1);
        !          1703:                client->addr_str_len = addr_str_len;
        !          1704:                efree(addr_str);
        !          1705:        }
        !          1706:        php_http_parser_init(&client->parser, PHP_HTTP_REQUEST);
        !          1707:        client->request_read = 0;
        !          1708:        client->current_header_name = NULL;
        !          1709:        client->current_header_name_len = 0;
        !          1710:        client->current_header_name_allocated = 0;
        !          1711:        client->post_read_offset = 0;
        !          1712:        if (FAILURE == php_cli_server_request_ctor(&client->request)) {
        !          1713:                return FAILURE;
        !          1714:        }
        !          1715:        client->content_sender_initialized = 0;
        !          1716:        client->capturing = 0;
        !          1717:        client->file_fd = -1;
        !          1718:        return SUCCESS;
        !          1719: } /* }}} */
        !          1720: 
        !          1721: static void php_cli_server_client_dtor(php_cli_server_client *client) /* {{{ */
        !          1722: {
        !          1723:        php_cli_server_request_dtor(&client->request);
        !          1724:        if (client->file_fd >= 0) {
        !          1725:                close(client->file_fd);
        !          1726:                client->file_fd = -1;
        !          1727:        }
        !          1728:        pefree(client->addr, 1);
        !          1729:        pefree(client->addr_str, 1);
        !          1730:        if (client->content_sender_initialized) {
        !          1731:                php_cli_server_content_sender_dtor(&client->content_sender);
        !          1732:        }
        !          1733:        if (client->capturing) {
        !          1734:                php_cli_server_buffer_dtor(&client->capture_buffer);
        !          1735:        }
        !          1736: } /* }}} */
        !          1737: 
        !          1738: static void php_cli_server_close_connection(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) /* {{{ */
        !          1739: {
        !          1740: #ifdef DEBUG
        !          1741:        php_cli_server_logf("%s Closing" TSRMLS_CC, client->addr_str);
        !          1742: #endif
        !          1743:        zend_hash_index_del(&server->clients, client->sock);
        !          1744: } /* }}} */
        !          1745: 
        !          1746: static int php_cli_server_send_error_page(php_cli_server *server, php_cli_server_client *client, int status TSRMLS_DC) /* {{{ */
        !          1747: {
        !          1748:        char *escaped_request_uri = NULL;
        !          1749:        size_t escaped_request_uri_len;
        !          1750:        const char *status_string = get_status_string(status);
        !          1751:        const char *content_template = get_template_string(status);
        !          1752:        char *errstr = get_last_error();
        !          1753:        assert(status_string && content_template);
        !          1754: 
        !          1755:        php_cli_server_content_sender_ctor(&client->content_sender);
        !          1756:        client->content_sender_initialized = 1;
        !          1757: 
        !          1758:        escaped_request_uri = php_escape_html_entities_ex((unsigned char *)client->request.request_uri, client->request.request_uri_len, &escaped_request_uri_len, 0, ENT_QUOTES, NULL, 0 TSRMLS_CC);
        !          1759: 
        !          1760:        {
        !          1761:                static const char prologue_template[] = "<html><head><title>%d %s</title>";
        !          1762:                php_cli_server_chunk *chunk = php_cli_server_chunk_heap_new_self_contained(strlen(prologue_template) + 3 + strlen(status_string) + 1);
        !          1763:                if (!chunk) {
        !          1764:                        goto fail;
        !          1765:                }
        !          1766:                snprintf(chunk->data.heap.p, chunk->data.heap.len, prologue_template, status, status_string, escaped_request_uri);
        !          1767:                chunk->data.heap.len = strlen(chunk->data.heap.p);
        !          1768:                php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
        !          1769:        }
        !          1770:        {
        !          1771:                int err = 0;
        !          1772:                zval *style = NULL; 
        !          1773:                zend_try {
        !          1774:                        if (!SG(sapi_started)) {
        !          1775:                                php_output_activate(TSRMLS_C);
        !          1776:                        }
        !          1777:                        php_output_start_user(NULL, 0, PHP_OUTPUT_HANDLER_STDFLAGS TSRMLS_CC);
        !          1778:                        php_info_print_style(TSRMLS_C);
        !          1779:                        MAKE_STD_ZVAL(style);
        !          1780:                        php_output_get_contents(style TSRMLS_CC);
        !          1781:                        php_output_discard(TSRMLS_C);
        !          1782:                        if (!SG(sapi_started)) {
        !          1783:                                static int (*send_header_func)(sapi_headers_struct * TSRMLS_DC);
        !          1784:                                send_header_func = sapi_module.send_headers;
        !          1785:                                /* we don't want the header to be sent now */
        !          1786:                                sapi_module.send_headers = sapi_cli_server_discard_headers;
        !          1787:                                php_output_deactivate(TSRMLS_C);
        !          1788:                                sapi_module.send_headers = send_header_func;
        !          1789:                        }
        !          1790:                        if (style && Z_STRVAL_P(style)) {
        !          1791:                                char *block = pestrndup(Z_STRVAL_P(style), Z_STRLEN_P(style), 1);
        !          1792:                                php_cli_server_chunk *chunk = php_cli_server_chunk_heap_new(block, block, Z_STRLEN_P(style));
        !          1793:                                if (!chunk) {
        !          1794:                                        zval_ptr_dtor(&style);
        !          1795:                                        goto fail;
        !          1796:                                }
        !          1797:                                php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
        !          1798:                                zval_ptr_dtor(&style);
        !          1799:                        } else {
        !          1800:                                err = 1;
        !          1801:                        }
        !          1802:                } zend_catch {
        !          1803:                        err = 1;
        !          1804:                } zend_end_try();
        !          1805:                if (err) {
        !          1806:                        goto fail;
        !          1807:                }
        !          1808:        }
        !          1809:        {
        !          1810:                static const char template[] = "</head><body>";
        !          1811:                php_cli_server_chunk *chunk = php_cli_server_chunk_immortal_new(template, sizeof(template) - 1);
        !          1812:                if (!chunk) {
        !          1813:                        goto fail;
        !          1814:                }
        !          1815:                php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
        !          1816:        }
        !          1817:        {
        !          1818:                php_cli_server_chunk *chunk = php_cli_server_chunk_heap_new_self_contained(strlen(content_template) + escaped_request_uri_len + 3 + strlen(status_string) + 1);
        !          1819:                if (!chunk) {
        !          1820:                        goto fail;
        !          1821:                }
        !          1822:                snprintf(chunk->data.heap.p, chunk->data.heap.len, content_template, status_string, escaped_request_uri);
        !          1823:                chunk->data.heap.len = strlen(chunk->data.heap.p);
        !          1824:                php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
        !          1825:        }
        !          1826:        {
        !          1827:                static const char epilogue_template[] = "</body></html>";
        !          1828:                php_cli_server_chunk *chunk = php_cli_server_chunk_immortal_new(epilogue_template, sizeof(epilogue_template) - 1);
        !          1829:                if (!chunk) {
        !          1830:                        goto fail;
        !          1831:                }
        !          1832:                php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
        !          1833:        }
        !          1834: 
        !          1835:        {
        !          1836:                php_cli_server_chunk *chunk;
        !          1837:                smart_str buffer = { 0 };
        !          1838:                append_http_status_line(&buffer, client->request.protocol_version, status, 1);
        !          1839:                if (!buffer.c) {
        !          1840:                        /* out of memory */
        !          1841:                        goto fail;
        !          1842:                }
        !          1843:                append_essential_headers(&buffer, client, 1);
        !          1844:                smart_str_appends_ex(&buffer, "Content-Type: text/html; charset=UTF-8\r\n", 1);
        !          1845:                smart_str_appends_ex(&buffer, "Content-Length: ", 1);
        !          1846:                smart_str_append_generic_ex(&buffer, php_cli_server_buffer_size(&client->content_sender.buffer), 1, size_t, _unsigned);
        !          1847:                smart_str_appendl_ex(&buffer, "\r\n", 2, 1);
        !          1848:                smart_str_appendl_ex(&buffer, "\r\n", 2, 1);
        !          1849:                
        !          1850:                chunk = php_cli_server_chunk_heap_new(buffer.c, buffer.c, buffer.len);
        !          1851:                if (!chunk) {
        !          1852:                        smart_str_free_ex(&buffer, 1);
        !          1853:                        goto fail;
        !          1854:                }
        !          1855:                php_cli_server_buffer_prepend(&client->content_sender.buffer, chunk);
        !          1856:        }
        !          1857: 
        !          1858:        php_cli_server_log_response(client, status, errstr ? errstr : "?" TSRMLS_CC);
        !          1859:        php_cli_server_poller_add(&server->poller, POLLOUT, client->sock);
        !          1860:        if (errstr) {
        !          1861:                pefree(errstr, 1);
        !          1862:        }
        !          1863:        efree(escaped_request_uri);
        !          1864:        return SUCCESS;
        !          1865: 
        !          1866: fail:
        !          1867:        efree(escaped_request_uri);
        !          1868:        return FAILURE;
        !          1869: } /* }}} */
        !          1870: 
        !          1871: static int php_cli_server_dispatch_script(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) /* {{{ */
        !          1872: {
        !          1873:        if (strlen(client->request.path_translated) != client->request.path_translated_len) {
        !          1874:                /* can't handle paths that contain nul bytes */
        !          1875:                return php_cli_server_send_error_page(server, client, 400 TSRMLS_CC);
        !          1876:        }
        !          1877:        {
        !          1878:                zend_file_handle zfd;
        !          1879:                zfd.type = ZEND_HANDLE_FILENAME;
        !          1880:                zfd.filename = SG(request_info).path_translated;
        !          1881:                zfd.handle.fp = NULL;
        !          1882:                zfd.free_filename = 0;
        !          1883:                zfd.opened_path = NULL;
        !          1884:                zend_try {
        !          1885:                        php_execute_script(&zfd TSRMLS_CC);
        !          1886:                } zend_end_try();
        !          1887:        }
        !          1888: 
        !          1889:        php_cli_server_log_response(client, SG(sapi_headers).http_response_code, NULL TSRMLS_CC);
        !          1890:        return SUCCESS;
        !          1891: } /* }}} */
        !          1892: 
        !          1893: static int php_cli_server_begin_send_static(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) /* {{{ */
        !          1894: {
        !          1895:        int fd;
        !          1896:        int status = 200;
        !          1897: 
        !          1898:        if (client->request.path_translated && strlen(client->request.path_translated) != client->request.path_translated_len) {
        !          1899:                /* can't handle paths that contain nul bytes */
        !          1900:                return php_cli_server_send_error_page(server, client, 400 TSRMLS_CC);
        !          1901:        }
        !          1902: 
        !          1903:        fd = client->request.path_translated ? open(client->request.path_translated, O_RDONLY): -1;
        !          1904:        if (fd < 0) {
        !          1905:                return php_cli_server_send_error_page(server, client, 404 TSRMLS_CC);
        !          1906:        }
        !          1907: 
        !          1908:        php_cli_server_content_sender_ctor(&client->content_sender);
        !          1909:        client->content_sender_initialized = 1;
        !          1910:        client->file_fd = fd;
        !          1911: 
        !          1912:        {
        !          1913:                php_cli_server_chunk *chunk;
        !          1914:                smart_str buffer = { 0 };
        !          1915:                const char *mime_type = get_mime_type(client->request.ext, client->request.ext_len);
        !          1916:                if (!mime_type) {
        !          1917:                        mime_type = "application/octet-stream";
        !          1918:                }
        !          1919: 
        !          1920:                append_http_status_line(&buffer, client->request.protocol_version, status, 1);
        !          1921:                if (!buffer.c) {
        !          1922:                        /* out of memory */
        !          1923:                        php_cli_server_log_response(client, 500, NULL TSRMLS_CC);
        !          1924:                        return FAILURE;
        !          1925:                }
        !          1926:                append_essential_headers(&buffer, client, 1);
        !          1927:                smart_str_appendl_ex(&buffer, "Content-Type: ", sizeof("Content-Type: ") - 1, 1);
        !          1928:                smart_str_appends_ex(&buffer, mime_type, 1);
        !          1929:                if (strncmp(mime_type, "text/", 5) == 0) {
        !          1930:                        smart_str_appends_ex(&buffer, "; charset=UTF-8", 1);
        !          1931:                }
        !          1932:                smart_str_appendl_ex(&buffer, "\r\n", 2, 1);
        !          1933:                smart_str_appends_ex(&buffer, "Content-Length: ", 1);
        !          1934:                smart_str_append_generic_ex(&buffer, client->request.sb.st_size, 1, size_t, _unsigned);
        !          1935:                smart_str_appendl_ex(&buffer, "\r\n", 2, 1);
        !          1936:                smart_str_appendl_ex(&buffer, "\r\n", 2, 1);
        !          1937:                chunk = php_cli_server_chunk_heap_new(buffer.c, buffer.c, buffer.len);
        !          1938:                if (!chunk) {
        !          1939:                        smart_str_free_ex(&buffer, 1);
        !          1940:                        php_cli_server_log_response(client, 500, NULL TSRMLS_CC);
        !          1941:                        return FAILURE;
        !          1942:                }
        !          1943:                php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
        !          1944:        }
        !          1945:        php_cli_server_log_response(client, 200, NULL TSRMLS_CC);
        !          1946:        php_cli_server_poller_add(&server->poller, POLLOUT, client->sock);
        !          1947:        return SUCCESS;
        !          1948: }
        !          1949: /* }}} */
        !          1950: 
        !          1951: static int php_cli_server_request_startup(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) { /* {{{ */ 
        !          1952:        char **auth;
        !          1953:        php_cli_server_client_populate_request_info(client, &SG(request_info));
        !          1954:        if (SUCCESS == zend_hash_find(&client->request.headers, "Authorization", sizeof("Authorization"), (void**)&auth)) {
        !          1955:                php_handle_auth_data(*auth TSRMLS_CC);
        !          1956:        }
        !          1957:        SG(sapi_headers).http_response_code = 200;
        !          1958:        if (FAILURE == php_request_startup(TSRMLS_C)) {
        !          1959:                /* should never be happen */
        !          1960:                destroy_request_info(&SG(request_info));
        !          1961:                return FAILURE;
        !          1962:        }
        !          1963:        PG(during_request_startup) = 0;
        !          1964: 
        !          1965:        return SUCCESS;
        !          1966: }
        !          1967: /* }}} */
        !          1968: 
        !          1969: static int php_cli_server_request_shutdown(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) { /* {{{ */
        !          1970:        php_request_shutdown(0);
        !          1971:        php_cli_server_close_connection(server, client TSRMLS_CC);
        !          1972:        destroy_request_info(&SG(request_info));
        !          1973:        SG(server_context) = NULL;
        !          1974:        SG(rfc1867_uploaded_files) = NULL;
        !          1975:        return SUCCESS;
        !          1976: }             
        !          1977: /* }}} */  
        !          1978: 
        !          1979: static int php_cli_server_dispatch_router(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) /* {{{ */
        !          1980: {
        !          1981:        int decline = 0;
        !          1982:        if (!php_handle_special_queries(TSRMLS_C)) {
        !          1983:                zend_file_handle zfd;
        !          1984:                char *old_cwd;
        !          1985: 
        !          1986:                ALLOCA_FLAG(use_heap)
        !          1987:                old_cwd = do_alloca(MAXPATHLEN, use_heap);
        !          1988:                old_cwd[0] = '\0';
        !          1989:                php_ignore_value(VCWD_GETCWD(old_cwd, MAXPATHLEN - 1));
        !          1990: 
        !          1991:                zfd.type = ZEND_HANDLE_FILENAME;
        !          1992:                zfd.filename = server->router;
        !          1993:                zfd.handle.fp = NULL;
        !          1994:                zfd.free_filename = 0;
        !          1995:                zfd.opened_path = NULL;
        !          1996: 
        !          1997:                zend_try {
        !          1998:                        zval *retval = NULL;
        !          1999:                        if (SUCCESS == zend_execute_scripts(ZEND_REQUIRE TSRMLS_CC, &retval, 1, &zfd)) {
        !          2000:                                if (retval) {
        !          2001:                                        decline = Z_TYPE_P(retval) == IS_BOOL && !Z_LVAL_P(retval);
        !          2002:                                        zval_ptr_dtor(&retval);
        !          2003:                                }
        !          2004:                        } else {
        !          2005:                                decline = 1;
        !          2006:                        }
        !          2007:                } zend_end_try();
        !          2008: 
        !          2009:                if (old_cwd[0] != '\0') {
        !          2010:                        php_ignore_value(VCWD_CHDIR(old_cwd));
        !          2011:                }
        !          2012: 
        !          2013:                free_alloca(old_cwd, use_heap);
        !          2014:        }
        !          2015: 
        !          2016:        return decline;
        !          2017: }
        !          2018: /* }}} */
        !          2019: 
        !          2020: static int php_cli_server_dispatch(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) /* {{{ */
        !          2021: {
        !          2022:        int is_static_file  = 0;
        !          2023: 
        !          2024:        SG(server_context) = client;
        !          2025:        if (client->request.ext_len != 3 || memcmp(client->request.ext, "php", 3) || !client->request.path_translated) {
        !          2026:                is_static_file = 1;
        !          2027:        }
        !          2028: 
        !          2029:        if (server->router || !is_static_file) {
        !          2030:                if (FAILURE == php_cli_server_request_startup(server, client TSRMLS_CC)) {
        !          2031:                        SG(server_context) = NULL;
        !          2032:                        php_cli_server_close_connection(server, client TSRMLS_CC);
        !          2033:                        destroy_request_info(&SG(request_info));
        !          2034:                        return SUCCESS;
        !          2035:                }
        !          2036:        } 
        !          2037: 
        !          2038:        if (server->router) {
        !          2039:                if (!php_cli_server_dispatch_router(server, client TSRMLS_CC)) {
        !          2040:                        php_cli_server_request_shutdown(server, client TSRMLS_CC);
        !          2041:                        return SUCCESS;
        !          2042:                }
        !          2043:        }
        !          2044: 
        !          2045:        if (!is_static_file) {
        !          2046:                if (SUCCESS == php_cli_server_dispatch_script(server, client TSRMLS_CC)
        !          2047:                                || SUCCESS != php_cli_server_send_error_page(server, client, 500 TSRMLS_CC)) {
        !          2048:                        php_cli_server_request_shutdown(server, client TSRMLS_CC);
        !          2049:                        return SUCCESS;
        !          2050:                } 
        !          2051:        } else {
        !          2052:                if (server->router) {
        !          2053:                        static int (*send_header_func)(sapi_headers_struct * TSRMLS_DC);
        !          2054:                        send_header_func = sapi_module.send_headers;
        !          2055:                        /* we don't want the header to be sent now */
        !          2056:                        sapi_module.send_headers = sapi_cli_server_discard_headers;
        !          2057:                        php_request_shutdown(0);
        !          2058:                        sapi_module.send_headers = send_header_func;
        !          2059:                        SG(rfc1867_uploaded_files) = NULL;
        !          2060:                } 
        !          2061:                if (SUCCESS != php_cli_server_begin_send_static(server, client TSRMLS_CC)) {
        !          2062:                        php_cli_server_close_connection(server, client TSRMLS_CC);
        !          2063:                }
        !          2064:                SG(server_context) = NULL;
        !          2065:                return SUCCESS;
        !          2066:        }
        !          2067: 
        !          2068:        SG(server_context) = NULL;
        !          2069:        destroy_request_info(&SG(request_info));
        !          2070:        return SUCCESS;
        !          2071: }
        !          2072: /* }}} */
        !          2073: 
        !          2074: static void php_cli_server_dtor(php_cli_server *server TSRMLS_DC) /* {{{ */
        !          2075: {
        !          2076:        zend_hash_destroy(&server->clients);
        !          2077:        if (server->server_sock >= 0) {
        !          2078:                closesocket(server->server_sock);
        !          2079:        }
        !          2080:        if (server->host) {
        !          2081:                pefree(server->host, 1);
        !          2082:        }
        !          2083:        if (server->document_root) {
        !          2084:                pefree(server->document_root, 1);
        !          2085:        }
        !          2086:        if (server->router) {
        !          2087:                pefree(server->router, 1);
        !          2088:        }
        !          2089: } /* }}} */
        !          2090: 
        !          2091: static void php_cli_server_client_dtor_wrapper(php_cli_server_client **p) /* {{{ */
        !          2092: {
        !          2093:        closesocket((*p)->sock);
        !          2094:        php_cli_server_poller_remove(&(*p)->server->poller, POLLIN | POLLOUT, (*p)->sock);
        !          2095:        php_cli_server_client_dtor(*p);
        !          2096:        pefree(*p, 1);
        !          2097: } /* }}} */
        !          2098: 
        !          2099: static int php_cli_server_ctor(php_cli_server *server, const char *addr, const char *document_root, const char *router TSRMLS_DC) /* {{{ */
        !          2100: {
        !          2101:        int retval = SUCCESS;
        !          2102:        char *host = NULL;
        !          2103:        char *errstr = NULL;
        !          2104:        char *_document_root = NULL;
        !          2105:        char *_router = NULL;
        !          2106:        int err = 0;
        !          2107:        int port = 3000;
        !          2108:        php_socket_t server_sock = SOCK_ERR;
        !          2109:        char *p = NULL;
        !          2110: 
        !          2111:        if (addr[0] == '[') {
        !          2112:                host = pestrdup(addr + 1, 1);
        !          2113:                if (!host) {
        !          2114:                        return FAILURE;
        !          2115:                }
        !          2116:                p = strchr(host, ']');
        !          2117:                if (p) {
        !          2118:                        *p++ = '\0';
        !          2119:                        if (*p == ':') {
        !          2120:                                port = strtol(p + 1, &p, 10);
        !          2121:                                if (port <= 0) {
        !          2122:                                        p = NULL;
        !          2123:                                }
        !          2124:                        } else if (*p != '\0') {
        !          2125:                                p = NULL;
        !          2126:                        }
        !          2127:                }
        !          2128:        } else {
        !          2129:                host = pestrdup(addr, 1);
        !          2130:                if (!host) {
        !          2131:                        return FAILURE;
        !          2132:                }
        !          2133:                p = strchr(host, ':');
        !          2134:                if (p) {
        !          2135:                        *p++ = '\0';
        !          2136:                        port = strtol(p, &p, 10);
        !          2137:                        if (port <= 0) {
        !          2138:                                p = NULL;
        !          2139:                        }
        !          2140:                }
        !          2141:        }
        !          2142:        if (!p) {
        !          2143:                fprintf(stderr, "Invalid address: %s\n", addr);
        !          2144:                retval = FAILURE;
        !          2145:                goto out;
        !          2146:        }
        !          2147: 
        !          2148:        server_sock = php_network_listen_socket(host, &port, SOCK_STREAM, &server->address_family, &server->socklen, &errstr TSRMLS_CC);
        !          2149:        if (server_sock == SOCK_ERR) {
        !          2150:                php_cli_server_logf("Failed to listen on %s:%d (reason: %s)" TSRMLS_CC, host, port, errstr ? errstr: "?");
        !          2151:                efree(errstr);
        !          2152:                retval = FAILURE;
        !          2153:                goto out;
        !          2154:        }
        !          2155:        server->server_sock = server_sock;
        !          2156: 
        !          2157:        err = php_cli_server_poller_ctor(&server->poller);
        !          2158:        if (SUCCESS != err) {
        !          2159:                goto out;
        !          2160:        }
        !          2161: 
        !          2162:        php_cli_server_poller_add(&server->poller, POLLIN, server_sock);
        !          2163: 
        !          2164:        server->host = host;
        !          2165:        server->port = port;
        !          2166: 
        !          2167:        zend_hash_init(&server->clients, 0, NULL, (void(*)(void*))php_cli_server_client_dtor_wrapper, 1);
        !          2168: 
        !          2169:        {
        !          2170:                size_t document_root_len = strlen(document_root);
        !          2171:                _document_root = pestrndup(document_root, document_root_len, 1);
        !          2172:                if (!_document_root) {
        !          2173:                        retval = FAILURE;
        !          2174:                        goto out;
        !          2175:                }
        !          2176:                server->document_root = _document_root;
        !          2177:                server->document_root_len = document_root_len;
        !          2178:        }
        !          2179: 
        !          2180:        if (router) {
        !          2181:                size_t router_len = strlen(router);
        !          2182:                _router = pestrndup(router, router_len, 1);
        !          2183:                if (!_router) {
        !          2184:                        retval = FAILURE;
        !          2185:                        goto out;
        !          2186:                }
        !          2187:                server->router = _router;
        !          2188:                server->router_len = router_len;
        !          2189:        } else {
        !          2190:                server->router = NULL;
        !          2191:                server->router_len = 0;
        !          2192:        }
        !          2193: 
        !          2194:        server->is_running = 1;
        !          2195: out:
        !          2196:        if (retval != SUCCESS) {
        !          2197:                if (host) {
        !          2198:                        pefree(host, 1);
        !          2199:                }
        !          2200:                if (_document_root) {
        !          2201:                        pefree(_document_root, 1);
        !          2202:                }
        !          2203:                if (_router) {
        !          2204:                        pefree(_router, 1);
        !          2205:                }
        !          2206:                if (server_sock >= -1) {
        !          2207:                        closesocket(server_sock);
        !          2208:                }
        !          2209:        }
        !          2210:        return retval;
        !          2211: } /* }}} */
        !          2212: 
        !          2213: static int php_cli_server_recv_event_read_request(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) /* {{{ */
        !          2214: {
        !          2215:        char *errstr = NULL;
        !          2216:        int status = php_cli_server_client_read_request(client, &errstr TSRMLS_CC);
        !          2217:        if (status < 0) {
        !          2218:                php_cli_server_logf("%s Invalid request (%s)" TSRMLS_CC, client->addr_str, errstr);
        !          2219:                efree(errstr);
        !          2220:                php_cli_server_close_connection(server, client TSRMLS_CC);
        !          2221:                return FAILURE;
        !          2222:        } else if (status == 1) {
        !          2223:                php_cli_server_poller_remove(&server->poller, POLLIN, client->sock);
        !          2224:                php_cli_server_dispatch(server, client TSRMLS_CC);
        !          2225:        } else {
        !          2226:                php_cli_server_poller_add(&server->poller, POLLIN, client->sock);
        !          2227:        }
        !          2228: 
        !          2229:        return SUCCESS;
        !          2230: } /* }}} */
        !          2231: 
        !          2232: static int php_cli_server_send_event(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) /* {{{ */
        !          2233: {
        !          2234:        if (client->content_sender_initialized) {
        !          2235:                if (client->file_fd >= 0 && !client->content_sender.buffer.first) {
        !          2236:                        size_t nbytes_read;
        !          2237:                        if (php_cli_server_content_sender_pull(&client->content_sender, client->file_fd, &nbytes_read)) {
        !          2238:                                php_cli_server_close_connection(server, client TSRMLS_CC);
        !          2239:                                return FAILURE;
        !          2240:                        }
        !          2241:                        if (nbytes_read == 0) {
        !          2242:                                close(client->file_fd);
        !          2243:                                client->file_fd = -1;
        !          2244:                        }
        !          2245:                }
        !          2246:                {
        !          2247:                        size_t nbytes_sent;
        !          2248:                        int err = php_cli_server_content_sender_send(&client->content_sender, client->sock, &nbytes_sent);
        !          2249:                        if (err && err != SOCK_EAGAIN) {
        !          2250:                                php_cli_server_close_connection(server, client TSRMLS_CC);
        !          2251:                                return FAILURE;
        !          2252:                        }
        !          2253:                }
        !          2254:                if (!client->content_sender.buffer.first && client->file_fd < 0) {
        !          2255:                        php_cli_server_close_connection(server, client TSRMLS_CC);
        !          2256:                }
        !          2257:        }
        !          2258:        return SUCCESS;
        !          2259: }
        !          2260: /* }}} */
        !          2261: 
        !          2262: typedef struct php_cli_server_do_event_for_each_fd_callback_params {
        !          2263: #ifdef ZTS
        !          2264:        void ***tsrm_ls;
        !          2265: #endif
        !          2266:        php_cli_server *server;
        !          2267:        int(*rhandler)(php_cli_server*, php_cli_server_client* TSRMLS_DC);
        !          2268:        int(*whandler)(php_cli_server*, php_cli_server_client* TSRMLS_DC);
        !          2269: } php_cli_server_do_event_for_each_fd_callback_params;
        !          2270: 
        !          2271: static int php_cli_server_do_event_for_each_fd_callback(void *_params, int fd, int event) /* {{{ */
        !          2272: {
        !          2273:        php_cli_server_do_event_for_each_fd_callback_params *params = _params;
        !          2274: #ifdef ZTS
        !          2275:        void ***tsrm_ls = params->tsrm_ls;
        !          2276: #endif
        !          2277:        php_cli_server *server = params->server;
        !          2278:        if (server->server_sock == fd) {
        !          2279:                php_cli_server_client *client = NULL;
        !          2280:                php_socket_t client_sock;
        !          2281:                socklen_t socklen = server->socklen;
        !          2282:                struct sockaddr *sa = pemalloc(server->socklen, 1);
        !          2283:                if (!sa) {
        !          2284:                        return FAILURE;
        !          2285:                }
        !          2286:                client_sock = accept(server->server_sock, sa, &socklen);
        !          2287:                if (client_sock < 0) {
        !          2288:                        char *errstr;
        !          2289:                        errstr = php_socket_strerror(php_socket_errno(), NULL, 0);
        !          2290:                        php_cli_server_logf("Failed to accept a client (reason: %s)" TSRMLS_CC, errstr);
        !          2291:                        efree(errstr);
        !          2292:                        pefree(sa, 1);
        !          2293:                        return SUCCESS;
        !          2294:                }
        !          2295:                if (SUCCESS != php_set_sock_blocking(client_sock, 0 TSRMLS_CC)) {
        !          2296:                        pefree(sa, 1);
        !          2297:                        closesocket(client_sock);
        !          2298:                        return SUCCESS;
        !          2299:                }
        !          2300:                if (!(client = pemalloc(sizeof(php_cli_server_client), 1)) || FAILURE == php_cli_server_client_ctor(client, server, client_sock, sa, socklen TSRMLS_CC)) {
        !          2301:                        php_cli_server_logf("Failed to create a new request object" TSRMLS_CC);
        !          2302:                        pefree(sa, 1);
        !          2303:                        closesocket(client_sock);
        !          2304:                        return SUCCESS;
        !          2305:                }
        !          2306: #ifdef DEBUG
        !          2307:                php_cli_server_logf("%s Accepted" TSRMLS_CC, client->addr_str);
        !          2308: #endif
        !          2309:                zend_hash_index_update(&server->clients, client_sock, &client, sizeof(client), NULL);
        !          2310:                php_cli_server_recv_event_read_request(server, client TSRMLS_CC);
        !          2311:        } else {
        !          2312:                php_cli_server_client **client;
        !          2313:                if (SUCCESS == zend_hash_index_find(&server->clients, fd, (void **)&client)) {
        !          2314:                        if (event & POLLIN) {
        !          2315:                                params->rhandler(server, *client TSRMLS_CC);
        !          2316:                        }
        !          2317:                        if (event & POLLOUT) {
        !          2318:                                params->whandler(server, *client TSRMLS_CC);
        !          2319:                        }
        !          2320:                }
        !          2321:        }
        !          2322:        return SUCCESS;
        !          2323: } /* }}} */
        !          2324: 
        !          2325: static void php_cli_server_do_event_for_each_fd(php_cli_server *server, int(*rhandler)(php_cli_server*, php_cli_server_client* TSRMLS_DC), int(*whandler)(php_cli_server*, php_cli_server_client* TSRMLS_DC) TSRMLS_DC) /* {{{ */
        !          2326: {
        !          2327:        php_cli_server_do_event_for_each_fd_callback_params params = {
        !          2328: #ifdef ZTS
        !          2329:                tsrm_ls,
        !          2330: #endif
        !          2331:                server,
        !          2332:                rhandler,
        !          2333:                whandler
        !          2334:        };
        !          2335: 
        !          2336:        php_cli_server_poller_iter_on_active(&server->poller, &params, php_cli_server_do_event_for_each_fd_callback);
        !          2337: } /* }}} */
        !          2338: 
        !          2339: static int php_cli_server_do_event_loop(php_cli_server *server TSRMLS_DC) /* {{{ */
        !          2340: {
        !          2341:        int retval = SUCCESS;
        !          2342:        while (server->is_running) {    
        !          2343:                static const struct timeval tv = { 1, 0 };
        !          2344:                int n = php_cli_server_poller_poll(&server->poller, &tv);
        !          2345:                if (n > 0) {
        !          2346:                        php_cli_server_do_event_for_each_fd(server,
        !          2347:                                        php_cli_server_recv_event_read_request,
        !          2348:                                        php_cli_server_send_event TSRMLS_CC);
        !          2349:                } else if (n == 0) {
        !          2350:                        /* do nothing */
        !          2351:                } else {
        !          2352:                        int err = php_socket_errno();
        !          2353:                        if (err != SOCK_EINTR) {
        !          2354:                                char *errstr = php_socket_strerror(err, NULL, 0);
        !          2355:                                php_cli_server_logf("%s" TSRMLS_CC, errstr);
        !          2356:                                efree(errstr);
        !          2357:                                retval = FAILURE;
        !          2358:                                goto out;
        !          2359:                        }
        !          2360:                }
        !          2361:        }
        !          2362: out:
        !          2363:        return retval;
        !          2364: } /* }}} */
        !          2365: 
        !          2366: static php_cli_server server;
        !          2367: 
        !          2368: static void php_cli_server_sigint_handler(int sig) /* {{{ */
        !          2369: {
        !          2370:        server.is_running = 0;
        !          2371: }
        !          2372: /* }}} */
        !          2373: 
        !          2374: int do_cli_server(int argc, char **argv TSRMLS_DC) /* {{{ */
        !          2375: {
        !          2376:        char *php_optarg = NULL;
        !          2377:        int php_optind = 1;
        !          2378:        int c;
        !          2379:        const char *server_bind_address = NULL;
        !          2380:        extern const opt_struct OPTIONS[];
        !          2381:        const char *document_root = NULL;
        !          2382:        const char *router = NULL;
        !          2383:        char document_root_buf[MAXPATHLEN];
        !          2384: 
        !          2385:        while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2))!=-1) {
        !          2386:                switch (c) {
        !          2387:                        case 'S':
        !          2388:                                server_bind_address = php_optarg;
        !          2389:                                break;
        !          2390:                        case 't':
        !          2391:                                document_root = php_optarg;
        !          2392:                                break;
        !          2393:                }
        !          2394:        }
        !          2395: 
        !          2396:        if (document_root) {
        !          2397:                struct stat sb;
        !          2398: 
        !          2399:                if (stat(document_root, &sb)) {
        !          2400:                        fprintf(stderr, "Directory %s does not exist.\n", document_root);
        !          2401:                        return 1;
        !          2402:                }
        !          2403:                if (!S_ISDIR(sb.st_mode)) {
        !          2404:                        fprintf(stderr, "%s is not a directory.\n", document_root);
        !          2405:                        return 1;
        !          2406:                }
        !          2407:                if (VCWD_REALPATH(document_root, document_root_buf)) {
        !          2408:                        document_root = document_root_buf;
        !          2409:                }
        !          2410:        } else {
        !          2411:                char *ret = NULL;
        !          2412: 
        !          2413: #if HAVE_GETCWD
        !          2414:                ret = VCWD_GETCWD(document_root_buf, MAXPATHLEN);
        !          2415: #elif HAVE_GETWD
        !          2416:                ret = VCWD_GETWD(document_root_buf);
        !          2417: #endif
        !          2418:                document_root = ret ? document_root_buf: ".";
        !          2419:        }
        !          2420: 
        !          2421:        if (argc > php_optind) {
        !          2422:                router = argv[php_optind];
        !          2423:        }
        !          2424: 
        !          2425:        if (FAILURE == php_cli_server_ctor(&server, server_bind_address, document_root, router TSRMLS_CC)) {
        !          2426:                return 1;
        !          2427:        }
        !          2428:        sapi_module.phpinfo_as_text = 0;
        !          2429: 
        !          2430:        {
        !          2431:                struct timeval tv;
        !          2432:                struct tm tm;
        !          2433:                char buf[52];
        !          2434:                gettimeofday(&tv, NULL);
        !          2435:                php_localtime_r(&tv.tv_sec, &tm);
        !          2436:                php_asctime_r(&tm, buf);
        !          2437:                printf("PHP %s Development Server started at %s"
        !          2438:                                "Listening on %s\n"
        !          2439:                                "Document root is %s\n"
        !          2440:                                "Press Ctrl-C to quit.\n",
        !          2441:                                PHP_VERSION, buf, server_bind_address, document_root);
        !          2442:        }
        !          2443: 
        !          2444: #if defined(HAVE_SIGNAL_H) && defined(SIGINT)
        !          2445:        signal(SIGINT, php_cli_server_sigint_handler);
        !          2446: #endif
        !          2447:        php_cli_server_do_event_loop(&server TSRMLS_CC);
        !          2448:        php_cli_server_dtor(&server TSRMLS_CC);
        !          2449:        return 0;
        !          2450: } /* }}} */
        !          2451: 
        !          2452: /*
        !          2453:  * Local variables:
        !          2454:  * tab-width: 4
        !          2455:  * c-basic-offset: 4
        !          2456:  * End:
        !          2457:  * vim600: noet sw=4 ts=4 fdm=marker
        !          2458:  * vim<600: noet sw=4 ts=4
        !          2459:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>