Mercurial > public > mercurial-scm > hg
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) |