RSocket channel error : "reactor.core.publisher.Operators.error - Operator called default onErrorDropped" with merged flux - spring

I want to create a rsocket channel where the data sent from the server can be either a reaction to a client request or a push. I use a flux merge for that.
It's referential data : the refresh can be asked by the client and the server can also push updates.
So I have this on the server side :
#MessageMapping("update-stream")
Flux<DomainObject> addUpdatesListener(Flux<RefreshRequest> requests) {
Flux<DomainObject> pushFlux = Flux.from(this.flux)
.doOnError((e) -> log.error("Error on push flux : {}", e, e));
return requests
.map(this::getUpdates)
.flatMap(Flux::fromIterable)
.doOnError((e) -> log.error("Error on channel flux : {}", e, e))
.mergeWith(pushFlux)
.doOnError((e) -> log.error("Error on merged flux : {}", e, e));
}
It works excepts that when I stop the client I have the following error :
06-07-2020 15:58:53.168 [reactor-http-nio-3] ERROR reactor.core.publisher.Operators.error - Operator called default onErrorDropped
java.util.concurrent.CancellationException: Disposed
at reactor.core.publisher.FluxProcessor.dispose(FluxProcessor.java:80)
at io.rsocket.core.RSocketResponder$3.hookOnCancel(RSocketResponder.java:513)
at reactor.core.publisher.BaseSubscriber.cancel(BaseSubscriber.java:230)
at java.base/java.lang.Iterable.forEach(Iterable.java:75)
at io.rsocket.core.RSocketResponder.cleanUpSendingSubscriptions(RSocketResponder.java:275)
at io.rsocket.core.RSocketResponder.cleanup(RSocketResponder.java:265)
at io.rsocket.core.RSocketResponder.tryTerminate(RSocketResponder.java:167)
at io.rsocket.core.RSocketResponder.tryTerminateOnConnectionClose(RSocketResponder.java:160)
at reactor.core.publisher.LambdaMonoSubscriber.onComplete(LambdaMonoSubscriber.java:132)
at reactor.core.publisher.MonoProcessor$NextInner.onComplete(MonoProcessor.java:518)
at reactor.core.publisher.MonoProcessor.onNext(MonoProcessor.java:308)
at reactor.core.publisher.MonoProcessor.onComplete(MonoProcessor.java:265)
at io.rsocket.internal.BaseDuplexConnection.dispose(BaseDuplexConnection.java:23)
at io.rsocket.transport.netty.TcpDuplexConnection.lambda$new$0(TcpDuplexConnection.java:60)
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:577)
at io.netty.util.concurrent.DefaultPromise.notifyListeners0(DefaultPromise.java:570)
at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:549)
at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:490)
at io.netty.util.concurrent.DefaultPromise.setValue0(DefaultPromise.java:615)
at io.netty.util.concurrent.DefaultPromise.setSuccess0(DefaultPromise.java:604)
at io.netty.util.concurrent.DefaultPromise.trySuccess(DefaultPromise.java:104)
at io.netty.channel.DefaultChannelPromise.trySuccess(DefaultChannelPromise.java:84)
at io.netty.channel.AbstractChannel$CloseFuture.setClosed(AbstractChannel.java:1158)
at io.netty.channel.AbstractChannel$AbstractUnsafe.doClose0(AbstractChannel.java:760)
at io.netty.channel.AbstractChannel$AbstractUnsafe.close(AbstractChannel.java:736)
at io.netty.channel.AbstractChannel$AbstractUnsafe.close(AbstractChannel.java:607)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.closeOnRead(AbstractNioByteChannel.java:105)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:171)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:834)
If I don't do the merge, I have no error.
I tried many different versions but I cant find a way to have both the push an no error logged on client quit.
What am I missing ?
Thank a lot.

The problem disapears when upgrading from spring-boot 2.3.0.RELEASE to 2.3.1.RELEASE.

Related

STOMP over WebSockets: Spring Boot expects JSON; NodeJs STOMP.js client fails to connect

When trying out STOMP over WebSockets, I noticed inconsistencies between different implementations, namely between a Spring Boot Java implementation and a NodeJs client written with STOMP.js.
When debugging into it, the difference is that in the Spring Boot app, the CONNECT message is expected to be a JSON array. For instance, this message is sent by their test client (written in JavaScript using the SocksJS library):
["CONNECT\naccept-version:1.1,1.0\nheart-beat:10000,10000\n\n\u0000"]
In contrast, my NodeJs STOMP.js test client (code is below) sends the following frame:
CONNECT
accept-version:1.0,1.1,1.2
heart-beat:4000,4000
^#
Unfortunately, I am not experienced with STOMP, but after reading through the specification, I did not understand why Spring Boot expects the data to be represented as a JSON array. Is this a known problem?
To demonstrate, let me share two example runs. One successful run to connect to RabbitMQ, followed by a failed attempt to connect against the Java Spring Boot app. (A reproducible setup with the code can be found at the end.)
Connect to RabbitMQ instance, which is configure to use STOMP over WebSockets (running on ws://localhost:15674/ws):
$ node client.js
Opening Web Socket...
Web Socket Opened...
>>> CONNECT
accept-version:1.0,1.1,1.2
heart-beat:4000,4000
Received data
<<< CONNECTED
server:RabbitMQ/3.8.8
session:session-WkKD6rN5BNc_ObKpziikYA
heart-beat:4000,4000
version:1.2
connected to server RabbitMQ/3.8.8
send PING every 4000ms
check PONG every 4000ms
onConnect called
<<< PONG
Received data
<<<
<<< PONG
>>> PING
Received data
<<<
Now connect (unsuccessfully) against the Spring Boot app (ws://localhost:5555/chat/123/k2qn3dl7/websocket):
node client.js
Opening Web Socket...
Web Socket Opened...
>>> CONNECT
accept-version:1.0,1.1,1.2
heart-beat:4000,4000
Received data
<<< o
Received data
<<< c[1007,""]
Connection closed to ws://localhost:5555/chat/123/k2qn3dl7/websocket
STOMP: scheduling reconnection in 5000ms
Opening Web Socket...
Web Socket Opened...
>>> CONNECT
accept-version:1.0,1.1,1.2
heart-beat:4000,4000
Received data
<<< o
^C
The reason why it fails is that Jackson (the JSON parser) failed to parse that payload:
CONNECT
accept-version:1.0,1.1,1.2
heart-beat:4000,4000
^#
As said, in the client that comes with the Spring Boot example, the payload looked like that:
["CONNECT\naccept-version:1.1,1.0\nheart-beat:10000,10000\n\n\u0000"]
Here is the full error in the Spring Boot app:
2021-07-22 13:58:59.546 INFO 74313 --- [nio-5555-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
2021-07-22 13:58:59.594 ERROR 74313 --- [nio-5555-exec-1] s.w.s.s.t.s.WebSocketServerSockJsSession : Broken data received. Terminating WebSocket connection abruptly
com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'CONNECT': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
at [Source: (String)"CONNECT
accept-version:1.0,1.1,1.2
heart-beat:4000,4000
"; line: 1, column: 8]
at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2337) ~[jackson-core-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:720) ~[jackson-core-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._reportInvalidToken(ReaderBasedJsonParser.java:2903) ~[jackson-core-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._handleOddValue(ReaderBasedJsonParser.java:1949) ~[jackson-core-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(ReaderBasedJsonParser.java:781) ~[jackson-core-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4684) ~[jackson-databind-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4586) ~[jackson-databind-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3548) ~[jackson-databind-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3516) ~[jackson-databind-2.12.3.jar:2.12.3]
at org.springframework.web.socket.sockjs.frame.Jackson2SockJsMessageCodec.decode(Jackson2SockJsMessageCodec.java:64) ~[spring-websocket-5.3.8.jar:5.3.8]
at org.springframework.web.socket.sockjs.transport.session.WebSocketServerSockJsSession.handleMessage(WebSocketServerSockJsSession.java:187) ~[spring-websocket-5.3.8.jar:5.3.8]
at org.springframework.web.socket.sockjs.transport.handler.SockJsWebSocketHandler.handleTextMessage(SockJsWebSocketHandler.java:93) ~[spring-websocket-5.3.8.jar:5.3.8]
at org.springframework.web.socket.handler.AbstractWebSocketHandler.handleMessage(AbstractWebSocketHandler.java:43) ~[spring-websocket-5.3.8.jar:5.3.8]
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.handleTextMessage(StandardWebSocketHandlerAdapter.java:114) ~[spring-websocket-5.3.8.jar:5.3.8]
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.access$000(StandardWebSocketHandlerAdapter.java:43) ~[spring-websocket-5.3.8.jar:5.3.8]
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:85) ~[spring-websocket-5.3.8.jar:5.3.8]
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:82) ~[spring-websocket-5.3.8.jar:5.3.8]
at org.apache.tomcat.websocket.WsFrameBase.sendMessageText(WsFrameBase.java:415) ~[tomcat-embed-websocket-9.0.46.jar:9.0.46]
at org.apache.tomcat.websocket.server.WsFrameServer.sendMessageText(WsFrameServer.java:129) ~[tomcat-embed-websocket-9.0.46.jar:9.0.46]
at org.apache.tomcat.websocket.WsFrameBase.processDataText(WsFrameBase.java:515) ~[tomcat-embed-websocket-9.0.46.jar:9.0.46]
at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:301) ~[tomcat-embed-websocket-9.0.46.jar:9.0.46]
at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:133) ~[tomcat-embed-websocket-9.0.46.jar:9.0.46]
at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:85) ~[tomcat-embed-websocket-9.0.46.jar:9.0.46]
at org.apache.tomcat.websocket.server.WsFrameServer.doOnDataAvailable(WsFrameServer.java:183) ~[tomcat-embed-websocket-9.0.46.jar:9.0.46]
at org.apache.tomcat.websocket.server.WsFrameServer.notifyDataAvailable(WsFrameServer.java:162) ~[tomcat-embed-websocket-9.0.46.jar:9.0.46]
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.upgradeDispatch(WsHttpUpgradeHandler.java:156) ~[tomcat-embed-websocket-9.0.46.jar:9.0.46]
at org.apache.coyote.http11.upgrade.UpgradeProcessorInternal.dispatch(UpgradeProcessorInternal.java:60) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:59) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1707) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
at java.base/java.lang.Thread.run(Thread.java:829) ~[na:na]
2021-07-22 13:59:04.610 ERROR 74313 --- [nio-5555-exec-2] s.w.s.s.t.s.WebSocketServerSockJsSession : Broken data received. Terminating WebSocket connection abruptly
Path to reproduce:
NodeJs client code
Spring Boot test app
RabbitMQ test instance
Client code written in NodeJs:
// Required dependencies:
// "#stomp/stompjs": "6.1.0"
// "websocket": "1.0.34"
// Polyfills. For details see:
// https://stomp-js.github.io/guide/stompjs/rx-stomp/ng2-stompjs/pollyfils-for-stompjs-v5.html
Object.assign(global, { WebSocket: require('websocket').w3cwebsocket });
const StompJs = require('#stomp/stompjs');
const client = new StompJs.Client({
//brokerURL: 'ws://localhost:15674/ws', // RabbitMQ (should work)
brokerURL: 'ws://localhost:5555/chat/123/k2qn3dl7/websocket', // Spring app (should fail)
reconnectDelay: 5000,
heartbeatIncoming: 4000,
heartbeatOutgoing: 4000,
logRawCommunication: true,
debug: (x) => console.log(x),
});
client.onConnect = function (frame) {
console.log('onConnect called');
};
client.activate();
The Spring Boot app can be found here. I started it on port 5555:
git clone git#github.com:eugenp/tutorials.git
cd tutorials/spring-websockets
SERVER_PORT=5555 mvn spring-boot:run
Note: if you then go to http://localhost:5555, you will see a chat application served by the Spring Boot app. When you click connect, a STOMP connection will be established.
To start RabbitMQ, you can use the Docker container used for the tests in STOMP.js:
git clone git#github.com:stomp-js/stompjs.git
cd stompjs
sudo docker build -t myrabbitmq rabbitmq/
sudo docker run --rm -p 15674:15674 myrabbitmq
In short: The JSON messages were not "STOMP over native WebSockets" but "STOMP over SocksJS". The additional JSON layer was introduced by the SocksJS protocol, which is used in the Spring Boot example application.
Here is the longer story. It turned out, that my endpoint was wrong. Instead of
'ws://localhost:5555/chat/123/k2qn3dl7/websocket'
it should have been
'ws://localhost:5555/chat'
It had the wrong URI because I was copying the output that I saw in the browser. Instead I should have looked at the configuration:
#Override
public void registerStompEndpoints(final StompEndpointRegistry registry) {
registry.addEndpoint("/chat");
registry.addEndpoint("/chat").withSockJS();
registry.addEndpoint("/chatwithbots");
registry.addEndpoint("/chatwithbots").withSockJS();
}
Now the confusing part. As can be seen from the configuration, the Spring Boot application defines fallbacks with SocksJS.
If you remove the fallback, the confusing error message goes away. Yet when the fallback is active, Spring will try to process the request as SocksJS. That is why it tries to parse the STOMP frame as JSON, which results in the misleading error message.
In addition, I got confused by the JavaScript client used in the Spring Boot example:
function connect() {
var socket = new SockJS('/chat');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/messages', function(messageOutput) {
showMessageOutput(JSON.parse(messageOutput.body));
});
});
}
It is not connected over native WebSocket but over SocksJS. That explains why Firefox shows JSON requests, not the expected STOMP frames.

Corda 4.3 client rpc throw error when we use LinearStateQueryCriteria to query by linear id

I am trying to invoke a get by linear id for a state from a springboot application using corda rpc client. cordapps are build using corda 4.3.
Below is the query i use:
QueryCriteria queryCriteria =
new QueryCriteria.LinearStateQueryCriteria(null, idList, status, contractStateTypes);
List<StateAndRef<DemoState>> stateAndRefs = cordaRPCOps.vaultQueryByCriteria(queryCriteria, stateType).getStates();
The query fails to retrive data and throws an exception as shown below, but if i use Corda 4.1 rpc client the same query returns a correct result.
Below is the exception shown in the corda node logs when using corda rpc 4.3:
[WARN ] 2020-04-28T15:35:12,118Z [Thread-964 (ActiveMQ-client-global-threads)] rpc.RPCServer.clientArtemisMessageHandler -
Inbound RPC failed [errorCode=1ctda0y, moreInformationAt=https://errors.corda.net/ENT/4.1/1ctda0y] {actor_id=user1, actor
_owning_identity=O=SBI, L=Panjim, C=IN, actor_store_id=NODE_CONFIG, invocation_id=81afab50-486a-406d-9e17-bbbcfdf5aaed, in
vocation_timestamp=2020-04-28T15:35:12.117Z, origin=user1, session_id=472a8267-7035-4e58-be6f-7b762c82bf2b, session_timest
amp=2020-04-28T15:32:59.497Z}
java.io.NotSerializableException: Internal deserialization failure: java.lang.ArrayIndexOutOfBoundsException: java.util.Li
st<*> -> net.corda.core.node.services.vault.QueryCriteria$LinearStateQueryCriteria -> null
at net.corda.serialization.internal.amqp.DeserializationInput.des(DeserializationInput.kt:106) ~[corda-serializati
on-4.1.jar:?]
at net.corda.serialization.internal.amqp.DeserializationInput.deserialize(DeserializationInput.kt:119) ~[corda-ser
ialization-4.1.jar:?]
at net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme.deserialize(AMQPSerializationScheme.kt:225) ~[corda-serialization-4.1.jar:?]
at net.corda.serialization.internal.SerializationFactoryImpl$deserialize$1$1.invoke(SerializationScheme.kt:105) ~[corda-serialization-4.1.jar:?]
at net.corda.core.serialization.SerializationFactory.withCurrentContext(SerializationAPI.kt:71) ~[corda-core-4.1.jar:?]
at net.corda.serialization.internal.SerializationFactoryImpl$deserialize$1.invoke(SerializationScheme.kt:105) ~[corda-serialization-4.1.jar:?]
at net.corda.serialization.internal.SerializationFactoryImpl$deserialize$1.invoke(SerializationScheme.kt:73) ~[corda-serialization-4.1.jar:?]
at net.corda.core.serialization.SerializationFactory.asCurrent(SerializationAPI.kt:85) ~[corda-core-4.1.jar:?]
at net.corda.serialization.internal.SerializationFactoryImpl.deserialize(SerializationScheme.kt:105) ~[corda-serialization-4.1.jar:?]
at net.corda.node.services.rpc.RPCServer.clientArtemisMessageHandler(RPCServer.kt:584) ~[corda-node-4.1.jar:?]
at net.corda.node.services.rpc.RPCServer.access$clientArtemisMessageHandler(RPCServer.kt:77) ~[corda-node-4.1.jar:?]
at net.corda.node.services.rpc.RPCServer$createRpcConsumer$1.invoke(RPCServer.kt:295) ~[corda-node-4.1.jar:?]
at net.corda.node.services.rpc.RPCServer$createRpcConsumer$1.invoke(RPCServer.kt:77) ~[corda-node-4.1.jar:?]
at net.corda.node.services.rpc.RPCServerKt$sam$org_apache_activemq_artemis_api_core_client_MessageHandler$0.onMessage(RPCServer.kt) ~[corda-node-4.1.jar:?]
at org.apache.activemq.artemis.core.client.impl.ClientConsumerImpl.callOnMessage(ClientConsumerImpl.java:1002) ~[artemis-core-client-2.6.2.jar:2.6.2]
at org.apache.activemq.artemis.core.client.impl.ClientConsumerImpl.access$400(ClientConsumerImpl.java:50) ~[artemis-core-client-2.6.2.jar:2.6.2]
at org.apache.activemq.artemis.core.client.impl.ClientConsumerImpl$Runner.run(ClientConsumerImpl.java:1125) ~[artemis-core-client-2.6.2.jar:2.6.2]
at org.apache.activemq.artemis.utils.actors.OrderedExecutor.doTask(OrderedExecutor.java:42) ~[artemis-commons-2.6.2.jar:2.6.2]
at org.apache.activemq.artemis.utils.actors.OrderedExecutor.doTask(OrderedExecutor.java:31) ~[artemis-commons-2.6.2.jar:2.6.2]
at org.apache.activemq.artemis.utils.actors.ProcessorBase.executePendingTasks(ProcessorBase.java:66) ~[artemis-commons-2.6.2.jar:2.6.2]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[?:1.8.0_172]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[?:1.8.0_172]
at org.apache.activemq.artemis.utils.ActiveMQThreadFactory$1.run(ActiveMQThreadFactory.java:118) ~[artemis-commons-2.6.2.jar:2.6.2]
Caused by: java.lang.ArrayIndexOutOfBoundsException: java.util.List<*> -> net.corda.core.node.services.vault.QueryCriteria$LinearStateQueryCriteria -> null
Any pointers as to how we could get this running using corda rpc 4.3?
It seems you are using CordaRPC 4.3 while your Corda node is running v4.1. In such cases, you need to provide the minimumServerProtocolVersion to CordaRPCClientConfiguration.
Here's a sample code:
CordaRPCClientConfiguration config =
new CordaRPCClientConfiguration(Duration.ofMinutes(3), 4);
CordaRPCOps rpcProxy = new CordaRPCClient(NetworkHostAndPort.parse("<host>:<port>"), config)
.start(<username>, <password>).getProxy();
Note the part CordaRPCClientConfiguration(Duration.ofMinutes(3), 4), "4" here is the minimumServerProtocolVersion supported which Corresponds to Corda 4.0.

Error while connecting to Spring Boot RSocket server from RSocket-Java Client

I am having issue while connecting to Spring Boot RSocket application over TCP. The client when using RSocketRequester works fine but when I try to connect using RSocketFactory client it keep getting errors. Code below.
RSocket rSocket = this.client = RSocketFactory
.connect()
.mimeType(WellKnownMimeType.MESSAGE_RSOCKET_ROUTING.toString(), MediaType.APPLICATION_JSON_VALUE)
.frameDecoder(PayloadDecoder.ZERO_COPY)
.transport(TcpClientTransport.create("localhost", 7000))
.start()
.block();
Flux<Payload> s = rSocket.requestStream(DefaultPayload.create("1234", "socket"));
s.subscribe();
This gives error as below:
java.lang.IndexOutOfBoundsException: readerIndex(1) + length(115) exceeds writerIndex(6): AbstractPooledDerivedByteBuf$PooledNonRetainedSlicedByteBuf(ridx: 1, widx: 6, cap: 6/6, unwrapped: PooledUnsafeDirectByteBuf(ridx: 27, widx: 27, cap: 1024))
at io.netty.buffer.AbstractByteBuf.checkReadableBytes0(AbstractByteBuf.java:1477)
at io.netty.buffer.AbstractByteBuf.checkReadableBytes(AbstractByteBuf.java:1463)
at io.netty.buffer.AbstractByteBuf.readSlice(AbstractByteBuf.java:880)
at io.rsocket.metadata.TaggingMetadata$1.next(TaggingMetadata.java:47)
at io.rsocket.metadata.TaggingMetadata$1.next(TaggingMetadata.java:37)
at org.springframework.messaging.rsocket.DefaultMetadataExtractor.extractEntry(DefaultMetadataExtractor.java:136)
at org.springframework.messaging.rsocket.DefaultMetadataExtractor.extract(DefaultMetadataExtractor.java:119)
at org.springframework.messaging.rsocket.annotation.support.MessagingRSocket.createHeaders(MessagingRSocket.java:195)
at org.springframework.messaging.rsocket.annotation.support.MessagingRSocket.handleAndReply(MessagingRSocket.java:167)
at org.springframework.messaging.rsocket.annotation.support.MessagingRSocket.requestStream(MessagingRSocket.java:127)
at io.rsocket.RSocketResponder.requestStream(RSocketResponder.java:207)
at io.rsocket.RSocketResponder.handleFrame(RSocketResponder.java:310)
at reactor.core.publisher.LambdaSubscriber.onNext(LambdaSubscriber.java:160)
at reactor.core.publisher.MonoFlatMapMany$FlatMapManyInner.onNext(MonoFlatMapMany.java:242)
at reactor.core.publisher.FluxGroupBy$UnicastGroupedFlux.drainRegular(FluxGroupBy.java:554)
at reactor.core.publisher.FluxGroupBy$UnicastGroupedFlux.drain(FluxGroupBy.java:630)
at reactor.core.publisher.FluxGroupBy$UnicastGroupedFlux.subscribe(FluxGroupBy.java:696)
at reactor.core.publisher.Flux.subscribe(Flux.java:8174)
at reactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onNext(MonoFlatMapMany.java:188)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1637)
at reactor.core.publisher.MonoProcessor.onNext(MonoProcessor.java:317)
at io.rsocket.internal.ClientServerInputMultiplexer.lambda$new$1(ClientServerInputMultiplexer.java:116)
at reactor.core.publisher.LambdaSubscriber.onNext(LambdaSubscriber.java:160)
at reactor.core.publisher.FluxGroupBy$GroupByMain.drainLoop(FluxGroupBy.java:380)
at reactor.core.publisher.FluxGroupBy$GroupByMain.drain(FluxGroupBy.java:316)
at reactor.core.publisher.FluxGroupBy$GroupByMain.onNext(FluxGroupBy.java:201)
at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:114)
at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:114)
at reactor.netty.channel.FluxReceive.drainReceiver(FluxReceive.java:218)
at reactor.netty.channel.FluxReceive.onInboundNext(FluxReceive.java:351)
at reactor.netty.channel.ChannelOperations.onInboundNext(ChannelOperations.java:348)
at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:90)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355)
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:321)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:295)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:830)
This particular error is as I understand is because of netty message wrapping (from other threads on stackoverflow) but how to solve it?
The server is Spring Boot 5+ RSocket but the client is only using RSocket-Java.
The problem is inside mime types.
In your case server awaits CBOR but you proceed application/json
The code solution: change the way of RSocketRequester initialization as at the example below and you client with be sending CBOR you can see at by enabling debug: logging.level.io.rsocket.FrameLogger: DEBUG. That's all for hello world, no need custom strategies or factories implementations on the client-side
#Bean
RSocketRequester rSocketRequester(RSocketStrategies strategies) {
return RSocketRequester
.builder()
.rsocketStrategies(strategies)
.connectTcp("127.0.0.1", 7000)
.retry(5)
.block();
}
Btw, I didn't reach the solution with JSON on both side even with custom Encoder & Decoder on both side. I guess the reason here is that there is no CBOR to Jackson converter and only vice versa: org.springframework.http.codec.cbor.Jackson2CborEncoder
From this, use the following to generate metadata.
CompositeByteBuf metadata = ByteBufAllocator.DEFAULT.compositeBuffer();
RoutingMetadata routingMetadata = TaggingMetadataCodec.createRoutingMetadata(ByteBufAllocator.DEFAULT, List.of("/route"));
CompositeMetadataCodec.encodeAndAddMetadata(metadata,
ByteBufAllocator.DEFAULT,
WellKnownMimeType.MESSAGE_RSOCKET_ROUTING,
routingMetadata.getContent());

Jetty websocket client issue

Use Eclipse IDE in an attempt to create a java websocket client for an OpenHab project, thus using default Jetty websocket library. The OpenHab project intend to connect to a websocket endpoint requiring token authorization, send a request message and retrieve live/continuous measurements.
So, I have
public static final String SUBSCRIPTION_URL = "wss://api.tibber.com/v1-beta/gql/subscriptions";
Further, my WS open code:
public void open() throws Exception {
if (isConnected()) {
logger.warn("Open: connection is already open");
}
logger.warn("Connecting to: {}", SUBSCRIPTION_URL);
sslContextFactory = new SslContextFactory(true);
sslContextFactory.setTrustAll(true);
client = new WebSocketClient(sslContextFactory);
client.setMaxIdleTimeout(360 * 1000);
TibberWebSocketListener socket = new TibberWebSocketListener();
request = new ClientUpgradeRequest();
String token = new StringBuilder("Bearer ").append(configuration.getToken()).toString();
request.setHeader("Authorization", token);
request.setSubProtocols("graphql-subscriptions");
client.start();
client.connect(socket, new URI(SUBSCRIPTION_URL), request);
}
However, with this code it seems like I get connected, but end up with an IOException: Broken pipe after 1 minute. I get the same error both if I just connect without message / connect with send message.
13:59:15.987 [safeCall-1] WARN o.o.b.t.i.handler.TibberHandler:346 - Connecting to: wss://api.tibber.com/v1-beta/gql/subscriptions
13:59:16.390 [#1379116703-141] WARN o.o.b.t.i.handler.TibberHandler:385 - Connected to Server
14:00:16.430 [#1379116703-142] WARN o.o.b.t.i.handler.TibberHandler:392 - Closing a WebSocket due to Disconnected
14:00:16.434 [#1379116703-139] WARN o.o.b.t.i.handler.TibberHandler:399 - Error during websocket communication: Broken pipe
java.io.IOException: Broken pipe
at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.flush(SslConnection.java:928)
at org.eclipse.jetty.io.WriteFlusher.flush(WriteFlusher.java:422)
at org.eclipse.jetty.io.WriteFlusher.write(WriteFlusher.java:277)
at org.eclipse.jetty.io.AbstractEndPoint.write(AbstractEndPoint.java:381)
at org.eclipse.jetty.websocket.common.io.FrameFlusher.flush(FrameFlusher.java:264)
at org.eclipse.jetty.websocket.common.io.FrameFlusher.process(FrameFlusher.java:193)
at org.eclipse.jetty.util.IteratingCallback.processing(IteratingCallback.java:241)
at org.eclipse.jetty.util.IteratingCallback.iterate(IteratingCallback.java:223)
at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.outgoingFrame(AbstractWebSocketConnection.java:516)
at org.eclipse.jetty.websocket.client.io.WebSocketClientConnection.outgoingFrame(WebSocketClientConnection.java:72)
at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.close(AbstractWebSocketConnection.java:184)
at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.onFillable(AbstractWebSocketConnection.java:458)
at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.onFillable(AbstractWebSocketConnection.java:428)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.onFillable(SslConnection.java:426)
at org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:320)
at org.eclipse.jetty.io.ssl.SslConnection$2.succeeded(SslConnection.java:158)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:117)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:336)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:313)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:171)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:129)
at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:367)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:782)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:918)
at java.lang.Thread.run(Thread.java:748)
14:00:16.435 [#1379116703-139] WARN o.o.b.t.i.handler.TibberHandler:392 - Closing a WebSocket due to Broken pipe
java.io.IOException: Broken pipe
That means the OS or network below Java (and Jetty) detected that the connection was closed.
"Broken Pipe" is actually fairly common, especially with mobile devices or wireless networking.
Not much Java or Jetty can do about it either, it happened outside of its control.

Spring websocket EOFException

My project is using spring-boot web socket and embedded tomcat to implement chat server. Everything is ok but sometimes I got EOFException, and then the client cannot send a message to chat server until I restart tomcat then everything worked ok. I don't know when EOFException will happen. Pls help me
[TRACE] 2017-10-23 06:17:10.707 [http-nio-7755-exec-4]
NativeWebSocketSession - Sending TextMessage payload=[{"result":..],
byteCount=164, last=true], StandardWebSocketSession[id=42b, uri=/chat]
[DEBUG] 2017-10-23 06:17:29.670 [http-nio-7755-exec-8]
LoggingWebSocketHandlerDecorator - Transport error in
StandardWebSocketSession[id=42b, uri=/chat] java.io.EOFException: null
at
org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.fillReadBuffer(NioEndpoint.java:1242)
~[tomcat-embed-core-8.5.16.jar!/:8.5.16] at
org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.read(NioEndpoint.java:1182)
~[tomcat-embed-core-8.5.16.jar!/:8.5.16] at
org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:72)
~[tomcat-embed-websocket-8.5.16.jar!/:8.5.16] at
org.apache.tomcat.websocket.server.WsFrameServer.doOnDataAvailable(WsFrameServer.java:171)
~[tomcat-embed-websocket-8.5.16.jar!/:8.5.16] at
org.apache.tomcat.websocket.server.WsFrameServer.notifyDataAvailable(WsFrameServer.java:151)
~[tomcat-embed-websocket-8.5.16.jar!/:8.5.16] at
org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.upgradeDispatch(WsHttpUpgradeHandler.java:148)
[tomcat-embed-websocket-8.5.16.jar!/:8.5.16] at
org.apache.coyote.http11.upgrade.UpgradeProcessorInternal.dispatch(UpgradeProcessorInternal.java:54)
[tomcat-embed-core-8.5.16.jar!/:8.5.16] at
org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:53)
[tomcat-embed-core-8.5.16.jar!/:8.5.16] at
org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
[tomcat-embed-core-8.5.16.jar!/:8.5.16] at
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455)
[tomcat-embed-core-8.5.16.jar!/:8.5.16] at
org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
[tomcat-embed-core-8.5.16.jar!/:8.5.16] at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
[?:1.8.0_131] at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
[?:1.8.0_131] at
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
[tomcat-embed-core-8.5.16.jar!/:8.5.16] at
java.lang.Thread.run(Thread.java:748) [?:1.8.0_131] [DEBUG] 2017-10-23
06:17:29.671 [http-nio-7755-exec-8] LoggingWebSocketHandlerDecorator -
StandardWebSocketSession[id=42b, uri=/chat] closed with
CloseStatus[code=1006, reason=null]
Yup, i solved it. This exception will happen after client or server was interrupted or stopped without calling close socket method(maybe lost internet, or shutdown laptop or mobile when using socket). So if we would like to solve this problem, we have to implement ping/pong mechanism, after interval if we cannot get pong response from client, we will close this socket. Another way, we can catch this exception, then we will close old socket.
Thanks,
Andy
Well, I couldn't follow that. So here's my solution.
I was getting mysterious EOFExceptions when user sessions ended, and my solution was to beef up error handling (use a more detailed onError function) in the websocket onError method, like this.
I credit my rooting and rutting around javacodegeeks for eventually pointed me in the right direction.
#OnError
public void onError(Session sess, Throwable e) {
Throwable cause = e.getCause();
/* normal handling... */
if (cause != null)
System.out.println("Error-info: cause->" + cause);
try {
// Likely EOF (i.e. user killed session)
// so just Close the input stream as instructed
sess.close();
} catch (IOException ex) {
System.out.println("Handling eof, A cascading IOException was caught: " + ex.getMessage());
ex.printStackTrace();
} finally {
System.out.println("Session error handled. (likely unexpected EOF) resulting in closing User Session.");
}
}

Resources