http2: add support for net/http.Server.ConnState tracking

This is required to work with the upcoming rewrite of the
httptest.Server's connection tracking in
https://go-review.googlesource.com/#/c/15151/
which (at least as of patchset 7) doesn't forcefully tear
down a StateNew connection during Server.Wait. That might
change.

In any case, this adds support for ConnState, which users would
expect regardless of protocol in a mixed HTTP/1 and HTTP/2
environment.

Change-Id: I124aafec29dda123a018935fa306f465ae99cd97
Reviewed-on: https://go-review.googlesource.com/15913
Reviewed-by: Andrew Gerrand <adg@golang.org>
diff --git a/http2/server.go b/http2/server.go
index fde3075..71fb200 100644
--- a/http2/server.go
+++ b/http2/server.go
@@ -443,6 +443,15 @@
 	return stateIdle, nil
 }
 
+// setConnState calls the net/http ConnState hook for this connection, if configured.
+// Note that the net/http package does StateNew and StateClosed for us.
+// There is currently no plan for StateHijacked or hijacking HTTP/2 connections.
+func (sc *serverConn) setConnState(state http.ConnState) {
+	if sc.hs.ConnState != nil {
+		sc.hs.ConnState(sc.conn, state)
+	}
+}
+
 func (sc *serverConn) vlogf(format string, args ...interface{}) {
 	if VerboseLogs {
 		sc.logf(format, args...)
@@ -640,6 +649,12 @@
 		sc.condlogf(err, "error reading preface from client %v: %v", sc.conn.RemoteAddr(), err)
 		return
 	}
+	// Now that we've got the preface, get us out of the
+	// "StateNew" state.  We can't go directly to idle, though.
+	// Active means we read some data and anticipate a request. We'll
+	// do another Active when we get a HEADERS frame.
+	sc.setConnState(http.StateActive)
+	sc.setConnState(http.StateIdle)
 
 	go sc.readFrames() // closed by defer sc.conn.Close above
 
@@ -1087,6 +1102,9 @@
 	}
 	st.state = stateClosed
 	sc.curOpenStreams--
+	if sc.curOpenStreams == 0 {
+		sc.setConnState(http.StateIdle)
+	}
 	delete(sc.streams, st.id)
 	if p := st.body; p != nil {
 		p.Close(err)
@@ -1263,6 +1281,9 @@
 		adjustStreamPriority(sc.streams, st.id, f.Priority)
 	}
 	sc.curOpenStreams++
+	if sc.curOpenStreams == 1 {
+		sc.setConnState(http.StateActive)
+	}
 	sc.req = requestParam{
 		stream: st,
 		header: make(http.Header),
diff --git a/http2/transport_test.go b/http2/transport_test.go
index b7bd629..7b62aa9 100644
--- a/http2/transport_test.go
+++ b/http2/transport_test.go
@@ -41,7 +41,7 @@
 	const body = "sup"
 	st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
 		io.WriteString(w, body)
-	})
+	}, optOnlyServer)
 	defer st.Close()
 
 	tr := &Transport{InsecureTLSDial: true}