github.com/cloudwego/kitex@v0.9.0/pkg/remote/trans/nphttp2/meta_api.go (about)

     1  /*
     2   * Copyright 2022 CloudWeGo Authors
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package nphttp2
    18  
    19  import (
    20  	"context"
    21  	"net"
    22  
    23  	"github.com/cloudwego/kitex/pkg/remote/trans/nphttp2/codes"
    24  	"github.com/cloudwego/kitex/pkg/remote/trans/nphttp2/metadata"
    25  	"github.com/cloudwego/kitex/pkg/remote/trans/nphttp2/status"
    26  	"github.com/cloudwego/kitex/pkg/streaming"
    27  )
    28  
    29  // SetHeader sets the header metadata to be sent from the server to the client.
    30  // The context provided must be the context passed to the server's handler.
    31  //
    32  // Streaming RPCs should prefer the SetHeader method of the ServerStream.
    33  //
    34  // When called multiple times, all the provided metadata will be merged.  All
    35  // the metadata will be sent out when one of the following happens:
    36  //
    37  //   - grpc.SendHeader is called, or for streaming handlers, stream.SendHeader.
    38  //   - The first response message is sent.  For unary handlers, this occurs when
    39  //     the handler returns; for streaming handlers, this can happen when stream's
    40  //     SendMsg method is called.
    41  //   - An RPC status is sent out (error or success).  This occurs when the handler
    42  //     returns.
    43  //
    44  // SetHeader will fail if called after any of the events above.
    45  //
    46  // The error returned is compatible with the status package.  However, the
    47  // status code will often not match the RPC status as seen by the client
    48  // application, and therefore, should not be relied upon for this purpose.
    49  func SetHeader(ctx context.Context, md metadata.MD) error {
    50  	if md.Len() == 0 {
    51  		return nil
    52  	}
    53  	stream := serverTransportStreamFromContext(ctx)
    54  	if stream == nil {
    55  		return status.Errorf(codes.Internal, "grpc: failed to fetch the stream from the context %v", ctx)
    56  	}
    57  	return stream.SetHeader(md)
    58  }
    59  
    60  // SendHeader sends header metadata. It may be called at most once, and may not
    61  // be called after any event that causes headers to be sent (see SetHeader for
    62  // a complete list).  The provided md and headers set by SetHeader() will be
    63  // sent.
    64  //
    65  // The error returned is compatible with the status package.  However, the
    66  // status code will often not match the RPC status as seen by the client
    67  // application, and therefore, should not be relied upon for this purpose.
    68  func SendHeader(ctx context.Context, md metadata.MD) error {
    69  	stream := serverTransportStreamFromContext(ctx)
    70  	if stream == nil {
    71  		return status.Errorf(codes.Internal, "grpc: failed to fetch the stream from the context %v", ctx)
    72  	}
    73  	if err := stream.SendHeader(md); err != nil {
    74  		return err
    75  	}
    76  	return nil
    77  }
    78  
    79  // SetTrailer sets the trailer metadata that will be sent when an RPC returns.
    80  // When called more than once, all the provided metadata will be merged.
    81  //
    82  // The error returned is compatible with the status package.  However, the
    83  // status code will often not match the RPC status as seen by the client
    84  // application, and therefore, should not be relied upon for this purpose.
    85  func SetTrailer(ctx context.Context, md metadata.MD) error {
    86  	if md.Len() == 0 {
    87  		return nil
    88  	}
    89  	stream := serverTransportStreamFromContext(ctx)
    90  	if stream == nil {
    91  		return status.Errorf(codes.Internal, "grpc: failed to fetch the stream from the context %v", ctx)
    92  	}
    93  	stream.SetTrailer(md)
    94  	return nil
    95  }
    96  
    97  func serverTransportStreamFromContext(ctx context.Context) streaming.Stream {
    98  	return streaming.GetStream(ctx)
    99  }
   100  
   101  type (
   102  	headerKey  struct{}
   103  	trailerKey struct{}
   104  )
   105  
   106  // GRPCHeader is used for unary call client to get header from response.
   107  func GRPCHeader(ctx context.Context, md *metadata.MD) context.Context {
   108  	return context.WithValue(ctx, headerKey{}, md)
   109  }
   110  
   111  // GRPCTrailer is used for unary call client to get taiiler from response.
   112  func GRPCTrailer(ctx context.Context, md *metadata.MD) context.Context {
   113  	return context.WithValue(ctx, trailerKey{}, md)
   114  }
   115  
   116  // GetHeaderMetadataFromCtx is used to get the metadata of stream Header from ctx.
   117  func GetHeaderMetadataFromCtx(ctx context.Context) *metadata.MD {
   118  	header := ctx.Value(headerKey{})
   119  	if header != nil {
   120  		return header.(*metadata.MD)
   121  	}
   122  	return nil
   123  }
   124  
   125  // set header and trailer to the ctx by default.
   126  func receiveHeaderAndTrailer(ctx context.Context, conn net.Conn) context.Context {
   127  	if md, err := conn.(hasHeader).Header(); err == nil {
   128  		if h := ctx.Value(headerKey{}); h != nil {
   129  			// If using GRPCHeader(), set the value directly
   130  			hd := h.(*metadata.MD)
   131  			*hd = md
   132  		} else {
   133  			ctx = context.WithValue(ctx, headerKey{}, &md)
   134  		}
   135  	}
   136  	if md := conn.(hasTrailer).Trailer(); md != nil {
   137  		if t := ctx.Value(trailerKey{}); t != nil {
   138  			// If using GRPCTrailer(), set the value directly
   139  			tr := t.(*metadata.MD)
   140  			*tr = md
   141  		} else {
   142  			ctx = context.WithValue(ctx, trailerKey{}, &md)
   143  		}
   144  	}
   145  	return ctx
   146  }