1 """Issue #143 - Socket timeouts in wsgi server not caught.
2 https://bitbucket.org/eventlet/eventlet/issue/143/
4 This file intentionally ignored by nose.
5 Caller process (tests.wsgi_test.TestWsgiConnTimeout) handles success / failure
8 Simulate server connection socket timeout without actually waiting.
9 Logs 'timed out' if server debug=True (similar to 'accepted' logging)
11 FAIL: if log (ie, _spawn_n_impl 'except:' catches timeout, logs TB)
12 NOTE: timeouts are NOT on server_sock, but on the conn sockets produced
13 by the socket.accept() call
15 server's socket.listen() sock - NaughtySocketAcceptWrap
19 server / client accept() conn - ExplodingConnectionWrap
23 connection makefile() file objects - ExplodingSocketFile <-- these raise
28 from eventlet.support import six
29 import tests.wsgi_test
32 # no standard tests in this file, ignore
36 TAG_BOOM = "=== ~* BOOM *~ ==="
41 class BufferLog(object):
44 output_buffer.append(s.rstrip())
47 # This test might make you wince
48 class NaughtySocketAcceptWrap(object):
49 # server's socket.accept(); patches resulting connection sockets
51 def __init__(self, sock):
53 self.sock._really_accept = self.sock.accept
54 self.sock.accept = self
58 self.sock.accept = self.sock._really_accept
59 del self.sock._really_accept
60 for conn_wrap in self.conn_reg:
64 output_buffer.append("ca-click")
65 for i in self.conn_reg:
69 output_buffer.append(self.__class__.__name__ + ".__call__")
70 conn, addr = self.sock._really_accept()
71 self.conn_reg.append(ExplodingConnectionWrap(conn))
75 class ExplodingConnectionWrap(object):
76 # new connection's socket.makefile
77 # eventlet *tends* to use socket.makefile, not raw socket methods.
78 # need to patch file operations
80 def __init__(self, conn):
82 self.conn._really_makefile = self.conn.makefile
83 self.conn.makefile = self
88 self.conn.makefile = self.conn._really_makefile
89 del self.conn._really_makefile
92 output_buffer.append("tick")
93 for i in self.file_reg:
96 def __call__(self, mode='r', bufsize=-1):
97 output_buffer.append(self.__class__.__name__ + ".__call__")
98 # file_obj = self.conn._really_makefile(*args, **kwargs)
99 file_obj = ExplodingSocketFile(self.conn._sock, mode, bufsize)
100 self.file_reg.append(file_obj)
104 class ExplodingSocketFile(eventlet.greenio._fileobject):
106 def __init__(self, sock, mode='rb', bufsize=-1, close=False):
107 args = [bufsize, close] if six.PY2 else []
108 super(self.__class__, self).__init__(sock, mode, *args)
112 output_buffer.append("beep")
117 output_buffer.append(TAG_BOOM)
118 raise socket.timeout("timed out")
120 def readline(self, *args, **kwargs):
121 output_buffer.append(self.__class__.__name__ + ".readline")
123 return super(self.__class__, self).readline(*args, **kwargs)
127 output_buffer[:] = []
129 server_sock = eventlet.listen(('localhost', 0))
130 server_addr = server_sock.getsockname()
131 sock_wrap = NaughtySocketAcceptWrap(server_sock)
134 eventlet.wsgi.server,
138 site=tests.wsgi_test.Site(),
144 sock1 = eventlet.connect(server_addr)
145 sock1.settimeout(0.1)
146 fd1 = sock1.makefile('rwb')
147 fd1.write(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
149 tests.wsgi_test.read_http(sock1)
151 # let the server socket ops catch up, set bomb
153 output_buffer.append("arming...")
156 # req #2 - old conn, post-arm - timeout
157 fd1.write(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
160 tests.wsgi_test.read_http(sock1)
161 assert False, 'Expected ConnectionClosed exception'
162 except tests.wsgi_test.ConnectionClosed:
168 # reset streams, then output trapped tracebacks
170 # check output asserts in tests.wsgi_test.TestHttpd
171 # test_143_server_connection_timeout_exception
173 return output_buffer[:]
177 output_normal = step(debug=False)
178 output_debug = step(debug=True)
180 assert "timed out" in output_debug[-1], repr(output_debug)
181 # if the BOOM check fails, it's because our timeout didn't happen
182 # (if eventlet stops using file.readline() to read HTTP headers,
184 assert TAG_BOOM == output_debug[-2], repr(output_debug)
185 assert TAG_BOOM == output_normal[-1], repr(output_normal)
186 assert "Traceback" not in output_debug, repr(output_debug)
187 assert "Traceback" not in output_normal, repr(output_normal)
190 if __name__ == '__main__':