diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index 0d3a895f9..1ef793c14 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -1,7 +1,7 @@ [package] org = "ballerina" name = "websocket" -version = "2.13.1" +version = "2.13.2" authors = ["Ballerina"] keywords = ["ws", "network", "bi-directional", "streaming", "service", "client"] repository = "https://github.com/ballerina-platform/module-ballerina-websocket" @@ -15,8 +15,8 @@ graalvmCompatible = true [[platform.java21.dependency]] groupId = "io.ballerina.stdlib" artifactId = "websocket-native" -version = "2.13.1" -path = "../native/build/libs/websocket-native-2.13.1.jar" +version = "2.13.2" +path = "../native/build/libs/websocket-native-2.13.2-SNAPSHOT.jar" [[platform.java21.dependency]] groupId = "io.ballerina.stdlib" @@ -85,5 +85,5 @@ version = "4.1.118.Final" path = "./lib/netty-handler-proxy-4.1.118.Final.jar" [[platform.java21.dependency]] -path = "../test-utils/build/libs/websocket-test-utils-2.13.1.jar" +path = "../test-utils/build/libs/websocket-test-utils-2.13.2-SNAPSHOT.jar" scope = "testOnly" diff --git a/ballerina/CompilerPlugin.toml b/ballerina/CompilerPlugin.toml index ab54a682e..4920708ae 100644 --- a/ballerina/CompilerPlugin.toml +++ b/ballerina/CompilerPlugin.toml @@ -3,4 +3,4 @@ id = "websocket-compiler-plugin" class = "io.ballerina.stdlib.websocket.plugin.WebSocketCompilerPlugin" [[dependency]] -path = "../compiler-plugin/build/libs/websocket-compiler-plugin-2.13.1.jar" +path = "../compiler-plugin/build/libs/websocket-compiler-plugin-2.13.2-SNAPSHOT.jar" diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 331a030a6..44cb70183 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -76,7 +76,7 @@ dependencies = [ [[package]] org = "ballerina" name = "http" -version = "2.13.1" +version = "2.13.2" dependencies = [ {org = "ballerina", name = "auth"}, {org = "ballerina", name = "cache"}, @@ -342,7 +342,7 @@ dependencies = [ [[package]] org = "ballerina" name = "websocket" -version = "2.13.1" +version = "2.13.2" dependencies = [ {org = "ballerina", name = "auth"}, {org = "ballerina", name = "constraint"}, diff --git a/ballerina/close_frame_return_types.bal b/ballerina/close_frame_return_types.bal new file mode 100644 index 000000000..7de2fa4a7 --- /dev/null +++ b/ballerina/close_frame_return_types.bal @@ -0,0 +1,101 @@ +// Copyright (c) 2025, WSO2 LLC. (http://www.wso2.org). +// +// WSO2 LLC. licenses this file to you 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. + +public readonly distinct class PredefinedCloseFrameType { +}; + +public readonly distinct class CustomCloseFrameType { +}; + +public final PredefinedCloseFrameType PREDEFINED_CLOSE_FRAME = new; +public final CustomCloseFrameType CUSTOM_CLOSE_FRAME = new; + +type CloseFrameBase record {| + readonly object {} 'type; + readonly int status; + string reason?; +|}; + +public type CustomCloseFrame record {| + *CloseFrameBase; + readonly CustomCloseFrameType 'type = CUSTOM_CLOSE_FRAME; +|}; + +public type NormalClosure record {| + *CloseFrameBase; + readonly PredefinedCloseFrameType 'type = PREDEFINED_CLOSE_FRAME; + readonly 1000 status = 1000; +|}; + +public type GoingAway record {| + *CloseFrameBase; + readonly PredefinedCloseFrameType 'type = PREDEFINED_CLOSE_FRAME; + readonly 1001 status = 1001; +|}; + +public type ProtocolError record {| + *CloseFrameBase; + readonly PredefinedCloseFrameType 'type = PREDEFINED_CLOSE_FRAME; + readonly 1002 status = 1002; + string reason = "Connection closed due to protocol error"; +|}; + +public type UnsupportedData record {| + *CloseFrameBase; + readonly PredefinedCloseFrameType 'type = PREDEFINED_CLOSE_FRAME; + readonly 1003 status = 1003; + string reason = "Endpoint received unsupported frame"; +|}; + +public type InvalidPayload record {| + *CloseFrameBase; + readonly PredefinedCloseFrameType 'type = PREDEFINED_CLOSE_FRAME; + readonly 1007 status = 1007; + string reason = "Payload does not match the expected format or encoding"; +|}; + +public type PolicyViolation record {| + *CloseFrameBase; + readonly PredefinedCloseFrameType 'type = PREDEFINED_CLOSE_FRAME; + readonly 1008 status = 1008; + string reason = "Received message violates its policy"; +|}; + +public type MessageTooBig record {| + *CloseFrameBase; + readonly PredefinedCloseFrameType 'type = PREDEFINED_CLOSE_FRAME; + readonly 1009 status = 1009; + string reason = "The received message exceeds the allowed size limit"; +|}; + +public type InternalServerError record {| + *CloseFrameBase; + readonly PredefinedCloseFrameType 'type = PREDEFINED_CLOSE_FRAME; + readonly 1011 status = 1011; + string reason = "Internal server error occurred"; +|}; + +public final readonly & NormalClosure NORMAL_CLOSURE = {}; +public final readonly & GoingAway GOING_AWAY = {}; +public final readonly & ProtocolError PROTOCOL_ERROR = {}; +public final readonly & UnsupportedData UNSUPPORTED_DATA = {}; +public final readonly & InvalidPayload INVALID_PAYLOAD = {}; +public final readonly & PolicyViolation POLICY_VIOLATION = {}; +public final readonly & MessageTooBig MESSAGE_TOO_BIG = {}; +public final readonly & InternalServerError INTERNAL_SERVER_ERROR = {}; + +public type CloseFrame NormalClosure|GoingAway|ProtocolError|UnsupportedData|InvalidPayload| + PolicyViolation|MessageTooBig|InternalServerError|CustomCloseFrame; diff --git a/ballerina/tests/close_frame_test.bal b/ballerina/tests/close_frame_test.bal new file mode 100644 index 000000000..c171b2277 --- /dev/null +++ b/ballerina/tests/close_frame_test.bal @@ -0,0 +1,310 @@ +// Copyright (c) 2025, WSO2 LLC. (http://www.wso2.org). +// +// WSO2 LLC. licenses this file to you 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. + +import ballerina/test; + +listener Listener l102 = new (22082); +listener Listener l103 = new (22083); +listener Listener l104 = new (22084); +listener Listener l105 = new (22085); +listener Listener l106 = new (22086); +listener Listener l107 = new (22087); +listener Listener l108 = new (22088); +listener Listener l109 = new (22089); +listener Listener l110 = new (22090); +listener Listener l111 = new (22091); + +service /onCloseFrame on l102 { + resource function get .() returns Service|UpgradeError { + return new WsService102(); + } +} + +service /onCloseFrame on l103 { + resource function get .() returns Service|UpgradeError { + return new WsService103(); + } +} + +service /onCloseFrame on l104 { + resource function get .() returns Service|UpgradeError { + return new WsService104(); + } +} + +service /onCloseFrame on l105 { + resource function get .() returns Service|UpgradeError { + return new WsService105(); + } +} + +service /onCloseFrame on l106 { + resource function get .() returns Service|UpgradeError { + return new WsService106(); + } +} + +service /onCloseFrame on l107 { + resource function get .() returns Service|UpgradeError { + return new WsService107(); + } +} + +service /onCloseFrame on l108 { + resource function get .() returns Service|UpgradeError { + return new WsService108(); + } +} + +service /onCloseFrame on l109 { + resource function get .() returns Service|UpgradeError { + return new WsService109(); + } +} + +service /onCloseFrame on l110 { + resource function get .() returns Service|UpgradeError { + return new WsService110(); + } +} + +@ServiceConfig { + dispatcherKey: "event", + dispatcherStreamId: "id" +} +service /onCloseFrame on l111 { + resource function get .() returns Service|UpgradeError { + return new WsService111(); + } +} + +service class WsService102 { + *Service; + + remote function onMessage(Caller caller, string data) returns CloseFrame { + return NORMAL_CLOSURE; + } +} + +service class WsService103 { + *Service; + + remote function onMessage(Caller caller, string data) returns CloseFrame { + return GOING_AWAY; + } +} + +service class WsService104 { + *Service; + + remote function onMessage(Caller caller, string data) returns CloseFrame { + return PROTOCOL_ERROR; + } +} + +service class WsService105 { + *Service; + + remote function onMessage(Caller caller, string data) returns CloseFrame { + return UNSUPPORTED_DATA; + } +} + +service class WsService106 { + *Service; + + remote function onMessage(Caller caller, string data) returns CloseFrame { + return INVALID_PAYLOAD; + } +} + +service class WsService107 { + *Service; + + remote function onMessage(Caller caller, string data) returns CloseFrame { + return POLICY_VIOLATION; + } +} + +service class WsService108 { + *Service; + + remote function onMessage(Caller caller, string data) returns CloseFrame { + return MESSAGE_TOO_BIG; + } +} + +service class WsService109 { + *Service; + + remote function onMessage(Caller caller, string data) returns CloseFrame { + return INTERNAL_SERVER_ERROR; + } +} + +service class WsService110 { + *Service; + + remote function onMessage(Caller caller, string data) returns CloseFrame { + return {status: 3555, reason: "Custom close frame message"}; + } +} + +service class WsService111 { + *Service; + + remote function onHeartbeat(Caller caller, string data) returns CloseFrame { + return NORMAL_CLOSURE; + } +} + +public function getErrorMessage(CloseFrame closeFrame) returns string { + string? reason = closeFrame.reason; + if reason is string { + return reason + ": Status code: " + closeFrame.status.toString(); + } + return "Connection closed Status code: " + closeFrame.status.toString(); +} + +@test:Config { + groups: ["closeFrame"] +} +public function testNormalClosure() returns Error? { + Client wsClient = check new ("ws://localhost:22082/onCloseFrame"); + check wsClient->writeMessage("Hi"); + anydata|Error res = wsClient->readMessage(); + test:assertTrue(res is Error); + if res is Error { + test:assertEquals(res.message(), getErrorMessage(NORMAL_CLOSURE)); + } +} + +@test:Config { + groups: ["closeFrame"] +} +public function testGoingAway() returns Error? { + Client wsClient = check new ("ws://localhost:22083/onCloseFrame"); + check wsClient->writeMessage("Hi"); + anydata|Error res = wsClient->readMessage(); + test:assertTrue(res is Error); + if res is Error { + test:assertEquals(res.message(), getErrorMessage(GOING_AWAY)); + } +} + +@test:Config { + groups: ["closeFrame"] +} +public function testProtocolError() returns Error? { + Client wsClient = check new ("ws://localhost:22084/onCloseFrame"); + check wsClient->writeMessage("Hi"); + anydata|Error res = wsClient->readMessage(); + test:assertTrue(res is Error); + if res is Error { + test:assertEquals(res.message(), getErrorMessage(PROTOCOL_ERROR)); + } +} + +@test:Config { + groups: ["closeFrame"] +} +public function testUnsupportedData() returns Error? { + Client wsClient = check new ("ws://localhost:22085/onCloseFrame"); + check wsClient->writeMessage("Hi"); + anydata|Error res = wsClient->readMessage(); + test:assertTrue(res is Error); + if res is Error { + test:assertEquals(res.message(), getErrorMessage(UNSUPPORTED_DATA)); + } +} + +@test:Config { + groups: ["closeFrame"] +} +public function testInvalidPayload() returns Error? { + Client wsClient = check new ("ws://localhost:22086/onCloseFrame"); + check wsClient->writeMessage("Hi"); + anydata|Error res = wsClient->readMessage(); + test:assertTrue(res is Error); + if res is Error { + test:assertEquals(res.message(), getErrorMessage(INVALID_PAYLOAD)); + } +} + +@test:Config { + groups: ["closeFrame"] +} +public function testPolicyViolation() returns Error? { + Client wsClient = check new ("ws://localhost:22087/onCloseFrame"); + check wsClient->writeMessage("Hi"); + anydata|Error res = wsClient->readMessage(); + test:assertTrue(res is Error); + if res is Error { + test:assertEquals(res.message(), getErrorMessage(POLICY_VIOLATION)); + } +} + +@test:Config { + groups: ["closeFrame"] +} +public function testMessageTooBig() returns Error? { + Client wsClient = check new ("ws://localhost:22088/onCloseFrame"); + check wsClient->writeMessage("Hi"); + anydata|Error res = wsClient->readMessage(); + test:assertTrue(res is Error); + if res is Error { + test:assertEquals(res.message(), getErrorMessage(MESSAGE_TOO_BIG)); + } +} + +@test:Config { + groups: ["closeFrame"] +} +public function testInternalServerError() returns Error? { + Client wsClient = check new ("ws://localhost:22089/onCloseFrame"); + check wsClient->writeMessage("Hi"); + anydata|Error res = wsClient->readMessage(); + test:assertTrue(res is Error); + if res is Error { + test:assertEquals(res.message(), getErrorMessage(INTERNAL_SERVER_ERROR)); + } +} + +@test:Config { + groups: ["closeFrame"] +} +public function testCustomCloseFrame() returns Error? { + Client wsClient = check new ("ws://localhost:22090/onCloseFrame"); + check wsClient->writeMessage("Hi"); + anydata|Error res = wsClient->readMessage(); + test:assertTrue(res is Error); + if res is Error { + test:assertEquals(res.message(), "Custom close frame message: Status code: 3555"); + } +} + +@test:Config { + groups: ["closeFrame"] +} +public function testCustomDispatcher() returns Error? { + Client wsClient = check new ("ws://localhost:22091/onCloseFrame"); + check wsClient->writeMessage({"event": "heartbeat", "id": "1"}); + anydata|Error res = wsClient->readMessage(); + test:assertTrue(res is Error); + if res is Error { + test:assertEquals(res.message(), getErrorMessage(NORMAL_CLOSURE)); + } +} diff --git a/ballerina/tests/websocket_client_exceptions.bal b/ballerina/tests/websocket_client_exceptions.bal index b56e75116..afa125622 100644 --- a/ballerina/tests/websocket_client_exceptions.bal +++ b/ballerina/tests/websocket_client_exceptions.bal @@ -83,7 +83,7 @@ public function testLongFrameError() returns Error? { runtime:sleep(0.5); var err = wsClientEp->ping(pingData); if err is error { - test:assertEquals(err.message(), "ProtocolError: io.netty.handler.codec.TooLongFrameException: " + + test:assertEquals(err.message(), "CorruptedFrameError: io.netty.handler.codec.TooLongFrameException: " + "invalid payload for PING (payload length must be <= 125, was 148"); } else { test:assertFail("Mismatched output"); diff --git a/ballerina/websocket_errors.bal b/ballerina/websocket_errors.bal index 540ddc37d..c7082a503 100644 --- a/ballerina/websocket_errors.bal +++ b/ballerina/websocket_errors.bal @@ -24,7 +24,7 @@ public type InvalidHandshakeError distinct Error; public type PayloadTooLargeError distinct Error; # Raised when the other side breaks the protocol. -public type ProtocolError distinct Error; +public type CorruptedFrameError distinct Error; # Raised during connection failures. public type ConnectionError distinct Error; @@ -33,7 +33,7 @@ public type ConnectionError distinct Error; public type ConnectionClosureError distinct ConnectionError; # Raised when an out of order/invalid continuation frame is received. -public type InvalidContinuationFrameError distinct ProtocolError; +public type InvalidContinuationFrameError distinct CorruptedFrameError; # Raised when the WebSocket upgrade is not accepted. public type UpgradeError distinct Error; diff --git a/changelog.md b/changelog.md index a73a4a4e9..f580a5ecb 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Added + +- [Implement websocket close frame support](https://github.com/ballerina-platform/ballerina-library/issues/7578) + ### Changed - [Move SSL context creation to the client initialization](https://github.com/ballerina-platform/ballerina-library/issues/1798) diff --git a/docs/spec/spec.md b/docs/spec/spec.md index 5dea9b243..904baa99b 100644 --- a/docs/spec/spec.md +++ b/docs/spec/spec.md @@ -36,6 +36,7 @@ The conforming implementation of the specification is released and included in t * [onError](#onerror) * 3.2.2. [Dispatching custom remote methods](#322-dispatching-custom-remote-methods) * [Dispatching custom error remote methods](#Dispatching custom error remote methods) + * 3.2.3. [Return types](#323-return-types) 4. [Client](#4-client) * 4.1. [Client Configurations](#41-client-configurations) * 4.2. [Initialization](#42-initialization) @@ -397,6 +398,47 @@ dispatching error remote function = "onHeartbeatError" 3. If an unmatching message type receives where a matching remote function is not implemented in the WebSocket service by the user, it gets dispatched to the default `onMessage` remote function if it is implemented. Or else it will get ignored. +#### 3.2.3. Return types + +The resource method supports `records`, `string`, `int`, `boolean`, `decimal`, `float` ,`json`, `xml` and `websocket:CloseFrame` as return types. +Whenever user returns a particular output, that will result in an websocket response to the caller who initiated the call. Therefore, user does not necessarily depend on the `websocket:Caller` and its remote methods to proceed with the response. + +```ballerina +remote isolated function onMessage(string data) returns User|string|int|boolean|decimal|float|json|xml|websocket:CloseFrame { +} +``` + +##### 3.2.3.1. Close Frame Records + +The `CloseFrame` Records represent WebSocket close frames. When a service returns a close frame record, the WebSocket module will automatically send the corresponding close frame and terminate the connection. + +Following is the `websocket:NormalClosure` definition. Likewise, some predefined close frame records are provided. + +```ballerina +public type NormalClosure record {| + *CloseFrameBase; + readonly PredefinedCloseFrameType 'type = PREDEFINED_CLOSE_FRAME; + readonly 1000 status = 1000; +|}; + +remote isolated function onMessage(string data) returns websocket:CloseFrame { + websocket:NormalClosure normalClosure = {reason: "Normal Closure"}; + return normalClosure; +} +``` + +Custom Close Frame records enable users to define close frames with any status code and reason within the range of 1000–4999. + +```ballerina +public type InvalidUserCloseFrame record {| + *websocket:CustomCloseFrame; +|}; + +remote isolated function onMessage(string data) returns InvalidUserCloseFrame { + return {status: 4444, reason: "Invalid User"}; +} +``` + ## 4. [Client](#4-client) `websocket:Client` can be used to send and receive data synchronously over WebSocket connection. The underlying implementation is non-blocking. @@ -760,4 +802,4 @@ public function main() returns error? { string stringResp = check 'string:fromBytes(byteResp); io:println(stringResp); } -``` \ No newline at end of file +``` diff --git a/native/src/main/java/io/ballerina/stdlib/websocket/WebSocketConstants.java b/native/src/main/java/io/ballerina/stdlib/websocket/WebSocketConstants.java index e3ad7aa16..793a853a4 100644 --- a/native/src/main/java/io/ballerina/stdlib/websocket/WebSocketConstants.java +++ b/native/src/main/java/io/ballerina/stdlib/websocket/WebSocketConstants.java @@ -139,6 +139,13 @@ public class WebSocketConstants { public static final String CONNECTION_CLOSED = "Connection closed"; public static final String STATUS_CODE = "Status code:"; + // Close Frame Records + public static final BString CLOSE_FRAME_TYPE = StringUtils.fromString("type"); + public static final BString CLOSE_FRAME_STATUS_CODE = StringUtils.fromString("status"); + public static final BString CLOSE_FRAME_REASON = StringUtils.fromString("reason"); + public static final String PREDEFINED_CLOSE_FRAME_TYPE = PACKAGE_WEBSOCKET + COLON + "PredefinedCloseFrameType"; + public static final String CUSTOM_CLOSE_FRAME_TYPE = PACKAGE_WEBSOCKET + COLON + "CustomCloseFrameType"; + private WebSocketConstants() { } @@ -150,7 +157,7 @@ public enum ErrorCode { ConnectionClosureError("ConnectionClosureError"), InvalidHandshakeError("InvalidHandshakeError"), PayloadTooLargeError("PayloadTooLargeError"), - ProtocolError("ProtocolError"), + CorruptedFrameError("CorruptedFrameError"), ConnectionError("ConnectionError"), InvalidContinuationFrameError("InvalidContinuationFrameError"), HandshakeTimedOut("HandshakeTimedOut"), diff --git a/native/src/main/java/io/ballerina/stdlib/websocket/WebSocketResourceDispatcher.java b/native/src/main/java/io/ballerina/stdlib/websocket/WebSocketResourceDispatcher.java index c9a9ee6fb..ba5f2d1fc 100644 --- a/native/src/main/java/io/ballerina/stdlib/websocket/WebSocketResourceDispatcher.java +++ b/native/src/main/java/io/ballerina/stdlib/websocket/WebSocketResourceDispatcher.java @@ -1127,6 +1127,17 @@ private static void executeResource(WebSocketService wsService, BObject balservi } StrandMetadata strandMetadata = new StrandMetadata(isIsolated(balservice, resource), properties); result = wsService.getRuntime().callMethod(balservice, resource, strandMetadata, bValues); + if (isCloseFrameRecord(result)) { + @SuppressWarnings(WebSocketConstants.UNCHECKED) + BMap closeFrameRecord = (BMap) result; + long status = closeFrameRecord.getIntValue(WebSocketConstants.CLOSE_FRAME_STATUS_CODE); + BString reason = closeFrameRecord.containsKey(WebSocketConstants.CLOSE_FRAME_REASON) ? + closeFrameRecord.getStringValue(WebSocketConstants.CLOSE_FRAME_REASON) + : StringUtils.fromString(""); + result = wsService.getRuntime().callMethod( + connectionInfo.getWebSocketEndpoint(), WebSocketConstants.RESOURCE_NAME_CLOSE, + strandMetadata, status, reason); + } callback.notifySuccess(result); WebSocketObservabilityUtil.observeResourceInvocation(connectionInfo, resource); } catch (BError bError) { @@ -1135,6 +1146,20 @@ private static void executeResource(WebSocketService wsService, BObject balservi }); } + @SuppressWarnings(WebSocketConstants.UNCHECKED) + private static boolean isCloseFrameRecord(Object obj) { + if (obj instanceof BMap) { + BMap bMap = (BMap) obj; + if (bMap.containsKey(WebSocketConstants.CLOSE_FRAME_TYPE) && + bMap.get(WebSocketConstants.CLOSE_FRAME_TYPE) instanceof BObject) { + String objectType = TypeUtils.getType(bMap.get(WebSocketConstants.CLOSE_FRAME_TYPE)).toString(); + return objectType.equals(WebSocketConstants.PREDEFINED_CLOSE_FRAME_TYPE) || + objectType.equals(WebSocketConstants.CUSTOM_CLOSE_FRAME_TYPE); + } + } + return false; + } + private static boolean isIsolated(BObject serviceObj, String remoteMethod) { ObjectType serviceObjType = (ObjectType) TypeUtils.getReferredType(serviceObj.getType()); return serviceObjType.isIsolated() && serviceObjType.isIsolated(remoteMethod); diff --git a/native/src/main/java/io/ballerina/stdlib/websocket/WebSocketUtil.java b/native/src/main/java/io/ballerina/stdlib/websocket/WebSocketUtil.java index de1be64c8..e6d54aa20 100644 --- a/native/src/main/java/io/ballerina/stdlib/websocket/WebSocketUtil.java +++ b/native/src/main/java/io/ballerina/stdlib/websocket/WebSocketUtil.java @@ -263,7 +263,7 @@ public static BError createErrorByType(Throwable throwable) { if (status == WebSocketCloseStatus.MESSAGE_TOO_BIG) { errorCode = WebSocketConstants.ErrorCode.PayloadTooLargeError.errorCode(); } else { - errorCode = WebSocketConstants.ErrorCode.ProtocolError.errorCode(); + errorCode = WebSocketConstants.ErrorCode.CorruptedFrameError.errorCode(); } } else if (throwable instanceof SSLException) { cause = createErrorCause(throwable.getMessage(), WebSocketConstants.ErrorCode.SslError.errorCode(), @@ -281,7 +281,7 @@ public static BError createErrorByType(Throwable throwable) { } else if (throwable instanceof TooLongFrameException) { errorCode = WebSocketConstants.ErrorCode.PayloadTooLargeError.errorCode(); } else if (throwable instanceof CodecException) { - errorCode = WebSocketConstants.ErrorCode.ProtocolError.errorCode(); + errorCode = WebSocketConstants.ErrorCode.CorruptedFrameError.errorCode(); } else if (throwable instanceof WebSocketHandshakeException) { errorCode = WebSocketConstants.ErrorCode.InvalidHandshakeError.errorCode(); } else if (throwable instanceof IOException) {