code: public SignalServiceEnvelope read(long timeout, TimeUnit unit, MessagePipeCallback callback)
        throws TimeoutException, IOException, InvalidVersionException
    {
      if (!credentialsProvider.isPresent()) {
        throw new IllegalArgumentException("You can't read messages if you haven't specified credentials");
      }
  
      while (true) {
        WebSocketRequestMessage  request            = websocket.readRequest(unit.toMillis(timeout));
        WebSocketResponseMessage response           = createWebSocketResponse(request);
        boolean                  signalKeyEncrypted = isSignalKeyEncrypted(request);
  
        try {
          if (isSignalServiceEnvelope(request)) {
            SignalServiceEnvelope envelope = new SignalServiceEnvelope(request.getBody().toByteArray(),
                                                                       credentialsProvider.get().getSignalingKey(),
                                                                       signalKeyEncrypted);
  
            callback.onMessage(envelope);
            return envelope;
          }
        } finally {
          websocket.sendResponse(response);
        }
      }
    }
docs: A blocking call that reads a message off the pipe (see {@link #read(long, java.util.concurrent.TimeUnit)}
  
  Unlike {@link #read(long, java.util.concurrent.TimeUnit)}, this method allows you
  to specify a callback that will be called before the received message is acknowledged.
  This allows you to write the received message to durable storage before acknowledging
  receipt of it to the server.
  
  @param timeout The timeout to wait for.
  @param unit The timeout time unit.
  @param callback A callback that will be called before the message receipt is
  acknowledged to the server.
  @return The message read (same as the message sent through the callback).
  @throws TimeoutException
  @throws IOException
  @throws InvalidVersionException