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 4
	if err != nil {
34 0
		debugLog("normalize absolute path failed: %s", err)
35 0
		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 4
	if path.IsAbs(refURL.Path) || filepath.IsAbs(refPath) {
48
		// refPath is actually absolute
49 4
		if refURL.Host != "" {
50 4
			return refPath
51
		}
52 4
		parts := strings.Split(refPath, "#")
53 4
		result := filepath.FromSlash(parts[0])
54 4
		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 4
	if !strings.HasPrefix(refPath, "#") {
63
		// combining paths
64 4
		if baseURL.Host != "" {
65 4
			baseURL.Path = path.Join(path.Dir(baseURL.Path), refURL.Path)
66 4
		} 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 0
	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 0
	if len(u.Scheme) <= 1 {
93
		// drive letter got caught as URI scheme
94 0
		return false
95
	}
96 0
	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 4
	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 4
	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 4
	if isAbs(relativeBaseURL) {
122
		// other absolute URL get unchanged (i.e. with a non-empty scheme)
123 0
		return ref
124
	}
125

126
	// for relative file URIs:
127 4
	originalRelativeBaseURL, _ := url.Parse(originalRelativeBase)
128 4
	originalRelativeBaseURL.Fragment = ""
129 4
	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 4
	if err != nil {
143
		// there is no common ancestor (e.g. different drives on windows)
144
		// leaves the ref unchanged
145 0
		return ref
146
	}
147 4
	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 4
	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 4
	if strings.HasPrefix(fname, "http") {
170 4
		return fname, nil
171
	}
172 4
	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 .

Loading