What is the state of compression support in gRPC for Python?

Viewed 1289

I am attempting to implement a gRPC client and server using the Python gRPC implementation as a reference.

According to the spec document at https://github.com/grpc/grpc/blob/master/doc/compression.md there are several use-cases that should be supported:

Compression MAY be configured by the Client Application by calling the appropriate API method. There are two scenarios where compression MAY be configured:

  • At channel creation time, which sets the channel default compression and therefore the compression that SHALL be used in the absence of per-RPC compression configuration.
  • At response time, via: For unary RPCs, the {Client,Server}Context instance. For streaming RPCs, the {Client,Server}Writer instance. In this case, configuration is reduced to disabling compression altogether.

...

Compression Method Asymmetry Between Peers

A gRPC peer MAY choose to respond using a different compression method to that of the request, including not performing any compression, regardless of channel and RPC settings (for example, if compression would result in small or negative gains).

...

Specific Disabling of Compression

If the user (through the previously described mechanisms) requests to disable compression the next message MUST be sent uncompressed. This is instrumental in preventing BEAST/CRIME attacks. This applies to both the unary and streaming cases.

Some of these seem to be possible, and others not. Tackling them one by one, here is what I think is possible:

  • At channel creation time, which sets the channel default compression and therefore the compression that SHALL be used in the absence of per-RPC compression configuration.

This works. On the server you can specify:

from grpc._cython.cygrpc import CompressionAlgorithm, CompressionLevel

server_options = [(
     "grpc.default_compression_algorithm", CompressionAlgorithm.gzip
),(
     "grpc.default_compression_level", CompressionLevel.high
)]

server = grpc.server(
    futures.ThreadPoolExecutor(max_workers=10), options=server_options
)

And on the client:

from grpc._cython.cygrpc import CompressionAlgorithm

channel_options = [(
     "grpc.default_compression_algorithm", CompressionAlgorithm.gzip
)]

channel = grpc.insecure_channel(
    "127.0.0.1:{}".format(port), options=channel_options
)

This correctly sets up the default algorithms on both sides.

You can override the compression algorithm for a specific call by setting the grpc-internal-encoding-request metadata key:

stub.MethodName(request, metadata={'grpc-internal-encoding-request': 'gzip'})

This is ultimately sets the grpc-encoding header on the request and the body is encoded appropriately.

So far so good. Next:

Compression Method Asymmetry Between Peers

A gRPC peer MAY choose to respond using a different compression method to that of the request, including not performing any compression, regardless of channel and RPC settings

I found an example of how to do this in this unit test and a referencing stack overflow question. However, when I try do this I see an error in my logs, and the response header is not changed:

prepare_application_metadata: {"created":"@1541795237.323499000","description":"Unallowed duplicate metadata","file":"src/core/lib/transport/metadata_batch.cc","file_line":113,"key":"grpc-internal-encoding-request","value":"gzip"}

The error is raised when the server also has a default compression algorithm set. When there is no default it does seem to work.

Relatedly, the accepted answer to this stackoverflow question suggests that grpc-internal-encoding-request should only be used in the client ( which makes sense given the name)

So I don't know whether there's a bug whereby you can't set a default and an override at the same time, or whether setting grpc-internal-encoding-request is genuinely invalid on the server side (and if so how the response encoding should be changed)

Finally:

Specific Disabling of Compression

If the user (through the previously described mechanisms) requests to disable compression the next message MUST be sent uncompressed. This is instrumental in preventing BEAST/CRIME attacks. This applies to both the unary and streaming cases.

This Github issue says that this is supported, but it seems only by the client stubs in the beta package, even though the referenced commit was merged in 2015. Is support for this still on some kind of roadmap, or have I missed something somewhere?

I don't have to use the Python implementation as my reference. Is there a more complete implementation in another language?

0 Answers
Related