Skip to content

Commit

Permalink
Add IM timed request builder/parser (project-chip#8264)
Browse files Browse the repository at this point in the history
  • Loading branch information
yunhanw-google authored and Nikita committed Sep 23, 2021
1 parent 02a090e commit 7981ba6
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/app/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ static_library("app") {
"MessageDef/StatusElement.h",
"MessageDef/SubscribeRequest.cpp",
"MessageDef/SubscribeResponse.cpp",
"MessageDef/TimedRequest.cpp",
"MessageDef/WriteRequest.cpp",
"MessageDef/WriteResponse.cpp",
"ReadClient.cpp",
Expand Down
103 changes: 103 additions & 0 deletions src/app/MessageDef/TimedRequest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
*
* Copyright (c) 2021 Project CHIP Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "TimedRequest.h"
#include "MessageDefHelper.h"

namespace chip {
namespace app {
CHIP_ERROR TimedRequest::Parser::Init(const chip::TLV::TLVReader & aReader)
{
// make a copy of the reader here
mReader.Init(aReader);
VerifyOrReturnLogError(chip::TLV::kTLVType_Structure == mReader.GetType(), CHIP_ERROR_WRONG_TLV_TYPE);
ReturnLogErrorOnFailure(mReader.EnterContainer(mOuterContainerType));
return CHIP_NO_ERROR;
}

#if CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK
CHIP_ERROR TimedRequest::Parser::CheckSchemaValidity() const
{
CHIP_ERROR err = CHIP_NO_ERROR;
uint16_t TagPresenceMask = 0;
chip::TLV::TLVReader reader;
PRETTY_PRINT("TimedRequest =");
PRETTY_PRINT("{");

// make a copy of the reader
reader.Init(mReader);

while (CHIP_NO_ERROR == (err = reader.Next()))
{
VerifyOrReturnLogError(chip::TLV::IsContextTag(reader.GetTag()), CHIP_ERROR_INVALID_TLV_TAG);
switch (chip::TLV::TagNumFromTag(reader.GetTag()))
{
case kCsTag_TimeoutMs:
VerifyOrReturnLogError(!(TagPresenceMask & (1 << kCsTag_TimeoutMs)), CHIP_ERROR_INVALID_TLV_TAG);
TagPresenceMask |= (1 << kCsTag_TimeoutMs);
VerifyOrReturnLogError(chip::TLV::kTLVType_UnsignedInteger == reader.GetType(), CHIP_ERROR_WRONG_TLV_TYPE);
#if CHIP_DETAIL_LOGGING
{
uint16_t timeout;
ReturnLogErrorOnFailure(reader.Get(timeout));
PRETTY_PRINT("\tTimeoutMs = 0x%" PRIx8 ",", timeout);
}
#endif // CHIP_DETAIL_LOGGING
break;
default:
ReturnLogErrorOnFailure(CHIP_ERROR_INVALID_TLV_TAG);
}
}
PRETTY_PRINT("}");
PRETTY_PRINT("");

if (CHIP_END_OF_TLV == err)
{
const uint16_t RequiredFields = (1 << kCsTag_TimeoutMs);

if ((TagPresenceMask & RequiredFields) == RequiredFields)
{
err = CHIP_NO_ERROR;
}
}
ReturnLogErrorOnFailure(err);
return reader.ExitContainer(mOuterContainerType);
}
#endif // CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK

CHIP_ERROR TimedRequest::Parser::GetTimeoutMs(uint16_t * const apTimeoutMs) const
{
return GetUnsignedInteger(kCsTag_TimeoutMs, apTimeoutMs);
}

CHIP_ERROR TimedRequest::Builder::Init(chip::TLV::TLVWriter * const apWriter)
{
return InitAnonymousStructure(apWriter);
}

TimedRequest::Builder & TimedRequest::Builder::TimeoutMs(const uint16_t aTimeoutMs)
{
// skip if error has already been set
if (mError == CHIP_NO_ERROR)
{
mError = mpWriter->Put(chip::TLV::ContextTag(kCsTag_TimeoutMs), aTimeoutMs);
ChipLogFunctError(mError);
}
EndOfContainer();
return *this;
}
} // namespace app
} // namespace chip
82 changes: 82 additions & 0 deletions src/app/MessageDef/TimedRequest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
*
* Copyright (c) 2021 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once
#include "Builder.h"
#include "Parser.h"
#include <app/AppBuildConfig.h>
#include <app/util/basic-types.h>
#include <core/CHIPCore.h>
#include <core/CHIPTLV.h>
#include <support/CodeUtils.h>
#include <support/logging/CHIPLogging.h>

namespace chip {
namespace app {
namespace TimedRequest {
enum
{
kCsTag_TimeoutMs = 0,
};

class Parser : public chip::app::Parser
{
public:
/**
* @param [in] aReader A pointer to a TLVReader, which should point to the beginning of this response
*/
CHIP_ERROR Init(const chip::TLV::TLVReader & aReader);
#if CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK
/**
* @brief Roughly verify the message is correctly formed
* 1) all mandatory tags are present
* 2) all elements have expected data type
* 3) any tag can only appear once
* 4) At the top level of the structure, unknown tags are ignored for forward compatibility
* @note The main use of this function is to print out what we're
* receiving during protocol development and debugging.
* The encoding rule has changed in IM encoding spec so this
* check is only "roughly" conformant now.
*
* @return #CHIP_NO_ERROR on success
*/
CHIP_ERROR CheckSchemaValidity() const;
#endif

/**
* @brief Get Timeout value. Next() must be called before accessing them.
*
* @return #CHIP_NO_ERROR on success
* #CHIP_END_OF_TLV if there is no such element
*/
CHIP_ERROR GetTimeoutMs(uint16_t * const apTimeoutMs) const;
};

class Builder : public chip::app::Builder
{
public:
CHIP_ERROR Init(chip::TLV::TLVWriter * const apWriter);

/**
* @brief Timeout value, sent by a client to a server, if there is a preceding successful Timed Request action,
* the following action SHALL be received before the end of the Timeout interval.
*/
TimedRequest::Builder & TimeoutMs(const uint16_t aTimeoutMs);
};
} // namespace TimedRequest
} // namespace app
} // namespace chip
50 changes: 50 additions & 0 deletions src/app/tests/TestMessageDef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <app/MessageDef/ReportData.h>
#include <app/MessageDef/SubscribeRequest.h>
#include <app/MessageDef/SubscribeResponse.h>
#include <app/MessageDef/TimedRequest.h>
#include <app/MessageDef/WriteRequest.h>
#include <app/MessageDef/WriteResponse.h>
#include <core/CHIPTLVDebug.hpp>
Expand Down Expand Up @@ -1007,6 +1008,35 @@ void ParseSubscribeResponse(nlTestSuite * apSuite, chip::TLV::TLVReader & aReade
NL_TEST_ASSERT(apSuite, finalSyncIntervalSeconds == 1 && err == CHIP_NO_ERROR);
}

void BuildTimedRequest(nlTestSuite * apSuite, chip::TLV::TLVWriter & aWriter)
{
CHIP_ERROR err = CHIP_NO_ERROR;
TimedRequest::Builder timedRequestBuilder;

err = timedRequestBuilder.Init(&aWriter);
NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);

timedRequestBuilder.TimeoutMs(1);
NL_TEST_ASSERT(apSuite, timedRequestBuilder.GetError() == CHIP_NO_ERROR);
}

void ParseTimedRequest(nlTestSuite * apSuite, chip::TLV::TLVReader & aReader)
{
CHIP_ERROR err = CHIP_NO_ERROR;

TimedRequest::Parser timedRequestarser;
uint16_t timeout = 0;

err = timedRequestarser.Init(aReader);
NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
#if CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK
err = timedRequestarser.CheckSchemaValidity();
NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
#endif
err = timedRequestarser.GetTimeoutMs(&timeout);
NL_TEST_ASSERT(apSuite, timeout == 1 && err == CHIP_NO_ERROR);
}

void AttributePathTest(nlTestSuite * apSuite, void * apContext)
{
CHIP_ERROR err = CHIP_NO_ERROR;
Expand Down Expand Up @@ -1511,6 +1541,25 @@ void SubscribeResponseTest(nlTestSuite * apSuite, void * apContext)
ParseSubscribeResponse(apSuite, reader);
}

void TimedRequestTest(nlTestSuite * apSuite, void * apContext)
{
CHIP_ERROR err = CHIP_NO_ERROR;
chip::System::PacketBufferTLVWriter writer;
chip::System::PacketBufferTLVReader reader;
writer.Init(chip::System::PacketBufferHandle::New(chip::System::PacketBuffer::kMaxSize));
BuildTimedRequest(apSuite, writer);
chip::System::PacketBufferHandle buf;
err = writer.Finalize(&buf);
NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);

DebugPrettyPrint(buf);

reader.Init(std::move(buf));
err = reader.Next();
NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
ParseTimedRequest(apSuite, reader);
}

void CheckPointRollbackTest(nlTestSuite * apSuite, void * apContext)
{
CHIP_ERROR err = CHIP_NO_ERROR;
Expand Down Expand Up @@ -1594,6 +1643,7 @@ const nlTest sTests[] =
NL_TEST_DEF("WriteResponseTest", WriteResponseTest),
NL_TEST_DEF("SubscribeRequestTest", SubscribeRequestTest),
NL_TEST_DEF("SubscribeResponseTest", SubscribeResponseTest),
NL_TEST_DEF("TimedRequestTest", TimedRequestTest),
NL_TEST_DEF("CheckPointRollbackTest", CheckPointRollbackTest),
NL_TEST_SENTINEL()
};
Expand Down

0 comments on commit 7981ba6

Please sign in to comment.