errors.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. package imagefetcher
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "net/http"
  7. "github.com/imgproxy/imgproxy/v3/ierrors"
  8. "github.com/imgproxy/imgproxy/v3/security"
  9. )
  10. const msgSourceImageIsUnreachable = "Source image is unreachable"
  11. type (
  12. ImageRequestError struct{ error }
  13. ImageRequstSchemeError string
  14. ImagePartialResponseError string
  15. ImageResponseStatusError string
  16. ImageTooManyRedirectsError string
  17. ImageRequestCanceledError struct{ error }
  18. ImageRequestTimeoutError struct{ error }
  19. NotModifiedError struct {
  20. headers http.Header
  21. }
  22. httpError interface {
  23. Timeout() bool
  24. }
  25. )
  26. func newImageRequestError(err error) error {
  27. return ierrors.Wrap(
  28. ImageRequestError{err},
  29. 1,
  30. ierrors.WithStatusCode(http.StatusNotFound),
  31. ierrors.WithPublicMessage(msgSourceImageIsUnreachable),
  32. ierrors.WithShouldReport(false),
  33. )
  34. }
  35. func (e ImageRequestError) Unwrap() error {
  36. return e.error
  37. }
  38. func newImageRequstSchemeError(scheme string) error {
  39. return ierrors.Wrap(
  40. ImageRequstSchemeError(fmt.Sprintf("Unknown scheme: %s", scheme)),
  41. 1,
  42. ierrors.WithStatusCode(http.StatusNotFound),
  43. ierrors.WithPublicMessage(msgSourceImageIsUnreachable),
  44. ierrors.WithShouldReport(false),
  45. )
  46. }
  47. func (e ImageRequstSchemeError) Error() string { return string(e) }
  48. func newImagePartialResponseError(msg string) error {
  49. return ierrors.Wrap(
  50. ImagePartialResponseError(msg),
  51. 1,
  52. ierrors.WithStatusCode(http.StatusNotFound),
  53. ierrors.WithPublicMessage(msgSourceImageIsUnreachable),
  54. ierrors.WithShouldReport(false),
  55. )
  56. }
  57. func (e ImagePartialResponseError) Error() string { return string(e) }
  58. func newImageResponseStatusError(status int, body string) error {
  59. var msg string
  60. if len(body) > 0 {
  61. msg = fmt.Sprintf("Status: %d; %s", status, body)
  62. } else {
  63. msg = fmt.Sprintf("Status: %d", status)
  64. }
  65. statusCode := 404
  66. if status >= 500 {
  67. statusCode = 500
  68. }
  69. return ierrors.Wrap(
  70. ImageResponseStatusError(msg),
  71. 1,
  72. ierrors.WithStatusCode(statusCode),
  73. ierrors.WithPublicMessage(msgSourceImageIsUnreachable),
  74. ierrors.WithShouldReport(false),
  75. )
  76. }
  77. func (e ImageResponseStatusError) Error() string { return string(e) }
  78. func newImageTooManyRedirectsError(n int) error {
  79. return ierrors.Wrap(
  80. ImageTooManyRedirectsError(fmt.Sprintf("Stopped after %d redirects", n)),
  81. 1,
  82. ierrors.WithStatusCode(http.StatusNotFound),
  83. ierrors.WithPublicMessage(msgSourceImageIsUnreachable),
  84. ierrors.WithShouldReport(false),
  85. )
  86. }
  87. func (e ImageTooManyRedirectsError) Error() string { return string(e) }
  88. func newImageRequestCanceledError(err error) error {
  89. return ierrors.Wrap(
  90. ImageRequestCanceledError{err},
  91. 2,
  92. ierrors.WithStatusCode(499),
  93. ierrors.WithPublicMessage(msgSourceImageIsUnreachable),
  94. ierrors.WithShouldReport(false),
  95. )
  96. }
  97. func (e ImageRequestCanceledError) Error() string {
  98. return fmt.Sprintf("The image request is cancelled: %s", e.error)
  99. }
  100. func (e ImageRequestCanceledError) Unwrap() error { return e.error }
  101. func newImageRequestTimeoutError(err error) error {
  102. return ierrors.Wrap(
  103. ImageRequestTimeoutError{err},
  104. 2,
  105. ierrors.WithStatusCode(http.StatusGatewayTimeout),
  106. ierrors.WithPublicMessage(msgSourceImageIsUnreachable),
  107. ierrors.WithShouldReport(false),
  108. )
  109. }
  110. func (e ImageRequestTimeoutError) Error() string {
  111. return fmt.Sprintf("The image request timed out: %s", e.error)
  112. }
  113. func (e ImageRequestTimeoutError) Unwrap() error { return e.error }
  114. func newNotModifiedError(headers http.Header) error {
  115. return ierrors.Wrap(
  116. NotModifiedError{headers},
  117. 1,
  118. ierrors.WithStatusCode(http.StatusNotModified),
  119. ierrors.WithPublicMessage("Not modified"),
  120. ierrors.WithShouldReport(false),
  121. )
  122. }
  123. func (e NotModifiedError) Error() string { return "Not modified" }
  124. func (e NotModifiedError) Headers() http.Header {
  125. return e.headers
  126. }
  127. // NOTE: make private when we remove download functions from imagedata package
  128. func WrapError(err error) error {
  129. isTimeout := false
  130. var secArrdErr security.SourceAddressError
  131. switch {
  132. case errors.Is(err, context.DeadlineExceeded):
  133. isTimeout = true
  134. case errors.Is(err, context.Canceled):
  135. return newImageRequestCanceledError(err)
  136. case errors.As(err, &secArrdErr):
  137. return ierrors.Wrap(
  138. err,
  139. 1,
  140. ierrors.WithStatusCode(404),
  141. ierrors.WithPublicMessage(msgSourceImageIsUnreachable),
  142. ierrors.WithShouldReport(false),
  143. )
  144. default:
  145. if httpErr, ok := err.(httpError); ok {
  146. isTimeout = httpErr.Timeout()
  147. }
  148. }
  149. if isTimeout {
  150. return newImageRequestTimeoutError(err)
  151. }
  152. return ierrors.Wrap(err, 1)
  153. }