Skip to content

Commit

Permalink
refactor: Client.clients & Agent is a Client.
Browse files Browse the repository at this point in the history
Refs: #616
  • Loading branch information
ronag committed Mar 28, 2021
1 parent 0c92094 commit 4288f00
Show file tree
Hide file tree
Showing 19 changed files with 629 additions and 472 deletions.
56 changes: 56 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,62 @@ for await (const data of body) {
console.log('trailers', trailers)
```

## `undici.request(url[, opts]): Promise`

* url `string | URL | object`
* opts `{ dispatcher: Dispatcher } & client.request.opts`
* // TODO: document maxRedirections?

`url` may contain path. `opts` may not contain path. `opts.method` is `GET` by default.
Calls `pool.request(opts)` on the pool returned from either the globalAgent (see [setGlobalDispatcher](#undicisetglobalagentagent)) or the agent passed to the `opts` argument.

Returns a promise with the result of the `request` method.

## `undici.stream(url, opts, factory): Promise`

* url `string | URL | object`
* opts `{ dispatcher: Dispatcher } & client.stream.opts`
* factory `client.stream.factory`
* // TODO: document maxRedirections?

`url` may contain path. `opts` may not contain path.
See [client.stream](docs/api/Client.md#clientstreamoptions-factory--callback) for details on the `opts` and `factory` arguments.
Calls `pool.stream(opts, factory)` on the pool returned from either the globalAgent (see [setGlobalDispatcher](#undicisetglobalagentagent)) or the agent passed to the `opts` argument.
Result is returned in the factory function. See [client.stream](docs/api/Client.md#clientstreamoptions-factory--callback) for more details.

## `undici.pipeline(url, opts, handler): Duplex`

* url `string | URL | object`
* opts `{ dispatcher: Dispatcher } & client.pipeline.opts`
* handler `client.pipeline.handler`
* // TODO: document maxRedirections?

`url` may contain path. `opts` may not contain path.

See [client.pipeline](docs/api/Client.md#clientpipelining) for details on the `opts` and `handler` arguments.

Calls `pool.pipeline(opts, factory)` on the pool returned from either the globalAgent (see [setGlobalDispatcher](#undicisetglobalagentagent)) or the agent passed to the `opts` argument.

See [client.pipeline](docs/api/Client.md#clientpipelining) for more details.

### `undici.connect(options[, callback])`

TODO: document

### `undici.upgrade(options[, callback])`

TODO: document

## `undici.setGlobalDispatcher(dispatcher)`

* dispatcher `Dispatcher`

Sets the global dispatcher used by global API methods.

## `undici.getGlobalDispatcher()`

TODO: document

## Specification Compliance

This section documents parts of the HTTP/1.1 specification which Undici does
Expand Down
120 changes: 45 additions & 75 deletions docs/api/Agent.md
Original file line number Diff line number Diff line change
@@ -1,119 +1,89 @@
# Agent

## `new undici.Agent(opts)`
Extends: `undici.Dispatcher`

Arguments:

* **factory** - Default: `(origin, opts) => new Pool(origin, opts)`
* // TODO: document rest opts?
Agent allow dispatching requests against multiple different origins.

Returns: `Agent`
Requests are not guaranteed to be dispatched in order of invocation.

Returns a new Agent instance used for dispatching requests.
## `new undici.Agent([options])`

### `Agent.get(origin)`

* origin `string` - A origin to be retrieved from the Agent.
Arguments:

This method retrieves Client instances from the Agent. If the client does not exist it is automatically added by calling
the `factory` method passed through the `Agent` constructor.
* **options** `AgentOptions` (optional)

### `Agent.dispatch(options, handlers)`
Returns: `Agent`

Dispatches a request.
### Parameter: `AgentOptions`

This API is expected to evolve through semver-major versions and is less stable than the preceding higher level APIs. It is primarily intended for library developers who implement higher level APIs on top of this.
Extends: [`ClientOptions`](docs/api/Pool.md#parameter-pooloptions)

Arguments:
* **factory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Pool(origin, opts)`
* **maxRedirections** `Integer` - Default: `0`.

* **options** `DispatchOptions`
* **handlers** `DispatchHandlers`
## Instance Properties

Returns: `void`
### `Pool.closed`

#### Parameter: `DispatchOptions`
Implements [Client.closed](docs/api/Client.md#clientclosed)

* **origin** `string | URL`
* **path** `string`
* **method** `string`
* **body** `string | Buffer | Uint8Array | stream.Readable | null` (optional) - Default: `null`
* **headers** `UndiciHeaders` (optional) - Default: `null`
* **idempotent** `boolean` (optional) - Default: `true` if `method` is `'HEAD'` or `'GET'` - Whether the requests can be safely retried or not. If `false` the request won't be sent until all preceeding requests in the pipeline has completed.
* **upgrade** `string | null` (optional) - Default: `method === 'CONNECT' || null` - Upgrade the request. Should be used to specify the kind of upgrade i.e. `'Websocket'`.
### `Pool.connected`

#### Parameter: `DispatchHandlers`
Implements [Client.connected](docs/api/Client.md#clientconnected)

* **onConnect** `(abort: () => void) => void` - Invoked before request is dispatched on socket. May be invoked multiple times when a request is retried when the request at the head of the pipeline fails.
* **onError** `(error: Error) => void` - Invoked when an error has occurred.
* **onUpgrade** `(statusCode: number, headers: string[] | null, socket: Duplex) => void` (optional) - Invoked when request is upgraded. Required if `DispatchOptions.upgrade` is defined or `DispatchOptions.method === 'CONNECT'`.
* **onHeaders** `(statusCode: number, headers: string[] | null, resume: () => void) => boolean` - Invoked when statusCode and headers have been received. May be invoked multiple times due to 1xx informational headers. Not required for `upgrade` requests.
* **onData** `(chunk: Buffer) => boolean` - Invoked when response payload data is received. Not required for `upgrade` requests.
* **onComplete** `(trailers: string[] | null) => void` - Invoked when response payload and trailers have been received and the request has completed. Not required for `upgrade` requests.
### `Pool.destroyed`

## `agent.close(): Promise`
Implements [Client.destroyed](docs/api/Client.md#clientdestroyed)

Returns a `Promise.all` operation closing all of the pool instances in the Agent instance. This calls `pool.close` under the hood.
### `Pool.pending`

## `agent.destroy(): Promise`
Implements [Client.pending](docs/api/Client.md#clientpending)

Returns a `Promise.all` operation destroying all of the pool instances in the Agent instance. This calls `pool.destroy` under the hood.
### `Pool.running`

## `undici.setGlobalAgent(agent)`
Implements [Client.running](docs/api/Client.md#clientrunning)

* agent `Agent`
### `Pool.size`

Sets the global agent used by `request`, `pipeline`, and `stream` methods.
The default global agent creates `undici.Pool`s with no max number of
connections.
Implements [Client.size](docs/api/Client.md#clientsize)

The agent must only **implement** the `Agent` API; not necessary extend from it.
## Instance Methods

## `undici.getGlobalAgent(agent)`
### `Agent.close(callback)`

TODO: document
Implements [`Dispatcher.close([callback])`](docs/api/Client.md#clientclose-callback-).

## `undici.request(url[, opts]): Promise`
### `Agent.connect(options[, callback])`

* url `string | URL | object`
* opts `{ agent: Agent } & client.request.opts`
* // TODO: document maxRedirections?
Implements [`Client.connect(options[, callback])`](docs/api/Client.md#clientconnectoptions--callback).

`url` may contain path. `opts` may not contain path. `opts.method` is `GET` by default.
Calls `pool.request(opts)` on the pool returned from either the globalAgent (see [setGlobalAgent](#undicisetglobalagentagent)) or the agent passed to the `opts` argument.
### `Agent.destroy([error, callback])`

Returns a promise with the result of the `request` method.
Implements [`Dispatcher.destroy([error, callback])`](docs/api/Client.md#clientdestroyerror).

## `undici.stream(url, opts, factory): Promise`
### `Agent.dispatch(options, handlers: AgentDispatchOptions)`

* url `string | URL | object`
* opts `{ agent: Agent } & client.stream.opts`
* factory `client.stream.factory`
* // TODO: document maxRedirections?
Implements [`Client.dispatch(options, handlers)`](docs/api/Client.md#clientdispatchoptions-handlers).

`url` may contain path. `opts` may not contain path.
See [client.stream](docs/api/Client.md#clientstreamoptions-factory--callback) for details on the `opts` and `factory` arguments.
Calls `pool.stream(opts, factory)` on the pool returned from either the globalAgent (see [setGlobalAgent](#undicisetglobalagentagent)) or the agent passed to the `opts` argument.
Result is returned in the factory function. See [client.stream](docs/api/Client.md#clientstreamoptions-factory--callback) for more details.
#### Parameter: `AgentDispatchOptions`

## `undici.pipeline(url, opts, handler): Duplex`
Extends: [`DispatchOptions``](docs/api/Dispatch.md#parameter-dispatchoptions)

* url `string | URL | object`
* opts `{ agent: Agent } & client.pipeline.opts`
* handler `client.pipeline.handler`
* // TODO: document maxRedirections?
* **origin** `string | URL`
* **maxRedirections** `Integer`.

`url` may contain path. `opts` may not contain path.
### `Agent.pipeline(options, handler)`

See [client.pipeline](docs/api/Client.md#clientpipelining) for details on the `opts` and `handler` arguments.
Implements [`Client.pipeline(options, handler)`](docs/api/Client.md#clientpipelineoptions-handler).

Calls `pool.pipeline(opts, factory)` on the pool returned from either the globalAgent (see [setGlobalAgent](#undicisetglobalagentagent)) or the agent passed to the `opts` argument.
### `Agent.request(options[, callback])`

See [client.pipeline](docs/api/Client.md#clientpipelining) for more details.
Implements [`Client.request(options [, callback])`](docs/api/Client.md#clientrequestoptions--callback).

### `undici.connect(options[, callback])`
### `Agent.stream(options, factory[, callback])`

TODO: document
Implements [`Client.stream(options, factory[, callback])`](docs/api/Client.md#clientstreamoptions-factory--callback).

### `undici.upgrade(options[, callback])`
### `Agent.upgrade(options[, callback])`

TODO: document
Implements [`Client.upgrade(options[, callback])`](docs/api/Client.md#clientupgradeoptions-callback).
79 changes: 17 additions & 62 deletions docs/api/Client.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# Class: Client

Extends: `events.EventEmitter`
Extends: `undici.Dispatcher`

A basic HTTP/1.1 client, mapped on top a single TCP/TLS connection. Pipelining is disabled by default.

Imports: `http`, `stream`, `events`
Requests are not guaranteed to be dispatched in order of invocation.

## `new Client(url[, options])`

Arguments:

* **url** `URL | string` - It should only include the **protocol, hostname, and port**.
* **url** `URL | string` - Should only include the **protocol, hostname, and port**.
* **options** `ClientOptions` (optional)

Returns: `Client`
Expand Down Expand Up @@ -43,13 +43,7 @@ const client = new Client('http://localhost:3000')

### `Client.close([callback])`

Closes the client and gracefully waits for enqueued requests to complete before resolving.

Arguments:

* **callback** `(error: Error | null, data: null) => void` (optional)

Returns: `void | Promise<null>` - Only returns a `Promise` if no `callback` argument was passed
Implements [`Dispatcher.close([callback])`](docs/api/Client.md#clientclose-callback-).

#### Example - Request resolves before Client closes

Expand Down Expand Up @@ -153,25 +147,11 @@ server.listen(() => {
})
```

### `Client.destroy([error][, callback])`

Destroy the client abruptly with the given error. All the pending and running requests will be asynchronously aborted and error. Waits until socket is closed before invoking the callback (or returning a promise if no callback is provided). Since this operation is asynchronously dispatched there might still be some progress on dispatched requests.

Both arguments are optional; the method can be called in four different ways:

```js
client.destroy() // -> Promise
client.destroy(new Error()) // -> Promise
client.destroy(() => {}) // -> void
client.destroy(new Error(), () => {}) // -> void
```

Arguments:
### `Client.destroy([error, callback])`

* **error** `Error | null` (optional)
* **callback** `() => void` (optional)
Implements [`Dispatcher.destroy([error, callback])`](docs/api/Dispatcher.md#dispatcher-callback-).

Returns: `void | Promise<void>` - Only returns a `Promise` if no `callback` argument was passed
Waits until socket is closed before invoking the callback (or returning a promise if no callback is provided).

#### Example - Request is aborted when Client is destroyed

Expand Down Expand Up @@ -206,36 +186,9 @@ server.listen(() => {
})
```

### `Client.dispatch(options, handlers)`

This is the low level API which all the preceding APIs are implemented on top of.

This API is expected to evolve through semver-major versions and is less stable than the preceding higher level APIs. It is primarily intended for library developers who implement higher level APIs on top of this.

Arguments:

* **options** `DispatchOptions`
* **handlers** `DispatchHandlers`

Returns: `void`

#### Parameter: `DispatchOptions`

* **path** `string`
* **method** `string`
* **body** `string | Buffer | Uint8Array | stream.Readable | null` (optional) - Default: `null`
* **headers** `UndiciHeaders` (optional) - Default: `null`
* **idempotent** `boolean` (optional) - Default: `true` if `method` is `'HEAD'` or `'GET'` - Whether the requests can be safely retried or not. If `false` the request won't be sent until all preceding requests in the pipeline has completed.
* **upgrade** `string | null` (optional) - Default: `method === 'CONNECT' || null` - Upgrade the request. Should be used to specify the kind of upgrade i.e. `'Websocket'`.

#### Parameter: `DispatchHandlers`
### `Client.dispatch(options, handler)`

* **onConnect** `(abort: () => void) => void` - Invoked before request is dispatched on socket. May be invoked multiple times when a request is retried when the request at the head of the pipeline fails.
* **onError** `(error: Error) => void` - Invoked when an error has occurred.
* **onUpgrade** `(statusCode: number, headers: string[] | null, socket: Duplex) => void` (optional) - Invoked when request is upgraded. Required if `DispatchOptions.upgrade` is defined or `DispatchOptions.method === 'CONNECT'`.
* **onHeaders** `(statusCode: number, headers: string[] | null, resume: () => void) => boolean` - Invoked when statusCode and headers have been received. May be invoked multiple times due to 1xx informational headers. Not required for `upgrade` requests.
* **onData** `(chunk: Buffer) => boolean` - Invoked when response payload data is received. Not required for `upgrade` requests.
* **onComplete** `(trailers: string[] | null) => void` - Invoked when response payload and trailers have been received and the request has completed. Not required for `upgrade` requests.
Implements [`Dispatcher.dispatch(options,handler)`](docs/api/Dispatcher.md#dispatchedispatch).

#### Example 1 - Dispatch GET request

Expand Down Expand Up @@ -818,7 +771,8 @@ The URL of the Client instance.

Parameters:

* **client** `Client`
* **origin** `URL`
* **targets** `Array<Dispatcher>`

Emitted when a socket has been created and connected. The client will connect once `client.size > 0`.

Expand All @@ -836,8 +790,8 @@ const server = createServer((request, response) => {
server.listen(() => {
const client = new Client(`http://localhost:${server.address().port}`)

client.on('connect', client => {
console.log(`Connected to ${client.url}`) // should print before the request body statement
client.on('connect', (origin) => {
console.log(`Connected to ${origin}`) // should print before the request body statement
})

client.request({
Expand All @@ -860,7 +814,8 @@ server.listen(() => {

Parameters:

* **client** `Client`
* **origin** `URL`
* **targets** `Array<Dispatcher>`
* **error** `Error`

Emitted when socket has disconnected. The error argument of the event is the error which caused the socket to disconnect. The client will reconnect if or once `client.size > 0`.
Expand All @@ -879,8 +834,8 @@ const server = createServer((request, response) => {
server.listen(() => {
const client = new Client(`http://localhost:${server.address().port}`)

client.on('disconnect', client => {
console.log(`Disconnected from ${client.url}`) // should print before the SocketError
client.on('disconnect', (origin) => {
console.log(`Disconnected from ${origin}`) // should print before the SocketError
})

client.request({
Expand Down
Loading

0 comments on commit 4288f00

Please sign in to comment.