Add python-eventlet 0.16.1
[packages/trusty/python-eventlet.git] / eventlet / tests / wsgi_test_conntimeout.py
1 """Issue #143 - Socket timeouts in wsgi server not caught.
2 https://bitbucket.org/eventlet/eventlet/issue/143/
3
4 This file intentionally ignored by nose.
5 Caller process (tests.wsgi_test.TestWsgiConnTimeout) handles success / failure
6
7
8 Simulate server connection socket timeout without actually waiting.
9 Logs 'timed out' if server debug=True (similar to 'accepted' logging)
10
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
14
15 server's socket.listen() sock - NaughtySocketAcceptWrap
16     /  |  \
17     |  |  |   (1 - many)
18     V  V  V
19 server / client accept() conn - ExplodingConnectionWrap
20     /  |  \
21     |  |  |   (1 - many)
22     V  V  V
23 connection makefile() file objects - ExplodingSocketFile <-- these raise
24 """
25 from __future__ import print_function
26
27 import eventlet
28
29 import socket
30 import sys
31
32 import tests.wsgi_test
33
34
35 # no standard tests in this file, ignore
36 __test__ = False
37
38
39 # This test might make you wince
40 class NaughtySocketAcceptWrap(object):
41     # server's socket.accept(); patches resulting connection sockets
42
43     def __init__(self, sock):
44         self.sock = sock
45         self.sock._really_accept = self.sock.accept
46         self.sock.accept = self
47         self.conn_reg = []
48
49     def unwrap(self):
50         self.sock.accept = self.sock._really_accept
51         del self.sock._really_accept
52         for conn_wrap in self.conn_reg:
53             conn_wrap.unwrap()
54
55     def arm(self):
56         print("ca-click")
57         for i in self.conn_reg:
58             i.arm()
59
60     def __call__(self):
61         print(self.__class__.__name__ + ".__call__")
62         conn, addr = self.sock._really_accept()
63         self.conn_reg.append(ExplodingConnectionWrap(conn))
64         return conn, addr
65
66
67 class ExplodingConnectionWrap(object):
68     # new connection's socket.makefile
69     # eventlet *tends* to use socket.makefile, not raw socket methods.
70     # need to patch file operations
71
72     def __init__(self, conn):
73         self.conn = conn
74         self.conn._really_makefile = self.conn.makefile
75         self.conn.makefile = self
76         self.armed = False
77         self.file_reg = []
78
79     def unwrap(self):
80         self.conn.makefile = self.conn._really_makefile
81         del self.conn._really_makefile
82
83     def arm(self):
84         print("tick")
85         for i in self.file_reg:
86             i.arm()
87
88     def __call__(self, mode='r', bufsize=-1):
89         print(self.__class__.__name__ + ".__call__")
90         # file_obj = self.conn._really_makefile(*args, **kwargs)
91         file_obj = ExplodingSocketFile(self.conn._sock, mode, bufsize)
92         self.file_reg.append(file_obj)
93         return file_obj
94
95
96 class ExplodingSocketFile(eventlet.greenio._fileobject):
97
98     def __init__(self, sock, mode='rb', bufsize=-1, close=False):
99         super(self.__class__, self).__init__(sock, mode, bufsize, close)
100         self.armed = False
101
102     def arm(self):
103         print("beep")
104         self.armed = True
105
106     def _fuse(self):
107         if self.armed:
108             print("=== ~* BOOM *~ ===")
109             raise socket.timeout("timed out")
110
111     def readline(self, *args, **kwargs):
112         print(self.__class__.__name__ + ".readline")
113         self._fuse()
114         return super(self.__class__, self).readline(*args, **kwargs)
115
116
117 if __name__ == '__main__':
118     for debug in (False, True):
119         print("SEPERATOR_SENTINEL")
120         print("debug set to: %s" % debug)
121
122         server_sock = eventlet.listen(('localhost', 0))
123         server_addr = server_sock.getsockname()
124         sock_wrap = NaughtySocketAcceptWrap(server_sock)
125
126         eventlet.spawn_n(
127             eventlet.wsgi.server,
128             debug=debug,
129             log=sys.stdout,
130             max_size=128,
131             site=tests.wsgi_test.Site(),
132             sock=server_sock,
133         )
134
135         try:
136             # req #1 - normal
137             sock1 = eventlet.connect(server_addr)
138             sock1.settimeout(0.1)
139             fd1 = sock1.makefile('rwb')
140             fd1.write(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
141             fd1.flush()
142             tests.wsgi_test.read_http(sock1)
143
144             # let the server socket ops catch up, set bomb
145             eventlet.sleep(0)
146             print("arming...")
147             sock_wrap.arm()
148
149             # req #2 - old conn, post-arm - timeout
150             fd1.write(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
151             fd1.flush()
152             try:
153                 tests.wsgi_test.read_http(sock1)
154                 assert False, 'Expected ConnectionClosed exception'
155             except tests.wsgi_test.ConnectionClosed:
156                 pass
157
158             fd1.close()
159             sock1.close()
160         finally:
161             # reset streams, then output trapped tracebacks
162             sock_wrap.unwrap()
163         # check output asserts in tests.wsgi_test.TestHttpd
164         # test_143_server_connection_timeout_exception