• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "connection/asio/asio_server.h"
17 
18 #include <memory>
19 #include <sstream>
20 #include <system_error>
21 
22 #include "websocketpp/close.hpp"
23 #include "websocketpp/http/constants.hpp"
24 #include "websocketpp/uri.hpp"
25 
26 #include "utils/json_builder.h"
27 #include "utils/logger.h"
28 
29 #include "connection/asio/asio_config.h"
30 
31 #define CONFIG AsioConfig  // NOLINT(cppcoreguidelines-macro-usage)
32 #include "server_endpoint-inl.h"
33 #undef CONFIG
34 
35 namespace ark::tooling::inspector {
36 template <typename ConnectionPtr>
HandleHttpRequest(ConnectionPtr conn)37 static void HandleHttpRequest(ConnectionPtr conn)
38 {
39     static constexpr std::string_view GET = "GET";
40     static constexpr std::string_view JSON_URI = "/json";
41     static constexpr std::string_view JSON_LIST_URI = "/json/list";
42     static constexpr std::string_view JSON_VERSION_URI = "/json/version";
43 
44     const auto &req = conn->get_request();
45     const auto &uri = req.get_uri();
46     if (req.get_method() != GET) {
47         CloseConnection(conn);
48         return;
49     }
50 
51     if (uri == JSON_URI || uri == JSON_LIST_URI) {
52         HandleJsonList(conn);
53     } else if (uri == JSON_VERSION_URI) {
54         HandleJsonVersion(conn);
55     } else {
56         CloseConnection(conn);
57         return;
58     }
59     conn->append_header("Content-Type", "application/json; charset=UTF-8");
60     conn->append_header("Cache-Control", "no-cache");
61     conn->set_status(websocketpp::http::status_code::value::ok);
62 }
63 
64 template <typename ConnectionPtr>
HandleJsonVersion(ConnectionPtr conn)65 static void HandleJsonVersion(ConnectionPtr conn)
66 {
67     JsonObjectBuilder builder;
68     builder.AddProperty("browser", "ArkTS");
69     builder.AddProperty("protocol-version", "1.1");
70     AddWebSocketDebuggerUrl(conn, builder);
71     conn->set_body(std::move(builder).Build());
72 }
73 
74 template <typename ConnectionPtr>
HandleJsonList(ConnectionPtr conn)75 static void HandleJsonList(ConnectionPtr conn)
76 {
77     auto buildJson = [conn](JsonObjectBuilder &builder) {
78         builder.AddProperty("description", "ArkTS");
79         // Empty "id" corresponds to constructed URLs
80         builder.AddProperty("id", "");
81         builder.AddProperty("type", "node");
82         // Do not add "url" fields as it dummy value might confuse some clients
83         AddWebSocketDebuggerUrl(conn, builder);
84         std::stringstream ss;
85         // CC-OFFNXT(WordsTool.74) fixed protocol url
86         ss << "devtools://devtools/bundled/devtools_app.html?ws=" << conn->get_host() << ':'
87            << static_cast<int>(conn->get_port());
88         builder.AddProperty("devToolsFrontendUrl", ss.str());
89     };
90     JsonArrayBuilder arrayBuilder;
91     arrayBuilder.Add(std::move(buildJson));
92     conn->set_body(std::move(arrayBuilder).Build());
93 }
94 
95 template <typename ConnectionPtr>
CloseConnection(ConnectionPtr conn)96 static void CloseConnection(ConnectionPtr conn)
97 {
98     std::error_code ec;
99     conn->close(websocketpp::close::status::protocol_error, "", ec);
100     if (ec) {
101         LOG(WARNING, DEBUGGER) << "Failed to close invalid HTTP connection";
102     }
103 }
104 
105 template <typename ConnectionPtr>
AddWebSocketDebuggerUrl(ConnectionPtr conn,JsonObjectBuilder & builder)106 static void AddWebSocketDebuggerUrl(ConnectionPtr conn, JsonObjectBuilder &builder)
107 {
108     std::stringstream ss;
109     ss << "ws://" << conn->get_host() << ':' << conn->get_port();
110     builder.AddProperty("webSocketDebuggerUrl", ss.str());
111 }
112 
Initialize()113 bool AsioServer::Initialize()
114 {
115     if (initialized_) {
116         return true;
117     }
118 
119     // Do JSON handshake, as it is expected by some debugger clients
120     endpoint_.set_http_handler([this](auto hdl) { HandleHttpRequest(endpoint_.get_con_from_hdl(hdl)); });
121 
122     std::error_code ec;
123     endpoint_.init_asio(ec);
124     if (ec) {
125         LOG(ERROR, DEBUGGER) << "Failed to initialize endpoint";
126         return false;
127     }
128 
129     endpoint_.set_reuse_addr(true);
130     initialized_ = true;
131     return true;
132 }
133 
Start(uint32_t port)134 websocketpp::uri_ptr AsioServer::Start(uint32_t port)
135 {
136     if (!Initialize()) {
137         return nullptr;
138     }
139 
140     std::error_code ec;
141 
142     endpoint_.listen(port, ec);
143     if (ec) {
144         LOG(ERROR, DEBUGGER) << "Failed to bind Inspector server on port " << port;
145         return nullptr;
146     }
147 
148     endpoint_.start_accept(ec);
149 
150     if (!ec) {
151         auto ep = endpoint_.get_local_endpoint(ec);
152 
153         if (!ec) {
154             LOG(INFO, DEBUGGER) << "Inspector server listening on " << ep;
155             return std::make_shared<websocketpp::uri>(false, ep.address().to_string(), ep.port(), "/");
156         }
157 
158         LOG(ERROR, DEBUGGER) << "Failed to get the TCP endpoint";
159     } else {
160         LOG(ERROR, DEBUGGER) << "Failed to start Inspector connection acceptance loop";
161     }
162 
163     endpoint_.stop_listening(ec);
164     return nullptr;
165 }
166 
Stop()167 bool AsioServer::Stop()
168 {
169     if (!Initialize()) {
170         return false;
171     }
172 
173     // Stop accepting new connections.
174     Kill();
175     // Close the current connection.
176     Close();
177 
178     std::error_code ec;
179     endpoint_.stop_listening(ec);
180     return !ec;
181 }
182 }  // namespace ark::tooling::inspector
183