root/sapi/cli/php_cli_server.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. php_cli_server_get_system_time
  2. php_cli_server_get_system_time
  3. char_ptr_dtor_p
  4. get_last_error
  5. status_comp
  6. get_status_string
  7. get_template_string
  8. append_http_status_line
  9. append_essential_headers
  10. get_mime_type
  11. PHP_FUNCTION
  12. add_response_header
  13. PHP_FUNCTION
  14. cli_server_init_globals
  15. PHP_INI_BEGIN
  16. PHP_MSHUTDOWN_FUNCTION
  17. PHP_MINFO_FUNCTION
  18. sapi_cli_server_startup
  19. sapi_cli_server_ub_write
  20. sapi_cli_server_flush
  21. sapi_cli_server_discard_headers
  22. sapi_cli_server_send_headers
  23. sapi_cli_server_read_cookies
  24. sapi_cli_server_read_post
  25. sapi_cli_server_register_variable
  26. sapi_cli_server_register_entry_cb
  27. sapi_cli_server_register_variables
  28. sapi_cli_server_log_message
  29. php_cli_server_poller_ctor
  30. php_cli_server_poller_add
  31. php_cli_server_poller_remove
  32. php_cli_server_poller_poll
  33. php_cli_server_poller_iter_on_active
  34. php_cli_server_chunk_size
  35. php_cli_server_chunk_dtor
  36. php_cli_server_buffer_dtor
  37. php_cli_server_buffer_ctor
  38. php_cli_server_buffer_append
  39. php_cli_server_buffer_prepend
  40. php_cli_server_buffer_size
  41. php_cli_server_chunk_immortal_new
  42. php_cli_server_chunk_heap_new
  43. php_cli_server_chunk_heap_new_self_contained
  44. php_cli_server_content_sender_dtor
  45. php_cli_server_content_sender_ctor
  46. php_cli_server_content_sender_send
  47. php_cli_server_content_sender_pull
  48. php_cli_is_output_tty
  49. php_cli_server_log_response
  50. php_cli_server_logf
  51. php_network_listen_socket
  52. php_cli_server_request_ctor
  53. php_cli_server_request_dtor
  54. php_cli_server_request_translate_vpath
  55. normalize_vpath
  56. php_cli_server_client_read_request_on_message_begin
  57. php_cli_server_client_read_request_on_path
  58. php_cli_server_client_read_request_on_query_string
  59. php_cli_server_client_read_request_on_url
  60. php_cli_server_client_read_request_on_fragment
  61. php_cli_server_client_read_request_on_header_field
  62. php_cli_server_client_read_request_on_header_value
  63. php_cli_server_client_read_request_on_headers_complete
  64. php_cli_server_client_read_request_on_body
  65. php_cli_server_client_read_request_on_message_complete
  66. php_cli_server_client_read_request
  67. php_cli_server_client_send_through
  68. php_cli_server_client_populate_request_info
  69. destroy_request_info
  70. php_cli_server_client_ctor
  71. php_cli_server_client_dtor
  72. php_cli_server_close_connection
  73. php_cli_server_send_error_page
  74. php_cli_server_dispatch_script
  75. php_cli_server_begin_send_static
  76. php_cli_server_request_startup
  77. php_cli_server_request_shutdown
  78. php_cli_server_dispatch_router
  79. php_cli_server_dispatch
  80. php_cli_server_mime_type_ctor
  81. php_cli_server_dtor
  82. php_cli_server_client_dtor_wrapper
  83. php_cli_server_ctor
  84. php_cli_server_recv_event_read_request
  85. php_cli_server_send_event
  86. php_cli_server_do_event_for_each_fd_callback
  87. php_cli_server_do_event_for_each_fd
  88. php_cli_server_do_event_loop
  89. php_cli_server_sigint_handler
  90. do_cli_server

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

/* [<][>][^][v][top][bottom][index][help] */