"use strict";
/**
 * 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.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateHeaders = exports.Response = exports.Route = exports.Request = void 0;
const url_1 = require("url");
const channelOwner_1 = require("./channelOwner");
const frame_1 = require("./frame");
const fs = require("fs");
const mime = require("mime");
const util = require("util");
const utils_1 = require("../utils/utils");
class Request extends channelOwner_1.ChannelOwner {
    constructor(parent, type, guid, initializer) {
        super(parent, type, guid, initializer);
        this._redirectedFrom = null;
        this._redirectedTo = null;
        this._failureText = null;
        this._redirectedFrom = Request.fromNullable(initializer.redirectedFrom);
        if (this._redirectedFrom)
            this._redirectedFrom._redirectedTo = this;
        this._headers = utils_1.headersArrayToObject(initializer.headers, true /* lowerCase */);
        this._postData = initializer.postData ? Buffer.from(initializer.postData, 'base64') : null;
        this._timing = {
            startTime: 0,
            domainLookupStart: -1,
            domainLookupEnd: -1,
            connectStart: -1,
            secureConnectionStart: -1,
            connectEnd: -1,
            requestStart: -1,
            responseStart: -1,
            responseEnd: -1,
        };
    }
    static from(request) {
        return request._object;
    }
    static fromNullable(request) {
        return request ? Request.from(request) : null;
    }
    url() {
        return this._initializer.url;
    }
    resourceType() {
        return this._initializer.resourceType;
    }
    method() {
        return this._initializer.method;
    }
    postData() {
        return this._postData ? this._postData.toString('utf8') : null;
    }
    postDataBuffer() {
        return this._postData;
    }
    postDataJSON() {
        const postData = this.postData();
        if (!postData)
            return null;
        const contentType = this.headers()['content-type'];
        if (!contentType)
            return null;
        if (contentType === 'application/x-www-form-urlencoded') {
            const entries = {};
            const parsed = new url_1.URLSearchParams(postData);
            for (const [k, v] of parsed.entries())
                entries[k] = v;
            return entries;
        }
        return JSON.parse(postData);
    }
    headers() {
        return { ...this._headers };
    }
    async response() {
        return Response.fromNullable((await this._channel.response()).response);
    }
    frame() {
        return frame_1.Frame.from(this._initializer.frame);
    }
    isNavigationRequest() {
        return this._initializer.isNavigationRequest;
    }
    redirectedFrom() {
        return this._redirectedFrom;
    }
    redirectedTo() {
        return this._redirectedTo;
    }
    failure() {
        if (this._failureText === null)
            return null;
        return {
            errorText: this._failureText
        };
    }
    timing() {
        return this._timing;
    }
    _finalRequest() {
        return this._redirectedTo ? this._redirectedTo._finalRequest() : this;
    }
}
exports.Request = Request;
class Route extends channelOwner_1.ChannelOwner {
    static from(route) {
        return route._object;
    }
    constructor(parent, type, guid, initializer) {
        super(parent, type, guid, initializer);
    }
    request() {
        return Request.from(this._initializer.request);
    }
    async abort(errorCode) {
        await this._channel.abort({ errorCode });
    }
    async fulfill(response) {
        let body = '';
        let isBase64 = false;
        let length = 0;
        if (response.path) {
            const buffer = await util.promisify(fs.readFile)(response.path);
            body = buffer.toString('base64');
            isBase64 = true;
            length = buffer.length;
        }
        else if (utils_1.isString(response.body)) {
            body = response.body;
            isBase64 = false;
            length = Buffer.byteLength(body);
        }
        else if (response.body) {
            body = response.body.toString('base64');
            isBase64 = true;
            length = response.body.length;
        }
        const headers = {};
        for (const header of Object.keys(response.headers || {}))
            headers[header.toLowerCase()] = String(response.headers[header]);
        if (response.contentType)
            headers['content-type'] = String(response.contentType);
        else if (response.path)
            headers['content-type'] = mime.getType(response.path) || 'application/octet-stream';
        if (length && !('content-length' in headers))
            headers['content-length'] = String(length);
        await this._channel.fulfill({
            status: response.status || 200,
            headers: utils_1.headersObjectToArray(headers),
            body,
            isBase64
        });
    }
    async continue(overrides = {}) {
        const postDataBuffer = utils_1.isString(overrides.postData) ? Buffer.from(overrides.postData, 'utf8') : overrides.postData;
        await this._channel.continue({
            method: overrides.method,
            headers: overrides.headers ? utils_1.headersObjectToArray(overrides.headers) : undefined,
            postData: postDataBuffer ? postDataBuffer.toString('base64') : undefined,
        });
    }
}
exports.Route = Route;
class Response extends channelOwner_1.ChannelOwner {
    constructor(parent, type, guid, initializer) {
        super(parent, type, guid, initializer);
        this._headers = utils_1.headersArrayToObject(initializer.headers, true /* lowerCase */);
        this._request = Request.from(this._initializer.request);
        this._request._headers = utils_1.headersArrayToObject(initializer.requestHeaders, true /* lowerCase */);
        Object.assign(this._request._timing, this._initializer.timing);
    }
    static from(response) {
        return response._object;
    }
    static fromNullable(response) {
        return response ? Response.from(response) : null;
    }
    url() {
        return this._initializer.url;
    }
    ok() {
        return this._initializer.status === 0 || (this._initializer.status >= 200 && this._initializer.status <= 299);
    }
    status() {
        return this._initializer.status;
    }
    statusText() {
        return this._initializer.statusText;
    }
    headers() {
        return { ...this._headers };
    }
    async finished() {
        const result = await this._channel.finished();
        if (result.error)
            return new Error(result.error);
        return null;
    }
    async body() {
        return Buffer.from((await this._channel.body()).binary, 'base64');
    }
    async text() {
        const content = await this.body();
        return content.toString('utf8');
    }
    async json() {
        const content = await this.text();
        return JSON.parse(content);
    }
    request() {
        return this._request;
    }
    frame() {
        return this._request.frame();
    }
}
exports.Response = Response;
function validateHeaders(headers) {
    for (const key of Object.keys(headers)) {
        const value = headers[key];
        if (!Object.is(value, undefined) && !utils_1.isString(value))
            throw new Error(`Expected value of header "${key}" to be String, but "${typeof value}" is found.`);
    }
}
exports.validateHeaders = validateHeaders;
//# sourceMappingURL=network.js.map