Skip to content

Commit

Permalink
Adds support for sending CLOSE frames without a payload as required b…
Browse files Browse the repository at this point in the history
…y spec. Updates tests to verify use case. See issue 8407. (helidon-io#8408)
  • Loading branch information
spericas authored and hrstoyanov committed Mar 12, 2024
1 parent 4674c55 commit 8740df5
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates.
* Copyright (c) 2023, 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -130,16 +130,29 @@ public WsSession pong(BufferData bufferData) {
return send(ClientWsFrame.control(WsOpCode.PONG, bufferData));
}

/**
* Closes WebSocket session. If {@code code} is negative, then a CLOSE frame
* with no code or reason is sent. This can be used to test CLOSE frames with
* no payload, as required by the spec.
*
* @param code close code, may be one of {@link WsCloseCodes}
* @param reason reason description
* @return the session
*/
@Override
public WsSession close(int code, String reason) {
closeSent = true;

byte[] reasonBytes = reason.getBytes(StandardCharsets.UTF_8);
BufferData bufferData = BufferData.create(2 + reasonBytes.length);
bufferData.writeInt16(code);
bufferData.write(reasonBytes);
send(ClientWsFrame.control(WsOpCode.CLOSE, bufferData));

// send empty close (no code or reason) if code is negative
if (code < 0) {
send(ClientWsFrame.control(WsOpCode.CLOSE, BufferData.empty()));
} else {
byte[] reasonBytes = reason.getBytes(StandardCharsets.UTF_8);
BufferData bufferData = BufferData.create(2 + reasonBytes.length);
bufferData.writeInt16(code);
bufferData.write(reasonBytes);
send(ClientWsFrame.control(WsOpCode.CLOSE, bufferData));
}
return this;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates.
* Copyright (c) 2023, 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -98,4 +98,23 @@ public void onOpen(WsSession session) {
assertThat(await, is(true));
assertThat(messages, hasItems(text));
}

/**
* Tests sending a close frame with no code or reason.
*/
@Test
void testEmptyClose() throws InterruptedException {
CountDownLatch messageLatch = new CountDownLatch(1);

wsClient.connect("/echo", new WsListener() {
@Override
public void onOpen(WsSession session) {
session.close(-1, ""); // empty close
messageLatch.countDown();
}
});

boolean await = messageLatch.await(10, TimeUnit.SECONDS);
assertThat(await, is(true));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -249,12 +249,13 @@ private boolean processFrame(ClientWsFrame frame) {
listener.onMessage(this, payload, frame.fin());
}
case CLOSE -> {
int status = payload.readInt16();
String reason;
int status = WsCloseCodes.NORMAL_CLOSE;
String reason = "normal";
if (payload.available() > 0) {
reason = payload.readString(payload.available(), StandardCharsets.UTF_8);
} else {
reason = "normal";
status = payload.readInt16();
if (payload.available() > 0) {
reason = payload.readString(payload.available(), StandardCharsets.UTF_8);
}
}
listener.onClose(this, status, reason);
if (!closeSent) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates.
* Copyright (c) 2023, 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -113,7 +113,8 @@ protected static FrameHeader readFrameHeader(DataReader reader, int maxFrameLeng
}

protected static BufferData readPayload(DataReader reader, FrameHeader header) {
return reader.readBuffer(header.length());
int length = header.length();
return length == 0 ? BufferData.empty() : reader.readBuffer(length);
}

protected static boolean isPayload(FrameHeader header) {
Expand Down

0 comments on commit 8740df5

Please sign in to comment.