Show More
@@ -1,320 +1,330 | |||||
1 | # hgweb/hgweb_mod.py - Web interface for a repository. |
|
1 | # hgweb/hgweb_mod.py - Web interface for a repository. | |
2 | # |
|
2 | # | |
3 | # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net> |
|
3 | # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net> | |
4 | # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> |
|
4 | # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> | |
5 | # |
|
5 | # | |
6 | # This software may be used and distributed according to the terms of the |
|
6 | # This software may be used and distributed according to the terms of the | |
7 | # GNU General Public License version 2 or any later version. |
|
7 | # GNU General Public License version 2 or any later version. | |
8 |
|
8 | |||
9 | import os |
|
9 | import os, zlib | |
10 | from mercurial import ui, hg, hook, error, encoding, templater, wireproto |
|
10 | from mercurial import ui, hg, hook, error, encoding, templater, wireproto | |
11 | from common import get_mtime, ErrorResponse, permhooks |
|
11 | from common import get_mtime, ErrorResponse, permhooks | |
12 | from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR |
|
12 | from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR | |
13 | from request import wsgirequest |
|
13 | from request import wsgirequest | |
14 | import webcommands, protocol, webutil |
|
14 | import webcommands, protocol, webutil | |
15 |
|
15 | |||
16 | perms = { |
|
16 | perms = { | |
17 | 'changegroup': 'pull', |
|
17 | 'changegroup': 'pull', | |
18 | 'changegroupsubset': 'pull', |
|
18 | 'changegroupsubset': 'pull', | |
19 | 'stream_out': 'pull', |
|
19 | 'stream_out': 'pull', | |
20 | 'listkeys': 'pull', |
|
20 | 'listkeys': 'pull', | |
21 | 'unbundle': 'push', |
|
21 | 'unbundle': 'push', | |
22 | 'pushkey': 'push', |
|
22 | 'pushkey': 'push', | |
23 | } |
|
23 | } | |
24 |
|
24 | |||
|
25 | HGTYPE = 'application/mercurial-0.1' | |||
25 | class webproto(object): |
|
26 | class webproto(object): | |
26 | def __init__(self, req): |
|
27 | def __init__(self, req): | |
27 | self.req = req |
|
28 | self.req = req | |
28 | self.response = '' |
|
29 | self.response = '' | |
29 | def getargs(self, args): |
|
30 | def getargs(self, args): | |
30 | data = {} |
|
31 | data = {} | |
31 | keys = args.split() |
|
32 | keys = args.split() | |
32 | for k in keys: |
|
33 | for k in keys: | |
33 | if k == '*': |
|
34 | if k == '*': | |
34 | star = {} |
|
35 | star = {} | |
35 | for key in self.req.form.keys(): |
|
36 | for key in self.req.form.keys(): | |
36 | if key not in keys: |
|
37 | if key not in keys: | |
37 | star[key] = self.req.form[key][0] |
|
38 | star[key] = self.req.form[key][0] | |
38 | data['*'] = star |
|
39 | data['*'] = star | |
39 | else: |
|
40 | else: | |
40 | data[k] = self.req.form[k][0] |
|
41 | data[k] = self.req.form[k][0] | |
41 | return [data[k] for k in keys] |
|
42 | return [data[k] for k in keys] | |
|
43 | def sendchangegroup(self, cg): | |||
|
44 | self.req.respond(HTTP_OK, HGTYPE) | |||
|
45 | z = zlib.compressobj() | |||
|
46 | while 1: | |||
|
47 | chunk = cg.read(4096) | |||
|
48 | if not chunk: | |||
|
49 | break | |||
|
50 | self.req.write(z.compress(chunk)) | |||
|
51 | self.req.write(z.flush()) | |||
|
52 | ||||
42 | def respond(self, s): |
|
53 | def respond(self, s): | |
43 | HGTYPE = 'application/mercurial-0.1' |
|
|||
44 | self.req.respond(HTTP_OK, HGTYPE, length=len(s)) |
|
54 | self.req.respond(HTTP_OK, HGTYPE, length=len(s)) | |
45 | self.response = s |
|
55 | self.response = s | |
46 |
|
56 | |||
47 | def callproto(repo, req, cmd): |
|
57 | def callproto(repo, req, cmd): | |
48 | p = webproto(req) |
|
58 | p = webproto(req) | |
49 | r = wireproto.dispatch(repo, p, cmd) |
|
59 | r = wireproto.dispatch(repo, p, cmd) | |
50 | yield p.response |
|
60 | yield p.response | |
51 |
|
61 | |||
52 | class hgweb(object): |
|
62 | class hgweb(object): | |
53 | def __init__(self, repo, name=None, baseui=None): |
|
63 | def __init__(self, repo, name=None, baseui=None): | |
54 | if isinstance(repo, str): |
|
64 | if isinstance(repo, str): | |
55 | if baseui: |
|
65 | if baseui: | |
56 | u = baseui.copy() |
|
66 | u = baseui.copy() | |
57 | else: |
|
67 | else: | |
58 | u = ui.ui() |
|
68 | u = ui.ui() | |
59 | self.repo = hg.repository(u, repo) |
|
69 | self.repo = hg.repository(u, repo) | |
60 | else: |
|
70 | else: | |
61 | self.repo = repo |
|
71 | self.repo = repo | |
62 |
|
72 | |||
63 | self.repo.ui.setconfig('ui', 'report_untrusted', 'off') |
|
73 | self.repo.ui.setconfig('ui', 'report_untrusted', 'off') | |
64 | self.repo.ui.setconfig('ui', 'interactive', 'off') |
|
74 | self.repo.ui.setconfig('ui', 'interactive', 'off') | |
65 | hook.redirect(True) |
|
75 | hook.redirect(True) | |
66 | self.mtime = -1 |
|
76 | self.mtime = -1 | |
67 | self.reponame = name |
|
77 | self.reponame = name | |
68 | self.archives = 'zip', 'gz', 'bz2' |
|
78 | self.archives = 'zip', 'gz', 'bz2' | |
69 | self.stripecount = 1 |
|
79 | self.stripecount = 1 | |
70 | # a repo owner may set web.templates in .hg/hgrc to get any file |
|
80 | # a repo owner may set web.templates in .hg/hgrc to get any file | |
71 | # readable by the user running the CGI script |
|
81 | # readable by the user running the CGI script | |
72 | self.templatepath = self.config('web', 'templates') |
|
82 | self.templatepath = self.config('web', 'templates') | |
73 |
|
83 | |||
74 | # The CGI scripts are often run by a user different from the repo owner. |
|
84 | # The CGI scripts are often run by a user different from the repo owner. | |
75 | # Trust the settings from the .hg/hgrc files by default. |
|
85 | # Trust the settings from the .hg/hgrc files by default. | |
76 | def config(self, section, name, default=None, untrusted=True): |
|
86 | def config(self, section, name, default=None, untrusted=True): | |
77 | return self.repo.ui.config(section, name, default, |
|
87 | return self.repo.ui.config(section, name, default, | |
78 | untrusted=untrusted) |
|
88 | untrusted=untrusted) | |
79 |
|
89 | |||
80 | def configbool(self, section, name, default=False, untrusted=True): |
|
90 | def configbool(self, section, name, default=False, untrusted=True): | |
81 | return self.repo.ui.configbool(section, name, default, |
|
91 | return self.repo.ui.configbool(section, name, default, | |
82 | untrusted=untrusted) |
|
92 | untrusted=untrusted) | |
83 |
|
93 | |||
84 | def configlist(self, section, name, default=None, untrusted=True): |
|
94 | def configlist(self, section, name, default=None, untrusted=True): | |
85 | return self.repo.ui.configlist(section, name, default, |
|
95 | return self.repo.ui.configlist(section, name, default, | |
86 | untrusted=untrusted) |
|
96 | untrusted=untrusted) | |
87 |
|
97 | |||
88 | def refresh(self, request=None): |
|
98 | def refresh(self, request=None): | |
89 | if request: |
|
99 | if request: | |
90 | self.repo.ui.environ = request.env |
|
100 | self.repo.ui.environ = request.env | |
91 | mtime = get_mtime(self.repo.spath) |
|
101 | mtime = get_mtime(self.repo.spath) | |
92 | if mtime != self.mtime: |
|
102 | if mtime != self.mtime: | |
93 | self.mtime = mtime |
|
103 | self.mtime = mtime | |
94 | self.repo = hg.repository(self.repo.ui, self.repo.root) |
|
104 | self.repo = hg.repository(self.repo.ui, self.repo.root) | |
95 | self.maxchanges = int(self.config("web", "maxchanges", 10)) |
|
105 | self.maxchanges = int(self.config("web", "maxchanges", 10)) | |
96 | self.stripecount = int(self.config("web", "stripes", 1)) |
|
106 | self.stripecount = int(self.config("web", "stripes", 1)) | |
97 | self.maxshortchanges = int(self.config("web", "maxshortchanges", 60)) |
|
107 | self.maxshortchanges = int(self.config("web", "maxshortchanges", 60)) | |
98 | self.maxfiles = int(self.config("web", "maxfiles", 10)) |
|
108 | self.maxfiles = int(self.config("web", "maxfiles", 10)) | |
99 | self.allowpull = self.configbool("web", "allowpull", True) |
|
109 | self.allowpull = self.configbool("web", "allowpull", True) | |
100 | encoding.encoding = self.config("web", "encoding", |
|
110 | encoding.encoding = self.config("web", "encoding", | |
101 | encoding.encoding) |
|
111 | encoding.encoding) | |
102 |
|
112 | |||
103 | def run(self): |
|
113 | def run(self): | |
104 | if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."): |
|
114 | if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."): | |
105 | raise RuntimeError("This function is only intended to be " |
|
115 | raise RuntimeError("This function is only intended to be " | |
106 | "called while running as a CGI script.") |
|
116 | "called while running as a CGI script.") | |
107 | import mercurial.hgweb.wsgicgi as wsgicgi |
|
117 | import mercurial.hgweb.wsgicgi as wsgicgi | |
108 | wsgicgi.launch(self) |
|
118 | wsgicgi.launch(self) | |
109 |
|
119 | |||
110 | def __call__(self, env, respond): |
|
120 | def __call__(self, env, respond): | |
111 | req = wsgirequest(env, respond) |
|
121 | req = wsgirequest(env, respond) | |
112 | return self.run_wsgi(req) |
|
122 | return self.run_wsgi(req) | |
113 |
|
123 | |||
114 | def run_wsgi(self, req): |
|
124 | def run_wsgi(self, req): | |
115 |
|
125 | |||
116 | self.refresh(req) |
|
126 | self.refresh(req) | |
117 |
|
127 | |||
118 | # work with CGI variables to create coherent structure |
|
128 | # work with CGI variables to create coherent structure | |
119 | # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME |
|
129 | # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME | |
120 |
|
130 | |||
121 | req.url = req.env['SCRIPT_NAME'] |
|
131 | req.url = req.env['SCRIPT_NAME'] | |
122 | if not req.url.endswith('/'): |
|
132 | if not req.url.endswith('/'): | |
123 | req.url += '/' |
|
133 | req.url += '/' | |
124 | if 'REPO_NAME' in req.env: |
|
134 | if 'REPO_NAME' in req.env: | |
125 | req.url += req.env['REPO_NAME'] + '/' |
|
135 | req.url += req.env['REPO_NAME'] + '/' | |
126 |
|
136 | |||
127 | if 'PATH_INFO' in req.env: |
|
137 | if 'PATH_INFO' in req.env: | |
128 | parts = req.env['PATH_INFO'].strip('/').split('/') |
|
138 | parts = req.env['PATH_INFO'].strip('/').split('/') | |
129 | repo_parts = req.env.get('REPO_NAME', '').split('/') |
|
139 | repo_parts = req.env.get('REPO_NAME', '').split('/') | |
130 | if parts[:len(repo_parts)] == repo_parts: |
|
140 | if parts[:len(repo_parts)] == repo_parts: | |
131 | parts = parts[len(repo_parts):] |
|
141 | parts = parts[len(repo_parts):] | |
132 | query = '/'.join(parts) |
|
142 | query = '/'.join(parts) | |
133 | else: |
|
143 | else: | |
134 | query = req.env['QUERY_STRING'].split('&', 1)[0] |
|
144 | query = req.env['QUERY_STRING'].split('&', 1)[0] | |
135 | query = query.split(';', 1)[0] |
|
145 | query = query.split(';', 1)[0] | |
136 |
|
146 | |||
137 | # process this if it's a protocol request |
|
147 | # process this if it's a protocol request | |
138 | # protocol bits don't need to create any URLs |
|
148 | # protocol bits don't need to create any URLs | |
139 | # and the clients always use the old URL structure |
|
149 | # and the clients always use the old URL structure | |
140 |
|
150 | |||
141 | cmd = req.form.get('cmd', [''])[0] |
|
151 | cmd = req.form.get('cmd', [''])[0] | |
142 | if cmd and cmd in protocol.__all__: |
|
152 | if cmd and cmd in protocol.__all__: | |
143 | if query: |
|
153 | if query: | |
144 | raise ErrorResponse(HTTP_NOT_FOUND) |
|
154 | raise ErrorResponse(HTTP_NOT_FOUND) | |
145 | try: |
|
155 | try: | |
146 | if cmd in perms: |
|
156 | if cmd in perms: | |
147 | try: |
|
157 | try: | |
148 | self.check_perm(req, perms[cmd]) |
|
158 | self.check_perm(req, perms[cmd]) | |
149 | except ErrorResponse, inst: |
|
159 | except ErrorResponse, inst: | |
150 | if cmd == 'unbundle': |
|
160 | if cmd == 'unbundle': | |
151 | req.drain() |
|
161 | req.drain() | |
152 | raise |
|
162 | raise | |
153 | if cmd in wireproto.commands: |
|
163 | if cmd in wireproto.commands: | |
154 | return callproto(self.repo, req, cmd) |
|
164 | return callproto(self.repo, req, cmd) | |
155 | method = getattr(protocol, cmd) |
|
165 | method = getattr(protocol, cmd) | |
156 | return method(self.repo, req) |
|
166 | return method(self.repo, req) | |
157 | except ErrorResponse, inst: |
|
167 | except ErrorResponse, inst: | |
158 | req.respond(inst, protocol.HGTYPE) |
|
168 | req.respond(inst, protocol.HGTYPE) | |
159 | if not inst.message: |
|
169 | if not inst.message: | |
160 | return [] |
|
170 | return [] | |
161 | return '0\n%s\n' % inst.message, |
|
171 | return '0\n%s\n' % inst.message, | |
162 |
|
172 | |||
163 | # translate user-visible url structure to internal structure |
|
173 | # translate user-visible url structure to internal structure | |
164 |
|
174 | |||
165 | args = query.split('/', 2) |
|
175 | args = query.split('/', 2) | |
166 | if 'cmd' not in req.form and args and args[0]: |
|
176 | if 'cmd' not in req.form and args and args[0]: | |
167 |
|
177 | |||
168 | cmd = args.pop(0) |
|
178 | cmd = args.pop(0) | |
169 | style = cmd.rfind('-') |
|
179 | style = cmd.rfind('-') | |
170 | if style != -1: |
|
180 | if style != -1: | |
171 | req.form['style'] = [cmd[:style]] |
|
181 | req.form['style'] = [cmd[:style]] | |
172 | cmd = cmd[style + 1:] |
|
182 | cmd = cmd[style + 1:] | |
173 |
|
183 | |||
174 | # avoid accepting e.g. style parameter as command |
|
184 | # avoid accepting e.g. style parameter as command | |
175 | if hasattr(webcommands, cmd): |
|
185 | if hasattr(webcommands, cmd): | |
176 | req.form['cmd'] = [cmd] |
|
186 | req.form['cmd'] = [cmd] | |
177 | else: |
|
187 | else: | |
178 | cmd = '' |
|
188 | cmd = '' | |
179 |
|
189 | |||
180 | if cmd == 'static': |
|
190 | if cmd == 'static': | |
181 | req.form['file'] = ['/'.join(args)] |
|
191 | req.form['file'] = ['/'.join(args)] | |
182 | else: |
|
192 | else: | |
183 | if args and args[0]: |
|
193 | if args and args[0]: | |
184 | node = args.pop(0) |
|
194 | node = args.pop(0) | |
185 | req.form['node'] = [node] |
|
195 | req.form['node'] = [node] | |
186 | if args: |
|
196 | if args: | |
187 | req.form['file'] = args |
|
197 | req.form['file'] = args | |
188 |
|
198 | |||
189 | ua = req.env.get('HTTP_USER_AGENT', '') |
|
199 | ua = req.env.get('HTTP_USER_AGENT', '') | |
190 | if cmd == 'rev' and 'mercurial' in ua: |
|
200 | if cmd == 'rev' and 'mercurial' in ua: | |
191 | req.form['style'] = ['raw'] |
|
201 | req.form['style'] = ['raw'] | |
192 |
|
202 | |||
193 | if cmd == 'archive': |
|
203 | if cmd == 'archive': | |
194 | fn = req.form['node'][0] |
|
204 | fn = req.form['node'][0] | |
195 | for type_, spec in self.archive_specs.iteritems(): |
|
205 | for type_, spec in self.archive_specs.iteritems(): | |
196 | ext = spec[2] |
|
206 | ext = spec[2] | |
197 | if fn.endswith(ext): |
|
207 | if fn.endswith(ext): | |
198 | req.form['node'] = [fn[:-len(ext)]] |
|
208 | req.form['node'] = [fn[:-len(ext)]] | |
199 | req.form['type'] = [type_] |
|
209 | req.form['type'] = [type_] | |
200 |
|
210 | |||
201 | # process the web interface request |
|
211 | # process the web interface request | |
202 |
|
212 | |||
203 | try: |
|
213 | try: | |
204 | tmpl = self.templater(req) |
|
214 | tmpl = self.templater(req) | |
205 | ctype = tmpl('mimetype', encoding=encoding.encoding) |
|
215 | ctype = tmpl('mimetype', encoding=encoding.encoding) | |
206 | ctype = templater.stringify(ctype) |
|
216 | ctype = templater.stringify(ctype) | |
207 |
|
217 | |||
208 | # check read permissions non-static content |
|
218 | # check read permissions non-static content | |
209 | if cmd != 'static': |
|
219 | if cmd != 'static': | |
210 | self.check_perm(req, None) |
|
220 | self.check_perm(req, None) | |
211 |
|
221 | |||
212 | if cmd == '': |
|
222 | if cmd == '': | |
213 | req.form['cmd'] = [tmpl.cache['default']] |
|
223 | req.form['cmd'] = [tmpl.cache['default']] | |
214 | cmd = req.form['cmd'][0] |
|
224 | cmd = req.form['cmd'][0] | |
215 |
|
225 | |||
216 | if cmd not in webcommands.__all__: |
|
226 | if cmd not in webcommands.__all__: | |
217 | msg = 'no such method: %s' % cmd |
|
227 | msg = 'no such method: %s' % cmd | |
218 | raise ErrorResponse(HTTP_BAD_REQUEST, msg) |
|
228 | raise ErrorResponse(HTTP_BAD_REQUEST, msg) | |
219 | elif cmd == 'file' and 'raw' in req.form.get('style', []): |
|
229 | elif cmd == 'file' and 'raw' in req.form.get('style', []): | |
220 | self.ctype = ctype |
|
230 | self.ctype = ctype | |
221 | content = webcommands.rawfile(self, req, tmpl) |
|
231 | content = webcommands.rawfile(self, req, tmpl) | |
222 | else: |
|
232 | else: | |
223 | content = getattr(webcommands, cmd)(self, req, tmpl) |
|
233 | content = getattr(webcommands, cmd)(self, req, tmpl) | |
224 | req.respond(HTTP_OK, ctype) |
|
234 | req.respond(HTTP_OK, ctype) | |
225 |
|
235 | |||
226 | return content |
|
236 | return content | |
227 |
|
237 | |||
228 | except error.LookupError, err: |
|
238 | except error.LookupError, err: | |
229 | req.respond(HTTP_NOT_FOUND, ctype) |
|
239 | req.respond(HTTP_NOT_FOUND, ctype) | |
230 | msg = str(err) |
|
240 | msg = str(err) | |
231 | if 'manifest' not in msg: |
|
241 | if 'manifest' not in msg: | |
232 | msg = 'revision not found: %s' % err.name |
|
242 | msg = 'revision not found: %s' % err.name | |
233 | return tmpl('error', error=msg) |
|
243 | return tmpl('error', error=msg) | |
234 | except (error.RepoError, error.RevlogError), inst: |
|
244 | except (error.RepoError, error.RevlogError), inst: | |
235 | req.respond(HTTP_SERVER_ERROR, ctype) |
|
245 | req.respond(HTTP_SERVER_ERROR, ctype) | |
236 | return tmpl('error', error=str(inst)) |
|
246 | return tmpl('error', error=str(inst)) | |
237 | except ErrorResponse, inst: |
|
247 | except ErrorResponse, inst: | |
238 | req.respond(inst, ctype) |
|
248 | req.respond(inst, ctype) | |
239 | return tmpl('error', error=inst.message) |
|
249 | return tmpl('error', error=inst.message) | |
240 |
|
250 | |||
241 | def templater(self, req): |
|
251 | def templater(self, req): | |
242 |
|
252 | |||
243 | # determine scheme, port and server name |
|
253 | # determine scheme, port and server name | |
244 | # this is needed to create absolute urls |
|
254 | # this is needed to create absolute urls | |
245 |
|
255 | |||
246 | proto = req.env.get('wsgi.url_scheme') |
|
256 | proto = req.env.get('wsgi.url_scheme') | |
247 | if proto == 'https': |
|
257 | if proto == 'https': | |
248 | proto = 'https' |
|
258 | proto = 'https' | |
249 | default_port = "443" |
|
259 | default_port = "443" | |
250 | else: |
|
260 | else: | |
251 | proto = 'http' |
|
261 | proto = 'http' | |
252 | default_port = "80" |
|
262 | default_port = "80" | |
253 |
|
263 | |||
254 | port = req.env["SERVER_PORT"] |
|
264 | port = req.env["SERVER_PORT"] | |
255 | port = port != default_port and (":" + port) or "" |
|
265 | port = port != default_port and (":" + port) or "" | |
256 | urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port) |
|
266 | urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port) | |
257 | staticurl = self.config("web", "staticurl") or req.url + 'static/' |
|
267 | staticurl = self.config("web", "staticurl") or req.url + 'static/' | |
258 | if not staticurl.endswith('/'): |
|
268 | if not staticurl.endswith('/'): | |
259 | staticurl += '/' |
|
269 | staticurl += '/' | |
260 |
|
270 | |||
261 | # some functions for the templater |
|
271 | # some functions for the templater | |
262 |
|
272 | |||
263 | def header(**map): |
|
273 | def header(**map): | |
264 | yield tmpl('header', encoding=encoding.encoding, **map) |
|
274 | yield tmpl('header', encoding=encoding.encoding, **map) | |
265 |
|
275 | |||
266 | def footer(**map): |
|
276 | def footer(**map): | |
267 | yield tmpl("footer", **map) |
|
277 | yield tmpl("footer", **map) | |
268 |
|
278 | |||
269 | def motd(**map): |
|
279 | def motd(**map): | |
270 | yield self.config("web", "motd", "") |
|
280 | yield self.config("web", "motd", "") | |
271 |
|
281 | |||
272 | # figure out which style to use |
|
282 | # figure out which style to use | |
273 |
|
283 | |||
274 | vars = {} |
|
284 | vars = {} | |
275 | styles = ( |
|
285 | styles = ( | |
276 | req.form.get('style', [None])[0], |
|
286 | req.form.get('style', [None])[0], | |
277 | self.config('web', 'style'), |
|
287 | self.config('web', 'style'), | |
278 | 'paper', |
|
288 | 'paper', | |
279 | ) |
|
289 | ) | |
280 | style, mapfile = templater.stylemap(styles, self.templatepath) |
|
290 | style, mapfile = templater.stylemap(styles, self.templatepath) | |
281 | if style == styles[0]: |
|
291 | if style == styles[0]: | |
282 | vars['style'] = style |
|
292 | vars['style'] = style | |
283 |
|
293 | |||
284 | start = req.url[-1] == '?' and '&' or '?' |
|
294 | start = req.url[-1] == '?' and '&' or '?' | |
285 | sessionvars = webutil.sessionvars(vars, start) |
|
295 | sessionvars = webutil.sessionvars(vars, start) | |
286 |
|
296 | |||
287 | if not self.reponame: |
|
297 | if not self.reponame: | |
288 | self.reponame = (self.config("web", "name") |
|
298 | self.reponame = (self.config("web", "name") | |
289 | or req.env.get('REPO_NAME') |
|
299 | or req.env.get('REPO_NAME') | |
290 | or req.url.strip('/') or self.repo.root) |
|
300 | or req.url.strip('/') or self.repo.root) | |
291 |
|
301 | |||
292 | # create the templater |
|
302 | # create the templater | |
293 |
|
303 | |||
294 | tmpl = templater.templater(mapfile, |
|
304 | tmpl = templater.templater(mapfile, | |
295 | defaults={"url": req.url, |
|
305 | defaults={"url": req.url, | |
296 | "staticurl": staticurl, |
|
306 | "staticurl": staticurl, | |
297 | "urlbase": urlbase, |
|
307 | "urlbase": urlbase, | |
298 | "repo": self.reponame, |
|
308 | "repo": self.reponame, | |
299 | "header": header, |
|
309 | "header": header, | |
300 | "footer": footer, |
|
310 | "footer": footer, | |
301 | "motd": motd, |
|
311 | "motd": motd, | |
302 | "sessionvars": sessionvars |
|
312 | "sessionvars": sessionvars | |
303 | }) |
|
313 | }) | |
304 | return tmpl |
|
314 | return tmpl | |
305 |
|
315 | |||
306 | def archivelist(self, nodeid): |
|
316 | def archivelist(self, nodeid): | |
307 | allowed = self.configlist("web", "allow_archive") |
|
317 | allowed = self.configlist("web", "allow_archive") | |
308 | for i, spec in self.archive_specs.iteritems(): |
|
318 | for i, spec in self.archive_specs.iteritems(): | |
309 | if i in allowed or self.configbool("web", "allow" + i): |
|
319 | if i in allowed or self.configbool("web", "allow" + i): | |
310 | yield {"type" : i, "extension" : spec[2], "node" : nodeid} |
|
320 | yield {"type" : i, "extension" : spec[2], "node" : nodeid} | |
311 |
|
321 | |||
312 | archive_specs = { |
|
322 | archive_specs = { | |
313 | 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None), |
|
323 | 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None), | |
314 | 'gz': ('application/x-tar', 'tgz', '.tar.gz', None), |
|
324 | 'gz': ('application/x-tar', 'tgz', '.tar.gz', None), | |
315 | 'zip': ('application/zip', 'zip', '.zip', None), |
|
325 | 'zip': ('application/zip', 'zip', '.zip', None), | |
316 | } |
|
326 | } | |
317 |
|
327 | |||
318 | def check_perm(self, req, op): |
|
328 | def check_perm(self, req, op): | |
319 | for hook in permhooks: |
|
329 | for hook in permhooks: | |
320 | hook(self, req, op) |
|
330 | hook(self, req, op) |
@@ -1,161 +1,124 | |||||
1 | # |
|
1 | # | |
2 | # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net> |
|
2 | # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net> | |
3 | # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> |
|
3 | # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> | |
4 | # |
|
4 | # | |
5 | # This software may be used and distributed according to the terms of the |
|
5 | # This software may be used and distributed according to the terms of the | |
6 | # GNU General Public License version 2 or any later version. |
|
6 | # GNU General Public License version 2 or any later version. | |
7 |
|
7 | |||
8 | import cStringIO, zlib, tempfile, errno, os, sys, urllib, copy |
|
8 | import cStringIO, zlib, tempfile, errno, os, sys, urllib, copy | |
9 | from mercurial import util, streamclone, pushkey |
|
9 | from mercurial import util, streamclone, pushkey | |
10 | from mercurial.node import bin, hex |
|
10 | from mercurial.node import bin, hex | |
11 | from mercurial import changegroup as changegroupmod |
|
11 | from mercurial import changegroup as changegroupmod | |
12 | from common import ErrorResponse, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR |
|
12 | from common import ErrorResponse, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR | |
13 |
|
13 | |||
14 | # __all__ is populated with the allowed commands. Be sure to add to it if |
|
14 | # __all__ is populated with the allowed commands. Be sure to add to it if | |
15 | # you're adding a new command, or the new command won't work. |
|
15 | # you're adding a new command, or the new command won't work. | |
16 |
|
16 | |||
17 | __all__ = [ |
|
17 | __all__ = [ | |
18 | 'lookup', 'heads', 'branches', 'between', 'changegroup', |
|
18 | 'lookup', 'heads', 'branches', 'between', 'changegroup', | |
19 | 'changegroupsubset', 'capabilities', 'unbundle', 'stream_out', |
|
19 | 'changegroupsubset', 'capabilities', 'unbundle', 'stream_out', | |
20 | 'branchmap', 'pushkey', 'listkeys' |
|
20 | 'branchmap', 'pushkey', 'listkeys' | |
21 | ] |
|
21 | ] | |
22 |
|
22 | |||
23 | HGTYPE = 'application/mercurial-0.1' |
|
23 | HGTYPE = 'application/mercurial-0.1' | |
24 | basecaps = 'lookup changegroupsubset branchmap pushkey'.split() |
|
24 | basecaps = 'lookup changegroupsubset branchmap pushkey'.split() | |
25 |
|
25 | |||
26 | def changegroup(repo, req): |
|
|||
27 | req.respond(HTTP_OK, HGTYPE) |
|
|||
28 | nodes = [] |
|
|||
29 |
|
||||
30 | if 'roots' in req.form: |
|
|||
31 | nodes = map(bin, req.form['roots'][0].split(" ")) |
|
|||
32 |
|
||||
33 | z = zlib.compressobj() |
|
|||
34 | f = repo.changegroup(nodes, 'serve') |
|
|||
35 | while 1: |
|
|||
36 | chunk = f.read(4096) |
|
|||
37 | if not chunk: |
|
|||
38 | break |
|
|||
39 | yield z.compress(chunk) |
|
|||
40 |
|
||||
41 | yield z.flush() |
|
|||
42 |
|
||||
43 | def changegroupsubset(repo, req): |
|
|||
44 | req.respond(HTTP_OK, HGTYPE) |
|
|||
45 | bases = [] |
|
|||
46 | heads = [] |
|
|||
47 |
|
||||
48 | if 'bases' in req.form: |
|
|||
49 | bases = [bin(x) for x in req.form['bases'][0].split(' ')] |
|
|||
50 | if 'heads' in req.form: |
|
|||
51 | heads = [bin(x) for x in req.form['heads'][0].split(' ')] |
|
|||
52 |
|
||||
53 | z = zlib.compressobj() |
|
|||
54 | f = repo.changegroupsubset(bases, heads, 'serve') |
|
|||
55 | while 1: |
|
|||
56 | chunk = f.read(4096) |
|
|||
57 | if not chunk: |
|
|||
58 | break |
|
|||
59 | yield z.compress(chunk) |
|
|||
60 |
|
||||
61 | yield z.flush() |
|
|||
62 |
|
||||
63 | def capabilities(repo, req): |
|
26 | def capabilities(repo, req): | |
64 | caps = copy.copy(basecaps) |
|
27 | caps = copy.copy(basecaps) | |
65 | if streamclone.allowed(repo.ui): |
|
28 | if streamclone.allowed(repo.ui): | |
66 | caps.append('stream=%d' % repo.changelog.version) |
|
29 | caps.append('stream=%d' % repo.changelog.version) | |
67 | if changegroupmod.bundlepriority: |
|
30 | if changegroupmod.bundlepriority: | |
68 | caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority)) |
|
31 | caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority)) | |
69 | rsp = ' '.join(caps) |
|
32 | rsp = ' '.join(caps) | |
70 | req.respond(HTTP_OK, HGTYPE, length=len(rsp)) |
|
33 | req.respond(HTTP_OK, HGTYPE, length=len(rsp)) | |
71 | yield rsp |
|
34 | yield rsp | |
72 |
|
35 | |||
73 | def unbundle(repo, req): |
|
36 | def unbundle(repo, req): | |
74 |
|
37 | |||
75 | proto = req.env.get('wsgi.url_scheme') or 'http' |
|
38 | proto = req.env.get('wsgi.url_scheme') or 'http' | |
76 | their_heads = req.form['heads'][0].split(' ') |
|
39 | their_heads = req.form['heads'][0].split(' ') | |
77 |
|
40 | |||
78 | def check_heads(): |
|
41 | def check_heads(): | |
79 | heads = map(hex, repo.heads()) |
|
42 | heads = map(hex, repo.heads()) | |
80 | return their_heads == [hex('force')] or their_heads == heads |
|
43 | return their_heads == [hex('force')] or their_heads == heads | |
81 |
|
44 | |||
82 | # fail early if possible |
|
45 | # fail early if possible | |
83 | if not check_heads(): |
|
46 | if not check_heads(): | |
84 | req.drain() |
|
47 | req.drain() | |
85 | raise ErrorResponse(HTTP_OK, 'unsynced changes') |
|
48 | raise ErrorResponse(HTTP_OK, 'unsynced changes') | |
86 |
|
49 | |||
87 | # do not lock repo until all changegroup data is |
|
50 | # do not lock repo until all changegroup data is | |
88 | # streamed. save to temporary file. |
|
51 | # streamed. save to temporary file. | |
89 |
|
52 | |||
90 | fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-') |
|
53 | fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-') | |
91 | fp = os.fdopen(fd, 'wb+') |
|
54 | fp = os.fdopen(fd, 'wb+') | |
92 | try: |
|
55 | try: | |
93 | length = int(req.env['CONTENT_LENGTH']) |
|
56 | length = int(req.env['CONTENT_LENGTH']) | |
94 | for s in util.filechunkiter(req, limit=length): |
|
57 | for s in util.filechunkiter(req, limit=length): | |
95 | fp.write(s) |
|
58 | fp.write(s) | |
96 |
|
59 | |||
97 | try: |
|
60 | try: | |
98 | lock = repo.lock() |
|
61 | lock = repo.lock() | |
99 | try: |
|
62 | try: | |
100 | if not check_heads(): |
|
63 | if not check_heads(): | |
101 | raise ErrorResponse(HTTP_OK, 'unsynced changes') |
|
64 | raise ErrorResponse(HTTP_OK, 'unsynced changes') | |
102 |
|
65 | |||
103 | fp.seek(0) |
|
66 | fp.seek(0) | |
104 | header = fp.read(6) |
|
67 | header = fp.read(6) | |
105 | if header.startswith('HG') and not header.startswith('HG10'): |
|
68 | if header.startswith('HG') and not header.startswith('HG10'): | |
106 | raise ValueError('unknown bundle version') |
|
69 | raise ValueError('unknown bundle version') | |
107 | elif header not in changegroupmod.bundletypes: |
|
70 | elif header not in changegroupmod.bundletypes: | |
108 | raise ValueError('unknown bundle compression type') |
|
71 | raise ValueError('unknown bundle compression type') | |
109 | gen = changegroupmod.unbundle(header, fp) |
|
72 | gen = changegroupmod.unbundle(header, fp) | |
110 |
|
73 | |||
111 | # send addchangegroup output to client |
|
74 | # send addchangegroup output to client | |
112 |
|
75 | |||
113 | oldio = sys.stdout, sys.stderr |
|
76 | oldio = sys.stdout, sys.stderr | |
114 | sys.stderr = sys.stdout = cStringIO.StringIO() |
|
77 | sys.stderr = sys.stdout = cStringIO.StringIO() | |
115 |
|
78 | |||
116 | try: |
|
79 | try: | |
117 | url = 'remote:%s:%s:%s' % ( |
|
80 | url = 'remote:%s:%s:%s' % ( | |
118 | proto, |
|
81 | proto, | |
119 | urllib.quote(req.env.get('REMOTE_HOST', '')), |
|
82 | urllib.quote(req.env.get('REMOTE_HOST', '')), | |
120 | urllib.quote(req.env.get('REMOTE_USER', ''))) |
|
83 | urllib.quote(req.env.get('REMOTE_USER', ''))) | |
121 | try: |
|
84 | try: | |
122 | ret = repo.addchangegroup(gen, 'serve', url, lock=lock) |
|
85 | ret = repo.addchangegroup(gen, 'serve', url, lock=lock) | |
123 | except util.Abort, inst: |
|
86 | except util.Abort, inst: | |
124 | sys.stdout.write("abort: %s\n" % inst) |
|
87 | sys.stdout.write("abort: %s\n" % inst) | |
125 | ret = 0 |
|
88 | ret = 0 | |
126 | finally: |
|
89 | finally: | |
127 | val = sys.stdout.getvalue() |
|
90 | val = sys.stdout.getvalue() | |
128 | sys.stdout, sys.stderr = oldio |
|
91 | sys.stdout, sys.stderr = oldio | |
129 | req.respond(HTTP_OK, HGTYPE) |
|
92 | req.respond(HTTP_OK, HGTYPE) | |
130 | return '%d\n%s' % (ret, val), |
|
93 | return '%d\n%s' % (ret, val), | |
131 | finally: |
|
94 | finally: | |
132 | lock.release() |
|
95 | lock.release() | |
133 | except ValueError, inst: |
|
96 | except ValueError, inst: | |
134 | raise ErrorResponse(HTTP_OK, inst) |
|
97 | raise ErrorResponse(HTTP_OK, inst) | |
135 | except (OSError, IOError), inst: |
|
98 | except (OSError, IOError), inst: | |
136 | error = getattr(inst, 'strerror', 'Unknown error') |
|
99 | error = getattr(inst, 'strerror', 'Unknown error') | |
137 | if not isinstance(error, str): |
|
100 | if not isinstance(error, str): | |
138 | error = 'Error: %s' % str(error) |
|
101 | error = 'Error: %s' % str(error) | |
139 | if inst.errno == errno.ENOENT: |
|
102 | if inst.errno == errno.ENOENT: | |
140 | code = HTTP_NOT_FOUND |
|
103 | code = HTTP_NOT_FOUND | |
141 | else: |
|
104 | else: | |
142 | code = HTTP_SERVER_ERROR |
|
105 | code = HTTP_SERVER_ERROR | |
143 | filename = getattr(inst, 'filename', '') |
|
106 | filename = getattr(inst, 'filename', '') | |
144 | # Don't send our filesystem layout to the client |
|
107 | # Don't send our filesystem layout to the client | |
145 | if filename and filename.startswith(repo.root): |
|
108 | if filename and filename.startswith(repo.root): | |
146 | filename = filename[len(repo.root)+1:] |
|
109 | filename = filename[len(repo.root)+1:] | |
147 | text = '%s: %s' % (error, filename) |
|
110 | text = '%s: %s' % (error, filename) | |
148 | else: |
|
111 | else: | |
149 | text = error.replace(repo.root + os.path.sep, '') |
|
112 | text = error.replace(repo.root + os.path.sep, '') | |
150 | raise ErrorResponse(code, text) |
|
113 | raise ErrorResponse(code, text) | |
151 | finally: |
|
114 | finally: | |
152 | fp.close() |
|
115 | fp.close() | |
153 | os.unlink(tempname) |
|
116 | os.unlink(tempname) | |
154 |
|
117 | |||
155 | def stream_out(repo, req): |
|
118 | def stream_out(repo, req): | |
156 | req.respond(HTTP_OK, HGTYPE) |
|
119 | req.respond(HTTP_OK, HGTYPE) | |
157 | try: |
|
120 | try: | |
158 | for chunk in streamclone.stream_out(repo): |
|
121 | for chunk in streamclone.stream_out(repo): | |
159 | yield chunk |
|
122 | yield chunk | |
160 | except streamclone.StreamException, inst: |
|
123 | except streamclone.StreamException, inst: | |
161 | yield str(inst) |
|
124 | yield str(inst) |
@@ -1,207 +1,188 | |||||
1 | # sshserver.py - ssh protocol server support for mercurial |
|
1 | # sshserver.py - ssh protocol server support for mercurial | |
2 | # |
|
2 | # | |
3 | # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> |
|
3 | # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> | |
4 | # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com> |
|
4 | # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com> | |
5 | # |
|
5 | # | |
6 | # This software may be used and distributed according to the terms of the |
|
6 | # This software may be used and distributed according to the terms of the | |
7 | # GNU General Public License version 2 or any later version. |
|
7 | # GNU General Public License version 2 or any later version. | |
8 |
|
8 | |||
9 | from i18n import _ |
|
9 | from i18n import _ | |
10 | from node import bin, hex |
|
10 | from node import bin, hex | |
11 | import streamclone, util, hook, pushkey, wireproto |
|
11 | import streamclone, util, hook, pushkey, wireproto | |
12 | import os, sys, tempfile, urllib, copy |
|
12 | import os, sys, tempfile, urllib, copy | |
13 |
|
13 | |||
14 | class sshserver(object): |
|
14 | class sshserver(object): | |
15 |
|
15 | |||
16 | caps = 'unbundle lookup changegroupsubset branchmap pushkey'.split() |
|
16 | caps = 'unbundle lookup changegroupsubset branchmap pushkey'.split() | |
17 |
|
17 | |||
18 | def __init__(self, ui, repo): |
|
18 | def __init__(self, ui, repo): | |
19 | self.ui = ui |
|
19 | self.ui = ui | |
20 | self.repo = repo |
|
20 | self.repo = repo | |
21 | self.lock = None |
|
21 | self.lock = None | |
22 | self.fin = sys.stdin |
|
22 | self.fin = sys.stdin | |
23 | self.fout = sys.stdout |
|
23 | self.fout = sys.stdout | |
24 |
|
24 | |||
25 | hook.redirect(True) |
|
25 | hook.redirect(True) | |
26 | sys.stdout = sys.stderr |
|
26 | sys.stdout = sys.stderr | |
27 |
|
27 | |||
28 | # Prevent insertion/deletion of CRs |
|
28 | # Prevent insertion/deletion of CRs | |
29 | util.set_binary(self.fin) |
|
29 | util.set_binary(self.fin) | |
30 | util.set_binary(self.fout) |
|
30 | util.set_binary(self.fout) | |
31 |
|
31 | |||
32 | def getargs(self, args): |
|
32 | def getargs(self, args): | |
33 | data = {} |
|
33 | data = {} | |
34 | keys = args.split() |
|
34 | keys = args.split() | |
35 | count = len(keys) |
|
35 | count = len(keys) | |
36 | for n in xrange(len(keys)): |
|
36 | for n in xrange(len(keys)): | |
37 | argline = self.fin.readline()[:-1] |
|
37 | argline = self.fin.readline()[:-1] | |
38 | arg, l = argline.split() |
|
38 | arg, l = argline.split() | |
39 | val = self.fin.read(int(l)) |
|
39 | val = self.fin.read(int(l)) | |
40 | if arg not in keys: |
|
40 | if arg not in keys: | |
41 | raise util.Abort("unexpected parameter %r" % arg) |
|
41 | raise util.Abort("unexpected parameter %r" % arg) | |
42 | if arg == '*': |
|
42 | if arg == '*': | |
43 | star = {} |
|
43 | star = {} | |
44 | for n in xrange(int(l)): |
|
44 | for n in xrange(int(l)): | |
45 | arg, l = argline.split() |
|
45 | arg, l = argline.split() | |
46 | val = self.fin.read(int(l)) |
|
46 | val = self.fin.read(int(l)) | |
47 | star[arg] = val |
|
47 | star[arg] = val | |
48 | data['*'] = star |
|
48 | data['*'] = star | |
49 | else: |
|
49 | else: | |
50 | data[arg] = val |
|
50 | data[arg] = val | |
51 | return [data[k] for k in keys] |
|
51 | return [data[k] for k in keys] | |
52 |
|
52 | |||
53 | def getarg(self, name): |
|
53 | def getarg(self, name): | |
54 | return self.getargs(name)[0] |
|
54 | return self.getargs(name)[0] | |
55 |
|
55 | |||
56 | def respond(self, v): |
|
56 | def respond(self, v): | |
57 | self.fout.write("%d\n" % len(v)) |
|
57 | self.fout.write("%d\n" % len(v)) | |
58 | self.fout.write(v) |
|
58 | self.fout.write(v) | |
59 | self.fout.flush() |
|
59 | self.fout.flush() | |
60 |
|
60 | |||
|
61 | def sendchangegroup(self, changegroup): | |||
|
62 | while True: | |||
|
63 | d = changegroup.read(4096) | |||
|
64 | if not d: | |||
|
65 | break | |||
|
66 | self.fout.write(d) | |||
|
67 | ||||
|
68 | self.fout.flush() | |||
|
69 | ||||
61 | def serve_forever(self): |
|
70 | def serve_forever(self): | |
62 | try: |
|
71 | try: | |
63 | while self.serve_one(): |
|
72 | while self.serve_one(): | |
64 | pass |
|
73 | pass | |
65 | finally: |
|
74 | finally: | |
66 | if self.lock is not None: |
|
75 | if self.lock is not None: | |
67 | self.lock.release() |
|
76 | self.lock.release() | |
68 | sys.exit(0) |
|
77 | sys.exit(0) | |
69 |
|
78 | |||
70 | def serve_one(self): |
|
79 | def serve_one(self): | |
71 | cmd = self.fin.readline()[:-1] |
|
80 | cmd = self.fin.readline()[:-1] | |
72 | if cmd and not wireproto.dispatch(self.repo, self, cmd): |
|
81 | if cmd and not wireproto.dispatch(self.repo, self, cmd): | |
73 | impl = getattr(self, 'do_' + cmd, None) |
|
82 | impl = getattr(self, 'do_' + cmd, None) | |
74 | if impl: |
|
83 | if impl: | |
75 | r = impl() |
|
84 | r = impl() | |
76 | if r is not None: |
|
85 | if r is not None: | |
77 | self.respond(r) |
|
86 | self.respond(r) | |
78 | else: self.respond("") |
|
87 | else: self.respond("") | |
79 | return cmd != '' |
|
88 | return cmd != '' | |
80 |
|
89 | |||
81 | def do_hello(self): |
|
90 | def do_hello(self): | |
82 | '''the hello command returns a set of lines describing various |
|
91 | '''the hello command returns a set of lines describing various | |
83 | interesting things about the server, in an RFC822-like format. |
|
92 | interesting things about the server, in an RFC822-like format. | |
84 | Currently the only one defined is "capabilities", which |
|
93 | Currently the only one defined is "capabilities", which | |
85 | consists of a line in the form: |
|
94 | consists of a line in the form: | |
86 |
|
95 | |||
87 | capabilities: space separated list of tokens |
|
96 | capabilities: space separated list of tokens | |
88 | ''' |
|
97 | ''' | |
89 | caps = copy.copy(self.caps) |
|
98 | caps = copy.copy(self.caps) | |
90 | if streamclone.allowed(self.repo.ui): |
|
99 | if streamclone.allowed(self.repo.ui): | |
91 | caps.append('stream=%d' % self.repo.changelog.version) |
|
100 | caps.append('stream=%d' % self.repo.changelog.version) | |
92 | return "capabilities: %s\n" % (' '.join(caps),) |
|
101 | return "capabilities: %s\n" % (' '.join(caps),) | |
93 |
|
102 | |||
94 | def do_lock(self): |
|
103 | def do_lock(self): | |
95 | '''DEPRECATED - allowing remote client to lock repo is not safe''' |
|
104 | '''DEPRECATED - allowing remote client to lock repo is not safe''' | |
96 |
|
105 | |||
97 | self.lock = self.repo.lock() |
|
106 | self.lock = self.repo.lock() | |
98 | return "" |
|
107 | return "" | |
99 |
|
108 | |||
100 | def do_unlock(self): |
|
109 | def do_unlock(self): | |
101 | '''DEPRECATED''' |
|
110 | '''DEPRECATED''' | |
102 |
|
111 | |||
103 | if self.lock: |
|
112 | if self.lock: | |
104 | self.lock.release() |
|
113 | self.lock.release() | |
105 | self.lock = None |
|
114 | self.lock = None | |
106 | return "" |
|
115 | return "" | |
107 |
|
116 | |||
108 | def do_changegroup(self): |
|
|||
109 | nodes = [] |
|
|||
110 | roots = self.getarg('roots') |
|
|||
111 | nodes = map(bin, roots.split(" ")) |
|
|||
112 |
|
||||
113 | cg = self.repo.changegroup(nodes, 'serve') |
|
|||
114 | while True: |
|
|||
115 | d = cg.read(4096) |
|
|||
116 | if not d: |
|
|||
117 | break |
|
|||
118 | self.fout.write(d) |
|
|||
119 |
|
||||
120 | self.fout.flush() |
|
|||
121 |
|
||||
122 | def do_changegroupsubset(self): |
|
|||
123 | bases, heads = self.getargs('bases heads') |
|
|||
124 | bases = [bin(n) for n in bases.split(' ')] |
|
|||
125 | heads = [bin(n) for n in heads.split(' ')] |
|
|||
126 |
|
||||
127 | cg = self.repo.changegroupsubset(bases, heads, 'serve') |
|
|||
128 | while True: |
|
|||
129 | d = cg.read(4096) |
|
|||
130 | if not d: |
|
|||
131 | break |
|
|||
132 | self.fout.write(d) |
|
|||
133 |
|
||||
134 | self.fout.flush() |
|
|||
135 |
|
||||
136 | def do_addchangegroup(self): |
|
117 | def do_addchangegroup(self): | |
137 | '''DEPRECATED''' |
|
118 | '''DEPRECATED''' | |
138 |
|
119 | |||
139 | if not self.lock: |
|
120 | if not self.lock: | |
140 | self.respond("not locked") |
|
121 | self.respond("not locked") | |
141 | return |
|
122 | return | |
142 |
|
123 | |||
143 | self.respond("") |
|
124 | self.respond("") | |
144 | r = self.repo.addchangegroup(self.fin, 'serve', self.client_url(), |
|
125 | r = self.repo.addchangegroup(self.fin, 'serve', self.client_url(), | |
145 | lock=self.lock) |
|
126 | lock=self.lock) | |
146 | return str(r) |
|
127 | return str(r) | |
147 |
|
128 | |||
148 | def client_url(self): |
|
129 | def client_url(self): | |
149 | client = os.environ.get('SSH_CLIENT', '').split(' ', 1)[0] |
|
130 | client = os.environ.get('SSH_CLIENT', '').split(' ', 1)[0] | |
150 | return 'remote:ssh:' + client |
|
131 | return 'remote:ssh:' + client | |
151 |
|
132 | |||
152 | def do_unbundle(self): |
|
133 | def do_unbundle(self): | |
153 | their_heads = self.getarg('heads').split() |
|
134 | their_heads = self.getarg('heads').split() | |
154 |
|
135 | |||
155 | def check_heads(): |
|
136 | def check_heads(): | |
156 | heads = map(hex, self.repo.heads()) |
|
137 | heads = map(hex, self.repo.heads()) | |
157 | return their_heads == [hex('force')] or their_heads == heads |
|
138 | return their_heads == [hex('force')] or their_heads == heads | |
158 |
|
139 | |||
159 | # fail early if possible |
|
140 | # fail early if possible | |
160 | if not check_heads(): |
|
141 | if not check_heads(): | |
161 | self.respond(_('unsynced changes')) |
|
142 | self.respond(_('unsynced changes')) | |
162 | return |
|
143 | return | |
163 |
|
144 | |||
164 | self.respond('') |
|
145 | self.respond('') | |
165 |
|
146 | |||
166 | # write bundle data to temporary file because it can be big |
|
147 | # write bundle data to temporary file because it can be big | |
167 | fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-') |
|
148 | fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-') | |
168 | fp = os.fdopen(fd, 'wb+') |
|
149 | fp = os.fdopen(fd, 'wb+') | |
169 | try: |
|
150 | try: | |
170 | count = int(self.fin.readline()) |
|
151 | count = int(self.fin.readline()) | |
171 | while count: |
|
152 | while count: | |
172 | fp.write(self.fin.read(count)) |
|
153 | fp.write(self.fin.read(count)) | |
173 | count = int(self.fin.readline()) |
|
154 | count = int(self.fin.readline()) | |
174 |
|
155 | |||
175 | was_locked = self.lock is not None |
|
156 | was_locked = self.lock is not None | |
176 | if not was_locked: |
|
157 | if not was_locked: | |
177 | self.lock = self.repo.lock() |
|
158 | self.lock = self.repo.lock() | |
178 | try: |
|
159 | try: | |
179 | if not check_heads(): |
|
160 | if not check_heads(): | |
180 | # someone else committed/pushed/unbundled while we |
|
161 | # someone else committed/pushed/unbundled while we | |
181 | # were transferring data |
|
162 | # were transferring data | |
182 | self.respond(_('unsynced changes')) |
|
163 | self.respond(_('unsynced changes')) | |
183 | return |
|
164 | return | |
184 | self.respond('') |
|
165 | self.respond('') | |
185 |
|
166 | |||
186 | # push can proceed |
|
167 | # push can proceed | |
187 |
|
168 | |||
188 | fp.seek(0) |
|
169 | fp.seek(0) | |
189 | r = self.repo.addchangegroup(fp, 'serve', self.client_url(), |
|
170 | r = self.repo.addchangegroup(fp, 'serve', self.client_url(), | |
190 | lock=self.lock) |
|
171 | lock=self.lock) | |
191 | self.respond(str(r)) |
|
172 | self.respond(str(r)) | |
192 | finally: |
|
173 | finally: | |
193 | if not was_locked: |
|
174 | if not was_locked: | |
194 | self.lock.release() |
|
175 | self.lock.release() | |
195 | self.lock = None |
|
176 | self.lock = None | |
196 | finally: |
|
177 | finally: | |
197 | fp.close() |
|
178 | fp.close() | |
198 | os.unlink(tempname) |
|
179 | os.unlink(tempname) | |
199 |
|
180 | |||
200 | def do_stream_out(self): |
|
181 | def do_stream_out(self): | |
201 | try: |
|
182 | try: | |
202 | for chunk in streamclone.stream_out(self.repo): |
|
183 | for chunk in streamclone.stream_out(self.repo): | |
203 | self.fout.write(chunk) |
|
184 | self.fout.write(chunk) | |
204 | self.fout.flush() |
|
185 | self.fout.flush() | |
205 | except streamclone.StreamException, inst: |
|
186 | except streamclone.StreamException, inst: | |
206 | self.fout.write(str(inst)) |
|
187 | self.fout.write(str(inst)) | |
207 | self.fout.flush() |
|
188 | self.fout.flush() |
@@ -1,75 +1,90 | |||||
1 | # wireproto.py - generic wire protocol support functions |
|
1 | # wireproto.py - generic wire protocol support functions | |
2 | # |
|
2 | # | |
3 | # Copyright 2005-2010 Matt Mackall <mpm@selenic.com> |
|
3 | # Copyright 2005-2010 Matt Mackall <mpm@selenic.com> | |
4 | # |
|
4 | # | |
5 | # This software may be used and distributed according to the terms of the |
|
5 | # This software may be used and distributed according to the terms of the | |
6 | # GNU General Public License version 2 or any later version. |
|
6 | # GNU General Public License version 2 or any later version. | |
7 |
|
7 | |||
8 | from i18n import _ |
|
8 | from i18n import _ | |
9 | from node import bin, hex |
|
9 | from node import bin, hex | |
10 | import urllib |
|
10 | import urllib | |
11 | import pushkey as pushkey_ |
|
11 | import pushkey as pushkey_ | |
12 |
|
12 | |||
13 | def dispatch(repo, proto, command): |
|
13 | def dispatch(repo, proto, command): | |
14 | if command not in commands: |
|
14 | if command not in commands: | |
15 | return False |
|
15 | return False | |
16 | func, spec = commands[command] |
|
16 | func, spec = commands[command] | |
17 | args = proto.getargs(spec) |
|
17 | args = proto.getargs(spec) | |
18 |
|
|
18 | r = func(repo, proto, *args) | |
|
19 | if r != None: | |||
|
20 | proto.respond(r) | |||
19 | return True |
|
21 | return True | |
20 |
|
22 | |||
21 | def between(repo, proto, pairs): |
|
23 | def between(repo, proto, pairs): | |
22 | pairs = [map(bin, p.split("-")) for p in pairs.split(" ")] |
|
24 | pairs = [map(bin, p.split("-")) for p in pairs.split(" ")] | |
23 | r = [] |
|
25 | r = [] | |
24 | for b in repo.between(pairs): |
|
26 | for b in repo.between(pairs): | |
25 | r.append(" ".join(map(hex, b)) + "\n") |
|
27 | r.append(" ".join(map(hex, b)) + "\n") | |
26 | return "".join(r) |
|
28 | return "".join(r) | |
27 |
|
29 | |||
28 | def branchmap(repo, proto): |
|
30 | def branchmap(repo, proto): | |
29 | branchmap = repo.branchmap() |
|
31 | branchmap = repo.branchmap() | |
30 | heads = [] |
|
32 | heads = [] | |
31 | for branch, nodes in branchmap.iteritems(): |
|
33 | for branch, nodes in branchmap.iteritems(): | |
32 | branchname = urllib.quote(branch) |
|
34 | branchname = urllib.quote(branch) | |
33 | branchnodes = [hex(node) for node in nodes] |
|
35 | branchnodes = [hex(node) for node in nodes] | |
34 | heads.append('%s %s' % (branchname, ' '.join(branchnodes))) |
|
36 | heads.append('%s %s' % (branchname, ' '.join(branchnodes))) | |
35 | return '\n'.join(heads) |
|
37 | return '\n'.join(heads) | |
36 |
|
38 | |||
37 | def branches(repo, proto, nodes): |
|
39 | def branches(repo, proto, nodes): | |
38 | nodes = map(bin, nodes.split(" ")) |
|
40 | nodes = map(bin, nodes.split(" ")) | |
39 | r = [] |
|
41 | r = [] | |
40 | for b in repo.branches(nodes): |
|
42 | for b in repo.branches(nodes): | |
41 | r.append(" ".join(map(hex, b)) + "\n") |
|
43 | r.append(" ".join(map(hex, b)) + "\n") | |
42 | return "".join(r) |
|
44 | return "".join(r) | |
43 |
|
45 | |||
|
46 | def changegroup(repo, proto, roots): | |||
|
47 | nodes = map(bin, roots.split(" ")) | |||
|
48 | cg = repo.changegroup(nodes, 'serve') | |||
|
49 | proto.sendchangegroup(cg) | |||
|
50 | ||||
|
51 | def changegroupsubset(repo, proto, bases, heads): | |||
|
52 | bases = [bin(n) for n in bases.split(' ')] | |||
|
53 | heads = [bin(n) for n in heads.split(' ')] | |||
|
54 | cg = repo.changegroupsubset(bases, heads, 'serve') | |||
|
55 | proto.sendchangegroup(cg) | |||
|
56 | ||||
44 | def heads(repo, proto): |
|
57 | def heads(repo, proto): | |
45 | h = repo.heads() |
|
58 | h = repo.heads() | |
46 | return " ".join(map(hex, h)) + "\n" |
|
59 | return " ".join(map(hex, h)) + "\n" | |
47 |
|
60 | |||
48 | def listkeys(repo, proto, namespace): |
|
61 | def listkeys(repo, proto, namespace): | |
49 | d = pushkey_.list(repo, namespace).items() |
|
62 | d = pushkey_.list(repo, namespace).items() | |
50 | t = '\n'.join(['%s\t%s' % (k.encode('string-escape'), |
|
63 | t = '\n'.join(['%s\t%s' % (k.encode('string-escape'), | |
51 | v.encode('string-escape')) for k, v in d]) |
|
64 | v.encode('string-escape')) for k, v in d]) | |
52 | return t |
|
65 | return t | |
53 |
|
66 | |||
54 | def lookup(repo, proto, key): |
|
67 | def lookup(repo, proto, key): | |
55 | try: |
|
68 | try: | |
56 | r = hex(repo.lookup(key)) |
|
69 | r = hex(repo.lookup(key)) | |
57 | success = 1 |
|
70 | success = 1 | |
58 | except Exception, inst: |
|
71 | except Exception, inst: | |
59 | r = str(inst) |
|
72 | r = str(inst) | |
60 | success = 0 |
|
73 | success = 0 | |
61 | return "%s %s\n" % (success, r) |
|
74 | return "%s %s\n" % (success, r) | |
62 |
|
75 | |||
63 | def pushkey(repo, proto, namespace, key, old, new): |
|
76 | def pushkey(repo, proto, namespace, key, old, new): | |
64 | r = pushkey_.push(repo, namespace, key, old, new) |
|
77 | r = pushkey_.push(repo, namespace, key, old, new) | |
65 | return '%s\n' % int(r) |
|
78 | return '%s\n' % int(r) | |
66 |
|
79 | |||
67 | commands = { |
|
80 | commands = { | |
68 | 'between': (between, 'pairs'), |
|
81 | 'between': (between, 'pairs'), | |
69 | 'branchmap': (branchmap, ''), |
|
82 | 'branchmap': (branchmap, ''), | |
70 | 'branches': (branches, 'nodes'), |
|
83 | 'branches': (branches, 'nodes'), | |
|
84 | 'changegroup': (changegroup, 'roots'), | |||
|
85 | 'changegroupsubset': (changegroupsubset, 'bases heads'), | |||
71 | 'heads': (heads, ''), |
|
86 | 'heads': (heads, ''), | |
72 | 'listkeys': (listkeys, 'namespace'), |
|
87 | 'listkeys': (listkeys, 'namespace'), | |
73 | 'lookup': (lookup, 'key'), |
|
88 | 'lookup': (lookup, 'key'), | |
74 | 'pushkey': (pushkey, 'namespace key old new'), |
|
89 | 'pushkey': (pushkey, 'namespace key old new'), | |
75 | } |
|
90 | } |
@@ -1,62 +1,62 | |||||
1 | #!/bin/sh |
|
1 | #!/bin/sh | |
2 | # This is a test of the wire protocol over CGI-based hgweb. |
|
2 | # This is a test of the wire protocol over CGI-based hgweb. | |
3 |
|
3 | |||
4 | echo % initialize repository |
|
4 | echo % initialize repository | |
5 | hg init test |
|
5 | hg init test | |
6 | cd test |
|
6 | cd test | |
7 | echo a > a |
|
7 | echo a > a | |
8 | hg ci -Ama |
|
8 | hg ci -Ama | |
9 | cd .. |
|
9 | cd .. | |
10 |
|
10 | |||
11 | cat >hgweb.cgi <<HGWEB |
|
11 | cat >hgweb.cgi <<HGWEB | |
12 | #!/usr/bin/env python |
|
12 | #!/usr/bin/env python | |
13 | # |
|
13 | # | |
14 | # An example CGI script to use hgweb, edit as necessary |
|
14 | # An example CGI script to use hgweb, edit as necessary | |
15 |
|
15 | |||
16 | import cgitb |
|
16 | import cgitb | |
17 | cgitb.enable() |
|
17 | cgitb.enable() | |
18 |
|
18 | |||
19 | from mercurial import demandimport; demandimport.enable() |
|
19 | from mercurial import demandimport; demandimport.enable() | |
20 | from mercurial.hgweb import hgweb |
|
20 | from mercurial.hgweb import hgweb | |
21 | from mercurial.hgweb import wsgicgi |
|
21 | from mercurial.hgweb import wsgicgi | |
22 |
|
22 | |||
23 | application = hgweb("test", "Empty test repository") |
|
23 | application = hgweb("test", "Empty test repository") | |
24 | wsgicgi.launch(application) |
|
24 | wsgicgi.launch(application) | |
25 | HGWEB |
|
25 | HGWEB | |
26 | chmod 755 hgweb.cgi |
|
26 | chmod 755 hgweb.cgi | |
27 |
|
27 | |||
28 | DOCUMENT_ROOT="/var/www/hg"; export DOCUMENT_ROOT |
|
28 | DOCUMENT_ROOT="/var/www/hg"; export DOCUMENT_ROOT | |
29 | GATEWAY_INTERFACE="CGI/1.1"; export GATEWAY_INTERFACE |
|
29 | GATEWAY_INTERFACE="CGI/1.1"; export GATEWAY_INTERFACE | |
30 | HTTP_ACCEPT="text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"; export HTTP_ACCEPT |
|
30 | HTTP_ACCEPT="text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"; export HTTP_ACCEPT | |
31 | HTTP_ACCEPT_CHARSET="ISO-8859-1,utf-8;q=0.7,*;q=0.7"; export HTTP_ACCEPT_CHARSET |
|
31 | HTTP_ACCEPT_CHARSET="ISO-8859-1,utf-8;q=0.7,*;q=0.7"; export HTTP_ACCEPT_CHARSET | |
32 | HTTP_ACCEPT_ENCODING="gzip,deflate"; export HTTP_ACCEPT_ENCODING |
|
32 | HTTP_ACCEPT_ENCODING="gzip,deflate"; export HTTP_ACCEPT_ENCODING | |
33 | HTTP_ACCEPT_LANGUAGE="en-us,en;q=0.5"; export HTTP_ACCEPT_LANGUAGE |
|
33 | HTTP_ACCEPT_LANGUAGE="en-us,en;q=0.5"; export HTTP_ACCEPT_LANGUAGE | |
34 | HTTP_CACHE_CONTROL="max-age=0"; export HTTP_CACHE_CONTROL |
|
34 | HTTP_CACHE_CONTROL="max-age=0"; export HTTP_CACHE_CONTROL | |
35 | HTTP_CONNECTION="keep-alive"; export HTTP_CONNECTION |
|
35 | HTTP_CONNECTION="keep-alive"; export HTTP_CONNECTION | |
36 | HTTP_HOST="hg.omnifarious.org"; export HTTP_HOST |
|
36 | HTTP_HOST="hg.omnifarious.org"; export HTTP_HOST | |
37 | HTTP_KEEP_ALIVE="300"; export HTTP_KEEP_ALIVE |
|
37 | HTTP_KEEP_ALIVE="300"; export HTTP_KEEP_ALIVE | |
38 | HTTP_USER_AGENT="Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.0.4) Gecko/20060608 Ubuntu/dapper-security Firefox/1.5.0.4"; export HTTP_USER_AGENT |
|
38 | HTTP_USER_AGENT="Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.0.4) Gecko/20060608 Ubuntu/dapper-security Firefox/1.5.0.4"; export HTTP_USER_AGENT | |
39 | PATH_INFO="/"; export PATH_INFO |
|
39 | PATH_INFO="/"; export PATH_INFO | |
40 | PATH_TRANSLATED="/var/www/hg/index.html"; export PATH_TRANSLATED |
|
40 | PATH_TRANSLATED="/var/www/hg/index.html"; export PATH_TRANSLATED | |
41 | REMOTE_ADDR="127.0.0.2"; export REMOTE_ADDR |
|
41 | REMOTE_ADDR="127.0.0.2"; export REMOTE_ADDR | |
42 | REMOTE_PORT="44703"; export REMOTE_PORT |
|
42 | REMOTE_PORT="44703"; export REMOTE_PORT | |
43 | REQUEST_METHOD="GET"; export REQUEST_METHOD |
|
43 | REQUEST_METHOD="GET"; export REQUEST_METHOD | |
44 | REQUEST_URI="/test/"; export REQUEST_URI |
|
44 | REQUEST_URI="/test/"; export REQUEST_URI | |
45 | SCRIPT_FILENAME="/home/hopper/hg_public/test.cgi"; export SCRIPT_FILENAME |
|
45 | SCRIPT_FILENAME="/home/hopper/hg_public/test.cgi"; export SCRIPT_FILENAME | |
46 | SCRIPT_NAME="/test"; export SCRIPT_NAME |
|
46 | SCRIPT_NAME="/test"; export SCRIPT_NAME | |
47 | SCRIPT_URI="http://hg.omnifarious.org/test/"; export SCRIPT_URI |
|
47 | SCRIPT_URI="http://hg.omnifarious.org/test/"; export SCRIPT_URI | |
48 | SCRIPT_URL="/test/"; export SCRIPT_URL |
|
48 | SCRIPT_URL="/test/"; export SCRIPT_URL | |
49 | SERVER_ADDR="127.0.0.1"; export SERVER_ADDR |
|
49 | SERVER_ADDR="127.0.0.1"; export SERVER_ADDR | |
50 | SERVER_ADMIN="eric@localhost"; export SERVER_ADMIN |
|
50 | SERVER_ADMIN="eric@localhost"; export SERVER_ADMIN | |
51 | SERVER_NAME="hg.omnifarious.org"; export SERVER_NAME |
|
51 | SERVER_NAME="hg.omnifarious.org"; export SERVER_NAME | |
52 | SERVER_PORT="80"; export SERVER_PORT |
|
52 | SERVER_PORT="80"; export SERVER_PORT | |
53 | SERVER_PROTOCOL="HTTP/1.1"; export SERVER_PROTOCOL |
|
53 | SERVER_PROTOCOL="HTTP/1.1"; export SERVER_PROTOCOL | |
54 | SERVER_SIGNATURE="<address>Apache/2.0.53 (Fedora) Server at hg.omnifarious.org Port 80</address>"; export SERVER_SIGNATURE |
|
54 | SERVER_SIGNATURE="<address>Apache/2.0.53 (Fedora) Server at hg.omnifarious.org Port 80</address>"; export SERVER_SIGNATURE | |
55 | SERVER_SOFTWARE="Apache/2.0.53 (Fedora)"; export SERVER_SOFTWARE |
|
55 | SERVER_SOFTWARE="Apache/2.0.53 (Fedora)"; export SERVER_SOFTWARE | |
56 |
|
56 | |||
57 | echo % try hgweb request |
|
57 | echo % try hgweb request | |
58 | QUERY_STRING="cmd=changegroup"; export QUERY_STRING |
|
58 | QUERY_STRING="cmd=changegroup&roots=0000000000000000000000000000000000000000"; export QUERY_STRING | |
59 | python hgweb.cgi >page1 2>&1 ; echo $? |
|
59 | python hgweb.cgi >page1 2>&1 ; echo $? | |
60 | python "$TESTDIR/md5sum.py" page1 |
|
60 | python "$TESTDIR/md5sum.py" page1 | |
61 |
|
61 | |||
62 | exit 0 |
|
62 | exit 0 |
@@ -1,64 +1,64 | |||||
1 | #!/bin/sh |
|
1 | #!/bin/sh | |
2 | # An attempt at more fully testing the hgweb web interface. |
|
2 | # An attempt at more fully testing the hgweb web interface. | |
3 | # The following things are tested elsewhere and are therefore omitted: |
|
3 | # The following things are tested elsewhere and are therefore omitted: | |
4 | # - archive, tested in test-archive |
|
4 | # - archive, tested in test-archive | |
5 | # - unbundle, tested in test-push-http |
|
5 | # - unbundle, tested in test-push-http | |
6 | # - changegroupsubset, tested in test-pull |
|
6 | # - changegroupsubset, tested in test-pull | |
7 |
|
7 | |||
8 | echo % Set up the repo |
|
8 | echo % Set up the repo | |
9 | hg init test |
|
9 | hg init test | |
10 | cd test |
|
10 | cd test | |
11 | mkdir da |
|
11 | mkdir da | |
12 | echo foo > da/foo |
|
12 | echo foo > da/foo | |
13 | echo foo > foo |
|
13 | echo foo > foo | |
14 | hg ci -Ambase |
|
14 | hg ci -Ambase | |
15 | hg tag 1.0 |
|
15 | hg tag 1.0 | |
16 | echo another > foo |
|
16 | echo another > foo | |
17 | hg branch stable |
|
17 | hg branch stable | |
18 | hg ci -Ambranch |
|
18 | hg ci -Ambranch | |
19 | hg serve --config server.uncompressed=False -n test -p $HGPORT -d --pid-file=hg.pid -E errors.log |
|
19 | hg serve --config server.uncompressed=False -n test -p $HGPORT -d --pid-file=hg.pid -E errors.log | |
20 | cat hg.pid >> $DAEMON_PIDS |
|
20 | cat hg.pid >> $DAEMON_PIDS | |
21 |
|
21 | |||
22 | echo % Logs and changes |
|
22 | echo % Logs and changes | |
23 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log/?style=atom' | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//" |
|
23 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log/?style=atom' | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//" | |
24 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log/1/?style=atom' | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//" |
|
24 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log/1/?style=atom' | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//" | |
25 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log/1/foo/?style=atom' | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//" |
|
25 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log/1/foo/?style=atom' | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//" | |
26 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/shortlog/' |
|
26 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/shortlog/' | |
27 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/rev/0/' |
|
27 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/rev/0/' | |
28 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/rev/1/?style=raw' |
|
28 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/rev/1/?style=raw' | |
29 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log?rev=base' |
|
29 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log?rev=base' | |
30 |
|
30 | |||
31 | echo % File-related |
|
31 | echo % File-related | |
32 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/foo/?style=raw' |
|
32 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/foo/?style=raw' | |
33 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/annotate/1/foo/?style=raw' |
|
33 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/annotate/1/foo/?style=raw' | |
34 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/?style=raw' |
|
34 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/?style=raw' | |
35 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/foo' |
|
35 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/foo' | |
36 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/filediff/1/foo/?style=raw' |
|
36 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/filediff/1/foo/?style=raw' | |
37 |
|
37 | |||
38 | echo % Overviews |
|
38 | echo % Overviews | |
39 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/raw-tags' |
|
39 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/raw-tags' | |
40 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/raw-branches' |
|
40 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/raw-branches' | |
41 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/summary/?style=gitweb' |
|
41 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/summary/?style=gitweb' | |
42 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/graph/?style=gitweb' |
|
42 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/graph/?style=gitweb' | |
43 |
|
43 | |||
44 | echo % capabilities |
|
44 | echo % capabilities | |
45 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=capabilities' |
|
45 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=capabilities' | |
46 | echo % heads |
|
46 | echo % heads | |
47 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=heads' |
|
47 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=heads' | |
48 | echo % lookup |
|
48 | echo % lookup | |
49 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=lookup&key=1' |
|
49 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=lookup&key=1' | |
50 | echo % branches |
|
50 | echo % branches | |
51 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=branches&nodes=0000000000000000000000000000000000000000' |
|
51 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=branches&nodes=0000000000000000000000000000000000000000' | |
52 | echo % changegroup |
|
52 | echo % changegroup | |
53 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=changegroup' \ |
|
53 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=changegroup&roots=0000000000000000000000000000000000000000' \ | |
54 | | $TESTDIR/printrepr.py |
|
54 | | $TESTDIR/printrepr.py | |
55 | echo % stream_out |
|
55 | echo % stream_out | |
56 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=stream_out' |
|
56 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=stream_out' | |
57 | echo % failing unbundle, requires POST request |
|
57 | echo % failing unbundle, requires POST request | |
58 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=unbundle' |
|
58 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=unbundle' | |
59 |
|
59 | |||
60 | echo % Static files |
|
60 | echo % Static files | |
61 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/static/style.css' |
|
61 | "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/static/style.css' | |
62 |
|
62 | |||
63 | echo % ERRORS ENCOUNTERED |
|
63 | echo % ERRORS ENCOUNTERED | |
64 | cat errors.log |
|
64 | cat errors.log |
General Comments 0
You need to be logged in to leave comments.
Login now