fix(fetch): allow UTF-8 in Location header (#30904)

This commit is contained in:
Max Schmitt 2024-05-21 09:15:33 +02:00 committed by GitHub
parent 042896472b
commit a93ad3dade
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 20 additions and 18 deletions

View File

@ -336,12 +336,15 @@ export abstract class APIRequestContext extends SdkObject {
redirectOptions.rejectUnauthorized = false;
// HTTP-redirect fetch step 4: If locationURL is null, then return response.
if (response.headers.location) {
// Best-effort UTF-8 decoding, per spec it's US-ASCII only, but browsers are more lenient.
// Node.js parses it as Latin1 via std::v8::String, so we convert it to UTF-8.
const locationHeaderValue = Buffer.from(response.headers.location ?? '', 'latin1').toString('utf8');
if (locationHeaderValue) {
let locationURL;
try {
locationURL = new URL(response.headers.location, url);
locationURL = new URL(locationHeaderValue, url);
} catch (error) {
reject(new Error(`uri requested responds with an invalid redirect URL: ${response.headers.location}`));
reject(new Error(`uri requested responds with an invalid redirect URL: ${locationHeaderValue}`));
request.destroy();
return;
}

View File

@ -198,6 +198,20 @@ it('should follow redirects', async ({ context, server }) => {
expect(await response.json()).toEqual({ foo: 'bar' });
});
it('should follow redirects correctly when Location header contains UTF-8 characters', async ({ context, server }) => {
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/30903' });
server.setRoute('/redirect', (req, res) => {
// Node.js only allows US-ASCII, so we can't send invalid headers directly. Sending it as a raw response instead.
res.socket.write('HTTP/1.1 301 Moved Permanently\r\n');
res.socket.write(`Location: ${server.PREFIX}/empty.html?message=マスクПривет\r\n`);
res.socket.write('\r\n');
res.socket.uncork();
res.socket.end();
});
const response = await context.request.get(server.PREFIX + '/redirect');
expect(response.url()).toBe(server.PREFIX + '/empty.html?' + new URLSearchParams({ message: 'マスクПривет' }));
});
it('should add cookies from Set-Cookie header', async ({ context, page, server }) => {
server.setRoute('/setcookie.html', (req, res) => {
res.setHeader('Set-Cookie', ['session=value', 'foo=bar; max-age=3600']);
@ -794,21 +808,6 @@ it('should respect timeout after redirects', async function({ context, server })
expect(error.message).toContain(`Request timed out after 100ms`);
});
it('should throw on a redirect with an invalid URL', async ({ context, server }) => {
server.setRedirect('/redirect', '/test');
server.setRoute('/test', (req, res) => {
// Node.js prevents us from responding with an invalid header, therefore we manually write the response.
const conn = res.connection!;
conn.write('HTTP/1.1 302\r\n');
conn.write('Location: https://здравствуйте/\r\n');
conn.write('\r\n');
conn.uncork();
conn.end();
});
const error = await context.request.get(server.PREFIX + '/redirect').catch(e => e);
expect(error.message).toContain('apiRequestContext.get: uri requested responds with an invalid redirect URL');
});
it('should not hang on a brotli encoded Range request', async ({ context, server }) => {
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/18190' });
it.skip(+process.versions.node.split('.')[0] < 18);