diff --git a/src/core/drive/form_submission.ts b/src/core/drive/form_submission.ts index 66bc3441b..01e6bfe06 100644 --- a/src/core/drive/form_submission.ts +++ b/src/core/drive/form_submission.ts @@ -104,16 +104,14 @@ export class FormSubmission { // Fetch request delegate - additionalHeadersForRequest(request: FetchRequest) { - const headers: FetchRequestHeaders = {} + prepareHeadersForRequest(headers: FetchRequestHeaders, request: FetchRequest) { if (!request.isIdempotent) { const token = getCookieValue(getMetaContent("csrf-param")) || getMetaContent("csrf-token") if (token) { headers["X-CSRF-Token"] = token } - headers["Accept"] = StreamMessage.contentType + headers["Accept"] = [ StreamMessage.contentType, headers["Accept"] ].join(", ") } - return headers } requestStarted(request: FetchRequest) { diff --git a/src/core/frames/frame_controller.ts b/src/core/frames/frame_controller.ts index ab6b7ed92..d0d507518 100644 --- a/src/core/frames/frame_controller.ts +++ b/src/core/frames/frame_controller.ts @@ -1,5 +1,5 @@ import { FrameElement, FrameElementDelegate, FrameLoadingStyle } from "../../elements/frame_element" -import { FetchMethod, FetchRequest, FetchRequestDelegate } from "../../http/fetch_request" +import { FetchMethod, FetchRequest, FetchRequestDelegate, FetchRequestHeaders } from "../../http/fetch_request" import { FetchResponse } from "../../http/fetch_response" import { AppearanceObserver, AppearanceObserverDelegate } from "../../observers/appearance_observer" import { parseHTMLDocument } from "../../util" @@ -124,8 +124,8 @@ export class FrameController implements AppearanceObserverDelegate, FetchRequest // Fetch request delegate - additionalHeadersForRequest(request: FetchRequest) { - return { "Turbo-Frame": this.id } + prepareHeadersForRequest(headers: FetchRequestHeaders, request: FetchRequest) { + headers["Turbo-Frame"] = this.id } requestStarted(request: FetchRequest) { diff --git a/src/http/fetch_request.ts b/src/http/fetch_request.ts index 326b5b440..4c6c90462 100644 --- a/src/http/fetch_request.ts +++ b/src/http/fetch_request.ts @@ -2,7 +2,7 @@ import { FetchResponse } from "./fetch_response" import { dispatch } from "../util" export interface FetchRequestDelegate { - additionalHeadersForRequest?(request: FetchRequest): { [header: string]: string } + prepareHeadersForRequest?(headers: FetchRequestHeaders, request: FetchRequest): void requestStarted(request: FetchRequest): void requestPreventedHandlingResponse(request: FetchRequest, response: FetchResponse): void requestSucceededWithResponse(request: FetchRequest, response: FetchResponse): void @@ -117,23 +117,22 @@ export class FetchRequest { } get headers() { - return { - "Accept": "text/html, application/xhtml+xml", - ...this.additionalHeaders - } - } - - get additionalHeaders() { - if (typeof this.delegate.additionalHeadersForRequest == "function") { - return this.delegate.additionalHeadersForRequest(this) - } else { - return {} + const headers = { ...this.defaultHeaders } + if (typeof this.delegate.prepareHeadersForRequest == "function") { + this.delegate.prepareHeadersForRequest(headers, this) } + return headers } get abortSignal() { return this.abortController.signal } + + get defaultHeaders() { + return { + "Accept": "text/html, application/xhtml+xml" + } + } } function mergeFormDataEntries(url: URL, entries: [string, FormDataEntryValue][]): URL { diff --git a/src/tests/fixtures/stream.html b/src/tests/fixtures/stream.html index d036f201c..3c02f6c59 100644 --- a/src/tests/fixtures/stream.html +++ b/src/tests/fixtures/stream.html @@ -7,6 +7,11 @@ +
+ + + +
First
diff --git a/src/tests/functional/stream_tests.ts b/src/tests/functional/stream_tests.ts index 4a83bcaa6..5bf205ffc 100644 --- a/src/tests/functional/stream_tests.ts +++ b/src/tests/functional/stream_tests.ts @@ -12,22 +12,12 @@ export class StreamTests extends FunctionalTestCase { element = await this.querySelector(selector) this.assert.equal(await element.getVisibleText(), "First") - await this.createMessage("Hello world!") + await this.clickSelector("#create [type=submit]") await this.nextBeat element = await this.querySelector(selector) this.assert.equal(await element.getVisibleText(), "Hello world!") } - - async createMessage(content: string) { - return this.post("/__turbo/messages", { content }) - } - - async post(path: string, params: any = {}) { - await this.evaluate((path, method, params) => { - fetch(location.origin + path, { method, body: new URLSearchParams(params) }) - }, path, "POST", params) - } } StreamTests.registerSuite() diff --git a/src/tests/server.ts b/src/tests/server.ts index 2c815db0d..318f1b35b 100644 --- a/src/tests/server.ts +++ b/src/tests/server.ts @@ -1,4 +1,4 @@ -import { Response, Router } from "express" +import { Request, Response, Router } from "express" import multer from "multer" import path from "path" import url from "url" @@ -8,6 +8,14 @@ const streamResponses: Set = new Set router.use(multer().none()) +router.use((request, response, next) => { + if (request.accepts(["text/html", "application/xhtml+xml"])) { + next() + } else { + response.sendStatus(422) + } +}) + router.post("/redirect", (request, response) => { const { path, ...query } = request.body const pathname = path ?? "/src/tests/fixtures/one.html" @@ -39,7 +47,7 @@ router.post("/messages", (request, response) => { const { content, status, type } = request.body if (typeof content == "string") { receiveMessage(content) - if (type == "stream") { + if (type == "stream" && acceptsStreams(request)) { response.type("text/vnd.turbo-stream.html; charset=utf-8") response.send(renderMessage(content)) } else { @@ -55,7 +63,7 @@ router.put("/messages/:id", (request, response) => { const { id } = request.params if (typeof content == "string") { receiveMessage(content) - if (type == "stream") { + if (type == "stream" && acceptsStreams(request)) { response.type("text/vnd.turbo-stream.html; charset=utf-8") response.send(renderMessage(id + ": " + content)) } else { @@ -99,6 +107,10 @@ function renderMessage(content: string) { ` } +function acceptsStreams(request: Request): boolean { + return !!request.accepts("text/vnd.turbo-stream.html") +} + function renderSSEData(data: any) { return `${data}`.split("\n").map(line => "data:" + line).join("\n") + "\n\n" }