Skip to content

🐛 fix: Always close form file#3786

Merged
ReneWerner87 merged 5 commits intogofiber:mainfrom
arturmelanchyk:reader-close
Oct 8, 2025
Merged

🐛 fix: Always close form file#3786
ReneWerner87 merged 5 commits intogofiber:mainfrom
arturmelanchyk:reader-close

Conversation

@arturmelanchyk
Copy link
Contributor

Description

Currently file doesn't get closed if any of errors at lines 299 or 303 return:

fiber/client/hooks.go

Lines 288 to 308 in e8f639a

// If reader is not set, open the file.
if v.reader == nil {
v.reader, err = os.Open(v.path)
if err != nil {
return fmt.Errorf("open file error: %w", err)
}
}
// Create form file and copy the content.
w, err := mw.CreateFormFile(v.fieldName, v.name)
if err != nil {
return fmt.Errorf("create file error: %w", err)
}
if _, err := io.CopyBuffer(w, v.reader, *fileBuf); err != nil {
return fmt.Errorf("failed to copy file data: %w", err)
}
if err := v.reader.Close(); err != nil {
return fmt.Errorf("close file error: %w", err)
}

Changes introduced

List the new features or adjustments introduced in this pull request. Provide details on benchmarks, documentation updates, changelog entries, and if applicable, the migration guide.

  • Benchmarks: Describe any performance benchmarks and improvements related to the changes.
  • Documentation Update: Detail the updates made to the documentation and links to the changed files.
  • Changelog/What's New: Include a summary of the additions for the upcoming release notes.
  • Migration Guide: If necessary, provide a guide or steps for users to migrate their existing code to accommodate these changes.
  • API Alignment with Express: Explain how the changes align with the Express API.
  • API Longevity: Discuss the steps taken to ensure that the new or updated APIs are consistent and not prone to breaking changes.
  • Examples: Provide examples demonstrating the new features or changes in action.

Type of change

Please delete options that are not relevant.

  • New feature (non-breaking change which adds functionality)
  • Enhancement (improvement to existing features and functionality)
  • Documentation update (changes to documentation)
  • Performance improvement (non-breaking change which improves efficiency)
  • Code consistency (non-breaking change which improves code reliability and robustness)

Checklist

Before you submit your pull request, please make sure you meet these requirements:

  • Followed the inspiration of the Express.js framework for new functionalities, making them similar in usage.
  • Conducted a self-review of the code and provided comments for complex or critical parts.
  • Updated the documentation in the /docs/ directory for Fiber's documentation.
  • Added or updated unit tests to validate the effectiveness of the changes or new features.
  • Ensured that new and existing unit tests pass locally with the changes.
  • Verified that any new dependencies are essential and have been agreed upon by the maintainers/community.
  • Aimed for optimal performance with minimal allocations in the new code.
  • Provided benchmarks for the new code to analyze and improve upon.

Commit formatting

Please use emojis in commit messages for an easy way to identify the purpose or intention of a commit. Check out the emoji cheatsheet here: CONTRIBUTING.md

Signed-off-by: Artur Melanchyk <13834276+arturmelanchyk@users.noreply.github.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 5, 2025

Walkthrough

Refactors file upload handling in client/hooks.go by extracting logic into a new helper addFormFile, updating error messages and flow. Adds tests in client/hooks_test.go to validate error cases for file-based request bodies, including missing files and missing file names.

Changes

Cohort / File(s) Summary of changes
Parser file handling refactor
client/hooks.go
Extracted file processing into private helper addFormFile; adjusted loop to delegate file form creation and copying; standardized error message to "open file error"; ensured reader closure via defer; streamlined control flow and error propagation.
Tests for file error handling
client/hooks_test.go
Added subtests for "file body open error" and "file body missing path and name"; imported filepath; validated error message content and ErrFileNoName for malformed file inputs.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant C as Caller
  participant P as parserRequestBodyFile
  participant H as addFormFile
  participant FS as FileSystem
  participant MF as MultipartWriter

  C->>P: parserRequestBodyFile(req)
  loop For each file f
    P->>H: addFormFile(form, f)
    alt Reader is nil
      H->>FS: Open(f.path)
      FS-->>H: File handle or error
      alt Open error
        H-->>P: error "open file error"
        P-->>C: error
      else Open ok
        note right of H: defer close reader
      end
    end
    H->>MF: CreateFormFile(f.name, f.filename)
    MF-->>H: formFile or error
    alt CreateFormFile error
      H-->>P: error
      P-->>C: error
    else
      H->>MF: Copy(reader -> formFile)
      alt Copy error
        H-->>P: error
        P-->>C: error
      else
        H-->>P: nil
      end
    end
  end
  P-->>C: nil
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10–15 minutes

Poem

I thump my paw at files that hide,
With paths that vanish, names denied—
Now helper hops to open, close,
And errors bloom like tidy rows.
I twitch my whiskers, tests aligned,
A clean refactor, carrot-signed. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (3 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description follows the template structure but leaves the “Changes introduced” section filled with placeholder checkboxes instead of summarizing the actual refactored file-handling logic and new tests, and it omits any “Fixes #” issue reference. Please update the “Changes introduced” section with concrete bullet points describing the refactored addFormFile helper, updated error handling, and added subtests, include the relevant issue reference using “Fixes #” if applicable, and check off or remove any checklist items to reflect what was actually completed.
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ⚠️ Warning The title clearly summarizes the main change of ensuring the form file is always closed but includes an unnecessary emoji prefix, which counts as noise under the title guidelines. Please remove the emoji so the title reads simply “fix: Always close form file” or similar concise phrasing without emojis.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@codecov
Copy link

codecov bot commented Oct 5, 2025

Codecov Report

❌ Patch coverage is 79.16667% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.65%. Comparing base (6f81d52) to head (8e61167).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
client/hooks.go 79.16% 3 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3786      +/-   ##
==========================================
- Coverage   91.67%   91.65%   -0.03%     
==========================================
  Files         113      113              
  Lines       11959    11966       +7     
==========================================
+ Hits        10964    10968       +4     
- Misses        731      734       +3     
  Partials      264      264              
Flag Coverage Δ
unittests 91.65% <79.16%> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

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

Signed-off-by: Artur Melanchyk <13834276+arturmelanchyk@users.noreply.github.com>
Signed-off-by: Artur Melanchyk <13834276+arturmelanchyk@users.noreply.github.com>
(cherry picked from commit 25df78f)
Signed-off-by: Artur Melanchyk <13834276+arturmelanchyk@users.noreply.github.com>
@arturmelanchyk arturmelanchyk marked this pull request as ready for review October 5, 2025 18:47
@arturmelanchyk arturmelanchyk requested a review from a team as a code owner October 5, 2025 18:47
@gaby gaby changed the title fix: always close form file 🐛 fix: Always close form file Oct 5, 2025
@gaby gaby requested a review from Copilot October 5, 2025 22:39
@gaby
Copy link
Member

gaby commented Oct 5, 2025

/gemini review

@gaby gaby added the v3 label Oct 5, 2025
@gaby gaby added this to v3 Oct 5, 2025
@gaby gaby added this to the v3 milestone Oct 5, 2025
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request effectively addresses a potential file descriptor leak by refactoring the file handling logic to use a defer statement, ensuring that file readers are always closed. The introduction of the addFormFile function improves code modularity. However, this change introduces a regression by ignoring the error returned by f.reader.Close(), which was previously handled. I've provided a suggestion to correctly handle this error while retaining the benefits of using defer.

Comment on lines +296 to 319
func addFormFile(mw *multipart.Writer, f *File, fileBuf *[]byte) error {
// If reader is not set, open the file.
if f.reader == nil {
var err error
f.reader, err = os.Open(f.path)
if err != nil {
return fmt.Errorf("create file error: %w", err)
return fmt.Errorf("open file error: %w", err)
}
}

if _, err := io.CopyBuffer(w, v.reader, *fileBuf); err != nil {
return fmt.Errorf("failed to copy file data: %w", err)
}
// Ensure the file reader is always closed after copying.
defer f.reader.Close() //nolint:errcheck // not needed

if err := v.reader.Close(); err != nil {
return fmt.Errorf("close file error: %w", err)
}
// Create form file and copy the content.
w, err := mw.CreateFormFile(f.fieldName, f.name)
if err != nil {
return fmt.Errorf("create file error: %w", err)
}

if _, err := io.CopyBuffer(w, f.reader, *fileBuf); err != nil {
return fmt.Errorf("failed to copy file data: %w", err)
}

return nil
Copy link
Contributor

Choose a reason for hiding this comment

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

high

While using defer is a great way to ensure the file is closed, ignoring the error from Close() is a regression from the previous behavior and is generally not recommended. The Close method can return important errors, for example, if there was an issue with the underlying file system. The nolint:errcheck comment states it's not needed, but the previous implementation did check this error.

I suggest modifying the function to handle the error from Close() while still benefiting from defer. This can be achieved using a named return value.

func addFormFile(mw *multipart.Writer, f *File, fileBuf *[]byte) (err error) {
	// If reader is not set, open the file.
	if f.reader == nil {
		f.reader, err = os.Open(f.path)
		if err != nil {
			return fmt.Errorf("open file error: %w", err)
		}
	}

	// Ensure the file reader is always closed after copying.
	defer func() {
		if closeErr := f.reader.Close(); closeErr != nil && err == nil {
			err = fmt.Errorf("close file error: %w", closeErr)
		}
	}()

	// Create form file and copy the content.
	var w io.Writer
	w, err = mw.CreateFormFile(f.fieldName, f.name)
	if err != nil {
		return fmt.Errorf("create file error: %w", err)
	}

	if _, err = io.CopyBuffer(w, f.reader, *fileBuf); err != nil {
		return fmt.Errorf("failed to copy file data: %w", err)
	}

	return nil
}

Copy link
Contributor

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

Fixes missing file Close calls when errors occur during multipart form file handling to ensure readers are always closed.

  • Extracts file handling into addFormFile with a deferred Close to guarantee cleanup on all error paths.
  • Adds tests for open failure and missing path/name validation.

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
client/hooks.go Refactors file handling into addFormFile, adding deferred Close; adjusts loop variables and field defaults.
client/hooks_test.go Adds tests for open error and missing path/name cases in file upload parsing.

@ReneWerner87 ReneWerner87 merged commit c2062a2 into gofiber:main Oct 8, 2025
14 of 15 checks passed
@github-project-automation github-project-automation bot moved this to Done in v3 Oct 8, 2025
Abhirup-99 pushed a commit to Abhirup-99/fiber that referenced this pull request Nov 3, 2025
Co-authored-by: Artur Melanchyk <13834276+arturmelanchyk@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants