Show More
@@ -138,6 +138,119 b' class _multifile(object):' | |||||
138 | f.seek(0) |
|
138 | f.seek(0) | |
139 | self._index = 0 |
|
139 | self._index = 0 | |
140 |
|
140 | |||
|
141 | def makev1commandrequest(ui, requestbuilder, caps, capablefn, | |||
|
142 | repobaseurl, cmd, args): | |||
|
143 | """Make an HTTP request to run a command for a version 1 client. | |||
|
144 | ||||
|
145 | ``caps`` is a set of known server capabilities. The value may be | |||
|
146 | None if capabilities are not yet known. | |||
|
147 | ||||
|
148 | ``capablefn`` is a function to evaluate a capability. | |||
|
149 | ||||
|
150 | ``cmd``, ``args``, and ``data`` define the command, its arguments, and | |||
|
151 | raw data to pass to it. | |||
|
152 | """ | |||
|
153 | if cmd == 'pushkey': | |||
|
154 | args['data'] = '' | |||
|
155 | data = args.pop('data', None) | |||
|
156 | headers = args.pop('headers', {}) | |||
|
157 | ||||
|
158 | ui.debug("sending %s command\n" % cmd) | |||
|
159 | q = [('cmd', cmd)] | |||
|
160 | headersize = 0 | |||
|
161 | varyheaders = [] | |||
|
162 | # Important: don't use self.capable() here or else you end up | |||
|
163 | # with infinite recursion when trying to look up capabilities | |||
|
164 | # for the first time. | |||
|
165 | postargsok = caps is not None and 'httppostargs' in caps | |||
|
166 | ||||
|
167 | # Send arguments via POST. | |||
|
168 | if postargsok and args: | |||
|
169 | strargs = urlreq.urlencode(sorted(args.items())) | |||
|
170 | if not data: | |||
|
171 | data = strargs | |||
|
172 | else: | |||
|
173 | if isinstance(data, bytes): | |||
|
174 | i = io.BytesIO(data) | |||
|
175 | i.length = len(data) | |||
|
176 | data = i | |||
|
177 | argsio = io.BytesIO(strargs) | |||
|
178 | argsio.length = len(strargs) | |||
|
179 | data = _multifile(argsio, data) | |||
|
180 | headers[r'X-HgArgs-Post'] = len(strargs) | |||
|
181 | elif args: | |||
|
182 | # Calling self.capable() can infinite loop if we are calling | |||
|
183 | # "capabilities". But that command should never accept wire | |||
|
184 | # protocol arguments. So this should never happen. | |||
|
185 | assert cmd != 'capabilities' | |||
|
186 | httpheader = capablefn('httpheader') | |||
|
187 | if httpheader: | |||
|
188 | headersize = int(httpheader.split(',', 1)[0]) | |||
|
189 | ||||
|
190 | # Send arguments via HTTP headers. | |||
|
191 | if headersize > 0: | |||
|
192 | # The headers can typically carry more data than the URL. | |||
|
193 | encargs = urlreq.urlencode(sorted(args.items())) | |||
|
194 | for header, value in encodevalueinheaders(encargs, 'X-HgArg', | |||
|
195 | headersize): | |||
|
196 | headers[header] = value | |||
|
197 | varyheaders.append(header) | |||
|
198 | # Send arguments via query string (Mercurial <1.9). | |||
|
199 | else: | |||
|
200 | q += sorted(args.items()) | |||
|
201 | ||||
|
202 | qs = '?%s' % urlreq.urlencode(q) | |||
|
203 | cu = "%s%s" % (repobaseurl, qs) | |||
|
204 | size = 0 | |||
|
205 | if util.safehasattr(data, 'length'): | |||
|
206 | size = data.length | |||
|
207 | elif data is not None: | |||
|
208 | size = len(data) | |||
|
209 | if data is not None and r'Content-Type' not in headers: | |||
|
210 | headers[r'Content-Type'] = r'application/mercurial-0.1' | |||
|
211 | ||||
|
212 | # Tell the server we accept application/mercurial-0.2 and multiple | |||
|
213 | # compression formats if the server is capable of emitting those | |||
|
214 | # payloads. | |||
|
215 | protoparams = {'partial-pull'} | |||
|
216 | ||||
|
217 | mediatypes = set() | |||
|
218 | if caps is not None: | |||
|
219 | mt = capablefn('httpmediatype') | |||
|
220 | if mt: | |||
|
221 | protoparams.add('0.1') | |||
|
222 | mediatypes = set(mt.split(',')) | |||
|
223 | ||||
|
224 | if '0.2tx' in mediatypes: | |||
|
225 | protoparams.add('0.2') | |||
|
226 | ||||
|
227 | if '0.2tx' in mediatypes and capablefn('compression'): | |||
|
228 | # We /could/ compare supported compression formats and prune | |||
|
229 | # non-mutually supported or error if nothing is mutually supported. | |||
|
230 | # For now, send the full list to the server and have it error. | |||
|
231 | comps = [e.wireprotosupport().name for e in | |||
|
232 | util.compengines.supportedwireengines(util.CLIENTROLE)] | |||
|
233 | protoparams.add('comp=%s' % ','.join(comps)) | |||
|
234 | ||||
|
235 | if protoparams: | |||
|
236 | protoheaders = encodevalueinheaders(' '.join(sorted(protoparams)), | |||
|
237 | 'X-HgProto', | |||
|
238 | headersize or 1024) | |||
|
239 | for header, value in protoheaders: | |||
|
240 | headers[header] = value | |||
|
241 | varyheaders.append(header) | |||
|
242 | ||||
|
243 | if varyheaders: | |||
|
244 | headers[r'Vary'] = r','.join(varyheaders) | |||
|
245 | ||||
|
246 | req = requestbuilder(pycompat.strurl(cu), data, headers) | |||
|
247 | ||||
|
248 | if data is not None: | |||
|
249 | ui.debug("sending %d bytes\n" % size) | |||
|
250 | req.add_unredirected_header(r'Content-Length', r'%d' % size) | |||
|
251 | ||||
|
252 | return req, cu, qs | |||
|
253 | ||||
141 | def sendrequest(ui, opener, req): |
|
254 | def sendrequest(ui, opener, req): | |
142 | """Send a prepared HTTP request. |
|
255 | """Send a prepared HTTP request. | |
143 |
|
256 | |||
@@ -228,104 +341,11 b' class httppeer(wireproto.wirepeer):' | |||||
228 |
|
341 | |||
229 | def _callstream(self, cmd, _compressible=False, **args): |
|
342 | def _callstream(self, cmd, _compressible=False, **args): | |
230 | args = pycompat.byteskwargs(args) |
|
343 | args = pycompat.byteskwargs(args) | |
231 | if cmd == 'pushkey': |
|
|||
232 | args['data'] = '' |
|
|||
233 | data = args.pop('data', None) |
|
|||
234 | headers = args.pop('headers', {}) |
|
|||
235 |
|
||||
236 | self.ui.debug("sending %s command\n" % cmd) |
|
|||
237 | q = [('cmd', cmd)] |
|
|||
238 | headersize = 0 |
|
|||
239 | varyheaders = [] |
|
|||
240 | # Important: don't use self.capable() here or else you end up |
|
|||
241 | # with infinite recursion when trying to look up capabilities |
|
|||
242 | # for the first time. |
|
|||
243 | postargsok = self._caps is not None and 'httppostargs' in self._caps |
|
|||
244 |
|
||||
245 | # Send arguments via POST. |
|
|||
246 | if postargsok and args: |
|
|||
247 | strargs = urlreq.urlencode(sorted(args.items())) |
|
|||
248 | if not data: |
|
|||
249 | data = strargs |
|
|||
250 | else: |
|
|||
251 | if isinstance(data, bytes): |
|
|||
252 | i = io.BytesIO(data) |
|
|||
253 | i.length = len(data) |
|
|||
254 | data = i |
|
|||
255 | argsio = io.BytesIO(strargs) |
|
|||
256 | argsio.length = len(strargs) |
|
|||
257 | data = _multifile(argsio, data) |
|
|||
258 | headers[r'X-HgArgs-Post'] = len(strargs) |
|
|||
259 | elif args: |
|
|||
260 | # Calling self.capable() can infinite loop if we are calling |
|
|||
261 | # "capabilities". But that command should never accept wire |
|
|||
262 | # protocol arguments. So this should never happen. |
|
|||
263 | assert cmd != 'capabilities' |
|
|||
264 | httpheader = self.capable('httpheader') |
|
|||
265 | if httpheader: |
|
|||
266 | headersize = int(httpheader.split(',', 1)[0]) |
|
|||
267 |
|
||||
268 | # Send arguments via HTTP headers. |
|
|||
269 | if headersize > 0: |
|
|||
270 | # The headers can typically carry more data than the URL. |
|
|||
271 | encargs = urlreq.urlencode(sorted(args.items())) |
|
|||
272 | for header, value in encodevalueinheaders(encargs, 'X-HgArg', |
|
|||
273 | headersize): |
|
|||
274 | headers[header] = value |
|
|||
275 | varyheaders.append(header) |
|
|||
276 | # Send arguments via query string (Mercurial <1.9). |
|
|||
277 | else: |
|
|||
278 | q += sorted(args.items()) |
|
|||
279 |
|
344 | |||
280 | qs = '?%s' % urlreq.urlencode(q) |
|
345 | req, cu, qs = makev1commandrequest(self.ui, self._requestbuilder, | |
281 | cu = "%s%s" % (self._url, qs) |
|
346 | self._caps, self.capable, | |
282 | size = 0 |
|
347 | self._url, cmd, args) | |
283 | if util.safehasattr(data, 'length'): |
|
|||
284 | size = data.length |
|
|||
285 | elif data is not None: |
|
|||
286 | size = len(data) |
|
|||
287 | if data is not None and r'Content-Type' not in headers: |
|
|||
288 | headers[r'Content-Type'] = r'application/mercurial-0.1' |
|
|||
289 |
|
||||
290 | # Tell the server we accept application/mercurial-0.2 and multiple |
|
|||
291 | # compression formats if the server is capable of emitting those |
|
|||
292 | # payloads. |
|
|||
293 | protoparams = {'partial-pull'} |
|
|||
294 |
|
||||
295 | mediatypes = set() |
|
|||
296 | if self._caps is not None: |
|
|||
297 | mt = self.capable('httpmediatype') |
|
|||
298 | if mt: |
|
|||
299 | protoparams.add('0.1') |
|
|||
300 | mediatypes = set(mt.split(',')) |
|
|||
301 |
|
||||
302 | if '0.2tx' in mediatypes: |
|
|||
303 | protoparams.add('0.2') |
|
|||
304 |
|
348 | |||
305 | if '0.2tx' in mediatypes and self.capable('compression'): |
|
|||
306 | # We /could/ compare supported compression formats and prune |
|
|||
307 | # non-mutually supported or error if nothing is mutually supported. |
|
|||
308 | # For now, send the full list to the server and have it error. |
|
|||
309 | comps = [e.wireprotosupport().name for e in |
|
|||
310 | util.compengines.supportedwireengines(util.CLIENTROLE)] |
|
|||
311 | protoparams.add('comp=%s' % ','.join(comps)) |
|
|||
312 |
|
||||
313 | if protoparams: |
|
|||
314 | protoheaders = encodevalueinheaders(' '.join(sorted(protoparams)), |
|
|||
315 | 'X-HgProto', |
|
|||
316 | headersize or 1024) |
|
|||
317 | for header, value in protoheaders: |
|
|||
318 | headers[header] = value |
|
|||
319 | varyheaders.append(header) |
|
|||
320 |
|
||||
321 | if varyheaders: |
|
|||
322 | headers[r'Vary'] = r','.join(varyheaders) |
|
|||
323 |
|
||||
324 | req = self._requestbuilder(pycompat.strurl(cu), data, headers) |
|
|||
325 |
|
||||
326 | if data is not None: |
|
|||
327 | self.ui.debug("sending %d bytes\n" % size) |
|
|||
328 | req.add_unredirected_header(r'Content-Length', r'%d' % size) |
|
|||
329 | try: |
|
349 | try: | |
330 | resp = sendrequest(self.ui, self._urlopener, req) |
|
350 | resp = sendrequest(self.ui, self._urlopener, req) | |
331 | except urlerr.httperror as inst: |
|
351 | except urlerr.httperror as inst: |
General Comments 0
You need to be logged in to leave comments.
Login now