Skip to content

Commit 975fad4

Browse files
committed
refactor: allows handler type to be specified.
1 parent 7833377 commit 975fad4

File tree

9 files changed

+115
-16
lines changed

9 files changed

+115
-16
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright (c) 2025.
3+
*
4+
* This file is part of Imposter.
5+
*
6+
* "Commons Clause" License Condition v1.0
7+
*
8+
* The Software is provided to you by the Licensor under the License, as
9+
* defined below, subject to the following condition.
10+
*
11+
* Without limiting other conditions in the License, the grant of rights
12+
* under the License will not include, and the License does not grant to
13+
* you, the right to Sell the Software.
14+
*
15+
* For purposes of the foregoing, "Sell" means practicing any or all of
16+
* the rights granted to you under the License to provide to third parties,
17+
* for a fee or other consideration (including without limitation fees for
18+
* hosting or consulting/support services related to the Software), a
19+
* product or service whose value derives, entirely or substantially, from
20+
* the functionality of the Software. Any license notice or attribution
21+
* required by the License must also include this Commons Clause License
22+
* Condition notice.
23+
*
24+
* Software: Imposter
25+
*
26+
* License: GNU Lesser General Public License version 3
27+
*
28+
* Licensor: Peter Cornish
29+
*
30+
* Imposter is free software: you can redistribute it and/or modify
31+
* it under the terms of the GNU Lesser General Public License as published by
32+
* the Free Software Foundation, either version 3 of the License, or
33+
* (at your option) any later version.
34+
*
35+
* Imposter is distributed in the hope that it will be useful,
36+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
37+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
38+
* GNU Lesser General Public License for more details.
39+
*
40+
* You should have received a copy of the GNU Lesser General Public License
41+
* along with Imposter. If not, see <https://www.gnu.org/licenses/>.
42+
*/
43+
44+
package io.gatehill.imposter.model
45+
46+
/**
47+
* Represents the type of handler.
48+
*/
49+
enum class HandlerType {
50+
INTERCEPTOR,
51+
RESOURCE,
52+
}

core/api/src/main/java/io/gatehill/imposter/service/HandlerService.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import io.gatehill.imposter.http.HttpExchangeFutureHandler
4848
import io.gatehill.imposter.http.HttpExchangeHandler
4949
import io.gatehill.imposter.http.HttpRouter
5050
import io.gatehill.imposter.http.ResourceMatcher
51+
import io.gatehill.imposter.model.HandlerType
5152
import io.gatehill.imposter.plugin.config.PluginConfig
5253
import io.gatehill.imposter.plugin.config.resource.BasicResourceConfig
5354
import io.gatehill.imposter.server.ServerFactory
@@ -71,12 +72,14 @@ interface HandlerService {
7172
* @param allPluginConfigs all plugin configurations
7273
* @param resourceMatcher the [ResourceMatcher] to use
7374
* @param httpExchangeHandler the consumer of the [HttpExchange]
75+
* @param handlerType the type of handler to build
7476
* @return the handler
7577
*/
7678
fun build(
7779
imposterConfig: ImposterConfig,
7880
allPluginConfigs: List<PluginConfig>,
7981
resourceMatcher: ResourceMatcher,
82+
handlerType: HandlerType = HandlerType.RESOURCE,
8083
httpExchangeHandler: HttpExchangeFutureHandler,
8184
): HttpExchangeFutureHandler
8285

@@ -87,6 +90,7 @@ interface HandlerService {
8790
imposterConfig: ImposterConfig,
8891
allPluginConfigs: List<PluginConfig>,
8992
resourceMatcher: ResourceMatcher,
93+
handlerType: HandlerType = HandlerType.RESOURCE,
9094
httpExchangeHandler: HttpExchangeHandler,
9195
): HttpExchangeFutureHandler
9296

@@ -111,14 +115,16 @@ interface HandlerService {
111115
imposterConfig: ImposterConfig,
112116
pluginConfig: PluginConfig,
113117
resourceMatcher: ResourceMatcher,
118+
handlerType: HandlerType = HandlerType.RESOURCE,
114119
httpExchangeHandler: HttpExchangeFutureHandler,
115120
): HttpExchangeFutureHandler
116121

117122
fun build(
118123
imposterConfig: ImposterConfig,
119124
pluginConfig: PluginConfig,
120125
resourceConfig: BasicResourceConfig,
121-
httpExchangeHandler: HttpExchangeFutureHandler
126+
handlerType: HandlerType = HandlerType.RESOURCE,
127+
httpExchangeHandler: HttpExchangeFutureHandler,
122128
): HttpExchangeFutureHandler
123129

124130
/**
@@ -128,6 +134,7 @@ interface HandlerService {
128134
imposterConfig: ImposterConfig,
129135
pluginConfig: PluginConfig,
130136
resourceMatcher: ResourceMatcher,
137+
handlerType: HandlerType = HandlerType.RESOURCE,
131138
httpExchangeHandler: HttpExchangeHandler,
132139
): HttpExchangeFutureHandler
133140

core/api/src/main/java/io/gatehill/imposter/util/ResourceUtil.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import java.util.regex.Pattern
5454
* @author Pete Cornish
5555
*/
5656
object ResourceUtil {
57+
const val RC_LAST_HANDLER_TYPE = "handler.type"
5758
const val RESOURCE_CONFIG_KEY = "io.gatehill.imposter.resourceConfig"
5859
const val RC_REQUEST_ID_KEY = "request.id"
5960
const val RC_SEND_NOT_FOUND_RESPONSE = "response.sendNotFoundResponse"

core/engine/src/main/java/io/gatehill/imposter/Imposter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ class Imposter(
203203
imposterConfig,
204204
allConfigs,
205205
resourceMatcher,
206-
serverFactory.createMetricsHandler()
206+
httpExchangeHandler = serverFactory.createMetricsHandler()
207207
)
208208
)
209209
}

core/engine/src/main/java/io/gatehill/imposter/plugin/config/ConfiguredPlugin.kt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,10 @@ abstract class ConfiguredPlugin<T : BasicPluginConfig> @Inject constructor(
119119
configureResourceRoutes(router)
120120
}
121121

122-
fun configureInterceptorRoutes(router: HttpRouter) {
123-
configs.forEach { config ->
124-
if (config is InterceptorsHolder<*>) {
125-
config.interceptors?.forEach { interceptor ->
126-
interceptorService.configureInterceptorRoute(router, config, interceptor)
127-
}
122+
fun configureInterceptorRoutes(router: HttpRouter) = configs.forEach { config ->
123+
if (config is InterceptorsHolder<*>) {
124+
config.interceptors?.forEach { interceptor ->
125+
interceptorService.configureInterceptorRoute(router, config, interceptor)
128126
}
129127
}
130128
}

core/engine/src/main/java/io/gatehill/imposter/service/HandlerServiceImpl.kt

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import io.gatehill.imposter.config.util.EnvVars
4949
import io.gatehill.imposter.http.*
5050
import io.gatehill.imposter.lifecycle.SecurityLifecycleHooks
5151
import io.gatehill.imposter.lifecycle.SecurityLifecycleListener
52+
import io.gatehill.imposter.model.HandlerType
5253
import io.gatehill.imposter.plugin.config.PluginConfig
5354
import io.gatehill.imposter.plugin.config.ResourcesHolder
5455
import io.gatehill.imposter.plugin.config.resource.BasicResourceConfig
@@ -87,23 +88,26 @@ class HandlerServiceImpl @Inject constructor(
8788
imposterConfig: ImposterConfig,
8889
allPluginConfigs: List<PluginConfig>,
8990
resourceMatcher: ResourceMatcher,
91+
handlerType: HandlerType,
9092
httpExchangeHandler: HttpExchangeFutureHandler,
9193
): HttpExchangeFutureHandler {
9294
val selectedConfig = securityService.findConfigPreferringSecurityPolicy(allPluginConfigs)
93-
return build(imposterConfig, selectedConfig, resourceMatcher, httpExchangeHandler)
95+
return build(imposterConfig, selectedConfig, resourceMatcher, handlerType, httpExchangeHandler)
9496
}
9597

9698
override fun build(
9799
imposterConfig: ImposterConfig,
98100
pluginConfig: PluginConfig,
99101
resourceMatcher: ResourceMatcher,
102+
handlerType: HandlerType,
100103
httpExchangeHandler: HttpExchangeFutureHandler,
101104
): HttpExchangeFutureHandler {
102105
val resolvedResourceConfigs = resolveResourceConfigs(pluginConfig)
103106
return { httpExchange: HttpExchange ->
104107
future {
105108
handle(
106109
pluginConfig,
110+
handlerType,
107111
httpExchangeHandler,
108112
httpExchange,
109113
resolvedResourceConfigs,
@@ -117,13 +121,15 @@ class HandlerServiceImpl @Inject constructor(
117121
imposterConfig: ImposterConfig,
118122
pluginConfig: PluginConfig,
119123
resourceConfig: BasicResourceConfig,
124+
handlerType: HandlerType,
120125
httpExchangeHandler: HttpExchangeFutureHandler,
121126
): HttpExchangeFutureHandler {
122127
val resolvedResourceConfigs = resolveResourceConfigs(pluginConfig)
123128
return { httpExchange: HttpExchange ->
124129
future {
125130
handle(
126131
pluginConfig,
132+
handlerType,
127133
httpExchangeHandler,
128134
httpExchange,
129135
resolvedResourceConfigs,
@@ -137,17 +143,19 @@ class HandlerServiceImpl @Inject constructor(
137143
imposterConfig: ImposterConfig,
138144
allPluginConfigs: List<PluginConfig>,
139145
resourceMatcher: ResourceMatcher,
146+
handlerType: HandlerType,
140147
httpExchangeHandler: HttpExchangeHandler,
141148
): HttpExchangeFutureHandler =
142-
build(imposterConfig, allPluginConfigs, resourceMatcher, wrapInFuture(httpExchangeHandler))
149+
build(imposterConfig, allPluginConfigs, resourceMatcher, handlerType, wrapInFuture(httpExchangeHandler))
143150

144151
override fun buildAndWrap(
145152
imposterConfig: ImposterConfig,
146153
pluginConfig: PluginConfig,
147154
resourceMatcher: ResourceMatcher,
155+
handlerType: HandlerType,
148156
httpExchangeHandler: HttpExchangeHandler,
149157
): HttpExchangeFutureHandler =
150-
build(imposterConfig, pluginConfig, resourceMatcher, wrapInFuture(httpExchangeHandler))
158+
build(imposterConfig, pluginConfig, resourceMatcher, handlerType, wrapInFuture(httpExchangeHandler))
151159

152160
/**
153161
* Wraps the given [httpExchangeHandler] in a [HttpExchangeFutureHandler] and returns the future.
@@ -162,8 +170,9 @@ class HandlerServiceImpl @Inject constructor(
162170

163171
override fun buildNotFoundExceptionHandler() = { httpExchange: HttpExchange ->
164172
if (
165-
null == httpExchange.get(ResourceUtil.RC_REQUEST_ID_KEY) ||
166-
httpExchange.get<Boolean>(ResourceUtil.RC_SEND_NOT_FOUND_RESPONSE) == true
173+
null == httpExchange.get(ResourceUtil.RC_REQUEST_ID_KEY)
174+
|| httpExchange.get<Boolean>(ResourceUtil.RC_SEND_NOT_FOUND_RESPONSE) == true
175+
|| httpExchange.get<HandlerType>(ResourceUtil.RC_LAST_HANDLER_TYPE) == HandlerType.INTERCEPTOR
167176
) {
168177
// only override response processing if the 404 did not originate from the mock engine
169178
// otherwise this will attempt to send a duplicate response to an already completed
@@ -260,6 +269,7 @@ class HandlerServiceImpl @Inject constructor(
260269

261270
private suspend fun handle(
262271
pluginConfig: PluginConfig,
272+
handlerType: HandlerType,
263273
httpExchangeHandler: HttpExchangeFutureHandler,
264274
httpExchange: HttpExchange,
265275
resourceConfigs: List<ResolvedResourceConfig>,
@@ -272,6 +282,7 @@ class HandlerServiceImpl @Inject constructor(
272282

273283
handle(
274284
pluginConfig,
285+
handlerType,
275286
httpExchangeHandler,
276287
httpExchange,
277288
resourceConfigs,
@@ -287,12 +298,15 @@ class HandlerServiceImpl @Inject constructor(
287298

288299
private suspend fun handle(
289300
pluginConfig: PluginConfig,
301+
handlerType: HandlerType,
290302
httpExchangeHandler: HttpExchangeFutureHandler,
291303
httpExchange: HttpExchange,
292304
resourceConfigs: List<ResolvedResourceConfig>,
293305
resourceConfig: BasicResourceConfig,
294306
) {
295307
try {
308+
httpExchange.put(ResourceUtil.RC_LAST_HANDLER_TYPE, handlerType)
309+
296310
val rootResourceConfig = pluginConfig as BasicResourceConfig
297311
val response = httpExchange.response
298312

core/engine/src/main/java/io/gatehill/imposter/service/InterceptorServiceImpl.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ package io.gatehill.imposter.service
4646
import io.gatehill.imposter.ImposterConfig
4747
import io.gatehill.imposter.http.HttpExchange
4848
import io.gatehill.imposter.http.HttpRouter
49+
import io.gatehill.imposter.model.HandlerType
4950
import io.gatehill.imposter.plugin.config.PluginConfig
5051
import io.gatehill.imposter.plugin.config.resource.BasicResourceConfig
5152
import io.gatehill.imposter.script.ResponseBehaviourType
@@ -74,7 +75,12 @@ class InterceptorServiceImpl @Inject constructor(
7475
pluginConfig: PluginConfig,
7576
interceptor: BasicResourceConfig,
7677
) {
77-
val routeHandler = handlerService.build(imposterConfig, pluginConfig, interceptor) { exchange ->
78+
val routeHandler = handlerService.build(
79+
imposterConfig,
80+
pluginConfig,
81+
interceptor,
82+
handlerType = HandlerType.INTERCEPTOR
83+
) { exchange ->
7884
future {
7985
val handler = buildHandler(exchange)
8086
responseRoutingService.route(

server/src/test/java/io/gatehill/imposter/server/InterceptorTest.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,21 @@ class InterceptorTest : BaseVerticleTest() {
8989
}
9090

9191
@Test
92-
fun `interceptors should be skipped`() {
92+
fun `no matching interceptor`() {
9393
RestAssured.given().`when`()
94-
.get("/example")
94+
.get("/no-interceptor-match")
9595
.then()
9696
.statusCode(200)
9797
.body(equalTo("default"))
9898
}
99+
100+
@Test
101+
fun `interceptor has same path as resource`() {
102+
RestAssured.given().`when`()
103+
.get("/example")
104+
.then()
105+
.statusCode(200)
106+
.body(equalTo("example"))
107+
.header("X-Interceptor", "example")
108+
}
99109
}

server/src/test/resources/interceptor/test-plugin-config.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,18 @@ interceptors:
1616
var req = stores.open("request");
1717
req.save("response", "passthrough");
1818
19+
- path: /example
20+
continue: true
21+
response:
22+
headers:
23+
X-Interceptor: "example"
24+
1925
resources:
26+
- path: /example
27+
method: GET
28+
response:
29+
content: "example"
30+
2031
- path: /*
2132
method: GET
2233
response:

0 commit comments

Comments
 (0)