Skip to content

Commit

Permalink
URI.toString takes parameter that controls how much encoding happens,…
Browse files Browse the repository at this point in the history
… affects #2990, #4315
  • Loading branch information
jrieken committed Mar 31, 2016
1 parent c924d40 commit 1783246
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 31 deletions.
53 changes: 31 additions & 22 deletions src/vs/base/common/uri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,21 @@

import platform = require('vs/base/common/platform');


function _encode(ch: string): string {
return '%' + ch.charCodeAt(0).toString(16).toUpperCase();
}

// see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
function fixedEncodeURIComponent(str: string): string {
return encodeURIComponent(str).replace(/[!'()*]/g, c => '%' + c.charCodeAt(0).toString(16).toUpperCase());
function encodeURIComponent2(str: string): string {
return encodeURIComponent(str).replace(/[!'()*]/g, _encode);
}

function encodeNoop(str: string): string {
return str;
}


/**
* Uniform Resource Identifier (URI) http://tools.ietf.org/html/rfc3986.
* This class is a simple parser which creates the basic component paths
Expand All @@ -34,7 +44,7 @@ export default class URI {
private static _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
private static _driveLetterPath = /^\/[a-zA-z]:/;
private static _upperCaseDrive = /^(\/)?([A-Z]:)/;

private _scheme: string;
private _authority: string;
private _path: string;
Expand Down Expand Up @@ -243,14 +253,25 @@ export default class URI {

// ---- printing/externalize ---------------------------

public toString(): string {
/**
* @param encode Only encode the minimally
*/
public toString(encode: boolean = true): string {
if (!encode) {
return URI._asFormatted(this, false);
}
if (!this._formatted) {
this._formatted = URI._asFormatted(this);
this._formatted = URI._asFormatted(this, true);
}
return this._formatted;
}

private static _asFormatted(uri: URI): string {
private static _asFormatted(uri: URI, encode: boolean): string {

const encoder = encode
? encodeURIComponent2
: encodeNoop;

const parts: string[] = [];

let {scheme, authority, path, query, fragment} = uri;
Expand All @@ -264,9 +285,9 @@ export default class URI {
authority = authority.toLowerCase();
let idx = authority.indexOf(':');
if (idx === -1) {
parts.push(fixedEncodeURIComponent(authority));
parts.push(encoder(authority));
} else {
parts.push(fixedEncodeURIComponent(authority.substr(0, idx)), authority.substr(idx));
parts.push(encoder(authority.substr(0, idx)), authority.substr(idx));
}
}
if (path) {
Expand All @@ -281,29 +302,17 @@ export default class URI {
while(true) {
let idx = path.indexOf(URI._slash, lastIdx);
if (idx === -1) {
parts.push(fixedEncodeURIComponent(path.substring(lastIdx)));
parts.push(encoder(path.substring(lastIdx)).replace(/[#?]/, _encode));
break;
}
parts.push(fixedEncodeURIComponent(path.substring(lastIdx, idx)), URI._slash);
parts.push(encoder(path.substring(lastIdx, idx)).replace(/[#?]/, _encode), URI._slash);
lastIdx = idx + 1;
};
}
if (query) {
// in http(s) querys often use 'key=value'-pairs and
// ampersand characters for multiple pairs
const encoder = /https?/i.test(scheme)
? encodeURI
: fixedEncodeURIComponent;

parts.push('?', encoder(query));
}
if (fragment) {
// in http(s) querys often use 'key=value'-pairs and
// ampersand characters for multiple pairs
const encoder = /https?/i.test(scheme)
? encodeURI
: fixedEncodeURIComponent;

parts.push('#', encoder(fragment));
}

Expand Down
44 changes: 35 additions & 9 deletions src/vs/base/test/common/uri.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,36 @@ suite('URI', () => {
assert.equal(URI.create('', '', 'my/path').toString(), 'my/path');
assert.equal(URI.create('', '', '/my/path').toString(), '/my/path');
//http://a-test-site.com/#test=true
assert.equal(URI.create('http', 'a-test-site.com', '/', 'test=true').toString(), 'http://a-test-site.com/?test=true');
assert.equal(URI.create('http', 'a-test-site.com', '/', '', 'test=true').toString(), 'http://a-test-site.com/#test=true');
assert.equal(URI.create('http', 'a-test-site.com', '/', 'test=true').toString(), 'http://a-test-site.com/?test%3Dtrue');
assert.equal(URI.create('http', 'a-test-site.com', '/', '', 'test=true').toString(), 'http://a-test-site.com/#test%3Dtrue');
});

test('http#toString, encode=FALSE', () => {
assert.equal(URI.create('http', 'a-test-site.com', '/', 'test=true').toString(false), 'http://a-test-site.com/?test=true');
assert.equal(URI.create('http', 'a-test-site.com', '/', '', 'test=true').toString(false), 'http://a-test-site.com/#test=true');
assert.equal(URI.create().withScheme('http').withPath('/api/files/test.me').withQuery('t=1234').toString(false), 'http:/api/files/test.me?t=1234');

var value = URI.parse('file://shares/pröjects/c%23/#l12');
assert.equal(value.authority, 'shares');
assert.equal(value.path, '/pröjects/c#/');
assert.equal(value.fragment, 'l12');
assert.equal(value.toString(), 'file://shares/pr%C3%B6jects/c%23/#l12');
assert.equal(value.toString(false), 'file://shares/pröjects/c%23/#l12');

var uri2 = URI.parse(value.toString(false));
var uri3 = URI.parse(value.toString(true));
assert.equal(uri2.authority, uri3.authority);
assert.equal(uri2.path, uri3.path);
assert.equal(uri2.query, uri3.query);
assert.equal(uri2.fragment, uri3.fragment);
});

test('with', () => {
assert.equal(URI.create().withScheme('http').withPath('/api/files/test.me').withQuery('t=1234').toString(), 'http:/api/files/test.me?t=1234');
assert.equal(URI.create().with('http', '', '/api/files/test.me', 't=1234', '').toString(), 'http:/api/files/test.me?t=1234');
assert.equal(URI.create().with('https', '', '/api/files/test.me', 't=1234', '').toString(), 'https:/api/files/test.me?t=1234');
assert.equal(URI.create().with('HTTP', '', '/api/files/test.me', 't=1234', '').toString(), 'HTTP:/api/files/test.me?t=1234');
assert.equal(URI.create().with('HTTPS', '', '/api/files/test.me', 't=1234', '').toString(), 'HTTPS:/api/files/test.me?t=1234');
assert.equal(URI.create().withScheme('http').withPath('/api/files/test.me').withQuery('t=1234').toString(), 'http:/api/files/test.me?t%3D1234');
assert.equal(URI.create().with('http', '', '/api/files/test.me', 't=1234', '').toString(), 'http:/api/files/test.me?t%3D1234');
assert.equal(URI.create().with('https', '', '/api/files/test.me', 't=1234', '').toString(), 'https:/api/files/test.me?t%3D1234');
assert.equal(URI.create().with('HTTP', '', '/api/files/test.me', 't=1234', '').toString(), 'HTTP:/api/files/test.me?t%3D1234');
assert.equal(URI.create().with('HTTPS', '', '/api/files/test.me', 't=1234', '').toString(), 'HTTPS:/api/files/test.me?t%3D1234');
assert.equal(URI.create().with('boo', '', '/api/files/test.me', 't=1234', '').toString(), 'boo:/api/files/test.me?t%3D1234');
});

Expand Down Expand Up @@ -333,14 +353,20 @@ suite('URI', () => {

let uri = URI.parse('http://go.microsoft.com/fwlink/?LinkId=518008');
assert.equal(uri.query, 'LinkId=518008');
assert.equal(uri.toString(), 'http://go.microsoft.com/fwlink/?LinkId=518008');
assert.equal(uri.toString(false), 'http://go.microsoft.com/fwlink/?LinkId=518008');
assert.equal(uri.toString(), 'http://go.microsoft.com/fwlink/?LinkId%3D518008');

let uri2 = URI.parse(uri.toString());
assert.equal(uri2.query, 'LinkId=518008');
assert.equal(uri2.query, uri.query);

uri = URI.parse('http://go.microsoft.com/fwlink/?LinkId=518008&foö&ké¥=üü');
assert.equal(uri.query, 'LinkId=518008&foö&ké¥=üü');
assert.equal(uri.toString(), 'http://go.microsoft.com/fwlink/?LinkId=518008&fo%C3%B6&k%C3%A9%C2%A5=%C3%BC%C3%BC');
assert.equal(uri.toString(false), 'http://go.microsoft.com/fwlink/?LinkId=518008&foö&ké¥=üü');
assert.equal(uri.toString(), 'http://go.microsoft.com/fwlink/?LinkId%3D518008%26fo%C3%B6%26k%C3%A9%C2%A5%3D%C3%BC%C3%BC');

uri2 = URI.parse(uri.toString());
assert.equal(uri2.query, 'LinkId=518008&foö&ké¥=üü');
assert.equal(uri2.query, uri.query);
});
});

0 comments on commit 1783246

Please sign in to comment.