Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fetch: fix wpt test request-upload.any.js #3234

Merged
merged 2 commits into from
May 10, 2024
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 test/wpt/server/lockedresource.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export class LockedResource {
constructor (resource) {
this.locked = false
this.waitingQueue = []
this.resource = resource
}

async acquire () {
return new Promise(resolve => {
if (!this.locked) {
// If the lock is not already acquired, acquire it immediately
this.locked = true
resolve(this.resource)
} else {
// If the lock is already acquired, queue the resolve function
this.waitingQueue.push(resolve)
}
})
}

release () {
if (this.waitingQueue.length > 0) {
// If there are functions waiting to acquire the lock, execute the next one in the queue
const nextResolve = this.waitingQueue.shift()
nextResolve(this.resource)
} else {
// If there are no functions waiting, release the lock
this.locked = false
}
}
}
135 changes: 78 additions & 57 deletions test/wpt/server/routes/network-partition-key.mjs
Original file line number Diff line number Diff line change
@@ -1,111 +1,132 @@
import { LockedResource } from '../lockedresource.mjs'

const stash = new Map()
const lockedStash = new LockedResource(stash)

/**
* @see https://github.com/web-platform-tests/wpt/blob/master/fetch/connection-pool/resources/network-partition-key.py
* @param {Parameters<import('http').RequestListener>[0]} req
* @param {Parameters<import('http').RequestListener>[1]} res
* @param {Parameters<import('http').RequestListener>[0]} request
* @param {Parameters<import('http').RequestListener>[1]} response
* @param {URL} url
*/
export function route (req, res, { searchParams, port }) {
res.setHeader('Cache-Control', 'no-store')
export async function route (request, response, { searchParams, port }) {
response.setHeader('Cache-Control', 'no-store')

const dispatch = searchParams.get('dispatch')
const uuid = searchParams.get('uuid')
const partitionId = searchParams.get('partition_id')

if (!uuid || !dispatch || !partitionId) {
res.statusCode = 404
res.end('Invalid query parameters')
return
return simpleResponse(request, response, 404, 'Not found', 'Invalid query parameters')
}

let testFailed = false
let requestCount = 0
let connectionCount = 0

if (searchParams.get('nocheck_partition') !== 'True') {
const addressKey = `${req.socket.localAddress}|${port}`
const serverState = stash.get(uuid) ?? {
testFailed: false,
requestCount: 0,
connectionCount: 0
}
if (searchParams.get('nocheck_partition') !== 'true') {
const stash = await lockedStash.acquire()
try {
const addressKey = `${request.socket.localAddress}|${port}`
const serverState = stash.get(uuid) ?? {
testFailed: false,
requestCount: 0,
connectionCount: 0,
sockets: new Set()
}

stash.delete(uuid)
requestCount = serverState.requestCount + 1
serverState.requestCount = requestCount
stash.delete(uuid)
requestCount = serverState.requestCount
requestCount += 1
serverState.requestCount = requestCount

if (Object.hasOwn(serverState, addressKey)) {
if (serverState[addressKey] !== partitionId) {
serverState.testFailed = true
if (addressKey in serverState) {
if (serverState[addressKey] !== partitionId) {
serverState.testFailed = true
}
}

// We can detect if a new connection is created by checking if the socket
// was already used in the test.
if (serverState.sockets.has(request.socket) === false) {
connectionCount = serverState.connectionCount
connectionCount += 1
serverState.connectionCount = connectionCount
serverState.sockets.add(request.socket)
}
} else {
connectionCount = serverState.connectionCount + 1
serverState.connectionCount = connectionCount
}

serverState[addressKey] = partitionId
testFailed = serverState.testFailed
stash.set(uuid, serverState)
serverState[addressKey] = partitionId
testFailed = serverState.testFailed
stash.set(uuid, serverState)
} finally {
lockedStash.release()
}
}

const origin = req.headers.origin
const origin = request.headers.origin
if (origin) {
res.setHeader('Access-Control-Allow-Origin', origin)
res.setHeader('Access-Control-Allow-Credentials', 'true')
response.setHeader('Access-Control-Allow-Origin', origin)
Dismissed Show dismissed Hide dismissed
response.setHeader('Access-Control-Allow-Credentials', 'true')
}

if (req.method === 'OPTIONS') {
return handlePreflight(req, res)
if (request.method === 'OPTIONS') {
return handlePreflight(request, response)
}

if (dispatch === 'fetch_file') {
res.end()
return
// There is currently no relevant wpt test that uses this dispatch
return response.end()
}

if (dispatch === 'check_partition') {
const status = searchParams.get('status') ?? 200

if (testFailed) {
res.statusCode = status
res.end('Multiple partition IDs used on a socket')
return
return simpleResponse(request, response, status, 'OK', 'Multiple partition IDs used on a socket')
}

let body = 'ok'
if (searchParams.get('addcounter')) {
if (searchParams.get('addcounter') === 'true') {
body += `. Request was sent ${requestCount} times. ${connectionCount} connections were created.`
res.statusCode = status
res.end(body)
return
return simpleResponse(request, response, status, 'OK', body)
}
}

if (dispatch === 'clean_up') {
stash.delete(uuid)
res.statusCode = 200
if (testFailed) {
res.end('Test failed, but cleanup completed.')
} else {
res.end('cleanup complete')
return simpleResponse(request, response, 200, 'OK', 'Test failed, but cleanup completed.')
}

return
return simpleResponse(request, response, 200, 'OK', 'cleanup complete')
}

res.statusCode = 404
res.end('Unrecognized dispatch parameter: ' + dispatch)
return simpleResponse(request, response, 404, 'Not found', 'Unrecognized dispatch parameter: ' + dispatch)
}

/**
* @param {Parameters<import('http').RequestListener>[0]} request
* @param {Parameters<import('http').RequestListener>[1]} response
*/
function handlePreflight (request, response) {
response.statusCode = 200
response.statusMessage = 'OK'
response.setHeader('Access-Control-Allow-Methods', 'GET')
response.setHeader('Access-Control-Allow-Headers', 'header-to-force-cors')
response.setHeader('Access-Control-Max-Age', '86400')
response.end('Preflight request')
}

/**
* @param {Parameters<import('http').RequestListener>[0]} req
* @param {Parameters<import('http').RequestListener>[1]} res
* @param {Parameters<import('http').RequestListener>[0]} request
* @param {Parameters<import('http').RequestListener>[1]} response
* @param {number} statusCode
* @param {string} statusMessage
* @param {string} body
* @param {string} [contentType='text/plain']
*/
function handlePreflight (req, res) {
res.statusCode = 200
res.setHeader('Access-Control-Allow-Methods', 'GET')
res.setHeader('Access-Control-Allow-Headers', 'header-to-force-cors')
res.setHeader('Access-Control-Max-Age', '86400')
res.end('Preflight request')
function simpleResponse (request, response, statusCode, statusMessage, body, contentType = 'text/plain') {
response.statusCode = statusCode
response.statusMessage = statusMessage
response.setHeader('Content-Type', contentType)
response.end(body)
}
5 changes: 0 additions & 5 deletions test/wpt/status/fetch.status.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,6 @@
"note": "undici doesn't filter headers",
"skip": true
},
"request-upload.any.js": {
"fail": [
"Fetch with POST with text body on 421 response should be retried once on new connection."
]
},
"request-upload.h2.any.js": {
"note": "undici doesn't support http/2",
"skip": true
Expand Down
Loading