Skip to content

Commit

Permalink
fix: Use NIO for creating tmp files (#4261)
Browse files Browse the repository at this point in the history
  • Loading branch information
johanandren authored May 5, 2023
1 parent 053cbfb commit da86304
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@

package akka.http.scaladsl.marshallers.xml

import java.io.File
import java.nio.file.Files

import org.xml.sax.SAXParseException

import scala.xml.NodeSeq
import scala.concurrent.{ Await, Future }
import scala.concurrent.duration._
import org.scalatest.Inside
import akka.util.ByteString
import akka.http.scaladsl.model.MediaTypes._
import akka.http.scaladsl.model._
import akka.http.scaladsl.testkit.ScalatestRouteTest
import akka.http.scaladsl.unmarshalling.Unmarshal
import akka.http.scaladsl.model._
import akka.testkit._
import MediaTypes._
import akka.http.scaladsl.unmarshalling.Unmarshaller.UnsupportedContentTypeException
import akka.testkit._
import akka.util.ByteString
import org.scalatest.Inside
import org.scalatest.freespec.AnyFreeSpec
import org.scalatest.matchers.should.Matchers
import org.xml.sax.SAXParseException

import java.io.File
import java.nio.file.Files
import scala.concurrent.duration._
import scala.concurrent.Await
import scala.concurrent.Future
import scala.xml.NodeSeq

class ScalaXmlSupportSpec extends AnyFreeSpec with Matchers with ScalatestRouteTest with Inside {
import ScalaXmlSupport._
Expand Down Expand Up @@ -98,7 +98,7 @@ class ScalaXmlSupportSpec extends AnyFreeSpec with Matchers with ScalatestRouteT
}

def withTempFile[T](content: String)(f: File => T): T = {
val file = File.createTempFile("xxe", ".txt")
val file = Files.createTempFile("xxe", ".txt").toFile
try {
Files.write(file.toPath, content.getBytes("UTF-8"))
f(file)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,21 @@
package akka.http.scaladsl.server
package directives

import java.io.File

import akka.http.impl.util._
import akka.http.scaladsl.model.MediaTypes._
import akka.http.scaladsl.model.Uri.Path
import akka.http.scaladsl.model._
import akka.http.scaladsl.model.headers._
import akka.http.scaladsl.settings.RoutingSettings
import akka.http.scaladsl.testkit.RouteTestTimeout
import akka.testkit._
import org.scalatest.Inside
import org.scalatest.Inspectors

import java.io.File
import java.nio.file.Files
import scala.concurrent.duration._
import scala.util.Properties
import org.scalatest.{ Inside, Inspectors }
import akka.http.scaladsl.model.MediaTypes._
import akka.http.scaladsl.model._
import akka.http.scaladsl.model.headers._
import akka.http.impl.util._
import akka.http.scaladsl.model.Uri.Path
import akka.testkit._

class FileAndResourceDirectivesSpec extends RoutingSpec with Inspectors with Inside {

Expand Down Expand Up @@ -48,7 +49,7 @@ class FileAndResourceDirectivesSpec extends RoutingSpec with Inspectors with Ins
Get() ~> getFromFile(Properties.javaHome) ~> check { handled shouldEqual false }
}
"return the file content with the MediaType matching the file extension" in {
val file = File.createTempFile("akka Http Test", ".PDF")
val file = Files.createTempFile("akka Http Test", ".PDF").toFile
try {
writeAllText("This is PDF", file)
Get() ~> getFromFile(file.getPath) ~> check {
Expand All @@ -60,7 +61,7 @@ class FileAndResourceDirectivesSpec extends RoutingSpec with Inspectors with Ins
} finally file.delete
}
"return the file content with MediaType 'application/octet-stream' on unknown file extensions" in {
val file = File.createTempFile("akkaHttpTest", null)
val file = Files.createTempFile("akkaHttpTest", null).toFile
try {
writeAllText("Some content", file)
Get() ~> getFromFile(file) ~> check {
Expand All @@ -71,7 +72,7 @@ class FileAndResourceDirectivesSpec extends RoutingSpec with Inspectors with Ins
}

"return a single range from a file" in {
val file = File.createTempFile("akkaHttpTest", null)
val file = Files.createTempFile("akkaHttpTest", null).toFile
try {
writeAllText("ABCDEFGHIJKLMNOPQRSTUVWXYZ", file)
Get() ~> addHeader(Range(ByteRange(0, 10))) ~> getFromFile(file) ~> check {
Expand All @@ -83,7 +84,7 @@ class FileAndResourceDirectivesSpec extends RoutingSpec with Inspectors with Ins
}

"return multiple ranges from a file at once" in {
val file = File.createTempFile("akkaHttpTest", null)
val file = Files.createTempFile("akkaHttpTest", null).toFile
try {
writeAllText("ABCDEFGHIJKLMNOPQRSTUVWXYZ", file)
val rangeHeader = Range(ByteRange(1, 10), ByteRange.suffix(10))
Expand All @@ -99,7 +100,7 @@ class FileAndResourceDirectivesSpec extends RoutingSpec with Inspectors with Ins
}

"properly handle zero-byte files" in {
val file = File.createTempFile("akkaHttpTest", null)
val file = Files.createTempFile("akkaHttpTest", null).toFile
try {
Get() ~> getFromFile(file) ~> check {
mediaType shouldEqual NoMediaType
Expand All @@ -109,7 +110,7 @@ class FileAndResourceDirectivesSpec extends RoutingSpec with Inspectors with Ins
}

"support precompressed files with registered MediaType" in {
val file = File.createTempFile("akkaHttpTest", ".svgz")
val file = Files.createTempFile("akkaHttpTest", ".svgz").toFile
try {
writeAllText("123", file)
Get() ~> getFromFile(file) ~> check {
Expand All @@ -121,7 +122,7 @@ class FileAndResourceDirectivesSpec extends RoutingSpec with Inspectors with Ins
}

"support files with registered MediaType and .gz suffix" in {
val file = File.createTempFile("akkaHttpTest", ".js.gz")
val file = Files.createTempFile("akkaHttpTest", ".js.gz").toFile
try {
writeAllText("456", file)
Get() ~> getFromFile(file) ~> check {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,20 @@ import java.io.File
import akka.NotUsed
import akka.http.scaladsl.model.{ Multipart, _ }
import akka.http.scaladsl.server.{ MissingFormFieldRejection, Route, RoutingSpec }
import akka.http.scaladsl.model.Multipart
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.MissingFormFieldRejection
import akka.http.scaladsl.server.Route
import akka.http.scaladsl.server.RoutingSpec
import akka.http.scaladsl.testkit.RouteTestTimeout
import akka.stream.scaladsl.Source
import akka.util.ByteString
import akka.testkit._
import akka.util.ByteString
import org.scalatest.concurrent.Eventually

import java.io.File
import java.nio.file.Files
import scala.annotation.nowarn
import scala.concurrent.Future
import scala.concurrent.duration._
Expand All @@ -34,7 +42,7 @@ class FileUploadDirectivesSpec extends RoutingSpec with Eventually {
@volatile var file: Option[File] = None

def tempDest(fileInfo: FileInfo): File = {
val dest = File.createTempFile("akka-http-FileUploadDirectivesSpec", ".tmp")
val dest = Files.createTempFile("akka-http-FileUploadDirectivesSpec", ".tmp").toFile
file = Some(dest)
dest
}
Expand Down Expand Up @@ -91,7 +99,7 @@ class FileUploadDirectivesSpec extends RoutingSpec with Eventually {
@volatile var files: Seq[File] = Nil

def tempDest(fileInfo: FileInfo): File = {
val dest = File.createTempFile("akka-http-FileUploadDirectivesSpec", ".tmp")
val dest = Files.createTempFile("akka-http-FileUploadDirectivesSpec", ".tmp").toFile
files = files :+ dest
dest
}
Expand Down Expand Up @@ -477,13 +485,33 @@ class FileUploadDirectivesSpec extends RoutingSpec with Eventually {

/** Mock Path implementation that allows to create a FileChannel that fails writes */
class MockFailingWritePath extends java.nio.file.Path { selfPath =>
import java.lang
import java.net.URI
import java.nio.{ ByteBuffer, MappedByteBuffer }
import java.nio.channels.{ FileChannel, FileLock, ReadableByteChannel, SeekableByteChannel, WritableByteChannel }
import java.nio.file.attribute.{ BasicFileAttributes, FileAttribute, FileAttributeView, UserPrincipalLookupService }
import java.nio.ByteBuffer
import java.nio.MappedByteBuffer
import java.nio.channels.FileChannel
import java.nio.channels.FileLock
import java.nio.channels.ReadableByteChannel
import java.nio.channels.SeekableByteChannel
import java.nio.channels.WritableByteChannel
import java.nio.file.AccessMode
import java.nio.file.CopyOption
import java.nio.file.DirectoryStream
import java.nio.file.FileStore
import java.nio.file.FileSystem
import java.nio.file.LinkOption
import java.nio.file.OpenOption
import java.nio.file.Path
import java.nio.file.PathMatcher
import java.nio.file.WatchEvent
import java.nio.file.WatchKey
import java.nio.file.WatchService
import java.nio.file.attribute.BasicFileAttributes
import java.nio.file.attribute.FileAttribute
import java.nio.file.attribute.FileAttributeView
import java.nio.file.attribute.UserPrincipalLookupService
import java.nio.file.spi.FileSystemProvider
import java.nio.file.{ AccessMode, CopyOption, DirectoryStream, FileStore, FileSystem, LinkOption, OpenOption, Path, PathMatcher, WatchEvent, WatchKey, WatchService }
import java.{ lang, util }
import java.util

@nowarn("msg=never used")
override def getFileSystem: FileSystem =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,26 @@

package akka.http.scaladsl.server.directives

import java.io.File
import akka.Done
import akka.annotation.ApiMayChange
import akka.dispatch.ExecutionContexts
import akka.http.scaladsl.server.{ Directive, Directive1, MissingFormFieldRejection }
import akka.http.scaladsl.model.{ ContentType, Multipart }
import akka.util.ByteString

import scala.collection.immutable
import scala.concurrent.{ Future, Promise }
import akka.stream.scaladsl._
import akka.http.javadsl
import akka.http.scaladsl.model.ContentType
import akka.http.scaladsl.model.Multipart
import akka.http.scaladsl.server.RouteResult
import akka.http.scaladsl.server.Directive
import akka.http.scaladsl.server.Directive1
import akka.http.scaladsl.server.MissingFormFieldRejection
import akka.stream.scaladsl._
import akka.util.ByteString

import scala.util.{ Failure, Success }
import java.io.File
import java.nio.file.Files
import scala.collection.immutable
import scala.concurrent.Future
import scala.concurrent.Promise
import scala.util.Failure
import scala.util.Success

/**
* @groupname fileupload File upload directives
Expand Down Expand Up @@ -108,8 +113,8 @@ trait FileUploadDirectives {
def fileUpload(fieldName: String): Directive1[(FileInfo, Source[ByteString, Any])] =
entity(as[Multipart.FormData]).flatMap { formData =>
Directive[Tuple1[(FileInfo, Source[ByteString, Any])]] { inner => ctx =>
import ctx.materializer
import ctx.executionContext
import ctx.materializer

// We complete the directive through this promise as soon as we encounter the
// selected part. This way the inner directive can consume it, after which we will
Expand Down Expand Up @@ -162,7 +167,7 @@ trait FileUploadDirectives {
implicit val ec = ctx.executionContext

def tempDest(fileInfo: FileInfo): File = {
val dest = File.createTempFile("akka-http-upload", ".tmp")
val dest = Files.createTempFile("akka-http-upload", ".tmp").toFile
dest.deleteOnExit()
dest
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.nio.file.Files;

import static akka.http.javadsl.server.PathMatchers.longSegment;
import static akka.http.javadsl.server.PathMatchers.segment;
Expand Down Expand Up @@ -65,7 +66,7 @@ Route uploadVideo() {
if ("file".equals(bodyPart.getName())) {
// stream into a file as the chunks of it arrives and return a CompletionStage
// file to where it got stored
final File file = File.createTempFile("upload", "tmp");
final File file = Files.createTempFile("upload", "tmp").toFile();
return bodyPart.getEntity().getDataBytes()
.runWith(FileIO.toPath(file.toPath()), materializer)
.thenApply(ignore ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,12 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.function.Function;

import static scala.compat.java8.JFunction.func;

//#storeUploadedFile
import static akka.http.javadsl.server.Directives.complete;
import static akka.http.javadsl.server.Directives.storeUploadedFile;
Expand Down Expand Up @@ -67,7 +64,7 @@ public void testStoreUploadedFile() {
//#storeUploadedFile
final Function<FileInfo, File> temporaryDestination = (info) -> {
try {
return File.createTempFile(info.getFileName(), ".tmp");
return Files.createTempFile(info.getFileName(), ".tmp").toFile();
} catch (Exception e) {
return null;
}
Expand Down Expand Up @@ -101,7 +98,7 @@ public void testStoreUploadedFiles() {
//#storeUploadedFiles
final Function<FileInfo, File> temporaryDestination = info -> {
try {
return File.createTempFile(info.getFileName(), ".tmp");
return Files.createTempFile(info.getFileName(), ".tmp").toFile();
} catch (Exception e) {
return null;
}
Expand Down Expand Up @@ -243,7 +240,7 @@ public void testFileProcessing() {

File tempFile = null;
try {
tempFile = File.createTempFile(prefix, suffix);
tempFile = Files.createTempFile(prefix, suffix).toFile();
tempFile.deleteOnExit();
Files.write(tempFile.toPath(), Arrays.asList("2,3,5", "7,11,13,17,23", "29,31,37"), Charset.forName("UTF-8"));
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@

package docs.http.scaladsl.server

import java.io.File

import akka.Done
import akka.actor.ActorRef
import akka.http.scaladsl.model.Multipart
import akka.http.scaladsl.model.Multipart.FormData.BodyPart
import akka.http.scaladsl.server.RoutingSpec
import akka.stream.scaladsl.Framing
import akka.stream.scaladsl._
import akka.http.scaladsl.model.Multipart
import akka.http.scaladsl.server.RoutingSpec
import akka.util.ByteString
import docs.CompileOnlySpec

import scala.concurrent.duration._
import java.io.File
import java.nio.file.Files
import scala.concurrent.Future
import scala.concurrent.duration._

class FileUploadExamplesSpec extends RoutingSpec with CompileOnlySpec {

Expand All @@ -38,9 +38,9 @@ class FileUploadExamplesSpec extends RoutingSpec with CompileOnlySpec {
case b: BodyPart if b.name == "file" =>
// stream into a file as the chunks of it arrives and return a future
// file to where it got stored
val file = File.createTempFile("upload", "tmp")
b.entity.dataBytes.runWith(FileIO.toPath(file.toPath)).map(_ =>
(b.name -> file))
val path = Files.createTempFile("upload", "tmp")
b.entity.dataBytes.runWith(FileIO.toPath(path)).map(_ =>
(b.name -> path.toFile))

case b: BodyPart =>
// collect form field values
Expand Down
Loading

0 comments on commit da86304

Please sign in to comment.