Last active
March 21, 2026 20:40
-
-
Save masakielastic/c91dba6845d09ae04727ae5008037c79 to your computer and use it in GitHub Desktop.
embed PHP と Stream API で TLS HTTP/1 サーバー。最新版は https://github.com/masakielastic/php-embed-stream-http
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <php.h> | |
| #include <Zend/zend_smart_str.h> | |
| #include <sapi/embed/php_embed.h> | |
| #define SERVER_ADDR "tls://127.0.0.1:8443" | |
| #define SERVER_CERT_PEM "localhost.pem" | |
| #define SERVER_KEY_PEM "localhost-key.pem" | |
| static int run_min_server(void); | |
| static php_stream_context *create_tls_server_context(void); | |
| static void set_context_string_option(php_stream_context *context, const char *wrapper, const char *option, const char *value); | |
| static void set_context_bool_option(php_stream_context *context, const char *wrapper, const char *option, bool value); | |
| int main(int argc, char **argv) | |
| { | |
| PHP_EMBED_START_BLOCK(argc, argv) | |
| (void) run_min_server(); | |
| PHP_EMBED_END_BLOCK() | |
| return 0; | |
| } | |
| static void set_context_string_option(php_stream_context *context, const char *wrapper, const char *option, const char *value) | |
| { | |
| zval zv; | |
| ZVAL_STRING(&zv, value); | |
| php_stream_context_set_option(context, (char *) wrapper, (char *) option, &zv); | |
| zval_ptr_dtor(&zv); | |
| } | |
| static void set_context_bool_option(php_stream_context *context, const char *wrapper, const char *option, bool value) | |
| { | |
| zval zv; | |
| ZVAL_BOOL(&zv, value ? 1 : 0); | |
| php_stream_context_set_option(context, (char *) wrapper, (char *) option, &zv); | |
| } | |
| static php_stream_context *create_tls_server_context(void) | |
| { | |
| php_stream_context *context; | |
| context = php_stream_context_alloc(); | |
| if (!context) { | |
| return NULL; | |
| } | |
| /* | |
| * local_cert may contain the certificate and private key in one PEM file. | |
| * local_pk is kept here to make split-file experiments easier. | |
| */ | |
| set_context_string_option(context, "ssl", "local_cert", SERVER_CERT_PEM); | |
| set_context_string_option(context, "ssl", "local_pk", SERVER_KEY_PEM); | |
| /* | |
| * These are mainly useful for local experiments with self-signed certs. | |
| * Tighten them for real deployments. | |
| */ | |
| set_context_bool_option(context, "ssl", "allow_self_signed", true); | |
| set_context_bool_option(context, "ssl", "verify_peer", false); | |
| set_context_bool_option(context, "ssl", "verify_peer_name", false); | |
| return context; | |
| } | |
| static int run_min_server(void) | |
| { | |
| php_stream *server = NULL; | |
| php_stream *client = NULL; | |
| php_stream_context *context = NULL; | |
| zend_string *err_text = NULL; | |
| zend_string *peer = NULL; | |
| int errcode = 0; | |
| context = create_tls_server_context(); | |
| if (!context) { | |
| php_printf("TLS context create failed\n"); | |
| return FAILURE; | |
| } | |
| server = php_stream_xport_create( | |
| SERVER_ADDR, | |
| sizeof(SERVER_ADDR) - 1, | |
| REPORT_ERRORS, | |
| STREAM_XPORT_SERVER | STREAM_XPORT_BIND | STREAM_XPORT_LISTEN, | |
| NULL, | |
| NULL, | |
| context, | |
| &err_text, | |
| &errcode | |
| ); | |
| if (!server) { | |
| php_printf("server create failed: %s (%d)\n", | |
| err_text ? ZSTR_VAL(err_text) : "unknown", errcode); | |
| if (err_text) { | |
| zend_string_release(err_text); | |
| } | |
| return FAILURE; | |
| } | |
| php_printf("TLS server listening on %s\n", SERVER_ADDR); | |
| php_printf("certificate: %s\n", SERVER_CERT_PEM); | |
| php_printf("private key: %s\n", SERVER_KEY_PEM); | |
| for (;;) { | |
| zend_string *accept_error = NULL; | |
| if (php_stream_xport_accept(server, &client, &peer, NULL, NULL, NULL, &accept_error) < 0) { | |
| php_printf("accept failed: %s\n", | |
| accept_error ? ZSTR_VAL(accept_error) : "unknown"); | |
| if (accept_error) { | |
| zend_string_release(accept_error); | |
| } | |
| break; | |
| } | |
| if (client) { | |
| char buf[4096]; | |
| ssize_t n; | |
| n = php_stream_read(client, buf, sizeof(buf) - 1); | |
| if (n > 0) { | |
| smart_str request_log = {0}; | |
| smart_str response = {0}; | |
| smart_str body = {0}; | |
| buf[n] = '\0'; | |
| smart_str_appends(&body, "hello over TLS\n"); | |
| smart_str_0(&body); | |
| smart_str_appends(&request_log, "request from "); | |
| if (peer) { | |
| smart_str_append(&request_log, peer); | |
| } else { | |
| smart_str_appends(&request_log, "<unknown>"); | |
| } | |
| smart_str_appends(&request_log, ":\n"); | |
| smart_str_appendl(&request_log, buf, (size_t) n); | |
| smart_str_appends(&request_log, "\n"); | |
| smart_str_0(&request_log); | |
| php_printf("%s", ZSTR_VAL(request_log.s)); | |
| smart_str_appends(&response, "HTTP/1.1 200 OK\r\n"); | |
| smart_str_appends(&response, "Content-Type: text/plain\r\n"); | |
| smart_str_append_printf(&response, "Content-Length: %zu\r\n", ZSTR_LEN(body.s)); | |
| smart_str_appends(&response, "Connection: close\r\n"); | |
| smart_str_appends(&response, "\r\n"); | |
| smart_str_append(&response, body.s); | |
| smart_str_0(&response); | |
| php_stream_write(client, ZSTR_VAL(response.s), ZSTR_LEN(response.s)); | |
| smart_str_free(&request_log); | |
| smart_str_free(&response); | |
| } | |
| php_stream_close(client); | |
| client = NULL; | |
| } | |
| if (peer) { | |
| zend_string_release(peer); | |
| peer = NULL; | |
| } | |
| } | |
| php_stream_close(server); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment