| // Copyright 2022 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package web |
| |
| import ( |
| "net/url" |
| "runtime" |
| "testing" |
| ) |
| |
| func TestURLToFilePath(t *testing.T) { |
| for _, tc := range urlTests() { |
| if tc.url == "" { |
| continue |
| } |
| tc := tc |
| |
| t.Run(tc.url, func(t *testing.T) { |
| u, err := url.Parse(tc.url) |
| if err != nil { |
| t.Fatalf("url.Parse(%q): %v", tc.url, err) |
| } |
| |
| path, err := URLToFilePath(u) |
| if err != nil { |
| if err.Error() == tc.wantErr { |
| return |
| } |
| if tc.wantErr == "" { |
| t.Fatalf("urlToFilePath(%v): %v; want <nil>", u, err) |
| } else { |
| t.Fatalf("urlToFilePath(%v): %v; want %s", u, err, tc.wantErr) |
| } |
| } |
| |
| if path != tc.filePath || tc.wantErr != "" { |
| t.Fatalf("urlToFilePath(%v) = %q, <nil>; want %q, %s", u, path, tc.filePath, tc.wantErr) |
| } |
| }) |
| } |
| } |
| |
| func TestURLFromFilePath(t *testing.T) { |
| for _, tc := range urlTests() { |
| if tc.filePath == "" { |
| continue |
| } |
| tc := tc |
| |
| t.Run(tc.filePath, func(t *testing.T) { |
| u, err := URLFromFilePath(tc.filePath) |
| if err != nil { |
| if err.Error() == tc.wantErr { |
| return |
| } |
| if tc.wantErr == "" { |
| t.Fatalf("urlFromFilePath(%v): %v; want <nil>", tc.filePath, err) |
| } else { |
| t.Fatalf("urlFromFilePath(%v): %v; want %s", tc.filePath, err, tc.wantErr) |
| } |
| } |
| |
| if tc.wantErr != "" { |
| t.Fatalf("urlFromFilePath(%v) = <nil>; want error: %s", tc.filePath, tc.wantErr) |
| } |
| |
| wantURL := tc.url |
| if tc.canonicalURL != "" { |
| wantURL = tc.canonicalURL |
| } |
| if u.String() != wantURL { |
| t.Errorf("urlFromFilePath(%v) = %v; want %s", tc.filePath, u, wantURL) |
| } |
| }) |
| } |
| } |
| |
| func urlTests() []urlTest { |
| if runtime.GOOS == "windows" { |
| return urlTestsWindows |
| } |
| return urlTestsOthers |
| } |
| |
| type urlTest struct { |
| url string |
| filePath string |
| canonicalURL string // If empty, assume equal to url. |
| wantErr string |
| } |
| |
| var urlTestsOthers = []urlTest{ |
| // Examples from RFC 8089: |
| { |
| url: `file:///path/to/file`, |
| filePath: `/path/to/file`, |
| }, |
| { |
| url: `file:/path/to/file`, |
| filePath: `/path/to/file`, |
| canonicalURL: `file:///path/to/file`, |
| }, |
| { |
| url: `file://localhost/path/to/file`, |
| filePath: `/path/to/file`, |
| canonicalURL: `file:///path/to/file`, |
| }, |
| |
| // We reject non-local files. |
| { |
| url: `file://host.example.com/path/to/file`, |
| wantErr: "file URL specifies non-local host", |
| }, |
| } |
| |
| var urlTestsWindows = []urlTest{ |
| // Examples from https://blogs.msdn.microsoft.com/ie/2006/12/06/file-uris-in-windows/: |
| |
| { |
| url: `file://laptop/My%20Documents/FileSchemeURIs.doc`, |
| filePath: `\\laptop\My Documents\FileSchemeURIs.doc`, |
| }, |
| { |
| url: `file:///C:/Documents%20and%20Settings/davris/FileSchemeURIs.doc`, |
| filePath: `C:\Documents and Settings\davris\FileSchemeURIs.doc`, |
| }, |
| { |
| url: `file:///D:/Program%20Files/Viewer/startup.htm`, |
| filePath: `D:\Program Files\Viewer\startup.htm`, |
| }, |
| { |
| url: `file:///C:/Program%20Files/Music/Web%20Sys/main.html?REQUEST=RADIO`, |
| filePath: `C:\Program Files\Music\Web Sys\main.html`, |
| canonicalURL: `file:///C:/Program%20Files/Music/Web%20Sys/main.html`, |
| }, |
| { |
| url: `file://applib/products/a-b/abc_9/4148.920a/media/start.swf`, |
| filePath: `\\applib\products\a-b\abc_9\4148.920a\media\start.swf`, |
| }, |
| { |
| url: `file:////applib/products/a%2Db/abc%5F9/4148.920a/media/start.swf`, |
| wantErr: "file URL missing drive letter", |
| }, |
| { |
| url: `C:\Program Files\Music\Web Sys\main.html?REQUEST=RADIO`, |
| wantErr: "non-file URL", |
| }, |
| |
| // The example "file://D:\Program Files\Viewer\startup.htm" errors out in |
| // url.Parse, so we substitute a slash-based path for testing instead. |
| { |
| url: `file://D:/Program Files/Viewer/startup.htm`, |
| wantErr: "file URL encodes volume in host field: too few slashes?", |
| }, |
| |
| // The blog post discourages the use of non-ASCII characters because they |
| // depend on the user's current codepage. However, when we are working with Go |
| // strings we assume UTF-8 encoding, and our url package refuses to encode |
| // URLs to non-ASCII strings. |
| { |
| url: `file:///C:/exampleㄓ.txt`, |
| filePath: `C:\exampleㄓ.txt`, |
| canonicalURL: `file:///C:/example%E3%84%93.txt`, |
| }, |
| { |
| url: `file:///C:/example%E3%84%93.txt`, |
| filePath: `C:\exampleㄓ.txt`, |
| }, |
| |
| // Examples from RFC 8089: |
| |
| // We allow the drive-letter variation from section E.2, because it is |
| // simpler to support than not to. However, we do not generate the shorter |
| // form in the reverse direction. |
| { |
| url: `file:c:/path/to/file`, |
| filePath: `c:\path\to\file`, |
| canonicalURL: `file:///c:/path/to/file`, |
| }, |
| |
| // We encode the UNC share name as the authority following section E.3.1, |
| // because that is what the Microsoft blog post explicitly recommends. |
| { |
| url: `file://host.example.com/Share/path/to/file.txt`, |
| filePath: `\\host.example.com\Share\path\to\file.txt`, |
| }, |
| |
| // We decline the four- and five-slash variations from section E.3.2. |
| // The paths in these URLs would change meaning under path.Clean. |
| { |
| url: `file:////host.example.com/path/to/file`, |
| wantErr: "file URL missing drive letter", |
| }, |
| { |
| url: `file://///host.example.com/path/to/file`, |
| wantErr: "file URL missing drive letter", |
| }, |
| } |