Skip to content

Commit

Permalink
feat: dump interceptor (#3118)
Browse files Browse the repository at this point in the history
  • Loading branch information
metcoder95 committed May 15, 2024
1 parent 0980f9d commit 106bf1c
Show file tree
Hide file tree
Showing 8 changed files with 701 additions and 6 deletions.
32 changes: 32 additions & 0 deletions docs/docs/api/Dispatcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -952,6 +952,38 @@ const client = new Client("http://example.com").compose(
);
```

##### `dump`

The `dump` interceptor enables you to dump the response body from a request upon a given limit.

**Options**
- `maxSize` - The maximum size (in bytes) of the response body to dump. If the size of the request's body exceeds this value then the connection will be closed. Default: `1048576`.

> The `Dispatcher#options` also gets extended with the options `dumpMaxSize`, `abortOnDumped`, and `waitForTrailers` which can be used to configure the interceptor at a request-per-request basis.
**Example - Basic Dump Interceptor**

```js
const { Client, interceptors } = require("undici");
const { dump } = interceptors;

const client = new Client("http://example.com").compose(
dump({
maxSize: 1024,
})
);

// or
client.dispatch(
{
path: "/",
method: "GET",
dumpMaxSize: 1024,
},
handler
);
```

## Instance Events

### Event: `'connect'`
Expand Down
3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ module.exports.RedirectHandler = RedirectHandler
module.exports.createRedirectInterceptor = createRedirectInterceptor
module.exports.interceptors = {
redirect: require('./lib/interceptor/redirect'),
retry: require('./lib/interceptor/retry')
retry: require('./lib/interceptor/retry'),
dump: require('./lib/interceptor/dump')
}

module.exports.buildConnector = buildConnector
Expand Down
123 changes: 123 additions & 0 deletions lib/interceptor/dump.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
'use strict'

const util = require('../core/util')
const { InvalidArgumentError, RequestAbortedError } = require('../core/errors')
const DecoratorHandler = require('../handler/decorator-handler')

class DumpHandler extends DecoratorHandler {
#maxSize = 1024 * 1024
#abort = null
#dumped = false
#aborted = false
#size = 0
#reason = null
#handler = null

constructor ({ maxSize }, handler) {
super(handler)

if (maxSize != null && (!Number.isFinite(maxSize) || maxSize < 1)) {
throw new InvalidArgumentError('maxSize must be a number greater than 0')
}

this.#maxSize = maxSize ?? this.#maxSize
this.#handler = handler
}

onConnect (abort) {
this.#abort = abort

this.#handler.onConnect(this.#customAbort.bind(this))
}

#customAbort (reason) {
this.#aborted = true
this.#reason = reason
}

// TODO: will require adjustment after new hooks are out
onHeaders (statusCode, rawHeaders, resume, statusMessage) {
const headers = util.parseHeaders(rawHeaders)
const contentLength = headers['content-length']

if (contentLength != null && contentLength > this.#maxSize) {
throw new RequestAbortedError(
`Response size (${contentLength}) larger than maxSize (${
this.#maxSize
})`
)
}

if (this.#aborted) {
return true
}

return this.#handler.onHeaders(
statusCode,
rawHeaders,
resume,
statusMessage
)
}

onError (err) {
if (this.#dumped) {
return
}

err = this.#reason ?? err

this.#handler.onError(err)
}

onData (chunk) {
this.#size = this.#size + chunk.length

if (this.#size >= this.#maxSize) {
this.#dumped = true

if (this.#aborted) {
this.#handler.onError(this.#reason)
} else {
this.#handler.onComplete([])
}
}

return true
}

onComplete (trailers) {
if (this.#dumped) {
return
}

if (this.#aborted) {
this.#handler.onError(this.reason)
return
}

this.#handler.onComplete(trailers)
}
}

function createDumpInterceptor (
{ maxSize: defaultMaxSize } = {
maxSize: 1024 * 1024
}
) {
return dispatch => {
return function Intercept (opts, handler) {
const { dumpMaxSize = defaultMaxSize } =
opts

const dumpHandler = new DumpHandler(
{ maxSize: dumpMaxSize },
handler
)

return dispatch(opts, dumpHandler)
}
}
}

module.exports = createDumpInterceptor
Loading

0 comments on commit 106bf1c

Please sign in to comment.