Skip to content

Commit 7a735ce

Browse files
authored
Merge pull request #2922 from murgatroid99/grpc-js_server_interceptor_connection_info
grpc-js: `ServerInterceptingCall`: add `getConnectionInfo` method
2 parents 4f06103 + b74de95 commit 7a735ce

File tree

3 files changed

+87
-4
lines changed

3 files changed

+87
-4
lines changed

packages/grpc-js/src/server-interceptors.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,13 @@ const defaultResponder: FullResponder = {
301301
},
302302
};
303303

304+
export interface ConnectionInfo {
305+
localAddress?: string | undefined;
306+
localPort?: number | undefined;
307+
remoteAddress?: string | undefined;
308+
remotePort?: number | undefined;
309+
}
310+
304311
export interface ServerInterceptingCallInterface {
305312
/**
306313
* Register the listener to handle inbound events.
@@ -338,6 +345,10 @@ export interface ServerInterceptingCallInterface {
338345
* Return the auth context of the connection the call is associated with.
339346
*/
340347
getAuthContext(): AuthContext;
348+
/**
349+
* Return information about the connection used to make the call.
350+
*/
351+
getConnectionInfo(): ConnectionInfo;
341352
}
342353

343354
export class ServerInterceptingCall implements ServerInterceptingCallInterface {
@@ -449,6 +460,9 @@ export class ServerInterceptingCall implements ServerInterceptingCallInterface {
449460
getAuthContext(): AuthContext {
450461
return this.nextCall.getAuthContext();
451462
}
463+
getConnectionInfo(): ConnectionInfo {
464+
return this.nextCall.getConnectionInfo();
465+
}
452466
}
453467

454468
export interface ServerInterceptor {
@@ -519,6 +533,7 @@ export class BaseServerInterceptingCall
519533
private receivedHalfClose = false;
520534
private streamEnded = false;
521535
private host: string;
536+
private connectionInfo: ConnectionInfo;
522537

523538
constructor(
524539
private readonly stream: http2.ServerHttp2Stream,
@@ -606,6 +621,14 @@ export class BaseServerInterceptingCall
606621
metadata.remove(http2.constants.HTTP2_HEADER_TE);
607622
metadata.remove(http2.constants.HTTP2_HEADER_CONTENT_TYPE);
608623
this.metadata = metadata;
624+
625+
const socket = stream.session?.socket;
626+
this.connectionInfo = {
627+
localAddress: socket?.localAddress,
628+
localPort: socket?.localPort,
629+
remoteAddress: socket?.remoteAddress,
630+
remotePort: socket?.remotePort
631+
};
609632
}
610633

611634
private handleTimeoutHeader(timeoutHeader: string) {
@@ -990,6 +1013,9 @@ export class BaseServerInterceptingCall
9901013
return {};
9911014
}
9921015
}
1016+
getConnectionInfo(): ConnectionInfo {
1017+
return this.connectionInfo;
1018+
}
9931019
}
9941020

9951021
export function getServerInterceptingCall(

packages/grpc-js/test/common.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,15 +150,15 @@ export class TestClient {
150150
this.client.waitForReady(deadline, callback);
151151
}
152152

153-
sendRequest(callback: (error?: grpc.ServiceError) => void) {
154-
this.client.echo({}, callback);
153+
sendRequest(callback: (error?: grpc.ServiceError) => void): grpc.ClientUnaryCall {
154+
return this.client.echo({}, callback);
155155
}
156156

157157
sendRequestWithMetadata(
158158
metadata: grpc.Metadata,
159159
callback: (error?: grpc.ServiceError) => void
160-
) {
161-
this.client.echo({}, metadata, callback);
160+
): grpc.ClientUnaryCall {
161+
return this.client.echo({}, metadata, callback);
162162
}
163163

164164
getChannelState() {

packages/grpc-js/test/test-server-interceptors.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,22 @@ const testHeaderInjectionInterceptor: grpc.ServerInterceptor = (
127127
});
128128
};
129129

130+
const callExaminationInterceptor: grpc.ServerInterceptor = (
131+
methodDescriptor,
132+
call
133+
) => {
134+
const connectionInfo = call.getConnectionInfo();
135+
return new grpc.ServerInterceptingCall(call, {
136+
sendMetadata: (metadata, next) => {
137+
metadata.add('local-address', `${connectionInfo.localAddress}`);
138+
metadata.add('local-port', `${connectionInfo.localPort}`);
139+
metadata.add('remote-address', `${connectionInfo.remoteAddress}`);
140+
metadata.add('remote-port', `${connectionInfo.remotePort}`);
141+
next(metadata);
142+
}
143+
})
144+
}
145+
130146
describe('Server interceptors', () => {
131147
describe('Auth-type interceptor', () => {
132148
let server: grpc.Server;
@@ -336,4 +352,45 @@ describe('Server interceptors', () => {
336352
});
337353
});
338354
});
355+
describe('Call properties', () => {
356+
let server: grpc.Server;
357+
let client: TestClient;
358+
let portNum: number;
359+
/* Tests that an interceptor can entirely prevent the handler from being
360+
* invoked, based on the contents of the metadata. */
361+
before(done => {
362+
server = new grpc.Server({ interceptors: [callExaminationInterceptor] });
363+
server.addService(echoService.service, {
364+
echo: (
365+
call: grpc.ServerUnaryCall<any, any>,
366+
callback: grpc.sendUnaryData<any>
367+
) => {
368+
callback(null, call.request);
369+
},
370+
});
371+
server.bindAsync(
372+
'[::1]:0',
373+
grpc.ServerCredentials.createInsecure(),
374+
(error, port) => {
375+
assert.ifError(error);
376+
client = new TestClient(`localhost:${port}`, false);
377+
portNum = port;
378+
done();
379+
}
380+
);
381+
});
382+
after(done => {
383+
client.close();
384+
server.tryShutdown(done);
385+
});
386+
it('Should get valid connection information', done => {
387+
const call = client.sendRequest(done);
388+
call.on('metadata', metadata => {
389+
assert.strictEqual(metadata.get('local-address')[0], '::1');
390+
assert.strictEqual(metadata.get('remote-address')[0], '::1');
391+
assert.strictEqual(metadata.get('local-port')[0], `${portNum}`);
392+
assert.notStrictEqual(metadata.get('remote-port')[0], 'undefined');
393+
});
394+
});
395+
});
339396
});

0 commit comments

Comments
 (0)