summaryrefslogtreecommitdiff
path: root/sapi/cli/php_cli_server.c
diff options
context:
space:
mode:
Diffstat (limited to 'sapi/cli/php_cli_server.c')
-rw-r--r--sapi/cli/php_cli_server.c296
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
- */