Merge pull request #17767 from spray/w/various-small-improvements

Various small improvements
This commit is contained in:
drewhk 2015-06-19 14:28:48 +02:00
commit e70b8c3e48
6 changed files with 109 additions and 24 deletions

View file

@ -28,6 +28,14 @@ public abstract class HttpCookie {
false, false, false, false,
Util.<String>scalaNone()); Util.<String>scalaNone());
} }
public static HttpCookie create(String name, String value, Option<String> domain, Option<String> path) {
return new akka.http.scaladsl.model.headers.HttpCookie(
name, value,
Util.<akka.http.scaladsl.model.DateTime>scalaNone(), Util.scalaNone(),
domain.asScala(), path.asScala(),
false, false,
Util.<String>scalaNone());
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static HttpCookie create( public static HttpCookie create(
String name, String name,
@ -49,4 +57,39 @@ public abstract class HttpCookie {
httpOnly, httpOnly,
extension.asScala()); extension.asScala());
} }
/**
* Returns a copy of this HttpCookie instance with the given expiration set.
*/
public abstract HttpCookie withExpires(DateTime dateTime);
/**
* Returns a copy of this HttpCookie instance with the given max age set.
*/
public abstract HttpCookie withMaxAge(long maxAge);
/**
* Returns a copy of this HttpCookie instance with the given domain set.
*/
public abstract HttpCookie withDomain(String domain);
/**
* Returns a copy of this HttpCookie instance with the given path set.
*/
public abstract HttpCookie withPath(String path);
/**
* Returns a copy of this HttpCookie instance with the given secure flag set.
*/
public abstract HttpCookie withSecure(boolean secure);
/**
* Returns a copy of this HttpCookie instance with the given http-only flag set.
*/
public abstract HttpCookie withHttpOnly(boolean httpOnly);
/**
* Returns a copy of this HttpCookie instance with the given extension set.
*/
public abstract HttpCookie withExtension(String extension);
} }

View file

@ -7,4 +7,8 @@ package akka.http.javadsl.model.headers;
public abstract class RawHeader extends akka.http.scaladsl.model.HttpHeader { public abstract class RawHeader extends akka.http.scaladsl.model.HttpHeader {
public abstract String name(); public abstract String name();
public abstract String value(); public abstract String value();
public static RawHeader create(String name, String value) {
return new akka.http.scaladsl.model.headers.RawHeader(name, value);
}
} }

View file

@ -4,6 +4,7 @@
package akka.http.scaladsl.model.headers package akka.http.scaladsl.model.headers
import akka.http.javadsl.model.headers
import akka.parboiled2.CharPredicate import akka.parboiled2.CharPredicate
import akka.japi.{ Option JOption } import akka.japi.{ Option JOption }
import akka.http.scaladsl.model.DateTime import akka.http.scaladsl.model.DateTime
@ -77,6 +78,20 @@ final case class HttpCookie(
def getMaxAge: JOption[java.lang.Long] = maxAge.asJava def getMaxAge: JOption[java.lang.Long] = maxAge.asJava
/** Java API */ /** Java API */
def getExpires: JOption[jm.DateTime] = expires.asJava def getExpires: JOption[jm.DateTime] = expires.asJava
/** Java API */
def withExpires(dateTime: jm.DateTime): headers.HttpCookie = copy(expires = Some(dateTime.asScala))
/** Java API */
def withDomain(domain: String): headers.HttpCookie = copy(domain = Some(domain))
/** Java API */
def withPath(path: String): headers.HttpCookie = copy(path = Some(path))
/** Java API */
def withMaxAge(maxAge: Long): headers.HttpCookie = copy(maxAge = Some(maxAge))
/** Java API */
def withSecure(secure: Boolean): headers.HttpCookie = copy(secure = secure)
/** Java API */
def withHttpOnly(httpOnly: Boolean): headers.HttpCookie = copy(httpOnly = httpOnly)
/** Java API */
def withExtension(extension: String): headers.HttpCookie = copy(extension = Some(extension))
} }
object HttpCookie { object HttpCookie {

View file

@ -53,16 +53,16 @@ class PathDirectivesSpec extends RoutingSpec with Inside {
val test = testFor(pathPrefix("ab[cd]+".r) { echoCaptureAndUnmatchedPath }) val test = testFor(pathPrefix("ab[cd]+".r) { echoCaptureAndUnmatchedPath })
"reject [/bar]" in test() "reject [/bar]" in test()
"reject [/ab/cd]" in test() "reject [/ab/cd]" in test()
"reject [/abcdef]" in test("abcd:ef") "accept [/abcdef]" in test("abcd:ef")
"reject [/abcdd/ef]" in test("abcdd:/ef") "accept [/abcdd/ef]" in test("abcdd:/ef")
} }
"""pathPrefix("ab(cd)".r)""" should { """pathPrefix("ab(cd)".r)""" should {
val test = testFor(pathPrefix("ab(cd)+".r) { echoCaptureAndUnmatchedPath }) val test = testFor(pathPrefix("ab(cd)+".r) { echoCaptureAndUnmatchedPath })
"reject [/bar]" in test() "reject [/bar]" in test()
"reject [/ab/cd]" in test() "reject [/ab/cd]" in test()
"reject [/abcdef]" in test("cd:ef") "accept [/abcdef]" in test("cd:ef")
"reject [/abcde/fg]" in test("cd:e/fg") "accept [/abcde/fg]" in test("cd:e/fg")
} }
"pathPrefix(regex)" should { "pathPrefix(regex)" should {
@ -132,40 +132,40 @@ class PathDirectivesSpec extends RoutingSpec with Inside {
val test = testFor(pathPrefix(separateOnSlashes("a/b")) { echoUnmatchedPath }) val test = testFor(pathPrefix(separateOnSlashes("a/b")) { echoUnmatchedPath })
"accept [/a/b]" in test("") "accept [/a/b]" in test("")
"accept [/a/b/]" in test("/") "accept [/a/b/]" in test("/")
"accept [/a/c]" in test() "reject [/a/c]" in test()
} }
"""pathPrefix(separateOnSlashes("abc"))""" should { """pathPrefix(separateOnSlashes("abc"))""" should {
val test = testFor(pathPrefix(separateOnSlashes("abc")) { echoUnmatchedPath }) val test = testFor(pathPrefix(separateOnSlashes("abc")) { echoUnmatchedPath })
"accept [/abc]" in test("") "accept [/abc]" in test("")
"accept [/abcdef]" in test("def") "accept [/abcdef]" in test("def")
"accept [/ab]" in test() "reject [/ab]" in test()
} }
"""pathPrefixTest("a" / Segment ~ Slash)""" should { """pathPrefixTest("a" / Segment ~ Slash)""" should {
val test = testFor(pathPrefixTest("a" / Segment ~ Slash) { echoCaptureAndUnmatchedPath }) val test = testFor(pathPrefixTest("a" / Segment ~ Slash) { echoCaptureAndUnmatchedPath })
"accept [/a/bc/]" in test("bc:/a/bc/") "accept [/a/bc/]" in test("bc:/a/bc/")
"accept [/a/bc]" in test() "reject [/a/bc]" in test()
"accept [/a/]" in test() "reject [/a/]" in test()
} }
"""pathSuffix("edit" / Segment)""" should { """pathSuffix("edit" / Segment)""" should {
val test = testFor(pathSuffix("edit" / Segment) { echoCaptureAndUnmatchedPath }) val test = testFor(pathSuffix("edit" / Segment) { echoCaptureAndUnmatchedPath })
"accept [/orders/123/edit]" in test("123:/orders/") "accept [/orders/123/edit]" in test("123:/orders/")
"accept [/orders/123/ed]" in test() "reject [/orders/123/ed]" in test()
"accept [/edit]" in test() "reject [/edit]" in test()
} }
"""pathSuffix("foo" / "bar" ~ "baz")""" should { """pathSuffix("foo" / "bar" ~ "baz")""" should {
val test = testFor(pathSuffix("foo" / "bar" ~ "baz") { echoUnmatchedPath }) val test = testFor(pathSuffix("foo" / "bar" ~ "baz") { echoUnmatchedPath })
"accept [/orders/barbaz/foo]" in test("/orders/") "accept [/orders/barbaz/foo]" in test("/orders/")
"accept [/orders/bazbar/foo]" in test() "reject [/orders/bazbar/foo]" in test()
} }
"pathSuffixTest(Slash)" should { "pathSuffixTest(Slash)" should {
val test = testFor(pathSuffixTest(Slash) { echoUnmatchedPath }) val test = testFor(pathSuffixTest(Slash) { echoUnmatchedPath })
"accept [/]" in test("/") "accept [/]" in test("/")
"accept [/foo/]" in test("/foo/") "accept [/foo/]" in test("/foo/")
"accept [/foo]" in test() "reject [/foo]" in test()
} }
"""pathPrefix("foo" | "bar")""" should { """pathPrefix("foo" | "bar")""" should {
@ -242,6 +242,20 @@ class PathDirectivesSpec extends RoutingSpec with Inside {
} }
} }
"""rawPathPrefix(Slash ~ "a" / Segment ~ Slash)""" should {
val test = testFor(rawPathPrefix(Slash ~ "a" / Segment ~ Slash) { echoCaptureAndUnmatchedPath })
"accept [/a/bc/]" in test("bc:")
"reject [/a/bc]" in test()
"reject [/ab/]" in test()
}
"""rawPathPrefixTest(Slash ~ "a" / Segment ~ Slash)""" should {
val test = testFor(rawPathPrefixTest(Slash ~ "a" / Segment ~ Slash) { echoCaptureAndUnmatchedPath })
"accept [/a/bc/]" in test("bc:/a/bc/")
"reject [/a/bc]" in test()
"reject [/ab/]" in test()
}
"PathMatchers" should { "PathMatchers" should {
{ {
val test = testFor(path(Rest.tmap { case Tuple1(s) Tuple1(s.split('-').toList) }) { echoComplete }) val test = testFor(path(Rest.tmap { case Tuple1(s) Tuple1(s.split('-').toList) }) { echoComplete })
@ -270,8 +284,17 @@ class PathDirectivesSpec extends RoutingSpec with Inside {
case class testFor(route: Route) { case class testFor(route: Route) {
def apply(expectedResponse: String = null): String Unit = exampleString def apply(expectedResponse: String = null): String Unit = exampleString
"\\[([^\\]]+)\\]".r.findFirstMatchIn(exampleString) match { """(accept|reject)\s+\[([^\]]+)\]""".r.findFirstMatchIn(exampleString) match {
case Some(uri) Get(uri.group(1)) ~> route ~> check { case Some(uri)
uri.group(1) match {
case "accept" if expectedResponse eq null
failTest("Example '" + exampleString + "' was missing an expectedResponse")
case "reject" if expectedResponse ne null
failTest("Example '" + exampleString + "' had an expectedResponse")
case _
}
Get(uri.group(2)) ~> route ~> check {
if (expectedResponse eq null) handled shouldEqual false if (expectedResponse eq null) handled shouldEqual false
else responseAs[String] shouldEqual expectedResponse else responseAs[String] shouldEqual expectedResponse
} }

View file

@ -146,6 +146,7 @@ object PathMatcher extends ImplicitPathMatcherConstruction {
else Unmatched else Unmatched
} }
/** Provoke implicit conversions to PathMatcher to be applied */
def apply[L](magnet: PathMatcher[L]): PathMatcher[L] = magnet def apply[L](magnet: PathMatcher[L]): PathMatcher[L] = magnet
implicit class PathMatcher1Ops[T](matcher: PathMatcher1[T]) { implicit class PathMatcher1Ops[T](matcher: PathMatcher1[T]) {

View file

@ -6,7 +6,6 @@ package akka.http.scaladsl.unmarshalling
import scala.collection.immutable import scala.collection.immutable
import scala.collection.immutable.VectorBuilder import scala.collection.immutable.VectorBuilder
import scala.concurrent.ExecutionContext
import akka.util.ByteString import akka.util.ByteString
import akka.event.{ NoLogging, LoggingAdapter } import akka.event.{ NoLogging, LoggingAdapter }
import akka.stream.impl.fusing.IteratorInterpreter import akka.stream.impl.fusing.IteratorInterpreter
@ -21,9 +20,9 @@ import HttpCharsets._
trait MultipartUnmarshallers { trait MultipartUnmarshallers {
implicit def defaultMultipartGeneralUnmarshaller(implicit ec: ExecutionContext, log: LoggingAdapter = NoLogging): FromEntityUnmarshaller[Multipart.General] = implicit def defaultMultipartGeneralUnmarshaller(implicit log: LoggingAdapter = NoLogging): FromEntityUnmarshaller[Multipart.General] =
multipartGeneralUnmarshaller(`UTF-8`) multipartGeneralUnmarshaller(`UTF-8`)
def multipartGeneralUnmarshaller(defaultCharset: HttpCharset)(implicit ec: ExecutionContext, log: LoggingAdapter = NoLogging): FromEntityUnmarshaller[Multipart.General] = def multipartGeneralUnmarshaller(defaultCharset: HttpCharset)(implicit log: LoggingAdapter = NoLogging): FromEntityUnmarshaller[Multipart.General] =
multipartUnmarshaller[Multipart.General, Multipart.General.BodyPart, Multipart.General.BodyPart.Strict]( multipartUnmarshaller[Multipart.General, Multipart.General.BodyPart, Multipart.General.BodyPart.Strict](
mediaRange = `multipart/*`, mediaRange = `multipart/*`,
defaultContentType = ContentTypes.`text/plain` withCharset defaultCharset, defaultContentType = ContentTypes.`text/plain` withCharset defaultCharset,
@ -32,7 +31,7 @@ trait MultipartUnmarshallers {
createStrictBodyPart = Multipart.General.BodyPart.Strict, createStrictBodyPart = Multipart.General.BodyPart.Strict,
createStrict = Multipart.General.Strict) createStrict = Multipart.General.Strict)
implicit def multipartFormDataUnmarshaller(implicit ec: ExecutionContext, log: LoggingAdapter = NoLogging): FromEntityUnmarshaller[Multipart.FormData] = implicit def multipartFormDataUnmarshaller(implicit log: LoggingAdapter = NoLogging): FromEntityUnmarshaller[Multipart.FormData] =
multipartUnmarshaller[Multipart.FormData, Multipart.FormData.BodyPart, Multipart.FormData.BodyPart.Strict]( multipartUnmarshaller[Multipart.FormData, Multipart.FormData.BodyPart, Multipart.FormData.BodyPart.Strict](
mediaRange = `multipart/form-data`, mediaRange = `multipart/form-data`,
defaultContentType = ContentTypes.`application/octet-stream`, defaultContentType = ContentTypes.`application/octet-stream`,
@ -41,9 +40,9 @@ trait MultipartUnmarshallers {
createStrictBodyPart = (entity, headers) Multipart.General.BodyPart.Strict(entity, headers).toFormDataBodyPart.get, createStrictBodyPart = (entity, headers) Multipart.General.BodyPart.Strict(entity, headers).toFormDataBodyPart.get,
createStrict = (_, parts) Multipart.FormData.Strict(parts)) createStrict = (_, parts) Multipart.FormData.Strict(parts))
implicit def defaultMultipartByteRangesUnmarshaller(implicit ec: ExecutionContext, log: LoggingAdapter = NoLogging): FromEntityUnmarshaller[Multipart.ByteRanges] = implicit def defaultMultipartByteRangesUnmarshaller(implicit log: LoggingAdapter = NoLogging): FromEntityUnmarshaller[Multipart.ByteRanges] =
multipartByteRangesUnmarshaller(`UTF-8`) multipartByteRangesUnmarshaller(`UTF-8`)
def multipartByteRangesUnmarshaller(defaultCharset: HttpCharset)(implicit ec: ExecutionContext, log: LoggingAdapter = NoLogging): FromEntityUnmarshaller[Multipart.ByteRanges] = def multipartByteRangesUnmarshaller(defaultCharset: HttpCharset)(implicit log: LoggingAdapter = NoLogging): FromEntityUnmarshaller[Multipart.ByteRanges] =
multipartUnmarshaller[Multipart.ByteRanges, Multipart.ByteRanges.BodyPart, Multipart.ByteRanges.BodyPart.Strict]( multipartUnmarshaller[Multipart.ByteRanges, Multipart.ByteRanges.BodyPart, Multipart.ByteRanges.BodyPart.Strict](
mediaRange = `multipart/byteranges`, mediaRange = `multipart/byteranges`,
defaultContentType = ContentTypes.`text/plain` withCharset defaultCharset, defaultContentType = ContentTypes.`text/plain` withCharset defaultCharset,
@ -57,7 +56,7 @@ trait MultipartUnmarshallers {
createBodyPart: (BodyPartEntity, List[HttpHeader]) BP, createBodyPart: (BodyPartEntity, List[HttpHeader]) BP,
createStreamed: (MultipartMediaType, Source[BP, Any]) T, createStreamed: (MultipartMediaType, Source[BP, Any]) T,
createStrictBodyPart: (HttpEntity.Strict, List[HttpHeader]) BPS, createStrictBodyPart: (HttpEntity.Strict, List[HttpHeader]) BPS,
createStrict: (MultipartMediaType, immutable.Seq[BPS]) T)(implicit ec: ExecutionContext, log: LoggingAdapter = NoLogging): FromEntityUnmarshaller[T] = createStrict: (MultipartMediaType, immutable.Seq[BPS]) T)(implicit log: LoggingAdapter = NoLogging): FromEntityUnmarshaller[T] =
Unmarshaller { implicit ec Unmarshaller { implicit ec
entity entity
if (entity.contentType.mediaType.isMultipart && mediaRange.matches(entity.contentType.mediaType)) { if (entity.contentType.mediaType.isMultipart && mediaRange.matches(entity.contentType.mediaType)) {