Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ServerInterceptor how to get header/trailer data #4317

Closed
zhenjunMa opened this issue Apr 2, 2021 · 5 comments
Closed

ServerInterceptor how to get header/trailer data #4317

zhenjunMa opened this issue Apr 2, 2021 · 5 comments

Comments

@zhenjunMa
Copy link

scene

I use grpc.UnaryServerInterceptor/grpc.StreamServerInterceptor to do some common logic before/after the biz code.

in the common logic, i want to get request header before biz code and get response header/trailer after biz code, but i found i can't do the latter.

my implementation

var unaryInterceptor grpc.UnaryServerInterceptor
unaryInterceptor = func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error){
                 //here i can get the header when request come
		md, ok := metadata.FromIncomingContext(ctx)
		if ok {
			//do something...
		}

                 //do biz logic
		resp, err = handler(ctx, req)

                 //here,i want get header/trailer to do something
                 //finally i use reflect achieve the goal
		stream := grpc.ServerTransportStreamFromContext(ctx)
		v := reflect.ValueOf(stream)
		fmt.Printf("header: %v", reflect.Indirect(v).FieldByName("header"))
		fmt.Printf("trailer: %v", reflect.Indirect(v).FieldByName("trailer"))

		return
	}

question

I can't find an easy way to get header/trailer after the biz logic, so i use reflect.
In my opinion, get header/trailer after the biz logic maybe a feature that many people need, so did i lost some doc about this or this feature is indeed unavailable now, need a PR? :)

looking forward reply.

@menghanl
Copy link
Contributor

menghanl commented Apr 2, 2021

This can be achieved by replacing the ServerTransportStreamFromContext in the context with a wrapped one, and override SetHeader(), SendHeader(), SetTrailer(). (I don't remember if this is the intended use case though)

type wrapper struct {
    grpc.ServerTransportStream
}

stream := grpc.ServerTransportStreamFromContext(ctx)
wrappedStream = &wrapper{stream}
newCtx := grpc.NewContextWithServerTransportStream(ctx, wrappedStream)

@zhenjunMa
Copy link
Author

@menghanl
Sorry, i can't get your point.

you mean this?

type wrapper struct {
	grpc.ServerTransportStream
}

func (w *wrapper) Method() string {

}
func (w *wrapper) SetHeader(md metadata.MD) error{
     //i have no idea about the implementation
     //any tips?
}
func (w *wrapper) SendHeader(md metadata.MD) error{

}
func (w *wrapper) SetTrailer(md metadata.MD) error{

}

my problem is how can i override SetHeader/SetTrailer to get the header/trailer fields in grpc.internal.transport.Stream

@menghanl
Copy link
Contributor

menghanl commented Apr 3, 2021

You are not implementing SetHeader. You just need to call the SetHeader, but at the same time, keep a copy of the header that's being sent.

type wrapper struct {
	grpc.ServerTransportStream

	header, trailer metadata.MD
}

func (w *wrapper) Method() string {
}
func (w *wrapper) SetHeader(md metadata.MD) error{
  if err := w.ServerTransportStream.SetHeader(md); err != nil {
    return err
  }
  w.header = md
  return nil
}
func (w *wrapper) SendHeader(md metadata.MD) error{
  ...
}
func (w *wrapper) SetTrailer(md metadata.MD) error{
  ...
}

stream := grpc.ServerTransportStreamFromContext(ctx)
wrappedStream = &wrapper{stream}
newCtx := grpc.NewContextWithServerTransportStream(ctx, wrappedStream)

hander(newCtx, ...)

fmt.Println(wrappedStream.header)

@zhenjunMa
Copy link
Author

@menghanl

Thank you for your reply, this indeed solve my problem, now this issue can be closed.

At last, I really hope you can think to expose more APIs in ServerTransportStream interface,

such as GetHeader()/GetTrailer(), which will make get info after biz logic easier.

And maybe this is also a common scene for other users.

@menghanl
Copy link
Contributor

menghanl commented Apr 5, 2021

I agree that ideally there should be an easier way to do this, but that will need a closer look.

The transport.Stream has internal methods to return Header and Trailer, but they all have assumptions about when they can be called.

Or, we can always wrap as mentioned above, so that it's available to all user interceptors.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 4, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants