- added more guards against panic when continuing on error
- removed more debug printing
- otherwise, no change in functionality
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
1 |
// Copyright 2015 go-swagger maintainers
|
|
2 |
//
|
|
3 |
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4 |
// you may not use this file except in compliance with the License.
|
|
5 |
// You may obtain a copy of the License at
|
|
6 |
//
|
|
7 |
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8 |
//
|
|
9 |
// Unless required by applicable law or agreed to in writing, software
|
|
10 |
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12 |
// See the License for the specific language governing permissions and
|
|
13 |
// limitations under the License.
|
|
14 |
|
|
15 |
package spec |
|
16 |
|
|
17 |
import ( |
|
18 |
"fmt"
|
|
19 |
"net/url"
|
|
20 |
"os"
|
|
21 |
"path"
|
|
22 |
"path/filepath"
|
|
23 |
"runtime"
|
|
24 |
"strings"
|
|
25 |
)
|
|
26 |
|
|
27 |
const windowsOS = "windows" |
|
28 |
|
|
29 |
// normalize absolute path for cache.
|
|
30 |
// on Windows, drive letters should be converted to lower as scheme in net/url.URL
|
|
31 |
func normalizeAbsPath(path string) string { |
|
32 | 4 |
u, err := url.Parse(path) |
33 |
if err != nil { |
|
34 |
debugLog("normalize absolute path failed: %s", err) |
|
35 |
return path |
|
36 |
}
|
|
37 | 4 |
return u.String() |
38 |
}
|
|
39 |
|
|
40 |
// base or refPath could be a file path or a URL
|
|
41 |
// given a base absolute path and a ref path, return the absolute path of refPath
|
|
42 |
// 1) if refPath is absolute, return it
|
|
43 |
// 2) if refPath is relative, join it with basePath keeping the scheme, hosts, and ports if exists
|
|
44 |
// base could be a directory or a full file path
|
|
45 |
func normalizePaths(refPath, base string) string { |
|
46 | 4 |
refURL, _ := url.Parse(refPath) |
47 |
if path.IsAbs(refURL.Path) || filepath.IsAbs(refPath) { |
|
48 |
// refPath is actually absolute
|
|
49 |
if refURL.Host != "" { |
|
50 | 4 |
return refPath |
51 |
}
|
|
52 | 4 |
parts := strings.Split(refPath, "#") |
53 | 4 |
result := filepath.FromSlash(parts[0]) |
54 |
if len(parts) == 2 { |
|
55 | 4 |
result += "#" + parts[1] |
56 |
}
|
|
57 | 4 |
return result |
58 |
}
|
|
59 |
|
|
60 |
// relative refPath
|
|
61 | 4 |
baseURL, _ := url.Parse(base) |
62 |
if !strings.HasPrefix(refPath, "#") { |
|
63 |
// combining paths
|
|
64 |
if baseURL.Host != "" { |
|
65 | 4 |
baseURL.Path = path.Join(path.Dir(baseURL.Path), refURL.Path) |
66 |
} else { // base is a file |
|
67 | 4 |
newBase := fmt.Sprintf("%s#%s", filepath.Join(filepath.Dir(base), filepath.FromSlash(refURL.Path)), refURL.Fragment) |
68 | 4 |
return newBase |
69 |
}
|
|
70 |
|
|
71 |
}
|
|
72 |
// copying fragment from ref to base
|
|
73 | 4 |
baseURL.Fragment = refURL.Fragment |
74 | 4 |
return baseURL.String() |
75 |
}
|
|
76 |
|
|
77 |
// isRoot is a temporary hack to discern windows file ref for ref.IsRoot().
|
|
78 |
// TODO: a more thorough change is needed to handle windows file refs.
|
|
79 |
func isRoot(ref *Ref) bool { |
|
80 | 4 |
if runtime.GOOS != windowsOS { |
81 | 4 |
return ref.IsRoot() |
82 |
}
|
|
83 |
return !filepath.IsAbs(ref.String()) |
|
84 |
}
|
|
85 |
|
|
86 |
// isAbs is a temporary hack to discern windows file ref for url IsAbs().
|
|
87 |
// TODO: a more thorough change is needed to handle windows file refs.
|
|
88 |
func isAbs(u *url.URL) bool { |
|
89 | 4 |
if runtime.GOOS != windowsOS { |
90 | 4 |
return u.IsAbs() |
91 |
}
|
|
92 |
if len(u.Scheme) <= 1 { |
|
93 |
// drive letter got caught as URI scheme
|
|
94 |
return false |
|
95 |
}
|
|
96 |
return u.IsAbs() |
|
97 |
}
|
|
98 |
|
|
99 |
// denormalizePaths returns to simplest notation on file $ref,
|
|
100 |
// i.e. strips the absolute path and sets a path relative to the base path.
|
|
101 |
//
|
|
102 |
// This is currently used when we rewrite ref after a circular ref has been detected
|
|
103 |
func denormalizeFileRef(ref *Ref, relativeBase, originalRelativeBase string) *Ref { |
|
104 | 4 |
debugLog("denormalizeFileRef for: %s (relative: %s, original: %s)", ref.String(), |
105 | 4 |
relativeBase, originalRelativeBase) |
106 |
|
|
107 |
// log.Printf("denormalize: %s, IsRoot: %t,HasFragmentOnly: %t, HasFullURL: %t", ref.String(), ref.IsRoot(), ref.HasFragmentOnly, ref.HasFullURL)
|
|
108 |
if ref.String() == "" || isRoot(ref) || ref.HasFragmentOnly { |
|
109 | 4 |
return ref |
110 |
}
|
|
111 |
// strip relativeBase from URI
|
|
112 | 4 |
relativeBaseURL, _ := url.Parse(relativeBase) |
113 | 4 |
relativeBaseURL.Fragment = "" |
114 |
|
|
115 |
if isAbs(relativeBaseURL) && strings.HasPrefix(ref.String(), relativeBase) { |
|
116 |
// this should work for absolute URI (e.g. http://...): we have an exact match, just trim prefix
|
|
117 | 4 |
r, _ := NewRef(strings.TrimPrefix(ref.String(), relativeBase)) |
118 | 4 |
return &r |
119 |
}
|
|
120 |
|
|
121 |
if isAbs(relativeBaseURL) { |
|
122 |
// other absolute URL get unchanged (i.e. with a non-empty scheme)
|
|
123 |
return ref |
|
124 |
}
|
|
125 |
|
|
126 |
// for relative file URIs:
|
|
127 | 4 |
originalRelativeBaseURL, _ := url.Parse(originalRelativeBase) |
128 | 4 |
originalRelativeBaseURL.Fragment = "" |
129 |
if strings.HasPrefix(ref.String(), originalRelativeBaseURL.String()) { |
|
130 |
// the resulting ref is in the expanded spec: return a local ref
|
|
131 | 4 |
r, _ := NewRef(strings.TrimPrefix(ref.String(), originalRelativeBaseURL.String())) |
132 | 4 |
return &r |
133 |
}
|
|
134 |
|
|
135 |
// check if we may set a relative path, considering the original base path for this spec.
|
|
136 |
// Example:
|
|
137 |
// spec is located at /mypath/spec.json
|
|
138 |
// my normalized ref points to: /mypath/item.json#/target
|
|
139 |
// expected result: item.json#/target
|
|
140 | 4 |
parts := strings.Split(ref.String(), "#") |
141 | 4 |
relativePath, err := filepath.Rel(filepath.Dir(originalRelativeBaseURL.String()), parts[0]) |
142 |
if err != nil { |
|
143 |
// there is no common ancestor (e.g. different drives on windows)
|
|
144 |
// leaves the ref unchanged
|
|
145 |
return ref |
|
146 |
}
|
|
147 |
if len(parts) == 2 { |
|
148 | 4 |
relativePath += "#" + parts[1] |
149 |
}
|
|
150 | 4 |
r, _ := NewRef(relativePath) |
151 | 4 |
return &r |
152 |
}
|
|
153 |
|
|
154 |
// relativeBase could be an ABSOLUTE file path or an ABSOLUTE URL
|
|
155 |
func normalizeFileRef(ref *Ref, relativeBase string) *Ref { |
|
156 |
// This is important for when the reference is pointing to the root schema
|
|
157 |
if ref.String() == "" { |
|
158 | 4 |
r, _ := NewRef(relativeBase) |
159 | 4 |
return &r |
160 |
}
|
|
161 |
|
|
162 | 4 |
s := normalizePaths(ref.String(), relativeBase) |
163 | 4 |
r, _ := NewRef(s) |
164 | 4 |
return &r |
165 |
}
|
|
166 |
|
|
167 |
// absPath returns the absolute path of a file
|
|
168 |
func absPath(fname string) (string, error) { |
|
169 |
if strings.HasPrefix(fname, "http") { |
|
170 | 4 |
return fname, nil |
171 |
}
|
|
172 |
if filepath.IsAbs(fname) { |
|
173 | 4 |
return fname, nil |
174 |
}
|
|
175 | 4 |
wd, err := os.Getwd() |
176 | 4 |
return normalizeAbsPath(filepath.Join(wd, fname)), err |
177 |
}
|
Read our documentation on viewing source code .