[Node.js] Webサーハ゛を作る。(2)が古くなったんで書き直してみた。clusterとmimeモジュールも使うようになってる。
説明はソースのコメントにしておいたので省略(^^;)

import * as http from "http";
import * as cluster from "cluster";	// (1)
import * as os from "os";
import * as url from "url";
import * as path from "path";
import * as querystring from "querystring";
import * as fs from "fs";
import * as mime from "mime";

const port = 8080;					// 待ち受けポート番号
const contentsDir = "www";			// コンテンツフォルダ
const defaultHtml = "index.html";	// ファイル名が省略された場合(ディレクトリを指定された場合)
									// 読み込むファイル名

// コンテンツフォルダのフルパスを生成
const wwwRoot = path.join(__dirname, contentsDir);
console.log("wwwRoot=" + wwwRoot);

// コア数取得
const numCPUs = os.cpus().length;

if(cluster.isMaster) {	// マスターなら
	// CPUコア数のワーカーを起動 (2)
	for(let i = 0; i < numCPUs; i++) {
		cluster.fork();
	}
	cluster.on("exit", (worker, code, signal)=>{
		console.log(`worker ${worker.process.pid} died`);
	});
} else {
	// サーバ起動
	const server = http.createServer((req, res)=>{
		console.dir(req.headers);
		const reqUrl = url.parse(<string>req.url, true);
		const contentsFile = path.join(wwwRoot, querystring.unescape(<string>reqUrl.pathname));
			
		// ファイルの情報を取得する。そして、コールバック関数でファイルやエラーをクライアントに返す。
		fs.stat(contentsFile, (err, stats)=>{
			if(err) {	// エラーなら、404エラーをクライアントに返す。
				console.log(err);
				responseError404(res);
				return;
			} else {
				responseContent(contentsFile, stats, res);
			}
		});
	})
	.listen(port);
}

// ファイルを返す。
function responseContent(contentsFile:string, stats:fs.Stats, res:http.ServerResponse) {
	if(stats.isDirectory()) {	// ディレクトリなら、defaultHtml(index.html)を追加して
								// fs.statを呼ぶ。
		const indexFile = path.join(contentsFile, defaultHtml);
		fs.stat(indexFile, (err, stats)=>{
			if(err) {	// エラーなら、404エラーをクライアントに返す。
				console.log(err);
				responseError404(res);
				return;
			} else {
				responseContent(indexFile, stats, res);
			}
		});
		return;
	} else if(stats.isFile()) {	// ファイルなら、ファイルを返す。
		// 拡張子からMIMEタイプを取得してヘッダに設定する。
		const extname = path.extname(contentsFile).toLocaleLowerCase();
		const mimeType = mime.lookup(extname);

		// レスポンスヘッダを設定
		responseSetHeader(res, 200, mimeType, stats.mtime);
		
		// ファイルのストリームを作って、クライアントに返す。
		//		pipe()でresにデータを流すようにする。
		// 		resはWritableインターフェイスが実装されているのでpipe()に渡すことができる。
		fs.createReadStream(contentsFile)
			.pipe(res);
	} else {	// ファイルでもディレクトリでもないなら
		// ファイルがなかったことにする。(404エラーをクライアントに返す)
		responseError404(res);
	}
}

// 404エラーをクライアントに返す。
function responseError404(res:http.ServerResponse):void {
	res.writeHead(404, { "Content-Type":"text/plain" });
	res.write("404 Not Found.\n");
	res.end();
}

// レスポンスヘッダを設定
function responseSetHeader(res:http.ServerResponse, statusCode:number, mimeType:string | null,
			lastModified:Date, nocache:boolean=false):void {
	if(mimeType) {
		res.setHeader("Content-Type", mimeType);
	}
	if(nocache) {
		res.setHeader("Pragma", "no-cache");
		res.setHeader("Cache-Control", "private, no-store, no-cache, must-revalidate");
		res.setHeader("Expires", new Date().toUTCString());
	}
	res.setHeader("Last-Modified", lastModified.toUTCString());
	res.statusCode = statusCode;
}