Skip to content

Commit 0a1bd10

Browse files
committed
move rewriter logic
1 parent 5f1a94a commit 0a1bd10

6 files changed

+194
-149
lines changed

packages/proxy/lib/http/response-middleware.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import type { HttpMiddleware, HttpMiddlewareThis } from '.'
2121
import type { IncomingMessage, IncomingHttpHeaders } from 'http'
2222

2323
import { cspHeaderNames, generateCspDirectives, nonceDirectives, parseCspHeaders, problematicCspDirectives, unsupportedCSPDirectives } from './util/csp-header'
24-
import { rewriteServiceWorker } from './util/service-worker'
24+
import { rewriteServiceWorker } from './util/service-worker-rewriter'
2525

2626
export interface ResponseMiddlewareProps {
2727
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/// <reference lib="dom" />
2+
3+
/**
4+
* Rewrites the service worker to listen to fetch events to determine if the service worker handled the request.
5+
* @param body the body of the service worker to rewrite
6+
* @returns the rewritten service worker
7+
*/
8+
export const rewriteServiceWorker = (body: Buffer) => {
9+
function __cypressCreateListenerFunction (listener) {
10+
return (event) => {
11+
// we want to override the respondWith method so we can track if it was called
12+
// to determine if the service worker handled the request
13+
const oldRespondWith = event.respondWith
14+
let respondWithCalled = false
15+
16+
event.respondWith = (...args) => {
17+
respondWithCalled = true
18+
oldRespondWith.call(event, ...args)
19+
}
20+
21+
const returnValue = listener(event)
22+
23+
// @ts-expect-error
24+
// call the CDP binding to inform the backend whether or not the service worker handled the request
25+
self.__cypressServiceWorkerFetchEvent(JSON.stringify({ url: event.request.url, respondWithCalled }))
26+
27+
return returnValue
28+
}
29+
}
30+
31+
function __cypressOverwriteAddRemoveEventListener () {
32+
const listeners = new WeakMap()
33+
34+
const oldAddEventListener = self.addEventListener
35+
36+
// Overwrite the addEventListener method so we can
37+
// determine if the service worker handled the request
38+
self.addEventListener = (type, listener, options) => {
39+
if (type === 'fetch') {
40+
const newListener = __cypressCreateListenerFunction(listener)
41+
42+
listeners.set(listener, newListener)
43+
44+
return oldAddEventListener(type, newListener, options)
45+
}
46+
47+
return oldAddEventListener(type, listener, options)
48+
}
49+
50+
const oldRemoveEventListener = self.removeEventListener
51+
52+
// Overwrite the removeEventListener method so we can
53+
// remove the listener from the map
54+
self.removeEventListener = (type, listener, options) => {
55+
if (type === 'fetch') {
56+
const newListener = listeners.get(listener)
57+
58+
listeners.delete(listener)
59+
60+
return oldRemoveEventListener(type, newListener, options)
61+
}
62+
63+
return oldRemoveEventListener(type, listener, options)
64+
}
65+
}
66+
67+
function __cypressOverwriteOnfetch () {
68+
const originalPropertyDescriptor = Object.getOwnPropertyDescriptor(
69+
self,
70+
'onfetch',
71+
)
72+
73+
if (!originalPropertyDescriptor) {
74+
return
75+
}
76+
77+
// Overwrite the onfetch property so we can
78+
// determine if the service worker handled the request
79+
Object.defineProperty(
80+
self,
81+
'onfetch',
82+
{
83+
configurable: originalPropertyDescriptor.configurable,
84+
enumerable: originalPropertyDescriptor.enumerable,
85+
get () {
86+
return originalPropertyDescriptor.get?.call(this)
87+
},
88+
set (value: (event) => void) {
89+
let newHandler
90+
91+
if (value) {
92+
newHandler = __cypressCreateListenerFunction(value)
93+
}
94+
95+
originalPropertyDescriptor.set?.call(this, newHandler)
96+
},
97+
},
98+
)
99+
}
100+
101+
const updatedBody = `
102+
${__cypressCreateListenerFunction};
103+
(${__cypressOverwriteAddRemoveEventListener})();
104+
(${__cypressOverwriteOnfetch})();
105+
${body}`
106+
107+
return updatedBody
108+
}

packages/proxy/lib/http/util/service-worker.ts

+1-110
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
/// <reference lib="dom" />
2-
31
import Debug from 'debug'
42
import pDefer from 'p-defer'
53
import type { BrowserPreRequest } from '../../types'
@@ -59,113 +57,6 @@ export const serviceWorkerFetchEventHandler = (handler: ServiceWorkerFetchHandle
5957
}
6058
}
6159

62-
/**
63-
* Rewrites the service worker to listen to fetch events to determine if the service worker handled the request.
64-
* @param body the body of the service worker to rewrite
65-
* @returns the rewritten service worker
66-
*/
67-
export const rewriteServiceWorker = (body: Buffer) => {
68-
function __cypressCreateListenerFunction (listener) {
69-
return (event) => {
70-
// we want to override the respondWith method so we can track if it was called
71-
// to determine if the service worker handled the request
72-
const oldRespondWith = event.respondWith
73-
let respondWithCalled = false
74-
75-
event.respondWith = (...args) => {
76-
respondWithCalled = true
77-
oldRespondWith.call(event, ...args)
78-
}
79-
80-
const returnValue = listener(event)
81-
82-
// @ts-expect-error
83-
// call the CDP binding to inform the backend whether or not the service worker handled the request
84-
self.__cypressServiceWorkerFetchEvent(JSON.stringify({ url: event.request.url, respondWithCalled }))
85-
86-
return returnValue
87-
}
88-
}
89-
90-
function __cypressOverwriteAddRemoveEventListener () {
91-
const listeners = new WeakMap()
92-
93-
const oldAddEventListener = self.addEventListener
94-
95-
// Overwrite the addEventListener method so we can
96-
// determine if the service worker handled the request
97-
self.addEventListener = (type, listener, options) => {
98-
if (type === 'fetch') {
99-
const newListener = __cypressCreateListenerFunction(listener)
100-
101-
listeners.set(listener, newListener)
102-
103-
return oldAddEventListener(type, newListener, options)
104-
}
105-
106-
return oldAddEventListener(type, listener, options)
107-
}
108-
109-
const oldRemoveEventListener = self.removeEventListener
110-
111-
// Overwrite the removeEventListener method so we can
112-
// remove the listener from the map
113-
self.removeEventListener = (type, listener, options) => {
114-
if (type === 'fetch') {
115-
const newListener = listeners.get(listener)
116-
117-
listeners.delete(listener)
118-
119-
return oldRemoveEventListener(type, newListener, options)
120-
}
121-
122-
return oldRemoveEventListener(type, listener, options)
123-
}
124-
}
125-
126-
function __cypressOverwriteOnfetch () {
127-
const originalPropertyDescriptor = Object.getOwnPropertyDescriptor(
128-
self,
129-
'onfetch',
130-
)
131-
132-
if (!originalPropertyDescriptor) {
133-
return
134-
}
135-
136-
// Overwrite the onfetch property so we can
137-
// determine if the service worker handled the request
138-
Object.defineProperty(
139-
self,
140-
'onfetch',
141-
{
142-
configurable: originalPropertyDescriptor.configurable,
143-
enumerable: originalPropertyDescriptor.enumerable,
144-
get () {
145-
return originalPropertyDescriptor.get?.call(this)
146-
},
147-
set (value: (event) => void) {
148-
let newHandler
149-
150-
if (value) {
151-
newHandler = __cypressCreateListenerFunction(value)
152-
}
153-
154-
originalPropertyDescriptor.set?.call(this, newHandler)
155-
},
156-
},
157-
)
158-
}
159-
160-
const updatedBody = `
161-
${__cypressCreateListenerFunction};
162-
(${__cypressOverwriteAddRemoveEventListener})();
163-
(${__cypressOverwriteOnfetch})();
164-
${body}`
165-
166-
return updatedBody
167-
}
168-
16960
/**
17061
* Manages service worker registrations and their controlled URLs.
17162
*
@@ -288,7 +179,7 @@ export class ServiceWorkerManager {
288179
activatedServiceWorker.scriptURL === paramlessDocumentURL ||
289180
!activatedServiceWorker.initiatorOrigin ||
290181
!paramlessDocumentURL.startsWith(activatedServiceWorker.initiatorOrigin)) {
291-
debug('Service worker not activated or request URL is from the service worker, skipping: %o', { activatedServiceWorker, paramlessDocumentURL })
182+
debug('Service worker not activated or request URL is from the service worker, skipping: %o', { activatedServiceWorker, browserPreRequest })
292183

293184
return
294185
}

packages/proxy/test/unit/http/response-middleware.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { RemoteStates } from '@packages/server/lib/remote_states'
88
import { Readable } from 'stream'
99
import * as rewriter from '../../../lib/http/util/rewriter'
1010
import { nonceDirectives, problematicCspDirectives, unsupportedCSPDirectives } from '../../../lib/http/util/csp-header'
11-
import * as serviceWorker from '../../../lib/http/util/service-worker'
11+
import * as serviceWorkerRewriter from '../../../lib/http/util/service-worker-rewriter'
1212

1313
describe('http/response-middleware', function () {
1414
it('exports the members in the correct order', function () {
@@ -2301,7 +2301,7 @@ describe('http/response-middleware', function () {
23012301
let rewriteServiceWorkerStub
23022302

23032303
beforeEach(() => {
2304-
rewriteServiceWorkerStub = sinon.spy(serviceWorker, 'rewriteServiceWorker')
2304+
rewriteServiceWorkerStub = sinon.spy(serviceWorkerRewriter, 'rewriteServiceWorker')
23052305
})
23062306

23072307
afterEach(() => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { expect } from 'chai'
2+
import { rewriteServiceWorker } from '../../../../lib/http/util/service-worker-rewriter'
3+
4+
describe('lib/http/util/service-worker-rewriter', () => {
5+
describe('rewriteServiceWorker', () => {
6+
it('rewrites the service worker', () => {
7+
const result = rewriteServiceWorker(Buffer.from('foo'))
8+
9+
const expected = `
10+
function __cypressCreateListenerFunction(listener) {
11+
return (event) => {
12+
// we want to override the respondWith method so we can track if it was called
13+
// to determine if the service worker handled the request
14+
const oldRespondWith = event.respondWith;
15+
let respondWithCalled = false;
16+
event.respondWith = (...args) => {
17+
respondWithCalled = true;
18+
oldRespondWith.call(event, ...args);
19+
};
20+
const returnValue = listener(event);
21+
// @ts-expect-error
22+
// call the CDP binding to inform the backend whether or not the service worker handled the request
23+
self.__cypressServiceWorkerFetchEvent(JSON.stringify({ url: event.request.url, respondWithCalled }));
24+
return returnValue;
25+
};
26+
};
27+
(function __cypressOverwriteAddRemoveEventListener() {
28+
const listeners = new WeakMap();
29+
const oldAddEventListener = self.addEventListener;
30+
// Overwrite the addEventListener method so we can
31+
// determine if the service worker handled the request
32+
self.addEventListener = (type, listener, options) => {
33+
if (type === 'fetch') {
34+
const newListener = __cypressCreateListenerFunction(listener);
35+
listeners.set(listener, newListener);
36+
return oldAddEventListener(type, newListener, options);
37+
}
38+
return oldAddEventListener(type, listener, options);
39+
};
40+
const oldRemoveEventListener = self.removeEventListener;
41+
// Overwrite the removeEventListener method so we can
42+
// remove the listener from the map
43+
self.removeEventListener = (type, listener, options) => {
44+
if (type === 'fetch') {
45+
const newListener = listeners.get(listener);
46+
listeners.delete(listener);
47+
return oldRemoveEventListener(type, newListener, options);
48+
}
49+
return oldRemoveEventListener(type, listener, options);
50+
};
51+
})();
52+
(function __cypressOverwriteOnfetch() {
53+
const originalPropertyDescriptor = Object.getOwnPropertyDescriptor(self, 'onfetch');
54+
if (!originalPropertyDescriptor) {
55+
return;
56+
}
57+
// Overwrite the onfetch property so we can
58+
// determine if the service worker handled the request
59+
Object.defineProperty(self, 'onfetch', {
60+
configurable: originalPropertyDescriptor.configurable,
61+
enumerable: originalPropertyDescriptor.enumerable,
62+
get() {
63+
var _a;
64+
return (_a = originalPropertyDescriptor.get) === null || _a === void 0 ? void 0 : _a.call(this);
65+
},
66+
set(value) {
67+
var _a;
68+
let newHandler;
69+
if (value) {
70+
newHandler = __cypressCreateListenerFunction(value);
71+
}
72+
(_a = originalPropertyDescriptor.set) === null || _a === void 0 ? void 0 : _a.call(this, newHandler);
73+
},
74+
});
75+
})();
76+
foo`
77+
78+
expect(result).to.equal(expected)
79+
})
80+
})
81+
})

packages/proxy/test/unit/http/util/service-worker.spec.ts

+1-36
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { expect } from 'chai'
22
import sinon from 'sinon'
3-
import { ServiceWorkerManager, serviceWorkerFetchEventHandler, rewriteServiceWorker } from '../../../../lib/http/util/service-worker'
3+
import { ServiceWorkerManager, serviceWorkerFetchEventHandler } from '../../../../lib/http/util/service-worker'
44

55
describe('lib/http/util/service-worker', () => {
66
describe('ServiceWorkerManager', () => {
@@ -885,39 +885,4 @@ describe('lib/http/util/service-worker', () => {
885885
expect(handler).not.to.have.been.called
886886
})
887887
})
888-
889-
describe('rewriteServiceWorker', () => {
890-
it('rewrites the service worker', () => {
891-
const result = rewriteServiceWorker(Buffer.from('foo'))
892-
893-
const expected = `
894-
(function overwriteAddEventListener() {
895-
const oldAddEventListener = self.addEventListener;
896-
self.addEventListener = (type, listener, options) => {
897-
if (type === 'fetch') {
898-
const newListener = (event) => {
899-
// we want to override the respondWith method so we can track if it was called
900-
// to determine if the service worker intercepted the request
901-
const oldRespondWith = event.respondWith;
902-
let respondWithCalled = false;
903-
event.respondWith = (response) => {
904-
respondWithCalled = true;
905-
oldRespondWith.call(event, response);
906-
};
907-
const returnValue = listener(event);
908-
// @ts-expect-error
909-
// call the CDP binding to inform the backend whether or not the service worker intercepted the request
910-
self.__cypressServiceWorkerFetchEvent(JSON.stringify({ url: event.request.url, respondWithCalled }));
911-
return returnValue;
912-
};
913-
return oldAddEventListener(type, newListener, options);
914-
}
915-
return oldAddEventListener(type, listener, options);
916-
};
917-
})();
918-
foo`
919-
920-
expect(result).to.equal(expected)
921-
})
922-
})
923888
})

0 commit comments

Comments
 (0)