463static void cli_server_init_globals(zend_cli_server_globals *cg)
518static size_t sapi_cli_server_ub_write(
const char *str,
size_t str_length)
524 return php_cli_server_client_send_through(
client, str, str_length);
527static void sapi_cli_server_flush(
void *server_context)
562 if (
SG(sapi_headers).http_status_line) {
563 smart_str_appends(&
buffer,
SG(sapi_headers).http_status_line);
564 smart_str_appendl(&
buffer,
"\r\n", 2);
569 append_essential_headers(&
buffer,
client, 0, sapi_headers);
575 smart_str_appendl(&
buffer,
"\r\n", 2);
579 smart_str_appendl(&
buffer,
"\r\n", 2);
588static char *sapi_cli_server_read_cookies(
void)
598static size_t sapi_cli_server_read_post(
char *
buf,
size_t count_bytes)
601 if (
client->request.content) {
602 size_t content_len =
client->request.content_len;
603 size_t nbytes_copied =
MIN(
client->post_read_offset + count_bytes, content_len) -
client->post_read_offset;
605 client->post_read_offset += nbytes_copied;
606 return nbytes_copied;
611static void sapi_cli_server_register_variable(
zval *track_vars_array,
const char *
key,
const char *
val)
613 char *new_val = (
char *)
val;
625static void sapi_cli_server_register_known_var_char(
zval *track_vars_array,
626 const char *
var_name,
size_t var_name_len,
const char *
value,
size_t value_len)
639static void sapi_cli_server_register_known_var_str(
zval *track_vars_array,
660 char *real_key, *
key;
673 sapi_cli_server_register_variable(track_vars_array,
key,
Z_STRVAL_P(entry));
675 sapi_cli_server_register_variable(track_vars_array, real_key,
Z_STRVAL_P(entry));
684static void sapi_cli_server_register_variables(
zval *track_vars_array)
688 sapi_cli_server_register_known_var_char(track_vars_array,
689 "DOCUMENT_ROOT",
strlen(
"DOCUMENT_ROOT"),
client->server->document_root,
client->server->document_root_len);
693 char addr[64], port[8];
694 const char *addr_start =
ZSTR_VAL(
client->addr_str), *addr_end = tmp;
695 if (addr_start[0] ==
'[') addr_start++;
696 if (addr_end[-1] ==
']') addr_end--;
698 strncpy(port, tmp + 1, 8);
700 size_t addr_len = addr_end - addr_start;
701 strncpy(
addr, addr_start, addr_len);
702 addr[addr_len] =
'\0';
704 sapi_cli_server_register_known_var_char(track_vars_array,
705 "REMOTE_ADDR",
strlen(
"REMOTE_ADDR"),
addr, addr_len);
706 sapi_cli_server_register_known_var_char(track_vars_array,
707 "REMOTE_PORT",
strlen(
"REMOTE_PORT"), port,
strlen(port));
709 sapi_cli_server_register_known_var_str(track_vars_array,
710 "REMOTE_ADDR",
strlen(
"REMOTE_ADDR"),
client->addr_str);
715 sapi_cli_server_register_known_var_str(track_vars_array,
"SERVER_SOFTWARE",
strlen(
"SERVER_SOFTWARE"), tmp);
720 sapi_cli_server_register_known_var_str(track_vars_array,
"SERVER_PROTOCOL",
strlen(
"SERVER_PROTOCOL"), tmp);
723 sapi_cli_server_register_known_var_char(track_vars_array,
727 sapi_cli_server_register_known_var_str(track_vars_array,
"SERVER_PORT",
strlen(
"SERVER_PORT"), tmp);
731 sapi_cli_server_register_known_var_str(track_vars_array,
732 "REQUEST_URI",
strlen(
"REQUEST_URI"),
client->request.request_uri);
733 sapi_cli_server_register_known_var_char(track_vars_array,
734 "REQUEST_METHOD",
strlen(
"REQUEST_METHOD"),
735 SG(request_info).request_method,
strlen(
SG(request_info).request_method));
736 sapi_cli_server_register_known_var_char(track_vars_array,
737 "SCRIPT_NAME",
strlen(
"SCRIPT_NAME"),
client->request.vpath,
client->request.vpath_len);
738 if (
SG(request_info).path_translated) {
739 sapi_cli_server_register_known_var_char(track_vars_array,
740 "SCRIPT_FILENAME",
strlen(
"SCRIPT_FILENAME"),
741 SG(request_info).path_translated,
strlen(
SG(request_info).path_translated));
742 }
else if (
client->server->router) {
743 sapi_cli_server_register_known_var_char(track_vars_array,
744 "SCRIPT_FILENAME",
strlen(
"SCRIPT_FILENAME"),
client->server->router,
client->server->router_len);
746 if (
client->request.path_info) {
747 sapi_cli_server_register_known_var_char(track_vars_array,
748 "PATH_INFO",
strlen(
"PATH_INFO"),
client->request.path_info,
client->request.path_info_len);
750 if (
client->request.path_info_len) {
752 sapi_cli_server_register_known_var_str(track_vars_array,
"PHP_SELF",
strlen(
"PHP_SELF"), tmp);
755 sapi_cli_server_register_known_var_char(track_vars_array,
758 if (
client->request.query_string) {
761 sapi_cli_server_register_variable(track_vars_array,
"QUERY_STRING",
client->request.query_string);
768static void sapi_cli_server_log_write(
int type,
const char *
msg)
772 if (php_cli_server_log_level <
type) {
776 if (!php_cli_server_get_system_time(
buf)) {
777 memmove(
buf,
"unknown time, can't be fetched",
sizeof(
"unknown time, can't be fetched"));
787 if (php_cli_server_workers_max > 1) {
788 fprintf(stderr,
"[%ld] [%s] %s\n", (
long) getpid(),
buf,
msg);
797static void sapi_cli_server_log_message(
const char *
msg,
int syslog_type_int)
805 "Built-in HTTP server",
807 sapi_cli_server_startup,
813 sapi_cli_server_ub_write,
814 sapi_cli_server_flush,
821 sapi_cli_server_send_headers,
824 sapi_cli_server_read_post,
825 sapi_cli_server_read_cookies,
827 sapi_cli_server_register_variables,
828 sapi_cli_server_log_message,
837 FD_ZERO(&poller->
rfds);
838 FD_ZERO(&poller->
wfds);
887 struct socket_entry {
890 } entries[FD_SETSIZE * 2];
892 struct socket_entry *
n = entries, *m;
894 for (i = 0; i < poller->
active.
rfds.fd_count; i++) {
901 for (i = 0; i < poller->
active.
wfds.fd_count; i++) {
902 struct socket_entry *e;
904 for (e = entries; e < m; e++) {
910 assert(
n < entries + FD_SETSIZE * 2);
918 struct socket_entry *e = entries;
930 for (
fd=0 ;
fd<=max_fd ;
fd++) {
948 switch (chunk->type) {
949 case PHP_CLI_SERVER_CHUNK_HEAP:
950 return chunk->data.heap.len;
951 case PHP_CLI_SERVER_CHUNK_IMMORTAL:
952 return chunk->data.immortal.len;
959 switch (chunk->type) {
960 case PHP_CLI_SERVER_CHUNK_HEAP:
961 if (chunk->data.heap.block != chunk) {
962 pefree(chunk->data.heap.block, 1);
965 case PHP_CLI_SERVER_CHUNK_IMMORTAL:
973 for (chunk =
buffer->first; chunk; chunk =
next) {
975 php_cli_server_chunk_dtor(chunk);
993 buffer->last->next = chunk;
1013 for (chunk =
buffer->first; chunk; chunk = chunk->next) {
1014 retval += php_cli_server_chunk_size(chunk);
1023 chunk->type = PHP_CLI_SERVER_CHUNK_IMMORTAL;
1025 chunk->data.immortal.p =
buf;
1026 chunk->data.immortal.len =
len;
1034 chunk->type = PHP_CLI_SERVER_CHUNK_HEAP;
1036 chunk->data.heap.block = block;
1037 chunk->data.heap.p =
buf;
1038 chunk->data.heap.len =
len;
1046 chunk->type = PHP_CLI_SERVER_CHUNK_HEAP;
1048 chunk->data.heap.block = chunk;
1049 chunk->data.heap.p = (
char *)(chunk + 1);
1050 chunk->data.heap.len =
len;
1056 php_cli_server_buffer_dtor(&sender->
buffer);
1061 php_cli_server_buffer_ctor(&sender->
buffer);
1067 size_t _nbytes_sent_total = 0;
1073 ssize_t nbytes_sent;
1077 switch (chunk->type) {
1078 case PHP_CLI_SERVER_CHUNK_HEAP:
1080 nbytes_sent = send(
fd, chunk->data.heap.p, (
int)chunk->data.heap.len, 0);
1082 nbytes_sent = send(
fd, chunk->data.heap.p, chunk->data.heap.len, 0);
1084 if (nbytes_sent < 0) {
1085 *nbytes_sent_total = _nbytes_sent_total;
1088 }
else if (nbytes_sent == chunk->data.heap.len) {
1090 }
else if (nbytes_sent == (ssize_t)chunk->data.heap.len) {
1092 php_cli_server_chunk_dtor(chunk);
1099 chunk->data.heap.p += nbytes_sent;
1100 chunk->data.heap.len -= nbytes_sent;
1102 _nbytes_sent_total += nbytes_sent;
1105 case PHP_CLI_SERVER_CHUNK_IMMORTAL:
1107 nbytes_sent = send(
fd, chunk->data.immortal.p, (
int)chunk->data.immortal.len, 0);
1109 nbytes_sent = send(
fd, chunk->data.immortal.p, chunk->data.immortal.len, 0);
1111 if (nbytes_sent < 0) {
1112 *nbytes_sent_total = _nbytes_sent_total;
1115 }
else if (nbytes_sent == chunk->data.immortal.len) {
1117 }
else if (nbytes_sent == (ssize_t)chunk->data.immortal.len) {
1119 php_cli_server_chunk_dtor(chunk);
1126 chunk->data.immortal.p += nbytes_sent;
1127 chunk->data.immortal.len -= nbytes_sent;
1129 _nbytes_sent_total += nbytes_sent;
1133 *nbytes_sent_total = _nbytes_sent_total;
1142 ssize_t _nbytes_read;
1147 _nbytes_read = read(
fd, chunk->data.heap.p, (
unsigned int)chunk->data.heap.len);
1149 _nbytes_read = read(
fd, chunk->data.heap.p, chunk->data.heap.len);
1151 if (_nbytes_read < 0) {
1153 char *errstr = get_last_error();
1157 php_cli_server_chunk_dtor(chunk);
1161 chunk->data.heap.len = _nbytes_read;
1162 php_cli_server_buffer_append(&sender->
buffer, chunk);
1163 *nbytes_read = _nbytes_read;
1168static int php_cli_is_output_tty(
void)
1173 return php_cli_output_is_tty;
1175#elif defined(PHP_WIN32)
1176static int php_cli_is_output_tty()
1181 return php_cli_output_is_tty;
1188 char *basic_buf, *message_buf =
"", *error_buf =
"";
1189 bool append_error_message = 0;
1191 if (
PG(last_error_message)) {
1195 effective_status = 500;
1198 append_error_message = 1;
1202#if defined(HAVE_UNISTD_H) || defined(PHP_WIN32)
1204 if (effective_status >= 500) {
1207 }
else if (effective_status >= 400) {
1210 }
else if (effective_status >= 200) {
1226 spprintf(&message_buf, 0,
" - %s", message);
1234 if (append_error_message) {
1235 spprintf(&error_buf, 0,
" - %s in %s on line %d",
1256 if (append_error_message) {
1261static void php_cli_server_logf(
int type,
const char *format, ...)
1266 if (php_cli_server_log_level <
type) {
1270 va_start(ap, format);
1278 sapi_cli_server_log_write(
type,
buf);
1287 struct sockaddr *sa =
NULL, **
p, **sal;
1290 if (num_addrs == 0) {
1293 for (
p = sal; *
p;
p++) {
1299 retval = socket((*p)->sa_family, socktype, 0);
1304 switch ((*p)->sa_family) {
1305#if defined(HAVE_GETADDRINFO) && defined(HAVE_IPV6)
1307 sa =
pemalloc(
sizeof(
struct sockaddr_in6), 1);
1308 *(
struct sockaddr_in6 *)sa = *(
struct sockaddr_in6 *)*
p;
1309 ((
struct sockaddr_in6 *)sa)->sin6_port = htons(*port);
1310 *socklen =
sizeof(
struct sockaddr_in6);
1314 sa =
pemalloc(
sizeof(
struct sockaddr_in), 1);
1315 *(
struct sockaddr_in *)sa = *(
struct sockaddr_in *)*
p;
1316 ((
struct sockaddr_in *)sa)->sin_port = htons(*port);
1317 *socklen =
sizeof(
struct sockaddr_in);
1344 *af = sa->sa_family;
1346 if (getsockname(
retval, sa, socklen)) {
1350 switch (sa->sa_family) {
1351#if defined(HAVE_GETADDRINFO) && defined(HAVE_IPV6)
1353 *port = ntohs(((
struct sockaddr_in6 *)sa)->sin6_port);
1357 *port = ntohs(((
struct sockaddr_in *)sa)->sin_port);
1443 static const char *index_files[] = {
"index.php",
"index.html",
NULL };
1445 char *
p =
buf, *prev_path =
NULL, *q, *vpath;
1446 size_t prev_path_len = 0;
1448 memmove(
p, document_root, document_root_len);
1449 p += document_root_len;
1452 if (request->
vpath[0] !=
'/') {
1470 if (sb.st_mode & S_IFDIR) {
1471 const char **
file = index_files;
1499 prev_path_len =
p - q;
1500 prev_path =
pestrndup(q, prev_path_len, 1);
1506 while (prev_path_len--) {
1507 if (prev_path[prev_path_len] ==
'\\') {
1508 prev_path[prev_path_len] =
'/';
1529 if (request->
vpath[i] ==
'\\') {
1530 request->
vpath[i] =
'/';
1538static void normalize_vpath(
char **
retval,
size_t *retval_len,
const char *vpath,
size_t vpath_len,
int persistent)
1540 char *decoded_vpath =
NULL;
1541 char *decoded_vpath_end;
1548 if (!decoded_vpath) {
1552 decoded_vpath_end = decoded_vpath +
php_raw_url_decode(decoded_vpath, (
int)vpath_len);
1556 char *
p = decoded_vpath;
1568 if (
p < decoded_vpath_end && *
p ==
'/') {
1570 while (
n < decoded_vpath_end && *
n ==
'/')
n++;
1572 decoded_vpath_end -=
n -
p;
1575 while (
p < decoded_vpath_end) {
1577 while (
n < decoded_vpath_end && *
n !=
'/')
n++;
1578 if (
n -
p == 2 &&
p[0] ==
'.' &&
p[1] ==
'.') {
1579 if (
p > decoded_vpath) {
1582 if (
p == decoded_vpath) {
1588 if (*(--
p) ==
'/') {
1594 while (
n < decoded_vpath_end && *
n ==
'/')
n++;
1596 decoded_vpath_end -=
n -
p;
1597 }
else if (
n -
p == 1 &&
p[0] ==
'.') {
1598 while (
n < decoded_vpath_end && *
n ==
'/')
n++;
1600 decoded_vpath_end -=
n -
p;
1602 if (
n < decoded_vpath_end) {
1604 while (nn < decoded_vpath_end && *nn ==
'/') nn++;
1606 memmove(
p, nn, decoded_vpath_end - nn);
1607 decoded_vpath_end -= nn -
p;
1614 *decoded_vpath_end =
'\0';
1616 *retval_len = decoded_vpath_end - decoded_vpath;
1621static int php_cli_server_client_read_request_on_message_begin(
php_http_parser *parser)
1626static int php_cli_server_client_read_request_on_path(
php_http_parser *parser,
const char *at,
size_t length)
1635 normalize_vpath(&vpath, &vpath_len, at, length, 1);
1636 client->request.vpath = vpath;
1637 client->request.vpath_len = vpath_len;
1642static int php_cli_server_client_read_request_on_query_string(
php_http_parser *parser,
const char *at,
size_t length)
1647 client->request.query_string_len = length;
1649 ZEND_ASSERT(length <= PHP_HTTP_MAX_HEADER_SIZE && PHP_HTTP_MAX_HEADER_SIZE - length >=
client->request.query_string_len);
1652 client->request.query_string_len += length;
1653 client->request.query_string[
client->request.query_string_len] =
'\0';
1658static int php_cli_server_client_read_request_on_url(
php_http_parser *parser,
const char *at,
size_t length)
1663 client->request.request_uri = zend_string_init(at, length,
true);
1667 ZEND_ASSERT(length <= PHP_HTTP_MAX_HEADER_SIZE && PHP_HTTP_MAX_HEADER_SIZE - length >=
client->request.query_string_len);
1669 client->request.request_uri = cli_concat_persistent_zstr_with_char(
client->request.request_uri, at, length);
1674static int php_cli_server_client_read_request_on_fragment(
php_http_parser *parser,
const char *at,
size_t length)
1694 if (!with_comma || entry ==
NULL) {
1718static zend_string* cli_concat_persistent_zstr_with_char(
zend_string *old_str,
const char *at,
size_t length)
1724 size_t old_length =
ZSTR_LEN(old_str);
1725 zend_string *str = zend_string_extend(old_str, old_length + length,
true);
1732static int php_cli_server_client_read_request_on_header_field(
php_http_parser *parser,
const char *at,
size_t length)
1735 switch (
client->last_header_element) {
1738 php_cli_server_client_save_header(
client);
1742 client->current_header_name = zend_string_init(at, length,
true);
1745 case HEADER_FIELD: {
1747 client->current_header_name = cli_concat_persistent_zstr_with_char(
client->current_header_name, at, length);
1752 client->last_header_element = HEADER_FIELD;
1756static int php_cli_server_client_read_request_on_header_value(
php_http_parser *parser,
const char *at,
size_t length)
1759 switch (
client->last_header_element) {
1762 client->current_header_value = zend_string_init(at, length,
true);
1765 case HEADER_VALUE: {
1767 client->current_header_value = cli_concat_persistent_zstr_with_char(
client->current_header_value, at, length);
1775 client->last_header_element = HEADER_VALUE;
1779static int php_cli_server_client_read_request_on_headers_complete(
php_http_parser *parser)
1782 switch (
client->last_header_element) {
1791 php_cli_server_client_save_header(
client);
1794 client->last_header_element = HEADER_NONE;
1798static int php_cli_server_client_read_request_on_body(
php_http_parser *parser,
const char *at,
size_t length)
1801 if (!
client->request.content) {
1803 client->request.content_len = 0;
1807 client->request.content_len += length;
1811static int php_cli_server_client_read_request_on_message_complete(
php_http_parser *parser)
1815 php_cli_server_request_translate_vpath(
client->server, &
client->request,
client->server->document_root,
client->server->document_root_len);
1816 if (
client->request.vpath) {
1817 const char *vpath =
client->request.vpath;
1818 const char *
end = vpath +
client->request.vpath_len;
1819 const char *
p =
end;
1821 client->request.ext_len = 0;
1832 client->request_read =
true;
1845 php_cli_server_client_read_request_on_message_begin,
1846 php_cli_server_client_read_request_on_path,
1847 php_cli_server_client_read_request_on_query_string,
1848 php_cli_server_client_read_request_on_url,
1849 php_cli_server_client_read_request_on_fragment,
1850 php_cli_server_client_read_request_on_header_field,
1851 php_cli_server_client_read_request_on_header_value,
1852 php_cli_server_client_read_request_on_headers_complete,
1853 php_cli_server_client_read_request_on_body,
1854 php_cli_server_client_read_request_on_message_complete
1856 size_t nbytes_consumed;
1858 if (
client->request_read) {
1861 nbytes_read = recv(
client->sock,
buf,
sizeof(
buf) - 1, 0);
1862 if (nbytes_read < 0) {
1873 }
else if (nbytes_read == 0) {
1875 *errstr =
estrdup(php_cli_server_request_error_unexpected_eof);
1882 if (nbytes_consumed != (
size_t)nbytes_read) {
1884 if ((
buf[0] & 0x80) ||
buf[0] == 0x16 ) {
1885 *errstr =
estrdup(
"Unsupported SSL request");
1887 *errstr =
estrdup(
"Malformed HTTP request");
1894 return client->request_read ? 1: 0;
1900 struct timeval
tv = { 10, 0 };
1902 int nbytes_left = (int)str_len;
1904 ssize_t nbytes_left = (ssize_t)str_len;
1910 ssize_t nbytes_sent;
1913 nbytes_sent = send(
client->sock, str + str_len - nbytes_left, nbytes_left, 0);
1914 if (nbytes_sent < 0) {
1930 nbytes_left -= nbytes_sent;
1931 }
while (nbytes_left > 0);
1962 client->sock = client_sock;
1964 client->addr_len = addr_len;
1970 client->addr_str = zend_string_dup(tmp_addr,
true);
1975 client->request_read =
false;
1977 client->last_header_element = HEADER_NONE;
1981 client->post_read_offset = 0;
1983 php_cli_server_request_ctor(&
client->request);
1985 client->content_sender_initialized =
false;
1991 php_cli_server_request_dtor(&
client->request);
1992 if (
client->file_fd >= 0) {
1999 if (
client->content_sender_initialized) {
2003 php_cli_server_content_sender_dtor(&
client->content_sender);
2017 const char *status_string = get_status_string(
status);
2018 const char *content_template = get_template_string(
status);
2019 char *errstr = get_last_error();
2020 assert(status_string && content_template);
2022 php_cli_server_content_sender_ctor(&
client->content_sender);
2023 client->content_sender_initialized =
true;
2029 static const char prologue_template[] =
"<!doctype html><html><head><title>%d %s</title>";
2034 snprintf(chunk->data.heap.p, chunk->data.heap.len, prologue_template,
status, status_string);
2035 chunk->data.heap.len =
strlen(chunk->data.heap.p);
2036 php_cli_server_buffer_append(&
client->content_sender.buffer, chunk);
2039 php_cli_server_chunk *chunk = php_cli_server_chunk_immortal_new(php_cli_server_css,
sizeof(php_cli_server_css) - 1);
2043 php_cli_server_buffer_append(&
client->content_sender.buffer, chunk);
2046 static const char template[] =
"</head><body>";
2047 php_cli_server_chunk *chunk = php_cli_server_chunk_immortal_new(
template,
sizeof(
template) - 1);
2051 php_cli_server_buffer_append(&
client->content_sender.buffer, chunk);
2058 snprintf(chunk->data.heap.p, chunk->data.heap.len, content_template, status_string,
ZSTR_VAL(escaped_request_uri));
2059 chunk->data.heap.len =
strlen(chunk->data.heap.p);
2060 php_cli_server_buffer_append(&
client->content_sender.buffer, chunk);
2063 static const char epilogue_template[] =
"</body></html>";
2064 php_cli_server_chunk *chunk = php_cli_server_chunk_immortal_new(epilogue_template,
sizeof(epilogue_template) - 1);
2068 php_cli_server_buffer_append(&
client->content_sender.buffer, chunk);
2082 smart_str_appends_ex(&
buffer,
"Content-Type: text/html; charset=UTF-8\r\n", 1);
2083 smart_str_appends_ex(&
buffer,
"Content-Length: ", 1);
2084 smart_str_append_unsigned_ex(&
buffer, php_cli_server_buffer_size(&
client->content_sender.buffer), 1);
2085 smart_str_appendl_ex(&
buffer,
"\r\n", 2, 1);
2087 smart_str_appends_ex(&
buffer,
"Allow: ", 1);
2089 smart_str_appends_ex(&
buffer,
", ", 1);
2091 smart_str_appends_ex(&
buffer,
", ", 1);
2093 smart_str_appendl_ex(&
buffer,
"\r\n", 2, 1);
2095 smart_str_appendl_ex(&
buffer,
"\r\n", 2, 1);
2099 smart_str_free_ex(&
buffer, 1);
2102 php_cli_server_buffer_prepend(&
client->content_sender.buffer, chunk);
2105 php_cli_server_log_response(
client,
status, errstr ? errstr :
"?");
2106 php_cli_server_poller_add(&server->poller,
POLLOUT,
client->sock);
2110 if (escaped_request_uri) {
2111 zend_string_free(escaped_request_uri);
2119 if (escaped_request_uri) {
2120 zend_string_free(escaped_request_uri);
2127 if (
strlen(
client->request.path_translated) !=
client->request.path_translated_len) {
2129 return php_cli_server_send_error_page(server,
client, 400);
2152 return php_cli_server_send_error_page(server,
client, 405);
2155 if (
client->request.path_translated &&
strlen(
client->request.path_translated) !=
client->request.path_translated_len) {
2157 return php_cli_server_send_error_page(server,
client, 400);
2166 if (
client->request.path_translated &&
2167 (
'.' ==
client->request.path_translated[
client->request.path_translated_len-1] ||
2168 ' ' ==
client->request.path_translated[
client->request.path_translated_len-1])) {
2169 return php_cli_server_send_error_page(server,
client, 500);
2172 fd =
client->request.path_translated ? php_win32_ioutil_open(
client->request.path_translated, O_RDONLY): -1;
2174 fd =
client->request.path_translated ? open(
client->request.path_translated, O_RDONLY): -1;
2177 return php_cli_server_send_error_page(server,
client, 404);
2180 php_cli_server_content_sender_ctor(&
client->content_sender);
2181 client->content_sender_initialized =
true;
2189 const char *mime_type = get_mime_type(server,
client->request.ext,
client->request.ext_len);
2194 php_cli_server_log_response(
client, 500,
NULL);
2199 smart_str_appendl_ex(&
buffer,
"Content-Type: ",
sizeof(
"Content-Type: ") - 1, 1);
2200 smart_str_appends_ex(&
buffer, mime_type, 1);
2201 if (
strncmp(mime_type,
"text/", 5) == 0) {
2202 smart_str_appends_ex(&
buffer,
"; charset=UTF-8", 1);
2204 smart_str_appendl_ex(&
buffer,
"\r\n", 2, 1);
2206 smart_str_appends_ex(&
buffer,
"Content-Length: ", 1);
2207 smart_str_append_unsigned_ex(&
buffer,
client->request.sb.st_size, 1);
2208 smart_str_appendl_ex(&
buffer,
"\r\n", 2, 1);
2209 smart_str_appendl_ex(&
buffer,
"\r\n", 2, 1);
2212 smart_str_free_ex(&
buffer, 1);
2213 php_cli_server_log_response(
client, 500,
NULL);
2216 php_cli_server_buffer_append(&
client->content_sender.buffer, chunk);
2218 php_cli_server_log_response(
client, 200,
NULL);
2219 php_cli_server_poller_add(&server->poller,
POLLOUT,
client->sock);
2226 php_cli_server_client_populate_request_info(
client, &
SG(request_info));
2230 SG(sapi_headers).http_response_code = 200;
2234 PG(during_request_startup) = 0;
2242 php_cli_server_close_connection(server,
client);
2243 destroy_request_info(&
SG(request_info));
2244 SG(server_context) =
NULL;
2245 SG(rfc1867_uploaded_files) =
NULL;
2246 SG(request_parse_body_context).throw_exceptions =
false;
2247 memset(&
SG(request_parse_body_context).options_cache, 0,
sizeof(
SG(request_parse_body_context).options_cache));
2253 bool decline =
false;
2271 CG(skip_shebang) =
true;
2284 if (old_cwd[0] !=
'\0') {
2296 int is_static_file = 0;
2300 if (
client->request.ext_len != 3
2301 || (
ext[0] !=
'p' &&
ext[0] !=
'P') || (
ext[1] !=
'h' &&
ext[1] !=
'H') || (
ext[2] !=
'p' &&
ext[2] !=
'P')
2302 || !
client->request.path_translated) {
2306 if (server->router || !is_static_file) {
2307 if (
FAILURE == php_cli_server_request_startup(server,
client)) {
2308 php_cli_server_request_shutdown(server,
client);
2313 if (server->router) {
2314 if (!php_cli_server_dispatch_router(server,
client)) {
2315 php_cli_server_request_shutdown(server,
client);
2320 if (!is_static_file) {
2322 if (
SUCCESS == php_cli_server_dispatch_script(server,
client)
2323 ||
FAILURE == php_cli_server_send_error_page(server,
client, 500)) {
2325 SG(sapi_headers).send_default_content_type = 0;
2327 php_cli_server_request_shutdown(server,
client);
2331 if (server->router) {
2335 SG(sapi_headers).send_default_content_type = 0;
2337 sapi_module.send_headers = sapi_cli_server_discard_headers;
2340 SG(sapi_headers).send_default_content_type = 1;
2341 SG(rfc1867_uploaded_files) =
NULL;
2342 SG(request_parse_body_context).throw_exceptions =
false;
2343 memset(&
SG(request_parse_body_context).options_cache, 0,
sizeof(
SG(request_parse_body_context).options_cache));
2345 if (
FAILURE == php_cli_server_begin_send_static(server,
client)) {
2346 php_cli_server_close_connection(server,
client);
2348 SG(server_context) =
NULL;
2352 SG(server_context) =
NULL;
2353 destroy_request_info(&
SG(request_info));
2365 for (pair = mime_type_map; pair->
ext; pair++) {
2367 zend_hash_str_add_ptr(&server->extension_mime_types, pair->
ext, ext_len, (
void*)pair->
mime_type);
2381 if (server->document_root) {
2382 pefree(server->document_root, 1);
2384 if (server->router) {
2385 pefree(server->router, 1);
2388 if (php_cli_server_workers_max > 1 &&
2389 php_cli_server_workers &&
2390 getpid() == php_cli_server_master) {
2393 for (php_cli_server_worker = 0;
2394 php_cli_server_worker < php_cli_server_workers_max;
2395 php_cli_server_worker++) {
2396 int php_cli_server_worker_status;
2399 if (waitpid(php_cli_server_workers[php_cli_server_worker],
2400 &php_cli_server_worker_status,
2406 }
while (!WIFEXITED(php_cli_server_worker_status) &&
2407 !WIFSIGNALED(php_cli_server_worker_status));
2410 pefree(php_cli_server_workers, 1);
2415static void php_cli_server_client_dtor_wrapper(
zval *
zv)
2421 php_cli_server_poller_remove(&
p->server->poller,
POLLIN |
POLLOUT,
p->sock);
2422 php_cli_server_client_dtor(
p);
2432static char *php_cli_server_parse_addr(
const char *
addr,
int *pport) {
2433 const char *
p, *
end;
2436 if (
addr[0] ==
'[') {
2451 port = strtol(
p + 1, (
char**)&
p, 10);
2456 if (port < 0 || port > 65535) {
2472 port = strtol(
end + 1, (
char**)&
p, 10);
2477 if (port < 0 || port > 65535) {
2485#if defined(HAVE_PRCTL) || defined(HAVE_PROCCTL)
2486static void php_cli_server_worker_install_pdeathsig(
void)
2489#if defined(HAVE_PRCTL)
2490 prctl(PR_SET_PDEATHSIG,
SIGTERM);
2491#elif defined(HAVE_PROCCTL)
2493 procctl(
P_PID, 0, PROC_PDEATHSIG_CTL, &signal);
2497 if (getppid() != php_cli_server_master) {
2503static void php_cli_server_startup_workers(
void) {
2504 char *workers =
getenv(
"PHP_CLI_SERVER_WORKERS");
2510 php_cli_server_workers_max =
ZEND_ATOL(workers);
2511 if (php_cli_server_workers_max > 1) {
2515 php_cli_server_workers_max,
sizeof(pid_t), 1);
2517 php_cli_server_master = getpid();
2519 for (php_cli_server_worker = 0;
2520 php_cli_server_worker < php_cli_server_workers_max;
2521 php_cli_server_worker++) {
2526 php_cli_server_workers_max =
2527 php_cli_server_worker + 1;
2529 }
else if (pid == 0) {
2530#if defined(HAVE_PRCTL) || defined(HAVE_PROCCTL)
2531 php_cli_server_worker_install_pdeathsig();
2535 php_cli_server_workers[php_cli_server_worker] = pid;
2539 fprintf(stderr,
"number of workers must be larger than 1\n");
2542 fprintf(stderr,
"forking is not supported on this platform\n");
2551 char *_document_root =
NULL;
2552 char *_router =
NULL;
2556 host = php_cli_server_parse_addr(
addr, &port);
2563 server_sock = php_network_listen_socket(host, &port,
SOCK_STREAM, &server->address_family, &server->socklen, &errstr);
2580 server->server_sock = server_sock;
2582 php_cli_server_startup_workers();
2584 php_cli_server_poller_ctor(&server->poller);
2586 php_cli_server_poller_add(&server->poller,
POLLIN, server_sock);
2588 server->host = host;
2589 server->port = port;
2594 size_t document_root_len =
strlen(document_root);
2595 _document_root =
pestrndup(document_root, document_root_len, 1);
2596 server->document_root = _document_root;
2597 server->document_root_len = document_root_len;
2601 size_t router_len =
strlen(router);
2602 _router =
pestrndup(router, router_len, 1);
2603 server->router = _router;
2604 server->router_len = router_len;
2606 server->router =
NULL;
2607 server->router_len = 0;
2610 php_cli_server_mime_type_ctor(server, mime_type_map);
2612 server->is_running = 1;
2618 if (_document_root) {
2619 pefree(_document_root, 1);
2624 if (server_sock > -1) {
2633 char *errstr =
NULL;
2635 switch (php_cli_server_client_read_request(
client, &errstr)) {
2638 if (
strcmp(errstr, php_cli_server_request_error_unexpected_eof) == 0 &&
client->parser.state ==
s_start_req) {
2640 "%s Closed without sending a request; it was probably just an unused speculative preconnection",
ZSTR_VAL(
client->addr_str));
2646 php_cli_server_close_connection(server,
client);
2650 return php_cli_server_send_error_page(server,
client, 501);
2652 php_cli_server_poller_remove(&server->poller,
POLLIN,
client->sock);
2653 return php_cli_server_dispatch(server,
client);
2655 php_cli_server_poller_add(&server->poller,
POLLIN,
client->sock);
2665 if (
client->content_sender_initialized) {
2666 if (
client->file_fd >= 0 && !
client->content_sender.buffer.first) {
2668 if (!php_cli_server_content_sender_pull(&
client->content_sender,
client->file_fd, &nbytes_read)) {
2669 php_cli_server_close_connection(server,
client);
2672 if (nbytes_read == 0) {
2679 int err = php_cli_server_content_sender_send(&
client->content_sender,
client->sock, &nbytes_sent);
2681 php_cli_server_close_connection(server,
client);
2685 if (!
client->content_sender.buffer.first &&
client->file_fd < 0) {
2686 php_cli_server_close_connection(server,
client);
2703 if (server->server_sock ==
fd) {
2707 struct sockaddr *sa =
pemalloc(server->socklen, 1);
2708 client_sock = accept(server->server_sock, sa, &socklen);
2714 "Failed to accept a client (reason: %s)", errstr);
2727 php_cli_server_client_ctor(
client, server, client_sock, sa, socklen);
2731 zend_hash_index_update_ptr(&server->clients, client_sock,
client);
2733 php_cli_server_poller_add(&server->poller,
POLLIN,
client->sock);
2736 if (
NULL != (
client = zend_hash_index_find_ptr(&server->clients,
fd))) {
2748static void php_cli_server_do_event_for_each_fd(
php_cli_server *server,
2758 if (
SUCCESS != php_cli_server_poller_iter_on_active(&server->poller, ¶ms, php_cli_server_do_event_for_each_fd_callback)) {
2766 while (server->is_running) {
2767 struct timeval
tv = { 1, 0 };
2768 int n = php_cli_server_poller_poll(&server->poller, &
tv);
2770 php_cli_server_do_event_for_each_fd(server,
2771 php_cli_server_recv_event_read_request,
2772 php_cli_server_send_event);
2773 }
else if (
n == 0) {
2794static void php_cli_server_sigint_handler(
int sig)
2803 char *php_optarg =
NULL;
2806 const char *server_bind_address =
NULL;
2808 const char *document_root =
NULL;
2813 const char *router =
NULL;
2816 while ((c =
php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2))!=-1) {
2819 server_bind_address = php_optarg;
2823 document_root = php_optarg;
2827 fprintf(stderr,
"Document root path is too long.\n");
2830 memmove(document_root_tmp, php_optarg, k + 1);
2834 document_root_tmp[k] =
'\0';
2836 }
while (
'"' == document_root_tmp[k] ||
' ' == document_root_tmp[k]);
2837 document_root = document_root_tmp;
2841 if (php_cli_server_log_level > 1) {
2842 php_cli_server_log_level--;
2848 if (document_root) {
2852 fprintf(stderr,
"Directory %s does not exist.\n", document_root);
2856 fprintf(stderr,
"%s is not a directory.\n", document_root);
2860 document_root = document_root_buf;
2867#elif defined(HAVE_GETWD)
2870 document_root =
ret ? document_root_buf:
".";
2873 if (argc > php_optind) {
2874 router = argv[php_optind];
2877 if (
FAILURE == php_cli_server_ctor(&server, server_bind_address, document_root, router)) {
2884 bool ipv6 =
strchr(server.host,
':');
2885 php_cli_server_logf(
2887 "PHP %s Development Server (http://%s%s%s:%d) started",
2889 ipv6 ?
"]" :
"", server.port);
2893 signal(
SIGINT, php_cli_server_sigint_handler);
2902 if (
SUCCESS != php_cli_server_do_event_loop(&server)) {
2905 php_cli_server_dtor(&server);