Skip to content

Commit 1c5bd2e

Browse files
committed
fix(@angular/ssr): ensure correct referer header handling in web request conversion
The `Referer` header is classified as a forbidden header name within the Web Standard Fetch API. This means it cannot be manually set directly within the `headers` option, instead it must be set when constructing a `new Request()` object. Closes #30581
1 parent a1e2bde commit 1c5bd2e

File tree

3 files changed

+87
-4
lines changed

3 files changed

+87
-4
lines changed

packages/angular/ssr/node/src/request.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,14 @@ export function createWebRequestFromNodeRequest(
3333
): Request {
3434
const { headers, method = 'GET' } = nodeRequest;
3535
const withBody = method !== 'GET' && method !== 'HEAD';
36+
const referrer = headers.referer && URL.canParse(headers.referer) ? headers.referer : undefined;
3637

3738
return new Request(createRequestUrl(nodeRequest), {
3839
method,
3940
headers: createRequestHeaders(headers),
4041
body: withBody ? nodeRequest : undefined,
4142
duplex: withBody ? 'half' : undefined,
43+
referrer,
4244
});
4345
}
4446

packages/angular/ssr/node/test/request_http1_spec.ts

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ describe('createWebRequestFromNodeRequest (HTTP/1.1)', () => {
4545
server.close(done);
4646
});
4747

48-
describe('GET Handling', () => {
48+
describe('GET handling', () => {
4949
it('should correctly handle a basic GET request', async () => {
5050
const nodeRequest = await extractNodeRequest(() => {
5151
request({
@@ -96,9 +96,51 @@ describe('createWebRequestFromNodeRequest (HTTP/1.1)', () => {
9696
expect(webRequest.headers.get('x-custom-header1')).toBe('value1');
9797
expect(webRequest.headers.get('x-custom-header2')).toBe('value2');
9898
});
99+
100+
it('should correctly handle the referer header', async () => {
101+
const referer = 'http://test-referer-site.com/page';
102+
const nodeRequest = await extractNodeRequest(() => {
103+
request({
104+
hostname: 'localhost',
105+
port,
106+
path: '/with-referer',
107+
method: 'GET',
108+
headers: {
109+
referer,
110+
},
111+
}).end();
112+
});
113+
114+
expect(nodeRequest.headers['referer']).toBe(referer);
115+
116+
const webRequest = createWebRequestFromNodeRequest(nodeRequest);
117+
expect(webRequest.headers.get('referer')).toBe(referer);
118+
expect(webRequest.referrer).toBe(referer);
119+
});
120+
121+
it('should handle an invalid referer header gracefully', async () => {
122+
const invalidReferer = '/invalid-referer';
123+
const nodeRequest = await extractNodeRequest(() => {
124+
request({
125+
hostname: 'localhost',
126+
port,
127+
path: '/with-invalid-referer',
128+
method: 'GET',
129+
headers: {
130+
referer: invalidReferer,
131+
},
132+
}).end();
133+
});
134+
135+
expect(nodeRequest.headers['referer']).toBe(invalidReferer);
136+
137+
const webRequest = createWebRequestFromNodeRequest(nodeRequest);
138+
expect(webRequest.headers.get('referer')).toBe(invalidReferer);
139+
expect(webRequest.referrer).toBe('about:client');
140+
});
99141
});
100142

101-
describe('POST Handling', () => {
143+
describe('POST handling', () => {
102144
it('should handle POST request with JSON body and correct response', async () => {
103145
const postData = JSON.stringify({ message: 'Hello from POST' });
104146
const nodeRequest = await extractNodeRequest(() => {

packages/angular/ssr/node/test/request_http2_spec.ts

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ describe('createWebRequestFromNodeRequest (HTTP/2)', () => {
5656
server.close(done);
5757
});
5858

59-
describe('GET Handling', () => {
59+
describe('GET handling', () => {
6060
it('should correctly handle a basic GET request', async () => {
6161
const nodeRequest = await extractNodeRequest(() => {
6262
client
@@ -106,9 +106,48 @@ describe('createWebRequestFromNodeRequest (HTTP/2)', () => {
106106
expect(webRequest.headers.get('x-custom-header1')).toBe('value1');
107107
expect(webRequest.headers.get('x-custom-header2')).toBe('value2');
108108
});
109+
110+
it('should correctly handle the referer header', async () => {
111+
const referer = 'http://test-referer-site.com/page';
112+
const nodeRequest = await extractNodeRequest(() => {
113+
client
114+
.request({
115+
':path': '/with-referer',
116+
':method': 'GET',
117+
referer,
118+
})
119+
.end();
120+
});
121+
122+
expect(nodeRequest.headers['referer']).toBe(referer);
123+
124+
const webRequest = createWebRequestFromNodeRequest(nodeRequest);
125+
expect(webRequest.headers.get('referer')).toBe(referer);
126+
expect(webRequest.referrer).toBe(referer);
127+
expect(webRequest.url).toBe(`http://localhost:${port}/with-referer`);
128+
});
129+
130+
it('should handle an invalid referer header gracefully', async () => {
131+
const invalidReferer = '/invalid-referer';
132+
const nodeRequest = await extractNodeRequest(() => {
133+
client
134+
.request({
135+
':path': '/with-referer',
136+
':method': 'GET',
137+
referer: invalidReferer,
138+
})
139+
.end();
140+
});
141+
142+
expect(nodeRequest.headers['referer']).toBe(invalidReferer);
143+
144+
const webRequest = createWebRequestFromNodeRequest(nodeRequest);
145+
expect(webRequest.headers.get('referer')).toBe(invalidReferer);
146+
expect(webRequest.referrer).toBe('about:client');
147+
});
109148
});
110149

111-
describe('POST Handling', () => {
150+
describe('POST handling', () => {
112151
it('should handle POST request with JSON body and correct response', async () => {
113152
const postData = JSON.stringify({ message: 'Hello from POST' });
114153
const nodeRequest = await extractNodeRequest(() => {

0 commit comments

Comments
 (0)