Use multi-value headers in netlify adapter and in server responses.

This commit is contained in:
Dillon Kearns 2023-05-15 11:26:48 -07:00
parent a0f43794c0
commit 5696c42a69
5 changed files with 46 additions and 24 deletions

View File

@ -108,17 +108,15 @@ export const handler = render;`
async function render(event, context) {
try {
const renderResult = await elmPages.render(await reqToJson(event));
const statusCode = renderResult.statusCode;
const headers = renderResult.headers;
const { headers, statusCode } = renderResult;
if (renderResult.kind === "bytes") {
return {
body: Buffer.from(renderResult.body).toString("base64"),
isBase64Encoded: true,
multiValueHeaders: {
"Content-Type": "application/octet-stream",
"x-powered-by": "elm-pages",
"Content-Type": ["application/octet-stream"],
"x-powered-by": ["elm-pages"],
...headers,
},
statusCode,
@ -134,8 +132,8 @@ async function render(event, context) {
return {
body: renderResult.body,
multiValueHeaders: {
"Content-Type": "text/html",
"x-powered-by": "elm-pages",
"Content-Type": ["text/html"],
"x-powered-by": ["elm-pages"],
...headers,
},
statusCode,
@ -147,9 +145,9 @@ async function render(event, context) {
return {
body: \`<body><h1>Error</h1><pre>\${JSON.stringify(error, null, 2)}</pre></body>\`,
statusCode: 500,
headers: {
"Content-Type": "text/html",
"x-powered-by": "elm-pages",
multiValueHeaders: {
"Content-Type": ["text/html"],
"x-powered-by": ["elm-pages"],
},
};
}

View File

@ -16,7 +16,7 @@
},
"../..": {
"name": "elm-pages",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.39",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {

View File

@ -534,9 +534,9 @@ export async function start(options) {
/<!-- ROOT -->\S*<html lang="en">/m,
info.rootElement
);
setHeaders(res, renderResult.headers);
res.writeHead(renderResult.statusCode, {
"Content-Type": "text/html",
...renderResult.headers,
});
res.end(renderedHtml);
} catch (e) {
@ -548,10 +548,8 @@ export async function start(options) {
case "api-response": {
if (renderResult.body.kind === "server-response") {
const serverResponse = renderResult.body;
res.writeHead(
serverResponse.statusCode,
serverResponse.headers
);
setHeaders(res, serverResponse.headers);
res.writeHead(serverResponse.statusCode);
res.end(serverResponse.body);
} else if (renderResult.body.kind === "static-file") {
let mimeType = serveStatic.mime.lookup(pathname || "text/html");
@ -594,6 +592,16 @@ export async function start(options) {
});
}
/**
* @param { http.ServerResponse } res
* @param {{ [key: string]: string[]; }} headers
*/
function setHeaders(res, headers) {
Object.keys(headers).forEach(function (key) {
res.setHeader(key, headers[key]);
});
}
/**
* @param {string} reviewReportJsonString
*/

View File

@ -11,7 +11,7 @@ import BuildError exposing (BuildError)
import Bytes exposing (Bytes)
import Bytes.Encode
import Codec
import Dict
import Dict exposing (Dict)
import FatalError exposing (FatalError)
import Head exposing (Tag)
import Html exposing (Html)
@ -583,6 +583,7 @@ initLegacy site ((RenderRequest.SinglePage includeHtml singleRequest _) as rende
, headers =
-- TODO should `responseInfo.headers` be used? Is there a problem in the case where there is both an action and data response in one? Do we need to make sure it is performed as two separate HTTP requests to ensure that the cookies are set correctly in that case?
actionHeaders
|> combineHeaders
}
|> ToJsPayload.PageProgress
|> Effect.SendSinglePageNew encodedData
@ -665,7 +666,7 @@ initLegacy site ((RenderRequest.SinglePage includeHtml singleRequest _) as rende
RenderRequest.HtmlAndJson ->
config.errorStatusCode error
, headers = record.headers
, headers = record.headers |> combineHeaders
}
|> ToJsPayload.PageProgress
|> Effect.SendSinglePageNew encodedData
@ -760,7 +761,7 @@ initLegacy site ((RenderRequest.SinglePage includeHtml singleRequest _) as rende
, staticHttpCache = Dict.empty
, is404 = False
, statusCode = statusCode
, headers = []
, headers = Dict.empty
}
|> ToJsPayload.PageProgress
|> Effect.SendSinglePageNew byteEncodedPageData
@ -966,7 +967,7 @@ render404Page config sharedData isDevServer path notFoundReason =
, staticHttpCache = Dict.empty
, is404 = True
, statusCode = 404
, headers = []
, headers = Dict.empty
}
|> ToJsPayload.PageProgress
|> Effect.SendSinglePageNew byteEncodedPageData
@ -998,7 +999,7 @@ render404Page config sharedData isDevServer path notFoundReason =
--model.allRawResponses |> Dict.Extra.filterMap (\_ v -> v)
, is404 = True
, statusCode = 404
, headers = []
, headers = Dict.empty
}
|> ToJsPayload.PageProgress
|> Effect.SendSinglePageNew byteEncodedPageData
@ -1059,8 +1060,23 @@ toRedirectResponse config serverRequestPayload includeHtml serverResponse respon
RenderRequest.HtmlAndJson ->
responseMetadata.statusCode
, headers = responseMetadata.headers --serverResponse.headers
, headers = responseMetadata.headers |> combineHeaders
}
|> ToJsPayload.PageProgress
|> Effect.SendSinglePageNew byteEncodedPageData
)
combineHeaders : List ( String, String ) -> Dict String (List String)
combineHeaders headers =
headers
|> List.foldl
(\( key, value ) dict ->
Dict.update key
(Maybe.map ((::) value)
>> Maybe.withDefault [ value ]
>> Just
)
dict
)
Dict.empty

View File

@ -25,7 +25,7 @@ type alias ToJsSuccessPayloadNew =
, staticHttpCache : Dict String String
, is404 : Bool
, statusCode : Int
, headers : List ( String, String )
, headers : Dict String (List String)
}
@ -76,7 +76,7 @@ successCodecNew canonicalSiteUrl currentPagePath =
|> Codec.field "statusCode" .statusCode Codec.int
|> Codec.field "headers"
.headers
(Codec.dict Codec.string |> Codec.map Dict.toList Dict.fromList)
(Codec.dict (Codec.list Codec.string))
|> Codec.buildObject