Skip to content

Commit 1a9496a

Browse files
mceachenBethGriggs
authored andcommitted
lib: add UNC support to url.pathToFileURL()
Fixes: #34736 PR-URL: #34743 Reviewed-By: Guy Bedford <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Rich Trott <[email protected]>
1 parent 9a79020 commit 1a9496a

File tree

3 files changed

+73
-21
lines changed

3 files changed

+73
-21
lines changed

‎lib/internal/url.js‎

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const{getConstructorOf, removeColors } = require('internal/util');
2727
const{
2828
ERR_ARG_NOT_ITERABLE,
2929
ERR_INVALID_ARG_TYPE,
30+
ERR_INVALID_ARG_VALUE,
3031
ERR_INVALID_CALLBACK,
3132
ERR_INVALID_FILE_URL_HOST,
3233
ERR_INVALID_FILE_URL_PATH,
@@ -1366,27 +1367,54 @@ const backslashRegEx = /\\/g;
13661367
constnewlineRegEx=/\n/g;
13671368
constcarriageReturnRegEx=/\r/g;
13681369
consttabRegEx=/\t/g;
1370+
1371+
functionencodePathChars(filepath){
1372+
if(filepath.includes('%'))
1373+
filepath=filepath.replace(percentRegEx,'%25');
1374+
// In posix, backslash is a valid character in paths:
1375+
if(!isWindows&&filepath.includes('\\'))
1376+
filepath=filepath.replace(backslashRegEx,'%5C');
1377+
if(filepath.includes('\n'))
1378+
filepath=filepath.replace(newlineRegEx,'%0A');
1379+
if(filepath.includes('\r'))
1380+
filepath=filepath.replace(carriageReturnRegEx,'%0D');
1381+
if(filepath.includes('\t'))
1382+
filepath=filepath.replace(tabRegEx,'%09');
1383+
returnfilepath;
1384+
}
1385+
13691386
functionpathToFileURL(filepath){
1370-
letresolved=path.resolve(filepath);
1371-
// path.resolve strips trailing slashes so we must add them back
1372-
constfilePathLast=filepath.charCodeAt(filepath.length-1);
1373-
if((filePathLast===CHAR_FORWARD_SLASH||
1374-
(isWindows&&filePathLast===CHAR_BACKWARD_SLASH))&&
1375-
resolved[resolved.length-1]!==path.sep)
1376-
resolved+='/';
13771387
constoutURL=newURL('file://');
1378-
if(resolved.includes('%'))
1379-
resolved=resolved.replace(percentRegEx,'%25');
1380-
// In posix, "/" is a valid character in paths
1381-
if(!isWindows&&resolved.includes('\\'))
1382-
resolved=resolved.replace(backslashRegEx,'%5C');
1383-
if(resolved.includes('\n'))
1384-
resolved=resolved.replace(newlineRegEx,'%0A');
1385-
if(resolved.includes('\r'))
1386-
resolved=resolved.replace(carriageReturnRegEx,'%0D');
1387-
if(resolved.includes('\t'))
1388-
resolved=resolved.replace(tabRegEx,'%09');
1389-
outURL.pathname=resolved;
1388+
if(isWindows&&filepath.startsWith('\\\\')){
1389+
// UNC path format: \\server\share\resource
1390+
constpaths=filepath.split('\\');
1391+
if(paths.length<=3){
1392+
thrownewERR_INVALID_ARG_VALUE(
1393+
'filepath',
1394+
filepath,
1395+
'Missing UNC resource path'
1396+
);
1397+
}
1398+
consthostname=paths[2];
1399+
if(hostname.length===0){
1400+
thrownewERR_INVALID_ARG_VALUE(
1401+
'filepath',
1402+
filepath,
1403+
'Empty UNC servername'
1404+
);
1405+
}
1406+
outURL.hostname=domainToASCII(hostname);
1407+
outURL.pathname=encodePathChars(paths.slice(3).join('/'));
1408+
}else{
1409+
letresolved=path.resolve(filepath);
1410+
// path.resolve strips trailing slashes so we must add them back
1411+
constfilePathLast=filepath.charCodeAt(filepath.length-1);
1412+
if((filePathLast===CHAR_FORWARD_SLASH||
1413+
(isWindows&&filePathLast===CHAR_BACKWARD_SLASH))&&
1414+
resolved[resolved.length-1]!==path.sep)
1415+
resolved+='/';
1416+
outURL.pathname=encodePathChars(resolved);
1417+
}
13901418
returnoutURL;
13911419
}
13921420

‎test/parallel/test-url-fileurltopath.js‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ assert.throws(() => url.fileURLToPath('https://a/b/c'),{
9494
// Euro sign (BMP code point)
9595
{path: 'C:\\€',fileURL: 'file:///C:/%E2%82%AC'},
9696
// Rocket emoji (non-BMP code point)
97-
{path: 'C:\\🚀',fileURL: 'file:///C:/%F0%9F%9A%80'}
97+
{path: 'C:\\🚀',fileURL: 'file:///C:/%F0%9F%9A%80'},
98+
// UNC path (see https://docs.microsoft.com/en-us/archive/blogs/ie/file-uris-in-windows)
99+
{path: '\\\\nas\\My Docs\\File.doc',fileURL: 'file://nas/My%20Docs/File.doc'},
98100
];
99101
}else{
100102
testCases=[

‎test/parallel/test-url-pathtofileurl.js‎

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,26 @@ const url = require('url');
2323
assert.ok(fileURL.includes('%25'));
2424
}
2525

26+
{
27+
if(isWindows){
28+
// UNC path: \\server\share\resource
29+
30+
// Missing server:
31+
assert.throws(()=>url.pathToFileURL('\\\\\\no-server'),{
32+
code: 'ERR_INVALID_ARG_VALUE'
33+
});
34+
35+
// Missing share or resource:
36+
assert.throws(()=>url.pathToFileURL('\\\\host'),{
37+
code: 'ERR_INVALID_ARG_VALUE'
38+
});
39+
}else{
40+
// UNC paths on posix are considered a single path that has backslashes:
41+
constfileURL=url.pathToFileURL('\\\\nas\\share\\path.txt').href;
42+
assert.match(fileURL,/file:\/\/.+%5C%5Cnas%5Cshare%5Cpath\.txt$/);
43+
}
44+
}
45+
2646
{
2747
lettestCases;
2848
if(isWindows){
@@ -68,7 +88,9 @@ const url = require('url');
6888
// Euro sign (BMP code point)
6989
{path: 'C:\\€',expected: 'file:///C:/%E2%82%AC'},
7090
// Rocket emoji (non-BMP code point)
71-
{path: 'C:\\🚀',expected: 'file:///C:/%F0%9F%9A%80'}
91+
{path: 'C:\\🚀',expected: 'file:///C:/%F0%9F%9A%80'},
92+
// UNC path (see https://docs.microsoft.com/en-us/archive/blogs/ie/file-uris-in-windows)
93+
{path: '\\\\nas\\My Docs\\File.doc',expected: 'file://nas/My%20Docs/File.doc'}
7294
];
7395
}else{
7496
testCases=[

0 commit comments

Comments
(0)