Error connect to Spring-boot-Rsocket (Auth JWT) from web-client RSocketWebSocketClient - reactjs

The connection to server with spring-boot client works good:
public RSocketAdapter() throws IOException {
requester = createRSocketRequesterBuilder()
.connectWebSocket(URI.create("ws://localhost:7878/"))
.block();
}
private RSocketRequester.Builder createRSocketRequesterBuilder() {
RSocketStrategies strategies = RSocketStrategies.builder()
.encoders(encoders -> encoders.add(new Jackson2CborEncoder()))
.decoders(decoders -> decoders.add(new Jackson2CborDecoder()))
.dataBufferFactory(new NettyDataBufferFactory(PooledByteBufAllocator.DEFAULT))
.build();
return RSocketRequester.builder().rsocketStrategies(strategies);
}
public Mono<HelloToken> signIn(String principal, String credential) {
return requester
.route("signin.v1")
.data(HelloUser.builder().userId(principal).password(credential).build())
.retrieveMono(HelloToken.class)
.doOnNext(token -> {
accessToken = token.getAccessToken();
})
.onErrorStop();
}
And server receives such frame:
Correct byte frame
But the same request from web-client:
authSocketReactiv = () => {
const maxRSocketRequestN = 2147483647;
const keepAlive = 60000;
const lifetime = 180000;
const dataMimeType = 'application/json';
const metadataMimeType = 'message/x.rsocket.authentication.bearer.v0';
var client = new RSocketClient({
serializers: {
data: JsonSerializer,
metadata: JsonSerializer,
},
setup: {
dataMimeType,
keepAlive,
lifetime,
metadataMimeType
},
transport: new RSocketWebSocketClient({
url: 'ws://localhost:7878'
},Encoders)
});
// Open the connection
client.connect().subscribe({
onComplete: socket => {
socket.requestStream({
data:{
'user_id': '0000',
'password': 'Zero4'
},
metadata:'signin.v1'
}).subscribe({
onComplete: () => console.log('complete'),
onError: error => {
console.log(error);
},
onNext: payload => {
console.log('Subscribe1');
},
onSubscribe: subscription => {
console.log('Subscribe');
subscription.request(2147483647);
},
});
},
onError: error => {
console.log(error);
},
onSubscribe: cancel => {
}
});
Forms the incorrect frame and fall with “metadata is malformed ERROR” :
Error byte frame from web
What encoding or buffering options should be used here? Thanks for any tips and suggestions.

You are likely going to want to work with composite metadata and set your metadataMimeType to MESSAGE_RSOCKET_COMPOSITE_METADATA.string.
The important bit is going to be the routing metadata, which is what tells the server how to route the incoming RSocket request.
I haven't dug through the server example code you linked on StackOverflow, but just by looking at your example code, you would supply the routing metadata with your requestStream as so:
Also, the example project you listed though references signin as a request/response so you actually don't want requestStream, but requestResponse.
socket
.requestResponse({
data: Buffer.from(JSON.stringify({
user_id: '0000',
password: 'Zero4'
})),
metadata: encodeCompositeMetadata([
[MESSAGE_RSOCKET_ROUTING, encodeRoute("signin.v1")],
]),
})
You will likely want to use BufferEncoders, as shown in this example. And additionally, I believe you should not use JsonSerializer for the metadata, but instead IdentitySerializer, which will pass the composite metadata buffer straight through, rather than trying to serialize to and from JSON.
You may still run into some issues, but I suspect that this will get you past the metadata is malformed ERROR error.
Hope that helps.

Grate thanks for the detailed advices. According to directions, this complined solution works for my case:
getAuthToken = () => {
const maxRSocketRequestN = 2147483647;
const keepAlive = 60000;
const lifetime = 180000;
const dataMimeType = APPLICATION_JSON.string;
const metadataMimeType = MESSAGE_RSOCKET_COMPOSITE_METADATA.string;
var client = new RSocketClient({
serializers: {
data: IdentitySerializer,
metadata: IdentitySerializer,
},
setup: {
dataMimeType,
keepAlive,
lifetime,
metadataMimeType
},
transport: new RSocketWebSocketClient({
url: 'ws://localhost:7878'
},BufferEncoders)
});
client.connect().then(
(socket) => {
socket.requestResponse({
data: Buffer.from(JSON.stringify({
user_id: '0000',
password: 'Zero4'
})),
metadata: encodeCompositeMetadata([
[MESSAGE_RSOCKET_ROUTING, encodeRoute("signin.v1")],
]),
}).subscribe({
onComplete: (data) => console.log(data),
onError: error =>
console.error(`Request-stream error:${error.message}`),
});
},
(error) => {
console.log("composite initial connection failed");
}
);

Related

RSocket error 0x201 (APPLICATION_ERROR): readerIndex(1) + length(102) exceeds writerIndex(8): UnpooledSlicedByteBu

setInterval(() => {
let that = this;
this.socket && this.socket.requestResponse({
data: '' + (++index),
metadata: 'org.mvnsearch.account.AccountService.findById',
}).subscribe({
onComplete(payload) {
let account = JSON.parse(payload.data);
that.setState({
nick: account.nick
})
},
onError: (e) => {
console.log('onError', e)
}
});
}, 2000)
trying to connect to spring rsocket using reactjs. getting an error before subscribe in the javascript code shown below.
**this.socket.requestResponse({
data: '' + (++index),
metadata: 'org.mvnsearch.account.AccountService.findById',
})**
How to resolve the above issue?
If you are using rsocket routing on the backend, it is length prefixed. See https://github.com/rsocket/rsocket-demo/blob/master/src/main/js/app.js#L22-L36
// Create an instance of a client
const client = new RSocketClient({
setup: {
keepAlive: 60000,
lifetime: 180000,
dataMimeType: 'application/json',
metadataMimeType: 'message/x.rsocket.routing.v0',
},
transport: new RSocketWebSocketClient({url: url}),
});
const stream = Flowable.just({
data: '{"join": {"name": "Web"}}',
metadata: String.fromCharCode('chat/web'.length) + 'chat/web',
});
The routing specification allows multiple routes, so the encoding of a single route is unfortunately complicated by this. https://github.com/rsocket/rsocket/blob/master/Extensions/Routing.md

Get Message From Kafka, send to Rsocket and Receive it from React client

I am trying to send data from kafka using Spring cloud stream to Rsocket and then represent data on React
Here is my configuration.
#Configuration
public class RsocketConsumerConfiguration {
#Bean
public Sinks.Many<Data> sender(){
return Sinks.many().multicast().directBestEffort();
}
}
#Controller
public class ServerController {
#Autowired
private Sinks.Many<Data> integer;
#MessageMapping("integer")
public Flux<Data> integer() {
return integer.asFlux();
}
#EnableBinding(IClientProcessor.class)
public class Listener {
#Autowired
private Sinks.Many<Data> integer;
#StreamListener(IClientProcessor.INTEGER)
public void integer(Data val) {
System.out.println(val);
integer.tryEmitNext(val);
}
}
let client = new RSocketClient({
transport: new RSocketWebSocketClient(
{
url: 'ws://localhost:7000/ws',
wsCreator: (url) => new WebSocket(url),
debug: true,
},
BufferEncoders,
),
setup: {
dataMimeType: "application/json",
metadataMimeType: MESSAGE_RSOCKET_COMPOSITE_METADATA.string,
keepAlive: 5000,
lifetime: 60000,
},
});
client
.then(rsocket => {
console.log("Connected to rsocket");
rsocket.requestStream({
metadata: Buffer.from(encodeCompositeMetadata([
[MESSAGE_RSOCKET_ROUTING, encodeRoute("integer")],
])),
})
.subscribe({
onSubscribe: s => {
s.request(2147483647)
},
onNext: (p) => {
let newData = {
time: new Date(JSON.parse(p.data).time).getUTCSeconds(),
integer: JSON.parse(p.data).integer
}
newData.integer >100?setInteger(currentData => [newData, ...currentData]):setInt(currentData => [newData, ...currentData])
console.log(newData)
},
onError: (e) => console.error(e),
onComplete: () => console.log("Done")
});
spring.cloud.stream.bindings.integer.destination=integer
Not able to see it in react app. Please advise. What I am doing wrong?
Given the data appears to be going directly from Kafka (via Spring) to the client, perhaps it would make more sense to stream Kafka messages via an internet-messaging broker to Internet-facing clients over WebSockets.
Disclosure: I am not the author of that article, but do work at that company where the author works. We see this use case frequently, so expect this approach may be useful.

Nativescript Class constructor Observable cannot be invoked without 'new'

I'm trying to upload a multipart form in nativescript and I'm using http-background. I keep getting the error Class constructor Observable cannot be invoked without 'new'. I've tried changing the compilerOptions target to es5 and es2017, but nothing changed.
Here's all my code from the component.
onSave(){
console.log("clicked")
this.proccessImageUpload(this.file);
}
public onSelectSingleTap() {
this.isSingleMode = true;
let context = imagepicker.create({
mode: "single"
});
this.startSelection(context);
}
private startSelection(context) {
let that = this;
context
.authorize()
.then(() => {
that.imageAssets = [];
that.imageSrc = null;
return context.present();
})
.then((selection) => {
console.log("Selection done: " + JSON.stringify(selection));
this.file = selection[0]._android;
that.imageSrc = that.isSingleMode && selection.length > 0 ? selection[0] : null;
// set the images to be loaded from the assets with optimal sizes (optimize memory usage)
selection.forEach(function (element) {
element.options.width = that.isSingleMode ? that.previewSize : that.thumbSize;
element.options.height = that.isSingleMode ? that.previewSize : that.thumbSize;
});
that.imageAssets = selection;
}).catch(function (e) {
console.log(e);
});
}
// proccess image function
proccessImageUpload(fileUri) {
var backgroundHttp = require("nativescript-background-http");
return new Promise((resolve, reject) => {
// body...
var request = {
url: 'http://192.168.0.2:4000/api/posts',
method: "POST",
headers: {
"Content-Type": "application/octet-stream",
"user_id": "<user_id>"
},
description: 'Uploading profile image..',
androidAutoDeleteAfterUpload: false,
androidNotificationTitle: 'Profile image'
}
var params = [
{ name: "title", value: "test" },
{ name: "content", value: "test" },
{ name: "fileToUpload", filename: fileUri, mimeType: "image/jpeg" }
];
var backgroundSession = backgroundHttp.session('image-upload');
var task = backgroundSession.uploadFile(fileUri, request);
task.on("progress", (e) => {
// console log data
console.log(`uploading... ${e.currentBytes} / ${e.totalBytes}`);
});
task.on("error", (e) => {
// console log data
console.log(`Error processing upload ${e.responseCode} code.`);
reject(`Error uploading image!`);
});
task.on("responded", (e) => {
// console log data
console.log(`received ${e.responseCode} code. Server sent: ${e.data}`);
// var uploaded_response = JSON.parse(e.data);
});
task.on("complete", (e) => {
// console log data
console.log(`upload complete!`);
console.log(`received ${e.responseCode} code`);
// console.log(e.data);
})
resolve(task);
});
}
I know the issue is coming from this line.
var task = backgroundSession.uploadFile(fileUri, request);
Any help would be greatly appreciated!
You use old version if nativescript-background-http plugin
You have to install latest version
tns plugin add #nativescript/background-http
I was able to get this working by installing tns version 6.
I had exactly the same problem. I got this from slack.com, compliments Chris Vietor
"tns plugin add nativescript-background-http" works with nativescript 6.
"tns plugin add #nativescript/background-http" works with nativescript 7.

How do I connect to RabbitMQ from my ReactJS application?

Im having trouble connecting to a RabbitMQ instance and can not find a good tutorial or guide for doing so. I can connect to a RabbitMQ websocket by
var ws = new WebSocket('ws://localhost:15674/ws')
But now I don't know how to connect to my cluster with my credentials. I also need to consume messages from a queue like this /exchange/myExchange/routingKey. I was able to easily do this in angular application by using RxStompService with the following code
rxStompService.configure({
brokerURL: `ws://localhost:15674/ws`,
connectHeaders: {
login: 'guest',
passcode: 'guest'
},
heartbeatIncoming: 0, // Typical value 0 - disabled
heartbeatOutgoing: 20000, // Typical value 20000 - every 20 seconds
reconnectDelay: 200,
debug: (msg: string): void => {
console.log(new Date(), msg);
}
})
this.exchange = 'myExchange'
this.routingKey = 'routingKey'
this.headers ={
'x-queue-name': 'myQueue',
'durable': 'true',
'auto-delete': 'false'
}
ngOnInit() {
this.rxStompService.watch(`/exchange/${this.exchange}/${this.routingKey}`, this.headers ).subscribe((message: Message) => {
this.user = new User(JSON.parse(message.body))
});
}
How can I do the same but from my react app?
I was able to connect and subscribe to a queue by using stompjs.
import Stomp from 'stompjs'
export function connectRabbit(){
let stompClient
var ws = new WebSocket('ws://localhost:15674/ws')
const headers = {
'login': 'guest',
'passcode': 'guest',
'durable': 'true',
'auto-delete': 'false'
}
stompClient = Stomp.over(ws)
stompClient.connect(headers , function(frame){
console.log('Connected')
const subscription = stompClient.subscribe('/queue/myQueue', function(message){
console.log(message)
})
})
}

How to handle response from Express response.write() in Angular $http

I'm trying to upload a csv file using ng-file-upoad. Here is my code snippet:
Upload.upload({
url: baseUrl + '/file-upload',
data: {
file: file
}
})
.then(function(res) {
console.log('success: ===> ', res);
}, function(err) {
console.log('erroir: ===> ', err);
}, function() {
console.log('progress: ', arguments);
});
And in node environment I'm parsing the file and inserting the data in database. I don't want to close the connection. That's why I used "response.write". Here is my code snippet:
var path = req.files.file.path,
currentIndex = 0;
fs.readFile(path, 'utf8', function(err, data) {
if(err) {
// handle error
} else {
// making array (dataArray) from data
dataArray.forEach(function(eachData){
newEntry = new app.db.models.SomeCollection(eachData);
newEntry.save(function(err, data) {
if (currentIndex === dataArray.length) {
res.end('DONE!');
} else {
currentIndex++;
res.write(JSON.stringify({
total: dataArray.length,
done: currentIndex
}));
}
});
})
}
});
My question is how I will get the data I'm passing in "res.write"? I don't want to use socket for only this purpose. Am I missing something?
As already explained here:
response.send(msg) is equal to response.write(msg);response.end();
Which means, send can only be called once, write can be called many times, but you must call end yourself.
You are probably not receiving the response because response.end() is missing.
Once you end() your response you should be able to access the response data in your angular controller in the Upload.upload promise that is returned.
It's not like close a connection as you said. This is not a socket-ish like implementation (such as ws or socket.io). Once a request is made it should have a response even if it is to provide error details about that request (i.e. status 401, 403, 404, etc).
in your angular component:
...
constructor(private incrementalService: IncrementalService) {}
incrementalTest() { //activate with a button or whatnot
this.incrementalService.increment().subscribe( (result:any) => {
if (result.partialText) {
console.log(partialText); //do whatever you need to do with your partial results here!
}
})
}
your angular service:
import { HttpClient, HttpHeaders } from '#angular/common/http';
public class IncrementalService {
constructor(private http: HttpClient) {}
increment(): Observable<ArrayBuffer> {
const options = {
reportProgress: true,
responseType: 'text',
observe: 'events'
}
return this.http.request('get', 'http://someURL', { ...this.addRawHeaderOptions(), ...options});
}
private addRawHeaderOptions() {
const authHeaders = new HttpHeaders({
'Content-Type': 'application/json',
//authorization, Cache-Control: 'no-cache, Pragma:'no-cache', et al. }
return { headers: authHeaders }
}
}
Finally, your back-end service (this is express, but should work similarly for raw node):
async function(request, response) {
const increments = [ 1,2,3,4 ];
response.set('Content-Type', 'text/html');
for (const value of increments) { //contains async call - not switch-outable for a forEach.
response.write(`increment - ${value} `);
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
await delay(1000)
}
response.status(200).end()
}
browser console output when run:
increment - 1
increment - 1 increment - 2
increment - 1 increment - 2 increment - 3
increment - 1 increment - 2 increment - 3 increment - 4
!!Sorry for any typos - i had to transcribe this from a locked-down machine.

Resources