##// END OF EJS Templates
tests: change how sockets are closed...
Gregory Szorc -
r41605:d343d9ac default
parent child Browse files
Show More
@@ -1,301 +1,318 b''
1 # badserverext.py - Extension making servers behave badly
1 # badserverext.py - Extension making servers behave badly
2 #
2 #
3 # Copyright 2017 Gregory Szorc <gregory.szorc@gmail.com>
3 # Copyright 2017 Gregory Szorc <gregory.szorc@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 # no-check-code
8 # no-check-code
9
9
10 """Extension to make servers behave badly.
10 """Extension to make servers behave badly.
11
11
12 This extension is useful for testing Mercurial behavior when various network
12 This extension is useful for testing Mercurial behavior when various network
13 events occur.
13 events occur.
14
14
15 Various config options in the [badserver] section influence behavior:
15 Various config options in the [badserver] section influence behavior:
16
16
17 closebeforeaccept
17 closebeforeaccept
18 If true, close() the server socket when a new connection arrives before
18 If true, close() the server socket when a new connection arrives before
19 accept() is called. The server will then exit.
19 accept() is called. The server will then exit.
20
20
21 closeafteraccept
21 closeafteraccept
22 If true, the server will close() the client socket immediately after
22 If true, the server will close() the client socket immediately after
23 accept().
23 accept().
24
24
25 closeafterrecvbytes
25 closeafterrecvbytes
26 If defined, close the client socket after receiving this many bytes.
26 If defined, close the client socket after receiving this many bytes.
27
27
28 closeaftersendbytes
28 closeaftersendbytes
29 If defined, close the client socket after sending this many bytes.
29 If defined, close the client socket after sending this many bytes.
30 """
30 """
31
31
32 from __future__ import absolute_import
32 from __future__ import absolute_import
33
33
34 import socket
34 import socket
35
35
36 from mercurial import(
36 from mercurial import(
37 pycompat,
37 registrar,
38 registrar,
38 )
39 )
39
40
40 from mercurial.hgweb import (
41 from mercurial.hgweb import (
41 server,
42 server,
42 )
43 )
43
44
44 configtable = {}
45 configtable = {}
45 configitem = registrar.configitem(configtable)
46 configitem = registrar.configitem(configtable)
46
47
47 configitem(b'badserver', b'closeafteraccept',
48 configitem(b'badserver', b'closeafteraccept',
48 default=False,
49 default=False,
49 )
50 )
50 configitem(b'badserver', b'closeafterrecvbytes',
51 configitem(b'badserver', b'closeafterrecvbytes',
51 default=b'0',
52 default=b'0',
52 )
53 )
53 configitem(b'badserver', b'closeaftersendbytes',
54 configitem(b'badserver', b'closeaftersendbytes',
54 default=b'0',
55 default=b'0',
55 )
56 )
56 configitem(b'badserver', b'closebeforeaccept',
57 configitem(b'badserver', b'closebeforeaccept',
57 default=False,
58 default=False,
58 )
59 )
59
60
60 # We can't adjust __class__ on a socket instance. So we define a proxy type.
61 # We can't adjust __class__ on a socket instance. So we define a proxy type.
61 class socketproxy(object):
62 class socketproxy(object):
62 __slots__ = (
63 __slots__ = (
63 '_orig',
64 '_orig',
64 '_logfp',
65 '_logfp',
65 '_closeafterrecvbytes',
66 '_closeafterrecvbytes',
66 '_closeaftersendbytes',
67 '_closeaftersendbytes',
67 )
68 )
68
69
69 def __init__(self, obj, logfp, closeafterrecvbytes=0,
70 def __init__(self, obj, logfp, closeafterrecvbytes=0,
70 closeaftersendbytes=0):
71 closeaftersendbytes=0):
71 object.__setattr__(self, '_orig', obj)
72 object.__setattr__(self, '_orig', obj)
72 object.__setattr__(self, '_logfp', logfp)
73 object.__setattr__(self, '_logfp', logfp)
73 object.__setattr__(self, '_closeafterrecvbytes', closeafterrecvbytes)
74 object.__setattr__(self, '_closeafterrecvbytes', closeafterrecvbytes)
74 object.__setattr__(self, '_closeaftersendbytes', closeaftersendbytes)
75 object.__setattr__(self, '_closeaftersendbytes', closeaftersendbytes)
75
76
76 def __getattribute__(self, name):
77 def __getattribute__(self, name):
77 if name in ('makefile',):
78 if name in ('makefile',):
78 return object.__getattribute__(self, name)
79 return object.__getattribute__(self, name)
79
80
80 return getattr(object.__getattribute__(self, '_orig'), name)
81 return getattr(object.__getattribute__(self, '_orig'), name)
81
82
82 def __delattr__(self, name):
83 def __delattr__(self, name):
83 delattr(object.__getattribute__(self, '_orig'), name)
84 delattr(object.__getattribute__(self, '_orig'), name)
84
85
85 def __setattr__(self, name, value):
86 def __setattr__(self, name, value):
86 setattr(object.__getattribute__(self, '_orig'), name, value)
87 setattr(object.__getattribute__(self, '_orig'), name, value)
87
88
88 def makefile(self, mode, bufsize):
89 def makefile(self, mode, bufsize):
89 f = object.__getattribute__(self, '_orig').makefile(mode, bufsize)
90 f = object.__getattribute__(self, '_orig').makefile(mode, bufsize)
90
91
91 logfp = object.__getattribute__(self, '_logfp')
92 logfp = object.__getattribute__(self, '_logfp')
92 closeafterrecvbytes = object.__getattribute__(self,
93 closeafterrecvbytes = object.__getattribute__(self,
93 '_closeafterrecvbytes')
94 '_closeafterrecvbytes')
94 closeaftersendbytes = object.__getattribute__(self,
95 closeaftersendbytes = object.__getattribute__(self,
95 '_closeaftersendbytes')
96 '_closeaftersendbytes')
96
97
97 return fileobjectproxy(f, logfp,
98 return fileobjectproxy(f, logfp,
98 closeafterrecvbytes=closeafterrecvbytes,
99 closeafterrecvbytes=closeafterrecvbytes,
99 closeaftersendbytes=closeaftersendbytes)
100 closeaftersendbytes=closeaftersendbytes)
100
101
101 # We can't adjust __class__ on socket._fileobject, so define a proxy.
102 # We can't adjust __class__ on socket._fileobject, so define a proxy.
102 class fileobjectproxy(object):
103 class fileobjectproxy(object):
103 __slots__ = (
104 __slots__ = (
104 '_orig',
105 '_orig',
105 '_logfp',
106 '_logfp',
106 '_closeafterrecvbytes',
107 '_closeafterrecvbytes',
107 '_closeaftersendbytes',
108 '_closeaftersendbytes',
108 )
109 )
109
110
110 def __init__(self, obj, logfp, closeafterrecvbytes=0,
111 def __init__(self, obj, logfp, closeafterrecvbytes=0,
111 closeaftersendbytes=0):
112 closeaftersendbytes=0):
112 object.__setattr__(self, '_orig', obj)
113 object.__setattr__(self, '_orig', obj)
113 object.__setattr__(self, '_logfp', logfp)
114 object.__setattr__(self, '_logfp', logfp)
114 object.__setattr__(self, '_closeafterrecvbytes', closeafterrecvbytes)
115 object.__setattr__(self, '_closeafterrecvbytes', closeafterrecvbytes)
115 object.__setattr__(self, '_closeaftersendbytes', closeaftersendbytes)
116 object.__setattr__(self, '_closeaftersendbytes', closeaftersendbytes)
116
117
117 def __getattribute__(self, name):
118 def __getattribute__(self, name):
118 if name in ('read', 'readline', 'write', '_writelog'):
119 if name in ('_close', 'read', 'readline', 'write', '_writelog'):
119 return object.__getattribute__(self, name)
120 return object.__getattribute__(self, name)
120
121
121 return getattr(object.__getattribute__(self, '_orig'), name)
122 return getattr(object.__getattribute__(self, '_orig'), name)
122
123
123 def __delattr__(self, name):
124 def __delattr__(self, name):
124 delattr(object.__getattribute__(self, '_orig'), name)
125 delattr(object.__getattribute__(self, '_orig'), name)
125
126
126 def __setattr__(self, name, value):
127 def __setattr__(self, name, value):
127 setattr(object.__getattribute__(self, '_orig'), name, value)
128 setattr(object.__getattribute__(self, '_orig'), name, value)
128
129
129 def _writelog(self, msg):
130 def _writelog(self, msg):
130 msg = msg.replace(b'\r', b'\\r').replace(b'\n', b'\\n')
131 msg = msg.replace(b'\r', b'\\r').replace(b'\n', b'\\n')
131
132
132 object.__getattribute__(self, '_logfp').write(msg)
133 object.__getattribute__(self, '_logfp').write(msg)
133 object.__getattribute__(self, '_logfp').write(b'\n')
134 object.__getattribute__(self, '_logfp').write(b'\n')
134 object.__getattribute__(self, '_logfp').flush()
135 object.__getattribute__(self, '_logfp').flush()
135
136
137 def _close(self):
138 # Python 3 uses an io.BufferedIO instance. Python 2 uses some file
139 # object wrapper.
140 if pycompat.ispy3:
141 orig = object.__getattribute__(self, '_orig')
142
143 if hasattr(orig, 'raw'):
144 orig.raw._sock.shutdown(socket.SHUT_RDWR)
145 else:
146 self.close()
147 else:
148 self._sock.shutdown(socket.SHUT_RDWR)
149
136 def read(self, size=-1):
150 def read(self, size=-1):
137 remaining = object.__getattribute__(self, '_closeafterrecvbytes')
151 remaining = object.__getattribute__(self, '_closeafterrecvbytes')
138
152
139 # No read limit. Call original function.
153 # No read limit. Call original function.
140 if not remaining:
154 if not remaining:
141 result = object.__getattribute__(self, '_orig').read(size)
155 result = object.__getattribute__(self, '_orig').read(size)
142 self._writelog(b'read(%d) -> (%d) (%s) %s' % (size,
156 self._writelog(b'read(%d) -> (%d) (%s) %s' % (size,
143 len(result),
157 len(result),
144 result))
158 result))
145 return result
159 return result
146
160
147 origsize = size
161 origsize = size
148
162
149 if size < 0:
163 if size < 0:
150 size = remaining
164 size = remaining
151 else:
165 else:
152 size = min(remaining, size)
166 size = min(remaining, size)
153
167
154 result = object.__getattribute__(self, '_orig').read(size)
168 result = object.__getattribute__(self, '_orig').read(size)
155 remaining -= len(result)
169 remaining -= len(result)
156
170
157 self._writelog(b'read(%d from %d) -> (%d) %s' % (
171 self._writelog(b'read(%d from %d) -> (%d) %s' % (
158 size, origsize, len(result), result))
172 size, origsize, len(result), result))
159
173
160 object.__setattr__(self, '_closeafterrecvbytes', remaining)
174 object.__setattr__(self, '_closeafterrecvbytes', remaining)
161
175
162 if remaining <= 0:
176 if remaining <= 0:
163 self._writelog(b'read limit reached, closing socket')
177 self._writelog(b'read limit reached, closing socket')
164 self._sock.close()
178 self._close()
179
165 # This is the easiest way to abort the current request.
180 # This is the easiest way to abort the current request.
166 raise Exception('connection closed after receiving N bytes')
181 raise Exception('connection closed after receiving N bytes')
167
182
168 return result
183 return result
169
184
170 def readline(self, size=-1):
185 def readline(self, size=-1):
171 remaining = object.__getattribute__(self, '_closeafterrecvbytes')
186 remaining = object.__getattribute__(self, '_closeafterrecvbytes')
172
187
173 # No read limit. Call original function.
188 # No read limit. Call original function.
174 if not remaining:
189 if not remaining:
175 result = object.__getattribute__(self, '_orig').readline(size)
190 result = object.__getattribute__(self, '_orig').readline(size)
176 self._writelog(b'readline(%d) -> (%d) %s' % (
191 self._writelog(b'readline(%d) -> (%d) %s' % (
177 size, len(result), result))
192 size, len(result), result))
178 return result
193 return result
179
194
180 origsize = size
195 origsize = size
181
196
182 if size < 0:
197 if size < 0:
183 size = remaining
198 size = remaining
184 else:
199 else:
185 size = min(remaining, size)
200 size = min(remaining, size)
186
201
187 result = object.__getattribute__(self, '_orig').readline(size)
202 result = object.__getattribute__(self, '_orig').readline(size)
188 remaining -= len(result)
203 remaining -= len(result)
189
204
190 self._writelog(b'readline(%d from %d) -> (%d) %s' % (
205 self._writelog(b'readline(%d from %d) -> (%d) %s' % (
191 size, origsize, len(result), result))
206 size, origsize, len(result), result))
192
207
193 object.__setattr__(self, '_closeafterrecvbytes', remaining)
208 object.__setattr__(self, '_closeafterrecvbytes', remaining)
194
209
195 if remaining <= 0:
210 if remaining <= 0:
196 self._writelog(b'read limit reached; closing socket')
211 self._writelog(b'read limit reached; closing socket')
197 self._sock.close()
212 self._close()
213
198 # This is the easiest way to abort the current request.
214 # This is the easiest way to abort the current request.
199 raise Exception('connection closed after receiving N bytes')
215 raise Exception('connection closed after receiving N bytes')
200
216
201 return result
217 return result
202
218
203 def write(self, data):
219 def write(self, data):
204 remaining = object.__getattribute__(self, '_closeaftersendbytes')
220 remaining = object.__getattribute__(self, '_closeaftersendbytes')
205
221
206 # No byte limit on this operation. Call original function.
222 # No byte limit on this operation. Call original function.
207 if not remaining:
223 if not remaining:
208 self._writelog(b'write(%d) -> %s' % (len(data), data))
224 self._writelog(b'write(%d) -> %s' % (len(data), data))
209 result = object.__getattribute__(self, '_orig').write(data)
225 result = object.__getattribute__(self, '_orig').write(data)
210 return result
226 return result
211
227
212 if len(data) > remaining:
228 if len(data) > remaining:
213 newdata = data[0:remaining]
229 newdata = data[0:remaining]
214 else:
230 else:
215 newdata = data
231 newdata = data
216
232
217 remaining -= len(newdata)
233 remaining -= len(newdata)
218
234
219 self._writelog(b'write(%d from %d) -> (%d) %s' % (
235 self._writelog(b'write(%d from %d) -> (%d) %s' % (
220 len(newdata), len(data), remaining, newdata))
236 len(newdata), len(data), remaining, newdata))
221
237
222 result = object.__getattribute__(self, '_orig').write(newdata)
238 result = object.__getattribute__(self, '_orig').write(newdata)
223
239
224 object.__setattr__(self, '_closeaftersendbytes', remaining)
240 object.__setattr__(self, '_closeaftersendbytes', remaining)
225
241
226 if remaining <= 0:
242 if remaining <= 0:
227 self._writelog(b'write limit reached; closing socket')
243 self._writelog(b'write limit reached; closing socket')
228 self._sock.close()
244 self._close()
245
229 raise Exception('connection closed after sending N bytes')
246 raise Exception('connection closed after sending N bytes')
230
247
231 return result
248 return result
232
249
233 def extsetup(ui):
250 def extsetup(ui):
234 # Change the base HTTP server class so various events can be performed.
251 # Change the base HTTP server class so various events can be performed.
235 # See SocketServer.BaseServer for how the specially named methods work.
252 # See SocketServer.BaseServer for how the specially named methods work.
236 class badserver(server.MercurialHTTPServer):
253 class badserver(server.MercurialHTTPServer):
237 def __init__(self, ui, *args, **kwargs):
254 def __init__(self, ui, *args, **kwargs):
238 self._ui = ui
255 self._ui = ui
239 super(badserver, self).__init__(ui, *args, **kwargs)
256 super(badserver, self).__init__(ui, *args, **kwargs)
240
257
241 recvbytes = self._ui.config(b'badserver', b'closeafterrecvbytes')
258 recvbytes = self._ui.config(b'badserver', b'closeafterrecvbytes')
242 recvbytes = recvbytes.split(b',')
259 recvbytes = recvbytes.split(b',')
243 self.closeafterrecvbytes = [int(v) for v in recvbytes if v]
260 self.closeafterrecvbytes = [int(v) for v in recvbytes if v]
244 sendbytes = self._ui.config(b'badserver', b'closeaftersendbytes')
261 sendbytes = self._ui.config(b'badserver', b'closeaftersendbytes')
245 sendbytes = sendbytes.split(b',')
262 sendbytes = sendbytes.split(b',')
246 self.closeaftersendbytes = [int(v) for v in sendbytes if v]
263 self.closeaftersendbytes = [int(v) for v in sendbytes if v]
247
264
248 # Need to inherit object so super() works.
265 # Need to inherit object so super() works.
249 class badrequesthandler(self.RequestHandlerClass, object):
266 class badrequesthandler(self.RequestHandlerClass, object):
250 def send_header(self, name, value):
267 def send_header(self, name, value):
251 # Make headers deterministic to facilitate testing.
268 # Make headers deterministic to facilitate testing.
252 if name.lower() == 'date':
269 if name.lower() == 'date':
253 value = 'Fri, 14 Apr 2017 00:00:00 GMT'
270 value = 'Fri, 14 Apr 2017 00:00:00 GMT'
254 elif name.lower() == 'server':
271 elif name.lower() == 'server':
255 value = 'badhttpserver'
272 value = 'badhttpserver'
256
273
257 return super(badrequesthandler, self).send_header(name,
274 return super(badrequesthandler, self).send_header(name,
258 value)
275 value)
259
276
260 self.RequestHandlerClass = badrequesthandler
277 self.RequestHandlerClass = badrequesthandler
261
278
262 # Called to accept() a pending socket.
279 # Called to accept() a pending socket.
263 def get_request(self):
280 def get_request(self):
264 if self._ui.configbool(b'badserver', b'closebeforeaccept'):
281 if self._ui.configbool(b'badserver', b'closebeforeaccept'):
265 self.socket.close()
282 self.socket.close()
266
283
267 # Tells the server to stop processing more requests.
284 # Tells the server to stop processing more requests.
268 self.__shutdown_request = True
285 self.__shutdown_request = True
269
286
270 # Simulate failure to stop processing this request.
287 # Simulate failure to stop processing this request.
271 raise socket.error('close before accept')
288 raise socket.error('close before accept')
272
289
273 if self._ui.configbool(b'badserver', b'closeafteraccept'):
290 if self._ui.configbool(b'badserver', b'closeafteraccept'):
274 request, client_address = super(badserver, self).get_request()
291 request, client_address = super(badserver, self).get_request()
275 request.close()
292 request.close()
276 raise socket.error('close after accept')
293 raise socket.error('close after accept')
277
294
278 return super(badserver, self).get_request()
295 return super(badserver, self).get_request()
279
296
280 # Does heavy lifting of processing a request. Invokes
297 # Does heavy lifting of processing a request. Invokes
281 # self.finish_request() which calls self.RequestHandlerClass() which
298 # self.finish_request() which calls self.RequestHandlerClass() which
282 # is a hgweb.server._httprequesthandler.
299 # is a hgweb.server._httprequesthandler.
283 def process_request(self, socket, address):
300 def process_request(self, socket, address):
284 # Wrap socket in a proxy if we need to count bytes.
301 # Wrap socket in a proxy if we need to count bytes.
285 if self.closeafterrecvbytes:
302 if self.closeafterrecvbytes:
286 closeafterrecvbytes = self.closeafterrecvbytes.pop(0)
303 closeafterrecvbytes = self.closeafterrecvbytes.pop(0)
287 else:
304 else:
288 closeafterrecvbytes = 0
305 closeafterrecvbytes = 0
289 if self.closeaftersendbytes:
306 if self.closeaftersendbytes:
290 closeaftersendbytes = self.closeaftersendbytes.pop(0)
307 closeaftersendbytes = self.closeaftersendbytes.pop(0)
291 else:
308 else:
292 closeaftersendbytes = 0
309 closeaftersendbytes = 0
293
310
294 if closeafterrecvbytes or closeaftersendbytes:
311 if closeafterrecvbytes or closeaftersendbytes:
295 socket = socketproxy(socket, self.errorlog,
312 socket = socketproxy(socket, self.errorlog,
296 closeafterrecvbytes=closeafterrecvbytes,
313 closeafterrecvbytes=closeafterrecvbytes,
297 closeaftersendbytes=closeaftersendbytes)
314 closeaftersendbytes=closeaftersendbytes)
298
315
299 return super(badserver, self).process_request(socket, address)
316 return super(badserver, self).process_request(socket, address)
300
317
301 server.MercurialHTTPServer = badserver
318 server.MercurialHTTPServer = badserver
General Comments 0
You need to be logged in to leave comments. Login now