Socket
Socket
Sign inDemoInstall

undici

Package Overview
Dependencies
0
Maintainers
2
Versions
205
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

undici

An HTTP/1.1 client, written from scratch for Node.js


Version published
Weekly downloads
9.3M
increased by5.43%
Maintainers
2
Install size
328 kB
Created
Weekly downloads
 

Package description

What is undici?

The undici npm package is a HTTP/1.1 client, written from scratch for Node.js, that is designed to be faster and more efficient than the built-in 'http' and 'https' modules. It provides a low-level API for making HTTP requests and can be used to build higher-level abstractions.

What are undici's main functionalities?

HTTP Request

Make an HTTP request and process the response. This is the basic functionality of undici, allowing you to send HTTP requests and receive responses.

const { request } = require('undici');

(async () => {
  const { statusCode, headers, body } = await request('https://example.com')
  console.log('response received', statusCode);
  for await (const data of body) {
    console.log('data', data);
  }
})();

HTTP Pool

Use a pool of connections to make HTTP requests. This is useful for making a large number of requests to the same server, as it reuses connections between requests.

const { Pool } = require('undici');

const pool = new Pool('https://example.com')

async function query() {
  const { body } = await pool.request({
    path: '/path',
    method: 'GET'
  })

  for await (const data of body) {
    console.log('data', data);
  }
}

query();

HTTP Stream

Stream an HTTP response to a file or another stream. This is useful for handling large responses that you don't want to hold in memory.

const { pipeline } = require('undici');
const fs = require('fs');

pipeline(
  'https://example.com',
  fs.createWriteStream('output.txt'),
  (err) => {
    if (err) {
      console.error('Pipeline failed', err);
    } else {
      console.log('Pipeline succeeded');
    }
  }
);

HTTP Upgrade

Upgrade an HTTP connection to another protocol, such as WebSockets. This is useful for protocols that start with an HTTP handshake and then upgrade to a different protocol.

const { connect } = require('undici');

(async () => {
  const { socket, statusCode, headers } = await connect({
    path: '/path',
    method: 'GET'
  });

  console.log('upgrade response', statusCode, headers);

  socket.on('data', (chunk) => {
    console.log('data', chunk.toString());
  });
})();

Other packages similar to undici

Readme

Source

undici

Node CI js-standard-style npm version

A HTTP/1.1 client, written from scratch for Node.js.

Undici means eleven in Italian. 1.1 -> 11 -> Eleven -> Undici. It is also a Stranger Things reference.

Install

npm i undici

Benchmarks

Machine: 2.8GHz AMD EPYC 7402P
Configuration: Node v14.4, HTTP/1.1 without TLS, 100 connections, Linux 5.4.12-1-lts

http - keepalive x 5,882 ops/sec ±1.87% (274 runs sampled)
undici - pipeline x 9,189 ops/sec ±2.02% (272 runs sampled)
undici - request x 12,623 ops/sec ±0.89% (277 runs sampled)
undici - stream x 14,136 ops/sec ±0.61% (280 runs sampled)
undici - dispatch x 14,883 ops/sec ±0.44% (281 runs sampled)

The benchmark is a simple hello world example.

API

new undici.Client(url, opts)

A basic HTTP/1.1 client, mapped on top a single TCP/TLS connection. Keepalive is enabled by default, and it cannot be turned off.

url can be a string or a URL object. It should only include the protocol, hostname, and the port.

Options:

  • socketTimeout: Number, the timeout after which a socket with active requests will time out. Monitors time between activity on a connected socket. Use 0 to disable it entirely. Default: 30e3 milliseconds (30s).

  • socketPath: String|Null, an IPC endpoint, either Unix domain socket or Windows named pipe. Default: null.

  • idleTimeout: Number, the timeout after which a socket without active requests will time out. Monitors time between activity on a connected socket. This value may be overriden by keep-alive hints from the server. Default: 4e3 milliseconds (4s).

  • keepAlive: Boolean, enable or disable keep alive connections. Default: true.

  • keepAliveMaxTimeout: Number, the maximum allowed idleTimeout when overriden by keep-alive hints from the server. Default: 600e3 milliseconds (10min).

  • keepAliveTimeoutThreshold: Number, a number subtracted from server keep-alive hints when overriding idleTimeout to account for timing inaccuries caused by e.g. transport latency. Default: 1e3 milliseconds (1s).

  • pipelining: Number, the amount of concurrent requests to be sent over the single TCP/TLS connection according to RFC7230. Default: 1.

  • tls: Object|Null, an options object which in the case of https will be passed to tls.connect. Default: null.

  • maxHeaderSize: Number, the maximum length of request headers in bytes. Default: 16384 (16KiB).

  • headersTimeout: Number, the amount of time the parser will wait to receive the complete HTTP headers (Node 14 and above only). Default: 30e3 milliseconds (30s).

client.request(opts[, callback(err, data)]): Promise|Void

Performs a HTTP request.

Options:

  • path: String
  • method: String
  • opaque: Any
  • body: String|Buffer|Uint8Array|stream.Readable|Null Default: null.
  • headers: Object|Null, an object with header-value pairs. Default: null.
  • signal: AbortController|EventEmitter|Null Default: null.
  • requestTimeout: Number, the timeout after which a request will time out, in milliseconds. Monitors time between request being enqueued and receiving a response. Use 0 to disable it entirely. Default: 30e3 milliseconds (30s).
  • idempotent: Boolean, 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. Default: true if method is HEAD or GET.

Headers are represented by an object like this:

{
  'content-length': '123',
  'content-type': 'text/plain',
  connection: 'keep-alive',
  host: 'mysite.com',
  accept: '*/*'
}

Keys are lowercased. Values are not modified. If you don't specify a host header, it will be derived from the url of the client instance.

The data parameter in callback is defined as follow:

  • statusCode: Number
  • opaque: Any
  • headers: Object, an object where all keys have been lowercased.
  • body: stream.Readable response payload. A user must either fully consume or destroy the body unless there is an error, or no further requests will be processed.

Returns a promise if no callback is provided.

Example:

const { Client } = require('undici')
const client = new Client(`http://localhost:3000`)

client.request({
  path: '/',
  method: 'GET'
}, function (err, data) {
  if (err) {
    // handle this in some way!
    return
  }

  const {
    statusCode,
    headers,
    body
  } = data

  console.log('response received', statusCode)
  console.log('headers', headers)

  body.setEncoding('utf8')
  body.on('data', console.log)

  client.close()
})

Non-idempotent requests will not be pipelined in order to avoid indirect failures.

Idempotent requests will be automatically retried if they fail due to indirect failure from the request at the head of the pipeline. This does not apply to idempotent requests with a stream request body.

Aborting a request

A request can may be aborted using either an AbortController or an EventEmitter. To use AbortController, you will need to npm i abort-controller.

const { AbortController } = require('abort-controller')
const { Client } = require('undici')

const client = new Client('http://localhost:3000')
const abortController = new AbortController()

client.request({
  path: '/',
  method: 'GET',
  signal: abortController.signal
}, function (err, data) {
  console.log(err) // RequestAbortedError
  client.close()
})

abortController.abort()

Alternatively, any EventEmitter that emits an 'abort' event may be used as an abort controller:

const EventEmitter = require('events')
const { Client } = require('undici')

const client = new Client'http://localhost:3000')
const ee = new EventEmitter()

client.request({
  path: '/',
  method: 'GET',
  signal: ee
}, function (err, data) {
  console.log(err) // RequestAbortedError
  client.close()
})

ee.emit('abort')

Destroying the request or response body will have the same effect.

client.stream(opts, factory(data)[, callback(err)]): Promise|Void

A faster version of request.

Unlike request this method expects factory to return a Writable which the response will be written to. This improves performance by avoiding creating an intermediate Readable when the user expects to directly pipe the response body to a Writable.

Options:

The data parameter in factory is defined as follow:

  • statusCode: Number
  • headers: Object, an object where all keys have been lowercased.
  • opaque: Any

The data parameter in callback is defined as follow:

  • opaque: Any
  • trailers: Object, an object where all keys have been lowercased.

Returns a promise if no callback is provided.

const { Client } = require('undici')
const client = new Client(`http://localhost:3000`)
const fs = require('fs')

client.stream({
  path: '/',
  method: 'GET',
  opaque: filename
}, ({ statusCode, headers, opaque: filename }) => {
  console.log('response received', statusCode)
  console.log('headers', headers)
  return fs.createWriteStream(filename)
}, (err) => {
  if (err) {
    console.error('failure', err)
  } else {
    console.log('success')
  }
})

opaque makes it possible to avoid creating a closure for the factory method:

function (req, res) {
   return client.stream({ ...opts, opaque: res }, proxy)
}

Instead of:

function (req, res) {
   return client.stream(opts, (data) => {
     // Creates closure to capture `res`.
     proxy({ ...data, opaque: res })
   }
}

client.pipeline(opts, handler(data)): Duplex

For easy use with stream.pipeline.

Options:

The data parameter in handler is defined as follow:

  • statusCode: Number
  • headers: Object, an object where all keys have been lowercased.
  • opaque: Any
  • body: stream.Readable response payload. A user must either fully consume or destroy the body unless there is an error, or no further requests will be processed.

handler should return a Readable from which the result will be read. Usually it should just return the body argument unless some kind of transformation needs to be performed based on e.g. headers or statusCode.

The handler should validate the response and save any required state. If there is an error it should be thrown.

Returns a Duplex which writes to the request and reads from the response.

const { Client } = require('undici')
const client = new Client(`http://localhost:3000`)
const fs = require('fs')
const stream = require('stream')

stream.pipeline(
  fs.createReadStream('source.raw'),
  client.pipeline({
    path: '/',
    method: 'PUT',
  }, ({ statusCode, headers, body }) => {
    if (statusCode !== 201) {
      throw new Error('invalid response')
    }

    if (isZipped(headers)) {
      return pipeline(body, unzip(), () => {})
    }

    return body
  }),
  fs.createWriteStream('response.raw'),
  (err) => {
    if (err) {
      console.error('failed')
    } else {
      console.log('succeeded')
    }
  }
)

client.upgrade(opts[, callback(err, data)]): Promise|Void

Upgrade to a different protocol.

Options:

  • path: String
  • opaque: Any
  • method: String Default: GET
  • headers: Object|Null, an object with header-value pairs. Default: null
  • signal: AbortController|EventEmitter|Null. Default: null
  • requestTimeout: Number, the timeout after which a request will time out, in milliseconds. Monitors time between request being enqueued and receiving a response. Use 0 to disable it entirely. Default: 30e3 milliseconds (30s).
  • protocol: String, a string of comma separated protocols, in descending preference order. Default: Websocket.

The data parameter in callback is defined as follow:

  • headers: Object, an object where all keys have been lowercased.
  • socket: Duplex
  • opaque

Returns a promise if no callback is provided.

client.connect(opts[, callback(err, data)]): Promise|Void

Starts two-way communications with the requested resource.

Options:

  • path: String
  • opaque: Any
  • headers: Object|Null, an object with header-value pairs. Default: null
  • signal: AbortController|EventEmitter|Null. Default: null
  • requestTimeout: Number, the timeout after which a request will time out, in milliseconds. Monitors time between request being enqueued and receiving a response. Use 0 to disable it entirely. Default: 30e3 milliseconds (30s).

The data parameter in callback is defined as follow:

  • statusCode: Number
  • headers: Object, an object where all keys have been lowercased.
  • socket: Duplex
  • opaque: Any

Returns a promise if no callback is provided.

client.dispatch(opts, handler): Promise|Void

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

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

Options:

  • path: String
  • method: String
  • body: String|Buffer|Uint8Array|stream.Readable|Null Default: null.
  • headers: Object|Null, an object with header-value pairs. Default: null.
  • requestTimeout: Number, the timeout after which a request will time out, in milliseconds. Monitors time between request being enqueued and receiving a response. Use 0 to disable it entirely. Default: 30e3 milliseconds (30s).
  • idempotent: Boolean, 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. Default: true if method is HEAD or GET.

The handler parameter is defined as follow:

  • onConnect(abort), 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.
    • abort(): Void, abort request.
  • onUpgrade(statusCode, headers, socket): Void, invoked when request is upgraded either due to a Upgrade header or CONNECT method.
    • statusCode: Number
    • headers: Array|Null
    • socket: Duplex
  • onHeaders(statusCode, headers, resume): Void, invoked when statusCode and headers have been received. May be invoked multiple times due to 1xx informational headers.
    • statusCode: Number
    • headers: Array|Null, an array of key-value pairs. Keys are not automatically lowercased.
    • resume(): Void, resume onData after returning false.
  • onData(chunk): Boolean, invoked when response payload data is received.
    • chunk: Buffer
  • onComplete(trailers): Void, invoked when response payload and trailers have been received and the request has completed.
    • trailers: Array|Null
  • onError(err): Void, invoked when an error has occured.
    • err: Error

The caller is responsible for handling the body argument, in terms of 'error' events and destroy():ing up until the onConnect handler has been invoked.

client.close([callback]): Promise|Void

Closes the client and gracefully waits fo enqueued requests to complete before invoking the callback.

Returns a promise if no callback is provided.

client.destroy([err][, callback]): Promise|Void

Destroy the client abruptly with the given err. All the pending and running requests will be asynchronously aborted and error. Waits until socket is closed before invoking the callback. Since this operation is asynchronously dispatched there might still be some progress on dispatched requests.

Returns a promise if no callback is provided.

client.pipelining: Number

Property to get and set the pipelining factor.

client.pending: Number

Number of queued requests.

client.running: Number

Number of inflight requests.

client.size: Number

Number of pending and running requests.

client.connected: Boolean

True if the client has an active connection. The client will lazily create a connection when it receives a request and will destroy it if there is no activity for the duration of the timeout value.

client.busy: Boolean

True if pipeline is saturated or blocked. Indicicates whether dispatching further requests is meaningful.

client.closed: Boolean

True after client.close() has been called.

client.destroyed: Boolean

True after client.destroyed() has been called or client.close() has been called and the client shutdown has completed.

Events
  • 'drain', emitted when pipeline is no longer fully saturated.

  • 'connect', emitted when a socket has been created and connected. The client will connect once client.size > 0.

  • 'disconnect', emitted when socket has disconnected. The first argument of the event is the error which caused the socket to disconnect. The client will reconnect if or once client.size > 0.

new undici.Pool(url, opts)

A pool of Client connected to the same upstream target.

Options:

  • ... same as Client.
  • connections, the number of clients to create. Default 10.

Pool does not guarantee that requests are dispatched in order of invocation.

pool.request(opts[, callback]): Promise|Void

Calls client.request(opts, callback) on one of the clients.

pool.stream(opts, factory[, callback]): Promise|Void

Calls client.stream(opts, factory, callback) on one of the clients.

pool.pipeline(opts, handler): Duplex

Calls client.pipeline(opts, handler) on one of the clients.

pool.upgrade(opts[, callback]): Promise|Void

Calls client.upgrade(opts, callback) on one of the clients.

pool.connect(opts[, callback]): Promise|Void

Calls client.connect(opts, callback) on one of the clients.

pool.dispatch(opts, handler): Void

Calls client.dispatch(opts, handler) on one of the clients.

pool.close([callback]): Promise|Void

Calls client.close(callback) on all the clients.

pool.destroy([err][, callback]): Promise|Void

Calls client.destroy(err, callback) on all the clients.

undici.errors

Undici exposes a variety of error objects that you can use to enhance your error handling. You can find all the error objects inside the errors key.

const { errors } = require('undici')
ErrorError CodesDescription
InvalidArgumentErrorUND_ERR_INVALID_ARGpassed an invalid argument.
InvalidReturnValueErrorUND_ERR_INVALID_RETURN_VALUEreturned an invalid value.
SocketTimeoutErrorUND_ERR_SOCKET_TIMEOUTa socket exceeds the socketTimeout option.
RequestTimeoutErrorUND_ERR_REQUEST_TIMEOUTa request exceeds the requestTimeout option.
RequestAbortedErrorUND_ERR_ABORTEDthe request has been aborted by the user
ClientDestroyedErrorUND_ERR_DESTROYEDtrying to use a destroyed client.
ClientClosedErrorUND_ERR_CLOSEDtrying to use a closed client.
SocketErrorUND_ERR_SOCKETthere is an error with the socket.
NotSupportedErrorUND_ERR_NOT_SUPPORTEDencountered unsupported functionality.
ContentLengthMismatchErrorUND_ERR_CONTENT_LENGTH_MISMATCHbody does not match content-length header
InformationalErrorUND_ERR_INFOexpected error with reason

Specification Compliance

This section documents parts of the HTTP/1.1 specification which Undici does not support or does not fully implement.

Expect

Undici does not support the Expect request header field. The request body is always immediately sent and the 100 Continue response will be ignored.

Refs: https://tools.ietf.org/html/rfc7231#section-5.1.1

Pipelining

Uncidi will only use pipelining if configured with a pipelining factor greater than 1.

Undici always assumes that connections are persistent and will immediatly pipeline requests, without checking whether the connection is persistent. Hence, automatic fallback to HTTP/1.0 or HTTP/1.1 without pipelining is not supported.

Undici will immediately pipeline when retrying requests afters a failed connection. However, Undici will not retry the first remaining requests in the prior pipeline and instead error the corresponding callback/promise/stream.

Refs: https://tools.ietf.org/html/rfc2616#section-8.1.2.2
Refs: https://tools.ietf.org/html/rfc7230#section-6.3.2

Collaborators

License

MIT

FAQs

Last updated on 05 Sep 2020

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc