/// <inheritdoc/> public void AddObserveRelation(ObserveRelation relation) { if (_observeRelations.AddOrUpdate(relation, false, (k, v) => true)) { if (log.IsDebugEnabled) { log.Debug("Replacing observe relation between " + relation.Key + " and resource " + Uri); } } else { if (log.IsDebugEnabled) { log.Debug("Successfully established observe relation between " + relation.Key + " and resource " + Uri); } } }
private void CheckForObserveOption(Exchange exchange, IResource resource) { Request request = exchange.Request; if (request.Method != Method.GET) { return; } System.Net.EndPoint source = request.Source; Int32?obs = request.Observe; if (obs.HasValue && resource.Observable) { if (obs == 0) { // Requests wants to observe and resource allows it :-) if (log.IsDebugEnabled) { log.Debug("Initiate an observe relation between " + source + " and resource " + resource.Uri); } ObservingEndpoint remote = _observeManager.FindObservingEndpoint(source); ObserveRelation relation = new ObserveRelation(_config, remote, resource, exchange); remote.AddObserveRelation(relation); exchange.Relation = relation; // all that's left is to add the relation to the resource which // the resource must do itself if the response is successful } else if (obs == 1) { ObserveRelation relation = _observeManager.GetRelation(source, request.Token); if (relation != null) { relation.Cancel(); } } } }
/// <inheritdoc/> public override void SendResponse(INextLayer nextLayer, Exchange exchange, Response response) { ObserveRelation relation = exchange.Relation; if (relation != null && relation.Established) { if (exchange.Request.IsAcknowledged || exchange.Request.Type == MessageType.NON) { // Transmit errors as CON if (!Code.IsSuccess(response.Code)) { if (log.IsDebugEnabled) { log.Debug("Response has error code " + response.Code + " and must be sent as CON"); } response.Type = MessageType.CON; relation.Cancel(); } else { // Make sure that every now and than a CON is mixed within if (relation.Check()) { if (log.IsDebugEnabled) { log.Debug("The observe relation check requires the notification to be sent as CON"); } response.Type = MessageType.CON; } else { // By default use NON, but do not override resource decision if (response.Type == MessageType.Unknown) { response.Type = MessageType.NON; } } } } // This is a notification response.Last = false; /* * The matcher must be able to find the NON notifications to remove * them from the exchangesByID map */ if (response.Type == MessageType.NON) { relation.AddNotification(response); } /* * Only one Confirmable message is allowed to be in transit. A CON * is in transit as long as it has not been acknowledged, rejected, * or timed out. All further notifications are postponed here. If a * former CON is acknowledged or timeouts, it starts the freshest * notification (In case of a timeout, it keeps the retransmission * counter). When a fresh/younger notification arrives but must be * postponed we forget any former notification. */ if (response.Type == MessageType.CON) { PrepareSelfReplacement(nextLayer, exchange, response); } // The decision whether to postpone this notification or not and the // decision which notification is the freshest to send next must be // synchronized lock (exchange) { Response current = relation.CurrentControlNotification; if (current != null && IsInTransit(current)) { if (log.IsDebugEnabled) { log.Debug("A former notification is still in transit. Postpone " + response); } // use the same ID response.ID = current.ID; relation.NextControlNotification = response; return; } else { relation.CurrentControlNotification = response; relation.NextControlNotification = null; } } } // else no observe was requested or the resource does not allow it base.SendResponse(nextLayer, exchange, response); }
private void PrepareSelfReplacement(INextLayer nextLayer, Exchange exchange, Response response) { response.Acknowledged += (o, e) => { lock (exchange) { ObserveRelation relation = exchange.Relation; Response next = relation.NextControlNotification; relation.CurrentControlNotification = next; // next may be null relation.NextControlNotification = null; if (next != null) { if (log.IsDebugEnabled) { log.Debug("Notification has been acknowledged, send the next one"); } // this is not a self replacement, hence a new ID next.ID = Message.None; // Create a new task for sending next response so that we can leave the sync-block Executor.Start(() => SendResponse(nextLayer, exchange, next)); } } }; response.Retransmitting += (o, e) => { lock (exchange) { ObserveRelation relation = exchange.Relation; Response next = relation.NextControlNotification; if (next != null) { if (log.IsDebugEnabled) { log.Debug("The notification has timed out and there is a fresher notification for the retransmission."); } // Cancel the original retransmission and send the fresh notification here response.IsCancelled = true; // use the same ID next.ID = response.ID; // Convert all notification retransmissions to CON if (next.Type != MessageType.CON) { next.Type = MessageType.CON; PrepareSelfReplacement(nextLayer, exchange, next); } relation.CurrentControlNotification = next; relation.NextControlNotification = null; // Create a new task for sending next response so that we can leave the sync-block Executor.Start(() => SendResponse(nextLayer, exchange, next)); } } }; response.TimedOut += (o, e) => { ObserveRelation relation = exchange.Relation; if (log.IsDebugEnabled) { log.Debug("Notification" + relation.Exchange.Request.TokenString + " timed out. Cancel all relations with source " + relation.Source); } relation.CancelAll(); }; }
/// <inheritdoc/> public void SendResponse(Exchange exchange, Response response) { if (response.ID == Message.None) { response.ID = System.Threading.Interlocked.Increment(ref _currentID) % (1 << 16); } /* * The response is a CON or NON or ACK and must be prepared for these * - CON => ACK / RST // we only care to stop retransmission * - NON => RST // we only care for observe * - ACK => nothing! * If this response goes lost, we must be prepared to get the same * CON/NON request with same MID again. We then find the corresponding * exchange and the ReliabilityLayer resends this response. */ // If this is a CON notification we now can forget all previous NON notifications if (response.Type == MessageType.CON || response.Type == MessageType.ACK) { ObserveRelation relation = exchange.Relation; if (relation != null) { RemoveNotificatoinsOf(relation); } } // Blockwise transfers are identified by URI and remote endpoint if (response.HasOption(OptionType.Block2)) { Request request = exchange.CurrentRequest; Exchange.KeyUri keyUri = new Exchange.KeyUri(request.URI, response.Destination); // Observe notifications only send the first block, hence do not store them as ongoing if (exchange.ResponseBlockStatus != null && !response.HasOption(OptionType.Observe)) { // Remember ongoing blockwise GET requests if (Utils.Put(_ongoingExchanges, keyUri, exchange) == null) { if (log.IsDebugEnabled) { log.Debug("Ongoing Block2 started late, storing " + keyUri + " for " + request); } } else { if (log.IsDebugEnabled) { log.Debug("Ongoing Block2 continued, storing " + keyUri + " for " + request); } } } else { if (log.IsDebugEnabled) { log.Debug("Ongoing Block2 completed, cleaning up " + keyUri + " for " + request); } Exchange exc; _ongoingExchanges.TryRemove(keyUri, out exc); } } // Insert CON and NON to match ACKs and RSTs to the exchange // Do not insert ACKs and RSTs. if (response.Type == MessageType.CON || response.Type == MessageType.NON) { Exchange.KeyID keyID = new Exchange.KeyID(response.ID, null); _exchangesByID[keyID] = exchange; } // Only CONs and Observe keep the exchange active if (response.Type != MessageType.CON && response.Last) { exchange.Complete = true; } }
/// <inheritdoc/> public void RemoveObserveRelation(ObserveRelation relation) { Boolean val; _observeRelations.TryRemove(relation, out val); }
private void OnExchangeCompleted(Object sender, EventArgs e) { Exchange exchange = (Exchange)sender; /* * Logging in this method leads to significant performance loss. * Uncomment logging code only for debugging purposes. */ if (exchange.Origin == Origin.Local) { // this endpoint created the Exchange by issuing a request Exchange.KeyID keyID = new Exchange.KeyID(exchange.CurrentRequest.ID, null, null); Exchange.KeyToken keyToken = new Exchange.KeyToken(exchange.CurrentRequest.Token); if (_Log.IsDebugEnabled) { _Log.Debug("Exchange completed: Cleaning up " + keyToken); } _exchangesByToken.Remove(keyToken); // in case an empty ACK was lost _exchangesByID.Remove(keyID); } else // Origin.Remote { // this endpoint created the Exchange to respond a request Response response = exchange.CurrentResponse; if (response != null && response.Type != MessageType.ACK) { // only response MIDs are stored for ACK and RST, no reponse Tokens Exchange.KeyID midKey = new Exchange.KeyID(response.ID, null, response.Session); //if (log.IsDebugEnabled) // log.Debug("Remote ongoing completed, cleaning up " + midKey); _exchangesByID.Remove(midKey); } Request request = exchange.CurrentRequest; if (request != null && (request.HasOption(OptionType.Block1) || response.HasOption(OptionType.Block2))) { #if INCLUDE_OSCOAP byte[] oscoapValue = null; if (request.HasOption(OptionType.Oscoap)) { oscoapValue = request.Oscoap.RawValue; } Exchange.KeyUri uriKey = new Exchange.KeyUri(request.URI, oscoapValue, request.Source); #else Exchange.KeyUri uriKey = new Exchange.KeyUri(request.URI, request.Source); #endif if (_Log.IsDebugEnabled) { _Log.Debug("Remote ongoing completed, cleaning up " + uriKey); } Exchange exc; _ongoingExchanges.TryRemove(uriKey, out exc); } // Remove all remaining NON-notifications if this exchange is an observe relation ObserveRelation relation = exchange.Relation; if (relation != null) { RemoveNotificatoinsOf(relation); } } }
/// <inheritdoc/> public void RemoveObserveRelation(ObserveRelation relation) { ((ICollection <KeyValuePair <String, ObserveRelation> >)_observeRelations).Remove(new KeyValuePair <String, ObserveRelation>(relation.Key, relation)); }
/// <inheritdoc/> public void SendResponse(Exchange exchange, Response response) { if (response.ID == Message.None) { response.ID = System.Threading.Interlocked.Increment(ref _currentID) % (1 << 16); } /* * The response is a CON or NON or ACK and must be prepared for these * - CON => ACK/RST // we only care to stop retransmission * - NON => RST // we don't care * - ACK => nothing! * If this response goes lost, we must be prepared to get the same * CON/NON request with same MID again. We then find the corresponding * exchange and the ReliabilityLayer resends this response. */ if (response.Destination == null) { throw new InvalidOperationException("Response has no destination set"); } // If this is a CON notification we now can forget all previous NON notifications if (response.Type == MessageType.CON || response.Type == MessageType.ACK) { ObserveRelation relation = exchange.Relation; if (relation != null) { RemoveNotificatoinsOf(relation); } } if (response.HasOption(OptionType.Block2)) { Request request = exchange.Request; Exchange.KeyUri keyUri = new Exchange.KeyUri(request.URI, response.Destination); if (exchange.ResponseBlockStatus != null && !response.HasOption(OptionType.Observe)) { // Remember ongoing blockwise GET requests if (log.IsDebugEnabled) { log.Debug("Ongoing Block2 started, storing " + keyUri + "\nOngoing " + request + "\nOngoing " + response); } _ongoingExchanges[keyUri] = exchange; } else { if (log.IsDebugEnabled) { log.Debug("Ongoing Block2 completed, cleaning up " + keyUri + "\nOngoing " + request + "\nOngoing " + response); } _ongoingExchanges.Remove(keyUri); } } // Insert CON and NON to match ACKs and RSTs to the exchange // Do not insert ACKs and RSTs. if (response.Type == MessageType.CON || response.Type == MessageType.NON) { Exchange.KeyID keyID = new Exchange.KeyID(response.ID, response.Destination); _exchangesByID[keyID] = exchange; } if (response.Type == MessageType.ACK || response.Type == MessageType.NON) { // Since this is an ACK or NON, the exchange is over with sending this response. if (response.Last) { exchange.Complete = true; } } // else this is a CON and we need to wait for the ACK or RST }