Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions packages/runtime-playground/src/external-http-load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,37 @@ export interface RuntimeExternalHttpLoadSample {
errorCode?: "fetch-failed" | "response-read-failed"
}

const RUNTIME_PREVIEW_READINESS_TIMEOUT_MS = 10_000
const RUNTIME_PREVIEW_READINESS_MAX_REDIRECTS = 3

export async function waitForRuntimePreviewReady(runtimeBaseUrl: string): Promise<void> {
const origin = new URL(runtimeBaseUrl)
let readinessUrl = new URL("/", origin)
const signal = AbortSignal.timeout(RUNTIME_PREVIEW_READINESS_TIMEOUT_MS)

for (let redirectCount = 0; redirectCount <= RUNTIME_PREVIEW_READINESS_MAX_REDIRECTS; redirectCount++) {
const response = await fetch(readinessUrl, { redirect: "manual", signal })
await response.arrayBuffer()
if (response.status < 300 || response.status >= 400) {
if (!response.ok) {
throw new Error(`runtime preview readiness returned HTTP ${response.status}`)
}
return
}

const location = response.headers.get("location")
if (!location) {
throw new Error("runtime preview readiness redirect is missing Location")
}
readinessUrl = new URL(location, readinessUrl)
if (readinessUrl.origin !== origin.origin) {
throw new Error("runtime preview readiness redirect leaves the preview origin")
}
}

throw new Error(`runtime preview readiness exceeded ${RUNTIME_PREVIEW_READINESS_MAX_REDIRECTS} same-origin redirects`)
}

export async function runRuntimeExternalHttpLoad(action: Record<string, unknown>, runtimeBaseUrl?: string): Promise<RuntimeExternalHttpLoadResult> {
if (!runtimeBaseUrl) {
throw new Error("external_http_load requires an active runtime preview origin")
Expand Down
5 changes: 4 additions & 1 deletion packages/runtime-playground/src/wordpress-command-runners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ import type { PlaygroundCliServer } from "./preview-server.js"
import { persistCorePhpunitResult, persistPluginPhpunitCompletedResult, persistPluginPhpunitResult, persistVfsDiagnosticFileToHost, readCorePhpunitDiagnostic, readPluginPhpunitCompletedResult, readPluginPhpunitDiagnostic, readPluginPhpunitDiscoveryResult } from "./runtime-diagnostics.js"
import { phpunitExecutionSemantics, requiresManagedMysqlMultisitePreinstall } from "./phpunit-command-semantics.js"
import { parsePhpunitOutput } from "./phpunit-test-results.js"
import { runRuntimeExternalHttpLoad, type RuntimeExternalHttpLoadResult } from "./external-http-load.js"
import { runRuntimeExternalHttpLoad, waitForRuntimePreviewReady, type RuntimeExternalHttpLoadResult } from "./external-http-load.js"
import type { RuntimeWpCliBridge } from "./runtime-wp-cli-bridge.js"
import { COMMAND_DIAGNOSTICS_ARTIFACT_SCHEMA, PERFORMANCE_OBSERVATION_SCHEMA, commandDiagnosticsCaptureArgs, commandDiagnosticsCaptureSpecFromArgs, createRuntimeCommandResultEnvelope, redactJsonValue, type ExecutionSpec, type MountSpec, type PerformanceObservation, type RuntimeCommandResultEnvelope, type RuntimeCreateSpec, type RuntimeEpisodeTraceRef } from "@automattic/wp-codebox-core"
import { wordpressUserSessionFromCommandArgs } from "./wordpress-user-sessions.js"
Expand Down Expand Up @@ -1176,6 +1176,9 @@ async function benchMergeExternalHttpLoadResults(
return text
}

// Playground's one-time auto-login redirect belongs to startup, not a measured sample.
await waitForRuntimePreviewReady(options.baseUrl)

const results = JSON.parse(text) as { schema?: string; scenarios?: Array<Record<string, any>>; provenance?: Record<string, any> }
if (results.schema !== "wp-codebox/bench-results/v1" || !Array.isArray(results.scenarios)) {
throw new Error("external-http-load could not merge into an invalid wordpress.bench result")
Expand Down
25 changes: 24 additions & 1 deletion tests/external-http-load.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import assert from "node:assert/strict"
import { createServer } from "node:http"
import { runRuntimeExternalHttpLoad } from "../packages/runtime-playground/src/external-http-load.ts"
import { runRuntimeExternalHttpLoad, waitForRuntimePreviewReady } from "../packages/runtime-playground/src/external-http-load.ts"

let activeRequests = 0
let maxActiveRequests = 0
Expand All @@ -9,6 +9,7 @@ let receivedMethod = ""
let receivedBody = ""
let receivedSecret = ""
let redirectTargetRequests = 0
let readinessRequests = 0

const redirectTarget = createServer((_request, response) => {
redirectTargetRequests++
Expand All @@ -18,7 +19,23 @@ await listen(redirectTarget)
const redirectTargetAddress = redirectTarget.address()
assert.ok(redirectTargetAddress && typeof redirectTargetAddress === "object")

const readinessRedirector = createServer((_request, response) => {
response.writeHead(302, { location: `http://127.0.0.1:${redirectTargetAddress.port}/outside` }).end()
})
await listen(readinessRedirector)
const readinessRedirectorAddress = readinessRedirector.address()
assert.ok(readinessRedirectorAddress && typeof readinessRedirectorAddress === "object")

const runtime = createServer(async (request, response) => {
if (request.url === "/") {
readinessRequests++
if (readinessRequests === 1) {
response.writeHead(302, { location: "/" }).end()
return
}
response.writeHead(204).end()
return
}
if (request.url === "/broken") {
response.destroy()
return
Expand Down Expand Up @@ -51,6 +68,11 @@ assert.ok(runtimeAddress && typeof runtimeAddress === "object")
const runtimeUrl = `http://127.0.0.1:${runtimeAddress.port}`

try {
await waitForRuntimePreviewReady(runtimeUrl)
assert.equal(readinessRequests, 2)
await assert.rejects(waitForRuntimePreviewReady(`http://127.0.0.1:${readinessRedirectorAddress.port}`), /leaves the preview origin/)
assert.equal(redirectTargetRequests, 0)

const matched = await runRuntimeExternalHttpLoad({
url: "/matched",
method: "POST",
Expand Down Expand Up @@ -124,6 +146,7 @@ try {
assert.equal(redirectTargetRequests, 0)
} finally {
await close(runtime)
await close(readinessRedirector)
await close(redirectTarget)
}

Expand Down
Loading