const fs = require('fs')
const ws = require('ws')
const https = require('https')
-const http = require('http')
const getport = require('get-port')
-const argv = require('minimist')(process.argv.slice(2))
+const mime = require('mime')
-const https_router_opts = {
- key: fs.readFileSync('stunnel.key'),
- cert: fs.readFileSync('stunnel.cert')
+const argv = require('minimist')(process.argv.slice(2), {
+ string: [ 'ca-cert', 'ca-key', 'config', 'client-js', 'host-js', 'electron', 'port' ],
+ boolean: [ 'remote-host' ],
+ alias: { c: 'config',
+ j: 'client-js',
+ J: 'host-js',
+ C: 'ca-cert',
+ K: 'ca-key',
+ e: 'electron',
+ r: 'remote-host',
+ p: 'port'
+ },
+ default: { config: undefined,
+ 'client-js': 'client.js',
+ 'host-js': 'host.js',
+ 'ca-cert': 'stunnel.cert',
+ 'ca-key': 'stunnel.key',
+ 'remote-host': true,
+ 'port': 2443
+ },
+ stopEarly: true,
+ unknown: (opt) => {
+ console.log(process.argv.join(" ") + '\nUnknown operator: ' + opt + `
+Usage: strapp [OPTION]...
+Route https connections from a hardware port to a remote host, and initiate
+peer-to-peer connection with clients.
+
+CONFIG
+ -c, --config=path Configuration file to use (/etc/strapp.conf)
+ - overridden by command line opts
+ -j, --client-js=path Path to the client Strapp code (./strapp-client.js)
+ -J, --host-js=path Path to the host Strapp code (./strapp-host.js)
+ -C, --ca-cert=path Accessible location of the CA Cert (./stunnel.cert)
+ -K, --ca-key=path Accessible location of the CA Key (./stunnel.key)
+ -p, --port=number The local port to bind HTTPS listener to (2443)
+
+ROUTING
+ -e, --electron=route Route to the local electron user (nil)
+ - enables optional electron dependency
+ -d, --dedicated=route Route all incoming connections to this route (nil)
+ - used in conjunction with '-e=my_route'
+
+(c)2017 jk software
+`)
+ process.exit()
+ }
+})
+
+//TODO: if (argv['config'] !== undefined), read and apply to argv without overwrite
+if (argv['www-path'] == undefined)
+ argv['www-path'] = 'www'
+else if (String(argv['www-path']).endsWith('/'))
+ argv['www-path'] = argv['www-path'].slice(0,-1)
+
+const routerOpts = {
+ key: fs.readFileSync(argv['ca-key']),
+ cert: fs.readFileSync(argv['ca-cert'])
}
+const skelPage = String(fs.readFileSync('skel.html')).split("<!--STRAPP-->")
+const clientJS = fs.readFileSync(argv['client-js'])
+const hostJS = fs.readFileSync(argv['host-js'])
+const routes = {}
+const fileBuf = {}
+let fbSize = 0
+fs.readdirSync(argv['www-path']).forEach((file) => {
+ if (fbSize++ < 50) {
+ fileBuf[file] = { mime: mime.lookup(argv['www-path'] + '/' + file),
+ data: fs.readFileSync(argv['www-path'] + '/' + file)
+ }
+ }
+})
-let https_routes = {}
-const https_router = https.createServer(https_router_opts, (request, response) => {
- let ht_argv = request.url.slice(1).split("?")
- console.log(ht_argv)
- if (ht_argv[0] in https_routes) {
- response.writeHead(200, { 'Content-Type': 'text/plain' })
- response.write('You are a remote client\r\n')
- let route = https_routes[ht_argv[0]]
- response.write('You should connect to ' + route.host + "\r\nOn port: " + route.port + "\r\n")
+const router = https.createServer(routerOpts, (request, response) => {
+ const htArgv = request.url.slice(1).split("?")
+ let routeName = htArgv[0].split('/')[0]
+ if (routeName === '')
+ routeName = 'index.html'
+ if (routeName.indexOf('.') != -1) {
+ if (routeName in fileBuf) {
+ response.writeHead(200, { 'Content-Type': fileBuf[routeName].mime })
+ response.write(fileBuf[routeName].data)
+ }
+ else {
+ if ('404.html' in fileBuf) {
+ response.writeHead(404, fileBuf['404.html'].mime)
+ response.write(fileBuf['404.html'].data)
+ }
+ else
+ response.writeHead(404)
+ }
response.end()
}
- else if (ht_argv[0].indexOf(".") == -1) {
- https_routes[ht_argv[0]] = 'true'
+ else if (routeName in routes) {
+ const route = routes[routeName]
response.writeHead(200, { 'Content-Type': 'text/html' })
- let new_route = {}
- new_route.host = request.headers['x-forwarded-for'] || request.connection.remoteAddress
+ response.write(skelPage[0] + clientJS + skelPage[1])
+ response.end()
+ route.socket.send(request.headers['x-forwarded-for'] || request.connection.remoteAddress)
+ }
+ else {
+ routes[htArgv[0]] = true
+ const newRoute = {}
+ newRoute.host = request.headers['x-forwarded-for'] || request.connection.remoteAddress
getport().then( (port) => {
- new_route.port = port
- new_route.httpd = https.createServer(https_router_opts, (request, response) => {
- }).listen(port)
- new_route.ws = new ws.Server( { server: new_route.httpd } )
- new_route.ws.on('connection', (ws) => { console.log("socket connected"); ws.send("CONNECTED") } )
- new_route.ws.on('message', (msg) => { console.log("Received message" + msg) })
- console.log("Listening for websocket socket " + new_route.port + " on " + new_route.host)
- console.log(new_route)
- https_routes[ht_argv[0]] = new_route
+ newRoute.port = port
+ newRoute.httpd = https.createServer(routerOpts, (request, response) => {
+ }).listen(newRoute.port)
+
+ newRoute.ws = new ws.Server( { server: newRoute.httpd } )
+ newRoute.ws.on('connection', (ws) => { console.log("socket connected"); newRoute.socket = ws; ws.send("CONNECTED") } )
+ newRoute.ws.on('message', (msg) => { console.log("Received message" + msg) })
+ console.log("Listening for websocket " + newRoute.host + " on port " + newRoute.port)
+ routes[htArgv[0]] = newRoute
}).then(() => {
- let str = String(fs.readFileSync('remote-server.html'))
- response.write(str.replace("$HOST","www.strapp.io").replace("$PORT",new_route.port))
+ response.writeHead(200, { 'Content-Type': 'text/html' })
+ response.write(skelPage[0] + 'const _strapp_host = \'www.strapp.io\'\n\tconst _strapp_port = \'' + newRoute.port + '\'\n' + hostJS + skelPage[1])
response.end()
})
}
-}).listen(2443)
-
-
+}).listen(argv['port'])
-if ("electron" in process.versions);
+//TODO: if ("electron" in process.versions) open a local renderwindow, and route to it
<!DOCTYPE HTML>
<html>
<head>
- <script type="text/javascript">
- function testStrapp() {
- document.getElementById("strapp-admin").innerHTML = "Dead"
- if ("WebSocket" in window) {
- const wsock = new WebSocket("wss://$HOST:$PORT")
- wsock.onopen = () => {
+ <title>Strapp.io Host</title>
+ <script type="text/javascript">
+ document.body = document.createElement('body')
+ const strappDiv = document.createElement('div')
+ document.body.appendChild(strappDiv)
+ strappDiv.innerHTML = "Connection Dead"
+ if ("WebSocket" in window) {
+ const wsock = new WebSocket("wss://$HOST:$PORT")
+ wsock.onopen = () => {
wsock.send("Test message")
- document.getElementById("strapp-admin").innerHTML = "Sent test"
- }
- wsock.onmessage = (evt) => {
- document.getElementById("strapp-admin").innerHTML = "Got Response\r\n" + evt.data
- }
- }
- else {
- document.getElementById("strapp-admin").innerHTML = "Your browser does not support Strapp"
- }
- }
- </script>
+ strappDiv.innerHTML = "Sent test"
+ }
+ wsock.onmessage = (evt) => {
+ strappDiv.innerHTML = "Got Response <br/>" + evt.data
+ }
+ } else {
+ strappDiv.innerHTML = "Your browser does not support Strapp"
+ }
+ </script>
</head>
- <body onload="javascript:testStrapp()">
- <div id="strapp-admin">
- Unterminated
- </div>
- </body>
</html>
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIE/TCCA+WgAwIBAgISA8tDqABqbcv2/gK8/q12IhazMA0GCSqGSIb3DQEBCwUA
+MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
+ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xNzA2MjMwMjI1MDBaFw0x
+NzA5MjEwMjI1MDBaMBgxFjAUBgNVBAMTDXd3dy5zdHJhcHAuaW8wggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCh1tSTfA1JAxsNJTwhCgwPMeY9Lib0PjiO
+/EOy0optOeDqOJwB1C0HRnTg4NSoj9q+B0joKuvjvLhYBob7sRdjqiDPsq78aESC
+ENP5rz1+O73wEtcv3G9z5IvCcnicYrWdRYipBgFiV6rxkzwZFBsxxy6W47/jjadX
+G1nhWTKy1Vkd2BjnB/B138qhJe0z45pRCvPVIaPlalQvSEiqrLhIOfmc1apFjBis
+cjI57bQ6AswgoELY5SP+edLVy3ZM/XAS+6aPVJumzQ+XCy3vCvOh0aJfoDa0Ewne
+DeMcTU9G3YicPOHwR5HT3Cd7OK+o7y4Aib0LJn1B1wbEwE8btkTRAgMBAAGjggIN
+MIICCTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUF
+BwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFHNyMj5LuteK5+BbAiTD7Vfqjv+n
+MB8GA1UdIwQYMBaAFKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsGAQUFBwEBBGMw
+YTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNlbmNyeXB0Lm9y
+ZzAvBggrBgEFBQcwAoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNlbmNyeXB0Lm9y
+Zy8wGAYDVR0RBBEwD4INd3d3LnN0cmFwcC5pbzCB/gYDVR0gBIH2MIHzMAgGBmeB
+DAECATCB5gYLKwYBBAGC3xMBAQEwgdYwJgYIKwYBBQUHAgEWGmh0dHA6Ly9jcHMu
+bGV0c2VuY3J5cHQub3JnMIGrBggrBgEFBQcCAjCBngyBm1RoaXMgQ2VydGlmaWNh
+dGUgbWF5IG9ubHkgYmUgcmVsaWVkIHVwb24gYnkgUmVseWluZyBQYXJ0aWVzIGFu
+ZCBvbmx5IGluIGFjY29yZGFuY2Ugd2l0aCB0aGUgQ2VydGlmaWNhdGUgUG9saWN5
+IGZvdW5kIGF0IGh0dHBzOi8vbGV0c2VuY3J5cHQub3JnL3JlcG9zaXRvcnkvMA0G
+CSqGSIb3DQEBCwUAA4IBAQABrj60dXDaiixw7Qd7it5YqLp0m5Gj8BR843pocoFv
+cdyeTPlyL0xWRkOn3++M2oN2MS6DXjt8nVoYrToGxqmgcpuwcn+iOv+yWiiaJWl2
+LWcfZxhD5rbSJGvkbXOI9nyTBcXF3t0rsKS+GJACaZkA0w9e+kADhJ0eGrbf30KT
+l5D9B7qX2LJaM+zcDY8gj3+tDj2hBK07JMekjC1C0y+Tq0WnvZZ3dw2YD4Jd8Ofc
+OmR20LGv4xz4wCJ7E3rqMqpUVgAfyM7yiQrnLcggIiMQRqACqJk4yn0+agsM6P2n
+Ru+x1nbXw87XsGwno5dYnqlonbUMrDBOFKX/jtoXjy+/
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCh1tSTfA1JAxsN
+JTwhCgwPMeY9Lib0PjiO/EOy0optOeDqOJwB1C0HRnTg4NSoj9q+B0joKuvjvLhY
+Bob7sRdjqiDPsq78aESCENP5rz1+O73wEtcv3G9z5IvCcnicYrWdRYipBgFiV6rx
+kzwZFBsxxy6W47/jjadXG1nhWTKy1Vkd2BjnB/B138qhJe0z45pRCvPVIaPlalQv
+SEiqrLhIOfmc1apFjBiscjI57bQ6AswgoELY5SP+edLVy3ZM/XAS+6aPVJumzQ+X
+Cy3vCvOh0aJfoDa0EwneDeMcTU9G3YicPOHwR5HT3Cd7OK+o7y4Aib0LJn1B1wbE
+wE8btkTRAgMBAAECggEAZYNvi6NAYg/EpBY7BS5cKhvOo4mTfZiZuXaasExH8ANi
+BQjFMpB4Phv/rB7axXCWcKP5I0fo9sylhPFzMeT/GtDYC++0uzZb4onTrMYy+D9Z
+AIGQ4MCQWE1/LNTIuq9f4+p9sifGLn13CFimVcS62hpqKtNoxb9Qu964HhXpKyqQ
+MeSZhJsN9/ixdAVW/JWMEcOZLtttaznJ/JPZu5cvt0cis86Iir4I89fowdFO3V+h
+WI03Ax/aafcthS1zOg4QLl/mCL69yURQPC1dYhDZ7pH/ZEqgNxp1B3+Sio37fe2A
+EJFx8kyb4+Jku7KkIYw8L+OelYxLlwEYjHkuQ+puwQKBgQDWHSA7COEvJxRScq0G
+07TaCmHMvhG+GtCDSLiST8pip64RDN/u3OpMn2MlLYQMSQB4XGdklBfb3IesbpLl
+DWIdMxQ4H1GFgfSQYiN5WzF2U6A1OG7IaWGc17hrB6hcFKrW6K8hstaboMEgtUnV
+87N9T4sr4G7B38bxI2f+VYi+jQKBgQDBf8d2yGBhfDBruNrTx+eN4ywccC+uWaOS
+vEcV1qf5hQDS2PA/7AQkxMofDDNZFak7baTbFAKhH+sUKriTyth2vgakHVS5xxJH
+WvuGJhttHheh35B1lGKVvYsJqpMG5oPpySqLLu1/gBzw0H+tnbdCA/iF//qodLD0
+vUIg6ksAVQKBgGZjz0Hr4SOXYJR82lllbBrI1DQAwKNUV1owsHLnTTUYUpk8uumT
+I0g5AESLG5Z0YpEsPxVN7IrrTsYaqox2dfDYuFaOQ4HAv588DxK9lU2sd/R7ZB7a
+ph9G2Z31L4G5MGlNJFLNHuuVWEW36/Mwcyj9nOQCk3SK+qvd7qElbhsRAoGAXlzK
+EQs2tVOy6VWEobLjITaf1F6BYi0kr/JRWD0OPYi3FhvlTcbzXlr5lpqEj/UYSnr1
+4kCn55uBMJyE1s4dMgsJgg8ruk1yhInCD3GV/mimbbJw6GYsjYmZvGUKvlq8Cc6o
+iKeh+oOnmWiUWz6GmBMHp5Fl07DUewWwsgyP9uECgYEAsdIajEA77zX0Zi/58DwC
+5OBpM5UhLBqjYPIFZgE+iowMRc79A484IBxVXnoYdGeDE1qvu4RTCggpCSmVuOFM
+kd+HY6zQNv1cVn/VXyJ2p1OK3J2gxY4nSrmy7zlIj3e7AOSswK0arenkuUlpBo7o
+G4kQUXI2Zjzw98EmRT60Yrg=
+-----END PRIVATE KEY-----