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