diff --git a/akka-docs-dev/rst/java/code/docs/http/javadsl/server/FormFieldRequestValsExampleTest.java b/akka-docs-dev/rst/java/code/docs/http/javadsl/server/FormFieldRequestValsExampleTest.java index d79636ba9a..705c89ef35 100644 --- a/akka-docs-dev/rst/java/code/docs/http/javadsl/server/FormFieldRequestValsExampleTest.java +++ b/akka-docs-dev/rst/java/code/docs/http/javadsl/server/FormFieldRequestValsExampleTest.java @@ -4,20 +4,13 @@ package docs.http.javadsl.server; -import akka.http.javadsl.model.ContentTypes; import akka.http.javadsl.model.FormData; import akka.http.javadsl.model.HttpRequest; -import akka.http.javadsl.model.headers.RawHeader; -import akka.http.javadsl.server.Marshallers; -import akka.http.javadsl.server.RequestVal; import akka.http.javadsl.server.Route; import akka.http.javadsl.server.values.FormField; import akka.http.javadsl.server.values.FormFields; -import akka.http.javadsl.server.values.Headers; import akka.http.javadsl.testkit.JUnitRouteTest; import akka.japi.Pair; -import docs.http.scaladsl.server.directives.Person; -import org.junit.Ignore; import org.junit.Test; public class FormFieldRequestValsExampleTest extends JUnitRouteTest { diff --git a/akka-docs-dev/rst/java/code/docs/http/javadsl/server/HttpServerExampleDocTest.java b/akka-docs-dev/rst/java/code/docs/http/javadsl/server/HttpServerExampleDocTest.java index a163e305cf..60f314f151 100644 --- a/akka-docs-dev/rst/java/code/docs/http/javadsl/server/HttpServerExampleDocTest.java +++ b/akka-docs-dev/rst/java/code/docs/http/javadsl/server/HttpServerExampleDocTest.java @@ -6,7 +6,6 @@ package docs.http.javadsl.server; import akka.actor.ActorSystem; import akka.dispatch.OnFailure; -import akka.http.impl.util.JavaMapping; import akka.http.impl.util.Util; import akka.http.javadsl.Http; import akka.http.javadsl.IncomingConnection; @@ -17,9 +16,7 @@ import akka.http.javadsl.model.HttpMethods; import akka.http.javadsl.model.HttpRequest; import akka.http.javadsl.model.HttpResponse; import akka.http.javadsl.model.Uri; -import akka.http.scaladsl.model.*; import akka.http.scaladsl.model.HttpEntity; -import akka.japi.JavaPartialFunction; import akka.japi.function.Function; import akka.japi.function.Procedure; import akka.stream.ActorMaterializer; @@ -32,7 +29,6 @@ import akka.stream.stage.PushStage; import akka.stream.stage.SyncDirective; import akka.stream.stage.TerminationDirective; import akka.util.ByteString; -import scala.Function1; import scala.concurrent.Await; import scala.concurrent.Future; import scala.concurrent.duration.FiniteDuration; @@ -206,7 +202,7 @@ public class HttpServerExampleDocTest { .withEntity(ContentTypes.TEXT_HTML, "Hello world!"); else if (uri.path().equals("/hello")) { - String name = Util.getOrElse(uri.parameter("name"), "Mister X"); + String name = Util.getOrElse(uri.query().get("name"), "Mister X"); return HttpResponse.create() diff --git a/akka-docs-dev/rst/scala/code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala b/akka-docs-dev/rst/scala/code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala index 7cd9e47687..917692f6ad 100644 --- a/akka-docs-dev/rst/scala/code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala +++ b/akka-docs-dev/rst/scala/code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala @@ -199,7 +199,7 @@ class BasicDirectivesExamplesSpec extends RoutingSpec { "textract" in { val pathAndQuery = textract { ctx => val uri = ctx.request.uri - (uri.path, uri.query) + (uri.path, uri.query()) } val route = pathAndQuery { (p, query) => diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/FormData.java b/akka-http-core/src/main/java/akka/http/javadsl/model/FormData.java index 1936758d72..0293531614 100644 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/FormData.java +++ b/akka-http-core/src/main/java/akka/http/javadsl/model/FormData.java @@ -4,34 +4,51 @@ package akka.http.javadsl.model; -import akka.http.impl.model.parser.CharacterClasses; -import akka.http.impl.util.JavaMapping; -import akka.http.impl.util.StringRendering; -import akka.http.scaladsl.model.Uri.Query; -import akka.http.scaladsl.model.UriRendering; import akka.japi.Pair; +import java.util.Map; /** * Simple model for `application/x-www-form-urlencoded` form data. */ -public abstract class FormData { +public final class FormData { - public abstract Query fields(); + private final Query fields; + public FormData(Query fields) { + this.fields = fields; + } + + /** + * Converts this FormData to a RequestEntity using UTF8 encoding. + */ public RequestEntity toEntity() { return toEntity(HttpCharsets.UTF_8); } + /** + * Converts this FormData to a RequestEntity using the given encoding. + */ public RequestEntity toEntity(HttpCharset charset) { - // TODO this logic is duplicated in scaladsl.model.FormData, spent hours trying to DRY it but compiler freaked out in a number of ways... -- ktoso - final akka.http.scaladsl.model.HttpCharset c = (akka.http.scaladsl.model.HttpCharset) charset; - final StringRendering render = (StringRendering) UriRendering.renderQuery(new StringRendering(), this.fields(), c.nioCharset(), CharacterClasses.unreserved()); - return HttpEntities.create(ContentType.create(MediaTypes.APPLICATION_X_WWW_FORM_URLENCODED, charset), render.get()); + return HttpEntities.create(ContentType.create(MediaTypes.APPLICATION_X_WWW_FORM_URLENCODED, charset), fields.render(charset)); } + /** + * Returns empty FormData. + */ + public static final FormData EMPTY = new FormData(Query.EMPTY); + + /** + * Creates the FormData from the given parameters. + */ @SafeVarargs - public static FormData create(Pair... fields) { - return akka.http.scaladsl.model.FormData.create(fields); + public static FormData create(Pair... params) { + return new FormData(Query.create(params)); } + /** + * Creates the FormData from the given parameters. + */ + public static FormData create(Map params) { + return new FormData(Query.create(params)); + } } diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/Host.java b/akka-http-core/src/main/java/akka/http/javadsl/model/Host.java index bdfc094965..baa40fd7d5 100644 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/Host.java +++ b/akka-http-core/src/main/java/akka/http/javadsl/model/Host.java @@ -47,14 +47,14 @@ public abstract class Host { /** * Parse the given Host string using the given charset and the default parsing-mode. */ - public static Host create(String string, Charset charset) { - return UriJavaAccessor.hostApply(string, charset); + public static Host create(String string, Uri.ParsingMode parsingMode) { + return UriJavaAccessor.hostApply(string, parsingMode); } /** * Parse the given Host string using the given charset and parsing-mode. */ public static Host create(String string, Charset charset, Uri.ParsingMode parsingMode) { - return UriJavaAccessor.hostApply(string, charset, parsingMode); + return akka.http.scaladsl.model.Uri.Host$.MODULE$.apply(string, charset, parsingMode); } } diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpCharset.java b/akka-http-core/src/main/java/akka/http/javadsl/model/HttpCharset.java index 5b8e38740c..053d476ca9 100644 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpCharset.java +++ b/akka-http-core/src/main/java/akka/http/javadsl/model/HttpCharset.java @@ -4,6 +4,8 @@ package akka.http.javadsl.model; +import java.nio.charset.Charset; + /** * Represents a charset in Http. See {@link HttpCharsets} for a set of predefined charsets and * static constructors to create custom charsets. @@ -37,4 +39,9 @@ public abstract class HttpCharset { * Returns the predefined alias names for this charset. */ public abstract Iterable getAliases(); + + /** + * Returns the Charset for this charset if available or throws an exception otherwise. + */ + public abstract Charset nioCharset(); } diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpMessage.java b/akka-http-core/src/main/java/akka/http/javadsl/model/HttpMessage.java index 53fb55b5e0..6493b7bad4 100644 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpMessage.java +++ b/akka-http-core/src/main/java/akka/http/javadsl/model/HttpMessage.java @@ -5,7 +5,6 @@ package akka.http.javadsl.model; import akka.japi.Option; -import akka.stream.javadsl.Source; import akka.util.ByteString; import java.io.File; diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/Query.java b/akka-http-core/src/main/java/akka/http/javadsl/model/Query.java new file mode 100644 index 0000000000..74e3d1141f --- /dev/null +++ b/akka-http-core/src/main/java/akka/http/javadsl/model/Query.java @@ -0,0 +1,108 @@ +/** + * Copyright (C) 2009-2014 Typesafe Inc. + */ + +package akka.http.javadsl.model; + +import akka.http.impl.model.JavaQuery; +import akka.http.scaladsl.model.*; +import akka.japi.Option; +import akka.japi.Pair; +import akka.parboiled2.CharPredicate; +import akka.parboiled2.ParserInput$; + +import java.nio.charset.Charset; +import java.util.List; +import java.util.Map; + +public abstract class Query { + /** + * Returns the value of the first parameter with the given key if it exists. + */ + public abstract Option get(String key); + + /** + * Returns the value of the first parameter with the given key or the provided default value. + */ + public abstract String getOrElse(String key, String _default); + + /** + * Returns the value of all parameters with the given key. + */ + public abstract List getAll(String key); + + /** + * Returns a `List` of all parameters of this Query. Use the `toMap()` + * method to filter out entries with duplicated keys. + */ + public abstract List> toList(); + + /** + * Returns a key/value map of the parameters of this Query. Use + * the `toList()` method to return all parameters if keys may occur + * multiple times. + */ + public abstract Map toMap(); + + /** + * Returns a `Map` of all parameters of this Query. Use the `toMap()` + * method to filter out entries with duplicated keys. + */ + public abstract Map> toMultiMap(); + + /** + * Returns a copy of this instance with a query parameter added. + */ + public abstract Query withParam(String key, String value); + + /** + * Renders this Query into its string representation using the given charset. + */ + public abstract String render(HttpCharset charset); + + /** + * Renders this Query into its string representation using the given charset and char predicate. + */ + public abstract String render(HttpCharset charset, CharPredicate keep); + + /** + * Returns an empty Query. + */ + public static final Query EMPTY = new JavaQuery(UriJavaAccessor.emptyQuery()); + + /** + * Returns a Query created by parsing the given undecoded string representation. + */ + public static Query create(String rawQuery) { + return new JavaQuery(akka.http.scaladsl.model.Uri.Query$.MODULE$.apply(rawQuery)); + } + + /** + * Returns a Query created by parsing the given undecoded string representation with the provided parsing mode. + */ + public static Query create(String rawQuery, akka.http.scaladsl.model.Uri.ParsingMode parsingMode) { + return new JavaQuery(UriJavaAccessor.queryApply(rawQuery, parsingMode)); + } + + /** + * Returns a Query created by parsing the given undecoded string representation with the provided charset and parsing mode. + */ + public static Query create(String rawQuery, Charset charset, akka.http.scaladsl.model.Uri.ParsingMode parsingMode) { + return new JavaQuery(akka.http.scaladsl.model.Uri.Query$.MODULE$.apply(ParserInput$.MODULE$.apply(rawQuery), charset, parsingMode)); + } + + /** + * Returns a Query from the given parameters. + */ + @SafeVarargs + public static Query create(Pair... params) { + return new JavaQuery(UriJavaAccessor.queryApply(params)); + } + + /** + * Returns a Query from the given parameters. + */ + public static Query create(Map params) { + return new JavaQuery(UriJavaAccessor.queryApply(params)); + } +} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/Uri.java b/akka-http-core/src/main/java/akka/http/javadsl/model/Uri.java index 2960b0ef00..1ef18e4991 100644 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/Uri.java +++ b/akka-http-core/src/main/java/akka/http/javadsl/model/Uri.java @@ -4,12 +4,14 @@ package akka.http.javadsl.model; -import akka.http.impl.util.JavaAccessors$; +import akka.http.impl.model.JavaUri; import akka.http.scaladsl.model.UriJavaAccessor; import akka.japi.Option; +import akka.japi.Pair; import akka.parboiled2.ParserInput$; import java.nio.charset.Charset; +import java.util.List; import java.util.Map; /** @@ -62,32 +64,24 @@ public abstract class Uri { public abstract Iterable pathSegments(); /** - * Returns a String representation of the query of this Uri. + * Returns a decoded String representation of the query of this Uri. */ - public abstract String queryString(); + public abstract Option queryString(Charset charset); /** - * Looks up a query parameter of this Uri. + * Returns an undecoded String representation of the query of this Uri. */ - public abstract Option parameter(String key); + public abstract Option rawQueryString(); /** - * Returns if the query of this Uri contains a parameter with the given key. + * Returns the parsed Query instance of this Uri. */ - public abstract boolean containsParameter(String key); + public abstract Query query(); /** - * Returns an `Iterable` of all query parameters of this Uri. Use the `parameterMap()` - * method to filter out entries with duplicated keys. + * Returns the parsed Query instance of this Uri using the given charset and parsing mode. */ - public abstract Iterable> parameters(); - - /** - * Returns a key/value map of the query parameters of this Uri. Use - * the `parameters()` method to return all parameters if keys may occur - * multiple times. - */ - public abstract Map parameterMap(); + public abstract Query query(Charset charset, akka.http.scaladsl.model.Uri.ParsingMode mode); /** * Returns the fragment part of this Uri. @@ -132,18 +126,18 @@ public abstract class Uri { /** * Returns a copy of this instance with a new query. */ - public abstract Uri query(String query); + public abstract Uri rawQueryString(String rawQuery); + + /** + * Returns a copy of this instance with a new query. + */ + public abstract Uri query(Query query); /** * Returns a copy of this instance that is relative. */ public abstract Uri toRelative(); - /** - * Returns a copy of this instance with a query parameter added. - */ - public abstract Uri addParameter(String key, String value); - /** * Returns a copy of this instance with a new fragment. */ @@ -156,34 +150,31 @@ public abstract class Uri { public static final akka.http.scaladsl.model.Uri.ParsingMode STRICT = UriJavaAccessor.pmStrict(); public static final akka.http.scaladsl.model.Uri.ParsingMode RELAXED = UriJavaAccessor.pmRelaxed(); - public static final akka.http.scaladsl.model.Uri.ParsingMode RELAXED_WITH_RAW_QUERY = UriJavaAccessor.pmRelaxedWithRawQuery(); /** * Creates a default Uri to be modified using the modification methods. */ - public static Uri create() { - return JavaAccessors$.MODULE$.Uri(akka.http.scaladsl.model.Uri.Empty$.MODULE$); - } + public static final Uri EMPTY = new JavaUri(akka.http.scaladsl.model.Uri.Empty$.MODULE$); /** * Returns a Uri created by parsing the given string representation. */ public static Uri create(String uri) { - return JavaAccessors$.MODULE$.Uri(akka.http.scaladsl.model.Uri.apply(uri)); + return new JavaUri(akka.http.scaladsl.model.Uri.apply(uri)); } /** - * Returns a Uri created by parsing the given string representation and parsing-mode. + * Returns a Uri created by parsing the given string representation with the provided parsing mode. */ public static Uri create(String uri, akka.http.scaladsl.model.Uri.ParsingMode parsingMode) { - return JavaAccessors$.MODULE$.Uri(akka.http.scaladsl.model.Uri.apply(ParserInput$.MODULE$.apply(uri), parsingMode)); + return new JavaUri(akka.http.scaladsl.model.Uri.apply(ParserInput$.MODULE$.apply(uri), parsingMode)); } /** - * Returns a Uri created by parsing the given string representation, charset, and parsing-mode. + * Returns a Uri created by parsing the given string representation with the provided charset and parsing mode. */ public static Uri create(String uri, Charset charset, akka.http.scaladsl.model.Uri.ParsingMode parsingMode) { - return JavaAccessors$.MODULE$.Uri(akka.http.scaladsl.model.Uri.apply(ParserInput$.MODULE$.apply(uri), charset, parsingMode)); + return new JavaUri(akka.http.scaladsl.model.Uri.apply(ParserInput$.MODULE$.apply(uri), charset, parsingMode)); } } diff --git a/akka-http-core/src/main/resources/reference.conf b/akka-http-core/src/main/resources/reference.conf index 34a6e5a77e..a284928059 100644 --- a/akka-http-core/src/main/resources/reference.conf +++ b/akka-http-core/src/main/resources/reference.conf @@ -277,10 +277,6 @@ akka.http { # # `relaxed`: all visible 7-Bit ASCII chars are allowed # - # `relaxed-with-raw-query`: like `relaxed` but additionally - # the URI query is not parsed, but delivered as one raw string - # as the `key` value of a single Query structure element. - # uri-parsing-mode = strict # Enables/disables the logging of warning messages in case an incoming diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/JavaQuery.scala b/akka-http-core/src/main/scala/akka/http/impl/model/JavaQuery.scala new file mode 100644 index 0000000000..878856605b --- /dev/null +++ b/akka-http-core/src/main/scala/akka/http/impl/model/JavaQuery.scala @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2009-2015 Typesafe Inc. + */ + +package akka.http.impl.model + +import java.{ util ⇒ ju } +import akka.http.impl.model.parser.CharacterClasses +import akka.http.impl.util.StringRendering +import akka.http.javadsl.model.HttpCharset +import akka.http.javadsl.{ model ⇒ jm } +import akka.http.scaladsl.model.UriRendering +import akka.http.scaladsl.{ model ⇒ sm } +import akka.japi.{ Pair, Option } +import akka.parboiled2.CharPredicate + +import scala.collection.JavaConverters._ +import akka.http.impl.util.JavaMapping.Implicits._ + +/** INTERNAL API */ +case class JavaQuery(query: sm.Uri.Query) extends jm.Query { + override def get(key: String): Option[String] = query.get(key) + override def toMap: ju.Map[String, String] = query.toMap.asJava + override def toList: ju.List[Pair[String, String]] = query.map(_.asJava).asJava + override def getOrElse(key: String, _default: String): String = query.getOrElse(key, _default) + override def toMultiMap: ju.Map[String, ju.List[String]] = query.toMultiMap.map { case (k, v) ⇒ (k, v.asJava) }.asJava + override def getAll(key: String): ju.List[String] = query.getAll(key).asJava + override def toString = query.toString + override def withParam(key: String, value: String): jm.Query = jm.Query.create(query.map(_.asJava) :+ Pair(key, value): _*) + override def render(charset: HttpCharset): String = + UriRendering.renderQuery(new StringRendering, query, charset.nioCharset, CharacterClasses.unreserved).get + override def render(charset: HttpCharset, keep: CharPredicate): String = render(charset, keep) +} diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/JavaUri.scala b/akka-http-core/src/main/scala/akka/http/impl/model/JavaUri.scala index 6fe329a5d1..7a947e80d2 100644 --- a/akka-http-core/src/main/scala/akka/http/impl/model/JavaUri.scala +++ b/akka-http-core/src/main/scala/akka/http/impl/model/JavaUri.scala @@ -4,7 +4,9 @@ package akka.http.impl.model -import java.{ util ⇒ ju, lang ⇒ jl } +import java.nio.charset.Charset +import java.{ lang ⇒ jl } +import akka.http.scaladsl.model.Uri.ParsingMode import akka.japi.Option import akka.http.javadsl.{ model ⇒ jm } import akka.http.scaladsl.{ model ⇒ sm } @@ -23,27 +25,21 @@ case class JavaUri(uri: sm.Uri) extends jm.Uri { def path(): String = uri.path.toString - import collection.JavaConverters._ def pathSegments(): jl.Iterable[String] = { - import sm.Uri.Path - import Path._ - def gatherSegments(path: Path): List[String] = path match { + import sm.Uri.Path._ + def gatherSegments(path: sm.Uri.Path): List[String] = path match { case Empty ⇒ Nil case Segment(head, tail) ⇒ head :: gatherSegments(tail) case Slash(tail) ⇒ gatherSegments(tail) } + import collection.JavaConverters._ gatherSegments(uri.path).asJava } - def queryString(): String = uri.query.toString - - def parameterMap(): ju.Map[String, String] = uri.query.toMap.asJava - def parameters(): jl.Iterable[Param] = - uri.query.map { case (k, v) ⇒ new ju.AbstractMap.SimpleImmutableEntry(k, v): Param }.toIterable.asJava - def containsParameter(key: String): Boolean = uri.query.get(key).isDefined - def parameter(key: String): Option[String] = uri.query.get(key) - - type Param = ju.Map.Entry[String, String] + def rawQueryString: Option[String] = uri.rawQueryString + def queryString(charset: Charset): Option[String] = uri.queryString(charset) + def query: jm.Query = uri.query().asJava + def query(charset: Charset, mode: ParsingMode): jm.Query = uri.query(charset, mode).asJava def fragment: Option[String] = uri.fragment @@ -62,16 +58,13 @@ case class JavaUri(uri: sm.Uri) extends jm.Uri { def toRelative: jm.Uri = t(_.toRelative) - def query(query: String): jm.Uri = t(_.withQuery(query)) - def addParameter(key: String, value: String): jm.Uri = t { u ⇒ - u.withQuery(((key -> value) +: u.query.reverse).reverse) - } + def rawQueryString(rawQuery: String): jm.Uri = t(_.withRawQueryString(rawQuery)) + def query(query: jm.Query): jm.Uri = t(_.withQuery(query.asScala)) def addPathSegment(segment: String): jm.Uri = t { u ⇒ - import sm.Uri.Path val newPath = - if (u.path.endsWithSlash) u.path ++ Path(segment) - else u.path ++ Path./(segment) + if (u.path.endsWithSlash) u.path ++ sm.Uri.Path(segment) + else u.path ++ sm.Uri.Path./(segment) u.withPath(newPath) } diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/parser/UriParser.scala b/akka-http-core/src/main/scala/akka/http/impl/model/parser/UriParser.scala index c185ea4975..9bb6077899 100644 --- a/akka-http-core/src/main/scala/akka/http/impl/model/parser/UriParser.scala +++ b/akka-http-core/src/main/scala/akka/http/impl/model/parser/UriParser.scala @@ -23,7 +23,7 @@ private[http] class UriParser(val input: ParserInput, def parseAbsoluteUri(): Uri = rule(`absolute-URI` ~ EOI).run() match { - case Right(_) => create(_scheme, _userinfo, _host, _port, collapseDotSegments(_path), _query, _fragment) + case Right(_) => create(_scheme, _userinfo, _host, _port, collapseDotSegments(_path), _rawQueryString, _fragment) case Left(error) => fail(error, "absolute URI") } @@ -35,7 +35,7 @@ private[http] class UriParser(val input: ParserInput, def parseAndResolveUriReference(base: Uri): Uri = rule(`URI-reference` ~ EOI).run() match { - case Right(_) => resolve(_scheme, _userinfo, _host, _port, _path, _query, _fragment, base) + case Right(_) => resolve(_scheme, _userinfo, _host, _port, _path, _rawQueryString, _fragment, base) case Left(error) => fail(error, "URI reference") } @@ -53,7 +53,7 @@ private[http] class UriParser(val input: ParserInput, def parseQuery(): Query = rule(query ~ EOI).run() match { - case Right(_) => _query + case Right(query) => query case Left(error) => fail(error, "query") } @@ -69,7 +69,6 @@ private[http] class UriParser(val input: ParserInput, private[this] val `query-char` = uriParsingMode match { case Uri.ParsingMode.Strict ⇒ `strict-query-char` case Uri.ParsingMode.Relaxed ⇒ `relaxed-query-char` - case Uri.ParsingMode.RelaxedWithRawQuery ⇒ `raw-query-char` } private[this] val `fragment-char` = uriParsingMode match { case Uri.ParsingMode.Strict ⇒ `query-fragment-char` @@ -81,12 +80,12 @@ private[http] class UriParser(val input: ParserInput, var _host: Host = Host.Empty var _port: Int = 0 var _path: Path = Path.Empty - var _query: Query = Query.Empty + var _rawQueryString: Option[String] = None var _fragment: Option[String] = None // http://tools.ietf.org/html/rfc3986#appendix-A - def URI = rule { scheme ~ ':' ~ `hier-part` ~ optional('?' ~ query) ~ optional('#' ~ fragment) } + def URI = rule { scheme ~ ':' ~ `hier-part` ~ optional('?' ~ rawQueryString) ~ optional('#' ~ fragment) } def origin = rule { scheme ~ ':' ~ '/' ~ '/' ~ hostAndPort } @@ -100,9 +99,9 @@ private[http] class UriParser(val input: ParserInput, def `URI-reference-pushed`: Rule1[Uri] = rule { `URI-reference` ~ push(createUriReference()) } - def `absolute-URI` = rule { scheme ~ ':' ~ `hier-part` ~ optional('?' ~ query) } + def `absolute-URI` = rule { scheme ~ ':' ~ `hier-part` ~ optional('?' ~ rawQueryString) } - def `relative-ref` = rule { `relative-part` ~ optional('?' ~ query) ~ optional('#' ~ fragment) } + def `relative-ref` = rule { `relative-part` ~ optional('?' ~ rawQueryString) ~ optional('#' ~ fragment) } def `relative-part` = rule( '/' ~ '/' ~ authority ~ `path-abempty` @@ -161,7 +160,12 @@ private[http] class UriParser(val input: ParserInput, def pchar = rule { `path-segment-char` ~ appendSB() | `pct-encoded` } - def query = { + def rawQueryString = rule { + clearSB() ~ oneOrMore(`raw-query-char` ~ appendSB()) ~ run(_rawQueryString = Some(sb.toString)) | run(_rawQueryString = Some("")) + } + + // http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1 + def query: Rule1[Query] = { def part = rule( clearSBForDecoding() ~ oneOrMore('+' ~ appendSB(' ') | `query-char` ~ appendSB() | `pct-encoded`) ~ push(getDecodedString()) | push("")) @@ -176,9 +180,7 @@ private[http] class UriParser(val input: ParserInput, } } - if (uriParsingMode == Uri.ParsingMode.RelaxedWithRawQuery) rule { - clearSB() ~ oneOrMore(`query-char` ~ appendSB()) ~ run(_query = Query.Raw(sb.toString)) | run(_query = Query.Empty) - } else rule { keyValuePairs ~> (_query = _) } + rule { keyValuePairs } } def fragment = rule( @@ -201,7 +203,7 @@ private[http] class UriParser(val input: ParserInput, // http://tools.ietf.org/html/rfc7230#section-5.3 def `request-target` = rule( - `absolute-path` ~ optional('?' ~ query) // origin-form + `absolute-path` ~ optional('?' ~ rawQueryString) // origin-form | `absolute-URI` // absolute-form | authority) // authority-form or asterisk-form @@ -209,7 +211,7 @@ private[http] class UriParser(val input: ParserInput, rule(`request-target` ~ EOI).run() match { case Right(_) => val path = if (_scheme.isEmpty) _path else collapseDotSegments(_path) - create(_scheme, _userinfo, _host, _port, path, _query, _fragment) + create(_scheme, _userinfo, _host, _port, path, _rawQueryString, _fragment) case Left(error) => fail(error, "request-target") } @@ -231,7 +233,7 @@ private[http] class UriParser(val input: ParserInput, private def createUriReference(): Uri = { val path = if (_scheme.isEmpty) _path else collapseDotSegments(_path) - create(_scheme, _userinfo, _host, _port, path, _query, _fragment) + create(_scheme, _userinfo, _host, _port, path, _rawQueryString, _fragment) } } diff --git a/akka-http-core/src/main/scala/akka/http/impl/util/JavaAccessors.scala b/akka-http-core/src/main/scala/akka/http/impl/util/JavaAccessors.scala index 1ad6d44c6c..4ba783c70e 100644 --- a/akka-http-core/src/main/scala/akka/http/impl/util/JavaAccessors.scala +++ b/akka-http-core/src/main/scala/akka/http/impl/util/JavaAccessors.scala @@ -7,7 +7,6 @@ package akka.http.impl.util import java.io.File import JavaMapping.Implicits._ -import akka.http.impl.model.JavaUri import akka.http.javadsl.model._ import akka.http.scaladsl.model @@ -26,9 +25,6 @@ object JavaAccessors { /** INTERNAL API */ def HttpResponse(): HttpResponse = model.HttpResponse() - /** INTERNAL API */ - def Uri(uri: model.Uri): Uri = JavaUri(uri) - /** INTERNAL API */ def HttpEntity(contentType: ContentType, file: File): UniversalEntity = model.HttpEntity(contentType.asScala, file) 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 8e0d4cc60d..11509255d8 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 @@ -6,7 +6,6 @@ package akka.http.impl.util import java.net.InetAddress import java.{ util ⇒ ju, lang ⇒ jl } -import akka.http.scaladsl.model.ws.Message import akka.japi.Pair import akka.stream.javadsl import akka.stream.scaladsl @@ -14,7 +13,7 @@ import akka.stream.scaladsl import scala.collection.immutable import scala.reflect.ClassTag import akka.japi -import akka.http.impl.model.JavaUri +import akka.http.impl.model.{ JavaQuery, JavaUri } import akka.http.javadsl.{ model ⇒ jm } import akka.http.scaladsl.{ model ⇒ sm } @@ -30,7 +29,7 @@ private[http] trait J2SMapping[J] { private[http] object J2SMapping { implicit def fromJavaMapping[J](implicit mapping: JavaMapping[J, _]): J2SMapping[J] { type S = mapping.S } = mapping - implicit def seqMapping[J](implicit mapping: J2SMapping[J]): J2SMapping[Seq[J]] { type S = immutable.Seq[mapping.S] } = + implicit def fromJavaSeqMapping[J](implicit mapping: J2SMapping[J]): J2SMapping[Seq[J]] { type S = immutable.Seq[mapping.S] } = new J2SMapping[Seq[J]] { type S = immutable.Seq[mapping.S] def toScala(javaObject: Seq[J]): S = javaObject.map(mapping.toScala(_)).toList @@ -45,7 +44,7 @@ private[http] trait S2JMapping[S] { /** INTERNAL API */ private[http] object S2JMapping { - implicit def fromJavaMapping[S](implicit mapping: JavaMapping[_, S]): S2JMapping[S] { type J = mapping.J } = mapping + implicit def fromScalaMapping[S](implicit mapping: JavaMapping[_, S]): S2JMapping[S] { type J = mapping.J } = mapping } /** INTERNAL API */ @@ -92,7 +91,7 @@ private[http] object JavaMapping { new JavaMapping[jl.Iterable[_J], immutable.Seq[_S]] { import collection.JavaConverters._ - def toJava(scalaObject: immutable.Seq[_S]): jl.Iterable[_J] = scalaObject.map(mapping.toJava(_)).asJavaCollection + def toJava(scalaObject: immutable.Seq[_S]): jl.Iterable[_J] = scalaObject.map(mapping.toJava).asJavaCollection def toScala(javaObject: jl.Iterable[_J]): immutable.Seq[_S] = Implicits.convertSeqToScala(iterableAsScalaIterableConverter(javaObject).asScala.toSeq) } @@ -104,24 +103,24 @@ private[http] object JavaMapping { } implicit def option[_J, _S](implicit mapping: JavaMapping[_J, _S]): JavaMapping[akka.japi.Option[_J], Option[_S]] = new JavaMapping[akka.japi.Option[_J], Option[_S]] { - def toScala(javaObject: japi.Option[_J]): Option[_S] = javaObject.asScala.map(mapping.toScala(_)) - def toJava(scalaObject: Option[_S]): japi.Option[_J] = japi.Option.fromScalaOption(scalaObject.map(mapping.toJava(_))) + def toScala(javaObject: japi.Option[_J]): Option[_S] = javaObject.asScala.map(mapping.toScala) + 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(_)) + 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.fromGraph { - scaladsl.Flow[JIn].map(inMapping.toScala(_)).viaMat(scalaObject)(scaladsl.Keep.right).map(outMapping.toJava(_)) + scaladsl.Flow[JIn].map(inMapping.toScala).viaMat(scalaObject)(scaladsl.Keep.right).map(outMapping.toJava) } } def scalaToJavaAdapterFlow[J, S](implicit mapping: JavaMapping[J, S]): scaladsl.Flow[S, J, Unit] = - scaladsl.Flow[S].map(mapping.toJava(_)) + scaladsl.Flow[S].map(mapping.toJava) def javaToScalaAdapterFlow[J, S](implicit mapping: JavaMapping[J, S]): scaladsl.Flow[J, S, Unit] = - scaladsl.Flow[J].map(mapping.toScala(_)) + scaladsl.Flow[J].map(mapping.toScala) def adapterBidiFlow[JIn, SIn, SOut, JOut](implicit inMapping: JavaMapping[JIn, SIn], outMapping: JavaMapping[JOut, SOut]): scaladsl.BidiFlow[JIn, SIn, SOut, JOut, Unit] = scaladsl.BidiFlow.fromFlowsMat(javaToScalaAdapterFlow(inMapping), scalaToJavaAdapterFlow(outMapping))(scaladsl.Keep.none) @@ -132,8 +131,8 @@ private[http] object JavaMapping { } implicit def tryMapping[_J, _S](implicit mapping: JavaMapping[_J, _S]): JavaMapping[Try[_J], Try[_S]] = new JavaMapping[Try[_J], Try[_S]] { - def toScala(javaObject: Try[_J]): S = javaObject.map(mapping.toScala(_)) - def toJava(scalaObject: Try[_S]): J = scalaObject.map(mapping.toJava(_)) + def toScala(javaObject: Try[_J]): S = javaObject.map(mapping.toScala) + def toJava(scalaObject: Try[_S]): J = scalaObject.map(mapping.toJava) } implicit object StringIdentity extends Identity[String] @@ -198,12 +197,17 @@ private[http] object JavaMapping { 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) + def toJava(scalaObject: S): 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) + def toScala(javaObject: J): Uri.S = cast[JavaUri](javaObject).uri + def toJava(scalaObject: S): Uri.J = JavaUri(scalaObject) + } + + implicit object Query extends JavaMapping[jm.Query, sm.Uri.Query] { + def toScala(javaObject: J): Query.S = cast[JavaQuery](javaObject).query + def toJava(scalaObject: S): Query.J = JavaQuery(scalaObject) } private def cast[T](obj: AnyRef)(implicit classTag: ClassTag[T]): T = diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/FormData.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/FormData.scala index 6ad16b3d9c..811e7ad2b0 100644 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/FormData.scala +++ b/akka-http-core/src/main/scala/akka/http/scaladsl/model/FormData.scala @@ -13,12 +13,11 @@ import akka.http.scaladsl.model.MediaTypes._ /** * Simple model for `application/x-www-form-urlencoded` form data. */ -final case class FormData(fields: Uri.Query) extends jm.FormData { - override def toEntity: akka.http.scaladsl.model.RequestEntity = +final case class FormData(fields: Uri.Query) { + def toEntity: akka.http.scaladsl.model.RequestEntity = toEntity(HttpCharsets.`UTF-8`) def toEntity(charset: HttpCharset): akka.http.scaladsl.model.RequestEntity = { - // TODO this logic is duplicated in javadsl.model.FormData, spent hours trying to DRY it but compiler freaked out in a number of ways... -- ktoso val render: StringRendering = UriRendering.renderQuery(new StringRendering, this.fields, charset.nioCharset, CharacterClasses.unreserved) HttpEntity(ContentType(`application/x-www-form-urlencoded`, `UTF-8`), render.get) } @@ -32,7 +31,4 @@ object FormData { def apply(fields: (String, String)*): FormData = if (fields.isEmpty) Empty else FormData(Uri.Query(fields: _*)) - - def create(fields: Array[akka.japi.Pair[String, String]]): FormData = - if (fields.isEmpty) Empty else FormData(Uri.Query(fields.map(_.toScala): _*)) } 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 7a7cedaac6..ddfb94032f 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 @@ -5,7 +5,6 @@ package akka.http.scaladsl.model import java.lang.{ Iterable ⇒ JIterable } -import akka.http.impl.util import scala.concurrent.duration.FiniteDuration import scala.concurrent.{ Future, ExecutionContext } @@ -14,7 +13,6 @@ import scala.reflect.{ classTag, ClassTag } import akka.parboiled2.CharUtils import akka.stream.Materializer import akka.util.ByteString -import akka.http.impl.model.JavaUri import akka.http.impl.util._ import akka.http.javadsl.{ model ⇒ jm } import akka.http.scaladsl.util.FastFuture._ @@ -267,10 +265,11 @@ final case class HttpRequest(method: HttpMethod = HttpMethods.GET, override def withUri(path: String): HttpRequest = withUri(Uri(path)) def withUri(uri: Uri): HttpRequest = copy(uri = uri) + import JavaMapping.Implicits._ /** Java API */ - override def getUri: jm.Uri = util.JavaAccessors.Uri(uri) + override def getUri: jm.Uri = uri.asJava /** Java API */ - override def withUri(relativeUri: akka.http.javadsl.model.Uri): HttpRequest = copy(uri = relativeUri.asInstanceOf[JavaUri].uri) + override def withUri(uri: jm.Uri): HttpRequest = copy(uri = uri.asScala) } object HttpRequest { diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/Uri.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/Uri.scala index 3186783306..9ee95ea011 100644 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/Uri.scala +++ b/akka-http-core/src/main/scala/akka/http/scaladsl/model/Uri.scala @@ -22,13 +22,26 @@ import Uri._ * An immutable model of an internet URI as defined by http://tools.ietf.org/html/rfc3986. * All members of this class represent the *decoded* URI elements (i.e. without percent-encoding). */ -sealed abstract case class Uri(scheme: String, authority: Authority, path: Path, query: Query, +sealed abstract case class Uri(scheme: String, authority: Authority, path: Path, rawQueryString: Option[String], fragment: Option[String]) { def isAbsolute: Boolean = !isRelative def isRelative: Boolean = scheme.isEmpty def isEmpty: Boolean + /** + * Parses the rawQueryString member into a Query instance. + */ + def query(charset: Charset = UTF8, mode: Uri.ParsingMode = Uri.ParsingMode.Relaxed): Query = rawQueryString match { + case Some(q) ⇒ new UriParser(q, charset, mode).parseQuery() + case None ⇒ Query.Empty + } + + /** + * Returns the query part of the Uri in its decoded form. + */ + def queryString(charset: Charset = UTF8): Option[String] = rawQueryString.map(s ⇒ decode(s, charset)) + /** * The effective port of this Uri given the currently set authority and scheme values. * If the authority has an explicitly set port (i.e. a non-zero port value) then this port @@ -40,8 +53,8 @@ sealed abstract case class Uri(scheme: String, authority: Authority, path: Path, * Returns a copy of this Uri with the given components. */ def copy(scheme: String = scheme, authority: Authority = authority, path: Path = path, - query: Query = query, fragment: Option[String] = fragment): Uri = - Uri(scheme, authority, path, query, fragment) + rawQueryString: Option[String] = rawQueryString, fragment: Option[String] = fragment): Uri = + Uri(scheme, authority, path, rawQueryString, fragment) /** * Returns a copy of this Uri with the given scheme. The `scheme` change of the Uri has the following @@ -95,22 +108,12 @@ sealed abstract case class Uri(scheme: String, authority: Authority, path: Path, /** * Returns a copy of this Uri with the given query. */ - def withQuery(query: Query): Uri = copy(query = query) + def withQuery(query: Query): Uri = copy(rawQueryString = if (query.isEmpty) None else Some(query.toString)) /** * Returns a copy of this Uri with a Query created using the given query string. */ - def withQuery(query: String): Uri = copy(query = Query(query)) - - /** - * Returns a copy of this Uri with a Query created using the given (key, value) tuples. - */ - def withQuery(kvp: (String, String)*): Uri = copy(query = Query(kvp: _*)) - - /** - * Returns a copy of this Uri with a Query created using the given map. - */ - def withQuery(map: Map[String, String]): Uri = copy(query = Query(map)) + def withRawQueryString(rawQuery: String): Uri = copy(rawQueryString = Some(rawQuery)) /** * Returns a copy of this Uri with the given fragment. @@ -123,7 +126,7 @@ sealed abstract case class Uri(scheme: String, authority: Authority, path: Path, * The given base Uri must be absolute. */ def resolvedAgainst(base: Uri): Uri = - resolve(scheme, authority.userinfo, authority.host, authority.port, path, query, fragment, base) + resolve(scheme, authority.userinfo, authority.host, authority.port, path, rawQueryString, fragment, base) /** * Converts this URI to an "effective HTTP request URI" as defined by @@ -131,14 +134,14 @@ sealed abstract case class Uri(scheme: String, authority: Authority, path: Path, */ def toEffectiveHttpRequestUri(hostHeaderHost: Host, hostHeaderPort: Int, securedConnection: Boolean = false, defaultAuthority: Authority = Authority.Empty): Uri = - effectiveHttpRequestUri(scheme, authority.host, authority.port, path, query, fragment, securedConnection, + effectiveHttpRequestUri(scheme, authority.host, authority.port, path, rawQueryString, fragment, securedConnection, hostHeaderHost, hostHeaderPort, defaultAuthority) /** * Converts this URI into a relative URI by keeping the path, query and fragment, but dropping the scheme and authority. */ def toRelative = - Uri(path = if (path.isEmpty) Uri.Path./ else path, query = query, fragment = fragment) + Uri(path = if (path.isEmpty) Uri.Path./ else path, queryString = rawQueryString, fragment = fragment) /** * Converts this URI into an HTTP request target "origin-form" as defined by @@ -148,7 +151,7 @@ sealed abstract case class Uri(scheme: String, authority: Authority, path: Path, * be a "relative" URI with a part component starting with a double slash.) */ def toHttpRequestTargetOriginForm = - create("", Authority.Empty, if (path.isEmpty) Uri.Path./ else path, query, None) + create("", Authority.Empty, if (path.isEmpty) Uri.Path./ else path, rawQueryString, None) /** * Drops the fragment from this URI @@ -160,7 +163,7 @@ sealed abstract case class Uri(scheme: String, authority: Authority, path: Path, } object Uri { - object Empty extends Uri("", Authority.Empty, Path.Empty, Query.Empty, None) { + object Empty extends Uri("", Authority.Empty, Path.Empty, None, None) { def isEmpty = true } @@ -210,13 +213,13 @@ object Uri { * http://tools.ietf.org/html/rfc3986 the method throws an `IllegalUriException`. */ def apply(scheme: String = "", authority: Authority = Authority.Empty, path: Path = Path.Empty, - query: Query = Query.Empty, fragment: Option[String] = None): Uri = { + queryString: Option[String] = None, fragment: Option[String] = None): Uri = { val p = verifyPath(path, scheme, authority.host) create( scheme = normalizeScheme(scheme), authority = authority.normalizedFor(scheme), path = if (scheme.isEmpty) p else collapseDotSegments(p), - query = query, + queryString = queryString, fragment = fragment) } @@ -227,9 +230,9 @@ object Uri { * http://tools.ietf.org/html/rfc3986 the method throws an `IllegalUriException`. */ def from(scheme: String = "", userinfo: String = "", host: String = "", port: Int = 0, path: String = "", - query: Query = Query.Empty, fragment: Option[String] = None, + queryString: Option[String] = None, fragment: Option[String] = None, mode: Uri.ParsingMode = Uri.ParsingMode.Relaxed): Uri = - apply(scheme, Authority(Host(host, UTF8, mode), normalizePort(port, scheme), userinfo), Path(path), query, fragment) + apply(scheme, Authority(Host(host, UTF8, mode), normalizePort(port, scheme), userinfo), Path(path), queryString, fragment) /** * Parses a string into a normalized absolute URI as defined by http://tools.ietf.org/html/rfc3986#section-4.3. @@ -279,7 +282,7 @@ object Uri { * Converts a set of URI components to an "effective HTTP request URI" as defined by * http://tools.ietf.org/html/rfc7230#section-5.5. */ - def effectiveHttpRequestUri(scheme: String, host: Host, port: Int, path: Path, query: Query, fragment: Option[String], + def effectiveHttpRequestUri(scheme: String, host: Host, port: Int, path: Path, query: Option[String], fragment: Option[String], securedConnection: Boolean, hostHeaderHost: Host, hostHeaderPort: Int, defaultAuthority: Authority = Authority.Empty): Uri = { var _scheme = scheme @@ -324,7 +327,7 @@ object Uri { def inetAddresses: immutable.Seq[InetAddress] def equalsIgnoreCase(other: Host): Boolean - override def toString() = UriRendering.HostRenderer.render(new StringRendering, this).get + override def toString = UriRendering.HostRenderer.render(new StringRendering, this).get // default implementations def isNamedHost: Boolean = false @@ -520,7 +523,6 @@ object Uri { sealed abstract class Query extends LinearSeq[(String, String)] with LinearSeqOptimized[(String, String), Query] { def key: String def value: String - def isRaw: Boolean def +:(kvp: (String, String)) = Query.Cons(kvp._1, kvp._2, this) def get(key: String): Option[String] = { @tailrec def g(q: Query): Option[String] = if (q.isEmpty) None else if (q.key == key) Some(q.value) else g(q.tail) @@ -556,18 +558,19 @@ object Uri { * Parses the given String into a Query instance. * Note that this method will never return Query.Empty, even for the empty String. * Empty strings will be parsed to `("", "") +: Query.Empty` - * If you want to allow for Query.Empty creation use the apply overload taking an `Option[String`. + * If you want to allow for Query.Empty creation use the apply overload taking an `Option[String]`. */ - def apply(string: String, charset: Charset = UTF8, mode: Uri.ParsingMode = Uri.ParsingMode.Relaxed): Query = - new UriParser(string, charset, mode).parseQuery() - def apply(input: Option[String]): Query = apply(input, Uri.ParsingMode.Relaxed) - def apply(input: Option[String], mode: Uri.ParsingMode): Query = input match { + def apply(string: String): Query = apply(string: ParserInput, UTF8, Uri.ParsingMode.Relaxed) + def apply(input: ParserInput, charset: Charset = UTF8, mode: Uri.ParsingMode = Uri.ParsingMode.Relaxed): Query = + new UriParser(input, charset, mode).parseQuery() + def apply(input: Option[String]): Query = apply(input, UTF8, Uri.ParsingMode.Relaxed) + def apply(input: Option[String], charset: Charset, mode: Uri.ParsingMode): Query = input match { case None ⇒ Query.Empty - case Some(string) ⇒ apply(string, mode = mode) + case Some(string) ⇒ apply(string, charset, mode) } - def apply(kvp: (String, String)*): Query = - kvp.foldRight(Query.Empty: Query) { case ((key, value), acc) ⇒ Cons(key, value, acc) } - def apply(map: Map[String, String]): Query = apply(map.toSeq: _*) + def apply(params: (String, String)*): Query = + params.foldRight(Query.Empty: Query) { case ((key, value), acc) ⇒ Cons(key, value, acc) } + def apply(params: Map[String, String]): Query = apply(params.toSeq: _*) def newBuilder: mutable.Builder[(String, String), Query] = new mutable.Builder[(String, String), Query] { val b = mutable.ArrayBuffer.newBuilder[(String, String)] @@ -579,23 +582,14 @@ object Uri { case object Empty extends Query { def key = throw new NoSuchElementException("key of empty path") def value = throw new NoSuchElementException("value of empty path") - def isRaw = true override def isEmpty = true override def head = throw new NoSuchElementException("head of empty list") override def tail = throw new UnsupportedOperationException("tail of empty query") } final case class Cons(key: String, value: String, override val tail: Query) extends Query { - def isRaw = false override def isEmpty = false override def head = (key, value) } - final case class Raw(value: String) extends Query { - def key = "" - def isRaw = true - override def isEmpty = false - override def head = ("", value) - override def tail = Empty - } } val defaultPorts: Map[String, Int] = @@ -608,25 +602,23 @@ object Uri { object ParsingMode { case object Strict extends ParsingMode case object Relaxed extends ParsingMode - case object RelaxedWithRawQuery extends ParsingMode def apply(string: String): ParsingMode = string match { - case "strict" ⇒ Strict - case "relaxed" ⇒ Relaxed - case "relaxed-with-raw-query" ⇒ RelaxedWithRawQuery - case x ⇒ throw new IllegalArgumentException(x + " is not a legal UriParsingMode") + case "strict" ⇒ Strict + case "relaxed" ⇒ Relaxed + case x ⇒ throw new IllegalArgumentException(x + " is not a legal UriParsingMode") } } // http://tools.ietf.org/html/rfc3986#section-5.2.2 - private[http] def resolve(scheme: String, userinfo: String, host: Host, port: Int, path: Path, query: Query, + private[http] def resolve(scheme: String, userinfo: String, host: Host, port: Int, path: Path, query: Option[String], fragment: Option[String], base: Uri): Uri = { require(base.isAbsolute, "Resolution base Uri must be absolute") if (scheme.isEmpty) if (host.isEmpty) if (path.isEmpty) { - val q = if (query.isEmpty) base.query else query + val q = if (query.isEmpty) base.rawQueryString else query create(base.scheme, base.authority, base.path, q, fragment) } else { // http://tools.ietf.org/html/rfc3986#section-5.2.3 @@ -747,14 +739,14 @@ object Uri { private[http] def fail(summary: String, detail: String = "") = throw IllegalUriException(summary, detail) - private[http] def create(scheme: String, userinfo: String, host: Host, port: Int, path: Path, query: Query, + private[http] def create(scheme: String, userinfo: String, host: Host, port: Int, path: Path, queryString: Option[String], fragment: Option[String]): Uri = - create(scheme, Authority(host, normalizePort(port, scheme), userinfo), path, query, fragment) + create(scheme, Authority(host, normalizePort(port, scheme), userinfo), path, queryString, fragment) - private[http] def create(scheme: String, authority: Authority, path: Path, query: Query, + private[http] def create(scheme: String, authority: Authority, path: Path, queryString: Option[String], fragment: Option[String]): Uri = - if (path.isEmpty && scheme.isEmpty && authority.isEmpty && query.isEmpty && fragment.isEmpty) Empty - else new Uri(scheme, authority, path, query, fragment) { def isEmpty = false } + if (path.isEmpty && scheme.isEmpty && authority.isEmpty && queryString.isEmpty && fragment.isEmpty) Empty + else new Uri(scheme, authority, path, queryString, fragment) { def isEmpty = false } } object UriRendering { @@ -803,7 +795,8 @@ object UriRendering { if (isAbsolute) r ~~ scheme ~~ ':' renderAuthority(r, authority, path, scheme, charset) renderPath(r, path, charset, encodeFirstSegmentColons = isRelative) - if (query.nonEmpty) renderQuery(r ~~ '?', query, charset) else r + rawQueryString.foreach(r ~~ '?' ~~ _) + r } def renderAuthority[R <: Rendering](r: R, authority: Authority, scheme: String, charset: Charset): r.type = @@ -842,7 +835,6 @@ object UriRendering { if (value ne Query.EmptyValue) r ~~ '=' enc(value) append(tail) - case Query.Raw(value) ⇒ r ~~ value } append(query) } @@ -879,11 +871,19 @@ abstract class UriJavaAccessor * INTERNAL API. */ object UriJavaAccessor { + import collection.JavaConverters._ + def hostApply(string: String): Host = Uri.Host(string) - def hostApply(string: String, charset: Charset): Host = Uri.Host(string, charset) - def hostApply(string: String, charset: Charset, pm: Uri.ParsingMode): Host = Uri.Host(string, charset, pm) + def hostApply(string: String, mode: Uri.ParsingMode): Host = Uri.Host(string, mode = mode) + def hostApply(string: String, charset: Charset): Host = Uri.Host(string, charset = charset) def emptyHost: Uri.Host = Uri.Host.Empty + + def queryApply(params: Array[akka.japi.Pair[String, String]]): Uri.Query = Uri.Query(params.map(_.toScala): _*) + def queryApply(params: java.util.Map[String, String]): Uri.Query = Uri.Query(params.asScala.toSeq: _*) + def queryApply(string: String, mode: Uri.ParsingMode): Uri.Query = Uri.Query(string, mode = mode) + def queryApply(string: String, charset: Charset): Uri.Query = Uri.Query(string, charset = charset) + def emptyQuery: Uri.Query = Uri.Query.Empty + def pmStrict: Uri.ParsingMode = Uri.ParsingMode.Strict def pmRelaxed: Uri.ParsingMode = Uri.ParsingMode.Relaxed - def pmRelaxedWithRawQuery: Uri.ParsingMode = Uri.ParsingMode.RelaxedWithRawQuery } diff --git a/akka-http-core/src/test/java/akka/http/javadsl/model/JavaApiTestCases.java b/akka-http-core/src/test/java/akka/http/javadsl/model/JavaApiTestCases.java index 1b09a0123a..f33b9d0675 100644 --- a/akka-http-core/src/test/java/akka/http/javadsl/model/JavaApiTestCases.java +++ b/akka-http-core/src/test/java/akka/http/javadsl/model/JavaApiTestCases.java @@ -7,6 +7,7 @@ package akka.http.javadsl.model; import akka.http.impl.util.Util; import akka.http.javadsl.model.headers.Authorization; import akka.http.javadsl.model.headers.HttpCredentials; +import akka.japi.Pair; public class JavaApiTestCases { /** Builds a request for use on the client side */ @@ -22,7 +23,7 @@ public class JavaApiTestCases { if (request.method() == HttpMethods.GET) { Uri uri = request.getUri(); if (uri.path().equals("/hello")) { - String name = Util.getOrElse(uri.parameter("name"), "Mister X"); + String name = Util.getOrElse(uri.query().get("name"), "Mister X"); return HttpResponse.create() @@ -56,14 +57,14 @@ public class JavaApiTestCases { /** Build a uri to send a form */ public static Uri createUriForOrder(String orderId, String price, String amount) { - return Uri.create() - .path("/order") - .addParameter("orderId", orderId) - .addParameter("price", price) - .addParameter("amount", amount); + return Uri.create("/order").query( + Query.create( + Pair.create("orderId", orderId), + Pair.create("price", price), + Pair.create("amount", amount))); } - public static Uri addSessionId(Uri uri) { - return uri.addParameter("session", "abcdefghijkl"); + public static Query addSessionId(Query query) { + return query.withParam("session", "abcdefghijkl"); } } diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/parsing/RequestParserSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/parsing/RequestParserSpec.scala index ad2ed0f687..3548d0c162 100644 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/parsing/RequestParserSpec.scala +++ b/akka-http-core/src/test/scala/akka/http/impl/engine/parsing/RequestParserSpec.scala @@ -25,7 +25,7 @@ import com.typesafe.config.{ Config, ConfigFactory } import org.scalatest.matchers.Matcher import org.scalatest.{ BeforeAndAfterAll, FreeSpec, Matchers } -import scala.concurrent.{ Await, Future } +import scala.concurrent.Future import scala.concurrent.duration._ class RequestParserSpec extends FreeSpec with Matchers with BeforeAndAfterAll { @@ -271,7 +271,7 @@ class RequestParserSpec extends FreeSpec with Matchers with BeforeAndAfterAll { |""" should parseTo( HttpRequest( GET, - "/foobar?q=baz", + "/foobar?q=b%61z", List( `Raw-Request-URI`("/f%6f%6fbar?q=b%61z"), Host("ping")), 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 2021d5833e..c8385d855d 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 @@ -576,8 +576,8 @@ class HttpHeaderSpec extends FreeSpec with Matchers { } "parse with custom uri parsing mode" in { - val targetUri = Uri("http://example.org/?abc=def=ghi", Uri.ParsingMode.RelaxedWithRawQuery) - HeaderParser.parseFull("location", "http://example.org/?abc=def=ghi", HeaderParser.Settings(uriParsingMode = Uri.ParsingMode.RelaxedWithRawQuery)) shouldEqual + val targetUri = Uri("http://example.org/?abc=def=ghi", Uri.ParsingMode.Relaxed) + HeaderParser.parseFull("location", "http://example.org/?abc=def=ghi", HeaderParser.Settings(uriParsingMode = Uri.ParsingMode.Relaxed)) shouldEqual Right(Location(targetUri)) } } diff --git a/akka-http-core/src/test/scala/akka/http/javadsl/model/JavaApiSpec.scala b/akka-http-core/src/test/scala/akka/http/javadsl/model/JavaApiSpec.scala index 4e78f685ca..2d0591840a 100644 --- a/akka-http-core/src/test/scala/akka/http/javadsl/model/JavaApiSpec.scala +++ b/akka-http-core/src/test/scala/akka/http/javadsl/model/JavaApiSpec.scala @@ -4,6 +4,8 @@ package akka.http.javadsl.model +import akka.japi.Pair + import org.scalatest.{ FreeSpec, MustMatchers } import scala.collection.JavaConverters._ @@ -11,9 +13,9 @@ import scala.collection.JavaConverters._ class JavaApiSpec extends FreeSpec with MustMatchers { "The Java API should work for" - { "work with Uris" - { - "addParameter" in { + "query" in { Uri.create("/abc") - .addParameter("name", "paul") must be(Uri.create("/abc?name=paul")) + .query(Query.create(Pair.create("name", "paul"))) must be(Uri.create("/abc?name=paul")) } "addSegment" in { Uri.create("/abc") @@ -38,33 +40,23 @@ class JavaApiSpec extends FreeSpec with MustMatchers { } "access parameterMap" in { Uri.create("/abc?name=blub&age=28") - .parameterMap().asScala must contain allOf ("name" -> "blub", "age" -> "28") + .query().toMap.asScala must contain allOf ("name" -> "blub", "age" -> "28") } "access parameters" in { val Seq(param1, param2, param3) = Uri.create("/abc?name=blub&age=28&name=blub2") - .parameters.asScala.toSeq + .query().toList.asScala.map(_.toScala) - param1.getKey must be("name") - param1.getValue must be("blub") - - param2.getKey must be("age") - param2.getValue must be("28") - - param3.getKey must be("name") - param3.getValue must be("blub2") - } - "containsParameter" in { - val uri = Uri.create("/abc?name=blub") - uri.containsParameter("name") must be(true) - uri.containsParameter("age") must be(false) + param1 must be("name" -> "blub") + param2 must be("age" -> "28") + param3 must be("name" -> "blub2") } "access single parameter" in { - val uri = Uri.create("/abc?name=blub") - uri.parameter("name") must be(akka.japi.Option.some("blub")) - uri.parameter("age") must be(akka.japi.Option.none) + val query = Uri.create("/abc?name=blub").query() + query.get("name") must be(akka.japi.Option.some("blub")) + query.get("age") must be(akka.japi.Option.none) - Uri.create("/abc?name=blub&name=blib").parameter("name") must be(akka.japi.Option.some("blub")) + Uri.create("/abc?name=blub&name=blib").query.get("name") must be(akka.japi.Option.some("blub")) } } } diff --git a/akka-http-core/src/test/scala/akka/http/javadsl/model/JavaApiTestCaseSpecs.scala b/akka-http-core/src/test/scala/akka/http/javadsl/model/JavaApiTestCaseSpecs.scala index 621de1a806..d01daf2a9d 100644 --- a/akka-http-core/src/test/scala/akka/http/javadsl/model/JavaApiTestCaseSpecs.scala +++ b/akka-http-core/src/test/scala/akka/http/javadsl/model/JavaApiTestCaseSpecs.scala @@ -55,8 +55,8 @@ class JavaApiTestCaseSpecs extends FreeSpec with MustMatchers { Uri.create("/order?orderId=123&price=149&amount=42")) } "addSessionId" in { - val origin = Uri.create("/order?orderId=123") - JavaApiTestCases.addSessionId(origin) must be(Uri.create("/order?orderId=123&session=abcdefghijkl")) + val orderId = Query.create("orderId=123") + Uri.create("/order").query(JavaApiTestCases.addSessionId(orderId)) must be(Uri.create("/order?orderId=123&session=abcdefghijkl")) } "create HttpsContext" in { import akka.japi.{ Option ⇒ JOption } diff --git a/akka-http-core/src/test/scala/akka/http/scaladsl/model/UriSpec.scala b/akka-http-core/src/test/scala/akka/http/scaladsl/model/UriSpec.scala index e49b19baa4..4c9e1d6e50 100644 --- a/akka-http-core/src/test/scala/akka/http/scaladsl/model/UriSpec.scala +++ b/akka-http-core/src/test/scala/akka/http/scaladsl/model/UriSpec.scala @@ -199,9 +199,9 @@ class UriSpec extends WordSpec with Matchers { def roundTripTo(p: Path, cs: Charset = UTF8) = Matcher[String] { s ⇒ val rendering = UriRendering.renderPath(new StringRendering, p, cs).get - if (rendering != s) MatchResult(false, s"The path rendered to '$rendering' rather than '$s'", "") - else if (Path(s, cs) != p) MatchResult(false, s"The string parsed to '${Path(s, cs)}' rather than '$p'", "") - else MatchResult(true, "", "") + if (rendering != s) MatchResult(matches = false, s"The path rendered to '$rendering' rather than '$s'", "") + else if (Path(s, cs) != p) MatchResult(matches = false, s"The string parsed to '${Path(s, cs)}' rather than '$p'", "") + else MatchResult(matches = true, "", "") } "" should roundTripTo(Empty) @@ -273,28 +273,29 @@ class UriSpec extends WordSpec with Matchers { "Uri.Query instances" should { def parser(mode: Uri.ParsingMode): String ⇒ Query = Query(_, mode = mode) - "be parsed and rendered correctly in strict mode" in { - val test = parser(Uri.ParsingMode.Strict) - test("") shouldEqual ("", "") +: Query.Empty - test("a") shouldEqual ("a", "") +: Query.Empty - test("a=") shouldEqual ("a", "") +: Query.Empty - test("=a") shouldEqual ("", "a") +: Query.Empty - test("a&") shouldEqual ("a", "") +: ("", "") +: Query.Empty - a[IllegalUriException] should be thrownBy test("a^=b") + "be parsed correctly in strict mode" in { + val strict = parser(Uri.ParsingMode.Strict) + strict("") shouldEqual ("", "") +: Query.Empty + strict("a") shouldEqual ("a", "") +: Query.Empty + strict("a=") shouldEqual ("a", "") +: Query.Empty + strict("a=+") shouldEqual ("a", " ") +: Query.Empty + strict("a=%2B") shouldEqual ("a", "+") +: Query.Empty + strict("=a") shouldEqual ("", "a") +: Query.Empty + strict("a&") shouldEqual ("a", "") +: ("", "") +: Query.Empty + strict("a=%62") shouldEqual ("a", "b") +: Query.Empty + a[IllegalUriException] should be thrownBy strict("a^=b") } - "be parsed and rendered correctly in relaxed mode" in { - val test = parser(Uri.ParsingMode.Relaxed) - test("") shouldEqual ("", "") +: Query.Empty - test("a") shouldEqual ("a", "") +: Query.Empty - test("a=") shouldEqual ("a", "") +: Query.Empty - test("=a") shouldEqual ("", "a") +: Query.Empty - test("a&") shouldEqual ("a", "") +: ("", "") +: Query.Empty - test("a^=b") shouldEqual ("a^", "b") +: Query.Empty - } - "be parsed and rendered correctly in relaxed-with-raw-query mode" in { - val test = parser(Uri.ParsingMode.RelaxedWithRawQuery) - test("a^=b&c").toString shouldEqual "a^=b&c" - test("a%2Fb") shouldEqual Uri.Query.Raw("a%2Fb") + "be parsed correctly in relaxed mode" in { + val relaxed = parser(Uri.ParsingMode.Relaxed) + relaxed("") shouldEqual ("", "") +: Query.Empty + relaxed("a") shouldEqual ("a", "") +: Query.Empty + relaxed("a=") shouldEqual ("a", "") +: Query.Empty + relaxed("a=+") shouldEqual ("a", " ") +: Query.Empty + relaxed("a=%2B") shouldEqual ("a", "+") +: Query.Empty + relaxed("=a") shouldEqual ("", "a") +: Query.Empty + relaxed("a&") shouldEqual ("a", "") +: ("", "") +: Query.Empty + relaxed("a=%62") shouldEqual ("a", "b") +: Query.Empty + relaxed("a^=b") shouldEqual ("a^", "b") +: Query.Empty } "properly support the retrieval interface" in { val query = Query("a=1&b=2&c=3&b=4&b") @@ -341,7 +342,7 @@ class UriSpec extends WordSpec with Matchers { Uri.from(scheme = "http", host = "www.ietf.org", path = "/rfc/rfc2396.txt") Uri("ldap://[2001:db8::7]/c=GB?objectClass?one") shouldEqual - Uri.from(scheme = "ldap", host = "[2001:db8::7]", path = "/c=GB", query = Query("objectClass?one")) + Uri.from(scheme = "ldap", host = "[2001:db8::7]", path = "/c=GB", queryString = Some("objectClass?one")) Uri("mailto:John.Doe@example.com") shouldEqual Uri.from(scheme = "mailto", path = "John.Doe@example.com") @@ -360,15 +361,16 @@ class UriSpec extends WordSpec with Matchers { // more examples Uri("http://") shouldEqual Uri(scheme = "http", authority = Authority(Host.Empty)) - Uri("http:?") shouldEqual Uri.from(scheme = "http", query = Query("")) - Uri("?a+b=c%2Bd") shouldEqual Uri.from(query = ("a b", "c+d") +: Query.Empty) + Uri("http:?") shouldEqual Uri.from(scheme = "http", queryString = Some("")) + Uri("http:") shouldEqual Uri.from(scheme = "http", queryString = None) + Uri("?a+b=c%2Bd").query() shouldEqual ("a b", "c+d") +: Query.Empty // illegal paths Uri("foo/another@url/[]and{}") shouldEqual Uri.from(path = "foo/another@url/%5B%5Dand%7B%7D") a[IllegalUriException] should be thrownBy Uri("foo/another@url/[]and{}", mode = Uri.ParsingMode.Strict) // handle query parameters with more than percent-encoded character - Uri("?%7Ba%7D=$%7B%7D", UTF8, Uri.ParsingMode.Strict) shouldEqual Uri(query = Query.Cons("{a}", "${}", Query.Empty)) + Uri("?%7Ba%7D=$%7B%7D", UTF8, Uri.ParsingMode.Strict).query() shouldEqual Query.Cons("{a}", "${}", Query.Empty) // don't double decode Uri("%2520").path.head shouldEqual "%20" @@ -430,13 +432,12 @@ class UriSpec extends WordSpec with Matchers { normalize("eXAMPLE://a/./b/../b/%63/{foo}/[bar]") shouldEqual "example://a/b/c/%7Bfoo%7D/%5Bbar%5D" a[IllegalUriException] should be thrownBy normalize("eXAMPLE://a/./b/../b/%63/{foo}/[bar]", mode = Uri.ParsingMode.Strict) - // queries + // queries and fragments normalize("?") shouldEqual "?" normalize("?key") shouldEqual "?key" normalize("?key=") shouldEqual "?key=" normalize("?key=&a=b") shouldEqual "?key=&a=b" - normalize("?key={}&a=[]") shouldEqual "?key=%7B%7D&a=%5B%5D" - a[IllegalUriException] should be thrownBy normalize("?key={}&a=[]", mode = Uri.ParsingMode.Strict) + normalize("?key={}&a=[]") shouldEqual "?key={}&a=[]" normalize("?=value") shouldEqual "?=value" normalize("?key=value") shouldEqual "?key=value" normalize("?a+b") shouldEqual "?a+b" @@ -456,9 +457,9 @@ class UriSpec extends WordSpec with Matchers { "support tunneling a URI through a query param" in { val uri = Uri("http://aHost/aPath?aParam=aValue#aFragment") val q = Query("uri" -> uri.toString) - val uri2 = Uri(path = Path./, query = q, fragment = Some("aFragment")).toString + val uri2 = Uri(path = Path./, fragment = Some("aFragment")).withQuery(q).toString uri2 shouldEqual "/?uri=http://ahost/aPath?aParam%3DaValue%23aFragment#aFragment" - Uri(uri2).query shouldEqual q + Uri(uri2).query() shouldEqual q Uri(q.getOrElse("uri", "")) shouldEqual uri } @@ -499,10 +500,10 @@ class UriSpec extends WordSpec with Matchers { } // illegal query - the[IllegalUriException] thrownBy Uri("?a=b=c") shouldBe { - IllegalUriException("Illegal URI reference: Invalid input '=', expected '+', query-char, 'EOI', '#', '&' or pct-encoded (line 1, column 5)", - "?a=b=c\n" + - " ^") + the[IllegalUriException] thrownBy Uri("?a=b=c").query() shouldBe { + IllegalUriException("Illegal query: Invalid input '=', expected '+', query-char, 'EOI', '&' or pct-encoded (line 1, column 4)", + "a=b=c\n" + + " ^") } } @@ -588,9 +589,8 @@ class UriSpec extends WordSpec with Matchers { uri.withUserInfo("someInfo") shouldEqual Uri("http://someInfo@host:80/path?query#fragment") uri.withQuery(Query("param1" -> "value1")) shouldEqual Uri("http://host:80/path?param1=value1#fragment") - uri.withQuery("param1=value1") shouldEqual Uri("http://host:80/path?param1=value1#fragment") - uri.withQuery(("param1", "value1")) shouldEqual Uri("http://host:80/path?param1=value1#fragment") - uri.withQuery(Map("param1" -> "value1")) shouldEqual Uri("http://host:80/path?param1=value1#fragment") + uri.withQuery(Query(Map("param1" -> "value1"))) shouldEqual Uri("http://host:80/path?param1=value1#fragment") + uri.withRawQueryString("param1=value1") shouldEqual Uri("http://host:80/path?param1=value1#fragment") uri.withFragment("otherFragment") shouldEqual Uri("http://host:80/path?query#otherFragment") } diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/ParameterDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/ParameterDirectives.scala index 5fa526d06a..a716952439 100644 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/ParameterDirectives.scala +++ b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/ParameterDirectives.scala @@ -57,13 +57,13 @@ object ParameterDirectives extends ParameterDirectives { import BasicDirectives._ private val _parameterMap: Directive1[Map[String, String]] = - extract(_.request.uri.query.toMap) + extract(_.request.uri.query().toMap) private val _parameterMultiMap: Directive1[Map[String, List[String]]] = - extract(_.request.uri.query.toMultiMap) + extract(_.request.uri.query().toMultiMap) private val _parameterSeq: Directive1[immutable.Seq[(String, String)]] = - extract(_.request.uri.query.toSeq) + extract(_.request.uri.query().toSeq) sealed trait ParamMagnet { type Out @@ -109,7 +109,7 @@ object ParameterDirectives extends ParameterDirectives { extractRequestContext flatMap { ctx ⇒ import ctx.executionContext import ctx.materializer - handleParamResult(paramName, fsou(ctx.request.uri.query get paramName)) + handleParamResult(paramName, fsou(ctx.request.uri.query().get(paramName))) } implicit def forString(implicit fsu: FSU[String]): ParamDefAux[String, Directive1[String]] = extractParameter[String, String] { string ⇒ filter(string, fsu) } @@ -134,7 +134,7 @@ object ParameterDirectives extends ParameterDirectives { extractRequestContext flatMap { ctx ⇒ import ctx.executionContext import ctx.materializer - onComplete(fsou(ctx.request.uri.query get paramName)) flatMap { + onComplete(fsou(ctx.request.uri.query().get(paramName))) flatMap { case Success(value) if value == requiredValue ⇒ pass case _ ⇒ reject } @@ -150,7 +150,7 @@ object ParameterDirectives extends ParameterDirectives { extractRequestContext flatMap { ctx ⇒ import ctx.executionContext import ctx.materializer - handleParamResult(paramName, Future.sequence(ctx.request.uri.query.getAll(paramName).map(fsu.apply))) + handleParamResult(paramName, Future.sequence(ctx.request.uri.query().getAll(paramName).map(fsu.apply))) } implicit def forRepVR[T](implicit fsu: FSU[T]): ParamDefAux[RepeatedValueReceptacle[T], Directive1[Iterable[T]]] = extractParameter[RepeatedValueReceptacle[T], Iterable[T]] { rvr ⇒ repeatedFilter(rvr.name, fsu) }