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) }