Skip to content

Commit

Permalink
[chip-tool] Add interactive server command to allow chip-tool to exec…
Browse files Browse the repository at this point in the history
…ute commands received over websockets (#24304)
  • Loading branch information
vivien-apple authored and pull[bot] committed Sep 22, 2023
1 parent 86a2954 commit 1083896
Show file tree
Hide file tree
Showing 9 changed files with 269 additions and 9 deletions.
8 changes: 7 additions & 1 deletion examples/chip-tool/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,12 @@ static_library("chip-tool-utils") {
deps = []

if (config_use_interactive_mode) {
sources += [ "commands/interactive/InteractiveCommands.cpp" ]
sources += [
"commands/interactive/InteractiveCommands.cpp",
"commands/interactive/WebSocketServer.cpp",
"commands/interactive/WebSocketServer.h",
"commands/interactive/WebSocketServerDelegate.h",
]
deps += [ "${editline_root}:editline" ]
}

Expand All @@ -108,6 +113,7 @@ static_library("chip-tool-utils") {
"${chip_root}/src/platform",
"${chip_root}/third_party/inipp",
"${chip_root}/third_party/jsoncpp",
"${chip_root}/third_party/libwebsockets",
]

public_configs = [ ":config" ]
Expand Down
1 change: 1 addition & 0 deletions examples/chip-tool/commands/interactive/Commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ void registerCommandsInteractive(Commands & commands, CredentialIssuerCommands *
commands_list clusterCommands = {
#if CONFIG_USE_INTERACTIVE_MODE
make_unique<InteractiveStartCommand>(&commands, credsIssuerConfig),
make_unique<InteractiveServerCommand>(&commands, credsIssuerConfig),
#endif // CONFIG_USE_INTERACTIVE_MODE
};

Expand Down
17 changes: 15 additions & 2 deletions examples/chip-tool/commands/interactive/InteractiveCommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ void ENFORCE_FORMAT(3, 0) LoggingCallback(const char * module, uint8_t category,
chip::Logging::Platform::LogV(module, category, msg, args);
ClearLine();
}
} // namespace

char * GetCommand(char * command)
{
Expand All @@ -64,6 +63,20 @@ char * GetCommand(char * command)

return command;
}
} // namespace

CHIP_ERROR InteractiveServerCommand::RunCommand()
{
ReturnErrorOnFailure(mWebSocketServer.Run(mPort, this));

SetCommandExitStatus(CHIP_NO_ERROR);
return CHIP_NO_ERROR;
}

bool InteractiveServerCommand::OnWebSocketMessageReceived(char * msg)
{
return ParseCommand(msg);
}

CHIP_ERROR InteractiveStartCommand::RunCommand()
{
Expand Down Expand Up @@ -93,7 +106,7 @@ CHIP_ERROR InteractiveStartCommand::RunCommand()
return CHIP_NO_ERROR;
}

bool InteractiveStartCommand::ParseCommand(char * command)
bool InteractiveCommand::ParseCommand(char * command)
{
if (strcmp(command, kInteractiveModeStopCommand) == 0)
{
Expand Down
43 changes: 37 additions & 6 deletions examples/chip-tool/commands/interactive/InteractiveCommands.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,53 @@
#include "../common/CHIPCommand.h"
#include "../common/Commands.h"

#include "InteractiveCommands.h"
#include "WebSocketServer.h"

class Commands;

class InteractiveStartCommand : public CHIPCommand
class InteractiveCommand : public CHIPCommand
{
public:
InteractiveCommand(const char * name, Commands * commandsHandler, CredentialIssuerCommands * credsIssuerConfig) :
CHIPCommand(name, credsIssuerConfig), mHandler(commandsHandler)
{}

/////////// CHIPCommand Interface /////////
chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(0); }

bool ParseCommand(char * command);

private:
Commands * mHandler = nullptr;
};

class InteractiveStartCommand : public InteractiveCommand
{
public:
InteractiveStartCommand(Commands * commandsHandler, CredentialIssuerCommands * credsIssuerConfig) :
CHIPCommand("start", credsIssuerConfig), mHandler(commandsHandler)
InteractiveCommand("start", commandsHandler, credsIssuerConfig)
{}

/////////// CHIPCommand Interface /////////
CHIP_ERROR RunCommand() override;
};

chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(0); }
class InteractiveServerCommand : public InteractiveCommand, public WebSocketServerDelegate
{
public:
InteractiveServerCommand(Commands * commandsHandler, CredentialIssuerCommands * credsIssuerConfig) :
InteractiveCommand("server", commandsHandler, credsIssuerConfig)
{
AddArgument("port", 0, UINT16_MAX, &mPort, "Port the websocket will listen to. Defaults to 9002.");
}

/////////// CHIPCommand Interface /////////
CHIP_ERROR RunCommand() override;

/////////// WebSocketServerDelegate Interface /////////
bool OnWebSocketMessageReceived(char * msg) override;

private:
bool ParseCommand(char * command);
Commands * mHandler = nullptr;
WebSocketServer mWebSocketServer;
chip::Optional<uint16_t> mPort;
};
147 changes: 147 additions & 0 deletions examples/chip-tool/commands/interactive/WebSocketServer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* Copyright (c) 2023 Project CHIP Authors
* All rights reserved.
*
* 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 "WebSocketServer.h"

constexpr uint16_t kDefaultWebSocketServerPort = 9002;
constexpr uint16_t kMaxMessageBufferLen = 8192;

namespace {
void LogWebSocketCallbackReason(lws_callback_reasons reason)
{
#if CHIP_DETAIL_LOGGING
switch (reason)
{
case LWS_CALLBACK_GET_THREAD_ID:
ChipLogDetail(chipTool, "LWS_CALLBACK_GET_THREAD_ID");
break;
case LWS_CALLBACK_ADD_HEADERS:
ChipLogDetail(chipTool, "LWS_CALLBACK_ADD_HEADERS");
break;
case LWS_CALLBACK_PROTOCOL_INIT:
ChipLogDetail(chipTool, "LWS_CALLBACK_PROTOCOL_INIT");
break;
case LWS_CALLBACK_PROTOCOL_DESTROY:
ChipLogDetail(chipTool, "LWS_CALLBACK_PROTOCOL_DESTROY");
break;
case LWS_CALLBACK_HTTP:
ChipLogDetail(chipTool, "LWS_CALLBACK_HTTP");
break;
case LWS_CALLBACK_EVENT_WAIT_CANCELLED:
ChipLogDetail(chipTool, "LWS_CALLBACK_EVENT_WAIT_CANCELLED");
break;
case LWS_CALLBACK_CLIENT_WRITEABLE:
ChipLogDetail(chipTool, "LWS_CALLBACK_CLIENT_WRITEABLE");
break;
case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
ChipLogDetail(chipTool, "LWS_CALLBACK_FILTER_NETWORK_CONNECTION");
break;
case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
ChipLogDetail(chipTool, "LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION");
break;
case LWS_CALLBACK_WSI_CREATE:
ChipLogDetail(chipTool, "LWS_CALLBACK_WSI_CREATE");
break;
case LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED:
ChipLogDetail(chipTool, "LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED");
break;
case LWS_CALLBACK_HTTP_CONFIRM_UPGRADE:
ChipLogDetail(chipTool, "LWS_CALLBACK_HTTP_CONFIRM_UPGRADE");
break;
case LWS_CALLBACK_HTTP_BIND_PROTOCOL:
ChipLogDetail(chipTool, "LWS_CALLBACK_HTTP_BIND_PROTOCOL");
break;
case LWS_CALLBACK_ESTABLISHED:
ChipLogDetail(chipTool, "LWS_CALLBACK_ESTABLISHED");
break;
case LWS_CALLBACK_RECEIVE:
ChipLogDetail(chipTool, "LWS_CALLBACK_RECEIVE");
break;
case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE:
ChipLogDetail(chipTool, "LWS_CALLBACK_WS_PEER_INITIATED_CLOSE");
break;
case LWS_CALLBACK_WSI_DESTROY:
ChipLogDetail(chipTool, "LWS_CALLBACK_WSI_DESTROY");
break;
case LWS_CALLBACK_CLOSED:
ChipLogDetail(chipTool, "LWS_CALLBACK_CLOSED");
break;
case LWS_CALLBACK_SERVER_WRITEABLE:
ChipLogDetail(chipTool, "LWS_CALLBACK_SERVER_WRITEABLE");
break;
case LWS_CALLBACK_CLOSED_HTTP:
ChipLogDetail(chipTool, "LWS_CALLBACK_CLOSED_HTTP");
break;
default:
ChipLogError(chipTool, "Unknown reason: %d ", static_cast<int>(reason));
}
#endif // CHIP_DETAIL_LOGGING
}

static int OnWebSocketCallback(lws * wsi, lws_callback_reasons reason, void * user, void * in, size_t len)
{
LogWebSocketCallbackReason(reason);

if (LWS_CALLBACK_RECEIVE == reason)
{
char msg[kMaxMessageBufferLen + 1 /* for null byte */] = {};
VerifyOrDie(sizeof(msg) > len);
memcpy(msg, in, len);

auto protocol = lws_get_protocol(wsi);
auto delegate = static_cast<WebSocketServerDelegate *>(protocol->user);
if (nullptr == delegate)
{
ChipLogError(chipTool, "Failed to retrieve the server interactive context.");
return -1;
}

if (!delegate->OnWebSocketMessageReceived(msg))
{
auto context = lws_get_context(wsi);
lws_default_loop_exit(context);
}
}
else if (LWS_CALLBACK_CLIENT_ESTABLISHED == reason)
{
lws_callback_on_writable(wsi);
}

return 0;
}
} // namespace

CHIP_ERROR WebSocketServer::Run(chip::Optional<uint16_t> port, WebSocketServerDelegate * delegate)
{
VerifyOrReturnError(nullptr != delegate, CHIP_ERROR_INVALID_ARGUMENT);

lws_protocols protocols[] = { { "ws", OnWebSocketCallback, 0, 0, 0, delegate, 0 }, LWS_PROTOCOL_LIST_TERM };

lws_context_creation_info info;
memset(&info, 0, sizeof(info));
info.port = port.ValueOr(kDefaultWebSocketServerPort);
info.iface = nullptr;
info.pt_serv_buf_size = kMaxMessageBufferLen;
info.protocols = protocols;

auto context = lws_create_context(&info);
VerifyOrReturnError(nullptr != context, CHIP_ERROR_INTERNAL);

lws_context_default_loop_run_destroy(context);
return CHIP_NO_ERROR;
}
31 changes: 31 additions & 0 deletions examples/chip-tool/commands/interactive/WebSocketServer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2023 Project CHIP Authors
* All rights reserved.
*
* 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 "WebSocketServerDelegate.h"

#include <lib/core/Optional.h>
#include <lib/support/CHIPMem.h>
#include <libwebsockets.h>

class WebSocketServer
{
public:
CHIP_ERROR Run(chip::Optional<uint16_t> port, WebSocketServerDelegate * delegate);
};
28 changes: 28 additions & 0 deletions examples/chip-tool/commands/interactive/WebSocketServerDelegate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) 2023 Project CHIP Authors
* All rights reserved.
*
* 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 "WebSocketServerDelegate.h"

class WebSocketServerDelegate
{
public:
virtual ~WebSocketServerDelegate(){};
virtual bool OnWebSocketMessageReceived(char * msg) = 0;
};
1 change: 1 addition & 0 deletions third_party/libwebsockets/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ config("libwebsockets_config") {
"-Wno-shadow",
"-Wno-unreachable-code",
"-Wno-format-nonliteral",
"-Wno-implicit-fallthrough",
]

defines = []
Expand Down
2 changes: 2 additions & 0 deletions third_party/libwebsockets/lws_config_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@
*/

#pragma once

#define LWS_HAVE_SYS_RESOURCE_H

0 comments on commit 1083896

Please sign in to comment.