• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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