From b2e6b650fd2ae776d329aa8c76e3c18c0e4254f7 Mon Sep 17 00:00:00 2001 From: Johannes Rudolph Date: Mon, 18 May 2015 16:09:26 +0200 Subject: [PATCH] !htc #16826 introduce HttpCookiePair for usage in `Cookie`-header --- .../CookieDirectivesExamplesSpec.scala | 14 ++--- .../http/javadsl/model/headers/Cookie.java | 9 ++-- .../javadsl/model/headers/HttpCookie.java | 11 ++-- .../javadsl/model/headers/HttpCookiePair.java | 24 +++++++++ .../http/impl/model/parser/CommonRules.scala | 2 +- .../impl/model/parser/SimpleHeaders.scala | 2 +- .../akka/http/impl/util/EnhancedString.scala | 2 +- .../akka/http/impl/util/JavaMapping.scala | 1 + .../http/scaladsl/model/HttpMessage.scala | 2 +- .../scaladsl/model/headers/HttpCookie.scala | 53 +++++++++++++++---- .../http/scaladsl/model/headers/headers.scala | 15 +++--- .../impl/model/parser/HttpHeaderSpec.scala | 19 +++---- .../http/javadsl/JavaApiTestCaseSpecs.scala | 4 +- .../directives/CookieDirectivesSpec.scala | 8 +-- .../server/directives/CookieDirectives.scala | 8 +-- 15 files changed, 115 insertions(+), 59 deletions(-) create mode 100644 akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpCookiePair.java diff --git a/akka-docs-dev/rst/scala/code/docs/http/scaladsl/server/directives/CookieDirectivesExamplesSpec.scala b/akka-docs-dev/rst/scala/code/docs/http/scaladsl/server/directives/CookieDirectivesExamplesSpec.scala index b6b47a4a45..9d0270a808 100644 --- a/akka-docs-dev/rst/scala/code/docs/http/scaladsl/server/directives/CookieDirectivesExamplesSpec.scala +++ b/akka-docs-dev/rst/scala/code/docs/http/scaladsl/server/directives/CookieDirectivesExamplesSpec.scala @@ -13,10 +13,10 @@ class CookieDirectivesExamplesSpec extends RoutingSpec { "cookie" in { val route = cookie("userName") { nameCookie => - complete(s"The logged in user is '${nameCookie.content}'") + complete(s"The logged in user is '${nameCookie.value}'") } - Get("/") ~> Cookie(HttpCookie("userName", "paul")) ~> route ~> check { + Get("/") ~> Cookie("userName" -> "paul") ~> route ~> check { responseAs[String] shouldEqual "The logged in user is 'paul'" } // missing cookie @@ -30,11 +30,11 @@ class CookieDirectivesExamplesSpec extends RoutingSpec { "optionalCookie" in { val route = optionalCookie("userName") { - case Some(nameCookie) => complete(s"The logged in user is '${nameCookie.content}'") + case Some(nameCookie) => complete(s"The logged in user is '${nameCookie.value}'") case None => complete("No user logged in") } - Get("/") ~> Cookie(HttpCookie("userName", "paul")) ~> route ~> check { + Get("/") ~> Cookie("userName" -> "paul") ~> route ~> check { responseAs[String] shouldEqual "The logged in user is 'paul'" } Get("/") ~> route ~> check { @@ -49,18 +49,18 @@ class CookieDirectivesExamplesSpec extends RoutingSpec { Get("/") ~> route ~> check { responseAs[String] shouldEqual "The user was logged out" - header[`Set-Cookie`] shouldEqual Some(`Set-Cookie`(HttpCookie("userName", content = "deleted", expires = Some(DateTime.MinValue)))) + header[`Set-Cookie`] shouldEqual Some(`Set-Cookie`(HttpCookie("userName", value = "deleted", expires = Some(DateTime.MinValue)))) } } "setCookie" in { val route = - setCookie(HttpCookie("userName", content = "paul")) { + setCookie(HttpCookie("userName", value = "paul")) { complete("The user was logged in") } Get("/") ~> route ~> check { responseAs[String] shouldEqual "The user was logged in" - header[`Set-Cookie`] shouldEqual Some(`Set-Cookie`(HttpCookie("userName", content = "paul"))) + header[`Set-Cookie`] shouldEqual Some(`Set-Cookie`(HttpCookie("userName", value = "paul"))) } } } diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Cookie.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Cookie.java index 0944449844..cc5c4a7f71 100644 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Cookie.java +++ b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Cookie.java @@ -9,9 +9,12 @@ package akka.http.javadsl.model.headers; * Specification: https://tools.ietf.org/html/rfc6265#section-4.2 */ public abstract class Cookie extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getCookies(); + public abstract Iterable getCookies(); - public static Cookie create(HttpCookie... cookies) { - return new akka.http.scaladsl.model.headers.Cookie(akka.http.impl.util.Util.convertArray(cookies)); + public static Cookie create(HttpCookiePair... cookies) { + return new akka.http.scaladsl.model.headers.Cookie(akka.http.impl.util.Util.convertArray(cookies)); + } + public static Cookie create(String name, String value) { + return create(HttpCookiePair.create(name, value)); } } diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpCookie.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpCookie.java index 8c1e0aca96..3faf0e266c 100644 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpCookie.java +++ b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpCookie.java @@ -10,7 +10,8 @@ import akka.japi.Option; public abstract class HttpCookie { public abstract String name(); - public abstract String content(); + public abstract String value(); + public abstract HttpCookiePair pair(); public abstract Option getExpires(); public abstract Option getMaxAge(); @@ -20,9 +21,9 @@ public abstract class HttpCookie { public abstract boolean httpOnly(); public abstract Option getExtension(); - public static HttpCookie create(String name, String content) { + public static HttpCookie create(String name, String value) { return new akka.http.scaladsl.model.headers.HttpCookie( - name, content, + name, value, Util.scalaNone(), Util.scalaNone(), Util.scalaNone(), Util.scalaNone(), false, false, Util.scalaNone()); @@ -30,7 +31,7 @@ public abstract class HttpCookie { @SuppressWarnings("unchecked") public static HttpCookie create( String name, - String content, + String value, Option expires, Option maxAge, Option domain, @@ -39,7 +40,7 @@ public abstract class HttpCookie { boolean httpOnly, Option extension) { return new akka.http.scaladsl.model.headers.HttpCookie( - name, content, + name, value, Util.convertOptionToScala(expires), ((Option) (Option) maxAge).asScala(), domain.asScala(), diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpCookiePair.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpCookiePair.java new file mode 100644 index 0000000000..88f9159806 --- /dev/null +++ b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpCookiePair.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2009-2015 Typesafe Inc. + */ + +package akka.http.javadsl.model.headers; + +/** + * Represents a cookie pair as used in the `Cookie` header as specified in + * http://tools.ietf.org/search/rfc6265#section-4.2.1 + */ +public abstract class HttpCookiePair { + public abstract String name(); + public abstract String value(); + + /** + * Converts this cookie pair into an HttpCookie to be used with the + * `Set-Cookie` header. + */ + public abstract HttpCookie toCookie(); + + public static HttpCookiePair create(String name, String value) { + return new akka.http.scaladsl.model.headers.HttpCookiePair(name, value); + } +} diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/parser/CommonRules.scala b/akka-http-core/src/main/scala/akka/http/impl/model/parser/CommonRules.scala index b83ada4c3d..e133beaecb 100644 --- a/akka-http-core/src/main/scala/akka/http/impl/model/parser/CommonRules.scala +++ b/akka-http-core/src/main/scala/akka/http/impl/model/parser/CommonRules.scala @@ -218,7 +218,7 @@ private[parser] trait CommonRules { this: Parser with StringBuilding ⇒ // https://tools.ietf.org/html/rfc6265#section-4.1.1 // ****************************************************************************************** def `cookie-pair` = rule { - `cookie-name` ~ ws('=') ~ `cookie-value` ~> (HttpCookie(_, _)) + `cookie-name` ~ ws('=') ~ `cookie-value` ~> (HttpCookiePair(_: String, _: String)) } def `cookie-name` = rule { token } diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/parser/SimpleHeaders.scala b/akka-http-core/src/main/scala/akka/http/impl/model/parser/SimpleHeaders.scala index 58244291f1..bbf9912606 100644 --- a/akka-http-core/src/main/scala/akka/http/impl/model/parser/SimpleHeaders.scala +++ b/akka-http-core/src/main/scala/akka/http/impl/model/parser/SimpleHeaders.scala @@ -184,7 +184,7 @@ private[parser] trait SimpleHeaders { this: Parser with CommonRules with CommonA // https://tools.ietf.org/html/rfc6265 def `set-cookie` = rule { - `cookie-pair` ~ zeroOrMore(ws(';') ~ `cookie-av`) ~ EOI ~> (`Set-Cookie`(_)) + `cookie-pair` ~> (_.toCookie) ~ zeroOrMore(ws(';') ~ `cookie-av`) ~ EOI ~> (`Set-Cookie`(_)) } // http://tools.ietf.org/html/rfc7230#section-6.7 diff --git a/akka-http-core/src/main/scala/akka/http/impl/util/EnhancedString.scala b/akka-http-core/src/main/scala/akka/http/impl/util/EnhancedString.scala index 82e73f98c3..12aa1a5009 100644 --- a/akka-http-core/src/main/scala/akka/http/impl/util/EnhancedString.scala +++ b/akka-http-core/src/main/scala/akka/http/impl/util/EnhancedString.scala @@ -53,7 +53,7 @@ private[http] class EnhancedString(val underlying: String) extends AnyVal { } /** - * Returns Some(String) if the underlying string is non-emtpy, None otherwise + * Returns Some(String) if the underlying string is non-empty, None otherwise */ def toOption: Option[String] = if ((underlying eq null) || underlying.isEmpty) None else Some(underlying) diff --git a/akka-http-core/src/main/scala/akka/http/impl/util/JavaMapping.scala b/akka-http-core/src/main/scala/akka/http/impl/util/JavaMapping.scala index 203b25d15e..f3913a127d 100644 --- a/akka-http-core/src/main/scala/akka/http/impl/util/JavaMapping.scala +++ b/akka-http-core/src/main/scala/akka/http/impl/util/JavaMapping.scala @@ -132,6 +132,7 @@ object JavaMapping { implicit object EntityTagRange extends Inherited[jm.headers.EntityTagRange, sm.headers.EntityTagRange] implicit object HttpChallenge extends Inherited[jm.headers.HttpChallenge, sm.headers.HttpChallenge] implicit object HttpCookie extends Inherited[jm.headers.HttpCookie, sm.headers.HttpCookie] + implicit object HttpCookiePair extends Inherited[jm.headers.HttpCookiePair, sm.headers.HttpCookiePair] implicit object HttpCredentials extends Inherited[jm.headers.HttpCredentials, sm.headers.HttpCredentials] implicit object HttpEncoding extends Inherited[jm.headers.HttpEncoding, sm.headers.HttpEncoding] implicit object HttpEncodingRange extends Inherited[jm.headers.HttpEncodingRange, sm.headers.HttpEncodingRange] diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpMessage.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpMessage.scala index e0af56e275..a78b2f4b64 100644 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpMessage.scala +++ b/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpMessage.scala @@ -199,7 +199,7 @@ final case class HttpRequest(method: HttpMethod = HttpMethods.GET, /** * All cookies provided by the client in one or more `Cookie` headers. */ - def cookies: immutable.Seq[HttpCookie] = for (`Cookie`(cookies) ← headers; cookie ← cookies) yield cookie + def cookies: immutable.Seq[HttpCookiePair] = for (`Cookie`(cookies) ← headers; cookie ← cookies) yield cookie /** * Determines whether the given media-type is accepted by the client. diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/HttpCookie.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/HttpCookie.scala index 86f453059a..717b3c1feb 100644 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/HttpCookie.scala +++ b/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/HttpCookie.scala @@ -11,29 +11,52 @@ import akka.http.impl.util._ import akka.http.javadsl.{ model ⇒ jm } import akka.http.impl.util.JavaMapping.Implicits._ +// see http://tools.ietf.org/html/rfc6265 +final case class HttpCookiePair( + name: String, + value: String) extends jm.headers.HttpCookiePair with ToStringRenderable { + + HttpCookiePair.validate(name, value) + + def render[R <: Rendering](r: R): r.type = r ~~ name ~~ '=' ~~ value + def toCookie: HttpCookie = HttpCookie.fromPair(this) +} +object HttpCookiePair { + def apply(pair: (String, String)): HttpCookiePair = HttpCookiePair(pair._1, pair._2) + + private[http] def validate(name: String, value: String): Unit = { + import HttpCookie._ + require(nameChars.matchesAll(name), s"'${nameChars.firstMismatch(name).get}' not allowed in cookie name ('$name')") + require(valueChars.matchesAll(value), s"'${valueChars.firstMismatch(value).get}' not allowed in cookie content ('$value')") + } +} + // see http://tools.ietf.org/html/rfc6265 final case class HttpCookie( name: String, - content: String, + value: String, expires: Option[DateTime] = None, maxAge: Option[Long] = None, domain: Option[String] = None, path: Option[String] = None, secure: Boolean = false, httpOnly: Boolean = false, - extension: Option[String] = None) extends jm.headers.HttpCookie with ValueRenderable { + extension: Option[String] = None) extends jm.headers.HttpCookie with ToStringRenderable { + + /** Returns the name/value pair for this cookie, to be used in [[Cookiie]] headers. */ + def pair: HttpCookiePair = HttpCookiePair(name, value) + + // TODO: suppress running these requires for cookies created from our header parser import HttpCookie._ - // TODO: suppress running these requires for cookies created from our header parser - require(nameChars.matchesAll(name), s"'${nameChars.firstMismatch(name).get}' not allowed in cookie name ('$name')") - require(contentChars.matchesAll(content), s"'${contentChars.firstMismatch(content).get}' not allowed in cookie content ('$content')") + HttpCookiePair.validate(name, value) require(domain.forall(domainChars.matchesAll), s"'${domainChars.firstMismatch(domain.get).get}' not allowed in cookie domain ('${domain.get}')") require(path.forall(pathOrExtChars.matchesAll), s"'${pathOrExtChars.firstMismatch(path.get).get}' not allowed in cookie path ('${path.get}')") require(extension.forall(pathOrExtChars.matchesAll), s"'${pathOrExtChars.firstMismatch(extension.get).get}' not allowed in cookie extension ('${extension.get}')") def render[R <: Rendering](r: R): r.type = { - r ~~ name ~~ '=' ~~ content + r ~~ name ~~ '=' ~~ value if (expires.isDefined) expires.get.renderRfc1123DateTimeString(r ~~ "; Expires=") if (maxAge.isDefined) r ~~ "; Max-Age=" ~~ maxAge.get if (domain.isDefined) r ~~ "; Domain=" ~~ domain.get @@ -57,12 +80,22 @@ final case class HttpCookie( } object HttpCookie { + def fromPair(pair: HttpCookiePair, + expires: Option[DateTime] = None, + maxAge: Option[Long] = None, + domain: Option[String] = None, + path: Option[String] = None, + secure: Boolean = false, + httpOnly: Boolean = false, + extension: Option[String] = None): HttpCookie = + HttpCookie(pair.name, pair.value, expires, maxAge, domain, path, secure, httpOnly, extension) + import akka.http.impl.model.parser.CharacterClasses._ - def nameChars = tchar + private[http] def nameChars = tchar // http://tools.ietf.org/html/rfc6265#section-4.1.1 // ; US-ASCII characters excluding CTLs, whitespace DQUOTE, comma, semicolon, and backslash - val contentChars = CharPredicate('\u0021', '\u0023' to '\u002B', '\u002D' to '\u003A', '\u003C' to '\u005B', '\u005D' to '\u007E') - val domainChars = ALPHANUM ++ ".-" - val pathOrExtChars = VCHAR ++ ' ' -- ';' + private[http] val valueChars = CharPredicate('\u0021', '\u0023' to '\u002B', '\u002D' to '\u003A', '\u003C' to '\u005B', '\u005D' to '\u007E') + private[http] val domainChars = ALPHANUM ++ ".-" + private[http] val pathOrExtChars = VCHAR ++ ' ' -- ';' } diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/headers.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/headers.scala index 156fba395d..37bc55c9bb 100644 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/headers.scala +++ b/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/headers.scala @@ -384,20 +384,19 @@ final case class `Content-Type` private[http] (contentType: ContentType) extends // https://tools.ietf.org/html/rfc6265#section-4.2 object Cookie extends ModeledCompanion { - implicit val cookieNameValueOnlyRenderer: Renderer[HttpCookie] = new Renderer[HttpCookie] { - def render[R <: Rendering](r: R, c: HttpCookie): r.type = r ~~ c.name ~~ '=' ~~ c.content - } - def apply(first: HttpCookie, more: HttpCookie*): Cookie = apply(immutable.Seq(first +: more: _*)) - implicit val cookiesRenderer = Renderer.seqRenderer[HttpCookie](separator = "; ") // cache + def apply(first: HttpCookiePair, more: HttpCookiePair*): Cookie = apply(immutable.Seq(first +: more: _*)) + def apply(name: String, value: String): Cookie = apply(HttpCookiePair(name, value)) + def apply(values: (String, String)*): Cookie = apply(values.map(HttpCookiePair(_)).toList) + implicit val cookiePairsRenderer = Renderer.seqRenderer[HttpCookiePair](separator = "; ") // cache } -final case class Cookie(cookies: immutable.Seq[HttpCookie]) extends jm.headers.Cookie with ModeledHeader { +final case class Cookie(cookies: immutable.Seq[HttpCookiePair]) extends jm.headers.Cookie with ModeledHeader { require(cookies.nonEmpty, "cookies must not be empty") - import Cookie.cookiesRenderer + import Cookie.cookiePairsRenderer def renderValue[R <: Rendering](r: R): r.type = r ~~ cookies protected def companion = Cookie /** Java API */ - def getCookies: Iterable[jm.headers.HttpCookie] = cookies.asJava + def getCookies: Iterable[jm.headers.HttpCookiePair] = cookies.asJava } // http://tools.ietf.org/html/rfc7231#section-7.1.1.2 diff --git a/akka-http-core/src/test/scala/akka/http/impl/model/parser/HttpHeaderSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/model/parser/HttpHeaderSpec.scala index 842c13df34..5de9aaf4a7 100644 --- a/akka-http-core/src/test/scala/akka/http/impl/model/parser/HttpHeaderSpec.scala +++ b/akka-http-core/src/test/scala/akka/http/impl/model/parser/HttpHeaderSpec.scala @@ -217,19 +217,12 @@ class HttpHeaderSpec extends FreeSpec with Matchers { } "Cookie" in { - "Cookie: SID=31d4d96e407aad42" =!= Cookie(HttpCookie("SID", "31d4d96e407aad42")) - "Cookie: SID=31d4d96e407aad42; lang=en>US" =!= Cookie(HttpCookie("SID", "31d4d96e407aad42"), HttpCookie("lang", "en>US")) - "Cookie: a=1;b=2" =!= Cookie(HttpCookie("a", "1"), HttpCookie("b", "2")).renderedTo("a=1; b=2") - "Cookie: a=1 ;b=2" =!= Cookie(HttpCookie("a", "1"), HttpCookie("b", "2")).renderedTo("a=1; b=2") - "Cookie: a=1; b=2" =!= Cookie(HttpCookie("a", "1"), HttpCookie("b", "2")) - "Cookie: a=1,b=2" =!= Cookie(HttpCookie("a", "1"), HttpCookie("b", "2")).renderedTo("a=1; b=2") - Cookie(HttpCookie("SID", "31d4d96e407aad42", - domain = Some("example.com"), - expires = Some(DateTime(2021, 6, 9, 10, 18, 14)), - path = Some("/hello"), - httpOnly = true, - extension = Some("fancyPants"), - secure = true)).toString shouldEqual "Cookie: SID=31d4d96e407aad42" + "Cookie: SID=31d4d96e407aad42" =!= Cookie("SID" -> "31d4d96e407aad42") + "Cookie: SID=31d4d96e407aad42; lang=en>US" =!= Cookie("SID" -> "31d4d96e407aad42", "lang" -> "en>US") + "Cookie: a=1;b=2" =!= Cookie("a" -> "1", "b" -> "2").renderedTo("a=1; b=2") + "Cookie: a=1 ;b=2" =!= Cookie("a" -> "1", "b" -> "2").renderedTo("a=1; b=2") + "Cookie: a=1; b=2" =!= Cookie("a" -> "1", "b" -> "2") + "Cookie: a=1,b=2" =!= Cookie("a" -> "1", "b" -> "2").renderedTo("a=1; b=2") } "Date" in { diff --git a/akka-http-core/src/test/scala/akka/http/javadsl/JavaApiTestCaseSpecs.scala b/akka-http-core/src/test/scala/akka/http/javadsl/JavaApiTestCaseSpecs.scala index ef41c53f79..882f3d63a7 100644 --- a/akka-http-core/src/test/scala/akka/http/javadsl/JavaApiTestCaseSpecs.scala +++ b/akka-http-core/src/test/scala/akka/http/javadsl/JavaApiTestCaseSpecs.scala @@ -4,6 +4,8 @@ package akka.http.javadsl.model +import akka.http.javadsl.model.headers.Cookie + import scala.collection.immutable import org.scalatest.{ MustMatchers, FreeSpec } import akka.http.scaladsl.model.headers.BasicHttpCredentials @@ -42,7 +44,7 @@ class JavaApiTestCaseSpecs extends FreeSpec with MustMatchers { model.HttpRequest(headers = immutable.Seq(model.headers.Authorization(BasicHttpCredentials("username", "password"))))) } "removeCookies" in { - val testRequest = model.HttpRequest(headers = immutable.Seq(model.headers.Cookie(model.headers.HttpCookie("test", "blub")))) + val testRequest = model.HttpRequest(headers = immutable.Seq(Cookie.create("test", "blub"))) JavaApiTestCases.removeCookies(testRequest) must be( model.HttpRequest()) } diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/CookieDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/CookieDirectivesSpec.scala index dab2344529..915f913296 100644 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/CookieDirectivesSpec.scala +++ b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/CookieDirectivesSpec.scala @@ -15,7 +15,7 @@ class CookieDirectivesSpec extends RoutingSpec { "The 'cookie' directive" should { "extract the respectively named cookie" in { - Get() ~> addHeader(Cookie(HttpCookie("fancy", "pants"))) ~> { + Get() ~> addHeader(Cookie("fancy" -> "pants")) ~> { cookie("fancy") { echoComplete } } ~> check { responseAs[String] shouldEqual "fancy=pants" } } @@ -25,8 +25,8 @@ class CookieDirectivesSpec extends RoutingSpec { } ~> check { rejection shouldEqual MissingCookieRejection("fancy") } } "properly pass through inner rejections" in { - Get() ~> addHeader(Cookie(HttpCookie("fancy", "pants"))) ~> { - cookie("fancy") { c ⇒ reject(ValidationRejection("Dont like " + c.content)) } + Get() ~> addHeader(Cookie("fancy" -> "pants")) ~> { + cookie("fancy") { c ⇒ reject(ValidationRejection("Dont like " + c.value)) } } ~> check { rejection shouldEqual ValidationRejection("Dont like pants") } } } @@ -56,7 +56,7 @@ class CookieDirectivesSpec extends RoutingSpec { "The 'optionalCookie' directive" should { "produce a `Some(cookie)` extraction if the cookie is present" in { - Get() ~> Cookie(HttpCookie("abc", "123")) ~> { + Get() ~> Cookie("abc" -> "123") ~> { optionalCookie("abc") { echoComplete } } ~> check { responseAs[String] shouldEqual "Some(abc=123)" } } diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/CookieDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/CookieDirectives.scala index 85e480c2d1..2bcb530ed1 100644 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/CookieDirectives.scala +++ b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/CookieDirectives.scala @@ -18,17 +18,17 @@ trait CookieDirectives { * Extracts an HttpCookie with the given name. If the cookie is not present the * request is rejected with a respective [[MissingCookieRejection]]. */ - def cookie(name: String): Directive1[HttpCookie] = + def cookie(name: String): Directive1[HttpCookiePair] = headerValue(findCookie(name)) | reject(MissingCookieRejection(name)) /** * Extracts an HttpCookie with the given name. * If the cookie is not present a value of `None` is extracted. */ - def optionalCookie(name: String): Directive1[Option[HttpCookie]] = + def optionalCookie(name: String): Directive1[Option[HttpCookiePair]] = optionalHeaderValue(findCookie(name)) - private def findCookie(name: String): HttpHeader ⇒ Option[HttpCookie] = { + private def findCookie(name: String): HttpHeader ⇒ Option[HttpCookiePair] = { case Cookie(cookies) ⇒ cookies.find(_.name == name) case _ ⇒ None } @@ -44,7 +44,7 @@ trait CookieDirectives { */ def deleteCookie(first: HttpCookie, more: HttpCookie*): Directive0 = respondWithHeaders((first :: more.toList).map { c ⇒ - `Set-Cookie`(c.copy(content = "deleted", expires = Some(DateTime.MinValue))) + `Set-Cookie`(c.copy(value = "deleted", expires = Some(DateTime.MinValue))) }) /**