!htp #18479 defer parsing of query key-value-pairs
This commit is contained in:
parent
1378fedad0
commit
8f75c97e47
25 changed files with 398 additions and 275 deletions
|
|
@ -4,20 +4,13 @@
|
||||||
|
|
||||||
package docs.http.javadsl.server;
|
package docs.http.javadsl.server;
|
||||||
|
|
||||||
import akka.http.javadsl.model.ContentTypes;
|
|
||||||
import akka.http.javadsl.model.FormData;
|
import akka.http.javadsl.model.FormData;
|
||||||
import akka.http.javadsl.model.HttpRequest;
|
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.Route;
|
||||||
import akka.http.javadsl.server.values.FormField;
|
import akka.http.javadsl.server.values.FormField;
|
||||||
import akka.http.javadsl.server.values.FormFields;
|
import akka.http.javadsl.server.values.FormFields;
|
||||||
import akka.http.javadsl.server.values.Headers;
|
|
||||||
import akka.http.javadsl.testkit.JUnitRouteTest;
|
import akka.http.javadsl.testkit.JUnitRouteTest;
|
||||||
import akka.japi.Pair;
|
import akka.japi.Pair;
|
||||||
import docs.http.scaladsl.server.directives.Person;
|
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class FormFieldRequestValsExampleTest extends JUnitRouteTest {
|
public class FormFieldRequestValsExampleTest extends JUnitRouteTest {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ package docs.http.javadsl.server;
|
||||||
|
|
||||||
import akka.actor.ActorSystem;
|
import akka.actor.ActorSystem;
|
||||||
import akka.dispatch.OnFailure;
|
import akka.dispatch.OnFailure;
|
||||||
import akka.http.impl.util.JavaMapping;
|
|
||||||
import akka.http.impl.util.Util;
|
import akka.http.impl.util.Util;
|
||||||
import akka.http.javadsl.Http;
|
import akka.http.javadsl.Http;
|
||||||
import akka.http.javadsl.IncomingConnection;
|
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.HttpRequest;
|
||||||
import akka.http.javadsl.model.HttpResponse;
|
import akka.http.javadsl.model.HttpResponse;
|
||||||
import akka.http.javadsl.model.Uri;
|
import akka.http.javadsl.model.Uri;
|
||||||
import akka.http.scaladsl.model.*;
|
|
||||||
import akka.http.scaladsl.model.HttpEntity;
|
import akka.http.scaladsl.model.HttpEntity;
|
||||||
import akka.japi.JavaPartialFunction;
|
|
||||||
import akka.japi.function.Function;
|
import akka.japi.function.Function;
|
||||||
import akka.japi.function.Procedure;
|
import akka.japi.function.Procedure;
|
||||||
import akka.stream.ActorMaterializer;
|
import akka.stream.ActorMaterializer;
|
||||||
|
|
@ -32,7 +29,6 @@ import akka.stream.stage.PushStage;
|
||||||
import akka.stream.stage.SyncDirective;
|
import akka.stream.stage.SyncDirective;
|
||||||
import akka.stream.stage.TerminationDirective;
|
import akka.stream.stage.TerminationDirective;
|
||||||
import akka.util.ByteString;
|
import akka.util.ByteString;
|
||||||
import scala.Function1;
|
|
||||||
import scala.concurrent.Await;
|
import scala.concurrent.Await;
|
||||||
import scala.concurrent.Future;
|
import scala.concurrent.Future;
|
||||||
import scala.concurrent.duration.FiniteDuration;
|
import scala.concurrent.duration.FiniteDuration;
|
||||||
|
|
@ -206,7 +202,7 @@ public class HttpServerExampleDocTest {
|
||||||
.withEntity(ContentTypes.TEXT_HTML,
|
.withEntity(ContentTypes.TEXT_HTML,
|
||||||
"<html><body>Hello world!</body></html>");
|
"<html><body>Hello world!</body></html>");
|
||||||
else if (uri.path().equals("/hello")) {
|
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
|
return
|
||||||
HttpResponse.create()
|
HttpResponse.create()
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,7 @@ class BasicDirectivesExamplesSpec extends RoutingSpec {
|
||||||
"textract" in {
|
"textract" in {
|
||||||
val pathAndQuery = textract { ctx =>
|
val pathAndQuery = textract { ctx =>
|
||||||
val uri = ctx.request.uri
|
val uri = ctx.request.uri
|
||||||
(uri.path, uri.query)
|
(uri.path, uri.query())
|
||||||
}
|
}
|
||||||
val route =
|
val route =
|
||||||
pathAndQuery { (p, query) =>
|
pathAndQuery { (p, query) =>
|
||||||
|
|
|
||||||
|
|
@ -4,34 +4,51 @@
|
||||||
|
|
||||||
package akka.http.javadsl.model;
|
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 akka.japi.Pair;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple model for `application/x-www-form-urlencoded` form data.
|
* 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() {
|
public RequestEntity toEntity() {
|
||||||
return toEntity(HttpCharsets.UTF_8);
|
return toEntity(HttpCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts this FormData to a RequestEntity using the given encoding.
|
||||||
|
*/
|
||||||
public RequestEntity toEntity(HttpCharset charset) {
|
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
|
return HttpEntities.create(ContentType.create(MediaTypes.APPLICATION_X_WWW_FORM_URLENCODED, charset), fields.render(charset));
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns empty FormData.
|
||||||
|
*/
|
||||||
|
public static final FormData EMPTY = new FormData(Query.EMPTY);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the FormData from the given parameters.
|
||||||
|
*/
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
public static FormData create(Pair<String, String>... fields) {
|
public static FormData create(Pair<String, String>... params) {
|
||||||
return akka.http.scaladsl.model.FormData.create(fields);
|
return new FormData(Query.create(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the FormData from the given parameters.
|
||||||
|
*/
|
||||||
|
public static FormData create(Map<String, String> params) {
|
||||||
|
return new FormData(Query.create(params));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,14 +47,14 @@ public abstract class Host {
|
||||||
/**
|
/**
|
||||||
* Parse the given Host string using the given charset and the default parsing-mode.
|
* Parse the given Host string using the given charset and the default parsing-mode.
|
||||||
*/
|
*/
|
||||||
public static Host create(String string, Charset charset) {
|
public static Host create(String string, Uri.ParsingMode parsingMode) {
|
||||||
return UriJavaAccessor.hostApply(string, charset);
|
return UriJavaAccessor.hostApply(string, parsingMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the given Host string using the given charset and parsing-mode.
|
* Parse the given Host string using the given charset and parsing-mode.
|
||||||
*/
|
*/
|
||||||
public static Host create(String string, Charset charset, Uri.ParsingMode parsingMode) {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
package akka.http.javadsl.model;
|
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
|
* Represents a charset in Http. See {@link HttpCharsets} for a set of predefined charsets and
|
||||||
* static constructors to create custom charsets.
|
* static constructors to create custom charsets.
|
||||||
|
|
@ -37,4 +39,9 @@ public abstract class HttpCharset {
|
||||||
* Returns the predefined alias names for this charset.
|
* Returns the predefined alias names for this charset.
|
||||||
*/
|
*/
|
||||||
public abstract Iterable<String> getAliases();
|
public abstract Iterable<String> getAliases();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Charset for this charset if available or throws an exception otherwise.
|
||||||
|
*/
|
||||||
|
public abstract Charset nioCharset();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
package akka.http.javadsl.model;
|
package akka.http.javadsl.model;
|
||||||
|
|
||||||
import akka.japi.Option;
|
import akka.japi.Option;
|
||||||
import akka.stream.javadsl.Source;
|
|
||||||
import akka.util.ByteString;
|
import akka.util.ByteString;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
|
||||||
108
akka-http-core/src/main/java/akka/http/javadsl/model/Query.java
Normal file
108
akka-http-core/src/main/java/akka/http/javadsl/model/Query.java
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
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<String> 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<String> 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<Pair<String, String>> 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<String, String> toMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a `Map` of all parameters of this Query. Use the `toMap()`
|
||||||
|
* method to filter out entries with duplicated keys.
|
||||||
|
*/
|
||||||
|
public abstract Map<String, List<String>> 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<String, String>... params) {
|
||||||
|
return new JavaQuery(UriJavaAccessor.queryApply(params));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a Query from the given parameters.
|
||||||
|
*/
|
||||||
|
public static Query create(Map<String, String> params) {
|
||||||
|
return new JavaQuery(UriJavaAccessor.queryApply(params));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,12 +4,14 @@
|
||||||
|
|
||||||
package akka.http.javadsl.model;
|
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.http.scaladsl.model.UriJavaAccessor;
|
||||||
import akka.japi.Option;
|
import akka.japi.Option;
|
||||||
|
import akka.japi.Pair;
|
||||||
import akka.parboiled2.ParserInput$;
|
import akka.parboiled2.ParserInput$;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -62,32 +64,24 @@ public abstract class Uri {
|
||||||
public abstract Iterable<String> pathSegments();
|
public abstract Iterable<String> 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<String> 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<String> parameter(String key);
|
public abstract Option<String> 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()`
|
* Returns the parsed Query instance of this Uri using the given charset and parsing mode.
|
||||||
* method to filter out entries with duplicated keys.
|
|
||||||
*/
|
*/
|
||||||
public abstract Iterable<Map.Entry<String, String>> parameters();
|
public abstract Query query(Charset charset, akka.http.scaladsl.model.Uri.ParsingMode mode);
|
||||||
|
|
||||||
/**
|
|
||||||
* 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<String, String> parameterMap();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the fragment part of this Uri.
|
* 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.
|
* 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.
|
* Returns a copy of this instance that is relative.
|
||||||
*/
|
*/
|
||||||
public abstract Uri toRelative();
|
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.
|
* 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 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 = 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.
|
* Creates a default Uri to be modified using the modification methods.
|
||||||
*/
|
*/
|
||||||
public static Uri create() {
|
public static final Uri EMPTY = new JavaUri(akka.http.scaladsl.model.Uri.Empty$.MODULE$);
|
||||||
return JavaAccessors$.MODULE$.Uri(akka.http.scaladsl.model.Uri.Empty$.MODULE$);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a Uri created by parsing the given string representation.
|
* Returns a Uri created by parsing the given string representation.
|
||||||
*/
|
*/
|
||||||
public static Uri create(String uri) {
|
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) {
|
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) {
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -277,10 +277,6 @@ akka.http {
|
||||||
#
|
#
|
||||||
# `relaxed`: all visible 7-Bit ASCII chars are allowed
|
# `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
|
uri-parsing-mode = strict
|
||||||
|
|
||||||
# Enables/disables the logging of warning messages in case an incoming
|
# Enables/disables the logging of warning messages in case an incoming
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,9 @@
|
||||||
|
|
||||||
package akka.http.impl.model
|
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.japi.Option
|
||||||
import akka.http.javadsl.{ model ⇒ jm }
|
import akka.http.javadsl.{ model ⇒ jm }
|
||||||
import akka.http.scaladsl.{ model ⇒ sm }
|
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
|
def path(): String = uri.path.toString
|
||||||
|
|
||||||
import collection.JavaConverters._
|
|
||||||
def pathSegments(): jl.Iterable[String] = {
|
def pathSegments(): jl.Iterable[String] = {
|
||||||
import sm.Uri.Path
|
import sm.Uri.Path._
|
||||||
import Path._
|
def gatherSegments(path: sm.Uri.Path): List[String] = path match {
|
||||||
def gatherSegments(path: Path): List[String] = path match {
|
|
||||||
case Empty ⇒ Nil
|
case Empty ⇒ Nil
|
||||||
case Segment(head, tail) ⇒ head :: gatherSegments(tail)
|
case Segment(head, tail) ⇒ head :: gatherSegments(tail)
|
||||||
case Slash(tail) ⇒ gatherSegments(tail)
|
case Slash(tail) ⇒ gatherSegments(tail)
|
||||||
}
|
}
|
||||||
|
import collection.JavaConverters._
|
||||||
gatherSegments(uri.path).asJava
|
gatherSegments(uri.path).asJava
|
||||||
}
|
}
|
||||||
|
|
||||||
def queryString(): String = uri.query.toString
|
def rawQueryString: Option[String] = uri.rawQueryString
|
||||||
|
def queryString(charset: Charset): Option[String] = uri.queryString(charset)
|
||||||
def parameterMap(): ju.Map[String, String] = uri.query.toMap.asJava
|
def query: jm.Query = uri.query().asJava
|
||||||
def parameters(): jl.Iterable[Param] =
|
def query(charset: Charset, mode: ParsingMode): jm.Query = uri.query(charset, mode).asJava
|
||||||
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 fragment: Option[String] = uri.fragment
|
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 toRelative: jm.Uri = t(_.toRelative)
|
||||||
|
|
||||||
def query(query: String): jm.Uri = t(_.withQuery(query))
|
def rawQueryString(rawQuery: String): jm.Uri = t(_.withRawQueryString(rawQuery))
|
||||||
def addParameter(key: String, value: String): jm.Uri = t { u ⇒
|
def query(query: jm.Query): jm.Uri = t(_.withQuery(query.asScala))
|
||||||
u.withQuery(((key -> value) +: u.query.reverse).reverse)
|
|
||||||
}
|
|
||||||
|
|
||||||
def addPathSegment(segment: String): jm.Uri = t { u ⇒
|
def addPathSegment(segment: String): jm.Uri = t { u ⇒
|
||||||
import sm.Uri.Path
|
|
||||||
val newPath =
|
val newPath =
|
||||||
if (u.path.endsWithSlash) u.path ++ Path(segment)
|
if (u.path.endsWithSlash) u.path ++ sm.Uri.Path(segment)
|
||||||
else u.path ++ Path./(segment)
|
else u.path ++ sm.Uri.Path./(segment)
|
||||||
|
|
||||||
u.withPath(newPath)
|
u.withPath(newPath)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ private[http] class UriParser(val input: ParserInput,
|
||||||
|
|
||||||
def parseAbsoluteUri(): Uri =
|
def parseAbsoluteUri(): Uri =
|
||||||
rule(`absolute-URI` ~ EOI).run() match {
|
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")
|
case Left(error) => fail(error, "absolute URI")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,7 +35,7 @@ private[http] class UriParser(val input: ParserInput,
|
||||||
|
|
||||||
def parseAndResolveUriReference(base: Uri): Uri =
|
def parseAndResolveUriReference(base: Uri): Uri =
|
||||||
rule(`URI-reference` ~ EOI).run() match {
|
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")
|
case Left(error) => fail(error, "URI reference")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,7 +53,7 @@ private[http] class UriParser(val input: ParserInput,
|
||||||
|
|
||||||
def parseQuery(): Query =
|
def parseQuery(): Query =
|
||||||
rule(query ~ EOI).run() match {
|
rule(query ~ EOI).run() match {
|
||||||
case Right(_) => _query
|
case Right(query) => query
|
||||||
case Left(error) => fail(error, "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 {
|
private[this] val `query-char` = uriParsingMode match {
|
||||||
case Uri.ParsingMode.Strict ⇒ `strict-query-char`
|
case Uri.ParsingMode.Strict ⇒ `strict-query-char`
|
||||||
case Uri.ParsingMode.Relaxed ⇒ `relaxed-query-char`
|
case Uri.ParsingMode.Relaxed ⇒ `relaxed-query-char`
|
||||||
case Uri.ParsingMode.RelaxedWithRawQuery ⇒ `raw-query-char`
|
|
||||||
}
|
}
|
||||||
private[this] val `fragment-char` = uriParsingMode match {
|
private[this] val `fragment-char` = uriParsingMode match {
|
||||||
case Uri.ParsingMode.Strict ⇒ `query-fragment-char`
|
case Uri.ParsingMode.Strict ⇒ `query-fragment-char`
|
||||||
|
|
@ -81,12 +80,12 @@ private[http] class UriParser(val input: ParserInput,
|
||||||
var _host: Host = Host.Empty
|
var _host: Host = Host.Empty
|
||||||
var _port: Int = 0
|
var _port: Int = 0
|
||||||
var _path: Path = Path.Empty
|
var _path: Path = Path.Empty
|
||||||
var _query: Query = Query.Empty
|
var _rawQueryString: Option[String] = None
|
||||||
var _fragment: Option[String] = None
|
var _fragment: Option[String] = None
|
||||||
|
|
||||||
// http://tools.ietf.org/html/rfc3986#appendix-A
|
// 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 }
|
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 `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(
|
def `relative-part` = rule(
|
||||||
'/' ~ '/' ~ authority ~ `path-abempty`
|
'/' ~ '/' ~ authority ~ `path-abempty`
|
||||||
|
|
@ -161,7 +160,12 @@ private[http] class UriParser(val input: ParserInput,
|
||||||
|
|
||||||
def pchar = rule { `path-segment-char` ~ appendSB() | `pct-encoded` }
|
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(
|
def part = rule(
|
||||||
clearSBForDecoding() ~ oneOrMore('+' ~ appendSB(' ') | `query-char` ~ appendSB() | `pct-encoded`) ~ push(getDecodedString())
|
clearSBForDecoding() ~ oneOrMore('+' ~ appendSB(' ') | `query-char` ~ appendSB() | `pct-encoded`) ~ push(getDecodedString())
|
||||||
| push(""))
|
| push(""))
|
||||||
|
|
@ -176,9 +180,7 @@ private[http] class UriParser(val input: ParserInput,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uriParsingMode == Uri.ParsingMode.RelaxedWithRawQuery) rule {
|
rule { keyValuePairs }
|
||||||
clearSB() ~ oneOrMore(`query-char` ~ appendSB()) ~ run(_query = Query.Raw(sb.toString)) | run(_query = Query.Empty)
|
|
||||||
} else rule { keyValuePairs ~> (_query = _) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def fragment = rule(
|
def fragment = rule(
|
||||||
|
|
@ -201,7 +203,7 @@ private[http] class UriParser(val input: ParserInput,
|
||||||
|
|
||||||
// http://tools.ietf.org/html/rfc7230#section-5.3
|
// http://tools.ietf.org/html/rfc7230#section-5.3
|
||||||
def `request-target` = rule(
|
def `request-target` = rule(
|
||||||
`absolute-path` ~ optional('?' ~ query) // origin-form
|
`absolute-path` ~ optional('?' ~ rawQueryString) // origin-form
|
||||||
| `absolute-URI` // absolute-form
|
| `absolute-URI` // absolute-form
|
||||||
| authority) // authority-form or asterisk-form
|
| authority) // authority-form or asterisk-form
|
||||||
|
|
||||||
|
|
@ -209,7 +211,7 @@ private[http] class UriParser(val input: ParserInput,
|
||||||
rule(`request-target` ~ EOI).run() match {
|
rule(`request-target` ~ EOI).run() match {
|
||||||
case Right(_) =>
|
case Right(_) =>
|
||||||
val path = if (_scheme.isEmpty) _path else collapseDotSegments(_path)
|
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")
|
case Left(error) => fail(error, "request-target")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -231,7 +233,7 @@ private[http] class UriParser(val input: ParserInput,
|
||||||
|
|
||||||
private def createUriReference(): Uri = {
|
private def createUriReference(): Uri = {
|
||||||
val path = if (_scheme.isEmpty) _path else collapseDotSegments(_path)
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ package akka.http.impl.util
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
import JavaMapping.Implicits._
|
import JavaMapping.Implicits._
|
||||||
import akka.http.impl.model.JavaUri
|
|
||||||
import akka.http.javadsl.model._
|
import akka.http.javadsl.model._
|
||||||
import akka.http.scaladsl.model
|
import akka.http.scaladsl.model
|
||||||
|
|
||||||
|
|
@ -26,9 +25,6 @@ object JavaAccessors {
|
||||||
/** INTERNAL API */
|
/** INTERNAL API */
|
||||||
def HttpResponse(): HttpResponse = model.HttpResponse()
|
def HttpResponse(): HttpResponse = model.HttpResponse()
|
||||||
|
|
||||||
/** INTERNAL API */
|
|
||||||
def Uri(uri: model.Uri): Uri = JavaUri(uri)
|
|
||||||
|
|
||||||
/** INTERNAL API */
|
/** INTERNAL API */
|
||||||
def HttpEntity(contentType: ContentType, file: File): UniversalEntity =
|
def HttpEntity(contentType: ContentType, file: File): UniversalEntity =
|
||||||
model.HttpEntity(contentType.asScala, file)
|
model.HttpEntity(contentType.asScala, file)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ package akka.http.impl.util
|
||||||
|
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
import java.{ util ⇒ ju, lang ⇒ jl }
|
import java.{ util ⇒ ju, lang ⇒ jl }
|
||||||
import akka.http.scaladsl.model.ws.Message
|
|
||||||
import akka.japi.Pair
|
import akka.japi.Pair
|
||||||
import akka.stream.javadsl
|
import akka.stream.javadsl
|
||||||
import akka.stream.scaladsl
|
import akka.stream.scaladsl
|
||||||
|
|
@ -14,7 +13,7 @@ import akka.stream.scaladsl
|
||||||
import scala.collection.immutable
|
import scala.collection.immutable
|
||||||
import scala.reflect.ClassTag
|
import scala.reflect.ClassTag
|
||||||
import akka.japi
|
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.javadsl.{ model ⇒ jm }
|
||||||
import akka.http.scaladsl.{ model ⇒ sm }
|
import akka.http.scaladsl.{ model ⇒ sm }
|
||||||
|
|
||||||
|
|
@ -30,7 +29,7 @@ private[http] trait J2SMapping[J] {
|
||||||
private[http] object J2SMapping {
|
private[http] object J2SMapping {
|
||||||
implicit def fromJavaMapping[J](implicit mapping: JavaMapping[J, _]): J2SMapping[J] { type S = mapping.S } = mapping
|
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]] {
|
new J2SMapping[Seq[J]] {
|
||||||
type S = immutable.Seq[mapping.S]
|
type S = immutable.Seq[mapping.S]
|
||||||
def toScala(javaObject: Seq[J]): S = javaObject.map(mapping.toScala(_)).toList
|
def toScala(javaObject: Seq[J]): S = javaObject.map(mapping.toScala(_)).toList
|
||||||
|
|
@ -45,7 +44,7 @@ private[http] trait S2JMapping[S] {
|
||||||
|
|
||||||
/** INTERNAL API */
|
/** INTERNAL API */
|
||||||
private[http] object S2JMapping {
|
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 */
|
/** INTERNAL API */
|
||||||
|
|
@ -92,7 +91,7 @@ private[http] object JavaMapping {
|
||||||
new JavaMapping[jl.Iterable[_J], immutable.Seq[_S]] {
|
new JavaMapping[jl.Iterable[_J], immutable.Seq[_S]] {
|
||||||
import collection.JavaConverters._
|
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] =
|
def toScala(javaObject: jl.Iterable[_J]): immutable.Seq[_S] =
|
||||||
Implicits.convertSeqToScala(iterableAsScalaIterableConverter(javaObject).asScala.toSeq)
|
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]] =
|
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]] {
|
new JavaMapping[akka.japi.Option[_J], Option[_S]] {
|
||||||
def toScala(javaObject: japi.Option[_J]): Option[_S] = javaObject.asScala.map(mapping.toScala(_))
|
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 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]] =
|
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]] {
|
new JavaMapping[javadsl.Flow[JIn, JOut, M], scaladsl.Flow[SIn, SOut, M]] {
|
||||||
def toScala(javaObject: javadsl.Flow[JIn, JOut, M]): S =
|
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 =
|
def toJava(scalaObject: scaladsl.Flow[SIn, SOut, M]): J =
|
||||||
javadsl.Flow.fromGraph {
|
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] =
|
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] =
|
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] =
|
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)
|
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]] =
|
implicit def tryMapping[_J, _S](implicit mapping: JavaMapping[_J, _S]): JavaMapping[Try[_J], Try[_S]] =
|
||||||
new JavaMapping[Try[_J], Try[_S]] {
|
new JavaMapping[Try[_J], Try[_S]] {
|
||||||
def toScala(javaObject: Try[_J]): S = javaObject.map(mapping.toScala(_))
|
def toScala(javaObject: Try[_J]): S = javaObject.map(mapping.toScala)
|
||||||
def toJava(scalaObject: Try[_S]): J = scalaObject.map(mapping.toJava(_))
|
def toJava(scalaObject: Try[_S]): J = scalaObject.map(mapping.toJava)
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit object StringIdentity extends Identity[String]
|
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] {
|
implicit object WsMessage extends JavaMapping[jm.ws.Message, sm.ws.Message] {
|
||||||
def toScala(javaObject: J): WsMessage.S = javaObject.asScala
|
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] {
|
implicit object Uri extends JavaMapping[jm.Uri, sm.Uri] {
|
||||||
def toScala(javaObject: jm.Uri): Uri.S = cast[JavaUri](javaObject).uri
|
def toScala(javaObject: J): Uri.S = cast[JavaUri](javaObject).uri
|
||||||
def toJava(scalaObject: sm.Uri): Uri.J = JavaAccessors.Uri(scalaObject)
|
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 =
|
private def cast[T](obj: AnyRef)(implicit classTag: ClassTag[T]): T =
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,11 @@ import akka.http.scaladsl.model.MediaTypes._
|
||||||
/**
|
/**
|
||||||
* Simple model for `application/x-www-form-urlencoded` form data.
|
* Simple model for `application/x-www-form-urlencoded` form data.
|
||||||
*/
|
*/
|
||||||
final case class FormData(fields: Uri.Query) extends jm.FormData {
|
final case class FormData(fields: Uri.Query) {
|
||||||
override def toEntity: akka.http.scaladsl.model.RequestEntity =
|
def toEntity: akka.http.scaladsl.model.RequestEntity =
|
||||||
toEntity(HttpCharsets.`UTF-8`)
|
toEntity(HttpCharsets.`UTF-8`)
|
||||||
|
|
||||||
def toEntity(charset: HttpCharset): akka.http.scaladsl.model.RequestEntity = {
|
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)
|
val render: StringRendering = UriRendering.renderQuery(new StringRendering, this.fields, charset.nioCharset, CharacterClasses.unreserved)
|
||||||
HttpEntity(ContentType(`application/x-www-form-urlencoded`, `UTF-8`), render.get)
|
HttpEntity(ContentType(`application/x-www-form-urlencoded`, `UTF-8`), render.get)
|
||||||
}
|
}
|
||||||
|
|
@ -32,7 +31,4 @@ object FormData {
|
||||||
|
|
||||||
def apply(fields: (String, String)*): FormData =
|
def apply(fields: (String, String)*): FormData =
|
||||||
if (fields.isEmpty) Empty else FormData(Uri.Query(fields: _*))
|
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): _*))
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
package akka.http.scaladsl.model
|
package akka.http.scaladsl.model
|
||||||
|
|
||||||
import java.lang.{ Iterable ⇒ JIterable }
|
import java.lang.{ Iterable ⇒ JIterable }
|
||||||
import akka.http.impl.util
|
|
||||||
|
|
||||||
import scala.concurrent.duration.FiniteDuration
|
import scala.concurrent.duration.FiniteDuration
|
||||||
import scala.concurrent.{ Future, ExecutionContext }
|
import scala.concurrent.{ Future, ExecutionContext }
|
||||||
|
|
@ -14,7 +13,6 @@ import scala.reflect.{ classTag, ClassTag }
|
||||||
import akka.parboiled2.CharUtils
|
import akka.parboiled2.CharUtils
|
||||||
import akka.stream.Materializer
|
import akka.stream.Materializer
|
||||||
import akka.util.ByteString
|
import akka.util.ByteString
|
||||||
import akka.http.impl.model.JavaUri
|
|
||||||
import akka.http.impl.util._
|
import akka.http.impl.util._
|
||||||
import akka.http.javadsl.{ model ⇒ jm }
|
import akka.http.javadsl.{ model ⇒ jm }
|
||||||
import akka.http.scaladsl.util.FastFuture._
|
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))
|
override def withUri(path: String): HttpRequest = withUri(Uri(path))
|
||||||
def withUri(uri: Uri): HttpRequest = copy(uri = uri)
|
def withUri(uri: Uri): HttpRequest = copy(uri = uri)
|
||||||
|
|
||||||
|
import JavaMapping.Implicits._
|
||||||
/** Java API */
|
/** Java API */
|
||||||
override def getUri: jm.Uri = util.JavaAccessors.Uri(uri)
|
override def getUri: jm.Uri = uri.asJava
|
||||||
/** Java API */
|
/** 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 {
|
object HttpRequest {
|
||||||
|
|
|
||||||
|
|
@ -22,13 +22,26 @@ import Uri._
|
||||||
* An immutable model of an internet URI as defined by http://tools.ietf.org/html/rfc3986.
|
* 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).
|
* 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]) {
|
fragment: Option[String]) {
|
||||||
|
|
||||||
def isAbsolute: Boolean = !isRelative
|
def isAbsolute: Boolean = !isRelative
|
||||||
def isRelative: Boolean = scheme.isEmpty
|
def isRelative: Boolean = scheme.isEmpty
|
||||||
def isEmpty: Boolean
|
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.
|
* 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
|
* 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.
|
* Returns a copy of this Uri with the given components.
|
||||||
*/
|
*/
|
||||||
def copy(scheme: String = scheme, authority: Authority = authority, path: Path = path,
|
def copy(scheme: String = scheme, authority: Authority = authority, path: Path = path,
|
||||||
query: Query = query, fragment: Option[String] = fragment): Uri =
|
rawQueryString: Option[String] = rawQueryString, fragment: Option[String] = fragment): Uri =
|
||||||
Uri(scheme, authority, path, query, fragment)
|
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
|
* 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.
|
* 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.
|
* Returns a copy of this Uri with a Query created using the given query string.
|
||||||
*/
|
*/
|
||||||
def withQuery(query: String): Uri = copy(query = Query(query))
|
def withRawQueryString(rawQuery: String): Uri = copy(rawQueryString = Some(rawQuery))
|
||||||
|
|
||||||
/**
|
|
||||||
* 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))
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a copy of this Uri with the given fragment.
|
* 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.
|
* The given base Uri must be absolute.
|
||||||
*/
|
*/
|
||||||
def resolvedAgainst(base: Uri): Uri =
|
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
|
* 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,
|
def toEffectiveHttpRequestUri(hostHeaderHost: Host, hostHeaderPort: Int, securedConnection: Boolean = false,
|
||||||
defaultAuthority: Authority = Authority.Empty): Uri =
|
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)
|
hostHeaderHost, hostHeaderPort, defaultAuthority)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts this URI into a relative URI by keeping the path, query and fragment, but dropping the scheme and authority.
|
* Converts this URI into a relative URI by keeping the path, query and fragment, but dropping the scheme and authority.
|
||||||
*/
|
*/
|
||||||
def toRelative =
|
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
|
* 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.)
|
* be a "relative" URI with a part component starting with a double slash.)
|
||||||
*/
|
*/
|
||||||
def toHttpRequestTargetOriginForm =
|
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
|
* Drops the fragment from this URI
|
||||||
|
|
@ -160,7 +163,7 @@ sealed abstract case class Uri(scheme: String, authority: Authority, path: Path,
|
||||||
}
|
}
|
||||||
|
|
||||||
object Uri {
|
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
|
def isEmpty = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,13 +213,13 @@ object Uri {
|
||||||
* http://tools.ietf.org/html/rfc3986 the method throws an `IllegalUriException`.
|
* http://tools.ietf.org/html/rfc3986 the method throws an `IllegalUriException`.
|
||||||
*/
|
*/
|
||||||
def apply(scheme: String = "", authority: Authority = Authority.Empty, path: Path = Path.Empty,
|
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)
|
val p = verifyPath(path, scheme, authority.host)
|
||||||
create(
|
create(
|
||||||
scheme = normalizeScheme(scheme),
|
scheme = normalizeScheme(scheme),
|
||||||
authority = authority.normalizedFor(scheme),
|
authority = authority.normalizedFor(scheme),
|
||||||
path = if (scheme.isEmpty) p else collapseDotSegments(p),
|
path = if (scheme.isEmpty) p else collapseDotSegments(p),
|
||||||
query = query,
|
queryString = queryString,
|
||||||
fragment = fragment)
|
fragment = fragment)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -227,9 +230,9 @@ object Uri {
|
||||||
* http://tools.ietf.org/html/rfc3986 the method throws an `IllegalUriException`.
|
* http://tools.ietf.org/html/rfc3986 the method throws an `IllegalUriException`.
|
||||||
*/
|
*/
|
||||||
def from(scheme: String = "", userinfo: String = "", host: String = "", port: Int = 0, path: String = "",
|
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 =
|
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.
|
* 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
|
* Converts a set of URI components to an "effective HTTP request URI" as defined by
|
||||||
* http://tools.ietf.org/html/rfc7230#section-5.5.
|
* 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,
|
securedConnection: Boolean, hostHeaderHost: Host, hostHeaderPort: Int,
|
||||||
defaultAuthority: Authority = Authority.Empty): Uri = {
|
defaultAuthority: Authority = Authority.Empty): Uri = {
|
||||||
var _scheme = scheme
|
var _scheme = scheme
|
||||||
|
|
@ -324,7 +327,7 @@ object Uri {
|
||||||
def inetAddresses: immutable.Seq[InetAddress]
|
def inetAddresses: immutable.Seq[InetAddress]
|
||||||
|
|
||||||
def equalsIgnoreCase(other: Host): Boolean
|
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
|
// default implementations
|
||||||
def isNamedHost: Boolean = false
|
def isNamedHost: Boolean = false
|
||||||
|
|
@ -520,7 +523,6 @@ object Uri {
|
||||||
sealed abstract class Query extends LinearSeq[(String, String)] with LinearSeqOptimized[(String, String), Query] {
|
sealed abstract class Query extends LinearSeq[(String, String)] with LinearSeqOptimized[(String, String), Query] {
|
||||||
def key: String
|
def key: String
|
||||||
def value: String
|
def value: String
|
||||||
def isRaw: Boolean
|
|
||||||
def +:(kvp: (String, String)) = Query.Cons(kvp._1, kvp._2, this)
|
def +:(kvp: (String, String)) = Query.Cons(kvp._1, kvp._2, this)
|
||||||
def get(key: String): Option[String] = {
|
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)
|
@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.
|
* Parses the given String into a Query instance.
|
||||||
* Note that this method will never return Query.Empty, even for the empty String.
|
* Note that this method will never return Query.Empty, even for the empty String.
|
||||||
* Empty strings will be parsed to `("", "") +: Query.Empty`
|
* 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 =
|
def apply(string: String): Query = apply(string: ParserInput, UTF8, Uri.ParsingMode.Relaxed)
|
||||||
new UriParser(string, charset, mode).parseQuery()
|
def apply(input: ParserInput, charset: Charset = UTF8, mode: Uri.ParsingMode = Uri.ParsingMode.Relaxed): Query =
|
||||||
def apply(input: Option[String]): Query = apply(input, Uri.ParsingMode.Relaxed)
|
new UriParser(input, charset, mode).parseQuery()
|
||||||
def apply(input: Option[String], mode: Uri.ParsingMode): Query = input match {
|
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 None ⇒ Query.Empty
|
||||||
case Some(string) ⇒ apply(string, mode = mode)
|
case Some(string) ⇒ apply(string, charset, mode)
|
||||||
}
|
}
|
||||||
def apply(kvp: (String, String)*): Query =
|
def apply(params: (String, String)*): Query =
|
||||||
kvp.foldRight(Query.Empty: Query) { case ((key, value), acc) ⇒ Cons(key, value, acc) }
|
params.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: Map[String, String]): Query = apply(params.toSeq: _*)
|
||||||
|
|
||||||
def newBuilder: mutable.Builder[(String, String), Query] = new mutable.Builder[(String, String), Query] {
|
def newBuilder: mutable.Builder[(String, String), Query] = new mutable.Builder[(String, String), Query] {
|
||||||
val b = mutable.ArrayBuffer.newBuilder[(String, String)]
|
val b = mutable.ArrayBuffer.newBuilder[(String, String)]
|
||||||
|
|
@ -579,23 +582,14 @@ object Uri {
|
||||||
case object Empty extends Query {
|
case object Empty extends Query {
|
||||||
def key = throw new NoSuchElementException("key of empty path")
|
def key = throw new NoSuchElementException("key of empty path")
|
||||||
def value = throw new NoSuchElementException("value of empty path")
|
def value = throw new NoSuchElementException("value of empty path")
|
||||||
def isRaw = true
|
|
||||||
override def isEmpty = true
|
override def isEmpty = true
|
||||||
override def head = throw new NoSuchElementException("head of empty list")
|
override def head = throw new NoSuchElementException("head of empty list")
|
||||||
override def tail = throw new UnsupportedOperationException("tail of empty query")
|
override def tail = throw new UnsupportedOperationException("tail of empty query")
|
||||||
}
|
}
|
||||||
final case class Cons(key: String, value: String, override val tail: Query) extends Query {
|
final case class Cons(key: String, value: String, override val tail: Query) extends Query {
|
||||||
def isRaw = false
|
|
||||||
override def isEmpty = false
|
override def isEmpty = false
|
||||||
override def head = (key, value)
|
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] =
|
val defaultPorts: Map[String, Int] =
|
||||||
|
|
@ -608,25 +602,23 @@ object Uri {
|
||||||
object ParsingMode {
|
object ParsingMode {
|
||||||
case object Strict extends ParsingMode
|
case object Strict extends ParsingMode
|
||||||
case object Relaxed extends ParsingMode
|
case object Relaxed extends ParsingMode
|
||||||
case object RelaxedWithRawQuery extends ParsingMode
|
|
||||||
|
|
||||||
def apply(string: String): ParsingMode =
|
def apply(string: String): ParsingMode =
|
||||||
string match {
|
string match {
|
||||||
case "strict" ⇒ Strict
|
case "strict" ⇒ Strict
|
||||||
case "relaxed" ⇒ Relaxed
|
case "relaxed" ⇒ Relaxed
|
||||||
case "relaxed-with-raw-query" ⇒ RelaxedWithRawQuery
|
case x ⇒ throw new IllegalArgumentException(x + " is not a legal UriParsingMode")
|
||||||
case x ⇒ throw new IllegalArgumentException(x + " is not a legal UriParsingMode")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// http://tools.ietf.org/html/rfc3986#section-5.2.2
|
// 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 = {
|
fragment: Option[String], base: Uri): Uri = {
|
||||||
require(base.isAbsolute, "Resolution base Uri must be absolute")
|
require(base.isAbsolute, "Resolution base Uri must be absolute")
|
||||||
if (scheme.isEmpty)
|
if (scheme.isEmpty)
|
||||||
if (host.isEmpty)
|
if (host.isEmpty)
|
||||||
if (path.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)
|
create(base.scheme, base.authority, base.path, q, fragment)
|
||||||
} else {
|
} else {
|
||||||
// http://tools.ietf.org/html/rfc3986#section-5.2.3
|
// 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 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 =
|
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 =
|
fragment: Option[String]): Uri =
|
||||||
if (path.isEmpty && scheme.isEmpty && authority.isEmpty && query.isEmpty && fragment.isEmpty) Empty
|
if (path.isEmpty && scheme.isEmpty && authority.isEmpty && queryString.isEmpty && fragment.isEmpty) Empty
|
||||||
else new Uri(scheme, authority, path, query, fragment) { def isEmpty = false }
|
else new Uri(scheme, authority, path, queryString, fragment) { def isEmpty = false }
|
||||||
}
|
}
|
||||||
|
|
||||||
object UriRendering {
|
object UriRendering {
|
||||||
|
|
@ -803,7 +795,8 @@ object UriRendering {
|
||||||
if (isAbsolute) r ~~ scheme ~~ ':'
|
if (isAbsolute) r ~~ scheme ~~ ':'
|
||||||
renderAuthority(r, authority, path, scheme, charset)
|
renderAuthority(r, authority, path, scheme, charset)
|
||||||
renderPath(r, path, charset, encodeFirstSegmentColons = isRelative)
|
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 =
|
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 ~~ '='
|
if (value ne Query.EmptyValue) r ~~ '='
|
||||||
enc(value)
|
enc(value)
|
||||||
append(tail)
|
append(tail)
|
||||||
case Query.Raw(value) ⇒ r ~~ value
|
|
||||||
}
|
}
|
||||||
append(query)
|
append(query)
|
||||||
}
|
}
|
||||||
|
|
@ -879,11 +871,19 @@ abstract class UriJavaAccessor
|
||||||
* INTERNAL API.
|
* INTERNAL API.
|
||||||
*/
|
*/
|
||||||
object UriJavaAccessor {
|
object UriJavaAccessor {
|
||||||
|
import collection.JavaConverters._
|
||||||
|
|
||||||
def hostApply(string: String): Host = Uri.Host(string)
|
def hostApply(string: String): Host = Uri.Host(string)
|
||||||
def hostApply(string: String, charset: Charset): Host = Uri.Host(string, charset)
|
def hostApply(string: String, mode: Uri.ParsingMode): Host = Uri.Host(string, mode = mode)
|
||||||
def hostApply(string: String, charset: Charset, pm: Uri.ParsingMode): Host = Uri.Host(string, charset, pm)
|
def hostApply(string: String, charset: Charset): Host = Uri.Host(string, charset = charset)
|
||||||
def emptyHost: Uri.Host = Uri.Host.Empty
|
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 pmStrict: Uri.ParsingMode = Uri.ParsingMode.Strict
|
||||||
def pmRelaxed: Uri.ParsingMode = Uri.ParsingMode.Relaxed
|
def pmRelaxed: Uri.ParsingMode = Uri.ParsingMode.Relaxed
|
||||||
def pmRelaxedWithRawQuery: Uri.ParsingMode = Uri.ParsingMode.RelaxedWithRawQuery
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ package akka.http.javadsl.model;
|
||||||
import akka.http.impl.util.Util;
|
import akka.http.impl.util.Util;
|
||||||
import akka.http.javadsl.model.headers.Authorization;
|
import akka.http.javadsl.model.headers.Authorization;
|
||||||
import akka.http.javadsl.model.headers.HttpCredentials;
|
import akka.http.javadsl.model.headers.HttpCredentials;
|
||||||
|
import akka.japi.Pair;
|
||||||
|
|
||||||
public class JavaApiTestCases {
|
public class JavaApiTestCases {
|
||||||
/** Builds a request for use on the client side */
|
/** Builds a request for use on the client side */
|
||||||
|
|
@ -22,7 +23,7 @@ public class JavaApiTestCases {
|
||||||
if (request.method() == HttpMethods.GET) {
|
if (request.method() == HttpMethods.GET) {
|
||||||
Uri uri = request.getUri();
|
Uri uri = request.getUri();
|
||||||
if (uri.path().equals("/hello")) {
|
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
|
return
|
||||||
HttpResponse.create()
|
HttpResponse.create()
|
||||||
|
|
@ -56,14 +57,14 @@ public class JavaApiTestCases {
|
||||||
|
|
||||||
/** Build a uri to send a form */
|
/** Build a uri to send a form */
|
||||||
public static Uri createUriForOrder(String orderId, String price, String amount) {
|
public static Uri createUriForOrder(String orderId, String price, String amount) {
|
||||||
return Uri.create()
|
return Uri.create("/order").query(
|
||||||
.path("/order")
|
Query.create(
|
||||||
.addParameter("orderId", orderId)
|
Pair.create("orderId", orderId),
|
||||||
.addParameter("price", price)
|
Pair.create("price", price),
|
||||||
.addParameter("amount", amount);
|
Pair.create("amount", amount)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Uri addSessionId(Uri uri) {
|
public static Query addSessionId(Query query) {
|
||||||
return uri.addParameter("session", "abcdefghijkl");
|
return query.withParam("session", "abcdefghijkl");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import com.typesafe.config.{ Config, ConfigFactory }
|
||||||
import org.scalatest.matchers.Matcher
|
import org.scalatest.matchers.Matcher
|
||||||
import org.scalatest.{ BeforeAndAfterAll, FreeSpec, Matchers }
|
import org.scalatest.{ BeforeAndAfterAll, FreeSpec, Matchers }
|
||||||
|
|
||||||
import scala.concurrent.{ Await, Future }
|
import scala.concurrent.Future
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
class RequestParserSpec extends FreeSpec with Matchers with BeforeAndAfterAll {
|
class RequestParserSpec extends FreeSpec with Matchers with BeforeAndAfterAll {
|
||||||
|
|
@ -271,7 +271,7 @@ class RequestParserSpec extends FreeSpec with Matchers with BeforeAndAfterAll {
|
||||||
|""" should parseTo(
|
|""" should parseTo(
|
||||||
HttpRequest(
|
HttpRequest(
|
||||||
GET,
|
GET,
|
||||||
"/foobar?q=baz",
|
"/foobar?q=b%61z",
|
||||||
List(
|
List(
|
||||||
`Raw-Request-URI`("/f%6f%6fbar?q=b%61z"),
|
`Raw-Request-URI`("/f%6f%6fbar?q=b%61z"),
|
||||||
Host("ping")),
|
Host("ping")),
|
||||||
|
|
|
||||||
|
|
@ -576,8 +576,8 @@ class HttpHeaderSpec extends FreeSpec with Matchers {
|
||||||
}
|
}
|
||||||
|
|
||||||
"parse with custom uri parsing mode" in {
|
"parse with custom uri parsing mode" in {
|
||||||
val targetUri = Uri("http://example.org/?abc=def=ghi", Uri.ParsingMode.RelaxedWithRawQuery)
|
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.RelaxedWithRawQuery)) shouldEqual
|
HeaderParser.parseFull("location", "http://example.org/?abc=def=ghi", HeaderParser.Settings(uriParsingMode = Uri.ParsingMode.Relaxed)) shouldEqual
|
||||||
Right(Location(targetUri))
|
Right(Location(targetUri))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
package akka.http.javadsl.model
|
package akka.http.javadsl.model
|
||||||
|
|
||||||
|
import akka.japi.Pair
|
||||||
|
|
||||||
import org.scalatest.{ FreeSpec, MustMatchers }
|
import org.scalatest.{ FreeSpec, MustMatchers }
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
|
|
@ -11,9 +13,9 @@ import scala.collection.JavaConverters._
|
||||||
class JavaApiSpec extends FreeSpec with MustMatchers {
|
class JavaApiSpec extends FreeSpec with MustMatchers {
|
||||||
"The Java API should work for" - {
|
"The Java API should work for" - {
|
||||||
"work with Uris" - {
|
"work with Uris" - {
|
||||||
"addParameter" in {
|
"query" in {
|
||||||
Uri.create("/abc")
|
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 {
|
"addSegment" in {
|
||||||
Uri.create("/abc")
|
Uri.create("/abc")
|
||||||
|
|
@ -38,33 +40,23 @@ class JavaApiSpec extends FreeSpec with MustMatchers {
|
||||||
}
|
}
|
||||||
"access parameterMap" in {
|
"access parameterMap" in {
|
||||||
Uri.create("/abc?name=blub&age=28")
|
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 {
|
"access parameters" in {
|
||||||
val Seq(param1, param2, param3) =
|
val Seq(param1, param2, param3) =
|
||||||
Uri.create("/abc?name=blub&age=28&name=blub2")
|
Uri.create("/abc?name=blub&age=28&name=blub2")
|
||||||
.parameters.asScala.toSeq
|
.query().toList.asScala.map(_.toScala)
|
||||||
|
|
||||||
param1.getKey must be("name")
|
param1 must be("name" -> "blub")
|
||||||
param1.getValue must be("blub")
|
param2 must be("age" -> "28")
|
||||||
|
param3 must be("name" -> "blub2")
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
"access single parameter" in {
|
"access single parameter" in {
|
||||||
val uri = Uri.create("/abc?name=blub")
|
val query = Uri.create("/abc?name=blub").query()
|
||||||
uri.parameter("name") must be(akka.japi.Option.some("blub"))
|
query.get("name") must be(akka.japi.Option.some("blub"))
|
||||||
uri.parameter("age") must be(akka.japi.Option.none)
|
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"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,8 +55,8 @@ class JavaApiTestCaseSpecs extends FreeSpec with MustMatchers {
|
||||||
Uri.create("/order?orderId=123&price=149&amount=42"))
|
Uri.create("/order?orderId=123&price=149&amount=42"))
|
||||||
}
|
}
|
||||||
"addSessionId" in {
|
"addSessionId" in {
|
||||||
val origin = Uri.create("/order?orderId=123")
|
val orderId = Query.create("orderId=123")
|
||||||
JavaApiTestCases.addSessionId(origin) must be(Uri.create("/order?orderId=123&session=abcdefghijkl"))
|
Uri.create("/order").query(JavaApiTestCases.addSessionId(orderId)) must be(Uri.create("/order?orderId=123&session=abcdefghijkl"))
|
||||||
}
|
}
|
||||||
"create HttpsContext" in {
|
"create HttpsContext" in {
|
||||||
import akka.japi.{ Option ⇒ JOption }
|
import akka.japi.{ Option ⇒ JOption }
|
||||||
|
|
|
||||||
|
|
@ -199,9 +199,9 @@ class UriSpec extends WordSpec with Matchers {
|
||||||
def roundTripTo(p: Path, cs: Charset = UTF8) =
|
def roundTripTo(p: Path, cs: Charset = UTF8) =
|
||||||
Matcher[String] { s ⇒
|
Matcher[String] { s ⇒
|
||||||
val rendering = UriRendering.renderPath(new StringRendering, p, cs).get
|
val rendering = UriRendering.renderPath(new StringRendering, p, cs).get
|
||||||
if (rendering != s) MatchResult(false, s"The path rendered to '$rendering' rather than '$s'", "<?>")
|
if (rendering != s) MatchResult(matches = 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 if (Path(s, cs) != p) MatchResult(matches = false, s"The string parsed to '${Path(s, cs)}' rather than '$p'", "<?>")
|
||||||
else MatchResult(true, "<?>", "<?>")
|
else MatchResult(matches = true, "<?>", "<?>")
|
||||||
}
|
}
|
||||||
|
|
||||||
"" should roundTripTo(Empty)
|
"" should roundTripTo(Empty)
|
||||||
|
|
@ -273,28 +273,29 @@ class UriSpec extends WordSpec with Matchers {
|
||||||
|
|
||||||
"Uri.Query instances" should {
|
"Uri.Query instances" should {
|
||||||
def parser(mode: Uri.ParsingMode): String ⇒ Query = Query(_, mode = mode)
|
def parser(mode: Uri.ParsingMode): String ⇒ Query = Query(_, mode = mode)
|
||||||
"be parsed and rendered correctly in strict mode" in {
|
"be parsed correctly in strict mode" in {
|
||||||
val test = parser(Uri.ParsingMode.Strict)
|
val strict = parser(Uri.ParsingMode.Strict)
|
||||||
test("") shouldEqual ("", "") +: Query.Empty
|
strict("") shouldEqual ("", "") +: Query.Empty
|
||||||
test("a") shouldEqual ("a", "") +: Query.Empty
|
strict("a") shouldEqual ("a", "") +: Query.Empty
|
||||||
test("a=") shouldEqual ("a", "") +: Query.Empty
|
strict("a=") shouldEqual ("a", "") +: Query.Empty
|
||||||
test("=a") shouldEqual ("", "a") +: Query.Empty
|
strict("a=+") shouldEqual ("a", " ") +: Query.Empty
|
||||||
test("a&") shouldEqual ("a", "") +: ("", "") +: Query.Empty
|
strict("a=%2B") shouldEqual ("a", "+") +: Query.Empty
|
||||||
a[IllegalUriException] should be thrownBy test("a^=b")
|
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 {
|
"be parsed correctly in relaxed mode" in {
|
||||||
val test = parser(Uri.ParsingMode.Relaxed)
|
val relaxed = parser(Uri.ParsingMode.Relaxed)
|
||||||
test("") shouldEqual ("", "") +: Query.Empty
|
relaxed("") shouldEqual ("", "") +: Query.Empty
|
||||||
test("a") shouldEqual ("a", "") +: Query.Empty
|
relaxed("a") shouldEqual ("a", "") +: Query.Empty
|
||||||
test("a=") shouldEqual ("a", "") +: Query.Empty
|
relaxed("a=") shouldEqual ("a", "") +: Query.Empty
|
||||||
test("=a") shouldEqual ("", "a") +: Query.Empty
|
relaxed("a=+") shouldEqual ("a", " ") +: Query.Empty
|
||||||
test("a&") shouldEqual ("a", "") +: ("", "") +: Query.Empty
|
relaxed("a=%2B") shouldEqual ("a", "+") +: Query.Empty
|
||||||
test("a^=b") shouldEqual ("a^", "b") +: Query.Empty
|
relaxed("=a") shouldEqual ("", "a") +: Query.Empty
|
||||||
}
|
relaxed("a&") shouldEqual ("a", "") +: ("", "") +: Query.Empty
|
||||||
"be parsed and rendered correctly in relaxed-with-raw-query mode" in {
|
relaxed("a=%62") shouldEqual ("a", "b") +: Query.Empty
|
||||||
val test = parser(Uri.ParsingMode.RelaxedWithRawQuery)
|
relaxed("a^=b") shouldEqual ("a^", "b") +: Query.Empty
|
||||||
test("a^=b&c").toString shouldEqual "a^=b&c"
|
|
||||||
test("a%2Fb") shouldEqual Uri.Query.Raw("a%2Fb")
|
|
||||||
}
|
}
|
||||||
"properly support the retrieval interface" in {
|
"properly support the retrieval interface" in {
|
||||||
val query = Query("a=1&b=2&c=3&b=4&b")
|
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.from(scheme = "http", host = "www.ietf.org", path = "/rfc/rfc2396.txt")
|
||||||
|
|
||||||
Uri("ldap://[2001:db8::7]/c=GB?objectClass?one") shouldEqual
|
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("mailto:John.Doe@example.com") shouldEqual
|
||||||
Uri.from(scheme = "mailto", path = "John.Doe@example.com")
|
Uri.from(scheme = "mailto", path = "John.Doe@example.com")
|
||||||
|
|
@ -360,15 +361,16 @@ class UriSpec extends WordSpec with Matchers {
|
||||||
|
|
||||||
// more examples
|
// more examples
|
||||||
Uri("http://") shouldEqual Uri(scheme = "http", authority = Authority(Host.Empty))
|
Uri("http://") shouldEqual Uri(scheme = "http", authority = Authority(Host.Empty))
|
||||||
Uri("http:?") shouldEqual Uri.from(scheme = "http", query = Query(""))
|
Uri("http:?") shouldEqual Uri.from(scheme = "http", queryString = Some(""))
|
||||||
Uri("?a+b=c%2Bd") shouldEqual Uri.from(query = ("a b", "c+d") +: Query.Empty)
|
Uri("http:") shouldEqual Uri.from(scheme = "http", queryString = None)
|
||||||
|
Uri("?a+b=c%2Bd").query() shouldEqual ("a b", "c+d") +: Query.Empty
|
||||||
|
|
||||||
// illegal paths
|
// illegal paths
|
||||||
Uri("foo/another@url/[]and{}") shouldEqual Uri.from(path = "foo/another@url/%5B%5Dand%7B%7D")
|
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)
|
a[IllegalUriException] should be thrownBy Uri("foo/another@url/[]and{}", mode = Uri.ParsingMode.Strict)
|
||||||
|
|
||||||
// handle query parameters with more than percent-encoded character
|
// 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
|
// don't double decode
|
||||||
Uri("%2520").path.head shouldEqual "%20"
|
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"
|
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)
|
a[IllegalUriException] should be thrownBy normalize("eXAMPLE://a/./b/../b/%63/{foo}/[bar]", mode = Uri.ParsingMode.Strict)
|
||||||
|
|
||||||
// queries
|
// queries and fragments
|
||||||
normalize("?") shouldEqual "?"
|
normalize("?") shouldEqual "?"
|
||||||
normalize("?key") shouldEqual "?key"
|
normalize("?key") shouldEqual "?key"
|
||||||
normalize("?key=") shouldEqual "?key="
|
normalize("?key=") shouldEqual "?key="
|
||||||
normalize("?key=&a=b") shouldEqual "?key=&a=b"
|
normalize("?key=&a=b") shouldEqual "?key=&a=b"
|
||||||
normalize("?key={}&a=[]") shouldEqual "?key=%7B%7D&a=%5B%5D"
|
normalize("?key={}&a=[]") shouldEqual "?key={}&a=[]"
|
||||||
a[IllegalUriException] should be thrownBy normalize("?key={}&a=[]", mode = Uri.ParsingMode.Strict)
|
|
||||||
normalize("?=value") shouldEqual "?=value"
|
normalize("?=value") shouldEqual "?=value"
|
||||||
normalize("?key=value") shouldEqual "?key=value"
|
normalize("?key=value") shouldEqual "?key=value"
|
||||||
normalize("?a+b") shouldEqual "?a+b"
|
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 {
|
"support tunneling a URI through a query param" in {
|
||||||
val uri = Uri("http://aHost/aPath?aParam=aValue#aFragment")
|
val uri = Uri("http://aHost/aPath?aParam=aValue#aFragment")
|
||||||
val q = Query("uri" -> uri.toString)
|
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"
|
uri2 shouldEqual "/?uri=http://ahost/aPath?aParam%3DaValue%23aFragment#aFragment"
|
||||||
Uri(uri2).query shouldEqual q
|
Uri(uri2).query() shouldEqual q
|
||||||
Uri(q.getOrElse("uri", "<nope>")) shouldEqual uri
|
Uri(q.getOrElse("uri", "<nope>")) shouldEqual uri
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -499,10 +500,10 @@ class UriSpec extends WordSpec with Matchers {
|
||||||
}
|
}
|
||||||
|
|
||||||
// illegal query
|
// illegal query
|
||||||
the[IllegalUriException] thrownBy Uri("?a=b=c") shouldBe {
|
the[IllegalUriException] thrownBy Uri("?a=b=c").query() shouldBe {
|
||||||
IllegalUriException("Illegal URI reference: Invalid input '=', expected '+', query-char, 'EOI', '#', '&' or pct-encoded (line 1, column 5)",
|
IllegalUriException("Illegal query: Invalid input '=', expected '+', query-char, 'EOI', '&' or pct-encoded (line 1, column 4)",
|
||||||
"?a=b=c\n" +
|
"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.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(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(Query(Map("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.withRawQueryString("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.withFragment("otherFragment") shouldEqual Uri("http://host:80/path?query#otherFragment")
|
uri.withFragment("otherFragment") shouldEqual Uri("http://host:80/path?query#otherFragment")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,13 +57,13 @@ object ParameterDirectives extends ParameterDirectives {
|
||||||
import BasicDirectives._
|
import BasicDirectives._
|
||||||
|
|
||||||
private val _parameterMap: Directive1[Map[String, String]] =
|
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]]] =
|
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)]] =
|
private val _parameterSeq: Directive1[immutable.Seq[(String, String)]] =
|
||||||
extract(_.request.uri.query.toSeq)
|
extract(_.request.uri.query().toSeq)
|
||||||
|
|
||||||
sealed trait ParamMagnet {
|
sealed trait ParamMagnet {
|
||||||
type Out
|
type Out
|
||||||
|
|
@ -109,7 +109,7 @@ object ParameterDirectives extends ParameterDirectives {
|
||||||
extractRequestContext flatMap { ctx ⇒
|
extractRequestContext flatMap { ctx ⇒
|
||||||
import ctx.executionContext
|
import ctx.executionContext
|
||||||
import ctx.materializer
|
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]] =
|
implicit def forString(implicit fsu: FSU[String]): ParamDefAux[String, Directive1[String]] =
|
||||||
extractParameter[String, String] { string ⇒ filter(string, fsu) }
|
extractParameter[String, String] { string ⇒ filter(string, fsu) }
|
||||||
|
|
@ -134,7 +134,7 @@ object ParameterDirectives extends ParameterDirectives {
|
||||||
extractRequestContext flatMap { ctx ⇒
|
extractRequestContext flatMap { ctx ⇒
|
||||||
import ctx.executionContext
|
import ctx.executionContext
|
||||||
import ctx.materializer
|
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 Success(value) if value == requiredValue ⇒ pass
|
||||||
case _ ⇒ reject
|
case _ ⇒ reject
|
||||||
}
|
}
|
||||||
|
|
@ -150,7 +150,7 @@ object ParameterDirectives extends ParameterDirectives {
|
||||||
extractRequestContext flatMap { ctx ⇒
|
extractRequestContext flatMap { ctx ⇒
|
||||||
import ctx.executionContext
|
import ctx.executionContext
|
||||||
import ctx.materializer
|
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]]] =
|
implicit def forRepVR[T](implicit fsu: FSU[T]): ParamDefAux[RepeatedValueReceptacle[T], Directive1[Iterable[T]]] =
|
||||||
extractParameter[RepeatedValueReceptacle[T], Iterable[T]] { rvr ⇒ repeatedFilter(rvr.name, fsu) }
|
extractParameter[RepeatedValueReceptacle[T], Iterable[T]] { rvr ⇒ repeatedFilter(rvr.name, fsu) }
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue