3 * Copyright 2014 gRPC authors.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
33 "github.com/golang/protobuf/proto"
34 "golang.org/x/net/http2"
35 "golang.org/x/net/http2/hpack"
36 spb "google.golang.org/genproto/googleapis/rpc/status"
37 "google.golang.org/grpc/codes"
38 "google.golang.org/grpc/status"
42 // http2MaxFrameLen specifies the max length of a HTTP2 frame.
43 http2MaxFrameLen = 16384 // 16KB frame
44 // http://http2.github.io/http2-spec/#SettingValues
45 http2InitHeaderTableSize = 4096
46 // http2IOBufSize specifies the buffer size for sending frames.
47 defaultWriteBufSize = 32 * 1024
48 defaultReadBufSize = 32 * 1024
52 clientPreface = []byte(http2.ClientPreface)
53 http2ErrConvTab = map[http2.ErrCode]codes.Code{
54 http2.ErrCodeNo: codes.Internal,
55 http2.ErrCodeProtocol: codes.Internal,
56 http2.ErrCodeInternal: codes.Internal,
57 http2.ErrCodeFlowControl: codes.ResourceExhausted,
58 http2.ErrCodeSettingsTimeout: codes.Internal,
59 http2.ErrCodeStreamClosed: codes.Internal,
60 http2.ErrCodeFrameSize: codes.Internal,
61 http2.ErrCodeRefusedStream: codes.Unavailable,
62 http2.ErrCodeCancel: codes.Canceled,
63 http2.ErrCodeCompression: codes.Internal,
64 http2.ErrCodeConnect: codes.Internal,
65 http2.ErrCodeEnhanceYourCalm: codes.ResourceExhausted,
66 http2.ErrCodeInadequateSecurity: codes.PermissionDenied,
67 http2.ErrCodeHTTP11Required: codes.FailedPrecondition,
69 statusCodeConvTab = map[codes.Code]http2.ErrCode{
70 codes.Internal: http2.ErrCodeInternal,
71 codes.Canceled: http2.ErrCodeCancel,
72 codes.Unavailable: http2.ErrCodeRefusedStream,
73 codes.ResourceExhausted: http2.ErrCodeEnhanceYourCalm,
74 codes.PermissionDenied: http2.ErrCodeInadequateSecurity,
76 httpStatusConvTab = map[int]codes.Code{
77 // 400 Bad Request - INTERNAL.
78 http.StatusBadRequest: codes.Internal,
79 // 401 Unauthorized - UNAUTHENTICATED.
80 http.StatusUnauthorized: codes.Unauthenticated,
81 // 403 Forbidden - PERMISSION_DENIED.
82 http.StatusForbidden: codes.PermissionDenied,
83 // 404 Not Found - UNIMPLEMENTED.
84 http.StatusNotFound: codes.Unimplemented,
85 // 429 Too Many Requests - UNAVAILABLE.
86 http.StatusTooManyRequests: codes.Unavailable,
87 // 502 Bad Gateway - UNAVAILABLE.
88 http.StatusBadGateway: codes.Unavailable,
89 // 503 Service Unavailable - UNAVAILABLE.
90 http.StatusServiceUnavailable: codes.Unavailable,
91 // 504 Gateway timeout - UNAVAILABLE.
92 http.StatusGatewayTimeout: codes.Unavailable,
96 // Records the states during HPACK decoding. Must be reset once the
97 // decoding of the entire headers are finished.
98 type decodeState struct {
100 // statusGen caches the stream status received from the trailer the server
101 // sent. Client side only. Do not access directly. After all trailers are
102 // parsed, use the status method to retrieve the status.
103 statusGen *status.Status
104 // rawStatusCode and rawStatusMsg are set from the raw trailer fields and are not
105 // intended for direct access outside of parsing.
109 // Server side only fields.
111 timeout time.Duration
113 // key-value metadata map from the peer.
114 mdata map[string][]string
119 // isReservedHeader checks whether hdr belongs to HTTP2 headers
120 // reserved by gRPC protocol. Any other headers are classified as the
121 // user-specified metadata.
122 func isReservedHeader(hdr string) bool {
123 if hdr != "" && hdr[0] == ':' {
133 "grpc-status-details-bin",
141 // isWhitelistedPseudoHeader checks whether hdr belongs to HTTP2 pseudoheaders
142 // that should be propagated into metadata visible to users.
143 func isWhitelistedPseudoHeader(hdr string) bool {
152 func validContentType(t string) bool {
153 e := "application/grpc"
154 if !strings.HasPrefix(t, e) {
157 // Support variations on the content-type
158 // (e.g. "application/grpc+blah", "application/grpc;blah").
159 if len(t) > len(e) && t[len(e)] != '+' && t[len(e)] != ';' {
165 func (d *decodeState) status() *status.Status {
166 if d.statusGen == nil {
167 // No status-details were provided; generate status using code/msg.
168 d.statusGen = status.New(codes.Code(int32(*(d.rawStatusCode))), d.rawStatusMsg)
173 const binHdrSuffix = "-bin"
175 func encodeBinHeader(v []byte) string {
176 return base64.RawStdEncoding.EncodeToString(v)
179 func decodeBinHeader(v string) ([]byte, error) {
181 // Input was padded, or padding was not necessary.
182 return base64.StdEncoding.DecodeString(v)
184 return base64.RawStdEncoding.DecodeString(v)
187 func encodeMetadataHeader(k, v string) string {
188 if strings.HasSuffix(k, binHdrSuffix) {
189 return encodeBinHeader(([]byte)(v))
194 func decodeMetadataHeader(k, v string) (string, error) {
195 if strings.HasSuffix(k, binHdrSuffix) {
196 b, err := decodeBinHeader(v)
197 return string(b), err
202 func (d *decodeState) decodeResponseHeader(frame *http2.MetaHeadersFrame) error {
203 for _, hf := range frame.Fields {
204 if err := d.processHeaderField(hf); err != nil {
209 // If grpc status exists, no need to check further.
210 if d.rawStatusCode != nil || d.statusGen != nil {
214 // If grpc status doesn't exist and http status doesn't exist,
215 // then it's a malformed header.
216 if d.httpStatus == nil {
217 return streamErrorf(codes.Internal, "malformed header: doesn't contain status(gRPC or HTTP)")
220 if *(d.httpStatus) != http.StatusOK {
221 code, ok := httpStatusConvTab[*(d.httpStatus)]
225 return streamErrorf(code, http.StatusText(*(d.httpStatus)))
228 // gRPC status doesn't exist and http status is OK.
229 // Set rawStatusCode to be unknown and return nil error.
230 // So that, if the stream has ended this Unknown status
231 // will be propogated to the user.
232 // Otherwise, it will be ignored. In which case, status from
233 // a later trailer, that has StreamEnded flag set, is propogated.
234 code := int(codes.Unknown)
235 d.rawStatusCode = &code
240 func (d *decodeState) addMetadata(k, v string) {
242 d.mdata = make(map[string][]string)
244 d.mdata[k] = append(d.mdata[k], v)
247 func (d *decodeState) processHeaderField(f hpack.HeaderField) error {
250 if !validContentType(f.Value) {
251 return streamErrorf(codes.FailedPrecondition, "transport: received the unexpected content-type %q", f.Value)
253 case "grpc-encoding":
256 code, err := strconv.Atoi(f.Value)
258 return streamErrorf(codes.Internal, "transport: malformed grpc-status: %v", err)
260 d.rawStatusCode = &code
262 d.rawStatusMsg = decodeGrpcMessage(f.Value)
263 case "grpc-status-details-bin":
264 v, err := decodeBinHeader(f.Value)
266 return streamErrorf(codes.Internal, "transport: malformed grpc-status-details-bin: %v", err)
269 if err := proto.Unmarshal(v, s); err != nil {
270 return streamErrorf(codes.Internal, "transport: malformed grpc-status-details-bin: %v", err)
272 d.statusGen = status.FromProto(s)
276 if d.timeout, err = decodeTimeout(f.Value); err != nil {
277 return streamErrorf(codes.Internal, "transport: malformed time-out: %v", err)
282 code, err := strconv.Atoi(f.Value)
284 return streamErrorf(codes.Internal, "transport: malformed http-status: %v", err)
287 case "grpc-tags-bin":
288 v, err := decodeBinHeader(f.Value)
290 return streamErrorf(codes.Internal, "transport: malformed grpc-tags-bin: %v", err)
293 d.addMetadata(f.Name, string(v))
294 case "grpc-trace-bin":
295 v, err := decodeBinHeader(f.Value)
297 return streamErrorf(codes.Internal, "transport: malformed grpc-trace-bin: %v", err)
300 d.addMetadata(f.Name, string(v))
302 if isReservedHeader(f.Name) && !isWhitelistedPseudoHeader(f.Name) {
305 v, err := decodeMetadataHeader(f.Name, f.Value)
307 errorf("Failed to decode metadata header (%q, %q): %v", f.Name, f.Value, err)
310 d.addMetadata(f.Name, string(v))
315 type timeoutUnit uint8
318 hour timeoutUnit = 'H'
319 minute timeoutUnit = 'M'
320 second timeoutUnit = 'S'
321 millisecond timeoutUnit = 'm'
322 microsecond timeoutUnit = 'u'
323 nanosecond timeoutUnit = 'n'
326 func timeoutUnitToDuration(u timeoutUnit) (d time.Duration, ok bool) {
329 return time.Hour, true
331 return time.Minute, true
333 return time.Second, true
335 return time.Millisecond, true
337 return time.Microsecond, true
339 return time.Nanosecond, true
345 const maxTimeoutValue int64 = 100000000 - 1
347 // div does integer division and round-up the result. Note that this is
348 // equivalent to (d+r-1)/r but has less chance to overflow.
349 func div(d, r time.Duration) int64 {
350 if m := d % r; m > 0 {
351 return int64(d/r + 1)
356 // TODO(zhaoq): It is the simplistic and not bandwidth efficient. Improve it.
357 func encodeTimeout(t time.Duration) string {
361 if d := div(t, time.Nanosecond); d <= maxTimeoutValue {
362 return strconv.FormatInt(d, 10) + "n"
364 if d := div(t, time.Microsecond); d <= maxTimeoutValue {
365 return strconv.FormatInt(d, 10) + "u"
367 if d := div(t, time.Millisecond); d <= maxTimeoutValue {
368 return strconv.FormatInt(d, 10) + "m"
370 if d := div(t, time.Second); d <= maxTimeoutValue {
371 return strconv.FormatInt(d, 10) + "S"
373 if d := div(t, time.Minute); d <= maxTimeoutValue {
374 return strconv.FormatInt(d, 10) + "M"
376 // Note that maxTimeoutValue * time.Hour > MaxInt64.
377 return strconv.FormatInt(div(t, time.Hour), 10) + "H"
380 func decodeTimeout(s string) (time.Duration, error) {
383 return 0, fmt.Errorf("transport: timeout string is too short: %q", s)
385 unit := timeoutUnit(s[size-1])
386 d, ok := timeoutUnitToDuration(unit)
388 return 0, fmt.Errorf("transport: timeout unit is not recognized: %q", s)
390 t, err := strconv.ParseInt(s[:size-1], 10, 64)
394 return d * time.Duration(t), nil
403 // encodeGrpcMessage is used to encode status code in header field
405 // It checks to see if each individual byte in msg is an
406 // allowable byte, and then either percent encoding or passing it through.
407 // When percent encoding, the byte is converted into hexadecimal notation
408 // with a '%' prepended.
409 func encodeGrpcMessage(msg string) string {
414 for i := 0; i < lenMsg; i++ {
416 if !(c >= spaceByte && c < tildaByte && c != percentByte) {
417 return encodeGrpcMessageUnchecked(msg)
423 func encodeGrpcMessageUnchecked(msg string) string {
426 for i := 0; i < lenMsg; i++ {
428 if c >= spaceByte && c < tildaByte && c != percentByte {
431 buf.WriteString(fmt.Sprintf("%%%02X", c))
437 // decodeGrpcMessage decodes the msg encoded by encodeGrpcMessage.
438 func decodeGrpcMessage(msg string) string {
443 for i := 0; i < lenMsg; i++ {
444 if msg[i] == percentByte && i+2 < lenMsg {
445 return decodeGrpcMessageUnchecked(msg)
451 func decodeGrpcMessageUnchecked(msg string) string {
454 for i := 0; i < lenMsg; i++ {
456 if c == percentByte && i+2 < lenMsg {
457 parsed, err := strconv.ParseUint(msg[i+1:i+3], 16, 8)
461 buf.WriteByte(byte(parsed))
478 func newFramer(conn net.Conn, writeBufferSize, readBufferSize int) *framer {
480 reader: bufio.NewReaderSize(conn, readBufferSize),
481 writer: bufio.NewWriterSize(conn, writeBufferSize),
483 f.fr = http2.NewFramer(f.writer, f.reader)
484 // Opt-in to Frame reuse API on framer to reduce garbage.
485 // Frames aren't safe to read from after a subsequent call to ReadFrame.
486 f.fr.SetReuseFrames()
487 f.fr.ReadMetaHeaders = hpack.NewDecoder(http2InitHeaderTableSize, nil)