Coverage for aiocoap/numbers/optionnumbers.py: 94%

101 statements  

« prev     ^ index     » next       coverage.py v7.6.3, created at 2024-10-15 22:10 +0000

1# SPDX-FileCopyrightText: Christian Amsüss and the aiocoap contributors 

2# 

3# SPDX-License-Identifier: MIT 

4 

5"""Known values for CoAP option numbers 

6 

7The values defined in `OptionNumber` correspond to the IANA registry "CoRE 

8Parameters", subregistries "CoAP Method Codes" and "CoAP Response Codes". 

9 

10The option numbers come with methods that can be used to evaluate their 

11properties, see the `OptionNumber` class for details. 

12""" 

13 

14import warnings 

15 

16from ..util import ExtensibleIntEnum 

17from .. import optiontypes 

18 

19 

20class OptionNumber(ExtensibleIntEnum): 

21 """A CoAP option number. 

22 

23 As the option number contains information on whether the option is 

24 critical, and whether it is safe-to-forward, those properties can be 

25 queried using the `is_*` group of methods. 

26 

27 Note that whether an option may be repeated or not does not only depend on 

28 the option, but also on the context, and is thus handled in the `Options` 

29 object instead.""" 

30 

31 IF_MATCH = 1 

32 URI_HOST = 3 

33 ETAG = 4 

34 IF_NONE_MATCH = 5 

35 OBSERVE = 6 

36 URI_PORT = 7 

37 LOCATION_PATH = 8 

38 OSCORE = 9 

39 URI_PATH = 11 

40 CONTENT_FORMAT = 12 

41 MAX_AGE = 14 

42 URI_QUERY = 15 

43 HOP_LIMIT = 16 

44 ACCEPT = 17 

45 Q_BLOCK1 = 19 

46 LOCATION_QUERY = 20 

47 EDHOC = 21 

48 BLOCK2 = 23 

49 BLOCK1 = 27 

50 SIZE2 = 28 

51 Q_BLOCK2 = 31 

52 PROXY_URI = 35 

53 PROXY_SCHEME = 39 

54 SIZE1 = 60 

55 ECHO = 252 

56 NO_RESPONSE = 258 

57 REQUEST_TAG = 292 

58 

59 # experimental for draft-amsuess-core-cachable-oscore 

60 # 

61 # Using the number suggested there (rather than a high one) as this is 

62 # going to be used in overhead comparisons. 

63 REQUEST_HASH = 548 

64 

65 @property 

66 def OBJECT_SECURITY(self): 

67 warnings.warn("OBJECT_SECURITY is a deprecated alias for OSCORE") 

68 return self.OSCORE 

69 

70 def __add__(self, delta): 

71 """Addition makes sense on these due to the delta encoding in CoAP 

72 serialization""" 

73 return type(self)(int(self) + delta) 

74 

75 def is_critical(self): 

76 return self & 0x01 == 0x01 

77 

78 def is_elective(self): 

79 return not self.is_critical() 

80 

81 def is_unsafe(self): 

82 return self & 0x02 == 0x02 

83 

84 def is_safetoforward(self): 

85 return not self.is_unsafe() 

86 

87 def is_nocachekey(self): 

88 if self.is_unsafe(): 

89 raise ValueError("NoCacheKey is only meaningful for safe options") 

90 return self & 0x1E == 0x1C 

91 

92 def is_cachekey(self): 

93 return not self.is_nocachekey() 

94 

95 def _get_format(self): 

96 if hasattr(self, "_format"): 

97 return self._format 

98 else: 

99 return optiontypes.OpaqueOption 

100 

101 def set_format(self, value): 

102 """Set the serialization format. 

103 

104 This affects any use of the option throughout the program; existing 

105 options should not be altered incompatibly. Use this on custom or 

106 experimental options. 

107 

108 This is available as a setter function in addition to write access 

109 through the `format` property to satisfy requirements imposed by mypy's 

110 special handling of enums. 

111 """ 

112 if hasattr(self, "_format"): 

113 if self._format != value: 

114 warnings.warn( 

115 "Altering the serialization format of {self}. This is a severe interoperability hazard with other modules, and should only be used during experimentation. This warning may be converted into an error at any time." 

116 ) 

117 self._format = value 

118 

119 format = property( 

120 _get_format, 

121 set_format, 

122 doc="Serialization format; see :func:`~aiocoap.numbers.optionnumbers.OptionNumber.set_format`", 

123 ) 

124 

125 def create_option(self, decode=None, value=None): 

126 """Return an Option element of the appropriate class from this option 

127 number. 

128 

129 An initial value may be set using the decode or value options, and will 

130 be fed to the resulting object's decode method or value property, 

131 respectively.""" 

132 option = self.format(self) 

133 if decode is not None: 

134 option.decode(decode) 

135 if value is not None: 

136 option.value = value 

137 return option 

138 

139 def _repr_html_(self): 

140 import html 

141 

142 properties = f"{'critical' if self.is_critical() else 'elective'}, {'safe-to-forward' if self.is_safetoforward() else 'proxy unsafe'}" 

143 if self.is_safetoforward(): 

144 properties += ( 

145 ", part of the cache key" 

146 if self.is_cachekey() 

147 else ", not part of the cache key" 

148 ) 

149 if hasattr(self, "name"): 

150 return f'<abbr title="option {int(self)}: {properties}">{html.escape(self.name)}</abbr>' 

151 else: 

152 return f'<abbr title="{properties}">Option {int(self)}</abbr>' 

153 

154 

155# OpaqueOption is set on formats where it is known to be used even though it is 

156# the default. This allows developers to rely on those interfaces to be stable 

157# (or at least to be notified visibly in the release notes). 

158 

159# RFC 7252 

160 

161OptionNumber.IF_MATCH.set_format(optiontypes.OpaqueOption) 

162OptionNumber.URI_HOST.set_format(optiontypes.StringOption) 

163OptionNumber.ETAG.set_format(optiontypes.OpaqueOption) 

164OptionNumber.URI_PORT.set_format(optiontypes.UintOption) 

165OptionNumber.LOCATION_PATH.set_format(optiontypes.StringOption) 

166OptionNumber.URI_PATH.set_format(optiontypes.StringOption) 

167OptionNumber.CONTENT_FORMAT.set_format(optiontypes.ContentFormatOption) 

168OptionNumber.MAX_AGE.set_format(optiontypes.UintOption) 

169OptionNumber.URI_QUERY.set_format(optiontypes.StringOption) 

170OptionNumber.ACCEPT.set_format(optiontypes.ContentFormatOption) 

171OptionNumber.LOCATION_QUERY.set_format(optiontypes.StringOption) 

172OptionNumber.PROXY_URI.set_format(optiontypes.StringOption) 

173OptionNumber.PROXY_SCHEME.set_format(optiontypes.StringOption) 

174OptionNumber.SIZE1.set_format(optiontypes.UintOption) 

175 

176# RFC 7959 

177 

178OptionNumber.BLOCK2.set_format(optiontypes.BlockOption) 

179OptionNumber.BLOCK1.set_format(optiontypes.BlockOption) 

180OptionNumber.SIZE2.set_format(optiontypes.UintOption) 

181 

182# RFC 7641 

183 

184OptionNumber.OBSERVE.set_format(optiontypes.UintOption) 

185 

186# RFC 7967 

187 

188OptionNumber.NO_RESPONSE.set_format(optiontypes.UintOption) 

189 

190# RFC 8613 

191 

192OptionNumber.OSCORE.set_format(optiontypes.OpaqueOption) 

193 

194# RFC 9175 

195 

196OptionNumber.ECHO.set_format(optiontypes.OpaqueOption) 

197OptionNumber.REQUEST_TAG.set_format(optiontypes.OpaqueOption) 

198 

199# RFC 8768 

200 

201OptionNumber.HOP_LIMIT.set_format(optiontypes.UintOption) 

202 

203# experimental for draft-amsuess-core-cachable-oscore 

204 

205OptionNumber.REQUEST_HASH.set_format(optiontypes.OpaqueOption)