Encoding
========

GRPC supports sending messages using any encoding format, and grpclib supports
this feature as well.

By default, gRPC interprets ``application/grpc`` content type as
``application/grpc+proto`` content type. So by default gRPC uses Protocol
Buffers as encoding format.

But why content type has such name with a ``proto`` subtype? This is because
messages in gRPC are sent as length-delimited stream of binary blobs. This
format can't be changed, so content type should always be in the
form of ``application/grpc+{subtype}``, where ``{subtype}`` can be anything you
want, e.g. ``proto``, ``fbs``, ``json``, ``thrift``, ``bson``, ``msgpack``.

Codec
~~~~~

In order to use custom serialization format, you should implement
:py:class:`~grpclib.encoding.base.CodecBase` abstract base class:

.. code-block:: python3

    from grpclib.encoding.base import CodecBase

    class JSONCodec(CodecBase):
        __content_subtype__ = 'json'

        def encode(self, message, message_type):
            return json.dumps(message, ensure_ascii=False).encode('utf-8')

        def decode(self, data: bytes, message_type):
            return json.loads(data.decode('utf-8'))


If your format doesn't have interface definition language (like protocol
buffers language) and code-generation tools (like ``protoc`` compiler), you will
have to manage your server-side and client-side code yourself. JSON format
doesn't have such tools, so let's try define our server-side and client side
code.

Naming Conventions
~~~~~~~~~~~~~~~~~~

Even if you don't use Protocol Buffers for messages encoding, this language also
defines coding style for services definition. These rules are related to
service names and method names, which are used by gRPC to build ``:path`` pseudo
header::

    :path = /dotted.package.CamelCaseServiceName/CamelCaseMethodName

`Protocol Buffers Style Guide`_ says:

    You should use CamelCase (with an initial capital) for both the service name
    and any RPC method names.

Server example
~~~~~~~~~~~~~~

.. code-block:: python3

    from grpclib.const import Cardinality, Handler
    from grpclib.server import Server

    class PingServiceHandler:

        async def Ping(self, stream):
            request = await stream.recv_message()
            ...
            await stream.send_message({'value': 'pong'})

        def __mapping__(self):
            return {
                '/ping.PingService/Ping': Handler(
                    self.UnaryUnary,
                    Cardinality.UNARY_UNARY,
                    None,
                    None,
                ),
            }

    server = Server([PingServiceHandler()], codec=JSONCodec())

Client example
~~~~~~~~~~~~~~

.. code-block:: python3

    from grpclib.client import Channel, UnaryUnaryMethod

    class PingServiceStub:

        def __init__(self, channel):
            self.Ping = UnaryUnaryMethod(
                channel,
                '/ping.PingService/Ping',
                None,
                None,
            )

    channel = Channel(codec=JSONCodec())
    ping_stub = PingServiceStub(channel)
    ...
    await ping_stub.Ping({'value': 'ping'})

.. _Protocol Buffers Style Guide: https://developers.google.com/protocol-buffers/docs/style
