grpc-js: ServerInterceptingCall: add getConnectionInfo method

This commit is contained in:
Michael Lumish 2025-03-12 17:38:21 -07:00
parent 4f0610338f
commit b74de954cf
3 changed files with 87 additions and 4 deletions

View File

@ -301,6 +301,13 @@ const defaultResponder: FullResponder = {
},
};
export interface ConnectionInfo {
localAddress?: string | undefined;
localPort?: number | undefined;
remoteAddress?: string | undefined;
remotePort?: number | undefined;
}
export interface ServerInterceptingCallInterface {
/**
* Register the listener to handle inbound events.
@ -338,6 +345,10 @@ export interface ServerInterceptingCallInterface {
* Return the auth context of the connection the call is associated with.
*/
getAuthContext(): AuthContext;
/**
* Return information about the connection used to make the call.
*/
getConnectionInfo(): ConnectionInfo;
}
export class ServerInterceptingCall implements ServerInterceptingCallInterface {
@ -449,6 +460,9 @@ export class ServerInterceptingCall implements ServerInterceptingCallInterface {
getAuthContext(): AuthContext {
return this.nextCall.getAuthContext();
}
getConnectionInfo(): ConnectionInfo {
return this.nextCall.getConnectionInfo();
}
}
export interface ServerInterceptor {
@ -519,6 +533,7 @@ export class BaseServerInterceptingCall
private receivedHalfClose = false;
private streamEnded = false;
private host: string;
private connectionInfo: ConnectionInfo;
constructor(
private readonly stream: http2.ServerHttp2Stream,
@ -606,6 +621,14 @@ export class BaseServerInterceptingCall
metadata.remove(http2.constants.HTTP2_HEADER_TE);
metadata.remove(http2.constants.HTTP2_HEADER_CONTENT_TYPE);
this.metadata = metadata;
const socket = stream.session?.socket;
this.connectionInfo = {
localAddress: socket?.localAddress,
localPort: socket?.localPort,
remoteAddress: socket?.remoteAddress,
remotePort: socket?.remotePort
};
}
private handleTimeoutHeader(timeoutHeader: string) {
@ -990,6 +1013,9 @@ export class BaseServerInterceptingCall
return {};
}
}
getConnectionInfo(): ConnectionInfo {
return this.connectionInfo;
}
}
export function getServerInterceptingCall(

View File

@ -150,15 +150,15 @@ export class TestClient {
this.client.waitForReady(deadline, callback);
}
sendRequest(callback: (error?: grpc.ServiceError) => void) {
this.client.echo({}, callback);
sendRequest(callback: (error?: grpc.ServiceError) => void): grpc.ClientUnaryCall {
return this.client.echo({}, callback);
}
sendRequestWithMetadata(
metadata: grpc.Metadata,
callback: (error?: grpc.ServiceError) => void
) {
this.client.echo({}, metadata, callback);
): grpc.ClientUnaryCall {
return this.client.echo({}, metadata, callback);
}
getChannelState() {

View File

@ -127,6 +127,22 @@ const testHeaderInjectionInterceptor: grpc.ServerInterceptor = (
});
};
const callExaminationInterceptor: grpc.ServerInterceptor = (
methodDescriptor,
call
) => {
const connectionInfo = call.getConnectionInfo();
return new grpc.ServerInterceptingCall(call, {
sendMetadata: (metadata, next) => {
metadata.add('local-address', `${connectionInfo.localAddress}`);
metadata.add('local-port', `${connectionInfo.localPort}`);
metadata.add('remote-address', `${connectionInfo.remoteAddress}`);
metadata.add('remote-port', `${connectionInfo.remotePort}`);
next(metadata);
}
})
}
describe('Server interceptors', () => {
describe('Auth-type interceptor', () => {
let server: grpc.Server;
@ -336,4 +352,45 @@ describe('Server interceptors', () => {
});
});
});
describe('Call properties', () => {
let server: grpc.Server;
let client: TestClient;
let portNum: number;
/* Tests that an interceptor can entirely prevent the handler from being
* invoked, based on the contents of the metadata. */
before(done => {
server = new grpc.Server({ interceptors: [callExaminationInterceptor] });
server.addService(echoService.service, {
echo: (
call: grpc.ServerUnaryCall<any, any>,
callback: grpc.sendUnaryData<any>
) => {
callback(null, call.request);
},
});
server.bindAsync(
'[::1]:0',
grpc.ServerCredentials.createInsecure(),
(error, port) => {
assert.ifError(error);
client = new TestClient(`localhost:${port}`, false);
portNum = port;
done();
}
);
});
after(done => {
client.close();
server.tryShutdown(done);
});
it('Should get valid connection information', done => {
const call = client.sendRequest(done);
call.on('metadata', metadata => {
assert.strictEqual(metadata.get('local-address')[0], '::1');
assert.strictEqual(metadata.get('remote-address')[0], '::1');
assert.strictEqual(metadata.get('local-port')[0], `${portNum}`);
assert.notStrictEqual(metadata.get('remote-port')[0], 'undefined');
});
});
});
});