##// END OF EJS Templates
keepalive: add more context to bad status line errors...
Gregory Szorc -
r34309:9bd00305 default
parent child Browse files
Show More
@@ -1,711 +1,716 b''
1 1 # This library is free software; you can redistribute it and/or
2 2 # modify it under the terms of the GNU Lesser General Public
3 3 # License as published by the Free Software Foundation; either
4 4 # version 2.1 of the License, or (at your option) any later version.
5 5 #
6 6 # This library is distributed in the hope that it will be useful,
7 7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 9 # Lesser General Public License for more details.
10 10 #
11 11 # You should have received a copy of the GNU Lesser General Public
12 12 # License along with this library; if not, see
13 13 # <http://www.gnu.org/licenses/>.
14 14
15 15 # This file is part of urlgrabber, a high-level cross-protocol url-grabber
16 16 # Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko
17 17
18 18 # Modified by Benoit Boissinot:
19 19 # - fix for digest auth (inspired from urllib2.py @ Python v2.4)
20 20 # Modified by Dirkjan Ochtman:
21 21 # - import md5 function from a local util module
22 22 # Modified by Augie Fackler:
23 23 # - add safesend method and use it to prevent broken pipe errors
24 24 # on large POST requests
25 25
26 26 """An HTTP handler for urllib2 that supports HTTP 1.1 and keepalive.
27 27
28 28 >>> import urllib2
29 29 >>> from keepalive import HTTPHandler
30 30 >>> keepalive_handler = HTTPHandler()
31 31 >>> opener = urlreq.buildopener(keepalive_handler)
32 32 >>> urlreq.installopener(opener)
33 33 >>>
34 34 >>> fo = urlreq.urlopen('http://www.python.org')
35 35
36 36 If a connection to a given host is requested, and all of the existing
37 37 connections are still in use, another connection will be opened. If
38 38 the handler tries to use an existing connection but it fails in some
39 39 way, it will be closed and removed from the pool.
40 40
41 41 To remove the handler, simply re-run build_opener with no arguments, and
42 42 install that opener.
43 43
44 44 You can explicitly close connections by using the close_connection()
45 45 method of the returned file-like object (described below) or you can
46 46 use the handler methods:
47 47
48 48 close_connection(host)
49 49 close_all()
50 50 open_connections()
51 51
52 52 NOTE: using the close_connection and close_all methods of the handler
53 53 should be done with care when using multiple threads.
54 54 * there is nothing that prevents another thread from creating new
55 55 connections immediately after connections are closed
56 56 * no checks are done to prevent in-use connections from being closed
57 57
58 58 >>> keepalive_handler.close_all()
59 59
60 60 EXTRA ATTRIBUTES AND METHODS
61 61
62 62 Upon a status of 200, the object returned has a few additional
63 63 attributes and methods, which should not be used if you want to
64 64 remain consistent with the normal urllib2-returned objects:
65 65
66 66 close_connection() - close the connection to the host
67 67 readlines() - you know, readlines()
68 68 status - the return status (i.e. 404)
69 69 reason - english translation of status (i.e. 'File not found')
70 70
71 71 If you want the best of both worlds, use this inside an
72 72 AttributeError-catching try:
73 73
74 74 >>> try: status = fo.status
75 75 >>> except AttributeError: status = None
76 76
77 77 Unfortunately, these are ONLY there if status == 200, so it's not
78 78 easy to distinguish between non-200 responses. The reason is that
79 79 urllib2 tries to do clever things with error codes 301, 302, 401,
80 80 and 407, and it wraps the object upon return.
81 81 """
82 82
83 83 # $Id: keepalive.py,v 1.14 2006/04/04 21:00:32 mstenner Exp $
84 84
85 85 from __future__ import absolute_import, print_function
86 86
87 87 import errno
88 88 import hashlib
89 89 import socket
90 90 import sys
91 91 import threading
92 92
93 from .i18n import _
93 94 from . import (
94 95 util,
95 96 )
96 97
97 98 httplib = util.httplib
98 99 urlerr = util.urlerr
99 100 urlreq = util.urlreq
100 101
101 102 DEBUG = None
102 103
103 104 class ConnectionManager(object):
104 105 """
105 106 The connection manager must be able to:
106 107 * keep track of all existing
107 108 """
108 109 def __init__(self):
109 110 self._lock = threading.Lock()
110 111 self._hostmap = {} # map hosts to a list of connections
111 112 self._connmap = {} # map connections to host
112 113 self._readymap = {} # map connection to ready state
113 114
114 115 def add(self, host, connection, ready):
115 116 self._lock.acquire()
116 117 try:
117 118 if host not in self._hostmap:
118 119 self._hostmap[host] = []
119 120 self._hostmap[host].append(connection)
120 121 self._connmap[connection] = host
121 122 self._readymap[connection] = ready
122 123 finally:
123 124 self._lock.release()
124 125
125 126 def remove(self, connection):
126 127 self._lock.acquire()
127 128 try:
128 129 try:
129 130 host = self._connmap[connection]
130 131 except KeyError:
131 132 pass
132 133 else:
133 134 del self._connmap[connection]
134 135 del self._readymap[connection]
135 136 self._hostmap[host].remove(connection)
136 137 if not self._hostmap[host]: del self._hostmap[host]
137 138 finally:
138 139 self._lock.release()
139 140
140 141 def set_ready(self, connection, ready):
141 142 try:
142 143 self._readymap[connection] = ready
143 144 except KeyError:
144 145 pass
145 146
146 147 def get_ready_conn(self, host):
147 148 conn = None
148 149 self._lock.acquire()
149 150 try:
150 151 if host in self._hostmap:
151 152 for c in self._hostmap[host]:
152 153 if self._readymap[c]:
153 154 self._readymap[c] = 0
154 155 conn = c
155 156 break
156 157 finally:
157 158 self._lock.release()
158 159 return conn
159 160
160 161 def get_all(self, host=None):
161 162 if host:
162 163 return list(self._hostmap.get(host, []))
163 164 else:
164 165 return dict(self._hostmap)
165 166
166 167 class KeepAliveHandler(object):
167 168 def __init__(self):
168 169 self._cm = ConnectionManager()
169 170
170 171 #### Connection Management
171 172 def open_connections(self):
172 173 """return a list of connected hosts and the number of connections
173 174 to each. [('foo.com:80', 2), ('bar.org', 1)]"""
174 175 return [(host, len(li)) for (host, li) in self._cm.get_all().items()]
175 176
176 177 def close_connection(self, host):
177 178 """close connection(s) to <host>
178 179 host is the host:port spec, as in 'www.cnn.com:8080' as passed in.
179 180 no error occurs if there is no connection to that host."""
180 181 for h in self._cm.get_all(host):
181 182 self._cm.remove(h)
182 183 h.close()
183 184
184 185 def close_all(self):
185 186 """close all open connections"""
186 187 for host, conns in self._cm.get_all().iteritems():
187 188 for h in conns:
188 189 self._cm.remove(h)
189 190 h.close()
190 191
191 192 def _request_closed(self, request, host, connection):
192 193 """tells us that this request is now closed and that the
193 194 connection is ready for another request"""
194 195 self._cm.set_ready(connection, 1)
195 196
196 197 def _remove_connection(self, host, connection, close=0):
197 198 if close:
198 199 connection.close()
199 200 self._cm.remove(connection)
200 201
201 202 #### Transaction Execution
202 203 def http_open(self, req):
203 204 return self.do_open(HTTPConnection, req)
204 205
205 206 def do_open(self, http_class, req):
206 207 host = req.get_host()
207 208 if not host:
208 209 raise urlerr.urlerror('no host given')
209 210
210 211 try:
211 212 h = self._cm.get_ready_conn(host)
212 213 while h:
213 214 r = self._reuse_connection(h, req, host)
214 215
215 216 # if this response is non-None, then it worked and we're
216 217 # done. Break out, skipping the else block.
217 218 if r:
218 219 break
219 220
220 221 # connection is bad - possibly closed by server
221 222 # discard it and ask for the next free connection
222 223 h.close()
223 224 self._cm.remove(h)
224 225 h = self._cm.get_ready_conn(host)
225 226 else:
226 227 # no (working) free connections were found. Create a new one.
227 228 h = http_class(host)
228 229 if DEBUG:
229 230 DEBUG.info("creating new connection to %s (%d)",
230 231 host, id(h))
231 232 self._cm.add(host, h, 0)
232 233 self._start_transaction(h, req)
233 234 r = h.getresponse()
235 # The string form of BadStatusLine is the status line. Add some context
236 # to make the error message slightly more useful.
237 except httplib.BadStatusLine as err:
238 raise urlerr.urlerror(_('bad HTTP status line: %s') % err.line)
234 239 except (socket.error, httplib.HTTPException) as err:
235 240 raise urlerr.urlerror(err)
236 241
237 242 # if not a persistent connection, don't try to reuse it
238 243 if r.will_close:
239 244 self._cm.remove(h)
240 245
241 246 if DEBUG:
242 247 DEBUG.info("STATUS: %s, %s", r.status, r.reason)
243 248 r._handler = self
244 249 r._host = host
245 250 r._url = req.get_full_url()
246 251 r._connection = h
247 252 r.code = r.status
248 253 r.headers = r.msg
249 254 r.msg = r.reason
250 255
251 256 return r
252 257
253 258 def _reuse_connection(self, h, req, host):
254 259 """start the transaction with a re-used connection
255 260 return a response object (r) upon success or None on failure.
256 261 This DOES not close or remove bad connections in cases where
257 262 it returns. However, if an unexpected exception occurs, it
258 263 will close and remove the connection before re-raising.
259 264 """
260 265 try:
261 266 self._start_transaction(h, req)
262 267 r = h.getresponse()
263 268 # note: just because we got something back doesn't mean it
264 269 # worked. We'll check the version below, too.
265 270 except (socket.error, httplib.HTTPException):
266 271 r = None
267 272 except: # re-raises
268 273 # adding this block just in case we've missed
269 274 # something we will still raise the exception, but
270 275 # lets try and close the connection and remove it
271 276 # first. We previously got into a nasty loop
272 277 # where an exception was uncaught, and so the
273 278 # connection stayed open. On the next try, the
274 279 # same exception was raised, etc. The trade-off is
275 280 # that it's now possible this call will raise
276 281 # a DIFFERENT exception
277 282 if DEBUG:
278 283 DEBUG.error("unexpected exception - closing "
279 284 "connection to %s (%d)", host, id(h))
280 285 self._cm.remove(h)
281 286 h.close()
282 287 raise
283 288
284 289 if r is None or r.version == 9:
285 290 # httplib falls back to assuming HTTP 0.9 if it gets a
286 291 # bad header back. This is most likely to happen if
287 292 # the socket has been closed by the server since we
288 293 # last used the connection.
289 294 if DEBUG:
290 295 DEBUG.info("failed to re-use connection to %s (%d)",
291 296 host, id(h))
292 297 r = None
293 298 else:
294 299 if DEBUG:
295 300 DEBUG.info("re-using connection to %s (%d)", host, id(h))
296 301
297 302 return r
298 303
299 304 def _start_transaction(self, h, req):
300 305 # What follows mostly reimplements HTTPConnection.request()
301 306 # except it adds self.parent.addheaders in the mix and sends headers
302 307 # in a deterministic order (to make testing easier).
303 308 headers = util.sortdict(self.parent.addheaders)
304 309 headers.update(sorted(req.headers.items()))
305 310 headers.update(sorted(req.unredirected_hdrs.items()))
306 311 headers = util.sortdict((n.lower(), v) for n, v in headers.items())
307 312 skipheaders = {}
308 313 for n in ('host', 'accept-encoding'):
309 314 if n in headers:
310 315 skipheaders['skip_' + n.replace('-', '_')] = 1
311 316 try:
312 317 if req.has_data():
313 318 data = req.get_data()
314 319 h.putrequest(
315 320 req.get_method(), req.get_selector(), **skipheaders)
316 321 if 'content-type' not in headers:
317 322 h.putheader('Content-type',
318 323 'application/x-www-form-urlencoded')
319 324 if 'content-length' not in headers:
320 325 h.putheader('Content-length', '%d' % len(data))
321 326 else:
322 327 h.putrequest(
323 328 req.get_method(), req.get_selector(), **skipheaders)
324 329 except socket.error as err:
325 330 raise urlerr.urlerror(err)
326 331 for k, v in headers.items():
327 332 h.putheader(k, v)
328 333 h.endheaders()
329 334 if req.has_data():
330 335 h.send(data)
331 336
332 337 class HTTPHandler(KeepAliveHandler, urlreq.httphandler):
333 338 pass
334 339
335 340 class HTTPResponse(httplib.HTTPResponse):
336 341 # we need to subclass HTTPResponse in order to
337 342 # 1) add readline() and readlines() methods
338 343 # 2) add close_connection() methods
339 344 # 3) add info() and geturl() methods
340 345
341 346 # in order to add readline(), read must be modified to deal with a
342 347 # buffer. example: readline must read a buffer and then spit back
343 348 # one line at a time. The only real alternative is to read one
344 349 # BYTE at a time (ick). Once something has been read, it can't be
345 350 # put back (ok, maybe it can, but that's even uglier than this),
346 351 # so if you THEN do a normal read, you must first take stuff from
347 352 # the buffer.
348 353
349 354 # the read method wraps the original to accommodate buffering,
350 355 # although read() never adds to the buffer.
351 356 # Both readline and readlines have been stolen with almost no
352 357 # modification from socket.py
353 358
354 359
355 360 def __init__(self, sock, debuglevel=0, strict=0, method=None):
356 361 httplib.HTTPResponse.__init__(self, sock, debuglevel=debuglevel,
357 362 strict=True, method=method,
358 363 buffering=True)
359 364 self.fileno = sock.fileno
360 365 self.code = None
361 366 self._rbuf = ''
362 367 self._rbufsize = 8096
363 368 self._handler = None # inserted by the handler later
364 369 self._host = None # (same)
365 370 self._url = None # (same)
366 371 self._connection = None # (same)
367 372
368 373 _raw_read = httplib.HTTPResponse.read
369 374
370 375 def close(self):
371 376 if self.fp:
372 377 self.fp.close()
373 378 self.fp = None
374 379 if self._handler:
375 380 self._handler._request_closed(self, self._host,
376 381 self._connection)
377 382
378 383 def close_connection(self):
379 384 self._handler._remove_connection(self._host, self._connection, close=1)
380 385 self.close()
381 386
382 387 def info(self):
383 388 return self.headers
384 389
385 390 def geturl(self):
386 391 return self._url
387 392
388 393 def read(self, amt=None):
389 394 # the _rbuf test is only in this first if for speed. It's not
390 395 # logically necessary
391 396 if self._rbuf and not amt is None:
392 397 L = len(self._rbuf)
393 398 if amt > L:
394 399 amt -= L
395 400 else:
396 401 s = self._rbuf[:amt]
397 402 self._rbuf = self._rbuf[amt:]
398 403 return s
399 404
400 405 s = self._rbuf + self._raw_read(amt)
401 406 self._rbuf = ''
402 407 return s
403 408
404 409 # stolen from Python SVN #68532 to fix issue1088
405 410 def _read_chunked(self, amt):
406 411 chunk_left = self.chunk_left
407 412 parts = []
408 413
409 414 while True:
410 415 if chunk_left is None:
411 416 line = self.fp.readline()
412 417 i = line.find(';')
413 418 if i >= 0:
414 419 line = line[:i] # strip chunk-extensions
415 420 try:
416 421 chunk_left = int(line, 16)
417 422 except ValueError:
418 423 # close the connection as protocol synchronization is
419 424 # probably lost
420 425 self.close()
421 426 raise httplib.IncompleteRead(''.join(parts))
422 427 if chunk_left == 0:
423 428 break
424 429 if amt is None:
425 430 parts.append(self._safe_read(chunk_left))
426 431 elif amt < chunk_left:
427 432 parts.append(self._safe_read(amt))
428 433 self.chunk_left = chunk_left - amt
429 434 return ''.join(parts)
430 435 elif amt == chunk_left:
431 436 parts.append(self._safe_read(amt))
432 437 self._safe_read(2) # toss the CRLF at the end of the chunk
433 438 self.chunk_left = None
434 439 return ''.join(parts)
435 440 else:
436 441 parts.append(self._safe_read(chunk_left))
437 442 amt -= chunk_left
438 443
439 444 # we read the whole chunk, get another
440 445 self._safe_read(2) # toss the CRLF at the end of the chunk
441 446 chunk_left = None
442 447
443 448 # read and discard trailer up to the CRLF terminator
444 449 ### note: we shouldn't have any trailers!
445 450 while True:
446 451 line = self.fp.readline()
447 452 if not line:
448 453 # a vanishingly small number of sites EOF without
449 454 # sending the trailer
450 455 break
451 456 if line == '\r\n':
452 457 break
453 458
454 459 # we read everything; close the "file"
455 460 self.close()
456 461
457 462 return ''.join(parts)
458 463
459 464 def readline(self):
460 465 # Fast path for a line is already available in read buffer.
461 466 i = self._rbuf.find('\n')
462 467 if i >= 0:
463 468 i += 1
464 469 line = self._rbuf[:i]
465 470 self._rbuf = self._rbuf[i:]
466 471 return line
467 472
468 473 # No newline in local buffer. Read until we find one.
469 474 chunks = [self._rbuf]
470 475 i = -1
471 476 readsize = self._rbufsize
472 477 while True:
473 478 new = self._raw_read(readsize)
474 479 if not new:
475 480 break
476 481
477 482 chunks.append(new)
478 483 i = new.find('\n')
479 484 if i >= 0:
480 485 break
481 486
482 487 # We either have exhausted the stream or have a newline in chunks[-1].
483 488
484 489 # EOF
485 490 if i == -1:
486 491 self._rbuf = ''
487 492 return ''.join(chunks)
488 493
489 494 i += 1
490 495 self._rbuf = chunks[-1][i:]
491 496 chunks[-1] = chunks[-1][:i]
492 497 return ''.join(chunks)
493 498
494 499 def readlines(self, sizehint=0):
495 500 total = 0
496 501 list = []
497 502 while True:
498 503 line = self.readline()
499 504 if not line:
500 505 break
501 506 list.append(line)
502 507 total += len(line)
503 508 if sizehint and total >= sizehint:
504 509 break
505 510 return list
506 511
507 512 def safesend(self, str):
508 513 """Send `str' to the server.
509 514
510 515 Shamelessly ripped off from httplib to patch a bad behavior.
511 516 """
512 517 # _broken_pipe_resp is an attribute we set in this function
513 518 # if the socket is closed while we're sending data but
514 519 # the server sent us a response before hanging up.
515 520 # In that case, we want to pretend to send the rest of the
516 521 # outgoing data, and then let the user use getresponse()
517 522 # (which we wrap) to get this last response before
518 523 # opening a new socket.
519 524 if getattr(self, '_broken_pipe_resp', None) is not None:
520 525 return
521 526
522 527 if self.sock is None:
523 528 if self.auto_open:
524 529 self.connect()
525 530 else:
526 531 raise httplib.NotConnected
527 532
528 533 # send the data to the server. if we get a broken pipe, then close
529 534 # the socket. we want to reconnect when somebody tries to send again.
530 535 #
531 536 # NOTE: we DO propagate the error, though, because we cannot simply
532 537 # ignore the error... the caller will know if they can retry.
533 538 if self.debuglevel > 0:
534 539 print("send:", repr(str))
535 540 try:
536 541 blocksize = 8192
537 542 read = getattr(str, 'read', None)
538 543 if read is not None:
539 544 if self.debuglevel > 0:
540 545 print("sending a read()able")
541 546 data = read(blocksize)
542 547 while data:
543 548 self.sock.sendall(data)
544 549 data = read(blocksize)
545 550 else:
546 551 self.sock.sendall(str)
547 552 except socket.error as v:
548 553 reraise = True
549 554 if v[0] == errno.EPIPE: # Broken pipe
550 555 if self._HTTPConnection__state == httplib._CS_REQ_SENT:
551 556 self._broken_pipe_resp = None
552 557 self._broken_pipe_resp = self.getresponse()
553 558 reraise = False
554 559 self.close()
555 560 if reraise:
556 561 raise
557 562
558 563 def wrapgetresponse(cls):
559 564 """Wraps getresponse in cls with a broken-pipe sane version.
560 565 """
561 566 def safegetresponse(self):
562 567 # In safesend() we might set the _broken_pipe_resp
563 568 # attribute, in which case the socket has already
564 569 # been closed and we just need to give them the response
565 570 # back. Otherwise, we use the normal response path.
566 571 r = getattr(self, '_broken_pipe_resp', None)
567 572 if r is not None:
568 573 return r
569 574 return cls.getresponse(self)
570 575 safegetresponse.__doc__ = cls.getresponse.__doc__
571 576 return safegetresponse
572 577
573 578 class HTTPConnection(httplib.HTTPConnection):
574 579 # use the modified response class
575 580 response_class = HTTPResponse
576 581 send = safesend
577 582 getresponse = wrapgetresponse(httplib.HTTPConnection)
578 583
579 584
580 585 #########################################################################
581 586 ##### TEST FUNCTIONS
582 587 #########################################################################
583 588
584 589
585 590 def continuity(url):
586 591 md5 = hashlib.md5
587 592 format = '%25s: %s'
588 593
589 594 # first fetch the file with the normal http handler
590 595 opener = urlreq.buildopener()
591 596 urlreq.installopener(opener)
592 597 fo = urlreq.urlopen(url)
593 598 foo = fo.read()
594 599 fo.close()
595 600 m = md5(foo)
596 601 print(format % ('normal urllib', m.hexdigest()))
597 602
598 603 # now install the keepalive handler and try again
599 604 opener = urlreq.buildopener(HTTPHandler())
600 605 urlreq.installopener(opener)
601 606
602 607 fo = urlreq.urlopen(url)
603 608 foo = fo.read()
604 609 fo.close()
605 610 m = md5(foo)
606 611 print(format % ('keepalive read', m.hexdigest()))
607 612
608 613 fo = urlreq.urlopen(url)
609 614 foo = ''
610 615 while True:
611 616 f = fo.readline()
612 617 if f:
613 618 foo = foo + f
614 619 else: break
615 620 fo.close()
616 621 m = md5(foo)
617 622 print(format % ('keepalive readline', m.hexdigest()))
618 623
619 624 def comp(N, url):
620 625 print(' making %i connections to:\n %s' % (N, url))
621 626
622 627 util.stdout.write(' first using the normal urllib handlers')
623 628 # first use normal opener
624 629 opener = urlreq.buildopener()
625 630 urlreq.installopener(opener)
626 631 t1 = fetch(N, url)
627 632 print(' TIME: %.3f s' % t1)
628 633
629 634 util.stdout.write(' now using the keepalive handler ')
630 635 # now install the keepalive handler and try again
631 636 opener = urlreq.buildopener(HTTPHandler())
632 637 urlreq.installopener(opener)
633 638 t2 = fetch(N, url)
634 639 print(' TIME: %.3f s' % t2)
635 640 print(' improvement factor: %.2f' % (t1 / t2))
636 641
637 642 def fetch(N, url, delay=0):
638 643 import time
639 644 lens = []
640 645 starttime = time.time()
641 646 for i in range(N):
642 647 if delay and i > 0:
643 648 time.sleep(delay)
644 649 fo = urlreq.urlopen(url)
645 650 foo = fo.read()
646 651 fo.close()
647 652 lens.append(len(foo))
648 653 diff = time.time() - starttime
649 654
650 655 j = 0
651 656 for i in lens[1:]:
652 657 j = j + 1
653 658 if not i == lens[0]:
654 659 print("WARNING: inconsistent length on read %i: %i" % (j, i))
655 660
656 661 return diff
657 662
658 663 def test_timeout(url):
659 664 global DEBUG
660 665 dbbackup = DEBUG
661 666 class FakeLogger(object):
662 667 def debug(self, msg, *args):
663 668 print(msg % args)
664 669 info = warning = error = debug
665 670 DEBUG = FakeLogger()
666 671 print(" fetching the file to establish a connection")
667 672 fo = urlreq.urlopen(url)
668 673 data1 = fo.read()
669 674 fo.close()
670 675
671 676 i = 20
672 677 print(" waiting %i seconds for the server to close the connection" % i)
673 678 while i > 0:
674 679 util.stdout.write('\r %2i' % i)
675 680 util.stdout.flush()
676 681 time.sleep(1)
677 682 i -= 1
678 683 util.stderr.write('\r')
679 684
680 685 print(" fetching the file a second time")
681 686 fo = urlreq.urlopen(url)
682 687 data2 = fo.read()
683 688 fo.close()
684 689
685 690 if data1 == data2:
686 691 print(' data are identical')
687 692 else:
688 693 print(' ERROR: DATA DIFFER')
689 694
690 695 DEBUG = dbbackup
691 696
692 697
693 698 def test(url, N=10):
694 699 print("performing continuity test (making sure stuff isn't corrupted)")
695 700 continuity(url)
696 701 print('')
697 702 print("performing speed comparison")
698 703 comp(N, url)
699 704 print('')
700 705 print("performing dropped-connection check")
701 706 test_timeout(url)
702 707
703 708 if __name__ == '__main__':
704 709 import time
705 710 try:
706 711 N = int(sys.argv[1])
707 712 url = sys.argv[2]
708 713 except (IndexError, ValueError):
709 714 print("%s <integer> <url>" % sys.argv[0])
710 715 else:
711 716 test(url, N)
@@ -1,905 +1,903 b''
1 1 #require killdaemons serve zstd
2 2
3 3 Client version is embedded in HTTP request and is effectively dynamic. Pin the
4 4 version so behavior is deterministic.
5 5
6 6 $ cat > fakeversion.py << EOF
7 7 > from mercurial import util
8 8 > util.version = lambda: '4.2'
9 9 > EOF
10 10
11 11 $ cat >> $HGRCPATH << EOF
12 12 > [extensions]
13 13 > fakeversion = `pwd`/fakeversion.py
14 14 > EOF
15 15
16 16 $ hg init server0
17 17 $ cd server0
18 18 $ touch foo
19 19 $ hg -q commit -A -m initial
20 20
21 21 Also disable compression because zstd is optional and causes output to vary
22 22 and because debugging partial responses is hard when compression is involved
23 23
24 24 $ cat > .hg/hgrc << EOF
25 25 > [extensions]
26 26 > badserver = $TESTDIR/badserverext.py
27 27 > [server]
28 28 > compressionengines = none
29 29 > EOF
30 30
31 31 Failure to accept() socket should result in connection related error message
32 32
33 33 $ hg --config badserver.closebeforeaccept=true serve -p $HGPORT -d --pid-file=hg.pid
34 34 $ cat hg.pid > $DAEMON_PIDS
35 35
36 36 $ hg clone http://localhost:$HGPORT/ clone
37 37 abort: error: Connection reset by peer (no-windows !)
38 38 abort: error: An existing connection was forcibly closed by the remote host (windows !)
39 39 [255]
40 40
41 41 (The server exits on its own, but there is a race between that and starting a new server.
42 42 So ensure the process is dead.)
43 43
44 44 $ killdaemons.py $DAEMON_PIDS
45 45
46 46 Failure immediately after accept() should yield connection related error message
47 47
48 48 $ hg --config badserver.closeafteraccept=true serve -p $HGPORT -d --pid-file=hg.pid
49 49 $ cat hg.pid > $DAEMON_PIDS
50 50
51 51 TODO: this usually outputs good results, but sometimes emits abort:
52 52 error: '' on FreeBSD and OS X.
53 53 What we ideally want are:
54 54
55 55 abort: error: Connection reset by peer (no-windows !)
56 56 abort: error: An existing connection was forcibly closed by the remote host (windows !)
57 57
58 58 The flakiness in this output was observable easily with
59 59 --runs-per-test=20 on macOS 10.12 during the freeze for 4.2.
60 60 $ hg clone http://localhost:$HGPORT/ clone
61 61 abort: error: * (glob)
62 62 [255]
63 63
64 64 $ killdaemons.py $DAEMON_PIDS
65 65
66 66 Failure to read all bytes in initial HTTP request should yield connection related error message
67 67
68 68 $ hg --config badserver.closeafterrecvbytes=1 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
69 69 $ cat hg.pid > $DAEMON_PIDS
70 70
71 TODO this error message is not very good
72
73 71 $ hg clone http://localhost:$HGPORT/ clone
74 abort: error: ''
72 abort: error: bad HTTP status line: ''
75 73 [255]
76 74
77 75 $ killdaemons.py $DAEMON_PIDS
78 76
79 77 $ cat error.log
80 78 readline(1 from 65537) -> (1) G
81 79 read limit reached; closing socket
82 80
83 81 $ rm -f error.log
84 82
85 83 Same failure, but server reads full HTTP request line
86 84
87 85 $ hg --config badserver.closeafterrecvbytes=40 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
88 86 $ cat hg.pid > $DAEMON_PIDS
89 87 $ hg clone http://localhost:$HGPORT/ clone
90 abort: error: ''
88 abort: error: bad HTTP status line: ''
91 89 [255]
92 90
93 91 $ killdaemons.py $DAEMON_PIDS
94 92
95 93 $ cat error.log
96 94 readline(40 from 65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
97 95 readline(7 from -1) -> (7) Accept-
98 96 read limit reached; closing socket
99 97
100 98 $ rm -f error.log
101 99
102 100 Failure on subsequent HTTP request on the same socket (cmd?batch)
103 101
104 102 $ hg --config badserver.closeafterrecvbytes=210 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
105 103 $ cat hg.pid > $DAEMON_PIDS
106 104 $ hg clone http://localhost:$HGPORT/ clone
107 abort: error: ''
105 abort: error: bad HTTP status line: ''
108 106 [255]
109 107
110 108 $ killdaemons.py $DAEMON_PIDS
111 109
112 110 $ cat error.log
113 111 readline(210 from 65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
114 112 readline(177 from -1) -> (27) Accept-Encoding: identity\r\n
115 113 readline(150 from -1) -> (35) accept: application/mercurial-0.1\r\n
116 114 readline(115 from -1) -> (2?) host: localhost:$HGPORT\r\n (glob)
117 115 readline(9? from -1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
118 116 readline(4? from -1) -> (2) \r\n (glob)
119 117 write(36) -> HTTP/1.1 200 Script output follows\r\n
120 118 write(23) -> Server: badhttpserver\r\n
121 119 write(37) -> Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
122 120 write(41) -> Content-Type: application/mercurial-0.1\r\n
123 121 write(21) -> Content-Length: 405\r\n
124 122 write(2) -> \r\n
125 123 write(405) -> lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx compression=none
126 124 readline(4? from 65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n (glob)
127 125 readline(1? from -1) -> (1?) Accept-Encoding* (glob)
128 126 read limit reached; closing socket
129 127 readline(210 from 65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
130 128 readline(184 from -1) -> (27) Accept-Encoding: identity\r\n
131 129 readline(157 from -1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
132 130 readline(128 from -1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
133 131 readline(87 from -1) -> (48) x-hgproto-1: 0.1 0.2 comp=zstd,zlib,none,bzip2\r\n
134 132 readline(39 from -1) -> (35) accept: application/mercurial-0.1\r\n
135 133 readline(4 from -1) -> (4) host
136 134 read limit reached; closing socket
137 135
138 136 $ rm -f error.log
139 137
140 138 Failure to read getbundle HTTP request
141 139
142 140 $ hg --config badserver.closeafterrecvbytes=292 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
143 141 $ cat hg.pid > $DAEMON_PIDS
144 142 $ hg clone http://localhost:$HGPORT/ clone
145 143 requesting all changes
146 abort: error: ''
144 abort: error: bad HTTP status line: ''
147 145 [255]
148 146
149 147 $ killdaemons.py $DAEMON_PIDS
150 148
151 149 $ cat error.log
152 150 readline(292 from 65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
153 151 readline(259 from -1) -> (27) Accept-Encoding: identity\r\n
154 152 readline(232 from -1) -> (35) accept: application/mercurial-0.1\r\n
155 153 readline(197 from -1) -> (2?) host: localhost:$HGPORT\r\n (glob)
156 154 readline(17? from -1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
157 155 readline(12? from -1) -> (2) \r\n (glob)
158 156 write(36) -> HTTP/1.1 200 Script output follows\r\n
159 157 write(23) -> Server: badhttpserver\r\n
160 158 write(37) -> Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
161 159 write(41) -> Content-Type: application/mercurial-0.1\r\n
162 160 write(21) -> Content-Length: 405\r\n
163 161 write(2) -> \r\n
164 162 write(405) -> lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx compression=none
165 163 readline\(12[34] from 65537\) -> \(2[67]\) GET /\?cmd=batch HTTP/1.1\\r\\n (re)
166 164 readline(9? from -1) -> (27) Accept-Encoding: identity\r\n (glob)
167 165 readline(7? from -1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n (glob)
168 166 readline(4? from -1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n (glob)
169 167 readline(1 from -1) -> (1) x (?)
170 168 read limit reached; closing socket
171 169 readline(292 from 65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
172 170 readline(266 from -1) -> (27) Accept-Encoding: identity\r\n
173 171 readline(239 from -1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
174 172 readline(210 from -1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
175 173 readline(169 from -1) -> (48) x-hgproto-1: 0.1 0.2 comp=zstd,zlib,none,bzip2\r\n
176 174 readline(121 from -1) -> (35) accept: application/mercurial-0.1\r\n
177 175 readline(86 from -1) -> (2?) host: localhost:$HGPORT\r\n (glob)
178 176 readline(6? from -1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
179 177 readline(1? from -1) -> (2) \r\n (glob)
180 178 write(36) -> HTTP/1.1 200 Script output follows\r\n
181 179 write(23) -> Server: badhttpserver\r\n
182 180 write(37) -> Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
183 181 write(41) -> Content-Type: application/mercurial-0.1\r\n
184 182 write(20) -> Content-Length: 42\r\n
185 183 write(2) -> \r\n
186 184 write(42) -> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n;
187 185 readline\(1[23] from 65537\) -> \(1[23]\) GET /\?cmd=ge.? (re)
188 186 read limit reached; closing socket
189 187 readline(292 from 65537) -> (30) GET /?cmd=getbundle HTTP/1.1\r\n
190 188 readline(262 from -1) -> (27) Accept-Encoding: identity\r\n
191 189 readline(235 from -1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
192 190 readline(206 from -1) -> (206) x-hgarg-1: bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Ali
193 191 read limit reached; closing socket
194 192
195 193 $ rm -f error.log
196 194
197 195 Now do a variation using POST to send arguments
198 196
199 197 $ hg --config experimental.httppostargs=true --config badserver.closeafterrecvbytes=315 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
200 198 $ cat hg.pid > $DAEMON_PIDS
201 199
202 200 $ hg clone http://localhost:$HGPORT/ clone
203 abort: error: ''
201 abort: error: bad HTTP status line: ''
204 202 [255]
205 203
206 204 $ killdaemons.py $DAEMON_PIDS
207 205
208 206 $ cat error.log
209 207 readline(315 from 65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
210 208 readline(282 from -1) -> (27) Accept-Encoding: identity\r\n
211 209 readline(255 from -1) -> (35) accept: application/mercurial-0.1\r\n
212 210 readline(220 from -1) -> (2?) host: localhost:$HGPORT\r\n (glob)
213 211 readline(19? from -1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
214 212 readline(14? from -1) -> (2) \r\n (glob)
215 213 write(36) -> HTTP/1.1 200 Script output follows\r\n
216 214 write(23) -> Server: badhttpserver\r\n
217 215 write(37) -> Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
218 216 write(41) -> Content-Type: application/mercurial-0.1\r\n
219 217 write(21) -> Content-Length: 418\r\n
220 218 write(2) -> \r\n
221 219 write(418) -> lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 httppostargs httpmediatype=0.1rx,0.1tx,0.2tx compression=none
222 220 readline\(14[67] from 65537\) -> \(2[67]\) POST /\?cmd=batch HTTP/1.1\\r\\n (re)
223 221 readline\(1(19|20) from -1\) -> \(27\) Accept-Encoding: identity\\r\\n (re)
224 222 readline(9? from -1) -> (41) content-type: application/mercurial-0.1\r\n (glob)
225 223 readline(5? from -1) -> (19) vary: X-HgProto-1\r\n (glob)
226 224 readline(3? from -1) -> (19) x-hgargs-post: 28\r\n (glob)
227 225 readline(1? from -1) -> (1?) x-hgproto-1: * (glob)
228 226 read limit reached; closing socket
229 227 readline(315 from 65537) -> (27) POST /?cmd=batch HTTP/1.1\r\n
230 228 readline(288 from -1) -> (27) Accept-Encoding: identity\r\n
231 229 readline(261 from -1) -> (41) content-type: application/mercurial-0.1\r\n
232 230 readline(220 from -1) -> (19) vary: X-HgProto-1\r\n
233 231 readline(201 from -1) -> (19) x-hgargs-post: 28\r\n
234 232 readline(182 from -1) -> (48) x-hgproto-1: 0.1 0.2 comp=zstd,zlib,none,bzip2\r\n
235 233 readline(134 from -1) -> (35) accept: application/mercurial-0.1\r\n
236 234 readline(99 from -1) -> (20) content-length: 28\r\n
237 235 readline(79 from -1) -> (2?) host: localhost:$HGPORT\r\n (glob)
238 236 readline(5? from -1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
239 237 readline(? from -1) -> (2) \r\n (glob)
240 238 read(? from 28) -> (?) cmds=* (glob)
241 239 read limit reached, closing socket
242 240 write(36) -> HTTP/1.1 500 Internal Server Error\r\n
243 241
244 242 $ rm -f error.log
245 243
246 244 Now move on to partial server responses
247 245
248 246 Server sends a single character from the HTTP response line
249 247
250 248 $ hg --config badserver.closeaftersendbytes=1 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
251 249 $ cat hg.pid > $DAEMON_PIDS
252 250
253 251 $ hg clone http://localhost:$HGPORT/ clone
254 abort: error: H
252 abort: error: bad HTTP status line: H
255 253 [255]
256 254
257 255 $ killdaemons.py $DAEMON_PIDS
258 256
259 257 $ cat error.log
260 258 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
261 259 readline(-1) -> (27) Accept-Encoding: identity\r\n
262 260 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
263 261 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
264 262 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
265 263 readline(-1) -> (2) \r\n
266 264 write(1 from 36) -> (0) H
267 265 write limit reached; closing socket
268 266 write(36) -> HTTP/1.1 500 Internal Server Error\r\n
269 267
270 268 $ rm -f error.log
271 269
272 270 Server sends an incomplete capabilities response body
273 271
274 272 $ hg --config badserver.closeaftersendbytes=180 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
275 273 $ cat hg.pid > $DAEMON_PIDS
276 274
277 275 $ hg clone http://localhost:$HGPORT/ clone
278 276 abort: HTTP request error (incomplete response; expected 385 bytes got 20)
279 277 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
280 278 [255]
281 279
282 280 $ killdaemons.py $DAEMON_PIDS
283 281
284 282 $ cat error.log
285 283 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
286 284 readline(-1) -> (27) Accept-Encoding: identity\r\n
287 285 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
288 286 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
289 287 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
290 288 readline(-1) -> (2) \r\n
291 289 write(36 from 36) -> (144) HTTP/1.1 200 Script output follows\r\n
292 290 write(23 from 23) -> (121) Server: badhttpserver\r\n
293 291 write(37 from 37) -> (84) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
294 292 write(41 from 41) -> (43) Content-Type: application/mercurial-0.1\r\n
295 293 write(21 from 21) -> (22) Content-Length: 405\r\n
296 294 write(2 from 2) -> (20) \r\n
297 295 write(20 from 405) -> (0) lookup changegroupsu
298 296 write limit reached; closing socket
299 297
300 298 $ rm -f error.log
301 299
302 300 Server sends incomplete headers for batch request
303 301
304 302 $ hg --config badserver.closeaftersendbytes=695 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
305 303 $ cat hg.pid > $DAEMON_PIDS
306 304
307 305 TODO this output is horrible
308 306
309 307 $ hg clone http://localhost:$HGPORT/ clone
310 308 abort: 'http://localhost:$HGPORT/' does not appear to be an hg repository:
311 309 ---%<--- (application/mercuria)
312 310
313 311 ---%<---
314 312 !
315 313 [255]
316 314
317 315 $ killdaemons.py $DAEMON_PIDS
318 316
319 317 $ cat error.log
320 318 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
321 319 readline(-1) -> (27) Accept-Encoding: identity\r\n
322 320 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
323 321 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
324 322 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
325 323 readline(-1) -> (2) \r\n
326 324 write(36 from 36) -> (659) HTTP/1.1 200 Script output follows\r\n
327 325 write(23 from 23) -> (636) Server: badhttpserver\r\n
328 326 write(37 from 37) -> (599) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
329 327 write(41 from 41) -> (558) Content-Type: application/mercurial-0.1\r\n
330 328 write(21 from 21) -> (537) Content-Length: 405\r\n
331 329 write(2 from 2) -> (535) \r\n
332 330 write(405 from 405) -> (130) lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx compression=none
333 331 readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
334 332 readline(-1) -> (27) Accept-Encoding: identity\r\n
335 333 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
336 334 readline(-1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
337 335 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=zstd,zlib,none,bzip2\r\n
338 336 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
339 337 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
340 338 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
341 339 readline(-1) -> (2) \r\n
342 340 write(36 from 36) -> (94) HTTP/1.1 200 Script output follows\r\n
343 341 write(23 from 23) -> (71) Server: badhttpserver\r\n
344 342 write(37 from 37) -> (34) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
345 343 write(34 from 41) -> (0) Content-Type: application/mercuria
346 344 write limit reached; closing socket
347 345 write(36) -> HTTP/1.1 500 Internal Server Error\r\n
348 346
349 347 $ rm -f error.log
350 348
351 349 Server sends an incomplete HTTP response body to batch request
352 350
353 351 $ hg --config badserver.closeaftersendbytes=760 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
354 352 $ cat hg.pid > $DAEMON_PIDS
355 353
356 354 TODO client spews a stack due to uncaught ValueError in batch.results()
357 355 $ hg clone http://localhost:$HGPORT/ clone 2> /dev/null
358 356 [1]
359 357
360 358 $ killdaemons.py $DAEMON_PIDS
361 359
362 360 $ cat error.log
363 361 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
364 362 readline(-1) -> (27) Accept-Encoding: identity\r\n
365 363 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
366 364 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
367 365 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
368 366 readline(-1) -> (2) \r\n
369 367 write(36 from 36) -> (724) HTTP/1.1 200 Script output follows\r\n
370 368 write(23 from 23) -> (701) Server: badhttpserver\r\n
371 369 write(37 from 37) -> (664) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
372 370 write(41 from 41) -> (623) Content-Type: application/mercurial-0.1\r\n
373 371 write(21 from 21) -> (602) Content-Length: 405\r\n
374 372 write(2 from 2) -> (600) \r\n
375 373 write(405 from 405) -> (195) lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx compression=none
376 374 readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
377 375 readline(-1) -> (27) Accept-Encoding: identity\r\n
378 376 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
379 377 readline(-1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
380 378 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=zstd,zlib,none,bzip2\r\n
381 379 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
382 380 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
383 381 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
384 382 readline(-1) -> (2) \r\n
385 383 write(36 from 36) -> (159) HTTP/1.1 200 Script output follows\r\n
386 384 write(23 from 23) -> (136) Server: badhttpserver\r\n
387 385 write(37 from 37) -> (99) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
388 386 write(41 from 41) -> (58) Content-Type: application/mercurial-0.1\r\n
389 387 write(20 from 20) -> (38) Content-Length: 42\r\n
390 388 write(2 from 2) -> (36) \r\n
391 389 write(36 from 42) -> (0) 96ee1d7354c4ad7372047672c36a1f561e3a
392 390 write limit reached; closing socket
393 391
394 392 $ rm -f error.log
395 393
396 394 Server sends incomplete headers for getbundle response
397 395
398 396 $ hg --config badserver.closeaftersendbytes=895 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
399 397 $ cat hg.pid > $DAEMON_PIDS
400 398
401 399 TODO this output is terrible
402 400
403 401 $ hg clone http://localhost:$HGPORT/ clone
404 402 requesting all changes
405 403 abort: 'http://localhost:$HGPORT/' does not appear to be an hg repository:
406 404 ---%<--- (application/mercuri)
407 405
408 406 ---%<---
409 407 !
410 408 [255]
411 409
412 410 $ killdaemons.py $DAEMON_PIDS
413 411
414 412 $ cat error.log
415 413 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
416 414 readline(-1) -> (27) Accept-Encoding: identity\r\n
417 415 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
418 416 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
419 417 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
420 418 readline(-1) -> (2) \r\n
421 419 write(36 from 36) -> (859) HTTP/1.1 200 Script output follows\r\n
422 420 write(23 from 23) -> (836) Server: badhttpserver\r\n
423 421 write(37 from 37) -> (799) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
424 422 write(41 from 41) -> (758) Content-Type: application/mercurial-0.1\r\n
425 423 write(21 from 21) -> (737) Content-Length: 405\r\n
426 424 write(2 from 2) -> (735) \r\n
427 425 write(405 from 405) -> (330) lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx compression=none
428 426 readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
429 427 readline(-1) -> (27) Accept-Encoding: identity\r\n
430 428 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
431 429 readline(-1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
432 430 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=zstd,zlib,none,bzip2\r\n
433 431 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
434 432 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
435 433 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
436 434 readline(-1) -> (2) \r\n
437 435 write(36 from 36) -> (294) HTTP/1.1 200 Script output follows\r\n
438 436 write(23 from 23) -> (271) Server: badhttpserver\r\n
439 437 write(37 from 37) -> (234) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
440 438 write(41 from 41) -> (193) Content-Type: application/mercurial-0.1\r\n
441 439 write(20 from 20) -> (173) Content-Length: 42\r\n
442 440 write(2 from 2) -> (171) \r\n
443 441 write(42 from 42) -> (129) 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n;
444 442 readline(65537) -> (30) GET /?cmd=getbundle HTTP/1.1\r\n
445 443 readline(-1) -> (27) Accept-Encoding: identity\r\n
446 444 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
447 445 readline(-1) -> (396) x-hgarg-1: bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=1&common=0000000000000000000000000000000000000000&heads=96ee1d7354c4ad7372047672c36a1f561e3a6a4c&listkeys=phases%2Cbookmarks\r\n
448 446 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=zstd,zlib,none,bzip2\r\n
449 447 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
450 448 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
451 449 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
452 450 readline(-1) -> (2) \r\n
453 451 write(36 from 36) -> (93) HTTP/1.1 200 Script output follows\r\n
454 452 write(23 from 23) -> (70) Server: badhttpserver\r\n
455 453 write(37 from 37) -> (33) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
456 454 write(33 from 41) -> (0) Content-Type: application/mercuri
457 455 write limit reached; closing socket
458 456 write(36) -> HTTP/1.1 500 Internal Server Error\r\n
459 457
460 458 $ rm -f error.log
461 459
462 460 Server sends empty HTTP body for getbundle
463 461
464 462 $ hg --config badserver.closeaftersendbytes=933 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
465 463 $ cat hg.pid > $DAEMON_PIDS
466 464
467 465 $ hg clone http://localhost:$HGPORT/ clone
468 466 requesting all changes
469 467 abort: HTTP request error (incomplete response)
470 468 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
471 469 [255]
472 470
473 471 $ killdaemons.py $DAEMON_PIDS
474 472
475 473 $ cat error.log
476 474 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
477 475 readline(-1) -> (27) Accept-Encoding: identity\r\n
478 476 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
479 477 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
480 478 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
481 479 readline(-1) -> (2) \r\n
482 480 write(36 from 36) -> (897) HTTP/1.1 200 Script output follows\r\n
483 481 write(23 from 23) -> (874) Server: badhttpserver\r\n
484 482 write(37 from 37) -> (837) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
485 483 write(41 from 41) -> (796) Content-Type: application/mercurial-0.1\r\n
486 484 write(21 from 21) -> (775) Content-Length: 405\r\n
487 485 write(2 from 2) -> (773) \r\n
488 486 write(405 from 405) -> (368) lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx compression=none
489 487 readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
490 488 readline(-1) -> (27) Accept-Encoding: identity\r\n
491 489 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
492 490 readline(-1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
493 491 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=zstd,zlib,none,bzip2\r\n
494 492 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
495 493 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
496 494 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
497 495 readline(-1) -> (2) \r\n
498 496 write(36 from 36) -> (332) HTTP/1.1 200 Script output follows\r\n
499 497 write(23 from 23) -> (309) Server: badhttpserver\r\n
500 498 write(37 from 37) -> (272) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
501 499 write(41 from 41) -> (231) Content-Type: application/mercurial-0.1\r\n
502 500 write(20 from 20) -> (211) Content-Length: 42\r\n
503 501 write(2 from 2) -> (209) \r\n
504 502 write(42 from 42) -> (167) 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n;
505 503 readline(65537) -> (30) GET /?cmd=getbundle HTTP/1.1\r\n
506 504 readline(-1) -> (27) Accept-Encoding: identity\r\n
507 505 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
508 506 readline(-1) -> (396) x-hgarg-1: bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=1&common=0000000000000000000000000000000000000000&heads=96ee1d7354c4ad7372047672c36a1f561e3a6a4c&listkeys=phases%2Cbookmarks\r\n
509 507 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=zstd,zlib,none,bzip2\r\n
510 508 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
511 509 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
512 510 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
513 511 readline(-1) -> (2) \r\n
514 512 write(36 from 36) -> (131) HTTP/1.1 200 Script output follows\r\n
515 513 write(23 from 23) -> (108) Server: badhttpserver\r\n
516 514 write(37 from 37) -> (71) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
517 515 write(41 from 41) -> (30) Content-Type: application/mercurial-0.2\r\n
518 516 write(28 from 28) -> (2) Transfer-Encoding: chunked\r\n
519 517 write(2 from 2) -> (0) \r\n
520 518 write limit reached; closing socket
521 519 write(36) -> HTTP/1.1 500 Internal Server Error\r\n
522 520
523 521 $ rm -f error.log
524 522
525 523 Server sends partial compression string
526 524
527 525 $ hg --config badserver.closeaftersendbytes=945 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
528 526 $ cat hg.pid > $DAEMON_PIDS
529 527
530 528 $ hg clone http://localhost:$HGPORT/ clone
531 529 requesting all changes
532 530 abort: HTTP request error (incomplete response; expected 1 bytes got 3)
533 531 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
534 532 [255]
535 533
536 534 $ killdaemons.py $DAEMON_PIDS
537 535
538 536 $ cat error.log
539 537 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
540 538 readline(-1) -> (27) Accept-Encoding: identity\r\n
541 539 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
542 540 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
543 541 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
544 542 readline(-1) -> (2) \r\n
545 543 write(36 from 36) -> (909) HTTP/1.1 200 Script output follows\r\n
546 544 write(23 from 23) -> (886) Server: badhttpserver\r\n
547 545 write(37 from 37) -> (849) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
548 546 write(41 from 41) -> (808) Content-Type: application/mercurial-0.1\r\n
549 547 write(21 from 21) -> (787) Content-Length: 405\r\n
550 548 write(2 from 2) -> (785) \r\n
551 549 write(405 from 405) -> (380) lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx compression=none
552 550 readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
553 551 readline(-1) -> (27) Accept-Encoding: identity\r\n
554 552 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
555 553 readline(-1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
556 554 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=zstd,zlib,none,bzip2\r\n
557 555 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
558 556 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
559 557 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
560 558 readline(-1) -> (2) \r\n
561 559 write(36 from 36) -> (344) HTTP/1.1 200 Script output follows\r\n
562 560 write(23 from 23) -> (321) Server: badhttpserver\r\n
563 561 write(37 from 37) -> (284) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
564 562 write(41 from 41) -> (243) Content-Type: application/mercurial-0.1\r\n
565 563 write(20 from 20) -> (223) Content-Length: 42\r\n
566 564 write(2 from 2) -> (221) \r\n
567 565 write(42 from 42) -> (179) 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n;
568 566 readline(65537) -> (30) GET /?cmd=getbundle HTTP/1.1\r\n
569 567 readline(-1) -> (27) Accept-Encoding: identity\r\n
570 568 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
571 569 readline(-1) -> (396) x-hgarg-1: bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=1&common=0000000000000000000000000000000000000000&heads=96ee1d7354c4ad7372047672c36a1f561e3a6a4c&listkeys=phases%2Cbookmarks\r\n
572 570 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=zstd,zlib,none,bzip2\r\n
573 571 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
574 572 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
575 573 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
576 574 readline(-1) -> (2) \r\n
577 575 write(36 from 36) -> (143) HTTP/1.1 200 Script output follows\r\n
578 576 write(23 from 23) -> (120) Server: badhttpserver\r\n
579 577 write(37 from 37) -> (83) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
580 578 write(41 from 41) -> (42) Content-Type: application/mercurial-0.2\r\n
581 579 write(28 from 28) -> (14) Transfer-Encoding: chunked\r\n
582 580 write(2 from 2) -> (12) \r\n
583 581 write(6 from 6) -> (6) 1\\r\\n\x04\\r\\n (esc)
584 582 write(6 from 9) -> (0) 4\r\nnon
585 583 write limit reached; closing socket
586 584 write(27) -> 15\r\nInternal Server Error\r\n
587 585
588 586 $ rm -f error.log
589 587
590 588 Server sends partial bundle2 header magic
591 589
592 590 $ hg --config badserver.closeaftersendbytes=954 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
593 591 $ cat hg.pid > $DAEMON_PIDS
594 592
595 593 $ hg clone http://localhost:$HGPORT/ clone
596 594 requesting all changes
597 595 abort: HTTP request error (incomplete response; expected 1 bytes got 3)
598 596 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
599 597 [255]
600 598
601 599 $ killdaemons.py $DAEMON_PIDS
602 600
603 601 $ tail -7 error.log
604 602 write(28 from 28) -> (23) Transfer-Encoding: chunked\r\n
605 603 write(2 from 2) -> (21) \r\n
606 604 write(6 from 6) -> (15) 1\\r\\n\x04\\r\\n (esc)
607 605 write(9 from 9) -> (6) 4\r\nnone\r\n
608 606 write(6 from 9) -> (0) 4\r\nHG2
609 607 write limit reached; closing socket
610 608 write(27) -> 15\r\nInternal Server Error\r\n
611 609
612 610 $ rm -f error.log
613 611
614 612 Server sends incomplete bundle2 stream params length
615 613
616 614 $ hg --config badserver.closeaftersendbytes=963 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
617 615 $ cat hg.pid > $DAEMON_PIDS
618 616
619 617 $ hg clone http://localhost:$HGPORT/ clone
620 618 requesting all changes
621 619 abort: HTTP request error (incomplete response; expected 1 bytes got 3)
622 620 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
623 621 [255]
624 622
625 623 $ killdaemons.py $DAEMON_PIDS
626 624
627 625 $ tail -8 error.log
628 626 write(28 from 28) -> (32) Transfer-Encoding: chunked\r\n
629 627 write(2 from 2) -> (30) \r\n
630 628 write(6 from 6) -> (24) 1\\r\\n\x04\\r\\n (esc)
631 629 write(9 from 9) -> (15) 4\r\nnone\r\n
632 630 write(9 from 9) -> (6) 4\r\nHG20\r\n
633 631 write(6 from 9) -> (0) 4\\r\\n\x00\x00\x00 (esc)
634 632 write limit reached; closing socket
635 633 write(27) -> 15\r\nInternal Server Error\r\n
636 634
637 635 $ rm -f error.log
638 636
639 637 Servers stops after bundle2 stream params header
640 638
641 639 $ hg --config badserver.closeaftersendbytes=966 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
642 640 $ cat hg.pid > $DAEMON_PIDS
643 641
644 642 $ hg clone http://localhost:$HGPORT/ clone
645 643 requesting all changes
646 644 abort: HTTP request error (incomplete response)
647 645 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
648 646 [255]
649 647
650 648 $ killdaemons.py $DAEMON_PIDS
651 649
652 650 $ tail -8 error.log
653 651 write(28 from 28) -> (35) Transfer-Encoding: chunked\r\n
654 652 write(2 from 2) -> (33) \r\n
655 653 write(6 from 6) -> (27) 1\\r\\n\x04\\r\\n (esc)
656 654 write(9 from 9) -> (18) 4\r\nnone\r\n
657 655 write(9 from 9) -> (9) 4\r\nHG20\r\n
658 656 write(9 from 9) -> (0) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
659 657 write limit reached; closing socket
660 658 write(27) -> 15\r\nInternal Server Error\r\n
661 659
662 660 $ rm -f error.log
663 661
664 662 Server stops sending after bundle2 part header length
665 663
666 664 $ hg --config badserver.closeaftersendbytes=975 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
667 665 $ cat hg.pid > $DAEMON_PIDS
668 666
669 667 $ hg clone http://localhost:$HGPORT/ clone
670 668 requesting all changes
671 669 abort: HTTP request error (incomplete response)
672 670 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
673 671 [255]
674 672
675 673 $ killdaemons.py $DAEMON_PIDS
676 674
677 675 $ tail -9 error.log
678 676 write(28 from 28) -> (44) Transfer-Encoding: chunked\r\n
679 677 write(2 from 2) -> (42) \r\n
680 678 write(6 from 6) -> (36) 1\\r\\n\x04\\r\\n (esc)
681 679 write(9 from 9) -> (27) 4\r\nnone\r\n
682 680 write(9 from 9) -> (18) 4\r\nHG20\r\n
683 681 write(9 from 9) -> (9) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
684 682 write(9 from 9) -> (0) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
685 683 write limit reached; closing socket
686 684 write(27) -> 15\r\nInternal Server Error\r\n
687 685
688 686 $ rm -f error.log
689 687
690 688 Server stops sending after bundle2 part header
691 689
692 690 $ hg --config badserver.closeaftersendbytes=1022 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
693 691 $ cat hg.pid > $DAEMON_PIDS
694 692
695 693 $ hg clone http://localhost:$HGPORT/ clone
696 694 requesting all changes
697 695 adding changesets
698 696 transaction abort!
699 697 rollback completed
700 698 abort: HTTP request error (incomplete response)
701 699 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
702 700 [255]
703 701
704 702 $ killdaemons.py $DAEMON_PIDS
705 703
706 704 $ tail -10 error.log
707 705 write(28 from 28) -> (91) Transfer-Encoding: chunked\r\n
708 706 write(2 from 2) -> (89) \r\n
709 707 write(6 from 6) -> (83) 1\\r\\n\x04\\r\\n (esc)
710 708 write(9 from 9) -> (74) 4\r\nnone\r\n
711 709 write(9 from 9) -> (65) 4\r\nHG20\r\n
712 710 write(9 from 9) -> (56) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
713 711 write(9 from 9) -> (47) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
714 712 write(47 from 47) -> (0) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
715 713 write limit reached; closing socket
716 714 write(27) -> 15\r\nInternal Server Error\r\n
717 715
718 716 $ rm -f error.log
719 717
720 718 Server stops after bundle2 part payload chunk size
721 719
722 720 $ hg --config badserver.closeaftersendbytes=1031 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
723 721 $ cat hg.pid > $DAEMON_PIDS
724 722
725 723 $ hg clone http://localhost:$HGPORT/ clone
726 724 requesting all changes
727 725 adding changesets
728 726 transaction abort!
729 727 rollback completed
730 728 abort: HTTP request error (incomplete response)
731 729 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
732 730 [255]
733 731
734 732 $ killdaemons.py $DAEMON_PIDS
735 733
736 734 $ tail -11 error.log
737 735 write(28 from 28) -> (100) Transfer-Encoding: chunked\r\n
738 736 write(2 from 2) -> (98) \r\n
739 737 write(6 from 6) -> (92) 1\\r\\n\x04\\r\\n (esc)
740 738 write(9 from 9) -> (83) 4\r\nnone\r\n
741 739 write(9 from 9) -> (74) 4\r\nHG20\r\n
742 740 write(9 from 9) -> (65) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
743 741 write(9 from 9) -> (56) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
744 742 write(47 from 47) -> (9) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
745 743 write(9 from 9) -> (0) 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
746 744 write limit reached; closing socket
747 745 write(27) -> 15\r\nInternal Server Error\r\n
748 746
749 747 $ rm -f error.log
750 748
751 749 Server stops sending in middle of bundle2 payload chunk
752 750
753 751 $ hg --config badserver.closeaftersendbytes=1504 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
754 752 $ cat hg.pid > $DAEMON_PIDS
755 753
756 754 $ hg clone http://localhost:$HGPORT/ clone
757 755 requesting all changes
758 756 adding changesets
759 757 transaction abort!
760 758 rollback completed
761 759 abort: HTTP request error (incomplete response)
762 760 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
763 761 [255]
764 762
765 763 $ killdaemons.py $DAEMON_PIDS
766 764
767 765 $ tail -12 error.log
768 766 write(28 from 28) -> (573) Transfer-Encoding: chunked\r\n
769 767 write(2 from 2) -> (571) \r\n
770 768 write(6 from 6) -> (565) 1\\r\\n\x04\\r\\n (esc)
771 769 write(9 from 9) -> (556) 4\r\nnone\r\n
772 770 write(9 from 9) -> (547) 4\r\nHG20\r\n
773 771 write(9 from 9) -> (538) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
774 772 write(9 from 9) -> (529) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
775 773 write(47 from 47) -> (482) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
776 774 write(9 from 9) -> (473) 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
777 775 write(473 from 473) -> (0) 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
778 776 write limit reached; closing socket
779 777 write(27) -> 15\r\nInternal Server Error\r\n
780 778
781 779 $ rm -f error.log
782 780
783 781 Server stops sending after 0 length payload chunk size
784 782
785 783 $ hg --config badserver.closeaftersendbytes=1513 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
786 784 $ cat hg.pid > $DAEMON_PIDS
787 785
788 786 $ hg clone http://localhost:$HGPORT/ clone
789 787 requesting all changes
790 788 adding changesets
791 789 adding manifests
792 790 adding file changes
793 791 added 1 changesets with 1 changes to 1 files
794 792 transaction abort!
795 793 rollback completed
796 794 abort: HTTP request error (incomplete response)
797 795 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
798 796 [255]
799 797
800 798 $ killdaemons.py $DAEMON_PIDS
801 799
802 800 $ tail -13 error.log
803 801 write(28 from 28) -> (582) Transfer-Encoding: chunked\r\n
804 802 write(2 from 2) -> (580) \r\n
805 803 write(6 from 6) -> (574) 1\\r\\n\x04\\r\\n (esc)
806 804 write(9 from 9) -> (565) 4\r\nnone\r\n
807 805 write(9 from 9) -> (556) 4\r\nHG20\r\n
808 806 write(9 from 9) -> (547) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
809 807 write(9 from 9) -> (538) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
810 808 write(47 from 47) -> (491) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
811 809 write(9 from 9) -> (482) 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
812 810 write(473 from 473) -> (9) 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
813 811 write(9 from 9) -> (0) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
814 812 write limit reached; closing socket
815 813 write(27) -> 15\r\nInternal Server Error\r\n
816 814
817 815 $ rm -f error.log
818 816
819 817 Server stops sending after 0 part bundle part header (indicating end of bundle2 payload)
820 818 This is before the 0 size chunked transfer part that signals end of HTTP response.
821 819
822 820 $ hg --config badserver.closeaftersendbytes=1710 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
823 821 $ cat hg.pid > $DAEMON_PIDS
824 822
825 823 $ hg clone http://localhost:$HGPORT/ clone
826 824 requesting all changes
827 825 adding changesets
828 826 adding manifests
829 827 adding file changes
830 828 added 1 changesets with 1 changes to 1 files
831 829 updating to branch default
832 830 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
833 831
834 832 $ killdaemons.py $DAEMON_PIDS
835 833
836 834 $ tail -22 error.log
837 835 write(28 from 28) -> (779) Transfer-Encoding: chunked\r\n
838 836 write(2 from 2) -> (777) \r\n
839 837 write(6 from 6) -> (771) 1\\r\\n\x04\\r\\n (esc)
840 838 write(9 from 9) -> (762) 4\r\nnone\r\n
841 839 write(9 from 9) -> (753) 4\r\nHG20\r\n
842 840 write(9 from 9) -> (744) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
843 841 write(9 from 9) -> (735) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
844 842 write(47 from 47) -> (688) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
845 843 write(9 from 9) -> (679) 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
846 844 write(473 from 473) -> (206) 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
847 845 write(9 from 9) -> (197) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
848 846 write(9 from 9) -> (188) 4\\r\\n\x00\x00\x00 \\r\\n (esc)
849 847 write(38 from 38) -> (150) 20\\r\\n\x08LISTKEYS\x00\x00\x00\x01\x01\x00 \x06namespacephases\\r\\n (esc)
850 848 write(9 from 9) -> (141) 4\\r\\n\x00\x00\x00:\\r\\n (esc)
851 849 write(64 from 64) -> (77) 3a\r\n96ee1d7354c4ad7372047672c36a1f561e3a6a4c 1\npublishing True\r\n
852 850 write(9 from 9) -> (68) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
853 851 write(9 from 9) -> (59) 4\\r\\n\x00\x00\x00#\\r\\n (esc)
854 852 write(41 from 41) -> (18) 23\\r\\n\x08LISTKEYS\x00\x00\x00\x02\x01\x00 namespacebookmarks\\r\\n (esc)
855 853 write(9 from 9) -> (9) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
856 854 write(9 from 9) -> (0) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
857 855 write limit reached; closing socket
858 856 write(27) -> 15\r\nInternal Server Error\r\n
859 857
860 858 $ rm -f error.log
861 859 $ rm -rf clone
862 860
863 861 Server sends a size 0 chunked-transfer size without terminating \r\n
864 862
865 863 $ hg --config badserver.closeaftersendbytes=1713 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
866 864 $ cat hg.pid > $DAEMON_PIDS
867 865
868 866 $ hg clone http://localhost:$HGPORT/ clone
869 867 requesting all changes
870 868 adding changesets
871 869 adding manifests
872 870 adding file changes
873 871 added 1 changesets with 1 changes to 1 files
874 872 updating to branch default
875 873 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
876 874
877 875 $ killdaemons.py $DAEMON_PIDS
878 876
879 877 $ tail -23 error.log
880 878 write(28 from 28) -> (782) Transfer-Encoding: chunked\r\n
881 879 write(2 from 2) -> (780) \r\n
882 880 write(6 from 6) -> (774) 1\\r\\n\x04\\r\\n (esc)
883 881 write(9 from 9) -> (765) 4\r\nnone\r\n
884 882 write(9 from 9) -> (756) 4\r\nHG20\r\n
885 883 write(9 from 9) -> (747) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
886 884 write(9 from 9) -> (738) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
887 885 write(47 from 47) -> (691) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
888 886 write(9 from 9) -> (682) 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
889 887 write(473 from 473) -> (209) 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
890 888 write(9 from 9) -> (200) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
891 889 write(9 from 9) -> (191) 4\\r\\n\x00\x00\x00 \\r\\n (esc)
892 890 write(38 from 38) -> (153) 20\\r\\n\x08LISTKEYS\x00\x00\x00\x01\x01\x00 \x06namespacephases\\r\\n (esc)
893 891 write(9 from 9) -> (144) 4\\r\\n\x00\x00\x00:\\r\\n (esc)
894 892 write(64 from 64) -> (80) 3a\r\n96ee1d7354c4ad7372047672c36a1f561e3a6a4c 1\npublishing True\r\n
895 893 write(9 from 9) -> (71) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
896 894 write(9 from 9) -> (62) 4\\r\\n\x00\x00\x00#\\r\\n (esc)
897 895 write(41 from 41) -> (21) 23\\r\\n\x08LISTKEYS\x00\x00\x00\x02\x01\x00 namespacebookmarks\\r\\n (esc)
898 896 write(9 from 9) -> (12) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
899 897 write(9 from 9) -> (3) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
900 898 write(3 from 5) -> (0) 0\r\n
901 899 write limit reached; closing socket
902 900 write(27) -> 15\r\nInternal Server Error\r\n
903 901
904 902 $ rm -f error.log
905 903 $ rm -rf clone
General Comments 0
You need to be logged in to leave comments. Login now