// Copyright 2025 Envoy Project Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

package envoy.extensions.filters.http.local_ratelimit.v3;

import "envoy/config/core/v3/base.proto";
import "envoy/config/route/v3/route_components.proto";
import "envoy/extensions/common/ratelimit/v3/ratelimit.proto";
import "envoy/type/v3/http_status.proto";
import "envoy/type/v3/token_bucket.proto";

import "google/protobuf/wrappers.proto";

import "udpa/annotations/status.proto";
import "validate/validate.proto";

option java_package = "io.envoyproxy.envoy.extensions.filters.http.local_ratelimit.v3";
option java_outer_classname = "LocalRateLimitProto";
option java_multiple_files = true;
option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/local_ratelimit/v3;local_ratelimitv3";
option (udpa.annotations.file_status).package_version_status = ACTIVE;

// [#protodoc-title: Local Rate limit]
// Local Rate limit :ref:`configuration overview <config_http_filters_local_rate_limit>`.
// [#extension: envoy.filters.http.local_ratelimit]

// [#next-free-field: 19]
message LocalRateLimit {
  // The human readable prefix to use when emitting stats.
  string stat_prefix = 1 [(validate.rules).string = {min_len: 1}];

  // This field allows for a custom HTTP response status code to the downstream client when
  // the request has been rate limited.
  // Defaults to 429 (TooManyRequests).
  //
  // .. note::
  //   If this is set to < 400, 429 will be used instead.
  type.v3.HttpStatus status = 2;

  // The token bucket configuration to use for rate limiting requests that are processed by this
  // filter. Each request processed by the filter consumes a single token. If the token is available,
  // the request will be allowed. If no tokens are available, the request will receive the configured
  // rate limit status.
  //
  // .. note::
  //   It's fine for the token bucket to be unset for the global configuration since the rate limit
  //   can be applied at a the virtual host or route level. Thus, the token bucket must be set
  //   for the per route configuration otherwise the config will be rejected.
  //
  // .. note::
  //   When using per route configuration, the bucket becomes unique to that route.
  //
  // .. note::
  //   In the current implementation the token bucket's :ref:`fill_interval
  //   <envoy_v3_api_field_type.v3.TokenBucket.fill_interval>` must be >= 50ms to avoid too aggressive
  //   refills.
  type.v3.TokenBucket token_bucket = 3;

  // If set, this will enable -- but not necessarily enforce -- the rate limit for the given
  // fraction of requests.
  // Defaults to 0% of requests for safety.
  config.core.v3.RuntimeFractionalPercent filter_enabled = 4;

  // If set, this will enforce the rate limit decisions for the given fraction of requests.
  //
  // Note: this only applies to the fraction of enabled requests.
  //
  // Defaults to 0% of requests for safety.
  config.core.v3.RuntimeFractionalPercent filter_enforced = 5;

  // Specifies a list of HTTP headers that should be added to each request that
  // has been rate limited and is also forwarded upstream. This can only occur when the
  // filter is enabled but not enforced.
  repeated config.core.v3.HeaderValueOption request_headers_to_add_when_not_enforced = 10
      [(validate.rules).repeated = {max_items: 10}];

  // Specifies a list of HTTP headers that should be added to each response for requests that
  // have been rate limited. This occurs when the filter is enabled and fully enforced.
  repeated config.core.v3.HeaderValueOption response_headers_to_add = 6
      [(validate.rules).repeated = {max_items: 10}];

  // The rate limit descriptor list to use in the local rate limit to override
  // on. The rate limit descriptor is selected by the first full match from the
  // request descriptors.
  //
  // Example on how to use :ref:`this <config_http_filters_local_rate_limit_descriptors>`.
  //
  // .. note::
  //
  //   In the current implementation the descriptor's token bucket :ref:`fill_interval
  //   <envoy_v3_api_field_type.v3.TokenBucket.fill_interval>` must be a multiple
  //   global :ref:`token bucket's<envoy_v3_api_field_extensions.filters.http.local_ratelimit.v3.LocalRateLimit.token_bucket>` fill interval.
  //
  //   The descriptors must match verbatim for rate limiting to apply. There is no partial
  //   match by a subset of descriptor entries in the current implementation.
  repeated common.ratelimit.v3.LocalRateLimitDescriptor descriptors = 8;

  // Specifies the rate limit configurations to be applied with the same
  // stage number. If not set, the default stage number is 0.
  //
  // .. note::
  //
  //  The filter supports a range of 0 - 10 inclusively for stage numbers.
  uint32 stage = 9 [(validate.rules).uint32 = {lte: 10}];

  // Specifies the scope of the rate limiter's token bucket.
  // If set to false, the token bucket is shared across all worker threads,
  // thus the rate limits are applied per Envoy process.
  // If set to true, a token bucket is allocated for each connection.
  // Thus the rate limits are applied per connection thereby allowing
  // one to rate limit requests on a per connection basis.
  // If unspecified, the default value is false.
  bool local_rate_limit_per_downstream_connection = 11;

  // Enables the local cluster level rate limiting, iff this is set explicitly. For example,
  // given an Envoy gateway that contains N Envoy instances and a rate limit rule X tokens
  // per second. If this is set, the total rate limit of whole gateway will always be X tokens
  // per second regardless of how N changes. If this is not set, the total rate limit of whole
  // gateway will be N * X tokens per second.
  //
  // .. note::
  //   This should never be set if the ``local_rate_limit_per_downstream_connection`` is set to
  //   true. Because if per connection rate limiting is enabled, we assume that the token buckets
  //   should never be shared across Envoy instances.
  //
  // .. note::
  //   This only works when the :ref:`local cluster name
  //   <envoy_v3_api_field_config.bootstrap.v3.ClusterManager.local_cluster_name>` is set and
  //   the related cluster is defined in the bootstrap configuration.
  common.ratelimit.v3.LocalClusterRateLimit local_cluster_rate_limit = 16;

  // Defines the standard version to use for X-RateLimit headers emitted by the filter.
  //
  // * ``X-RateLimit-Limit`` - indicates the request-quota associated to the
  //   client in the current time-window followed by the description of the
  //   quota policy.
  // * ``X-RateLimit-Remaining`` - indicates the remaining requests in the
  //   current time-window.
  // * ``X-RateLimit-Reset`` - indicates the number of seconds until reset of
  //   the current time-window.
  //
  // In case rate limiting policy specifies more then one time window, the values
  // above represent the window that is closest to reaching its limit.
  //
  // For more information about the headers specification see selected version of
  // the `draft RFC <https://tools.ietf.org/id/draft-polli-ratelimit-headers-03.html>`_.
  //
  // Disabled by default.
  common.ratelimit.v3.XRateLimitHeadersRFCVersion enable_x_ratelimit_headers = 12
      [(validate.rules).enum = {defined_only: true}];

  // Specifies if the local rate limit filter should include the virtual host rate limits.
  common.ratelimit.v3.VhRateLimitsOptions vh_rate_limits = 13
      [(validate.rules).enum = {defined_only: true}];

  // Specifies if default token bucket should be always consumed.
  // If set to false, default token bucket will only be consumed when there is
  // no matching descriptor. If set to true, default token bucket will always
  // be consumed. Default is true.
  google.protobuf.BoolValue always_consume_default_token_bucket = 14;

  // Specifies whether a ``RESOURCE_EXHAUSTED`` gRPC code must be returned instead
  // of the default ``UNAVAILABLE`` gRPC code for a rate limited gRPC call. The
  // HTTP code will be 200 for a gRPC response.
  bool rate_limited_as_resource_exhausted = 15;

  // Rate limit configuration that is used to generate a list of descriptor entries based on
  // the request context. The generated entries will be used to find one or multiple matched rate
  // limit rule from the ``descriptors``.
  // If this is set, then
  // :ref:`VirtualHost.rate_limits<envoy_v3_api_field_config.route.v3.VirtualHost.rate_limits>` or
  // :ref:`RouteAction.rate_limits<envoy_v3_api_field_config.route.v3.RouteAction.rate_limits>` fields
  // will be ignored.
  //
  // .. note::
  //   Not all configuration fields of
  //   :ref:`rate limit config <envoy_v3_api_msg_config.route.v3.RateLimit>` is supported at here.
  //   Following fields are not supported:
  //
  //   1. :ref:`rate limit stage <envoy_v3_api_field_config.route.v3.RateLimit.stage>`.
  //   2. :ref:`dynamic metadata <envoy_v3_api_field_config.route.v3.RateLimit.Action.dynamic_metadata>`.
  //   3. :ref:`disable_key <envoy_v3_api_field_config.route.v3.RateLimit.disable_key>`.
  //   4. :ref:`override limit <envoy_v3_api_field_config.route.v3.RateLimit.limit>`.
  repeated config.route.v3.RateLimit rate_limits = 17;

  // Specifies the max dynamic descriptors kept in the cache for a particular wildcard descriptor
  // configured in the global :ref:`descriptors<envoy_v3_api_field_extensions.filters.http.local_ratelimit.v3.LocalRateLimit.descriptors>`.
  // Wildcard descriptor means descriptor has one or more entries with just key and value omitted. For example if user has configured two descriptors
  // with blank value entries, then max dynamic descriptors stored in the LRU cache will be 2 * max_dynamic_descriptors.
  // Actual number of dynamic descriptors will depend on the cardinality of unique values received from the http request for the omitted
  // values.
  // Minimum is 1. Default is 20.
  google.protobuf.UInt32Value max_dynamic_descriptors = 18 [(validate.rules).uint32 = {gte: 1}];
}
