Skip to content

Commit 2765353

Browse files
Damian-Nordicrestyled-commits
authored andcommitted
[bdx] Extract functions for building and parsing URI and add unit tests (#12784)
* [bdx] Add utility for parsing and making URI * Restyled by whitespace Co-authored-by: Restyled.io <commits@restyled.io>
1 parent 48b4b1e commit 2765353

File tree

11 files changed

+267
-95
lines changed

11 files changed

+267
-95
lines changed

examples/ota-provider-app/ota-provider-common/OTAProviderExample.cpp

+2-18
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <crypto/RandUtils.h>
2727
#include <lib/core/CHIPTLV.h>
2828
#include <lib/support/CHIPMemString.h>
29+
#include <protocols/bdx/BdxUri.h>
2930

3031
#include <string.h>
3132

@@ -63,23 +64,6 @@ void GenerateUpdateToken(uint8_t * buf, size_t bufSize)
6364
}
6465
}
6566

66-
bool GenerateBdxUri(NodeId nodeId, CharSpan fileDesignator, MutableCharSpan outUri)
67-
{
68-
static constexpr char bdxPrefix[] = "bdx://";
69-
size_t nodeIdHexStrLen = sizeof(nodeId) * 2;
70-
size_t expectedLength = strlen(bdxPrefix) + nodeIdHexStrLen + 1 + fileDesignator.size();
71-
72-
if (expectedLength >= outUri.size())
73-
{
74-
return false;
75-
}
76-
77-
size_t written = static_cast<size_t>(snprintf(outUri.data(), outUri.size(), "%s" ChipLogFormatX64 "/%s", bdxPrefix,
78-
ChipLogValueX64(nodeId), fileDesignator.data()));
79-
80-
return expectedLength == written;
81-
}
82-
8367
OTAProviderExample::OTAProviderExample()
8468
{
8569
memset(mOTAFilePath, 0, kFilepathBufLen);
@@ -128,7 +112,7 @@ EmberAfStatus OTAProviderExample::HandleQueryImage(chip::app::CommandHandler * c
128112

129113
// Only doing BDX transport for now
130114
MutableCharSpan uri(uriBuf, kUriMaxLen);
131-
GenerateBdxUri(nodeId, CharSpan(mOTAFilePath, strlen(mOTAFilePath)), uri);
115+
chip::bdx::MakeURI(nodeId, CharSpan(mOTAFilePath, strlen(mOTAFilePath)), uri);
132116
ChipLogDetail(SoftwareUpdate, "Generated URI: %.*s", static_cast<int>(uri.size()), uri.data());
133117
}
134118

src/app/clusters/ota-requestor/BDXDownloader.cpp

-59
Original file line numberDiff line numberDiff line change
@@ -217,63 +217,4 @@ CHIP_ERROR BDXDownloader::HandleBdxEvent(const chip::bdx::TransferSession::Outpu
217217
return CHIP_NO_ERROR;
218218
}
219219

220-
// TODO: Add unit tests for parsing BDX URI
221-
CHIP_ERROR BDXDownloader::ParseBdxUri(CharSpan uri, NodeId & nodeId, MutableCharSpan fileDesignator)
222-
{
223-
// Check against minimum length of a valid BDX URI
224-
if (uri.size() < kValidBdxUriMinLen)
225-
{
226-
return CHIP_ERROR_INVALID_STRING_LENGTH;
227-
}
228-
229-
uint8_t readValue[kUriMaxLen];
230-
Encoding::LittleEndian::Reader uriReader(reinterpret_cast<const uint8_t *>(uri.data()), uri.size());
231-
232-
// Check the scheme field matches the BDX prefix
233-
memset(readValue, 0, sizeof(readValue));
234-
ReturnErrorOnFailure(uriReader.ReadBytes(readValue, sizeof(bdxPrefix)).StatusCode());
235-
ByteSpan expectedScheme(bdxPrefix, sizeof(bdxPrefix));
236-
ByteSpan actualScheme(readValue, sizeof(bdxPrefix));
237-
if (!expectedScheme.data_equal(actualScheme))
238-
{
239-
return CHIP_ERROR_INVALID_SCHEME_PREFIX;
240-
}
241-
242-
// Extract the node ID from the authority field
243-
memset(readValue, 0, sizeof(readValue));
244-
ReturnErrorOnFailure(uriReader.ReadBytes(readValue, kNodeIdHexStringLen).StatusCode());
245-
uint8_t buffer[kNodeIdHexStringLen];
246-
if (Encoding::HexToBytes(reinterpret_cast<const char *>(readValue), kNodeIdHexStringLen, buffer, kNodeIdHexStringLen) == 0)
247-
{
248-
return CHIP_ERROR_INVALID_DESTINATION_NODE_ID;
249-
}
250-
nodeId = Encoding::BigEndian::Get64(buffer);
251-
if (!IsOperationalNodeId(nodeId))
252-
{
253-
return CHIP_ERROR_INVALID_DESTINATION_NODE_ID;
254-
}
255-
256-
// Verify the separator between authority and path fields
257-
memset(readValue, 0, sizeof(readValue));
258-
ReturnErrorOnFailure(uriReader.ReadBytes(readValue, sizeof(bdxSeparator)).StatusCode());
259-
ByteSpan expectedSeparator(bdxSeparator, sizeof(bdxSeparator));
260-
ByteSpan actualSeparator(readValue, sizeof(bdxSeparator));
261-
if (!expectedSeparator.data_equal(actualSeparator))
262-
{
263-
return CHIP_ERROR_MISSING_URI_SEPARATOR;
264-
}
265-
266-
// Extract file designator from the path field
267-
size_t fileDesignatorLength = uriReader.Remaining();
268-
memset(readValue, 0, sizeof(readValue));
269-
ReturnErrorOnFailure(uriReader.ReadBytes(readValue, fileDesignatorLength).StatusCode());
270-
size_t written = static_cast<size_t>(snprintf(fileDesignator.data(), fileDesignator.size(), "%s", readValue));
271-
if (written != fileDesignatorLength)
272-
{
273-
return CHIP_ERROR_INTERNAL;
274-
}
275-
276-
return CHIP_NO_ERROR;
277-
}
278-
279220
} // namespace chip

src/app/clusters/ota-requestor/BDXDownloader.h

-12
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,6 @@
3333

3434
namespace chip {
3535

36-
// Constants for BDX URI parsing
37-
constexpr uint8_t bdxPrefix[] = { 'b', 'd', 'x', ':', '/', '/' };
38-
constexpr uint8_t bdxSeparator[] = { '/' };
39-
constexpr uint8_t kValidBdxUriMinLen = 24;
40-
constexpr uint8_t kNodeIdHexStringLen = 16;
41-
constexpr size_t kUriMaxLen = 256;
42-
4336
class BDXDownloader : public chip::OTADownloader
4437
{
4538
public:
@@ -72,11 +65,6 @@ class BDXDownloader : public chip::OTADownloader
7265
CHIP_ERROR FetchNextData() override;
7366
// TODO: override SkipData
7467

75-
/**
76-
* Validate the URI and parse the BDX URI for various fields
77-
*/
78-
CHIP_ERROR ParseBdxUri(CharSpan uri, NodeId & nodeId, MutableCharSpan fileDesignator);
79-
8068
private:
8169
void PollTransferSession();
8270
CHIP_ERROR HandleBdxEvent(const chip::bdx::TransferSession::OutputEvent & outEvent);

src/app/clusters/ota-requestor/OTARequestor.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <app-common/zap-generated/attributes/Accessors.h>
2424
#include <lib/core/CHIPEncoding.h>
2525
#include <platform/CHIPDeviceLayer.h>
26+
#include <protocols/bdx/BdxUri.h>
2627
#include <zap-generated/CHIPClusters.h>
2728

2829
#include "BDXDownloader.h"
@@ -114,10 +115,9 @@ void OTARequestor::OnQueryImageResponse(void * context, const QueryImageResponse
114115
VerifyOrReturn(response.imageURI.HasValue(), ChipLogError(SoftwareUpdate, "Update is available but no image URI present"));
115116

116117
// Parse out the provider node ID and file designator from the image URI
117-
NodeId nodeId = kUndefinedNodeId;
118-
char fileDesignatorBuffer[kUriMaxLen] = { 0 };
119-
MutableCharSpan fileDesignator(fileDesignatorBuffer, kUriMaxLen);
120-
CHIP_ERROR err = requestorCore->mBdxDownloader->ParseBdxUri(response.imageURI.Value(), nodeId, fileDesignator);
118+
NodeId nodeId = kUndefinedNodeId;
119+
CharSpan fileDesignator;
120+
CHIP_ERROR err = bdx::ParseURI(response.imageURI.Value(), nodeId, fileDesignator);
121121
VerifyOrReturn(err == CHIP_NO_ERROR,
122122
ChipLogError(SoftwareUpdate, "Parse BDX image URI (%.*s) returned err=%" CHIP_ERROR_FORMAT,
123123
static_cast<int>(response.imageURI.Value().size()), response.imageURI.Value().data(),

src/lib/support/Span.h

+6
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ class Span
8888
return Span(mDataBuf + offset, length);
8989
}
9090

91+
Span SubSpan(size_t offset) const
92+
{
93+
VerifyOrDie(offset <= mDataLen);
94+
return Span(mDataBuf + offset, mDataLen - offset);
95+
}
96+
9197
// Allow reducing the size of a span.
9298
void reduce_size(size_t new_size)
9399
{

src/lib/support/tests/TestSpan.cpp

+26-2
Original file line numberDiff line numberDiff line change
@@ -247,12 +247,36 @@ static void TestSpanOfPointers(nlTestSuite * inSuite, void * inContext)
247247
NL_TEST_ASSERT(inSuite, s3.data_equal(s5));
248248
}
249249

250+
static void TestSubSpan(nlTestSuite * inSuite, void * inContext)
251+
{
252+
uint8_t array[16];
253+
ByteSpan span(array);
254+
255+
NL_TEST_ASSERT(inSuite, span.data() == &array[0]);
256+
NL_TEST_ASSERT(inSuite, span.size() == 16);
257+
258+
ByteSpan subspan = span.SubSpan(1, 14);
259+
NL_TEST_ASSERT(inSuite, subspan.data() == &array[1]);
260+
NL_TEST_ASSERT(inSuite, subspan.size() == 14);
261+
262+
subspan = span.SubSpan(1, 0);
263+
NL_TEST_ASSERT(inSuite, subspan.size() == 0);
264+
265+
subspan = span.SubSpan(10);
266+
NL_TEST_ASSERT(inSuite, subspan.data() == &array[10]);
267+
NL_TEST_ASSERT(inSuite, subspan.size() == 6);
268+
269+
subspan = span.SubSpan(16);
270+
NL_TEST_ASSERT(inSuite, subspan.size() == 0);
271+
}
272+
250273
#define NL_TEST_DEF_FN(fn) NL_TEST_DEF("Test " #fn, fn)
251274
/**
252275
* Test Suite. It lists all the test functions.
253276
*/
254-
static const nlTest sTests[] = { NL_TEST_DEF_FN(TestByteSpan), NL_TEST_DEF_FN(TestMutableByteSpan),
255-
NL_TEST_DEF_FN(TestFixedByteSpan), NL_TEST_DEF_FN(TestSpanOfPointers), NL_TEST_SENTINEL() };
277+
static const nlTest sTests[] = { NL_TEST_DEF_FN(TestByteSpan), NL_TEST_DEF_FN(TestMutableByteSpan),
278+
NL_TEST_DEF_FN(TestFixedByteSpan), NL_TEST_DEF_FN(TestSpanOfPointers),
279+
NL_TEST_DEF_FN(TestSubSpan), NL_TEST_SENTINEL() };
256280

257281
int TestSpan(void)
258282
{

src/protocols/bdx/BUILD.gn

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ static_library("bdx") {
2222
"BdxMessages.h",
2323
"BdxTransferSession.cpp",
2424
"BdxTransferSession.h",
25+
"BdxUri.cpp",
26+
"BdxUri.h",
2527
"TransferFacilitator.cpp",
2628
"TransferFacilitator.h",
2729
]

src/protocols/bdx/BdxUri.cpp

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
*
3+
* Copyright (c) 2021 Project CHIP Authors
4+
* All rights reserved.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
#include "BdxUri.h"
20+
21+
#include <lib/core/CHIPEncoding.h>
22+
#include <lib/core/CHIPSafeCasts.h>
23+
#include <lib/support/BufferWriter.h>
24+
#include <lib/support/BytesToHex.h>
25+
#include <lib/support/CodeUtils.h>
26+
27+
#include <cstring>
28+
29+
namespace chip {
30+
namespace bdx {
31+
32+
constexpr size_t kSchemeLen = sizeof(kScheme) - 1;
33+
constexpr size_t kHostPos = kSchemeLen;
34+
constexpr size_t kHostLen = sizeof(NodeId) * 2;
35+
constexpr size_t kHostEnd = kHostPos + kHostLen;
36+
constexpr size_t kSeparatorLen = 1;
37+
constexpr size_t kMinUriLen = kHostEnd + kSeparatorLen + 1 /* file-designator */;
38+
39+
static_assert(sizeof(NodeId) == sizeof(uint64_t), "The code below assumes NodeId is uint64_t");
40+
41+
CHIP_ERROR ParseURI(CharSpan uri, NodeId & nodeId, CharSpan & fileDesignator)
42+
{
43+
VerifyOrReturnError(uri.size() >= kMinUriLen, CHIP_ERROR_INVALID_STRING_LENGTH);
44+
VerifyOrReturnError(memcmp(uri.data(), kScheme, kSchemeLen) == 0, CHIP_ERROR_INVALID_SCHEME_PREFIX);
45+
46+
uint8_t nodeIdBytes[sizeof(NodeId)];
47+
VerifyOrReturnError(Encoding::HexToBytes(uri.data() + kHostPos, kHostLen, nodeIdBytes, sizeof(nodeIdBytes)) ==
48+
sizeof(nodeIdBytes),
49+
CHIP_ERROR_INVALID_DESTINATION_NODE_ID);
50+
51+
nodeId = Encoding::BigEndian::Get64(nodeIdBytes);
52+
VerifyOrReturnError(IsOperationalNodeId(nodeId), CHIP_ERROR_INVALID_DESTINATION_NODE_ID);
53+
VerifyOrReturnError(uri.data()[kHostEnd] == '/', CHIP_ERROR_MISSING_URI_SEPARATOR);
54+
55+
fileDesignator = uri.SubSpan(kHostEnd + kSeparatorLen);
56+
57+
return CHIP_NO_ERROR;
58+
}
59+
60+
CHIP_ERROR MakeURI(NodeId nodeId, CharSpan fileDesignator, MutableCharSpan & uri)
61+
{
62+
VerifyOrReturnError(fileDesignator.size() > 0, CHIP_ERROR_INVALID_STRING_LENGTH);
63+
64+
uint8_t nodeIdBytes[sizeof(NodeId)];
65+
Encoding::BigEndian::Put64(nodeIdBytes, nodeId);
66+
67+
char nodeIdHex[sizeof(NodeId) * 2];
68+
ReturnErrorOnFailure(Encoding::BytesToUppercaseHexBuffer(nodeIdBytes, sizeof(nodeIdBytes), nodeIdHex, sizeof(nodeIdHex)));
69+
70+
Encoding::BufferWriter writer(Uint8::from_char(uri.data()), uri.size());
71+
writer.Put(kScheme, kSchemeLen);
72+
writer.Put(nodeIdHex, sizeof(nodeIdHex));
73+
writer.Put("/");
74+
writer.Put(fileDesignator.data(), fileDesignator.size());
75+
76+
VerifyOrReturnError(writer.Fit(), CHIP_ERROR_BUFFER_TOO_SMALL);
77+
uri.reduce_size(writer.WritePos());
78+
79+
return CHIP_NO_ERROR;
80+
}
81+
82+
} // namespace bdx
83+
} // namespace chip

src/protocols/bdx/BdxUri.h

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
*
3+
* Copyright (c) 2021 Project CHIP Authors
4+
* All rights reserved.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
#include <lib/core/NodeId.h>
20+
#include <lib/support/Span.h>
21+
22+
namespace chip {
23+
namespace bdx {
24+
25+
constexpr const char kScheme[] = "bdx://";
26+
27+
CHIP_ERROR ParseURI(CharSpan uri, NodeId & nodeId, CharSpan & fileDesignator);
28+
CHIP_ERROR MakeURI(NodeId nodeId, CharSpan fileDesignator, MutableCharSpan & uri);
29+
30+
} // namespace bdx
31+
} // namespace chip

src/protocols/bdx/tests/BUILD.gn

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ chip_test_suite("tests") {
2525
test_sources = [
2626
"TestBdxMessages.cpp",
2727
"TestBdxTransferSession.cpp",
28+
"TestBdxUri.cpp",
2829
]
2930

3031
public_deps = [

0 commit comments

Comments
 (0)