2019-12-10 15:13:56 -08:00
/ * *
* Copyright 2017 Google Inc . All rights reserved .
* Modifications 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 .
* /
2020-09-04 22:37:38 -07:00
import { TimeoutSettings } from '../utils/timeoutSettings' ;
2020-09-18 17:36:43 -07:00
import { mkdirIfNeeded } from '../utils/utils' ;
import { Browser , BrowserOptions } from './browser' ;
2020-09-04 22:37:38 -07:00
import { Download } from './download' ;
import * as frames from './frames' ;
2020-08-17 16:19:21 -07:00
import { helper } from './helper' ;
2020-03-05 17:22:57 -08:00
import * as network from './network' ;
2020-11-12 12:41:23 -08:00
import { Page , PageBinding , PageDelegate } from './page' ;
2021-02-09 14:44:48 -08:00
import { Progress } from './progress' ;
import { Selectors , serverSelectors } from './selectors' ;
2020-09-04 22:37:38 -07:00
import * as types from './types' ;
2021-02-11 06:36:15 -08:00
import path from 'path' ;
2021-02-09 14:44:48 -08:00
import { CallMetadata , SdkObject } from './instrumentation' ;
2019-12-11 07:18:43 -08:00
2020-09-04 22:37:38 -07:00
export class Video {
2020-09-18 17:36:43 -07:00
readonly _videoId : string ;
readonly _path : string ;
2020-10-13 22:15:51 -07:00
readonly _relativePath : string ;
2020-09-18 17:36:43 -07:00
readonly _context : BrowserContext ;
readonly _finishedPromise : Promise < void > ;
2020-10-02 17:27:56 -07:00
private _finishCallback : ( ) = > void = ( ) = > { } ;
private _callbackOnFinish ? : ( ) = > Promise < void > ;
2020-09-18 17:36:43 -07:00
2020-10-13 22:15:51 -07:00
constructor ( context : BrowserContext , videoId : string , p : string ) {
2020-09-18 17:36:43 -07:00
this . _videoId = videoId ;
2020-10-13 22:15:51 -07:00
this . _path = p ;
2020-11-02 19:42:05 -08:00
this . _relativePath = path . relative ( context . _options . recordVideo ! . dir , p ) ;
2020-09-18 17:36:43 -07:00
this . _context = context ;
2020-08-25 13:07:32 -07:00
this . _finishedPromise = new Promise ( fulfill = > this . _finishCallback = fulfill ) ;
}
2020-10-02 17:27:56 -07:00
async _finish() {
if ( this . _callbackOnFinish )
await this . _callbackOnFinish ( ) ;
this . _finishCallback ( ) ;
}
_waitForCallbackOnFinish ( callback : ( ) = > Promise < void > ) {
this . _callbackOnFinish = callback ;
}
2020-08-19 12:45:31 -07:00
}
2021-02-09 09:00:00 -08:00
export abstract class BrowserContext extends SdkObject {
2020-08-21 16:26:33 -07:00
static Events = {
Close : 'close' ,
Page : 'page' ,
2020-10-02 17:27:56 -07:00
VideoStarted : 'videostarted' ,
2021-01-25 14:49:26 -08:00
BeforeClose : 'beforeclose' ,
StdOut : 'stdout' ,
StdErr : 'stderr' ,
2020-08-21 16:26:33 -07:00
} ;
2020-03-05 17:22:57 -08:00
readonly _timeoutSettings = new TimeoutSettings ( ) ;
readonly _pageBindings = new Map < string , PageBinding > ( ) ;
2020-08-17 14:12:31 -07:00
readonly _options : types.BrowserContextOptions ;
2020-08-18 17:34:04 -07:00
_requestInterceptor? : network.RouteHandler ;
2020-06-29 16:26:32 -07:00
private _isPersistentContext : boolean ;
2020-07-08 21:36:03 -07:00
private _closedStatus : 'open' | 'closing' | 'closed' = 'open' ;
2020-06-10 15:12:50 -07:00
readonly _closePromise : Promise < Error > ;
2020-03-05 17:22:57 -08:00
private _closePromiseFulfill : ( ( error : Error ) = > void ) | undefined ;
2020-03-20 19:45:35 -07:00
readonly _permissions = new Map < string , string [ ] > ( ) ;
2020-04-02 17:56:14 -07:00
readonly _downloads = new Set < Download > ( ) ;
2020-08-19 10:31:59 -07:00
readonly _browser : Browser ;
2020-08-25 16:33:16 -07:00
readonly _browserContextId : string | undefined ;
2020-09-02 16:15:43 -07:00
private _selectors? : Selectors ;
2020-11-13 14:24:53 -08:00
private _origins = new Set < string > ( ) ;
2021-02-02 14:25:51 -08:00
terminalSize : { rows? : number , columns? : number } = { } ;
2020-03-05 17:22:57 -08:00
2020-08-25 16:33:16 -07:00
constructor ( browser : Browser , options : types.BrowserContextOptions , browserContextId : string | undefined ) {
2021-02-09 09:00:00 -08:00
super ( browser ) ;
this . attribution . context = this ;
2020-08-19 10:31:59 -07:00
this . _browser = browser ;
2020-03-05 17:22:57 -08:00
this . _options = options ;
2020-08-25 16:33:16 -07:00
this . _browserContextId = browserContextId ;
this . _isPersistentContext = ! browserContextId ;
2020-03-05 17:22:57 -08:00
this . _closePromise = new Promise ( fulfill = > this . _closePromiseFulfill = fulfill ) ;
}
2020-09-02 16:15:43 -07:00
_setSelectors ( selectors : Selectors ) {
this . _selectors = selectors ;
}
2021-02-09 09:00:00 -08:00
selectors ( ) : Selectors {
2021-02-09 14:44:48 -08:00
return this . _selectors || serverSelectors ;
2020-09-02 16:15:43 -07:00
}
2020-05-27 22:16:54 -07:00
async _initialize() {
2021-02-09 14:44:48 -08:00
await this . instrumentation . onContextCreated ( this ) ;
2020-08-28 10:51:55 -07:00
}
2020-10-01 11:06:19 -07:00
async _ensureVideosPath() {
2020-11-02 19:42:05 -08:00
if ( this . _options . recordVideo )
await mkdirIfNeeded ( path . join ( this . _options . recordVideo . dir , 'dummy' ) ) ;
2020-09-18 17:36:43 -07:00
}
2020-03-05 17:22:57 -08:00
_browserClosed() {
2020-03-13 11:33:33 -07:00
for ( const page of this . pages ( ) )
2020-03-05 17:22:57 -08:00
page . _didClose ( ) ;
2020-06-29 16:26:32 -07:00
this . _didCloseInternal ( ) ;
2020-03-05 17:22:57 -08:00
}
2020-06-29 16:26:32 -07:00
private _didCloseInternal() {
2020-07-08 21:36:03 -07:00
if ( this . _closedStatus === 'closed' ) {
// We can come here twice if we close browser context and browser
// at the same time.
return ;
}
this . _closedStatus = 'closed' ;
2020-04-02 17:56:14 -07:00
this . _downloads . clear ( ) ;
2020-06-29 16:26:32 -07:00
this . _closePromiseFulfill ! ( new Error ( 'Context closed' ) ) ;
2020-08-21 16:26:33 -07:00
this . emit ( BrowserContext . Events . Close ) ;
2020-03-05 17:22:57 -08:00
}
// BrowserContext methods.
2020-03-13 11:33:33 -07:00
abstract pages ( ) : Page [ ] ;
2020-11-12 12:41:23 -08:00
abstract newPageDelegate ( ) : Promise < PageDelegate > ;
2020-06-25 08:30:56 -07:00
abstract _doCookies ( urls : string [ ] ) : Promise < types.NetworkCookie [ ] > ;
abstract addCookies ( cookies : types.SetNetworkCookieParam [ ] ) : Promise < void > ;
2020-03-05 17:22:57 -08:00
abstract clearCookies ( ) : Promise < void > ;
2020-03-17 15:32:50 -07:00
abstract _doGrantPermissions ( origin : string , permissions : string [ ] ) : Promise < void > ;
abstract _doClearPermissions ( ) : Promise < void > ;
2020-08-17 16:19:21 -07:00
abstract setGeolocation ( geolocation? : types.Geolocation ) : Promise < void > ;
abstract _doSetHTTPCredentials ( httpCredentials? : types.Credentials ) : Promise < void > ;
2020-08-18 15:38:29 -07:00
abstract setExtraHTTPHeaders ( headers : types.HeadersArray ) : Promise < void > ;
2020-03-05 17:22:57 -08:00
abstract setOffline ( offline : boolean ) : Promise < void > ;
2020-06-25 08:30:56 -07:00
abstract _doAddInitScript ( expression : string ) : Promise < void > ;
2020-05-18 14:28:06 -07:00
abstract _doExposeBinding ( binding : PageBinding ) : Promise < void > ;
2020-08-18 17:34:04 -07:00
abstract _doUpdateRequestInterception ( ) : Promise < void > ;
2020-06-29 16:26:32 -07:00
abstract _doClose ( ) : Promise < void > ;
2020-03-05 17:22:57 -08:00
2020-06-25 08:30:56 -07:00
async cookies ( urls : string | string [ ] | undefined = [ ] ) : Promise < types.NetworkCookie [ ] > {
if ( urls && ! Array . isArray ( urls ) )
urls = [ urls ] ;
return await this . _doCookies ( urls as string [ ] ) ;
}
2020-08-17 16:19:21 -07:00
setHTTPCredentials ( httpCredentials? : types.Credentials ) : Promise < void > {
2020-06-30 11:02:12 -07:00
return this . _doSetHTTPCredentials ( httpCredentials ) ;
}
2020-10-01 22:47:31 -07:00
async exposeBinding ( name : string , needsHandle : boolean , playwrightBinding : frames.FunctionWithSource ) : Promise < void > {
2020-12-02 13:43:16 -08:00
const identifier = PageBinding . identifier ( name , 'main' ) ;
if ( this . _pageBindings . has ( identifier ) )
throw new Error ( ` Function " ${ name } " has been already registered ` ) ;
2020-05-18 14:28:06 -07:00
for ( const page of this . pages ( ) ) {
2020-12-02 13:43:16 -08:00
if ( page . getBinding ( name , 'main' ) )
2020-05-18 14:28:06 -07:00
throw new Error ( ` Function " ${ name } " has been already registered in one of the pages ` ) ;
}
2020-12-02 13:43:16 -08:00
const binding = new PageBinding ( name , playwrightBinding , needsHandle , 'main' ) ;
this . _pageBindings . set ( identifier , binding ) ;
2020-05-18 14:28:06 -07:00
this . _doExposeBinding ( binding ) ;
}
2020-08-20 14:19:27 -07:00
async grantPermissions ( permissions : string [ ] , origin? : string ) {
let resolvedOrigin = '*' ;
if ( origin ) {
const url = new URL ( origin ) ;
resolvedOrigin = url . origin ;
2020-03-17 15:32:50 -07:00
}
2020-08-20 14:19:27 -07:00
const existing = new Set ( this . _permissions . get ( resolvedOrigin ) || [ ] ) ;
2020-03-17 15:32:50 -07:00
permissions . forEach ( p = > existing . add ( p ) ) ;
const list = [ . . . existing . values ( ) ] ;
2020-08-20 14:19:27 -07:00
this . _permissions . set ( resolvedOrigin , list ) ;
await this . _doGrantPermissions ( resolvedOrigin , list ) ;
2020-03-17 15:32:50 -07:00
}
async clearPermissions() {
this . _permissions . clear ( ) ;
await this . _doClearPermissions ( ) ;
}
2020-03-05 17:22:57 -08:00
setDefaultNavigationTimeout ( timeout : number ) {
this . _timeoutSettings . setDefaultNavigationTimeout ( timeout ) ;
}
setDefaultTimeout ( timeout : number ) {
this . _timeoutSettings . setDefaultTimeout ( timeout ) ;
}
2020-04-20 07:52:26 -07:00
2020-12-10 16:37:18 -08:00
async _loadDefaultContextAsIs ( progress : Progress ) : Promise < Page [ ] > {
2020-08-17 14:36:51 -07:00
if ( ! this . pages ( ) . length ) {
2020-08-21 16:26:33 -07:00
const waitForEvent = helper . waitForEvent ( progress , this , BrowserContext . Events . Page ) ;
2020-08-17 14:36:51 -07:00
progress . cleanupWhenAborted ( ( ) = > waitForEvent . dispose ) ;
await waitForEvent . promise ;
}
2020-05-10 15:23:53 -07:00
const pages = this . pages ( ) ;
2020-09-14 16:43:17 -07:00
await pages [ 0 ] . mainFrame ( ) . _waitForLoadState ( progress , 'load' ) ;
2020-12-10 16:37:18 -08:00
return pages ;
}
async _loadDefaultContext ( progress : Progress ) {
const pages = await this . _loadDefaultContextAsIs ( progress ) ;
2020-05-21 15:13:16 -07:00
if ( this . _options . isMobile || this . _options . locale ) {
// Workaround for:
// - chromium fails to change isMobile for existing page;
// - webkit fails to change locale for existing page.
const oldPage = pages [ 0 ] ;
await this . newPage ( ) ;
await oldPage . close ( ) ;
2020-05-10 15:23:53 -07:00
}
}
2020-06-05 13:50:15 -07:00
protected _authenticateProxyViaHeader() {
2021-01-29 16:00:56 -08:00
const proxy = this . _options . proxy || this . _browser . options . proxy || { username : undefined , password : undefined } ;
2020-06-05 13:50:15 -07:00
const { username , password } = proxy ;
if ( username ) {
this . _options . httpCredentials = { username , password : password ! } ;
const token = Buffer . from ( ` ${ username } : ${ password } ` ) . toString ( 'base64' ) ;
2020-08-18 15:38:29 -07:00
this . _options . extraHTTPHeaders = network . mergeHeaders ( [
this . _options . extraHTTPHeaders ,
network . singleHeader ( 'Proxy-Authorization' , ` Basic ${ token } ` ) ,
] ) ;
2020-06-05 13:50:15 -07:00
}
}
protected _authenticateProxyViaCredentials() {
2021-01-29 16:00:56 -08:00
const proxy = this . _options . proxy || this . _browser . options . proxy ;
2020-06-05 13:50:15 -07:00
if ( ! proxy )
return ;
const { username , password } = proxy ;
2020-12-21 11:47:13 -08:00
if ( username )
this . _options . httpCredentials = { username , password : password || '' } ;
2020-06-05 13:50:15 -07:00
}
2020-06-29 16:26:32 -07:00
2020-08-18 17:34:04 -07:00
async _setRequestInterceptor ( handler : network.RouteHandler | undefined ) : Promise < void > {
this . _requestInterceptor = handler ;
await this . _doUpdateRequestInterception ( ) ;
}
2020-11-04 16:23:38 -08:00
isClosingOrClosed() {
return this . _closedStatus !== 'open' ;
}
2020-06-29 16:26:32 -07:00
async close() {
2020-07-08 21:36:03 -07:00
if ( this . _closedStatus === 'open' ) {
2021-01-25 14:49:26 -08:00
this . emit ( BrowserContext . Events . BeforeClose ) ;
2020-07-08 21:36:03 -07:00
this . _closedStatus = 'closing' ;
2020-10-04 18:18:05 -07:00
2021-02-09 14:44:48 -08:00
await this . instrumentation . onContextWillDestroy ( this ) ;
2020-10-26 14:32:07 -07:00
2020-10-04 18:18:05 -07:00
// Collect videos/downloads that we will await.
2020-09-18 17:36:43 -07:00
const promises : Promise < any > [ ] = [ ] ;
for ( const download of this . _downloads )
promises . push ( download . delete ( ) ) ;
for ( const video of this . _browser . _idToVideo . values ( ) ) {
if ( video . _context === this )
promises . push ( video . _finishedPromise ) ;
}
2020-10-04 18:18:05 -07:00
if ( this . _isPersistentContext ) {
// Close all the pages instead of the context,
// because we cannot close the default context.
await Promise . all ( this . pages ( ) . map ( page = > page . close ( ) ) ) ;
} else {
// Close the context.
await this . _doClose ( ) ;
}
// Wait for the videos/downloads to finish.
2020-09-18 17:36:43 -07:00
await Promise . all ( promises ) ;
2020-10-04 18:18:05 -07:00
// Persistent context should also close the browser.
if ( this . _isPersistentContext )
await this . _browser . close ( ) ;
// Bookkeeping.
2021-02-09 14:44:48 -08:00
await this . instrumentation . onContextWillDestroy ( this ) ;
2020-06-29 16:26:32 -07:00
this . _didCloseInternal ( ) ;
}
await this . _closePromise ;
}
2020-11-12 12:41:23 -08:00
async newPage ( ) : Promise < Page > {
const pageDelegate = await this . newPageDelegate ( ) ;
const pageOrError = await pageDelegate . pageOrError ( ) ;
if ( pageOrError instanceof Page ) {
if ( pageOrError . isClosed ( ) )
throw new Error ( 'Page has been closed.' ) ;
return pageOrError ;
}
throw pageOrError ;
}
2020-11-13 14:24:53 -08:00
addVisitedOrigin ( origin : string ) {
this . _origins . add ( origin ) ;
}
2021-02-09 14:44:48 -08:00
async storageState ( metadata : CallMetadata ) : Promise < types.StorageState > {
2020-11-13 14:24:53 -08:00
const result : types.StorageState = {
cookies : ( await this . cookies ( ) ) . filter ( c = > c . value !== '' ) ,
origins : [ ]
} ;
if ( this . _origins . size ) {
const page = await this . newPage ( ) ;
await page . _setServerRequestInterceptor ( handler = > {
handler . fulfill ( { body : '<html></html>' } ) . catch ( ( ) = > { } ) ;
} ) ;
for ( const origin of this . _origins ) {
const originStorage : types.OriginStorage = { origin , localStorage : [ ] } ;
result . origins . push ( originStorage ) ;
const frame = page . mainFrame ( ) ;
2021-02-09 14:44:48 -08:00
await frame . goto ( metadata , origin ) ;
2020-11-13 14:24:53 -08:00
const storage = await frame . _evaluateExpression ( ` ({
localStorage : Object.keys ( localStorage ) . map ( name = > ( { name , value : localStorage.getItem ( name ) } ) ) ,
} ) ` , false, undefined, 'utility');
originStorage . localStorage = storage . localStorage ;
}
await page . close ( ) ;
}
return result ;
}
2021-02-09 14:44:48 -08:00
async setStorageState ( metadata : CallMetadata , state : types.SetStorageState ) {
2020-11-13 14:24:53 -08:00
if ( state . cookies )
await this . addCookies ( state . cookies ) ;
if ( state . origins && state . origins . length ) {
const page = await this . newPage ( ) ;
await page . _setServerRequestInterceptor ( handler = > {
handler . fulfill ( { body : '<html></html>' } ) . catch ( ( ) = > { } ) ;
} ) ;
for ( const originState of state . origins ) {
const frame = page . mainFrame ( ) ;
2021-02-09 14:44:48 -08:00
await frame . goto ( metadata , originState . origin ) ;
2020-11-13 14:24:53 -08:00
await frame . _evaluateExpression ( `
originState = > {
for ( const { name , value } of ( originState . localStorage || [ ] ) )
localStorage . setItem ( name , value ) ;
} ` , true, originState, 'utility');
}
await page . close ( ) ;
}
}
2020-12-23 14:15:16 -08:00
2021-01-24 19:21:19 -08:00
async extendInjectedScript ( source : string ) {
2021-01-24 08:44:11 -08:00
const installInFrame = ( frame : frames.Frame ) = > frame . extendInjectedScript ( source ) . catch ( e = > { } ) ;
2020-12-23 14:15:16 -08:00
const installInPage = ( page : Page ) = > {
2021-01-13 14:25:42 -08:00
page . on ( Page . Events . InternalFrameNavigatedToNewDocument , installInFrame ) ;
2020-12-23 14:15:16 -08:00
return Promise . all ( page . frames ( ) . map ( installInFrame ) ) ;
} ;
this . on ( BrowserContext . Events . Page , installInPage ) ;
return Promise . all ( this . pages ( ) . map ( installInPage ) ) ;
}
2020-02-24 08:53:30 -08:00
}
2020-01-13 17:16:05 -08:00
2020-08-19 10:31:59 -07:00
export function assertBrowserContextIsNotOwned ( context : BrowserContext ) {
2020-03-13 11:33:33 -07:00
for ( const page of context . pages ( ) ) {
2020-02-24 08:53:30 -08:00
if ( page . _ownedContext )
throw new Error ( 'Please use browser.newContext() for multi-page scripts that share the context.' ) ;
2020-01-13 17:16:05 -08:00
}
2020-02-24 08:53:30 -08:00
}
2020-02-11 10:27:19 -08:00
2020-09-18 17:36:43 -07:00
export function validateBrowserContextOptions ( options : types.BrowserContextOptions , browserOptions : BrowserOptions ) {
2020-08-18 09:37:40 -07:00
if ( options . noDefaultViewport && options . deviceScaleFactor !== undefined )
2020-05-12 18:31:17 -07:00
throw new Error ( ` "deviceScaleFactor" option is not supported with null "viewport" ` ) ;
2020-08-18 09:37:40 -07:00
if ( options . noDefaultViewport && options . isMobile !== undefined )
2020-05-12 18:31:17 -07:00
throw new Error ( ` "isMobile" option is not supported with null "viewport" ` ) ;
2020-08-18 09:37:40 -07:00
if ( ! options . viewport && ! options . noDefaultViewport )
options . viewport = { width : 1280 , height : 720 } ;
2021-02-10 13:37:27 -08:00
if ( options . recordVideo ) {
if ( ! options . recordVideo . size ) {
if ( options . noDefaultViewport ) {
options . recordVideo . size = { width : 800 , height : 600 } ;
} else {
const size = options . viewport ! ;
const scale = Math . min ( 1 , 800 / Math . max ( size . width , size . height ) ) ;
2021-02-08 10:59:48 -08:00
options . recordVideo . size = {
width : Math.floor ( size . width * scale ) ,
height : Math.floor ( size . height * scale )
} ;
}
}
2021-02-10 13:37:27 -08:00
// Make sure both dimensions are odd, this is required for vp8
options . recordVideo . size ! . width &= ~ 1 ;
options . recordVideo . size ! . height &= ~ 1 ;
2021-02-08 10:59:48 -08:00
}
2020-10-29 16:12:30 -07:00
if ( options . proxy ) {
if ( ! browserOptions . proxy )
2020-11-02 12:48:05 -08:00
throw new Error ( ` Browser needs to be launched with the global proxy. If all contexts override the proxy, global proxy will be never used and can be any string, for example "launch({ proxy: { server: 'per-context' } })" ` ) ;
2020-10-29 16:12:30 -07:00
options . proxy = normalizeProxySettings ( options . proxy ) ;
}
2020-08-18 09:37:40 -07:00
verifyGeolocation ( options . geolocation ) ;
2019-12-10 15:13:56 -08:00
}
2020-01-13 15:39:13 -08:00
2020-08-18 09:37:40 -07:00
export function verifyGeolocation ( geolocation? : types.Geolocation ) {
if ( ! geolocation )
return ;
geolocation . accuracy = geolocation . accuracy || 0 ;
const { longitude , latitude , accuracy } = geolocation ;
2020-07-21 15:25:31 -07:00
if ( longitude < - 180 || longitude > 180 )
throw new Error ( ` geolocation.longitude: precondition -180 <= LONGITUDE <= 180 failed. ` ) ;
if ( latitude < - 90 || latitude > 90 )
throw new Error ( ` geolocation.latitude: precondition -90 <= LATITUDE <= 90 failed. ` ) ;
if ( accuracy < 0 )
throw new Error ( ` geolocation.accuracy: precondition 0 <= ACCURACY failed. ` ) ;
2020-01-13 15:39:13 -08:00
}
2020-06-05 13:50:15 -07:00
2020-08-28 14:17:16 -07:00
export function normalizeProxySettings ( proxy : types.ProxySettings ) : types . ProxySettings {
2020-06-05 13:50:15 -07:00
let { server , bypass } = proxy ;
2020-08-28 14:17:16 -07:00
let url ;
try {
// new URL('127.0.0.1:8080') throws
// new URL('localhost:8080') fails to parse host or protocol
// In both of these cases, we need to try re-parse URL with `http://` prefix.
url = new URL ( server ) ;
if ( ! url . host || ! url . protocol )
url = new URL ( 'http://' + server ) ;
} catch ( e ) {
2020-06-05 13:50:15 -07:00
url = new URL ( 'http://' + server ) ;
}
2021-02-08 12:07:45 -08:00
if ( url . protocol === 'socks4:' && ( proxy . username || proxy . password ) )
throw new Error ( ` Socks4 proxy protocol does not support authentication ` ) ;
if ( url . protocol === 'socks5:' && ( proxy . username || proxy . password ) )
throw new Error ( ` Browser does not support socks5 proxy authentication ` ) ;
2020-08-28 14:17:16 -07:00
server = url . protocol + '//' + url . host ;
2020-06-05 13:50:15 -07:00
if ( bypass )
bypass = bypass . split ( ',' ) . map ( t = > t . trim ( ) ) . join ( ',' ) ;
return { . . . proxy , server , bypass } ;
}