Skip to content

Commit 79fb0b5

Browse files
committed
Merge pull request lightbody#243 from jekh/refactor-additional-filter-data
Replaced originalRequest on filters with new HttpMessageInfo object
2 parents ae42d73 + 6bda8bd commit 79fb0b5

File tree

13 files changed

+377
-152
lines changed

13 files changed

+377
-152
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ For most use cases, including inspecting and modifying requests/responses, `addR
250250
```java
251251
proxy.addRequestFilter(new RequestFilter() {
252252
@Override
253-
public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpRequest originalRequest) {
253+
public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) {
254254
if (request.getUri().equals("/some-endpoint-to-intercept")) {
255255
// retrieve the existing message contents as a String or, for binary contents, as a byte[]
256256
String messageContents = contents.getTextContents();
@@ -271,7 +271,7 @@ For most use cases, including inspecting and modifying requests/responses, `addR
271271
// responses are equally as simple:
272272
proxy.addResponseFilter(new ResponseFilter() {
273273
@Override
274-
public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpRequest originalRequest) {
274+
public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) {
275275
if (/*...some filtering criteria...*/) {
276276
contents.setTextContents("This message body will appear in all responses!");
277277
}
@@ -281,7 +281,7 @@ For most use cases, including inspecting and modifying requests/responses, `addR
281281

282282
With Java 8, the syntax is even more concise:
283283
```java
284-
proxy.addResponseFilter((response, contents, originalRequest) -> {
284+
proxy.addResponseFilter((response, contents, messageInfo) -> {
285285
if (/*...some filtering criteria...*/) {
286286
contents.setTextContents("This message body will appear in all responses!");
287287
}
@@ -298,7 +298,7 @@ When running the REST API with LittleProxy enabled, you cannot use the legacy `/
298298

299299
##### Request filters
300300

301-
Javascript request filters have access to the variables `request` (type `io.netty.handler.codec.http.HttpRequest`), `contents` (type `net.lightbody.bmp.util.HttpMessageContents`), and `originalRequest` (type `io.netty.handler.codec.http.HttpRequest`). `originalRequest` contains the original request received from the client and does not reflect any changes made by previous filters. If the javascript returns an object of type `io.netty.handler.codec.http.HttpResponse`, the HTTP request will "short-circuit" and return the response immediately.
301+
Javascript request filters have access to the variables `request` (type `io.netty.handler.codec.http.HttpRequest`), `contents` (type `net.lightbody.bmp.util.HttpMessageContents`), and `messageInfo` (type `net.lightbody.bmp.util.HttpMessageInfo`). `messageInfo` contains additional information about the message, including whether the message is sent over HTTP or HTTPS, as well as the original request received from the client before any changes made by previous filters. If the javascript returns an object of type `io.netty.handler.codec.http.HttpResponse`, the HTTP request will "short-circuit" and return the response immediately.
302302

303303
**Example: Modify User-Agent header**
304304

@@ -308,7 +308,7 @@ curl -i -X POST -H 'Content-Type: text/plain' -d "request.headers().remove('User
308308

309309
##### Response filters
310310

311-
Javascript response filters have access to the variables `response` (type `io.netty.handler.codec.http.HttpResponse`), `contents` (type `net.lightbody.bmp.util.HttpMessageContents`), and `originalRequest` (type `io.netty.handler.codec.http.HttpRequest`). As in the request filter, `originalRequest` contains the original request received from the client and does not reflect any changes made by previous filters.
311+
Javascript response filters have access to the variables `response` (type `io.netty.handler.codec.http.HttpResponse`), `contents` (type `net.lightbody.bmp.util.HttpMessageContents`), and `messageInfo` (type `net.lightbody.bmp.util.HttpMessageInfo`). As in the request filter, `messageInfo` contains additional information about the message.
312312

313313
**Example: Modify response body**
314314

browsermob-core-littleproxy/src/main/java/net/lightbody/bmp/filters/HarCaptureFilter.java

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -349,23 +349,7 @@ protected void captureRequestUrl(HttpRequest httpRequest) {
349349
// url [string] - Absolute URL of the request (fragments are not included).
350350
// the URI on the httpRequest may only identify the path of the resource, so find the full URL.
351351
// the full URL consists of the scheme + host + port (if non-standard) + path + query params + fragment.
352-
// Scheme: the scheme (HTTP/HTTPS) may or may not be part of the request, and so must be generated based on the
353-
// type of connection.
354-
// Host and Port: for HTTP requests, the host and port can be read from the request itself using the URI and/or
355-
// Host header. for HTTPS requests, the host and port are not available in the request. by using the
356-
// getRequestHostAndPort() helper method in HttpsAwareFiltersAdapter, we can capture the host and port for
357-
// HTTPS requests.
358-
// Path + Query Params + Fragment: these elements are contained in the HTTP request
359-
String url;
360-
if (isHttps()) {
361-
String hostAndPort = getRequestHostAndPort();
362-
String path = BrowserMobHttpUtil.getPathFromRequest(httpRequest);
363-
url = "https://" + hostAndPort + path;
364-
} else {
365-
String hostAndPort = BrowserMobHttpUtil.getHostAndPortFromRequest(httpRequest);
366-
String path = BrowserMobHttpUtil.getPathFromRequest(httpRequest);
367-
url = "http://" + hostAndPort + path;
368-
}
352+
String url = getFullUrl(httpRequest);
369353

370354
HarRequest request = new HarRequest(httpRequest.getMethod().toString(), url, httpRequest.getProtocolVersion().text());
371355
harEntry.setRequest(request);
@@ -625,13 +609,7 @@ protected void captureConnectTiming() {
625609
* @param httpRequest HTTP request to take the hostname from
626610
*/
627611
protected void populateAddressFromCache(HttpRequest httpRequest) {
628-
String serverHost;
629-
if (isHttps()) {
630-
HostAndPort hostAndPort = HostAndPort.fromString(getRequestHostAndPort());
631-
serverHost = hostAndPort.getHostText();
632-
} else {
633-
serverHost = BrowserMobHttpUtil.getHostFromRequest(httpRequest);
634-
}
612+
String serverHost = getHostAndPort(httpRequest);
635613

636614
if (serverHost != null && !serverHost.isEmpty()) {
637615
String resolvedAddress = resolvedAddresses.get(serverHost);

browsermob-core-littleproxy/src/main/java/net/lightbody/bmp/filters/HttpsAwareFiltersAdapter.java

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
package net.lightbody.bmp.filters;
22

3+
import com.google.common.net.HostAndPort;
34
import io.netty.channel.ChannelHandlerContext;
45
import io.netty.handler.codec.http.HttpRequest;
56
import io.netty.util.Attribute;
67
import io.netty.util.AttributeKey;
8+
import net.lightbody.bmp.util.BrowserMobHttpUtil;
79
import org.littleshoot.proxy.HttpFiltersAdapter;
810

911
/**
1012
* The HttpsAwareFiltersAdapter exposes the original host and the "real" host (after filter modifications) to filters for HTTPS
1113
* requets. HTTPS requests do not normally contain the host in the URI, and the Host header may be missing or spoofed.
1214
* <p/>
13-
* <b>Note:</b> The {@link #getRequestHostAndPort()} and {@link #getOriginalRequestHostAndPort()} methods can only be
15+
* <b>Note:</b> The {@link #getHttpsRequestHostAndPort()} and {@link #getHttpsOriginalRequestHostAndPort()} methods can only be
1416
* called when the request is an HTTPS request. Otherwise they will throw an IllegalStateException.
1517
*/
1618
public class HttpsAwareFiltersAdapter extends HttpFiltersAdapter {
@@ -44,7 +46,7 @@ public boolean isHttps() {
4446
* @return host and port of this HTTPS request
4547
* @throws IllegalStateException if this is not an HTTPS request
4648
*/
47-
public String getRequestHostAndPort() throws IllegalStateException {
49+
public String getHttpsRequestHostAndPort() throws IllegalStateException {
4850
if (!isHttps()) {
4951
throw new IllegalStateException("Request is not HTTPS. Cannot get host and port on non-HTTPS request using this method.");
5052
}
@@ -60,12 +62,71 @@ public String getRequestHostAndPort() throws IllegalStateException {
6062
* @return host and port of this HTTPS request
6163
* @throws IllegalStateException if this is not an HTTPS request
6264
*/
63-
public String getOriginalRequestHostAndPort() throws IllegalStateException {
65+
public String getHttpsOriginalRequestHostAndPort() throws IllegalStateException {
6466
if (!isHttps()) {
6567
throw new IllegalStateException("Request is not HTTPS. Cannot get original host and port on non-HTTPS request using this method.");
6668
}
6769

6870
Attribute<String> hostnameAttr = ctx.attr(AttributeKey.<String>valueOf(ORIGINAL_HOST_ATTRIBUTE_NAME));
6971
return hostnameAttr.get();
7072
}
73+
74+
/**
75+
* Returns the full, absolute URL of the specified request for both HTTP and HTTPS URLs. The request may reflect
76+
* modifications from this or other filters. This filter instance must be currently handling the specified request;
77+
* otherwise the results are undefined.
78+
*
79+
* @param modifiedRequest a possibly-modified version of the request currently being processed
80+
* @return the full URL of the request, including scheme, host, port, path, and query parameters
81+
*/
82+
public String getFullUrl(HttpRequest modifiedRequest) {
83+
// To get the full URL, we need to retrieve the Scheme, Host + Port, Path, and Query Params from the request.
84+
// Scheme: the scheme (HTTP/HTTPS) may or may not be part of the request, and so must be generated based on the
85+
// type of connection.
86+
// Host and Port: for HTTP requests, the host and port can be read from the request itself using the URI and/or
87+
// Host header. for HTTPS requests, the host and port are not available in the request. by using the
88+
// getHttpsRequestHostAndPort() helper method in HttpsAwareFiltersAdapter, we can capture the host and port for
89+
// HTTPS requests.
90+
// Path + Query Params + Fragment: these elements are contained in the HTTP request
91+
String url;
92+
if (isHttps()) {
93+
String hostAndPort = getHttpsRequestHostAndPort();
94+
String path = BrowserMobHttpUtil.getPathFromRequest(modifiedRequest);
95+
url = "https://" + hostAndPort + path;
96+
} else {
97+
String hostAndPort = BrowserMobHttpUtil.getHostAndPortFromRequest(modifiedRequest);
98+
String path = BrowserMobHttpUtil.getPathFromRequest(modifiedRequest);
99+
url = "http://" + hostAndPort + path;
100+
}
101+
return url;
102+
}
103+
104+
/**
105+
* Returns the full, absolute URL of the original request from the client for both HTTP and HTTPS URLs. The URL
106+
* will not reflect modifications from this or other filters.
107+
*
108+
* @return the full URL of the original request, including scheme, host, port, path, and query parameters
109+
*/
110+
public String getOriginalUrl() {
111+
return getFullUrl(originalRequest);
112+
}
113+
114+
/**
115+
* Returns the host and port of the specified request for both HTTP and HTTPS requests. The request may reflect
116+
* modifications from this or other filters. This filter instance must be currently handling the specified request;
117+
* otherwise the results are undefined.
118+
*
119+
* @param modifiedRequest a possibly-modified version of the request currently being processed
120+
* @return host and port of the specified request
121+
*/
122+
public String getHostAndPort(HttpRequest modifiedRequest) {
123+
String serverHost;
124+
if (isHttps()) {
125+
HostAndPort hostAndPort = HostAndPort.fromString(getHttpsRequestHostAndPort());
126+
serverHost = hostAndPort.getHostText();
127+
} else {
128+
serverHost = BrowserMobHttpUtil.getHostFromRequest(modifiedRequest);
129+
}
130+
return serverHost;
131+
}
71132
}

browsermob-core-littleproxy/src/main/java/net/lightbody/bmp/filters/RequestFilterAdapter.java

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
import io.netty.handler.codec.http.HttpRequest;
77
import io.netty.handler.codec.http.HttpResponse;
88
import net.lightbody.bmp.util.HttpMessageContents;
9+
import net.lightbody.bmp.util.HttpMessageInfo;
910
import org.littleshoot.proxy.HttpFilters;
10-
import org.littleshoot.proxy.HttpFiltersAdapter;
1111
import org.littleshoot.proxy.HttpFiltersSourceAdapter;
1212

1313
/**
1414
* A filter adapter for {@link RequestFilter} implementations. Executes the filter when the {@link HttpFilters#clientToProxyRequest(HttpObject)}
1515
* method is invoked.
1616
*/
17-
public class RequestFilterAdapter extends HttpFiltersAdapter {
17+
public class RequestFilterAdapter extends HttpsAwareFiltersAdapter {
1818
private final RequestFilter requestFilter;
1919

2020
public RequestFilterAdapter(HttpRequest originalRequest, ChannelHandlerContext ctx, RequestFilter requestFilter) {
@@ -23,12 +23,6 @@ public RequestFilterAdapter(HttpRequest originalRequest, ChannelHandlerContext c
2323
this.requestFilter = requestFilter;
2424
}
2525

26-
public RequestFilterAdapter(HttpRequest originalRequest, RequestFilter requestFilter) {
27-
super(originalRequest);
28-
29-
this.requestFilter = requestFilter;
30-
}
31-
3226
@Override
3327
public HttpResponse clientToProxyRequest(HttpObject httpObject) {
3428
// only filter when the original HttpRequest comes through. the RequestFilterAdapter is not designed to filter
@@ -45,7 +39,9 @@ public HttpResponse clientToProxyRequest(HttpObject httpObject) {
4539
contents = null;
4640
}
4741

48-
HttpResponse response = requestFilter.filterRequest(httpRequest, contents, originalRequest);
42+
HttpMessageInfo messageData = new HttpMessageInfo(originalRequest, ctx, isHttps(), getOriginalUrl());
43+
44+
HttpResponse response = requestFilter.filterRequest(httpRequest, contents, messageData);
4945
if (response != null) {
5046
return response;
5147
}

browsermob-core-littleproxy/src/main/java/net/lightbody/bmp/filters/ResponseFilterAdapter.java

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
import io.netty.handler.codec.http.HttpRequest;
77
import io.netty.handler.codec.http.HttpResponse;
88
import net.lightbody.bmp.util.HttpMessageContents;
9+
import net.lightbody.bmp.util.HttpMessageInfo;
910
import org.littleshoot.proxy.HttpFilters;
10-
import org.littleshoot.proxy.HttpFiltersAdapter;
1111
import org.littleshoot.proxy.HttpFiltersSourceAdapter;
1212

1313
/**
1414
* A filter adapter for {@link ResponseFilter} implementations. Executes the filter when the {@link HttpFilters#serverToProxyResponse(HttpObject)}
1515
* method is invoked.
1616
*/
17-
public class ResponseFilterAdapter extends HttpFiltersAdapter {
17+
public class ResponseFilterAdapter extends HttpsAwareFiltersAdapter {
1818
private final ResponseFilter responseFilter;
1919

2020
public ResponseFilterAdapter(HttpRequest originalRequest, ChannelHandlerContext ctx, ResponseFilter responseFilter) {
@@ -23,12 +23,6 @@ public ResponseFilterAdapter(HttpRequest originalRequest, ChannelHandlerContext
2323
this.responseFilter = responseFilter;
2424
}
2525

26-
public ResponseFilterAdapter(HttpRequest originalRequest, ResponseFilter responseFilter) {
27-
super(originalRequest);
28-
29-
this.responseFilter = responseFilter;
30-
}
31-
3226
@Override
3327
public HttpObject serverToProxyResponse(HttpObject httpObject) {
3428
// only filter when the original HttpResponse comes through. the ResponseFilterAdapter is not designed to filter
@@ -45,7 +39,9 @@ public HttpObject serverToProxyResponse(HttpObject httpObject) {
4539
contents = null;
4640
}
4741

48-
responseFilter.filterResponse(httpResponse, contents, originalRequest);
42+
HttpMessageInfo messageData = new HttpMessageInfo(originalRequest, ctx, isHttps(), getOriginalUrl());
43+
44+
responseFilter.filterResponse(httpResponse, contents, messageData);
4945
}
5046

5147
return super.serverToProxyResponse(httpObject);

0 commit comments

Comments
 (0)