From f0839587a2db6cf82796fd040bd9d4871135f692 Mon Sep 17 00:00:00 2001 From: satorg Date: Wed, 12 Aug 2020 00:59:31 -0700 Subject: [PATCH] add `Obfuscated.fromString`; improve `Element` --- .../scala/org/http4s/headers/Forwarded.scala | 38 +++++++++++++++++-- .../http4s/parser/ForwardedModelParsing.scala | 14 ++++++- .../org/http4s/headers/ForwardedSpec.scala | 15 ++++++++ 3 files changed, 63 insertions(+), 4 deletions(-) diff --git a/core/src/main/scala/org/http4s/headers/Forwarded.scala b/core/src/main/scala/org/http4s/headers/Forwarded.scala index 6f6516674e3..739e4b00292 100644 --- a/core/src/main/scala/org/http4s/headers/Forwarded.scala +++ b/core/src/main/scala/org/http4s/headers/Forwarded.scala @@ -23,7 +23,7 @@ object Forwarded object Node { def apply(nodeName: Name, nodePort: Port): Node = apply(nodeName, Some(nodePort)) - sealed trait Name + sealed trait Name { _: Product => } object Name { final case class Ipv4(address: Uri.Ipv4Address) extends Name @@ -40,7 +40,7 @@ object Forwarded : Name = apply(Uri.Ipv6Address(a, b, c, d, e, f, g, h)) } - sealed trait Port + sealed trait Port { _: Product => } object Port { final case class Num(value: Int) extends Port @@ -52,7 +52,26 @@ object Forwarded } } - final case class Obfuscated(value: String) extends Name with Port + sealed trait Obfuscated extends Name with Port { _: Product => + + /** + * Obfuscated value doesn't include the leading underscore symbol. + */ + def value: String + } + object Obfuscated { + def fromString(s: String): ParseResult[Obfuscated] = + new ModelNodeObfuscatedValueParser(s).parse.map(apply) + + def unapply(o: Obfuscated): Option[String] = Some(o.value) + + // Referenced by model parsers. + private[http4s] def apply(s: String): Obfuscated = C(s) + + private[this] final case class C(value: String) extends Obfuscated { + override def productPrefix: String = "Obfuscated" + } + } def fromString(s: String): ParseResult[Node] = new ModelNodeParser(s).parse } @@ -78,6 +97,11 @@ object Forwarded def withFor(value: Node): Element def withHost(value: Host): Element def withProto(value: Proto): Element + + def withoutBy: Element + def withoutFor: Element + def withoutHost: Element + def withoutProto: Element } /** @@ -101,6 +125,11 @@ object Forwarded def withHost(value: Host): Element = copy(`host` = Some(value)) def withProto(value: Proto): Element = copy(`proto` = Some(value)) + def withoutBy: Element = copy(`by` = None) + def withoutFor: Element = copy(`for` = None) + def withoutHost: Element = copy(`host` = None) + def withoutProto: Element = copy(`proto` = None) + override def productPrefix: String = "Element" } @@ -108,6 +137,9 @@ object Forwarded def withFor(value: Node): Element = C(`for` = Some(value)) def withHost(value: Host): Element = C(`host` = Some(value)) def withProto(value: Proto): Element = C(`proto` = Some(value)) + + def unapply(elem: Element): Option[(Option[Node], Option[Node], Option[Host], Option[Proto])] = + Some((elem.`by`, elem.`for`, elem.`host`, elem.`proto`)) } override def parse(s: String): ParseResult[Forwarded] = HttpHeaderParser.FORWARDED(s) diff --git a/core/src/main/scala/org/http4s/parser/ForwardedModelParsing.scala b/core/src/main/scala/org/http4s/parser/ForwardedModelParsing.scala index 1ca1597bec7..4ce3bcfdb3c 100644 --- a/core/src/main/scala/org/http4s/parser/ForwardedModelParsing.scala +++ b/core/src/main/scala/org/http4s/parser/ForwardedModelParsing.scala @@ -46,9 +46,14 @@ private[http4s] trait ForwardedModelParsing { model: Forwarded.type => } | ModelNodeObfuscated } + protected final def ModelNodeObfuscatedValue: Rule1[String] = + rule { + capture(oneOrMore(AlphaNum | '.' | '_' | '-')) + } + protected final def ModelNodeObfuscated: Rule1[model.Node.Obfuscated] = rule { - '_' ~ capture(oneOrMore(AlphaNum | '.' | '_' | '-')) ~> model.Node.Obfuscated + '_' ~ ModelNodeObfuscatedValue ~> { model.Node.Obfuscated(_: String) } } protected final def ModelHost: Rule1[model.Host] = @@ -64,6 +69,13 @@ private[http4s] trait ForwardedModelParsing { model: Forwarded.type => override def main: Rule1[Node] = rule(ModelNode ~ EOI) } + protected final class ModelNodeObfuscatedValueParser(s: String) + extends Http4sParser[String](s, "invalid obfuscated value") + with ModelParsers { + + override def main: Rule1[String] = rule(ModelNodeObfuscatedValue ~ EOI) + } + protected final class ModelHostParser(s: String) extends Http4sParser[model.Host](s, "invalid host") with ModelParsers { diff --git a/tests/src/test/scala/org/http4s/headers/ForwardedSpec.scala b/tests/src/test/scala/org/http4s/headers/ForwardedSpec.scala index 8c9457a9262..7f7c2460dfb 100644 --- a/tests/src/test/scala/org/http4s/headers/ForwardedSpec.scala +++ b/tests/src/test/scala/org/http4s/headers/ForwardedSpec.scala @@ -64,6 +64,21 @@ class ForwardedSpec extends Specification with Tables { } } } + "Node.Obfuscated" >> { + import Forwarded.Node.Obfuscated + + "parse valid obfuscated values" in { + val validValues = Seq( + "1", + "a", + "_" + ) + + Result.foreach(validValues) { value => + Obfuscated.fromString(value) must beRight((_: Obfuscated).value must_=== value) + } + } + } "Host" >> { import Forwarded.Host