UNPKG

@nasriya/hypercloud

Version:

Nasriya HyperCloud is a lightweight Node.js HTTP2 framework.

143 lines (142 loc) 5.18 kB
import helpers from '../../../utils/helpers.js'; import RequestBody from './requestBody.js'; /** * Convert the query back to string * @param {string} q The query object */ export function buildQuery(q) { let query = ''; for (const [key, value] of Object.entries(q)) { if (query.length > 0) { query = `${query}&`; } query = `${query}${key}=${value}`; } return Object.keys(query).length > 0 ? `?${query}` : ''; } export function cookieParser(rawCookieHeader) { // Parse the raw cookie header into an object const cookies = {}; if (rawCookieHeader && typeof rawCookieHeader === 'string') { rawCookieHeader.split(';').forEach(cookie => { const parts = cookie.split('='); const name = parts.shift().trim(); const value = decodeURIComponent(parts.join('=')).trim(); cookies[name] = value; }); } return cookies; } /** * Parse the request body * @param {any} body * @param {string} contentType * @returns {BodyParserResult} */ export function bodyParser(body, contentType) { /**@type {BodyParserResult} */ const request = { body, bodyType: null }; // Handle different types of bodies if (contentType.includes('text/plain') || contentType.includes('text/html') || contentType.includes('application/xml')) { request.bodyType = 'text'; return request; } if (contentType.includes('application/javascript')) { request.bodyType = 'javascript'; return request; } if (contentType.includes('application/json')) { try { const jsonData = JSON.parse(request.body); request.body = new RequestBody().from(jsonData); request.bodyType = 'json'; return request; } catch (error) { console.error('Error parsing JSON:', error); throw error; } } if (contentType.includes('application/x-www-form-urlencoded')) { // Parse the form data const sections = request.body.split('&').filter(i => i.length > 0); const body = new RequestBody(); for (const section of sections) { const [key, value] = section.split('='); body.set(key, value); } request.body = body._toJSON(); request.bodyType = 'json'; return request; } if (contentType.includes('application/octet-stream')) { request.bodyType = 'buffer'; // Handle binary data return request; } if (contentType.includes('application/graphql')) { request.bodyType = 'graphql'; return request; } if (contentType.includes('application/vnd.apple.mpegurl')) { request.bodyType = 'hls'; return request; } // Handle other types of raw data request.bodyType = 'buffer'; return request; } /** * Extract the IP address from the request. If priority of choosing the IP is: 1) `X-Real-IP`, 2) `x-forwarded-for`, and 3) The actual remote address * @param {http2.Http2ServerRequest} req The HTTP2 request * @param {string[]} [trusted_proxies] The trusted proxy IPs * @returns {string} */ export async function getClientIP(req, trusted_proxies) { const local_ips = await helpers.getLocalIPs(); trusted_proxies = [...new Set([...trusted_proxies, ...local_ips])]; trusted_proxies.sort(); /**The `X-Real-IP` (if present) */ const realIP = (() => { if (req.headers['X-Real-IP']) { const xReal = req.headers['X-Real-IP']; const real = Array.isArray(xReal) ? xReal[0] : xReal; if (helpers.validate.ipAddress(real)) { return real === '::1' ? local_ips[0] : real; } else { helpers.printConsole(`The value of the 'X-Real-IP' header is invalid. Expected a valid IP address but got ${real}`); } } return null; })(); // If real IP found, return it. if (realIP) { return realIP; } /**The `remoteAddress` IP */ const remoteAddress = (() => { if (req.socket.remoteAddress === '::1') { return local_ips[0]; } if (req.socket.remoteAddress?.includes('::ffff:')) { return req.socket.remoteAddress.replace('::ffff:', ''); } return req.socket.remoteAddress || 'UnknownIP'; })(); if (Array.isArray(trusted_proxies) && trusted_proxies.includes(remoteAddress)) { // Check if the request has the X-Forwarded-For header const forwardedFor = req.headers['x-forwarded-for']; if (typeof forwardedFor === 'string' && helpers.is.validString(forwardedFor)) { // The header may contain multiple IP addresses separated by commas // The client's IP address is usually the first one in the list const ipAddresses = forwardedFor.split(','); return ipAddresses[0].trim(); } else if (Array.isArray(forwardedFor)) { return forwardedFor[0]; } } // If the X-Forwarded-For header is not present, fallback to the remote address return remoteAddress; }