From 9cadad009e6aa78e15d752e2011705d7d91b8d2e Mon Sep 17 00:00:00 2001 From: Imogen <59090860+ImogenBits@users.noreply.github.com> Date: Mon, 8 May 2023 19:01:19 +0200 Subject: [PATCH 1/3] api: respect timeouts on Windows named pipes (#3112) Signed-off-by: Imogen <59090860+ImogenBits@users.noreply.github.com> --- docker/transport/npipesocket.py | 55 +++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/docker/transport/npipesocket.py b/docker/transport/npipesocket.py index 766372aefd..9cbe40cc7f 100644 --- a/docker/transport/npipesocket.py +++ b/docker/transport/npipesocket.py @@ -4,6 +4,9 @@ import win32file import win32pipe +import pywintypes +import win32event +import win32api cERROR_PIPE_BUSY = 0xe7 cSECURITY_SQOS_PRESENT = 0x100000 @@ -54,7 +57,9 @@ def connect(self, address, retry_count=0): 0, None, win32file.OPEN_EXISTING, - cSECURITY_ANONYMOUS | cSECURITY_SQOS_PRESENT, + (cSECURITY_ANONYMOUS + | cSECURITY_SQOS_PRESENT + | win32file.FILE_FLAG_OVERLAPPED), 0 ) except win32pipe.error as e: @@ -131,22 +136,37 @@ def recv_into(self, buf, nbytes=0): if not isinstance(buf, memoryview): readbuf = memoryview(buf) - err, data = win32file.ReadFile( - self._handle, - readbuf[:nbytes] if nbytes else readbuf - ) - return len(data) - - def _recv_into_py2(self, buf, nbytes): - err, data = win32file.ReadFile(self._handle, nbytes or len(buf)) - n = len(data) - buf[:n] = data - return n + event = win32event.CreateEvent(None, True, True, None) + try: + overlapped = pywintypes.OVERLAPPED() + overlapped.hEvent = event + err, data = win32file.ReadFile( + self._handle, + readbuf[:nbytes] if nbytes else readbuf, + overlapped + ) + wait_result = win32event.WaitForSingleObject(event, self._timeout) + if wait_result == win32event.WAIT_TIMEOUT: + win32file.CancelIo(self._handle) + raise TimeoutError + return win32file.GetOverlappedResult(self._handle, overlapped, 0) + finally: + win32api.CloseHandle(event) @check_closed def send(self, string, flags=0): - err, nbytes = win32file.WriteFile(self._handle, string) - return nbytes + event = win32event.CreateEvent(None, True, True, None) + try: + overlapped = pywintypes.OVERLAPPED() + overlapped.hEvent = event + win32file.WriteFile(self._handle, string, overlapped) + wait_result = win32event.WaitForSingleObject(event, self._timeout) + if wait_result == win32event.WAIT_TIMEOUT: + win32file.CancelIo(self._handle) + raise TimeoutError + return win32file.GetOverlappedResult(self._handle, overlapped, 0) + finally: + win32api.CloseHandle(event) @check_closed def sendall(self, string, flags=0): @@ -165,15 +185,12 @@ def setblocking(self, flag): def settimeout(self, value): if value is None: # Blocking mode - self._timeout = win32pipe.NMPWAIT_WAIT_FOREVER + self._timeout = win32event.INFINITE elif not isinstance(value, (float, int)) or value < 0: raise ValueError('Timeout value out of range') - elif value == 0: - # Non-blocking mode - self._timeout = win32pipe.NMPWAIT_NO_WAIT else: # Timeout mode - Value converted to milliseconds - self._timeout = value * 1000 + self._timeout = int(value * 1000) def gettimeout(self): return self._timeout From c5e582c413a4a3a9eff6ee8208d195f657ffda94 Mon Sep 17 00:00:00 2001 From: loicleyendecker Date: Thu, 11 May 2023 18:36:37 +0100 Subject: [PATCH 2/3] api: avoid socket timeouts when executing commands (#3125) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only listen to read events when polling a socket in order to avoid incorrectly trying to read from a socket that is not actually ready. Signed-off-by: Loïc Leyendecker --- docker/utils/socket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/utils/socket.py b/docker/utils/socket.py index 3c31a98c5d..efb7458ee8 100644 --- a/docker/utils/socket.py +++ b/docker/utils/socket.py @@ -37,7 +37,7 @@ def read(socket, n=4096): select.select([socket], [], []) else: poll = select.poll() - poll.register(socket) + poll.register(socket, select.POLLIN | select.POLLPRI) poll.poll() try: From 14e8d07d4515eb893c35926aca75ecd521781baf Mon Sep 17 00:00:00 2001 From: Milas Bowman Date: Thu, 11 May 2023 15:35:42 -0400 Subject: [PATCH 3/3] docs: update changelog (#3127) Signed-off-by: Milas Bowman --- docs/change-log.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/docs/change-log.md b/docs/change-log.md index 5927728b1a..0d60f882d6 100644 --- a/docs/change-log.md +++ b/docs/change-log.md @@ -1,6 +1,47 @@ Changelog ========== +6.1.2 +----- + +#### Bugfixes +- Fix for socket timeouts on long `docker exec` calls + +6.1.1 +----- + +#### Bugfixes +- Fix `containers.stats()` hanging with `stream=True` +- Correct return type in docs for `containers.diff()` method + + +6.1.0 +----- + +### Upgrade Notes +- Errors are no longer returned during client initialization if the credential helper cannot be found. A warning will be emitted instead, and an error is returned if the credential helper is used. + +### Features +- Python 3.11 support +- Use `poll()` instead of `select()` on non-Windows platforms +- New API fields + - `network_driver_opt` on container run / create + - `one-shot` on container stats + - `status` on services list + +### Bugfixes +- Support for requests 2.29.0+ and urllib3 2.x +- Do not strip characters from volume names +- Fix connection leak on container.exec_* operations +- Fix errors closing named pipes on Windows + +6.0.1 +----- + +### Bugfixes +- Fix for `The pipe has been ended errors` on Windows +- Support floats for container log filtering by timestamp (`since` / `until`) + 6.0.0 -----