Search code examples
scalaakka-http

Akka-Http File Upload sending response before request is completely read


I have the following route in my akka-http server:

def tempDestination(fileInfo: FileInfo): File = {
    log.info(s"File info for upload: [$fileInfo]")
    File.createTempFile(fileInfo.fileName, ".tmp")
}
...
post {
    log.info("Inside the file upload path")
    fileUpload("zip") {
      case (fileInfo, bytes) =>
        val dest = tempDestination(fileInfo)
        val uploadedF: Future[(FileInfo, File)] =
          bytes
            .runWith(FileIO.toPath(dest.toPath))
            .map(_ => (fileInfo, dest))

        onSuccess(uploadedF) { (info, file) =>
            val fileCreated: Future[MyFile] = (fileRegistryActor ? NewFile(file)).mapTo[MyFile]
            complete((StatusCodes.Created, fileCreated))
        }
    }
}

After uploading a file to this via a form, I see this in the logs:

Sending an 2xx 'early' response before end of request was received... Note that the connection will be closed after this response. Also, many clients will not read early responses! Consider only issuing this response after the request data has been completely read!

I thought that the uploadedF Future would not be completed until the Sink's IOResult was created and completed. What am I missing? And what's the proper mechanism to determine whether my whole request has been read AND my file has been completely written to disk?


Solution

  • So what appeared to solve this for me was changing the route from just post to post & extractRequest.

    def tempDestination(fileInfo: FileInfo): File = {
        log.info(s"File info for upload: [$fileInfo]")
        File.createTempFile(fileInfo.fileName, ".tmp")
    }
    ...
    (post & extractRequest) { _ =>
        log.info("Inside the file upload path")
        fileUpload("zip") {
          case (fileInfo, bytes) =>
            val dest = tempDestination(fileInfo)
            val uploadedF: Future[(FileInfo, File)] =
              bytes
                .runWith(FileIO.toPath(dest.toPath))
                .map(_ => (fileInfo, dest))
    
            onSuccess(uploadedF) { (info, file) =>
                val fileCreated: Future[MyFile] = (fileRegistryActor ? NewFile(file)).mapTo[MyFile]
                complete((StatusCodes.Created, fileCreated))
            }
        }
    }