diff --git a/conformance/client_config.yaml b/conformance/client_config.yaml index 796997a..b34a092 100644 --- a/conformance/client_config.yaml +++ b/conformance/client_config.yaml @@ -8,6 +8,7 @@ features: - PROTOCOL_GRPC_WEB codecs: - CODEC_PROTO + - CODEC_JSON compressions: - COMPRESSION_IDENTITY - COMPRESSION_GZIP diff --git a/conformance/client_runner.py b/conformance/client_runner.py index 52d878e..95a72c5 100755 --- a/conformance/client_runner.py +++ b/conformance/client_runner.py @@ -310,6 +310,7 @@ async def handle_message(msg: client_compat_pb2.ClientCompatRequest) -> client_c payloads = [] try: options = ClientOptions() + if msg.protocol == config_pb2.PROTOCOL_GRPC: options.protocol = "grpc" if msg.protocol == config_pb2.PROTOCOL_GRPC_WEB: @@ -318,6 +319,9 @@ async def handle_message(msg: client_compat_pb2.ClientCompatRequest) -> client_c if msg.compression == config_pb2.COMPRESSION_GZIP: options.request_compression_name = "gzip" + if msg.codec == config_pb2.CODEC_JSON: + options.use_binary_format = False + client = service_connect.ConformanceServiceClient(base_url=url, session=session, options=options) if msg.stream_type == config_pb2.STREAM_TYPE_UNARY: if msg.request_delay_ms > 0: diff --git a/src/connect/client.py b/src/connect/client.py index b773e9c..e74fbb2 100644 --- a/src/connect/client.py +++ b/src/connect/client.py @@ -11,7 +11,7 @@ from yarl import URL from connect.code import Code -from connect.codec import Codec, ProtoBinaryCodec +from connect.codec import Codec, CodecNameType, ProtoBinaryCodec, ProtoJSONCodec from connect.compression import COMPRESSION_IDENTITY, Compression, GZipCompression, get_compresion_from_name from connect.connect import ( Spec, @@ -120,7 +120,12 @@ def __init__(self, raw_url: str, options: ClientOptions): elif options.protocol == "grpc-web": self.protocol = ProtocolGRPC(web=True) self.procedure = proto_path - self.codec = ProtoBinaryCodec() + + if options.use_binary_format: + self.codec = ProtoBinaryCodec() + else: + self.codec = ProtoJSONCodec(CodecNameType.JSON) + self.request_compression_name = options.request_compression_name self.compressions = [GZipCompression()] if self.request_compression_name and self.request_compression_name != COMPRESSION_IDENTITY: diff --git a/src/connect/codec.py b/src/connect/codec.py index faebd0a..84d2932 100644 --- a/src/connect/codec.py +++ b/src/connect/codec.py @@ -249,7 +249,7 @@ def marshal(self, message: Any) -> bytes: json_str = json_format.MessageToJson(message) - return json_str.encode("utf-8") + return json_str.encode() def unmarshal(self, data: bytes, message: Any) -> Any: """Unmarshal the given byte data into a protobuf message. @@ -269,7 +269,7 @@ def unmarshal(self, data: bytes, message: Any) -> Any: if not isinstance(obj, google.protobuf.message.Message): raise ValueError("Data is not a protobuf message") - return json_format.Parse(data.decode("utf-8"), obj, ignore_unknown_fields=True) + return json_format.Parse(data.decode(), obj, ignore_unknown_fields=True) def marshal_stable(self, message: Any) -> bytes: """Serialize a protobuf message to a JSON string encoded as UTF-8 bytes in a deterministic way. @@ -298,7 +298,7 @@ def marshal_stable(self, message: Any) -> bytes: parsed = json.loads(json_str) compacted_json = json.dumps(parsed, separators=(",", ":")) - return compacted_json.encode("utf-8") + return compacted_json.encode() def is_binary(self) -> bool: """Determine if the codec is binary. diff --git a/src/connect/options.py b/src/connect/options.py index ef7eba6..e614f21 100644 --- a/src/connect/options.py +++ b/src/connect/options.py @@ -87,6 +87,9 @@ class ClientOptions(BaseModel): protocol: Literal["connect", "grpc", "grpc-web"] = Field(default="connect") """The protocol to use for the request.""" + use_binary_format: bool = Field(default=True) + """A boolean indicating whether to use binary format for the request.""" + def merge(self, override_options: "ClientOptions | None" = None) -> "ClientOptions": """Merge this options object with an override options object.