comparison mercurial/url.py @ 49277:51b07ac1991c stable

url: raise error if CONNECT request to proxy was unsuccessful The deleted code didn?t work on Python 3. On Python 2 (or Python 3 after adapting it), the function returned in the error case. The subsequent creation of SSL socket fails during handshake with a nonsense error. Instead, the user should get an error of what went wrong. I don?t see how the deleted code would be useful in the error case. The new code is also closer of what the standard library is doing nowadays that it has proxy support (which we don?t use in the moment). In the test, I use port 0 because all the HGPORTs were already taken. In practice, there should not be any server listening on port 0.
author Manuel Jacob <me@manueljacob.de>
date Sat, 04 Jun 2022 02:39:38 +0200
parents 8e5192e41e0b
children 127d33e63d1a
comparison
equal deleted inserted replaced
49276:3b102efde517 49277:51b07ac1991c
12 import base64 12 import base64
13 import socket 13 import socket
14 import sys 14 import sys
15 15
16 from .i18n import _ 16 from .i18n import _
17 from .pycompat import getattr
18 from . import ( 17 from . import (
19 encoding, 18 encoding,
20 error, 19 error,
21 httpconnection as httpconnectionmod, 20 httpconnection as httpconnectionmod,
22 keepalive, 21 keepalive,
198 197
199 class httpconnection(keepalive.HTTPConnection): 198 class httpconnection(keepalive.HTTPConnection):
200 # must be able to send big bundle as stream. 199 # must be able to send big bundle as stream.
201 send = _gen_sendfile(keepalive.HTTPConnection.send) 200 send = _gen_sendfile(keepalive.HTTPConnection.send)
202 201
203 def getresponse(self):
204 proxyres = getattr(self, 'proxyres', None)
205 if proxyres:
206 if proxyres.will_close:
207 self.close()
208 self.proxyres = None
209 return proxyres
210 return keepalive.HTTPConnection.getresponse(self)
211
212 202
213 # Large parts of this function have their origin from before Python 2.6 203 # Large parts of this function have their origin from before Python 2.6
214 # and could potentially be removed. 204 # and could potentially be removed.
215 def _generic_start_transaction(handler, h, req): 205 def _generic_start_transaction(handler, h, req):
216 tunnel_host = req._tunnel_host 206 tunnel_host = req._tunnel_host
259 version, status, reason = res._read_status() 249 version, status, reason = res._read_status()
260 if status != httplib.CONTINUE: 250 if status != httplib.CONTINUE:
261 break 251 break
262 # skip lines that are all whitespace 252 # skip lines that are all whitespace
263 list(iter(lambda: res.fp.readline().strip(), b'')) 253 list(iter(lambda: res.fp.readline().strip(), b''))
264 res.status = status 254
265 res.reason = reason.strip() 255 if status == 200:
266
267 if res.status == 200:
268 # skip lines until we find a blank line 256 # skip lines until we find a blank line
269 list(iter(res.fp.readline, b'\r\n')) 257 list(iter(res.fp.readline, b'\r\n'))
270 return True
271
272 if version == b'HTTP/1.0':
273 res.version = 10
274 elif version.startswith(b'HTTP/1.'):
275 res.version = 11
276 elif version == b'HTTP/0.9':
277 res.version = 9
278 else: 258 else:
279 raise httplib.UnknownProtocol(version) 259 self.close()
280 260 raise socket.error(
281 if res.version == 9: 261 "Tunnel connection failed: %d %s" % (status, reason.strip())
282 res.length = None 262 )
283 res.chunked = 0
284 res.will_close = 1
285 res.msg = httplib.HTTPMessage(stringio())
286 return False
287
288 res.msg = httplib.HTTPMessage(res.fp)
289 res.msg.fp = None
290
291 # are we using the chunked-style of transfer encoding?
292 trenc = res.msg.getheader(b'transfer-encoding')
293 if trenc and trenc.lower() == b"chunked":
294 res.chunked = 1
295 res.chunk_left = None
296 else:
297 res.chunked = 0
298
299 # will the connection close at the end of the response?
300 res.will_close = res._check_close()
301
302 # do we have a Content-Length?
303 # NOTE: RFC 2616, section 4.4, #3 says we ignore this if
304 # transfer-encoding is "chunked"
305 length = res.msg.getheader(b'content-length')
306 if length and not res.chunked:
307 try:
308 res.length = int(length)
309 except ValueError:
310 res.length = None
311 else:
312 if res.length < 0: # ignore nonsensical negative lengths
313 res.length = None
314 else:
315 res.length = None
316
317 # does the body have a fixed length? (of zero)
318 if (
319 status == httplib.NO_CONTENT
320 or status == httplib.NOT_MODIFIED
321 or 100 <= status < 200
322 or res._method == b'HEAD' # 1xx codes
323 ):
324 res.length = 0
325
326 # if the connection remains open, and we aren't using chunked, and
327 # a content-length was not provided, then assume that the connection
328 # WILL close.
329 if not res.will_close and not res.chunked and res.length is None:
330 res.will_close = 1
331
332 self.proxyres = res
333
334 return False
335 263
336 264
337 class httphandler(keepalive.HTTPHandler): 265 class httphandler(keepalive.HTTPHandler):
338 def http_open(self, req): 266 def http_open(self, req):
339 return self.do_open(httpconnection, req) 267 return self.do_open(httpconnection, req)