Merge pull request #19584 from cmbaxter/feature/xff-avoid-dns-cmbaxter
!htt #19388 Removing use of InetAddress.getByName
This commit is contained in:
commit
10d3af1478
13 changed files with 104 additions and 26 deletions
|
|
@ -8,6 +8,7 @@ import akka.http.scaladsl.model._
|
|||
import akka.http.scaladsl.server._
|
||||
import headers._
|
||||
import docs.http.scaladsl.server.RoutingSpec
|
||||
import java.net.InetAddress
|
||||
|
||||
class MiscDirectivesExamplesSpec extends RoutingSpec {
|
||||
|
||||
|
|
@ -17,7 +18,7 @@ class MiscDirectivesExamplesSpec extends RoutingSpec {
|
|||
}
|
||||
|
||||
// tests:
|
||||
Get("/").withHeaders(`Remote-Address`(RemoteAddress("192.168.3.12"))) ~> route ~> check {
|
||||
Get("/").withHeaders(`Remote-Address`(RemoteAddress(InetAddress.getByName("192.168.3.12")))) ~> route ~> check {
|
||||
responseAs[String] shouldEqual "Client's ip is 192.168.3.12"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,9 +26,6 @@ public abstract class RemoteAddress {
|
|||
public static RemoteAddress create(InetSocketAddress address) {
|
||||
return akka.http.scaladsl.model.RemoteAddress.apply(address);
|
||||
}
|
||||
public static RemoteAddress create(String address) {
|
||||
return akka.http.scaladsl.model.RemoteAddress.apply(address);
|
||||
}
|
||||
public static RemoteAddress create(byte[] address) {
|
||||
return akka.http.scaladsl.model.RemoteAddress.apply(address);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2016 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.http.javadsl.model.headers;
|
||||
|
||||
/**
|
||||
* Model for the `X-Real-Ip` header.
|
||||
*/
|
||||
public abstract class XRealIp extends akka.http.scaladsl.model.HttpHeader {
|
||||
public abstract akka.http.javadsl.model.RemoteAddress address();
|
||||
|
||||
public static XRealIp create(akka.http.javadsl.model.RemoteAddress address) {
|
||||
return new akka.http.scaladsl.model.headers.X$minusReal$minusIp(((akka.http.scaladsl.model.RemoteAddress) address));
|
||||
}
|
||||
}
|
||||
|
|
@ -154,7 +154,8 @@ private[http] object HeaderParser {
|
|||
"upgrade",
|
||||
"user-agent",
|
||||
"www-authenticate",
|
||||
"x-forwarded-for")
|
||||
"x-forwarded-for",
|
||||
"x-real-ip")
|
||||
|
||||
abstract class Settings {
|
||||
def uriParsingMode: Uri.ParsingMode
|
||||
|
|
|
|||
|
|
@ -221,4 +221,9 @@ private[parser] trait SimpleHeaders { this: Parser with CommonRules with CommonA
|
|||
def addr = rule { (`ip-v4-address` | `ip-v6-address`) ~> (RemoteAddress(_)) | "unknown" ~ push(RemoteAddress.Unknown) }
|
||||
rule { oneOrMore(addr).separatedBy(listSep) ~ EOI ~> (`X-Forwarded-For`(_)) }
|
||||
}
|
||||
|
||||
def `x-real-ip` = rule {
|
||||
(`ip-v4-address` | `ip-v6-address`) ~ EOI ~> (b ⇒ `X-Real-Ip`(RemoteAddress(b)))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -45,9 +45,6 @@ object RemoteAddress {
|
|||
def isUnknown = false
|
||||
}
|
||||
|
||||
def apply(s: String): RemoteAddress =
|
||||
try IP(InetAddress.getByName(s)) catch { case _: UnknownHostException ⇒ Unknown }
|
||||
|
||||
def apply(a: InetAddress, port: Option[Int] = None): IP = IP(a, port)
|
||||
|
||||
def apply(a: InetSocketAddress): IP = IP(a.getAddress, Some(a.getPort))
|
||||
|
|
|
|||
|
|
@ -896,8 +896,7 @@ final case class `WWW-Authenticate`(challenges: immutable.Seq[HttpChallenge]) ex
|
|||
}
|
||||
|
||||
// http://en.wikipedia.org/wiki/X-Forwarded-For
|
||||
object `X-Forwarded-For` extends ModeledCompanion[`X-Forwarded-For`] {
|
||||
def apply(first: String, more: String*): `X-Forwarded-For` = apply(RemoteAddress(first), more.map(RemoteAddress(_)): _*)
|
||||
object `X-Forwarded-For` extends ModeledCompanion[`X-Forwarded-For`] {
|
||||
def apply(first: RemoteAddress, more: RemoteAddress*): `X-Forwarded-For` = apply(immutable.Seq(first +: more: _*))
|
||||
implicit val addressesRenderer = Renderer.defaultSeqRenderer[RemoteAddress] // cache
|
||||
}
|
||||
|
|
@ -911,3 +910,10 @@ final case class `X-Forwarded-For`(addresses: immutable.Seq[RemoteAddress]) exte
|
|||
/** Java API */
|
||||
def getAddresses: Iterable[jm.RemoteAddress] = addresses.asJava
|
||||
}
|
||||
|
||||
object `X-Real-Ip` extends ModeledCompanion[`X-Real-Ip`]
|
||||
final case class `X-Real-Ip`(address:RemoteAddress) extends jm.headers.XRealIp
|
||||
with RequestHeader {
|
||||
def renderValue[R <: Rendering](r: R): r.type = r ~~ address
|
||||
protected def companion = `X-Real-Ip`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,6 +128,13 @@ class HttpHeaderParserSpec extends WordSpec with Matchers with BeforeAndAfterAll
|
|||
parseAndCache("Origin: localhost:8080\r\nx")() shouldEqual RawHeader("origin", "localhost:8080")
|
||||
}
|
||||
|
||||
"parse and cache an X-Forwarded-For with a hostname in it as a RawHeader" in new TestSetup() {
|
||||
parseAndCache("X-Forwarded-For: 1.2.3.4, akka.io\r\nx")() shouldEqual RawHeader("x-forwarded-for", "1.2.3.4, akka.io")
|
||||
}
|
||||
|
||||
"parse and cache an X-Real-Ip with a hostname as it's value as a RawHeader" in new TestSetup() {
|
||||
parseAndCache("X-Real-Ip: akka.io\r\nx")() shouldEqual RawHeader("x-real-ip", "akka.io")
|
||||
}
|
||||
"parse and cache a raw header" in new TestSetup(primed = false) {
|
||||
insert("hello: bob", 'Hello)
|
||||
val (ixA, headerA) = parseLine("Fancy-Pants: foo\r\nx")
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import MediaRanges._
|
|||
import HttpCharsets._
|
||||
import HttpEncodings._
|
||||
import HttpMethods._
|
||||
import java.net.InetAddress
|
||||
|
||||
class HttpHeaderSpec extends FreeSpec with Matchers {
|
||||
val `application/vnd.spray` = MediaType.applicationBinary("vnd.spray", MediaType.Compressible)
|
||||
|
|
@ -545,14 +546,14 @@ class HttpHeaderSpec extends FreeSpec with Matchers {
|
|||
}
|
||||
|
||||
"X-Forwarded-For" in {
|
||||
"X-Forwarded-For: 1.2.3.4" =!= `X-Forwarded-For`("1.2.3.4")
|
||||
"X-Forwarded-For: 234.123.5.6, 8.8.8.8" =!= `X-Forwarded-For`("234.123.5.6", "8.8.8.8")
|
||||
"X-Forwarded-For: 1.2.3.4, unknown" =!= `X-Forwarded-For`(RemoteAddress("1.2.3.4"), RemoteAddress.Unknown)
|
||||
"X-Forwarded-For: 192.0.2.43, 2001:db8:cafe:0:0:0:0:17" =!= `X-Forwarded-For`("192.0.2.43", "2001:db8:cafe::17")
|
||||
"X-Forwarded-For: 1234:5678:9abc:def1:2345:6789:abcd:ef00" =!= `X-Forwarded-For`("1234:5678:9abc:def1:2345:6789:abcd:ef00")
|
||||
"X-Forwarded-For: 1234:567:9a:d:2:67:abc:ef00" =!= `X-Forwarded-For`("1234:567:9a:d:2:67:abc:ef00")
|
||||
"X-Forwarded-For: 1.2.3.4" =!= `X-Forwarded-For`(remoteAddress("1.2.3.4"))
|
||||
"X-Forwarded-For: 234.123.5.6, 8.8.8.8" =!= `X-Forwarded-For`(remoteAddress("234.123.5.6"), remoteAddress("8.8.8.8"))
|
||||
"X-Forwarded-For: 1.2.3.4, unknown" =!= `X-Forwarded-For`(remoteAddress("1.2.3.4"), RemoteAddress.Unknown)
|
||||
"X-Forwarded-For: 192.0.2.43, 2001:db8:cafe:0:0:0:0:17" =!= `X-Forwarded-For`(remoteAddress("192.0.2.43"), remoteAddress("2001:db8:cafe::17"))
|
||||
"X-Forwarded-For: 1234:5678:9abc:def1:2345:6789:abcd:ef00" =!= `X-Forwarded-For`(remoteAddress("1234:5678:9abc:def1:2345:6789:abcd:ef00"))
|
||||
"X-Forwarded-For: 1234:567:9a:d:2:67:abc:ef00" =!= `X-Forwarded-For`(remoteAddress("1234:567:9a:d:2:67:abc:ef00"))
|
||||
"X-Forwarded-For: 2001:db8:85a3::8a2e:370:7334" =!=> "2001:db8:85a3:0:0:8a2e:370:7334"
|
||||
"X-Forwarded-For: 1:2:3:4:5:6:7:8" =!= `X-Forwarded-For`("1:2:3:4:5:6:7:8")
|
||||
"X-Forwarded-For: 1:2:3:4:5:6:7:8" =!= `X-Forwarded-For`(remoteAddress("1:2:3:4:5:6:7:8"))
|
||||
"X-Forwarded-For: ::2:3:4:5:6:7:8" =!=> "0:2:3:4:5:6:7:8"
|
||||
"X-Forwarded-For: ::3:4:5:6:7:8" =!=> "0:0:3:4:5:6:7:8"
|
||||
"X-Forwarded-For: ::4:5:6:7:8" =!=> "0:0:0:4:5:6:7:8"
|
||||
|
|
@ -574,6 +575,44 @@ class HttpHeaderSpec extends FreeSpec with Matchers {
|
|||
"X-Forwarded-For: 1:2:3:4:5::7:8" =!=> "1:2:3:4:5:0:7:8"
|
||||
"X-Forwarded-For: 1:2:3:4:5:6::8" =!=> "1:2:3:4:5:6:0:8"
|
||||
"X-Forwarded-For: ::" =!=> "0:0:0:0:0:0:0:0"
|
||||
"X-Forwarded-For: 1.2.3.4, akka.io" =!=
|
||||
ErrorInfo(
|
||||
"Illegal HTTP header 'X-Forwarded-For': Invalid input 'k', expected HEXDIG, h8, ':', ch16o or cc (line 1, column 11)",
|
||||
"1.2.3.4, akka.io\n ^")
|
||||
}
|
||||
|
||||
"X-Real-Ip" in {
|
||||
"X-Real-Ip: 1.2.3.4" =!= `X-Real-Ip`(remoteAddress("1.2.3.4"))
|
||||
"X-Real-Ip: 2001:db8:cafe:0:0:0:0:17" =!= `X-Real-Ip`(remoteAddress("2001:db8:cafe:0:0:0:0:17"))
|
||||
"X-Real-Ip: 1234:5678:9abc:def1:2345:6789:abcd:ef00" =!= `X-Real-Ip`(remoteAddress("1234:5678:9abc:def1:2345:6789:abcd:ef00"))
|
||||
"X-Real-Ip: 1234:567:9a:d:2:67:abc:ef00" =!= `X-Real-Ip`(remoteAddress("1234:567:9a:d:2:67:abc:ef00"))
|
||||
"X-Real-Ip: 2001:db8:85a3::8a2e:370:7334" =!=> "2001:db8:85a3:0:0:8a2e:370:7334"
|
||||
"X-Real-Ip: 1:2:3:4:5:6:7:8" =!= `X-Real-Ip`(remoteAddress("1:2:3:4:5:6:7:8"))
|
||||
"X-Real-Ip: ::2:3:4:5:6:7:8" =!=> "0:2:3:4:5:6:7:8"
|
||||
"X-Real-Ip: ::3:4:5:6:7:8" =!=> "0:0:3:4:5:6:7:8"
|
||||
"X-Real-Ip: ::4:5:6:7:8" =!=> "0:0:0:4:5:6:7:8"
|
||||
"X-Real-Ip: ::5:6:7:8" =!=> "0:0:0:0:5:6:7:8"
|
||||
"X-Real-Ip: ::6:7:8" =!=> "0:0:0:0:0:6:7:8"
|
||||
"X-Real-Ip: ::7:8" =!=> "0:0:0:0:0:0:7:8"
|
||||
"X-Real-Ip: ::8" =!=> "0:0:0:0:0:0:0:8"
|
||||
"X-Real-Ip: 1:2:3:4:5:6:7::" =!=> "1:2:3:4:5:6:7:0"
|
||||
"X-Real-Ip: 1:2:3:4:5:6::" =!=> "1:2:3:4:5:6:0:0"
|
||||
"X-Real-Ip: 1:2:3:4:5::" =!=> "1:2:3:4:5:0:0:0"
|
||||
"X-Real-Ip: 1:2:3:4::" =!=> "1:2:3:4:0:0:0:0"
|
||||
"X-Real-Ip: 1:2:3::" =!=> "1:2:3:0:0:0:0:0"
|
||||
"X-Real-Ip: 1:2::" =!=> "1:2:0:0:0:0:0:0"
|
||||
"X-Real-Ip: 1::" =!=> "1:0:0:0:0:0:0:0"
|
||||
"X-Real-Ip: 1::3:4:5:6:7:8" =!=> "1:0:3:4:5:6:7:8"
|
||||
"X-Real-Ip: 1:2::4:5:6:7:8" =!=> "1:2:0:4:5:6:7:8"
|
||||
"X-Real-Ip: 1:2:3::5:6:7:8" =!=> "1:2:3:0:5:6:7:8"
|
||||
"X-Real-Ip: 1:2:3:4::6:7:8" =!=> "1:2:3:4:0:6:7:8"
|
||||
"X-Real-Ip: 1:2:3:4:5::7:8" =!=> "1:2:3:4:5:0:7:8"
|
||||
"X-Real-Ip: 1:2:3:4:5:6::8" =!=> "1:2:3:4:5:6:0:8"
|
||||
"X-Real-Ip: ::" =!=> "0:0:0:0:0:0:0:0"
|
||||
"X-Real-Ip: akka.io" =!=
|
||||
ErrorInfo(
|
||||
"Illegal HTTP header 'X-Real-Ip': Invalid input 'k', expected HEXDIG, h8, ':', ch16o or cc (line 1, column 2)",
|
||||
"akka.io\n ^")
|
||||
}
|
||||
|
||||
"RawHeader" in {
|
||||
|
|
@ -663,4 +702,6 @@ class HttpHeaderSpec extends FreeSpec with Matchers {
|
|||
val info = result.errors.head
|
||||
fail(s"Input `${header.header}` failed to parse:\n${info.summary}\n${info.detail}")
|
||||
}
|
||||
|
||||
def remoteAddress(ip: String) = RemoteAddress(InetAddress.getByName(ip))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ package akka.http.scaladsl.model.headers
|
|||
|
||||
import akka.http.impl.util._
|
||||
import org.scalatest._
|
||||
|
||||
import java.net.InetAddress
|
||||
import akka.http.scaladsl.model._
|
||||
|
||||
class HeaderSpec extends FreeSpec with Matchers {
|
||||
|
|
@ -99,7 +99,8 @@ class HeaderSpec extends FreeSpec with Matchers {
|
|||
`Transfer-Encoding`(TransferEncodings.chunked),
|
||||
Upgrade(Vector(UpgradeProtocol("HTTP", Some("2.0")))),
|
||||
`User-Agent`("Akka HTTP Client 2.4"),
|
||||
`X-Forwarded-For`(RemoteAddress("192.168.0.1")))
|
||||
`X-Forwarded-For`(RemoteAddress(InetAddress.getByName("192.168.0.1"))),
|
||||
`X-Real-Ip`(RemoteAddress(InetAddress.getByName("192.168.1.1"))))
|
||||
|
||||
requestHeaders.foreach { header ⇒
|
||||
header shouldBe 'renderInRequests
|
||||
|
|
|
|||
|
|
@ -13,8 +13,11 @@ import akka.http.javadsl.server.RequestVals;
|
|||
import akka.http.javadsl.server.Unmarshallers;
|
||||
import akka.http.javadsl.testkit.JUnitRouteTest;
|
||||
import akka.http.javadsl.testkit.TestRoute;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class RequestValTest extends JUnitRouteTest {
|
||||
|
|
@ -62,16 +65,16 @@ public class RequestValTest extends JUnitRouteTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testClientIpExtraction() {
|
||||
public void testClientIpExtraction() throws UnknownHostException{
|
||||
TestRoute route = testRoute(completeWithValueToString(RequestVals.clientIP()));
|
||||
|
||||
route
|
||||
.run(HttpRequest.create().addHeader(XForwardedFor.create(RemoteAddress.create("127.0.0.2"))))
|
||||
.run(HttpRequest.create().addHeader(XForwardedFor.create(RemoteAddress.create(InetAddress.getByName("127.0.0.2")))))
|
||||
.assertStatusCode(200)
|
||||
.assertEntity("127.0.0.2");
|
||||
|
||||
route
|
||||
.run(HttpRequest.create().addHeader(akka.http.javadsl.model.headers.RemoteAddress.create(RemoteAddress.create("127.0.0.3"))))
|
||||
.run(HttpRequest.create().addHeader(akka.http.javadsl.model.headers.RemoteAddress.create(RemoteAddress.create(InetAddress.getByName("127.0.0.3")))))
|
||||
.assertStatusCode(200)
|
||||
.assertEntity("127.0.0.3");
|
||||
|
||||
|
|
|
|||
|
|
@ -10,22 +10,23 @@ import scala.concurrent.duration._
|
|||
import scala.util.Try
|
||||
import akka.http.scaladsl.model._
|
||||
import headers._
|
||||
import java.net.InetAddress
|
||||
|
||||
class MiscDirectivesSpec extends RoutingSpec {
|
||||
|
||||
"the extractClientIP directive" should {
|
||||
"extract from a X-Forwarded-For header" in {
|
||||
Get() ~> addHeaders(`X-Forwarded-For`("2.3.4.5"), RawHeader("x-real-ip", "1.2.3.4")) ~> {
|
||||
Get() ~> addHeaders(`X-Forwarded-For`(remoteAddress("2.3.4.5")), RawHeader("x-real-ip", "1.2.3.4")) ~> {
|
||||
extractClientIP { echoComplete }
|
||||
} ~> check { responseAs[String] shouldEqual "2.3.4.5" }
|
||||
}
|
||||
"extract from a Remote-Address header" in {
|
||||
Get() ~> addHeaders(RawHeader("x-real-ip", "1.2.3.4"), `Remote-Address`(RemoteAddress("5.6.7.8"))) ~> {
|
||||
Get() ~> addHeaders(`X-Real-Ip`(remoteAddress("1.2.3.4")), `Remote-Address`(remoteAddress("5.6.7.8"))) ~> {
|
||||
extractClientIP { echoComplete }
|
||||
} ~> check { responseAs[String] shouldEqual "5.6.7.8" }
|
||||
}
|
||||
"extract from a X-Real-IP header" in {
|
||||
Get() ~> addHeader(RawHeader("x-real-ip", "1.2.3.4")) ~> {
|
||||
Get() ~> addHeader(`X-Real-Ip`(remoteAddress("1.2.3.4"))) ~> {
|
||||
extractClientIP { echoComplete }
|
||||
} ~> check { responseAs[String] shouldEqual "1.2.3.4" }
|
||||
}
|
||||
|
|
@ -85,4 +86,6 @@ class MiscDirectivesSpec extends RoutingSpec {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
def remoteAddress(ip: String) = RemoteAddress(InetAddress.getByName(ip))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ object MiscDirectives extends MiscDirectives {
|
|||
private val _extractClientIP: Directive1[RemoteAddress] =
|
||||
headerValuePF { case `X-Forwarded-For`(Seq(address, _*)) ⇒ address } |
|
||||
headerValuePF { case `Remote-Address`(address) ⇒ address } |
|
||||
headerValuePF { case h if h.is("x-real-ip") ⇒ RemoteAddress(h.value) }
|
||||
headerValuePF { case `X-Real-Ip`(address) ⇒ address }
|
||||
|
||||
private val _requestEntityEmpty: Directive0 =
|
||||
extract(_.request.entity.isKnownEmpty).flatMap(if (_) pass else reject)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue