| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent ed18b9c commit 5112315
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -98,6 +98,10 @@ changes: | |||
| 98 | 98 | ||
| 99 | 99 | See [`Session Resumption`][] for information about TLS session reuse. | |
| 100 | 100 | ||
| 101 | + Requests that specify a custom `checkServerIdentity` option are not eligible | ||
| 102 | + for connection reuse or TLS session reuse by an `https.Agent`, unless the | ||
| 103 | + `checkServerIdentity` option was specified when constructing the Agent. | ||
| 104 | + | ||
| 101 | 105 | #### Event: `'keylog'` | |
| 102 | 106 | ||
| 103 | 107 | <!-- YAML | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -35,6 +35,7 @@ const { | |||
| 35 | 35 | ObjectSetPrototypeOf, | |
| 36 | 36 | ReflectApply, | |
| 37 | 37 | ReflectConstruct, | |
| 38 | + Symbol, | ||
| 38 | 39 | } = primordials; | |
| 39 | 40 | ||
| 40 | 41 | const { | |
@@ -48,6 +49,8 @@ const { ERR_PROXY_TUNNEL } = require('internal/errors').codes; | |||
| 48 | 49 | assertCrypto(); | |
| 49 | 50 | ||
| 50 | 51 | const tls = require('tls'); | |
| 52 | + const kPerRequestCheckServerIdentity = Symbol('per-request checkServerIdentity'); | ||
| 53 | + let perRequestCheckServerIdentityIndex = 0; | ||
| 51 | 54 | const { | |
| 52 | 55 | kProxyConfig, | |
| 53 | 56 | checkShouldUseProxy, | |
@@ -277,6 +280,8 @@ function establishTunnel(agent, socket, options, tunnelConfig, afterSocket) { | |||
| 277 | 280 | tunneldSocket.removeListener('error', onTLSHandshakeError); | |
| 278 | 281 | afterSocket(null, tunneldSocket); | |
| 279 | 282 | }); | |
| 283 | + if (requestOptions[kPerRequestCheckServerIdentity]) | ||
| 284 | + tunneldSocket[kPerRequestCheckServerIdentity] = true; | ||
| 280 | 285 | tunneldSocket.on('free', () => { | |
| 281 | 286 | debug('Propagate free event from tunneled socket to tunnel socket'); | |
| 282 | 287 | socket.emit('free'); | |
@@ -345,7 +350,9 @@ function createConnection(...args) { | |||
| 345 | 350 | ||
| 346 | 351 | debug('createConnection', options); | |
| 347 | 352 | ||
| 348 | - if (options._agentKey) { | ||
| 353 | + const reuseSession = options._agentKey && | ||
| 354 | + !options[kPerRequestCheckServerIdentity]; | ||
| 355 | + if (reuseSession) { | ||
| 349 | 356 | const session = this._getSession(options._agentKey); | |
| 350 | 357 | if (session) { | |
| 351 | 358 | debug('reuse session for %j', options._agentKey); | |
@@ -412,7 +419,10 @@ function createConnection(...args) { | |||
| 412 | 419 | socket[kWaitForProxyTunnel] = true; | |
| 413 | 420 | } | |
| 414 | 421 | ||
| 415 | - if (options._agentKey) { | ||
| 422 | + if (options[kPerRequestCheckServerIdentity]) | ||
| 423 | + socket[kPerRequestCheckServerIdentity] = true; | ||
| 424 | + | ||
| 425 | + if (reuseSession) { | ||
| 416 | 426 | // Cache new session for reuse | |
| 417 | 427 | socket.on('session', (session) => { | |
| 418 | 428 | this._cacheSession(options._agentKey, session); | |
@@ -467,6 +477,12 @@ function Agent(options) { | |||
| 467 | 477 | ObjectSetPrototypeOf(Agent.prototype, HttpAgent.prototype); | |
| 468 | 478 | ObjectSetPrototypeOf(Agent, HttpAgent); | |
| 469 | 479 | Agent.prototype.createConnection = createConnection; | |
| 480 | + Agent.prototype.keepSocketAlive = function keepSocketAlive(socket) { | ||
| 481 | + if (socket[kPerRequestCheckServerIdentity]) | ||
| 482 | + return false; | ||
| 483 | + | ||
| 484 | + return FunctionPrototypeCall(HttpAgent.prototype.keepSocketAlive, this, socket); | ||
| 485 | + }; | ||
| 470 | 486 | ||
| 471 | 487 | function getPfxAgentKey(pfx, passphrase) { | |
| 472 | 488 | if (!ArrayIsArray(pfx)) | |
@@ -575,6 +591,9 @@ Agent.prototype.getName = function getName(options = kEmptyObject) { | |||
| 575 | 591 | if (options.privateKeyEngine) | |
| 576 | 592 | name += options.privateKeyEngine; | |
| 577 | 593 | ||
| 594 | + if (options[kPerRequestCheckServerIdentity]) | ||
| 595 | + name += `:${options[kPerRequestCheckServerIdentity]}`; | ||
| 596 | + | ||
| 578 | 597 | return name; | |
| 579 | 598 | }; | |
| 580 | 599 | ||
@@ -619,6 +638,20 @@ const globalAgent = new Agent({ | |||
| 619 | 638 | proxyEnv: getOptionValue('--use-env-proxy') ? filterEnvForProxies(process.env) : undefined, | |
| 620 | 639 | }); | |
| 621 | 640 | ||
| 641 | + function hasAgentCheckServerIdentity(options) { | ||
| 642 | + let { agent } = options; | ||
| 643 | + if (agent === false) | ||
| 644 | + return false; | ||
| 645 | + | ||
| 646 | + if (agent === null || agent === undefined) { | ||
| 647 | + if (typeof options.createConnection === 'function') | ||
| 648 | + return false; | ||
| 649 | + agent = module.exports.globalAgent; | ||
| 650 | + } | ||
| 651 | + | ||
| 652 | + return agent?.options?.checkServerIdentity !== undefined; | ||
| 653 | + } | ||
| 654 | + | ||
| 622 | 655 | /** | |
| 623 | 656 | * Makes a request to a secure web server. | |
| 624 | 657 | * @param {...any} args | |
@@ -638,6 +671,13 @@ function request(...args) { | |||
| 638 | 671 | ObjectAssign(options, ArrayPrototypeShift(args)); | |
| 639 | 672 | } | |
| 640 | 673 | ||
| 674 | + if (options.checkServerIdentity !== undefined && | ||
| 675 | + options.checkServerIdentity !== tls.checkServerIdentity && | ||
| 676 | + !hasAgentCheckServerIdentity(options)) { | ||
| 677 | + options[kPerRequestCheckServerIdentity] = | ||
| 678 | + ++perRequestCheckServerIdentityIndex; | ||
| 679 | + } | ||
| 680 | + | ||
| 641 | 681 | options._defaultAgent = module.exports.globalAgent; | |
| 642 | 682 | ArrayPrototypeUnshift(args, options); | |
| 643 | 683 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,113 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + const common = require('../common'); | ||
| 3 | + if (!common.hasCrypto) | ||
| 4 | + common.skip('missing crypto'); | ||
| 5 | + | ||
| 6 | + const assert = require('assert'); | ||
| 7 | + const fixtures = require('../common/fixtures'); | ||
| 8 | + const https = require('https'); | ||
| 9 | + const { once } = require('events'); | ||
| 10 | + | ||
| 11 | + const key = fixtures.readKey('agent1-key.pem'); | ||
| 12 | + const cert = fixtures.readKey('agent1-cert.pem'); | ||
| 13 | + const ca = fixtures.readKey('ca1-cert.pem'); | ||
| 14 | + const expectedError = /rejected by callback/; | ||
| 15 | + | ||
| 16 | + function request(options) { | ||
| 17 | + return new Promise((resolve, reject) => { | ||
| 18 | + const req = https.get({ | ||
| 19 | + host: '127.0.0.1', | ||
| 20 | + servername: 'agent1', | ||
| 21 | + ca: [ca], | ||
| 22 | + ...options, | ||
| 23 | + }, (res) => { | ||
| 24 | + const socket = res.socket; | ||
| 25 | + res.resume(); | ||
| 26 | + res.on('end', () => resolve({ | ||
| 27 | + socket, | ||
| 28 | + reusedSocket: req.reusedSocket, | ||
| 29 | + })); | ||
| 30 | + }); | ||
| 31 | + | ||
| 32 | + req.on('error', reject); | ||
| 33 | + }); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + const server = https.createServer({ | ||
| 37 | + key, | ||
| 38 | + cert, | ||
| 39 | + minVersion: 'TLSv1.2', | ||
| 40 | + maxVersion: 'TLSv1.2', | ||
| 41 | + }, (req, res) => { | ||
| 42 | + res.end('ok'); | ||
| 43 | + }); | ||
| 44 | + | ||
| 45 | + (async function() { | ||
| 46 | + server.listen(0); | ||
| 47 | + await once(server, 'listening'); | ||
| 48 | + | ||
| 49 | + const port = server.address().port; | ||
| 50 | + let acceptCalls = 0; | ||
| 51 | + let rejectCalls = 0; | ||
| 52 | + const acceptingCheck = () => { | ||
| 53 | + acceptCalls++; | ||
| 54 | + }; | ||
| 55 | + const rejectingCheck = () => { | ||
| 56 | + rejectCalls++; | ||
| 57 | + return new Error('rejected by callback'); | ||
| 58 | + }; | ||
| 59 | + | ||
| 60 | + const sessionAgent = new https.Agent(); | ||
| 61 | + const keepAliveAgent = new https.Agent({ | ||
| 62 | + keepAlive: true, | ||
| 63 | + maxCachedSessions: 0, | ||
| 64 | + }); | ||
| 65 | + const agentLevelAgent = new https.Agent({ | ||
| 66 | + checkServerIdentity: acceptingCheck, | ||
| 67 | + }); | ||
| 68 | + | ||
| 69 | + try { | ||
| 70 | + await request({ | ||
| 71 | + port, | ||
| 72 | + agent: sessionAgent, | ||
| 73 | + checkServerIdentity: acceptingCheck, | ||
| 74 | + }); | ||
| 75 | + assert.deepStrictEqual(sessionAgent._sessionCache.map, {}); | ||
| 76 | + await assert.rejects(request({ | ||
| 77 | + port, | ||
| 78 | + agent: sessionAgent, | ||
| 79 | + checkServerIdentity: rejectingCheck, | ||
| 80 | + }), expectedError); | ||
| 81 | + | ||
| 82 | + await request({ | ||
| 83 | + port, | ||
| 84 | + agent: keepAliveAgent, | ||
| 85 | + checkServerIdentity: acceptingCheck, | ||
| 86 | + }); | ||
| 87 | + await assert.rejects(request({ | ||
| 88 | + port, | ||
| 89 | + agent: keepAliveAgent, | ||
| 90 | + checkServerIdentity: rejectingCheck, | ||
| 91 | + }), expectedError); | ||
| 92 | + | ||
| 93 | + const first = await request({ | ||
| 94 | + port, | ||
| 95 | + agent: agentLevelAgent, | ||
| 96 | + }); | ||
| 97 | + assert.strictEqual(first.socket.isSessionReused(), false); | ||
| 98 | + const second = await request({ | ||
| 99 | + port, | ||
| 100 | + agent: agentLevelAgent, | ||
| 101 | + }); | ||
| 102 | + assert.strictEqual(second.socket.isSessionReused(), true); | ||
| 103 | + | ||
| 104 | + assert.strictEqual(acceptCalls, 3); | ||
| 105 | + assert.strictEqual(rejectCalls, 2); | ||
| 106 | + } finally { | ||
| 107 | + sessionAgent.destroy(); | ||
| 108 | + keepAliveAgent.destroy(); | ||
| 109 | + agentLevelAgent.destroy(); | ||
| 110 | + server.close(); | ||
| 111 | + await once(server, 'close'); | ||
| 112 | + } | ||
| 113 | + })().then(common.mustCall()); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments