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 17352bb
Show file tree
Hide file tree
Showing 4 changed files with 377 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
340 changes: 340 additions & 0 deletions test/traffic/fixtures/archives/response-cloning.har
Original file line number Diff line number Diff line change
@@ -0,0 +1,340 @@
{
"log": {
"version": "1.2",
"creator": {
"name": "WebInspector",
"version": "537.36"
},
"pages": [],
"entries": [
{
"_initiator": {
"type": "script",
"stack": {
"callFrames": [
{
"functionName": "eval",
"scriptId": "21",
"url": "",
"lineNumber": 3,
"columnNumber": 39
},
{
"functionName": "eval",
"scriptId": "21",
"url": "",
"lineNumber": 1,
"columnNumber": 56
},
{
"functionName": "evaluate",
"scriptId": "14",
"url": "",
"lineNumber": 340,
"columnNumber": 15
},
{
"functionName": "",
"scriptId": "20",
"url": "",
"lineNumber": 0,
"columnNumber": 43
}
]
}
},
"_priority": "High",
"_resourceType": "fetch",
"cache": {},
"connection": "30",
"request": {
"method": "GET",
"url": "http://127.0.0.1:49925/timing/instant",
"httpVersion": "HTTP/1.1",
"headers": [
{
"name": "Host",
"value": "127.0.0.1:49925"
},
{
"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": 483,
"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": "11"
},
{
"name": "ETag",
"value": "W/\"b-Kq5sNclPz7QV2+lfQIuc6R7oRu0\""
},
{
"name": "Date",
"value": "Fri, 13 Aug 2021 09:22:40 GMT"
},
{
"name": "Connection",
"value": "keep-alive"
}
],
"cookies": [],
"content": {
"size": 11,
"mimeType": "text/html",
"compression": 0,
"text": "hello world"
},
"redirectURL": "",
"headersSize": 236,
"bodySize": 11,
"_transferSize": 247,
"_error": null
},
"serverIPAddress": "127.0.0.1",
"startedDateTime": "2021-08-13T09:22:40.733Z",
"time": 6.724999999859283,
"timings": {
"blocked": 0.6439999999957509,
"dns": 0.0050000000000000044,
"ssl": -1,
"connect": 0.28300000000000003,
"send": 0.14999999999999997,
"wait": 5.358999999898442,
"receive": 0.28399999996508996,
"_blocked_queueing": 0.49699999999575084
}
},
{
"_initiator": {
"type": "script",
"stack": {
"callFrames": [
{
"functionName": "eval",
"scriptId": "21",
"url": "",
"lineNumber": 3,
"columnNumber": 39
},
{
"functionName": "eval",
"scriptId": "21",
"url": "",
"lineNumber": 1,
"columnNumber": 56
},
{
"functionName": "evaluate",
"scriptId": "14",
"url": "",
"lineNumber": 340,
"columnNumber": 15
},
{
"functionName": "",
"scriptId": "20",
"url": "",
"lineNumber": 0,
"columnNumber": 43
}
]
}
},
"_priority": "High",
"_resourceType": "fetch",
"cache": {},
"connection": "36",
"request": {
"method": "GET",
"url": "http://127.0.0.1:49925/timing/delayed",
"httpVersion": "HTTP/1.1",
"headers": [
{
"name": "Host",
"value": "127.0.0.1:49925"
},
{
"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": 483,
"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": "12"
},
{
"name": "ETag",
"value": "W/\"c-/0EpPAXscro5d2oTFT8UafBL698\""
},
{
"name": "Date",
"value": "Fri, 13 Aug 2021 09:22:41 GMT"
},
{
"name": "Connection",
"value": "keep-alive"
}
],
"cookies": [],
"content": {
"size": 12,
"mimeType": "text/html",
"compression": 0,
"text": "delayed body"
},
"redirectURL": "",
"headersSize": 236,
"bodySize": 12,
"_transferSize": 248,
"_error": null
},
"serverIPAddress": "127.0.0.1",
"startedDateTime": "2021-08-13T09:22:40.734Z",
"time": 511.02700000002284,
"timings": {
"blocked": 0.5700000000008003,
"dns": 0.0040000000000000036,
"ssl": -1,
"connect": 0.135,
"send": 0.061000000000000026,
"wait": 509.84600000003655,
"receive": 0.4109999999855063,
"_blocked_queueing": 0.49500000000080036
}
}
]
}
}
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.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/request-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 17352bb

Please sign in to comment.