=htj #16436 add missing Java directives

This commit is contained in:
Johannes Rudolph 2015-06-18 16:44:16 +02:00 committed by Mathias
parent 10ea40b2f8
commit 83833ae4f8
59 changed files with 2595 additions and 236 deletions

View file

@ -40,4 +40,9 @@ public abstract class StatusCode {
* a non-empty entity.
*/
public abstract boolean allowsEntity();
/**
* Returns if the status-code is a redirection status code.
*/
public abstract boolean isRedirection();
}

View file

@ -6,6 +6,10 @@ package akka.http.impl.util
import java.net.InetAddress
import java.{ util ju, lang jl }
import akka.http.scaladsl.model.ws.Message
import akka.stream.javadsl
import akka.stream.scaladsl
import scala.collection.immutable
import scala.reflect.ClassTag
import akka.japi
@ -52,6 +56,9 @@ object JavaMapping {
def asJava: J
}
def toJava[J, S](s: S)(implicit mapping: JavaMapping[J, S]): J = mapping.toJava(s)
def toScala[J, S](j: J)(implicit mapping: JavaMapping[J, S]): S = mapping.toScala(j)
object Implicits {
import scala.language.implicitConversions
@ -87,6 +94,16 @@ object JavaMapping {
def toJava(scalaObject: Option[_S]): japi.Option[_J] = japi.Option.fromScalaOption(scalaObject.map(mapping.toJava(_)))
}
implicit def flowMapping[JIn, SIn, JOut, SOut, M](implicit inMapping: JavaMapping[JIn, SIn], outMapping: JavaMapping[JOut, SOut]): JavaMapping[javadsl.Flow[JIn, JOut, M], scaladsl.Flow[SIn, SOut, M]] =
new JavaMapping[javadsl.Flow[JIn, JOut, M], scaladsl.Flow[SIn, SOut, M]] {
def toScala(javaObject: javadsl.Flow[JIn, JOut, M]): S =
scaladsl.Flow[SIn].map(inMapping.toJava).viaMat(javaObject)(scaladsl.Keep.right).map(outMapping.toScala)
def toJava(scalaObject: scaladsl.Flow[SIn, SOut, M]): J =
javadsl.Flow.wrap {
scaladsl.Flow[JIn].map(inMapping.toScala).viaMat(scalaObject)(scaladsl.Keep.right).map(outMapping.toJava)
}
}
implicit object StringIdentity extends Identity[String]
implicit object LongMapping extends JavaMapping[jl.Long, Long] {
@ -144,6 +161,11 @@ object JavaMapping {
implicit object ProductVersion extends Inherited[jm.headers.ProductVersion, sm.headers.ProductVersion]
implicit object RangeUnit extends Inherited[jm.headers.RangeUnit, sm.headers.RangeUnit]
implicit object WsMessage extends JavaMapping[jm.ws.Message, sm.ws.Message] {
def toScala(javaObject: J): WsMessage.S = javaObject.asScala
def toJava(scalaObject: Message): WsMessage.J = jm.ws.Message.adapt(scalaObject)
}
implicit object Uri extends JavaMapping[jm.Uri, sm.Uri] {
def toScala(javaObject: jm.Uri): Uri.S = cast[JavaUri](javaObject).uri
def toJava(scalaObject: sm.Uri): Uri.J = JavaAccessors.Uri(scalaObject)

View file

@ -16,6 +16,7 @@ sealed abstract class StatusCode extends jm.StatusCode with LazyValueBytesRender
def defaultMessage: String
def isSuccess: Boolean
def isFailure: Boolean
def isRedirection: Boolean
def allowsEntity: Boolean
}
@ -35,16 +36,25 @@ object StatusCodes extends ObjectRegistry[Int, StatusCode] {
sealed protected abstract class HttpFailure extends StatusCode {
def isSuccess = false
def isFailure = true
def isRedirection: Boolean = false
def allowsEntity = true
}
// format: OFF
final case class Informational private[StatusCodes] (intValue: Int)(val reason: String,
val defaultMessage: String) extends HttpSuccess { def allowsEntity = false }
val defaultMessage: String) extends HttpSuccess {
def allowsEntity = false
def isRedirection: Boolean = false
}
final case class Success private[StatusCodes] (intValue: Int)(val reason: String, val defaultMessage: String,
val allowsEntity: Boolean = true) extends HttpSuccess
val allowsEntity: Boolean = true) extends HttpSuccess {
def isRedirection: Boolean = false
}
final case class Redirection private[StatusCodes] (intValue: Int)(val reason: String, val defaultMessage: String,
val htmlTemplate: String, val allowsEntity: Boolean = true) extends HttpSuccess
val htmlTemplate: String, val allowsEntity: Boolean = true) extends HttpSuccess {
def isRedirection: Boolean = true
}
final case class ClientError private[StatusCodes] (intValue: Int)(val reason: String, val defaultMessage: String) extends HttpFailure
final case class ServerError private[StatusCodes] (intValue: Int)(val reason: String, val defaultMessage: String) extends HttpFailure
@ -54,6 +64,7 @@ object StatusCodes extends ObjectRegistry[Int, StatusCode] {
val isSuccess: Boolean,
val allowsEntity: Boolean) extends StatusCode {
def isFailure: Boolean = !isSuccess
def isRedirection: Boolean = false
}
private def reg[T <: StatusCode](code: T): T = {

View file

@ -5,6 +5,8 @@
package akka.http.scaladsl.model.ws
import java.lang.Iterable
import akka.http.impl.util.JavaMapping
import scala.collection.immutable
import akka.stream
import akka.stream.javadsl
@ -63,13 +65,13 @@ trait UpgradeToWebsocket extends jm.ws.UpgradeToWebsocket {
* Java API
*/
def handleMessagesWith(handlerFlow: stream.javadsl.Flow[jm.ws.Message, jm.ws.Message, _]): HttpResponse =
handleMessages(adaptJavaFlow(handlerFlow))
handleMessages(JavaMapping.toScala(handlerFlow))
/**
* Java API
*/
def handleMessagesWith(handlerFlow: stream.javadsl.Flow[jm.ws.Message, jm.ws.Message, _], subprotocol: String): HttpResponse =
handleMessages(adaptJavaFlow(handlerFlow), subprotocol = Some(subprotocol))
handleMessages(JavaMapping.toScala(handlerFlow), subprotocol = Some(subprotocol))
/**
* Java API
@ -85,8 +87,6 @@ trait UpgradeToWebsocket extends jm.ws.UpgradeToWebsocket {
subprotocol: String): HttpResponse =
handleMessages(createScalaFlow(inSink, outSource), subprotocol = Some(subprotocol))
private[this] def adaptJavaFlow(handlerFlow: stream.javadsl.Flow[jm.ws.Message, jm.ws.Message, _]): Flow[Message, Message, Any] =
Flow[Message].map(jm.ws.Message.adapt).via(handlerFlow.asScala).map(_.asScala)
private[this] def createScalaFlow(inSink: stream.javadsl.Sink[jm.ws.Message, _], outSource: stream.javadsl.Source[jm.ws.Message, _]): Flow[Message, Message, Any] =
adaptJavaFlow(Flow.wrap(inSink.asScala, outSource.asScala)((_, _) ()).asJava)
JavaMapping.toScala(Flow.wrap(inSink.asScala, outSource.asScala)((_, _) ()).asJava)
}

View file

@ -4,6 +4,8 @@
package akka.http.javadsl.testkit
import akka.http.javadsl.server._
import Directives._
import org.junit.rules.ExternalResource
import org.junit.{ Rule, Assert }
import scala.concurrent.duration._
@ -35,6 +37,11 @@ abstract class JUnitRouteTestBase extends RouteTest {
throw new IllegalStateException("Assertion should have failed")
}
}
protected def completeWithValueToString[T](value: RequestVal[T]): Route =
handleWith(value, new Handler1[T] {
def handle(ctx: RequestContext, t: T): RouteResult = ctx.complete(t.toString)
})
}
abstract class JUnitRouteTest extends JUnitRouteTestBase {
private[this] val _systemResource = new ActorSystemResource

View file

@ -26,8 +26,12 @@ abstract class RouteTest extends AllDirectives {
protected def awaitDuration: FiniteDuration = 500.millis
def runRoute(route: Route, request: HttpRequest): TestResponse = {
val scalaRoute = ScalaRoute.seal(RouteImplementation(route))
def runRoute(route: Route, request: HttpRequest): TestResponse =
runScalaRoute(ScalaRoute.seal(RouteImplementation(route)), request)
def runRouteUnSealed(route: Route, request: HttpRequest): TestResponse =
runScalaRoute(RouteImplementation(route), request)
private def runScalaRoute(scalaRoute: ScalaRoute, request: HttpRequest): TestResponse = {
val result = scalaRoute(new server.RequestContextImpl(request.asScala, NoLogging, RoutingSettings(system)))
result.awaitResult(awaitDuration) match {

View file

@ -11,8 +11,8 @@ import akka.http.javadsl.server.values.*;
import java.io.IOException;
public class SimpleServerApp8 extends HttpApp {
static Parameter<Integer> x = Parameters.integer("x");
static Parameter<Integer> y = Parameters.integer("y");
static Parameter<Integer> x = Parameters.intValue("x");
static Parameter<Integer> y = Parameters.intValue("y");
static PathMatcher<Integer> xSegment = PathMatchers.integerNumber();
static PathMatcher<Integer> ySegment = PathMatchers.integerNumber();

View file

@ -11,6 +11,11 @@ import akka.http.javadsl.server.values.*;
import static akka.http.javadsl.server.Directives.*;
public class HandlerBindingTest extends JUnitRouteTest {
Parameter<Integer> aParam = Parameters.intValue("a");
Parameter<Integer> bParam = Parameters.intValue("b");
Parameter<Integer> cParam = Parameters.intValue("c");
Parameter<Integer> dParam = Parameters.intValue("d");
@Test
public void testHandlerWithoutExtractions() {
Route route = handleWith(ctx -> ctx.complete("Ok"));
@ -19,7 +24,7 @@ public class HandlerBindingTest extends JUnitRouteTest {
}
@Test
public void testHandler1() {
Route route = handleWith(Parameters.integer("a"), (ctx, a) -> ctx.complete("Ok " + a));
Route route = handleWith(aParam, (ctx, a) -> ctx.complete("Ok " + a));
TestResponse response = runRoute(route, HttpRequest.GET("?a=23"));
response.assertStatusCode(200);
response.assertEntity("Ok 23");
@ -28,8 +33,8 @@ public class HandlerBindingTest extends JUnitRouteTest {
public void testHandler2() {
Route route =
handleWith(
Parameters.integer("a"),
Parameters.integer("b"),
aParam,
bParam,
(ctx, a, b) -> ctx.complete("Sum: " + (a + b)));
TestResponse response = runRoute(route, HttpRequest.GET("?a=23&b=42"));
response.assertStatusCode(200);
@ -39,9 +44,9 @@ public class HandlerBindingTest extends JUnitRouteTest {
public void testHandler3() {
Route route =
handleWith(
Parameters.integer("a"),
Parameters.integer("b"),
Parameters.integer("c"),
aParam,
bParam,
cParam,
(ctx, a, b, c) -> ctx.complete("Sum: " + (a + b + c)));
TestResponse response = runRoute(route, HttpRequest.GET("?a=23&b=42&c=30"));
response.assertStatusCode(200);
@ -51,10 +56,10 @@ public class HandlerBindingTest extends JUnitRouteTest {
public void testHandler4() {
Route route =
handleWith(
Parameters.integer("a"),
Parameters.integer("b"),
Parameters.integer("c"),
Parameters.integer("d"),
aParam,
bParam,
cParam,
dParam,
(ctx, a, b, c, d) -> ctx.complete("Sum: " + (a + b + c + d)));
TestResponse response = runRoute(route, HttpRequest.GET("?a=23&b=42&c=30&d=45"));
response.assertStatusCode(200);
@ -67,10 +72,10 @@ public class HandlerBindingTest extends JUnitRouteTest {
public void testHandler4MethodRef() {
Route route =
handleWith(
Parameters.integer("a"),
Parameters.integer("b"),
Parameters.integer("c"),
Parameters.integer("d"),
aParam,
bParam,
cParam,
dParam,
this::sum);
TestResponse response = runRoute(route, HttpRequest.GET("?a=23&b=42&c=30&d=45"));
response.assertStatusCode(200);

View file

@ -5,17 +5,20 @@
package akka.http.javadsl.server.examples.simple;
import akka.actor.ActorSystem;
import akka.dispatch.Futures;
import akka.http.javadsl.server.*;
import akka.http.javadsl.server.values.Parameter;
import akka.http.javadsl.server.values.Parameters;
import akka.http.javadsl.server.values.PathMatcher;
import akka.http.javadsl.server.values.PathMatchers;
import scala.concurrent.Future;
import java.io.IOException;
import java.util.concurrent.Callable;
public class SimpleServerApp extends HttpApp {
static Parameter<Integer> x = Parameters.integer("x");
static Parameter<Integer> y = Parameters.integer("y");
static Parameter<Integer> x = Parameters.intValue("x");
static Parameter<Integer> y = Parameters.intValue("y");
static PathMatcher<Integer> xSegment = PathMatchers.integerNumber();
static PathMatcher<Integer> ySegment = PathMatchers.integerNumber();
@ -24,6 +27,13 @@ public class SimpleServerApp extends HttpApp {
int result = x * y;
return ctx.complete(String.format("%d * %d = %d", x, y, result));
}
public static Future<RouteResult> multiplyAsync(final RequestContext ctx, final int x, final int y) {
return Futures.future(new Callable<RouteResult>() {
public RouteResult call() throws Exception {
return multiply(ctx, x, y);
}
}, ctx.executionContext());
}
@Override
public Route createRoute() {
@ -59,6 +69,10 @@ public class SimpleServerApp extends HttpApp {
path("multiply", xSegment, ySegment).route(
// bind handler by reflection
handleWith(SimpleServerApp.class, "multiply", xSegment, ySegment)
),
path("multiplyAsync", xSegment, ySegment).route(
// bind async handler by reflection
handleWith(SimpleServerApp.class, "multiplyAsync", xSegment, ySegment)
)
);
}

View file

@ -46,8 +46,8 @@ public class CompleteTest extends JUnitRouteTest {
}
@Test
public void completeWithFuture() {
Parameter<Integer> x = Parameters.integer("x");
Parameter<Integer> y = Parameters.integer("y");
Parameter<Integer> x = Parameters.intValue("x");
Parameter<Integer> y = Parameters.intValue("y");
Handler2<Integer, Integer> slowCalc = new Handler2<Integer, Integer>() {
@Override

View file

@ -26,8 +26,8 @@ public class HandlerBindingTest extends JUnitRouteTest {
}
@Test
public void testHandlerWithSomeExtractions() {
final Parameter<Integer> a = Parameters.integer("a");
final Parameter<Integer> b = Parameters.integer("b");
final Parameter<Integer> a = Parameters.intValue("a");
final Parameter<Integer> b = Parameters.intValue("b");
Route route = handleWith(
new Handler() {
@ -42,7 +42,7 @@ public class HandlerBindingTest extends JUnitRouteTest {
}
@Test
public void testHandlerIfExtractionFails() {
final Parameter<Integer> a = Parameters.integer("a");
final Parameter<Integer> a = Parameters.intValue("a");
Route route = handleWith(
new Handler() {
@ -58,7 +58,7 @@ public class HandlerBindingTest extends JUnitRouteTest {
}
@Test
public void testHandler1() {
final Parameter<Integer> a = Parameters.integer("a");
final Parameter<Integer> a = Parameters.intValue("a");
Route route = handleWith(
a,
@ -76,8 +76,8 @@ public class HandlerBindingTest extends JUnitRouteTest {
@Test
public void testHandler2() {
Route route = handleWith(
Parameters.integer("a"),
Parameters.integer("b"),
Parameters.intValue("a"),
Parameters.intValue("b"),
new Handler2<Integer, Integer>() {
@Override
public RouteResult handle(RequestContext ctx, Integer a, Integer b) {
@ -92,9 +92,9 @@ public class HandlerBindingTest extends JUnitRouteTest {
@Test
public void testHandler3() {
Route route = handleWith(
Parameters.integer("a"),
Parameters.integer("b"),
Parameters.integer("c"),
Parameters.intValue("a"),
Parameters.intValue("b"),
Parameters.intValue("c"),
new Handler3<Integer, Integer, Integer>() {
@Override
public RouteResult handle(RequestContext ctx, Integer a, Integer b, Integer c) {
@ -109,10 +109,10 @@ public class HandlerBindingTest extends JUnitRouteTest {
@Test
public void testHandler4() {
Route route = handleWith(
Parameters.integer("a"),
Parameters.integer("b"),
Parameters.integer("c"),
Parameters.integer("d"),
Parameters.intValue("a"),
Parameters.intValue("b"),
Parameters.intValue("c"),
Parameters.intValue("d"),
new Handler4<Integer, Integer, Integer, Integer>() {
@Override
public RouteResult handle(RequestContext ctx, Integer a, Integer b, Integer c, Integer d) {
@ -131,7 +131,7 @@ public class HandlerBindingTest extends JUnitRouteTest {
return ctx.complete("Negated: " + (- a));
}
}
Route route = handleWith(new Test(), "negate", Parameters.integer("a"));
Route route = handleWith(new Test(), "negate", Parameters.intValue("a"));
runRoute(route, HttpRequest.GET("?a=23"))
.assertStatusCode(200)
.assertEntity("Negated: -23");
@ -142,7 +142,7 @@ public class HandlerBindingTest extends JUnitRouteTest {
}
@Test
public void testStaticReflectiveHandler() {
Route route = handleWith(HandlerBindingTest.class, "squared", Parameters.integer("a"));
Route route = handleWith(HandlerBindingTest.class, "squared", Parameters.intValue("a"));
runRoute(route, HttpRequest.GET("?a=23"))
.assertStatusCode(200)
.assertEntity("Squared: 529");

View file

@ -11,6 +11,7 @@ import akka.http.javadsl.model.headers.ContentEncoding;
import akka.http.javadsl.model.headers.HttpEncodings;
import akka.http.javadsl.server.Coder;
import akka.stream.ActorMaterializer;
import akka.http.javadsl.server.*;
import akka.util.ByteString;
import org.junit.*;
import scala.concurrent.Await;
@ -86,7 +87,53 @@ public class CodingDirectivesTest extends JUnitRouteTest {
}
@Test
public void testAutomaticDecoding() {}
public void testAutomaticDecoding() {
TestRoute route =
testRoute(
decodeRequest(
completeWithValueToString(RequestVals.entityAs(Unmarshallers.String()))
)
);
HttpRequest deflateRequest =
HttpRequest.POST("/")
.addHeader(ContentEncoding.create(HttpEncodings.DEFLATE))
.withEntity(Coder.Deflate.encode(ByteString.fromString("abcdef")));
route.run(deflateRequest)
.assertStatusCode(200)
.assertEntity("abcdef");
HttpRequest gzipRequest =
HttpRequest.POST("/")
.addHeader(ContentEncoding.create(HttpEncodings.GZIP))
.withEntity(Coder.Gzip.encode(ByteString.fromString("hijklmnopq")));
route.run(gzipRequest)
.assertStatusCode(200)
.assertEntity("hijklmnopq");
}
@Test
public void testGzipDecoding() {}
public void testGzipDecoding() {
TestRoute route =
testRoute(
decodeRequestWith(Coder.Gzip,
completeWithValueToString(RequestVals.entityAs(Unmarshallers.String()))
)
);
HttpRequest gzipRequest =
HttpRequest.POST("/")
.addHeader(ContentEncoding.create(HttpEncodings.GZIP))
.withEntity(Coder.Gzip.encode(ByteString.fromString("hijklmnopq")));
route.run(gzipRequest)
.assertStatusCode(200)
.assertEntity("hijklmnopq");
HttpRequest deflateRequest =
HttpRequest.POST("/")
.addHeader(ContentEncoding.create(HttpEncodings.DEFLATE))
.withEntity(Coder.Deflate.encode(ByteString.fromString("abcdef")));
route.run(deflateRequest)
.assertStatusCode(400)
.assertEntity("The request's Content-Encoding is not supported. Expected:\ngzip");
}
}

View file

@ -14,8 +14,8 @@ import akka.http.javadsl.testkit.*;
public class ExecutionDirectivesTest extends JUnitRouteTest {
@Test
public void testCatchExceptionThrownFromHandler() {
Parameter<Integer> a = Parameters.integer("a");
Parameter<Integer> b = Parameters.integer("b");
Parameter<Integer> a = Parameters.intValue("a");
Parameter<Integer> b = Parameters.intValue("b");
Handler2<Integer, Integer> divide =
new Handler2<Integer, Integer>() {
@Override

View file

@ -0,0 +1,72 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.javadsl.server.directives;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.Uri;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.http.javadsl.testkit.TestRoute;
import akka.japi.function.Function;
import org.junit.Test;
import java.util.ArrayList;
public class HostDirectivesTest extends JUnitRouteTest {
@Test
public void testHostFilterBySingleName() {
TestRoute route = testRoute(host("example.org", complete("OK!")));
route
.run(HttpRequest.create().withUri(Uri.create("http://example.org")))
.assertStatusCode(200)
.assertEntity("OK!");
route
.run(HttpRequest.create().withUri(Uri.create("https://other.org")))
.assertStatusCode(404);
}
@Test
public void testHostFilterByNames() {
ArrayList<String> hosts = new ArrayList<String>();
hosts.add("example.org");
hosts.add("example2.org");
TestRoute route = testRoute(host(hosts, complete("OK!")));
route
.run(HttpRequest.create().withUri(Uri.create("http://example.org")))
.assertStatusCode(200)
.assertEntity("OK!");
route
.run(HttpRequest.create().withUri(Uri.create("http://example2.org")))
.assertStatusCode(200)
.assertEntity("OK!");
route
.run(HttpRequest.create().withUri(Uri.create("https://other.org")))
.assertStatusCode(404);
}
@Test
public void testHostFilterByPredicate() {
Function<String, Boolean> predicate =
new Function<String, Boolean>(){
@Override
public Boolean apply(String hostName) throws Exception {
return hostName.contains("ample");
}
};
TestRoute route = testRoute(host(predicate, complete("OK!")));
route
.run(HttpRequest.create().withUri(Uri.create("http://example.org")))
.assertStatusCode(200)
.assertEntity("OK!");
route
.run(HttpRequest.create().withUri(Uri.create("https://other.org")))
.assertStatusCode(404);
}
}

View file

@ -0,0 +1,117 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.javadsl.server.directives;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.Uri;
import akka.http.javadsl.server.RequestContext;
import akka.http.javadsl.server.RequestVal;
import akka.http.javadsl.server.RequestVals;
import akka.http.javadsl.server.values.Parameter;
import akka.http.javadsl.server.values.Parameters;
import akka.http.javadsl.server.values.PathMatchers;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.http.javadsl.testkit.TestRoute;
import akka.japi.function.Function;
import akka.japi.function.Function2;
import org.junit.Test;
public class MiscDirectivesTest extends JUnitRouteTest {
static Parameter<String> stringParam = Parameters.stringValue("stringParam");
static Function<String, Boolean> isShort =
new Function<String, Boolean>() {
@Override
public Boolean apply(String str) throws Exception {
return str.length() < 5;
}
};
@Test
public void testValidateRequestContext() {
Function<RequestContext, Boolean> hasShortPath =
new Function<RequestContext, Boolean>() {
@Override
public Boolean apply(RequestContext ctx) throws Exception {
return ctx.request().getUri().path().toString().length() < 5;
}
};
TestRoute route = testRoute(validate(hasShortPath, "Path too long!", complete("OK!")));
route
.run(HttpRequest.create().withUri(Uri.create("/abc")))
.assertStatusCode(200)
.assertEntity("OK!");
route
.run(HttpRequest.create().withUri(Uri.create("/abcdefghijkl")))
.assertStatusCode(400)
.assertEntity("Path too long!");
}
@Test
public void testValidateOneStandaloneRequestVal() {
TestRoute route = testRoute(validate(stringParam, isShort, "stringParam too long!", complete("OK!")));
route
.run(HttpRequest.GET("/?stringParam=abcd"))
.assertStatusCode(200)
.assertEntity("OK!");
route
.run(HttpRequest.GET("/?stringParam=abcdefg"))
.assertStatusCode(400)
.assertEntity("stringParam too long!");
}
@Test
public void testValidateOnePathMatcherRequestVal() {
RequestVal<String> nameSegment = PathMatchers.segment();
TestRoute route = testRoute(
path("people", nameSegment, "address").route(
validate(nameSegment, isShort, "Segment too long!", complete("OK!"))
)
);
route
.run(HttpRequest.GET("/people/john/address"))
.assertStatusCode(200)
.assertEntity("OK!");
route
.run(HttpRequest.GET("/people/hermanbaker/address"))
.assertStatusCode(400)
.assertEntity("Segment too long!");
}
@Test
public void testValidateTwoRequestVals() {
Function2<String, String, Boolean> stringParamEqualsHostName =
new Function2<String, String, Boolean>() {
@Override
public Boolean apply(String stringParam, String hostName) throws Exception {
return stringParam.equals(hostName);
}
};
TestRoute route =
testRoute(
validate(stringParam, RequestVals.host(), stringParamEqualsHostName, "stringParam must equal hostName!",
complete("OK!")));
route
.run(HttpRequest.GET("http://example.org/?stringParam=example.org"))
.assertStatusCode(200)
.assertEntity("OK!");
route
.run(HttpRequest.GET("http://blubber.org/?stringParam=example.org"))
.assertStatusCode(400)
.assertEntity("stringParam must equal hostName!");
route
.run(HttpRequest.GET("http://blubber.org/"))
.assertStatusCode(404);
}
}

View file

@ -4,6 +4,7 @@
package akka.http.javadsl.server.directives;
import akka.http.javadsl.model.StatusCodes;
import akka.http.javadsl.server.values.PathMatcher;
import org.junit.Test;
@ -76,24 +77,140 @@ public class PathDirectivesTest extends JUnitRouteTest {
TestRoute route =
testRoute(
path("hey", name).route(toStringEcho(name))
path("hey", name).route(completeWithValueToString(name))
);
route.run(HttpRequest.GET("/hey/jude"))
.assertEntity("jude");
}
@Test
public void testPathEnd() {
TestRoute route =
testRoute(
pathPrefix("test").route(
pathEnd().route(complete("end")),
path("abc").route(complete("abc"))
)
);
route.run(HttpRequest.GET("/test"))
.assertEntity("end");
route.run(HttpRequest.GET("/test/abc"))
.assertEntity("abc");
route.run(HttpRequest.GET("/xyz"))
.assertStatusCode(404);
}
@Test
public void testSingleSlash() {
TestRoute route =
testRoute(
pathPrefix("test").route(
pathSingleSlash().route(complete("Ok"))
)
);
route.run(HttpRequest.GET("/"))
route.run(HttpRequest.GET("/test/"))
.assertEntity("Ok");
route.run(HttpRequest.GET("/test"))
.assertStatusCode(404);
}
@Test
public void testPathEndOrSingleSlash() {
TestRoute route =
testRoute(
pathPrefix("test").route(
pathEndOrSingleSlash().route(complete("Ok"))
)
);
route.run(HttpRequest.GET("/test"))
.assertEntity("Ok");
route.run(HttpRequest.GET("/test/"))
.assertEntity("Ok");
route.run(HttpRequest.GET("/abc"))
.assertStatusCode(404);
}
@Test
public void testRawPathPrefixTest() {
TestRoute route =
testRoute(
rawPathPrefixTest(PathMatchers.SLASH(), "abc").route(
completeWithValueToString(RequestVals.unmatchedPath())
)
);
route.run(HttpRequest.GET("/abc"))
.assertEntity("/abc");
route.run(HttpRequest.GET("/abc/def"))
.assertEntity("/abc/def");
route.run(HttpRequest.GET("/abcd/ef"))
.assertEntity("/abcd/ef");
route.run(HttpRequest.GET("/xyz/def"))
.assertStatusCode(404);
}
@Test
public void testPathPrefixTest() {
TestRoute route =
testRoute(
pathPrefixTest("abc").route(completeWithValueToString(RequestVals.unmatchedPath()))
);
route.run(HttpRequest.GET("/abc"))
.assertEntity("/abc");
route.run(HttpRequest.GET("/abc/def"))
.assertEntity("/abc/def");
route.run(HttpRequest.GET("/abcd/ef"))
.assertEntity("/abcd/ef");
route.run(HttpRequest.GET("/xyz/def"))
.assertStatusCode(404);
}
@Test
public void testPathSuffix() {
TestRoute route =
testRoute(
pathSuffix(PathMatchers.SLASH(), "abc").route(completeWithValueToString(RequestVals.unmatchedPath()))
);
route.run(HttpRequest.GET("/test/abc/"))
.assertEntity("/test/");
route.run(HttpRequest.GET("/abc/"))
.assertEntity("/");
route.run(HttpRequest.GET("/abc/def"))
.assertStatusCode(404);
route.run(HttpRequest.GET("/abc"))
.assertStatusCode(404);
}
@Test
public void testPathSuffixTest() {
TestRoute route =
testRoute(
pathSuffixTest("abc").route(completeWithValueToString(RequestVals.unmatchedPath()))
);
route.run(HttpRequest.GET("/test/abc"))
.assertEntity("/test/abc");
route.run(HttpRequest.GET("/abc"))
.assertEntity("/abc");
route.run(HttpRequest.GET("/abc/def"))
.assertStatusCode(404);
}
@ -103,7 +220,7 @@ public class PathDirectivesTest extends JUnitRouteTest {
TestRoute route =
testRoute(
path("age", age).route(toStringEcho(age))
path("age", age).route(completeWithValueToString(age))
);
route.run(HttpRequest.GET("/age/38"))
@ -112,7 +229,6 @@ public class PathDirectivesTest extends JUnitRouteTest {
route.run(HttpRequest.GET("/age/abc"))
.assertStatusCode(404);
}
@Test
public void testTwoVals() {
// tests that `x` and `y` have different identities which is important for
@ -122,7 +238,7 @@ public class PathDirectivesTest extends JUnitRouteTest {
TestRoute route =
testRoute(
path("multiply", x, y).route(
path("multiply", x, "with", y).route(
handleWith(x, y, new Handler2<Integer, Integer>() {
@Override
public RouteResult handle(RequestContext ctx, Integer x, Integer y) {
@ -132,7 +248,7 @@ public class PathDirectivesTest extends JUnitRouteTest {
)
);
route.run(HttpRequest.GET("/multiply/3/6"))
route.run(HttpRequest.GET("/multiply/3/with/6"))
.assertEntity("3 * 6 = 18");
}
@ -142,7 +258,7 @@ public class PathDirectivesTest extends JUnitRouteTest {
TestRoute route =
testRoute(
path("color", color).route(toStringEcho(color))
path("color", color).route(completeWithValueToString(color))
);
route.run(HttpRequest.GET("/color/a0c2ef"))
@ -155,7 +271,7 @@ public class PathDirectivesTest extends JUnitRouteTest {
TestRoute route =
testRoute(
path("bigage", bigAge).route(toStringEcho(bigAge))
path("bigage", bigAge).route(completeWithValueToString(bigAge))
);
route.run(HttpRequest.GET("/bigage/12345678901"))
@ -168,7 +284,7 @@ public class PathDirectivesTest extends JUnitRouteTest {
TestRoute route =
testRoute(
path("code", code).route(toStringEcho(code))
path("code", code).route(completeWithValueToString(code))
);
route.run(HttpRequest.GET("/code/a0b1c2d3e4f5"))
@ -181,7 +297,7 @@ public class PathDirectivesTest extends JUnitRouteTest {
TestRoute route =
testRoute(
path("pets", rest).route(toStringEcho(rest))
path("pets", rest).route(completeWithValueToString(rest))
);
route.run(HttpRequest.GET("/pets/afdaoisd/asda/sfasfasf/asf"))
@ -194,7 +310,7 @@ public class PathDirectivesTest extends JUnitRouteTest {
TestRoute route =
testRoute(
path("by-uuid", uuid).route(toStringEcho(uuid))
path("by-uuid", uuid).route(completeWithValueToString(uuid))
);
route.run(HttpRequest.GET("/by-uuid/6ba7b811-9dad-11d1-80b4-00c04fd430c8"))
@ -207,19 +323,42 @@ public class PathDirectivesTest extends JUnitRouteTest {
TestRoute route =
testRoute(
path("pets", segments).route(toStringEcho(segments))
path("pets", segments).route(completeWithValueToString(segments))
);
route.run(HttpRequest.GET("/pets/cat/dog"))
.assertEntity("[cat, dog]");
}
private <T> Route toStringEcho(RequestVal<T> value) {
return handleWith(value, new Handler1<T>() {
@Override
public RouteResult handle(RequestContext ctx, T t) {
return ctx.complete(t.toString());
}
});
@Test
public void testRedirectToTrailingSlashIfMissing() {
TestRoute route =
testRoute(
redirectToTrailingSlashIfMissing(StatusCodes.FOUND, complete("Ok"))
);
route.run(HttpRequest.GET("/home"))
.assertStatusCode(302)
.assertHeaderExists("Location", "/home/");
route.run(HttpRequest.GET("/home/"))
.assertStatusCode(200)
.assertEntity("Ok");
}
@Test
public void testRedirectToNoTrailingSlashIfPresent() {
TestRoute route =
testRoute(
redirectToNoTrailingSlashIfPresent(StatusCodes.FOUND, complete("Ok"))
);
route.run(HttpRequest.GET("/home/"))
.assertStatusCode(302)
.assertHeaderExists("Location", "/home");
route.run(HttpRequest.GET("/home"))
.assertStatusCode(200)
.assertEntity("Ok");
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.javadsl.server.directives;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.StatusCodes;
import akka.http.javadsl.model.Uri;
import akka.http.javadsl.model.headers.Location;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.http.javadsl.testkit.TestRoute;
import org.junit.Test;
public class RouteDirectivesTest extends JUnitRouteTest {
@Test
public void testRedirection() {
Uri targetUri = Uri.create("http://example.com");
TestRoute route =
testRoute(
redirect(targetUri, StatusCodes.FOUND)
);
route
.run(HttpRequest.create())
.assertStatusCode(302)
.assertHeaderExists(Location.create(targetUri));
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.javadsl.server.directives;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.Uri;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.http.javadsl.testkit.TestRoute;
import org.junit.Test;
public class SchemeDirectivesTest extends JUnitRouteTest {
@Test
public void testSchemeFilter() {
TestRoute route = testRoute(scheme("http", complete("OK!")));
route
.run(HttpRequest.create().withUri(Uri.create("http://example.org")))
.assertStatusCode(200)
.assertEntity("OK!");
route
.run(HttpRequest.create().withUri(Uri.create("https://example.org")))
.assertStatusCode(400)
.assertEntity("Uri scheme not allowed, supported schemes: http");
}
}

View file

@ -19,4 +19,13 @@ public class SimpleServerTest extends JUnitRouteTest {
.assertStatusCode(200)
.assertEntity("42 + 23 = 65");
}
@Test
public void testMultiplyAsync() {
TestResponse response = route.run(HttpRequest.GET("/multiplyAsync/42/23"));
response
.assertStatusCode(200)
.assertEntity("42 * 23 = 966");
}
}

View file

@ -0,0 +1,71 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.javadsl.server.values;
import akka.http.javadsl.model.DateTime;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.headers.HttpCookie;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.http.javadsl.testkit.TestRoute;
import org.junit.Test;
public class CookiesTest extends JUnitRouteTest {
Cookie userIdCookie = Cookies.create("userId").withDomain("example.com").withPath("/admin");
@Test
public void testCookieValue() {
TestRoute route =
testRoute(completeWithValueToString(userIdCookie.value()));
route.run(HttpRequest.create())
.assertStatusCode(400)
.assertEntity("Request is missing required cookie 'userId'");
route.run(HttpRequest.create().addHeader(akka.http.javadsl.model.headers.Cookie.create("userId", "12345")))
.assertStatusCode(200)
.assertEntity("12345");
}
@Test
public void testCookieOptionalValue() {
TestRoute route =
testRoute(completeWithValueToString(userIdCookie.optionalValue()));
route.run(HttpRequest.create())
.assertStatusCode(200)
.assertEntity("None");
route.run(HttpRequest.create().addHeader(akka.http.javadsl.model.headers.Cookie.create("userId", "12345")))
.assertStatusCode(200)
.assertEntity("Some(12345)");
}
@Test
public void testCookieSet() {
TestRoute route =
testRoute(
userIdCookie.set("12").route(
complete("OK!")
)
);
route.run(HttpRequest.create())
.assertStatusCode(200)
.assertHeaderExists("Set-Cookie", "userId=12; Domain=example.com; Path=/admin")
.assertEntity("OK!");
}
@Test
public void testDeleteCookie() {
TestRoute route =
testRoute(
userIdCookie.delete(
complete("OK!")
)
);
route.run(HttpRequest.create())
.assertStatusCode(200)
.assertHeaderExists("Set-Cookie", "userId=deleted; Expires=Wed, 01 Jan 1800 00:00:00 GMT; Domain=example.com; Path=/admin")
.assertEntity("OK!");
}
}

View file

@ -0,0 +1,323 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.javadsl.server.values;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.MediaTypes;
import akka.http.javadsl.server.RequestVal;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.http.javadsl.testkit.TestRoute;
import akka.japi.Option;
import org.junit.Test;
import java.util.AbstractMap;
import java.util.Map;
public class FormFieldsTest extends JUnitRouteTest {
static FormField<String> stringParam = FormFields.stringValue("stringParam");
static FormField<Byte> byteParam = FormFields.byteValue("byteParam");
static FormField<Short> shortParam = FormFields.shortValue("shortParam");
static FormField<Integer> intParam = FormFields.intValue("intParam");
static FormField<Long> longParam = FormFields.longValue("longParam");
static FormField<Float> floatParam = FormFields.floatValue("floatParam");
static FormField<Double> doubleParam = FormFields.doubleValue("doubleParam");
static FormField<Byte> hexByteParam = FormFields.hexByteValue("hexByteParam");
static FormField<Short> hexShortParam = FormFields.hexShortValue("hexShortParam");
static FormField<Integer> hexIntParam = FormFields.hexIntValue("hexIntParam");
static FormField<Long> hexLongParam = FormFields.hexLongValue("hexLongParam");
static RequestVal<String> nameWithDefault = FormFields.stringValue("nameWithDefault").withDefault("John Doe");
static RequestVal<Option<Integer>> optionalIntParam = FormFields.intValue("optionalIntParam").optional();
private Map.Entry<String, String> entry(String name, String value) {
return new AbstractMap.SimpleImmutableEntry<String, String>(name, value);
}
private HttpRequest urlEncodedRequest(Map.Entry<String, String>... entries) {
StringBuilder sb = new StringBuilder();
boolean next = false;
for (Map.Entry<String, String> entry: entries) {
if (next) {
sb.append('&');
next = true;
}
sb.append(entry.getKey());
sb.append('=');
sb.append(entry.getValue());
}
return
HttpRequest.POST("/test")
.withEntity(MediaTypes.APPLICATION_X_WWW_FORM_URLENCODED.toContentType(), sb.toString());
}
private HttpRequest singleParameterUrlEncodedRequest(String name, String value) {
return urlEncodedRequest(entry(name, value));
}
@Test
public void testStringFormFieldExtraction() {
TestRoute route = testRoute(completeWithValueToString(stringParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(400)
.assertEntity("Request is missing required form field 'stringParam'");
route
.run(singleParameterUrlEncodedRequest("stringParam", "john"))
.assertStatusCode(200)
.assertEntity("john");
}
@Test
public void testByteFormFieldExtraction() {
TestRoute route = testRoute(completeWithValueToString(byteParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(400)
.assertEntity("Request is missing required form field 'byteParam'");
route
.run(singleParameterUrlEncodedRequest("byteParam", "test"))
.assertStatusCode(400)
.assertEntity("The form field 'byteParam' was malformed:\n'test' is not a valid 8-bit signed integer value");
route
.run(singleParameterUrlEncodedRequest("byteParam", "1000"))
.assertStatusCode(400)
.assertEntity("The form field 'byteParam' was malformed:\n'1000' is not a valid 8-bit signed integer value");
route
.run(singleParameterUrlEncodedRequest("byteParam", "48"))
.assertStatusCode(200)
.assertEntity("48");
}
@Test
public void testShortFormFieldExtraction() {
TestRoute route = testRoute(completeWithValueToString(shortParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(400)
.assertEntity("Request is missing required form field 'shortParam'");
route
.run(singleParameterUrlEncodedRequest("shortParam", "test"))
.assertStatusCode(400)
.assertEntity("The form field 'shortParam' was malformed:\n'test' is not a valid 16-bit signed integer value");
route
.run(singleParameterUrlEncodedRequest("shortParam", "100000"))
.assertStatusCode(400)
.assertEntity("The form field 'shortParam' was malformed:\n'100000' is not a valid 16-bit signed integer value");
route
.run(singleParameterUrlEncodedRequest("shortParam", "1234"))
.assertStatusCode(200)
.assertEntity("1234");
}
@Test
public void testIntegerFormFieldExtraction() {
TestRoute route = testRoute(completeWithValueToString(intParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(400)
.assertEntity("Request is missing required form field 'intParam'");
route
.run(singleParameterUrlEncodedRequest("intParam", "test"))
.assertStatusCode(400)
.assertEntity("The form field 'intParam' was malformed:\n'test' is not a valid 32-bit signed integer value");
route
.run(singleParameterUrlEncodedRequest("intParam", "48"))
.assertStatusCode(200)
.assertEntity("48");
}
@Test
public void testLongFormFieldExtraction() {
TestRoute route = testRoute(completeWithValueToString(longParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(400)
.assertEntity("Request is missing required form field 'longParam'");
route
.run(singleParameterUrlEncodedRequest("longParam", "test"))
.assertStatusCode(400)
.assertEntity("The form field 'longParam' was malformed:\n'test' is not a valid 64-bit signed integer value");
route
.run(singleParameterUrlEncodedRequest("longParam", "123456"))
.assertStatusCode(200)
.assertEntity("123456");
}
@Test
public void testFloatFormFieldExtraction() {
TestRoute route = testRoute(completeWithValueToString(floatParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(400)
.assertEntity("Request is missing required form field 'floatParam'");
route
.run(singleParameterUrlEncodedRequest("floatParam", "test"))
.assertStatusCode(400)
.assertEntity("The form field 'floatParam' was malformed:\n'test' is not a valid 32-bit floating point value");
route
.run(singleParameterUrlEncodedRequest("floatParam", "48.123"))
.assertStatusCode(200)
.assertEntity("48.123");
}
@Test
public void testDoubleFormFieldExtraction() {
TestRoute route = testRoute(completeWithValueToString(doubleParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(400)
.assertEntity("Request is missing required form field 'doubleParam'");
route
.run(singleParameterUrlEncodedRequest("doubleParam", "test"))
.assertStatusCode(400)
.assertEntity("The form field 'doubleParam' was malformed:\n'test' is not a valid 64-bit floating point value");
route
.run(singleParameterUrlEncodedRequest("doubleParam", "0.234213235987"))
.assertStatusCode(200)
.assertEntity("0.234213235987");
}
@Test
public void testHexByteFormFieldExtraction() {
TestRoute route = testRoute(completeWithValueToString(hexByteParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(400)
.assertEntity("Request is missing required form field 'hexByteParam'");
route
.run(singleParameterUrlEncodedRequest("hexByteParam", "test"))
.assertStatusCode(400)
.assertEntity("The form field 'hexByteParam' was malformed:\n'test' is not a valid 8-bit hexadecimal integer value");
route
.run(singleParameterUrlEncodedRequest("hexByteParam", "1000"))
.assertStatusCode(400)
.assertEntity("The form field 'hexByteParam' was malformed:\n'1000' is not a valid 8-bit hexadecimal integer value");
route
.run(singleParameterUrlEncodedRequest("hexByteParam", "48"))
.assertStatusCode(200)
.assertEntity(Integer.toString(0x48));
}
@Test
public void testHexShortFormFieldExtraction() {
TestRoute route = testRoute(completeWithValueToString(hexShortParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(400)
.assertEntity("Request is missing required form field 'hexShortParam'");
route
.run(singleParameterUrlEncodedRequest("hexShortParam", "test"))
.assertStatusCode(400)
.assertEntity("The form field 'hexShortParam' was malformed:\n'test' is not a valid 16-bit hexadecimal integer value");
route
.run(singleParameterUrlEncodedRequest("hexShortParam", "100000"))
.assertStatusCode(400)
.assertEntity("The form field 'hexShortParam' was malformed:\n'100000' is not a valid 16-bit hexadecimal integer value");
route
.run(singleParameterUrlEncodedRequest("hexShortParam", "1234"))
.assertStatusCode(200)
.assertEntity(Integer.toString(0x1234));
}
@Test
public void testHexIntegerFormFieldExtraction() {
TestRoute route = testRoute(completeWithValueToString(hexIntParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(400)
.assertEntity("Request is missing required form field 'hexIntParam'");
route
.run(singleParameterUrlEncodedRequest("hexIntParam", "test"))
.assertStatusCode(400)
.assertEntity("The form field 'hexIntParam' was malformed:\n'test' is not a valid 32-bit hexadecimal integer value");
route
.run(singleParameterUrlEncodedRequest("hexIntParam", "12345678"))
.assertStatusCode(200)
.assertEntity(Integer.toString(0x12345678));
}
@Test
public void testHexLongFormFieldExtraction() {
TestRoute route = testRoute(completeWithValueToString(hexLongParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(400)
.assertEntity("Request is missing required form field 'hexLongParam'");
route
.run(singleParameterUrlEncodedRequest("hexLongParam", "test"))
.assertStatusCode(400)
.assertEntity("The form field 'hexLongParam' was malformed:\n'test' is not a valid 64-bit hexadecimal integer value");
route
.run(singleParameterUrlEncodedRequest("hexLongParam", "123456789a"))
.assertStatusCode(200)
.assertEntity(Long.toString(0x123456789aL));
}
@Test
public void testOptionalIntFormFieldExtraction() {
TestRoute route = testRoute(completeWithValueToString(optionalIntParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(200)
.assertEntity("None");
route
.run(singleParameterUrlEncodedRequest("optionalIntParam", "23"))
.assertStatusCode(200)
.assertEntity("Some(23)");
}
@Test
public void testStringFormFieldExtractionWithDefaultValue() {
TestRoute route = testRoute(completeWithValueToString(nameWithDefault));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(200)
.assertEntity("John Doe");
route
.run(singleParameterUrlEncodedRequest("nameWithDefault", "paul"))
.assertStatusCode(200)
.assertEntity("paul");
}
}

View file

@ -0,0 +1,136 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.javadsl.server.values;
import org.junit.Test;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.http.javadsl.testkit.TestRoute;
import akka.http.javadsl.model.*;
import akka.http.javadsl.model.headers.Age;
import akka.http.javadsl.model.headers.RawHeader;
import akka.http.javadsl.server.values.*;
public class HeadersTest extends JUnitRouteTest {
Header<HttpHeader> XTestHeader = Headers.byName("X-Test-Header");
Header<Age> AgeHeader = Headers.byClass(Age.class);
RawHeader testHeaderInstance = RawHeader.create("X-Test-Header", "abcdef-test");
Age ageHeaderInstance = Age.create(1000);
@Test
public void testValueByName() {
TestRoute route = testRoute(completeWithValueToString(XTestHeader.value()));
route
.run(HttpRequest.create())
.assertStatusCode(400)
.assertEntity("Request is missing required HTTP header 'X-Test-Header'");
route
.run(HttpRequest.create().addHeader(testHeaderInstance))
.assertStatusCode(200)
.assertEntity("abcdef-test");
}
@Test
public void testOptionalValueByName() {
TestRoute route = testRoute(completeWithValueToString(XTestHeader.optionalValue()));
route
.run(HttpRequest.create())
.assertStatusCode(200)
.assertEntity("None");
route
.run(HttpRequest.create().addHeader(testHeaderInstance))
.assertStatusCode(200)
.assertEntity("Some(abcdef-test)");
}
@Test
public void testInstanceByName() {
TestRoute route = testRoute(completeWithValueToString(XTestHeader.instance()));
route
.run(HttpRequest.create())
.assertStatusCode(400)
.assertEntity("Request is missing required HTTP header 'X-Test-Header'");
route
.run(HttpRequest.create().addHeader(testHeaderInstance))
.assertStatusCode(200)
.assertEntity("X-Test-Header: abcdef-test");
}
@Test
public void testOptionalInstanceByName() {
TestRoute route = testRoute(completeWithValueToString(XTestHeader.optionalInstance()));
route
.run(HttpRequest.create())
.assertStatusCode(200)
.assertEntity("None");
route
.run(HttpRequest.create().addHeader(testHeaderInstance))
.assertStatusCode(200)
.assertEntity("Some(X-Test-Header: abcdef-test)");
}
@Test
public void testValueByClass() {
TestRoute route = testRoute(completeWithValueToString(AgeHeader.value()));
route
.run(HttpRequest.create())
.assertStatusCode(400)
.assertEntity("Request is missing required HTTP header 'Age'");
route
.run(HttpRequest.create().addHeader(ageHeaderInstance))
.assertStatusCode(200)
.assertEntity("1000");
}
@Test
public void testOptionalValueByClass() {
TestRoute route = testRoute(completeWithValueToString(AgeHeader.optionalValue()));
route
.run(HttpRequest.create())
.assertStatusCode(200)
.assertEntity("None");
route
.run(HttpRequest.create().addHeader(ageHeaderInstance))
.assertStatusCode(200)
.assertEntity("Some(1000)");
}
@Test
public void testInstanceByClass() {
TestRoute route = testRoute(completeWithValueToString(AgeHeader.instance()));
route
.run(HttpRequest.create())
.assertStatusCode(400)
.assertEntity("Request is missing required HTTP header 'Age'");
route
.run(HttpRequest.create().addHeader(ageHeaderInstance))
.assertStatusCode(200)
.assertEntity("Age: 1000");
}
@Test
public void testOptionalInstanceByClass() {
TestRoute route = testRoute(completeWithValueToString(AgeHeader.optionalInstance()));
route
.run(HttpRequest.create())
.assertStatusCode(200)
.assertEntity("None");
route
.run(HttpRequest.create().addHeader(ageHeaderInstance))
.assertStatusCode(200)
.assertEntity("Some(Age: 1000)");
}
}

View file

@ -0,0 +1,411 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.javadsl.server.values;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.server.Handler1;
import akka.http.javadsl.server.RequestContext;
import akka.http.javadsl.server.RequestVal;
import akka.http.javadsl.server.RouteResult;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.http.javadsl.testkit.TestRoute;
import akka.japi.Option;
import org.junit.Test;
import java.util.*;
public class ParametersTest extends JUnitRouteTest {
static Parameter<String> stringParam = Parameters.stringValue("stringParam");
static Parameter<Byte> byteParam = Parameters.byteValue("byteParam");
static Parameter<Short> shortParam = Parameters.shortValue("shortParam");
static Parameter<Integer> intParam = Parameters.intValue("intParam");
static Parameter<Long> longParam = Parameters.longValue("longParam");
static Parameter<Float> floatParam = Parameters.floatValue("floatParam");
static Parameter<Double> doubleParam = Parameters.doubleValue("doubleParam");
static Parameter<Byte> hexByteParam = Parameters.hexByteValue("hexByteParam");
static Parameter<Short> hexShortParam = Parameters.hexShortValue("hexShortParam");
static Parameter<Integer> hexIntParam = Parameters.hexIntValue("hexIntParam");
static Parameter<Long> hexLongParam = Parameters.hexLongValue("hexLongParam");
static RequestVal<String> nameWithDefault = Parameters.stringValue("nameWithDefault").withDefault("John Doe");
static RequestVal<Option<Integer>> optionalIntParam = Parameters.intValue("optionalIntParam").optional();
static RequestVal<Map<String, String>> paramMap = Parameters.asMap();
static RequestVal<Map<String, Collection<String>>> paramMultiMap = Parameters.asMultiMap();
static RequestVal<Collection<Map.Entry<String, String>>> paramEntries = Parameters.asCollection();
@Test
public void testStringParameterExtraction() {
TestRoute route = testRoute(completeWithValueToString(stringParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(404)
.assertEntity("Request is missing required query parameter 'stringParam'");
route
.run(HttpRequest.create().withUri("/abc?stringParam=john"))
.assertStatusCode(200)
.assertEntity("john");
}
@Test
public void testByteParameterExtraction() {
TestRoute route = testRoute(completeWithValueToString(byteParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(404)
.assertEntity("Request is missing required query parameter 'byteParam'");
route
.run(HttpRequest.create().withUri("/abc?byteParam=test"))
.assertStatusCode(400)
.assertEntity("The query parameter 'byteParam' was malformed:\n'test' is not a valid 8-bit signed integer value");
route
.run(HttpRequest.create().withUri("/abc?byteParam=1000"))
.assertStatusCode(400)
.assertEntity("The query parameter 'byteParam' was malformed:\n'1000' is not a valid 8-bit signed integer value");
route
.run(HttpRequest.create().withUri("/abc?byteParam=48"))
.assertStatusCode(200)
.assertEntity("48");
}
@Test
public void testShortParameterExtraction() {
TestRoute route = testRoute(completeWithValueToString(shortParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(404)
.assertEntity("Request is missing required query parameter 'shortParam'");
route
.run(HttpRequest.create().withUri("/abc?shortParam=test"))
.assertStatusCode(400)
.assertEntity("The query parameter 'shortParam' was malformed:\n'test' is not a valid 16-bit signed integer value");
route
.run(HttpRequest.create().withUri("/abc?shortParam=100000"))
.assertStatusCode(400)
.assertEntity("The query parameter 'shortParam' was malformed:\n'100000' is not a valid 16-bit signed integer value");
route
.run(HttpRequest.create().withUri("/abc?shortParam=1234"))
.assertStatusCode(200)
.assertEntity("1234");
}
@Test
public void testIntegerParameterExtraction() {
TestRoute route = testRoute(completeWithValueToString(intParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(404)
.assertEntity("Request is missing required query parameter 'intParam'");
route
.run(HttpRequest.create().withUri("/abc?intParam=test"))
.assertStatusCode(400)
.assertEntity("The query parameter 'intParam' was malformed:\n'test' is not a valid 32-bit signed integer value");
route
.run(HttpRequest.create().withUri("/abc?intParam=48"))
.assertStatusCode(200)
.assertEntity("48");
}
@Test
public void testLongParameterExtraction() {
TestRoute route = testRoute(completeWithValueToString(longParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(404)
.assertEntity("Request is missing required query parameter 'longParam'");
route
.run(HttpRequest.create().withUri("/abc?longParam=test"))
.assertStatusCode(400)
.assertEntity("The query parameter 'longParam' was malformed:\n'test' is not a valid 64-bit signed integer value");
route
.run(HttpRequest.create().withUri("/abc?longParam=123456"))
.assertStatusCode(200)
.assertEntity("123456");
}
@Test
public void testFloatParameterExtraction() {
TestRoute route = testRoute(completeWithValueToString(floatParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(404)
.assertEntity("Request is missing required query parameter 'floatParam'");
route
.run(HttpRequest.create().withUri("/abc?floatParam=test"))
.assertStatusCode(400)
.assertEntity("The query parameter 'floatParam' was malformed:\n'test' is not a valid 32-bit floating point value");
route
.run(HttpRequest.create().withUri("/abc?floatParam=48"))
.assertStatusCode(200)
.assertEntity("48.0");
}
@Test
public void testDoubleParameterExtraction() {
TestRoute route = testRoute(completeWithValueToString(doubleParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(404)
.assertEntity("Request is missing required query parameter 'doubleParam'");
route
.run(HttpRequest.create().withUri("/abc?doubleParam=test"))
.assertStatusCode(400)
.assertEntity("The query parameter 'doubleParam' was malformed:\n'test' is not a valid 64-bit floating point value");
route
.run(HttpRequest.create().withUri("/abc?doubleParam=48"))
.assertStatusCode(200)
.assertEntity("48.0");
}
@Test
public void testHexByteParameterExtraction() {
TestRoute route = testRoute(completeWithValueToString(hexByteParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(404)
.assertEntity("Request is missing required query parameter 'hexByteParam'");
route
.run(HttpRequest.create().withUri("/abc?hexByteParam=test"))
.assertStatusCode(400)
.assertEntity("The query parameter 'hexByteParam' was malformed:\n'test' is not a valid 8-bit hexadecimal integer value");
route
.run(HttpRequest.create().withUri("/abc?hexByteParam=1000"))
.assertStatusCode(400)
.assertEntity("The query parameter 'hexByteParam' was malformed:\n'1000' is not a valid 8-bit hexadecimal integer value");
route
.run(HttpRequest.create().withUri("/abc?hexByteParam=48"))
.assertStatusCode(200)
.assertEntity(Integer.toString(0x48));
}
@Test
public void testHexShortParameterExtraction() {
TestRoute route = testRoute(completeWithValueToString(hexShortParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(404)
.assertEntity("Request is missing required query parameter 'hexShortParam'");
route
.run(HttpRequest.create().withUri("/abc?hexShortParam=test"))
.assertStatusCode(400)
.assertEntity("The query parameter 'hexShortParam' was malformed:\n'test' is not a valid 16-bit hexadecimal integer value");
route
.run(HttpRequest.create().withUri("/abc?hexShortParam=100000"))
.assertStatusCode(400)
.assertEntity("The query parameter 'hexShortParam' was malformed:\n'100000' is not a valid 16-bit hexadecimal integer value");
route
.run(HttpRequest.create().withUri("/abc?hexShortParam=1234"))
.assertStatusCode(200)
.assertEntity(Integer.toString(0x1234));
}
@Test
public void testHexIntegerParameterExtraction() {
TestRoute route = testRoute(completeWithValueToString(hexIntParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(404)
.assertEntity("Request is missing required query parameter 'hexIntParam'");
route
.run(HttpRequest.create().withUri("/abc?hexIntParam=test"))
.assertStatusCode(400)
.assertEntity("The query parameter 'hexIntParam' was malformed:\n'test' is not a valid 32-bit hexadecimal integer value");
route
.run(HttpRequest.create().withUri("/abc?hexIntParam=12345678"))
.assertStatusCode(200)
.assertEntity(Integer.toString(0x12345678));
}
@Test
public void testHexLongParameterExtraction() {
TestRoute route = testRoute(completeWithValueToString(hexLongParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(404)
.assertEntity("Request is missing required query parameter 'hexLongParam'");
route
.run(HttpRequest.create().withUri("/abc?hexLongParam=test"))
.assertStatusCode(400)
.assertEntity("The query parameter 'hexLongParam' was malformed:\n'test' is not a valid 64-bit hexadecimal integer value");
route
.run(HttpRequest.create().withUri("/abc?hexLongParam=123456789a"))
.assertStatusCode(200)
.assertEntity(Long.toString(0x123456789aL));
}
@Test
public void testParametersAsMapExtraction() {
TestRoute route = testRoute(handleWith(paramMap, new Handler1<Map<String, String>>(){
@Override
public RouteResult handle(RequestContext ctx, Map<String, String> paramMap) {
ArrayList<String> keys = new ArrayList<String>(paramMap.keySet());
Collections.sort(keys);
StringBuilder res = new StringBuilder();
res.append(paramMap.size()).append(": [");
for (String key: keys)
res.append(key).append(" -> ").append(paramMap.get(key)).append(", ");
res.append(']');
return ctx.complete(res.toString());
}
}));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(200)
.assertEntity("0: []");
route
.run(HttpRequest.create().withUri("/abc?a=b"))
.assertStatusCode(200)
.assertEntity("1: [a -> b, ]");
route
.run(HttpRequest.create().withUri("/abc?a=b&c=d"))
.assertStatusCode(200)
.assertEntity("2: [a -> b, c -> d, ]");
}
@Test
public void testParametersAsMultiMapExtraction() {
TestRoute route = testRoute(handleWith(paramMultiMap, new Handler1<Map<String, Collection<String>>>(){
@Override
public RouteResult handle(RequestContext ctx, Map<String, Collection<String>> paramMap) {
ArrayList<String> keys = new ArrayList<String>(paramMap.keySet());
Collections.sort(keys);
StringBuilder res = new StringBuilder();
res.append(paramMap.size()).append(": [");
for (String key: keys) {
res.append(key).append(" -> [");
ArrayList<String> values = new ArrayList<String>(paramMap.get(key));
Collections.sort(values);
for (String value: values)
res.append(value).append(", ");
res.append("], ");
}
res.append(']');
return ctx.complete(res.toString());
}
}));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(200)
.assertEntity("0: []");
route
.run(HttpRequest.create().withUri("/abc?a=b"))
.assertStatusCode(200)
.assertEntity("1: [a -> [b, ], ]");
route
.run(HttpRequest.create().withUri("/abc?a=b&c=d&a=a"))
.assertStatusCode(200)
.assertEntity("2: [a -> [a, b, ], c -> [d, ], ]");
}
@Test
public void testParametersAsCollectionExtraction() {
TestRoute route = testRoute(handleWith(paramEntries, new Handler1<Collection<Map.Entry<String, String>>>(){
@Override
public RouteResult handle(RequestContext ctx, Collection<Map.Entry<String, String>> paramEntries) {
ArrayList<Map.Entry<String, String>> entries = new ArrayList<Map.Entry<String, String>>(paramEntries);
Collections.sort(entries, new Comparator<Map.Entry<String, String>>() {
@Override
public int compare(Map.Entry<String, String> e1, Map.Entry<String, String> e2) {
int res = e1.getKey().compareTo(e2.getKey());
return res == 0 ? e1.getValue().compareTo(e2.getValue()) : res;
}
});
StringBuilder res = new StringBuilder();
res.append(paramEntries.size()).append(": [");
for (Map.Entry<String, String> entry: entries)
res.append(entry.getKey()).append(" -> ").append(entry.getValue()).append(", ");
res.append(']');
return ctx.complete(res.toString());
}
}));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(200)
.assertEntity("0: []");
route
.run(HttpRequest.create().withUri("/abc?a=b&e=f&c=d"))
.assertStatusCode(200)
.assertEntity("3: [a -> b, c -> d, e -> f, ]");
route
.run(HttpRequest.create().withUri("/abc?a=b&e=f&c=d&a=z"))
.assertStatusCode(200)
.assertEntity("4: [a -> b, a -> z, c -> d, e -> f, ]");
}
@Test
public void testOptionalIntParameterExtraction() {
TestRoute route = testRoute(completeWithValueToString(optionalIntParam));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(200)
.assertEntity("None");
route
.run(HttpRequest.create().withUri("/abc?optionalIntParam=23"))
.assertStatusCode(200)
.assertEntity("Some(23)");
}
@Test
public void testStringParameterExtractionWithDefaultValue() {
TestRoute route = testRoute(completeWithValueToString(nameWithDefault));
route
.run(HttpRequest.create().withUri("/abc"))
.assertStatusCode(200)
.assertEntity("John Doe");
route
.run(HttpRequest.create().withUri("/abc?nameWithDefault=paul"))
.assertStatusCode(200)
.assertEntity("paul");
}
}

View file

@ -0,0 +1,102 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.javadsl.server.values;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.RemoteAddress;
import akka.http.javadsl.model.Uri;
import akka.http.javadsl.model.headers.RawHeader;
import akka.http.javadsl.model.headers.XForwardedFor;
import akka.http.javadsl.server.RequestVals;
import akka.http.javadsl.server.Unmarshallers;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.http.javadsl.testkit.TestRoute;
import org.junit.Test;
import java.util.regex.Pattern;
public class RequestValTest extends JUnitRouteTest {
@Test
public void testSchemeExtraction() {
TestRoute route = testRoute(completeWithValueToString(RequestVals.scheme()));
route
.run(HttpRequest.create().withUri(Uri.create("http://example.org")))
.assertStatusCode(200)
.assertEntity("http");
route
.run(HttpRequest.create().withUri(Uri.create("https://example.org")))
.assertStatusCode(200)
.assertEntity("https");
}
@Test
public void testHostExtraction() {
TestRoute route = testRoute(completeWithValueToString(RequestVals.host()));
route
.run(HttpRequest.create().withUri(Uri.create("http://example.org")))
.assertStatusCode(200)
.assertEntity("example.org");
}
@Test
public void testHostPatternExtraction() {
TestRoute route =
testRoute(
completeWithValueToString(
RequestVals.matchAndExtractHost(Pattern.compile(".*\\.([^.]*)"))));
route
.run(HttpRequest.create().withUri(Uri.create("http://example.org")))
.assertStatusCode(200)
.assertEntity("org");
route
.run(HttpRequest.create().withUri(Uri.create("http://example.de")))
.assertStatusCode(200)
.assertEntity("de");
}
@Test
public void testClientIpExtraction() {
TestRoute route = testRoute(completeWithValueToString(RequestVals.clientIP()));
route
.run(HttpRequest.create().addHeader(XForwardedFor.create(RemoteAddress.create("127.0.0.2"))))
.assertStatusCode(200)
.assertEntity("127.0.0.2");
route
.run(HttpRequest.create().addHeader(akka.http.javadsl.model.headers.RemoteAddress.create(RemoteAddress.create("127.0.0.3"))))
.assertStatusCode(200)
.assertEntity("127.0.0.3");
route
.run(HttpRequest.create().addHeader(RawHeader.create("X-Real-IP", "127.0.0.4")))
.assertStatusCode(200)
.assertEntity("127.0.0.4");
route
.run(HttpRequest.create())
.assertStatusCode(404);
}
@Test
public void testEntityAsString() {
TestRoute route =
testRoute(
completeWithValueToString(RequestVals.entityAs(Unmarshallers.String()))
);
HttpRequest request =
HttpRequest.POST("/")
.withEntity("abcdef");
route.run(request)
.assertStatusCode(200)
.assertEntity("abcdef");
}
}

View file

@ -9,7 +9,6 @@ import org.scalatest.{ FreeSpec, Inside }
import akka.http.scaladsl.unmarshalling.Unmarshaller.HexInt
class ParameterDirectivesSpec extends FreeSpec with GenericRoutingSpec with Inside {
"when used with 'as[Int]' the parameter directive should" - {
"extract a parameter value as Int" in {
Get("/?amount=123") ~> {
@ -205,4 +204,23 @@ class ParameterDirectivesSpec extends FreeSpec with GenericRoutingSpec with Insi
} ~> check { responseAs[String] === "List(3, 10)" }
}
}
"The 'parameterSeq' directive should" - {
val completeAsList =
parameterSeq { params
val sorted = params.sorted
complete(s"${sorted.size}: [${sorted.map(e e._1 + " -> " + e._2).mkString(", ")}]")
}
"extract parameters with different keys" in {
Get("/?a=b&e=f&c=d") ~> completeAsList ~> check {
responseAs[String] shouldEqual "3: [a -> b, c -> d, e -> f]"
}
}
"extract parameters with duplicate keys" in {
Get("/?a=b&e=f&c=d&a=z") ~> completeAsList ~> check {
responseAs[String] shouldEqual "4: [a -> b, a -> z, c -> d, e -> f]"
}
}
}
}

View file

@ -0,0 +1,32 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.impl.server
import akka.http.javadsl.model.headers.HttpCookie
import akka.http.javadsl.server.values.Cookie
import akka.http.javadsl.server.{ Directive, Directives, RequestVal }
import akka.http.scaladsl.server.Directive1
import akka.http.scaladsl.server.directives.CookieDirectives._
import akka.japi.Option
import akka.http.impl.util.JavaMapping.Implicits._
case class CookieImpl(name: String, domain: Option[String] = None, path: Option[String] = None) extends Cookie {
def withDomain(domain: String): Cookie = copy(domain = Option.some(domain))
def withPath(path: String): Cookie = copy(path = Option.some(path))
val value: RequestVal[String] =
new StandaloneExtractionImpl[String] {
def directive: Directive1[String] = cookie(name).map(_.value)
}
def optionalValue(): RequestVal[Option[String]] =
new StandaloneExtractionImpl[Option[String]] {
def directive: Directive1[Option[String]] = optionalCookie(name).map(_.map(_.value).asJava)
}
def set(value: String): Directive =
Directives.custom(Directives.setCookie(
HttpCookie.create(name, value, domain, path), _, _: _*))
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.impl.server
import akka.http.javadsl.server.RequestVal
import akka.http.javadsl.server.values.FormField
import akka.http.scaladsl.common.{ StrictForm, NameUnmarshallerReceptacle, NameReceptacle }
import akka.http.scaladsl.unmarshalling._
import akka.http.scaladsl.util.FastFuture
import akka.japi.{ Option JOption }
import scala.concurrent.{ Future, ExecutionContext }
import scala.reflect.ClassTag
import akka.http.scaladsl.server.directives.FormFieldDirectives
import akka.http.scaladsl.server.{ Directives, Directive1 }
import FormFieldDirectives._
/**
* INTERNAL API
*/
private[http] class FormFieldImpl[T, U](receptacle: NameReceptacle[T])(
implicit fu: FromStrictFormFieldUnmarshaller[T], tTag: ClassTag[U], conv: T U)
extends StandaloneExtractionImpl[U] with FormField[U] {
import Directives._
def directive: Directive1[U] =
extractMaterializer.flatMap { implicit fm
formField(receptacle).map(conv)
}
def optional: RequestVal[JOption[U]] =
new StandaloneExtractionImpl[JOption[U]] {
def directive: Directive1[JOption[U]] = optionalDirective
}
private def optionalDirective: Directive1[JOption[U]] =
extractMaterializer.flatMap { implicit fm
formField(receptacle.?).map(v JOption.fromScalaOption(v.map(conv)))
}
def withDefault(defaultValue: U): RequestVal[U] =
new StandaloneExtractionImpl[U] {
def directive: Directive1[U] = optionalDirective.map(_.getOrElse(defaultValue))
}
}
object FormFieldImpl {
def apply[T, U](receptacle: NameReceptacle[T])(implicit fu: FromStrictFormFieldUnmarshaller[T], tTag: ClassTag[U], conv: T U): FormField[U] =
new FormFieldImpl[T, U](receptacle)(fu, tTag, conv)
def apply[T, U](receptacle: NameUnmarshallerReceptacle[T])(implicit tTag: ClassTag[U], conv: T U): FormField[U] =
apply(new NameReceptacle[T](receptacle.name))(StrictForm.Field.unmarshallerFromFSU(receptacle.um), tTag, conv)
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.impl.server
import akka.http.javadsl.model.HttpHeader
import akka.http.javadsl.server.RequestVal
import akka.http.javadsl.server.values.Header
import akka.http.scaladsl
import akka.http.scaladsl.server._
import akka.http.scaladsl.server.directives.BasicDirectives._
import akka.http.scaladsl.server.directives.RouteDirectives._
import scala.reflect.ClassTag
/**
* Internal API
*/
private[http] object HeaderImpl {
def apply[T <: HttpHeader](
name: String,
optionalDirective: ClassTag[T with scaladsl.model.HttpHeader] Directive1[Option[T with scaladsl.model.HttpHeader]], tClassTag: ClassTag[T]): Header[T] = {
type U = T with scaladsl.model.HttpHeader
// cast is safe because creation of javadsl.model.HttpHeader that are not <: scaladsl.model.HttpHeader is forbidden
implicit def uClassTag = tClassTag.asInstanceOf[ClassTag[U]]
new Header[U] {
val instanceDirective: Directive1[U] =
optionalDirective(uClassTag).flatMap {
case Some(v) provide(v)
case None reject(MissingHeaderRejection(name))
}
def instance(): RequestVal[U] =
new StandaloneExtractionImpl[U] {
def directive: Directive1[U] = instanceDirective
}
def optionalInstance(): RequestVal[Option[U]] =
new StandaloneExtractionImpl[Option[U]] {
def directive: Directive1[Option[U]] = optionalDirective(uClassTag)
}
def value(): RequestVal[String] =
new StandaloneExtractionImpl[String] {
def directive: Directive1[String] = instanceDirective.map(_.value)
}
def optionalValue(): RequestVal[Option[String]] =
new StandaloneExtractionImpl[Option[String]] {
def directive: Directive1[Option[String]] = optionalDirective(uClassTag).map(_.map(_.value))
}
}.asInstanceOf[Header[T]] // undeclared covariance
}
}

View file

@ -4,22 +4,46 @@
package akka.http.impl.server
import akka.http.javadsl.server.RequestVal
import akka.http.javadsl.server.values.Parameter
import akka.http.scaladsl.common.{ NameUnmarshallerReceptacle, NameReceptacle }
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.unmarshalling._
import akka.japi.{ Option JOption }
import scala.concurrent.ExecutionContext
import scala.reflect.ClassTag
import akka.http.scaladsl.server.directives.{ ParameterDirectives, BasicDirectives }
import akka.http.scaladsl.server.directives.ParameterDirectives
import akka.http.scaladsl.server.Directive1
import akka.http.scaladsl.server.directives.ParameterDirectives.ParamMagnet
/**
* INTERNAL API
*/
private[http] class ParameterImpl[T: ClassTag](val underlying: ExecutionContext ParamMagnet { type Out = Directive1[T] })
extends StandaloneExtractionImpl[T] with Parameter[T] {
private[http] class ParameterImpl[T, U](receptacle: NameReceptacle[T])(
implicit fu: FromStringUnmarshaller[T], tTag: ClassTag[U], conv: T U)
extends StandaloneExtractionImpl[U] with Parameter[U] {
def directive: Directive1[T] =
BasicDirectives.extractExecutionContext.flatMap { implicit ec
ParameterDirectives.parameter(underlying(ec))
import ParameterDirectives._
def directive: Directive1[U] = parameter(receptacle).map(conv)
def optional: RequestVal[JOption[U]] =
new StandaloneExtractionImpl[JOption[U]] {
def directive: Directive1[JOption[U]] = optionalDirective
}
private def optionalDirective: Directive1[JOption[U]] =
extractMaterializer.flatMap { implicit fm
parameter(receptacle.?).map(v JOption.fromScalaOption(v.map(conv)))
}
def withDefault(defaultValue: U): RequestVal[U] =
new StandaloneExtractionImpl[U] {
def directive: Directive1[U] = optionalDirective.map(_.getOrElse(defaultValue))
}
}
private[http] object ParameterImpl {
def apply[T, U](receptacle: NameReceptacle[T])(implicit fu: FromStringUnmarshaller[T], tTag: ClassTag[U], conv: T U): Parameter[U] =
new ParameterImpl(receptacle)(fu, tTag, conv)
def apply[T, U](receptacle: NameUnmarshallerReceptacle[T])(implicit tTag: ClassTag[U], conv: T U): Parameter[U] =
new ParameterImpl(new NameReceptacle(receptacle.name))(receptacle.um, tTag, conv)
}

View file

@ -5,6 +5,7 @@
package akka.http.impl.server
import akka.http.javadsl.server.values.PathMatcher
import akka.japi.Option
import scala.reflect.ClassTag
import akka.http.scaladsl.server.{ PathMatcher ScalaPathMatcher }
@ -13,4 +14,6 @@ import akka.http.scaladsl.server.{ PathMatcher ⇒ ScalaPathMatcher }
* INTERNAL API
*/
private[http] class PathMatcherImpl[T: ClassTag](val matcher: ScalaPathMatcher[Tuple1[T]])
extends ExtractionImpl[T] with PathMatcher[T]
extends ExtractionImpl[T] with PathMatcher[T] {
def optional: PathMatcher[Option[T]] = new PathMatcherImpl[Option[T]](matcher.?.map(Option.fromScalaOption))
}

View file

@ -4,7 +4,7 @@
package akka.http.impl.server
import scala.concurrent.Future
import scala.concurrent.{ ExecutionContext, Future }
import akka.http.javadsl.{ model jm }
import akka.http.impl.util.JavaMapping.Implicits._
import akka.http.scaladsl.server.{ RequestContext ScalaRequestContext }
@ -14,8 +14,6 @@ import akka.http.javadsl.server._
* INTERNAL API
*/
private[http] final case class RequestContextImpl(underlying: ScalaRequestContext) extends RequestContext {
import underlying.executionContext
// provides auto-conversion to japi.RouteResult
import RouteResultImpl._
@ -25,7 +23,7 @@ private[http] final case class RequestContextImpl(underlying: ScalaRequestContex
def completeWith(futureResult: Future[RouteResult]): RouteResult =
futureResult.flatMap {
case r: RouteResultImpl r.underlying
}
}(executionContext)
def complete(text: String): RouteResult = underlying.complete(text)
def completeWithStatus(statusCode: Int): RouteResult =
completeWithStatus(jm.StatusCodes.get(statusCode))
@ -40,4 +38,6 @@ private[http] final case class RequestContextImpl(underlying: ScalaRequestContex
def complete(response: jm.HttpResponse): RouteResult = underlying.complete(response.asScala)
def notFound(): RouteResult = underlying.reject()
def executionContext: ExecutionContext = underlying.executionContext
}

View file

@ -4,7 +4,10 @@
package akka.http.impl.server
import akka.http.impl.util.JavaMapping
import akka.http.javadsl.server.values.{ PathMatcher, BasicUserCredentials }
import akka.http.scaladsl.model.StatusCodes.Redirection
import akka.http.scaladsl.server.util.TupleOps.Join
import scala.language.implicitConversions
import scala.annotation.tailrec
@ -13,8 +16,8 @@ import akka.http.javadsl.model.ContentType
import akka.http.scaladsl.server.directives.{ UserCredentials, ContentTypeResolver }
import akka.http.scaladsl.server.directives.FileAndResourceDirectives.DirectoryRenderer
import akka.http.scaladsl.model.HttpHeader
import akka.http.scaladsl.model.headers.CustomHeader
import akka.http.scaladsl.server.{ Route ScalaRoute, Directive0, Directives }
import akka.http.scaladsl.model.headers.{ HttpCookie, CustomHeader }
import akka.http.scaladsl.server.{ Route ScalaRoute, Directive ScalaDirective, PathMatcher ScalaPathMatcher, PathMatcher1, Directive0, Directive1, Directives }
import akka.http.impl.util.JavaMapping.Implicits._
import akka.http.scaladsl.server
import akka.http.javadsl.server._
@ -26,11 +29,13 @@ import RouteStructure._
private[http] trait ExtractionMap extends CustomHeader {
def get[T](key: RequestVal[T]): Option[T]
def set[T](key: RequestVal[T], value: T): ExtractionMap
def addAll(values: Map[RequestVal[_], Any]): ExtractionMap
}
/**
* INTERNAL API
*/
private[http] object ExtractionMap {
val Empty = ExtractionMap(Map.empty)
implicit def apply(map: Map[RequestVal[_], Any]): ExtractionMap =
new ExtractionMap {
def get[T](key: RequestVal[T]): Option[T] =
@ -39,6 +44,9 @@ private[http] object ExtractionMap {
def set[T](key: RequestVal[T], value: T): ExtractionMap =
ExtractionMap(map.updated(key, value))
def addAll(values: Map[RequestVal[_], Any]): ExtractionMap =
ExtractionMap(map ++ values)
// CustomHeader methods
override def suppressRendering: Boolean = true
def name(): String = "ExtractedValues"
@ -50,99 +58,146 @@ private[http] object ExtractionMap {
* INTERNAL API
*/
private[http] object RouteImplementation extends Directives with server.RouteConcatenation {
def apply(route: Route): ScalaRoute = route match {
case RouteAlternatives(children)
val converted = children.map(RouteImplementation.apply)
converted.reduce(_ ~ _)
case RawPathPrefix(elements, children)
val inner = apply(RouteAlternatives(children))
def apply(route: Route): ScalaRoute = {
def directiveFor(route: DirectiveRoute): Directive0 = route match {
case RouteAlternatives() ScalaDirective.Empty
case RawPathPrefix(elements) pathMatcherDirective[String](elements, rawPathPrefix)
case RawPathPrefixTest(elements) pathMatcherDirective[String](elements, rawPathPrefixTest)
case PathSuffix(elements) pathMatcherDirective[String](elements, pathSuffix)
case PathSuffixTest(elements) pathMatcherDirective[String](elements, pathSuffixTest)
case RedirectToTrailingSlashIfMissing(code) redirectToTrailingSlashIfMissing(code.asScala.asInstanceOf[Redirection])
case RedirectToNoTrailingSlashIfPresent(code) redirectToNoTrailingSlashIfPresent(code.asScala.asInstanceOf[Redirection])
def one[T](matcher: PathMatcher[T]): Directive0 =
rawPathPrefix(matcher.asInstanceOf[PathMatcherImpl[T]].matcher) flatMap { value
addExtraction(matcher, value)
case MethodFilter(m) method(m.asScala)
case Extract(extractions)
extractRequestContext.flatMap { ctx
extractions.map { e
e.directive.flatMap(addExtraction(e.asInstanceOf[RequestVal[Any]], _))
}.reduce(_ & _)
}
elements.map(one(_)).reduce(_ & _).apply(inner)
case GetFromResource(path, contentType, classLoader)
getFromResource(path, contentType.asScala, classLoader)
case GetFromResourceDirectory(path, classLoader, resolver)
getFromResourceDirectory(path, classLoader)(scalaResolver(resolver))
case GetFromFile(file, contentType)
getFromFile(file, contentType.asScala)
case GetFromDirectory(directory, true, resolver)
extractExecutionContext { implicit ec
getFromBrowseableDirectory(directory.getPath)(DirectoryRenderer.defaultDirectoryRenderer, scalaResolver(resolver))
}
case FileAndResourceRouteWithDefaultResolver(constructor)
RouteImplementation(constructor(new directives.ContentTypeResolver {
def resolve(fileName: String): ContentType = ContentTypeResolver.Default(fileName)
}))
case BasicAuthentication(authenticator)
authenticateBasicAsync(authenticator.realm, { creds
val javaCreds =
creds match {
case UserCredentials.Missing
new BasicUserCredentials {
def available: Boolean = false
def userName: String = throw new IllegalStateException("Credentials missing")
def verifySecret(secret: String): Boolean = throw new IllegalStateException("Credentials missing")
}
case p @ UserCredentials.Provided(name)
new BasicUserCredentials {
def available: Boolean = true
def userName: String = name
def verifySecret(secret: String): Boolean = p.verifySecret(secret)
}
}
case MethodFilter(m, children)
val inner = apply(RouteAlternatives(children))
method(m.asScala).apply(inner)
authenticator.authenticate(javaCreds)
}).flatMap { user
addExtraction(authenticator.asInstanceOf[RequestVal[Any]], user)
}
case Extract(extractions, children)
val inner = apply(RouteAlternatives(children))
extractRequestContext.flatMap { ctx
extractions.map { e
e.directive.flatMap(addExtraction(e.asInstanceOf[RequestVal[Any]], _))
}.reduce(_ & _)
}.apply(inner)
case EncodeResponse(coders)
val scalaCoders = coders.map(_._underlyingScalaCoder())
encodeResponseWith(scalaCoders.head, scalaCoders.tail: _*)
case BasicAuthentication(authenticator, children)
val inner = apply(RouteAlternatives(children))
authenticateBasicAsync(authenticator.realm, { creds
val javaCreds =
creds match {
case UserCredentials.Missing
new BasicUserCredentials {
def available: Boolean = false
def userName: String = throw new IllegalStateException("Credentials missing")
def verifySecret(secret: String): Boolean = throw new IllegalStateException("Credentials missing")
}
case p @ UserCredentials.Provided(name)
new BasicUserCredentials {
def available: Boolean = true
def userName: String = name
def verifySecret(secret: String): Boolean = p.verifySecret(secret)
}
}
case DecodeRequest(coders) decodeRequestWith(coders.map(_._underlyingScalaCoder()): _*)
case Conditional(eTag, lastModified) conditional(eTag.map(_.asScala), lastModified.map(_.asScala))
case h: HostFilter host(h.filter _)
case SchemeFilter(schemeName) scheme(schemeName)
authenticator.authenticate(javaCreds)
}).flatMap { user
addExtraction(authenticator.asInstanceOf[RequestVal[Any]], user)
}.apply(inner)
case HandleExceptions(handler)
val pf: akka.http.scaladsl.server.ExceptionHandler = akka.http.scaladsl.server.ExceptionHandler {
case e: RuntimeException apply(handler.handle(e))
}
handleExceptions(pf)
case EncodeResponse(coders, children)
val scalaCoders = coders.map(_._underlyingScalaCoder())
encodeResponseWith(scalaCoders.head, scalaCoders.tail: _*).apply(apply(RouteAlternatives(children)))
case Validated(isValid, errorMsg) validate(isValid, errorMsg)
case RangeSupport() withRangeSupport
case SetCookie(cookie) setCookie(cookie.asScala)
case DeleteCookie(name, domain, path) deleteCookie(HttpCookie(name, domain = domain, path = path, value = "deleted"))
}
case Conditional(eTag, lastModified, children)
conditional(eTag.asScala, lastModified.asScala).apply(apply(RouteAlternatives(children)))
route match {
case route: DirectiveRoute directiveFor(route).apply(fromAlternatives(route.children))
case GetFromResource(path, contentType, classLoader) getFromResource(path, contentType.asScala, classLoader)
case GetFromResourceDirectory(path, classLoader, resolver) getFromResourceDirectory(path, classLoader)(scalaResolver(resolver))
case GetFromFile(file, contentType) getFromFile(file, contentType.asScala)
case GetFromDirectory(directory, true, resolver)
extractExecutionContext { implicit ec
getFromBrowseableDirectory(directory.getPath)(DirectoryRenderer.defaultDirectoryRenderer, scalaResolver(resolver))
}
case FileAndResourceRouteWithDefaultResolver(constructor)
RouteImplementation(constructor(new directives.ContentTypeResolver {
def resolve(fileName: String): ContentType = ContentTypeResolver.Default(fileName)
}))
case HandleExceptions(handler, children)
val pf: akka.http.scaladsl.server.ExceptionHandler = akka.http.scaladsl.server.ExceptionHandler {
case e: RuntimeException apply(handler.handle(e))
}
handleExceptions(pf).apply(apply(RouteAlternatives(children)))
case HandleWebsocketMessages(handler) handleWebsocketMessages(JavaMapping.toScala(handler))
case Redirect(uri, code) redirect(uri.asScala, code.asScala.asInstanceOf[Redirection]) // guarded by require in Redirect
case o: OpaqueRoute
(ctx o.handle(new RequestContextImpl(ctx)).asInstanceOf[RouteResultImpl].underlying)
case dyn: DynamicDirectiveRoute1[t1Type]
def runToRoute(t1: t1Type): ScalaRoute =
apply(dyn.createDirective(t1).route(dyn.innerRoute, dyn.moreInnerRoutes: _*))
case p: Product extractExecutionContext { implicit ec complete(500, s"Not implemented: ${p.productPrefix}") }
requestValToDirective(dyn.value1)(runToRoute)
case dyn: DynamicDirectiveRoute2[t1Type, t2Type]
def runToRoute(t1: t1Type, t2: t2Type): ScalaRoute =
apply(dyn.createDirective(t1, t2).route(dyn.innerRoute, dyn.moreInnerRoutes: _*))
(requestValToDirective(dyn.value1) & requestValToDirective(dyn.value2))(runToRoute)
case o: OpaqueRoute (ctx o.handle(new RequestContextImpl(ctx)).asInstanceOf[RouteResultImpl].underlying)
case p: Product extractExecutionContext { implicit ec complete(500, s"Not implemented: ${p.productPrefix}") }
}
}
def pathMatcherDirective[T](matchers: immutable.Seq[PathMatcher[_]],
directive: PathMatcher1[T] Directive1[T] // this type is too specific and only a placeholder for a proper polymorphic function
): Directive0 = {
// Concatenating PathMatchers is a bit complicated as we don't want to build up a tuple
// but something which we can later split all the separate values and add them to the
// ExtractionMap.
//
// This is achieved by providing a specialized `Join` instance to use with PathMatcher
// which provides the desired behavior.
type ValMap = Tuple1[Map[RequestVal[_], Any]]
object AddToMapJoin extends Join[ValMap, ValMap] {
type Out = ValMap
def apply(prefix: ValMap, suffix: ValMap): AddToMapJoin.Out =
Tuple1(prefix._1 ++ suffix._1)
}
def toScala(matcher: PathMatcher[_]): ScalaPathMatcher[ValMap] =
matcher.asInstanceOf[PathMatcherImpl[_]].matcher.transform(_.map(v Tuple1(Map(matcher -> v._1))))
def addExtractions(valMap: T): Directive0 = transformExtractionMap(_.addAll(valMap.asInstanceOf[Map[RequestVal[_], Any]]))
val reduced: ScalaPathMatcher[ValMap] = matchers.map(toScala).reduce(_.~(_)(AddToMapJoin))
directive(reduced.asInstanceOf[PathMatcher1[T]]).flatMap(addExtractions)
}
def addExtraction[T](key: RequestVal[T], value: T): Directive0 = {
@tailrec def addToExtractionMap(headers: immutable.Seq[HttpHeader], prefix: Vector[HttpHeader] = Vector.empty): immutable.Seq[HttpHeader] =
def fromAlternatives(alternatives: Seq[Route]): ScalaRoute =
alternatives.map(RouteImplementation.apply).reduce(_ ~ _)
def addExtraction[T](key: RequestVal[T], value: T): Directive0 =
transformExtractionMap(_.set(key, value))
def transformExtractionMap(f: ExtractionMap ExtractionMap): Directive0 = {
@tailrec def updateExtractionMap(headers: immutable.Seq[HttpHeader], prefix: Vector[HttpHeader] = Vector.empty): immutable.Seq[HttpHeader] =
headers match {
case (m: ExtractionMap) +: rest m.set(key, value) +: (prefix ++ rest)
case other +: rest addToExtractionMap(rest, prefix :+ other)
case Nil ExtractionMap(Map(key -> value)) +: prefix
case (m: ExtractionMap) +: rest f(m) +: (prefix ++ rest)
case other +: rest updateExtractionMap(rest, prefix :+ other)
case Nil f(ExtractionMap.Empty) +: prefix
}
mapRequest(_.mapHeaders(addToExtractionMap(_)))
mapRequest(_.mapHeaders(updateExtractionMap(_)))
}
private def scalaResolver(resolver: directives.ContentTypeResolver): ContentTypeResolver =
ContentTypeResolver(f resolver.resolve(f).asScala)
def requestValToDirective[T](value: RequestVal[T]): Directive1[T] =
value match {
case s: StandaloneExtractionImpl[_] s.directive
case v: RequestVal[_] extract(ctx v.get(new RequestContextImpl(ctx)))
}
}

View file

@ -5,12 +5,14 @@
package akka.http.impl.server
import java.io.File
import akka.http.javadsl.model.ws.Message
import akka.http.javadsl.server.values.{ PathMatcher, HttpBasicAuthenticator }
import akka.stream.javadsl.Flow
import scala.language.existentials
import scala.collection.immutable
import akka.http.javadsl.model.{ DateTime, ContentType, HttpMethod }
import akka.http.javadsl.model.headers.EntityTag
import akka.http.javadsl.model._
import akka.http.javadsl.model.headers.{ HttpCookie, EntityTag }
import akka.http.javadsl.server.directives.ContentTypeResolver
import akka.http.javadsl.server._
@ -19,13 +21,15 @@ import akka.http.javadsl.server._
*/
private[http] object RouteStructure {
trait DirectiveRoute extends Route {
def children: immutable.Seq[Route]
def innerRoute: Route
def moreInnerRoutes: immutable.Seq[Route]
def children: immutable.Seq[Route] = innerRoute +: moreInnerRoutes
require(children.nonEmpty)
}
case class RouteAlternatives(children: immutable.Seq[Route]) extends DirectiveRoute
case class MethodFilter(method: HttpMethod, children: immutable.Seq[Route]) extends DirectiveRoute {
case class RouteAlternatives()(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute
case class MethodFilter(method: HttpMethod)(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute {
def filter(ctx: RequestContext): Boolean = ctx.request.method == method
}
@ -34,15 +38,54 @@ private[http] object RouteStructure {
case class GetFromResourceDirectory(resourceDirectory: String, classLoader: ClassLoader, resolver: ContentTypeResolver) extends Route
case class GetFromFile(file: File, contentType: ContentType) extends Route
case class GetFromDirectory(directory: File, browseable: Boolean, resolver: ContentTypeResolver) extends Route
case class Redirect(uri: Uri, redirectionType: StatusCode) extends Route {
require(redirectionType.isRedirection, s"`redirectionType` must be a redirection status code but was $redirectionType")
}
case class RawPathPrefix(pathElements: immutable.Seq[PathMatcher[_]], children: immutable.Seq[Route]) extends DirectiveRoute
case class Extract(extractions: Seq[StandaloneExtractionImpl[_]], children: immutable.Seq[Route]) extends DirectiveRoute
case class BasicAuthentication(authenticator: HttpBasicAuthenticator[_], children: immutable.Seq[Route]) extends DirectiveRoute
case class EncodeResponse(coders: immutable.Seq[Coder], children: immutable.Seq[Route]) extends DirectiveRoute
case class PathSuffix(pathElements: immutable.Seq[PathMatcher[_]])(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute
case class PathSuffixTest(pathElements: immutable.Seq[PathMatcher[_]])(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute
case class RawPathPrefix(pathElements: immutable.Seq[PathMatcher[_]])(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute
case class RawPathPrefixTest(pathElements: immutable.Seq[PathMatcher[_]])(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute
case class RedirectToTrailingSlashIfMissing(redirectionType: StatusCode)(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute {
require(redirectionType.isRedirection, s"`redirectionType` must be a redirection status code but was $redirectionType")
}
case class RedirectToNoTrailingSlashIfPresent(redirectionType: StatusCode)(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute {
require(redirectionType.isRedirection, s"`redirectionType` must be a redirection status code but was $redirectionType")
}
case class Extract(extractions: Seq[StandaloneExtractionImpl[_]])(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute
case class BasicAuthentication(authenticator: HttpBasicAuthenticator[_])(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute
case class EncodeResponse(coders: immutable.Seq[Coder])(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute
case class DecodeRequest(coders: immutable.Seq[Coder])(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute
case class Conditional(entityTag: EntityTag, lastModified: DateTime, children: immutable.Seq[Route]) extends DirectiveRoute
case class Conditional(entityTag: Option[EntityTag] = None, lastModified: Option[DateTime] = None)(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute {
require(entityTag.isDefined || lastModified.isDefined)
}
case class HandleExceptions(handler: ExceptionHandler, children: immutable.Seq[Route]) extends DirectiveRoute
abstract class DynamicDirectiveRoute1[T1](val value1: RequestVal[T1])(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends Route {
def createDirective(t1: T1): Directive
}
abstract class DynamicDirectiveRoute2[T1, T2](val value1: RequestVal[T1], val value2: RequestVal[T2])(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends Route {
def createDirective(t1: T1, t2: T2): Directive
}
case class Validated(isValid: Boolean, errorMsg: String)(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute
case class HandleExceptions(handler: ExceptionHandler)(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute
sealed abstract class HostFilter extends DirectiveRoute {
def filter(hostName: String): Boolean
}
case class HostNameFilter(hostWhiteList: immutable.Seq[String])(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends HostFilter {
def filter(hostName: String): Boolean = hostWhiteList.contains(hostName)
}
abstract class GenericHostFilter(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends HostFilter
case class SchemeFilter(scheme: String)(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute
case class RangeSupport()(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute
case class HandleWebsocketMessages(handler: Flow[Message, Message, Any]) extends Route
case class SetCookie(cookie: HttpCookie)(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute
case class DeleteCookie(name: String, domain: Option[String], path: Option[String])(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute
abstract class OpaqueRoute(extractions: RequestVal[_]*) extends Route {
def handle(ctx: RequestContext): RouteResult

View file

@ -15,6 +15,12 @@ import akka.http.scaladsl.server._
private[http] abstract class StandaloneExtractionImpl[T: ClassTag] extends ExtractionImpl[T] with RequestVal[T] {
def directive: Directive1[T]
}
private[http] object StandaloneExtractionImpl {
def apply[T: ClassTag](extractionDirective: Directive1[T]): RequestVal[T] =
new StandaloneExtractionImpl[T] {
def directive: Directive1[T] = extractionDirective
}
}
/**
* INTERNAL API

View file

@ -9,8 +9,7 @@ import scala.collection.immutable
import scala.annotation.varargs
import akka.http.javadsl.model.HttpMethods
// FIXME: add support for the remaining directives, see #16436
abstract class AllDirectives extends PathDirectives
abstract class AllDirectives extends WebsocketDirectives
/**
*
@ -19,8 +18,8 @@ object Directives extends AllDirectives {
/**
* INTERNAL API
*/
private[http] def custom(f: immutable.Seq[Route] Route): Directive =
private[http] def custom(f: (Route, immutable.Seq[Route]) Route): Directive =
new AbstractDirective {
def createRoute(first: Route, others: Array[Route]): Route = f(first +: others.toVector)
def createRoute(first: Route, others: Array[Route]): Route = f(first, others.toList)
}
}

View file

@ -7,7 +7,7 @@ package akka.http.javadsl.server
import akka.http.javadsl.model._
import akka.util.ByteString
import scala.concurrent.Future
import scala.concurrent.{ ExecutionContext, Future }
/**
* The RequestContext represents the state of the request while it is routed through
@ -61,5 +61,8 @@ trait RequestContext {
*/
def notFound(): RouteResult
/** Returns the ExecutionContext of this RequestContext */
def executionContext(): ExecutionContext
// FIXME: provide proper support for rejections, see #16438
}

View file

@ -4,13 +4,14 @@
package akka.http.javadsl.server
import java.util.regex.Pattern
import java.{ util ju }
import scala.concurrent.Future
import scala.reflect.ClassTag
import akka.http.javadsl.model.HttpMethod
import akka.http.javadsl.model.{ RemoteAddress, HttpMethod }
import akka.http.scaladsl.server
import akka.http.scaladsl.server._
import akka.http.scaladsl.server.directives.{ RouteDirectives, BasicDirectives }
import akka.http.scaladsl.server.directives._
import akka.http.impl.server.{ UnmarshallerImpl, ExtractingStandaloneExtractionImpl, RequestContextImpl, StandaloneExtractionImpl }
import akka.http.scaladsl.util.FastFuture
import akka.http.impl.util.JavaMapping.Implicits._
@ -38,6 +39,58 @@ object RequestVals {
def extract(ctx: server.RequestContext): Future[HttpMethod] = FastFuture.successful(ctx.request.method.asJava)
}
/**
* Extracts the scheme used for this request.
*/
def requestContext: RequestVal[RequestContext] =
new StandaloneExtractionImpl[RequestContext] {
def directive: Directive1[RequestContext] = BasicDirectives.extractRequestContext.map(RequestContextImpl(_): RequestContext)
}
/**
* Extracts the unmatched path of the request context.
*/
def unmatchedPath: RequestVal[String] =
new ExtractingStandaloneExtractionImpl[String] {
def extract(ctx: server.RequestContext): Future[String] = FastFuture.successful(ctx.unmatchedPath.toString)
}
/**
* Extracts the scheme used for this request.
*/
def scheme: RequestVal[String] =
new StandaloneExtractionImpl[String] {
def directive: Directive1[String] = SchemeDirectives.extractScheme
}
/**
* Extracts the host name this request targeted.
*/
def host: RequestVal[String] =
new StandaloneExtractionImpl[String] {
def directive: Directive1[String] = HostDirectives.extractHost
}
/**
* Extracts the host name this request targeted.
*/
def matchAndExtractHost(regex: Pattern): RequestVal[String] =
new StandaloneExtractionImpl[String] {
def directive: Directive1[String] = HostDirectives.host(regex.pattern().r)
}
/**
* Directive extracting the IP of the client from either the X-Forwarded-For, Remote-Address or X-Real-IP header
* (in that order of priority).
*
* TODO: add link to the configuration entry that would add a remote-address header
*/
def clientIP(): RequestVal[RemoteAddress] =
new StandaloneExtractionImpl[RemoteAddress] {
def directive: Directive1[RemoteAddress] =
MiscDirectives.extractClientIP.map(x x: RemoteAddress) // missing covariance of Directive
}
/**
* Creates a new [[RequestVal]] given a [[ju.Map]] and a [[RequestVal]] that represents the key.
* The new RequestVal represents the existing value as looked up in the map. If the key doesn't

View file

@ -0,0 +1,16 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.javadsl.server
import akka.http.impl.server.UnmarshallerImpl
import akka.http.scaladsl.unmarshalling.{ FromMessageUnmarshaller, PredefinedFromEntityUnmarshallers }
object Unmarshallers {
def String: Unmarshaller[String] =
new UnmarshallerImpl[String]({ (ec, mat)
implicit val _ = mat
implicitly[FromMessageUnmarshaller[String]]
})
}

View file

@ -5,20 +5,22 @@
package akka.http.javadsl.server.directives
import scala.annotation.varargs
import java.lang.reflect.Method
import java.lang.reflect.{ ParameterizedType, Method }
import akka.http.javadsl.model.{ StatusCode, HttpResponse }
import akka.http.javadsl.server._
import akka.http.impl.server.RouteStructure._
import akka.http.impl.server._
import scala.concurrent.Future
abstract class BasicDirectives {
/**
* Tries the given routes in sequence until the first one matches.
*/
@varargs
def route(route: Route, others: Route*): Route =
RouteAlternatives(route +: others.toVector)
def route(innerRoute: Route, moreInnerRoutes: Route*): Route =
RouteAlternatives()(innerRoute, moreInnerRoutes.toList)
/**
* A route that completes the request with a static text
@ -64,7 +66,7 @@ abstract class BasicDirectives {
*/
@varargs
def extractHere(extractions: RequestVal[_]*): Directive =
Directives.custom(Extract(extractions.map(_.asInstanceOf[StandaloneExtractionImpl[_ <: AnyRef]]), _))
Directives.custom(Extract(extractions.map(_.asInstanceOf[StandaloneExtractionImpl[_ <: AnyRef]])))
/**
* A route that handles the request with the given opaque handler. Specify a set of extractions
@ -168,22 +170,35 @@ abstract class BasicDirectives {
res
}
def returnTypeMatches(method: Method): Boolean =
method.getReturnType == classOf[RouteResult]
method.getReturnType == classOf[RouteResult] || returnsFuture(method)
def returnsFuture(method: Method): Boolean =
method.getReturnType == classOf[Future[_]] &&
method.getGenericReturnType.isInstanceOf[ParameterizedType] &&
method.getGenericReturnType.asInstanceOf[ParameterizedType].getActualTypeArguments()(0) == classOf[RouteResult]
/** Makes sure both RouteResult and Future[RouteResult] are acceptable result types. */
def adaptResult(method: Method): (RequestContext, AnyRef) RouteResult =
if (returnsFuture(method)) (ctx, v) ctx.completeWith(v.asInstanceOf[Future[RouteResult]])
else (_, v) v.asInstanceOf[RouteResult]
val IdentityAdaptor: (RequestContext, Seq[Any]) Seq[Any] = (_, ps) ps
def methodInvocator(method: Method, adaptParams: (RequestContext, Seq[Any]) Seq[Any]): (RequestContext, Seq[Any]) RouteResult = {
val resultAdaptor = adaptResult(method)
if (!method.isAccessible) method.setAccessible(true)
if (adaptParams == IdentityAdaptor)
(ctx, params) resultAdaptor(ctx, method.invoke(instance, params.toArray.asInstanceOf[Array[AnyRef]]: _*))
else
(ctx, params) resultAdaptor(ctx, method.invoke(instance, adaptParams(ctx, params).toArray.asInstanceOf[Array[AnyRef]]: _*))
}
object ParameterTypes {
def unapply(method: Method): Option[List[Class[_]]] = Some(method.getParameterTypes.toList)
}
methods.filter(returnTypeMatches).collectFirst {
case method @ ParameterTypes(RequestContextClass :: rest) if paramsMatch(rest) {
if (!method.isAccessible) method.setAccessible(true) // FIXME: test what happens if this fails
(ctx: RequestContext, params: Seq[Any]) method.invoke(instance, (ctx +: params).toArray.asInstanceOf[Array[AnyRef]]: _*).asInstanceOf[RouteResult]
}
case method @ ParameterTypes(rest) if paramsMatch(rest) {
if (!method.isAccessible) method.setAccessible(true)
(ctx: RequestContext, params: Seq[Any]) method.invoke(instance, params.toArray.asInstanceOf[Array[AnyRef]]: _*).asInstanceOf[RouteResult]
}
case method @ ParameterTypes(RequestContextClass :: rest) if paramsMatch(rest) methodInvocator(method, _ +: _)
case method @ ParameterTypes(rest) if paramsMatch(rest) methodInvocator(method, IdentityAdaptor)
}.getOrElse(throw new RuntimeException("No suitable method found"))
}
def lookupMethod() = {

View file

@ -14,12 +14,46 @@ import scala.annotation.varargs
abstract class CacheConditionDirectives extends BasicDirectives {
/**
* Wraps its inner route with support for Conditional Requests as defined
* by tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-26
* by http://tools.ietf.org/html/rfc7232
*
* In particular the algorithm defined by tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-26#section-6
* In particular the algorithm defined by http://tools.ietf.org/html/rfc7232#section-6
* is implemented by this directive.
*
* Note: if you want to combine this directive with `withRangeSupport(...)` you need to put
* it on the *outside* of the `withRangeSupport(...)` directive, i.e. `withRangeSupport(...)`
* must be on a deeper level in your route structure in order to function correctly.
*/
@varargs
def conditional(entityTag: EntityTag, lastModified: DateTime, innerRoutes: Route*): Route =
RouteStructure.Conditional(entityTag, lastModified, innerRoutes.toVector)
def conditional(entityTag: EntityTag, innerRoute: Route, moreInnerRoutes: Route*): Route =
RouteStructure.Conditional(entityTag = Some(entityTag))(innerRoute, moreInnerRoutes.toList)
/**
* Wraps its inner route with support for Conditional Requests as defined
* by http://tools.ietf.org/html/rfc7232
*
* In particular the algorithm defined by http://tools.ietf.org/html/rfc7232#section-6
* is implemented by this directive.
*
* Note: if you want to combine this directive with `withRangeSupport(...)` you need to put
* it on the *outside* of the `withRangeSupport(...)` directive, i.e. `withRangeSupport(...)`
* must be on a deeper level in your route structure in order to function correctly.
*/
@varargs
def conditional(lastModified: DateTime, innerRoute: Route, moreInnerRoutes: Route*): Route =
RouteStructure.Conditional(lastModified = Some(lastModified))(innerRoute, moreInnerRoutes.toList)
/**
* Wraps its inner route with support for Conditional Requests as defined
* by http://tools.ietf.org/html/rfc7232
*
* In particular the algorithm defined by http://tools.ietf.org/html/rfc7232#section-6
* is implemented by this directive.
*
* Note: if you want to combine this directive with `withRangeSupport(...)` you need to put
* it on the *outside* of the `withRangeSupport(...)` directive, i.e. `withRangeSupport(...)`
* must be on a deeper level in your route structure in order to function correctly.
*/
@varargs
def conditional(entityTag: EntityTag, lastModified: DateTime, innerRoute: Route, moreInnerRoutes: Route*): Route =
RouteStructure.Conditional(Some(entityTag), Some(lastModified))(innerRoute, moreInnerRoutes.toList)
}

View file

@ -2,11 +2,18 @@
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.javadsl.server.directives
package akka.http.javadsl.server
package directives
import akka.http.javadsl.server.Directive
import akka.http.javadsl.server.Directives
import akka.http.javadsl.server.Route
import akka.http.scaladsl.coding.Decoder
import akka.http.scaladsl.server._
import scala.annotation.varargs
import akka.http.scaladsl
import akka.http.impl.server.RouteStructure
import akka.http.javadsl.server.{ Coder, Directive, Directives, Route }
abstract class CodingDirectives extends CacheConditionDirectives {
/**
@ -14,9 +21,8 @@ abstract class CodingDirectives extends CacheConditionDirectives {
* using one of the predefined coders, `Gzip`, `Deflate`, or `NoCoding` depending on
* a potential [[akka.http.javadsl.model.headers.AcceptEncoding]] header from the client.
*/
@varargs def encodeResponse(innerRoutes: Route*): Route =
// FIXME: make sure this list stays synchronized with the Scala one
RouteStructure.EncodeResponse(List(Coder.NoCoding, Coder.Gzip, Coder.Deflate), innerRoutes.toVector)
@varargs def encodeResponse(innerRoute: Route, moreInnerRoutes: Route*): Route =
RouteStructure.EncodeResponse(CodingDirectives._DefaultCodersToEncodeResponse)(innerRoute, moreInnerRoutes.toList)
/**
* A directive that Wraps its inner routes with encoding support.
@ -27,5 +33,42 @@ abstract class CodingDirectives extends CacheConditionDirectives {
* will be respected (or otherwise, if no matching .
*/
@varargs def encodeResponse(coders: Coder*): Directive =
Directives.custom(RouteStructure.EncodeResponse(coders.toList, _))
Directives.custom(RouteStructure.EncodeResponse(coders.toList))
/**
* Decodes the incoming request using the given Decoder.
* If the request encoding doesn't match the request is rejected with an `UnsupportedRequestEncodingRejection`.
*/
@varargs def decodeRequestWith(decoder: Coder, innerRoute: Route, moreInnerRoutes: Route*): Route =
RouteStructure.DecodeRequest(decoder :: Nil)(innerRoute, moreInnerRoutes.toList)
/**
* Decodes the incoming request if it is encoded with one of the given
* encoders. If the request encoding doesn't match one of the given encoders
* the request is rejected with an `UnsupportedRequestEncodingRejection`.
* If no decoders are given the default encoders (``Gzip``, ``Deflate``, ``NoCoding``) are used.
*/
@varargs def decodeRequestWith(decoders: Coder*): Directive =
Directives.custom(RouteStructure.DecodeRequest(decoders.toList))
/**
* Decompresses the incoming request if it is ``gzip`` or ``deflate`` compressed.
* Uncompressed requests are passed through untouched.
* If the request encoded with another encoding the request is rejected with an `UnsupportedRequestEncodingRejection`.
*/
@varargs def decodeRequest(innerRoute: Route, moreInnerRoutes: Route*): Route =
RouteStructure.DecodeRequest(CodingDirectives._DefaultCodersToDecodeRequest)(innerRoute, moreInnerRoutes.toList)
}
/**
* Internal API
*/
private[http] object CodingDirectives {
private[http] val _DefaultCodersToEncodeResponse =
scaladsl.server.directives.CodingDirectives.DefaultEncodeResponseEncoders
.map(c Coder.values().find(_._underlyingScalaCoder() == c).get)
private[http] val _DefaultCodersToDecodeRequest =
scaladsl.server.directives.CodingDirectives.DefaultCoders
.map(c Coder.values().find(_._underlyingScalaCoder() == c).get)
}

View file

@ -0,0 +1,19 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.javadsl.server.directives
import akka.http.impl.server.RouteStructure
import akka.http.javadsl.model.headers.HttpCookie
import akka.http.javadsl.server.Route
import scala.annotation.varargs
abstract class CookieDirectives extends CodingDirectives {
/**
* Adds a Set-Cookie header with the given cookies to all responses of its inner route.
*/
@varargs def setCookie(cookie: HttpCookie, innerRoute: Route, moreInnerRoutes: Route*): Route =
RouteStructure.SetCookie(cookie)(innerRoute, moreInnerRoutes.toList)
}

View file

@ -9,11 +9,11 @@ import akka.http.impl.server.RouteStructure
import scala.annotation.varargs
abstract class ExecutionDirectives extends CodingDirectives {
abstract class ExecutionDirectives extends CookieDirectives {
/**
* Handles exceptions in the inner routes using the specified handler.
*/
@varargs
def handleExceptions(handler: ExceptionHandler, innerRoutes: Route*): Route =
RouteStructure.HandleExceptions(handler, innerRoutes.toVector)
def handleExceptions(handler: ExceptionHandler, innerRoute: Route, moreInnerRoutes: Route*): Route =
RouteStructure.HandleExceptions(handler)(innerRoute, moreInnerRoutes.toList)
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.javadsl.server
package directives
import java.lang.{ Iterable JIterable, Boolean JBoolean }
import scala.annotation.varargs
import scala.collection.JavaConverters._
import akka.http.impl.server.RouteStructure.{ GenericHostFilter, HostNameFilter }
abstract class HostDirectives extends FileAndResourceDirectives {
/**
* Rejects all requests with a host name different from the given one.
*/
@varargs
def host(hostName: String, innerRoute: Route, moreInnerRoutes: Route*): Route =
HostNameFilter(hostName :: Nil)(innerRoute, moreInnerRoutes.toList)
/**
* Rejects all requests with a host name different from the given ones.
*/
@varargs
def host(hostNames: JIterable[String], innerRoute: Route, moreInnerRoutes: Route*): Route =
HostNameFilter(hostNames.asScala.toList)(innerRoute, moreInnerRoutes.toList)
/**
* Rejects all requests for whose host name the given predicate function returns false.
*/
@varargs
def host(predicate: akka.japi.function.Function[String, JBoolean], innerRoute: Route, moreInnerRoutes: Route*): Route =
new GenericHostFilter(innerRoute, moreInnerRoutes.toList) {
def filter(hostName: String): Boolean = predicate.apply(hostName)
}
}

View file

@ -10,36 +10,36 @@ import akka.http.impl.server.RouteStructure
import scala.annotation.varargs
abstract class MethodDirectives extends FileAndResourceDirectives {
abstract class MethodDirectives extends HostDirectives {
/** Handles the inner routes if the incoming request is a GET request, rejects the request otherwise */
@varargs
def get(innerRoutes: Route*): Route = method(HttpMethods.GET, innerRoutes: _*)
def get(innerRoute: Route, moreInnerRoutes: Route*): Route = method(HttpMethods.GET, innerRoute, moreInnerRoutes: _*)
/** Handles the inner routes if the incoming request is a POST request, rejects the request otherwise */
@varargs
def post(innerRoutes: Route*): Route = method(HttpMethods.POST, innerRoutes: _*)
def post(innerRoute: Route, moreInnerRoutes: Route*): Route = method(HttpMethods.POST, innerRoute, moreInnerRoutes: _*)
/** Handles the inner routes if the incoming request is a PUT request, rejects the request otherwise */
@varargs
def put(innerRoutes: Route*): Route = method(HttpMethods.PUT, innerRoutes: _*)
def put(innerRoute: Route, moreInnerRoutes: Route*): Route = method(HttpMethods.PUT, innerRoute, moreInnerRoutes: _*)
/** Handles the inner routes if the incoming request is a DELETE request, rejects the request otherwise */
@varargs
def delete(innerRoutes: Route*): Route = method(HttpMethods.DELETE, innerRoutes: _*)
def delete(innerRoute: Route, moreInnerRoutes: Route*): Route = method(HttpMethods.DELETE, innerRoute, moreInnerRoutes: _*)
/** Handles the inner routes if the incoming request is a HEAD request, rejects the request otherwise */
@varargs
def head(innerRoutes: Route*): Route = method(HttpMethods.HEAD, innerRoutes: _*)
def head(innerRoute: Route, moreInnerRoutes: Route*): Route = method(HttpMethods.HEAD, innerRoute, moreInnerRoutes: _*)
/** Handles the inner routes if the incoming request is a OPTIONS request, rejects the request otherwise */
@varargs
def options(innerRoutes: Route*): Route = method(HttpMethods.OPTIONS, innerRoutes: _*)
def options(innerRoute: Route, moreInnerRoutes: Route*): Route = method(HttpMethods.OPTIONS, innerRoute, moreInnerRoutes: _*)
/** Handles the inner routes if the incoming request is a PATCH request, rejects the request otherwise */
@varargs
def patch(innerRoutes: Route*): Route = method(HttpMethods.PATCH, innerRoutes: _*)
def patch(innerRoute: Route, moreInnerRoutes: Route*): Route = method(HttpMethods.PATCH, innerRoute, moreInnerRoutes: _*)
/** Handles the inner routes if the incoming request is a request with the given method, rejects the request otherwise */
@varargs
def method(method: HttpMethod, innerRoutes: Route*): Route = RouteStructure.MethodFilter(method, innerRoutes.toVector)
def method(method: HttpMethod, innerRoute: Route, moreInnerRoutes: Route*): Route = RouteStructure.MethodFilter(method)(innerRoute, moreInnerRoutes.toList)
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.javadsl.server
package directives
import java.lang.{ Boolean JBoolean }
import akka.http.impl.server.RouteStructure.{ DynamicDirectiveRoute2, Validated, DynamicDirectiveRoute1 }
import scala.annotation.varargs
import akka.japi.function.{ Function2, Function }
abstract class MiscDirectives extends MethodDirectives {
/**
* Returns a Route which checks the given condition on the request context before running its inner Route.
* If the condition fails the route is rejected with a [[spray.routing.ValidationRejection]].
*/
@varargs
def validate(check: Function[RequestContext, JBoolean], errorMsg: String, innerRoute: Route, moreInnerRoutes: Route*): Route =
validate(RequestVals.requestContext, check, errorMsg, innerRoute, moreInnerRoutes: _*)
/**
* Returns a Route which checks the given condition before running its inner Route. If the condition fails the
* route is rejected with a [[spray.routing.ValidationRejection]].
*/
@varargs
def validate[T](value: RequestVal[T], check: Function[T, JBoolean], errorMsg: String, innerRoute: Route, moreInnerRoutes: Route*): Route =
new DynamicDirectiveRoute1[T](value)(innerRoute, moreInnerRoutes.toList) {
def createDirective(t1: T): Directive = Directives.custom(Validated(check.apply(t1), errorMsg))
}
/**
* Returns a Route which checks the given condition before running its inner Route. If the condition fails the
* route is rejected with a [[spray.routing.ValidationRejection]].
*/
@varargs
def validate[T1, T2](value1: RequestVal[T1],
value2: RequestVal[T2],
check: Function2[T1, T2, JBoolean],
errorMsg: String,
innerRoute: Route, moreInnerRoutes: Route*): Route =
new DynamicDirectiveRoute2[T1, T2](value1, value2)(innerRoute, moreInnerRoutes.toList) {
def createDirective(t1: T1, t2: T2): Directive = Directives.custom(Validated(check.apply(t1, t2), errorMsg))
}
}

View file

@ -6,12 +6,14 @@ package akka.http.javadsl.server
package directives
import akka.http.impl.server.RouteStructure
import akka.http.impl.server.RouteStructure.{ RedirectToNoTrailingSlashIfPresent, RedirectToTrailingSlashIfMissing }
import akka.http.javadsl.model.StatusCode
import akka.http.javadsl.server.values.{ PathMatchers, PathMatcher }
import scala.annotation.varargs
import scala.collection.immutable
abstract class PathDirectives extends MethodDirectives {
abstract class PathDirectives extends MiscDirectives {
/**
* Tries to consumes the complete unmatched path given a number of PathMatchers. Between each
* of the matchers a `/` will be matched automatically.
@ -20,20 +22,48 @@ abstract class PathDirectives extends MethodDirectives {
*/
@varargs
def path(matchers: AnyRef*): Directive =
forMatchers(joinWithSlash(convertMatchers(matchers)) :+ PathMatchers.END)
RawPathPrefixForMatchers(joinWithSlash(convertMatchers(matchers)) :+ PathMatchers.END)
@varargs
def pathPrefix(matchers: AnyRef*): Directive =
forMatchers(joinWithSlash(convertMatchers(matchers)))
RawPathPrefixForMatchers(joinWithSlash(convertMatchers(matchers)))
def pathSingleSlash: Directive = forMatchers(List(PathMatchers.SLASH, PathMatchers.END))
@varargs
def pathPrefixTest(matchers: AnyRef*): Directive =
RawPathPrefixTestForMatchers(joinWithSlash(convertMatchers(matchers)))
@varargs
def rawPathPrefix(matchers: AnyRef*): Directive =
forMatchers(convertMatchers(matchers))
RawPathPrefixForMatchers(convertMatchers(matchers))
private def forMatchers(matchers: immutable.Seq[PathMatcher[_]]): Directive =
Directives.custom(RouteStructure.RawPathPrefix(matchers, _))
@varargs
def rawPathPrefixTest(matchers: AnyRef*): Directive =
RawPathPrefixTestForMatchers(convertMatchers(matchers))
@varargs
def pathSuffix(matchers: AnyRef*): Directive =
Directives.custom(RouteStructure.PathSuffix(convertMatchers(matchers)))
@varargs
def pathSuffixTest(matchers: AnyRef*): Directive =
Directives.custom(RouteStructure.PathSuffixTest(convertMatchers(matchers)))
def pathEnd: Directive = RawPathPrefixForMatchers(PathMatchers.END :: Nil)
def pathSingleSlash: Directive = RawPathPrefixForMatchers(List(PathMatchers.SLASH, PathMatchers.END))
def pathEndOrSingleSlash: Directive = RawPathPrefixForMatchers(List(PathMatchers.SLASH.optional, PathMatchers.END))
@varargs
def redirectToTrailingSlashIfMissing(redirectionStatusCode: StatusCode, innerRoute: Route, moreInnerRoutes: Route*): Route =
RedirectToTrailingSlashIfMissing(redirectionStatusCode)(innerRoute, moreInnerRoutes.toList)
@varargs
def redirectToNoTrailingSlashIfPresent(redirectionStatusCode: StatusCode, innerRoute: Route, moreInnerRoutes: Route*): Route =
RedirectToNoTrailingSlashIfPresent(redirectionStatusCode)(innerRoute, moreInnerRoutes.toList)
private def RawPathPrefixForMatchers(matchers: immutable.Seq[PathMatcher[_]]): Directive =
Directives.custom(RouteStructure.RawPathPrefix(matchers))
private def RawPathPrefixTestForMatchers(matchers: immutable.Seq[PathMatcher[_]]): Directive =
Directives.custom(RouteStructure.RawPathPrefixTest(matchers))
private def joinWithSlash(matchers: immutable.Seq[PathMatcher[_]]): immutable.Seq[PathMatcher[_]] = {
def join(result: immutable.Seq[PathMatcher[_]], next: PathMatcher[_]): immutable.Seq[PathMatcher[_]] =
@ -46,6 +76,7 @@ abstract class PathDirectives extends MethodDirectives {
def parse(matcher: AnyRef): PathMatcher[_] = matcher match {
case p: PathMatcher[_] p
case name: String PathMatchers.segment(name)
case x throw new IllegalArgumentException(s"Matcher of class ${x.getClass} is unsupported for PathDirectives")
}
matchers.map(parse).toVector

View file

@ -0,0 +1,28 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.javadsl.server
package directives
import akka.http.impl.server.RouteStructure.RangeSupport
import scala.annotation.varargs
abstract class RangeDirectives extends PathDirectives {
/**
* Answers GET requests with an `Accept-Ranges: bytes` header and converts HttpResponses coming back from its inner
* route into partial responses if the initial request contained a valid `Range` request header. The requested
* byte-ranges may be coalesced.
* This directive is transparent to non-GET requests
* Rejects requests with unsatisfiable ranges `UnsatisfiableRangeRejection`.
* Rejects requests with too many expected ranges.
*
* Note: if you want to combine this directive with `conditional(...)` you need to put
* it on the *inside* of the `conditional(...)` directive, i.e. `conditional(...)` must be
* on a higher level in your route structure in order to function correctly.
*
* @see https://tools.ietf.org/html/rfc7233
*/
@varargs def withRangeSupport(innerRoute: Route, moreInnerRoutes: Route*): Route = RangeSupport()(innerRoute, moreInnerRoutes.toList)
}

View file

@ -0,0 +1,18 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.javadsl.server
package directives
import akka.http.impl.server.RouteStructure.Redirect
import akka.http.javadsl.model.{ StatusCode, Uri }
abstract class RouteDirectives extends RangeDirectives {
/**
* Completes the request with redirection response of the given type to the given URI.
*
* The ``redirectionType`` must be a StatusCode for which ``isRedirection`` returns true.
*/
def redirect(uri: Uri, redirectionType: StatusCode): Route = Redirect(uri, redirectionType)
}

View file

@ -0,0 +1,18 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.javadsl.server.directives
import akka.http.impl.server.RouteStructure.SchemeFilter
import akka.http.javadsl.server.Route
import scala.annotation.varargs
abstract class SchemeDirectives extends RouteDirectives {
/**
* Rejects all requests whose Uri scheme does not match the given one.
*/
@varargs
def scheme(scheme: String, innerRoute: Route, moreInnerRoutes: Route*): Route = SchemeFilter(scheme)(innerRoute, moreInnerRoutes.toList)
}

View file

@ -0,0 +1,19 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.javadsl.server
package directives
import akka.http.impl.server.RouteStructure
import akka.http.javadsl.model.ws.Message
import akka.stream.javadsl.Flow
abstract class WebsocketDirectives extends SchemeDirectives {
/**
* Handles websocket requests with the given handler and rejects other requests with a
* [[ExpectedWebsocketRequestRejection]].
*/
def handleWebsocketMessages(handler: Flow[Message, Message, Any]): Route =
RouteStructure.HandleWebsocketMessages(handler)
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.javadsl.server.values
import akka.http.impl.server.{ RouteStructure, CookieImpl }
import akka.http.javadsl.server.{ Directive, RequestVal, Route }
import akka.japi.Option
import scala.annotation.varargs
import scala.collection.immutable
abstract class Cookie {
def name(): String
def domain(): Option[String]
def path(): Option[String]
def withDomain(domain: String): Cookie
def withPath(path: String): Cookie
def value(): RequestVal[String]
def optionalValue(): RequestVal[Option[String]]
def set(value: String): Directive
@varargs
def delete(innerRoute: Route, moreInnerRoutes: Route*): Route =
RouteStructure.DeleteCookie(name(), domain(), path())(innerRoute, moreInnerRoutes.toList)
}
object Cookies {
def create(name: String): Cookie = new CookieImpl(name)
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.javadsl.server
package values
import java.{ lang jl }
import akka.japi.{ Option JOption }
import akka.http.impl.server.FormFieldImpl
import akka.http.scaladsl.unmarshalling._
trait FormField[T] extends RequestVal[T] {
def optional: RequestVal[JOption[T]]
def withDefault(defaultValue: T): RequestVal[T]
}
object FormFields {
import akka.http.scaladsl.common.ToNameReceptacleEnhancements._
def stringValue(name: String): FormField[String] = FormFieldImpl(name)
def intValue(name: String): FormField[jl.Integer] = FormFieldImpl(name.as[Int])
def byteValue(name: String): FormField[jl.Byte] = FormFieldImpl(name.as[Byte])
def shortValue(name: String): FormField[jl.Short] = FormFieldImpl(name.as[Short])
def longValue(name: String): FormField[jl.Long] = FormFieldImpl(name.as[Long])
def floatValue(name: String): FormField[jl.Float] = FormFieldImpl(name.as[Float])
def doubleValue(name: String): FormField[jl.Double] = FormFieldImpl(name.as[Double])
def booleanValue(name: String): FormField[jl.Boolean] = FormFieldImpl(name.as[Boolean])
def hexByteValue(name: String): FormField[jl.Byte] = FormFieldImpl(name.as(Unmarshaller.HexByte))
def hexShortValue(name: String): FormField[jl.Short] = FormFieldImpl(name.as(Unmarshaller.HexShort))
def hexIntValue(name: String): FormField[jl.Integer] = FormFieldImpl(name.as(Unmarshaller.HexInt))
def hexLongValue(name: String): FormField[jl.Long] = FormFieldImpl(name.as(Unmarshaller.HexLong))
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.javadsl.server.values
import akka.http.impl.server.HeaderImpl
import akka.http.javadsl.model.HttpHeader
import akka.http.javadsl.server.RequestVal
import akka.http.scaladsl.model
import akka.http.scaladsl.server.Directive1
import akka.http.scaladsl.server.util.ClassMagnet
import scala.reflect.{ ClassTag, classTag }
trait Header[T <: HttpHeader] {
def instance(): RequestVal[T]
def optionalInstance(): RequestVal[Option[T]]
def value(): RequestVal[String]
def optionalValue(): RequestVal[Option[String]]
}
object Headers {
import akka.http.scaladsl.server.directives.BasicDirectives._
import akka.http.scaladsl.server.directives.HeaderDirectives._
def byName(name: String): Header[HttpHeader] =
HeaderImpl[HttpHeader](name, _ optionalHeaderInstanceByName(name.toLowerCase()), classTag[HttpHeader])
def byClass[T <: HttpHeader](clazz: Class[T]): Header[T] =
HeaderImpl[T](clazz.getSimpleName, ct optionalHeaderValueByType(ClassMagnet()(ct)), ClassTag(clazz))
private def optionalHeaderInstanceByName(lowercaseName: String): Directive1[Option[model.HttpHeader]] =
extract(_.request.headers.collectFirst {
case h @ model.HttpHeader(`lowercaseName`, _) h
})
}

View file

@ -54,5 +54,5 @@ abstract class HttpBasicAuthenticator[T](val realm: String) extends AbstractDire
* INTERNAL API
*/
protected[http] final def createRoute(first: Route, others: Array[Route]): Route =
RouteStructure.BasicAuthentication(this, (first +: others).toVector)
RouteStructure.BasicAuthentication(this)(first, others.toList)
}

View file

@ -4,20 +4,30 @@
package akka.http.javadsl.server.values
import java.util.AbstractMap.SimpleEntry
import java.{ lang jl }
import akka.http.impl.server.ParameterImpl
import java.util.{ Map JMap, Collection JCollection }
import akka.http.scaladsl.server.directives.ParameterDirectives
import akka.http.scaladsl.unmarshalling.Unmarshaller
import scala.reflect.ClassTag
import akka.japi.{ Option JOption }
import akka.http.impl.server.{ StandaloneExtractionImpl, ParameterImpl }
import akka.http.javadsl.server.RequestVal
import akka.http.scaladsl.server.Directive1
import akka.http.scaladsl.server.directives.ParameterDirectives.ParamMagnet
import scala.concurrent.ExecutionContext
import scala.reflect.ClassTag
/**
* A RequestVal representing a query parameter of type T.
*/
trait Parameter[T] extends RequestVal[T]
trait Parameter[T] extends RequestVal[T] {
def optional: RequestVal[JOption[T]]
def withDefault(defaultValue: T): RequestVal[T]
}
/**
* A collection of predefined parameters.
@ -29,17 +39,29 @@ object Parameters {
/**
* A string query parameter.
*/
def string(name: String): Parameter[String] =
fromScalaParam(implicit ec ParamMagnet(name))
def stringValue(name: String): Parameter[String] = ParameterImpl(name)
/**
* An integer query parameter.
*/
def integer(name: String): Parameter[jl.Integer] =
fromScalaParam[jl.Integer](implicit ec
ParamMagnet(name.as[Int]).asInstanceOf[ParamMagnet { type Out = Directive1[jl.Integer] }])
def intValue(name: String): Parameter[jl.Integer] = ParameterImpl(name.as[Int])
def byteValue(name: String): Parameter[jl.Byte] = ParameterImpl(name.as[Byte])
def shortValue(name: String): Parameter[jl.Short] = ParameterImpl(name.as[Short])
def longValue(name: String): Parameter[jl.Long] = ParameterImpl(name.as[Long])
def floatValue(name: String): Parameter[jl.Float] = ParameterImpl(name.as[Float])
def doubleValue(name: String): Parameter[jl.Double] = ParameterImpl(name.as[Double])
def booleanValue(name: String): Parameter[jl.Boolean] = ParameterImpl(name.as[Boolean])
private def fromScalaParam[T: ClassTag](underlying: ExecutionContext ParamMagnet { type Out = Directive1[T] }): Parameter[T] =
new ParameterImpl[T](underlying)
def hexByteValue(name: String): Parameter[jl.Byte] = ParameterImpl(name.as(Unmarshaller.HexByte))
def hexShortValue(name: String): Parameter[jl.Short] = ParameterImpl(name.as(Unmarshaller.HexShort))
def hexIntValue(name: String): Parameter[jl.Integer] = ParameterImpl(name.as(Unmarshaller.HexInt))
def hexLongValue(name: String): Parameter[jl.Long] = ParameterImpl(name.as(Unmarshaller.HexLong))
import scala.collection.JavaConverters._
def asMap: RequestVal[JMap[String, String]] = StandaloneExtractionImpl(ParameterDirectives.parameterMap.map(_.asJava))
def asMultiMap: RequestVal[JMap[String, JCollection[String]]] =
StandaloneExtractionImpl(ParameterDirectives.parameterMultiMap.map(_.mapValues(_.asJavaCollection).asJava))
def asCollection: RequestVal[JCollection[JMap.Entry[String, String]]] =
StandaloneExtractionImpl(ParameterDirectives.parameterSeq.map(_.map(e new SimpleEntry(e._1, e._2): JMap.Entry[String, String]).asJavaCollection))
}

View file

@ -9,6 +9,7 @@ import java.{ lang ⇒ jl, util ⇒ ju }
import akka.http.impl.server.PathMatcherImpl
import akka.http.javadsl.server.RequestVal
import akka.http.scaladsl.server.{ PathMatcher0, PathMatcher1, PathMatchers ScalaPathMatchers }
import akka.japi.Option
import scala.collection.JavaConverters._
import scala.reflect.ClassTag
@ -21,7 +22,9 @@ import scala.reflect.ClassTag
* Using a PathMatcher with the [[Directives.path]] or [[Directives.pathPrefix]] directives
* "consumes" a part of the path which is recorded in [[RequestContext.unmatchedPath]].
*/
trait PathMatcher[T] extends RequestVal[T]
trait PathMatcher[T] extends RequestVal[T] {
def optional: PathMatcher[Option[T]]
}
/**
* A collection of predefined path matchers.

View file

@ -42,6 +42,8 @@ sealed abstract class StrictForm {
object StrictForm {
sealed trait Field
object Field {
private[http] def fromString(value: String): Field = FromString(value)
private[StrictForm] final case class FromString(value: String) extends Field
private[StrictForm] final case class FromPart(value: Multipart.FormData.BodyPart.Strict) extends Field

View file

@ -38,7 +38,7 @@ trait CodingDirectives {
* identity, gzip or deflate then no encoding is used.
*/
def encodeResponse: Directive0 =
encodeResponseWith(NoCoding, Gzip, Deflate)
_encodeResponse(DefaultEncodeResponseEncoders)
/**
* Encodes the response with the encoding that is requested by the client via the `Accept-
@ -110,6 +110,8 @@ trait CodingDirectives {
object CodingDirectives extends CodingDirectives {
val DefaultCoders: immutable.Seq[Coder] = immutable.Seq(Gzip, Deflate, NoCoding)
private[http] val DefaultEncodeResponseEncoders = immutable.Seq(NoCoding, Gzip, Deflate)
def theseOrDefault[T >: Coder](these: Seq[T]): Seq[T] = if (these.isEmpty) DefaultCoders else these
import BasicDirectives._