perkeep/vendor/google.golang.org/grpc/clientconn.go

281 lines
9.4 KiB
Go

/*
*
* 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 grpc
import (
"context"
"errors"
"fmt"
"net/http"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/stats"
)
var (
// ErrClientConnClosing indicates that the operation is illegal because
// the ClientConn is closing.
ErrClientConnClosing = errors.New("grpc: the client connection is closing")
// ErrClientConnTimeout indicates that the ClientConn cannot establish the
// underlying connections within the specified timeout.
// DEPRECATED: Please use context.DeadlineExceeded instead. This error will be
// removed in Q1 2017.
ErrClientConnTimeout = errors.New("grpc: timed out when dialing")
// errNoTransportSecurity indicates that there is no transport security
// being set for ClientConn. Users should either set one or explicitly
// call WithInsecure DialOption to disable security.
errNoTransportSecurity = errors.New("grpc: no transport security set (use grpc.WithInsecure() explicitly or set credentials)")
// errTransportCredentialsMissing indicates that users want to transmit security
// information (e.g., oauth2 token) which requires secure connection on an insecure
// connection.
errTransportCredentialsMissing = errors.New("grpc: the credentials require transport level security (use grpc.WithTransportCredentials() to set)")
// errCredentialsConflict indicates that grpc.WithTransportCredentials()
// and grpc.WithInsecure() are both called for a connection.
errCredentialsConflict = errors.New("grpc: transport credentials are set for an insecure connection (grpc.WithTransportCredentials() and grpc.WithInsecure() are both called)")
)
type clientOptions struct {
codec Codec
cp Compressor
dc Decompressor
// All may be zero:
perRPCCreds []credentials.PerRPCCredentials
userAgent string
statsHandler stats.Handler
transportCreds credentials.TransportCredentials // only checked for non-nil for now
insecure bool // not TLS
}
// DialOption is a client option.
//
// Despite its name, it does not necessarily have anything to do with
// dialing.
//
// TODO: rename this.
type DialOption func(*clientOptions)
// WithCodec returns a DialOption which sets a codec for message marshaling and unmarshaling.
func WithCodec(c Codec) DialOption {
return func(o *clientOptions) {
o.codec = c
}
}
// WithCompressor returns a DialOption which sets a CompressorGenerator for generating message
// compressor.
func WithCompressor(cp Compressor) DialOption {
return func(o *clientOptions) {
o.cp = cp
}
}
// WithDecompressor returns a DialOption which sets a DecompressorGenerator for generating
// message decompressor.
func WithDecompressor(dc Decompressor) DialOption {
return func(o *clientOptions) {
o.dc = dc
}
}
// WithPerRPCCredentials returns an option which sets
// credentials which will place auth state on each outbound RPC.
func WithPerRPCCredentials(creds credentials.PerRPCCredentials) DialOption {
return func(o *clientOptions) {
o.perRPCCreds = append(o.perRPCCreds, creds)
}
}
// WithStatsHandler returns a DialOption that specifies the stats handler
// for all the RPCs and underlying network connections in this ClientConn.
func WithStatsHandler(h stats.Handler) DialOption {
return func(o *clientOptions) {
o.statsHandler = h
}
}
// WithUserAgent returns a DialOption that specifies a user agent string for all the RPCs.
func WithUserAgent(s string) DialOption {
return func(o *clientOptions) {
o.userAgent = s
}
}
// NewClient returns a new gRPC client for the provided target server.
// If the provided HTTP client is nil, http.DefaultClient is used.
// The target should be a URL scheme and authority, without a path.
// For example, "https://api.example.com" for TLS or "http://10.0.5.3:5000"
// for unencrypted HTTP/2.
//
// The returned type is named "ClientConn" for legacy reasons. It does
// not necessarily represent one actual connection. (It might be zero
// or multiple.)
func NewClient(hc *http.Client, target string, opts ...DialOption) (*ClientConn, error) {
if hc == nil {
hc = http.DefaultClient
}
if target == "" {
return nil, errors.New("NewClientConn: missing required target parameter")
}
cc := &ClientConn{
hc: hc,
target: target,
}
for _, opt := range opts {
opt(&cc.opts)
}
// Set defaults.
if cc.opts.codec == nil {
cc.opts.codec = protoCodec{}
}
return cc, nil
}
// ConnectivityState indicates the state of a client connection.
type ConnectivityState int
const (
// Idle indicates the ClientConn is idle.
Idle ConnectivityState = iota
// Connecting indicates the ClienConn is connecting.
Connecting
// Ready indicates the ClientConn is ready for work.
Ready
// TransientFailure indicates the ClientConn has seen a failure but expects to recover.
TransientFailure
// Shutdown indicates the ClientConn has started shutting down.
Shutdown
)
func (s ConnectivityState) String() string {
switch s {
case Idle:
return "IDLE"
case Connecting:
return "CONNECTING"
case Ready:
return "READY"
case TransientFailure:
return "TRANSIENT_FAILURE"
case Shutdown:
return "SHUTDOWN"
default:
panic(fmt.Sprintf("unknown connectivity state: %d", s))
}
}
// ClientConn is a gRPC client.
//
// Despite its name, it is not necessarily a single
// connection. Depending on its underlying transport, it could be
// using zero or multiple TCP or other connections, and changing over
// time.
type ClientConn struct {
target string // server URL prefix (scheme + authority, optional port), without path ("https://api.example.com"); use http:// for h2c
opts clientOptions
hc *http.Client
sc *ServiceConfig // TODO(bradfitz): support; may be nil for now
}
func (cc *ClientConn) getMethodConfig(method string) (m MethodConfig, ok bool) {
if cc.sc == nil {
return
}
m, ok = cc.sc.Methods[method]
return
}
// Close tears down the ClientConn and all underlying connections.
func (cc *ClientConn) Close() error {
// TODO(bradfitz): something? maybe just close some cancel
// chan that we then merge into all http Request's context
// with some new context.Context impl? And then do some
// http.Transport.CloseIdleConnections? But first research
// what callers of this actually expect. It's unclear.
return nil
}
// WithTransportCredentials is controls whether to use TLS or not for connections.
//
// Deprecated: this is only respected in a minimal form to let
// existing code in the wild work. Uew NewClient instead.
func WithTransportCredentials(creds credentials.TransportCredentials) DialOption {
return func(o *clientOptions) {
o.transportCreds = creds
}
}
// WithInsecure returns a DialOption which disables transport security for this ClientConn.
// WithInsecure is mutually exclusive with use of WithTransportCredentials or https
// endpoints.
func WithInsecure() DialOption {
return func(o *clientOptions) { o.insecure = true }
}
// DialContext is the old way to create a gRPC client.
//
// Deprecated: use NewClient instead.
func DialContext(ctx context.Context, target string, opts ...DialOption) (*ClientConn, error) {
var o clientOptions
for _, opt := range opts {
opt(&o)
}
if (o.transportCreds != nil) == o.insecure {
return nil, fmt.Errorf("only one of TransportCredentials or Insecure may be used")
}
if o.transportCreds != nil {
if o.transportCreds.Info().SecurityProtocol == "tls" {
target = "https://" + target
// TODO(bradfitz): care about the rest? use the interface?
// Not today. Prefer to delete the interace.
} else {
return nil, fmt.Errorf("unsupported TransportCredentials %+v", o.transportCreds.Info())
}
}
if o.insecure {
panic("TODO: implement insecure http2.Transport dialing")
}
return NewClient(nil, target, opts...)
}
// Dial is the old way to create a gRPC client.
//
// Deprecated: use NewClient instead. This only exists to let existing code
// in the wild work.
func Dial(target string, opts ...DialOption) (*ClientConn, error) {
return DialContext(context.Background(), target, opts...)
}