cmd/tailscale/cli: actually perform Noise request in 'debug ts2021'
This actually performs a Noise request in the 'debug ts2021' command, instead of just exiting once we've dialed a connection. This can help debug certain forms of captive portals and deep packet inspection that will allow a connection, but will RST the connection when trying to send data on the post-upgraded TCP connection. Updates #1634 Signed-off-by: Andrew Dunham <andrew@du.nham.ca> Change-Id: I1e46ca9c9a0751c55f16373a6a76cdc24fec1f18
This commit is contained in:
parent
732605f961
commit
24976b5bfd
|
@ -28,10 +28,12 @@ import (
|
||||||
|
|
||||||
"github.com/peterbourgon/ff/v3/ffcli"
|
"github.com/peterbourgon/ff/v3/ffcli"
|
||||||
"golang.org/x/net/http/httpproxy"
|
"golang.org/x/net/http/httpproxy"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
"tailscale.com/client/tailscale"
|
"tailscale.com/client/tailscale"
|
||||||
"tailscale.com/client/tailscale/apitype"
|
"tailscale.com/client/tailscale/apitype"
|
||||||
"tailscale.com/control/controlhttp"
|
"tailscale.com/control/controlhttp"
|
||||||
"tailscale.com/hostinfo"
|
"tailscale.com/hostinfo"
|
||||||
|
"tailscale.com/internal/noiseconn"
|
||||||
"tailscale.com/ipn"
|
"tailscale.com/ipn"
|
||||||
"tailscale.com/net/tsaddr"
|
"tailscale.com/net/tsaddr"
|
||||||
"tailscale.com/net/tshttpproxy"
|
"tailscale.com/net/tshttpproxy"
|
||||||
|
@ -801,7 +803,10 @@ func runTS2021(ctx context.Context, args []string) error {
|
||||||
log.Printf("Dial(%q, %q) ...", network, address)
|
log.Printf("Dial(%q, %q) ...", network, address)
|
||||||
c, err := dialer.DialContext(ctx, network, address)
|
c, err := dialer.DialContext(ctx, network, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// skip logging context cancellation errors
|
||||||
|
if !errors.Is(err, context.Canceled) {
|
||||||
log.Printf("Dial(%q, %q) = %v", network, address, err)
|
log.Printf("Dial(%q, %q) = %v", network, address, err)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Dial(%q, %q) = %v / %v", network, address, c.LocalAddr(), c.RemoteAddr())
|
log.Printf("Dial(%q, %q) = %v / %v", network, address, c.LocalAddr(), c.RemoteAddr())
|
||||||
}
|
}
|
||||||
|
@ -835,32 +840,51 @@ func runTS2021(ctx context.Context, args []string) error {
|
||||||
|
|
||||||
log.Printf("final underlying conn: %v / %v", conn.LocalAddr(), conn.RemoteAddr())
|
log.Printf("final underlying conn: %v / %v", conn.LocalAddr(), conn.RemoteAddr())
|
||||||
|
|
||||||
// Make a /whois request to the server to verify that we can actually
|
h2Transport, err := http2.ConfigureTransports(&http.Transport{
|
||||||
|
IdleConnTimeout: time.Second,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("http2.ConfigureTransports: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, create a Noise conn over the existing conn.
|
||||||
|
nc, err := noiseconn.New(conn.Conn, h2Transport, 0, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("noiseconn.New: %w", err)
|
||||||
|
}
|
||||||
|
defer nc.Close()
|
||||||
|
|
||||||
|
// Reserve a RoundTrip for the whoami request.
|
||||||
|
ok, _, err := nc.ReserveNewRequest(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ReserveNewRequest: %w", err)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return errors.New("ReserveNewRequest failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a /whoami request to the server to verify that we can actually
|
||||||
// communicate over the newly-established connection.
|
// communicate over the newly-established connection.
|
||||||
whoisURL := "http://" + ts2021Args.host + "/machine/whois"
|
whoamiURL := "http://" + ts2021Args.host + "/machine/whoami"
|
||||||
req, err = http.NewRequestWithContext(ctx, "GET", whoisURL, nil)
|
req, err = http.NewRequestWithContext(ctx, "GET", whoamiURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
resp, err := nc.RoundTrip(req)
|
||||||
// Use a fake http.Transport that just "dials" by returning the above
|
|
||||||
// conn.
|
|
||||||
tr := http.DefaultTransport.(*http.Transport).Clone()
|
|
||||||
tr.ForceAttemptHTTP2 = true
|
|
||||||
tr.DialContext = func(context.Context, string, string) (net.Conn, error) {
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
resp, err := tr.RoundTrip(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("RoundTrip whois request: %w", err)
|
return fmt.Errorf("RoundTrip whoami request: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
log.Printf("whoami request returned status %v", resp.Status)
|
||||||
|
} else {
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("reading whois response: %w", err)
|
return fmt.Errorf("reading whoami response: %w", err)
|
||||||
|
}
|
||||||
|
log.Printf("whoami response: %q", body)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("whois response: %q", body)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
||||||
tailscale.com/cmd/tailscale/cli from tailscale.com/cmd/tailscale
|
tailscale.com/cmd/tailscale/cli from tailscale.com/cmd/tailscale
|
||||||
tailscale.com/cmd/tailscale/cli/ffcomplete from tailscale.com/cmd/tailscale/cli
|
tailscale.com/cmd/tailscale/cli/ffcomplete from tailscale.com/cmd/tailscale/cli
|
||||||
tailscale.com/cmd/tailscale/cli/ffcomplete/internal from tailscale.com/cmd/tailscale/cli/ffcomplete
|
tailscale.com/cmd/tailscale/cli/ffcomplete/internal from tailscale.com/cmd/tailscale/cli/ffcomplete
|
||||||
tailscale.com/control/controlbase from tailscale.com/control/controlhttp
|
tailscale.com/control/controlbase from tailscale.com/control/controlhttp+
|
||||||
tailscale.com/control/controlhttp from tailscale.com/cmd/tailscale/cli
|
tailscale.com/control/controlhttp from tailscale.com/cmd/tailscale/cli
|
||||||
tailscale.com/control/controlknobs from tailscale.com/net/portmapper
|
tailscale.com/control/controlknobs from tailscale.com/net/portmapper
|
||||||
tailscale.com/derp from tailscale.com/derp/derphttp
|
tailscale.com/derp from tailscale.com/derp/derphttp
|
||||||
|
@ -89,6 +89,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
||||||
tailscale.com/health from tailscale.com/net/tlsdial+
|
tailscale.com/health from tailscale.com/net/tlsdial+
|
||||||
tailscale.com/health/healthmsg from tailscale.com/cmd/tailscale/cli
|
tailscale.com/health/healthmsg from tailscale.com/cmd/tailscale/cli
|
||||||
tailscale.com/hostinfo from tailscale.com/client/web+
|
tailscale.com/hostinfo from tailscale.com/client/web+
|
||||||
|
tailscale.com/internal/noiseconn from tailscale.com/cmd/tailscale/cli
|
||||||
tailscale.com/ipn from tailscale.com/client/tailscale+
|
tailscale.com/ipn from tailscale.com/client/tailscale+
|
||||||
tailscale.com/ipn/ipnstate from tailscale.com/client/tailscale+
|
tailscale.com/ipn/ipnstate from tailscale.com/client/tailscale+
|
||||||
tailscale.com/licenses from tailscale.com/client/web+
|
tailscale.com/licenses from tailscale.com/client/web+
|
||||||
|
@ -188,7 +189,8 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
||||||
golang.org/x/net/dns/dnsmessage from net+
|
golang.org/x/net/dns/dnsmessage from net+
|
||||||
golang.org/x/net/http/httpguts from net/http+
|
golang.org/x/net/http/httpguts from net/http+
|
||||||
golang.org/x/net/http/httpproxy from net/http+
|
golang.org/x/net/http/httpproxy from net/http+
|
||||||
golang.org/x/net/http2/hpack from net/http
|
golang.org/x/net/http2 from tailscale.com/cmd/tailscale/cli+
|
||||||
|
golang.org/x/net/http2/hpack from net/http+
|
||||||
golang.org/x/net/icmp from tailscale.com/net/ping
|
golang.org/x/net/icmp from tailscale.com/net/ping
|
||||||
golang.org/x/net/idna from golang.org/x/net/http/httpguts+
|
golang.org/x/net/idna from golang.org/x/net/http/httpguts+
|
||||||
golang.org/x/net/ipv4 from github.com/miekg/dns+
|
golang.org/x/net/ipv4 from github.com/miekg/dns+
|
||||||
|
|
|
@ -245,7 +245,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||||
tailscale.com/clientupdate from tailscale.com/client/web+
|
tailscale.com/clientupdate from tailscale.com/client/web+
|
||||||
tailscale.com/clientupdate/distsign from tailscale.com/clientupdate
|
tailscale.com/clientupdate/distsign from tailscale.com/clientupdate
|
||||||
tailscale.com/cmd/tailscaled/childproc from tailscale.com/cmd/tailscaled+
|
tailscale.com/cmd/tailscaled/childproc from tailscale.com/cmd/tailscaled+
|
||||||
tailscale.com/control/controlbase from tailscale.com/control/controlclient+
|
tailscale.com/control/controlbase from tailscale.com/control/controlhttp+
|
||||||
tailscale.com/control/controlclient from tailscale.com/cmd/tailscaled+
|
tailscale.com/control/controlclient from tailscale.com/cmd/tailscaled+
|
||||||
tailscale.com/control/controlhttp from tailscale.com/control/controlclient
|
tailscale.com/control/controlhttp from tailscale.com/control/controlclient
|
||||||
tailscale.com/control/controlknobs from tailscale.com/control/controlclient+
|
tailscale.com/control/controlknobs from tailscale.com/control/controlclient+
|
||||||
|
@ -265,6 +265,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||||
tailscale.com/health from tailscale.com/control/controlclient+
|
tailscale.com/health from tailscale.com/control/controlclient+
|
||||||
tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal
|
tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal
|
||||||
tailscale.com/hostinfo from tailscale.com/client/web+
|
tailscale.com/hostinfo from tailscale.com/client/web+
|
||||||
|
tailscale.com/internal/noiseconn from tailscale.com/control/controlclient
|
||||||
tailscale.com/ipn from tailscale.com/client/tailscale+
|
tailscale.com/ipn from tailscale.com/client/tailscale+
|
||||||
tailscale.com/ipn/conffile from tailscale.com/cmd/tailscaled+
|
tailscale.com/ipn/conffile from tailscale.com/cmd/tailscaled+
|
||||||
💣 tailscale.com/ipn/ipnauth from tailscale.com/ipn/ipnlocal+
|
💣 tailscale.com/ipn/ipnauth from tailscale.com/ipn/ipnlocal+
|
||||||
|
|
|
@ -174,12 +174,12 @@ func (nc *NoiseClient) GetSingleUseRoundTripper(ctx context.Context) (http.Round
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
rt, earlyPayloadMaybeNil, err := conn.ReserveNewRequest(ctx)
|
ok, earlyPayloadMaybeNil, err := conn.ReserveNewRequest(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if rt != nil {
|
if ok {
|
||||||
return rt, earlyPayloadMaybeNil, nil
|
return conn, earlyPayloadMaybeNil, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, nil, errors.New("[unexpected] failed to reserve a request on a connection")
|
return nil, nil, errors.New("[unexpected] failed to reserve a request on a connection")
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
|
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
"tailscale.com/control/controlhttp"
|
"tailscale.com/control/controlhttp"
|
||||||
|
"tailscale.com/internal/noiseconn"
|
||||||
"tailscale.com/net/netmon"
|
"tailscale.com/net/netmon"
|
||||||
"tailscale.com/net/tsdial"
|
"tailscale.com/net/tsdial"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
|
@ -93,20 +94,19 @@ func (tt noiseClientTest) run(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
select {
|
payload, err := c.GetEarlyPayload(ctx)
|
||||||
case <-c.earlyPayloadReady:
|
if err != nil {
|
||||||
gotNonNil := c.earlyPayload != nil
|
t.Fatal("timed out waiting for didReadHeaderCh")
|
||||||
if gotNonNil != tt.sendEarlyPayload {
|
|
||||||
t.Errorf("sendEarlyPayload = %v but got earlyPayload = %T", tt.sendEarlyPayload, c.earlyPayload)
|
|
||||||
}
|
|
||||||
if c.earlyPayload != nil {
|
|
||||||
if c.earlyPayload.NodeKeyChallenge != chalPrivate.Public() {
|
|
||||||
t.Errorf("earlyPayload.NodeKeyChallenge = %v; want %v", c.earlyPayload.NodeKeyChallenge, chalPrivate.Public())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case <-ctx.Done():
|
gotNonNil := payload != nil
|
||||||
t.Fatal("timed out waiting for didReadHeaderCh")
|
if gotNonNil != tt.sendEarlyPayload {
|
||||||
|
t.Errorf("sendEarlyPayload = %v but got earlyPayload = %T", tt.sendEarlyPayload, payload)
|
||||||
|
}
|
||||||
|
if payload != nil {
|
||||||
|
if payload.NodeKeyChallenge != chalPrivate.Public() {
|
||||||
|
t.Errorf("earlyPayload.NodeKeyChallenge = %v; want %v", payload.NodeKeyChallenge, chalPrivate.Public())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkRes := func(t *testing.T, res *http.Response) {
|
checkRes := func(t *testing.T, res *http.Response) {
|
||||||
|
@ -184,7 +184,7 @@ func (up *Upgrader) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
// https://httpwg.org/specs/rfc7540.html#rfc.section.4.1 (Especially not
|
// https://httpwg.org/specs/rfc7540.html#rfc.section.4.1 (Especially not
|
||||||
// an HTTP/2 settings frame, which isn't of type 'T')
|
// an HTTP/2 settings frame, which isn't of type 'T')
|
||||||
var notH2Frame [5]byte
|
var notH2Frame [5]byte
|
||||||
copy(notH2Frame[:], earlyPayloadMagic)
|
copy(notH2Frame[:], noiseconn.EarlyPayloadMagic)
|
||||||
var lenBuf [4]byte
|
var lenBuf [4]byte
|
||||||
binary.BigEndian.PutUint32(lenBuf[:], uint32(len(earlyJSON)))
|
binary.BigEndian.PutUint32(lenBuf[:], uint32(len(earlyJSON)))
|
||||||
// These writes are all buffered by caller, so fine to do them
|
// These writes are all buffered by caller, so fine to do them
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Package noiseconn contains an internal-only wrapper around controlbase.Conn
|
||||||
|
// that properly handles the early payload sent by the server before the HTTP/2
|
||||||
|
// session begins.
|
||||||
|
//
|
||||||
|
// See the documentation on the Conn type for more details.
|
||||||
package noiseconn
|
package noiseconn
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -16,9 +24,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Conn is a wrapper around controlbase.Conn.
|
// Conn is a wrapper around controlbase.Conn.
|
||||||
// It allows attaching an ID to a connection to allow
|
//
|
||||||
// cleaning up references in the pool when the connection
|
// It allows attaching an ID to a connection to allow cleaning up references in
|
||||||
// is closed.
|
// the pool when the connection is closed, properly handles an optional "early
|
||||||
|
// payload" that's sent prior to beginning the HTTP/2 session, and provides a
|
||||||
|
// way to return a connection to a pool when the connection is closed.
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
*controlbase.Conn
|
*controlbase.Conn
|
||||||
id int
|
id int
|
||||||
|
@ -59,9 +69,13 @@ func (c *Conn) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||||
return c.h2cc.RoundTrip(r)
|
return c.h2cc.RoundTrip(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getEarlyPayload waits for the early noise payload to arrive.
|
// GetEarlyPayload waits for the early Noise payload to arrive.
|
||||||
// It may return (nil, nil) if the server begins HTTP/2 without one.
|
// It may return (nil, nil) if the server begins HTTP/2 without one.
|
||||||
func (c *Conn) getEarlyPayload(ctx context.Context) (*tailcfg.EarlyNoise, error) {
|
//
|
||||||
|
// It is safe to call this multiple times; all callers will block until the
|
||||||
|
// early Noise payload is ready (if any) and will return the same result for
|
||||||
|
// the lifetime of the Conn.
|
||||||
|
func (c *Conn) GetEarlyPayload(ctx context.Context) (*tailcfg.EarlyNoise, error) {
|
||||||
select {
|
select {
|
||||||
case <-c.earlyPayloadReady:
|
case <-c.earlyPayloadReady:
|
||||||
return c.earlyPayload, c.earlyPayloadErr
|
return c.earlyPayload, c.earlyPayloadErr
|
||||||
|
@ -71,30 +85,39 @@ func (c *Conn) getEarlyPayload(ctx context.Context) (*tailcfg.EarlyNoise, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReserveNewRequest will reserve a new concurrent request on the connection.
|
// ReserveNewRequest will reserve a new concurrent request on the connection.
|
||||||
// It returns a non-nil http.RoundTripper if the reservation was successful,
|
//
|
||||||
// and any early Noise payload if present. If a reservation was not successful,
|
// It returns whether the reservation was successful, and any early Noise
|
||||||
// it will return nil with no error.
|
// payload if present. If a reservation was not successful, it will return
|
||||||
func (c *Conn) ReserveNewRequest(ctx context.Context) (http.RoundTripper, *tailcfg.EarlyNoise, error) {
|
// false and nil for the early payload.
|
||||||
earlyPayloadMaybeNil, err := c.getEarlyPayload(ctx)
|
func (c *Conn) ReserveNewRequest(ctx context.Context) (bool, *tailcfg.EarlyNoise, error) {
|
||||||
|
earlyPayloadMaybeNil, err := c.GetEarlyPayload(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return false, nil, err
|
||||||
}
|
}
|
||||||
if c.h2cc.ReserveNewRequest() {
|
if c.h2cc.ReserveNewRequest() {
|
||||||
return c, earlyPayloadMaybeNil, nil
|
return true, earlyPayloadMaybeNil, nil
|
||||||
}
|
}
|
||||||
return nil, nil, nil
|
return false, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanTakeNewRequest reports whether the underlying HTTP/2 connection can take
|
||||||
|
// a new request, meaning it has not been closed or received or sent a GOAWAY.
|
||||||
|
func (c *Conn) CanTakeNewRequest() bool {
|
||||||
|
return c.h2cc.CanTakeNewRequest()
|
||||||
}
|
}
|
||||||
|
|
||||||
// The first 9 bytes from the server to client over Noise are either an HTTP/2
|
// The first 9 bytes from the server to client over Noise are either an HTTP/2
|
||||||
// settings frame (a normal HTTP/2 setup) or, as we added later, an "early payload"
|
// settings frame (a normal HTTP/2 setup) or, as we added later, an "early payload"
|
||||||
// header that's also 9 bytes long: 5 bytes (earlyPayloadMagic) followed by 4 bytes
|
// header that's also 9 bytes long: 5 bytes (EarlyPayloadMagic) followed by 4 bytes
|
||||||
// of length. Then that many bytes of JSON-encoded tailcfg.EarlyNoise.
|
// of length. Then that many bytes of JSON-encoded tailcfg.EarlyNoise.
|
||||||
// The early payload is optional. Some servers may not send it.
|
// The early payload is optional. Some servers may not send it.
|
||||||
const (
|
const (
|
||||||
hdrLen = 9 // http2 frame header size; also size of our early payload size header
|
hdrLen = 9 // http2 frame header size; also size of our early payload size header
|
||||||
earlyPayloadMagic = "\xff\xff\xffTS"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// EarlyPayloadMagic is the 5-byte magic prefix that indicates an early payload.
|
||||||
|
const EarlyPayloadMagic = "\xff\xff\xffTS"
|
||||||
|
|
||||||
// returnErrReader is an io.Reader that always returns an error.
|
// returnErrReader is an io.Reader that always returns an error.
|
||||||
type returnErrReader struct {
|
type returnErrReader struct {
|
||||||
err error // the error to return
|
err error // the error to return
|
||||||
|
@ -129,13 +152,13 @@ func (c *Conn) readHeader() {
|
||||||
setErr(err)
|
setErr(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if string(hdr[:len(earlyPayloadMagic)]) != earlyPayloadMagic {
|
if string(hdr[:len(EarlyPayloadMagic)]) != EarlyPayloadMagic {
|
||||||
// No early payload. We have to return the 9 bytes read we already
|
// No early payload. We have to return the 9 bytes read we already
|
||||||
// consumed.
|
// consumed.
|
||||||
c.reader = io.MultiReader(bytes.NewReader(hdr[:]), c.Conn)
|
c.reader = io.MultiReader(bytes.NewReader(hdr[:]), c.Conn)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
epLen := binary.BigEndian.Uint32(hdr[len(earlyPayloadMagic):])
|
epLen := binary.BigEndian.Uint32(hdr[len(EarlyPayloadMagic):])
|
||||||
if epLen > 10<<20 {
|
if epLen > 10<<20 {
|
||||||
setErr(errors.New("invalid early payload length"))
|
setErr(errors.New("invalid early payload length"))
|
||||||
return
|
return
|
||||||
|
@ -162,9 +185,3 @@ func (c *Conn) Close() error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanTakeNewRequest reports whether the connection can take a new request,
|
|
||||||
// meaning it has not been closed or received or sent a GOAWAY.
|
|
||||||
func (c *Conn) CanTakeNewRequest() bool {
|
|
||||||
return c.h2cc.CanTakeNewRequest()
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue