reconcile merge

Signed-off-by: Huamin Chen <hchen@redhat.com>
This commit is contained in:
Huamin Chen
2019-01-15 16:20:41 +00:00
parent 85b8415024
commit e46099a504
2425 changed files with 271763 additions and 40453 deletions

View File

@ -27,16 +27,42 @@ import (
"google.golang.org/grpc/grpclog"
)
// Logger is the global binary logger for the binary. One of this should be
// Logger is the global binary logger. It can be used to get binary logger for
// each method.
type Logger interface {
getMethodLogger(methodName string) *MethodLogger
}
// binLogger is the global binary logger for the binary. One of this should be
// built at init time from the configuration (environment varialbe or flags).
//
// It is used to get a methodLogger for each individual method.
var Logger *logger
var binLogger Logger
// SetLogger sets the binarg logger.
//
// Only call this at init time.
func SetLogger(l Logger) {
binLogger = l
}
// GetMethodLogger returns the methodLogger for the given methodName.
//
// methodName should be in the format of "/service/method".
//
// Each methodLogger returned by this method is a new instance. This is to
// generate sequence id within the call.
func GetMethodLogger(methodName string) *MethodLogger {
if binLogger == nil {
return nil
}
return binLogger.getMethodLogger(methodName)
}
func init() {
const envStr = "GRPC_BINARY_LOG_FILTER"
configStr := os.Getenv(envStr)
Logger = newLoggerFromConfigString(configStr)
binLogger = NewLoggerFromConfigString(configStr)
}
type methodLoggerConfig struct {
@ -113,13 +139,13 @@ func (l *logger) setBlacklist(method string) error {
return nil
}
// GetMethodLogger returns the methodLogger for the given methodName.
// getMethodLogger returns the methodLogger for the given methodName.
//
// methodName should be in the format of "/service/method".
//
// Each methodLogger returned by this method is a new instance. This is to
// generate sequence id within the call.
func (l *logger) GetMethodLogger(methodName string) *MethodLogger {
func (l *logger) getMethodLogger(methodName string) *MethodLogger {
s, m, err := parseMethodName(methodName)
if err != nil {
grpclog.Infof("binarylogging: failed to parse %q: %v", methodName, err)

File diff suppressed because it is too large Load Diff

View File

@ -78,12 +78,12 @@ func TestGetMethodLogger(t *testing.T) {
},
}
for _, tc := range testCases {
l := newLoggerFromConfigString(tc.in)
l := NewLoggerFromConfigString(tc.in)
if l == nil {
t.Errorf("in: %q, failed to create logger from config string", tc.in)
continue
}
ml := l.GetMethodLogger(tc.method)
ml := l.getMethodLogger(tc.method)
if ml == nil {
t.Errorf("in: %q, method logger is nil, want non-nil", tc.in)
continue
@ -134,12 +134,12 @@ func TestGetMethodLoggerOff(t *testing.T) {
},
}
for _, tc := range testCases {
l := newLoggerFromConfigString(tc.in)
l := NewLoggerFromConfigString(tc.in)
if l == nil {
t.Errorf("in: %q, failed to create logger from config string", tc.in)
continue
}
ml := l.GetMethodLogger(tc.method)
ml := l.getMethodLogger(tc.method)
if ml != nil {
t.Errorf("in: %q, method logger is non-nil, want nil", tc.in)
}

View File

@ -0,0 +1,42 @@
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// This file contains exported variables/functions that are exported for testing
// only.
//
// An ideal way for this would be to put those in a *_test.go but in binarylog
// package. But this doesn't work with staticcheck with go module. Error was:
// "MdToMetadataProto not declared by package binarylog". This could be caused
// by the way staticcheck looks for files for a certain package, which doesn't
// support *_test.go files.
//
// Move those to binary_test.go when staticcheck is fixed.
package binarylog
var (
// AllLogger is a logger that logs all headers/messages for all RPCs. It's
// for testing only.
AllLogger = NewLoggerFromConfigString("*")
// MdToMetadataProto converts metadata to a binary logging proto message.
// It's for testing only.
MdToMetadataProto = mdToMetadataProto
// AddrToProto converts an address to a binary logging proto message. It's
// for testing only.
AddrToProto = addrToProto
)

View File

@ -28,7 +28,8 @@ import (
"google.golang.org/grpc/grpclog"
)
// newLoggerFromConfigString reads the string and build a logger.
// NewLoggerFromConfigString reads the string and build a logger. It can be used
// to build a new logger and assign it to binarylog.Logger.
//
// Example filter config strings:
// - "" Nothing will be logged
@ -43,7 +44,10 @@ import (
//
// If two configs exist for one certain method or service, the one specified
// later overrides the privous config.
func newLoggerFromConfigString(s string) *logger {
func NewLoggerFromConfigString(s string) Logger {
if s == "" {
return nil
}
l := newEmptyLogger()
methods := strings.Split(s, ",")
for _, method := range methods {

View File

@ -34,7 +34,7 @@ func TestNewLoggerFromConfigString(t *testing.T) {
fullM2 = s1 + "/" + m2
)
c := fmt.Sprintf("*{h:1;m:2},%s{h},%s{m},%s{h;m}", s1+"/*", fullM1, fullM2)
l := newLoggerFromConfigString(c)
l := NewLoggerFromConfigString(c).(*logger)
if l.all.hdr != 1 || l.all.msg != 2 {
t.Errorf("l.all = %#v, want headerLen: 1, messageLen: 2", l.all)
@ -83,7 +83,7 @@ func TestNewLoggerFromConfigStringInvalid(t *testing.T) {
"*,*{h:1;m:1}",
}
for _, tc := range testCases {
l := newLoggerFromConfigString(tc)
l := NewLoggerFromConfigString(tc)
if l != nil {
t.Errorf("With config %q, want logger %v, got %v", tc, nil, l)
}

View File

@ -206,8 +206,8 @@ func (c *ServerHeader) toProto() *pb.GrpcLogEntry {
// ClientMessage configs the binary log entry to be a ClientMessage entry.
type ClientMessage struct {
OnClientSide bool
// Message should only be a proto.Message. Could add support for other
// message types in the future.
// Message can be a proto.Message or []byte. Other messages formats are not
// supported.
Message interface{}
}
@ -246,8 +246,8 @@ func (c *ClientMessage) toProto() *pb.GrpcLogEntry {
// ServerMessage configs the binary log entry to be a ServerMessage entry.
type ServerMessage struct {
OnClientSide bool
// Message should only be a proto.Message. Could add support for other
// message types in the future.
// Message can be a proto.Message or []byte. Other messages formats are not
// supported.
Message interface{}
}
@ -372,7 +372,7 @@ func (c *Cancel) toProto() *pb.GrpcLogEntry {
// omitted.
func metadataKeyOmit(key string) bool {
switch key {
case "lb-token", ":path", ":authority", "content-encoding", "user-agent", "te":
case "lb-token", ":path", ":authority", "content-encoding", "content-type", "user-agent", "te":
return true
case "grpc-trace-bin": // grpc-trace-bin is special because it's visiable to users.
return false

View File

@ -37,7 +37,7 @@ func TestLog(t *testing.T) {
ml := newMethodLogger(10, 10)
// Set sink to testing buffer.
buf := bytes.NewBuffer(nil)
ml.sink = NewWriterSink(buf)
ml.sink = newWriterSink(buf)
addr := "1.2.3.4"
port := 790
@ -337,7 +337,7 @@ func TestLog(t *testing.T) {
tc.want.SequenceIdWithinCall = uint64(i + 1)
ml.Log(tc.config)
inSink := new(pb.GrpcLogEntry)
if err := proto.Unmarshal(buf.Bytes(), inSink); err != nil {
if err := proto.Unmarshal(buf.Bytes()[4:], inSink); err != nil {
t.Errorf("failed to unmarshal bytes in sink to proto: %v", err)
continue
}

View File

@ -19,7 +19,13 @@
package binarylog
import (
"bufio"
"encoding/binary"
"fmt"
"io"
"io/ioutil"
"sync"
"time"
"github.com/golang/protobuf/proto"
pb "google.golang.org/grpc/binarylog/grpc_binarylog_v1"
@ -34,20 +40,34 @@ var (
//
// Not thread safe. Only set during initialization.
func SetDefaultSink(s Sink) {
if defaultSink != nil {
defaultSink.Close()
}
defaultSink = s
}
// Sink writes log entry into the binary log sink.
type Sink interface {
Write(*pb.GrpcLogEntry)
// Write will be called to write the log entry into the sink.
//
// It should be thread-safe so it can be called in parallel.
Write(*pb.GrpcLogEntry) error
// Close will be called when the Sink is replaced by a new Sink.
Close() error
}
type noopSink struct{}
func (ns *noopSink) Write(*pb.GrpcLogEntry) {}
func (ns *noopSink) Write(*pb.GrpcLogEntry) error { return nil }
func (ns *noopSink) Close() error { return nil }
// NewWriterSink creates a binary log sink with the given writer.
func NewWriterSink(w io.Writer) Sink {
// newWriterSink creates a binary log sink with the given writer.
//
// Write() marshalls the proto message and writes it to the given writer. Each
// message is prefixed with a 4 byte big endian unsigned integer as the length.
//
// No buffer is done, Close() doesn't try to close the writer.
func newWriterSink(w io.Writer) *writerSink {
return &writerSink{out: w}
}
@ -55,10 +75,88 @@ type writerSink struct {
out io.Writer
}
func (fs *writerSink) Write(e *pb.GrpcLogEntry) {
func (ws *writerSink) Write(e *pb.GrpcLogEntry) error {
b, err := proto.Marshal(e)
if err != nil {
grpclog.Infof("binary logging: failed to marshal proto message: %v", err)
}
fs.out.Write(b)
hdr := make([]byte, 4)
binary.BigEndian.PutUint32(hdr, uint32(len(b)))
if _, err := ws.out.Write(hdr); err != nil {
return err
}
if _, err := ws.out.Write(b); err != nil {
return err
}
return nil
}
func (ws *writerSink) Close() error { return nil }
type bufWriteCloserSink struct {
mu sync.Mutex
closer io.Closer
out *writerSink // out is built on buf.
buf *bufio.Writer // buf is kept for flush.
writeStartOnce sync.Once
writeTicker *time.Ticker
}
func (fs *bufWriteCloserSink) Write(e *pb.GrpcLogEntry) error {
// Start the write loop when Write is called.
fs.writeStartOnce.Do(fs.startFlushGoroutine)
fs.mu.Lock()
if err := fs.out.Write(e); err != nil {
fs.mu.Unlock()
return err
}
fs.mu.Unlock()
return nil
}
const (
bufFlushDuration = 60 * time.Second
)
func (fs *bufWriteCloserSink) startFlushGoroutine() {
fs.writeTicker = time.NewTicker(bufFlushDuration)
go func() {
for range fs.writeTicker.C {
fs.mu.Lock()
fs.buf.Flush()
fs.mu.Unlock()
}
}()
}
func (fs *bufWriteCloserSink) Close() error {
if fs.writeTicker != nil {
fs.writeTicker.Stop()
}
fs.mu.Lock()
fs.buf.Flush()
fs.closer.Close()
fs.out.Close()
fs.mu.Unlock()
return nil
}
func newBufWriteCloserSink(o io.WriteCloser) Sink {
bufW := bufio.NewWriter(o)
return &bufWriteCloserSink{
closer: o,
out: newWriterSink(bufW),
buf: bufW,
}
}
// NewTempFileSink creates a temp file and returns a Sink that writes to this
// file.
func NewTempFileSink() (Sink, error) {
tempFile, err := ioutil.TempFile("/tmp", "grpcgo_binarylog_*.txt")
if err != nil {
return nil, fmt.Errorf("failed to create temp file: %v", err)
}
return newBufWriteCloserSink(tempFile), nil
}

View File

@ -558,7 +558,7 @@ func (c *channelMap) GetServerSockets(id int64, startID int64) ([]*SocketMetric,
ids = append(ids, k)
}
sort.Sort(int64Slice(ids))
idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= id })
idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= startID })
count := 0
var end bool
for i, v := range ids[idx:] {
@ -601,8 +601,11 @@ func (c *channelMap) GetChannel(id int64) *ChannelMetric {
}
cm.NestedChans = copyMap(cn.nestedChans)
cm.SubChans = copyMap(cn.subChans)
// cn.c can be set to &dummyChannel{} when deleteSelfFromMap is called. Save a copy of cn.c when
// holding the lock to prevent potential data race.
chanCopy := cn.c
c.mu.RUnlock()
cm.ChannelData = cn.c.ChannelzMetric()
cm.ChannelData = chanCopy.ChannelzMetric()
cm.ID = cn.id
cm.RefName = cn.refName
cm.Trace = cn.trace.dumpData()
@ -620,8 +623,11 @@ func (c *channelMap) GetSubChannel(id int64) *SubChannelMetric {
return nil
}
cm.Sockets = copyMap(sc.sockets)
// sc.c can be set to &dummyChannel{} when deleteSelfFromMap is called. Save a copy of sc.c when
// holding the lock to prevent potential data race.
chanCopy := sc.c
c.mu.RUnlock()
cm.ChannelData = sc.c.ChannelzMetric()
cm.ChannelData = chanCopy.ChannelzMetric()
cm.ID = sc.id
cm.RefName = sc.refName
cm.Trace = sc.trace.dumpData()

View File

@ -1,4 +1,4 @@
// +build !appengine,go1.7
// +build !appengine
/*
*

View File

@ -1,4 +1,4 @@
// +build !linux appengine !go1.7
// +build !linux appengine
/*
*

View File

@ -1,4 +1,4 @@
// +build linux,go1.9,!appengine
// +build linux,!appengine
/*
*

View File

@ -1,4 +1,4 @@
// +build !linux !go1.9 appengine
// +build !linux appengine
/*
*

View File

@ -25,11 +25,45 @@ import (
)
const (
prefix = "GRPC_GO_"
retryStr = prefix + "RETRY"
prefix = "GRPC_GO_"
retryStr = prefix + "RETRY"
requireHandshakeStr = prefix + "REQUIRE_HANDSHAKE"
)
// RequireHandshakeSetting describes the settings for handshaking.
type RequireHandshakeSetting int
const (
// RequireHandshakeHybrid (default, deprecated) indicates to wait for
// handshake before considering a connection ready, but wait before
// considering successful.
RequireHandshakeHybrid RequireHandshakeSetting = iota
// RequireHandshakeOn (default after the 1.17 release) indicates to wait
// for handshake before considering a connection ready/successful.
RequireHandshakeOn
// RequireHandshakeOff indicates to not wait for handshake before
// considering a connection ready/successful.
RequireHandshakeOff
)
var (
// Retry is set if retry is explicitly enabled via "GRPC_GO_RETRY=on".
Retry = strings.EqualFold(os.Getenv(retryStr), "on")
// RequireHandshake is set based upon the GRPC_GO_REQUIRE_HANDSHAKE
// environment variable.
//
// Will be removed after the 1.18 release.
RequireHandshake RequireHandshakeSetting
)
func init() {
switch strings.ToLower(os.Getenv(requireHandshakeStr)) {
case "on":
RequireHandshake = RequireHandshakeOn
case "off":
RequireHandshake = RequireHandshakeOff
case "hybrid":
// Will be removed after the 1.17 release.
RequireHandshake = RequireHandshakeHybrid
}
}

View File

@ -20,12 +20,16 @@
// the sync package.
package grpcsync
import "sync"
import (
"sync"
"sync/atomic"
)
// Event represents a one-time event that may occur in the future.
type Event struct {
c chan struct{}
o sync.Once
fired int32
c chan struct{}
o sync.Once
}
// Fire causes e to complete. It is safe to call multiple times, and
@ -34,6 +38,7 @@ type Event struct {
func (e *Event) Fire() bool {
ret := false
e.o.Do(func() {
atomic.StoreInt32(&e.fired, 1)
close(e.c)
ret = true
})
@ -47,12 +52,7 @@ func (e *Event) Done() <-chan struct{} {
// HasFired returns true if Fire has been called.
func (e *Event) HasFired() bool {
select {
case <-e.c:
return true
default:
return false
}
return atomic.LoadInt32(&e.fired) == 1
}
// NewEvent returns a new, ready-to-use Event.

View File

@ -20,7 +20,7 @@
// symbols to avoid circular dependencies.
package internal
import "golang.org/x/net/context"
import "context"
var (
// WithContextDialer is exported by clientconn.go

View File

@ -1,4 +1,4 @@
// +build !appengine,go1.7
// +build !appengine
/*
*
@ -23,7 +23,10 @@
package syscall
import (
"fmt"
"net"
"syscall"
"time"
"golang.org/x/sys/unix"
"google.golang.org/grpc/grpclog"
@ -65,3 +68,47 @@ func CPUTimeDiff(first *Rusage, latest *Rusage) (float64, float64) {
return uTimeElapsed, sTimeElapsed
}
// SetTCPUserTimeout sets the TCP user timeout on a connection's socket
func SetTCPUserTimeout(conn net.Conn, timeout time.Duration) error {
tcpconn, ok := conn.(*net.TCPConn)
if !ok {
// not a TCP connection. exit early
return nil
}
rawConn, err := tcpconn.SyscallConn()
if err != nil {
return fmt.Errorf("error getting raw connection: %v", err)
}
err = rawConn.Control(func(fd uintptr) {
err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_USER_TIMEOUT, int(timeout/time.Millisecond))
})
if err != nil {
return fmt.Errorf("error setting option on socket: %v", err)
}
return nil
}
// GetTCPUserTimeout gets the TCP user timeout on a connection's socket
func GetTCPUserTimeout(conn net.Conn) (opt int, err error) {
tcpconn, ok := conn.(*net.TCPConn)
if !ok {
err = fmt.Errorf("conn is not *net.TCPConn. got %T", conn)
return
}
rawConn, err := tcpconn.SyscallConn()
if err != nil {
err = fmt.Errorf("error getting raw connection: %v", err)
return
}
err = rawConn.Control(func(fd uintptr) {
opt, err = syscall.GetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_USER_TIMEOUT)
})
if err != nil {
err = fmt.Errorf("error getting option on socket: %v", err)
return
}
return
}

View File

@ -1,4 +1,4 @@
// +build !linux appengine !go1.7
// +build !linux appengine
/*
*
@ -20,7 +20,12 @@
package syscall
import "google.golang.org/grpc/grpclog"
import (
"net"
"time"
"google.golang.org/grpc/grpclog"
)
func init() {
grpclog.Info("CPU time info is unavailable on non-linux or appengine environment.")
@ -45,3 +50,14 @@ func GetRusage() (rusage *Rusage) {
func CPUTimeDiff(first *Rusage, latest *Rusage) (float64, float64) {
return 0, 0
}
// SetTCPUserTimeout is a no-op function under non-linux or appengine environments
func SetTCPUserTimeout(conn net.Conn, timeout time.Duration) error {
return nil
}
// GetTCPUserTimeout is a no-op function under non-linux or appengine environments
// a negative return value indicates the operation is not supported
func GetTCPUserTimeout(conn net.Conn) (int, error) {
return -1, nil
}

View File

@ -16,6 +16,7 @@
*
*/
// Package testutils contains testing helpers.
package testutils
import (

View File

@ -24,9 +24,10 @@ import (
)
const (
// bdpLimit is the maximum value the flow control windows
// will be increased to.
bdpLimit = (1 << 20) * 4
// bdpLimit is the maximum value the flow control windows will be increased
// to. TCP typically limits this to 4MB, but some systems go up to 16MB.
// Since this is only a limit, it is safe to make it optimistic.
bdpLimit = (1 << 20) * 16
// alpha is a constant factor used to keep a moving average
// of RTTs.
alpha = 0.9

View File

@ -1,52 +0,0 @@
// +build go1.6,!go1.7
/*
*
* Copyright 2016 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package transport
import (
"net"
"net/http"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"golang.org/x/net/context"
)
// dialContext connects to the address on the named network.
func dialContext(ctx context.Context, network, address string) (net.Conn, error) {
return (&net.Dialer{Cancel: ctx.Done()}).Dial(network, address)
}
// ContextErr converts the error from context package into a status error.
func ContextErr(err error) error {
switch err {
case context.DeadlineExceeded:
return status.Error(codes.DeadlineExceeded, err.Error())
case context.Canceled:
return status.Error(codes.Canceled, err.Error())
}
return status.Errorf(codes.Internal, "Unexpected error from context packet: %v", err)
}
// contextFromRequest returns a background context.
func contextFromRequest(r *http.Request) context.Context {
return context.Background()
}

View File

@ -1,53 +0,0 @@
// +build go1.7
/*
*
* Copyright 2016 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package transport
import (
"context"
"net"
"net/http"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
netctx "golang.org/x/net/context"
)
// dialContext connects to the address on the named network.
func dialContext(ctx context.Context, network, address string) (net.Conn, error) {
return (&net.Dialer{}).DialContext(ctx, network, address)
}
// ContextErr converts the error from context package into a status error.
func ContextErr(err error) error {
switch err {
case context.DeadlineExceeded, netctx.DeadlineExceeded:
return status.Error(codes.DeadlineExceeded, err.Error())
case context.Canceled, netctx.Canceled:
return status.Error(codes.Canceled, err.Error())
}
return status.Errorf(codes.Internal, "Unexpected error from context packet: %v", err)
}
// contextFromRequest returns a context from the HTTP Request.
func contextFromRequest(r *http.Request) context.Context {
return r.Context()
}

View File

@ -24,6 +24,7 @@
package transport
import (
"context"
"errors"
"fmt"
"io"
@ -34,7 +35,6 @@ import (
"time"
"github.com/golang/protobuf/proto"
"golang.org/x/net/context"
"golang.org/x/net/http2"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
@ -307,7 +307,7 @@ func (ht *serverHandlerTransport) WriteHeader(s *Stream, md metadata.MD) error {
func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream), traceCtx func(context.Context, string) context.Context) {
// With this transport type there will be exactly 1 stream: this HTTP request.
ctx := contextFromRequest(ht.req)
ctx := ht.req.Context()
var cancel context.CancelFunc
if ht.timeoutSet {
ctx, cancel = context.WithTimeout(ctx, ht.timeout)

View File

@ -19,6 +19,7 @@
package transport
import (
"context"
"errors"
"fmt"
"io"
@ -32,7 +33,6 @@ import (
"github.com/golang/protobuf/proto"
dpb "github.com/golang/protobuf/ptypes/duration"
"golang.org/x/net/context"
epb "google.golang.org/genproto/googleapis/rpc/errdetails"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"

View File

@ -19,6 +19,8 @@
package transport
import (
"context"
"fmt"
"io"
"math"
"net"
@ -28,13 +30,13 @@ import (
"sync/atomic"
"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/internal/channelz"
"google.golang.org/grpc/internal/syscall"
"google.golang.org/grpc/keepalive"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
@ -121,7 +123,7 @@ func dial(ctx context.Context, fn func(context.Context, string) (net.Conn, error
if fn != nil {
return fn(ctx, addr)
}
return dialContext(ctx, "tcp", addr)
return (&net.Dialer{}).DialContext(ctx, "tcp", addr)
}
func isTemporary(err error) bool {
@ -165,6 +167,21 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr TargetInfo, opts Conne
conn.Close()
}
}(conn)
kp := opts.KeepaliveParams
// Validate keepalive parameters.
if kp.Time == 0 {
kp.Time = defaultClientKeepaliveTime
}
if kp.Timeout == 0 {
kp.Timeout = defaultClientKeepaliveTimeout
}
keepaliveEnabled := false
if kp.Time != infinity {
if err = syscall.SetTCPUserTimeout(conn, kp.Timeout); err != nil {
return nil, connectionErrorf(false, err, "transport: failed to set TCP_USER_TIMEOUT: %v", err)
}
keepaliveEnabled = true
}
var (
isSecure bool
authInfo credentials.AuthInfo
@ -188,14 +205,6 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr TargetInfo, opts Conne
}
isSecure = true
}
kp := opts.KeepaliveParams
// Validate keepalive parameters.
if kp.Time == 0 {
kp.Time = defaultClientKeepaliveTime
}
if kp.Timeout == 0 {
kp.Timeout = defaultClientKeepaliveTimeout
}
dynamicWindow := true
icwz := int32(initialWindowSize)
if opts.InitialConnWindowSize >= defaultWindowSize {
@ -239,6 +248,7 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr TargetInfo, opts Conne
czData: new(channelzData),
onGoAway: onGoAway,
onClose: onClose,
keepaliveEnabled: keepaliveEnabled,
}
t.controlBuf = newControlBuffer(t.ctxDone)
if opts.InitialWindowSize >= defaultWindowSize {
@ -265,10 +275,9 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr TargetInfo, opts Conne
t.statsHandler.HandleConn(t.ctx, connBegin)
}
if channelz.IsOn() {
t.channelzID = channelz.RegisterNormalSocket(t, opts.ChannelzParentID, "")
t.channelzID = channelz.RegisterNormalSocket(t, opts.ChannelzParentID, fmt.Sprintf("%s -> %s", t.localAddr, t.remoteAddr))
}
if t.kp.Time != infinity {
t.keepaliveEnabled = true
if t.keepaliveEnabled {
go t.keepalive()
}
// Start the reader goroutine for incoming message. Each transport has

View File

@ -20,6 +20,7 @@ package transport
import (
"bytes"
"context"
"errors"
"fmt"
"io"
@ -31,7 +32,6 @@ import (
"time"
"github.com/golang/protobuf/proto"
"golang.org/x/net/context"
"golang.org/x/net/http2"
"golang.org/x/net/http2/hpack"
@ -237,7 +237,7 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err
t.stats.HandleConn(t.ctx, connBegin)
}
if channelz.IsOn() {
t.channelzID = channelz.RegisterNormalSocket(t, config.ChannelzParentID, "")
t.channelzID = channelz.RegisterNormalSocket(t, config.ChannelzParentID, fmt.Sprintf("%s -> %s", t.remoteAddr, t.localAddr))
}
t.framer.writer.Flush()

View File

@ -22,6 +22,7 @@
package transport
import (
"context"
"errors"
"fmt"
"io"
@ -29,7 +30,6 @@ import (
"sync"
"sync/atomic"
"golang.org/x/net/context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/keepalive"
@ -186,8 +186,12 @@ type Stream struct {
headerDone uint32 // set when headerChan is closed. Used to avoid closing headerChan multiple times.
// hdrMu protects header and trailer metadata on the server-side.
hdrMu sync.Mutex
header metadata.MD // the received header metadata.
hdrMu sync.Mutex
// On client side, header keeps the received header metadata.
//
// On server side, header keeps the header set by SetHeader(). The complete
// header will merged into this after t.WriteHeader() is called.
header metadata.MD
trailer metadata.MD // the key-value map of trailer metadata.
noHeaders bool // set if the client never received headers (set only after the stream is done).
@ -266,10 +270,19 @@ func (s *Stream) Done() <-chan struct{} {
return s.done
}
// 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 canceled/expired.
// Header returns the header metadata of the stream.
//
// On client side, it 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 canceled/expired.
//
// On server side, it returns the out header after t.WriteHeader is called.
func (s *Stream) Header() (metadata.MD, error) {
if s.headerChan == nil && s.header != nil {
// On server side, return the header in stream. It will be the out
// header after t.WriteHeader is called.
return s.header.Copy(), nil
}
err := s.waitOnHeader()
// Even if the stream is closed, header is returned if available.
select {
@ -710,3 +723,14 @@ type channelzData struct {
lastMsgSentTime int64
lastMsgRecvTime int64
}
// ContextErr converts the error from context package into a status error.
func ContextErr(err error) error {
switch err {
case context.DeadlineExceeded:
return status.Error(codes.DeadlineExceeded, err.Error())
case context.Canceled:
return status.Error(codes.Canceled, err.Error())
}
return status.Errorf(codes.Internal, "Unexpected error from context packet: %v", err)
}

View File

@ -21,6 +21,7 @@ package transport
import (
"bufio"
"bytes"
"context"
"encoding/binary"
"errors"
"fmt"
@ -36,11 +37,11 @@ import (
"testing"
"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/internal/leakcheck"
"google.golang.org/grpc/internal/syscall"
"google.golang.org/grpc/keepalive"
"google.golang.org/grpc/status"
)
@ -2027,13 +2028,10 @@ func setUpHTTPStatusTest(t *testing.T, httpStatus int, wh writeHeaders) (*Stream
wh: wh,
}
server.start(t, lis)
// TODO(deklerk): we can `defer cancel()` here after we drop Go 1.6 support. Until then,
// doing a `defer cancel()` could cause the dialer to become broken:
// https://github.com/golang/go/issues/15078, https://github.com/golang/go/issues/15035
connectCtx, cancel := context.WithDeadline(context.Background(), time.Now().Add(2*time.Second))
defer cancel()
client, err := newHTTP2Client(connectCtx, context.Background(), TargetInfo{Addr: lis.Addr().String()}, ConnectOptions{}, func() {}, func(GoAwayReason) {}, func() {})
if err != nil {
cancel() // Do not cancel in success path.
lis.Close()
t.Fatalf("Error creating client. Err: %v", err)
}
@ -2317,3 +2315,61 @@ func TestHeaderTblSize(t *testing.T) {
t.Fatalf("expected len(limits) = 2 within 10s, got != 2")
}
}
// TestTCPUserTimeout tests that the TCP_USER_TIMEOUT socket option is set to the
// keepalive timeout, as detailed in proposal A18
func TestTCPUserTimeout(t *testing.T) {
tests := []struct {
time time.Duration
timeout time.Duration
}{
{
10 * time.Second,
10 * time.Second,
},
{
0,
0,
},
}
for _, tt := range tests {
server, client, cancel := setUpWithOptions(
t,
0,
&ServerConfig{
KeepaliveParams: keepalive.ServerParameters{
Time: tt.timeout,
Timeout: tt.timeout,
},
},
normal,
ConnectOptions{
KeepaliveParams: keepalive.ClientParameters{
Time: tt.time,
Timeout: tt.timeout,
},
},
)
defer cancel()
defer server.stop()
defer client.Close()
stream, err := client.NewStream(context.Background(), &CallHdr{})
if err != nil {
t.Fatalf("Client failed to create RPC request: %v", err)
}
client.closeStream(stream, io.EOF, true, http2.ErrCodeCancel, nil, nil, false)
opt, err := syscall.GetTCPUserTimeout(client.conn)
if err != nil {
t.Fatalf("GetTCPUserTimeout error: %v", err)
}
if opt < 0 {
t.Skipf("skipping test on unsupported environment")
}
if timeoutMS := int(tt.timeout / time.Millisecond); timeoutMS != opt {
t.Fatalf("wrong TCP_USER_TIMEOUT set on conn. expected %d. got %d",
timeoutMS, opt)
}
}
}