/// <summary> /// Create a new CoAP messages with <see cref="CoapMessageCode"/> set based on the type of <see cref="Exception"/> provided. /// </summary> /// <remarks> /// <see cref="CoapMessage"/>.<see cref="Code"/> will be set to <see cref="CoapMessageCode.InternalServerError"/> by default unless: /// <list type="bullet"> /// <item> /// <description>When <paramref name="exception"/> is of type <see cref="CoapException"/>. Then <see cref="CoapMessage"/>.<see cref="Code"/> will be set to <see cref="CoapException"/>.<see cref="CoapException.ResponseCode"/></description> /// </item> /// <item> /// <description>When <paramref name="exception"/> is of type <see cref="NotImplementedException"/>. Then <see cref="CoapMessage"/>.<see cref="Code"/> will be set to <see cref="CoapMessageCode.NotImplemented"/></description> /// </item> /// </list> /// </remarks> /// <param name="exception"></param> /// <returns></returns> public static CoapMessage FromException(Exception exception) { var result = new CoapMessage { Type = CoapMessageType.Reset, Code = CoapMessageCode.InternalServerError, Options = { new ContentFormat(ContentFormatType.TextPlain) }, Payload = Encoding.UTF8.GetBytes(exception.Message) }; switch (exception) { case CoapException coapEx: result.Code = coapEx.ResponseCode; break; case NotImplementedException _: result.Code = CoapMessageCode.NotImplemented; break; } return(result); }
/// <summary> /// Attempts to read the entire body of the block-wise message. Using the <paramref name="originalRequest"/> to request blocks. /// </summary> /// <param name="message">A message containing a <see cref="Block2"/> option.</param> /// <param name="client"></param> /// <param name="originalRequest">The orignal request which the block-wise response was for.</param> /// <returns>The completed body for the block-wise messages.</returns> public static byte[] GetCompletedBlockWisePayload(this CoapMessage message, CoapClient client, CoapMessage originalRequest) { var block2 = message.Options.Get <Options.Block2>() ?? throw new ArgumentException($"{nameof(CoapMessage)} does not contain a {nameof(Options.Block2)} option", nameof(message)); if (originalRequest == null) { throw new ArgumentNullException("Please provide original requesting message", nameof(originalRequest)); } if (block2.BlockNumber != 0) { throw new CoapBlockException($"Can not get completed payload starting with block {block2.BlockNumber}. Please start from 0"); } var memoryStream = new MemoryStream(); using (var reader = new CoapBlockStreamReader(client, message, originalRequest)) reader.CopyTo(memoryStream); return(memoryStream.ToArray()); }
/// <summary> /// Creates an <see cref="CoapException"/> with details populated from <paramref name="message"/> with an optional inner-exception. /// </summary> /// <param name="message"></param> /// <param name="innerExcpetion"></param> /// <returns></returns> public static CoapException FromCoapMessage(CoapMessage message, Exception innerExcpetion = null) { if (message == null) { throw new ArgumentNullException(nameof(message)); } var errorMessage = $"({message.Code.Class}.{message.Code.Detail:D2})"; var contentFormat = message.Options.Get <Options.ContentFormat>(); if (contentFormat != null && message.Payload != null) { if (contentFormat.MediaType == Options.ContentFormatType.TextPlain) { errorMessage += System.Text.Encoding.UTF8.GetString(message.Payload); } else { errorMessage += string.Join(", ", message.Payload.Select(b => $"0x{b:X2}")); } } return(new CoapException(errorMessage, innerExcpetion, message.Code)); }
/// <summary> /// <see cref="SendAsync(CoapMessage, ICoapEndpoint, CancellationToken)"/> /// </summary> /// <param name="message"></param> /// <param name="endpoint"></param> /// <returns></returns> public virtual async Task <int> SendAsync(CoapMessage message, ICoapEndpoint endpoint) => await SendAsync(message, endpoint, CancellationToken.None);
/// <summary> /// <see cref="SendAsync(CoapMessage, ICoapEndpoint, CancellationToken)"/> /// </summary> /// <param name="message"></param> /// <param name="token"></param> /// <returns></returns> public virtual async Task <int> SendAsync(CoapMessage message, CancellationToken token) => await SendAsync(message, null, token);
private async Task ReceiveAsyncInternal() { try { while (true) { if (Endpoint == null) { return; } var payload = await Endpoint.ReceiveAsync(); var message = new CoapMessage(Endpoint.IsMulticast); try { message.Deserialise(payload.Payload); } catch (CoapMessageFormatException) { if (message.Type == CoapMessageType.Confirmable && !Endpoint.IsMulticast) { await SendAsync(new CoapMessage { Id = message.Id, Type = CoapMessageType.Reset }, payload.Endpoint); } throw; } if (_messageResponses.ContainsKey(message.Id)) { _messageResponses[message.Id].TrySetResult(message); } lock (_receiveQueue) { _receiveQueue.Enqueue(Task.FromResult(new CoapReceiveResult(payload.Endpoint, message))); } _receiveEvent.Set(); } } catch (Exception ex) { if (ex is CoapEndpointException) { Endpoint?.Dispose(); Endpoint = null; foreach (var response in _messageResponses.Values) { response.TrySetCanceled(); } } lock (_receiveQueue) { // Gona cheat and enque that exception so it gets thrown as if this detached-infinite-loop never existed... _receiveQueue.Enqueue(Task.FromException <CoapReceiveResult>(ex)); } _receiveEvent.Set(); } }
public static CoapBlockWiseContext CreateBlockWiseContext(this CoapMessage message, CoapClient client, CoapMessage response = null) { if (!message.Code.IsRequest()) { throw new ArgumentException($"A block-Wise context requires a base request message. Message code {message.Code} is invalid.", nameof(message)); } if (response != null && response.Code.IsRequest()) { throw new ArgumentException($"A block-Wise context response can not be set from a message code {message.Code}.", nameof(response)); } return(new CoapBlockWiseContext(client, message, response)); }
public static CoapMessageIdentifier GetIdentifier(this CoapMessage message, ICoapEndpoint endpoint = null, bool isRequest = false) => new CoapMessageIdentifier(message, endpoint, isRequest);
public virtual Task <CoapMessage> DeleteAsync(CoapMessage request, ICoapConnectionInformation connectionInformation) => DeleteAsync(request);
public virtual Task <CoapMessage> PostAsync(CoapMessage request) => Task.FromResult(Post(request));
/// <summary> /// <see cref="SendAsync(CoapMessage, ICoapEndpoint, CancellationToken)"/> /// </summary> /// <param name="message"></param> /// <returns></returns> public virtual async Task <CoapMessageIdentifier> SendAsync(CoapMessage message) => await SendAsync(message, null, CancellationToken.None);
/// <summary> /// Checks if a <see cref="CoapReceiveResult"/> has been received for the coresponding <paramref name="request"/> and returns it. /// Otherwise waits until a new result is received unless cancelled by the <paramref name="token"/> or the <see cref="MaxRetransmitAttempts"/> is reached. /// </summary> /// <param name="request">Waits for a result with a coresponding request message.</param> /// <param name="token">Token to cancel the blocking Receive operation</param> /// <returns>Valid result if a result is received, <c>null</c> if canceled.</returns> /// <exception cref="CoapClientException">If the timeout period * maximum retransmission attempts was reached.</exception> public Task <CoapMessage> GetResponseAsync(CoapMessage request, ICoapEndpoint endpoint = null, bool isRequest = false, CancellationToken token = default(CancellationToken), bool dequeue = true) => GetResponseAsync(request.GetIdentifier(endpoint, isRequest), token, dequeue);
private async Task ReceiveAsyncInternal() { var isMulticast = Endpoint?.IsMulticast ?? false; try { while (true) { Task <CoapPacket> payloadTask; lock (this) { if (Endpoint == null) { return; } payloadTask = Endpoint.ReceiveAsync(_receiveTaskCTS.Token); } var payload = await payloadTask; var receivedAt = DateTime.Now; var message = new CoapMessage { IsMulticast = isMulticast }; try { message.FromBytes(payload.Payload); // Ignore non-empty reset messages if (message.Type == CoapMessageType.Reset && message.Code != CoapMessageCode.None) { continue; } // Reject confirmable empty messages if (message.Type == CoapMessageType.Confirmable && message.Code == CoapMessageCode.None) { await SendAsync(new CoapMessage { Id = message.Id, Type = CoapMessageType.Reset }, payload.Endpoint); continue; } // Ignore repeated messages if (IsRepeated(payload.Endpoint, message.Id)) { continue; } } catch (CoapMessageFormatException) { if (message.Type == CoapMessageType.Confirmable && !isMulticast) { await SendAsync(new CoapMessage { Id = message.Id, Type = CoapMessageType.Reset }, payload.Endpoint); } if (message.Type == CoapMessageType.Acknowledgement && Coap.ReservedMessageCodeClasses.Contains(message.Code.Class)) { continue; } throw; } lock (_recentMessages) { var messageId = message.GetIdentifier(payload.Endpoint); if (_messageResponses.ContainsKey(messageId)) { _messageResponses[messageId].TrySetResult(message); } _recentMessages.Enqueue(Tuple.Create(receivedAt, payload.Endpoint, message)); } _receiveQueue.Enqueue(Task.FromResult(new CoapReceiveResult(payload.Endpoint, message))); _receiveEvent.Set(); } } catch (Exception ex) { if (ex is CoapEndpointException) { var endpoint = Endpoint; lock (this) Endpoint = null; endpoint?.Dispose(); foreach (var response in _messageResponses.Values) { response.TrySetCanceled(); } } // Gona cheat and enque that exception so it gets thrown as if this detached-infinite-loop never existed... _receiveQueue.Enqueue(Task.FromException <CoapReceiveResult>(ex)); _receiveEvent.Set(); } }
public virtual Task <int> PostAsync(string uri, CoapMessage message, ICoapEndpoint endpoint = null) { throw new NotImplementedException(); }
public virtual Task <CoapMessage> DeleteAsync(CoapMessage request) => Task.FromResult(Delete(request));
public CoapReceiveResult(ICoapEndpoint endpoint, CoapMessage message) { Endpoint = endpoint; Message = message; }
/// <summary> /// Checks if a <see cref="CoapMessage"/> is part ofa block-wise transfer by checking for the presence of either <see cref="CoAPNet.Options.Block1"/> or <see cref="CoAPNet.Options.Block2"/>. /// </summary> /// <param name="message"></param> /// <returns><c>true</c> when <paramref name="message"/> is part of a Block-Wise transfer</returns> public static bool IsBlockWise(this CoapMessage message) { return(message.Options.Any(o => o.OptionNumber == CoapRegisteredOptionNumber.Block1 || o.OptionNumber == CoapRegisteredOptionNumber.Block2)); }