WebSocket has been implemented.

This commit is contained in:
crusader 2017-07-14 20:17:49 +09:00
parent 2c1a524dde
commit f461d42744
15 changed files with 381 additions and 11 deletions

View File

@ -79,6 +79,7 @@
"redux": "^3.7.1",
"redux-saga": "^0.15.4",
"reselect": "^3.0.1",
"semantic-ui-css":"^2.2.10",
"semantic-ui-react": "^0.70.0",
"socket.io-client": "^2.0.3"
},

View File

@ -4,7 +4,6 @@ import { fork } from 'redux-saga/effects';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import * as injectTapEventPlugin from 'react-tap-event-plugin';
@ -20,8 +19,6 @@ injectTapEventPlugin();
function* app(): any {
const appContainer: HTMLElement = yield Platform.getAppContainer('react-placeholder');
ReactDOM.render(
<div style={{
width: '100vw',
@ -40,14 +37,13 @@ function* app(): any {
ReactDOM.render(
<Provider store={store}>
<MuiThemeProvider muiTheme={muiTheme}>
<ConnectedRouter history={history}>
{routes}
</ConnectedRouter>
</MuiThemeProvider>
</Provider>,
appContainer,
);
}
sagaMiddleware.run(app);

View File

@ -0,0 +1,136 @@
import {
ProtocolNotification,
ProtocolRequest,
ProtocolResponse,
} from './protocol';
import {
PROTOCOL_NAME as RPCProtocol,
RPCRequest,
RPCResponse,
} from './protocol/rpc';
export type OnConnectFunc = () => void;
export type OnDisconnectFunc = () => void;
export type OnResponseFunc = (response: any) => void;
type ID_TYPE = number;
interface RequestQueue {
resolve: (value?: any) => void;
reject: (reason?: any) => void;
}
export default class WebSocketRPC {
private url: string;
private conn: WebSocket;
private isReady: boolean;
private requestID: ID_TYPE = 0;
private onConnectListeners: OnConnectFunc[] = [];
private onDisconnectListeners: OnDisconnectFunc[] = [];
private onResponseListeners: Map<string, OnResponseFunc[]>;
private requestQueue: Map<ID_TYPE, RequestQueue>;
/**
* name
*/
public constructor(url: string) {
this.isReady = false;
this.url = url;
this.onResponseListeners = new Map();
this.requestQueue = new Map();
this.initialize();
}
public Call(method: string, params: any[]): Promise<any> {
return new Promise<any>((resolve, reject) => {
const requestID = this.getRequestID();
let request = new ProtocolRequest<ID_TYPE>(RPCProtocol, requestID);
let req = new RPCRequest(method, params);
request.setBody(req.toJSON());
this.conn.send(request.toJSON());
this.requestQueue.set(requestID, {resolve: resolve, reject: reject});
});
}
public OnConnect(cb: OnConnectFunc): void {
if (this.isReady) {
cb();
}
this.onConnectListeners.push(cb);
}
public OnDisconnect(cb: OnDisconnectFunc): void {
this.onDisconnectListeners.push(cb);
}
private initialize(): void {
this.conn = new WebSocket(this.url);
this.conn.onopen = (e: Event): any => {
this.fireOnConnect();
this.isReady = true;
return null;
};
this.conn.onclose = (e: CloseEvent): any => {
this.fireOnDisconnect();
return null;
};
this.conn.onmessage = (e: MessageEvent): any => {
let response: ProtocolResponse<ID_TYPE> = JSON.parse(e.data);
let res: RPCResponse = JSON.parse(response.getBody());
const requestID = response.getID();
const error = response.getError();
const result = res.getResult();
if (this.requestQueue.has(requestID)) {
let promise = this.requestQueue.get(requestID);
this.requestQueue.delete(requestID);
if (null != result) {
promise.resolve(result);
} else if (null != error) {
promise.reject(error);
} else {
console.log('??????');
}
} else {
// perhaps sever side send message
}
this.fireOnMessage(e);
};
}
private fireOnConnect(): void {
for (let i = 0; i < this.onConnectListeners.length; i++) {
this.onConnectListeners[i]();
}
}
private fireOnDisconnect(): void {
for (let i = 0; i < this.onDisconnectListeners.length; i++) {
this.onDisconnectListeners[i]();
}
}
private fireOnMessage(e: MessageEvent): void {
for (let i = 0; i < this.onDisconnectListeners.length; i++) {
this.onDisconnectListeners[i]();
}
}
private getRequestID(): number {
return ++this.requestID;
}
}

View File

@ -0,0 +1,46 @@
export const enum ErrorCode {
E_PARSE = -32700,
E_INVALID_REQ = -32600,
E_NO_METHOD = -32601,
E_BAD_PARAMS = -32602,
E_INTERNAL = -32603,
E_SERVER = -32000,
}
export class ProtocolError {
private code: ErrorCode;
private message: string;
private data: any;
public constructor(code: ErrorCode, message: string, data: any) {
this.code = code;
this.message = message;
this.data = data;
}
/**
* getID
*/
public getCode(): ErrorCode {
return this.code;
}
/**
* getBody
*/
public getMessage(): string {
return this.message;
}
/**
* getError
*/
public getData(): any {
return this.data;
}
public static newError(json: string): ProtocolError {
return JSON.parse(json);
}
}

View File

@ -0,0 +1,44 @@
export class ProtocolHeader<ID> {
private readonly protocol: string;
private readonly id: ID;
private body: string;
public constructor(protocol: string, id: ID) {
this.protocol = protocol;
this.id = id;
}
/**
* getProtocol
*/
public getProtocol(): string {
return this.protocol;
}
/**
* getID
*/
public getID(): ID {
return this.id;
}
/**
* setID
*/
public setBody(body: string): void {
this.body = body;
}
/**
* getID
*/
public getBody(): string {
return this.body;
}
/**
* toJSON
*/
public toJSON(): string {
return JSON.stringify(this);
}
}

View File

@ -0,0 +1,35 @@
export class ProtocolNotification {
private readonly protocol: string;
private body: string;
public constructor(protocol: string) {
this.protocol = protocol;
}
/**
* getProtocol
*/
public getProtocol(): string {
return this.protocol;
}
/**
* setBody
*/
public setBody(body: string): void {
this.body = body;
}
/**
* getBody
*/
public getBody(): string {
return this.body;
}
/**
* toJSON
*/
public toJSON(): string {
return JSON.stringify(this);
}
}

View File

@ -0,0 +1,7 @@
import { ProtocolHeader } from './ProtocolHeader';
export class ProtocolRequest<ID> extends ProtocolHeader<ID> {
public constructor(protocol: string, id: ID) {
super(protocol, id);
}
}

View File

@ -0,0 +1,23 @@
import { ProtocolHeader } from './ProtocolHeader';
import { ProtocolError } from './ProtocolError';
export class ProtocolResponse<ID> extends ProtocolHeader<ID> {
private error: ProtocolError;
public constructor(protocol: string, id: ID) {
super(protocol, id);
}
/**
* setError
*/
public setError(error: ProtocolError): void {
this.error = error;
}
/**
* getError
*/
public getError(): ProtocolError {
return this.error;
}
}

View File

@ -0,0 +1,6 @@
export abstract class ProtocolSub {
/**
* toJSON
*/
public abstract toJSON(): string;
}

View File

@ -0,0 +1,6 @@
export * from './ProtocolError';
export * from './ProtocolHeader';
export * from './ProtocolNotification';
export * from './ProtocolRequest';
export * from './ProtocolResponse';
export * from './ProtocolSub';

View File

@ -0,0 +1,33 @@
import { ProtocolSub } from '../ProtocolSub';
export class RPCRequest extends ProtocolSub {
private readonly method: string;
private readonly params: any[];
public constructor(method: string, params: any[]) {
super();
this.method = method;
this.params = params;
}
/**
* getMethod
*/
public getMethod(): string {
return this.method;
}
/**
* getParams
*/
public getParams(): any[] {
return this.params;
}
/**
* toJSON
*/
public toJSON(): string {
return JSON.stringify(this);
}
}

View File

@ -0,0 +1,24 @@
import { ProtocolSub } from '../ProtocolSub';
export class RPCResponse extends ProtocolSub {
private readonly result: any;
public constructor(result: any) {
super();
this.result = result;
}
/**
* getResult
*/
public getResult(): any {
return this.result;
}
/**
* toJSON
*/
public toJSON(): string {
return JSON.stringify(this);
}
}

View File

@ -0,0 +1,5 @@
export * from './RPCRequest';
export * from './RPCResponse';
export const PROTOCOL_NAME = 'jsonrpc';

View File

@ -1,6 +1,7 @@
import Service from '@overflow/commons/api/Service';
import Member from '../model/Member';
@ServiceClass
export class MemberService extends Service {
public constructor() {

View File

@ -1,7 +1,7 @@
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"baseUrl": "src/ts",
"baseUrl": ".",
"declaration": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
@ -11,7 +11,7 @@
"es5",
"es6"
],
"module": "esnext",
"module": "umd",
"moduleResolution": "node",
"newLine": "LF",
"noImplicitAny": false,
@ -19,6 +19,12 @@
"outDir": "dist/ts/",
"preserveConstEnums": true,
"pretty": true,
"paths": {
"*": [
"./src/ts/*",
"./types/*"
]
},
"removeComments": true,
"sourceMap": true,
"target": "es5",
@ -34,6 +40,7 @@
"build",
"config",
"node_modules",
"public"
"public",
"types"
]
}