From bd51e23184cb463b14b0bc5eb2024e1a6d10aedc Mon Sep 17 00:00:00 2001 From: Lin Wang Date: Sat, 25 Mar 2023 02:15:21 +0800 Subject: [PATCH] [Refactor] Replace url parse format resolve with whatwg url (#2910) Replace the url.parse/url.format/url.resolve function in the code base with whatwg-url. The main change is to change the url.parse to new URL(), and change url.format to [URL object].toString and change the url.resolve to new URL() toString(). Another major change is to replace the url parts in test config with URL object for transmission. Partially completes https://github.com/opensearch-project/OpenSearch-Dashboards/issues/1561 Signed-off-by: Lin Wang Co-authored-by: Josh Romero --- CHANGELOG.md | 1 + .../public/url_generator.ts | 14 ++----- .../src/osd_client/osd_client_requester.ts | 7 ++-- packages/osd-opensearch-archiver/src/cli.ts | 5 +-- .../src/failed_tests_reporter/github_api.ts | 10 ++--- .../lib/config/config.ts | 8 ++++ .../lib/config/schema.ts | 2 + .../docker_servers/docker_servers_service.ts | 7 +--- .../legacy_opensearch_test_cluster.js | 7 ++-- .../opensearch_test_config.js | 26 ++++++++----- packages/osd-test/src/osd/osd_test_config.ts | 34 +++++++++++----- src/cli/serve/serve.js | 3 +- src/cli_plugin/install/download.js | 11 ++++-- .../public/application/utils/parse_app_url.ts | 4 +- src/core/public/chrome/chrome_service.tsx | 3 +- .../public/chrome/ui/header/header_logo.tsx | 7 ++-- .../public/chrome/ui/header/home_loader.tsx | 7 ++-- .../server/http/base_path_proxy_server.ts | 23 +++++------ src/core/server/http/http_server.mocks.ts | 6 +-- src/core/server/http/http_server.ts | 3 +- src/core/server/http/https_redirect_server.ts | 15 ++----- .../legacy/opensearch_client_config.ts | 6 +-- .../server/lib/opensearch_proxy_config.ts | 3 +- .../console/server/lib/proxy_config.ts | 16 ++++---- .../public/map/service_settings.test.js | 18 ++++----- .../components/url_panel_content.test.tsx | 4 +- .../public/components/url_panel_content.tsx | 31 +++++---------- src/plugins/share/public/lib/url_shortener.ts | 16 +++----- src/test_utils/get_url.js | 28 +++++++------ src/test_utils/get_url.test.ts | 39 +++++-------------- test/api_integration/services/supertest.ts | 5 +-- test/common/config.js | 3 +- test/common/services/deployment.ts | 4 +- test/common/services/legacy_opensearch.ts | 4 +- test/common/services/opensearch.ts | 5 +-- test/common/services/opensearch_archiver.ts | 3 +- .../opensearch_dashboards_server.ts | 3 +- test/common/services/security/test_user.ts | 14 +++---- .../functional/apps/discover/_shared_links.js | 6 +-- test/functional/page_objects/common_page.ts | 6 +-- test/functional/page_objects/context_page.ts | 2 +- test/functional/services/supertest.ts | 3 +- .../application_links/redirect_app_links.ts | 5 +-- .../core_plugins/application_leave_confirm.ts | 22 ++++++----- .../core_plugins/application_status.ts | 23 ++++++----- .../test_suites/core_plugins/applications.ts | 21 +++++----- .../http/platform/headers.ts | 7 +--- .../http/ssl_redirect/config.js | 11 ++---- test/server_integration/services/supertest.js | 18 +++------ 49 files changed, 240 insertions(+), 289 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2651793f431..6fcebf4fc5ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,6 +96,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Temporary workaround for task-kill exceptions on Windows when it is passed a pid for a process that is already dead ([#2842](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2842)) - [Vis Builder] Fix empty workspace animation does not work in firefox ([#2853](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2853)) - Bumped `del` version to fix MacOS race condition ([#2847](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2873)) +- [Chore] Update deprecated url methods (url.parse(), url.format()) ([#1561](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/1561)) - [Build] Fixed "Last Access Time" not being set by `scanCopy` on Windows ([#2964](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2964)) - [Vis Builder] Add global data persistence for vis builder #2896 ([#2896](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2896)) - Update `leaflet-vega` and fix its usage ([#3005](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3005)) diff --git a/examples/url_generators_examples/public/url_generator.ts b/examples/url_generators_examples/public/url_generator.ts index dba057eb6de4..512dc191782a 100644 --- a/examples/url_generators_examples/public/url_generator.ts +++ b/examples/url_generators_examples/public/url_generator.ts @@ -28,7 +28,6 @@ * under the License. */ -import url from 'url'; import { UrlGeneratorState, UrlGeneratorsDefinition } from '../../../src/plugins/share/public'; /** @@ -53,16 +52,11 @@ export const createHelloPageLinkGenerator = ( createUrl: async (state) => { const startServices = await getStartServices(); const appBasePath = startServices.appBasePath; - const parsedUrl = url.parse(window.location.href); - return url.format({ - protocol: parsedUrl.protocol, - host: parsedUrl.host, - pathname: `${appBasePath}/hello`, - query: { - ...state, - }, - }); + const url = new URL(`${appBasePath}/hello`, window.location.origin); + url.search = new URLSearchParams({ ...state }).toString(); + + return url.toString(); }, }); diff --git a/packages/osd-dev-utils/src/osd_client/osd_client_requester.ts b/packages/osd-dev-utils/src/osd_client/osd_client_requester.ts index effa5da6dd7e..807ec64b0e5a 100644 --- a/packages/osd-dev-utils/src/osd_client/osd_client_requester.ts +++ b/packages/osd-dev-utils/src/osd_client/osd_client_requester.ts @@ -28,7 +28,6 @@ * under the License. */ -import Url from 'url'; import Https from 'https'; import Axios, { AxiosResponse } from 'axios'; @@ -93,7 +92,7 @@ export class OsdClientRequester { constructor(private readonly log: ToolingLog, options: Options) { this.url = options.url; this.httpsAgent = - Url.parse(options.url).protocol === 'https:' + new URL(options.url).protocol === 'https:' ? new Https.Agent({ ca: options.certificateAuthorities, }) @@ -105,11 +104,11 @@ export class OsdClientRequester { } public resolveUrl(relativeUrl: string = '/') { - return Url.resolve(this.pickUrl(), relativeUrl); + return new URL(relativeUrl, this.pickUrl()).toString(); } async request(options: ReqOptions): Promise> { - const url = Url.resolve(this.pickUrl(), options.path); + const url = new URL(options.path, this.pickUrl()).toString(); const description = options.description || `${options.method} ${url}`; let attempt = 0; const maxAttempts = options.retries ?? DEFAULT_MAX_ATTEMPTS; diff --git a/packages/osd-opensearch-archiver/src/cli.ts b/packages/osd-opensearch-archiver/src/cli.ts index 39dbbb264cd7..002e3e287587 100644 --- a/packages/osd-opensearch-archiver/src/cli.ts +++ b/packages/osd-opensearch-archiver/src/cli.ts @@ -35,7 +35,6 @@ *************************************************************/ import Path from 'path'; -import Url from 'url'; import readline from 'readline'; import { RunWithCommands, createFlagError } from '@osd/dev-utils'; @@ -72,7 +71,7 @@ export function runCli() { throw createFlagError('--opensearch-url must be a string'); } if (!opensearchUrl && config) { - opensearchUrl = Url.format(config.get('servers.opensearch')); + opensearchUrl = config.get('servers.opensearch.serverUrl'); } if (!opensearchUrl) { throw createFlagError('--opensearch-url or --config must be defined'); @@ -83,7 +82,7 @@ export function runCli() { throw createFlagError('--opensearch-dashboards-url must be a string'); } if (!opensearchDashboardsUrl && config) { - opensearchDashboardsUrl = Url.format(config.get('servers.opensearchDashboards')); + opensearchDashboardsUrl = config.get('servers.opensearchDashboards.serverUrl') as string; } if (!opensearchDashboardsUrl) { throw createFlagError('---url or --config must be defined'); diff --git a/packages/osd-test/src/failed_tests_reporter/github_api.ts b/packages/osd-test/src/failed_tests_reporter/github_api.ts index c384d56eb104..c9df43472585 100644 --- a/packages/osd-test/src/failed_tests_reporter/github_api.ts +++ b/packages/osd-test/src/failed_tests_reporter/github_api.ts @@ -28,8 +28,6 @@ * under the License. */ -import Url from 'url'; - import Axios, { AxiosRequestConfig, AxiosInstance } from 'axios'; import parseLinkHeader from 'parse-link-header'; import { ToolingLog, isAxiosResponseError, isAxiosRequestError } from '@osd/dev-utils'; @@ -98,7 +96,7 @@ export class GithubApi { nextRequest: { safeForDryRun: true, method: 'GET', - url: Url.resolve(BASE_URL, 'issues'), + url: new URL('issues', BASE_URL).toString(), params: { state: 'all', per_page: '100', @@ -158,7 +156,7 @@ export class GithubApi { await this.request( { method: 'PATCH', - url: Url.resolve(BASE_URL, `issues/${encodeURIComponent(issueNumber)}`), + url: new URL(`issues/${encodeURIComponent(issueNumber)}`, BASE_URL).toString(), data: { state: 'open', // Reopen issue if it was closed. body: newBody, @@ -172,7 +170,7 @@ export class GithubApi { await this.request( { method: 'POST', - url: Url.resolve(BASE_URL, `issues/${encodeURIComponent(issueNumber)}/comments`), + url: new URL(`issues/${encodeURIComponent(issueNumber)}/comments`, BASE_URL).toString(), data: { body: commentBody, }, @@ -185,7 +183,7 @@ export class GithubApi { const resp = await this.request( { method: 'POST', - url: Url.resolve(BASE_URL, 'issues'), + url: new URL('issues', BASE_URL).toString(), data: { title, body, diff --git a/packages/osd-test/src/functional_test_runner/lib/config/config.ts b/packages/osd-test/src/functional_test_runner/lib/config/config.ts index 3fd162bef29b..6aba4b29f7b4 100644 --- a/packages/osd-test/src/functional_test_runner/lib/config/config.ts +++ b/packages/osd-test/src/functional_test_runner/lib/config/config.ts @@ -126,6 +126,10 @@ export class Config { if (typeof v === 'function') { return v; } + + if (v instanceof URL) { + return new URL(v.toString()); + } }); } @@ -134,6 +138,10 @@ export class Config { if (typeof v === 'function') { return v; } + + if (v instanceof URL) { + return new URL(v.toString()); + } }); } } diff --git a/packages/osd-test/src/functional_test_runner/lib/config/schema.ts b/packages/osd-test/src/functional_test_runner/lib/config/schema.ts index 447db1e97874..720bd0940f87 100644 --- a/packages/osd-test/src/functional_test_runner/lib/config/schema.ts +++ b/packages/osd-test/src/functional_test_runner/lib/config/schema.ts @@ -50,6 +50,8 @@ const urlPartsSchema = () => pathname: Joi.string().regex(/^\//, 'start with a /'), hash: Joi.string().regex(/^\//, 'start with a /'), certificateAuthorities: Joi.array().items(Joi.binary()).optional(), + fullURL: Joi.object().type(URL), + serverUrl: Joi.string(), }) .default(); diff --git a/packages/osd-test/src/functional_test_runner/lib/docker_servers/docker_servers_service.ts b/packages/osd-test/src/functional_test_runner/lib/docker_servers/docker_servers_service.ts index 954740ca24ff..c34b5c659dd5 100644 --- a/packages/osd-test/src/functional_test_runner/lib/docker_servers/docker_servers_service.ts +++ b/packages/osd-test/src/functional_test_runner/lib/docker_servers/docker_servers_service.ts @@ -28,7 +28,6 @@ * under the License. */ -import Url from 'url'; import execa from 'execa'; import * as Rx from 'rxjs'; import { filter, take, map } from 'rxjs/operators'; @@ -54,11 +53,7 @@ export class DockerServersService { this.servers = Object.entries(configs).map(([name, config]) => ({ ...config, name, - url: Url.format({ - protocol: 'http:', - hostname: 'localhost', - port: config.port, - }), + url: new URL(`http://localhost:${config.port}`).toString(), })); this.lifecycle.beforeTests.add(async () => { diff --git a/packages/osd-test/src/legacy_opensearch/legacy_opensearch_test_cluster.js b/packages/osd-test/src/legacy_opensearch/legacy_opensearch_test_cluster.js index 72506099dfe4..07fbd4cdb1de 100644 --- a/packages/osd-test/src/legacy_opensearch/legacy_opensearch_test_cluster.js +++ b/packages/osd-test/src/legacy_opensearch/legacy_opensearch_test_cluster.js @@ -29,7 +29,6 @@ */ import { resolve } from 'path'; -import { format } from 'url'; import { get, toPath } from 'lodash'; import { Cluster } from '@osd/opensearch'; import { CI_PARALLEL_PROCESS_PREFIX } from '../ci_parallel_process_prefix'; @@ -135,10 +134,10 @@ export function createLegacyOpenSearchTestCluster(options = {}) { } getUrl() { - const parts = opensearchTestConfig.getUrlParts(); - parts.port = port; + const url = new URL(opensearchTestConfig.getUrlParts().fullURL); + url.port = port; - return format(parts); + return url.toString().slice(0, -1); } })(); } diff --git a/packages/osd-test/src/legacy_opensearch/opensearch_test_config.js b/packages/osd-test/src/legacy_opensearch/opensearch_test_config.js index 7befa4226afd..22bb858411fd 100644 --- a/packages/osd-test/src/legacy_opensearch/opensearch_test_config.js +++ b/packages/osd-test/src/legacy_opensearch/opensearch_test_config.js @@ -28,7 +28,6 @@ * under the License. */ -import url, { format as formatUrl } from 'url'; import pkg from '../../../../package.json'; import { adminTestUser } from '../osd'; @@ -42,7 +41,7 @@ export const opensearchTestConfig = new (class OpenSearchTestConfig { } getUrl() { - return formatUrl(this.getUrlParts()); + return this.getUrlParts().serverUrl; } getBuildFrom() { @@ -56,30 +55,39 @@ export const opensearchTestConfig = new (class OpenSearchTestConfig { getUrlParts() { // Allow setting one complete TEST_OPENSEARCH_URL for opensearch like https://opensearch:changeme@example.com:9200 if (process.env.TEST_OPENSEARCH_URL) { - const testOpenSearchUrl = url.parse(process.env.TEST_OPENSEARCH_URL); + const testOpenSearchUrl = new URL('', process.env.TEST_OPENSEARCH_URL); + testOpenSearchUrl.pathname = ''; return { // have to remove the ":" off protocol protocol: testOpenSearchUrl.protocol.slice(0, -1), hostname: testOpenSearchUrl.hostname, port: parseInt(testOpenSearchUrl.port, 10), - username: testOpenSearchUrl.auth.split(':')[0], - password: testOpenSearchUrl.auth.split(':')[1], - auth: testOpenSearchUrl.auth, + username: testOpenSearchUrl.username, + password: testOpenSearchUrl.password, + auth: `${testOpenSearchUrl.username}:${testOpenSearchUrl.password}`, + fullURL: testOpenSearchUrl, + serverUrl: testOpenSearchUrl.toString().slice(0, -1), }; } const username = process.env.TEST_OPENSEARCH_USERNAME || adminTestUser.username; const password = process.env.TEST_OPENSEARCH_PASSWORD || adminTestUser.password; + const protocol = process.env.TEST_OPENSEARCH_PROTOCOL || 'http'; + const hostname = process.env.TEST_OPENSEARCH_HOSTNAME || 'localhost'; + const port = parseInt(process.env.TEST_OPENSEARCH_PORT, 10) || 9220; + const fullURL = new URL('', `${protocol}://${username}:${password}@${hostname}:${port}`); return { // Allow setting any individual component(s) of the URL, // or use default values (username and password from ../osd/users.js) - protocol: process.env.TEST_OPENSEARCH_PROTOCOL || 'http', - hostname: process.env.TEST_OPENSEARCH_HOSTNAME || 'localhost', - port: parseInt(process.env.TEST_OPENSEARCH_PORT, 10) || 9220, + protocol, + hostname, + port, auth: `${username}:${password}`, username: username, password: password, + fullURL, + serverUrl: fullURL.toString().slice(0, -1), }; } })(); diff --git a/packages/osd-test/src/osd/osd_test_config.ts b/packages/osd-test/src/osd/osd_test_config.ts index 6c3ef573511b..b5fc179b7a97 100644 --- a/packages/osd-test/src/osd/osd_test_config.ts +++ b/packages/osd-test/src/osd/osd_test_config.ts @@ -28,7 +28,6 @@ * under the License. */ -import url from 'url'; import { opensearchDashboardsTestUser } from './users'; interface UrlParts { @@ -38,6 +37,8 @@ interface UrlParts { auth?: string; username?: string; password?: string; + fullURL: URL; + serverUrl: string; } export const osdTestConfig = new (class OsdTestConfig { @@ -48,16 +49,22 @@ export const osdTestConfig = new (class OsdTestConfig { getUrlParts(): UrlParts { // allow setting one complete TEST_OPENSEARCH_DASHBOARDS_URL for opensearch like https://opensearch:changeme@example.com:9200 if (process.env.TEST_OPENSEARCH_DASHBOARDS_URL) { - const testOpenSearchDashboardsUrl = url.parse(process.env.TEST_OPENSEARCH_DASHBOARDS_URL); + const testOpenSearchDashboardsUrl = new URL('', process.env.TEST_OPENSEARCH_DASHBOARDS_URL); + testOpenSearchDashboardsUrl.pathname = ''; return { protocol: testOpenSearchDashboardsUrl.protocol?.slice(0, -1), hostname: testOpenSearchDashboardsUrl.hostname ?? undefined, port: testOpenSearchDashboardsUrl.port ? parseInt(testOpenSearchDashboardsUrl.port, 10) : undefined, - auth: testOpenSearchDashboardsUrl.auth ?? undefined, - username: testOpenSearchDashboardsUrl.auth?.split(':')[0], - password: testOpenSearchDashboardsUrl.auth?.split(':')[1], + auth: + testOpenSearchDashboardsUrl.username && testOpenSearchDashboardsUrl.password + ? `${testOpenSearchDashboardsUrl.username}:${testOpenSearchDashboardsUrl.password}` + : undefined, + username: testOpenSearchDashboardsUrl.username ?? undefined, + password: testOpenSearchDashboardsUrl.password ?? undefined, + fullURL: testOpenSearchDashboardsUrl, + serverUrl: testOpenSearchDashboardsUrl.toString().slice(0, -1), }; } @@ -65,15 +72,22 @@ export const osdTestConfig = new (class OsdTestConfig { process.env.TEST_OPENSEARCH_DASHBOARDS_USERNAME || opensearchDashboardsTestUser.username; const password = process.env.TEST_OPENSEARCH_DASHBOARDS_PASSWORD || opensearchDashboardsTestUser.password; + const protocol = process.env.TEST_OPENSEARCH_DASHBOARDS_PROTOCOL || 'http'; + const hostname = process.env.TEST_OPENSEARCH_DASHBOARDS_HOSTNAME || 'localhost'; + const port = process.env.TEST_OPENSEARCH_DASHBOARDS_PORT + ? parseInt(process.env.TEST_OPENSEARCH_DASHBOARDS_PORT, 10) + : 5620; + const fullURL = new URL(`${protocol}://${username}:${password}@${hostname}:${port}`); + return { - protocol: process.env.TEST_OPENSEARCH_DASHBOARDS_PROTOCOL || 'http', - hostname: process.env.TEST_OPENSEARCH_DASHBOARDS_HOSTNAME || 'localhost', - port: process.env.TEST_OPENSEARCH_DASHBOARDS_PORT - ? parseInt(process.env.TEST_OPENSEARCH_DASHBOARDS_PORT, 10) - : 5620, + protocol, + hostname, + port, auth: `${username}:${password}`, username, password, + fullURL, + serverUrl: fullURL.toString().slice(0, -1), }; } })(); diff --git a/src/cli/serve/serve.js b/src/cli/serve/serve.js index 34c03c2547bc..6f89911ed089 100644 --- a/src/cli/serve/serve.js +++ b/src/cli/serve/serve.js @@ -32,7 +32,6 @@ import { set as lodashSet } from '@elastic/safer-lodash-set'; import _ from 'lodash'; import { statSync } from 'fs'; import { resolve } from 'path'; -import url from 'url'; import { getConfigPath } from '@osd/utils'; import { IS_OPENSEARCH_DASHBOARDS_DISTRIBUTABLE } from '../../legacy/utils'; @@ -110,7 +109,7 @@ function applyConfigOverrides(rawConfig, opts, extraCliOptions) { const opensearchHosts = ( (customOpenSearchHosts.length > 0 && customOpenSearchHosts) || ['https://localhost:9200'] ).map((hostUrl) => { - const parsedUrl = url.parse(hostUrl); + const parsedUrl = new URL('', hostUrl); if (parsedUrl.hostname !== 'localhost') { throw new Error( `Hostname "${parsedUrl.hostname}" can't be used with --ssl. Must be "localhost" to work with certificates.` diff --git a/src/cli_plugin/install/download.js b/src/cli_plugin/install/download.js index d614b7ac230b..892fc7296969 100644 --- a/src/cli_plugin/install/download.js +++ b/src/cli_plugin/install/download.js @@ -28,8 +28,6 @@ * under the License. */ -import { parse } from 'url'; - import { UnsupportedProtocolError } from '../lib/errors'; import { downloadHttpFile } from './downloaders/http'; import { downloadLocalFile } from './downloaders/file'; @@ -56,14 +54,19 @@ export function _checkFilePathDeprecation(sourceUrl, logger) { } export function _downloadSingle(settings, logger, sourceUrl) { - const urlInfo = parse(sourceUrl); + let urlInfo; + try { + urlInfo = new URL('', sourceUrl); + } catch (e) { + return Promise.reject(new UnsupportedProtocolError()); + } let downloadPromise; if (/^file/.test(urlInfo.protocol)) { _checkFilePathDeprecation(sourceUrl, logger); downloadPromise = downloadLocalFile( logger, - _getFilePath(urlInfo.path, sourceUrl), + _getFilePath(urlInfo.pathname, sourceUrl), settings.tempArchiveFile ); } else if (/^https?/.test(urlInfo.protocol)) { diff --git a/src/core/public/application/utils/parse_app_url.ts b/src/core/public/application/utils/parse_app_url.ts index 433e1a877bc4..1ad496613608 100644 --- a/src/core/public/application/utils/parse_app_url.ts +++ b/src/core/public/application/utils/parse_app_url.ts @@ -29,7 +29,6 @@ */ import { getUrlOrigin } from '@osd/std'; -import { resolve } from 'url'; import { IBasePath } from '../../http'; import { App, ParsedAppUrl } from '../types'; @@ -57,7 +56,6 @@ export const parseAppUrl = ( if (!currentOrigin) { throw new Error('when manually provided, currentUrl must be valid url with an origin'); } - const currentPath = currentUrl.substring(currentOrigin.length); // remove the origin from the given url if (url.startsWith(currentOrigin)) { @@ -66,7 +64,7 @@ export const parseAppUrl = ( // if the path is relative (i.e `../../to/somewhere`), we convert it to absolute if (!url.startsWith('/')) { - url = resolve(currentPath, url); + url = new URL(url, currentUrl).toString().substring(currentOrigin.length); } // if using a basePath and the absolute path does not starts with it, it can't be a match diff --git a/src/core/public/chrome/chrome_service.tsx b/src/core/public/chrome/chrome_service.tsx index da5cbda1c92a..a78232f4e336 100644 --- a/src/core/public/chrome/chrome_service.tsx +++ b/src/core/public/chrome/chrome_service.tsx @@ -33,7 +33,6 @@ import React from 'react'; import { FormattedMessage } from '@osd/i18n/react'; import { BehaviorSubject, combineLatest, merge, Observable, of, ReplaySubject } from 'rxjs'; import { flatMap, map, takeUntil } from 'rxjs/operators'; -import { parse } from 'url'; import { EuiLink } from '@elastic/eui'; import { mountReactNode } from '../utils/mount'; import { InternalApplicationStart } from '../application'; @@ -123,7 +122,7 @@ export class ChromeService { */ private initVisibility(application: StartDeps['application']) { // Start off the chrome service hidden if "embed" is in the hash query string. - const isEmbedded = 'embed' in parse(location.hash.slice(1), true).query; + const isEmbedded = new URL(location.hash.slice(1), location.origin).searchParams.has('embed'); this.isForceHidden$ = new BehaviorSubject(isEmbedded); const appHidden$ = merge( diff --git a/src/core/public/chrome/ui/header/header_logo.tsx b/src/core/public/chrome/ui/header/header_logo.tsx index abc32daabdf9..26b1783e132b 100644 --- a/src/core/public/chrome/ui/header/header_logo.tsx +++ b/src/core/public/chrome/ui/header/header_logo.tsx @@ -33,7 +33,6 @@ import { i18n } from '@osd/i18n'; import React from 'react'; import useObservable from 'react-use/lib/useObservable'; import { Observable } from 'rxjs'; -import Url from 'url'; import { ChromeNavLink } from '../..'; import { ChromeBranding } from '../../chrome_service'; @@ -74,11 +73,11 @@ function onClick( } if (forceNavigation) { - const toParsed = Url.parse(anchor.href); - const fromParsed = Url.parse(document.location.href); + const toParsed = new URL(anchor.href); + const fromParsed = new URL(document.location.href); const sameProto = toParsed.protocol === fromParsed.protocol; const sameHost = toParsed.host === fromParsed.host; - const samePath = toParsed.path === fromParsed.path; + const samePath = toParsed.pathname === fromParsed.pathname; if (sameProto && sameHost && samePath) { if (toParsed.hash) { diff --git a/src/core/public/chrome/ui/header/home_loader.tsx b/src/core/public/chrome/ui/header/home_loader.tsx index 4cd231bdf83e..0083df43ff8b 100644 --- a/src/core/public/chrome/ui/header/home_loader.tsx +++ b/src/core/public/chrome/ui/header/home_loader.tsx @@ -34,7 +34,6 @@ import { i18n } from '@osd/i18n'; import React from 'react'; import useObservable from 'react-use/lib/useObservable'; import { Observable } from 'rxjs'; -import Url from 'url'; import { EuiHeaderSectionItemButton } from '@elastic/eui'; import { ChromeNavLink } from '../..'; import { ChromeBranding } from '../../chrome_service'; @@ -78,11 +77,11 @@ function onClick( } if (forceNavigation) { - const toParsed = Url.parse(anchor.href); - const fromParsed = Url.parse(document.location.href); + const toParsed = new URL(anchor.href); + const fromParsed = new URL(document.location.href); const sameProto = toParsed.protocol === fromParsed.protocol; const sameHost = toParsed.host === fromParsed.host; - const samePath = toParsed.path === fromParsed.path; + const samePath = toParsed.pathname === fromParsed.pathname; if (sameProto && sameHost && samePath) { if (toParsed.hash) { diff --git a/src/core/server/http/base_path_proxy_server.ts b/src/core/server/http/base_path_proxy_server.ts index 7df5a1e6d301..f82e5a94d34a 100644 --- a/src/core/server/http/base_path_proxy_server.ts +++ b/src/core/server/http/base_path_proxy_server.ts @@ -28,7 +28,6 @@ * under the License. */ -import Url from 'url'; import { Agent as HttpsAgent, ServerOptions as TlsOptions } from 'https'; import apm from 'elastic-apm-node'; @@ -173,16 +172,18 @@ export class BasePathProxyServer { agent: this.httpsAgent, passThrough: true, xforward: true, - mapUri: async (request: Request) => ({ - uri: Url.format({ - hostname: request.server.info.host, - port: this.devConfig.basePathProxyTargetPort, - protocol: request.server.info.protocol, - pathname: `${this.httpConfig.basePath}/${request.params.osdPath}`, - query: request.query, - }), - headers: request.headers, - }), + mapUri: async (request: Request) => { + const uri = new URL( + `${this.httpConfig.basePath}/${request.params.osdPath}`, + `${request.server.info.protocol}://${request.server.info.host}:${this.devConfig.basePathProxyTargetPort}` + ); + uri.search = new URLSearchParams(request.query).toString(); + + return { + uri: uri.toString(), + headers: request.headers, + }; + }, }, }, method: '*', diff --git a/src/core/server/http/http_server.mocks.ts b/src/core/server/http/http_server.mocks.ts index 8e50e331200d..01fda75aab78 100644 --- a/src/core/server/http/http_server.mocks.ts +++ b/src/core/server/http/http_server.mocks.ts @@ -28,7 +28,6 @@ * under the License. */ -import { URL, format as formatUrl } from 'url'; import { Request } from '@hapi/hapi'; import { merge } from 'lodash'; import { Socket } from 'net'; @@ -140,10 +139,7 @@ type DeepPartialObject = { [P in keyof T]+?: DeepPartial }; function createRawRequestMock(customization: DeepPartial = {}) { const pathname = customization.url?.pathname || '/'; const path = `${pathname}${customization.url?.search || ''}`; - const url = new URL( - formatUrl(Object.assign({ pathname, path, href: path }, customization.url)), - 'http://localhost' - ); + const url = new URL(path, 'http://localhost'); return merge( {}, diff --git a/src/core/server/http/http_server.ts b/src/core/server/http/http_server.ts index dc3727662c77..c0d2caaab727 100644 --- a/src/core/server/http/http_server.ts +++ b/src/core/server/http/http_server.ts @@ -30,7 +30,6 @@ import { Server } from '@hapi/hapi'; import HapiStaticFiles from '@hapi/inert'; -import url from 'url'; import uuid from 'uuid'; import { Logger, LoggerFactory } from '../logging'; @@ -294,7 +293,7 @@ export class HttpServer { this.server.ext('onRequest', (request, h) => { const { referrer } = request.info; if (referrer !== '') { - const { hostname } = url.parse(referrer); + const { hostname } = new URL('', referrer); if (!hostname || !list.includes(hostname)) { request.info.acceptEncoding = ''; } diff --git a/src/core/server/http/https_redirect_server.ts b/src/core/server/http/https_redirect_server.ts index e0747466a389..ea7adc652583 100644 --- a/src/core/server/http/https_redirect_server.ts +++ b/src/core/server/http/https_redirect_server.ts @@ -29,7 +29,6 @@ */ import { Request, ResponseToolkit, Server } from '@hapi/hapi'; -import { format as formatUrl } from 'url'; import { Logger } from '../logging'; import { HttpConfig } from './http_config'; @@ -62,17 +61,9 @@ export class HttpsRedirectServer { ); this.server.ext('onRequest', (request: Request, responseToolkit: ResponseToolkit) => { - return responseToolkit - .redirect( - formatUrl({ - hostname: config.host, - pathname: request.url.pathname, - port: config.port, - protocol: 'https', - search: request.url.search, - }) - ) - .takeover(); + const redirectUrl = new URL(request.url.pathname, `https://${config.host}:${config.port}`); + redirectUrl.search = request.url.search; + return responseToolkit.redirect(redirectUrl.toString()).takeover(); }); try { diff --git a/src/core/server/opensearch/legacy/opensearch_client_config.ts b/src/core/server/opensearch/legacy/opensearch_client_config.ts index 3aac7fcc48e8..76d077131eab 100644 --- a/src/core/server/opensearch/legacy/opensearch_client_config.ts +++ b/src/core/server/opensearch/legacy/opensearch_client_config.ts @@ -32,7 +32,6 @@ import { ConfigOptions } from 'elasticsearch'; import { cloneDeep } from 'lodash'; import { Duration } from 'moment'; import { checkServerIdentity } from 'tls'; -import url from 'url'; import { pick } from '@osd/std'; import { Logger } from '../../logging'; import { OpenSearchConfig } from '../opensearch_config'; @@ -131,17 +130,18 @@ export function parseOpenSearchClientConfig( if (Array.isArray(config.hosts)) { const needsAuth = auth !== false && config.username && config.password; opensearchClientConfig.hosts = config.hosts.map((nodeUrl: string) => { - const uri = url.parse(nodeUrl); + const uri = new URL('', nodeUrl); const httpsURI = uri.protocol === 'https:'; const httpURI = uri.protocol === 'http:'; + const query = uri.searchParams.toString(); const host: Record = { host: uri.hostname, port: uri.port || (httpsURI && '443') || (httpURI && '80'), protocol: uri.protocol, path: uri.pathname, - query: uri.query, + query: query === '' ? null : query, headers: { ...DEFAULT_HEADERS, ...config.customHeaders, diff --git a/src/plugins/console/server/lib/opensearch_proxy_config.ts b/src/plugins/console/server/lib/opensearch_proxy_config.ts index b2b108b20e90..3824e8c43c01 100644 --- a/src/plugins/console/server/lib/opensearch_proxy_config.ts +++ b/src/plugins/console/server/lib/opensearch_proxy_config.ts @@ -31,12 +31,11 @@ import _ from 'lodash'; import http from 'http'; import https from 'https'; -import url from 'url'; import { OpenSearchConfigForProxy } from '../types'; const createAgent = (legacyConfig: OpenSearchConfigForProxy): http.Agent | https.Agent => { - const target = url.parse(_.head(legacyConfig.hosts) as any); + const target = new URL('', _.head(legacyConfig.hosts)!); if (!/^https/.test(target.protocol || '')) return new http.Agent(); const agentOptions: https.AgentOptions = {}; diff --git a/src/plugins/console/server/lib/proxy_config.ts b/src/plugins/console/server/lib/proxy_config.ts index c762462f6de2..8ccb0c6de6a5 100644 --- a/src/plugins/console/server/lib/proxy_config.ts +++ b/src/plugins/console/server/lib/proxy_config.ts @@ -29,7 +29,6 @@ */ import { values } from 'lodash'; -import { format as formatUrl } from 'url'; import { Agent as HttpsAgent, AgentOptions } from 'https'; import { WildcardMatcher } from './wildcard_matcher'; @@ -62,13 +61,14 @@ export class ProxyConfig { const rawMatches = { ...config.match, }; - this.id = - formatUrl({ - protocol: rawMatches.protocol, - hostname: rawMatches.host, - port: rawMatches.port, - pathname: rawMatches.path, - }) || '*'; + try { + this.id = new URL( + rawMatches.path, + `${rawMatches.protocol}://${rawMatches.host}:${rawMatches.port}` + ).toString(); + } catch (e) { + this.id = '*'; + } this.matchers = { protocol: new WildcardMatcher(rawMatches.protocol), diff --git a/src/plugins/maps_legacy/public/map/service_settings.test.js b/src/plugins/maps_legacy/public/map/service_settings.test.js index 89d7cfeec6e7..56671b849210 100644 --- a/src/plugins/maps_legacy/public/map/service_settings.test.js +++ b/src/plugins/maps_legacy/public/map/service_settings.test.js @@ -34,8 +34,6 @@ jest.mock('../opensearch_dashboards_services', () => ({ }, })); -import url from 'url'; - import EMS_FILES from '../__tests__/map/ems_mocks/sample_files.json'; import EMS_TILES from '../__tests__/map/ems_mocks/sample_tiles.json'; import EMS_STYLE_ROAD_MAP_BRIGHT from '../__tests__/map/ems_mocks/sample_style_bright'; @@ -121,11 +119,11 @@ describe('service_settings (FKA tile_map test)', function () { 'OpenStreetMap contributors | OpenMapTiles | MapTiler | <iframe id=\'iframe\' style=\'position:fixed;height: 40%;width: 100%;top: 60%;left: 5%;right:5%;border: 0px;background:white;\' src=\'http://256.256.256.256\'></iframe>' ); - const urlObject = url.parse(attrs.url, true); + const urlObject = new URL('', attrs.url); expect(urlObject.hostname).toEqual('tiles.foobar'); - expect(urlObject.query.my_app_name).toEqual('opensearchDashboards'); - expect(urlObject.query.opensearch_tile_service_tos).toEqual('agree'); - expect(typeof urlObject.query.my_app_version).toEqual('string'); + expect(urlObject.searchParams.get('my_app_name')).toEqual('opensearchDashboards'); + expect(urlObject.searchParams.get('opensearch_tile_service_tos')).toEqual('agree'); + expect(typeof urlObject.searchParams.get('my_app_version')).toEqual('string'); }); it('should get options', async function () { @@ -143,9 +141,9 @@ describe('service_settings (FKA tile_map test)', function () { let serviceSettings; async function assertQuery(expected) { const attrs = await serviceSettings.getAttributesForTMSLayer(tilemapServices[0]); - const urlObject = url.parse(attrs.url, true); + const urlObject = new URL('', attrs.url); Object.keys(expected).forEach((key) => { - expect(urlObject.query[key]).toEqual(expected[key]); + expect(urlObject.searchParams.get(key)).toEqual(expected[key]); }); } @@ -317,9 +315,9 @@ describe('service_settings (FKA tile_map test)', function () { const assertions = fileLayers.map(async function (fileLayer) { expect(fileLayer.origin).toEqual(ORIGIN.EMS); const fileUrl = await serviceSettings.getUrlForRegionLayer(fileLayer); - const urlObject = url.parse(fileUrl, true); + const urlObject = new URL('', fileUrl); Object.keys({ foo: 'bar', opensearch_tile_service_tos: 'agree' }).forEach((key) => { - expect(typeof urlObject.query[key]).toEqual('string'); + expect(typeof urlObject.searchParams.get(key)).toEqual('string'); }); }); diff --git a/src/plugins/share/public/components/url_panel_content.test.tsx b/src/plugins/share/public/components/url_panel_content.test.tsx index 32b59cdf2b5f..902425d82650 100644 --- a/src/plugins/share/public/components/url_panel_content.test.tsx +++ b/src/plugins/share/public/components/url_panel_content.test.tsx @@ -77,7 +77,7 @@ describe('share url panel content', () => { component.find(EuiRadioGroup).prop('onChange')!(ExportUrlAsType.EXPORT_URL_AS_SAVED_OBJECT); }); expect(component.find(EuiCopy).prop('textToCopy')).toEqual( - 'http://localhost:5601/app/myapp#/?_g=()' + 'http://localhost:5601/app/myapp#/?_g=%28%29' ); }); @@ -168,7 +168,7 @@ describe('share url panel content', () => { component.find(EuiRadioGroup).prop('onChange')!(ExportUrlAsType.EXPORT_URL_AS_SAVED_OBJECT); }); expect(component.find(EuiCopy).prop('textToCopy')).toEqual( - asIframe('http://localhost:5601/app/myapp#/?embed=true&_g=()') + asIframe('http://localhost:5601/app/myapp#/?embed=true&_g=%28%29') ); }); diff --git a/src/plugins/share/public/components/url_panel_content.tsx b/src/plugins/share/public/components/url_panel_content.tsx index 9e97def0e41f..6f042f4d77f4 100644 --- a/src/plugins/share/public/components/url_panel_content.tsx +++ b/src/plugins/share/public/components/url_panel_content.tsx @@ -45,8 +45,6 @@ import { EuiSwitchEvent, } from '@elastic/eui'; -import { format as formatUrl, parse as parseUrl } from 'url'; - import { FormattedMessage, I18nProvider } from '@osd/i18n/react'; import { HttpStart } from 'opensearch-dashboards/public'; import { i18n } from '@osd/i18n'; @@ -185,30 +183,21 @@ export class UrlPanelContent extends Component { const url = this.getSnapshotUrl(); - const parsedUrl = parseUrl(url); + const parsedUrl = new URL(url); if (!parsedUrl || !parsedUrl.hash) { return; } // Get the application route, after the hash, and remove the #. - const parsedAppUrl = parseUrl(parsedUrl.hash.slice(1), true); - - const formattedUrl = formatUrl({ - protocol: parsedUrl.protocol, - auth: parsedUrl.auth, - host: parsedUrl.host, - pathname: parsedUrl.pathname, - hash: formatUrl({ - pathname: parsedAppUrl.pathname, - query: { - // Add global state to the URL so that the iframe doesn't just show the time range - // default. - _g: parsedAppUrl.query._g, - }, - }), - }); - - return this.updateUrlParams(formattedUrl); + const parsedAppUrl = new URL(parsedUrl.hash.slice(1), window.location.origin); + const formattedAppUrl = new URL(parsedAppUrl.pathname, parsedAppUrl.origin); + const formattedUrl = new URL(parsedUrl.pathname, parsedUrl.origin); + formattedAppUrl.search = new URLSearchParams({ + _g: parsedAppUrl.searchParams.get('_g') ?? '', + }).toString(); + formattedUrl.hash = formattedAppUrl.toString().substring(formattedAppUrl.origin.length); + + return this.updateUrlParams(formattedUrl.toString()); }; private getSnapshotUrl = () => { diff --git a/src/plugins/share/public/lib/url_shortener.ts b/src/plugins/share/public/lib/url_shortener.ts index 81fb7d7ff651..3a98afa7631e 100644 --- a/src/plugins/share/public/lib/url_shortener.ts +++ b/src/plugins/share/public/lib/url_shortener.ts @@ -28,7 +28,6 @@ * under the License. */ -import url from 'url'; import { HttpStart } from 'opensearch-dashboards/public'; import { CREATE_PATH, getGotoPath } from '../../common/short_url_routes'; @@ -36,20 +35,15 @@ export async function shortenUrl( absoluteUrl: string, { basePath, post }: { basePath: string; post: HttpStart['post'] } ) { - const parsedUrl = url.parse(absoluteUrl); - if (!parsedUrl || !parsedUrl.path) { + const parsedUrl = new URL(absoluteUrl); + if (!parsedUrl || !parsedUrl.pathname) { return; } - const path = parsedUrl.path.replace(basePath, ''); - const hash = parsedUrl.hash ? parsedUrl.hash : ''; - const relativeUrl = path + hash; + parsedUrl.pathname = parsedUrl.pathname.replace(basePath, ''); + const relativeUrl = parsedUrl.toString().substring(parsedUrl.origin.length); const body = JSON.stringify({ url: relativeUrl }); const resp = await post(CREATE_PATH, { body }); - return url.format({ - protocol: parsedUrl.protocol, - host: parsedUrl.host, - pathname: `${basePath}${getGotoPath(resp.urlId)}`, - }); + return new URL(`${basePath}${getGotoPath(resp.urlId)}`, parsedUrl.origin).toString(); } diff --git a/src/test_utils/get_url.js b/src/test_utils/get_url.js index 3b25d2bc626b..5168bdb08171 100644 --- a/src/test_utils/get_url.js +++ b/src/test_utils/get_url.js @@ -28,9 +28,6 @@ * under the License. */ -import _ from 'lodash'; -import url from 'url'; - /** * Converts a config and a pathname to a url * @param {object} config A url config @@ -50,17 +47,24 @@ import url from 'url'; * @return {string} */ -export default function getUrl(config, app) { - return url.format(_.assign({}, config, app)); +export default function getUrl(url, app) { + url = new URL(url); + if (app.pathname) { + url.pathname = app.pathname; + } + if (app.hash) { + url.hash = app.hash; + } + return url.toString(); } -getUrl.noAuth = function getUrlNoAuth(config, app) { - config = _.pickBy(config, function (val, param) { - return param !== 'auth'; - }); - return getUrl(config, app); +getUrl.noAuth = function getUrlNoAuth(url, app) { + url = new URL(url); + url.username = ''; + url.password = ''; + return getUrl(url, app); }; -getUrl.baseUrl = function getBaseUrl(config) { - return url.format(_.pick(config, 'protocol', 'hostname', 'port')); +getUrl.baseUrl = function getBaseUrl(url) { + return url.origin; }; diff --git a/src/test_utils/get_url.test.ts b/src/test_utils/get_url.test.ts index c784c9c60b95..59cf2c3b22e1 100644 --- a/src/test_utils/get_url.test.ts +++ b/src/test_utils/get_url.test.ts @@ -33,46 +33,27 @@ import getUrl from './get_url'; describe('getUrl', function () { it('should convert to a url', function () { - const url = getUrl( - { - protocol: 'http', - hostname: 'localhost', - }, - { - pathname: 'foo', - } - ); + const url = getUrl(new URL('http://localhost'), { + pathname: 'foo', + }); expect(url).to.be('http://localhost/foo'); }); it('should convert to a url with port', function () { - const url = getUrl( - { - protocol: 'http', - hostname: 'localhost', - port: 9220, - }, - { - pathname: 'foo', - } - ); + const url = getUrl(new URL('http://localhost:9220'), { + pathname: 'foo', + }); expect(url).to.be('http://localhost:9220/foo'); }); it('should convert to a secure hashed url', function () { expect( - getUrl( - { - protocol: 'https', - hostname: 'localhost', - }, - { - pathname: 'foo', - hash: 'bar', - } - ) + getUrl(new URL('https://localhost'), { + pathname: 'foo', + hash: 'bar', + }) ).to.be('https://localhost/foo#bar'); }); }); diff --git a/test/api_integration/services/supertest.ts b/test/api_integration/services/supertest.ts index 369c25902b11..4eaf2a2531da 100644 --- a/test/api_integration/services/supertest.ts +++ b/test/api_integration/services/supertest.ts @@ -29,18 +29,17 @@ */ import { FtrProviderContext } from 'test/functional/ftr_provider_context'; -import { format as formatUrl } from 'url'; import supertestAsPromised from 'supertest-as-promised'; export function OpenSearchDashboardsSupertestProvider({ getService }: FtrProviderContext) { const config = getService('config'); - const opensearchDashboardsServerUrl = formatUrl(config.get('servers.opensearchDashboards')); + const opensearchDashboardsServerUrl = config.get('servers.opensearchDashboards.serverUrl'); return supertestAsPromised(opensearchDashboardsServerUrl); } export function OpenSearchSupertestProvider({ getService }: FtrProviderContext) { const config = getService('config'); - const elasticSearchServerUrl = formatUrl(config.get('servers.opensearch')); + const elasticSearchServerUrl = config.get('servers.opensearch.serverUrl'); return supertestAsPromised(elasticSearchServerUrl); } diff --git a/test/common/config.js b/test/common/config.js index 5db5748087a3..cff91302899d 100644 --- a/test/common/config.js +++ b/test/common/config.js @@ -29,7 +29,6 @@ */ //import path from 'path'; -import { format as formatUrl } from 'url'; import { opensearchTestConfig, osdTestConfig, opensearchDashboardsServerTestUser } from '@osd/test'; import { services } from './services'; @@ -55,7 +54,7 @@ export default function () { '--logging.json=false', `--server.port=${osdTestConfig.getPort()}`, '--status.allowAnonymous=true', - `--opensearch.hosts=${formatUrl(servers.opensearch)}`, + `--opensearch.hosts=${servers.opensearch.serverUrl}`, `--opensearch.username=${opensearchDashboardsServerTestUser.username}`, `--opensearch.password=${opensearchDashboardsServerTestUser.password}`, `--home.disableWelcomeScreen=false`, diff --git a/test/common/services/deployment.ts b/test/common/services/deployment.ts index d28ed55a2bb3..ebf94108f21c 100644 --- a/test/common/services/deployment.ts +++ b/test/common/services/deployment.ts @@ -44,14 +44,14 @@ export function DeploymentProvider({ getService }: FtrProviderContext) { * Returns OpenSearch Dashboards host URL */ getHostPort() { - return getUrl.baseUrl(config.get('servers.opensearchDashboards')); + return getUrl.baseUrl(config.get('servers.opensearchDashboards.fullURL')); }, /** * Returns OpenSearch host URL */ getOpenSearchHostPort() { - return getUrl.baseUrl(config.get('servers.opensearch')); + return getUrl.baseUrl(config.get('servers.opensearch.fullURL')); }, /** diff --git a/test/common/services/legacy_opensearch.ts b/test/common/services/legacy_opensearch.ts index 597a55a79f3b..1293171a28ff 100644 --- a/test/common/services/legacy_opensearch.ts +++ b/test/common/services/legacy_opensearch.ts @@ -28,8 +28,6 @@ * under the License. */ -import { format as formatUrl } from 'url'; - import * as legacyOpenSearch from 'elasticsearch'; import { DEFAULT_API_VERSION } from '../../../src/core/server/opensearch/opensearch_config'; @@ -42,7 +40,7 @@ export function LegacyOpenSearchProvider({ return new legacyOpenSearch.Client({ apiVersion: DEFAULT_API_VERSION, - host: formatUrl(config.get('servers.opensearch')), + host: config.get('servers.opensearch.serverUrl'), requestTimeout: config.get('timeouts.opensearchRequestTimeout'), }); } diff --git a/test/common/services/opensearch.ts b/test/common/services/opensearch.ts index b162ee961f6d..1c1543174c43 100644 --- a/test/common/services/opensearch.ts +++ b/test/common/services/opensearch.ts @@ -28,7 +28,6 @@ * under the License. */ -import { format as formatUrl } from 'url'; import fs from 'fs'; import { Client } from '@opensearch-project/opensearch'; import { CA_CERT_PATH } from '@osd/dev-utils'; @@ -40,7 +39,7 @@ export function OpenSearchProvider({ getService }: FtrProviderContext) { if (process.env.TEST_CLOUD) { return new Client({ - nodes: [formatUrl(config.get('servers.opensearch'))], + nodes: [config.get('servers.opensearch.serverUrl')], requestTimeout: config.get('timeouts.opensearchRequestTimeout'), }); } else { @@ -48,7 +47,7 @@ export function OpenSearchProvider({ getService }: FtrProviderContext) { ssl: { ca: fs.readFileSync(CA_CERT_PATH, 'utf-8'), }, - nodes: [formatUrl(config.get('servers.opensearch'))], + nodes: [config.get('servers.opensearch.serverUrl')], requestTimeout: config.get('timeouts.opensearchRequestTimeout'), }); } diff --git a/test/common/services/opensearch_archiver.ts b/test/common/services/opensearch_archiver.ts index eb36c9e5a1d5..3985c7aaf466 100644 --- a/test/common/services/opensearch_archiver.ts +++ b/test/common/services/opensearch_archiver.ts @@ -28,7 +28,6 @@ * under the License. */ -import { format as formatUrl } from 'url'; import { OpenSearchArchiver } from '@osd/opensearch-archiver'; import { FtrProviderContext } from '../ftr_provider_context'; @@ -55,7 +54,7 @@ export function OpenSearchArchiverProvider({ client, dataDir, log, - opensearchDashboardsUrl: formatUrl(config.get('servers.opensearchDashboards')), + opensearchDashboardsUrl: config.get('servers.opensearchDashboards.serverUrl'), }); if (hasService('opensearchDashboardsServer')) { diff --git a/test/common/services/opensearch_dashboards_server/opensearch_dashboards_server.ts b/test/common/services/opensearch_dashboards_server/opensearch_dashboards_server.ts index c989bbff9caa..31e75db89a01 100644 --- a/test/common/services/opensearch_dashboards_server/opensearch_dashboards_server.ts +++ b/test/common/services/opensearch_dashboards_server/opensearch_dashboards_server.ts @@ -28,7 +28,6 @@ * under the License. */ -import Url from 'url'; import { OsdClient } from '@osd/dev-utils'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -37,7 +36,7 @@ export function OpenSearchDashboardsServerProvider({ getService }: FtrProviderCo const log = getService('log'); const config = getService('config'); const lifecycle = getService('lifecycle'); - const url = Url.format(config.get('servers.opensearchDashboards')); + const url = config.get('servers.opensearchDashboards.serverUrl'); const defaults = config.get('uiSettings.defaults'); const osd = new OsdClient({ log, diff --git a/test/common/services/security/test_user.ts b/test/common/services/security/test_user.ts index 8cb7b14af24e..fa361705b2d9 100644 --- a/test/common/services/security/test_user.ts +++ b/test/common/services/security/test_user.ts @@ -28,7 +28,6 @@ * under the License. */ -import { format as formatUrl } from 'url'; import supertestAsPromised from 'supertest-as-promised'; import { Role } from './role'; @@ -114,12 +113,11 @@ export async function createTestUserService( export function TestUserSupertestProvider({ getService }: FtrProviderContext) { const config = getService('config'); - const opensearchDashboardsServerConfig = config.get('servers.opensearchDashboards'); - - return supertestAsPromised( - formatUrl({ - ...opensearchDashboardsServerConfig, - auth: `${TEST_USER_NAME}:${TEST_USER_PASSWORD}`, - }) + const opensearchDashboardsServerConfig = new URL( + config.get('servers.opensearchDashboards.fullURL') ); + opensearchDashboardsServerConfig.username = TEST_USER_NAME; + opensearchDashboardsServerConfig.password = TEST_USER_PASSWORD; + + return supertestAsPromised(opensearchDashboardsServerConfig.toString().slice(0, -1)); } diff --git a/test/functional/apps/discover/_shared_links.js b/test/functional/apps/discover/_shared_links.js index 96b7f705c73a..ecae25d8d4d3 100644 --- a/test/functional/apps/discover/_shared_links.js +++ b/test/functional/apps/discover/_shared_links.js @@ -123,9 +123,9 @@ export default function ({ getService, getPageObjects }) { baseUrl + '/app/discover#' + '/view/ab12e3c0-f231-11e6-9486-733b1ac9221a' + - '?_g=(filters%3A!()%2CrefreshInterval%3A(pause%3A!t%2Cvalue%3A0)' + - "%2Ctime%3A(from%3A'2015-09-19T06%3A31%3A44.000Z'%2C" + - "to%3A'2015-09-23T18%3A31%3A44.000Z'))"; + '?_g=%28filters%3A%21%28%29%2CrefreshInterval%3A%28pause%3A%21t%2Cvalue%3A0%29%' + + '2Ctime%3A%28from%3A%272015-09-19T06%3A31%3A44.000Z%27%2C' + + 'to%3A%272015-09-23T18%3A31%3A44.000Z%27%29%29'; await PageObjects.discover.loadSavedSearch('A Saved Search'); await PageObjects.share.clickShareTopNavButton(); await PageObjects.share.exportAsSavedObject(); diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts index 1a303dab14dc..b770ca0de7f0 100644 --- a/test/functional/page_objects/common_page.ts +++ b/test/functional/page_objects/common_page.ts @@ -105,7 +105,7 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo useActualUrl, insertTimestamp, } = navigateProps; - const appUrl = getUrl.noAuth(config.get('servers.opensearchDashboards'), appConfig); + const appUrl = getUrl.noAuth(config.get('servers.opensearchDashboards.fullURL'), appConfig); await retry.try(async () => { if (useActualUrl) { @@ -234,12 +234,12 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo if (config.has(['apps', appName])) { // Legacy applications const appConfig = config.get(['apps', appName]); - appUrl = getUrl.noAuth(config.get('servers.opensearchDashboards'), { + appUrl = getUrl.noAuth(config.get('servers.opensearchDashboards.fullURL'), { pathname: `${basePath}${appConfig.pathname}`, hash: hash || appConfig.hash, }); } else { - appUrl = getUrl.noAuth(config.get('servers.opensearchDashboards'), { + appUrl = getUrl.noAuth(config.get('servers.opensearchDashboards.fullURL'), { pathname: `${basePath}/app/${appName}`, hash, }); diff --git a/test/functional/page_objects/context_page.ts b/test/functional/page_objects/context_page.ts index ea6a56eb18f4..30f14fba2387 100644 --- a/test/functional/page_objects/context_page.ts +++ b/test/functional/page_objects/context_page.ts @@ -51,7 +51,7 @@ export function ContextPageProvider({ getService, getPageObjects }: FtrProviderC ...DEFAULT_INITIAL_STATE, ...overrideInitialState, }); - const appUrl = getUrl.noAuth(config.get('servers.opensearchDashboards'), { + const appUrl = getUrl.noAuth(config.get('servers.opensearchDashboards.fullURL'), { ...config.get('apps.context'), hash: `${config.get('apps.context.hash')}/${indexPattern}/${anchorId}?_a=${initialState}`, }); diff --git a/test/functional/services/supertest.ts b/test/functional/services/supertest.ts index cc33c618be52..c84ff6c0a8fd 100644 --- a/test/functional/services/supertest.ts +++ b/test/functional/services/supertest.ts @@ -29,12 +29,11 @@ */ import { FtrProviderContext } from 'test/functional/ftr_provider_context'; -import { format as formatUrl } from 'url'; import supertestAsPromised from 'supertest-as-promised'; export function OpenSearchDashboardsSupertestProvider({ getService }: FtrProviderContext) { const config = getService('config'); - const opensearchDashboardsServerUrl = formatUrl(config.get('servers.opensearchDashboards')); + const opensearchDashboardsServerUrl = config.get('servers.opensearchDashboards.serverUrl'); return supertestAsPromised(opensearchDashboardsServerUrl); } diff --git a/test/plugin_functional/test_suites/application_links/redirect_app_links.ts b/test/plugin_functional/test_suites/application_links/redirect_app_links.ts index 135cdb125ae5..8df9df61ab5e 100644 --- a/test/plugin_functional/test_suites/application_links/redirect_app_links.ts +++ b/test/plugin_functional/test_suites/application_links/redirect_app_links.ts @@ -28,7 +28,6 @@ * under the License. */ -import url from 'url'; import expect from '@osd/expect'; import { PluginFunctionalProviderContext } from '../../services'; import '../../plugins/core_app_status/public/types'; @@ -40,8 +39,8 @@ declare global { } const getPathWithHash = (absoluteUrl: string) => { - const parsed = url.parse(absoluteUrl); - return `${parsed.path}${parsed.hash ?? ''}`; + const parsed = new URL('', absoluteUrl); + return parsed.toString().substring(parsed.origin.length); }; export default function ({ getService, getPageObjects }: PluginFunctionalProviderContext) { diff --git a/test/plugin_functional/test_suites/core_plugins/application_leave_confirm.ts b/test/plugin_functional/test_suites/core_plugins/application_leave_confirm.ts index b0d04e77954c..56eb0509a00a 100644 --- a/test/plugin_functional/test_suites/core_plugins/application_leave_confirm.ts +++ b/test/plugin_functional/test_suites/core_plugins/application_leave_confirm.ts @@ -28,19 +28,21 @@ * under the License. */ -import url from 'url'; import expect from '@osd/expect'; import { PluginFunctionalProviderContext } from '../../services'; -const getOpenSearchDashboardsUrl = (pathname?: string, search?: string) => - url.format({ - protocol: 'http:', - hostname: process.env.TEST_OPENSEARCH_DASHBOARDS_HOST || 'localhost', - port: process.env.TEST_OPENSEARCH_DASHBOARDS_PORT || '5620', - pathname, - search, - }); - +const getOpenSearchDashboardsUrl = (pathname?: string, search?: string) => { + const url = new URL( + pathname ?? '', + `http://${process.env.TEST_OPENSEARCH_DASHBOARDS_HOST || 'localhost'}:${ + process.env.TEST_OPENSEARCH_DASHBOARDS_PORT || '5620' + }` + ); + if (search) { + url.search = search; + } + return url.toString(); +}; export default function ({ getService, getPageObjects }: PluginFunctionalProviderContext) { const PageObjects = getPageObjects(['common']); const browser = getService('browser'); diff --git a/test/plugin_functional/test_suites/core_plugins/application_status.ts b/test/plugin_functional/test_suites/core_plugins/application_status.ts index b2cb48a50485..3685be38f0ed 100644 --- a/test/plugin_functional/test_suites/core_plugins/application_status.ts +++ b/test/plugin_functional/test_suites/core_plugins/application_status.ts @@ -28,7 +28,6 @@ * under the License. */ -import Url from 'url'; import expect from '@osd/expect'; import { AppNavLinkStatus, @@ -38,14 +37,18 @@ import { import { PluginFunctionalProviderContext } from '../../services'; import '../../plugins/core_app_status/public/types'; -const getOpenSearchDashboardsUrl = (pathname?: string, search?: string) => - Url.format({ - protocol: 'http:', - hostname: process.env.TEST_OPENSEARCH_DASHBOARDS_HOST || 'localhost', - port: process.env.TEST_OPENSEARCH_DASHBOARDS_PORT || '5620', - pathname, - search, - }); +const getOpenSearchDashboardsUrl = (pathname?: string, search?: string) => { + const url = new URL( + pathname ?? '', + `http://${process.env.TEST_OPENSEARCH_DASHBOARDS_HOST || 'localhost'}:${ + process.env.TEST_OPENSEARCH_DASHBOARDS_PORT || '5620' + }` + ); + if (search) { + url.search = search; + } + return url.toString(); +}; export default function ({ getService, getPageObjects }: PluginFunctionalProviderContext) { const PageObjects = getPageObjects(['common']); @@ -126,7 +129,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide await navigateToApp('app_status'); expect(await testSubjects.exists('appStatusApp')).to.eql(true); const currentUrl = await browser.getCurrentUrl(); - expect(Url.parse(currentUrl).pathname).to.eql('/app/app_status/arbitrary/path'); + expect(new URL('', currentUrl).pathname).to.eql('/app/app_status/arbitrary/path'); }); it('can change the state of the currently mounted app', async () => { diff --git a/test/plugin_functional/test_suites/core_plugins/applications.ts b/test/plugin_functional/test_suites/core_plugins/applications.ts index 95c37f99748c..18e780a68568 100644 --- a/test/plugin_functional/test_suites/core_plugins/applications.ts +++ b/test/plugin_functional/test_suites/core_plugins/applications.ts @@ -28,7 +28,6 @@ * under the License. */ -import url from 'url'; import expect from '@osd/expect'; import { PluginFunctionalProviderContext } from '../../services'; @@ -50,14 +49,18 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide return (await wrapper.getSize()).height; }; - const getOpenSearchDashboardsUrl = (pathname?: string, search?: string) => - url.format({ - protocol: 'http:', - hostname: process.env.TEST_OPENSEARCH_DASHBOARDS_HOST || 'localhost', - port: process.env.TEST_OPENSEARCH_DASHBOARDS_PORT || '5620', - pathname, - search, - }); + const getOpenSearchDashboardsUrl = (pathname?: string, search?: string) => { + const url = new URL( + pathname ?? '', + `http://${process.env.TEST_OPENSEARCH_DASHBOARDS_HOST || 'localhost'}:${ + process.env.TEST_OPENSEARCH_DASHBOARDS_PORT || '5620' + }` + ); + if (search) { + url.search = search; + } + return url.toString(); + }; /** Use retry logic to make URL assertions less flaky */ const waitForUrlToBe = (pathname?: string, search?: string) => { diff --git a/test/server_integration/http/platform/headers.ts b/test/server_integration/http/platform/headers.ts index dadabe1e6058..d5eb2dc8d547 100644 --- a/test/server_integration/http/platform/headers.ts +++ b/test/server_integration/http/platform/headers.ts @@ -29,7 +29,6 @@ */ import Http from 'http'; -import Url from 'url'; import { FtrProviderContext } from '../../services/types'; // @ts-ignore @@ -47,9 +46,7 @@ export default function ({ getService }: FtrProviderContext) { const agent = new Http.Agent({ keepAlive: true, }); - const { protocol, hostname, port } = Url.parse( - getUrl.baseUrl(config.get('servers.opensearchDashboards')) - ); + const { protocol, hostname, port } = config.get('servers.opensearchDashboards.fullURL'); function performRequest() { return new Promise((resolve, reject) => { @@ -57,7 +54,7 @@ export default function ({ getService }: FtrProviderContext) { { protocol, hostname, - port, + port: parseInt(port, 10), path: '/', method: 'GET', agent, diff --git a/test/server_integration/http/ssl_redirect/config.js b/test/server_integration/http/ssl_redirect/config.js index 1bd6000fc62d..ae24f8de654a 100644 --- a/test/server_integration/http/ssl_redirect/config.js +++ b/test/server_integration/http/ssl_redirect/config.js @@ -28,7 +28,6 @@ * under the License. */ -import Url from 'url'; import { readFileSync } from 'fs'; import { CA_CERT_PATH, OSD_CERT_PATH, OSD_KEY_PATH } from '@osd/dev-utils'; @@ -39,6 +38,9 @@ export default async function ({ readConfigFile }) { const certificateAuthorities = [readFileSync(CA_CERT_PATH)]; const redirectPort = httpConfig.get('servers.opensearchDashboards.port') + 1234; + const opensearchDashboardsUrl = new URL(httpConfig.get('servers.opensearchDashboards.fullURL')); + opensearchDashboardsUrl.port = redirectPort; + opensearchDashboardsUrl.protocol = 'http'; return { testFiles: [require.resolve('./')], @@ -46,12 +48,7 @@ export default async function ({ readConfigFile }) { ...httpConfig.get('services'), supertest: createOpenSearchDashboardsSupertestProvider({ certificateAuthorities, - opensearchDashboardsUrl: Url.format({ - ...httpConfig.get('servers.opensearchDashboards'), - port: redirectPort, - // test with non ssl protocol - protocol: 'http', - }), + opensearchDashboardsUrl: opensearchDashboardsUrl.toString().slice(0, -1), }), }, servers: { diff --git a/test/server_integration/services/supertest.js b/test/server_integration/services/supertest.js index 0667f1fb1a82..91ab7bcdc54f 100644 --- a/test/server_integration/services/supertest.js +++ b/test/server_integration/services/supertest.js @@ -28,8 +28,6 @@ * under the License. */ -import { format as formatUrl } from 'url'; - import supertestAsPromised from 'supertest-as-promised'; export function createOpenSearchDashboardsSupertestProvider({ @@ -39,7 +37,7 @@ export function createOpenSearchDashboardsSupertestProvider({ return function ({ getService }) { const config = getService('config'); opensearchDashboardsUrl = - opensearchDashboardsUrl ?? formatUrl(config.get('servers.opensearchDashboards')); + opensearchDashboardsUrl ?? config.get('servers.opensearchDashboards.serverUrl'); return certificateAuthorities ? supertestAsPromised.agent(opensearchDashboardsUrl, { ca: certificateAuthorities }) @@ -49,18 +47,14 @@ export function createOpenSearchDashboardsSupertestProvider({ export function OpenSearchDashboardsSupertestWithoutAuthProvider({ getService }) { const config = getService('config'); - const opensearchDashboardsServerConfig = config.get('servers.opensearchDashboards'); + const opensearchDashboardsServerURL = new URL(config.get('servers.opensearchDashboards.fullURL')); + opensearchDashboardsServerURL.username = ''; + opensearchDashboardsServerURL.password = ''; - return supertestAsPromised( - formatUrl({ - ...opensearchDashboardsServerConfig, - auth: false, - }) - ); + return supertestAsPromised(opensearchDashboardsServerURL.toString().slice(0, -1)); } export function OpenSearchSupertestProvider({ getService }) { const config = getService('config'); - const elasticSearchServerUrl = formatUrl(config.get('servers.opensearch')); - return supertestAsPromised(elasticSearchServerUrl); + return supertestAsPromised(config.get('servers.opensearch.serverUrl')); }