Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/sugar_network/model/routes.py
diff options
context:
space:
mode:
Diffstat (limited to 'sugar_network/model/routes.py')
-rw-r--r--sugar_network/model/routes.py154
1 files changed, 30 insertions, 124 deletions
diff --git a/sugar_network/model/routes.py b/sugar_network/model/routes.py
index c8f8da6..ff0377f 100644
--- a/sugar_network/model/routes.py
+++ b/sugar_network/model/routes.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2013 Aleksey Lim
+# Copyright (C) 2013-2014 Aleksey Lim
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -14,55 +14,21 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
-import mimetypes
-from os.path import split
-from sugar_network import static, db
-from sugar_network.toolkit.router import route, fallbackroute, Blob, ACL
+from sugar_network.db import files
+from sugar_network.toolkit.router import route
+from sugar_network.toolkit.coroutine import this
from sugar_network.toolkit import coroutine
_logger = logging.getLogger('model.routes')
-class VolumeRoutes(db.Routes):
-
- @route('GET', ['context', None], cmd='feed',
- mime_type='application/json')
- def feed(self, request, distro):
- context = self.volume['context'].get(request.guid)
- releases = self.volume['release']
- versions = []
-
- impls, __ = releases.find(context=context.guid,
- not_layer='deleted', **request)
- for impl in impls:
- version = impl.properties([
- 'guid', 'ctime', 'layer', 'author', 'tags',
- 'version', 'stability', 'license', 'notes',
- ])
- if context['dependencies']:
- requires = version.setdefault('requires', {})
- for i in context['dependencies']:
- requires.setdefault(i, {})
- version['data'] = data = impl.meta('data')
- for key in ('mtime', 'seqno', 'blob'):
- if key in data:
- del data[key]
- versions.append(version)
-
- result = {'releases': versions}
- if distro:
- aliases = context['aliases'].get(distro)
- if aliases and 'binary' in aliases:
- result['packages'] = aliases['binary']
- return result
-
-
class FrontRoutes(object):
def __init__(self):
- self._pooler = _Pooler()
+ self._spooler = coroutine.Spooler()
+ this.broadcast = self._broadcast
@route('GET', mime_type='text/html')
def hello(self):
@@ -80,34 +46,14 @@ class FrontRoutes(object):
response.content_length = 0
@route('GET', cmd='subscribe', mime_type='text/event-stream')
- def subscribe(self, request=None, response=None, ping=False, **condition):
+ def subscribe(self, request=None, response=None, **condition):
"""Subscribe to Server-Sent Events."""
if request is not None and not condition:
condition = request
if response is not None:
response.content_type = 'text/event-stream'
response['Cache-Control'] = 'no-cache'
- return self._pull_events(request, ping, condition)
-
- @route('POST', cmd='broadcast',
- mime_type='application/json', acl=ACL.LOCAL)
- def broadcast(self, event=None, request=None):
- if request is not None:
- event = request.content
- _logger.debug('Broadcast event: %r', event)
- self._pooler.notify_all(event)
-
- @fallbackroute('GET', ['static'])
- def get_static(self, request):
- path = static.path(*request.path[1:])
- if not mimetypes.inited:
- mimetypes.init()
- mime_type = mimetypes.types_map.get('.' + path.rsplit('.', 1)[-1])
- return Blob({
- 'blob': path,
- 'filename': split(path)[-1],
- 'mime_type': mime_type,
- })
+ return self._pull_events(request, condition)
@route('GET', ['robots.txt'], mime_type='text/plain')
def robots(self, request, response):
@@ -115,34 +61,29 @@ class FrontRoutes(object):
@route('GET', ['favicon.ico'])
def favicon(self, request, response):
- return Blob({
- 'blob': static.path('favicon.ico'),
- 'mime_type': 'image/x-icon',
- })
-
- def _pull_events(self, request, ping, condition):
- _logger.debug('Start subscription, total=%s', self._pooler.waiters + 1)
-
- if ping:
- # XXX The whole commands' kwargs handling should be redesigned
- if 'ping' in condition:
- condition.pop('ping')
- # If non-greenlet application needs only to initiate
- # a subscription and do not stuck in waiting for the first event,
- # it should pass `ping` argument to return fake event to unblock
- # `GET /?cmd=subscribe` call.
- yield {'event': 'pong'}
-
- rfile = None
+ return files.get('favicon.ico')
+
+ def _broadcast(self, event):
+ _logger.debug('Broadcast event: %r', event)
+ self._spooler.notify_all(event)
+
+ def _pull_events(self, request, condition):
+ _logger.debug('Start %s-nth subscription', self._spooler.waiters + 1)
+
+ # Unblock `GET /?cmd=subscribe` call to let non-greenlet application
+ # initiate a subscription and do not stuck in waiting for the 1st event
+ yield {'event': 'pong'}
+
+ subscription = None
if request is not None:
- rfile = request.content_stream
- if rfile is not None:
- coroutine.spawn(self._waiter_for_closing, rfile)
+ subscription = request.content_stream
+ if subscription is not None:
+ coroutine.spawn(self._wait_for_closing, subscription)
while True:
- event = self._pooler.wait()
+ event = self._spooler.wait()
if not isinstance(event, dict):
- if event is rfile:
+ if event is subscription:
break
else:
continue
@@ -155,48 +96,13 @@ class FrontRoutes(object):
else:
yield event
- _logger.debug('Stop subscription, total=%s', self._pooler.waiters)
+ _logger.debug('Stop %s-nth subscription', self._spooler.waiters)
- def _waiter_for_closing(self, rfile):
+ def _wait_for_closing(self, rfile):
try:
coroutine.select([rfile.fileno()], [], [])
finally:
- self._pooler.notify_all(rfile)
-
-
-class _Pooler(object):
- """One-producer-to-many-consumers events delivery."""
-
- def __init__(self):
- self._value = None
- self._waiters = 0
- self._ready = coroutine.Event()
- self._open = coroutine.Event()
- self._open.set()
-
- @property
- def waiters(self):
- return self._waiters
-
- def wait(self):
- self._open.wait()
- self._waiters += 1
- try:
- self._ready.wait()
- finally:
- self._waiters -= 1
- if self._waiters == 0:
- self._ready.clear()
- self._open.set()
- return self._value
-
- def notify_all(self, value=None):
- self._open.wait()
- if not self._waiters:
- return
- self._open.clear()
- self._value = value
- self._ready.set()
+ self._spooler.notify_all(rfile)
_HELLO_HTML = """\