##// END OF EJS Templates
httprepo: order URL query string fields for readability...
Steven Brown -
r13555:970150dd default
parent child Browse files
Show More
@@ -1,203 +1,202 b''
1 1 # httprepo.py - HTTP repository proxy classes for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 from node import nullid
10 10 from i18n import _
11 11 import changegroup, statichttprepo, error, url, util, wireproto
12 12 import os, urllib, urllib2, urlparse, zlib, httplib
13 13 import errno, socket
14 14
15 15 def zgenerator(f):
16 16 zd = zlib.decompressobj()
17 17 try:
18 18 for chunk in util.filechunkiter(f):
19 19 while chunk:
20 20 yield zd.decompress(chunk, 2**18)
21 21 chunk = zd.unconsumed_tail
22 22 except httplib.HTTPException:
23 23 raise IOError(None, _('connection ended unexpectedly'))
24 24 yield zd.flush()
25 25
26 26 class httprepository(wireproto.wirerepository):
27 27 def __init__(self, ui, path):
28 28 self.path = path
29 29 self.caps = None
30 30 self.handler = None
31 31 scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
32 32 if query or frag:
33 33 raise util.Abort(_('unsupported URL component: "%s"') %
34 34 (query or frag))
35 35
36 36 # urllib cannot handle URLs with embedded user or passwd
37 37 self._url, authinfo = url.getauthinfo(path)
38 38
39 39 self.ui = ui
40 40 self.ui.debug('using %s\n' % self._url)
41 41
42 42 self.urlopener = url.opener(ui, authinfo)
43 43
44 44 def __del__(self):
45 45 for h in self.urlopener.handlers:
46 46 h.close()
47 47 if hasattr(h, "close_all"):
48 48 h.close_all()
49 49
50 50 def url(self):
51 51 return self.path
52 52
53 53 # look up capabilities only when needed
54 54
55 55 def get_caps(self):
56 56 if self.caps is None:
57 57 try:
58 58 self.caps = set(self._call('capabilities').split())
59 59 except error.RepoError:
60 60 self.caps = set()
61 61 self.ui.debug('capabilities: %s\n' %
62 62 (' '.join(self.caps or ['none'])))
63 63 return self.caps
64 64
65 65 capabilities = property(get_caps)
66 66
67 67 def lock(self):
68 68 raise util.Abort(_('operation not supported over http'))
69 69
70 70 def _callstream(self, cmd, **args):
71 71 if cmd == 'pushkey':
72 72 args['data'] = ''
73 73 data = args.pop('data', None)
74 74 headers = args.pop('headers', {})
75 75 self.ui.debug("sending %s command\n" % cmd)
76 q = {"cmd": cmd}
77 q.update(args)
76 q = [('cmd', cmd)] + sorted(args.items())
78 77 qs = '?%s' % urllib.urlencode(q)
79 78 cu = "%s%s" % (self._url, qs)
80 79 req = urllib2.Request(cu, data, headers)
81 80 if data is not None:
82 81 # len(data) is broken if data doesn't fit into Py_ssize_t
83 82 # add the header ourself to avoid OverflowError
84 83 size = data.__len__()
85 84 self.ui.debug("sending %s bytes\n" % size)
86 85 req.add_unredirected_header('Content-Length', '%d' % size)
87 86 try:
88 87 resp = self.urlopener.open(req)
89 88 except urllib2.HTTPError, inst:
90 89 if inst.code == 401:
91 90 raise util.Abort(_('authorization failed'))
92 91 raise
93 92 except httplib.HTTPException, inst:
94 93 self.ui.debug('http error while sending %s command\n' % cmd)
95 94 self.ui.traceback()
96 95 raise IOError(None, inst)
97 96 except IndexError:
98 97 # this only happens with Python 2.3, later versions raise URLError
99 98 raise util.Abort(_('http error, possibly caused by proxy setting'))
100 99 # record the url we got redirected to
101 100 resp_url = resp.geturl()
102 101 if resp_url.endswith(qs):
103 102 resp_url = resp_url[:-len(qs)]
104 103 if self._url.rstrip('/') != resp_url.rstrip('/'):
105 104 self.ui.status(_('real URL is %s\n') % resp_url)
106 105 self._url = resp_url
107 106 try:
108 107 proto = resp.getheader('content-type')
109 108 except AttributeError:
110 109 proto = resp.headers['content-type']
111 110
112 111 safeurl = url.hidepassword(self._url)
113 112 # accept old "text/plain" and "application/hg-changegroup" for now
114 113 if not (proto.startswith('application/mercurial-') or
115 114 proto.startswith('text/plain') or
116 115 proto.startswith('application/hg-changegroup')):
117 116 self.ui.debug("requested URL: '%s'\n" % url.hidepassword(cu))
118 117 raise error.RepoError(
119 118 _("'%s' does not appear to be an hg repository:\n"
120 119 "---%%<--- (%s)\n%s\n---%%<---\n")
121 120 % (safeurl, proto, resp.read()))
122 121
123 122 if proto.startswith('application/mercurial-'):
124 123 try:
125 124 version = proto.split('-', 1)[1]
126 125 version_info = tuple([int(n) for n in version.split('.')])
127 126 except ValueError:
128 127 raise error.RepoError(_("'%s' sent a broken Content-Type "
129 128 "header (%s)") % (safeurl, proto))
130 129 if version_info > (0, 1):
131 130 raise error.RepoError(_("'%s' uses newer protocol %s") %
132 131 (safeurl, version))
133 132
134 133 return resp
135 134
136 135 def _call(self, cmd, **args):
137 136 fp = self._callstream(cmd, **args)
138 137 try:
139 138 return fp.read()
140 139 finally:
141 140 # if using keepalive, allow connection to be reused
142 141 fp.close()
143 142
144 143 def _callpush(self, cmd, cg, **args):
145 144 # have to stream bundle to a temp file because we do not have
146 145 # http 1.1 chunked transfer.
147 146
148 147 type = ""
149 148 types = self.capable('unbundle')
150 149 # servers older than d1b16a746db6 will send 'unbundle' as a
151 150 # boolean capability
152 151 try:
153 152 types = types.split(',')
154 153 except AttributeError:
155 154 types = [""]
156 155 if types:
157 156 for x in types:
158 157 if x in changegroup.bundletypes:
159 158 type = x
160 159 break
161 160
162 161 tempname = changegroup.writebundle(cg, None, type)
163 162 fp = url.httpsendfile(self.ui, tempname, "rb")
164 163 headers = {'Content-Type': 'application/mercurial-0.1'}
165 164
166 165 try:
167 166 try:
168 167 r = self._call(cmd, data=fp, headers=headers, **args)
169 168 return r.split('\n', 1)
170 169 except socket.error, err:
171 170 if err.args[0] in (errno.ECONNRESET, errno.EPIPE):
172 171 raise util.Abort(_('push failed: %s') % err.args[1])
173 172 raise util.Abort(err.args[1])
174 173 finally:
175 174 fp.close()
176 175 os.unlink(tempname)
177 176
178 177 def _abort(self, exception):
179 178 raise exception
180 179
181 180 def _decompress(self, stream):
182 181 return util.chunkbuffer(zgenerator(stream))
183 182
184 183 class httpsrepository(httprepository):
185 184 def __init__(self, ui, path):
186 185 if not url.has_https:
187 186 raise util.Abort(_('Python support for SSL and HTTPS '
188 187 'is not installed'))
189 188 httprepository.__init__(self, ui, path)
190 189
191 190 def instance(ui, path, create):
192 191 if create:
193 192 raise util.Abort(_('cannot create new http repository'))
194 193 try:
195 194 if path.startswith('https:'):
196 195 inst = httpsrepository(ui, path)
197 196 else:
198 197 inst = httprepository(ui, path)
199 198 inst.between([(nullid, nullid)])
200 199 return inst
201 200 except error.RepoError:
202 201 ui.note('(falling back to static-http)\n')
203 202 return statichttprepo.instance(ui, "static-" + path, create)
@@ -1,124 +1,124 b''
1 1
2 2 $ hg init a
3 3 $ cd a
4 4 $ echo a > a
5 5 $ hg ci -Ama -d '1123456789 0'
6 6 adding a
7 7 $ hg --config server.uncompressed=True serve -p $HGPORT -d --pid-file=hg.pid
8 8 $ cat hg.pid >> $DAEMON_PIDS
9 9 $ cd ..
10 10 $ ("$TESTDIR/tinyproxy.py" $HGPORT1 localhost >proxy.log 2>&1 </dev/null &
11 11 $ echo $! > proxy.pid)
12 12 $ cat proxy.pid >> $DAEMON_PIDS
13 13 $ sleep 2
14 14
15 15 url for proxy, stream
16 16
17 17 $ http_proxy=http://localhost:$HGPORT1/ hg --config http_proxy.always=True clone --uncompressed http://localhost:$HGPORT/ b
18 18 streaming all changes
19 19 3 files to transfer, 303 bytes of data
20 20 transferred * bytes in * seconds (*B/sec) (glob)
21 21 updating to branch default
22 22 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
23 23 $ cd b
24 24 $ hg verify
25 25 checking changesets
26 26 checking manifests
27 27 crosschecking files in changesets and manifests
28 28 checking files
29 29 1 files, 1 changesets, 1 total revisions
30 30 $ cd ..
31 31
32 32 url for proxy, pull
33 33
34 34 $ http_proxy=http://localhost:$HGPORT1/ hg --config http_proxy.always=True clone http://localhost:$HGPORT/ b-pull
35 35 requesting all changes
36 36 adding changesets
37 37 adding manifests
38 38 adding file changes
39 39 added 1 changesets with 1 changes to 1 files
40 40 updating to branch default
41 41 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
42 42 $ cd b-pull
43 43 $ hg verify
44 44 checking changesets
45 45 checking manifests
46 46 crosschecking files in changesets and manifests
47 47 checking files
48 48 1 files, 1 changesets, 1 total revisions
49 49 $ cd ..
50 50
51 51 host:port for proxy
52 52
53 53 $ http_proxy=localhost:$HGPORT1 hg clone --config http_proxy.always=True http://localhost:$HGPORT/ c
54 54 requesting all changes
55 55 adding changesets
56 56 adding manifests
57 57 adding file changes
58 58 added 1 changesets with 1 changes to 1 files
59 59 updating to branch default
60 60 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
61 61
62 62 proxy url with user name and password
63 63
64 64 $ http_proxy=http://user:passwd@localhost:$HGPORT1 hg clone --config http_proxy.always=True http://localhost:$HGPORT/ d
65 65 requesting all changes
66 66 adding changesets
67 67 adding manifests
68 68 adding file changes
69 69 added 1 changesets with 1 changes to 1 files
70 70 updating to branch default
71 71 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
72 72
73 73 url with user name and password
74 74
75 75 $ http_proxy=http://user:passwd@localhost:$HGPORT1 hg clone --config http_proxy.always=True http://user:passwd@localhost:$HGPORT/ e
76 76 requesting all changes
77 77 adding changesets
78 78 adding manifests
79 79 adding file changes
80 80 added 1 changesets with 1 changes to 1 files
81 81 updating to branch default
82 82 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
83 83
84 84 bad host:port for proxy
85 85
86 86 $ http_proxy=localhost:$HGPORT2 hg clone --config http_proxy.always=True http://localhost:$HGPORT/ f
87 87 abort: error: Connection refused
88 88 [255]
89 89
90 90 do not use the proxy if it is in the no list
91 91
92 92 $ http_proxy=localhost:$HGPORT1 hg clone --config http_proxy.no=localhost http://localhost:$HGPORT/ g
93 93 requesting all changes
94 94 adding changesets
95 95 adding manifests
96 96 adding file changes
97 97 added 1 changesets with 1 changes to 1 files
98 98 updating to branch default
99 99 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
100 100 $ cat proxy.log
101 * - - [*] "GET http://localhost:$HGPORT/?pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000&cmd=between HTTP/1.1" - - (glob)
101 * - - [*] "GET http://localhost:$HGPORT/?cmd=between&pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000 HTTP/1.1" - - (glob)
102 102 * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
103 103 * - - [*] "GET http://localhost:$HGPORT/?cmd=stream_out HTTP/1.1" - - (glob)
104 * - - [*] "GET http://localhost:$HGPORT/?pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000&cmd=between HTTP/1.1" - - (glob)
104 * - - [*] "GET http://localhost:$HGPORT/?cmd=between&pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000 HTTP/1.1" - - (glob)
105 105 * - - [*] "GET http://localhost:$HGPORT/?cmd=heads HTTP/1.1" - - (glob)
106 106 * - - [*] "GET http://localhost:$HGPORT/?cmd=changegroup&roots=0000000000000000000000000000000000000000 HTTP/1.1" - - (glob)
107 107 * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
108 108 * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
109 * - - [*] "GET http://localhost:$HGPORT/?pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000&cmd=between HTTP/1.1" - - (glob)
109 * - - [*] "GET http://localhost:$HGPORT/?cmd=between&pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000 HTTP/1.1" - - (glob)
110 110 * - - [*] "GET http://localhost:$HGPORT/?cmd=heads HTTP/1.1" - - (glob)
111 111 * - - [*] "GET http://localhost:$HGPORT/?cmd=changegroup&roots=0000000000000000000000000000000000000000 HTTP/1.1" - - (glob)
112 112 * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
113 113 * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
114 * - - [*] "GET http://localhost:$HGPORT/?pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000&cmd=between HTTP/1.1" - - (glob)
114 * - - [*] "GET http://localhost:$HGPORT/?cmd=between&pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000 HTTP/1.1" - - (glob)
115 115 * - - [*] "GET http://localhost:$HGPORT/?cmd=heads HTTP/1.1" - - (glob)
116 116 * - - [*] "GET http://localhost:$HGPORT/?cmd=changegroup&roots=0000000000000000000000000000000000000000 HTTP/1.1" - - (glob)
117 117 * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
118 118 * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
119 * - - [*] "GET http://localhost:$HGPORT/?pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000&cmd=between HTTP/1.1" - - (glob)
119 * - - [*] "GET http://localhost:$HGPORT/?cmd=between&pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000 HTTP/1.1" - - (glob)
120 120 * - - [*] "GET http://localhost:$HGPORT/?cmd=heads HTTP/1.1" - - (glob)
121 121 * - - [*] "GET http://localhost:$HGPORT/?cmd=changegroup&roots=0000000000000000000000000000000000000000 HTTP/1.1" - - (glob)
122 122 * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
123 123 * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
124 124
General Comments 0
You need to be logged in to leave comments. Login now