Skip to content

feature: http badrequest code instead of 500, update tests, modernize#225

Merged
rustatian merged 8 commits intomasterfrom
feature/bad-req-invalid-multipart
Mar 6, 2026
Merged

feature: http badrequest code instead of 500, update tests, modernize#225
rustatian merged 8 commits intomasterfrom
feature/bad-req-invalid-multipart

Conversation

@rustatian
Copy link
Member

@rustatian rustatian commented Mar 6, 2026

Reason for This PR

Description of Changes

  • Use http bad request (400) http code if the multipart request is broken instead of 500.
  • Update and modernize tests.
  • Remove io.Pipe direct stdin/out reading for OTEL, replace with InMemory otel storage.

License Acceptance

By submitting this pull request, I confirm that my contribution is made under
the terms of the MIT license.

PR Checklist

[Author TODO: Meet these criteria.]
[Reviewer TODO: Verify that these criteria are met. Request changes if not]

  • All commits in this PR are signed (git commit -s).
  • The reason for this PR is clearly provided (issue no. or explanation).
  • The description of changes is clear and encompassing.
  • Any required documentation changes (code and docs) are included in this PR.
  • Any user-facing changes are mentioned in CHANGELOG.md.
  • All added/changed functionality is tested.

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced error handling for network disruptions and unexpected connection closures.
  • Chores

    • Updated continuous integration workflows and linting tools to newer versions.
    • Updated PHP package dependencies.
    • Modernized test infrastructure with improved concurrency patterns.

Signed-off-by: Valery Piashchynski <piashchynski.valery@gmail.com>
@rustatian rustatian self-assigned this Mar 6, 2026
Copilot AI review requested due to automatic review settings March 6, 2026 09:40
@rustatian rustatian added the enhancement New feature or request label Mar 6, 2026
Copy link

Copilot AI left a comment

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 improves error handling by returning HTTP 400 (Bad Request) instead of HTTP 500 for broken multipart requests, modernizes tests throughout the tests/ package, and replaces the brittle io.Pipe/stderr-based OTEL span verification with an in-memory exporter.

Changes:

  • handler/handler.go: Return HTTP 400 when io.EOF or io.ErrUnexpectedEOF is returned during request parsing (e.g. truncated multipart body), instead of 500.
  • tests/: Modernize all test goroutines to use wg.Go(func(){...}) (Go 1.22+ sync.WaitGroup.Go), for range N idiom, replace otel.Plugin + stderr pipe with an in-memory tracetest exporter via a new tracetestMiddleware, and add a new handler/handler_test.go with unit tests for the 400/413 behavior.
  • tests/php_test_files/composer.json / tests/go.mod: Replace guzzlehttp/guzzle v6 adapter with guzzle v7 adapter; promote several indirect Go module dependencies to direct.

Reviewed changes

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

Show a summary per file
File Description
handler/handler.go Adds io.EOF/io.ErrUnexpectedEOF check to return HTTP 400 for malformed multipart requests
handler/uploads.go Modernizes goroutine spawning to use wg.Go
handler/handler_test.go New unit tests covering 400/413 multipart/stream errors and handleError behaviour
tests/http_plugin_test.go Replaces wg.Add(1); go func(){ defer wg.Done() ... }() with wg.Go(...) throughout
tests/http_plugin2_test.go Same goroutine modernisation + for range N idiom
tests/http_plugin3_test.go Same goroutine modernisation + for range N idiom
tests/http_plugin4_test.go Same goroutine modernisation
tests/http_otlp_test.go Replaces stderr pipe OTEL verification with in-memory tracetest exporter
tests/configs/.rr-http-otel.yaml / .rr-http-otel2.yaml Remove otel: config block; use tracetestOtel middleware name
tests/go.mod Promotes OTEL and context deps to direct; marks otel/v5 indirect
tests/php_test_files/composer.json Upgrades Guzzle v6 → v7 adapter, removes deprecated packages
.golangci.yml Raises goconst min-occurrences from 3 to 10
go.work.sum Updated dependency checksums

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

dependabot bot and others added 6 commits March 6, 2026 10:56
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 5 to 8.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](actions/download-artifact@v5...v8)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 7 to 9.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](golangci/golangci-lint-action@v7...v9)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-version: '9'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@v4...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4 to 5.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](codecov/codecov-action@v4...v5)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4 to 6.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](actions/setup-go@v4...v6)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Valery Piashchynski <piashchynski.valery@gmail.com>
@coderabbitai
Copy link

coderabbitai bot commented Mar 6, 2026

📝 Walkthrough

Walkthrough

This pull request updates GitHub Actions workflows to newer action versions, refactors goroutine patterns across test files to use WaitGroup.Go, adds EOF error handling in the HTTP handler to return 400 Bad Request for truncated multipart requests, replaces otel instrumentation with custom test middleware for tracing, and updates dependencies.

Changes

Cohort / File(s) Summary
GitHub Actions Workflows
.github/workflows/codeql-analysis.yml, .github/workflows/linters.yml, .github/workflows/linux.yml
Updated action versions: checkout (v4→v6), setup-go (v4→v6), golangci-lint-action (v7→v9), cache (v3→v4), download-artifact (v5→v8), codecov-action (v4→v5). Reordered coverage artifact handling to use new paths and filtering logic.
Go Linter Configuration
.golangci.yml
Increased goconst rule's min-occurrences threshold from 3 to 10.
HTTP Handler & Tests
handler/handler.go, handler/handler_test.go
Added io import and EOF/ErrUnexpectedEOF error handling to return http.StatusBadRequest for truncated multipart requests. Introduced comprehensive test suite validating error handling, URI construction, IP extraction, and pool interactions.
Goroutine Pattern Refactoring
handler/uploads.go, tests/http_plugin_test.go, tests/http_plugin2_test.go, tests/http_plugin3_test.go, tests/http_plugin4_test.go
Replaced manual goroutine setup (wg.Add/Done with defer) with WaitGroup.Go pattern across multiple tests and handler code. Converted some index-based for loops to range-based loops.
Test Tracing Configuration
tests/configs/.rr-http-otel.yaml, tests/configs/.rr-http-otel2.yaml, tests/http_otlp_test.go
Replaced otel.Plugin with custom tracetestMiddleware. Removed otel configuration blocks and replaced middleware references. Introduced in-memory span collection and tracing assertions.
Dependencies
tests/go.mod, tests/php_test_files/composer.json
Updated module dependencies: context and otelhttp changed from indirect to direct; otel/v5 changed from direct to indirect. PHP composer.json: replaced guzzle6-based adapters with guzzle7-adapter.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • wolfy-j

Poem

🐰 With actions fresh and goroutines aligned,
EOF errors now gracefully defined,
Bad requests return 400, not in vain,
Traces dance through memory's refrain,
RoadRunner bounds with elegance divine!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Title clearly references the main change (HTTP bad request code instead of 500) and mentions test/code modernization, accurately summarizing the primary objectives.
Description check ✅ Passed PR description includes issue reference (#2302), clear description of changes, license acceptance, and completed checklist items; follows template structure.
Linked Issues check ✅ Passed Code changes in handler.go properly address issue #2302 by treating io.EOF/io.ErrUnexpectedEOF as HTTP 400 Bad Request for malformed multipart requests.
Out of Scope Changes check ✅ Passed All changes align with stated objectives: handler error handling for issue #2302, test modernization, OTEL in-memory storage, and CI dependency updates.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/bad-req-invalid-multipart

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (4)
tests/http_plugin2_test.go (1)

402-426: Inconsistent goroutine pattern within the same test.

TestHTTPBigResp uses the modernized wg.Go pattern for the main loop (lines 374-398) but still uses the old go func() { defer wg2.Done() ... }() pattern for the parallel HTTP requests.

Consider applying the same wg.Go pattern consistently:

♻️ Suggested refactor for consistency
-	wg2 := &sync.WaitGroup{}
-	wg2.Add(2)
-	go func() {
-		defer wg2.Done()
+	wg2 := &sync.WaitGroup{}
+	wg2.Go(func() {
 		req, err1 := http.NewRequest(http.MethodGet, "http://127.0.0.1:15399", nil)
 		require.NoError(t, err1)
 
 		r, err1 := http.DefaultClient.Do(req)
 		require.NoError(t, err1)
 		require.Equal(t, 200, r.StatusCode)
 		_ = r.Body.Close()
-	}()
+	})
 
-	go func() {
-		defer wg2.Done()
+	wg2.Go(func() {
 		req, err2 := http.NewRequest(http.MethodGet, "http://127.0.0.1:15399", nil) //nolint:noctx
 		require.NoError(t, err2)
 
 		r, err2 := http.DefaultClient.Do(req)
 		require.NoError(t, err2)
 		require.Equal(t, 200, r.StatusCode)
 		_ = r.Body.Close()
-	}()
+	})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/http_plugin2_test.go` around lines 402 - 426, The two anonymous
goroutines that manually call defer wg2.Done() should be converted to the
consistent wg2.Go pattern used earlier in TestHTTPBigResp: replace each "go
func() { defer wg2.Done() ... }()" with "wg2.Go(func() error { ... return nil
})" (keep the same request logic and require assertions inside), remove the
explicit Done calls, and return nil at the end of each wg2.Go closure so
wg2.Wait() still synchronizes correctly.
handler/handler_test.go (1)

23-33: mockPool.Exec returns nil channel on success — document or guard.

When execErr is nil, Exec returns (nil, nil). If a future test expects successful execution, reading from the nil channel will panic. Current tests are safe since they all set execErr.

Consider either documenting this limitation more explicitly or returning a closed channel for the success case:

♻️ Defensive fix for future-proofing
 func (m *mockPool) Exec(_ context.Context, _ *payload.Payload, _ chan struct{}) (chan *staticPool.PExec, error) {
+	if m.execErr != nil {
+		return nil, m.execErr
+	}
+	// Return closed channel to prevent deadlock in success path.
+	// Note: real pool behavior is more complex; extend mockPool as needed.
+	ch := make(chan *staticPool.PExec)
+	close(ch)
-	return nil, m.execErr
+	return ch, nil
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@handler/handler_test.go` around lines 23 - 33, The mockPool.Exec currently
returns (nil, nil) when execErr is nil which will cause a panic if a test tries
to read from the returned channel; update mockPool.Exec to return a non-nil,
already-closed channel on success so consumers can safely range/read from it.
Modify the mockPool.Exec implementation to check m.execErr and when nil create a
channel of *staticPool.PExec, close it, and return it with nil error; keep the
existing behavior of returning (nil, m.execErr) when execErr is non-nil. Ensure
references are to mockPool.Exec and the execErr field so the change is easy to
locate.
.github/workflows/codeql-analysis.yml (1)

48-48: Consider updating CodeQL actions for consistency.

Other actions in this workflow and across the repository are being upgraded, but github/codeql-action remains at v2. CodeQL v3 has been available; consider updating for consistency if compatible.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/codeql-analysis.yml at line 48, The workflow references
github/codeql-action/init@v2; update the CodeQL actions to v3 for consistency by
changing the `uses: github/codeql-action/init@v2` entry (and any other CodeQL
steps like `github/codeql-action/analyze`) to the corresponding `@v3` releases,
ensure any v3-required inputs or option names used by the `init` and `analyze`
steps are adjusted to match the v3 docs, and run a quick local or CI validation
to confirm the workflow syntax and behavior remain compatible.
.golangci.yml (1)

41-41: Relaxed goconst threshold from 3 to 10 occurrences.

This significantly reduces the sensitivity of the constant detection linter. While this can reduce noise (especially in test files with repeated literals), it may also miss opportunities to extract meaningful constants that appear 3-9 times.

If this change is to accommodate test patterns in this PR, consider instead adding goconst to the test file exclusions (alongside dupl, funlen, etc. at lines 71-76) rather than raising the global threshold.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.golangci.yml at line 41, Revert the global goconst threshold change (set
min-occurrences back to 3) and instead add goconst to the test-file exclusion
block used for dupl/funlen so the linter stays strict globally but ignores
repeated literals in tests; update the .golangci.yml: restore the
min-occurrences value and add "goconst" to the same exclusion/disable list (the
block alongside dupl and funlen) so only tests are exempt.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In @.github/workflows/codeql-analysis.yml:
- Line 48: The workflow references github/codeql-action/init@v2; update the
CodeQL actions to v3 for consistency by changing the `uses:
github/codeql-action/init@v2` entry (and any other CodeQL steps like
`github/codeql-action/analyze`) to the corresponding `@v3` releases, ensure any
v3-required inputs or option names used by the `init` and `analyze` steps are
adjusted to match the v3 docs, and run a quick local or CI validation to confirm
the workflow syntax and behavior remain compatible.

In @.golangci.yml:
- Line 41: Revert the global goconst threshold change (set min-occurrences back
to 3) and instead add goconst to the test-file exclusion block used for
dupl/funlen so the linter stays strict globally but ignores repeated literals in
tests; update the .golangci.yml: restore the min-occurrences value and add
"goconst" to the same exclusion/disable list (the block alongside dupl and
funlen) so only tests are exempt.

In `@handler/handler_test.go`:
- Around line 23-33: The mockPool.Exec currently returns (nil, nil) when execErr
is nil which will cause a panic if a test tries to read from the returned
channel; update mockPool.Exec to return a non-nil, already-closed channel on
success so consumers can safely range/read from it. Modify the mockPool.Exec
implementation to check m.execErr and when nil create a channel of
*staticPool.PExec, close it, and return it with nil error; keep the existing
behavior of returning (nil, m.execErr) when execErr is non-nil. Ensure
references are to mockPool.Exec and the execErr field so the change is easy to
locate.

In `@tests/http_plugin2_test.go`:
- Around line 402-426: The two anonymous goroutines that manually call defer
wg2.Done() should be converted to the consistent wg2.Go pattern used earlier in
TestHTTPBigResp: replace each "go func() { defer wg2.Done() ... }()" with
"wg2.Go(func() error { ... return nil })" (keep the same request logic and
require assertions inside), remove the explicit Done calls, and return nil at
the end of each wg2.Go closure so wg2.Wait() still synchronizes correctly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1eff3079-3238-4624-8adc-c11b710b7541

📥 Commits

Reviewing files that changed from the base of the PR and between a9524ff and 20fab26.

⛔ Files ignored due to path filters (1)
  • go.work.sum is excluded by !**/*.sum
📒 Files selected for processing (16)
  • .github/workflows/codeql-analysis.yml
  • .github/workflows/linters.yml
  • .github/workflows/linux.yml
  • .golangci.yml
  • handler/handler.go
  • handler/handler_test.go
  • handler/uploads.go
  • tests/configs/.rr-http-otel.yaml
  • tests/configs/.rr-http-otel2.yaml
  • tests/go.mod
  • tests/http_otlp_test.go
  • tests/http_plugin2_test.go
  • tests/http_plugin3_test.go
  • tests/http_plugin4_test.go
  • tests/http_plugin_test.go
  • tests/php_test_files/composer.json

@codecov
Copy link

codecov bot commented Mar 6, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 61.20%. Comparing base (a6e7375) to head (3b5e751).
⚠️ Report is 18 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff             @@
##           master     #225       +/-   ##
===========================================
+ Coverage        0   61.20%   +61.20%     
===========================================
  Files           0        6        +6     
  Lines           0      250      +250     
===========================================
+ Hits            0      153      +153     
- Misses          0       81       +81     
- Partials        0       16       +16     

☔ 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.

@rustatian rustatian merged commit 19cefe5 into master Mar 6, 2026
9 checks passed
@rustatian rustatian deleted the feature/bad-req-invalid-multipart branch March 6, 2026 11:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[💡 FEATURE REQUEST]: http plugin should response "400 Bad request" for invalid multipart request

2 participants