自己署名証明書(オレオレ証明書)を使ってHTTPSサーバーをNode.jsで立ち上げる(windows10) その2

  • このエントリーをはてなブックマークに追加

前回の記事
自己署名証明書(オレオレ証明書)を使ってHTTPSサーバーをNode.jsで立ち上げる(windows10) その1 (attacktube.com)

HTTPSサーバーを立ち上げる。
前回作ったコードをより細かくする。
HTTPSサーバーを立ち上げるコード「https.js」を次に示す。
現地時間を取得するために自作モジュールLocalTime.jsを使用している。

環境
Windows10(64bit)
nvm(Node Version Manager)を使用する。
nvmはnodeとnpmのバージョンの切り替えができる。
node.jsはアップデートが頻繁に行われるため、バージョン管理ツールを使うと都合が良いことが多い。
「特定のプロジェクトに必要なNode.jsのバージョンを簡単にインストールして使用することができる。」や「プロジェクト毎にNode.jsのバージョンの切り替えが簡単になる。」等のメリットがある。
nvm 1.1.10
node v18.15.0
npm 9.5.0




(function() {

    // 自作モジュールLocalTime.jsをrequireを使ってインポートして、定数localTimeに格納する。
    const localTime = require("./LocalTime.js");

    // LocalTimeの言語を日本語に設定
    localTime.SetLang("JP");
    // LocalTimeのタイムゾーンを東京(TYO)に設定
    localTime.SetTimeZone("TYO");

    // Node.jsのhttpsモジュールをrequireを使ってインポートして、定数httpsに格納する。
    const https = require("https");
    // Node.jsのfsモジュールをrequireを使ってインポートして、定数httpsに格納する。
    // fsモジュールはファイルシステムへのアクセスを提供するためのモジュールである。
    const fs = require('fs');
    const port = 443;//port番号
    const hostname = 'localhost'; // ホスト名またはドメイン名

    const options = {
       //秘密鍵
       key: fs.readFileSync('server-key.pem'),
       //自己署名証明書(オレオレ証明書)
       cert: fs.readFileSync('server-crt.pem')
    };

    // httpsオブジェクトからcreateServerメソッドを実行する。
    // createServerメソッドは、引数としてリクエストを処理するコールバック関数を受け取る。
    // このコールバック関数は、リクエストが発生した際に実行され、リクエストオブジェクト(req)とレスポンスオブジェクト(res)を受け取る
    // つまり、このコールバック関数は、https://localhost:3000にアクセスしたときに実行される。
    // このコールバック関数内で、適切なレスポンス(res)を生成してクライアントに送信する処理を実装する。
    // リクエストオブジェクト(req)は、http.IncomingMessageオブジェクトのインスタンスである。
    // リクエストオブジェクト(req)は、HTTPリクエスト(リクエストヘッダーやボディ)のデータを読み取るためのデータを読み取るためのReadableストリームです。
    // レスポンスオブジェクト(res)は、http.ServerResponseオブジェクトのインスタンスである。
    // レスポンスオブジェクト(res)は、Writableストリームとして機能する。
    // レスポンスオブジェクト(res)は、クライアントに対してデータを送信するためのストリームとして扱われる。
    
    // レスポンスオブジェクト(res)はhttp.ServerResponseオブジェクトのインスタンスであり、HTTPレスポンスを生成および送信するための
    // Writableストリームです。レスポンスオブジェクト(res)に対して書き込まれたデータはクライアントに送信されます。
    // writeメソッドやendメソッドを使用してレスポンスボディを書き込むことができます。
    // レスポンスオブジェクト(res)のボディに書き込むにはwriteメソッドを使う。
    // レスポンスオブジェクト(res)の送信を完了するためにはendメソッドを使う。
    
    const server = https.createServer(options, (req, res) => {
    
        console.log(
            `Executed. \n` +
            `${localTime.GetLocalTimeString()} \n` +
            `${req.method} \n` +
            `${req.url} \n` +
            `${req.headers["user-agent"]}`
        );
        
        const url = `https://${req.headers.host}`;

        // URL解析
        // req.urlは相対URLが入る。
        // urlはベースurl(相対URLを解決するための基準となるURL)が入る。
        const myURL = new URL(req.url, url);
        const whitespace = 2;
        const result = [];
        const names = [];

        let stringLengthMax = 0;
        let buf = ' ';
        let blanks = '';

        console.log("");

        result.push(myURL.href);
        result.push(myURL.origin);
        result.push(myURL.protocol);
        result.push(myURL.username);
        result.push(myURL.password);
        result.push(myURL.host);
        result.push(myURL.port);
        result.push(myURL.hostname);
        result.push(myURL.pathname);
        result.push(myURL.hash);
        result.push(myURL.search);

        names.push("href");
        names.push("origin");
        names.push("protocol");
        names.push("username");
        names.push("password");
        names.push("host");
        names.push("port");
        names.push("hostname");
        names.push("pathname");
        names.push("hash");
        names.push("search");

        // names配列の中の文字列で最大長をstringLengthMaxに格納する。
        names.forEach(t => {
            if (t.length > stringLengthMax) stringLengthMax = t.length;
        });

        // bufに格納した空の文字列を「stringLengthMax + whitespace」回数分のコピーを含む新しい文字列blanksを作る。 
        blanks = buf.repeat(stringLengthMax + whitespace);

        // URLの解析結果を出す。
        names.forEach((t, u) => (console.log(t + ':' + blanks.slice(t.length) + result[u])));

        console.log("");
        console.log("req.headers = ");
        console.log(req.headers);
        console.log("");

        let str = [];

        str.push("method:");
        str.push(req.method);
        str.push("\n");
        str.push("req.url:");
        str.push(req.url);
        str.push("\n");

        if (myURL.pathname === "/") {

            // ドキュメントルート(/)がリクエストさた場合
            res.writeHead(200, {
                'Content-Type': 'text/plain;charset=utf-8'
            });

            if (req.method === "POST") {

                let i = 0

                // data受信イベントの発生時に断片データ(chunk)を取得する。
                req.on('data', chunk => {
                    console.log(localTime.GetLocalTimeString() + " i = " + i + ", chunk = " + chunk + "");
                    i += 1;
                    str.push(chunk);
                });

                // 受信完了(end)イベント発生時
                //「end」イベントは、レスポンスのデータの読み取りが完了した場合に発生する。
                req.on('end', function() {
                    str.push("\n");
                    //レスポンスデータをchunk分割する。
                    res.write(str.join(""));
                    res.end("Hello World! " + req.method + "\n");
                });

            } else if (req.method === "GET") {

                // URLSearchParamsに含まれる値はforEach()メソッドで取り出し可能である。
                myURL.searchParams.forEach(function(value, key) {
                    str.push(key + " = " + value + "\n");
                    console.log(localTime.GetLocalTimeString() + ", " + key + " = " + value);
                });

                // レスポンスデータをchunk分割する。
                res.write(str.join(""));
                res.end("Hello World! " + req.method + "\n");

            } else {

                let i = 0

                // data受信イベントの発生時に断片データ(chunk)を取得する。
                req.on('data', chunk => {
                    console.log(localTime.GetLocalTimeString() + " i = " + i + ", chunk = " + chunk + "");
                    i += 1;
                    str.push(chunk);
                });

                // 受信完了(end)イベント発生時
                // 「end」イベントは、レスポンスのデータの読み取りが完了した場合に発生する。
                req.on('end', function() {
                    str.push("\n");
                    //レスポンスデータをchunk分割する。
                    res.write(str.join(""));
                    res.end("Hi! " + req.method);
                });

            }

        } else {

            // ドキュメントルート(/)がリクエストされない場合はメソッドに関係なくここを通る。
            res.writeHead(404, {
                'Content-Type': 'text/plain;charset=utf-8'
            });

            // レスポンスデータをchunk分割する。
            res.write(str.join(""));
            res.end("404 Not Found");

        }

    });

    // serverインスタンスオブジェクトからlistenメソッドを実行する。
    // httpsサーバーが指定されたポート(443)でリクエストを受け付けるようになる。
    server.listen(port, hostname, () => {
        console.log(localTime.GetLocalTimeString() + ` Server running at https://${hostname}:${port}`);
    });

})();

http.jsを実行して、firefoxで「https://localhost」にアクセスすると次のようになる。


c:\node\https_server>node https.js
[TYO] 2023-05-27(Sat) 20:03:57.637 Server running at https://localhost:443
Executed.
[TYO] 2023-05-27(Sat) 20:04:01.409
GET
/
Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0

href:      https://localhost/
origin:    https://localhost
protocol:  https:
username:
password:
host:      localhost
port:
hostname:  localhost
pathname:  /
hash:
search:

req.headers =
{
  host: 'localhost',
  'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0',
  accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
  'accept-language': 'ja,en-US;q=0.7,en;q=0.3',
  'accept-encoding': 'gzip, deflate, br',
  connection: 'keep-alive',
  cookie: '_ga=GA1.1.1823735261.1646346341',
  'upgrade-insecure-requests': '1',
  'sec-fetch-dest': 'document',
  'sec-fetch-mode': 'navigate',
  'sec-fetch-site': 'none',
  'sec-fetch-user': '?1'
}
  • このエントリーをはてなブックマークに追加

SNSでもご購読できます。

コメントを残す

*