-
-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
659 additions
and
801 deletions.
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
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,153 @@ | ||
import { wLogger } from '@tosu/common'; | ||
import http, { IncomingMessage, ServerResponse } from 'http'; | ||
|
||
import { getContentType, sendJson } from '../utils/index'; | ||
|
||
interface ExtendedIncomingMessage extends IncomingMessage { | ||
instanceManager: any; | ||
query: { [key: string]: string }; | ||
params: { [key: string]: string }; | ||
getContentType: (text: string) => string; | ||
sendJson: ( | ||
response: http.ServerResponse, | ||
json: object | any[] | ||
) => http.ServerResponse<http.IncomingMessage>; | ||
} | ||
|
||
type RequestHandler = ( | ||
req: ExtendedIncomingMessage, | ||
res: http.ServerResponse, | ||
next: () => void | ||
) => void; | ||
|
||
type RouteHandler = { | ||
(req: ExtendedIncomingMessage, res: ServerResponse): void; | ||
}; | ||
|
||
export class HttpServer { | ||
private middlewares: RequestHandler[] = []; | ||
server: http.Server; | ||
private routes: { | ||
[method: string]: [ | ||
{ | ||
path: string | RegExp; | ||
handler: RouteHandler; | ||
} | ||
]; | ||
} = {}; | ||
|
||
constructor() { | ||
// @ts-ignore | ||
this.server = http.createServer(this.handleRequest.bind(this)); | ||
} | ||
|
||
use(middleware: RequestHandler) { | ||
this.middlewares.push(middleware); | ||
} | ||
|
||
route( | ||
path: string | RegExp, | ||
method: | ||
| 'GET' | ||
| 'POST' | ||
| 'HEAD' | ||
| 'PUT' | ||
| 'DELETE' | ||
| 'CONNECT' | ||
| 'OPTIONS' | ||
| 'TRACE' | ||
| 'PATCH', | ||
handler: RouteHandler | ||
) { | ||
// @ts-ignore | ||
if (this.routes[method] == null) this.routes[method] = []; | ||
|
||
// @ts-ignore | ||
const find = this.routes[method].find((r) => r.path == path); | ||
if (!find) this.routes[method].push({ path, handler }); | ||
} | ||
|
||
private handleRequest( | ||
req: ExtendedIncomingMessage, | ||
res: http.ServerResponse | ||
) { | ||
const startTime = performance.now(); | ||
let index = 0; | ||
|
||
res.on('finish', () => { | ||
const finishTime = performance.now(); | ||
wLogger.debug( | ||
`[${(finishTime - startTime).toFixed(2)}ms] ${req.method} ${ | ||
res.statusCode | ||
} ${res.getHeader('content-type')} ${req.url}` | ||
); | ||
}); | ||
|
||
const next = () => { | ||
if (index < this.middlewares.length) { | ||
const middleware = this.middlewares[index++]; | ||
middleware(req, res, next); | ||
|
||
return; | ||
} | ||
|
||
this.handleNext(req, res); | ||
}; | ||
|
||
next(); | ||
} | ||
|
||
private handleNext(req: ExtendedIncomingMessage, res: http.ServerResponse) { | ||
const method = req.method || 'GET'; | ||
const hostname = req.headers.host; // Hostname | ||
const url = req.url || '/'; // URL | ||
|
||
const parsedURL = new URL(`http://${hostname}${req.url}`); | ||
|
||
// parse query parameters | ||
req.query = {}; | ||
req.params = {}; | ||
|
||
// add functions (thats probably shittiest way to do so, but i want to try it) | ||
req.getContentType = getContentType; | ||
req.sendJson = sendJson; | ||
|
||
parsedURL.searchParams.forEach( | ||
(value, key) => (req.query[key] = value) | ||
); | ||
|
||
const routes = this.routes[method] || []; | ||
for (let i = 0; i < routes.length; i++) { | ||
const route = routes[i]; | ||
let routeExists = false; | ||
|
||
if (route.path instanceof RegExp && route.path.test(url)) { | ||
routeExists = true; | ||
|
||
// turn groups to route params | ||
const array = Object.keys(route.path.exec(url)?.groups || {}); | ||
for (let g = 0; g < array.length; g++) { | ||
const key = array[g]; | ||
const value = route.path.exec(url)?.groups?.[key]; | ||
|
||
if (key == null || value == null) continue; | ||
req.params[key] = value; | ||
} | ||
} else if (typeof route.path == 'string') | ||
routeExists = route.path == url; | ||
|
||
if (!routeExists) continue; | ||
return route.handler(req, res); | ||
} | ||
|
||
res.statusCode = 404; | ||
res.end('Not Found'); | ||
return; | ||
} | ||
|
||
listen(port: number, hostname: string) { | ||
this.server.listen(port, hostname, () => { | ||
wLogger.info(`Web server started on http://${hostname}:${port}`); | ||
}); | ||
} | ||
} |
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,3 @@ | ||
export * from './utils/index'; | ||
export * from './http/index'; | ||
export * from './socket/index'; |
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,19 @@ | ||
{ | ||
"name": "@tosu/server", | ||
"version": "0.0.1", | ||
"description": "", | ||
"author": "cyperdark", | ||
"main": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
"scripts": { | ||
"prepare": "npm run build", | ||
"build": "tsc" | ||
}, | ||
"dependencies": { | ||
"@tosu/common": "workspace:*", | ||
"ws": "^8.16.0" | ||
}, | ||
"devDependencies": { | ||
"@types/ws": "^8.5.10" | ||
} | ||
} |
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,38 @@ | ||
import { sleep, wLogger } from '@tosu/common'; | ||
import { config } from '@tosu/common/dist/config'; | ||
import WebSocket from 'ws'; | ||
|
||
export const WebSocketV1 = (instancesManager: any) => { | ||
const wss = new WebSocket.Server({ noServer: true }); | ||
wss.on('connection', async (ws) => { | ||
wLogger.debug('>>> ws: CONNECTED'); | ||
let isSocketConnected = true; | ||
|
||
ws.on('close', function (reasonCode, description) { | ||
isSocketConnected = false; | ||
wLogger.debug('>>> ws: CLOSED'); | ||
}); | ||
|
||
ws.on('error', function (reasonCode, description) { | ||
isSocketConnected = false; | ||
wLogger.debug(`>>> ws: error: ${reasonCode} [${description}]`); | ||
}); | ||
|
||
while (isSocketConnected) { | ||
const osuInstances: any = Object.values( | ||
instancesManager.osuInstances || {} | ||
); | ||
if (osuInstances.length < 1) { | ||
await sleep(500); | ||
continue; | ||
} | ||
|
||
ws.send(JSON.stringify(osuInstances[0].getState(instancesManager))); | ||
await sleep(config.wsSendInterval); | ||
} | ||
}); | ||
|
||
return wss; | ||
}; | ||
|
||
export { WebSocket }; |
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,27 @@ | ||
{ | ||
"compilerOptions": { | ||
"lib": [ | ||
"ES2020" | ||
], | ||
"module": "commonjs", | ||
"moduleResolution": "Node", | ||
"allowJs": true, | ||
"esModuleInterop": true, | ||
"outDir": "dist", | ||
"rootDir": "./", | ||
"sourceMap": false, | ||
"declaration": false, | ||
"strict": true, | ||
"noImplicitAny": false, | ||
"target": "ES2020", | ||
"strictPropertyInitialization": false, | ||
"baseUrl": ".", | ||
}, | ||
"exclude": [ | ||
"node_modules", | ||
"dist", | ||
], | ||
"include": [ | ||
"**/*" | ||
], | ||
} |
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,37 @@ | ||
import http from 'http'; | ||
import path from 'path'; | ||
|
||
export function getContentType(text: string) { | ||
const extension = path.extname(text); | ||
|
||
const contentType = | ||
{ | ||
'.html': 'text/html', | ||
'.js': 'text/javascript', | ||
'.css': 'text/css', | ||
'.json': 'application/json', | ||
'.png': 'image/png', | ||
'.jpg': 'image/jpg', | ||
'.gif': 'image/gif', | ||
'.svg': 'image/svg+xml', | ||
'.wav': 'audio/wav', | ||
'.mp4': 'video/mp4', | ||
'.woff': 'application/font-woff', | ||
'.ttf': 'application/font-ttf', | ||
'.eot': 'application/vnd.ms-fontobject', | ||
'.otf': 'application/font-otf', | ||
'.wasm': 'application/wasm' | ||
}[extension] || 'application/octet-stream'; | ||
|
||
return contentType; | ||
} | ||
|
||
export function sendJson(response: http.ServerResponse, json: object | any[]) { | ||
response.setHeader('Content-Type', 'application/json'); | ||
|
||
try { | ||
return response.end(JSON.stringify(json)); | ||
} catch (error) { | ||
return response.end(JSON.stringify({ error: 'Json parsing error' })); | ||
} | ||
} |
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 was deleted.
Oops, something went wrong.
Oops, something went wrong.