browser-canvas-fingerprinting
Version:
A simple canvas fingerprinting implementation in browser with specific information used to generate fingerprint
163 lines (139 loc) • 4.22 kB
JavaScript
// test/test-runner.js
import { createServer } from 'http';
import { readFile } from 'fs/promises';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import { spawn } from 'child_process';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
class TestServer {
constructor(port = 13879) {
this.port = port;
this.server = null;
this.baseDir = join(__dirname, '..'); // 项目根目录
}
async start() {
return new Promise((resolve, reject) => {
this.server = createServer(async (req, res) => {
try {
// 处理请求
let filePath = req.url === '/' ? '/test/index.html' : req.url;
filePath = filePath.split('?')[0]; // 去掉查询参数
// 安全限制,只允许访问项目内的文件
if (filePath.includes('..')) {
res.writeHead(403);
res.end('Forbidden');
return;
}
const fullPath = join(this.baseDir, filePath);
// 设置 MIME 类型
const mimeTypes = {
'.html': 'text/html',
'.js': 'text/javascript',
'.css': 'text/css',
'.json': 'application/json',
'.png': 'image/png',
'.jpg': 'image/jpg',
'.gif': 'image/gif',
'.ico': 'image/x-icon'
};
const ext = filePath.substring(filePath.lastIndexOf('.'));
const contentType = mimeTypes[ext] || 'application/octet-stream';
const content = await readFile(fullPath);
res.writeHead(200, { 'Content-Type': contentType });
res.end(content, 'utf-8');
} catch (error) {
if (error.code === 'ENOENT') {
res.writeHead(404);
res.end('File not found');
} else {
res.writeHead(500);
res.end('Server error: ' + error.message);
}
}
});
this.server.listen(this.port, '127.0.0.1', (err) => {
if (err) {
reject(err);
} else {
console.log(`Test server running at http://127.0.0.1:${this.port}/test/`);
resolve();
}
});
// 设置超时自动关闭(10分钟后)
this.timeout = setTimeout(() => {
console.log('Test server timeout, shutting down...');
this.stop();
}, 60 * 60 * 1000);
});
}
stop() {
return new Promise((resolve) => {
if (this.timeout) {
clearTimeout(this.timeout);
}
if (this.server) {
this.server.close(() => {
console.log('Test server stopped');
resolve();
});
} else {
resolve();
}
});
}
}
class TestRunner {
constructor() {
this.testServer = new TestServer();
this.isWindows = process.platform === 'win32';
}
async run() {
try {
// 启动测试服务器
await this.testServer.start();
// 打开浏览器
await this.openBrowser();
console.log('Server started, time: 60min');
console.log('Ctrl+C to stop');
// 等待用户中断或超时
process.on('SIGINT', async () => {
console.log('\nShutting down...');
await this.cleanup();
process.exit(0);
});
} catch (error) {
console.error('FAIL:', error);
await this.cleanup();
process.exit(1);
}
}
async openBrowser() {
const url = `http://127.0.0.1:${this.testServer.port}/${process.argv[2] === 'production' ? 'test/production.html' : ''}`;
if (this.isWindows) {
// Windows
spawn('cmd', ['/c', 'start', url], { stdio: 'inherit' });
} else {
// Linux/macOS
const commands = [
'xdg-open', // Linux
'open' // macOS
];
for (const cmd of commands) {
try {
spawn(cmd, [url], { stdio: 'inherit' });
break;
} catch (error) {
continue;
}
}
}
console.log(`Browser launched: ${url}`);
}
async cleanup() {
await this.testServer.stop();
}
}
// 运行测试
const runner = new TestRunner();
runner.run();