* =htp #20915 fix too small timing issue in DirectivesSpec * applying scalariform reformatting that keeps popping up
This commit is contained in:
parent
b4cd30cf94
commit
54ecf32d2b
7 changed files with 119 additions and 113 deletions
|
|
@ -14,6 +14,9 @@ import akka.stream.javadsl.Source;
|
||||||
import akka.util.ByteString;
|
import akka.util.ByteString;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import scala.concurrent.duration.Duration;
|
||||||
|
import scala.concurrent.duration.FiniteDuration;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
|
@ -21,114 +24,117 @@ import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CompletionStage;
|
import java.util.concurrent.CompletionStage;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
public class FileUploadDirectivesExamplesTest extends JUnitRouteTest {
|
public class FileUploadDirectivesExamplesTest extends JUnitRouteTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUploadedFile() {
|
public void testUploadedFile() {
|
||||||
//#uploadedFile
|
//#uploadedFile
|
||||||
// function (FileInfo, File) => Route to process the file metadata and file itself
|
// function (FileInfo, File) => Route to process the file metadata and file itself
|
||||||
BiFunction<FileInfo, File, Route> infoFileRoute =
|
BiFunction<FileInfo, File, Route> infoFileRoute =
|
||||||
(info, file) -> {
|
(info, file) -> {
|
||||||
// do something with the file and file metadata ...
|
// do something with the file and file metadata ...
|
||||||
file.delete();
|
file.delete();
|
||||||
return complete(StatusCodes.OK);
|
return complete(StatusCodes.OK);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
final Route route = uploadedFile("csv", infoFileRoute);
|
final Route route = uploadedFile("csv", infoFileRoute);
|
||||||
|
|
||||||
Map<String, String> filenameMapping = new HashMap<>();
|
Map<String, String> filenameMapping = new HashMap<>();
|
||||||
filenameMapping.put("filename", "data.csv");
|
filenameMapping.put("filename", "data.csv");
|
||||||
|
|
||||||
akka.http.javadsl.model.Multipart.FormData multipartForm =
|
akka.http.javadsl.model.Multipart.FormData multipartForm =
|
||||||
Multiparts.createStrictFormDataFromParts(Multiparts.createFormDataBodyPartStrict("csv",
|
Multiparts.createStrictFormDataFromParts(Multiparts.createFormDataBodyPartStrict("csv",
|
||||||
HttpEntities.create(ContentTypes.TEXT_PLAIN_UTF8,
|
HttpEntities.create(ContentTypes.TEXT_PLAIN_UTF8,
|
||||||
"1,5,7\n11,13,17"), filenameMapping));
|
"1,5,7\n11,13,17"), filenameMapping));
|
||||||
|
|
||||||
// test:
|
// test:
|
||||||
testRoute(route).run(HttpRequest.POST("/").withEntity(
|
testRoute(route).run(HttpRequest.POST("/")
|
||||||
multipartForm.toEntity(HttpCharsets.UTF_8, BodyPartRenderer
|
.withEntity(
|
||||||
.randomBoundaryWithDefaults())))
|
multipartForm.toEntity(HttpCharsets.UTF_8,
|
||||||
.assertStatusCode(StatusCodes.OK);
|
BodyPartRenderer
|
||||||
//#
|
.randomBoundaryWithDefaults())))
|
||||||
|
.assertStatusCode(StatusCodes.OK);
|
||||||
|
//#
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFileUpload() {
|
||||||
|
//#fileUpload
|
||||||
|
final Route route = extractRequestContext(ctx -> {
|
||||||
|
// function (FileInfo, Source<ByteString,Object>) => Route to process the file contents
|
||||||
|
BiFunction<FileInfo, Source<ByteString, Object>, Route> processUploadedFile =
|
||||||
|
(metadata, byteSource) -> {
|
||||||
|
CompletionStage<Integer> sumF = byteSource.via(Framing.delimiter(
|
||||||
|
ByteString.fromString("\n"), 1024))
|
||||||
|
.mapConcat(bs -> Arrays.asList(bs.utf8String().split(",")))
|
||||||
|
.map(s -> Integer.parseInt(s))
|
||||||
|
.runFold(0, (acc, n) -> acc + n, ctx.getMaterializer());
|
||||||
|
return onSuccess(() -> sumF, sum -> complete("Sum: " + sum));
|
||||||
|
};
|
||||||
|
return fileUpload("csv", processUploadedFile);
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, String> filenameMapping = new HashMap<>();
|
||||||
|
filenameMapping.put("filename", "primes.csv");
|
||||||
|
|
||||||
|
akka.http.javadsl.model.Multipart.FormData multipartForm =
|
||||||
|
Multiparts.createStrictFormDataFromParts(
|
||||||
|
Multiparts.createFormDataBodyPartStrict("csv",
|
||||||
|
HttpEntities.create(ContentTypes.TEXT_PLAIN_UTF8,
|
||||||
|
"2,3,5\n7,11,13,17,23\n29,31,37\n"), filenameMapping));
|
||||||
|
|
||||||
|
// test:
|
||||||
|
testRoute(route).run(HttpRequest.POST("/").withEntity(
|
||||||
|
multipartForm.toEntity(HttpCharsets.UTF_8, BodyPartRenderer.randomBoundaryWithDefaults())))
|
||||||
|
.assertStatusCode(StatusCodes.OK).assertEntityAs(Unmarshaller.entityToString(), "Sum: 178");
|
||||||
|
//#
|
||||||
|
}
|
||||||
|
|
||||||
|
@Ignore("compileOnly")
|
||||||
|
@Test
|
||||||
|
public void testFileProcessing() {
|
||||||
|
//#fileProcessing
|
||||||
|
final Route route = extractRequestContext(ctx -> {
|
||||||
|
// function (FileInfo, Source<ByteString,Object>) => Route to process the file contents
|
||||||
|
BiFunction<FileInfo, Source<ByteString, Object>, Route> processUploadedFile =
|
||||||
|
(metadata, byteSource) -> {
|
||||||
|
CompletionStage<Integer> sumF = byteSource.via(Framing.delimiter(
|
||||||
|
ByteString.fromString("\n"), 1024))
|
||||||
|
.mapConcat(bs -> Arrays.asList(bs.utf8String().split(",")))
|
||||||
|
.map(s -> Integer.parseInt(s))
|
||||||
|
.runFold(0, (acc, n) -> acc + n, ctx.getMaterializer());
|
||||||
|
return onSuccess(() -> sumF, sum -> complete("Sum: " + sum));
|
||||||
|
};
|
||||||
|
return fileUpload("csv", processUploadedFile);
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, String> filenameMapping = new HashMap<>();
|
||||||
|
filenameMapping.put("filename", "primes.csv");
|
||||||
|
|
||||||
|
String prefix = "primes";
|
||||||
|
String suffix = ".csv";
|
||||||
|
|
||||||
|
File tempFile = null;
|
||||||
|
try {
|
||||||
|
tempFile = File.createTempFile(prefix, suffix);
|
||||||
|
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) {
|
||||||
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFileUpload() {
|
|
||||||
//#fileUpload
|
|
||||||
final Route route = extractRequestContext(ctx -> {
|
|
||||||
// function (FileInfo, Source<ByteString,Object>) => Route to process the file contents
|
|
||||||
BiFunction<FileInfo, Source<ByteString, Object>, Route> processUploadedFile =
|
|
||||||
(metadata, byteSource) -> {
|
|
||||||
CompletionStage<Integer> sumF = byteSource.via(Framing.delimiter(
|
|
||||||
ByteString.fromString("\n"), 1024))
|
|
||||||
.mapConcat(bs -> Arrays.asList(bs.utf8String().split(",")))
|
|
||||||
.map(s -> Integer.parseInt(s))
|
|
||||||
.runFold(0, (acc, n) -> acc + n, ctx.getMaterializer());
|
|
||||||
return onSuccess(() -> sumF, sum -> complete("Sum: " + sum));
|
|
||||||
};
|
|
||||||
return fileUpload("csv", processUploadedFile);
|
|
||||||
});
|
|
||||||
|
|
||||||
Map<String, String> filenameMapping = new HashMap<>();
|
akka.http.javadsl.model.Multipart.FormData multipartForm =
|
||||||
filenameMapping.put("filename", "primes.csv");
|
Multiparts.createFormDataFromPath("csv", ContentTypes.TEXT_PLAIN_UTF8, tempFile.toPath());
|
||||||
|
|
||||||
akka.http.javadsl.model.Multipart.FormData multipartForm =
|
// test:
|
||||||
Multiparts.createStrictFormDataFromParts(
|
testRoute(route).run(HttpRequest.POST("/").withEntity(
|
||||||
Multiparts.createFormDataBodyPartStrict("csv",
|
multipartForm.toEntity(HttpCharsets.UTF_8, BodyPartRenderer.randomBoundaryWithDefaults())))
|
||||||
HttpEntities.create(ContentTypes.TEXT_PLAIN_UTF8,
|
.assertStatusCode(StatusCodes.OK).assertEntityAs(Unmarshaller.entityToString(), "Sum: 178");
|
||||||
"2,3,5\n7,11,13,17,23\n29,31,37\n"), filenameMapping));
|
//#
|
||||||
|
}
|
||||||
// test:
|
|
||||||
testRoute(route).run(HttpRequest.POST("/").withEntity(
|
|
||||||
multipartForm.toEntity(HttpCharsets.UTF_8, BodyPartRenderer.randomBoundaryWithDefaults())))
|
|
||||||
.assertStatusCode(StatusCodes.OK).assertEntityAs(Unmarshaller.entityToString(), "Sum: 178");
|
|
||||||
//#
|
|
||||||
}
|
|
||||||
|
|
||||||
@Ignore("compileOnly")
|
|
||||||
@Test
|
|
||||||
public void testFileProcessing() {
|
|
||||||
//#fileProcessing
|
|
||||||
final Route route = extractRequestContext(ctx -> {
|
|
||||||
// function (FileInfo, Source<ByteString,Object>) => Route to process the file contents
|
|
||||||
BiFunction<FileInfo, Source<ByteString, Object>, Route> processUploadedFile =
|
|
||||||
(metadata, byteSource) -> {
|
|
||||||
CompletionStage<Integer> sumF = byteSource.via(Framing.delimiter(
|
|
||||||
ByteString.fromString("\n"), 1024))
|
|
||||||
.mapConcat(bs -> Arrays.asList(bs.utf8String().split(",")))
|
|
||||||
.map(s -> Integer.parseInt(s))
|
|
||||||
.runFold(0, (acc, n) -> acc + n, ctx.getMaterializer());
|
|
||||||
return onSuccess(() -> sumF, sum -> complete("Sum: " + sum));
|
|
||||||
};
|
|
||||||
return fileUpload("csv", processUploadedFile);
|
|
||||||
});
|
|
||||||
|
|
||||||
Map<String, String> filenameMapping = new HashMap<>();
|
|
||||||
filenameMapping.put("filename", "primes.csv");
|
|
||||||
|
|
||||||
String prefix = "primes";
|
|
||||||
String suffix = ".csv";
|
|
||||||
|
|
||||||
File tempFile = null;
|
|
||||||
try {
|
|
||||||
tempFile = File.createTempFile(prefix, suffix);
|
|
||||||
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) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
akka.http.javadsl.model.Multipart.FormData multipartForm =
|
|
||||||
Multiparts.createFormDataFromPath("csv", ContentTypes.TEXT_PLAIN_UTF8, tempFile.toPath());
|
|
||||||
|
|
||||||
// test:
|
|
||||||
testRoute(route).run(HttpRequest.POST("/").withEntity(
|
|
||||||
multipartForm.toEntity(HttpCharsets.UTF_8, BodyPartRenderer.randomBoundaryWithDefaults())))
|
|
||||||
.assertStatusCode(StatusCodes.OK).assertEntityAs(Unmarshaller.entityToString(), "Sum: 178");
|
|
||||||
//#
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ class ActorWithArgs(arg: String) extends Actor {
|
||||||
//#actor-with-value-class-argument
|
//#actor-with-value-class-argument
|
||||||
class Argument(val value: String) extends AnyVal
|
class Argument(val value: String) extends AnyVal
|
||||||
class ValueClassActor(arg: Argument) extends Actor {
|
class ValueClassActor(arg: Argument) extends Actor {
|
||||||
def receive = {case _ => () }
|
def receive = { case _ => () }
|
||||||
}
|
}
|
||||||
|
|
||||||
object ValueClassActor {
|
object ValueClassActor {
|
||||||
|
|
|
||||||
|
|
@ -416,8 +416,8 @@ private[http] object HttpServerBluePrint {
|
||||||
if (isEarlyResponse && response.status.isSuccess)
|
if (isEarlyResponse && response.status.isSuccess)
|
||||||
log.warning(
|
log.warning(
|
||||||
"Sending an 2xx 'early' response before end of request was received... " +
|
"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! " +
|
"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!")
|
"Consider only issuing this response after the request data has been completely read!")
|
||||||
val close = requestStart.closeRequested ||
|
val close = requestStart.closeRequested ||
|
||||||
(requestStart.expect100Continue && oneHundredContinueResponsePending) ||
|
(requestStart.expect100Continue && oneHundredContinueResponsePending) ||
|
||||||
(isClosed(requestParsingIn) && openRequests.isEmpty) ||
|
(isClosed(requestParsingIn) && openRequests.isEmpty) ||
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ abstract class RouteTest extends AllDirectives {
|
||||||
implicit def materializer: Materializer
|
implicit def materializer: Materializer
|
||||||
implicit def executionContext: ExecutionContextExecutor = system.dispatcher
|
implicit def executionContext: ExecutionContextExecutor = system.dispatcher
|
||||||
|
|
||||||
protected def awaitDuration: FiniteDuration = 500.millis
|
protected def awaitDuration: FiniteDuration = 3.seconds
|
||||||
|
|
||||||
protected def defaultHostInfo: DefaultHostInfo = DefaultHostInfo(Host.create("example.com"), false)
|
protected def defaultHostInfo: DefaultHostInfo = DefaultHostInfo(Host.create("example.com"), false)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ class MethodDirectivesSpec extends RoutingSpec {
|
||||||
Head() ~> headRoute ~> check {
|
Head() ~> headRoute ~> check {
|
||||||
status shouldEqual StatusCodes.OK
|
status shouldEqual StatusCodes.OK
|
||||||
|
|
||||||
val lengthF = response._3.dataBytes.runFold(0)((c, _) => c+1)
|
val lengthF = response._3.dataBytes.runFold(0)((c, _) ⇒ c + 1)
|
||||||
val length = Await.result(lengthF, Duration(100, "millis"))
|
val length = Await.result(lengthF, Duration(100, "millis"))
|
||||||
length shouldEqual 0
|
length shouldEqual 0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -265,13 +265,13 @@ object Credentials {
|
||||||
abstract case class Provided(identifier: String) extends Credentials {
|
abstract case class Provided(identifier: String) extends Credentials {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* First applies the passed in `hasher` function to the received secret part of the Credentials
|
* First applies the passed in `hasher` function to the received secret part of the Credentials
|
||||||
* and then safely compares the passed in `secret` with the hashed received secret.
|
* and then safely compares the passed in `secret` with the hashed received secret.
|
||||||
* This method can be used if the secret is not stored in plain text.
|
* This method can be used if the secret is not stored in plain text.
|
||||||
* Use of this method instead of manual String equality testing is recommended in order to guard against timing attacks.
|
* Use of this method instead of manual String equality testing is recommended in order to guard against timing attacks.
|
||||||
*
|
*
|
||||||
* See also [[EnhancedString#secure_==]], for more information.
|
* See also [[EnhancedString#secure_==]], for more information.
|
||||||
*/
|
*/
|
||||||
def verify(secret: String, hasher: String ⇒ String): Boolean
|
def verify(secret: String, hasher: String ⇒ String): Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -280,7 +280,7 @@ object Credentials {
|
||||||
*
|
*
|
||||||
* See also [[EnhancedString#secure_==]], for more information.
|
* See also [[EnhancedString#secure_==]], for more information.
|
||||||
*/
|
*/
|
||||||
def verify(secret: String): Boolean = verify(secret, x => x)
|
def verify(secret: String): Boolean = verify(secret, x ⇒ x)
|
||||||
}
|
}
|
||||||
|
|
||||||
def apply(cred: Option[HttpCredentials]): Credentials = {
|
def apply(cred: Option[HttpCredentials]): Credentials = {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue