server.js 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. import http from 'http';
  2. import path from 'path';
  3. import { createReadStream, existsSync, statSync } from 'fs';
  4. const mimeTypes = {
  5. '.html': 'text/html',
  6. '.js': 'application/javascript',
  7. '.css': 'text/css',
  8. '.json': 'application/json',
  9. '.png': 'image/png',
  10. '.jpg': 'image/jpeg',
  11. '.gif': 'image/gif',
  12. '.svg': 'image/svg+xml',
  13. '.mp3': 'audio/mpeg',
  14. '.mp4': 'video/mp4',
  15. '.webm': 'video/webm',
  16. '.ogv': 'video/ogg',
  17. '.ogg': 'audio/ogg',
  18. '.woff': 'font/woff',
  19. '.woff2': 'font/woff2',
  20. '.ttf': 'font/ttf',
  21. '.glb': 'model/gltf-binary',
  22. '.gltf': 'model/gltf+json',
  23. '.hdr': 'application/octet-stream',
  24. '.exr': 'application/octet-stream',
  25. '.fbx': 'application/octet-stream',
  26. '.bin': 'application/octet-stream',
  27. '.cube': 'text/plain'
  28. };
  29. const rootDirectory = path.resolve();
  30. const server = http.createServer( ( req, res ) => {
  31. const pathname = decodeURIComponent( req.url.split( '?' )[ 0 ] );
  32. const filePath = path.normalize( path.join( rootDirectory, pathname ) );
  33. // Prevent path traversal attacks
  34. if ( ! filePath.startsWith( rootDirectory ) ) {
  35. res.writeHead( 403 );
  36. res.end( 'Forbidden' );
  37. return;
  38. }
  39. if ( ! existsSync( filePath ) ) {
  40. res.writeHead( 404 );
  41. res.end( 'File not found' );
  42. return;
  43. }
  44. const ext = path.extname( filePath ).toLowerCase();
  45. const contentType = mimeTypes[ ext ] || 'application/octet-stream';
  46. const stat = statSync( filePath );
  47. const fileSize = stat.size;
  48. const range = req.headers.range;
  49. if ( range ) {
  50. const parts = range.replace( /bytes=/, '' ).split( '-' );
  51. const start = parseInt( parts[ 0 ], 10 );
  52. const end = parts[ 1 ] ? parseInt( parts[ 1 ], 10 ) : fileSize - 1;
  53. res.writeHead( 206, {
  54. 'Content-Range': `bytes ${start}-${end}/${fileSize}`,
  55. 'Accept-Ranges': 'bytes',
  56. 'Content-Length': end - start + 1,
  57. 'Content-Type': contentType
  58. } );
  59. createReadStream( filePath, { start, end } ).pipe( res );
  60. } else {
  61. res.writeHead( 200, {
  62. 'Content-Length': fileSize,
  63. 'Content-Type': contentType
  64. } );
  65. createReadStream( filePath ).pipe( res );
  66. }
  67. } );
  68. export default server;
粤ICP备19079148号