Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 08b41ad

Browse files
committed
Fix kubernetes port forwarding from hanging in certain edge cases
It turns out SSL sockets can buffer data such that the select method is not aware of it. See: https://docs.python.org/3/library/ssl.html#notes-on-non-blocking-sockets https://docs.python.org/3/library/ssl.html#ssl.SSLSocket.pending
1 parent 51c4816 commit 08b41ad

File tree

2 files changed

+32
-28
lines changed

2 files changed

+32
-28
lines changed

kubernetes/base/stream/ws_client.py

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -370,34 +370,38 @@ def _proxy(self):
370370
r, w, _ = select.select(rlist, wlist, [])
371371
for sock in r:
372372
if sock == self.websocket:
373-
opcode, frame = self.websocket.recv_data_frame(True)
374-
if opcode == ABNF.OPCODE_BINARY:
375-
if not frame.data:
376-
raise RuntimeError("Unexpected frame data size")
377-
channel = six.byte2int(frame.data)
378-
if channel >= len(channel_ports):
379-
raise RuntimeError("Unexpected channel number: %s" % channel)
380-
port = channel_ports[channel]
381-
if channel_initialized[channel]:
382-
if channel % 2:
383-
if port.error is None:
384-
port.error = ''
385-
port.error += frame.data[1:].decode()
373+
pending = True
374+
while pending:
375+
opcode, frame = self.websocket.recv_data_frame(True)
376+
if opcode == ABNF.OPCODE_BINARY:
377+
if not frame.data:
378+
raise RuntimeError("Unexpected frame data size")
379+
channel = six.byte2int(frame.data)
380+
if channel >= len(channel_ports):
381+
raise RuntimeError("Unexpected channel number: %s" % channel)
382+
port = channel_ports[channel]
383+
if channel_initialized[channel]:
384+
if channel % 2:
385+
if port.error is None:
386+
port.error = ''
387+
port.error += frame.data[1:].decode()
388+
else:
389+
port.data += frame.data[1:]
386390
else:
387-
port.data += frame.data[1:]
388-
else:
389-
if len(frame.data) != 3:
390-
raise RuntimeError(
391-
"Unexpected initial channel frame data size"
392-
)
393-
port_number = six.byte2int(frame.data[1:2]) + (six.byte2int(frame.data[2:3]) * 256)
394-
if port_number != port.port_number:
395-
raise RuntimeError(
396-
"Unexpected port number in initial channel frame: %s" % port_number
397-
)
398-
channel_initialized[channel] = True
399-
elif opcode not in (ABNF.OPCODE_PING, ABNF.OPCODE_PONG, ABNF.OPCODE_CLOSE):
400-
raise RuntimeError("Unexpected websocket opcode: %s" % opcode)
391+
if len(frame.data) != 3:
392+
raise RuntimeError(
393+
"Unexpected initial channel frame data size"
394+
)
395+
port_number = six.byte2int(frame.data[1:2]) + (six.byte2int(frame.data[2:3]) * 256)
396+
if port_number != port.port_number:
397+
raise RuntimeError(
398+
"Unexpected port number in initial channel frame: %s" % port_number
399+
)
400+
channel_initialized[channel] = True
401+
elif opcode not in (ABNF.OPCODE_PING, ABNF.OPCODE_PONG, ABNF.OPCODE_CLOSE):
402+
raise RuntimeError("Unexpected websocket opcode: %s" % opcode)
403+
if not isinstance(self.websocket.sock, ssl.SSLSocket) or not self.websocket.sock.pending():
404+
pending = False
401405
else:
402406
port = local_ports[sock]
403407
data = port.python.recv(1024 * 1024)

kubernetes/e2e_test/test_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ def test_exit_code(self):
233233
# Skipping this test as this flakes a lot
234234
# See: https://github.com/kubernetes-client/python/issues/1300
235235
# Re-enable the test once the flakiness is investigated
236-
@unittest.skip("skipping due to extreme flakiness")
236+
#@unittest.skip("skipping due to extreme flakiness")
237237
def test_portforward_raw(self):
238238
client = api_client.ApiClient(configuration=self.config)
239239
api = core_v1_api.CoreV1Api(client)

0 commit comments

Comments
 (0)