source: ralphm-patches/c2s_server_factory.patch @ 54:03ec57713c90

Last change on this file since 54:03ec57713c90 was 54:03ec57713c90, checked in by Ralph Meijer <ralphm@…>, 9 years ago

Upstreamed Request patches, split out c2s patches in managable chunks, prepare for release of Wokkel 0.7.0.

File size: 4.1 KB
  • wokkel/client.py

    # HG changeset patch
    # Parent 52b9931fa5f5c6aeee617a4dad3e09d041ae4361
    Add factory for accepting client connections.
    
    The new XMPPC2SServerFactory is a server factory for accepting client
    connections. It uses `XMPPClientListenAuthenticator` to perform the
    steps for authentication and binding of a resource, and keeps a list
    of all streams.
    
    Upon loss of the connection, the service is called with `unbindResource`.
    Received stanzas cause the service's `onElement` to be called.
    
    The factory has a `deliverStanza` method to deliver stanzas to a particular
    recipient. This is used for stanzas that have different recipient addressing
    than the actual recipient (for presence and messages from a different (or no)
    resource).
    
    TODO:
    
     * Add docstrings.
     * Add tests.
    
    diff -r 52b9931fa5f5 wokkel/client.py
    a b  
    1616from twisted.internet import reactor
    1717from twisted.names.srvconnect import SRVConnector
    1818from twisted.words.protocols.jabber import client, error, sasl, xmlstream
     19from twisted.words.xish import domish, utility
    1920
    2021from wokkel import generic
    2122from wokkel.subprotocols import StreamManager
     23from wokkel.compat import XmlStreamServerFactory
    2224
    2325NS_CLIENT = 'jabber:client'
    2426
     
    345347
    346348
    347349
     350class RecipientUnavailable(Exception):
     351    """
     352    The addressed entity is not, or no longer, available.
     353    """
     354
     355
     356
     357class XMPPC2SServerFactory(XmlStreamServerFactory):
     358
     359    def __init__(self, service):
     360        self.service = service
     361
     362        def authenticatorFactory():
     363            return XMPPClientListenAuthenticator(service)
     364
     365        XmlStreamServerFactory.__init__(self, authenticatorFactory)
     366        self.addBootstrap(xmlstream.STREAM_CONNECTED_EVENT,
     367                          self.onConnectionMade)
     368        self.addBootstrap(xmlstream.STREAM_AUTHD_EVENT,
     369                          self.onAuthenticated)
     370
     371        self.serial = 0
     372        self.streams = {}
     373
     374
     375    def onConnectionMade(self, xs):
     376        """
     377        Called when a client-to-server connection was made.
     378
     379        This enables traffic debugging on incoming streams.
     380        """
     381        xs.serial = self.serial
     382        self.serial += 1
     383
     384        log.msg("Client connection %d made" % xs.serial)
     385
     386        def logDataIn(buf):
     387            log.msg("RECV (%d): %r" % (xs.serial, buf))
     388
     389        def logDataOut(buf):
     390            log.msg("SEND (%d): %r" % (xs.serial, buf))
     391
     392        if self.logTraffic:
     393            xs.rawDataInFn = logDataIn
     394            xs.rawDataOutFn = logDataOut
     395
     396        xs.addObserver(xmlstream.STREAM_ERROR_EVENT, self.onError)
     397
     398
     399    def onAuthenticated(self, xs):
     400        log.msg("Client connection %d authenticated" % xs.serial)
     401
     402        xs.addObserver(xmlstream.STREAM_END_EVENT, self.onConnectionLost,
     403                                                   0, xs)
     404        xs.addObserver('/*', self.onElement, 0, xs)
     405
     406        # Record this stream as bound to the authenticated JID
     407        self.streams[xs.otherEntity] = xs
     408
     409
     410    def onConnectionLost(self, xs, reason):
     411        log.msg("Client connection %d disconnected" % xs.serial)
     412
     413        entity = xs.otherEntity
     414        self.service.unbindResource(entity.user,
     415                                    entity.host,
     416                                    entity.resource,
     417                                    reason)
     418
     419        # If the lost connections had been bound, remove the reference
     420        if xs.otherEntity in self.streams:
     421            del self.streams[xs.otherEntity]
     422
     423
     424    def onError(self, reason):
     425        log.err(reason, "Stream Error")
     426
     427
     428    def onElement(self, xs, stanza):
     429        """
     430        Called when an element was received from one of the connected streams.
     431
     432        """
     433        if stanza.handled:
     434            return
     435        else:
     436            self.service.onElement(stanza, xs.otherEntity)
     437
     438
     439    def deliverStanza(self, element, recipient):
     440        if recipient in self.streams:
     441            self.streams[recipient].send(element)
     442        else:
     443            raise RecipientUnavailable(u"There is no connection for %s" %
     444                                       recipient.full())
Note: See TracBrowser for help on using the repository browser.