Skip to content

Commit 5ade47a

Browse files
nik-localstackGitHub Copilotclaude
authored
fix(postgresql-proxy): prevent SSL COPY stalls by draining pending SSL buffer (#12)
* fix(postgresql-proxy): prevent SSL COPY stalls by draining nonblocking reads * chore: bump version to 0.3.1 and update changelog --------- Co-authored-by: GitHub Copilot <copilot@github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 37a1fee commit 5ade47a

3 files changed

Lines changed: 16 additions & 1 deletion

File tree

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ If you want to test it, do this. Otherwise scroll down for instructions on how t
5555
```
5656

5757
### Changelog
58+
- v0.3.1
59+
- Fix SSL COPY stalls by draining pending SSL buffer after recv [#11](https://github.com/localstack/postgresql-proxy/pull/11)
60+
- Fix intermittent `BlockingIOError` on macOS during SSL negotiation
5861
- v0.3.0
5962
- Add support for SSL connections [#9](https://github.com/localstack/postgresql-proxy/pull/9)
6063
- v0.2.1

postgresql_proxy/proxy.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ def accept_wrapper(self, sock: socket.socket):
130130

131131
# Accept the raw connection
132132
clientsocket, address = sock.accept()
133+
# On macOS, accepted sockets inherit O_NONBLOCK from the listening socket.
134+
# SSL negotiation uses blocking recv, so we must set blocking explicitly here.
135+
clientsocket.setblocking(True)
133136

134137
# Check if SSL is enabled for this proxy
135138
if self.ssl_context:
@@ -234,6 +237,15 @@ def service_connection(self, key: SelectorKeyProxy, mask):
234237
if recv_data:
235238
LOG.debug('%s received data:\n%s', conn.name, recv_data)
236239
conn.received(recv_data)
240+
# excerpt from https://docs.python.org/3/library/ssl.html#ssl-nonblocking
241+
# Conversely, since the SSL layer has its own framing, a SSL socket may still have data available
242+
# for reading without select() being aware of it. Therefore, you should first call SSLSocket.recv()
243+
# to drain any potentially available data, and then only block on a select() call if still necessary.
244+
while isinstance(sock, ssl.SSLSocket) and sock.pending() > 0:
245+
extra = sock.recv(4096)
246+
if extra:
247+
LOG.debug('%s received pending SSL data:\n%s', conn.name, extra)
248+
conn.received(extra)
237249
else:
238250
self._unregister_conn(conn)
239251
LOG.debug('%s connection closing %s', conn.name, conn.address)

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
setup(
1212
name='postgresql-proxy',
13-
version='0.3.0',
13+
version='0.3.1',
1414
description='Postgresql Proxy',
1515
packages=find_packages(exclude=('tests', 'tests.*')),
1616
install_requires=install_requires,

0 commit comments

Comments
 (0)