Example #1
0
 def __init__(self, msgSocket=None, port=soscoap.COAP_PORT):
     '''Pass in msgSocket only for unit testing.
     Pass in port for non-standard CoAP port.
     '''
     self._msgSocket = msgSocket if msgSocket else MessageSocket(port)
     self._msgSocket.registerForReceive(self._handleMessage)
     
     self._resourceGetHook  = EventHook()
     self._resourcePutHook  = EventHook()
     self._resourcePostHook = EventHook()
     
     # A random start is recommended in Sec. 4.4.
     self._nextMessageId = random.randint(0, 0xFFFF)
Example #2
0
    def __init__(self, msgSocket=None, sourcePort=soscoap.COAP_PORT, dest=None):
        '''Client initialization, espeically for networking.
        
        :param msgSocket: MessageSocket Pass in only for unit testing
        :param sourcePort: int Port for source socket
        :param dest: tuple 2-tuple (string,int) for destination host address
                     and port
        '''
        destTuple = None
        if dest:
            info = socket.getaddrinfo(dest[0], dest[1], socket.AF_INET6,
                                                        socket.SOCK_DGRAM)
            log.debug('getaddrinfo: {0}'.format(info))
            # Assume we want the first 5-tuple entry returned
            destTuple = info[0][4]

        if msgSocket:
            self._msgSocket = msgSocket
        else:
            self._msgSocket = MessageSocket(localPort=sourcePort, remote=destTuple)

        self._msgSocket.registerForReceive(self._handleMessage)

        self._responseHook = EventHook()
        
        # A random start is recommended in Sec. 4.4.
        self._nextMessageId = random.randint(0, 0xFFFF)
Example #3
0
class CoapServer(object):
    '''Server for CoAP requests. Requires another entity to define its use by
    registering event handlers.
    
    Events:
        Register a handler for an event via the 'registerFor<Event>' method. A
        handler may raise an IgnoreRequestException to silently ignore a request.
        
        :ResourceGet:  Server requests the value for the provided resource, to 
                       service a client GET request.
        :ResourcePut:  Server forwards the value for the provided resource, to 
                       service a client PUT request.
        :ResourcePost: Server forwards the value for the provided resource, to 
                       service a client POST request.
        
    Usage:
        #. cs = CoapServer() -- Create instance
        #. Register event handlers as needed; for example, cs.registerForResourceGet(). 
        #. cs.start() -- Start to listen for requests

     Attributes:
        :_msgSocket: MessageSocket to send/receive messages
        :_resourceGetHook:  EventHook triggered when GET resource requested
        :_resourcePutHook:  EventHook triggered when PUT resource requested
        :_resourcePostHook: EventHook triggered when POST resource requested
        :_nextMessageId:    Next sequential value for a new Message ID

    .. automethod:: soscoap.server.CoapServer.__init__
   '''
    def __init__(self, msgSocket=None, port=soscoap.COAP_PORT):
        '''Pass in msgSocket only for unit testing.
        Pass in port for non-standard CoAP port.
        '''
        self._msgSocket = msgSocket if msgSocket else MessageSocket(port)
        self._msgSocket.registerForReceive(self._handleMessage)
        
        self._resourceGetHook  = EventHook()
        self._resourcePutHook  = EventHook()
        self._resourcePostHook = EventHook()
        
        # A random start is recommended in Sec. 4.4.
        self._nextMessageId = random.randint(0, 0xFFFF)
                
    def registerForResourceGet(self, handler):
        self._resourceGetHook.register(handler)
        
    def registerForResourcePost(self, handler):
        self._resourcePostHook.register(handler)
        
    def registerForResourcePut(self, handler):
        self._resourcePutHook.register(handler)
        
    def _handleMessage(self, message):
        resource = None
        try:
            resource = SosResourceTransfer(message.absolutePath(), 
                                           sourceAddress=message.address)

            # only reads the first query segment
            queryList  = message.findOption(OptionType.UriQuery)
            resource.pathQuery = queryList[0].value if len(queryList) else None


            if message.codeDetail == RequestCode.GET:
                log.debug('Handling resource GET request...')
                # Retrieve requested resource via event, and send reply
                self._resourceGetHook.trigger(resource)
                self._sendGetReply(message, resource)

            elif message.codeDetail == RequestCode.PUT:
                log.debug('Handling resource PUT request...')
                resource.value = message.typedPayload()
                self._resourcePutHook.trigger(resource)
                self._sendPutReply(message, resource)

            elif message.codeDetail == RequestCode.POST:
                log.debug('Handling resource POST request...')
                resource.value = message.typedPayload()
                self._resourcePostHook.trigger(resource)
                self._sendPostReply(message, resource)

        except IgnoreRequestException:
            log.info('Ignoring request')
        except:
            log.exception('Error handling message; will send error reply')
            self._sendErrorReply(message, resource)
            
    def _createReplyTemplate(self, request, resource):
        '''Creates a reply message with common code for any reply
        
        :returns: CoapMessage Created reply
        '''
        msg             = CoapMessage()
        msg.address     = request.address
        msg.tokenLength = request.tokenLength
        msg.token       = request.token
        msg.codeClass   = resource.resultClass
        msg.codeDetail  = resource.resultCode
                                    
        if request.messageType == MessageType.CON:
            msg.messageType = MessageType.ACK
            msg.messageId   = request.messageId
        elif request.messageType == MessageType.NON:
            msg.messageType = MessageType.NON
            msg.messageId   = self._popMessageId()
        else:
            log.error('Sending Reset due to unexpected messageType: {0}', msg.messageType)
            msg.messageType = MessageType.RST
            msg.messageId   = request.messageId
            
        return msg
    
    def _sendGetReply(self, request, resource):
        '''Sends a reply to a GET request with the content for the provided resource.
        
        :param request: CoapMessage
        '''
        if not resource.resultClass:
            resource.resultClass = CodeClass.Success
            resource.resultCode  = SuccessResponseCode.Content

        msg         = self._createReplyTemplate(request, resource)
        msg.payload = bytearray(resource.value, soscoap.BYTESTR_ENCODING) \
                                    if resource.type == 'string' \
                                    else resource.value
                                    
        # Only add option for a string, and assume text-plain format.
        if resource.type == 'string':
            msg.addOption( CoapOption(OptionType.ContentFormat, MediaType.TextPlain) )
            
        self._msgSocket.send(msg)
    
    def _sendPostReply(self, request, resource):
        '''Sends a reply to a POST request confirming the changes.
        
        :param request: CoapMessage
        '''
        if not resource.resultClass:
            resource.resultClass = CodeClass.Success
            resource.resultCode  = SuccessResponseCode.Changed

        msg = self._createReplyTemplate(request, resource)
        
        self._msgSocket.send(msg)
    
    def _sendPutReply(self, request, resource):
        '''Sends a reply to a PUT request confirming the changes.
        
        :param request: CoapMessage
        '''
        if not resource.resultClass:
            resource.resultClass = CodeClass.Success
            resource.resultCode  = SuccessResponseCode.Changed

        msg = self._createReplyTemplate(request, resource)
        
        self._msgSocket.send(msg)
    
    def _sendErrorReply(self, request, resource):
        '''Sends a reply when an error has occurred in processing.
        
        :param request: CoapMessage
        :param resource: SosResourceTransfer
        '''
        msg             = self._createReplyTemplate(request, resource)
        msg.codeClass   = CodeClass.ServerError
        msg.codeDetail  = ServerResponseCode.InternalServerError
        
        self._msgSocket.send(msg)
        
    def _popMessageId(self):
        '''Returns the next sequential message ID, and increments'''
        nextid = self._nextMessageId
        self._nextMessageId = (self._nextMessageId+1 if self._nextMessageId < 0xFFFF 
                                                     else 1)
        return nextid
        
    def start(self):
        log.info('Starting asyncore loop')
        asyncore.loop()
Example #4
0
class CoapClient(object):
    '''Client for CoAP requests. Like a CoAP server, binds to a socket, usually
    on the standard CoAP port. However, only accepts incoming responses when
    there is an outstanding request.
    
    Events:
        Register a handler for an event via the 'registerFor<Event>' method.
        
        :ResourceGet:  Client has received a response for a resource GET request.

    Usage:
        #. cc = CoapClient() -- Create instance, using the standard CoAP port.
        #. cc.start() -- Start networking.
        *. ... -- Send messages (TBD)
        *. cc.close() -- Cleanup

     Attributes:
        :_msgSocket: MessageSocket to send/receive messages
        :_responseHook:  EventHook triggered when resource response received
        :_nextMessageId: Next sequential value for a new Message ID

    .. automethod:: soscoap.server.CoapClient.__init__
   '''
    def __init__(self, msgSocket=None, sourcePort=soscoap.COAP_PORT, dest=None):
        '''Client initialization, espeically for networking.
        
        :param msgSocket: MessageSocket Pass in only for unit testing
        :param sourcePort: int Port for source socket
        :param dest: tuple 2-tuple (string,int) for destination host address
                     and port
        '''
        destTuple = None
        if dest:
            info = socket.getaddrinfo(dest[0], dest[1], socket.AF_INET6,
                                                        socket.SOCK_DGRAM)
            log.debug('getaddrinfo: {0}'.format(info))
            # Assume we want the first 5-tuple entry returned
            destTuple = info[0][4]

        if msgSocket:
            self._msgSocket = msgSocket
        else:
            self._msgSocket = MessageSocket(localPort=sourcePort, remote=destTuple)

        self._msgSocket.registerForReceive(self._handleMessage)

        self._responseHook = EventHook()
        
        # A random start is recommended in Sec. 4.4.
        self._nextMessageId = random.randint(0, 0xFFFF)

    def close(self):
        '''Releases system resources'''
        self._msgSocket.close()
                
    def registerForResponse(self, handler):
        self._responseHook.register(handler)

    def send(self, message):
        '''Send a message'''
        self._msgSocket.send(message)
        
    def _handleMessage(self, message):
        try:
            log.debug('Handling resource response...')
            self._responseHook.trigger(message)

        except:
            log.exception('Error handling response')

    def _popMessageId(self):
        '''Returns the next sequential message ID, and increments'''
        nextid = self._nextMessageId
        self._nextMessageId = (self._nextMessageId+1 if self._nextMessageId < 0xFFFF 
                                                     else 1)
        return nextid
        
    def start(self):
        '''Start networking, with a one second timeout so client doesn't wait
        to send.'''
        log.info('Starting asyncore loop')
        asyncore.loop(1)