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