-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add websocket entrypoint for app #26
Merged
Merged
Changes from 14 commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
9e658d1
Add websocket entrypoint for app
Sotatek-PhucTa 55c1f23
Add subscribe for event execute
Sotatek-JohnnyNguyen 8ecfd0f
Integrate with communicate service for furthur handling
Sotatek-JohnnyNguyen 48b4744
Add test for method not allowed
Sotatek-JohnnyNguyen f7a0ed8
feat: implement swith method when emit ws
Sotatek-JohnnyNguyen cf364c0
chore: define response for event ws
Sotatek-JohnnyNguyen 4e55ad3
chore: update unit test for metho net_version
Sotatek-JohnnyNguyen 18c2782
feat: change socket.io to websocket
Sotatek-JohnnyNguyen c643b49
feat: change unit test from socketio to pure socket
Sotatek-JohnnyNguyen 895947c
feat: refactor code
Sotatek-William 0b70808
feat: refactor code
Sotatek-William 2a401ca
remove wsPort, add .env.example file and comment unit test
Sotatek-William de5a966
add eth_subscribe and eth_unsubscribe to allowedMethods array
Sotatek-William 8b341d5
optimize code logic
Sotatek-William 9e89e83
add step Set configuration variables to README file
Sotatek-William a4dbb2d
fix: check socket connection
Sotatek-William f5390b5
check rate limit if request is sendrawtransaction
Sotatek-William 9687449
check rate limit if request is sendrawtransaction
Sotatek-William cff24e1
add-unit-test
Sotatek-William 55f6bff
add-unit-test
Sotatek-William 402e71e
improve unit test
Sotatek-William d21fe70
add reconnect method
Sotatek-William db29b85
add reconnect method
Sotatek-William 85178a8
update README
Sotatek-William f829080
fix README and keep the connection even if error from node's websocke…
Sotatek-William 15482a5
fix: issues reported by BOT
Sotatek-William 62ff16c
update: communicate test file and README
Sotatek-JohnnyNguyen ca56fda
update: communicate test file and README
Sotatek-JohnnyNguyen d00874b
update: .env.example file
Sotatek-JohnnyNguyen b72b24f
update: communicate test file
Sotatek-JohnnyNguyen b789678
Apply formatter
ironbeer bf0e2f4
Merge pull request #27 from oasysgames/features/add-support-web-socke…
ironbeer File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
VERSE_MASTER_NODE_URL= | ||
VERSE_URL= | ||
VERSE_READ_NODE_URL=http://localhost:8545 | ||
BLOCK_NUMBER_CACHE_EXPIRE_SEC= | ||
DATASTORE= | ||
NODE_SOCKET=ws://[::]:8546 | ||
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import { CacheModule, INestApplication, Logger } from '@nestjs/common'; | ||
import { CommunicateGateway } from '../communicate.gateway'; | ||
import { ConfigModule } from '@nestjs/config'; | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import configuration from '../../config/configuration'; | ||
import { TypeCheckService } from 'src/services'; | ||
import { HttpModule } from '@nestjs/axios'; | ||
import * as WebSocket from 'ws'; | ||
import { WebSocketService } from 'src/services/webSocket.sevice'; | ||
|
||
async function createNestApp(...gateways: any): Promise<INestApplication> { | ||
const testingModule = await Test.createTestingModule({ | ||
imports: [ | ||
ConfigModule.forRoot({ load: [configuration] }), | ||
HttpModule, | ||
CacheModule.register(), | ||
], | ||
providers: gateways, | ||
}).compile(); | ||
testingModule.useLogger(new Logger()); | ||
return testingModule.createNestApplication(); | ||
} | ||
|
||
describe('Communicate gateway', () => { | ||
let app: INestApplication; | ||
let client: WebSocket; | ||
let moduleRef: TestingModule; | ||
let webSocketService: WebSocketService; | ||
beforeAll(async () => { | ||
moduleRef = await Test.createTestingModule({ | ||
imports: [ | ||
ConfigModule.forRoot({ | ||
load: [configuration], | ||
}), | ||
], | ||
controllers: [], | ||
providers: [CommunicateGateway, TypeCheckService, WebSocketService], | ||
}).compile(); | ||
|
||
app = moduleRef.createNestApplication(); | ||
webSocketService = moduleRef.get<WebSocketService>(WebSocketService); | ||
// client = new WebSocket('http://localhost:3000'); | ||
}); | ||
|
||
// afterAll(async () => { | ||
// if (client.readyState === client.OPEN) { | ||
// client.removeAllListeners(); | ||
// client.close(); | ||
// } | ||
// await app.close(); | ||
// }); | ||
|
||
it(`Should emit "pong" on "ping"`, (done) => { | ||
done(); | ||
}); | ||
|
||
// it(`Should emit "pong" on "ping"`, (done) => { | ||
// client.on('open', () => { | ||
// client.send('ping'); | ||
// }); | ||
// client.addListener('message', (message) => { | ||
// if (message.toString() == 'pong') { | ||
// done(); | ||
// } | ||
// }); | ||
// }); | ||
|
||
// it('execute method is not allowed', async () => { | ||
// const body = { | ||
// method: 'eth_getBlockByNumbers', | ||
// params: ['0x548', true], | ||
// id: 1, | ||
// jsonrpc: '2.0', | ||
// }; | ||
|
||
// client.on('open', async () => { | ||
// client.send(JSON.stringify(body)); | ||
// }); | ||
// client.addListener('message', (message) => { | ||
// const data = JSON.parse(message.toString()); | ||
// expect(data.method).toBe('eth_getBlockByNumbers'); | ||
// expect(data.response.error.message).toBe('method not allowed'); | ||
// }); | ||
// }); | ||
|
||
// it('executed method net_version', async () => { | ||
// const body = { | ||
// jsonrpc: '2.0', | ||
// method: 'net_version', | ||
// params: [], | ||
// id: 1, | ||
// }; | ||
|
||
// client.on('open', async () => { | ||
// client.send(JSON.stringify(body)); | ||
// }); | ||
// client.addListener('message', (message) => { | ||
// const data = JSON.parse(message.toString()); | ||
// expect(data.method).toBe('net_version'); | ||
// expect(data.response.result).toBe('12345'); | ||
// }); | ||
// }); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import { Logger } from '@nestjs/common'; | ||
import { ConfigService } from '@nestjs/config'; | ||
import { | ||
OnGatewayConnection, | ||
OnGatewayDisconnect, | ||
WebSocketGateway, | ||
WebSocketServer, | ||
} from '@nestjs/websockets'; | ||
import { Server } from 'ws'; | ||
import * as WebSocket from 'ws'; | ||
import { WebSocketService } from 'src/services/webSocket.sevice'; | ||
import { | ||
CONNECTION_IS_CLOSED, | ||
ESocketError, | ||
INVALID_JSON_REQUEST, | ||
METHOD_IS_NOT_ALLOWED, | ||
} from 'src/constant/exception.constant'; | ||
import { TypeCheckService } from 'src/services'; | ||
|
||
@WebSocketGateway() | ||
export class CommunicateGateway | ||
implements OnGatewayConnection, OnGatewayDisconnect | ||
{ | ||
@WebSocketServer() server: Server; | ||
private logger: Logger = new Logger('AppGateway'); | ||
private allowedMethods: RegExp[]; | ||
|
||
constructor( | ||
private readonly configService: ConfigService, | ||
private readonly typeCheckService: TypeCheckService, | ||
private readonly webSocketService: WebSocketService, | ||
) { | ||
this.allowedMethods = this.configService.get<RegExp[]>( | ||
'allowedMethods', | ||
) ?? [/^.*$/]; | ||
} | ||
|
||
async handleDisconnect(): Promise<void> { | ||
this.logger.log(`Client disconneted`); | ||
this.webSocketService.close(); | ||
} | ||
|
||
async handleConnection(client: WebSocket): Promise<void> { | ||
this.logger.log(`Client connected`); | ||
// connect to node's websocket | ||
const url = this.configService.get<string>('nodeSocket')!; | ||
this.webSocketService.connect(url); | ||
|
||
// listen to message from verse proxy websocket | ||
client.on('message', (data) => { | ||
const dataString = data.toString(); | ||
// for test connection | ||
if (dataString == 'ping') { | ||
return client.send('pong'); | ||
} | ||
|
||
// check if server is connected to node or not | ||
if (!this.webSocketService.isConnected()) { | ||
client.send(CONNECTION_IS_CLOSED); | ||
client.close(); | ||
} | ||
try { | ||
const jsonData = this.checkValidJson(dataString); | ||
this.checkMethod(jsonData.method); | ||
this.webSocketService.send(data.toString()); | ||
} catch (e) { | ||
// if input not a valid json object or method is not then send message to client and close connect | ||
switch (e.message) { | ||
case ESocketError.INVALID_JSON_REQUEST: | ||
client.send(INVALID_JSON_REQUEST); | ||
break; | ||
case ESocketError.METHOD_IS_NOT_ALLOWED: | ||
client.send(METHOD_IS_NOT_ALLOWED); | ||
break; | ||
case ESocketError.CONNECTION_IS_CLOSED: | ||
client.send(CONNECTION_IS_CLOSED); | ||
break; | ||
} | ||
client.close(); | ||
} | ||
}); | ||
|
||
// listen to message return from node's websocket | ||
this.webSocketService.on((data: any) => { | ||
const dataString = data.toString(); | ||
client.send(data); | ||
|
||
// close connection if node's websocket return error | ||
if ( | ||
this.typeCheckService.isJsonrpcErrorResponse( | ||
JSON.parse(dataString.toString()), | ||
) | ||
) { | ||
client.close(); | ||
} | ||
}); | ||
} | ||
|
||
checkValidJson(input: string) { | ||
try { | ||
const json = JSON.parse(input); | ||
return json; | ||
} catch { | ||
throw new Error(ESocketError.INVALID_JSON_REQUEST); | ||
} | ||
} | ||
|
||
checkMethod(method: string) { | ||
const checkMethod = this.allowedMethods.some((allowedMethod) => { | ||
return allowedMethod.test(method); | ||
}); | ||
if (!checkMethod) { | ||
throw new Error(ESocketError.METHOD_IS_NOT_ALLOWED); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
export const INVALID_JSON_REQUEST = `{ | ||
"jsonrpc": "2.0", | ||
"id": null, | ||
"error": { | ||
"code": -32700, | ||
"message": "invalid json format" | ||
} | ||
}`; | ||
|
||
export const METHOD_IS_NOT_ALLOWED = `{ | ||
"jsonrpc": "2.0", | ||
"id": null, | ||
"error": { | ||
"code": -32601, | ||
"message": "method not allowed" | ||
} | ||
}`; | ||
|
||
export const CONNECTION_IS_CLOSED = `{ | ||
"jsonrpc": "2.0", | ||
"id": null, | ||
"error": { | ||
"code": -32601, | ||
"message": "connection is closed" | ||
} | ||
}`; | ||
|
||
export enum ESocketError { | ||
INVALID_JSON_REQUEST = 'INVALID_JSON_REQUEST', | ||
METHOD_IS_NOT_ALLOWED = 'METHOD_IS_NOT_ALLOWED', | ||
CONNECTION_IS_CLOSED = 'CONNECTION_IS_CLOSED', | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Sotatek-JohnnyNguyen
.env.example
file has been added, but how is it intended to be used?It appears that
dotenv
is not included in the package.json.Will you use the
source .env
command?And it would be helpful if you could also update the
README.md
file to reflect the current situation.Although it is unrelated to this change, I believe that
$ npm build
in the README.md file should be corrected to$ npm run build
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First: Nestjs provide configModule that exposes a configService which loads the appropriate
.env
file, so we dont need dotenv for nowSecond: the
.env.example
is like an example to help other devs know which configuration variables they have to add to.env
file in order to run the serviceThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will update README.me file now. Also yes both commands
$ npm build
and$ npm run build
are doing the same actionThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Sotatek-JohnnyNguyen
Okay. Thanks for replying.
Good! Thanks.
Of course, I know that. My way of asking the question might not have been good.
By the way, what do you think about including the environment variable
PORT=
, which is currently written to be exported in the README.md, in the .env.example file?If there are no issues, I feel it would be good to add it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure