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
21 changes: 21 additions & 0 deletions docs/src/api/class-response.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,27 @@ Contains a boolean stating whether the response was successful (status in the ra

Returns the matching [Request] object.

## async method: Response.securityDetails
- returns: <[null]|[Object]>
- `issuer` <[string]> Common Name component of the Issuer field.
from the certificate. This should only be used for informational purposes. Optional.
- `protocol` <[string]> The specific TLS protocol used. (e.g. `TLS 1.3`). Optional.
- `subjectName` <[string]> Common Name component of the Subject
field from the certificate. This should only be used for informational purposes. Optional.
- `validFrom` <[int]> Unix timestamp (in seconds) specifying
when this cert becomes valid. Optional.
- `validTo` <[int]> Unix timestamp (in seconds) specifying
when this cert becomes invalid. Optional.

Returns SSL and other security information.

## async method: Response.serverAddr
- returns: <[null]|[Object]>
- `ipAddress` <[string]> IPv4 or IPV6 address of the server.
- `port` <[int]>

Returns the IP address and port of the server.

## method: Response.status
- returns: <[int]>

Expand Down
10 changes: 9 additions & 1 deletion src/client/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { URLSearchParams } from 'url';
import * as channels from '../protocol/channels';
import { ChannelOwner } from './channelOwner';
import { Frame } from './frame';
import { Headers, WaitForEventOptions } from './types';
import { Headers, RemoteAddr, SecurityDetails, WaitForEventOptions } from './types';
import fs from 'fs';
import * as mime from 'mime';
import { isString, headersObjectToArray, headersArrayToObject } from '../utils/utils';
Expand Down Expand Up @@ -325,6 +325,14 @@ export class Response extends ChannelOwner<channels.ResponseChannel, channels.Re
frame(): Frame {
return this._request.frame();
}

async serverAddr(): Promise<RemoteAddr|null> {
return (await this._channel.serverAddr()).value || null;
}

async securityDetails(): Promise<SecurityDetails|null> {
return (await this._channel.securityDetails()).value || null;
}
}

export class WebSocket extends ChannelOwner<channels.WebSocketChannel, channels.WebSocketInitializer> implements api.WebSocket {
Expand Down
3 changes: 3 additions & 0 deletions src/client/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,6 @@ export type SelectorEngine = {
*/
queryAll(root: HTMLElement, selector: string): HTMLElement[];
};

export type RemoteAddr = channels.RemoteAddr;
export type SecurityDetails = channels.SecurityDetails;
8 changes: 8 additions & 0 deletions src/dispatchers/networkDispatchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ export class ResponseDispatcher extends Dispatcher<Response, channels.ResponseIn
async body(): Promise<channels.ResponseBodyResult> {
return { binary: (await this._object.body()).toString('base64') };
}

async securityDetails(): Promise<channels.ResponseSecurityDetailsResult> {
return { value: await this._object.securityDetails() || undefined };
Comment thread
rwoll marked this conversation as resolved.
}

async serverAddr(): Promise<channels.ResponseServerAddrResult> {
return { value: await this._object.serverAddr() || undefined };
Comment thread
rwoll marked this conversation as resolved.
}
}

export class RouteDispatcher extends Dispatcher<Route, channels.RouteInitializer> implements channels.RouteChannel {
Expand Down
25 changes: 25 additions & 0 deletions src/protocol/channels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2372,6 +2372,8 @@ export type ResponseInitializer = {
export interface ResponseChannel extends Channel {
body(params?: ResponseBodyParams, metadata?: Metadata): Promise<ResponseBodyResult>;
finished(params?: ResponseFinishedParams, metadata?: Metadata): Promise<ResponseFinishedResult>;
securityDetails(params?: ResponseSecurityDetailsParams, metadata?: Metadata): Promise<ResponseSecurityDetailsResult>;
serverAddr(params?: ResponseServerAddrParams, metadata?: Metadata): Promise<ResponseServerAddrResult>;
}
export type ResponseBodyParams = {};
export type ResponseBodyOptions = {};
Expand All @@ -2383,6 +2385,29 @@ export type ResponseFinishedOptions = {};
export type ResponseFinishedResult = {
error?: string,
};
export type ResponseSecurityDetailsParams = {};
export type ResponseSecurityDetailsOptions = {};
export type ResponseSecurityDetailsResult = {
value?: SecurityDetails,
Comment thread
rwoll marked this conversation as resolved.
};
export type ResponseServerAddrParams = {};
export type ResponseServerAddrOptions = {};
export type ResponseServerAddrResult = {
value?: RemoteAddr,
};

export type SecurityDetails = {
issuer?: string,
protocol?: string,
subjectName?: string,
validFrom?: number,
validTo?: number,
};

export type RemoteAddr = {
ipAddress: string,
port: number,
};

// ----------- WebSocket -----------
export type WebSocketInitializer = {
Expand Down
25 changes: 25 additions & 0 deletions src/protocol/protocol.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1927,6 +1927,31 @@ Response:
returns:
error: string?

securityDetails:
returns:
value: SecurityDetails?

serverAddr:
returns:
value: RemoteAddr?


SecurityDetails:
type: object
properties:
issuer: string?
protocol: string?
subjectName: string?
validFrom: number?
validTo: number?


RemoteAddr:
type: object
properties:
ipAddress: string
port: number


WebSocket:
type: interface
Expand Down
13 changes: 13 additions & 0 deletions src/protocol/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,19 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
});
scheme.ResponseBodyParams = tOptional(tObject({}));
scheme.ResponseFinishedParams = tOptional(tObject({}));
scheme.ResponseSecurityDetailsParams = tOptional(tObject({}));
scheme.ResponseServerAddrParams = tOptional(tObject({}));
scheme.SecurityDetails = tObject({
issuer: tOptional(tString),
protocol: tOptional(tString),
subjectName: tOptional(tString),
validFrom: tOptional(tNumber),
validTo: tOptional(tNumber),
});
scheme.RemoteAddr = tObject({
ipAddress: tString,
port: tNumber,
});
scheme.BindingCallRejectParams = tObject({
error: tType('SerializedError'),
});
Expand Down
22 changes: 22 additions & 0 deletions tests/har.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,3 +363,25 @@ it('should have connection details for failed requests', async ({ contextFactory
expect(serverIPAddress).toMatch(/^127\.0\.0\.1|\[::1\]/);
expect(port).toBe(server.PORT);
});

it('should return server address directly from response', async ({ page, server }) => {
const response = await page.goto(server.EMPTY_PAGE);
const { ipAddress, port } = await response.serverAddr();
expect(ipAddress).toMatch(/^127\.0\.0\.1|\[::1\]/);
expect(port).toBe(server.PORT);
});

it('should return security details directly from response', async ({ contextFactory, httpsServer, browserName, platform }) => {
it.fail(browserName === 'webkit' && platform === 'linux', 'https://github.com/microsoft/playwright/issues/6759');

const context = await contextFactory({ ignoreHTTPSErrors: true });
const page = await context.newPage();
const response = await page.goto(httpsServer.EMPTY_PAGE);
const securityDetails = await response.securityDetails();
if (browserName === 'webkit' && platform === 'win32')
expect(securityDetails).toEqual({subjectName: 'puppeteer-tests', validFrom: 1550084863, validTo: -1});
else if (browserName === 'webkit')
expect(securityDetails).toEqual({protocol: 'TLS 1.3', subjectName: 'puppeteer-tests', validFrom: 1550084863, validTo: 33086084863});
else
expect(securityDetails).toEqual({issuer: 'puppeteer-tests', protocol: 'TLS 1.3', subjectName: 'puppeteer-tests', validFrom: 1550084863, validTo: 33086084863});
});
44 changes: 44 additions & 0 deletions types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10357,6 +10357,50 @@ export interface Response {
*/
request(): Request;

/**
* Returns SSL and other security information.
*/
securityDetails(): Promise<null|{
/**
* Common Name component of the Issuer field. from the certificate. This should only be used for informational purposes.
* Optional.
*/
issuer?: string;

/**
* The specific TLS protocol used. (e.g. `TLS 1.3`). Optional.
*/
protocol?: string;

/**
* Common Name component of the Subject field from the certificate. This should only be used for informational purposes.
* Optional.
*/
subjectName?: string;

/**
* Unix timestamp (in seconds) specifying when this cert becomes valid. Optional.
*/
validFrom?: number;

/**
* Unix timestamp (in seconds) specifying when this cert becomes invalid. Optional.
*/
validTo?: number;
}>;

/**
* Returns the IP address and port of the server.
*/
serverAddr(): Promise<null|{
/**
* IPv4 or IPV6 address of the server.
*/
ipAddress: string;

port: number;
}>;

/**
* Contains the status code of the response (e.g., 200 for a success).
*/
Expand Down