private BlockwiseStatus FindResponseBlockStatus(Exchange exchange, Response response) { // NOTICE: This method is used by sendResponse and receiveResponse. Be // careful, making changes to the status in here. BlockwiseStatus status = exchange.ResponseBlockStatus; if (status == null) { status = new BlockwiseStatus(response.ContentType); status.CurrentSZX = BlockOption.EncodeSZX(_defaultBlockSize); exchange.ResponseBlockStatus = status; if (log.IsDebugEnabled) { log.Debug("There is no blockwise status yet. Create and set new block2 status: " + status); } } else { if (log.IsDebugEnabled) { log.Debug("Current blockwise status: " + status); } } return(status); }
public override void Send(Message message) { var blockInfo = NegotiateBlockSize(message); var isBlockwise = message.Payload.Length > BlockOption.DecodeSzx(blockInfo.Szx); if (isBlockwise) { if (!message.HasToken) { message.Token = _tokenManager.AcquireToken(); } var block = message.GetBlock(blockInfo); var optionNumber = message is Request ? OptionNumber.Block1 : OptionNumber.Block2; if (block.GetBlockOption(optionNumber).M > 0) { _incomplete.Add(message.GetTransactionKey(), message); } SendMessageOverLowerLayer(block); } else { SendMessageOverLowerLayer(message); } }
/// <summary> /// Initializes a transfer layer. /// </summary> /// <param name="defaultBlockSize">The default block size used for block-wise transfers or -1 to disable outgoing block-wise transfers</param> public TransferLayer(Int32 defaultBlockSize) { if (defaultBlockSize == 0) { defaultBlockSize = CoapConstants.DefaultBlockSize; } if (defaultBlockSize > 0) { _defaultSZX = BlockOption.EncodeSZX(defaultBlockSize); if (!BlockOption.ValidSZX(_defaultSZX)) { _defaultSZX = defaultBlockSize > 1024 ? 6 : BlockOption.EncodeSZX(defaultBlockSize & 0x07f0); if (log.IsWarnEnabled) { log.Warn(String.Format("TransferLayer - Unsupported block size {0}, using {1} instead", defaultBlockSize, BlockOption.DecodeSZX(_defaultSZX))); } } } else { // disable outgoing blockwise transfers _defaultSZX = -1; } }
/// <summary> /// Notice: /// This method is used by SendRequest and ReceiveRequest. /// Be careful, making changes to the status in here. /// </summary> private BlockwiseStatus FindRequestBlockStatus(Exchange exchange, Request request) { BlockwiseStatus status = exchange.RequestBlockStatus; if (status == null) { status = new BlockwiseStatus(request.ContentType); status.CurrentSZX = BlockOption.EncodeSZX(_defaultBlockSize); exchange.RequestBlockStatus = status; if (log.IsDebugEnabled) { log.Debug("There is no assembler status yet. Create and set new Block1 status: " + status); } } else { if (log.IsDebugEnabled) { log.Debug("Current Block1 status: " + status); } } // sets a timeout to complete exchange PrepareBlockCleanup(exchange); return(status); }
/// <summary> /// Notice: /// This method is used by SendResponse and ReceiveResponse. /// Be careful, making changes to the status in here. /// </summary> private BlockwiseStatus FindResponseBlockStatus(Exchange exchange, Response response) { BlockwiseStatus status = exchange.ResponseBlockStatus; if (status == null) { int blockSize = _defaultBlockSize; if (response.Session != null && response.Session.IsReliable) { blockSize = response.Session.MaxSendSize - 100; } status = new BlockwiseStatus(response.ContentType) { CurrentSZX = BlockOption.EncodeSZX(blockSize) }; exchange.ResponseBlockStatus = status; log.Debug(m => m("There is no blockwise status yet. Create and set new Block2 status: {0}", status)); } else { log.Debug(m => m("Current Block2 status: {0}", status)); } // sets a timeout to complete exchange PrepareBlockCleanup(exchange); return(status); }
public void Should_decode_szx() { var blockSize1 = BlockOption.DecodeSzx(1); var blockSize2 = BlockOption.DecodeSzx(3); Assert.AreEqual(32, blockSize1); Assert.AreEqual(128, blockSize2); }
public void Should_encode_szx() { var szx1 = BlockOption.EncodeSzx(16); var szx2 = BlockOption.EncodeSzx(1024); Assert.AreEqual(0, szx1); Assert.AreEqual(6, szx2); }
public void Should_construct_block1_option() { var option = new BlockOption(OptionNumber.Block1, 3, 1, BlockOption.EncodeSzx(128)); Assert.AreEqual(59, ByteConverter.GetInt(option.Value)); Assert.AreEqual(3, option.Num); Assert.AreEqual(1, option.M); Assert.AreEqual(128, BlockOption.DecodeSzx(option.Szx)); }
public void Should_construct_block2_option() { var option = new BlockOption(OptionNumber.Block2, 2, 0, BlockOption.EncodeSzx(32)); Assert.AreEqual(33, ByteConverter.GetInt(option.Value)); Assert.AreEqual(2, option.Num); Assert.AreEqual(0, option.M); Assert.AreEqual(32, BlockOption.DecodeSzx(option.Szx)); }
/// <inheritdoc/> public override void SendResponse(INextLayer nextLayer, Exchange exchange, Response response) { BlockOption block1 = exchange.Block1ToAck; if (block1 != null) { exchange.Block1ToAck = null; } if (RequiresBlockwise(exchange, response)) { log.Debug(m => m("Response payload {0}/{1} requires Blockwise", response.PayloadSize, _maxMessageSize)); BlockwiseStatus status = FindResponseBlockStatus(exchange, response); Response block = GetNextResponseBlock(response, status); if (block1 != null) { // in case we still have to ack the last block1 block.SetOption(block1); } if (block.Token == null) { block.Token = exchange.Request.Token; } if (status.Complete) { // clean up blockwise status log.Debug(m => m("Ongoing finished on first block {0}", status)); exchange.ResponseBlockStatus = null; ClearBlockCleanup(exchange); } else { log.Debug(m => m("Ongoing started {0}", status)); } exchange.CurrentResponse = block; base.SendResponse(nextLayer, exchange, block); } else { if (block1 != null) { response.SetOption(block1); } exchange.CurrentResponse = response; // Block1 transfer completed ClearBlockCleanup(exchange); base.SendResponse(nextLayer, exchange, response); } }
public static Response Simple_atomic_blockwise_put_response(int num) { var m = num < 2 ? 1 : 0; var response = new Response(MessageType.Acknowledgement, CodeRegistry.Changed) { Id = 1234, Token = ByteConverter.GetBytes(0x17) }; response.AddOption(new BlockOption(OptionNumber.Block1, num, m, BlockOption.EncodeSzx(128))); return(response); }
public TransferLayer(ILayer lowerLayer, int blockSize) : base(lowerLayer) { _tokenManager = new TokenManager(); _incomplete = new Dictionary <string, Message>(); if (blockSize > 0) { _szx = BlockOption.EncodeSzx(blockSize); } }
// example 2 (figure 3): Blockwise GET with early negotiation public static Request Blockwise_get_with_early_negotiation(int num) { var id = 1234 + num; var uri = new Uri("coap://server/status"); var request = new Request(CodeRegistry.Get, true) { Id = id, Uri = uri }; request.AddOption(new BlockOption(OptionNumber.Block2, num, 0, BlockOption.EncodeSzx(64))); return(request); }
public static Response Simple_blockwise_get_block(int num) { var id = 1234 + num; var m = num < 2 ? 1 : 0; var block = new Response(MessageType.Acknowledgement, CodeRegistry.Content) { Id = id, Payload = new byte[128] }; block.AddOption(new BlockOption(OptionNumber.Block2, num, m, BlockOption.EncodeSzx(128))); return(block); }
private void EarlyBlock2Negotiation(Exchange exchange, Request request) { // Call this method when a request has completely arrived (might have // been sent in one piece without blockwise). if (request.HasOption(OptionType.Block2)) { BlockOption block2 = request.Block2; BlockwiseStatus status2 = new BlockwiseStatus(request.ContentType, block2.NUM, block2.SZX); log.Debug(m => m("Request with early block negotiation " + block2 + ". Create and set new Block2 status: " + status2)); exchange.ResponseBlockStatus = status2; } }
/// <inheritdoc/> public override void SendResponse(INextLayer nextLayer, Exchange exchange, Response response) { BlockOption block1 = exchange.Block1ToAck; if (block1 != null) { exchange.Block1ToAck = null; } if (RequiresBlockwise(exchange, response)) { // This must be a large response to a GET or POST request (PUT?) if (log.IsDebugEnabled) { log.Debug("Response payload " + response.PayloadSize + "/" + _maxMessageSize + " requires Blockwise"); } BlockwiseStatus status = FindResponseBlockStatus(exchange, response); Response block = GetNextResponseBlock(response, status); block.Type = response.Type; // This is only true for the first block if (block1 != null) // in case we still have to ack the last block1 { block.SetOption(block1); } if (block.Token == null) { block.Token = exchange.Request.Token; } if (response.HasOption(OptionType.Observe)) { // the ACK for the first block should acknowledge the whole notification exchange.CurrentResponse = response; } else { exchange.CurrentResponse = block; } base.SendResponse(nextLayer, exchange, block); } else { if (block1 != null) { response.SetOption(block1); } exchange.CurrentResponse = response; base.SendResponse(nextLayer, exchange, response); } }
void SetProps(Player p, BlockProps[] scope, BlockID block, string[] args) { BlockOption opt = BlockOptions.Find(args[2]); if (opt == null) { Help(p); return; } string value = args.Length > 3 ? args[3] : ""; opt.SetFunc(p, scope, block, value); scope[block].ChangedScope |= BlockOptions.ScopeId(scope); BlockOptions.ApplyChanges(scope, p.level, block, true); }
private void EarlyBlock2Negotiation(Exchange exchange, Request request) { // Call this method when a request has completely arrived (might have // been sent in one piece without blockwise). if (request.HasOption(OptionType.Block2)) { BlockOption block2 = request.Block2; if (log.IsDebugEnabled) { log.Debug("Request demands blockwise transfer of response with option " + block2 + ". Create and set new block2 status"); } BlockwiseStatus status2 = new BlockwiseStatus(request.ContentType, block2.NUM, block2.SZX); exchange.ResponseBlockStatus = status2; } }
/// <inheritdoc/> public override void SendRequest(INextLayer nextLayer, Exchange exchange, Request request) { // This assumes we don't change individual options - if this is not true then we need to do a deep copy. exchange.PreSecurityOptions = request.GetOptions().ToList(); if ((request.Oscoap == null) && (exchange.OscoreContext == null)) { base.SendRequest(nextLayer, exchange, request); } else if (request.HasOption(OptionType.Block2) && request.Block2.NUM > 0) { // This is the case if the user has explicitly added a block option // for random access. // Note: We do not regard it as random access when the block num is // 0. This is because the user might just want to do early block // size negotiation but actually wants to receive all blocks. log.Debug("Request carries explicit defined block2 option: create random access blockwise status"); BlockwiseStatus status = new BlockwiseStatus(request.ContentFormat); BlockOption block2 = request.Block2; status.CurrentSZX = block2.SZX; status.CurrentNUM = block2.NUM; status.IsRandomAccess = true; exchange.OSCOAP_ResponseBlockStatus = status; base.SendRequest(nextLayer, exchange, request); } else if (RequiresBlockwise(request)) { // This must be a large POST or PUT request log.Debug(m => m($"Request payload {request.PayloadSize}/{_maxMessageSize} requires Blockwise.")); BlockwiseStatus status = FindRequestBlockStatus(exchange, request); Request block = GetNextRequestBlock(request, exchange.PreSecurityOptions, status); exchange.OscoreRequestBlockStatus = status; exchange.CurrentRequest = block; log.Debug($"Block message to send: {block}"); base.SendRequest(nextLayer, exchange, block); } else { exchange.CurrentRequest = request; base.SendRequest(nextLayer, exchange, request); } }
/// <inheritdoc/> public override void SendRequest(INextLayer nextLayer, Exchange exchange, Request request) { if ((request.Oscoap == null) && (exchange.OscoapContext == null)) { base.SendRequest(nextLayer, exchange, request); } else if (request.HasOption(OptionType.Block2) && request.Block2.NUM > 0) { // This is the case if the user has explicitly added a block option // for random access. // Note: We do not regard it as random access when the block num is // 0. This is because the user might just want to do early block // size negotiation but actually wants to receive all blocks. if (log.IsDebugEnabled) { log.Debug("Request carries explicit defined block2 option: create random access blockwise status"); } BlockwiseStatus status = new BlockwiseStatus(request.ContentFormat); BlockOption block2 = request.Block2; status.CurrentSZX = block2.SZX; status.CurrentNUM = block2.NUM; status.IsRandomAccess = true; exchange.OSCOAP_ResponseBlockStatus = status; base.SendRequest(nextLayer, exchange, request); } else if (RequiresBlockwise(request)) { // This must be a large POST or PUT request if (log.IsDebugEnabled) { log.Debug("Request payload " + request.PayloadSize + "/" + _maxMessageSize + " requires Blockwise."); } BlockwiseStatus status = FindRequestBlockStatus(exchange, request); Request block = GetNextRequestBlock(request, status); exchange.OSCOAP_RequestBlockStatus = status; exchange.CurrentRequest = block; base.SendRequest(nextLayer, exchange, block); } else { exchange.CurrentRequest = request; base.SendRequest(nextLayer, exchange, request); } }
public void CanBlockUntilViewIsUpdated(BlockOption blockOption) { // arrange var slowView = new MongoDbViewManager <SlowView>(_mongoDatabase); _dispatcher.AddViewManager(slowView); _commandProcessor.ProcessCommand(new BitePotato("potato1", .1m)); _commandProcessor.ProcessCommand(new BitePotato("potato1", .1m)); _commandProcessor.ProcessCommand(new BitePotato("potato1", .1m)); _commandProcessor.ProcessCommand(new BitePotato("potato1", .1m)); var result = _commandProcessor.ProcessCommand(new BitePotato("potato1", 1)); // act switch (blockOption) { case BlockOption.BlockOnViewManager: Console.WriteLine("Waiting for {0} on the view...", result.GetNewPosition()); slowView.WaitUntilProcessed(result, TimeSpan.FromSeconds(2)).Wait(); break; case BlockOption.BlockOnEventDispatcher: Console.WriteLine("Waiting for {0} on the dispatcher...", result.GetNewPosition()); _dispatcher.WaitUntilProcessed <SlowView>(result, TimeSpan.FromSeconds(2)).Wait(); break; } // assert var instance = slowView.Load(InstancePerAggregateRootLocator.GetViewIdFromAggregateRootId("potato1")); if (blockOption == BlockOption.NoBlock) { Assert.That(instance, Is.Null); Console.WriteLine("View instance was null, just as expected"); } else { Assert.That(instance, Is.Not.Null); Console.WriteLine("View instance was properly updated, just as expected"); } }
public override void Help(Player p, string message) { if (message.CaselessEq("props") || message.CaselessEq("properties")) { p.Message("&HProperties: &f{0}", BlockOptions.Options.Join(o => o.Name)); p.Message("&HUse &T/Help BlockProps [property] &Hfor more details"); return; } BlockOption opt = BlockOptions.Find(message); if (opt != null) { p.Message(opt.Help); } else { p.Message("&WUnrecognised property \"{0}\"", message); } }
public static Message GetBlock(this Message message, int num, int szx) { var blockSize = BlockOption.DecodeSzx(szx); var payloadOffset = num * blockSize; var payloadLeft = message.Payload.Length - payloadOffset; if (payloadLeft > 0) { var block = message is Request ? (Message) new Request(message.Code, message.Type == MessageType.Confirmable) { Id = message.Id } : new Response(message.Type, message.Code) { Id = message.Id }; foreach (var option in message.Options) { block.AddOption(option); } var m = blockSize < payloadLeft ? 1 : 0; if (m == 0) { blockSize = payloadLeft; } var payload = new byte[blockSize]; Buffer.BlockCopy(message.Payload, payloadOffset, payload, 0, blockSize); block.Payload = payload; var optionNumber = message is Request ? OptionNumber.Block1 : OptionNumber.Block2; block.AddOption(new BlockOption(optionNumber, num, m, szx)); return(block); } return(null); }
public TransferContext(Message msg) { if (msg is Request) { cache = msg; uriPath = msg.UriPath; uriQuery = msg.UriQuery; current = (BlockOption)msg.GetFirstOption(OptionType.Block1); } else if (msg is Response) { msg.RequiresToken = false; cache = msg; uriPath = ((Response)msg).Request.UriPath; uriQuery = ((Response)msg).Request.UriQuery; current = (BlockOption)msg.GetFirstOption(OptionType.Block2); } if (log.IsDebugEnabled) { log.Debug(String.Format("TransferLayer - Created new transfer context for {0}?{1}: {2}", uriPath, uriQuery, msg.SequenceKey)); } }
private static Message GetBlock(Message msg, Int32 num, Int32 szx) { Int32 blockSize = BlockOption.DecodeSZX(szx); Int32 payloadOffset = num * blockSize; Int32 payloadLeft = msg.PayloadSize - payloadOffset; if (payloadLeft > 0) { Message block = null; if (msg is Request) { block = new Request(msg.Code, msg.IsConfirmable); } else { block = new Response(msg.Code); if (num == 0 && msg.Type == MessageType.CON) { block.Type = MessageType.CON; } else { block.Type = msg.IsNonConfirmable ? MessageType.NON : MessageType.ACK; } block.ID = msg.ID; } block.PeerAddress = msg.PeerAddress; block.Token = msg.Token; // use same options foreach (Option opt in msg.GetOptions()) { block.AddOption(opt); } // calculate 'more' bit Boolean m = blockSize < payloadLeft; // limit block size to size of payload left if (!m) { blockSize = payloadLeft; } // copy payload block Byte[] blockPayload = new Byte[blockSize]; Array.Copy(msg.Payload, payloadOffset, blockPayload, 0, blockSize); block.Payload = blockPayload; Option blockOpt = null; if (msg is Request) { blockOpt = new BlockOption(OptionType.Block1, num, szx, m); } else { blockOpt = new BlockOption(OptionType.Block2, num, szx, m); } block.SetOption(blockOpt); return(block); } else { return(null); } }
/// <inheritdoc/> public override void ReceiveResponse(INextLayer nextLayer, Exchange exchange, Response response) { // do not continue fetching blocks if canceled if (exchange.Request.IsCancelled) { // reject (in particular for Block+Observe) if (response.Type != MessageType.ACK) { log.Debug("Rejecting blockwise transfer for canceled Exchange"); EmptyMessage rst = EmptyMessage.NewRST(response); SendEmptyMessage(nextLayer, exchange, rst); // Matcher sets exchange as complete when RST is sent } return; } if (!response.HasOption(OptionType.Block1) && !response.HasOption(OptionType.Block2)) { // There is no block1 or block2 option, therefore it is a normal response exchange.Response = response; base.ReceiveResponse(nextLayer, exchange, response); return; } BlockOption block1 = response.Block1; if (block1 != null) { // TODO: What if request has not been sent blockwise (server error) log.Debug(m => m("Response acknowledges block " + block1)); BlockwiseStatus status = exchange.RequestBlockStatus; if (exchange.Request.Session == null) { exchange.Request.Session = exchange.CurrentRequest.Session; if (exchange.Request.Session.IsReliable && exchange.Request.Session.MaxSendSize >= 1024) { status.CurrentSZX = 6; } } if (!status.Complete) { // TODO: the response code should be CONTINUE. Otherwise deliver // Send next block int currentSize = 1 << (4 + status.CurrentSZX); int nextNum = status.CurrentNUM + currentSize / block1.Size; log.Debug(m => m("Send next block num = " + nextNum)); status.CurrentNUM = nextNum; status.CurrentSZX = block1.SZX; Request nextBlock = GetNextRequestBlock(exchange.Request, status); if (exchange.Request.IsMulticast) { // Multicast jumps to the Unicast address exchange.Request.Destination = response.Source; } if (nextBlock.Token == null) { nextBlock.Token = response.Token; // reuse same token } nextBlock.RemoveOptions(OptionType.Observe); exchange.CurrentRequest = nextBlock; base.SendRequest(nextLayer, exchange, nextBlock); // do not deliver response } else if (!response.HasOption(OptionType.Block2)) { // All request block have been acknowledged and we receive a piggy-backed // response that needs no blockwise transfer. Thus, deliver it. base.ReceiveResponse(nextLayer, exchange, response); } else { log.Debug("Response has Block2 option and is therefore sent blockwise"); } } BlockOption block2 = response.Block2; if (block2 != null) { BlockwiseStatus status = FindResponseBlockStatus(exchange, response); if (block2.NUM == status.CurrentNUM) { // We got the block we expected :-) int?obs = response.Observe; if (obs.HasValue) { status.Observe = obs.Value; } // Is the block sized as we expect it int blockCount = 1; int blockSize = 1 << (block2.SZX + 4); if (response.Payload != null) { if (block2.SZX == 7) { blockSize = 1024; blockCount = (response.Payload.Length + 1023) / 1024; } if (response.Payload.Length != blockSize * blockCount) { if (!block2.M) { if ((blockCount - 1) * blockSize >= response.Payload.Length || response.Payload.Length >= blockSize * blockCount) { // This is problem as the body size is wrong. return; } } else { // The body size is wrong return; } } } status.AddBlock(response.Payload); // notify blocking progress exchange.Request.FireResponding(response); if (status.IsRandomAccess) { // The client has requested this specific block and we deliver it exchange.Response = response; base.ReceiveResponse(nextLayer, exchange, response); } else if (block2.M) { log.Debug("Request the next response block"); exchange.Complete = true; exchange.Complete = false; Request request = exchange.Request; int num = block2.NUM + blockCount; int szx = block2.SZX; bool m = false; Request block = new Request(request.Method) { Type = request.Type, Destination = request.IsMulticast ? response.Source : request.Destination, Session = request.Session }; // NON could make sense over SMS or similar transports block.SetOptions(request.GetOptions()); block.SetOption(new BlockOption(OptionType.Block2, num, szx, m)); // we use the same token to ease traceability (GET without Observe no longer cancels relations) // block.Token = response.Token; // make sure not to use Observe for block retrieval block.RemoveOptions(OptionType.Observe); status.CurrentNUM = num; exchange.CurrentRequest = block; base.SendRequest(nextLayer, exchange, block); } else { log.Debug(m => m("We have received all {0} blocks of the response. Assemble and deliver.", status.BlockCount)); Response assembled = new Response(response.StatusCode); AssembleMessage(status, assembled, response); assembled.Type = response.Type; // set overall transfer RTT assembled.RTT = (DateTime.Now - exchange.Timestamp).TotalMilliseconds; // Check if this response is a notification int observe = status.Observe; if (observe != BlockwiseStatus.NoObserve) { assembled.AddOption(Option.Create(OptionType.Observe, observe)); // This is necessary for notifications that are sent blockwise: // Reset block number AND container with all blocks exchange.ResponseBlockStatus = null; } log.Debug(m => m("Assembled response: {0}", assembled)); exchange.Response = assembled; base.ReceiveResponse(nextLayer, exchange, assembled); } } else { // ERROR, wrong block number (server error) // TODO: This scenario is not specified in the draft. // Currently, we reject it and cancel the request. log.Warn(m => m("Wrong block number. Expected {0} but received {1}" + ". Reject response; exchange has failed.", status.CurrentNUM, block2.NUM)); if (response.Type == MessageType.CON) { EmptyMessage rst = EmptyMessage.NewRST(response); base.SendEmptyMessage(nextLayer, exchange, rst); } exchange.Request.IsCancelled = true; } } }
/// <summary> /// Sending a message. /// </summary> /// <param name="msg">The message to be sent</param> protected override void DoSendMessage(Message msg) { int sendSZX = _defaultSZX; int sendNUM = 0; // block negotiation if ((msg is Response) && ((Response)msg).Request != null) { BlockOption buddyBlock = (BlockOption)((Response)msg).Request.GetFirstOption(OptionType.Block2); if (buddyBlock != null) { if (buddyBlock.SZX < sendSZX) { sendSZX = buddyBlock.SZX; } sendNUM = buddyBlock.NUM; } } // check if transfer needs to be split up if (msg.PayloadSize > BlockOption.DecodeSZX(sendSZX)) { // split message up using block1 for requests and block2 for responses Message msgBlock = GetBlock(msg, sendNUM, sendSZX); if (msgBlock != null) { BlockOption block1 = (BlockOption)msgBlock.GetFirstOption(OptionType.Block1); BlockOption block2 = (BlockOption)msgBlock.GetFirstOption(OptionType.Block2); // only cache if blocks remaining for request if ((block1 != null && block1.M) || (block2 != null && block2.M)) { msg.SetOption(block1); msg.SetOption(block2); TransferContext transfer = new TransferContext(msg); _outgoing[msg.SequenceKey] = transfer; if (log.IsDebugEnabled) { log.Debug(String.Format("TransferLayer - Caching blockwise transfer for NUM {0} : {1}", sendNUM, msg.SequenceKey)); } } else { // must be block2 by client if (log.IsDebugEnabled) { log.Debug(String.Format("TransferLayer - Answering block request without caching: {0} | {1}", msg.SequenceKey, block2)); } } // send block and wait for reply SendMessageOverLowerLayer(msgBlock); } else { // must be block2 by client if (log.IsInfoEnabled) { log.Info(String.Format("TransferLayer - Rejecting initial out-of-scope request: {0} | NUM: {1}, SZX: {2} ({3} bytes), M: n/a, {4} bytes available", msg.SequenceKey, sendNUM, sendSZX, BlockOption.DecodeSZX(sendSZX), msg.PayloadSize)); } HandleOutOfScopeError(msg.NewReply(true)); } } else { // send complete message SendMessageOverLowerLayer(msg); } }
/// <inheritdoc/> public override void ReceiveRequest(INextLayer nextLayer, Exchange exchange, Request request) { if (request.HasOption(OptionType.Block1)) { // If this is a multicast address we are receiving this on, then we should ignore it if (request.IsMulticast) { return; } // This must be a large POST or PUT request BlockOption block1 = request.Block1; log.Debug(m => m("Request contains block1 option {0}", block1)); BlockwiseStatus status = FindRequestBlockStatus(exchange, request); if (block1.NUM == 0 && status.CurrentNUM > 0) { // reset the blockwise transfer log.Debug("Block1 num is 0, the client has restarted the blockwise transfer. Reset status."); status = new BlockwiseStatus(request.ContentType); exchange.RequestBlockStatus = status; } if (block1.NUM == status.CurrentNUM) { if (request.ContentType == status.ContentFormat) { status.AddBlock(request.Payload); } else { Response error = Response.CreateResponse(request, StatusCode.RequestEntityIncomplete); error.AddOption(new BlockOption(OptionType.Block1, block1.NUM, block1.SZX, block1.M)); error.SetPayload("Changed Content-Format"); exchange.CurrentResponse = error; base.SendResponse(nextLayer, exchange, error); return; } status.CurrentNUM += 1; if (block1.M) { log.Debug("There are more blocks to come. Acknowledge this block."); Response piggybacked = Response.CreateResponse(request, StatusCode.Continue); piggybacked.AddOption(new BlockOption(OptionType.Block1, block1.NUM, block1.SZX, true)); piggybacked.Last = false; exchange.CurrentResponse = piggybacked; base.SendResponse(nextLayer, exchange, piggybacked); // do not assemble and deliver the request yet } else { log.Debug("This was the last block. Deliver request"); // Remember block to acknowledge. TODO: We might make this a boolean flag in status. exchange.Block1ToAck = block1; // Block2 early negotiation EarlyBlock2Negotiation(exchange, request); // Assemble and deliver Request assembled = new Request(request.Method); AssembleMessage(status, assembled, request); assembled.Session = request.Session; exchange.Request = assembled; exchange.CurrentRequest = assembled; base.ReceiveRequest(nextLayer, exchange, assembled); } } else { // ERROR, wrong number, Incomplete log.Warn(m => m("Wrong block number. Expected {0} but received {1}. Respond with 4.08 (Request Entity Incomplete).", status.CurrentNUM, block1.NUM)); Response error = Response.CreateResponse(request, StatusCode.RequestEntityIncomplete); error.AddOption(new BlockOption(OptionType.Block1, block1.NUM, block1.SZX, block1.M)); error.SetPayload("Wrong block number"); exchange.CurrentResponse = error; base.SendResponse(nextLayer, exchange, error); } } else if (exchange.Response != null && request.HasOption(OptionType.Block2)) { // The response has already been generated and the client just wants // the next block of it BlockOption block2 = request.Block2; Response response = exchange.Response; BlockwiseStatus status = FindResponseBlockStatus(exchange, response); status.CurrentNUM = block2.NUM; status.CurrentSZX = block2.SZX; Response block = GetNextResponseBlock(response, status); block.Token = request.Token; block.RemoveOptions(OptionType.Observe); if (status.Complete) { // clean up blockwise status log.Debug(m => m("Ongoing is complete {0}", status)); exchange.ResponseBlockStatus = null; ClearBlockCleanup(exchange); } else { log.Debug(m => m("Ongoing is continuing {0}", status)); } exchange.CurrentResponse = block; base.SendResponse(nextLayer, exchange, block); } else { EarlyBlock2Negotiation(exchange, request); exchange.Request = request; base.ReceiveRequest(nextLayer, exchange, request); } }
/// <inheritdoc/> public override void ReceiveResponse(INextLayer nextLayer, Exchange exchange, Response response) { // do not continue fetching blocks if canceled if (exchange.Request.IsCancelled) { // reject (in particular for Block+Observe) if (response.Type != MessageType.ACK) { if (log.IsDebugEnabled) { log.Debug("Rejecting blockwise transfer for canceled Exchange"); } EmptyMessage rst = EmptyMessage.NewRST(response); SendEmptyMessage(nextLayer, exchange, rst); // Matcher sets exchange as complete when RST is sent } return; } if (!response.HasOption(OptionType.Block1) && !response.HasOption(OptionType.Block2)) { // There is no block1 or block2 option, therefore it is a normal response exchange.Response = response; base.ReceiveResponse(nextLayer, exchange, response); return; } BlockOption block1 = response.Block1; if (block1 != null) { // TODO: What if request has not been sent blockwise (server error) if (log.IsDebugEnabled) { log.Debug("Response acknowledges block " + block1); } BlockwiseStatus status = exchange.RequestBlockStatus; if (!status.Complete) { // TODO: the response code should be CONTINUE. Otherwise deliver // Send next block Int32 currentSize = 1 << (4 + status.CurrentSZX); Int32 nextNum = status.CurrentNUM + currentSize / block1.Size; if (log.IsDebugEnabled) { log.Debug("Send next block num = " + nextNum); } status.CurrentNUM = nextNum; status.CurrentSZX = block1.SZX; Request nextBlock = GetNextRequestBlock(exchange.Request, status); if (nextBlock.Token == null) { nextBlock.Token = response.Token; // reuse same token } exchange.CurrentRequest = nextBlock; base.SendRequest(nextLayer, exchange, nextBlock); // do not deliver response } else if (!response.HasOption(OptionType.Block2)) { // All request block have been acknowledged and we receive a piggy-backed // response that needs no blockwise transfer. Thus, deliver it. base.ReceiveResponse(nextLayer, exchange, response); } else { if (log.IsDebugEnabled) { log.Debug("Response has Block2 option and is therefore sent blockwise"); } } } BlockOption block2 = response.Block2; if (block2 != null) { BlockwiseStatus status = FindResponseBlockStatus(exchange, response); if (block2.NUM == status.CurrentNUM) { // We got the block we expected :-) status.AddBlock(response.Payload); Int32?obs = response.Observe; if (obs.HasValue) { status.Observe = obs.Value; } // notify blocking progress exchange.Request.FireResponding(response); if (status.IsRandomAccess) { // The client has requested this specifc block and we deliver it exchange.Response = response; base.ReceiveResponse(nextLayer, exchange, response); } else if (block2.M) { if (log.IsDebugEnabled) { log.Debug("Request the next response block"); } Request request = exchange.Request; Int32 num = block2.NUM + 1; Int32 szx = block2.SZX; Boolean m = false; Request block = new Request(request.Method); // NON could make sense over SMS or similar transports block.Type = request.Type; block.Destination = request.Destination; block.SetOptions(request.GetOptions()); block.SetOption(new BlockOption(OptionType.Block2, num, szx, m)); // we use the same token to ease traceability (GET without Observe no longer cancels relations) block.Token = response.Token; // make sure not to use Observe for block retrieval block.RemoveOptions(OptionType.Observe); status.CurrentNUM = num; exchange.CurrentRequest = block; base.SendRequest(nextLayer, exchange, block); } else { if (log.IsDebugEnabled) { log.Debug("We have received all " + status.BlockCount + " blocks of the response. Assemble and deliver."); } Response assembled = new Response(response.StatusCode); AssembleMessage(status, assembled, response); assembled.Type = response.Type; // set overall transfer RTT assembled.RTT = (DateTime.Now - exchange.Timestamp).TotalMilliseconds; // Check if this response is a notification Int32 observe = status.Observe; if (observe != BlockwiseStatus.NoObserve) { assembled.AddOption(Option.Create(OptionType.Observe, observe)); // This is necessary for notifications that are sent blockwise: // Reset block number AND container with all blocks exchange.ResponseBlockStatus = null; } if (log.IsDebugEnabled) { log.Debug("Assembled response: " + assembled); } exchange.Response = assembled; base.ReceiveResponse(nextLayer, exchange, assembled); } } else { // ERROR, wrong block number (server error) // TODO: This scenario is not specified in the draft. // Currently, we reject it and cancel the request. if (log.IsWarnEnabled) { log.Warn("Wrong block number. Expected " + status.CurrentNUM + " but received " + block2.NUM + ". Reject response; exchange has failed."); } if (response.Type == MessageType.CON) { EmptyMessage rst = EmptyMessage.NewRST(response); base.SendEmptyMessage(nextLayer, exchange, rst); } exchange.Request.IsCancelled = true; } } }
public void CanBlockUntilViewIsUpdated(BlockOption blockOption) { // arrange var slowView = new MongoDbViewManager<SlowView>(_mongoDatabase); _dispatcher.AddViewManager(slowView); _commandProcessor.ProcessCommand(new BitePotato("potato1", .1m)); _commandProcessor.ProcessCommand(new BitePotato("potato1", .1m)); _commandProcessor.ProcessCommand(new BitePotato("potato1", .1m)); _commandProcessor.ProcessCommand(new BitePotato("potato1", .1m)); var result = _commandProcessor.ProcessCommand(new BitePotato("potato1", 1)); // act switch (blockOption) { case BlockOption.BlockOnViewManager: Console.WriteLine("Waiting for {0} on the view...", result.GetNewPosition()); slowView.WaitUntilProcessed(result, TimeSpan.FromSeconds(2)).Wait(); break; case BlockOption.BlockOnEventDispatcher: Console.WriteLine("Waiting for {0} on the dispatcher...", result.GetNewPosition()); _dispatcher.WaitUntilProcessed<SlowView>(result, TimeSpan.FromSeconds(2)).Wait(); break; } // assert var instance = slowView.Load(InstancePerAggregateRootLocator.GetViewIdFromAggregateRootId("potato1")); if (blockOption == BlockOption.NoBlock) { Assert.That(instance, Is.Null); Console.WriteLine("View instance was null, just as expected"); } else { Assert.That(instance, Is.Not.Null); Console.WriteLine("View instance was properly updated, just as expected"); } }
/// <inheritdoc/> public override void SendResponse(INextLayer nextLayer, Exchange exchange, Response response) { if ((exchange.OscoapContext == null) && (response.Oscoap == null)) { base.SendResponse(nextLayer, exchange, response); return; } BlockOption block1 = exchange.Block1ToAck; if (block1 != null) { exchange.Block1ToAck = null; } if (RequiresBlockwise(exchange, response)) { if (log.IsDebugEnabled) { log.Debug("Response payload " + response.PayloadSize + "/" + _maxMessageSize + " requires Blockwise"); } BlockwiseStatus status = FindResponseBlockStatus(exchange, response); Response block = GetNextResponseBlock(response, status); if (block1 != null) // in case we still have to ack the last block1 { block.SetOption(block1); } if (block.Token == null) { block.Token = exchange.Request.Token; } if (status.Complete) { // clean up blockwise status if (log.IsDebugEnabled) { log.Debug("Ongoing finished on first block " + status); } exchange.OSCOAP_ResponseBlockStatus = null; ClearBlockCleanup(exchange); } else { if (log.IsDebugEnabled) { log.Debug("Ongoing started " + status); } } exchange.CurrentResponse = block; base.SendResponse(nextLayer, exchange, block); } else { if (block1 != null) { response.SetOption(block1); } exchange.CurrentResponse = response; // Block1 transfer completed ClearBlockCleanup(exchange); base.SendResponse(nextLayer, exchange, response); } }