<commit_before>// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

import { HeaderNames } from "./HeaderNames";
import { HttpClient } from "./HttpClient";
import { MessageHeaders } from "./IHubProtocol";
import { ILogger, LogLevel } from "./ILogger";
import { ITransport, TransferFormat } from "./ITransport";
import { WebSocketConstructor } from "./Polyfills";
import { Arg, getDataDetail, getUserAgentHeader, Platform } from "./Utils";

/** @private */
export class WebSocketTransport implements ITransport {
    private readonly _logger: ILogger;
    private readonly _accessTokenFactory: (() => string | Promise<string>) | undefined;
    private readonly _logMessageContent: boolean;
    private readonly _webSocketConstructor: WebSocketConstructor;
    private readonly _httpClient: HttpClient;
    private _webSocket?: WebSocket;
    private _headers: MessageHeaders;

    public onreceive: ((data: string | ArrayBuffer) => void) | null;
    public onclose: ((error?: Error) => void) | null;

    constructor(httpClient: HttpClient, accessTokenFactory: (() => string | Promise<string>) | undefined, logger: ILogger,
                logMessageContent: boolean, webSocketConstructor: WebSocketConstructor, headers: MessageHeaders) {
        this._logger = logger;
        this._accessTokenFactory = accessTokenFactory;
        this._logMessageContent = logMessageContent;
        this._webSocketConstructor = webSocketConstructor;
        this._httpClient = httpClient;

        this.onreceive = null;
        this.onclose = null;
        this._headers = headers;
    }

    public async connect(url: string, transferFormat: TransferFormat): Promise<void> {
        Arg.isRequired(url, "url");
        Arg.isRequired(transferFormat, "transferFormat");
        Arg.isIn(transferFormat, TransferFormat, "transferFormat");
        this._logger.log(LogLevel.Trace, "(WebSockets transport) Connecting.");

        let token: string;
        if (this._accessTokenFactory) {
            token = await this._accessTokenFactory();
        }

        return new Promise<void>((resolve, reject) => {
            url = url.replace(/^http/, "ws");
            let webSocket: WebSocket | undefined;
            const cookies = this._httpClient.getCookieString(url);
            let opened = false;

            if (Platform.isNode) {
                const headers: {[k: string]: string} = {};
                const [name, value] = getUserAgentHeader();
                headers[name] = value;
                if (token) {
                    headers[HeaderNames.Authorization] = `Bearer ${token}`;
                }

                if (cookies) {
                    headers[HeaderNames.Cookie] = cookies;
                }

                // Only pass headers when in non-browser environments
                webSocket = new this._webSocketConstructor(url, undefined, {
                    headers: { ...headers, ...this._headers },
                });
            }
            else
            {<commit_msg>Send token in header on React Native (#40347)<commit_after>// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

import { HeaderNames } from "./HeaderNames";
import { HttpClient } from "./HttpClient";
import { MessageHeaders } from "./IHubProtocol";
import { ILogger, LogLevel } from "./ILogger";
import { ITransport, TransferFormat } from "./ITransport";
import { WebSocketConstructor } from "./Polyfills";
import { Arg, getDataDetail, getUserAgentHeader, Platform } from "./Utils";

/** @private */
export class WebSocketTransport implements ITransport {
    private readonly _logger: ILogger;
    private readonly _accessTokenFactory: (() => string | Promise<string>) | undefined;
    private readonly _logMessageContent: boolean;
    private readonly _webSocketConstructor: WebSocketConstructor;
    private readonly _httpClient: HttpClient;
    private _webSocket?: WebSocket;
    private _headers: MessageHeaders;

    public onreceive: ((data: string | ArrayBuffer) => void) | null;
    public onclose: ((error?: Error) => void) | null;

    constructor(httpClient: HttpClient, accessTokenFactory: (() => string | Promise<string>) | undefined, logger: ILogger,
                logMessageContent: boolean, webSocketConstructor: WebSocketConstructor, headers: MessageHeaders) {
        this._logger = logger;
        this._accessTokenFactory = accessTokenFactory;
        this._logMessageContent = logMessageContent;
        this._webSocketConstructor = webSocketConstructor;
        this._httpClient = httpClient;

        this.onreceive = null;
        this.onclose = null;
        this._headers = headers;
    }

    public async connect(url: string, transferFormat: TransferFormat): Promise<void> {
        Arg.isRequired(url, "url");
        Arg.isRequired(transferFormat, "transferFormat");
        Arg.isIn(transferFormat, TransferFormat, "transferFormat");
        this._logger.log(LogLevel.Trace, "(WebSockets transport) Connecting.");

        let token: string;
        if (this._accessTokenFactory) {
            token = await this._accessTokenFactory();
        }

        return new Promise<void>((resolve, reject) => {
            url = url.replace(/^http/, "ws");
            let webSocket: WebSocket | undefined;
            const cookies = this._httpClient.getCookieString(url);
            let opened = false;

            if (Platform.isNode || Platform.isReactNative) {
                const headers: {[k: string]: string} = {};
                const [name, value] = getUserAgentHeader();
                headers[name] = value;
                if (token) {
                    headers[HeaderNames.Authorization] = `Bearer ${token}`;
                }

                if (cookies) {
                    headers[HeaderNames.Cookie] = cookies;
                }

                // Only pass headers when in non-browser environments
                webSocket = new this._webSocketConstructor(url, undefined, {
                    headers: { ...headers, ...this._headers },
                });
            }
            else
            {
{'fraction_non_alphanumeric': 0.10654685494223363, 'fraction_numerical': 0.0008023106546854943, 'mean_word_length': 2.547524188958452, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3105330', 'n_tokens_mistral': 1559, 'n_tokens_neox': 1508, 'n_words': 434}