[coap] move blockwise transfer logic to dedicated methods (#12240)

This change reorganizes the CoAP blockwise transfer implementation to
improve code structure and readability.

The logic for handling blockwise transfers is extracted from
`ProcessReceivedRequest()` and `ProcessReceivedResponse()` into two new
private helper methods: `ProcessBlockwiseRequest()` and
`ProcessBlockwiseResponse()`.

This separation makes the main request and response processing methods
simpler and more focused on their primary role, delegating the
complexities of blockwise transfers to dedicated functions.

Additionally, this change introduces `Message::UriPathStringBuffer` as a
`typedef` to provide a clear and consistent type for handling URI path
string buffers.
This commit is contained in:
Abtin Keshavarzian
2025-12-31 14:40:35 -08:00
committed by GitHub
parent ed6235304b
commit bd897509f5
4 changed files with 227 additions and 192 deletions
+213 -187
View File
@@ -682,10 +682,6 @@ void CoapBase::ProcessReceivedResponse(Message &aMessage, const Ip6::MessageInfo
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
bool responseObserve = false;
#endif
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
uint8_t blockOptionType = 0;
uint32_t totalTransferSize = 0;
#endif
request = FindRelatedRequest(aMessage, aMessageInfo, metadata);
VerifyOrExit(request != nullptr);
@@ -761,89 +757,13 @@ void CoapBase::ProcessReceivedResponse(Message &aMessage, const Ip6::MessageInfo
}
else
#endif
{
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
{
if (metadata.mBlockwiseTransmitHook != nullptr || metadata.mBlockwiseReceiveHook != nullptr)
{
// Search for CoAP Block-Wise Option [RFC7959]
Option::Iterator iterator;
SuccessOrExit(error = iterator.Init(aMessage));
while (!iterator.IsDone())
{
switch (iterator.GetOption()->GetNumber())
{
case kOptionBlock1:
blockOptionType += 1;
break;
case kOptionBlock2:
blockOptionType += 2;
break;
case kOptionSize2:
// ToDo: wait for method to read uint option values
totalTransferSize = 0;
break;
default:
break;
}
SuccessOrExit(error = iterator.Advance());
}
}
switch (blockOptionType)
{
case 0:
// Piggybacked response.
FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, kErrorNone);
break;
case 1: // Block1 option
if (aMessage.GetCode() == kCodeContinue && metadata.mBlockwiseTransmitHook != nullptr)
{
error = SendNextBlock1Request(*request, aMessage, aMessageInfo, metadata);
}
if (aMessage.GetCode() != kCodeContinue || metadata.mBlockwiseTransmitHook == nullptr ||
error != kErrorNone)
{
FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, error);
}
break;
case 2: // Block2 option
if (aMessage.GetCode() < kCodeBadRequest && metadata.mBlockwiseReceiveHook != nullptr)
{
error =
SendNextBlock2Request(*request, aMessage, aMessageInfo, metadata, totalTransferSize, false);
}
if (aMessage.GetCode() >= kCodeBadRequest || metadata.mBlockwiseReceiveHook == nullptr ||
error != kErrorNone)
{
FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, error);
}
break;
case 3: // Block1 & Block2 option
if (aMessage.GetCode() < kCodeBadRequest && metadata.mBlockwiseReceiveHook != nullptr)
{
error =
SendNextBlock2Request(*request, aMessage, aMessageInfo, metadata, totalTransferSize, true);
}
FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, error);
break;
default:
error = kErrorAbort;
FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, error);
break;
}
}
#else // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
{
SuccessOrExit(error = ProcessBlockwiseResponse(aMessage, aMessageInfo, *request, metadata));
#else
FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, kErrorNone);
#endif
}
#endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
}
// Silently ignore acknowledgments carrying requests (RFC 7252, p. 4.2)
@@ -914,14 +834,8 @@ exit:
void CoapBase::ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
char uriPath[Message::kMaxReceivedUriPath + 1];
Error error = kErrorNone;
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
Option::Iterator iterator;
char *curUriPath = uriPath;
uint8_t blockOptionType = 0;
uint32_t totalTransferSize = 0;
#endif
Message::UriPathStringBuffer uriPath;
Error error = kErrorNone;
if (mInterceptor.IsSet())
{
@@ -944,106 +858,15 @@ void CoapBase::ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo
}
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
SuccessOrExit(error = iterator.Init(aMessage));
while (!iterator.IsDone())
{
switch (iterator.GetOption()->GetNumber())
{
case kOptionUriPath:
if (curUriPath != uriPath)
{
*curUriPath++ = '/';
}
bool didHandle = false;
VerifyOrExit(curUriPath + iterator.GetOption()->GetLength() < GetArrayEnd(uriPath), error = kErrorParse);
IgnoreError(iterator.ReadOptionValue(curUriPath));
curUriPath += iterator.GetOption()->GetLength();
break;
case kOptionBlock1:
blockOptionType += 1;
break;
case kOptionBlock2:
blockOptionType += 2;
break;
case kOptionSize1:
// ToDo: wait for method to read uint option values
totalTransferSize = 0;
break;
default:
break;
}
SuccessOrExit(error = iterator.Advance());
}
curUriPath[0] = kNullChar;
for (const ResourceBlockWise &resource : mBlockWiseResources)
{
if (!StringMatch(resource.GetUriPath(), uriPath))
{
continue;
}
if ((resource.mReceiveHook != nullptr || resource.mTransmitHook != nullptr) && blockOptionType != 0)
{
switch (blockOptionType)
{
case 1:
if (resource.mReceiveHook != nullptr)
{
switch (ProcessBlock1Request(aMessage, aMessageInfo, resource, totalTransferSize))
{
case kErrorNone:
resource.HandleRequest(aMessage, aMessageInfo);
OT_FALL_THROUGH;
case kErrorBusy:
error = kErrorNone;
break;
case kErrorNoBufs:
IgnoreReturnValue(SendHeaderResponse(kCodeRequestTooLarge, aMessage, aMessageInfo));
error = kErrorDrop;
break;
case kErrorNoFrameReceived:
IgnoreReturnValue(SendHeaderResponse(kCodeRequestIncomplete, aMessage, aMessageInfo));
error = kErrorDrop;
break;
default:
IgnoreReturnValue(SendHeaderResponse(kCodeInternalError, aMessage, aMessageInfo));
error = kErrorDrop;
break;
}
}
break;
case 2:
if (resource.mTransmitHook != nullptr)
{
if ((error = ProcessBlock2Request(aMessage, aMessageInfo, resource)) != kErrorNone)
{
IgnoreReturnValue(SendHeaderResponse(kCodeInternalError, aMessage, aMessageInfo));
error = kErrorDrop;
}
}
break;
}
ExitNow();
}
else
{
resource.HandleRequest(aMessage, aMessageInfo);
error = kErrorNone;
ExitNow();
}
SuccessOrExit(error = ProcessBlockwiseRequest(aMessage, aMessageInfo, uriPath, didHandle));
VerifyOrExit(!didHandle);
}
#else
SuccessOrExit(error = aMessage.ReadUriPathOptions(uriPath));
#endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
#endif
if ((mResourceHandler != nullptr) && mResourceHandler(*this, uriPath, aMessage, aMessageInfo))
{
@@ -1145,6 +968,209 @@ exit:
return error;
}
Error CoapBase::ProcessBlockwiseResponse(Message &aResponse,
const Ip6::MessageInfo &aMessageInfo,
Message &aRequest,
const Metadata &aMetadata)
{
Error error = kErrorNone;
uint8_t blockOptionType = 0;
uint32_t totalTransferSize = 0;
if (aMetadata.mBlockwiseTransmitHook != nullptr || aMetadata.mBlockwiseReceiveHook != nullptr)
{
// Search for CoAP Block-Wise Option [RFC7959]
Option::Iterator iterator;
SuccessOrExit(error = iterator.Init(aResponse));
while (!iterator.IsDone())
{
switch (iterator.GetOption()->GetNumber())
{
case kOptionBlock1:
blockOptionType += 1;
break;
case kOptionBlock2:
blockOptionType += 2;
break;
case kOptionSize2:
// ToDo: wait for method to read uint option values
totalTransferSize = 0;
break;
default:
break;
}
SuccessOrExit(error = iterator.Advance());
}
}
switch (blockOptionType)
{
case 0:
// Piggybacked response.
FinalizeCoapTransaction(aRequest, aMetadata, &aResponse, &aMessageInfo, kErrorNone);
break;
case 1: // Block1 option
if (aResponse.GetCode() == kCodeContinue && aMetadata.mBlockwiseTransmitHook != nullptr)
{
error = SendNextBlock1Request(aRequest, aResponse, aMessageInfo, aMetadata);
}
if (aResponse.GetCode() != kCodeContinue || aMetadata.mBlockwiseTransmitHook == nullptr || error != kErrorNone)
{
FinalizeCoapTransaction(aRequest, aMetadata, &aResponse, &aMessageInfo, error);
}
break;
case 2: // Block2 option
if (aResponse.GetCode() < kCodeBadRequest && aMetadata.mBlockwiseReceiveHook != nullptr)
{
error = SendNextBlock2Request(aRequest, aResponse, aMessageInfo, aMetadata, totalTransferSize, false);
}
if (aResponse.GetCode() >= kCodeBadRequest || aMetadata.mBlockwiseReceiveHook == nullptr || error != kErrorNone)
{
FinalizeCoapTransaction(aRequest, aMetadata, &aResponse, &aMessageInfo, error);
}
break;
case 3: // Block1 & Block2 option
if (aResponse.GetCode() < kCodeBadRequest && aMetadata.mBlockwiseReceiveHook != nullptr)
{
error = SendNextBlock2Request(aRequest, aResponse, aMessageInfo, aMetadata, totalTransferSize, true);
}
FinalizeCoapTransaction(aRequest, aMetadata, &aResponse, &aMessageInfo, error);
break;
default:
error = kErrorAbort;
FinalizeCoapTransaction(aRequest, aMetadata, &aResponse, &aMessageInfo, error);
break;
}
exit:
return error;
}
Error CoapBase::ProcessBlockwiseRequest(Message &aMessage,
const Ip6::MessageInfo &aMessageInfo,
Message::UriPathStringBuffer &aUriPath,
bool &aDidHandle)
{
Error error = kErrorNone;
Option::Iterator iterator;
char *curUriPath = aUriPath;
uint8_t blockOptionType = 0;
uint32_t totalTransferSize = 0;
SuccessOrExit(error = iterator.Init(aMessage));
while (!iterator.IsDone())
{
switch (iterator.GetOption()->GetNumber())
{
case kOptionUriPath:
if (curUriPath != aUriPath)
{
*curUriPath++ = '/';
}
VerifyOrExit(curUriPath + iterator.GetOption()->GetLength() < GetArrayEnd(aUriPath), error = kErrorParse);
IgnoreError(iterator.ReadOptionValue(curUriPath));
curUriPath += iterator.GetOption()->GetLength();
break;
case kOptionBlock1:
blockOptionType += 1;
break;
case kOptionBlock2:
blockOptionType += 2;
break;
case kOptionSize1:
// ToDo: wait for method to read uint option values
totalTransferSize = 0;
break;
default:
break;
}
SuccessOrExit(error = iterator.Advance());
}
curUriPath[0] = kNullChar;
for (const ResourceBlockWise &resource : mBlockWiseResources)
{
if (!StringMatch(resource.GetUriPath(), aUriPath))
{
continue;
}
if ((resource.mReceiveHook != nullptr || resource.mTransmitHook != nullptr) && blockOptionType != 0)
{
switch (blockOptionType)
{
case 1:
if (resource.mReceiveHook != nullptr)
{
switch (ProcessBlock1Request(aMessage, aMessageInfo, resource, totalTransferSize))
{
case kErrorNone:
resource.HandleRequest(aMessage, aMessageInfo);
OT_FALL_THROUGH;
case kErrorBusy:
error = kErrorNone;
break;
case kErrorNoBufs:
IgnoreError(SendHeaderResponse(kCodeRequestTooLarge, aMessage, aMessageInfo));
error = kErrorDrop;
break;
case kErrorNoFrameReceived:
IgnoreError(SendHeaderResponse(kCodeRequestIncomplete, aMessage, aMessageInfo));
error = kErrorDrop;
break;
default:
IgnoreError(SendHeaderResponse(kCodeInternalError, aMessage, aMessageInfo));
error = kErrorDrop;
break;
}
}
break;
case 2:
if (resource.mTransmitHook != nullptr)
{
if ((error = ProcessBlock2Request(aMessage, aMessageInfo, resource)) != kErrorNone)
{
IgnoreError(SendHeaderResponse(kCodeInternalError, aMessage, aMessageInfo));
error = kErrorDrop;
}
}
break;
}
aDidHandle = true;
ExitNow();
}
else
{
resource.HandleRequest(aMessage, aMessageInfo);
error = kErrorNone;
aDidHandle = true;
ExitNow();
}
}
exit:
return error;
}
void CoapBase::FreeLastBlockResponse(void)
{
if (mLastResponse != nullptr)
+8
View File
@@ -812,6 +812,14 @@ private:
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
Error ProcessBlockwiseSend(Message &aMessage, BlockwiseTransmitHook aTransmitHook, void *aContext);
Error ProcessBlockwiseResponse(Message &aResponse,
const Ip6::MessageInfo &aMessageInfo,
Message &aRequest,
const Metadata &aMetadata);
Error ProcessBlockwiseRequest(Message &aMessage,
const Ip6::MessageInfo &aMessageInfo,
Message::UriPathStringBuffer &aUriPath,
bool &aDidHandle);
void FreeLastBlockResponse(void);
Error CacheLastBlockResponse(Message *aResponse);
Error PrepareNextBlockRequest(Message::BlockType aType,
+1 -1
View File
@@ -246,7 +246,7 @@ exit:
return error;
}
Error Message::ReadUriPathOptions(char (&aUriPath)[kMaxReceivedUriPath + 1]) const
Error Message::ReadUriPathOptions(UriPathStringBuffer &aUriPath) const
{
char *curUriPath = aUriPath;
Error error = kErrorNone;
+5 -4
View File
@@ -192,6 +192,8 @@ public:
typedef ot::Coap::Type Type; ///< CoAP Type.
typedef ot::Coap::Code Code; ///< CoAP Code.
typedef char UriPathStringBuffer[kMaxReceivedUriPath + 1]; ///< Buffer to store a received URI Path string.
/**
* CoAP Block1/Block2 Types
*/
@@ -457,15 +459,14 @@ public:
Error AppendUriPathOptions(const char *aUriPath);
/**
* Reads the Uri-Path options and constructs the URI path in the buffer referenced by @p `aUriPath`.
* Reads the Uri-Path options and constructs the URI path in the buffer referenced by @p aUriPath.
*
* @param[in] aUriPath A reference to the buffer for storing URI path.
* NOTE: The buffer size must be `kMaxReceivedUriPath + 1`.
* @param[out] aUriPath A reference to the buffer to output the read URI path.
*
* @retval kErrorNone Successfully read the Uri-Path options.
* @retval kErrorParse CoAP Option header not well-formed.
*/
Error ReadUriPathOptions(char (&aUriPath)[kMaxReceivedUriPath + 1]) const;
Error ReadUriPathOptions(UriPathStringBuffer &aUriPath) const;
/**
* Appends a Uri-Query option.