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

io.fabric8.kubernetes.client.jdkhttp.JdkWebSocketImpl Maven / Gradle / Ivy

There is a newer version: 7.0.1
Show newest version
/*
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.fabric8.kubernetes.client.jdkhttp;

import io.fabric8.kubernetes.client.http.BufferUtil;
import io.fabric8.kubernetes.client.http.WebSocket;
import io.fabric8.kubernetes.client.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

class JdkWebSocketImpl implements WebSocket, java.net.http.WebSocket.Listener {

  private static final Logger LOG = LoggerFactory.getLogger(JdkWebSocketImpl.class);

  private volatile java.net.http.WebSocket webSocket;
  private final AtomicLong queueSize = new AtomicLong();
  private final Listener listener;
  private final StringBuilder stringBuilder = new StringBuilder();
  private final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
  private final WritableByteChannel byteChannel = Channels.newChannel(byteArrayOutputStream);
  private final CompletableFuture terminated = new CompletableFuture<>();

  public JdkWebSocketImpl(Listener listener) {
    this.listener = listener;
  }

  @Override
  public CompletionStage onBinary(java.net.http.WebSocket webSocket, ByteBuffer data, boolean last) {
    try {
      byteChannel.write(data);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
    if (last) {
      ByteBuffer value = ByteBuffer.wrap(byteArrayOutputStream.toByteArray());
      byteArrayOutputStream.reset();
      listener.onMessage(this, value);
    } else {
      webSocket.request(1);
    }
    return null;
  }

  @Override
  public CompletionStage onText(java.net.http.WebSocket webSocket, CharSequence data, boolean last) {
    stringBuilder.append(data);
    if (last) {
      String value = stringBuilder.toString();
      stringBuilder.setLength(0);
      listener.onMessage(this, value);
    } else {
      webSocket.request(1);
    }
    return null;
  }

  @Override
  public CompletionStage onClose(java.net.http.WebSocket webSocket, int statusCode, String reason) {
    terminated.complete(null);
    listener.onClose(this, statusCode, reason);
    return null; // should immediately initiate an implicit sendClose
  }

  @Override
  public void onError(java.net.http.WebSocket webSocket, Throwable error) {
    terminated.complete(null);
    listener.onError(this, error);
  }

  @Override
  public void onOpen(java.net.http.WebSocket webSocket) {
    this.webSocket = webSocket;
    webSocket.request(1);
    listener.onOpen(this);
  }

  @Override
  public boolean send(ByteBuffer buffer) {
    buffer = BufferUtil.copy(buffer);
    final int size = buffer.remaining();
    queueSize.addAndGet(size);
    CompletableFuture cf = webSocket.sendBinary(buffer, true);
    if (cf.isDone()) {
      queueSize.addAndGet(-size);
      return !cf.isCompletedExceptionally();
    }
    cf.whenComplete((b, t) -> {
      if (t != null) {
        LOG.warn("Queued write did not succeed", t);
        abort();
      }
      queueSize.addAndGet(-size);
    });
    return true;
  }

  @Override
  public synchronized boolean sendClose(int code, String reason) {
    if (webSocket.isOutputClosed()) {
      return false;
    }
    CompletableFuture cf = webSocket.sendClose(code, reason == null ? "Closing" : reason);
    cf = cf.whenComplete((w, t) -> {
      if (t != null) {
        LOG.warn("Queued close did not succeed", t);
        abort();
      } else if (w != null) {
        webSocket.request(1); // there may not be demand, so request more
        CompletableFuture future = Utils.schedule(Runnable::run, this::abort, 1, TimeUnit.MINUTES);
        terminated.whenComplete((v, ignored) -> future.cancel(true));
      }
    });
    return !cf.isCompletedExceptionally();
  }

  private void abort() {
    if (!webSocket.isOutputClosed() || !webSocket.isInputClosed()) {
      LOG.warn("Aborting WebSocket due to a write error or failure with sendClose");
      webSocket.abort();
      if (terminated.complete(null)) {
        listener.onClose(this, 1006, "Aborted the WebSocket");
      }
    }
  }

  @Override
  public long queueSize() {
    return queueSize.get();
  }

  @Override
  public void request() {
    this.webSocket.request(1);
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy