errors.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. package imagedata
  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. type (
  11. ImageRequestError struct{ error }
  12. ImageRequstSchemeError string
  13. ImagePartialResponseError string
  14. ImageResponseStatusError string
  15. ImageTooManyRedirectsError string
  16. ImageRequestCanceledError struct{ error }
  17. ImageRequestTimeoutError struct{ error }
  18. NotModifiedError struct {
  19. headers map[string]string
  20. }
  21. httpError interface {
  22. Timeout() bool
  23. }
  24. )
  25. func newImageRequestError(err error) error {
  26. return ierrors.Wrap(
  27. ImageRequestError{err},
  28. 1,
  29. ierrors.WithStatusCode(http.StatusNotFound),
  30. ierrors.WithPublicMessage(msgSourceImageIsUnreachable),
  31. ierrors.WithShouldReport(false),
  32. )
  33. }
  34. func (e ImageRequestError) Unwrap() error {
  35. return e.error
  36. }
  37. func newImageRequstSchemeError(scheme string) error {
  38. return ierrors.Wrap(
  39. ImageRequstSchemeError(fmt.Sprintf("Unknown scheme: %s", scheme)),
  40. 1,
  41. ierrors.WithStatusCode(http.StatusNotFound),
  42. ierrors.WithPublicMessage(msgSourceImageIsUnreachable),
  43. ierrors.WithShouldReport(false),
  44. )
  45. }
  46. func (e ImageRequstSchemeError) Error() string { return string(e) }
  47. func newImagePartialResponseError(msg string) error {
  48. return ierrors.Wrap(
  49. ImagePartialResponseError(msg),
  50. 1,
  51. ierrors.WithStatusCode(http.StatusNotFound),
  52. ierrors.WithPublicMessage(msgSourceImageIsUnreachable),
  53. ierrors.WithShouldReport(false),
  54. )
  55. }
  56. func (e ImagePartialResponseError) Error() string { return string(e) }
  57. func newImageResponseStatusError(status int, body string) error {
  58. var msg string
  59. if len(body) > 0 {
  60. msg = fmt.Sprintf("Status: %d; %s", status, body)
  61. } else {
  62. msg = fmt.Sprintf("Status: %d", status)
  63. }
  64. statusCode := 404
  65. if status >= 400 && status < 500 {
  66. statusCode = status
  67. } else if status >= 500 {
  68. statusCode = http.StatusBadGateway
  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 map[string]string) 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() map[string]string {
  126. return e.headers
  127. }
  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. }