rsocket-rxjs
Version:
RSocket Protocol Client Implementation
339 lines (338 loc) • 14.4 kB
JavaScript
import { BehaviorSubject, range, timer } from "rxjs";
import { concatMap, flatMap, reduce } from 'rxjs/operators';
import { Payload } from "../../core/protocol/payload";
import { RSocketClient } from "../../core/rsocket-client.impl";
import { WebsocketTransport } from "../../core/transport/websocket-transport.impl";
import { EncodingRSocket } from "../encoding-rsocket-client";
import { WellKnownMimeTypes } from "../well-known-mime-types";
import { MessageRoutingRSocket } from "./message-routing-rsocket";
const WebSocket = require('ws');
global.WebSocket = WebSocket;
describe("request_patterns", () => {
let socket;
beforeAll(done => {
const transport = new WebsocketTransport("ws://localhost:8080/rsocket");
const client = new RSocketClient(transport, undefined, {
setupPayload: new Payload(new Uint8Array(0)),
// setupPayload: new Payload(
// encodeJson(stringToUtf8ArrayBuffer('Test-Client'), encodingClient),
// encodeCompositionMetadata([
// {
// type: WellKnownMimeTypes.MESSAGE_X_RSOCKET_ROUTING_V0.name,
// data: 'connect-client'
// }
// ], encodingClient)),
dataMimeType: WellKnownMimeTypes.APPLICATION_JSON.name,
metadataMimeType: WellKnownMimeTypes.MESSAGE_X_RSOCKET_COMPOSITE_METADATA_V0.name,
honorsLease: false,
keepaliveTime: 30000,
majorVersion: 1,
minorVersion: 0,
maxLifetime: 100000,
fragmentSize: 50 * 1024
});
const encodingClient = new EncodingRSocket(client);
socket = new MessageRoutingRSocket(encodingClient);
const responder = socket.responder;
// responder.addRequestResponseHandler('/basic/setup-payload', ans => {
// expect(ans).toEqual('Test-Client');
// done();
// return ans;
// })
client.establish();
done();
});
it("Returns Request Response payload", done => {
socket.simpleRequestResponse('/basic/request-response', 'Hello World').subscribe(ans => {
expect(ans).toEqual("Hello World");
done();
});
});
it("Maps Error", done => {
socket.simpleRequestResponse('/error/request-response', 'Hello World').subscribe(ans => {
}, (err) => {
expect(err.message.match(/Error: (\d+)\. Message: "(.+)"$/)[1]).toEqual("513");
expect(err.message.match(/Error: (\d+)\. Message: "(.+)"$/)[2]).toEqual("Hello World");
done();
});
});
it("Subscribes Using Request Stream", done => {
let counter = 0;
socket.simpleRequestStream('/basic/request-stream', 42).subscribe(ans => {
expect(ans).toEqual(counter++);
}, err => { }, () => {
expect(counter).toEqual(42);
done();
});
});
it("Respects Backpressure Requester", done => {
let counter = 0;
const requester = new BehaviorSubject(1);
socket.simpleRequestStream('/basic/request-stream', 42, requester).subscribe(ans => {
expect(ans).toEqual(counter++);
requester.next(1);
}, err => { }, () => {
expect(counter).toEqual(42);
done();
});
});
it("Request Stream sends cancel signal", done => {
let counter = 0;
const sub = socket.simpleRequestStream('/basic/request-stream/unending').subscribe(ans => {
expect(ans).toEqual(counter++);
});
timer(200).subscribe(a => sub.unsubscribe());
timer(400).pipe(flatMap(s => socket.simpleRequestResponse('/basic/request-stream/is-canceled'))).subscribe(n => {
expect(n).toBeTruthy();
done();
});
});
it("Request FNF reaches server", done => {
socket.simpleRequestFNF('/basic/request-fnf', 'Must be 42');
timer(200).pipe(flatMap(s => socket.simpleRequestResponse('/basic/request-fnf/check'))).subscribe(n => {
expect(n).toEqual('Must be 42');
done();
});
});
it("Handles Request Response / dataOnly", done => {
socket.addRequestResponseHandler('/basic/request-response', input => input + '-hello');
socket.simpleRequestResponse('/basic/request-reverse-response', {
topic: '/basic/request-response',
data: "world"
}).subscribe(ans => {
expect(ans).toEqual("world-hello");
done();
});
});
it("Handles Request Response / decodedPayload", done => {
socket.responder.removeHandler('/basic/request-response');
socket.addRequestResponseHandler('/basic/request-response', input => ({ data: input + '-hello' }), { payloadType: 'decodedPayload' });
socket.simpleRequestResponse('/basic/request-reverse-response', {
topic: '/basic/request-response',
data: "world"
}).subscribe(ans => {
expect(ans).toEqual("world-hello");
done();
});
});
it("Handles Empty Request Response", done => {
socket.addRequestResponseHandler('/basic/empty-request-response', data => {
expect(data).toBeUndefined();
return 'hello';
});
socket.simpleRequestResponse('/basic/empty-request-reverse-response', {
topic: '/basic/empty-request-response',
data: "\"empty\""
}).subscribe(ans => {
expect(ans).toEqual("hello");
done();
});
});
it("Handles Request Stream", done => {
socket.addRequestStreamHandler('/basic/request-response', data => range(0, Number(data)));
socket.simpleRequestResponse('/basic/request-reverse-stream', {
topic: '/basic/request-response',
data: 42
}).subscribe(ans => {
range(0, 42).pipe(reduce((a, b) => a + b, 0)).subscribe(result => expect(ans).toEqual(result), err => {
fail(err);
}, () => done());
});
});
it("Authenticates using simple authentication on request-response", done => {
socket.simpleRequestResponse('/secure/request-response', 'DoSthUnallowed', {
type: 'simple',
username: 'user',
password: 'pass'
}).subscribe(ans => {
expect(ans).toEqual('DoSthUnallowed');
done();
});
});
it("Authenticates using simple authentication on request-stream", done => {
socket.simpleRequestStream('/secure/request-stream', 'DoSthUnallowed', undefined, {
type: 'simple',
username: 'user',
password: 'pass'
}).subscribe(ans => {
expect(ans).toEqual('DoSthUnallowed');
done();
});
});
it("Authenticates using fire and forget", done => {
const number = Math.random();
socket.simpleRequestFNF('/secure/fnf', number, {
type: 'simple',
username: 'user',
password: 'pass'
});
timer(200).pipe(concatMap(t => {
return socket.simpleRequestResponse('/secure/fnf/verify', undefined, {
type: 'simple',
username: 'user',
password: 'pass'
});
}))
.subscribe(ans => {
expect(Number(ans)).toEqual(number);
done();
});
});
it("Fails unauthenticated", done => {
socket.simpleRequestResponse('/secure/request-response', 'DoSthUnallowed').subscribe(ans => {
expect(ans).toBe("Error: 513. Message: Access Denied");
done();
}, err => {
expect(err.message).toMatch(/^Error: 513.+$/);
done();
});
});
it("Fails with wrong credentials", done => {
socket.simpleRequestResponse('/secure/request-response', 'DoSthUnallowed', {
type: 'simple',
username: 'user',
password: 'passgweg'
}).subscribe(ans => {
expect(ans).toBe("Error: 513. Message: Access Denied");
done();
}, err => {
expect(err.message).toMatch(/^Error: 513.+$/);
done();
});
});
it("Fails with unallowed role", done => {
socket.simpleRequestResponse('/secure/request-response', 'DoSthUnallowed', {
type: 'simple',
username: 'test',
password: 'pass'
}).subscribe(ans => {
expect(ans).toBe("Error: 513. Message: Access Denied");
done();
}, err => {
expect(err.message).toMatch(/^Error: 513.+$/);
done();
});
});
it("Send fragments", done => {
jest.setTimeout(20000);
const databuffer = new Uint8Array(50 * 1024);
for (let i = 0; i < databuffer.length; i++) {
databuffer[i] = Math.floor(Math.random() * 100);
}
socket.simpleRequestResponse('/basic/request-response/byte', databuffer).subscribe(ans => {
for (let i = 0; i < databuffer.length; i++) {
expect(ans[i]).toEqual(databuffer[i]);
}
done();
}, err => {
fail(err.message);
});
});
// it("Accepts application/octet-stream mime type", done => {
// socket.simpleRequestResponse('/binary/request-response', new TextEncoder().encode("Hello World").buffer, MimeType.APPLICATION_OCTET_STREAM, MimeType.APPLICATION_OCTET_STREAM).subscribe(ans => {
// expect(new TextDecoder().decode(ans)).toEqual('Hello World To You Too!');
// done();
// });
// })
afterAll(done => {
socket.close().subscribe({
complete: () => {
done();
}
});
});
});
// describe("fluent_requests", () => {
// let requester: RSocketRoutingRequester;
// beforeAll(done => {
// const transport = new WebsocketTransport("ws://localhost:8080/rsocket");
// const responder = new RSocketRoutingResponder(MimeTypeRegistry.defaultRegistry());
// const client = new RSocketClient(transport, responder, MimeTypeRegistry.defaultRegistry());
// requester = new RSocketRoutingRequester(client);
// client.establish({
// dataMimeType: MimeType.APPLICATION_JSON,
// metadataMimeType: MimeType.MESSAGE_X_RSOCKET_COMPOSITE_METADATA,
// honorsLease: false,
// keepaliveTime: 30000,
// majorVersion: 1,
// minorVersion: 0,
// maxLifetime: 100000,
// });
// done();
// })
// it("Returns Request Response payload", done => {
// requester.route('/basic/request-response').data('Hello World').simpleRequestResponse().subscribe(ans => {
// expect(ans).toEqual("Hello World");
// done();
// })
// });
// it("Subscribes Using Request Stream", done => {
// let counter = 0;
// requester.route('/basic/request-stream').data(42).simpleRequestStream().subscribe(ans => {
// expect(ans).toEqual(counter++);
// }, err => { }, () => {
// expect(counter).toEqual(42);
// done();
// });
// });
// it("Request FNF reaches server", done => {
// requester.route('/basic/request-fnf').data('Must be 42').fireAndForget();
// timer(200).pipe(concatMap(s => requester.simpleRequestResponse('/basic/request-fnf/check'))).subscribe(n => {
// expect(n).toEqual('Must be 42');
// done();
// });
// });
// afterAll(done => {
// requester.rsocket.close().subscribe({
// complete: () => {
// done();
// }
// });
// })
// });
// describe("mimetypes", () => {
// let socket: MessageRoutingRSocket;
// let stringReverseMime: MimeType<string>;
// let textPlainMime: MimeType<string>;
// beforeAll(() => {
// const transport = new WebsocketTransport("ws://localhost:8080/rsocket");
// const client = new RSocketClient(transport, MimeTypeRegistry.defaultRegistry());
// stringReverseMime = new MimeType('application/stringreverse',
// {
// encoder: (text: string) => new TextEncoder().encode(text.split("").reverse().join("")),
// decoder: (buffer) => new TextDecoder().decode(buffer).split("").reverse().join("")
// }
// );
// client.mimeTypeRegistry.registerMimeType(stringReverseMime);
// textPlainMime = new MimeType('text/plain', {
// encoder: text => new TextEncoder().encode(text),
// decoder: buffer => new TextDecoder().decode(buffer)
// });
// client.mimeTypeRegistry.registerMimeType(textPlainMime);
// socket = new MessageRoutingRSocket(client);
// client.establish({
// dataMimeType: MimeType.APPLICATION_OCTET_STREAM,
// metadataMimeType: MimeType.MESSAGE_X_RSOCKET_COMPOSITE_METADATA,
// honorsLease: false,
// keepaliveTime: 30000,
// majorVersion: 1,
// minorVersion: 0,
// maxLifetime: 100000,
// });
// });
// it("Correctly submits stringreverse mime type", done => {
// socket.simpleRequestResponse('/basic/mime/stringreverse', "Hello World", stringReverseMime, stringReverseMime).subscribe(value => {
// expect(value).toEqual("Hello World");
// done();
// });
// });
// it("Distinguishes between input and output mime type", done => {
// socket.simpleRequestResponse('/basic/mime/stringreverse', "Hello World", stringReverseMime, textPlainMime).subscribe(value => {
// expect(value).toEqual("Hello World");
// done();
// });
// });
// afterAll(() => {
// socket.rsocket.close();
// });
// });