Skip to content

Commit

Permalink
chore!: bump dependency send@3 (#455)
Browse files Browse the repository at this point in the history
* chore!: bump dependency send@3

* fixup

* fixup

* chore: use v5 workflow

* chore: bump dependency [email protected]

* fixup
  • Loading branch information
climba03003 authored Jul 12, 2024
1 parent fc88938 commit cd0eae2
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 152 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ on:

jobs:
test:
uses: fastify/workflows/.github/workflows/plugins-ci.yml@v4.1.0
uses: fastify/workflows/.github/workflows/plugins-ci.yml@v5.0.0
with:
license-check: true
lint: true
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -477,13 +477,13 @@ If an error occurs while trying to send a file, the error will be passed
to Fastify's error handler. You can set a custom error handler with
[`fastify.setErrorHandler()`](https://fastify.dev/docs/latest/Reference/Server/#seterrorhandler).

### Payload `stream.filename`
### Payload `stream.path`

If you need to access the filename inside the `onSend` hook, you can use `payload.filename`.
If you need to access the file path inside the `onSend` hook, you can use `payload.path`.

```js
fastify.addHook('onSend', function (req, reply, payload, next) {
console.log(payload.filename)
console.log(payload.path)
next()
})
```
Expand Down
260 changes: 122 additions & 138 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use strict'

const { PassThrough } = require('node:stream')
const path = require('node:path')
const { fileURLToPath } = require('node:url')
const { statSync } = require('node:fs')
Expand Down Expand Up @@ -170,7 +169,7 @@ async function fastifyStatic (fastify, opts) {

const allowedPath = opts.allowedPath

function pumpSendToReply (
async function pumpSendToReply (
request,
reply,
pathname,
Expand Down Expand Up @@ -222,163 +221,148 @@ async function fastifyStatic (fastify, opts) {
}

// `send(..., path, ...)` will URI-decode path so we pass an encoded path here
const stream = send(request.raw, encodeURI(pathnameForSend), options)
let resolvedFilename
stream.on('file', function (file) {
resolvedFilename = file
})

const wrap = new PassThrough({
flush (cb) {
this.finished = true
if (reply.raw.statusCode === 304) {
reply.send('')
const {
statusCode,
headers,
stream,
type,
metadata
} = await send(request.raw, encodeURI(pathnameForSend), options)
switch (type) {
case 'directory': {
const path = metadata.path
if (opts.list) {
await dirList.send({
reply,
dir: path,
options: opts.list,
route: pathname,
prefix,
dotfiles: opts.dotfiles
}).catch((err) => reply.send(err))
}
cb()
}
})

wrap.getHeader = reply.getHeader.bind(reply)
wrap.setHeader = reply.header.bind(reply)
wrap.removeHeader = () => {}
wrap.finished = false
if (opts.redirect === true) {
try {
reply.redirect(301, getRedirectUrl(request.raw.url))
} /* c8 ignore start */ catch (error) {
// the try-catch here is actually unreachable, but we keep it for safety and prevent DoS attack
await reply.send(error)

Check warning

Code scanning / CodeQL

Information exposure through a stack trace Medium

This information exposed to the user depends on
stack trace information
.
} /* c8 ignore stop */
} else {
// if is a directory path without a trailing slash, and has an index file, reply as if it has a trailing slash
if (!pathname.endsWith('/') && findIndexFile(pathname, options.root, options.index)) {
return pumpSendToReply(
request,
reply,
pathname + '/',
rootPath,
undefined,
undefined,
checkedEncodings
)
}

Object.defineProperty(wrap, 'filename', {
get () {
return resolvedFilename
}
})
Object.defineProperty(wrap, 'statusCode', {
get () {
return reply.raw.statusCode
},
set (code) {
reply.code(code)
reply.callNotFound()
}
break
}
})

if (request.method === 'HEAD') {
wrap.on('finish', reply.send.bind(reply))
} else {
wrap.on('pipe', function () {
if (encoding) {
reply.header('content-type', getContentType(pathname))
reply.header('content-encoding', encoding)
case 'error': {
if (
statusCode === 403 &&
(!options.index || !options.index.length) &&
pathnameForSend[pathnameForSend.length - 1] === '/'
) {
if (opts.list) {
await dirList.send({
reply,
dir: dirList.path(opts.root, pathname),
options: opts.list,
route: pathname,
prefix,
dotfiles: opts.dotfiles
}).catch((err) => reply.send(err))
}
}
reply.send(wrap)
})
}

if (setHeaders !== undefined) {
stream.on('headers', setHeaders)
}

stream.on('directory', function (_, path) {
if (opts.list) {
dirList.send({
reply,
dir: path,
options: opts.list,
route: pathname,
prefix,
dotfiles: opts.dotfiles
}).catch((err) => reply.send(err))
return
}
if (metadata.error.code === 'ENOENT') {
// when preCompress is enabled and the path is a directory without a trailing slash
if (opts.preCompressed && encoding) {
const indexPathname = findIndexFile(pathname, options.root, options.index)
if (indexPathname) {
return pumpSendToReply(
request,
reply,
pathname + '/',
rootPath,
undefined,
undefined,
checkedEncodings
)
}
}

if (opts.redirect === true) {
try {
reply.redirect(301, getRedirectUrl(request.raw.url))
} /* c8 ignore start */ catch (error) {
// the try-catch here is actually unreachable, but we keep it for safety and prevent DoS attack
reply.send(error)
} /* c8 ignore stop */
} else {
// if is a directory path without a trailing slash, and has an index file, reply as if it has a trailing slash
if (!pathname.endsWith('/') && findIndexFile(pathname, options.root, options.index)) {
return pumpSendToReply(
request,
reply,
pathname + '/',
rootPath,
undefined,
undefined,
checkedEncodings
)
}
// if file exists, send real file, otherwise send dir list if name match
if (opts.list && dirList.handle(pathname, opts.list)) {
await dirList.send({
reply,
dir: dirList.path(opts.root, pathname),
options: opts.list,
route: pathname,
prefix,
dotfiles: opts.dotfiles
}).catch((err) => reply.send(err))
return
}

reply.callNotFound()
}
})
// root paths left to try?
if (Array.isArray(rootPath) && rootPathOffset < (rootPath.length - 1)) {
return pumpSendToReply(request, reply, pathname, rootPath, rootPathOffset + 1)
}

stream.on('error', function (err) {
if (err.code === 'ENOENT') {
// when preCompress is enabled and the path is a directory without a trailing slash
if (opts.preCompressed && encoding) {
const indexPathname = findIndexFile(pathname, options.root, options.index)
if (indexPathname) {
if (opts.preCompressed && !checkedEncodings.has(encoding)) {
checkedEncodings.add(encoding)
return pumpSendToReply(
request,
reply,
pathname + '/',
pathnameOrig,
rootPath,
undefined,
rootPathOffset,
undefined,
checkedEncodings
)
}
}

// if file exists, send real file, otherwise send dir list if name match
if (opts.list && dirList.handle(pathname, opts.list)) {
dirList.send({
reply,
dir: dirList.path(opts.root, pathname),
options: opts.list,
route: pathname,
prefix,
dotfiles: opts.dotfiles
}).catch((err) => reply.send(err))
return
}

// root paths left to try?
if (Array.isArray(rootPath) && rootPathOffset < (rootPath.length - 1)) {
return pumpSendToReply(request, reply, pathname, rootPath, rootPathOffset + 1)
return reply.callNotFound()
}

if (opts.preCompressed && !checkedEncodings.has(encoding)) {
checkedEncodings.add(encoding)
return pumpSendToReply(
request,
reply,
pathnameOrig,
rootPath,
rootPathOffset,
undefined,
checkedEncodings
)
// The `send` library terminates the request with a 404 if the requested
// path contains a dotfile and `send` is initialized with `{dotfiles:
// 'ignore'}`. `send` aborts the request before getting far enough to
// check if the file exists (hence, a 404 `NotFoundError` instead of
// `ENOENT`).
// https://github.com/pillarjs/send/blob/de073ed3237ade9ff71c61673a34474b30e5d45b/index.js#L582
if (metadata.error.status === 404) {
return reply.callNotFound()
}

return reply.callNotFound()
await reply.send(metadata.error)
break
}

// The `send` library terminates the request with a 404 if the requested
// path contains a dotfile and `send` is initialized with `{dotfiles:
// 'ignore'}`. `send` aborts the request before getting far enough to
// check if the file exists (hence, a 404 `NotFoundError` instead of
// `ENOENT`).
// https://github.com/pillarjs/send/blob/de073ed3237ade9ff71c61673a34474b30e5d45b/index.js#L582
if (err.status === 404) {
return reply.callNotFound()
case 'file': {
reply.code(statusCode)
if (setHeaders !== undefined) {
setHeaders(reply.raw, metadata.path, metadata.stat)
}
reply.headers(headers)
if (encoding) {
reply.header('content-type', getContentType(pathname))
reply.header('content-encoding', encoding)
}
await reply.send(stream)
break
}

reply.send(err)
})

// we cannot use pump, because send error
// handling is not compatible
stream.pipe(wrap)
}
}

function setUpHeadAndGet (routeOpts, route, file, rootPath) {
Expand All @@ -393,11 +377,11 @@ async function fastifyStatic (fastify, opts) {
fastify.route(toSetUp)
}

function serveFileHandler (req, reply) {
async function serveFileHandler (req, reply) {
// TODO: remove the fallback branch when bump major
/* c8 ignore next */
const routeConfig = req.routeOptions?.config || req.routeConfig
pumpSendToReply(req, reply, routeConfig.file, routeConfig.rootPath)
return pumpSendToReply(req, reply, routeConfig.file, routeConfig.rootPath)
}
}

Expand Down Expand Up @@ -547,7 +531,7 @@ function getRedirectUrl (url) {
}

module.exports = fp(fastifyStatic, {
fastify: '4.x',
// fastify: '4.x',
name: '@fastify/static'
})
module.exports.default = fastifyStatic
Expand Down
6 changes: 3 additions & 3 deletions lib/dirList.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,17 +125,17 @@ const dirList = {
entries.dirs.forEach(entry => nameEntries.dirs.push(entry.name))
entries.files.forEach(entry => nameEntries.files.push(entry.name))

reply.send(nameEntries)
await reply.send(nameEntries)
} else {
reply.send(entries)
await reply.send(entries)
}
return
}

const html = options.render(
entries.dirs.map(entry => dirList.htmlInfo(entry, route, prefix, options)),
entries.files.map(entry => dirList.htmlInfo(entry, route, prefix, options)))
reply.type('text/html').send(html)
await reply.type('text/html').send(html)
},

/**
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"homepage": "https://github.com/fastify/fastify-static",
"dependencies": {
"@fastify/accept-negotiator": "^2.0.0-pre.fv5.1",
"@fastify/send": "^3.0.0-pre.fv5.1",
"@fastify/send": "^3.1.0",
"content-disposition": "^0.5.4",
"fastify-plugin": "^5.0.0-pre.fv5.1",
"fastq": "^1.17.1",
Expand Down
Loading

0 comments on commit cd0eae2

Please sign in to comment.