/**
|
* HTTP Client Module
|
* This module provides a stateless, function-based API for making HTTP requests.
|
* Each request function (get, post, request, etc.) creates an isolated,
|
* short-lived native client instance, ensuring thread-safety and preventing state conflicts.
|
*
|
* Features:
|
* - GET/POST/PUT/PATCH/DELETE requests
|
* - File upload/download with progress
|
* - HTTPS with certificate verification options (disabled by default)
|
* - Fully configurable via an options object per request
|
*
|
* Usage:
|
* - Simple requests: `httpclient.get(url, { timeout: 3000 })`
|
* - Complex requests: `httpclient.request({ url, method, headers: { 'Content-Type': 'application/json' }, body, ... })`
|
*
|
* Doc/Demo : https://github.com/DejaOS/DejaOS
|
*/
|
import * as native from './libvbar-m-dxhttpclient.so'
|
|
const httpclient = {}
|
|
/**
|
* The core request function. Each call invokes the native stateless request function.
|
* @param {object} options - Request options.
|
* @param {string} options.url - The request URL. (Required)
|
* @param {string} [options.method='GET'] - The request method (GET, POST, etc.).
|
* @param {object<string, string>|string[]} [options.headers] - Request headers as a key-value object (recommended) or an array of strings for backward compatibility.
|
* @param {string|object} [options.body] - The request body. JS objects will be stringified to JSON.
|
* @param {number} [options.timeout=5000] - Timeout in milliseconds.
|
* @param {Function} [options.onProgress] - Progress callback function.
|
* @param {number} [options.verifyPeer] - Certificate verification (0: disable, 1: enable). Default: 0 (disabled for convenience in IoT).
|
* @param {number} [options.verifyHost] - Hostname verification (0: disable, 2: enable). Default: 0 (disabled for convenience in IoT).
|
* @param {string} [options.caFile] - Path to CA certificate file.
|
* @returns {object} Response result { code, status, message, data }.
|
* @throws {Error} Throws error on invalid input.
|
*/
|
httpclient.request = function (options) {
|
if (!options || typeof options !== 'object') {
|
throw new Error("Request options object is required");
|
}
|
if (!options.url) {
|
throw new Error("options.url is required");
|
}
|
|
const optsToSet = { ...options };
|
|
// Default method to GET
|
if (!optsToSet.method) {
|
optsToSet.method = 'GET';
|
}
|
|
// Default timeout
|
if (optsToSet.timeout === undefined) {
|
optsToSet.timeout = 5000;
|
}
|
|
// Convert headers object to array of strings for C layer BEFORE further processing
|
if (optsToSet.headers && typeof optsToSet.headers === 'object' && !Array.isArray(optsToSet.headers)) {
|
optsToSet.headers = Object.entries(optsToSet.headers).map(([key, value]) => `${key}: ${value}`);
|
}
|
if (!optsToSet.headers) {
|
optsToSet.headers = [];
|
}
|
// Auto-stringify JSON body and set Content-Type header
|
if (optsToSet.body && typeof optsToSet.body === 'object') {
|
optsToSet.body = JSON.stringify(optsToSet.body);
|
// Add header only if not already present
|
if (!optsToSet.headers.some(h => h.toLowerCase().startsWith('content-type:'))) {
|
optsToSet.headers.push('Content-Type: application/json');
|
}
|
}
|
|
return native.request(optsToSet);
|
}
|
|
/**
|
* Send GET request.
|
* @param {string} url - Request URL.
|
* @param {number} [timeout=5000] - Timeout in milliseconds. For backward compatibility.
|
* @param {object} [options] - Additional request options (headers, etc.).
|
* @returns {object} Response result.
|
*/
|
httpclient.get = function (url, timeout = 5000, options = {}) {
|
return httpclient.request({ ...options, url: url, method: 'GET', timeout: timeout });
|
}
|
|
/**
|
* Send POST request with a body.
|
* @param {string} url - Request URL.
|
* @param {string|object} data - Request body. JS objects will be stringified as JSON.
|
* @param {number} [timeout=5000] - Timeout in milliseconds. For backward compatibility.
|
* @param {object} [options] - Additional request options (headers, timeout, etc.).
|
* @returns {object} Response result.
|
*/
|
httpclient.post = function (url, data, timeout = 5000, options = {}) {
|
return httpclient.request({ ...options, url: url, method: 'POST', body: data, timeout: timeout });
|
}
|
|
/**
|
* Send PUT request with a body.
|
* @param {string} url - Request URL.
|
* @param {string|object} data - Request body. JS objects will be stringified as JSON.
|
* @param {number} [timeout=5000] - Timeout in milliseconds. For backward compatibility.
|
* @param {object} [options] - Additional request options.
|
* @returns {object} Response result.
|
*/
|
httpclient.put = function (url, data, timeout = 5000, options = {}) {
|
return httpclient.request({ ...options, url: url, method: 'PUT', body: data, timeout: timeout });
|
}
|
|
/**
|
* Send PATCH request with a body.
|
* @param {string} url - Request URL.
|
* @param {string|object} data - Request body. JS objects will be stringified as JSON.
|
* @param {number} [timeout=5000] - Timeout in milliseconds. For backward compatibility.
|
* @param {object} [options] - Additional request options.
|
* @returns {object} Response result.
|
*/
|
httpclient.patch = function (url, data, timeout = 5000, options = {}) {
|
return httpclient.request({ ...options, url: url, method: 'PATCH', body: data, timeout: timeout });
|
}
|
|
/**
|
* Send DELETE request.
|
* @param {string} url - Request URL.
|
* @param {number} [timeout=5000] - Timeout in milliseconds. For backward compatibility.
|
* @param {object} [options] - Additional request options.
|
* @returns {object} Response result.
|
*/
|
httpclient.delete = function (url, timeout = 5000, options = {}) {
|
return httpclient.request({ ...options, url: url, method: 'DELETE', timeout: timeout });
|
}
|
|
/**
|
* Download a file.
|
* @param {string} url - Request URL.
|
* @param {string} localPath - Local path to save the file.
|
* @param {number} [timeout=30000] - Timeout in milliseconds. For backward compatibility.
|
* @param {object} [options] - Additional request options.
|
* @returns {object} Response result (without data field).
|
*/
|
httpclient.download = function (url, localPath, timeout = 30000, options = {}) {
|
if (!url) throw new Error("URL is required");
|
if (!localPath) throw new Error("Local path is required");
|
|
const optsToSet = {
|
...options,
|
timeout: timeout, // Longer default timeout for downloads
|
url: url,
|
method: 'GET'
|
};
|
|
// Convert headers object to array of strings for C layer
|
if (optsToSet.headers && typeof optsToSet.headers === 'object' && !Array.isArray(optsToSet.headers)) {
|
optsToSet.headers = Object.entries(optsToSet.headers).map(([key, value]) => `${key}: ${value}`);
|
}
|
|
return native.download(localPath, optsToSet);
|
}
|
|
/**
|
* Upload a file.
|
* @param {string} url - Request URL.
|
* @param {string} localPath - Local path of the file to upload.
|
* @param {number} [timeout=30000] - Timeout in milliseconds. For backward compatibility.
|
* @param {object} [options] - Additional request options.
|
* @returns {object} Response result.
|
*/
|
httpclient.upload = function (url, localPath, timeout = 30000, options = {}) {
|
if (!url) throw new Error("URL is required");
|
if (!localPath) throw new Error("Local path is required");
|
|
const optsToSet = {
|
...options,
|
timeout: timeout, // Longer default timeout for uploads
|
url: url,
|
method: 'POST'
|
};
|
|
// Convert headers object to array of strings for C layer
|
if (optsToSet.headers && typeof optsToSet.headers === 'object' && !Array.isArray(optsToSet.headers)) {
|
optsToSet.headers = Object.entries(optsToSet.headers).map(([key, value]) => `${key}: ${value}`);
|
}
|
|
return native.upload(localPath, optsToSet);
|
}
|
|
|
// #region Deprecated Functions
|
// The following functions are deprecated and will be removed in a future version.
|
// They are kept for backward compatibility to guide users to the new stateless API.
|
|
const DEPRECATION_ERROR_MSG = "The stateful API (init, deinit, setOpt, reset) is deprecated. Please use the new stateless, function-based API by passing all options in a single object to methods like request(), get(), post(), etc.";
|
|
/**
|
* @deprecated Use the new stateless API.
|
*/
|
httpclient.init = function () {
|
// This function is now a no-op, but we don't throw an error
|
// to allow old code to run without crashing, though it's discouraged.
|
}
|
|
/**
|
* @deprecated Use the new stateless API.
|
*/
|
httpclient.deinit = function () {
|
// This function is now a no-op.
|
}
|
|
|
/**
|
* @deprecated Use the new stateless API by passing options to request() or other methods.
|
*/
|
httpclient.setOpt = function () {
|
throw new Error(DEPRECATION_ERROR_MSG);
|
}
|
|
/**
|
* @deprecated Use the new stateless API. Each call is already isolated.
|
*/
|
httpclient.reset = function () {
|
throw new Error(DEPRECATION_ERROR_MSG);
|
}
|
|
// #endregion
|
|
export default httpclient;
|