Alien-SVN
view release on metacpan or search on metacpan
src/subversion/tools/server-side/svnpubsub/svnpubsub/server.py view on Meta::CPAN
try:
import simplejson as json
except ImportError:
import json
import sys
import twisted
from twisted.internet import reactor
from twisted.internet import defer
from twisted.web import server
from twisted.web import resource
from twisted.python import log
import time
class Commit:
def __init__(self, r):
self.__dict__.update(r)
if not self.check_value('repository'):
raise ValueError('Invalid Repository Value')
if not self.check_value('type'):
raise ValueError('Invalid Type Value')
if not self.check_value('format'):
raise ValueError('Invalid Format Value')
if not self.check_value('id'):
raise ValueError('Invalid ID Value')
def check_value(self, k):
return hasattr(self, k) and self.__dict__[k]
def render_commit(self):
obj = {'commit': {}}
obj['commit'].update(self.__dict__)
return json.dumps(obj)
def render_log(self):
try:
paths_changed = " %d paths changed" % len(self.changed)
except:
paths_changed = ""
return "%s:%s repo '%s' id '%s'%s" % (self.type,
self.format,
self.repository,
self.id,
paths_changed)
HEARTBEAT_TIME = 15
class Client(object):
def __init__(self, pubsub, r, type, repository):
self.pubsub = pubsub
r.notifyFinish().addErrback(self.finished)
self.r = r
self.type = type
self.repository = repository
self.alive = True
log.msg("OPEN: %s:%d (%d clients online)"% (r.getClientIP(), r.client.port, pubsub.cc()+1))
def finished(self, reason):
self.alive = False
log.msg("CLOSE: %s:%d (%d clients online)"% (self.r.getClientIP(), self.r.client.port, self.pubsub.cc()))
try:
self.pubsub.remove(self)
except ValueError:
pass
def interested_in(self, commit):
if self.type and self.type != commit.type:
return False
if self.repository and self.repository != commit.repository:
return False
return True
def notify(self, data):
self.write(data)
def start(self):
self.write_start()
reactor.callLater(HEARTBEAT_TIME, self.heartbeat, None)
def heartbeat(self, args):
if self.alive:
self.write_heartbeat()
reactor.callLater(HEARTBEAT_TIME, self.heartbeat, None)
def write_data(self, data):
self.write(data + "\n\0")
""" "Data must not be unicode" is what the interfaces.ITransport says... grr. """
def write(self, input):
self.r.write(str(input))
def write_start(self):
self.r.setHeader('X-SVNPubSub-Version', '1')
self.r.setHeader('content-type', 'application/vnd.apache.vc-notify+json')
self.write('{"svnpubsub": {"version": 1}}\n\0')
def write_heartbeat(self):
self.write(json.dumps({"stillalive": time.time()}) + "\n\0")
class SvnPubSub(resource.Resource):
isLeaf = True
clients = []
def cc(self):
return len(self.clients)
def remove(self, c):
self.clients.remove(c)
def render_GET(self, request):
log.msg("REQUEST: %s" % (request.uri))
request.setHeader('content-type', 'text/plain')
repository = None
type = None
uri = request.uri.split('/')
uri_len = len(uri)
if uri_len < 2 or uri_len > 4:
request.setResponseCode(400)
return "Invalid path\n"
if uri_len >= 3:
type = uri[2]
if uri_len == 4:
repository = uri[3]
# Convert wild card to None.
if type == '*':
type = None
if repository == '*':
repository = None
c = Client(self, request, type, repository)
self.clients.append(c)
c.start()
return twisted.web.server.NOT_DONE_YET
def notifyAll(self, commit):
data = commit.render_commit()
log.msg("COMMIT: %s (%d clients)" % (commit.render_log(), self.cc()))
for client in self.clients:
if client.interested_in(commit):
client.write_data(data)
def render_PUT(self, request):
request.setHeader('content-type', 'text/plain')
ip = request.getClientIP()
if ip != "127.0.0.1":
request.setResponseCode(401)
return "Access Denied"
input = request.content.read()
#import pdb;pdb.set_trace()
#print "input: %s" % (input)
try:
( run in 1.400 second using v1.01-cache-2.11-cpan-df04353d9ac )