123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- /*
- *
- * Copyright 2014, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
- package transport
- import (
- "bufio"
- "fmt"
- "io"
- "net"
- "strconv"
- "strings"
- "sync/atomic"
- "time"
- "golang.org/x/net/http2"
- "golang.org/x/net/http2/hpack"
- "google.golang.org/grpc/codes"
- "google.golang.org/grpc/grpclog"
- "google.golang.org/grpc/metadata"
- )
- const (
- // The primary user agent
- primaryUA = "grpc-go/0.11"
- // http2MaxFrameLen specifies the max length of a HTTP2 frame.
- http2MaxFrameLen = 16384 // 16KB frame
- // http://http2.github.io/http2-spec/#SettingValues
- http2InitHeaderTableSize = 4096
- // http2IOBufSize specifies the buffer size for sending frames.
- http2IOBufSize = 32 * 1024
- )
- var (
- clientPreface = []byte(http2.ClientPreface)
- http2ErrConvTab = map[http2.ErrCode]codes.Code{
- http2.ErrCodeNo: codes.Internal,
- http2.ErrCodeProtocol: codes.Internal,
- http2.ErrCodeInternal: codes.Internal,
- http2.ErrCodeFlowControl: codes.ResourceExhausted,
- http2.ErrCodeSettingsTimeout: codes.Internal,
- http2.ErrCodeStreamClosed: codes.Internal,
- http2.ErrCodeFrameSize: codes.Internal,
- http2.ErrCodeRefusedStream: codes.Unavailable,
- http2.ErrCodeCancel: codes.Canceled,
- http2.ErrCodeCompression: codes.Internal,
- http2.ErrCodeConnect: codes.Internal,
- http2.ErrCodeEnhanceYourCalm: codes.ResourceExhausted,
- http2.ErrCodeInadequateSecurity: codes.PermissionDenied,
- http2.ErrCodeHTTP11Required: codes.FailedPrecondition,
- }
- statusCodeConvTab = map[codes.Code]http2.ErrCode{
- codes.Internal: http2.ErrCodeInternal,
- codes.Canceled: http2.ErrCodeCancel,
- codes.Unavailable: http2.ErrCodeRefusedStream,
- codes.ResourceExhausted: http2.ErrCodeEnhanceYourCalm,
- codes.PermissionDenied: http2.ErrCodeInadequateSecurity,
- }
- )
- // Records the states during HPACK decoding. Must be reset once the
- // decoding of the entire headers are finished.
- type decodeState struct {
- err error // first error encountered decoding
- encoding string
- // statusCode caches the stream status received from the trailer
- // the server sent. Client side only.
- statusCode codes.Code
- statusDesc string
- // Server side only fields.
- timeoutSet bool
- timeout time.Duration
- method string
- // key-value metadata map from the peer.
- mdata map[string][]string
- }
- // isReservedHeader checks whether hdr belongs to HTTP2 headers
- // reserved by gRPC protocol. Any other headers are classified as the
- // user-specified metadata.
- func isReservedHeader(hdr string) bool {
- if hdr != "" && hdr[0] == ':' {
- return true
- }
- switch hdr {
- case "content-type",
- "grpc-message-type",
- "grpc-encoding",
- "grpc-message",
- "grpc-status",
- "grpc-timeout",
- "te":
- return true
- default:
- return false
- }
- }
- func (d *decodeState) setErr(err error) {
- if d.err == nil {
- d.err = err
- }
- }
- func (d *decodeState) processHeaderField(f hpack.HeaderField) {
- switch f.Name {
- case "content-type":
- if !strings.Contains(f.Value, "application/grpc") {
- d.setErr(StreamErrorf(codes.FailedPrecondition, "transport: received the unexpected content-type %q", f.Value))
- return
- }
- case "grpc-encoding":
- d.encoding = f.Value
- case "grpc-status":
- code, err := strconv.Atoi(f.Value)
- if err != nil {
- d.setErr(StreamErrorf(codes.Internal, "transport: malformed grpc-status: %v", err))
- return
- }
- d.statusCode = codes.Code(code)
- case "grpc-message":
- d.statusDesc = f.Value
- case "grpc-timeout":
- d.timeoutSet = true
- var err error
- d.timeout, err = timeoutDecode(f.Value)
- if err != nil {
- d.setErr(StreamErrorf(codes.Internal, "transport: malformed time-out: %v", err))
- return
- }
- case ":path":
- d.method = f.Value
- default:
- if !isReservedHeader(f.Name) {
- if f.Name == "user-agent" {
- i := strings.LastIndex(f.Value, " ")
- if i == -1 {
- // There is no application user agent string being set.
- return
- }
- // Extract the application user agent string.
- f.Value = f.Value[:i]
- }
- if d.mdata == nil {
- d.mdata = make(map[string][]string)
- }
- k, v, err := metadata.DecodeKeyValue(f.Name, f.Value)
- if err != nil {
- grpclog.Printf("Failed to decode (%q, %q): %v", f.Name, f.Value, err)
- return
- }
- d.mdata[k] = append(d.mdata[k], v)
- }
- }
- }
- type timeoutUnit uint8
- const (
- hour timeoutUnit = 'H'
- minute timeoutUnit = 'M'
- second timeoutUnit = 'S'
- millisecond timeoutUnit = 'm'
- microsecond timeoutUnit = 'u'
- nanosecond timeoutUnit = 'n'
- )
- func timeoutUnitToDuration(u timeoutUnit) (d time.Duration, ok bool) {
- switch u {
- case hour:
- return time.Hour, true
- case minute:
- return time.Minute, true
- case second:
- return time.Second, true
- case millisecond:
- return time.Millisecond, true
- case microsecond:
- return time.Microsecond, true
- case nanosecond:
- return time.Nanosecond, true
- default:
- }
- return
- }
- const maxTimeoutValue int64 = 100000000 - 1
- // div does integer division and round-up the result. Note that this is
- // equivalent to (d+r-1)/r but has less chance to overflow.
- func div(d, r time.Duration) int64 {
- if m := d % r; m > 0 {
- return int64(d/r + 1)
- }
- return int64(d / r)
- }
- // TODO(zhaoq): It is the simplistic and not bandwidth efficient. Improve it.
- func timeoutEncode(t time.Duration) string {
- if d := div(t, time.Nanosecond); d <= maxTimeoutValue {
- return strconv.FormatInt(d, 10) + "n"
- }
- if d := div(t, time.Microsecond); d <= maxTimeoutValue {
- return strconv.FormatInt(d, 10) + "u"
- }
- if d := div(t, time.Millisecond); d <= maxTimeoutValue {
- return strconv.FormatInt(d, 10) + "m"
- }
- if d := div(t, time.Second); d <= maxTimeoutValue {
- return strconv.FormatInt(d, 10) + "S"
- }
- if d := div(t, time.Minute); d <= maxTimeoutValue {
- return strconv.FormatInt(d, 10) + "M"
- }
- // Note that maxTimeoutValue * time.Hour > MaxInt64.
- return strconv.FormatInt(div(t, time.Hour), 10) + "H"
- }
- func timeoutDecode(s string) (time.Duration, error) {
- size := len(s)
- if size < 2 {
- return 0, fmt.Errorf("transport: timeout string is too short: %q", s)
- }
- unit := timeoutUnit(s[size-1])
- d, ok := timeoutUnitToDuration(unit)
- if !ok {
- return 0, fmt.Errorf("transport: timeout unit is not recognized: %q", s)
- }
- t, err := strconv.ParseInt(s[:size-1], 10, 64)
- if err != nil {
- return 0, err
- }
- return d * time.Duration(t), nil
- }
- type framer struct {
- numWriters int32
- reader io.Reader
- writer *bufio.Writer
- fr *http2.Framer
- }
- func newFramer(conn net.Conn) *framer {
- f := &framer{
- reader: bufio.NewReaderSize(conn, http2IOBufSize),
- writer: bufio.NewWriterSize(conn, http2IOBufSize),
- }
- f.fr = http2.NewFramer(f.writer, f.reader)
- f.fr.ReadMetaHeaders = hpack.NewDecoder(http2InitHeaderTableSize, nil)
- return f
- }
- func (f *framer) adjustNumWriters(i int32) int32 {
- return atomic.AddInt32(&f.numWriters, i)
- }
- // The following writeXXX functions can only be called when the caller gets
- // unblocked from writableChan channel (i.e., owns the privilege to write).
- func (f *framer) writeContinuation(forceFlush bool, streamID uint32, endHeaders bool, headerBlockFragment []byte) error {
- if err := f.fr.WriteContinuation(streamID, endHeaders, headerBlockFragment); err != nil {
- return err
- }
- if forceFlush {
- return f.writer.Flush()
- }
- return nil
- }
- func (f *framer) writeData(forceFlush bool, streamID uint32, endStream bool, data []byte) error {
- if err := f.fr.WriteData(streamID, endStream, data); err != nil {
- return err
- }
- if forceFlush {
- return f.writer.Flush()
- }
- return nil
- }
- func (f *framer) writeGoAway(forceFlush bool, maxStreamID uint32, code http2.ErrCode, debugData []byte) error {
- if err := f.fr.WriteGoAway(maxStreamID, code, debugData); err != nil {
- return err
- }
- if forceFlush {
- return f.writer.Flush()
- }
- return nil
- }
- func (f *framer) writeHeaders(forceFlush bool, p http2.HeadersFrameParam) error {
- if err := f.fr.WriteHeaders(p); err != nil {
- return err
- }
- if forceFlush {
- return f.writer.Flush()
- }
- return nil
- }
- func (f *framer) writePing(forceFlush, ack bool, data [8]byte) error {
- if err := f.fr.WritePing(ack, data); err != nil {
- return err
- }
- if forceFlush {
- return f.writer.Flush()
- }
- return nil
- }
- func (f *framer) writePriority(forceFlush bool, streamID uint32, p http2.PriorityParam) error {
- if err := f.fr.WritePriority(streamID, p); err != nil {
- return err
- }
- if forceFlush {
- return f.writer.Flush()
- }
- return nil
- }
- func (f *framer) writePushPromise(forceFlush bool, p http2.PushPromiseParam) error {
- if err := f.fr.WritePushPromise(p); err != nil {
- return err
- }
- if forceFlush {
- return f.writer.Flush()
- }
- return nil
- }
- func (f *framer) writeRSTStream(forceFlush bool, streamID uint32, code http2.ErrCode) error {
- if err := f.fr.WriteRSTStream(streamID, code); err != nil {
- return err
- }
- if forceFlush {
- return f.writer.Flush()
- }
- return nil
- }
- func (f *framer) writeSettings(forceFlush bool, settings ...http2.Setting) error {
- if err := f.fr.WriteSettings(settings...); err != nil {
- return err
- }
- if forceFlush {
- return f.writer.Flush()
- }
- return nil
- }
- func (f *framer) writeSettingsAck(forceFlush bool) error {
- if err := f.fr.WriteSettingsAck(); err != nil {
- return err
- }
- if forceFlush {
- return f.writer.Flush()
- }
- return nil
- }
- func (f *framer) writeWindowUpdate(forceFlush bool, streamID, incr uint32) error {
- if err := f.fr.WriteWindowUpdate(streamID, incr); err != nil {
- return err
- }
- if forceFlush {
- return f.writer.Flush()
- }
- return nil
- }
- func (f *framer) flushWrite() error {
- return f.writer.Flush()
- }
- func (f *framer) readFrame() (http2.Frame, error) {
- return f.fr.ReadFrame()
- }
- func (f *framer) errorDetail() error {
- return f.fr.ErrorDetail()
- }
|