You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2434 lines
68 KiB

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