@@ -34,6 +34,13 @@ type routeMux struct {
3434 varsUpdater varsf
3535}
3636
37+ type srv struct {
38+ schemes []string
39+ host , base string
40+ server * openapi3.Server
41+ varsUpdater varsf
42+ }
43+
3744var singleVariableMatcher = regexp .MustCompile (`^\{([^{}]+)\}$` )
3845
3946// TODO: Handle/HandlerFunc + ServeHTTP (When there is a match, the route variables can be retrieved calling mux.Vars(request))
@@ -42,78 +49,22 @@ var singleVariableMatcher = regexp.MustCompile(`^\{([^{}]+)\}$`)
4249// Assumes spec is .Validate()d
4350// Note that a variable for the port number MUST have a default value and only this value will match as the port (see issue #367).
4451func NewRouter (doc * openapi3.T ) (routers.Router , error ) {
45- type srv struct {
46- schemes []string
47- host , base string
48- server * openapi3.Server
49- varsUpdater varsf
52+ servers , err := makeServers (doc .Servers )
53+ if err != nil {
54+ return nil , err
5055 }
51- servers := make ([]srv , 0 , len (doc .Servers ))
52- for _ , server := range doc .Servers {
53- serverURL := server .URL
54- if submatch := singleVariableMatcher .FindStringSubmatch (serverURL ); submatch != nil {
55- sVar := submatch [1 ]
56- sVal := server .Variables [sVar ].Default
57- serverURL = strings .ReplaceAll (serverURL , "{" + sVar + "}" , sVal )
58- var varsUpdater varsf
59- if lhs := strings .TrimSuffix (serverURL , server .Variables [sVar ].Default ); lhs != "" {
60- varsUpdater = func (vars map [string ]string ) { vars [sVar ] = lhs }
61- }
62- servers = append (servers , srv {
63- base : server .Variables [sVar ].Default ,
64- server : server ,
65- varsUpdater : varsUpdater ,
66- })
67- continue
68- }
69-
70- var schemes []string
71- if strings .Contains (serverURL , "://" ) {
72- scheme0 := strings .Split (serverURL , "://" )[0 ]
73- schemes = permutePart (scheme0 , server )
74- serverURL = strings .Replace (serverURL , scheme0 + "://" , schemes [0 ]+ "://" , 1 )
75- }
7656
77- // If a variable represents the port "http://domain.tld:{port}/bla"
78- // then url.Parse() cannot parse "http://domain.tld:`bEncode({port})`/bla"
79- // and mux is not able to set the {port} variable
80- // So we just use the default value for this variable.
81- // See https://github.com/getkin/kin-openapi/issues/367
82- var varsUpdater varsf
83- if lhs := strings .Index (serverURL , ":{" ); lhs > 0 {
84- rest := serverURL [lhs + len (":{" ):]
85- rhs := strings .Index (rest , "}" )
86- portVariable := rest [:rhs ]
87- portValue := server .Variables [portVariable ].Default
88- serverURL = strings .ReplaceAll (serverURL , "{" + portVariable + "}" , portValue )
89- varsUpdater = func (vars map [string ]string ) {
90- vars [portVariable ] = portValue
91- }
92- }
93-
94- u , err := url .Parse (bEncode (serverURL ))
95- if err != nil {
96- return nil , err
97- }
98- path := bDecode (u .EscapedPath ())
99- if len (path ) > 0 && path [len (path )- 1 ] == '/' {
100- path = path [:len (path )- 1 ]
101- }
102- servers = append (servers , srv {
103- host : bDecode (u .Host ), //u.Hostname()?
104- base : path ,
105- schemes : schemes , // scheme: []string{scheme0}, TODO: https://github.com/gorilla/mux/issues/624
106- server : server ,
107- varsUpdater : varsUpdater ,
108- })
109- }
110- if len (servers ) == 0 {
111- servers = append (servers , srv {})
112- }
11357 muxRouter := mux .NewRouter ().UseEncodedPath ()
11458 r := & Router {}
11559 for _ , path := range orderedPaths (doc .Paths ) {
60+ servers := servers
61+
11662 pathItem := doc .Paths [path ]
63+ if len (pathItem .Servers ) > 0 {
64+ if servers , err = makeServers (pathItem .Servers ); err != nil {
65+ return nil , err
66+ }
67+ }
11768
11869 operations := pathItem .Operations ()
11970 methods := make ([]string , 0 , len (operations ))
@@ -177,6 +128,73 @@ func (r *Router) FindRoute(req *http.Request) (*routers.Route, map[string]string
177128 return nil , nil , routers .ErrPathNotFound
178129}
179130
131+ func makeServers (in openapi3.Servers ) ([]srv , error ) {
132+ servers := make ([]srv , 0 , len (in ))
133+ for _ , server := range in {
134+ serverURL := server .URL
135+ if submatch := singleVariableMatcher .FindStringSubmatch (serverURL ); submatch != nil {
136+ sVar := submatch [1 ]
137+ sVal := server .Variables [sVar ].Default
138+ serverURL = strings .ReplaceAll (serverURL , "{" + sVar + "}" , sVal )
139+ var varsUpdater varsf
140+ if lhs := strings .TrimSuffix (serverURL , server .Variables [sVar ].Default ); lhs != "" {
141+ varsUpdater = func (vars map [string ]string ) { vars [sVar ] = lhs }
142+ }
143+ servers = append (servers , srv {
144+ base : server .Variables [sVar ].Default ,
145+ server : server ,
146+ varsUpdater : varsUpdater ,
147+ })
148+ continue
149+ }
150+
151+ var schemes []string
152+ if strings .Contains (serverURL , "://" ) {
153+ scheme0 := strings .Split (serverURL , "://" )[0 ]
154+ schemes = permutePart (scheme0 , server )
155+ serverURL = strings .Replace (serverURL , scheme0 + "://" , schemes [0 ]+ "://" , 1 )
156+ }
157+
158+ // If a variable represents the port "http://domain.tld:{port}/bla"
159+ // then url.Parse() cannot parse "http://domain.tld:`bEncode({port})`/bla"
160+ // and mux is not able to set the {port} variable
161+ // So we just use the default value for this variable.
162+ // See https://github.com/getkin/kin-openapi/issues/367
163+ var varsUpdater varsf
164+ if lhs := strings .Index (serverURL , ":{" ); lhs > 0 {
165+ rest := serverURL [lhs + len (":{" ):]
166+ rhs := strings .Index (rest , "}" )
167+ portVariable := rest [:rhs ]
168+ portValue := server .Variables [portVariable ].Default
169+ serverURL = strings .ReplaceAll (serverURL , "{" + portVariable + "}" , portValue )
170+ varsUpdater = func (vars map [string ]string ) {
171+ vars [portVariable ] = portValue
172+ }
173+ }
174+
175+ u , err := url .Parse (bEncode (serverURL ))
176+ if err != nil {
177+ return nil , err
178+ }
179+ path := bDecode (u .EscapedPath ())
180+ if len (path ) > 0 && path [len (path )- 1 ] == '/' {
181+ path = path [:len (path )- 1 ]
182+ }
183+ servers = append (servers , srv {
184+ host : bDecode (u .Host ), //u.Hostname()?
185+ base : path ,
186+ schemes : schemes , // scheme: []string{scheme0}, TODO: https://github.com/gorilla/mux/issues/624
187+ server : server ,
188+ varsUpdater : varsUpdater ,
189+ })
190+ }
191+ if len (servers ) == 0 {
192+ servers = append (servers , srv {})
193+ }
194+
195+ return servers , nil
196+ }
197+
180198func orderedPaths (paths map [string ]* openapi3.PathItem ) []string {
181199 // https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#pathsObject
182200 // When matching URLs, concrete (non-templated) paths would be matched
0 commit comments