/**
|
* 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;
|