@@ -6,18 +6,35 @@ import no.nav.security.mock.oauth2.extensions.endsWith
6
6
private val log = KotlinLogging .logger { }
7
7
8
8
typealias RequestHandler = (OAuth2HttpRequest ) -> OAuth2HttpResponse
9
- internal typealias ExceptionHandler = (OAuth2HttpRequest , Throwable ) -> OAuth2HttpResponse
9
+
10
+ interface Interceptor
11
+
12
+ fun interface RequestInterceptor : Interceptor {
13
+ fun intercept (request : OAuth2HttpRequest ): OAuth2HttpRequest
14
+ }
15
+
16
+ fun interface ResponseInterceptor : Interceptor {
17
+ fun intercept (request : OAuth2HttpRequest , response : OAuth2HttpResponse ): OAuth2HttpResponse
18
+ }
10
19
11
20
interface Route : RequestHandler {
21
+
12
22
fun match (request : OAuth2HttpRequest ): Boolean
13
23
14
24
class Builder {
15
25
private val routes: MutableList <Route > = mutableListOf ()
26
+ private val interceptors: MutableList <Interceptor > = mutableListOf ()
16
27
17
28
private var exceptionHandler: ExceptionHandler = { _, throwable ->
18
29
throw throwable
19
30
}
20
31
32
+ fun interceptors (vararg interceptor : Interceptor ) = apply {
33
+ interceptor.forEach {
34
+ interceptors.add(it)
35
+ }
36
+ }
37
+
21
38
fun attach (vararg route : Route ) = apply {
22
39
route.forEach {
23
40
routes.add(it)
@@ -56,40 +73,64 @@ interface Route : RequestHandler {
56
73
routes.add(routeFromPathAndMethod(path, method, requestHandler))
57
74
}
58
75
59
- fun build (): Route = object : PathRoute {
60
- override fun matchPath (request : OAuth2HttpRequest ): Boolean =
61
- routes.any { it.matchPath(request) }
62
-
63
- override fun match (request : OAuth2HttpRequest ): Boolean =
64
- routes.firstOrNull { it.match(request) } != null
65
-
66
- override fun invoke (request : OAuth2HttpRequest ): OAuth2HttpResponse =
67
- try {
68
- routes.firstOrNull { it.match(request) }?.invoke(request) ? : noMatch(request)
69
- } catch (t: Throwable ) {
70
- exceptionHandler(request, t)
71
- }
72
-
73
- override fun toString (): String = routes.toString()
74
-
75
- private fun noMatch (request : OAuth2HttpRequest ): OAuth2HttpResponse {
76
- log.debug(" no route matching url=${request.url} with method=${request.method} " )
77
- return if (matchPath(request)) {
78
- methodNotAllowed()
79
- } else {
80
- notFound(" no routes found" )
81
- }
82
- }
83
-
84
- private fun Route.matchPath (request : OAuth2HttpRequest ): Boolean = (this as ? PathRoute )?.matchPath(request) ? : false
85
- }
76
+ fun build (): Route = PathRouter (routes, interceptors, exceptionHandler)
86
77
}
87
78
}
88
79
80
+ internal typealias ExceptionHandler = (OAuth2HttpRequest , Throwable ) -> OAuth2HttpResponse
81
+
89
82
internal interface PathRoute : Route {
90
83
fun matchPath (request : OAuth2HttpRequest ): Boolean
91
84
}
92
85
86
+ internal class PathRouter (
87
+ private val routes : MutableList <Route >,
88
+ private val interceptors : MutableList <Interceptor >,
89
+ private val exceptionHandler : ExceptionHandler ,
90
+ ) : PathRoute {
91
+
92
+ override fun matchPath (request : OAuth2HttpRequest ): Boolean = routes.any { it.matchPath(request) }
93
+ override fun match (request : OAuth2HttpRequest ): Boolean = routes.firstOrNull { it.match(request) } != null
94
+
95
+ override fun invoke (request : OAuth2HttpRequest ): OAuth2HttpResponse = runCatching {
96
+ routes.findHandler(request).invokeWith(request, interceptors)
97
+ }.getOrElse {
98
+ exceptionHandler(request, it)
99
+ }
100
+
101
+ override fun toString (): String = routes.toString()
102
+
103
+ private fun MutableList<Route>.findHandler (request : OAuth2HttpRequest ): RequestHandler =
104
+ this .firstOrNull { it.match(request) } ? : { req -> noMatch(req) }
105
+
106
+ private fun RequestHandler.invokeWith (request : OAuth2HttpRequest , interceptors : MutableList <Interceptor >): OAuth2HttpResponse {
107
+ return if (interceptors.size > 0 ) {
108
+
109
+ val filteredRequest = interceptors.filterIsInstance<RequestInterceptor >().fold(request) { next, interceptor ->
110
+ interceptor.intercept(next)
111
+ }
112
+ val res = this .invoke(filteredRequest)
113
+ val filteredResponse = interceptors.filterIsInstance<ResponseInterceptor >().fold(res.copy()) { next, interceptor ->
114
+ interceptor.intercept(request, next)
115
+ }
116
+ filteredResponse
117
+ } else {
118
+ this .invoke(request)
119
+ }
120
+ }
121
+
122
+ private fun noMatch (request : OAuth2HttpRequest ): OAuth2HttpResponse {
123
+ log.debug(" no route matching url=${request.url} with method=${request.method} " )
124
+ return if (matchPath(request)) {
125
+ methodNotAllowed()
126
+ } else {
127
+ notFound(" no routes found" )
128
+ }
129
+ }
130
+
131
+ private fun Route.matchPath (request : OAuth2HttpRequest ): Boolean = (this as ? PathRoute )?.matchPath(request) ? : false
132
+ }
133
+
93
134
fun routes (vararg route : Route ): Route = routes {
94
135
attach(* route)
95
136
}
0 commit comments