GCC Code Coverage Report
Directory: ../src/ Exec Total Coverage
File: /home/node-core-coverage/node-core-coverage/workdir/node/out/../src/inspector_socket_server.cc Lines: 25 248 10.1 %
Date: 2016-12-18 Branches: 10 149 6.7 %

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
void Escape(std::string* string) {
22
  for (char& c : *string) {
23
    c = (c == '\"' || c == '\\') ? '_' : c;
24
  }
25
}
26
27
7
std::string GetWsUrl(int port, const std::string& id) {
28
  char buf[1024];
29
14
  snprintf(buf, sizeof(buf), "127.0.0.1:%d/%s", port, id.c_str());
30
21
  return buf;
31
}
32
33
std::string MapToString(const std::map<std::string, std::string>& object) {
34
  bool first = true;
35
  std::ostringstream json;
36
  json << "{\n";
37
  for (const auto& name_value : object) {
38
    if (!first)
39
      json << ",\n";
40
    first = false;
41
    json << "  \"" << name_value.first << "\": \"";
42
    json << name_value.second << "\"";
43
  }
44
  json << "\n} ";
45
  return json.str();
46
}
47
48
std::string MapsToString(
49
    const std::vector<std::map<std::string, std::string>>& array) {
50
  bool first = true;
51
  std::ostringstream json;
52
  json << "[ ";
53
  for (const auto& object : array) {
54
    if (!first)
55
      json << ", ";
56
    first = false;
57
    json << MapToString(object);
58
  }
59
  json << "]\n\n";
60
  return json.str();
61
}
62
63
const char* MatchPathSegment(const char* path, const char* expected) {
64
  size_t len = strlen(expected);
65
  if (StringEqualNoCaseN(path, expected, len)) {
66
    if (path[len] == '/') return path + len + 1;
67
    if (path[len] == '\0') return path + len;
68
  }
69
  return nullptr;
70
}
71
72
void OnBufferAlloc(uv_handle_t* handle, size_t len, uv_buf_t* buf) {
73
  buf->base = new char[len];
74
  buf->len = len;
75
}
76
77
7
void PrintDebuggerReadyMessage(int port, const std::vector<std::string>& ids) {
78
  fprintf(stderr,
79
          "Debugger listening on port %d.\n"
80
          "Warning: This is an experimental feature "
81
          "and could change at any time.\n",
82
14
          port);
83
14
  if (ids.size() == 1)
84
7
    fprintf(stderr, "To start debugging, open the following URL in Chrome:\n");
85
14
  if (ids.size() > 1)
86
    fprintf(stderr, "To start debugging, open the following URLs in Chrome:\n");
87
35
  for (const std::string& id : ids) {
88
7
    fprintf(stderr,
89
            "    chrome-devtools://devtools/bundled/inspector.html?"
90
28
            "experiments=true&v8only=true&ws=%s\n", GetWsUrl(port, id).c_str());
91
  }
92
7
  fflush(stderr);
93
7
}
94
95
void SendHttpResponse(InspectorSocket* socket, const std::string& response) {
96
  const char HEADERS[] = "HTTP/1.0 200 OK\r\n"
97
                         "Content-Type: application/json; charset=UTF-8\r\n"
98
                         "Cache-Control: no-cache\r\n"
99
                         "Content-Length: %zu\r\n"
100
                         "\r\n";
101
  char header[sizeof(HEADERS) + 20];
102
  int header_len = snprintf(header, sizeof(header), HEADERS, response.size());
103
  inspector_write(socket, header, header_len);
104
  inspector_write(socket, response.data(), response.size());
105
}
106
107
void SendVersionResponse(InspectorSocket* socket) {
108
  std::map<std::string, std::string> response;
109
  response["Browser"] = "node.js/" NODE_VERSION;
110
  response["Protocol-Version"] = "1.1";
111
  SendHttpResponse(socket, MapToString(response));
112
}
113
114
void SendProtocolJson(InspectorSocket* socket) {
115
  z_stream strm;
116
  strm.zalloc = Z_NULL;
117
  strm.zfree = Z_NULL;
118
  strm.opaque = Z_NULL;
119
  CHECK_EQ(Z_OK, inflateInit(&strm));
120
  static const size_t kDecompressedSize =
121
      PROTOCOL_JSON[0] * 0x10000u +
122
      PROTOCOL_JSON[1] * 0x100u +
123
      PROTOCOL_JSON[2];
124
  strm.next_in = const_cast<uint8_t*>(PROTOCOL_JSON + 3);
125
  strm.avail_in = sizeof(PROTOCOL_JSON) - 3;
126
  std::string data(kDecompressedSize, '\0');
127
  strm.next_out = reinterpret_cast<Byte*>(&data[0]);
128
  strm.avail_out = data.size();
129
  CHECK_EQ(Z_STREAM_END, inflate(&strm, Z_FINISH));
130
  CHECK_EQ(0, strm.avail_out);
131
  CHECK_EQ(Z_OK, inflateEnd(&strm));
132
  SendHttpResponse(socket, data);
133
}
134
135
}  // namespace
136
137
138
class Closer {
139
 public:
140
  explicit Closer(InspectorSocketServer* server) : server_(server),
141
                                                   close_count_(0) { }
142
143
  void AddCallback(InspectorSocketServer::ServerCallback callback) {
144
    if (callback == nullptr)
145
      return;
146
    callbacks_.insert(callback);
147
  }
148
149
  void DecreaseExpectedCount() {
150
    --close_count_;
151
    NotifyIfDone();
152
  }
153
154
  void IncreaseExpectedCount() {
155
    ++close_count_;
156
  }
157
158
  void NotifyIfDone() {
159
    if (close_count_ == 0) {
160
      for (auto callback : callbacks_) {
161
        callback(server_);
162
      }
163
      InspectorSocketServer* server = server_;
164
      delete server->closer_;
165
      server->closer_ = nullptr;
166
    }
167
  }
168
169
 private:
170
  InspectorSocketServer* server_;
171
  std::set<InspectorSocketServer::ServerCallback> callbacks_;
172
  int close_count_;
173
};
174
175
class SocketSession {
176
 public:
177
  SocketSession(InspectorSocketServer* server, int id);
178
  void Close(bool socket_cleanup, Closer* closer);
179
  void Declined() { state_ = State::kDeclined; }
180
  static SocketSession* From(InspectorSocket* socket) {
181
    return node::ContainerOf(&SocketSession::socket_, socket);
182
  }
183
  void FrontendConnected();
184
  InspectorSocketServer* GetServer() { return server_; }
185
  int Id() { return id_; }
186
  void Send(const std::string& message);
187
  void SetTargetId(const std::string& target_id) {
188
    CHECK(target_id_.empty());
189
    target_id_ = target_id;
190
  }
191
  InspectorSocket* Socket() { return &socket_; }
192
  const std::string TargetId() { return target_id_; }
193
194
 private:
195
  enum class State { kHttp, kWebSocket, kClosing, kEOF, kDeclined };
196
  static void CloseCallback_(InspectorSocket* socket, int code);
197
  static void ReadCallback_(uv_stream_t* stream, ssize_t read,
198
                            const uv_buf_t* buf);
199
  void OnRemoteDataIO(InspectorSocket* socket, ssize_t read,
200
                      const uv_buf_t* buf);
201
  const int id_;
202
  Closer* closer_;
203
  InspectorSocket socket_;
204
  InspectorSocketServer* server_;
205
  std::string target_id_;
206
  State state_;
207
};
208
209
7
InspectorSocketServer::InspectorSocketServer(SocketServerDelegate* delegate,
210
                                             int port) : loop_(nullptr),
211
                                                         delegate_(delegate),
212
                                                         port_(port),
213
                                                         server_(uv_tcp_t()),
214
                                                         closer_(nullptr),
215
21
                                                         next_session_id_(0) { }
216
217
218
// static
219
bool InspectorSocketServer::HandshakeCallback(InspectorSocket* socket,
220
                                              inspector_handshake_event event,
221
                                              const std::string& path) {
222
  InspectorSocketServer* server = SocketSession::From(socket)->GetServer();
223
  const std::string& id = path.empty() ? path : path.substr(1);
224
  switch (event) {
225
  case kInspectorHandshakeHttpGet:
226
    return server->RespondToGet(socket, path);
227
  case kInspectorHandshakeUpgrading:
228
    return server->SessionStarted(SocketSession::From(socket), id);
229
  case kInspectorHandshakeUpgraded:
230
    SocketSession::From(socket)->FrontendConnected();
231
    return true;
232
  case kInspectorHandshakeFailed:
233
    SocketSession::From(socket)->Close(false, nullptr);
234
    return false;
235
  default:
236
    UNREACHABLE();
237
    return false;
238
  }
239
}
240
241
bool InspectorSocketServer::SessionStarted(SocketSession* session,
242
                                           const std::string& id) {
243
  bool connected = false;
244
  if (TargetExists(id)) {
245
    connected = delegate_->StartSession(session->Id(), id);
246
  }
247
  if (connected) {
248
    connected_sessions_[session->Id()] = session;
249
    session->SetTargetId(id);
250
  } else {
251
    session->Declined();
252
  }
253
  return connected;
254
}
255
256
void InspectorSocketServer::SessionTerminated(int session_id) {
257
  if (connected_sessions_.erase(session_id) == 0) {
258
    return;
259
  }
260
  delegate_->EndSession(session_id);
261
  if (connected_sessions_.empty() &&
262
      uv_is_active(reinterpret_cast<uv_handle_t*>(&server_))) {
263
    PrintDebuggerReadyMessage(port_, delegate_->GetTargetIds());
264
  }
265
}
266
267
bool InspectorSocketServer::RespondToGet(InspectorSocket* socket,
268
                                         const std::string& path) {
269
  const char* command = MatchPathSegment(path.c_str(), "/json");
270
  if (command == nullptr)
271
    return false;
272
273
  if (MatchPathSegment(command, "list") || command[0] == '\0') {
274
    SendListResponse(socket);
275
    return true;
276
  } else if (MatchPathSegment(command, "protocol")) {
277
    SendProtocolJson(socket);
278
    return true;
279
  } else if (MatchPathSegment(command, "version")) {
280
    SendVersionResponse(socket);
281
    return true;
282
  } else if (const char* target_id = MatchPathSegment(command, "activate")) {
283
    if (TargetExists(target_id)) {
284
      SendHttpResponse(socket, "Target activated");
285
      return true;
286
    }
287
    return false;
288
  }
289
  return false;
290
}
291
292
void InspectorSocketServer::SendListResponse(InspectorSocket* socket) {
293
  std::vector<std::map<std::string, std::string>> response;
294
  for (const std::string& id : delegate_->GetTargetIds()) {
295
    response.push_back(std::map<std::string, std::string>());
296
    std::map<std::string, std::string>& target_map = response.back();
297
    target_map["description"] = "node.js instance";
298
    target_map["faviconUrl"] = "https://nodejs.org/static/favicon.ico";
299
    target_map["id"] = id;
300
    target_map["title"] = delegate_->GetTargetTitle(id);
301
    Escape(&target_map["title"]);
302
    target_map["type"] = "node";
303
    // This attribute value is a "best effort" URL that is passed as a JSON
304
    // string. It is not guaranteed to resolve to a valid resource.
305
    target_map["url"] = delegate_->GetTargetUrl(id);
306
    Escape(&target_map["url"]);
307
308
    bool connected = false;
309
    for (const auto& session : connected_sessions_) {
310
      if (session.second->TargetId() == id) {
311
        connected = true;
312
        break;
313
      }
314
    }
315
    if (!connected) {
316
      std::string address = GetWsUrl(port_, id);
317
      std::ostringstream frontend_url;
318
      frontend_url << "chrome-devtools://devtools/bundled";
319
      frontend_url << "/inspector.html?experiments=true&v8only=true&ws=";
320
      frontend_url << address;
321
      target_map["devtoolsFrontendUrl"] += frontend_url.str();
322
      target_map["webSocketDebuggerUrl"] = "ws://" + address;
323
    }
324
  }
325
  SendHttpResponse(socket, MapsToString(response));
326
}
327
328
7
bool InspectorSocketServer::Start(uv_loop_t* loop) {
329
7
  loop_ = loop;
330
  sockaddr_in addr;
331
7
  uv_tcp_init(loop_, &server_);
332
7
  uv_ip4_addr("0.0.0.0", port_, &addr);
333
  int err = uv_tcp_bind(&server_,
334
7
                        reinterpret_cast<const struct sockaddr*>(&addr), 0);
335
7
  if (err == 0) {
336
    err = uv_listen(reinterpret_cast<uv_stream_t*>(&server_), 1,
337
7
                    SocketConnectedCallback);
338
  }
339

14
  if (err == 0 && connected_sessions_.empty()) {
340
7
    PrintDebuggerReadyMessage(port_, delegate_->GetTargetIds());
341
  }
342

7
  if (err != 0 && connected_sessions_.empty()) {
343
    fprintf(stderr, "Unable to open devtools socket: %s\n", uv_strerror(err));
344
    uv_close(reinterpret_cast<uv_handle_t*>(&server_), nullptr);
345
    return false;
346
  }
347
  return true;
348
}
349
350
void InspectorSocketServer::Stop(ServerCallback cb) {
351
  if (closer_ == nullptr) {
352
    closer_ = new Closer(this);
353
  }
354
  closer_->AddCallback(cb);
355
356
  uv_handle_t* handle = reinterpret_cast<uv_handle_t*>(&server_);
357
  if (uv_is_active(handle)) {
358
    closer_->IncreaseExpectedCount();
359
    uv_close(reinterpret_cast<uv_handle_t*>(&server_), ServerClosedCallback);
360
  }
361
  closer_->NotifyIfDone();
362
}
363
364
void InspectorSocketServer::TerminateConnections(ServerCallback cb) {
365
  if (closer_ == nullptr) {
366
    closer_ = new Closer(this);
367
  }
368
  closer_->AddCallback(cb);
369
  std::map<int, SocketSession*> sessions;
370
  std::swap(sessions, connected_sessions_);
371
  for (const auto& session : sessions) {
372
    int id = session.second->Id();
373
    session.second->Close(true, closer_);
374
    delegate_->EndSession(id);
375
  }
376
  closer_->NotifyIfDone();
377
}
378
379
bool InspectorSocketServer::TargetExists(const std::string& id) {
380
  const std::vector<std::string>& target_ids = delegate_->GetTargetIds();
381
  const auto& found = std::find(target_ids.begin(), target_ids.end(), id);
382
  return found != target_ids.end();
383
}
384
385
void InspectorSocketServer::Send(int session_id, const std::string& message) {
386
  auto session_iterator = connected_sessions_.find(session_id);
387
  if (session_iterator != connected_sessions_.end()) {
388
    session_iterator->second->Send(message);
389
  }
390
}
391
392
// static
393
void InspectorSocketServer::ServerClosedCallback(uv_handle_t* server) {
394
  InspectorSocketServer* socket_server = InspectorSocketServer::From(server);
395
  if (socket_server->closer_)
396
    socket_server->closer_->DecreaseExpectedCount();
397
}
398
399
// static
400
void InspectorSocketServer::SocketConnectedCallback(uv_stream_t* server,
401
                                                    int status) {
402
  if (status == 0) {
403
    InspectorSocketServer* socket_server = InspectorSocketServer::From(server);
404
    // Memory is freed when the socket closes.
405
    SocketSession* session =
406
        new SocketSession(socket_server, socket_server->next_session_id_++);
407
    if (inspector_accept(server, session->Socket(), HandshakeCallback) != 0) {
408
      delete session;
409
    }
410
  }
411
}
412
413
// InspectorSession tracking
414
SocketSession::SocketSession(InspectorSocketServer* server, int id)
415
                             : id_(id), closer_(nullptr), server_(server),
416
                               state_(State::kHttp) { }
417
418
void SocketSession::Close(bool socket_cleanup, Closer* closer) {
419
  CHECK_EQ(closer_, nullptr);
420
  CHECK_NE(state_, State::kClosing);
421
  server_->SessionTerminated(id_);
422
  if (socket_cleanup) {
423
    state_ = State::kClosing;
424
    closer_ = closer;
425
    if (closer_ != nullptr)
426
      closer->IncreaseExpectedCount();
427
    inspector_close(&socket_, CloseCallback_);
428
  } else {
429
    delete this;
430
  }
431
}
432
433
// static
434
void SocketSession::CloseCallback_(InspectorSocket* socket, int code) {
435
  SocketSession* session = SocketSession::From(socket);
436
  CHECK_EQ(State::kClosing, session->state_);
437
  Closer* closer = session->closer_;
438
  if (closer != nullptr)
439
    closer->DecreaseExpectedCount();
440
  delete session;
441
}
442
443
void SocketSession::FrontendConnected() {
444
  CHECK_EQ(State::kHttp, state_);
445
  state_ = State::kWebSocket;
446
  inspector_read_start(&socket_, OnBufferAlloc, ReadCallback_);
447
}
448
449
// static
450
void SocketSession::ReadCallback_(uv_stream_t* stream, ssize_t read,
451
                                  const uv_buf_t* buf) {
452
  InspectorSocket* socket = inspector_from_stream(stream);
453
  SocketSession::From(socket)->OnRemoteDataIO(socket, read, buf);
454
}
455
456
void SocketSession::OnRemoteDataIO(InspectorSocket* socket, ssize_t read,
457
                                   const uv_buf_t* buf) {
458
  if (read > 0) {
459
    server_->Delegate()->MessageReceived(id_, std::string(buf->base, read));
460
  } else {
461
    server_->SessionTerminated(id_);
462
    Close(true, nullptr);
463
  }
464
  if (buf != nullptr && buf->base != nullptr)
465
    delete[] buf->base;
466
}
467
468
void SocketSession::Send(const std::string& message) {
469
  inspector_write(&socket_, message.data(), message.length());
470
}
471
472
}  // namespace inspector
473
}  // namespace node