Comparing version 1.0.2 to 1.0.3
@@ -22,3 +22,3 @@ 'use strict' | ||
path: '/', | ||
port: 3000, | ||
port: 3009, | ||
agent: new http.Agent({ | ||
@@ -25,0 +25,0 @@ keepAlive: true, |
@@ -7,2 +7,2 @@ 'use strict' | ||
res.end('hello world') | ||
}).listen(3000) | ||
}).listen(3009) |
@@ -23,2 +23,3 @@ 'use strict' | ||
kQueue, | ||
kServerName, | ||
kSocketTimeout, | ||
@@ -102,2 +103,3 @@ kRequestTimeout, | ||
this[kDestroyed] = false | ||
this[kServerName] = null | ||
this[kTLSOpts] = tls | ||
@@ -134,3 +136,11 @@ this[kRetryDelay] = 0 | ||
get connected () { | ||
return this[kSocket] && !this[kSocket].connecting && !this[kSocket].destroyed | ||
return ( | ||
this[kSocket] && | ||
this[kSocket].connecting !== true && | ||
// Older versions of Node don't set secureConnecting to false. | ||
(this[kSocket].authorized !== false || | ||
this[kSocket].authorizationError | ||
) && | ||
!this[kSocket].destroyed | ||
) | ||
} | ||
@@ -433,4 +443,8 @@ | ||
const { protocol, port, hostname } = client[kUrl] | ||
const servername = client[kServerName] || (client[kTLSOpts] && client[kTLSOpts].servername) | ||
const socket = protocol === 'https:' | ||
? tls.connect(port || /* istanbul ignore next */ 443, hostname, client[kTLSOpts]) | ||
? tls.connect(port || /* istanbul ignore next */ 443, hostname, { | ||
...client[kTLSOpts], | ||
servername | ||
}) | ||
: net.connect(port || /* istanbul ignore next */ 80, hostname) | ||
@@ -448,3 +462,4 @@ | ||
socket | ||
.on('connect', function () { | ||
.setNoDelay(true) | ||
.on(protocol === 'https:' ? 'secureConnect' : 'connect', function () { | ||
const client = this[kClient] | ||
@@ -465,2 +480,22 @@ | ||
.on('error', function (err) { | ||
if (err.code === 'ERR_TLS_CERT_ALTNAME_INVALID') { | ||
assert(!client.running) | ||
while (client.pending && client[kQueue][client[kPendingIdx]].servername === servername) { | ||
client[kQueue][client[kPendingIdx]++].invoke(err, null) | ||
} | ||
} else if ( | ||
!client.running && | ||
err.code !== 'ECONNRESET' && | ||
err.code !== 'ECONNREFUSED' && | ||
err.code !== 'EHOSTUNREACH' && | ||
err.code !== 'EHOSTDOWN' && | ||
err.code !== 'UND_ERR_SOCKET' | ||
) { | ||
// Error is not caused by running request and not a recoverable | ||
// socket error. | ||
for (const request of client[kQueue].splice(client[kPendingIdx])) { | ||
request.invoke(err, null) | ||
} | ||
} | ||
this[kError] = err | ||
@@ -562,2 +597,14 @@ }) | ||
if (client[kServerName] !== request.servername) { | ||
if (client.running) { | ||
return | ||
} | ||
client[kServerName] = request.servername | ||
if (client[kSocket]) { | ||
client[kSocket].destroy() | ||
} | ||
} | ||
if (!client[kSocket] && !client[kRetryTimeout]) { | ||
@@ -670,2 +717,3 @@ connect(client) | ||
if (!socket.destroyed) { | ||
assert(client.running) | ||
socket.destroy(err) | ||
@@ -672,0 +720,0 @@ } |
@@ -129,2 +129,3 @@ const { | ||
const ret = new Duplex({ | ||
readableObjectMode: opts.objectMode, | ||
autoDestroy: true, | ||
@@ -131,0 +132,0 @@ read () { |
@@ -11,43 +11,4 @@ 'use strict' | ||
const assert = require('assert') | ||
const net = require('net') | ||
const methods = [ | ||
'ACL', | ||
'BIND', | ||
'CHECKOUT', | ||
'CONNECT', | ||
'COPY', | ||
'DELETE', | ||
'GET', | ||
'HEAD', | ||
'LINK', | ||
'LOCK', | ||
'M-SEARCH', | ||
'MERGE', | ||
'MKACTIVITY', | ||
'MKCALENDAR', | ||
'MKCOL', | ||
'MOVE', | ||
'NOTIFY', | ||
'OPTIONS', | ||
'PATCH', | ||
'POST', | ||
'PROPFIND', | ||
'PROPPATCH', | ||
'PURGE', | ||
'PUT', | ||
'REBIND', | ||
'REPORT', | ||
'SEARCH', | ||
'SOURCE', | ||
'SUBSCRIBE', | ||
'TRACE', | ||
'UNBIND', | ||
'UNLINK', | ||
'UNLOCK', | ||
'UNSUBSCRIBE' | ||
].reduce((acc, m) => { | ||
acc[m] = true | ||
return acc | ||
}, {}) | ||
function isValidBody (body) { | ||
@@ -68,2 +29,3 @@ return body == null || | ||
opaque, | ||
servername, | ||
signal, | ||
@@ -81,4 +43,4 @@ requestTimeout | ||
if (typeof method !== 'string' || !methods[method]) { | ||
throw new InvalidArgumentError('method must be a valid method') | ||
if (typeof method !== 'string') { | ||
throw new InvalidArgumentError('method must be a string') | ||
} | ||
@@ -110,2 +72,9 @@ | ||
const hostHeader = headers && (headers.host || headers.Host) | ||
this.servername = servername || hostHeader || hostname | ||
if (net.isIP(this.servername) || this.servername.startsWith('[')) { | ||
this.servername = null | ||
} | ||
this.chunked = !headers || headers['content-length'] === undefined | ||
@@ -141,3 +110,2 @@ | ||
const hostHeader = headers && (headers.host || headers.Host) | ||
if (!hostHeader) { | ||
@@ -205,6 +173,5 @@ header += `host: ${hostname}\r\n` | ||
if ( | ||
this.streaming && | ||
this.body && | ||
!this.body.destroyed && | ||
typeof this.body.destroy === 'function' | ||
typeof this.body.destroy === 'function' && | ||
!this.body.destroyed | ||
) { | ||
@@ -217,2 +184,3 @@ this.body.destroy(err) | ||
this.body = null | ||
this.servername = null | ||
this.callback = null | ||
@@ -219,0 +187,0 @@ this.opaque = null |
@@ -7,2 +7,3 @@ module.exports = { | ||
kRequestTimeout: Symbol('request timeout'), | ||
kServerName: Symbol('server name'), | ||
kTLSOpts: Symbol('TLS Options'), | ||
@@ -9,0 +10,0 @@ kClosed: Symbol('closed'), |
{ | ||
"name": "undici", | ||
"version": "1.0.2", | ||
"version": "1.0.3", | ||
"description": "An HTTP/1.1 client, written from scratch for Node.js", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -279,2 +279,3 @@ # undici | ||
* ... same as [`client.request(opts, callback)`][request]. | ||
* `objectMode`, `true` if the `handler` will return an object stream. | ||
* `opaque`, passed as `opaque` to `handler`. Used | ||
@@ -281,0 +282,0 @@ to avoid creating a closure. |
@@ -9,2 +9,3 @@ 'use strict' | ||
Readable, | ||
Transform, | ||
Writable, | ||
@@ -695,1 +696,29 @@ PassThrough | ||
}) | ||
test('pipeline objectMode', (t) => { | ||
t.plan(1) | ||
const server = createServer((req, res) => { | ||
res.end(JSON.stringify({ asd: 1 })) | ||
}) | ||
t.tearDown(server.close.bind(server)) | ||
server.listen(0, () => { | ||
const client = new Client(`http://localhost:${server.address().port}`) | ||
t.tearDown(client.close.bind(client)) | ||
client | ||
.pipeline({ path: '/', method: 'GET', objectMode: true }, ({ body }) => { | ||
return pipeline(body, new Transform({ | ||
readableObjectMode: true, | ||
transform (chunk, encoding, callback) { | ||
callback(null, JSON.parse(chunk)) | ||
} | ||
}), () => {}) | ||
}) | ||
.on('data', data => { | ||
t.strictDeepEqual(data, { asd: 1 }) | ||
}) | ||
.end() | ||
}) | ||
}) |
@@ -8,2 +8,3 @@ 'use strict' | ||
const { Readable } = require('stream') | ||
const { kSocket } = require('../lib/symbols') | ||
@@ -159,12 +160,13 @@ test('basic get', (t) => { | ||
client.request({ path: '/', method: 'POST', body: expected }, (err, { statusCode, headers, body }) => { | ||
client.request({ path: '/', method: 'POST', body: expected }, (err, data) => { | ||
t.error(err) | ||
t.strictEqual(statusCode, 200) | ||
t.strictEqual(data.statusCode, 200) | ||
const bufs = [] | ||
body.on('data', (buf) => { | ||
bufs.push(buf) | ||
}) | ||
body.on('end', () => { | ||
t.strictEqual('hello', Buffer.concat(bufs).toString('utf8')) | ||
}) | ||
data.body | ||
.on('data', (buf) => { | ||
bufs.push(buf) | ||
}) | ||
.on('end', () => { | ||
t.strictEqual('hello', Buffer.concat(bufs).toString('utf8')) | ||
}) | ||
}) | ||
@@ -673,1 +675,23 @@ }) | ||
}) | ||
test('non recoverable socket error fails pending request', (t) => { | ||
t.plan(2) | ||
const server = createServer((req, res) => { | ||
res.end() | ||
}) | ||
t.tearDown(server.close.bind(server)) | ||
server.listen(0, () => { | ||
const client = new Client(`http://localhost:${server.address().port}`) | ||
t.tearDown(client.close.bind(client)) | ||
client.request({ path: '/', method: 'GET' }, (err, data) => { | ||
t.strictEqual(err.message, 'kaboom') | ||
}) | ||
client.request({ path: '/', method: 'GET' }, (err, data) => { | ||
t.strictEqual(err.message, 'kaboom') | ||
}) | ||
client[kSocket].destroy(new Error('kaboom')) | ||
}) | ||
}) |
@@ -38,3 +38,3 @@ 'use strict' | ||
test('method', (t) => { | ||
t.plan(6) | ||
t.plan(3) | ||
@@ -46,11 +46,5 @@ const client = new Client(url) | ||
t.ok(err instanceof errors.InvalidArgumentError) | ||
t.strictEqual(err.message, 'method must be a valid method') | ||
t.strictEqual(err.message, 'method must be a string') | ||
t.strictEqual(res, null) | ||
}) | ||
client.request({ path: '/', method: 'WOOW' }, (err, res) => { | ||
t.ok(err instanceof errors.InvalidArgumentError) | ||
t.strictEqual(err.message, 'method must be a valid method') | ||
t.strictEqual(res, null) | ||
}) | ||
}) | ||
@@ -57,0 +51,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
199707
33
6349
523
27