blocking a non-blocking socket
An interesting question has come up in the development of the SSL module. The function ssl.wrap_socket() takes a flag, do_handshake_on_connect, which tells it whether to do the SSL handshake before returning an SSLSocket object to the caller. If the socket being wrapped is non-blocking, the code in wrap_socket() must invoke do_handshake() multiple times, and effectively block until the handshake is done. Right now, I'm doing it with this loop: if do_handshake_on_connect: # have to loop to support non-blocking sockets while True: try: self.do_handshake() break except SSLError, err: if err.args[0] == SSL_ERROR_WANT_READ: select.select([self], [], []) elif err.args[0] == SSL_ERROR_WANT_WRITE: select.select([], [self], []) else: raise But this seems fragile to me. "select" and/or "poll" is awfully system-dependent. What I'd like to do is just use the socket API, something like: blocking = self.getblocking() try: self.setblocking(1) self.do_handshake() finally: self.setblocking(blocking) But there's no "getblocking" method on sockets. Instead, there's "gettimeout". So I'd write something like timeout = self.gettimeout() try: self.setblocking(1) self.do_handshake() finally: if (timeout == 0.0): self.setblocking(0) But my mother taught me never to test for equality against floating-point zero. But in this case it might just fly... Or, should I just set the timeout: timeout = self.gettimeout() try: self.settimeout(None) self.do_handshake() finally: self.settimeout(timeout) This is the solution I'm leaning towards... Any recommendations? Bill
On Sun, Dec 02, 2007 at 12:23:01PM -0800, Bill Janssen wrote: [skip]
Or, should I just set the timeout:
timeout = self.gettimeout() try: self.settimeout(None) self.do_handshake() finally: self.settimeout(timeout)
Yes, this is the correct solution for all cases: if the timeout is None (socket is blocking) or 0 (non-blocking) or not-0 (blocking with timeout) - just set it back. Oleg. -- Oleg Broytmann http://phd.pp.ru/ phd@phd.pp.ru Programmers don't die, they just GOSUB without RETURN.
An interesting question has come up in the development of the SSL module.
The function ssl.wrap_socket() takes a flag, do_handshake_on_connect, which tells it whether to do the SSL handshake before returning an SSLSocket object to the caller. If the socket being wrapped is non-blocking, the code in wrap_socket() must invoke do_handshake() multiple times, and effectively block until the handshake is done.
Right now, I'm doing it with this loop:
if do_handshake_on_connect: # have to loop to support non-blocking sockets while True: try: self.do_handshake() break except SSLError, err: if err.args[0] == SSL_ERROR_WANT_READ: select.select([self], [], []) elif err.args[0] == SSL_ERROR_WANT_WRITE: select.select([], [self], []) else: raise
Hello Bill, Another way of doing it could be to expose a connect() method on the ssl objects. It changes the socket.ssl api, but I'd say it is in the same spirit as the do_handshake_on_connect parameter since no existing code will break. The caller then calls connect() until it does not return SSL_ERROR_WANT_[WRITE|READ]. This only applies when the underlying socket is non-blocking and the do_handshake_on_connect parameter is false. Arguably, this is similar to how normal sockets are treated in asyncore, only that he caller must be prepared to respond to (multiple?) readable and writable events for the connection to be established. Cheers Audun
Thanks, Audun. If you look at the code, you'll see that both a connect method and a do_handshake method already exist, and work pretty much as you describe. The issue is what to do when the user doesn't use them -- specifies do_handshake_on_connect=True.
Another way of doing it could be to expose a connect() method on the ssl objects. It changes the socket.ssl api, but I'd say it is in the same spirit as the do_handshake_on_connect parameter since no existing code will break. The caller then calls connect() until it does not return
Bill
participants (3)
-
Audun Ostrem Nordal
-
Bill Janssen
-
Oleg Broytmann