Node クックブック - izudon/izudon.github.io GitHub Wiki

Node クックブック

本書で使われるモジュール

  • 6章 Express で開発をスピードアップ
    • express
  • 5章 Websocket を使って Ajax を超える
    • websocket
    • socket.io サーバからクライアントの(またはその逆)メソッドを呼び出せる。
      • 'コールバック' という。
    • now Nowjs コールバックを一段と簡単にした。
    • socket.io-client
  • 4章 データベース接続
    • ya-csv CSVファイル
    • mysql MySQL
    • mongodb MongoDB
    • mongoskin mongodb のスーパーセット
    • cradle CouchDB - ACID をサポート。
      • CouchDB は REST API 準拠なので http.request で全部できなくもない。
      • MongoDB も 4.0 からトランザクション(ACID 特性)をサポートした。
    • redis Redis - メモリ内に収まる程度のデータの場合、最適な選択肢。
    • hiredis Cで書いた redis。Redis 提供。redis よりも高速。
  • 3章 データのシリアル化
    • JSON は JSON.dtringify() JSON.parse() がビルトインされている。
    • xml2js XMLファイル・データを扱う
    • colors 色をつけやすくする
  • 2章 HTTPオブジェクトをより深く
    • connect
    • formidable
  • 1章 Webサーバを立てる
    • http - HTTP サーバや HTTP 接続を構築したりする場合
    • path - パスのベースネームを取得したりする場合など
    • fs - ファイルシステムのファイルを利用する場合
    • url - クエリストリングを取り出す場合など
    • querystring - ポストデータを連想記憶配列化する場合など

2章 HTTP オブジェクトをより深く

2.2 アップロードファイルを受信する

$ sudo npm install -g formidable
<form method="post" enntype="multipart/form-data">
	<input type="file" name="userfile1"><br>
	<input type="file" name="userfile1"><br>
	<input type="submit">
</form>

2.1 POST データを処理する

  • PHP では $_POST['fieldname'] を使える。
    • POST データを $_POST[] に格納するまでブロッキングされているため。
  • Node ではどうやるかはすべて開発者に委ねられている。
  • querystring モジュール
    • querystring.parse( postData ) でポストデータを連想記憶配列化できる。
  • util モジュール
    • util.inspect(postDataObject) で、オブジェクトをJSON形式で出力。

2.1.1 connect.bodyParser を使う。

$ sudo npm install -g connect
  • connect モジュールによるサーバの生成
    • メソッドチェーンで様々な設定をつなぎ、
      最後にコールバック関数をチェーンする。
var app = connect()
	.use( connect.bodyParser() )
	.use( connect.limit('64kb') )
	.use( function( req, res ){
		;
	}

1章 Webサーバを立てる

1.5 ファイルシステムをハッキングから守る

Node はミニマリストアプローチのため堅牢さは実装者に大きく依存する(!!)。

  • path.noemalize()
    • パスの正規化により 相対ディレクトリ攻撃ヌルバイト攻撃
      回避しようとするもの。
      • 相対ディレクトリ攻撃 http://server.name/../../../../../passwd
      • ヌルバイト攻撃 http://server.name/secure.js%00/index.html
  • path.basename()
    • ベースネームのみ取得(前回までのコード)= 比較的安全

1.5.1 ホワイトリスト

  • 請求できるファイル名をこちらから限定してしまう。

1.5.2 node-static

  • キャッシュ機能を備えた、成熟したモジュール。
  • RFC2616(HTTP 1.1)準拠。

1.4 ストリーミングによりパフォーマンスを最適化する

データをディスクから全部読み込んでしまうのではなく
response に pipe することができる。

  • fs.createReadStream() <- readStresm オブジェクトを返す。
  • fs.createReadStream().on( 'open', function(){ ... } );
    • オープン時にイベントハンドラを仕掛ける。
  • fs.createReadStream().once( 'open', function(){ ... } );
    • オープン時に1度しか使わない処理なので .once() で仕掛ける。
    • イベントが発生してイベントハンドラの実行が終わり次第イベントリスナは解放。
  • fs.createReadStream().once( 'error', function( e ){ ... } );
    • エラーハンドラを仕掛けられる。
  • stream.pipe( response )
    • これでフローを制御しながら延々とやっておいてくれる。
  • readStream クラス
    • EventEmitter クラスを継承
    • -> 豊富なイベントハンドラ
  • readStream クラス
    • inputStream クラスの子クラス?
    • -> pipe() メソッドで outputStream にパイプできる?
  • stream.on( 'data', function( data ){ ... } );
    • data イベントは、ディスクから1チャンクデータを取得するたびに呼ばれる。
      1チャンクのデフォルトは 64KB。
  • Buffer クラス
    • インスタンス生成時にサイズ指定してまとまった大きさのメモリを確保できる。
      変数の代わりにここに今回は溜めこんでる。
    • ここでは stat() したときの stats.size つまりファイルサイズ分の
      領域をあらかじめ確保し、data イベント毎に1チャンク分を足していってる。
    • -> あまりにも大きいファイルがあると領域確保失敗の恐れ。
      (バッファオーバーフロー:次項で対策)

1.4.1 システムをバッファオーバーフローから守る

  • キャッシュをもうクラスとして実装しちゃった。
  • サイズ上限を決めそれ以下のファイルのみキャッシュすることにした。

補足:

1.3 コンテンツキャッシュによる高速配信

  • スクリプト内変数に情報を貯めておけば、次のセッションでも使いまわせる。

1.3.1 コンテンツの変更を反映する

  • Date.now() で現在日時、
    fs.stat() でファイル最終更新日時等(ctime, mtime, atime)が取得できる。

1.2 スタティックファイルを配信する

  • fs モジュール
    • ファイルシステムのファイルを扱うモジュール。
    • fs.exists( <pathname>, function( exists ){ ... } );
      • <pathname> のファイルが存在するか調べる。
      • 相対パス指定の起点はスクリプト(server.js)の実行ディレクトリ。
      • 存在すれば true 存在しなければ false を引数に入れて
        コールバック関数を呼び出す。
    • fs.readFile( <pathname>, function( err, data ){ ... } );
      • <pathname> のファイルを読み込み、コールバック関数を呼ぶ。
      • err なんらかのエラーがあれば true が格納。
      • data 読み込んだデータが格納。

1.1 URLルーティング

  • decodeURI()
    • %20 などを解釈してデコードする
  • http モジュール
    • HTTP リクエストとレスポンスを処理する。
    • http.createServer( function( request, response ){ ... } ).listen(8080);
      • HTTP サーバプロセスを作成し、ポート 8080番 を開いて接続を待つ。
      • 具体的な処理はコールバック関数の中に。
    • request.url
      • リクエストのURLが格納されてくる。
    • response.writeHead( 200, { 'Content-Type': 'text/html' } )
      • ステータスコード 200 番で応答を作成。
      • Content-Type などその他もろもろのヘッダ情報は第2引数のハッシュに指定。
    • response.end( 'Wahoo!' )
      • 'Wahoo!' と出力してレスポンスを終える。
        response.finishedtrue になる。
  • path モジュール
    • path.baseneme() でベースネームを取り出せる。
    • path.basename( '/foo/bar/baz.html' ) -> baz.html
    • path.basename( '/foo/bar/baz.html', '.html' ) -> baz
  • url モジュール
    • クエリストリングを取り出せる。
    • url.parse( decodeURL( request.url ), true ).query.id
⚠️ **GitHub.com Fallback** ⚠️