Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/hgweb/protocol.py @ 6784:18c429ea3a0e
hgweb: all protocol functions have become generators
Using the write() callable supplied by the start_response() call is
frowned upon by the WSGI spec, returning an iterable over the content chunks
is the recommended way. Be aware, though: returning many small chunks will
slow down responses, because the server has to flush each chunk separately.
author | Dirkjan Ochtman <dirkjan@ochtman.nl> |
---|---|
date | Sun, 29 Jun 2008 15:23:09 +0200 |
parents | b9d6ab187523 |
children | 88a1bcc5c6a7 |
comparison
equal
deleted
inserted
replaced
6783:6d824dc86907 | 6784:18c429ea3a0e |
---|---|
28 except Exception,inst: | 28 except Exception,inst: |
29 r = str(inst) | 29 r = str(inst) |
30 success = 0 | 30 success = 0 |
31 resp = "%s %s\n" % (success, r) | 31 resp = "%s %s\n" % (success, r) |
32 req.respond(HTTP_OK, HGTYPE, length=len(resp)) | 32 req.respond(HTTP_OK, HGTYPE, length=len(resp)) |
33 req.write(resp) | 33 yield resp |
34 | 34 |
35 def heads(repo, req): | 35 def heads(repo, req): |
36 resp = " ".join(map(hex, repo.heads())) + "\n" | 36 resp = " ".join(map(hex, repo.heads())) + "\n" |
37 req.respond(HTTP_OK, HGTYPE, length=len(resp)) | 37 req.respond(HTTP_OK, HGTYPE, length=len(resp)) |
38 req.write(resp) | 38 yield resp |
39 | 39 |
40 def branches(repo, req): | 40 def branches(repo, req): |
41 nodes = [] | 41 nodes = [] |
42 if 'nodes' in req.form: | 42 if 'nodes' in req.form: |
43 nodes = map(bin, req.form['nodes'][0].split(" ")) | 43 nodes = map(bin, req.form['nodes'][0].split(" ")) |
44 resp = cStringIO.StringIO() | 44 resp = cStringIO.StringIO() |
45 for b in repo.branches(nodes): | 45 for b in repo.branches(nodes): |
46 resp.write(" ".join(map(hex, b)) + "\n") | 46 resp.write(" ".join(map(hex, b)) + "\n") |
47 resp = resp.getvalue() | 47 resp = resp.getvalue() |
48 req.respond(HTTP_OK, HGTYPE, length=len(resp)) | 48 req.respond(HTTP_OK, HGTYPE, length=len(resp)) |
49 req.write(resp) | 49 yield resp |
50 | 50 |
51 def between(repo, req): | 51 def between(repo, req): |
52 if 'pairs' in req.form: | 52 if 'pairs' in req.form: |
53 pairs = [map(bin, p.split("-")) | 53 pairs = [map(bin, p.split("-")) |
54 for p in req.form['pairs'][0].split(" ")] | 54 for p in req.form['pairs'][0].split(" ")] |
55 resp = cStringIO.StringIO() | 55 resp = cStringIO.StringIO() |
56 for b in repo.between(pairs): | 56 for b in repo.between(pairs): |
57 resp.write(" ".join(map(hex, b)) + "\n") | 57 resp.write(" ".join(map(hex, b)) + "\n") |
58 resp = resp.getvalue() | 58 resp = resp.getvalue() |
59 req.respond(HTTP_OK, HGTYPE, length=len(resp)) | 59 req.respond(HTTP_OK, HGTYPE, length=len(resp)) |
60 req.write(resp) | 60 yield resp |
61 | 61 |
62 def changegroup(repo, req): | 62 def changegroup(repo, req): |
63 req.respond(HTTP_OK, HGTYPE) | 63 req.respond(HTTP_OK, HGTYPE) |
64 nodes = [] | 64 nodes = [] |
65 | 65 |
70 f = repo.changegroup(nodes, 'serve') | 70 f = repo.changegroup(nodes, 'serve') |
71 while 1: | 71 while 1: |
72 chunk = f.read(4096) | 72 chunk = f.read(4096) |
73 if not chunk: | 73 if not chunk: |
74 break | 74 break |
75 req.write(z.compress(chunk)) | 75 yield z.compress(chunk) |
76 | 76 |
77 req.write(z.flush()) | 77 yield z.flush() |
78 | 78 |
79 def changegroupsubset(repo, req): | 79 def changegroupsubset(repo, req): |
80 req.respond(HTTP_OK, HGTYPE) | 80 req.respond(HTTP_OK, HGTYPE) |
81 bases = [] | 81 bases = [] |
82 heads = [] | 82 heads = [] |
90 f = repo.changegroupsubset(bases, heads, 'serve') | 90 f = repo.changegroupsubset(bases, heads, 'serve') |
91 while 1: | 91 while 1: |
92 chunk = f.read(4096) | 92 chunk = f.read(4096) |
93 if not chunk: | 93 if not chunk: |
94 break | 94 break |
95 req.write(z.compress(chunk)) | 95 yield z.compress(chunk) |
96 | 96 |
97 req.write(z.flush()) | 97 yield z.flush() |
98 | 98 |
99 def capabilities(repo, req): | 99 def capabilities(repo, req): |
100 caps = ['lookup', 'changegroupsubset'] | 100 caps = ['lookup', 'changegroupsubset'] |
101 if repo.ui.configbool('server', 'uncompressed', untrusted=True): | 101 if repo.ui.configbool('server', 'uncompressed', untrusted=True): |
102 caps.append('stream=%d' % repo.changelog.version) | 102 caps.append('stream=%d' % repo.changelog.version) |
103 if changegroupmod.bundlepriority: | 103 if changegroupmod.bundlepriority: |
104 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority)) | 104 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority)) |
105 rsp = ' '.join(caps) | 105 rsp = ' '.join(caps) |
106 req.respond(HTTP_OK, HGTYPE, length=len(rsp)) | 106 req.respond(HTTP_OK, HGTYPE, length=len(rsp)) |
107 req.write(rsp) | 107 yield rsp |
108 | 108 |
109 def unbundle(repo, req): | 109 def unbundle(repo, req): |
110 | 110 |
111 def bail(response, headers={}): | 111 errorfmt = '0\n%s\n' |
112 length = int(req.env.get('CONTENT_LENGTH', 0)) | |
113 for s in util.filechunkiter(req, limit=length): | |
114 # drain incoming bundle, else client will not see | |
115 # response when run outside cgi script | |
116 pass | |
117 | |
118 status = headers.pop('status', HTTP_OK) | |
119 req.header(headers.items()) | |
120 req.respond(status, HGTYPE) | |
121 req.write('0\n') | |
122 req.write(response) | |
123 | |
124 proto = req.env.get('wsgi.url_scheme') or 'http' | 112 proto = req.env.get('wsgi.url_scheme') or 'http' |
125 their_heads = req.form['heads'][0].split(' ') | 113 their_heads = req.form['heads'][0].split(' ') |
126 | 114 |
127 def check_heads(): | 115 def check_heads(): |
128 heads = map(hex, repo.heads()) | 116 heads = map(hex, repo.heads()) |
129 return their_heads == [hex('force')] or their_heads == heads | 117 return their_heads == [hex('force')] or their_heads == heads |
130 | 118 |
131 # fail early if possible | 119 # fail early if possible |
132 if not check_heads(): | 120 if not check_heads(): |
133 bail('unsynced changes\n') | 121 length = int(req.env.get('CONTENT_LENGTH', 0)) |
122 for s in util.filechunkiter(req, limit=length): | |
123 # drain incoming bundle, else client will not see | |
124 # response when run outside cgi script | |
125 pass | |
126 req.respond(HTTP_OK, HGTYPE) | |
127 yield errorfmt % 'unsynced changes' | |
134 return | 128 return |
135 | 129 |
136 req.respond(HTTP_OK, HGTYPE) | 130 req.respond(HTTP_OK, HGTYPE) |
137 | 131 |
138 # do not lock repo until all changegroup data is | 132 # do not lock repo until all changegroup data is |
147 | 141 |
148 try: | 142 try: |
149 lock = repo.lock() | 143 lock = repo.lock() |
150 try: | 144 try: |
151 if not check_heads(): | 145 if not check_heads(): |
152 req.write('0\n') | 146 yield errorfmt % 'unsynced changes' |
153 req.write('unsynced changes\n') | |
154 return | 147 return |
155 | 148 |
156 fp.seek(0) | 149 fp.seek(0) |
157 header = fp.read(6) | 150 header = fp.read(6) |
158 if header.startswith('HG') and not header.startswith('HG10'): | 151 if header.startswith('HG') and not header.startswith('HG10'): |
175 sys.stdout.write("abort: %s\n" % inst) | 168 sys.stdout.write("abort: %s\n" % inst) |
176 ret = 0 | 169 ret = 0 |
177 finally: | 170 finally: |
178 val = sys.stdout.getvalue() | 171 val = sys.stdout.getvalue() |
179 sys.stdout, sys.stderr = oldio | 172 sys.stdout, sys.stderr = oldio |
180 req.write('%d\n' % ret) | 173 yield '%d\n%s' % (ret, val) |
181 req.write(val) | |
182 finally: | 174 finally: |
183 del lock | 175 del lock |
184 except ValueError, inst: | 176 except ValueError, inst: |
185 req.write('0\n') | 177 yield errorfmt % inst |
186 req.write(str(inst) + '\n') | |
187 except (OSError, IOError), inst: | 178 except (OSError, IOError), inst: |
188 req.write('0\n') | |
189 filename = getattr(inst, 'filename', '') | 179 filename = getattr(inst, 'filename', '') |
190 # Don't send our filesystem layout to the client | 180 # Don't send our filesystem layout to the client |
191 if filename.startswith(repo.root): | 181 if filename.startswith(repo.root): |
192 filename = filename[len(repo.root)+1:] | 182 filename = filename[len(repo.root)+1:] |
193 else: | 183 else: |
196 if inst.errno == errno.ENOENT: | 186 if inst.errno == errno.ENOENT: |
197 code = HTTP_NOT_FOUND | 187 code = HTTP_NOT_FOUND |
198 else: | 188 else: |
199 code = HTTP_SERVER_ERROR | 189 code = HTTP_SERVER_ERROR |
200 req.respond(code) | 190 req.respond(code) |
201 req.write('%s: %s\n' % (error, filename)) | 191 yield '0\n%s: %s\n' % (error, filename) |
202 finally: | 192 finally: |
203 fp.close() | 193 fp.close() |
204 os.unlink(tempname) | 194 os.unlink(tempname) |
205 | 195 |
206 def stream_out(repo, req): | 196 def stream_out(repo, req): |
207 req.respond(HTTP_OK, HGTYPE) | 197 req.respond(HTTP_OK, HGTYPE) |
208 for chunk in streamclone.stream_out(repo, untrusted=True): | 198 return streamclone.stream_out(repo, untrusted=True) |
209 req.write(chunk) |