Skip to content

Commit 6ccd669

Browse files
committed
http2: export Server.ServeConn
Fixes golang/go#12737 Updates golang/go#14141 Change-Id: I552b603b63a7c87d7fcdb4eb09f96ab9fd0ec0aa Reviewed-on: https://go-review.googlesource.com/19176 Reviewed-by: Andrew Gerrand <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 74d7983 commit 6ccd669

File tree

4 files changed

+169
-16
lines changed

4 files changed

+169
-16
lines changed

http2/http2.go

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package http2
1717

1818
import (
1919
"bufio"
20+
"crypto/tls"
2021
"errors"
2122
"fmt"
2223
"io"
@@ -422,3 +423,7 @@ var isTokenTable = [127]bool{
422423
'|': true,
423424
'~': true,
424425
}
426+
427+
type connectionStater interface {
428+
ConnectionState() tls.ConnectionState
429+
}

http2/server.go

+57-9
Original file line numberDiff line numberDiff line change
@@ -195,28 +195,76 @@ func ConfigureServer(s *http.Server, conf *Server) error {
195195
if testHookOnConn != nil {
196196
testHookOnConn()
197197
}
198-
conf.handleConn(hs, c, h)
198+
conf.ServeConn(c, &ServeConnOpts{
199+
Handler: h,
200+
BaseConfig: hs,
201+
})
199202
}
200203
s.TLSNextProto[NextProtoTLS] = protoHandler
201204
s.TLSNextProto["h2-14"] = protoHandler // temporary; see above.
202205
return nil
203206
}
204207

205-
func (srv *Server) handleConn(hs *http.Server, c net.Conn, h http.Handler) {
208+
// ServeConnOpts are options for the Server.ServeConn method.
209+
type ServeConnOpts struct {
210+
// BaseConfig optionally sets the base configuration
211+
// for values. If nil, defaults are used.
212+
BaseConfig *http.Server
213+
214+
// Handler specifies which handler to use for processing
215+
// requests. If nil, BaseConfig.Handler is used. If BaseConfig
216+
// or BaseConfig.Handler is nil, http.DefaultServeMux is used.
217+
Handler http.Handler
218+
}
219+
220+
func (o *ServeConnOpts) baseConfig() *http.Server {
221+
if o != nil && o.BaseConfig != nil {
222+
return o.BaseConfig
223+
}
224+
return new(http.Server)
225+
}
226+
227+
func (o *ServeConnOpts) handler() http.Handler {
228+
if o != nil {
229+
if o.Handler != nil {
230+
return o.Handler
231+
}
232+
if o.BaseConfig != nil && o.BaseConfig.Handler != nil {
233+
return o.BaseConfig.Handler
234+
}
235+
}
236+
return http.DefaultServeMux
237+
}
238+
239+
// ServeConn serves HTTP/2 requests on the provided connection and
240+
// blocks until the connection is no longer readable.
241+
//
242+
// ServeConn starts speaking HTTP/2 assuming that c has not had any
243+
// reads or writes. It writes its initial settings frame and expects
244+
// to be able to read the preface and settings frame from the
245+
// client. If c has a ConnectionState method like a *tls.Conn, the
246+
// ConnectionState is used to verify the TLS ciphersuite and to set
247+
// the Request.TLS field in Handlers.
248+
//
249+
// ServeConn does not support h2c by itself. Any h2c support must be
250+
// implemented in terms of providing a suitably-behaving net.Conn.
251+
//
252+
// The opts parameter is optional. If nil, default values are used.
253+
func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
206254
sc := &serverConn{
207-
srv: srv,
208-
hs: hs,
255+
srv: s,
256+
hs: opts.baseConfig(),
209257
conn: c,
210258
remoteAddrStr: c.RemoteAddr().String(),
211259
bw: newBufferedWriter(c),
212-
handler: h,
260+
handler: opts.handler(),
213261
streams: make(map[uint32]*stream),
214262
readFrameCh: make(chan readFrameResult),
215263
wantWriteFrameCh: make(chan frameWriteMsg, 8),
216264
wroteFrameCh: make(chan frameWriteResult, 1), // buffered; one send in writeFrameAsync
217265
bodyReadCh: make(chan bodyReadMsg), // buffering doesn't matter either way
218266
doneServing: make(chan struct{}),
219-
advMaxStreams: srv.maxConcurrentStreams(),
267+
advMaxStreams: s.maxConcurrentStreams(),
220268
writeSched: writeScheduler{
221269
maxFrameSize: initialMaxFrameSize,
222270
},
@@ -232,10 +280,10 @@ func (srv *Server) handleConn(hs *http.Server, c net.Conn, h http.Handler) {
232280
sc.hpackDecoder.SetMaxStringLength(sc.maxHeaderStringLen())
233281

234282
fr := NewFramer(sc.bw, c)
235-
fr.SetMaxReadFrameSize(srv.maxReadFrameSize())
283+
fr.SetMaxReadFrameSize(s.maxReadFrameSize())
236284
sc.framer = fr
237285

238-
if tc, ok := c.(*tls.Conn); ok {
286+
if tc, ok := c.(connectionStater); ok {
239287
sc.tlsState = new(tls.ConnectionState)
240288
*sc.tlsState = tc.ConnectionState()
241289
// 9.2 Use of TLS Features
@@ -265,7 +313,7 @@ func (srv *Server) handleConn(hs *http.Server, c net.Conn, h http.Handler) {
265313
// So for now, do nothing here again.
266314
}
267315

268-
if !srv.PermitProhibitedCipherSuites && isBadCipher(sc.tlsState.CipherSuite) {
316+
if !s.PermitProhibitedCipherSuites && isBadCipher(sc.tlsState.CipherSuite) {
269317
// "Endpoints MAY choose to generate a connection error
270318
// (Section 5.4.1) of type INADEQUATE_SECURITY if one of
271319
// the prohibited cipher suites are negotiated."

http2/server_test.go

+107-4
Original file line numberDiff line numberDiff line change
@@ -2808,12 +2808,16 @@ func TestIssue53(t *testing.T) {
28082808
"\r\n\r\n\x00\x00\x00\x01\ainfinfin\ad"
28092809
s := &http.Server{
28102810
ErrorLog: log.New(io.MultiWriter(stderrv(), twriter{t: t}), "", log.LstdFlags),
2811+
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
2812+
w.Write([]byte("hello"))
2813+
}),
2814+
}
2815+
s2 := &Server{
2816+
MaxReadFrameSize: 1 << 16,
2817+
PermitProhibitedCipherSuites: true,
28112818
}
2812-
s2 := &Server{MaxReadFrameSize: 1 << 16, PermitProhibitedCipherSuites: true}
28132819
c := &issue53Conn{[]byte(data), false, false}
2814-
s2.handleConn(s, c, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
2815-
w.Write([]byte("hello"))
2816-
}))
2820+
s2.ServeConn(c, &ServeConnOpts{BaseConfig: s})
28172821
if !c.closed {
28182822
t.Fatal("connection is not closed")
28192823
}
@@ -2977,3 +2981,102 @@ func TestServerNoDuplicateContentType(t *testing.T) {
29772981
t.Errorf("Headers mismatch.\n got: %q\nwant: %q\n", headers, want)
29782982
}
29792983
}
2984+
2985+
type connStateConn struct {
2986+
net.Conn
2987+
cs tls.ConnectionState
2988+
}
2989+
2990+
func (c connStateConn) ConnectionState() tls.ConnectionState { return c.cs }
2991+
2992+
// golang.org/issue/12737 -- handle any net.Conn, not just
2993+
// *tls.Conn.
2994+
func TestServerHandleCustomConn(t *testing.T) {
2995+
var s Server
2996+
c1, c2 := net.Pipe()
2997+
clientDone := make(chan struct{})
2998+
handlerDone := make(chan struct{})
2999+
var req *http.Request
3000+
go func() {
3001+
defer close(clientDone)
3002+
defer c2.Close()
3003+
fr := NewFramer(c2, c2)
3004+
io.WriteString(c2, ClientPreface)
3005+
fr.WriteSettings()
3006+
fr.WriteSettingsAck()
3007+
f, err := fr.ReadFrame()
3008+
if err != nil {
3009+
t.Error(err)
3010+
return
3011+
}
3012+
if sf, ok := f.(*SettingsFrame); !ok || sf.IsAck() {
3013+
t.Errorf("Got %v; want non-ACK SettingsFrame", summarizeFrame(f))
3014+
return
3015+
}
3016+
f, err = fr.ReadFrame()
3017+
if err != nil {
3018+
t.Error(err)
3019+
return
3020+
}
3021+
if sf, ok := f.(*SettingsFrame); !ok || !sf.IsAck() {
3022+
t.Errorf("Got %v; want ACK SettingsFrame", summarizeFrame(f))
3023+
return
3024+
}
3025+
var henc hpackEncoder
3026+
fr.WriteHeaders(HeadersFrameParam{
3027+
StreamID: 1,
3028+
BlockFragment: henc.encodeHeaderRaw(t, ":method", "GET", ":path", "/", ":scheme", "https", ":authority", "foo.com"),
3029+
EndStream: true,
3030+
EndHeaders: true,
3031+
})
3032+
go io.Copy(ioutil.Discard, c2)
3033+
<-handlerDone
3034+
}()
3035+
const testString = "my custom ConnectionState"
3036+
fakeConnState := tls.ConnectionState{
3037+
ServerName: testString,
3038+
Version: tls.VersionTLS12,
3039+
}
3040+
go s.ServeConn(connStateConn{c1, fakeConnState}, &ServeConnOpts{
3041+
BaseConfig: &http.Server{
3042+
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
3043+
defer close(handlerDone)
3044+
req = r
3045+
}),
3046+
}})
3047+
select {
3048+
case <-clientDone:
3049+
case <-time.After(5 * time.Second):
3050+
t.Fatal("timeout waiting for handler")
3051+
}
3052+
if req.TLS == nil {
3053+
t.Fatalf("Request.TLS is nil. Got: %#v", req)
3054+
}
3055+
if req.TLS.ServerName != testString {
3056+
t.Fatalf("Request.TLS = %+v; want ServerName of %q", req.TLS, testString)
3057+
}
3058+
}
3059+
3060+
type hpackEncoder struct {
3061+
enc *hpack.Encoder
3062+
buf bytes.Buffer
3063+
}
3064+
3065+
func (he *hpackEncoder) encodeHeaderRaw(t *testing.T, headers ...string) []byte {
3066+
if len(headers)%2 == 1 {
3067+
panic("odd number of kv args")
3068+
}
3069+
he.buf.Reset()
3070+
if he.enc == nil {
3071+
he.enc = hpack.NewEncoder(&he.buf)
3072+
}
3073+
for len(headers) > 0 {
3074+
k, v := headers[0], headers[1]
3075+
err := he.enc.WriteField(hpack.HeaderField{Name: k, Value: v})
3076+
if err != nil {
3077+
t.Fatalf("HPACK encoding error for %q/%q: %v", k, v, err)
3078+
}
3079+
headers = headers[2:]
3080+
}
3081+
return he.buf.Bytes()
3082+
}

http2/transport.go

-3
Original file line numberDiff line numberDiff line change
@@ -406,9 +406,6 @@ func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
406406
// henc in response to SETTINGS frames?
407407
cc.henc = hpack.NewEncoder(&cc.hbuf)
408408

409-
type connectionStater interface {
410-
ConnectionState() tls.ConnectionState
411-
}
412409
if cs, ok := c.(connectionStater); ok {
413410
state := cs.ConnectionState()
414411
cc.tlsState = &state

0 commit comments

Comments
 (0)