##// END OF EJS Templates
tests: add b'' prefixes to badserverext.py...
Gregory Szorc -
r41604:ba729816 default
parent child Browse files
Show More
@@ -1,301 +1,301 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 registrar,
37 registrar,
38 )
38 )
39
39
40 from mercurial.hgweb import (
40 from mercurial.hgweb import (
41 server,
41 server,
42 )
42 )
43
43
44 configtable = {}
44 configtable = {}
45 configitem = registrar.configitem(configtable)
45 configitem = registrar.configitem(configtable)
46
46
47 configitem(b'badserver', b'closeafteraccept',
47 configitem(b'badserver', b'closeafteraccept',
48 default=False,
48 default=False,
49 )
49 )
50 configitem(b'badserver', b'closeafterrecvbytes',
50 configitem(b'badserver', b'closeafterrecvbytes',
51 default='0',
51 default=b'0',
52 )
52 )
53 configitem(b'badserver', b'closeaftersendbytes',
53 configitem(b'badserver', b'closeaftersendbytes',
54 default='0',
54 default=b'0',
55 )
55 )
56 configitem(b'badserver', b'closebeforeaccept',
56 configitem(b'badserver', b'closebeforeaccept',
57 default=False,
57 default=False,
58 )
58 )
59
59
60 # We can't adjust __class__ on a socket instance. So we define a proxy type.
60 # We can't adjust __class__ on a socket instance. So we define a proxy type.
61 class socketproxy(object):
61 class socketproxy(object):
62 __slots__ = (
62 __slots__ = (
63 '_orig',
63 '_orig',
64 '_logfp',
64 '_logfp',
65 '_closeafterrecvbytes',
65 '_closeafterrecvbytes',
66 '_closeaftersendbytes',
66 '_closeaftersendbytes',
67 )
67 )
68
68
69 def __init__(self, obj, logfp, closeafterrecvbytes=0,
69 def __init__(self, obj, logfp, closeafterrecvbytes=0,
70 closeaftersendbytes=0):
70 closeaftersendbytes=0):
71 object.__setattr__(self, '_orig', obj)
71 object.__setattr__(self, '_orig', obj)
72 object.__setattr__(self, '_logfp', logfp)
72 object.__setattr__(self, '_logfp', logfp)
73 object.__setattr__(self, '_closeafterrecvbytes', closeafterrecvbytes)
73 object.__setattr__(self, '_closeafterrecvbytes', closeafterrecvbytes)
74 object.__setattr__(self, '_closeaftersendbytes', closeaftersendbytes)
74 object.__setattr__(self, '_closeaftersendbytes', closeaftersendbytes)
75
75
76 def __getattribute__(self, name):
76 def __getattribute__(self, name):
77 if name in ('makefile',):
77 if name in ('makefile',):
78 return object.__getattribute__(self, name)
78 return object.__getattribute__(self, name)
79
79
80 return getattr(object.__getattribute__(self, '_orig'), name)
80 return getattr(object.__getattribute__(self, '_orig'), name)
81
81
82 def __delattr__(self, name):
82 def __delattr__(self, name):
83 delattr(object.__getattribute__(self, '_orig'), name)
83 delattr(object.__getattribute__(self, '_orig'), name)
84
84
85 def __setattr__(self, name, value):
85 def __setattr__(self, name, value):
86 setattr(object.__getattribute__(self, '_orig'), name, value)
86 setattr(object.__getattribute__(self, '_orig'), name, value)
87
87
88 def makefile(self, mode, bufsize):
88 def makefile(self, mode, bufsize):
89 f = object.__getattribute__(self, '_orig').makefile(mode, bufsize)
89 f = object.__getattribute__(self, '_orig').makefile(mode, bufsize)
90
90
91 logfp = object.__getattribute__(self, '_logfp')
91 logfp = object.__getattribute__(self, '_logfp')
92 closeafterrecvbytes = object.__getattribute__(self,
92 closeafterrecvbytes = object.__getattribute__(self,
93 '_closeafterrecvbytes')
93 '_closeafterrecvbytes')
94 closeaftersendbytes = object.__getattribute__(self,
94 closeaftersendbytes = object.__getattribute__(self,
95 '_closeaftersendbytes')
95 '_closeaftersendbytes')
96
96
97 return fileobjectproxy(f, logfp,
97 return fileobjectproxy(f, logfp,
98 closeafterrecvbytes=closeafterrecvbytes,
98 closeafterrecvbytes=closeafterrecvbytes,
99 closeaftersendbytes=closeaftersendbytes)
99 closeaftersendbytes=closeaftersendbytes)
100
100
101 # We can't adjust __class__ on socket._fileobject, so define a proxy.
101 # We can't adjust __class__ on socket._fileobject, so define a proxy.
102 class fileobjectproxy(object):
102 class fileobjectproxy(object):
103 __slots__ = (
103 __slots__ = (
104 '_orig',
104 '_orig',
105 '_logfp',
105 '_logfp',
106 '_closeafterrecvbytes',
106 '_closeafterrecvbytes',
107 '_closeaftersendbytes',
107 '_closeaftersendbytes',
108 )
108 )
109
109
110 def __init__(self, obj, logfp, closeafterrecvbytes=0,
110 def __init__(self, obj, logfp, closeafterrecvbytes=0,
111 closeaftersendbytes=0):
111 closeaftersendbytes=0):
112 object.__setattr__(self, '_orig', obj)
112 object.__setattr__(self, '_orig', obj)
113 object.__setattr__(self, '_logfp', logfp)
113 object.__setattr__(self, '_logfp', logfp)
114 object.__setattr__(self, '_closeafterrecvbytes', closeafterrecvbytes)
114 object.__setattr__(self, '_closeafterrecvbytes', closeafterrecvbytes)
115 object.__setattr__(self, '_closeaftersendbytes', closeaftersendbytes)
115 object.__setattr__(self, '_closeaftersendbytes', closeaftersendbytes)
116
116
117 def __getattribute__(self, name):
117 def __getattribute__(self, name):
118 if name in ('read', 'readline', 'write', '_writelog'):
118 if name in ('read', 'readline', 'write', '_writelog'):
119 return object.__getattribute__(self, name)
119 return object.__getattribute__(self, name)
120
120
121 return getattr(object.__getattribute__(self, '_orig'), name)
121 return getattr(object.__getattribute__(self, '_orig'), name)
122
122
123 def __delattr__(self, name):
123 def __delattr__(self, name):
124 delattr(object.__getattribute__(self, '_orig'), name)
124 delattr(object.__getattribute__(self, '_orig'), name)
125
125
126 def __setattr__(self, name, value):
126 def __setattr__(self, name, value):
127 setattr(object.__getattribute__(self, '_orig'), name, value)
127 setattr(object.__getattribute__(self, '_orig'), name, value)
128
128
129 def _writelog(self, msg):
129 def _writelog(self, msg):
130 msg = msg.replace('\r', '\\r').replace('\n', '\\n')
130 msg = msg.replace(b'\r', b'\\r').replace(b'\n', b'\\n')
131
131
132 object.__getattribute__(self, '_logfp').write(msg)
132 object.__getattribute__(self, '_logfp').write(msg)
133 object.__getattribute__(self, '_logfp').write('\n')
133 object.__getattribute__(self, '_logfp').write(b'\n')
134 object.__getattribute__(self, '_logfp').flush()
134 object.__getattribute__(self, '_logfp').flush()
135
135
136 def read(self, size=-1):
136 def read(self, size=-1):
137 remaining = object.__getattribute__(self, '_closeafterrecvbytes')
137 remaining = object.__getattribute__(self, '_closeafterrecvbytes')
138
138
139 # No read limit. Call original function.
139 # No read limit. Call original function.
140 if not remaining:
140 if not remaining:
141 result = object.__getattribute__(self, '_orig').read(size)
141 result = object.__getattribute__(self, '_orig').read(size)
142 self._writelog('read(%d) -> (%d) (%s) %s' % (size,
142 self._writelog(b'read(%d) -> (%d) (%s) %s' % (size,
143 len(result),
143 len(result),
144 result))
144 result))
145 return result
145 return result
146
146
147 origsize = size
147 origsize = size
148
148
149 if size < 0:
149 if size < 0:
150 size = remaining
150 size = remaining
151 else:
151 else:
152 size = min(remaining, size)
152 size = min(remaining, size)
153
153
154 result = object.__getattribute__(self, '_orig').read(size)
154 result = object.__getattribute__(self, '_orig').read(size)
155 remaining -= len(result)
155 remaining -= len(result)
156
156
157 self._writelog('read(%d from %d) -> (%d) %s' % (
157 self._writelog(b'read(%d from %d) -> (%d) %s' % (
158 size, origsize, len(result), result))
158 size, origsize, len(result), result))
159
159
160 object.__setattr__(self, '_closeafterrecvbytes', remaining)
160 object.__setattr__(self, '_closeafterrecvbytes', remaining)
161
161
162 if remaining <= 0:
162 if remaining <= 0:
163 self._writelog('read limit reached, closing socket')
163 self._writelog(b'read limit reached, closing socket')
164 self._sock.close()
164 self._sock.close()
165 # This is the easiest way to abort the current request.
165 # This is the easiest way to abort the current request.
166 raise Exception('connection closed after receiving N bytes')
166 raise Exception('connection closed after receiving N bytes')
167
167
168 return result
168 return result
169
169
170 def readline(self, size=-1):
170 def readline(self, size=-1):
171 remaining = object.__getattribute__(self, '_closeafterrecvbytes')
171 remaining = object.__getattribute__(self, '_closeafterrecvbytes')
172
172
173 # No read limit. Call original function.
173 # No read limit. Call original function.
174 if not remaining:
174 if not remaining:
175 result = object.__getattribute__(self, '_orig').readline(size)
175 result = object.__getattribute__(self, '_orig').readline(size)
176 self._writelog('readline(%d) -> (%d) %s' % (
176 self._writelog(b'readline(%d) -> (%d) %s' % (
177 size, len(result), result))
177 size, len(result), result))
178 return result
178 return result
179
179
180 origsize = size
180 origsize = size
181
181
182 if size < 0:
182 if size < 0:
183 size = remaining
183 size = remaining
184 else:
184 else:
185 size = min(remaining, size)
185 size = min(remaining, size)
186
186
187 result = object.__getattribute__(self, '_orig').readline(size)
187 result = object.__getattribute__(self, '_orig').readline(size)
188 remaining -= len(result)
188 remaining -= len(result)
189
189
190 self._writelog('readline(%d from %d) -> (%d) %s' % (
190 self._writelog(b'readline(%d from %d) -> (%d) %s' % (
191 size, origsize, len(result), result))
191 size, origsize, len(result), result))
192
192
193 object.__setattr__(self, '_closeafterrecvbytes', remaining)
193 object.__setattr__(self, '_closeafterrecvbytes', remaining)
194
194
195 if remaining <= 0:
195 if remaining <= 0:
196 self._writelog('read limit reached; closing socket')
196 self._writelog(b'read limit reached; closing socket')
197 self._sock.close()
197 self._sock.close()
198 # This is the easiest way to abort the current request.
198 # This is the easiest way to abort the current request.
199 raise Exception('connection closed after receiving N bytes')
199 raise Exception('connection closed after receiving N bytes')
200
200
201 return result
201 return result
202
202
203 def write(self, data):
203 def write(self, data):
204 remaining = object.__getattribute__(self, '_closeaftersendbytes')
204 remaining = object.__getattribute__(self, '_closeaftersendbytes')
205
205
206 # No byte limit on this operation. Call original function.
206 # No byte limit on this operation. Call original function.
207 if not remaining:
207 if not remaining:
208 self._writelog('write(%d) -> %s' % (len(data), data))
208 self._writelog(b'write(%d) -> %s' % (len(data), data))
209 result = object.__getattribute__(self, '_orig').write(data)
209 result = object.__getattribute__(self, '_orig').write(data)
210 return result
210 return result
211
211
212 if len(data) > remaining:
212 if len(data) > remaining:
213 newdata = data[0:remaining]
213 newdata = data[0:remaining]
214 else:
214 else:
215 newdata = data
215 newdata = data
216
216
217 remaining -= len(newdata)
217 remaining -= len(newdata)
218
218
219 self._writelog('write(%d from %d) -> (%d) %s' % (
219 self._writelog(b'write(%d from %d) -> (%d) %s' % (
220 len(newdata), len(data), remaining, newdata))
220 len(newdata), len(data), remaining, newdata))
221
221
222 result = object.__getattribute__(self, '_orig').write(newdata)
222 result = object.__getattribute__(self, '_orig').write(newdata)
223
223
224 object.__setattr__(self, '_closeaftersendbytes', remaining)
224 object.__setattr__(self, '_closeaftersendbytes', remaining)
225
225
226 if remaining <= 0:
226 if remaining <= 0:
227 self._writelog('write limit reached; closing socket')
227 self._writelog(b'write limit reached; closing socket')
228 self._sock.close()
228 self._sock.close()
229 raise Exception('connection closed after sending N bytes')
229 raise Exception('connection closed after sending N bytes')
230
230
231 return result
231 return result
232
232
233 def extsetup(ui):
233 def extsetup(ui):
234 # Change the base HTTP server class so various events can be performed.
234 # Change the base HTTP server class so various events can be performed.
235 # See SocketServer.BaseServer for how the specially named methods work.
235 # See SocketServer.BaseServer for how the specially named methods work.
236 class badserver(server.MercurialHTTPServer):
236 class badserver(server.MercurialHTTPServer):
237 def __init__(self, ui, *args, **kwargs):
237 def __init__(self, ui, *args, **kwargs):
238 self._ui = ui
238 self._ui = ui
239 super(badserver, self).__init__(ui, *args, **kwargs)
239 super(badserver, self).__init__(ui, *args, **kwargs)
240
240
241 recvbytes = self._ui.config(b'badserver', b'closeafterrecvbytes')
241 recvbytes = self._ui.config(b'badserver', b'closeafterrecvbytes')
242 recvbytes = recvbytes.split(',')
242 recvbytes = recvbytes.split(b',')
243 self.closeafterrecvbytes = [int(v) for v in recvbytes if v]
243 self.closeafterrecvbytes = [int(v) for v in recvbytes if v]
244 sendbytes = self._ui.config(b'badserver', b'closeaftersendbytes')
244 sendbytes = self._ui.config(b'badserver', b'closeaftersendbytes')
245 sendbytes = sendbytes.split(',')
245 sendbytes = sendbytes.split(b',')
246 self.closeaftersendbytes = [int(v) for v in sendbytes if v]
246 self.closeaftersendbytes = [int(v) for v in sendbytes if v]
247
247
248 # Need to inherit object so super() works.
248 # Need to inherit object so super() works.
249 class badrequesthandler(self.RequestHandlerClass, object):
249 class badrequesthandler(self.RequestHandlerClass, object):
250 def send_header(self, name, value):
250 def send_header(self, name, value):
251 # Make headers deterministic to facilitate testing.
251 # Make headers deterministic to facilitate testing.
252 if name.lower() == 'date':
252 if name.lower() == 'date':
253 value = 'Fri, 14 Apr 2017 00:00:00 GMT'
253 value = 'Fri, 14 Apr 2017 00:00:00 GMT'
254 elif name.lower() == 'server':
254 elif name.lower() == 'server':
255 value = 'badhttpserver'
255 value = 'badhttpserver'
256
256
257 return super(badrequesthandler, self).send_header(name,
257 return super(badrequesthandler, self).send_header(name,
258 value)
258 value)
259
259
260 self.RequestHandlerClass = badrequesthandler
260 self.RequestHandlerClass = badrequesthandler
261
261
262 # Called to accept() a pending socket.
262 # Called to accept() a pending socket.
263 def get_request(self):
263 def get_request(self):
264 if self._ui.configbool(b'badserver', b'closebeforeaccept'):
264 if self._ui.configbool(b'badserver', b'closebeforeaccept'):
265 self.socket.close()
265 self.socket.close()
266
266
267 # Tells the server to stop processing more requests.
267 # Tells the server to stop processing more requests.
268 self.__shutdown_request = True
268 self.__shutdown_request = True
269
269
270 # Simulate failure to stop processing this request.
270 # Simulate failure to stop processing this request.
271 raise socket.error('close before accept')
271 raise socket.error('close before accept')
272
272
273 if self._ui.configbool('badserver', 'closeafteraccept'):
273 if self._ui.configbool(b'badserver', b'closeafteraccept'):
274 request, client_address = super(badserver, self).get_request()
274 request, client_address = super(badserver, self).get_request()
275 request.close()
275 request.close()
276 raise socket.error('close after accept')
276 raise socket.error('close after accept')
277
277
278 return super(badserver, self).get_request()
278 return super(badserver, self).get_request()
279
279
280 # Does heavy lifting of processing a request. Invokes
280 # Does heavy lifting of processing a request. Invokes
281 # self.finish_request() which calls self.RequestHandlerClass() which
281 # self.finish_request() which calls self.RequestHandlerClass() which
282 # is a hgweb.server._httprequesthandler.
282 # is a hgweb.server._httprequesthandler.
283 def process_request(self, socket, address):
283 def process_request(self, socket, address):
284 # Wrap socket in a proxy if we need to count bytes.
284 # Wrap socket in a proxy if we need to count bytes.
285 if self.closeafterrecvbytes:
285 if self.closeafterrecvbytes:
286 closeafterrecvbytes = self.closeafterrecvbytes.pop(0)
286 closeafterrecvbytes = self.closeafterrecvbytes.pop(0)
287 else:
287 else:
288 closeafterrecvbytes = 0
288 closeafterrecvbytes = 0
289 if self.closeaftersendbytes:
289 if self.closeaftersendbytes:
290 closeaftersendbytes = self.closeaftersendbytes.pop(0)
290 closeaftersendbytes = self.closeaftersendbytes.pop(0)
291 else:
291 else:
292 closeaftersendbytes = 0
292 closeaftersendbytes = 0
293
293
294 if closeafterrecvbytes or closeaftersendbytes:
294 if closeafterrecvbytes or closeaftersendbytes:
295 socket = socketproxy(socket, self.errorlog,
295 socket = socketproxy(socket, self.errorlog,
296 closeafterrecvbytes=closeafterrecvbytes,
296 closeafterrecvbytes=closeafterrecvbytes,
297 closeaftersendbytes=closeaftersendbytes)
297 closeaftersendbytes=closeaftersendbytes)
298
298
299 return super(badserver, self).process_request(socket, address)
299 return super(badserver, self).process_request(socket, address)
300
300
301 server.MercurialHTTPServer = badserver
301 server.MercurialHTTPServer = badserver
General Comments 0
You need to be logged in to leave comments. Login now