context.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. package runtime
  2. import (
  3. "fmt"
  4. "net"
  5. "net/http"
  6. "strconv"
  7. "strings"
  8. "time"
  9. "golang.org/x/net/context"
  10. "google.golang.org/grpc/codes"
  11. "google.golang.org/grpc/grpclog"
  12. "google.golang.org/grpc/metadata"
  13. "google.golang.org/grpc/status"
  14. )
  15. // MetadataHeaderPrefix is the http prefix that represents custom metadata
  16. // parameters to or from a gRPC call.
  17. const MetadataHeaderPrefix = "Grpc-Metadata-"
  18. // MetadataPrefix is the prefix for grpc-gateway supplied custom metadata fields.
  19. const MetadataPrefix = "grpcgateway-"
  20. // MetadataTrailerPrefix is prepended to gRPC metadata as it is converted to
  21. // HTTP headers in a response handled by grpc-gateway
  22. const MetadataTrailerPrefix = "Grpc-Trailer-"
  23. const metadataGrpcTimeout = "Grpc-Timeout"
  24. const xForwardedFor = "X-Forwarded-For"
  25. const xForwardedHost = "X-Forwarded-Host"
  26. var (
  27. // DefaultContextTimeout is used for gRPC call context.WithTimeout whenever a Grpc-Timeout inbound
  28. // header isn't present. If the value is 0 the sent `context` will not have a timeout.
  29. DefaultContextTimeout = 0 * time.Second
  30. )
  31. /*
  32. AnnotateContext adds context information such as metadata from the request.
  33. At a minimum, the RemoteAddr is included in the fashion of "X-Forwarded-For",
  34. except that the forwarded destination is not another HTTP service but rather
  35. a gRPC service.
  36. */
  37. func AnnotateContext(ctx context.Context, mux *ServeMux, req *http.Request) (context.Context, error) {
  38. var pairs []string
  39. timeout := DefaultContextTimeout
  40. if tm := req.Header.Get(metadataGrpcTimeout); tm != "" {
  41. var err error
  42. timeout, err = timeoutDecode(tm)
  43. if err != nil {
  44. return nil, status.Errorf(codes.InvalidArgument, "invalid grpc-timeout: %s", tm)
  45. }
  46. }
  47. for key, vals := range req.Header {
  48. for _, val := range vals {
  49. // For backwards-compatibility, pass through 'authorization' header with no prefix.
  50. if strings.ToLower(key) == "authorization" {
  51. pairs = append(pairs, "authorization", val)
  52. }
  53. if h, ok := mux.incomingHeaderMatcher(key); ok {
  54. pairs = append(pairs, h, val)
  55. }
  56. }
  57. }
  58. if host := req.Header.Get(xForwardedHost); host != "" {
  59. pairs = append(pairs, strings.ToLower(xForwardedHost), host)
  60. } else if req.Host != "" {
  61. pairs = append(pairs, strings.ToLower(xForwardedHost), req.Host)
  62. }
  63. if addr := req.RemoteAddr; addr != "" {
  64. if remoteIP, _, err := net.SplitHostPort(addr); err == nil {
  65. if fwd := req.Header.Get(xForwardedFor); fwd == "" {
  66. pairs = append(pairs, strings.ToLower(xForwardedFor), remoteIP)
  67. } else {
  68. pairs = append(pairs, strings.ToLower(xForwardedFor), fmt.Sprintf("%s, %s", fwd, remoteIP))
  69. }
  70. } else {
  71. grpclog.Printf("invalid remote addr: %s", addr)
  72. }
  73. }
  74. if timeout != 0 {
  75. ctx, _ = context.WithTimeout(ctx, timeout)
  76. }
  77. if len(pairs) == 0 {
  78. return ctx, nil
  79. }
  80. md := metadata.Pairs(pairs...)
  81. if mux.metadataAnnotator != nil {
  82. md = metadata.Join(md, mux.metadataAnnotator(ctx, req))
  83. }
  84. return metadata.NewOutgoingContext(ctx, md), nil
  85. }
  86. // ServerMetadata consists of metadata sent from gRPC server.
  87. type ServerMetadata struct {
  88. HeaderMD metadata.MD
  89. TrailerMD metadata.MD
  90. }
  91. type serverMetadataKey struct{}
  92. // NewServerMetadataContext creates a new context with ServerMetadata
  93. func NewServerMetadataContext(ctx context.Context, md ServerMetadata) context.Context {
  94. return context.WithValue(ctx, serverMetadataKey{}, md)
  95. }
  96. // ServerMetadataFromContext returns the ServerMetadata in ctx
  97. func ServerMetadataFromContext(ctx context.Context) (md ServerMetadata, ok bool) {
  98. md, ok = ctx.Value(serverMetadataKey{}).(ServerMetadata)
  99. return
  100. }
  101. func timeoutDecode(s string) (time.Duration, error) {
  102. size := len(s)
  103. if size < 2 {
  104. return 0, fmt.Errorf("timeout string is too short: %q", s)
  105. }
  106. d, ok := timeoutUnitToDuration(s[size-1])
  107. if !ok {
  108. return 0, fmt.Errorf("timeout unit is not recognized: %q", s)
  109. }
  110. t, err := strconv.ParseInt(s[:size-1], 10, 64)
  111. if err != nil {
  112. return 0, err
  113. }
  114. return d * time.Duration(t), nil
  115. }
  116. func timeoutUnitToDuration(u uint8) (d time.Duration, ok bool) {
  117. switch u {
  118. case 'H':
  119. return time.Hour, true
  120. case 'M':
  121. return time.Minute, true
  122. case 'S':
  123. return time.Second, true
  124. case 'm':
  125. return time.Millisecond, true
  126. case 'u':
  127. return time.Microsecond, true
  128. case 'n':
  129. return time.Nanosecond, true
  130. default:
  131. }
  132. return
  133. }
  134. // isPermanentHTTPHeader checks whether hdr belongs to the list of
  135. // permenant request headers maintained by IANA.
  136. // http://www.iana.org/assignments/message-headers/message-headers.xml
  137. func isPermanentHTTPHeader(hdr string) bool {
  138. switch hdr {
  139. case
  140. "Accept",
  141. "Accept-Charset",
  142. "Accept-Language",
  143. "Accept-Ranges",
  144. "Authorization",
  145. "Cache-Control",
  146. "Content-Type",
  147. "Cookie",
  148. "Date",
  149. "Expect",
  150. "From",
  151. "Host",
  152. "If-Match",
  153. "If-Modified-Since",
  154. "If-None-Match",
  155. "If-Schedule-Tag-Match",
  156. "If-Unmodified-Since",
  157. "Max-Forwards",
  158. "Origin",
  159. "Pragma",
  160. "Referer",
  161. "User-Agent",
  162. "Via",
  163. "Warning":
  164. return true
  165. }
  166. return false
  167. }