-
Notifications
You must be signed in to change notification settings - Fork 84
Expand file tree
/
Copy pathstatic.js
More file actions
89 lines (80 loc) · 2.92 KB
/
static.js
File metadata and controls
89 lines (80 loc) · 2.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// Copyright (c) 2026 RobotWebTools Contributors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// rclnodejs/web demo — static-file server (page side; named `static.js`
// to avoid being confused with the runtime-side `runtime.js`).
//
// Serves index.html on port 8080 and maps `/sdk/*` to the in-repo
// `web/` folder so the page can `import { connect } from '/sdk/index.js'`
// without bundling. In a downstream project you'd `npm install rclnodejs`
// and `import { connect } from 'rclnodejs/web'` instead.
//
// Pair with `node runtime.js` (the rclnodejs/web runtime + the demo's
// ROS 2 nodes) in another shell — the same split as the TypeScript
// demo's `tsx server.ts` + `vite`. Production deployments use nginx,
// a CDN, or any other static host.
'use strict';
const path = require('path');
const http = require('http');
const fs = require('fs');
const STATIC_PORT = Number(process.env.STATIC_PORT || 8080);
const demoDir = __dirname;
const sdkDir = path.resolve(__dirname, '..', '..', '..', 'web');
const server = http.createServer((req, res) => {
let urlPath = (req.url || '/').split('?')[0];
if (urlPath === '/') urlPath = '/index.html';
let baseDir;
let relPath;
if (urlPath.startsWith('/sdk/')) {
baseDir = sdkDir;
relPath = urlPath.slice('/sdk/'.length);
} else {
baseDir = demoDir;
relPath = urlPath.replace(/^\/+/, '');
}
const filePath = path.resolve(baseDir, relPath);
// Confine reads to baseDir. `path.relative` collapses `..` segments,
// so any escape attempt either yields a result that starts with `..`
// or is absolute — reject both. (`startsWith(baseDir)` alone would
// false-positive on a sibling like `${baseDir}-other/...`.)
const rel = path.relative(baseDir, filePath);
if (rel.startsWith('..') || path.isAbsolute(rel)) {
res.writeHead(403).end('forbidden');
return;
}
fs.readFile(filePath, (err, data) => {
if (err) {
res.writeHead(404).end('not found');
return;
}
const ext = path.extname(filePath).toLowerCase();
const ctype =
{
'.html': 'text/html; charset=utf-8',
'.js': 'application/javascript; charset=utf-8',
'.mjs': 'application/javascript; charset=utf-8',
'.css': 'text/css; charset=utf-8',
'.json': 'application/json; charset=utf-8',
}[ext] || 'application/octet-stream';
res.writeHead(200, { 'content-type': ctype }).end(data);
});
});
server.on('error', (err) => {
console.error(err);
process.exit(1);
});
server.listen(STATIC_PORT, () => {
console.log(`Static files : http://localhost:${STATIC_PORT}/`);
});
const stop = () => {
console.log('\nstopping…');
server.close();
process.exit(0);
};
process.once('SIGINT', stop);
process.once('SIGTERM', stop);