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:
@@ -1,3 +1,14 @@
|
|||||||
# pixel-streaming-session-server
|
# 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=
|
||||||
@@ -6,6 +6,18 @@ const body_parser = require('body-parser')
|
|||||||
const {request_logger, error_logger} = require('./src/middlewares/logger')
|
const {request_logger, error_logger} = require('./src/middlewares/logger')
|
||||||
const {error_handler} = require('./src/middlewares/error_handler')
|
const {error_handler} = require('./src/middlewares/error_handler')
|
||||||
const {start_observer} = require('./src/modules/session_observer')
|
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()
|
start_observer()
|
||||||
|
|
||||||
@@ -24,5 +36,4 @@ app.use(error_handler)
|
|||||||
|
|
||||||
// start listen server
|
// start listen server
|
||||||
app.listen(port)
|
app.listen(port)
|
||||||
console.log('all config.js tests passed successfully [not developed]')
|
|
||||||
console.log(`Listening at http://localhost:${port}`)
|
console.log(`Listening at http://localhost:${port}`)
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
|
require('dotenv').config()
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
port: parseInt(process.env.PORT),
|
port: parseInt(process.env.PORT),
|
||||||
external_url: process.env.EXTERNAL_URL,
|
external_domain: process.env.EXTERNAL_DOMAIN,
|
||||||
coordinator_url: process.env.COORDINATOR_URL,
|
coordinator_url: process.env.COORDINATOR_URL,
|
||||||
database_url: process.env.DATABASE_URL,
|
database_url: process.env.DATABASE_URL,
|
||||||
database_name: process.env.DATABASE_NAME,
|
database_name: process.env.DATABASE_NAME,
|
||||||
|
|||||||
Generated
+3018
File diff suppressed because it is too large
Load Diff
@@ -17,8 +17,12 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"celebrate": "^15.0.1",
|
"celebrate": "^15.0.1",
|
||||||
|
"dotenv": "^16.0.3",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"is-running": "^2.1.0",
|
"is-running": "^2.1.0",
|
||||||
|
"mongodb": "^4.13.0",
|
||||||
|
"node-datetime": "^2.1.2",
|
||||||
|
"node-modules": "^1.0.1",
|
||||||
"portfinder": "^1.0.32",
|
"portfinder": "^1.0.32",
|
||||||
"request": "^2.88.2"
|
"request": "^2.88.2"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const {get_app_path} = require('../modules/titles')
|
|||||||
const not_found_error = require('../../lib/src/http/errors/not_found_error')
|
const not_found_error = require('../../lib/src/http/errors/not_found_error')
|
||||||
const server_error = require('../../lib/src/http/errors/server_error')
|
const server_error = require('../../lib/src/http/errors/server_error')
|
||||||
const {run_webrtc, run_app} = require('../modules/run_process')
|
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) => {
|
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'))
|
next(new server_error('can not add session to database'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json({websocket_url:`wss://${external_url}${webrtc_port}/`})
|
res.json({websocket_url:create_websocket_url(webrtc_port)})
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
@@ -2,18 +2,21 @@ const { MongoClient } = require("mongodb")
|
|||||||
const { database_url, database_name } = require('../../config')
|
const { database_url, database_name } = require('../../config')
|
||||||
let max_database_connection_timeout = 1000
|
let max_database_connection_timeout = 1000
|
||||||
|
|
||||||
|
const client = new MongoClient(database_url, {
|
||||||
|
serverSelectionTimeoutMS: max_database_connection_timeout,
|
||||||
|
useUnifiedTopology: true
|
||||||
|
})
|
||||||
|
|
||||||
const get_db = async () => {
|
const get_db = async () => {
|
||||||
const client = new MongoClient(database_url, {
|
|
||||||
serverSelectionTimeoutMS: max_database_connection_timeout,
|
|
||||||
useUnifiedTopology: true
|
|
||||||
})
|
|
||||||
try {
|
try {
|
||||||
await client.connect()
|
await client.connect()
|
||||||
|
await client.db(database_name).command({ ping: 1 })
|
||||||
|
return client.db(database_name)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('can not connect to database')
|
console.error('can not connect to database')
|
||||||
|
await client.close()
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return client.db(database_name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const add_running_session = async (session_id, app_pid, webrtc_pid) => {
|
const add_running_session = async (session_id, app_pid, webrtc_pid) => {
|
||||||
@@ -46,7 +49,6 @@ const get_running_sessions = async () => {
|
|||||||
if (!db) {
|
if (!db) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
var running_sessions = await db.collection('running_session').find().toArray()
|
var running_sessions = await db.collection('running_session').find().toArray()
|
||||||
return (!running_sessions.length) ? null : running_sessions
|
return (!running_sessions.length) ? null : running_sessions
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ const is_proc_running = (pid) => {
|
|||||||
return is_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 timeout_count = 0
|
||||||
var check_times = 10
|
var check_times = 10
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ const is_proc_running_async = async (pid, check_time) => {
|
|||||||
if (timeout_count > check_times) {
|
if (timeout_count > check_times) {
|
||||||
return true
|
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
|
var webrtc_proc
|
||||||
try {
|
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
|
detached: true
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -49,9 +50,18 @@ const run_app = async (app_path, app_port) => {
|
|||||||
var app_proc
|
var app_proc
|
||||||
try {
|
try {
|
||||||
app_proc = spawn(app_path, [].concat(app_args_static,
|
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
|
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) {
|
} catch (err) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ const run_process = require('../modules/run_process')
|
|||||||
const database = require('../database/database')
|
const database = require('../database/database')
|
||||||
const {is_proc_running, kill_proc} = require('./run_process')
|
const {is_proc_running, kill_proc} = require('./run_process')
|
||||||
const coordinator = require('./coordinator')
|
const coordinator = require('./coordinator')
|
||||||
|
const wait_list = require('./wait_list')
|
||||||
|
|
||||||
const observer_timeout = 100
|
const observer_timeout = 100
|
||||||
|
var pending_list = new wait_list([])
|
||||||
|
|
||||||
const start_observer = () => {
|
const start_observer = () => {
|
||||||
check()
|
check()
|
||||||
@@ -16,16 +18,22 @@ const check_sessions = async (sessions) => {
|
|||||||
await Promise.all(sessions.map(async (session) => {
|
await Promise.all(sessions.map(async (session) => {
|
||||||
var webrtc_running = is_proc_running(session.webrtc_pid)
|
var webrtc_running = is_proc_running(session.webrtc_pid)
|
||||||
var app_running = is_proc_running(session.app_pid)
|
var app_running = is_proc_running(session.app_pid)
|
||||||
|
|
||||||
if (webrtc_running && app_running) {
|
if (webrtc_running && app_running) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// if something running kill process and skip remove from database
|
// if something running kill process and skip remove from database
|
||||||
if (!webrtc_running && !app_running) {
|
if (!webrtc_running && !app_running) {
|
||||||
var close_session_result = await coordinator.close_session(session.session_id)
|
if (!pending_list.is_waiting(session.session_id)) {
|
||||||
if (close_session_result) {
|
pending_list.add(session.session_id)
|
||||||
database.remove_running_session(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 {
|
} else {
|
||||||
kill_proc(session.webrtc_pid)
|
kill_proc(session.webrtc_pid)
|
||||||
@@ -36,7 +44,7 @@ const check_sessions = async (sessions) => {
|
|||||||
|
|
||||||
const check = async () => {
|
const check = async () => {
|
||||||
var sessions = await database.get_running_sessions()
|
var sessions = await database.get_running_sessions()
|
||||||
|
|
||||||
if (!sessions) {
|
if (!sessions) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
@@ -1,10 +1,10 @@
|
|||||||
[
|
[
|
||||||
{
|
|
||||||
"title":"test-fortis",
|
|
||||||
"path":"D:/shared/Builds/Fortis_UnStable_64/WindowsNoEditor/FORTIS_Taktika.exe"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"title":"test-ivazowsky",
|
"title":"test-ivazowsky",
|
||||||
"path":"D:/shared/Builds/ivazowsky/Ivaz_Optimized_2/Ivazowsky.exe"
|
"path":"D:/shared/Builds/ivazowsky/Ivaz_Optimized_2/Ivazowsky.exe"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title":"test-mosharov",
|
||||||
|
"path":"D:/shared/Builds/mosharov/mosharov-stream-v1/Masharovdev.exe"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
Reference in New Issue
Block a user