Skip to content

Commit

Permalink
src: add alpn & sni properties on SocketOptions and SocketInfo
Browse files Browse the repository at this point in the history
  • Loading branch information
flakey5 committed Dec 31, 2023
1 parent b2ac4ba commit ac17d98
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 3 deletions.
19 changes: 17 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,19 +73,34 @@ export class Socket {
allowHalfOpen: this.allowHalfOpen,
};
if (this.secureTransport === 'on') {
this.socket = tls.connect(connectOptions);
this.socket = tls.connect({
...connectOptions,
ALPNProtocols: options?.alpn,
servername: options?.sni,
});
} else {
this.socket = net.connect(connectOptions);
}
} else {
this.socket = new tls.TLSSocket(addressOrSocket);
this.socket = new tls.TLSSocket(addressOrSocket, {
ALPNProtocols: options?.alpn,
});
}

if (this.socket instanceof tls.TLSSocket) {
this.socket.on('secureConnect', () => {
// Typescript doesn't deduce that it can only be TLSSocket in this scope
const tlsSocket = this.socket as tls.TLSSocket;

let alpnProtocol: string | undefined;
if (typeof tlsSocket.alpnProtocol === 'string') {
alpnProtocol = tlsSocket.alpnProtocol;
}

this.openedResolve({
remoteAddress: this.socket.remoteAddress,
localAddress: this.socket.localAddress,
alpn: alpnProtocol,
});
});
} else {
Expand Down
14 changes: 14 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,19 @@ export interface SocketOptions {
* This option is similar to that offered by the Node.js net module and allows interoperability with code which utilizes it.
*/
allowHalfOpen?: boolean;
/**
* The Application-Layer Protocol Negotiation list to send, as an array of strings.
* If the server agrees with one of the protocols specified in this list, it will
* return the matching protocol in the info property. May be specified if and only
* if secureTransport is on or starttls.
*/
alpn?: string[];
/**
* The Server Name Indication TLS option to send as part of the TLS handshake.
* If specified, requests that the server send a certificate with a matching
* common name. May be specified if and only if secureTransport is on or starttls.
*/
sni?: string;
}

export interface SocketAddress {
Expand All @@ -25,4 +38,5 @@ export interface SocketAddress {
export interface SocketInfo {
remoteAddress?: string;
localAddress?: string;
alpn?: string;
}
27 changes: 26 additions & 1 deletion test/tls.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import tap from 'tap';
import { SocketError, connect } from '../src';
import { listenAndGetSocketAddress, writeAndReadSocket } from './utils';

function getTLSServer(): tls.Server {
function getTLSServer(alpnProtocols?: string[]): tls.Server {
const server = tls.createServer({
key: fs.readFileSync(path.join(__dirname, '/certs/server/server.key')),
cert: fs.readFileSync(path.join(__dirname, '/certs/server/server.crt')),
ca: fs.readFileSync(path.join(__dirname, '/certs/ca/ca.crt')),
rejectUnauthorized: false,
ALPNProtocols: alpnProtocols
});

return server;
Expand Down Expand Up @@ -59,6 +60,30 @@ void tap.test('Socket `connect` with TLS', (t) => {
t.end();
});

void t.test('can get correct alpn protocol', async (t) => {
const server = getTLSServer(['h2c']);
const address = await listenAndGetSocketAddress(server);
const socket = connect(
address,
{
secureTransport: 'on',
alpn: ['h2c']
}
);
t.throws(
() => {
socket.startTls();
},
new SocketError("secureTransport must be set to 'starttls'"),
'calling .startTls() throws an error',
);
const socketInfo = await socket.opened;
t.equal(socketInfo.alpn, 'h2c');
await socket.close();
server.close();
t.end();
});

t.end();
});

Expand Down

0 comments on commit ac17d98

Please sign in to comment.