http2: add support for raw header arrays in h2Stream.respond() · nodejs/node@dafee05

@@ -2541,8 +2541,31 @@ function callStreamClose(stream) {

25412541

stream.close();

25422542

}

254325432544-

function processHeaders(oldHeaders, options) {

2545-

assertIsObject(oldHeaders, 'headers');

2544+

function prepareResponseHeaders(stream, headersParam, options) {

2545+

let headers;

2546+

let statusCode;

2547+2548+

if (ArrayIsArray(headersParam)) {

2549+

({

2550+

headers,

2551+

statusCode,

2552+

} = prepareResponseHeadersArray(headersParam, options));

2553+

stream[kRawHeaders] = headers;

2554+

} else {

2555+

({

2556+

headers,

2557+

statusCode,

2558+

} = prepareResponseHeadersObject(headersParam, options));

2559+

stream[kSentHeaders] = headers;

2560+

}

2561+2562+

const headersList = buildNgHeaderString(headers, assertValidPseudoHeaderResponse);

2563+2564+

return { headers, headersList, statusCode };

2565+

}

2566+2567+

function prepareResponseHeadersObject(oldHeaders, options) {

2568+

assertIsObject(oldHeaders, 'headers', ['Object', 'Array']);

25462569

const headers = { __proto__: null };

2547257025482571

if (oldHeaders !== null && oldHeaders !== undefined) {

@@ -2563,23 +2586,58 @@ function processHeaders(oldHeaders, options) {

25632586

headers[HTTP2_HEADER_DATE] ??= utcDate();

25642587

}

256525882589+

validatePreparedResponseHeaders(headers, statusCode);

2590+2591+

return {

2592+

headers,

2593+

statusCode: headers[HTTP2_HEADER_STATUS],

2594+

};

2595+

}

2596+2597+

function prepareResponseHeadersArray(headers, options) {

2598+

let statusCode;

2599+

let isDateSet = false;

2600+2601+

for (let i = 0; i < headers.length; i += 2) {

2602+

const header = headers[i].toLowerCase();

2603+

const value = headers[i + 1];

2604+2605+

if (header === HTTP2_HEADER_STATUS) {

2606+

statusCode = value | 0;

2607+

} else if (header === HTTP2_HEADER_DATE) {

2608+

isDateSet = true;

2609+

}

2610+

}

2611+2612+

if (!statusCode) {

2613+

statusCode = HTTP_STATUS_OK;

2614+

headers.unshift(HTTP2_HEADER_STATUS, statusCode);

2615+

}

2616+2617+

if (!isDateSet && (options.sendDate == null || options.sendDate)) {

2618+

headers.push(HTTP2_HEADER_DATE, utcDate());

2619+

}

2620+2621+

validatePreparedResponseHeaders(headers, statusCode);

2622+2623+

return { headers, statusCode };

2624+

}

2625+2626+

function validatePreparedResponseHeaders(headers, statusCode) {

25662627

// This is intentionally stricter than the HTTP/1 implementation, which

25672628

// allows values between 100 and 999 (inclusive) in order to allow for

25682629

// backwards compatibility with non-spec compliant code. With HTTP/2,

25692630

// we have the opportunity to start fresh with stricter spec compliance.

25702631

// This will have an impact on the compatibility layer for anyone using

25712632

// non-standard, non-compliant status codes.

25722633

if (statusCode < 200 || statusCode > 599)

2573-

throw new ERR_HTTP2_STATUS_INVALID(headers[HTTP2_HEADER_STATUS]);

2634+

throw new ERR_HTTP2_STATUS_INVALID(statusCode);

2574263525752636

const neverIndex = headers[kSensitiveHeaders];

25762637

if (neverIndex !== undefined && !ArrayIsArray(neverIndex))

25772638

throw new ERR_INVALID_ARG_VALUE('headers[http2.neverIndex]', neverIndex);

2578-2579-

return headers;

25802639

}

258126402582-25832641

function onFileUnpipe() {

25842642

const stream = this.sink[kOwner];

25852643

if (stream.ownsFd)

@@ -2882,7 +2940,7 @@ class ServerHttp2Stream extends Http2Stream {

28822940

}

2883294128842942

// Initiate a response on this Http2Stream

2885-

respond(headers, options) {

2943+

respond(headersParam, options) {

28862944

if (this.destroyed || this.closed)

28872945

throw new ERR_HTTP2_INVALID_STREAM();

28882946

if (this.headersSent)

@@ -2907,15 +2965,16 @@ class ServerHttp2Stream extends Http2Stream {

29072965

state.flags |= STREAM_FLAGS_HAS_TRAILERS;

29082966

}

290929672910-

headers = processHeaders(headers, options);

2911-

const headersList = buildNgHeaderString(headers, assertValidPseudoHeaderResponse);

2912-

this[kSentHeaders] = headers;

2968+

const {

2969+

headers,

2970+

headersList,

2971+

statusCode,

2972+

} = prepareResponseHeaders(this, headersParam, options);

2913297329142974

state.flags |= STREAM_FLAGS_HEADERS_SENT;

2915297529162976

// Close the writable side if the endStream option is set or status

29172977

// is one of known codes with no payload, or it's a head request

2918-

const statusCode = headers[HTTP2_HEADER_STATUS] | 0;

29192978

if (!!options.endStream ||

29202979

statusCode === HTTP_STATUS_NO_CONTENT ||

29212980

statusCode === HTTP_STATUS_RESET_CONTENT ||

@@ -2945,7 +3004,7 @@ class ServerHttp2Stream extends Http2Stream {

29453004

// regular file, here the fd is passed directly. If the underlying

29463005

// mechanism is not able to read from the fd, then the stream will be

29473006

// reset with an error code.

2948-

respondWithFD(fd, headers, options) {

3007+

respondWithFD(fd, headersParam, options) {

29493008

if (this.destroyed || this.closed)

29503009

throw new ERR_HTTP2_INVALID_STREAM();

29513010

if (this.headersSent)

@@ -2982,8 +3041,11 @@ class ServerHttp2Stream extends Http2Stream {

29823041

this[kUpdateTimer]();

29833042

this.ownsFd = false;

298430432985-

headers = processHeaders(headers, options);

2986-

const statusCode = headers[HTTP2_HEADER_STATUS] |= 0;

3044+

const {

3045+

headers,

3046+

statusCode,

3047+

} = prepareResponseHeadersObject(headersParam, options);

3048+29873049

// Payload/DATA frames are not permitted in these cases

29883050

if (statusCode === HTTP_STATUS_NO_CONTENT ||

29893051

statusCode === HTTP_STATUS_RESET_CONTENT ||

@@ -3011,7 +3073,7 @@ class ServerHttp2Stream extends Http2Stream {

30113073

// giving the user an opportunity to verify the details and set additional

30123074

// headers. If statCheck returns false, the operation is aborted and no

30133075

// file details are sent.

3014-

respondWithFile(path, headers, options) {

3076+

respondWithFile(path, headersParam, options) {

30153077

if (this.destroyed || this.closed)

30163078

throw new ERR_HTTP2_INVALID_STREAM();

30173079

if (this.headersSent)

@@ -3042,8 +3104,11 @@ class ServerHttp2Stream extends Http2Stream {

30423104

this[kUpdateTimer]();

30433105

this.ownsFd = true;

304431063045-

headers = processHeaders(headers, options);

3046-

const statusCode = headers[HTTP2_HEADER_STATUS] |= 0;

3107+

const {

3108+

headers,

3109+

statusCode,

3110+

} = prepareResponseHeadersObject(headersParam, options);

3111+30473112

// Payload/DATA frames are not permitted in these cases

30483113

if (statusCode === HTTP_STATUS_NO_CONTENT ||

30493114

statusCode === HTTP_STATUS_RESET_CONTENT ||