Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
fenos committed Dec 7, 2023
2 parents 0846a40 + f1a4ac3 commit 562a875
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 3 deletions.
6 changes: 6 additions & 0 deletions packages/server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ This can be used to implement post-processing validation.
This can be used for things like access control.
You can `throw` an Object and the HTTP request will be aborted with the provided `body` and `status_code` (or their fallbacks).

#### `options.onResponseError`

`onResponseError` will be invoked when an error response is about to be sent by the server.
you use this function to map custom errors to tus errors or for custom observability. (`(req, res, err) => Promise<{status_code: number; body: string} | void> | {status_code: number; body: string} | void`)

#### `server.handle(req, res)`

The main server request handler invoked on every request.
Expand Down Expand Up @@ -203,6 +208,7 @@ const port = 1080
const app = express()
const uploadApp = express()
const server = new Server({
path: '/uploads',
datastore: new FileStore({directory: '/files'}),
})

Expand Down
19 changes: 16 additions & 3 deletions packages/server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,22 @@ export class Server extends EventEmitter {
req.method = (req.headers['x-http-method-override'] as string).toUpperCase()
}

const onError = (error: {status_code?: number; body?: string; message: string}) => {
const status_code = error.status_code || ERRORS.UNKNOWN_ERROR.status_code
const body = error.body || `${ERRORS.UNKNOWN_ERROR.body}${error.message || ''}\n`
const onError = async (error: {
status_code?: number
body?: string
message: string
}) => {
let status_code = error.status_code || ERRORS.UNKNOWN_ERROR.status_code
let body = error.body || `${ERRORS.UNKNOWN_ERROR.body}${error.message || ''}\n`

if (this.options.onResponseError) {
const errorMapping = await this.options.onResponseError(req, res, error as Error)
if (errorMapping) {
status_code = errorMapping.status_code
body = errorMapping.body
}
}

return this.write(res, status_code, body)
}

Expand Down
10 changes: 10 additions & 0 deletions packages/server/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ export type ServerOptions = {
res: http.ServerResponse,
uploadId: string
) => Promise<void>
// `onResponseError` will be invoked when an error response is about to be sent by the server.
// you use this function to map custom errors to tus errors or for custom observability.
onResponseError?: (
req: http.IncomingMessage,
res: http.ServerResponse,
err: Error | {status_code: number; body: string}
) =>
| Promise<{status_code: number; body: string} | void>
| {status_code: number; body: string}
| void
}

export type RouteHandler = (req: http.IncomingMessage, res: http.ServerResponse) => void
27 changes: 27 additions & 0 deletions packages/server/test/Server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {Server} from '../src'
import {FileStore} from '@tus/file-store'
import {DataStore} from '../src/models'
import {TUS_RESUMABLE, EVENTS} from '../src/constants'
import sinon from 'sinon'

// Test server crashes on http://{some-ip} so we remove the protocol...
const removeProtocol = (location: string) => location.slice(6)
Expand Down Expand Up @@ -439,5 +440,31 @@ describe('Server', () => {
})
})
})

it('should fire onResponseError hook when an error is thrown', async () => {
const spy = sinon.spy()
const server = new Server({
path: '/test/output',
datastore: new FileStore({directory: './test/output'}),
onResponseError: () => {
spy()
return {status_code: 404, body: 'custom-error'}
},
onUploadFinish() {
throw {body: 'no', status_code: 500}
},
})

await request(server.listen())
.post(server.options.path)
.set('Tus-Resumable', TUS_RESUMABLE)
.set('Upload-Length', '4')
.set('Upload-Offset', '0')
.set('Content-Type', 'application/offset+octet-stream')
.send('test')
.expect(404, 'custom-error')

assert.equal(spy.calledOnce, true)
})
})
})

0 comments on commit 562a875

Please sign in to comment.