// 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.file_system_buffer.v3;

import "envoy/extensions/common/async_files/v3/async_file_manager.proto";

import "google/protobuf/wrappers.proto";

import "xds/annotations/v3/status.proto";

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

option java_package = "io.envoyproxy.envoy.extensions.filters.http.file_system_buffer.v3";
option java_outer_classname = "FileSystemBufferProto";
option java_multiple_files = true;
option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/file_system_buffer/v3;file_system_bufferv3";
option (udpa.annotations.file_status).package_version_status = ACTIVE;
option (xds.annotations.v3.file_status).work_in_progress = true;

// [#protodoc-title: FileSystemBufferFilterConfig]
// [#extension: envoy.filters.http.file_system_buffer]

// The behavior of the filter for a stream.
// [#next-free-field: 6]
message BufferBehavior {
  message StreamWhenPossible {
  }

  message Bypass {
  }

  message InjectContentLengthIfNecessary {
  }

  message FullyBufferAndAlwaysInjectContentLength {
  }

  message FullyBuffer {
  }

  oneof behavior {
    option (validate.required) = true;

    // Don't inject ``content-length`` header.
    // Output immediately, buffer only if output is slower than input.
    StreamWhenPossible stream_when_possible = 1;

    // Never buffer, do nothing.
    Bypass bypass = 2;

    // If ``content-length`` is not present, buffer the entire input,
    // inject ``content-length`` header, then output.
    // If ``content-length`` is already present, act like ``stream_when_possible``.
    InjectContentLengthIfNecessary inject_content_length_if_necessary = 3;

    // Always buffer the entire input, and inject ``content-length``,
    // overwriting any provided content-length header.
    FullyBufferAndAlwaysInjectContentLength fully_buffer_and_always_inject_content_length = 4;

    // Always buffer the entire input, do not modify ``content-length``.
    FullyBuffer fully_buffer = 5;
  }
}

// The configuration for one direction of the filter behavior.
message StreamConfig {
  // Whether to bypass / stream / fully buffer / etc.
  // If unset in route, vhost and listener config, the default is ``stream_when_possible``.
  BufferBehavior behavior = 1;

  // The amount stored in the memory buffer before buffering to disk.
  // If unset in route, vhost and listener config, defaults to a hardcoded value of 1MiB
  google.protobuf.UInt64Value memory_buffer_bytes_limit = 2 [(validate.rules).uint64 = {gt: 0}];

  // The maximum storage (excluding memory) to be buffered in this filter.
  // If unset in route, vhost and listener config, defaults to a hardcoded value of 32MiB
  google.protobuf.UInt64Value storage_buffer_bytes_limit = 3;

  // The maximum amount that can be queued for writing to storage, above which the
  // source is requested to pause. If unset, defaults to the same value as
  // ``memory_buffer_bytes_limit``.
  //
  // For example, assuming the recipient is not consuming data at all, if
  // ``memory_buffer_bytes_limit`` was 32MiB, and ``storage_buffer_queue_high_watermark_bytes``
  // was 64MiB, and the filesystem is backed up so writes are not occurring promptly,
  // then:
  //
  // * Any request less than 32MiB will eventually pass through without ever attempting
  //   to write to disk.
  // * Any request with over 32MiB buffered will start trying to write to disk.
  //   If it reaches (32+64)MiB buffered in memory (write to disk isn't keeping up), a high
  //   watermark signal is sent to the source.
  // * Any stream whose total size exceeds
  //   ``memory_buffer_bytes_limit + storage_buffer_bytes_limit`` will provoke an error.
  //   (Note, if the recipient *is* consuming data then it is possible for such an
  //   oversized request to pass through the buffer filter, provided the recipient
  //   isn't consuming data too slowly.)
  //
  // The low watermark signal is sent when the memory buffer is at size
  // ``memory_buffer_bytes_limit + (storage_buffer_queue_high_watermark_bytes / 2)``.
  google.protobuf.UInt64Value storage_buffer_queue_high_watermark_bytes = 4;
}

// A :ref:`file system buffer <config_http_filters_file_system_buffer>` filter configuration.
//
// Route-specific configs override only the fields they explicitly include; unset
// fields inherit from the vhost or listener-level config, or, if never set,
// and not required, use a default value.
message FileSystemBufferFilterConfig {
  // A configuration for an AsyncFileManager.
  //
  // If unset in route, vhost and listener, and the behavior is not ``bypass``
  // in both directions, an Internal Server Error response will be sent.
  common.async_files.v3.AsyncFileManagerConfig manager_config = 1;

  // An optional path to which the unlinked files should be written - this may
  // determine which physical storage device will be used.
  //
  // If unset in route, vhost and listener, will use the environment variable
  // ``TMPDIR``, or, if that's also unset, will use ``/tmp``.
  google.protobuf.StringValue storage_buffer_path = 2;

  // Optional configuration for how to buffer (or not) requests.
  // If unset in route, vhost and listener, ``StreamConfig`` default values will be used
  // (with behavior ``stream_when_possible``)
  StreamConfig request = 3;

  // Optional configuration for how to buffer (or not) responses.
  // If unset in route, vhost and listener, ``StreamConfig`` default values will be used
  // (with behavior ``stream_when_possible``)
  StreamConfig response = 4;
}
