/// <inheritdoc/> public void SendRequest(Exchange exchange, Request request) { if (request.ID == Message.None) { request.ID = System.Threading.Interlocked.Increment(ref _currentID) % (1 << 16); } /* * The request is a CON or NON and must be prepared for these responses * - CON => ACK / RST / ACK+response / CON+response / NON+response * - NON => RST / CON+response / NON+response * If this request goes lost, we do not get anything back. */ // the MID is from the local namespace -- use blank address Exchange.KeyID keyID = new Exchange.KeyID(request.ID, null, request.Session); // If we do not have a token, then create one Exchange.KeyToken keyToken; if (request.Token == null) { int length = _tokenLength; if (_tokenLength < 0) { length = _random.Next(8); } byte[] token = new byte[length]; int tries = 0; do { if ((length < 8) && (tries > length * 5 + 1)) { length += 1; tries = 0; token = new byte[length]; } _random.NextBytes(token); keyToken = new Exchange.KeyToken(token); } while (_exchangesByToken.ContainsKey(keyToken)); request.Token = token; } else { keyToken = new Exchange.KeyToken(request.Token); } exchange.Completed += OnExchangeCompleted; _Log.Debug(m => m("Stored open request by {0}, {1}", keyID, keyToken)); _exchangesByID[keyID] = exchange; _exchangesByToken[keyToken] = exchange; }
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))) { Exchange.KeyUri uriKey = new Exchange.KeyUri(request, request.Source); _Log.Debug(m => m($"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 Exchange ReceiveResponse(Response response) { /* * This response could be * - The first CON/NON/ACK+response => deliver * - Retransmitted CON (because client got no ACK) * => resend ACK */ Exchange.KeyID keyId; if (response.Type == MessageType.ACK) { // own namespace keyId = new Exchange.KeyID(response.ID, null, response.Session); } else { // remote namespace keyId = new Exchange.KeyID(response.ID, response.Source, response.Session); } Exchange.KeyToken keyToken = new Exchange.KeyToken(response.Token); Exchange exchange; if (_exchangesByToken.TryGetValue(keyToken, out exchange)) { // We need to play games if this is multicast if (exchange.CurrentRequest.IsMulticast) { Exchange newExchange = new Exchange(exchange); newExchange.Request = exchange.Request; exchange = newExchange; } // There is an exchange with the given token Exchange prev = _deduplicator.FindPrevious(keyId, exchange); if (prev != null) { // (and thus it holds: prev == exchange) _Log.Info(m => m($"Duplicate response for open exchange: {response}")); response.Duplicate = true; } else { keyId = new Exchange.KeyID(exchange.CurrentRequest.ID, null, response.Session); _Log.Debug(m => m($"Exchange got response: Cleaning up {keyId}")); _exchangesByID.Remove(keyId); } if (response.Type == MessageType.ACK && exchange.CurrentRequest.ID != response.ID) { // The token matches but not the MID. This is a response for an older exchange _Log.Warn(m => m($"Possible MID reuse before lifetime end: {response.TokenString} expected MID {exchange.CurrentRequest.ID} but received {response.ID}")); } return(exchange); } else { // There is no exchange with the given token. if (response.Type != MessageType.ACK) { // only act upon separate responses Exchange prev = _deduplicator.Find(keyId); if (prev != null) { _Log.Info(m => m($"Duplicate response for completed exchange: {response}")); response.Duplicate = true; return(prev); } } else { _Log.Info(m => m($"Ignoring unmatchable piggy-backed response from {response.Source}: {response}")); } // ignore response return(null); } }
/// <inheritdoc/> public Exchange ReceiveResponse(Response response) { /* * This response could be * - The first CON/NON/ACK+response => deliver * - Retransmitted CON (because client got no ACK) * => resend ACK */ Exchange.KeyID keyId; if (response.Type == MessageType.ACK) { // own namespace keyId = new Exchange.KeyID(response.ID, null, response.Session); } else { // remote namespace keyId = new Exchange.KeyID(response.ID, response.Source, response.Session); } Exchange.KeyToken keyToken = new Exchange.KeyToken(response.Token); Exchange exchange; if (_exchangesByToken.TryGetValue(keyToken, out exchange)) { // There is an exchange with the given token Exchange prev = _deduplicator.FindPrevious(keyId, exchange); if (prev != null) { // (and thus it holds: prev == exchange) if (_Log.IsInfoEnabled) { _Log.Info("Duplicate response for open exchange: " + response); } response.Duplicate = true; } else { keyId = new Exchange.KeyID(exchange.CurrentRequest.ID, null, response.Session); if (_Log.IsDebugEnabled) { _Log.Debug("Exchange got response: Cleaning up " + keyId); } _exchangesByID.Remove(keyId); } if (response.Type == MessageType.ACK && exchange.CurrentRequest.ID != response.ID) { // The token matches but not the MID. This is a response for an older exchange if (_Log.IsWarnEnabled) { _Log.Warn("Possible MID reuse before lifetime end: " + response.TokenString + " expected MID " + exchange.CurrentRequest.ID + " but received " + response.ID); } } return(exchange); } else { // There is no exchange with the given token. if (response.Type != MessageType.ACK) { // only act upon separate responses Exchange prev = _deduplicator.Find(keyId); if (prev != null) { if (_Log.IsInfoEnabled) { _Log.Info("Duplicate response for completed exchange: " + response); } response.Duplicate = true; return(prev); } } else { if (_Log.IsInfoEnabled) { _Log.Info("Ignoring unmatchable piggy-backed response from " + response.Source + ": " + response); } } // ignore response return(null); } }