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