httpconnection.py
140 lines
| 4.2 KiB
| text/x-python
|
PythonLexer
/ mercurial / httpconnection.py
Augie Fackler
|
r14244 | # httpconnection.py - urllib2 handler for new http support | ||
# | ||||
Raphaël Gomès
|
r47575 | # Copyright 2005, 2006, 2007, 2008 Olivia Mackall <olivia@selenic.com> | ||
Augie Fackler
|
r14244 | # Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br> | ||
# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com> | ||||
# Copyright 2011 Google, Inc. | ||||
# | ||||
# This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | ||||
Gregory Szorc
|
r27521 | |||
Matt Harbison
|
r52756 | from __future__ import annotations | ||
Gregory Szorc
|
r27521 | |||
import os | ||||
Augie Fackler
|
r14244 | |||
Gregory Szorc
|
r27521 | from .i18n import _ | ||
Gregory Szorc
|
r43355 | from .pycompat import open | ||
Gregory Szorc
|
r27521 | from . import ( | ||
Augie Fackler
|
r36669 | pycompat, | ||
Gregory Szorc
|
r27521 | util, | ||
) | ||||
r47669 | from .utils import ( | |||
urlutil, | ||||
) | ||||
Augie Fackler
|
r14244 | |||
timeless
|
r28883 | urlerr = util.urlerr | ||
urlreq = util.urlreq | ||||
Raphaël Gomès
|
r52596 | |||
Augie Fackler
|
r14244 | # moved here from url.py to avoid a cycle | ||
Gregory Szorc
|
r49801 | class httpsendfile: | ||
Augie Fackler
|
r14244 | """This is a wrapper around the objects returned by python's "open". | ||
Mads Kiilerich
|
r15152 | Its purpose is to send file-like objects via HTTP. | ||
It do however not define a __len__ attribute because the length | ||||
might be more than Py_ssize_t can handle. | ||||
Augie Fackler
|
r14244 | """ | ||
def __init__(self, ui, *args, **kwargs): | ||||
self.ui = ui | ||||
self._data = open(*args, **kwargs) | ||||
self.seek = self._data.seek | ||||
self.close = self._data.close | ||||
self.write = self._data.write | ||||
Mads Kiilerich
|
r15152 | self.length = os.fstat(self._data.fileno()).st_size | ||
Augie Fackler
|
r14244 | self._pos = 0 | ||
Matt Harbison
|
r44780 | self._progress = self._makeprogress() | ||
def _makeprogress(self): | ||||
Augie Fackler
|
r14244 | # We pass double the max for total because we currently have | ||
# to send the bundle twice in the case of a server that | ||||
# requires authentication. Since we can't know until we try | ||||
# once whether authentication will be required, just lie to | ||||
# the user and maybe the push succeeds suddenly at 50%. | ||||
Matt Harbison
|
r44780 | return self.ui.makeprogress( | ||
Augie Fackler
|
r43347 | _(b'sending'), unit=_(b'kb'), total=(self.length // 1024 * 2) | ||
Augie Fackler
|
r43345 | ) | ||
Martin von Zweigbergk
|
r38412 | |||
def read(self, *args, **kwargs): | ||||
ret = self._data.read(*args, **kwargs) | ||||
if not ret: | ||||
self._progress.complete() | ||||
return ret | ||||
self._pos += len(ret) | ||||
self._progress.update(self._pos // 1024) | ||||
Augie Fackler
|
r14244 | return ret | ||
Mads Kiilerich
|
r30142 | def __enter__(self): | ||
return self | ||||
def __exit__(self, exc_type, exc_val, exc_tb): | ||||
self.close() | ||||
Augie Fackler
|
r43345 | |||
Augie Fackler
|
r14244 | # moved here from url.py to avoid a cycle | ||
Patrick Mezard
|
r15025 | def readauthforuri(ui, uri, user): | ||
Augie Fackler
|
r36669 | uri = pycompat.bytesurl(uri) | ||
Augie Fackler
|
r14244 | # Read configuration | ||
Gregory Szorc
|
r31300 | groups = {} | ||
Augie Fackler
|
r43347 | for key, val in ui.configitems(b'auth'): | ||
if key in (b'cookiefile',): | ||||
Gregory Szorc
|
r31935 | continue | ||
Augie Fackler
|
r43347 | if b'.' not in key: | ||
ui.warn(_(b"ignoring invalid [auth] key '%s'\n") % key) | ||||
Augie Fackler
|
r14244 | continue | ||
Augie Fackler
|
r43347 | group, setting = key.rsplit(b'.', 1) | ||
Gregory Szorc
|
r31300 | gdict = groups.setdefault(group, {}) | ||
Augie Fackler
|
r43347 | if setting in (b'username', b'cert', b'key'): | ||
Augie Fackler
|
r14244 | val = util.expandpath(val) | ||
gdict[setting] = val | ||||
# Find the best match | ||||
Augie Fackler
|
r43347 | scheme, hostpath = uri.split(b'://', 1) | ||
Patrick Mezard
|
r15005 | bestuser = None | ||
Augie Fackler
|
r14244 | bestlen = 0 | ||
bestauth = None | ||||
Gregory Szorc
|
r49768 | for group, auth in groups.items(): | ||
Augie Fackler
|
r43347 | if user and user != auth.get(b'username', user): | ||
Patrick Mezard
|
r15005 | # If a username was set in the URI, the entry username | ||
# must either match it or be unset | ||||
continue | ||||
Augie Fackler
|
r43347 | prefix = auth.get(b'prefix') | ||
Augie Fackler
|
r14244 | if not prefix: | ||
continue | ||||
Matt Harbison
|
r40699 | |||
r47669 | prefixurl = urlutil.url(prefix) | |||
Matt Harbison
|
r40699 | if prefixurl.user and prefixurl.user != user: | ||
# If a username was set in the prefix, it must match the username in | ||||
# the URI. | ||||
continue | ||||
# The URI passed in has been stripped of credentials, so erase the user | ||||
# here to allow simpler matching. | ||||
prefixurl.user = None | ||||
prefix = bytes(prefixurl) | ||||
Augie Fackler
|
r43347 | p = prefix.split(b'://', 1) | ||
Augie Fackler
|
r14244 | if len(p) > 1: | ||
schemes, prefix = [p[0]], p[1] | ||||
else: | ||||
Augie Fackler
|
r43347 | schemes = (auth.get(b'schemes') or b'https').split() | ||
Augie Fackler
|
r43345 | if ( | ||
Augie Fackler
|
r43347 | (prefix == b'*' or hostpath.startswith(prefix)) | ||
Augie Fackler
|
r43345 | and ( | ||
len(prefix) > bestlen | ||||
or ( | ||||
len(prefix) == bestlen | ||||
and not bestuser | ||||
Augie Fackler
|
r43347 | and b'username' in auth | ||
Augie Fackler
|
r43345 | ) | ||
) | ||||
and scheme in schemes | ||||
): | ||||
Augie Fackler
|
r14244 | bestlen = len(prefix) | ||
bestauth = group, auth | ||||
Augie Fackler
|
r43347 | bestuser = auth.get(b'username') | ||
Patrick Mezard
|
r15005 | if user and not bestuser: | ||
Augie Fackler
|
r43347 | auth[b'username'] = user | ||
Augie Fackler
|
r14244 | return bestauth | ||