Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NIO Promise Leak During Response Body Stream #2389

Closed
jshier opened this issue Jun 10, 2020 · 3 comments
Closed

NIO Promise Leak During Response Body Stream #2389

jshier opened this issue Jun 10, 2020 · 3 comments
Labels
bug Something isn't working
Projects

Comments

@jshier
Copy link
Contributor

jshier commented Jun 10, 2020

Steps to reproduce

Attempting to repeatedly write to a Response body stream results in a promise leak.

This route:

app.on(.GET, "chunked", ":count") { request -> Response in
    guard let count = request.parameters["count", as: Int.self], count > 0, count <= 100 else {
        return Response(status: .badRequest)
    }
    let response = Response(body: .init(stream: { writer in
        for i in 0..<count {
            writer.write(.buffer(.init(integer: i)))
        }
    }, count: 0))
    return response
}

Results in a runtime assertion about "leaking promise":

Fatal error: leaking promise created at (file: "/SourcePackages/checkouts/vapor/Sources/Vapor/HTTP/Server/HTTPServerHandler.swift", line: 53): file /SourcePackages/checkouts/vapor/Sources/Vapor/HTTP/Server/HTTPServerHandler.swift, line 53
2020-06-10 17:49:10.639020-0400 firewalk[70185:2526213] Fatal error: leaking promise created at (file: "/SourcePackages/checkouts/vapor/Sources/Vapor/HTTP/Server/HTTPServerHandler.swift", line: 53): file /SourcePackages/checkouts/vapor/Sources/Vapor/HTTP/Server/HTTPServerHandler.swift, line 53

Which is this code:

func serialize(_ response: Response, for request: Request, context: ChannelHandlerContext) {
    switch request.version.major {
    case 2:
        context.write(self.wrapOutboundOut(response), promise: nil)
    default:
        response.headers.add(name: .connection, value: request.isKeepAlive ? "keep-alive" : "close")
        let done = context.write(self.wrapOutboundOut(response)) // Leaked
        if !request.isKeepAlive {
            done.whenComplete { _ in
                context.close(mode: .output, promise: nil)
            }
        }
    }
}

Expected behavior

No leaked promises.

Actual behavior

Runtime assertion.

Environment

  • Vapor Framework version: 4.8.0
  • Vapor Toolbox version: N/A
  • OS version: 10.15.5
@Joannis
Copy link
Member

Joannis commented Jun 22, 2020

Hello @jshier, coincidentally @Wessi also ran into this issue in #2390 and provides a solution. I think this is a problem that should be primarily clarified in a better error message.

@jshier
Copy link
Contributor Author

jshier commented Jun 22, 2020

Yes, I filed this bug on his suggestion during a discussion of streaming bodies. He later did his own experiments and hit the same issue.

@tanner0101 tanner0101 added this to To Do in Vapor 4 via automation Jul 14, 2020
@tanner0101
Copy link
Member

This should be fixed by https://github.com/vapor/vapor/releases/tag/4.11.0, lmk if that works for you.

Vapor 4 automation moved this from To Do to Done Jul 14, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
Vapor 4
  
Done
Development

No branches or pull requests

3 participants