summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Willemsen <dwillemsen@google.com>2015-08-19 23:36:35 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2015-08-19 23:36:35 +0000
commit7c9263bce09725227dc518ee298b350becbdf26e (patch)
treec2547dbfca738c2da6763fb5fe59195f4ee10658
parentdab9e99f0f6d4f82e0e61b37396f7c0915a8d508 (diff)
parent0745bb26571e0cf097baebd48761b8cd743ec7fc (diff)
downloadgit-repo-7c9263bce09725227dc518ee298b350becbdf26e.tar.gz
Merge "Support smart-sync through persistent-http[s]"
-rw-r--r--git_config.py39
-rw-r--r--project.py39
-rw-r--r--subcmds/sync.py105
3 files changed, 145 insertions, 38 deletions
diff --git a/git_config.py b/git_config.py
index 8ded7c25..0379181a 100644
--- a/git_config.py
+++ b/git_config.py
@@ -15,6 +15,8 @@
15 15
16from __future__ import print_function 16from __future__ import print_function
17 17
18import contextlib
19import errno
18import json 20import json
19import os 21import os
20import re 22import re
@@ -502,6 +504,43 @@ def GetSchemeFromUrl(url):
502 return m.group(1) 504 return m.group(1)
503 return None 505 return None
504 506
507@contextlib.contextmanager
508def GetUrlCookieFile(url, quiet):
509 if url.startswith('persistent-'):
510 try:
511 p = subprocess.Popen(
512 ['git-remote-persistent-https', '-print_config', url],
513 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
514 stderr=subprocess.PIPE)
515 try:
516 cookieprefix = 'http.cookiefile='
517 proxyprefix = 'http.proxy='
518 cookiefile = None
519 proxy = None
520 for line in p.stdout:
521 line = line.strip()
522 if line.startswith(cookieprefix):
523 cookiefile = line[len(cookieprefix):]
524 if line.startswith(proxyprefix):
525 proxy = line[len(proxyprefix):]
526 # Leave subprocess open, as cookie file may be transient.
527 if cookiefile or proxy:
528 yield cookiefile, proxy
529 return
530 finally:
531 p.stdin.close()
532 if p.wait():
533 err_msg = p.stderr.read()
534 if ' -print_config' in err_msg:
535 pass # Persistent proxy doesn't support -print_config.
536 elif not quiet:
537 print(err_msg, file=sys.stderr)
538 except OSError as e:
539 if e.errno == errno.ENOENT:
540 pass # No persistent proxy.
541 raise
542 yield GitConfig.ForUser().GetString('http.cookiefile'), None
543
505def _preconnect(url): 544def _preconnect(url):
506 m = URI_ALL.match(url) 545 m = URI_ALL.match(url)
507 if m: 546 if m:
diff --git a/project.py b/project.py
index d82f3d72..24cac8bd 100644
--- a/project.py
+++ b/project.py
@@ -13,7 +13,6 @@
13# limitations under the License. 13# limitations under the License.
14 14
15from __future__ import print_function 15from __future__ import print_function
16import contextlib
17import errno 16import errno
18import filecmp 17import filecmp
19import glob 18import glob
@@ -31,7 +30,7 @@ import traceback
31 30
32from color import Coloring 31from color import Coloring
33from git_command import GitCommand, git_require 32from git_command import GitCommand, git_require
34from git_config import GitConfig, IsId, GetSchemeFromUrl, ID_RE 33from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, ID_RE
35from error import GitError, HookError, UploadError, DownloadError 34from error import GitError, HookError, UploadError, DownloadError
36from error import ManifestInvalidRevisionError 35from error import ManifestInvalidRevisionError
37from error import NoManifestException 36from error import NoManifestException
@@ -2030,7 +2029,7 @@ class Project(object):
2030 os.remove(tmpPath) 2029 os.remove(tmpPath)
2031 if 'http_proxy' in os.environ and 'darwin' == sys.platform: 2030 if 'http_proxy' in os.environ and 'darwin' == sys.platform:
2032 cmd += ['--proxy', os.environ['http_proxy']] 2031 cmd += ['--proxy', os.environ['http_proxy']]
2033 with self._GetBundleCookieFile(srcUrl, quiet) as cookiefile: 2032 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, proxy):
2034 if cookiefile: 2033 if cookiefile:
2035 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile] 2034 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
2036 if srcUrl.startswith('persistent-'): 2035 if srcUrl.startswith('persistent-'):
@@ -2078,40 +2077,6 @@ class Project(object):
2078 except OSError: 2077 except OSError:
2079 return False 2078 return False
2080 2079
2081 @contextlib.contextmanager
2082 def _GetBundleCookieFile(self, url, quiet):
2083 if url.startswith('persistent-'):
2084 try:
2085 p = subprocess.Popen(
2086 ['git-remote-persistent-https', '-print_config', url],
2087 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
2088 stderr=subprocess.PIPE)
2089 try:
2090 prefix = 'http.cookiefile='
2091 cookiefile = None
2092 for line in p.stdout:
2093 line = line.strip()
2094 if line.startswith(prefix):
2095 cookiefile = line[len(prefix):]
2096 break
2097 # Leave subprocess open, as cookie file may be transient.
2098 if cookiefile:
2099 yield cookiefile
2100 return
2101 finally:
2102 p.stdin.close()
2103 if p.wait():
2104 err_msg = p.stderr.read()
2105 if ' -print_config' in err_msg:
2106 pass # Persistent proxy doesn't support -print_config.
2107 elif not quiet:
2108 print(err_msg, file=sys.stderr)
2109 except OSError as e:
2110 if e.errno == errno.ENOENT:
2111 pass # No persistent proxy.
2112 raise
2113 yield GitConfig.ForUser().GetString('http.cookiefile')
2114
2115 def _Checkout(self, rev, quiet=False): 2080 def _Checkout(self, rev, quiet=False):
2116 cmd = ['checkout'] 2081 cmd = ['checkout']
2117 if quiet: 2082 if quiet:
diff --git a/subcmds/sync.py b/subcmds/sync.py
index 652a0c0d..0fc493c4 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -23,18 +23,26 @@ import shutil
23import socket 23import socket
24import subprocess 24import subprocess
25import sys 25import sys
26import tempfile
26import time 27import time
27 28
28from pyversion import is_python3 29from pyversion import is_python3
29if is_python3(): 30if is_python3():
31 import http.cookiejar as cookielib
32 import urllib.error
30 import urllib.parse 33 import urllib.parse
34 import urllib.request
31 import xmlrpc.client 35 import xmlrpc.client
32else: 36else:
37 import cookielib
33 import imp 38 import imp
39 import urllib2
34 import urlparse 40 import urlparse
35 import xmlrpclib 41 import xmlrpclib
36 urllib = imp.new_module('urllib') 42 urllib = imp.new_module('urllib')
43 urllib.error = urllib2
37 urllib.parse = urlparse 44 urllib.parse = urlparse
45 urllib.request = urllib2
38 xmlrpc = imp.new_module('xmlrpc') 46 xmlrpc = imp.new_module('xmlrpc')
39 xmlrpc.client = xmlrpclib 47 xmlrpc.client = xmlrpclib
40 48
@@ -57,6 +65,7 @@ except ImportError:
57 multiprocessing = None 65 multiprocessing = None
58 66
59from git_command import GIT, git_require 67from git_command import GIT, git_require
68from git_config import GetSchemeFromUrl, GetUrlCookieFile
60from git_refs import R_HEADS, HEAD 69from git_refs import R_HEADS, HEAD
61import gitc_utils 70import gitc_utils
62from project import Project 71from project import Project
@@ -598,8 +607,12 @@ later is required to fix a server side protocol bug.
598 (username, password), 607 (username, password),
599 1) 608 1)
600 609
610 transport = PersistentTransport(manifest_server)
611 if manifest_server.startswith('persistent-'):
612 manifest_server = manifest_server[len('persistent-'):]
613
601 try: 614 try:
602 server = xmlrpc.client.Server(manifest_server) 615 server = xmlrpc.client.Server(manifest_server, transport=transport)
603 if opt.smart_sync: 616 if opt.smart_sync:
604 p = self.manifest.manifestProject 617 p = self.manifest.manifestProject
605 b = p.GetBranch(p.CurrentBranch) 618 b = p.GetBranch(p.CurrentBranch)
@@ -879,3 +892,93 @@ class _FetchTimes(object):
879 os.remove(self._path) 892 os.remove(self._path)
880 except OSError: 893 except OSError:
881 pass 894 pass
895
896# This is a replacement for xmlrpc.client.Transport using urllib2
897# and supporting persistent-http[s]. It cannot change hosts from
898# request to request like the normal transport, the real url
899# is passed during initialization.
900class PersistentTransport(xmlrpc.client.Transport):
901 def __init__(self, orig_host):
902 self.orig_host = orig_host
903
904 def request(self, host, handler, request_body, verbose=False):
905 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
906 # Python doesn't understand cookies with the #HttpOnly_ prefix
907 # Since we're only using them for HTTP, copy the file temporarily,
908 # stripping those prefixes away.
909 tmpcookiefile = tempfile.NamedTemporaryFile()
910 try:
911 with open(cookiefile) as f:
912 for line in f:
913 if line.startswith("#HttpOnly_"):
914 line = line[len("#HttpOnly_"):]
915 tmpcookiefile.write(line)
916 tmpcookiefile.flush()
917
918 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
919 cookiejar.load()
920 finally:
921 tmpcookiefile.close()
922
923 proxyhandler = urllib.request.ProxyHandler
924 if proxy:
925 proxyhandler = urllib.request.ProxyHandler({
926 "http": proxy,
927 "https": proxy })
928
929 opener = urllib.request.build_opener(
930 urllib.request.HTTPCookieProcessor(cookiejar),
931 proxyhandler)
932
933 url = urllib.parse.urljoin(self.orig_host, handler)
934 parse_results = urllib.parse.urlparse(url)
935
936 scheme = parse_results.scheme
937 if scheme == 'persistent-http':
938 scheme = 'http'
939 if scheme == 'persistent-https':
940 # If we're proxying through persistent-https, use http. The
941 # proxy itself will do the https.
942 if proxy:
943 scheme = 'http'
944 else:
945 scheme = 'https'
946
947 # Parse out any authentication information using the base class
948 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
949
950 url = urllib.parse.urlunparse((
951 scheme,
952 host,
953 parse_results.path,
954 parse_results.params,
955 parse_results.query,
956 parse_results.fragment))
957
958 request = urllib.request.Request(url, request_body)
959 if extra_headers is not None:
960 for (name, header) in extra_headers:
961 request.add_header(name, header)
962 request.add_header('Content-Type', 'text/xml')
963 try:
964 response = opener.open(request)
965 except urllib.error.HTTPError as e:
966 if e.code == 501:
967 # We may have been redirected through a login process
968 # but our POST turned into a GET. Retry.
969 response = opener.open(request)
970 else:
971 raise
972
973 p, u = xmlrpc.client.getparser()
974 while 1:
975 data = response.read(1024)
976 if not data:
977 break
978 p.feed(data)
979 p.close()
980 return u.close()
981
982 def close(self):
983 pass
984