Created
July 10, 2025 18:47
-
-
Save masakielastic/a59b6670fcb44ad6293ce8ba679fa6be to your computer and use it in GitHub Desktop.
stream_socket_server による TLS 対応の HTTP/1 サーバー。自己署名の証明書と秘密鍵の自動生成機能つき。
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
<?php | |
// --- 1. 自己署名証明書+秘密鍵を生成 --- // | |
// OpenSSL 設定(鍵長・鍵タイプ等) | |
$config = [ | |
"private_key_bits" => 2048, | |
"private_key_type" => OPENSSL_KEYTYPE_RSA, | |
]; | |
// 1.1. 秘密鍵を生成 | |
$privateKey = openssl_pkey_new($config); | |
if ($privateKey === false) { | |
die("秘密鍵の生成に失敗: " . openssl_error_string() . "\n"); | |
} | |
// 1.2. CSR を生成(DN 情報は適宜変更してください) | |
$dn = [ | |
"countryName" => "JP", | |
"stateOrProvinceName" => "Tokyo", | |
"localityName" => "Chiyoda-ku", | |
"organizationName" => "Example Co., Ltd.", | |
"organizationalUnitName" => "IT", | |
"commonName" => "localhost", | |
"emailAddress" => "admin@localhost", | |
]; | |
$csr = openssl_csr_new($dn, $privateKey, $config); | |
if ($csr === false) { | |
die("CSR の生成に失敗: " . openssl_error_string() . "\n"); | |
} | |
// 1.3. CSR を自己署名して証明書を生成(365日有効) | |
$validDays = 365; | |
$x509 = openssl_csr_sign($csr, null, $privateKey, $validDays, $config); | |
if ($x509 === false) { | |
die("証明書の署名に失敗: " . openssl_error_string() . "\n"); | |
} | |
// 1.4. PEM 文字列として取得 | |
openssl_pkey_export($privateKey, $privateKeyPem); | |
openssl_x509_export($x509, $certPem); | |
// 1.5. 証明書 + 鍵 をまとめた PEM ファイルを一時ディレクトリに書き出し | |
$pemFile = sys_get_temp_dir() . '/selfsigned-' . uniqid() . '.pem'; | |
$pemCombined = $certPem . "\n" . $privateKeyPem; | |
if (file_put_contents($pemFile, $pemCombined) === false) { | |
die("PEM ファイルの書き出しに失敗\n"); | |
} | |
// --- 2. TLS 対応 HTTP/1 サーバー起動 --- // | |
// SSL コンテキストに先ほど作った PEM ファイルを指定 | |
$context = stream_context_create([ | |
'ssl' => [ | |
'local_cert' => $pemFile, | |
'allow_self_signed' => true, // テスト用:自己署名を許可 | |
'verify_peer' => false, // クライアント証明書は検証しない | |
'verify_peer_name' => false, | |
], | |
]); | |
// 8443番ポートで TLS サーバを起動 | |
$server = stream_socket_server( | |
'tls://0.0.0.0:8443', | |
$errno, | |
$errstr, | |
STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, | |
$context | |
); | |
if (!$server) { | |
die("サーバの起動に失敗: $errstr ($errno)\n"); | |
} | |
echo "Listening on https://0.0.0.0:8443\n"; | |
while ($client = @stream_socket_accept($server, -1)) { | |
// リクエストヘッダ部を読み込む | |
$req = ''; | |
while (!feof($client) && strpos($req, "\r\n\r\n") === false) { | |
$req .= fgets($client, 1024); | |
} | |
// GET/POST とパスを簡易ログ | |
if (preg_match('#^(GET|POST)\s+([^\s]+)\s+HTTP/1\.[01]#', $req, $m)) { | |
list(, $method, $path) = $m; | |
echo "[".date('Y-m-d H:i:s')."] $method $path\n"; | |
} | |
// シンプルな HTML レスポンス | |
$body = "<html><body><h1>こんにちは、世界!</h1><p>あなたは {$method} {$path} をリクエストしました。</p></body></html>"; | |
$res = "HTTP/1.1 200 OK\r\n" | |
. "Content-Type: text/html; charset=UTF-8\r\n" | |
. "Content-Length: ".strlen($body)."\r\n" | |
. "Connection: close\r\n\r\n" | |
. $body; | |
fwrite($client, $res); | |
fclose($client); | |
} | |
fclose($server); | |
// ※ 必要に応じて script 終了時に @unlink($pemFile) で一時ファイルを削除してください。 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment