Skip to content

Commit 6b6b0c8

Browse files
committed
bugfix(port-forward): Correctly handle known errors
These two errors can occur in the following scenarios: ECONNRESET: the target process reset connection between CRI and itself. see: #111825 for detail EPIPE: the target process did not read the received data, causing the buffer in the kernel to be full, resulting in the occurrence of Zero Window, then closing the connection (FIN, RESET) see: #74551 for detail In both cases, we should RESET the httpStream. Signed-off-by: wangxiang <[email protected]> (cherry picked from commit 232538b) Signed-off-by: sxllwx <[email protected]>
1 parent 6795037 commit 6b6b0c8

File tree

1 file changed

+38
-6
lines changed

1 file changed

+38
-6
lines changed

pkg/cri/streaming/portforward/httpstream.go

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import (
3838
"net/http"
3939
"strconv"
4040
"sync"
41+
"syscall"
4142
"time"
4243

4344
api "k8s.io/api/core/v1"
@@ -242,8 +243,16 @@ Loop:
242243
// portForward invokes the httpStreamHandler's forwarder.PortForward
243244
// function for the given stream pair.
244245
func (h *httpStreamHandler) portForward(p *httpStreamPair) {
245-
defer p.dataStream.Close()
246-
defer p.errorStream.Close()
246+
resetStreams := false
247+
defer func() {
248+
if resetStreams {
249+
p.dataStream.Reset()
250+
p.errorStream.Reset()
251+
return
252+
}
253+
p.dataStream.Close()
254+
p.errorStream.Close()
255+
}()
247256

248257
portString := p.dataStream.Headers().Get(api.PortHeader)
249258
port, _ := strconv.ParseInt(portString, 10, 32)
@@ -252,11 +261,34 @@ func (h *httpStreamHandler) portForward(p *httpStreamPair) {
252261
err := h.forwarder.PortForward(h.pod, h.uid, int32(port), p.dataStream)
253262
klog.V(5).Infof("(conn=%p, request=%s) done invoking forwarder.PortForward for port %s", h.conn, p.requestID, portString)
254263

255-
if err != nil {
256-
msg := fmt.Errorf("error forwarding port %d to pod %s, uid %v: %v", port, h.pod, h.uid, err)
257-
utilruntime.HandleError(msg)
258-
fmt.Fprint(p.errorStream, msg.Error())
264+
// happy path, we have successfully completed forwarding task
265+
if err == nil {
266+
return
267+
}
268+
269+
if errors.Is(err, syscall.EPIPE) || errors.Is(err, syscall.ECONNRESET) {
270+
// In the process of forwarding, we encountered error types that can be handled:
271+
//
272+
// These two errors can occur in the following scenarios:
273+
// ECONNRESET: the target process reset connection between CRI and itself.
274+
// see: https://github.com/kubernetes/kubernetes/issues/111825 for detail
275+
//
276+
// EPIPE: the target process did not read the received data, causing the
277+
// buffer in the kernel to be full, resulting in the occurrence of Zero Window,
278+
// then closing the connection (FIN, RESET)
279+
// see: https://github.com/kubernetes/kubernetes/issues/74551 for detail
280+
//
281+
// In both cases, we should RESET the httpStream.
282+
klog.ErrorS(err, "forwarding port", "conn", h.conn, "request", p.requestID, "port", portString)
283+
resetStreams = true
284+
return
259285
}
286+
287+
// We don't know how to deal with other types of errors,
288+
// try to forward them to errStream, let our user know what happened
289+
msg := fmt.Errorf("error forwarding port %d to pod %s, uid %v: %v", port, h.pod, h.uid, err)
290+
utilruntime.HandleError(msg)
291+
fmt.Fprint(p.errorStream, msg.Error())
260292
}
261293

262294
// httpStreamPair represents the error and data streams for a port

0 commit comments

Comments
 (0)