Skip to content

Commit

Permalink
fix(MockHttpSocket): forward tls socket properties (#556)
Browse files Browse the repository at this point in the history
  • Loading branch information
kettanaito authored Apr 17, 2024
1 parent 085d1ec commit 430c65e
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 1 deletion.
37 changes: 36 additions & 1 deletion src/interceptors/ClientRequest/MockHttpSocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,28 @@ export class MockHttpSocket extends MockSocket {
}
}

// Forward TLS Socket properties onto this Socket instance
// in the case of a TLS/SSL connection.
if (Reflect.get(socket, 'encrypted')) {
const tlsProperties = [
'encrypted',
'authorized',
'getProtocol',
'getSession',
'isSessionReused',
]

tlsProperties.forEach((propertyName) => {
Object.defineProperty(this, propertyName, {
enumerable: true,
get: () => {
const value = Reflect.get(socket, propertyName)
return typeof value === 'function' ? value.bind(socket) : value
},
})
})
}

socket
.on('lookup', (...args) => this.emit('lookup', ...args))
.on('connect', () => {
Expand Down Expand Up @@ -331,9 +353,22 @@ export class MockHttpSocket extends MockSocket {
if (this.baseUrl.protocol === 'https:') {
this.emit('secure')
this.emit('secureConnect')

// A single TLS connection is represented by two "session" events.
this.emit('session', Buffer.from('mock-session-renegotiate'))
this.emit(
'session',
this.connectionOptions.session ||
Buffer.from('mock-session-renegotiate')
)
this.emit('session', Buffer.from('mock-session-resume'))

Reflect.set(this, 'encrypted', true)
// The server certificate is not the same as a CA
// passed to the TLS socket connection options.
Reflect.set(this, 'authorized', false)
Reflect.set(this, 'getProtocol', () => 'TLSv1.3')
Reflect.set(this, 'getSession', () => undefined)
Reflect.set(this, 'isSessionReused', () => false)
}
}

Expand Down
66 changes: 66 additions & 0 deletions test/modules/http/compliance/http-ssl-socket.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* @vitest-environment node
*/
import { it, expect, beforeAll, afterEach, afterAll } from 'vitest'
import https from 'node:https'
import type { TLSSocket } from 'node:tls'
import { DeferredPromise } from '@open-draft/deferred-promise'
import { ClientRequestInterceptor } from '../../../../src/interceptors/ClientRequest'

const interceptor = new ClientRequestInterceptor()

beforeAll(() => {
interceptor.apply()
})

afterEach(() => {
interceptor.removeAllListeners()
})

afterAll(() => {
interceptor.dispose()
})

it('emits a correct TLS Socket instance for a handled HTTPS request', async () => {
interceptor.on('request', ({ request }) => {
request.respondWith(new Response('hello world'))
})

const request = https.get('https://example.com')
const socketPromise = new DeferredPromise<TLSSocket>()
request.on('socket', (socket) => {
socket.on('connect', () => socketPromise.resolve(socket as TLSSocket))
})

const socket = await socketPromise

// Must be a TLS socket.
expect(socket.encrypted).toBe(true)
// The server certificate wasn't signed by one of the CA
// specified in the Socket constructor.
expect(socket.authorized).toBe(false)

expect(socket.getSession()).toBeUndefined()
expect(socket.getProtocol()).toBe('TLSv1.3')
expect(socket.isSessionReused()).toBe(false)
})

it('emits a correct TLS Socket instance for a bypassed HTTPS request', async () => {
const request = https.get('https://example.com')
const socketPromise = new DeferredPromise<TLSSocket>()
request.on('socket', (socket) => {
socket.on('connect', () => socketPromise.resolve(socket as TLSSocket))
})

const socket = await socketPromise

// Must be a TLS socket.
expect(socket.encrypted).toBe(true)
// The server certificate wasn't signed by one of the CA
// specified in the Socket constructor.
expect(socket.authorized).toBe(false)

expect(socket.getSession()).toBeUndefined()
expect(socket.getProtocol()).toBe('TLSv1.3')
expect(socket.isSessionReused()).toBe(false)
})

0 comments on commit 430c65e

Please sign in to comment.