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: 275 309 89.0 %
Date: 2019-05-05 22:32:45 Branches: 86 130 66.2 %

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

44
    if (was_attached && state_ == ServerState::kRunning
281

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

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