mirror of
https://gitee.com/openharmony/arkcompiler_toolchain
synced 2025-02-20 01:01:37 +00:00
!11 replace the boost websocket
Merge pull request !11 from huangfeijie/master
This commit is contained in:
commit
e33a2ffd0c
@ -82,10 +82,7 @@ ohos_shared_library("ark_debugger") {
|
||||
|
||||
ohos_source_set("ark_debugger_static") {
|
||||
deps = []
|
||||
defines = [
|
||||
"BOOST_ERROR_CODE_HEADER_ONLY",
|
||||
"BOOST_CLANG",
|
||||
]
|
||||
defines = []
|
||||
|
||||
defines += [ "ACE_LOG_TAG=\"ArkDebugger\"" ]
|
||||
|
||||
@ -94,11 +91,11 @@ ohos_source_set("ark_debugger_static") {
|
||||
}
|
||||
|
||||
include_dirs = [
|
||||
".",
|
||||
"//third_party/boost",
|
||||
"//third_party/boost/boost",
|
||||
"$toolchain_root",
|
||||
"$toolchain_root/websocket",
|
||||
]
|
||||
|
||||
deps += [ "$toolchain_root/websocket:websocket" ]
|
||||
sources = [
|
||||
"inspector.cpp",
|
||||
"library_loader.cpp",
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <iostream>
|
||||
#include <shared_mutex>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "log_wrapper.h"
|
||||
|
||||
@ -28,62 +29,29 @@ std::shared_mutex g_mutex;
|
||||
void WsServer::RunServer()
|
||||
{
|
||||
terminateExecution_ = false;
|
||||
try {
|
||||
tid_ = pthread_self();
|
||||
webSocket_ = std::make_unique<WebSocket>();
|
||||
#if !defined(OHOS_PLATFORM)
|
||||
constexpr int32_t DEFAULT_INSEPTOR_PORT = 9230;
|
||||
CommProtocol::endpoint endPoint(CommProtocol::v4(), DEFAULT_INSEPTOR_PORT);
|
||||
webSocket_->StartForSimulator();
|
||||
#else
|
||||
int appPid = getpid();
|
||||
std::string pidStr = std::to_string(appPid);
|
||||
std::string instanceIdStr("");
|
||||
/**
|
||||
* The old version of IDE is not compatible with the new images due to the connect server.
|
||||
* The First instance will use "pid" instead of "pid + instanceId" to avoid this.
|
||||
* If old version of IDE does not get the instanceId by connect server, it can still connect the debug server.
|
||||
*/
|
||||
if (instanceId_ != 0) {
|
||||
instanceIdStr = std::to_string(instanceId_);
|
||||
}
|
||||
std::string sockName = '\0' + pidStr + instanceIdStr + componentName_;
|
||||
LOGI("WsServer RunServer: %{public}d%{public}d%{public}s", appPid, instanceId_, componentName_.c_str());
|
||||
CommProtocol::endpoint endPoint(sockName);
|
||||
tid_ = pthread_self();
|
||||
int appPid = getpid();
|
||||
std::string pidStr = std::to_string(appPid);
|
||||
std::string instanceIdStr("");
|
||||
|
||||
if (instanceId_ != 0) {
|
||||
instanceIdStr = std::to_string(instanceId_);
|
||||
}
|
||||
std::string sockName = pidStr + instanceIdStr + componentName_;
|
||||
LOGI("WsServer RunServer: %{public}d%{public}s%{public}s", appPid, instanceIdStr.c_str(), componentName_.c_str());
|
||||
if (!webSocket_->StartWebSocket(sockName)) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
ioContext_ = std::make_unique<boost::asio::io_context>();
|
||||
CommProtocol::socket socket(*ioContext_);
|
||||
CommProtocol::acceptor acceptor(*ioContext_, endPoint);
|
||||
auto& connectFlag = connectState_;
|
||||
acceptor.async_accept(socket, [&connectFlag](const boost::system::error_code& error) {
|
||||
if (!error) {
|
||||
connectFlag = true;
|
||||
}
|
||||
});
|
||||
ioContext_->run();
|
||||
if (terminateExecution_ || !connectState_) {
|
||||
return;
|
||||
while (!terminateExecution_) {
|
||||
std::optional<std::string> message = webSocket_->Decode();
|
||||
if (message.has_value()) {
|
||||
wsOnMessage_(std::move(message.value()));
|
||||
}
|
||||
webSocket_ = std::unique_ptr<websocket::stream<CommProtocol::socket>>(
|
||||
std::make_unique<websocket::stream<CommProtocol::socket>>(std::move(socket)));
|
||||
webSocket_->accept();
|
||||
while (!terminateExecution_) {
|
||||
beast::flat_buffer buffer;
|
||||
boost::system::error_code error;
|
||||
webSocket_->read(buffer, error);
|
||||
if (error) {
|
||||
webSocket_.reset();
|
||||
return;
|
||||
}
|
||||
std::string message = boost::beast::buffers_to_string(buffer.data());
|
||||
LOGI("WsServer OnMessage: %{public}s", message.c_str());
|
||||
wsOnMessage_(std::move(message));
|
||||
}
|
||||
} catch (const beast::system_error& se) {
|
||||
if (se.code() != websocket::error::closed) {
|
||||
webSocket_.reset();
|
||||
LOGE("Error system_error, %{public}s", se.what());
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LOGE("Error exception, %{public}s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,13 +59,13 @@ void WsServer::StopServer()
|
||||
{
|
||||
LOGI("WsServer StopServer");
|
||||
terminateExecution_ = true;
|
||||
if (!connectState_) {
|
||||
ioContext_->stop();
|
||||
} else if (webSocket_ != nullptr) {
|
||||
boost::system::error_code error;
|
||||
webSocket_->close(websocket::close_code::normal, error);
|
||||
if (webSocket_ != nullptr) {
|
||||
webSocket_->Close();
|
||||
#if defined(OHOS_PLATFORM)
|
||||
pthread_join(tid_, nullptr);
|
||||
#endif
|
||||
webSocket_.reset();
|
||||
}
|
||||
pthread_join(tid_, nullptr);
|
||||
}
|
||||
|
||||
void WsServer::SendReply(const std::string& message) const
|
||||
@ -108,18 +76,6 @@ void WsServer::SendReply(const std::string& message) const
|
||||
return;
|
||||
}
|
||||
LOGI("WsServer SendReply: %{public}s", message.c_str());
|
||||
try {
|
||||
boost::beast::multi_buffer buffer;
|
||||
boost::beast::ostream(buffer) << message;
|
||||
|
||||
webSocket_->text(webSocket_->got_text());
|
||||
webSocket_->write(buffer.data());
|
||||
} catch (const beast::system_error& se) {
|
||||
if (se.code() != websocket::error::closed) {
|
||||
LOGE("SendReply Error system_error");
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LOGE("SendReply Error exception");
|
||||
}
|
||||
webSocket_->SendReply(message);
|
||||
}
|
||||
} // namespace OHOS::ArkCompiler::Toolchain
|
||||
|
@ -16,29 +16,12 @@
|
||||
#ifndef ARKCOMPILER_TOOLCHAIN_INSPECTOR_WS_SERVER_H
|
||||
#define ARKCOMPILER_TOOLCHAIN_INSPECTOR_WS_SERVER_H
|
||||
|
||||
#include <boost/asio/error.hpp>
|
||||
#if !defined(OHOS_PLATFORM)
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#else
|
||||
#include <boost/asio/local/stream_protocol.hpp>
|
||||
#endif
|
||||
#include <boost/beast/core.hpp>
|
||||
#include <boost/beast/websocket.hpp>
|
||||
#include "websocket.h"
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
namespace OHOS::ArkCompiler::Toolchain {
|
||||
namespace beast = boost::beast;
|
||||
namespace websocket = beast::websocket;
|
||||
#if !defined(OHOS_PLATFORM)
|
||||
using CommProtocol = boost::asio::ip::tcp;
|
||||
#else
|
||||
using CommProtocol = boost::asio::local::stream_protocol;
|
||||
#endif
|
||||
|
||||
class WsServer {
|
||||
public:
|
||||
WsServer(const std::string& component, const std::function<void(std::string&&)>& onMessage, int32_t instanceId)
|
||||
@ -50,14 +33,14 @@ public:
|
||||
void SendReply(const std::string& message) const;
|
||||
|
||||
private:
|
||||
std::atomic<bool> connectState_ {false};
|
||||
std::atomic<bool> terminateExecution_ { false };
|
||||
[[maybe_unused]] int32_t instanceId_ {0};
|
||||
#if defined(OHOS_PLATFORM)
|
||||
pthread_t tid_ {0};
|
||||
#endif
|
||||
std::string componentName_ {};
|
||||
std::function<void(std::string&&)> wsOnMessage_ {};
|
||||
std::unique_ptr<websocket::stream<CommProtocol::socket>> webSocket_ { nullptr };
|
||||
std::unique_ptr<boost::asio::io_context> ioContext_ { nullptr };
|
||||
std::unique_ptr<WebSocket> webSocket_ { nullptr };
|
||||
};
|
||||
} // namespace OHOS::ArkCompiler::Toolchain
|
||||
|
||||
|
100
websocket/BUILD.gn
Normal file
100
websocket/BUILD.gn
Normal file
@ -0,0 +1,100 @@
|
||||
# Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
# 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.
|
||||
|
||||
import("//arkcompiler/toolchain/toolchain.gni")
|
||||
import("//build/ohos.gni")
|
||||
|
||||
config("websocket_config") {
|
||||
cflags_cc = [
|
||||
"-fno-complete-member-pointers",
|
||||
"-Wno-implicit-fallthrough",
|
||||
"-fvisibility=default",
|
||||
"-frtti",
|
||||
]
|
||||
}
|
||||
|
||||
config("websocket_platform_config") {
|
||||
defines = []
|
||||
if (is_ohos) {
|
||||
defines += [
|
||||
"OHOS_PLATFORM",
|
||||
"UNIX_PLATFORM",
|
||||
]
|
||||
} else if (is_mingw) {
|
||||
defines += [ "WINDOWS_PLATFORM" ]
|
||||
} else if (is_mac) {
|
||||
defines += [
|
||||
"MAC_PLATFORM",
|
||||
"UNIX_PLATFORM",
|
||||
]
|
||||
} else if (target_os == "android") {
|
||||
defines += [
|
||||
"ANDROID_PLATFORM",
|
||||
"UNIX_PLATFORM",
|
||||
]
|
||||
} else if (target_os == "ios") {
|
||||
defines += [
|
||||
"UNIX_PLATFORM",
|
||||
"IOS_PLATFORM",
|
||||
]
|
||||
} else {
|
||||
defines += [ "UNIX_PLATFORM" ]
|
||||
}
|
||||
}
|
||||
|
||||
ohos_source_set("websocket") {
|
||||
defines = []
|
||||
deps = []
|
||||
|
||||
configs = [ sdk_libc_secshared_config ]
|
||||
if (is_mingw || is_mac) {
|
||||
cflags = [ "-std=c++17" ]
|
||||
if (is_mingw) {
|
||||
platform = "windows"
|
||||
} else {
|
||||
platform = "mac"
|
||||
}
|
||||
deps += [
|
||||
"//base/hiviewdfx/hilog/interfaces/native/innerkits:libhilog_$platform",
|
||||
]
|
||||
} else if (target_os == "android") {
|
||||
aosp_deps = [ "shared_library:liblog" ]
|
||||
} else {
|
||||
external_deps = [ "hiviewdfx_hilog_native:libhilog" ]
|
||||
}
|
||||
|
||||
include_dirs = []
|
||||
|
||||
include_dirs += [
|
||||
"$toolchain_root/inspector",
|
||||
"//third_party/openssl/include",
|
||||
"//third_party/openssl:libcrypto_static",
|
||||
"//utils/native/base/include",
|
||||
]
|
||||
|
||||
sources = [ "websocket.cpp" ]
|
||||
|
||||
deps += [
|
||||
"//third_party/openssl:libcrypto_static",
|
||||
"//third_party/openssl:ssl_source",
|
||||
sdk_libc_secshared_dep,
|
||||
]
|
||||
|
||||
configs += [
|
||||
":websocket_config",
|
||||
":websocket_platform_config",
|
||||
]
|
||||
|
||||
subsystem_name = "ark"
|
||||
part_name = "toolchain"
|
||||
}
|
58
websocket/define.h
Normal file
58
websocket/define.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef ARKCOMPILER_TOOLCHAIN_INSPECTOR_DEFINE_H
|
||||
#define ARKCOMPILER_TOOLCHAIN_INSPECTOR_DEFINE_H
|
||||
|
||||
#include <fstream>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <securec.h>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#if defined(WINDOWS_PLATFORM)
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
namespace OHOS::ArkCompiler::Toolchain {
|
||||
std::vector<std::string> ProtocolSplit(const std::string& str, const std::string& input)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
int32_t prev = 0;
|
||||
int32_t len = input.length();
|
||||
int32_t cur = str.find(input);
|
||||
while (cur != std::string::npos) {
|
||||
std::string tmp = str.substr(prev, cur - prev);
|
||||
result.push_back(tmp);
|
||||
prev = cur + len;
|
||||
cur = str.find(input, prev);
|
||||
}
|
||||
if (prev < str.size()) {
|
||||
std::string tmp = str.substr(prev);
|
||||
result.push_back(tmp);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace OHOS::ArkCompiler::Toolchain
|
||||
|
||||
#endif // ARKCOMPILER_TOOLCHAIN_INSPECTOR_DEFINE_H
|
356
websocket/websocket.cpp
Normal file
356
websocket/websocket.cpp
Normal file
@ -0,0 +1,356 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* 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 "websocket.h"
|
||||
|
||||
#include "define.h"
|
||||
#include "log_wrapper.h"
|
||||
#include "securec.h"
|
||||
|
||||
namespace OHOS::ArkCompiler::Toolchain {
|
||||
/**
|
||||
* SendMessage in WebSocket has 3 situations:
|
||||
* 1. message's length <= 125
|
||||
* 2. message's length >= 126 && messages's length < 65536
|
||||
* 3. message's length >= 65536
|
||||
*/
|
||||
|
||||
void WebSocket::SendReply(const std::string& message) const
|
||||
{
|
||||
int32_t msgLen = message.length();
|
||||
std::unique_ptr<char []> msgBuf = std::make_unique<char []>(msgLen + 11); // 11: the maximum expandable length
|
||||
char* sendBuf = msgBuf.get();
|
||||
int32_t sendMsgLen;
|
||||
sendBuf[0] = 0x81; // 0x81: the text message sent by the server should start with '0x81'.
|
||||
|
||||
// Depending on the length of the messages, server will use shift operation to get the res
|
||||
// and store them in the buffer.
|
||||
if (msgLen <= 125) { // 125: situation 1 when message's length <= 125
|
||||
sendBuf[1] = msgLen;
|
||||
sendMsgLen = 2; // 2: the length of header frame is 2
|
||||
} else if (msgLen < 65536) { // 65536: message's length
|
||||
sendBuf[1] = 126; // 126: payloadLen according to the spec
|
||||
sendBuf[2] = ((msgLen >> 8) & 0xff); // 8: shift right by 8 bits => res * (256^1)
|
||||
sendBuf[3] = (msgLen & 0xff); // 3: store len's data => res * (256^0)
|
||||
sendMsgLen = 4; // 4: the length of header frame is 4
|
||||
} else {
|
||||
sendBuf[1] = 127; // 127: payloadLen according to the spec
|
||||
for (int32_t i = 2; i <= 5; i++) { // 2 ~ 5: unused bits
|
||||
sendBuf[i] = 0;
|
||||
}
|
||||
sendBuf[6] = ((msgLen & 0xff000000) >> 24); // 6: shift 24 bits => res * (256^3)
|
||||
sendBuf[7] = ((msgLen & 0x00ff0000) >> 16); // 7: shift 16 bits => res * (256^2)
|
||||
sendBuf[8] = ((msgLen & 0x0000ff00) >> 8); // 8: shift 8 bits => res * (256^1)
|
||||
sendBuf[9] = (msgLen & 0x000000ff); // 9: res * (256^0)
|
||||
sendMsgLen = 10; // 10: the length of header frame is 10
|
||||
}
|
||||
if (memcpy_s(sendBuf + sendMsgLen, msgLen, message.c_str(), msgLen) != EOK) {
|
||||
LOGE("SendReply: memcpy_s failed");
|
||||
return;
|
||||
}
|
||||
msgBuf[sendMsgLen + msgLen] = '\0';
|
||||
send(client_, sendBuf, sendMsgLen + msgLen, 0);
|
||||
}
|
||||
|
||||
bool WebSocket::HttpProtocolDecode(const std::string& request, HttpProtocol& req)
|
||||
{
|
||||
if (request.find("GET") == std::string::npos) {
|
||||
LOGE("Handshake failed: lack of necessary info");
|
||||
return false;
|
||||
}
|
||||
std::vector<std::string> reqStr = ProtocolSplit(request, EOL);
|
||||
for (size_t i = 0; i < reqStr.size(); i++) {
|
||||
if (i == 0) {
|
||||
std::vector<std::string> headers = ProtocolSplit(reqStr.at(i), " ");
|
||||
req.version = headers.at(2); // 2: to get the version param
|
||||
} else if (i < reqStr.size() - 1) {
|
||||
std::vector<std::string> headers = ProtocolSplit(reqStr.at(i), ": ");
|
||||
if (reqStr.at(i).find("Connection") != std::string::npos) {
|
||||
req.connection = headers.at(1); // 1: to get the connection param
|
||||
} else if (reqStr.at(i).find("Upgrade") != std::string::npos) {
|
||||
req.upgrade = headers.at(1); // 1: to get the upgrade param
|
||||
} else if (reqStr.at(i).find("Sec-WebSocket-Key") != std::string::npos) {
|
||||
req.secWebSocketKey = headers.at(1); // 1: to get the secWebSocketKey param
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The wired format of this data transmission section is described in detail through ABNFRFC5234.
|
||||
* When receive the message, we should decode it according the spec. The structure is as follows:
|
||||
* 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
|
||||
* +-+-+-+-+-------+-+-------------+-------------------------------+
|
||||
* |F|R|R|R| opcode|M| Payload len | Extended payload length |
|
||||
* |I|S|S|S| (4) |A| (7) | (16/64) |
|
||||
* |N|V|V|V| |S| | (if payload len==126/127) |
|
||||
* | |1|2|3| |K| | |
|
||||
* +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|
||||
* | Extended payload length continued, if payload len == 127 |
|
||||
* + - - - - - - - - - - - - - - - +-------------------------------+
|
||||
* | |Masking-key, if MASK set to 1 |
|
||||
* +-------------------------------+-------------------------------+
|
||||
* | Masking-key (continued) | Payload Data |
|
||||
* +-------------------------------- - - - - - - - - - - - - - - - +
|
||||
* : Payload Data continued ... :
|
||||
* + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|
||||
* | Payload Data continued ... |
|
||||
* +---------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
bool WebSocket::HandleFrame(WebSocketFrame& wsFrame)
|
||||
{
|
||||
if (wsFrame.payloadLen == 126) { // 126: the payloadLen read from frame
|
||||
char recvbuf[PAYLOAD_LEN + 1];
|
||||
int32_t bufLen = recv(client_, recvbuf, PAYLOAD_LEN, 0);
|
||||
if (bufLen < PAYLOAD_LEN) {
|
||||
LOGE("ReadMsg failed readRet=%{public}d", bufLen);
|
||||
return false;
|
||||
}
|
||||
recvbuf[PAYLOAD_LEN] = '\0';
|
||||
uint16_t msgLen;
|
||||
if (memcpy_s(&msgLen, sizeof(recvbuf), recvbuf, sizeof(recvbuf) - 1) != EOK) {
|
||||
LOGE("HandleFrame: memcpy_s failed");
|
||||
return false;
|
||||
}
|
||||
wsFrame.payloadLen = ntohs(msgLen);
|
||||
} else if (wsFrame.payloadLen > 126) { // 126: the payloadLen read from frame
|
||||
char recvbuf[EXTEND_PATLOAD_LEN + 1];
|
||||
int32_t bufLen = recv(client_, recvbuf, EXTEND_PATLOAD_LEN, 0);
|
||||
if (bufLen < EXTEND_PATLOAD_LEN) {
|
||||
LOGE("ReadMsg failed readRet=%{public}d", bufLen);
|
||||
return false;
|
||||
}
|
||||
recvbuf[EXTEND_PATLOAD_LEN] = '\0';
|
||||
uint64_t msgLen;
|
||||
if (memcpy_s(&msgLen, sizeof(recvbuf), recvbuf, sizeof(recvbuf) - 1) != EOK) {
|
||||
LOGE("HandleFrame: memcpy_s failed");
|
||||
return false;
|
||||
}
|
||||
wsFrame.payloadLen = ntohl(msgLen);
|
||||
}
|
||||
return DecodeMessage(wsFrame);
|
||||
}
|
||||
|
||||
bool WebSocket::DecodeMessage(WebSocketFrame& wsFrame)
|
||||
{
|
||||
wsFrame.payload = std::make_unique<char []>(wsFrame.payloadLen + 1);
|
||||
if (wsFrame.mask == 1) {
|
||||
char buf[wsFrame.payloadLen + 1];
|
||||
char mask[SOCKET_MASK_LEN + 1];
|
||||
int32_t bufLen = recv(client_, mask, SOCKET_MASK_LEN, 0);
|
||||
if (bufLen != SOCKET_MASK_LEN) {
|
||||
LOGE("ReadMsg failed readRet=%{public}d", bufLen);
|
||||
return false;
|
||||
}
|
||||
mask[SOCKET_MASK_LEN] = '\0';
|
||||
if (memcpy_s(wsFrame.maskingkey, SOCKET_MASK_LEN, mask, sizeof(mask) - 1) != EOK) {
|
||||
LOGE("DecodeMessage: memcpy_s failed");
|
||||
return false;
|
||||
}
|
||||
uint64_t msgLen = recv(client_, buf, wsFrame.payloadLen, 0);
|
||||
while (msgLen < wsFrame.payloadLen) {
|
||||
uint64_t len = recv(client_, buf + msgLen, wsFrame.payloadLen - msgLen, 0);
|
||||
msgLen += len;
|
||||
}
|
||||
buf[wsFrame.payloadLen] = '\0';
|
||||
for (uint64_t i = 0; i < wsFrame.payloadLen; i++) {
|
||||
uint64_t j = i % SOCKET_MASK_LEN;
|
||||
wsFrame.payload.get()[i] = buf[i] ^ wsFrame.maskingkey[j];
|
||||
}
|
||||
} else {
|
||||
char buf[wsFrame.payloadLen + 1];
|
||||
uint64_t msgLen = recv(client_, buf, wsFrame.payloadLen, 0);
|
||||
if (msgLen != wsFrame.payloadLen) {
|
||||
LOGE("ReadMsg failed");
|
||||
return false;
|
||||
}
|
||||
buf[wsFrame.payloadLen] = '\0';
|
||||
if (memcpy_s(wsFrame.payload.get(), wsFrame.payloadLen, buf, wsFrame.payloadLen) != EOK) {
|
||||
LOGE("DecodeMessage: memcpy_s failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
wsFrame.payload.get()[wsFrame.payloadLen] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebSocket::ProtocolUpgrade(const HttpProtocol& req)
|
||||
{
|
||||
std::string rawKey = req.secWebSocketKey + WEB_SOCKET_GUID;
|
||||
unsigned const char* webSocketKey = reinterpret_cast<unsigned const char*>(std::move(rawKey).c_str());
|
||||
unsigned char hash[SHA_DIGEST_LENGTH + 1];
|
||||
SHA1(webSocketKey, strlen(reinterpret_cast<const char*>(webSocketKey)), hash);
|
||||
hash[SHA_DIGEST_LENGTH] = '\0';
|
||||
unsigned char encodedKey[ENCODED_KEY_LEN];
|
||||
EVP_EncodeBlock(encodedKey, reinterpret_cast<const unsigned char*>(hash), SHA_DIGEST_LENGTH);
|
||||
std::string response;
|
||||
|
||||
std::ostringstream sstream;
|
||||
sstream << "HTTP/1.1 101 Switching Protocols" << EOL;
|
||||
sstream << "Connection: upgrade" << EOL;
|
||||
sstream << "Upgrade: websocket" << EOL;
|
||||
sstream << "Sec-WebSocket-Accept: " << encodedKey << EOL;
|
||||
sstream << EOL;
|
||||
response = sstream.str();
|
||||
int32_t sendLen = send(client_, response.c_str(), response.length(), 0);
|
||||
if (sendLen <= 0) {
|
||||
LOGE("ProtocolUpgrade: Send failed");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<std::string> WebSocket::Decode()
|
||||
{
|
||||
char recvbuf[SOCKET_HEADER_LEN + 1];
|
||||
int32_t msgLen = recv(client_, recvbuf, SOCKET_HEADER_LEN, 0);
|
||||
if (msgLen != SOCKET_HEADER_LEN) {
|
||||
LOGE("Decode Failed: missing necessary header info");
|
||||
return {};
|
||||
}
|
||||
recvbuf[SOCKET_HEADER_LEN] = '\0';
|
||||
WebSocketFrame wsFrame;
|
||||
int32_t index = 0;
|
||||
wsFrame.fin = static_cast<uint8_t>(recvbuf[index] >> 7); // 7: shift right by 7 bits to get the fin
|
||||
wsFrame.opcode = static_cast<uint8_t>(recvbuf[index] & 0xf);
|
||||
if (wsFrame.opcode == 0x1) { // 0x1: 0x1 means a text frame
|
||||
index++;
|
||||
wsFrame.mask = static_cast<uint8_t>((recvbuf[index] >> 7) & 0x1); // 7: to get the mask
|
||||
wsFrame.payloadLen = recvbuf[index] & 0x7f;
|
||||
HandleFrame(wsFrame);
|
||||
return wsFrame.payload.get();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool WebSocket::HttpHandShake()
|
||||
{
|
||||
char msgBuf[SOCKET_HANDSHAKE_LEN];
|
||||
connectState_ = true;
|
||||
int32_t msgLen = recv(client_, msgBuf, SOCKET_HANDSHAKE_LEN, 0);
|
||||
if (msgLen <= 0) {
|
||||
LOGE("ReadMsg failed readRet=%{public}d", msgLen);
|
||||
return false;
|
||||
} else {
|
||||
msgBuf[msgLen - 1] = '\0';
|
||||
HttpProtocol req;
|
||||
if (!HttpProtocolDecode(msgBuf, req)) {
|
||||
LOGE("HttpHandShake: Upgrade failed");
|
||||
return false;
|
||||
} else if (req.connection.find("Upgrade") != std::string::npos &&
|
||||
req.upgrade.find("websocket") != std::string::npos && req.version.compare("HTTP/1.1") == 0) {
|
||||
ProtocolUpgrade(req);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !defined(OHOS_PLATFORM)
|
||||
bool WebSocket::StartForSimulator()
|
||||
{
|
||||
#if defined(WINDOWS_PLATFORM)
|
||||
WORD sockVersion = MAKEWORD(2, 2); // 2: version 2.2
|
||||
WSADATA wsaData;
|
||||
if (WSAStartup(sockVersion, &wsaData) != 0) {
|
||||
LOGE("StartWebSocket WSA init failed");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
fd_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (fd_ < SOCKET_SUCCESS) {
|
||||
LOGE("StartWebSocket socket init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
sockaddr_in sin;
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons(9230); // 9230: sockName for tcp
|
||||
if (bind(fd_, reinterpret_cast<struct sockaddr*>(&sin), sizeof(sin)) < SOCKET_SUCCESS) {
|
||||
LOGE("StartWebSocket bind failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (listen(fd_, 1) < SOCKET_SUCCESS) {
|
||||
LOGE("StartWebSocket listen failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((client_ = accept(fd_, nullptr, nullptr)) < SOCKET_SUCCESS) {
|
||||
LOGE("StartWebSocket accept failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!HttpHandShake()) {
|
||||
LOGE("StartWebSocket HttpHandShake failed");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
bool WebSocket::StartWebSocket(std::string sockName)
|
||||
{
|
||||
fd_ = socket(AF_UNIX, SOCK_STREAM, 0); // 0: defautlt protocol
|
||||
if (fd_ < SOCKET_SUCCESS) {
|
||||
LOGE("StartWebSocket socket init failed");
|
||||
return false;
|
||||
}
|
||||
struct sockaddr_un un;
|
||||
if (memset_s(&un, sizeof(un), 0, sizeof(un)) != EOK) {
|
||||
LOGE("StartWebSocket memset_s failed");
|
||||
return false;
|
||||
}
|
||||
un.sun_family = AF_UNIX;
|
||||
if (strcpy_s(un.sun_path + 1, sizeof(un.sun_path) - 1, sockName.c_str()) != EOK) {
|
||||
LOGE("StartWebSocket strcpy_s failed");
|
||||
return false;
|
||||
}
|
||||
un.sun_path[0] = '\0';
|
||||
int32_t len = offsetof(struct sockaddr_un, sun_path) + strlen(sockName.c_str()) + 1;
|
||||
if (bind(fd_, reinterpret_cast<struct sockaddr*>(&un), len) < SOCKET_SUCCESS) {
|
||||
LOGE("StartWebSocket bind failed");
|
||||
return false;
|
||||
}
|
||||
if (listen(fd_, 1) < SOCKET_SUCCESS) { // 1: connection num
|
||||
LOGE("StartWebSocket listen failed");
|
||||
return false;
|
||||
}
|
||||
if ((client_ = accept(fd_, nullptr, nullptr)) < SOCKET_SUCCESS) {
|
||||
LOGE("StartWebSocket accept failed");
|
||||
return false;
|
||||
}
|
||||
if (!HttpHandShake()) {
|
||||
LOGE("StartWebSocket HttpHandShake failed");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void WebSocket::Close()
|
||||
{
|
||||
if (fd_ >= SOCKET_SUCCESS) {
|
||||
#if defined(OHOS_PLATFORM)
|
||||
if (connectState_) {
|
||||
shutdown(client_, SHUT_RDWR);
|
||||
close(client_);
|
||||
}
|
||||
shutdown(fd_, SHUT_RDWR);
|
||||
#endif
|
||||
close(fd_);
|
||||
}
|
||||
}
|
||||
} // namespace OHOS::ArkCompiler::Toolchain
|
70
websocket/websocket.h
Normal file
70
websocket/websocket.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef ARKCOMPILER_TOOLCHAIN_WEBSOCKET_WEBSOCKET_H
|
||||
#define ARKCOMPILER_TOOLCHAIN_WEBSOCKET_WEBSOCKET_H
|
||||
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
|
||||
namespace OHOS::ArkCompiler::Toolchain {
|
||||
struct WebSocketFrame {
|
||||
uint8_t fin;
|
||||
uint8_t opcode;
|
||||
uint8_t mask;
|
||||
uint64_t payloadLen;
|
||||
char maskingkey[5];
|
||||
std::unique_ptr<char []> payload;
|
||||
};
|
||||
|
||||
struct HttpProtocol {
|
||||
std::string connection;
|
||||
std::string upgrade;
|
||||
std::string version;
|
||||
std::string secWebSocketKey;
|
||||
};
|
||||
|
||||
class WebSocket {
|
||||
public:
|
||||
WebSocket() = default;
|
||||
~WebSocket() = default;
|
||||
std::optional<std::string> Decode();
|
||||
void Close();
|
||||
bool DecodeMessage(WebSocketFrame& wsFrame);
|
||||
bool HttpHandShake();
|
||||
bool HttpProtocolDecode(const std::string& request, HttpProtocol& req);
|
||||
bool HandleFrame(WebSocketFrame& wsFrame);
|
||||
bool ProtocolUpgrade(const HttpProtocol& req);
|
||||
void SendReply(const std::string& message) const;
|
||||
bool StartForSimulator();
|
||||
bool StartWebSocket(std::string sockName);
|
||||
std::atomic<bool> connectState_;
|
||||
|
||||
private:
|
||||
int32_t client_ {0};
|
||||
int32_t fd_ {0};
|
||||
const int32_t ENCODED_KEY_LEN = 128;
|
||||
const char* EOL = "\r\n";
|
||||
const int32_t SOCKET_HANDSHAKE_LEN = 1024;
|
||||
const int32_t SOCKET_HEADER_LEN = 2;
|
||||
const int32_t SOCKET_MASK_LEN = 4;
|
||||
const int32_t SOCKET_SUCCESS = 0;
|
||||
const int32_t PAYLOAD_LEN = 2;
|
||||
const int32_t EXTEND_PATLOAD_LEN = 8;
|
||||
const char* WEB_SOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
};
|
||||
} // namespace OHOS::ArkCompiler::Toolchain
|
||||
|
||||
#endif // ARKCOMPILER_TOOLCHAIN_WEBSOCKET_WEBSOCKET_H
|
Loading…
x
Reference in New Issue
Block a user