|
|
|
@ -42,12 +42,24 @@ type noiseConn struct {
|
|
|
|
|
reader io.Reader // (effectively Conn.Reader after header)
|
|
|
|
|
earlyPayloadReady chan struct{} // closed after earlyPayload is set (including set to nil)
|
|
|
|
|
earlyPayload *tailcfg.EarlyNoise
|
|
|
|
|
earlyPayloadErr error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *noiseConn) RoundTrip(r *http.Request) (*http.Response, error) {
|
|
|
|
|
return c.h2cc.RoundTrip(r)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// getEarlyPayload waits for the early noise payload to arrive.
|
|
|
|
|
// It may return (nil, nil) if the server begins HTTP/2 without one.
|
|
|
|
|
func (c *noiseConn) getEarlyPayload(ctx context.Context) (*tailcfg.EarlyNoise, error) {
|
|
|
|
|
select {
|
|
|
|
|
case <-c.earlyPayloadReady:
|
|
|
|
|
return c.earlyPayload, c.earlyPayloadErr
|
|
|
|
|
case <-ctx.Done():
|
|
|
|
|
return nil, ctx.Err()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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"
|
|
|
|
|
// header that's also 9 bytes long: 5 bytes (earlyPayloadMagic) followed by 4 bytes
|
|
|
|
@ -80,33 +92,38 @@ func (c *noiseConn) Read(p []byte) (n int, err error) {
|
|
|
|
|
// c.earlyPayload, closing c.earlyPayloadReady, and initializing c.reader for
|
|
|
|
|
// future reads.
|
|
|
|
|
func (c *noiseConn) readHeader() {
|
|
|
|
|
defer close(c.earlyPayloadReady)
|
|
|
|
|
|
|
|
|
|
setErr := func(err error) {
|
|
|
|
|
c.reader = returnErrReader{err}
|
|
|
|
|
c.earlyPayloadErr = err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var hdr [hdrLen]byte
|
|
|
|
|
if _, err := io.ReadFull(c.Conn, hdr[:]); err != nil {
|
|
|
|
|
c.reader = returnErrReader{err}
|
|
|
|
|
setErr(err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if string(hdr[:len(earlyPayloadMagic)]) != earlyPayloadMagic {
|
|
|
|
|
// No early payload. We have to return the 9 bytes read we already
|
|
|
|
|
// consumed.
|
|
|
|
|
close(c.earlyPayloadReady)
|
|
|
|
|
c.reader = io.MultiReader(bytes.NewReader(hdr[:]), c.Conn)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
epLen := binary.BigEndian.Uint32(hdr[len(earlyPayloadMagic):])
|
|
|
|
|
if epLen > 10<<20 {
|
|
|
|
|
c.reader = returnErrReader{errors.New("invalid early payload length")}
|
|
|
|
|
setErr(errors.New("invalid early payload length"))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
payBuf := make([]byte, epLen)
|
|
|
|
|
if _, err := io.ReadFull(c.Conn, payBuf); err != nil {
|
|
|
|
|
c.reader = returnErrReader{err}
|
|
|
|
|
setErr(err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if err := json.Unmarshal(payBuf, &c.earlyPayload); err != nil {
|
|
|
|
|
c.reader = returnErrReader{err}
|
|
|
|
|
setErr(err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
close(c.earlyPayloadReady)
|
|
|
|
|
c.reader = c.Conn
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|