Skip to content

Commit 8a796d5

Browse files
authored
Merge pull request #3014 from lhcopetti/include-client-ip-matcher
Expose native matcher for client ip address
2 parents ec515d7 + b632839 commit 8a796d5

File tree

9 files changed

+113
-8
lines changed

9 files changed

+113
-8
lines changed

src/main/java/com/github/tomakehurst/wiremock/client/BasicMappingBuilder.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2011-2024 Thomas Akehurst
2+
* Copyright (C) 2011-2025 Thomas Akehurst
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -93,6 +93,12 @@ public MappingBuilder withPort(int port) {
9393
return this;
9494
}
9595

96+
@Override
97+
public MappingBuilder withClientIp(StringValuePattern clientIpPattern) {
98+
requestPatternBuilder.withClientIp(clientIpPattern);
99+
return this;
100+
}
101+
96102
@Override
97103
public BasicMappingBuilder withHeader(String key, StringValuePattern headerPattern) {
98104
requestPatternBuilder.withHeader(key, headerPattern);

src/main/java/com/github/tomakehurst/wiremock/client/MappingBuilder.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2011-2024 Thomas Akehurst
2+
* Copyright (C) 2011-2025 Thomas Akehurst
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -33,6 +33,8 @@ public interface MappingBuilder {
3333

3434
MappingBuilder withPort(int port);
3535

36+
MappingBuilder withClientIp(StringValuePattern hostPattern);
37+
3638
MappingBuilder atPriority(Integer priority);
3739

3840
MappingBuilder withHeader(String key, StringValuePattern headerPattern);

src/main/java/com/github/tomakehurst/wiremock/matching/RequestPattern.java

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2011-2024 Thomas Akehurst
2+
* Copyright (C) 2011-2025 Thomas Akehurst
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -46,6 +46,7 @@ public class RequestPattern implements NamedValueMatcher<Request> {
4646
private final String scheme;
4747
private final StringValuePattern host;
4848
private final Integer port;
49+
private final StringValuePattern clientIp;
4950
private final UrlPattern url;
5051
private final RequestMethod method;
5152
private final Map<String, MultiValuePattern> headers;
@@ -66,6 +67,7 @@ public RequestPattern(
6667
final String scheme,
6768
final StringValuePattern host,
6869
final Integer port,
70+
final StringValuePattern clientIp,
6971
final UrlPattern url,
7072
final RequestMethod method,
7173
final Map<String, MultiValuePattern> headers,
@@ -81,6 +83,7 @@ public RequestPattern(
8183
this.scheme = scheme;
8284
this.host = host;
8385
this.port = port;
86+
this.clientIp = clientIp;
8487
this.url = getFirstNonNull(url, UrlPattern.ANY);
8588
this.method = getFirstNonNull(method, RequestMethod.ANY);
8689
this.headers = headers;
@@ -103,6 +106,7 @@ public MatchResult match(Request request) {
103106
requestPartMatchResults.add(weight(schemeMatches(request), 3.0));
104107
requestPartMatchResults.add(weight(hostMatches(request), 10.0));
105108
requestPartMatchResults.add(weight(portMatches(request), 10.0));
109+
requestPartMatchResults.add(weight(clientIpMatches(request), 3.0));
106110
requestPartMatchResults.add(
107111
weight(RequestPattern.this.url.match(request.getUrl()), 10.0));
108112
requestPartMatchResults.add(
@@ -146,6 +150,7 @@ public RequestPattern(
146150
@JsonProperty("host") StringValuePattern host,
147151
@JsonProperty("port") Integer port,
148152
@JsonProperty("url") String url,
153+
@JsonProperty("clientIp") StringValuePattern clientIp,
149154
@JsonProperty("urlPattern") String urlPattern,
150155
@JsonProperty("urlPath") String urlPath,
151156
@JsonProperty("urlPathPattern") String urlPathPattern,
@@ -165,6 +170,7 @@ public RequestPattern(
165170
scheme,
166171
host,
167172
port,
173+
clientIp,
168174
UrlPattern.fromOneOf(url, urlPattern, urlPath, urlPathPattern, urlPathTemplate),
169175
method,
170176
headers,
@@ -184,6 +190,7 @@ public RequestPattern(
184190
null,
185191
null,
186192
null,
193+
null,
187194
anyUrl(),
188195
RequestMethod.ANY,
189196
null,
@@ -202,6 +209,7 @@ public RequestPattern(ValueMatcher<Request> customMatcher) {
202209
null,
203210
null,
204211
null,
212+
null,
205213
UrlPattern.ANY,
206214
RequestMethod.ANY,
207215
null,
@@ -221,6 +229,7 @@ public RequestPattern(CustomMatcherDefinition customMatcherDefinition) {
221229
null,
222230
null,
223231
null,
232+
null,
224233
UrlPattern.ANY,
225234
RequestMethod.ANY,
226235
null,
@@ -299,6 +308,10 @@ private MatchResult portMatches(final Request request) {
299308
return port != null ? MatchResult.of(request.getPort() == port) : MatchResult.exactMatch();
300309
}
301310

311+
private MatchResult clientIpMatches(final Request request) {
312+
return clientIp != null ? clientIp.match(request.getClientIp()) : MatchResult.exactMatch();
313+
}
314+
302315
private MatchResult allHeadersMatchResult(final Request request) {
303316
Map<String, MultiValuePattern> combinedHeaders = combineBasicAuthAndOtherHeaders();
304317

@@ -427,6 +440,10 @@ public Integer getPort() {
427440
return port;
428441
}
429442

443+
public StringValuePattern getClientIp() {
444+
return clientIp;
445+
}
446+
430447
public String getUrl() {
431448
return urlPatternOrNull(UrlPattern.class, false);
432449
}

src/main/java/com/github/tomakehurst/wiremock/matching/RequestPatternBuilder.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2016-2024 Thomas Akehurst
2+
* Copyright (C) 2016-2025 Thomas Akehurst
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -33,6 +33,7 @@ public class RequestPatternBuilder {
3333
private String scheme;
3434
private StringValuePattern hostPattern;
3535
private Integer port;
36+
private StringValuePattern clientIpPattern;
3637
private UrlPattern url = UrlPattern.ANY;
3738
private RequestMethod method = RequestMethod.ANY;
3839
private Map<String, MultiValuePattern> headers = new LinkedHashMap<>();
@@ -97,6 +98,7 @@ public static RequestPatternBuilder like(RequestPattern requestPattern) {
9798
builder.scheme = requestPattern.getScheme();
9899
builder.hostPattern = requestPattern.getHost();
99100
builder.port = requestPattern.getPort();
101+
builder.clientIpPattern = requestPattern.getClientIp();
100102
builder.url = requestPattern.getUrlMatcher();
101103
builder.method = requestPattern.getMethod();
102104
if (requestPattern.getHeaders() != null) {
@@ -147,6 +149,11 @@ public RequestPatternBuilder withPort(int port) {
147149
return this;
148150
}
149151

152+
public RequestPatternBuilder withClientIp(StringValuePattern clientIpPattern) {
153+
this.clientIpPattern = clientIpPattern;
154+
return this;
155+
}
156+
150157
public RequestPatternBuilder withUrl(String url) {
151158
this.url = WireMock.urlEqualTo(url);
152159
return this;
@@ -270,6 +277,7 @@ public RequestPattern build() {
270277
scheme,
271278
hostPattern,
272279
port,
280+
clientIpPattern,
273281
url,
274282
method,
275283
headers.isEmpty() ? null : headers,

src/main/java/com/github/tomakehurst/wiremock/verification/diff/Diff.java

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2016-2024 Thomas Akehurst
2+
* Copyright (C) 2016-2025 Thomas Akehurst
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -106,6 +106,7 @@ public List<DiffLine<?>> getLines(Map<String, RequestMatcherExtension> customMat
106106

107107
addHostSectionIfPresent(diffLineList);
108108
addPortSectionIfPresent(diffLineList);
109+
addClientIpSectionIfPresent(diffLineList);
109110
addSchemeSectionIfPresent(diffLineList);
110111
addMethodSection(diffLineList);
111112
UrlPattern urlPattern = getFirstNonNull(requestPattern.getUrlMatcher(), anyUrl());
@@ -351,6 +352,17 @@ private void addPortSectionIfPresent(List<DiffLine<?>> diffLineList) {
351352
}
352353
}
353354

355+
private void addClientIpSectionIfPresent(List<DiffLine<?>> diffLineList) {
356+
if (requestPattern.getClientIp() != null) {
357+
StringValuePattern expectedClientIp = equalTo(String.valueOf(requestPattern.getClientIp()));
358+
String actualClientIp = request.getClientIp();
359+
DiffLine<String> clientIpSection =
360+
new DiffLine<>(
361+
"ClientIp", expectedClientIp, actualClientIp, expectedClientIp.getExpected());
362+
diffLineList.addAll(toDiffDescriptionLines(clientIpSection));
363+
}
364+
}
365+
354366
private void addHostSectionIfPresent(List<DiffLine<?>> diffLineList) {
355367
if (requestPattern.getHost() != null) {
356368
String hostOperator = generateOperatorString(requestPattern.getHost(), "");

src/main/resources/swagger/wiremock-admin-api.json

+4
Original file line numberDiff line numberDiff line change
@@ -2134,6 +2134,10 @@
21342134
"maximum": 65535,
21352135
"description": "The HTTP port number of the request URL"
21362136
},
2137+
"clientIp": {
2138+
"type": "string",
2139+
"description": "The IP of the client making the request"
2140+
},
21372141
"method": {
21382142
"type": "string",
21392143
"pattern": "^[A-Z]+$",

src/test/java/com/github/tomakehurst/wiremock/StubbingWithBrowserProxyAcceptanceTest.java

+40-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2020-2021 Thomas Akehurst
2+
* Copyright (C) 2020-2025 Thomas Akehurst
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -117,6 +117,45 @@ public void doesNotMatchOnPortNumberWhenIncorrect() throws Exception {
117117
makeRequestAndAssertNotOk(request);
118118
}
119119

120+
@Test
121+
public void matchesClientIp() throws Exception {
122+
stubFor(
123+
get(urlPathEqualTo("/mypath"))
124+
.withClientIp(equalTo("192.168.1.1"))
125+
.willReturn(ok(EXPECTED_RESPONSE_BODY)));
126+
127+
ClassicHttpRequest request =
128+
ClassicRequestBuilder.get("http://localhost:1234/mypath")
129+
.addHeader("X-Forwarded-For", "192.168.1.1")
130+
.build();
131+
makeRequestAndAssertOk(request);
132+
}
133+
134+
@Test
135+
public void doesNotMatchClientIpWhenItIsIncorrect() throws Exception {
136+
stubFor(
137+
get(urlPathEqualTo("/mypath"))
138+
.withClientIp(equalTo("192.168.1.1"))
139+
.willReturn(ok(EXPECTED_RESPONSE_BODY)));
140+
141+
ClassicHttpRequest request =
142+
ClassicRequestBuilder.get("http://localhost:1234/mypath")
143+
.addHeader("X-Forwarded-For", "192.168.100.1")
144+
.build();
145+
makeRequestAndAssertNotOk(request);
146+
}
147+
148+
@Test
149+
public void doesNotMatchClientIpWhenHeaderIsNotPresent() throws Exception {
150+
stubFor(
151+
get(urlPathEqualTo("/mypath"))
152+
.withClientIp(equalTo("5.5.5.5"))
153+
.willReturn(ok(EXPECTED_RESPONSE_BODY)));
154+
155+
ClassicHttpRequest request = ClassicRequestBuilder.get("http://localhost:1234/mypath").build();
156+
makeRequestAndAssertNotOk(request);
157+
}
158+
120159
@Test
121160
public void matchesOnScheme() throws Exception {
122161
stubFor(

src/test/java/com/github/tomakehurst/wiremock/matching/RequestPatternBuilderTest.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2017-2024 Thomas Akehurst
2+
* Copyright (C) 2017-2025 Thomas Akehurst
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -67,6 +67,7 @@ void likeRequestPatternWithoutCustomMatcher() {
6767
"https",
6868
WireMock.equalTo("my.wiremock.org"),
6969
1234,
70+
WireMock.equalTo("192.168.2.2"),
7071
WireMock.urlEqualTo("/foo"),
7172
RequestMethod.POST,
7273
Map.of("X-Header", MultiValuePattern.of(WireMock.equalTo("bar"))),
@@ -127,6 +128,7 @@ void likeRequestPatternWithoutMultipartMatcher() {
127128
"https",
128129
WireMock.equalTo("my.wiremock.org"),
129130
1234,
131+
WireMock.equalTo("192.168.1.1"),
130132
WireMock.urlEqualTo("/foo"),
131133
RequestMethod.POST,
132134
Map.of("X-Header", MultiValuePattern.of(WireMock.equalTo("bar"))),

src/test/java/com/github/tomakehurst/wiremock/verification/diff/DiffTest.java

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2016-2024 Thomas Akehurst
2+
* Copyright (C) 2016-2025 Thomas Akehurst
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -497,6 +497,21 @@ void includeHostnameIfSpecifiedWithNonEqualTo() {
497497
"wrong.host\n" + "ANY\n" + "/thing\n")));
498498
}
499499

500+
@Test
501+
void includeClientIpIfSpecified() {
502+
Diff diff =
503+
new Diff(
504+
newRequestPattern(ANY, anyUrl()).withClientIp(equalTo("192.168.1.1")).build(),
505+
mockRequest().clientIp("192.168.2.2").url("/thing"));
506+
507+
assertThat(
508+
diff.toString(),
509+
is(
510+
junitStyleDiffMessage(
511+
"equalTo 192.168.1.1\n" + "ANY\n" + "/thing\n",
512+
"192.168.2.2\n" + "ANY\n" + "/thing\n")));
513+
}
514+
500515
@Test
501516
void includePortIfSpecified() {
502517
Diff diff =

0 commit comments

Comments
 (0)