Skip to content

Add Rack::TempfileReaper to the endpoint middleware stack - #3041

Open
anyingiit wants to merge 1 commit into
ruby-grape:masterfrom
anyingiit:add-tempfile-reaper
Open

anyingiit wants to merge 1 commit into
ruby-grape:masterfrom
anyingiit:add-tempfile-reaper

Conversation

@anyingiit

@anyingiit anyingiit commented Oct 22, 2026 •

Copy link
Copy Markdown

Requested by winshyon

Description

Multipart uploads parsed by Rack::Multipart create tempfiles that are only cleaned up when something runs Rack::TempfileReaper. Rails includes it by default, but a Grape API served directly by Rack (or by any non-Rails host) left those tempfiles on disk until GC/process exit, as described in #2487.

This adds Rack::TempfileReaper as the outermost middleware in Grape::Endpoint#build_stack (before Rack::Head and Grape::Middleware::Error), so the files are closed and unlinked once the response body is closed. Because it sits outside the error middleware, files are also reaped when the endpoint ends with error!/a rescued exception, and the reaper itself closes them if an exception escapes.

Notes for reviewers:

  • Mounted inside Rails (or another app that already uses the reaper): the middleware is idempotent — it does env['rack.tempfiles'] ||= [] and both reapers just close! the same tempfiles (Tempfile#close! on an already closed/unlinked file is a no-op). I checked this with an outer Rack::TempfileReaper wrapping a Grape API on Rack 3.2.7 and Rack 2.2.24: 201 response, tempfile unlinked, no error.
  • The cost is one extra Rack::BodyProxy per request on the endpoint stack. I put it in the endpoint stack (rather than the API instance) to mirror Rack::Head, which already lives there; happy to move it if you prefer it elsewhere.
  • The spec lives next to the existing Tempfile upload spec in spec/grape/integration/rack_spec.rb.

Motivation / disclosure: I had some spare AI-assistant quota (Claude Code) and am using it to try to
help projects with open good-first-issues. The change was prepared with Claude Code and verified as listed
below. If it doesn't fit, isn't up to your bar, or you'd simply rather not take it — please feel free to
close it, no hard feelings at all 🙂

Related issue

Closes #2487

Checklist

  • Tests pass locally (bundle exec rake: RuboCop 358 files, no offenses; RSpec 3155 examples, 0 failures on current master. Before rebasing, also rspec spec --exclude-pattern=... with gemfiles/rack_2_2.gemfile (3079 examples, 0 failures) and gemfiles/rails_8_1.gemfile (3077 examples, 0 failures). The 2 new examples in spec/grape/integration/rack_spec.rb fail on master without the change on both Rack 3.2 and Rack 2.2)
  • CHANGELOG.md is updated (if applicable) — one line under 4.1.0 Features
  • Documentation is updated (if applicable) — n/a (no documented behavior changes; no UPGRADING entry since it is not a contract break)

🤖 Generated with Claude Code

Multipart uploads parsed by Rack::Multipart create tempfiles that were
left open on disk after the request unless the host app (e.g. Rails)
already ran Rack::TempfileReaper. Use it in every endpoint's stack so
they are closed and unlinked once the response body is closed.

Fixes ruby-grape#2487
@anyingiit
anyingiit force-pushed the add-tempfile-reaper branch from 22f3f9f to f2dbebb Compare October 22, 2026 19:14
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Danger Report

No issues found.

View run

@dblock

dblock commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

We support multipart uploads by default, don’t we? If I take Grape "as is" right now with an API that doesn’t care about multiple downloads and just send it a billion multipart uploads, will it not cleanup those temp files? If so I think we need this on by default, otherwise it should just be documented.

Answer: standalone Grape supports multipart uploads but currently does not guarantee cleanup of upload tempfiles when the response closes. When request handling triggers multipart parsing, Rack creates tempfiles; without Rack::TempfileReaper, their cleanup relies on garbage collection/finalizers rather than response completion. This does not require the endpoint to explicitly declare an upload parameter, although merely sending a multipart request does not necessarily cause parsing. Rails already includes the reaper. Therefore, cleanup should be enabled by default in Grape rather than only documented as an opt-in.

— AI posting on behalf of @dblock.

@dblock

dblock commented Oct 23, 2026

Copy link
Copy Markdown
Member

I fixed danger-changelog in 0.10.1, dblock/danger-changelog#85

@dblock
dblock requested a review from ericproulx October 23, 2026 17:20
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.

Include Rack::TempFileReaper in Grape ?

2 participants