Show More
@@ -21,6 +21,7 b' from .thirdparty import (' | |||||
21 | from . import ( |
|
21 | from . import ( | |
22 | encoding, |
|
22 | encoding, | |
23 | error, |
|
23 | error, | |
|
24 | pycompat, | |||
24 | util, |
|
25 | util, | |
25 | wireprototypes, |
|
26 | wireprototypes, | |
26 | ) |
|
27 | ) | |
@@ -429,6 +430,26 b' def createcommandresponseeosframe(stream' | |||||
429 | flags=FLAG_COMMAND_RESPONSE_EOS, |
|
430 | flags=FLAG_COMMAND_RESPONSE_EOS, | |
430 | payload=b'') |
|
431 | payload=b'') | |
431 |
|
432 | |||
|
433 | def createalternatelocationresponseframe(stream, requestid, location): | |||
|
434 | data = { | |||
|
435 | b'status': b'redirect', | |||
|
436 | b'location': { | |||
|
437 | b'url': location.url, | |||
|
438 | b'mediatype': location.mediatype, | |||
|
439 | } | |||
|
440 | } | |||
|
441 | ||||
|
442 | for a in (r'size', r'fullhashes', r'fullhashseed', r'serverdercerts', | |||
|
443 | r'servercadercerts'): | |||
|
444 | value = getattr(location, a) | |||
|
445 | if value is not None: | |||
|
446 | data[b'location'][pycompat.bytestr(a)] = value | |||
|
447 | ||||
|
448 | return stream.makeframe(requestid=requestid, | |||
|
449 | typeid=FRAME_TYPE_COMMAND_RESPONSE, | |||
|
450 | flags=FLAG_COMMAND_RESPONSE_CONTINUATION, | |||
|
451 | payload=b''.join(cborutil.streamencode(data))) | |||
|
452 | ||||
432 | def createcommanderrorresponse(stream, requestid, message, args=None): |
|
453 | def createcommanderrorresponse(stream, requestid, message, args=None): | |
433 | # TODO should this be using a list of {'msg': ..., 'args': {}} so atom |
|
454 | # TODO should this be using a list of {'msg': ..., 'args': {}} so atom | |
434 | # formatting works consistently? |
|
455 | # formatting works consistently? | |
@@ -813,6 +834,7 b' class serverreactor(object):' | |||||
813 |
|
834 | |||
814 | def sendframes(): |
|
835 | def sendframes(): | |
815 | emitted = False |
|
836 | emitted = False | |
|
837 | alternatelocationsent = False | |||
816 | emitter = bufferingcommandresponseemitter(stream, requestid) |
|
838 | emitter = bufferingcommandresponseemitter(stream, requestid) | |
817 | while True: |
|
839 | while True: | |
818 | try: |
|
840 | try: | |
@@ -841,6 +863,25 b' class serverreactor(object):' | |||||
841 | break |
|
863 | break | |
842 |
|
864 | |||
843 | try: |
|
865 | try: | |
|
866 | # Alternate location responses can only be the first and | |||
|
867 | # only object in the output stream. | |||
|
868 | if isinstance(o, wireprototypes.alternatelocationresponse): | |||
|
869 | if emitted: | |||
|
870 | raise error.ProgrammingError( | |||
|
871 | 'alternatelocationresponse seen after initial ' | |||
|
872 | 'output object') | |||
|
873 | ||||
|
874 | yield createalternatelocationresponseframe( | |||
|
875 | stream, requestid, o) | |||
|
876 | ||||
|
877 | alternatelocationsent = True | |||
|
878 | emitted = True | |||
|
879 | continue | |||
|
880 | ||||
|
881 | if alternatelocationsent: | |||
|
882 | raise error.ProgrammingError( | |||
|
883 | 'object follows alternatelocationresponse') | |||
|
884 | ||||
844 | if not emitted: |
|
885 | if not emitted: | |
845 | yield createcommandresponseokframe(stream, requestid) |
|
886 | yield createcommandresponseokframe(stream, requestid) | |
846 | emitted = True |
|
887 | emitted = True | |
@@ -977,6 +1018,7 b' class serverreactor(object):' | |||||
977 | 'requestid': requestid, |
|
1018 | 'requestid': requestid, | |
978 | 'command': request[b'name'], |
|
1019 | 'command': request[b'name'], | |
979 | 'args': request[b'args'], |
|
1020 | 'args': request[b'args'], | |
|
1021 | 'redirect': request.get(b'redirect'), | |||
980 | 'data': entry['data'].getvalue() if entry['data'] else None, |
|
1022 | 'data': entry['data'].getvalue() if entry['data'] else None, | |
981 | } |
|
1023 | } | |
982 |
|
1024 |
@@ -368,3 +368,20 b' class encodedresponse(object):' | |||||
368 | and the content from this object is used instead. |
|
368 | and the content from this object is used instead. | |
369 | """ |
|
369 | """ | |
370 | data = attr.ib() |
|
370 | data = attr.ib() | |
|
371 | ||||
|
372 | @attr.s | |||
|
373 | class alternatelocationresponse(object): | |||
|
374 | """Represents a response available at an alternate location. | |||
|
375 | ||||
|
376 | Instances are sent in place of actual response objects when the server | |||
|
377 | is sending a "content redirect" response. | |||
|
378 | ||||
|
379 | Only compatible with wire protocol version 2. | |||
|
380 | """ | |||
|
381 | url = attr.ib() | |||
|
382 | mediatype = attr.ib() | |||
|
383 | size = attr.ib(default=None) | |||
|
384 | fullhashes = attr.ib(default=None) | |||
|
385 | fullhashseed = attr.ib(default=None) | |||
|
386 | serverdercerts = attr.ib(default=None) | |||
|
387 | servercadercerts = attr.ib(default=None) |
@@ -312,7 +312,7 b' def _httpv2runcommand(ui, repo, req, res' | |||||
312 | res.headers[b'Content-Type'] = FRAMINGTYPE |
|
312 | res.headers[b'Content-Type'] = FRAMINGTYPE | |
313 |
|
313 | |||
314 | try: |
|
314 | try: | |
315 | objs = dispatch(repo, proto, command['command']) |
|
315 | objs = dispatch(repo, proto, command['command'], command['redirect']) | |
316 |
|
316 | |||
317 | action, meta = reactor.oncommandresponsereadyobjects( |
|
317 | action, meta = reactor.oncommandresponsereadyobjects( | |
318 | outstream, command['requestid'], objs) |
|
318 | outstream, command['requestid'], objs) | |
@@ -339,7 +339,7 b' def _httpv2runcommand(ui, repo, req, res' | |||||
339 | def getdispatchrepo(repo, proto, command): |
|
339 | def getdispatchrepo(repo, proto, command): | |
340 | return repo.filtered('served') |
|
340 | return repo.filtered('served') | |
341 |
|
341 | |||
342 | def dispatch(repo, proto, command): |
|
342 | def dispatch(repo, proto, command, redirect): | |
343 | """Run a wire protocol command. |
|
343 | """Run a wire protocol command. | |
344 |
|
344 | |||
345 | Returns an iterable of objects that will be sent to the client. |
|
345 | Returns an iterable of objects that will be sent to the client. | |
@@ -364,8 +364,17 b' def dispatch(repo, proto, command):' | |||||
364 | yield o |
|
364 | yield o | |
365 | return |
|
365 | return | |
366 |
|
366 | |||
|
367 | if redirect: | |||
|
368 | redirecttargets = redirect[b'targets'] | |||
|
369 | redirecthashes = redirect[b'hashes'] | |||
|
370 | else: | |||
|
371 | redirecttargets = [] | |||
|
372 | redirecthashes = [] | |||
|
373 | ||||
367 | cacher = makeresponsecacher(repo, proto, command, args, |
|
374 | cacher = makeresponsecacher(repo, proto, command, args, | |
368 |
cborutil.streamencode |
|
375 | cborutil.streamencode, | |
|
376 | redirecttargets=redirecttargets, | |||
|
377 | redirecthashes=redirecthashes) | |||
369 |
|
378 | |||
370 | # But we have no cacher. Do default handling. |
|
379 | # But we have no cacher. Do default handling. | |
371 | if not cacher: |
|
380 | if not cacher: | |
@@ -751,7 +760,8 b' def makecommandcachekeyfn(command, local' | |||||
751 |
|
760 | |||
752 | return cachekeyfn |
|
761 | return cachekeyfn | |
753 |
|
762 | |||
754 |
def makeresponsecacher(repo, proto, command, args, objencoderfn |
|
763 | def makeresponsecacher(repo, proto, command, args, objencoderfn, | |
|
764 | redirecttargets, redirecthashes): | |||
755 | """Construct a cacher for a cacheable command. |
|
765 | """Construct a cacher for a cacheable command. | |
756 |
|
766 | |||
757 | Returns an ``iwireprotocolcommandcacher`` instance. |
|
767 | Returns an ``iwireprotocolcommandcacher`` instance. |
@@ -430,10 +430,10 b' Command frames can be reflected via debu' | |||||
430 | s> Server: testing stub value\r\n |
|
430 | s> Server: testing stub value\r\n | |
431 | s> Date: $HTTP_DATE$\r\n |
|
431 | s> Date: $HTTP_DATE$\r\n | |
432 | s> Content-Type: text/plain\r\n |
|
432 | s> Content-Type: text/plain\r\n | |
433 |
s> Content-Length: 2 |
|
433 | s> Content-Length: 223\r\n | |
434 | s> \r\n |
|
434 | s> \r\n | |
435 | s> received: 1 1 1 \xa2Dargs\xa2Dbar1CvalCfooDval1DnameHcommand1\n |
|
435 | s> received: 1 1 1 \xa2Dargs\xa2Dbar1CvalCfooDval1DnameHcommand1\n | |
436 | s> ["runcommand", {"args": {"bar1": "val", "foo": "val1"}, "command": "command1", "data": null, "requestid": 1}]\n |
|
436 | s> ["runcommand", {"args": {"bar1": "val", "foo": "val1"}, "command": "command1", "data": null, "redirect": null, "requestid": 1}]\n | |
437 | s> received: <no frame>\n |
|
437 | s> received: <no frame>\n | |
438 | s> {"action": "noop"} |
|
438 | s> {"action": "noop"} | |
439 |
|
439 |
@@ -1,11 +1,20 b'' | |||||
1 | $ . $TESTDIR/wireprotohelpers.sh |
|
1 | $ . $TESTDIR/wireprotohelpers.sh | |
2 |
|
2 | |||
|
3 | $ cat >> $HGRCPATH << EOF | |||
|
4 | > [extensions] | |||
|
5 | > blackbox = | |||
|
6 | > [blackbox] | |||
|
7 | > track = simplecache | |||
|
8 | > EOF | |||
|
9 | ||||
3 | $ hg init server |
|
10 | $ hg init server | |
4 | $ enablehttpv2 server |
|
11 | $ enablehttpv2 server | |
5 | $ cd server |
|
12 | $ cd server | |
6 | $ cat >> .hg/hgrc << EOF |
|
13 | $ cat >> .hg/hgrc << EOF | |
7 | > [extensions] |
|
14 | > [extensions] | |
8 | > simplecache = $TESTDIR/wireprotosimplecache.py |
|
15 | > simplecache = $TESTDIR/wireprotosimplecache.py | |
|
16 | > [simplecache] | |||
|
17 | > cacheapi = true | |||
9 | > EOF |
|
18 | > EOF | |
10 |
|
19 | |||
11 | $ echo a0 > a |
|
20 | $ echo a0 > a | |
@@ -1183,5 +1192,178 b' Unknown tls value is filtered from compa' | |||||
1183 | } |
|
1192 | } | |
1184 | ] |
|
1193 | ] | |
1185 |
|
1194 | |||
|
1195 | Set up the server to issue content redirects to its built-in API server. | |||
|
1196 | ||||
|
1197 | $ cat > redirects.py << EOF | |||
|
1198 | > [ | |||
|
1199 | > { | |||
|
1200 | > b'name': b'local', | |||
|
1201 | > b'protocol': b'http', | |||
|
1202 | > b'uris': [b'http://example.com/'], | |||
|
1203 | > }, | |||
|
1204 | > ] | |||
|
1205 | > EOF | |||
|
1206 | ||||
|
1207 | Request to eventual cache URL should return 404 (validating the cache server works) | |||
|
1208 | ||||
|
1209 | $ sendhttpraw << EOF | |||
|
1210 | > httprequest GET api/simplecache/missingkey | |||
|
1211 | > user-agent: test | |||
|
1212 | > EOF | |||
|
1213 | using raw connection to peer | |||
|
1214 | s> GET /api/simplecache/missingkey HTTP/1.1\r\n | |||
|
1215 | s> Accept-Encoding: identity\r\n | |||
|
1216 | s> user-agent: test\r\n | |||
|
1217 | s> host: $LOCALIP:$HGPORT\r\n (glob) | |||
|
1218 | s> \r\n | |||
|
1219 | s> makefile('rb', None) | |||
|
1220 | s> HTTP/1.1 404 Not Found\r\n | |||
|
1221 | s> Server: testing stub value\r\n | |||
|
1222 | s> Date: $HTTP_DATE$\r\n | |||
|
1223 | s> Content-Type: text/plain\r\n | |||
|
1224 | s> Content-Length: 22\r\n | |||
|
1225 | s> \r\n | |||
|
1226 | s> key not found in cache | |||
|
1227 | ||||
|
1228 | Send a cacheable request | |||
|
1229 | ||||
|
1230 | $ sendhttpv2peer << EOF | |||
|
1231 | > command manifestdata | |||
|
1232 | > nodes eval:[b'\x99\x2f\x47\x79\x02\x9a\x3d\xf8\xd0\x66\x6d\x00\xbb\x92\x4f\x69\x63\x4e\x26\x41'] | |||
|
1233 | > tree eval:b'' | |||
|
1234 | > fields eval:[b'parents'] | |||
|
1235 | > EOF | |||
|
1236 | creating http peer for wire protocol version 2 | |||
|
1237 | sending manifestdata command | |||
|
1238 | s> POST /api/exp-http-v2-0002/ro/manifestdata HTTP/1.1\r\n | |||
|
1239 | s> Accept-Encoding: identity\r\n | |||
|
1240 | s> accept: application/mercurial-exp-framing-0005\r\n | |||
|
1241 | s> content-type: application/mercurial-exp-framing-0005\r\n | |||
|
1242 | s> content-length: 128\r\n | |||
|
1243 | s> host: $LOCALIP:$HGPORT\r\n (glob) | |||
|
1244 | s> user-agent: Mercurial debugwireproto\r\n | |||
|
1245 | s> \r\n | |||
|
1246 | s> x\x00\x00\x01\x00\x01\x01\x11\xa3Dargs\xa3Ffields\x81GparentsEnodes\x81T\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&ADtree@DnameLmanifestdataHredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x81Elocal | |||
|
1247 | s> makefile('rb', None) | |||
|
1248 | s> HTTP/1.1 200 OK\r\n | |||
|
1249 | s> Server: testing stub value\r\n | |||
|
1250 | s> Date: $HTTP_DATE$\r\n | |||
|
1251 | s> Content-Type: application/mercurial-exp-framing-0005\r\n | |||
|
1252 | s> Transfer-Encoding: chunked\r\n | |||
|
1253 | s> \r\n | |||
|
1254 | s> 13\r\n | |||
|
1255 | s> \x0b\x00\x00\x01\x00\x02\x011 | |||
|
1256 | s> \xa1FstatusBok | |||
|
1257 | s> \r\n | |||
|
1258 | received frame(size=11; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=continuation) | |||
|
1259 | s> 63\r\n | |||
|
1260 | s> [\x00\x00\x01\x00\x02\x001 | |||
|
1261 | s> \xa1Jtotalitems\x01\xa2DnodeT\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&AGparents\x82T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 | |||
|
1262 | s> \r\n | |||
|
1263 | received frame(size=91; request=1; stream=2; streamflags=; type=command-response; flags=continuation) | |||
|
1264 | s> 8\r\n | |||
|
1265 | s> \x00\x00\x00\x01\x00\x02\x002 | |||
|
1266 | s> \r\n | |||
|
1267 | s> 0\r\n | |||
|
1268 | s> \r\n | |||
|
1269 | received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos) | |||
|
1270 | response: gen[ | |||
|
1271 | { | |||
|
1272 | b'totalitems': 1 | |||
|
1273 | }, | |||
|
1274 | { | |||
|
1275 | b'node': b'\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A', | |||
|
1276 | b'parents': [ | |||
|
1277 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', | |||
|
1278 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' | |||
|
1279 | ] | |||
|
1280 | } | |||
|
1281 | ] | |||
|
1282 | ||||
|
1283 | Cached entry should be available on server | |||
|
1284 | ||||
|
1285 | $ sendhttpraw << EOF | |||
|
1286 | > httprequest GET api/simplecache/c045a581599d58608efd3d93d8129841f2af04a0 | |||
|
1287 | > user-agent: test | |||
|
1288 | > EOF | |||
|
1289 | using raw connection to peer | |||
|
1290 | s> GET /api/simplecache/c045a581599d58608efd3d93d8129841f2af04a0 HTTP/1.1\r\n | |||
|
1291 | s> Accept-Encoding: identity\r\n | |||
|
1292 | s> user-agent: test\r\n | |||
|
1293 | s> host: $LOCALIP:$HGPORT\r\n (glob) | |||
|
1294 | s> \r\n | |||
|
1295 | s> makefile('rb', None) | |||
|
1296 | s> HTTP/1.1 200 OK\r\n | |||
|
1297 | s> Server: testing stub value\r\n | |||
|
1298 | s> Date: $HTTP_DATE$\r\n | |||
|
1299 | s> Content-Type: application/mercurial-cbor\r\n | |||
|
1300 | s> Content-Length: 91\r\n | |||
|
1301 | s> \r\n | |||
|
1302 | s> \xa1Jtotalitems\x01\xa2DnodeT\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&AGparents\x82T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 | |||
|
1303 | cbor> [ | |||
|
1304 | { | |||
|
1305 | b'totalitems': 1 | |||
|
1306 | }, | |||
|
1307 | { | |||
|
1308 | b'node': b'\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A', | |||
|
1309 | b'parents': [ | |||
|
1310 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', | |||
|
1311 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' | |||
|
1312 | ] | |||
|
1313 | } | |||
|
1314 | ] | |||
|
1315 | ||||
|
1316 | 2nd request should result in content redirect response | |||
|
1317 | ||||
|
1318 | $ sendhttpv2peer << EOF | |||
|
1319 | > command manifestdata | |||
|
1320 | > nodes eval:[b'\x99\x2f\x47\x79\x02\x9a\x3d\xf8\xd0\x66\x6d\x00\xbb\x92\x4f\x69\x63\x4e\x26\x41'] | |||
|
1321 | > tree eval:b'' | |||
|
1322 | > fields eval:[b'parents'] | |||
|
1323 | > EOF | |||
|
1324 | creating http peer for wire protocol version 2 | |||
|
1325 | sending manifestdata command | |||
|
1326 | s> POST /api/exp-http-v2-0002/ro/manifestdata HTTP/1.1\r\n | |||
|
1327 | s> Accept-Encoding: identity\r\n | |||
|
1328 | s> accept: application/mercurial-exp-framing-0005\r\n | |||
|
1329 | s> content-type: application/mercurial-exp-framing-0005\r\n | |||
|
1330 | s> content-length: 128\r\n | |||
|
1331 | s> host: $LOCALIP:$HGPORT\r\n (glob) | |||
|
1332 | s> user-agent: Mercurial debugwireproto\r\n | |||
|
1333 | s> \r\n | |||
|
1334 | s> x\x00\x00\x01\x00\x01\x01\x11\xa3Dargs\xa3Ffields\x81GparentsEnodes\x81T\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&ADtree@DnameLmanifestdataHredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x81Elocal | |||
|
1335 | s> makefile('rb', None) | |||
|
1336 | s> HTTP/1.1 200 OK\r\n | |||
|
1337 | s> Server: testing stub value\r\n | |||
|
1338 | s> Date: $HTTP_DATE$\r\n | |||
|
1339 | s> Content-Type: application/mercurial-exp-framing-0005\r\n | |||
|
1340 | s> Transfer-Encoding: chunked\r\n | |||
|
1341 | s> \r\n | |||
|
1342 | s> *\r\n (glob) | |||
|
1343 | s> \x*\x00\x00\x01\x00\x02\x011 (glob) | |||
|
1344 | s> \xa2Hlocation\xa2ImediatypeX\x1aapplication/mercurial-cborCurl*http://*:$HGPORT/api/simplecache/c045a581599d58608efd3d93d8129841f2af04a0FstatusHredirect (glob) | |||
|
1345 | s> \r\n | |||
|
1346 | received frame(size=*; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=continuation) (glob) | |||
|
1347 | s> 8\r\n | |||
|
1348 | s> \x00\x00\x00\x01\x00\x02\x001 | |||
|
1349 | s> \r\n | |||
|
1350 | s> 8\r\n | |||
|
1351 | received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=continuation) | |||
|
1352 | s> \x00\x00\x00\x01\x00\x02\x002 | |||
|
1353 | s> \r\n | |||
|
1354 | s> 0\r\n | |||
|
1355 | s> \r\n | |||
|
1356 | received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos) | |||
|
1357 | abort: redirect responses not yet supported | |||
|
1358 | [255] | |||
|
1359 | ||||
1186 |
$ |
|
1360 | $ cat error.log | |
1187 | $ killdaemons.py |
|
1361 | $ killdaemons.py | |
|
1362 | ||||
|
1363 | $ cat .hg/blackbox.log | |||
|
1364 | *> cacher constructed for manifestdata (glob) | |||
|
1365 | *> cache miss for c045a581599d58608efd3d93d8129841f2af04a0 (glob) | |||
|
1366 | *> storing cache entry for c045a581599d58608efd3d93d8129841f2af04a0 (glob) | |||
|
1367 | *> cacher constructed for manifestdata (glob) | |||
|
1368 | *> cache hit for c045a581599d58608efd3d93d8129841f2af04a0 (glob) | |||
|
1369 | *> sending content redirect for c045a581599d58608efd3d93d8129841f2af04a0 to http://*:$HGPORT/api/simplecache/c045a581599d58608efd3d93d8129841f2af04a0 (glob) |
@@ -69,6 +69,7 b' class ServerReactorTests(unittest.TestCa' | |||||
69 | b'requestid': 1, |
|
69 | b'requestid': 1, | |
70 | b'command': b'mycommand', |
|
70 | b'command': b'mycommand', | |
71 | b'args': {}, |
|
71 | b'args': {}, | |
|
72 | b'redirect': None, | |||
72 | b'data': None, |
|
73 | b'data': None, | |
73 | }) |
|
74 | }) | |
74 |
|
75 | |||
@@ -86,6 +87,7 b' class ServerReactorTests(unittest.TestCa' | |||||
86 | b'requestid': 41, |
|
87 | b'requestid': 41, | |
87 | b'command': b'mycommand', |
|
88 | b'command': b'mycommand', | |
88 | b'args': {b'foo': b'bar'}, |
|
89 | b'args': {b'foo': b'bar'}, | |
|
90 | b'redirect': None, | |||
89 | b'data': None, |
|
91 | b'data': None, | |
90 | }) |
|
92 | }) | |
91 |
|
93 | |||
@@ -100,6 +102,7 b' class ServerReactorTests(unittest.TestCa' | |||||
100 | b'requestid': 1, |
|
102 | b'requestid': 1, | |
101 | b'command': b'mycommand', |
|
103 | b'command': b'mycommand', | |
102 | b'args': {b'foo': b'bar', b'biz': b'baz'}, |
|
104 | b'args': {b'foo': b'bar', b'biz': b'baz'}, | |
|
105 | b'redirect': None, | |||
103 | b'data': None, |
|
106 | b'data': None, | |
104 | }) |
|
107 | }) | |
105 |
|
108 | |||
@@ -115,6 +118,7 b' class ServerReactorTests(unittest.TestCa' | |||||
115 | b'requestid': 1, |
|
118 | b'requestid': 1, | |
116 | b'command': b'mycommand', |
|
119 | b'command': b'mycommand', | |
117 | b'args': {}, |
|
120 | b'args': {}, | |
|
121 | b'redirect': None, | |||
118 | b'data': b'data!', |
|
122 | b'data': b'data!', | |
119 | }) |
|
123 | }) | |
120 |
|
124 | |||
@@ -137,6 +141,7 b' class ServerReactorTests(unittest.TestCa' | |||||
137 | b'requestid': 1, |
|
141 | b'requestid': 1, | |
138 | b'command': b'mycommand', |
|
142 | b'command': b'mycommand', | |
139 | b'args': {}, |
|
143 | b'args': {}, | |
|
144 | b'redirect': None, | |||
140 | b'data': b'data1data2data3', |
|
145 | b'data': b'data1data2data3', | |
141 | }) |
|
146 | }) | |
142 |
|
147 | |||
@@ -160,6 +165,7 b' class ServerReactorTests(unittest.TestCa' | |||||
160 | b'key': b'val', |
|
165 | b'key': b'val', | |
161 | b'foo': b'bar', |
|
166 | b'foo': b'bar', | |
162 | }, |
|
167 | }, | |
|
168 | b'redirect': None, | |||
163 | b'data': b'value1value2', |
|
169 | b'data': b'value1value2', | |
164 | }) |
|
170 | }) | |
165 |
|
171 | |||
@@ -235,6 +241,7 b' class ServerReactorTests(unittest.TestCa' | |||||
235 | b'requestid': 1, |
|
241 | b'requestid': 1, | |
236 | b'command': b'command', |
|
242 | b'command': b'command', | |
237 | b'args': {}, |
|
243 | b'args': {}, | |
|
244 | b'redirect': None, | |||
238 | b'data': None, |
|
245 | b'data': None, | |
239 | }) |
|
246 | }) | |
240 |
|
247 | |||
@@ -291,12 +298,14 b' class ServerReactorTests(unittest.TestCa' | |||||
291 | b'requestid': 3, |
|
298 | b'requestid': 3, | |
292 | b'command': b'command3', |
|
299 | b'command': b'command3', | |
293 | b'args': {b'biz': b'baz', b'key': b'val'}, |
|
300 | b'args': {b'biz': b'baz', b'key': b'val'}, | |
|
301 | b'redirect': None, | |||
294 | b'data': None, |
|
302 | b'data': None, | |
295 | }) |
|
303 | }) | |
296 | self.assertEqual(results[5][1], { |
|
304 | self.assertEqual(results[5][1], { | |
297 | b'requestid': 1, |
|
305 | b'requestid': 1, | |
298 | b'command': b'command1', |
|
306 | b'command': b'command1', | |
299 | b'args': {b'foo': b'bar', b'key1': b'val'}, |
|
307 | b'args': {b'foo': b'bar', b'key1': b'val'}, | |
|
308 | b'redirect': None, | |||
300 | b'data': None, |
|
309 | b'data': None, | |
301 | }) |
|
310 | }) | |
302 |
|
311 |
@@ -12,6 +12,7 b' from mercurial import (' | |||||
12 | registrar, |
|
12 | registrar, | |
13 | repository, |
|
13 | repository, | |
14 | util, |
|
14 | util, | |
|
15 | wireprotoserver, | |||
15 | wireprototypes, |
|
16 | wireprototypes, | |
16 | wireprotov2server, |
|
17 | wireprotov2server, | |
17 | ) |
|
18 | ) | |
@@ -25,18 +26,59 b' CACHE = None' | |||||
25 | configtable = {} |
|
26 | configtable = {} | |
26 | configitem = registrar.configitem(configtable) |
|
27 | configitem = registrar.configitem(configtable) | |
27 |
|
28 | |||
|
29 | configitem('simplecache', 'cacheapi', | |||
|
30 | default=False) | |||
28 | configitem('simplecache', 'cacheobjects', |
|
31 | configitem('simplecache', 'cacheobjects', | |
29 | default=False) |
|
32 | default=False) | |
30 | configitem('simplecache', 'redirectsfile', |
|
33 | configitem('simplecache', 'redirectsfile', | |
31 | default=None) |
|
34 | default=None) | |
32 |
|
35 | |||
|
36 | # API handler that makes cached keys available. | |||
|
37 | def handlecacherequest(rctx, req, res, checkperm, urlparts): | |||
|
38 | if rctx.repo.ui.configbool('simplecache', 'cacheobjects'): | |||
|
39 | res.status = b'500 Internal Server Error' | |||
|
40 | res.setbodybytes(b'cacheobjects not supported for api server') | |||
|
41 | return | |||
|
42 | ||||
|
43 | if not urlparts: | |||
|
44 | res.status = b'200 OK' | |||
|
45 | res.headers[b'Content-Type'] = b'text/plain' | |||
|
46 | res.setbodybytes(b'simple cache server') | |||
|
47 | return | |||
|
48 | ||||
|
49 | key = b'/'.join(urlparts) | |||
|
50 | ||||
|
51 | if key not in CACHE: | |||
|
52 | res.status = b'404 Not Found' | |||
|
53 | res.headers[b'Content-Type'] = b'text/plain' | |||
|
54 | res.setbodybytes(b'key not found in cache') | |||
|
55 | return | |||
|
56 | ||||
|
57 | res.status = b'200 OK' | |||
|
58 | res.headers[b'Content-Type'] = b'application/mercurial-cbor' | |||
|
59 | res.setbodybytes(CACHE[key]) | |||
|
60 | ||||
|
61 | def cachedescriptor(req, repo): | |||
|
62 | return {} | |||
|
63 | ||||
|
64 | wireprotoserver.API_HANDLERS[b'simplecache'] = { | |||
|
65 | 'config': (b'simplecache', b'cacheapi'), | |||
|
66 | 'handler': handlecacherequest, | |||
|
67 | 'apidescriptor': cachedescriptor, | |||
|
68 | } | |||
|
69 | ||||
33 | @interfaceutil.implementer(repository.iwireprotocolcommandcacher) |
|
70 | @interfaceutil.implementer(repository.iwireprotocolcommandcacher) | |
34 | class memorycacher(object): |
|
71 | class memorycacher(object): | |
35 |
def __init__(self, ui, command, encodefn |
|
72 | def __init__(self, ui, command, encodefn, redirecttargets, redirecthashes, | |
|
73 | req): | |||
36 | self.ui = ui |
|
74 | self.ui = ui | |
37 | self.encodefn = encodefn |
|
75 | self.encodefn = encodefn | |
|
76 | self.redirecttargets = redirecttargets | |||
|
77 | self.redirecthashes = redirecthashes | |||
|
78 | self.req = req | |||
38 | self.key = None |
|
79 | self.key = None | |
39 | self.cacheobjects = ui.configbool('simplecache', 'cacheobjects') |
|
80 | self.cacheobjects = ui.configbool('simplecache', 'cacheobjects') | |
|
81 | self.cacheapi = ui.configbool('simplecache', 'cacheapi') | |||
40 | self.buffered = [] |
|
82 | self.buffered = [] | |
41 |
|
83 | |||
42 | ui.log('simplecache', 'cacher constructed for %s\n', command) |
|
84 | ui.log('simplecache', 'cacher constructed for %s\n', command) | |
@@ -65,6 +107,37 b' class memorycacher(object):' | |||||
65 | entry = CACHE[self.key] |
|
107 | entry = CACHE[self.key] | |
66 | self.ui.log('simplecache', 'cache hit for %s\n', self.key) |
|
108 | self.ui.log('simplecache', 'cache hit for %s\n', self.key) | |
67 |
|
109 | |||
|
110 | redirectable = True | |||
|
111 | ||||
|
112 | if not self.cacheapi: | |||
|
113 | redirectable = False | |||
|
114 | elif not self.redirecttargets: | |||
|
115 | redirectable = False | |||
|
116 | else: | |||
|
117 | clienttargets = set(self.redirecttargets) | |||
|
118 | ourtargets = set(t[b'name'] for t in loadredirecttargets(self.ui)) | |||
|
119 | ||||
|
120 | # We only ever redirect to a single target (for now). So we don't | |||
|
121 | # need to store which target matched. | |||
|
122 | if not clienttargets & ourtargets: | |||
|
123 | redirectable = False | |||
|
124 | ||||
|
125 | if redirectable: | |||
|
126 | paths = self.req.dispatchparts[:-3] | |||
|
127 | paths.append(b'simplecache') | |||
|
128 | paths.append(self.key) | |||
|
129 | ||||
|
130 | url = b'%s/%s' % (self.req.advertisedbaseurl, b'/'.join(paths)) | |||
|
131 | ||||
|
132 | #url = b'http://example.com/%s' % self.key | |||
|
133 | self.ui.log('simplecache', 'sending content redirect for %s to ' | |||
|
134 | '%s\n', self.key, url) | |||
|
135 | response = wireprototypes.alternatelocationresponse( | |||
|
136 | url=url, | |||
|
137 | mediatype=b'application/mercurial-cbor') | |||
|
138 | ||||
|
139 | return {'objs': [response]} | |||
|
140 | ||||
68 | if self.cacheobjects: |
|
141 | if self.cacheobjects: | |
69 | return { |
|
142 | return { | |
70 | 'objs': entry, |
|
143 | 'objs': entry, | |
@@ -91,8 +164,10 b' class memorycacher(object):' | |||||
91 |
|
164 | |||
92 | return [] |
|
165 | return [] | |
93 |
|
166 | |||
94 |
def makeresponsecacher(orig, repo, proto, command, args, objencoderfn |
|
167 | def makeresponsecacher(orig, repo, proto, command, args, objencoderfn, | |
95 | return memorycacher(repo.ui, command, objencoderfn) |
|
168 | redirecttargets, redirecthashes): | |
|
169 | return memorycacher(repo.ui, command, objencoderfn, redirecttargets, | |||
|
170 | redirecthashes, proto._req) | |||
96 |
|
171 | |||
97 | def loadredirecttargets(ui): |
|
172 | def loadredirecttargets(ui): | |
98 | path = ui.config('simplecache', 'redirectsfile') |
|
173 | path = ui.config('simplecache', 'redirectsfile') |
General Comments 0
You need to be logged in to leave comments.
Login now