=htc #19650 expse effectivePort on Uri, better port handling

* =htc #19650 Public access to effectivePort in Uri

* =htc #19650 normalize port as late as possible
This commit is contained in:
Johan Andrén 2016-05-12 08:55:06 +02:00 committed by Konrad Malawski
parent 14320f4801
commit 0aea3af0ad
5 changed files with 48 additions and 26 deletions

View file

@ -53,7 +53,7 @@ object WebSocketClientBlueprint {
val valve = StreamUtils.OneTimeValve()
val (initialRequest, key) = Handshake.Client.buildRequest(uri, extraHeaders, subprotocol.toList, settings.websocketRandomFactory())
val hostHeader = Host(uri.authority)
val hostHeader = Host(uri.authority.normalizedFor(uri.scheme))
val renderedInitialRequest =
HttpRequestRendererFactory.renderStrict(RequestRenderingContext(initialRequest, hostHeader), settings, log)

View file

@ -21,7 +21,7 @@ case class JavaUri(uri: sm.Uri) extends jm.Uri {
def scheme(): String = uri.scheme
def host(): jm.Host = uri.authority.host
def port(): Int = uri.authority.port
def port(): Int = uri.effectivePort
def userInfo(): String = uri.authority.userinfo
def path(): String = uri.path.toString

View file

@ -43,13 +43,11 @@ sealed abstract case class Uri(scheme: String, authority: Authority, path: Path,
def queryString(charset: Charset = UTF8): Option[String] = rawQueryString.map(s decode(s, charset))
/**
* INTERNAL API
*
* 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
* is the effective port. Otherwise the default port for the current scheme is returned.
*/
private[akka] def effectivePort: Int = if (authority.port != 0) authority.port else defaultPorts(scheme)
def effectivePort: Int = if (authority.port != 0) authority.port else defaultPorts(scheme)
/**
* Returns a copy of this Uri with the given components.
@ -210,7 +208,7 @@ object Uri {
/**
* Creates a new Uri instance from the given components.
* All components are verified and normalized.
* All components are verified and normalized except the authority which is kept as provided.
* If the given combination of components does not constitute a valid URI as defined by
* http://tools.ietf.org/html/rfc3986 the method throws an `IllegalUriException`.
*/
@ -219,7 +217,7 @@ object Uri {
val p = verifyPath(path, scheme, authority.host)
create(
scheme = normalizeScheme(scheme),
authority = authority.normalizedFor(scheme),
authority = authority,
path = if (scheme.isEmpty) p else collapseDotSegments(p),
queryString = queryString,
fragment = fragment)
@ -277,8 +275,11 @@ object Uri {
* If strict is `false`, accepts unencoded visible 7-bit ASCII characters in addition to the RFC.
* If the given string is not a valid URI the method throws an `IllegalUriException`.
*/
def normalize(uri: ParserInput, charset: Charset = UTF8, mode: Uri.ParsingMode = Uri.ParsingMode.Relaxed): String =
UriRendering.renderUri(new StringRendering, apply(uri, charset, mode), charset).get
def normalize(uri: ParserInput, charset: Charset = UTF8, mode: Uri.ParsingMode = Uri.ParsingMode.Relaxed): String = {
val parsed = apply(uri, charset, mode)
val normalized = parsed.copy(authority = parsed.authority.normalizedFor(parsed.scheme))
UriRendering.renderUri(new StringRendering, normalized, charset).get
}
/**
* Converts a set of URI components to an "effective HTTP request URI" as defined by
@ -307,6 +308,10 @@ object Uri {
def httpScheme(securedConnection: Boolean = false) = if (securedConnection) "https" else "http"
/**
* @param port A port number that may be `0` to signal the default port of for scheme.
* In general what you want is not the value of this field but [[Uri.effectivePort]].
*/
final case class Authority(host: Host, port: Int = 0, userinfo: String = "") {
def isEmpty = equals(Authority.Empty)
def nonEmpty = !isEmpty
@ -742,7 +747,7 @@ object Uri {
private[http] def create(scheme: String, userinfo: String, host: Host, port: Int, path: Path, queryString: Option[String],
fragment: Option[String]): Uri =
create(scheme, Authority(host, normalizePort(port, scheme), userinfo), path, queryString, fragment)
create(scheme, Authority(host, port, userinfo), path, queryString, fragment)
private[http] def create(scheme: String, authority: Authority, path: Path, queryString: Option[String],
fragment: Option[String]): Uri =

View file

@ -382,7 +382,7 @@ class UriSpec extends WordSpec with Matchers {
// empty host
Uri("http://:8000/foo") shouldEqual Uri("http", Authority(Host.Empty, 8000), Path / "foo")
Uri("http://:80/foo") shouldEqual Uri("http", Authority.Empty, Path / "foo")
Uri("http://:80/foo") shouldEqual Uri("http", Authority(Host.Empty, 80), Path / "foo")
}
"properly complete a normalization cycle" in {
@ -571,38 +571,55 @@ class UriSpec extends WordSpec with Matchers {
}
"provide sugar for fluent transformations" in {
val uri = Uri("http://host:80/path?query#fragment")
val uri = Uri("http://host/path?query#fragment")
val explicitDefault = Uri("http://host:80/path?query#fragment")
val nonDefaultUri = Uri("http://host:6060/path?query#fragment")
uri.withScheme("https") shouldEqual Uri("https://host/path?query#fragment")
explicitDefault.withScheme("https") shouldEqual Uri("https://host:80/path?query#fragment")
nonDefaultUri.withScheme("https") shouldEqual Uri("https://host:6060/path?query#fragment")
uri.withAuthority(Authority(Host("other"), 3030)) shouldEqual Uri("http://other:3030/path?query#fragment")
uri.withAuthority(Host("other"), 3030) shouldEqual Uri("http://other:3030/path?query#fragment")
uri.withAuthority("other", 3030) shouldEqual Uri("http://other:3030/path?query#fragment")
uri.withHost(Host("other")) shouldEqual Uri("http://other:80/path?query#fragment")
uri.withHost("other") shouldEqual Uri("http://other:80/path?query#fragment")
uri.withHost(Host("other")) shouldEqual Uri("http://other/path?query#fragment")
explicitDefault.withHost(Host("other")) shouldEqual Uri("http://other:80/path?query#fragment")
uri.withHost("other") shouldEqual Uri("http://other/path?query#fragment")
explicitDefault.withHost("other") shouldEqual Uri("http://other:80/path?query#fragment")
uri.withPort(90) shouldEqual Uri("http://host:90/path?query#fragment")
explicitDefault.withPort(90) shouldEqual Uri("http://host:90/path?query#fragment")
uri.withPath(Path("/newpath")) shouldEqual Uri("http://host/newpath?query#fragment")
uri.withUserInfo("someInfo") shouldEqual Uri("http://someInfo@host:80/path?query#fragment")
explicitDefault.withPath(Path("/newpath")) shouldEqual Uri("http://host:80/newpath?query#fragment")
uri.withQuery(Query("param1" -> "value1")) shouldEqual Uri("http://host:80/path?param1=value1#fragment")
uri.withQuery(Query(Map("param1" -> "value1"))) shouldEqual Uri("http://host:80/path?param1=value1#fragment")
uri.withRawQueryString("param1=value1") shouldEqual Uri("http://host:80/path?param1=value1#fragment")
uri.withUserInfo("someInfo") shouldEqual Uri("http://someInfo@host/path?query#fragment")
explicitDefault.withUserInfo("someInfo") shouldEqual Uri("http://someInfo@host:80/path?query#fragment")
uri.withFragment("otherFragment") shouldEqual Uri("http://host:80/path?query#otherFragment")
uri.withQuery(Query("param1" -> "value1")) shouldEqual Uri("http://host/path?param1=value1#fragment")
uri.withQuery(Query(Map("param1" -> "value1"))) shouldEqual Uri("http://host/path?param1=value1#fragment")
uri.withRawQueryString("param1=value1") shouldEqual Uri("http://host/path?param1=value1#fragment")
uri.withFragment("otherFragment") shouldEqual Uri("http://host/path?query#otherFragment")
}
"return the correct effective port" in {
80 shouldEqual Uri("http://host/").effectivePort
21 shouldEqual Uri("ftp://host/").effectivePort
9090 shouldEqual Uri("http://host:9090/").effectivePort
443 shouldEqual Uri("https://host/").effectivePort
Uri("http://host/").effectivePort shouldEqual 80
Uri("ftp://host/").effectivePort shouldEqual 21
Uri("http://host:9090/").effectivePort shouldEqual 9090
Uri("https://host/").effectivePort shouldEqual 443
4450 shouldEqual Uri("https://host/").withPort(4450).effectivePort
4450 shouldEqual Uri("https://host:3030/").withPort(4450).effectivePort
Uri("https://host/").withPort(4450).effectivePort shouldEqual 4450
Uri("https://host:3030/").withPort(4450).effectivePort shouldEqual 4450
}
"keep the specified authority port" in {
Uri("example.com").withPort(0).authority.port shouldEqual 0
Uri("example.com").withPort(80).authority.port shouldEqual 80
Uri("http://example.com").withPort(80).authority.port shouldEqual 80
Uri("http://example.com").withPort(0).authority.port shouldEqual 0
Uri("https://example.com").withPort(0).authority.port shouldEqual 0
Uri("https://example.com").withPort(443).authority.port shouldEqual 443
}
"properly render as HTTP request target origin forms" in {