errors.go 4.7 KB

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