Skip to content
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
9 changes: 2 additions & 7 deletions src/client/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,8 @@ export class Request extends ChannelOwner<channels.RequestChannel, channels.Requ

_actualHeaders(): Promise<RawHeaders> {
if (!this._actualHeadersPromise) {
this._actualHeadersPromise = this.response().then(response => {
// there is no response, so should we return the headers we have now?
if (!response)
return this._provisionalHeaders;
return response._wrapApiCall(async (channel: channels.ResponseChannel) => {
return new RawHeaders((await channel.rawRequestHeaders()).headers);
});
this._actualHeadersPromise = this._wrapApiCall(async (channel: channels.RequestChannel) => {
return new RawHeaders((await channel.rawRequestHeaders()).headers);
});
}
return this._actualHeadersPromise;
Expand Down
8 changes: 4 additions & 4 deletions src/dispatchers/networkDispatchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ export class RequestDispatcher extends Dispatcher<Request, channels.RequestIniti
});
}

async rawRequestHeaders(params?: channels.RequestRawRequestHeadersParams): Promise<channels.RequestRawRequestHeadersResult> {
return { headers: await this._object.rawRequestHeaders() };
}

async response(): Promise<channels.RequestResponseResult> {
return { response: lookupNullableDispatcher<ResponseDispatcher>(await this._object.response()) };
}
Expand Down Expand Up @@ -86,10 +90,6 @@ export class ResponseDispatcher extends Dispatcher<Response, channels.ResponseIn
return { value: await this._object.serverAddr() || undefined };
}

async rawRequestHeaders(params?: channels.ResponseRawRequestHeadersParams): Promise<channels.ResponseRawRequestHeadersResult> {
return { headers: await this._object.rawRequestHeaders() };
}

async rawResponseHeaders(params?: channels.ResponseRawResponseHeadersParams): Promise<channels.ResponseRawResponseHeadersResult> {
return { headers: await this._object.rawResponseHeaders() };
}
Expand Down
12 changes: 6 additions & 6 deletions src/protocol/channels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2770,12 +2770,18 @@ export type RequestInitializer = {
};
export interface RequestChannel extends Channel {
response(params?: RequestResponseParams, metadata?: Metadata): Promise<RequestResponseResult>;
rawRequestHeaders(params?: RequestRawRequestHeadersParams, metadata?: Metadata): Promise<RequestRawRequestHeadersResult>;
}
export type RequestResponseParams = {};
export type RequestResponseOptions = {};
export type RequestResponseResult = {
response?: ResponseChannel,
};
export type RequestRawRequestHeadersParams = {};
export type RequestRawRequestHeadersOptions = {};
export type RequestRawRequestHeadersResult = {
headers: NameValue[],
};

export interface RequestEvents {
}
Expand Down Expand Up @@ -2864,7 +2870,6 @@ export interface ResponseChannel extends Channel {
body(params?: ResponseBodyParams, metadata?: Metadata): Promise<ResponseBodyResult>;
securityDetails(params?: ResponseSecurityDetailsParams, metadata?: Metadata): Promise<ResponseSecurityDetailsResult>;
serverAddr(params?: ResponseServerAddrParams, metadata?: Metadata): Promise<ResponseServerAddrResult>;
rawRequestHeaders(params?: ResponseRawRequestHeadersParams, metadata?: Metadata): Promise<ResponseRawRequestHeadersResult>;
rawResponseHeaders(params?: ResponseRawResponseHeadersParams, metadata?: Metadata): Promise<ResponseRawResponseHeadersResult>;
sizes(params?: ResponseSizesParams, metadata?: Metadata): Promise<ResponseSizesResult>;
}
Expand All @@ -2883,11 +2888,6 @@ export type ResponseServerAddrOptions = {};
export type ResponseServerAddrResult = {
value?: RemoteAddr,
};
export type ResponseRawRequestHeadersParams = {};
export type ResponseRawRequestHeadersOptions = {};
export type ResponseRawRequestHeadersResult = {
headers: NameValue[],
};
export type ResponseRawResponseHeadersParams = {};
export type ResponseRawResponseHeadersOptions = {};
export type ResponseRawResponseHeadersResult = {
Expand Down
11 changes: 5 additions & 6 deletions src/protocol/protocol.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2324,6 +2324,11 @@ Request:
returns:
response: Response?

rawRequestHeaders:
returns:
headers:
type: array
items: NameValue


Route:
Expand Down Expand Up @@ -2407,12 +2412,6 @@ Response:
returns:
value: RemoteAddr?

rawRequestHeaders:
returns:
headers:
type: array
items: NameValue

rawResponseHeaders:
returns:
headers:
Expand Down
2 changes: 1 addition & 1 deletion src/protocol/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
state: tOptional(tEnum(['attached', 'detached', 'visible', 'hidden'])),
});
scheme.RequestResponseParams = tOptional(tObject({}));
scheme.RequestRawRequestHeadersParams = tOptional(tObject({}));
scheme.RouteAbortParams = tObject({
errorCode: tOptional(tString),
});
Expand Down Expand Up @@ -1128,7 +1129,6 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
scheme.ResponseBodyParams = tOptional(tObject({}));
scheme.ResponseSecurityDetailsParams = tOptional(tObject({}));
scheme.ResponseServerAddrParams = tOptional(tObject({}));
scheme.ResponseRawRequestHeadersParams = tOptional(tObject({}));
scheme.ResponseRawResponseHeadersParams = tOptional(tObject({}));
scheme.ResponseSizesParams = tOptional(tObject({}));
scheme.SecurityDetails = tObject({
Expand Down
16 changes: 15 additions & 1 deletion src/server/chromium/crNetworkManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,13 @@ export class CRNetworkManager {
}

_onRequestPaused(workerFrame: frames.Frame | undefined, event: Protocol.Fetch.requestPausedPayload) {
if (!event.responseStatusCode && !event.responseErrorReason) {
// Request intercepted, deliver signal to the tracker.
const request = this._requestIdToRequest.get(event.networkId!);
if (request)
this._responseExtraInfoTracker.requestPaused(request.request, event);
}

if (!this._userRequestInterceptionEnabled && this._protocolRequestInterceptionEnabled) {
this._client._sendMayFail('Fetch.continueRequest', {
requestId: event.requestId
Expand Down Expand Up @@ -619,6 +626,13 @@ class ResponseExtraInfoTracker {
this._innerResponseReceived(info, event.response);
}

requestPaused(request: network.Request, event: Protocol.Fetch.requestPausedPayload) {
// requestWillBeSentExtraInfo is not being called when interception
// is enabled. But interception is mutually exclusive with the redirects.
// So we can use the headers from the Fetch.requestPausedPayload immediately.
request.setRawRequestHeaders(headersObjectToArray(event.request.headers, '\n'));
}

private _innerResponseReceived(info: RequestInfo, response: Protocol.Network.Response) {
if (!response.connectionId) {
// Starting with this response we no longer can guarantee that response and extra info correspond to the same index.
Expand Down Expand Up @@ -684,7 +698,7 @@ class ResponseExtraInfoTracker {
const response = info.responses[index];
const requestExtraInfo = info.requestWillBeSentExtraInfo[index];
if (response && requestExtraInfo)
response.setRawRequestHeaders(headersObjectToArray(requestExtraInfo.headers, '\n'));
response.request().setRawRequestHeaders(headersObjectToArray(requestExtraInfo.headers, '\n'));
const responseExtraInfo = info.responseReceivedExtraInfo[index];
if (response && responseExtraInfo) {
response.setRawResponseHeaders(headersObjectToArray(responseExtraInfo.headers, '\n'));
Expand Down
57 changes: 31 additions & 26 deletions src/server/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export class Request extends SdkObject {
private _postData: Buffer | null;
readonly _headers: types.HeadersArray;
private _headersMap = new Map<string, string>();
private _rawRequestHeadersPromise: ManualPromise<types.HeadersArray> | undefined;
private _frame: frames.Frame;
private _waitForResponsePromise = new ManualPromise<Response | null>();
_responseEndTiming = -1;
Expand Down Expand Up @@ -153,8 +154,23 @@ export class Request extends SdkObject {
return this._headersMap.get(name);
}

async rawHeaders(): Promise<NameValue[]> {
return this._headers;
setWillReceiveExtraHeaders() {
if (!this._rawRequestHeadersPromise)
this._rawRequestHeadersPromise = new ManualPromise();
}

setRawRequestHeaders(headers: types.HeadersArray) {
if (!this._rawRequestHeadersPromise)
this._rawRequestHeadersPromise = new ManualPromise();
this._rawRequestHeadersPromise!.resolve(headers);
}

async rawRequestHeaders(): Promise<NameValue[]> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment explaining 3 ways the raw headers could be received

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which 3 ways? :)

return this._rawRequestHeadersPromise || Promise.resolve(this._headers);
}

rawRequestHeadersPromise(): Promise<types.HeadersArray> | undefined {
return this._rawRequestHeadersPromise;
}

response(): PromiseLike<Response | null> {
Expand Down Expand Up @@ -197,6 +213,17 @@ export class Request extends SdkObject {
bodySize(): number {
return this.postDataBuffer()?.length || 0;
}

async requestHeadersSize(): Promise<number> {
let headersSize = 4; // 4 = 2 spaces + 2 line breaks (GET /path \r\n)
headersSize += this.method().length;
headersSize += (new URL(this.url())).pathname.length;
headersSize += 8; // httpVersion
const headers = this.rawRequestHeadersPromise() ? await this.rawRequestHeadersPromise()! : this._headers;
for (const header of headers)
headersSize += header.name.length + header.value.length + 4; // 4 = ': ' + '\r\n'
return headersSize;
}
}

export class Route extends SdkObject {
Expand Down Expand Up @@ -316,7 +343,6 @@ export class Response extends SdkObject {
private _timing: ResourceTiming;
private _serverAddrPromise = new ManualPromise<RemoteAddr | undefined>();
private _securityDetailsPromise = new ManualPromise<SecurityDetails | undefined>();
private _rawRequestHeadersPromise: ManualPromise<types.HeadersArray> | undefined;
private _rawResponseHeadersPromise: ManualPromise<types.HeadersArray> | undefined;
private _httpVersion: string | undefined;

Expand Down Expand Up @@ -372,25 +398,15 @@ export class Response extends SdkObject {
return this._headersMap.get(name);
}

async rawRequestHeaders(): Promise<NameValue[]> {
return this._rawRequestHeadersPromise || Promise.resolve(this._request._headers);
}

async rawResponseHeaders(): Promise<NameValue[]> {
return this._rawResponseHeadersPromise || Promise.resolve(this._headers);
}

setWillReceiveExtraHeaders() {
this._rawRequestHeadersPromise = new ManualPromise();
this._request.setWillReceiveExtraHeaders();
this._rawResponseHeadersPromise = new ManualPromise();
}

setRawRequestHeaders(headers: types.HeadersArray) {
if (!this._rawRequestHeadersPromise)
this._rawRequestHeadersPromise = new ManualPromise();
this._rawRequestHeadersPromise!.resolve(headers);
}

setRawResponseHeaders(headers: types.HeadersArray) {
if (!this._rawResponseHeadersPromise)
this._rawResponseHeadersPromise = new ManualPromise();
Expand Down Expand Up @@ -436,17 +452,6 @@ export class Response extends SdkObject {
return this._httpVersion;
}

private async _requestHeadersSize(): Promise<number> {
let headersSize = 4; // 4 = 2 spaces + 2 line breaks (GET /path \r\n)
headersSize += this._request.method().length;
headersSize += (new URL(this.url())).pathname.length;
headersSize += 8; // httpVersion
const headers = this._rawRequestHeadersPromise ? await this._rawRequestHeadersPromise : this._request._headers;
for (const header of headers)
headersSize += header.name.length + header.value.length + 4; // 4 = ': ' + '\r\n'
return headersSize;
}

private async _responseHeadersSize(): Promise<number> {
if (this._request.responseSize.responseHeadersSize)
return this._request.responseSize.responseHeadersSize;
Expand All @@ -467,7 +472,7 @@ export class Response extends SdkObject {

async sizes(): Promise<ResourceSizes> {
await this._finishedPromise;
const requestHeadersSize = await this._requestHeadersSize();
const requestHeadersSize = await this._request.requestHeadersSize();
const responseHeadersSize = await this._responseHeadersSize();
let { encodedBodySize } = this._request.responseSize;
if (!encodedBodySize) {
Expand Down
2 changes: 1 addition & 1 deletion src/server/supplements/har/harTracer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ export class HarTracer {
if (details)
harEntry._securityDetails = details;
}));
this._addBarrier(page, response.rawRequestHeaders().then(headers => {
this._addBarrier(page, request.rawRequestHeaders().then(headers => {
for (const header of headers.filter(header => header.name.toLowerCase() === 'cookie'))
harEntry.request.cookies.push(...header.value.split(';').map(parseCookie));
harEntry.request.headers = headers;
Expand Down
2 changes: 1 addition & 1 deletion src/server/webkit/wkPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1016,7 +1016,7 @@ export class WKPage implements PageDelegate {
const headers = { ...event.response.requestHeaders };
if (!headers['host'])
headers['Host'] = new URL(request.request.url()).host;
response.setRawRequestHeaders(headersObjectToArray(headers));
request.request.setRawRequestHeaders(headersObjectToArray(headers));
}
this._page._frameManager.requestReceivedResponse(response);

Expand Down
10 changes: 10 additions & 0 deletions tests/page/page-route.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -672,3 +672,13 @@ it('should support the times parameter with route matching', async ({ page, serv
await page.goto(server.EMPTY_PAGE);
expect(intercepted).toHaveLength(1);
});

it('should contain raw header', async ({ page, server }) => {
let headers: any;
await page.route('**/*', async route => {
headers = await route.request().allHeaders();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check that it actually returns expected headers?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we are returning raw headers here, so I only check that it accept one exists.

route.continue();
});
await page.goto(server.PREFIX + '/empty.html');
expect(headers.accept).toBeTruthy();
});