Description
What version of Go are you using (go version
)?
$ go version go version go1.19.5 linux/amd64
Does this issue reproduce with the latest release?
The issue with the latest code of master still exists
What operating system and processor architecture are you using (go env
)?
go env
Output
$ go env
What did you do?
When Containerd processes exec requests, it will upgrade the HTTP protocol to SPDY through HTTP Upgrade.
// UpgradeResponse upgrades an HTTP response to one that supports multiplexed
// streams. newStreamHandler will be called synchronously whenever the
// other end of the upgraded connection creates a new stream.
func (u responseUpgrader) UpgradeResponse(w http.ResponseWriter, req *http.Request, newStreamHandler httpstream.NewStreamHandler) httpstream.Connection {
connectionHeader := strings.ToLower(req.Header.Get(httpstream.HeaderConnection))
upgradeHeader := strings.ToLower(req.Header.Get(httpstream.HeaderUpgrade))
if !strings.Contains(connectionHeader, strings.ToLower(httpstream.HeaderUpgrade)) || !strings.Contains(upgradeHeader, strings.ToLower(HeaderSpdy31)) {
errorMsg := fmt.Sprintf("unable to upgrade: missing upgrade headers in request: %#v", req.Header)
http.Error(w, errorMsg, http.StatusBadRequest)
return nil
}
hijacker, ok := w.(http.Hijacker)
if !ok {
errorMsg := fmt.Sprintf("unable to upgrade: unable to hijack response")
http.Error(w, errorMsg, http.StatusInternalServerError)
return nil
}
w.Header().Add(httpstream.HeaderConnection, httpstream.HeaderUpgrade)
w.Header().Add(httpstream.HeaderUpgrade, HeaderSpdy31)
w.WriteHeader(http.StatusSwitchingProtocols)
conn, bufrw, err := hijacker.Hijack()
if err != nil {
runtime.HandleError(fmt.Errorf("unable to upgrade: error hijacking response: %v", err))
return nil
}
In the Hijack
function, if writeHeader==true
, w.cw.flush()
will be called
Lines 2075 to 2077 in 24f83ed
and the writeHeader
in the flush
function will eventually clear the content of req.Body:
Lines 397 to 402 in 2c95fa4
Lines 1401 to 1422 in 2c95fa4
So if you want to clear req.Body, writeHeader
must be true. writeHeader is set in the (w *response) WriteHeader
function. But if the code is 101, it will not be set. So in our scene, writeHeader
is always false.
Lines 1143 to 1181 in 24f83ed
If there is data in the HTTP Body (such as Transfer-Encoding: chunked time, Body is 0\r\n\r\n), the raw conn obtained by Hijack will always have dirty data, resulting in the upgraded protocol (such as SPDY, WebSocket) have problems.
Can we modify Hijack
like this?
// Hijack implements the Hijacker.Hijack method. Our response is both a ResponseWriter
// and a Hijacker.
func (w *response) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error) {
if w.handlerDone.isSet() {
panic("net/http: Hijack called after ServeHTTP finished")
}
// if w.wroteHeader {
w.cw.flush() // Always call flush
// }