Skip to content

confighttp: add ExposedHeaders to CORSConfig#15119

Merged
codeboten merged 1 commit into
open-telemetry:mainfrom
Rajneesh180:feat/cors-exposed-headers
Jun 1, 2026
Merged

confighttp: add ExposedHeaders to CORSConfig#15119
codeboten merged 1 commit into
open-telemetry:mainfrom
Rajneesh180:feat/cors-exposed-headers

Conversation

@Rajneesh180

Copy link
Copy Markdown
Contributor

CORSConfig maps AllowedOrigins, AllowedHeaders, and MaxAge into cors.Options, but doesn't map ExposedHeaders even though rs/cors supports it. This means there's no way to set Access-Control-Expose-Headers through receiver config — anyone who needs it has to write a custom middleware.

This came up in jaegertracing/jaeger#8128 where the Jaeger UI needs specific headers exposed over CORS but has to work around this gap with a hand-rolled middleware.

Changes

  • Add ExposedHeaders []string to CORSConfig
  • Wire it into cors.Options in ToServer()
  • Add test
  • Update README

Copilot AI review requested due to automatic review settings April 15, 2026 08:05
@Rajneesh180
Rajneesh180 requested a review from a team as a code owner April 15, 2026 08:05
@Rajneesh180
Rajneesh180 requested a review from mx-psi April 15, 2026 08:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR extends config/confighttp server CORS configuration to support setting Access-Control-Expose-Headers via receiver configuration, aligning CORSConfig with rs/cors capabilities.

Changes:

  • Add ExposedHeaders []string to CORSConfig.
  • Map CORSConfig.ExposedHeaders into cors.Options in ServerConfig.ToServer().
  • Add a server-side test and update README documentation; add a changelog entry.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
config/confighttp/server.go Adds ExposedHeaders to CORSConfig and wires it into cors.Options.
config/confighttp/server_test.go Adds a test asserting Access-Control-Expose-Headers is emitted on actual requests.
config/confighttp/README.md Documents the new exposed_headers config key and provides an example.
.chloggen/add-cors-exposed-headers.yaml Adds release-note entry for the new configuration option.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +346 to +348
// ExposedHeaders sets the value of the Access-Control-Expose-Headers response
// header, indicating which headers are safe to expose to the API of a CORS response.
ExposedHeaders []string `mapstructure:"exposed_headers,omitempty"`

Copilot AI Apr 15, 2026

Copy link

Choose a reason for hiding this comment

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

CORSConfig gained ExposedHeaders, but the package’s config.schema.yaml still defines cors_config without an exposed_headers property. This will make the new setting invisible/invalid for schema-driven tooling. Update config/confighttp/config.schema.yaml to include exposed_headers (type array of strings) with a matching description.

Copilot uses AI. Check for mistakes.
Comment on lines 246 to +250
co := cors.Options{
AllowedOrigins: corsConfig.AllowedOrigins,
AllowCredentials: true,
AllowedHeaders: corsConfig.AllowedHeaders,
ExposedHeaders: corsConfig.ExposedHeaders,

Copilot AI Apr 15, 2026

Copy link

Choose a reason for hiding this comment

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

CORS config is ignored when AllowedOrigins is empty, but the warning currently only triggers when AllowedHeaders is set. With the new ExposedHeaders field (and existing MaxAge), misconfigurations can now be silently ignored. Consider broadening the warning condition/message to include other non-empty CORS fields (e.g., ExposedHeaders, MaxAge).

Copilot uses AI. Check for mistakes.
Comment on lines +458 to +467
func TestHTTPCorsExposedHeaders(t *testing.T) {
sc := &ServerConfig{
NetAddr: confignet.AddrConfig{
Endpoint: "localhost:0",
Transport: confignet.TransportTypeTCP,
},
CORS: configoptional.Some(CORSConfig{
AllowedOrigins: []string{"http://allowed.com"},
ExposedHeaders: []string{"X-Custom-Header", "X-Another-Header"},
}),

Copilot AI Apr 15, 2026

Copy link

Choose a reason for hiding this comment

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

This test validates wiring into cors.Options, but it doesn’t verify that exposed_headers can be set via YAML/unmarshal (the core motivation in the PR description). Consider extending the existing YAML unmarshal “comprehensive config” testdata + assertions to include exposed_headers, so the mapstructure tag is exercised.

Copilot uses AI. Check for mistakes.
@codecov

codecov Bot commented Apr 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.25%. Comparing base (307e3ab) to head (2a4db6b).
⚠️ Report is 142 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #15119      +/-   ##
==========================================
+ Coverage   91.24%   91.25%   +0.01%     
==========================================
  Files         699      699              
  Lines       44913    44914       +1     
==========================================
+ Hits        40979    40986       +7     
+ Misses       2786     2782       -4     
+ Partials     1148     1146       -2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Rajneesh180
Rajneesh180 force-pushed the feat/cors-exposed-headers branch from 5d10680 to 2a4db6b Compare April 15, 2026 16:17
@codspeed-hq

codspeed-hq Bot commented Apr 16, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 30.87%

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

❌ 2 regressed benchmarks
✅ 5 untouched benchmarks
⏩ 76 skipped benchmarks1

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
BenchmarkHTTPProtoLogsSequential 3.7 ms 5 ms -25.49%
BenchmarkGRPCLogsSequential 4.3 ms 6.2 ms -30.87%

Comparing Rajneesh180:feat/cors-exposed-headers (2a4db6b) with main (307e3ab)

Open in CodSpeed

Footnotes

  1. 76 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@Rajneesh180

Copy link
Copy Markdown
Contributor Author

@mx-psi The CodSpeed regression looks like a false positive — their own report flags "Unknown Walltime execution environment detected" and "Different runtime environments detected," which explains the variance. The change itself only adds a []string field to the struct.

The YAML unmarshal test for ExposedHeaders and the config.schema.yaml update are both already included in this commit (the Copilot review items). Let me know if anything else is needed!

@dmathieu

Copy link
Copy Markdown
Member

Yes, codespeed is rather unreliable. You can ignore it.

@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

This PR was marked stale due to lack of activity. It will be closed in 14 days.

@github-actions github-actions Bot added Stale and removed Stale labels May 6, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This PR was marked stale due to lack of activity. It will be closed in 14 days.

@github-actions github-actions Bot added Stale and removed Stale labels May 21, 2026
@codeboten
codeboten added this pull request to the merge queue Jun 1, 2026
Merged via the queue into open-telemetry:main with commit 07f953b Jun 1, 2026
65 of 66 checks passed
@otelbot

otelbot Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Thank you for your contribution @Rajneesh180! 🎉 We would like to hear from you about your experience contributing to OpenTelemetry by taking a few minutes to fill out this survey.

Rajneesh180 added a commit to Rajneesh180/jaeger that referenced this pull request Jun 11, 2026
…rtracing#7827)

confighttp v0.154.0 added ExposedHeaders to CORSConfig
(open-telemetry/opentelemetry-collector#15119), so the MCP server no
longer needs a custom middleware to send Access-Control-Expose-Headers.
Browser-based MCP clients can read the Mcp-Session-Id response header
by listing it under cors::exposed_headers.

- bump confighttp to v0.154.0 along with its interlocked collector modules
- drop the custom expose-headers middleware
- document the CORS setup for browser clients in README and Config
- cover CORS preflight, origin matching, allowed-headers auto-mirror,
  and exposed_headers in server tests

Signed-off-by: Rajneesh Chaudhary <[email protected]>
TimoBehrendt pushed a commit to TimoBehrendt/tracebasedlogsampler that referenced this pull request Jun 17, 2026
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [go.opentelemetry.io/collector/component](https://github.com/open-telemetry/opentelemetry-collector) | `v1.59.0` → `v1.60.0` | ![age](https://developer.mend.io/api/mc/badges/age/go/go.opentelemetry.io%2fcollector%2fcomponent/v1.60.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/go.opentelemetry.io%2fcollector%2fcomponent/v1.59.0/v1.60.0?slim=true) |
| [go.opentelemetry.io/collector/component/componenttest](https://github.com/open-telemetry/opentelemetry-collector) | `v0.153.0` → `v0.154.0` | ![age](https://developer.mend.io/api/mc/badges/age/go/go.opentelemetry.io%2fcollector%2fcomponent%2fcomponenttest/v0.154.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/go.opentelemetry.io%2fcollector%2fcomponent%2fcomponenttest/v0.153.0/v0.154.0?slim=true) |
| [go.opentelemetry.io/collector/confmap](https://github.com/open-telemetry/opentelemetry-collector) | `v1.59.0` → `v1.60.0` | ![age](https://developer.mend.io/api/mc/badges/age/go/go.opentelemetry.io%2fcollector%2fconfmap/v1.60.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/go.opentelemetry.io%2fcollector%2fconfmap/v1.59.0/v1.60.0?slim=true) |
| [go.opentelemetry.io/collector/consumer](https://github.com/open-telemetry/opentelemetry-collector) | `v1.59.0` → `v1.60.0` | ![age](https://developer.mend.io/api/mc/badges/age/go/go.opentelemetry.io%2fcollector%2fconsumer/v1.60.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/go.opentelemetry.io%2fcollector%2fconsumer/v1.59.0/v1.60.0?slim=true) |
| [go.opentelemetry.io/collector/consumer/consumertest](https://github.com/open-telemetry/opentelemetry-collector) | `v0.153.0` → `v0.154.0` | ![age](https://developer.mend.io/api/mc/badges/age/go/go.opentelemetry.io%2fcollector%2fconsumer%2fconsumertest/v0.154.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/go.opentelemetry.io%2fcollector%2fconsumer%2fconsumertest/v0.153.0/v0.154.0?slim=true) |
| [go.opentelemetry.io/collector/pdata](https://github.com/open-telemetry/opentelemetry-collector) | `v1.59.0` → `v1.60.0` | ![age](https://developer.mend.io/api/mc/badges/age/go/go.opentelemetry.io%2fcollector%2fpdata/v1.60.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/go.opentelemetry.io%2fcollector%2fpdata/v1.59.0/v1.60.0?slim=true) |
| [go.opentelemetry.io/collector/processor](https://github.com/open-telemetry/opentelemetry-collector) | `v1.59.0` → `v1.60.0` | ![age](https://developer.mend.io/api/mc/badges/age/go/go.opentelemetry.io%2fcollector%2fprocessor/v1.60.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/go.opentelemetry.io%2fcollector%2fprocessor/v1.59.0/v1.60.0?slim=true) |
| [go.opentelemetry.io/collector/processor/processortest](https://github.com/open-telemetry/opentelemetry-collector) | `v0.153.0` → `v0.154.0` | ![age](https://developer.mend.io/api/mc/badges/age/go/go.opentelemetry.io%2fcollector%2fprocessor%2fprocessortest/v0.154.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/go.opentelemetry.io%2fcollector%2fprocessor%2fprocessortest/v0.153.0/v0.154.0?slim=true) |

---

### Release Notes

<details>
<summary>open-telemetry/opentelemetry-collector (go.opentelemetry.io/collector/component)</summary>

### [`v1.60.0`](https://github.com/open-telemetry/opentelemetry-collector/blob/HEAD/CHANGELOG.md#v1600v01540)

##### 🛑 Breaking changes 🛑

- `cmd/builder`: The `--skip-get-modules` flag will no longer regenerate your `go.mod` file. ([#&#8203;15390](open-telemetry/opentelemetry-collector#15390))
  This is mostly a bug fix, as it led to adverse behaviour that was unintended in the described flow in the README.
  Now when you run `--skip-get-modules`, your `go.mod` file will truly be untouched by `ocb` as the info log claims.

##### 💡 Enhancements 💡

- `pkg/config/configtls`: Add `include_insecure_cipher_suites` to configtls to enable insecure cipher suites. Insecure cipher suites are disabled by default for security. ([#&#8203;13829](open-telemetry/opentelemetry-collector#13829))
- `pkg/confighttp`: Add `ExposedHeaders` field to `CORSConfig` to allow setting the `Access-Control-Expose-Headers` response header. ([#&#8203;15119](open-telemetry/opentelemetry-collector#15119))

##### 🧰 Bug fixes 🧰

- `cmd/mdatagen`: Removes the extra line in the README.md between status and description ([#&#8203;15306](open-telemetry/opentelemetry-collector#15306))
- `pkg/exporterhelper`: Fix nil-pointer panic in `sending_queue::batch` Unmarshal when `sending_queue::sizer` is set and `sending_queue::batch::enabled` is false. ([#&#8203;14687](open-telemetry/opentelemetry-collector#14687))
  When `sending_queue::sizer` was set and `sending_queue::batch::enabled: false`
  cleared the batch Optional to None, the sizer-inheritance branch in
  `queuebatch.Config.Unmarshal` dereferenced a nil Optional and crashed the
  collector at startup. The branch now also requires `Batch.HasValue()`.

<!-- previous-version -->

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My41LjQiLCJ1cGRhdGVkSW5WZXIiOiI0My41LjQiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbXX0=-->

Reviewed-on: https://gitea.t000-n.de/t.behrendt/tracebasedlogsampler/pulls/75
Reviewed-by: t.behrendt <[email protected]>
Co-authored-by: Renovate Bot <[email protected]>
Co-committed-by: Renovate Bot <[email protected]>
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.

4 participants