Skip to content

Commit

Permalink
align network module with BiDi spec and scaffold responseStarted
Browse files Browse the repository at this point in the history
  • Loading branch information
Thiago Perrotta committed May 23, 2023
1 parent fce48df commit abf07a2
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 102 deletions.
3 changes: 2 additions & 1 deletion src/bidiMapper/domains/events/SubscriptionManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -451,8 +451,9 @@ describe('unroll events', () => {
it('all Network events', () => {
expect(unrollEvents([Network.AllEvents])).to.deep.equal([
Network.EventNames.BeforeRequestSentEvent,
Network.EventNames.ResponseCompletedEvent,
Network.EventNames.FetchErrorEvent,
Network.EventNames.ResponseStartedEvent,
Network.EventNames.ResponseCompletedEvent,
]);
});

Expand Down
7 changes: 4 additions & 3 deletions src/bidiMapper/domains/network/networkProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import Protocol from 'devtools-protocol';
import {CdpClient} from '../../CdpConnection.js';
import {IEventManager} from '../events/EventManager.js';
import {DefaultMap} from '../../../utils/DefaultMap.js';
import {Network} from '../../../protocol/protocol.js';

import {NetworkRequest} from './networkRequest.js';

Expand All @@ -35,11 +36,11 @@ export class NetworkProcessor {
* Map of request ID to NetworkRequest objects. Needed as long as information
* about requests comes from different events.
*/
readonly #requestMap: DefaultMap<string, NetworkRequest>;
readonly #requestMap: DefaultMap<Network.Request, NetworkRequest>;

private constructor(eventManager: IEventManager) {
this.#eventManager = eventManager;
this.#requestMap = new DefaultMap<string, NetworkRequest>(
this.#requestMap = new DefaultMap(
(requestId) => new NetworkRequest(requestId, this.#eventManager)
);
}
Expand Down Expand Up @@ -100,7 +101,7 @@ export class NetworkProcessor {
return networkProcessor;
}

#getOrCreateNetworkRequest(requestId: string): NetworkRequest {
#getOrCreateNetworkRequest(requestId: Network.Request): NetworkRequest {
return this.#requestMap.get(requestId);
}
}
207 changes: 119 additions & 88 deletions src/bidiMapper/domains/network/networkRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
* @fileoverview `NetworkRequest` represents a single network request and keeps
* track of all the related CDP events.
*/

import Protocol from 'devtools-protocol';

import {Deferred} from '../../../utils/deferred.js';
Expand All @@ -29,101 +28,103 @@ import {Network} from '../../../protocol/protocol.js';

export class NetworkRequest {
static #unknown = 'UNKNOWN';
requestId: string;

/**
* Each network request has an associated request id, which is a string
* uniquely identifying that request.
*
* The identifier for a request resulting from a redirect matches that of the
* request that initiated it.
*/
requestId: Network.Request;

#eventManager: IEventManager;
#requestWillBeSentEvent: Protocol.Network.RequestWillBeSentEvent | undefined;
#requestWillBeSentExtraInfoEvent:
| Protocol.Network.RequestWillBeSentExtraInfoEvent
| undefined;
#responseReceivedEvent: Protocol.Network.ResponseReceivedEvent | undefined;

#responseReceivedExtraInfoEvent:
| Protocol.Network.ResponseReceivedExtraInfoEvent
| undefined;
#requestWillBeSentEvent?: Protocol.Network.RequestWillBeSentEvent;
#requestWillBeSentExtraInfoEvent?: Protocol.Network.RequestWillBeSentExtraInfoEvent;
#responseReceivedEvent?: Protocol.Network.ResponseReceivedEvent;
#responseReceivedExtraInfoEvent?: Protocol.Network.ResponseReceivedExtraInfoEvent;

#beforeRequestSentDeferred: Deferred<void>;
#responseReceivedDeferred: Deferred<void>;

constructor(requestId: string, eventManager: IEventManager) {
constructor(requestId: Network.Request, eventManager: IEventManager) {
this.requestId = requestId;
this.#eventManager = eventManager;
this.#beforeRequestSentDeferred = new Deferred<void>();
this.#responseReceivedDeferred = new Deferred<void>();
}

onRequestWillBeSentEvent(
requestWillBeSentEvent: Protocol.Network.RequestWillBeSentEvent
) {
onRequestWillBeSentEvent(event: Protocol.Network.RequestWillBeSentEvent) {
if (this.#requestWillBeSentEvent !== undefined) {
// TODO: Handle redirect event, requestId is same for the redirect chain
return;
}
this.#requestWillBeSentEvent = event;

this.#requestWillBeSentEvent = requestWillBeSentEvent;
if (this.#requestWillBeSentExtraInfoEvent !== undefined) {
this.#beforeRequestSentDeferred.resolve();
}

this.#sendBeforeRequestEvent();
}

onRequestWillBeSentExtraInfoEvent(
requestWillBeSentExtraInfoEvent: Protocol.Network.RequestWillBeSentExtraInfoEvent
event: Protocol.Network.RequestWillBeSentExtraInfoEvent
) {
if (this.#requestWillBeSentExtraInfoEvent !== undefined) {
// TODO: Handle redirect event, requestId is same for the redirect chain
return;
}
this.#requestWillBeSentExtraInfoEvent = requestWillBeSentExtraInfoEvent;
this.#requestWillBeSentExtraInfoEvent = event;

if (this.#requestWillBeSentEvent !== undefined) {
this.#beforeRequestSentDeferred.resolve();
}
}

onResponseReceivedEvent(
responseReceivedEvent: Protocol.Network.ResponseReceivedEvent
) {
onResponseReceivedEvent(event: Protocol.Network.ResponseReceivedEvent) {
if (this.#responseReceivedEvent !== undefined) {
// TODO: Handle redirect event, requestId is same for the redirect chain
return;
}
this.#responseReceivedEvent = responseReceivedEvent;
if (
!responseReceivedEvent.hasExtraInfo &&
!this.#beforeRequestSentDeferred.isFinished
) {
this.#responseReceivedEvent = event;

if (!event.hasExtraInfo && !this.#beforeRequestSentDeferred.isFinished) {
this.#beforeRequestSentDeferred.resolve();
}

if (
!responseReceivedEvent.hasExtraInfo ||
!event.hasExtraInfo ||
this.#responseReceivedExtraInfoEvent !== undefined
) {
this.#responseReceivedDeferred.resolve();
}

this.#sendResponseReceivedEvent();
}

onResponseReceivedEventExtraInfo(
responseReceivedExtraInfoEvent: Protocol.Network.ResponseReceivedExtraInfoEvent
event: Protocol.Network.ResponseReceivedExtraInfoEvent
) {
if (this.#responseReceivedExtraInfoEvent !== undefined) {
// TODO: Handle redirect event, requestId is same for the redirect chain
return;
}
this.#responseReceivedExtraInfoEvent = responseReceivedExtraInfoEvent;
this.#responseReceivedExtraInfoEvent = event;

if (this.#responseReceivedEvent !== undefined) {
this.#responseReceivedDeferred.resolve();
}
}

onLoadingFailedEvent(
loadingFailedEvent: Protocol.Network.LoadingFailedEvent
) {
onLoadingFailedEvent(event: Protocol.Network.LoadingFailedEvent) {
this.#beforeRequestSentDeferred.resolve();
this.#responseReceivedDeferred.reject(loadingFailedEvent);
this.#responseReceivedDeferred.reject(event);

const params: Network.FetchErrorParams = {
...this.#getBaseEventParams(),
errorText: loadingFailedEvent.errorText,
errorText: event.errorText,
};

this.#eventManager.registerEvent(
Expand Down Expand Up @@ -151,17 +152,22 @@ export class NetworkRequest {
if (this.#requestWillBeSentEvent === undefined) {
throw new Error('RequestWillBeSentEvent is not set');
}

const params: Network.BeforeRequestSentParams = {
...this.#getBaseEventParams(),
initiator: {type: this.#getInitiatorType()},
initiator: {
type: NetworkRequest.#getInitiatorType(
this.#requestWillBeSentEvent.initiator.type
),
},
};
return {
method: Network.EventNames.BeforeRequestSentEvent,
params,
};
}

#getBaseEventParams(): Network.BaseEventParams {
#getBaseEventParams(): Network.BaseParameters {
return {
context: this.#requestWillBeSentEvent?.frameId ?? null,
navigation: this.#requestWillBeSentEvent?.loaderId ?? null,
Expand Down Expand Up @@ -232,53 +238,32 @@ export class NetworkRequest {
};
}

#getInitiatorType(): Network.Initiator['type'] {
switch (this.#requestWillBeSentEvent?.initiator.type) {
case 'parser':
case 'script':
case 'preflight':
return this.#requestWillBeSentEvent.initiator.type;
default:
return 'other';
_sendResponseStartedEvent() {
if (!this.#isIgnoredEvent()) {
this.#eventManager.registerEvent(
this.#getResponseStartedEvent(),
this.#requestWillBeSentEvent?.frameId ?? null
);
}
}

static #getCookiesSameSite(
cdpSameSiteValue: string | undefined
): Network.Cookie['sameSite'] {
switch (cdpSameSiteValue) {
case 'Strict':
return 'strict';
case 'Lax':
return 'lax';
default:
return 'none';
#getResponseStartedEvent(): Network.ResponseStartedEvent {
if (this.#requestWillBeSentEvent === undefined) {
throw new Error('RequestWillBeSentEvent is not set');
}
}

static #getCookies(
associatedCookies: Protocol.Network.BlockedCookieWithReason[]
): Network.Cookie[] {
return associatedCookies.map((cookieInfo) => {
return {
name: cookieInfo.cookie.name,
value: cookieInfo.cookie.value,
domain: cookieInfo.cookie.domain,
path: cookieInfo.cookie.path,
expires: cookieInfo.cookie.expires,
size: cookieInfo.cookie.size,
httpOnly: cookieInfo.cookie.httpOnly,
secure: cookieInfo.cookie.secure,
sameSite: NetworkRequest.#getCookiesSameSite(
cookieInfo.cookie.sameSite
),
};
});
return {
method: Network.EventNames.ResponseStartedEvent,
params: {
...this.#getBaseEventParams(),
// TODO: populate response.
response: {},
} as Network.ResponseStartedParams,
};
}

#sendResponseReceivedEvent() {
if (!this.#isIgnoredEvent()) {
// Wait for both ResponseReceived and ResponseReceivedExtraInfo events.
this.#eventManager.registerPromiseEvent(
this.#responseReceivedDeferred.then(() =>
this.#getResponseReceivedEvent()
Expand All @@ -290,27 +275,27 @@ export class NetworkRequest {
}

#getResponseReceivedEvent(): Network.ResponseCompletedEvent {
if (this.#responseReceivedEvent === undefined) {
throw new Error('ResponseReceivedEvent is not set');
}
if (this.#requestWillBeSentEvent === undefined) {
throw new Error('RequestWillBeSentEvent is not set');
}
if (this.#responseReceivedEvent === undefined) {
throw new Error('ResponseReceivedEvent is not set');
}

return {
method: Network.EventNames.ResponseCompletedEvent,
params: {
...this.#getBaseEventParams(),
response: {
url: this.#responseReceivedEvent.response.url,
protocol: this.#responseReceivedEvent.response.protocol,
protocol: this.#responseReceivedEvent.response.protocol ?? '',
status: this.#responseReceivedEvent.response.status,
statusText: this.#responseReceivedEvent.response.statusText,
fromCache:
this.#responseReceivedEvent.response.fromDiskCache ||
this.#responseReceivedEvent.response.fromPrefetchCache,
// TODO: implement.
headers: this.#getHeaders(
(this.#responseReceivedEvent.response.fromDiskCache ||
this.#responseReceivedEvent.response.fromPrefetchCache) ??
false,
headers: NetworkRequest.#getHeaders(
this.#responseReceivedEvent.response.headers
),
mimeType: this.#responseReceivedEvent.response.mimeType,
Expand All @@ -324,21 +309,67 @@ export class NetworkRequest {
size: -1,
},
},
} as Network.ResponseCompletedParams,
},
};
}

#getHeaders(headers: Protocol.Network.Headers) {
#isIgnoredEvent(): boolean {
return (
this.#requestWillBeSentEvent?.request.url.endsWith('/favicon.ico') ??
false
);
}

static #getHeaders(headers: Protocol.Network.Headers): Network.Header[] {
return Object.keys(headers).map((key) => ({
name: key,
value: headers[key],
}));
}

#isIgnoredEvent(): boolean {
return (
this.#requestWillBeSentEvent?.request.url.endsWith('/favicon.ico') ??
false
);
static #getInitiatorType(
initiatorType: Protocol.Network.Initiator['type']
): Network.Initiator['type'] {
switch (initiatorType) {
case 'parser':
case 'script':
case 'preflight':
return initiatorType;
default:
return 'other';
}
}

static #getCookies(
associatedCookies: Protocol.Network.BlockedCookieWithReason[]
): Network.Cookie[] {
return associatedCookies.map((cookieInfo) => {
return {
name: cookieInfo.cookie.name,
value: cookieInfo.cookie.value,
domain: cookieInfo.cookie.domain,
path: cookieInfo.cookie.path,
expires: cookieInfo.cookie.expires,
size: cookieInfo.cookie.size,
httpOnly: cookieInfo.cookie.httpOnly,
secure: cookieInfo.cookie.secure,
sameSite: NetworkRequest.#getCookiesSameSite(
cookieInfo.cookie.sameSite
),
};
});
}

static #getCookiesSameSite(
cdpSameSiteValue?: string
): Network.Cookie['sameSite'] {
switch (cdpSameSiteValue) {
case 'Strict':
return 'strict';
case 'Lax':
return 'lax';
default:
return 'none';
}
}
}
Loading

0 comments on commit abf07a2

Please sign in to comment.