GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage/nodes/benchmark/out/../src/inspector_socket_server.cc Lines: 269 309 87.1 %
Date: 2017-06-14 Branches: 94 155 60.6 %

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
namespace {
16
17
static const uint8_t PROTOCOL_JSON[] = {
18
  #include "v8_inspector_protocol_json.h"  // NOLINT(build/include_order)
19
};
20
21
16
void Escape(std::string* string) {
22
1547
  for (char& c : *string) {
23

1531
    c = (c == '\"' || c == '\\') ? '_' : c;
24
  }
25
16
}
26
27
31
std::string GetWsUrl(const std::string& host, int port, const std::string& id) {
28
  char buf[1024];
29
31
  snprintf(buf, sizeof(buf), "%s:%d/%s", host.c_str(), port, id.c_str());
30
31
  return buf;
31
}
32
33
9
std::string MapToString(const std::map<std::string, std::string>& object) {
34
9
  bool first = true;
35
9
  std::ostringstream json;
36
9
  json << "{\n";
37
73
  for (const auto& name_value : object) {
38
64
    if (!first)
39
55
      json << ",\n";
40
64
    first = false;
41
64
    json << "  \"" << name_value.first << "\": \"";
42
64
    json << name_value.second << "\"";
43
  }
44
9
  json << "\n} ";
45
9
  return json.str();
46
}
47
48
8
std::string MapsToString(
49
    const std::vector<std::map<std::string, std::string>>& array) {
50
8
  bool first = true;
51
8
  std::ostringstream json;
52
8
  json << "[ ";
53
16
  for (const auto& object : array) {
54
8
    if (!first)
55
      json << ", ";
56
8
    first = false;
57
8
    json << MapToString(object);
58
  }
59
8
  json << "]\n\n";
60
8
  return json.str();
61
}
62
63
35
const char* MatchPathSegment(const char* path, const char* expected) {
64
35
  size_t len = strlen(expected);
65
35
  if (StringEqualNoCaseN(path, expected, len)) {
66
22
    if (path[len] == '/') return path + len + 1;
67
10
    if (path[len] == '\0') return path + len;
68
  }
69
13
  return nullptr;
70
}
71
72
43
void OnBufferAlloc(uv_handle_t* handle, size_t len, uv_buf_t* buf) {
73
43
  buf->base = new char[len];
74
43
  buf->len = len;
75
43
}
76
77
24
void PrintDebuggerReadyMessage(const std::string& host,
78
                               int port,
79
                               const std::vector<std::string>& ids,
80
                               FILE* out) {
81
24
  if (out == NULL) {
82
24
    return;
83
  }
84
48
  for (const std::string& id : ids) {
85
    fprintf(out, "Debugger listening on ws://%s\n",
86
24
            GetWsUrl(host, port, id).c_str());
87
  }
88
  fprintf(out, "For help see %s\n",
89
24
          "https://nodejs.org/en/docs/inspector");
90
24
  fflush(out);
91
}
92
93
9
void SendHttpResponse(InspectorSocket* socket, const std::string& response) {
94
  const char HEADERS[] = "HTTP/1.0 200 OK\r\n"
95
                         "Content-Type: application/json; charset=UTF-8\r\n"
96
                         "Cache-Control: no-cache\r\n"
97
                         "Content-Length: %zu\r\n"
98
9
                         "\r\n";
99
  char header[sizeof(HEADERS) + 20];
100
9
  int header_len = snprintf(header, sizeof(header), HEADERS, response.size());
101
9
  inspector_write(socket, header, header_len);
102
9
  inspector_write(socket, response.data(), response.size());
103
9
}
104
105
1
void SendVersionResponse(InspectorSocket* socket) {
106
1
  std::map<std::string, std::string> response;
107
1
  response["Browser"] = "node.js/" NODE_VERSION;
108
1
  response["Protocol-Version"] = "1.1";
109
1
  SendHttpResponse(socket, MapToString(response));
110
1
}
111
112
void SendProtocolJson(InspectorSocket* socket) {
113
  z_stream strm;
114
  strm.zalloc = Z_NULL;
115
  strm.zfree = Z_NULL;
116
  strm.opaque = Z_NULL;
117
  CHECK_EQ(Z_OK, inflateInit(&strm));
118
  static const size_t kDecompressedSize =
119
      PROTOCOL_JSON[0] * 0x10000u +
120
      PROTOCOL_JSON[1] * 0x100u +
121
      PROTOCOL_JSON[2];
122
  strm.next_in = const_cast<uint8_t*>(PROTOCOL_JSON + 3);
123
  strm.avail_in = sizeof(PROTOCOL_JSON) - 3;
124
  std::string data(kDecompressedSize, '\0');
125
  strm.next_out = reinterpret_cast<Byte*>(&data[0]);
126
  strm.avail_out = data.size();
127
  CHECK_EQ(Z_STREAM_END, inflate(&strm, Z_FINISH));
128
  CHECK_EQ(0, strm.avail_out);
129
  CHECK_EQ(Z_OK, inflateEnd(&strm));
130
  SendHttpResponse(socket, data);
131
}
132
133
7
int GetSocketHost(uv_tcp_t* socket, std::string* out_host) {
134
  char ip[INET6_ADDRSTRLEN];
135
  sockaddr_storage addr;
136
7
  int len = sizeof(addr);
137
  int err = uv_tcp_getsockname(socket,
138
                               reinterpret_cast<struct sockaddr*>(&addr),
139
7
                               &len);
140
7
  if (err != 0)
141
    return err;
142
7
  if (addr.ss_family == AF_INET6) {
143
    const sockaddr_in6* v6 = reinterpret_cast<const sockaddr_in6*>(&addr);
144
    err = uv_ip6_name(v6, ip, sizeof(ip));
145
  } else {
146
7
    const sockaddr_in* v4 = reinterpret_cast<const sockaddr_in*>(&addr);
147
7
    err = uv_ip4_name(v4, ip, sizeof(ip));
148
  }
149
7
  if (err != 0)
150
    return err;
151
7
  *out_host = ip;
152
7
  return err;
153
}
154
155
20
int GetPort(uv_tcp_t* socket, int* out_port) {
156
  sockaddr_storage addr;
157
20
  int len = sizeof(addr);
158
  int err = uv_tcp_getsockname(socket,
159
                               reinterpret_cast<struct sockaddr*>(&addr),
160
20
                               &len);
161
20
  if (err != 0)
162
    return err;
163
  int port;
164
20
  if (addr.ss_family == AF_INET6)
165
    port = reinterpret_cast<const sockaddr_in6*>(&addr)->sin6_port;
166
  else
167
20
    port = reinterpret_cast<const sockaddr_in*>(&addr)->sin_port;
168
20
  *out_port = ntohs(port);
169
20
  return err;
170
}
171
172
}  // namespace
173
174
175
3
class Closer {
176
 public:
177
3
  explicit Closer(InspectorSocketServer* server) : server_(server),
178
3
                                                   close_count_(0) { }
179
180
3
  void AddCallback(InspectorSocketServer::ServerCallback callback) {
181
3
    if (callback == nullptr)
182
6
      return;
183
    callbacks_.insert(callback);
184
  }
185
186
3
  void DecreaseExpectedCount() {
187
3
    --close_count_;
188
3
    NotifyIfDone();
189
3
  }
190
191
3
  void IncreaseExpectedCount() {
192
3
    ++close_count_;
193
3
  }
194
195
6
  void NotifyIfDone() {
196
6
    if (close_count_ == 0) {
197
3
      for (auto callback : callbacks_) {
198
        callback(server_);
199
      }
200
3
      InspectorSocketServer* server = server_;
201
3
      delete server->closer_;
202
3
      server->closer_ = nullptr;
203
    }
204
6
  }
205
206
 private:
207
  InspectorSocketServer* server_;
208
  std::set<InspectorSocketServer::ServerCallback> callbacks_;
209
  int close_count_;
210
};
211
212
20
class SocketSession {
213
 public:
214
  SocketSession(InspectorSocketServer* server, int id);
215
  void Close();
216
  void Declined() { state_ = State::kDeclined; }
217
114
  static SocketSession* From(InspectorSocket* socket) {
218
114
    return node::ContainerOf(&SocketSession::socket_, socket);
219
  }
220
  void FrontendConnected();
221
37
  InspectorSocketServer* GetServer() { return server_; }
222
30
  int Id() { return id_; }
223
  void Send(const std::string& message);
224
5
  void SetTargetId(const std::string& target_id) {
225
5
    CHECK(target_id_.empty());
226
5
    target_id_ = target_id;
227
5
  }
228
20
  InspectorSocket* Socket() { return &socket_; }
229
1
  const std::string TargetId() { return target_id_; }
230
231
 private:
232
  enum class State { kHttp, kWebSocket, kClosing, kEOF, kDeclined };
233
  static void CloseCallback(InspectorSocket* socket, int code);
234
  static void ReadCallback(uv_stream_t* stream, ssize_t read,
235
                           const uv_buf_t* buf);
236
  void OnRemoteDataIO(ssize_t read, const uv_buf_t* buf);
237
  const int id_;
238
  InspectorSocket socket_;
239
  InspectorSocketServer* server_;
240
  std::string target_id_;
241
  State state_;
242
};
243
244
20
InspectorSocketServer::InspectorSocketServer(SocketServerDelegate* delegate,
245
                                             uv_loop_t* loop,
246
                                             const std::string& host,
247
                                             int port,
248
                                             FILE* out) : loop_(loop),
249
                                                          delegate_(delegate),
250
                                                          host_(host),
251
                                                          port_(port),
252
                                                          server_(uv_tcp_t()),
253
                                                          closer_(nullptr),
254
                                                          next_session_id_(0),
255
20
                                                          out_(out) {
256
20
  state_ = ServerState::kNew;
257
20
}
258
259
// static
260
37
bool InspectorSocketServer::HandshakeCallback(InspectorSocket* socket,
261
                                              inspector_handshake_event event,
262
                                              const std::string& path) {
263
37
  InspectorSocketServer* server = SocketSession::From(socket)->GetServer();
264
37
  const std::string& id = path.empty() ? path : path.substr(1);
265

37
  switch (event) {
266
  case kInspectorHandshakeHttpGet:
267
12
    return server->RespondToGet(socket, path);
268
  case kInspectorHandshakeUpgrading:
269
5
    return server->SessionStarted(SocketSession::From(socket), id);
270
  case kInspectorHandshakeUpgraded:
271
5
    SocketSession::From(socket)->FrontendConnected();
272
5
    return true;
273
  case kInspectorHandshakeFailed:
274
15
    server->SessionTerminated(SocketSession::From(socket));
275
15
    return false;
276
  default:
277
    UNREACHABLE();
278
    return false;
279
37
  }
280
}
281
282
5
bool InspectorSocketServer::SessionStarted(SocketSession* session,
283
                                           const std::string& id) {
284
5
  bool connected = false;
285
5
  if (TargetExists(id)) {
286
5
    connected = delegate_->StartSession(session->Id(), id);
287
  }
288
5
  if (connected) {
289
5
    connected_sessions_[session->Id()] = session;
290
5
    session->SetTargetId(id);
291
  } else {
292
    session->Declined();
293
  }
294
5
  return connected;
295
}
296
297
20
void InspectorSocketServer::SessionTerminated(SocketSession* session) {
298
20
  int id = session->Id();
299
20
  if (connected_sessions_.erase(id) != 0) {
300
5
    delegate_->EndSession(id);
301
5
    if (connected_sessions_.empty()) {
302
5
      if (state_ == ServerState::kRunning) {
303
        PrintDebuggerReadyMessage(host_, port_,
304
4
                                  delegate_->GetTargetIds(), out_);
305
      }
306
5
      if (state_ == ServerState::kStopped) {
307
1
        delegate_->ServerDone();
308
      }
309
    }
310
  }
311
20
  delete session;
312
20
}
313
314
12
bool InspectorSocketServer::RespondToGet(InspectorSocket* socket,
315
                                         const std::string& path) {
316
12
  const char* command = MatchPathSegment(path.c_str(), "/json");
317
12
  if (command == nullptr)
318
    return false;
319
320

12
  if (MatchPathSegment(command, "list") || command[0] == '\0') {
321
8
    SendListResponse(socket);
322
8
    return true;
323
4
  } else if (MatchPathSegment(command, "protocol")) {
324
    SendProtocolJson(socket);
325
    return true;
326
4
  } else if (MatchPathSegment(command, "version")) {
327
1
    SendVersionResponse(socket);
328
1
    return true;
329
3
  } else if (const char* target_id = MatchPathSegment(command, "activate")) {
330
2
    if (TargetExists(target_id)) {
331
      SendHttpResponse(socket, "Target activated");
332
      return true;
333
    }
334
2
    return false;
335
  }
336
1
  return false;
337
}
338
339
8
void InspectorSocketServer::SendListResponse(InspectorSocket* socket) {
340
8
  std::vector<std::map<std::string, std::string>> response;
341
16
  for (const std::string& id : delegate_->GetTargetIds()) {
342
8
    response.push_back(std::map<std::string, std::string>());
343
8
    std::map<std::string, std::string>& target_map = response.back();
344
8
    target_map["description"] = "node.js instance";
345
8
    target_map["faviconUrl"] = "https://nodejs.org/static/favicon.ico";
346
8
    target_map["id"] = id;
347
8
    target_map["title"] = delegate_->GetTargetTitle(id);
348
8
    Escape(&target_map["title"]);
349
8
    target_map["type"] = "node";
350
    // This attribute value is a "best effort" URL that is passed as a JSON
351
    // string. It is not guaranteed to resolve to a valid resource.
352
8
    target_map["url"] = delegate_->GetTargetUrl(id);
353
8
    Escape(&target_map["url"]);
354
355
8
    bool connected = false;
356
8
    for (const auto& session : connected_sessions_) {
357
1
      if (session.second->TargetId() == id) {
358
1
        connected = true;
359
1
        break;
360
      }
361
    }
362
8
    if (!connected) {
363
7
      std::string host;
364
7
      GetSocketHost(&socket->tcp, &host);
365
14
      std::string address = GetWsUrl(host, port_, id);
366
14
      std::ostringstream frontend_url;
367
7
      frontend_url << "chrome-devtools://devtools/bundled";
368
7
      frontend_url << "/inspector.html?experiments=true&v8only=true&ws=";
369
7
      frontend_url << address;
370
7
      target_map["devtoolsFrontendUrl"] += frontend_url.str();
371
14
      target_map["webSocketDebuggerUrl"] = "ws://" + address;
372
    }
373
8
  }
374
8
  SendHttpResponse(socket, MapsToString(response));
375
8
}
376
377
20
bool InspectorSocketServer::Start() {
378
20
  CHECK_EQ(state_, ServerState::kNew);
379
  sockaddr_in addr;
380
20
  uv_tcp_init(loop_, &server_);
381
20
  uv_ip4_addr(host_.c_str(), port_, &addr);
382
  int err = uv_tcp_bind(&server_,
383
20
                        reinterpret_cast<const struct sockaddr*>(&addr), 0);
384
20
  if (err == 0)
385
20
    err = GetPort(&server_, &port_);
386
20
  if (err == 0) {
387
    err = uv_listen(reinterpret_cast<uv_stream_t*>(&server_), 1,
388
20
                    SocketConnectedCallback);
389
  }
390

20
  if (err == 0 && connected_sessions_.empty()) {
391
20
    state_ = ServerState::kRunning;
392
20
    PrintDebuggerReadyMessage(host_, port_, delegate_->GetTargetIds(), out_);
393
  }
394

20
  if (err != 0 && connected_sessions_.empty()) {
395
    if (out_ != NULL) {
396
      fprintf(out_, "Starting inspector on %s:%d failed: %s\n",
397
              host_.c_str(), port_, uv_strerror(err));
398
      fflush(out_);
399
    }
400
    uv_close(reinterpret_cast<uv_handle_t*>(&server_), nullptr);
401
    return false;
402
  }
403
20
  return true;
404
}
405
406
3
void InspectorSocketServer::Stop(ServerCallback cb) {
407
3
  CHECK_EQ(state_, ServerState::kRunning);
408
3
  if (closer_ == nullptr) {
409
3
    closer_ = new Closer(this);
410
  }
411
3
  closer_->AddCallback(cb);
412
3
  closer_->IncreaseExpectedCount();
413
3
  state_ = ServerState::kStopping;
414
3
  uv_close(reinterpret_cast<uv_handle_t*>(&server_), ServerClosedCallback);
415
3
  closer_->NotifyIfDone();
416
3
}
417
418
3
void InspectorSocketServer::TerminateConnections() {
419
4
  for (const auto& session : connected_sessions_) {
420
1
    session.second->Close();
421
  }
422
3
}
423
424
7
bool InspectorSocketServer::TargetExists(const std::string& id) {
425
7
  const std::vector<std::string>& target_ids = delegate_->GetTargetIds();
426
7
  const auto& found = std::find(target_ids.begin(), target_ids.end(), id);
427
7
  return found != target_ids.end();
428
}
429
430
383
void InspectorSocketServer::Send(int session_id, const std::string& message) {
431
383
  auto session_iterator = connected_sessions_.find(session_id);
432
383
  if (session_iterator != connected_sessions_.end()) {
433
383
    session_iterator->second->Send(message);
434
  }
435
383
}
436
437
// static
438
3
void InspectorSocketServer::ServerClosedCallback(uv_handle_t* server) {
439
3
  InspectorSocketServer* socket_server = InspectorSocketServer::From(server);
440
3
  CHECK_EQ(socket_server->state_, ServerState::kStopping);
441
3
  if (socket_server->closer_) {
442
3
    socket_server->closer_->DecreaseExpectedCount();
443
  }
444
3
  if (socket_server->connected_sessions_.empty()) {
445
2
    socket_server->delegate_->ServerDone();
446
  }
447
3
  socket_server->state_ = ServerState::kStopped;
448
3
}
449
450
// static
451
20
void InspectorSocketServer::SocketConnectedCallback(uv_stream_t* server,
452
                                                    int status) {
453
20
  if (status == 0) {
454
20
    InspectorSocketServer* socket_server = InspectorSocketServer::From(server);
455
    // Memory is freed when the socket closes.
456
    SocketSession* session =
457
20
        new SocketSession(socket_server, socket_server->next_session_id_++);
458
20
    if (inspector_accept(server, session->Socket(), HandshakeCallback) != 0) {
459
      delete session;
460
    }
461
  }
462
20
}
463
464
// InspectorSession tracking
465
20
SocketSession::SocketSession(InspectorSocketServer* server, int id)
466
                             : id_(id), server_(server),
467
20
                               state_(State::kHttp) { }
468
469
5
void SocketSession::Close() {
470
5
  CHECK_NE(state_, State::kClosing);
471
5
  state_ = State::kClosing;
472
5
  inspector_close(&socket_, CloseCallback);
473
5
}
474
475
// static
476
5
void SocketSession::CloseCallback(InspectorSocket* socket, int code) {
477
5
  SocketSession* session = SocketSession::From(socket);
478
5
  CHECK_EQ(State::kClosing, session->state_);
479
5
  session->server_->SessionTerminated(session);
480
5
}
481
482
5
void SocketSession::FrontendConnected() {
483
5
  CHECK_EQ(State::kHttp, state_);
484
5
  state_ = State::kWebSocket;
485
5
  inspector_read_start(&socket_, OnBufferAlloc, ReadCallback);
486
5
}
487
488
// static
489
47
void SocketSession::ReadCallback(uv_stream_t* stream, ssize_t read,
490
                                 const uv_buf_t* buf) {
491
47
  InspectorSocket* socket = inspector_from_stream(stream);
492
47
  SocketSession::From(socket)->OnRemoteDataIO(read, buf);
493
47
}
494
495
47
void SocketSession::OnRemoteDataIO(ssize_t read, const uv_buf_t* buf) {
496
47
  if (read > 0) {
497
43
    server_->Delegate()->MessageReceived(id_, std::string(buf->base, read));
498
  } else {
499
4
    Close();
500
  }
501

47
  if (buf != nullptr && buf->base != nullptr)
502
43
    delete[] buf->base;
503
47
}
504
505
383
void SocketSession::Send(const std::string& message) {
506
383
  inspector_write(&socket_, message.data(), message.length());
507
383
}
508
509
}  // namespace inspector
510
}  // namespace node