-
Notifications
You must be signed in to change notification settings - Fork 83
Expand file tree
/
Copy pathCoapMessage.cs
More file actions
343 lines (306 loc) · 7.87 KB
/
CoapMessage.cs
File metadata and controls
343 lines (306 loc) · 7.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using Waher.Content;
using Waher.Networking.CoAP.Options;
using Waher.Security.DTLS.Events;
namespace Waher.Networking.CoAP
{
/// <summary>
/// Contains information about a CoAP message.
/// </summary>
public class CoapMessage : IHostReference
{
private Dictionary<string, string> uriQuery = null;
private Dictionary<string, string> locationQuery = null;
private readonly CoapOption[] options;
private readonly CoapMessageType type;
private readonly CoapCode code;
private readonly IPEndPoint from;
private readonly UdpEventArgs e;
private Uri baseUri = null;
private byte[] payload;
private readonly ushort messageId;
private readonly ulong token;
private string host = null;
private string path = null;
private string subPath = null;
private string locationPath = null;
private ushort? contentFormat = null;
private ulong? accept = null;
private ushort? port = null;
private uint maxAge = 60;
private uint? size1 = null;
private uint? size2 = null;
private CoapOptionBlock1 block1 = null;
private CoapOptionBlock2 block2 = null;
private uint? observe = null;
internal CoapMessage(CoapMessageType Type, CoapCode Code, ushort MessageId,
ulong Token, CoapOption[] Options, byte[] Payload, IPEndPoint From,
UdpEventArgs e)
{
this.type = Type;
this.code = Code;
this.messageId = MessageId;
this.token = Token;
this.options = Options;
this.payload = Payload;
this.from = From;
this.e = e;
}
/// <summary>
/// Type of message.
/// </summary>
public CoapMessageType Type => this.type;
/// <summary>
/// Message code.
/// </summary>
public CoapCode Code => this.code;
/// <summary>
/// Message ID.
/// </summary>
public ushort MessageId => this.messageId;
/// <summary>
/// Token
/// </summary>
public ulong Token => this.token;
/// <summary>
/// Available options.
/// </summary>
public CoapOption[] Options => this.options;
/// <summary>
/// Payload, if available, or null otherwise.
/// </summary>
public byte[] Payload
{
get => this.payload;
internal set => this.payload = value;
}
/// <summary>
/// Base URI, if available, or null otherwise.
/// </summary>
public Uri BaseUri
{
get => this.baseUri;
internal set => this.baseUri = value;
}
/// <summary>
/// From where the message came.
/// </summary>
public IPEndPoint From => this.from;
/// <summary>
/// UTF-8 encoded authenticated Identity, if available, null otherwise.
/// </summary>
public byte[] Identity => this.e?.Identity;
/// <summary>
/// Authenticated Identity string, if available, null otherwise.
/// </summary>
public string IdentityString => this.e?.IdentityString;
/// <summary>
/// Optional accept option.
/// </summary>
public ulong? Accept
{
get => this.accept;
internal set => this.accept = value;
}
/// <summary>
/// Optional content format option.
/// </summary>
public ushort? ContentFormat
{
get => this.contentFormat;
internal set => this.contentFormat = value;
}
/// <summary>
/// Optional URI query parameters.
/// </summary>
public Dictionary<string, string> UriQuery
{
get => this.uriQuery;
internal set => this.uriQuery = value;
}
/// <summary>
/// Tries to get a URI query parameter value.
/// </summary>
/// <param name="Name">Parameter name.</param>
/// <param name="Value">Parameter value.</param>
/// <returns>If a parameter with the gíven name was found.</returns>
public bool TryGetUriQueryParameter(string Name, out string Value)
{
if (this.uriQuery is null)
{
Value = null;
return false;
}
else
return this.uriQuery.TryGetValue(Name, out Value);
}
/// <summary>
/// Optional URI Port number option.
/// </summary>
public ushort? Port
{
get => this.port;
internal set => this.port = value;
}
/// <summary>
/// Optional URI Host option.
/// </summary>
public string Host
{
get => this.host;
internal set => this.host = value;
}
/// <summary>
/// Optional URI Path options, appended into a path string.
/// </summary>
public string Path
{
get => this.path;
set => this.path = value;
}
/// <summary>
/// Part of the <see cref="Path"/> not matched by the corresponding resource
/// processing the message, in case the resource supports subpaths.
/// </summary>
public string SubPath
{
get => this.subPath;
set => this.subPath = value;
}
/// <summary>
/// Max Age option (number of seconds).
/// </summary>
public uint MaxAge
{
get => this.maxAge;
internal set => this.maxAge = value;
}
/// <summary>
/// Optional Location Path options, appended into a path string.
/// </summary>
public string LocationPath
{
get => this.locationPath;
internal set => this.locationPath = value;
}
/// <summary>
/// Optional Location Query parameters.
/// </summary>
public Dictionary<string, string> LocationQuery
{
get => this.locationQuery;
internal set => this.locationQuery = value;
}
/// <summary>
/// Tries to get a Location query parameter value.
/// </summary>
/// <param name="Name">Parameter name.</param>
/// <param name="Value">Parameter value.</param>
/// <returns>If a location parameter was found with the given name.</returns>
public bool TryGetLocationQueryParameter(string Name, out string Value)
{
if (this.locationQuery is null)
{
Value = null;
return false;
}
else
return this.locationQuery.TryGetValue(Name, out Value);
}
/// <summary>
/// Optional Size1 option.
/// </summary>
public uint? Size1
{
get => this.size1;
internal set => this.size1 = value;
}
/// <summary>
/// Optional Size2 option.
/// </summary>
public uint? Size2
{
get => this.size2;
internal set => this.size2 = value;
}
/// <summary>
/// Optional Block1 option (request payload).
/// </summary>
public CoapOptionBlock1 Block1
{
get => this.block1;
internal set => this.block1 = value;
}
/// <summary>
/// Optional Block2 option (response payload).
/// </summary>
public CoapOptionBlock2 Block2
{
get => this.block2;
internal set => this.block2 = value;
}
/// <summary>
/// Optional Observe option.
/// </summary>
public uint? Observe
{
get => this.observe;
internal set => this.observe = value;
}
/// <summary>
/// Generates an URI for the message.
/// </summary>
/// <returns>URI string.</returns>
public string GetUri()
{
return CoapEndpoint.GetUri(this.host, this.port, this.path, this.uriQuery);
}
/// <summary>
/// Decodes the payload of the message.
/// </summary>
/// <returns>Decoded payload.</returns>
[Obsolete("Use DecodeAsync instead, for better asynchronous performance.")]
public object Decode()
{
return this.DecodeAsync().Result;
}
/// <summary>
/// Decodes the payload of the message.
/// </summary>
/// <returns>Decoded payload.</returns>
public async Task<object> DecodeAsync()
{
if (this.payload is null)
return null;
else if (!this.contentFormat.HasValue)
return this.payload;
else
{
ContentResponse Content = await CoapEndpoint.DecodeAsync((int)this.contentFormat.Value, this.payload, this.baseUri);
Content.AssertOk();
return Content.Decoded;
}
}
/// <summary>
/// Checks if a given content format is acceptable to the client.
/// </summary>
/// <param name="ContentFormat">Content Format to check.</param>
/// <returns>If the content format is acceptable or not.</returns>
public bool IsAcceptable(int ContentFormat)
{
if (this.accept is null)
return true;
if (ContentFormat < 0)
return false;
foreach (CoapOption Option in this.options)
{
if (Option is CoapOptionAccept Accept && Accept.Value == (uint)ContentFormat)
return true;
}
return false;
}
}
}