velocious 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/dependabot.yml +9 -0
- package/bin/velocious +14 -0
- package/index.cjs +11 -0
- package/package.json +29 -0
- package/peak_flow.yml +17 -0
- package/spec/database/connection/drivers/mysql/query-parser-spec.cjs +34 -0
- package/spec/dummy/config/databases.example.json +10 -0
- package/spec/dummy/config/databases.json +0 -0
- package/spec/dummy/config/databases.peakflow.json +11 -0
- package/spec/dummy/index.cjs +40 -0
- package/spec/dummy/src/models/task.cjs +4 -0
- package/spec/dummy/src/routes/tasks/controller.cjs +18 -0
- package/spec/dummy/src/routes/tasks/index.ejs +1 -0
- package/spec/dummy/src/routes/tasks/show.ejs +0 -0
- package/spec/dummy/src/routes.cjs +10 -0
- package/spec/http-server/client-spec.cjs +35 -0
- package/spec/http-server/get-spec.cjs +13 -0
- package/spec/support/jasmine.json +11 -0
- package/src/application.cjs +36 -0
- package/src/configuration.cjs +14 -0
- package/src/controller.cjs +50 -0
- package/src/database/connection/drivers/mysql/index.cjs +5 -0
- package/src/database/connection/drivers/mysql/options.cjs +7 -0
- package/src/database/connection/drivers/mysql/query-parser.cjs +26 -0
- package/src/database/connection/index.cjs +2 -0
- package/src/database/handler.cjs +5 -0
- package/src/database/index.cjs +9 -0
- package/src/database/pool/index.cjs +2 -0
- package/src/database/query/from-base.cjs +15 -0
- package/src/database/query/from-plain.cjs +12 -0
- package/src/database/query/from-table.cjs +12 -0
- package/src/database/query/index.cjs +59 -0
- package/src/database/query/join-base.cjs +15 -0
- package/src/database/query/join-plain.cjs +12 -0
- package/src/database/query/select-base.cjs +15 -0
- package/src/database/query/select-plain.cjs +12 -0
- package/src/database/query/select-table-and-column.cjs +21 -0
- package/src/database/query-parser/from-parser.cjs +35 -0
- package/src/database/query-parser/joins-parser.cjs +33 -0
- package/src/database/query-parser/options.cjs +20 -0
- package/src/database/query-parser/select-parser.cjs +42 -0
- package/src/database/record/index.cjs +5 -0
- package/src/error-logger.js +18 -0
- package/src/http-server/client/index.cjs +75 -0
- package/src/http-server/client/request-parser.cjs +92 -0
- package/src/http-server/client/request-runner.cjs +32 -0
- package/src/http-server/client/request.cjs +25 -0
- package/src/http-server/client/response.cjs +28 -0
- package/src/http-server/index.cjs +78 -0
- package/src/http-server/worker-handler/index.cjs +78 -0
- package/src/http-server/worker-handler/socket-handler.cjs +35 -0
- package/src/http-server/worker-handler/worker-script.cjs +4 -0
- package/src/http-server/worker-handler/worker-thread.cjs +49 -0
- package/src/logger.cjs +11 -0
- package/src/routes/base-route.cjs +25 -0
- package/src/routes/get-route.cjs +18 -0
- package/src/routes/index.cjs +9 -0
- package/src/routes/resolver.cjs +61 -0
- package/src/routes/resource-route.cjs +39 -0
- package/src/routes/root-route.cjs +4 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const {digs} = require("diggerize")
|
|
2
|
+
|
|
3
|
+
module.exports = class VelociousDatabaseQueryParserFromParser {
|
|
4
|
+
constructor({pretty, query, queryParserOptions}) {
|
|
5
|
+
this.pretty = pretty
|
|
6
|
+
this.query = query
|
|
7
|
+
this.queryParserOptions = queryParserOptions
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
toSql() {
|
|
11
|
+
const {pretty, query} = digs(this, "pretty", "query")
|
|
12
|
+
|
|
13
|
+
let sql = " FROM"
|
|
14
|
+
|
|
15
|
+
for (const fromKey in query._froms) {
|
|
16
|
+
const from = query._froms[fromKey]
|
|
17
|
+
|
|
18
|
+
from.setOptions(this.queryParserOptions)
|
|
19
|
+
|
|
20
|
+
if (fromKey > 0) {
|
|
21
|
+
sql += ","
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (pretty) {
|
|
25
|
+
sql += "\n "
|
|
26
|
+
} else {
|
|
27
|
+
sql += " "
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
sql += from.toSql()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return sql
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const {digs} = require("diggerize")
|
|
2
|
+
|
|
3
|
+
module.exports = class VelocuiousDatabaseQueryParserJoinsParser {
|
|
4
|
+
constructor({pretty, query, queryParserOptions}) {
|
|
5
|
+
this.pretty = pretty
|
|
6
|
+
this.query = query
|
|
7
|
+
this.queryParserOptions = queryParserOptions
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
toSql() {
|
|
11
|
+
const {pretty, query} = digs(this, "pretty", "query")
|
|
12
|
+
|
|
13
|
+
let sql = ""
|
|
14
|
+
|
|
15
|
+
for (const joinKey in query._joins) {
|
|
16
|
+
const join = query._joins[joinKey]
|
|
17
|
+
|
|
18
|
+
join.setOptions(this.queryParserOptions)
|
|
19
|
+
|
|
20
|
+
if (joinKey == 0) {
|
|
21
|
+
if (pretty) {
|
|
22
|
+
sql += "\n\n"
|
|
23
|
+
} else {
|
|
24
|
+
sql += " "
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
sql += join.toSql()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return sql
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const {digg} = require("diggerize")
|
|
2
|
+
|
|
3
|
+
module.exports = class VelociousDatabaseQueryParserOptions {
|
|
4
|
+
constructor(options) {
|
|
5
|
+
this.columnQuote = digg(options, "columnQuote")
|
|
6
|
+
this.tableQuote = digg(options, "tableQuote")
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
quoteColumnName(columnName) {
|
|
10
|
+
if (columnName.includes(this.columnQuote)) throw new Error(`Invalid column name: ${columnName}`)
|
|
11
|
+
|
|
12
|
+
return `${this.columnQuote}${columnName}${this.columnQuote}`
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
quoteTableName(tableName) {
|
|
16
|
+
if (tableName.includes(this.tableQuote)) throw new Error(`Invalid table name: ${tableName}`)
|
|
17
|
+
|
|
18
|
+
return `${this.tableQuote}${tableName}${this.tableQuote}`
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const {digs} = require("diggerize")
|
|
2
|
+
|
|
3
|
+
module.exports = class VelociousDatabaseQueryParserSelectParser {
|
|
4
|
+
constructor({pretty, query, queryParserOptions}) {
|
|
5
|
+
this.pretty = pretty
|
|
6
|
+
this.query = query
|
|
7
|
+
this.queryParserOptions = queryParserOptions
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
toSql() {
|
|
11
|
+
const {pretty, query} = digs(this, "pretty", "query")
|
|
12
|
+
|
|
13
|
+
let sql = ""
|
|
14
|
+
|
|
15
|
+
sql += "SELECT"
|
|
16
|
+
|
|
17
|
+
if (pretty) {
|
|
18
|
+
sql += "\n"
|
|
19
|
+
} else {
|
|
20
|
+
sql += " "
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
for (const selectKey in query._selects) {
|
|
24
|
+
const selectValue = query._selects[selectKey]
|
|
25
|
+
|
|
26
|
+
selectValue.setOptions(this.queryParserOptions)
|
|
27
|
+
|
|
28
|
+
sql += selectValue.toSql()
|
|
29
|
+
|
|
30
|
+
if (selectKey + 1 < query._selects.length) {
|
|
31
|
+
if (pretty) {
|
|
32
|
+
sql += ","
|
|
33
|
+
sql += " "
|
|
34
|
+
} else {
|
|
35
|
+
sql += ", "
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return sql
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module.exports = function errorLogger(callback) {
|
|
2
|
+
return async function(...args) {
|
|
3
|
+
try {
|
|
4
|
+
await callback(...args)
|
|
5
|
+
} catch (error) {
|
|
6
|
+
console.error(`ErrorLogger: ${error.message}`)
|
|
7
|
+
|
|
8
|
+
if (error.stack) {
|
|
9
|
+
console.error("Stack", error.stack)
|
|
10
|
+
} else {
|
|
11
|
+
console.error("No stack")
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Give console some time to write out messages before crashing
|
|
15
|
+
setTimeout(() => { throw error })
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const Configuration = require("../../configuration.cjs")
|
|
2
|
+
const {digg} = require("diggerize")
|
|
3
|
+
const {EventEmitter} = require("events")
|
|
4
|
+
const logger = require("../../logger.cjs")
|
|
5
|
+
const Request = require("./request.cjs")
|
|
6
|
+
const RequestRunner = require("./request-runner.cjs")
|
|
7
|
+
|
|
8
|
+
module.exports = class VeoliciousHttpServerClient {
|
|
9
|
+
events = new EventEmitter()
|
|
10
|
+
state = "initial"
|
|
11
|
+
|
|
12
|
+
constructor({clientCount, configuration, onExecuteRequest}) {
|
|
13
|
+
if (!configuration) throw new Error("No configuration given")
|
|
14
|
+
|
|
15
|
+
this.clientCount = clientCount
|
|
16
|
+
this.configuration = configuration
|
|
17
|
+
this.onExecuteRequest = onExecuteRequest
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
executeCurrentRequest() {
|
|
21
|
+
logger(this, "executeCurrentRequest")
|
|
22
|
+
|
|
23
|
+
this.state = "response"
|
|
24
|
+
|
|
25
|
+
const requestRunner = new RequestRunner({
|
|
26
|
+
configuration: this.configuration,
|
|
27
|
+
request: this.currentRequest,
|
|
28
|
+
routes: this.routes
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
requestRunner.events.on("done", (requestRunner) => this.sendResponse(requestRunner))
|
|
32
|
+
requestRunner.run()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
onWrite(data) {
|
|
36
|
+
if (this.state == "initial") {
|
|
37
|
+
this.currentRequest = new Request({
|
|
38
|
+
configuration: this.configuration
|
|
39
|
+
})
|
|
40
|
+
this.currentRequest.requestParser.events.on("done", () => this.executeCurrentRequest())
|
|
41
|
+
this.currentRequest.feed(data)
|
|
42
|
+
this.state = "requestStarted"
|
|
43
|
+
} else if (this.state == "requestStarted") {
|
|
44
|
+
this.currentRequest.feed(data)
|
|
45
|
+
} else {
|
|
46
|
+
throw new Error(`Unknown state: ${this.state}`)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
sendResponse(requestRunner) {
|
|
51
|
+
const response = digg(requestRunner, "response")
|
|
52
|
+
const body = response.getBody()
|
|
53
|
+
const date = new Date()
|
|
54
|
+
|
|
55
|
+
response.addHeader("Connection", "keep-alive")
|
|
56
|
+
response.addHeader("Content-Length", response.body.length)
|
|
57
|
+
response.addHeader("Date", date.toUTCString())
|
|
58
|
+
response.addHeader("Server", "Velocious")
|
|
59
|
+
|
|
60
|
+
let headers = ""
|
|
61
|
+
|
|
62
|
+
headers += "HTTP/1.1 200 OK\r\n"
|
|
63
|
+
|
|
64
|
+
for (const headerKey in response.headers) {
|
|
65
|
+
for (const headerValue of response.headers[headerKey]) {
|
|
66
|
+
headers += `${headerKey}: ${headerValue}\r\n`
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
headers += "\r\n"
|
|
71
|
+
|
|
72
|
+
this.events.emit("output", headers)
|
|
73
|
+
this.events.emit("output", body)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const {EventEmitter} = require("events")
|
|
2
|
+
const logger = require("../../logger.cjs")
|
|
3
|
+
|
|
4
|
+
module.exports = class VelociousHttpServerClientRequestParser {
|
|
5
|
+
constructor({configuration}) {
|
|
6
|
+
if (!configuration) throw new Error("No configuration given")
|
|
7
|
+
|
|
8
|
+
this.configuration = configuration
|
|
9
|
+
this.data = []
|
|
10
|
+
this.events = new EventEmitter()
|
|
11
|
+
this.headers = []
|
|
12
|
+
this.headersByName = {}
|
|
13
|
+
this.state = "status"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
addHeader(name, value) {
|
|
17
|
+
logger(this, "addHeader", {name, value})
|
|
18
|
+
|
|
19
|
+
this.headers.push({name, value})
|
|
20
|
+
|
|
21
|
+
const formattedName = name.toLowerCase().trim()
|
|
22
|
+
|
|
23
|
+
this.headersByName[formattedName] = value
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
feed(data) {
|
|
27
|
+
if (this.state == "status" || this.state == "headers") {
|
|
28
|
+
for (const char of data) {
|
|
29
|
+
this.data.push(char)
|
|
30
|
+
|
|
31
|
+
if (char == 10) {
|
|
32
|
+
const line = String.fromCharCode.apply(null, this.data)
|
|
33
|
+
|
|
34
|
+
this.data = []
|
|
35
|
+
this.parse(line)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
matchAndRemove(regex) {
|
|
42
|
+
const match = this.data.match(regex)
|
|
43
|
+
|
|
44
|
+
if (!match) {
|
|
45
|
+
return null
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
this.data = this.data.replace(regex, "")
|
|
49
|
+
|
|
50
|
+
return match
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
parse(line) {
|
|
54
|
+
if (this.state == "status") {
|
|
55
|
+
this.parseStatusLine(line)
|
|
56
|
+
} else if (this.state == "headers") {
|
|
57
|
+
this.parseHeader(line)
|
|
58
|
+
} else {
|
|
59
|
+
throw new Error(`Unknown state: ${this.state}`)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
parseHeader(line) {
|
|
64
|
+
let match
|
|
65
|
+
|
|
66
|
+
if (match = line.match(/^(.+): (.+)\r\n/)) {
|
|
67
|
+
const name = match[1]
|
|
68
|
+
const value = match[2]
|
|
69
|
+
|
|
70
|
+
this.addHeader(name, value)
|
|
71
|
+
} else if (line == "\r\n") {
|
|
72
|
+
if (this.httpMethod.toUpperCase() == "GET") {
|
|
73
|
+
this.state = "done"
|
|
74
|
+
this.events.emit("done")
|
|
75
|
+
} else {
|
|
76
|
+
throw new Error(`Unknown HTTP method: ${this.httpMethod}`)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
parseStatusLine(line) {
|
|
82
|
+
const match = line.match(/^(GET|POST) (.+?) HTTP\/1\.1\r\n/)
|
|
83
|
+
|
|
84
|
+
if (!match) {
|
|
85
|
+
throw new Error(`Couldn't match status line from: ${line}`)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
this.httpMethod = match[1]
|
|
89
|
+
this.path = match[2]
|
|
90
|
+
this.state = "headers"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const EventEmitter = require("events")
|
|
2
|
+
const logger = require("../../logger.cjs")
|
|
3
|
+
const Response = require("./response.cjs")
|
|
4
|
+
const RoutesResolver = require("../../routes/resolver.cjs")
|
|
5
|
+
|
|
6
|
+
module.exports = class VelociousHttpServerClientRequestRunner {
|
|
7
|
+
events = new EventEmitter()
|
|
8
|
+
|
|
9
|
+
constructor({configuration, request}) {
|
|
10
|
+
if (!configuration) throw new Error("No configuration given")
|
|
11
|
+
if (!request) throw new Error("No request given")
|
|
12
|
+
|
|
13
|
+
this.configuration = configuration
|
|
14
|
+
this.request = request
|
|
15
|
+
this.response = new Response({configuration})
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async run() {
|
|
19
|
+
if (!this.request) throw new Error("No request?")
|
|
20
|
+
|
|
21
|
+
logger(this, "Run request")
|
|
22
|
+
|
|
23
|
+
const routesResolver = new RoutesResolver({
|
|
24
|
+
configuration: this.configuration,
|
|
25
|
+
request: this.request,
|
|
26
|
+
response: this.response
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
await routesResolver.resolve()
|
|
30
|
+
this.events.emit("done", this)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const {digg} = require("diggerize")
|
|
2
|
+
const RequestParser = require("./request-parser.cjs")
|
|
3
|
+
|
|
4
|
+
module.exports = class VelociousHttpServerClientRequest {
|
|
5
|
+
constructor({configuration}) {
|
|
6
|
+
this.configuration = configuration
|
|
7
|
+
this.requestParser = new RequestParser({configuration})
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
feed(data) {
|
|
11
|
+
this.requestParser.feed(data)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
httpMethod() {
|
|
15
|
+
return digg(this, "requestParser", "httpMethod")
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
host() {
|
|
19
|
+
return digg(this, "requestParser", "headersByName", "host")
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
path() {
|
|
23
|
+
return digg(this, "requestParser", "path")
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module.exports = class VelociousHttpServerClientResponse {
|
|
2
|
+
body = undefined
|
|
3
|
+
headers = {}
|
|
4
|
+
|
|
5
|
+
constructor({configuration}) {
|
|
6
|
+
this.configuration = configuration
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
addHeader(key, value) {
|
|
10
|
+
if (!(key in this.headers)) {
|
|
11
|
+
this.headers[key] = []
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
this.headers[key].push(value)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
getBody() {
|
|
18
|
+
if (this.body) {
|
|
19
|
+
return this.body
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
throw new Error("No body has been set")
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
setBody(value) {
|
|
26
|
+
this.body = value
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const logger = require("../logger.cjs")
|
|
2
|
+
const Net = require("net")
|
|
3
|
+
const WorkerHandler = require("./worker-handler/index.cjs")
|
|
4
|
+
|
|
5
|
+
module.exports = class VelociousHttpServer {
|
|
6
|
+
constructor({configuration, host, maxWorkers, port}) {
|
|
7
|
+
this.configuration = configuration
|
|
8
|
+
this.host = host
|
|
9
|
+
this.port = port
|
|
10
|
+
this.clientCount = 0
|
|
11
|
+
this.maxWorkers = maxWorkers || 16
|
|
12
|
+
this.workerCount = 0
|
|
13
|
+
this.workerHandlers = []
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async start() {
|
|
17
|
+
// We need at least one worker to handle requests
|
|
18
|
+
if (this.workerHandlers.length == 0) {
|
|
19
|
+
await this.spawnWorker()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
this.netServer = new Net.Server()
|
|
23
|
+
this.netServer.on("connection", (socket) => this.onConnection(socket))
|
|
24
|
+
this.netServer.listen(this.port, () => {
|
|
25
|
+
logger(this, `Velocious listening on ${this.host}:${this.port}`)
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
stop() {
|
|
30
|
+
return new Promise((resolve) => {
|
|
31
|
+
this.netServer?.close(() => resolve())
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
onConnection(socket) {
|
|
36
|
+
const clientCount = this.clientCount
|
|
37
|
+
|
|
38
|
+
logger(this, `New client ${clientCount}`)
|
|
39
|
+
|
|
40
|
+
this.clientCount++
|
|
41
|
+
|
|
42
|
+
const workerHandler = this.workerHandlerToUse()
|
|
43
|
+
|
|
44
|
+
logger(this, `Gave client ${clientCount} to worker ${workerHandler.workerCount}`)
|
|
45
|
+
|
|
46
|
+
workerHandler.addSocketConnection({
|
|
47
|
+
socket,
|
|
48
|
+
clientCount: this.clientCount
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async spawnWorker() {
|
|
53
|
+
const workerCount = this.workerCount
|
|
54
|
+
|
|
55
|
+
this.workerCount++
|
|
56
|
+
|
|
57
|
+
const workerHandler = new WorkerHandler({
|
|
58
|
+
configuration: this.configuration,
|
|
59
|
+
workerCount
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
await workerHandler.start()
|
|
63
|
+
this.workerHandlers.push(workerHandler)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
workerHandlerToUse() {
|
|
67
|
+
logger(this, `Worker handlers length: ${this.workerHandlers.length}`)
|
|
68
|
+
|
|
69
|
+
const randomWorkerNumber = parseInt(Math.random() * this.workerHandlers.length)
|
|
70
|
+
const workerHandler = this.workerHandlers[randomWorkerNumber]
|
|
71
|
+
|
|
72
|
+
if (!workerHandler) {
|
|
73
|
+
throw new Error(`No workerHandler by that number: ${randomWorkerNumber}`)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return workerHandler
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const {digs} = require("diggerize")
|
|
2
|
+
const logger = require("../../logger.cjs")
|
|
3
|
+
const SocketHandler = require("./socket-handler.cjs")
|
|
4
|
+
const {Worker} = require("worker_threads")
|
|
5
|
+
|
|
6
|
+
module.exports = class VelociousHttpServerWorker {
|
|
7
|
+
constructor({configuration, workerCount}) {
|
|
8
|
+
this.configuration = configuration
|
|
9
|
+
this.socketHandlers = {}
|
|
10
|
+
this.workerCount = workerCount
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async start() {
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
const {debug, directory} = digs(this.configuration, "debug", "directory")
|
|
16
|
+
|
|
17
|
+
this.onStartCallback = resolve
|
|
18
|
+
this.worker = new Worker("./src/http-server/worker-handler/worker-script.cjs", {
|
|
19
|
+
workerData: {
|
|
20
|
+
debug,
|
|
21
|
+
directory,
|
|
22
|
+
workerCount: this.workerCount
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
this.worker.on("error", (error) => this.onWorkerError(error))
|
|
26
|
+
this.worker.on("exit", (code) => this.onWorkerExit(code))
|
|
27
|
+
this.worker.on("message", (message) => this.onWorkerMessage(message))
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
addSocketConnection({socket, clientCount}) {
|
|
32
|
+
socket.on("end", () => {
|
|
33
|
+
logger(this, `Removing ${clientCount} from socketHandlers`)
|
|
34
|
+
delete this.socketHandlers[clientCount]
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const socketHandler = new SocketHandler({
|
|
38
|
+
configuration: this.configuration,
|
|
39
|
+
socket,
|
|
40
|
+
clientCount,
|
|
41
|
+
worker: this.worker
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
this.socketHandlers[clientCount] = socketHandler
|
|
45
|
+
this.worker.postMessage({command: "newClient", clientCount})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
onWorkerError(error) {
|
|
49
|
+
throw new Error(`Worker error: ${error}`)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
onWorkerExit(code) {
|
|
53
|
+
if (code !== 0) {
|
|
54
|
+
throw new Error(`Client worker stopped with exit code ${code}`)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
onWorkerMessage(data) {
|
|
59
|
+
logger(this, `Worker message`, data)
|
|
60
|
+
|
|
61
|
+
const {command} = digs(data, "command")
|
|
62
|
+
|
|
63
|
+
if (command == "started") {
|
|
64
|
+
this.onStartCallback()
|
|
65
|
+
this.onStartCallback = null
|
|
66
|
+
} else if (command == "clientOutput") {
|
|
67
|
+
logger(this, "CLIENT OUTPUT", data)
|
|
68
|
+
|
|
69
|
+
const {clientCount, output} = digs(data, "clientCount", "output")
|
|
70
|
+
|
|
71
|
+
logger(this, "CLIENT OUTPUT", data)
|
|
72
|
+
|
|
73
|
+
this.socketHandlers[clientCount].send(output)
|
|
74
|
+
} else {
|
|
75
|
+
throw new Error(`Unknown command: ${command}`)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const logger = require("../../logger.cjs")
|
|
2
|
+
|
|
3
|
+
module.exports = class VelociousHttpServerWorkerHandlerSocketHandler {
|
|
4
|
+
constructor({configuration, socket, clientCount, worker}) {
|
|
5
|
+
if (!configuration) throw new Error("No configuration given")
|
|
6
|
+
|
|
7
|
+
this.configuration = configuration
|
|
8
|
+
this.socket = socket
|
|
9
|
+
this.clientCount = clientCount
|
|
10
|
+
this.worker = worker
|
|
11
|
+
|
|
12
|
+
socket.on("data", (chunk) => this.onSocketData(chunk))
|
|
13
|
+
socket.on("end", () => this.onSocketEnd())
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
onSocketData(chunk) {
|
|
17
|
+
logger(this, `Socket ${this.clientCount}: ${chunk}`)
|
|
18
|
+
|
|
19
|
+
this.worker.postMessage({
|
|
20
|
+
command: "clientWrite",
|
|
21
|
+
chunk,
|
|
22
|
+
clientCount: this.clientCount
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
onSocketEnd() {
|
|
27
|
+
logger(this, `Socket ${this.clientCount} end`)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
send(data) {
|
|
31
|
+
logger(this, "Send", data)
|
|
32
|
+
|
|
33
|
+
this.socket.write(data)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const Client = require("../client/index.cjs")
|
|
2
|
+
const Configuration = require("../../configuration.cjs")
|
|
3
|
+
const {digg, digs} = require("diggerize")
|
|
4
|
+
const errorLogger = require("../../error-logger")
|
|
5
|
+
const logger = require("../../logger.cjs")
|
|
6
|
+
|
|
7
|
+
module.exports = class VelociousHttpServerWorkerHandlerWorkerThread {
|
|
8
|
+
constructor({parentPort, workerData}) {
|
|
9
|
+
const {debug, directory, workerCount} = digs(workerData, "debug", "directory", "workerCount")
|
|
10
|
+
|
|
11
|
+
this.clients = {}
|
|
12
|
+
this.configuration = new Configuration({debug, directory})
|
|
13
|
+
this.parentPort = parentPort
|
|
14
|
+
this.workerCount = workerCount
|
|
15
|
+
|
|
16
|
+
parentPort.on("message", errorLogger((data) => this.onCommand(data)))
|
|
17
|
+
|
|
18
|
+
logger(this, `Worker ${workerCount} started`)
|
|
19
|
+
|
|
20
|
+
parentPort.postMessage({command: "started"})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
onCommand(data) {
|
|
24
|
+
logger(this, `Worker ${this.workerCount} received command`, data)
|
|
25
|
+
|
|
26
|
+
const {command} = data
|
|
27
|
+
|
|
28
|
+
if (command == "newClient") {
|
|
29
|
+
const {clientCount} = digs(data, "clientCount")
|
|
30
|
+
const client = new Client({
|
|
31
|
+
clientCount,
|
|
32
|
+
configuration: this.configuration
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
client.events.on("output", (output) => {
|
|
36
|
+
this.parentPort.postMessage({command: "clientOutput", clientCount, output})
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
this.clients[clientCount] = client
|
|
40
|
+
} else if (command == "clientWrite") {
|
|
41
|
+
const {chunk, clientCount} = digs(data, "chunk", "clientCount")
|
|
42
|
+
const client = digg(this.clients, clientCount)
|
|
43
|
+
|
|
44
|
+
client.onWrite(chunk)
|
|
45
|
+
} else {
|
|
46
|
+
throw new Error(`Unknown command: ${command}`)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/logger.cjs
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const {digg} = require("diggerize")
|
|
2
|
+
|
|
3
|
+
module.exports = function log(object, ...messages) {
|
|
4
|
+
if (!object.configuration) console.error(`No configuration on ${object.constructor.name}`)
|
|
5
|
+
|
|
6
|
+
if (object.configuration?.debug) {
|
|
7
|
+
const className = digg(object, "constructor", "name")
|
|
8
|
+
|
|
9
|
+
console.log(className, ...messages)
|
|
10
|
+
}
|
|
11
|
+
}
|