Skip to content

WebSocket is close before output stream are flushed #2386

@dani8art

Description

@dani8art

Describe the bug
When WebSocketStreamHandled.java#close is called as part of an ExecProcess#destroy there could be data that is not being flushed from the corresponding output stream. This is due to the execution order, this order may be changed by the following and we will be closing the WebSocket in a safer way.

  @Override
  public synchronized void close() {
    if (state != State.CLOSED) {
      // Close all output streams.  Caller of getInputStream(int) is responsible
      // for closing returned input streams
      for (PipedOutputStream out : pipedOutput.values()) {
        try {
          out.flush();
        } catch (IOException ex) {
          log.error("Error on flush", ex);
        }
        try {
          out.close();
        } catch (IOException ex) {
          log.error("Error on close", ex);
        }
      }
      for (OutputStream out : output.values()) {
        try {
          out.flush();
        } catch (IOException ex) {
          log.error("Error on flush", ex);
        }
        try {
          out.close();
        } catch (IOException ex) {
          log.error("Error on close", ex);
        }
      }

      state = State.CLOSED;
      if (null != socket) {
        // code 1000 means "Normal Closure"
        socket.close(1000, "Triggered client-side terminate");
        log.debug("Successfully closed socket.");
      }
    }
    notifyAll();
  }

Client Version
e.g. 14.0.0

Kubernetes Version
e.g. 1.24.x

Java Version
e.g. Java 17

To Reproduce

  • Prepare some large files, it could be like 4-5 files of 1Mb each of them.
  • Prepare a script to copy then like
   try {
      Copy kubeCopy = new Copy(client);

      int copyExit = kubeCopy.copyFileToPodAsync(namespace, host, null, localPath, remotePath).get(1, TimeUnit.SECONDS);

      if (copyExit > 0) {
        throw Errors.internal("Copy %s to %s:%s:%s failed", localPath, namespace, host, remotePath).get();
      }
    } catch (ApiException | IOException | InterruptedException | ExecutionException e) {
      throw Errors.internal("Error deploying the context to the remote host", e).get();
    }
  • Then close() may be called while there are data in the WebSocket queue, making the WebSocketStreamHandler.this.socket.send(byteString) return false because of this piece of code
if (failed || enqueuedClose) return false // where enqueuedClose=true

Expected behaviour

WebSocketStreamHandler lets enqueue data flush before closing the web socket.

KubeConfig
N/A

Server (please complete the following information):

  • OS: Linux
  • Environment: container
  • Cloud: Any

Additional context
Add any other context about the problem here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions