/**
  * {@inheritdoc}
  */
 public function upgradeConnection(BufferedDuplexStreamInterface $socket, HttpRequest $request, HttpResponse $response, HttpEndpoint $endpoint, callable $action) : \Generator
 {
     $response = $this->assertUpgradePossible($request, $response);
     if ($response instanceof HttpResponse) {
         return $response;
     }
     // Discard HTTP request body before switching to web socket protocol.
     $body = (yield from $request->getBody()->getInputStream());
     try {
         while (!$body->eof()) {
             yield from $body->read();
         }
     } finally {
         $body->close();
     }
     ((yield currentTask()))->setAutoShutdown(true);
     try {
         yield from $this->sendHandshake($socket, $request);
         if ($this->logger) {
             $this->logger->debug('Upgraded HTTP/{version} connection to {protocol}', ['version' => $request->getProtocolVersion(), 'protocol' => 'WebSocket']);
         }
         $client = new Client($socket, $this->logger);
         $result = $this->app->open($client);
         if ($result instanceof \Generator) {
             yield from $result;
         }
         $decoder = new MessageDecoder($socket, true, $this->logger);
         while (!$socket->eof()) {
             $message = (yield from $decoder->readNextMessage($client->id));
             if ($message === false) {
                 break;
             }
             $client->time = time();
             if ($message instanceof Frame) {
                 switch ($message->opcode) {
                     case Frame::CONNECTION_CLOSE:
                         break 2;
                     case Frame::PING:
                         yield from $socket->write((new Frame(Frame::PONG, $message->data))->encode(), 10000);
                         break;
                 }
                 continue;
             }
             if (\is_string($message)) {
                 $result = $this->app->onMessage($message, $client);
             } elseif ($message instanceof InputStreamInterface) {
                 $result = $this->app->onBinaryMessage($message, $client);
             }
             if ($result instanceof \Generator) {
                 $task = (yield runTask($result));
                 $task->onError(function (ExecutorInterface $executor, Task $task, \Throwable $e) {
                     $this->app->onError($e);
                 });
             }
         }
     } catch (TaskCanceledException $e) {
         yield from $this->sendCloseFrame($socket, $e);
         $client->disconnect();
         $result = $this->app->close($client);
         if ($result instanceof \Generator) {
             yield from $result;
         }
     } finally {
         $socket->close();
     }
 }
 protected function handleFrames() : \Generator
 {
     ((yield currentTask()))->setAutoShutdown(true);
     $decoder = new MessageDecoder($this->stream, false, $this->logger);
     try {
         while (true) {
             $message = (yield from $decoder->readNextMessage());
             if ($message === false) {
                 break;
             }
             if ($message instanceof Frame) {
                 switch ($message->opcode) {
                     case Frame::CONNECTION_CLOSE:
                         break 2;
                     case Frame::PING:
                         yield from $this->stream->write((new Frame(Frame::PONG, $message->data))->encode(), 10000);
                         break;
                 }
                 continue;
             }
             $this->events->emit(new MessageReceivedEvent($message, $this));
         }
     } catch (TaskCanceledException $e) {
         $frame = new Frame(Frame::CONNECTION_CLOSE, pack('n', Frame::CODE_NORMAL_CLOSURE));
         yield from $this->stream->write($frame->encode(random_bytes(4)), 100000);
     } finally {
         $this->watcher = NULL;
         $this->stream->close();
     }
 }