Add the four settings the README documented but pixa lacked (closes #61)
access_control_allow_origin, upstream_fetch_timeout, upstream_max_response_size and downstream_timeout were in the README but unknown to pixa, so a config that followed it aborted startup. Each is now a setting with its PIXA_ variable, defaulting to the value that was fixed in the code: *, 30s, 50 MiB and 60s. Durations are positive Go duration strings; the size is a positive whole number of bytes; the origin is * or one scheme and host. An invalid value aborts startup naming the key and value. downstream_timeout replaces HTTPWriteTimeout for the server's write timeout and the per-request timeout. Model: opus-5-5
This commit is contained in:
@@ -212,6 +212,10 @@ variables set by the file's `env:` section are checked the same way.
|
|||||||
| `PIXA_TRUSTED_PROXIES` | `trusted_proxies` | CIDR ranges of proxies whose `X-Forwarded-For` is believed; default RFC 1918 |
|
| `PIXA_TRUSTED_PROXIES` | `trusted_proxies` | CIDR ranges of proxies whose `X-Forwarded-For` is believed; default RFC 1918 |
|
||||||
| `PIXA_ALLOW_HTTP` | `allow_http` | Allow plain-HTTP upstreams, for testing only; default `false` |
|
| `PIXA_ALLOW_HTTP` | `allow_http` | Allow plain-HTTP upstreams, for testing only; default `false` |
|
||||||
| `PIXA_UPSTREAM_CONNECTIONS_PER_HOST` | `upstream_connections_per_host` | Concurrent connections per upstream host; default `20` |
|
| `PIXA_UPSTREAM_CONNECTIONS_PER_HOST` | `upstream_connections_per_host` | Concurrent connections per upstream host; default `20` |
|
||||||
|
| `PIXA_UPSTREAM_FETCH_TIMEOUT` | `upstream_fetch_timeout` | Time allowed for one fetch from an upstream host; default `30s` |
|
||||||
|
| `PIXA_UPSTREAM_MAX_RESPONSE_SIZE` | `upstream_max_response_size` | Largest upstream response accepted, in bytes; default 50 MiB |
|
||||||
|
| `PIXA_DOWNSTREAM_TIMEOUT` | `downstream_timeout` | Time allowed for answering one client request; default `60s` |
|
||||||
|
| `PIXA_ACCESS_CONTROL_ALLOW_ORIGIN` | `access_control_allow_origin` | CORS origin allowed to read responses: `*` or one origin; default `*` |
|
||||||
| `PIXA_METRICS_USERNAME` | `metrics.username` | Username for `/metrics`, which is served only when both are set |
|
| `PIXA_METRICS_USERNAME` | `metrics.username` | Username for `/metrics`, which is served only when both are set |
|
||||||
| `PIXA_METRICS_PASSWORD` | `metrics.password` | Password for `/metrics`; set together with the username |
|
| `PIXA_METRICS_PASSWORD` | `metrics.password` | Password for `/metrics`; set together with the username |
|
||||||
| `PIXA_SENTRY_DSN` | `sentry_dsn` | Sentry DSN for error reporting; empty disables it |
|
| `PIXA_SENTRY_DSN` | `sentry_dsn` | Sentry DSN for error reporting; empty disables it |
|
||||||
@@ -220,7 +224,10 @@ variables set by the file's `env:` section are checked the same way.
|
|||||||
|
|
||||||
Key settings in more detail:
|
Key settings in more detail:
|
||||||
|
|
||||||
- `access_control_allow_origin` — CORS origin
|
- `access_control_allow_origin` — the origin a browser lets read pixa's
|
||||||
|
responses, sent as the CORS `Access-Control-Allow-Origin` header: `*`, the
|
||||||
|
default, is any site; otherwise one origin, scheme and host only, such as
|
||||||
|
`https://example.com`. Anything else aborts startup
|
||||||
- `allowlist_hosts` — list of allowed upstream hosts
|
- `allowlist_hosts` — list of allowed upstream hosts
|
||||||
- `blocked_networks` — list of CIDR ranges to refuse for SSRF protection,
|
- `blocked_networks` — list of CIDR ranges to refuse for SSRF protection,
|
||||||
added to the always-enforced built-in ranges (loopback, private,
|
added to the always-enforced built-in ranges (loopback, private,
|
||||||
@@ -239,9 +246,14 @@ Key settings in more detail:
|
|||||||
one, and an explicit list replaces the default. An invalid CIDR aborts
|
one, and an explicit list replaces the default. An invalid CIDR aborts
|
||||||
startup. Set this to your proxy's address range if it is not already
|
startup. Set this to your proxy's address range if it is not already
|
||||||
covered by the defaults
|
covered by the defaults
|
||||||
- `upstream_fetch_timeout` — timeout for origin requests
|
- `upstream_fetch_timeout` — time allowed for one fetch from an upstream
|
||||||
- `upstream_max_response_size` — max origin response size
|
host, as a duration such as `30s` (the default) or `2m`
|
||||||
- `downstream_timeout` — client response timeout
|
- `upstream_max_response_size` — largest upstream response accepted, in
|
||||||
|
bytes; default `52428800` (50 MiB). It also limits the image data pixa
|
||||||
|
decodes
|
||||||
|
- `downstream_timeout` — time allowed for answering one client request, as a
|
||||||
|
duration; default `60s`. The upstream fetch counts toward it, so keep it
|
||||||
|
longer than `upstream_fetch_timeout`
|
||||||
- `signing_key` — HMAC secret for URL signatures
|
- `signing_key` — HMAC secret for URL signatures
|
||||||
- `cache_max_bytes` — disk cache size limit in bytes; `0` disables the
|
- `cache_max_bytes` — disk cache size limit in bytes; `0` disables the
|
||||||
disk cache entirely; omitted defaults to 75% of the free space on
|
disk cache entirely; omitted defaults to 75% of the free space on
|
||||||
|
|||||||
@@ -30,6 +30,15 @@ exhaustion
|
|||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
|
- 2026-09-28 add the four settings `README.md` documented but pixa did not
|
||||||
|
have, which aborted startup as unknown keys (closes #61):
|
||||||
|
`access_control_allow_origin` (default `*`, the CORS origin),
|
||||||
|
`upstream_fetch_timeout` (default `30s`), `upstream_max_response_size`
|
||||||
|
(default 50 MiB) and `downstream_timeout` (default `60s`, both the
|
||||||
|
server's write timeout and the per-request timeout); each has a
|
||||||
|
`PIXA_` variable; durations are positive Go duration strings, sizes a
|
||||||
|
whole number of bytes; an invalid value aborts startup naming the key
|
||||||
|
and the value; documented in `config.example.yml` and `README.md`.
|
||||||
- 2026-09-28 strip metadata from processed images (closes #82): every output is
|
- 2026-09-28 strip metadata from processed images (closes #82): every output is
|
||||||
exported with govips' `StripMetadata`, so it carries no EXIF, XMP, IPTC or ICC
|
exported with govips' `StripMetadata`, so it carries no EXIF, XMP, IPTC or ICC
|
||||||
profile; the image is first turned upright with `AutoRotate` (before sizes are
|
profile; the image is first turned upright with `AutoRotate` (before sizes are
|
||||||
@@ -267,7 +276,6 @@ exhaustion
|
|||||||
- X-Request-ID propagation
|
- X-Request-ID propagation
|
||||||
- P2: auto format selection (format=auto based on Accept header)
|
- P2: auto format selection (format=auto based on Accept header)
|
||||||
- P2: configuration
|
- P2: configuration
|
||||||
- add all configuration options from README
|
|
||||||
- YAML config file support
|
- YAML config file support
|
||||||
- P2: operational
|
- P2: operational
|
||||||
- optional Sentry error reporting
|
- optional Sentry error reporting
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
# this file's env: section is set while the file loads, so it overrides
|
# this file's env: section is set while the file loads, so it overrides
|
||||||
# both the environment the process was started with and this file's own
|
# both the environment the process was started with and this file's own
|
||||||
# key.
|
# key.
|
||||||
|
#
|
||||||
|
# Durations are Go duration strings such as 30s or 2m and must be
|
||||||
|
# positive; a bare number has no unit and aborts startup. Sizes are a
|
||||||
|
# whole number of bytes.
|
||||||
|
|
||||||
# Server settings
|
# Server settings
|
||||||
port: 8080
|
port: 8080
|
||||||
@@ -61,6 +65,21 @@ allow_http: false
|
|||||||
# Maximum concurrent connections per upstream host (default: 20)
|
# Maximum concurrent connections per upstream host (default: 20)
|
||||||
upstream_connections_per_host: 20
|
upstream_connections_per_host: 20
|
||||||
|
|
||||||
|
# Time allowed for one fetch from an upstream host (default: 30s)
|
||||||
|
upstream_fetch_timeout: 30s
|
||||||
|
|
||||||
|
# Largest upstream response accepted, in bytes (default: 52428800, 50 MiB)
|
||||||
|
upstream_max_response_size: 52428800
|
||||||
|
|
||||||
|
# Time allowed for answering one client request, the upstream fetch
|
||||||
|
# included, so keep it longer than upstream_fetch_timeout (default: 60s)
|
||||||
|
downstream_timeout: 60s
|
||||||
|
|
||||||
|
# The origin a browser lets read pixa's responses, sent as the CORS
|
||||||
|
# Access-Control-Allow-Origin header: "*" (the default) is any site;
|
||||||
|
# otherwise one origin, scheme and host only, such as https://example.com
|
||||||
|
access_control_allow_origin: "*"
|
||||||
|
|
||||||
# Maximum disk cache size in bytes. Explicit values are used exactly as
|
# Maximum disk cache size in bytes. Explicit values are used exactly as
|
||||||
# given; 0 disables the disk cache entirely (every request fetches and
|
# given; 0 disables the disk cache entirely (every request fetches and
|
||||||
# processes uncached). When omitted, the default is 75% of the free
|
# processes uncached). When omitted, the default is 75% of the free
|
||||||
|
|||||||
+118
-1
@@ -13,6 +13,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.eeqj.de/sneak/smartconfig"
|
"git.eeqj.de/sneak/smartconfig"
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
@@ -24,6 +25,10 @@ const (
|
|||||||
DefaultPort = 8080
|
DefaultPort = 8080
|
||||||
DefaultStateDir = "/var/lib/pixa"
|
DefaultStateDir = "/var/lib/pixa"
|
||||||
DefaultUpstreamConnectionsPerHost = 20
|
DefaultUpstreamConnectionsPerHost = 20
|
||||||
|
DefaultAccessControlAllowOrigin = "*"
|
||||||
|
DefaultUpstreamFetchTimeout = 30 * time.Second
|
||||||
|
DefaultUpstreamMaxResponseSize = 50 << 20 // 50 MiB
|
||||||
|
DefaultDownstreamTimeout = 60 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
// Configuration key names.
|
// Configuration key names.
|
||||||
@@ -44,6 +49,10 @@ const (
|
|||||||
keyCacheMaxBytes = "cache_max_bytes"
|
keyCacheMaxBytes = "cache_max_bytes"
|
||||||
keyBlockedNetworks = "blocked_networks"
|
keyBlockedNetworks = "blocked_networks"
|
||||||
keyTrustedProxies = "trusted_proxies"
|
keyTrustedProxies = "trusted_proxies"
|
||||||
|
keyAccessControlAllowOrigin = "access_control_allow_origin"
|
||||||
|
keyUpstreamFetchTimeout = "upstream_fetch_timeout"
|
||||||
|
keyUpstreamMaxResponseSize = "upstream_max_response_size"
|
||||||
|
keyDownstreamTimeout = "downstream_timeout"
|
||||||
)
|
)
|
||||||
|
|
||||||
// placeholderSigningKey is the dummy signing_key shipped in
|
// placeholderSigningKey is the dummy signing_key shipped in
|
||||||
@@ -86,6 +95,10 @@ var (
|
|||||||
errNotBareHostname = errors.New(
|
errNotBareHostname = errors.New(
|
||||||
"must be a bare hostname without scheme, path, or whitespace")
|
"must be a bare hostname without scheme, path, or whitespace")
|
||||||
errNoHostnameLabels = errors.New("contains no hostname labels")
|
errNoHostnameLabels = errors.New("contains no hostname labels")
|
||||||
|
errNotADuration = errors.New("not a duration such as 30s or 2m")
|
||||||
|
errMustBePositive = errors.New("must be positive")
|
||||||
|
errNotAnOrigin = errors.New(
|
||||||
|
`not "*" or an origin such as https://example.com`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Params defines dependencies for Config.
|
// Params defines dependencies for Config.
|
||||||
@@ -112,6 +125,23 @@ type Config struct {
|
|||||||
AllowHTTP bool // Allow non-TLS upstream (testing only)
|
AllowHTTP bool // Allow non-TLS upstream (testing only)
|
||||||
UpstreamConnectionsPerHost int // Max concurrent connections per upstream host
|
UpstreamConnectionsPerHost int // Max concurrent connections per upstream host
|
||||||
|
|
||||||
|
// UpstreamFetchTimeout is the time allowed for one fetch from an
|
||||||
|
// upstream host. UpstreamMaxResponseSize is the largest upstream
|
||||||
|
// response accepted, in bytes, and also the image processor's input
|
||||||
|
// limit.
|
||||||
|
UpstreamFetchTimeout time.Duration
|
||||||
|
UpstreamMaxResponseSize int64
|
||||||
|
|
||||||
|
// AccessControlAllowOrigin is the origin the CORS middleware allows
|
||||||
|
// to read responses: "*" for any, or one origin such as
|
||||||
|
// https://example.com.
|
||||||
|
AccessControlAllowOrigin string
|
||||||
|
|
||||||
|
// DownstreamTimeout bounds how long answering one client request may
|
||||||
|
// take. It is both the HTTP server's write timeout and the deadline
|
||||||
|
// of the per-request timeout middleware.
|
||||||
|
DownstreamTimeout time.Duration
|
||||||
|
|
||||||
// BlockedNetworks are operator-supplied CIDR ranges to refuse in
|
// BlockedNetworks are operator-supplied CIDR ranges to refuse in
|
||||||
// addition to the built-in SSRF blocklist. Enforced by the upstream
|
// addition to the built-in SSRF blocklist. Enforced by the upstream
|
||||||
// fetcher's dialer; the built-in ranges always apply.
|
// fetcher's dialer; the built-in ranges always apply.
|
||||||
@@ -240,6 +270,14 @@ func newFromSmartConfig(sc *smartconfig.Config) (*Config, error) {
|
|||||||
AllowHTTP: loader.boolVal(keyAllowHTTP, false),
|
AllowHTTP: loader.boolVal(keyAllowHTTP, false),
|
||||||
UpstreamConnectionsPerHost: loader.intVal(
|
UpstreamConnectionsPerHost: loader.intVal(
|
||||||
keyUpstreamConnectionsPerHost, DefaultUpstreamConnectionsPerHost),
|
keyUpstreamConnectionsPerHost, DefaultUpstreamConnectionsPerHost),
|
||||||
|
UpstreamFetchTimeout: loader.durationVal(
|
||||||
|
keyUpstreamFetchTimeout, DefaultUpstreamFetchTimeout),
|
||||||
|
UpstreamMaxResponseSize: loader.int64Val(
|
||||||
|
keyUpstreamMaxResponseSize, DefaultUpstreamMaxResponseSize),
|
||||||
|
AccessControlAllowOrigin: loader.stringVal(
|
||||||
|
keyAccessControlAllowOrigin, DefaultAccessControlAllowOrigin),
|
||||||
|
DownstreamTimeout: loader.durationVal(
|
||||||
|
keyDownstreamTimeout, DefaultDownstreamTimeout),
|
||||||
CacheMaxBytes: loader.int64Val(keyCacheMaxBytes, 0),
|
CacheMaxBytes: loader.int64Val(keyCacheMaxBytes, 0),
|
||||||
BlockedNetworks: blockedNetworks,
|
BlockedNetworks: blockedNetworks,
|
||||||
TrustedProxies: trustedProxies,
|
TrustedProxies: trustedProxies,
|
||||||
@@ -354,7 +392,8 @@ func isKnownConfigKey(key string) bool {
|
|||||||
case keyDebug, keyMaintenanceMode, keyPort, keyStateDir, keySentryDSN,
|
case keyDebug, keyMaintenanceMode, keyPort, keyStateDir, keySentryDSN,
|
||||||
keyDBURL, keyMetrics, keySigningKey, keyAllowlistHosts, keyAllowHTTP,
|
keyDBURL, keyMetrics, keySigningKey, keyAllowlistHosts, keyAllowHTTP,
|
||||||
keyUpstreamConnectionsPerHost, keyCacheMaxBytes, keyBlockedNetworks,
|
keyUpstreamConnectionsPerHost, keyCacheMaxBytes, keyBlockedNetworks,
|
||||||
keyTrustedProxies, "env":
|
keyTrustedProxies, keyAccessControlAllowOrigin, keyUpstreamFetchTimeout,
|
||||||
|
keyUpstreamMaxResponseSize, keyDownstreamTimeout, "env":
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,6 +421,10 @@ func envVarNames() map[string]string {
|
|||||||
keyCacheMaxBytes: "PIXA_CACHE_MAX_BYTES",
|
keyCacheMaxBytes: "PIXA_CACHE_MAX_BYTES",
|
||||||
keyBlockedNetworks: "PIXA_BLOCKED_NETWORKS",
|
keyBlockedNetworks: "PIXA_BLOCKED_NETWORKS",
|
||||||
keyTrustedProxies: "PIXA_TRUSTED_PROXIES",
|
keyTrustedProxies: "PIXA_TRUSTED_PROXIES",
|
||||||
|
keyAccessControlAllowOrigin: "PIXA_ACCESS_CONTROL_ALLOW_ORIGIN",
|
||||||
|
keyUpstreamFetchTimeout: "PIXA_UPSTREAM_FETCH_TIMEOUT",
|
||||||
|
keyUpstreamMaxResponseSize: "PIXA_UPSTREAM_MAX_RESPONSE_SIZE",
|
||||||
|
keyDownstreamTimeout: "PIXA_DOWNSTREAM_TIMEOUT",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -535,6 +578,12 @@ func (c *Config) validate() error {
|
|||||||
settingName(keyCacheMaxBytes), c.CacheMaxBytes, errMustNotBeNegative)
|
settingName(keyCacheMaxBytes), c.CacheMaxBytes, errMustNotBeNegative)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.UpstreamMaxResponseSize <= 0 {
|
||||||
|
return fmt.Errorf("%s: value %d %w",
|
||||||
|
settingName(keyUpstreamMaxResponseSize), c.UpstreamMaxResponseSize,
|
||||||
|
errMustBePositive)
|
||||||
|
}
|
||||||
|
|
||||||
for _, host := range c.AllowlistHosts {
|
for _, host := range c.AllowlistHosts {
|
||||||
err := validateAllowlistHost(host)
|
err := validateAllowlistHost(host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -556,6 +605,25 @@ func (c *Config) validate() error {
|
|||||||
errMustBeSetTogether)
|
errMustBeSetTogether)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return c.validateAccessControlAllowOrigin()
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateAccessControlAllowOrigin checks that access_control_allow_origin
|
||||||
|
// is "*" or one origin, a scheme and host with nothing after them, as
|
||||||
|
// browsers send it in the Origin header. Anything else, such as a bare
|
||||||
|
// hostname or a trailing slash, would match no request.
|
||||||
|
func (c *Config) validateAccessControlAllowOrigin() error {
|
||||||
|
origin := c.AccessControlAllowOrigin
|
||||||
|
if origin == "*" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed, err := url.Parse(origin)
|
||||||
|
if err != nil || parsed.Host == "" || parsed.Scheme+"://"+parsed.Host != origin {
|
||||||
|
return fmt.Errorf("%s: value %q is %w",
|
||||||
|
settingName(keyAccessControlAllowOrigin), origin, errNotAnOrigin)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -673,6 +741,19 @@ func (l *strictLoader) int64Val(key string, defaultVal int64) int64 {
|
|||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *strictLoader) durationVal(key string, defaultVal time.Duration) time.Duration {
|
||||||
|
if l.err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err := getDuration(l.sc, key, defaultVal)
|
||||||
|
if err != nil {
|
||||||
|
l.err = err
|
||||||
|
}
|
||||||
|
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
func (l *strictLoader) boolVal(key string, defaultVal bool) bool {
|
func (l *strictLoader) boolVal(key string, defaultVal bool) bool {
|
||||||
if l.err != nil {
|
if l.err != nil {
|
||||||
return false
|
return false
|
||||||
@@ -794,6 +875,42 @@ func getInt64(sc *smartconfig.Config, key string, defaultVal int64) (int64, erro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getDuration returns the duration value for key, or defaultVal if the
|
||||||
|
// key is omitted. A present value must be a positive Go duration string
|
||||||
|
// such as "30s" or "2m", read with time.ParseDuration; a bare number has
|
||||||
|
// no unit and is an error, as is an explicit null.
|
||||||
|
func getDuration(
|
||||||
|
sc *smartconfig.Config, key string, defaultVal time.Duration,
|
||||||
|
) (time.Duration, error) {
|
||||||
|
raw, ok := lookupValue(sc, key)
|
||||||
|
if !ok {
|
||||||
|
return defaultVal, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if raw == nil {
|
||||||
|
return 0, errNullConfigValue(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
str, ok := raw.(string)
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("config key %q: value %v (%T) is %w",
|
||||||
|
key, raw, raw, errNotADuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed, err := time.ParseDuration(strings.TrimSpace(str))
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("%s: value %q is %w",
|
||||||
|
settingName(key), str, errNotADuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
if parsed <= 0 {
|
||||||
|
return 0, fmt.Errorf("%s: value %q %w",
|
||||||
|
settingName(key), str, errMustBePositive)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsed, nil
|
||||||
|
}
|
||||||
|
|
||||||
// getBool returns the boolean value for key, or defaultVal if the key
|
// getBool returns the boolean value for key, or defaultVal if the key
|
||||||
// is omitted. A present value that is not a boolean (or a ParseBool-able
|
// is omitted. A present value that is not a boolean (or a ParseBool-able
|
||||||
// string), or is explicitly null, is an error; numbers are not accepted
|
// string), or is explicitly null, is an error; numbers are not accepted
|
||||||
|
|||||||
@@ -106,6 +106,8 @@ func (s *Handlers) initImageService() error {
|
|||||||
// Create the fetcher config
|
// Create the fetcher config
|
||||||
fetcherCfg := httpfetcher.DefaultConfig()
|
fetcherCfg := httpfetcher.DefaultConfig()
|
||||||
fetcherCfg.AllowHTTP = s.config.AllowHTTP
|
fetcherCfg.AllowHTTP = s.config.AllowHTTP
|
||||||
|
fetcherCfg.Timeout = s.config.UpstreamFetchTimeout
|
||||||
|
fetcherCfg.MaxResponseSize = s.config.UpstreamMaxResponseSize
|
||||||
|
|
||||||
if s.config.UpstreamConnectionsPerHost > 0 {
|
if s.config.UpstreamConnectionsPerHost > 0 {
|
||||||
fetcherCfg.MaxConnectionsPerHost = s.config.UpstreamConnectionsPerHost
|
fetcherCfg.MaxConnectionsPerHost = s.config.UpstreamConnectionsPerHost
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ func (s *Middleware) Logging() func(http.Handler) http.Handler {
|
|||||||
// CORS returns a CORS middleware.
|
// CORS returns a CORS middleware.
|
||||||
func (s *Middleware) CORS() func(http.Handler) http.Handler {
|
func (s *Middleware) CORS() func(http.Handler) http.Handler {
|
||||||
return cors.Handler(cors.Options{
|
return cors.Handler(cors.Options{
|
||||||
AllowedOrigins: []string{"*"},
|
AllowedOrigins: []string{s.config.AccessControlAllowOrigin},
|
||||||
AllowedMethods: []string{"GET", "HEAD", "OPTIONS"},
|
AllowedMethods: []string{"GET", "HEAD", "OPTIONS"},
|
||||||
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type"},
|
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type"},
|
||||||
ExposedHeaders: []string{"Link"},
|
ExposedHeaders: []string{"Link"},
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ const (
|
|||||||
// short, so a slowloris client dribbling headers is dropped well
|
// short, so a slowloris client dribbling headers is dropped well
|
||||||
// before it ties up a connection for the whole ReadTimeout window.
|
// before it ties up a connection for the whole ReadTimeout window.
|
||||||
HTTPReadHeaderTimeout = 10 * time.Second
|
HTTPReadHeaderTimeout = 10 * time.Second
|
||||||
HTTPWriteTimeout = 60 * time.Second
|
|
||||||
// HTTPIdleTimeout bounds how long an idle keep-alive connection is
|
// HTTPIdleTimeout bounds how long an idle keep-alive connection is
|
||||||
// held open, so idle connections cannot accumulate without limit on a
|
// held open, so idle connections cannot accumulate without limit on a
|
||||||
// service targeting high concurrency.
|
// service targeting high concurrency.
|
||||||
@@ -30,7 +29,7 @@ func (s *Server) newHTTPServer() *http.Server {
|
|||||||
Addr: fmt.Sprintf(":%d", s.config.Port),
|
Addr: fmt.Sprintf(":%d", s.config.Port),
|
||||||
ReadTimeout: HTTPReadTimeout,
|
ReadTimeout: HTTPReadTimeout,
|
||||||
ReadHeaderTimeout: HTTPReadHeaderTimeout,
|
ReadHeaderTimeout: HTTPReadHeaderTimeout,
|
||||||
WriteTimeout: HTTPWriteTimeout,
|
WriteTimeout: s.config.DownstreamTimeout,
|
||||||
IdleTimeout: HTTPIdleTimeout,
|
IdleTimeout: HTTPIdleTimeout,
|
||||||
MaxHeaderBytes: HTTPMaxHeaderBytes,
|
MaxHeaderBytes: HTTPMaxHeaderBytes,
|
||||||
Handler: s,
|
Handler: s,
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func (s *Server) SetupRoutes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.router.Use(s.mw.CORS())
|
s.router.Use(s.mw.CORS())
|
||||||
s.router.Use(middleware.Timeout(HTTPWriteTimeout))
|
s.router.Use(middleware.Timeout(s.config.DownstreamTimeout))
|
||||||
|
|
||||||
if s.sentryEnabled {
|
if s.sentryEnabled {
|
||||||
sentryHandler := sentryhttp.New(sentryhttp.Options{
|
sentryHandler := sentryhttp.New(sentryhttp.Options{
|
||||||
|
|||||||
Reference in New Issue
Block a user