GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/node-core-coverage/node-core-coverage/workdir/node/out/../src/inspector_socket.cc Lines: 0 292 0.0 %
Date: 2016-09-05 Branches: 0 153 0.0 %

Line Branch Exec Source
1
#include "inspector_socket.h"
2
#include "util.h"
3
#include "util-inl.h"
4
5
#define NODE_WANT_INTERNALS 1
6
#include "base64.h"
7
8
#include "openssl/sha.h"  // Sha-1 hash
9
10
#include <string.h>
11
#include <vector>
12
13
#define ACCEPT_KEY_LENGTH base64_encoded_size(20)
14
#define BUFFER_GROWTH_CHUNK_SIZE 1024
15
16
#define DUMP_READS 0
17
#define DUMP_WRITES 0
18
19
static const char CLOSE_FRAME[] = {'\x88', '\x00'};
20
21
enum ws_decode_result {
22
  FRAME_OK, FRAME_INCOMPLETE, FRAME_CLOSE, FRAME_ERROR
23
};
24
25
#if DUMP_READS || DUMP_WRITES
26
static void dump_hex(const char* buf, size_t len) {
27
  const char* ptr = buf;
28
  const char* end = ptr + len;
29
  const char* cptr;
30
  char c;
31
  int i;
32
33
  while (ptr < end) {
34
    cptr = ptr;
35
    for (i = 0; i < 16 && ptr < end; i++) {
36
      printf("%2.2X  ", *(ptr++));
37
    }
38
    for (i = 72 - (i * 4); i > 0; i--) {
39
      printf(" ");
40
    }
41
    for (i = 0; i < 16 && cptr < end; i++) {
42
      c = *(cptr++);
43
      printf("%c", (c > 0x19) ? c : '.');
44
    }
45
    printf("\n");
46
  }
47
  printf("\n\n");
48
}
49
#endif
50
51
static void remove_from_beginning(std::vector<char>* buffer, size_t count) {
52
  buffer->erase(buffer->begin(), buffer->begin() + count);
53
}
54
55
static void dispose_inspector(uv_handle_t* handle) {
56
  inspector_socket_t* inspector = inspector_from_stream(handle);
57
  inspector_cb close =
58
      inspector->ws_mode ? inspector->ws_state->close_cb : nullptr;
59
  inspector->buffer.clear();
60
  delete inspector->ws_state;
61
  inspector->ws_state = nullptr;
62
  if (close) {
63
    close(inspector, 0);
64
  }
65
}
66
67
static void close_connection(inspector_socket_t* inspector) {
68
  uv_handle_t* socket = reinterpret_cast<uv_handle_t*>(&inspector->client);
69
  if (!uv_is_closing(socket)) {
70
    uv_read_stop(reinterpret_cast<uv_stream_t*>(socket));
71
    uv_close(socket, dispose_inspector);
72
  }
73
}
74
75
struct WriteRequest {
76
  WriteRequest(inspector_socket_t* inspector, const char* data, size_t size)
77
      : inspector(inspector)
78
      , storage(data, data + size)
79
      , buf(uv_buf_init(&storage[0], storage.size())) {}
80
81
  static WriteRequest* from_write_req(uv_write_t* req) {
82
    return node::ContainerOf(&WriteRequest::req, req);
83
  }
84
85
  inspector_socket_t* const inspector;
86
  std::vector<char> storage;
87
  uv_write_t req;
88
  uv_buf_t buf;
89
};
90
91
// Cleanup
92
static void write_request_cleanup(uv_write_t* req, int status) {
93
  delete WriteRequest::from_write_req(req);
94
}
95
96
static int write_to_client(inspector_socket_t* inspector,
97
                           const char* msg,
98
                           size_t len,
99
                           uv_write_cb write_cb = write_request_cleanup) {
100
#if DUMP_WRITES
101
  printf("%s (%ld bytes):\n", __FUNCTION__, len);
102
  dump_hex(msg, len);
103
#endif
104
105
  // Freed in write_request_cleanup
106
  WriteRequest* wr = new WriteRequest(inspector, msg, len);
107
  uv_stream_t* stream = reinterpret_cast<uv_stream_t*>(&inspector->client);
108
  return uv_write(&wr->req, stream, &wr->buf, 1, write_cb) < 0;
109
}
110
111
// Constants for hybi-10 frame format.
112
113
typedef int OpCode;
114
115
const OpCode kOpCodeContinuation = 0x0;
116
const OpCode kOpCodeText = 0x1;
117
const OpCode kOpCodeBinary = 0x2;
118
const OpCode kOpCodeClose = 0x8;
119
const OpCode kOpCodePing = 0x9;
120
const OpCode kOpCodePong = 0xA;
121
122
const unsigned char kFinalBit = 0x80;
123
const unsigned char kReserved1Bit = 0x40;
124
const unsigned char kReserved2Bit = 0x20;
125
const unsigned char kReserved3Bit = 0x10;
126
const unsigned char kOpCodeMask = 0xF;
127
const unsigned char kMaskBit = 0x80;
128
const unsigned char kPayloadLengthMask = 0x7F;
129
130
const size_t kMaxSingleBytePayloadLength = 125;
131
const size_t kTwoBytePayloadLengthField = 126;
132
const size_t kEightBytePayloadLengthField = 127;
133
const size_t kMaskingKeyWidthInBytes = 4;
134
135
static std::vector<char> encode_frame_hybi17(const char* message,
136
                                             size_t data_length) {
137
  std::vector<char> frame;
138
  OpCode op_code = kOpCodeText;
139
  frame.push_back(kFinalBit | op_code);
140
  if (data_length <= kMaxSingleBytePayloadLength) {
141
    frame.push_back(static_cast<char>(data_length));
142
  } else if (data_length <= 0xFFFF) {
143
    frame.push_back(kTwoBytePayloadLengthField);
144
    frame.push_back((data_length & 0xFF00) >> 8);
145
    frame.push_back(data_length & 0xFF);
146
  } else {
147
    frame.push_back(kEightBytePayloadLengthField);
148
    char extended_payload_length[8];
149
    size_t remaining = data_length;
150
    // Fill the length into extended_payload_length in the network byte order.
151
    for (int i = 0; i < 8; ++i) {
152
      extended_payload_length[7 - i] = remaining & 0xFF;
153
      remaining >>= 8;
154
    }
155
    frame.insert(frame.end(), extended_payload_length,
156
                 extended_payload_length + 8);
157
    ASSERT_EQ(0, remaining);
158
  }
159
  frame.insert(frame.end(), message, message + data_length);
160
  return frame;
161
}
162
163
static ws_decode_result decode_frame_hybi17(const std::vector<char>& buffer,
164
                                            bool client_frame,
165
                                            int* bytes_consumed,
166
                                            std::vector<char>* output,
167
                                            bool* compressed) {
168
  *bytes_consumed = 0;
169
  if (buffer.size() < 2)
170
    return FRAME_INCOMPLETE;
171
172
  auto it = buffer.begin();
173
174
  unsigned char first_byte = *it++;
175
  unsigned char second_byte = *it++;
176
177
  bool final = (first_byte & kFinalBit) != 0;
178
  bool reserved1 = (first_byte & kReserved1Bit) != 0;
179
  bool reserved2 = (first_byte & kReserved2Bit) != 0;
180
  bool reserved3 = (first_byte & kReserved3Bit) != 0;
181
  int op_code = first_byte & kOpCodeMask;
182
  bool masked = (second_byte & kMaskBit) != 0;
183
  *compressed = reserved1;
184
  if (!final || reserved2 || reserved3)
185
    return FRAME_ERROR;  // Only compression extension is supported.
186
187
  bool closed = false;
188
  switch (op_code) {
189
    case kOpCodeClose:
190
      closed = true;
191
      break;
192
    case kOpCodeText:
193
      break;
194
    case kOpCodeBinary:        // We don't support binary frames yet.
195
    case kOpCodeContinuation:  // We don't support binary frames yet.
196
    case kOpCodePing:          // We don't support binary frames yet.
197
    case kOpCodePong:          // We don't support binary frames yet.
198
    default:
199
      return FRAME_ERROR;
200
  }
201
202
  // In Hybi-17 spec client MUST mask its frame.
203
  if (client_frame && !masked) {
204
    return FRAME_ERROR;
205
  }
206
207
  uint64_t payload_length64 = second_byte & kPayloadLengthMask;
208
  if (payload_length64 > kMaxSingleBytePayloadLength) {
209
    int extended_payload_length_size;
210
    if (payload_length64 == kTwoBytePayloadLengthField) {
211
      extended_payload_length_size = 2;
212
    } else if (payload_length64 == kEightBytePayloadLengthField) {
213
      extended_payload_length_size = 8;
214
    } else {
215
      return FRAME_ERROR;
216
    }
217
    if ((buffer.end() - it) < extended_payload_length_size)
218
      return FRAME_INCOMPLETE;
219
    payload_length64 = 0;
220
    for (int i = 0; i < extended_payload_length_size; ++i) {
221
      payload_length64 <<= 8;
222
      payload_length64 |= static_cast<unsigned char>(*it++);
223
    }
224
  }
225
226
  static const uint64_t max_payload_length = 0x7FFFFFFFFFFFFFFFull;
227
  static const size_t max_length = SIZE_MAX;
228
  if (payload_length64 > max_payload_length ||
229
      payload_length64 > max_length - kMaskingKeyWidthInBytes) {
230
    // WebSocket frame length too large.
231
    return FRAME_ERROR;
232
  }
233
  size_t payload_length = static_cast<size_t>(payload_length64);
234
235
  if (buffer.size() - kMaskingKeyWidthInBytes < payload_length)
236
    return FRAME_INCOMPLETE;
237
238
  std::vector<char>::const_iterator masking_key = it;
239
  std::vector<char>::const_iterator payload = it + kMaskingKeyWidthInBytes;
240
  for (size_t i = 0; i < payload_length; ++i)  // Unmask the payload.
241
    output->insert(output->end(),
242
                   payload[i] ^ masking_key[i % kMaskingKeyWidthInBytes]);
243
244
  size_t pos = it + kMaskingKeyWidthInBytes + payload_length - buffer.begin();
245
  *bytes_consumed = pos;
246
  return closed ? FRAME_CLOSE : FRAME_OK;
247
}
248
249
static void invoke_read_callback(inspector_socket_t* inspector,
250
                                 int status, const uv_buf_t* buf) {
251
  if (inspector->ws_state->read_cb) {
252
    inspector->ws_state->read_cb(
253
        reinterpret_cast<uv_stream_t*>(&inspector->client), status, buf);
254
  }
255
}
256
257
static void shutdown_complete(inspector_socket_t* inspector) {
258
  close_connection(inspector);
259
}
260
261
static void on_close_frame_written(uv_write_t* req, int status) {
262
  WriteRequest* wr = WriteRequest::from_write_req(req);
263
  inspector_socket_t* inspector = wr->inspector;
264
  delete wr;
265
  inspector->ws_state->close_sent = true;
266
  if (inspector->ws_state->received_close) {
267
    shutdown_complete(inspector);
268
  }
269
}
270
271
static void close_frame_received(inspector_socket_t* inspector) {
272
  inspector->ws_state->received_close = true;
273
  if (!inspector->ws_state->close_sent) {
274
    invoke_read_callback(inspector, 0, 0);
275
    write_to_client(inspector, CLOSE_FRAME, sizeof(CLOSE_FRAME),
276
                    on_close_frame_written);
277
  } else {
278
    shutdown_complete(inspector);
279
  }
280
}
281
282
static int parse_ws_frames(inspector_socket_t* inspector) {
283
  int bytes_consumed = 0;
284
  std::vector<char> output;
285
  bool compressed = false;
286
287
  ws_decode_result r =  decode_frame_hybi17(inspector->buffer,
288
                                            true /* client_frame */,
289
                                            &bytes_consumed, &output,
290
                                            &compressed);
291
  // Compressed frame means client is ignoring the headers and misbehaves
292
  if (compressed || r == FRAME_ERROR) {
293
    invoke_read_callback(inspector, UV_EPROTO, nullptr);
294
    close_connection(inspector);
295
    bytes_consumed = 0;
296
  } else if (r == FRAME_CLOSE) {
297
    close_frame_received(inspector);
298
    bytes_consumed = 0;
299
  } else if (r == FRAME_OK && inspector->ws_state->alloc_cb
300
             && inspector->ws_state->read_cb) {
301
    uv_buf_t buffer;
302
    size_t len = output.size();
303
    inspector->ws_state->alloc_cb(
304
        reinterpret_cast<uv_handle_t*>(&inspector->client),
305
        len, &buffer);
306
    CHECK_GE(buffer.len, len);
307
    memcpy(buffer.base, &output[0], len);
308
    invoke_read_callback(inspector, len, &buffer);
309
  }
310
  return bytes_consumed;
311
}
312
313
static void prepare_buffer(uv_handle_t* stream, size_t len, uv_buf_t* buf) {
314
  *buf = uv_buf_init(new char[len], len);
315
}
316
317
static void reclaim_uv_buf(inspector_socket_t* inspector, const uv_buf_t* buf,
318
                           ssize_t read) {
319
  if (read > 0) {
320
    std::vector<char>& buffer = inspector->buffer;
321
    buffer.insert(buffer.end(), buf->base, buf->base + read);
322
  }
323
  delete[] buf->base;
324
}
325
326
static void websockets_data_cb(uv_stream_t* stream, ssize_t nread,
327
                               const uv_buf_t* buf) {
328
  inspector_socket_t* inspector = inspector_from_stream(stream);
329
  reclaim_uv_buf(inspector, buf, nread);
330
  if (nread < 0 || nread == UV_EOF) {
331
    inspector->connection_eof = true;
332
    if (!inspector->shutting_down && inspector->ws_state->read_cb) {
333
      inspector->ws_state->read_cb(stream, nread, nullptr);
334
    }
335
  } else {
336
    #if DUMP_READS
337
      printf("%s read %ld bytes\n", __FUNCTION__, nread);
338
      if (nread > 0) {
339
        dump_hex(inspector->buffer.data() + inspector->buffer.size() - nread,
340
                 nread);
341
      }
342
    #endif
343
    // 2. Parse.
344
    int processed = 0;
345
    do {
346
      processed = parse_ws_frames(inspector);
347
      // 3. Fix the buffer size & length
348
      if (processed > 0) {
349
        remove_from_beginning(&inspector->buffer, processed);
350
      }
351
    } while (processed > 0 && !inspector->buffer.empty());
352
  }
353
}
354
355
int inspector_read_start(inspector_socket_t* inspector,
356
                          uv_alloc_cb alloc_cb, uv_read_cb read_cb) {
357
  ASSERT(inspector->ws_mode);
358
  ASSERT(!inspector->shutting_down || read_cb == nullptr);
359
  inspector->ws_state->close_sent = false;
360
  inspector->ws_state->alloc_cb = alloc_cb;
361
  inspector->ws_state->read_cb = read_cb;
362
  int err =
363
      uv_read_start(reinterpret_cast<uv_stream_t*>(&inspector->client),
364
                    prepare_buffer,
365
                    websockets_data_cb);
366
  if (err < 0) {
367
    close_connection(inspector);
368
  }
369
  return err;
370
}
371
372
void inspector_read_stop(inspector_socket_t* inspector) {
373
  uv_read_stop(reinterpret_cast<uv_stream_t*>(&inspector->client));
374
  inspector->ws_state->alloc_cb = nullptr;
375
  inspector->ws_state->read_cb = nullptr;
376
}
377
378
static void generate_accept_string(const std::string& client_key,
379
                                   char (*buffer)[ACCEPT_KEY_LENGTH]) {
380
  // Magic string from websockets spec.
381
  static const char ws_magic[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
382
  std::string input(client_key + ws_magic);
383
  char hash[SHA_DIGEST_LENGTH];
384
  SHA1(reinterpret_cast<const unsigned char*>(&input[0]), input.size(),
385
       reinterpret_cast<unsigned char*>(hash));
386
  node::base64_encode(hash, sizeof(hash), *buffer, sizeof(*buffer));
387
}
388
389
static int header_value_cb(http_parser* parser, const char* at, size_t length) {
390
  static const char SEC_WEBSOCKET_KEY_HEADER[] = "Sec-WebSocket-Key";
391
  auto inspector = static_cast<inspector_socket_t*>(parser->data);
392
  auto state = inspector->http_parsing_state;
393
  state->parsing_value = true;
394
  if (state->current_header.size() == sizeof(SEC_WEBSOCKET_KEY_HEADER) - 1 &&
395
      node::StringEqualNoCaseN(state->current_header.data(),
396
                               SEC_WEBSOCKET_KEY_HEADER,
397
                               sizeof(SEC_WEBSOCKET_KEY_HEADER) - 1)) {
398
    state->ws_key.append(at, length);
399
  }
400
  return 0;
401
}
402
403
static int header_field_cb(http_parser* parser, const char* at, size_t length) {
404
  auto inspector = static_cast<inspector_socket_t*>(parser->data);
405
  auto state = inspector->http_parsing_state;
406
  if (state->parsing_value) {
407
    state->parsing_value = false;
408
    state->current_header.clear();
409
  }
410
  state->current_header.append(at, length);
411
  return 0;
412
}
413
414
static int path_cb(http_parser* parser, const char* at, size_t length) {
415
  auto inspector = static_cast<inspector_socket_t*>(parser->data);
416
  auto state = inspector->http_parsing_state;
417
  state->path.append(at, length);
418
  return 0;
419
}
420
421
static void handshake_complete(inspector_socket_t* inspector) {
422
  uv_read_stop(reinterpret_cast<uv_stream_t*>(&inspector->client));
423
  handshake_cb callback = inspector->http_parsing_state->callback;
424
  inspector->ws_state = new ws_state_s();
425
  inspector->ws_mode = true;
426
  callback(inspector, kInspectorHandshakeUpgraded,
427
           inspector->http_parsing_state->path);
428
}
429
430
static void cleanup_http_parsing_state(inspector_socket_t* inspector) {
431
  delete inspector->http_parsing_state;
432
  inspector->http_parsing_state = nullptr;
433
}
434
435
static void report_handshake_failure_cb(uv_handle_t* handle) {
436
  dispose_inspector(handle);
437
  inspector_socket_t* inspector = inspector_from_stream(handle);
438
  handshake_cb cb = inspector->http_parsing_state->callback;
439
  cleanup_http_parsing_state(inspector);
440
  cb(inspector, kInspectorHandshakeFailed, std::string());
441
}
442
443
static void close_and_report_handshake_failure(inspector_socket_t* inspector) {
444
  uv_handle_t* socket = reinterpret_cast<uv_handle_t*>(&inspector->client);
445
  if (uv_is_closing(socket)) {
446
    report_handshake_failure_cb(socket);
447
  } else {
448
    uv_read_stop(reinterpret_cast<uv_stream_t*>(socket));
449
    uv_close(socket, report_handshake_failure_cb);
450
  }
451
}
452
453
static void handshake_failed(inspector_socket_t* inspector) {
454
  const char HANDSHAKE_FAILED_RESPONSE[] =
455
      "HTTP/1.0 400 Bad Request\r\n"
456
      "Content-Type: text/html; charset=UTF-8\r\n\r\n"
457
      "WebSockets request was expected\r\n";
458
  write_to_client(inspector, HANDSHAKE_FAILED_RESPONSE,
459
                  sizeof(HANDSHAKE_FAILED_RESPONSE) - 1);
460
  close_and_report_handshake_failure(inspector);
461
}
462
463
// init_handshake references message_complete_cb
464
static void init_handshake(inspector_socket_t* inspector);
465
466
static int message_complete_cb(http_parser* parser) {
467
  inspector_socket_t* inspector =
468
      reinterpret_cast<inspector_socket_t*>(parser->data);
469
  struct http_parsing_state_s* state = inspector->http_parsing_state;
470
  if (parser->method != HTTP_GET) {
471
    handshake_failed(inspector);
472
  } else if (!parser->upgrade) {
473
    if (state->callback(inspector, kInspectorHandshakeHttpGet, state->path)) {
474
      init_handshake(inspector);
475
    } else {
476
      handshake_failed(inspector);
477
    }
478
  } else if (state->ws_key.empty()) {
479
    handshake_failed(inspector);
480
  } else if (state->callback(inspector, kInspectorHandshakeUpgrading,
481
                             state->path)) {
482
    char accept_string[ACCEPT_KEY_LENGTH];
483
    generate_accept_string(state->ws_key, &accept_string);
484
    const char accept_ws_prefix[] = "HTTP/1.1 101 Switching Protocols\r\n"
485
                                    "Upgrade: websocket\r\n"
486
                                    "Connection: Upgrade\r\n"
487
                                    "Sec-WebSocket-Accept: ";
488
    const char accept_ws_suffix[] = "\r\n\r\n";
489
    std::string reply(accept_ws_prefix, sizeof(accept_ws_prefix) - 1);
490
    reply.append(accept_string, sizeof(accept_string));
491
    reply.append(accept_ws_suffix, sizeof(accept_ws_suffix) - 1);
492
    if (write_to_client(inspector, &reply[0], reply.size()) >= 0) {
493
      handshake_complete(inspector);
494
      inspector->http_parsing_state->done = true;
495
    } else {
496
      close_and_report_handshake_failure(inspector);
497
    }
498
  } else {
499
    handshake_failed(inspector);
500
  }
501
  return 0;
502
}
503
504
static void data_received_cb(uv_stream_s* client, ssize_t nread,
505
                             const uv_buf_t* buf) {
506
#if DUMP_READS
507
  if (nread >= 0) {
508
    printf("%s (%ld bytes)\n", __FUNCTION__, nread);
509
    dump_hex(buf->base, nread);
510
  } else {
511
    printf("[%s:%d] %s\n", __FUNCTION__, __LINE__, uv_err_name(nread));
512
  }
513
#endif
514
  inspector_socket_t* inspector = inspector_from_stream(client);
515
  reclaim_uv_buf(inspector, buf, nread);
516
  if (nread < 0 || nread == UV_EOF) {
517
    close_and_report_handshake_failure(inspector);
518
  } else {
519
    http_parsing_state_s* state = inspector->http_parsing_state;
520
    http_parser* parser = &state->parser;
521
    http_parser_execute(parser, &state->parser_settings,
522
                        inspector->buffer.data(), nread);
523
    remove_from_beginning(&inspector->buffer, nread);
524
    if (parser->http_errno != HPE_OK) {
525
      handshake_failed(inspector);
526
    }
527
    if (inspector->http_parsing_state->done) {
528
      cleanup_http_parsing_state(inspector);
529
    }
530
  }
531
}
532
533
static void init_handshake(inspector_socket_t* inspector) {
534
  http_parsing_state_s* state = inspector->http_parsing_state;
535
  CHECK_NE(state, nullptr);
536
  state->current_header.clear();
537
  state->ws_key.clear();
538
  state->path.clear();
539
  state->done = false;
540
  http_parser_init(&state->parser, HTTP_REQUEST);
541
  state->parser.data = inspector;
542
  http_parser_settings* settings = &state->parser_settings;
543
  http_parser_settings_init(settings);
544
  settings->on_header_field = header_field_cb;
545
  settings->on_header_value = header_value_cb;
546
  settings->on_message_complete = message_complete_cb;
547
  settings->on_url = path_cb;
548
}
549
550
int inspector_accept(uv_stream_t* server, inspector_socket_t* inspector,
551
                     handshake_cb callback) {
552
  ASSERT_NE(callback, nullptr);
553
  CHECK_EQ(inspector->http_parsing_state, nullptr);
554
  inspector->http_parsing_state = new http_parsing_state_s();
555
  uv_stream_t* client = reinterpret_cast<uv_stream_t*>(&inspector->client);
556
  CHECK_NE(client, nullptr);
557
  int err = uv_tcp_init(server->loop, &inspector->client);
558
559
  if (err == 0) {
560
    err = uv_accept(server, client);
561
  }
562
  if (err == 0) {
563
    init_handshake(inspector);
564
    inspector->http_parsing_state->callback = callback;
565
    err = uv_read_start(client, prepare_buffer,
566
                        data_received_cb);
567
  }
568
  if (err != 0) {
569
    uv_close(reinterpret_cast<uv_handle_t*>(client), NULL);
570
  }
571
  return err;
572
}
573
574
void inspector_write(inspector_socket_t* inspector, const char* data,
575
                     size_t len) {
576
  if (inspector->ws_mode) {
577
    std::vector<char> output = encode_frame_hybi17(data, len);
578
    write_to_client(inspector, &output[0], output.size());
579
  } else {
580
    write_to_client(inspector, data, len);
581
  }
582
}
583
584
void inspector_close(inspector_socket_t* inspector,
585
    inspector_cb callback) {
586
  // libuv throws assertions when closing stream that's already closed - we
587
  // need to do the same.
588
  ASSERT(!uv_is_closing(reinterpret_cast<uv_handle_t*>(&inspector->client)));
589
  ASSERT(!inspector->shutting_down);
590
  inspector->shutting_down = true;
591
  inspector->ws_state->close_cb = callback;
592
  if (inspector->connection_eof) {
593
    close_connection(inspector);
594
  } else {
595
    inspector_read_stop(inspector);
596
    write_to_client(inspector, CLOSE_FRAME, sizeof(CLOSE_FRAME),
597
                    on_close_frame_written);
598
    inspector_read_start(inspector, nullptr, nullptr);
599
  }
600
}
601
602
bool inspector_is_active(const struct inspector_socket_s* inspector) {
603
  const uv_handle_t* client =
604
      reinterpret_cast<const uv_handle_t*>(&inspector->client);
605
  return !inspector->shutting_down && !uv_is_closing(client);
606
}