Browse Source

Break http2*.go up into http2 and server parts.

Brad Fitzpatrick 11 years ago
parent
commit
b331b819ec
4 changed files with 1676 additions and 1646 deletions
  1. 0 857
      http2.go
  2. 40 789
      http2_test.go
  3. 870 0
      server.go
  4. 766 0
      server_test.go

+ 0 - 857
http2.go

@@ -16,23 +16,9 @@
 // This package currently targets draft-14. See http://http2.github.io/
 package http2
 
-// TODO: finish GOAWAY support. Consider each incoming frame type and whether
-// it should be ignored during a shutdown race.
-
 import (
-	"bytes"
-	"crypto/tls"
-	"errors"
-	"fmt"
-	"io"
-	"log"
-	"net"
 	"net/http"
-	"net/url"
 	"strconv"
-	"strings"
-
-	"github.com/bradfitz/http2/hpack"
 )
 
 var VerboseLogs = false
@@ -58,85 +44,6 @@ var (
 	clientPreface = []byte(ClientPreface)
 )
 
-// Server is an HTTP2 server.
-type Server struct {
-	// MaxStreams optionally ...
-	MaxStreams int
-}
-
-func (srv *Server) handleConn(hs *http.Server, c net.Conn, h http.Handler) {
-	sc := &serverConn{
-		hs:                hs,
-		conn:              c,
-		handler:           h,
-		framer:            NewFramer(c, c), // TODO: write to a (custom?) buffered writer that can alternate when it's in buffered mode.
-		streams:           make(map[uint32]*stream),
-		canonHeader:       make(map[string]string),
-		readFrameCh:       make(chan frameAndProcessed),
-		readFrameErrCh:    make(chan error, 1),
-		writeHeaderCh:     make(chan headerWriteReq), // must not be buffered
-		windowUpdateCh:    make(chan windowUpdateReq, 8),
-		flow:              newFlow(initialWindowSize),
-		doneServing:       make(chan struct{}),
-		maxWriteFrameSize: initialMaxFrameSize,
-		initialWindowSize: initialWindowSize,
-		serveG:            newGoroutineLock(),
-	}
-	sc.hpackEncoder = hpack.NewEncoder(&sc.headerWriteBuf)
-	sc.hpackDecoder = hpack.NewDecoder(initialHeaderTableSize, sc.onNewHeaderField)
-	sc.serve()
-}
-
-// frameAndProcessed coordinates the readFrames and serve goroutines, since
-// the Framer interface only permits the most recently-read Frame from being
-// accessed. The serve goroutine sends on processed to signal to the readFrames
-// goroutine that another frame may be read.
-type frameAndProcessed struct {
-	f         Frame
-	processed chan struct{}
-}
-
-type serverConn struct {
-	// Immutable:
-	hs             *http.Server
-	conn           net.Conn
-	handler        http.Handler
-	framer         *Framer
-	hpackDecoder   *hpack.Decoder
-	hpackEncoder   *hpack.Encoder
-	doneServing    chan struct{}          // closed when serverConn.serve ends
-	readFrameCh    chan frameAndProcessed // written by serverConn.readFrames
-	readFrameErrCh chan error
-	writeHeaderCh  chan headerWriteReq // must not be buffered
-	windowUpdateCh chan windowUpdateReq
-	serveG         goroutineLock // used to verify funcs are on serve()
-	flow           *flow         // the connection-wide one
-
-	// Everything following is owned by the serve loop; use serveG.check()
-	maxStreamID       uint32 // max ever seen
-	streams           map[uint32]*stream
-	maxWriteFrameSize uint32 // TODO: update this when settings come in
-	initialWindowSize int32
-	canonHeader       map[string]string // http2-lower-case -> Go-Canonical-Case
-	sentGoAway        bool
-	req               requestParam // non-zero while reading request headers
-	headerWriteBuf    bytes.Buffer // used to write response headers
-}
-
-// requestParam is the state of the next request, initialized over
-// potentially several frames HEADERS + zero or more CONTINUATION
-// frames.
-type requestParam struct {
-	// stream is non-nil if we're reading (HEADER or CONTINUATION)
-	// frames for a request (but not DATA).
-	stream            *stream
-	header            http.Header
-	method, path      string
-	scheme, authority string
-	sawRegularHeader  bool // saw a non-pseudo header already
-	invalidHeader     bool // an invalid header was seen
-}
-
 type streamState int
 
 const (
@@ -149,770 +56,6 @@ const (
 	stateClosed
 )
 
-type stream struct {
-	id    uint32
-	state streamState // owned by serverConn's processing loop
-	flow  *flow       // limits writing from Handler to client
-	body  *pipe       // non-nil if expecting DATA frames
-
-	bodyBytes     int64 // body bytes seen so far
-	declBodyBytes int64 // or -1 if undeclared
-}
-
-func (sc *serverConn) state(streamID uint32) streamState {
-	sc.serveG.check()
-	// http://http2.github.io/http2-spec/#rfc.section.5.1
-	if st, ok := sc.streams[streamID]; ok {
-		return st.state
-	}
-	// "The first use of a new stream identifier implicitly closes all
-	// streams in the "idle" state that might have been initiated by
-	// that peer with a lower-valued stream identifier. For example, if
-	// a client sends a HEADERS frame on stream 7 without ever sending a
-	// frame on stream 5, then stream 5 transitions to the "closed"
-	// state when the first frame for stream 7 is sent or received."
-	if streamID <= sc.maxStreamID {
-		return stateClosed
-	}
-	return stateIdle
-}
-
-func (sc *serverConn) vlogf(format string, args ...interface{}) {
-	if VerboseLogs {
-		sc.logf(format, args...)
-	}
-}
-
-func (sc *serverConn) logf(format string, args ...interface{}) {
-	if lg := sc.hs.ErrorLog; lg != nil {
-		lg.Printf(format, args...)
-	} else {
-		log.Printf(format, args...)
-	}
-}
-
-func (sc *serverConn) condlogf(err error, format string, args ...interface{}) {
-	if err == nil {
-		return
-	}
-	str := err.Error()
-	if strings.Contains(str, "use of closed network connection") {
-		// Boring, expected errors.
-		sc.vlogf(format, args...)
-	} else {
-		sc.logf(format, args...)
-	}
-}
-
-func (sc *serverConn) onNewHeaderField(f hpack.HeaderField) {
-	sc.serveG.check()
-	switch {
-	case !validHeader(f.Name):
-		sc.req.invalidHeader = true
-	case strings.HasPrefix(f.Name, ":"):
-		if sc.req.sawRegularHeader {
-			sc.logf("pseudo-header after regular header")
-			sc.req.invalidHeader = true
-			return
-		}
-		var dst *string
-		switch f.Name {
-		case ":method":
-			dst = &sc.req.method
-		case ":path":
-			dst = &sc.req.path
-		case ":scheme":
-			dst = &sc.req.scheme
-		case ":authority":
-			dst = &sc.req.authority
-		default:
-			// 8.1.2.1 Pseudo-Header Fields
-			// "Endpoints MUST treat a request or response
-			// that contains undefined or invalid
-			// pseudo-header fields as malformed (Section
-			// 8.1.2.6)."
-			sc.logf("invalid pseudo-header %q", f.Name)
-			sc.req.invalidHeader = true
-			return
-		}
-		if *dst != "" {
-			sc.logf("duplicate pseudo-header %q sent", f.Name)
-			sc.req.invalidHeader = true
-			return
-		}
-		*dst = f.Value
-	case f.Name == "cookie":
-		sc.req.sawRegularHeader = true
-		if s, ok := sc.req.header["Cookie"]; ok && len(s) == 1 {
-			s[0] = s[0] + "; " + f.Value
-		} else {
-			sc.req.header.Add("Cookie", f.Value)
-		}
-	default:
-		sc.req.sawRegularHeader = true
-		sc.req.header.Add(sc.canonicalHeader(f.Name), f.Value)
-	}
-}
-
-func (sc *serverConn) canonicalHeader(v string) string {
-	sc.serveG.check()
-	// TODO: use a sync.Pool instead of putting the cache on *serverConn?
-	cv, ok := sc.canonHeader[v]
-	if !ok {
-		cv = http.CanonicalHeaderKey(v)
-		sc.canonHeader[v] = cv
-	}
-	return cv
-}
-
-// readFrames is the loop that reads incoming frames.
-// It's run on its own goroutine.
-func (sc *serverConn) readFrames() {
-	processed := make(chan struct{}, 1)
-	for {
-		f, err := sc.framer.ReadFrame()
-		if err != nil {
-			close(sc.readFrameCh)
-			sc.readFrameErrCh <- err
-			return
-		}
-		sc.readFrameCh <- frameAndProcessed{f, processed}
-		<-processed
-	}
-}
-
-func (sc *serverConn) serve() {
-	sc.serveG.check()
-	defer sc.conn.Close()
-	defer close(sc.doneServing)
-
-	sc.vlogf("HTTP/2 connection from %v on %p", sc.conn.RemoteAddr(), sc.hs)
-
-	// Read the client preface
-	buf := make([]byte, len(ClientPreface))
-	// TODO: timeout reading from the client
-	if _, err := io.ReadFull(sc.conn, buf); err != nil {
-		sc.logf("error reading client preface: %v", err)
-		return
-	}
-	if !bytes.Equal(buf, clientPreface) {
-		sc.logf("bogus greeting from client: %q", buf)
-		return
-	}
-	sc.vlogf("client %v said hello", sc.conn.RemoteAddr())
-
-	f, err := sc.framer.ReadFrame()
-	if err != nil {
-		sc.logf("error reading initial frame from client: %v", err)
-		return
-	}
-	sf, ok := f.(*SettingsFrame)
-	if !ok {
-		sc.logf("invalid initial frame type %T received from client", f)
-		return
-	}
-	if err := sf.ForeachSetting(sc.processSetting); err != nil {
-		sc.logf("initial settings error: %v", err)
-		return
-	}
-
-	// TODO: don't send two network packets for our SETTINGS + our
-	// ACK of their settings.  But if we make framer write to a
-	// *bufio.Writer, that increases the per-connection memory
-	// overhead, and there could be many idle conns. So maybe some
-	// liveswitchWriter-like thing where we only switch to a
-	// *bufio Writer when we really need one temporarily, else go
-	// back to an unbuffered writes by default.
-	if err := sc.framer.WriteSettings( /* TODO: actual settings */ ); err != nil {
-		sc.logf("error writing server's initial settings: %v", err)
-		return
-	}
-	if err := sc.framer.WriteSettingsAck(); err != nil {
-		sc.logf("error writing server's ack of client's settings: %v", err)
-		return
-	}
-
-	go sc.readFrames()
-
-	for {
-		select {
-		case hr := <-sc.writeHeaderCh:
-			if err := sc.writeHeaderInLoop(hr); err != nil {
-				sc.condlogf(err, "error writing response header: %v", err)
-				return
-			}
-		case wu := <-sc.windowUpdateCh:
-			if err := sc.sendWindowUpdateInLoop(wu); err != nil {
-				sc.condlogf(err, "error writing window update: %v", err)
-				return
-			}
-		case fp, ok := <-sc.readFrameCh:
-			if !ok {
-				err := <-sc.readFrameErrCh
-				if err != io.EOF {
-					errstr := err.Error()
-					if !strings.Contains(errstr, "use of closed network connection") {
-						sc.logf("client %s stopped sending frames: %v", sc.conn.RemoteAddr(), errstr)
-					}
-				}
-				return
-			}
-			f := fp.f
-			sc.vlogf("got %v: %#v", f.Header(), f)
-			err := sc.processFrame(f)
-			fp.processed <- struct{}{} // let readFrames proceed
-			switch ev := err.(type) {
-			case nil:
-				// nothing.
-			case StreamError:
-				if err := sc.resetStreamInLoop(ev); err != nil {
-					sc.logf("Error writing RSTSTream: %v", err)
-					return
-				}
-			case ConnectionError:
-				sc.logf("Disconnecting; %v", ev)
-				return
-			case goAwayFlowError:
-				if err := sc.goAway(ErrCodeFlowControl); err != nil {
-					sc.condlogf(err, "failed to GOAWAY: %v", err)
-					return
-				}
-			default:
-				sc.logf("Disconnection due to other error: %v", err)
-				return
-			}
-		}
-	}
-}
-
-func (sc *serverConn) goAway(code ErrCode) error {
-	sc.serveG.check()
-	sc.sentGoAway = true
-	return sc.framer.WriteGoAway(sc.maxStreamID, code, nil)
-}
-
-func (sc *serverConn) resetStreamInLoop(se StreamError) error {
-	sc.serveG.check()
-	if err := sc.framer.WriteRSTStream(se.streamID, uint32(se.code)); err != nil {
-		return err
-	}
-	delete(sc.streams, se.streamID)
-	return nil
-}
-
-func (sc *serverConn) curHeaderStreamID() uint32 {
-	sc.serveG.check()
-	st := sc.req.stream
-	if st == nil {
-		return 0
-	}
-	return st.id
-}
-
-func (sc *serverConn) processFrame(f Frame) error {
-	sc.serveG.check()
-
-	if s := sc.curHeaderStreamID(); s != 0 {
-		if cf, ok := f.(*ContinuationFrame); !ok {
-			return ConnectionError(ErrCodeProtocol)
-		} else if cf.Header().StreamID != s {
-			return ConnectionError(ErrCodeProtocol)
-		}
-	}
-
-	switch f := f.(type) {
-	case *SettingsFrame:
-		return sc.processSettings(f)
-	case *HeadersFrame:
-		return sc.processHeaders(f)
-	case *ContinuationFrame:
-		return sc.processContinuation(f)
-	case *WindowUpdateFrame:
-		return sc.processWindowUpdate(f)
-	case *PingFrame:
-		return sc.processPing(f)
-	case *DataFrame:
-		return sc.processData(f)
-	default:
-		log.Printf("Ignoring unknown frame %#v", f)
-		return nil
-	}
-}
-
-func (sc *serverConn) processPing(f *PingFrame) error {
-	sc.serveG.check()
-	if f.Flags.Has(FlagSettingsAck) {
-		// 6.7 PING: " An endpoint MUST NOT respond to PING frames
-		// containing this flag."
-		return nil
-	}
-	if f.StreamID != 0 {
-		// "PING frames are not associated with any individual
-		// stream. If a PING frame is received with a stream
-		// identifier field value other than 0x0, the recipient MUST
-		// respond with a connection error (Section 5.4.1) of type
-		// PROTOCOL_ERROR."
-		return ConnectionError(ErrCodeProtocol)
-	}
-	return sc.framer.WritePing(true, f.Data)
-}
-
-func (sc *serverConn) processWindowUpdate(f *WindowUpdateFrame) error {
-	sc.serveG.check()
-	switch {
-	case f.StreamID != 0: // stream-level flow control
-		st := sc.streams[f.StreamID]
-		if st == nil {
-			// "WINDOW_UPDATE can be sent by a peer that has sent a
-			// frame bearing the END_STREAM flag. This means that a
-			// receiver could receive a WINDOW_UPDATE frame on a "half
-			// closed (remote)" or "closed" stream. A receiver MUST
-			// NOT treat this as an error, see Section 5.1."
-			return nil
-		}
-		if !st.flow.add(int32(f.Increment)) {
-			return StreamError{f.StreamID, ErrCodeFlowControl}
-		}
-	default: // connection-level flow control
-		if !sc.flow.add(int32(f.Increment)) {
-			return goAwayFlowError{}
-		}
-	}
-	return nil
-}
-
-func (sc *serverConn) processSettings(f *SettingsFrame) error {
-	sc.serveG.check()
-	return f.ForeachSetting(sc.processSetting)
-}
-
-func (sc *serverConn) processSetting(s Setting) error {
-	sc.serveG.check()
-	sc.vlogf("processing setting %v", s)
-	switch s.ID {
-	case SettingInitialWindowSize:
-		return sc.processSettingInitialWindowSize(s.Val)
-	}
-	log.Printf("TODO: handle %v", s)
-	return nil
-}
-
-func (sc *serverConn) processSettingInitialWindowSize(val uint32) error {
-	sc.serveG.check()
-	if val > (1<<31 - 1) {
-		// 6.5.2 Defined SETTINGS Parameters
-		// "Values above the maximum flow control window size of
-		// 231-1 MUST be treated as a connection error (Section
-		// 5.4.1) of type FLOW_CONTROL_ERROR."
-		return ConnectionError(ErrCodeFlowControl)
-	}
-
-	// "A SETTINGS frame can alter the initial flow control window
-	// size for all current streams. When the value of
-	// SETTINGS_INITIAL_WINDOW_SIZE changes, a receiver MUST
-	// adjust the size of all stream flow control windows that it
-	// maintains by the difference between the new value and the
-	// old value."
-	old := sc.initialWindowSize
-	sc.initialWindowSize = int32(val)
-	growth := sc.initialWindowSize - old // may be negative
-	for _, st := range sc.streams {
-		if !st.flow.add(growth) {
-			// 6.9.2 Initial Flow Control Window Size
-			// "An endpoint MUST treat a change to
-			// SETTINGS_INITIAL_WINDOW_SIZE that causes any flow
-			// control window to exceed the maximum size as a
-			// connection error (Section 5.4.1) of type
-			// FLOW_CONTROL_ERROR."
-			return ConnectionError(ErrCodeFlowControl)
-		}
-	}
-	return nil
-}
-
-func (sc *serverConn) processData(f *DataFrame) error {
-	sc.serveG.check()
-	// "If a DATA frame is received whose stream is not in "open"
-	// or "half closed (local)" state, the recipient MUST respond
-	// with a stream error (Section 5.4.2) of type STREAM_CLOSED."
-	id := f.Header().StreamID
-	st, ok := sc.streams[id]
-	if !ok || (st.state != stateOpen && st.state != stateHalfClosedLocal) {
-		return StreamError{id, ErrCodeStreamClosed}
-	}
-	if st.body == nil {
-		// Not expecting data.
-		// TODO: which error code?
-		return StreamError{id, ErrCodeStreamClosed}
-	}
-	data := f.Data()
-
-	// Sender sending more than they'd declared?
-	if st.declBodyBytes != -1 && st.bodyBytes+int64(len(data)) > st.declBodyBytes {
-		st.body.Close(fmt.Errorf("Sender tried to send more than declared Content-Length of %d bytes", st.declBodyBytes))
-		return StreamError{id, ErrCodeStreamClosed}
-	}
-	if len(data) > 0 {
-		// TODO: verify they're allowed to write with the flow control
-		// window we'd advertised to them.
-		// TODO: verify n from Write
-		if _, err := st.body.Write(data); err != nil {
-			return StreamError{id, ErrCodeStreamClosed}
-		}
-		st.bodyBytes += int64(len(data))
-	}
-	if f.Header().Flags.Has(FlagDataEndStream) {
-		if st.declBodyBytes != -1 && st.declBodyBytes != st.bodyBytes {
-			st.body.Close(fmt.Errorf("Request declared a Content-Length of %d but only wrote %d bytes",
-				st.declBodyBytes, st.bodyBytes))
-		} else {
-			st.body.Close(io.EOF)
-		}
-	}
-	return nil
-}
-
-func (sc *serverConn) processHeaders(f *HeadersFrame) error {
-	sc.serveG.check()
-	id := f.Header().StreamID
-	if sc.sentGoAway {
-		// Ignore.
-		return nil
-	}
-	// http://http2.github.io/http2-spec/#rfc.section.5.1.1
-	if id%2 != 1 || id <= sc.maxStreamID || sc.req.stream != nil {
-		// Streams initiated by a client MUST use odd-numbered
-		// stream identifiers. [...] The identifier of a newly
-		// established stream MUST be numerically greater than all
-		// streams that the initiating endpoint has opened or
-		// reserved. [...]  An endpoint that receives an unexpected
-		// stream identifier MUST respond with a connection error
-		// (Section 5.4.1) of type PROTOCOL_ERROR.
-		return ConnectionError(ErrCodeProtocol)
-	}
-	if id > sc.maxStreamID {
-		sc.maxStreamID = id
-	}
-	st := &stream{
-		id:    id,
-		state: stateOpen,
-		flow:  newFlow(sc.initialWindowSize),
-	}
-	if f.Header().Flags.Has(FlagHeadersEndStream) {
-		st.state = stateHalfClosedRemote
-	}
-	sc.streams[id] = st
-	sc.req = requestParam{
-		stream: st,
-		header: make(http.Header),
-	}
-	return sc.processHeaderBlockFragment(st, f.HeaderBlockFragment(), f.HeadersEnded())
-}
-
-func (sc *serverConn) processContinuation(f *ContinuationFrame) error {
-	sc.serveG.check()
-	st := sc.streams[f.Header().StreamID]
-	if st == nil || sc.curHeaderStreamID() != st.id {
-		return ConnectionError(ErrCodeProtocol)
-	}
-	return sc.processHeaderBlockFragment(st, f.HeaderBlockFragment(), f.HeadersEnded())
-}
-
-func (sc *serverConn) processHeaderBlockFragment(st *stream, frag []byte, end bool) error {
-	sc.serveG.check()
-	if _, err := sc.hpackDecoder.Write(frag); err != nil {
-		// TODO: convert to stream error I assume?
-		return err
-	}
-	if !end {
-		return nil
-	}
-	if err := sc.hpackDecoder.Close(); err != nil {
-		// TODO: convert to stream error I assume?
-		return err
-	}
-	rw, req, err := sc.newWriterAndRequest()
-	sc.req = requestParam{}
-	if err != nil {
-		return err
-	}
-	st.body = req.Body.(*requestBody).pipe // may be nil
-	st.declBodyBytes = req.ContentLength
-	go sc.runHandler(rw, req)
-	return nil
-}
-
-func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, error) {
-	sc.serveG.check()
-	rp := &sc.req
-	if rp.invalidHeader || rp.method == "" || rp.path == "" ||
-		(rp.scheme != "https" && rp.scheme != "http") {
-		// See 8.1.2.6 Malformed Requests and Responses:
-		//
-		// Malformed requests or responses that are detected
-		// MUST be treated as a stream error (Section 5.4.2)
-		// of type PROTOCOL_ERROR."
-		//
-		// 8.1.2.3 Request Pseudo-Header Fields
-		// "All HTTP/2 requests MUST include exactly one valid
-		// value for the :method, :scheme, and :path
-		// pseudo-header fields"
-		return nil, nil, StreamError{rp.stream.id, ErrCodeProtocol}
-	}
-	var tlsState *tls.ConnectionState // make this non-nil if https
-	if rp.scheme == "https" {
-		// TODO: get from sc's ConnectionState
-		tlsState = &tls.ConnectionState{}
-	}
-	authority := rp.authority
-	if authority == "" {
-		authority = rp.header.Get("Host")
-	}
-	bodyOpen := rp.stream.state == stateOpen
-	body := &requestBody{
-		sc:       sc,
-		streamID: rp.stream.id,
-	}
-	req := &http.Request{
-		Method:     rp.method,
-		URL:        &url.URL{},
-		RemoteAddr: sc.conn.RemoteAddr().String(),
-		Header:     rp.header,
-		RequestURI: rp.path,
-		Proto:      "HTTP/2.0",
-		ProtoMajor: 2,
-		ProtoMinor: 0,
-		TLS:        tlsState,
-		Host:       authority,
-		Body:       body,
-	}
-	if bodyOpen {
-		body.pipe = &pipe{
-			b: buffer{buf: make([]byte, 65536)}, // TODO: share/remove
-		}
-		body.pipe.c.L = &body.pipe.m
-
-		if vv, ok := rp.header["Content-Length"]; ok {
-			req.ContentLength, _ = strconv.ParseInt(vv[0], 10, 64)
-		} else {
-			req.ContentLength = -1
-		}
-	}
-	rw := &responseWriter{
-		sc:       sc,
-		streamID: rp.stream.id,
-		req:      req,
-		body:     body,
-	}
-	return rw, req, nil
-}
-
-// Run on its own goroutine.
-func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request) {
-	defer rw.handlerDone()
-	// TODO: catch panics like net/http.Server
-	sc.handler.ServeHTTP(rw, req)
-}
-
-// called from handler goroutines
-func (sc *serverConn) writeData(streamID uint32, p []byte) (n int, err error) {
-	// TODO: implement
-	log.Printf("WRITE on %d: %q", streamID, p)
-	return len(p), nil
-}
-
-// headerWriteReq is a request to write an HTTP response header from a server Handler.
-type headerWriteReq struct {
-	streamID    uint32
-	httpResCode int
-	h           http.Header // may be nil
-	endStream   bool
-}
-
-// called from handler goroutines.
-// h may be nil.
-func (sc *serverConn) writeHeader(req headerWriteReq) {
-	sc.writeHeaderCh <- req
-}
-
-func (sc *serverConn) writeHeaderInLoop(req headerWriteReq) error {
-	sc.serveG.check()
-	sc.headerWriteBuf.Reset()
-	sc.hpackEncoder.WriteField(hpack.HeaderField{Name: ":status", Value: httpCodeString(req.httpResCode)})
-	for k, vv := range req.h {
-		for _, v := range vv {
-			// TODO: for gargage, cache lowercase copies of headers at
-			// least for common ones and/or popular recent ones for
-			// this serverConn. LRU?
-			sc.hpackEncoder.WriteField(hpack.HeaderField{Name: strings.ToLower(k), Value: v})
-		}
-	}
-	headerBlock := sc.headerWriteBuf.Bytes()
-	if len(headerBlock) > int(sc.maxWriteFrameSize) {
-		// we'll need continuation ones.
-		panic("TODO")
-	}
-	return sc.framer.WriteHeaders(HeadersFrameParam{
-		StreamID:      req.streamID,
-		BlockFragment: headerBlock,
-		EndStream:     req.endStream,
-		EndHeaders:    true, // no continuation yet
-	})
-}
-
-type windowUpdateReq struct {
-	streamID uint32
-	n        uint32
-}
-
-// called from handler goroutines
-func (sc *serverConn) sendWindowUpdate(streamID uint32, n int) {
-	const maxUint32 = 2147483647
-	for n >= maxUint32 {
-		sc.windowUpdateCh <- windowUpdateReq{streamID, maxUint32}
-		n -= maxUint32
-	}
-	if n > 0 {
-		sc.windowUpdateCh <- windowUpdateReq{streamID, uint32(n)}
-	}
-}
-
-func (sc *serverConn) sendWindowUpdateInLoop(wu windowUpdateReq) error {
-	sc.serveG.check()
-	// TODO: sc.bufferedOutput.StartBuffering()
-	if err := sc.framer.WriteWindowUpdate(0, wu.n); err != nil {
-		return err
-	}
-	if err := sc.framer.WriteWindowUpdate(wu.streamID, wu.n); err != nil {
-		return err
-	}
-	// TODO: return sc.bufferedOutput.Flush()
-	return nil
-}
-
-// ConfigureServer adds HTTP/2 support to a net/http Server.
-//
-// The configuration conf may be nil.
-//
-// ConfigureServer must be called before s begins serving.
-func ConfigureServer(s *http.Server, conf *Server) {
-	if conf == nil {
-		conf = new(Server)
-	}
-	if s.TLSConfig == nil {
-		s.TLSConfig = new(tls.Config)
-	}
-	haveNPN := false
-	for _, p := range s.TLSConfig.NextProtos {
-		if p == npnProto {
-			haveNPN = true
-			break
-		}
-	}
-	if !haveNPN {
-		s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, npnProto)
-	}
-
-	if s.TLSNextProto == nil {
-		s.TLSNextProto = map[string]func(*http.Server, *tls.Conn, http.Handler){}
-	}
-	s.TLSNextProto[npnProto] = func(hs *http.Server, c *tls.Conn, h http.Handler) {
-		if testHookOnConn != nil {
-			testHookOnConn()
-		}
-		conf.handleConn(hs, c, h)
-	}
-}
-
-type requestBody struct {
-	sc       *serverConn
-	streamID uint32
-	closed   bool
-	pipe     *pipe // non-nil if we have a HTTP entity message body
-}
-
-var errClosedBody = errors.New("body closed by handler")
-
-func (b *requestBody) Close() error {
-	if b.pipe != nil {
-		b.pipe.Close(errClosedBody)
-	}
-	b.closed = true
-	return nil
-}
-
-func (b *requestBody) Read(p []byte) (n int, err error) {
-	if b.pipe == nil {
-		return 0, io.EOF
-	}
-	n, err = b.pipe.Read(p)
-	if n > 0 {
-		b.sc.sendWindowUpdate(b.streamID, n)
-		// TODO: tell b.sc to send back 'n' flow control quota credits to the sender
-	}
-	return
-}
-
-type responseWriter struct {
-	sc           *serverConn
-	streamID     uint32
-	wroteHeaders bool
-	h            http.Header
-
-	req  *http.Request
-	body *requestBody // to close at end of request, if DATA frames didn't
-}
-
-// TODO: bufio writing of responseWriter. add Flush, add pools of
-// bufio.Writers, adjust bufio writer sized based on frame size
-// updates from peer? For now: naive.
-
-func (w *responseWriter) Header() http.Header {
-	if w.h == nil {
-		w.h = make(http.Header)
-	}
-	return w.h
-}
-
-func (w *responseWriter) WriteHeader(code int) {
-	if w.wroteHeaders {
-		return
-	}
-	// TODO: defer actually writing this frame until a Flush or
-	// handlerDone, like net/http's Server. then we can coalesce
-	// e.g. a 204 response to have a Header response frame with
-	// END_STREAM set, without a separate frame being sent in
-	// handleDone.
-	w.wroteHeaders = true
-	w.sc.writeHeader(headerWriteReq{
-		streamID:    w.streamID,
-		httpResCode: code,
-		h:           w.h,
-	})
-}
-
-// TODO: responseWriter.WriteString too?
-
-func (w *responseWriter) Write(p []byte) (n int, err error) {
-	if !w.wroteHeaders {
-		w.WriteHeader(200)
-	}
-	return w.sc.writeData(w.streamID, p) // blocks waiting for tokens
-}
-
-func (w *responseWriter) handlerDone() {
-	if !w.wroteHeaders {
-		w.sc.writeHeader(headerWriteReq{
-			streamID:    w.streamID,
-			httpResCode: 200,
-			h:           w.h,
-			endStream:   true, // handler has finished; can't be any data.
-		})
-	}
-}
-
-var testHookOnConn func() // for testing
-
 func validHeader(v string) bool {
 	if len(v) == 0 {
 		return false

+ 40 - 789
http2_test.go

@@ -9,24 +9,14 @@ package http2
 
 import (
 	"bytes"
-	"crypto/tls"
 	"errors"
 	"flag"
 	"fmt"
-	"io"
-	"io/ioutil"
-	"log"
-	"net"
 	"net/http"
-	"net/http/httptest"
-	"os"
 	"os/exec"
-	"reflect"
 	"strconv"
 	"strings"
-	"sync/atomic"
 	"testing"
-	"time"
 
 	"github.com/bradfitz/http2/hpack"
 )
@@ -36,785 +26,6 @@ func init() {
 	flag.BoolVar(&VerboseLogs, "verboseh2", false, "Verbose HTTP/2 debug logging")
 }
 
-type serverTester struct {
-	cc     net.Conn // client conn
-	t      *testing.T
-	ts     *httptest.Server
-	fr     *Framer
-	logBuf *bytes.Buffer
-}
-
-func newServerTester(t *testing.T, handler http.HandlerFunc) *serverTester {
-	logBuf := new(bytes.Buffer)
-	ts := httptest.NewUnstartedServer(handler)
-	ConfigureServer(ts.Config, &Server{})
-	ts.TLS = ts.Config.TLSConfig // the httptest.Server has its own copy of this TLS config
-	ts.Config.ErrorLog = log.New(io.MultiWriter(twriter{t: t}, logBuf), "", log.LstdFlags)
-	ts.StartTLS()
-
-	if VerboseLogs {
-		t.Logf("Running test server at: %s", ts.URL)
-	}
-	cc, err := tls.Dial("tcp", ts.Listener.Addr().String(), &tls.Config{
-		InsecureSkipVerify: true,
-		NextProtos:         []string{npnProto},
-	})
-	if err != nil {
-		t.Fatal(err)
-	}
-	log.SetOutput(twriter{t})
-	return &serverTester{
-		t:      t,
-		ts:     ts,
-		cc:     cc,
-		fr:     NewFramer(cc, cc),
-		logBuf: logBuf,
-	}
-}
-
-func (st *serverTester) Close() {
-	st.ts.Close()
-	st.cc.Close()
-	log.SetOutput(os.Stderr)
-}
-
-// greet initiates the client's HTTP/2 connection into a state where
-// frames may be sent.
-func (st *serverTester) greet() {
-	st.writePreface()
-	st.writeInitialSettings()
-	st.wantSettings()
-	st.writeSettingsAck()
-	st.wantSettingsAck()
-}
-
-func (st *serverTester) writePreface() {
-	n, err := st.cc.Write(clientPreface)
-	if err != nil {
-		st.t.Fatalf("Error writing client preface: %v", err)
-	}
-	if n != len(clientPreface) {
-		st.t.Fatalf("Writing client preface, wrote %d bytes; want %d", n, len(clientPreface))
-	}
-}
-
-func (st *serverTester) writeInitialSettings() {
-	if err := st.fr.WriteSettings(); err != nil {
-		st.t.Fatalf("Error writing initial SETTINGS frame from client to server: %v", err)
-	}
-}
-
-func (st *serverTester) writeSettingsAck() {
-	if err := st.fr.WriteSettingsAck(); err != nil {
-		st.t.Fatalf("Error writing ACK of server's SETTINGS: %v", err)
-	}
-}
-
-func (st *serverTester) writeHeaders(p HeadersFrameParam) {
-	if err := st.fr.WriteHeaders(p); err != nil {
-		st.t.Fatalf("Error writing HEADERS: %v", err)
-	}
-}
-
-// bodylessReq1 writes a HEADERS frames with StreamID 1 and EndStream and EndHeaders set.
-func (st *serverTester) bodylessReq1(headers ...string) {
-	st.writeHeaders(HeadersFrameParam{
-		StreamID:      1, // clients send odd numbers
-		BlockFragment: encodeHeader(st.t, headers...),
-		EndStream:     true,
-		EndHeaders:    true,
-	})
-}
-
-func (st *serverTester) writeData(streamID uint32, endStream bool, data []byte) {
-	if err := st.fr.WriteData(streamID, endStream, data); err != nil {
-		st.t.Fatalf("Error writing DATA: %v", err)
-	}
-}
-
-func (st *serverTester) readFrame() (Frame, error) {
-	frc := make(chan Frame, 1)
-	errc := make(chan error, 1)
-	go func() {
-		fr, err := st.fr.ReadFrame()
-		if err != nil {
-			errc <- err
-		} else {
-			frc <- fr
-		}
-	}()
-	t := time.NewTimer(2 * time.Second)
-	defer t.Stop()
-	select {
-	case f := <-frc:
-		return f, nil
-	case err := <-errc:
-		return nil, err
-	case <-t.C:
-		return nil, errors.New("timeout waiting for frame")
-	}
-}
-
-func (st *serverTester) wantSettings() *SettingsFrame {
-	f, err := st.readFrame()
-	if err != nil {
-		st.t.Fatalf("Error while expecting a SETTINGS frame: %v", err)
-	}
-	sf, ok := f.(*SettingsFrame)
-	if !ok {
-		st.t.Fatalf("got a %T; want *SettingsFrame", f)
-	}
-	return sf
-}
-
-func (st *serverTester) wantPing() *PingFrame {
-	f, err := st.readFrame()
-	if err != nil {
-		st.t.Fatalf("Error while expecting a PING frame: %v", err)
-	}
-	pf, ok := f.(*PingFrame)
-	if !ok {
-		st.t.Fatalf("got a %T; want *PingFrame", f)
-	}
-	return pf
-}
-
-func (st *serverTester) wantRSTStream(streamID uint32, errCode ErrCode) {
-	f, err := st.readFrame()
-	if err != nil {
-		st.t.Fatalf("Error while expecting an RSTStream frame: %v", err)
-	}
-	rs, ok := f.(*RSTStreamFrame)
-	if !ok {
-		st.t.Fatalf("got a %T; want *RSTStreamFrame", f)
-	}
-	if rs.FrameHeader.StreamID != streamID {
-		st.t.Fatalf("RSTStream StreamID = %d; want %d", rs.FrameHeader.StreamID, streamID)
-	}
-	if rs.ErrCode != uint32(errCode) {
-		st.t.Fatalf("RSTStream ErrCode = %d (%s); want %d (%s)", rs.ErrCode, rs.ErrCode, errCode, errCode)
-	}
-}
-
-func (st *serverTester) wantWindowUpdate(streamID, incr uint32) {
-	f, err := st.readFrame()
-	if err != nil {
-		st.t.Fatalf("Error while expecting an RSTStream frame: %v", err)
-	}
-	wu, ok := f.(*WindowUpdateFrame)
-	if !ok {
-		st.t.Fatalf("got a %T; want *WindowUpdateFrame", f)
-	}
-	if wu.FrameHeader.StreamID != streamID {
-		st.t.Fatalf("WindowUpdate StreamID = %d; want %d", wu.FrameHeader.StreamID, streamID)
-	}
-	if wu.Increment != incr {
-		st.t.Fatalf("WindowUpdate increment = %d; want %d", wu.Increment, incr)
-	}
-}
-
-func (st *serverTester) wantSettingsAck() {
-	f, err := st.readFrame()
-	if err != nil {
-		st.t.Fatal(err)
-	}
-	sf, ok := f.(*SettingsFrame)
-	if !ok {
-		st.t.Fatalf("Wanting a settings ACK, received a %T", f)
-	}
-	if !sf.Header().Flags.Has(FlagSettingsAck) {
-		st.t.Fatal("Settings Frame didn't have ACK set")
-	}
-
-}
-
-func TestServer(t *testing.T) {
-	gotReq := make(chan bool, 1)
-	st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
-		w.Header().Set("Foo", "Bar")
-		gotReq <- true
-	})
-	defer st.Close()
-
-	covers("3.5", `
-		The server connection preface consists of a potentially empty
-		SETTINGS frame ([SETTINGS]) that MUST be the first frame the
-		server sends in the HTTP/2 connection.
-	`)
-
-	st.writePreface()
-	st.writeInitialSettings()
-	st.wantSettings().ForeachSetting(func(s Setting) error {
-		t.Logf("Server sent setting %v = %v", s.ID, s.Val)
-		return nil
-	})
-	st.writeSettingsAck()
-	st.wantSettingsAck()
-
-	st.writeHeaders(HeadersFrameParam{
-		StreamID:      1, // clients send odd numbers
-		BlockFragment: encodeHeader(t),
-		EndStream:     true, // no DATA frames
-		EndHeaders:    true,
-	})
-
-	select {
-	case <-gotReq:
-	case <-time.After(2 * time.Second):
-		t.Error("timeout waiting for request")
-	}
-}
-
-func TestServer_Request_Get(t *testing.T) {
-	testServerRequest(t, func(st *serverTester) {
-		st.writeHeaders(HeadersFrameParam{
-			StreamID:      1, // clients send odd numbers
-			BlockFragment: encodeHeader(t, "foo-bar", "some-value"),
-			EndStream:     true, // no DATA frames
-			EndHeaders:    true,
-		})
-	}, func(r *http.Request) {
-		if r.Method != "GET" {
-			t.Errorf("Method = %q; want GET", r.Method)
-		}
-		if r.ContentLength != 0 {
-			t.Errorf("ContentLength = %v; want 0", r.ContentLength)
-		}
-		if r.Close {
-			t.Error("Close = true; want false")
-		}
-		if !strings.Contains(r.RemoteAddr, ":") {
-			t.Errorf("RemoteAddr = %q; want something with a colon", r.RemoteAddr)
-		}
-		if r.Proto != "HTTP/2.0" || r.ProtoMajor != 2 || r.ProtoMinor != 0 {
-			t.Errorf("Proto = %q Major=%v,Minor=%v; want HTTP/2.0", r.Proto, r.ProtoMajor, r.ProtoMinor)
-		}
-		wantHeader := http.Header{
-			"Foo-Bar": []string{"some-value"},
-		}
-		if !reflect.DeepEqual(r.Header, wantHeader) {
-			t.Errorf("Header = %#v; want %#v", r.Header, wantHeader)
-		}
-		if n, err := r.Body.Read([]byte(" ")); err != io.EOF || n != 0 {
-			t.Errorf("Read = %d, %v; want 0, EOF", n, err)
-		}
-	})
-}
-
-// TODO: add a test with EndStream=true on the HEADERS but setting a
-// Content-Length anyway.  Should we just omit it and force it to
-// zero?
-
-func TestServer_Request_Post_NoContentLength_EndStream(t *testing.T) {
-	testServerRequest(t, func(st *serverTester) {
-		st.writeHeaders(HeadersFrameParam{
-			StreamID:      1, // clients send odd numbers
-			BlockFragment: encodeHeader(t, ":method", "POST"),
-			EndStream:     true,
-			EndHeaders:    true,
-		})
-	}, func(r *http.Request) {
-		if r.Method != "POST" {
-			t.Errorf("Method = %q; want POST", r.Method)
-		}
-		if r.ContentLength != 0 {
-			t.Errorf("ContentLength = %v; want 0", r.ContentLength)
-		}
-		if n, err := r.Body.Read([]byte(" ")); err != io.EOF || n != 0 {
-			t.Errorf("Read = %d, %v; want 0, EOF", n, err)
-		}
-	})
-}
-
-func TestServer_Request_Post_Body_ImmediateEOF(t *testing.T) {
-	testBodyContents(t, -1, "", func(st *serverTester) {
-		st.writeHeaders(HeadersFrameParam{
-			StreamID:      1, // clients send odd numbers
-			BlockFragment: encodeHeader(t, ":method", "POST"),
-			EndStream:     false, // to say DATA frames are coming
-			EndHeaders:    true,
-		})
-		st.writeData(1, true, nil) // just kidding. empty body.
-	})
-}
-
-func TestServer_Request_Post_Body_OneData(t *testing.T) {
-	const content = "Some content"
-	testBodyContents(t, -1, content, func(st *serverTester) {
-		st.writeHeaders(HeadersFrameParam{
-			StreamID:      1, // clients send odd numbers
-			BlockFragment: encodeHeader(t, ":method", "POST"),
-			EndStream:     false, // to say DATA frames are coming
-			EndHeaders:    true,
-		})
-		st.writeData(1, true, []byte(content))
-	})
-}
-
-func TestServer_Request_Post_Body_TwoData(t *testing.T) {
-	const content = "Some content"
-	testBodyContents(t, -1, content, func(st *serverTester) {
-		st.writeHeaders(HeadersFrameParam{
-			StreamID:      1, // clients send odd numbers
-			BlockFragment: encodeHeader(t, ":method", "POST"),
-			EndStream:     false, // to say DATA frames are coming
-			EndHeaders:    true,
-		})
-		st.writeData(1, false, []byte(content[:5]))
-		st.writeData(1, true, []byte(content[5:]))
-	})
-}
-
-func TestServer_Request_Post_Body_ContentLength_Correct(t *testing.T) {
-	const content = "Some content"
-	testBodyContents(t, int64(len(content)), content, func(st *serverTester) {
-		st.writeHeaders(HeadersFrameParam{
-			StreamID: 1, // clients send odd numbers
-			BlockFragment: encodeHeader(t,
-				":method", "POST",
-				"content-length", strconv.Itoa(len(content)),
-			),
-			EndStream:  false, // to say DATA frames are coming
-			EndHeaders: true,
-		})
-		st.writeData(1, true, []byte(content))
-	})
-}
-
-func TestServer_Request_Post_Body_ContentLength_TooLarge(t *testing.T) {
-	testBodyContentsFail(t, 3, "Request declared a Content-Length of 3 but only wrote 2 bytes",
-		func(st *serverTester) {
-			st.writeHeaders(HeadersFrameParam{
-				StreamID: 1, // clients send odd numbers
-				BlockFragment: encodeHeader(t,
-					":method", "POST",
-					"content-length", "3",
-				),
-				EndStream:  false, // to say DATA frames are coming
-				EndHeaders: true,
-			})
-			st.writeData(1, true, []byte("12"))
-		})
-}
-
-func TestServer_Request_Post_Body_ContentLength_TooSmall(t *testing.T) {
-	testBodyContentsFail(t, 4, "Sender tried to send more than declared Content-Length of 4 bytes",
-		func(st *serverTester) {
-			st.writeHeaders(HeadersFrameParam{
-				StreamID: 1, // clients send odd numbers
-				BlockFragment: encodeHeader(t,
-					":method", "POST",
-					"content-length", "4",
-				),
-				EndStream:  false, // to say DATA frames are coming
-				EndHeaders: true,
-			})
-			st.writeData(1, true, []byte("12345"))
-		})
-}
-
-func testBodyContents(t *testing.T, wantContentLength int64, wantBody string, write func(st *serverTester)) {
-	testServerRequest(t, write, func(r *http.Request) {
-		if r.Method != "POST" {
-			t.Errorf("Method = %q; want POST", r.Method)
-		}
-		if r.ContentLength != wantContentLength {
-			t.Errorf("ContentLength = %v; want %d", r.ContentLength, wantContentLength)
-		}
-		all, err := ioutil.ReadAll(r.Body)
-		if err != nil {
-			t.Fatal(err)
-		}
-		if string(all) != wantBody {
-			t.Errorf("Read = %q; want %q", all, wantBody)
-		}
-		if err := r.Body.Close(); err != nil {
-			t.Fatalf("Close: %v", err)
-		}
-	})
-}
-
-func testBodyContentsFail(t *testing.T, wantContentLength int64, wantReadError string, write func(st *serverTester)) {
-	testServerRequest(t, write, func(r *http.Request) {
-		if r.Method != "POST" {
-			t.Errorf("Method = %q; want POST", r.Method)
-		}
-		if r.ContentLength != wantContentLength {
-			t.Errorf("ContentLength = %v; want %d", r.ContentLength, wantContentLength)
-		}
-		all, err := ioutil.ReadAll(r.Body)
-		if err == nil {
-			t.Fatalf("expected an error (%q) reading from the body. Successfully read %q instead.",
-				wantReadError, all)
-		}
-		if !strings.Contains(err.Error(), wantReadError) {
-			t.Fatalf("Body.Read = %v; want substring %q", err, wantReadError)
-		}
-		if err := r.Body.Close(); err != nil {
-			t.Fatalf("Close: %v", err)
-		}
-	})
-}
-
-// Using a Host header, instead of :authority
-func TestServer_Request_Get_Host(t *testing.T) {
-	const host = "example.com"
-	testServerRequest(t, func(st *serverTester) {
-		st.writeHeaders(HeadersFrameParam{
-			StreamID:      1, // clients send odd numbers
-			BlockFragment: encodeHeader(t, "host", host),
-			EndStream:     true,
-			EndHeaders:    true,
-		})
-	}, func(r *http.Request) {
-		if r.Host != host {
-			t.Errorf("Host = %q; want %q", r.Host, host)
-		}
-	})
-}
-
-// Using an :authority pseudo-header, instead of Host
-func TestServer_Request_Get_Authority(t *testing.T) {
-	const host = "example.com"
-	testServerRequest(t, func(st *serverTester) {
-		st.writeHeaders(HeadersFrameParam{
-			StreamID:      1, // clients send odd numbers
-			BlockFragment: encodeHeader(t, ":authority", host),
-			EndStream:     true,
-			EndHeaders:    true,
-		})
-	}, func(r *http.Request) {
-		if r.Host != host {
-			t.Errorf("Host = %q; want %q", r.Host, host)
-		}
-	})
-}
-
-func TestServer_Request_WithContinuation(t *testing.T) {
-	wantHeader := http.Header{
-		"Foo-One":   []string{"value-one"},
-		"Foo-Two":   []string{"value-two"},
-		"Foo-Three": []string{"value-three"},
-	}
-	testServerRequest(t, func(st *serverTester) {
-		fullHeaders := encodeHeader(t,
-			"foo-one", "value-one",
-			"foo-two", "value-two",
-			"foo-three", "value-three",
-		)
-		remain := fullHeaders
-		chunks := 0
-		for len(remain) > 0 {
-			const maxChunkSize = 5
-			chunk := remain
-			if len(chunk) > maxChunkSize {
-				chunk = chunk[:maxChunkSize]
-			}
-			remain = remain[len(chunk):]
-
-			if chunks == 0 {
-				st.writeHeaders(HeadersFrameParam{
-					StreamID:      1, // clients send odd numbers
-					BlockFragment: chunk,
-					EndStream:     true,  // no DATA frames
-					EndHeaders:    false, // we'll have continuation frames
-				})
-			} else {
-				err := st.fr.WriteContinuation(1, len(remain) == 0, chunk)
-				if err != nil {
-					t.Fatal(err)
-				}
-			}
-			chunks++
-		}
-		if chunks < 2 {
-			t.Fatal("too few chunks")
-		}
-	}, func(r *http.Request) {
-		if !reflect.DeepEqual(r.Header, wantHeader) {
-			t.Errorf("Header = %#v; want %#v", r.Header, wantHeader)
-		}
-	})
-}
-
-// Concatenated cookie headers. ("8.1.2.5 Compressing the Cookie Header Field")
-func TestServer_Request_CookieConcat(t *testing.T) {
-	const host = "example.com"
-	testServerRequest(t, func(st *serverTester) {
-		st.bodylessReq1(
-			":authority", host,
-			"cookie", "a=b",
-			"cookie", "c=d",
-			"cookie", "e=f",
-		)
-	}, func(r *http.Request) {
-		const want = "a=b; c=d; e=f"
-		if got := r.Header.Get("Cookie"); got != want {
-			t.Errorf("Cookie = %q; want %q", got, want)
-		}
-	})
-}
-
-func TestServer_Request_Reject_CapitalHeader(t *testing.T) {
-	testRejectRequest(t, func(st *serverTester) { st.bodylessReq1("UPPER", "v") })
-}
-
-func TestServer_Request_Reject_Pseudo_Missing_method(t *testing.T) {
-	testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":method", "") })
-}
-
-func TestServer_Request_Reject_Pseudo_ExactlyOne(t *testing.T) {
-	// 8.1.2.3 Request Pseudo-Header Fields
-	// "All HTTP/2 requests MUST include exactly one valid value" ...
-	testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":method", "GET", ":method", "POST") })
-}
-
-func TestServer_Request_Reject_Pseudo_AfterRegular(t *testing.T) {
-	// 8.1.2.3 Request Pseudo-Header Fields
-	// "All pseudo-header fields MUST appear in the header block
-	// before regular header fields. Any request or response that
-	// contains a pseudo-header field that appears in a header
-	// block after a regular header field MUST be treated as
-	// malformed (Section 8.1.2.6)."
-	testRejectRequest(t, func(st *serverTester) {
-		var buf bytes.Buffer
-		enc := hpack.NewEncoder(&buf)
-		enc.WriteField(hpack.HeaderField{Name: ":method", Value: "GET"})
-		enc.WriteField(hpack.HeaderField{Name: "regular", Value: "foobar"})
-		enc.WriteField(hpack.HeaderField{Name: ":path", Value: "/"})
-		enc.WriteField(hpack.HeaderField{Name: ":scheme", Value: "https"})
-		st.writeHeaders(HeadersFrameParam{
-			StreamID:      1, // clients send odd numbers
-			BlockFragment: buf.Bytes(),
-			EndStream:     true,
-			EndHeaders:    true,
-		})
-	})
-}
-
-func TestServer_Request_Reject_Pseudo_Missing_path(t *testing.T) {
-	testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":path", "") })
-}
-
-func TestServer_Request_Reject_Pseudo_Missing_scheme(t *testing.T) {
-	testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":scheme", "") })
-}
-
-func TestServer_Request_Reject_Pseudo_scheme_invalid(t *testing.T) {
-	testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":scheme", "bogus") })
-}
-
-func TestServer_Request_Reject_Pseudo_Unknown(t *testing.T) {
-	testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":unknown_thing", "") })
-}
-
-func testRejectRequest(t *testing.T, send func(*serverTester)) {
-	st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
-		t.Fatal("server request made it to handler; should've been rejected")
-	})
-	defer st.Close()
-
-	st.greet()
-	send(st)
-	st.wantRSTStream(1, ErrCodeProtocol)
-}
-
-func TestServer_Ping(t *testing.T) {
-	st := newServerTester(t, nil)
-	defer st.Close()
-	st.greet()
-
-	// Server should ignore this one, since it has ACK set.
-	ackPingData := [8]byte{1, 2, 4, 8, 16, 32, 64, 128}
-	if err := st.fr.WritePing(true, ackPingData); err != nil {
-		t.Fatal(err)
-	}
-
-	// But the server should reply to this one, since ACK is false.
-	pingData := [8]byte{1, 2, 3, 4, 5, 6, 7, 8}
-	if err := st.fr.WritePing(false, pingData); err != nil {
-		t.Fatal(err)
-	}
-
-	pf := st.wantPing()
-	if !pf.Flags.Has(FlagPingAck) {
-		t.Error("response ping doesn't have ACK set")
-	}
-	if pf.Data != pingData {
-		t.Errorf("response ping has data %q; want %q", pf.Data, pingData)
-	}
-}
-
-func TestServer_Handler_Sends_WindowUpdate(t *testing.T) {
-	puppet := newHandlerPuppet()
-	st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
-		puppet.act(w, r)
-	})
-	defer st.Close()
-	defer puppet.done()
-
-	st.greet()
-
-	st.writeHeaders(HeadersFrameParam{
-		StreamID:      1, // clients send odd numbers
-		BlockFragment: encodeHeader(t, ":method", "POST"),
-		EndStream:     false, // data coming
-		EndHeaders:    true,
-	})
-	st.writeData(1, true, []byte("abcdef"))
-	puppet.do(func(w http.ResponseWriter, r *http.Request) {
-		buf := make([]byte, 3)
-		_, err := io.ReadFull(r.Body, buf)
-		if err != nil {
-			t.Error(err)
-			return
-		}
-		if string(buf) != "abc" {
-			t.Errorf("read %q; want abc", buf)
-		}
-	})
-	st.wantWindowUpdate(0, 3)
-	st.wantWindowUpdate(1, 3)
-	puppet.do(func(w http.ResponseWriter, r *http.Request) {
-		buf := make([]byte, 3)
-		_, err := io.ReadFull(r.Body, buf)
-		if err != nil {
-			t.Error(err)
-			return
-		}
-		if string(buf) != "def" {
-			t.Errorf("read %q; want abc", buf)
-		}
-	})
-	st.wantWindowUpdate(0, 3)
-	st.wantWindowUpdate(1, 3)
-}
-
-// TODO: test HEADERS w/o EndHeaders + another HEADERS (should get rejected)
-// TODO: test HEADERS w/ EndHeaders + a continuation HEADERS (should get rejected)
-
-// testServerRequest sets up an idle HTTP/2 connection and lets you
-// write a single request with writeReq, and then verify that the
-// *http.Request is built correctly in checkReq.
-func testServerRequest(t *testing.T, writeReq func(*serverTester), checkReq func(*http.Request)) {
-	gotReq := make(chan bool, 1)
-	st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
-		if r.Body == nil {
-			t.Fatal("nil Body")
-		}
-		checkReq(r)
-		gotReq <- true
-	})
-	defer st.Close()
-
-	st.greet()
-	writeReq(st)
-
-	select {
-	case <-gotReq:
-	case <-time.After(2 * time.Second):
-		t.Error("timeout waiting for request")
-	}
-
-}
-
-func TestServerWithCurl(t *testing.T) {
-	requireCurl(t)
-
-	ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		// TODO: add a bunch of different tests with different
-		// behavior, as a function of r or a table.
-		// -- with request body, without.
-		// -- no interaction with w.
-		// -- panic
-		// -- modify Header only, but no writes or writeheader (this test)
-		// -- WriteHeader only
-		// -- Write only
-		// -- WriteString
-		// -- both
-		// -- huge headers over a frame size so we get continuation headers.
-		// Look at net/http's Server tests for inspiration.
-		w.Header().Set("Foo", "Bar")
-	}))
-	ConfigureServer(ts.Config, &Server{})
-	ts.TLS = ts.Config.TLSConfig // the httptest.Server has its own copy of this TLS config
-	ts.StartTLS()
-	defer ts.Close()
-
-	var gotConn int32
-	testHookOnConn = func() { atomic.StoreInt32(&gotConn, 1) }
-
-	t.Logf("Running test server for curl to hit at: %s", ts.URL)
-	container := curl(t, "--silent", "--http2", "--insecure", "-v", ts.URL)
-	defer kill(container)
-	resc := make(chan interface{}, 1)
-	go func() {
-		res, err := dockerLogs(container)
-		if err != nil {
-			resc <- err
-		} else {
-			resc <- res
-		}
-	}()
-	select {
-	case res := <-resc:
-		if err, ok := res.(error); ok {
-			t.Fatal(err)
-		}
-		if !strings.Contains(string(res.([]byte)), "< foo:Bar") {
-			t.Errorf("didn't see foo:Bar header")
-			t.Logf("Got: %s", res)
-		}
-	case <-time.After(3 * time.Second):
-		t.Errorf("timeout waiting for curl")
-	}
-
-	if atomic.LoadInt32(&gotConn) == 0 {
-		t.Error("never saw an http2 connection")
-	}
-}
-
-func dockerLogs(container string) ([]byte, error) {
-	out, err := exec.Command("docker", "wait", container).CombinedOutput()
-	if err != nil {
-		return out, err
-	}
-	exitStatus, err := strconv.Atoi(strings.TrimSpace(string(out)))
-	if err != nil {
-		return out, errors.New("unexpected exit status from docker wait")
-	}
-	out, err = exec.Command("docker", "logs", container).CombinedOutput()
-	exec.Command("docker", "rm", container).Run()
-	if err == nil && exitStatus != 0 {
-		err = fmt.Errorf("exit status %d", exitStatus)
-	}
-	return out, err
-}
-
-func kill(container string) {
-	exec.Command("docker", "kill", container).Run()
-	exec.Command("docker", "rm", container).Run()
-}
-
-// Verify that curl has http2.
-func requireCurl(t *testing.T) {
-	out, err := dockerLogs(curl(t, "--version"))
-	if err != nil {
-		t.Skipf("failed to determine curl features; skipping test")
-	}
-	if !strings.Contains(string(out), "HTTP2") {
-		t.Skip("curl doesn't support HTTP2; skipping test")
-	}
-}
-
-func curl(t *testing.T, args ...string) (container string) {
-	out, err := exec.Command("docker", append([]string{"run", "-d", "--net=host", "gohttp2/curl"}, args...)...).CombinedOutput()
-	if err != nil {
-		t.Skipf("Failed to run curl in docker: %v, %s", err, out)
-	}
-	return strings.TrimSpace(string(out))
-}
-
 type twriter struct {
 	t testing.TB
 }
@@ -869,6 +80,25 @@ func encodeHeader(t *testing.T, headers ...string) []byte {
 	return buf.Bytes()
 }
 
+// Verify that curl has http2.
+func requireCurl(t *testing.T) {
+	out, err := dockerLogs(curl(t, "--version"))
+	if err != nil {
+		t.Skipf("failed to determine curl features; skipping test")
+	}
+	if !strings.Contains(string(out), "HTTP2") {
+		t.Skip("curl doesn't support HTTP2; skipping test")
+	}
+}
+
+func curl(t *testing.T, args ...string) (container string) {
+	out, err := exec.Command("docker", append([]string{"run", "-d", "--net=host", "gohttp2/curl"}, args...)...).CombinedOutput()
+	if err != nil {
+		t.Skipf("Failed to run curl in docker: %v, %s", err, out)
+	}
+	return strings.TrimSpace(string(out))
+}
+
 type puppetCommand struct {
 	fn   func(w http.ResponseWriter, r *http.Request)
 	done chan<- bool
@@ -897,3 +127,24 @@ func (p *handlerPuppet) do(fn func(http.ResponseWriter, *http.Request)) {
 	p.ch <- puppetCommand{fn, done}
 	<-done
 }
+func dockerLogs(container string) ([]byte, error) {
+	out, err := exec.Command("docker", "wait", container).CombinedOutput()
+	if err != nil {
+		return out, err
+	}
+	exitStatus, err := strconv.Atoi(strings.TrimSpace(string(out)))
+	if err != nil {
+		return out, errors.New("unexpected exit status from docker wait")
+	}
+	out, err = exec.Command("docker", "logs", container).CombinedOutput()
+	exec.Command("docker", "rm", container).Run()
+	if err == nil && exitStatus != 0 {
+		err = fmt.Errorf("exit status %d", exitStatus)
+	}
+	return out, err
+}
+
+func kill(container string) {
+	exec.Command("docker", "kill", container).Run()
+	exec.Command("docker", "rm", container).Run()
+}

+ 870 - 0
server.go

@@ -0,0 +1,870 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+package http2
+
+import (
+	"bytes"
+	"crypto/tls"
+	"errors"
+	"fmt"
+	"io"
+	"log"
+	"net"
+	"net/http"
+	"net/url"
+	"strconv"
+	"strings"
+
+	"github.com/bradfitz/http2/hpack"
+)
+
+// TODO: finish GOAWAY support. Consider each incoming frame type and whether
+// it should be ignored during a shutdown race.
+
+// Server is an HTTP/2 server.
+type Server struct {
+	// MaxStreams optionally ...
+	MaxStreams int
+}
+
+func (srv *Server) handleConn(hs *http.Server, c net.Conn, h http.Handler) {
+	sc := &serverConn{
+		hs:                hs,
+		conn:              c,
+		handler:           h,
+		framer:            NewFramer(c, c), // TODO: write to a (custom?) buffered writer that can alternate when it's in buffered mode.
+		streams:           make(map[uint32]*stream),
+		canonHeader:       make(map[string]string),
+		readFrameCh:       make(chan frameAndProcessed),
+		readFrameErrCh:    make(chan error, 1),
+		writeHeaderCh:     make(chan headerWriteReq), // must not be buffered
+		windowUpdateCh:    make(chan windowUpdateReq, 8),
+		flow:              newFlow(initialWindowSize),
+		doneServing:       make(chan struct{}),
+		maxWriteFrameSize: initialMaxFrameSize,
+		initialWindowSize: initialWindowSize,
+		serveG:            newGoroutineLock(),
+	}
+	sc.hpackEncoder = hpack.NewEncoder(&sc.headerWriteBuf)
+	sc.hpackDecoder = hpack.NewDecoder(initialHeaderTableSize, sc.onNewHeaderField)
+	sc.serve()
+}
+
+// frameAndProcessed coordinates the readFrames and serve goroutines, since
+// the Framer interface only permits the most recently-read Frame from being
+// accessed. The serve goroutine sends on processed to signal to the readFrames
+// goroutine that another frame may be read.
+type frameAndProcessed struct {
+	f         Frame
+	processed chan struct{}
+}
+
+type serverConn struct {
+	// Immutable:
+	hs             *http.Server
+	conn           net.Conn
+	handler        http.Handler
+	framer         *Framer
+	hpackDecoder   *hpack.Decoder
+	hpackEncoder   *hpack.Encoder
+	doneServing    chan struct{}          // closed when serverConn.serve ends
+	readFrameCh    chan frameAndProcessed // written by serverConn.readFrames
+	readFrameErrCh chan error
+	writeHeaderCh  chan headerWriteReq // must not be buffered
+	windowUpdateCh chan windowUpdateReq
+	serveG         goroutineLock // used to verify funcs are on serve()
+	flow           *flow         // the connection-wide one
+
+	// Everything following is owned by the serve loop; use serveG.check()
+	maxStreamID       uint32 // max ever seen
+	streams           map[uint32]*stream
+	maxWriteFrameSize uint32 // TODO: update this when settings come in
+	initialWindowSize int32
+	canonHeader       map[string]string // http2-lower-case -> Go-Canonical-Case
+	sentGoAway        bool
+	req               requestParam // non-zero while reading request headers
+	headerWriteBuf    bytes.Buffer // used to write response headers
+}
+
+// requestParam is the state of the next request, initialized over
+// potentially several frames HEADERS + zero or more CONTINUATION
+// frames.
+type requestParam struct {
+	// stream is non-nil if we're reading (HEADER or CONTINUATION)
+	// frames for a request (but not DATA).
+	stream            *stream
+	header            http.Header
+	method, path      string
+	scheme, authority string
+	sawRegularHeader  bool // saw a non-pseudo header already
+	invalidHeader     bool // an invalid header was seen
+}
+
+type stream struct {
+	id    uint32
+	state streamState // owned by serverConn's processing loop
+	flow  *flow       // limits writing from Handler to client
+	body  *pipe       // non-nil if expecting DATA frames
+
+	bodyBytes     int64 // body bytes seen so far
+	declBodyBytes int64 // or -1 if undeclared
+}
+
+func (sc *serverConn) state(streamID uint32) streamState {
+	sc.serveG.check()
+	// http://http2.github.io/http2-spec/#rfc.section.5.1
+	if st, ok := sc.streams[streamID]; ok {
+		return st.state
+	}
+	// "The first use of a new stream identifier implicitly closes all
+	// streams in the "idle" state that might have been initiated by
+	// that peer with a lower-valued stream identifier. For example, if
+	// a client sends a HEADERS frame on stream 7 without ever sending a
+	// frame on stream 5, then stream 5 transitions to the "closed"
+	// state when the first frame for stream 7 is sent or received."
+	if streamID <= sc.maxStreamID {
+		return stateClosed
+	}
+	return stateIdle
+}
+
+func (sc *serverConn) vlogf(format string, args ...interface{}) {
+	if VerboseLogs {
+		sc.logf(format, args...)
+	}
+}
+
+func (sc *serverConn) logf(format string, args ...interface{}) {
+	if lg := sc.hs.ErrorLog; lg != nil {
+		lg.Printf(format, args...)
+	} else {
+		log.Printf(format, args...)
+	}
+}
+
+func (sc *serverConn) condlogf(err error, format string, args ...interface{}) {
+	if err == nil {
+		return
+	}
+	str := err.Error()
+	if strings.Contains(str, "use of closed network connection") {
+		// Boring, expected errors.
+		sc.vlogf(format, args...)
+	} else {
+		sc.logf(format, args...)
+	}
+}
+
+func (sc *serverConn) onNewHeaderField(f hpack.HeaderField) {
+	sc.serveG.check()
+	switch {
+	case !validHeader(f.Name):
+		sc.req.invalidHeader = true
+	case strings.HasPrefix(f.Name, ":"):
+		if sc.req.sawRegularHeader {
+			sc.logf("pseudo-header after regular header")
+			sc.req.invalidHeader = true
+			return
+		}
+		var dst *string
+		switch f.Name {
+		case ":method":
+			dst = &sc.req.method
+		case ":path":
+			dst = &sc.req.path
+		case ":scheme":
+			dst = &sc.req.scheme
+		case ":authority":
+			dst = &sc.req.authority
+		default:
+			// 8.1.2.1 Pseudo-Header Fields
+			// "Endpoints MUST treat a request or response
+			// that contains undefined or invalid
+			// pseudo-header fields as malformed (Section
+			// 8.1.2.6)."
+			sc.logf("invalid pseudo-header %q", f.Name)
+			sc.req.invalidHeader = true
+			return
+		}
+		if *dst != "" {
+			sc.logf("duplicate pseudo-header %q sent", f.Name)
+			sc.req.invalidHeader = true
+			return
+		}
+		*dst = f.Value
+	case f.Name == "cookie":
+		sc.req.sawRegularHeader = true
+		if s, ok := sc.req.header["Cookie"]; ok && len(s) == 1 {
+			s[0] = s[0] + "; " + f.Value
+		} else {
+			sc.req.header.Add("Cookie", f.Value)
+		}
+	default:
+		sc.req.sawRegularHeader = true
+		sc.req.header.Add(sc.canonicalHeader(f.Name), f.Value)
+	}
+}
+
+func (sc *serverConn) canonicalHeader(v string) string {
+	sc.serveG.check()
+	// TODO: use a sync.Pool instead of putting the cache on *serverConn?
+	cv, ok := sc.canonHeader[v]
+	if !ok {
+		cv = http.CanonicalHeaderKey(v)
+		sc.canonHeader[v] = cv
+	}
+	return cv
+}
+
+// readFrames is the loop that reads incoming frames.
+// It's run on its own goroutine.
+func (sc *serverConn) readFrames() {
+	processed := make(chan struct{}, 1)
+	for {
+		f, err := sc.framer.ReadFrame()
+		if err != nil {
+			close(sc.readFrameCh)
+			sc.readFrameErrCh <- err
+			return
+		}
+		sc.readFrameCh <- frameAndProcessed{f, processed}
+		<-processed
+	}
+}
+
+func (sc *serverConn) serve() {
+	sc.serveG.check()
+	defer sc.conn.Close()
+	defer close(sc.doneServing)
+
+	sc.vlogf("HTTP/2 connection from %v on %p", sc.conn.RemoteAddr(), sc.hs)
+
+	// Read the client preface
+	buf := make([]byte, len(ClientPreface))
+	// TODO: timeout reading from the client
+	if _, err := io.ReadFull(sc.conn, buf); err != nil {
+		sc.logf("error reading client preface: %v", err)
+		return
+	}
+	if !bytes.Equal(buf, clientPreface) {
+		sc.logf("bogus greeting from client: %q", buf)
+		return
+	}
+	sc.vlogf("client %v said hello", sc.conn.RemoteAddr())
+
+	f, err := sc.framer.ReadFrame()
+	if err != nil {
+		sc.logf("error reading initial frame from client: %v", err)
+		return
+	}
+	sf, ok := f.(*SettingsFrame)
+	if !ok {
+		sc.logf("invalid initial frame type %T received from client", f)
+		return
+	}
+	if err := sf.ForeachSetting(sc.processSetting); err != nil {
+		sc.logf("initial settings error: %v", err)
+		return
+	}
+
+	// TODO: don't send two network packets for our SETTINGS + our
+	// ACK of their settings.  But if we make framer write to a
+	// *bufio.Writer, that increases the per-connection memory
+	// overhead, and there could be many idle conns. So maybe some
+	// liveswitchWriter-like thing where we only switch to a
+	// *bufio Writer when we really need one temporarily, else go
+	// back to an unbuffered writes by default.
+	if err := sc.framer.WriteSettings( /* TODO: actual settings */ ); err != nil {
+		sc.logf("error writing server's initial settings: %v", err)
+		return
+	}
+	if err := sc.framer.WriteSettingsAck(); err != nil {
+		sc.logf("error writing server's ack of client's settings: %v", err)
+		return
+	}
+
+	go sc.readFrames()
+
+	for {
+		select {
+		case hr := <-sc.writeHeaderCh:
+			if err := sc.writeHeaderInLoop(hr); err != nil {
+				sc.condlogf(err, "error writing response header: %v", err)
+				return
+			}
+		case wu := <-sc.windowUpdateCh:
+			if err := sc.sendWindowUpdateInLoop(wu); err != nil {
+				sc.condlogf(err, "error writing window update: %v", err)
+				return
+			}
+		case fp, ok := <-sc.readFrameCh:
+			if !ok {
+				err := <-sc.readFrameErrCh
+				if err != io.EOF {
+					errstr := err.Error()
+					if !strings.Contains(errstr, "use of closed network connection") {
+						sc.logf("client %s stopped sending frames: %v", sc.conn.RemoteAddr(), errstr)
+					}
+				}
+				return
+			}
+			f := fp.f
+			sc.vlogf("got %v: %#v", f.Header(), f)
+			err := sc.processFrame(f)
+			fp.processed <- struct{}{} // let readFrames proceed
+			switch ev := err.(type) {
+			case nil:
+				// nothing.
+			case StreamError:
+				if err := sc.resetStreamInLoop(ev); err != nil {
+					sc.logf("Error writing RSTSTream: %v", err)
+					return
+				}
+			case ConnectionError:
+				sc.logf("Disconnecting; %v", ev)
+				return
+			case goAwayFlowError:
+				if err := sc.goAway(ErrCodeFlowControl); err != nil {
+					sc.condlogf(err, "failed to GOAWAY: %v", err)
+					return
+				}
+			default:
+				sc.logf("Disconnection due to other error: %v", err)
+				return
+			}
+		}
+	}
+}
+
+func (sc *serverConn) goAway(code ErrCode) error {
+	sc.serveG.check()
+	sc.sentGoAway = true
+	return sc.framer.WriteGoAway(sc.maxStreamID, code, nil)
+}
+
+func (sc *serverConn) resetStreamInLoop(se StreamError) error {
+	sc.serveG.check()
+	if err := sc.framer.WriteRSTStream(se.streamID, uint32(se.code)); err != nil {
+		return err
+	}
+	delete(sc.streams, se.streamID)
+	return nil
+}
+
+func (sc *serverConn) curHeaderStreamID() uint32 {
+	sc.serveG.check()
+	st := sc.req.stream
+	if st == nil {
+		return 0
+	}
+	return st.id
+}
+
+func (sc *serverConn) processFrame(f Frame) error {
+	sc.serveG.check()
+
+	if s := sc.curHeaderStreamID(); s != 0 {
+		if cf, ok := f.(*ContinuationFrame); !ok {
+			return ConnectionError(ErrCodeProtocol)
+		} else if cf.Header().StreamID != s {
+			return ConnectionError(ErrCodeProtocol)
+		}
+	}
+
+	switch f := f.(type) {
+	case *SettingsFrame:
+		return sc.processSettings(f)
+	case *HeadersFrame:
+		return sc.processHeaders(f)
+	case *ContinuationFrame:
+		return sc.processContinuation(f)
+	case *WindowUpdateFrame:
+		return sc.processWindowUpdate(f)
+	case *PingFrame:
+		return sc.processPing(f)
+	case *DataFrame:
+		return sc.processData(f)
+	default:
+		log.Printf("Ignoring unknown frame %#v", f)
+		return nil
+	}
+}
+
+func (sc *serverConn) processPing(f *PingFrame) error {
+	sc.serveG.check()
+	if f.Flags.Has(FlagSettingsAck) {
+		// 6.7 PING: " An endpoint MUST NOT respond to PING frames
+		// containing this flag."
+		return nil
+	}
+	if f.StreamID != 0 {
+		// "PING frames are not associated with any individual
+		// stream. If a PING frame is received with a stream
+		// identifier field value other than 0x0, the recipient MUST
+		// respond with a connection error (Section 5.4.1) of type
+		// PROTOCOL_ERROR."
+		return ConnectionError(ErrCodeProtocol)
+	}
+	return sc.framer.WritePing(true, f.Data)
+}
+
+func (sc *serverConn) processWindowUpdate(f *WindowUpdateFrame) error {
+	sc.serveG.check()
+	switch {
+	case f.StreamID != 0: // stream-level flow control
+		st := sc.streams[f.StreamID]
+		if st == nil {
+			// "WINDOW_UPDATE can be sent by a peer that has sent a
+			// frame bearing the END_STREAM flag. This means that a
+			// receiver could receive a WINDOW_UPDATE frame on a "half
+			// closed (remote)" or "closed" stream. A receiver MUST
+			// NOT treat this as an error, see Section 5.1."
+			return nil
+		}
+		if !st.flow.add(int32(f.Increment)) {
+			return StreamError{f.StreamID, ErrCodeFlowControl}
+		}
+	default: // connection-level flow control
+		if !sc.flow.add(int32(f.Increment)) {
+			return goAwayFlowError{}
+		}
+	}
+	return nil
+}
+
+func (sc *serverConn) processSettings(f *SettingsFrame) error {
+	sc.serveG.check()
+	return f.ForeachSetting(sc.processSetting)
+}
+
+func (sc *serverConn) processSetting(s Setting) error {
+	sc.serveG.check()
+	sc.vlogf("processing setting %v", s)
+	switch s.ID {
+	case SettingInitialWindowSize:
+		return sc.processSettingInitialWindowSize(s.Val)
+	}
+	log.Printf("TODO: handle %v", s)
+	return nil
+}
+
+func (sc *serverConn) processSettingInitialWindowSize(val uint32) error {
+	sc.serveG.check()
+	if val > (1<<31 - 1) {
+		// 6.5.2 Defined SETTINGS Parameters
+		// "Values above the maximum flow control window size of
+		// 231-1 MUST be treated as a connection error (Section
+		// 5.4.1) of type FLOW_CONTROL_ERROR."
+		return ConnectionError(ErrCodeFlowControl)
+	}
+
+	// "A SETTINGS frame can alter the initial flow control window
+	// size for all current streams. When the value of
+	// SETTINGS_INITIAL_WINDOW_SIZE changes, a receiver MUST
+	// adjust the size of all stream flow control windows that it
+	// maintains by the difference between the new value and the
+	// old value."
+	old := sc.initialWindowSize
+	sc.initialWindowSize = int32(val)
+	growth := sc.initialWindowSize - old // may be negative
+	for _, st := range sc.streams {
+		if !st.flow.add(growth) {
+			// 6.9.2 Initial Flow Control Window Size
+			// "An endpoint MUST treat a change to
+			// SETTINGS_INITIAL_WINDOW_SIZE that causes any flow
+			// control window to exceed the maximum size as a
+			// connection error (Section 5.4.1) of type
+			// FLOW_CONTROL_ERROR."
+			return ConnectionError(ErrCodeFlowControl)
+		}
+	}
+	return nil
+}
+
+func (sc *serverConn) processData(f *DataFrame) error {
+	sc.serveG.check()
+	// "If a DATA frame is received whose stream is not in "open"
+	// or "half closed (local)" state, the recipient MUST respond
+	// with a stream error (Section 5.4.2) of type STREAM_CLOSED."
+	id := f.Header().StreamID
+	st, ok := sc.streams[id]
+	if !ok || (st.state != stateOpen && st.state != stateHalfClosedLocal) {
+		return StreamError{id, ErrCodeStreamClosed}
+	}
+	if st.body == nil {
+		// Not expecting data.
+		// TODO: which error code?
+		return StreamError{id, ErrCodeStreamClosed}
+	}
+	data := f.Data()
+
+	// Sender sending more than they'd declared?
+	if st.declBodyBytes != -1 && st.bodyBytes+int64(len(data)) > st.declBodyBytes {
+		st.body.Close(fmt.Errorf("Sender tried to send more than declared Content-Length of %d bytes", st.declBodyBytes))
+		return StreamError{id, ErrCodeStreamClosed}
+	}
+	if len(data) > 0 {
+		// TODO: verify they're allowed to write with the flow control
+		// window we'd advertised to them.
+		// TODO: verify n from Write
+		if _, err := st.body.Write(data); err != nil {
+			return StreamError{id, ErrCodeStreamClosed}
+		}
+		st.bodyBytes += int64(len(data))
+	}
+	if f.Header().Flags.Has(FlagDataEndStream) {
+		if st.declBodyBytes != -1 && st.declBodyBytes != st.bodyBytes {
+			st.body.Close(fmt.Errorf("Request declared a Content-Length of %d but only wrote %d bytes",
+				st.declBodyBytes, st.bodyBytes))
+		} else {
+			st.body.Close(io.EOF)
+		}
+	}
+	return nil
+}
+
+func (sc *serverConn) processHeaders(f *HeadersFrame) error {
+	sc.serveG.check()
+	id := f.Header().StreamID
+	if sc.sentGoAway {
+		// Ignore.
+		return nil
+	}
+	// http://http2.github.io/http2-spec/#rfc.section.5.1.1
+	if id%2 != 1 || id <= sc.maxStreamID || sc.req.stream != nil {
+		// Streams initiated by a client MUST use odd-numbered
+		// stream identifiers. [...] The identifier of a newly
+		// established stream MUST be numerically greater than all
+		// streams that the initiating endpoint has opened or
+		// reserved. [...]  An endpoint that receives an unexpected
+		// stream identifier MUST respond with a connection error
+		// (Section 5.4.1) of type PROTOCOL_ERROR.
+		return ConnectionError(ErrCodeProtocol)
+	}
+	if id > sc.maxStreamID {
+		sc.maxStreamID = id
+	}
+	st := &stream{
+		id:    id,
+		state: stateOpen,
+		flow:  newFlow(sc.initialWindowSize),
+	}
+	if f.Header().Flags.Has(FlagHeadersEndStream) {
+		st.state = stateHalfClosedRemote
+	}
+	sc.streams[id] = st
+	sc.req = requestParam{
+		stream: st,
+		header: make(http.Header),
+	}
+	return sc.processHeaderBlockFragment(st, f.HeaderBlockFragment(), f.HeadersEnded())
+}
+
+func (sc *serverConn) processContinuation(f *ContinuationFrame) error {
+	sc.serveG.check()
+	st := sc.streams[f.Header().StreamID]
+	if st == nil || sc.curHeaderStreamID() != st.id {
+		return ConnectionError(ErrCodeProtocol)
+	}
+	return sc.processHeaderBlockFragment(st, f.HeaderBlockFragment(), f.HeadersEnded())
+}
+
+func (sc *serverConn) processHeaderBlockFragment(st *stream, frag []byte, end bool) error {
+	sc.serveG.check()
+	if _, err := sc.hpackDecoder.Write(frag); err != nil {
+		// TODO: convert to stream error I assume?
+		return err
+	}
+	if !end {
+		return nil
+	}
+	if err := sc.hpackDecoder.Close(); err != nil {
+		// TODO: convert to stream error I assume?
+		return err
+	}
+	rw, req, err := sc.newWriterAndRequest()
+	sc.req = requestParam{}
+	if err != nil {
+		return err
+	}
+	st.body = req.Body.(*requestBody).pipe // may be nil
+	st.declBodyBytes = req.ContentLength
+	go sc.runHandler(rw, req)
+	return nil
+}
+
+func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, error) {
+	sc.serveG.check()
+	rp := &sc.req
+	if rp.invalidHeader || rp.method == "" || rp.path == "" ||
+		(rp.scheme != "https" && rp.scheme != "http") {
+		// See 8.1.2.6 Malformed Requests and Responses:
+		//
+		// Malformed requests or responses that are detected
+		// MUST be treated as a stream error (Section 5.4.2)
+		// of type PROTOCOL_ERROR."
+		//
+		// 8.1.2.3 Request Pseudo-Header Fields
+		// "All HTTP/2 requests MUST include exactly one valid
+		// value for the :method, :scheme, and :path
+		// pseudo-header fields"
+		return nil, nil, StreamError{rp.stream.id, ErrCodeProtocol}
+	}
+	var tlsState *tls.ConnectionState // make this non-nil if https
+	if rp.scheme == "https" {
+		// TODO: get from sc's ConnectionState
+		tlsState = &tls.ConnectionState{}
+	}
+	authority := rp.authority
+	if authority == "" {
+		authority = rp.header.Get("Host")
+	}
+	bodyOpen := rp.stream.state == stateOpen
+	body := &requestBody{
+		sc:       sc,
+		streamID: rp.stream.id,
+	}
+	req := &http.Request{
+		Method:     rp.method,
+		URL:        &url.URL{},
+		RemoteAddr: sc.conn.RemoteAddr().String(),
+		Header:     rp.header,
+		RequestURI: rp.path,
+		Proto:      "HTTP/2.0",
+		ProtoMajor: 2,
+		ProtoMinor: 0,
+		TLS:        tlsState,
+		Host:       authority,
+		Body:       body,
+	}
+	if bodyOpen {
+		body.pipe = &pipe{
+			b: buffer{buf: make([]byte, 65536)}, // TODO: share/remove
+		}
+		body.pipe.c.L = &body.pipe.m
+
+		if vv, ok := rp.header["Content-Length"]; ok {
+			req.ContentLength, _ = strconv.ParseInt(vv[0], 10, 64)
+		} else {
+			req.ContentLength = -1
+		}
+	}
+	rw := &responseWriter{
+		sc:       sc,
+		streamID: rp.stream.id,
+		req:      req,
+		body:     body,
+	}
+	return rw, req, nil
+}
+
+// Run on its own goroutine.
+func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request) {
+	defer rw.handlerDone()
+	// TODO: catch panics like net/http.Server
+	sc.handler.ServeHTTP(rw, req)
+}
+
+// called from handler goroutines
+func (sc *serverConn) writeData(streamID uint32, p []byte) (n int, err error) {
+	// TODO: implement
+	log.Printf("WRITE on %d: %q", streamID, p)
+	return len(p), nil
+}
+
+// headerWriteReq is a request to write an HTTP response header from a server Handler.
+type headerWriteReq struct {
+	streamID    uint32
+	httpResCode int
+	h           http.Header // may be nil
+	endStream   bool
+}
+
+// called from handler goroutines.
+// h may be nil.
+func (sc *serverConn) writeHeader(req headerWriteReq) {
+	sc.writeHeaderCh <- req
+}
+
+func (sc *serverConn) writeHeaderInLoop(req headerWriteReq) error {
+	sc.serveG.check()
+	sc.headerWriteBuf.Reset()
+	sc.hpackEncoder.WriteField(hpack.HeaderField{Name: ":status", Value: httpCodeString(req.httpResCode)})
+	for k, vv := range req.h {
+		for _, v := range vv {
+			// TODO: for gargage, cache lowercase copies of headers at
+			// least for common ones and/or popular recent ones for
+			// this serverConn. LRU?
+			sc.hpackEncoder.WriteField(hpack.HeaderField{Name: strings.ToLower(k), Value: v})
+		}
+	}
+	headerBlock := sc.headerWriteBuf.Bytes()
+	if len(headerBlock) > int(sc.maxWriteFrameSize) {
+		// we'll need continuation ones.
+		panic("TODO")
+	}
+	return sc.framer.WriteHeaders(HeadersFrameParam{
+		StreamID:      req.streamID,
+		BlockFragment: headerBlock,
+		EndStream:     req.endStream,
+		EndHeaders:    true, // no continuation yet
+	})
+}
+
+type windowUpdateReq struct {
+	streamID uint32
+	n        uint32
+}
+
+// called from handler goroutines
+func (sc *serverConn) sendWindowUpdate(streamID uint32, n int) {
+	const maxUint32 = 2147483647
+	for n >= maxUint32 {
+		sc.windowUpdateCh <- windowUpdateReq{streamID, maxUint32}
+		n -= maxUint32
+	}
+	if n > 0 {
+		sc.windowUpdateCh <- windowUpdateReq{streamID, uint32(n)}
+	}
+}
+
+func (sc *serverConn) sendWindowUpdateInLoop(wu windowUpdateReq) error {
+	sc.serveG.check()
+	// TODO: sc.bufferedOutput.StartBuffering()
+	if err := sc.framer.WriteWindowUpdate(0, wu.n); err != nil {
+		return err
+	}
+	if err := sc.framer.WriteWindowUpdate(wu.streamID, wu.n); err != nil {
+		return err
+	}
+	// TODO: return sc.bufferedOutput.Flush()
+	return nil
+}
+
+// ConfigureServer adds HTTP/2 support to a net/http Server.
+//
+// The configuration conf may be nil.
+//
+// ConfigureServer must be called before s begins serving.
+func ConfigureServer(s *http.Server, conf *Server) {
+	if conf == nil {
+		conf = new(Server)
+	}
+	if s.TLSConfig == nil {
+		s.TLSConfig = new(tls.Config)
+	}
+	haveNPN := false
+	for _, p := range s.TLSConfig.NextProtos {
+		if p == npnProto {
+			haveNPN = true
+			break
+		}
+	}
+	if !haveNPN {
+		s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, npnProto)
+	}
+
+	if s.TLSNextProto == nil {
+		s.TLSNextProto = map[string]func(*http.Server, *tls.Conn, http.Handler){}
+	}
+	s.TLSNextProto[npnProto] = func(hs *http.Server, c *tls.Conn, h http.Handler) {
+		if testHookOnConn != nil {
+			testHookOnConn()
+		}
+		conf.handleConn(hs, c, h)
+	}
+}
+
+type requestBody struct {
+	sc       *serverConn
+	streamID uint32
+	closed   bool
+	pipe     *pipe // non-nil if we have a HTTP entity message body
+}
+
+var errClosedBody = errors.New("body closed by handler")
+
+func (b *requestBody) Close() error {
+	if b.pipe != nil {
+		b.pipe.Close(errClosedBody)
+	}
+	b.closed = true
+	return nil
+}
+
+func (b *requestBody) Read(p []byte) (n int, err error) {
+	if b.pipe == nil {
+		return 0, io.EOF
+	}
+	n, err = b.pipe.Read(p)
+	if n > 0 {
+		b.sc.sendWindowUpdate(b.streamID, n)
+		// TODO: tell b.sc to send back 'n' flow control quota credits to the sender
+	}
+	return
+}
+
+type responseWriter struct {
+	sc           *serverConn
+	streamID     uint32
+	wroteHeaders bool
+	h            http.Header
+
+	req  *http.Request
+	body *requestBody // to close at end of request, if DATA frames didn't
+}
+
+// TODO: bufio writing of responseWriter. add Flush, add pools of
+// bufio.Writers, adjust bufio writer sized based on frame size
+// updates from peer? For now: naive.
+
+func (w *responseWriter) Header() http.Header {
+	if w.h == nil {
+		w.h = make(http.Header)
+	}
+	return w.h
+}
+
+func (w *responseWriter) WriteHeader(code int) {
+	if w.wroteHeaders {
+		return
+	}
+	// TODO: defer actually writing this frame until a Flush or
+	// handlerDone, like net/http's Server. then we can coalesce
+	// e.g. a 204 response to have a Header response frame with
+	// END_STREAM set, without a separate frame being sent in
+	// handleDone.
+	w.wroteHeaders = true
+	w.sc.writeHeader(headerWriteReq{
+		streamID:    w.streamID,
+		httpResCode: code,
+		h:           w.h,
+	})
+}
+
+// TODO: responseWriter.WriteString too?
+
+func (w *responseWriter) Write(p []byte) (n int, err error) {
+	if !w.wroteHeaders {
+		w.WriteHeader(200)
+	}
+	return w.sc.writeData(w.streamID, p) // blocks waiting for tokens
+}
+
+func (w *responseWriter) handlerDone() {
+	if !w.wroteHeaders {
+		w.sc.writeHeader(headerWriteReq{
+			streamID:    w.streamID,
+			httpResCode: 200,
+			h:           w.h,
+			endStream:   true, // handler has finished; can't be any data.
+		})
+	}
+}
+
+var testHookOnConn func() // for testing

+ 766 - 0
server_test.go

@@ -0,0 +1,766 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+package http2
+
+import (
+	"bytes"
+	"crypto/tls"
+	"errors"
+	"io"
+	"io/ioutil"
+	"log"
+	"net"
+	"net/http"
+	"net/http/httptest"
+	"os"
+	"reflect"
+	"strconv"
+	"strings"
+	"sync/atomic"
+	"testing"
+	"time"
+
+	"github.com/bradfitz/http2/hpack"
+)
+
+type serverTester struct {
+	cc     net.Conn // client conn
+	t      *testing.T
+	ts     *httptest.Server
+	fr     *Framer
+	logBuf *bytes.Buffer
+}
+
+func newServerTester(t *testing.T, handler http.HandlerFunc) *serverTester {
+	logBuf := new(bytes.Buffer)
+	ts := httptest.NewUnstartedServer(handler)
+	ConfigureServer(ts.Config, &Server{})
+	ts.TLS = ts.Config.TLSConfig // the httptest.Server has its own copy of this TLS config
+	ts.Config.ErrorLog = log.New(io.MultiWriter(twriter{t: t}, logBuf), "", log.LstdFlags)
+	ts.StartTLS()
+
+	if VerboseLogs {
+		t.Logf("Running test server at: %s", ts.URL)
+	}
+	cc, err := tls.Dial("tcp", ts.Listener.Addr().String(), &tls.Config{
+		InsecureSkipVerify: true,
+		NextProtos:         []string{npnProto},
+	})
+	if err != nil {
+		t.Fatal(err)
+	}
+	log.SetOutput(twriter{t})
+	return &serverTester{
+		t:      t,
+		ts:     ts,
+		cc:     cc,
+		fr:     NewFramer(cc, cc),
+		logBuf: logBuf,
+	}
+}
+
+func (st *serverTester) Close() {
+	st.ts.Close()
+	st.cc.Close()
+	log.SetOutput(os.Stderr)
+}
+
+// greet initiates the client's HTTP/2 connection into a state where
+// frames may be sent.
+func (st *serverTester) greet() {
+	st.writePreface()
+	st.writeInitialSettings()
+	st.wantSettings()
+	st.writeSettingsAck()
+	st.wantSettingsAck()
+}
+
+func (st *serverTester) writePreface() {
+	n, err := st.cc.Write(clientPreface)
+	if err != nil {
+		st.t.Fatalf("Error writing client preface: %v", err)
+	}
+	if n != len(clientPreface) {
+		st.t.Fatalf("Writing client preface, wrote %d bytes; want %d", n, len(clientPreface))
+	}
+}
+
+func (st *serverTester) writeInitialSettings() {
+	if err := st.fr.WriteSettings(); err != nil {
+		st.t.Fatalf("Error writing initial SETTINGS frame from client to server: %v", err)
+	}
+}
+
+func (st *serverTester) writeSettingsAck() {
+	if err := st.fr.WriteSettingsAck(); err != nil {
+		st.t.Fatalf("Error writing ACK of server's SETTINGS: %v", err)
+	}
+}
+
+func (st *serverTester) writeHeaders(p HeadersFrameParam) {
+	if err := st.fr.WriteHeaders(p); err != nil {
+		st.t.Fatalf("Error writing HEADERS: %v", err)
+	}
+}
+
+// bodylessReq1 writes a HEADERS frames with StreamID 1 and EndStream and EndHeaders set.
+func (st *serverTester) bodylessReq1(headers ...string) {
+	st.writeHeaders(HeadersFrameParam{
+		StreamID:      1, // clients send odd numbers
+		BlockFragment: encodeHeader(st.t, headers...),
+		EndStream:     true,
+		EndHeaders:    true,
+	})
+}
+
+func (st *serverTester) writeData(streamID uint32, endStream bool, data []byte) {
+	if err := st.fr.WriteData(streamID, endStream, data); err != nil {
+		st.t.Fatalf("Error writing DATA: %v", err)
+	}
+}
+
+func (st *serverTester) readFrame() (Frame, error) {
+	frc := make(chan Frame, 1)
+	errc := make(chan error, 1)
+	go func() {
+		fr, err := st.fr.ReadFrame()
+		if err != nil {
+			errc <- err
+		} else {
+			frc <- fr
+		}
+	}()
+	t := time.NewTimer(2 * time.Second)
+	defer t.Stop()
+	select {
+	case f := <-frc:
+		return f, nil
+	case err := <-errc:
+		return nil, err
+	case <-t.C:
+		return nil, errors.New("timeout waiting for frame")
+	}
+}
+
+func (st *serverTester) wantSettings() *SettingsFrame {
+	f, err := st.readFrame()
+	if err != nil {
+		st.t.Fatalf("Error while expecting a SETTINGS frame: %v", err)
+	}
+	sf, ok := f.(*SettingsFrame)
+	if !ok {
+		st.t.Fatalf("got a %T; want *SettingsFrame", f)
+	}
+	return sf
+}
+
+func (st *serverTester) wantPing() *PingFrame {
+	f, err := st.readFrame()
+	if err != nil {
+		st.t.Fatalf("Error while expecting a PING frame: %v", err)
+	}
+	pf, ok := f.(*PingFrame)
+	if !ok {
+		st.t.Fatalf("got a %T; want *PingFrame", f)
+	}
+	return pf
+}
+
+func (st *serverTester) wantRSTStream(streamID uint32, errCode ErrCode) {
+	f, err := st.readFrame()
+	if err != nil {
+		st.t.Fatalf("Error while expecting an RSTStream frame: %v", err)
+	}
+	rs, ok := f.(*RSTStreamFrame)
+	if !ok {
+		st.t.Fatalf("got a %T; want *RSTStreamFrame", f)
+	}
+	if rs.FrameHeader.StreamID != streamID {
+		st.t.Fatalf("RSTStream StreamID = %d; want %d", rs.FrameHeader.StreamID, streamID)
+	}
+	if rs.ErrCode != uint32(errCode) {
+		st.t.Fatalf("RSTStream ErrCode = %d (%s); want %d (%s)", rs.ErrCode, rs.ErrCode, errCode, errCode)
+	}
+}
+
+func (st *serverTester) wantWindowUpdate(streamID, incr uint32) {
+	f, err := st.readFrame()
+	if err != nil {
+		st.t.Fatalf("Error while expecting an RSTStream frame: %v", err)
+	}
+	wu, ok := f.(*WindowUpdateFrame)
+	if !ok {
+		st.t.Fatalf("got a %T; want *WindowUpdateFrame", f)
+	}
+	if wu.FrameHeader.StreamID != streamID {
+		st.t.Fatalf("WindowUpdate StreamID = %d; want %d", wu.FrameHeader.StreamID, streamID)
+	}
+	if wu.Increment != incr {
+		st.t.Fatalf("WindowUpdate increment = %d; want %d", wu.Increment, incr)
+	}
+}
+
+func (st *serverTester) wantSettingsAck() {
+	f, err := st.readFrame()
+	if err != nil {
+		st.t.Fatal(err)
+	}
+	sf, ok := f.(*SettingsFrame)
+	if !ok {
+		st.t.Fatalf("Wanting a settings ACK, received a %T", f)
+	}
+	if !sf.Header().Flags.Has(FlagSettingsAck) {
+		st.t.Fatal("Settings Frame didn't have ACK set")
+	}
+
+}
+
+func TestServer(t *testing.T) {
+	gotReq := make(chan bool, 1)
+	st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Foo", "Bar")
+		gotReq <- true
+	})
+	defer st.Close()
+
+	covers("3.5", `
+		The server connection preface consists of a potentially empty
+		SETTINGS frame ([SETTINGS]) that MUST be the first frame the
+		server sends in the HTTP/2 connection.
+	`)
+
+	st.writePreface()
+	st.writeInitialSettings()
+	st.wantSettings().ForeachSetting(func(s Setting) error {
+		t.Logf("Server sent setting %v = %v", s.ID, s.Val)
+		return nil
+	})
+	st.writeSettingsAck()
+	st.wantSettingsAck()
+
+	st.writeHeaders(HeadersFrameParam{
+		StreamID:      1, // clients send odd numbers
+		BlockFragment: encodeHeader(t),
+		EndStream:     true, // no DATA frames
+		EndHeaders:    true,
+	})
+
+	select {
+	case <-gotReq:
+	case <-time.After(2 * time.Second):
+		t.Error("timeout waiting for request")
+	}
+}
+
+func TestServer_Request_Get(t *testing.T) {
+	testServerRequest(t, func(st *serverTester) {
+		st.writeHeaders(HeadersFrameParam{
+			StreamID:      1, // clients send odd numbers
+			BlockFragment: encodeHeader(t, "foo-bar", "some-value"),
+			EndStream:     true, // no DATA frames
+			EndHeaders:    true,
+		})
+	}, func(r *http.Request) {
+		if r.Method != "GET" {
+			t.Errorf("Method = %q; want GET", r.Method)
+		}
+		if r.ContentLength != 0 {
+			t.Errorf("ContentLength = %v; want 0", r.ContentLength)
+		}
+		if r.Close {
+			t.Error("Close = true; want false")
+		}
+		if !strings.Contains(r.RemoteAddr, ":") {
+			t.Errorf("RemoteAddr = %q; want something with a colon", r.RemoteAddr)
+		}
+		if r.Proto != "HTTP/2.0" || r.ProtoMajor != 2 || r.ProtoMinor != 0 {
+			t.Errorf("Proto = %q Major=%v,Minor=%v; want HTTP/2.0", r.Proto, r.ProtoMajor, r.ProtoMinor)
+		}
+		wantHeader := http.Header{
+			"Foo-Bar": []string{"some-value"},
+		}
+		if !reflect.DeepEqual(r.Header, wantHeader) {
+			t.Errorf("Header = %#v; want %#v", r.Header, wantHeader)
+		}
+		if n, err := r.Body.Read([]byte(" ")); err != io.EOF || n != 0 {
+			t.Errorf("Read = %d, %v; want 0, EOF", n, err)
+		}
+	})
+}
+
+// TODO: add a test with EndStream=true on the HEADERS but setting a
+// Content-Length anyway.  Should we just omit it and force it to
+// zero?
+
+func TestServer_Request_Post_NoContentLength_EndStream(t *testing.T) {
+	testServerRequest(t, func(st *serverTester) {
+		st.writeHeaders(HeadersFrameParam{
+			StreamID:      1, // clients send odd numbers
+			BlockFragment: encodeHeader(t, ":method", "POST"),
+			EndStream:     true,
+			EndHeaders:    true,
+		})
+	}, func(r *http.Request) {
+		if r.Method != "POST" {
+			t.Errorf("Method = %q; want POST", r.Method)
+		}
+		if r.ContentLength != 0 {
+			t.Errorf("ContentLength = %v; want 0", r.ContentLength)
+		}
+		if n, err := r.Body.Read([]byte(" ")); err != io.EOF || n != 0 {
+			t.Errorf("Read = %d, %v; want 0, EOF", n, err)
+		}
+	})
+}
+
+func TestServer_Request_Post_Body_ImmediateEOF(t *testing.T) {
+	testBodyContents(t, -1, "", func(st *serverTester) {
+		st.writeHeaders(HeadersFrameParam{
+			StreamID:      1, // clients send odd numbers
+			BlockFragment: encodeHeader(t, ":method", "POST"),
+			EndStream:     false, // to say DATA frames are coming
+			EndHeaders:    true,
+		})
+		st.writeData(1, true, nil) // just kidding. empty body.
+	})
+}
+
+func TestServer_Request_Post_Body_OneData(t *testing.T) {
+	const content = "Some content"
+	testBodyContents(t, -1, content, func(st *serverTester) {
+		st.writeHeaders(HeadersFrameParam{
+			StreamID:      1, // clients send odd numbers
+			BlockFragment: encodeHeader(t, ":method", "POST"),
+			EndStream:     false, // to say DATA frames are coming
+			EndHeaders:    true,
+		})
+		st.writeData(1, true, []byte(content))
+	})
+}
+
+func TestServer_Request_Post_Body_TwoData(t *testing.T) {
+	const content = "Some content"
+	testBodyContents(t, -1, content, func(st *serverTester) {
+		st.writeHeaders(HeadersFrameParam{
+			StreamID:      1, // clients send odd numbers
+			BlockFragment: encodeHeader(t, ":method", "POST"),
+			EndStream:     false, // to say DATA frames are coming
+			EndHeaders:    true,
+		})
+		st.writeData(1, false, []byte(content[:5]))
+		st.writeData(1, true, []byte(content[5:]))
+	})
+}
+
+func TestServer_Request_Post_Body_ContentLength_Correct(t *testing.T) {
+	const content = "Some content"
+	testBodyContents(t, int64(len(content)), content, func(st *serverTester) {
+		st.writeHeaders(HeadersFrameParam{
+			StreamID: 1, // clients send odd numbers
+			BlockFragment: encodeHeader(t,
+				":method", "POST",
+				"content-length", strconv.Itoa(len(content)),
+			),
+			EndStream:  false, // to say DATA frames are coming
+			EndHeaders: true,
+		})
+		st.writeData(1, true, []byte(content))
+	})
+}
+
+func TestServer_Request_Post_Body_ContentLength_TooLarge(t *testing.T) {
+	testBodyContentsFail(t, 3, "Request declared a Content-Length of 3 but only wrote 2 bytes",
+		func(st *serverTester) {
+			st.writeHeaders(HeadersFrameParam{
+				StreamID: 1, // clients send odd numbers
+				BlockFragment: encodeHeader(t,
+					":method", "POST",
+					"content-length", "3",
+				),
+				EndStream:  false, // to say DATA frames are coming
+				EndHeaders: true,
+			})
+			st.writeData(1, true, []byte("12"))
+		})
+}
+
+func TestServer_Request_Post_Body_ContentLength_TooSmall(t *testing.T) {
+	testBodyContentsFail(t, 4, "Sender tried to send more than declared Content-Length of 4 bytes",
+		func(st *serverTester) {
+			st.writeHeaders(HeadersFrameParam{
+				StreamID: 1, // clients send odd numbers
+				BlockFragment: encodeHeader(t,
+					":method", "POST",
+					"content-length", "4",
+				),
+				EndStream:  false, // to say DATA frames are coming
+				EndHeaders: true,
+			})
+			st.writeData(1, true, []byte("12345"))
+		})
+}
+
+func testBodyContents(t *testing.T, wantContentLength int64, wantBody string, write func(st *serverTester)) {
+	testServerRequest(t, write, func(r *http.Request) {
+		if r.Method != "POST" {
+			t.Errorf("Method = %q; want POST", r.Method)
+		}
+		if r.ContentLength != wantContentLength {
+			t.Errorf("ContentLength = %v; want %d", r.ContentLength, wantContentLength)
+		}
+		all, err := ioutil.ReadAll(r.Body)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if string(all) != wantBody {
+			t.Errorf("Read = %q; want %q", all, wantBody)
+		}
+		if err := r.Body.Close(); err != nil {
+			t.Fatalf("Close: %v", err)
+		}
+	})
+}
+
+func testBodyContentsFail(t *testing.T, wantContentLength int64, wantReadError string, write func(st *serverTester)) {
+	testServerRequest(t, write, func(r *http.Request) {
+		if r.Method != "POST" {
+			t.Errorf("Method = %q; want POST", r.Method)
+		}
+		if r.ContentLength != wantContentLength {
+			t.Errorf("ContentLength = %v; want %d", r.ContentLength, wantContentLength)
+		}
+		all, err := ioutil.ReadAll(r.Body)
+		if err == nil {
+			t.Fatalf("expected an error (%q) reading from the body. Successfully read %q instead.",
+				wantReadError, all)
+		}
+		if !strings.Contains(err.Error(), wantReadError) {
+			t.Fatalf("Body.Read = %v; want substring %q", err, wantReadError)
+		}
+		if err := r.Body.Close(); err != nil {
+			t.Fatalf("Close: %v", err)
+		}
+	})
+}
+
+// Using a Host header, instead of :authority
+func TestServer_Request_Get_Host(t *testing.T) {
+	const host = "example.com"
+	testServerRequest(t, func(st *serverTester) {
+		st.writeHeaders(HeadersFrameParam{
+			StreamID:      1, // clients send odd numbers
+			BlockFragment: encodeHeader(t, "host", host),
+			EndStream:     true,
+			EndHeaders:    true,
+		})
+	}, func(r *http.Request) {
+		if r.Host != host {
+			t.Errorf("Host = %q; want %q", r.Host, host)
+		}
+	})
+}
+
+// Using an :authority pseudo-header, instead of Host
+func TestServer_Request_Get_Authority(t *testing.T) {
+	const host = "example.com"
+	testServerRequest(t, func(st *serverTester) {
+		st.writeHeaders(HeadersFrameParam{
+			StreamID:      1, // clients send odd numbers
+			BlockFragment: encodeHeader(t, ":authority", host),
+			EndStream:     true,
+			EndHeaders:    true,
+		})
+	}, func(r *http.Request) {
+		if r.Host != host {
+			t.Errorf("Host = %q; want %q", r.Host, host)
+		}
+	})
+}
+
+func TestServer_Request_WithContinuation(t *testing.T) {
+	wantHeader := http.Header{
+		"Foo-One":   []string{"value-one"},
+		"Foo-Two":   []string{"value-two"},
+		"Foo-Three": []string{"value-three"},
+	}
+	testServerRequest(t, func(st *serverTester) {
+		fullHeaders := encodeHeader(t,
+			"foo-one", "value-one",
+			"foo-two", "value-two",
+			"foo-three", "value-three",
+		)
+		remain := fullHeaders
+		chunks := 0
+		for len(remain) > 0 {
+			const maxChunkSize = 5
+			chunk := remain
+			if len(chunk) > maxChunkSize {
+				chunk = chunk[:maxChunkSize]
+			}
+			remain = remain[len(chunk):]
+
+			if chunks == 0 {
+				st.writeHeaders(HeadersFrameParam{
+					StreamID:      1, // clients send odd numbers
+					BlockFragment: chunk,
+					EndStream:     true,  // no DATA frames
+					EndHeaders:    false, // we'll have continuation frames
+				})
+			} else {
+				err := st.fr.WriteContinuation(1, len(remain) == 0, chunk)
+				if err != nil {
+					t.Fatal(err)
+				}
+			}
+			chunks++
+		}
+		if chunks < 2 {
+			t.Fatal("too few chunks")
+		}
+	}, func(r *http.Request) {
+		if !reflect.DeepEqual(r.Header, wantHeader) {
+			t.Errorf("Header = %#v; want %#v", r.Header, wantHeader)
+		}
+	})
+}
+
+// Concatenated cookie headers. ("8.1.2.5 Compressing the Cookie Header Field")
+func TestServer_Request_CookieConcat(t *testing.T) {
+	const host = "example.com"
+	testServerRequest(t, func(st *serverTester) {
+		st.bodylessReq1(
+			":authority", host,
+			"cookie", "a=b",
+			"cookie", "c=d",
+			"cookie", "e=f",
+		)
+	}, func(r *http.Request) {
+		const want = "a=b; c=d; e=f"
+		if got := r.Header.Get("Cookie"); got != want {
+			t.Errorf("Cookie = %q; want %q", got, want)
+		}
+	})
+}
+
+func TestServer_Request_Reject_CapitalHeader(t *testing.T) {
+	testRejectRequest(t, func(st *serverTester) { st.bodylessReq1("UPPER", "v") })
+}
+
+func TestServer_Request_Reject_Pseudo_Missing_method(t *testing.T) {
+	testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":method", "") })
+}
+
+func TestServer_Request_Reject_Pseudo_ExactlyOne(t *testing.T) {
+	// 8.1.2.3 Request Pseudo-Header Fields
+	// "All HTTP/2 requests MUST include exactly one valid value" ...
+	testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":method", "GET", ":method", "POST") })
+}
+
+func TestServer_Request_Reject_Pseudo_AfterRegular(t *testing.T) {
+	// 8.1.2.3 Request Pseudo-Header Fields
+	// "All pseudo-header fields MUST appear in the header block
+	// before regular header fields. Any request or response that
+	// contains a pseudo-header field that appears in a header
+	// block after a regular header field MUST be treated as
+	// malformed (Section 8.1.2.6)."
+	testRejectRequest(t, func(st *serverTester) {
+		var buf bytes.Buffer
+		enc := hpack.NewEncoder(&buf)
+		enc.WriteField(hpack.HeaderField{Name: ":method", Value: "GET"})
+		enc.WriteField(hpack.HeaderField{Name: "regular", Value: "foobar"})
+		enc.WriteField(hpack.HeaderField{Name: ":path", Value: "/"})
+		enc.WriteField(hpack.HeaderField{Name: ":scheme", Value: "https"})
+		st.writeHeaders(HeadersFrameParam{
+			StreamID:      1, // clients send odd numbers
+			BlockFragment: buf.Bytes(),
+			EndStream:     true,
+			EndHeaders:    true,
+		})
+	})
+}
+
+func TestServer_Request_Reject_Pseudo_Missing_path(t *testing.T) {
+	testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":path", "") })
+}
+
+func TestServer_Request_Reject_Pseudo_Missing_scheme(t *testing.T) {
+	testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":scheme", "") })
+}
+
+func TestServer_Request_Reject_Pseudo_scheme_invalid(t *testing.T) {
+	testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":scheme", "bogus") })
+}
+
+func TestServer_Request_Reject_Pseudo_Unknown(t *testing.T) {
+	testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":unknown_thing", "") })
+}
+
+func testRejectRequest(t *testing.T, send func(*serverTester)) {
+	st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
+		t.Fatal("server request made it to handler; should've been rejected")
+	})
+	defer st.Close()
+
+	st.greet()
+	send(st)
+	st.wantRSTStream(1, ErrCodeProtocol)
+}
+
+func TestServer_Ping(t *testing.T) {
+	st := newServerTester(t, nil)
+	defer st.Close()
+	st.greet()
+
+	// Server should ignore this one, since it has ACK set.
+	ackPingData := [8]byte{1, 2, 4, 8, 16, 32, 64, 128}
+	if err := st.fr.WritePing(true, ackPingData); err != nil {
+		t.Fatal(err)
+	}
+
+	// But the server should reply to this one, since ACK is false.
+	pingData := [8]byte{1, 2, 3, 4, 5, 6, 7, 8}
+	if err := st.fr.WritePing(false, pingData); err != nil {
+		t.Fatal(err)
+	}
+
+	pf := st.wantPing()
+	if !pf.Flags.Has(FlagPingAck) {
+		t.Error("response ping doesn't have ACK set")
+	}
+	if pf.Data != pingData {
+		t.Errorf("response ping has data %q; want %q", pf.Data, pingData)
+	}
+}
+
+func TestServer_Handler_Sends_WindowUpdate(t *testing.T) {
+	puppet := newHandlerPuppet()
+	st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
+		puppet.act(w, r)
+	})
+	defer st.Close()
+	defer puppet.done()
+
+	st.greet()
+
+	st.writeHeaders(HeadersFrameParam{
+		StreamID:      1, // clients send odd numbers
+		BlockFragment: encodeHeader(t, ":method", "POST"),
+		EndStream:     false, // data coming
+		EndHeaders:    true,
+	})
+	st.writeData(1, true, []byte("abcdef"))
+	puppet.do(func(w http.ResponseWriter, r *http.Request) {
+		buf := make([]byte, 3)
+		_, err := io.ReadFull(r.Body, buf)
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		if string(buf) != "abc" {
+			t.Errorf("read %q; want abc", buf)
+		}
+	})
+	st.wantWindowUpdate(0, 3)
+	st.wantWindowUpdate(1, 3)
+	puppet.do(func(w http.ResponseWriter, r *http.Request) {
+		buf := make([]byte, 3)
+		_, err := io.ReadFull(r.Body, buf)
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		if string(buf) != "def" {
+			t.Errorf("read %q; want abc", buf)
+		}
+	})
+	st.wantWindowUpdate(0, 3)
+	st.wantWindowUpdate(1, 3)
+}
+
+// TODO: test HEADERS w/o EndHeaders + another HEADERS (should get rejected)
+// TODO: test HEADERS w/ EndHeaders + a continuation HEADERS (should get rejected)
+
+// testServerRequest sets up an idle HTTP/2 connection and lets you
+// write a single request with writeReq, and then verify that the
+// *http.Request is built correctly in checkReq.
+func testServerRequest(t *testing.T, writeReq func(*serverTester), checkReq func(*http.Request)) {
+	gotReq := make(chan bool, 1)
+	st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
+		if r.Body == nil {
+			t.Fatal("nil Body")
+		}
+		checkReq(r)
+		gotReq <- true
+	})
+	defer st.Close()
+
+	st.greet()
+	writeReq(st)
+
+	select {
+	case <-gotReq:
+	case <-time.After(2 * time.Second):
+		t.Error("timeout waiting for request")
+	}
+}
+
+func TestServerWithCurl(t *testing.T) {
+	requireCurl(t)
+
+	ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		// TODO: add a bunch of different tests with different
+		// behavior, as a function of r or a table.
+		// -- with request body, without.
+		// -- no interaction with w.
+		// -- panic
+		// -- modify Header only, but no writes or writeheader (this test)
+		// -- WriteHeader only
+		// -- Write only
+		// -- WriteString
+		// -- both
+		// -- huge headers over a frame size so we get continuation headers.
+		// Look at net/http's Server tests for inspiration.
+		w.Header().Set("Foo", "Bar")
+	}))
+	ConfigureServer(ts.Config, &Server{})
+	ts.TLS = ts.Config.TLSConfig // the httptest.Server has its own copy of this TLS config
+	ts.StartTLS()
+	defer ts.Close()
+
+	var gotConn int32
+	testHookOnConn = func() { atomic.StoreInt32(&gotConn, 1) }
+
+	t.Logf("Running test server for curl to hit at: %s", ts.URL)
+	container := curl(t, "--silent", "--http2", "--insecure", "-v", ts.URL)
+	defer kill(container)
+	resc := make(chan interface{}, 1)
+	go func() {
+		res, err := dockerLogs(container)
+		if err != nil {
+			resc <- err
+		} else {
+			resc <- res
+		}
+	}()
+	select {
+	case res := <-resc:
+		if err, ok := res.(error); ok {
+			t.Fatal(err)
+		}
+		if !strings.Contains(string(res.([]byte)), "< foo:Bar") {
+			t.Errorf("didn't see foo:Bar header")
+			t.Logf("Got: %s", res)
+		}
+	case <-time.After(3 * time.Second):
+		t.Errorf("timeout waiting for curl")
+	}
+
+	if atomic.LoadInt32(&gotConn) == 0 {
+		t.Error("never saw an http2 connection")
+	}
+}