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