From 4733aa03c4fa76055885bfe59f8a36bc7a4b751b Mon Sep 17 00:00:00 2001 From: Mao Mao Date: Fri, 28 Aug 2026 21:27:04 +0800 Subject: [PATCH] feat: support CORS Private Network Access (#350) --- README.md | 4 +++- lib/config.go | 13 +++++++------ lib/config_test.go | 3 +++ lib/handler.go | 13 +++++++------ lib/handler_test.go | 23 +++++++++++++++++++++++ 5 files changed, 43 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index fefb78c..1318eb9 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,8 @@ cors: # Whether or not CORS configuration should be applied. Default is 'false'. enabled: true credentials: true + # Allow Private Network Access preflight requests. Default is 'false'. + allow_private_network: false # The following are the default CORS settings when it is enabled. allowed_hosts: - '*' @@ -240,7 +242,7 @@ A `regex` rule is matched literally against the path, and gets none of the above ### CORS -The `allowed_*` properties are optional, the default value for each of them will be `*`. `exposed_headers` is optional as well, but is not set if not defined. Setting `credentials` to `true` will allow you to: +The `allowed_*` properties are optional, the default value for each of them will be `*`. `exposed_headers` is optional as well, but is not set if not defined. Setting `allow_private_network` to `true` to allow Private-Network-Access preflight requests. Setting `credentials` to `true` will allow you to: 1. Use `withCredentials = true` in javascript. 2. Use the `username:password@host` syntax. diff --git a/lib/config.go b/lib/config.go index 3761652..4b2da91 100644 --- a/lib/config.go +++ b/lib/config.go @@ -383,10 +383,11 @@ type Log struct { } type CORS struct { - Enabled bool - Credentials bool - AllowedHeaders []string `mapstructure:"allowed_headers"` - AllowedHosts []string `mapstructure:"allowed_hosts"` - AllowedMethods []string `mapstructure:"allowed_methods"` - ExposedHeaders []string `mapstructure:"exposed_headers"` + Enabled bool + Credentials bool + AllowPrivateNetwork bool `mapstructure:"allow_private_network"` + AllowedHeaders []string `mapstructure:"allowed_headers"` + AllowedHosts []string `mapstructure:"allowed_hosts"` + AllowedMethods []string `mapstructure:"allowed_methods"` + ExposedHeaders []string `mapstructure:"exposed_headers"` } diff --git a/lib/config_test.go b/lib/config_test.go index 1218cbb..4853e83 100644 --- a/lib/config_test.go +++ b/lib/config_test.go @@ -55,6 +55,7 @@ func TestConfigDefaults(t *testing.T) { require.EqualValues(t, []string{"*"}, cfg.CORS.AllowedHosts) require.EqualValues(t, []string{"Authorization", "Content-Type", "Content-Range", "Depth", "Destination", "If", "Lock-Token", "Overwrite", "X-Update-Range"}, cfg.CORS.AllowedHeaders) require.EqualValues(t, []string{"COPY", "DELETE", "GET", "HEAD", "LOCK", "MKCOL", "MOVE", "OPTIONS", "PATCH", "POST", "PROPFIND", "PROPPATCH", "PUT", "UNLOCK"}, cfg.CORS.AllowedMethods) + require.False(t, cfg.CORS.AllowPrivateNetwork) } func TestConfigCascade(t *testing.T) { @@ -356,6 +357,7 @@ func TestConfigKeys(t *testing.T) { cors: enabled: true credentials: true + allow_private_network: true allowed_headers: - Depth allowed_hosts: @@ -369,6 +371,7 @@ cors: require.True(t, cfg.CORS.Enabled) require.True(t, cfg.CORS.Credentials) + require.True(t, cfg.CORS.AllowPrivateNetwork) require.EqualValues(t, []string{"Content-Length", "Content-Range"}, cfg.CORS.ExposedHeaders) require.EqualValues(t, []string{"Depth"}, cfg.CORS.AllowedHeaders) require.EqualValues(t, []string{"http://localhost:8080"}, cfg.CORS.AllowedHosts) diff --git a/lib/handler.go b/lib/handler.go index e1b88e7..9af90c0 100644 --- a/lib/handler.go +++ b/lib/handler.go @@ -48,12 +48,13 @@ func NewHandler(c *Config) (http.Handler, error) { if c.CORS.Enabled { return cors.New(cors.Options{ - AllowCredentials: c.CORS.Credentials, - AllowedOrigins: c.CORS.AllowedHosts, - AllowedMethods: c.CORS.AllowedMethods, - AllowedHeaders: c.CORS.AllowedHeaders, - ExposedHeaders: c.CORS.ExposedHeaders, - OptionsPassthrough: false, + AllowCredentials: c.CORS.Credentials, + AllowPrivateNetwork: c.CORS.AllowPrivateNetwork, + AllowedOrigins: c.CORS.AllowedHosts, + AllowedMethods: c.CORS.AllowedMethods, + AllowedHeaders: c.CORS.AllowedHeaders, + ExposedHeaders: c.CORS.ExposedHeaders, + OptionsPassthrough: false, }).Handler(h), nil } diff --git a/lib/handler_test.go b/lib/handler_test.go index ec503c2..9daecab 100644 --- a/lib/handler_test.go +++ b/lib/handler_test.go @@ -86,6 +86,29 @@ func TestServerDefaults(t *testing.T) { require.ErrorContains(t, client.Write("/foo.txt", []byte("hello world 2"), 0666), "403") } +func TestServerCORSPrivateNetwork(t *testing.T) { + t.Parallel() + + srv := makeTestServer(t, ` +cors: + enabled: true + allow_private_network: true`) + defer srv.Close() + + req, err := http.NewRequest(http.MethodOptions, srv.URL, nil) + require.NoError(t, err) + req.Header.Set("Origin", "https://example.com") + req.Header.Set("Access-Control-Request-Method", http.MethodGet) + req.Header.Set("Access-Control-Request-Private-Network", "true") + + resp, err := http.DefaultClient.Do(req) + require.NoError(t, err) + defer func() { _ = resp.Body.Close() }() + + require.Equal(t, http.StatusNoContent, resp.StatusCode) + require.Equal(t, "true", resp.Header.Get("Access-Control-Allow-Private-Network")) +} + func TestServerPartialUpdateOptions(t *testing.T) { t.Parallel()