Finished up SSL.Connection compatibility. Backwards compatibility change: it used to be that you could just call close() on an ssl socket, but now you have to call shutdown() and then close(). This is in line with the normal OpenSSL behavior and means that we no longer penalize correctly-written clients. Fixed wsgi and api_test to reflect this changed assumption.

This commit is contained in:
Ryan Williams
2009-07-19 03:55:54 -07:00
parent 6d51a0e7f5
commit 85e75d572d
3 changed files with 123 additions and 24 deletions

View File

@@ -233,12 +233,6 @@ class GreenSocket(object):
if self.closed: if self.closed:
return return
self.closed = True self.closed = True
if self.is_secure:
# *NOTE: This is not quite the correct SSL shutdown sequence.
# We should actually be checking the return value of shutdown.
# Note also that this is not the same as calling self.shutdown().
self.fd.shutdown()
fn = self.close = self.fd.close fn = self.close = self.fd.close
try: try:
res = fn(*args, **kw) res = fn(*args, **kw)
@@ -362,10 +356,7 @@ class GreenSocket(object):
return fn(*args, **kw) return fn(*args, **kw)
def shutdown(self, *args, **kw): def shutdown(self, *args, **kw):
if self.is_secure: fn = self.shutdown = self.fd.shutdown
fn = self.shutdown = self.fd.sock_shutdown
else:
fn = self.shutdown = self.fd.shutdown
return fn(*args, **kw) return fn(*args, **kw)
def settimeout(self, howlong): def settimeout(self, howlong):
@@ -543,10 +534,69 @@ class GreenSSL(GreenSocket):
"GreenSSL can only be constructed with an "\ "GreenSSL can only be constructed with an "\
"OpenSSL Connection object") "OpenSSL Connection object")
self.sock = self self.sock = self
def close(self):
# *NOTE: in older versions of eventlet, we called shutdown() on SSL sockets
# before closing them. That wasn't right because correctly-written clients
# would have already called shutdown, and calling shutdown a second time
# triggers unwanted bidirectional communication.
super(GreenSSL, self).close()
def do_handshake(self):
""" Perform an SSL handshake (usually called after renegotiate or one of
set_accept_state or set_accept_state). This can raise the same exceptions as
send and recv. """
if self.act_non_blocking:
return self.fd.do_handshake()
while True:
try:
return self.fd.do_handshake()
except util.SSL.WantReadError:
trampoline(self.fd.fileno(),
read=True,
timeout=self.timeout,
timeout_exc=socket.timeout)
except util.SSL.WantWriteError:
trampoline(self.fd.fileno(),
write=True,
timeout=self.timeout,
timeout_exc=socket.timeout)
def dup(self):
raise NotImplementedError("Dup not supported on SSL sockets")
def get_app_data(self, *args, **kw):
fn = self.get_app_data = self.fd.get_app_data
return fn(*args, **kw)
def set_app_data(self, *args, **kw):
fn = self.set_app_data = self.fd.set_app_data
return fn(*args, **kw)
def get_cipher_list(self, *args, **kw):
fn = self.get_cipher_list = self.fd.get_cipher_list
return fn(*args, **kw)
def get_context(self, *args, **kw):
fn = self.get_context = self.fd.get_context
return fn(*args, **kw)
def get_peer_certificate(self, *args, **kw):
fn = self.get_peer_certificate = self.fd.get_peer_certificate
return fn(*args, **kw)
def makefile(self, mode='r', bufsize=-1):
raise NotImplementedError("Makefile not supported on SSL sockets")
def pending(self, *args, **kw):
fn = self.pending = self.fd.pending
return fn(*args, **kw)
def read(self, size): def read(self, size):
"""Works like a blocking call to SSL_read(), whose behavior is """Works like a blocking call to SSL_read(), whose behavior is
described here: http://www.openssl.org/docs/ssl/SSL_read.html""" described here: http://www.openssl.org/docs/ssl/SSL_read.html"""
if self.act_non_blocking:
return self.fd.read(size)
while True: while True:
try: try:
return self.fd.read(size) return self.fd.read(size)
@@ -565,12 +615,18 @@ class GreenSSL(GreenSocket):
return '' return ''
recv = read recv = read
def renegotiate(self, *args, **kw):
fn = self.renegotiate = self.fd.renegotiate
return fn(*args, **kw)
def write(self, data): def write(self, data):
"""Works like a blocking call to SSL_write(), whose behavior is """Works like a blocking call to SSL_write(), whose behavior is
described here: http://www.openssl.org/docs/ssl/SSL_write.html""" described here: http://www.openssl.org/docs/ssl/SSL_write.html"""
if not data: if not data:
return 0 # calling SSL_write() with 0 bytes to be sent is undefined return 0 # calling SSL_write() with 0 bytes to be sent is undefined
if self.act_non_blocking:
return self.fd.write(data)
while True: while True:
try: try:
return self.fd.write(data) return self.fd.write(data)
@@ -596,15 +652,55 @@ class GreenSSL(GreenSocket):
tail = self.send(data) tail = self.send(data)
while tail < len(data): while tail < len(data):
tail += self.send(data[tail:]) tail += self.send(data[tail:])
def set_accept_state(self, *args, **kw):
fn = self.set_accept_state = self.fd.set_accept_state
return fn(*args, **kw)
def dup(self): def set_connect_state(self, *args, **kw):
raise NotImplementedError("Dup not supported on SSL sockets") fn = self.set_connect_state = self.fd.set_connect_state
return fn(*args, **kw)
def shutdown(self):
if self.act_non_blocking:
return self.fd.shutdown()
while True:
try:
return self.fd.shutdown()
except util.SSL.WantReadError:
trampoline(self.fd.fileno(),
read=True,
timeout=self.timeout,
timeout_exc=socket.timeout)
except util.SSL.WantWriteError:
trampoline(self.fd.fileno(),
write=True,
timeout=self.timeout,
timeout_exc=socket.timeout)
def get_shutdown(self, *args, **kw):
fn = self.get_shutdown = self.fd.get_shutdown
return fn(*args, **kw)
def set_shutdown(self, *args, **kw):
fn = self.set_shutdown = self.fd.set_shutdown
return fn(*args, **kw)
def sock_shutdown(self, *args, **kw):
fn = self.sock_shutdown = self.fd.sock_shutdown
return fn(*args, **kw)
def state_string(self, *args, **kw):
fn = self.state_string = self.fd.state_string
return fn(*args, **kw)
def makefile(self, mode='r', bufsize=-1): def want_read(self, *args, **kw):
raise NotImplementedError("Makefile not supported on SSL sockets") fn = self.want_read = self.fd.want_read
return fn(*args, **kw)
def pending(self, *args, **kw): def want_write(self, *args, **kw):
fn = self.pending = self.fd.pending fn = self.want_write = self.fd.want_write
return fn(*args, **kw) return fn(*args, **kw)

View File

@@ -358,6 +358,8 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
def finish(self): def finish(self):
BaseHTTPServer.BaseHTTPRequestHandler.finish(self) BaseHTTPServer.BaseHTTPRequestHandler.finish(self)
if self.connection.is_secure:
self.connection.shutdown()
self.connection.close() self.connection.close()
@@ -459,6 +461,8 @@ def server(sock, site,
break break
finally: finally:
try: try:
if sock.is_secure:
sock.shutdown()
sock.close() sock.close()
except socket.error, e: except socket.error, e:
if e[0] != errno.EPIPE: if e[0] != errno.EPIPE:

View File

@@ -21,6 +21,7 @@
import os import os
import os.path import os.path
import socket
from unittest import TestCase, main from unittest import TestCase, main
from eventlet import api from eventlet import api
@@ -60,7 +61,7 @@ class TestApi(TestCase):
def accept_once(listenfd): def accept_once(listenfd):
try: try:
conn, addr = listenfd.accept() conn, addr = listenfd.accept()
fd = conn.makeGreenFile() fd = conn.makefile()
conn.close() conn.close()
fd.write('hello\n') fd.write('hello\n')
fd.close() fd.close()
@@ -71,7 +72,7 @@ class TestApi(TestCase):
api.spawn(accept_once, server) api.spawn(accept_once, server)
client = api.connect_tcp(('127.0.0.1', server.getsockname()[1])) client = api.connect_tcp(('127.0.0.1', server.getsockname()[1]))
fd = client.makeGreenFile() fd = client.makefile()
client.close() client.close()
assert fd.readline() == 'hello\n' assert fd.readline() == 'hello\n'
@@ -84,9 +85,7 @@ class TestApi(TestCase):
def accept_once(listenfd): def accept_once(listenfd):
try: try:
conn, addr = listenfd.accept() conn, addr = listenfd.accept()
fl = conn.makeGreenFile('w') conn.write('hello\r\n')
fl.write('hello\r\n')
fl.close()
conn.close() conn.close()
finally: finally:
listenfd.close() listenfd.close()
@@ -98,10 +97,10 @@ class TestApi(TestCase):
client = util.wrap_ssl( client = util.wrap_ssl(
api.connect_tcp(('127.0.0.1', server.getsockname()[1]))) api.connect_tcp(('127.0.0.1', server.getsockname()[1])))
client = client.makeGreenFile() fd = socket._fileobject(client, 'rb', 8192)
assert client.readline() == 'hello\r\n' assert fd.readline() == 'hello\r\n'
assert client.read() == '' assert fd.read() == ''
client.close() client.close()
def test_server(self): def test_server(self):