config tests added,

titles tests added,
.env file support added with dotenv,
README.md file updated for .evn file support,
database lost connection bug crash fixed,
session server run_session long timeout bug fixed,
links module added,
webrtc_proc json arguments added,
app_proc stdout and stderr support added,
infinity app_proc spawn freeze bug fixed
This commit is contained in:
C
2023-01-30 17:55:15 +05:00
parent e3e2cc97aa
commit dae22f393c
14 changed files with 3222 additions and 26 deletions
+12 -1
View File
@@ -1,3 +1,14 @@
# pixel-streaming-session-server
pixel streaming session server
pixel streaming session server
configure environment variables with .env file
.env:
PORT=
EXTERNAL_DOMAIN=""
COORDINATOR_URL=""
DATABASE_URL=""
DATABASE_NAME=""
WEBRTC_PORT_BEGIN=
+12 -1
View File
@@ -6,6 +6,18 @@ const body_parser = require('body-parser')
const {request_logger, error_logger} = require('./src/middlewares/logger')
const {error_handler} = require('./src/middlewares/error_handler')
const {start_observer} = require('./src/modules/session_observer')
const {test_config} = require('./tests/config')
const {test_titles} = require('./tests/titles')
if (!test_config()) {
process.exit(1)
}
if (!test_titles()) {
process.exit(1)
}
console.log('all initial tests successfully passed')
start_observer()
@@ -24,5 +36,4 @@ app.use(error_handler)
// start listen server
app.listen(port)
console.log('all config.js tests passed successfully [not developed]')
console.log(`Listening at http://localhost:${port}`)
+3 -1
View File
@@ -1,6 +1,8 @@
require('dotenv').config()
module.exports = {
port: parseInt(process.env.PORT),
external_url: process.env.EXTERNAL_URL,
external_domain: process.env.EXTERNAL_DOMAIN,
coordinator_url: process.env.COORDINATOR_URL,
database_url: process.env.DATABASE_URL,
database_name: process.env.DATABASE_NAME,
+3018
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -17,8 +17,12 @@
"license": "ISC",
"dependencies": {
"celebrate": "^15.0.1",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"is-running": "^2.1.0",
"mongodb": "^4.13.0",
"node-datetime": "^2.1.2",
"node-modules": "^1.0.1",
"portfinder": "^1.0.32",
"request": "^2.88.2"
}
+3 -3
View File
@@ -4,7 +4,7 @@ const {get_app_path} = require('../modules/titles')
const not_found_error = require('../../lib/src/http/errors/not_found_error')
const server_error = require('../../lib/src/http/errors/server_error')
const {run_webrtc, run_app} = require('../modules/run_process')
const {external_url} = require('../../config')
const {create_websocket_url} = require('../modules/links')
const run_session = async (req, res, next) => {
@@ -42,8 +42,8 @@ const run_session = async (req, res, next) => {
next(new server_error('can not add session to database'))
return
}
res.json({websocket_url:`wss://${external_url}${webrtc_port}/`})
res.json({websocket_url:create_websocket_url(webrtc_port)})
}
module.exports = {
+9 -7
View File
@@ -2,18 +2,21 @@ const { MongoClient } = require("mongodb")
const { database_url, database_name } = require('../../config')
let max_database_connection_timeout = 1000
const client = new MongoClient(database_url, {
serverSelectionTimeoutMS: max_database_connection_timeout,
useUnifiedTopology: true
})
const get_db = async () => {
const client = new MongoClient(database_url, {
serverSelectionTimeoutMS: max_database_connection_timeout,
useUnifiedTopology: true
})
try {
await client.connect()
await client.db(database_name).command({ ping: 1 })
return client.db(database_name)
} catch (err) {
console.log('can not connect to database')
console.error('can not connect to database')
await client.close()
return null
}
return client.db(database_name)
}
const add_running_session = async (session_id, app_pid, webrtc_pid) => {
@@ -46,7 +49,6 @@ const get_running_sessions = async () => {
if (!db) {
return null
}
var running_sessions = await db.collection('running_session').find().toArray()
return (!running_sessions.length) ? null : running_sessions
}
+17
View File
@@ -0,0 +1,17 @@
const {external_domain} = require('../../config')
const create_websocket_url = (webrtc_port) => {
var valid_external_domain = external_domain
// if external domain contains http OR https part remove it
valid_external_domain = valid_external_domain.replace('http://', '').replace('https://', '')
valid_external_domain = (valid_external_domain.slice(-1) == '/') ?
valid_external_domain.substring(0, valid_external_domain.length - 1) : valid_external_domain
return `wss://${valid_external_domain}/${webrtc_port}/`
}
module.exports = {
create_websocket_url
}
+14 -4
View File
@@ -10,7 +10,7 @@ const is_proc_running = (pid) => {
return is_running(pid)
}
const is_proc_running_async = async (pid, check_time) => {
const is_proc_running_async = async (pid, time_ms) => {
var timeout_count = 0
var check_times = 10
@@ -22,7 +22,7 @@ const is_proc_running_async = async (pid, check_time) => {
if (timeout_count > check_times) {
return true
}
await new Promise(resolve => setTimeout(resolve, check_time / check_times))
await new Promise(resolve => setTimeout(resolve, time_ms / check_times))
}
}
@@ -33,7 +33,8 @@ const run_webrtc = async (webrtc_port, app_port) => {
var webrtc_proc
try {
webrtc_proc = fork(webrtc_server_path, [webrtc_port.toString(), app_port.toString()], {
var webrtc_proc_arg = JSON.stringify({webrtc_port:webrtc_port, app_port:app_port})
webrtc_proc = fork(webrtc_server_path, [webrtc_proc_arg], {
detached: true
})
} catch (err) {
@@ -49,9 +50,18 @@ const run_app = async (app_path, app_port) => {
var app_proc
try {
app_proc = spawn(app_path, [].concat(app_args_static,
[app_port_arg + app_port.toString()]), {
[app_port_arg + app_port.toString()], { cwd: './', stdio: ['ignore', 'pipe', 'pipe']}), {
detached: true
})
// bind some events to unfreez process
app_proc.stdout.on('data', data => {
//console.log('stdout: ' + data.toString());
})
app_proc.stderr.on('data', data => {
//console.log('stderr: ' + data.toString());
})
} catch (err) {
return null
}
+13 -5
View File
@@ -2,8 +2,10 @@ const run_process = require('../modules/run_process')
const database = require('../database/database')
const {is_proc_running, kill_proc} = require('./run_process')
const coordinator = require('./coordinator')
const wait_list = require('./wait_list')
const observer_timeout = 100
var pending_list = new wait_list([])
const start_observer = () => {
check()
@@ -16,16 +18,22 @@ const check_sessions = async (sessions) => {
await Promise.all(sessions.map(async (session) => {
var webrtc_running = is_proc_running(session.webrtc_pid)
var app_running = is_proc_running(session.app_pid)
if (webrtc_running && app_running) {
return
}
// if something running kill process and skip remove from database
if (!webrtc_running && !app_running) {
var close_session_result = await coordinator.close_session(session.session_id)
if (close_session_result) {
database.remove_running_session(session.session_id)
if (!pending_list.is_waiting(session.session_id)) {
pending_list.add(session.session_id)
var close_session_result = await coordinator.close_session(session.session_id)
if (close_session_result) {
database.remove_running_session(session.session_id)
} else {
pending_list.remove(session.session_id)
console.log('can not connect to coordinator')
}
}
} else {
kill_proc(session.webrtc_pid)
@@ -36,7 +44,7 @@ const check_sessions = async (sessions) => {
const check = async () => {
var sessions = await database.get_running_sessions()
if (!sessions) {
return
}
+20
View File
@@ -0,0 +1,20 @@
module.exports = class wait_list {
constructor(list) {
this.list = list
}
is_waiting(id) {
return (this.list.indexOf(id) > -1)
}
add(id) {
if (this.is_waiting(id)) {
return
}
this.list.push(id)
}
remove(id) {
this.list.splice(this.list.indexOf(id), 1)
}
}
+69
View File
@@ -0,0 +1,69 @@
var config = require('../config')
const not_valid = (message) => {
console.error('not valid (config.js): ', message)
}
const test_config = () => {
var config_pass = true
if (!config) {
not_valid('config')
config_pass = false
}
if (!config.port) {
not_valid('port')
config_pass = false
}
if (!config.external_domain) {
not_valid('external_domain')
config_pass = false
}
if (!config.coordinator_url) {
not_valid('coordinator_url')
config_pass = false
}
if (!config.database_url) {
not_valid('database_url')
config_pass = false
}
if (!config.database_name) {
not_valid('database_name')
config_pass = false
}
if (!config.webrtc_port_begin) {
not_valid('webrtc_port_begin')
config_pass = false
}
if (!config.session_limit) {
not_valid('session_limit')
config_pass = false
}
if (!config.log_path) {
not_valid('log_path')
config_pass = false
}
if (!config.webrtc_server_path) {
not_valid('webrtc_server_path')
config_pass = false
}
if (!config.app_args) {
not_valid('webrtc_server_path')
config_pass = false
}
return config_pass
}
module.exports = {
test_config
}
+24
View File
@@ -0,0 +1,24 @@
const fs = require('fs')
const titles = require('../titles')
const path_not_exists = (message) => {
console.error('path not exists (titles.json): \n', message)
}
const test_titles = () => {
var titles_pass = true
titles.forEach(title => {
if (!fs.existsSync(title.path)) {
titles_pass = false
path_not_exists(`\ttitle: ${title.title}, path: ${title.path}`)
}
})
return titles_pass
}
module.exports = {
test_titles
}
+4 -4
View File
@@ -1,10 +1,10 @@
[
{
"title":"test-fortis",
"path":"D:/shared/Builds/Fortis_UnStable_64/WindowsNoEditor/FORTIS_Taktika.exe"
},
{
"title":"test-ivazowsky",
"path":"D:/shared/Builds/ivazowsky/Ivaz_Optimized_2/Ivazowsky.exe"
},
{
"title":"test-mosharov",
"path":"D:/shared/Builds/mosharov/mosharov-stream-v1/Masharovdev.exe"
}
]