1 /*
2 * Copyright (c) 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 "inspector_socket_server.h"
17
18 #include <algorithm>
19 #include <cstdio>
20 #include <iostream>
21 #include <map>
22 #include <securec.h>
23 #include <set>
24 #include <sstream>
25 #include <string>
26 #include <unistd.h>
27
28 #include "jsvm_version.h"
29 #include "uv.h"
30 #include "v8_inspector_protocol_json.h"
31 #include "zlib.h"
32
33 namespace jsvm {
34 namespace inspector {
35
36 // Function is declared in inspector_io.h so the rest of the node does not
37 // depend on inspector_socket_server.h
38 std::string FormatWsAddress(const std::string& host, int port, const std::string& targetId, bool includeProtocol);
39 namespace {
FormatHostPort(const std::string & host,int port)40 std::string FormatHostPort(const std::string& host, int port)
41 {
42 // Host is valid (socket was bound) so colon means it's a v6 IP address
43 bool isIPV6 = (host.find(':') != std::string::npos);
44 std::ostringstream url;
45 if (isIPV6) {
46 url << '[' << host << ']';
47 } else {
48 url << host;
49 }
50 url << ':' << port;
51 return url.str();
52 }
53
Escape(std::string * string)54 void Escape(std::string* string)
55 {
56 for (char& c : *string) {
57 c = (c == '\"' || c == '\\') ? '_' : c;
58 }
59 }
60
FormatAddress(const std::string & host,const std::string & targetId,bool includeProtocol)61 std::string FormatAddress(const std::string& host, const std::string& targetId, bool includeProtocol)
62 {
63 std::ostringstream url;
64 if (includeProtocol) {
65 url << "ws://";
66 }
67 url << host << '/' << targetId;
68 return url.str();
69 }
70
MapToString(const std::map<std::string,std::string> & object)71 std::string MapToString(const std::map<std::string, std::string>& object)
72 {
73 bool first = true;
74 std::ostringstream json;
75 json << "{\n";
76 for (const auto& nameValue : object) {
77 if (!first) {
78 json << ",\n";
79 }
80 first = false;
81 json << " \"" << nameValue.first << "\": \"";
82 json << nameValue.second << "\"";
83 }
84 json << "\n} ";
85 return json.str();
86 }
87
MapsToString(const std::vector<std::map<std::string,std::string>> & array)88 std::string MapsToString(const std::vector<std::map<std::string, std::string>>& array)
89 {
90 bool isFirst = true;
91 std::ostringstream json;
92 json << "[ ";
93 for (const auto& object : array) {
94 if (!isFirst) {
95 json << ", ";
96 }
97 isFirst = false;
98 json << MapToString(object);
99 }
100 json << "]\n\n";
101 return json.str();
102 }
103
SendHttpResponse(InspectorSocket * socket,const std::string & response,int code)104 void SendHttpResponse(InspectorSocket* socket, const std::string& response, int code)
105 {
106 const char headers[] = "HTTP/1.0 %d OK\r\n"
107 "Content-Type: application/json; charset=UTF-8\r\n"
108 "Cache-Control: no-cache\r\n"
109 "Content-Length: %zu\r\n"
110 "\r\n";
111 char header[sizeof(headers) + 20];
112 int headerLen = snprintf_s(header, sizeof(header), sizeof(header) - 1, headers, code, response.size());
113 CHECK(headerLen >= 0);
114 socket->Write(header, headerLen);
115 socket->Write(response.data(), response.size());
116 }
117
MatchPathSegment(const char * path,const char * expected)118 const char* MatchPathSegment(const char* path, const char* expected)
119 {
120 size_t len = strlen(expected);
121 if (StringEqualNoCaseN(path, expected, len)) {
122 if (path[len] == '/') {
123 return path + len + 1;
124 }
125 if (path[len] == '\0') {
126 return path + len;
127 }
128 }
129 return nullptr;
130 }
131
132 enum HttpStatusCode : int {
133 HTTP_OK = 200,
134 HTTP_NOT_FOUND = 404,
135 };
136
SendHttpNotFound(InspectorSocket * socket)137 void SendHttpNotFound(InspectorSocket* socket)
138 {
139 SendHttpResponse(socket, "", HttpStatusCode::HTTP_NOT_FOUND);
140 }
141
SendVersionResponse(InspectorSocket * socket)142 void SendVersionResponse(InspectorSocket* socket)
143 {
144 std::map<std::string, std::string> response;
145 response["Protocol-Version"] = "1.1";
146 response["Browser"] = "jsvm/" JSVM_VERSION_STRING;
147 SendHttpResponse(socket, MapToString(response), HttpStatusCode::HTTP_OK);
148 }
149
SendProtocolJson(InspectorSocket * socket)150 void SendProtocolJson(InspectorSocket* socket)
151 {
152 z_stream strm;
153 strm.zalloc = Z_NULL;
154 strm.zfree = Z_NULL;
155 strm.opaque = Z_NULL;
156 CHECK_EQ(inflateInit(&strm), Z_OK);
157 static const size_t decompressedSize = PROTOCOL_JSON[0] * 0x10000u + PROTOCOL_JSON[1] * 0x100u + PROTOCOL_JSON[2];
158 strm.next_in = const_cast<uint8_t*>(PROTOCOL_JSON + ByteOffset::BYTE_3);
159 strm.avail_in = sizeof(PROTOCOL_JSON) - ByteOffset::BYTE_3;
160 std::string data(decompressedSize, '\0');
161 strm.next_out = reinterpret_cast<Byte*>(data.data());
162 strm.avail_out = data.size();
163 CHECK_EQ(inflate(&strm, Z_FINISH), Z_STREAM_END);
164 CHECK_EQ(strm.avail_out, 0);
165 CHECK_EQ(inflateEnd(&strm), Z_OK);
166 SendHttpResponse(socket, data, HttpStatusCode::HTTP_OK);
167 }
168 } // namespace
169
FormatWsAddress(const std::string & host,int port,const std::string & targetId,bool includeProtocol)170 std::string FormatWsAddress(const std::string& host, int port, const std::string& targetId, bool includeProtocol)
171 {
172 return FormatAddress(FormatHostPort(host, port), targetId, includeProtocol);
173 }
174
175 class SocketSession {
176 public:
177 SocketSession(InspectorSocketServer* server, int id, int serverPort);
Close()178 void Close()
179 {
180 wsSocket.reset();
181 }
182 void Send(const std::string& message);
Own(InspectorSocket::Pointer wsSocketParam)183 void Own(InspectorSocket::Pointer wsSocketParam)
184 {
185 wsSocket = std::move(wsSocketParam);
186 }
GetId() const187 int GetId() const
188 {
189 return id;
190 }
ServerPort()191 int ServerPort()
192 {
193 return serverPort;
194 }
GetWsSocket()195 InspectorSocket* GetWsSocket()
196 {
197 return wsSocket.get();
198 }
Accept(const std::string & wsKey)199 void Accept(const std::string& wsKey)
200 {
201 wsSocket->AcceptUpgrade(wsKey);
202 }
Decline()203 void Decline()
204 {
205 wsSocket->CancelHandshake();
206 }
207
208 class Delegate : public InspectorSocket::Delegate {
209 public:
Delegate(InspectorSocketServer * server,int sessionId)210 Delegate(InspectorSocketServer* server, int sessionId) : server(server), usessionId(sessionId) {}
~Delegate()211 ~Delegate() override
212 {
213 server->SessionTerminated(usessionId);
214 }
215 void OnHttpGet(const std::string& host, const std::string& path) override;
216 void OnSocketUpgrade(const std::string& host, const std::string& path, const std::string& wsKey) override;
217 void OnWsFrame(const std::vector<char>& data) override;
218
219 private:
Session()220 SocketSession* Session()
221 {
222 return server->Session(usessionId);
223 }
224
225 InspectorSocketServer* server;
226 int usessionId;
227 };
228
229 private:
230 const int id;
231 InspectorSocket::Pointer wsSocket;
232 const int serverPort;
233 };
234
235 class ServerSocket {
236 public:
ServerSocket(InspectorSocketServer * server)237 explicit ServerSocket(InspectorSocketServer* server)
238 : tcpSocket(uv_tcp_t()), server(server), unixSocket(uv_pipe_t())
239 {}
240 int Listen(sockaddr* addr, uv_loop_t* loop, int pid = -1);
Close()241 void Close()
242 {
243 uv_close(reinterpret_cast<uv_handle_t*>(&tcpSocket), FreeOnCloseCallback);
244 }
CloseUnix()245 void CloseUnix()
246 {
247 if (unixSocketOn) {
248 uv_close(reinterpret_cast<uv_handle_t*>(&unixSocket), nullptr);
249 unixSocketOn = false;
250 }
251 }
GetPort() const252 int GetPort() const
253 {
254 return port;
255 }
256
257 private:
258 template<typename UvHandle>
FromTcpSocket(UvHandle * socket)259 static ServerSocket* FromTcpSocket(UvHandle* socket)
260 {
261 return jsvm::inspector::ContainerOf(&ServerSocket::tcpSocket, reinterpret_cast<uv_tcp_t*>(socket));
262 }
263 static void SocketConnectedCallback(uv_stream_t* tcpSocket, int status);
264 static void UnixSocketConnectedCallback(uv_stream_t* unixSocket, int status);
FreeOnCloseCallback(uv_handle_t * tcpSocket)265 static void FreeOnCloseCallback(uv_handle_t* tcpSocket)
266 {
267 delete FromTcpSocket(tcpSocket);
268 }
269 int DetectPort(uv_loop_t* loop, int pid);
270 ~ServerSocket() = default;
271
272 uv_tcp_t tcpSocket;
273 InspectorSocketServer* server;
274 uv_pipe_t unixSocket;
275 int port = -1;
276 bool unixSocketOn = false;
277 };
278
PrintDebuggerReadyMessage(const std::string & host,const std::vector<InspectorSocketServer::ServerSocketPtr> & serverSockets,const std::vector<std::string> & ids,const char * verb,bool publishUidStderr,FILE * out)279 void PrintDebuggerReadyMessage(const std::string& host,
280 const std::vector<InspectorSocketServer::ServerSocketPtr>& serverSockets,
281 const std::vector<std::string>& ids,
282 const char* verb,
283 bool publishUidStderr,
284 FILE* out)
285 {
286 if (!publishUidStderr || out == nullptr) {
287 return;
288 }
289 for (const auto& serverSocket : serverSockets) {
290 for (const std::string& id : ids) {
291 if (fprintf(out, "Debugger %s on %s\n", verb,
292 FormatWsAddress(host, serverSocket->GetPort(), id, true).c_str()) < 0) {
293 return;
294 }
295 }
296 }
297 if (fprintf(out, "For help, see: %s\n", "https://nodejs.org/en/docs/inspector") < 0) {
298 return;
299 }
300 if (fflush(out) != 0) {
301 return;
302 }
303 }
304
InspectorSocketServer(std::unique_ptr<SocketServerDelegate> delegateParam,uv_loop_t * loop,const std::string & host,int port,const InspectPublishUid & inspectPublishUid,FILE * out,int pid)305 InspectorSocketServer::InspectorSocketServer(std::unique_ptr<SocketServerDelegate> delegateParam,
306 uv_loop_t* loop,
307 const std::string& host,
308 int port,
309 const InspectPublishUid& inspectPublishUid,
310 FILE* out,
311 int pid)
312 : loop(loop), delegate(std::move(delegateParam)), host(host), port(port), inspectPublishUid(inspectPublishUid),
313 nextSessionId(0), out(out), pid(pid)
314 {
315 delegate->AssignServer(this);
316 state = ServerState::NEW;
317 }
318
319 InspectorSocketServer::~InspectorSocketServer() = default;
320
Session(int sessionId)321 SocketSession* InspectorSocketServer::Session(int sessionId)
322 {
323 auto it = connectedSessions.find(sessionId);
324 return it == connectedSessions.end() ? nullptr : it->second.second.get();
325 }
326
SessionStarted(int sessionId,const std::string & targetId,const std::string & wsKey)327 void InspectorSocketServer::SessionStarted(int sessionId, const std::string& targetId, const std::string& wsKey)
328 {
329 SocketSession* session = Session(sessionId);
330 if (!TargetExists(targetId)) {
331 session->Decline();
332 return;
333 }
334 connectedSessions[sessionId].first = targetId;
335 session->Accept(wsKey);
336 delegate->StartSession(sessionId, targetId);
337 }
338
SessionTerminated(int sessionId)339 void InspectorSocketServer::SessionTerminated(int sessionId)
340 {
341 if (Session(sessionId) == nullptr) {
342 return;
343 }
344 bool wasAttached = connectedSessions[sessionId].first != "";
345 if (wasAttached) {
346 delegate->EndSession(sessionId);
347 }
348 connectedSessions.erase(sessionId);
349 if (connectedSessions.empty()) {
350 if (wasAttached && state == ServerState::RUNNING && !serverSockets.empty()) {
351 PrintDebuggerReadyMessage(host, serverSockets, delegate->GetTargetIds(), "ending",
352 inspectPublishUid.console, out);
353 }
354 if (state == ServerState::STOPPED) {
355 delegate.reset();
356 }
357 }
358 }
359
HandleGetRequest(int sessionId,const std::string & hostName,const std::string & path)360 bool InspectorSocketServer::HandleGetRequest(int sessionId, const std::string& hostName, const std::string& path)
361 {
362 SocketSession* session = Session(sessionId);
363 InspectorSocket* socket = session->GetWsSocket();
364 if (!inspectPublishUid.http) {
365 SendHttpNotFound(socket);
366 return true;
367 }
368 const char* command = MatchPathSegment(path.c_str(), "/json");
369 if (!command) {
370 return false;
371 }
372
373 bool hasHandled = false;
374 if (MatchPathSegment(command, "list") || command[0] == '\0') {
375 SendListResponse(socket, hostName, session);
376 hasHandled = true;
377 } else if (MatchPathSegment(command, "protocol")) {
378 SendProtocolJson(socket);
379 hasHandled = true;
380 } else if (MatchPathSegment(command, "version")) {
381 SendVersionResponse(socket);
382 hasHandled = true;
383 }
384 return hasHandled;
385 }
386
SendListResponse(InspectorSocket * socket,const std::string & hostName,SocketSession * session)387 void InspectorSocketServer::SendListResponse(InspectorSocket* socket,
388 const std::string& hostName,
389 SocketSession* session)
390 {
391 std::vector<std::map<std::string, std::string>> response;
392 for (const std::string& id : delegate->GetTargetIds()) {
393 response.push_back(std::map<std::string, std::string>());
394 std::map<std::string, std::string>& targetMap = response.back();
395 targetMap["description"] = "jsvm instance";
396 targetMap["id"] = id;
397 targetMap["title"] = delegate->GetTargetTitle(id);
398 Escape(&targetMap["title"]);
399 targetMap["type"] = "node";
400 // This attribute value is a "best effort" URL that is passed as a JSON
401 // string. It is not guaranteed to resolve to a valid resource.
402 targetMap["url"] = delegate->GetTargetUrl(id);
403 Escape(&targetMap["url"]);
404
405 std::string detectedHost = hostName;
406 if (detectedHost.empty()) {
407 detectedHost = FormatHostPort(socket->GetHost(), session->ServerPort());
408 }
409 std::string formattedAddress = FormatAddress(detectedHost, id, false);
410 targetMap["devtoolsFrontendUrl"] = GetFrontendURL(false, formattedAddress);
411 // The compat URL is for Chrome browsers older than 66.0.3345.0
412 targetMap["devtoolsFrontendUrlCompat"] = GetFrontendURL(true, formattedAddress);
413 targetMap["webSocketDebuggerUrl"] = FormatAddress(detectedHost, id, true);
414 }
415 SendHttpResponse(socket, MapsToString(response), HttpStatusCode::HTTP_OK);
416 }
417
GetFrontendURL(bool isCompat,const std::string & formattedAddress)418 std::string InspectorSocketServer::GetFrontendURL(bool isCompat, const std::string& formattedAddress)
419 {
420 std::ostringstream frontendUrl;
421 frontendUrl << "devtools://devtools/bundled/";
422 frontendUrl << (isCompat ? "inspector" : "js_app");
423 frontendUrl << ".html?v8only=true&ws=";
424 frontendUrl << formattedAddress;
425 return frontendUrl.str();
426 }
427
Start()428 bool InspectorSocketServer::Start()
429 {
430 CHECK_NOT_NULL(delegate);
431 CHECK_EQ(state, ServerState::NEW);
432 std::unique_ptr<SocketServerDelegate> delegateHolder;
433 // We will return it if startup is successful
434 delegate.swap(delegateHolder);
435 struct addrinfo hints;
436 memset_s(&hints, sizeof(hints), 0, sizeof(hints));
437 hints.ai_flags = AI_NUMERICSERV;
438 hints.ai_socktype = SOCK_STREAM;
439 uv_getaddrinfo_t req;
440 const std::string portString = std::to_string(port);
441 int err = uv_getaddrinfo(loop, &req, nullptr, host.c_str(), portString.c_str(), &hints);
442 if (err < 0) {
443 if (out != nullptr) {
444 if (fprintf(out, "Unable to resolve \"%s\": %s\n", host.c_str(), uv_strerror(err)) < 0) {
445 return false;
446 }
447 }
448 return false;
449 }
450 for (addrinfo* address = req.addrinfo; address != nullptr; address = address->ai_next) {
451 auto serverSocket = ServerSocketPtr(new ServerSocket(this));
452 err = serverSocket->Listen(address->ai_addr, loop, pid);
453 if (err == 0) {
454 serverSockets.push_back(std::move(serverSocket));
455 }
456 }
457 uv_freeaddrinfo(req.addrinfo);
458
459 // We only show error if we failed to start server on all addresses. We only
460 // show one error, for the last address.
461 if (serverSockets.empty()) {
462 if (out != nullptr) {
463 if (fprintf(out, "Starting inspector on %s:%d failed: %s\n", host.c_str(), port, uv_strerror(err)) < 0) {
464 return false;
465 }
466 if (fflush(out) != 0) {
467 return false;
468 }
469 }
470 return false;
471 }
472 delegate.swap(delegateHolder);
473 state = ServerState::RUNNING;
474 PrintDebuggerReadyMessage(host, serverSockets, delegate->GetTargetIds(), "listening", inspectPublishUid.console,
475 out);
476 return true;
477 }
478
Stop()479 void InspectorSocketServer::Stop()
480 {
481 if (state == ServerState::STOPPED) {
482 return;
483 }
484 CHECK_EQ(state, ServerState::RUNNING);
485 state = ServerState::STOPPED;
486 serverSockets.clear();
487 if (Done()) {
488 delegate.reset();
489 }
490 }
491
TerminateConnections()492 void InspectorSocketServer::TerminateConnections()
493 {
494 for (const auto& keyValue : connectedSessions) {
495 keyValue.second.second->Close();
496 }
497 }
498
TargetExists(const std::string & id)499 bool InspectorSocketServer::TargetExists(const std::string& id)
500 {
501 const std::vector<std::string>& targetIds = delegate->GetTargetIds();
502 const auto& found = std::find(targetIds.begin(), targetIds.end(), id);
503 return found != targetIds.end();
504 }
505
GetPort() const506 int InspectorSocketServer::GetPort() const
507 {
508 if (!serverSockets.empty()) {
509 return serverSockets[0]->GetPort();
510 }
511 return port;
512 }
513
Accept(int serverPort,uv_stream_t * serverSocket)514 void InspectorSocketServer::Accept(int serverPort, uv_stream_t* serverSocket)
515 {
516 std::unique_ptr<SocketSession> session = std::make_unique<SocketSession>(this, nextSessionId++, serverPort);
517
518 InspectorSocket::DelegatePointer delegatePointer =
519 InspectorSocket::DelegatePointer(new SocketSession::Delegate(this, session->GetId()));
520
521 InspectorSocket::Pointer inspector = InspectorSocket::Accept(serverSocket, std::move(delegatePointer));
522 if (inspector) {
523 session->Own(std::move(inspector));
524 connectedSessions[session->GetId()].second = std::move(session);
525 }
526 }
527
Send(int sessionId,const std::string & message)528 void InspectorSocketServer::Send(int sessionId, const std::string& message)
529 {
530 SocketSession* session = Session(sessionId);
531 if (session != nullptr) {
532 session->Send(message);
533 }
534 }
535
CloseServerSocket(ServerSocket * server)536 void InspectorSocketServer::CloseServerSocket(ServerSocket* server)
537 {
538 server->Close();
539 server->CloseUnix();
540 }
541
542 // InspectorSession tracking
SocketSession(InspectorSocketServer * server,int id,int serverPort)543 SocketSession::SocketSession(InspectorSocketServer* server, int id, int serverPort) : id(id), serverPort(serverPort) {}
544
Send(const std::string & message)545 void SocketSession::Send(const std::string& message)
546 {
547 wsSocket->Write(message.data(), message.length());
548 }
549
OnHttpGet(const std::string & host,const std::string & path)550 void SocketSession::Delegate::OnHttpGet(const std::string& host, const std::string& path)
551 {
552 if (!server->HandleGetRequest(usessionId, host, path)) {
553 Session()->GetWsSocket()->CancelHandshake();
554 }
555 }
556
OnSocketUpgrade(const std::string & host,const std::string & path,const std::string & wsKey)557 void SocketSession::Delegate::OnSocketUpgrade(const std::string& host,
558 const std::string& path,
559 const std::string& wsKey)
560 {
561 std::string id = path.empty() ? path : path.substr(1);
562 server->SessionStarted(usessionId, id, wsKey);
563 }
564
OnWsFrame(const std::vector<char> & data)565 void SocketSession::Delegate::OnWsFrame(const std::vector<char>& data)
566 {
567 server->MessageReceived(usessionId, std::string(data.data(), data.size()));
568 }
569
570 // ServerSocket implementation
DetectPort(uv_loop_t * loop,int pid)571 int ServerSocket::DetectPort(uv_loop_t* loop, int pid)
572 {
573 sockaddr_storage addr;
574 int len = sizeof(addr);
575 int err = uv_tcp_getsockname(&tcpSocket, reinterpret_cast<struct sockaddr*>(&addr), &len);
576 if (err != 0) {
577 return err;
578 }
579 int portNum;
580 if (addr.ss_family == AF_INET6) {
581 portNum = reinterpret_cast<const sockaddr_in6*>(&addr)->sin6_port;
582 } else {
583 portNum = reinterpret_cast<const sockaddr_in*>(&addr)->sin_port;
584 }
585 port = ntohs(portNum);
586 if (!unixSocketOn && pid != -1) {
587 auto unixDomainSocketPath = "jsvm_devtools_remote_" + std::to_string(port) + "_" + std::to_string(pid);
588 auto* abstract = new char[unixDomainSocketPath.length() + 2];
589 abstract[0] = '\0';
590 int ret = strcpy_s(abstract + 1, unixDomainSocketPath.length() + 2, unixDomainSocketPath.c_str());
591 CHECK(ret == 0);
592 auto status = uv_pipe_init(loop, &unixSocket, 0);
593 if (status == 0) {
594 status = uv_pipe_bind2(&unixSocket, abstract, unixDomainSocketPath.length() + 1, 0);
595 }
596 if (status == 0) {
597 constexpr int unixBacklog = 128;
598 status = uv_listen(reinterpret_cast<uv_stream_t*>(&unixSocket), unixBacklog,
599 ServerSocket::UnixSocketConnectedCallback);
600 }
601 unixSocketOn = status == 0;
602 delete[] abstract;
603 }
604 return err;
605 }
606
Listen(sockaddr * addr,uv_loop_t * loop,int pid)607 int ServerSocket::Listen(sockaddr* addr, uv_loop_t* loop, int pid)
608 {
609 uv_tcp_t* server = &tcpSocket;
610 CHECK_EQ(0, uv_tcp_init(loop, server));
611 int err = uv_tcp_bind(server, addr, 0);
612 if (err == 0) {
613 // 511 is the value used by a 'net' module by default
614 err = uv_listen(reinterpret_cast<uv_stream_t*>(server), 511, ServerSocket::SocketConnectedCallback);
615 }
616 if (err == 0) {
617 err = DetectPort(loop, pid);
618 }
619 return err;
620 }
621
622 // static
SocketConnectedCallback(uv_stream_t * tcpSocket,int status)623 void ServerSocket::SocketConnectedCallback(uv_stream_t* tcpSocket, int status)
624 {
625 if (status == 0) {
626 ServerSocket* serverSocket = ServerSocket::FromTcpSocket(tcpSocket);
627 // Memory is freed when the socket closes.
628 serverSocket->server->Accept(serverSocket->port, tcpSocket);
629 }
630 }
631
UnixSocketConnectedCallback(uv_stream_t * unixSocket,int status)632 void ServerSocket::UnixSocketConnectedCallback(uv_stream_t* unixSocket, int status)
633 {
634 if (status == 0) {
635 (void)unixSocket;
636 }
637 }
638 } // namespace inspector
639 } // namespace jsvm
640