Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions gateway/backend_car.go
Original file line number Diff line number Diff line change
Expand Up @@ -1125,6 +1125,12 @@ func isRetryableError(err error) (bool, error) {
// blockstoreErrToGatewayErr translates underlying blockstore error into one that gateway code will return as HTTP 502 or 504
// it also makes sure Retry-After hint from remote blockstore will be passed to HTTP client, if present.
func blockstoreErrToGatewayErr(err error) error {
// Check for blocked content error first
var blocked *ErrorContentBlocked
if errors.As(err, &blocked) {
return err
}

if errors.Is(err, &ErrorStatusCode{}) ||
errors.Is(err, &ErrorRetryAfter{}) {
// already correct error
Expand Down
48 changes: 46 additions & 2 deletions gateway/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ type ErrorRetryAfter struct {
RetryAfter time.Duration
}

// ErrorContentBlocked represents an error when content is blocked due to filtering
type ErrorContentBlocked struct {
Err error
StatusCode int
}

func NewErrorRetryAfter(err error, retryAfter time.Duration) *ErrorRetryAfter {
if err == nil {
err = ErrServiceUnavailable
Expand All @@ -49,6 +55,18 @@ func NewErrorRetryAfter(err error, retryAfter time.Duration) *ErrorRetryAfter {
}
}

func NewErrorContentBlocked(err error, statusCode int) *ErrorContentBlocked {
// Will default to Error:451 if no status code is provided
if statusCode != http.StatusGone && statusCode != http.StatusUnavailableForLegalReasons {
statusCode = http.StatusUnavailableForLegalReasons // 451 by default
}
Comment on lines +59 to +62
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: legal one should not be the default, switch to 410

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


return &ErrorContentBlocked{
Err: err,
StatusCode: statusCode,
}
}

func (e *ErrorRetryAfter) Error() string {
var text string
if e.Err != nil {
Expand All @@ -73,6 +91,26 @@ func (e *ErrorRetryAfter) Is(err error) bool {
}
}

func (e *ErrorContentBlocked) Error() string {
if e.Err != nil {
return fmt.Sprintf("content blocked: %s", e.Err.Error())
}
return "content blocked and cannot be provided"
}

func (e *ErrorContentBlocked) Unwrap() error {
return e.Err
}

func (e *ErrorContentBlocked) Is(err error) bool {
switch err.(type) {
case *ErrorContentBlocked:
return true
default:
return false
}
}

// RetryAfterHeader returns the [Retry-After] header value as a string, representing the number
// of seconds to wait before making a new request, rounded to the nearest second.
// This function follows the [Retry-After] header definition as specified in RFC 9110.
Expand Down Expand Up @@ -187,7 +225,12 @@ func webError(w http.ResponseWriter, r *http.Request, c *Config, err error, defa
case errors.Is(err, &cid.ErrInvalidCid{}):
code = http.StatusBadRequest
case isErrContentBlocked(err):
code = http.StatusGone
var blocked *ErrorContentBlocked
if errors.As(err, &blocked) {
code = blocked.StatusCode
} else {
code = http.StatusUnavailableForLegalReasons // Default to 451
}
case isErrNotFound(err):
code = http.StatusNotFound
case errors.Is(err, context.DeadlineExceeded):
Expand Down Expand Up @@ -255,5 +298,6 @@ func isErrNotFound(err error) bool {
func isErrContentBlocked(err error) bool {
// TODO: we match error message to avoid pulling nopfs as a dependency
// Ref. https://github.com/ipfs-shipyard/nopfs/blob/cde3b5ba964c13e977f4a95f3bd8ca7d7710fbda/status.go#L87-L89
return strings.Contains(err.Error(), "blocked and cannot be provided")
var blocked *ErrorContentBlocked
return errors.As(err, &blocked)
Comment on lines 298 to +302
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now nothing will return ErrorContentBlocked type, so isErrContentBlocked will always return false and break content blocking response code in projects that use boxo/gateway, like Kubo.

What is the plan for making this work?

We don't want to depend on https://github.com/ipfs-shipyard/nopfs as noted in the comment here. Perhaps we want to keep the string.Contains and check it in addition to the type check here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}
Loading