Skip to content

Commit cdb9fc6

Browse files
Added support for Error Responses from the Reflection Service (grpc#1682)
Motivation: Whenever an error occurs during the exchange between the server implementing the Reflection Service and a client, the client should receive an Error Message. Modifications: Replaced throwing errors with creating an Error Reponse and sending it back to the client. Also, changed the APIs to use the Result type and added new extensions for the Reflection_ServerReflectionResponse initialisation. Also modified the tests. Result: When an error occurs within the Reflection Service, the clients will be sent an Error Response.
1 parent b58291e commit cdb9fc6

File tree

4 files changed

+412
-209
lines changed

4 files changed

+412
-209
lines changed

Sources/GRPCReflectionService/Server/ReflectionService.swift

Lines changed: 125 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ internal struct ReflectionServiceData: Sendable {
129129

130130
internal func serialisedFileDescriptorProtosForDependenciesOfFile(
131131
named fileName: String
132-
) throws -> [Data] {
132+
) -> Result<[Data], GRPCStatus> {
133133
var toVisit = Deque<String>()
134134
var visited = Set<String>()
135135
var serializedFileDescriptorProtos: [Data] = []
@@ -147,37 +147,59 @@ internal struct ReflectionServiceData: Sendable {
147147
let serializedFileDescriptorProto = protoData.serializedFileDescriptorProto
148148
serializedFileDescriptorProtos.append(serializedFileDescriptorProto)
149149
} else {
150-
throw GRPCStatus(
151-
code: .notFound,
152-
message: "The provided file or a dependency of the provided file could not be found."
150+
return .failure(
151+
GRPCStatus(
152+
code: .notFound,
153+
message: "The provided file or a dependency of the provided file could not be found."
154+
)
153155
)
154156
}
155157
visited.insert(currentFileName)
156158
}
157-
return serializedFileDescriptorProtos
159+
return .success(serializedFileDescriptorProtos)
158160
}
159161

160-
internal func nameOfFileContainingSymbol(named symbolName: String) -> String? {
161-
return self.fileNameBySymbol[symbolName]
162+
internal func nameOfFileContainingSymbol(named symbolName: String) -> Result<String, GRPCStatus> {
163+
guard let fileName = self.fileNameBySymbol[symbolName] else {
164+
return .failure(
165+
GRPCStatus(
166+
code: .notFound,
167+
message: "The provided symbol could not be found."
168+
)
169+
)
170+
}
171+
return .success(fileName)
162172
}
163173

164174
internal func nameOfFileContainingExtension(
165175
extendeeName: String,
166176
fieldNumber number: Int32
167-
) -> String? {
177+
) -> Result<String, GRPCStatus> {
168178
let key = ExtensionDescriptor(extendeeTypeName: extendeeName, fieldNumber: number)
169-
return self.fileNameByExtensionDescriptor[key]
179+
guard let fileName = self.fileNameByExtensionDescriptor[key] else {
180+
return .failure(
181+
GRPCStatus(
182+
code: .notFound,
183+
message: "The provided extension could not be found."
184+
)
185+
)
186+
}
187+
return .success(fileName)
170188
}
171189

172190
// Returns an empty array if the type has no extensions.
173-
internal func extensionsFieldNumbersOfType(named typeName: String) throws -> [Int32] {
191+
internal func extensionsFieldNumbersOfType(
192+
named typeName: String
193+
) -> Result<[Int32], GRPCStatus> {
174194
guard let fieldNumbers = self.fieldNumbersByType[typeName] else {
175-
throw GRPCStatus(
176-
code: .invalidArgument,
177-
message: "The provided type is invalid."
195+
return .failure(
196+
GRPCStatus(
197+
code: .invalidArgument,
198+
message: "The provided type is invalid."
199+
)
178200
)
179201
}
180-
return fieldNumbers
202+
return .success(fieldNumbers)
181203
}
182204
}
183205

@@ -191,17 +213,26 @@ internal final class ReflectionServiceProvider: Reflection_ServerReflectionAsync
191213
)
192214
}
193215

216+
internal func _findFileByFileName(
217+
_ fileName: String
218+
) -> Result<Reflection_ServerReflectionResponse.OneOf_MessageResponse, GRPCStatus> {
219+
return self.protoRegistry
220+
.serialisedFileDescriptorProtosForDependenciesOfFile(named: fileName)
221+
.map { fileDescriptorProtos in
222+
Reflection_ServerReflectionResponse.OneOf_MessageResponse.fileDescriptorResponse(
223+
.with {
224+
$0.fileDescriptorProto = fileDescriptorProtos
225+
}
226+
)
227+
}
228+
}
229+
194230
internal func findFileByFileName(
195231
_ fileName: String,
196232
request: Reflection_ServerReflectionRequest
197-
) throws -> Reflection_ServerReflectionResponse {
198-
return Reflection_ServerReflectionResponse(
199-
request: request,
200-
fileDescriptorResponse: try .with {
201-
$0.fileDescriptorProto = try self.protoRegistry
202-
.serialisedFileDescriptorProtosForDependenciesOfFile(named: fileName)
203-
}
204-
)
233+
) -> Reflection_ServerReflectionResponse {
234+
let result = self._findFileByFileName(fileName)
235+
return result.makeResponse(request: request)
205236
}
206237

207238
internal func getServicesNames(
@@ -215,53 +246,50 @@ internal final class ReflectionServiceProvider: Reflection_ServerReflectionAsync
215246
}
216247
return Reflection_ServerReflectionResponse(
217248
request: request,
218-
listServicesResponse: listServicesResponse
249+
messageResponse: .listServicesResponse(listServicesResponse)
219250
)
220251
}
221252

222253
internal func findFileBySymbol(
223254
_ symbolName: String,
224255
request: Reflection_ServerReflectionRequest
225-
) throws -> Reflection_ServerReflectionResponse {
226-
guard let fileName = self.protoRegistry.nameOfFileContainingSymbol(named: symbolName) else {
227-
throw GRPCStatus(
228-
code: .notFound,
229-
message: "The provided symbol could not be found."
230-
)
256+
) -> Reflection_ServerReflectionResponse {
257+
let result = self.protoRegistry.nameOfFileContainingSymbol(
258+
named: symbolName
259+
).flatMap {
260+
self._findFileByFileName($0)
231261
}
232-
return try self.findFileByFileName(fileName, request: request)
262+
return result.makeResponse(request: request)
233263
}
234264

235265
internal func findFileByExtension(
236266
extensionRequest: Reflection_ExtensionRequest,
237267
request: Reflection_ServerReflectionRequest
238-
) throws -> Reflection_ServerReflectionResponse {
239-
guard
240-
let fileName = self.protoRegistry.nameOfFileContainingExtension(
241-
extendeeName: extensionRequest.containingType,
242-
fieldNumber: extensionRequest.extensionNumber
243-
)
244-
else {
245-
throw GRPCStatus(
246-
code: .notFound,
247-
message: "The provided extension could not be found."
248-
)
268+
) -> Reflection_ServerReflectionResponse {
269+
let result = self.protoRegistry.nameOfFileContainingExtension(
270+
extendeeName: extensionRequest.containingType,
271+
fieldNumber: extensionRequest.extensionNumber
272+
).flatMap {
273+
self._findFileByFileName($0)
249274
}
250-
return try self.findFileByFileName(fileName, request: request)
275+
return result.makeResponse(request: request)
251276
}
252277

253278
internal func findExtensionsFieldNumbersOfType(
254279
named typeName: String,
255280
request: Reflection_ServerReflectionRequest
256-
) throws -> Reflection_ServerReflectionResponse {
257-
let fieldNumbers = try self.protoRegistry.extensionsFieldNumbersOfType(named: typeName)
258-
return Reflection_ServerReflectionResponse(
259-
request: request,
260-
extensionNumberResponse: .with {
261-
$0.baseTypeName = typeName
262-
$0.extensionNumber = fieldNumbers
263-
}
264-
)
281+
) -> Reflection_ServerReflectionResponse {
282+
let result = self.protoRegistry.extensionsFieldNumbersOfType(
283+
named: typeName
284+
).map { fieldNumbers in
285+
Reflection_ServerReflectionResponse.OneOf_MessageResponse.allExtensionNumbersResponse(
286+
Reflection_ExtensionNumberResponse.with {
287+
$0.baseTypeName = typeName
288+
$0.extensionNumber = fieldNumbers
289+
}
290+
)
291+
}
292+
return result.makeResponse(request: request)
265293
}
266294

267295
internal func serverReflectionInfo(
@@ -272,7 +300,7 @@ internal final class ReflectionServiceProvider: Reflection_ServerReflectionAsync
272300
for try await request in requestStream {
273301
switch request.messageRequest {
274302
case let .fileByFilename(fileName):
275-
let response = try self.findFileByFileName(
303+
let response = self.findFileByFileName(
276304
fileName,
277305
request: request
278306
)
@@ -283,28 +311,37 @@ internal final class ReflectionServiceProvider: Reflection_ServerReflectionAsync
283311
try await responseStream.send(response)
284312

285313
case let .fileContainingSymbol(symbolName):
286-
let response = try self.findFileBySymbol(
314+
let response = self.findFileBySymbol(
287315
symbolName,
288316
request: request
289317
)
290318
try await responseStream.send(response)
291319

292320
case let .fileContainingExtension(extensionRequest):
293-
let response = try self.findFileByExtension(
321+
let response = self.findFileByExtension(
294322
extensionRequest: extensionRequest,
295323
request: request
296324
)
297325
try await responseStream.send(response)
298326

299327
case let .allExtensionNumbersOfType(typeName):
300-
let response = try self.findExtensionsFieldNumbersOfType(
328+
let response = self.findExtensionsFieldNumbersOfType(
301329
named: typeName,
302330
request: request
303331
)
304332
try await responseStream.send(response)
305333

306334
default:
307-
throw GRPCStatus(code: .unimplemented)
335+
let response = Reflection_ServerReflectionResponse(
336+
request: request,
337+
messageResponse: .errorResponse(
338+
Reflection_ErrorResponse.with {
339+
$0.errorCode = Int32(GRPCStatus.Code.unimplemented.rawValue)
340+
$0.errorMessage = "The request is not implemented."
341+
}
342+
)
343+
)
344+
try await responseStream.send(response)
308345
}
309346
}
310347
}
@@ -313,34 +350,12 @@ internal final class ReflectionServiceProvider: Reflection_ServerReflectionAsync
313350
extension Reflection_ServerReflectionResponse {
314351
init(
315352
request: Reflection_ServerReflectionRequest,
316-
fileDescriptorResponse: Reflection_FileDescriptorResponse
317-
) {
318-
self = .with {
319-
$0.validHost = request.host
320-
$0.originalRequest = request
321-
$0.fileDescriptorResponse = fileDescriptorResponse
322-
}
323-
}
324-
325-
init(
326-
request: Reflection_ServerReflectionRequest,
327-
listServicesResponse: Reflection_ListServiceResponse
328-
) {
329-
self = .with {
330-
$0.validHost = request.host
331-
$0.originalRequest = request
332-
$0.listServicesResponse = listServicesResponse
333-
}
334-
}
335-
336-
init(
337-
request: Reflection_ServerReflectionRequest,
338-
extensionNumberResponse: Reflection_ExtensionNumberResponse
353+
messageResponse: Reflection_ServerReflectionResponse.OneOf_MessageResponse
339354
) {
340355
self = .with {
341356
$0.validHost = request.host
342357
$0.originalRequest = request
343-
$0.allExtensionNumbersResponse = extensionNumberResponse
358+
$0.messageResponse = messageResponse
344359
}
345360
}
346361
}
@@ -378,3 +393,33 @@ extension Google_Protobuf_FileDescriptorProto {
378393
return names
379394
}
380395
}
396+
397+
extension Result<Reflection_ServerReflectionResponse.OneOf_MessageResponse, GRPCStatus> {
398+
func recover() -> Result<Reflection_ServerReflectionResponse.OneOf_MessageResponse, Never> {
399+
self.flatMapError { status in
400+
let error = Reflection_ErrorResponse.with {
401+
$0.errorCode = Int32(status.code.rawValue)
402+
$0.errorMessage = status.message ?? ""
403+
}
404+
return .success(.errorResponse(error))
405+
}
406+
}
407+
408+
func makeResponse(
409+
request: Reflection_ServerReflectionRequest
410+
) -> Reflection_ServerReflectionResponse {
411+
let result = self.recover().attachRequest(request)
412+
// Safe to '!' as the failure type is 'Never'.
413+
return try! result.get()
414+
}
415+
}
416+
417+
extension Result where Success == Reflection_ServerReflectionResponse.OneOf_MessageResponse {
418+
func attachRequest(
419+
_ request: Reflection_ServerReflectionRequest
420+
) -> Result<Reflection_ServerReflectionResponse, Failure> {
421+
self.map { message in
422+
Reflection_ServerReflectionResponse(request: request, messageResponse: message)
423+
}
424+
}
425+
}

0 commit comments

Comments
 (0)