Skip to content

Commit

Permalink
feat(examples): uWebSockets basic
Browse files Browse the repository at this point in the history
  • Loading branch information
Ni55aN committed Oct 22, 2021
1 parent 3b26a23 commit 96a8038
Show file tree
Hide file tree
Showing 9 changed files with 185 additions and 1 deletion.
28 changes: 28 additions & 0 deletions examples/client/list/client-basic-uws.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { $, h, onMount } from 'easyhard'
import { easyhardClient } from 'easyhard-client'
import { map, take } from 'rxjs/operators'
import { BasicActionsUWS } from '../../shared'

const client = easyhardClient<BasicActionsUWS>()

function App() {
const count1 = client.call('getData').pipe(
take(5),
map(data => String(data.count))
)
const count2 = $(undefined).pipe(
client.pipe('getIP'),
map(data => String(data.ip))
)

const el = h('div', {},
h('div', {}, count1),
h('div', {}, count2)
)

onMount(el, () => client.connect(() => new WebSocket(`ws://${location.host}/uws/basic/`), { http: `http://${location.host}/uws/basic/` }))

return el
}

document.body.appendChild(App())
4 changes: 4 additions & 0 deletions examples/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion examples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
"module-alias": "^2.2.2",
"rxjs": "^7.1.0",
"todomvc-app-css": "^2.4.1",
"tsconfig-paths": "^3.9.0"
"tsconfig-paths": "^3.9.0",
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.0.0"
},
"devDependencies": {
"babel-plugin-overload": "0.0.2",
Expand Down
1 change: 1 addition & 0 deletions examples/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import reconnect from './reconnect'
import request from './request'
import unstable from './unstable'
import upload from './upload'
import './uws'

const app = express()
expressWs(app)
Expand Down
15 changes: 15 additions & 0 deletions examples/server/uws/basic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { easyhardServer } from './easyhard-server';
import { BasicActionsUWS } from '../../shared'
import { getInterval } from '../shared';
import { map, mergeMap, take } from 'rxjs/operators';
import { interval } from 'rxjs';

export default easyhardServer<BasicActionsUWS>({
getData: getInterval(),
getIP: mergeMap((payload) => {
return interval(500).pipe(
take(14),
map(count => ({ ip: String(count) + '|' + payload.$request.socket.ip }))
)
}),
})
109 changes: 109 additions & 0 deletions examples/server/uws/easyhard-server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { WebSocketState, WsConnection } from 'easyhard-bridge'
import { getUID } from 'easyhard-common'
import { attach, Attachment, BodyListeners, CookieSetters, Handlers, Http, ReqListeners } from 'easyhard-server'
import { WebSocketBehavior, WebSocket, HttpResponse, HttpRequest } from 'uWebSockets.js'
import { TextDecoder } from 'util'

type HttpTunnel = (res: HttpResponse, req: HttpRequest) => void

const decoder = new TextDecoder('utf-8')

function arrayBufferToString(data: ArrayBuffer) {
return decoder.decode(new Uint8Array(data))
}

export function useHttp(): Http & { tunnel: HttpTunnel } {
const reqListeners: ReqListeners = new Map()
const bodyListeners: BodyListeners = new Map()
const cookieSetters: CookieSetters = new Map()

function tunnel(res: HttpResponse, req: HttpRequest) {
}

return {
bodyListeners,
reqListeners,
cookieSetters,
tunnel
}
}

type Request = HttpRequest & { socket: { ip: string }}
type Props = { open: (ws: WebSocket, attachment: Attachment) => void }

export function easyhardServer<T>(actions: Handlers<T, Request>): { attachClient: (props: Props) => WebSocketBehavior, httpTunnel: HttpTunnel } {
const http = useHttp()
type Connection = WsConnection & { listeners: [string, (...args: any[]) => any][], emit: <T>(event: string, payload: T) => void }
const connections = new Map<string, Connection>()

function attachClient(props: Props): WebSocketBehavior {
return {
upgrade(res, req, context) {
const id = getUID()

res.upgrade(
{ id, req: <Request>{
...req,
socket: { ip: arrayBufferToString(res.getRemoteAddressAsText()) }
}},
req.getHeader('sec-websocket-key'),
req.getHeader('sec-websocket-protocol'),
req.getHeader('sec-websocket-extensions'),
context
)
},
open: (ws: WebSocket) => {
const { id, req } = ws
const listeners: Connection['listeners'] = []
const connection: Connection = {
listeners,
emit(event, payload) {
listeners
.filter(e => e[0] === event)
.forEach(e => e[1](payload))
},
addEventListener(event, handler) {
listeners.push([event, handler])
},
removeEventListener(event, handler) {
const listenersToRemove = [...listeners].filter(e => e[0] === event && e[1] === handler)

listenersToRemove.forEach(item => {
const index = listeners.indexOf(item)

if (index >= 0) listeners.splice(index, 1)
})
},
readyState: WebSocketState.OPEN,
send(data) {
ws.send(data)
}
}
const attachment = attach(actions, connection, req, http)

connections.set(id, connection)
props.open(ws, attachment)
},
message: (ws, message) => {
const id: string = ws.id
const connection = connections.get(id)
const data = arrayBufferToString(message)

connection?.emit('message', { data })
},
close: (ws, code, message) => {
const id: string = ws.id
const connection = connections.get(id)
if (connection) connection.readyState = WebSocketState.CLOSED

connection?.emit('close', { code, reson: message, wasClean: true })
connections.delete(id)
}
}
}

return {
attachClient,
httpTunnel: http.tunnel
}
}
17 changes: 17 additions & 0 deletions examples/server/uws/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import uWS from 'uWebSockets.js'
import basic from './basic'

const app = uWS.App()

app.ws('/uws/basic/', basic.attachClient({ open() {}}))
app.any('/uws/basic/', basic.httpTunnel)

const port = 9001;

app.listen(port, (token) => {
if (token) {
console.log('Listening to port ' + port);
} else {
console.log('Failed to listen to port ' + port);
}
});
5 changes: 5 additions & 0 deletions examples/shared/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ export interface BasicActions {
emptyResponse2: OperatorFunction<{ value: number }, void>
}

export interface BasicActionsUWS {
getData: GetData
getIP: OperatorFunction<void, { ip: string }>
}

export interface CookieActions {
sendCookie: OperatorFunction<{ value: Cookie }, { value: string | null, ok?: boolean }>
setCookie: OperatorFunction<void, { newCookie: Cookie, newCookie2: Cookie }>
Expand Down
4 changes: 4 additions & 0 deletions examples/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ module.exports = {
target: 'ws://localhost:3000',
ws: true
},
'/uws': {
target: 'ws://localhost:9001',
ws: true
},
},
},
module: {
Expand Down

0 comments on commit 96a8038

Please sign in to comment.