@@ -7,14 +7,15 @@ import {
7
7
import * as grpc from '@grpc/grpc-js' ;
8
8
import * as protoLoader from '@grpc/proto-loader' ;
9
9
10
- import { ExtensionNumberResponse__Output } from './generated/grpc/reflection/v1/ExtensionNumberResponse' ;
11
- import { FileDescriptorResponse__Output } from './generated/grpc/reflection/v1/FileDescriptorResponse' ;
12
- import { ListServiceResponse__Output } from './generated/grpc/reflection/v1/ListServiceResponse' ;
13
- import { ServerReflectionRequest } from './generated/grpc/reflection/v1/ServerReflectionRequest' ;
14
- import { ServerReflectionResponse } from './generated/grpc/reflection/v1/ServerReflectionResponse' ;
15
- import { visit } from './protobuf-visitor' ;
16
- import { scope } from './utils' ;
17
- import { PROTO_LOADER_OPTS } from './constants' ;
10
+ import { ExtensionNumberResponse__Output } from '../generated/grpc/reflection/v1/ExtensionNumberResponse' ;
11
+ import { FileDescriptorResponse__Output } from '../generated/grpc/reflection/v1/FileDescriptorResponse' ;
12
+ import { ListServiceResponse__Output } from '../generated/grpc/reflection/v1/ListServiceResponse' ;
13
+ import { ServerReflectionRequest } from '../generated/grpc/reflection/v1/ServerReflectionRequest' ;
14
+ import { ServerReflectionResponse } from '../generated/grpc/reflection/v1/ServerReflectionResponse' ;
15
+ import { visit } from './common/protobuf-visitor' ;
16
+ import { scope } from './common/utils' ;
17
+ import { PROTO_LOADER_OPTS } from './common/constants' ;
18
+ import { ReflectionServerOptions } from './common/interfaces' ;
18
19
19
20
export class ReflectionError extends Error {
20
21
constructor (
@@ -37,22 +38,27 @@ export class ReflectionError extends Error {
37
38
export class ReflectionV1Implementation {
38
39
39
40
/** The full list of proto files (including imported deps) that the gRPC server includes */
40
- private fileDescriptorSet = new FileDescriptorSet ( ) ;
41
+ private readonly fileDescriptorSet = new FileDescriptorSet ( ) ;
41
42
42
43
/** An index of proto files by file name (eg. 'sample.proto') */
43
- private fileNameIndex : Record < string , FileDescriptorProto > = { } ;
44
+ private readonly fileNameIndex : Record < string , FileDescriptorProto > = { } ;
44
45
45
46
/** An index of proto files by type extension relationship
46
47
*
47
48
* extensionIndex[<pkg>.<msg>][<field#>] contains a reference to the file containing an
48
49
* extension for the type "<pkg>.<msg>" and field number "<field#>"
49
50
*/
50
- private extensionIndex : Record < string , Record < number , FileDescriptorProto > > = { } ;
51
+ private readonly extensionIndex : Record < string , Record < number , FileDescriptorProto > > = { } ;
51
52
52
53
/** An index of fully qualified symbol names (eg. 'sample.Message') to the files that contain them */
53
- private symbolMap : Record < string , FileDescriptorProto > = { } ;
54
+ private readonly symbolMap : Record < string , FileDescriptorProto > = { } ;
55
+
56
+ /** Options that the user provided for this service */
57
+ private readonly options ?: ReflectionServerOptions ;
58
+
59
+ constructor ( root : protoLoader . PackageDefinition , options ?: ReflectionServerOptions ) {
60
+ this . options = options ;
54
61
55
- constructor ( root : protoLoader . PackageDefinition ) {
56
62
Object . values ( root ) . forEach ( ( { fileDescriptorProtos } ) => {
57
63
// Add file descriptors to the FileDescriptorSet.
58
64
// We use the Array check here because a ServiceDefinition could have a method named the same thing
@@ -88,10 +94,10 @@ export class ReflectionV1Implementation {
88
94
extension : ( fqn , file , ext ) => {
89
95
index ( fqn , file ) ;
90
96
91
- const extendeeName = ext . getExtendee ( ) ;
97
+ const extendeeName = ext . getExtendee ( ) || '' ;
92
98
this . extensionIndex [ extendeeName ] = {
93
99
...( this . extensionIndex [ extendeeName ] || { } ) ,
94
- [ ext . getNumber ( ) ] : file ,
100
+ [ ext . getNumber ( ) || - 1 ] : file ,
95
101
} ;
96
102
} ,
97
103
} ) ,
@@ -126,25 +132,26 @@ export class ReflectionV1Implementation {
126
132
return ;
127
133
}
128
134
129
- if ( referencedFile !== sourceFile ) {
130
- sourceFile . addDependency ( referencedFile . getName ( ) ) ;
135
+ const fname = referencedFile . getName ( ) ;
136
+ if ( referencedFile !== sourceFile && fname ) {
137
+ sourceFile . addDependency ( fname ) ;
131
138
}
132
139
} ;
133
140
134
141
this . fileDescriptorSet . getFileList ( ) . forEach ( ( file ) =>
135
142
visit ( file , {
136
- field : ( fqn , file , field ) => addReference ( field . getTypeName ( ) , file , scope ( fqn ) ) ,
137
- extension : ( fqn , file , ext ) => addReference ( ext . getTypeName ( ) , file , scope ( fqn ) ) ,
143
+ field : ( fqn , file , field ) => addReference ( field . getTypeName ( ) || '' , file , scope ( fqn ) ) ,
144
+ extension : ( fqn , file , ext ) => addReference ( ext . getTypeName ( ) || '' , file , scope ( fqn ) ) ,
138
145
method : ( fqn , file , method ) => {
139
- addReference ( method . getInputType ( ) , file , scope ( fqn ) ) ;
140
- addReference ( method . getOutputType ( ) , file , scope ( fqn ) ) ;
146
+ addReference ( method . getInputType ( ) || '' , file , scope ( fqn ) ) ;
147
+ addReference ( method . getOutputType ( ) || '' , file , scope ( fqn ) ) ;
141
148
} ,
142
149
} ) ,
143
150
) ;
144
151
}
145
152
146
153
addToServer ( server : Pick < grpc . Server , 'addService' > ) {
147
- const protoPath = path . join ( __dirname , '../proto/grpc/reflection/v1/reflection.proto' ) ;
154
+ const protoPath = path . join ( __dirname , '../../ proto/grpc/reflection/v1/reflection.proto' ) ;
148
155
const pkgDefinition = protoLoader . loadSync ( protoPath , PROTO_LOADER_OPTS ) ;
149
156
const pkg = grpc . loadPackageDefinition ( pkgDefinition ) as any ;
150
157
@@ -180,8 +187,10 @@ export class ReflectionV1Implementation {
180
187
} else if ( message . fileByFilename !== undefined ) {
181
188
response . fileDescriptorResponse = this . fileByFilename ( message . fileByFilename ) ;
182
189
} else if ( message . fileContainingExtension !== undefined ) {
183
- const { containingType, extensionNumber } = message . fileContainingExtension ;
184
- response . fileDescriptorResponse = this . fileContainingExtension ( containingType , extensionNumber ) ;
190
+ response . fileDescriptorResponse = this . fileContainingExtension (
191
+ message . fileContainingExtension ?. containingType || '' ,
192
+ message . fileContainingExtension ?. extensionNumber || - 1
193
+ ) ;
185
194
} else if ( message . allExtensionNumbersOfType ) {
186
195
response . allExtensionNumbersResponse = this . allExtensionNumbersOfType ( message . allExtensionNumbersOfType ) ;
187
196
} else {
@@ -224,7 +233,12 @@ export class ReflectionV1Implementation {
224
233
)
225
234
. flat ( ) ;
226
235
227
- return { service : services . map ( ( service ) => ( { name : service } ) ) } ;
236
+ const whitelist = new Set ( this . options ?. services ?? undefined ) ;
237
+ const exposedServices = this . options ?. services ?
238
+ services . filter ( service => whitelist . has ( service ) )
239
+ : services ;
240
+
241
+ return { service : exposedServices . map ( ( service ) => ( { name : service } ) ) } ;
228
242
}
229
243
230
244
/** Find the proto file(s) that declares the given fully-qualified symbol name
0 commit comments