Skip to content

Commit

Permalink
martian(h2): fix half-close propagation downstream
Browse files Browse the repository at this point in the history
Use http2.Response.Close() to send RST_STREAM frame.
Reimplement writeFlusher as h2Writer.

This is to be tested in Sauce Connect due to lack of testing infrastructure for http2 at the moment.
I added #869 to fix that.
  • Loading branch information
mmatczuk committed Jul 26, 2024
1 parent 7816067 commit fb8f70e
Showing 1 changed file with 20 additions and 11 deletions.
31 changes: 20 additions & 11 deletions internal/martian/proxy_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ func (p proxyHandler) tunnel(name string, rw http.ResponseWriter, req *http.Requ

cc = []copier{
{"outbound " + name, crw, req.Body},
{"inbound " + name, writeFlusher{rw, rc}, crw},
{"inbound " + name, makeH2Writer(rw, rc, req), crw},
}
default:
err := fmt.Errorf("unsupported protocol version: %d", req.ProtoMajor)
Expand Down Expand Up @@ -337,27 +337,36 @@ func (p proxyHandler) handleRequest(rw http.ResponseWriter, req *http.Request) {
}
}

type writeFlusher struct {
rw io.Writer
rc *http.ResponseController
type h2Writer struct {
w io.Writer
flush func() error
close func() error
}

func (w writeFlusher) Write(p []byte) (n int, err error) {
n, err = w.rw.Write(p)
func makeH2Writer(rw http.ResponseWriter, rc *http.ResponseController, req *http.Request) *h2Writer {
return &h2Writer{
w: rw,
flush: rc.Flush,
close: req.Body.Close,
}
}

func (w h2Writer) Write(p []byte) (n int, err error) {
n, err = w.w.Write(p)

if n > 0 {
if err := w.rc.Flush(); err != nil {
if err := w.flush(); err != nil {
log.Errorf(context.TODO(), "got error while flushing response back to client: %v", err)
}
}

return
}

func (w writeFlusher) CloseWrite() error {
// This is a nop implementation of closeWriter.
// It avoids printing the error log "cannot close write side of inbound CONNECT tunnel".
return nil
func (w h2Writer) CloseWrite() error {
// Close request body to signal the end of the request.
// This results RST_STREAM frame with error code NO_ERROR to be sent to the other side.
return w.close()
}

func (p proxyHandler) writeErrorResponse(rw http.ResponseWriter, req *http.Request, err error) {
Expand Down

0 comments on commit fb8f70e

Please sign in to comment.