GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/inspector_socket_server.cc Lines: 277 310 89.4 %
Date: 2019-03-02 22:23:06 Branches: 87 128 68.0 %

Line Branch Exec Source
1
#include "inspector_socket_server.h"
2
3
#include "node.h"
4
#include "uv.h"
5
#include "zlib.h"
6
7
#include <algorithm>
8
#include <map>
9
#include <set>
10
#include <sstream>
11
12
namespace node {
13
namespace inspector {
14
15
// Function is declared in inspector_io.h so the rest of the node does not
16
// depend on inspector_socket_server.h
17
std::string FormatWsAddress(const std::string& host, int port,
18
                            const std::string& target_id,
19
                            bool include_protocol);
20
namespace {
21
22
static const uint8_t PROTOCOL_JSON[] = {
23
  #include "v8_inspector_protocol_json.h"  // NOLINT(build/include_order)
24
};
25
26
30
void Escape(std::string* string) {
27
2377
  for (char& c : *string) {
28

2347
    c = (c == '\"' || c == '\\') ? '_' : c;
29
  }
30
30
}
31
32
77
std::string FormatHostPort(const std::string& host, int port) {
33
  // Host is valid (socket was bound) so colon means it's a v6 IP address
34
77
  bool v6 = host.find(':') != std::string::npos;
35
77
  std::ostringstream url;
36
77
  if (v6) {
37
4
    url << '[';
38
  }
39
77
  url << host;
40
77
  if (v6) {
41
4
    url << ']';
42
  }
43
77
  url << ':' << port;
44
77
  return url.str();
45
}
46
47
107
std::string FormatAddress(const std::string& host,
48
                          const std::string& target_id,
49
                          bool include_protocol) {
50
107
  std::ostringstream url;
51
107
  if (include_protocol)
52
92
    url << "ws://";
53
107
  url << host << '/' << target_id;
54
107
  return url.str();
55
}
56
57
16
std::string MapToString(const std::map<std::string, std::string>& object) {
58
16
  bool first = true;
59
16
  std::ostringstream json;
60
16
  json << "{\n";
61
153
  for (const auto& name_value : object) {
62
137
    if (!first)
63
121
      json << ",\n";
64
137
    first = false;
65
137
    json << "  \"" << name_value.first << "\": \"";
66
137
    json << name_value.second << "\"";
67
  }
68
16
  json << "\n} ";
69
16
  return json.str();
70
}
71
72
15
std::string MapsToString(
73
    const std::vector<std::map<std::string, std::string>>& array) {
74
15
  bool first = true;
75
15
  std::ostringstream json;
76
15
  json << "[ ";
77
30
  for (const auto& object : array) {
78
15
    if (!first)
79
      json << ", ";
80
15
    first = false;
81
15
    json << MapToString(object);
82
  }
83
15
  json << "]\n\n";
84
15
  return json.str();
85
}
86
87
46
const char* MatchPathSegment(const char* path, const char* expected) {
88
46
  size_t len = strlen(expected);
89
46
  if (StringEqualNoCaseN(path, expected, len)) {
90
34
    if (path[len] == '/') return path + len + 1;
91
16
    if (path[len] == '\0') return path + len;
92
  }
93
12
  return nullptr;
94
}
95
96
74
void PrintDebuggerReadyMessage(const std::string& host,
97
                               int port,
98
                               const std::vector<std::string>& ids,
99
                               FILE* out) {
100
74
  if (out == nullptr) {
101
74
    return;
102
  }
103
148
  for (const std::string& id : ids) {
104
    fprintf(out, "Debugger listening on %s\n",
105
74
            FormatWsAddress(host, port, id, true).c_str());
106
  }
107
  fprintf(out, "For help, see: %s\n",
108
74
          "https://nodejs.org/en/docs/inspector");
109
74
  fflush(out);
110
}
111
112
16
void SendHttpResponse(InspectorSocket* socket, const std::string& response) {
113
  const char HEADERS[] = "HTTP/1.0 200 OK\r\n"
114
                         "Content-Type: application/json; charset=UTF-8\r\n"
115
                         "Cache-Control: no-cache\r\n"
116
                         "Content-Length: %zu\r\n"
117
16
                         "\r\n";
118
  char header[sizeof(HEADERS) + 20];
119
16
  int header_len = snprintf(header, sizeof(header), HEADERS, response.size());
120
16
  socket->Write(header, header_len);
121
16
  socket->Write(response.data(), response.size());
122
16
}
123
124
1
void SendVersionResponse(InspectorSocket* socket) {
125
1
  std::map<std::string, std::string> response;
126
1
  response["Browser"] = "node.js/" NODE_VERSION;
127
1
  response["Protocol-Version"] = "1.1";
128
1
  SendHttpResponse(socket, MapToString(response));
129
1
}
130
131
void SendProtocolJson(InspectorSocket* socket) {
132
  z_stream strm;
133
  strm.zalloc = Z_NULL;
134
  strm.zfree = Z_NULL;
135
  strm.opaque = Z_NULL;
136
  CHECK_EQ(Z_OK, inflateInit(&strm));
137
  static const size_t kDecompressedSize =
138
      PROTOCOL_JSON[0] * 0x10000u +
139
      PROTOCOL_JSON[1] * 0x100u +
140
      PROTOCOL_JSON[2];
141
  strm.next_in = const_cast<uint8_t*>(PROTOCOL_JSON + 3);
142
  strm.avail_in = sizeof(PROTOCOL_JSON) - 3;
143
  std::string data(kDecompressedSize, '\0');
144
  strm.next_out = reinterpret_cast<Byte*>(&data[0]);
145
  strm.avail_out = data.size();
146
  CHECK_EQ(Z_STREAM_END, inflate(&strm, Z_FINISH));
147
  CHECK_EQ(0, strm.avail_out);
148
  CHECK_EQ(Z_OK, inflateEnd(&strm));
149
  SendHttpResponse(socket, data);
150
}
151
}  // namespace
152
153
77
std::string FormatWsAddress(const std::string& host, int port,
154
                            const std::string& target_id,
155
                            bool include_protocol) {
156
77
  return FormatAddress(FormatHostPort(host, port), target_id, include_protocol);
157
}
158
159
35
class SocketSession {
160
 public:
161
  SocketSession(InspectorSocketServer* server, int id, int server_port);
162
1
  void Close() {
163
1
    ws_socket_.reset();
164
1
  }
165
  void Send(const std::string& message);
166
35
  void Own(InspectorSocket::Pointer ws_socket) {
167
35
    ws_socket_ = std::move(ws_socket);
168
35
  }
169
70
  int id() const { return id_; }
170
  int server_port() {
171
    return server_port_;
172
  }
173
22
  InspectorSocket* ws_socket() {
174
22
    return ws_socket_.get();
175
  }
176
13
  void Accept(const std::string& ws_key) {
177
13
    ws_socket_->AcceptUpgrade(ws_key);
178
13
  }
179
  void Decline() {
180
    ws_socket_->CancelHandshake();
181
  }
182
183
  class Delegate : public InspectorSocket::Delegate {
184
   public:
185
35
    Delegate(InspectorSocketServer* server, int session_id)
186
35
             : server_(server), session_id_(session_id) { }
187
105
    ~Delegate() override {
188
35
      server_->SessionTerminated(session_id_);
189
70
    }
190
    void OnHttpGet(const std::string& host, const std::string& path) override;
191
    void OnSocketUpgrade(const std::string& host, const std::string& path,
192
                         const std::string& ws_key) override;
193
    void OnWsFrame(const std::vector<char>& data) override;
194
195
   private:
196
3
    SocketSession* Session() {
197
3
      return server_->Session(session_id_);
198
    }
199
200
    InspectorSocketServer* server_;
201
    int session_id_;
202
  };
203
204
 private:
205
  const int id_;
206
  InspectorSocket::Pointer ws_socket_;
207
  const int server_port_;
208
};
209
210
class ServerSocket {
211
 public:
212
76
  explicit ServerSocket(InspectorSocketServer* server)
213
76
                        : tcp_socket_(uv_tcp_t()), server_(server) {}
214
  int Listen(sockaddr* addr, uv_loop_t* loop);
215
76
  void Close() {
216
76
    uv_close(reinterpret_cast<uv_handle_t*>(&tcp_socket_), FreeOnCloseCallback);
217
76
  }
218
147
  int port() const { return port_; }
219
220
 private:
221
  template <typename UvHandle>
222
111
  static ServerSocket* FromTcpSocket(UvHandle* socket) {
223
    return node::ContainerOf(&ServerSocket::tcp_socket_,
224
111
                             reinterpret_cast<uv_tcp_t*>(socket));
225
  }
226
  static void SocketConnectedCallback(uv_stream_t* tcp_socket, int status);
227
76
  static void FreeOnCloseCallback(uv_handle_t* tcp_socket_) {
228
76
    delete FromTcpSocket(tcp_socket_);
229
76
  }
230
  int DetectPort();
231
  ~ServerSocket() = default;
232
233
  uv_tcp_t tcp_socket_;
234
  InspectorSocketServer* server_;
235
  int port_ = -1;
236
};
237
238
74
InspectorSocketServer::InspectorSocketServer(
239
    std::unique_ptr<SocketServerDelegate> delegate, uv_loop_t* loop,
240
    const std::string& host, int port, FILE* out)
241
74
    : loop_(loop), delegate_(std::move(delegate)), host_(host), port_(port),
242
148
      next_session_id_(0), out_(out) {
243
74
  delegate_->AssignServer(this);
244
74
  state_ = ServerState::kNew;
245
74
}
246
247
InspectorSocketServer::~InspectorSocketServer() = default;
248
249
972
SocketSession* InspectorSocketServer::Session(int session_id) {
250
972
  auto it = connected_sessions_.find(session_id);
251
972
  return it == connected_sessions_.end() ? nullptr : it->second.second.get();
252
}
253
254
13
void InspectorSocketServer::SessionStarted(int session_id,
255
                                           const std::string& id,
256
                                           const std::string& ws_key) {
257
13
  SocketSession* session = Session(session_id);
258
13
  if (!TargetExists(id)) {
259
    session->Decline();
260
13
    return;
261
  }
262
13
  connected_sessions_[session_id].first = id;
263
13
  session->Accept(ws_key);
264
13
  delegate_->StartSession(session_id, id);
265
}
266
267
35
void InspectorSocketServer::SessionTerminated(int session_id) {
268
35
  if (Session(session_id) == nullptr) {
269
35
    return;
270
  }
271
35
  bool was_attached = connected_sessions_[session_id].first != "";
272
35
  if (was_attached) {
273
13
    delegate_->EndSession(session_id);
274
  }
275
35
  connected_sessions_.erase(session_id);
276
35
  if (connected_sessions_.empty()) {
277

45
    if (was_attached && state_ == ServerState::kRunning
278

34
        && !server_sockets_.empty()) {
279
1
      PrintDebuggerReadyMessage(host_, server_sockets_[0]->port(),
280
2
                                delegate_->GetTargetIds(), out_);
281
    }
282
33
    if (state_ == ServerState::kStopped) {
283
11
      delegate_.reset();
284
    }
285
  }
286
}
287
288
19
bool InspectorSocketServer::HandleGetRequest(int session_id,
289
                                             const std::string& host,
290
                                             const std::string& path) {
291
19
  SocketSession* session = Session(session_id);
292
19
  InspectorSocket* socket = session->ws_socket();
293
19
  const char* command = MatchPathSegment(path.c_str(), "/json");
294
19
  if (command == nullptr)
295
    return false;
296
297

19
  if (MatchPathSegment(command, "list") || command[0] == '\0') {
298
15
    SendListResponse(socket, host, session);
299
15
    return true;
300
4
  } else if (MatchPathSegment(command, "protocol")) {
301
    SendProtocolJson(socket);
302
    return true;
303
4
  } else if (MatchPathSegment(command, "version")) {
304
1
    SendVersionResponse(socket);
305
1
    return true;
306
  }
307
3
  return false;
308
}
309
310
15
void InspectorSocketServer::SendListResponse(InspectorSocket* socket,
311
                                             const std::string& host,
312
                                             SocketSession* session) {
313
15
  std::vector<std::map<std::string, std::string>> response;
314
30
  for (const std::string& id : delegate_->GetTargetIds()) {
315
15
    response.push_back(std::map<std::string, std::string>());
316
15
    std::map<std::string, std::string>& target_map = response.back();
317
15
    target_map["description"] = "node.js instance";
318
15
    target_map["faviconUrl"] = "https://nodejs.org/static/favicon.ico";
319
15
    target_map["id"] = id;
320
15
    target_map["title"] = delegate_->GetTargetTitle(id);
321
15
    Escape(&target_map["title"]);
322
15
    target_map["type"] = "node";
323
    // This attribute value is a "best effort" URL that is passed as a JSON
324
    // string. It is not guaranteed to resolve to a valid resource.
325
15
    target_map["url"] = delegate_->GetTargetUrl(id);
326
15
    Escape(&target_map["url"]);
327
328
15
    std::string detected_host = host;
329
15
    if (detected_host.empty()) {
330
      detected_host = FormatHostPort(socket->GetHost(),
331
                                     session->server_port());
332
    }
333
30
    std::string formatted_address = FormatAddress(detected_host, id, false);
334
30
    target_map["devtoolsFrontendUrl"] = GetFrontendURL(false,
335
15
                                                       formatted_address);
336
    // The compat URL is for Chrome browsers older than 66.0.3345.0
337
30
    target_map["devtoolsFrontendUrlCompat"] = GetFrontendURL(true,
338
15
                                                             formatted_address);
339
15
    target_map["webSocketDebuggerUrl"] = FormatAddress(detected_host, id, true);
340
30
  }
341
15
  SendHttpResponse(socket, MapsToString(response));
342
15
}
343
344
30
std::string InspectorSocketServer::GetFrontendURL(bool is_compat,
345
    const std::string &formatted_address) {
346
30
  std::ostringstream frontend_url;
347
30
  frontend_url << "chrome-devtools://devtools/bundled/";
348
30
  frontend_url << (is_compat ? "inspector" : "js_app");
349
30
  frontend_url << ".html?experiments=true&v8only=true&ws=";
350
30
  frontend_url << formatted_address;
351
30
  return frontend_url.str();
352
}
353
354
74
bool InspectorSocketServer::Start() {
355
74
  CHECK_NOT_NULL(delegate_);
356
74
  CHECK_EQ(state_, ServerState::kNew);
357
74
  std::unique_ptr<SocketServerDelegate> delegate_holder;
358
  // We will return it if startup is successful
359
74
  delegate_.swap(delegate_holder);
360
  struct addrinfo hints;
361
74
  memset(&hints, 0, sizeof(hints));
362
74
  hints.ai_flags = AI_NUMERICSERV;
363
74
  hints.ai_socktype = SOCK_STREAM;
364
  uv_getaddrinfo_t req;
365
148
  const std::string port_string = std::to_string(port_);
366
  int err = uv_getaddrinfo(loop_, &req, nullptr, host_.c_str(),
367
74
                           port_string.c_str(), &hints);
368
74
  if (err < 0) {
369
    if (out_ != nullptr) {
370
      fprintf(out_, "Unable to resolve \"%s\": %s\n", host_.c_str(),
371
              uv_strerror(err));
372
    }
373
    return false;
374
  }
375
150
  for (addrinfo* address = req.addrinfo; address != nullptr;
376
       address = address->ai_next) {
377
76
    auto server_socket = ServerSocketPtr(new ServerSocket(this));
378
76
    err = server_socket->Listen(address->ai_addr, loop_);
379
76
    if (err == 0)
380
75
      server_sockets_.push_back(std::move(server_socket));
381
76
  }
382
74
  uv_freeaddrinfo(req.addrinfo);
383
384
  // We only show error if we failed to start server on all addresses. We only
385
  // show one error, for the last address.
386
74
  if (server_sockets_.empty()) {
387
1
    if (out_ != nullptr) {
388
      fprintf(out_, "Starting inspector on %s:%d failed: %s\n",
389
1
              host_.c_str(), port_, uv_strerror(err));
390
1
      fflush(out_);
391
    }
392
1
    return false;
393
  }
394
73
  delegate_.swap(delegate_holder);
395
73
  state_ = ServerState::kRunning;
396
  // getaddrinfo sorts the addresses, so the first port is most relevant.
397
73
  PrintDebuggerReadyMessage(host_, server_sockets_[0]->port(),
398
146
                            delegate_->GetTargetIds(), out_);
399
147
  return true;
400
}
401
402
73
void InspectorSocketServer::Stop() {
403
73
  if (state_ == ServerState::kStopped)
404
73
    return;
405
73
  CHECK_EQ(state_, ServerState::kRunning);
406
73
  state_ = ServerState::kStopped;
407
73
  server_sockets_.clear();
408
73
  if (done())
409
62
    delegate_.reset();
410
}
411
412
4
void InspectorSocketServer::TerminateConnections() {
413
5
  for (const auto& key_value : connected_sessions_)
414
1
    key_value.second.second->Close();
415
4
}
416
417
13
bool InspectorSocketServer::TargetExists(const std::string& id) {
418
13
  const std::vector<std::string>& target_ids = delegate_->GetTargetIds();
419
13
  const auto& found = std::find(target_ids.begin(), target_ids.end(), id);
420
13
  return found != target_ids.end();
421
}
422
423
73
int InspectorSocketServer::Port() const {
424
73
  if (!server_sockets_.empty()) {
425
73
    return server_sockets_[0]->port();
426
  }
427
  return port_;
428
}
429
430
35
void InspectorSocketServer::Accept(int server_port,
431
                                   uv_stream_t* server_socket) {
432
  std::unique_ptr<SocketSession> session(
433
35
      new SocketSession(this, next_session_id_++, server_port));
434
435
  InspectorSocket::DelegatePointer delegate =
436
      InspectorSocket::DelegatePointer(
437
70
          new SocketSession::Delegate(this, session->id()));
438
439
  InspectorSocket::Pointer inspector =
440
70
      InspectorSocket::Accept(server_socket, std::move(delegate));
441
35
  if (inspector) {
442
35
    session->Own(std::move(inspector));
443
35
    connected_sessions_[session->id()].second = std::move(session);
444
35
  }
445
35
}
446
447
902
void InspectorSocketServer::Send(int session_id, const std::string& message) {
448
902
  SocketSession* session = Session(session_id);
449
902
  if (session != nullptr) {
450
902
    session->Send(message);
451
  }
452
902
}
453
454
76
void InspectorSocketServer::CloseServerSocket(ServerSocket* server) {
455
76
  server->Close();
456
76
}
457
458
// InspectorSession tracking
459
35
SocketSession::SocketSession(InspectorSocketServer* server, int id,
460
                             int server_port)
461
35
    : id_(id), server_port_(server_port) {}
462
463
902
void SocketSession::Send(const std::string& message) {
464
902
  ws_socket_->Write(message.data(), message.length());
465
902
}
466
467
19
void SocketSession::Delegate::OnHttpGet(const std::string& host,
468
                                        const std::string& path) {
469
19
  if (!server_->HandleGetRequest(session_id_, host, path))
470
3
    Session()->ws_socket()->CancelHandshake();
471
19
}
472
473
13
void SocketSession::Delegate::OnSocketUpgrade(const std::string& host,
474
                                              const std::string& path,
475
                                              const std::string& ws_key) {
476
13
  std::string id = path.empty() ? path : path.substr(1);
477
13
  server_->SessionStarted(session_id_, id, ws_key);
478
13
}
479
480
113
void SocketSession::Delegate::OnWsFrame(const std::vector<char>& data) {
481
  server_->MessageReceived(session_id_,
482
113
                           std::string(data.data(), data.size()));
483
113
}
484
485
// ServerSocket implementation
486
75
int ServerSocket::DetectPort() {
487
  sockaddr_storage addr;
488
75
  int len = sizeof(addr);
489
  int err = uv_tcp_getsockname(&tcp_socket_,
490
75
                               reinterpret_cast<struct sockaddr*>(&addr), &len);
491
75
  if (err != 0)
492
    return err;
493
  int port;
494
75
  if (addr.ss_family == AF_INET6)
495
6
    port = reinterpret_cast<const sockaddr_in6*>(&addr)->sin6_port;
496
  else
497
69
    port = reinterpret_cast<const sockaddr_in*>(&addr)->sin_port;
498
75
  port_ = ntohs(port);
499
75
  return err;
500
}
501
502
76
int ServerSocket::Listen(sockaddr* addr, uv_loop_t* loop) {
503
76
  uv_tcp_t* server = &tcp_socket_;
504
76
  CHECK_EQ(0, uv_tcp_init(loop, server));
505
76
  int err = uv_tcp_bind(server, addr, 0);
506
76
  if (err == 0) {
507
    // 511 is the value used by a 'net' module by default
508
    err = uv_listen(reinterpret_cast<uv_stream_t*>(server), 511,
509
76
                    ServerSocket::SocketConnectedCallback);
510
  }
511
76
  if (err == 0) {
512
75
    err = DetectPort();
513
  }
514
76
  return err;
515
}
516
517
// static
518
35
void ServerSocket::SocketConnectedCallback(uv_stream_t* tcp_socket,
519
                                           int status) {
520
35
  if (status == 0) {
521
35
    ServerSocket* server_socket = ServerSocket::FromTcpSocket(tcp_socket);
522
    // Memory is freed when the socket closes.
523
35
    server_socket->server_->Accept(server_socket->port_, tcp_socket);
524
  }
525
35
}
526
}  // namespace inspector
527
}  // namespace node