dubbo-go
dubbo-go copied to clipboard
Triple header/trailer usage pattern proposal
New Triple is based on connect-go (https://github.com/connectrpc/connect-go) which makes use of Golang generic feature. For instance, client and server side code of unary invocation would be like this:
// client side
Ping(context.Context, *connect_go.Request[v1.PingRequest]) (*connect_go.Response[v1.PingResponse], error)
// server side
Ping(context.Context, *connect_go.Request[v1.PingRequest]) (*connect_go.Response[v1.PingResponse], error)
// uniform Request
type Request[T any] struct {
Msg *T
spec Spec
peer Peer
header http.Header
}
// uniform Response
type Response[T any] struct {
Msg *T
header http.Header
trailer http.Header
}
Users could inject or receive headers and trailers directly from Request and Response just like this:
// client side
req := connect.NewRequest(&pingv1.PingRequest{})
req.Header().Set("hello", "wourld")
resp, err := Ping(context.Background(), req)
headers := resp.Header().Values("hello")
trailers := resp.Trailer().Values()
// do something with headers and trailers
// server side
func (p *pingServer) Ping(ctx context.Context, req *connect.Request[pingv1.PingRequest]) (*connect.Response[pingv1.PingResponse], error) {
headers := req.Headers().Values("hello")
// do something with headers
response := connect.NewResponse(
&pingv1.PingResponse{},
)
// return headers and trailers
response.Header().Set(handlerHeader, headerValue)
response.Trailer().Set(handlerTrailer, trailerValue)
return response, nil
}
This pattern is very uniform and easy to understand. But since we decided not to use generics API for compatibility, new Triple has to make use of some grpc-like pattern to send and receive headers and trailers.
client-side sending headers
ctx := triple.NewOutgoingContext(context.Background(), http.Header{"hello", "triple"})
resp, err := cli.Greet(ctx, &v1.GreetRequest{})
// or
ctx := triple.NewOutgoingContext(context.Background(), http.Header{"hello", "triple"})
ctx = triple.AppendToOutgoingContext(ctx, "hello", "dubbo", "hey", "hessian")
resp, err := cli.Greet(ctx, &v1.GreetRequest{})
// headers would be hello: triple, dubbo; hey: hessian.
client-side receiving headers and trailers
WIP
server-side receiving headers
func (srv *Server) Greet(ctx context.Context, req *greet.GreetRequest) (*greet.GreetResponse, error) {
headers, ok := triple.FromIncomingContext(ctx)
// do something with headers and ok flag
}
server-side sending headers and trailers
func (srv *Server) Greet(ctx context.Context, req *greet.GreetRequest) (*greet.GreetResponse, error) {
triple.SetHeader(ctx, http.Header{"hi", "triple"})
triple.SetTrailer(ctx, http.Header("end", "end"))
}
func (srv *Server) GreetStream(ctx context.Context, stream greet.GreetService_GreetStreamServer) error {
// stream could also make use of triple.SetHeader and triple.SetTrailer
// headers would be sent in the first call of stream.Send
// in some scenarios, users would hope to send headers directly
triple.Send(ctx, http.Header{"hi", "triple"})
}
Refer to (https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md) for more information.