/** * HTTP Server Module * Features: * - Supports GET/POST requests * - Supports file upload via raw body ('application/octet-stream') and multipart form data ('multipart/form-data'). * - Supports file download * - Supports static html file service * - Multi-threading not supported, all functions should run in a single thread. For cross-thread communication, use dxEventbus to pass data * * Usage: * - Simple WebServer * - Simple Web API Service * * Doc/Demo : https://github.com/DejaOS/DejaOS */ import { httpserverClass } from './libvbar-m-dxhttpserver.so' let server = null; const httpserver = {} httpserver.init = function () { if (!server) { server = new httpserverClass(); } } /** * Route the HTTP request. A maximum of 100 routes can be registered. * @param {string} path - The path to route the request. Should start with '/'. * Supports wildcard matching by ending with '/*'. * @param {function} callback - The callback function to handle the request * @param {object} callback.req - The request object * @param {string} callback.req.method - The HTTP method (GET, POST, etc.) * @param {string} callback.req.url - The request URL * @param {string} callback.req.query - The query string (e.g. "a=1&b=2") * @param {object} callback.req.headers - The request headers * @param {string} [callback.req.body] - The request body (only for specific Content-Type) * @param {function(string): boolean} callback.req.saveFile - Function to save uploaded file from raw request body. Returns true on success. * @param {function(string): object} callback.req.saveMultipartFile - Function to handle 'multipart/form-data' upload. It saves the file part to the specified path and returns other fields as an object. * @param {object} callback.res - The response object * @param {function} callback.res.send - Send response with body and headers,the header should be a object and the size should be < 512 * @param {function} callback.res.sendFile - Send file as response * * @example * // Basic usage * httpserver.route('/hello', function(req, res) { * res.send('Hello World', {'Content-Type': 'text/plain'}); * }); * * @example * // Wildcard route to handle all requests under /api/ * httpserver.route('/api/*', function(req, res) { * // req.url will be the full URL, e.g., "/api/users/123" * if (req.url.startsWith('/api/users/')) { * const userId = req.url.substring(11); * res.send(`User ID is ${userId}`); * } else { * res.send('Welcome to the API!'); * } * }); * * @example * // Handle file upload (raw body). Client should POST the file content directly. * // Example with curl: * // curl -X POST --data-binary "@/path/to/your/file.txt" \ * // -H "Content-Type: application/octet-stream" \ * // http://127.0.0.1:8080/upload * httpserver.route('/upload', function(req, res) { * req.saveFile('/app/code/data/file_saved.txt'); * res.send('File saved'); * }); * * @example * // Handle multipart/form-data upload. * // This saves the file part to the specified path and returns other form fields. * // Example with curl: * // curl -X POST -F "file1=@/path/to/your/file.bin" \ * // -F "user=JohnDoe" \ * // -F "timestamp=1678886400" \ * // http://127.0.0.1:8080/form-upload * httpserver.route('/form-upload', function(req, res) { * const fields = req.saveMultipartFile('/app/code/data/uploaded_file.bin'); * // fields will be: { user: "JohnDoe", timestamp: "1678886400" } * res.send(`File saved, user was ${fields.user}`); * }); * * @example * // Handle file download * httpserver.route('/download', function(req, res) { * res.sendFile('/app/code/data/file.txt'); * }); */ httpserver.route = function (path, callback) { httpserver.init(); // Wrap the user's callback in a try...catch block to handle uncaught exceptions const wrappedCallback = (req, res) => { try { callback(req, res); } catch (e) { try { res.send(JSON.stringify({ error: "Internal Server Error", message: String(e)+"\n"+e.stack }), { "Content-Type": "application/json" }); } catch (resError) { } } }; server.route(path, wrappedCallback); }; /** * Starts the HTTP server listening for connections. * @param {number} port */ httpserver.listen = function (port) { httpserver.init(); server.listen(port); } /** * Loop the HTTP server */ httpserver.loop = function () { httpserver.init(); server.loop(); } /** * Serve static files * @param {string} path The path to serve static files,should be start with '/' * @param {string} dir The directory to serve static files,should be a absolute path start with '/app' */ httpserver.serveStatic = function (path, dir) { httpserver.init(); if (!path) { path = '/'; } if (!path.startsWith('/')) { path = '/' + path; } if (!path.endsWith('/')) { path = path + '/'; } //path should be start with '/' and end with '/',or '/' server.serveStatic(path, dir); } /** * Deinitialize the server */ httpserver.deinit = function () { if (server) { server = null; } } /** * Get the native server object * @returns {Object} Native server object */ httpserver.getNative = function () { httpserver.init(); return server; } export default httpserver;