http2: fix optimized write scheduling

Fixes regression from https://golang.org/cl/31495 which generated
massive stacks like:

net/http.(*http2serverConn).startFrameWrite(0x1890e7e0, 0x859bc70, 0x18c26050, 0x1897c1c0, 0x0)
    /tmp/workdir/go/src/net/http/h2_bundle.go:3664 +0x34a
net/http.(*http2serverConn).scheduleFrameWrite(0x1890e7e0)
    /tmp/workdir/go/src/net/http/h2_bundle.go:3779 +0x114
net/http.(*http2serverConn).wroteFrame(0x1890e7e0, 0x859bb50, 0x18c26060, 0x0, 0x0, 0x0, 0x0)
    /tmp/workdir/go/src/net/http/h2_bundle.go:3742 +0xc8
net/http.(*http2serverConn).startFrameWrite(0x1890e7e0, 0x859bb50, 0x18c26060, 0x0, 0x0)
    /tmp/workdir/go/src/net/http/h2_bundle.go:3689 +0x23f
net/http.(*http2serverConn).scheduleFrameWrite(0x1890e7e0)
    /tmp/workdir/go/src/net/http/h2_bundle.go:3779 +0x114
net/http.(*http2serverConn).writeFrame(0x1890e7e0, 0x859bb50, 0x18c26060, 0x0, 0x0)
    /tmp/workdir/go/src/net/http/h2_bundle.go:3648 +0x6b
net/http.(*http2serverConn).resetStream(0x1890e7e0, 0x1, 0x8, 0x0, 0x0)
    /tmp/workdir/go/src/net/http/h2_bundle.go:3815 +0x91
net/http.(*http2serverConn).wroteFrame(0x1890e7e0, 0x859b6d0, 0x1890db00, 0x1897c1c0, 0x18ede040, 0x0, 0x0)
    /tmp/workdir/go/src/net/http/h2_bundle.go:3736 +0x124
net/http.(*http2serverConn).startFrameWrite(0x1890e7e0, 0x859b6d0, 0x1890db00, 0x1897c1c0, 0x18ede040)
    /tmp/workdir/go/src/net/http/h2_bundle.go:3689 +0x23f
net/http.(*http2serverConn).scheduleFrameWrite(0x1890e7e0)
    /tmp/workdir/go/src/net/http/h2_bundle.go:3779 +0x114
net/http.(*http2serverConn).wroteFrame(0x1890e7e0, 0x859bc70, 0x18c26018, 0x0, 0x0, 0x0, 0x0)
    /tmp/workdir/go/src/net/http/h2_bundle.go:3742 +0xc8
net/http.(*http2serverConn).startFrameWrite(0x1890e7e0, 0x859bc70, 0x18c26018, 0x0, 0x0)
    /tmp/workdir/go/src/net/http/h2_bundle.go:3689 +0x23f
net/http.(*http2serverConn).scheduleFrameWrite(0x1890e7e0)
    /tmp/workdir/go/src/net/http/h2_bundle.go:3779 +0x114
net/http.(*http2serverConn).wroteFrame(0x1890e7e0, 0x859b730, 0x18ede000, 0x1897c1c0, 0x0, 0x0, 0x0)
    /tmp/workdir/go/src/net/http/h2_bundle.go:3742 +0xc8
net/http.(*http2serverConn).serve(0x1890e7e0)
    /tmp/workdir/go/src/net/http/h2_bundle.go:3504 +0x50d
net/http.(*http2Server).ServeConn(0x18ab49a0, 0x859e040, 0x18df2000, 0x1894ae18)
    /tmp/workdir/go/src/net/http/h2_bundle.go:3152 +0x6d9
net/http.http2ConfigureServer.func1(0x18d364e0, 0x18df2000, 0x859a1b0, 0x18ab2308)
    /tmp/workdir/go/src/net/http/h2_bundle.go:3033 +0x70
net/http.(*conn).serve(0x18ad60f0, 0x859cbc0, 0x18aa42a0)
    /tmp/workdir/go/src/net/http/server.go:1633 +0xf03

This changes it to be a loop instead.

This also fixes the "internal error: attempt to send frame on
half-closed-local stream" crash from golang/go#17627 because
wroteFrame set stateHalfClosedLocal temporarily while calling
resetStream which itself does a write in some cases. Prior to CL 31495
those writes were processed breadth-first. CL 31495 made the writes
generated from resetStream (like the window updates returning unused
flow-control) more aggressive and scheduled immediately. This loop
restores the old write scheduling.

Fixes golang/go#17627

Change-Id: I9651568e4105791d24d46819499efc7e018c86c3
Reviewed-on: https://go-review.googlesource.com/32217
Reviewed-by: Tom Bergan <tombergan@google.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
1 file changed