GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage/nodes/benchmark/out/../test/cctest/test_inspector_socket_server.cc Lines: 322 333 96.7 %
Date: 2019-01-07 12:15:22 Branches: 216 434 49.8 %

Line Branch Exec Source
1
#include "inspector_socket_server.h"
2
3
#include "node.h"
4
#include "gtest/gtest.h"
5
6
#include <algorithm>
7
#include <sstream>
8
9
static uv_loop_t loop;
10
11
static const char HOST[] = "127.0.0.1";
12
13
static const char CLIENT_CLOSE_FRAME[] = "\x88\x80\x2D\x0E\x1E\xFA";
14
static const char SERVER_CLOSE_FRAME[] = "\x88\x00";
15
16
static const char MAIN_TARGET_ID[] = "main-target";
17
18
static const char WS_HANDSHAKE_RESPONSE[] =
19
    "HTTP/1.1 101 Switching Protocols\r\n"
20
    "Upgrade: websocket\r\n"
21
    "Connection: Upgrade\r\n"
22
    "Sec-WebSocket-Accept: Dt87H1OULVZnSJo/KgMUYI7xPCg=\r\n\r\n";
23
24
#define SPIN_WHILE(condition)                                                  \
25
  {                                                                            \
26
    Timeout timeout(&loop);                                                    \
27
    while ((condition) && !timeout.timed_out) {                                \
28
      uv_run(&loop, UV_RUN_ONCE);                                              \
29
    }                                                                          \
30
    ASSERT_FALSE((condition));                                                 \
31
  }
32
33
namespace {
34
35
using InspectorSocketServer = node::inspector::InspectorSocketServer;
36
using SocketServerDelegate = node::inspector::SocketServerDelegate;
37
38
class Timeout {
39
 public:
40
83
  explicit Timeout(uv_loop_t* loop) : timed_out(false), done_(false) {
41
83
    uv_timer_init(loop, &timer_);
42
83
    uv_timer_start(&timer_, Timeout::set_flag, 5000, 0);
43
83
    uv_unref(reinterpret_cast<uv_handle_t*>(&timer_));
44
83
  }
45
46
83
  ~Timeout() {
47
83
    uv_timer_stop(&timer_);
48
83
    uv_close(reinterpret_cast<uv_handle_t*>(&timer_), mark_done);
49
249
    while (!done_) {
50
83
      uv_run(&loop, UV_RUN_NOWAIT);
51
    }
52
83
  }
53
  bool timed_out;
54
55
 private:
56
  static void set_flag(uv_timer_t* timer) {
57
    Timeout* t = node::ContainerOf(&Timeout::timer_, timer);
58
    t->timed_out = true;
59
  }
60
61
83
  static void mark_done(uv_handle_t* timer) {
62
    Timeout* t = node::ContainerOf(&Timeout::timer_,
63
83
        reinterpret_cast<uv_timer_t*>(timer));
64
83
    t->done_ = true;
65
83
  }
66
67
  bool done_;
68
  uv_timer_t timer_;
69
};
70
71
20
class InspectorSocketServerTest : public ::testing::Test {
72
 protected:
73
10
  void SetUp() override {
74
10
    EXPECT_EQ(0, uv_loop_init(&loop));
75
10
  }
76
77
10
  void TearDown() override {
78
10
    const int err = uv_loop_close(&loop);
79
10
    if (err != 0) {
80
      uv_print_all_handles(&loop, stderr);
81
    }
82
10
    EXPECT_EQ(0, err);
83
10
  }
84
};
85
86
12
class SocketWrapper {
87
 public:
88
12
  explicit SocketWrapper(uv_loop_t* loop) : closed_(false),
89
                                            eof_(false),
90
                                            loop_(loop),
91
                                            socket_(uv_tcp_t()),
92
                                            connected_(false),
93
12
                                            sending_(false) { }
94
95
12
  void Connect(std::string host, int port, bool v6 = false) {
96
12
    closed_ = false;
97
12
    connection_failed_ = false;
98
12
    connected_ = false;
99
12
    eof_ = false;
100
12
    contents_.clear();
101
12
    uv_tcp_init(loop_, &socket_);
102
    union {sockaddr generic; sockaddr_in v4; sockaddr_in6 v6;} addr;
103
12
    int err = 0;
104
12
    if (v6) {
105
1
      err = uv_ip6_addr(host.c_str(), port, &addr.v6);
106
    } else {
107
11
      err = uv_ip4_addr(host.c_str(), port, &addr.v4);
108
    }
109
12
    CHECK_EQ(0, err);
110
12
    err = uv_tcp_connect(&connect_, &socket_, &addr.generic, Connected_);
111
12
    CHECK_EQ(0, err);
112


24
    SPIN_WHILE(!connected_)
113
    uv_read_start(reinterpret_cast<uv_stream_t*>(&socket_), AllocCallback,
114
12
                  ReadCallback);
115
  }
116
117
  void ExpectFailureToConnect(std::string host, int port) {
118
    connected_ = false;
119
    connection_failed_ = false;
120
    closed_ = false;
121
    eof_ = false;
122
    contents_.clear();
123
    uv_tcp_init(loop_, &socket_);
124
    sockaddr_in addr;
125
    int err = uv_ip4_addr(host.c_str(), port, &addr);
126
    CHECK_EQ(0, err);
127
    err = uv_tcp_connect(&connect_, &socket_,
128
                         reinterpret_cast<const sockaddr*>(&addr),
129
                         ConnectionMustFail_);
130
    CHECK_EQ(0, err);
131
    SPIN_WHILE(!connection_failed_)
132
    uv_read_start(reinterpret_cast<uv_stream_t*>(&socket_), AllocCallback,
133
                  ReadCallback);
134
  }
135
136
12
  void Close() {
137
12
    uv_close(reinterpret_cast<uv_handle_t*>(&socket_), ClosedCallback);
138


12
    SPIN_WHILE(!closed_);
139
  }
140
141
20
  void Expect(const std::string& expects) {
142



20
    SPIN_WHILE(contents_.size() < expects.length());
143

20
    ASSERT_STREQ(expects.c_str(),
144
20
                 std::string(contents_.data(), expects.length()).c_str());
145
20
    contents_.erase(contents_.begin(), contents_.begin() + expects.length());
146
  }
147
148
4
  void ExpectEOF() {
149


8
    SPIN_WHILE(!eof_);
150
4
    Close();
151
  }
152
153
6
  void TestHttpRequest(const std::string& path,
154
                       const std::string& expected_reply) {
155
6
    std::ostringstream expectations;
156
    expectations << "HTTP/1.0 200 OK\r\n"
157
                    "Content-Type: application/json; charset=UTF-8\r\n"
158
                    "Cache-Control: no-cache\r\n"
159
6
                    "Content-Length: ";
160
6
    expectations << expected_reply.length() + 2;
161
6
    expectations << "\r\n\r\n" << expected_reply << "\n\n";
162
12
    Write("GET " + path + " HTTP/1.1\r\n"
163
6
          "Host: localhost:9229\r\n\r\n");
164
6
    Expect(expectations.str());
165
6
  }
166
167
19
  void Write(const std::string& data) {
168

19
    ASSERT_FALSE(sending_);
169
    uv_buf_t buf[1];
170
19
    buf[0].base = const_cast<char*>(data.data());
171
19
    buf[0].len = data.length();
172
19
    sending_ = true;
173
    int err = uv_write(&write_, reinterpret_cast<uv_stream_t*>(&socket_),
174
19
                       buf, 1, WriteDone_);
175
19
    CHECK_EQ(err, 0);
176


19
    SPIN_WHILE(sending_);
177
  }
178
179
 private:
180
27
  static void AllocCallback(uv_handle_t*, size_t size, uv_buf_t* buf) {
181
27
    *buf = uv_buf_init(new char[size], size);
182
27
  }
183
184
12
  static void ClosedCallback(uv_handle_t* handle) {
185
    SocketWrapper* wrapper =
186
        node::ContainerOf(&SocketWrapper::socket_,
187
12
                          reinterpret_cast<uv_tcp_t*>(handle));
188

24
    ASSERT_FALSE(wrapper->closed_);
189
12
    wrapper->closed_ = true;
190
  }
191
192
12
  static void Connected_(uv_connect_t* connect, int status) {
193
12
    EXPECT_EQ(0, status) << "Unable to connect: " << uv_strerror(status);
194
    SocketWrapper* wrapper =
195
12
        node::ContainerOf(&SocketWrapper::connect_, connect);
196
12
    wrapper->connected_ = true;
197
12
  }
198
199
  static void ConnectionMustFail_(uv_connect_t* connect, int status) {
200
    EXPECT_EQ(UV_ECONNREFUSED, status);
201
    SocketWrapper* wrapper =
202
        node::ContainerOf(&SocketWrapper::connect_, connect);
203
    wrapper->connection_failed_ = true;
204
  }
205
206
27
  static void ReadCallback(uv_stream_t* stream, ssize_t read,
207
                           const uv_buf_t* buf) {
208
    SocketWrapper* wrapper =
209
        node::ContainerOf(&SocketWrapper::socket_,
210
27
                          reinterpret_cast<uv_tcp_t*>(stream));
211
27
    if (read == UV_EOF) {
212
5
      wrapper->eof_ = true;
213
    } else {
214
22
      wrapper->contents_.insert(wrapper->contents_.end(), buf->base,
215
44
                                buf->base + read);
216
    }
217
27
    delete[] buf->base;
218
27
  }
219
19
  static void WriteDone_(uv_write_t* req, int err) {
220
19
    CHECK_EQ(0, err);
221
    SocketWrapper* wrapper =
222
19
        node::ContainerOf(&SocketWrapper::write_, req);
223

38
    ASSERT_TRUE(wrapper->sending_);
224
19
    wrapper->sending_ = false;
225
  }
226
  bool IsConnected() { return connected_; }
227
228
  bool closed_;
229
  bool eof_;
230
  uv_loop_t* loop_;
231
  uv_tcp_t socket_;
232
  uv_connect_t connect_;
233
  uv_write_t write_;
234
  bool connected_;
235
  bool connection_failed_;
236
  bool sending_;
237
  std::vector<char> contents_;
238
};
239
240
11
class ServerHolder {
241
 public:
242
9
  ServerHolder(bool has_targets, uv_loop_t* loop, int port)
243
9
               : ServerHolder(has_targets, loop, HOST, port, nullptr) { }
244
245
  ServerHolder(bool has_targets, uv_loop_t* loop,
246
               const std::string host, int port, FILE* out);
247
248
25
  InspectorSocketServer* operator->() {
249
25
    return server_.get();
250
  }
251
252
13
  int port() {
253
13
    return server_->Port();
254
  }
255
256
8
  bool done() {
257
8
    return server_->done();
258
  }
259
260
6
  void Disconnected() {
261
6
    disconnected++;
262
6
  }
263
264
11
  void Done() {
265
11
    delegate_done = true;
266
11
  }
267
268
6
  void Connected(int id) {
269
6
    buffer_.clear();
270
6
    session_id_ = id;
271
6
    connected++;
272
6
  }
273
274
2
  void Received(const std::string& message) {
275
2
    buffer_.insert(buffer_.end(), message.begin(), message.end());
276
2
  }
277
278
3
  void Write(const std::string& message) {
279
3
    server_->Send(session_id_, message);
280
3
  }
281
282
2
  void Expect(const std::string& expects) {
283



2
    SPIN_WHILE(buffer_.size() < expects.length());
284

2
    ASSERT_STREQ(std::string(buffer_.data(), expects.length()).c_str(),
285
2
                 expects.c_str());
286
2
    buffer_.erase(buffer_.begin(), buffer_.begin() + expects.length());
287
  }
288
289
  int connected = 0;
290
  int disconnected = 0;
291
  bool delegate_done = false;
292
293
 private:
294
  std::unique_ptr<InspectorSocketServer> server_;
295
  std::vector<char> buffer_;
296
  int session_id_;
297
};
298
299
class TestSocketServerDelegate : public SocketServerDelegate {
300
 public:
301
11
  explicit TestSocketServerDelegate(
302
      ServerHolder* server,
303
      const std::vector<std::string>& target_ids)
304
      : harness_(server),
305
        targets_(target_ids),
306
11
        session_id_(0) {}
307
308
33
  ~TestSocketServerDelegate() {
309
11
    harness_->Done();
310
22
  }
311
312
11
  void AssignServer(InspectorSocketServer* server) override {
313
11
    server_ = server;
314
11
  }
315
316
6
  void StartSession(int session_id, const std::string& target_id) override {
317
6
    session_id_ = session_id;
318
6
    CHECK_NE(targets_.end(),
319
             std::find(targets_.begin(), targets_.end(), target_id));
320
6
    harness_->Connected(session_id_);
321
6
  }
322
323
2
  void MessageReceived(int session_id, const std::string& message) override {
324
2
    CHECK_EQ(session_id_, session_id);
325
2
    harness_->Received(message);
326
2
  }
327
328
6
  void EndSession(int session_id) override {
329
6
    CHECK_EQ(session_id_, session_id);
330
6
    harness_->Disconnected();
331
6
  }
332
333
24
  std::vector<std::string> GetTargetIds() override {
334
24
    return targets_;
335
  }
336
337
  std::string GetTargetTitle(const std::string& id) override {
338
    return id + " Target Title";
339
  }
340
341
  std::string GetTargetUrl(const std::string& id) override {
342
    return "file://" + id + "/script.js";
343
  }
344
345
 private:
346
  ServerHolder* harness_;
347
  const std::vector<std::string> targets_;
348
  InspectorSocketServer* server_;
349
  int session_id_;
350
};
351
352
11
ServerHolder::ServerHolder(bool has_targets, uv_loop_t* loop,
353
11
                           const std::string host, int port, FILE* out) {
354
11
  std::vector<std::string> targets;
355
11
  if (has_targets)
356
6
    targets = { MAIN_TARGET_ID };
357
  std::unique_ptr<TestSocketServerDelegate> delegate(
358
22
      new TestSocketServerDelegate(this, targets));
359
  server_.reset(
360
22
      new InspectorSocketServer(std::move(delegate), loop, host, port, out));
361
11
}
362
363
2
static void TestHttpRequest(int port, const std::string& path,
364
                            const std::string& expected_body) {
365
2
  SocketWrapper socket(&loop);
366
2
  socket.Connect(HOST, port);
367
2
  socket.TestHttpRequest(path, expected_body);
368
2
  socket.Close();
369
2
}
370
371
8
static const std::string WsHandshakeRequest(const std::string& target_id) {
372
16
  return "GET /" + target_id + " HTTP/1.1\r\n"
373
         "Host: localhost:9229\r\n"
374
         "Upgrade: websocket\r\n"
375
         "Connection: Upgrade\r\n"
376
         "Sec-WebSocket-Key: aaa==\r\n"
377
16
         "Sec-WebSocket-Version: 13\r\n\r\n";
378
}
379
}  // anonymous namespace
380
381
382
5
TEST_F(InspectorSocketServerTest, InspectorSessions) {
383
1
  ServerHolder server(true, &loop, 0);
384

1
  ASSERT_TRUE(server->Start());
385
386
1
  SocketWrapper well_behaved_socket(&loop);
387
  // Regular connection
388
1
  well_behaved_socket.Connect(HOST, server.port());
389
1
  well_behaved_socket.Write(WsHandshakeRequest(MAIN_TARGET_ID));
390
1
  well_behaved_socket.Expect(WS_HANDSHAKE_RESPONSE);
391
392
1
  EXPECT_EQ(1, server.connected);
393
394
1
  well_behaved_socket.Write("\x81\x84\x7F\xC2\x66\x31\x4E\xF0\x55\x05");
395
396
1
  server.Expect("1234");
397
1
  server.Write("5678");
398
399
1
  well_behaved_socket.Expect("\x81\x4" "5678");
400
1
  well_behaved_socket.Write(CLIENT_CLOSE_FRAME);
401
1
  well_behaved_socket.Expect(SERVER_CLOSE_FRAME);
402
403
1
  EXPECT_EQ(1, server.disconnected);
404
405
1
  well_behaved_socket.Close();
406
407
  // Bogus target - start session callback should not even be invoked
408
1
  SocketWrapper bogus_target_socket(&loop);
409
1
  bogus_target_socket.Connect(HOST, server.port());
410
1
  bogus_target_socket.Write(WsHandshakeRequest("bogus_target"));
411
1
  bogus_target_socket.Expect("HTTP/1.0 400 Bad Request");
412
1
  bogus_target_socket.ExpectEOF();
413
1
  EXPECT_EQ(1, server.connected);
414
1
  EXPECT_EQ(1, server.disconnected);
415
416
  // Drop connection (no proper close frames)
417
1
  SocketWrapper dropped_connection_socket(&loop);
418
1
  dropped_connection_socket.Connect(HOST, server.port());
419
1
  dropped_connection_socket.Write(WsHandshakeRequest(MAIN_TARGET_ID));
420
1
  dropped_connection_socket.Expect(WS_HANDSHAKE_RESPONSE);
421
422
1
  EXPECT_EQ(2, server.connected);
423
424
1
  server.Write("5678");
425
1
  dropped_connection_socket.Expect("\x81\x4" "5678");
426
427
1
  dropped_connection_socket.Close();
428


1
  SPIN_WHILE(server.disconnected < 2);
429
430
  // Reconnect regular connection
431
1
  SocketWrapper stays_till_termination_socket(&loop);
432
1
  stays_till_termination_socket.Connect(HOST, server.port());
433
1
  stays_till_termination_socket.Write(WsHandshakeRequest(MAIN_TARGET_ID));
434
1
  stays_till_termination_socket.Expect(WS_HANDSHAKE_RESPONSE);
435
436


1
  SPIN_WHILE(3 != server.connected);
437
438
1
  server.Write("5678");
439
1
  stays_till_termination_socket.Expect("\x81\x4" "5678");
440
441
  stays_till_termination_socket
442
1
      .Write("\x81\x84\x7F\xC2\x66\x31\x4E\xF0\x55\x05");
443
1
  server.Expect("1234");
444
445
1
  server->Stop();
446
1
  server->TerminateConnections();
447
448
1
  stays_till_termination_socket.Write(CLIENT_CLOSE_FRAME);
449
1
  stays_till_termination_socket.Expect(SERVER_CLOSE_FRAME);
450
451


1
  SPIN_WHILE(3 != server.disconnected);
452



1
  SPIN_WHILE(!server.done());
453


1
  stays_till_termination_socket.ExpectEOF();
454
}
455
456
5
TEST_F(InspectorSocketServerTest, ServerDoesNothing) {
457
1
  ServerHolder server(true, &loop, 0);
458

1
  ASSERT_TRUE(server->Start());
459
1
  server->Stop();
460
1
  server->TerminateConnections();
461



1
  SPIN_WHILE(!server.done());
462

1
  ASSERT_TRUE(server.delegate_done);
463



1
  SPIN_WHILE(uv_loop_alive(&loop));
464
}
465
466
5
TEST_F(InspectorSocketServerTest, ServerWithoutTargets) {
467
1
  ServerHolder server(false, &loop, 0);
468

1
  ASSERT_TRUE(server->Start());
469
1
  TestHttpRequest(server.port(), "/json/list", "[ ]");
470
1
  TestHttpRequest(server.port(), "/json", "[ ]");
471
472
  // Declined connection
473
1
  SocketWrapper socket(&loop);
474
1
  socket.Connect(HOST, server.port());
475
1
  socket.Write(WsHandshakeRequest("any target id"));
476
1
  socket.Expect("HTTP/1.0 400 Bad Request");
477
1
  socket.ExpectEOF();
478
1
  server->Stop();
479
1
  server->TerminateConnections();
480



1
  SPIN_WHILE(!server.done());
481




1
  SPIN_WHILE(uv_loop_alive(&loop));
482
}
483
484
5
TEST_F(InspectorSocketServerTest, ServerCannotStart) {
485
1
  ServerHolder server1(false, &loop, 0);
486

1
  ASSERT_TRUE(server1->Start());
487
1
  ServerHolder server2(false, &loop, server1.port());
488

1
  ASSERT_FALSE(server2->Start());
489
1
  server1->Stop();
490
1
  server1->TerminateConnections();
491



1
  SPIN_WHILE(!server1.done());
492

1
  ASSERT_TRUE(server1.delegate_done);
493




1
  SPIN_WHILE(uv_loop_alive(&loop));
494
}
495
496
5
TEST_F(InspectorSocketServerTest, StoppingServerDoesNotKillConnections) {
497
1
  ServerHolder server(false, &loop, 0);
498

1
  ASSERT_TRUE(server->Start());
499
1
  SocketWrapper socket1(&loop);
500
1
  socket1.Connect(HOST, server.port());
501
1
  socket1.TestHttpRequest("/json/list", "[ ]");
502
1
  server->Stop();
503
1
  socket1.TestHttpRequest("/json/list", "[ ]");
504
1
  socket1.Close();
505
1
  uv_run(&loop, UV_RUN_DEFAULT);
506


1
  ASSERT_TRUE(server.delegate_done);
507
}
508
509
5
TEST_F(InspectorSocketServerTest, ClosingConnectionReportsDone) {
510
1
  ServerHolder server(false, &loop, 0);
511

1
  ASSERT_TRUE(server->Start());
512
1
  SocketWrapper socket1(&loop);
513
1
  socket1.Connect(HOST, server.port());
514
1
  socket1.TestHttpRequest("/json/list", "[ ]");
515
1
  server->Stop();
516
1
  socket1.TestHttpRequest("/json/list", "[ ]");
517
1
  socket1.Close();
518
1
  uv_run(&loop, UV_RUN_DEFAULT);
519


1
  ASSERT_TRUE(server.delegate_done);
520
}
521
522
5
TEST_F(InspectorSocketServerTest, ClosingSocketReportsDone) {
523
1
  ServerHolder server(true, &loop, 0);
524

1
  ASSERT_TRUE(server->Start());
525
1
  SocketWrapper socket1(&loop);
526
1
  socket1.Connect(HOST, server.port());
527
1
  socket1.Write(WsHandshakeRequest(MAIN_TARGET_ID));
528
1
  socket1.Expect(WS_HANDSHAKE_RESPONSE);
529
1
  server->Stop();
530

1
  ASSERT_FALSE(server.delegate_done);
531
1
  socket1.Close();
532



1
  SPIN_WHILE(!server.delegate_done);
533
}
534
535
5
TEST_F(InspectorSocketServerTest, TerminatingSessionReportsDone) {
536
1
  ServerHolder server(true, &loop, 0);
537

1
  ASSERT_TRUE(server->Start());
538
1
  SocketWrapper socket1(&loop);
539
1
  socket1.Connect(HOST, server.port());
540
1
  socket1.Write(WsHandshakeRequest(MAIN_TARGET_ID));
541
1
  socket1.Expect(WS_HANDSHAKE_RESPONSE);
542
1
  server->Stop();
543

1
  ASSERT_FALSE(server.delegate_done);
544
1
  server->TerminateConnections();
545
1
  socket1.Expect(SERVER_CLOSE_FRAME);
546
1
  socket1.Write(CLIENT_CLOSE_FRAME);
547
1
  socket1.ExpectEOF();
548



1
  SPIN_WHILE(!server.delegate_done);
549
}
550
551
5
TEST_F(InspectorSocketServerTest, FailsToBindToNodejsHost) {
552
1
  ServerHolder server(true, &loop, "nodejs.org", 80, nullptr);
553

1
  ASSERT_FALSE(server->Start());
554



1
  SPIN_WHILE(uv_loop_alive(&loop));
555
}
556
557
1
bool has_ipv6_address() {
558
1
  uv_interface_address_s* addresses = nullptr;
559
1
  int address_count = 0;
560
1
  int err = uv_interface_addresses(&addresses, &address_count);
561
1
  if (err != 0) {
562
    return false;
563
  }
564
1
  bool has_address = false;
565
7
  for (int i = 0; i < address_count; i++) {
566
6
    if (addresses[i].address.address6.sin6_family == AF_INET6) {
567
3
      has_address = true;
568
    }
569
  }
570
1
  uv_free_interface_addresses(addresses, address_count);
571
1
  return has_address;
572
}
573
574
5
TEST_F(InspectorSocketServerTest, BindsToIpV6) {
575
1
  if (!has_ipv6_address()) {
576
    fprintf(stderr, "No IPv6 network detected\n");
577
    return;
578
  }
579
1
  ServerHolder server(true, &loop, "::", 0, nullptr);
580

1
  ASSERT_TRUE(server->Start());
581
1
  SocketWrapper socket1(&loop);
582
1
  socket1.Connect("::1", server.port(), true);
583
1
  socket1.Write(WsHandshakeRequest(MAIN_TARGET_ID));
584
1
  socket1.Expect(WS_HANDSHAKE_RESPONSE);
585
1
  server->Stop();
586

1
  ASSERT_FALSE(server.delegate_done);
587
1
  socket1.Close();
588



1
  SPIN_WHILE(!server.delegate_done);
589

3
}