
send v4.1.1
CVE-2017-20165に対応するためのsendモジュールのフォーク
@fastify/send
Sendは、ファイルシステムからファイルをHTTPレスポンスとしてストリーミングするためのライブラリです。部分応答(Ranges)、条件付きGETネゴシエーション(If-Match、If-Unmodified-Since、If-None-Match、If-Modified-Since)、高いテストカバレッジ、そしてアプリケーションやフレームワークで適切なアクションを取るために利用できる詳細なイベントをサポートしています。
インストール
これは npm registry から入手できる Node.js モジュールです。インストールは npm install コマンド を使用して行います。
$ npm install @fastify/send
TypeScript
TypeScript を使用する場合は @types/mime@3 を使用する必要があります。@types/mime@4 では mime 型が削除されました。
$ npm install -D @types/mime@3
API
const send = require('@fastify/send')
send(req, path, [options])
指定されたパスに対して res に送信するための statusCode、headers、stream を提供します。req は Node.js HTTP リクエスト、path は送信する URL エンコードされたパス(実際のファイルシステムパスではありません)です。
オプション
acceptRanges
範囲リクエストの受け入れを有効または無効にします。デフォルトは true です。無効にすると Accept-Ranges は送信されず、Range リクエストヘッダーの内容は無視されます。
cacheControl
Cache-Control レスポンスヘッダーの設定を有効または無効にします。デフォルトは true です。無効にすると immutable と maxAge オプションは無視されます。
contentType
デフォルトでは、このライブラリは mime モジュールを使用して、要求されたファイルの拡張子に基づいてレスポンスの Content-Type を設定します。
この機能を無効にするには、contentType を false に設定します。無効にした場合、Content-Type ヘッダーは手動で設定する必要があります。
dotfiles
"ドットファイル" に遭遇したときの扱い方を設定します。ドットファイルとは、ドット(".")で始まるファイルまたはディレクトリです。このチェックはパス自体に対して行われ、そのパスがディスク上に存在するかどうかはチェックされないことに注意してください。root が指定されている場合、ルートより上のドットファイルのみがチェックされます(つまり、ルート自体が "deny" に設定されている場合でも、ドットファイル内にある可能性があります)。
'allow'ドットファイルに対する特別な扱いは行いません。'deny'ドットファイルへのリクエストに対して 403 を送信します。'ignore'ドットファイルが存在しないものとして扱い、404 を返します。
デフォルト値は 'ignore' に 似ています が、下位互換性のため、ドットで始まるディレクトリ内のファイルは無視されない点が異なります。
end
ストリームが終了するバイトオフセット。デフォルトはファイルの長さから 1 を引いた値です。終了はストリームに含まれます。つまり end: 3 はストリームに 4 バイト目を含めることを意味します。
etag
etag 生成を有効または無効にします。デフォルトは true です。
extensions
指定されたファイルが存在しない場合、指定された拡張子のいずれかを指定された順序で追加してみます。デフォルトでは無効(false に設定)です。拡張子なしの HTML ファイルを提供する場合の例: ['html', 'htm']。要求されたファイルがすでに拡張子を持っている場合はスキップされます。
immutable
Cache-Control レスポンスヘッダーの immutable ディレクティブを有効または無効にします。デフォルトは false です。true に設定した場合、キャッシュを有効にするために maxAge オプションも指定する必要があります。immutable ディレクティブは、maxAge オプションの有効期間中、サポートされているクライアントがファイルの変更を確認するための条件付きリクエストを行うのを防ぎます。
index
デフォルトでは send は "index.html" ファイルをサポートします。これを無効にするには false を設定するか、新しいインデックスを指定するには文字列または配列を優先順位順に渡します。
lastModified
Last-Modified ヘッダーを有効または無効にします。デフォルトは true です。ファイルシステムの最終更新日時を使用します。
maxAge
HTTP キャッシュの最大経過時間をミリ秒単位で指定します。デフォルトは 0 です。ms モジュールで受け入れられる文字列を指定することもできます。
maxContentRangeChunkSize
レスポンスコンテンツの最大サイズを指定します。デフォルトはファイル全体のサイズです。acceptRanges が true の場合に使用されます。
root
path からの相対パスでファイルを提供します。
start
ストリームが開始するバイトオフセット。デフォルトは 0 です。開始は含まれます。つまり start: 2 はストリームに 3 バイト目を含めることを意味します。
highWaterMark
このオプションを指定すると、内部バッファが基になるリソースからの読み取りを一時停止する前に保持する最大バイト数が設定されます。このオプションを省略した場合(または undefined を渡した場合)、Node.js は読み取り可能なバイナリストリームの組み込みデフォルトにフォールバックします。
.mime
mime エクスポートは、mime npm モジュール のグローバルインスタンスです。
これは、ファイル拡張子に関連付けられた MIME タイプ、およびファイルの MIME タイプを解決する方法(不明なファイル拡張子に使用するデフォルトタイプなど)のその他のオプションを設定するために使用されます。
キャッシュ
内部キャッシュは 行いません。代わりに Varnish などのリバースプロキシキャッシュ、または CDN と呼ばれる高度なものを使うべきです。もしアプリケーションがシングルノードのメモリキャッシュの恩恵を受けられるほど小さいのであれば、そもそもキャッシュは必要ないほど小さいということです ;)。
デバッグ
debug() の出力を有効にするには、NODE_DEBUG をエクスポートします:
$ NODE_DEBUG=send node app
テストの実行
$ npm install
$ npm test
例
特定のファイルを提供する
このシンプルな例では、すべてのリクエストに対して特定のファイルを送信します。
const http = require('node:http')
const send = require('send')
const server = http.createServer(async function onRequest (req, res) {
const { statusCode, headers, stream } = await send(req, '/path/to/index.html')
res.writeHead(statusCode, headers)
stream.pipe(res)
})
server.listen(3000)
ディレクトリ内のすべてのファイルを提供する
このシンプルな例では、指定されたディレクトリ内のすべてのファイルをトップレベルとして提供します。たとえば、GET /foo.txt というリクエストは /www/public/foo.txt を返します。
const http = require('node:http')
const parseUrl = require('parseurl')
const send = require('@fastify/send')
const server = http.createServer(async function onRequest (req, res) {
const { statusCode, headers, stream } = await send(req, parseUrl(req).pathname, { root: '/www/public' })
res.writeHead(statusCode, headers)
stream.pipe(res)
})
server.listen(3000)
カスタムファイルタイプ
const http = require('node:http')
const parseUrl = require('parseurl')
const send = require('@fastify/send')
// デフォルトの不明なタイプを text/plain に設定
send.mime.default_type = 'text/plain'
// カスタムタイプを追加
send.mime.define({
'application/x-my-type': ['x-mt', 'x-mtt']
})
const server = http.createServer(function onRequest (req, res) {
const { statusCode, headers, stream } = await send(req, parseUrl(req).pathname, { root: '/www/public' })
res.writeHead(statusCode, headers)
stream.pipe(res)
})
server.listen(3000)
カスタムディレクトリインデックスビュー
これは、ディレクトリの一覧を表示するカスタム関数を使用して、ディレクトリ構造を提供する例です。
const http = require('node:http')
const fs = require('node:fs')
const parseUrl = require('parseurl')
const send = require('@fastify/send')
// /www/example.com/public/* 内の任意のファイルを転送
// ディレクトリ一覧用のカスタムハンドラ付き
const server = http.createServer(async function onRequest (req, res) {
const { statusCode, headers, stream, type, metadata } = await send(req, parseUrl(req).pathname, { index: false, root: '/www/public' })
if(type === 'directory') {
// ディレクトリリストを取得
const list = await readdir(metadata.path)
// ディレクトリのインデックスをレンダリング
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' })
res.end(list.join('\n') + '\n')
} else {
res.writeHead(statusCode, headers)
stream.pipe(res)
}
})
server.listen(3000)
ルートディレクトリからの配信とカスタムエラーハンドリング
const http = require('node:http')
const parseUrl = require('parseurl')
const send = require('@fastify/send')
const server = http.createServer(async function onRequest (req, res) {
// /www/example.com/public/* 内の任意のファイルを転送
const { statusCode, headers, stream, type, metadata } = await send(req, parseUrl(req).pathname, { root: '/www/public' })
switch (type) {
case 'directory': {
// カスタムディレクトリハンドリングロジック:
res.writeHead(301, {
'Location': metadata.requestPath + '/'
})
res.end('Redirecting to ' + metadata.requestPath + '/')
break
}
case 'error': {
// カスタムエラーハンドリングロジック:
res.writeHead(metadata.error.status ?? 500, {})
res.end(metadata.error.message)
break
}
default: {
// カスタムヘッダー
// すべてのファイルをダウンロード用に提供
res.setHeader('Content-Disposition', 'attachment')
res.writeHead(statusCode, headers)
stream.pipe(res)
}
}
})
server.listen(3000)
ライセンス
MIT のもとでライセンスされています。