func matchHTTP2Field(r io.Reader, name, value string) (matched bool) { if !hasHTTP2Preface(r) { return false } framer := http2.NewFramer(ioutil.Discard, r) hdec := hpack.NewDecoder(uint32(4<<10), func(hf hpack.HeaderField) { if hf.Name == name && hf.Value == value { matched = true } }) for { f, err := framer.ReadFrame() if err != nil { return false } switch f := f.(type) { case *http2.HeadersFrame: if _, err := hdec.Write(f.HeaderBlockFragment()); err != nil { return false } if matched { return true } if f.FrameHeader.Flags&http2.FlagHeadersEndHeaders != 0 { return false } } } }
func newFramer(conn net.Conn) *framer { f := &framer{ reader: conn, writer: bufio.NewWriterSize(conn, http2IOBufSize), } f.fr = http2.NewFramer(f.writer, f.reader) return f }
func newFramer(conn net.Conn) *framer { f := &framer{ reader: bufio.NewReaderSize(conn, http2IOBufSize), writer: bufio.NewWriterSize(conn, http2IOBufSize), } f.fr = http2.NewFramer(f.writer, f.reader) f.fr.ReadMetaHeaders = hpack.NewDecoder(http2InitHeaderTableSize, nil) return f }
func newServerTesterFromConn(t testing.TB, cc io.ReadWriteCloser) *serverTester { st := &serverTester{ t: t, cc: cc, frc: make(chan http2.Frame, 1), frErrc: make(chan error, 1), } st.hpackEnc = hpack.NewEncoder(&st.headerBuf) st.fr = http2.NewFramer(cc, cc) st.fr.ReadMetaHeaders = hpack.NewDecoder(4096 /*initialHeaderTableSize*/, nil) return st }
func newFramer(conn net.Conn) *framer { // 1. 首先: reader/writer 是对Conn做了一层buffer f := &framer{ reader: bufio.NewReaderSize(conn, http2IOBufSize), writer: bufio.NewWriterSize(conn, http2IOBufSize), } // 2. fr: 在buffer的基础上在做了http2的处理 f.fr = http2.NewFramer(f.writer, f.reader) // 3. f.fr.ReadMetaHeaders = hpack.NewDecoder(http2InitHeaderTableSize, nil) return f }
func matchHTTP2Field(w io.Writer, r io.Reader, name, value string) (matched bool) { if !hasHTTP2Preface(r) { return false } done := false framer := http2.NewFramer(w, r) hdec := hpack.NewDecoder(uint32(4<<10), func(hf hpack.HeaderField) { if hf.Name == name { done = true if hf.Value == value { matched = true } } }) for { f, err := framer.ReadFrame() if err != nil { return false } switch f := f.(type) { case *http2.SettingsFrame: if err := framer.WriteSettings(); err != nil { return false } case *http2.ContinuationFrame: if _, err := hdec.Write(f.HeaderBlockFragment()); err != nil { return false } done = done || f.FrameHeader.Flags&http2.FlagHeadersEndHeaders != 0 case *http2.HeadersFrame: if _, err := hdec.Write(f.HeaderBlockFragment()); err != nil { return false } done = done || f.FrameHeader.Flags&http2.FlagHeadersEndHeaders != 0 } if done { return matched } } }
// newServerTesterInternal creates test context. If frontendTLS is // true, set up TLS frontend connection. connectPort is the server // side port where client connection is made. func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handler, frontendTLS bool, connectPort int, clientConfig *tls.Config) *serverTester { ts := httptest.NewUnstartedServer(handler) args := []string{} backendTLS := false dns := false externalDNS := false acceptProxyProtocol := false for _, k := range src_args { switch k { case "--http2-bridge": backendTLS = true case "--dns": dns = true case "--external-dns": dns = true externalDNS = true case "--accept-proxy-protocol": acceptProxyProtocol = true default: args = append(args, k) } } if backendTLS { nghttp2.ConfigureServer(ts.Config, &nghttp2.Server{}) // According to httptest/server.go, we have to set // NextProtos separately for ts.TLS. NextProtos set // in nghttp2.ConfigureServer is effectively ignored. ts.TLS = new(tls.Config) ts.TLS.NextProtos = append(ts.TLS.NextProtos, "h2") ts.StartTLS() args = append(args, "-k") } else { ts.Start() } scheme := "http" if frontendTLS { scheme = "https" args = append(args, testDir+"/server.key", testDir+"/server.crt") } backendURL, err := url.Parse(ts.URL) if err != nil { t.Fatalf("Error parsing URL from httptest.Server: %v", err) } // URL.Host looks like "127.0.0.1:8080", but we want // "127.0.0.1,8080" b := "-b" if !externalDNS { b += fmt.Sprintf("%v;", strings.Replace(backendURL.Host, ":", ",", -1)) } else { sep := strings.LastIndex(backendURL.Host, ":") if sep == -1 { t.Fatalf("backendURL.Host %v does not contain separator ':'", backendURL.Host) } // We use awesome service xip.io. b += fmt.Sprintf("%v.xip.io,%v;", backendURL.Host[:sep], backendURL.Host[sep+1:]) } if backendTLS { b += ";proto=h2;tls" } if dns { b += ";dns" } noTLS := ";no-tls" if frontendTLS { noTLS = "" } var proxyProto string if acceptProxyProtocol { proxyProto = ";proxyproto" } args = append(args, fmt.Sprintf("-f127.0.0.1,%v%v%v", serverPort, noTLS, proxyProto), b, "--errorlog-file="+logDir+"/log.txt", "-LINFO") authority := fmt.Sprintf("127.0.0.1:%v", connectPort) st := &serverTester{ cmd: exec.Command(serverBin, args...), t: t, ts: ts, url: fmt.Sprintf("%v://%v", scheme, authority), frontendHost: fmt.Sprintf("127.0.0.1:%v", serverPort), backendHost: backendURL.Host, nextStreamID: 1, authority: authority, frCh: make(chan http2.Frame), spdyFrCh: make(chan spdy.Frame), errCh: make(chan error), } if err := st.cmd.Start(); err != nil { st.t.Fatalf("Error starting %v: %v", serverBin, err) } retry := 0 for { time.Sleep(50 * time.Millisecond) var conn net.Conn var err error if frontendTLS { var tlsConfig *tls.Config if clientConfig == nil { tlsConfig = new(tls.Config) } else { tlsConfig = clientConfig } tlsConfig.InsecureSkipVerify = true tlsConfig.NextProtos = []string{"h2", "spdy/3.1"} conn, err = tls.Dial("tcp", authority, tlsConfig) } else { conn, err = net.Dial("tcp", authority) } if err != nil { retry += 1 if retry >= 100 { st.Close() st.t.Fatalf("Error server is not responding too long; server command-line arguments may be invalid") } continue } if frontendTLS { tlsConn := conn.(*tls.Conn) cs := tlsConn.ConnectionState() if !cs.NegotiatedProtocolIsMutual { st.Close() st.t.Fatalf("Error negotiated next protocol is not mutual") } } st.conn = conn break } st.fr = http2.NewFramer(st.conn, st.conn) spdyFr, err := spdy.NewFramer(st.conn, st.conn) if err != nil { st.Close() st.t.Fatalf("Error spdy.NewFramer: %v", err) } st.spdyFr = spdyFr st.enc = hpack.NewEncoder(&st.headerBlkBuf) st.dec = hpack.NewDecoder(4096, func(f hpack.HeaderField) { st.header.Add(f.Name, f.Value) }) return st }
func (app *h2i) Main() error { cfg := &tls.Config{ ServerName: app.host, NextProtos: strings.Split(*flagNextProto, ","), InsecureSkipVerify: *flagInsecure, } hostAndPort := withPort(app.host) log.Printf("Connecting to %s ...", hostAndPort) tc, err := tls.Dial("tcp", hostAndPort, cfg) if err != nil { return fmt.Errorf("Error dialing %s: %v", withPort(app.host), err) } log.Printf("Connected to %v", tc.RemoteAddr()) defer tc.Close() if err := tc.Handshake(); err != nil { return fmt.Errorf("TLS handshake: %v", err) } if !*flagInsecure { if err := tc.VerifyHostname(app.host); err != nil { return fmt.Errorf("VerifyHostname: %v", err) } } state := tc.ConnectionState() log.Printf("Negotiated protocol %q", state.NegotiatedProtocol) if !state.NegotiatedProtocolIsMutual || state.NegotiatedProtocol == "" { return fmt.Errorf("Could not negotiate protocol mutually") } if _, err := io.WriteString(tc, http2.ClientPreface); err != nil { return err } app.framer = http2.NewFramer(tc, tc) oldState, err := terminal.MakeRaw(0) if err != nil { return err } defer terminal.Restore(0, oldState) var screen = struct { io.Reader io.Writer }{os.Stdin, os.Stdout} app.term = terminal.NewTerminal(screen, "h2i> ") lastWord := regexp.MustCompile(`.+\W(\w+)$`) app.term.AutoCompleteCallback = func(line string, pos int, key rune) (newLine string, newPos int, ok bool) { if key != '\t' { return } if pos != len(line) { // TODO: we're being lazy for now, only supporting tab completion at the end. return } // Auto-complete for the command itself. if !strings.Contains(line, " ") { var name string name, _, ok = lookupCommand(line) if !ok { return } return name, len(name), true } _, c, ok := lookupCommand(line[:strings.IndexByte(line, ' ')]) if !ok || c.complete == nil { return } if strings.HasSuffix(line, " ") { app.logf("%s", strings.Join(c.complete(), " ")) return line, pos, true } m := lastWord.FindStringSubmatch(line) if m == nil { return line, len(line), true } soFar := m[1] var match []string for _, cand := range c.complete() { if len(soFar) > len(cand) || !strings.EqualFold(cand[:len(soFar)], soFar) { continue } match = append(match, cand) } if len(match) == 0 { return } if len(match) > 1 { // TODO: auto-complete any common prefix app.logf("%s", strings.Join(match, " ")) return line, pos, true } newLine = line[:len(line)-len(soFar)] + match[0] return newLine, len(newLine), true } errc := make(chan error, 2) go func() { errc <- app.readFrames() }() go func() { errc <- app.readConsole() }() return <-errc }
// newServerTesterInternal creates test context. If frontendTLS is // true, set up TLS frontend connection. func newServerTesterInternal(args []string, t *testing.T, handler http.Handler, frontendTLS bool, clientConfig *tls.Config) *serverTester { ts := httptest.NewUnstartedServer(handler) backendTLS := false for _, k := range args { switch k { case "--http2-bridge": backendTLS = true } } if backendTLS { nghttp2.ConfigureServer(ts.Config, &nghttp2.Server{}) // According to httptest/server.go, we have to set // NextProtos separately for ts.TLS. NextProtos set // in nghttp2.ConfigureServer is effectively ignored. ts.TLS = new(tls.Config) ts.TLS.NextProtos = append(ts.TLS.NextProtos, "h2-14") ts.StartTLS() args = append(args, "-k") } else { ts.Start() } scheme := "http" if frontendTLS { scheme = "https" args = append(args, testDir+"/server.key", testDir+"/server.crt") } else { args = append(args, "--frontend-no-tls") } backendURL, err := url.Parse(ts.URL) if err != nil { t.Fatalf("Error parsing URL from httptest.Server: %v", err) } // URL.Host looks like "127.0.0.1:8080", but we want // "127.0.0.1,8080" b := "-b" + strings.Replace(backendURL.Host, ":", ",", -1) args = append(args, fmt.Sprintf("-f127.0.0.1,%v", serverPort), b, "--errorlog-file="+testDir+"/log.txt", "-LINFO") authority := fmt.Sprintf("127.0.0.1:%v", serverPort) st := &serverTester{ cmd: exec.Command(serverBin, args...), t: t, ts: ts, url: fmt.Sprintf("%v://%v", scheme, authority), frontendHost: fmt.Sprintf("127.0.0.1:%v", serverPort), backendHost: backendURL.Host, nextStreamID: 1, authority: authority, frCh: make(chan http2.Frame), spdyFrCh: make(chan spdy.Frame), errCh: make(chan error), } if err := st.cmd.Start(); err != nil { st.t.Fatalf("Error starting %v: %v", serverBin, err) } retry := 0 for { var conn net.Conn var err error if frontendTLS { var tlsConfig *tls.Config if clientConfig == nil { tlsConfig = new(tls.Config) } else { tlsConfig = clientConfig } tlsConfig.InsecureSkipVerify = true tlsConfig.NextProtos = []string{"h2-14", "spdy/3.1"} conn, err = tls.Dial("tcp", authority, tlsConfig) } else { conn, err = net.Dial("tcp", authority) } if err != nil { retry += 1 if retry >= 100 { st.Close() st.t.Fatalf("Error server is not responding too long; server command-line arguments may be invalid") } time.Sleep(150 * time.Millisecond) continue } if frontendTLS { tlsConn := conn.(*tls.Conn) cs := tlsConn.ConnectionState() if !cs.NegotiatedProtocolIsMutual { st.Close() st.t.Fatalf("Error negotiated next protocol is not mutual") } } st.conn = conn break } st.fr = http2.NewFramer(st.conn, st.conn) spdyFr, err := spdy.NewFramer(st.conn, st.conn) if err != nil { st.Close() st.t.Fatalf("Error spdy.NewFramer: %v", err) } st.spdyFr = spdyFr st.enc = hpack.NewEncoder(&st.headerBlkBuf) st.dec = hpack.NewDecoder(4096, func(f hpack.HeaderField) { st.header.Add(f.Name, f.Value) }) return st }
func CreateHttp2Conn(ctx *Context, sn bool) *Http2Conn { var conn net.Conn var err error if ctx.Tls { conn, err = connectTls(ctx) } else { conn, err = net.DialTimeout("tcp", ctx.Authority(), ctx.Timeout) } if err != nil { printError(fmt.Sprintf("Unable to connect to the target server (%v)", err)) os.Exit(1) } fmt.Fprintf(conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") fr := http2.NewFramer(conn, conn) settings := map[http2.SettingID]uint32{} if sn { doneCh := make(chan bool, 1) errCh := make(chan error, 1) fr.WriteSettings() go func() { local := false remote := false for { f, err := fr.ReadFrame() if err != nil { errCh <- err return } switch f := f.(type) { case *http2.SettingsFrame: if f.IsAck() { local = true } else { f.ForeachSetting(func(setting http2.Setting) error { settings[setting.ID] = setting.Val return nil }) fr.WriteSettingsAck() remote = true } } if local && remote { doneCh <- true return } } }() select { case <-doneCh: // Nothing to do. case <-errCh: printError("HTTP/2 settings negotiation failed") os.Exit(1) case <-time.After(ctx.Timeout): printError("HTTP/2 settings negotiation timeout") os.Exit(1) } } fr.AllowIllegalWrites = true dataCh := make(chan http2.Frame) errCh := make(chan error, 1) http2Conn := &Http2Conn{ conn: conn, fr: fr, dataCh: dataCh, errCh: errCh, Settings: settings, } http2Conn.HpackEncoder = hpack.NewEncoder(&http2Conn.HeaderWriteBuf) return http2Conn }
func newHTTP2Connection( isServer bool, netConn net.Conn, serverStreamHandler StreamHandler) (*http2Connection, error) { var hpackBuffer bytes.Buffer writer := bufio.NewWriterSize(netConn, http2IOBufSize) conn := &http2Connection{ isServer: isServer, serverStreamHandler: serverStreamHandler, netConn: netConn, writer: writer, framer: http2.NewFramer(writer, netConn), hpackBuffer: &hpackBuffer, hpackEncoder: hpack.NewEncoder(&hpackBuffer), controlBuffer: leverutil.NewUnboundedChannel(), flowControl: &inFlow{limit: initialConnWindowSize}, sendQuotaPool: newQuotaPool(defaultWindowSize), writableChan: make(chan int, 1), shutdownChan: make(chan struct{}), closedChan: make(chan struct{}), streamSendQuota: defaultWindowSize, streams: make(map[uint32]*HTTP2Stream), } if !isServer { conn.nextStreamID = 1 // Odd numbers for client-initiated streams. n, err := netConn.Write(clientPreface) if err != nil { return nil, err } if n != len(clientPreface) { return nil, fmt.Errorf("Preface length mismatch") } } var settings []http2.Setting if initialWindowSize != defaultWindowSize { settings = append( settings, http2.Setting{ ID: http2.SettingInitialWindowSize, Val: uint32(initialWindowSize), }) } err := conn.framer.WriteSettings(settings...) if err != nil { return nil, err } windowDelta := uint32(initialConnWindowSize - defaultWindowSize) if windowDelta > 0 { err := conn.framer.WriteWindowUpdate(0, windowDelta) if err != nil { return nil, err } } conn.writer.Flush() conn.writableChan <- 0 // The controller is responsible with writing frames on the wire. go conn.controller() // The reader reads in frames from the wire, handles stream 0 / // control frames and dispatches frames for the rest of the streams. go conn.reader() return conn, nil }
func TestHTTP2MatchHeaderField(t *testing.T) { defer leakCheck(t)() errCh := make(chan error) defer func() { select { case err := <-errCh: t.Fatal(err) default: } }() name := "name" value := "value" writer, reader := net.Pipe() go func() { if _, err := io.WriteString(writer, http2.ClientPreface); err != nil { t.Fatal(err) } var buf bytes.Buffer enc := hpack.NewEncoder(&buf) if err := enc.WriteField(hpack.HeaderField{Name: name, Value: value}); err != nil { t.Fatal(err) } framer := http2.NewFramer(writer, nil) err := framer.WriteHeaders(http2.HeadersFrameParam{ StreamID: 1, BlockFragment: buf.Bytes(), EndStream: true, EndHeaders: true, }) if err != nil { t.Fatal(err) } if err := writer.Close(); err != nil { t.Fatal(err) } }() l := newChanListener() l.connCh <- reader muxl := New(l) // Register a bogus matcher that only reads one byte. muxl.Match(func(r io.Reader) bool { var b [1]byte _, _ = r.Read(b[:]) return false }) // Create a matcher that cannot match the response. muxl.Match(HTTP2HeaderField(name, "another"+value)) // Then match with the expected field. h2l := muxl.Match(HTTP2HeaderField(name, value)) go safeServe(errCh, muxl) muxedConn, err := h2l.Accept() close(l.connCh) if err != nil { t.Fatal(err) } var b [len(http2.ClientPreface)]byte // We have the sniffed buffer first... if _, err := muxedConn.Read(b[:]); err == io.EOF { t.Fatal(err) } if string(b[:]) != http2.ClientPreface { t.Errorf("got unexpected read %s, expected %s", b, http2.ClientPreface) } }