2020-01-06 18:22:35 -08:00
/ * *
* Copyright ( c ) Microsoft Corporation .
*
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an "AS IS" BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* /
2019-11-26 11:23:13 -08:00
2022-07-01 12:49:43 -07:00
import type * as contexts from './browserContext' ;
import type * as pages from './page' ;
2022-04-06 13:57:14 -08:00
import type * as frames from './frames' ;
import type * as types from './types' ;
2022-09-20 18:41:51 -07:00
import type * as channels from '@protocol/channels' ;
2022-04-07 12:55:44 -08:00
import { assert } from '../utils' ;
2023-11-01 16:36:39 -07:00
import { ManualPromise } from '../utils/manualPromise' ;
2021-02-09 14:44:48 -08:00
import { SdkObject } from './instrumentation' ;
2022-09-04 10:52:20 -07:00
import type { HeadersArray , NameValue } from '../common/types' ;
2021-11-05 16:27:49 +01:00
import { APIRequestContext } from './fetch' ;
2022-09-04 10:52:20 -07:00
import type { NormalizedContinueOverrides } from './types' ;
2023-06-02 13:00:27 -07:00
import { BrowserContext } from './browserContext' ;
2019-11-27 12:44:12 -08:00
2022-06-14 22:02:15 -07:00
export function filterCookies ( cookies : channels.NetworkCookie [ ] , urls : string [ ] ) : channels . NetworkCookie [ ] {
2019-11-26 11:23:13 -08:00
const parsedURLs = urls . map ( s = > new URL ( s ) ) ;
// Chromiums's cookies are missing sameSite when it is 'None'
return cookies . filter ( c = > {
if ( ! parsedURLs . length )
return true ;
for ( const parsedURL of parsedURLs ) {
2021-01-12 15:56:29 -08:00
let domain = c . domain ;
if ( ! domain . startsWith ( '.' ) )
domain = '.' + domain ;
if ( ! ( '.' + parsedURL . hostname ) . endsWith ( domain ) )
2019-11-26 11:23:13 -08:00
continue ;
if ( ! parsedURL . pathname . startsWith ( c . path ) )
continue ;
2022-03-02 09:33:30 -08:00
if ( parsedURL . protocol !== 'https:' && parsedURL . hostname !== 'localhost' && c . secure )
2019-11-26 11:23:13 -08:00
continue ;
return true ;
}
return false ;
} ) ;
}
2019-11-27 12:44:12 -08:00
2022-03-02 23:10:40 +01:00
// Rollover to 5-digit year:
// 253402300799 == Fri, 31 Dec 9999 23:59:59 +0000 (UTC)
// 253402300800 == Sat, 1 Jan 1000 00:00:00 +0000 (UTC)
2023-07-20 15:42:52 -07:00
export const kMaxCookieExpiresDateInSeconds = 253402300799 ;
2022-03-02 23:10:40 +01:00
2022-06-14 22:02:15 -07:00
export function rewriteCookies ( cookies : channels.SetNetworkCookie [ ] ) : channels . SetNetworkCookie [ ] {
2019-12-02 16:36:46 -08:00
return cookies . map ( c = > {
2019-12-03 10:51:41 -08:00
assert ( c . url || ( c . domain && c . path ) , 'Cookie should have a url or a domain/path pair' ) ;
assert ( ! ( c . url && c . domain ) , 'Cookie should have either url or domain' ) ;
2021-09-09 17:22:02 +08:00
assert ( ! ( c . url && c . path ) , 'Cookie should have either url or path' ) ;
2022-03-02 23:10:40 +01:00
assert ( ! ( c . expires && c . expires < 0 && c . expires !== - 1 ) , 'Cookie should have a valid expires, only -1 or a positive number for the unix timestamp in seconds is allowed' ) ;
assert ( ! ( c . expires && c . expires > 0 && c . expires > kMaxCookieExpiresDateInSeconds ) , 'Cookie should have a valid expires, only -1 or a positive number for the unix timestamp in seconds is allowed' ) ;
2021-09-27 18:58:08 +02:00
const copy = { . . . c } ;
2019-12-02 16:36:46 -08:00
if ( copy . url ) {
assert ( copy . url !== 'about:blank' , ` Blank page can not have cookie " ${ c . name } " ` ) ;
assert ( ! copy . url . startsWith ( 'data:' ) , ` Data URL page can not have cookie " ${ c . name } " ` ) ;
const url = new URL ( copy . url ) ;
copy . domain = url . hostname ;
copy . path = url . pathname . substring ( 0 , url . pathname . lastIndexOf ( '/' ) + 1 ) ;
copy . secure = url . protocol === 'https:' ;
}
return copy ;
} ) ;
}
2021-01-25 14:49:51 -08:00
export function parsedURL ( url : string ) : URL | null {
try {
return new URL ( url ) ;
} catch ( e ) {
return null ;
}
}
export function stripFragmentFromUrl ( url : string ) : string {
if ( ! url . includes ( '#' ) )
2019-12-18 17:27:20 -07:00
return url ;
2021-01-25 14:49:51 -08:00
return url . substring ( 0 , url . indexOf ( '#' ) ) ;
2019-12-18 17:27:20 -07:00
}
2021-02-09 09:00:00 -08:00
export class Request extends SdkObject {
2019-12-11 16:19:37 -08:00
private _response : Response | null = null ;
2020-03-16 13:31:06 -07:00
private _redirectedFrom : Request | null ;
2021-08-30 20:43:40 -07:00
_redirectedTo : Request | null = null ;
2019-12-14 08:20:51 -08:00
readonly _documentId? : string ;
2020-01-17 17:14:39 -08:00
readonly _isFavicon : boolean ;
2020-06-26 11:51:47 -07:00
_failureText : string | null = null ;
2019-11-27 12:44:12 -08:00
private _url : string ;
private _resourceType : string ;
private _method : string ;
2020-07-22 15:59:37 -07:00
private _postData : Buffer | null ;
2022-09-04 10:52:20 -07:00
readonly _headers : HeadersArray ;
2020-10-26 14:32:07 -07:00
private _headersMap = new Map < string , string > ( ) ;
2022-07-01 12:49:43 -07:00
readonly _frame : frames.Frame | null = null ;
readonly _serviceWorker : pages.Worker | null = null ;
readonly _context : contexts.BrowserContext ;
2022-09-04 10:52:20 -07:00
private _rawRequestHeadersPromise = new ManualPromise < HeadersArray > ( ) ;
2021-08-29 11:21:06 -07:00
private _waitForResponsePromise = new ManualPromise < Response | null > ( ) ;
2020-10-21 23:25:57 -07:00
_responseEndTiming = - 1 ;
2022-09-04 10:52:20 -07:00
private _overrides : NormalizedContinueOverrides | undefined ;
2019-11-27 12:44:12 -08:00
2022-07-01 12:49:43 -07:00
constructor ( context : contexts.BrowserContext , frame : frames.Frame | null , serviceWorker : pages.Worker | null , redirectedFrom : Request | null , documentId : string | undefined ,
2022-09-04 10:52:20 -07:00
url : string , resourceType : string , method : string , postData : Buffer | null , headers : HeadersArray ) {
2022-07-01 12:49:43 -07:00
super ( frame || context , 'request' ) ;
2020-01-16 16:58:02 -08:00
assert ( ! url . startsWith ( 'data:' ) , 'Data urls should not fire requests' ) ;
2022-07-01 12:49:43 -07:00
this . _context = context ;
2019-11-27 12:44:12 -08:00
this . _frame = frame ;
2022-07-01 12:49:43 -07:00
this . _serviceWorker = serviceWorker ;
2020-03-16 13:31:06 -07:00
this . _redirectedFrom = redirectedFrom ;
if ( redirectedFrom )
redirectedFrom . _redirectedTo = this ;
2019-12-14 08:20:51 -08:00
this . _documentId = documentId ;
2019-12-18 17:27:20 -07:00
this . _url = stripFragmentFromUrl ( url ) ;
2019-11-27 12:44:12 -08:00
this . _resourceType = resourceType ;
this . _method = method ;
this . _postData = postData ;
this . _headers = headers ;
2022-09-04 10:52:20 -07:00
this . _updateHeadersMap ( ) ;
2021-09-02 08:31:25 -07:00
this . _isFavicon = url . endsWith ( '/favicon.ico' ) || ! ! redirectedFrom ? . _isFavicon ;
2019-11-27 12:44:12 -08:00
}
2019-12-16 16:32:04 -08:00
_setFailureText ( failureText : string ) {
2019-11-27 12:44:12 -08:00
this . _failureText = failureText ;
2021-08-29 11:21:06 -07:00
this . _waitForResponsePromise . resolve ( null ) ;
2019-11-27 12:44:12 -08:00
}
2022-09-04 10:52:20 -07:00
_setOverrides ( overrides : types.NormalizedContinueOverrides ) {
this . _overrides = overrides ;
this . _updateHeadersMap ( ) ;
}
private _updateHeadersMap() {
for ( const { name , value } of this . headers ( ) )
this . _headersMap . set ( name . toLowerCase ( ) , value ) ;
}
_hasOverrides() {
return ! ! this . _overrides ;
}
2019-11-27 12:44:12 -08:00
url ( ) : string {
2022-09-04 10:52:20 -07:00
return this . _overrides ? . url || this . _url ;
2019-11-27 12:44:12 -08:00
}
resourceType ( ) : string {
return this . _resourceType ;
}
method ( ) : string {
2022-09-04 10:52:20 -07:00
return this . _overrides ? . method || this . _method ;
2019-11-27 12:44:12 -08:00
}
2020-07-22 15:59:37 -07:00
postDataBuffer ( ) : Buffer | null {
2022-09-04 10:52:20 -07:00
return this . _overrides ? . postData || this . _postData ;
2019-11-27 12:44:12 -08:00
}
2022-09-04 10:52:20 -07:00
headers ( ) : HeadersArray {
return this . _overrides ? . headers || this . _headers ;
2019-11-27 12:44:12 -08:00
}
2020-10-26 14:32:07 -07:00
headerValue ( name : string ) : string | undefined {
return this . _headersMap . get ( name ) ;
}
2022-06-24 13:51:09 -07:00
// "null" means no raw headers available - we'll use provisional headers as raw headers.
2022-09-04 10:52:20 -07:00
setRawRequestHeaders ( headers : HeadersArray | null ) {
2022-06-24 13:51:09 -07:00
if ( ! this . _rawRequestHeadersPromise . isDone ( ) )
this . _rawRequestHeadersPromise . resolve ( headers || this . _headers ) ;
2021-10-04 15:10:16 -08:00
}
2022-09-04 10:52:20 -07:00
async rawRequestHeaders ( ) : Promise < HeadersArray > {
2023-11-01 16:36:39 -07:00
return this . _overrides ? . headers || this . _rawRequestHeadersPromise ;
2021-09-01 18:28:20 -07:00
}
2021-08-29 11:21:06 -07:00
response ( ) : PromiseLike < Response | null > {
2023-11-01 16:36:39 -07:00
return this . _waitForResponsePromise ;
2019-12-11 17:46:26 -08:00
}
2020-03-13 08:54:19 -07:00
_existingResponse ( ) : Response | null {
return this . _response ;
2019-12-11 16:19:37 -08:00
}
_setResponse ( response : Response ) {
this . _response = response ;
2021-08-29 11:21:06 -07:00
this . _waitForResponsePromise . resolve ( response ) ;
2019-12-11 16:19:37 -08:00
}
2020-03-16 13:31:06 -07:00
_finalRequest ( ) : Request {
return this . _redirectedTo ? this . _redirectedTo . _finalRequest ( ) : this ;
}
2022-07-01 12:49:43 -07:00
frame ( ) : frames . Frame | null {
2019-11-27 12:44:12 -08:00
return this . _frame ;
}
2022-07-01 12:49:43 -07:00
serviceWorker ( ) : pages . Worker | null {
return this . _serviceWorker ;
}
2019-11-27 12:44:12 -08:00
isNavigationRequest ( ) : boolean {
2019-12-14 08:20:51 -08:00
return ! ! this . _documentId ;
2019-11-27 12:44:12 -08:00
}
2020-03-16 13:31:06 -07:00
redirectedFrom ( ) : Request | null {
return this . _redirectedFrom ;
}
2020-06-26 11:51:47 -07:00
failure ( ) : { errorText : string } | null {
2019-12-30 14:05:28 -08:00
if ( this . _failureText === null )
2019-11-27 12:44:12 -08:00
return null ;
return {
errorText : this._failureText
} ;
}
2019-12-30 14:05:28 -08:00
2021-08-27 20:42:45 +02:00
bodySize ( ) : number {
return this . postDataBuffer ( ) ? . length || 0 ;
}
2021-10-04 15:10:16 -08:00
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
2022-06-24 13:51:09 -07:00
const headers = await this . rawRequestHeaders ( ) ;
2021-10-04 15:10:16 -08:00
for ( const header of headers )
headersSize += header . name . length + header . value . length + 4 ; // 4 = ': ' + '\r\n'
return headersSize ;
}
2020-03-13 14:30:40 -07:00
}
2021-02-09 09:00:00 -08:00
export class Route extends SdkObject {
2020-03-13 14:30:40 -07:00
private readonly _request : Request ;
private readonly _delegate : RouteDelegate ;
private _handled = false ;
constructor ( request : Request , delegate : RouteDelegate ) {
2022-08-18 20:12:33 +02:00
super ( request . _frame || request . _context , 'route' ) ;
2020-03-13 14:30:40 -07:00
this . _request = request ;
this . _delegate = delegate ;
2022-07-07 12:07:09 -08:00
this . _request . _context . addRouteInFlight ( this ) ;
2020-03-13 14:30:40 -07:00
}
request ( ) : Request {
return this . _request ;
}
2022-06-21 11:01:01 -07:00
async abort ( errorCode : string = 'failed' ) {
2022-01-27 14:58:02 -08:00
this . _startHandling ( ) ;
2023-06-02 13:00:27 -07:00
this . _request . _context . emit ( BrowserContext . Events . RequestAborted , this . _request ) ;
2023-06-30 13:08:18 -07:00
await this . _delegate . abort ( errorCode ) ;
2022-07-07 12:07:09 -08:00
this . _endHandling ( ) ;
2019-12-30 14:05:28 -08:00
}
2022-06-21 11:01:01 -07:00
async redirectNavigationRequest ( url : string ) {
this . _startHandling ( ) ;
assert ( this . _request . isNavigationRequest ( ) ) ;
2022-07-01 12:49:43 -07:00
this . _request . frame ( ) ! . redirectNavigation ( url , this . _request . _documentId ! , this . _request . headerValue ( 'referer' ) ) ;
2022-06-21 11:01:01 -07:00
}
2022-03-25 14:56:57 -07:00
async fulfill ( overrides : channels.RouteFulfillParams ) {
2022-01-27 14:58:02 -08:00
this . _startHandling ( ) ;
2021-06-18 11:04:48 -07:00
let body = overrides . body ;
let isBase64 = overrides . isBase64 || false ;
2021-08-11 14:47:05 -07:00
if ( body === undefined ) {
2021-09-08 14:59:12 -07:00
if ( overrides . fetchResponseUid ) {
2022-07-01 12:49:43 -07:00
const buffer = this . _request . _context . fetchRequest . fetchResponses . get ( overrides . fetchResponseUid ) || APIRequestContext . findResponseBody ( overrides . fetchResponseUid ) ;
2021-09-08 14:59:12 -07:00
assert ( buffer , 'Fetch response has been disposed' ) ;
2021-09-30 23:12:33 +02:00
body = buffer . toString ( 'base64' ) ;
isBase64 = true ;
2021-06-18 11:04:48 -07:00
} else {
body = '' ;
isBase64 = false ;
}
}
2022-03-25 14:56:57 -07:00
const headers = [ . . . ( overrides . headers || [ ] ) ] ;
2022-03-31 19:21:21 -07:00
this . _maybeAddCorsHeaders ( headers ) ;
2023-06-30 13:08:18 -07:00
this . _request . _context . emit ( BrowserContext . Events . RequestFulfilled , this . _request ) ;
2020-08-20 11:25:33 -07:00
await this . _delegate . fulfill ( {
2021-08-24 11:07:54 -07:00
status : overrides.status || 200 ,
2022-03-25 14:56:57 -07:00
headers ,
2021-06-18 11:04:48 -07:00
body ,
isBase64 ,
2020-08-20 11:25:33 -07:00
} ) ;
2022-07-07 12:07:09 -08:00
this . _endHandling ( ) ;
2019-12-30 14:05:28 -08:00
}
2022-03-31 19:21:21 -07:00
// See https://github.com/microsoft/playwright/issues/12929
private _maybeAddCorsHeaders ( headers : NameValue [ ] ) {
const origin = this . _request . headerValue ( 'origin' ) ;
if ( ! origin )
return ;
const requestUrl = new URL ( this . _request . url ( ) ) ;
if ( ! requestUrl . protocol . startsWith ( 'http' ) )
return ;
if ( requestUrl . origin === origin . trim ( ) )
return ;
const corsHeader = headers . find ( ( { name } ) = > name === 'access-control-allow-origin' ) ;
if ( corsHeader )
return ;
headers . push ( { name : 'access-control-allow-origin' , value : origin } ) ;
headers . push ( { name : 'access-control-allow-credentials' , value : 'true' } ) ;
headers . push ( { name : 'vary' , value : 'Origin' } ) ;
}
2023-06-14 09:37:19 -07:00
async continue ( overrides : types.NormalizedContinueOverrides ) {
2022-01-27 14:58:02 -08:00
this . _startHandling ( ) ;
2020-11-16 09:59:00 -08:00
if ( overrides . url ) {
const newUrl = new URL ( overrides . url ) ;
const oldUrl = new URL ( this . _request . url ( ) ) ;
if ( oldUrl . protocol !== newUrl . protocol )
2021-02-24 15:11:34 -08:00
throw new Error ( 'New URL must have same protocol as overridden URL' ) ;
2020-11-16 09:59:00 -08:00
}
2022-09-04 10:52:20 -07:00
this . _request . _setOverrides ( overrides ) ;
2023-06-06 16:55:53 -07:00
if ( ! overrides . isFallback )
2023-06-02 13:00:27 -07:00
this . _request . _context . emit ( BrowserContext . Events . RequestContinued , this . _request ) ;
2023-06-30 13:08:18 -07:00
await this . _delegate . continue ( this . _request , overrides ) ;
2022-07-07 12:07:09 -08:00
this . _endHandling ( ) ;
2019-12-30 14:05:28 -08:00
}
2022-01-27 14:58:02 -08:00
private _startHandling() {
assert ( ! this . _handled , 'Route is already handled!' ) ;
this . _handled = true ;
}
2022-07-07 12:07:09 -08:00
private _endHandling() {
this . _request . _context . removeRouteInFlight ( this ) ;
}
2019-11-27 12:44:12 -08:00
}
2023-01-27 10:43:19 -08:00
export type RouteHandler = ( route : Route , request : Request ) = > boolean ;
2020-03-13 14:30:40 -07:00
2020-04-01 14:42:47 -07:00
type GetResponseBodyCallback = ( ) = > Promise < Buffer > ;
2019-11-27 12:44:12 -08:00
2020-10-21 23:25:57 -07:00
export type ResourceTiming = {
startTime : number ;
domainLookupStart : number ;
domainLookupEnd : number ;
connectStart : number ;
secureConnectionStart : number ;
connectEnd : number ;
requestStart : number ;
responseStart : number ;
} ;
2021-09-02 10:39:57 -07:00
export type ResourceSizes = {
requestBodySize : number ,
requestHeadersSize : number ,
responseBodySize : number ,
responseHeadersSize : number ,
2022-06-29 18:11:22 -07:00
transferSize : number ,
2021-09-02 10:39:57 -07:00
} ;
2021-06-15 00:48:08 -07:00
export type RemoteAddr = {
ipAddress : string ;
port : number ;
2021-06-23 11:08:35 +02:00
} ;
2021-06-15 00:48:08 -07:00
export type SecurityDetails = {
2022-06-24 13:51:09 -07:00
protocol? : string ;
subjectName? : string ;
issuer? : string ;
validFrom? : number ;
validTo? : number ;
2021-06-15 00:48:08 -07:00
} ;
2021-02-09 09:00:00 -08:00
export class Response extends SdkObject {
2019-11-27 16:02:31 -08:00
private _request : Request ;
2020-04-01 14:42:47 -07:00
private _contentPromise : Promise < Buffer > | null = null ;
2021-08-30 20:43:40 -07:00
_finishedPromise = new ManualPromise < void > ( ) ;
2019-11-27 12:44:12 -08:00
private _status : number ;
private _statusText : string ;
private _url : string ;
2022-09-04 10:52:20 -07:00
private _headers : HeadersArray ;
2020-10-26 14:32:07 -07:00
private _headersMap = new Map < string , string > ( ) ;
2019-11-27 12:44:12 -08:00
private _getResponseBodyCallback : GetResponseBodyCallback ;
2020-10-21 23:25:57 -07:00
private _timing : ResourceTiming ;
2021-08-29 11:21:06 -07:00
private _serverAddrPromise = new ManualPromise < RemoteAddr | undefined > ( ) ;
private _securityDetailsPromise = new ManualPromise < SecurityDetails | undefined > ( ) ;
2022-09-04 10:52:20 -07:00
private _rawResponseHeadersPromise = new ManualPromise < HeadersArray > ( ) ;
2021-08-27 20:42:45 +02:00
private _httpVersion : string | undefined ;
2022-06-08 17:34:19 -04:00
private _fromServiceWorker : boolean ;
2022-06-29 18:11:22 -07:00
private _encodedBodySizePromise = new ManualPromise < number | null > ( ) ;
private _transferSizePromise = new ManualPromise < number | null > ( ) ;
private _responseHeadersSizePromise = new ManualPromise < number | null > ( ) ;
2019-11-27 12:44:12 -08:00
2022-09-04 10:52:20 -07:00
constructor ( request : Request , status : number , statusText : string , headers : HeadersArray , timing : ResourceTiming , getResponseBodyCallback : GetResponseBodyCallback , fromServiceWorker : boolean , httpVersion? : string ) {
2022-07-01 12:49:43 -07:00
super ( request . frame ( ) || request . _context , 'response' ) ;
2019-11-27 12:44:12 -08:00
this . _request = request ;
2020-10-21 23:25:57 -07:00
this . _timing = timing ;
2019-11-27 12:44:12 -08:00
this . _status = status ;
this . _statusText = statusText ;
this . _url = request . url ( ) ;
this . _headers = headers ;
2020-10-26 14:32:07 -07:00
for ( const { name , value } of this . _headers )
this . _headersMap . set ( name . toLowerCase ( ) , value ) ;
2019-11-27 12:44:12 -08:00
this . _getResponseBodyCallback = getResponseBodyCallback ;
2019-12-11 17:46:26 -08:00
this . _request . _setResponse ( this ) ;
2021-07-08 18:22:37 +02:00
this . _httpVersion = httpVersion ;
2022-06-08 17:34:19 -04:00
this . _fromServiceWorker = fromServiceWorker ;
2019-11-27 12:44:12 -08:00
}
2021-06-15 00:48:08 -07:00
_serverAddrFinished ( addr? : RemoteAddr ) {
2021-08-29 11:21:06 -07:00
this . _serverAddrPromise . resolve ( addr ) ;
2021-06-15 00:48:08 -07:00
}
_securityDetailsFinished ( securityDetails? : SecurityDetails ) {
2021-08-29 11:21:06 -07:00
this . _securityDetailsPromise . resolve ( securityDetails ) ;
2021-06-15 00:48:08 -07:00
}
2021-08-30 20:43:40 -07:00
_requestFinished ( responseEndTiming : number ) {
2020-10-21 23:25:57 -07:00
this . _request . _responseEndTiming = Math . max ( responseEndTiming , this . _timing . responseStart ) ;
2022-09-26 17:12:47 -07:00
// Set start time equal to end when request is served from memory cache.
if ( this . _timing . requestStart === - 1 )
this . _timing . requestStart = this . _request . _responseEndTiming ;
2021-08-30 20:43:40 -07:00
this . _finishedPromise . resolve ( ) ;
2019-11-27 12:44:12 -08:00
}
2021-07-08 18:22:37 +02:00
_setHttpVersion ( httpVersion : string ) {
this . _httpVersion = httpVersion ;
}
2019-11-27 12:44:12 -08:00
url ( ) : string {
return this . _url ;
}
status ( ) : number {
return this . _status ;
}
statusText ( ) : string {
return this . _statusText ;
}
2022-09-04 10:52:20 -07:00
headers ( ) : HeadersArray {
2020-08-18 15:38:29 -07:00
return this . _headers ;
2019-11-27 12:44:12 -08:00
}
2020-10-26 14:32:07 -07:00
headerValue ( name : string ) : string | undefined {
return this . _headersMap . get ( name ) ;
}
2021-09-01 18:28:20 -07:00
async rawResponseHeaders ( ) : Promise < NameValue [ ] > {
2022-06-24 13:51:09 -07:00
return this . _rawResponseHeadersPromise ;
2021-09-01 18:28:20 -07:00
}
2022-06-24 13:51:09 -07:00
// "null" means no raw headers available - we'll use provisional headers as raw headers.
2022-09-04 10:52:20 -07:00
setRawResponseHeaders ( headers : HeadersArray | null ) {
2022-06-24 13:51:09 -07:00
if ( ! this . _rawResponseHeadersPromise . isDone ( ) )
this . _rawResponseHeadersPromise . resolve ( headers || this . _headers ) ;
2021-09-01 18:28:20 -07:00
}
2022-06-29 18:11:22 -07:00
setTransferSize ( size : number | null ) {
this . _transferSizePromise . resolve ( size ) ;
}
setEncodedBodySize ( size : number | null ) {
this . _encodedBodySizePromise . resolve ( size ) ;
}
setResponseHeadersSize ( size : number | null ) {
this . _responseHeadersSizePromise . resolve ( size ) ;
}
2020-10-21 23:25:57 -07:00
timing ( ) : ResourceTiming {
return this . _timing ;
}
2021-06-15 00:48:08 -07:00
async serverAddr ( ) : Promise < RemoteAddr | null > {
return await this . _serverAddrPromise || null ;
}
async securityDetails ( ) : Promise < SecurityDetails | null > {
return await this . _securityDetailsPromise || null ;
}
2020-04-01 14:42:47 -07:00
body ( ) : Promise < Buffer > {
2019-11-27 12:44:12 -08:00
if ( ! this . _contentPromise ) {
2021-08-30 20:43:40 -07:00
this . _contentPromise = this . _finishedPromise . then ( async ( ) = > {
2021-08-31 12:11:32 -07:00
if ( this . _status >= 300 && this . _status <= 399 )
2021-08-30 20:43:40 -07:00
throw new Error ( 'Response body is unavailable for redirect responses' ) ;
2019-11-27 12:44:12 -08:00
return this . _getResponseBodyCallback ( ) ;
} ) ;
}
return this . _contentPromise ;
}
2019-11-27 16:02:31 -08:00
request ( ) : Request {
2019-11-27 12:44:12 -08:00
return this . _request ;
}
2022-07-01 12:49:43 -07:00
frame ( ) : frames . Frame | null {
2019-11-27 12:44:12 -08:00
return this . _request . frame ( ) ;
}
2021-08-27 20:42:45 +02:00
httpVersion ( ) : string {
if ( ! this . _httpVersion )
return 'HTTP/1.1' ;
if ( this . _httpVersion === 'http/1.1' )
return 'HTTP/1.1' ;
2021-12-10 11:24:52 -08:00
if ( this . _httpVersion === 'h2' )
return 'HTTP/2.0' ;
2021-08-27 20:42:45 +02:00
return this . _httpVersion ;
}
2022-06-08 17:34:19 -04:00
fromServiceWorker ( ) : boolean {
return this . _fromServiceWorker ;
}
2022-06-29 18:11:22 -07:00
async responseHeadersSize ( ) : Promise < number > {
const availableSize = await this . _responseHeadersSizePromise ;
if ( availableSize !== null )
return availableSize ;
// Fallback to calculating it manually.
2021-08-27 20:42:45 +02:00
let headersSize = 4 ; // 4 = 2 spaces + 2 line breaks (HTTP/1.1 200 Ok\r\n)
headersSize += 8 ; // httpVersion;
headersSize += 3 ; // statusCode;
headersSize += this . statusText ( ) . length ;
2022-06-29 18:11:22 -07:00
const headers = await this . _rawResponseHeadersPromise ;
2021-09-02 10:39:57 -07:00
for ( const header of headers )
2021-08-27 20:42:45 +02:00
headersSize += header . name . length + header . value . length + 4 ; // 4 = ': ' + '\r\n'
headersSize += 2 ; // '\r\n'
return headersSize ;
}
2021-09-02 10:39:57 -07:00
async sizes ( ) : Promise < ResourceSizes > {
2021-10-04 15:10:16 -08:00
const requestHeadersSize = await this . _request . requestHeadersSize ( ) ;
2022-06-29 18:11:22 -07:00
const responseHeadersSize = await this . responseHeadersSize ( ) ;
let encodedBodySize = await this . _encodedBodySizePromise ;
if ( encodedBodySize === null ) {
// Fallback to calculating it manually.
const headers = await this . _rawResponseHeadersPromise ;
2021-09-02 20:48:23 -07:00
const contentLength = headers . find ( h = > h . name . toLowerCase ( ) === 'content-length' ) ? . value ;
2021-09-07 19:19:12 +02:00
encodedBodySize = contentLength ? + contentLength : 0 ;
2021-09-02 10:39:57 -07:00
}
2022-06-29 18:11:22 -07:00
let transferSize = await this . _transferSizePromise ;
if ( transferSize === null ) {
// Fallback to calculating it manually.
transferSize = responseHeadersSize + encodedBodySize ;
}
2021-09-02 10:39:57 -07:00
return {
requestBodySize : this._request.bodySize ( ) ,
requestHeadersSize ,
2021-09-07 19:19:12 +02:00
responseBodySize : encodedBodySize ,
2021-09-02 10:39:57 -07:00
responseHeadersSize ,
2022-06-29 18:11:22 -07:00
transferSize ,
2021-09-02 10:39:57 -07:00
} ;
}
2019-11-27 12:44:12 -08:00
}
2019-12-30 14:05:28 -08:00
2021-02-09 09:00:00 -08:00
export class WebSocket extends SdkObject {
2020-10-26 22:20:43 -07:00
private _url : string ;
2021-11-24 10:46:32 -08:00
private _notified = false ;
2020-10-26 22:20:43 -07:00
static Events = {
Close : 'close' ,
2020-11-19 12:09:42 -08:00
SocketError : 'socketerror' ,
2020-10-26 22:20:43 -07:00
FrameReceived : 'framereceived' ,
FrameSent : 'framesent' ,
} ;
2021-02-09 09:00:00 -08:00
constructor ( parent : SdkObject , url : string ) {
2021-04-20 23:03:56 -07:00
super ( parent , 'ws' ) ;
2020-10-26 22:20:43 -07:00
this . _url = url ;
}
2021-11-24 10:46:32 -08:00
markAsNotified() {
// Sometimes we get "onWebSocketRequest" twice, at least in Chromium.
// Perhaps websocket is restarted because of chrome.webRequest extensions api?
// Or maybe the handshake response was a redirect?
if ( this . _notified )
return false ;
this . _notified = true ;
return true ;
}
2020-10-26 22:20:43 -07:00
url ( ) : string {
return this . _url ;
}
frameSent ( opcode : number , data : string ) {
this . emit ( WebSocket . Events . FrameSent , { opcode , data } ) ;
}
frameReceived ( opcode : number , data : string ) {
this . emit ( WebSocket . Events . FrameReceived , { opcode , data } ) ;
}
error ( errorMessage : string ) {
2020-11-19 12:09:42 -08:00
this . emit ( WebSocket . Events . SocketError , errorMessage ) ;
2020-10-26 22:20:43 -07:00
}
closed() {
this . emit ( WebSocket . Events . Close ) ;
}
}
2020-03-13 14:30:40 -07:00
export interface RouteDelegate {
2019-12-30 14:05:28 -08:00
abort ( errorCode : string ) : Promise < void > ;
2020-06-29 16:37:38 -07:00
fulfill ( response : types.NormalizedFulfillResponse ) : Promise < void > ;
2021-11-02 17:48:38 -07:00
continue ( request : Request , overrides : types.NormalizedContinueOverrides ) : Promise < void > ;
2019-12-30 14:05:28 -08:00
}
// List taken from https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml with extra 306 and 418 codes.
export const STATUS_TEXTS : { [ status : string ] : string } = {
'100' : 'Continue' ,
'101' : 'Switching Protocols' ,
'102' : 'Processing' ,
'103' : 'Early Hints' ,
'200' : 'OK' ,
'201' : 'Created' ,
'202' : 'Accepted' ,
'203' : 'Non-Authoritative Information' ,
'204' : 'No Content' ,
'205' : 'Reset Content' ,
'206' : 'Partial Content' ,
'207' : 'Multi-Status' ,
'208' : 'Already Reported' ,
'226' : 'IM Used' ,
'300' : 'Multiple Choices' ,
'301' : 'Moved Permanently' ,
'302' : 'Found' ,
'303' : 'See Other' ,
'304' : 'Not Modified' ,
'305' : 'Use Proxy' ,
'306' : 'Switch Proxy' ,
'307' : 'Temporary Redirect' ,
'308' : 'Permanent Redirect' ,
'400' : 'Bad Request' ,
'401' : 'Unauthorized' ,
'402' : 'Payment Required' ,
'403' : 'Forbidden' ,
'404' : 'Not Found' ,
'405' : 'Method Not Allowed' ,
'406' : 'Not Acceptable' ,
'407' : 'Proxy Authentication Required' ,
'408' : 'Request Timeout' ,
'409' : 'Conflict' ,
'410' : 'Gone' ,
'411' : 'Length Required' ,
'412' : 'Precondition Failed' ,
'413' : 'Payload Too Large' ,
'414' : 'URI Too Long' ,
'415' : 'Unsupported Media Type' ,
'416' : 'Range Not Satisfiable' ,
'417' : 'Expectation Failed' ,
'418' : 'I\'m a teapot' ,
'421' : 'Misdirected Request' ,
'422' : 'Unprocessable Entity' ,
'423' : 'Locked' ,
'424' : 'Failed Dependency' ,
'425' : 'Too Early' ,
'426' : 'Upgrade Required' ,
'428' : 'Precondition Required' ,
'429' : 'Too Many Requests' ,
'431' : 'Request Header Fields Too Large' ,
'451' : 'Unavailable For Legal Reasons' ,
'500' : 'Internal Server Error' ,
'501' : 'Not Implemented' ,
'502' : 'Bad Gateway' ,
'503' : 'Service Unavailable' ,
'504' : 'Gateway Timeout' ,
'505' : 'HTTP Version Not Supported' ,
'506' : 'Variant Also Negotiates' ,
'507' : 'Insufficient Storage' ,
'508' : 'Loop Detected' ,
'510' : 'Not Extended' ,
'511' : 'Network Authentication Required' ,
} ;
2020-02-26 12:42:20 -08:00
2022-09-04 10:52:20 -07:00
export function singleHeader ( name : string , value : string ) : HeadersArray {
2020-08-18 15:38:29 -07:00
return [ { name , value } ] ;
2020-02-26 12:42:20 -08:00
}
2022-09-04 10:52:20 -07:00
export function mergeHeaders ( headers : ( HeadersArray | undefined | null ) [ ] ) : HeadersArray {
2020-02-26 12:42:20 -08:00
const lowerCaseToValue = new Map < string , string > ( ) ;
const lowerCaseToOriginalCase = new Map < string , string > ( ) ;
for ( const h of headers ) {
if ( ! h )
continue ;
2020-08-18 15:38:29 -07:00
for ( const { name , value } of h ) {
const lower = name . toLowerCase ( ) ;
lowerCaseToOriginalCase . set ( lower , name ) ;
lowerCaseToValue . set ( lower , value ) ;
2020-02-26 12:42:20 -08:00
}
}
2022-09-04 10:52:20 -07:00
const result : HeadersArray = [ ] ;
2020-02-26 12:42:20 -08:00
for ( const [ lower , value ] of lowerCaseToValue )
2020-08-18 15:38:29 -07:00
result . push ( { name : lowerCaseToOriginalCase.get ( lower ) ! , value } ) ;
2020-02-26 12:42:20 -08:00
return result ;
}