Skip to content

Request HTTP(s) URLs in a complex world.

License

Notifications You must be signed in to change notification settings

node-modules/urllib

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

urllib

NPM version Node.js CI Test coverage Known Vulnerabilities npm download

Request HTTP URLs in a complex world β€” basic and digest authentication, redirections, timeout and more.

Install

npm install urllib

Usage

TypeScript and ESM

import { request } from 'urllib';

const { data, res } = await request('http://cnodejs.org/');
// result: { data: Buffer, res: Response }
console.log('status: %s, body size: %d, headers: %j', res.status, data.length, res.headers);

CommonJS

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

const { data, res } = await request('http://cnodejs.org/');
// result: { data: Buffer, res: Response }
console.log('status: %s, body size: %d, headers: %j', res.status, data.length, res.headers);

API Doc

Method: async request(url[, options])

Arguments

  • url String | Object - The URL to request, either a String or a Object that return by url.parse.
  • options Object - Optional
    • method String - Request method, defaults to GET. Could be GET, POST, DELETE or PUT. Alias 'type'.
    • data Object - Data to be sent. Will be stringify automatically.
    • content String | Buffer - Manually set the content of payload. If set, data will be ignored.
    • stream stream.Readable - Stream to be pipe to the remote. If set, data and content will be ignored.
    • writeStream stream.Writable - A writable stream to be piped by the response stream. Responding data will be write to this stream and callback will be called with data set null after finished writing.
    • files {Array<ReadStream|Buffer|String> | Object | ReadStream | Buffer | String - The files will send with multipart/form-data format, base on formstream. If method not set, will use POST method by default.
    • contentType String - Type of request data. Could be json (Notes: not use application/json here). If it's json, will auto set Content-Type: application/json header.
    • dataType String - Type of response data. Could be text or json. If it's text, the callbacked data would be a String. If it's json, the data of callback would be a parsed JSON Object and will auto set Accept: application/json header. Default callbacked data would be a Buffer.
    • fixJSONCtlChars Boolean - Fix the control characters (U+0000 through U+001F) before JSON parse response. Default is false.
    • headers Object - Request headers.
    • timeout Number | Array - Request timeout in milliseconds for connecting phase and response receiving phase. Default is 5000. You can use timeout: 5000 to tell urllib use same timeout on two phase or set them seperately such as timeout: [3000, 5000], which will set connecting timeout to 3s and response 5s.
    • keepAliveTimeout number | null - Default is 4000, 4 seconds - The timeout after which a socket without active requests will time out. Monitors time between activity on a connected socket. This value may be overridden by keep-alive hints from the server. See MDN: HTTP - Headers - Keep-Alive directives for more details.
    • auth String - username:password used in HTTP Basic Authorization.
    • digestAuth String - username:password used in HTTP Digest Authorization.
    • followRedirect Boolean - follow HTTP 3xx responses as redirects. defaults to true.
    • maxRedirects Number - The maximum number of redirects to follow, defaults to 10.
    • formatRedirectUrl Function - Format the redirect url by your self. Default is url.resolve(from, to).
    • beforeRequest Function - Before request hook, you can change every thing here.
    • streaming Boolean - let you get the res object when request connected, default false. alias customResponse
    • compressed Boolean - Accept gzip, br response content and auto decode it, default is false.
    • timing Boolean - Enable timing or not, default is true.
    • socketPath String | null - request a unix socket service, default is null.
    • highWaterMark Number - default is 67108864, 64 KiB.

Options: options.data

When making a request:

await request('https://example.com', {
  method: 'GET',
  data: {
    'a': 'hello',
    'b': 'world',
  },
});

For GET request, data will be stringify to query string, e.g. http://example.com/?a=hello&b=world.

For others like POST, PATCH or PUT request, in defaults, the data will be stringify into application/x-www-form-urlencoded format if content-type header is not set.

If content-type is application/json, the data will be JSON.stringify to JSON data format.

Options: options.content

options.content is useful when you wish to construct the request body by yourself, for example making a content-type: application/json request.

Notes that if you want to send a JSON body, you should stringify it yourself:

await request('https://example.com', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  content: JSON.stringify({
    a: 'hello',
    b: 'world',
  }),
});

It would make a HTTP request like:

POST / HTTP/1.1
host: example.com
content-type: application/json

{
  "a": "hello",
  "b": "world"
}

This exmaple can use options.data with application/json content type:

await request('https://example.com', {
  method: 'POST',
  headers: {
    'content-type': 'application/json'
  },
  data: {
    a: 'hello',
    b: 'world',
  }
});

Options: options.files

Upload a file with a hello field.

await request('https://example.com/upload', {
  method: 'POST',
  files: __filename,
  data: {
    hello: 'hello urllib',
  },
});

Upload multi files with a hello field.

await request('https://example.com/upload', {
  method: 'POST',
  files: [
    __filename,
    fs.createReadStream(__filename),
    Buffer.from('mock file content'),
  ],
  data: {
    hello: 'hello urllib with multi files',
  },
});

Custom file field name with uploadfile.

await request('https://example.com/upload', {
  method: 'POST',
  files: {
    uploadfile: __filename,
  },
});

Response Object

Response is normal object, it contains:

  • status or statusCode: response status code.
    • -1 meaning some network error like ENOTFOUND
    • -2 meaning ConnectionTimeoutError
  • headers: response http headers, default is {}
  • size: response size
  • aborted: response was aborted or not
  • rt: total request and response time in ms.
  • timing: timing object if timing enable.
  • socket: socket info

Run test with debug log

NODE_DEBUG=urllib:* npm test

Request with HTTP2

Create a HttpClient with options.allowH2 = true

import { HttpClient } from 'urllib';

const httpClient = new HttpClient({
  allowH2: true,
});

const response = await httpClient.request('https://node.js.org');
console.log(response.status);
console.log(response.headers);

Mocking Request

export from undici

import { strict as assert } from 'assert';
import { MockAgent, setGlobalDispatcher, request } from 'urllib';

const mockAgent = new MockAgent();
setGlobalDispatcher(mockAgent);

const mockPool = mockAgent.get('http://localhost:7001');

mockPool.intercept({
  path: '/foo',
  method: 'POST',
}).reply(400, {
  message: 'mock 400 bad request',
});

const response = await request('http://localhost:7001/foo', {
  method: 'POST',
  dataType: 'json',
});
assert.equal(response.status, 400);
assert.deepEqual(response.data, { message: 'mock 400 bad request' });

Request through a http proxy

export from undici

import { ProxyAgent, request } from 'urllib';

const proxyAgent = new ProxyAgent('http://my.proxy.com:8080');
const response = await request('https://www.npmjs.com/package/urllib', {
  dispatcher: proxyAgent,
});
console.log(response.status, response.headers);

Benchmarks

Fork undici benchmarks script

undici@6.19.2

Node.js v18.20.3

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ (index) β”‚         Tests         β”‚ Samples β”‚       Result       β”‚  Tolerance  β”‚ Difference with slowest β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    0    β”‚  'urllib2 - request'  β”‚   10    β”‚  '321.53 req/sec'  β”‚ 'Β± 0.38 %'  β”‚           '-'           β”‚
β”‚    1    β”‚ 'http - no keepalive' β”‚   10    β”‚  '607.77 req/sec'  β”‚ 'Β± 0.80 %'  β”‚       '+ 89.02 %'       β”‚
β”‚    2    β”‚         'got'         β”‚   101   β”‚ '7929.51 req/sec'  β”‚ 'Β± 4.46 %'  β”‚      '+ 2366.15 %'      β”‚
β”‚    3    β”‚     'node-fetch'      β”‚   40    β”‚ '8651.95 req/sec'  β”‚ 'Β± 2.99 %'  β”‚      '+ 2590.84 %'      β”‚
β”‚    4    β”‚       'request'       β”‚   101   β”‚ '8864.09 req/sec'  β”‚ 'Β± 7.81 %'  β”‚      '+ 2656.82 %'      β”‚
β”‚    5    β”‚   'undici - fetch'    β”‚   101   β”‚ '9607.01 req/sec'  β”‚ 'Β± 4.23 %'  β”‚      '+ 2887.87 %'      β”‚
β”‚    6    β”‚        'axios'        β”‚   55    β”‚ '10378.80 req/sec' β”‚ 'Β± 2.94 %'  β”‚      '+ 3127.91 %'      β”‚
β”‚    7    β”‚     'superagent'      β”‚   75    β”‚ '11286.74 req/sec' β”‚ 'Β± 2.90 %'  β”‚      '+ 3410.29 %'      β”‚
β”‚    8    β”‚  'http - keepalive'   β”‚   60    β”‚ '11288.96 req/sec' β”‚ 'Β± 2.95 %'  β”‚      '+ 3410.98 %'      β”‚
β”‚    9    β”‚  'urllib4 - request'  β”‚   101   β”‚ '11352.65 req/sec' β”‚ 'Β± 10.20 %' β”‚      '+ 3430.79 %'      β”‚
β”‚   10    β”‚  'urllib3 - request'  β”‚   40    β”‚ '13831.19 req/sec' β”‚ 'Β± 2.89 %'  β”‚      '+ 4201.64 %'      β”‚
β”‚   11    β”‚  'undici - pipeline'  β”‚   60    β”‚ '14562.44 req/sec' β”‚ 'Β± 2.91 %'  β”‚      '+ 4429.06 %'      β”‚
β”‚   12    β”‚  'undici - request'   β”‚   70    β”‚ '19630.64 req/sec' β”‚ 'Β± 2.87 %'  β”‚      '+ 6005.32 %'      β”‚
β”‚   13    β”‚   'undici - stream'   β”‚   55    β”‚ '20843.50 req/sec' β”‚ 'Β± 2.90 %'  β”‚      '+ 6382.54 %'      β”‚
β”‚   14    β”‚  'undici - dispatch'  β”‚   55    β”‚ '21233.10 req/sec' β”‚ 'Β± 2.82 %'  β”‚      '+ 6503.70 %'      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Node.js v20.15.0

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ (index) β”‚ Tests                 β”‚ Samples β”‚ Result             β”‚ Tolerance  β”‚ Difference with slowest β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 0       β”‚ 'urllib2 - request'   β”‚ 10      β”‚ '332.91 req/sec'   β”‚ 'Β± 1.13 %' β”‚ '-'                     β”‚
β”‚ 1       β”‚ 'http - no keepalive' β”‚ 10      β”‚ '615.50 req/sec'   β”‚ 'Β± 2.25 %' β”‚ '+ 84.88 %'             β”‚
β”‚ 2       β”‚ 'got'                 β”‚ 55      β”‚ '7658.39 req/sec'  β”‚ 'Β± 2.98 %' β”‚ '+ 2200.42 %'           β”‚
β”‚ 3       β”‚ 'node-fetch'          β”‚ 30      β”‚ '7832.96 req/sec'  β”‚ 'Β± 2.96 %' β”‚ '+ 2252.86 %'           β”‚
β”‚ 4       β”‚ 'axios'               β”‚ 40      β”‚ '8607.27 req/sec'  β”‚ 'Β± 2.79 %' β”‚ '+ 2485.44 %'           β”‚
β”‚ 5       β”‚ 'request'             β”‚ 35      β”‚ '8703.49 req/sec'  β”‚ 'Β± 2.84 %' β”‚ '+ 2514.35 %'           β”‚
β”‚ 6       β”‚ 'undici - fetch'      β”‚ 65      β”‚ '9971.24 req/sec'  β”‚ 'Β± 2.96 %' β”‚ '+ 2895.15 %'           β”‚
β”‚ 7       β”‚ 'superagent'          β”‚ 30      β”‚ '11006.46 req/sec' β”‚ 'Β± 2.90 %' β”‚ '+ 3206.11 %'           β”‚
β”‚ 8       β”‚ 'http - keepalive'    β”‚ 55      β”‚ '11610.14 req/sec' β”‚ 'Β± 2.87 %' β”‚ '+ 3387.44 %'           β”‚
β”‚ 9       β”‚ 'urllib3 - request'   β”‚ 25      β”‚ '13873.38 req/sec' β”‚ 'Β± 2.96 %' β”‚ '+ 4067.27 %'           β”‚
β”‚ 10      β”‚ 'urllib4 - request'   β”‚ 25      β”‚ '14291.36 req/sec' β”‚ 'Β± 2.92 %' β”‚ '+ 4192.82 %'           β”‚
β”‚ 11      β”‚ 'undici - pipeline'   β”‚ 45      β”‚ '14617.69 req/sec' β”‚ 'Β± 2.84 %' β”‚ '+ 4290.85 %'           β”‚
β”‚ 12      β”‚ 'undici - dispatch'   β”‚ 101     β”‚ '18716.29 req/sec' β”‚ 'Β± 3.97 %' β”‚ '+ 5521.98 %'           β”‚
β”‚ 13      β”‚ 'undici - request'    β”‚ 101     β”‚ '19165.16 req/sec' β”‚ 'Β± 3.25 %' β”‚ '+ 5656.81 %'           β”‚
β”‚ 14      β”‚ 'undici - stream'     β”‚ 30      β”‚ '21816.28 req/sec' β”‚ 'Β± 2.99 %' β”‚ '+ 6453.15 %'           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Node.js v22.3.0

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ (index) β”‚ Tests                 β”‚ Samples β”‚ Result             β”‚ Tolerance  β”‚ Difference with slowest β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 0       β”‚ 'urllib2 - request'   β”‚ 15      β”‚ '297.46 req/sec'   β”‚ 'Β± 2.65 %' β”‚ '-'                     β”‚
β”‚ 1       β”‚ 'http - no keepalive' β”‚ 10      β”‚ '598.25 req/sec'   β”‚ 'Β± 1.94 %' β”‚ '+ 101.12 %'            β”‚
β”‚ 2       β”‚ 'axios'               β”‚ 30      β”‚ '8487.94 req/sec'  β”‚ 'Β± 2.91 %' β”‚ '+ 2753.52 %'           β”‚
β”‚ 3       β”‚ 'got'                 β”‚ 50      β”‚ '10054.46 req/sec' β”‚ 'Β± 2.89 %' β”‚ '+ 3280.16 %'           β”‚
β”‚ 4       β”‚ 'request'             β”‚ 45      β”‚ '10306.02 req/sec' β”‚ 'Β± 2.87 %' β”‚ '+ 3364.73 %'           β”‚
β”‚ 5       β”‚ 'node-fetch'          β”‚ 55      β”‚ '11160.02 req/sec' β”‚ 'Β± 2.87 %' β”‚ '+ 3651.83 %'           β”‚
β”‚ 6       β”‚ 'superagent'          β”‚ 80      β”‚ '11302.28 req/sec' β”‚ 'Β± 2.85 %' β”‚ '+ 3699.66 %'           β”‚
β”‚ 7       β”‚ 'undici - fetch'      β”‚ 60      β”‚ '11357.87 req/sec' β”‚ 'Β± 2.89 %' β”‚ '+ 3718.35 %'           β”‚
β”‚ 8       β”‚ 'http - keepalive'    β”‚ 60      β”‚ '13782.10 req/sec' β”‚ 'Β± 2.97 %' β”‚ '+ 4533.34 %'           β”‚
β”‚ 9       β”‚ 'urllib4 - request'   β”‚ 70      β”‚ '15965.62 req/sec' β”‚ 'Β± 2.88 %' β”‚ '+ 5267.40 %'           β”‚
β”‚ 10      β”‚ 'urllib3 - request'   β”‚ 55      β”‚ '16010.37 req/sec' β”‚ 'Β± 2.90 %' β”‚ '+ 5282.45 %'           β”‚
β”‚ 11      β”‚ 'undici - pipeline'   β”‚ 35      β”‚ '17969.37 req/sec' β”‚ 'Β± 2.95 %' β”‚ '+ 5941.03 %'           β”‚
β”‚ 12      β”‚ 'undici - dispatch'   β”‚ 101     β”‚ '18765.50 req/sec' β”‚ 'Β± 3.01 %' β”‚ '+ 6208.68 %'           β”‚
β”‚ 13      β”‚ 'undici - request'    β”‚ 85      β”‚ '20091.12 req/sec' β”‚ 'Β± 2.95 %' β”‚ '+ 6654.33 %'           β”‚
β”‚ 14      β”‚ 'undici - stream'     β”‚ 45      β”‚ '21599.12 req/sec' β”‚ 'Β± 2.81 %' β”‚ '+ 7161.30 %'           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

License

MIT

Contributors

Contributors

Made with contributors-img.