English | 中文
On a unary call of the tRPC protocol, a frame can carry a trailing span of raw bytes (the Attachment) after the serialized and compressed business body. Those bytes are not serialized or compressed. The sender appends them directly after the business body, and the receiver splits them off using attachment_size in the protocol frame header.
Attachment transfer applies only to the
trpcprotocol. HTTP does not support it.
The read and write functions live in trpc_sdk.codec and operate on the TrpcMessage attached to the current TrpcContext:
- On write: pass
bytesorstr. Astris encoded tobytesas UTF-8. - On read: the default return type is
bytes(an emptyb""when there is no attachment). Withis_str=True, the value is decoded as UTF-8 and returned as a string.
Set the request attachment with set_request_attachment before the call.
Context cloning matters. The proxy generated by the stub defaults to need_clone_ctx=True. When the context is cloned, the framework copies the request attachment already set (request_attachment) onto the cloned message. The response attachment returned by the server (response_attachment) is written onto the cloned message that actually sent the call. To read the response attachment from the original ctx after the call returns, construct the proxy with need_clone_ctx=False:
from trpc_sdk.abc import TrpcContext
from trpc_sdk.client import Options
from trpc_sdk.codec import get_response_attachment
from trpc_sdk.codec import set_request_attachment
proxy = rpc.GreeterClientProxyImpl(need_clone_ctx=False)
ctx = TrpcContext()
set_request_attachment(ctx, b"request attachment")
options = Options().set_target("ip://127.0.0.1:8000").set_protocol("trpc")
reply = await proxy.asyncSayHello(ctx, req, options)
print(get_response_attachment(ctx, is_str=True))In the handler, read the attachment sent by the client with get_request_attachment, and set the attachment returned to the client with set_response_attachment:
from trpc_sdk.codec import get_request_attachment
from trpc_sdk.codec import set_response_attachment
async def SayHello(self, ctx, request):
incoming = get_request_attachment(ctx, is_str=True)
set_response_attachment(ctx, f"return: {incoming}")
...- Attachments are not forwarded downstream automatically. The format and meaning of an attachment are a business convention. The framework does not check that the attachment matches the business body. When the server continues the call as a client, it does not copy the upstream request attachment onto the outbound request. To pass it on, call
set_request_attachmenton the outbound context yourself. - Protocol compatibility and unpacking errors. If
attachment_sizein the frame header is larger than the bytes left in the receive buffer, unpacking fails immediately (the server raisesServerDecodeError, and the client raisesClientDecodeError). Every hop on the call chain must recognizeattachment_size. A peer that does not support attachments treats the business body and the attachment bytes as one span and deserializes them as Protobuf, which fails to parse.