mirror of
https://github.com/Kong/insomnia.git
synced 2026-04-21 14:47:46 -04:00
fix(pre-request script): avoid encoding tags in parsing request urls - INS-3379 (#7249)
* fix(pre-request script): avoid encoding tags in parsing request urls * fix: refactor requestBody transforming as functions with tests * fix: lint error * fix: revert url decoding * fix: query params are duplicated after script execution * fix: query params are duplicated after script execution * fix: pathParameters property is empty in pre-request script
This commit is contained in:
@@ -1,15 +1,9 @@
|
||||
import url from 'node:url';
|
||||
|
||||
import { describe, expect, it } from '@jest/globals';
|
||||
|
||||
import { Certificate } from '../certificates';
|
||||
import { setUrlParser } from '../urls';
|
||||
|
||||
describe('test Certificate object', () => {
|
||||
it('test methods', () => {
|
||||
// make URL work in Node.js
|
||||
setUrlParser(url.URL);
|
||||
|
||||
const cert = new Certificate({
|
||||
name: 'Certificate for example.com',
|
||||
matches: ['https://example.com'],
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import url from 'node:url';
|
||||
|
||||
import { describe, expect, it } from '@jest/globals';
|
||||
|
||||
import { Request, RequestBody } from '../request';
|
||||
import { setUrlParser } from '../urls';
|
||||
import { mergeRequestBody, Request, RequestBody, toScriptRequestBody } from '../request';
|
||||
|
||||
describe('test request and response objects', () => {
|
||||
setUrlParser(url.URL);
|
||||
|
||||
it('test RequestBody methods', () => {
|
||||
const reqBody = new RequestBody({
|
||||
mode: 'urlencoded',
|
||||
@@ -72,4 +67,49 @@ describe('test request and response objects', () => {
|
||||
const req2 = req.clone();
|
||||
expect(req2.toJSON()).toEqual(req.toJSON());
|
||||
});
|
||||
|
||||
it('test Request body transforming', () => {
|
||||
const bodies = [
|
||||
{
|
||||
mimeType: 'text/plain',
|
||||
text: 'rawContent',
|
||||
},
|
||||
{
|
||||
mimeType: 'application/octet-stream',
|
||||
fileName: 'path/to/file',
|
||||
},
|
||||
{
|
||||
mimeType: 'application/x-www-form-urlencoded',
|
||||
params: [
|
||||
{ name: 'k1', value: 'v1' },
|
||||
{ name: 'k2', value: 'v2' },
|
||||
],
|
||||
},
|
||||
{
|
||||
mimeType: 'application/json',
|
||||
text: `{
|
||||
query: 'query',
|
||||
operationName: 'operation',
|
||||
variables: 'var',
|
||||
}`,
|
||||
},
|
||||
{
|
||||
mimeType: 'image/gif',
|
||||
fileName: '/path/to/image',
|
||||
},
|
||||
{
|
||||
mimeType: 'multipart/form-data',
|
||||
params: [
|
||||
{ name: 'k1', type: 'text', value: 'v1' },
|
||||
{ name: 'k2', type: 'file', value: '/path/to/image' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
bodies.forEach(body => {
|
||||
const originalReqBody = body;
|
||||
const scriptReqBody = new RequestBody(toScriptRequestBody(body));
|
||||
expect(mergeRequestBody(scriptReqBody, originalReqBody)).toEqual(originalReqBody);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import url from 'node:url';
|
||||
|
||||
import { describe, expect, it } from '@jest/globals';
|
||||
|
||||
import { Request } from '../request';
|
||||
import { Response } from '../response';
|
||||
import { setUrlParser } from '../urls';
|
||||
|
||||
describe('test request and response objects', () => {
|
||||
setUrlParser(url.URL);
|
||||
|
||||
it('test Response methods', () => {
|
||||
const req = new Request({
|
||||
url: 'https://hostname.com/path',
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import url from 'node:url';
|
||||
|
||||
import { describe, expect, it } from '@jest/globals';
|
||||
|
||||
import { QueryParam, setUrlParser, Url, UrlMatchPattern } from '../urls';
|
||||
import { QueryParam, Url, UrlMatchPattern } from '../urls';
|
||||
import { Variable } from '../variables';
|
||||
|
||||
describe('test Url object', () => {
|
||||
setUrlParser(url.URL);
|
||||
|
||||
it('test QueryParam', () => {
|
||||
const queryParam = new QueryParam({
|
||||
key: 'uname',
|
||||
@@ -57,33 +53,34 @@ describe('test Url object', () => {
|
||||
],
|
||||
});
|
||||
|
||||
expect(url.getHost()).toEqual('hostvalue.com');
|
||||
expect(url.getHost()).toEqual('hostValue.com');
|
||||
expect(url.getPath()).toEqual('/pathLevel1/pathLevel2');
|
||||
|
||||
expect(url.getQueryString()).toEqual('key1=value1&key2=value2&key3=value3');
|
||||
expect(url.getPathWithQuery()).toEqual('/pathLevel1/pathLevel2?key1=value1&key2=value2&key3=value3');
|
||||
expect(url.getRemote(true)).toEqual('hostvalue.com:777');
|
||||
expect(url.getRemote(false)).toEqual('hostvalue.com:777'); // TODO: add more cases
|
||||
expect(url.getRemote(true)).toEqual('hostValue.com:777');
|
||||
expect(url.getRemote(false)).toEqual('hostValue.com:777'); // TODO: add more cases
|
||||
|
||||
url.removeQueryParams([
|
||||
new QueryParam({ key: 'key1', value: 'value1' }),
|
||||
]);
|
||||
url.removeQueryParams('key3');
|
||||
expect(url.getQueryString()).toEqual('key2=value2');
|
||||
expect(url.toString()).toEqual('https://usernameValue:passwordValue@hostvalue.com:777/pathLevel1/pathLevel2?key2=value2#hashValue');
|
||||
expect(url.toString()).toEqual('https://usernameValue:passwordValue@hostValue.com:777/pathLevel1/pathLevel2?key2=value2#hashValue');
|
||||
|
||||
const url2 = new Url('https://usernameValue:passwordValue@hostvalue.com:777/pathLevel1/pathLevel2?key1=value1&key2=value2#hashValue');
|
||||
expect(url2.getHost()).toEqual('hostvalue.com');
|
||||
const url2 = new Url('https://usernameValue:passwordValue@hostValue.com:777/pathLevel1/pathLevel2?key1=value1&key2=value2#hashValue');
|
||||
expect(url2.getHost()).toEqual('hostValue.com');
|
||||
expect(url2.getPath()).toEqual('/pathLevel1/pathLevel2');
|
||||
expect(url2.getQueryString()).toEqual('key1=value1&key2=value2');
|
||||
expect(url2.getPathWithQuery()).toEqual('/pathLevel1/pathLevel2?key1=value1&key2=value2');
|
||||
expect(url2.getRemote(true)).toEqual('hostvalue.com:777');
|
||||
expect(url2.getRemote(false)).toEqual('hostvalue.com:777'); // TODO: add more cases
|
||||
expect(url2.getRemote(true)).toEqual('hostValue.com:777');
|
||||
expect(url2.getRemote(false)).toEqual('hostValue.com:777'); // TODO: add more cases
|
||||
|
||||
url2.removeQueryParams([
|
||||
new QueryParam({ key: 'key1', value: 'value1' }),
|
||||
]);
|
||||
expect(url2.getQueryString()).toEqual('key2=value2');
|
||||
expect(url2.toString()).toEqual('https://usernameValue:passwordValue@hostvalue.com:777/pathLevel1/pathLevel2?key2=value2#hashValue');
|
||||
expect(url2.toString()).toEqual('https://usernameValue:passwordValue@hostValue.com:777/pathLevel1/pathLevel2?key2=value2#hashValue');
|
||||
});
|
||||
|
||||
it('test Url static methods', () => {
|
||||
@@ -94,6 +91,72 @@ describe('test Url object', () => {
|
||||
|
||||
expect(urlObj.toString()).toEqual(urlStr);
|
||||
});
|
||||
|
||||
const urlParsingTests = [
|
||||
{
|
||||
testName: 'interal url',
|
||||
url: 'inso/',
|
||||
},
|
||||
{
|
||||
testName: 'interal url with protocol',
|
||||
url: 'http://inso/',
|
||||
},
|
||||
{
|
||||
testName: 'interal url with auth',
|
||||
url: 'http://name:pwd@inso/',
|
||||
},
|
||||
{
|
||||
testName: 'interal url with auth without protocol',
|
||||
url: 'name:pwd@inso/',
|
||||
},
|
||||
{
|
||||
testName: 'ip address',
|
||||
url: 'http://127.0.0.1/',
|
||||
},
|
||||
{
|
||||
testName: 'localhost',
|
||||
url: 'https://localhost/',
|
||||
},
|
||||
{
|
||||
testName: 'url with query params',
|
||||
url: 'localhost/?k=v',
|
||||
},
|
||||
{
|
||||
testName: 'url with hash',
|
||||
url: 'localhost/#myHash',
|
||||
},
|
||||
{
|
||||
testName: 'url with query params and hash',
|
||||
url: 'localhost/?k=v#myHash',
|
||||
},
|
||||
{
|
||||
testName: 'url with query params and hash',
|
||||
url: 'localhost/?k={{ myValue }}',
|
||||
},
|
||||
{
|
||||
testName: 'url with query params and hash',
|
||||
url: 'localhost/#My{{ hashValue }}',
|
||||
},
|
||||
{
|
||||
testName: 'url with path params',
|
||||
url: 'inso.com/:path1/:path',
|
||||
},
|
||||
{
|
||||
testName: 'url with tags and path params',
|
||||
url: '{{ _.baseUrl }}/:path1/:path',
|
||||
},
|
||||
{
|
||||
testName: 'hybrid of path params and tags',
|
||||
url: '{{ baseUrl }}/:path_{{ _.pathSuffix }}',
|
||||
},
|
||||
];
|
||||
|
||||
urlParsingTests.forEach(testCase => {
|
||||
it(`parsing url: ${testCase.testName}`, () => {
|
||||
const urlObj = new Url(testCase.url);
|
||||
expect(urlObj.toString()).toEqual(testCase.url);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('test Url Match Pattern', () => {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { expect } from 'chai';
|
||||
|
||||
import { ClientCertificate } from '../../models/client-certificate';
|
||||
import { RequestBodyParameter, RequestHeader } from '../../models/request';
|
||||
import { RequestHeader } from '../../models/request';
|
||||
import { Settings } from '../../models/settings';
|
||||
import { toPreRequestAuth } from './auth';
|
||||
import { CookieObject } from './cookies';
|
||||
import { Environment, Variables } from './environments';
|
||||
import { RequestContext } from './interfaces';
|
||||
import { unsupportedError } from './properties';
|
||||
import { Request as ScriptRequest, RequestBodyOptions, RequestOptions } from './request';
|
||||
import { Request as ScriptRequest, RequestOptions, toScriptRequestBody } from './request';
|
||||
import { Response as ScriptResponse } from './response';
|
||||
import { sendRequest } from './send-request';
|
||||
import { test } from './test';
|
||||
@@ -125,26 +125,6 @@ export function initInsomniaObject(
|
||||
data: iterationData,
|
||||
});
|
||||
|
||||
let reqBodyOpt: RequestBodyOptions = { mode: undefined };
|
||||
if (rawObj.request.body.text != null) {
|
||||
reqBodyOpt = {
|
||||
mode: 'raw',
|
||||
raw: rawObj.request.body.text,
|
||||
};
|
||||
} else if (rawObj.request.body.fileName != null && rawObj.request.body.fileName !== '') {
|
||||
reqBodyOpt = {
|
||||
mode: 'file',
|
||||
file: rawObj.request.body.fileName,
|
||||
};
|
||||
} else if (rawObj.request.body.params != null) {
|
||||
reqBodyOpt = {
|
||||
mode: 'urlencoded',
|
||||
urlencoded: rawObj.request.body.params.map(
|
||||
(param: RequestBodyParameter) => ({ key: param.name, value: param.value })
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
const certificate = rawObj.clientCertificates != null && rawObj.clientCertificates.length > 0 ?
|
||||
{
|
||||
disabled: false,
|
||||
@@ -203,10 +183,11 @@ export function initInsomniaObject(
|
||||
header: rawObj.request.headers.map(
|
||||
(header: RequestHeader) => ({ key: header.name, value: header.value })
|
||||
),
|
||||
body: reqBodyOpt,
|
||||
body: toScriptRequestBody(rawObj.request.body),
|
||||
auth: toPreRequestAuth(rawObj.request.authentication),
|
||||
proxy,
|
||||
certificate,
|
||||
pathParameters: rawObj.request.pathParameters,
|
||||
};
|
||||
const request = new ScriptRequest(reqOpt);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { init as initClientCertificate } from '../../../src/models/client-certificate';
|
||||
import { Request as InsomniaRequest, RequestPathParameter } from '../../../src/models/request';
|
||||
import { Request as InsomniaRequest, RequestBody as InsomniaRequestBody, RequestPathParameter } from '../..//models/request';
|
||||
import { ClientCertificate } from '../../models/client-certificate';
|
||||
import { RequestBodyParameter } from '../../models/request';
|
||||
import { Settings } from '../../models/settings';
|
||||
import { AuthOptions, AuthOptionTypes, fromPreRequestAuth, RequestAuth } from './auth';
|
||||
import { CertificateOptions } from './certificates';
|
||||
@@ -20,7 +21,7 @@ export interface RequestBodyOptions {
|
||||
formdata?: { key: string; value: string; type?: string }[];
|
||||
graphql?: { query: string; operationName: string; variables: object };
|
||||
raw?: string;
|
||||
urlencoded?: { key: string; value: string }[];
|
||||
urlencoded?: { key: string; value: string; type?: string }[];
|
||||
options?: object;
|
||||
}
|
||||
|
||||
@@ -91,7 +92,7 @@ function getClassFields(opts: RequestBodyOptions) {
|
||||
QueryParam,
|
||||
undefined,
|
||||
opts.urlencoded
|
||||
.map(entry => ({ key: entry.key, value: entry.value }))
|
||||
.map(entry => ({ key: entry.key, value: entry.value, type: entry.type }))
|
||||
.map(kv => new QueryParam(kv)),
|
||||
);
|
||||
}
|
||||
@@ -526,13 +527,38 @@ export function mergeClientCertificates(
|
||||
throw Error('Invalid certificate configuration: "cert+key" and "pfx" can not be set at the same time');
|
||||
}
|
||||
|
||||
export function mergeRequests(
|
||||
originalReq: InsomniaRequest,
|
||||
updatedReq: Request
|
||||
): InsomniaRequest {
|
||||
export function toScriptRequestBody(insomniaReqBody: InsomniaRequestBody): RequestBodyOptions {
|
||||
let reqBodyOpt: RequestBodyOptions = { mode: undefined };
|
||||
|
||||
if (insomniaReqBody.text !== undefined) {
|
||||
reqBodyOpt = {
|
||||
mode: 'raw',
|
||||
raw: insomniaReqBody.text,
|
||||
};
|
||||
} else if (insomniaReqBody.fileName !== undefined && insomniaReqBody.fileName !== '') {
|
||||
reqBodyOpt = {
|
||||
mode: 'file',
|
||||
file: insomniaReqBody.fileName,
|
||||
};
|
||||
} else if (insomniaReqBody.params !== undefined) {
|
||||
reqBodyOpt = {
|
||||
mode: 'urlencoded',
|
||||
urlencoded: insomniaReqBody.params.map(
|
||||
(param: RequestBodyParameter) => ({ key: param.name, value: param.value, type: param.type })
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return reqBodyOpt;
|
||||
}
|
||||
|
||||
export function mergeRequestBody(
|
||||
updatedReqBody: RequestBody | undefined,
|
||||
originalReqBody: InsomniaRequestBody
|
||||
): InsomniaRequestBody {
|
||||
let mimeType = 'application/octet-stream';
|
||||
if (updatedReq.body) {
|
||||
switch (updatedReq.body.mode) {
|
||||
if (updatedReqBody) {
|
||||
switch (updatedReqBody.mode) {
|
||||
case undefined:
|
||||
mimeType = 'application/octet-stream';
|
||||
break;
|
||||
@@ -554,31 +580,34 @@ export function mergeRequests(
|
||||
mimeType = 'application/json';
|
||||
break;
|
||||
default:
|
||||
throw Error(`unknown body mode: ${updatedReq.body.mode}`);
|
||||
throw Error(`unknown request body mode: ${updatedReqBody.mode}`);
|
||||
}
|
||||
}
|
||||
if (originalReq.body.mimeType) {
|
||||
mimeType = originalReq.body.mimeType;
|
||||
if (originalReqBody.mimeType) {
|
||||
mimeType = originalReqBody.mimeType;
|
||||
}
|
||||
|
||||
const queryParameters = updatedReq.url.query.map(
|
||||
queryParam => ({ name: queryParam.key, value: queryParam.value })
|
||||
,
|
||||
{},
|
||||
);
|
||||
const updatedReqProperties: Partial<InsomniaRequest> = {
|
||||
// url is encoded during parsing phase. Need decode url In order to recognized variables
|
||||
url: decodeURI(typeof updatedReq.url === 'string' ? updatedReq.url : updatedReq.url.toString()),
|
||||
method: updatedReq.method,
|
||||
body: {
|
||||
mimeType: mimeType,
|
||||
text: updatedReq.body?.raw,
|
||||
fileName: updatedReq.body?.file,
|
||||
params: updatedReq.body?.urlencoded?.map(
|
||||
(param: { key: string; value: string }) => ({ name: param.key, value: param.value }),
|
||||
{},
|
||||
return {
|
||||
mimeType: mimeType,
|
||||
text: updatedReqBody?.raw,
|
||||
fileName: updatedReqBody?.file,
|
||||
params: updatedReqBody?.urlencoded?.map(
|
||||
(param: { key: string; value: string; type?: string }) => (
|
||||
{ name: param.key, value: param.value, type: param.type }
|
||||
),
|
||||
},
|
||||
{},
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
export function mergeRequests(
|
||||
originalReq: InsomniaRequest,
|
||||
updatedReq: Request
|
||||
): InsomniaRequest {
|
||||
const updatedReqProperties: Partial<InsomniaRequest> = {
|
||||
url: updatedReq.url.toString(),
|
||||
method: updatedReq.method,
|
||||
body: mergeRequestBody(updatedReq.body, originalReq.body),
|
||||
headers: updatedReq.headers.map(
|
||||
(header: Header) => ({
|
||||
name: header.key,
|
||||
@@ -589,7 +618,7 @@ export function mergeRequests(
|
||||
authentication: fromPreRequestAuth(updatedReq.auth),
|
||||
preRequestScript: '',
|
||||
pathParameters: updatedReq.pathParameters,
|
||||
parameters: queryParameters,
|
||||
parameters: [], // set empty array as parameters will be part of url field
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import { Property, PropertyBase, PropertyList } from './properties';
|
||||
import { Variable, VariableList } from './variables';
|
||||
|
||||
// TODO: make it also work with node.js
|
||||
let UrlParser = URL;
|
||||
let UrlSearchParams = URLSearchParams;
|
||||
export function setUrlParser(provider: any) {
|
||||
UrlParser = provider;
|
||||
}
|
||||
export function setUrlSearchParams(provider: any) {
|
||||
UrlSearchParams = provider;
|
||||
}
|
||||
@@ -21,8 +16,9 @@ export class QueryParam extends Property {
|
||||
|
||||
key: string;
|
||||
value: string;
|
||||
type?: string;
|
||||
|
||||
constructor(options: { key: string; value: string } | string) {
|
||||
constructor(options: { key: string; value: string; type?: string } | string) {
|
||||
super();
|
||||
|
||||
if (typeof options === 'string') {
|
||||
@@ -30,12 +26,14 @@ export class QueryParam extends Property {
|
||||
const optionsObj = JSON.parse(options);
|
||||
this.key = optionsObj.key;
|
||||
this.value = optionsObj.value;
|
||||
this.type = optionsObj.type;
|
||||
} catch (e) {
|
||||
throw Error(`invalid QueryParam options ${e}`);
|
||||
}
|
||||
} else if (typeof options === 'object' && ('key' in options) && ('value' in options)) {
|
||||
this.key = options.key;
|
||||
this.value = options.value;
|
||||
this.type = options.type;
|
||||
} else {
|
||||
throw Error('unknown options for new QueryParam');
|
||||
}
|
||||
@@ -93,7 +91,7 @@ export class QueryParam extends Property {
|
||||
return params.toString();
|
||||
}
|
||||
|
||||
update(param: string | { key: string; value: string }) {
|
||||
update(param: string | { key: string; value: string; type?: string }) {
|
||||
if (typeof param === 'string') {
|
||||
const paramObj = QueryParam.parseSingle(param);
|
||||
this.key = typeof paramObj.key === 'string' ? paramObj.key : '';
|
||||
@@ -101,6 +99,7 @@ export class QueryParam extends Property {
|
||||
} else if ('key' in param && 'value' in param) {
|
||||
this.key = param.key;
|
||||
this.value = param.value;
|
||||
this.type = param.type;
|
||||
} else {
|
||||
throw Error('the param for update must be: string | { key: string; value: string }');
|
||||
}
|
||||
@@ -145,12 +144,6 @@ export class Url extends PropertyBase {
|
||||
}
|
||||
|
||||
private setFields(def: UrlOptions | string) {
|
||||
if (typeof def === 'string') {
|
||||
def = def.includes('://') ? def : 'http://' + def;
|
||||
} else if (!def.protocol || def.protocol === '') {
|
||||
def.protocol = 'http://';
|
||||
}
|
||||
|
||||
const urlObj = typeof def === 'string' ? Url.parse(def) : def;
|
||||
|
||||
if (urlObj) {
|
||||
@@ -193,30 +186,98 @@ export class Url extends PropertyBase {
|
||||
}
|
||||
|
||||
static parse(urlStr: string): UrlOptions | undefined {
|
||||
// TODO: enable validation
|
||||
// if (!UrlParser.canParse(urlStr)) {
|
||||
// console.error(`invalid URL string ${urlStr}`);
|
||||
// return undefined;
|
||||
// }
|
||||
// the URL API (for web) is not leveraged here because the input string could contain tags for interpolation
|
||||
// which will be encoded, then it would introduce confusion for users in manipulation
|
||||
|
||||
const url = new UrlParser(urlStr);
|
||||
const query = Array.from(url.searchParams.entries())
|
||||
.map(kv => {
|
||||
const kvArray = kv as [string, string];
|
||||
return { key: kvArray[0], value: kvArray[1] };
|
||||
});
|
||||
const endOfProto = urlStr.indexOf('://');
|
||||
const protocol = endOfProto >= 0 ? urlStr.slice(0, endOfProto + 1) : '';
|
||||
|
||||
const potentialStartOfAuth = protocol === '' ? 0 : endOfProto + 3;
|
||||
const endOfAuth = urlStr.indexOf('@', potentialStartOfAuth);
|
||||
let auth = undefined;
|
||||
if (endOfAuth >= 0 && potentialStartOfAuth < endOfAuth) { // e.g., '@insomnia.com' will be ignored
|
||||
const authStr = endOfAuth >= 0 ? urlStr.slice(potentialStartOfAuth, endOfAuth) : '';
|
||||
const authParts = authStr?.split(':');
|
||||
if (authParts.length < 2) {
|
||||
throw Error('new Url(): failed to parse auth in url ${urlStr}');
|
||||
}
|
||||
auth = { username: authParts[0], password: authParts[1] };
|
||||
}
|
||||
|
||||
const startOfHash = urlStr.indexOf('#');
|
||||
const hash = startOfHash >= 0 ? urlStr.slice(startOfHash + 1) : undefined;
|
||||
|
||||
const endOfQuery = startOfHash >= 0 ? startOfHash : urlStr.length;
|
||||
const startOfQuery = urlStr.lastIndexOf('?', endOfQuery);
|
||||
const query = new Array<{ key: string; value: string }>();
|
||||
if (startOfQuery >= 0) {
|
||||
const queryStr = urlStr.slice(startOfQuery + 1, endOfQuery);
|
||||
query.push(
|
||||
...queryStr
|
||||
.split('&')
|
||||
.map(pairStr => {
|
||||
const queryParts = pairStr.split('=');
|
||||
const key = queryParts[0];
|
||||
const value = queryParts.length > 1 ? queryParts[1] : '';
|
||||
return { key, value };
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const startOfPathname = urlStr.indexOf('/', endOfProto >= 0 ? endOfProto + 3 : 0);
|
||||
const path = new Array<string>();
|
||||
if (startOfPathname >= 0) {
|
||||
let endOfPathname = urlStr.length;
|
||||
if (startOfQuery >= 0) {
|
||||
endOfPathname = startOfQuery;
|
||||
} else if (startOfHash >= 0) {
|
||||
endOfPathname = startOfHash;
|
||||
}
|
||||
const pathname = urlStr.slice(startOfPathname, endOfPathname);
|
||||
path.push(
|
||||
...pathname.split('/'),
|
||||
);
|
||||
}
|
||||
|
||||
let potentialStartOfHostname = 0;
|
||||
if (endOfAuth >= 0) {
|
||||
potentialStartOfHostname = endOfAuth + 1;
|
||||
} else if (endOfProto >= 0) {
|
||||
potentialStartOfHostname = endOfProto + 3;
|
||||
}
|
||||
let potentialEndOfHostname = urlStr.length;
|
||||
if (startOfPathname >= 0) {
|
||||
potentialEndOfHostname = startOfPathname;
|
||||
} else if (startOfQuery >= 0) {
|
||||
potentialEndOfHostname = startOfQuery;
|
||||
} else if (startOfHash >= 0) {
|
||||
potentialEndOfHostname = startOfHash;
|
||||
}
|
||||
const host = new Array<string>();
|
||||
let port = undefined;
|
||||
if (potentialStartOfHostname < potentialEndOfHostname) {
|
||||
const hostname = urlStr.slice(potentialStartOfHostname, potentialEndOfHostname);
|
||||
const hostnameParts = hostname.split(':');
|
||||
if (hostnameParts.length === 2) {
|
||||
port = hostnameParts[1];
|
||||
} else if (hostnameParts.length > 2) {
|
||||
throw Error('new Url(): failed to parse hostname in url ${urlStr}');
|
||||
}
|
||||
|
||||
host.push(
|
||||
...
|
||||
hostnameParts[0].split('.'),
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
auth: url.username !== '' ? { // TODO: make it compatible with RequestAuth
|
||||
username: url.username,
|
||||
password: url.password,
|
||||
} : undefined,
|
||||
hash: url.hash,
|
||||
host: url.hostname.split('/'),
|
||||
path: url.pathname.split('/'),
|
||||
port: url.port,
|
||||
protocol: url.protocol, // e.g. https:
|
||||
auth,
|
||||
protocol,
|
||||
host,
|
||||
port,
|
||||
path,
|
||||
query,
|
||||
hash,
|
||||
variables: [],
|
||||
};
|
||||
}
|
||||
@@ -236,7 +297,7 @@ export class Url extends PropertyBase {
|
||||
}
|
||||
|
||||
getHost() {
|
||||
return this.host.join('.').toLowerCase();
|
||||
return this.host.join('.');
|
||||
}
|
||||
|
||||
getPath(unresolved?: boolean) {
|
||||
@@ -259,7 +320,11 @@ export class Url extends PropertyBase {
|
||||
const params = new UrlSearchParams();
|
||||
this.query.each(param => params.append(param.key, param.value), {});
|
||||
|
||||
return params.toString();
|
||||
const queryParamStrs = this.query.map(pair => {
|
||||
return pair.value ? `${pair.key}=${pair.value}` : pair.key;
|
||||
}, {});
|
||||
|
||||
return queryParamStrs.join('&');
|
||||
}
|
||||
|
||||
getRemote(forcePort?: boolean) {
|
||||
@@ -310,20 +375,19 @@ export class Url extends PropertyBase {
|
||||
}
|
||||
|
||||
toString(forceProtocol?: boolean) {
|
||||
const protocol = forceProtocol ?
|
||||
(this.protocol ? this.protocol : 'https:') :
|
||||
(this.protocol ? this.protocol : '');
|
||||
const protocolStr = forceProtocol ?
|
||||
(this.protocol ? `${this.protocol}//` : 'http://') :
|
||||
(this.protocol ? `${this.protocol}//` : '');
|
||||
|
||||
const parser = new UrlParser(`${protocol}//` + this.getHost());
|
||||
parser.username = this.auth?.username || '';
|
||||
parser.password = this.auth?.password || '';
|
||||
parser.port = this.port || '';
|
||||
parser.pathname = this.getPath();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
parser.search = this.getQueryString();
|
||||
parser.hash = this.hash || '';
|
||||
const authStr = this.auth ? `${this.auth.username}:${this.auth.password}@` : '';
|
||||
const hostStr = this.getHost();
|
||||
const portStr = this.port ? `:${this.port}` : '';
|
||||
const pathStr = this.getPath();
|
||||
const queryStr = this.getQueryString() ? `?${this.getQueryString()}` : '';
|
||||
const hashStr = this.hash ? `#${this.hash}` : '';
|
||||
|
||||
return parser.toString();
|
||||
return `${protocolStr}${authStr}${hostStr}${portStr}${pathStr}${queryStr}${hashStr}`;
|
||||
// return parser.toString();
|
||||
}
|
||||
|
||||
update(url: UrlOptions | string) {
|
||||
@@ -567,9 +631,6 @@ export class UrlMatchPatternList<T extends UrlMatchPattern> extends PropertyList
|
||||
return '_kind' in obj && obj._kind === 'UrlMatchPatternList';
|
||||
}
|
||||
|
||||
// TODO: unsupported yet
|
||||
// toObject(excludeDisabledopt, nullable, caseSensitiveopt, nullable, multiValueopt, nullable, sanitizeKeysopt) → {Object}
|
||||
|
||||
test(urlStr: string) {
|
||||
return this
|
||||
.filter(matchPattern => matchPattern.test(urlStr), {})
|
||||
|
||||
Reference in New Issue
Block a user