Skip to content

fix: restore req/res prototypes when a mounted app hands control back - #7434

Closed
santusht06 wants to merge 1 commit into
expressjs:masterfrom
santusht06:fix/restore-prototypes-router-subapp
Closed

fix: restore req/res prototypes when a mounted app hands control back#7434
santusht06 wants to merge 1 commit into
expressjs:masterfrom
santusht06:fix/restore-prototypes-router-subapp

Conversation

@santusht06

Copy link
Copy Markdown

Problem

When an express() sub-app is mounted via router.use() instead of app.use(), app.handle alters the prototypes of req and res to point to subApp.request and subApp.response, but never restores them.

Because router.use() does not wrap the sub-app in the mounted_app cleanup closure found in app.use(), the prototype swap is permanent for the remainder of the request. Any middleware or handler executed downstream (after subApp calls next() or next(err)) reads req.app, req.ip, req.secure, and req.hostname under the sub-app's configuration rather than the parent app's settings, and any prototype extensions on the parent app are lost.

Solution

Inside app.handle() in lib/application.js, capture the incoming req and res prototypes before mutating them whenever a callback is provided (indicating a mounted sub-app or delegated caller). Wrap done to restore the captured prototypes via Object.setPrototypeOf() prior to invoking callback(err).

This ensures that regardless of whether the app was mounted through app.use(), router.use(), directly invoked as middleware, or passed an error via next(err), the prototype swap is always safely restored when control returns to the caller.

Changes Made

  • lib/application.js:
    • In app.handle, wrap done when callback is present to restore req and res prototypes to their pre-invocation state upon completion.
  • test/app.use.js:
    • Added test case for prototype restoration when sub-app is mounted on a router and calls next().
    • Added test case for prototype restoration when sub-app passes an error to next(err) on a router.
    • Added test case for preserving parent custom request and response extensions across router-mounted sub-apps.

Test Plan

  • Run npm test to ensure all 1,259 test cases pass cleanly.
  • Run npm run lint to verify style compliance.

Fixes #7427

When an express() sub-app is mounted on a router via router.use(),
the request and response prototypes are altered in app.handle but
never restored upon delegating back to the caller callback.

This commit captures the incoming prototypes when a callback is
provided in app.handle and wraps the done callback to restore the
original request and response prototypes on both success and error
exit paths.

Fixes expressjs#7427
@krzysdz

krzysdz commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Duplicate of #7428

@krzysdz krzysdz marked this as a duplicate of #7428 Aug 27, 2026
@krzysdz krzysdz closed this Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

router.use() silently accepts express() sub-apps without prototype restoration

2 participants