
send v4.1.1
Форк модуля send для устранения CVE-2017-20165
@fastify/send
Send — это библиотека для потоковой передачи файлов из файловой системы в качестве HTTP-ответа с поддержкой частичных ответов (Ranges), согласования условных GET-запросов (If-Match, If-Unmodified-Since, If-None-Match, If-Modified-Since), высокой степенью покрытия тестами и гранулярными событиями, которые могут использоваться для выполнения соответствующих действий в вашем приложении или фреймворке.
Установка
Это модуль Node.js, доступный через реестр npm. Установка выполняется с помощью команды 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])
Предоставляет statusCode, headers и stream для заданного пути, чтобы отправить их в res. Параметр req — это HTTP-запрос Node.js, а path — это urlencoded-путь для отправки (urlencoded, а не фактический путь в файловой системе).
Параметры
acceptRanges
Включает или отключает приём запросов с диапазонами, по умолчанию true.
При отключении заголовок Accept-Ranges не отправляется, а содержимое заголовка запроса Range игнорируется.
cacheControl
Включает или отключает установку заголовка ответа Cache-Control, по умолчанию true. При отключении параметры immutable и maxAge игнорируются.
contentType
По умолчанию эта библиотека использует модуль mime для установки Content-Type ответа на основе расширения запрошенного файла.
Чтобы отключить эту функциональность, установите contentType в false.
Если эта возможность отключена, заголовок Content-Type необходимо будет задавать вручную.
dotfiles
Определяет, как обрабатываются «dotfiles» при их обнаружении. Dotfile — это файл или каталог, имя которого начинается с точки («.»). Обратите внимание, что проверка выполняется для самого пути и не проверяет существование пути на диске. Если указан root, проверяются только dotfiles выше корня (т.е. сам корень может находиться внутри dotfile, если задано значение "deny").
'allow'— без особой обработки dotfiles.'deny'— отправлять 403 для любого запроса dotfile.'ignore'— считать, что dotfile не существует, и возвращать 404.
Значение по умолчанию похоже на 'ignore', за исключением того, что этот вариант по умолчанию не игнорирует файлы внутри каталога, имя которого начинается с точки, для обратной совместимости.
end
Смещение в байтах, на котором заканчивается поток, по умолчанию равно длине файла минус 1. Конец является включительным в потоке, то есть end: 3 включит 4-й байт в поток.
etag
Включает или отключает генерацию etag, по умолчанию true.
extensions
Если заданный файл не существует, пытается добавить одно из указанных расширений в заданном порядке. По умолчанию эта функция отключена (установлена в false). Пример значения, которое позволит обслуживать HTML-файлы без расширения: ['html', 'htm']. Этот параметр пропускается, если запрошенный файл уже имеет расширение.
immutable
Включает или отключает директиву immutable в заголовке ответа Cache-Control, по умолчанию false. Если установлено true, следует также указать параметр maxAge для включения кэширования. Директива immutable предотвратит условные запросы поддерживающих клиентов в течение срока действия параметра maxAge для проверки изменений файла.
index
По умолчанию send поддерживает файлы «index.html»; чтобы отключить это, установите false, либо укажите новый индекс, передав строку или массив в предпочтительном порядке.
lastModified
Включает или отключает заголовок Last-Modified, по умолчанию true. Использует значение последнего изменения файловой системы.
maxAge
Задаёт max-age в миллисекундах для 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')
// Default unknown types to text/plain
send.mime.default_type = 'text/plain'
// Add a custom type
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')
// Transfer arbitrary files from within /www/example.com/public/*
// with a custom handler for directory listing
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') {
// get directory list
const list = await readdir(metadata.path)
// render an index for the directory
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) {
// transfer arbitrary files from within
// /www/example.com/public/*
const { statusCode, headers, stream, type, metadata } = await send(req, parseUrl(req).pathname, { root: '/www/public' })
switch (type) {
case 'directory': {
// your custom directory handling logic:
res.writeHead(301, {
'Location': metadata.requestPath + '/'
})
res.end('Redirecting to ' + metadata.requestPath + '/')
break
}
case 'error': {
// your custom error-handling logic:
res.writeHead(metadata.error.status ?? 500, {})
res.end(metadata.error.message)
break
}
default: {
// your custom headers
// serve all files for download
res.setHeader('Content-Disposition', 'attachment')
res.writeHead(statusCode, headers)
stream.pipe(res)
}
}
})
server.listen(3000)
Лицензия
Лицензировано под MIT.