diff --git a/internal/ratelimit/handler.go b/internal/ratelimit/handler.go index dec2a69dee..8db61ccfb8 100644 --- a/internal/ratelimit/handler.go +++ b/internal/ratelimit/handler.go @@ -11,6 +11,7 @@ import ( "regexp" "github.com/hashicorp/boundary/globals" + "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/event" "github.com/hashicorp/boundary/internal/types/action" "github.com/hashicorp/boundary/internal/types/resource" @@ -155,6 +156,17 @@ func Handler(ctx context.Context, l *rate.Limiter, next http.Handler) http.Handl return } + l.SetUsageHeader(quota, rw.Header()) + if err := l.SetPolicyHeader(res, a, rw.Header()); err != nil { + // Wrap error to emit an error event. An error here would be + // unexpected, since the only possible error would be + // ErrLimitPolicyNotFound which would have been returned by Allow + // and handled above. And even that would be unexpected, given how + // the limiter is initialized and the checks done by + // extractResourceAction. + errors.Wrap(ctx, err, op) + } + if !allowed { rw.Header().Add("Retry-After", fmt.Sprintf("%.0f", quota.ResetsIn().Seconds())) rw.WriteHeader(http.StatusTooManyRequests) diff --git a/internal/ratelimit/handler_test.go b/internal/ratelimit/handler_test.go index 013cabf1cf..969cb39790 100644 --- a/internal/ratelimit/handler_test.go +++ b/internal/ratelimit/handler_test.go @@ -73,7 +73,10 @@ func TestHandler(t *testing.T) { "127.0.0.1", "authtoken", http.StatusOK, - http.Header{}, + http.Header{ + "RateLimit-Policy": []string{`10;w=60;comment="total", 10;w=60;comment="ip-address", 10;w=60;comment="auth-token"`}, + "RateLimit": []string{`limit=10, remaining=9, reset=59`}, + }, }, { "AllowedCreate", @@ -115,7 +118,10 @@ func TestHandler(t *testing.T) { "127.0.0.1", "authtoken", http.StatusOK, - http.Header{}, + http.Header{ + "RateLimit-Policy": []string{`10;w=60;comment="total", 10;w=60;comment="ip-address", 10;w=60;comment="auth-token"`}, + "RateLimit": []string{`limit=10, remaining=9, reset=59`}, + }, }, { "AllowedRead", @@ -157,7 +163,10 @@ func TestHandler(t *testing.T) { "127.0.0.1", "authtoken", http.StatusOK, - http.Header{}, + http.Header{ + "RateLimit-Policy": []string{`10;w=60;comment="total", 10;w=60;comment="ip-address", 10;w=60;comment="auth-token"`}, + "RateLimit": []string{`limit=10, remaining=9, reset=59`}, + }, }, { "AllowedUpdate", @@ -199,7 +208,10 @@ func TestHandler(t *testing.T) { "127.0.0.1", "authtoken", http.StatusOK, - http.Header{}, + http.Header{ + "RateLimit-Policy": []string{`10;w=60;comment="total", 10;w=60;comment="ip-address", 10;w=60;comment="auth-token"`}, + "RateLimit": []string{`limit=10, remaining=9, reset=59`}, + }, }, { "AllowedDelete", @@ -241,7 +253,10 @@ func TestHandler(t *testing.T) { "127.0.0.1", "authtoken", http.StatusOK, - http.Header{}, + http.Header{ + "RateLimit-Policy": []string{`10;w=60;comment="total", 10;w=60;comment="ip-address", 10;w=60;comment="auth-token"`}, + "RateLimit": []string{`limit=10, remaining=9, reset=59`}, + }, }, { "AllowedAuthorizeSession", @@ -283,7 +298,10 @@ func TestHandler(t *testing.T) { "127.0.0.1", "authtoken", http.StatusOK, - http.Header{}, + http.Header{ + "RateLimit-Policy": []string{`10;w=60;comment="total", 10;w=60;comment="ip-address", 10;w=60;comment="auth-token"`}, + "RateLimit": []string{`limit=10, remaining=9, reset=59`}, + }, }, { "Limited", @@ -334,7 +352,9 @@ func TestHandler(t *testing.T) { "authtoken", http.StatusTooManyRequests, http.Header{ - "Retry-After": []string{"60"}, + "Retry-After": []string{"60"}, + "RateLimit-Policy": []string{`2;w=60;comment="total", 2;w=60;comment="ip-address", 2;w=60;comment="auth-token"`}, + "RateLimit": []string{`limit=2, remaining=0, reset=59`}, }, }, {