Skip to content

Commit

Permalink
feat: connections and streams now include a metadata getter for the c…
Browse files Browse the repository at this point in the history
…onnection info

* This is to match the stream interface for Polykey's agnostic RPC streams.
* Also including a cancel method to match that interface

* Fixes #16

[ci skip]
  • Loading branch information
tegefaulkes committed May 10, 2023
1 parent eddd860 commit e325865
Show file tree
Hide file tree
Showing 8 changed files with 323 additions and 54 deletions.
16 changes: 16 additions & 0 deletions src/QUICConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { QUICConfig } from './config';
import type { Host, Port, RemoteInfo, StreamId } from './types';
import type { Connection, ConnectionErrorCode, SendInfo } from './native/types';
import type { StreamCodeToReason, StreamReasonToCode } from './types';
import type { ConnectionMetadata } from './types';
import {
CreateDestroy,
ready,
Expand Down Expand Up @@ -299,6 +300,21 @@ class QUICConnection extends EventTarget {
return this.socket.port;
}

public get remoteInfo(): ConnectionMetadata {
const derCerts = this.conn.peerCertChain();
const remoteCertificates =
derCerts != null
? derCerts.map((der) => utils.certificateDERToPEM(der))
: null;
return {
remoteCertificates,
localHost: this.localHost,
localPort: this.localPort,
remoteHost: this.remoteHost,
remotePort: this.remotePort,
};
}

/**
* This provides the ability to destroy with a specific error. This will wait for the connection to fully drain.
*/
Expand Down
4 changes: 1 addition & 3 deletions src/QUICSocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,7 @@ class QUICSocket extends EventTarget {
// Each send/recv/timeout may result in a destruction
if (!conn[destroyed]) {
// Ignore any errors, concurrent with destruction
await conn.send().catch((e) => {
this.logger.error(`not destroyed send ${e.message}`);
});
await conn.send().catch(() => {});
}
};

Expand Down
31 changes: 31 additions & 0 deletions src/QUICStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
StreamId,
StreamReasonToCode,
StreamCodeToReason,
ConnectionMetadata,
} from './types';
import type { Connection } from './native/types';
import { ReadableStream, WritableStream } from 'stream/web';
Expand Down Expand Up @@ -203,6 +204,21 @@ class QUICStream
return this._recvPaused;
}

/**
* Connection information including hosts, ports and cert data.
*/
public get remoteInfo(): ConnectionMetadata {
return this.connection.remoteInfo;
}

/**
* Duplicating `remoteInfo` functionality.
* This strictly exists to work with agnostic RPC stream interface.
*/
public get meta(): ConnectionMetadata {
return this.connection.remoteInfo;
}

/**
* This method can be arrived top-down or bottom-up:
*
Expand Down Expand Up @@ -236,6 +252,21 @@ class QUICStream
this.logger.info(`Destroyed ${this.constructor.name}`);
}

/**
* Used to cancel the streams. This function is synchronous but triggers some asynchronous events.
*/
public cancel(reason?: any): void {
reason = reason ?? new errors.ErrorQUICStreamCancel();
if (!this._recvClosed) {
this.readableController.error(reason);
void this.closeRecv(true, reason);
}
if (!this._sendClosed) {
this.writableController.error(reason);
void this.closeSend(true, reason);
}
}

/**
* External push is converted to internal pull
* Internal system decides when to unblock
Expand Down
5 changes: 5 additions & 0 deletions src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ class ErrorQUICStreamClose<T> extends ErrorQUICStream<T> {
static description = 'QUIC Stream force close';
}

class ErrorQUICStreamCancel<T> extends ErrorQUICStream<T> {
static description = 'QUIC Stream was cancelled without a provided reason';
}

class ErrorQUICStreamUnexpectedClose<T> extends ErrorQUICStream<T> {
static description = 'QUIC Stream closed early with no reason given';
}
Expand Down Expand Up @@ -122,6 +126,7 @@ export {
ErrorQUICStreamDestroyed,
ErrorQUICStreamLocked,
ErrorQUICStreamClose,
ErrorQUICStreamCancel,
ErrorQUICStreamUnexpectedClose,
ErrorQUICUndefinedBehaviour,
};
9 changes: 9 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ type StreamCodeToReason = (
code: number,
) => any | PromiseLike<any>;

type ConnectionMetadata = {
remoteCertificates: Array<string> | null;
localHost: Host;
localPort: Port;
remoteHost: Host;
remotePort: Port;
};

export type {
Opaque,
Callback,
Expand All @@ -104,4 +112,5 @@ export type {
RemoteInfo,
StreamReasonToCode,
StreamCodeToReason,
ConnectionMetadata,
};
20 changes: 20 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,24 @@ function never(): never {
throw new errors.ErrorQUICUndefinedBehaviour();
}

function certificateDERToPEM(der: Uint8Array): string {
const data = Buffer.from(der);
const contents =
data
.toString('base64')
.replace(/(.{64})/g, '$1\n')
.trimEnd() + '\n';
return `-----BEGIN CERTIFICATE-----\n${contents}-----END CERTIFICATE-----\n`;
}

function certificatePEMsToCertChainPem(pems: Array<string>): string {
let certChainPEM = '';
for (const pem of pems) {
certChainPEM += pem;
}
return certChainPEM;
}

export {
isIPv4,
isIPv6,
Expand All @@ -268,4 +286,6 @@ export {
encodeConnectionId,
decodeConnectionId,
never,
certificateDERToPEM,
certificatePEMsToCertChainPem,
};
Loading

0 comments on commit e325865

Please sign in to comment.