Skip to content
File

Blob: src/node/internal/internal_http_util.ts

typescript60 lines
1// Copyright (c) 2017-2022 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4// Copyright Joyent and Node contributors. All rights reserved. MIT license.
5import type { IncomingMessage } from 'node:http';
6 
7export type IncomingMessageCallback = (req: IncomingMessage) => void;
8 
9interface OnceOptions {
10 preserveReturnValue?: boolean;
11}
12 
13export function once<RT>(
14 this: unknown,
15 callback: (...allArgs: unknown[]) => RT,
16 options?: OnceOptions
17): (...all: unknown[]) => RT {
18 const preserveReturnValue = options?.preserveReturnValue ?? false;
19 let called = false;
20 let returnValue: RT;
21 return function (this: unknown, ...args: unknown[]): RT {
22 if (called) return returnValue;
23 called = true;
24 const result = Reflect.apply(callback, this, args);
25 returnValue = preserveReturnValue ? result : (undefined as RT);
26 return result;
27 };
28}
29 
30export const kServerResponse = Symbol('ServerResponse');
31export const kIncomingMessage = Symbol('IncomingMessage');
32 
33// RFC 7230 compliant header value splitting that respects quoted-string constructions
34// Returns only the first value up to the first comma (outside of quoted strings)
35// Spec: https://www.rfc-editor.org/rfc/rfc7230.html#appendix-B
36// Edge cases handled:
37// - Quoted strings with commas: 'text/plain; param="a, b"' -> 'text/plain; param="a, b"'
38// - Escaped quotes in strings: 'text/plain; param="a \"quoted\" value"' -> 'text/plain; param="a \"quoted\" value"'
39// - Multiple values: 'text/plain; f="a, b", text/html' -> 'text/plain; f="a, b"'
40// - Whitespace before commas: 'value1 \t , value2' -> 'value1'
41// - No commas: 'single-value' -> 'single-value'
42export function splitHeaderValue(value: string): string {
43 let inQuotes = false;
44 
45 for (let i = 0; i < value.length; i++) {
46 const char = value[i];
47 
48 if (char === '"') {
49 inQuotes = !inQuotes;
50 } else if (char === '\\' && inQuotes) {
51 i++; // Skip next character (it's escaped)
52 } else if (!inQuotes && char === ',') {
53 // Found unquoted comma, return
54 return value.slice(0, i);
55 }
56 }
57 
58 return value;
59}