All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.contek.invoker.bybit.api.websocket.WebSocketAuthenticator Maven / Gradle / Ivy

package io.contek.invoker.bybit.api.websocket;

import com.google.common.collect.ImmutableList;
import io.contek.invoker.bybit.api.websocket.common.WebSocketOperationRequest;
import io.contek.invoker.bybit.api.websocket.common.WebSocketOperationResponse;
import io.contek.invoker.commons.websocket.AnyWebSocketMessage;
import io.contek.invoker.commons.websocket.IWebSocketAuthenticator;
import io.contek.invoker.commons.websocket.WebSocketSession;
import io.contek.invoker.security.ICredential;
import org.slf4j.Logger;

import javax.annotation.concurrent.ThreadSafe;
import java.time.Clock;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicBoolean;

import static io.contek.invoker.bybit.api.websocket.common.constants.WebSocketOperationKeys._auth;
import static org.slf4j.LoggerFactory.getLogger;

@ThreadSafe
final class WebSocketAuthenticator implements IWebSocketAuthenticator {

  private static final Duration EXPIRE_DELAY = Duration.ofSeconds(30);
  private static final Logger log = getLogger(WebSocketAuthenticator.class);

  private final ICredential credential;
  private final WebSocketRequestIdGenerator requestIdGenerator;
  private final Clock clock;

  private final AtomicBoolean pending = new AtomicBoolean();
  private final AtomicBoolean authenticated = new AtomicBoolean();

  WebSocketAuthenticator(
      ICredential credential, WebSocketRequestIdGenerator requestIdGenerator, Clock clock) {
    this.credential = credential;
    this.requestIdGenerator = requestIdGenerator;
    this.clock = clock;
  }

  @Override
  public void handshake(WebSocketSession session) {
    if (credential.isAnonymous()) {
      return;
    }
    String key = credential.getApiKeyId();

    // Add more time to account for network delay.
    String expires = Long.toString(clock.instant().plus(EXPIRE_DELAY).toEpochMilli());
    String payload = "GET/realtime" + expires;
    String signature = credential.sign(payload);

    WebSocketOperationRequest request = new WebSocketOperationRequest();
    request.req_id = requestIdGenerator.generateNext();
    request.op = _auth;
    request.args = ImmutableList.of(key, expires, signature);

    log.info("Requesting authentication for {}.", credential.getApiKeyId());
    session.send(request);
    pending.set(true);
  }

  @Override
  public boolean isPending() {
    return pending.get();
  }

  @Override
  public boolean isCompleted() {
    return credential.isAnonymous() || authenticated.get();
  }

  @Override
  public void onMessage(AnyWebSocketMessage message, WebSocketSession session) {
    if (isCompleted()) {
      return;
    }

    if (!(message instanceof WebSocketOperationResponse confirmation)) {
      return;
    }

    if (!confirmation.op.equals(_auth)) {
      return;
    }

    pending.set(false);
    if (!confirmation.success) {
      throw new IllegalStateException();
    }

    authenticated.set(true);
    log.info("Authentication for {} completed.", credential.getApiKeyId());
  }

  @Override
  public void afterDisconnect() {
    pending.set(false);
    authenticated.set(false);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy