wireprotosimplecache.py
193 lines
| 5.8 KiB
| text/x-python
|
PythonLexer
/ tests / wireprotosimplecache.py
Gregory Szorc
|
r40057 | # wireprotosimplecache.py - Extension providing in-memory wire protocol cache | ||
# | ||||
# Copyright 2018 Gregory Szorc <gregory.szorc@gmail.com> | ||||
# | ||||
# This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | ||||
from __future__ import absolute_import | ||||
from mercurial import ( | ||||
extensions, | ||||
registrar, | ||||
repository, | ||||
util, | ||||
Gregory Szorc
|
r40061 | wireprotoserver, | ||
Gregory Szorc
|
r40057 | wireprototypes, | ||
wireprotov2server, | ||||
) | ||||
from mercurial.utils import ( | ||||
interfaceutil, | ||||
Gregory Szorc
|
r40059 | stringutil, | ||
Gregory Szorc
|
r40057 | ) | ||
CACHE = None | ||||
configtable = {} | ||||
configitem = registrar.configitem(configtable) | ||||
Pulkit Goyal
|
r40254 | configitem(b'simplecache', b'cacheapi', | ||
Gregory Szorc
|
r40061 | default=False) | ||
Pulkit Goyal
|
r40254 | configitem(b'simplecache', b'cacheobjects', | ||
Gregory Szorc
|
r40057 | default=False) | ||
Pulkit Goyal
|
r40254 | configitem(b'simplecache', b'redirectsfile', | ||
Gregory Szorc
|
r40059 | default=None) | ||
Gregory Szorc
|
r40057 | |||
Gregory Szorc
|
r40061 | # API handler that makes cached keys available. | ||
def handlecacherequest(rctx, req, res, checkperm, urlparts): | ||||
Pulkit Goyal
|
r40254 | if rctx.repo.ui.configbool(b'simplecache', b'cacheobjects'): | ||
Gregory Szorc
|
r40061 | res.status = b'500 Internal Server Error' | ||
res.setbodybytes(b'cacheobjects not supported for api server') | ||||
return | ||||
if not urlparts: | ||||
res.status = b'200 OK' | ||||
res.headers[b'Content-Type'] = b'text/plain' | ||||
res.setbodybytes(b'simple cache server') | ||||
return | ||||
key = b'/'.join(urlparts) | ||||
if key not in CACHE: | ||||
res.status = b'404 Not Found' | ||||
res.headers[b'Content-Type'] = b'text/plain' | ||||
res.setbodybytes(b'key not found in cache') | ||||
return | ||||
res.status = b'200 OK' | ||||
res.headers[b'Content-Type'] = b'application/mercurial-cbor' | ||||
res.setbodybytes(CACHE[key]) | ||||
def cachedescriptor(req, repo): | ||||
return {} | ||||
wireprotoserver.API_HANDLERS[b'simplecache'] = { | ||||
Pulkit Goyal
|
r40254 | b'config': (b'simplecache', b'cacheapi'), | ||
b'handler': handlecacherequest, | ||||
b'apidescriptor': cachedescriptor, | ||||
Gregory Szorc
|
r40061 | } | ||
Gregory Szorc
|
r40057 | @interfaceutil.implementer(repository.iwireprotocolcommandcacher) | ||
class memorycacher(object): | ||||
Gregory Szorc
|
r40061 | def __init__(self, ui, command, encodefn, redirecttargets, redirecthashes, | ||
req): | ||||
Gregory Szorc
|
r40057 | self.ui = ui | ||
self.encodefn = encodefn | ||||
Gregory Szorc
|
r40061 | self.redirecttargets = redirecttargets | ||
self.redirecthashes = redirecthashes | ||||
self.req = req | ||||
Gregory Szorc
|
r40057 | self.key = None | ||
Pulkit Goyal
|
r40254 | self.cacheobjects = ui.configbool(b'simplecache', b'cacheobjects') | ||
self.cacheapi = ui.configbool(b'simplecache', b'cacheapi') | ||||
Gregory Szorc
|
r40057 | self.buffered = [] | ||
Pulkit Goyal
|
r40254 | ui.log(b'simplecache', b'cacher constructed for %s\n', command) | ||
Gregory Szorc
|
r40057 | |||
def __enter__(self): | ||||
return self | ||||
def __exit__(self, exctype, excvalue, exctb): | ||||
if exctype: | ||||
Pulkit Goyal
|
r40254 | self.ui.log(b'simplecache', b'cacher exiting due to error\n') | ||
Gregory Szorc
|
r40057 | |||
def adjustcachekeystate(self, state): | ||||
# Needed in order to make tests deterministic. Don't copy this | ||||
# pattern for production caches! | ||||
del state[b'repo'] | ||||
def setcachekey(self, key): | ||||
self.key = key | ||||
return True | ||||
def lookup(self): | ||||
if self.key not in CACHE: | ||||
Pulkit Goyal
|
r40254 | self.ui.log(b'simplecache', b'cache miss for %s\n', self.key) | ||
Gregory Szorc
|
r40057 | return None | ||
entry = CACHE[self.key] | ||||
Pulkit Goyal
|
r40254 | self.ui.log(b'simplecache', b'cache hit for %s\n', self.key) | ||
Gregory Szorc
|
r40057 | |||
Gregory Szorc
|
r40061 | redirectable = True | ||
if not self.cacheapi: | ||||
redirectable = False | ||||
elif not self.redirecttargets: | ||||
redirectable = False | ||||
else: | ||||
clienttargets = set(self.redirecttargets) | ||||
ourtargets = set(t[b'name'] for t in loadredirecttargets(self.ui)) | ||||
# We only ever redirect to a single target (for now). So we don't | ||||
# need to store which target matched. | ||||
if not clienttargets & ourtargets: | ||||
redirectable = False | ||||
if redirectable: | ||||
paths = self.req.dispatchparts[:-3] | ||||
paths.append(b'simplecache') | ||||
paths.append(self.key) | ||||
Gregory Szorc
|
r40206 | url = b'%s/%s' % (self.req.baseurl, b'/'.join(paths)) | ||
Gregory Szorc
|
r40061 | |||
#url = b'http://example.com/%s' % self.key | ||||
Pulkit Goyal
|
r40254 | self.ui.log(b'simplecache', b'sending content redirect for %s to ' | ||
b'%s\n', self.key, url) | ||||
Gregory Szorc
|
r40061 | response = wireprototypes.alternatelocationresponse( | ||
url=url, | ||||
mediatype=b'application/mercurial-cbor') | ||||
Pulkit Goyal
|
r40254 | return {b'objs': [response]} | ||
Gregory Szorc
|
r40061 | |||
Gregory Szorc
|
r40057 | if self.cacheobjects: | ||
return { | ||||
Pulkit Goyal
|
r40254 | b'objs': entry, | ||
Gregory Szorc
|
r40057 | } | ||
else: | ||||
return { | ||||
Pulkit Goyal
|
r40254 | b'objs': [wireprototypes.encodedresponse(entry)], | ||
Gregory Szorc
|
r40057 | } | ||
def onobject(self, obj): | ||||
if self.cacheobjects: | ||||
self.buffered.append(obj) | ||||
else: | ||||
self.buffered.extend(self.encodefn(obj)) | ||||
yield obj | ||||
def onfinished(self): | ||||
Pulkit Goyal
|
r40254 | self.ui.log(b'simplecache', b'storing cache entry for %s\n', self.key) | ||
Gregory Szorc
|
r40057 | if self.cacheobjects: | ||
CACHE[self.key] = self.buffered | ||||
else: | ||||
CACHE[self.key] = b''.join(self.buffered) | ||||
return [] | ||||
Gregory Szorc
|
r40061 | def makeresponsecacher(orig, repo, proto, command, args, objencoderfn, | ||
redirecttargets, redirecthashes): | ||||
return memorycacher(repo.ui, command, objencoderfn, redirecttargets, | ||||
redirecthashes, proto._req) | ||||
Gregory Szorc
|
r40057 | |||
Gregory Szorc
|
r40059 | def loadredirecttargets(ui): | ||
Pulkit Goyal
|
r40254 | path = ui.config(b'simplecache', b'redirectsfile') | ||
Gregory Szorc
|
r40059 | if not path: | ||
return [] | ||||
with open(path, 'rb') as fh: | ||||
s = fh.read() | ||||
return stringutil.evalpythonliteral(s) | ||||
def getadvertisedredirecttargets(orig, repo, proto): | ||||
return loadredirecttargets(repo.ui) | ||||
Gregory Szorc
|
r40057 | def extsetup(ui): | ||
global CACHE | ||||
CACHE = util.lrucachedict(10000) | ||||
Pulkit Goyal
|
r40254 | extensions.wrapfunction(wireprotov2server, b'makeresponsecacher', | ||
Gregory Szorc
|
r40057 | makeresponsecacher) | ||
Pulkit Goyal
|
r40254 | extensions.wrapfunction(wireprotov2server, b'getadvertisedredirecttargets', | ||
Gregory Szorc
|
r40059 | getadvertisedredirecttargets) | ||