Skip to content

Commit

Permalink
fix(fromTraffic): clone unique handler responses
Browse files Browse the repository at this point in the history
Repeated calls to unique request handlers would return unreadable
responses that were already consumed. By cloning the response before
returning, each returned response is able to be read as normal.

Fixes #66
  • Loading branch information
danielegarciav committed Oct 25, 2024
1 parent 9728e37 commit d45c219
Show file tree
Hide file tree
Showing 4 changed files with 381 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/traffic/from-traffic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export function fromTraffic(
await delay(entry.time)
}

return response
return isUniqueHandler ? response.clone() : response
},
{
once: !isUniqueHandler,
Expand Down
344 changes: 344 additions & 0 deletions test/traffic/fixtures/archives/response-cloning.har
Original file line number Diff line number Diff line change
@@ -0,0 +1,344 @@
{
"log": {
"version": "1.2",
"creator": {
"name": "WebInspector",
"version": "537.36"
},
"pages": [],
"entries": [
{
"_initiator": {
"type": "script",
"stack": {
"callFrames": [
{
"functionName": "eval",
"scriptId": "13",
"url": "",
"lineNumber": 3,
"columnNumber": 39
},
{
"functionName": "eval",
"scriptId": "13",
"url": "",
"lineNumber": 1,
"columnNumber": 56
},
{
"functionName": "evaluate",
"scriptId": "6",
"url": "",
"lineNumber": 340,
"columnNumber": 15
},
{
"functionName": "",
"scriptId": "12",
"url": "",
"lineNumber": 0,
"columnNumber": 43
}
]
}
},
"_priority": "High",
"_resourceType": "fetch",
"cache": {},
"connection": "29",
"request": {
"method": "GET",
"url": "http://127.0.0.1:51345/resource",
"httpVersion": "HTTP/1.1",
"headers": [
{
"name": "Host",
"value": "127.0.0.1:51345"
},
{
"name": "Connection",
"value": "keep-alive"
},
{
"name": "sec-ch-ua",
"value": "\"Chromium\";v=\"93\", \" Not;A Brand\";v=\"99\""
},
{
"name": "sec-ch-ua-mobile",
"value": "?0"
},
{
"name": "User-Agent",
"value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4576.0 Safari/537.36"
},
{
"name": "sec-ch-ua-platform",
"value": "\"macOS\""
},
{
"name": "Accept",
"value": "*/*"
},
{
"name": "Origin",
"value": "null"
},
{
"name": "Sec-Fetch-Site",
"value": "cross-site"
},
{
"name": "Sec-Fetch-Mode",
"value": "cors"
},
{
"name": "Sec-Fetch-Dest",
"value": "empty"
},
{
"name": "Accept-Encoding",
"value": "gzip, deflate, br"
},
{
"name": "Accept-Language",
"value": "en-US,en;q=0.9"
}
],
"queryString": [],
"cookies": [],
"headersSize": 477,
"bodySize": 0
},
"response": {
"status": 200,
"statusText": "OK",
"httpVersion": "HTTP/1.1",
"headers": [
{
"name": "X-Powered-By",
"value": "Express"
},
{
"name": "Access-Control-Allow-Origin",
"value": "*"
},
{
"name": "Content-Type",
"value": "text/html; charset=utf-8"
},
{
"name": "Content-Length",
"value": "14"
},
{
"name": "ETag",
"value": "W/\"e-lDXCsB3mEANLxMr+ZDknI3O1vrg\""
},
{
"name": "Date",
"value": "Sun, 15 Aug 2021 13:41:24 GMT"
},
{
"name": "Connection",
"value": "keep-alive"
}
],
"cookies": [],
"content": {
"size": 14,
"mimeType": "text/html",
"compression": 0,
"text": "one"
},
"redirectURL": "",
"headersSize": 236,
"bodySize": 14,
"_transferSize": 250,
"_error": null
},
"serverIPAddress": "127.0.0.1",
"startedDateTime": "2021-08-15T13:41:24.681Z",
"time": 6.774000000096828,
"timings": {
"blocked": 0.7860000000414147,
"dns": 0.013000000000000012,
"ssl": -1,
"connect": 0.38500000000000006,
"send": 0.07999999999999996,
"wait": 5.130000000105326,
"receive": 0.37999999995008693,
"_blocked_queueing": 0.6140000000414148
}
},
{
"_initiator": {
"type": "script",
"stack": {
"callFrames": [
{
"functionName": "eval",
"scriptId": "13",
"url": "",
"lineNumber": 3,
"columnNumber": 39
},
{
"functionName": "eval",
"scriptId": "13",
"url": "",
"lineNumber": 1,
"columnNumber": 56
},
{
"functionName": "evaluate",
"scriptId": "6",
"url": "",
"lineNumber": 340,
"columnNumber": 15
},
{
"functionName": "",
"scriptId": "12",
"url": "",
"lineNumber": 0,
"columnNumber": 43
}
]
}
},
"_priority": "High",
"_resourceType": "fetch",
"cache": {},
"connection": "34",
"request": {
"method": "GET",
"url": "http://127.0.0.1:51345/resource",
"httpVersion": "HTTP/1.1",
"headers": [
{
"name": "Host",
"value": "127.0.0.1:51345"
},
{
"name": "Connection",
"value": "keep-alive"
},
{
"name": "sec-ch-ua",
"value": "\"Chromium\";v=\"93\", \" Not;A Brand\";v=\"99\""
},
{
"name": "sec-ch-ua-mobile",
"value": "?0"
},
{
"name": "User-Agent",
"value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4576.0 Safari/537.36"
},
{
"name": "sec-ch-ua-platform",
"value": "\"macOS\""
},
{
"name": "Accept",
"value": "*/*"
},
{
"name": "Origin",
"value": "null"
},
{
"name": "Sec-Fetch-Site",
"value": "cross-site"
},
{
"name": "Sec-Fetch-Mode",
"value": "cors"
},
{
"name": "Sec-Fetch-Dest",
"value": "empty"
},
{
"name": "Accept-Encoding",
"value": "gzip, deflate, br"
},
{
"name": "Accept-Language",
"value": "en-US,en;q=0.9"
},
{
"name": "If-None-Match",
"value": "W/\"e-lDXCsB3mEANLxMr+ZDknI3O1vrg\""
}
],
"queryString": [],
"cookies": [],
"headersSize": 527,
"bodySize": 0
},
"response": {
"status": 200,
"statusText": "OK",
"httpVersion": "HTTP/1.1",
"headers": [
{
"name": "X-Powered-By",
"value": "Express"
},
{
"name": "Access-Control-Allow-Origin",
"value": "*"
},
{
"name": "Content-Type",
"value": "text/html; charset=utf-8"
},
{
"name": "Content-Length",
"value": "14"
},
{
"name": "ETag",
"value": "W/\"e-3dlKK58QOHvviPbNHbANXgv8hSk\""
},
{
"name": "Date",
"value": "Sun, 15 Aug 2021 13:41:24 GMT"
},
{
"name": "Connection",
"value": "keep-alive"
}
],
"cookies": [],
"content": {
"size": 14,
"mimeType": "text/html",
"compression": 0,
"text": "two"
},
"redirectURL": "",
"headersSize": 236,
"bodySize": 14,
"_transferSize": 250,
"_error": null
},
"serverIPAddress": "127.0.0.1",
"startedDateTime": "2021-08-15T13:41:24.681Z",
"time": 7.915999999921842,
"timings": {
"blocked": 6.177999999940497,
"dns": 0.004999999999999893,
"ssl": -1,
"connect": 0.12699999999999978,
"send": 0.3100000000000005,
"wait": 1.0960000000315482,
"receive": 0.1999999999497959,
"_blocked_queueing": 0.6989999999404972
}
}
]
}
}
21 changes: 21 additions & 0 deletions test/traffic/fixtures/requests/response-cloning.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createTrafficScenario } from '../../utils/create-traffic-scenario'

createTrafficScenario(
(app) => {
let isFirstRequest = true

app.get('/resource', (req, res) => {
if (isFirstRequest) {
isFirstRequest = false
return res.send('one')
}

res.send('two')
})
},
(server) => [
// Intentionally request two same endpoints.
[server.http.url('/resource')],
[server.http.url('/resource')],
],
)
15 changes: 15 additions & 0 deletions test/traffic/response-cloning.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { fromTraffic } from '../../src/traffic/from-traffic.js'
import { InspectedHandler, inspectHandlers } from '../support/inspect.js'
import { _toHeaders, normalizeLocalhost, readArchive } from './utils/index.js'

it('subsequent requests to the same endpoint should return a readable response', async () => {
const har = readArchive('test/traffic/fixtures/archives/response-cloning.har')
const handlers = fromTraffic(har, normalizeLocalhost)
await inspectHandlers(handlers)
const repeatedHandlerResponse = await handlers[handlers.length - 1]!.run({
request: new Request('http://localhost/resource'),
requestId: crypto.randomUUID(),
})
expect(repeatedHandlerResponse?.response).toBeDefined()
await expect(repeatedHandlerResponse!.response!.text()).resolves.toBe('two')
})

0 comments on commit d45c219

Please sign in to comment.