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

Assertion 'Response body stream writer deinitialized before .end or .error was sent.' hit in Vapor when using body.collect in Middleware & streaming #2933

Open
weissi opened this issue Jan 9, 2023 · 4 comments
Labels
bug Something isn't working

Comments

@weissi
Copy link
Contributor

weissi commented Jan 9, 2023

I've got a Vapor app which has a small Middleware which just attempts to print the first bits of the incoming body and then just continue the request chain. This appears to work fine until I use a route (/echo) which uses body streaming.

When I then post to that route, Vapor hits this assertion

assert(self.isComplete, "Response body stream writer deinitialized before .end or .error was sent.")
var env = try Environment.detect()
try LoggingSystem.bootstrap(from: &env)

let app = Application(env)
defer { app.shutdown() }

struct MyMiddleware: Middleware {
    func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture<Response> {
        request.body.collect(max: 1).whenSuccess { print("first body bits", $0.map { String(buffer: $0) } ?? "n/a") }
        return next.respond(to: request)
    }
}

app.middleware.use(MyMiddleware(), at: .beginning)

app.on(.POST, "echo", body: .stream)  { request -> Response in
    let r = Response(body: .init(stream: { writer in
        request.body.drain { body in
            switch body {
            case .buffer(let buffer):
                return writer.write(.buffer(buffer))
            case .error(let error):
                return writer.write(.error(error))
            case .end:
                return writer.write(.end)
            }
        }
    }))
    r.headers.add(name: "content-type", value: "application/octet-stream")
    return r
}

try app.run()

To trigger the crash, you can post anything that's like 2000 bytes or more usually. For example

curl -v -d "$(python -c 'print 100000*"x"')" http://localhost:8080/echo

When the middleware isn't used, this appears to work ok.


Vapor: 4.68.0

@weissi weissi added the bug Something isn't working label Jan 9, 2023
@mkll
Copy link
Sponsor

mkll commented Jan 9, 2023

You can't use request.body.collect() while streaming. Your route handler is correct, your middleware is not.

@weissi
Copy link
Contributor Author

weissi commented Jan 9, 2023

@mkll Hmm, but the middleware doesn't really know anything about the request, right? Basically this would mean that any middleware that looks into the body at all is incompatible with all routes that stream, correct?

Because body.drain from a middleware also doesn't work.

If that's all working as designed, then at least a better error message would be good, no?

@Joannis
Copy link
Member

Joannis commented Jan 9, 2023

Interesting use case. I think this should be supported nonetheless.

@weissi
Copy link
Contributor Author

weissi commented Jan 9, 2023

Thanks @0xTim, this might be related to #2742

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
None yet
Development

No branches or pull requests

3 participants