Skip to content

Commit ad0cc1c

Browse files
committed
Support parallel resolution of supported reflection protocol check
1 parent 004395c commit ad0cc1c

File tree

1 file changed

+90
-50
lines changed

1 file changed

+90
-50
lines changed

src/client.ts

Lines changed: 90 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,20 @@ import type {
2020
ServerReflectionResponse,
2121
} from './reflection_providers/v1alpha/reflection_pb';
2222

23-
const supportedReflectionProtocols = [
24-
{
25-
protocol: 'v1alpha',
23+
const supportedReflectionProtocols = {
24+
v1alpha: {
25+
priority: 0,
2626
serviceName: 'grpc.reflection.v1alpha.ServerReflection',
2727
client: import('./reflection_providers/v1alpha/reflection_pb'),
2828
service: import('./reflection_providers/v1alpha/reflection_grpc_pb'),
2929
},
30-
{
31-
protocol: 'v1',
30+
v1: {
31+
priority: 1,
3232
serviceName: 'grpc.reflection.v1.ServerReflection',
3333
client: import('./reflection_providers/v1/reflection_pb'),
3434
service: import('./reflection_providers/v1/reflection_grpc_pb'),
3535
},
36-
];
36+
};
3737

3838
export class Client {
3939
metadata: Metadata;
@@ -95,60 +95,100 @@ export class Client {
9595
});
9696
}
9797

98-
private async initializeReflectionClient() {
99-
if (this.grpcClient || this.compatibleProtocol) {
100-
return;
101-
}
98+
private async evaluateServerReflectionProtocol() {
99+
const evaluationPromises = [];
102100

103101
// Check protocol compatibility and initialize gRPC client based on that
104-
for (const protocolConfig of supportedReflectionProtocols) {
105-
const {
106-
protocol,
107-
service: servicePromise,
108-
client: clientPromise,
109-
} = protocolConfig;
110-
111-
const [protocolService, protocolClient] = await Promise.all([
112-
servicePromise,
113-
clientPromise,
114-
]);
115-
116-
const grpcClientForProtocol = new protocolService.ServerReflectionClient(
117-
this.url,
118-
this.credentials,
119-
this.clientOptions
102+
for (const protocol of Object.keys(supportedReflectionProtocols)) {
103+
type PromiseReturnType = {
104+
successful: boolean;
105+
priority: number;
106+
effect?: () => void;
107+
error?: ServiceError;
108+
};
109+
110+
evaluationPromises.push(
111+
// eslint-disable-next-line no-async-promise-executor
112+
new Promise<PromiseReturnType>(async resolve => {
113+
const protocolConfig =
114+
supportedReflectionProtocols[
115+
protocol as keyof typeof supportedReflectionProtocols
116+
];
117+
const {
118+
service: servicePromise,
119+
client: clientPromise,
120+
} = protocolConfig;
121+
122+
const [protocolService, protocolClient] = await Promise.all([
123+
servicePromise,
124+
clientPromise,
125+
]);
126+
127+
const grpcClientForProtocol = new protocolService.ServerReflectionClient(
128+
this.url,
129+
this.credentials,
130+
this.clientOptions
131+
);
132+
133+
const request = new protocolClient.ServerReflectionRequest();
134+
135+
request.setListServices('*');
136+
137+
try {
138+
const [reflectionResponse] = await this.sendReflectionRequest(
139+
request,
140+
grpcClientForProtocol
141+
);
142+
143+
return resolve({
144+
successful: true,
145+
priority: protocolConfig.priority,
146+
effect: () => {
147+
this.grpcClient = grpcClientForProtocol;
148+
this.compatibleProtocol = protocol;
149+
this.CompatibleServerReflectionRequest =
150+
protocolClient.ServerReflectionRequest;
151+
this.reflectionResponseCache = reflectionResponse;
152+
},
153+
});
154+
} catch (error) {
155+
return resolve({
156+
successful: false,
157+
priority: protocolConfig.priority,
158+
error: error as ServiceError,
159+
});
160+
}
161+
})
120162
);
163+
}
121164

122-
const request = new protocolClient.ServerReflectionRequest();
165+
const evaluationResults = await Promise.all(evaluationPromises);
123166

124-
request.setListServices('*');
167+
const [successfulReflectionByPriority] = evaluationResults
168+
.filter(res => res.successful)
169+
.sort((res1, res2) => res2.priority - res1.priority);
125170

126-
try {
127-
const [reflectionResponse] = await this.sendReflectionRequest(
128-
request,
129-
grpcClientForProtocol
130-
);
171+
if (!successfulReflectionByPriority) {
172+
const noCompatibleProtocolRrror = new Error(
173+
'No compatible reflection protocol found.'
174+
);
131175

132-
this.grpcClient = grpcClientForProtocol;
133-
this.compatibleProtocol = protocol;
134-
this.CompatibleServerReflectionRequest =
135-
protocolClient.ServerReflectionRequest;
136-
this.reflectionResponseCache = reflectionResponse;
137-
138-
break; // Exit loop on first successful protocol
139-
} catch (error) {
140-
if ((error as ServiceError).code !== GrpcStatus.UNIMPLEMENTED) {
141-
// Something is wrong with the gRPC server itself
142-
throw error;
143-
}
176+
const resultWithServiceError = evaluationResults.find(res => {
177+
// Something is actually wrong with the gRPC service
178+
return res.error && res.error.code !== GrpcStatus.UNIMPLEMENTED;
179+
});
144180

145-
continue; // Try next protocol
146-
}
181+
throw resultWithServiceError?.error || noCompatibleProtocolRrror;
147182
}
148183

149-
if (!this.grpcClient) {
150-
throw new Error('No compatible reflection protocol found.');
151-
}
184+
// Set grpc client and other properties based on highest priority successful protocol
185+
successfulReflectionByPriority.effect!();
186+
}
187+
188+
private async initializeReflectionClient() {
189+
if (this.grpcClient || this.compatibleProtocol) return;
190+
191+
await this.evaluateServerReflectionProtocol();
152192
}
153193

154194
async listServices(): Promise<string[]> {

0 commit comments

Comments
 (0)