Skip to content

Commit

Permalink
merge upstream http2: 2024-09-06(3c333c)
Browse files Browse the repository at this point in the history
  • Loading branch information
imroc committed Sep 11, 2024
1 parent fb51d5b commit db181cc
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 77 deletions.
40 changes: 37 additions & 3 deletions internal/http2/frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,9 @@ func (h2f *Framer) currentRequest(id uint32) *http.Request {
// returned error is errFrameTooLarge. Other errors may be of type
// ConnectionError, StreamError, or anything else from the underlying
// reader.
//
// If ReadFrame returns an error and a non-nil Frame, the Frame's StreamID
// indicates the stream responsible for the error.
func (h2f *Framer) ReadFrame() (Frame, error) {
h2f.errDetail = nil
if h2f.lastFrame != nil {
Expand Down Expand Up @@ -1555,7 +1558,7 @@ func (fr *Framer) maxHeaderStringLen() int {
// readMetaFrame returns 0 or more CONTINUATION frames from fr and
// merge them into the provided hf and returns a MetaHeadersFrame
// with the decoded hpack values.
func (h2f *Framer) readMetaFrame(hf *HeadersFrame, dumps []*dump.Dumper) (*MetaHeadersFrame, error) {
func (h2f *Framer) readMetaFrame(hf *HeadersFrame, dumps []*dump.Dumper) (Frame, error) {
if h2f.AllowIllegalReads {
return nil, errors.New("illegal use of AllowIllegalReads with ReadMetaHeaders")
}
Expand Down Expand Up @@ -1598,6 +1601,7 @@ func (h2f *Framer) readMetaFrame(hf *HeadersFrame, dumps []*dump.Dumper) (*MetaH
if size > remainSize {
hdec.SetEmitEnabled(false)
mh.Truncated = true
remainSize = 0
return
}
remainSize -= size
Expand All @@ -1621,8 +1625,38 @@ func (h2f *Framer) readMetaFrame(hf *HeadersFrame, dumps []*dump.Dumper) (*MetaH
var hc headersOrContinuation = hf
for {
frag := hc.HeaderBlockFragment()

// Avoid parsing large amounts of headers that we will then discard.
// If the sender exceeds the max header list size by too much,
// skip parsing the fragment and close the connection.
//
// "Too much" is either any CONTINUATION frame after we've already
// exceeded the max header list size (in which case remainSize is 0),
// or a frame whose encoded size is more than twice the remaining
// header list bytes we're willing to accept.
if int64(len(frag)) > int64(2*remainSize) {
if VerboseLogs {
log.Printf("http2: header list too large")
}
// It would be nice to send a RST_STREAM before sending the GOAWAY,
// but the structure of the server's frame writer makes this difficult.
return mh, ConnectionError(ErrCodeProtocol)
}

// Also close the connection after any CONTINUATION frame following an
// invalid header, since we stop tracking the size of the headers after
// an invalid one.
if invalid != nil {
if VerboseLogs {
log.Printf("http2: invalid header: %v", invalid)
}
// It would be nice to send a RST_STREAM before sending the GOAWAY,
// but the structure of the server's frame writer makes this difficult.
return mh, ConnectionError(ErrCodeProtocol)
}

if _, err := hdec.Write(frag); err != nil {
return nil, ConnectionError(ErrCodeCompression)
return mh, ConnectionError(ErrCodeCompression)
}

if hc.HeadersEnded() {
Expand All @@ -1639,7 +1673,7 @@ func (h2f *Framer) readMetaFrame(hf *HeadersFrame, dumps []*dump.Dumper) (*MetaH
mh.HeadersFrame.invalidate()

if err := hdec.Close(); err != nil {
return nil, ConnectionError(ErrCodeCompression)
return mh, ConnectionError(ErrCodeCompression)
}
if invalid != nil {
h2f.errDetail = invalid
Expand Down
7 changes: 3 additions & 4 deletions internal/http2/http2.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ package http2
import (
"bufio"
"crypto/tls"
"golang.org/x/net/http/httpguts"
"net/http"
"os"
"sort"
"strconv"
"strings"
"sync"

"golang.org/x/net/http/httpguts"
)

var (
Expand Down Expand Up @@ -50,9 +51,7 @@ const (
initialWindowSize = 65535 // 6.9.2 Initial Flow Control Window Size
)

var (
clientPreface = []byte(ClientPreface)
)
var clientPreface = []byte(ClientPreface)

// validWireHeaderFieldName reports whether v is a valid header field
// name (key). See httpguts.ValidHeaderName for the base rules.
Expand Down
11 changes: 10 additions & 1 deletion internal/http2/pipe.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ func (p *pipe) Read(d []byte) (n int, err error) {
}
}

var errClosedPipeWrite = errors.New("write on closed buffer")
var (
errClosedPipeWrite = errors.New("write on closed buffer")
errUninitializedPipeWrite = errors.New("write on uninitialized buffer")
)

// Write copies bytes from p into the buffer and wakes a reader.
// It is an error to write more data than the buffer can hold.
Expand All @@ -91,6 +94,12 @@ func (p *pipe) Write(d []byte) (n int, err error) {
if p.err != nil || p.breakErr != nil {
return 0, errClosedPipeWrite
}
// pipe.setBuffer is never invoked, leaving the buffer uninitialized.
// We shouldn't try to write to an uninitialized pipe,
// but returning an error is better than panicking.
if p.b == nil {
return 0, errUninitializedPipeWrite
}
return p.b.Write(d)
}

Expand Down
20 changes: 20 additions & 0 deletions internal/http2/timer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2024 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.
package http2

import "time"

// A timer is a time.Timer, as an interface which can be replaced in tests.
type timer = interface {
C() <-chan time.Time
Reset(d time.Duration) bool
Stop() bool
}

// timeTimer adapts a time.Timer to the timer interface.
type timeTimer struct {
*time.Timer
}

func (t timeTimer) C() <-chan time.Time { return t.Timer.C }
Loading

0 comments on commit db181cc

Please sign in to comment.