vapor/vapor

Precondition failure when write request body into file asyncronously.

alex1704 opened this issue · 10 comments

Describe the bug

I'm implementing upload functionality with HTTPBodyStreamStrategy == .stream. Inside request handler i'm writing data to file in chunks ayncronously. When large file is uploaded precondition failure is executed.

Here is my implementation:

import Vapor
import NIO

func routes(_ app: Application) throws {
    app.on(.POST, "upload", body: .stream, use: { req async throws in
        var offset: Int64 = 0
        let uploads = req.application.directory.workingDirectory.appending("uploads")
        let outPath = "\(uploads)/file"
        try? FileManager.default.createDirectory(atPath: uploads, withIntermediateDirectories: false)
        try? FileManager.default.removeItem(atPath: outPath)
        let fileHandle = try NIOFileHandle(path: outPath, mode: .write, flags: .allowFileCreation())

        for try await buffer in req.body {
            try await req.application.fileio.write(fileHandle: fileHandle,
                                                   toOffset: offset,
                                                   buffer: buffer,
                                                   eventLoop: req.eventLoop).get()
            offset += Int64(buffer.readableBytes)
        }

        try fileHandle.close()
        return "ok"
    })
}

To Reproduce

  1. create large file (600M): mkfile -n 600m file
  2. curl -X POST 'http://localhost:8080/upload' --data-binary '@file'
  3. see error

error

Also i noticed that error appear after repeating curl request several times on smaller files, like 100M.

Expected behavior

No error should happen.
If i use futures implementation, everything works fine.

Environment

  • Vapor Framework version: 4.74.1
  • Vapor Toolbox version: 18.6.0
  • OS version: macOS 13.2 (22D49)

This should now be fixed, feel free to reopen if not!

@0xTim, still can reproduce. I created hello world project with upload endpoint. Additionally added bigFileTest.sh script to replicate the issue.
My current environment:

  • Vapor:
    • framework: 4.86.2
    • toolbox: 18.7.4
  • OS: 13.6 (22G120)
    helloVapor.zip
tib commented

I can confirm, this bug still exists in Vapor 4.92.1, any ideas how to fix it? 🤔

@tib does the async-streaming-body-client-disconnect branch fix it?

@tib does the async-streaming-body-client-disconnect branch fix it?

Can't find the branch

@hsharghi we've just released https://github.com/vapor/vapor/releases/tag/4.92.3 which has the fix in it

@alex1704 do you want to confirm it's fixed?

@0xTim sure, will test it later.

@0xTim The issue still exists

Vapor/RequestBody+Concurrency.swift:34: Fatal error

preconditionFailure()              NIO-SGLTN-0-#5 (10): Fatal error

yep, can confirm hsharghi message, bug still persists in 4.92.3 version.