diff options
Diffstat (limited to 'sapi/cli/php_cli_server.c')
-rw-r--r-- | sapi/cli/php_cli_server.c | 296 |
1 files changed, 203 insertions, 93 deletions
diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c index 38cc094954..f00d8dee54 100644 --- a/sapi/cli/php_cli_server.c +++ b/sapi/cli/php_cli_server.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | PHP Version 7 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2018 The PHP Group | + | Copyright (c) The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -37,22 +37,16 @@ #include <unixlib/local.h> #endif - -#if HAVE_TIME_H -#include <time.h> -#endif #if HAVE_SYS_TIME_H #include <sys/time.h> #endif #if HAVE_UNISTD_H #include <unistd.h> #endif -#if HAVE_SIGNAL_H + #include <signal.h> -#endif -#if HAVE_SETLOCALE #include <locale.h> -#endif + #if HAVE_DLFCN_H #include <dlfcn.h> #endif @@ -107,6 +101,13 @@ #define OUTPUT_IS_TTY 1 #define OUTPUT_NOT_TTY 0 +#if HAVE_FORK +# include <sys/wait.h> +static pid_t php_cli_server_master; +static pid_t *php_cli_server_workers; +static zend_long php_cli_server_workers_max; +#endif + typedef struct php_cli_server_poller { fd_set rfds, wfds; struct { @@ -208,6 +209,12 @@ static php_cli_server_http_response_status_code_pair template_map[] = { { 501, "<h1>%s</h1><p>Request method not supported.</p>" } }; +#define PHP_CLI_SERVER_LOG_PROCESS 1 +#define PHP_CLI_SERVER_LOG_ERROR 2 +#define PHP_CLI_SERVER_LOG_MESSAGE 3 + +static int php_cli_server_log_level = 3; + #if HAVE_UNISTD_H static int php_cli_output_is_tty = OUTPUT_NOT_CHECKED; #endif @@ -217,7 +224,7 @@ static const char php_cli_server_request_error_unexpected_eof[] = "Unexpected EO static size_t php_cli_server_client_send_through(php_cli_server_client *client, const char *str, size_t str_len); static php_cli_server_chunk *php_cli_server_chunk_heap_new_self_contained(size_t len); static void php_cli_server_buffer_append(php_cli_server_buffer *buffer, php_cli_server_chunk *chunk); -static void php_cli_server_logf(const char *format, ...); +static void php_cli_server_logf(int type, const char *format, ...); static void php_cli_server_log_response(php_cli_server_client *client, int status, const char *message); ZEND_DECLARE_MODULE_GLOBALS(cli_server); @@ -489,9 +496,57 @@ const zend_function_entry server_additional_functions[] = { static int sapi_cli_server_startup(sapi_module_struct *sapi_module) /* {{{ */ { + char *workers; + if (php_module_startup(sapi_module, &cli_server_module_entry, 1) == FAILURE) { return FAILURE; } + + if ((workers = getenv("PHP_CLI_SERVER_WORKERS"))) { +#ifndef SO_REUSEPORT + fprintf(stderr, "platform does not support SO_REUSEPORT, cannot create workers\n"); +#elif HAVE_FORK + ZEND_ATOL(php_cli_server_workers_max, workers); + + if (php_cli_server_workers_max > 1) { + zend_long php_cli_server_worker; + + php_cli_server_workers = calloc( + php_cli_server_workers_max, sizeof(pid_t)); + if (!php_cli_server_workers) { + php_cli_server_workers_max = 1; + + return SUCCESS; + } + + php_cli_server_master = getpid(); + + for (php_cli_server_worker = 0; + php_cli_server_worker < php_cli_server_workers_max; + php_cli_server_worker++) { + pid_t pid = fork(); + + if (pid == FAILURE) { + /* no more forks allowed, work with what we have ... */ + php_cli_server_workers_max = + php_cli_server_worker + 1; + return SUCCESS; + } else if (pid == SUCCESS) { + return SUCCESS; + } else { + php_cli_server_workers[ + php_cli_server_worker + ] = pid; + } + } + } else { + fprintf(stderr, "number of workers must be larger than 1\n"); + } +#else + fprintf(stderr, "forking is not supported on this platform\n"); +#endif + } + return SUCCESS; } /* }}} */ @@ -695,10 +750,14 @@ static void sapi_cli_server_register_variables(zval *track_vars_array) /* {{{ */ zend_hash_apply_with_arguments(&client->request.headers, (apply_func_args_t)sapi_cli_server_register_entry_cb, 1, track_vars_array); } /* }}} */ -static void sapi_cli_server_log_message(char *msg, int syslog_type_int) /* {{{ */ +static void sapi_cli_server_log_write(int type, char *msg) /* {{{ */ { char buf[52]; + if (php_cli_server_log_level < type) { + return; + } + if (php_cli_server_get_system_time(buf) != 0) { memmove(buf, "unknown time, can't be fetched", sizeof("unknown time, can't be fetched")); } else { @@ -709,7 +768,20 @@ static void sapi_cli_server_log_message(char *msg, int syslog_type_int) /* {{{ * memmove(buf, "unknown", sizeof("unknown")); } } +#ifdef HAVE_FORK + if (php_cli_server_workers_max > 1) { + fprintf(stderr, "[%ld] [%s] %s\n", (long) getpid(), buf, msg); + } else { + fprintf(stderr, "[%s] %s\n", buf, msg); + } +#else fprintf(stderr, "[%s] %s\n", buf, msg); +#endif +} /* }}} */ + +static void sapi_cli_server_log_message(char *msg, int syslog_type_int) /* {{{ */ +{ + sapi_cli_server_log_write(PHP_CLI_SERVER_LOG_MESSAGE, msg); } /* }}} */ /* {{{ sapi_module_struct cli_server_sapi_module @@ -844,14 +916,14 @@ static int php_cli_server_poller_iter_on_active(php_cli_server_poller *poller, v for (fd=0 ; fd<=max_fd ; fd++) { if (PHP_SAFE_FD_ISSET(fd, &poller->active.rfds)) { - if (SUCCESS != callback(opaque, fd, POLLIN)) { - retval = FAILURE; - } + if (SUCCESS != callback(opaque, fd, POLLIN)) { + retval = FAILURE; + } } if (PHP_SAFE_FD_ISSET(fd, &poller->active.wfds)) { - if (SUCCESS != callback(opaque, fd, POLLOUT)) { - retval = FAILURE; - } + if (SUCCESS != callback(opaque, fd, POLLOUT)) { + retval = FAILURE; + } } } #endif @@ -1064,9 +1136,11 @@ static int php_cli_server_content_sender_pull(php_cli_server_content_sender *sen _nbytes_read = read(fd, chunk->data.heap.p, chunk->data.heap.len); #endif if (_nbytes_read < 0) { - char *errstr = get_last_error(); - php_cli_server_logf("%s", errstr); - pefree(errstr, 1); + if (php_cli_server_log_level >= PHP_CLI_SERVER_LOG_ERROR) { + char *errstr = get_last_error(); + php_cli_server_logf(PHP_CLI_SERVER_LOG_ERROR, "%s", errstr); + pefree(errstr, 1); + } php_cli_server_chunk_dtor(chunk); pefree(chunk, 1); return 1; @@ -1126,7 +1200,7 @@ static void php_cli_server_log_response(php_cli_server_client *client, int statu #endif /* basic */ - spprintf(&basic_buf, 0, "%s [%d]: %s", client->addr_str, status, client->request.request_uri); + spprintf(&basic_buf, 0, "%s [%d]: %s %s", client->addr_str, status, SG(request_info).request_method, client->request.request_uri); if (!basic_buf) { return; } @@ -1153,9 +1227,9 @@ static void php_cli_server_log_response(php_cli_server_client *client, int statu } if (color) { - php_cli_server_logf("\x1b[3%dm%s%s%s\x1b[0m", color, basic_buf, message_buf, error_buf); + php_cli_server_logf(PHP_CLI_SERVER_LOG_MESSAGE, "\x1b[3%dm%s%s%s\x1b[0m", color, basic_buf, message_buf, error_buf); } else { - php_cli_server_logf("%s%s%s", basic_buf, message_buf, error_buf); + php_cli_server_logf(PHP_CLI_SERVER_LOG_MESSAGE, "%s%s%s", basic_buf, message_buf, error_buf); } efree(basic_buf); @@ -1167,11 +1241,15 @@ static void php_cli_server_log_response(php_cli_server_client *client, int statu } } /* }}} */ -static void php_cli_server_logf(const char *format, ...) /* {{{ */ +static void php_cli_server_logf(int type, const char *format, ...) /* {{{ */ { char *buf = NULL; va_list ap; + if (php_cli_server_log_level < type) { + return; + } + va_start(ap, format); vspprintf(&buf, 0, format, ap); va_end(ap); @@ -1180,9 +1258,7 @@ static void php_cli_server_logf(const char *format, ...) /* {{{ */ return; } - if (sapi_module.log_message) { - sapi_module.log_message(buf, -1); - } + sapi_cli_server_log_write(type, buf); efree(buf); } /* }}} */ @@ -1237,6 +1313,13 @@ static php_socket_t php_network_listen_socket(const char *host, int *port, int s } #endif +#if defined(HAVE_FORK) && defined(SO_REUSEPORT) + if (php_cli_server_workers_max > 1) { + int val = 1; + setsockopt(retval, SOL_SOCKET, SO_REUSEPORT, (char*)&val, sizeof(val)); + } +#endif + if (bind(retval, sa, *socklen) == SOCK_CONN_ERR) { err = php_socket_errno(); if (err == SOCK_EINVAL || err == SOCK_EADDRINUSE) { @@ -1299,9 +1382,6 @@ out: static int php_cli_server_request_ctor(php_cli_server_request *req) /* {{{ */ { -#ifdef ZTS -ZEND_TSRMLS_CACHE_UPDATE(); -#endif req->protocol_version = 0; req->request_uri = NULL; req->request_uri_len = 0; @@ -1457,6 +1537,7 @@ static void normalize_vpath(char **retval, size_t *retval_len, const char *vpath char *p; *retval = NULL; + *retval_len = 0; decoded_vpath = pestrndup(vpath, vpath_len, persistent); if (!decoded_vpath) { @@ -1736,20 +1817,30 @@ static int php_cli_server_client_read_request(php_cli_server_client *client, cha if (err == SOCK_EAGAIN) { return 0; } - *errstr = php_socket_strerror(err, NULL, 0); + + if (php_cli_server_log_level >= PHP_CLI_SERVER_LOG_ERROR) { + *errstr = php_socket_strerror(err, NULL, 0); + } + return -1; } else if (nbytes_read == 0) { - *errstr = estrdup(php_cli_server_request_error_unexpected_eof); + if (php_cli_server_log_level >= PHP_CLI_SERVER_LOG_ERROR) { + *errstr = estrdup(php_cli_server_request_error_unexpected_eof); + } + return -1; } client->parser.data = client; nbytes_consumed = php_http_parser_execute(&client->parser, &settings, buf, nbytes_read); if (nbytes_consumed != (size_t)nbytes_read) { - if (buf[0] & 0x80 /* SSLv2 */ || buf[0] == 0x16 /* SSLv3/TLSv1 */) { - *errstr = estrdup("Unsupported SSL request"); - } else { - *errstr = estrdup("Malformed HTTP request"); + if (php_cli_server_log_level >= PHP_CLI_SERVER_LOG_ERROR) { + if (buf[0] & 0x80 /* SSLv2 */ || buf[0] == 0x16 /* SSLv3/TLSv1 */) { + *errstr = estrdup("Unsupported SSL request"); + } else { + *errstr = estrdup("Malformed HTTP request"); + } } + return -1; } if (client->current_header_name) { @@ -1873,9 +1964,8 @@ static void php_cli_server_client_dtor(php_cli_server_client *client) /* {{{ */ static void php_cli_server_close_connection(php_cli_server *server, php_cli_server_client *client) /* {{{ */ { -#if PHP_DEBUG - php_cli_server_logf("%s Closing", client->addr_str); -#endif + php_cli_server_logf(PHP_CLI_SERVER_LOG_MESSAGE, "%s Closing", client->addr_str); + zend_hash_index_del(&server->clients, client->sock); } /* }}} */ @@ -1982,11 +2072,7 @@ static int php_cli_server_dispatch_script(php_cli_server *server, php_cli_server } { zend_file_handle zfd; - zfd.type = ZEND_HANDLE_FILENAME; - zfd.filename = SG(request_info).path_translated; - zfd.handle.fp = NULL; - zfd.free_filename = 0; - zfd.opened_path = NULL; + zend_stream_init_filename(&zfd, SG(request_info).path_translated); zend_try { php_execute_script(&zfd); } zend_end_try(); @@ -2107,11 +2193,7 @@ static int php_cli_server_dispatch_router(php_cli_server *server, php_cli_server old_cwd[0] = '\0'; php_ignore_value(VCWD_GETCWD(old_cwd, MAXPATHLEN - 1)); - zfd.type = ZEND_HANDLE_FILENAME; - zfd.filename = server->router; - zfd.handle.fp = NULL; - zfd.free_filename = 0; - zfd.opened_path = NULL; + zend_stream_init_filename(&zfd, server->router); zend_try { zval retval; @@ -2176,12 +2258,12 @@ static int php_cli_server_dispatch(php_cli_server *server, php_cli_server_client static int (*send_header_func)(sapi_headers_struct *); send_header_func = sapi_module.send_headers; /* do not generate default content type header */ - SG(sapi_headers).send_default_content_type = 0; + SG(sapi_headers).send_default_content_type = 0; /* we don't want headers to be sent */ sapi_module.send_headers = sapi_cli_server_discard_headers; php_request_shutdown(0); sapi_module.send_headers = send_header_func; - SG(sapi_headers).send_default_content_type = 1; + SG(sapi_headers).send_default_content_type = 1; SG(rfc1867_uploaded_files) = NULL; } if (SUCCESS != php_cli_server_begin_send_static(server, client)) { @@ -2227,12 +2309,39 @@ static void php_cli_server_dtor(php_cli_server *server) /* {{{ */ if (server->router) { pefree(server->router, 1); } +#if HAVE_FORK + if (php_cli_server_workers_max > 1 && + php_cli_server_workers && + getpid() == php_cli_server_master) { + zend_long php_cli_server_worker; + + for (php_cli_server_worker = 0; + php_cli_server_worker < php_cli_server_workers_max; + php_cli_server_worker++) { + int php_cli_server_worker_status; + + do { + if (waitpid(php_cli_server_workers[php_cli_server_worker], + &php_cli_server_worker_status, + 0) == FAILURE) { + /* an extremely bad thing happened */ + break; + } + + } while (!WIFEXITED(php_cli_server_worker_status) && + !WIFSIGNALED(php_cli_server_worker_status)); + } + + free(php_cli_server_workers); + } +#endif } /* }}} */ static void php_cli_server_client_dtor_wrapper(zval *zv) /* {{{ */ { php_cli_server_client *p = Z_PTR_P(zv); + shutdown(p->sock, SHUT_RDWR); closesocket(p->sock); php_cli_server_poller_remove(&p->server->poller, POLLIN | POLLOUT, p->sock); php_cli_server_client_dtor(p); @@ -2290,7 +2399,7 @@ static int php_cli_server_ctor(php_cli_server *server, const char *addr, const c server_sock = php_network_listen_socket(host, &port, SOCK_STREAM, &server->address_family, &server->socklen, &errstr); if (server_sock == SOCK_ERR) { - php_cli_server_logf("Failed to listen on %s:%d (reason: %s)", host, port, errstr ? ZSTR_VAL(errstr) : "?"); + php_cli_server_logf(PHP_CLI_SERVER_LOG_ERROR, "Failed to listen on %s:%d (reason: %s)", host, port, errstr ? ZSTR_VAL(errstr) : "?"); if (errstr) { zend_string_release_ex(errstr, 0); } @@ -2365,14 +2474,15 @@ static int php_cli_server_recv_event_read_request(php_cli_server *server, php_cl char *errstr = NULL; int status = php_cli_server_client_read_request(client, &errstr); if (status < 0) { - if (strcmp(errstr, php_cli_server_request_error_unexpected_eof) == 0 && client->parser.state == s_start_req) { -#if PHP_DEBUG - php_cli_server_logf("%s Closed without sending a request; it was probably just an unused speculative preconnection", client->addr_str); -#endif - } else { - php_cli_server_logf("%s Invalid request (%s)", client->addr_str, errstr); + if (errstr) { + if (strcmp(errstr, php_cli_server_request_error_unexpected_eof) == 0 && client->parser.state == s_start_req) { + php_cli_server_logf(PHP_CLI_SERVER_LOG_MESSAGE, + "%s Closed without sending a request; it was probably just an unused speculative preconnection", client->addr_str); + } else { + php_cli_server_logf(PHP_CLI_SERVER_LOG_ERROR, "%s Invalid request (%s)", client->addr_str, errstr); + } + efree(errstr); } - efree(errstr); php_cli_server_close_connection(server, client); return FAILURE; } else if (status == 1 && client->request.request_method == PHP_HTTP_NOT_IMPLEMENTED) { @@ -2434,10 +2544,12 @@ static int php_cli_server_do_event_for_each_fd_callback(void *_params, php_socke struct sockaddr *sa = pemalloc(server->socklen, 1); client_sock = accept(server->server_sock, sa, &socklen); if (!ZEND_VALID_SOCKET(client_sock)) { - char *errstr; - errstr = php_socket_strerror(php_socket_errno(), NULL, 0); - php_cli_server_logf("Failed to accept a client (reason: %s)", errstr); - efree(errstr); + if (php_cli_server_log_level >= PHP_CLI_SERVER_LOG_ERROR) { + char *errstr = php_socket_strerror(php_socket_errno(), NULL, 0); + php_cli_server_logf(PHP_CLI_SERVER_LOG_ERROR, + "Failed to accept a client (reason: %s)", errstr); + efree(errstr); + } pefree(sa, 1); return SUCCESS; } @@ -2448,16 +2560,17 @@ static int php_cli_server_do_event_for_each_fd_callback(void *_params, php_socke } client = pemalloc(sizeof(php_cli_server_client), 1); if (FAILURE == php_cli_server_client_ctor(client, server, client_sock, sa, socklen)) { - php_cli_server_logf("Failed to create a new request object"); + php_cli_server_logf(PHP_CLI_SERVER_LOG_ERROR, "Failed to create a new request object"); pefree(sa, 1); closesocket(client_sock); return SUCCESS; } -#if PHP_DEBUG - php_cli_server_logf("%s Accepted", client->addr_str); -#endif + + php_cli_server_logf(PHP_CLI_SERVER_LOG_MESSAGE, "%s Accepted", client->addr_str); + zend_hash_index_update_ptr(&server->clients, client_sock, client); - php_cli_server_recv_event_read_request(server, client); + + php_cli_server_poller_add(&server->poller, POLLIN, client->sock); } else { php_cli_server_client *client; if (NULL != (client = zend_hash_index_find_ptr(&server->clients, fd))) { @@ -2498,9 +2611,11 @@ static int php_cli_server_do_event_loop(php_cli_server *server) /* {{{ */ } else { int err = php_socket_errno(); if (err != SOCK_EINTR) { - char *errstr = php_socket_strerror(err, NULL, 0); - php_cli_server_logf("%s", errstr); - efree(errstr); + if (php_cli_server_log_level >= PHP_CLI_SERVER_LOG_ERROR) { + char *errstr = php_socket_strerror(err, NULL, 0); + php_cli_server_logf(PHP_CLI_SERVER_LOG_ERROR, "%s", errstr); + efree(errstr); + } retval = FAILURE; goto out; } @@ -2557,6 +2672,11 @@ int do_cli_server(int argc, char **argv) /* {{{ */ document_root = document_root_tmp; #endif break; + case 'q': + if (php_cli_server_log_level > 1) { + php_cli_server_log_level--; + } + break; } } @@ -2595,33 +2715,23 @@ int do_cli_server(int argc, char **argv) /* {{{ */ sapi_module.phpinfo_as_text = 0; { - char buf[52]; - - if (php_cli_server_get_system_time(buf) != 0) { - memmove(buf, "unknown time, can't be fetched", sizeof("unknown time, can't be fetched")); - } - - printf("PHP %s Development Server started at %s" - "Listening on http://%s\n" - "Document root is %s\n" - "Press Ctrl-C to quit.\n", - PHP_VERSION, buf, server_bind_address, document_root); + php_cli_server_logf( + PHP_CLI_SERVER_LOG_PROCESS, + "PHP %s Development Server (http://%s) started", + PHP_VERSION, server_bind_address); } -#if defined(HAVE_SIGNAL_H) && defined(SIGINT) +#if defined(SIGINT) signal(SIGINT, php_cli_server_sigint_handler); - zend_signal_init(); #endif + +#if defined(SIGPIPE) + signal(SIGPIPE, SIG_IGN); +#endif + + zend_signal_init(); + php_cli_server_do_event_loop(&server); php_cli_server_dtor(&server); return 0; } /* }}} */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: noet sw=4 ts=4 fdm=marker - * vim<600: noet sw=4 ts=4 - */ |