Skip to content

Instantly share code, notes, and snippets.

@masakielastic
Created July 10, 2025 18:47
Show Gist options
  • Save masakielastic/a59b6670fcb44ad6293ce8ba679fa6be to your computer and use it in GitHub Desktop.
Save masakielastic/a59b6670fcb44ad6293ce8ba679fa6be to your computer and use it in GitHub Desktop.
stream_socket_server による TLS 対応の HTTP/1 サーバー。自己署名の証明書と秘密鍵の自動生成機能つき。
<?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