Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
5faca74
feat: add sumi rpc
bytemain Jan 18, 2024
1143f07
test: fix testcase
bytemain Jan 18, 2024
eb23e8d
chore: update code
bytemain Jan 18, 2024
bfa30d5
fix: forward binary in ext host
bytemain Jan 19, 2024
78a1f66
feat: optimize proxy create function
bytemain Jan 19, 2024
e0e9d5b
feat: using an incrementing request id
bytemain Jan 19, 2024
9ee0154
feat: support one of
bytemain Jan 19, 2024
349fd99
fix: protocol repo not defined
bytemain Jan 24, 2024
621e8b8
feat: use text decoder
bytemain Jan 24, 2024
aa17902
refactor: update code
bytemain Jan 25, 2024
effc328
refactor: update protocol register
bytemain Jan 26, 2024
174440a
test: fix testcase
bytemain Jan 29, 2024
67c8a43
test: update testcase
bytemain Jan 30, 2024
bc0de9a
chore: update code
bytemain Jan 30, 2024
8055fc3
chore: update types
bytemain Jan 30, 2024
3194981
feat: connection add dispose method
bytemain Jan 31, 2024
936bc12
feat: avoid create function multiple times
bytemain Feb 1, 2024
86d524a
refactor: simplify ext connection implementation
bytemain Feb 1, 2024
6d2c304
feat(rpc): support cancellation
bytemain Feb 2, 2024
f81d1c7
refactor: update code
bytemain Feb 2, 2024
b3e25e3
feat(rpc): support any serializer
bytemain Feb 2, 2024
529a712
refactor: update code
bytemain Feb 2, 2024
907bf26
feat: any serializer can reviver uri
bytemain Feb 2, 2024
80d8e00
feat: ext rpc protocol use channel
bytemain Feb 2, 2024
a26ff0b
fix: on request not found args
bytemain Feb 2, 2024
b133b91
fix: rpc timeout not work
bytemain Feb 2, 2024
511069b
refactor: update code
bytemain Feb 2, 2024
d5433db
docs: add more docs
bytemain Feb 2, 2024
947ab07
fix: rpc broken
bytemain Feb 3, 2024
f9f4341
docs: update docs
bytemain Feb 4, 2024
972dcec
fix: message port on message not work
bytemain Feb 4, 2024
c5260e4
test: fix types
bytemain Feb 4, 2024
1176d2e
refactor: use new style MessagePort event listener
bytemain Feb 4, 2024
35683bc
fix: any serializer cannot handle Uint8Array
bytemain Feb 4, 2024
3c3036c
test: fix testcase
bytemain Feb 4, 2024
a5ca110
refactor: update code
bytemain Feb 4, 2024
6d76f81
test: remove only case
bytemain Feb 5, 2024
63abdb7
feat: compatible with legacy
bytemain Feb 5, 2024
a5be947
refactor: rpc use sumi connection
bytemain Feb 5, 2024
1a204ad
refactor: add sumi connection multiplexer
bytemain Feb 5, 2024
bf6e03e
test: fix testcases
bytemain Feb 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions packages/connection/__test__/common/buffers.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Buffers, copy } from '../../src/common/connection/buffers';
import { Buffers, copy } from '../../src/common/buffers/buffers';

describe('Buffers', () => {
it('can push and slice', () => {
Expand Down Expand Up @@ -232,21 +232,21 @@ describe('Buffers', () => {
expect(cursor.lineOffset).toEqual(1);
expect(list.pos(cursor.offset)).toEqual({ buf: cursor.line, offset: cursor.lineOffset });

cursor.move(1);
cursor.skip(1);
expect(cursor.offset).toEqual(6);
expect(cursor.value).toEqual(list.get(cursor.offset));
expect(cursor.line).toEqual(1);
expect(cursor.lineOffset).toEqual(2);
expect(list.pos(cursor.offset)).toEqual({ buf: cursor.line, offset: cursor.lineOffset });

cursor.move(2);
cursor.skip(2);
expect(cursor.offset).toEqual(8);
expect(cursor.value).toEqual(list.get(cursor.offset));
expect(cursor.line).toEqual(1);
expect(cursor.lineOffset).toEqual(4);
expect(list.pos(cursor.offset)).toEqual({ buf: cursor.line, offset: cursor.lineOffset });

cursor.move(3);
cursor.skip(3);
expect(cursor.offset).toEqual(11);
expect(cursor.value).toEqual(list.get(cursor.offset));
expect(cursor.line).toEqual(2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@
import { BinaryReader } from '@furyjs/fury/dist/lib/reader';

import {
StreamPacketDecoder,
createStreamPacket,
kMagicNumber,
} from '../../src/common/connection/drivers/stream-decoder';

const reader = BinaryReader({});
LengthFieldBasedFrameDecoder,
indicator,
prependLengthField,
} from '../../src/common/connection/drivers/frame-decoder';

function round(x: number, count: number) {
return Math.round(x * 10 ** count) / 10 ** count;
Expand Down Expand Up @@ -42,14 +40,7 @@ console.timeEnd('createPayload');
// 1m
const pressure = 1024 * 1024;

const purePackets = [p1k, p64k, p128k, p5m, p10m].map((v) => [createStreamPacket(v), v] as const);

const mixedPackets = [p1m, p5m].map((v) => {
const sumiPacket = createStreamPacket(v);
const newPacket = createPayload(1024 + sumiPacket.byteLength);
newPacket.set(sumiPacket, 1024);
return [newPacket, v] as const;
});
const purePackets = [p1k, p64k, p128k, p5m, p10m].map((v) => [prependLengthField(v), v] as const);

const size = purePackets.reduce((acc, v) => acc + v[0].byteLength, 0);

Expand All @@ -61,22 +52,30 @@ purePackets.forEach((v) => {
offset += sumiPacket.byteLength;
});

const mixedPackets = [p1m, p5m].map((v) => {
const sumiPacket = prependLengthField(v);
const newPacket = createPayload(1024 + sumiPacket.byteLength);
newPacket.set(sumiPacket, 1024);
return [newPacket, v] as const;
});

const packets = [...purePackets, ...mixedPackets];

describe('stream-packet', () => {
it('can create sumi stream packet', () => {
describe('frame decoder', () => {
it('can create frame', () => {
const content = new Uint8Array([1, 2, 3]);
const packet = createStreamPacket(content);
const packet = prependLengthField(content);
const reader = BinaryReader({});

reader.reset(packet);
expect(reader.uint32()).toBe(kMagicNumber);
expect(reader.varUInt32()).toBe(content.byteLength);
expect(Uint8Array.from(reader.buffer(4))).toEqual(indicator);
expect(reader.uint32()).toBe(content.byteLength);
expect(Uint8Array.from(reader.buffer(content.byteLength))).toEqual(content);
});

packets.forEach(([packet, expected]) => {
it(`can decode stream packet: ${round(packet.byteLength / 1024 / 1024, 2)}m`, (done) => {
const decoder = new StreamPacketDecoder();
it(`can decode stream: ${round(packet.byteLength / 1024 / 1024, 2)}m`, (done) => {
const decoder = new LengthFieldBasedFrameDecoder();

decoder.onData((data) => {
fastExpectBufferEqual(data, expected);
Expand All @@ -95,8 +94,8 @@ describe('stream-packet', () => {
});
});

it('can decode a stream payload contains multiple packets', (done) => {
const decoder = new StreamPacketDecoder();
it('can decode a stream payload contains multiple frames', (done) => {
const decoder = new LengthFieldBasedFrameDecoder();
const expectCount = purePackets.length;
let count = 0;
decoder.onData((data) => {
Expand All @@ -120,18 +119,17 @@ describe('stream-packet', () => {
logMemoryUsage();
});

it('can decode a stream it header and payload are separated', (done) => {
it('can decode a stream it has no valid length info', (done) => {
const v = createPayload(1024);
const sumiPacket = createStreamPacket(v);
const sumiPacket = prependLengthField(v);

const decoder = new StreamPacketDecoder();
const decoder = new LengthFieldBasedFrameDecoder();
decoder.onData((data) => {
fastExpectBufferEqual(data, v);
done();
});

console.log('write chunk', sumiPacket.byteLength);

// use pressure = 2 to simulate the header and payload are separated
const pressure = 2;
for (let i = 0; i < sumiPacket.byteLength; i += pressure) {
Expand Down
64 changes: 64 additions & 0 deletions packages/connection/__test__/common/fury-extends/one-of.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* eslint-disable no-console */

import { PingMessage, PongMessage, parse, stringify } from '../../../src/common/ws-channel';

describe('oneOf', () => {
function testIt(obj: any) {
const bytes = stringify(obj);
const obj2 = parse(bytes);
expect(obj2).toEqual(obj);
const str = JSON.stringify(obj);

console.log('bytes.length', bytes.byteLength);
console.log('json length', str.length);
}

it('should serialize and deserialize', () => {
const obj = {
kind: 'ping',
clientId: '123',
id: '456',
} as PingMessage;

testIt(obj);

const obj2 = {
kind: 'pong',
clientId: '123',
id: '456',
} as PongMessage;

testIt(obj2);

const obj3 = {
kind: 'open',
clientId: '123',
id: '456',
path: '/test',
};

testIt(obj3);

const obj4 = {
kind: 'server-ready',
id: '456',
};

testIt(obj4);

const obj5 = {
kind: 'data',
id: '456',
content: 'hello',
};

testIt(obj5);

const obj6 = {
kind: 'binary',
id: '456',
binary: Buffer.from([1, 2, 3]),
};
testIt(obj6);
});
});
34 changes: 34 additions & 0 deletions packages/connection/__test__/common/rpc/common-tester.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { IConnectionPair } from './utils';

export const test = (
name: string,
options: {
pairFactory: () => IConnectionPair;
factory: (pair: IConnectionPair) => any;
},
) => {
const { factory, pairFactory } = options;
describe(name, () => {
let pair: IConnectionPair;
beforeEach(() => {
pair = pairFactory();
});

afterEach(() => {
pair && pair.close();
});

it('can call method', async () => {
const { invoker1, invoker2 } = factory(pair);

const result = await invoker1.add(1, 2);
expect(result).toBe(3);

const result2 = await invoker2.shortUrl('1234567890abcdefg');
expect(result2).toBe('1234567890');

const result3 = await invoker2.returnUndefined();
expect(result3).toBeUndefined();
});
});
};
9 changes: 9 additions & 0 deletions packages/connection/__test__/common/rpc/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { test } from './common-tester';
import { createLegacyRPCClientPair, createMessageConnectionPair } from './utils';

const factory = (pair: any) => createLegacyRPCClientPair(pair);

test('legacy json rpc', {
factory,
pairFactory: createMessageConnectionPair,
});
77 changes: 77 additions & 0 deletions packages/connection/__test__/common/rpc/sumi-rpc.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Type } from '@furyjs/fury';

import { METHOD_NOT_REGISTERED } from '@opensumi/ide-connection/lib/common/constants';

import { test } from './common-tester';
import { createConnectionPair, createSumiRPCClientPair } from './utils';

test('sumi rpc', {
factory: createSumiRPCClientPair,
pairFactory: createConnectionPair,
});

describe('sumi rpc only', () => {
let pair: ReturnType<typeof createConnectionPair>;
beforeEach(() => {
pair = createConnectionPair();
});

afterEach(() => {
pair && pair.close();
});

it('can throw error', async () => {
const { invoker1, invoker2, client1, client2 } = createSumiRPCClientPair(pair);

client1.listenService({
shortUrl: (url: string) => {
if (!url) {
throw new Error('url is empty');
}
return url.slice(0, 10);
},
});
client2.listenService({
add: (a: number, b: number) => {
if (a === 0) {
throw new Error('a is zero');
}
return a + b;
},
});

await expect(invoker1.add(0, 2)).rejects.toThrow('a is zero');
await expect(invoker2.shortUrl('')).rejects.toThrow('url is empty');

await expect(invoker1.throwAString()).resolves.toEqual(METHOD_NOT_REGISTERED);
await expect(invoker2.throwAString()).resolves.toEqual(METHOD_NOT_REGISTERED);

client2.listenService({
throwAString: () => {
// eslint-disable-next-line no-throw-literal
throw 'a string';
},
});

try {
await invoker1.throwAString();
} catch (error) {
expect(typeof error).toBe('string');
expect(error).toBe('a string');
}
});

it('can throw error when method not found', async () => {
const { invoker1, invoker2, repo } = createSumiRPCClientPair(pair);
repo.loadProtocolMethod({
method: 'notFound',
request: [],
response: {
type: Type.any(),
},
});

await expect(invoker1.notFound()).resolves.toEqual(METHOD_NOT_REGISTERED);
await expect(invoker2.notFound()).resolves.toEqual(METHOD_NOT_REGISTERED);
});
});
Loading