Skip to content

🎯 Close Request Body on Authentication Middleware Early-Return to Prevent Connection Leaks #1

Description

@madalynerlge2

📝 Description

Under heavy load, services utilizing the authentication middleware (such as BasicAuth or custom token-based auth handlers) experience connection leaks and file descriptor exhaustion. This occurs because when authentication fails, the middleware aborts the request chain early (e.g., returning a 401 Unauthorized status) but does not explicitly close or drain the incoming c.Request.Body.

While Go's net/http server attempts to automatically close the request body after the handler returns, under high concurrency and with large request payloads, the connection can hang or leak if the body is not explicitly closed or drained upon an early abort. Explicitly closing the request body on early-return ensures that the underlying TCP connection can be recycled immediately by the server.

🎯 Acceptance Criteria

  • When an authentication middleware (e.g., BasicAuth(), BasicAuthForRealm()) aborts the request early due to failed credentials, the request body (c.Request.Body) must be explicitly closed.
  • The fix must handle cases where c.Request or c.Request.Body is nil to prevent nil-pointer dereference panics.
  • Ensure that connection reuse (Keep-Alive) behaves correctly for aborted requests where the body is closed early.
  • No regression in standard request processing where authentication succeeds.

🛠️ Technical Specifications & Context

The issue resides in the middleware implementation, specifically within the authentication handlers.

Key Files to Modify:

  • auth.go (or wherever BasicAuth / BasicAuthForRealm are defined)

Suggested Implementation:

In the authentication middleware, when credentials fail and c.AbortWithStatus(...) is called, ensure the request body is closed:

if c.Request.Body != nil {
    c.Request.Body.Close()
}

Alternatively, if this is a broader issue across all early aborts, consider adding a mechanism in context.go inside the Abort or AbortWithStatus methods, or document/implement a middleware that drains and closes the body for any aborted request. However, targeting the auth middleware directly is the safest first step:

// Example in auth.go
func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc {
    // ... setup ...
    return func(c *Context) {
        // Search user/pass
        if user, passwords, ok := c.Request.BasicAuth(); ok {
            if password, ok := accounts[user]; ok && secureCompare(password, passwords) {
                c.Set(AuthUserKey, user)
                return
            }
        }

        // Credentials failed
        c.Header("WWW-Authenticate", realm)
        if c.Request.Body != nil {
            c.Request.Body.Close() // Explicitly close to prevent connection leaks
        }
        c.AbortWithStatus(http.StatusUnauthorized)
    }
}

🧪 Verification & Testing

Automated Tests

  1. Add a test case in auth_test.go that simulates a request with a non-empty body to an authenticated endpoint with invalid credentials.
  2. Verify that the request body is closed (you can wrap the ReadCloser in a mock to assert that Close() was called).

Manual Verification under Load

  1. Run a test Gin server with BasicAuth enabled.
  2. Use a load testing tool like wrk or hey to send a high volume of unauthorized requests with a payload:
    hey -n 10000 -c 100 -m POST -d "large_dummy_payload" http://localhost:8080/protected
  3. Monitor open file descriptors/connections using lsof or netstat to ensure they do not leak or remain in CLOSE_WAIT indefinitely.

Opire Bounty


This repo is using Opire - what does it mean? 👇
💵 Everyone can add rewards for this issue commenting /reward 100 (replace 100 with the amount).
🕵️‍♂️ If someone starts working on this issue to earn the rewards, they can comment /try to let everyone know!
🙌 And when they open the PR, they can comment /claim #1 either in the PR description or in a PR's comment.

🪙 Also, everyone can tip any user commenting /tip 20 @madalynerlge2 (replace 20 with the amount, and @madalynerlge2 with the user to tip).

📖 If you want to learn more, check out our documentation.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions