• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "inspector_socket_server.h"
2 
3 #include "node.h"
4 #include "node_options.h"
5 #include "util-inl.h"
6 #include "gtest/gtest.h"
7 
8 #include <algorithm>
9 #include <memory>
10 #include <sstream>
11 
12 static uv_loop_t loop;
13 
14 static const char HOST[] = "127.0.0.1";
15 
16 static const char CLIENT_CLOSE_FRAME[] = "\x88\x80\x2D\x0E\x1E\xFA";
17 static const char SERVER_CLOSE_FRAME[] = "\x88\x00";
18 
19 static const char MAIN_TARGET_ID[] = "main-target";
20 
21 static const char WS_HANDSHAKE_RESPONSE[] =
22     "HTTP/1.1 101 Switching Protocols\r\n"
23     "Upgrade: websocket\r\n"
24     "Connection: Upgrade\r\n"
25     "Sec-WebSocket-Accept: Dt87H1OULVZnSJo/KgMUYI7xPCg=\r\n\r\n";
26 
27 #define SPIN_WHILE(condition)                                                  \
28   {                                                                            \
29     Timeout timeout(&loop);                                                    \
30     while ((condition) && !timeout.timed_out) {                                \
31       uv_run(&loop, UV_RUN_ONCE);                                              \
32     }                                                                          \
33     ASSERT_FALSE((condition));                                                 \
34   }
35 
36 namespace {
37 
38 using InspectorSocketServer = node::inspector::InspectorSocketServer;
39 using SocketServerDelegate = node::inspector::SocketServerDelegate;
40 
41 class Timeout {
42  public:
Timeout(uv_loop_t * loop)43   explicit Timeout(uv_loop_t* loop) : timed_out(false), done_(false) {
44     uv_timer_init(loop, &timer_);
45     uv_timer_start(&timer_, Timeout::set_flag, 5000, 0);
46     uv_unref(reinterpret_cast<uv_handle_t*>(&timer_));
47   }
48 
~Timeout()49   ~Timeout() {
50     uv_timer_stop(&timer_);
51     uv_close(reinterpret_cast<uv_handle_t*>(&timer_), mark_done);
52     while (!done_) {
53       uv_run(&loop, UV_RUN_NOWAIT);
54     }
55   }
56   bool timed_out;
57 
58  private:
set_flag(uv_timer_t * timer)59   static void set_flag(uv_timer_t* timer) {
60     Timeout* t = node::ContainerOf(&Timeout::timer_, timer);
61     t->timed_out = true;
62   }
63 
mark_done(uv_handle_t * timer)64   static void mark_done(uv_handle_t* timer) {
65     Timeout* t = node::ContainerOf(&Timeout::timer_,
66         reinterpret_cast<uv_timer_t*>(timer));
67     t->done_ = true;
68   }
69 
70   bool done_;
71   uv_timer_t timer_;
72 };
73 
74 class InspectorSocketServerTest : public ::testing::Test {
75  protected:
SetUp()76   void SetUp() override {
77     EXPECT_EQ(0, uv_loop_init(&loop));
78   }
79 
TearDown()80   void TearDown() override {
81     const int err = uv_loop_close(&loop);
82     if (err != 0) {
83       uv_print_all_handles(&loop, stderr);
84     }
85     EXPECT_EQ(0, err);
86   }
87 };
88 
89 class SocketWrapper {
90  public:
SocketWrapper(uv_loop_t * loop)91   explicit SocketWrapper(uv_loop_t* loop) : closed_(false),
92                                             eof_(false),
93                                             loop_(loop),
94                                             socket_(uv_tcp_t()),
95                                             connected_(false),
96                                             sending_(false) { }
97 
Connect(const std::string & host,int port,bool v6=false)98   void Connect(const std::string& host, int port, bool v6 = false) {
99     closed_ = false;
100     connection_failed_ = false;
101     connected_ = false;
102     eof_ = false;
103     contents_.clear();
104     uv_tcp_init(loop_, &socket_);
105     union {sockaddr generic; sockaddr_in v4; sockaddr_in6 v6;} addr;
106     int err = 0;
107     if (v6) {
108       err = uv_ip6_addr(host.c_str(), port, &addr.v6);
109     } else {
110       err = uv_ip4_addr(host.c_str(), port, &addr.v4);
111     }
112     CHECK_EQ(0, err);
113     err = uv_tcp_connect(&connect_, &socket_, &addr.generic, Connected_);
114     CHECK_EQ(0, err);
115     SPIN_WHILE(!connected_)
116     uv_read_start(reinterpret_cast<uv_stream_t*>(&socket_), AllocCallback,
117                   ReadCallback);
118   }
119 
ExpectFailureToConnect(const std::string & host,int port)120   void ExpectFailureToConnect(const std::string& host, int port) {
121     connected_ = false;
122     connection_failed_ = false;
123     closed_ = false;
124     eof_ = false;
125     contents_.clear();
126     uv_tcp_init(loop_, &socket_);
127     sockaddr_in addr;
128     int err = uv_ip4_addr(host.c_str(), port, &addr);
129     CHECK_EQ(0, err);
130     err = uv_tcp_connect(&connect_, &socket_,
131                          reinterpret_cast<const sockaddr*>(&addr),
132                          ConnectionMustFail_);
133     CHECK_EQ(0, err);
134     SPIN_WHILE(!connection_failed_)
135     uv_read_start(reinterpret_cast<uv_stream_t*>(&socket_), AllocCallback,
136                   ReadCallback);
137   }
138 
Close()139   void Close() {
140     uv_close(reinterpret_cast<uv_handle_t*>(&socket_), ClosedCallback);
141     SPIN_WHILE(!closed_);
142   }
143 
Expect(const std::string & expects)144   void Expect(const std::string& expects) {
145     SPIN_WHILE(contents_.size() < expects.length());
146     ASSERT_STREQ(expects.c_str(),
147                  std::string(contents_.data(), expects.length()).c_str());
148     contents_.erase(contents_.begin(), contents_.begin() + expects.length());
149   }
150 
ExpectEOF()151   void ExpectEOF() {
152     SPIN_WHILE(!eof_);
153     Close();
154   }
155 
TestHttpRequest(const std::string & path,const std::string & expected_reply)156   void TestHttpRequest(const std::string& path,
157                        const std::string& expected_reply) {
158     std::ostringstream expectations;
159     expectations << "HTTP/1.0 200 OK\r\n"
160                     "Content-Type: application/json; charset=UTF-8\r\n"
161                     "Cache-Control: no-cache\r\n"
162                     "Content-Length: ";
163     expectations << expected_reply.length() + 2;
164     expectations << "\r\n\r\n" << expected_reply << "\n\n";
165     Write("GET " + path + " HTTP/1.1\r\n"
166           "Host: localhost:9229\r\n\r\n");
167     Expect(expectations.str());
168   }
169 
Write(const std::string & data)170   void Write(const std::string& data) {
171     ASSERT_FALSE(sending_);
172     uv_buf_t buf[1];
173     buf[0].base = const_cast<char*>(data.data());
174     buf[0].len = data.length();
175     sending_ = true;
176     int err = uv_write(&write_, reinterpret_cast<uv_stream_t*>(&socket_),
177                        buf, 1, WriteDone_);
178     CHECK_EQ(err, 0);
179     SPIN_WHILE(sending_);
180   }
181 
182  private:
AllocCallback(uv_handle_t *,size_t size,uv_buf_t * buf)183   static void AllocCallback(uv_handle_t*, size_t size, uv_buf_t* buf) {
184     *buf = uv_buf_init(new char[size], size);
185   }
186 
ClosedCallback(uv_handle_t * handle)187   static void ClosedCallback(uv_handle_t* handle) {
188     SocketWrapper* wrapper =
189         node::ContainerOf(&SocketWrapper::socket_,
190                           reinterpret_cast<uv_tcp_t*>(handle));
191     ASSERT_FALSE(wrapper->closed_);
192     wrapper->closed_ = true;
193   }
194 
Connected_(uv_connect_t * connect,int status)195   static void Connected_(uv_connect_t* connect, int status) {
196     EXPECT_EQ(0, status) << "Unable to connect: " << uv_strerror(status);
197     SocketWrapper* wrapper =
198         node::ContainerOf(&SocketWrapper::connect_, connect);
199     wrapper->connected_ = true;
200   }
201 
ConnectionMustFail_(uv_connect_t * connect,int status)202   static void ConnectionMustFail_(uv_connect_t* connect, int status) {
203     EXPECT_EQ(UV_ECONNREFUSED, status);
204     SocketWrapper* wrapper =
205         node::ContainerOf(&SocketWrapper::connect_, connect);
206     wrapper->connection_failed_ = true;
207   }
208 
ReadCallback(uv_stream_t * stream,ssize_t read,const uv_buf_t * buf)209   static void ReadCallback(uv_stream_t* stream, ssize_t read,
210                            const uv_buf_t* buf) {
211     SocketWrapper* wrapper =
212         node::ContainerOf(&SocketWrapper::socket_,
213                           reinterpret_cast<uv_tcp_t*>(stream));
214     if (read == UV_EOF) {
215       wrapper->eof_ = true;
216     } else {
217       wrapper->contents_.insert(wrapper->contents_.end(), buf->base,
218                                 buf->base + read);
219     }
220     delete[] buf->base;
221   }
WriteDone_(uv_write_t * req,int err)222   static void WriteDone_(uv_write_t* req, int err) {
223     CHECK_EQ(0, err);
224     SocketWrapper* wrapper =
225         node::ContainerOf(&SocketWrapper::write_, req);
226     ASSERT_TRUE(wrapper->sending_);
227     wrapper->sending_ = false;
228   }
IsConnected()229   bool IsConnected() { return connected_; }
230 
231   bool closed_;
232   bool eof_;
233   uv_loop_t* loop_;
234   uv_tcp_t socket_;
235   uv_connect_t connect_;
236   uv_write_t write_;
237   bool connected_;
238   bool connection_failed_;
239   bool sending_;
240   std::vector<char> contents_;
241 };
242 
243 class ServerHolder {
244  public:
ServerHolder(bool has_targets,uv_loop_t * loop,int port)245   ServerHolder(bool has_targets, uv_loop_t* loop, int port)
246                : ServerHolder(has_targets, loop, HOST, port, nullptr) { }
247 
248   ServerHolder(bool has_targets, uv_loop_t* loop,
249                const std::string& host, int port, FILE* out);
250 
operator ->()251   InspectorSocketServer* operator->() {
252     return server_.get();
253   }
254 
port()255   int port() {
256     return server_->Port();
257   }
258 
done()259   bool done() {
260     return server_->done();
261   }
262 
Disconnected()263   void Disconnected() {
264     disconnected++;
265   }
266 
Done()267   void Done() {
268     delegate_done = true;
269   }
270 
Connected(int id)271   void Connected(int id) {
272     buffer_.clear();
273     session_id_ = id;
274     connected++;
275   }
276 
Received(const std::string & message)277   void Received(const std::string& message) {
278     buffer_.insert(buffer_.end(), message.begin(), message.end());
279   }
280 
Write(const std::string & message)281   void Write(const std::string& message) {
282     server_->Send(session_id_, message);
283   }
284 
Expect(const std::string & expects)285   void Expect(const std::string& expects) {
286     SPIN_WHILE(buffer_.size() < expects.length());
287     ASSERT_STREQ(std::string(buffer_.data(), expects.length()).c_str(),
288                  expects.c_str());
289     buffer_.erase(buffer_.begin(), buffer_.begin() + expects.length());
290   }
291 
292   int connected = 0;
293   int disconnected = 0;
294   bool delegate_done = false;
295 
296  private:
297   std::unique_ptr<InspectorSocketServer> server_;
298   std::vector<char> buffer_;
299   int session_id_;
300 };
301 
302 class TestSocketServerDelegate : public SocketServerDelegate {
303  public:
TestSocketServerDelegate(ServerHolder * server,const std::vector<std::string> & target_ids)304   explicit TestSocketServerDelegate(
305       ServerHolder* server,
306       const std::vector<std::string>& target_ids)
307       : harness_(server),
308         targets_(target_ids),
309         session_id_(0) {}
310 
~TestSocketServerDelegate()311   ~TestSocketServerDelegate() override {
312     harness_->Done();
313   }
314 
AssignServer(InspectorSocketServer * server)315   void AssignServer(InspectorSocketServer* server) override {
316     server_ = server;
317   }
318 
StartSession(int session_id,const std::string & target_id)319   void StartSession(int session_id, const std::string& target_id) override {
320     session_id_ = session_id;
321     CHECK_NE(targets_.end(),
322              std::find(targets_.begin(), targets_.end(), target_id));
323     harness_->Connected(session_id_);
324   }
325 
MessageReceived(int session_id,const std::string & message)326   void MessageReceived(int session_id, const std::string& message) override {
327     CHECK_EQ(session_id_, session_id);
328     harness_->Received(message);
329   }
330 
EndSession(int session_id)331   void EndSession(int session_id) override {
332     CHECK_EQ(session_id_, session_id);
333     harness_->Disconnected();
334   }
335 
GetTargetIds()336   std::vector<std::string> GetTargetIds() override {
337     return targets_;
338   }
339 
GetTargetTitle(const std::string & id)340   std::string GetTargetTitle(const std::string& id) override {
341     return id + " Target Title";
342   }
343 
GetTargetUrl(const std::string & id)344   std::string GetTargetUrl(const std::string& id) override {
345     return "file://" + id + "/script.js";
346   }
347 
348  private:
349   ServerHolder* harness_;
350   const std::vector<std::string> targets_;
351   InspectorSocketServer* server_;
352   int session_id_;
353 };
354 
ServerHolder(bool has_targets,uv_loop_t * loop,const std::string & host,int port,FILE * out)355 ServerHolder::ServerHolder(bool has_targets, uv_loop_t* loop,
356                            const std::string& host, int port, FILE* out) {
357   std::vector<std::string> targets;
358   if (has_targets)
359     targets = { MAIN_TARGET_ID };
360   std::unique_ptr<TestSocketServerDelegate> delegate(
361       new TestSocketServerDelegate(this, targets));
362   node::InspectPublishUid inspect_publish_uid;
363   inspect_publish_uid.console = true;
364   inspect_publish_uid.http = true;
365   server_ = std::make_unique<InspectorSocketServer>(
366       std::move(delegate), loop, host, port, inspect_publish_uid, out);
367 }
368 
TestHttpRequest(int port,const std::string & path,const std::string & expected_body)369 static void TestHttpRequest(int port, const std::string& path,
370                             const std::string& expected_body) {
371   SocketWrapper socket(&loop);
372   socket.Connect(HOST, port);
373   socket.TestHttpRequest(path, expected_body);
374   socket.Close();
375 }
376 
WsHandshakeRequest(const std::string & target_id)377 static const std::string WsHandshakeRequest(const std::string& target_id) {
378   return "GET /" + target_id + " HTTP/1.1\r\n"
379          "Host: localhost:9229\r\n"
380          "Upgrade: websocket\r\n"
381          "Connection: Upgrade\r\n"
382          "Sec-WebSocket-Key: aaa==\r\n"
383          "Sec-WebSocket-Version: 13\r\n\r\n";
384 }
385 }  // anonymous namespace
386 
387 
TEST_F(InspectorSocketServerTest,InspectorSessions)388 TEST_F(InspectorSocketServerTest, InspectorSessions) {
389   ServerHolder server(true, &loop, 0);
390   ASSERT_TRUE(server->Start());
391 
392   SocketWrapper well_behaved_socket(&loop);
393   // Regular connection
394   well_behaved_socket.Connect(HOST, server.port());
395   well_behaved_socket.Write(WsHandshakeRequest(MAIN_TARGET_ID));
396   well_behaved_socket.Expect(WS_HANDSHAKE_RESPONSE);
397 
398   EXPECT_EQ(1, server.connected);
399 
400   well_behaved_socket.Write("\x81\x84\x7F\xC2\x66\x31\x4E\xF0\x55\x05");
401 
402   server.Expect("1234");
403   server.Write("5678");
404 
405   well_behaved_socket.Expect("\x81\x4" "5678");
406   well_behaved_socket.Write(CLIENT_CLOSE_FRAME);
407   well_behaved_socket.Expect(SERVER_CLOSE_FRAME);
408 
409   EXPECT_EQ(1, server.disconnected);
410 
411   well_behaved_socket.Close();
412 
413   // Bogus target - start session callback should not even be invoked
414   SocketWrapper bogus_target_socket(&loop);
415   bogus_target_socket.Connect(HOST, server.port());
416   bogus_target_socket.Write(WsHandshakeRequest("bogus_target"));
417   bogus_target_socket.Expect("HTTP/1.0 400 Bad Request");
418   bogus_target_socket.ExpectEOF();
419   EXPECT_EQ(1, server.connected);
420   EXPECT_EQ(1, server.disconnected);
421 
422   // Drop connection (no proper close frames)
423   SocketWrapper dropped_connection_socket(&loop);
424   dropped_connection_socket.Connect(HOST, server.port());
425   dropped_connection_socket.Write(WsHandshakeRequest(MAIN_TARGET_ID));
426   dropped_connection_socket.Expect(WS_HANDSHAKE_RESPONSE);
427 
428   EXPECT_EQ(2, server.connected);
429 
430   server.Write("5678");
431   dropped_connection_socket.Expect("\x81\x4" "5678");
432 
433   dropped_connection_socket.Close();
434   SPIN_WHILE(server.disconnected < 2);
435 
436   // Reconnect regular connection
437   SocketWrapper stays_till_termination_socket(&loop);
438   stays_till_termination_socket.Connect(HOST, server.port());
439   stays_till_termination_socket.Write(WsHandshakeRequest(MAIN_TARGET_ID));
440   stays_till_termination_socket.Expect(WS_HANDSHAKE_RESPONSE);
441 
442   SPIN_WHILE(3 != server.connected);
443 
444   server.Write("5678");
445   stays_till_termination_socket.Expect("\x81\x4" "5678");
446 
447   stays_till_termination_socket
448       .Write("\x81\x84\x7F\xC2\x66\x31\x4E\xF0\x55\x05");
449   server.Expect("1234");
450 
451   server->Stop();
452   server->TerminateConnections();
453 
454   stays_till_termination_socket.Write(CLIENT_CLOSE_FRAME);
455   stays_till_termination_socket.Expect(SERVER_CLOSE_FRAME);
456 
457   SPIN_WHILE(3 != server.disconnected);
458   SPIN_WHILE(!server.done());
459   stays_till_termination_socket.ExpectEOF();
460 }
461 
TEST_F(InspectorSocketServerTest,ServerDoesNothing)462 TEST_F(InspectorSocketServerTest, ServerDoesNothing) {
463   ServerHolder server(true, &loop, 0);
464   ASSERT_TRUE(server->Start());
465   server->Stop();
466   server->TerminateConnections();
467   SPIN_WHILE(!server.done());
468   ASSERT_TRUE(server.delegate_done);
469   SPIN_WHILE(uv_loop_alive(&loop));
470 }
471 
TEST_F(InspectorSocketServerTest,ServerWithoutTargets)472 TEST_F(InspectorSocketServerTest, ServerWithoutTargets) {
473   ServerHolder server(false, &loop, 0);
474   ASSERT_TRUE(server->Start());
475   TestHttpRequest(server.port(), "/json/list", "[ ]");
476   TestHttpRequest(server.port(), "/json", "[ ]");
477 
478   // Declined connection
479   SocketWrapper socket(&loop);
480   socket.Connect(HOST, server.port());
481   socket.Write(WsHandshakeRequest("any target id"));
482   socket.Expect("HTTP/1.0 400 Bad Request");
483   socket.ExpectEOF();
484   server->Stop();
485   server->TerminateConnections();
486   SPIN_WHILE(!server.done());
487   SPIN_WHILE(uv_loop_alive(&loop));
488 }
489 
TEST_F(InspectorSocketServerTest,ServerCannotStart)490 TEST_F(InspectorSocketServerTest, ServerCannotStart) {
491   ServerHolder server1(false, &loop, 0);
492   ASSERT_TRUE(server1->Start());
493   ServerHolder server2(false, &loop, server1.port());
494   ASSERT_FALSE(server2->Start());
495   server1->Stop();
496   server1->TerminateConnections();
497   SPIN_WHILE(!server1.done());
498   ASSERT_TRUE(server1.delegate_done);
499   SPIN_WHILE(uv_loop_alive(&loop));
500 }
501 
TEST_F(InspectorSocketServerTest,StoppingServerDoesNotKillConnections)502 TEST_F(InspectorSocketServerTest, StoppingServerDoesNotKillConnections) {
503   ServerHolder server(false, &loop, 0);
504   ASSERT_TRUE(server->Start());
505   SocketWrapper socket1(&loop);
506   socket1.Connect(HOST, server.port());
507   socket1.TestHttpRequest("/json/list", "[ ]");
508   server->Stop();
509   socket1.TestHttpRequest("/json/list", "[ ]");
510   socket1.Close();
511   uv_run(&loop, UV_RUN_DEFAULT);
512   ASSERT_TRUE(server.delegate_done);
513 }
514 
TEST_F(InspectorSocketServerTest,ClosingConnectionReportsDone)515 TEST_F(InspectorSocketServerTest, ClosingConnectionReportsDone) {
516   ServerHolder server(false, &loop, 0);
517   ASSERT_TRUE(server->Start());
518   SocketWrapper socket1(&loop);
519   socket1.Connect(HOST, server.port());
520   socket1.TestHttpRequest("/json/list", "[ ]");
521   server->Stop();
522   socket1.TestHttpRequest("/json/list", "[ ]");
523   socket1.Close();
524   uv_run(&loop, UV_RUN_DEFAULT);
525   ASSERT_TRUE(server.delegate_done);
526 }
527 
TEST_F(InspectorSocketServerTest,ClosingSocketReportsDone)528 TEST_F(InspectorSocketServerTest, ClosingSocketReportsDone) {
529   ServerHolder server(true, &loop, 0);
530   ASSERT_TRUE(server->Start());
531   SocketWrapper socket1(&loop);
532   socket1.Connect(HOST, server.port());
533   socket1.Write(WsHandshakeRequest(MAIN_TARGET_ID));
534   socket1.Expect(WS_HANDSHAKE_RESPONSE);
535   server->Stop();
536   ASSERT_FALSE(server.delegate_done);
537   socket1.Close();
538   SPIN_WHILE(!server.delegate_done);
539 }
540 
TEST_F(InspectorSocketServerTest,TerminatingSessionReportsDone)541 TEST_F(InspectorSocketServerTest, TerminatingSessionReportsDone) {
542   ServerHolder server(true, &loop, 0);
543   ASSERT_TRUE(server->Start());
544   SocketWrapper socket1(&loop);
545   socket1.Connect(HOST, server.port());
546   socket1.Write(WsHandshakeRequest(MAIN_TARGET_ID));
547   socket1.Expect(WS_HANDSHAKE_RESPONSE);
548   server->Stop();
549   ASSERT_FALSE(server.delegate_done);
550   server->TerminateConnections();
551   socket1.Expect(SERVER_CLOSE_FRAME);
552   socket1.Write(CLIENT_CLOSE_FRAME);
553   socket1.ExpectEOF();
554   SPIN_WHILE(!server.delegate_done);
555 }
556 
TEST_F(InspectorSocketServerTest,FailsToBindToNodejsHost)557 TEST_F(InspectorSocketServerTest, FailsToBindToNodejsHost) {
558   ServerHolder server(true, &loop, "nodejs.org", 80, nullptr);
559   ASSERT_FALSE(server->Start());
560   SPIN_WHILE(uv_loop_alive(&loop));
561 }
562 
has_ipv6_address()563 bool has_ipv6_address() {
564   uv_interface_address_s* addresses = nullptr;
565   int address_count = 0;
566   int err = uv_interface_addresses(&addresses, &address_count);
567   if (err != 0) {
568     return false;
569   }
570   bool has_address = false;
571   for (int i = 0; i < address_count; i++) {
572     if (addresses[i].address.address6.sin6_family == AF_INET6) {
573       has_address = true;
574     }
575   }
576   uv_free_interface_addresses(addresses, address_count);
577   return has_address;
578 }
579 
TEST_F(InspectorSocketServerTest,BindsToIpV6)580 TEST_F(InspectorSocketServerTest, BindsToIpV6) {
581   if (!has_ipv6_address()) {
582     fprintf(stderr, "No IPv6 network detected\n");
583     return;
584   }
585   ServerHolder server(true, &loop, "::", 0, nullptr);
586   ASSERT_TRUE(server->Start());
587   SocketWrapper socket1(&loop);
588   socket1.Connect("::1", server.port(), true);
589   socket1.Write(WsHandshakeRequest(MAIN_TARGET_ID));
590   socket1.Expect(WS_HANDSHAKE_RESPONSE);
591   server->Stop();
592   ASSERT_FALSE(server.delegate_done);
593   socket1.Close();
594   SPIN_WHILE(!server.delegate_done);
595 }
596