From 35fd9f297eda05c57756a715ddc41ba273c9eef3 Mon Sep 17 00:00:00 2001 From: richardtmiles Date: Thu, 25 Apr 2024 08:04:01 -0600 Subject: [PATCH 01/12] Adding PHP_FUNCTION apache_connection_stream to expose ap_get_conn_socket Apache CGI doesn't support WebSocket Upgrade requests out-of-the-box. php://input uses apache brigade/buckets but these are not fired with websocket Input; moreover, the php://input stream is cached and not seekable. php://stdio is the php code buffer so it is not useful here either. This allow a direct connection to the client. --- .DS_Store | Bin 0 -> 10244 bytes .gitignore | 6 + ext/.DS_Store | Bin 0 -> 6148 bytes ext/standard/php_fopen_wrapper.c | 18 ++- sapi/apache2handler/php_functions.c | 123 ++++++++++++++++++++ sapi/apache2handler/php_functions_arginfo.h | 4 + 6 files changed, 146 insertions(+), 5 deletions(-) create mode 100644 .DS_Store create mode 100644 ext/.DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..8060b2bde57951e48b081ddb9ebc6b9af91076fe GIT binary patch literal 10244 zcmeHMPiS0482@H>lWd5r)zCJ!nk?p^U|V*R_E3ZErfd!-i0E3-fRLmcvT1j-+ubDX zL1c^dU_pp@QBm;VzZL{@5fqUg>M56krBM%21SvGMl$N%a9{PPV^WOVr-|V)hMan#Q z@7wI}``+(2znM30X957?&{94PkOY7@o)ot{h8N30IGv#*qQ7|)qriHABT$4{yv`Tr zLfP&M=YVs-Ip7>{4mbz?4-Vivn~r#oitg$ha1J;JdSFAjxnQST}4$6LY0Hi%tEhFgk;CM zq)Z2qQPEwU1I~f41GIMUhCQ@qhbmvcr*lWjrHU%pdfT2=Drd7Z#qtcI@z*{(nC`ps z+i0`Q{6l6|3Y5>9OGJ6Uz)hL{hHL`*OtnG0vpbG2ZvL{ z+vV{>0hVBfvx8W<8qVMvwmvcH`O$D6uB3Su*8|ZhB!dprGY@&(VVO3xz?rvAl$epKlbi)j4Uy!;>*rqBRGt|T2h&_vIzH?#S>tD zb^4c|pHa`7uH#OGQO=jRv_gC_Z2LTp0mJ{u#t-xm`WWZ@?~~r~o!)$>YdNb8=e&`P z^NN?AhIv>p`h9f$l;qVI=Z!v_Sp9WV&$B)r_-w;DFN{}+w+rW!GWPwF+&wm04gI%< z!Wmq{2cKU*w{}%(Xt)n|pm~;|XMp6O#})Kp5ekM6o8SM$F|Oh8%3JBOn!o&7&Thjw zuXwWV@#5SLRf89{+|s-f4ioHIsj|aRqobz_(b-&kemZ{>1tDxIvzl&Y;%cKj846dQ~{SPm1c>Ugx6)0(* z#r1%oM+@g&dZ*PwT6))CHc_tQtJSAGFIYYAeS3z%f8a~v??mP*_QWzeV!8qGY$MJ! z9Ek4lG#K~bDw^dOI@Dq-25&$PUWZrscmTM&fEs*^^MBg6_V_!hFBl&5f(K z$N%=)?c)Kn4d=X(@8q~6Zg)5boCD5*b~+GMWJ%ipA8Y>q|8`1pT+RXKK${&9v1~4T z2ubzcdWfEu=Se)L@T7zC>MF_-yvP+iM!6l2f6#XP^LSEjiXt8OIiq7;Mf?Vjw*NDr X#XbT5eFkp*58&4Sa&ZEW*!urIn%a7w literal 0 HcmV?d00001 diff --git a/.gitignore b/.gitignore index b76b5a787caa..98f5717ff1fa 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,10 @@ # Various temporary generated files *.tmp +# editor includes +*.iml +.idea + # ------------------------------------------------------------------------------ # Generated by the PHP build system # ------------------------------------------------------------------------------ @@ -260,6 +264,7 @@ phpt.* # Temporary PHP INI configuration file(s) for tests, generated by `make test` tmp-php.ini +php.ini # ------------------------------------------------------------------------------ # Generated by GCC's gcov and LCOV via build/Makefile.gcov @@ -270,6 +275,7 @@ tmp-php.ini /gcovr_html/ /lcov_html/ /php_lcov.info +compile_commands.json # ------------------------------------------------------------------------------ # Archives generated during the PHP release process diff --git a/ext/.DS_Store b/ext/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..733a361d0cdb76ac394c51eac8c72ea07be3d93b GIT binary patch literal 6148 zcmeHKy-LGS6h1dC7=MP2LYHhVMeODnOLTIPzJP5bR%lF6yE)_md;lk3L1!lycNZ5y z+y%i$@H_V&+QhWo6p?e_+%Gvl_nv%#oE#!DopIbIY7mhRW%QP@%nTv_8qn|_ z?d~8=GPasm@hSXm2Ke38D5jK>(Q)zpJ?~FFNg74{QQC*azq-CTczb``DSG1t-fTGC z)!TeP8ZF_pj0V($4(X_Lu6enwMikG#-)N2H{BQdt>9mgdQN?=9_8r&b(>YzBnp5!5 zqce(26`izJX2le1y8PVQYkpWxDti^HS`=5T%WFPyT`J0eGN26n6a(nlEdHuPU6lc4 zKpB`Yz~6@e$`}gf4(+D{iyZ-gb(pQ-nr8{l2?RsI+#x&=<5Gbx)yRlpTsrK5#D#*n zLzhlQh7Thr8<|jyJstZ8Hk?f8P*-I@87MPQbGHrd|GV?&|8kPvDFe#Dzhc1Dqi)o} zBS~+qJRJAh0D1^z;kex4Bn20?6(g2g@jBEB>;Yc@L&4l3ED-$>ur%nR4E!nsU*&#r AUjP6A literal 0 HcmV?d00001 diff --git a/ext/standard/php_fopen_wrapper.c b/ext/standard/php_fopen_wrapper.c index cca9445801f0..8ad26d161058 100644 --- a/ext/standard/php_fopen_wrapper.c +++ b/ext/standard/php_fopen_wrapper.c @@ -243,7 +243,8 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c } if (!strcasecmp(path, "input")) { - php_stream_input_t *input; + + php_stream_input_t *input; if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) { php_stream_wrapper_warn(wrapper, context, options, @@ -253,14 +254,21 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c } input = ecalloc(1, sizeof(*input)); + if ((input->body = SG(request_info).request_body)) { + php_stream_rewind(input->body); - } else { + + } else { + input->body = php_stream_temp_create_ex(TEMP_STREAM_DEFAULT, SAPI_POST_BLOCK_SIZE, PG(upload_tmp_dir)); + SG(request_info).request_body = input->body; - } + + } return php_stream_alloc(&php_stream_input_ops, input, 0, "rb"); + } if (!strcasecmp(path, "stdin")) { @@ -289,7 +297,7 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c if (!strcmp(sapi_module.name, "cli")) { static int cli_out = 0; fd = STDOUT_FILENO; - if (cli_out++) { + if (cli_out) { fd = dup(fd); } else { cli_out = 1; @@ -305,7 +313,7 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c if (!strcmp(sapi_module.name, "cli")) { static int cli_err = 0; fd = STDERR_FILENO; - if (cli_err++) { + if (cli_err) { fd = dup(fd); } else { cli_err = 1; diff --git a/sapi/apache2handler/php_functions.c b/sapi/apache2handler/php_functions.c index 1baa1f5225e0..b249b8d4137b 100644 --- a/sapi/apache2handler/php_functions.c +++ b/sapi/apache2handler/php_functions.c @@ -46,6 +46,8 @@ #include "php_apache.h" #include "php_functions_arginfo.h" +#include "ext/standard/sha1.h" +#include "ext/standard/php_standard.h" #ifdef ZTS int php_apache2_info_id; @@ -205,6 +207,127 @@ PHP_FUNCTION(apache_response_headers) } /* }}} */ +/* {{{ Sends response headers required for a WebSocket connection */ +PHP_FUNCTION(apache_websocket_accept) { + php_struct *ctx = SG(server_context); + request_rec *r; + char *key; + char *concat; + unsigned char sha1str[20]; // SHA1 hash size + zend_string *encoded; + char *result; + sapi_header_line ctr = {0}; + apr_socket_t *apr_sock; + int fd; + + r = ctx->r; + + ZEND_PARSE_PARAMETERS_NONE(); + + // Retrieve the Sec-WebSocket-Key header + const apr_array_header_t *fields; + apr_table_entry_t *e = 0; + fields = apr_table_elts(r->headers_in); + e = (apr_table_entry_t *) fields->elts; + for (int i = 0; i < fields->nelts; i++) { + if (strcasecmp(e[i].key, "Sec-WebSocket-Key") == 0) { + key = e[i].val; + break; + } + } + + if (!key) { + php_error_docref(NULL, E_WARNING, "Sec-WebSocket-Key header not found."); + RETURN_FALSE; + } + + size_t key_len = strlen(key); + + // Prepare the source string with the GUID appended + concat = emalloc(key_len + 37); // Key length + GUID length + // @link https://www.rfc-editor.org/rfc/rfc6455 + snprintf(concat, key_len + 37, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", key); + + // Compute the SHA1 hash + PHP_SHA1_CTX context; + PHP_SHA1Init(&context); + PHP_SHA1Update(&context, (unsigned char *)concat, strlen(concat)); + PHP_SHA1Final(sha1str, &context); + efree(concat); + + // Base64 encode the SHA1 hash + encoded = php_base64_encode(sha1str, sizeof(sha1str)); + result = estrndup(ZSTR_VAL(encoded), ZSTR_LEN(encoded)); + zend_string_release(encoded); + + // Send the Upgrade header + ctr.line = "Upgrade: websocket"; + ctr.line_len = sizeof("Upgrade: websocket") - 1; + sapi_header_op(SAPI_HEADER_REPLACE, &ctr); + + // Send the Connection header + ctr.line = "Connection: Upgrade"; + ctr.line_len = sizeof("Connection: Upgrade") - 1; + sapi_header_op(SAPI_HEADER_REPLACE, &ctr); + + // Send the Sec-WebSocket-Accept header + ctr.line_len = sizeof("Sec-WebSocket-Accept: ") - 1 + ZSTR_LEN(encoded) + 1; + char *sec_accept_header = emalloc(ctr.line_len); + snprintf(sec_accept_header, ctr.line_len, "Sec-WebSocket-Accept: %s", result); + ctr.line = sec_accept_header; + sapi_header_op(SAPI_HEADER_REPLACE, &ctr); + efree(sec_accept_header); + + // Flush all output buffers to ensure headers are sent immediately + php_output_flush_all(); + + efree(result); + + apr_sock = ap_get_conn_socket(r->connection); + + // Get the native descriptor + apr_os_sock_get(&fd, apr_sock); + + // Use php_stream_sock_open_from_socket() to create a PHP stream + php_stream *stream = php_stream_sock_open_from_socket(fd, NULL); + + if (!stream) { + RETURN_FALSE; + } + + php_stream_to_zval(stream, return_value); + +} +/* }}} */ + +/* {{{ Sends response headers required for a WebSocket connection */ +PHP_FUNCTION(apache_websocket_stream) { + php_struct *ctx = SG(server_context); + request_rec *r; + apr_socket_t *apr_sock; + int fd; + + r = ctx->r; + + ZEND_PARSE_PARAMETERS_NONE(); + + apr_sock = ap_get_conn_socket(r->connection); + + // Get the native descriptor + apr_os_sock_get(&fd, apr_sock); + + // Use php_stream_sock_open_from_socket() to create a PHP stream + php_stream *stream = php_stream_sock_open_from_socket(fd, NULL); + + if (!stream) { + RETURN_FALSE; + } + + php_stream_to_zval(stream, return_value); + +} +/* }}} */ + /* {{{ Get and set Apache request notes */ PHP_FUNCTION(apache_note) { diff --git a/sapi/apache2handler/php_functions_arginfo.h b/sapi/apache2handler/php_functions_arginfo.h index f816c29f8621..4e8566084434 100644 --- a/sapi/apache2handler/php_functions_arginfo.h +++ b/sapi/apache2handler/php_functions_arginfo.h @@ -41,6 +41,8 @@ ZEND_FUNCTION(apache_lookup_uri); ZEND_FUNCTION(virtual); ZEND_FUNCTION(apache_request_headers); ZEND_FUNCTION(apache_response_headers); +ZEND_FUNCTION(apache_websocket_accept); +ZEND_FUNCTION(apache_websocket_stream); ZEND_FUNCTION(apache_note); ZEND_FUNCTION(apache_setenv); ZEND_FUNCTION(apache_getenv); @@ -53,6 +55,8 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(apache_request_headers, arginfo_apache_request_headers) ZEND_RAW_FENTRY("getallheaders", zif_apache_request_headers, arginfo_getallheaders, 0, NULL, NULL) ZEND_FE(apache_response_headers, arginfo_apache_response_headers) + ZEND_FE(apache_websocket_accept, NULL) + ZEND_FE(apache_websocket_stream, NULL) ZEND_FE(apache_note, arginfo_apache_note) ZEND_FE(apache_setenv, arginfo_apache_setenv) ZEND_FE(apache_getenv, arginfo_apache_getenv) From c6f239a0ec2c35c32b8663a208096c69ab3c6be0 Mon Sep 17 00:00:00 2001 From: richardtmiles Date: Thu, 25 Apr 2024 08:05:21 -0600 Subject: [PATCH 02/12] Adding PHP_FUNCTION apache_connection_stream to expose ap_get_conn_socket for CGI WebSockets --- sapi/apache2handler/php_functions.c | 97 +-------------------- sapi/apache2handler/php_functions_arginfo.h | 6 +- 2 files changed, 4 insertions(+), 99 deletions(-) diff --git a/sapi/apache2handler/php_functions.c b/sapi/apache2handler/php_functions.c index b249b8d4137b..c3ef316ffa9f 100644 --- a/sapi/apache2handler/php_functions.c +++ b/sapi/apache2handler/php_functions.c @@ -207,101 +207,8 @@ PHP_FUNCTION(apache_response_headers) } /* }}} */ -/* {{{ Sends response headers required for a WebSocket connection */ -PHP_FUNCTION(apache_websocket_accept) { - php_struct *ctx = SG(server_context); - request_rec *r; - char *key; - char *concat; - unsigned char sha1str[20]; // SHA1 hash size - zend_string *encoded; - char *result; - sapi_header_line ctr = {0}; - apr_socket_t *apr_sock; - int fd; - - r = ctx->r; - - ZEND_PARSE_PARAMETERS_NONE(); - - // Retrieve the Sec-WebSocket-Key header - const apr_array_header_t *fields; - apr_table_entry_t *e = 0; - fields = apr_table_elts(r->headers_in); - e = (apr_table_entry_t *) fields->elts; - for (int i = 0; i < fields->nelts; i++) { - if (strcasecmp(e[i].key, "Sec-WebSocket-Key") == 0) { - key = e[i].val; - break; - } - } - - if (!key) { - php_error_docref(NULL, E_WARNING, "Sec-WebSocket-Key header not found."); - RETURN_FALSE; - } - - size_t key_len = strlen(key); - - // Prepare the source string with the GUID appended - concat = emalloc(key_len + 37); // Key length + GUID length - // @link https://www.rfc-editor.org/rfc/rfc6455 - snprintf(concat, key_len + 37, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", key); - - // Compute the SHA1 hash - PHP_SHA1_CTX context; - PHP_SHA1Init(&context); - PHP_SHA1Update(&context, (unsigned char *)concat, strlen(concat)); - PHP_SHA1Final(sha1str, &context); - efree(concat); - - // Base64 encode the SHA1 hash - encoded = php_base64_encode(sha1str, sizeof(sha1str)); - result = estrndup(ZSTR_VAL(encoded), ZSTR_LEN(encoded)); - zend_string_release(encoded); - - // Send the Upgrade header - ctr.line = "Upgrade: websocket"; - ctr.line_len = sizeof("Upgrade: websocket") - 1; - sapi_header_op(SAPI_HEADER_REPLACE, &ctr); - - // Send the Connection header - ctr.line = "Connection: Upgrade"; - ctr.line_len = sizeof("Connection: Upgrade") - 1; - sapi_header_op(SAPI_HEADER_REPLACE, &ctr); - - // Send the Sec-WebSocket-Accept header - ctr.line_len = sizeof("Sec-WebSocket-Accept: ") - 1 + ZSTR_LEN(encoded) + 1; - char *sec_accept_header = emalloc(ctr.line_len); - snprintf(sec_accept_header, ctr.line_len, "Sec-WebSocket-Accept: %s", result); - ctr.line = sec_accept_header; - sapi_header_op(SAPI_HEADER_REPLACE, &ctr); - efree(sec_accept_header); - - // Flush all output buffers to ensure headers are sent immediately - php_output_flush_all(); - - efree(result); - - apr_sock = ap_get_conn_socket(r->connection); - - // Get the native descriptor - apr_os_sock_get(&fd, apr_sock); - - // Use php_stream_sock_open_from_socket() to create a PHP stream - php_stream *stream = php_stream_sock_open_from_socket(fd, NULL); - - if (!stream) { - RETURN_FALSE; - } - - php_stream_to_zval(stream, return_value); - -} -/* }}} */ - -/* {{{ Sends response headers required for a WebSocket connection */ -PHP_FUNCTION(apache_websocket_stream) { +/* {{{ The allows for direct select calls on apache's connection socket */ +PHP_FUNCTION(apache_connection_stream) { php_struct *ctx = SG(server_context); request_rec *r; apr_socket_t *apr_sock; diff --git a/sapi/apache2handler/php_functions_arginfo.h b/sapi/apache2handler/php_functions_arginfo.h index 4e8566084434..d1d981bf49a8 100644 --- a/sapi/apache2handler/php_functions_arginfo.h +++ b/sapi/apache2handler/php_functions_arginfo.h @@ -41,8 +41,7 @@ ZEND_FUNCTION(apache_lookup_uri); ZEND_FUNCTION(virtual); ZEND_FUNCTION(apache_request_headers); ZEND_FUNCTION(apache_response_headers); -ZEND_FUNCTION(apache_websocket_accept); -ZEND_FUNCTION(apache_websocket_stream); +ZEND_FUNCTION(apache_connection_stream); ZEND_FUNCTION(apache_note); ZEND_FUNCTION(apache_setenv); ZEND_FUNCTION(apache_getenv); @@ -55,8 +54,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(apache_request_headers, arginfo_apache_request_headers) ZEND_RAW_FENTRY("getallheaders", zif_apache_request_headers, arginfo_getallheaders, 0, NULL, NULL) ZEND_FE(apache_response_headers, arginfo_apache_response_headers) - ZEND_FE(apache_websocket_accept, NULL) - ZEND_FE(apache_websocket_stream, NULL) + ZEND_FE(apache_connection_stream, NULL) ZEND_FE(apache_note, arginfo_apache_note) ZEND_FE(apache_setenv, arginfo_apache_setenv) ZEND_FE(apache_getenv, arginfo_apache_getenv) From f9413244e5b98f2c7b0f3b6c211c465739366a26 Mon Sep 17 00:00:00 2001 From: richardtmiles Date: Thu, 25 Apr 2024 08:48:02 -0600 Subject: [PATCH 03/12] Added the apache_connection_stream() function for CGI WebSockets --- NEWS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS b/NEWS index 96895dc4a4bc..2130f4dca319 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.6.0beta1 +- apache2handler: + . Added the apache_connection_stream() function for CGI WebSockets. (Richard Tyler Miles) + - Core: . Changed run-tests.php to run test subprocesses without a shell where possible. (NickSdot) From b45474e02f4b0973b67e53631d09c25039bef586 Mon Sep 17 00:00:00 2001 From: richardtmiles Date: Thu, 25 Apr 2024 08:51:48 -0600 Subject: [PATCH 04/12] Cleaning commit, removing import --- ext/standard/php_fopen_wrapper.c | 11 ----------- sapi/apache2handler/php_functions.c | 1 - 2 files changed, 12 deletions(-) diff --git a/ext/standard/php_fopen_wrapper.c b/ext/standard/php_fopen_wrapper.c index 8ad26d161058..286e4768c3ac 100644 --- a/ext/standard/php_fopen_wrapper.c +++ b/ext/standard/php_fopen_wrapper.c @@ -243,32 +243,21 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c } if (!strcasecmp(path, "input")) { - php_stream_input_t *input; - if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) { php_stream_wrapper_warn(wrapper, context, options, Disabled, "URL file-access is disabled in the server configuration"); return NULL; } - input = ecalloc(1, sizeof(*input)); - if ((input->body = SG(request_info).request_body)) { - php_stream_rewind(input->body); - } else { - input->body = php_stream_temp_create_ex(TEMP_STREAM_DEFAULT, SAPI_POST_BLOCK_SIZE, PG(upload_tmp_dir)); - SG(request_info).request_body = input->body; - } - return php_stream_alloc(&php_stream_input_ops, input, 0, "rb"); - } if (!strcasecmp(path, "stdin")) { diff --git a/sapi/apache2handler/php_functions.c b/sapi/apache2handler/php_functions.c index c3ef316ffa9f..b5a2141153ed 100644 --- a/sapi/apache2handler/php_functions.c +++ b/sapi/apache2handler/php_functions.c @@ -46,7 +46,6 @@ #include "php_apache.h" #include "php_functions_arginfo.h" -#include "ext/standard/sha1.h" #include "ext/standard/php_standard.h" #ifdef ZTS From d076b9c8d72c8e653af5042d4eea2d4af08daf70 Mon Sep 17 00:00:00 2001 From: richardtmiles Date: Thu, 25 Apr 2024 10:04:13 -0600 Subject: [PATCH 05/12] updating branch name --- sapi/apache2handler/php_functions.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sapi/apache2handler/php_functions.c b/sapi/apache2handler/php_functions.c index b5a2141153ed..7d05140c56d5 100644 --- a/sapi/apache2handler/php_functions.c +++ b/sapi/apache2handler/php_functions.c @@ -206,7 +206,7 @@ PHP_FUNCTION(apache_response_headers) } /* }}} */ -/* {{{ The allows for direct select calls on apache's connection socket */ +/* {{{ The allows for direct select calls on Apache's connection socket */ PHP_FUNCTION(apache_connection_stream) { php_struct *ctx = SG(server_context); request_rec *r; From 80c1f008db69b0b8d4c38329ff421c8172f4265b Mon Sep 17 00:00:00 2001 From: richardtmiles Date: Thu, 25 Apr 2024 16:29:48 -0600 Subject: [PATCH 06/12] untracking .DS_Store --- ext/.DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 ext/.DS_Store diff --git a/ext/.DS_Store b/ext/.DS_Store deleted file mode 100644 index 733a361d0cdb76ac394c51eac8c72ea07be3d93b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKy-LGS6h1dC7=MP2LYHhVMeODnOLTIPzJP5bR%lF6yE)_md;lk3L1!lycNZ5y z+y%i$@H_V&+QhWo6p?e_+%Gvl_nv%#oE#!DopIbIY7mhRW%QP@%nTv_8qn|_ z?d~8=GPasm@hSXm2Ke38D5jK>(Q)zpJ?~FFNg74{QQC*azq-CTczb``DSG1t-fTGC z)!TeP8ZF_pj0V($4(X_Lu6enwMikG#-)N2H{BQdt>9mgdQN?=9_8r&b(>YzBnp5!5 zqce(26`izJX2le1y8PVQYkpWxDti^HS`=5T%WFPyT`J0eGN26n6a(nlEdHuPU6lc4 zKpB`Yz~6@e$`}gf4(+D{iyZ-gb(pQ-nr8{l2?RsI+#x&=<5Gbx)yRlpTsrK5#D#*n zLzhlQh7Thr8<|jyJstZ8Hk?f8P*-I@87MPQbGHrd|GV?&|8kPvDFe#Dzhc1Dqi)o} zBS~+qJRJAh0D1^z;kex4Bn20?6(g2g@jBEB>;Yc@L&4l3ED-$>ur%nR4E!nsU*&#r AUjP6A From f7f1e6fdc7477ec8bbbf2c9f4cf591b7b50b1d67 Mon Sep 17 00:00:00 2001 From: richardtmiles Date: Thu, 25 Apr 2024 16:30:39 -0600 Subject: [PATCH 07/12] untracking .DS_Store --- .DS_Store | Bin 10244 -> 0 bytes .gitignore | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 8060b2bde57951e48b081ddb9ebc6b9af91076fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10244 zcmeHMPiS0482@H>lWd5r)zCJ!nk?p^U|V*R_E3ZErfd!-i0E3-fRLmcvT1j-+ubDX zL1c^dU_pp@QBm;VzZL{@5fqUg>M56krBM%21SvGMl$N%a9{PPV^WOVr-|V)hMan#Q z@7wI}``+(2znM30X957?&{94PkOY7@o)ot{h8N30IGv#*qQ7|)qriHABT$4{yv`Tr zLfP&M=YVs-Ip7>{4mbz?4-Vivn~r#oitg$ha1J;JdSFAjxnQST}4$6LY0Hi%tEhFgk;CM zq)Z2qQPEwU1I~f41GIMUhCQ@qhbmvcr*lWjrHU%pdfT2=Drd7Z#qtcI@z*{(nC`ps z+i0`Q{6l6|3Y5>9OGJ6Uz)hL{hHL`*OtnG0vpbG2ZvL{ z+vV{>0hVBfvx8W<8qVMvwmvcH`O$D6uB3Su*8|ZhB!dprGY@&(VVO3xz?rvAl$epKlbi)j4Uy!;>*rqBRGt|T2h&_vIzH?#S>tD zb^4c|pHa`7uH#OGQO=jRv_gC_Z2LTp0mJ{u#t-xm`WWZ@?~~r~o!)$>YdNb8=e&`P z^NN?AhIv>p`h9f$l;qVI=Z!v_Sp9WV&$B)r_-w;DFN{}+w+rW!GWPwF+&wm04gI%< z!Wmq{2cKU*w{}%(Xt)n|pm~;|XMp6O#})Kp5ekM6o8SM$F|Oh8%3JBOn!o&7&Thjw zuXwWV@#5SLRf89{+|s-f4ioHIsj|aRqobz_(b-&kemZ{>1tDxIvzl&Y;%cKj846dQ~{SPm1c>Ugx6)0(* z#r1%oM+@g&dZ*PwT6))CHc_tQtJSAGFIYYAeS3z%f8a~v??mP*_QWzeV!8qGY$MJ! z9Ek4lG#K~bDw^dOI@Dq-25&$PUWZrscmTM&fEs*^^MBg6_V_!hFBl&5f(K z$N%=)?c)Kn4d=X(@8q~6Zg)5boCD5*b~+GMWJ%ipA8Y>q|8`1pT+RXKK${&9v1~4T z2ubzcdWfEu=Se)L@T7zC>MF_-yvP+iM!6l2f6#XP^LSEjiXt8OIiq7;Mf?Vjw*NDr X#XbT5eFkp*58&4Sa&ZEW*!urIn%a7w diff --git a/.gitignore b/.gitignore index 98f5717ff1fa..40d777882771 100644 --- a/.gitignore +++ b/.gitignore @@ -265,6 +265,7 @@ phpt.* # Temporary PHP INI configuration file(s) for tests, generated by `make test` tmp-php.ini php.ini +.DS_Store # ------------------------------------------------------------------------------ # Generated by GCC's gcov and LCOV via build/Makefile.gcov @@ -275,7 +276,7 @@ php.ini /gcovr_html/ /lcov_html/ /php_lcov.info -compile_commands.json +/compile_commands.json # ------------------------------------------------------------------------------ # Archives generated during the PHP release process From 353038ef8c213e1a2b1826341feec1ea4bc147ad Mon Sep 17 00:00:00 2001 From: richardtmiles Date: Thu, 25 Apr 2024 17:00:30 -0600 Subject: [PATCH 08/12] error handling --- sapi/apache2handler/php_functions.c | 31 ++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/sapi/apache2handler/php_functions.c b/sapi/apache2handler/php_functions.c index 7d05140c56d5..bfca23fe9831 100644 --- a/sapi/apache2handler/php_functions.c +++ b/sapi/apache2handler/php_functions.c @@ -212,25 +212,50 @@ PHP_FUNCTION(apache_connection_stream) { request_rec *r; apr_socket_t *apr_sock; int fd; + php_stream *stream; + + // Ensure the server context is available + if (!ctx) { + php_error_docref(NULL, E_WARNING, "Server context is not available"); + RETURN_FALSE; + } r = ctx->r; + // Ensure the request record is available + if (!r) { + php_error_docref(NULL, E_WARNING, "Request record is not available"); + RETURN_FALSE; + } + + // Check for proper parsing of parameters (none expected) ZEND_PARSE_PARAMETERS_NONE(); apr_sock = ap_get_conn_socket(r->connection); + // Ensure the connection socket is available + if (!apr_sock) { + php_error_docref(NULL, E_WARNING, "Failed to obtain connection socket"); + RETURN_FALSE; + } + // Get the native descriptor - apr_os_sock_get(&fd, apr_sock); + if (apr_os_sock_get(&fd, apr_sock) != APR_SUCCESS) { + php_error_docref(NULL, E_WARNING, "Failed to get native socket descriptor"); + RETURN_FALSE; + } // Use php_stream_sock_open_from_socket() to create a PHP stream - php_stream *stream = php_stream_sock_open_from_socket(fd, NULL); + stream = php_stream_sock_open_from_socket(fd, NULL); + // Check if the stream was successfully created if (!stream) { + php_error_docref(NULL, E_WARNING, "Failed to open stream from socket"); RETURN_FALSE; } + // Convert the php_stream to a zval and return it php_stream_to_zval(stream, return_value); - } /* }}} */ From c40764e4f95ead782e7993cf28e87d6a7d992b9e Mon Sep 17 00:00:00 2001 From: richardtmiles Date: Thu, 25 Apr 2024 17:03:23 -0600 Subject: [PATCH 09/12] order of ZEND_PARSE_PARAMETERS_NONE --- sapi/apache2handler/php_functions.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sapi/apache2handler/php_functions.c b/sapi/apache2handler/php_functions.c index bfca23fe9831..f696d1909abc 100644 --- a/sapi/apache2handler/php_functions.c +++ b/sapi/apache2handler/php_functions.c @@ -214,6 +214,10 @@ PHP_FUNCTION(apache_connection_stream) { int fd; php_stream *stream; + + // Check for proper parsing of parameters (none expected) + ZEND_PARSE_PARAMETERS_NONE(); + // Ensure the server context is available if (!ctx) { php_error_docref(NULL, E_WARNING, "Server context is not available"); @@ -228,9 +232,6 @@ PHP_FUNCTION(apache_connection_stream) { RETURN_FALSE; } - // Check for proper parsing of parameters (none expected) - ZEND_PARSE_PARAMETERS_NONE(); - apr_sock = ap_get_conn_socket(r->connection); // Ensure the connection socket is available From acd35ca4596145682649105afcfe58d8e2cab0df Mon Sep 17 00:00:00 2001 From: richardtmiles Date: Fri, 26 Apr 2024 11:49:16 -0600 Subject: [PATCH 10/12] PHP CLISERVER apache_connection_stream function When a connection is received you must hit enter in your server terminal to complete the connection. CLI server is single threaded, so only basic debugging can be done regarding websockets. --- sapi/cli/php_cli_server.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c index 007718e37b9c..5631ccd4f900 100644 --- a/sapi/cli/php_cli_server.c +++ b/sapi/cli/php_cli_server.c @@ -447,6 +447,27 @@ PHP_FUNCTION(apache_response_headers) /* {{{ */ } /* }}} */ +PHP_FUNCTION(apache_connection_stream) /* {{{ */ +{ + php_cli_server_client *client = SG(server_context); + + // Check for proper parsing of parameters (none expected) + ZEND_PARSE_PARAMETERS_NONE(); + + // Use php_stream_sock_open_from_socket() to create a PHP stream + php_stream *stream = php_stream_sock_open_from_socket(client->sock, NULL); + + // Check if the stream was successfully created + if (!stream) { + php_error_docref(NULL, E_WARNING, "Failed to open stream from socket in cli server"); + RETURN_FALSE; + } + + // Convert the php_stream to a zval and return it + php_stream_to_zval(stream, return_value); +} +/* }}} */ + /* {{{ cli_server module */ static void cli_server_init_globals(zend_cli_server_globals *cg) @@ -495,6 +516,7 @@ const zend_function_entry server_additional_functions[] = { PHP_FE(cli_get_process_title, arginfo_cli_get_process_title) PHP_FE(apache_request_headers, arginfo_apache_request_headers) PHP_FE(apache_response_headers, arginfo_apache_response_headers) + PHP_FE(apache_connection_stream, NULL) PHP_FALIAS(getallheaders, apache_request_headers, arginfo_getallheaders) PHP_FE_END }; From 9990384b8d3c64ab8ec99e92a234b27fb5454bf7 Mon Sep 17 00:00:00 2001 From: Richard Tyler Miles Date: Wed, 5 Aug 2026 19:21:35 -0600 Subject: [PATCH 11/12] Clean up apache_connection_stream PR --- NEWS | 3 - sapi/apache2handler/php_functions.c | 90 +++++++++++++---------------- sapi/cli/php_cli_server.c | 25 ++++---- 3 files changed, 52 insertions(+), 66 deletions(-) diff --git a/NEWS b/NEWS index 2130f4dca319..96895dc4a4bc 100644 --- a/NEWS +++ b/NEWS @@ -2,9 +2,6 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.6.0beta1 -- apache2handler: - . Added the apache_connection_stream() function for CGI WebSockets. (Richard Tyler Miles) - - Core: . Changed run-tests.php to run test subprocesses without a shell where possible. (NickSdot) diff --git a/sapi/apache2handler/php_functions.c b/sapi/apache2handler/php_functions.c index f696d1909abc..f749bac4a735 100644 --- a/sapi/apache2handler/php_functions.c +++ b/sapi/apache2handler/php_functions.c @@ -46,7 +46,6 @@ #include "php_apache.h" #include "php_functions_arginfo.h" -#include "ext/standard/php_standard.h" #ifdef ZTS int php_apache2_info_id; @@ -207,56 +206,45 @@ PHP_FUNCTION(apache_response_headers) /* }}} */ /* {{{ The allows for direct select calls on Apache's connection socket */ -PHP_FUNCTION(apache_connection_stream) { - php_struct *ctx = SG(server_context); - request_rec *r; - apr_socket_t *apr_sock; - int fd; - php_stream *stream; - - - // Check for proper parsing of parameters (none expected) - ZEND_PARSE_PARAMETERS_NONE(); - - // Ensure the server context is available - if (!ctx) { - php_error_docref(NULL, E_WARNING, "Server context is not available"); - RETURN_FALSE; - } - - r = ctx->r; - - // Ensure the request record is available - if (!r) { - php_error_docref(NULL, E_WARNING, "Request record is not available"); - RETURN_FALSE; - } - - apr_sock = ap_get_conn_socket(r->connection); - - // Ensure the connection socket is available - if (!apr_sock) { - php_error_docref(NULL, E_WARNING, "Failed to obtain connection socket"); - RETURN_FALSE; - } - - // Get the native descriptor - if (apr_os_sock_get(&fd, apr_sock) != APR_SUCCESS) { - php_error_docref(NULL, E_WARNING, "Failed to get native socket descriptor"); - RETURN_FALSE; - } - - // Use php_stream_sock_open_from_socket() to create a PHP stream - stream = php_stream_sock_open_from_socket(fd, NULL); - - // Check if the stream was successfully created - if (!stream) { - php_error_docref(NULL, E_WARNING, "Failed to open stream from socket"); - RETURN_FALSE; - } - - // Convert the php_stream to a zval and return it - php_stream_to_zval(stream, return_value); +PHP_FUNCTION(apache_connection_stream) +{ + php_struct *ctx = SG(server_context); + request_rec *r; + apr_socket_t *apr_sock; + int fd; + php_stream *stream; + + ZEND_PARSE_PARAMETERS_NONE(); + + if (!ctx) { + php_error_docref(NULL, E_WARNING, "Server context is not available"); + RETURN_FALSE; + } + + r = ctx->r; + if (!r) { + php_error_docref(NULL, E_WARNING, "Request record is not available"); + RETURN_FALSE; + } + + apr_sock = ap_get_conn_socket(r->connection); + if (!apr_sock) { + php_error_docref(NULL, E_WARNING, "Failed to obtain connection socket"); + RETURN_FALSE; + } + + if (apr_os_sock_get(&fd, apr_sock) != APR_SUCCESS) { + php_error_docref(NULL, E_WARNING, "Failed to get native socket descriptor"); + RETURN_FALSE; + } + + stream = php_stream_sock_open_from_socket(fd, NULL); + if (!stream) { + php_error_docref(NULL, E_WARNING, "Failed to open stream from socket"); + RETURN_FALSE; + } + + php_stream_to_zval(stream, return_value); } /* }}} */ diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c index 5631ccd4f900..d2e59bc16522 100644 --- a/sapi/cli/php_cli_server.c +++ b/sapi/cli/php_cli_server.c @@ -449,22 +449,23 @@ PHP_FUNCTION(apache_response_headers) /* {{{ */ PHP_FUNCTION(apache_connection_stream) /* {{{ */ { - php_cli_server_client *client = SG(server_context); + php_cli_server_client *client = SG(server_context); + php_stream *stream; - // Check for proper parsing of parameters (none expected) - ZEND_PARSE_PARAMETERS_NONE(); + ZEND_PARSE_PARAMETERS_NONE(); - // Use php_stream_sock_open_from_socket() to create a PHP stream - php_stream *stream = php_stream_sock_open_from_socket(client->sock, NULL); + if (!client) { + php_error_docref(NULL, E_WARNING, "Server context is not available"); + RETURN_FALSE; + } - // Check if the stream was successfully created - if (!stream) { - php_error_docref(NULL, E_WARNING, "Failed to open stream from socket in cli server"); - RETURN_FALSE; - } + stream = php_stream_sock_open_from_socket(client->sock, NULL); + if (!stream) { + php_error_docref(NULL, E_WARNING, "Failed to open stream from socket in cli server"); + RETURN_FALSE; + } - // Convert the php_stream to a zval and return it - php_stream_to_zval(stream, return_value); + php_stream_to_zval(stream, return_value); } /* }}} */ From 1e61b668c32ea3284146f1b9a1d94dca166ee674 Mon Sep 17 00:00:00 2001 From: Richard Tyler Miles Date: Wed, 5 Aug 2026 19:22:23 -0600 Subject: [PATCH 12/12] Drop unrelated wrapper and gitignore changes --- .gitignore | 7 ------- ext/standard/php_fopen_wrapper.c | 13 ++++++++----- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 40d777882771..b76b5a787caa 100644 --- a/.gitignore +++ b/.gitignore @@ -19,10 +19,6 @@ # Various temporary generated files *.tmp -# editor includes -*.iml -.idea - # ------------------------------------------------------------------------------ # Generated by the PHP build system # ------------------------------------------------------------------------------ @@ -264,8 +260,6 @@ phpt.* # Temporary PHP INI configuration file(s) for tests, generated by `make test` tmp-php.ini -php.ini -.DS_Store # ------------------------------------------------------------------------------ # Generated by GCC's gcov and LCOV via build/Makefile.gcov @@ -276,7 +270,6 @@ php.ini /gcovr_html/ /lcov_html/ /php_lcov.info -/compile_commands.json # ------------------------------------------------------------------------------ # Archives generated during the PHP release process diff --git a/ext/standard/php_fopen_wrapper.c b/ext/standard/php_fopen_wrapper.c index 286e4768c3ac..cca9445801f0 100644 --- a/ext/standard/php_fopen_wrapper.c +++ b/ext/standard/php_fopen_wrapper.c @@ -243,20 +243,23 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c } if (!strcasecmp(path, "input")) { - php_stream_input_t *input; + php_stream_input_t *input; + if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) { php_stream_wrapper_warn(wrapper, context, options, Disabled, "URL file-access is disabled in the server configuration"); return NULL; } + input = ecalloc(1, sizeof(*input)); if ((input->body = SG(request_info).request_body)) { php_stream_rewind(input->body); - } else { + } else { input->body = php_stream_temp_create_ex(TEMP_STREAM_DEFAULT, SAPI_POST_BLOCK_SIZE, PG(upload_tmp_dir)); SG(request_info).request_body = input->body; - } + } + return php_stream_alloc(&php_stream_input_ops, input, 0, "rb"); } @@ -286,7 +289,7 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c if (!strcmp(sapi_module.name, "cli")) { static int cli_out = 0; fd = STDOUT_FILENO; - if (cli_out) { + if (cli_out++) { fd = dup(fd); } else { cli_out = 1; @@ -302,7 +305,7 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c if (!strcmp(sapi_module.name, "cli")) { static int cli_err = 0; fd = STDERR_FILENO; - if (cli_err) { + if (cli_err++) { fd = dup(fd); } else { cli_err = 1;