dubbo-go icon indicating copy to clipboard operation
dubbo-go copied to clipboard

Triple header/trailer usage pattern proposal

Open DMwangnima opened this issue 1 year ago • 2 comments

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.

DMwangnima avatar Sep 11 '23 16:09 DMwangnima

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"})
}

DMwangnima avatar Sep 11 '23 16:09 DMwangnima

Refer to (https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md) for more information.

DMwangnima avatar Sep 11 '23 16:09 DMwangnima