Skip to content

Commit 0c0b5a6

Browse files
committed
Host & port parsing
1 parent 4b56eaf commit 0c0b5a6

File tree

3 files changed

+61
-1
lines changed

3 files changed

+61
-1
lines changed

core/src/main/scala/sttp/model/headers/Forwarded.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ case class Forwarded(by: Option[String], `for`: Option[String], host: Option[Str
88
/** Serialize a single [[Forwarded]] header to a string
99
*
1010
* @see
11-
* [[Forwarded#toString]] for a multi-header variant
11+
* [[Forwarded.toString]] for a multi-header variant
1212
*/
1313
override def toString: String = {
1414
val sb = new java.lang.StringBuilder()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package sttp.model.headers
2+
3+
import sttp.model.internal.ParseUtils
4+
5+
object Host {
6+
7+
/** Parses the given string into a host and an optional port. If the host is an IPv6 address, the surrounding `[` and
8+
* `]` characters are removed. If the port is not a number, it is discarded.
9+
*
10+
* For example, `example.com:8080` will be parsed into `("example.com", Some(8080))`.
11+
*/
12+
def parseHostAndPort(v: String): (String, Option[Int]) = {
13+
val lastColonIndex = v.lastIndexOf(":")
14+
val lastBracketIndex = v.lastIndexOf("]") // checking if the last colon is part of an IPv6 address
15+
val (host, port) =
16+
if (lastColonIndex == -1 || lastBracketIndex > lastColonIndex) (v, None)
17+
else {
18+
(v.substring(0, lastColonIndex), ParseUtils.toIntOption(v.substring(lastColonIndex + 1)))
19+
}
20+
21+
val hostWithoutBrackets = if (host.startsWith("[") && host.endsWith("]")) {
22+
host.substring(1, host.length - 1)
23+
} else {
24+
host
25+
}
26+
27+
(hostWithoutBrackets, port)
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package sttp.model.headers
2+
3+
import org.scalatest.flatspec.AnyFlatSpec
4+
import org.scalatest.matchers.should.Matchers
5+
6+
class HostTest extends AnyFlatSpec with Matchers {
7+
it should "parse host and port" in {
8+
Host.parseHostAndPort("example.com:8080") shouldBe (("example.com", Some(8080)))
9+
}
10+
11+
it should "parse host without port" in {
12+
Host.parseHostAndPort("example.com") shouldBe (("example.com", None))
13+
}
14+
15+
it should "parse IPv4 address with port" in {
16+
Host.parseHostAndPort("192.168.1.1:8080") shouldBe (("192.168.1.1", Some(8080)))
17+
}
18+
19+
it should "parse IPv4 address without port" in {
20+
Host.parseHostAndPort("192.168.1.1") shouldBe (("192.168.1.1", None))
21+
}
22+
23+
it should "parse IPv6 address with port" in {
24+
Host.parseHostAndPort("[2001:db8::1]:8080") shouldBe (("2001:db8::1", Some(8080)))
25+
}
26+
27+
it should "parse IPv6 address without port" in {
28+
Host.parseHostAndPort("[2001:db8::1]") shouldBe (("2001:db8::1", None))
29+
}
30+
31+
}

0 commit comments

Comments
 (0)