-
Notifications
You must be signed in to change notification settings - Fork 786
/
Forwarded.scala
153 lines (118 loc) · 5.01 KB
/
Forwarded.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
* Copyright 2013-2020 http4s.org
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.http4s.headers
import java.net.{Inet4Address, Inet6Address}
import cats.data.NonEmptyList
import org.http4s.parser.{ForwardedModelParsing, HttpHeaderParser}
import org.http4s.util.Writer
import org.http4s.{Header, HeaderKey, ParseResult, Uri}
object Forwarded
extends HeaderKey.Internal[Forwarded]
with HeaderKey.Recurring
with ForwardedModelParsing {
final case class Node(nodeName: Node.Name, nodePort: Option[Node.Port] = None)
object Node {
def apply(nodeName: Name, nodePort: Port): Node = apply(nodeName, Some(nodePort))
sealed trait Name { _: Product => }
object Name {
final case class Ipv4(address: Uri.Ipv4Address) extends Name
final case class Ipv6(address: Uri.Ipv6Address) extends Name
case object Unknown extends Name
def apply(address: Uri.Ipv4Address): Name = Name.Ipv4(address)
def apply(address: Inet4Address): Name = apply(Uri.Ipv4Address.fromInet4Address(address))
def apply(a: Byte, b: Byte, c: Byte, d: Byte): Name = apply(Uri.Ipv4Address(a, b, c, d))
def apply(address: Uri.Ipv6Address): Name = Name.Ipv6(address)
def apply(address: Inet6Address): Name = apply(Uri.Ipv6Address.fromInet6Address(address))
def apply(a: Short, b: Short, c: Short, d: Short, e: Short, f: Short, g: Short, h: Short)
: Name = apply(Uri.Ipv6Address(a, b, c, d, e, f, g, h))
}
sealed trait Port { _: Product => }
object Port {
final case class Num(value: Int) extends Port
def apply(num: Int): Port = Port.Num(num)
def unapply(port: Port): Option[Int] =
PartialFunction.condOpt(port) {
case Num(num) => num
}
}
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
}
final case class Host(host: Uri.Host, port: Option[Int] = None)
object Host {
def apply(host: Uri.Host, port: Int): Host = apply(host, Some(port))
def fromString(s: String): ParseResult[Host] = new ModelHostParser(s).parse
}
type Proto = Uri.Scheme
val Proto: Uri.Scheme.type = Uri.Scheme
sealed trait Element extends Product {
def `by`: Option[Node]
def `for`: Option[Node]
def `host`: Option[Host]
def `proto`: Option[Proto]
def withBy(value: Node): Element
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
}
/**
* Enables the following construction syntax (while preserving type safety and consistency):
* {{{
* Element.`by=`(<by-node>).`for=`(<for-node>).`host=`(<host>).`proto=`(<schema>)`
* }}}
*/
object Element {
// Since at least one of the fields must be set to `Some`,
// the `Element` trait implementation is hidden.
private[this] final case class C(
`by`: Option[Node] = None,
`for`: Option[Node] = None,
`host`: Option[Host] = None,
`proto`: Option[Proto] = None)
extends Element {
def withBy(value: Node): Element = copy(`by` = Some(value))
def withFor(value: Node): Element = copy(`for` = Some(value))
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"
}
def withBy(value: Node): Element = C(`by` = Some(value))
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)
}
final case class Forwarded(values: NonEmptyList[Forwarded.Element]) extends Header.Recurring {
override type Value = Forwarded.Element
override def key: Forwarded.type = Forwarded
override def renderValue(writer: Writer): writer.type = ???
}