예제 #1
0
파일: h2spec.go 프로젝트: dockbiz/h2spec
func CreateHttp2Conn(ctx *Context, sn bool) *Http2Conn {
	conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", ctx.Host, ctx.Port))
	if err != nil {
		fmt.Println("Unable to connect to the target server.")
		os.Exit(1)
	}

	fmt.Fprintf(conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")

	if sn {
		done := false
		fr := http2.NewFramer(conn, conn)
		fr.WriteSettings()

		for {
			f, _ := fr.ReadFrame()
			switch f := f.(type) {
			case *http2.SettingsFrame:
				if f.IsAck() {
					done = true
				} else {
					fr.WriteSettingsAck()
				}
			default:
				done = true
			}

			if done {
				break
			}
		}
	}

	fr := http2.NewFramer(conn, conn)
	fr.AllowIllegalWrites = true
	dataCh := make(chan http2.Frame)
	errCh := make(chan error, 1)

	http2Conn := &Http2Conn{
		conn:   conn,
		fr:     fr,
		dataCh: dataCh,
		errCh:  errCh,
	}

	go func() {
		for {
			f, err := fr.ReadFrame()
			dataCh <- f
			if err != nil {
				errCh <- err
				return
			}
		}
	}()

	return http2Conn
}
예제 #2
0
파일: matchers.go 프로젝트: joushou/cmux
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:
			hdec.Write(f.HeaderBlockFragment())
			if matched {
				return true
			}

			if f.FrameHeader.Flags&http2.FlagHeadersEndHeaders != 0 {
				return false
			}
		}
	}
}
예제 #3
0
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
}
예제 #4
0
파일: http2.go 프로젝트: sjn1978/go-fuzz
func Fuzz(data []byte) int {
	framer := http2.NewFramer(ioutil.Discard, bytes.NewReader(data))
	framer.SetMaxReadFrameSize(64 << 10)
	framer.AllowIllegalWrites = true
	for score := 0; ; score++ {
		f, err := framer.ReadFrame()
		if err != nil {
			if f != nil {
				panic(fmt.Sprintf("ReadFrame failed with '%v' but frame is not nil", err))
			}
			return score
		}
		switch ff := f.(type) {
		case *http2.ContinuationFrame:
			err = framer.WriteContinuation(ff.Header().StreamID, ff.HeadersEnded(), ff.HeaderBlockFragment())
		case *http2.DataFrame:
			err = framer.WriteData(ff.Header().StreamID, ff.StreamEnded(), ff.Data())
		case *http2.GoAwayFrame:
			err = framer.WriteGoAway(ff.LastStreamID, ff.ErrCode, ff.DebugData())
		case *http2.HeadersFrame:
			err = framer.WriteHeaders(http2.HeadersFrameParam{ff.Header().StreamID, ff.HeaderBlockFragment(), ff.StreamEnded(), ff.HeadersEnded(), 0, ff.Priority})
		case *http2.PingFrame:
			err = framer.WritePing(ff.Header().Flags&http2.FlagPingAck != 0, ff.Data)
		case *http2.PriorityFrame:
			err = framer.WritePriority(ff.Header().StreamID, ff.PriorityParam)
		case *http2.PushPromiseFrame:
			err = framer.WritePushPromise(http2.PushPromiseParam{ff.Header().StreamID, ff.PromiseID, ff.HeaderBlockFragment(), ff.HeadersEnded(), 0})
		case *http2.RSTStreamFrame:
			err = framer.WriteRSTStream(ff.Header().StreamID, ff.ErrCode)
		case *http2.SettingsFrame:
			var ss []http2.Setting
			ff.ForeachSetting(func(s http2.Setting) error {
				ss = append(ss, s)
				return nil
			})
			if ff.IsAck() {
				err = framer.WriteSettingsAck()
			} else {
				err = framer.WriteSettings(ss...)
			}
		case *http2.WindowUpdateFrame:
			err = framer.WriteWindowUpdate(ff.Header().StreamID, ff.Increment)
		case *http2.UnknownFrame:
			err = framer.WriteRawFrame(ff.Header().Type, ff.Header().Flags, ff.Header().StreamID, ff.Payload())
		default:
			panic(fmt.Sprintf("unknown frame type %+v", ff))
		}
		if err != nil {
			panic(fmt.Sprintf("WriteFrame failed with '%v'", err))
		}
	}
}
예제 #5
0
// newHTTP2Client constructs a connected ClientTransport to addr based on HTTP2
// and starts to receive messages on it. Non-nil error returns if construction
// fails.
func newHTTP2Client(addr string, authOpts []credentials.Credentials) (_ ClientTransport, err error) {
	var (
		connErr error
		conn    net.Conn
	)
	scheme := "http"
	// TODO(zhaoq): Use DialTimeout instead.
	for _, c := range authOpts {
		if ccreds, ok := c.(credentials.TransportAuthenticator); ok {
			scheme = "https"
			// TODO(zhaoq): Now the first TransportAuthenticator is used if there are
			// multiple ones provided. Revisit this if it is not appropriate. Probably
			// place the ClientTransport construction into a separate function to make
			// things clear.
			conn, connErr = ccreds.Dial(addr)
			break
		}
	}
	if scheme == "http" {
		conn, connErr = net.Dial("tcp", addr)
	}
	if connErr != nil {
		return nil, ConnectionErrorf("transport: %v", connErr)
	}
	defer func() {
		if err != nil {
			conn.Close()
		}
	}()
	// Send connection preface to server.
	n, err := conn.Write(clientPreface)
	if err != nil {
		return nil, ConnectionErrorf("transport: %v", err)
	}
	if n != len(clientPreface) {
		return nil, ConnectionErrorf("transport: preface mismatch, wrote %d bytes; want %d", n, len(clientPreface))
	}
	framer := http2.NewFramer(conn, conn)
	if err := framer.WriteSettings(); err != nil {
		return nil, ConnectionErrorf("transport: %v", err)
	}
	var buf bytes.Buffer
	t := &http2Client{
		target: addr,
		conn:   conn,
		// The client initiated stream id is odd starting from 1.
		nextID:        1,
		writableChan:  make(chan int, 1),
		shutdownChan:  make(chan struct{}),
		errorChan:     make(chan struct{}),
		framer:        framer,
		hBuf:          &buf,
		hEnc:          hpack.NewEncoder(&buf),
		controlBuf:    newRecvBuffer(),
		sendQuotaPool: newQuotaPool(initialWindowSize),
		scheme:        scheme,
		state:         reachable,
		activeStreams: make(map[uint32]*Stream),
		maxStreams:    math.MaxUint32,
		authCreds:     authOpts,
	}
	go t.controller()
	t.writableChan <- 0
	// Start the reader goroutine for incoming message. The threading model
	// on receiving is that each transport has a dedicated goroutine which
	// reads HTTP2 frame from network. Then it dispatches the frame to the
	// corresponding stream entity.
	go t.reader()
	return t, nil
}
예제 #6
0
파일: h2spec.go 프로젝트: idurando/h2spec
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.Dial("tcp", ctx.Authority())
	}

	if err != nil {
		fmt.Printf("Unable to connect to the target server: %v\n", 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:
			fmt.Println("HTTP/2 settings negotiation failed")
			os.Exit(1)
		case <-time.After(ctx.Timeout):
			fmt.Println("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
}
예제 #7
0
func (t *Transport) newClientConn(host, port, key string) (*clientConn, error) {
	cfg := &tls.Config{
		ServerName:         host,
		NextProtos:         []string{http2.NextProtoTLS},
		InsecureSkipVerify: t.InsecureTLSDial,
	}
	tconn, err := tls.Dial("tcp", host+":"+port, cfg)
	if err != nil {
		return nil, err
	}
	if err := tconn.Handshake(); err != nil {
		return nil, err
	}
	if !t.InsecureTLSDial {
		if err := tconn.VerifyHostname(cfg.ServerName); err != nil {
			return nil, err
		}
	}
	state := tconn.ConnectionState()
	if p := state.NegotiatedProtocol; p != http2.NextProtoTLS {
		// TODO(bradfitz): fall back to Fallback
		// return nil, fmt.Errorf("bad protocol: %v", p)
		return nil, errBadProtocol
	}
	if !state.NegotiatedProtocolIsMutual {
		return nil, errors.New("could not negotiate protocol mutually")
	}
	if _, err := tconn.Write(clientPreface); err != nil {
		return nil, err
	}

	cc := &clientConn{
		t:                    t,
		tconn:                tconn,
		connKey:              []string{key}, // TODO: cert's validated hostnames too
		tlsState:             &state,
		readerDone:           make(chan struct{}),
		nextStreamID:         1,
		maxFrameSize:         16 << 10, // spec default
		initialWindowSize:    65535,    // spec default
		maxConcurrentStreams: 1000,     // "infinite", per spec. 1000 seems good enough.
		streams:              make(map[uint32]*clientStream),
	}
	cc.bw = bufio.NewWriter(stickyErrWriter{tconn, &cc.werr})
	cc.br = bufio.NewReader(tconn)
	cc.fr = http2.NewFramer(cc.bw, cc.br)
	cc.henc = hpack.NewEncoder(&cc.hbuf)

	cc.fr.WriteSettings()
	// TODO: re-send more conn-level flow control tokens when server uses all these.
	cc.fr.WriteWindowUpdate(0, 1<<30) // um, 0x7fffffff doesn't work to Google? it hangs?
	cc.bw.Flush()
	if cc.werr != nil {
		return nil, cc.werr
	}

	// Read the obligatory SETTINGS frame
	f, err := cc.fr.ReadFrame()
	if err != nil {
		return nil, err
	}
	sf, ok := f.(*http2.SettingsFrame)
	if !ok {
		return nil, fmt.Errorf("expected settings frame, got: %T", f)
	}
	cc.fr.WriteSettingsAck()
	cc.bw.Flush()

	sf.ForeachSetting(func(s http2.Setting) error {
		switch s.ID {
		case http2.SettingMaxFrameSize:
			cc.maxFrameSize = s.Val
		case http2.SettingMaxConcurrentStreams:
			cc.maxConcurrentStreams = s.Val
		case http2.SettingInitialWindowSize:
			cc.initialWindowSize = s.Val
		default:
			// TODO(bradfitz): handle more
			log.Printf("Unhandled Setting: %v", s)
		}
		return nil
	})
	// TODO: figure out henc size
	cc.hdec = hpack.NewDecoder(initialHeaderTableSize, cc.onNewHeaderField)

	go cc.readLoop()
	return cc, nil
}
예제 #8
0
func (conn *Connection) SetupFramer() {
	conn.Framer = http2.NewFramer(conn.Raw, conn.Raw)
	conn.Framer.AllowIllegalWrites = true
}
예제 #9
0
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
}
예제 #10
0
func custom() {
	dest := "localhost"
	port := ":5500"
	// Create a new client.

	tlscfg := &tls.Config{
		ServerName:         dest,
		NextProtos:         []string{http2.NextProtoTLS},
		InsecureSkipVerify: true,
	}

	conn, err := tls.Dial("tcp", dest+port, tlscfg)
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	conn.Handshake()
	state := conn.ConnectionState()
	fmt.Printf("Protocol is : %q\n", state.NegotiatedProtocol)

	if _, err := io.WriteString(conn, http2.ClientPreface); err != nil {
		fmt.Printf("Preface failed: %s", err)
		return
	}

	var hbuf bytes.Buffer
	framer := http2.NewFramer(conn, conn)

	enc := hpack.NewEncoder(&hbuf)
	writeHeader(enc, ":authority", "localhost")
	writeHeader(enc, ":method", "GET")
	writeHeader(enc, ":path", "/ping")
	writeHeader(enc, ":scheme", "https")
	writeHeader(enc, "Accept", "*/*")

	if len(hbuf.Bytes()) > 16<<10 {
		fmt.Printf("Need CONTINUATION\n")
	}

	headers := http2.HeadersFrameParam{
		StreamID:      1,
		EndStream:     true,
		EndHeaders:    true,
		BlockFragment: hbuf.Bytes(),
	}

	fmt.Printf("All the stuff: %q\n", headers.BlockFragment)

	go listen(framer)

	framer.WriteSettings()
	framer.WriteWindowUpdate(0, 1<<30)
	framer.WriteSettingsAck()

	time.Sleep(time.Second * 2)
	framer.WriteHeaders(headers)
	time.Sleep(time.Second * 2)

	/* A ping HTTP request
	var payload [8]byte
	copy(payload[:], "_c0ffee_")
	framer.WritePing(false, payload)
	rawpong, err := framer.ReadFrame()
	if err != nil {
		panic(err)
	}

	pong, ok := rawpong.(*http2.PingFrame)
	if !ok {
		fmt.Printf("Instead of a Ping, I got this: %v\n", pong)
		return
	}

	fmt.Printf("Pong: %q\n", pong.Data)
	*/

}
예제 #11
0
// 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
}