grpc-swift
grpc-swift copied to clipboard
Extensions are not deserialized
Describe the bug
Having a service and messages like these:
service MyService {
rpc RequestMyData (MyRequest) returns (MyResponse) {}
}
message MyRequest {
}
message MyResponse {
MyData data = 1;
}
message MyData {
required string id = 1;
extensions 1000 to max;
}
message MyCustomData {
required string customString = 1;
extend MyData {
optional MyCustomData customData = 1000;
}
}
Golang server sends a response and sets MyCustomData as extension of MyData. iOS client receives the MyResponse message, but getting nil for the extension of MyData.
Both server and client are able to set & get extensions of the messages created locally.
GRPC Serialization code shows a sign that extensions are just ignored, because it just passes serialized data to the Message init and doesn't pass the extensions:
public struct ProtobufDeserializer<Message: SwiftProtobuf.Message>: MessageDeserializer {
@inlinable
public func deserialize(byteBuffer: ByteBuffer) throws -> Message {
var buffer = byteBuffer
// '!' is okay; we can always read 'readableBytes'.
let data = buffer.readData(length: buffer.readableBytes)!
return try Message(serializedData: data)
}
}
swift-protobuf Message sources:
@inlinable
public init(
serializedData data: Data,
extensions: ExtensionMap? = nil,
partial: Bool = false,
options: BinaryDecodingOptions = BinaryDecodingOptions()
) throws {
self.init()
#if swift(>=5.0)
try merge(contiguousBytes: data, extensions: extensions, partial: partial, options: options)
#else
try merge(serializedData: data, extensions: extensions, partial: partial, options: options)
#endif
}
protoc-gen-grpc-swift version: latest
Feeding in extension data (or decoding options) is not currently possible; we should add support for this though.
For the client, you can work around it by providing a wrapper type for your messages:
struct ProtoWrapper<ProtoMessage: Message>: GRPCPayload {
var message: ProtoMessage
init(_ message: ProtoMessage) {
self.message = message
}
init(serializedByteBuffer buffer: inout ByteBuffer) throws {
let extensions = ...
self.message = try .init(serializedData: Data(buffer: buffer), extensions: extensions)
}
func serialize(into buffer: inout ByteBuffer) throws {
buffer.writeData(try self.message.serializedData())
}
}
extension Message {
var wrapped: ProtoWrapper<Self> { return .init(self) }
}
And then manually making calls with your client:
let client = GRPCAnyServiceClient(channel: ...)
let call = client.makeUnaryCall(path: "/echo.Echo/Get", request: request.wrapped, responseType: ProtoMessage<Response>.self)