ReverseProxyHandler (HTTP Proxy)

Overview

ReverseProxyHandler is a HTTP proxy handler. It is the very basic resources for AILERON Gateway to work as a gateway.

It works as:

  • HTTP Handler

Config yaml format becomes like below. And the resource specific spec is defined in in the proto format shown in the Resource Definition.

apiVersion: core/v1
kind: ReverseProxyHandler
metadata:
  name: "default"
  namespace: "default"
spec: {}

Features

1. Proxy request

ReverseProxyHandler proxies following types of requests.

2. Load Balancing

The ReverseProxyHandler supports following load balancing algorithm.

LB AlgorithmHash basedConsistentWhen assigned upstream inactive
(Weighted) Round RobinNo-Skip inactive upstream.
(Weighted) RandomNo-Skip inactive upstream.
(Weighted) Direct HashYesNoError.
(Weighted) Ring HashYesYesSkip inactive upstream.
(Weighted) MaglevYesYesRecalculate hash table.

Following hash sources are available for hash based load balancing.

Hash Source Limitations:

  • Only 1 value is randomly used if there were multiple header values bounded to a single key.
  • Only 1 value is randomly used if there were multiple query values bounded to a single key.
  • ReverseProxyHandler must be registered to a server with path parameter pattern. See http#ServeMux.
Hashing MethodsHash Source
HeaderA header value.
Multiple HeaderA joined string of header values.
Header PatternA string extracted from header value by regular expression.
CookieA cookie value.
QueryA URL query value.
Path ParamA URL path parameter.
Client AddrClient ip and port.

3. Rremove Hop-by-hop headers

Hop-by-Hop headers are removed when proxying requests. Headers to be removed are described in RFC 7230 and RFC 2616. One of the reference implementations would be httputil/reverseproxy.go.

AILERON Gateway removes following headers from requests to be proxied.

  • Connection
  • Keep-Alive
  • Proxy-Connection
  • Proxy-Authenticate
  • Proxy-Authorization
  • Te
  • Trailer
  • Transfer-Encoding
  • Upgrade
  • Headers listed in Connection

4. Adding forwarded headers

Forwarded related headers are added to the proxy headers. X-Forwarded-For, X-Forwarded-Host, X-Forwarded-Port and X-Forwarded-Proto are added. But Forwarded header is not added itself. This is because the X-Forwarded-* header is more generally used than Forwarded itself.

Following table shows the forwarded headers and the example values.

HeaderAddedPrior valuesValueExample
X-Forwarded-ForYesKeepClient IP address192.167.0.1, 127.0.0.1
X-Forwarded-PortYesDiscardClient port number12345
X-Forwarded-HostYesDiscardRequested host nameexample.com
X-Forwarded-ProtoYesDiscardRequested schemehttp or https
ForwardedNo-All of above-

Resource Definition

ReverseProxyHandler is defined in the proto/core/v1/httpproxy.proto

syntax = "proto3";
package core.v1;

import "buf/validate/validate.proto";
import "core/v1/http.proto";
import "core/v1/resilience.proto";
import "kernel/matcher.proto";
import "kernel/network.proto";
import "kernel/resource.proto";

option go_package = "github.com/aileron-gateway/aileron-gateway/apis/core/v1";

// ReverseProxyHandler resource definition.
// apiVersion="core/v1", kind="ReverseProxyHandler".
message ReverseProxyHandler {
    string                  APIVersion = 1 [json_name = "apiVersion"];
    string                  Kind       = 2 [json_name = "kind"];
    kernel.Metadata         Metadata   = 3 [json_name = "metadata"];
    ReverseProxyHandlerSpec Spec       = 4 [json_name = "spec"];
}

// ReverseProxyHandlerSpec is the specifications for the ReverseProxyHandler object.
message ReverseProxyHandlerSpec {
    // [OPTIONAL]
    // ErrorHandler is the reference to a ErrorHandler object.
    // Referred object must implement ErrorHandler interface.
    // Default error handler is used when not set.
    kernel.Reference ErrorHandler = 1 [json_name = "errorHandler"];

    // [OPTIONAL]
    // Patterns is path patterns that this handler is registered to a server.
    // Default is not set.
    repeated string Patterns = 2 [json_name = "patterns", (buf.validate.field).repeated.unique = true];

    // [OPTIONA]
    // Methods is the list of HTTP method this handler can handle.
    // Note that it depends on the multiplexer, or HTTP router
    // if this field can be used.
    // If not set, all methods are accepted.
    // Default is not set.
    repeated HTTPMethod Methods = 3 [json_name = "methods", (buf.validate.field).repeated.unique = true];

    // [OPTIONAL]
    // Tripperwares is the list of references to Tripperwares  object.
    // Referred object must implement Tripperware interface.
    // Default is not set.
    repeated kernel.Reference Tripperwares = 4 [json_name = "tripperwares"];

    // [OPTIONAL]
    // RoundTripper is the references to a roundTripper  object.
    // Referred object must implement RoundTripper interface.
    // Default roundTripper is used when not set.
    kernel.Reference RoundTripper = 5 [json_name = "roundTripper"];

    // [OPTIONAL]
    // LoadBalancers is the list of load balancers.
    // Proxy upstreams are specified in this field.
    // Requests will be proxied to the first matched upstream
    // by matching with the load balancers defined order.
    // Default is not set.
    repeated LoadBalancerSpec LoadBalancers = 6 [json_name = "loadBalancers"];
}

// LoadBalancerSpec is the specification of LoadBalancer objects.
message LoadBalancerSpec {
    // [OPTIONAL]
    // LBAlgorithm specifies the load balancing algorithm.
    // Default RoundRobin will be used if not set.
    // Default is not set.
    LBAlgorithm LBAlgorithm = 1 [json_name = "lbAlgorithm"];

    // [REQUIRED]
    // Upstreams is the list of upstream server of proxy target.
    // An internal server error will be returned when no upstreams are defined.
    // Default is not set.
    repeated UpstreamSpec Upstreams = 2 [json_name = "upstreams"];

    // [OPTIONAL]
    // PathMatcher is the path matching algorithm to be used.
    // If need multiple path matchers, use PathMatchers field instead.
    // If both PathMatcher and PathMatchers are set, the PathMatcher
    // is appended as the first matcher of PathMatchers.
    // If both PathMatcher and PathMatchers are not set,
    // HTTP error responses are returned to all requests.
    // Default is not set.
    PathMatcherSpec PathMatcher = 3 [json_name = "pathMatcher"];

    // [OPTIONAL]
    // PathMatchers is the list of path matching algorithm to be used.
    // A prefix matcher with "/" prefix will be used which matches all
    // requests if not set.
    // If need only 1 path matcher, PathMatcher field can be used instead.
    // If both PathMatcher and PathMatchers are set, the PathMatcher
    // is appended as the first matcher of PathMatchers.
    // HTTP error responses are returned to all requests.
    // Matchers are OR condition and the first matched one is used.
    // Default is not set.
    repeated PathMatcherSpec PathMatchers = 4 [json_name = "pathMatchers"];

    // [OPTIONAL]
    // Methods is the list of HTTP method this loadbalancer can accept.
    // If not set, all methods are accepted.
    // Default is not set.
    repeated HTTPMethod Methods = 5 [json_name = "methods", (buf.validate.field).repeated.unique = true];

    // [OPTIONAL]
    // Hosts is the list of hosts to allow.
    // If not set, all hosts are allowed.
    // List all host names because the value are matched by exact matching algorithm.
    // Wildcard characters such as "*" are not available.
    // Default is not set.
    repeated string Hosts = 6 [json_name = "hosts", (buf.validate.field).repeated.unique = true];

    // [OPTIONAL]
    // PathParamMatcher is the path parameter value matcher to check
    // if this loadbalancer can accept the target request.
    // Path parameter is only available when the handler was registered
    // to a server with patterns containing path parameters
    // described at https://pkg.go.dev/net/http#hdr-Patterns.
    // Listed matchers are evaluated by AND condition.
    // If OR matching condition is necessary, set the condition within a single matcher.
    // Default is not set.
    repeated ParamMatcherSpec PathParamMatchers = 7 [json_name = "pathParamMatchers"];

    // [OPTIONAL]
    // HeaderMatcher is the header value matcher to check
    // if this loadbalancer can accept the target request.
    // If multiple header values were found, they are joined
    // with a comma "," and agggregated to a singled string.
    // For example ["foo", "bar"] will be "foo,bar" and the matcher
    // is applied to the joined value "foo,bar".
    // Listed matchers are evaluated by AND condition.
    // If OR matching condition is necessary, set the condition within a single matcher.
    // Default is not set.
    repeated ParamMatcherSpec HeaderMatchers = 8 [json_name = "headerMatchers"];

    // [OPTIONAL]
    // QueryMatcher is the URL query value matcher to check
    // this loadbalancer can accept the target request.
    // If multiple query values were found, they are joined
    // with a comma "," and agggregated to a singled string.
    // For example ["foo", "bar"] will be "foo,bar" and the matcher
    // is applied to the joined value "foo,bar".
    // Listed matchers are evaluated by AND condition.
    // If OR matching condition is necessary, set the condition within a single matcher.
    // Default is not set.
    repeated ParamMatcherSpec QueryMatchers = 9 [json_name = "queryMatchers"];

    // [OPTIONAL]
    // HashTableSize is the size of hash tables for hash-based load balancers.
    // This field is used by Maglev and RingHash load balancers.
    // If not set, default value is used.
    // Default value depends on the load balancer.
    int32 HashTableSize = 10 [json_name = "hashTableSize"];

    // [OPTIONAL]
    // Hashers is the hashing methods for hash-based load balancers.
    // This field is optional but should be set at least 1
    // for hash-based load balancers.
    // Default is not set.
    repeated HTTPHasherSpec Hashers = 11 [json_name = "hashers"];
}

// PathMatcherSpec is the specification of PathMatcher object
// used for path matching of incoming HTTP requests.
message PathMatcherSpec {
    // [OPTIONAL]
    // Match is the url path pattern to be matched to this matcher.
    // The grammar of the pattern depends on the MatchType.
    // This pattern should not contain prefix set by TrimPrefix or AppendPrefix.
    // Currently, only 1 prefix string can be set here.
    // Use Regex or RegexPOSIX match type and ser Rewrite field
    // if you need trim multiple prefix.
    // It can also trim or rewrite specific patterns.
    // Default is not set.
    string Match = 1 [json_name = "match"];

    // [OPTIONAL]
    // MatchType is the type of pattern matching algorithm.
    // The path pattern specified at the Match field should follow the
    // grammar of this match type.
    // Default is [Prefix].
    kernel.MatchType MatchType = 2 [json_name = "matchType"];

    // [OPTIONAL]
    // Rewrite is the path rewrite expression.
    // This field is used when the MatchType is Regex or RegexPOSIX.
    // Checkout https://pkg.go.dev/regexp#Regexp.ExpandString
    // Default is not set.
    string Rewrite = 3 [json_name = "rewrite"];

    // [OPTIONAL]
    // TrimPrefix is the prefix string to be removed from the URL path.
    // For example, "/trim/me", "/prefix", "/api".
    // This prefix is removed before checking the match.
    // So the Match filed should not contain this value.
    // Default is not set.
    string TrimPrefix = 4 [json_name = "trimPrefix"];

    // [OPTIONAL]
    // AppendPrefix is the prefix string to be added to the URL path.
    // For example, "/append/me", "/prefix", "/api".
    // This prefix is added after checking the match.
    // So the Match filed should not contain this value.
    // Default is not set.
    string AppendPrefix = 5 [json_name = "appendPrefix"];
}

// ParamMatcherSpec is the specification of ParamMatcherParamMatcherSpec object
// used for header or query value matching.
message ParamMatcherSpec {
    // [REQUIRED]
    // Key is the key name to check.
    // If the specified key were not found in header, query or path params,
    // this matcher fails without calling the match function specified at MatchType.
    // Default is not set.
    string Key = 1 [json_name = "key", (buf.validate.field).string.min_len = 1];

    // [OPTIONAL]
    // Patterns is the value pattern list.
    // The grammar of the pattern depends on the MatchType.
    // Patterns are evaluated by OR condition.
    // It will be considered that at least 1 pattern matched to a target,
    // this macher object returns true.
    // Default is not set, which means an empty string.
    repeated string Patterns = 2 [json_name = "patterns"];

    // [OPTIONAL]
    // MatchType is the type of pattern matching algorithm.
    // The pattern specified at the Pattern field should follow the
    // grammar of this match type.
    // Default is [Exact].
    kernel.MatchType MatchType = 3 [json_name = "matchType"];
}

// UpstreamSpec is the specification of Upstream object.
message UpstreamSpec {
    // [REQUIRED]
    // URL is the base url for for proxy.
    // This field can contain URL path.
    // For example "http://localhost:8080/api/"
    // Default is not set.
    string URL = 1 [json_name = "url", (buf.validate.field).string.pattern = "(http://|https://).*"];

    // [OPTIONAL]
    // Weight is the weight, or priority of this target.
    // Set -1 to disable this upstream.
    // 0 is the same as default value 1.
    // Default is [1].
    int32 Weight = 2 [json_name = "weight", (buf.validate.field).int32 = {gte : -1, lte : 1000}];

    // [OPTIONAL]
    // EnablePassive enables passive health check.
    // Default is [false].
    bool EnablePassive = 3 [json_name = "enablePassive"];

    // [OPTIONAL]
    // EnableActive enables active health check.
    // Default is [false].
    bool EnableActive = 4 [json_name = "enableActive"];

    // [OPTIONAL]
    // InitialDelay is the wait time in seconds until to start active health checking after starts.
    // Note that this field is used only when the active health checking is configured.
    // Default is [0].
    int32 InitialDelay = 7 [json_name = "initialDelay"];

    // [OPTIONAL]
    // CheckInterval is the interval of active health check in seconds.
    // Note that this field is used only when the active health checking is configured.
    // Default is [1].
    int32 CheckInterval = 8 [json_name = "checkInterval"];

    // [OPTIONAL]
    // NetworkType is network type used for active health cheking.
    // Note that this field is used only when the active health checking is configured.
    // Default is [HTTP].
    kernel.NetworkType NetworkType = 9 [json_name = "networkType"];

    // [OPTIONAL]
    // Protocol is the protocol number or a protocol name used for health checking.
    // This field is used when "IP", "IP4" or "IP6" is selected as the network type.
    // See https://pkg.go.dev/net#Dial for more detail.
    // Default is ["icmp"].
    string Protocol = 10 [json_name = "protocol"];

    // [OPTIONAL]
    // Address is the target IP address or URL for active health checking.
    // For example, specify a url "http://example.com/healthy" for HTTP network type,
    // "127.0.0.1:8080" for TCP and UDP type, "127.0.0.1" for IP type.
    // See https://pkg.go.dev/net#Dial for more detail.
    // Default is not set.
    string Address = 11 [json_name = "address"];

    // [OPTIONAL]
    // [This feature is disabled now]
    // CircuitBreaker is circuit breaker specification for health checking.
    // Default values are used when not specified.
    CircuitBreaker CircuitBreaker = 12 [json_name = "circuitBreaker"];
}

// CircuitBreaker is the specification of CircuitBreaker object.
message CircuitBreaker {
    // [OPTIONAL]
    // FailureThreshold is the threshold to consider upstream unhealthy.
    // The unit of the value differs depending on  the count algorithm.
    // The unit is count for ConsecutiveCounter and percentage 0-100 for others.
    // Default is [5].
    int32 FailureThreshold = 1 [json_name = "failureThreshold", (buf.validate.field).int32 = {gte : 0}];

    // [OPTIONAL]
    // SuccessThreshold is the threshold to consider upstream healthy.
    // The unit of the value differs depending on  the count algorithm.
    // The unit is count for ConsecutiveCounter and percentage 0-100 for others.
    // Default is [80].
    int32 SuccessThreshold = 2 [json_name = "successThreshold", (buf.validate.field).int32 = {gte : 0}];

    // [OPTIONAL]
    // EffectiveFailureSamples is the number of health check samples that is
    // considered to be meaningful for calculating failure rate.
    // The default value of 20 means that the initial 20 requests are
    // accepted after the status of circuit breaker changed to closed
    // inspite of the failure rate.
    // Ths value will be ignored for ConsecutiveCounter.
    // Default is [20].
    int32 EffectiveFailureSamples = 3 [json_name = "effectiveFailureSamples", (buf.validate.field).int32 = {gte : 0}];

    // [OPTIONAL]
    // EffectiveSuccessSamples is the number of health check samples that is
    // considered to be meaningful for calculating success rate
    // when the status of circuit breaker is .
    // The default value of 20 means that the initial 20 requests are
    // accepted after the status of circuit breaker changed to closed
    // inspite of the failure rate.
    // Ths value will be ignored for ConsecutiveCounter.
    // Default is [20].
    int32 EffectiveSuccessSamples = 4 [json_name = "effectiveSuccessSamples", (buf.validate.field).int32 = {gte : 0}];

    // [OPTIONAL]
    // WaitDuration is wait time to reset success and failure count
    // of circuit breaker in seconds.
    // Default is [180], or 3 minutes.
    int32 WaitDuration = 5 [json_name = "waitDuration", (buf.validate.field).int32 = {gte : 1}];

    // [OPTIONAL]
    // CircuitBreakerCounter is the success and failure counter of health cheking for circuit breaker.
    // ConsecutiveCounter is set by default.
    // Currently, these configs do not work.
    oneof CircuitBreakerCounter {
        ConsecutiveCounterSpec             ConsecutiveCounter             = 6 [json_name = "consecutiveCounter"];
        CountBasedFixedWindowCounterSpec   CountBasedFixedWindowCounter   = 7 [json_name = "countBasedFixedWindowCounter"];
        TimeBasedFixedWindowCounterSpec    TimeBasedFixedWindowCounter    = 8 [json_name = "timeBasedFixedWindowCounter"];
        CountBasedSlidingWindowCounterSpec CountBasedSlidingWindowCounter = 9 [json_name = "countBasedSlidingWindowCounter"];
        TimeBasedSlidingWindowCounterSpec  TimeBasedSlidingWindowCounter  = 10 [json_name = "timeBasedSlidingWindowCounter"];
    }
}

// ConsecutiveCounterSpec is the specification of the circuit breaker counter
// with consecutive counting algorithm.
message ConsecutiveCounterSpec {
}

// CountBasedFixedWindowCounterSpec is the specification of the circuit breaker counter
// with count-based fixed window algorithm.
message CountBasedFixedWindowCounterSpec {
    // [REQUIRED]
    // This value must be grater than or equal to effectiveFailureSamples and effectiveSuccessSamples.
    int32 Samples = 1 [json_name = "samples", (buf.validate.field).int32.gte = 1];
}

// TimeBasedFixedWindowCounterSpec is the specification of the circuit breaker counter
// with time-based fixed window algorithm.
message TimeBasedFixedWindowCounterSpec {
    // [OPTIONAL]
    // Window width in seconds.
    // Default is [5].
    int32 WindowWidth = 1 [json_name = "windowWidth", (buf.validate.field).int32.gte = 1];
}

// CountBasedSlidingWindowCounterSpec is the specification of the circuit breaker counter
// with count-based sliding window algorithm.
message CountBasedSlidingWindowCounterSpec {
    // [REQUIRED]
    // This value must be grater than or equal to effectiveFailureSamples and effectiveSuccessSamples.
    int32 Samples = 1 [json_name = "samples", (buf.validate.field).int32.gte = 1];
    // [REQUIRED]
    int32 HistoryLimit = 2 [json_name = "historyLimit", (buf.validate.field).int32.gte = 1];
}

// TimeBasedSlidingWindowCounterSpec is the specification of the circuit breaker counter
// with time-based sliding window algorithm.
message TimeBasedSlidingWindowCounterSpec {
    // [OPTIONAL]
    // Window width in seconds.
    // Default is [5].
    int32 WindowWidth = 1 [json_name = "windowWidth", (buf.validate.field).int32.gte = 1];
    // [REQUIRED]
    int32 HistoryLimit = 2 [json_name = "historyLimit", (buf.validate.field).int32.gte = 1];
}

References


Last modified June 2, 2025: update docs (df954a4)