From 0745bb26571e0cf097baebd48761b8cd743ec7fc Mon Sep 17 00:00:00 2001 From: Dan Willemsen Date: Mon, 17 Aug 2015 13:41:45 -0700 Subject: Support smart-sync through persistent-http[s] Use the same cookies and proxy that git traffic goes through for persistent-http[s] to support authentication for smart-sync. Change-Id: I20f4a281c259053a5a4fdbc48b1bca48e781c692 --- subcmds/sync.py | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 1 deletion(-) (limited to 'subcmds/sync.py') diff --git a/subcmds/sync.py b/subcmds/sync.py index 43d450be..ed8622c1 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py @@ -23,18 +23,26 @@ import shutil import socket import subprocess import sys +import tempfile import time from pyversion import is_python3 if is_python3(): + import http.cookiejar as cookielib + import urllib.error import urllib.parse + import urllib.request import xmlrpc.client else: + import cookielib import imp + import urllib2 import urlparse import xmlrpclib urllib = imp.new_module('urllib') + urllib.error = urllib2 urllib.parse = urlparse + urllib.request = urllib2 xmlrpc = imp.new_module('xmlrpc') xmlrpc.client = xmlrpclib @@ -57,6 +65,7 @@ except ImportError: multiprocessing = None from git_command import GIT, git_require +from git_config import GetSchemeFromUrl, GetUrlCookieFile from git_refs import R_HEADS, HEAD from project import Project from project import RemoteSpec @@ -575,8 +584,12 @@ later is required to fix a server side protocol bug. (username, password), 1) + transport = PersistentTransport(manifest_server) + if manifest_server.startswith('persistent-'): + manifest_server = manifest_server[len('persistent-'):] + try: - server = xmlrpc.client.Server(manifest_server) + server = xmlrpc.client.Server(manifest_server, transport=transport) if opt.smart_sync: p = self.manifest.manifestProject b = p.GetBranch(p.CurrentBranch) @@ -850,3 +863,93 @@ class _FetchTimes(object): os.remove(self._path) except OSError: pass + +# This is a replacement for xmlrpc.client.Transport using urllib2 +# and supporting persistent-http[s]. It cannot change hosts from +# request to request like the normal transport, the real url +# is passed during initialization. +class PersistentTransport(xmlrpc.client.Transport): + def __init__(self, orig_host): + self.orig_host = orig_host + + def request(self, host, handler, request_body, verbose=False): + with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy): + # Python doesn't understand cookies with the #HttpOnly_ prefix + # Since we're only using them for HTTP, copy the file temporarily, + # stripping those prefixes away. + tmpcookiefile = tempfile.NamedTemporaryFile() + try: + with open(cookiefile) as f: + for line in f: + if line.startswith("#HttpOnly_"): + line = line[len("#HttpOnly_"):] + tmpcookiefile.write(line) + tmpcookiefile.flush() + + cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name) + cookiejar.load() + finally: + tmpcookiefile.close() + + proxyhandler = urllib.request.ProxyHandler + if proxy: + proxyhandler = urllib.request.ProxyHandler({ + "http": proxy, + "https": proxy }) + + opener = urllib.request.build_opener( + urllib.request.HTTPCookieProcessor(cookiejar), + proxyhandler) + + url = urllib.parse.urljoin(self.orig_host, handler) + parse_results = urllib.parse.urlparse(url) + + scheme = parse_results.scheme + if scheme == 'persistent-http': + scheme = 'http' + if scheme == 'persistent-https': + # If we're proxying through persistent-https, use http. The + # proxy itself will do the https. + if proxy: + scheme = 'http' + else: + scheme = 'https' + + # Parse out any authentication information using the base class + host, extra_headers, _ = self.get_host_info(parse_results.netloc) + + url = urllib.parse.urlunparse(( + scheme, + host, + parse_results.path, + parse_results.params, + parse_results.query, + parse_results.fragment)) + + request = urllib.request.Request(url, request_body) + if extra_headers is not None: + for (name, header) in extra_headers: + request.add_header(name, header) + request.add_header('Content-Type', 'text/xml') + try: + response = opener.open(request) + except urllib.error.HTTPError as e: + if e.code == 501: + # We may have been redirected through a login process + # but our POST turned into a GET. Retry. + response = opener.open(request) + else: + raise + + p, u = xmlrpc.client.getparser() + while 1: + data = response.read(1024) + if not data: + break + p.feed(data) + p.close() + return u.close() + + def close(self): + pass + -- cgit v1.2.3-54-g00ecf