##// END OF EJS Templates
tests: constant-fold a `pycompat.ispy3` in testlib/badserverext.py
Manuel Jacob -
r50193:425ca342 default
parent child Browse files
Show More
@@ -1,461 +1,456
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 close-before-accept
17 close-before-accept
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 close-after-accept
21 close-after-accept
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 close-after-recv-bytes
25 close-after-recv-bytes
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 (The value is a list, multiple values can use used to close a series of requests
27 (The value is a list, multiple values can use used to close a series of requests
28 request)
28 request)
29
29
30 close-after-recv-patterns
30 close-after-recv-patterns
31 If defined, the `close-after-recv-bytes` values only start counting after the
31 If defined, the `close-after-recv-bytes` values only start counting after the
32 `read` operation that encountered the defined patterns.
32 `read` operation that encountered the defined patterns.
33 (The value is a list, multiple values can use used to close a series of requests
33 (The value is a list, multiple values can use used to close a series of requests
34 request)
34 request)
35
35
36 close-after-send-bytes
36 close-after-send-bytes
37 If defined, close the client socket after sending this many bytes.
37 If defined, close the client socket after sending this many bytes.
38 (The value is a list, multiple values can use used to close a series of requests
38 (The value is a list, multiple values can use used to close a series of requests
39 request)
39 request)
40
40
41 close-after-send-patterns
41 close-after-send-patterns
42 If defined, close the client socket after the configured regexp is seen.
42 If defined, close the client socket after the configured regexp is seen.
43 (The value is a list, multiple values can use used to close a series of requests
43 (The value is a list, multiple values can use used to close a series of requests
44 request)
44 request)
45 """
45 """
46
46
47
47
48 import re
48 import re
49 import socket
49 import socket
50
50
51 from mercurial import (
51 from mercurial import (
52 pycompat,
53 registrar,
52 registrar,
54 )
53 )
55
54
56 from mercurial.hgweb import server
55 from mercurial.hgweb import server
57
56
58 configtable = {}
57 configtable = {}
59 configitem = registrar.configitem(configtable)
58 configitem = registrar.configitem(configtable)
60
59
61 configitem(
60 configitem(
62 b'badserver',
61 b'badserver',
63 b'close-after-accept',
62 b'close-after-accept',
64 default=False,
63 default=False,
65 )
64 )
66 configitem(
65 configitem(
67 b'badserver',
66 b'badserver',
68 b'close-after-recv-bytes',
67 b'close-after-recv-bytes',
69 default=b'0',
68 default=b'0',
70 )
69 )
71 configitem(
70 configitem(
72 b'badserver',
71 b'badserver',
73 b'close-after-recv-patterns',
72 b'close-after-recv-patterns',
74 default=b'',
73 default=b'',
75 )
74 )
76 configitem(
75 configitem(
77 b'badserver',
76 b'badserver',
78 b'close-after-send-bytes',
77 b'close-after-send-bytes',
79 default=b'0',
78 default=b'0',
80 )
79 )
81 configitem(
80 configitem(
82 b'badserver',
81 b'badserver',
83 b'close-after-send-patterns',
82 b'close-after-send-patterns',
84 default=b'',
83 default=b'',
85 )
84 )
86 configitem(
85 configitem(
87 b'badserver',
86 b'badserver',
88 b'close-before-accept',
87 b'close-before-accept',
89 default=False,
88 default=False,
90 )
89 )
91
90
92
91
93 class ConditionTracker:
92 class ConditionTracker:
94 def __init__(
93 def __init__(
95 self,
94 self,
96 close_after_recv_bytes,
95 close_after_recv_bytes,
97 close_after_recv_patterns,
96 close_after_recv_patterns,
98 close_after_send_bytes,
97 close_after_send_bytes,
99 close_after_send_patterns,
98 close_after_send_patterns,
100 ):
99 ):
101 self._all_close_after_recv_bytes = close_after_recv_bytes
100 self._all_close_after_recv_bytes = close_after_recv_bytes
102 self._all_close_after_recv_patterns = close_after_recv_patterns
101 self._all_close_after_recv_patterns = close_after_recv_patterns
103 self._all_close_after_send_bytes = close_after_send_bytes
102 self._all_close_after_send_bytes = close_after_send_bytes
104 self._all_close_after_send_patterns = close_after_send_patterns
103 self._all_close_after_send_patterns = close_after_send_patterns
105
104
106 self.target_recv_bytes = None
105 self.target_recv_bytes = None
107 self.remaining_recv_bytes = None
106 self.remaining_recv_bytes = None
108 self.recv_patterns = None
107 self.recv_patterns = None
109 self.recv_data = b''
108 self.recv_data = b''
110 self.target_send_bytes = None
109 self.target_send_bytes = None
111 self.remaining_send_bytes = None
110 self.remaining_send_bytes = None
112 self.send_pattern = None
111 self.send_pattern = None
113 self.send_data = b''
112 self.send_data = b''
114
113
115 def start_next_request(self):
114 def start_next_request(self):
116 """move to the next set of close condition"""
115 """move to the next set of close condition"""
117 if self._all_close_after_recv_bytes:
116 if self._all_close_after_recv_bytes:
118 self.target_recv_bytes = self._all_close_after_recv_bytes.pop(0)
117 self.target_recv_bytes = self._all_close_after_recv_bytes.pop(0)
119 self.remaining_recv_bytes = self.target_recv_bytes
118 self.remaining_recv_bytes = self.target_recv_bytes
120 else:
119 else:
121 self.target_recv_bytes = None
120 self.target_recv_bytes = None
122 self.remaining_recv_bytes = None
121 self.remaining_recv_bytes = None
123
122
124 self.recv_data = b''
123 self.recv_data = b''
125 if self._all_close_after_recv_patterns:
124 if self._all_close_after_recv_patterns:
126 self.recv_pattern = self._all_close_after_recv_patterns.pop(0)
125 self.recv_pattern = self._all_close_after_recv_patterns.pop(0)
127 else:
126 else:
128 self.recv_pattern = None
127 self.recv_pattern = None
129
128
130 if self._all_close_after_send_bytes:
129 if self._all_close_after_send_bytes:
131 self.target_send_bytes = self._all_close_after_send_bytes.pop(0)
130 self.target_send_bytes = self._all_close_after_send_bytes.pop(0)
132 self.remaining_send_bytes = self.target_send_bytes
131 self.remaining_send_bytes = self.target_send_bytes
133 else:
132 else:
134 self.target_send_bytes = None
133 self.target_send_bytes = None
135 self.remaining_send_bytes = None
134 self.remaining_send_bytes = None
136
135
137 self.send_data = b''
136 self.send_data = b''
138 if self._all_close_after_send_patterns:
137 if self._all_close_after_send_patterns:
139 self.send_pattern = self._all_close_after_send_patterns.pop(0)
138 self.send_pattern = self._all_close_after_send_patterns.pop(0)
140 else:
139 else:
141 self.send_pattern = None
140 self.send_pattern = None
142
141
143 def might_close(self):
142 def might_close(self):
144 """True, if any processing will be needed"""
143 """True, if any processing will be needed"""
145 if self.remaining_recv_bytes is not None:
144 if self.remaining_recv_bytes is not None:
146 return True
145 return True
147 if self.recv_pattern is not None:
146 if self.recv_pattern is not None:
148 return True
147 return True
149 if self.remaining_send_bytes is not None:
148 if self.remaining_send_bytes is not None:
150 return True
149 return True
151 if self.send_pattern is not None:
150 if self.send_pattern is not None:
152 return True
151 return True
153 return False
152 return False
154
153
155 def forward_write(self, obj, method, data, *args, **kwargs):
154 def forward_write(self, obj, method, data, *args, **kwargs):
156 """call an underlying write function until condition are met
155 """call an underlying write function until condition are met
157
156
158 When the condition are met the socket is closed
157 When the condition are met the socket is closed
159 """
158 """
160 remaining = self.remaining_send_bytes
159 remaining = self.remaining_send_bytes
161 pattern = self.send_pattern
160 pattern = self.send_pattern
162
161
163 orig = object.__getattribute__(obj, '_orig')
162 orig = object.__getattribute__(obj, '_orig')
164 bmethod = method.encode('ascii')
163 bmethod = method.encode('ascii')
165 func = getattr(orig, method)
164 func = getattr(orig, method)
166
165
167 if pattern:
166 if pattern:
168 self.send_data += data
167 self.send_data += data
169 pieces = pattern.split(self.send_data, maxsplit=1)
168 pieces = pattern.split(self.send_data, maxsplit=1)
170 if len(pieces) > 1:
169 if len(pieces) > 1:
171 dropped = len(pieces[-1])
170 dropped = len(pieces[-1])
172 remaining = len(data) - dropped
171 remaining = len(data) - dropped
173
172
174 if remaining:
173 if remaining:
175 remaining = max(0, remaining)
174 remaining = max(0, remaining)
176
175
177 if not remaining:
176 if not remaining:
178 newdata = data
177 newdata = data
179 else:
178 else:
180 if remaining < len(data):
179 if remaining < len(data):
181 newdata = data[0:remaining]
180 newdata = data[0:remaining]
182 else:
181 else:
183 newdata = data
182 newdata = data
184 remaining -= len(newdata)
183 remaining -= len(newdata)
185 self.remaining_send_bytes = remaining
184 self.remaining_send_bytes = remaining
186
185
187 result = func(newdata, *args, **kwargs)
186 result = func(newdata, *args, **kwargs)
188
187
189 if remaining is None:
188 if remaining is None:
190 obj._writelog(b'%s(%d) -> %s' % (bmethod, len(data), data))
189 obj._writelog(b'%s(%d) -> %s' % (bmethod, len(data), data))
191 else:
190 else:
192 msg = b'%s(%d from %d) -> (%d) %s'
191 msg = b'%s(%d from %d) -> (%d) %s'
193 msg %= (bmethod, len(newdata), len(data), remaining, newdata)
192 msg %= (bmethod, len(newdata), len(data), remaining, newdata)
194 obj._writelog(msg)
193 obj._writelog(msg)
195
194
196 if remaining is not None and remaining <= 0:
195 if remaining is not None and remaining <= 0:
197 obj._writelog(b'write limit reached; closing socket')
196 obj._writelog(b'write limit reached; closing socket')
198 object.__getattribute__(obj, '_cond_close')()
197 object.__getattribute__(obj, '_cond_close')()
199 raise Exception('connection closed after sending N bytes')
198 raise Exception('connection closed after sending N bytes')
200
199
201 return result
200 return result
202
201
203 def forward_read(self, obj, method, size=-1):
202 def forward_read(self, obj, method, size=-1):
204 """call an underlying read function until condition are met
203 """call an underlying read function until condition are met
205
204
206 When the condition are met the socket is closed
205 When the condition are met the socket is closed
207 """
206 """
208 remaining = self.remaining_recv_bytes
207 remaining = self.remaining_recv_bytes
209 pattern = self.recv_pattern
208 pattern = self.recv_pattern
210
209
211 orig = object.__getattribute__(obj, '_orig')
210 orig = object.__getattribute__(obj, '_orig')
212 bmethod = method.encode('ascii')
211 bmethod = method.encode('ascii')
213 func = getattr(orig, method)
212 func = getattr(orig, method)
214
213
215 requested_size = size
214 requested_size = size
216 actual_size = size
215 actual_size = size
217
216
218 if pattern is None and remaining:
217 if pattern is None and remaining:
219 if size < 0:
218 if size < 0:
220 actual_size = remaining
219 actual_size = remaining
221 else:
220 else:
222 actual_size = min(remaining, requested_size)
221 actual_size = min(remaining, requested_size)
223
222
224 result = func(actual_size)
223 result = func(actual_size)
225
224
226 if pattern is None and remaining:
225 if pattern is None and remaining:
227 remaining -= len(result)
226 remaining -= len(result)
228 self.remaining_recv_bytes = remaining
227 self.remaining_recv_bytes = remaining
229
228
230 if requested_size == 65537:
229 if requested_size == 65537:
231 requested_repr = b'~'
230 requested_repr = b'~'
232 else:
231 else:
233 requested_repr = b'%d' % requested_size
232 requested_repr = b'%d' % requested_size
234 if requested_size == actual_size:
233 if requested_size == actual_size:
235 msg = b'%s(%s) -> (%d) %s'
234 msg = b'%s(%s) -> (%d) %s'
236 msg %= (bmethod, requested_repr, len(result), result)
235 msg %= (bmethod, requested_repr, len(result), result)
237 else:
236 else:
238 msg = b'%s(%d from %s) -> (%d) %s'
237 msg = b'%s(%d from %s) -> (%d) %s'
239 msg %= (bmethod, actual_size, requested_repr, len(result), result)
238 msg %= (bmethod, actual_size, requested_repr, len(result), result)
240 obj._writelog(msg)
239 obj._writelog(msg)
241
240
242 if pattern is not None:
241 if pattern is not None:
243 self.recv_data += result
242 self.recv_data += result
244 if pattern.search(self.recv_data):
243 if pattern.search(self.recv_data):
245 # start counting bytes starting with the next read
244 # start counting bytes starting with the next read
246 self.recv_pattern = None
245 self.recv_pattern = None
247
246
248 if remaining is not None and remaining <= 0:
247 if remaining is not None and remaining <= 0:
249 obj._writelog(b'read limit reached; closing socket')
248 obj._writelog(b'read limit reached; closing socket')
250 obj._cond_close()
249 obj._cond_close()
251
250
252 # This is the easiest way to abort the current request.
251 # This is the easiest way to abort the current request.
253 raise Exception('connection closed after receiving N bytes')
252 raise Exception('connection closed after receiving N bytes')
254
253
255 return result
254 return result
256
255
257
256
258 # We can't adjust __class__ on a socket instance. So we define a proxy type.
257 # We can't adjust __class__ on a socket instance. So we define a proxy type.
259 class socketproxy:
258 class socketproxy:
260 __slots__ = ('_orig', '_logfp', '_cond')
259 __slots__ = ('_orig', '_logfp', '_cond')
261
260
262 def __init__(self, obj, logfp, condition_tracked):
261 def __init__(self, obj, logfp, condition_tracked):
263 object.__setattr__(self, '_orig', obj)
262 object.__setattr__(self, '_orig', obj)
264 object.__setattr__(self, '_logfp', logfp)
263 object.__setattr__(self, '_logfp', logfp)
265 object.__setattr__(self, '_cond', condition_tracked)
264 object.__setattr__(self, '_cond', condition_tracked)
266
265
267 def __getattribute__(self, name):
266 def __getattribute__(self, name):
268 if name in ('makefile', 'sendall', '_writelog', '_cond_close'):
267 if name in ('makefile', 'sendall', '_writelog', '_cond_close'):
269 return object.__getattribute__(self, name)
268 return object.__getattribute__(self, name)
270
269
271 return getattr(object.__getattribute__(self, '_orig'), name)
270 return getattr(object.__getattribute__(self, '_orig'), name)
272
271
273 def __delattr__(self, name):
272 def __delattr__(self, name):
274 delattr(object.__getattribute__(self, '_orig'), name)
273 delattr(object.__getattribute__(self, '_orig'), name)
275
274
276 def __setattr__(self, name, value):
275 def __setattr__(self, name, value):
277 setattr(object.__getattribute__(self, '_orig'), name, value)
276 setattr(object.__getattribute__(self, '_orig'), name, value)
278
277
279 def _writelog(self, msg):
278 def _writelog(self, msg):
280 msg = msg.replace(b'\r', b'\\r').replace(b'\n', b'\\n')
279 msg = msg.replace(b'\r', b'\\r').replace(b'\n', b'\\n')
281
280
282 object.__getattribute__(self, '_logfp').write(msg)
281 object.__getattribute__(self, '_logfp').write(msg)
283 object.__getattribute__(self, '_logfp').write(b'\n')
282 object.__getattribute__(self, '_logfp').write(b'\n')
284 object.__getattribute__(self, '_logfp').flush()
283 object.__getattribute__(self, '_logfp').flush()
285
284
286 def makefile(self, mode, bufsize):
285 def makefile(self, mode, bufsize):
287 f = object.__getattribute__(self, '_orig').makefile(mode, bufsize)
286 f = object.__getattribute__(self, '_orig').makefile(mode, bufsize)
288
287
289 logfp = object.__getattribute__(self, '_logfp')
288 logfp = object.__getattribute__(self, '_logfp')
290 cond = object.__getattribute__(self, '_cond')
289 cond = object.__getattribute__(self, '_cond')
291
290
292 return fileobjectproxy(f, logfp, cond)
291 return fileobjectproxy(f, logfp, cond)
293
292
294 def sendall(self, data, flags=0):
293 def sendall(self, data, flags=0):
295 cond = object.__getattribute__(self, '_cond')
294 cond = object.__getattribute__(self, '_cond')
296 return cond.forward_write(self, 'sendall', data, flags)
295 return cond.forward_write(self, 'sendall', data, flags)
297
296
298 def _cond_close(self):
297 def _cond_close(self):
299 object.__getattribute__(self, '_orig').shutdown(socket.SHUT_RDWR)
298 object.__getattribute__(self, '_orig').shutdown(socket.SHUT_RDWR)
300
299
301
300
302 # We can't adjust __class__ on socket._fileobject, so define a proxy.
301 # We can't adjust __class__ on socket._fileobject, so define a proxy.
303 class fileobjectproxy:
302 class fileobjectproxy:
304 __slots__ = ('_orig', '_logfp', '_cond')
303 __slots__ = ('_orig', '_logfp', '_cond')
305
304
306 def __init__(self, obj, logfp, condition_tracked):
305 def __init__(self, obj, logfp, condition_tracked):
307 object.__setattr__(self, '_orig', obj)
306 object.__setattr__(self, '_orig', obj)
308 object.__setattr__(self, '_logfp', logfp)
307 object.__setattr__(self, '_logfp', logfp)
309 object.__setattr__(self, '_cond', condition_tracked)
308 object.__setattr__(self, '_cond', condition_tracked)
310
309
311 def __getattribute__(self, name):
310 def __getattribute__(self, name):
312 if name in (
311 if name in (
313 '_close',
312 '_close',
314 'read',
313 'read',
315 'readline',
314 'readline',
316 'write',
315 'write',
317 '_writelog',
316 '_writelog',
318 '_cond_close',
317 '_cond_close',
319 ):
318 ):
320 return object.__getattribute__(self, name)
319 return object.__getattribute__(self, name)
321
320
322 return getattr(object.__getattribute__(self, '_orig'), name)
321 return getattr(object.__getattribute__(self, '_orig'), name)
323
322
324 def __delattr__(self, name):
323 def __delattr__(self, name):
325 delattr(object.__getattribute__(self, '_orig'), name)
324 delattr(object.__getattribute__(self, '_orig'), name)
326
325
327 def __setattr__(self, name, value):
326 def __setattr__(self, name, value):
328 setattr(object.__getattribute__(self, '_orig'), name, value)
327 setattr(object.__getattribute__(self, '_orig'), name, value)
329
328
330 def _writelog(self, msg):
329 def _writelog(self, msg):
331 msg = msg.replace(b'\r', b'\\r').replace(b'\n', b'\\n')
330 msg = msg.replace(b'\r', b'\\r').replace(b'\n', b'\\n')
332
331
333 object.__getattribute__(self, '_logfp').write(msg)
332 object.__getattribute__(self, '_logfp').write(msg)
334 object.__getattribute__(self, '_logfp').write(b'\n')
333 object.__getattribute__(self, '_logfp').write(b'\n')
335 object.__getattribute__(self, '_logfp').flush()
334 object.__getattribute__(self, '_logfp').flush()
336
335
337 def _close(self):
336 def _close(self):
338 # Python 3 uses an io.BufferedIO instance. Python 2 uses some file
337 # We wrap an io.BufferedIO instance.
339 # object wrapper.
338 orig = object.__getattribute__(self, '_orig')
340 if pycompat.ispy3:
341 orig = object.__getattribute__(self, '_orig')
342
339
343 if hasattr(orig, 'raw'):
340 if hasattr(orig, 'raw'):
344 orig.raw._sock.shutdown(socket.SHUT_RDWR)
341 orig.raw._sock.shutdown(socket.SHUT_RDWR)
345 else:
346 self.close()
347 else:
342 else:
348 self._sock.shutdown(socket.SHUT_RDWR)
343 self.close()
349
344
350 def read(self, size=-1):
345 def read(self, size=-1):
351 cond = object.__getattribute__(self, '_cond')
346 cond = object.__getattribute__(self, '_cond')
352 return cond.forward_read(self, 'read', size)
347 return cond.forward_read(self, 'read', size)
353
348
354 def readline(self, size=-1):
349 def readline(self, size=-1):
355 cond = object.__getattribute__(self, '_cond')
350 cond = object.__getattribute__(self, '_cond')
356 return cond.forward_read(self, 'readline', size)
351 return cond.forward_read(self, 'readline', size)
357
352
358 def write(self, data):
353 def write(self, data):
359 cond = object.__getattribute__(self, '_cond')
354 cond = object.__getattribute__(self, '_cond')
360 return cond.forward_write(self, 'write', data)
355 return cond.forward_write(self, 'write', data)
361
356
362 def _cond_close(self):
357 def _cond_close(self):
363 self._close()
358 self._close()
364
359
365
360
366 def process_bytes_config(value):
361 def process_bytes_config(value):
367 parts = value.split(b',')
362 parts = value.split(b',')
368 integers = [int(v) for v in parts if v]
363 integers = [int(v) for v in parts if v]
369 return [v if v else None for v in integers]
364 return [v if v else None for v in integers]
370
365
371
366
372 def process_pattern_config(value):
367 def process_pattern_config(value):
373 patterns = []
368 patterns = []
374 for p in value.split(b','):
369 for p in value.split(b','):
375 if not p:
370 if not p:
376 p = None
371 p = None
377 else:
372 else:
378 p = re.compile(p, re.DOTALL | re.MULTILINE)
373 p = re.compile(p, re.DOTALL | re.MULTILINE)
379 patterns.append(p)
374 patterns.append(p)
380 return patterns
375 return patterns
381
376
382
377
383 def extsetup(ui):
378 def extsetup(ui):
384 # Change the base HTTP server class so various events can be performed.
379 # Change the base HTTP server class so various events can be performed.
385 # See SocketServer.BaseServer for how the specially named methods work.
380 # See SocketServer.BaseServer for how the specially named methods work.
386 class badserver(server.MercurialHTTPServer):
381 class badserver(server.MercurialHTTPServer):
387 def __init__(self, ui, *args, **kwargs):
382 def __init__(self, ui, *args, **kwargs):
388 self._ui = ui
383 self._ui = ui
389 super(badserver, self).__init__(ui, *args, **kwargs)
384 super(badserver, self).__init__(ui, *args, **kwargs)
390
385
391 all_recv_bytes = self._ui.config(
386 all_recv_bytes = self._ui.config(
392 b'badserver', b'close-after-recv-bytes'
387 b'badserver', b'close-after-recv-bytes'
393 )
388 )
394 all_recv_bytes = process_bytes_config(all_recv_bytes)
389 all_recv_bytes = process_bytes_config(all_recv_bytes)
395 all_recv_pattern = self._ui.config(
390 all_recv_pattern = self._ui.config(
396 b'badserver', b'close-after-recv-patterns'
391 b'badserver', b'close-after-recv-patterns'
397 )
392 )
398 all_recv_pattern = process_pattern_config(all_recv_pattern)
393 all_recv_pattern = process_pattern_config(all_recv_pattern)
399 all_send_bytes = self._ui.config(
394 all_send_bytes = self._ui.config(
400 b'badserver', b'close-after-send-bytes'
395 b'badserver', b'close-after-send-bytes'
401 )
396 )
402 all_send_bytes = process_bytes_config(all_send_bytes)
397 all_send_bytes = process_bytes_config(all_send_bytes)
403 all_send_patterns = self._ui.config(
398 all_send_patterns = self._ui.config(
404 b'badserver', b'close-after-send-patterns'
399 b'badserver', b'close-after-send-patterns'
405 )
400 )
406 all_send_patterns = process_pattern_config(all_send_patterns)
401 all_send_patterns = process_pattern_config(all_send_patterns)
407 self._cond = ConditionTracker(
402 self._cond = ConditionTracker(
408 all_recv_bytes,
403 all_recv_bytes,
409 all_recv_pattern,
404 all_recv_pattern,
410 all_send_bytes,
405 all_send_bytes,
411 all_send_patterns,
406 all_send_patterns,
412 )
407 )
413
408
414 # Need to inherit object so super() works.
409 # Need to inherit object so super() works.
415 class badrequesthandler(self.RequestHandlerClass, object):
410 class badrequesthandler(self.RequestHandlerClass, object):
416 def send_header(self, name, value):
411 def send_header(self, name, value):
417 # Make headers deterministic to facilitate testing.
412 # Make headers deterministic to facilitate testing.
418 if name.lower() == 'date':
413 if name.lower() == 'date':
419 value = 'Fri, 14 Apr 2017 00:00:00 GMT'
414 value = 'Fri, 14 Apr 2017 00:00:00 GMT'
420 elif name.lower() == 'server':
415 elif name.lower() == 'server':
421 value = 'badhttpserver'
416 value = 'badhttpserver'
422
417
423 return super(badrequesthandler, self).send_header(
418 return super(badrequesthandler, self).send_header(
424 name, value
419 name, value
425 )
420 )
426
421
427 self.RequestHandlerClass = badrequesthandler
422 self.RequestHandlerClass = badrequesthandler
428
423
429 # Called to accept() a pending socket.
424 # Called to accept() a pending socket.
430 def get_request(self):
425 def get_request(self):
431 if self._ui.configbool(b'badserver', b'close-before-accept'):
426 if self._ui.configbool(b'badserver', b'close-before-accept'):
432 self.socket.close()
427 self.socket.close()
433
428
434 # Tells the server to stop processing more requests.
429 # Tells the server to stop processing more requests.
435 self.__shutdown_request = True
430 self.__shutdown_request = True
436
431
437 # Simulate failure to stop processing this request.
432 # Simulate failure to stop processing this request.
438 raise socket.error('close before accept')
433 raise socket.error('close before accept')
439
434
440 if self._ui.configbool(b'badserver', b'close-after-accept'):
435 if self._ui.configbool(b'badserver', b'close-after-accept'):
441 request, client_address = super(badserver, self).get_request()
436 request, client_address = super(badserver, self).get_request()
442 request.close()
437 request.close()
443 raise socket.error('close after accept')
438 raise socket.error('close after accept')
444
439
445 return super(badserver, self).get_request()
440 return super(badserver, self).get_request()
446
441
447 # Does heavy lifting of processing a request. Invokes
442 # Does heavy lifting of processing a request. Invokes
448 # self.finish_request() which calls self.RequestHandlerClass() which
443 # self.finish_request() which calls self.RequestHandlerClass() which
449 # is a hgweb.server._httprequesthandler.
444 # is a hgweb.server._httprequesthandler.
450 def process_request(self, socket, address):
445 def process_request(self, socket, address):
451 # Wrap socket in a proxy if we need to count bytes.
446 # Wrap socket in a proxy if we need to count bytes.
452 self._cond.start_next_request()
447 self._cond.start_next_request()
453
448
454 if self._cond.might_close():
449 if self._cond.might_close():
455 socket = socketproxy(
450 socket = socketproxy(
456 socket, self.errorlog, condition_tracked=self._cond
451 socket, self.errorlog, condition_tracked=self._cond
457 )
452 )
458
453
459 return super(badserver, self).process_request(socket, address)
454 return super(badserver, self).process_request(socket, address)
460
455
461 server.MercurialHTTPServer = badserver
456 server.MercurialHTTPServer = badserver
General Comments 0
You need to be logged in to leave comments. Login now