1*cfb92d14SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*cfb92d14SAndroid Build Coastguard Worker# 3*cfb92d14SAndroid Build Coastguard Worker# Copyright (c) 2016, The OpenThread Authors. 4*cfb92d14SAndroid Build Coastguard Worker# All rights reserved. 5*cfb92d14SAndroid Build Coastguard Worker# 6*cfb92d14SAndroid Build Coastguard Worker# Redistribution and use in source and binary forms, with or without 7*cfb92d14SAndroid Build Coastguard Worker# modification, are permitted provided that the following conditions are met: 8*cfb92d14SAndroid Build Coastguard Worker# 1. Redistributions of source code must retain the above copyright 9*cfb92d14SAndroid Build Coastguard Worker# notice, this list of conditions and the following disclaimer. 10*cfb92d14SAndroid Build Coastguard Worker# 2. Redistributions in binary form must reproduce the above copyright 11*cfb92d14SAndroid Build Coastguard Worker# notice, this list of conditions and the following disclaimer in the 12*cfb92d14SAndroid Build Coastguard Worker# documentation and/or other materials provided with the distribution. 13*cfb92d14SAndroid Build Coastguard Worker# 3. Neither the name of the copyright holder nor the 14*cfb92d14SAndroid Build Coastguard Worker# names of its contributors may be used to endorse or promote products 15*cfb92d14SAndroid Build Coastguard Worker# derived from this software without specific prior written permission. 16*cfb92d14SAndroid Build Coastguard Worker# 17*cfb92d14SAndroid Build Coastguard Worker# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18*cfb92d14SAndroid Build Coastguard Worker# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19*cfb92d14SAndroid Build Coastguard Worker# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20*cfb92d14SAndroid Build Coastguard Worker# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21*cfb92d14SAndroid Build Coastguard Worker# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22*cfb92d14SAndroid Build Coastguard Worker# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23*cfb92d14SAndroid Build Coastguard Worker# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24*cfb92d14SAndroid Build Coastguard Worker# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25*cfb92d14SAndroid Build Coastguard Worker# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26*cfb92d14SAndroid Build Coastguard Worker# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27*cfb92d14SAndroid Build Coastguard Worker# POSSIBILITY OF SUCH DAMAGE. 28*cfb92d14SAndroid Build Coastguard Worker# 29*cfb92d14SAndroid Build Coastguard Worker 30*cfb92d14SAndroid Build Coastguard Workerimport collections 31*cfb92d14SAndroid Build Coastguard Workerimport io 32*cfb92d14SAndroid Build Coastguard Workerimport struct 33*cfb92d14SAndroid Build Coastguard Worker 34*cfb92d14SAndroid Build Coastguard Workerfrom binascii import hexlify 35*cfb92d14SAndroid Build Coastguard Workerfrom enum import IntEnum 36*cfb92d14SAndroid Build Coastguard Worker 37*cfb92d14SAndroid Build Coastguard Worker 38*cfb92d14SAndroid Build Coastguard Workerclass CoapMessageType(IntEnum): 39*cfb92d14SAndroid Build Coastguard Worker CON = 0 # Confirmable 40*cfb92d14SAndroid Build Coastguard Worker NON = 1 # Non-confirmable 41*cfb92d14SAndroid Build Coastguard Worker ACK = 2 # Acknowledgement 42*cfb92d14SAndroid Build Coastguard Worker RST = 3 # Reset 43*cfb92d14SAndroid Build Coastguard Worker 44*cfb92d14SAndroid Build Coastguard Worker 45*cfb92d14SAndroid Build Coastguard Workerclass CoapOptionsTypes(IntEnum): 46*cfb92d14SAndroid Build Coastguard Worker IF_MATCH = 1 47*cfb92d14SAndroid Build Coastguard Worker URI_HOST = 3 48*cfb92d14SAndroid Build Coastguard Worker ETAG = 4 49*cfb92d14SAndroid Build Coastguard Worker IF_NOT_MATCH = 5 50*cfb92d14SAndroid Build Coastguard Worker URI_PORT = 7 51*cfb92d14SAndroid Build Coastguard Worker LOCATION_PATH = 8 52*cfb92d14SAndroid Build Coastguard Worker URI_PATH = 11 53*cfb92d14SAndroid Build Coastguard Worker CONTENT_FORMAT = 12 54*cfb92d14SAndroid Build Coastguard Worker MAX_AGE = 14 55*cfb92d14SAndroid Build Coastguard Worker URI_QUERY = 15 56*cfb92d14SAndroid Build Coastguard Worker ACCEPT = 17 57*cfb92d14SAndroid Build Coastguard Worker LOCATION_QUERY = 20 58*cfb92d14SAndroid Build Coastguard Worker PROXY_URI = 35 59*cfb92d14SAndroid Build Coastguard Worker PROXY_SCHEME = 39 60*cfb92d14SAndroid Build Coastguard Worker SIZE1 = 60 61*cfb92d14SAndroid Build Coastguard Worker 62*cfb92d14SAndroid Build Coastguard Worker 63*cfb92d14SAndroid Build Coastguard Workerclass CoapOptionHeader(object): 64*cfb92d14SAndroid Build Coastguard Worker """ Class representing CoAP optional header. """ 65*cfb92d14SAndroid Build Coastguard Worker 66*cfb92d14SAndroid Build Coastguard Worker def __init__(self, delta, length): 67*cfb92d14SAndroid Build Coastguard Worker self._delta = delta 68*cfb92d14SAndroid Build Coastguard Worker self._length = length 69*cfb92d14SAndroid Build Coastguard Worker 70*cfb92d14SAndroid Build Coastguard Worker @property 71*cfb92d14SAndroid Build Coastguard Worker def delta(self): 72*cfb92d14SAndroid Build Coastguard Worker return self._delta 73*cfb92d14SAndroid Build Coastguard Worker 74*cfb92d14SAndroid Build Coastguard Worker @property 75*cfb92d14SAndroid Build Coastguard Worker def length(self): 76*cfb92d14SAndroid Build Coastguard Worker return self._length 77*cfb92d14SAndroid Build Coastguard Worker 78*cfb92d14SAndroid Build Coastguard Worker @property 79*cfb92d14SAndroid Build Coastguard Worker def is_payload_marker(self): 80*cfb92d14SAndroid Build Coastguard Worker return self.delta == 0xF and self.length == 0xF 81*cfb92d14SAndroid Build Coastguard Worker 82*cfb92d14SAndroid Build Coastguard Worker @classmethod 83*cfb92d14SAndroid Build Coastguard Worker def _read_extended_value(cls, data, value): 84*cfb92d14SAndroid Build Coastguard Worker if value == 13: 85*cfb92d14SAndroid Build Coastguard Worker return ord(data.read(1)) + 13 86*cfb92d14SAndroid Build Coastguard Worker elif value == 14: 87*cfb92d14SAndroid Build Coastguard Worker data.read(1) 88*cfb92d14SAndroid Build Coastguard Worker return ord(data.read(1)) + 269 89*cfb92d14SAndroid Build Coastguard Worker else: 90*cfb92d14SAndroid Build Coastguard Worker return value 91*cfb92d14SAndroid Build Coastguard Worker 92*cfb92d14SAndroid Build Coastguard Worker @classmethod 93*cfb92d14SAndroid Build Coastguard Worker def from_bytes(cls, data): 94*cfb92d14SAndroid Build Coastguard Worker initial_byte = ord(data.read(1)) 95*cfb92d14SAndroid Build Coastguard Worker 96*cfb92d14SAndroid Build Coastguard Worker delta = (initial_byte >> 4) & 0xF 97*cfb92d14SAndroid Build Coastguard Worker length = initial_byte & 0xF 98*cfb92d14SAndroid Build Coastguard Worker 99*cfb92d14SAndroid Build Coastguard Worker delta = cls._read_extended_value(data, delta) 100*cfb92d14SAndroid Build Coastguard Worker length = cls._read_extended_value(data, length) 101*cfb92d14SAndroid Build Coastguard Worker 102*cfb92d14SAndroid Build Coastguard Worker return cls(delta, length) 103*cfb92d14SAndroid Build Coastguard Worker 104*cfb92d14SAndroid Build Coastguard Worker 105*cfb92d14SAndroid Build Coastguard Workerclass CoapOption(object): 106*cfb92d14SAndroid Build Coastguard Worker """ Class representing CoAP option. """ 107*cfb92d14SAndroid Build Coastguard Worker 108*cfb92d14SAndroid Build Coastguard Worker def __init__(self, _type, value): 109*cfb92d14SAndroid Build Coastguard Worker self._type = _type 110*cfb92d14SAndroid Build Coastguard Worker self._value = value 111*cfb92d14SAndroid Build Coastguard Worker 112*cfb92d14SAndroid Build Coastguard Worker @property 113*cfb92d14SAndroid Build Coastguard Worker def type(self): 114*cfb92d14SAndroid Build Coastguard Worker return self._type 115*cfb92d14SAndroid Build Coastguard Worker 116*cfb92d14SAndroid Build Coastguard Worker @property 117*cfb92d14SAndroid Build Coastguard Worker def value(self): 118*cfb92d14SAndroid Build Coastguard Worker return self._value 119*cfb92d14SAndroid Build Coastguard Worker 120*cfb92d14SAndroid Build Coastguard Worker def __repr__(self): 121*cfb92d14SAndroid Build Coastguard Worker return "CoapOption(type={}, value={})".format(self.type, hexlify(self.value)) 122*cfb92d14SAndroid Build Coastguard Worker 123*cfb92d14SAndroid Build Coastguard Worker 124*cfb92d14SAndroid Build Coastguard Workerclass CoapOptionsFactory(object): 125*cfb92d14SAndroid Build Coastguard Worker """ Factory that produces CoAP options. """ 126*cfb92d14SAndroid Build Coastguard Worker 127*cfb92d14SAndroid Build Coastguard Worker def parse(self, data, message_info): 128*cfb92d14SAndroid Build Coastguard Worker options = [] 129*cfb92d14SAndroid Build Coastguard Worker 130*cfb92d14SAndroid Build Coastguard Worker _type = 0 131*cfb92d14SAndroid Build Coastguard Worker while data.tell() < len(data.getvalue()): 132*cfb92d14SAndroid Build Coastguard Worker option_header = CoapOptionHeader.from_bytes(data) 133*cfb92d14SAndroid Build Coastguard Worker if option_header.is_payload_marker: 134*cfb92d14SAndroid Build Coastguard Worker break 135*cfb92d14SAndroid Build Coastguard Worker 136*cfb92d14SAndroid Build Coastguard Worker _type += option_header.delta 137*cfb92d14SAndroid Build Coastguard Worker value = data.read(option_header.length) 138*cfb92d14SAndroid Build Coastguard Worker 139*cfb92d14SAndroid Build Coastguard Worker option = CoapOption(_type, value) 140*cfb92d14SAndroid Build Coastguard Worker options.append(option) 141*cfb92d14SAndroid Build Coastguard Worker 142*cfb92d14SAndroid Build Coastguard Worker return options 143*cfb92d14SAndroid Build Coastguard Worker 144*cfb92d14SAndroid Build Coastguard Worker 145*cfb92d14SAndroid Build Coastguard Workerclass CoapCode(object): 146*cfb92d14SAndroid Build Coastguard Worker """ Class representing CoAP code. """ 147*cfb92d14SAndroid Build Coastguard Worker 148*cfb92d14SAndroid Build Coastguard Worker def __init__(self, code): 149*cfb92d14SAndroid Build Coastguard Worker self._code = code 150*cfb92d14SAndroid Build Coastguard Worker 151*cfb92d14SAndroid Build Coastguard Worker @property 152*cfb92d14SAndroid Build Coastguard Worker def code(self): 153*cfb92d14SAndroid Build Coastguard Worker return self._code 154*cfb92d14SAndroid Build Coastguard Worker 155*cfb92d14SAndroid Build Coastguard Worker @property 156*cfb92d14SAndroid Build Coastguard Worker def _class(self): 157*cfb92d14SAndroid Build Coastguard Worker return (self.code >> 5) & 0x7 158*cfb92d14SAndroid Build Coastguard Worker 159*cfb92d14SAndroid Build Coastguard Worker @property 160*cfb92d14SAndroid Build Coastguard Worker def detail(self): 161*cfb92d14SAndroid Build Coastguard Worker return self.code & 0x1F 162*cfb92d14SAndroid Build Coastguard Worker 163*cfb92d14SAndroid Build Coastguard Worker @classmethod 164*cfb92d14SAndroid Build Coastguard Worker def from_class_and_detail(cls, _class, detail): 165*cfb92d14SAndroid Build Coastguard Worker return cls(((_class & 0x7) << 5) | (detail & 0x1F)) 166*cfb92d14SAndroid Build Coastguard Worker 167*cfb92d14SAndroid Build Coastguard Worker @classmethod 168*cfb92d14SAndroid Build Coastguard Worker def from_dotted(cls, dotted_str): 169*cfb92d14SAndroid Build Coastguard Worker _class, detail = dotted_str.split(".") 170*cfb92d14SAndroid Build Coastguard Worker return cls.from_class_and_detail(int(_class), int(detail)) 171*cfb92d14SAndroid Build Coastguard Worker 172*cfb92d14SAndroid Build Coastguard Worker def is_equal_dotted(self, dotted_code): 173*cfb92d14SAndroid Build Coastguard Worker other = self.from_dotted(dotted_code) 174*cfb92d14SAndroid Build Coastguard Worker return self.code == other.code 175*cfb92d14SAndroid Build Coastguard Worker 176*cfb92d14SAndroid Build Coastguard Worker @property 177*cfb92d14SAndroid Build Coastguard Worker def dotted(self): 178*cfb92d14SAndroid Build Coastguard Worker return ".".join(["{:01d}".format(self._class), "{:02d}".format(self.detail)]) 179*cfb92d14SAndroid Build Coastguard Worker 180*cfb92d14SAndroid Build Coastguard Worker def __eq__(self, other): 181*cfb92d14SAndroid Build Coastguard Worker if isinstance(other, int): 182*cfb92d14SAndroid Build Coastguard Worker return self.code == other 183*cfb92d14SAndroid Build Coastguard Worker 184*cfb92d14SAndroid Build Coastguard Worker elif isinstance(other, str): 185*cfb92d14SAndroid Build Coastguard Worker return self.is_equal_dotted(other) 186*cfb92d14SAndroid Build Coastguard Worker 187*cfb92d14SAndroid Build Coastguard Worker elif isinstance(other, self.__class__): 188*cfb92d14SAndroid Build Coastguard Worker return self.code == other.code 189*cfb92d14SAndroid Build Coastguard Worker 190*cfb92d14SAndroid Build Coastguard Worker else: 191*cfb92d14SAndroid Build Coastguard Worker raise TypeError("Could not compare {} and {}".format(type(self), type(other))) 192*cfb92d14SAndroid Build Coastguard Worker 193*cfb92d14SAndroid Build Coastguard Worker def __repr__(self): 194*cfb92d14SAndroid Build Coastguard Worker return self.dotted 195*cfb92d14SAndroid Build Coastguard Worker 196*cfb92d14SAndroid Build Coastguard Worker 197*cfb92d14SAndroid Build Coastguard Workerclass CoapMessage(object): 198*cfb92d14SAndroid Build Coastguard Worker """ Class representing CoAP message. """ 199*cfb92d14SAndroid Build Coastguard Worker 200*cfb92d14SAndroid Build Coastguard Worker def __init__( 201*cfb92d14SAndroid Build Coastguard Worker self, 202*cfb92d14SAndroid Build Coastguard Worker version, 203*cfb92d14SAndroid Build Coastguard Worker _type, 204*cfb92d14SAndroid Build Coastguard Worker code, 205*cfb92d14SAndroid Build Coastguard Worker message_id, 206*cfb92d14SAndroid Build Coastguard Worker token, 207*cfb92d14SAndroid Build Coastguard Worker options, 208*cfb92d14SAndroid Build Coastguard Worker payload, 209*cfb92d14SAndroid Build Coastguard Worker uri_path=None, 210*cfb92d14SAndroid Build Coastguard Worker ): 211*cfb92d14SAndroid Build Coastguard Worker self._version = version 212*cfb92d14SAndroid Build Coastguard Worker self._type = _type 213*cfb92d14SAndroid Build Coastguard Worker self._code = code 214*cfb92d14SAndroid Build Coastguard Worker self._message_id = message_id 215*cfb92d14SAndroid Build Coastguard Worker self._token = token 216*cfb92d14SAndroid Build Coastguard Worker self._options = options 217*cfb92d14SAndroid Build Coastguard Worker self._payload = payload 218*cfb92d14SAndroid Build Coastguard Worker self._uri_path = uri_path 219*cfb92d14SAndroid Build Coastguard Worker 220*cfb92d14SAndroid Build Coastguard Worker @property 221*cfb92d14SAndroid Build Coastguard Worker def version(self): 222*cfb92d14SAndroid Build Coastguard Worker return self._version 223*cfb92d14SAndroid Build Coastguard Worker 224*cfb92d14SAndroid Build Coastguard Worker @property 225*cfb92d14SAndroid Build Coastguard Worker def type(self): 226*cfb92d14SAndroid Build Coastguard Worker return self._type 227*cfb92d14SAndroid Build Coastguard Worker 228*cfb92d14SAndroid Build Coastguard Worker @property 229*cfb92d14SAndroid Build Coastguard Worker def code(self): 230*cfb92d14SAndroid Build Coastguard Worker return self._code 231*cfb92d14SAndroid Build Coastguard Worker 232*cfb92d14SAndroid Build Coastguard Worker @property 233*cfb92d14SAndroid Build Coastguard Worker def message_id(self): 234*cfb92d14SAndroid Build Coastguard Worker return self._message_id 235*cfb92d14SAndroid Build Coastguard Worker 236*cfb92d14SAndroid Build Coastguard Worker @property 237*cfb92d14SAndroid Build Coastguard Worker def token(self): 238*cfb92d14SAndroid Build Coastguard Worker return self._token 239*cfb92d14SAndroid Build Coastguard Worker 240*cfb92d14SAndroid Build Coastguard Worker @property 241*cfb92d14SAndroid Build Coastguard Worker def tkl(self): 242*cfb92d14SAndroid Build Coastguard Worker return len(self._token) 243*cfb92d14SAndroid Build Coastguard Worker 244*cfb92d14SAndroid Build Coastguard Worker @property 245*cfb92d14SAndroid Build Coastguard Worker def options(self): 246*cfb92d14SAndroid Build Coastguard Worker return self._options 247*cfb92d14SAndroid Build Coastguard Worker 248*cfb92d14SAndroid Build Coastguard Worker @property 249*cfb92d14SAndroid Build Coastguard Worker def payload(self): 250*cfb92d14SAndroid Build Coastguard Worker return self._payload 251*cfb92d14SAndroid Build Coastguard Worker 252*cfb92d14SAndroid Build Coastguard Worker @property 253*cfb92d14SAndroid Build Coastguard Worker def uri_path(self): 254*cfb92d14SAndroid Build Coastguard Worker return self._uri_path 255*cfb92d14SAndroid Build Coastguard Worker 256*cfb92d14SAndroid Build Coastguard Worker def __repr__(self): 257*cfb92d14SAndroid Build Coastguard Worker options_str = ", ".join([repr(opt) for opt in self.options]) 258*cfb92d14SAndroid Build Coastguard Worker return ("CoapMessage(version={}, type={}, code={}, message_id={}, token={}, options=[{}], payload={},", 259*cfb92d14SAndroid Build Coastguard Worker "uri-path='{}')").format( 260*cfb92d14SAndroid Build Coastguard Worker self.version, 261*cfb92d14SAndroid Build Coastguard Worker CoapMessageType.name[self.type], 262*cfb92d14SAndroid Build Coastguard Worker self.code, 263*cfb92d14SAndroid Build Coastguard Worker self.message_id, 264*cfb92d14SAndroid Build Coastguard Worker hexlify(self.token), 265*cfb92d14SAndroid Build Coastguard Worker options_str, 266*cfb92d14SAndroid Build Coastguard Worker self.payload, 267*cfb92d14SAndroid Build Coastguard Worker self.uri_path, 268*cfb92d14SAndroid Build Coastguard Worker ) 269*cfb92d14SAndroid Build Coastguard Worker 270*cfb92d14SAndroid Build Coastguard Worker 271*cfb92d14SAndroid Build Coastguard Workerclass CoapMessageProxy(object): 272*cfb92d14SAndroid Build Coastguard Worker """ Proxy class of CoAP message. 273*cfb92d14SAndroid Build Coastguard Worker 274*cfb92d14SAndroid Build Coastguard Worker The main idea behind this class is to delay parsing payload. Due to architecture of the existing solution 275*cfb92d14SAndroid Build Coastguard Worker it is possible to process confirmation message before a request message. In such case it is not possible 276*cfb92d14SAndroid Build Coastguard Worker to get URI path to get proper payload parser. 277*cfb92d14SAndroid Build Coastguard Worker """ 278*cfb92d14SAndroid Build Coastguard Worker 279*cfb92d14SAndroid Build Coastguard Worker def __init__( 280*cfb92d14SAndroid Build Coastguard Worker self, 281*cfb92d14SAndroid Build Coastguard Worker coap_message, 282*cfb92d14SAndroid Build Coastguard Worker message_info, 283*cfb92d14SAndroid Build Coastguard Worker mid_to_uri_path_binder, 284*cfb92d14SAndroid Build Coastguard Worker uri_path_based_payload_factories, 285*cfb92d14SAndroid Build Coastguard Worker ): 286*cfb92d14SAndroid Build Coastguard Worker self._coap_message = coap_message 287*cfb92d14SAndroid Build Coastguard Worker self._message_info = message_info 288*cfb92d14SAndroid Build Coastguard Worker self._mid_to_uri_path_binder = mid_to_uri_path_binder 289*cfb92d14SAndroid Build Coastguard Worker self._uri_path_based_payload_factories = (uri_path_based_payload_factories) 290*cfb92d14SAndroid Build Coastguard Worker 291*cfb92d14SAndroid Build Coastguard Worker @property 292*cfb92d14SAndroid Build Coastguard Worker def version(self): 293*cfb92d14SAndroid Build Coastguard Worker return self._coap_message.version 294*cfb92d14SAndroid Build Coastguard Worker 295*cfb92d14SAndroid Build Coastguard Worker @property 296*cfb92d14SAndroid Build Coastguard Worker def type(self): 297*cfb92d14SAndroid Build Coastguard Worker return self._coap_message.type 298*cfb92d14SAndroid Build Coastguard Worker 299*cfb92d14SAndroid Build Coastguard Worker @property 300*cfb92d14SAndroid Build Coastguard Worker def code(self): 301*cfb92d14SAndroid Build Coastguard Worker return self._coap_message.code 302*cfb92d14SAndroid Build Coastguard Worker 303*cfb92d14SAndroid Build Coastguard Worker @property 304*cfb92d14SAndroid Build Coastguard Worker def message_id(self): 305*cfb92d14SAndroid Build Coastguard Worker return self._coap_message.message_id 306*cfb92d14SAndroid Build Coastguard Worker 307*cfb92d14SAndroid Build Coastguard Worker @property 308*cfb92d14SAndroid Build Coastguard Worker def token(self): 309*cfb92d14SAndroid Build Coastguard Worker return self._coap_message.token 310*cfb92d14SAndroid Build Coastguard Worker 311*cfb92d14SAndroid Build Coastguard Worker @property 312*cfb92d14SAndroid Build Coastguard Worker def tkl(self): 313*cfb92d14SAndroid Build Coastguard Worker return self._coap_message.tkl 314*cfb92d14SAndroid Build Coastguard Worker 315*cfb92d14SAndroid Build Coastguard Worker @property 316*cfb92d14SAndroid Build Coastguard Worker def options(self): 317*cfb92d14SAndroid Build Coastguard Worker return self._coap_message.options 318*cfb92d14SAndroid Build Coastguard Worker 319*cfb92d14SAndroid Build Coastguard Worker @property 320*cfb92d14SAndroid Build Coastguard Worker def payload(self): 321*cfb92d14SAndroid Build Coastguard Worker try: 322*cfb92d14SAndroid Build Coastguard Worker binded_uri_path = self._mid_to_uri_path_binder.get_uri_path_for(self.message_id, self.token) 323*cfb92d14SAndroid Build Coastguard Worker 324*cfb92d14SAndroid Build Coastguard Worker factory = self._uri_path_based_payload_factories[binded_uri_path] 325*cfb92d14SAndroid Build Coastguard Worker 326*cfb92d14SAndroid Build Coastguard Worker return factory.parse(io.BytesIO(self._coap_message.payload), self._message_info) 327*cfb92d14SAndroid Build Coastguard Worker 328*cfb92d14SAndroid Build Coastguard Worker except RuntimeError: 329*cfb92d14SAndroid Build Coastguard Worker return self._coap_message.payload 330*cfb92d14SAndroid Build Coastguard Worker 331*cfb92d14SAndroid Build Coastguard Worker @property 332*cfb92d14SAndroid Build Coastguard Worker def uri_path(self): 333*cfb92d14SAndroid Build Coastguard Worker return self._coap_message.uri_path 334*cfb92d14SAndroid Build Coastguard Worker 335*cfb92d14SAndroid Build Coastguard Worker def __repr__(self): 336*cfb92d14SAndroid Build Coastguard Worker options_str = ", ".join([repr(opt) for opt in self.options]) 337*cfb92d14SAndroid Build Coastguard Worker return ("CoapMessageProxy(version={}, type={}, code={}, message_id={}, token={}, options=[{}], payload={},", 338*cfb92d14SAndroid Build Coastguard Worker "uri-path='{}')").format( 339*cfb92d14SAndroid Build Coastguard Worker self.version, 340*cfb92d14SAndroid Build Coastguard Worker self.type, 341*cfb92d14SAndroid Build Coastguard Worker self.code, 342*cfb92d14SAndroid Build Coastguard Worker self.message_id, 343*cfb92d14SAndroid Build Coastguard Worker hexlify(self.token), 344*cfb92d14SAndroid Build Coastguard Worker options_str, 345*cfb92d14SAndroid Build Coastguard Worker self.payload, 346*cfb92d14SAndroid Build Coastguard Worker self.uri_path, 347*cfb92d14SAndroid Build Coastguard Worker ) 348*cfb92d14SAndroid Build Coastguard Worker 349*cfb92d14SAndroid Build Coastguard Worker 350*cfb92d14SAndroid Build Coastguard Workerclass CoapMessageIdToUriPathBinder: 351*cfb92d14SAndroid Build Coastguard Worker """ Class binds message id and token with URI path. """ 352*cfb92d14SAndroid Build Coastguard Worker 353*cfb92d14SAndroid Build Coastguard Worker def __init__(self): 354*cfb92d14SAndroid Build Coastguard Worker self._uri_path_binds = collections.defaultdict(collections.defaultdict) 355*cfb92d14SAndroid Build Coastguard Worker 356*cfb92d14SAndroid Build Coastguard Worker def add_uri_path_for(self, message_id, token, uri_path): 357*cfb92d14SAndroid Build Coastguard Worker self._uri_path_binds[message_id][hexlify(token)] = uri_path 358*cfb92d14SAndroid Build Coastguard Worker 359*cfb92d14SAndroid Build Coastguard Worker def get_uri_path_for(self, message_id, token): 360*cfb92d14SAndroid Build Coastguard Worker try: 361*cfb92d14SAndroid Build Coastguard Worker return self._uri_path_binds[message_id][hexlify(token)] 362*cfb92d14SAndroid Build Coastguard Worker except KeyError: 363*cfb92d14SAndroid Build Coastguard Worker raise RuntimeError("Could not find URI PATH for message_id: {} and token: {}".format( 364*cfb92d14SAndroid Build Coastguard Worker message_id, hexlify(token))) 365*cfb92d14SAndroid Build Coastguard Worker 366*cfb92d14SAndroid Build Coastguard Worker 367*cfb92d14SAndroid Build Coastguard Workerclass CoapMessageFactory(object): 368*cfb92d14SAndroid Build Coastguard Worker """ Factory that produces CoAP messages. """ 369*cfb92d14SAndroid Build Coastguard Worker 370*cfb92d14SAndroid Build Coastguard Worker def __init__( 371*cfb92d14SAndroid Build Coastguard Worker self, 372*cfb92d14SAndroid Build Coastguard Worker options_factory, 373*cfb92d14SAndroid Build Coastguard Worker uri_path_based_payload_factories, 374*cfb92d14SAndroid Build Coastguard Worker message_id_to_uri_path_binder, 375*cfb92d14SAndroid Build Coastguard Worker ): 376*cfb92d14SAndroid Build Coastguard Worker self._options_factory = options_factory 377*cfb92d14SAndroid Build Coastguard Worker self._uri_path_based_payload_factories = (uri_path_based_payload_factories) 378*cfb92d14SAndroid Build Coastguard Worker self._mid_to_uri_path_binder = message_id_to_uri_path_binder 379*cfb92d14SAndroid Build Coastguard Worker 380*cfb92d14SAndroid Build Coastguard Worker def _uri_path_from(self, options): 381*cfb92d14SAndroid Build Coastguard Worker uri_path_options = [] 382*cfb92d14SAndroid Build Coastguard Worker 383*cfb92d14SAndroid Build Coastguard Worker for option in options: 384*cfb92d14SAndroid Build Coastguard Worker if option.type == CoapOptionsTypes.URI_PATH: 385*cfb92d14SAndroid Build Coastguard Worker uri_path_options.append(option.value.decode("utf-8")) 386*cfb92d14SAndroid Build Coastguard Worker 387*cfb92d14SAndroid Build Coastguard Worker if not uri_path_options: 388*cfb92d14SAndroid Build Coastguard Worker return None 389*cfb92d14SAndroid Build Coastguard Worker 390*cfb92d14SAndroid Build Coastguard Worker return "/" + "/".join(uri_path_options) 391*cfb92d14SAndroid Build Coastguard Worker 392*cfb92d14SAndroid Build Coastguard Worker def _parse_initial_byte(self, data, message_info): 393*cfb92d14SAndroid Build Coastguard Worker initial_byte = ord(data.read(1)) 394*cfb92d14SAndroid Build Coastguard Worker 395*cfb92d14SAndroid Build Coastguard Worker version = (initial_byte >> 6) & 0x3 396*cfb92d14SAndroid Build Coastguard Worker _type = CoapMessageType((initial_byte >> 4) & 0x3) 397*cfb92d14SAndroid Build Coastguard Worker token_length = initial_byte & 0xF 398*cfb92d14SAndroid Build Coastguard Worker 399*cfb92d14SAndroid Build Coastguard Worker return version, _type, token_length 400*cfb92d14SAndroid Build Coastguard Worker 401*cfb92d14SAndroid Build Coastguard Worker def parse(self, data, message_info): 402*cfb92d14SAndroid Build Coastguard Worker version, _type, token_length = self._parse_initial_byte(data, message_info) 403*cfb92d14SAndroid Build Coastguard Worker 404*cfb92d14SAndroid Build Coastguard Worker code = CoapCode(ord(data.read(1))) 405*cfb92d14SAndroid Build Coastguard Worker message_id = struct.unpack(">H", data.read(2))[0] 406*cfb92d14SAndroid Build Coastguard Worker token = data.read(token_length) 407*cfb92d14SAndroid Build Coastguard Worker 408*cfb92d14SAndroid Build Coastguard Worker options = self._options_factory.parse(data, message_info) 409*cfb92d14SAndroid Build Coastguard Worker 410*cfb92d14SAndroid Build Coastguard Worker uri_path = self._uri_path_from(options) 411*cfb92d14SAndroid Build Coastguard Worker if uri_path is not None: 412*cfb92d14SAndroid Build Coastguard Worker self._mid_to_uri_path_binder.add_uri_path_for(message_id, token, uri_path) 413*cfb92d14SAndroid Build Coastguard Worker 414*cfb92d14SAndroid Build Coastguard Worker coap_message = CoapMessage( 415*cfb92d14SAndroid Build Coastguard Worker version, 416*cfb92d14SAndroid Build Coastguard Worker _type, 417*cfb92d14SAndroid Build Coastguard Worker code, 418*cfb92d14SAndroid Build Coastguard Worker message_id, 419*cfb92d14SAndroid Build Coastguard Worker token, 420*cfb92d14SAndroid Build Coastguard Worker options, 421*cfb92d14SAndroid Build Coastguard Worker data.read(), 422*cfb92d14SAndroid Build Coastguard Worker uri_path, 423*cfb92d14SAndroid Build Coastguard Worker ) 424*cfb92d14SAndroid Build Coastguard Worker 425*cfb92d14SAndroid Build Coastguard Worker return CoapMessageProxy( 426*cfb92d14SAndroid Build Coastguard Worker coap_message, 427*cfb92d14SAndroid Build Coastguard Worker message_info, 428*cfb92d14SAndroid Build Coastguard Worker self._mid_to_uri_path_binder, 429*cfb92d14SAndroid Build Coastguard Worker self._uri_path_based_payload_factories, 430*cfb92d14SAndroid Build Coastguard Worker ) 431