@@ -3,6 +3,7 @@
|
3 | 3 | // Probes gateway state for upgrade-survivor E2E scenarios. |
4 | 4 | import fs from "node:fs"; |
5 | 5 | import path from "node:path"; |
| 6 | +import { readBoundedResponseText } from "../../../lib/bounded-response.mjs"; |
6 | 7 | |
7 | 8 | const args = process.argv.slice(2); |
8 | 9 | |
@@ -104,47 +105,29 @@ function matchesDegradedReadyExpectation(body) {
|
104 | 105 | ); |
105 | 106 | } |
106 | 107 | |
107 | | -async function readBoundedResponseText(response, byteLimit) { |
108 | | -const contentLength = response.headers?.get?.("content-length"); |
109 | | -if (contentLength && /^\d+$/u.test(contentLength)) { |
110 | | -const parsedContentLength = Number(contentLength); |
111 | | -if (Number.isSafeInteger(parsedContentLength) && parsedContentLength > byteLimit) { |
112 | | -await response.body?.cancel().catch(() => undefined); |
113 | | -throw new Error(`${url} probe body exceeded ${byteLimit} bytes`); |
114 | | -} |
115 | | -} |
116 | | - |
117 | | -const reader = response.body?.getReader(); |
118 | | -if (!reader) { |
119 | | -return ""; |
120 | | -} |
121 | | -const chunks = []; |
122 | | -let totalBytes = 0; |
123 | | -for (;;) { |
124 | | -const { done, value } = await reader.read(); |
125 | | -if (done) { |
126 | | -break; |
127 | | -} |
128 | | -totalBytes += value.byteLength; |
129 | | -if (totalBytes > byteLimit) { |
130 | | -await reader.cancel(); |
131 | | -throw new Error(`${url} probe body exceeded ${byteLimit} bytes`); |
132 | | -} |
133 | | -chunks.push(Buffer.from(value)); |
134 | | -} |
135 | | -return Buffer.concat(chunks, totalBytes).toString("utf8"); |
136 | | -} |
137 | | - |
138 | 108 | async function fetchProbeText() { |
139 | 109 | const elapsedMs = Date.now() - startedAt; |
140 | 110 | const remainingMs = Math.max(1, timeoutMs - elapsedMs); |
141 | 111 | const controller = new AbortController(); |
142 | | -const timer = setTimeout(() => controller.abort(), Math.min(attemptTimeoutMs, remainingMs)); |
| 112 | +const attemptDeadlineMs = Math.min(attemptTimeoutMs, remainingMs); |
| 113 | +let timer; |
| 114 | +const timeoutPromise = new Promise((_resolve, reject) => { |
| 115 | +timer = setTimeout(() => { |
| 116 | +reject(new Error(`${url} probe attempt timed out after ${attemptDeadlineMs}ms`)); |
| 117 | +controller.abort(); |
| 118 | +}, attemptDeadlineMs); |
| 119 | +}); |
143 | 120 | try { |
144 | | -const response = await fetch(url, { method: "GET", signal: controller.signal }); |
| 121 | +const response = await Promise.race([ |
| 122 | +fetch(url, { method: "GET", signal: controller.signal }), |
| 123 | +timeoutPromise, |
| 124 | +]); |
145 | 125 | return { |
146 | 126 | response, |
147 | | -text: await readBoundedResponseText(response, maxBodyBytes), |
| 127 | +text: await readBoundedResponseText(response, `${url} probe`, maxBodyBytes, { |
| 128 | +formatTooLargeMessage: (_label, bytes) => `${url} probe body exceeded ${bytes} bytes`, |
| 129 | + timeoutPromise, |
| 130 | +}), |
148 | 131 | }; |
149 | 132 | } finally { |
150 | 133 | clearTimeout(timer); |
|