104 lines
3.2 KiB
Go
104 lines
3.2 KiB
Go
package openapi3
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/url"
|
|
"path/filepath"
|
|
)
|
|
|
|
// ReadFromURIFunc defines a function which reads the contents of a resource
|
|
// located at a URI.
|
|
type ReadFromURIFunc func(loader *Loader, url *url.URL) ([]byte, error)
|
|
|
|
// ErrURINotSupported indicates the ReadFromURIFunc does not know how to handle a
|
|
// given URI.
|
|
var ErrURINotSupported = errors.New("unsupported URI")
|
|
|
|
// ReadFromURIs returns a ReadFromURIFunc which tries to read a URI using the
|
|
// given reader functions, in the same order. If a reader function does not
|
|
// support the URI and returns ErrURINotSupported, the next function is checked
|
|
// until a match is found, or the URI is not supported by any.
|
|
func ReadFromURIs(readers ...ReadFromURIFunc) ReadFromURIFunc {
|
|
return func(loader *Loader, url *url.URL) ([]byte, error) {
|
|
for i := range readers {
|
|
buf, err := readers[i](loader, url)
|
|
if err == ErrURINotSupported {
|
|
continue
|
|
} else if err != nil {
|
|
return nil, err
|
|
}
|
|
return buf, nil
|
|
}
|
|
return nil, ErrURINotSupported
|
|
}
|
|
}
|
|
|
|
// DefaultReadFromURI returns a caching ReadFromURIFunc which can read remote
|
|
// HTTP URIs and local file URIs.
|
|
var DefaultReadFromURI = URIMapCache(ReadFromURIs(ReadFromHTTP(http.DefaultClient), ReadFromFile))
|
|
|
|
// ReadFromHTTP returns a ReadFromURIFunc which uses the given http.Client to
|
|
// read the contents from a remote HTTP URI. This client may be customized to
|
|
// implement timeouts, RFC 7234 caching, etc.
|
|
func ReadFromHTTP(cl *http.Client) ReadFromURIFunc {
|
|
return func(loader *Loader, location *url.URL) ([]byte, error) {
|
|
if location.Scheme == "" || location.Host == "" {
|
|
return nil, ErrURINotSupported
|
|
}
|
|
req, err := http.NewRequest("GET", location.String(), nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp, err := cl.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode > 399 {
|
|
return nil, fmt.Errorf("error loading %q: request returned status code %d", location.String(), resp.StatusCode)
|
|
}
|
|
return ioutil.ReadAll(resp.Body)
|
|
}
|
|
}
|
|
|
|
// ReadFromFile is a ReadFromURIFunc which reads local file URIs.
|
|
func ReadFromFile(loader *Loader, location *url.URL) ([]byte, error) {
|
|
if location.Host != "" {
|
|
return nil, ErrURINotSupported
|
|
}
|
|
if location.Scheme != "" && location.Scheme != "file" {
|
|
return nil, ErrURINotSupported
|
|
}
|
|
return ioutil.ReadFile(location.Path)
|
|
}
|
|
|
|
// URIMapCache returns a ReadFromURIFunc that caches the contents read from URI
|
|
// locations in a simple map. This cache implementation is suitable for
|
|
// short-lived processes such as command-line tools which process OpenAPI
|
|
// documents.
|
|
func URIMapCache(reader ReadFromURIFunc) ReadFromURIFunc {
|
|
cache := map[string][]byte{}
|
|
return func(loader *Loader, location *url.URL) (buf []byte, err error) {
|
|
if location.Scheme == "" || location.Scheme == "file" {
|
|
if !filepath.IsAbs(location.Path) {
|
|
// Do not cache relative file paths; this can cause trouble if
|
|
// the current working directory changes when processing
|
|
// multiple top-level documents.
|
|
return reader(loader, location)
|
|
}
|
|
}
|
|
uri := location.String()
|
|
var ok bool
|
|
if buf, ok = cache[uri]; ok {
|
|
return
|
|
}
|
|
if buf, err = reader(loader, location); err != nil {
|
|
return
|
|
}
|
|
cache[uri] = buf
|
|
return
|
|
}
|
|
}
|