http2/h2c: Respect the req.Context()

When using h2c.NewHandler, the *http.Request object for h2c requests has a
.Context() that doesn't inherit from the *http.Server's BaseContext.  This
is surprising for users of vanilla net/http, and is surprising to users of
http2.ConfigureServer; HTTP/1 requests inherit from that BaseContext, and
TLS h2 requests inherit from that BaseContext, but cleartext h2c requests
don't.

So, modify h2c.NewHander to respect that base Context, by way of the
hijacked Request's .Context().

Change-Id: Ibc24ae6e2fb153d9561827ad791329487dae8e5a
GitHub-Last-Rev: 821b2070f7eebf900bb2e45ec92d80699f99e80d
GitHub-Pull-Request: golang/net#88
Reviewed-on: https://go-review.googlesource.com/c/net/+/278972
Reviewed-by: Liam Haworth <liam@haworth.id.au>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Trust: Brad Fitzpatrick <bradfitz@golang.org>
Trust: Damien Neil <dneil@google.com>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/http2/h2c/h2c.go b/http2/h2c/h2c.go
index 07c5c9a..16319b8 100644
--- a/http2/h2c/h2c.go
+++ b/http2/h2c/h2c.go
@@ -84,14 +84,20 @@
 		}
 		defer conn.Close()
 
-		s.s.ServeConn(conn, &http2.ServeConnOpts{Handler: s.Handler})
+		s.s.ServeConn(conn, &http2.ServeConnOpts{
+			Context: r.Context(),
+			Handler: s.Handler,
+		})
 		return
 	}
 	// Handle Upgrade to h2c (RFC 7540 Section 3.2)
 	if conn, err := h2cUpgrade(w, r); err == nil {
 		defer conn.Close()
 
-		s.s.ServeConn(conn, &http2.ServeConnOpts{Handler: s.Handler})
+		s.s.ServeConn(conn, &http2.ServeConnOpts{
+			Context: r.Context(),
+			Handler: s.Handler,
+		})
 		return
 	}
 
diff --git a/http2/h2c/h2c_test.go b/http2/h2c/h2c_test.go
index 57aadc0..bd9461e 100644
--- a/http2/h2c/h2c_test.go
+++ b/http2/h2c/h2c_test.go
@@ -7,9 +7,14 @@
 import (
 	"bufio"
 	"bytes"
+	"context"
+	"crypto/tls"
 	"fmt"
+	"io/ioutil"
 	"log"
+	"net"
 	"net/http"
+	"net/http/httptest"
 	"testing"
 
 	"golang.org/x/net/http2"
@@ -56,3 +61,46 @@
 	}
 	log.Fatal(h1s.ListenAndServe())
 }
+
+func TestContext(t *testing.T) {
+	baseCtx := context.WithValue(context.Background(), "testkey", "testvalue")
+
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		if r.ProtoMajor != 2 {
+			t.Errorf("Request wasn't handled by h2c.  Got ProtoMajor=%v", r.ProtoMajor)
+		}
+		if r.Context().Value("testkey") != "testvalue" {
+			t.Errorf("Request doesn't have expected base context: %v", r.Context())
+		}
+		fmt.Fprint(w, "Hello world")
+	})
+
+	h2s := &http2.Server{}
+	h1s := httptest.NewUnstartedServer(NewHandler(handler, h2s))
+	h1s.Config.BaseContext = func(_ net.Listener) context.Context {
+		return baseCtx
+	}
+	h1s.Start()
+	defer h1s.Close()
+
+	client := &http.Client{
+		Transport: &http2.Transport{
+			AllowHTTP: true,
+			DialTLS: func(network, addr string, _ *tls.Config) (net.Conn, error) {
+				return net.Dial(network, addr)
+			},
+		},
+	}
+
+	resp, err := client.Get(h1s.URL)
+	if err != nil {
+		t.Fatal(err)
+	}
+	_, err = ioutil.ReadAll(resp.Body)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if err := resp.Body.Close(); err != nil {
+		t.Fatal(err)
+	}
+}