Mercurial > libervia-pubsub
comparison idavoll/gateway.py @ 183:c21b986cff30
Implement HTTP client to gateway and implement functional tests with it.
author | Ralph Meijer <ralphm@ik.nu> |
---|---|
date | Fri, 11 Apr 2008 14:41:16 +0000 |
parents | faf1c9bc2612 |
children | 9038908dc2f5 |
comparison
equal
deleted
inserted
replaced
182:4aa29b1a8c67 | 183:c21b986cff30 |
---|---|
1 # -*- test-case-name: idavoll.test.test_gateway -*- | |
2 # | |
1 # Copyright (c) 2003-2008 Ralph Meijer | 3 # Copyright (c) 2003-2008 Ralph Meijer |
2 # See LICENSE for details. | 4 # See LICENSE for details. |
3 | 5 |
6 """ | |
7 Web resources and client for interacting with pubsub services. | |
8 """ | |
9 | |
4 import cgi | 10 import cgi |
5 from time import gmtime, strftime | 11 from time import gmtime, strftime |
12 import urllib | |
13 import urlparse | |
6 | 14 |
7 import simplejson | 15 import simplejson |
8 | 16 |
9 from twisted.application import service | 17 from twisted.application import service |
10 from twisted.internet import defer, reactor | 18 from twisted.internet import defer, reactor |
19 from twisted.python import log | |
11 from twisted.web import client | 20 from twisted.web import client |
12 from twisted.web2 import http, http_headers, resource, responsecode | 21 from twisted.web2 import http, http_headers, resource, responsecode |
22 from twisted.web2 import channel, server | |
13 from twisted.web2.stream import readStream | 23 from twisted.web2.stream import readStream |
14 from twisted.words.protocols.jabber.jid import JID | 24 from twisted.words.protocols.jabber.jid import JID |
15 from twisted.words.protocols.jabber.error import StanzaError | 25 from twisted.words.protocols.jabber.error import StanzaError |
16 from twisted.words.xish import domish | 26 from twisted.words.xish import domish |
17 | 27 |
19 from wokkel.pubsub import PubSubClient | 29 from wokkel.pubsub import PubSubClient |
20 | 30 |
21 from idavoll import error | 31 from idavoll import error |
22 | 32 |
23 NS_ATOM = 'http://www.w3.org/2005/Atom' | 33 NS_ATOM = 'http://www.w3.org/2005/Atom' |
24 | 34 MIME_ATOM_ENTRY = 'application/atom+xml;type=entry' |
35 MIME_JSON = 'application/json' | |
25 | 36 |
26 class RemoteSubscriptionService(service.Service, PubSubClient): | 37 class RemoteSubscriptionService(service.Service, PubSubClient): |
27 | 38 |
28 def __init__(self, jid): | 39 def __init__(self, jid): |
29 self.jid = jid | 40 self.jid = jid |
140 | 151 |
141 if eventType: | 152 if eventType: |
142 headers['Event'] = eventType | 153 headers['Event'] = eventType |
143 | 154 |
144 def postNotification(callbackURI): | 155 def postNotification(callbackURI): |
145 return client.getPage(str(callbackURI), | 156 d = client.getPage(str(callbackURI), |
146 method='POST', | 157 method='POST', |
147 postdata=postdata, | 158 postdata=postdata, |
148 headers=headers) | 159 headers=headers) |
160 d.addErrback(log.err) | |
149 | 161 |
150 for callbackURI in callbacks: | 162 for callbackURI in callbacks: |
151 reactor.callLater(0, postNotification, callbackURI) | 163 reactor.callLater(0, postNotification, callbackURI) |
152 | 164 |
153 | 165 |
326 responsecode.UNSUPPORTED_MEDIA_TYPE, | 338 responsecode.UNSUPPORTED_MEDIA_TYPE, |
327 "Unsupported Media Type: %s" % | 339 "Unsupported Media Type: %s" % |
328 http_headers.generateContentType(ctype))) | 340 http_headers.generateContentType(ctype))) |
329 | 341 |
330 def parseXMLPayload(self, stream): | 342 def parseXMLPayload(self, stream): |
331 if not stream: | |
332 print "Stream is empty", repr(stream) | |
333 elif not stream.length: | |
334 print "Stream length is", repr(stream.length) | |
335 p = WebStreamParser() | 343 p = WebStreamParser() |
336 return p.parse(stream) | 344 return p.parse(stream) |
337 | 345 |
338 def http_POST(self, request): | 346 def http_POST(self, request): |
339 """ | 347 """ |
464 def __init__(self, service): | 472 def __init__(self, service): |
465 self.service = service | 473 self.service = service |
466 | 474 |
467 def render(self, request): | 475 def render(self, request): |
468 def responseFromNodes(nodeIdentifiers): | 476 def responseFromNodes(nodeIdentifiers): |
469 import pprint | 477 return http.Response(responsecode.OK, |
470 return http.Response(responsecode.OK, stream=pprint.pformat(nodeIdentifiers)) | 478 stream=simplejson.dumps(nodeIdentifiers)) |
471 | 479 |
472 d = self.service.get_nodes() | 480 d = self.service.get_nodes() |
473 d.addCallback(responseFromNodes) | 481 d.addCallback(responseFromNodes) |
474 return d | 482 return d |
483 | |
484 | |
485 def getPageWithFactory(url, contextFactory=None, *args, **kwargs): | |
486 """Download a web page. | |
487 | |
488 Download a page. Return the factory that holds a deferred, which will | |
489 callback with a page (as a string) or errback with a description of the | |
490 error. | |
491 | |
492 See HTTPClientFactory to see what extra args can be passed. | |
493 """ | |
494 | |
495 scheme, host, port, path = client._parse(url) | |
496 factory = client.HTTPClientFactory(url, *args, **kwargs) | |
497 factory.protocol.handleStatus_204 = lambda self: self.handleStatus_200() | |
498 | |
499 if scheme == 'https': | |
500 from twisted.internet import ssl | |
501 if contextFactory is None: | |
502 contextFactory = ssl.ClientContextFactory() | |
503 reactor.connectSSL(host, port, factory, contextFactory) | |
504 else: | |
505 reactor.connectTCP(host, port, factory) | |
506 return factory | |
507 | |
508 | |
509 class CallbackResource(resource.Resource): | |
510 """ | |
511 Web resource for retrieving gateway notifications. | |
512 """ | |
513 | |
514 def __init__(self, callback): | |
515 self.callback = callback | |
516 | |
517 http_GET = None | |
518 | |
519 def http_POST(self, request): | |
520 p = WebStreamParser() | |
521 d = p.parse(request.stream) | |
522 d.addCallback(self.callback, request.headers) | |
523 d.addCallback(lambda _: http.Response(responsecode.NO_CONTENT)) | |
524 return d | |
525 | |
526 class GatewayClient(service.Service): | |
527 """ | |
528 Service that provides client access to the HTTP Gateway into Idavoll. | |
529 """ | |
530 | |
531 agent = "Idavoll HTTP Gateway Client" | |
532 | |
533 def __init__(self, baseURI, callbackHost=None, callbackPort=None): | |
534 self.baseURI = baseURI | |
535 self.callbackHost = callbackHost or 'localhost' | |
536 self.callbackPort = callbackPort or 8087 | |
537 root = resource.Resource() | |
538 root.child_callback = CallbackResource(lambda *args, **kwargs: self.callback(*args, **kwargs)) | |
539 self.site = server.Site(root) | |
540 | |
541 def startService(self): | |
542 self.port = reactor.listenTCP(self.callbackPort, | |
543 channel.HTTPFactory(self.site)) | |
544 | |
545 def stopService(self): | |
546 return self.port.stopListening() | |
547 | |
548 def _makeURI(self, verb, query=None): | |
549 uriComponents = urlparse.urlparse(self.baseURI) | |
550 uri = urlparse.urlunparse((uriComponents[0], | |
551 uriComponents[1], | |
552 uriComponents[2] + verb, | |
553 '', | |
554 query and urllib.urlencode(query) or '', | |
555 '')) | |
556 return uri | |
557 | |
558 def callback(self, data, headers): | |
559 pass | |
560 | |
561 def create(self): | |
562 f = getPageWithFactory(self._makeURI('create'), | |
563 method='POST', | |
564 agent=self.agent) | |
565 return f.deferred.addCallback(simplejson.loads) | |
566 | |
567 def publish(self, entry, xmppURI=None): | |
568 query = xmppURI and {'uri': xmppURI} | |
569 | |
570 f = getPageWithFactory(self._makeURI('publish', query), | |
571 method='POST', | |
572 postdata=entry.toXml().encode('utf-8'), | |
573 headers={'Content-Type': MIME_ATOM_ENTRY}, | |
574 agent=self.agent) | |
575 return f.deferred.addCallback(simplejson.loads) | |
576 | |
577 def listNodes(self): | |
578 f = getPageWithFactory(self._makeURI('list'), | |
579 method='GET', | |
580 agent=self.agent) | |
581 return f.deferred.addCallback(simplejson.loads) | |
582 | |
583 def subscribe(self, xmppURI): | |
584 params = {'uri': xmppURI, | |
585 'callback': 'http://%s:%s/callback' % (self.callbackHost, | |
586 self.callbackPort)} | |
587 f = getPageWithFactory(self._makeURI('subscribe'), | |
588 method='POST', | |
589 postdata=simplejson.dumps(params), | |
590 headers={'Content-Type': MIME_JSON}, | |
591 agent=self.agent) | |
592 return f.deferred |