UNPKG

@opra/client

Version:

Opra Client package

93 lines (92 loc) 3.33 kB
import { PassThrough } from 'node:stream'; const CRLF = '\r\n'; export function urlPath(strings, ...values) { let str = ''; let i; for (i = 0; i < strings.length; i++) { str += strings[0] + encodeURIComponent(values[i]); } return str; } /** * Serializes an HTTP request context into raw HTTP/1.1 bytes. * Returns a Buffer for synchronous body types (null, string, Buffer, plain object) * or a PassThrough stream for readable/stream bodies. */ export function serializeHttpRequest(ctx) { const url = ctx.url instanceof URL ? ctx.url : new URL(String(ctx.url)); const requestLine = `${ctx.method} ${url.pathname}${url.search} HTTP/1.1${CRLF}`; const rawBody = ctx.body; let bodyBuffer; let defaultContentType; if (rawBody == null) { bodyBuffer = Buffer.alloc(0); } else if (typeof rawBody === 'string') { bodyBuffer = Buffer.from(rawBody, 'utf-8'); defaultContentType = 'text/plain; charset="UTF-8"'; } else if (typeof rawBody === 'number' || typeof rawBody === 'boolean') { bodyBuffer = Buffer.from(String(rawBody), 'utf-8'); defaultContentType = 'text/plain; charset="UTF-8"'; } else if (Buffer.isBuffer(rawBody)) { bodyBuffer = rawBody; defaultContentType = 'application/octet-stream'; } else if ( /* Node.js Readable */ typeof rawBody.pipe === 'function' || /* Web ReadableStream */ typeof rawBody.getReader === 'function') { /* Streaming body — write header preamble then pipe body */ const pass = new PassThrough(); let headerLines = requestLine; ctx.headers.forEach((value, name) => { headerLines += `${name}: ${value}${CRLF}`; }); headerLines += CRLF; pass.write(Buffer.from(headerLines, 'utf-8')); if (typeof rawBody.pipe === 'function') { rawBody.pipe(pass); } else { /* Web ReadableStream */ (async () => { try { for await (const chunk of rawBody) { pass.write(chunk); } pass.end(); } catch (err) { pass.destroy(err); } })(); } return pass; } else { /* Plain object → JSON */ bodyBuffer = Buffer.from(JSON.stringify(rawBody), 'utf-8'); defaultContentType = 'application/json; charset="UTF-8"'; } /* Build header section */ let headerLines = requestLine; let hasContentType = false; let hasContentLength = false; ctx.headers.forEach((value, name) => { const lower = name.toLowerCase(); if (lower === 'content-type') hasContentType = true; if (lower === 'content-length') hasContentLength = true; headerLines += `${name}: ${value}${CRLF}`; }); if (defaultContentType && !hasContentType && bodyBuffer.length > 0) { headerLines += `Content-Type: ${defaultContentType}${CRLF}`; } if (!hasContentLength && bodyBuffer.length > 0) { headerLines += `Content-Length: ${bodyBuffer.length}${CRLF}`; } headerLines += CRLF; return Buffer.concat([Buffer.from(headerLines, 'utf-8'), bodyBuffer]); }