Compare commits

...

5 Commits

Author SHA1 Message Date
1a68dc58eb upload: Honor REPO_HOST_PORT_INFO environment variable
REPO_HOST_PORT_INFO can be set to 'host:port' and be used
instead of the review URL given in the manifest.

Change-Id: I440bdecb2c2249fe5285ec5d0c28a937b4053450
Signed-off-by: Shawn O. Pearce <sop@google.com>
2011-10-11 14:12:46 -07:00
df5ee52050 Fix Python 2.4 support
Change-Id: I89521ae52fa564f0d849cc51e71fee65b3c47bab
Signed-off-by: Shawn O. Pearce <sop@google.com>
2011-10-11 14:06:11 -07:00
fab96c68e3 Work around Python 2.7 urllib2 bug
If the remote is using authenticated HTTP, but does not have
$GIT_URL/clone.bundle files in each repository, an initial sync
would fail around 8 projects in due to the library not resetting
the number of failures after getting a 404.

Work around this by updating the retry counter ourselves.

The urllib2 library is also not thread-safe. Make it somewhat
safer by wrapping the critical section with a lock.

Change-Id: I886e2750ef4793cbe2150c3b5396eb9f10974f7f
Signed-off-by: Shawn O. Pearce <sop@google.com>
2011-10-11 12:18:07 -07:00
bf1fbb20ab Fix AttributeError: 'HTTPError' object has no attribute 'reason'
Not every version of urllib2 supplies a reason object on the
HTTPError exception that it throws from urlopen().  Work around
this by using str(e) instead and hope the string formatting includes
sufficient information.

Change-Id: I0f4586dba0aa7152691b2371627c951f91fdfc8d
Signed-off-by: Shawn O. Pearce <sop@google.com>
2011-10-11 09:31:58 -07:00
29472463ba Work around Python 2.7 failure to initialize base class
urllib2 returns a malformed HTTPError object in certain situations.
For example, urllib2 has a couple of places where it creates an
HTTPError object with no fp:

  if self.retried > 5:
    # retry sending the username:password 5 times before failing.
    raise HTTPError(req.get_full_url(), 401, "basic auth failed",
                    headers, None)

When it does that, HTTPError's ctor doesn't call through to
addinfourl's ctor:

  # The addinfourl classes depend on fp being a valid file
  # object.  In some cases, the HTTPError may not have a valid
  # file object.  If this happens, the simplest workaround is to
  # not initialize the base classes.
  if fp is not None:
    self.__super_init(fp, hdrs, url, code)

Which means the 'headers' slot in addinfourl is not initialized and
info() fails.  It is completely insane that urllib2 decides not to
initialize its own base class sometimes.

Change-Id: I32a0d738f71bdd7d38d86078b71d9001e26f1ec3
Signed-off-by: Shawn O. Pearce <sop@google.com>
2011-10-11 09:24:07 -07:00
4 changed files with 86 additions and 43 deletions

View File

@ -26,7 +26,6 @@ import time
import urllib2 import urllib2
from signal import SIGTERM from signal import SIGTERM
from urllib2 import urlopen, HTTPError
from error import GitError, UploadError from error import GitError, UploadError
from trace import Trace from trace import Trace
@ -576,9 +575,19 @@ class Remote(object):
self._review_protocol = info[0] self._review_protocol = info[0]
self._review_host = info[1] self._review_host = info[1]
self._review_port = info[2] self._review_port = info[2]
elif 'REPO_HOST_PORT_INFO' in os.environ:
info = os.environ['REPO_HOST_PORT_INFO']
self._review_protocol = 'ssh'
self._review_host = info.split(" ")[0]
self._review_port = info.split(" ")[1]
REVIEW_CACHE[u] = (
self._review_protocol,
self._review_host,
self._review_port)
else: else:
try: try:
info = urlopen(u).read() info = urllib2.urlopen(u).read()
if info == 'NOT_AVAILABLE': if info == 'NOT_AVAILABLE':
raise UploadError('%s: SSH disabled' % self.review) raise UploadError('%s: SSH disabled' % self.review)
if '<' in info: if '<' in info:
@ -590,15 +599,15 @@ class Remote(object):
self._review_protocol = 'ssh' self._review_protocol = 'ssh'
self._review_host = info.split(" ")[0] self._review_host = info.split(" ")[0]
self._review_port = info.split(" ")[1] self._review_port = info.split(" ")[1]
except urllib2.URLError, e: except urllib2.HTTPError, e:
raise UploadError('%s: %s' % (self.review, e.reason[1]))
except HTTPError, e:
if e.code == 404: if e.code == 404:
self._review_protocol = 'http-post' self._review_protocol = 'http-post'
self._review_host = None self._review_host = None
self._review_port = None self._review_port = None
else: else:
raise UploadError('Upload over ssh unavailable') raise UploadError('Upload over SSH unavailable')
except urllib2.URLError, e:
raise UploadError('%s: %s' % (self.review, str(e)))
REVIEW_CACHE[u] = ( REVIEW_CACHE[u] = (
self._review_protocol, self._review_protocol,

18
main.py
View File

@ -273,6 +273,22 @@ class _UserAgentHandler(urllib2.BaseHandler):
req.add_header('User-Agent', _UserAgent()) req.add_header('User-Agent', _UserAgent())
return req return req
class _BasicAuthHandler(urllib2.HTTPBasicAuthHandler):
def http_error_auth_reqed(self, authreq, host, req, headers):
try:
old_add_header = req.add_header
def _add_header(name, val):
val = val.replace('\n', '')
old_add_header(name, val)
req.add_header = _add_header
return urllib2.AbstractBasicAuthHandler.http_error_auth_reqed(
self, authreq, host, req, headers)
except:
reset = getattr(self, 'reset_retry_count', None)
if reset is not None:
reset()
raise
def init_http(): def init_http():
handlers = [_UserAgentHandler()] handlers = [_UserAgentHandler()]
@ -287,7 +303,7 @@ def init_http():
pass pass
except IOError: except IOError:
pass pass
handlers.append(urllib2.HTTPBasicAuthHandler(mgr)) handlers.append(_BasicAuthHandler(mgr))
if 'http_proxy' in os.environ: if 'http_proxy' in os.environ:
url = os.environ['http_proxy'] url = os.environ['http_proxy']

View File

@ -24,6 +24,16 @@ import sys
import time import time
import urllib2 import urllib2
try:
import threading as _threading
except ImportError:
import dummy_threading as _threading
try:
from os import SEEK_END
except ImportError:
SEEK_END = 2
from color import Coloring from color import Coloring
from git_command import GitCommand from git_command import GitCommand
from git_config import GitConfig, IsId, GetSchemeFromUrl from git_config import GitConfig, IsId, GetSchemeFromUrl
@ -34,6 +44,8 @@ from progress import Progress
from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
_urllib_lock = _threading.Lock()
def _lwrite(path, content): def _lwrite(path, content):
lock = '%s.lock' % path lock = '%s.lock' % path
@ -1455,37 +1467,47 @@ class Project(object):
done = False done = False
dest = open(tmpPath, 'a+b') dest = open(tmpPath, 'a+b')
try: try:
dest.seek(0, os.SEEK_END) dest.seek(0, SEEK_END)
pos = dest.tell() pos = dest.tell()
req = urllib2.Request(srcUrl) _urllib_lock.acquire()
if pos > 0:
req.add_header('Range', 'bytes=%d-' % pos)
try: try:
r = urllib2.urlopen(req) req = urllib2.Request(srcUrl)
except urllib2.HTTPError, e: if pos > 0:
if e.code == 404: req.add_header('Range', 'bytes=%d-' % pos)
keep = False
return False try:
elif e.info()['content-type'] == 'text/plain': r = urllib2.urlopen(req)
try: except urllib2.HTTPError, e:
msg = e.read() def _content_type():
if len(msg) > 0 and msg[-1] == '\n': try:
msg = msg[0:-1] return e.info()['content-type']
msg = ' (%s)' % msg except:
except: return None
msg = ''
else: if e.code == 404:
try: keep = False
from BaseHTTPServer import BaseHTTPRequestHandler return False
res = BaseHTTPRequestHandler.responses[e.code] elif _content_type() == 'text/plain':
msg = ' (%s: %s)' % (res[0], res[1]) try:
except: msg = e.read()
msg = '' if len(msg) > 0 and msg[-1] == '\n':
raise DownloadError('HTTP %s%s' % (e.code, msg)) msg = msg[0:-1]
except urllib2.URLError, e: msg = ' (%s)' % msg
raise DownloadError('%s (%s)' % (e.reason, req.get_host())) except:
msg = ''
else:
try:
from BaseHTTPServer import BaseHTTPRequestHandler
res = BaseHTTPRequestHandler.responses[e.code]
msg = ' (%s: %s)' % (res[0], res[1])
except:
msg = ''
raise DownloadError('HTTP %s%s' % (e.code, msg))
except urllib2.URLError, e:
raise DownloadError('%s: %s ' % (req.get_host(), str(e)))
finally:
_urllib_lock.release()
p = None p = None
try: try:

View File

@ -195,15 +195,11 @@ later is required to fix a server side protocol bug.
fetched.add(project.gitdir) fetched.add(project.gitdir)
pm.update() pm.update()
except BaseException, e: except _FetchError:
# Notify the _Fetch() function about all errors.
err_event.set() err_event.set()
except:
# If we got our own _FetchError, we don't want a stack trace. err_event.set()
# However, if we got something else (something in Sync_NetworkHalf?), raise
# we'd like one (so re-raise after we've set err_event).
if not isinstance(e, _FetchError):
raise
finally: finally:
if did_lock: if did_lock:
lock.release() lock.release()