Skip to content

Conversation

@jub0bs
Copy link
Contributor

@jub0bs jub0bs commented Nov 21, 2025

No description provided.

}
if c.allowCredentials {
headers["Access-Control-Allow-Credentials"] = headerTrue
headers.Set("Access-Control-Allow-Credentials", "true")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To keep the allocation optimization and avoid triggering the concurrency check, we could create a new slice with the same backing array. Something like headerTrue[:1]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But then handlers could still mutate the first element of the shared backing array through the new slice...

Copy link
Owner

@rs rs Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They only append or set right? There should no slide[0] = val as far as I remember

Copy link
Contributor Author

@jub0bs jub0bs Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I don't understand your last comment... 🤔

What I meant in my earlier comment is that, even if you write

headers["Access-Control-Allow-Credentials"] = headerTrue[:1]

the tests that I posted in #198 keep failing, partly because the first element of headerTrue can still be mutated.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would it be mutated?

Copy link
Contributor Author

@jub0bs jub0bs Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The failing tests included in #198 show how. Here is a version of the mutating handler focused on ACAO:

func(w http.ResponseWriter, r *http.Request) {
	acao := w.Header()["Access-Control-Allow-Credentials"] // aliases slices headerTrue, headerTrue[:1], etc.
	if len(acao) > 0 {
		acao[0] = "oops!"
		fmt.Println(headerTrue[0]) // oops!
	}
}

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an unlikely use case. The caller would have to assume there is a first element they did not add and would know what it is.

Copy link
Contributor Author

@jub0bs jub0bs Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may be right about the unlikelihood of such a use case, but the fact remains: a handler can corrupt a CORS middleware that wraps it and inadvertently introduce non-obvious synchronisation bugs (non-obvious because it would be natural to assume that the acao slice doesn't alias anything at package level of rs/cors). I think most users of your library would value the guarantee that no such bugs are possible more than saving a couple of allocations during middleware execution.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rs For completeness, here are some benchmark results ("old" and "new" correspond to master and my branch, respectively):

goos: darwin
goarch: arm64
pkg: github.com/rs/cors
cpu: Apple M4
                            │      old      │                 new                  │
                            │    sec/op     │    sec/op      vs base               │
Without-10                     3.279n ±  2%    3.265n ±  6%        ~ (p=0.732 n=6)
Default-10                     98.96n ±  2%   184.55n ± 14%  +86.48% (p=0.002 n=6)
AllowedOrigin-10               142.0n ±  1%    160.5n ± 10%  +13.10% (p=0.002 n=6)
Preflight-10                   247.7n ±  3%    339.9n ± 11%  +37.20% (p=0.002 n=6)
PreflightHeader-10             269.6n ±  2%    358.3n ±  3%  +32.93% (p=0.002 n=6)
PreflightAdversarialACRH-10    501.1n ± 18%    536.6n ±  2%        ~ (p=0.065 n=6)
Wildcard/match-10              6.625n ±  1%    6.612n ±  1%        ~ (p=0.240 n=6)
Wildcard/too_short-10         0.9658n ± 22%   0.9652n ±  3%        ~ (p=0.413 n=6)
geomean                        42.10n          50.20n        +19.26%

                            │     old      │                 new                  │
                            │     B/op     │    B/op     vs base                  │
Without-10                    0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
Default-10                    16.00 ± 0%     48.00 ± 0%  +200.00% (p=0.002 n=6)
AllowedOrigin-10              16.00 ± 0%     32.00 ± 0%  +100.00% (p=0.002 n=6)
Preflight-10                  16.00 ± 0%     48.00 ± 0%  +200.00% (p=0.002 n=6)
PreflightHeader-10            16.00 ± 0%     48.00 ± 0%  +200.00% (p=0.002 n=6)
PreflightAdversarialACRH-10   40.00 ± 0%     56.00 ± 0%   +40.00% (p=0.002 n=6)
Wildcard/match-10             0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
Wildcard/too_short-10         0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
geomean                                  ²                +71.72%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                            │     old      │                 new                  │
                            │  allocs/op   │ allocs/op   vs base                  │
Without-10                    0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
Default-10                    1.000 ± 0%     3.000 ± 0%  +200.00% (p=0.002 n=6)
AllowedOrigin-10              1.000 ± 0%     2.000 ± 0%  +100.00% (p=0.002 n=6)
Preflight-10                  1.000 ± 0%     3.000 ± 0%  +200.00% (p=0.002 n=6)
PreflightHeader-10            1.000 ± 0%     3.000 ± 0%  +200.00% (p=0.002 n=6)
PreflightAdversarialACRH-10   2.000 ± 0%     3.000 ± 0%   +50.00% (p=0.002 n=6)
Wildcard/match-10             0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
Wildcard/too_short-10         0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
geomean                                  ²                +73.21%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants