Skip to content

Commit 6e653f1

Browse files
authored
chore: use fetch for range conformance tests (#937)
When a range header is detected, use an in-page fetch invocation to run the test, this way it will be handled by the service worker and not trigger https://issues.chromium.org/issues/469788775 Enables all single, multi and full-range conformance tests.
1 parent 706bf94 commit 6e653f1

File tree

8 files changed

+396
-166
lines changed

8 files changed

+396
-166
lines changed

src/lib/headers-to-object.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export function headersToObject (headers: Headers): Record<string, string> {
2+
const output: Record<string, string> = {}
3+
4+
for (const [key, value] of headers.entries()) {
5+
output[key] = value
6+
}
7+
8+
return output
9+
}

src/sw/handlers/content-request-handler.ts

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -518,24 +518,6 @@ function createHeaders (event: FetchEvent): Headers {
518518
const headers = new Headers(event.request.headers)
519519
const url = new URL(event.request.url)
520520

521-
// the default browser accept header is too broad so remove it
522-
// headers.delete('accept')
523-
524-
// conformance tests may set these headers via search params
525-
const HEADERS = [
526-
'accept',
527-
'range',
528-
'if-none-match'
529-
]
530-
531-
HEADERS.forEach(header => {
532-
const value = url.searchParams.get(header)
533-
534-
if (value != null) {
535-
headers.set(header, value)
536-
}
537-
})
538-
539521
// override the Accept header if the format param is present
540522
// https://specs.ipfs.tech/http-gateways/path-gateway/#format-request-query-parameter
541523
const format = url.searchParams.get('format')

src/sw/pages/render-entity.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,8 @@
11
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
2+
import { headersToObject } from '../../lib/headers-to-object.ts'
23
import { APP_NAME, APP_VERSION, GIT_REVISION } from '../../version.js'
34
import { htmlPage } from './page.js'
45

5-
function headersToObject (headers: Headers): Record<string, string> {
6-
const output: Record<string, string> = {}
7-
8-
for (const [key, value] of headers.entries()) {
9-
output[key] = value
10-
}
11-
12-
return output
13-
}
14-
156
/**
167
* Shows an error page to the user
178
*/

test-conformance/conformance.test.ts

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import { test, expect } from '@playwright/test'
1313
import { execa } from 'execa'
1414
import { base36 } from 'multiformats/bases/base36'
1515
import { CID } from 'multiformats/cid'
16-
import { formatSearch, parseQuery } from '../src/lib/query-helpers.ts'
1716
import { loadWithServiceWorker } from '../test-e2e/fixtures/load-with-service-worker.js'
17+
import { makeFetchRequest } from '../test-e2e/fixtures/make-range-request.ts'
1818
import { setConfig } from '../test-e2e/fixtures/set-sw-config.ts'
1919
import { waitForServiceWorker } from '../test-e2e/fixtures/wait-for-service-worker.js'
2020
import { GWC_IMAGE } from './fixtures/constants.js'
@@ -23,8 +23,8 @@ import expectedPassingTests from './fixtures/expected-passing-tests.json' with {
2323
import { getReportDetails } from './fixtures/get-report-details.js'
2424
import { getTestsToRun } from './fixtures/get-tests-to-run.js'
2525
import { getTestsToSkip } from './fixtures/get-tests-to-skip.js'
26-
import type { BrowserContext } from '@playwright/test'
27-
import type { Server } from 'node:http'
26+
import type { BrowserContext, Response } from '@playwright/test'
27+
import type { IncomingHttpHeaders, Server } from 'node:http'
2828

2929
test.describe.configure({ mode: 'serial' })
3030

@@ -107,32 +107,6 @@ test.describe('@helia/service-worker-gateway - gateway conformance', () => {
107107
url.port = `${3000}`
108108
}
109109

110-
// translate headers used by tests into non-standard query params
111-
const HEADERS = [
112-
'accept',
113-
'range',
114-
'if-none-match'
115-
]
116-
117-
const overrides: Record<string, string> = {}
118-
119-
for (const header of HEADERS) {
120-
if (req.headers[header] != null) {
121-
let value = req.headers[header]
122-
123-
if (Array.isArray(value)) {
124-
value = value.join(', ')
125-
}
126-
127-
overrides[header] = value
128-
}
129-
}
130-
131-
url.search = formatSearch({
132-
...parseQuery(url),
133-
...overrides
134-
})
135-
136110
context = await browser.newContext()
137111
const page = await context.newPage()
138112

@@ -160,17 +134,34 @@ test.describe('@helia/service-worker-gateway - gateway conformance', () => {
160134

161135
console.info('REQUEST', id, req.method, url.toString(), req.headers)
162136

163-
const response = await loadWithServiceWorker(page, url.toString(), {
164-
redirect: maybeAsSubdomainUrlRedirect(url),
137+
let response: Response
165138

166-
// all data should be local so no need to wait for long timeout
167-
timeout: 10_000
168-
})
139+
// if headers used by tests are sent, make an in-page window.fetch
140+
// request instead of loading a URL as this is the only way
141+
// specific headers can be sent
142+
const HEADERS = [
143+
'accept',
144+
'range',
145+
'if-none-match'
146+
]
147+
148+
if ([...Object.keys(req.headers)].some(key => HEADERS.includes(key))) {
149+
response = await makeFetchRequest(page, url, {
150+
headers: new Headers(incomingHeadersToObject(req.headers))
151+
})
152+
} else {
153+
response = await loadWithServiceWorker(page, url.toString(), {
154+
redirect: maybeAsSubdomainUrlRedirect(url),
155+
156+
// all data should be local so no need to wait for long timeout
157+
timeout: 10_000
158+
})
159+
}
169160

170161
res.statusCode = response.status()
171162
res.statusMessage = response.statusText()
172163

173-
console.info('RESPONSE', id, req.method, url.toString(), response.status(), await response.allHeaders())
164+
console.info('RESPONSE', id, req.method, url.toString(), res.statusCode, await response.allHeaders())
174165
const body = await response.body()
175166

176167
if (response.status() === 500) {
@@ -353,3 +344,21 @@ function maybeAsSubdomainUrlRedirect (url: URL): string | undefined {
353344
function encodeDNSLinkLabel (name: string): string {
354345
return name.replace(/-/g, '--').replace(/\./g, '-')
355346
}
347+
348+
function incomingHeadersToObject (headers: IncomingHttpHeaders): Record<string, string> {
349+
const output: Record<string, string> = {}
350+
351+
for (let [key, value] of Object.entries(headers)) {
352+
if (value == null) {
353+
continue
354+
}
355+
356+
if (Array.isArray(value)) {
357+
value = value.join(',')
358+
}
359+
360+
output[key] = value
361+
}
362+
363+
return output
364+
}

0 commit comments

Comments
 (0)