Skip to content

Commit

Permalink
http2: close connections when receiving too many headers (#32)
Browse files Browse the repository at this point in the history
* http2: close connections when receiving too many headers

* fix annotation: struture -> structure

* fix format
  • Loading branch information
Weaxs authored Apr 23, 2024
1 parent ef05266 commit 7a95689
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 0 deletions.
31 changes: 31 additions & 0 deletions frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -1528,6 +1528,7 @@ func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
if size > remainSize {
hdec.SetEmitEnabled(false)
mh.Truncated = true
remainSize = 0
return
}
remainSize -= size
Expand All @@ -1540,6 +1541,36 @@ func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
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 {
hlog.SystemLogger().Infof("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 nil, 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 {
hlog.SystemLogger().Infof("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 nil, ConnectionError(ErrCodeProtocol)
}

if _, err := hdec.Write(frag); err != nil {
return nil, ConnectionError(ErrCodeCompression)
}
Expand Down
85 changes: 85 additions & 0 deletions server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3785,3 +3785,88 @@ func TestServer_Request_Te_Header_Check(t *testing.T) {
}
})
}

func TestServerContinuationFlood(t *testing.T) {
st := newHertzServerTester(t, func(c context.Context, ctx *app.RequestContext) {
})

defer st.Close()
st.writePreface()
st.writeInitialSettings()
st.writeSettingsAck()
framer := NewFramer(st.cc, newMockTLSConn(st.cc))
framer.MaxHeaderListSize = uint32(2368)
st.fr = framer
// st.fr.MaxHeaderListSize = uint32(2368)

st.writeHeaders(HeadersFrameParam{
StreamID: 1,
BlockFragment: st.encodeHeader(),
EndStream: true,
})
for i := 0; i < 22000; i++ {
st.fr.WriteContinuation(1, false, st.encodeHeaderRaw(
fmt.Sprintf("x-%v", i), "1234567890",
))
}
st.fr.WriteContinuation(1, true, st.encodeHeaderRaw(
"x-last-header", "1",
))

var sawGoAway bool
for {
f, err := st.readFrame()
if err != nil {
break
}
switch f.(type) {
case *GoAwayFrame:
sawGoAway = true
case *HeadersFrame:
t.Fatalf("received HEADERS frame; want GOAWAY")
}
}
if !sawGoAway {
t.Errorf("connection closed with no GOAWAY frame; want one")
}
}

func TestServerContinuationAfterInvalidHeader(t *testing.T) {
st := newHertzServerTester(t, func(c context.Context, ctx *app.RequestContext) {
})

defer st.Close()

st.writePreface()
st.writeInitialSettings()
st.writeSettingsAck()

st.writeHeaders(HeadersFrameParam{
StreamID: 1,
BlockFragment: st.encodeHeader(),
EndStream: true,
})
st.fr.WriteContinuation(1, false, st.encodeHeaderRaw(
"x-invalid-header", "\x00",
))
st.fr.WriteContinuation(1, true, st.encodeHeaderRaw(
"x-valid-header", "1",
))

var sawGoAway bool
for {
f, err := st.readFrame()
if err != nil {
break
}
switch f.(type) {
case *GoAwayFrame:
sawGoAway = true
case *HeadersFrame:
t.Fatalf("received HEADERS frame; want GOAWAY")
}
}
if !sawGoAway {
t.Errorf("connection closed with no GOAWAY frame; want one")
}
}

0 comments on commit 7a95689

Please sign in to comment.