private void serviceDisconnect(
     String targetId, HttpServletRequest request, HttpServletResponse response) {
   // Do not remove the ClientDelegate from the gateway here,
   // since closing the ClientDelegate will resume the connect request
   // and we remove the ClientDelegate from the gateway there
   ClientDelegate client = gateway.getClientDelegate(targetId);
   if (client != null) client.close();
 }
 private void expireConnect(ClientDelegate client, long time) {
   String targetId = client.getTargetId();
   logger.info(
       "Client with targetId {} missing, last seen {} ms ago, closing it",
       targetId,
       System.currentTimeMillis() - time);
   client.close();
   // If the client expired, means that it did not connect,
   // so there no request to resume, and we cleanup here
   // (while normally this cleanup is done in serviceConnect())
   unschedule(targetId);
   gateway.removeClientDelegate(targetId);
 }
  private void serviceConnect(
      String targetId, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
      throws IOException {
    unschedule(targetId);

    ClientDelegate client = gateway.getClientDelegate(targetId);
    if (client == null) {
      // Expired client tries to connect without handshake
      httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
      return;
    }

    flush(client, httpRequest, httpResponse);

    if (client.isClosed()) gateway.removeClientDelegate(targetId);
  }
  private void flush(
      ClientDelegate client, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
      throws IOException {
    List<RHTTPRequest> requests = client.process(httpRequest);
    if (requests != null) {
      // Schedule before sending the requests, to avoid that the remote client
      // reconnects before we have scheduled the expiration timeout.
      if (!client.isClosed()) schedule(client);

      ServletOutputStream output = httpResponse.getOutputStream();
      for (RHTTPRequest request : requests) output.write(request.getFrameBytes());
      // I could count the framed bytes of all requests and set a Content-Length header,
      // but the implementation of ServletOutputStream takes care of everything:
      // if the request was HTTP/1.1, then flushing result in a chunked response, but the
      // client know how to handle it; if the request was HTTP/1.0, then no chunking.
      // To avoid chunking in HTTP/1.1 I must set the Content-Length header.
      output.flush();
      logger.debug("Delivered to device {} requests {} ", client.getTargetId(), requests);
    }
  }
 private void schedule(ClientDelegate client) {
   Future<?> task =
       scheduler.schedule(new ClientExpirationTask(client), clientTimeout, TimeUnit.MILLISECONDS);
   Future<?> existing = expirations.put(client.getTargetId(), task);
   assert existing == null;
 }