aboutsummaryrefslogtreecommitdiff
path: root/vendor/google.golang.org/grpc/transport
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/google.golang.org/grpc/transport')
-rw-r--r--vendor/google.golang.org/grpc/transport/control.go210
-rw-r--r--vendor/google.golang.org/grpc/transport/handler_server.go393
-rw-r--r--vendor/google.golang.org/grpc/transport/http2_client.go953
-rw-r--r--vendor/google.golang.org/grpc/transport/http2_server.go743
-rw-r--r--vendor/google.golang.org/grpc/transport/http_util.go422
-rw-r--r--vendor/google.golang.org/grpc/transport/transport.go513
6 files changed, 3234 insertions, 0 deletions
diff --git a/vendor/google.golang.org/grpc/transport/control.go b/vendor/google.golang.org/grpc/transport/control.go
new file mode 100644
index 0000000..7e9bdf3
--- /dev/null
+++ b/vendor/google.golang.org/grpc/transport/control.go
@@ -0,0 +1,210 @@
+/*
+ *
+ * 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 (
+ "fmt"
+ "sync"
+
+ "golang.org/x/net/http2"
+)
+
+const (
+ // The default value of flow control window size in HTTP2 spec.
+ defaultWindowSize = 65535
+ // The initial window size for flow control.
+ initialWindowSize = defaultWindowSize // for an RPC
+ initialConnWindowSize = defaultWindowSize * 16 // for a connection
+)
+
+// The following defines various control items which could flow through
+// the control buffer of transport. They represent different aspects of
+// control tasks, e.g., flow control, settings, streaming resetting, etc.
+type windowUpdate struct {
+ streamID uint32
+ increment uint32
+}
+
+func (*windowUpdate) item() {}
+
+type settings struct {
+ ack bool
+ ss []http2.Setting
+}
+
+func (*settings) item() {}
+
+type resetStream struct {
+ streamID uint32
+ code http2.ErrCode
+}
+
+func (*resetStream) item() {}
+
+type flushIO struct {
+}
+
+func (*flushIO) item() {}
+
+type ping struct {
+ ack bool
+ data [8]byte
+}
+
+func (*ping) item() {}
+
+// quotaPool is a pool which accumulates the quota and sends it to acquire()
+// when it is available.
+type quotaPool struct {
+ c chan int
+
+ mu sync.Mutex
+ quota int
+}
+
+// newQuotaPool creates a quotaPool which has quota q available to consume.
+func newQuotaPool(q int) *quotaPool {
+ qb := &quotaPool{
+ c: make(chan int, 1),
+ }
+ if q > 0 {
+ qb.c <- q
+ } else {
+ qb.quota = q
+ }
+ return qb
+}
+
+// add adds n to the available quota and tries to send it on acquire.
+func (qb *quotaPool) add(n int) {
+ qb.mu.Lock()
+ defer qb.mu.Unlock()
+ qb.quota += n
+ if qb.quota <= 0 {
+ return
+ }
+ select {
+ case qb.c <- qb.quota:
+ qb.quota = 0
+ default:
+ }
+}
+
+// cancel cancels the pending quota sent on acquire, if any.
+func (qb *quotaPool) cancel() {
+ qb.mu.Lock()
+ defer qb.mu.Unlock()
+ select {
+ case n := <-qb.c:
+ qb.quota += n
+ default:
+ }
+}
+
+// reset cancels the pending quota sent on acquired, incremented by v and sends
+// it back on acquire.
+func (qb *quotaPool) reset(v int) {
+ qb.mu.Lock()
+ defer qb.mu.Unlock()
+ select {
+ case n := <-qb.c:
+ qb.quota += n
+ default:
+ }
+ qb.quota += v
+ if qb.quota <= 0 {
+ return
+ }
+ select {
+ case qb.c <- qb.quota:
+ qb.quota = 0
+ default:
+ }
+}
+
+// acquire returns the channel on which available quota amounts are sent.
+func (qb *quotaPool) acquire() <-chan int {
+ return qb.c
+}
+
+// inFlow deals with inbound flow control
+type inFlow struct {
+ // The inbound flow control limit for pending data.
+ limit uint32
+
+ mu sync.Mutex
+ // pendingData is the overall data which have been received but not been
+ // consumed by applications.
+ pendingData uint32
+ // The amount of data the application has consumed but grpc has not sent
+ // window update for them. Used to reduce window update frequency.
+ pendingUpdate uint32
+}
+
+// onData is invoked when some data frame is received. It updates pendingData.
+func (f *inFlow) onData(n uint32) error {
+ f.mu.Lock()
+ defer f.mu.Unlock()
+ f.pendingData += n
+ if f.pendingData+f.pendingUpdate > f.limit {
+ return fmt.Errorf("received %d-bytes data exceeding the limit %d bytes", f.pendingData+f.pendingUpdate, f.limit)
+ }
+ return nil
+}
+
+// onRead is invoked when the application reads the data. It returns the window size
+// to be sent to the peer.
+func (f *inFlow) onRead(n uint32) uint32 {
+ f.mu.Lock()
+ defer f.mu.Unlock()
+ if f.pendingData == 0 {
+ return 0
+ }
+ f.pendingData -= n
+ f.pendingUpdate += n
+ if f.pendingUpdate >= f.limit/4 {
+ wu := f.pendingUpdate
+ f.pendingUpdate = 0
+ return wu
+ }
+ return 0
+}
+
+func (f *inFlow) resetPendingData() uint32 {
+ f.mu.Lock()
+ defer f.mu.Unlock()
+ n := f.pendingData
+ f.pendingData = 0
+ return n
+}
diff --git a/vendor/google.golang.org/grpc/transport/handler_server.go b/vendor/google.golang.org/grpc/transport/handler_server.go
new file mode 100644
index 0000000..7a4ae07
--- /dev/null
+++ b/vendor/google.golang.org/grpc/transport/handler_server.go
@@ -0,0 +1,393 @@
+/*
+ * Copyright 2016, 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.
+ *
+ */
+
+// This file is the implementation of a gRPC server using HTTP/2 which
+// uses the standard Go http2 Server implementation (via the
+// http.Handler interface), rather than speaking low-level HTTP/2
+// frames itself. It is the implementation of *grpc.Server.ServeHTTP.
+
+package transport
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "net/http"
+ "strings"
+ "sync"
+ "time"
+
+ "golang.org/x/net/context"
+ "golang.org/x/net/http2"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/credentials"
+ "google.golang.org/grpc/metadata"
+ "google.golang.org/grpc/peer"
+)
+
+// NewServerHandlerTransport returns a ServerTransport handling gRPC
+// from inside an http.Handler. It requires that the http Server
+// supports HTTP/2.
+func NewServerHandlerTransport(w http.ResponseWriter, r *http.Request) (ServerTransport, error) {
+ if r.ProtoMajor != 2 {
+ return nil, errors.New("gRPC requires HTTP/2")
+ }
+ if r.Method != "POST" {
+ return nil, errors.New("invalid gRPC request method")
+ }
+ if !strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
+ return nil, errors.New("invalid gRPC request content-type")
+ }
+ if _, ok := w.(http.Flusher); !ok {
+ return nil, errors.New("gRPC requires a ResponseWriter supporting http.Flusher")
+ }
+ if _, ok := w.(http.CloseNotifier); !ok {
+ return nil, errors.New("gRPC requires a ResponseWriter supporting http.CloseNotifier")
+ }
+
+ st := &serverHandlerTransport{
+ rw: w,
+ req: r,
+ closedCh: make(chan struct{}),
+ writes: make(chan func()),
+ }
+
+ if v := r.Header.Get("grpc-timeout"); v != "" {
+ to, err := timeoutDecode(v)
+ if err != nil {
+ return nil, StreamErrorf(codes.Internal, "malformed time-out: %v", err)
+ }
+ st.timeoutSet = true
+ st.timeout = to
+ }
+
+ var metakv []string
+ if r.Host != "" {
+ metakv = append(metakv, ":authority", r.Host)
+ }
+ for k, vv := range r.Header {
+ k = strings.ToLower(k)
+ if isReservedHeader(k) && !isWhitelistedPseudoHeader(k){
+ continue
+ }
+ for _, v := range vv {
+ if k == "user-agent" {
+ // user-agent is special. Copying logic of http_util.go.
+ if i := strings.LastIndex(v, " "); i == -1 {
+ // There is no application user agent string being set
+ continue
+ } else {
+ v = v[:i]
+ }
+ }
+ metakv = append(metakv, k, v)
+ }
+ }
+ st.headerMD = metadata.Pairs(metakv...)
+
+ return st, nil
+}
+
+// serverHandlerTransport is an implementation of ServerTransport
+// which replies to exactly one gRPC request (exactly one HTTP request),
+// using the net/http.Handler interface. This http.Handler is guaranteed
+// at this point to be speaking over HTTP/2, so it's able to speak valid
+// gRPC.
+type serverHandlerTransport struct {
+ rw http.ResponseWriter
+ req *http.Request
+ timeoutSet bool
+ timeout time.Duration
+ didCommonHeaders bool
+
+ headerMD metadata.MD
+
+ closeOnce sync.Once
+ closedCh chan struct{} // closed on Close
+
+ // writes is a channel of code to run serialized in the
+ // ServeHTTP (HandleStreams) goroutine. The channel is closed
+ // when WriteStatus is called.
+ writes chan func()
+}
+
+func (ht *serverHandlerTransport) Close() error {
+ ht.closeOnce.Do(ht.closeCloseChanOnce)
+ return nil
+}
+
+func (ht *serverHandlerTransport) closeCloseChanOnce() { close(ht.closedCh) }
+
+func (ht *serverHandlerTransport) RemoteAddr() net.Addr { return strAddr(ht.req.RemoteAddr) }
+
+// strAddr is a net.Addr backed by either a TCP "ip:port" string, or
+// the empty string if unknown.
+type strAddr string
+
+func (a strAddr) Network() string {
+ if a != "" {
+ // Per the documentation on net/http.Request.RemoteAddr, if this is
+ // set, it's set to the IP:port of the peer (hence, TCP):
+ // https://golang.org/pkg/net/http/#Request
+ //
+ // If we want to support Unix sockets later, we can
+ // add our own grpc-specific convention within the
+ // grpc codebase to set RemoteAddr to a different
+ // format, or probably better: we can attach it to the
+ // context and use that from serverHandlerTransport.RemoteAddr.
+ return "tcp"
+ }
+ return ""
+}
+
+func (a strAddr) String() string { return string(a) }
+
+// do runs fn in the ServeHTTP goroutine.
+func (ht *serverHandlerTransport) do(fn func()) error {
+ select {
+ case ht.writes <- fn:
+ return nil
+ case <-ht.closedCh:
+ return ErrConnClosing
+ }
+}
+
+func (ht *serverHandlerTransport) WriteStatus(s *Stream, statusCode codes.Code, statusDesc string) error {
+ err := ht.do(func() {
+ ht.writeCommonHeaders(s)
+
+ // And flush, in case no header or body has been sent yet.
+ // This forces a separation of headers and trailers if this is the
+ // first call (for example, in end2end tests's TestNoService).
+ ht.rw.(http.Flusher).Flush()
+
+ h := ht.rw.Header()
+ h.Set("Grpc-Status", fmt.Sprintf("%d", statusCode))
+ if statusDesc != "" {
+ h.Set("Grpc-Message", statusDesc)
+ }
+ if md := s.Trailer(); len(md) > 0 {
+ for k, vv := range md {
+ // Clients don't tolerate reading restricted headers after some non restricted ones were sent.
+ if isReservedHeader(k) {
+ continue
+ }
+ for _, v := range vv {
+ // http2 ResponseWriter mechanism to
+ // send undeclared Trailers after the
+ // headers have possibly been written.
+ h.Add(http2.TrailerPrefix+k, v)
+ }
+ }
+ }
+ })
+ close(ht.writes)
+ return err
+}
+
+// writeCommonHeaders sets common headers on the first write
+// call (Write, WriteHeader, or WriteStatus).
+func (ht *serverHandlerTransport) writeCommonHeaders(s *Stream) {
+ if ht.didCommonHeaders {
+ return
+ }
+ ht.didCommonHeaders = true
+
+ h := ht.rw.Header()
+ h["Date"] = nil // suppress Date to make tests happy; TODO: restore
+ h.Set("Content-Type", "application/grpc")
+
+ // Predeclare trailers we'll set later in WriteStatus (after the body).
+ // This is a SHOULD in the HTTP RFC, and the way you add (known)
+ // Trailers per the net/http.ResponseWriter contract.
+ // See https://golang.org/pkg/net/http/#ResponseWriter
+ // and https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
+ h.Add("Trailer", "Grpc-Status")
+ h.Add("Trailer", "Grpc-Message")
+
+ if s.sendCompress != "" {
+ h.Set("Grpc-Encoding", s.sendCompress)
+ }
+}
+
+func (ht *serverHandlerTransport) Write(s *Stream, data []byte, opts *Options) error {
+ return ht.do(func() {
+ ht.writeCommonHeaders(s)
+ ht.rw.Write(data)
+ if !opts.Delay {
+ ht.rw.(http.Flusher).Flush()
+ }
+ })
+}
+
+func (ht *serverHandlerTransport) WriteHeader(s *Stream, md metadata.MD) error {
+ return ht.do(func() {
+ ht.writeCommonHeaders(s)
+ h := ht.rw.Header()
+ for k, vv := range md {
+ // Clients don't tolerate reading restricted headers after some non restricted ones were sent.
+ if isReservedHeader(k) {
+ continue
+ }
+ for _, v := range vv {
+ h.Add(k, v)
+ }
+ }
+ ht.rw.WriteHeader(200)
+ ht.rw.(http.Flusher).Flush()
+ })
+}
+
+func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream)) {
+ // With this transport type there will be exactly 1 stream: this HTTP request.
+
+ var ctx context.Context
+ var cancel context.CancelFunc
+ if ht.timeoutSet {
+ ctx, cancel = context.WithTimeout(context.Background(), ht.timeout)
+ } else {
+ ctx, cancel = context.WithCancel(context.Background())
+ }
+
+ // requestOver is closed when either the request's context is done
+ // or the status has been written via WriteStatus.
+ requestOver := make(chan struct{})
+
+ // clientGone receives a single value if peer is gone, either
+ // because the underlying connection is dead or because the
+ // peer sends an http2 RST_STREAM.
+ clientGone := ht.rw.(http.CloseNotifier).CloseNotify()
+ go func() {
+ select {
+ case <-requestOver:
+ return
+ case <-ht.closedCh:
+ case <-clientGone:
+ }
+ cancel()
+ }()
+
+ req := ht.req
+
+ s := &Stream{
+ id: 0, // irrelevant
+ windowHandler: func(int) {}, // nothing
+ cancel: cancel,
+ buf: newRecvBuffer(),
+ st: ht,
+ method: req.URL.Path,
+ recvCompress: req.Header.Get("grpc-encoding"),
+ }
+ pr := &peer.Peer{
+ Addr: ht.RemoteAddr(),
+ }
+ if req.TLS != nil {
+ pr.AuthInfo = credentials.TLSInfo{*req.TLS}
+ }
+ ctx = metadata.NewContext(ctx, ht.headerMD)
+ ctx = peer.NewContext(ctx, pr)
+ s.ctx = newContextWithStream(ctx, s)
+ s.dec = &recvBufferReader{ctx: s.ctx, recv: s.buf}
+
+ // readerDone is closed when the Body.Read-ing goroutine exits.
+ readerDone := make(chan struct{})
+ go func() {
+ defer close(readerDone)
+
+ // TODO: minimize garbage, optimize recvBuffer code/ownership
+ const readSize = 8196
+ for buf := make([]byte, readSize); ; {
+ n, err := req.Body.Read(buf)
+ if n > 0 {
+ s.buf.put(&recvMsg{data: buf[:n:n]})
+ buf = buf[n:]
+ }
+ if err != nil {
+ s.buf.put(&recvMsg{err: mapRecvMsgError(err)})
+ return
+ }
+ if len(buf) == 0 {
+ buf = make([]byte, readSize)
+ }
+ }
+ }()
+
+ // startStream is provided by the *grpc.Server's serveStreams.
+ // It starts a goroutine serving s and exits immediately.
+ // The goroutine that is started is the one that then calls
+ // into ht, calling WriteHeader, Write, WriteStatus, Close, etc.
+ startStream(s)
+
+ ht.runStream()
+ close(requestOver)
+
+ // Wait for reading goroutine to finish.
+ req.Body.Close()
+ <-readerDone
+}
+
+func (ht *serverHandlerTransport) runStream() {
+ for {
+ select {
+ case fn, ok := <-ht.writes:
+ if !ok {
+ return
+ }
+ fn()
+ case <-ht.closedCh:
+ return
+ }
+ }
+}
+
+// mapRecvMsgError returns the non-nil err into the appropriate
+// error value as expected by callers of *grpc.parser.recvMsg.
+// In particular, in can only be:
+// * io.EOF
+// * io.ErrUnexpectedEOF
+// * of type transport.ConnectionError
+// * of type transport.StreamError
+func mapRecvMsgError(err error) error {
+ if err == io.EOF || err == io.ErrUnexpectedEOF {
+ return err
+ }
+ if se, ok := err.(http2.StreamError); ok {
+ if code, ok := http2ErrConvTab[se.Code]; ok {
+ return StreamError{
+ Code: code,
+ Desc: se.Error(),
+ }
+ }
+ }
+ return ConnectionError{Desc: err.Error()}
+}
diff --git a/vendor/google.golang.org/grpc/transport/http2_client.go b/vendor/google.golang.org/grpc/transport/http2_client.go
new file mode 100644
index 0000000..e624f8d
--- /dev/null
+++ b/vendor/google.golang.org/grpc/transport/http2_client.go
@@ -0,0 +1,953 @@
+/*
+ *
+ * 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 (
+ "bytes"
+ "io"
+ "math"
+ "net"
+ "strings"
+ "sync"
+ "time"
+
+ "golang.org/x/net/context"
+ "golang.org/x/net/http2"
+ "golang.org/x/net/http2/hpack"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/credentials"
+ "google.golang.org/grpc/grpclog"
+ "google.golang.org/grpc/metadata"
+ "google.golang.org/grpc/peer"
+)
+
+// http2Client implements the ClientTransport interface with HTTP2.
+type http2Client struct {
+ target string // server name/addr
+ userAgent string
+ conn net.Conn // underlying communication channel
+ authInfo credentials.AuthInfo // auth info about the connection
+ nextID uint32 // the next stream ID to be used
+
+ // writableChan synchronizes write access to the transport.
+ // A writer acquires the write lock by sending a value on writableChan
+ // and releases it by receiving from writableChan.
+ writableChan chan int
+ // shutdownChan is closed when Close is called.
+ // Blocking operations should select on shutdownChan to avoid
+ // blocking forever after Close.
+ // TODO(zhaoq): Maybe have a channel context?
+ shutdownChan chan struct{}
+ // errorChan is closed to notify the I/O error to the caller.
+ errorChan chan struct{}
+
+ framer *framer
+ hBuf *bytes.Buffer // the buffer for HPACK encoding
+ hEnc *hpack.Encoder // HPACK encoder
+
+ // controlBuf delivers all the control related tasks (e.g., window
+ // updates, reset streams, and various settings) to the controller.
+ controlBuf *recvBuffer
+ fc *inFlow
+ // sendQuotaPool provides flow control to outbound message.
+ sendQuotaPool *quotaPool
+ // streamsQuota limits the max number of concurrent streams.
+ streamsQuota *quotaPool
+
+ // The scheme used: https if TLS is on, http otherwise.
+ scheme string
+
+ authCreds []credentials.Credentials
+
+ mu sync.Mutex // guard the following variables
+ state transportState // the state of underlying connection
+ activeStreams map[uint32]*Stream
+ // The max number of concurrent streams
+ maxStreams int
+ // the per-stream outbound flow control window size set by the peer.
+ streamSendQuota uint32
+}
+
+// newHTTP2Client constructs a connected ClientTransport to addr based on HTTP2
+// and starts to receive messages on it. Non-nil error returns if construction
+// fails.
+func newHTTP2Client(addr string, opts *ConnectOptions) (_ ClientTransport, err error) {
+ if opts.Dialer == nil {
+ // Set the default Dialer.
+ opts.Dialer = func(addr string, timeout time.Duration) (net.Conn, error) {
+ return net.DialTimeout("tcp", addr, timeout)
+ }
+ }
+ scheme := "http"
+ startT := time.Now()
+ timeout := opts.Timeout
+ conn, connErr := opts.Dialer(addr, timeout)
+ if connErr != nil {
+ return nil, ConnectionErrorf("transport: %v", connErr)
+ }
+ var authInfo credentials.AuthInfo
+ for _, c := range opts.AuthOptions {
+ if ccreds, ok := c.(credentials.TransportAuthenticator); ok {
+ scheme = "https"
+ // TODO(zhaoq): Now the first TransportAuthenticator is used if there are
+ // multiple ones provided. Revisit this if it is not appropriate. Probably
+ // place the ClientTransport construction into a separate function to make
+ // things clear.
+ if timeout > 0 {
+ timeout -= time.Since(startT)
+ }
+ conn, authInfo, connErr = ccreds.ClientHandshake(addr, conn, timeout)
+ break
+ }
+ }
+ if connErr != nil {
+ return nil, ConnectionErrorf("transport: %v", connErr)
+ }
+ defer func() {
+ if err != nil {
+ conn.Close()
+ }
+ }()
+ ua := primaryUA
+ if opts.UserAgent != "" {
+ ua = opts.UserAgent + " " + ua
+ }
+ var buf bytes.Buffer
+ t := &http2Client{
+ target: addr,
+ userAgent: ua,
+ conn: conn,
+ authInfo: authInfo,
+ // The client initiated stream id is odd starting from 1.
+ nextID: 1,
+ writableChan: make(chan int, 1),
+ shutdownChan: make(chan struct{}),
+ errorChan: make(chan struct{}),
+ framer: newFramer(conn),
+ hBuf: &buf,
+ hEnc: hpack.NewEncoder(&buf),
+ controlBuf: newRecvBuffer(),
+ fc: &inFlow{limit: initialConnWindowSize},
+ sendQuotaPool: newQuotaPool(defaultWindowSize),
+ scheme: scheme,
+ state: reachable,
+ activeStreams: make(map[uint32]*Stream),
+ authCreds: opts.AuthOptions,
+ maxStreams: math.MaxInt32,
+ streamSendQuota: defaultWindowSize,
+ }
+ // Start the reader goroutine for incoming message. Each transport has
+ // a dedicated goroutine which reads HTTP2 frame from network. Then it
+ // dispatches the frame to the corresponding stream entity.
+ go t.reader()
+ // Send connection preface to server.
+ n, err := t.conn.Write(clientPreface)
+ if err != nil {
+ t.Close()
+ return nil, ConnectionErrorf("transport: %v", err)
+ }
+ if n != len(clientPreface) {
+ t.Close()
+ return nil, ConnectionErrorf("transport: preface mismatch, wrote %d bytes; want %d", n, len(clientPreface))
+ }
+ if initialWindowSize != defaultWindowSize {
+ err = t.framer.writeSettings(true, http2.Setting{http2.SettingInitialWindowSize, uint32(initialWindowSize)})
+ } else {
+ err = t.framer.writeSettings(true)
+ }
+ if err != nil {
+ t.Close()
+ return nil, ConnectionErrorf("transport: %v", err)
+ }
+ // Adjust the connection flow control window if needed.
+ if delta := uint32(initialConnWindowSize - defaultWindowSize); delta > 0 {
+ if err := t.framer.writeWindowUpdate(true, 0, delta); err != nil {
+ t.Close()
+ return nil, ConnectionErrorf("transport: %v", err)
+ }
+ }
+ go t.controller()
+ t.writableChan <- 0
+ return t, nil
+}
+
+func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *Stream {
+ // TODO(zhaoq): Handle uint32 overflow of Stream.id.
+ s := &Stream{
+ id: t.nextID,
+ method: callHdr.Method,
+ sendCompress: callHdr.SendCompress,
+ buf: newRecvBuffer(),
+ fc: &inFlow{limit: initialWindowSize},
+ sendQuotaPool: newQuotaPool(int(t.streamSendQuota)),
+ headerChan: make(chan struct{}),
+ }
+ t.nextID += 2
+ s.windowHandler = func(n int) {
+ t.updateWindow(s, uint32(n))
+ }
+ // Make a stream be able to cancel the pending operations by itself.
+ s.ctx, s.cancel = context.WithCancel(ctx)
+ s.dec = &recvBufferReader{
+ ctx: s.ctx,
+ recv: s.buf,
+ }
+ return s
+}
+
+// NewStream creates a stream and register it into the transport as "active"
+// streams.
+func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Stream, err error) {
+ // Record the timeout value on the context.
+ var timeout time.Duration
+ if dl, ok := ctx.Deadline(); ok {
+ timeout = dl.Sub(time.Now())
+ }
+ select {
+ case <-ctx.Done():
+ return nil, ContextErr(ctx.Err())
+ default:
+ }
+ pr := &peer.Peer{
+ Addr: t.conn.RemoteAddr(),
+ }
+ // Attach Auth info if there is any.
+ if t.authInfo != nil {
+ pr.AuthInfo = t.authInfo
+ }
+ ctx = peer.NewContext(ctx, pr)
+ authData := make(map[string]string)
+ for _, c := range t.authCreds {
+ // Construct URI required to get auth request metadata.
+ var port string
+ if pos := strings.LastIndex(t.target, ":"); pos != -1 {
+ // Omit port if it is the default one.
+ if t.target[pos+1:] != "443" {
+ port = ":" + t.target[pos+1:]
+ }
+ }
+ pos := strings.LastIndex(callHdr.Method, "/")
+ if pos == -1 {
+ return nil, StreamErrorf(codes.InvalidArgument, "transport: malformed method name: %q", callHdr.Method)
+ }
+ audience := "https://" + callHdr.Host + port + callHdr.Method[:pos]
+ data, err := c.GetRequestMetadata(ctx, audience)
+ if err != nil {
+ return nil, StreamErrorf(codes.InvalidArgument, "transport: %v", err)
+ }
+ for k, v := range data {
+ authData[k] = v
+ }
+ }
+ t.mu.Lock()
+ if t.activeStreams == nil {
+ t.mu.Unlock()
+ return nil, ErrConnClosing
+ }
+ if t.state != reachable {
+ t.mu.Unlock()
+ return nil, ErrConnClosing
+ }
+ checkStreamsQuota := t.streamsQuota != nil
+ t.mu.Unlock()
+ if checkStreamsQuota {
+ sq, err := wait(ctx, t.shutdownChan, t.streamsQuota.acquire())
+ if err != nil {
+ return nil, err
+ }
+ // Returns the quota balance back.
+ if sq > 1 {
+ t.streamsQuota.add(sq - 1)
+ }
+ }
+ if _, err := wait(ctx, t.shutdownChan, t.writableChan); err != nil {
+ // Return the quota back now because there is no stream returned to the caller.
+ if _, ok := err.(StreamError); ok && checkStreamsQuota {
+ t.streamsQuota.add(1)
+ }
+ return nil, err
+ }
+ t.mu.Lock()
+ if t.state != reachable {
+ t.mu.Unlock()
+ return nil, ErrConnClosing
+ }
+ s := t.newStream(ctx, callHdr)
+ t.activeStreams[s.id] = s
+
+ // This stream is not counted when applySetings(...) initialize t.streamsQuota.
+ // Reset t.streamsQuota to the right value.
+ var reset bool
+ if !checkStreamsQuota && t.streamsQuota != nil {
+ reset = true
+ }
+ t.mu.Unlock()
+ if reset {
+ t.streamsQuota.reset(-1)
+ }
+
+ // HPACK encodes various headers. Note that once WriteField(...) is
+ // called, the corresponding headers/continuation frame has to be sent
+ // because hpack.Encoder is stateful.
+ t.hBuf.Reset()
+ t.hEnc.WriteField(hpack.HeaderField{Name: ":method", Value: "POST"})
+ t.hEnc.WriteField(hpack.HeaderField{Name: ":scheme", Value: t.scheme})
+ t.hEnc.WriteField(hpack.HeaderField{Name: ":path", Value: callHdr.Method})
+ t.hEnc.WriteField(hpack.HeaderField{Name: ":authority", Value: callHdr.Host})
+ t.hEnc.WriteField(hpack.HeaderField{Name: "content-type", Value: "application/grpc"})
+ t.hEnc.WriteField(hpack.HeaderField{Name: "user-agent", Value: t.userAgent})
+ t.hEnc.WriteField(hpack.HeaderField{Name: "te", Value: "trailers"})
+
+ if callHdr.SendCompress != "" {
+ t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-encoding", Value: callHdr.SendCompress})
+ }
+ if timeout > 0 {
+ t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-timeout", Value: timeoutEncode(timeout)})
+ }
+ for k, v := range authData {
+ // Capital header names are illegal in HTTP/2.
+ k = strings.ToLower(k)
+ t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: v})
+ }
+ var (
+ hasMD bool
+ endHeaders bool
+ )
+ if md, ok := metadata.FromContext(ctx); ok {
+ hasMD = true
+ for k, v := range md {
+ // HTTP doesn't allow you to set pseudoheaders after non pseudoheaders were set.
+ if isReservedHeader(k) {
+ continue
+ }
+ for _, entry := range v {
+ t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry})
+ }
+ }
+ }
+ first := true
+ // Sends the headers in a single batch even when they span multiple frames.
+ for !endHeaders {
+ size := t.hBuf.Len()
+ if size > http2MaxFrameLen {
+ size = http2MaxFrameLen
+ } else {
+ endHeaders = true
+ }
+ var flush bool
+ if endHeaders && (hasMD || callHdr.Flush) {
+ flush = true
+ }
+ if first {
+ // Sends a HeadersFrame to server to start a new stream.
+ p := http2.HeadersFrameParam{
+ StreamID: s.id,
+ BlockFragment: t.hBuf.Next(size),
+ EndStream: false,
+ EndHeaders: endHeaders,
+ }
+ // Do a force flush for the buffered frames iff it is the last headers frame
+ // and there is header metadata to be sent. Otherwise, there is flushing until
+ // the corresponding data frame is written.
+ err = t.framer.writeHeaders(flush, p)
+ first = false
+ } else {
+ // Sends Continuation frames for the leftover headers.
+ err = t.framer.writeContinuation(flush, s.id, endHeaders, t.hBuf.Next(size))
+ }
+ if err != nil {
+ t.notifyError(err)
+ return nil, ConnectionErrorf("transport: %v", err)
+ }
+ }
+ t.writableChan <- 0
+ return s, nil
+}
+
+// CloseStream clears the footprint of a stream when the stream is not needed any more.
+// This must not be executed in reader's goroutine.
+func (t *http2Client) CloseStream(s *Stream, err error) {
+ var updateStreams bool
+ t.mu.Lock()
+ if t.activeStreams == nil {
+ t.mu.Unlock()
+ return
+ }
+ if t.streamsQuota != nil {
+ updateStreams = true
+ }
+ if t.state == draining && len(t.activeStreams) == 1 {
+ // The transport is draining and s is the last live stream on t.
+ t.mu.Unlock()
+ t.Close()
+ return
+ }
+ delete(t.activeStreams, s.id)
+ t.mu.Unlock()
+ if updateStreams {
+ t.streamsQuota.add(1)
+ }
+ // In case stream sending and receiving are invoked in separate
+ // goroutines (e.g., bi-directional streaming), the caller needs
+ // to call cancel on the stream to interrupt the blocking on
+ // other goroutines.
+ s.cancel()
+ s.mu.Lock()
+ if q := s.fc.resetPendingData(); q > 0 {
+ if n := t.fc.onRead(q); n > 0 {
+ t.controlBuf.put(&windowUpdate{0, n})
+ }
+ }
+ if s.state == streamDone {
+ s.mu.Unlock()
+ return
+ }
+ if !s.headerDone {
+ close(s.headerChan)
+ s.headerDone = true
+ }
+ s.state = streamDone
+ s.mu.Unlock()
+ if _, ok := err.(StreamError); ok {
+ t.controlBuf.put(&resetStream{s.id, http2.ErrCodeCancel})
+ }
+}
+
+// Close kicks off the shutdown process of the transport. This should be called
+// only once on a transport. Once it is called, the transport should not be
+// accessed any more.
+func (t *http2Client) Close() (err error) {
+ t.mu.Lock()
+ if t.state == reachable {
+ close(t.errorChan)
+ }
+ if t.state == closing {
+ t.mu.Unlock()
+ return
+ }
+ t.state = closing
+ t.mu.Unlock()
+ close(t.shutdownChan)
+ err = t.conn.Close()
+ t.mu.Lock()
+ streams := t.activeStreams
+ t.activeStreams = nil
+ t.mu.Unlock()
+ // Notify all active streams.
+ for _, s := range streams {
+ s.mu.Lock()
+ if !s.headerDone {
+ close(s.headerChan)
+ s.headerDone = true
+ }
+ s.mu.Unlock()
+ s.write(recvMsg{err: ErrConnClosing})
+ }
+ return
+}
+
+func (t *http2Client) GracefulClose() error {
+ t.mu.Lock()
+ if t.state == closing {
+ t.mu.Unlock()
+ return nil
+ }
+ if t.state == draining {
+ t.mu.Unlock()
+ return nil
+ }
+ t.state = draining
+ active := len(t.activeStreams)
+ t.mu.Unlock()
+ if active == 0 {
+ return t.Close()
+ }
+ return nil
+}
+
+// Write formats the data into HTTP2 data frame(s) and sends it out. The caller
+// should proceed only if Write returns nil.
+// TODO(zhaoq): opts.Delay is ignored in this implementation. Support it later
+// if it improves the performance.
+func (t *http2Client) Write(s *Stream, data []byte, opts *Options) error {
+ r := bytes.NewBuffer(data)
+ for {
+ var p []byte
+ if r.Len() > 0 {
+ size := http2MaxFrameLen
+ s.sendQuotaPool.add(0)
+ // Wait until the stream has some quota to send the data.
+ sq, err := wait(s.ctx, t.shutdownChan, s.sendQuotaPool.acquire())
+ if err != nil {
+ return err
+ }
+ t.sendQuotaPool.add(0)
+ // Wait until the transport has some quota to send the data.
+ tq, err := wait(s.ctx, t.shutdownChan, t.sendQuotaPool.acquire())
+ if err != nil {
+ if _, ok := err.(StreamError); ok {
+ t.sendQuotaPool.cancel()
+ }
+ return err
+ }
+ if sq < size {
+ size = sq
+ }
+ if tq < size {
+ size = tq
+ }
+ p = r.Next(size)
+ ps := len(p)
+ if ps < sq {
+ // Overbooked stream quota. Return it back.
+ s.sendQuotaPool.add(sq - ps)
+ }
+ if ps < tq {
+ // Overbooked transport quota. Return it back.
+ t.sendQuotaPool.add(tq - ps)
+ }
+ }
+ var (
+ endStream bool
+ forceFlush bool
+ )
+ if opts.Last && r.Len() == 0 {
+ endStream = true
+ }
+ // Indicate there is a writer who is about to write a data frame.
+ t.framer.adjustNumWriters(1)
+ // Got some quota. Try to acquire writing privilege on the transport.
+ if _, err := wait(s.ctx, t.shutdownChan, t.writableChan); err != nil {
+ if _, ok := err.(StreamError); ok {
+ // Return the connection quota back.
+ t.sendQuotaPool.add(len(p))
+ }
+ if t.framer.adjustNumWriters(-1) == 0 {
+ // This writer is the last one in this batch and has the
+ // responsibility to flush the buffered frames. It queues
+ // a flush request to controlBuf instead of flushing directly
+ // in order to avoid the race with other writing or flushing.
+ t.controlBuf.put(&flushIO{})
+ }
+ return err
+ }
+ select {
+ case <-s.ctx.Done():
+ t.sendQuotaPool.add(len(p))
+ if t.framer.adjustNumWriters(-1) == 0 {
+ t.controlBuf.put(&flushIO{})
+ }
+ t.writableChan <- 0
+ return ContextErr(s.ctx.Err())
+ default:
+ }
+ if r.Len() == 0 && t.framer.adjustNumWriters(0) == 1 {
+ // Do a force flush iff this is last frame for the entire gRPC message
+ // and the caller is the only writer at this moment.
+ forceFlush = true
+ }
+ // If WriteData fails, all the pending streams will be handled
+ // by http2Client.Close(). No explicit CloseStream() needs to be
+ // invoked.
+ if err := t.framer.writeData(forceFlush, s.id, endStream, p); err != nil {
+ t.notifyError(err)
+ return ConnectionErrorf("transport: %v", err)
+ }
+ if t.framer.adjustNumWriters(-1) == 0 {
+ t.framer.flushWrite()
+ }
+ t.writableChan <- 0
+ if r.Len() == 0 {
+ break
+ }
+ }
+ if !opts.Last {
+ return nil
+ }
+ s.mu.Lock()
+ if s.state != streamDone {
+ if s.state == streamReadDone {
+ s.state = streamDone
+ } else {
+ s.state = streamWriteDone
+ }
+ }
+ s.mu.Unlock()
+ return nil
+}
+
+func (t *http2Client) getStream(f http2.Frame) (*Stream, bool) {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ s, ok := t.activeStreams[f.Header().StreamID]
+ return s, ok
+}
+
+// updateWindow adjusts the inbound quota for the stream and the transport.
+// Window updates will deliver to the controller for sending when
+// the cumulative quota exceeds the corresponding threshold.
+func (t *http2Client) updateWindow(s *Stream, n uint32) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if s.state == streamDone {
+ return
+ }
+ if w := t.fc.onRead(n); w > 0 {
+ t.controlBuf.put(&windowUpdate{0, w})
+ }
+ if w := s.fc.onRead(n); w > 0 {
+ t.controlBuf.put(&windowUpdate{s.id, w})
+ }
+}
+
+func (t *http2Client) handleData(f *http2.DataFrame) {
+ size := len(f.Data())
+ if err := t.fc.onData(uint32(size)); err != nil {
+ t.notifyError(ConnectionErrorf("%v", err))
+ return
+ }
+ // Select the right stream to dispatch.
+ s, ok := t.getStream(f)
+ if !ok {
+ if w := t.fc.onRead(uint32(size)); w > 0 {
+ t.controlBuf.put(&windowUpdate{0, w})
+ }
+ return
+ }
+ if size > 0 {
+ s.mu.Lock()
+ if s.state == streamDone {
+ s.mu.Unlock()
+ // The stream has been closed. Release the corresponding quota.
+ if w := t.fc.onRead(uint32(size)); w > 0 {
+ t.controlBuf.put(&windowUpdate{0, w})
+ }
+ return
+ }
+ if err := s.fc.onData(uint32(size)); err != nil {
+ s.state = streamDone
+ s.statusCode = codes.Internal
+ s.statusDesc = err.Error()
+ s.mu.Unlock()
+ s.write(recvMsg{err: io.EOF})
+ t.controlBuf.put(&resetStream{s.id, http2.ErrCodeFlowControl})
+ return
+ }
+ s.mu.Unlock()
+ // TODO(bradfitz, zhaoq): A copy is required here because there is no
+ // guarantee f.Data() is consumed before the arrival of next frame.
+ // Can this copy be eliminated?
+ data := make([]byte, size)
+ copy(data, f.Data())
+ s.write(recvMsg{data: data})
+ }
+ // The server has closed the stream without sending trailers. Record that
+ // the read direction is closed, and set the status appropriately.
+ if f.FrameHeader.Flags.Has(http2.FlagDataEndStream) {
+ s.mu.Lock()
+ if s.state == streamWriteDone {
+ s.state = streamDone
+ } else {
+ s.state = streamReadDone
+ }
+ s.statusCode = codes.Internal
+ s.statusDesc = "server closed the stream without sending trailers"
+ s.mu.Unlock()
+ s.write(recvMsg{err: io.EOF})
+ }
+}
+
+func (t *http2Client) handleRSTStream(f *http2.RSTStreamFrame) {
+ s, ok := t.getStream(f)
+ if !ok {
+ return
+ }
+ s.mu.Lock()
+ if s.state == streamDone {
+ s.mu.Unlock()
+ return
+ }
+ s.state = streamDone
+ if !s.headerDone {
+ close(s.headerChan)
+ s.headerDone = true
+ }
+ s.statusCode, ok = http2ErrConvTab[http2.ErrCode(f.ErrCode)]
+ if !ok {
+ grpclog.Println("transport: http2Client.handleRSTStream found no mapped gRPC status for the received http2 error ", f.ErrCode)
+ s.statusCode = codes.Unknown
+ }
+ s.mu.Unlock()
+ s.write(recvMsg{err: io.EOF})
+}
+
+func (t *http2Client) handleSettings(f *http2.SettingsFrame) {
+ if f.IsAck() {
+ return
+ }
+ var ss []http2.Setting
+ f.ForeachSetting(func(s http2.Setting) error {
+ ss = append(ss, s)
+ return nil
+ })
+ // The settings will be applied once the ack is sent.
+ t.controlBuf.put(&settings{ack: true, ss: ss})
+}
+
+func (t *http2Client) handlePing(f *http2.PingFrame) {
+ pingAck := &ping{ack: true}
+ copy(pingAck.data[:], f.Data[:])
+ t.controlBuf.put(pingAck)
+}
+
+func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) {
+ // TODO(zhaoq): GoAwayFrame handler to be implemented
+}
+
+func (t *http2Client) handleWindowUpdate(f *http2.WindowUpdateFrame) {
+ id := f.Header().StreamID
+ incr := f.Increment
+ if id == 0 {
+ t.sendQuotaPool.add(int(incr))
+ return
+ }
+ if s, ok := t.getStream(f); ok {
+ s.sendQuotaPool.add(int(incr))
+ }
+}
+
+// operateHeaders takes action on the decoded headers.
+func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
+ s, ok := t.getStream(frame)
+ if !ok {
+ return
+ }
+ var state decodeState
+ for _, hf := range frame.Fields {
+ state.processHeaderField(hf)
+ }
+ if state.err != nil {
+ s.write(recvMsg{err: state.err})
+ // Something wrong. Stops reading even when there is remaining.
+ return
+ }
+
+ endStream := frame.StreamEnded()
+
+ s.mu.Lock()
+ if !endStream {
+ s.recvCompress = state.encoding
+ }
+ if !s.headerDone {
+ if !endStream && len(state.mdata) > 0 {
+ s.header = state.mdata
+ }
+ close(s.headerChan)
+ s.headerDone = true
+ }
+ if !endStream || s.state == streamDone {
+ s.mu.Unlock()
+ return
+ }
+
+ if len(state.mdata) > 0 {
+ s.trailer = state.mdata
+ }
+ s.state = streamDone
+ s.statusCode = state.statusCode
+ s.statusDesc = state.statusDesc
+ s.mu.Unlock()
+
+ s.write(recvMsg{err: io.EOF})
+}
+
+func handleMalformedHTTP2(s *Stream, err error) {
+ s.mu.Lock()
+ if !s.headerDone {
+ close(s.headerChan)
+ s.headerDone = true
+ }
+ s.mu.Unlock()
+ s.write(recvMsg{err: err})
+}
+
+// reader runs as a separate goroutine in charge of reading data from network
+// connection.
+//
+// TODO(zhaoq): currently one reader per transport. Investigate whether this is
+// optimal.
+// TODO(zhaoq): Check the validity of the incoming frame sequence.
+func (t *http2Client) reader() {
+ // Check the validity of server preface.
+ frame, err := t.framer.readFrame()
+ if err != nil {
+ t.notifyError(err)
+ return
+ }
+ sf, ok := frame.(*http2.SettingsFrame)
+ if !ok {
+ t.notifyError(err)
+ return
+ }
+ t.handleSettings(sf)
+
+ // loop to keep reading incoming messages on this transport.
+ for {
+ frame, err := t.framer.readFrame()
+ if err != nil {
+ // Abort an active stream if the http2.Framer returns a
+ // http2.StreamError. This can happen only if the server's response
+ // is malformed http2.
+ if se, ok := err.(http2.StreamError); ok {
+ t.mu.Lock()
+ s := t.activeStreams[se.StreamID]
+ t.mu.Unlock()
+ if s != nil {
+ // use error detail to provide better err message
+ handleMalformedHTTP2(s, StreamErrorf(http2ErrConvTab[se.Code], "%v", t.framer.errorDetail()))
+ }
+ continue
+ } else {
+ // Transport error.
+ t.notifyError(err)
+ return
+ }
+ }
+ switch frame := frame.(type) {
+ case *http2.MetaHeadersFrame:
+ t.operateHeaders(frame)
+ case *http2.DataFrame:
+ t.handleData(frame)
+ case *http2.RSTStreamFrame:
+ t.handleRSTStream(frame)
+ case *http2.SettingsFrame:
+ t.handleSettings(frame)
+ case *http2.PingFrame:
+ t.handlePing(frame)
+ case *http2.GoAwayFrame:
+ t.handleGoAway(frame)
+ case *http2.WindowUpdateFrame:
+ t.handleWindowUpdate(frame)
+ default:
+ grpclog.Printf("transport: http2Client.reader got unhandled frame type %v.", frame)
+ }
+ }
+}
+
+func (t *http2Client) applySettings(ss []http2.Setting) {
+ for _, s := range ss {
+ switch s.ID {
+ case http2.SettingMaxConcurrentStreams:
+ // TODO(zhaoq): This is a hack to avoid significant refactoring of the
+ // code to deal with the unrealistic int32 overflow. Probably will try
+ // to find a better way to handle this later.
+ if s.Val > math.MaxInt32 {
+ s.Val = math.MaxInt32
+ }
+ t.mu.Lock()
+ reset := t.streamsQuota != nil
+ if !reset {
+ t.streamsQuota = newQuotaPool(int(s.Val) - len(t.activeStreams))
+ }
+ ms := t.maxStreams
+ t.maxStreams = int(s.Val)
+ t.mu.Unlock()
+ if reset {
+ t.streamsQuota.reset(int(s.Val) - ms)
+ }
+ case http2.SettingInitialWindowSize:
+ t.mu.Lock()
+ for _, stream := range t.activeStreams {
+ // Adjust the sending quota for each stream.
+ stream.sendQuotaPool.reset(int(s.Val - t.streamSendQuota))
+ }
+ t.streamSendQuota = s.Val
+ t.mu.Unlock()
+ }
+ }
+}
+
+// controller running in a separate goroutine takes charge of sending control
+// frames (e.g., window update, reset stream, setting, etc.) to the server.
+func (t *http2Client) controller() {
+ for {
+ select {
+ case i := <-t.controlBuf.get():
+ t.controlBuf.load()
+ select {
+ case <-t.writableChan:
+ switch i := i.(type) {
+ case *windowUpdate:
+ t.framer.writeWindowUpdate(true, i.streamID, i.increment)
+ case *settings:
+ if i.ack {
+ t.framer.writeSettingsAck(true)
+ t.applySettings(i.ss)
+ } else {
+ t.framer.writeSettings(true, i.ss...)
+ }
+ case *resetStream:
+ t.framer.writeRSTStream(true, i.streamID, i.code)
+ case *flushIO:
+ t.framer.flushWrite()
+ case *ping:
+ t.framer.writePing(true, i.ack, i.data)
+ default:
+ grpclog.Printf("transport: http2Client.controller got unexpected item type %v\n", i)
+ }
+ t.writableChan <- 0
+ continue
+ case <-t.shutdownChan:
+ return
+ }
+ case <-t.shutdownChan:
+ return
+ }
+ }
+}
+
+func (t *http2Client) Error() <-chan struct{} {
+ return t.errorChan
+}
+
+func (t *http2Client) notifyError(err error) {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ // make sure t.errorChan is closed only once.
+ if t.state == reachable {
+ t.state = unreachable
+ close(t.errorChan)
+ grpclog.Printf("transport: http2Client.notifyError got notified that the client transport was broken %v.", err)
+ }
+}
diff --git a/vendor/google.golang.org/grpc/transport/http2_server.go b/vendor/google.golang.org/grpc/transport/http2_server.go
new file mode 100644
index 0000000..1c4d585
--- /dev/null
+++ b/vendor/google.golang.org/grpc/transport/http2_server.go
@@ -0,0 +1,743 @@
+/*
+ *
+ * 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 (
+ "bytes"
+ "errors"
+ "io"
+ "math"
+ "net"
+ "strconv"
+ "sync"
+
+ "golang.org/x/net/context"
+ "golang.org/x/net/http2"
+ "golang.org/x/net/http2/hpack"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/credentials"
+ "google.golang.org/grpc/grpclog"
+ "google.golang.org/grpc/metadata"
+ "google.golang.org/grpc/peer"
+)
+
+// ErrIllegalHeaderWrite indicates that setting header is illegal because of
+// the stream's state.
+var ErrIllegalHeaderWrite = errors.New("transport: the stream is done or WriteHeader was already called")
+
+// http2Server implements the ServerTransport interface with HTTP2.
+type http2Server struct {
+ conn net.Conn
+ maxStreamID uint32 // max stream ID ever seen
+ authInfo credentials.AuthInfo // auth info about the connection
+ // writableChan synchronizes write access to the transport.
+ // A writer acquires the write lock by receiving a value on writableChan
+ // and releases it by sending on writableChan.
+ writableChan chan int
+ // shutdownChan is closed when Close is called.
+ // Blocking operations should select on shutdownChan to avoid
+ // blocking forever after Close.
+ shutdownChan chan struct{}
+ framer *framer
+ hBuf *bytes.Buffer // the buffer for HPACK encoding
+ hEnc *hpack.Encoder // HPACK encoder
+
+ // The max number of concurrent streams.
+ maxStreams uint32
+ // controlBuf delivers all the control related tasks (e.g., window
+ // updates, reset streams, and various settings) to the controller.
+ controlBuf *recvBuffer
+ fc *inFlow
+ // sendQuotaPool provides flow control to outbound message.
+ sendQuotaPool *quotaPool
+
+ mu sync.Mutex // guard the following
+ state transportState
+ activeStreams map[uint32]*Stream
+ // the per-stream outbound flow control window size set by the peer.
+ streamSendQuota uint32
+}
+
+// newHTTP2Server constructs a ServerTransport based on HTTP2. ConnectionError is
+// returned if something goes wrong.
+func newHTTP2Server(conn net.Conn, maxStreams uint32, authInfo credentials.AuthInfo) (_ ServerTransport, err error) {
+ framer := newFramer(conn)
+ // Send initial settings as connection preface to client.
+ var settings []http2.Setting
+ // TODO(zhaoq): Have a better way to signal "no limit" because 0 is
+ // permitted in the HTTP2 spec.
+ if maxStreams == 0 {
+ maxStreams = math.MaxUint32
+ } else {
+ settings = append(settings, http2.Setting{http2.SettingMaxConcurrentStreams, maxStreams})
+ }
+ if initialWindowSize != defaultWindowSize {
+ settings = append(settings, http2.Setting{http2.SettingInitialWindowSize, uint32(initialWindowSize)})
+ }
+ if err := framer.writeSettings(true, settings...); err != nil {
+ return nil, ConnectionErrorf("transport: %v", err)
+ }
+ // Adjust the connection flow control window if needed.
+ if delta := uint32(initialConnWindowSize - defaultWindowSize); delta > 0 {
+ if err := framer.writeWindowUpdate(true, 0, delta); err != nil {
+ return nil, ConnectionErrorf("transport: %v", err)
+ }
+ }
+ var buf bytes.Buffer
+ t := &http2Server{
+ conn: conn,
+ authInfo: authInfo,
+ framer: framer,
+ hBuf: &buf,
+ hEnc: hpack.NewEncoder(&buf),
+ maxStreams: maxStreams,
+ controlBuf: newRecvBuffer(),
+ fc: &inFlow{limit: initialConnWindowSize},
+ sendQuotaPool: newQuotaPool(defaultWindowSize),
+ state: reachable,
+ writableChan: make(chan int, 1),
+ shutdownChan: make(chan struct{}),
+ activeStreams: make(map[uint32]*Stream),
+ streamSendQuota: defaultWindowSize,
+ }
+ go t.controller()
+ t.writableChan <- 0
+ return t, nil
+}
+
+// operateHeader takes action on the decoded headers.
+func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(*Stream)) {
+ buf := newRecvBuffer()
+ s := &Stream{
+ id: frame.Header().StreamID,
+ st: t,
+ buf: buf,
+ fc: &inFlow{limit: initialWindowSize},
+ }
+
+ var state decodeState
+ for _, hf := range frame.Fields {
+ state.processHeaderField(hf)
+ }
+ if err := state.err; err != nil {
+ if se, ok := err.(StreamError); ok {
+ t.controlBuf.put(&resetStream{s.id, statusCodeConvTab[se.Code]})
+ }
+ return
+ }
+
+ if frame.StreamEnded() {
+ // s is just created by the caller. No lock needed.
+ s.state = streamReadDone
+ }
+ s.recvCompress = state.encoding
+ if state.timeoutSet {
+ s.ctx, s.cancel = context.WithTimeout(context.TODO(), state.timeout)
+ } else {
+ s.ctx, s.cancel = context.WithCancel(context.TODO())
+ }
+ pr := &peer.Peer{
+ Addr: t.conn.RemoteAddr(),
+ }
+ // Attach Auth info if there is any.
+ if t.authInfo != nil {
+ pr.AuthInfo = t.authInfo
+ }
+ s.ctx = peer.NewContext(s.ctx, pr)
+ // Cache the current stream to the context so that the server application
+ // can find out. Required when the server wants to send some metadata
+ // back to the client (unary call only).
+ s.ctx = newContextWithStream(s.ctx, s)
+ // Attach the received metadata to the context.
+ if len(state.mdata) > 0 {
+ s.ctx = metadata.NewContext(s.ctx, state.mdata)
+ }
+
+ s.dec = &recvBufferReader{
+ ctx: s.ctx,
+ recv: s.buf,
+ }
+ s.recvCompress = state.encoding
+ s.method = state.method
+ t.mu.Lock()
+ if t.state != reachable {
+ t.mu.Unlock()
+ return
+ }
+ if uint32(len(t.activeStreams)) >= t.maxStreams {
+ t.mu.Unlock()
+ t.controlBuf.put(&resetStream{s.id, http2.ErrCodeRefusedStream})
+ return
+ }
+ s.sendQuotaPool = newQuotaPool(int(t.streamSendQuota))
+ t.activeStreams[s.id] = s
+ t.mu.Unlock()
+ s.windowHandler = func(n int) {
+ t.updateWindow(s, uint32(n))
+ }
+ handle(s)
+}
+
+// HandleStreams receives incoming streams using the given handler. This is
+// typically run in a separate goroutine.
+func (t *http2Server) HandleStreams(handle func(*Stream)) {
+ // Check the validity of client preface.
+ preface := make([]byte, len(clientPreface))
+ if _, err := io.ReadFull(t.conn, preface); err != nil {
+ grpclog.Printf("transport: http2Server.HandleStreams failed to receive the preface from client: %v", err)
+ t.Close()
+ return
+ }
+ if !bytes.Equal(preface, clientPreface) {
+ grpclog.Printf("transport: http2Server.HandleStreams received bogus greeting from client: %q", preface)
+ t.Close()
+ return
+ }
+
+ frame, err := t.framer.readFrame()
+ if err != nil {
+ grpclog.Printf("transport: http2Server.HandleStreams failed to read frame: %v", err)
+ t.Close()
+ return
+ }
+ sf, ok := frame.(*http2.SettingsFrame)
+ if !ok {
+ grpclog.Printf("transport: http2Server.HandleStreams saw invalid preface type %T from client", frame)
+ t.Close()
+ return
+ }
+ t.handleSettings(sf)
+
+ for {
+ frame, err := t.framer.readFrame()
+ if err != nil {
+ if se, ok := err.(http2.StreamError); ok {
+ t.mu.Lock()
+ s := t.activeStreams[se.StreamID]
+ t.mu.Unlock()
+ if s != nil {
+ t.closeStream(s)
+ }
+ t.controlBuf.put(&resetStream{se.StreamID, se.Code})
+ continue
+ }
+ t.Close()
+ return
+ }
+ switch frame := frame.(type) {
+ case *http2.MetaHeadersFrame:
+ id := frame.Header().StreamID
+ if id%2 != 1 || id <= t.maxStreamID {
+ // illegal gRPC stream id.
+ grpclog.Println("transport: http2Server.HandleStreams received an illegal stream id: ", id)
+ t.Close()
+ break
+ }
+ t.maxStreamID = id
+ t.operateHeaders(frame, handle)
+ case *http2.DataFrame:
+ t.handleData(frame)
+ case *http2.RSTStreamFrame:
+ t.handleRSTStream(frame)
+ case *http2.SettingsFrame:
+ t.handleSettings(frame)
+ case *http2.PingFrame:
+ t.handlePing(frame)
+ case *http2.WindowUpdateFrame:
+ t.handleWindowUpdate(frame)
+ case *http2.GoAwayFrame:
+ break
+ default:
+ grpclog.Printf("transport: http2Server.HandleStreams found unhandled frame type %v.", frame)
+ }
+ }
+}
+
+func (t *http2Server) getStream(f http2.Frame) (*Stream, bool) {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ if t.activeStreams == nil {
+ // The transport is closing.
+ return nil, false
+ }
+ s, ok := t.activeStreams[f.Header().StreamID]
+ if !ok {
+ // The stream is already done.
+ return nil, false
+ }
+ return s, true
+}
+
+// updateWindow adjusts the inbound quota for the stream and the transport.
+// Window updates will deliver to the controller for sending when
+// the cumulative quota exceeds the corresponding threshold.
+func (t *http2Server) updateWindow(s *Stream, n uint32) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if s.state == streamDone {
+ return
+ }
+ if w := t.fc.onRead(n); w > 0 {
+ t.controlBuf.put(&windowUpdate{0, w})
+ }
+ if w := s.fc.onRead(n); w > 0 {
+ t.controlBuf.put(&windowUpdate{s.id, w})
+ }
+}
+
+func (t *http2Server) handleData(f *http2.DataFrame) {
+ size := len(f.Data())
+ if err := t.fc.onData(uint32(size)); err != nil {
+ grpclog.Printf("transport: http2Server %v", err)
+ t.Close()
+ return
+ }
+ // Select the right stream to dispatch.
+ s, ok := t.getStream(f)
+ if !ok {
+ if w := t.fc.onRead(uint32(size)); w > 0 {
+ t.controlBuf.put(&windowUpdate{0, w})
+ }
+ return
+ }
+ if size > 0 {
+ s.mu.Lock()
+ if s.state == streamDone {
+ s.mu.Unlock()
+ // The stream has been closed. Release the corresponding quota.
+ if w := t.fc.onRead(uint32(size)); w > 0 {
+ t.controlBuf.put(&windowUpdate{0, w})
+ }
+ return
+ }
+ if err := s.fc.onData(uint32(size)); err != nil {
+ s.mu.Unlock()
+ t.closeStream(s)
+ t.controlBuf.put(&resetStream{s.id, http2.ErrCodeFlowControl})
+ return
+ }
+ s.mu.Unlock()
+ // TODO(bradfitz, zhaoq): A copy is required here because there is no
+ // guarantee f.Data() is consumed before the arrival of next frame.
+ // Can this copy be eliminated?
+ data := make([]byte, size)
+ copy(data, f.Data())
+ s.write(recvMsg{data: data})
+ }
+ if f.Header().Flags.Has(http2.FlagDataEndStream) {
+ // Received the end of stream from the client.
+ s.mu.Lock()
+ if s.state != streamDone {
+ if s.state == streamWriteDone {
+ s.state = streamDone
+ } else {
+ s.state = streamReadDone
+ }
+ }
+ s.mu.Unlock()
+ s.write(recvMsg{err: io.EOF})
+ }
+}
+
+func (t *http2Server) handleRSTStream(f *http2.RSTStreamFrame) {
+ s, ok := t.getStream(f)
+ if !ok {
+ return
+ }
+ t.closeStream(s)
+}
+
+func (t *http2Server) handleSettings(f *http2.SettingsFrame) {
+ if f.IsAck() {
+ return
+ }
+ var ss []http2.Setting
+ f.ForeachSetting(func(s http2.Setting) error {
+ ss = append(ss, s)
+ return nil
+ })
+ // The settings will be applied once the ack is sent.
+ t.controlBuf.put(&settings{ack: true, ss: ss})
+}
+
+func (t *http2Server) handlePing(f *http2.PingFrame) {
+ pingAck := &ping{ack: true}
+ copy(pingAck.data[:], f.Data[:])
+ t.controlBuf.put(pingAck)
+}
+
+func (t *http2Server) handleWindowUpdate(f *http2.WindowUpdateFrame) {
+ id := f.Header().StreamID
+ incr := f.Increment
+ if id == 0 {
+ t.sendQuotaPool.add(int(incr))
+ return
+ }
+ if s, ok := t.getStream(f); ok {
+ s.sendQuotaPool.add(int(incr))
+ }
+}
+
+func (t *http2Server) writeHeaders(s *Stream, b *bytes.Buffer, endStream bool) error {
+ first := true
+ endHeaders := false
+ var err error
+ // Sends the headers in a single batch.
+ for !endHeaders {
+ size := t.hBuf.Len()
+ if size > http2MaxFrameLen {
+ size = http2MaxFrameLen
+ } else {
+ endHeaders = true
+ }
+ if first {
+ p := http2.HeadersFrameParam{
+ StreamID: s.id,
+ BlockFragment: b.Next(size),
+ EndStream: endStream,
+ EndHeaders: endHeaders,
+ }
+ err = t.framer.writeHeaders(endHeaders, p)
+ first = false
+ } else {
+ err = t.framer.writeContinuation(endHeaders, s.id, endHeaders, b.Next(size))
+ }
+ if err != nil {
+ t.Close()
+ return ConnectionErrorf("transport: %v", err)
+ }
+ }
+ return nil
+}
+
+// WriteHeader sends the header metedata md back to the client.
+func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error {
+ s.mu.Lock()
+ if s.headerOk || s.state == streamDone {
+ s.mu.Unlock()
+ return ErrIllegalHeaderWrite
+ }
+ s.headerOk = true
+ s.mu.Unlock()
+ if _, err := wait(s.ctx, t.shutdownChan, t.writableChan); err != nil {
+ return err
+ }
+ t.hBuf.Reset()
+ t.hEnc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
+ t.hEnc.WriteField(hpack.HeaderField{Name: "content-type", Value: "application/grpc"})
+ if s.sendCompress != "" {
+ t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-encoding", Value: s.sendCompress})
+ }
+ for k, v := range md {
+ if isReservedHeader(k) {
+ // Clients don't tolerate reading restricted headers after some non restricted ones were sent.
+ continue
+ }
+ for _, entry := range v {
+ t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry})
+ }
+ }
+ if err := t.writeHeaders(s, t.hBuf, false); err != nil {
+ return err
+ }
+ t.writableChan <- 0
+ return nil
+}
+
+// WriteStatus sends stream status to the client and terminates the stream.
+// There is no further I/O operations being able to perform on this stream.
+// TODO(zhaoq): Now it indicates the end of entire stream. Revisit if early
+// OK is adopted.
+func (t *http2Server) WriteStatus(s *Stream, statusCode codes.Code, statusDesc string) error {
+ var headersSent bool
+ s.mu.Lock()
+ if s.state == streamDone {
+ s.mu.Unlock()
+ return nil
+ }
+ if s.headerOk {
+ headersSent = true
+ }
+ s.mu.Unlock()
+ if _, err := wait(s.ctx, t.shutdownChan, t.writableChan); err != nil {
+ return err
+ }
+ t.hBuf.Reset()
+ if !headersSent {
+ t.hEnc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
+ t.hEnc.WriteField(hpack.HeaderField{Name: "content-type", Value: "application/grpc"})
+ }
+ t.hEnc.WriteField(
+ hpack.HeaderField{
+ Name: "grpc-status",
+ Value: strconv.Itoa(int(statusCode)),
+ })
+ t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-message", Value: statusDesc})
+ // Attach the trailer metadata.
+ for k, v := range s.trailer {
+ // Clients don't tolerate reading restricted headers after some non restricted ones were sent.
+ if isReservedHeader(k) {
+ continue
+ }
+ for _, entry := range v {
+ t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry})
+ }
+ }
+ if err := t.writeHeaders(s, t.hBuf, true); err != nil {
+ t.Close()
+ return err
+ }
+ t.closeStream(s)
+ t.writableChan <- 0
+ return nil
+}
+
+// Write converts the data into HTTP2 data frame and sends it out. Non-nil error
+// is returns if it fails (e.g., framing error, transport error).
+func (t *http2Server) Write(s *Stream, data []byte, opts *Options) error {
+ // TODO(zhaoq): Support multi-writers for a single stream.
+ var writeHeaderFrame bool
+ s.mu.Lock()
+ if s.state == streamDone {
+ s.mu.Unlock()
+ return StreamErrorf(codes.Unknown, "the stream has been done")
+ }
+ if !s.headerOk {
+ writeHeaderFrame = true
+ s.headerOk = true
+ }
+ s.mu.Unlock()
+ if writeHeaderFrame {
+ if _, err := wait(s.ctx, t.shutdownChan, t.writableChan); err != nil {
+ return err
+ }
+ t.hBuf.Reset()
+ t.hEnc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
+ t.hEnc.WriteField(hpack.HeaderField{Name: "content-type", Value: "application/grpc"})
+ if s.sendCompress != "" {
+ t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-encoding", Value: s.sendCompress})
+ }
+ p := http2.HeadersFrameParam{
+ StreamID: s.id,
+ BlockFragment: t.hBuf.Bytes(),
+ EndHeaders: true,
+ }
+ if err := t.framer.writeHeaders(false, p); err != nil {
+ t.Close()
+ return ConnectionErrorf("transport: %v", err)
+ }
+ t.writableChan <- 0
+ }
+ r := bytes.NewBuffer(data)
+ for {
+ if r.Len() == 0 {
+ return nil
+ }
+ size := http2MaxFrameLen
+ s.sendQuotaPool.add(0)
+ // Wait until the stream has some quota to send the data.
+ sq, err := wait(s.ctx, t.shutdownChan, s.sendQuotaPool.acquire())
+ if err != nil {
+ return err
+ }
+ t.sendQuotaPool.add(0)
+ // Wait until the transport has some quota to send the data.
+ tq, err := wait(s.ctx, t.shutdownChan, t.sendQuotaPool.acquire())
+ if err != nil {
+ if _, ok := err.(StreamError); ok {
+ t.sendQuotaPool.cancel()
+ }
+ return err
+ }
+ if sq < size {
+ size = sq
+ }
+ if tq < size {
+ size = tq
+ }
+ p := r.Next(size)
+ ps := len(p)
+ if ps < sq {
+ // Overbooked stream quota. Return it back.
+ s.sendQuotaPool.add(sq - ps)
+ }
+ if ps < tq {
+ // Overbooked transport quota. Return it back.
+ t.sendQuotaPool.add(tq - ps)
+ }
+ t.framer.adjustNumWriters(1)
+ // Got some quota. Try to acquire writing privilege on the
+ // transport.
+ if _, err := wait(s.ctx, t.shutdownChan, t.writableChan); err != nil {
+ if _, ok := err.(StreamError); ok {
+ // Return the connection quota back.
+ t.sendQuotaPool.add(ps)
+ }
+ if t.framer.adjustNumWriters(-1) == 0 {
+ // This writer is the last one in this batch and has the
+ // responsibility to flush the buffered frames. It queues
+ // a flush request to controlBuf instead of flushing directly
+ // in order to avoid the race with other writing or flushing.
+ t.controlBuf.put(&flushIO{})
+ }
+ return err
+ }
+ select {
+ case <-s.ctx.Done():
+ t.sendQuotaPool.add(ps)
+ if t.framer.adjustNumWriters(-1) == 0 {
+ t.controlBuf.put(&flushIO{})
+ }
+ t.writableChan <- 0
+ return ContextErr(s.ctx.Err())
+ default:
+ }
+ var forceFlush bool
+ if r.Len() == 0 && t.framer.adjustNumWriters(0) == 1 && !opts.Last {
+ forceFlush = true
+ }
+ if err := t.framer.writeData(forceFlush, s.id, false, p); err != nil {
+ t.Close()
+ return ConnectionErrorf("transport: %v", err)
+ }
+ if t.framer.adjustNumWriters(-1) == 0 {
+ t.framer.flushWrite()
+ }
+ t.writableChan <- 0
+ }
+
+}
+
+func (t *http2Server) applySettings(ss []http2.Setting) {
+ for _, s := range ss {
+ if s.ID == http2.SettingInitialWindowSize {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ for _, stream := range t.activeStreams {
+ stream.sendQuotaPool.reset(int(s.Val - t.streamSendQuota))
+ }
+ t.streamSendQuota = s.Val
+ }
+
+ }
+}
+
+// controller running in a separate goroutine takes charge of sending control
+// frames (e.g., window update, reset stream, setting, etc.) to the server.
+func (t *http2Server) controller() {
+ for {
+ select {
+ case i := <-t.controlBuf.get():
+ t.controlBuf.load()
+ select {
+ case <-t.writableChan:
+ switch i := i.(type) {
+ case *windowUpdate:
+ t.framer.writeWindowUpdate(true, i.streamID, i.increment)
+ case *settings:
+ if i.ack {
+ t.framer.writeSettingsAck(true)
+ t.applySettings(i.ss)
+ } else {
+ t.framer.writeSettings(true, i.ss...)
+ }
+ case *resetStream:
+ t.framer.writeRSTStream(true, i.streamID, i.code)
+ case *flushIO:
+ t.framer.flushWrite()
+ case *ping:
+ t.framer.writePing(true, i.ack, i.data)
+ default:
+ grpclog.Printf("transport: http2Server.controller got unexpected item type %v\n", i)
+ }
+ t.writableChan <- 0
+ continue
+ case <-t.shutdownChan:
+ return
+ }
+ case <-t.shutdownChan:
+ return
+ }
+ }
+}
+
+// Close starts shutting down the http2Server transport.
+// TODO(zhaoq): Now the destruction is not blocked on any pending streams. This
+// could cause some resource issue. Revisit this later.
+func (t *http2Server) Close() (err error) {
+ t.mu.Lock()
+ if t.state == closing {
+ t.mu.Unlock()
+ return errors.New("transport: Close() was already called")
+ }
+ t.state = closing
+ streams := t.activeStreams
+ t.activeStreams = nil
+ t.mu.Unlock()
+ close(t.shutdownChan)
+ err = t.conn.Close()
+ // Cancel all active streams.
+ for _, s := range streams {
+ s.cancel()
+ }
+ return
+}
+
+// closeStream clears the footprint of a stream when the stream is not needed
+// any more.
+func (t *http2Server) closeStream(s *Stream) {
+ t.mu.Lock()
+ delete(t.activeStreams, s.id)
+ t.mu.Unlock()
+ // In case stream sending and receiving are invoked in separate
+ // goroutines (e.g., bi-directional streaming), cancel needs to be
+ // called to interrupt the potential blocking on other goroutines.
+ s.cancel()
+ s.mu.Lock()
+ if q := s.fc.resetPendingData(); q > 0 {
+ if w := t.fc.onRead(q); w > 0 {
+ t.controlBuf.put(&windowUpdate{0, w})
+ }
+ }
+ if s.state == streamDone {
+ s.mu.Unlock()
+ return
+ }
+ s.state = streamDone
+ s.mu.Unlock()
+}
+
+func (t *http2Server) RemoteAddr() net.Addr {
+ return t.conn.RemoteAddr()
+}
diff --git a/vendor/google.golang.org/grpc/transport/http_util.go b/vendor/google.golang.org/grpc/transport/http_util.go
new file mode 100644
index 0000000..a4b1b07
--- /dev/null
+++ b/vendor/google.golang.org/grpc/transport/http_util.go
@@ -0,0 +1,422 @@
+/*
+ *
+ * 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
+ }
+}
+
+// isWhitelistedPseudoHeader checks whether hdr belongs to HTTP2 pseudoheaders
+// that should be propagated into metadata visible to users.
+func isWhitelistedPseudoHeader(hdr string) bool {
+ switch hdr {
+ case ":authority":
+ 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) || isWhitelistedPseudoHeader(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()
+}
diff --git a/vendor/google.golang.org/grpc/transport/transport.go b/vendor/google.golang.org/grpc/transport/transport.go
new file mode 100644
index 0000000..1c9af54
--- /dev/null
+++ b/vendor/google.golang.org/grpc/transport/transport.go
@@ -0,0 +1,513 @@
+/*
+ *
+ * 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 defines and implements message oriented communication channel
+to complete various transactions (e.g., an RPC).
+*/
+package transport // import "google.golang.org/grpc/transport"
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "sync"
+ "time"
+
+ "golang.org/x/net/context"
+ "golang.org/x/net/trace"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/credentials"
+ "google.golang.org/grpc/metadata"
+)
+
+// recvMsg represents the received msg from the transport. All transport
+// protocol specific info has been removed.
+type recvMsg struct {
+ data []byte
+ // nil: received some data
+ // io.EOF: stream is completed. data is nil.
+ // other non-nil error: transport failure. data is nil.
+ err error
+}
+
+func (*recvMsg) item() {}
+
+// All items in an out of a recvBuffer should be the same type.
+type item interface {
+ item()
+}
+
+// recvBuffer is an unbounded channel of item.
+type recvBuffer struct {
+ c chan item
+ mu sync.Mutex
+ backlog []item
+}
+
+func newRecvBuffer() *recvBuffer {
+ b := &recvBuffer{
+ c: make(chan item, 1),
+ }
+ return b
+}
+
+func (b *recvBuffer) put(r item) {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+ if len(b.backlog) == 0 {
+ select {
+ case b.c <- r:
+ return
+ default:
+ }
+ }
+ b.backlog = append(b.backlog, r)
+}
+
+func (b *recvBuffer) load() {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+ if len(b.backlog) > 0 {
+ select {
+ case b.c <- b.backlog[0]:
+ b.backlog = b.backlog[1:]
+ default:
+ }
+ }
+}
+
+// get returns the channel that receives an item in the buffer.
+//
+// Upon receipt of an item, the caller should call load to send another
+// item onto the channel if there is any.
+func (b *recvBuffer) get() <-chan item {
+ return b.c
+}
+
+// recvBufferReader implements io.Reader interface to read the data from
+// recvBuffer.
+type recvBufferReader struct {
+ ctx context.Context
+ recv *recvBuffer
+ last *bytes.Reader // Stores the remaining data in the previous calls.
+ err error
+}
+
+// Read reads the next len(p) bytes from last. If last is drained, it tries to
+// read additional data from recv. It blocks if there no additional data available
+// in recv. If Read returns any non-nil error, it will continue to return that error.
+func (r *recvBufferReader) Read(p []byte) (n int, err error) {
+ if r.err != nil {
+ return 0, r.err
+ }
+ defer func() { r.err = err }()
+ if r.last != nil && r.last.Len() > 0 {
+ // Read remaining data left in last call.
+ return r.last.Read(p)
+ }
+ select {
+ case <-r.ctx.Done():
+ return 0, ContextErr(r.ctx.Err())
+ case i := <-r.recv.get():
+ r.recv.load()
+ m := i.(*recvMsg)
+ if m.err != nil {
+ return 0, m.err
+ }
+ r.last = bytes.NewReader(m.data)
+ return r.last.Read(p)
+ }
+}
+
+type streamState uint8
+
+const (
+ streamActive streamState = iota
+ streamWriteDone // EndStream sent
+ streamReadDone // EndStream received
+ streamDone // sendDone and recvDone or RSTStreamFrame is sent or received.
+)
+
+// Stream represents an RPC in the transport layer.
+type Stream struct {
+ id uint32
+ // nil for client side Stream.
+ st ServerTransport
+ // ctx is the associated context of the stream.
+ ctx context.Context
+ cancel context.CancelFunc
+ // method records the associated RPC method of the stream.
+ method string
+ recvCompress string
+ sendCompress string
+ buf *recvBuffer
+ dec io.Reader
+ fc *inFlow
+ recvQuota uint32
+ // The accumulated inbound quota pending for window update.
+ updateQuota uint32
+ // The handler to control the window update procedure for both this
+ // particular stream and the associated transport.
+ windowHandler func(int)
+
+ sendQuotaPool *quotaPool
+ // Close headerChan to indicate the end of reception of header metadata.
+ headerChan chan struct{}
+ // header caches the received header metadata.
+ header metadata.MD
+ // The key-value map of trailer metadata.
+ trailer metadata.MD
+
+ mu sync.RWMutex // guard the following
+ // headerOK becomes true from the first header is about to send.
+ headerOk bool
+ state streamState
+ // true iff headerChan is closed. Used to avoid closing headerChan
+ // multiple times.
+ headerDone bool
+ // the status received from the server.
+ statusCode codes.Code
+ statusDesc string
+}
+
+// RecvCompress returns the compression algorithm applied to the inbound
+// message. It is empty string if there is no compression applied.
+func (s *Stream) RecvCompress() string {
+ return s.recvCompress
+}
+
+// SetSendCompress sets the compression algorithm to the stream.
+func (s *Stream) SetSendCompress(str string) {
+ s.sendCompress = str
+}
+
+// Header acquires the key-value pairs of header metadata once it
+// is available. It blocks until i) the metadata is ready or ii) there is no
+// header metadata or iii) the stream is cancelled/expired.
+func (s *Stream) Header() (metadata.MD, error) {
+ select {
+ case <-s.ctx.Done():
+ return nil, ContextErr(s.ctx.Err())
+ case <-s.headerChan:
+ return s.header.Copy(), nil
+ }
+}
+
+// Trailer returns the cached trailer metedata. Note that if it is not called
+// after the entire stream is done, it could return an empty MD. Client
+// side only.
+func (s *Stream) Trailer() metadata.MD {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+ return s.trailer.Copy()
+}
+
+// ServerTransport returns the underlying ServerTransport for the stream.
+// The client side stream always returns nil.
+func (s *Stream) ServerTransport() ServerTransport {
+ return s.st
+}
+
+// Context returns the context of the stream.
+func (s *Stream) Context() context.Context {
+ return s.ctx
+}
+
+// TraceContext recreates the context of s with a trace.Trace.
+func (s *Stream) TraceContext(tr trace.Trace) {
+ s.ctx = trace.NewContext(s.ctx, tr)
+}
+
+// Method returns the method for the stream.
+func (s *Stream) Method() string {
+ return s.method
+}
+
+// StatusCode returns statusCode received from the server.
+func (s *Stream) StatusCode() codes.Code {
+ return s.statusCode
+}
+
+// StatusDesc returns statusDesc received from the server.
+func (s *Stream) StatusDesc() string {
+ return s.statusDesc
+}
+
+// ErrIllegalTrailerSet indicates that the trailer has already been set or it
+// is too late to do so.
+var ErrIllegalTrailerSet = errors.New("transport: trailer has been set")
+
+// SetTrailer sets the trailer metadata which will be sent with the RPC status
+// by the server. This can only be called at most once. Server side only.
+func (s *Stream) SetTrailer(md metadata.MD) error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if s.trailer != nil {
+ return ErrIllegalTrailerSet
+ }
+ s.trailer = md.Copy()
+ return nil
+}
+
+func (s *Stream) write(m recvMsg) {
+ s.buf.put(&m)
+}
+
+// Read reads all the data available for this Stream from the transport and
+// passes them into the decoder, which converts them into a gRPC message stream.
+// The error is io.EOF when the stream is done or another non-nil error if
+// the stream broke.
+func (s *Stream) Read(p []byte) (n int, err error) {
+ n, err = s.dec.Read(p)
+ if err != nil {
+ return
+ }
+ s.windowHandler(n)
+ return
+}
+
+// The key to save transport.Stream in the context.
+type streamKey struct{}
+
+// newContextWithStream creates a new context from ctx and attaches stream
+// to it.
+func newContextWithStream(ctx context.Context, stream *Stream) context.Context {
+ return context.WithValue(ctx, streamKey{}, stream)
+}
+
+// StreamFromContext returns the stream saved in ctx.
+func StreamFromContext(ctx context.Context) (s *Stream, ok bool) {
+ s, ok = ctx.Value(streamKey{}).(*Stream)
+ return
+}
+
+// state of transport
+type transportState int
+
+const (
+ reachable transportState = iota
+ unreachable
+ closing
+ draining
+)
+
+// NewServerTransport creates a ServerTransport with conn or non-nil error
+// if it fails.
+func NewServerTransport(protocol string, conn net.Conn, maxStreams uint32, authInfo credentials.AuthInfo) (ServerTransport, error) {
+ return newHTTP2Server(conn, maxStreams, authInfo)
+}
+
+// ConnectOptions covers all relevant options for dialing a server.
+type ConnectOptions struct {
+ // UserAgent is the application user agent.
+ UserAgent string
+ // Dialer specifies how to dial a network address.
+ Dialer func(string, time.Duration) (net.Conn, error)
+ // AuthOptions stores the credentials required to setup a client connection and/or issue RPCs.
+ AuthOptions []credentials.Credentials
+ // Timeout specifies the timeout for dialing a client connection.
+ Timeout time.Duration
+}
+
+// NewClientTransport establishes the transport with the required ConnectOptions
+// and returns it to the caller.
+func NewClientTransport(target string, opts *ConnectOptions) (ClientTransport, error) {
+ return newHTTP2Client(target, opts)
+}
+
+// Options provides additional hints and information for message
+// transmission.
+type Options struct {
+ // Last indicates whether this write is the last piece for
+ // this stream.
+ Last bool
+
+ // Delay is a hint to the transport implementation for whether
+ // the data could be buffered for a batching write. The
+ // Transport implementation may ignore the hint.
+ Delay bool
+}
+
+// CallHdr carries the information of a particular RPC.
+type CallHdr struct {
+ // Host specifies the peer's host.
+ Host string
+
+ // Method specifies the operation to perform.
+ Method string
+
+ // RecvCompress specifies the compression algorithm applied on
+ // inbound messages.
+ RecvCompress string
+
+ // SendCompress specifies the compression algorithm applied on
+ // outbound message.
+ SendCompress string
+
+ // Flush indicates whether a new stream command should be sent
+ // to the peer without waiting for the first data. This is
+ // only a hint. The transport may modify the flush decision
+ // for performance purposes.
+ Flush bool
+}
+
+// ClientTransport is the common interface for all gRPC client-side transport
+// implementations.
+type ClientTransport interface {
+ // Close tears down this transport. Once it returns, the transport
+ // should not be accessed any more. The caller must make sure this
+ // is called only once.
+ Close() error
+
+ // GracefulClose starts to tear down the transport. It stops accepting
+ // new RPCs and wait the completion of the pending RPCs.
+ GracefulClose() error
+
+ // Write sends the data for the given stream. A nil stream indicates
+ // the write is to be performed on the transport as a whole.
+ Write(s *Stream, data []byte, opts *Options) error
+
+ // NewStream creates a Stream for an RPC.
+ NewStream(ctx context.Context, callHdr *CallHdr) (*Stream, error)
+
+ // CloseStream clears the footprint of a stream when the stream is
+ // not needed any more. The err indicates the error incurred when
+ // CloseStream is called. Must be called when a stream is finished
+ // unless the associated transport is closing.
+ CloseStream(stream *Stream, err error)
+
+ // Error returns a channel that is closed when some I/O error
+ // happens. Typically the caller should have a goroutine to monitor
+ // this in order to take action (e.g., close the current transport
+ // and create a new one) in error case. It should not return nil
+ // once the transport is initiated.
+ Error() <-chan struct{}
+}
+
+// ServerTransport is the common interface for all gRPC server-side transport
+// implementations.
+//
+// Methods may be called concurrently from multiple goroutines, but
+// Write methods for a given Stream will be called serially.
+type ServerTransport interface {
+ // HandleStreams receives incoming streams using the given handler.
+ HandleStreams(func(*Stream))
+
+ // WriteHeader sends the header metadata for the given stream.
+ // WriteHeader may not be called on all streams.
+ WriteHeader(s *Stream, md metadata.MD) error
+
+ // Write sends the data for the given stream.
+ // Write may not be called on all streams.
+ Write(s *Stream, data []byte, opts *Options) error
+
+ // WriteStatus sends the status of a stream to the client.
+ // WriteStatus is the final call made on a stream and always
+ // occurs.
+ WriteStatus(s *Stream, statusCode codes.Code, statusDesc string) error
+
+ // Close tears down the transport. Once it is called, the transport
+ // should not be accessed any more. All the pending streams and their
+ // handlers will be terminated asynchronously.
+ Close() error
+
+ // RemoteAddr returns the remote network address.
+ RemoteAddr() net.Addr
+}
+
+// StreamErrorf creates an StreamError with the specified error code and description.
+func StreamErrorf(c codes.Code, format string, a ...interface{}) StreamError {
+ return StreamError{
+ Code: c,
+ Desc: fmt.Sprintf(format, a...),
+ }
+}
+
+// ConnectionErrorf creates an ConnectionError with the specified error description.
+func ConnectionErrorf(format string, a ...interface{}) ConnectionError {
+ return ConnectionError{
+ Desc: fmt.Sprintf(format, a...),
+ }
+}
+
+// ConnectionError is an error that results in the termination of the
+// entire connection and the retry of all the active streams.
+type ConnectionError struct {
+ Desc string
+}
+
+func (e ConnectionError) Error() string {
+ return fmt.Sprintf("connection error: desc = %q", e.Desc)
+}
+
+// Define some common ConnectionErrors.
+var ErrConnClosing = ConnectionError{Desc: "transport is closing"}
+
+// StreamError is an error that only affects one stream within a connection.
+type StreamError struct {
+ Code codes.Code
+ Desc string
+}
+
+func (e StreamError) Error() string {
+ return fmt.Sprintf("stream error: code = %d desc = %q", e.Code, e.Desc)
+}
+
+// ContextErr converts the error from context package into a StreamError.
+func ContextErr(err error) StreamError {
+ switch err {
+ case context.DeadlineExceeded:
+ return StreamErrorf(codes.DeadlineExceeded, "%v", err)
+ case context.Canceled:
+ return StreamErrorf(codes.Canceled, "%v", err)
+ }
+ panic(fmt.Sprintf("Unexpected error from context packet: %v", err))
+}
+
+// wait blocks until it can receive from ctx.Done, closing, or proceed.
+// If it receives from ctx.Done, it returns 0, the StreamError for ctx.Err.
+// If it receives from closing, it returns 0, ErrConnClosing.
+// If it receives from proceed, it returns the received integer, nil.
+func wait(ctx context.Context, closing <-chan struct{}, proceed <-chan int) (int, error) {
+ select {
+ case <-ctx.Done():
+ return 0, ContextErr(ctx.Err())
+ case <-closing:
+ return 0, ErrConnClosing
+ case i := <-proceed:
+ return i, nil
+ }
+}