diff --git a/src/core/coap/coap.cpp b/src/core/coap/coap.cpp index 071064170..681eab9fa 100644 --- a/src/core/coap/coap.cpp +++ b/src/core/coap/coap.cpp @@ -80,16 +80,6 @@ void CoapBase::ClearRequests(const Ip6::Address *aAddress) } } -#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE -void CoapBase::AddBlockWiseResource(ResourceBlockWise &aResource) { IgnoreError(mBlockWiseResources.Add(aResource)); } - -void CoapBase::RemoveBlockWiseResource(ResourceBlockWise &aResource) -{ - IgnoreError(mBlockWiseResources.Remove(aResource)); - aResource.SetNext(nullptr); -} -#endif - void CoapBase::AddResource(Resource &aResource) { IgnoreError(mResources.Add(aResource)); } void CoapBase::RemoveResource(Resource &aResource) @@ -634,408 +624,6 @@ void CoapBase::DequeueMessage(Message &aMessage) ScheduleRetransmissionTimer(); } -#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE -void CoapBase::FreeLastBlockResponse(void) -{ - if (mLastResponse != nullptr) - { - mLastResponse->Free(); - mLastResponse = nullptr; - } -} - -Error CoapBase::CacheLastBlockResponse(Message *aResponse) -{ - Error error = kErrorNone; - // Save last response for block-wise transfer - FreeLastBlockResponse(); - - if ((mLastResponse = aResponse->Clone()) == nullptr) - { - error = kErrorNoBufs; - } - - return error; -} - -Error CoapBase::PrepareNextBlockRequest(Message::BlockType aType, - bool aMoreBlocks, - Message &aRequestOld, - Message &aRequest, - Message &aMessage) -{ - Error error = kErrorNone; - bool isOptionSet = false; - uint16_t blockOption = 0; - Option::Iterator iterator; - Metadata metadata; - - blockOption = (aType == Message::kBlockType1) ? kOptionBlock1 : kOptionBlock2; - - aRequest.Init(kTypeConfirmable, static_cast(aRequestOld.GetCode())); - // Iterate after metadata copied and removed. - metadata.ReadFrom(aRequestOld); - metadata.RemoveFrom(aRequestOld); - // Per RFC 7959, all requests in a block-wise transfer MUST use the same token. - IgnoreError(aRequest.SetTokenFromMessage(aRequestOld)); - SuccessOrExit(error = iterator.Init(aRequestOld)); - - // Copy options from last response to next message - for (; !iterator.IsDone() && iterator.GetOption()->GetLength() != 0; error = iterator.Advance()) - { - uint16_t optionNumber = iterator.GetOption()->GetNumber(); - - SuccessOrExit(error); - - // Check if option to copy next is higher than or equal to Block1 option - if (optionNumber >= blockOption && !isOptionSet) - { - // Write Block1 option to next message - SuccessOrExit(error = aRequest.AppendBlockOption(aType, aMessage.GetBlockWiseBlockNumber() + 1, aMoreBlocks, - aMessage.GetBlockWiseBlockSize())); - aRequest.SetBlockWiseBlockNumber(aMessage.GetBlockWiseBlockNumber() + 1); - aRequest.SetBlockWiseBlockSize(aMessage.GetBlockWiseBlockSize()); - aRequest.SetMoreBlocksFlag(aMoreBlocks); - - isOptionSet = true; - - // If option to copy next is Block1 or Block2 option, option is not copied - if (optionNumber == kOptionBlock1 || optionNumber == kOptionBlock2) - { - continue; - } - } - - // Copy option - SuccessOrExit(error = aRequest.AppendOptionFromMessage(optionNumber, iterator.GetOption()->GetLength(), - iterator.GetMessage(), - iterator.GetOptionValueMessageOffset())); - } - - if (!isOptionSet) - { - // Write Block1 option to next message - SuccessOrExit(error = aRequest.AppendBlockOption(aType, aMessage.GetBlockWiseBlockNumber() + 1, aMoreBlocks, - aMessage.GetBlockWiseBlockSize())); - aRequest.SetBlockWiseBlockNumber(aMessage.GetBlockWiseBlockNumber() + 1); - aRequest.SetBlockWiseBlockSize(aMessage.GetBlockWiseBlockSize()); - aRequest.SetMoreBlocksFlag(aMoreBlocks); - } - - error = metadata.AppendTo(aRequestOld); - -exit: - return error; -} - -Error CoapBase::SendNextBlock1Request(Message &aRequest, - Message &aMessage, - const Ip6::MessageInfo &aMessageInfo, - const Metadata &aCoapMetadata) -{ - Error error = kErrorNone; - Message *request = nullptr; - bool moreBlocks = false; - uint8_t buf[kMaxBlockLength] = {0}; - uint16_t bufLen = kMaxBlockLength; - - SuccessOrExit(error = aRequest.ReadBlockOptionValues(kOptionBlock1)); - SuccessOrExit(error = aMessage.ReadBlockOptionValues(kOptionBlock1)); - - // Conclude block-wise transfer if last block has been received - if (!aRequest.IsMoreBlocksFlagSet()) - { - FinalizeCoapTransaction(aRequest, aCoapMetadata, &aMessage, &aMessageInfo, kErrorNone); - ExitNow(); - } - - // Get next block - VerifyOrExit((bufLen = otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize())) <= kMaxBlockLength, - error = kErrorNoBufs); - - SuccessOrExit( - error = aCoapMetadata.mBlockwiseTransmitHook(aCoapMetadata.mResponseContext, buf, - otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) * - (aMessage.GetBlockWiseBlockNumber() + 1), - &bufLen, &moreBlocks)); - - // Check if block length is valid - VerifyOrExit(bufLen <= otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()), error = kErrorInvalidArgs); - - // Init request for next block - VerifyOrExit((request = NewMessage()) != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = PrepareNextBlockRequest(Message::kBlockType1, moreBlocks, aRequest, *request, aMessage)); - - SuccessOrExit(error = request->SetPayloadMarker()); - - SuccessOrExit(error = request->AppendBytes(buf, bufLen)); - - DequeueMessage(aRequest); - - LogInfo("Send Block1 Nr. %d, Size: %d bytes, More Blocks Flag: %d", request->GetBlockWiseBlockNumber(), - otCoapBlockSizeFromExponent(request->GetBlockWiseBlockSize()), request->IsMoreBlocksFlagSet()); - - SuccessOrExit(error = SendMessage(*request, aMessageInfo, TxParameters::GetDefault(), - aCoapMetadata.mResponseHandler, aCoapMetadata.mResponseContext, - aCoapMetadata.mBlockwiseTransmitHook, aCoapMetadata.mBlockwiseReceiveHook)); - -exit: - FreeMessageOnError(request, error); - - return error; -} - -Error CoapBase::SendNextBlock2Request(Message &aRequest, - Message &aMessage, - const Ip6::MessageInfo &aMessageInfo, - const Metadata &aCoapMetadata, - uint32_t aTotalLength, - bool aBeginBlock1Transfer) -{ - Error error = kErrorNone; - Message *request = nullptr; - uint8_t buf[kMaxBlockLength] = {0}; - uint16_t bufLen = kMaxBlockLength; - - SuccessOrExit(error = aMessage.ReadBlockOptionValues(kOptionBlock2)); - - // Check payload and block length - VerifyOrExit((aMessage.GetLength() - aMessage.GetOffset()) <= - otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) && - (aMessage.GetLength() - aMessage.GetOffset()) <= kMaxBlockLength, - error = kErrorNoBufs); - - // Read and then forward payload to receive hook function - bufLen = aMessage.ReadBytes(aMessage.GetOffset(), buf, aMessage.GetLength() - aMessage.GetOffset()); - SuccessOrExit( - error = aCoapMetadata.mBlockwiseReceiveHook(aCoapMetadata.mResponseContext, buf, - otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) * - aMessage.GetBlockWiseBlockNumber(), - bufLen, aMessage.IsMoreBlocksFlagSet(), aTotalLength)); - - // CoAP Block-Wise Transfer continues - LogInfo("Received Block2 Nr. %d , Size: %d bytes, More Blocks Flag: %d", aMessage.GetBlockWiseBlockNumber(), - otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()), aMessage.IsMoreBlocksFlagSet()); - - // Conclude block-wise transfer if last block has been received - if (!aMessage.IsMoreBlocksFlagSet()) - { - FinalizeCoapTransaction(aRequest, aCoapMetadata, &aMessage, &aMessageInfo, kErrorNone); - ExitNow(); - } - - // Init request for next block - VerifyOrExit((request = NewMessage()) != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = PrepareNextBlockRequest(Message::kBlockType2, aMessage.IsMoreBlocksFlagSet(), aRequest, - *request, aMessage)); - - if (!aBeginBlock1Transfer) - { - DequeueMessage(aRequest); - } - - LogInfo("Request Block2 Nr. %d, Size: %d bytes", request->GetBlockWiseBlockNumber(), - otCoapBlockSizeFromExponent(request->GetBlockWiseBlockSize())); - - SuccessOrExit(error = - SendMessage(*request, aMessageInfo, TxParameters::GetDefault(), aCoapMetadata.mResponseHandler, - aCoapMetadata.mResponseContext, nullptr, aCoapMetadata.mBlockwiseReceiveHook)); - -exit: - FreeMessageOnError(request, error); - - return error; -} - -Error CoapBase::ProcessBlock1Request(Message &aMessage, - const Ip6::MessageInfo &aMessageInfo, - const ResourceBlockWise &aResource, - uint32_t aTotalLength) -{ - Error error = kErrorNone; - Message *response = nullptr; - uint8_t buf[kMaxBlockLength] = {0}; - uint16_t bufLen = kMaxBlockLength; - - SuccessOrExit(error = aMessage.ReadBlockOptionValues(kOptionBlock1)); - - // Read and then forward payload to receive hook function - VerifyOrExit((aMessage.GetLength() - aMessage.GetOffset()) <= kMaxBlockLength, error = kErrorNoBufs); - bufLen = aMessage.ReadBytes(aMessage.GetOffset(), buf, aMessage.GetLength() - aMessage.GetOffset()); - SuccessOrExit(error = aResource.HandleBlockReceive(buf, - otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) * - aMessage.GetBlockWiseBlockNumber(), - bufLen, aMessage.IsMoreBlocksFlagSet(), aTotalLength)); - - if (aMessage.IsMoreBlocksFlagSet()) - { - // Set up next response - VerifyOrExit((response = NewMessage()) != nullptr, error = kErrorFailed); - response->Init(kTypeAck, kCodeContinue); - response->SetMessageId(aMessage.GetMessageId()); - IgnoreReturnValue(response->SetToken(AsConst(aMessage).GetToken(), aMessage.GetTokenLength())); - - response->SetBlockWiseBlockNumber(aMessage.GetBlockWiseBlockNumber()); - response->SetMoreBlocksFlag(aMessage.IsMoreBlocksFlagSet()); - response->SetBlockWiseBlockSize(aMessage.GetBlockWiseBlockSize()); - - SuccessOrExit(error = response->AppendBlockOption(Message::kBlockType1, response->GetBlockWiseBlockNumber(), - response->IsMoreBlocksFlagSet(), - response->GetBlockWiseBlockSize())); - - SuccessOrExit(error = CacheLastBlockResponse(response)); - - LogInfo("Acknowledge Block1 Nr. %d, Size: %d bytes", response->GetBlockWiseBlockNumber(), - otCoapBlockSizeFromExponent(response->GetBlockWiseBlockSize())); - - SuccessOrExit(error = SendMessage(*response, aMessageInfo)); - - error = kErrorBusy; - } - else - { - // Conclude block-wise transfer if last block has been received - FreeLastBlockResponse(); - error = kErrorNone; - } - -exit: - if (error != kErrorNone && error != kErrorBusy && response != nullptr) - { - response->Free(); - } - - return error; -} - -Error CoapBase::ProcessBlock2Request(Message &aMessage, - const Ip6::MessageInfo &aMessageInfo, - const ResourceBlockWise &aResource) -{ - Error error = kErrorNone; - Message *response = nullptr; - uint8_t buf[kMaxBlockLength] = {0}; - uint16_t bufLen = kMaxBlockLength; - bool moreBlocks = false; - uint64_t optionBuf = 0; - Option::Iterator iterator; - - SuccessOrExit(error = aMessage.ReadBlockOptionValues(kOptionBlock2)); - - LogInfo("Request for Block2 Nr. %d, Size: %d bytes received", aMessage.GetBlockWiseBlockNumber(), - otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize())); - - if (aMessage.GetBlockWiseBlockNumber() == 0) - { - aResource.HandleRequest(aMessage, aMessageInfo); - ExitNow(); - } - - // Set up next response - VerifyOrExit((response = NewMessage()) != nullptr, error = kErrorNoBufs); - response->Init(kTypeAck, kCodeContent); - response->SetMessageId(aMessage.GetMessageId()); - - SuccessOrExit(error = response->SetTokenFromMessage(aMessage)); - - VerifyOrExit((bufLen = otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize())) <= kMaxBlockLength, - error = kErrorNoBufs); - SuccessOrExit(error = aResource.HandleBlockTransmit(buf, - otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) * - aMessage.GetBlockWiseBlockNumber(), - &bufLen, &moreBlocks)); - - response->SetMoreBlocksFlag(moreBlocks); - if (moreBlocks) - { - switch (bufLen) - { - case 1024: - response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_1024); - break; - case 512: - response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_512); - break; - case 256: - response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_256); - break; - case 128: - response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_128); - break; - case 64: - response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_64); - break; - case 32: - response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_32); - break; - case 16: - response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_16); - break; - default: - error = kErrorInvalidArgs; - ExitNow(); - break; - } - } - else - { - // Verify that buffer length is not larger than requested block size - VerifyOrExit(bufLen <= otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()), - error = kErrorInvalidArgs); - response->SetBlockWiseBlockSize(aMessage.GetBlockWiseBlockSize()); - } - - response->SetBlockWiseBlockNumber( - (otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) * aMessage.GetBlockWiseBlockNumber()) / - (otCoapBlockSizeFromExponent(response->GetBlockWiseBlockSize()))); - - // Copy options from last response - SuccessOrExit(error = iterator.Init(*mLastResponse)); - - while (!iterator.IsDone()) - { - uint16_t optionNumber = iterator.GetOption()->GetNumber(); - - if (optionNumber == kOptionBlock2) - { - SuccessOrExit(error = response->AppendBlockOption(Message::kBlockType2, response->GetBlockWiseBlockNumber(), - response->IsMoreBlocksFlagSet(), - response->GetBlockWiseBlockSize())); - } - else if (optionNumber == kOptionBlock1) - { - SuccessOrExit(error = iterator.ReadOptionValue(&optionBuf)); - SuccessOrExit(error = response->AppendOption(optionNumber, iterator.GetOption()->GetLength(), &optionBuf)); - } - - SuccessOrExit(error = iterator.Advance()); - } - - SuccessOrExit(error = response->SetPayloadMarker()); - SuccessOrExit(error = response->AppendBytes(buf, bufLen)); - - if (response->IsMoreBlocksFlagSet()) - { - SuccessOrExit(error = CacheLastBlockResponse(response)); - } - else - { - // Conclude block-wise transfer if last block has been received - FreeLastBlockResponse(); - } - - LogInfo("Send Block2 Nr. %d, Size: %d bytes, More Blocks Flag %d", response->GetBlockWiseBlockNumber(), - otCoapBlockSizeFromExponent(response->GetBlockWiseBlockSize()), response->IsMoreBlocksFlagSet()); - - SuccessOrExit(error = SendMessage(*response, aMessageInfo)); - -exit: - FreeMessageOnError(response, error); - - return error; -} -#endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE - void CoapBase::SendCopy(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Error error; @@ -1535,6 +1123,421 @@ exit: } } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// `CoapBase` - BLockwise transfer methods + +#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE + +void CoapBase::AddBlockWiseResource(ResourceBlockWise &aResource) { IgnoreError(mBlockWiseResources.Add(aResource)); } + +void CoapBase::RemoveBlockWiseResource(ResourceBlockWise &aResource) +{ + IgnoreError(mBlockWiseResources.Remove(aResource)); + aResource.SetNext(nullptr); +} + +void CoapBase::FreeLastBlockResponse(void) +{ + if (mLastResponse != nullptr) + { + mLastResponse->Free(); + mLastResponse = nullptr; + } +} + +Error CoapBase::CacheLastBlockResponse(Message *aResponse) +{ + Error error = kErrorNone; + // Save last response for block-wise transfer + FreeLastBlockResponse(); + + if ((mLastResponse = aResponse->Clone()) == nullptr) + { + error = kErrorNoBufs; + } + + return error; +} + +Error CoapBase::PrepareNextBlockRequest(Message::BlockType aType, + bool aMoreBlocks, + Message &aRequestOld, + Message &aRequest, + Message &aMessage) +{ + Error error = kErrorNone; + bool isOptionSet = false; + uint16_t blockOption = 0; + Option::Iterator iterator; + Metadata metadata; + + blockOption = (aType == Message::kBlockType1) ? kOptionBlock1 : kOptionBlock2; + + aRequest.Init(kTypeConfirmable, static_cast(aRequestOld.GetCode())); + // Iterate after metadata copied and removed. + metadata.ReadFrom(aRequestOld); + metadata.RemoveFrom(aRequestOld); + // Per RFC 7959, all requests in a block-wise transfer MUST use the same token. + IgnoreError(aRequest.SetTokenFromMessage(aRequestOld)); + SuccessOrExit(error = iterator.Init(aRequestOld)); + + // Copy options from last response to next message + for (; !iterator.IsDone() && iterator.GetOption()->GetLength() != 0; error = iterator.Advance()) + { + uint16_t optionNumber = iterator.GetOption()->GetNumber(); + + SuccessOrExit(error); + + // Check if option to copy next is higher than or equal to Block1 option + if (optionNumber >= blockOption && !isOptionSet) + { + // Write Block1 option to next message + SuccessOrExit(error = aRequest.AppendBlockOption(aType, aMessage.GetBlockWiseBlockNumber() + 1, aMoreBlocks, + aMessage.GetBlockWiseBlockSize())); + aRequest.SetBlockWiseBlockNumber(aMessage.GetBlockWiseBlockNumber() + 1); + aRequest.SetBlockWiseBlockSize(aMessage.GetBlockWiseBlockSize()); + aRequest.SetMoreBlocksFlag(aMoreBlocks); + + isOptionSet = true; + + // If option to copy next is Block1 or Block2 option, option is not copied + if (optionNumber == kOptionBlock1 || optionNumber == kOptionBlock2) + { + continue; + } + } + + // Copy option + SuccessOrExit(error = aRequest.AppendOptionFromMessage(optionNumber, iterator.GetOption()->GetLength(), + iterator.GetMessage(), + iterator.GetOptionValueMessageOffset())); + } + + if (!isOptionSet) + { + // Write Block1 option to next message + SuccessOrExit(error = aRequest.AppendBlockOption(aType, aMessage.GetBlockWiseBlockNumber() + 1, aMoreBlocks, + aMessage.GetBlockWiseBlockSize())); + aRequest.SetBlockWiseBlockNumber(aMessage.GetBlockWiseBlockNumber() + 1); + aRequest.SetBlockWiseBlockSize(aMessage.GetBlockWiseBlockSize()); + aRequest.SetMoreBlocksFlag(aMoreBlocks); + } + + error = metadata.AppendTo(aRequestOld); + +exit: + return error; +} + +Error CoapBase::SendNextBlock1Request(Message &aRequest, + Message &aMessage, + const Ip6::MessageInfo &aMessageInfo, + const Metadata &aCoapMetadata) +{ + Error error = kErrorNone; + Message *request = nullptr; + bool moreBlocks = false; + uint8_t buf[kMaxBlockLength] = {0}; + uint16_t bufLen = kMaxBlockLength; + + SuccessOrExit(error = aRequest.ReadBlockOptionValues(kOptionBlock1)); + SuccessOrExit(error = aMessage.ReadBlockOptionValues(kOptionBlock1)); + + // Conclude block-wise transfer if last block has been received + if (!aRequest.IsMoreBlocksFlagSet()) + { + FinalizeCoapTransaction(aRequest, aCoapMetadata, &aMessage, &aMessageInfo, kErrorNone); + ExitNow(); + } + + // Get next block + VerifyOrExit((bufLen = otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize())) <= kMaxBlockLength, + error = kErrorNoBufs); + + SuccessOrExit( + error = aCoapMetadata.mBlockwiseTransmitHook(aCoapMetadata.mResponseContext, buf, + otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) * + (aMessage.GetBlockWiseBlockNumber() + 1), + &bufLen, &moreBlocks)); + + // Check if block length is valid + VerifyOrExit(bufLen <= otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()), error = kErrorInvalidArgs); + + // Init request for next block + VerifyOrExit((request = NewMessage()) != nullptr, error = kErrorNoBufs); + SuccessOrExit(error = PrepareNextBlockRequest(Message::kBlockType1, moreBlocks, aRequest, *request, aMessage)); + + SuccessOrExit(error = request->SetPayloadMarker()); + + SuccessOrExit(error = request->AppendBytes(buf, bufLen)); + + DequeueMessage(aRequest); + + LogInfo("Send Block1 Nr. %d, Size: %d bytes, More Blocks Flag: %d", request->GetBlockWiseBlockNumber(), + otCoapBlockSizeFromExponent(request->GetBlockWiseBlockSize()), request->IsMoreBlocksFlagSet()); + + SuccessOrExit(error = SendMessage(*request, aMessageInfo, TxParameters::GetDefault(), + aCoapMetadata.mResponseHandler, aCoapMetadata.mResponseContext, + aCoapMetadata.mBlockwiseTransmitHook, aCoapMetadata.mBlockwiseReceiveHook)); + +exit: + FreeMessageOnError(request, error); + + return error; +} + +Error CoapBase::SendNextBlock2Request(Message &aRequest, + Message &aMessage, + const Ip6::MessageInfo &aMessageInfo, + const Metadata &aCoapMetadata, + uint32_t aTotalLength, + bool aBeginBlock1Transfer) +{ + Error error = kErrorNone; + Message *request = nullptr; + uint8_t buf[kMaxBlockLength] = {0}; + uint16_t bufLen = kMaxBlockLength; + + SuccessOrExit(error = aMessage.ReadBlockOptionValues(kOptionBlock2)); + + // Check payload and block length + VerifyOrExit((aMessage.GetLength() - aMessage.GetOffset()) <= + otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) && + (aMessage.GetLength() - aMessage.GetOffset()) <= kMaxBlockLength, + error = kErrorNoBufs); + + // Read and then forward payload to receive hook function + bufLen = aMessage.ReadBytes(aMessage.GetOffset(), buf, aMessage.GetLength() - aMessage.GetOffset()); + SuccessOrExit( + error = aCoapMetadata.mBlockwiseReceiveHook(aCoapMetadata.mResponseContext, buf, + otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) * + aMessage.GetBlockWiseBlockNumber(), + bufLen, aMessage.IsMoreBlocksFlagSet(), aTotalLength)); + + // CoAP Block-Wise Transfer continues + LogInfo("Received Block2 Nr. %d , Size: %d bytes, More Blocks Flag: %d", aMessage.GetBlockWiseBlockNumber(), + otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()), aMessage.IsMoreBlocksFlagSet()); + + // Conclude block-wise transfer if last block has been received + if (!aMessage.IsMoreBlocksFlagSet()) + { + FinalizeCoapTransaction(aRequest, aCoapMetadata, &aMessage, &aMessageInfo, kErrorNone); + ExitNow(); + } + + // Init request for next block + VerifyOrExit((request = NewMessage()) != nullptr, error = kErrorNoBufs); + SuccessOrExit(error = PrepareNextBlockRequest(Message::kBlockType2, aMessage.IsMoreBlocksFlagSet(), aRequest, + *request, aMessage)); + + if (!aBeginBlock1Transfer) + { + DequeueMessage(aRequest); + } + + LogInfo("Request Block2 Nr. %d, Size: %d bytes", request->GetBlockWiseBlockNumber(), + otCoapBlockSizeFromExponent(request->GetBlockWiseBlockSize())); + + SuccessOrExit(error = + SendMessage(*request, aMessageInfo, TxParameters::GetDefault(), aCoapMetadata.mResponseHandler, + aCoapMetadata.mResponseContext, nullptr, aCoapMetadata.mBlockwiseReceiveHook)); + +exit: + FreeMessageOnError(request, error); + + return error; +} + +Error CoapBase::ProcessBlock1Request(Message &aMessage, + const Ip6::MessageInfo &aMessageInfo, + const ResourceBlockWise &aResource, + uint32_t aTotalLength) +{ + Error error = kErrorNone; + Message *response = nullptr; + uint8_t buf[kMaxBlockLength] = {0}; + uint16_t bufLen = kMaxBlockLength; + + SuccessOrExit(error = aMessage.ReadBlockOptionValues(kOptionBlock1)); + + // Read and then forward payload to receive hook function + VerifyOrExit((aMessage.GetLength() - aMessage.GetOffset()) <= kMaxBlockLength, error = kErrorNoBufs); + bufLen = aMessage.ReadBytes(aMessage.GetOffset(), buf, aMessage.GetLength() - aMessage.GetOffset()); + SuccessOrExit(error = aResource.HandleBlockReceive(buf, + otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) * + aMessage.GetBlockWiseBlockNumber(), + bufLen, aMessage.IsMoreBlocksFlagSet(), aTotalLength)); + + if (aMessage.IsMoreBlocksFlagSet()) + { + // Set up next response + VerifyOrExit((response = NewMessage()) != nullptr, error = kErrorFailed); + response->Init(kTypeAck, kCodeContinue); + response->SetMessageId(aMessage.GetMessageId()); + IgnoreReturnValue(response->SetToken(AsConst(aMessage).GetToken(), aMessage.GetTokenLength())); + + response->SetBlockWiseBlockNumber(aMessage.GetBlockWiseBlockNumber()); + response->SetMoreBlocksFlag(aMessage.IsMoreBlocksFlagSet()); + response->SetBlockWiseBlockSize(aMessage.GetBlockWiseBlockSize()); + + SuccessOrExit(error = response->AppendBlockOption(Message::kBlockType1, response->GetBlockWiseBlockNumber(), + response->IsMoreBlocksFlagSet(), + response->GetBlockWiseBlockSize())); + + SuccessOrExit(error = CacheLastBlockResponse(response)); + + LogInfo("Acknowledge Block1 Nr. %d, Size: %d bytes", response->GetBlockWiseBlockNumber(), + otCoapBlockSizeFromExponent(response->GetBlockWiseBlockSize())); + + SuccessOrExit(error = SendMessage(*response, aMessageInfo)); + + error = kErrorBusy; + } + else + { + // Conclude block-wise transfer if last block has been received + FreeLastBlockResponse(); + error = kErrorNone; + } + +exit: + if (error != kErrorNone && error != kErrorBusy && response != nullptr) + { + response->Free(); + } + + return error; +} + +Error CoapBase::ProcessBlock2Request(Message &aMessage, + const Ip6::MessageInfo &aMessageInfo, + const ResourceBlockWise &aResource) +{ + Error error = kErrorNone; + Message *response = nullptr; + uint8_t buf[kMaxBlockLength] = {0}; + uint16_t bufLen = kMaxBlockLength; + bool moreBlocks = false; + uint64_t optionBuf = 0; + Option::Iterator iterator; + + SuccessOrExit(error = aMessage.ReadBlockOptionValues(kOptionBlock2)); + + LogInfo("Request for Block2 Nr. %d, Size: %d bytes received", aMessage.GetBlockWiseBlockNumber(), + otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize())); + + if (aMessage.GetBlockWiseBlockNumber() == 0) + { + aResource.HandleRequest(aMessage, aMessageInfo); + ExitNow(); + } + + // Set up next response + VerifyOrExit((response = NewMessage()) != nullptr, error = kErrorNoBufs); + response->Init(kTypeAck, kCodeContent); + response->SetMessageId(aMessage.GetMessageId()); + + SuccessOrExit(error = response->SetTokenFromMessage(aMessage)); + + VerifyOrExit((bufLen = otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize())) <= kMaxBlockLength, + error = kErrorNoBufs); + SuccessOrExit(error = aResource.HandleBlockTransmit(buf, + otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) * + aMessage.GetBlockWiseBlockNumber(), + &bufLen, &moreBlocks)); + + response->SetMoreBlocksFlag(moreBlocks); + if (moreBlocks) + { + switch (bufLen) + { + case 1024: + response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_1024); + break; + case 512: + response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_512); + break; + case 256: + response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_256); + break; + case 128: + response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_128); + break; + case 64: + response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_64); + break; + case 32: + response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_32); + break; + case 16: + response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_16); + break; + default: + error = kErrorInvalidArgs; + ExitNow(); + break; + } + } + else + { + // Verify that buffer length is not larger than requested block size + VerifyOrExit(bufLen <= otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()), + error = kErrorInvalidArgs); + response->SetBlockWiseBlockSize(aMessage.GetBlockWiseBlockSize()); + } + + response->SetBlockWiseBlockNumber( + (otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) * aMessage.GetBlockWiseBlockNumber()) / + (otCoapBlockSizeFromExponent(response->GetBlockWiseBlockSize()))); + + // Copy options from last response + SuccessOrExit(error = iterator.Init(*mLastResponse)); + + while (!iterator.IsDone()) + { + uint16_t optionNumber = iterator.GetOption()->GetNumber(); + + if (optionNumber == kOptionBlock2) + { + SuccessOrExit(error = response->AppendBlockOption(Message::kBlockType2, response->GetBlockWiseBlockNumber(), + response->IsMoreBlocksFlagSet(), + response->GetBlockWiseBlockSize())); + } + else if (optionNumber == kOptionBlock1) + { + SuccessOrExit(error = iterator.ReadOptionValue(&optionBuf)); + SuccessOrExit(error = response->AppendOption(optionNumber, iterator.GetOption()->GetLength(), &optionBuf)); + } + + SuccessOrExit(error = iterator.Advance()); + } + + SuccessOrExit(error = response->SetPayloadMarker()); + SuccessOrExit(error = response->AppendBytes(buf, bufLen)); + + if (response->IsMoreBlocksFlagSet()) + { + SuccessOrExit(error = CacheLastBlockResponse(response)); + } + else + { + // Conclude block-wise transfer if last block has been received + FreeLastBlockResponse(); + } + + LogInfo("Send Block2 Nr. %d, Size: %d bytes, More Blocks Flag %d", response->GetBlockWiseBlockNumber(), + otCoapBlockSizeFromExponent(response->GetBlockWiseBlockSize()), response->IsMoreBlocksFlagSet()); + + SuccessOrExit(error = SendMessage(*response, aMessageInfo)); + +exit: + FreeMessageOnError(response, error); + + return error; +} + +#endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE + //--------------------------------------------------------------------------------------------------------------------- // CoapBase::ResponseCache diff --git a/src/core/coap/coap.hpp b/src/core/coap/coap.hpp index 948217f54..c352921a1 100644 --- a/src/core/coap/coap.hpp +++ b/src/core/coap/coap.hpp @@ -268,7 +268,7 @@ protected: mHandler(mContext, &aMessage, &aMessageInfo); } }; -#endif +#endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE /** * Implements the CoAP client and server. @@ -276,10 +276,6 @@ protected: class CoapBase : public InstanceLocator, private NonCopyable { public: -#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE - static constexpr uint16_t kMaxBlockLength = OPENTHREAD_CONFIG_COAP_MAX_BLOCK_LENGTH; -#endif - /** * Pointer is called before CoAP server processing a CoAP message. * @@ -770,6 +766,8 @@ protected: void SetResourceHandler(ResourceHandler aHandler) { mResourceHandler = aHandler; } private: + static constexpr uint16_t kMaxBlockLength = OPENTHREAD_CONFIG_COAP_MAX_BLOCK_LENGTH; + struct Metadata : public Message::FooterData { Ip6::Address mSourceAddress; // IPv6 address of the message source. @@ -828,28 +826,30 @@ private: TimerMilliContext mTimer; }; - Message *InitMessage(Message *aMessage, Type aType, Uri aUri); - Message *InitResponse(Message *aMessage, const Message &aRequest); - + Message *InitMessage(Message *aMessage, Type aType, Uri aUri); + Message *InitResponse(Message *aMessage, const Message &aRequest); void ScheduleRetransmissionTimer(void); static void HandleRetransmissionTimer(Timer &aTimer); void HandleRetransmissionTimer(void); - - void ClearRequests(const Ip6::Address *aAddress); - Message *CopyAndEnqueueMessage(const Message &aMessage, uint16_t aCopyLength, const Metadata &aMetadata); - void DequeueMessage(Message &aMessage); - Message *FindRelatedRequest(const Message &aResponse, const Ip6::MessageInfo &aMessageInfo, Metadata &aMetadata); - void FinalizeCoapTransaction(Message &aRequest, - const Metadata &aMetadata, - Message *aResponse, - const Ip6::MessageInfo *aMessageInfo, - Error aResult); - bool InvokeResponseFallback(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) const; + void ClearRequests(const Ip6::Address *aAddress); + Message *CopyAndEnqueueMessage(const Message &aMessage, uint16_t aCopyLength, const Metadata &aMetadata); + void DequeueMessage(Message &aMessage); + Message *FindRelatedRequest(const Message &aResponse, const Ip6::MessageInfo &aMessageInfo, Metadata &aMetadata); + void FinalizeCoapTransaction(Message &aRequest, + const Metadata &aMetadata, + Message *aResponse, + const Ip6::MessageInfo *aMessageInfo, + Error aResult); + bool InvokeResponseFallback(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) const; + void ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + void ProcessReceivedResponse(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + void SendCopy(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + Error SendEmptyMessage(Type aType, const Message &aRequest, const Ip6::MessageInfo &aMessageInfo); + Error Send(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE void FreeLastBlockResponse(void); Error CacheLastBlockResponse(Message *aResponse); - Error PrepareNextBlockRequest(Message::BlockType aType, bool aMoreBlocks, Message &aRequestOld, @@ -862,11 +862,6 @@ private: Error ProcessBlock2Request(Message &aMessage, const Ip6::MessageInfo &aMessageInfo, const ResourceBlockWise &aResource); -#endif - void ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - void ProcessReceivedResponse(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - -#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE Error SendNextBlock1Request(Message &aRequest, Message &aMessage, const Ip6::MessageInfo &aMessageInfo, @@ -878,10 +873,6 @@ private: uint32_t aTotalLength, bool aBeginBlock1Transfer); #endif - void SendCopy(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - Error SendEmptyMessage(Type aType, const Message &aRequest, const Ip6::MessageInfo &aMessageInfo); - - Error Send(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); MessageQueue mPendingRequests; uint16_t mMessageId;