0
Fork 0
mirror of https://github.com/willnorris/imageproxy.git synced 2025-03-11 02:19:14 -05:00

refactor error handling logic

this is mostly a noop for now, but will make it easier to migrate to
using httputil.ReverseProxy.
This commit is contained in:
Will Norris 2017-06-17 11:22:02 -04:00
parent 1dbc725ae3
commit 5f3c970a20
2 changed files with 31 additions and 19 deletions

21
data.go
View file

@ -36,15 +36,18 @@ const (
optScaleUp = "scaleUp" optScaleUp = "scaleUp"
) )
// urlError reports a malformed URL error. type requestError struct {
type urlError struct {
message string message string
url *url.URL status int
} }
func (e urlError) Error() string { func (e requestError) Error() string { return e.message }
return fmt.Sprintf("malformed URL %q: %s", e.url, e.message) func (e requestError) StatusCode() int { return e.status }
func urlError(msg string, u *url.URL) error {
return requestError{fmt.Sprintf("malformed URL %q: %s", u, msg), http.StatusBadRequest}
} }
func permissionError(msg string) error { return requestError{msg, http.StatusForbidden} }
// Options specifies transformations to be performed on the requested image. // Options specifies transformations to be performed on the requested image.
type Options struct { type Options struct {
@ -270,13 +273,13 @@ func NewRequest(r *http.Request, baseURL *url.URL) (*Request, error) {
// first segment should be options // first segment should be options
parts := strings.SplitN(path, "/", 2) parts := strings.SplitN(path, "/", 2)
if len(parts) != 2 { if len(parts) != 2 {
return nil, urlError{"too few path segments", r.URL} return nil, urlError("too few path segments", r.URL)
} }
var err error var err error
req.URL, err = parseURL(parts[1]) req.URL, err = parseURL(parts[1])
if err != nil { if err != nil {
return nil, urlError{fmt.Sprintf("unable to parse remote URL: %v", err), r.URL} return nil, urlError(fmt.Sprintf("unable to parse remote URL: %v", err), r.URL)
} }
req.Options = ParseOptions(parts[0]) req.Options = ParseOptions(parts[0])
@ -287,11 +290,11 @@ func NewRequest(r *http.Request, baseURL *url.URL) (*Request, error) {
} }
if !req.URL.IsAbs() { if !req.URL.IsAbs() {
return nil, urlError{"must provide absolute remote URL", r.URL} return nil, urlError("must provide absolute remote URL", r.URL)
} }
if req.URL.Scheme != "http" && req.URL.Scheme != "https" { if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
return nil, urlError{"remote URL must have http or https scheme", r.URL} return nil, urlError("remote URL must have http or https scheme", r.URL)
} }
// query string is always part of the remote URL // query string is always part of the remote URL

View file

@ -115,9 +115,7 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
func (p *Proxy) serveImage(w http.ResponseWriter, r *http.Request) { func (p *Proxy) serveImage(w http.ResponseWriter, r *http.Request) {
req, err := NewRequest(r, p.DefaultBaseURL) req, err := NewRequest(r, p.DefaultBaseURL)
if err != nil { if err != nil {
msg := fmt.Sprintf("invalid request URL: %v", err) p.writeError(w, err)
glog.Error(msg)
http.Error(w, msg, http.StatusBadRequest)
return return
} }
@ -125,16 +123,13 @@ func (p *Proxy) serveImage(w http.ResponseWriter, r *http.Request) {
req.Options.ScaleUp = p.ScaleUp req.Options.ScaleUp = p.ScaleUp
if err := p.allowed(req); err != nil { if err := p.allowed(req); err != nil {
glog.Error(err) p.writeError(w, err)
http.Error(w, err.Error(), http.StatusForbidden)
return return
} }
resp, err := p.Client.Get(req.String()) resp, err := p.Client.Get(req.String())
if err != nil { if err != nil {
msg := fmt.Sprintf("error fetching remote image: %v", err) p.writeError(w, err)
glog.Error(msg)
http.Error(w, msg, http.StatusInternalServerError)
return return
} }
defer resp.Body.Close() defer resp.Body.Close()
@ -154,6 +149,20 @@ func (p *Proxy) serveImage(w http.ResponseWriter, r *http.Request) {
io.Copy(w, resp.Body) io.Copy(w, resp.Body)
} }
// writerError writes err to the http response.
func (p *Proxy) writeError(w http.ResponseWriter, err error) {
type statusCoder interface {
StatusCode() int
}
glog.Error(err)
code := http.StatusBadGateway
if err, ok := err.(statusCoder); ok {
code = err.StatusCode()
}
http.Error(w, err.Error(), code)
}
// copyHeader copies header values from src to dst, adding to any existing // copyHeader copies header values from src to dst, adding to any existing
// values with the same header name. If keys is not empty, only those header // values with the same header name. If keys is not empty, only those header
// keys will be copied. // keys will be copied.
@ -176,7 +185,7 @@ func copyHeader(dst, src http.Header, keys ...string) {
// allowed. // allowed.
func (p *Proxy) allowed(r *Request) error { func (p *Proxy) allowed(r *Request) error {
if len(p.Referrers) > 0 && !validReferrer(p.Referrers, r.Original) { if len(p.Referrers) > 0 && !validReferrer(p.Referrers, r.Original) {
return fmt.Errorf("request does not contain an allowed referrer: %v", r) return permissionError(fmt.Sprintf("request does not contain an allowed referrer: %v", r))
} }
if len(p.Whitelist) == 0 && len(p.SignatureKey) == 0 { if len(p.Whitelist) == 0 && len(p.SignatureKey) == 0 {
@ -191,7 +200,7 @@ func (p *Proxy) allowed(r *Request) error {
return nil return nil
} }
return fmt.Errorf("request does not contain an allowed host or valid signature: %v", r) return permissionError(fmt.Sprintf("request does not contain an allowed host or valid signature: %v", r))
} }
// validHost returns whether the host in u matches one of hosts. // validHost returns whether the host in u matches one of hosts.