1 |
|
|
#include "inspector_socket_server.h" |
2 |
|
|
|
3 |
|
|
#include "node.h" |
4 |
|
|
#include "util-inl.h" |
5 |
|
|
#include "uv.h" |
6 |
|
|
#include "zlib.h" |
7 |
|
|
|
8 |
|
|
#include <algorithm> |
9 |
|
|
#include <map> |
10 |
|
|
#include <set> |
11 |
|
|
#include <sstream> |
12 |
|
|
|
13 |
|
|
namespace node { |
14 |
|
|
namespace inspector { |
15 |
|
|
|
16 |
|
|
// Function is declared in inspector_io.h so the rest of the node does not |
17 |
|
|
// depend on inspector_socket_server.h |
18 |
|
|
std::string FormatWsAddress(const std::string& host, int port, |
19 |
|
|
const std::string& target_id, |
20 |
|
|
bool include_protocol); |
21 |
|
|
namespace { |
22 |
|
|
|
23 |
|
|
static const uint8_t PROTOCOL_JSON[] = { |
24 |
|
|
#include "v8_inspector_protocol_json.h" // NOLINT(build/include_order) |
25 |
|
|
}; |
26 |
|
|
|
27 |
|
42 |
void Escape(std::string* string) { |
28 |
✓✓ |
2959 |
for (char& c : *string) { |
29 |
✓✗✓✗
|
2917 |
c = (c == '\"' || c == '\\') ? '_' : c; |
30 |
|
|
} |
31 |
|
42 |
} |
32 |
|
|
|
33 |
|
84 |
std::string FormatHostPort(const std::string& host, int port) { |
34 |
|
|
// Host is valid (socket was bound) so colon means it's a v6 IP address |
35 |
|
84 |
bool v6 = host.find(':') != std::string::npos; |
36 |
|
168 |
std::ostringstream url; |
37 |
✓✓ |
84 |
if (v6) { |
38 |
|
4 |
url << '['; |
39 |
|
|
} |
40 |
|
84 |
url << host; |
41 |
✓✓ |
84 |
if (v6) { |
42 |
|
4 |
url << ']'; |
43 |
|
|
} |
44 |
|
84 |
url << ':' << port; |
45 |
|
84 |
return url.str(); |
46 |
|
|
} |
47 |
|
|
|
48 |
|
126 |
std::string FormatAddress(const std::string& host, |
49 |
|
|
const std::string& target_id, |
50 |
|
|
bool include_protocol) { |
51 |
|
252 |
std::ostringstream url; |
52 |
✓✓ |
126 |
if (include_protocol) |
53 |
|
105 |
url << "ws://"; |
54 |
|
126 |
url << host << '/' << target_id; |
55 |
|
126 |
return url.str(); |
56 |
|
|
} |
57 |
|
|
|
58 |
|
22 |
std::string MapToString(const std::map<std::string, std::string>& object) { |
59 |
|
22 |
bool first = true; |
60 |
|
44 |
std::ostringstream json; |
61 |
|
22 |
json << "{\n"; |
62 |
✓✓ |
213 |
for (const auto& name_value : object) { |
63 |
✓✓ |
191 |
if (!first) |
64 |
|
169 |
json << ",\n"; |
65 |
|
191 |
first = false; |
66 |
|
191 |
json << " \"" << name_value.first << "\": \""; |
67 |
|
191 |
json << name_value.second << "\""; |
68 |
|
|
} |
69 |
|
22 |
json << "\n} "; |
70 |
|
22 |
return json.str(); |
71 |
|
|
} |
72 |
|
|
|
73 |
|
27 |
std::string MapsToString( |
74 |
|
|
const std::vector<std::map<std::string, std::string>>& array) { |
75 |
|
27 |
bool first = true; |
76 |
|
54 |
std::ostringstream json; |
77 |
|
27 |
json << "[ "; |
78 |
✓✓ |
48 |
for (const auto& object : array) { |
79 |
✗✓ |
21 |
if (!first) |
80 |
|
|
json << ", "; |
81 |
|
21 |
first = false; |
82 |
|
21 |
json << MapToString(object); |
83 |
|
|
} |
84 |
|
27 |
json << "]\n\n"; |
85 |
|
27 |
return json.str(); |
86 |
|
|
} |
87 |
|
|
|
88 |
|
70 |
const char* MatchPathSegment(const char* path, const char* expected) { |
89 |
|
70 |
size_t len = strlen(expected); |
90 |
✓✓ |
70 |
if (StringEqualNoCaseN(path, expected, len)) { |
91 |
✓✓ |
57 |
if (path[len] == '/') return path + len + 1; |
92 |
✓✗ |
28 |
if (path[len] == '\0') return path + len; |
93 |
|
|
} |
94 |
|
13 |
return nullptr; |
95 |
|
|
} |
96 |
|
|
|
97 |
|
29 |
void SendHttpResponse(InspectorSocket* socket, |
98 |
|
|
const std::string& response, |
99 |
|
|
int code) { |
100 |
|
29 |
const char HEADERS[] = "HTTP/1.0 %d OK\r\n" |
101 |
|
|
"Content-Type: application/json; charset=UTF-8\r\n" |
102 |
|
|
"Cache-Control: no-cache\r\n" |
103 |
|
|
"Content-Length: %zu\r\n" |
104 |
|
|
"\r\n"; |
105 |
|
|
char header[sizeof(HEADERS) + 20]; |
106 |
|
29 |
int header_len = snprintf(header, |
107 |
|
|
sizeof(header), |
108 |
|
|
HEADERS, |
109 |
|
|
code, |
110 |
|
|
response.size()); |
111 |
|
29 |
socket->Write(header, header_len); |
112 |
|
29 |
socket->Write(response.data(), response.size()); |
113 |
|
29 |
} |
114 |
|
|
|
115 |
|
1 |
void SendVersionResponse(InspectorSocket* socket) { |
116 |
|
1 |
std::map<std::string, std::string> response; |
117 |
|
1 |
response["Browser"] = "node.js/" NODE_VERSION; |
118 |
|
1 |
response["Protocol-Version"] = "1.1"; |
119 |
|
1 |
SendHttpResponse(socket, MapToString(response), 200); |
120 |
|
1 |
} |
121 |
|
|
|
122 |
|
1 |
void SendHttpNotFound(InspectorSocket* socket) { |
123 |
|
1 |
SendHttpResponse(socket, "", 404); |
124 |
|
1 |
} |
125 |
|
|
|
126 |
|
|
void SendProtocolJson(InspectorSocket* socket) { |
127 |
|
|
z_stream strm; |
128 |
|
|
strm.zalloc = Z_NULL; |
129 |
|
|
strm.zfree = Z_NULL; |
130 |
|
|
strm.opaque = Z_NULL; |
131 |
|
|
CHECK_EQ(Z_OK, inflateInit(&strm)); |
132 |
|
|
static const size_t kDecompressedSize = |
133 |
|
|
PROTOCOL_JSON[0] * 0x10000u + |
134 |
|
|
PROTOCOL_JSON[1] * 0x100u + |
135 |
|
|
PROTOCOL_JSON[2]; |
136 |
|
|
strm.next_in = const_cast<uint8_t*>(PROTOCOL_JSON + 3); |
137 |
|
|
strm.avail_in = sizeof(PROTOCOL_JSON) - 3; |
138 |
|
|
std::string data(kDecompressedSize, '\0'); |
139 |
|
|
strm.next_out = reinterpret_cast<Byte*>(&data[0]); |
140 |
|
|
strm.avail_out = data.size(); |
141 |
|
|
CHECK_EQ(Z_STREAM_END, inflate(&strm, Z_FINISH)); |
142 |
|
|
CHECK_EQ(0, strm.avail_out); |
143 |
|
|
CHECK_EQ(Z_OK, inflateEnd(&strm)); |
144 |
|
|
SendHttpResponse(socket, data, 200); |
145 |
|
|
} |
146 |
|
|
} // namespace |
147 |
|
|
|
148 |
|
84 |
std::string FormatWsAddress(const std::string& host, int port, |
149 |
|
|
const std::string& target_id, |
150 |
|
|
bool include_protocol) { |
151 |
|
84 |
return FormatAddress(FormatHostPort(host, port), target_id, include_protocol); |
152 |
|
|
} |
153 |
|
|
|
154 |
|
|
class SocketSession { |
155 |
|
|
public: |
156 |
|
|
SocketSession(InspectorSocketServer* server, int id, int server_port); |
157 |
|
3 |
void Close() { |
158 |
|
3 |
ws_socket_.reset(); |
159 |
|
3 |
} |
160 |
|
|
void Send(const std::string& message); |
161 |
|
55 |
void Own(InspectorSocket::Pointer ws_socket) { |
162 |
|
55 |
ws_socket_ = std::move(ws_socket); |
163 |
|
55 |
} |
164 |
|
110 |
int id() const { return id_; } |
165 |
|
|
int server_port() { |
166 |
|
|
return server_port_; |
167 |
|
|
} |
168 |
|
35 |
InspectorSocket* ws_socket() { |
169 |
|
35 |
return ws_socket_.get(); |
170 |
|
|
} |
171 |
|
23 |
void Accept(const std::string& ws_key) { |
172 |
|
23 |
ws_socket_->AcceptUpgrade(ws_key); |
173 |
|
23 |
} |
174 |
|
1 |
void Decline() { |
175 |
|
1 |
ws_socket_->CancelHandshake(); |
176 |
|
1 |
} |
177 |
|
|
|
178 |
|
|
class Delegate : public InspectorSocket::Delegate { |
179 |
|
|
public: |
180 |
|
55 |
Delegate(InspectorSocketServer* server, int session_id) |
181 |
|
55 |
: server_(server), session_id_(session_id) { } |
182 |
|
220 |
~Delegate() override { |
183 |
|
110 |
server_->SessionTerminated(session_id_); |
184 |
|
220 |
} |
185 |
|
|
void OnHttpGet(const std::string& host, const std::string& path) override; |
186 |
|
|
void OnSocketUpgrade(const std::string& host, const std::string& path, |
187 |
|
|
const std::string& ws_key) override; |
188 |
|
|
void OnWsFrame(const std::vector<char>& data) override; |
189 |
|
|
|
190 |
|
|
private: |
191 |
|
3 |
SocketSession* Session() { |
192 |
|
3 |
return server_->Session(session_id_); |
193 |
|
|
} |
194 |
|
|
|
195 |
|
|
InspectorSocketServer* server_; |
196 |
|
|
int session_id_; |
197 |
|
|
}; |
198 |
|
|
|
199 |
|
|
private: |
200 |
|
|
const int id_; |
201 |
|
|
InspectorSocket::Pointer ws_socket_; |
202 |
|
|
const int server_port_; |
203 |
|
|
}; |
204 |
|
|
|
205 |
|
|
class ServerSocket { |
206 |
|
|
public: |
207 |
|
94 |
explicit ServerSocket(InspectorSocketServer* server) |
208 |
|
94 |
: tcp_socket_(uv_tcp_t()), server_(server) {} |
209 |
|
|
int Listen(sockaddr* addr, uv_loop_t* loop); |
210 |
|
94 |
void Close() { |
211 |
|
94 |
uv_close(reinterpret_cast<uv_handle_t*>(&tcp_socket_), FreeOnCloseCallback); |
212 |
|
94 |
} |
213 |
|
169 |
int port() const { return port_; } |
214 |
|
|
|
215 |
|
|
private: |
216 |
|
|
template <typename UvHandle> |
217 |
|
298 |
static ServerSocket* FromTcpSocket(UvHandle* socket) { |
218 |
|
596 |
return node::ContainerOf(&ServerSocket::tcp_socket_, |
219 |
|
298 |
reinterpret_cast<uv_tcp_t*>(socket)); |
220 |
|
|
} |
221 |
|
|
static void SocketConnectedCallback(uv_stream_t* tcp_socket, int status); |
222 |
|
94 |
static void FreeOnCloseCallback(uv_handle_t* tcp_socket_) { |
223 |
|
94 |
delete FromTcpSocket(tcp_socket_); |
224 |
|
94 |
} |
225 |
|
|
int DetectPort(); |
226 |
|
|
~ServerSocket() = default; |
227 |
|
|
|
228 |
|
|
uv_tcp_t tcp_socket_; |
229 |
|
|
InspectorSocketServer* server_; |
230 |
|
|
int port_ = -1; |
231 |
|
|
}; |
232 |
|
|
|
233 |
|
89 |
void PrintDebuggerReadyMessage( |
234 |
|
|
const std::string& host, |
235 |
|
|
const std::vector<InspectorSocketServer::ServerSocketPtr>& server_sockets, |
236 |
|
|
const std::vector<std::string>& ids, |
237 |
|
|
const char* verb, |
238 |
|
|
bool publish_uid_stderr, |
239 |
|
|
FILE* out) { |
240 |
✓✓✓✓
|
89 |
if (!publish_uid_stderr || out == nullptr) { |
241 |
|
12 |
return; |
242 |
|
|
} |
243 |
✓✓ |
156 |
for (const auto& server_socket : server_sockets) { |
244 |
✓✓ |
158 |
for (const std::string& id : ids) { |
245 |
|
79 |
fprintf(out, "Debugger %s on %s\n", |
246 |
|
|
verb, |
247 |
|
158 |
FormatWsAddress(host, server_socket->port(), id, true).c_str()); |
248 |
|
|
} |
249 |
|
|
} |
250 |
|
77 |
fprintf(out, "For help, see: %s\n", |
251 |
|
|
"https://nodejs.org/en/docs/inspector"); |
252 |
|
77 |
fflush(out); |
253 |
|
|
} |
254 |
|
|
|
255 |
|
89 |
InspectorSocketServer::InspectorSocketServer( |
256 |
|
|
std::unique_ptr<SocketServerDelegate> delegate, uv_loop_t* loop, |
257 |
|
|
const std::string& host, int port, |
258 |
|
89 |
const InspectPublishUid& inspect_publish_uid, FILE* out) |
259 |
|
|
: loop_(loop), |
260 |
|
89 |
delegate_(std::move(delegate)), |
261 |
|
|
host_(host), |
262 |
|
|
port_(port), |
263 |
|
|
inspect_publish_uid_(inspect_publish_uid), |
264 |
|
|
next_session_id_(0), |
265 |
|
89 |
out_(out) { |
266 |
|
89 |
delegate_->AssignServer(this); |
267 |
|
89 |
state_ = ServerState::kNew; |
268 |
|
89 |
} |
269 |
|
|
|
270 |
|
|
InspectorSocketServer::~InspectorSocketServer() = default; |
271 |
|
|
|
272 |
|
2214 |
SocketSession* InspectorSocketServer::Session(int session_id) { |
273 |
|
2214 |
auto it = connected_sessions_.find(session_id); |
274 |
✗✓ |
2214 |
return it == connected_sessions_.end() ? nullptr : it->second.second.get(); |
275 |
|
|
} |
276 |
|
|
|
277 |
|
24 |
void InspectorSocketServer::SessionStarted(int session_id, |
278 |
|
|
const std::string& id, |
279 |
|
|
const std::string& ws_key) { |
280 |
|
24 |
SocketSession* session = Session(session_id); |
281 |
✓✓ |
24 |
if (!TargetExists(id)) { |
282 |
|
1 |
session->Decline(); |
283 |
|
1 |
return; |
284 |
|
|
} |
285 |
|
23 |
connected_sessions_[session_id].first = id; |
286 |
|
23 |
session->Accept(ws_key); |
287 |
|
23 |
delegate_->StartSession(session_id, id); |
288 |
|
|
} |
289 |
|
|
|
290 |
|
55 |
void InspectorSocketServer::SessionTerminated(int session_id) { |
291 |
✗✓ |
55 |
if (Session(session_id) == nullptr) { |
292 |
|
|
return; |
293 |
|
|
} |
294 |
|
55 |
bool was_attached = connected_sessions_[session_id].first != ""; |
295 |
✓✓ |
55 |
if (was_attached) { |
296 |
|
23 |
delegate_->EndSession(session_id); |
297 |
|
|
} |
298 |
|
55 |
connected_sessions_.erase(session_id); |
299 |
✓✓ |
55 |
if (connected_sessions_.empty()) { |
300 |
✓✓ |
21 |
if (was_attached && state_ == ServerState::kRunning |
301 |
✓✓✓✗ ✓✓ |
71 |
&& !server_sockets_.empty()) { |
302 |
|
9 |
PrintDebuggerReadyMessage(host_, |
303 |
|
3 |
server_sockets_, |
304 |
|
6 |
delegate_->GetTargetIds(), |
305 |
|
|
"ending", |
306 |
|
3 |
inspect_publish_uid_.console, |
307 |
|
|
out_); |
308 |
|
|
} |
309 |
✓✓ |
50 |
if (state_ == ServerState::kStopped) { |
310 |
|
20 |
delegate_.reset(); |
311 |
|
|
} |
312 |
|
|
} |
313 |
|
|
} |
314 |
|
|
|
315 |
|
32 |
bool InspectorSocketServer::HandleGetRequest(int session_id, |
316 |
|
|
const std::string& host, |
317 |
|
|
const std::string& path) { |
318 |
|
32 |
SocketSession* session = Session(session_id); |
319 |
|
32 |
InspectorSocket* socket = session->ws_socket(); |
320 |
✓✓ |
32 |
if (!inspect_publish_uid_.http) { |
321 |
|
1 |
SendHttpNotFound(socket); |
322 |
|
1 |
return true; |
323 |
|
|
} |
324 |
|
31 |
const char* command = MatchPathSegment(path.c_str(), "/json"); |
325 |
✗✓ |
31 |
if (command == nullptr) |
326 |
|
|
return false; |
327 |
|
|
|
328 |
✓✓✓✓ ✓✓ |
31 |
if (MatchPathSegment(command, "list") || command[0] == '\0') { |
329 |
|
27 |
SendListResponse(socket, host, session); |
330 |
|
27 |
return true; |
331 |
✗✓ |
4 |
} else if (MatchPathSegment(command, "protocol")) { |
332 |
|
|
SendProtocolJson(socket); |
333 |
|
|
return true; |
334 |
✓✓ |
4 |
} else if (MatchPathSegment(command, "version")) { |
335 |
|
1 |
SendVersionResponse(socket); |
336 |
|
1 |
return true; |
337 |
|
|
} |
338 |
|
3 |
return false; |
339 |
|
|
} |
340 |
|
|
|
341 |
|
27 |
void InspectorSocketServer::SendListResponse(InspectorSocket* socket, |
342 |
|
|
const std::string& host, |
343 |
|
|
SocketSession* session) { |
344 |
|
27 |
std::vector<std::map<std::string, std::string>> response; |
345 |
✓✓ |
48 |
for (const std::string& id : delegate_->GetTargetIds()) { |
346 |
|
21 |
response.push_back(std::map<std::string, std::string>()); |
347 |
|
21 |
std::map<std::string, std::string>& target_map = response.back(); |
348 |
|
21 |
target_map["description"] = "node.js instance"; |
349 |
|
42 |
target_map["faviconUrl"] = |
350 |
|
21 |
"https://nodejs.org/static/images/favicons/favicon.ico"; |
351 |
|
21 |
target_map["id"] = id; |
352 |
|
21 |
target_map["title"] = delegate_->GetTargetTitle(id); |
353 |
|
21 |
Escape(&target_map["title"]); |
354 |
|
21 |
target_map["type"] = "node"; |
355 |
|
|
// This attribute value is a "best effort" URL that is passed as a JSON |
356 |
|
|
// string. It is not guaranteed to resolve to a valid resource. |
357 |
|
21 |
target_map["url"] = delegate_->GetTargetUrl(id); |
358 |
|
21 |
Escape(&target_map["url"]); |
359 |
|
|
|
360 |
|
42 |
std::string detected_host = host; |
361 |
✗✓ |
21 |
if (detected_host.empty()) { |
362 |
|
|
detected_host = FormatHostPort(socket->GetHost(), |
363 |
|
|
session->server_port()); |
364 |
|
|
} |
365 |
|
21 |
std::string formatted_address = FormatAddress(detected_host, id, false); |
366 |
|
42 |
target_map["devtoolsFrontendUrl"] = GetFrontendURL(false, |
367 |
|
21 |
formatted_address); |
368 |
|
|
// The compat URL is for Chrome browsers older than 66.0.3345.0 |
369 |
|
42 |
target_map["devtoolsFrontendUrlCompat"] = GetFrontendURL(true, |
370 |
|
21 |
formatted_address); |
371 |
|
21 |
target_map["webSocketDebuggerUrl"] = FormatAddress(detected_host, id, true); |
372 |
|
|
} |
373 |
|
27 |
SendHttpResponse(socket, MapsToString(response), 200); |
374 |
|
27 |
} |
375 |
|
|
|
376 |
|
42 |
std::string InspectorSocketServer::GetFrontendURL(bool is_compat, |
377 |
|
|
const std::string &formatted_address) { |
378 |
|
84 |
std::ostringstream frontend_url; |
379 |
|
42 |
frontend_url << "devtools://devtools/bundled/"; |
380 |
✓✓ |
42 |
frontend_url << (is_compat ? "inspector" : "js_app"); |
381 |
|
42 |
frontend_url << ".html?experiments=true&v8only=true&ws="; |
382 |
|
42 |
frontend_url << formatted_address; |
383 |
|
42 |
return frontend_url.str(); |
384 |
|
|
} |
385 |
|
|
|
386 |
|
89 |
bool InspectorSocketServer::Start() { |
387 |
✗✓ |
89 |
CHECK_NOT_NULL(delegate_); |
388 |
✗✓ |
89 |
CHECK_EQ(state_, ServerState::kNew); |
389 |
|
89 |
std::unique_ptr<SocketServerDelegate> delegate_holder; |
390 |
|
|
// We will return it if startup is successful |
391 |
|
89 |
delegate_.swap(delegate_holder); |
392 |
|
|
struct addrinfo hints; |
393 |
|
89 |
memset(&hints, 0, sizeof(hints)); |
394 |
|
89 |
hints.ai_flags = AI_NUMERICSERV; |
395 |
|
89 |
hints.ai_socktype = SOCK_STREAM; |
396 |
|
|
uv_getaddrinfo_t req; |
397 |
|
178 |
const std::string port_string = std::to_string(port_); |
398 |
|
89 |
int err = uv_getaddrinfo(loop_, &req, nullptr, host_.c_str(), |
399 |
|
|
port_string.c_str(), &hints); |
400 |
✗✓ |
89 |
if (err < 0) { |
401 |
|
|
if (out_ != nullptr) { |
402 |
|
|
fprintf(out_, "Unable to resolve \"%s\": %s\n", host_.c_str(), |
403 |
|
|
uv_strerror(err)); |
404 |
|
|
} |
405 |
|
|
return false; |
406 |
|
|
} |
407 |
✓✓ |
183 |
for (addrinfo* address = req.addrinfo; address != nullptr; |
408 |
|
94 |
address = address->ai_next) { |
409 |
|
188 |
auto server_socket = ServerSocketPtr(new ServerSocket(this)); |
410 |
|
94 |
err = server_socket->Listen(address->ai_addr, loop_); |
411 |
✓✓ |
94 |
if (err == 0) |
412 |
|
88 |
server_sockets_.push_back(std::move(server_socket)); |
413 |
|
|
} |
414 |
|
89 |
uv_freeaddrinfo(req.addrinfo); |
415 |
|
|
|
416 |
|
|
// We only show error if we failed to start server on all addresses. We only |
417 |
|
|
// show one error, for the last address. |
418 |
✓✓ |
89 |
if (server_sockets_.empty()) { |
419 |
✓✓ |
3 |
if (out_ != nullptr) { |
420 |
|
1 |
fprintf(out_, "Starting inspector on %s:%d failed: %s\n", |
421 |
|
|
host_.c_str(), port_, uv_strerror(err)); |
422 |
|
1 |
fflush(out_); |
423 |
|
|
} |
424 |
|
3 |
return false; |
425 |
|
|
} |
426 |
|
86 |
delegate_.swap(delegate_holder); |
427 |
|
86 |
state_ = ServerState::kRunning; |
428 |
|
258 |
PrintDebuggerReadyMessage(host_, |
429 |
|
86 |
server_sockets_, |
430 |
|
172 |
delegate_->GetTargetIds(), |
431 |
|
|
"listening", |
432 |
|
86 |
inspect_publish_uid_.console, |
433 |
|
|
out_); |
434 |
|
86 |
return true; |
435 |
|
|
} |
436 |
|
|
|
437 |
|
99 |
void InspectorSocketServer::Stop() { |
438 |
✓✓ |
99 |
if (state_ == ServerState::kStopped) |
439 |
|
13 |
return; |
440 |
✗✓ |
86 |
CHECK_EQ(state_, ServerState::kRunning); |
441 |
|
86 |
state_ = ServerState::kStopped; |
442 |
|
86 |
server_sockets_.clear(); |
443 |
✓✓ |
86 |
if (done()) |
444 |
|
66 |
delegate_.reset(); |
445 |
|
|
} |
446 |
|
|
|
447 |
|
22 |
void InspectorSocketServer::TerminateConnections() { |
448 |
✓✓ |
25 |
for (const auto& key_value : connected_sessions_) |
449 |
|
3 |
key_value.second.second->Close(); |
450 |
|
22 |
} |
451 |
|
|
|
452 |
|
24 |
bool InspectorSocketServer::TargetExists(const std::string& id) { |
453 |
|
24 |
const std::vector<std::string>& target_ids = delegate_->GetTargetIds(); |
454 |
|
24 |
const auto& found = std::find(target_ids.begin(), target_ids.end(), id); |
455 |
|
24 |
return found != target_ids.end(); |
456 |
|
|
} |
457 |
|
|
|
458 |
|
90 |
int InspectorSocketServer::Port() const { |
459 |
✓✗ |
90 |
if (!server_sockets_.empty()) { |
460 |
|
90 |
return server_sockets_[0]->port(); |
461 |
|
|
} |
462 |
|
|
return port_; |
463 |
|
|
} |
464 |
|
|
|
465 |
|
55 |
void InspectorSocketServer::Accept(int server_port, |
466 |
|
|
uv_stream_t* server_socket) { |
467 |
|
|
std::unique_ptr<SocketSession> session( |
468 |
|
110 |
new SocketSession(this, next_session_id_++, server_port)); |
469 |
|
|
|
470 |
|
|
InspectorSocket::DelegatePointer delegate = |
471 |
|
|
InspectorSocket::DelegatePointer( |
472 |
|
110 |
new SocketSession::Delegate(this, session->id())); |
473 |
|
|
|
474 |
|
|
InspectorSocket::Pointer inspector = |
475 |
|
110 |
InspectorSocket::Accept(server_socket, std::move(delegate)); |
476 |
✓✗ |
55 |
if (inspector) { |
477 |
|
55 |
session->Own(std::move(inspector)); |
478 |
|
55 |
connected_sessions_[session->id()].second = std::move(session); |
479 |
|
|
} |
480 |
|
55 |
} |
481 |
|
|
|
482 |
|
2100 |
void InspectorSocketServer::Send(int session_id, const std::string& message) { |
483 |
|
2100 |
SocketSession* session = Session(session_id); |
484 |
✓✗ |
2100 |
if (session != nullptr) { |
485 |
|
2100 |
session->Send(message); |
486 |
|
|
} |
487 |
|
2100 |
} |
488 |
|
|
|
489 |
|
94 |
void InspectorSocketServer::CloseServerSocket(ServerSocket* server) { |
490 |
|
94 |
server->Close(); |
491 |
|
94 |
} |
492 |
|
|
|
493 |
|
|
// InspectorSession tracking |
494 |
|
55 |
SocketSession::SocketSession(InspectorSocketServer* server, int id, |
495 |
|
55 |
int server_port) |
496 |
|
55 |
: id_(id), server_port_(server_port) {} |
497 |
|
|
|
498 |
|
2100 |
void SocketSession::Send(const std::string& message) { |
499 |
|
2100 |
ws_socket_->Write(message.data(), message.length()); |
500 |
|
2100 |
} |
501 |
|
|
|
502 |
|
32 |
void SocketSession::Delegate::OnHttpGet(const std::string& host, |
503 |
|
|
const std::string& path) { |
504 |
✓✓ |
32 |
if (!server_->HandleGetRequest(session_id_, host, path)) |
505 |
|
3 |
Session()->ws_socket()->CancelHandshake(); |
506 |
|
32 |
} |
507 |
|
|
|
508 |
|
24 |
void SocketSession::Delegate::OnSocketUpgrade(const std::string& host, |
509 |
|
|
const std::string& path, |
510 |
|
|
const std::string& ws_key) { |
511 |
✗✓ |
48 |
std::string id = path.empty() ? path : path.substr(1); |
512 |
|
24 |
server_->SessionStarted(session_id_, id, ws_key); |
513 |
|
24 |
} |
514 |
|
|
|
515 |
|
135 |
void SocketSession::Delegate::OnWsFrame(const std::vector<char>& data) { |
516 |
|
135 |
server_->MessageReceived(session_id_, |
517 |
|
270 |
std::string(data.data(), data.size())); |
518 |
|
135 |
} |
519 |
|
|
|
520 |
|
|
// ServerSocket implementation |
521 |
|
88 |
int ServerSocket::DetectPort() { |
522 |
|
|
sockaddr_storage addr; |
523 |
|
88 |
int len = sizeof(addr); |
524 |
|
88 |
int err = uv_tcp_getsockname(&tcp_socket_, |
525 |
|
|
reinterpret_cast<struct sockaddr*>(&addr), &len); |
526 |
✗✓ |
88 |
if (err != 0) |
527 |
|
|
return err; |
528 |
|
|
int port; |
529 |
✓✓ |
88 |
if (addr.ss_family == AF_INET6) |
530 |
|
7 |
port = reinterpret_cast<const sockaddr_in6*>(&addr)->sin6_port; |
531 |
|
|
else |
532 |
|
81 |
port = reinterpret_cast<const sockaddr_in*>(&addr)->sin_port; |
533 |
|
88 |
port_ = ntohs(port); |
534 |
|
88 |
return err; |
535 |
|
|
} |
536 |
|
|
|
537 |
|
94 |
int ServerSocket::Listen(sockaddr* addr, uv_loop_t* loop) { |
538 |
|
94 |
uv_tcp_t* server = &tcp_socket_; |
539 |
✗✓ |
94 |
CHECK_EQ(0, uv_tcp_init(loop, server)); |
540 |
|
94 |
int err = uv_tcp_bind(server, addr, 0); |
541 |
✓✓ |
94 |
if (err == 0) { |
542 |
|
|
// 511 is the value used by a 'net' module by default |
543 |
|
90 |
err = uv_listen(reinterpret_cast<uv_stream_t*>(server), 511, |
544 |
|
|
ServerSocket::SocketConnectedCallback); |
545 |
|
|
} |
546 |
✓✓ |
94 |
if (err == 0) { |
547 |
|
88 |
err = DetectPort(); |
548 |
|
|
} |
549 |
|
94 |
return err; |
550 |
|
|
} |
551 |
|
|
|
552 |
|
|
// static |
553 |
|
55 |
void ServerSocket::SocketConnectedCallback(uv_stream_t* tcp_socket, |
554 |
|
|
int status) { |
555 |
✓✗ |
55 |
if (status == 0) { |
556 |
|
55 |
ServerSocket* server_socket = ServerSocket::FromTcpSocket(tcp_socket); |
557 |
|
|
// Memory is freed when the socket closes. |
558 |
|
55 |
server_socket->server_->Accept(server_socket->port_, tcp_socket); |
559 |
|
|
} |
560 |
|
55 |
} |
561 |
|
|
} // namespace inspector |
562 |
|
|
} // namespace node |