/**
 * 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.
 */
import net from 'net';
import { resolveGlobToRegexPattern } from '@isomorphic/urlMatch';
import { fetchData } from '../utils';
import { getUserAgent } from '../userAgent';
import { Dispatcher } from './dispatcher';
import { SdkObject } from '../instrumentation';
import * as localUtils from '../localUtils';
import { deviceDescriptors as descriptors } from '../deviceDescriptors';
import { JsonPipeDispatcher } from '../dispatchers/jsonPipeDispatcher';
import { PipeTransport } from '../pipeTransport';
import { SocksInterceptor } from '../socksInterceptor';
import { WebSocketTransport } from '../transport';
export class LocalUtilsDispatcher extends Dispatcher {
    _type_LocalUtils;
    _harBackends = new Map();
    _stackSessions = new Map();
    constructor(scope, playwright) {
        const localUtils = new SdkObject(playwright, 'localUtils', 'localUtils');
        localUtils.logName = 'browser';
        const deviceDescriptors = Object.entries(descriptors)
            .map(([name, descriptor]) => ({ name, descriptor }));
        super(scope, localUtils, 'LocalUtils', {
            deviceDescriptors,
        });
        this._type_LocalUtils = true;
    }
    async zip(params, progress) {
        return await localUtils.zip(progress, this._stackSessions, params);
    }
    async harOpen(params, progress) {
        return await localUtils.harOpen(progress, this._harBackends, params);
    }
    async harLookup(params, progress) {
        return await localUtils.harLookup(progress, this._harBackends, params);
    }
    async harClose(params, progress) {
        localUtils.harClose(this._harBackends, params);
    }
    async harUnzip(params, progress) {
        return await localUtils.harUnzip(progress, params);
    }
    async tracingStarted(params, progress) {
        return await localUtils.tracingStarted(progress, this._stackSessions, params);
    }
    async traceDiscarded(params, progress) {
        return await localUtils.traceDiscarded(progress, this._stackSessions, params);
    }
    async addStackToTracingNoReply(params, progress) {
        localUtils.addStackToTracingNoReply(this._stackSessions, params);
    }
    async connect(params, progress) {
        if (URL.canParse(params.endpoint))
            return await this._connectOverWebSocket(progress, params);
        return await progress.race(this._connectOverPipe(params));
    }
    async _connectOverWebSocket(progress, params) {
        const wsHeaders = {
            'User-Agent': getUserAgent(),
            'x-playwright-proxy': params.exposeNetwork ?? '',
            ...params.headers,
        };
        const wsEndpoint = await urlToWSEndpoint(progress, params.endpoint);
        const transport = await WebSocketTransport.connect(progress, wsEndpoint, { headers: wsHeaders, followRedirects: true, debugLogHeader: 'x-playwright-debug-log' });
        const socksInterceptor = new SocksInterceptor(transport, params.exposeNetwork, params.socksProxyRedirectPortForTest);
        const pipe = new JsonPipeDispatcher(this);
        transport.onmessage = json => {
            if (socksInterceptor.interceptMessage(json))
                return;
            const cb = () => {
                try {
                    pipe.dispatch(json);
                }
                catch (e) {
                    transport.close();
                }
            };
            if (params.slowMo)
                setTimeout(cb, params.slowMo);
            else
                cb();
        };
        pipe.on('message', message => {
            transport.send(message);
        });
        transport.onclose = (reason) => {
            socksInterceptor?.cleanup();
            pipe.wasClosed(reason);
        };
        pipe.on('close', () => transport.close());
        return { pipe, headers: transport.headers };
    }
    async _connectOverPipe(params) {
        const socket = await new Promise((resolve, reject) => {
            const conn = net.connect(params.endpoint, () => resolve(conn));
            conn.on('error', reject);
        });
        const transport = new PipeTransport(socket, socket);
        const pipe = new JsonPipeDispatcher(this);
        transport.onmessage = json => {
            const cb = () => {
                try {
                    pipe.dispatch(json);
                }
                catch (e) {
                    transport.close();
                }
            };
            if (params.slowMo)
                setTimeout(cb, params.slowMo);
            else
                cb();
        };
        pipe.on('message', message => {
            transport.send(message);
        });
        transport.onclose = (reason) => {
            pipe.wasClosed(reason);
        };
        pipe.on('close', () => socket.end());
        return { pipe, headers: [] };
    }
    async globToRegex(params, progress) {
        const regex = resolveGlobToRegexPattern(params.baseURL, params.glob, params.webSocketUrl);
        return { regex };
    }
}
async function urlToWSEndpoint(progress, endpointURL) {
    if (endpointURL.startsWith('ws'))
        return endpointURL;
    progress.log(`<ws preparing> retrieving websocket url from ${endpointURL}`);
    const fetchUrl = new URL(endpointURL);
    if (!fetchUrl.pathname.endsWith('/'))
        fetchUrl.pathname += '/';
    fetchUrl.pathname += 'json';
    const json = await fetchData(progress, {
        url: fetchUrl.toString(),
        method: 'GET',
        headers: { 'User-Agent': getUserAgent() },
    }, async (params, response) => {
        return new Error(`Unexpected status ${response.statusCode} when connecting to ${fetchUrl.toString()}.\n` +
            `This does not look like a Playwright server, try connecting via ws://.`);
    });
    const wsUrl = new URL(endpointURL);
    let wsEndpointPath = JSON.parse(json).wsEndpointPath;
    if (wsEndpointPath.startsWith('/'))
        wsEndpointPath = wsEndpointPath.substring(1);
    if (!wsUrl.pathname.endsWith('/'))
        wsUrl.pathname += '/';
    wsUrl.pathname += wsEndpointPath;
    wsUrl.protocol = wsUrl.protocol === 'https:' ? 'wss:' : 'ws:';
    return wsUrl.toString();
}
//# sourceMappingURL=localUtilsDispatcher.js.map