Skip to content

Commit 6132025

Browse files
committed
[java][bidi] Add response handler
1 parent 5daec91 commit 6132025

File tree

3 files changed

+290
-33
lines changed

3 files changed

+290
-33
lines changed

java/src/org/openqa/selenium/remote/Network.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@
1919

2020
import java.net.URI;
2121
import java.util.function.Predicate;
22+
import java.util.function.Supplier;
2223
import java.util.function.UnaryOperator;
2324
import org.openqa.selenium.Beta;
2425
import org.openqa.selenium.UsernameAndPassword;
2526
import org.openqa.selenium.remote.http.HttpRequest;
27+
import org.openqa.selenium.remote.http.HttpResponse;
2628

2729
@Beta
2830
public interface Network {
@@ -35,9 +37,15 @@ public interface Network {
3537

3638
void clearAuthenticationHandlers();
3739

38-
long addRequestHandler(Predicate<URI> filter, UnaryOperator<HttpRequest> handler);
40+
long addRequestHandler(Predicate<HttpRequest> filter, UnaryOperator<HttpRequest> handler);
3941

4042
void removeRequestHandler(long id);
4143

4244
void clearRequestHandlers();
45+
46+
long addResponseHandler(Predicate<HttpRequest> filter, Supplier<HttpResponse> handler);
47+
48+
void removeResponseHandler(long id);
49+
50+
void clearResponseHandlers();
4351
}

java/src/org/openqa/selenium/remote/RemoteNetwork.java

Lines changed: 105 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.concurrent.ConcurrentHashMap;
2626
import java.util.concurrent.atomic.AtomicLong;
2727
import java.util.function.Predicate;
28+
import java.util.function.Supplier;
2829
import java.util.function.UnaryOperator;
2930
import org.openqa.selenium.Beta;
3031
import org.openqa.selenium.UsernameAndPassword;
@@ -36,10 +37,12 @@
3637
import org.openqa.selenium.bidi.network.ContinueRequestParameters;
3738
import org.openqa.selenium.bidi.network.Header;
3839
import org.openqa.selenium.bidi.network.InterceptPhase;
40+
import org.openqa.selenium.bidi.network.ProvideResponseParameters;
3941
import org.openqa.selenium.bidi.network.RequestData;
4042
import org.openqa.selenium.remote.http.Contents;
4143
import org.openqa.selenium.remote.http.HttpMethod;
4244
import org.openqa.selenium.remote.http.HttpRequest;
45+
import org.openqa.selenium.remote.http.HttpResponse;
4346

4447
@Beta
4548
class RemoteNetwork implements Network {
@@ -51,6 +54,8 @@ class RemoteNetwork implements Network {
5154

5255
private final Map<Long, RequestDetails> requestHandlers = new ConcurrentHashMap<>();
5356

57+
private final Map<Long, ResponseDetails> responseHandlers = new ConcurrentHashMap<>();
58+
5459
private final AtomicLong callBackId = new AtomicLong(1);
5560

5661
public RemoteNetwork(WebDriver driver) {
@@ -97,24 +102,60 @@ private void interceptRequest() {
97102
ContinueRequestParameters continueRequestParameters =
98103
new ContinueRequestParameters(requestId);
99104

100-
Optional<UnaryOperator<HttpRequest>> requestHandler = getRequestHandler(uri);
105+
RequestData interceptedRequest = beforeRequestSent.getRequest();
106+
107+
// Build the originalRequest object from the intercepted request details.
108+
HttpRequest originalRequest =
109+
new HttpRequest(
110+
HttpMethod.getHttpMethod(interceptedRequest.getMethod()),
111+
interceptedRequest.getUrl());
112+
113+
// Populate the headers of the original request.
114+
// Body is not available as part of WebDriver Spec, hence we cannot add that or use that.
115+
interceptedRequest
116+
.getHeaders()
117+
.forEach(
118+
header ->
119+
originalRequest.addHeader(header.getName(), header.getValue().getValue()));
120+
121+
Optional<UnaryOperator<HttpRequest>> requestHandler = getRequestHandler(originalRequest);
122+
123+
Optional<Supplier<HttpResponse>> responseHandler = getResponseHandler(originalRequest);
124+
125+
// If request and response handler both are registered for same uri,
126+
// then the response will be mocked instead of modifying the outgoing request.
127+
// This can be altered in the future to let the modified request go through and have a
128+
// response mock for that modified request.
129+
// If in future the Browsers support intercepting at "response started" phase and allow
130+
// using provide response command in that phase.
131+
// Currently, only intercepting in "before request sent" phase is permitted.
132+
if (responseHandler.isPresent()) {
133+
ProvideResponseParameters provideResponseParameters =
134+
new ProvideResponseParameters(requestId);
101135

102-
if (requestHandler.isPresent()) {
103-
RequestData interceptedRequest = beforeRequestSent.getRequest();
136+
HttpResponse modifiedResponse = responseHandler.get().get();
137+
138+
provideResponseParameters.statusCode(modifiedResponse.getStatus());
139+
140+
List<Header> headerList = new ArrayList<>();
141+
modifiedResponse.forEachHeader(
142+
(name, value) ->
143+
headerList.add(
144+
new Header(name, new BytesValue(BytesValue.Type.STRING, value))));
104145

105-
// Build the originalRequest object from the intercepted request details.
106-
HttpRequest originalRequest =
107-
new HttpRequest(
108-
HttpMethod.getHttpMethod(interceptedRequest.getMethod()),
109-
interceptedRequest.getUrl());
146+
if (!headerList.isEmpty()) {
147+
provideResponseParameters.headers(headerList);
148+
}
110149

111-
// Populate the headers of the original request.
112-
interceptedRequest
113-
.getHeaders()
114-
.forEach(
115-
header ->
116-
originalRequest.addHeader(header.getName(), header.getValue().getValue()));
150+
Contents.Supplier content = modifiedResponse.getContent();
117151

152+
if (content.length() > 0) {
153+
provideResponseParameters.body(
154+
new BytesValue(BytesValue.Type.STRING, Contents.utf8String(content)));
155+
}
156+
network.provideResponse(provideResponseParameters);
157+
return;
158+
} else if (requestHandler.isPresent()) {
118159
HttpRequest modifiedRequest = requestHandler.get().apply(originalRequest);
119160

120161
continueRequestParameters.method(modifiedRequest.getMethod());
@@ -144,13 +185,20 @@ private void interceptRequest() {
144185
});
145186
}
146187

147-
private Optional<UnaryOperator<HttpRequest>> getRequestHandler(URI uri) {
188+
private Optional<UnaryOperator<HttpRequest>> getRequestHandler(HttpRequest request) {
148189
return requestHandlers.values().stream()
149-
.filter(requestDetails -> requestDetails.getFilter().test(uri))
190+
.filter(requestDetails -> requestDetails.getFilter().test(request))
150191
.map(RequestDetails::getHandler)
151192
.findFirst();
152193
}
153194

195+
private Optional<Supplier<HttpResponse>> getResponseHandler(HttpRequest request) {
196+
return responseHandlers.values().stream()
197+
.filter(responseDetails -> responseDetails.getFilter().test(request))
198+
.map(ResponseDetails::getHandler)
199+
.findFirst();
200+
}
201+
154202
@Override
155203
public long addAuthenticationHandler(UsernameAndPassword usernameAndPassword) {
156204
return addAuthenticationHandler(url -> true, usernameAndPassword);
@@ -176,7 +224,7 @@ public void clearAuthenticationHandlers() {
176224
}
177225

178226
@Override
179-
public long addRequestHandler(Predicate<URI> filter, UnaryOperator<HttpRequest> handler) {
227+
public long addRequestHandler(Predicate<HttpRequest> filter, UnaryOperator<HttpRequest> handler) {
180228
long id = this.callBackId.incrementAndGet();
181229

182230
requestHandlers.put(id, new RequestDetails(filter, handler));
@@ -193,6 +241,25 @@ public void clearRequestHandlers() {
193241
requestHandlers.clear();
194242
}
195243

244+
// Allows mocking the response body
245+
@Override
246+
public long addResponseHandler(Predicate<HttpRequest> filter, Supplier<HttpResponse> handler) {
247+
long id = this.callBackId.incrementAndGet();
248+
249+
responseHandlers.put(id, new ResponseDetails(filter, handler));
250+
return id;
251+
}
252+
253+
@Override
254+
public void removeResponseHandler(long id) {
255+
responseHandlers.remove(id);
256+
}
257+
258+
@Override
259+
public void clearResponseHandlers() {
260+
responseHandlers.clear();
261+
}
262+
196263
private class AuthDetails {
197264
private final Predicate<URI> filter;
198265
private final UsernameAndPassword usernameAndPassword;
@@ -212,20 +279,38 @@ public UsernameAndPassword getUsernameAndPassword() {
212279
}
213280

214281
private class RequestDetails {
215-
private final Predicate<URI> filter;
282+
private final Predicate<HttpRequest> filter;
216283
private final UnaryOperator<HttpRequest> handler;
217284

218-
public RequestDetails(Predicate<URI> filter, UnaryOperator<HttpRequest> handler) {
285+
public RequestDetails(Predicate<HttpRequest> filter, UnaryOperator<HttpRequest> handler) {
219286
this.filter = filter;
220287
this.handler = handler;
221288
}
222289

223-
public Predicate<URI> getFilter() {
290+
public Predicate<HttpRequest> getFilter() {
224291
return this.filter;
225292
}
226293

227294
public UnaryOperator<HttpRequest> getHandler() {
228295
return this.handler;
229296
}
230297
}
298+
299+
private class ResponseDetails {
300+
private final Predicate<HttpRequest> filter;
301+
private final Supplier<HttpResponse> handler;
302+
303+
public ResponseDetails(Predicate<HttpRequest> filter, Supplier<HttpResponse> handler) {
304+
this.filter = filter;
305+
this.handler = handler;
306+
}
307+
308+
public Predicate<HttpRequest> getFilter() {
309+
return this.filter;
310+
}
311+
312+
public Supplier<HttpResponse> getHandler() {
313+
return this.handler;
314+
}
315+
}
231316
}

0 commit comments

Comments
 (0)