/transport/server/play-server/src/test/scala/play/core/server/common/ForwardedHeaderHandlerSpec.scala

https://github.com/playframework/playframework · Scala · 526 lines · 492 code · 25 blank · 9 comment · 6 complexity · c91c73826398a6625f861152b6d602b0 MD5 · raw file

  1. /*
  2. * Copyright (C) Lightbend Inc. <https://www.lightbend.com>
  3. */
  4. package play.core.server.common
  5. import java.net.InetAddress
  6. import com.google.common.net.InetAddresses
  7. import org.specs2.mutable.Specification
  8. import play.api.mvc.Headers
  9. import play.api.mvc.request.RemoteConnection
  10. import play.api.Configuration
  11. import play.api.PlayException
  12. import play.core.server.common.ForwardedHeaderHandler._
  13. class ForwardedHeaderHandlerSpec extends Specification {
  14. "ForwardedHeaderHandler" should {
  15. """not accept a wrong setting as "play.http.forwarded.version" in config""" in {
  16. handler(version("rfc7240")) must throwA[PlayException]
  17. }
  18. "parse rfc7239 entries" in {
  19. val results = processHeaders(
  20. version("rfc7239") ++ trustedProxies("192.0.2.60/24"),
  21. headers(
  22. """
  23. |Forwarded: for="_gazonk"
  24. |Forwarded: For="[2001:db8:cafe::17]:4711"
  25. |Forwarded: for=192.0.2.60;proto=http;by=203.0.113.43
  26. |Forwarded: for=192.0.2.43, for=198.51.100.17, for=127.0.0.1
  27. |Forwarded: for=192.0.2.61;proto=https
  28. |Forwarded: for=unknown
  29. |Forwarded: For="[::ffff:192.168.0.9]";proto=https
  30. """.stripMargin
  31. )
  32. )
  33. results.length must_== 9
  34. results(0)._1 must_== ForwardedEntry(Some("_gazonk"), None)
  35. results(0)._2 must beLeft
  36. results(0)._3 must beNone
  37. results(1)._1 must_== ForwardedEntry(Some("[2001:db8:cafe::17]:4711"), None)
  38. results(1)._2 must beRight(ParsedForwardedEntry(addr("2001:db8:cafe::17"), false))
  39. results(1)._3 must beSome(false)
  40. results(2)._1 must_== ForwardedEntry(Some("192.0.2.60"), Some("http"))
  41. results(2)._2 must beRight(ParsedForwardedEntry(addr("192.0.2.60"), false))
  42. results(2)._3 must beSome(true)
  43. results(3)._1 must_== ForwardedEntry(Some("192.0.2.43"), None)
  44. results(3)._2 must beRight(ParsedForwardedEntry(addr("192.0.2.43"), false))
  45. results(3)._3 must beSome(true)
  46. results(4)._1 must_== ForwardedEntry(Some("198.51.100.17"), None)
  47. results(4)._2 must beRight(ParsedForwardedEntry(addr("198.51.100.17"), false))
  48. results(4)._3 must beSome(false)
  49. results(5)._1 must_== ForwardedEntry(Some("127.0.0.1"), None)
  50. results(5)._2 must beRight(ParsedForwardedEntry(addr("127.0.0.1"), false))
  51. results(5)._3 must beSome(false)
  52. results(6)._1 must_== ForwardedEntry(Some("192.0.2.61"), Some("https"))
  53. results(6)._2 must beRight(ParsedForwardedEntry(addr("192.0.2.61"), true))
  54. results(6)._3 must beSome(true)
  55. results(7)._1 must_== ForwardedEntry(Some("unknown"), None)
  56. results(7)._2 must beLeft
  57. results(7)._3 must beNone
  58. results(8)._1 must_== ForwardedEntry(Some("[::ffff:192.168.0.9]"), Some("https"))
  59. results(8)._2 must beRight(ParsedForwardedEntry(addr("::ffff:192.168.0.9"), true))
  60. results(8)._3 must beSome(false)
  61. }
  62. "parse x-forwarded entries" in {
  63. val results = processHeaders(
  64. version("x-forwarded") ++ trustedProxies("2001:db8:cafe::17"),
  65. headers(
  66. """
  67. |X-Forwarded-For: 192.168.1.1, ::1, [2001:db8:cafe::17], 127.0.0.1, ::ffff:123.123.123.123
  68. |X-Forwarded-Proto: https, http, https, http, https
  69. """.stripMargin
  70. )
  71. )
  72. results.length must_== 5
  73. results(0)._1 must_== ForwardedEntry(Some("192.168.1.1"), Some("https"))
  74. results(0)._2 must beRight(ParsedForwardedEntry(addr("192.168.1.1"), true))
  75. results(0)._3 must beSome(false)
  76. results(1)._1 must_== ForwardedEntry(Some("::1"), Some("http"))
  77. results(1)._2 must beRight(ParsedForwardedEntry(addr("::1"), false))
  78. results(1)._3 must beSome(false)
  79. results(2)._1 must_== ForwardedEntry(Some("[2001:db8:cafe::17]"), Some("https"))
  80. results(2)._2 must beRight(ParsedForwardedEntry(addr("2001:db8:cafe::17"), true))
  81. results(2)._3 must beSome(true)
  82. results(3)._1 must_== ForwardedEntry(Some("127.0.0.1"), Some("http"))
  83. results(3)._2 must beRight(ParsedForwardedEntry(addr("127.0.0.1"), false))
  84. results(3)._3 must beSome(false)
  85. results(4)._1 must_== ForwardedEntry(Some("::ffff:123.123.123.123"), Some("https"))
  86. results(4)._2 must beRight(ParsedForwardedEntry(addr("::ffff:123.123.123.123"), true))
  87. results(4)._3 must beSome(false)
  88. }
  89. "default to trusting IPv4 and IPv6 localhost with rfc7239 when there is config with default settings" in {
  90. remoteConnectionToLocalhost(
  91. version("rfc7239"),
  92. """
  93. |Forwarded: for=192.0.2.43;proto=https, for="[::1]"
  94. """.stripMargin
  95. ) mustEqual RemoteConnection("192.0.2.43", true, None)
  96. }
  97. "ignore proxy hosts with rfc7239 when no proxies are trusted" in {
  98. remoteConnectionToLocalhost(
  99. version("rfc7239") ++ trustedProxies(),
  100. """
  101. |Forwarded: for="_gazonk"
  102. |Forwarded: For="[2001:db8:cafe::17]:4711"
  103. |Forwarded: for=192.0.2.60;proto=http;by=203.0.113.43
  104. |Forwarded: for=192.0.2.43, for=198.51.100.17, for=127.0.0.1
  105. """.stripMargin
  106. ) mustEqual RemoteConnection(localhost, false, None)
  107. }
  108. "get first untrusted proxy host with rfc7239 with ipv4 localhost" in {
  109. remoteConnectionToLocalhost(
  110. version("rfc7239"),
  111. """
  112. |Forwarded: for="_gazonk"
  113. |Forwarded: For="[2001:db8:cafe::17]:4711"
  114. |Forwarded: for=192.0.2.60;proto=http;by=203.0.113.43
  115. |Forwarded: for=192.0.2.43, for=198.51.100.17, for=127.0.0.1
  116. """.stripMargin
  117. ) mustEqual RemoteConnection("198.51.100.17", false, None)
  118. }
  119. "get first untrusted proxy host with rfc7239 with ipv6 localhost" in {
  120. remoteConnectionToLocalhost(
  121. version("rfc7239"),
  122. """
  123. |Forwarded: for="_gazonk"
  124. |Forwarded: For="[2001:db8:cafe::17]:4711"
  125. |Forwarded: for=192.0.2.60;proto=http;by=203.0.113.43
  126. |Forwarded: for=192.0.2.43, for=[::1]
  127. """.stripMargin
  128. ) mustEqual RemoteConnection("192.0.2.43", false, None)
  129. }
  130. "get first untrusted proxy with rfc7239 with trusted proxy subnet" in {
  131. remoteConnectionToLocalhost(
  132. version("rfc7239") ++ trustedProxies("192.168.1.1/24", "127.0.0.1"),
  133. """
  134. |Forwarded: for="_gazonk"
  135. |Forwarded: For="[2001:db8:cafe::17]:4711"
  136. |Forwarded: for=192.0.2.60;proto=http;by=203.0.113.43
  137. |Forwarded: for=192.168.1.10, for=127.0.0.1
  138. """.stripMargin
  139. ) mustEqual RemoteConnection("192.0.2.60", false, None)
  140. }
  141. "get first untrusted proxy protocol with rfc7239 with trusted localhost proxy" in {
  142. remoteConnectionToLocalhost(
  143. version("rfc7239") ++ trustedProxies("127.0.0.1"),
  144. """
  145. |Forwarded: for="_gazonk"
  146. |Forwarded: For="[2001:db8:cafe::17]:4711"
  147. |Forwarded: for=192.0.2.60;proto=http;by=203.0.113.43
  148. |Forwarded: for=192.168.1.10, for=127.0.0.1
  149. """.stripMargin
  150. ) mustEqual RemoteConnection("192.168.1.10", false, None)
  151. }
  152. "get first untrusted proxy protocol with rfc7239 with subnet mask" in {
  153. remoteConnectionToLocalhost(
  154. version("rfc7239") ++ trustedProxies("192.168.1.1/24", "127.0.0.1"),
  155. """
  156. |Forwarded: for="_gazonk"
  157. |Forwarded: For="[2001:db8:cafe::17]:4711"
  158. |Forwarded: for=192.0.2.60;proto=https;by=203.0.113.43
  159. |Forwarded: for=192.168.1.10, for=127.0.0.1
  160. """.stripMargin
  161. ) mustEqual RemoteConnection("192.0.2.60", true, None)
  162. }
  163. "handle IPv6 addresses with rfc7239" in {
  164. remoteConnectionToLocalhost(
  165. version("rfc7239") ++ trustedProxies("127.0.0.1"),
  166. """
  167. |Forwarded: For=[2001:db8:cafe::17]:4711
  168. """.stripMargin
  169. ) mustEqual RemoteConnection("2001:db8:cafe::17", false, None)
  170. }
  171. "handle quoted IPv6 addresses with rfc7239" in {
  172. remoteConnectionToLocalhost(
  173. version("rfc7239") ++ trustedProxies("127.0.0.1"),
  174. """
  175. |Forwarded: For="[2001:db8:cafe::17]:4711"
  176. """.stripMargin
  177. ) mustEqual RemoteConnection("2001:db8:cafe::17", false, None)
  178. }
  179. "handle quoted IPv4-mapped IPv6 addresses with rfc7239" in {
  180. handler(version("rfc7239") ++ trustedProxies("fe80::1", "::ffff:123.123.123.123"))
  181. .forwardedConnection(
  182. RemoteConnection("fe80::1", false, None),
  183. headers("""
  184. |Forwarded: For="[::ffff:99.99.99.99]:4711"
  185. |Forwarded: For="[::ffff:123.123.123.123]"
  186. """.stripMargin)
  187. ) mustEqual RemoteConnection(addr("::ffff:99.99.99.99"), false, None)
  188. }
  189. "ignore obfuscated addresses with rfc7239" in {
  190. remoteConnectionToLocalhost(
  191. version("rfc7239") ++ trustedProxies("192.168.1.1/24", "127.0.0.1"),
  192. """
  193. |Forwarded: for="_gazonk"
  194. |Forwarded: for=192.168.1.10, for=127.0.0.1
  195. """.stripMargin
  196. ) mustEqual RemoteConnection("192.168.1.10", false, None)
  197. }
  198. "ignore unknown addresses with rfc7239" in {
  199. remoteConnectionToLocalhost(
  200. version("rfc7239") ++ trustedProxies("192.168.1.1/24", "127.0.0.1"),
  201. """
  202. |Forwarded: for=unknown
  203. |Forwarded: for=192.168.1.10, for=127.0.0.1
  204. """.stripMargin
  205. ) mustEqual RemoteConnection("192.168.1.10", false, None)
  206. }
  207. "ignore rfc7239 header with empty addresses" in {
  208. handler(version("rfc7239") ++ trustedProxies("192.0.2.43"))
  209. .forwardedConnection(
  210. RemoteConnection("192.0.2.43", true, None),
  211. headers("""
  212. |Forwarded: for=""
  213. """.stripMargin)
  214. ) mustEqual RemoteConnection("192.0.2.43", true, None)
  215. }
  216. "partly ignore rfc7239 header with some empty addresses" in {
  217. remoteConnectionToLocalhost(
  218. version("rfc7239") ++ trustedProxies("192.168.1.1/24", "127.0.0.1"),
  219. """
  220. |Forwarded: for=, for=
  221. |Forwarded: for=192.168.1.10, for=127.0.0.1
  222. """.stripMargin
  223. ) mustEqual RemoteConnection("192.168.1.10", false, None)
  224. }
  225. "ignore rfc7239 header field with missing = sign" in {
  226. remoteConnectionToLocalhost(
  227. version("rfc7239") ++ trustedProxies("192.168.1.1/24", "127.0.0.1"),
  228. """
  229. |Forwarded: for
  230. |Forwarded: for=192.168.1.10, for=127.0.0.1
  231. """.stripMargin
  232. ) mustEqual RemoteConnection("192.168.1.10", false, None)
  233. }
  234. "ignore rfc7239 header field with two == signs" in {
  235. remoteConnectionToLocalhost(
  236. version("rfc7239") ++ trustedProxies("192.168.1.1/24", "127.0.0.1"),
  237. """
  238. |Forwarded: for==
  239. |Forwarded: for=192.168.1.10, for=127.0.0.1
  240. """.stripMargin
  241. ) mustEqual RemoteConnection("192.168.1.10", false, None)
  242. }
  243. // This quotation handling is not RFC-compliant but we want to make sure we
  244. // at least handle the case gracefully.
  245. "don't unquote rfc7239 header field with one \" character" in {
  246. remoteConnectionToLocalhost(
  247. version("rfc7239") ++ trustedProxies("192.168.1.1/24", "127.0.0.1"),
  248. """
  249. |Forwarded: for==
  250. |Forwarded: for=192.168.1.10, for=127.0.0.1
  251. """.stripMargin
  252. ) mustEqual RemoteConnection("192.168.1.10", false, None)
  253. }
  254. // This quotation handling is not RFC-compliant but we want to make sure we
  255. // at least handle the case gracefully.
  256. "unquote and ignore rfc7239 empty quoted header field" in {
  257. remoteConnectionToLocalhost(
  258. version("rfc7239") ++ trustedProxies("192.168.1.1/24", "127.0.0.1"),
  259. """
  260. |Forwarded: for=""
  261. |Forwarded: for=192.168.1.10, for=127.0.0.1
  262. """.stripMargin
  263. ) mustEqual RemoteConnection("192.168.1.10", false, None)
  264. }
  265. // This quotation handling is not RFC-compliant but we want to make sure we
  266. // at least handle the case gracefully.
  267. "kind of unquote rfc7239 header field with three \" characters" in {
  268. remoteConnectionToLocalhost(
  269. version("rfc7239") ++ trustedProxies("192.168.1.1/24", "127.0.0.1"),
  270. """
  271. |Forwarded: for=""" + '"' + '"' + '"' + """
  272. |Forwarded: for=192.168.1.10, for=127.0.0.1
  273. """.stripMargin
  274. ) mustEqual RemoteConnection("192.168.1.10", false, None)
  275. }
  276. "default to trusting IPv4 and IPv6 localhost with x-forwarded when there is no config" in {
  277. noConfigHandler.forwardedConnection(
  278. RemoteConnection(localhost, false, None),
  279. headers("""
  280. |X-Forwarded-For: 192.0.2.43, ::1, 127.0.0.1, [::1]
  281. |X-Forwarded-Proto: https, http, http, https
  282. """.stripMargin)
  283. ) mustEqual RemoteConnection("192.0.2.43", true, None)
  284. }
  285. "trust IPv4 and IPv6 localhost with x-forwarded when there is config with default settings" in {
  286. remoteConnectionToLocalhost(
  287. version("x-forwarded"),
  288. """
  289. |X-Forwarded-For: 192.0.2.43, ::1
  290. |X-Forwarded-Proto: https, https
  291. """.stripMargin
  292. ) mustEqual RemoteConnection("192.0.2.43", true, None)
  293. }
  294. "get first untrusted proxy with x-forwarded with subnet mask" in {
  295. remoteConnectionToLocalhost(
  296. version("x-forwarded") ++ trustedProxies("192.168.1.1/24", "127.0.0.1"),
  297. """
  298. |X-Forwarded-For: 203.0.113.43, 192.168.1.43
  299. |X-Forwarded-Proto: https, http
  300. """.stripMargin
  301. ) mustEqual RemoteConnection("203.0.113.43", true, None)
  302. }
  303. "not treat the first x-forwarded entry as a proxy even if it is in trustedProxies range" in {
  304. handler(version("x-forwarded") ++ trustedProxies("192.168.1.1/24", "127.0.0.1"))
  305. .forwardedConnection(
  306. RemoteConnection(localhost, true, None),
  307. headers("""
  308. |X-Forwarded-For: 192.168.1.2, 192.168.1.3
  309. |X-Forwarded-Proto: http, http
  310. """.stripMargin)
  311. ) mustEqual RemoteConnection("192.168.1.2", false, None)
  312. }
  313. "assume http protocol with x-forwarded when proto list is missing" in {
  314. remoteConnectionToLocalhost(
  315. version("x-forwarded") ++ trustedProxies("192.168.1.1/24", "127.0.0.1"),
  316. """
  317. |X-Forwarded-For: 203.0.113.43
  318. """.stripMargin
  319. ) mustEqual RemoteConnection("203.0.113.43", false, None)
  320. }
  321. "assume http protocol with x-forwarded when proto list is shorter than for list" in {
  322. remoteConnectionToLocalhost(
  323. version("x-forwarded") ++ trustedProxies("192.168.1.1/24", "127.0.0.1"),
  324. """
  325. |X-Forwarded-For: 203.0.113.43, 192.168.1.43
  326. |X-Forwarded-Proto: https
  327. """.stripMargin
  328. ) mustEqual RemoteConnection("203.0.113.43", false, None)
  329. }
  330. "assume http protocol with x-forwarded when proto list is shorter than for list and all addresses are trusted" in {
  331. remoteConnectionToLocalhost(
  332. version("x-forwarded") ++ trustedProxies("0.0.0.0/0"),
  333. """
  334. |X-Forwarded-For: 203.0.113.43, 192.168.1.43
  335. |X-Forwarded-Proto: https
  336. """.stripMargin
  337. ) mustEqual RemoteConnection("203.0.113.43", false, None)
  338. }
  339. "assume http protocol with x-forwarded when proto list is longer than for list" in {
  340. remoteConnectionToLocalhost(
  341. version("x-forwarded") ++ trustedProxies("192.168.1.1/24", "127.0.0.1"),
  342. """
  343. |X-Forwarded-For: 203.0.113.43, 192.168.1.43
  344. |X-Forwarded-Proto: https, https, https
  345. """.stripMargin
  346. ) mustEqual RemoteConnection("203.0.113.43", false, None)
  347. }
  348. "assume http protocol with x-forwarded when proto is unrecognized" in {
  349. remoteConnectionToLocalhost(
  350. version("x-forwarded") ++ trustedProxies("127.0.0.1"),
  351. """
  352. |X-Forwarded-For: 203.0.113.43
  353. |X-Forwarded-Proto: smtp
  354. """.stripMargin
  355. ) mustEqual RemoteConnection("203.0.113.43", false, None)
  356. }
  357. "fall back to connection when single x-forwarded-for entry cannot be parsed" in {
  358. remoteConnectionToLocalhost(
  359. version("x-forwarded") ++ trustedProxies("127.0.0.1"),
  360. """
  361. |X-Forwarded-For: ???
  362. """.stripMargin
  363. ) mustEqual RemoteConnection(localhost, false, None)
  364. }
  365. // example from issue #5299
  366. "handle single unquoted IPv6 addresses in x-forwarded-for headers" in {
  367. remoteConnectionToLocalhost(
  368. version("x-forwarded") ++ trustedProxies("127.0.0.1"),
  369. """
  370. |X-Forwarded-For: ::1
  371. """.stripMargin
  372. ) mustEqual RemoteConnection("::1", false, None)
  373. }
  374. // example from RFC 7239 section 7.4
  375. "handle unquoted IPv6 addresses in x-forwarded-for headers" in {
  376. remoteConnectionToLocalhost(
  377. version("x-forwarded") ++ trustedProxies("127.0.0.1", "2001:db8:cafe::17"),
  378. """
  379. |X-Forwarded-For: 192.0.2.43, 2001:db8:cafe::17
  380. """.stripMargin
  381. ) mustEqual RemoteConnection("192.0.2.43", false, None)
  382. }
  383. // We're really forgiving about quoting for X-Forwarded-For headers,
  384. // since there isn't a real spec to follow.
  385. "handle lots of different IPv6 address quoting in x-forwarded-for headers" in {
  386. remoteConnectionToLocalhost(
  387. version("x-forwarded"),
  388. """
  389. |X-Forwarded-For: 192.0.2.43, "::1", ::1, "[::1]", [::1]
  390. """.stripMargin
  391. ) mustEqual RemoteConnection("192.0.2.43", false, None)
  392. }
  393. // We're really forgiving about quoting for X-Forwarded-For headers,
  394. // since there isn't a real spec to follow.
  395. "handle lots of different IPv6 address and proto quoting in x-forwarded-for headers" in {
  396. remoteConnectionToLocalhost(
  397. version("x-forwarded"),
  398. """
  399. |X-Forwarded-For: 192.0.2.43, "::1", ::1, "[::1]", [::1]
  400. |X-Forwarded-Proto: "https", http, http, "http", http
  401. """.stripMargin
  402. ) mustEqual RemoteConnection("192.0.2.43", true, None)
  403. }
  404. "ignore x-forward header with empty addresses" in {
  405. handler(version("x-forwarded") ++ trustedProxies("192.0.2.43"))
  406. .forwardedConnection(
  407. RemoteConnection("192.0.2.43", true, None),
  408. headers("""
  409. |X-Forwarded-For: ,,
  410. """.stripMargin)
  411. ) mustEqual RemoteConnection("192.0.2.43", true, None)
  412. }
  413. "partly ignore x-forward header with some empty addresses" in {
  414. remoteConnectionToLocalhost(version("x-forwarded"), """
  415. |X-Forwarded-For: ,,192.0.2.43
  416. """.stripMargin) mustEqual RemoteConnection("192.0.2.43", false, None)
  417. }
  418. "return the first address if all addresses are trusted with RFC 7239" in {
  419. remoteConnectionToLocalhost(
  420. version("rfc7239") ++ trustedProxies("192.168.1.1/24", "127.0.0.1"),
  421. """
  422. |Forwarded: for=192.168.1.12, for=192.168.1.10, for=127.0.0.1
  423. """.stripMargin
  424. ) mustEqual RemoteConnection("192.168.1.12", false, None)
  425. }
  426. "return the first address if all addresses are trusted with X-Forwarded-For" in {
  427. remoteConnectionToLocalhost(
  428. version("x-forwarded") ++ trustedProxies("192.168.1.1/24", "127.0.0.1"),
  429. """
  430. |X-Forwarded-For: 192.168.1.12, "192.168.1.10", 127.0.0.1
  431. |X-Forwarded-Proto: http, http, http
  432. """.stripMargin
  433. ) mustEqual RemoteConnection("192.168.1.12", false, None)
  434. }
  435. }
  436. def noConfigHandler =
  437. new ForwardedHeaderHandler(ForwardedHeaderHandlerConfig(None))
  438. def handler(config: Map[String, Any]) =
  439. new ForwardedHeaderHandler(
  440. ForwardedHeaderHandlerConfig(Some(Configuration.from(config).withFallback(Configuration.reference)))
  441. )
  442. def remoteConnectionToLocalhost(config: Map[String, Any], headersText: String): RemoteConnection =
  443. handler(config).forwardedConnection(RemoteConnection("127.0.0.1", false, None), headers(headersText))
  444. def version(s: String) = {
  445. Map("play.http.forwarded.version" -> s)
  446. }
  447. def trustedProxies(s: String*) = {
  448. Map("play.http.forwarded.trustedProxies" -> s)
  449. }
  450. def headers(s: String): Headers = {
  451. def split(s: String, regex: String): Option[(String, String)] = s.split(regex, 2).toList match {
  452. case k :: v :: Nil => Some(k -> v)
  453. case _ => None
  454. }
  455. new Headers(s.split("\r?\n").toSeq.flatMap(split(_, ":\\s*")))
  456. }
  457. def processHeaders(
  458. config: Map[String, Any],
  459. headers: Headers
  460. ): Seq[(ForwardedEntry, Either[String, ParsedForwardedEntry], Option[Boolean])] = {
  461. val configuration = ForwardedHeaderHandlerConfig(Some(Configuration.from(config)))
  462. configuration.forwardedHeaders(headers).map { forwardedEntry =>
  463. val errorOrConnection = configuration.parseEntry(forwardedEntry)
  464. val trusted = errorOrConnection match {
  465. case Left(_) => None
  466. case Right(connection) => Some(configuration.isTrustedProxy(connection.address))
  467. }
  468. (forwardedEntry, errorOrConnection, trusted)
  469. }
  470. }
  471. def addr(ip: String): InetAddress = InetAddresses.forString(ip)
  472. val localhost: InetAddress = addr("127.0.0.1")
  473. }