Re-add support for Go1.6 (#1603)
diff --git a/.travis.yml b/.travis.yml
index 22bf250..413881a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,7 @@
language: go
go:
+ - 1.6.x
- 1.7.x
- 1.8.x
- 1.9.x
diff --git a/README.md b/README.md
index 12ee08e..118327b 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,8 @@
Prerequisites
-------------
-This requires Go 1.7 or later.
+This requires Go 1.6 or later. Go 1.7 will be required as of the next gRPC-Go
+release (1.8).
Constraints
-----------
diff --git a/benchmark/benchmark16_test.go b/benchmark/benchmark16_test.go
new file mode 100644
index 0000000..fc33e80
--- /dev/null
+++ b/benchmark/benchmark16_test.go
@@ -0,0 +1,112 @@
+// +build go1.6,!go1.7
+
+/*
+ *
+ * Copyright 2017 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 benchmark
+
+import (
+ "os"
+ "testing"
+
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/benchmark/stats"
+)
+
+func BenchmarkClientStreamc1(b *testing.B) {
+ grpc.EnableTracing = true
+ runStream(b, stats.Features{"", true, 0, 0, 0, 1, 1, 1, false})
+}
+
+func BenchmarkClientStreamc8(b *testing.B) {
+ grpc.EnableTracing = true
+ runStream(b, stats.Features{"", true, 0, 0, 0, 8, 1, 1, false})
+}
+
+func BenchmarkClientStreamc64(b *testing.B) {
+ grpc.EnableTracing = true
+ runStream(b, stats.Features{"", true, 0, 0, 0, 64, 1, 1, false})
+}
+
+func BenchmarkClientStreamc512(b *testing.B) {
+ grpc.EnableTracing = true
+ runStream(b, stats.Features{"", true, 0, 0, 0, 512, 1, 1, false})
+}
+func BenchmarkClientUnaryc1(b *testing.B) {
+ grpc.EnableTracing = true
+ runStream(b, stats.Features{"", true, 0, 0, 0, 1, 1, 1, false})
+}
+
+func BenchmarkClientUnaryc8(b *testing.B) {
+ grpc.EnableTracing = true
+ runStream(b, stats.Features{"", true, 0, 0, 0, 8, 1, 1, false})
+}
+
+func BenchmarkClientUnaryc64(b *testing.B) {
+ grpc.EnableTracing = true
+ runStream(b, stats.Features{"", true, 0, 0, 0, 64, 1, 1, false})
+}
+
+func BenchmarkClientUnaryc512(b *testing.B) {
+ grpc.EnableTracing = true
+ runStream(b, stats.Features{"", true, 0, 0, 0, 512, 1, 1, false})
+}
+
+func BenchmarkClientStreamNoTracec1(b *testing.B) {
+ grpc.EnableTracing = false
+ runStream(b, stats.Features{"", false, 0, 0, 0, 1, 1, 1, false})
+}
+
+func BenchmarkClientStreamNoTracec8(b *testing.B) {
+ grpc.EnableTracing = false
+ runStream(b, stats.Features{"", false, 0, 0, 0, 8, 1, 1, false})
+}
+
+func BenchmarkClientStreamNoTracec64(b *testing.B) {
+ grpc.EnableTracing = false
+ runStream(b, stats.Features{"", false, 0, 0, 0, 64, 1, 1, false})
+}
+
+func BenchmarkClientStreamNoTracec512(b *testing.B) {
+ grpc.EnableTracing = false
+ runStream(b, stats.Features{"", false, 0, 0, 0, 512, 1, 1, false})
+}
+func BenchmarkClientUnaryNoTracec1(b *testing.B) {
+ grpc.EnableTracing = false
+ runStream(b, stats.Features{"", false, 0, 0, 0, 1, 1, 1, false})
+}
+
+func BenchmarkClientUnaryNoTracec8(b *testing.B) {
+ grpc.EnableTracing = false
+ runStream(b, stats.Features{"", false, 0, 0, 0, 8, 1, 1, false})
+}
+
+func BenchmarkClientUnaryNoTracec64(b *testing.B) {
+ grpc.EnableTracing = false
+ runStream(b, stats.Features{"", false, 0, 0, 0, 64, 1, 1, false})
+}
+
+func BenchmarkClientUnaryNoTracec512(b *testing.B) {
+ grpc.EnableTracing = false
+ runStream(b, stats.Features{"", false, 0, 0, 0, 512, 1, 1, false})
+ runStream(b, stats.Features{"", false, 0, 0, 0, 512, 1, 1, false})
+}
+
+func TestMain(m *testing.M) {
+ os.Exit(stats.RunTestMain(m))
+}
diff --git a/benchmark/benchmark_test.go b/benchmark/benchmark17_test.go
similarity index 100%
rename from benchmark/benchmark_test.go
rename to benchmark/benchmark17_test.go
diff --git a/clientconn.go b/clientconn.go
index 3269de0..21541b6 100644
--- a/clientconn.go
+++ b/clientconn.go
@@ -383,7 +383,7 @@
if cc.dopts.copts.Dialer == nil {
cc.dopts.copts.Dialer = newProxyDialer(
func(ctx context.Context, addr string) (net.Conn, error) {
- return (&net.Dialer{}).DialContext(ctx, "tcp", addr)
+ return dialContext(ctx, "tcp", addr)
},
)
}
diff --git a/examples/README.md b/examples/README.md
index 6d9175c..0bb2c6b 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -8,7 +8,7 @@
PREREQUISITES
-------------
-- This requires Go 1.7 or later
+- This requires Go 1.6 or later
- Requires that [GOPATH is set](https://golang.org/doc/code.html#GOPATH)
```
diff --git a/go16.go b/go16.go
new file mode 100644
index 0000000..f3dbf21
--- /dev/null
+++ b/go16.go
@@ -0,0 +1,98 @@
+// +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 grpc
+
+import (
+ "fmt"
+ "io"
+ "net"
+ "net/http"
+ "os"
+
+ "golang.org/x/net/context"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+ "google.golang.org/grpc/transport"
+)
+
+// 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)
+}
+
+func sendHTTPRequest(ctx context.Context, req *http.Request, conn net.Conn) error {
+ req.Cancel = ctx.Done()
+ if err := req.Write(conn); err != nil {
+ return fmt.Errorf("failed to write the HTTP request: %v", err)
+ }
+ return nil
+}
+
+// toRPCErr converts an error into an error from the status package.
+func toRPCErr(err error) error {
+ if _, ok := status.FromError(err); ok {
+ return err
+ }
+ switch e := err.(type) {
+ case transport.StreamError:
+ return status.Error(e.Code, e.Desc)
+ case transport.ConnectionError:
+ return status.Error(codes.Unavailable, e.Desc)
+ default:
+ switch err {
+ case context.DeadlineExceeded:
+ return status.Error(codes.DeadlineExceeded, err.Error())
+ case context.Canceled:
+ return status.Error(codes.Canceled, err.Error())
+ case ErrClientConnClosing:
+ return status.Error(codes.FailedPrecondition, err.Error())
+ }
+ }
+ return status.Error(codes.Unknown, err.Error())
+}
+
+// convertCode converts a standard Go error into its canonical code. Note that
+// this is only used to translate the error returned by the server applications.
+func convertCode(err error) codes.Code {
+ switch err {
+ case nil:
+ return codes.OK
+ case io.EOF:
+ return codes.OutOfRange
+ case io.ErrClosedPipe, io.ErrNoProgress, io.ErrShortBuffer, io.ErrShortWrite, io.ErrUnexpectedEOF:
+ return codes.FailedPrecondition
+ case os.ErrInvalid:
+ return codes.InvalidArgument
+ case context.Canceled:
+ return codes.Canceled
+ case context.DeadlineExceeded:
+ return codes.DeadlineExceeded
+ }
+ switch {
+ case os.IsExist(err):
+ return codes.AlreadyExists
+ case os.IsNotExist(err):
+ return codes.NotFound
+ case os.IsPermission(err):
+ return codes.PermissionDenied
+ }
+ return codes.Unknown
+}
diff --git a/go17.go b/go17.go
new file mode 100644
index 0000000..de23098
--- /dev/null
+++ b/go17.go
@@ -0,0 +1,99 @@
+// +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 grpc
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "net"
+ "net/http"
+ "os"
+
+ netctx "golang.org/x/net/context"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+ "google.golang.org/grpc/transport"
+)
+
+// 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)
+}
+
+func sendHTTPRequest(ctx context.Context, req *http.Request, conn net.Conn) error {
+ req = req.WithContext(ctx)
+ if err := req.Write(conn); err != nil {
+ return fmt.Errorf("failed to write the HTTP request: %v", err)
+ }
+ return nil
+}
+
+// toRPCErr converts an error into an error from the status package.
+func toRPCErr(err error) error {
+ if _, ok := status.FromError(err); ok {
+ return err
+ }
+ switch e := err.(type) {
+ case transport.StreamError:
+ return status.Error(e.Code, e.Desc)
+ case transport.ConnectionError:
+ return status.Error(codes.Unavailable, e.Desc)
+ default:
+ 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())
+ case ErrClientConnClosing:
+ return status.Error(codes.FailedPrecondition, err.Error())
+ }
+ }
+ return status.Error(codes.Unknown, err.Error())
+}
+
+// convertCode converts a standard Go error into its canonical code. Note that
+// this is only used to translate the error returned by the server applications.
+func convertCode(err error) codes.Code {
+ switch err {
+ case nil:
+ return codes.OK
+ case io.EOF:
+ return codes.OutOfRange
+ case io.ErrClosedPipe, io.ErrNoProgress, io.ErrShortBuffer, io.ErrShortWrite, io.ErrUnexpectedEOF:
+ return codes.FailedPrecondition
+ case os.ErrInvalid:
+ return codes.InvalidArgument
+ case context.Canceled, netctx.Canceled:
+ return codes.Canceled
+ case context.DeadlineExceeded, netctx.DeadlineExceeded:
+ return codes.DeadlineExceeded
+ }
+ switch {
+ case os.IsExist(err):
+ return codes.AlreadyExists
+ case os.IsNotExist(err):
+ return codes.NotFound
+ case os.IsPermission(err):
+ return codes.PermissionDenied
+ }
+ return codes.Unknown
+}
diff --git a/naming/go17.go b/naming/go17.go
index 8bdf21e..a537b08 100644
--- a/naming/go17.go
+++ b/naming/go17.go
@@ -1,4 +1,4 @@
-// +build go1.7, !go1.8
+// +build go1.6, !go1.8
/*
*
diff --git a/naming/go17_test.go b/naming/go17_test.go
index d1de221..db39b9a 100644
--- a/naming/go17_test.go
+++ b/naming/go17_test.go
@@ -1,4 +1,4 @@
-// +build go1.7, !go1.8
+// +build go1.6, !go1.8
/*
*
diff --git a/proxy.go b/proxy.go
index 3e17efe..2d40236 100644
--- a/proxy.go
+++ b/proxy.go
@@ -82,8 +82,7 @@
Header: map[string][]string{"User-Agent": {grpcUA}},
})
- req = req.WithContext(ctx)
- if err := req.Write(conn); err != nil {
+ if err := sendHTTPRequest(ctx, req, conn); err != nil {
return nil, fmt.Errorf("failed to write the HTTP request: %v", err)
}
diff --git a/rpc_util.go b/rpc_util.go
index d1f42fa..d3ce2a3 100644
--- a/rpc_util.go
+++ b/rpc_util.go
@@ -21,12 +21,10 @@
import (
"bytes"
"compress/gzip"
- stdctx "context"
"encoding/binary"
"io"
"io/ioutil"
"math"
- "os"
"sync"
"time"
@@ -411,57 +409,6 @@
return
}
-// toRPCErr converts an error into an error from the status package.
-func toRPCErr(err error) error {
- if _, ok := status.FromError(err); ok {
- return err
- }
- switch e := err.(type) {
- case transport.StreamError:
- return status.Error(e.Code, e.Desc)
- case transport.ConnectionError:
- return status.Error(codes.Unavailable, e.Desc)
- default:
- switch err {
- case context.DeadlineExceeded, stdctx.DeadlineExceeded:
- return status.Error(codes.DeadlineExceeded, err.Error())
- case context.Canceled, stdctx.Canceled:
- return status.Error(codes.Canceled, err.Error())
- case ErrClientConnClosing:
- return status.Error(codes.FailedPrecondition, err.Error())
- }
- }
- return status.Error(codes.Unknown, err.Error())
-}
-
-// convertCode converts a standard Go error into its canonical code. Note that
-// this is only used to translate the error returned by the server applications.
-func convertCode(err error) codes.Code {
- switch err {
- case nil:
- return codes.OK
- case io.EOF:
- return codes.OutOfRange
- case io.ErrClosedPipe, io.ErrNoProgress, io.ErrShortBuffer, io.ErrShortWrite, io.ErrUnexpectedEOF:
- return codes.FailedPrecondition
- case os.ErrInvalid:
- return codes.InvalidArgument
- case context.Canceled, stdctx.Canceled:
- return codes.Canceled
- case context.DeadlineExceeded, stdctx.DeadlineExceeded:
- return codes.DeadlineExceeded
- }
- switch {
- case os.IsExist(err):
- return codes.AlreadyExists
- case os.IsNotExist(err):
- return codes.NotFound
- case os.IsPermission(err):
- return codes.PermissionDenied
- }
- return codes.Unknown
-}
-
// Code returns the error code for err if it was produced by the rpc system.
// Otherwise, it returns codes.Unknown.
//
diff --git a/transport/go16.go b/transport/go16.go
new file mode 100644
index 0000000..7cffee1
--- /dev/null
+++ b/transport/go16.go
@@ -0,0 +1,45 @@
+// +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"
+
+ "google.golang.org/grpc/codes"
+
+ "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 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)
+ }
+ return streamErrorf(codes.Internal, "Unexpected error from context packet: %v", err)
+}
diff --git a/transport/go17.go b/transport/go17.go
new file mode 100644
index 0000000..2464e69
--- /dev/null
+++ b/transport/go17.go
@@ -0,0 +1,46 @@
+// +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"
+
+ "google.golang.org/grpc/codes"
+
+ 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 StreamError.
+func ContextErr(err error) StreamError {
+ switch err {
+ case context.DeadlineExceeded, netctx.DeadlineExceeded:
+ return streamErrorf(codes.DeadlineExceeded, "%v", err)
+ case context.Canceled, netctx.Canceled:
+ return streamErrorf(codes.Canceled, "%v", err)
+ }
+ return streamErrorf(codes.Internal, "Unexpected error from context packet: %v", err)
+}
diff --git a/transport/http2_client.go b/transport/http2_client.go
index 71731a6..5985666 100644
--- a/transport/http2_client.go
+++ b/transport/http2_client.go
@@ -109,7 +109,7 @@
if fn != nil {
return fn(ctx, addr)
}
- return (&net.Dialer{}).DialContext(ctx, "tcp", addr)
+ return dialContext(ctx, "tcp", addr)
}
func isTemporary(err error) bool {
@@ -148,9 +148,11 @@
ctx, cancel := context.WithCancel(ctx)
connectCtx, connectCancel := context.WithTimeout(ctx, timeout)
defer func() {
- connectCancel()
if err != nil {
cancel()
+ // Don't call connectCancel in success path due to a race in Go 1.6:
+ // https://github.com/golang/go/issues/15078.
+ connectCancel()
}
}()
diff --git a/transport/transport.go b/transport/transport.go
index 2cf9bd3..44d48a9 100644
--- a/transport/transport.go
+++ b/transport/transport.go
@@ -21,7 +21,6 @@
package transport // import "google.golang.org/grpc/transport"
import (
- stdctx "context"
"fmt"
"io"
"net"
@@ -724,17 +723,6 @@
}
}
-// ContextErr converts the error from context package into a StreamError.
-func ContextErr(err error) StreamError {
- switch err {
- case context.DeadlineExceeded, stdctx.DeadlineExceeded:
- return streamErrorf(codes.DeadlineExceeded, "%v", err)
- case context.Canceled, stdctx.Canceled:
- return streamErrorf(codes.Canceled, "%v", err)
- }
- return streamErrorf(codes.Internal, "Unexpected error from context packet: %v", err)
-}
-
// GoAwayReason contains the reason for the GoAway frame received.
type GoAwayReason uint8