Skip to content

Commit

Permalink
Merge branch 'main' into fix/3736
Browse files Browse the repository at this point in the history
  • Loading branch information
metcoder95 authored Oct 16, 2024
2 parents 183f8e9 + 68107da commit 437f3ee
Show file tree
Hide file tree
Showing 79 changed files with 4,217 additions and 756 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ Returns a promise with the result of the `Dispatcher.request` method.

Calls `options.dispatcher.request(options)`.

See [Dispatcher.request](./docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback) for more details, and [request examples](./examples/README.md) for examples.
See [Dispatcher.request](./docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback) for more details, and [request examples](./docs/examples/README.md) for examples.

### `undici.stream([url, options, ]factory): Promise`

Expand Down
26 changes: 26 additions & 0 deletions benchmarks/fetch/webidl-is.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { bench, run, barplot } from 'mitata'
import { Headers, FormData } from '../../index.js'
import { webidl } from '../../lib/web/fetch/webidl.js'

const headers = new Headers()
const fd = new FormData()

barplot(() => {
bench('webidl.is.FormData (ok)', () => {
return webidl.is.FormData(fd)
})

bench('webidl.is.FormData (bad)', () => {
return !webidl.is.FormData(headers)
})

bench('instanceof (ok)', () => {
return fd instanceof FormData
})

bench('instanceof (bad)', () => {
return !(headers instanceof FormData)
})
})

await run()
116 changes: 116 additions & 0 deletions docs/docs/api/CacheStore.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Cache Store

A Cache Store is responsible for storing and retrieving cached responses.
It is also responsible for deciding which specific response to use based off of
a response's `Vary` header (if present).

## Pre-built Cache Stores

### `MemoryCacheStore`

The `MemoryCacheStore` stores the responses in-memory.

**Options**

- `maxEntries` - The maximum amount of responses to store. Default `Infinity`.
- `maxEntrySize` - The maximum size in bytes that a response's body can be. If a response's body is greater than or equal to this, the response will not be cached.

## Defining a Custom Cache Store

The store must implement the following functions:

### Getter: `isFull`

This tells the cache interceptor if the store is full or not. If this is true,
the cache interceptor will not attempt to cache the response.

### Function: `createReadStream`

Parameters:

* **req** `Dispatcher.RequestOptions` - Incoming request

Returns: `CacheStoreReadable | Promise<CacheStoreReadable | undefined> | undefined` - If the request is cached, a readable for the body is returned. Otherwise, `undefined` is returned.

### Function: `createWriteStream`

Parameters:

* **req** `Dispatcher.RequestOptions` - Incoming request
* **value** `CacheStoreValue` - Response to store

Returns: `CacheStoreWriteable | undefined` - If the store is full, return `undefined`. Otherwise, return a writable so that the cache interceptor can stream the body and trailers to the store.

## `CacheStoreValue`

This is an interface containing the majority of a response's data (minus the body).

### Property `statusCode`

`number` - The response's HTTP status code.

### Property `statusMessage`

`string` - The response's HTTP status message.

### Property `rawHeaders`

`(Buffer | Buffer[])[]` - The response's headers.

### Property `rawTrailers`

`string[] | undefined` - The response's trailers.

### Property `vary`

`Record<string, string> | undefined` - The headers defined by the response's `Vary` header
and their respective values for later comparison

For example, for a response like
```
Vary: content-encoding, accepts
content-encoding: utf8
accepts: application/json
```

This would be
```js
{
'content-encoding': 'utf8',
accepts: 'application/json'
}
```

### Property `cachedAt`

`number` - Time in millis that this value was cached.

### Property `staleAt`

`number` - Time in millis that this value is considered stale.

### Property `deleteAt`

`number` - Time in millis that this value is to be deleted from the cache. This
is either the same sa staleAt or the `max-stale` caching directive.

The store must not return a response after the time defined in this property.

## `CacheStoreReadable`

This extends Node's [`Readable`](https://nodejs.org/api/stream.html#class-streamreadable)
and defines extra properties relevant to the cache interceptor.

### Getter: `value`

The response's [`CacheStoreValue`](#cachestorevalue)

## `CacheStoreWriteable`

This extends Node's [`Writable`](https://nodejs.org/api/stream.html#class-streamwritable)
and defines extra properties relevant to the cache interceptor.

### Setter: `rawTrailers`

If the response has trailers, the cache interceptor will pass them to the cache
interceptor through this method.
10 changes: 10 additions & 0 deletions docs/docs/api/Dispatcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -1233,6 +1233,16 @@ test('should not error if request status code is not in the specified error code
The Response Error Interceptor provides a robust mechanism for handling HTTP response errors by capturing detailed error information and propagating it through a structured `ResponseError` class. This enhancement improves error handling and debugging capabilities in applications using the interceptor.
##### `Cache Interceptor`
The `cache` interceptor implements client-side response caching as described in
[RFC9111](https://www.rfc-editor.org/rfc/rfc9111.html).
**Options**
- `store` - The [`CacheStore`](./CacheStore.md) to store and retrieve responses from. Default is [`MemoryCacheStore`](./CacheStore.md#memorycachestore).
- `methods` - The [**safe** HTTP methods](https://www.rfc-editor.org/rfc/rfc9110#section-9.2.1) to cache the response of.
## Instance Events
### Event: `'connect'`
Expand Down
2 changes: 2 additions & 0 deletions docs/docs/api/MockAgent.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Extends: [`AgentOptions`](Agent.md#parameter-agentoptions)

* **agent** `Agent` (optional) - Default: `new Agent([options])` - a custom agent encapsulated by the MockAgent.

* **ignoreTrailingSlash** `boolean` (optional) - Default: `false` - set the default value for `ignoreTrailingSlash` for interceptors.

### Example - Basic MockAgent instantiation

This will instantiate the MockAgent. It will not do anything until registered as the agent to use with requests and mock interceptions are added.
Expand Down
1 change: 1 addition & 0 deletions docs/docs/api/MockPool.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Returns: `MockInterceptor` corresponding to the input options.
* **body** `string | RegExp | (body: string) => boolean` - (optional) - a matcher for the HTTP request body.
* **headers** `Record<string, string | RegExp | (body: string) => boolean`> - (optional) - a matcher for the HTTP request headers. To be intercepted, a request must match all defined headers. Extra headers not defined here may (or may not) be included in the request and do not affect the interception in any way.
* **query** `Record<string, any> | null` - (optional) - a matcher for the HTTP request query string params. Only applies when a `string` was provided for `MockPoolInterceptOptions.path`.
* **ignoreTrailingSlash** `boolean` - (optional) - set to `true` if the matcher should also match by ignoring potential trailing slashes in `MockPoolInterceptOptions.path`.

### Return: `MockInterceptor`

Expand Down
7 changes: 6 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ module.exports.interceptors = {
redirect: require('./lib/interceptor/redirect'),
retry: require('./lib/interceptor/retry'),
dump: require('./lib/interceptor/dump'),
dns: require('./lib/interceptor/dns')
dns: require('./lib/interceptor/dns'),
cache: require('./lib/interceptor/cache')
}

module.exports.cacheStores = {
MemoryCacheStore: require('./lib/cache/memory-cache-store')
}

module.exports.buildConnector = buildConnector
Expand Down
12 changes: 6 additions & 6 deletions lib/api/readable.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ class BodyReadable extends Readable {
* @see https://fetch.spec.whatwg.org/#dom-body-text
* @returns {Promise<string>}
*/
async text () {
text () {
return consume(this, 'text')
}

Expand All @@ -174,7 +174,7 @@ class BodyReadable extends Readable {
* @see https://fetch.spec.whatwg.org/#dom-body-json
* @returns {Promise<unknown>}
*/
async json () {
json () {
return consume(this, 'json')
}

Expand All @@ -184,7 +184,7 @@ class BodyReadable extends Readable {
* @see https://fetch.spec.whatwg.org/#dom-body-blob
* @returns {Promise<Blob>}
*/
async blob () {
blob () {
return consume(this, 'blob')
}

Expand All @@ -194,7 +194,7 @@ class BodyReadable extends Readable {
* @see https://fetch.spec.whatwg.org/#dom-body-bytes
* @returns {Promise<Uint8Array>}
*/
async bytes () {
bytes () {
return consume(this, 'bytes')
}

Expand All @@ -204,7 +204,7 @@ class BodyReadable extends Readable {
* @see https://fetch.spec.whatwg.org/#dom-body-arraybuffer
* @returns {Promise<ArrayBuffer>}
*/
async arrayBuffer () {
arrayBuffer () {
return consume(this, 'arrayBuffer')
}

Expand Down Expand Up @@ -355,7 +355,7 @@ function isUnusable (bodyReadable) {
* @param {string} type
* @returns {Promise<any>}
*/
async function consume (stream, type) {
function consume (stream, type) {
assert(!stream[kConsume])

return new Promise((resolve, reject) => {
Expand Down
Loading

0 comments on commit 437f3ee

Please sign in to comment.