diff --git a/.travis.yml b/.travis.yml index 016ceb70b..a2b4446dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,9 @@ sudo: false language: java +dist: bionic jdk: - # Not running tests against openjdk7, since the SunEC is not included in travis-ci's version of openjdk7. - # Not running tests against oraclejdk7, since travis-ci no longer provides it. - # - openjdk7 - - oraclejdk8 + - openjdk11 cache: directories: diff --git a/.whitesource b/.whitesource new file mode 100644 index 000000000..d40db00ae --- /dev/null +++ b/.whitesource @@ -0,0 +1,3 @@ +{ + "settingsInheritedFrom": "hellofresh/whitesource-config@master" +} \ No newline at end of file diff --git a/README.md b/README.md index e932b282c..25ee51d33 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ Set a URL to blacklist | PUT | */proxy/[port]/blacklist* |

*regex* - The black Clears all URL patterns from the blacklist | DELETE | */proxy/[port]/blacklist* || Limit the bandwidth through the proxy on the *[port]* | PUT | */proxy/[port]/limit* |

*downstreamKbps* - Sets the downstream bandwidth limit in kbps. Optional.

*upstreamKbps* - Sets the upstream bandwidth limit kbps. Optional, by default unlimited.

*downstreamMaxKB* - Specifies how many kilobytes in total the client is allowed to download through the proxy. Optional, by default unlimited.

*upstreamMaxKB* - Specifies how many kilobytes in total the client is allowed to upload through the proxy. Optional, by default unlimited.

*latency* - Add the given latency to each HTTP request. Optional, by default all requests are invoked without latency.

*enable* - A boolean that enable bandwidth limiter. Optional, by default to "false", but setting any of the properties above will implicitly enable throttling

*payloadPercentage* - Specifying what percentage of data sent is payload, e.g. use this to take into account overhead due to tcp/ip. Optional.

*maxBitsPerSecond* - The max bits per seconds you want this instance of StreamManager to respect. Optional.

Displays the amount of data remaining to be uploaded/downloaded until the limit is reached | GET | */proxy/[port]/limit* || -Set and override HTTP Request headers | POST | */proxy/[port]/headers* | Payload data should be **JSON** encoded set of headers. Where key is a header name (such as "User-Agent") and value is a value of HTTP header to setup (such as "BrowserMob-Agent"). Example: `{"User-Agent": "BrowserMob-Agent"}`| +Set and override HTTP Request headers | POST | */proxy/[port]/headers* | Payload data should be **JSON** encoded set of headers and optionally `headersFilterRegexp: .*.*` . Where key is a header name (such as "User-Agent") and value is a value of HTTP header to setup (such as "BrowserMob-Agent"). Example: `{"User-Agent": "BrowserMob-Agent"}`| Overrides normal DNS lookups and remaps the given hosts with the associated IP address | POST | */proxy/[port]/hosts* | Payload data should be **JSON** encoded set of hosts. Where key is a host name (such as "example.com") and value is a IP address which associatied with host hame (such as "1.2.3.4"'). Example: `{"example.com": "1.2.3.4"}`| Sets automatic basic authentication for the specified domain | POST | */proxy/[port]/auth/basic/[domain]* | Payload data should be **JSON** encoded username and password name/value pairs. Example: `{"username": "myUsername", "password": "myPassword"}`| Wait till all request are being made | PUT | */proxy/[port]/wait* |

*quietPeriodInMs* - Wait till all request are being made. Optional.

*timeoutInMs* - Sets quiet period in milliseconds. Optional.

| @@ -190,6 +190,7 @@ system properties will be used to specify the upstream proxy. - -proxyPortRange \-\ - Range of ports reserved for proxies. Only applies if *port* parameter is not supplied in the POST request. Default values are \+1 to \+500+1. - -ttl \ + - -maxResponseSizeBytes max size of response in bytes by default 2097152 (2 MB) - Proxy will be automatically deleted after a specified time period. Off by default. ### Embedded Mode @@ -395,7 +396,7 @@ When you build the latest code from source, you'll have access to the latest sna net.lightbody.bmp browsermob-core - 2.1.6-SNAPSHOT + 2.1.38 test ``` diff --git a/browsermob-core/pom.xml b/browsermob-core/pom.xml index 2071434ee..daa1d380a 100644 --- a/browsermob-core/pom.xml +++ b/browsermob-core/pom.xml @@ -1,11 +1,12 @@ - + jar browsermob-proxy net.lightbody.bmp - 2.1.6-SNAPSHOT + 2.1.38 4.0.0 @@ -13,7 +14,7 @@ BrowserMob Proxy Core (LittleProxy) Module - 7.6.16.v20140903 + 9.4.21.v20190926 @@ -41,7 +42,7 @@ org.apache.maven.plugins maven-surefire-plugin - -Xmx1g -XX:MaxPermSize=256m + -Xmx1g @@ -49,7 +50,7 @@ - net.lightbody.bmp + org.littleshoot littleproxy @@ -68,6 +69,12 @@ jackson-core + + co.elastic.logging + log4j2-ecs-layout + + + com.fasterxml.jackson.core jackson-databind @@ -86,7 +93,7 @@ dnsjava dnsjava - 2.1.8 + 2.1.9 @@ -133,12 +140,24 @@ bcpkix-jdk15on + + com.timgroup + java-statsd-client + + net.lightbody.bmp mitm ${project.version} + + + org.brotli + dec + 0.1.2 + + org.javassist @@ -149,12 +168,10 @@ org.apache.logging.log4j log4j-api - test org.apache.logging.log4j log4j-core - test org.apache.logging.log4j @@ -242,7 +259,12 @@ org.hamcrest - hamcrest-library + hamcrest + test + + + org.apache.httpcomponents + httpclient test diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/BrowserMobProxy.java b/browsermob-core/src/main/java/net/lightbody/bmp/BrowserMobProxy.java index a04ffe7af..44f1ac326 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/BrowserMobProxy.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/BrowserMobProxy.java @@ -39,7 +39,7 @@ public interface BrowserMobProxy { * Starts the proxy on the specified port. The proxy will listen for connections on the network interface specified by the bindAddress, and will * also initiate connections to upstream servers on the same network interface. * - * @param port port to listen on + * @param port port to listen on * @param bindAddress address of the network interface on which the proxy will listen for connections and also attempt to connect to upstream servers. * @throws java.lang.IllegalStateException if the proxy has already been started */ @@ -49,7 +49,7 @@ public interface BrowserMobProxy { * Starts the proxy on the specified port. The proxy will listen for connections on the network interface specified by the clientBindAddress, and will * initiate connections to upstream servers from the network interface specified by the serverBindAddress. * - * @param port port to listen on + * @param port port to listen on * @param clientBindAddress address of the network interface on which the proxy will listen for connections * @param serverBindAddress address of the network interface on which the proxy will connect to upstream servers * @throws java.lang.IllegalStateException if the proxy has already been started @@ -68,7 +68,7 @@ public interface BrowserMobProxy { * @throws java.lang.IllegalStateException if the proxy has not been started. */ void stop(); - + /** * Like {@link #stop()}, shuts down the proxy server and no longer accepts incoming connections, but does not wait for any existing * network traffic to cease. Any existing connections to clients or to servers may be force-killed immediately. @@ -125,7 +125,7 @@ public interface BrowserMobProxy { /** * Starts a new HAR file with the specified page name and page title. Enables HAR capture if it was not previously enabled. * - * @param initialPageRef initial page name of the new HAR file + * @param initialPageRef initial page name of the new HAR file * @param initialPageTitle initial page title of the new HAR file * @return existing HAR file, or null if none exists or HAR capture was disabled */ @@ -215,7 +215,7 @@ public interface BrowserMobProxy { * Starts a new HAR page using the specified pageRef as the page name and the pageTitle as the page title. Populates the * {@link net.lightbody.bmp.core.har.HarPageTimings#onLoad} value based on the amount of time the current page has been captured. * - * @param pageRef name of the new page + * @param pageRef name of the new page * @param pageTitle title of the new page * @return the HAR as it existed immediately after ending the current page * @throws java.lang.IllegalStateException if HAR capture has not been enabled via {@link #newHar()} or {@link #newHar(String)} @@ -258,7 +258,7 @@ public interface BrowserMobProxy { * The minimum amount of time that will elapse between the time the proxy begins receiving a response from the server and the time the * proxy begins sending the response to the client. * - * @param latency minimum latency, or 0 for no minimum + * @param latency minimum latency, or 0 for no minimum * @param timeUnit TimeUnit for the latency */ void setLatency(long latency, TimeUnit timeUnit); @@ -268,7 +268,7 @@ public interface BrowserMobProxy { * specified time, the proxy will respond with an HTTP 502 Bad Gateway. The default value is 60 seconds. * * @param connectionTimeout maximum time to wait to establish a connection to a server, or 0 to wait indefinitely - * @param timeUnit TimeUnit for the connectionTimeout + * @param timeUnit TimeUnit for the connectionTimeout */ void setConnectTimeout(int connectionTimeout, TimeUnit timeUnit); @@ -279,7 +279,7 @@ public interface BrowserMobProxy { * connection to the client may be closed abruptly. The default value is 60 seconds. * * @param idleConnectionTimeout maximum time to allow a connection to remain idle, or 0 to wait indefinitely. - * @param timeUnit TimeUnit for the idleConnectionTimeout + * @param timeUnit TimeUnit for the idleConnectionTimeout */ void setIdleConnectionTimeout(int idleConnectionTimeout, TimeUnit timeUnit); @@ -290,7 +290,7 @@ public interface BrowserMobProxy { * connection to the client may be closed abruptly. The default value is 0 (wait indefinitely). * * @param requestTimeout maximum time to wait for an HTTP response, or 0 to wait indefinitely - * @param timeUnit TimeUnit for the requestTimeout + * @param timeUnit TimeUnit for the requestTimeout */ void setRequestTimeout(int requestTimeout, TimeUnit timeUnit); @@ -298,7 +298,7 @@ public interface BrowserMobProxy { * Enables automatic authorization for the specified domain and auth type. Every request sent to the specified domain will contain the * specified authorization information. * - * @param domain domain automatically send authorization information to + * @param domain domain automatically send authorization information to * @param username authorization username * @param password authorization password * @param authType authorization type @@ -340,7 +340,7 @@ public interface BrowserMobProxy { * For example, the following rewrite rule: * *
   {@code proxy.rewriteUrl("http://www\\.(yahoo|bing)\\.com/\\?(\\w+)=(\\w+)", "http://www.google.com/?originalDomain=$1&$2=$3");}
- * + *

* will match an HTTP request (but not HTTPS!) to www.yahoo.com or www.bing.com with exactly 1 query parameter, * and replace it with a call to www.google.com with an 'originalDomain' query parameter, as well as the original query parameter. *

@@ -353,7 +353,7 @@ public interface BrowserMobProxy { * will result in the proxy making a request to: *

   {@code http://www.google.com?originalDomain=bing&anotherParam=anotherValue}
* - * @param urlPattern URL-matching regular expression + * @param urlPattern URL-matching regular expression * @param replacementExpression an expression, which may optionally contain capture groups, which will replace any URL which matches urlPattern */ void rewriteUrl(String urlPattern, String replacementExpression); @@ -410,8 +410,8 @@ public interface BrowserMobProxy { *

* See {@link #blacklistRequests(String, int)} for details on the URL the urlPattern will match. * - * @param urlPattern URL-matching regular expression to blacklist - * @param statusCode HTTP status code to return + * @param urlPattern URL-matching regular expression to blacklist + * @param statusCode HTTP status code to return * @param httpMethodPattern regular expression matching a request's HTTP method */ void blacklistRequests(String urlPattern, int statusCode, String httpMethodPattern); @@ -447,7 +447,7 @@ public interface BrowserMobProxy { * whitelist response code. * * @param urlPatterns URL-matching regular expressions to whitelist; null or an empty collection will enable an empty whitelist - * @param statusCode HTTP status code to return to clients when a URL matches a pattern + * @param statusCode HTTP status code to return to clients when a URL matches a pattern */ void whitelistRequests(Collection urlPatterns, int statusCode); @@ -501,11 +501,18 @@ public interface BrowserMobProxy { /** * Adds a new HTTP header to every request. If the header already exists on the request, it will be replaced with the specified header. * - * @param name name of the header to add + * @param name name of the header to add * @param value new header's value */ void addHeader(String name, String value); + /** + * Header filter regexp. + * + * @param headerFilterRegexp the header filter regexp + */ + void headerFilterRegexp(String headerFilterRegexp); + /** * Removes a header previously added with {@link #addHeader(String name, String value)}. * @@ -545,8 +552,8 @@ public interface BrowserMobProxy { * for the quiet period within the specified timeout, otherwise returns false. * * @param quietPeriod amount of time after which network traffic will be considered "stopped" - * @param timeout maximum amount of time to wait for network traffic to stop - * @param timeUnit TimeUnit for the quietPeriod and timeout + * @param timeout maximum amount of time to wait for network traffic to stop + * @param timeUnit TimeUnit for the quietPeriod and timeout * @return true if network traffic is stopped, otherwise false */ boolean waitForQuiescence(long quietPeriod, long timeout, TimeUnit timeUnit); @@ -587,7 +594,7 @@ public interface BrowserMobProxy { * {@link org.littleshoot.proxy.HttpFilters} instance (typically, a subclass of {@link org.littleshoot.proxy.HttpFiltersAdapter}). * To disable or bypass a filter on a per-request basis, the filterRequest() method may return null. * - * @param filterFactory factory to generate HttpFilters + * @param filterFactory factory to generate HttpFilters */ void addLastHttpFilterFactory(HttpFiltersSource filterFactory); diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/BrowserMobProxyServer.java b/browsermob-core/src/main/java/net/lightbody/bmp/BrowserMobProxyServer.java index d4ddc5543..0951594cb 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/BrowserMobProxyServer.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/BrowserMobProxyServer.java @@ -4,7 +4,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.MapMaker; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpObject; import io.netty.handler.codec.http.HttpRequest; import net.lightbody.bmp.client.ClientUtil; @@ -12,48 +12,19 @@ import net.lightbody.bmp.core.har.HarLog; import net.lightbody.bmp.core.har.HarNameVersion; import net.lightbody.bmp.core.har.HarPage; -import net.lightbody.bmp.filters.AddHeadersFilter; -import net.lightbody.bmp.filters.AutoBasicAuthFilter; -import net.lightbody.bmp.filters.BlacklistFilter; -import net.lightbody.bmp.filters.BrowserMobHttpFilterChain; -import net.lightbody.bmp.filters.HarCaptureFilter; -import net.lightbody.bmp.filters.HttpConnectHarCaptureFilter; -import net.lightbody.bmp.filters.HttpsHostCaptureFilter; -import net.lightbody.bmp.filters.HttpsOriginalHostCaptureFilter; -import net.lightbody.bmp.filters.LatencyFilter; -import net.lightbody.bmp.filters.RegisterRequestFilter; -import net.lightbody.bmp.filters.RequestFilter; -import net.lightbody.bmp.filters.RequestFilterAdapter; -import net.lightbody.bmp.filters.ResolvedHostnameCacheFilter; -import net.lightbody.bmp.filters.ResponseFilter; -import net.lightbody.bmp.filters.ResponseFilterAdapter; -import net.lightbody.bmp.filters.RewriteUrlFilter; -import net.lightbody.bmp.filters.UnregisterRequestFilter; -import net.lightbody.bmp.filters.WhitelistFilter; +import net.lightbody.bmp.filters.*; import net.lightbody.bmp.mitm.KeyStoreFileCertificateSource; import net.lightbody.bmp.mitm.TrustSource; import net.lightbody.bmp.mitm.keys.ECKeyGenerator; import net.lightbody.bmp.mitm.keys.RSAKeyGenerator; import net.lightbody.bmp.mitm.manager.ImpersonatingMitmManager; -import net.lightbody.bmp.proxy.ActivityMonitor; -import net.lightbody.bmp.proxy.BlacklistEntry; -import net.lightbody.bmp.proxy.CaptureType; -import net.lightbody.bmp.proxy.RewriteRule; -import net.lightbody.bmp.proxy.Whitelist; +import net.lightbody.bmp.proxy.*; import net.lightbody.bmp.proxy.auth.AuthType; import net.lightbody.bmp.proxy.dns.AdvancedHostResolver; import net.lightbody.bmp.proxy.dns.DelegatingHostResolver; import net.lightbody.bmp.util.BrowserMobHttpUtil; import net.lightbody.bmp.util.BrowserMobProxyUtil; -import org.littleshoot.proxy.ChainedProxy; -import org.littleshoot.proxy.ChainedProxyAdapter; -import org.littleshoot.proxy.ChainedProxyManager; -import org.littleshoot.proxy.HttpFilters; -import org.littleshoot.proxy.HttpFiltersSource; -import org.littleshoot.proxy.HttpFiltersSourceAdapter; -import org.littleshoot.proxy.HttpProxyServer; -import org.littleshoot.proxy.HttpProxyServerBootstrap; -import org.littleshoot.proxy.MitmManager; +import org.littleshoot.proxy.*; import org.littleshoot.proxy.impl.DefaultHttpProxyServer; import org.littleshoot.proxy.impl.ProxyUtils; import org.littleshoot.proxy.impl.ThreadPoolConfiguration; @@ -62,16 +33,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.EnumSet; -import java.util.List; -import java.util.Map; -import java.util.Queue; -import java.util.Set; +import java.util.*; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; @@ -183,6 +145,11 @@ public class BrowserMobProxyServer implements BrowserMobProxy { */ private volatile int connectTimeoutMs; + /** + * Regexp to check request url and inject headers if url match regexp. + */ + private static String headersFilterRegexp; + /** * The amount of time a connection to a server can remain idle while receiving data from the server. */ @@ -337,28 +304,25 @@ public int getMaximumResponseBufferSizeInBytes() { // chained proxy after the proxy is started. bootstrappedWithDefaultChainedProxy.set(true); - bootstrap.withChainProxyManager(new ChainedProxyManager() { - @Override - public void lookupChainedProxies(HttpRequest httpRequest, Queue chainedProxies) { - final InetSocketAddress upstreamProxy = upstreamProxyAddress; - if (upstreamProxy != null) { - chainedProxies.add(new ChainedProxyAdapter() { - @Override - public InetSocketAddress getChainedProxyAddress() { - return upstreamProxy; - } - - @Override - public void filterRequest(HttpObject httpObject) { - String chainedProxyAuth = chainedProxyCredentials; - if (chainedProxyAuth != null) { - if (httpObject instanceof HttpRequest) { - HttpHeaders.addHeader((HttpRequest)httpObject, HttpHeaders.Names.PROXY_AUTHORIZATION, "Basic " + chainedProxyAuth); - } + bootstrap.withChainProxyManager((httpRequest, chainedProxies) -> { + final InetSocketAddress upstreamProxy = upstreamProxyAddress; + if (upstreamProxy != null) { + chainedProxies.add(new ChainedProxyAdapter() { + @Override + public InetSocketAddress getChainedProxyAddress() { + return upstreamProxy; + } + + @Override + public void filterRequest(HttpObject httpObject) { + String chainedProxyAuth = chainedProxyCredentials; + if (chainedProxyAuth != null) { + if (httpObject instanceof HttpRequest) { + ((HttpRequest) httpObject).headers().add(HttpHeaderNames.PROXY_AUTHORIZATION, "Basic " + chainedProxyAuth); } } - }); - } + } + }); } }); } @@ -633,6 +597,11 @@ public void addHeaders(Map headers) { this.additionalHeaders = newHeaders; } + @Override + public void headerFilterRegexp(String headerFilterRegexp) { + this.headersFilterRegexp = headerFilterRegexp; + } + @Override public void setLatency(long latency, TimeUnit timeUnit) { this.latencyMs = (int) TimeUnit.MILLISECONDS.convert(latency, timeUnit); @@ -934,7 +903,7 @@ public void addLastHttpFilterFactory(HttpFiltersSource filterFactory) { */ @Override public void addResponseFilter(ResponseFilter filter) { - addLastHttpFilterFactory(new ResponseFilterAdapter.FilterSource(filter)); + addLastHttpFilterFactory(new ResponseFilterAdapter.FilterSource(filter, Integer.parseInt(System.getProperty("maxResponseSizeBytes", String.valueOf(ResponseFilterAdapter.FilterSource.DEFAULT_MAXIMUM_RESPONSE_BUFFER_SIZE))))); } /** @@ -1093,8 +1062,8 @@ public HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerCont addHttpFilterFactory(new HttpFiltersSourceAdapter() { @Override - public HttpFilters filterRequest(HttpRequest originalRequest) { - return new AddHeadersFilter(originalRequest, additionalHeaders); + public HttpFilters filterRequest(HttpRequest originalRequest,ChannelHandlerContext ctx) { + return new AddHeadersFilter(originalRequest,ctx, additionalHeaders, headersFilterRegexp); } }); @@ -1111,6 +1080,13 @@ public HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerCont return new UnregisterRequestFilter(originalRequest, ctx, activityMonitor); } }); + + addHttpFilterFactory(new HttpFiltersSourceAdapter() { + @Override + public HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerContext ctx) { + return new StatsDMetricsFilter(originalRequest, ctx); + } + }); } private int getMaximumRequestBufferSize() { diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarEntry.java b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarEntry.java index 1864a78c7..5b6529322 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarEntry.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarEntry.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Date; import java.util.concurrent.TimeUnit; @@ -17,6 +18,8 @@ public class HarEntry { private volatile HarCache cache = new HarCache(); private volatile HarTimings timings = new HarTimings(); private volatile String serverIPAddress; + @JsonProperty("time") + private volatile long time; private volatile String connection; private volatile String comment = ""; @@ -157,4 +160,10 @@ public String getConnection() { public void setConnection(String connection) { this.connection = connection; } + + public HarEntry setTime(long time) { + this.time = time; + return this; + } + } diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarLog.java b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarLog.java index 879e071f3..c7a6d1ef5 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarLog.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarLog.java @@ -10,8 +10,8 @@ public class HarLog { private final String version = "1.2"; private volatile HarNameVersion creator; private volatile HarNameVersion browser; - private final List pages = new CopyOnWriteArrayList(); - private final List entries = new CopyOnWriteArrayList(); + private final List pages = new CopyOnWriteArrayList<>(); + private final List entries = new CopyOnWriteArrayList<>(); private volatile String comment = ""; public HarLog() { diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarRequest.java b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarRequest.java index 2cfe68957..4e549ad77 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarRequest.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarRequest.java @@ -1,6 +1,8 @@ package net.lightbody.bmp.core.har; import com.fasterxml.jackson.annotation.JsonInclude; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -10,9 +12,9 @@ public class HarRequest { private volatile String method; private volatile String url; private volatile String httpVersion; - private final List cookies = new CopyOnWriteArrayList(); - private final List headers = new CopyOnWriteArrayList(); - private final List queryString = new CopyOnWriteArrayList(); + private final List cookies = new CopyOnWriteArrayList<>(); + private final List headers = new CopyOnWriteArrayList<>(); + private final List queryString = new CopyOnWriteArrayList<>(); private volatile HarPostData postData; private volatile long headersSize; // Odd grammar in spec private volatile long bodySize; @@ -95,4 +97,41 @@ public void setComment(String comment) { this.comment = comment; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || getClass() != o.getClass()) return false; + + HarRequest that = (HarRequest) o; + + return new EqualsBuilder() + .append(headersSize, that.headersSize) + .append(bodySize, that.bodySize) + .append(method, that.method) + .append(url, that.url) + .append(httpVersion, that.httpVersion) + .append(cookies, that.cookies) + .append(headers, that.headers) + .append(queryString, that.queryString) + .append(postData, that.postData) + .append(comment, that.comment) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(method) + .append(url) + .append(httpVersion) + .append(cookies) + .append(headers) + .append(queryString) + .append(postData) + .append(headersSize) + .append(bodySize) + .append(comment) + .toHashCode(); + } } diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarResponse.java b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarResponse.java index f82f248dd..b0e562a3b 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarResponse.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarResponse.java @@ -11,8 +11,8 @@ public class HarResponse { private volatile int status; private volatile String statusText; private volatile String httpVersion; - private final List cookies = new CopyOnWriteArrayList(); - private final List headers = new CopyOnWriteArrayList(); + private final List cookies = new CopyOnWriteArrayList<>(); + private final List headers = new CopyOnWriteArrayList<>(); private final HarContent content = new HarContent(); private volatile String redirectURL = ""; diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarTimings.java b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarTimings.java index efb8d8612..3849baf4b 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarTimings.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarTimings.java @@ -1,15 +1,24 @@ package net.lightbody.bmp.core.har; +import com.fasterxml.jackson.annotation.JsonProperty; + import java.util.concurrent.TimeUnit; public class HarTimings { // optional values are initialized to -1, which indicates they do not apply to the current request, according to the HAR spec + @JsonProperty("blocked") private volatile long blockedNanos = -1; + @JsonProperty("dns") private volatile long dnsNanos = -1; + @JsonProperty("connect") private volatile long connectNanos = -1; + @JsonProperty("send") private volatile long sendNanos; + @JsonProperty("wait") private volatile long waitNanos; + @JsonProperty("receive") private volatile long receiveNanos; + @JsonProperty("ssl") private volatile long sslNanos = -1; private volatile String comment = ""; @@ -49,7 +58,7 @@ public long getDns(TimeUnit timeUnit) { public void setDns(long dns, TimeUnit timeUnit) { if (dns == -1) { this.dnsNanos = -1; - } else{ + } else { this.dnsNanos = TimeUnit.NANOSECONDS.convert(dns, timeUnit); } } diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/AddHeadersFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/AddHeadersFilter.java index 5b584ecfd..16bdccc72 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/filters/AddHeadersFilter.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/AddHeadersFilter.java @@ -1,9 +1,10 @@ package net.lightbody.bmp.filters; +import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.HttpObject; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponse; -import org.littleshoot.proxy.HttpFiltersAdapter; +import org.apache.commons.lang3.StringUtils; import java.util.Collections; import java.util.Map; @@ -12,29 +13,48 @@ * Adds the headers specified in the constructor to this request. The filter does not make a defensive copy of the map, so there is no guarantee * that the map at the time of construction will contain the same values when the filter is actually invoked, if the map is modified concurrently. */ -public class AddHeadersFilter extends HttpFiltersAdapter { +public class AddHeadersFilter extends HttpsAwareFiltersAdapter { private final Map additionalHeaders; + private static String headersSpecificFilter; - public AddHeadersFilter(HttpRequest originalRequest, Map additionalHeaders) { - super(originalRequest); + public AddHeadersFilter(HttpRequest originalRequest, ChannelHandlerContext ctx, Map additionalHeaders, String headersSpecificFilter) { + super(originalRequest, ctx); if (additionalHeaders != null) { this.additionalHeaders = additionalHeaders; } else { this.additionalHeaders = Collections.emptyMap(); } + if (StringUtils.isNotEmpty(headersSpecificFilter)) { + setHeadersSpecificFilter(headersSpecificFilter); + } } @Override public HttpResponse clientToProxyRequest(HttpObject httpObject) { if (httpObject instanceof HttpRequest) { HttpRequest httpRequest = (HttpRequest) httpObject; - - for (Map.Entry header : additionalHeaders.entrySet()) { - httpRequest.headers().add(header.getKey(), header.getValue()); + if (StringUtils.isNotEmpty(headersSpecificFilter)) { + if (getFullUrl(httpRequest).matches(headersSpecificFilter)) { + for (Map.Entry header : additionalHeaders.entrySet()) { + httpRequest.headers().add(header.getKey(), header.getValue()); + } + } + } else { + for (Map.Entry header : additionalHeaders.entrySet()) { + httpRequest.headers().add(header.getKey(), header.getValue()); + } } } return null; } + + public String getHeadersSpecificFilter() { + return headersSpecificFilter; + } + + private synchronized void setHeadersSpecificFilter(String headersSpecificFilter) { + this.headersSpecificFilter = headersSpecificFilter; + } } diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/AutoBasicAuthFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/AutoBasicAuthFilter.java index 758f9bfaa..4cc840d97 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/filters/AutoBasicAuthFilter.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/AutoBasicAuthFilter.java @@ -1,10 +1,7 @@ package net.lightbody.bmp.filters; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.*; import org.littleshoot.proxy.impl.ProxyUtils; import java.util.Map; @@ -45,7 +42,7 @@ public HttpResponse clientToProxyRequest(HttpObject httpObject) { // if there is an entry in the credentials map matching this hostname, add the credentials to the request String base64CredentialsForHostname = credentialsByHostname.get(hostname); if (base64CredentialsForHostname != null) { - httpRequest.headers().add(HttpHeaders.Names.AUTHORIZATION, "Basic " + base64CredentialsForHostname); + httpRequest.headers().add(HttpHeaderNames.AUTHORIZATION, "Basic " + base64CredentialsForHostname); } } diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/BlacklistFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/BlacklistFilter.java index b267673e1..0eef83d36 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/filters/BlacklistFilter.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/BlacklistFilter.java @@ -1,13 +1,7 @@ package net.lightbody.bmp.filters; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.*; import net.lightbody.bmp.proxy.BlacklistEntry; import java.util.Collection; @@ -38,15 +32,15 @@ public HttpResponse clientToProxyRequest(HttpObject httpObject) { String url = getFullUrl(httpRequest); for (BlacklistEntry entry : blacklistedUrls) { - if (HttpMethod.CONNECT.equals(httpRequest.getMethod()) && entry.getHttpMethodPattern() == null) { + if (HttpMethod.CONNECT.equals(httpRequest.method()) && entry.getHttpMethodPattern() == null) { // do not allow CONNECTs to be blacklisted unless a method pattern is explicitly specified continue; } - if (entry.matches(url, httpRequest.getMethod().name())) { + if (entry.matches(url, httpRequest.method().name())) { HttpResponseStatus status = HttpResponseStatus.valueOf(entry.getStatusCode()); - HttpResponse resp = new DefaultFullHttpResponse(httpRequest.getProtocolVersion(), status); - HttpHeaders.setContentLength(resp, 0L); + HttpResponse resp = new DefaultFullHttpResponse(httpRequest.protocolVersion(), status); + HttpUtil.setContentLength(resp, 0L); return resp; } diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/BrowserMobHttpFilterChain.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/BrowserMobHttpFilterChain.java index 6ddf1331f..b7c11044e 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/filters/BrowserMobHttpFilterChain.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/BrowserMobHttpFilterChain.java @@ -1,12 +1,7 @@ package net.lightbody.bmp.filters; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.*; import net.lightbody.bmp.BrowserMobProxyServer; import org.littleshoot.proxy.HttpFilters; import org.littleshoot.proxy.HttpFiltersAdapter; @@ -54,9 +49,9 @@ public BrowserMobHttpFilterChain(BrowserMobProxyServer proxyServer, HttpRequest @Override public HttpResponse clientToProxyRequest(HttpObject httpObject) { if (proxyServer.isStopped()) { - log.warn("Aborting request to {} because proxy is stopped", originalRequest.getUri()); - HttpResponse abortedResponse = new DefaultFullHttpResponse(originalRequest.getProtocolVersion(), HttpResponseStatus.SERVICE_UNAVAILABLE); - HttpHeaders.setContentLength(abortedResponse, 0L); + log.warn("Aborting request to {} because proxy is stopped", originalRequest.uri()); + HttpResponse abortedResponse = new DefaultFullHttpResponse(originalRequest.protocolVersion(), HttpResponseStatus.SERVICE_UNAVAILABLE); + HttpUtil.setContentLength(abortedResponse, 0L); return abortedResponse; } diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/ClientRequestCaptureFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/ClientRequestCaptureFilter.java index d6bd1b58c..3315c0640 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/filters/ClientRequestCaptureFilter.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/ClientRequestCaptureFilter.java @@ -2,12 +2,7 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.LastHttpContent; +import io.netty.handler.codec.http.*; import net.lightbody.bmp.util.BrowserMobHttpUtil; import org.littleshoot.proxy.HttpFiltersAdapter; @@ -60,7 +55,7 @@ public HttpResponse clientToProxyRequest(HttpObject httpObject) { if (httpContent instanceof LastHttpContent) { LastHttpContent lastHttpContent = (LastHttpContent) httpContent; - trailingHeaders = lastHttpContent .trailingHeaders(); + trailingHeaders = lastHttpContent.trailingHeaders(); } } diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/HarCaptureFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/HarCaptureFilter.java index ca8c044c2..e4aa0a582 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/filters/HarCaptureFilter.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/HarCaptureFilter.java @@ -2,9 +2,13 @@ import com.google.common.collect.ImmutableList; import com.google.common.io.BaseEncoding; +import com.timgroup.statsd.NonBlockingStatsDClient; +import com.timgroup.statsd.StatsDClient; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpHeaderValues; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpObject; import io.netty.handler.codec.http.HttpRequest; @@ -26,10 +30,12 @@ import net.lightbody.bmp.filters.support.HttpConnectTiming; import net.lightbody.bmp.filters.util.HarCaptureUtil; import net.lightbody.bmp.proxy.CaptureType; +import net.lightbody.bmp.util.BeansJsonMapper; import net.lightbody.bmp.util.BrowserMobHttpUtil; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.StringMapMessage; import org.littleshoot.proxy.impl.ProxyUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -40,12 +46,20 @@ import java.util.EnumSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import static net.lightbody.bmp.filters.StatsDMetricsFilter.getProxyPrefix; +import static net.lightbody.bmp.filters.StatsDMetricsFilter.getStatsDHost; +import static net.lightbody.bmp.filters.StatsDMetricsFilter.getStatsDPort; +import static net.lightbody.bmp.filters.StatsDMetricsFilter.prepareMetric; + public class HarCaptureFilter extends HttpsAwareFiltersAdapter { - private static final Logger log = LoggerFactory.getLogger(HarCaptureFilter.class); + private static final Logger log = LogManager.getLogger(HarCaptureFilter.class); + + private static final InheritableThreadLocal isAlreadyLoggedIn = new InheritableThreadLocal<>(); /** * The currently active HAR at the time the current request is received. @@ -123,16 +137,16 @@ public class HarCaptureFilter extends HttpsAwareFiltersAdapter { *

* Regardless of the CaptureTypes specified in dataToCapture, the HarCaptureFilter will always capture: *

    - *
  • Request and response sizes
  • - *
  • HTTP request and status lines
  • - *
  • Page timing information
  • + *
  • Request and response sizes
  • + *
  • HTTP request and status lines
  • + *
  • Page timing information
  • *
* * @param originalRequest the original HttpRequest from the HttpFiltersSource factory - * @param har a reference to the ProxyServer's current HAR file at the time this request is received (can be null if HAR capture is not required) - * @param currentPageRef the ProxyServer's currentPageRef at the time this request is received from the client - * @param dataToCapture the data types to capture for this request. null or empty set indicates only basic information will be - * captured (see {@link net.lightbody.bmp.proxy.CaptureType} for information on data collected for each CaptureType) + * @param har a reference to the ProxyServer's current HAR file at the time this request is received (can be null if HAR capture is not required) + * @param currentPageRef the ProxyServer's currentPageRef at the time this request is received from the client + * @param dataToCapture the data types to capture for this request. null or empty set indicates only basic information will be + * captured (see {@link net.lightbody.bmp.proxy.CaptureType} for information on data collected for each CaptureType) */ public HarCaptureFilter(HttpRequest originalRequest, ChannelHandlerContext ctx, Har har, String currentPageRef, Set dataToCapture) { super(originalRequest, ctx); @@ -269,7 +283,7 @@ public HttpObject serverToProxyResponse(HttpObject httpObject) { harEntry.getResponse().setBodySize(responseBodySize.get()); } - + logFailedRequestIfRequired(harEntry.getRequest(), harEntry.getResponse()); return super.serverToProxyResponse(httpObject); } @@ -297,6 +311,13 @@ else if (sendFinishedNanos > 0L && responseReceiveStartedNanos == 0L) { else if (responseReceiveStartedNanos > 0L) { harEntry.getTimings().setReceive(timeoutTimestampNanos - responseReceiveStartedNanos, TimeUnit.NANOSECONDS); } + + StatsDClient client = createStatsDClient(); + client.increment(getProxyPrefix().concat(prepareMetric(harEntry.getRequest().getUrl())) + .concat("." + harEntry.getResponse().getStatus()).concat(".response_timeout")); + client.stop(); + + logFailedRequestIfRequired(harEntry.getRequest(), harEntry.getResponse()); } /** @@ -312,7 +333,7 @@ private HarRequest createHarRequestForHttpRequest(HttpRequest httpRequest) { // the full URL consists of the scheme + host + port (if non-standard) + path + query params + fragment. String url = getFullUrl(httpRequest); - return new HarRequest(httpRequest.getMethod().toString(), url, httpRequest.getProtocolVersion().text()); + return new HarRequest(httpRequest.method().toString(), url, httpRequest.protocolVersion().text()); } //TODO: add unit tests for these utility-like capture() methods @@ -320,7 +341,7 @@ private HarRequest createHarRequestForHttpRequest(HttpRequest httpRequest) { protected void captureQueryParameters(HttpRequest httpRequest) { // capture query parameters. it is safe to assume the query string is UTF-8, since it "should" be in US-ASCII (a subset of UTF-8), // but sometimes does include UTF-8 characters. - QueryStringDecoder queryStringDecoder = new QueryStringDecoder(httpRequest.getUri(), StandardCharsets.UTF_8); + QueryStringDecoder queryStringDecoder = new QueryStringDecoder(httpRequest.uri(), StandardCharsets.UTF_8); try { for (Map.Entry> entry : queryStringDecoder.parameters().entrySet()) { @@ -331,13 +352,13 @@ protected void captureQueryParameters(HttpRequest httpRequest) { } catch (IllegalArgumentException e) { // QueryStringDecoder will throw an IllegalArgumentException if it cannot interpret a query string. rather than cause the entire request to // fail by propagating the exception, simply skip the query parameter capture. - harEntry.setComment("Unable to decode query parameters on URI: " + httpRequest.getUri()); - log.info("Unable to decode query parameters on URI: " + httpRequest.getUri(), e); + harEntry.setComment("Unable to decode query parameters on URI: " + httpRequest.uri()); + log.info("Unable to decode query parameters on URI: " + httpRequest.uri(), e); } } protected void captureRequestHeaderSize(HttpRequest httpRequest) { - String requestLine = httpRequest.getMethod().toString() + ' ' + httpRequest.getUri() + ' ' + httpRequest.getProtocolVersion().toString(); + String requestLine = httpRequest.method().toString() + ' ' + httpRequest.uri() + ' ' + httpRequest.protocolVersion().toString(); // +2 => CRLF after status line, +4 => header/data separation long requestHeadersSize = requestLine.length() + 6; @@ -348,7 +369,7 @@ protected void captureRequestHeaderSize(HttpRequest httpRequest) { } protected void captureRequestCookies(HttpRequest httpRequest) { - String cookieHeader = httpRequest.headers().get(HttpHeaders.Names.COOKIE); + String cookieHeader = httpRequest.headers().get(HttpHeaderNames.COOKIE); if (cookieHeader == null) { return; } @@ -388,9 +409,9 @@ protected void captureRequestContent(HttpRequest httpRequest, byte[] fullMessage return; } - String contentType = HttpHeaders.getHeader(httpRequest, HttpHeaders.Names.CONTENT_TYPE); + String contentType = httpRequest.headers().get(HttpHeaderNames.CONTENT_TYPE); if (contentType == null) { - log.warn("No content type specified in request to {}. Content will be treated as {}", httpRequest.getUri(), BrowserMobHttpUtil.UNKNOWN_CONTENT_TYPE); + log.warn("No content type specified in request to {}. Content will be treated as {}", httpRequest.uri(), BrowserMobHttpUtil.UNKNOWN_CONTENT_TYPE); contentType = BrowserMobHttpUtil.UNKNOWN_CONTENT_TYPE; } @@ -400,24 +421,20 @@ protected void captureRequestContent(HttpRequest httpRequest, byte[] fullMessage postData.setMimeType(contentType); boolean urlEncoded; - if (contentType.startsWith(HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED)) { - urlEncoded = true; - } else { - urlEncoded = false; - } + urlEncoded = contentType.startsWith(HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString()); Charset charset; try { - charset = BrowserMobHttpUtil.readCharsetInContentTypeHeader(contentType); + charset = BrowserMobHttpUtil.readCharsetInContentTypeHeader(contentType); } catch (UnsupportedCharsetException e) { - log.warn("Found unsupported character set in Content-Type header '{}' in HTTP request to {}. Content will not be captured in HAR.", contentType, httpRequest.getUri(), e); + log.warn("Found unsupported character set in Content-Type header '{}' in HTTP request to {}. Content will not be captured in HAR.", contentType, httpRequest.uri(), e); return; } if (charset == null) { // no charset specified, so use the default -- but log a message since this might not encode the data correctly charset = BrowserMobHttpUtil.DEFAULT_HTTP_CHARSET; - log.debug("No charset specified; using charset {} to decode contents to {}", charset, httpRequest.getUri()); + log.debug("No charset specified; using charset {} to decode contents to {}", charset, httpRequest.uri()); } if (urlEncoded) { @@ -447,9 +464,9 @@ protected void captureResponseContent(HttpResponse httpResponse, byte[] fullMess // force binary if the content encoding is not supported boolean forceBinary = false; - String contentType = HttpHeaders.getHeader(httpResponse, HttpHeaders.Names.CONTENT_TYPE); + String contentType = httpResponse.headers().get(HttpHeaderNames.CONTENT_TYPE); if (contentType == null) { - log.warn("No content type specified in response from {}. Content will be treated as {}", originalRequest.getUri(), BrowserMobHttpUtil.UNKNOWN_CONTENT_TYPE); + log.warn("No content type specified in response from {}. Content will be treated as {}", originalRequest.uri(), BrowserMobHttpUtil.UNKNOWN_CONTENT_TYPE); contentType = BrowserMobHttpUtil.UNKNOWN_CONTENT_TYPE; } @@ -463,14 +480,14 @@ protected void captureResponseContent(HttpResponse httpResponse, byte[] fullMess try { charset = BrowserMobHttpUtil.readCharsetInContentTypeHeader(contentType); } catch (UnsupportedCharsetException e) { - log.warn("Found unsupported character set in Content-Type header '{}' in HTTP response from {}. Content will not be captured in HAR.", contentType, originalRequest.getUri(), e); + log.warn("Found unsupported character set in Content-Type header '{}' in HTTP response from {}. Content will not be captured in HAR.", contentType, originalRequest.uri(), e); return; } if (charset == null) { // no charset specified, so use the default -- but log a message since this might not encode the data correctly charset = BrowserMobHttpUtil.DEFAULT_HTTP_CHARSET; - log.debug("No charset specified; using charset {} to decode contents from {}", charset, originalRequest.getUri()); + log.debug("No charset specified; using charset {} to decode contents from {}", charset, originalRequest.uri()); } if (!forceBinary && BrowserMobHttpUtil.hasTextualContent(contentType)) { @@ -485,7 +502,7 @@ protected void captureResponseContent(HttpResponse httpResponse, byte[] fullMess } protected void captureResponse(HttpResponse httpResponse) { - HarResponse response = new HarResponse(httpResponse.getStatus().code(), httpResponse.getStatus().reasonPhrase(), httpResponse.getProtocolVersion().text()); + HarResponse response = new HarResponse(httpResponse.status().code(), httpResponse.status().reasonPhrase(), httpResponse.protocolVersion().text()); harEntry.setResponse(response); captureResponseHeaderSize(httpResponse); @@ -506,7 +523,7 @@ protected void captureResponse(HttpResponse httpResponse) { } protected void captureResponseMimeType(HttpResponse httpResponse) { - String contentType = HttpHeaders.getHeader(httpResponse, HttpHeaders.Names.CONTENT_TYPE); + String contentType = httpResponse.headers().get(HttpHeaderNames.CONTENT_TYPE); // don't set the mimeType to null, since mimeType is a required field if (contentType != null) { harEntry.getResponse().getContent().setMimeType(contentType); @@ -514,7 +531,7 @@ protected void captureResponseMimeType(HttpResponse httpResponse) { } protected void captureResponseCookies(HttpResponse httpResponse) { - List setCookieHeaders = httpResponse.headers().getAll(HttpHeaders.Names.SET_COOKIE); + List setCookieHeaders = httpResponse.headers().getAll(HttpHeaderNames.SET_COOKIE); if (setCookieHeaders == null) { return; } @@ -552,7 +569,7 @@ protected void captureResponseCookies(HttpResponse httpResponse) { } protected void captureResponseHeaderSize(HttpResponse httpResponse) { - String statusLine = httpResponse.getProtocolVersion().toString() + ' ' + httpResponse.getStatus().toString(); + String statusLine = httpResponse.protocolVersion().toString() + ' ' + httpResponse.status().toString(); // +2 => CRLF after status line, +4 => header/data separation long responseHeadersSize = statusLine.length() + 6; HttpHeaders headers = httpResponse.headers(); @@ -569,7 +586,7 @@ protected void captureResponseHeaders(HttpResponse httpResponse) { } protected void captureRedirectUrl(HttpResponse httpResponse) { - String locationHeaderValue = HttpHeaders.getHeader(httpResponse, HttpHeaders.Names.LOCATION); + String locationHeaderValue = httpResponse.headers().get(HttpHeaderNames.LOCATION); if (locationHeaderValue != null) { harEntry.getResponse().setRedirectURL(locationHeaderValue); } @@ -629,7 +646,7 @@ protected void populateAddressFromCache(HttpRequest httpRequest) { log.trace("Unable to find cached IP address for host: {}. IP address in HAR entry will be blank.", serverHost); } } else { - log.warn("Unable to identify host from request uri: {}", httpRequest.getUri()); + log.warn("Unable to identify host from request uri: {}", httpRequest.uri()); } } @@ -658,6 +675,12 @@ public void proxyToServerResolutionFailed(String hostAndPort) { if (dnsResolutionStartedNanos > 0L) { harEntry.getTimings().setDns(System.nanoTime() - dnsResolutionStartedNanos, TimeUnit.NANOSECONDS); } + + StatsDClient client = createStatsDClient(); + client.increment(getProxyPrefix().concat(prepareMetric(harEntry.getRequest().getUrl())) + .concat("." + harEntry.getResponse().getStatus()).concat(".server_resolution_fail")); + client.stop(); + logFailedRequestIfRequired(harEntry.getRequest(), harEntry.getResponse()); } @Override @@ -692,6 +715,7 @@ public void proxyToServerConnectionStarted() { @Override public void proxyToServerConnectionFailed() { HarResponse response = HarCaptureUtil.createHarResponseForFailure(); + harEntry.setResponse(response); response.setError(HarCaptureUtil.getConnectionFailedErrorMessage()); @@ -700,6 +724,11 @@ public void proxyToServerConnectionFailed() { if (connectionStartedNanos > 0L) { harEntry.getTimings().setConnect(System.nanoTime() - connectionStartedNanos, TimeUnit.NANOSECONDS); } + + StatsDClient client = createStatsDClient(); + client.increment(getProxyPrefix().concat(prepareMetric(harEntry.getRequest().getUrl())) + .concat("." + harEntry.getResponse().getStatus()).concat(".server_connection_fail")); + client.stop(); } @Override @@ -763,4 +792,24 @@ public void serverToProxyResponseReceived() { harEntry.getTimings().setReceive(0L, TimeUnit.NANOSECONDS); } } + + protected static void logFailedRequestIfRequired(HarRequest request, HarResponse response) { + if ((Objects.isNull(isAlreadyLoggedIn.get()) || + isAlreadyLoggedIn.get().hashCode() != request.hashCode()) + && (response.getStatus() >= 500 || response.getStatus() == 0)) { + log.error(new StringMapMessage() + .with("message", "received bad status code") + .with("caller", "mobproxy") + .with("http_response_code", String.valueOf(response.getStatus())) + .with("http_host", request.getUrl()) + .with("request_details", BeansJsonMapper.getJsonString(request)) + .with("method", request.getMethod()) + .with("response", BeansJsonMapper.getJsonString(response))); + isAlreadyLoggedIn.set(request); + } + } + + private StatsDClient createStatsDClient() { + return new NonBlockingStatsDClient("automated_tests", getStatsDHost(), getStatsDPort()); + } } diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpConnectHarCaptureFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpConnectHarCaptureFilter.java index 1717370a2..de4829a67 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpConnectHarCaptureFilter.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpConnectHarCaptureFilter.java @@ -5,11 +5,7 @@ import io.netty.handler.codec.http.HttpObject; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponse; -import net.lightbody.bmp.core.har.Har; -import net.lightbody.bmp.core.har.HarEntry; -import net.lightbody.bmp.core.har.HarRequest; -import net.lightbody.bmp.core.har.HarResponse; -import net.lightbody.bmp.core.har.HarTimings; +import net.lightbody.bmp.core.har.*; import net.lightbody.bmp.filters.support.HttpConnectTiming; import net.lightbody.bmp.filters.util.HarCaptureUtil; import net.lightbody.bmp.util.HttpUtil; @@ -31,7 +27,6 @@ * static methods. This filter also handles HTTP CONNECT errors and creates HAR entries for those errors, since there * would otherwise not be any record in the HAR of the error (if the CONNECT fails, there will be no subsequent "real" * request in which to record the error). - * */ public class HttpConnectHarCaptureFilter extends HttpsAwareFiltersAdapter implements ModifiedRequestAwareFilter { private static final Logger log = LoggerFactory.getLogger(HttpConnectHarCaptureFilter.class); @@ -104,7 +99,7 @@ public class HttpConnectHarCaptureFilter extends HttpsAwareFiltersAdapter implem /** * Stores SSL connection timing information from HTTP CONNNECT requests. This timing information is stored in the first HTTP request * after the CONNECT, not in the CONNECT itself, so it needs to be stored across requests. - * + *

* This is the only state stored across multiple requests. */ private static final ConcurrentMap httpConnectTimes = @@ -159,6 +154,7 @@ public void proxyToServerResolutionFailed(String hostAndPort) { harEntry.getTimings().setDns(System.nanoTime() - dnsResolutionStartedNanos, TimeUnit.NANOSECONDS); } + HarCaptureFilter.logFailedRequestIfRequired(harEntry.getRequest(), harEntry.getResponse()); httpConnectTimes.remove(clientAddress); } @@ -219,6 +215,7 @@ else if (sendFinishedNanos > 0L && responseReceiveStartedNanos == 0L) { else if (responseReceiveStartedNanos > 0L) { harEntry.getTimings().setReceive(timeoutTimestampNanos - responseReceiveStartedNanos, TimeUnit.NANOSECONDS); } + HarCaptureFilter.logFailedRequestIfRequired(harEntry.getRequest(), harEntry.getResponse()); } @Override @@ -337,7 +334,6 @@ private HarEntry createHarEntryForFailedCONNECT(String errorMessage) { populateServerIpAddress(harEntry); - return harEntry; } @@ -358,7 +354,7 @@ private void populateServerIpAddress(HarEntry harEntry) { log.trace("Unable to find cached IP address for host: {}. IP address in HAR entry will be blank.", serverHost); } } else { - log.warn("Unable to identify host from request uri: {}", modifiedHttpRequest.getUri()); + log.warn("Unable to identify host from request uri: {}", modifiedHttpRequest.uri()); } } } @@ -373,7 +369,7 @@ private void populateServerIpAddress(HarEntry harEntry) { private HarRequest createRequestForFailedConnect(HttpRequest httpConnectRequest) { String url = getFullUrl(httpConnectRequest); - return new HarRequest(httpConnectRequest.getMethod().toString(), url, httpConnectRequest.getProtocolVersion().text()); + return new HarRequest(httpConnectRequest.method().toString(), url, httpConnectRequest.protocolVersion().text()); } /** diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpsAwareFiltersAdapter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpsAwareFiltersAdapter.java index e727f2b00..aa898e508 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpsAwareFiltersAdapter.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpsAwareFiltersAdapter.java @@ -32,7 +32,7 @@ public HttpsAwareFiltersAdapter(HttpRequest originalRequest, ChannelHandlerConte * @return true if https, false if http */ public boolean isHttps() { - Attribute isHttpsAttr = ctx.attr(AttributeKey.valueOf(IS_HTTPS_ATTRIBUTE_NAME)); + Attribute isHttpsAttr = ctx.attr(AttributeKey.valueOf(IS_HTTPS_ATTRIBUTE_NAME)); Boolean isHttps = isHttpsAttr.get(); if (isHttps == null) { @@ -54,14 +54,14 @@ public String getFullUrl(HttpRequest modifiedRequest) { // special case: for HTTPS requests, the full URL is scheme (https://) + the URI of this request if (ProxyUtils.isCONNECT(modifiedRequest)) { // CONNECT requests contain the default port, even if it isn't specified on the request. - String hostNoDefaultPort = BrowserMobHttpUtil.removeMatchingPort(modifiedRequest.getUri(), 443); + String hostNoDefaultPort = BrowserMobHttpUtil.removeMatchingPort(modifiedRequest.uri(), 443); return "https://" + hostNoDefaultPort; } // To get the full URL, we need to retrieve the Scheme, Host + Port, Path, and Query Params from the request. // If the request URI starts with http:// or https://, it is already a full URL and can be returned directly. - if (HttpUtil.startsWithHttpOrHttps(modifiedRequest.getUri())) { - return modifiedRequest.getUri(); + if (HttpUtil.startsWithHttpOrHttps(modifiedRequest.uri())) { + return modifiedRequest.uri(); } // The URI did not include the scheme and host, so examine the request to obtain them: @@ -70,7 +70,7 @@ public String getFullUrl(HttpRequest modifiedRequest) { // Path + Query Params: since the request URI doesn't start with the scheme, we can safely assume that the URI // contains only the path and query params. String hostAndPort = getHostAndPort(modifiedRequest); - String path = modifiedRequest.getUri(); + String path = modifiedRequest.uri(); String url; if (isHttps()) { url = "https://" + hostAndPort + path; @@ -139,7 +139,7 @@ private String getHttpsRequestHostAndPort() throws IllegalStateException { throw new IllegalStateException("Request is not HTTPS. Cannot get host and port on non-HTTPS request using this method."); } - Attribute hostnameAttr = ctx.attr(AttributeKey.valueOf(HOST_ATTRIBUTE_NAME)); + Attribute hostnameAttr = ctx.attr(AttributeKey.valueOf(HOST_ATTRIBUTE_NAME)); return hostnameAttr.get(); } @@ -156,7 +156,7 @@ private String getHttpsOriginalRequestHostAndPort() throws IllegalStateException throw new IllegalStateException("Request is not HTTPS. Cannot get original host and port on non-HTTPS request using this method."); } - Attribute hostnameAttr = ctx.attr(AttributeKey.valueOf(ORIGINAL_HOST_ATTRIBUTE_NAME)); + Attribute hostnameAttr = ctx.attr(AttributeKey.valueOf(ORIGINAL_HOST_ATTRIBUTE_NAME)); return hostnameAttr.get(); } } diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpsHostCaptureFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpsHostCaptureFilter.java index f2a52a014..3225ae8ad 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpsHostCaptureFilter.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpsHostCaptureFilter.java @@ -27,8 +27,8 @@ public HttpResponse clientToProxyRequest(HttpObject httpObject) { HttpRequest httpRequest = (HttpRequest) httpObject; if (ProxyUtils.isCONNECT(httpRequest)) { - Attribute hostname = ctx.attr(AttributeKey.valueOf(HttpsAwareFiltersAdapter.HOST_ATTRIBUTE_NAME)); - String hostAndPort = httpRequest.getUri(); + Attribute hostname = ctx.attr(AttributeKey.valueOf(HttpsAwareFiltersAdapter.HOST_ATTRIBUTE_NAME)); + String hostAndPort = httpRequest.uri(); // CONNECT requests contain the port, even when using the default port. a sensible default is to remove the // default port, since in most cases it is not explicitly specified and its presence (in a HAR file, for example) diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpsOriginalHostCaptureFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpsOriginalHostCaptureFilter.java index 4a6894c4d..85a7b444a 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpsOriginalHostCaptureFilter.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpsOriginalHostCaptureFilter.java @@ -4,7 +4,6 @@ import io.netty.handler.codec.http.HttpRequest; import io.netty.util.Attribute; import io.netty.util.AttributeKey; -import org.littleshoot.proxy.HttpFiltersAdapter; import org.littleshoot.proxy.impl.ProxyUtils; /** @@ -23,11 +22,11 @@ public HttpsOriginalHostCaptureFilter(HttpRequest originalRequest, ChannelHandle // capturing the original host (and the remapped/modified host in clientToProxyRequest() below) guarantees that we will // have the "true" host, rather than relying on the Host header in subsequent requests (which may be absent or spoofed by malicious clients). if (ProxyUtils.isCONNECT(originalRequest)) { - Attribute originalHostAttr = ctx.attr(AttributeKey.valueOf(HttpsAwareFiltersAdapter.ORIGINAL_HOST_ATTRIBUTE_NAME)); - String hostAndPort = originalRequest.getUri(); + Attribute originalHostAttr = ctx.attr(AttributeKey.valueOf(HttpsAwareFiltersAdapter.ORIGINAL_HOST_ATTRIBUTE_NAME)); + String hostAndPort = originalRequest.uri(); originalHostAttr.set(hostAndPort); - Attribute isHttpsAttr = ctx.attr(AttributeKey.valueOf(HttpsAwareFiltersAdapter.IS_HTTPS_ATTRIBUTE_NAME)); + Attribute isHttpsAttr = ctx.attr(AttributeKey.valueOf(HttpsAwareFiltersAdapter.IS_HTTPS_ATTRIBUTE_NAME)); isHttpsAttr.set(true); } } diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/ResponseFilterAdapter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/ResponseFilterAdapter.java index 7edc34dd1..7097d07bd 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/filters/ResponseFilterAdapter.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/ResponseFilterAdapter.java @@ -62,7 +62,7 @@ public void setModifiedHttpRequest(HttpRequest modifiedHttpRequest) { * and sets a maximum response buffer size of 2 MiB. */ public static class FilterSource extends HttpFiltersSourceAdapter { - private static final int DEFAULT_MAXIMUM_RESPONSE_BUFFER_SIZE = 2097152; + public static final int DEFAULT_MAXIMUM_RESPONSE_BUFFER_SIZE = 2097152; private final ResponseFilter filter; private final int maximumResponseBufferSizeInBytes; @@ -100,7 +100,7 @@ public HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerCont @Override public int getMaximumResponseBufferSizeInBytes() { - return maximumResponseBufferSizeInBytes; + return Math.toIntExact(maximumResponseBufferSizeInBytes); } } } diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/RewriteUrlFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/RewriteUrlFilter.java index ef0e223c0..d1fc9f29b 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/filters/RewriteUrlFilter.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/RewriteUrlFilter.java @@ -1,10 +1,7 @@ package net.lightbody.bmp.filters; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.*; import net.lightbody.bmp.util.HttpUtil; import net.lightbody.bmp.proxy.RewriteRule; import net.lightbody.bmp.util.BrowserMobHttpUtil; @@ -64,7 +61,7 @@ public HttpResponse clientToProxyRequest(HttpObject httpObject) { // if the URI in the request contains the scheme, host, and port, the request's URI can be replaced // with the rewritten URI. if not (for example, on HTTPS requests), strip the scheme, host, and port from // the rewritten URL before replacing the URI on the request. - String uriFromRequest = httpRequest.getUri(); + String uriFromRequest = httpRequest.uri(); if (HttpUtil.startsWithHttpOrHttps(uriFromRequest)) { httpRequest.setUri(rewrittenUrl); } else { @@ -117,8 +114,8 @@ public HttpResponse clientToProxyRequest(HttpObject httpObject) { originalHostAndPort, modifiedHostAndPort); } else { // only modify the Host header if it already exists - if (httpRequest.headers().contains(HttpHeaders.Names.HOST)) { - HttpHeaders.setHost(httpRequest, modifiedHostAndPort); + if (httpRequest.headers().contains(HttpHeaderNames.HOST)) { + httpRequest.headers().set(HttpHeaderNames.HOST, modifiedHostAndPort); } } } diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/ServerResponseCaptureFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/ServerResponseCaptureFilter.java index d69ad8f76..f40c8c6ab 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/filters/ServerResponseCaptureFilter.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/ServerResponseCaptureFilter.java @@ -2,12 +2,7 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.LastHttpContent; +import io.netty.handler.codec.http.*; import net.lightbody.bmp.util.BrowserMobHttpUtil; import org.littleshoot.proxy.HttpFiltersAdapter; import org.slf4j.Logger; @@ -25,6 +20,7 @@ */ public class ServerResponseCaptureFilter extends HttpFiltersAdapter { private static final Logger log = LoggerFactory.getLogger(ServerResponseCaptureFilter.class); + private static final String BROTLI_COMPRESSION = "br"; /** * Populated by serverToProxyResponse() when processing the HttpResponse object @@ -116,7 +112,7 @@ protected void captureFullResponseContents() { if (decompressEncodedContent) { decompressContents(); - } else { + } else { // will not decompress response } } else { @@ -126,12 +122,19 @@ protected void captureFullResponseContents() { } protected void decompressContents() { - if (contentEncoding.equals(HttpHeaders.Values.GZIP)) { + if (contentEncoding.equals(HttpHeaderValues.GZIP.toString())) { try { fullResponseContents = BrowserMobHttpUtil.decompressContents(getRawResponseContents()); decompressionSuccessful = true; } catch (RuntimeException e) { - log.warn("Failed to decompress response with encoding type " + contentEncoding + " when decoding request from " + originalRequest.getUri(), e); + log.warn("Failed to decompress response with encoding type " + contentEncoding + " when decoding request from " + originalRequest.uri(), e); + } + } else if (contentEncoding.equals(BROTLI_COMPRESSION)) { + try { + fullResponseContents = BrowserMobHttpUtil.decompressBrotliContents(getRawResponseContents()); + decompressionSuccessful = true; + } catch (RuntimeException e) { + log.warn("Failed to decompress response with encoding type " + contentEncoding + " when decoding request from " + originalRequest.uri(), e); } } else { log.warn("Cannot decode unsupported content encoding type {}", contentEncoding); @@ -139,7 +142,7 @@ protected void decompressContents() { } protected void captureContentEncoding(HttpResponse httpResponse) { - contentEncoding = HttpHeaders.getHeader(httpResponse, HttpHeaders.Names.CONTENT_ENCODING); + contentEncoding = httpResponse.headers().get(HttpHeaderNames.CONTENT_ENCODING); } protected void captureTrailingHeaders(LastHttpContent lastContent) { @@ -147,7 +150,7 @@ protected void captureTrailingHeaders(LastHttpContent lastContent) { // technically, the Content-Encoding header can be in a trailing header, although this is excruciatingly uncommon if (trailingHeaders != null) { - String trailingContentEncoding = trailingHeaders.get(HttpHeaders.Names.CONTENT_ENCODING); + String trailingContentEncoding = trailingHeaders.get(HttpHeaderNames.CONTENT_ENCODING); if (trailingContentEncoding != null) { contentEncoding = trailingContentEncoding; } diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/StatsDMetricsFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/StatsDMetricsFilter.java new file mode 100644 index 000000000..c5ff20642 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/StatsDMetricsFilter.java @@ -0,0 +1,64 @@ +package net.lightbody.bmp.filters; + +import com.timgroup.statsd.NonBlockingStatsDClient; +import com.timgroup.statsd.StatsDClient; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; + +import java.net.URI; +import java.net.URISyntaxException; + +public class StatsDMetricsFilter extends HttpsAwareFiltersAdapter { + public StatsDMetricsFilter(HttpRequest originalRequest, ChannelHandlerContext ctx) { + super(originalRequest, ctx); + } + + @Override + public HttpObject serverToProxyResponse(HttpObject httpObject) { + if (httpObject instanceof HttpResponse) { + HttpResponse httpResponse = (HttpResponse) httpObject; + prepareStatsDMetrics(httpResponse.status().code()); + } + return super.serverToProxyResponse(httpObject); + } + + private void prepareStatsDMetrics(int status) { + if (status > 399 || status == 0) { + String url = getFullUrl(originalRequest); + String metric = getProxyPrefix().concat( + prepareMetric(url)).concat(String.format(".%s", status)); + StatsDClient client = new NonBlockingStatsDClient("automated_tests", getStatsDHost(), getStatsDPort()); + client.increment(metric); + client.stop(); + } + } + + + protected static String getStatsDHost() { + return StringUtils.isEmpty(System.getenv("STATSD_HOST")) ? "localhost" : System.getenv("STATSD_HOST"); + } + + protected static int getStatsDPort() { + return StringUtils.isEmpty(System.getenv("STATSD_PORT")) ? 8125 : NumberUtils.toInt(System.getenv("STATSD_PORT")); + } + + protected static String getProxyPrefix() { + return "proxy."; + } + + protected static String prepareMetric(String initialUrl) { + URI uri = null; + try { + uri = new URI(initialUrl); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + return uri.getHost().concat(uri.getPath()).replaceAll("/", "_") + .replaceAll("\\.", "_"); + } + +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/WhitelistFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/WhitelistFilter.java index ad8fbf6f6..247769928 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/filters/WhitelistFilter.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/WhitelistFilter.java @@ -1,12 +1,7 @@ package net.lightbody.bmp.filters; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.*; import org.littleshoot.proxy.impl.ProxyUtils; import java.util.Collection; @@ -23,7 +18,7 @@ public class WhitelistFilter extends HttpsAwareFiltersAdapter { private final int whitelistResponseCode; private final Collection whitelistUrls; - public WhitelistFilter(HttpRequest originalRequest, ChannelHandlerContext ctx, boolean whitelistEnabled,int whitelistResponseCode, + public WhitelistFilter(HttpRequest originalRequest, ChannelHandlerContext ctx, boolean whitelistEnabled, int whitelistResponseCode, Collection whitelistUrls) { super(originalRequest, ctx); @@ -63,8 +58,8 @@ public HttpResponse clientToProxyRequest(HttpObject httpObject) { if (!urlWhitelisted) { HttpResponseStatus status = HttpResponseStatus.valueOf(whitelistResponseCode); - HttpResponse resp = new DefaultFullHttpResponse(httpRequest.getProtocolVersion(), status); - HttpHeaders.setContentLength(resp, 0L); + HttpResponse resp = new DefaultFullHttpResponse(httpRequest.protocolVersion(), status); + HttpUtil.setContentLength(resp, 0L); return resp; } diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/DnsJavaResolver.java b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/DnsJavaResolver.java index 432032ed7..b45adb151 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/DnsJavaResolver.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/DnsJavaResolver.java @@ -108,7 +108,7 @@ protected Collection resolveHostByType(String host, int type) { } // convert the records we found into IPv4/IPv6 InetAddress objects - List addrList = new ArrayList(records.length); + List addrList = new ArrayList<>(records.length); // the InetAddresses returned by dnsjava include the trailing dot, e.g. "www.google.com." -- use the passed-in (or remapped) host value instead for (Record record : records) { diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/NativeResolver.java b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/NativeResolver.java index c71c61495..00fd62390 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/NativeResolver.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/NativeResolver.java @@ -38,9 +38,8 @@ public void setNegativeDNSCacheTimeout(int timeout, TimeUnit timeUnit) { @Override public Collection resolveRemapped(String remappedHost) { try { - Collection addresses = Arrays.asList(InetAddress.getAllByName(remappedHost)); + return Arrays.asList(InetAddress.getAllByName(remappedHost)); - return addresses; } catch (UnknownHostException e) { return Collections.emptyList(); } diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/util/BeansJsonMapper.java b/browsermob-core/src/main/java/net/lightbody/bmp/util/BeansJsonMapper.java new file mode 100644 index 000000000..5680b517a --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/util/BeansJsonMapper.java @@ -0,0 +1,39 @@ +package net.lightbody.bmp.util; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class BeansJsonMapper { + static final ObjectMapper objectMapper = new ObjectMapper() + .enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .setVisibility(new ObjectMapper().getSerializationConfig().getDefaultVisibilityChecker() + .withFieldVisibility(JsonAutoDetect.Visibility.ANY) + .withGetterVisibility(JsonAutoDetect.Visibility.NONE) + .withSetterVisibility(JsonAutoDetect.Visibility.NONE) + .withCreatorVisibility(JsonAutoDetect.Visibility.NONE)); + + private BeansJsonMapper() { + } + + public static T json2Bean(String json, Class classToInit) { + try { + return (T) objectMapper.readValue(json, classToInit); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Failed to instantiate class: " + classToInit.getName()); + } + } + + public static String getJsonString(Object object) { + try { + return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object); + } catch (JsonProcessingException e) { + e.printStackTrace(); + throw new RuntimeException("Failed to serialize object to json: " + object.getClass().getName()); + } + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/util/BrowserMobHttpUtil.java b/browsermob-core/src/main/java/net/lightbody/bmp/util/BrowserMobHttpUtil.java index 98172810f..e8986680a 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/util/BrowserMobHttpUtil.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/util/BrowserMobHttpUtil.java @@ -3,6 +3,7 @@ import com.google.common.io.BaseEncoding; import com.google.common.net.HostAndPort; import com.google.common.net.MediaType; +import org.brotli.dec.BrotliInputStream; import io.netty.buffer.ByteBuf; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpRequest; @@ -14,6 +15,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.InputStream; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; @@ -49,7 +51,7 @@ public class BrowserMobHttpUtil { * Likewise, special treatment of ISO-8859-1 has been removed from the * Accept-Charset header field. * - * + *

* Technically, we would have to determine the charset on a per-content-type basis, but generally speaking, UTF-8 is a * pretty safe default. (NOTE: In the previous HTTP/1.1 spec, section 3.7.1, the default charset was defined as ISO-8859-1.) */ @@ -78,7 +80,7 @@ public static long getHeaderSize(HttpHeaders headers) { /** * Decompresses the gzipped byte stream. * - * @param fullMessage gzipped byte stream to decomress + * @param fullMessage gzipped byte stream to decompress * @return decompressed bytes * @throws DecompressionException thrown if the fullMessage cannot be read or decompressed for any reason */ @@ -111,6 +113,42 @@ public static byte[] decompressContents(byte[] fullMessage) throws Decompression return fullMessage; } + /** + * Decompresses the brotli byze stream + * + * @param fullMessage brotli byte stream to decompress + * @return decompressed bytes + * @throws DecompressionException thrown if the fullMessage cannot be read or decompressed for any reason + */ + public static byte[] decompressBrotliContents(byte[] fullMessage) throws DecompressionException { + InputStream brotliReader = null; + ByteArrayOutputStream uncompressed; + try { + brotliReader = new BrotliInputStream(new ByteArrayInputStream(fullMessage)); + + uncompressed = new ByteArrayOutputStream(fullMessage.length); + + byte[] decompressBuffer = new byte[DECOMPRESS_BUFFER_SIZE]; + int bytesRead; + while ((bytesRead = brotliReader.read(decompressBuffer)) > -1) { + uncompressed.write(decompressBuffer, 0, bytesRead); + } + + fullMessage = uncompressed.toByteArray(); + } catch (IOException e) { + throw new DecompressionException("Unable to decompress response", e); + } finally { + try { + if (brotliReader != null) { + brotliReader.close(); + } + } catch (IOException e) { + log.warn("Unable to close brotli stream", e); + } + } + return fullMessage; + } + /** * Returns true if the content type string indicates textual content. Currently these are any Content-Types that start with one of the * following: @@ -129,11 +167,12 @@ public static byte[] decompressContents(byte[] fullMessage) throws Decompression public static boolean hasTextualContent(String contentType) { return contentType != null && (contentType.startsWith("text/") || - contentType.startsWith("application/x-javascript") || - contentType.startsWith("application/javascript") || - contentType.startsWith("application/json") || - contentType.startsWith("application/xml") || - contentType.startsWith("application/xhtml+xml") + contentType.startsWith("application/x-javascript") || + contentType.startsWith("application/javascript") || + contentType.startsWith("application/json") || + contentType.startsWith("application/xml") || + contentType.startsWith("application/xhtml+xml") || + (contentType.startsWith("application/") && contentType.endsWith("+json")) ); } @@ -184,8 +223,8 @@ public static Charset readCharsetInContentTypeHeader(String contentTypeHeader) t MediaType mediaType; try { - mediaType = MediaType.parse(contentTypeHeader); - } catch (IllegalArgumentException e) { + mediaType = MediaType.parse(contentTypeHeader); + } catch (java.lang.IllegalArgumentException e) { log.info("Unable to parse Content-Type header: {}. Content-Type header will be ignored.", contentTypeHeader, e); return null; } @@ -207,14 +246,14 @@ public static Charset readCharsetInContentTypeHeader(String contentTypeHeader) t */ public static String getRawPathAndParamsFromRequest(HttpRequest httpRequest) throws URISyntaxException { // if this request's URI contains a full URI (including scheme, host, etc.), strip away the non-path components - if (HttpUtil.startsWithHttpOrHttps(httpRequest.getUri())) { - return getRawPathAndParamsFromUri(httpRequest.getUri()); + if (HttpUtil.startsWithHttpOrHttps(httpRequest.uri())) { + return getRawPathAndParamsFromUri(httpRequest.uri()); } else { // to provide consistent validation behavior for URIs that contain a scheme and those that don't, attempt to parse // the URI, even though we discard the parsed URI object - new URI(httpRequest.getUri()); + new URI(httpRequest.uri()); - return httpRequest.getUri(); + return httpRequest.uri(); } } @@ -246,7 +285,7 @@ public static String getRawPathAndParamsFromUri(String uriString) throws URISynt * @return true if the response is a redirect, otherwise false */ public static boolean isRedirect(HttpResponse httpResponse) { - switch (httpResponse.getStatus().code()) { + switch (httpResponse.status().code()) { case 300: case 301: case 302: @@ -269,7 +308,7 @@ public static boolean isRedirect(HttpResponse httpResponse) { * parsing the hostname, but makes no guarantees. In general, it should be validated externally, if necessary. * * @param hostWithPort string containing a hostname and optional port - * @param portNumber port to remove from the string + * @param portNumber port to remove from the string * @return string with the specified port removed, or the original string if it did not contain the portNumber */ public static String removeMatchingPort(String hostWithPort, int portNumber) { diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/util/BrowserMobProxyUtil.java b/browsermob-core/src/main/java/net/lightbody/bmp/util/BrowserMobProxyUtil.java index 62f33cf06..19001df25 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/util/BrowserMobProxyUtil.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/util/BrowserMobProxyUtil.java @@ -33,19 +33,14 @@ public class BrowserMobProxyUtil { /** * Singleton version string loader. */ - private static final Supplier version = Suppliers.memoize(new Supplier() { - @Override - public String get() { - return readVersionFileOnClasspath(); - } - }); + private static final Supplier version = Suppliers.memoize(BrowserMobProxyUtil::readVersionFileOnClasspath); /** * Copies {@link HarEntry} and {@link HarPage} references from the specified har to a new har copy, up to and including * the specified pageRef. Does not perform a "deep copy", so any subsequent modification to the entries or pages will * be reflected in the copied har. * - * @param har existing har to copy + * @param har existing har to copy * @param pageRef last page ID to copy * @return copy of a {@link Har} with entries and pages from the original har, or null if the input har is null */ @@ -59,7 +54,7 @@ public static Har copyHarThroughPageRef(Har har, String pageRef) { } // collect the page refs that need to be copied to new har copy. - Set pageRefsToCopy = new HashSet(); + Set pageRefsToCopy = new HashSet<>(); for (HarPage page : har.getLog().getPages()) { pageRefsToCopy.add(page.getId()); diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/util/HttpMessageContents.java b/browsermob-core/src/main/java/net/lightbody/bmp/util/HttpMessageContents.java index 969a4d810..23a3cddcc 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/util/HttpMessageContents.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/util/HttpMessageContents.java @@ -1,7 +1,7 @@ package net.lightbody.bmp.util; import io.netty.handler.codec.http.FullHttpMessage; -import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpHeaderNames; import net.lightbody.bmp.exception.UnsupportedCharsetException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,7 +11,7 @@ /** * Helper class to wrap the contents of an {@link io.netty.handler.codec.http.HttpMessage}. Contains convenience methods to extract and * manipulate the contents of the wrapped {@link io.netty.handler.codec.http.HttpMessage}. - * + *

* TODO: Currently this class only wraps FullHttpMessages, since it must modify the Content-Length header; determine if this may be applied to chunked messages as well */ public class HttpMessageContents { @@ -96,7 +96,7 @@ public byte[] getBinaryContents() { * @return the message's content type */ public String getContentType() { - String contentTypeHeader = HttpHeaders.getHeader(httpMessage, HttpHeaders.Names.CONTENT_TYPE); + String contentTypeHeader = httpMessage.headers().get(HttpHeaderNames.CONTENT_TYPE); if (contentTypeHeader == null || contentTypeHeader.isEmpty()) { return BrowserMobHttpUtil.UNKNOWN_CONTENT_TYPE; } else { diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/util/HttpObjectUtil.java b/browsermob-core/src/main/java/net/lightbody/bmp/util/HttpObjectUtil.java index 169a878f6..945f09d4a 100644 --- a/browsermob-core/src/main/java/net/lightbody/bmp/util/HttpObjectUtil.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/util/HttpObjectUtil.java @@ -1,9 +1,6 @@ package net.lightbody.bmp.util; -import io.netty.handler.codec.http.FullHttpMessage; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMessage; +import io.netty.handler.codec.http.*; import net.lightbody.bmp.exception.UnsupportedCharsetException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,22 +18,22 @@ public class HttpObjectUtil { * Replaces the entity body of the message with the specified contents. Encodes the message contents according to charset in the message's * Content-Type header, or uses {@link BrowserMobHttpUtil#DEFAULT_HTTP_CHARSET} if none is specified. * Note: If the charset of the message is not supported on this platform, this will throw an {@link java.nio.charset.UnsupportedCharsetException}. - * + *

* TODO: Currently this method only works for FullHttpMessages, since it must modify the Content-Length header; determine if this may be applied to chunked messages as well * - * @param message the HTTP message to manipulate + * @param message the HTTP message to manipulate * @param newContents the new entity body contents * @throws java.nio.charset.UnsupportedCharsetException if the charset in the message is not supported on this platform */ public static void replaceTextHttpEntityBody(FullHttpMessage message, String newContents) { // get the content type for this message so we can encode the newContents into a byte stream appropriately - String contentTypeHeader = message.headers().get(HttpHeaders.Names.CONTENT_TYPE); + String contentTypeHeader = message.headers().get(HttpHeaderNames.CONTENT_TYPE); Charset messageCharset; try { messageCharset = BrowserMobHttpUtil.readCharsetInContentTypeHeader(contentTypeHeader); } catch (UnsupportedCharsetException e) { - java.nio.charset.UnsupportedCharsetException cause = e.getUnsupportedCharsetExceptionCause() ; + java.nio.charset.UnsupportedCharsetException cause = e.getUnsupportedCharsetExceptionCause(); log.error("Found unsupported character set in Content-Type header '{}' while attempting to replace contents of HTTP message.", contentTypeHeader, cause); throw cause; @@ -56,7 +53,7 @@ public static void replaceTextHttpEntityBody(FullHttpMessage message, String new * Replaces an HTTP entity body with the specified binary contents. * TODO: Currently this method only works for FullHttpMessages, since it must modify the Content-Length header; determine if this may be applied to chunked messages as well * - * @param message the HTTP message to manipulate + * @param message the HTTP message to manipulate * @param newBinaryContents the new entity body contents */ public static void replaceBinaryHttpEntityBody(FullHttpMessage message, byte[] newBinaryContents) { @@ -66,7 +63,7 @@ public static void replaceBinaryHttpEntityBody(FullHttpMessage message, byte[] n message.content().writeBytes(newBinaryContents); // update the Content-Length header, since the size may have changed - message.headers().set(HttpHeaders.Names.CONTENT_LENGTH, newBinaryContents.length); + message.headers().set(HttpHeaderNames.CONTENT_LENGTH, newBinaryContents.length); } /** @@ -74,7 +71,7 @@ public static void replaceBinaryHttpEntityBody(FullHttpMessage message, byte[] n * the character set is not specified or is unknown, you still must specify a suitable default charset (see {@link BrowserMobHttpUtil#DEFAULT_HTTP_CHARSET}). * * @param httpContent HTTP content object to extract the entity body from - * @param charset character set of the entity body + * @param charset character set of the entity body * @return String representation of the entity body * @throws IllegalArgumentException if the charset is null */ @@ -105,7 +102,7 @@ public static String extractHttpEntityBody(FullHttpMessage httpMessage) { // to alert the client code. java.nio.charset.UnsupportedCharsetException cause = e.getUnsupportedCharsetExceptionCause(); - String contentTypeHeader = HttpHeaders.getHeader(httpMessage, HttpHeaders.Names.CONTENT_TYPE); + String contentTypeHeader = httpMessage.headers().get(HttpHeaderNames.CONTENT_TYPE); log.error("Cannot retrieve text contents of message because HTTP message declares a character set that is not supported on this platform. Content type header: {}.", contentTypeHeader, cause); throw cause; @@ -124,7 +121,7 @@ public static String extractHttpEntityBody(FullHttpMessage httpMessage) { * @throws UnsupportedCharsetException if there is a charset specified in the content-type header, but it is not supported */ public static Charset getCharsetFromMessage(HttpMessage httpMessage) throws UnsupportedCharsetException { - String contentTypeHeader = HttpHeaders.getHeader(httpMessage, HttpHeaders.Names.CONTENT_TYPE); + String contentTypeHeader = httpMessage.headers().get(HttpHeaderNames.CONTENT_TYPE); Charset charset = BrowserMobHttpUtil.readCharsetInContentTypeHeader(contentTypeHeader); if (charset == null) { diff --git a/browsermob-core/src/test/groovy/net/lightbody/bmp/filters/RewriteUrlFilterTest.groovy b/browsermob-core/src/test/groovy/net/lightbody/bmp/filters/RewriteUrlFilterTest.groovy index 0a6991b3e..d5e4f1a12 100644 --- a/browsermob-core/src/test/groovy/net/lightbody/bmp/filters/RewriteUrlFilterTest.groovy +++ b/browsermob-core/src/test/groovy/net/lightbody/bmp/filters/RewriteUrlFilterTest.groovy @@ -2,6 +2,7 @@ package net.lightbody.bmp.filters import com.google.common.collect.ImmutableList import io.netty.channel.ChannelHandlerContext +import io.netty.handler.codec.http.HttpHeaderNames import io.netty.handler.codec.http.HttpHeaders import io.netty.handler.codec.http.HttpRequest import io.netty.util.Attribute @@ -37,10 +38,10 @@ class RewriteUrlFilterTest extends MockServerTest { @Test void testRewriteWithCaptureGroups() { HttpHeaders mockHeaders = mock(HttpHeaders.class) - when(mockHeaders.contains(HttpHeaders.Names.HOST)).thenReturn(false) + when(mockHeaders.contains(HttpHeaderNames.HOST)).thenReturn(false) HttpRequest request = mock(HttpRequest.class); - when(request.getUri()).thenReturn('http://www.yahoo.com?param=someValue'); + when(request.uri()).thenReturn('http://www.yahoo.com?param=someValue'); when(request.headers()).thenReturn(mockHeaders) Collection rewriteRules = ImmutableList.of(new RewriteRule('http://www\\.(yahoo|bing)\\.com\\?(\\w+)=(\\w+)', 'http://www.google.com?originalDomain=$1&$2=$3')); @@ -50,7 +51,7 @@ class RewriteUrlFilterTest extends MockServerTest { when(mockIsHttpsAttribute.get()).thenReturn(Boolean.FALSE) ChannelHandlerContext mockCtx = mock(ChannelHandlerContext) - when(mockCtx.attr(AttributeKey.valueOf(HttpsAwareFiltersAdapter.IS_HTTPS_ATTRIBUTE_NAME))) + when(mockCtx.attr(AttributeKey. valueOf(HttpsAwareFiltersAdapter.IS_HTTPS_ATTRIBUTE_NAME))) .thenReturn(mockIsHttpsAttribute) RewriteUrlFilter filter = new RewriteUrlFilter(request, mockCtx, rewriteRules); @@ -62,10 +63,10 @@ class RewriteUrlFilterTest extends MockServerTest { @Test void testRewriteMultipleMatches() { HttpHeaders mockHeaders = mock(HttpHeaders.class) - when(mockHeaders.contains(HttpHeaders.Names.HOST)).thenReturn(false) + when(mockHeaders.contains(HttpHeaderNames.HOST)).thenReturn(false) HttpRequest request = mock(HttpRequest.class); - when(request.getUri()).thenReturn('http://www.yahoo.com?param=someValue'); + when(request.uri()).thenReturn('http://www.yahoo.com?param=someValue'); when(request.headers()).thenReturn(mockHeaders) Collection rewriteRules = ImmutableList.of( @@ -78,13 +79,13 @@ class RewriteUrlFilterTest extends MockServerTest { when(mockIsHttpsAttribute.get()).thenReturn(Boolean.FALSE) ChannelHandlerContext mockCtx = mock(ChannelHandlerContext) - when(mockCtx.attr(AttributeKey.valueOf(HttpsAwareFiltersAdapter.IS_HTTPS_ATTRIBUTE_NAME))) + when(mockCtx.attr(AttributeKey. valueOf(HttpsAwareFiltersAdapter.IS_HTTPS_ATTRIBUTE_NAME))) .thenReturn(mockIsHttpsAttribute) RewriteUrlFilter filter = new RewriteUrlFilter(request, mockCtx, rewriteRules); filter.clientToProxyRequest(request); - verify(request).setUri('http://www.google.com?originalDomain=bing&newparam=newsomeValue'); + verify(request).setUri("http://www.google.com?originalDomain=bing&newparam=newsomeValue"); } @Test diff --git a/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/BindAddressTest.groovy b/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/BindAddressTest.groovy index 16fd4b5c4..381008582 100644 --- a/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/BindAddressTest.groovy +++ b/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/BindAddressTest.groovy @@ -1,18 +1,21 @@ package net.lightbody.bmp.proxy + import net.lightbody.bmp.BrowserMobProxy import net.lightbody.bmp.BrowserMobProxyServer import net.lightbody.bmp.proxy.test.util.MockServerTest import net.lightbody.bmp.proxy.test.util.NewProxyServerTestUtil +import org.apache.http.client.methods.CloseableHttpResponse import org.apache.http.client.methods.HttpGet import org.apache.http.conn.HttpHostConnectException +import org.apache.http.impl.client.CloseableHttpClient +import org.apache.http.impl.client.HttpClients import org.junit.After import org.junit.Before import org.junit.Test import org.mockserver.matchers.Times import static org.junit.Assert.assertEquals -import static org.junit.Assume.assumeNoException import static org.mockserver.model.HttpRequest.request import static org.mockserver.model.HttpResponse.response @@ -58,16 +61,16 @@ class BindAddressTest extends MockServerTest { // find the local host address to bind to that isn't loopback. since ProxyServerTest.getNewHtpClient creates an HTTP client that // connects to a proxy at 127.0.0.1, the HTTP client should *not* be able to connect to the proxy - InetAddress localHostAddr - try { - localHostAddr = InetAddress.getLocalHost() - } catch (UnknownHostException e) { - assumeNoException("Could not get a localhost address. Skipping test.", e) - return - } + Socket socket = new Socket() + socket.connect(new InetSocketAddress("google.com", 80)) + socket.getLocalAddress() proxy = new BrowserMobProxyServer() - proxy.start(0, localHostAddr) + proxy.start(0, new InetAddress().getByName(socket.getLocalAddress().getHostAddress())) + + + CloseableHttpClient httpclient = HttpClients.createDefault(); + CloseableHttpResponse response = httpclient.execute(new HttpGet("http://127.0.0.1:${mockServerPort}/clientbind")); NewProxyServerTestUtil.getNewHttpClient(proxy.getPort()).withCloseable { it.execute(new HttpGet("http://127.0.0.1:${mockServerPort}/clientbind")) diff --git a/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/WhitelistTest.groovy b/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/WhitelistTest.groovy index 22dd101bb..aa2d40b59 100644 --- a/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/WhitelistTest.groovy +++ b/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/WhitelistTest.groovy @@ -37,9 +37,9 @@ class WhitelistTest extends MockServerTest { @Test void testWhitelistCannotShortCircuitCONNECT() { HttpRequest request = mock(HttpRequest.class) - when(request.getMethod()).thenReturn(HttpMethod.CONNECT) - when(request.getUri()).thenReturn('somedomain.com:443') - when(request.getProtocolVersion()).thenReturn(HttpVersion.HTTP_1_1) + when(request.method()).thenReturn(HttpMethod.CONNECT) + when(request.uri()).thenReturn('somedomain.com:443') + when(request.protocolVersion()).thenReturn(HttpVersion.HTTP_1_1) // create a whitelist filter that whitelists no requests (i.e., all requests should return the specified HTTP 500 status code) WhitelistFilter filter = new WhitelistFilter(request, null, true, 500, []) diff --git a/browsermob-core/src/test/java/net/lightbody/bmp/proxy/InterceptorTest.java b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/InterceptorTest.java index 3df880424..bdb2eb298 100644 --- a/browsermob-core/src/test/java/net/lightbody/bmp/proxy/InterceptorTest.java +++ b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/InterceptorTest.java @@ -1,24 +1,13 @@ package net.lightbody.bmp.proxy; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.DefaultHttpResponse; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.*; import net.lightbody.bmp.BrowserMobProxy; import net.lightbody.bmp.BrowserMobProxyServer; -import net.lightbody.bmp.filters.RequestFilter; import net.lightbody.bmp.filters.RequestFilterAdapter; -import net.lightbody.bmp.filters.ResponseFilter; import net.lightbody.bmp.filters.ResponseFilterAdapter; import net.lightbody.bmp.proxy.test.util.MockServerTest; import net.lightbody.bmp.proxy.test.util.NewProxyServerTestUtil; -import net.lightbody.bmp.util.HttpMessageContents; -import net.lightbody.bmp.util.HttpMessageInfo; import net.lightbody.bmp.util.HttpObjectUtil; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; @@ -40,14 +29,9 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import static org.hamcrest.Matchers.endsWith; -import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.hamcrest.CoreMatchers.endsWith; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.*; import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpResponse.response; @@ -84,7 +68,7 @@ public void testCanShortCircuitResponse() throws IOException { proxy.start(); final AtomicBoolean interceptorFired = new AtomicBoolean(false); - final AtomicBoolean shortCircuitFired= new AtomicBoolean(false); + final AtomicBoolean shortCircuitFired = new AtomicBoolean(false); proxy.addFirstHttpFilterFactory(new HttpFiltersSourceAdapter() { @Override @@ -97,8 +81,8 @@ public HttpResponse clientToProxyRequest(HttpObject httpObject) { HttpRequest httpRequest = (HttpRequest) httpObject; - if (httpRequest.getMethod().equals(HttpMethod.GET) && httpRequest.getUri().contains("/shortcircuit204")) { - HttpResponse httpResponse = new DefaultHttpResponse(httpRequest.getProtocolVersion(), HttpResponseStatus.NO_CONTENT); + if (httpRequest.method().equals(HttpMethod.GET) && httpRequest.uri().contains("/shortcircuit204")) { + HttpResponse httpResponse = new DefaultHttpResponse(httpRequest.protocolVersion(), HttpResponseStatus.NO_CONTENT); shortCircuitFired.set(true); @@ -160,7 +144,7 @@ public void testCanModifyRequest() throws IOException { Times.exactly(1)) .respond(response() .withStatusCode(200) - .withHeader(new Header(HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=utf-8")) + .withHeader(new Header(HttpHeaderNames.CONTENT_TYPE.toString(), "text/plain; charset=utf-8")) .withBody("success")); proxy = new BrowserMobProxyServer(); @@ -174,7 +158,7 @@ public HttpFilters filterRequest(HttpRequest originalRequest) { public HttpResponse clientToProxyRequest(HttpObject httpObject) { if (httpObject instanceof HttpRequest) { HttpRequest httpRequest = (HttpRequest) httpObject; - httpRequest.setUri(httpRequest.getUri().replace("/originalrequest", "/modifyrequest")); + httpRequest.setUri(httpRequest.uri().replace("/originalrequest", "/modifyrequest")); } return super.clientToProxyRequest(httpObject); @@ -209,17 +193,14 @@ public void testRequestFilterCanModifyHttpRequestBody() throws IOException { proxy = new BrowserMobProxyServer(); proxy.start(); - proxy.addRequestFilter(new RequestFilter() { - @Override - public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { - if (contents.isText()) { - if (contents.getTextContents().equals(originalText)) { - contents.setTextContents(newText); - } + proxy.addRequestFilter((request, contents, messageInfo) -> { + if (contents.isText()) { + if (contents.getTextContents().equals(originalText)) { + contents.setTextContents(newText); } - - return null; } + + return null; }); try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { @@ -251,17 +232,14 @@ public void testRequestFilterCanModifyHttpsRequestBody() throws IOException { proxy.setTrustAllServers(true); proxy.start(); - proxy.addRequestFilter(new RequestFilter() { - @Override - public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { - if (contents.isText()) { - if (contents.getTextContents().equals(originalText)) { - contents.setTextContents(newText); - } + proxy.addRequestFilter((request, contents, messageInfo) -> { + if (contents.isText()) { + if (contents.getTextContents().equals(originalText)) { + contents.setTextContents(newText); } - - return null; } + + return null; }); try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { @@ -277,8 +255,8 @@ public HttpResponse filterRequest(HttpRequest request, HttpMessageContents conte @Test public void testResponseFilterCanModifyBinaryContents() throws IOException { - final byte[] originalBytes = new byte[] {1, 2, 3, 4, 5}; - final byte[] newBytes = new byte[] {20, 30, 40, 50, 60}; + final byte[] originalBytes = new byte[]{1, 2, 3, 4, 5}; + final byte[] newBytes = new byte[]{20, 30, 40, 50, 60}; mockServer.when(request() .withMethod("GET") @@ -286,19 +264,16 @@ public void testResponseFilterCanModifyBinaryContents() throws IOException { Times.exactly(1)) .respond(response() .withStatusCode(200) - .withHeader(new Header(HttpHeaders.Names.CONTENT_TYPE, "application/octet-stream")) + .withHeader(new Header(HttpHeaderNames.CONTENT_TYPE.toString(), "application/octet-stream")) .withBody(originalBytes)); proxy = new BrowserMobProxyServer(); proxy.start(); - proxy.addResponseFilter(new ResponseFilter() { - @Override - public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { - if (!contents.isText()) { - if (Arrays.equals(originalBytes, contents.getBinaryContents())) { - contents.setBinaryContents(newBytes); - } + proxy.addResponseFilter((response, contents, messageInfo) -> { + if (!contents.isText()) { + if (Arrays.equals(originalBytes, contents.getBinaryContents())) { + contents.setBinaryContents(newBytes); } } }); @@ -324,19 +299,16 @@ public void testResponseFilterCanModifyHttpTextContents() throws IOException { Times.exactly(1)) .respond(response() .withStatusCode(200) - .withHeader(new Header(HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=utf-8")) + .withHeader(new Header(HttpHeaderNames.CONTENT_TYPE.toString(), "text/plain; charset=utf-8")) .withBody(originalText)); proxy = new BrowserMobProxyServer(); proxy.start(); - proxy.addResponseFilter(new ResponseFilter() { - @Override - public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { - if (contents.isText()) { - if (contents.getTextContents().equals(originalText)) { - contents.setTextContents(newText); - } + proxy.addResponseFilter((response, contents, messageInfo) -> { + if (contents.isText()) { + if (contents.getTextContents().equals(originalText)) { + contents.setTextContents(newText); } } }); @@ -363,20 +335,17 @@ public void testResponseFilterCanModifyHttpsTextContents() throws IOException { Times.exactly(1)) .respond(response() .withStatusCode(200) - .withHeader(new Header(HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=utf-8")) + .withHeader(new Header(HttpHeaderNames.CONTENT_TYPE.toString(), "text/plain; charset=utf-8")) .withBody(originalText)); proxy = new BrowserMobProxyServer(); proxy.setTrustAllServers(true); proxy.start(); - proxy.addResponseFilter(new ResponseFilter() { - @Override - public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { - if (contents.isText()) { - if (contents.getTextContents().equals(originalText)) { - contents.setTextContents(newText); - } + proxy.addResponseFilter((response, contents, messageInfo) -> { + if (contents.isText()) { + if (contents.getTextContents().equals(originalText)) { + contents.setTextContents(newText); } } }); @@ -400,19 +369,14 @@ public void testResponseInterceptorWithoutBody() throws IOException { Times.exactly(1)) .respond(response() .withStatusCode(200) - .withHeader(new Header(HttpHeaders.Names.CONTENT_TYPE, "application/octet-stream"))); + .withHeader(new Header(HttpHeaderNames.CONTENT_TYPE.toString(), "application/octet-stream"))); proxy = new BrowserMobProxyServer(); proxy.start(); final AtomicReference responseContents = new AtomicReference<>(); - proxy.addResponseFilter(new ResponseFilter() { - @Override - public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { - responseContents.set(contents.getBinaryContents()); - } - }); + proxy.addResponseFilter((response, contents, messageInfo) -> responseContents.set(contents.getBinaryContents())); try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { CloseableHttpResponse response = httpClient.execute(new HttpHead("http://localhost:" + mockServerPort + "/interceptortest")); @@ -435,25 +399,17 @@ public void testResponseFilterOriginalRequestNotModified() throws IOException { proxy = new BrowserMobProxyServer(); proxy.start(); - proxy.addRequestFilter(new RequestFilter() { - @Override - public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { - if (request.getUri().endsWith("/originalendpoint")) { - request.setUri(request.getUri().replaceAll("originalendpoint", "modifiedendpoint")); - } - - return null; + proxy.addRequestFilter((request, contents, messageInfo) -> { + if (request.uri().endsWith("/originalendpoint")) { + request.setUri(request.uri().replaceAll("originalendpoint", "modifiedendpoint")); } + + return null; }); final AtomicReference originalRequestUri = new AtomicReference<>(); - proxy.addResponseFilter(new ResponseFilter() { - @Override - public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { - originalRequestUri.set(messageInfo.getOriginalRequest().getUri()); - } - }); + proxy.addResponseFilter((response, contents, messageInfo) -> originalRequestUri.set(messageInfo.getOriginalRequest().uri())); try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { CloseableHttpResponse response = httpClient.execute(new HttpGet("http://localhost:" + mockServerPort + "/originalendpoint")); @@ -479,23 +435,17 @@ public void testMessageContentsNotAvailableWithoutAggregation() throws IOExcepti final AtomicBoolean requestContentsNull = new AtomicBoolean(false); final AtomicBoolean responseContentsNull = new AtomicBoolean(false); - proxy.addFirstHttpFilterFactory(new RequestFilterAdapter.FilterSource(new RequestFilter() { - @Override - public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { - if (contents == null) { - requestContentsNull.set(true); - } - - return null; + proxy.addFirstHttpFilterFactory(new RequestFilterAdapter.FilterSource((request, contents, messageInfo) -> { + if (contents == null) { + requestContentsNull.set(true); } + + return null; }, 0)); - proxy.addFirstHttpFilterFactory(new ResponseFilterAdapter.FilterSource(new ResponseFilter() { - @Override - public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { - if (contents == null) { - responseContentsNull.set(true); - } + proxy.addFirstHttpFilterFactory(new ResponseFilterAdapter.FilterSource((response, contents, messageInfo) -> { + if (contents == null) { + responseContentsNull.set(true); } }, 0)); @@ -526,16 +476,13 @@ public void testMitmDisabledHttpsRequestFilterNotAvailable() throws IOException final AtomicBoolean connectRequestFilterFired = new AtomicBoolean(false); final AtomicBoolean getRequestFilterFired = new AtomicBoolean(false); - proxy.addRequestFilter(new RequestFilter() { - @Override - public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { - if (request.getMethod().equals(HttpMethod.CONNECT)) { - connectRequestFilterFired.set(true); - } else if (request.getMethod().equals(HttpMethod.GET)) { - getRequestFilterFired.set(true); - } - return null; + proxy.addRequestFilter((request, contents, messageInfo) -> { + if (request.method().equals(HttpMethod.CONNECT)) { + connectRequestFilterFired.set(true); + } else if (request.method().equals(HttpMethod.GET)) { + getRequestFilterFired.set(true); } + return null; }); try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { @@ -569,12 +516,7 @@ public void testMitmDisabledHttpsResponseFilterNotAvailable() throws IOException final AtomicBoolean responseFilterFired = new AtomicBoolean(false); - proxy.addResponseFilter(new ResponseFilter() { - @Override - public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { - responseFilterFired.set(true); - } - }); + proxy.addResponseFilter((response, contents, messageInfo) -> responseFilterFired.set(true)); try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { CloseableHttpResponse response = httpClient.execute(new HttpGet("https://localhost:" + mockServerPort + "/mitmdisabled")); @@ -594,7 +536,7 @@ private void testModifiedResponse(final String originalText, final String newTex Times.exactly(1)) .respond(response() .withStatusCode(200) - .withHeader(new Header(HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=utf-8")) + .withHeader(new Header(HttpHeaderNames.CONTENT_TYPE.toString(), "text/plain; charset=utf-8")) .withBody(originalText)); proxy = new BrowserMobProxyServer(); @@ -718,16 +660,13 @@ public void testHttpResponseFilterMessageInfoPopulated() throws IOException { final AtomicReference requestFilterOriginalUrl = new AtomicReference<>(); final AtomicReference requestFilterUrl = new AtomicReference<>(); - proxy.addRequestFilter(new RequestFilter() { - @Override - public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { - requestCtx.set(messageInfo.getChannelHandlerContext()); - requestOriginalRequest.set(messageInfo.getOriginalRequest()); - requestIsHttps.set(messageInfo.isHttps()); - requestFilterOriginalUrl.set(messageInfo.getOriginalUrl()); - requestFilterUrl.set(messageInfo.getUrl()); - return null; - } + proxy.addRequestFilter((request, contents, messageInfo) -> { + requestCtx.set(messageInfo.getChannelHandlerContext()); + requestOriginalRequest.set(messageInfo.getOriginalRequest()); + requestIsHttps.set(messageInfo.isHttps()); + requestFilterOriginalUrl.set(messageInfo.getOriginalUrl()); + requestFilterUrl.set(messageInfo.getUrl()); + return null; }); final AtomicReference responseCtx = new AtomicReference<>(); @@ -736,15 +675,12 @@ public HttpResponse filterRequest(HttpRequest request, HttpMessageContents conte final AtomicReference responseFilterOriginalUrl = new AtomicReference<>(); final AtomicReference responseFilterUrl = new AtomicReference<>(); - proxy.addResponseFilter(new ResponseFilter() { - @Override - public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { - responseCtx.set(messageInfo.getChannelHandlerContext()); - responseOriginalRequest.set(messageInfo.getOriginalRequest()); - responseIsHttps.set(messageInfo.isHttps()); - responseFilterOriginalUrl.set(messageInfo.getOriginalUrl()); - responseFilterUrl.set(messageInfo.getUrl()); - } + proxy.addResponseFilter((response, contents, messageInfo) -> { + responseCtx.set(messageInfo.getChannelHandlerContext()); + responseOriginalRequest.set(messageInfo.getOriginalRequest()); + responseIsHttps.set(messageInfo.isHttps()); + responseFilterOriginalUrl.set(messageInfo.getOriginalUrl()); + responseFilterUrl.set(messageInfo.getUrl()); }); try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { @@ -782,37 +718,28 @@ public void testHttpResponseFilterUrlReflectsModifications() throws IOException final AtomicReference requestFilterOriginalUrl = new AtomicReference<>(); final AtomicReference requestFilterUrl = new AtomicReference<>(); - proxy.addRequestFilter(new RequestFilter() { - @Override - public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { - requestFilterOriginalUrl.set(messageInfo.getOriginalUrl()); - requestFilterUrl.set(messageInfo.getUrl()); - return null; - } + proxy.addRequestFilter((request, contents, messageInfo) -> { + requestFilterOriginalUrl.set(messageInfo.getOriginalUrl()); + requestFilterUrl.set(messageInfo.getUrl()); + return null; }); // request filters get added to the beginning of the filter chain, so add this uri-modifying request filter after // adding the capturing request filter above. - proxy.addRequestFilter(new RequestFilter() { - @Override - public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { - if (request.getUri().endsWith("/originalurl")) { - String newUrl = request.getUri().replaceAll("originalurl", "urlreflectsmodifications"); - request.setUri(newUrl); - } - return null; + proxy.addRequestFilter((request, contents, messageInfo) -> { + if (request.uri().endsWith("/originalurl")) { + String newUrl = request.uri().replaceAll("originalurl", "urlreflectsmodifications"); + request.setUri(newUrl); } + return null; }); final AtomicReference responseFilterOriginalUrl = new AtomicReference<>(); final AtomicReference responseFilterUrl = new AtomicReference<>(); - proxy.addResponseFilter(new ResponseFilter() { - @Override - public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { - responseFilterOriginalUrl.set(messageInfo.getOriginalUrl()); - responseFilterUrl.set(messageInfo.getUrl()); - } + proxy.addResponseFilter((response, contents, messageInfo) -> { + responseFilterOriginalUrl.set(messageInfo.getOriginalUrl()); + responseFilterUrl.set(messageInfo.getUrl()); }); try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { @@ -846,37 +773,28 @@ public void testHttpsResponseFilterUrlReflectsModifications() throws IOException final AtomicReference requestFilterOriginalUrl = new AtomicReference<>(); final AtomicReference requestFilterUrl = new AtomicReference<>(); - proxy.addRequestFilter(new RequestFilter() { - @Override - public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { - requestFilterOriginalUrl.set(messageInfo.getOriginalUrl()); - requestFilterUrl.set(messageInfo.getUrl()); - return null; - } + proxy.addRequestFilter((request, contents, messageInfo) -> { + requestFilterOriginalUrl.set(messageInfo.getOriginalUrl()); + requestFilterUrl.set(messageInfo.getUrl()); + return null; }); // request filters get added to the beginning of the filter chain, so add this uri-modifying request filter after // adding the capturing request filter above. - proxy.addRequestFilter(new RequestFilter() { - @Override - public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { - if (request.getUri().endsWith("/originalurl")) { - String newUrl = request.getUri().replaceAll("originalurl", "urlreflectsmodifications"); - request.setUri(newUrl); - } - return null; + proxy.addRequestFilter((request, contents, messageInfo) -> { + if (request.uri().endsWith("/originalurl")) { + String newUrl = request.uri().replaceAll("originalurl", "urlreflectsmodifications"); + request.setUri(newUrl); } + return null; }); final AtomicReference responseFilterOriginalUrl = new AtomicReference<>(); final AtomicReference responseFilterUrl = new AtomicReference<>(); - proxy.addResponseFilter(new ResponseFilter() { - @Override - public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { - responseFilterOriginalUrl.set(messageInfo.getOriginalUrl()); - responseFilterUrl.set(messageInfo.getUrl()); - } + proxy.addResponseFilter((response, contents, messageInfo) -> { + responseFilterOriginalUrl.set(messageInfo.getOriginalUrl()); + responseFilterUrl.set(messageInfo.getUrl()); }); try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { @@ -913,15 +831,12 @@ public void testHttpsResponseFilterMessageInfoPopulated() throws IOException { final AtomicBoolean requestIsHttps = new AtomicBoolean(false); final AtomicReference requestOriginalUrl = new AtomicReference<>(); - proxy.addRequestFilter(new RequestFilter() { - @Override - public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { - requestCtx.set(messageInfo.getChannelHandlerContext()); - requestOriginalRequest.set(messageInfo.getOriginalRequest()); - requestIsHttps.set(messageInfo.isHttps()); - requestOriginalUrl.set(messageInfo.getOriginalUrl()); - return null; - } + proxy.addRequestFilter((request, contents, messageInfo) -> { + requestCtx.set(messageInfo.getChannelHandlerContext()); + requestOriginalRequest.set(messageInfo.getOriginalRequest()); + requestIsHttps.set(messageInfo.isHttps()); + requestOriginalUrl.set(messageInfo.getOriginalUrl()); + return null; }); final AtomicReference responseCtx = new AtomicReference<>(); @@ -929,14 +844,11 @@ public HttpResponse filterRequest(HttpRequest request, HttpMessageContents conte final AtomicBoolean responseIsHttps = new AtomicBoolean(false); final AtomicReference responseOriginalUrl = new AtomicReference<>(); - proxy.addResponseFilter(new ResponseFilter() { - @Override - public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { - responseCtx.set(messageInfo.getChannelHandlerContext()); - responseOriginalRequest.set(messageInfo.getOriginalRequest()); - responseIsHttps.set(messageInfo.isHttps()); - responseOriginalUrl.set(messageInfo.getOriginalUrl()); - } + proxy.addResponseFilter((response, contents, messageInfo) -> { + responseCtx.set(messageInfo.getChannelHandlerContext()); + responseOriginalRequest.set(messageInfo.getOriginalRequest()); + responseIsHttps.set(messageInfo.isHttps()); + responseOriginalUrl.set(messageInfo.getOriginalUrl()); }); try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { diff --git a/browsermob-core/src/test/java/net/lightbody/bmp/proxy/QuiescenceTest.java b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/QuiescenceTest.java index 60842b0be..cbbe669df 100644 --- a/browsermob-core/src/test/java/net/lightbody/bmp/proxy/QuiescenceTest.java +++ b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/QuiescenceTest.java @@ -19,10 +19,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpResponse.response; @@ -218,7 +215,6 @@ public void testWaitForQuiescenceTimeoutLessThanQuietPeriodUnuccessful() throws } @Test - @Ignore //TODO: ignoring this test because it seems to fail on Java 8 under travis-ci. determine if there is an actual code defect, or just a test/environment defect. public void testWaitForQuiescenceInterruptedBySecondRequestSuccessful() throws InterruptedException { mockServer.when( request().withMethod("GET") @@ -235,27 +231,24 @@ public void testWaitForQuiescenceInterruptedBySecondRequestSuccessful() throws I final AtomicBoolean exceptionOccurred = new AtomicBoolean(); - new Thread(new Runnable() { - @Override - public void run() { - try (CloseableHttpClient client = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { - HttpResponse response = client.execute(new HttpGet("http://127.0.0.1:" + mockServerPort + "/successquiesence2s")); - EntityUtils.consumeQuietly(response.getEntity()); - firstRequestStatusCode.set(response.getStatusLine().getStatusCode()); + new Thread(() -> { + try (CloseableHttpClient client = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + HttpResponse response = client.execute(new HttpGet("http://127.0.0.1:" + mockServerPort + "/successquiesence2s")); + EntityUtils.consumeQuietly(response.getEntity()); + firstRequestStatusCode.set(response.getStatusLine().getStatusCode()); - Thread.sleep(1000); + Thread.sleep(1000); - response = client.execute(new HttpGet("http://127.0.0.1:" + mockServerPort + "/successquiesence2s")); - EntityUtils.consumeQuietly(response.getEntity()); + response = client.execute(new HttpGet("http://127.0.0.1:" + mockServerPort + "/successquiesence2s")); + EntityUtils.consumeQuietly(response.getEntity()); - secondRequestFinished.set(System.nanoTime()); + secondRequestFinished.set(System.nanoTime()); - secondRequestStatusCode.set(response.getStatusLine().getStatusCode()); - } catch (IOException | InterruptedException e) { - exceptionOccurred.set(true); + secondRequestStatusCode.set(response.getStatusLine().getStatusCode()); + } catch (IOException | InterruptedException e) { + exceptionOccurred.set(true); - log.error("Exception occurred while making HTTP request", e); - } + log.error("Exception occurred while making HTTP request", e); } }).start(); diff --git a/browsermob-core/src/test/java/net/lightbody/bmp/proxy/dns/ChainedHostResolverTest.java b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/dns/ChainedHostResolverTest.java index fbf09d63c..04de7c9b9 100644 --- a/browsermob-core/src/test/java/net/lightbody/bmp/proxy/dns/ChainedHostResolverTest.java +++ b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/dns/ChainedHostResolverTest.java @@ -17,20 +17,13 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; +import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.not; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; public class ChainedHostResolverTest { @Test @@ -62,7 +55,7 @@ public void testResolveReturnsFirstResults() { ChainedHostResolver chainResolver = new ChainedHostResolver(ImmutableList.of(firstResolver, secondResolver)); when(firstResolver.resolve("1.1.1.1")).thenReturn(TestConstants.addressOnesList); - when(secondResolver.resolve("1.1.1.1")).thenReturn(Collections.emptyList()); + when(secondResolver.resolve("1.1.1.1")).thenReturn(Collections.emptyList()); Collection results = chainResolver.resolve("1.1.1.1"); assertNotNull("Resolver should not return null results", results); diff --git a/browsermob-core/src/test/java/net/lightbody/bmp/proxy/test/util/MockServerTest.java b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/test/util/MockServerTest.java index 9e1ba94ef..24108d5cc 100644 --- a/browsermob-core/src/test/java/net/lightbody/bmp/proxy/test/util/MockServerTest.java +++ b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/test/util/MockServerTest.java @@ -14,7 +14,7 @@ public class MockServerTest { @Before public void setUpMockServer() { mockServer = new ClientAndServer(0); - mockServerPort = mockServer.getPort(); + mockServerPort = mockServer.getLocalPort(); } @After diff --git a/browsermob-core/src/test/java/net/lightbody/bmp/proxy/test/util/NewProxyServerTestUtil.java b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/test/util/NewProxyServerTestUtil.java index af86c4b96..b68e1bd85 100644 --- a/browsermob-core/src/test/java/net/lightbody/bmp/proxy/test/util/NewProxyServerTestUtil.java +++ b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/test/util/NewProxyServerTestUtil.java @@ -2,8 +2,8 @@ import org.apache.http.HttpHost; import org.apache.http.client.CookieStore; +import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; -import org.apache.http.conn.ssl.SSLContexts; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; @@ -12,8 +12,6 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; public class NewProxyServerTestUtil { /** @@ -31,26 +29,19 @@ public static CloseableHttpClient getNewHttpClient(int proxyPort) { * Creates an all-trusting CloseableHttpClient (for tests ONLY!) that will connect to a proxy at 127.0.0.1:proxyPort, * using the specified cookie store. * - * @param proxyPort port of the proxy running at 127.0.0.1 + * @param proxyPort port of the proxy running at 127.0.0.1 * @param cookieStore CookieStore for HTTP cookies * @return a new CloseableHttpClient */ public static CloseableHttpClient getNewHttpClient(int proxyPort, CookieStore cookieStore) { try { // Trust all certs -- under no circumstances should this ever be used outside of testing - SSLContext sslcontext = SSLContexts.custom() - .useTLS() - .loadTrustMaterial(null, new TrustStrategy() { - @Override - public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { - return true; - } - }) + SSLContext sslcontext = org.apache.http.ssl.SSLContexts.custom() + .loadTrustMaterial(null, (TrustStrategy) (chain, authType) -> true) .build(); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( - sslcontext, - SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + sslcontext, NoopHostnameVerifier.INSTANCE); CloseableHttpClient httpclient = HttpClients.custom() .setSSLSocketFactory(sslsf) diff --git a/browsermob-dist/pom.xml b/browsermob-dist/pom.xml index 59edc89cb..221cd5b83 100644 --- a/browsermob-dist/pom.xml +++ b/browsermob-dist/pom.xml @@ -1,9 +1,10 @@ - + browsermob-proxy net.lightbody.bmp - 2.1.6-SNAPSHOT + 2.1.38 4.0.0 @@ -14,7 +15,6 @@ ${project.parent.artifactId}-${project.version} - ${netty-4.1.version} @@ -39,6 +39,17 @@ org.apache.logging.log4j log4j-api + + + biz.paluch.logging + logstash-gelf + + + + co.elastic.logging + log4j2-ecs-layout + + org.apache.logging.log4j log4j-core @@ -98,7 +109,18 @@ org.apache.maven.plugins maven-shade-plugin - 3.0.0 + 3.2.0 + + false + + + *:* + + **/Log4j2Plugins.dat + + + + package @@ -117,7 +139,10 @@ - + + net.lightbody.bmp.proxy.Main @@ -125,6 +150,13 @@ + + + com.github.edwgiz + maven-shade-plugin.log4j2-cachefile-transformer + 2.8.1 + + maven-assembly-plugin diff --git a/browsermob-dist/src/main/resources/bmp-logging.yaml b/browsermob-dist/src/main/resources/bmp-logging.yaml index 3114ff47f..1a237ce8b 100644 --- a/browsermob-dist/src/main/resources/bmp-logging.yaml +++ b/browsermob-dist/src/main/resources/bmp-logging.yaml @@ -8,30 +8,41 @@ configuration: name: console target: SYSTEM_OUT PatternLayout: - pattern: "[%-5level %date{ISO8601} %logger] (%thread) %msg %xThrowable%n" - file: - - - name: file - fileName: bmp.log - PatternLayout: - pattern: "[%-5level %date{ISO8601} %logger] (%thread) %msg %xThrowable%n" - append: false + pattern: "[%-5level %date{ISO8601} %c{1}] %msg %xThrowable%n" + gelf: + - name: gelf + host: tcp:localhost + port: 12204 + filterStackTrace: true + mdcProfiling: true + includeFullMdc: true + facility: mobproxy + version: 1.1 + originHost: "%host{fqdn}" + maximumMessageSize: "8192" + extractStackTrace: false + Field: + - name: simpleClassName + pattern: "%C{1}" + - name: server + pattern: "%host" + - name: timestamp + pattern: "%date{ISO8601}" + + loggers: logger: - - - name: net.lightbody.bmp.proxy.jetty.util.ThreadedServer - level: warn + - name: net.lightbody.bmp.proxy.jetty.util.ThreadedServer + level: fatal additivity: false - - - name: net.lightbody.bmp + - name: org.littleshoot.proxy # to suppress unwanted BMP logging statements, set the level below for the source logger to WARN or ERROR. # to enable more verbose logging, set the level to DEBUG or TRACE. - level: info + level: fatal + root: # to suppress unwanted logging statements globally, set the level below to WARN or ERROR. - level: info + level: error appender-ref: - - - ref: console - - - ref: file \ No newline at end of file + - ref: console + - ref: gelf \ No newline at end of file diff --git a/browsermob-legacy/pom.xml b/browsermob-legacy/pom.xml index 592543c91..985c60154 100644 --- a/browsermob-legacy/pom.xml +++ b/browsermob-legacy/pom.xml @@ -1,11 +1,12 @@ - + jar browsermob-proxy net.lightbody.bmp - 2.1.6-SNAPSHOT + 2.1.38 4.0.0 @@ -13,7 +14,7 @@ BrowserMob Proxy Legacy (Jetty) Module - 7.6.16.v20140903 + 9.4.31.v20200723 true @@ -23,7 +24,7 @@ org.apache.maven.plugins maven-surefire-plugin - -Xmx1g -XX:MaxPermSize=256m + -Xmx1g ${use.littleproxy} @@ -80,13 +81,13 @@ commons-io commons-io - 2.5 + 2.6 javax.servlet - servlet-api - 2.5 + javax.servlet-api + 4.0.1 @@ -109,7 +110,7 @@ org.hamcrest - hamcrest-library + hamcrest test diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/LegacyProxyServer.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/LegacyProxyServer.java index 103f2f25a..9e1f390f1 100644 --- a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/LegacyProxyServer.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/LegacyProxyServer.java @@ -18,7 +18,7 @@ /** * Describes the legacy BrowserMob Proxy 2.0 interface. Clients should not implement or use this interface. - * + *

* Use {@link BrowserMobProxy}. */ public interface LegacyProxyServer { @@ -122,6 +122,8 @@ public interface LegacyProxyServer { void addHeader(String name, String value); + void headerFilterRegexp(String headerFilterRegexp); + void setCaptureHeaders(boolean captureHeaders); void setCaptureContent(boolean captureContent); diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/ProxyServer.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/ProxyServer.java index 29c5f897f..1b351d1f2 100644 --- a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/ProxyServer.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/ProxyServer.java @@ -205,7 +205,7 @@ public void stop() { server.stop(); } } catch (InterruptedException e) { - // the try/catch block in server.stop() is manufacturing a phantom InterruptedException, so this should not occur + // the try/catch block in server.stop() is manufacturing a phantom InterruptedException, so this should not occur throw new JettyException("Exception occurred when stopping the server", e); } } @@ -236,10 +236,10 @@ public void setPort(int port) { /** * Get the the InetAddress that the Proxy server binds to when it starts. - * + * * If not otherwise set via {@link #setLocalHost(InetAddress)}, defaults to * 0.0.0.0 (i.e. bind to any interface). - * + * * Note - just because we bound to the address, doesn't mean that it can be * reached. E.g. trying to connect to 0.0.0.0 is going to fail. Use * {@link #getConnectableLocalHost()} if you're looking for a host that can be @@ -258,22 +258,22 @@ public InetAddress getLocalHost() { } return localHost; } - + /** * Return a plausible {@link InetAddress} that other processes can use to * contact the proxy. - * + * * In essence, this is the same as {@link #getLocalHost()}, but avoids * returning 0.0.0.0. as no-one can connect to that. If no other host has * been set via {@link #setLocalHost(InetAddress)}, will return * {@link InetAddress#getLocalHost()} - * + * * No attempt is made to check the address for reachability before it is * returned. */ @Override public InetAddress getConnectableLocalHost() throws UnknownHostException { - + if (getLocalHost().equals(InetAddress.getByName("0.0.0.0"))) { return InetAddress.getLocalHost(); } else { @@ -295,12 +295,12 @@ public void setLocalHost(InetAddress localHost) { throw new IllegalArgumentException("localHost address must be address of a local adapter (attempted to use: " + localHost + ")", e); } if (localInterface != null) { - this.localHost = localHost; + this.localHost = localHost; } else { throw new IllegalArgumentException("localHost address must be address of a local adapter (attempted to use: " + localHost + ")"); } } - + } @Override @@ -544,6 +544,11 @@ public void addHeaders(Map headers) { } } + @Override + public void headerFilterRegexp(String headerFilterRegexp){ + client.setHeadersFilterRegexp(headerFilterRegexp); + } + public void remapHost(String source, String target) { if (client.getResolver() instanceof AdvancedHostResolver) { AdvancedHostResolver advancedHostResolver = (AdvancedHostResolver) client.getResolver(); @@ -660,7 +665,7 @@ public void clearRewriteRules() { public void blacklistRequests(String pattern, int responseCode) { client.blacklistRequests(pattern, responseCode, null); } - + @Override public void blacklistRequests(String pattern, int responseCode, String method) { client.blacklistRequests(pattern, responseCode, method); @@ -690,7 +695,7 @@ public Collection getBlacklist() { public List getBlacklistedRequests() { return client.getBlacklistedRequests(); } - + @Override public Collection getBlacklistedUrls() { return client.getBlacklistedUrls(); @@ -709,7 +714,7 @@ public boolean isWhitelistEnabled() { public List getWhitelistRequests() { return client.getWhitelistRequests(); } - + @Override public Collection getWhitelistUrls() { ImmutableList.Builder builder = ImmutableList.builder(); @@ -748,10 +753,10 @@ public void addWhitelistPattern(String urlPattern) { } /** - * Whitelists the specified requests. + * Whitelists the specified requests. *

* Note: This method overwrites any existing whitelist. - * + * * @param patterns regular expression patterns matching URLs to whitelist * @param responseCode response code to return for non-whitelisted URLs */ @@ -759,10 +764,10 @@ public void addWhitelistPattern(String urlPattern) { public void whitelistRequests(String[] patterns, int responseCode) { client.whitelistRequests(patterns, responseCode); } - + /** * Enables an empty whitelist, which will return the specified responseCode for all requests. - * + * * @param responseCode HTTP response code to return for all requests */ @Override @@ -799,7 +804,7 @@ public void removeHeader(String name) { @Override public void removeAllHeaders() { - client.setAdditionalHeaders(Collections.emptyMap()); + client.setAdditionalHeaders(Collections.emptyMap()); } @Override @@ -847,7 +852,7 @@ public void setCaptureHeaders(boolean captureHeaders) { public void setCaptureContent(boolean captureContent) { client.setCaptureContent(captureContent); } - + @Override public void setCaptureBinaryContent(boolean captureBinaryContent) { client.setCaptureBinaryContent(captureBinaryContent); diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/BrowserMobHttpClient.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/BrowserMobHttpClient.java index 00cdd8179..62c56d8d7 100644 --- a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/BrowserMobHttpClient.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/BrowserMobHttpClient.java @@ -127,7 +127,7 @@ public class BrowserMobHttpClient { private static final String VERSION = "2.1"; private static final Logger LOG = LoggerFactory.getLogger(BrowserMobHttpClient.class); - + private static final int BUFFER = 4096; private volatile Har har; @@ -137,7 +137,7 @@ public class BrowserMobHttpClient { * keep headers */ private volatile boolean captureHeaders; - + /** * keep contents */ @@ -152,7 +152,7 @@ public class BrowserMobHttpClient { * socket factory dedicated to port 80 (HTTP) */ private final SimulatedSocketFactory socketFactory; - + /** * socket factory dedicated to port 443 (HTTPS) */ @@ -160,51 +160,56 @@ public class BrowserMobHttpClient { private final PoolingHttpClientConnectionManager httpClientConnMgr; - + /** * Builders for httpClient * Each time you change their configuration you should call updateHttpClient() */ private final Builder requestConfigBuilder; private final HttpClientBuilder httpClientBuilder; - + /** * The current httpClient which will execute HTTP requests */ private volatile CloseableHttpClient httpClient; - + private final BasicCookieStore cookieStore = new BasicCookieStore(); - + /** * List of rejected URL patterns */ private final Collection blacklistEntries = new CopyOnWriteArrayList(); - + /** * List of accepted URL patterns */ private volatile Whitelist whitelist = Whitelist.WHITELIST_DISABLED; - + /** * List of URLs to rewrite */ private final CopyOnWriteArrayList rewriteRules = new CopyOnWriteArrayList(); - + /** * triggers to process when sending request */ private final List requestInterceptors = new CopyOnWriteArrayList(); - + /** * triggers to process when receiving response */ private final List responseInterceptors = new CopyOnWriteArrayList(); - + /** * additional headers sent with request */ private final Map additionalHeaders = new ConcurrentHashMap(); - + + /** + * Regexp to check request url and inject headers if url match regexp. + */ + private static String headersFilterRegexp; + /** * request timeout: set to -1 to disable timeout */ @@ -214,7 +219,7 @@ public class BrowserMobHttpClient { * is it possible to add a new request? */ private final AtomicBoolean allowNewRequests = new AtomicBoolean(true); - + /** * Hostname resolver that wraps a {@link net.lightbody.bmp.proxy.dns.HostResolver}. The wrapped HostResolver can be replaced safely at * runtime using {@link LegacyHostResolverAdapter#setResolver(net.lightbody.bmp.proxy.dns.AdvancedHostResolver)}. @@ -226,22 +231,22 @@ public class BrowserMobHttpClient { * does the proxy support gzip compression? (set to false if you go through a browser) */ private boolean decompress = true; - + /** * set of active requests */ private final Set activeRequests = Collections.newSetFromMap(new ConcurrentHashMap()); - + /** * credentials used for authentication */ private WildcardMatchingCredentialsProvider credsProvider; - + /** * is the client shutdown? */ private volatile boolean shutdown = false; - + /** * authentication type used */ @@ -251,17 +256,17 @@ public class BrowserMobHttpClient { * does the proxy follow redirects? (set to false if you go through a browser) */ private boolean followRedirects = true; - + /** * maximum redirects supported by the proxy */ private static final int MAX_REDIRECT = 10; - + /** * remaining requests counter */ private final AtomicInteger requestCounter; - + /** * Init HTTP client * @param streamManager will be capped to 100 Megabits (by default it is disabled) @@ -276,13 +281,13 @@ public BrowserMobHttpClient(final StreamManager streamManager, AtomicInteger req .setConnectionRequestTimeout(60000) .setConnectTimeout(2000) .setSocketTimeout(60000); - + // we associate each SocketFactory with their protocols Registry registry = RegistryBuilder.create() .register("http", this.socketFactory) .register("https", this.sslSocketFactory) .build(); - + httpClientConnMgr = new PoolingHttpClientConnectionManager(registry, resolverWrapper) { @Override public ConnectionRequest requestConnection(HttpRoute route, Object state) { @@ -312,7 +317,7 @@ public boolean cancel() { credsProvider = new WildcardMatchingCredentialsProvider(); httpClientBuilder = getDefaultHttpClientBuilder(streamManager); httpClient = httpClientBuilder.build(); - + HttpClientInterrupter.watch(this); } @@ -324,10 +329,10 @@ private HttpClientBuilder getDefaultHttpClientBuilder(final StreamManager stream @Override protected HttpResponse doSendRequest(HttpRequest request, HttpClientConnection conn, HttpContext context) throws IOException, HttpException { long start = System.nanoTime(); - + // send request HttpResponse response = super.doSendRequest(request, conn, context); - + // set "sending" for resource RequestInfo.get().send(start, System.nanoTime()); return response; @@ -337,7 +342,7 @@ protected HttpResponse doSendRequest(HttpRequest request, HttpClientConnection c protected HttpResponse doReceiveResponse(HttpRequest request, HttpClientConnection conn, HttpContext context) throws HttpException, IOException { long start = System.nanoTime(); HttpResponse response = super.doReceiveResponse(request, conn, context); - + // +4 => header/data separation long responseHeadersSize = response.getStatusLine().toString().length() + 4; for (Header header : response.getAllHeaders()) { @@ -363,7 +368,7 @@ protected HttpResponse doReceiveResponse(HttpRequest request, HttpClientConnecti } // set waiting time RequestInfo.get().wait(start, System.nanoTime()); - + return response; } }) @@ -471,7 +476,7 @@ public BrowserMobHttpRequest newGet(String url, net.lightbody.bmp.proxy.jetty.ht throw reportBadURI(url, "GET", e); } } - + public BrowserMobHttpRequest newPatch(String url, net.lightbody.bmp.proxy.jetty.http.HttpRequest proxyRequest) { try { URI uri = makeUri(url); @@ -589,7 +594,7 @@ public void checkTimeout() { for (ActiveRequest activeRequest : activeRequests) { activeRequest.checkTimeout(); } - + // Close expired connections httpClientConnMgr.closeExpiredConnections(); // Optionally, close connections @@ -601,7 +606,7 @@ public BrowserMobHttpResponse execute(BrowserMobHttpRequest req) { if (!allowNewRequests.get()) { throw new RuntimeException("No more requests allowed"); } - + try { requestCounter.incrementAndGet(); @@ -632,7 +637,7 @@ private BrowserMobHttpResponse execute(BrowserMobHttpRequest req, int depth) { HttpRequestBase method = req.getMethod(); String url = method.getURI().toString(); - + // process any rewrite requests boolean rewrote = false; String newUrl = url; @@ -663,7 +668,7 @@ private BrowserMobHttpResponse execute(BrowserMobHttpRequest req, int depth) { break; } } - + // url does not match whitelist, set the response code if (!found) { mockResponseCode = currentWhitelist.getResponseCode(); @@ -698,7 +703,7 @@ private BrowserMobHttpResponse execute(BrowserMobHttpRequest req, int depth) { if (os == null) { os = new CappedByteArrayOutputStream(1024 * 1024); // MOB-216 don't buffer more than 1 MB } - + // link the object up now, before we make the request, so that if we get cut off (ie: favicon.ico request and browser shuts down) // we still have the attempt associated, even if we never got a response HarEntry entry = new HarEntry(harPageRef); @@ -815,10 +820,10 @@ public HeaderElement[] getElements() throws ParseException { if(decompress && response.getEntity().getContentLength() != 0) { //getContentLength<0 if unknown if (gzipping) { is = new GZIPInputStream(is); - } else if (deflating) { + } else if (deflating) { // RAW deflate only // WARN : if system is using zlib<=1.1.4 the stream must be append with a dummy byte - // that is not requiered for zlib>1.1.4 (not mentioned on current Inflater javadoc) + // that is not requiered for zlib>1.1.4 (not mentioned on current Inflater javadoc) is = new InflaterInputStream(is, new Inflater(true)); } } @@ -903,8 +908,8 @@ public HeaderElement[] getElements() throws ParseException { } } } - - + + // +4 => header/data separation long requestHeadersSize = method.getRequestLine().toString().length() + 4; long requestBodySize = 0; @@ -919,7 +924,7 @@ public HeaderElement[] getElements() throws ParseException { entry.getRequest().setHeadersSize(requestHeadersSize); entry.getRequest().setBodySize(requestBodySize); if (captureContent) { - + // can we understand the POST data at all? if (method instanceof HttpEntityEnclosingRequestBase && req.getCopy() != null) { HttpEntityEnclosingRequestBase enclosingReq = (HttpEntityEnclosingRequestBase) method; @@ -950,7 +955,7 @@ public HeaderElement[] getElements() throws ParseException { LOG.info("Unexpected problem when parsing input copy", e); } catch (RuntimeException e) { LOG.info("Unexpected problem when parsing input copy", e); - } + } } else { // not URL encoded, so let's grab the body of the POST and capture that try { @@ -988,12 +993,12 @@ public HeaderElement[] getElements() throws ParseException { // ok, we need to decompress it before we can put it in the har file try { InputStream temp = null; - if(gzipping){ + if(gzipping){ temp = new GZIPInputStream(new ByteArrayInputStream(copy.toByteArray())); } else if (deflating) { // RAW deflate only? // WARN : if system is using zlib<=1.1.4 the stream must be append with a dummy byte - // that is not requiered for zlib>1.1.4 (not mentioned on current Inflater javadoc) + // that is not requiered for zlib>1.1.4 (not mentioned on current Inflater javadoc) temp = new InflaterInputStream(new ByteArrayInputStream(copy.toByteArray()), new Inflater(true)); } copy = new ByteArrayOutputStream(); @@ -1001,7 +1006,7 @@ public HeaderElement[] getElements() throws ParseException { } catch (IOException e) { throw new RuntimeException("Error when decompressing input stream", e); } - } + } if (hasTextualContent(contentType)) { setTextOfEntry(entry, copy, contentType); @@ -1095,7 +1100,8 @@ private boolean hasTextualContent(String contentType) { contentType.startsWith("application/javascript") || contentType.startsWith("application/json") || contentType.startsWith("application/xml") || - contentType.startsWith("application/xhtml+xml"); + contentType.startsWith("application/xhtml+xml") || + (contentType.startsWith("application/") && contentType.endsWith("+json")); } private void setBinaryContentOfEntry(HarEntry entry, ByteArrayOutputStream copy) { @@ -1113,7 +1119,7 @@ private void setTextOfEntry(HarEntry entry, ByteArrayOutputStream copy, String c } } - + public void shutdown() { shutdown = true; abortActiveRequests(); @@ -1129,7 +1135,7 @@ public void abortActiveRequests() { for (ActiveRequest activeRequest : activeRequests) { activeRequest.abort(); } - + activeRequests.clear(); } @@ -1221,10 +1227,10 @@ public void blacklistRequests(String pattern, int responseCode, String method) { public List getBlacklistedRequests() { List blacklist = new ArrayList(blacklistEntries.size()); blacklist.addAll(blacklistEntries); - + return blacklist; } - + public Collection getBlacklistedUrls() { return blacklistEntries; } @@ -1236,7 +1242,7 @@ public void clearBlacklist() { public boolean isWhitelistEnabled() { return whitelist.isEnabled(); } - + /** * @deprecated use getWhitelistUrls() * @return unmodifiable list of whitelisted Patterns @@ -1245,19 +1251,19 @@ public boolean isWhitelistEnabled() { public List getWhitelistRequests() { List whitelistPatterns = new ArrayList(whitelist.getPatterns().size()); whitelistPatterns.addAll(whitelist.getPatterns()); - + return Collections.unmodifiableList(whitelistPatterns); } - + /** * Retrieves Patterns of URLs that have been whitelisted. - * + * * @return unmodifiable whitelisted URL Patterns */ public Collection getWhitelistUrls() { return whitelist.getPatterns(); } - + public int getWhitelistResponseCode() { return whitelist.getResponseCode(); } @@ -1265,9 +1271,9 @@ public int getWhitelistResponseCode() { /** * Whitelist the specified request patterns, returning the specified responseCode for non-whitelisted * requests. - * - * @param patterns regular expression strings matching URL patterns to whitelist. if empty or null, - * the whitelist will be enabled but will not match any URLs. + * + * @param patterns regular expression strings matching URL patterns to whitelist. if empty or null, + * the whitelist will be enabled but will not match any URLs. * @param responseCode the HTTP response code to return for non-whitelisted requests */ public void whitelistRequests(String[] patterns, int responseCode) { @@ -1277,14 +1283,14 @@ public void whitelistRequests(String[] patterns, int responseCode) { whitelist = new Whitelist(patterns, responseCode); } } - + /** - * Clears and disables the current whitelist. + * Clears and disables the current whitelist. */ public void clearWhitelist() { whitelist = Whitelist.WHITELIST_DISABLED; } - + public void addHeader(String name, String value) { additionalHeaders.put(name, value); } @@ -1315,7 +1321,7 @@ public void validate(Cookie cookie, CookieOrigin origin) }; } }; - + Registry r = RegistryBuilder.create() .register(CookieSpecs.BEST_MATCH, new BestMatchSpecFactory()) @@ -1323,15 +1329,15 @@ public void validate(Cookie cookie, CookieOrigin origin) new BrowserCompatSpecFactory()) .register("easy", easySpecProvider) .build(); - + RequestConfig requestConfig = RequestConfig.custom() .setCookieSpec("easy") .build(); - + httpClientBuilder.setDefaultCookieSpecRegistry(r) .setDefaultRequestConfig(requestConfig); updateHttpClient(); - + decompress = false; setFollowRedirects(false); } @@ -1340,13 +1346,13 @@ public void validate(Cookie cookie, CookieOrigin origin) * CloseableHttpClient doesn't permit anymore to change parameters easily. * This method allow you to rebuild the httpClientBuilder to get the CloseableHttpClient * When the config is changed. - * + * * So httpClient reference change this may lead to concurrency issue. */ private void updateHttpClient(){ httpClient = httpClientBuilder.build(); } - + public String remappedHost(String host) { if (resolverWrapper.getResolver() instanceof AdvancedHostResolver) { AdvancedHostResolver advancedHostResolver = (AdvancedHostResolver) resolverWrapper.getResolver(); @@ -1422,7 +1428,7 @@ class ActiveRequest { this.request = request; this.start = start; } - + /** * Checks the timeout for this request, and aborts if necessary. * @return true if the request was aborted for exceeding its timeout, otherwise false. @@ -1431,7 +1437,7 @@ boolean checkTimeout() { if (aborting.get()) { return false; } - + if (requestTimeout != -1) { if (request != null && start != null && new Date(System.currentTimeMillis() - requestTimeout).after(start)) { boolean okayToAbort = aborting.compareAndSet(false, true); @@ -1439,18 +1445,18 @@ boolean checkTimeout() { LOG.info("Aborting request to {} after it failed to complete in {} ms", request.getURI().toString(), requestTimeout); abort(); - + return true; } } } - + return false; } public void abort() { request.abort(); - + // no need to close the connection -- the call to request.abort() releases the connection itself } } @@ -1512,6 +1518,14 @@ public static long copyWithStats(InputStream is, OutputStream os) throws IOExcep return bytesCopied; } + public String getHeadersFilterRegexp() { + return headersFilterRegexp; + } + + public void setHeadersFilterRegexp(String headersFilterRegexp) { + this.headersFilterRegexp = headersFilterRegexp; + } + public boolean isCaptureBinaryContent() { return captureBinaryContent; } diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/TrustingSSLSocketFactory.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/TrustingSSLSocketFactory.java index 3b68802c5..bdf8bfa7a 100644 --- a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/TrustingSSLSocketFactory.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/TrustingSSLSocketFactory.java @@ -34,16 +34,11 @@ public enum SSLAlgorithm { private StreamManager streamManager; static { - sslContext = SSLContexts.createDefault(); + sslContext = org.apache.http.ssl.SSLContexts.createDefault(); try { - sslContext = SSLContexts.custom().loadTrustMaterial(null, - new TrustStrategy() { - @Override - public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { - return true; - } - } - ).build(); + sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, + (TrustStrategy) (chain, authType) -> true + ).build(); sslContext.init(null, new TrustManager[]{new TrustEverythingSSLTrustManager()}, null); } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) { diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/SunJsseListener.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/SunJsseListener.java index 0c2262bba..89b64a54a 100644 --- a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/SunJsseListener.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/SunJsseListener.java @@ -15,13 +15,12 @@ package net.lightbody.bmp.proxy.jetty.http; -import com.sun.net.ssl.*; import net.lightbody.bmp.proxy.jetty.log.LogFactory; import net.lightbody.bmp.proxy.jetty.util.InetAddrPort; import net.lightbody.bmp.proxy.jetty.util.Password; import org.apache.commons.logging.Log; -import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.*; import java.io.File; import java.io.FileInputStream; import java.security.KeyStore; @@ -30,25 +29,26 @@ /* ------------------------------------------------------------ */ -/** SSL Socket Listener for Sun's JSSE. - * + +/** + * SSL Socket Listener for Sun's JSSE. + *

* This specialization of JsseListener is an specific listener * using the Sun reference implementation. - * + *

* This is heavily based on the work from Court Demas, which in * turn is based on the work from Forge Research. * - * @version $Id: SunJsseListener.java,v 1.20 2005/08/13 00:01:24 gregwilkins Exp $ - * @deprecated Use org.mortbay.http.SslListener * @author Greg Wilkins (gregw@mortbay.com) * @author Court Demas (court@kiwiconsulting.com) * @author Forge Research Pty Ltd ACN 003 491 576 + * @version $Id: SunJsseListener.java,v 1.20 2005/08/13 00:01:24 gregwilkins Exp $ + * @deprecated Use org.mortbay.http.SslListener **/ -public class SunJsseListener extends JsseListener -{ +public class SunJsseListener extends JsseListener { private static Log log = LogFactory.getLog(SunJsseListener.class); - private String _keystore=DEFAULT_KEYSTORE ; + private String _keystore = DEFAULT_KEYSTORE; private transient Password _password; private transient Password _keypassword; private String _keystore_type = DEFAULT_KEYSTORE_TYPE; @@ -56,149 +56,136 @@ public class SunJsseListener extends JsseListener private String _keystore_provider_class = DEFAULT_KEYSTORE_PROVIDER_CLASS; private boolean _useDefaultTrustStore = false; - /* ------------------------------------------------------------ */ - static - { - Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider()); - } /* ------------------------------------------------------------ */ - public void setKeystore(String keystore) - { + public void setKeystore(String keystore) { _keystore = keystore; } - + /* ------------------------------------------------------------ */ - public String getKeystore() - { + public String getKeystore() { return _keystore; } - + /* ------------------------------------------------------------ */ - public void setPassword(String password) - { - _password = Password.getPassword(PASSWORD_PROPERTY,password,null); + public void setPassword(String password) { + _password = Password.getPassword(PASSWORD_PROPERTY, password, null); } /* ------------------------------------------------------------ */ - public void setKeyPassword(String password) - { - _keypassword = Password.getPassword(KEYPASSWORD_PROPERTY,password,null); + public void setKeyPassword(String password) { + _keypassword = Password.getPassword(KEYPASSWORD_PROPERTY, password, null); } - - + + /* ------------------------------------------------------------ */ - public void setKeystoreType(String keystore_type) - { + public void setKeystoreType(String keystore_type) { _keystore_type = keystore_type; } - + /* ------------------------------------------------------------ */ - public String getKeystoreType() - { + public String getKeystoreType() { return _keystore_type; } /* ------------------------------------------------------------ */ - public void setKeystoreProviderName(String name) - { + public void setKeystoreProviderName(String name) { _keystore_provider_name = name; } /* ------------------------------------------------------------ */ - public String getKeystoreProviderName() - { + public String getKeystoreProviderName() { return _keystore_provider_name; } /* ------------------------------------------------------------ */ - public String getKeystoreProviderClass() - { + public String getKeystoreProviderClass() { return _keystore_provider_class; } /* ------------------------------------------------------------ */ - public void setKeystoreProviderClass(String classname) - { + public void setKeystoreProviderClass(String classname) { _keystore_provider_class = classname; } /* ------------------------------------------------------------ */ + /** * Gets the default trust store flag. * * @return true if the default truststore will be used to initialize the * TrustManager, false otherwise. */ - public boolean getUseDefaultTrustStore() - { + public boolean getUseDefaultTrustStore() { return _useDefaultTrustStore; } /* ------------------------------------------------------------ */ + /** * Set a flag to determine if the default truststore should be used to * initialize the TrustManager. The default truststore will typically be * the ${JAVA_HOME}/jre/lib/security/cacerts. * * @param flag if true, the default truststore will be used. If false, the - * configured keystore will be used as the truststore. + * configured keystore will be used as the truststore. */ - public void setUseDefaultTrustStore(boolean flag) - { + public void setUseDefaultTrustStore(boolean flag) { _useDefaultTrustStore = flag; } /* ------------------------------------------------------------ */ - /** Constructor. + + /** + * Constructor. */ - public SunJsseListener() - { + public SunJsseListener() { super(); } /* ------------------------------------------------------------ */ - /** Constructor. - * @param p_address + + /** + * Constructor. + * + * @param p_address */ - public SunJsseListener(InetAddrPort p_address) - { - super( p_address); + public SunJsseListener(InetAddrPort p_address) { + super(p_address); } - + /* ------------------------------------------------------------ */ - /* - * @return - * @exception Exception + /* + * @return + * @exception Exception */ protected SSLServerSocketFactory createFactory() - throws Exception - { - _keystore = System.getProperty( KEYSTORE_PROPERTY,_keystore); - - log.info(KEYSTORE_PROPERTY+"="+_keystore); - - if (_password==null) - _password = Password.getPassword(PASSWORD_PROPERTY,null,null); - log.info(PASSWORD_PROPERTY+"="+_password.toStarString()); - - if (_keypassword==null) + throws Exception { + _keystore = System.getProperty(KEYSTORE_PROPERTY, _keystore); + + log.info(KEYSTORE_PROPERTY + "=" + _keystore); + + if (_password == null) + _password = Password.getPassword(PASSWORD_PROPERTY, null, null); + log.info(PASSWORD_PROPERTY + "=" + _password.toStarString()); + + if (_keypassword == null) _keypassword = Password.getPassword(KEYPASSWORD_PROPERTY, - null, - _password.toString()); - log.info(KEYPASSWORD_PROPERTY+"="+_keypassword.toStarString()); + null, + _password.toString()); + log.info(KEYPASSWORD_PROPERTY + "=" + _keypassword.toStarString()); KeyStore ks = null; - log.info(KEYSTORE_TYPE_PROPERTY+"="+_keystore_type); - + log.info(KEYSTORE_TYPE_PROPERTY + "=" + _keystore_type); + if (_keystore_provider_class != null) { // find provider. // avoid creating another instance if already installed in Security. java.security.Provider[] installed_providers = Security.getProviders(); java.security.Provider myprovider = null; - for (int i=0; i < installed_providers.length; i++) { + for (int i = 0; i < installed_providers.length; i++) { if (installed_providers[i].getClass().getName().equals(_keystore_provider_class)) { myprovider = installed_providers[i]; break; @@ -209,37 +196,37 @@ protected SSLServerSocketFactory createFactory() myprovider = (java.security.Provider) Class.forName(_keystore_provider_class).newInstance(); Security.addProvider(myprovider); } - log.info(KEYSTORE_PROVIDER_CLASS_PROPERTY+"="+_keystore_provider_class); - ks = KeyStore.getInstance(_keystore_type,myprovider.getName()); + log.info(KEYSTORE_PROVIDER_CLASS_PROPERTY + "=" + _keystore_provider_class); + ks = KeyStore.getInstance(_keystore_type, myprovider.getName()); } else if (_keystore_provider_name != null) { - log.info(KEYSTORE_PROVIDER_NAME_PROPERTY+"="+_keystore_provider_name); - ks = KeyStore.getInstance(_keystore_type,_keystore_provider_name); + log.info(KEYSTORE_PROVIDER_NAME_PROPERTY + "=" + _keystore_provider_name); + ks = KeyStore.getInstance(_keystore_type, _keystore_provider_name); } else { ks = KeyStore.getInstance(_keystore_type); - log.info(KEYSTORE_PROVIDER_NAME_PROPERTY+"=[DEFAULT]"); + log.info(KEYSTORE_PROVIDER_NAME_PROPERTY + "=[DEFAULT]"); } - - ks.load( new FileInputStream( new File( _keystore ) ), - _password.toString().toCharArray()); - - KeyManagerFactory km = KeyManagerFactory.getInstance( "SunX509","SunJSSE"); - km.init( ks, _keypassword.toString().toCharArray() ); - KeyManager[] kma = km.getKeyManagers(); - - TrustManagerFactory tm = TrustManagerFactory.getInstance("SunX509","SunJSSE"); + + ks.load(new FileInputStream(new File(_keystore)), + _password.toString().toCharArray()); + + KeyManagerFactory km = KeyManagerFactory.getInstance("SunX509", "SunJSSE"); + km.init(ks, _keypassword.toString().toCharArray()); + KeyManager[] kma = km.getKeyManagers(); + + TrustManagerFactory tm = TrustManagerFactory.getInstance("SunX509", "SunJSSE"); if (_useDefaultTrustStore) { - tm.init( (KeyStore)null ); + tm.init((KeyStore) null); } else { - tm.init( ks ); + tm.init(ks); } TrustManager[] tma = tm.getTrustManagers(); - - SSLContext sslc = SSLContext.getInstance( "SSL" ); - sslc.init( kma, tma, SecureRandom.getInstance("SHA1PRNG")); - + + SSLContext sslc = SSLContext.getInstance("SSL"); + sslc.init(kma, tma, SecureRandom.getInstance("SHA1PRNG")); + SSLServerSocketFactory ssfc = sslc.getServerSocketFactory(); - log.info("SSLServerSocketFactory="+ssfc); + log.info("SSLServerSocketFactory=" + ssfc); return ssfc; } } diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/Server.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/Server.java index 6b04eafb6..d483f13ef 100644 --- a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/Server.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/Server.java @@ -35,105 +35,104 @@ /* ------------------------------------------------------------ */ -/** The Jetty HttpServer. - * + +/** + * The Jetty HttpServer. + *

* This specialization of org.mortbay.http.HttpServer adds knowledge * about servlets and their specialized contexts. It also included * support for initialization from xml configuration files * that follow the XmlConfiguration dtd. - * + *

* HttpContexts created by Server are of the type * org.mortbay.jetty.servlet.ServletHttpContext unless otherwise * specified. - * + *

* This class also provides a main() method which starts a server for * each config file passed on the command line. If the system * property JETTY_NO_SHUTDOWN_HOOK is not set to true, then a shutdown - * hook is thread is registered to stop these servers. + * hook is thread is registered to stop these servers. * + * @author Greg Wilkins (gregw) + * @version $Revision: 1.40 $ * @see net.lightbody.bmp.proxy.jetty.xml.XmlConfiguration * @see net.lightbody.bmp.proxy.jetty.jetty.servlet.ServletHttpContext - * @version $Revision: 1.40 $ - * @author Greg Wilkins (gregw) */ -public class Server extends HttpServer -{ +public class Server extends HttpServer { static Log log = LogFactory.getLog(Server.class); - private String[] _webAppConfigurationClassNames = - new String[]{"XMLConfiguration", "JettyWebConfiguration"}; + private String[] _webAppConfigurationClassNames = + new String[]{"XMLConfiguration", "JettyWebConfiguration"}; private String _configuration; private String _rootWebApp; private static ShutdownHookThread hookThread = new ShutdownHookThread(); - + /* ------------------------------------------------------------ */ - /** Constructor. + + /** + * Constructor. */ - public Server() - { + public Server() { } - + /* ------------------------------------------------------------ */ - /** Constructor. + + /** + * Constructor. + * * @param configuration The filename or URL of the XML - * configuration file. + * configuration file. */ public Server(String configuration) - throws IOException - { + throws IOException { this(Resource.newResource(configuration).getURL()); } - + /* ------------------------------------------------------------ */ - /** Constructor. + + /** + * Constructor. + * * @param configuration The filename or URL of the XML - * configuration file. + * configuration file. */ public Server(Resource configuration) - throws IOException - { + throws IOException { this(configuration.getURL()); } - + /* ------------------------------------------------------------ */ - /** Constructor. + + /** + * Constructor. + * * @param configuration The filename or URL of the XML - * configuration file. + * configuration file. */ public Server(URL configuration) - throws IOException - { - _configuration=configuration.toString(); + throws IOException { + _configuration = configuration.toString(); Server.hookThread.add(this); - try - { - XmlConfiguration config=new XmlConfiguration(configuration); + try { + XmlConfiguration config = new XmlConfiguration(configuration); config.configure(this); - } - catch(IOException e) - { + } catch (IOException e) { throw e; - } - catch(InvocationTargetException e) - { - log.warn(LogSupport.EXCEPTION,e.getTargetException()); - throw new IOException("Jetty configuration problem: "+e.getTargetException()); - } - catch(Exception e) - { - log.warn(LogSupport.EXCEPTION,e); - throw new IOException("Jetty configuration problem: "+e); + } catch (InvocationTargetException e) { + log.warn(LogSupport.EXCEPTION, e.getTargetException()); + throw new IOException("Jetty configuration problem: " + e.getTargetException()); + } catch (Exception e) { + log.warn(LogSupport.EXCEPTION, e); + throw new IOException("Jetty configuration problem: " + e); } } /* ------------------------------------------------------------ */ - public boolean getStopAtShutdown() - { + public boolean getStopAtShutdown() { return hookThread.contains(this); } - + /* ------------------------------------------------------------ */ - public void setStopAtShutdown(boolean stop) - { + public void setStopAtShutdown(boolean stop) { if (stop) hookThread.add(this); else @@ -141,268 +140,283 @@ public void setStopAtShutdown(boolean stop) } /* ------------------------------------------------------------ */ - /** Get the root webapp name. - * @return The name of the root webapp (eg. "root" for root.war). + + /** + * Get the root webapp name. + * + * @return The name of the root webapp (eg. "root" for root.war). */ - public String getRootWebApp() - { + public String getRootWebApp() { return _rootWebApp; } - + /* ------------------------------------------------------------ */ - /** Set the root webapp name. - * @param rootWebApp The name of the root webapp (eg. "root" for root.war). + + /** + * Set the root webapp name. + * + * @param rootWebApp The name of the root webapp (eg. "root" for root.war). */ - public void setRootWebApp(String rootWebApp) - { + public void setRootWebApp(String rootWebApp) { _rootWebApp = rootWebApp; } - + /* ------------------------------------------------------------ */ - /** Configure the server from an XML file. + + /** + * Configure the server from an XML file. + * * @param configuration The filename or URL of the XML - * configuration file. + * configuration file. */ public void configure(String configuration) - throws IOException - { + throws IOException { - URL url=Resource.newResource(configuration).getURL(); - if (_configuration!=null && _configuration.equals(url.toString())) + URL url = Resource.newResource(configuration).getURL(); + if (_configuration != null && _configuration.equals(url.toString())) return; - if (_configuration!=null) - throw new IllegalStateException("Already configured with "+_configuration); - try - { - XmlConfiguration config=new XmlConfiguration(url); - _configuration=url.toString(); + if (_configuration != null) + throw new IllegalStateException("Already configured with " + _configuration); + try { + XmlConfiguration config = new XmlConfiguration(url); + _configuration = url.toString(); config.configure(this); - } - catch(IOException e) - { + } catch (IOException e) { throw e; - } - catch(Exception e) - { - log.warn(LogSupport.EXCEPTION,e); - throw new IOException("Jetty configuration problem: "+e); + } catch (Exception e) { + log.warn(LogSupport.EXCEPTION, e); + throw new IOException("Jetty configuration problem: " + e); } } - + /* ------------------------------------------------------------ */ - public String getConfiguration() - { + public String getConfiguration() { return _configuration; } - + /* ------------------------------------------------------------ */ - /** Create a new ServletHttpContext. + + /** + * Create a new ServletHttpContext. * Ths method is called by HttpServer to creat new contexts. Thus * calls to addContext or getContext that result in a new Context * being created will return an * org.mortbay.jetty.servlet.ServletHttpContext instance. + * * @return ServletHttpContext */ - protected HttpContext newHttpContext() - { + protected HttpContext newHttpContext() { return new ServletHttpContext(); } - + /* ------------------------------------------------------------ */ - /** Create a new WebApplicationContext. - * Ths method is called by Server to creat new contexts for web - * applications. Thus calls to addWebApplication that result in + + /** + * Create a new WebApplicationContext. + * Ths method is called by Server to creat new contexts for web + * applications. Thus calls to addWebApplication that result in * a new Context being created will return an correct class instance. * Derived class can override this method to create instance of its * own class derived from WebApplicationContext in case it needs more * functionality. + * * @param webApp The Web application directory or WAR file. * @return WebApplicationContext */ protected WebApplicationContext newWebApplicationContext( - String webApp - ) - { + String webApp + ) { return new WebApplicationContext(webApp); } /* ------------------------------------------------------------ */ - /** Add Web Application. + + /** + * Add Web Application. + * * @param contextPathSpec The context path spec. Which must be of - * the form / or /path/* - * @param webApp The Web application directory or WAR file. + * the form / or /path/* + * @param webApp The Web application directory or WAR file. * @return The WebApplicationContext - * @exception IOException + * @throws IOException */ public WebApplicationContext addWebApplication(String contextPathSpec, String webApp) - throws IOException - { - return addWebApplication(null,contextPathSpec,webApp); + throws IOException { + return addWebApplication(null, contextPathSpec, webApp); } - + /* ------------------------------------------------------------ */ - /** Add Web Application. - * @param virtualHost Virtual host name or null + + /** + * Add Web Application. + * + * @param virtualHost Virtual host name or null * @param contextPathSpec The context path spec. Which must be of - * the form / or /path/* - * @param webApp The Web application directory or WAR file. + * the form / or /path/* + * @param webApp The Web application directory or WAR file. * @return The WebApplicationContext - * @exception IOException + * @throws IOException */ public WebApplicationContext addWebApplication(String virtualHost, String contextPathSpec, String webApp) - throws IOException - { + throws IOException { WebApplicationContext appContext = - newWebApplicationContext(webApp); + newWebApplicationContext(webApp); appContext.setContextPath(contextPathSpec); - addContext(virtualHost,appContext); - if(log.isDebugEnabled())log.debug("Web Application "+appContext+" added"); + addContext(virtualHost, appContext); + if (log.isDebugEnabled()) log.debug("Web Application " + appContext + " added"); return appContext; } - + /* ------------------------------------------------------------ */ - /** Add Web Applications. + + /** + * Add Web Applications. * Add auto webapplications to the server. The name of the * webapp directory or war is used as the context name. If a * webapp is called "root" it is added at "/". + * * @param webapps Directory file name or URL to look for auto webapplication. - * @exception IOException + * @throws IOException */ public WebApplicationContext[] addWebApplications(String webapps) - throws IOException - { - return addWebApplications(null,webapps,null,false); + throws IOException { + return addWebApplications(null, webapps, null, false); } - + /* ------------------------------------------------------------ */ - /** Add Web Applications. + + /** + * Add Web Applications. * Add auto webapplications to the server. The name of the * webapp directory or war is used as the context name. If the * webapp matches the rootWebApp it is added as the "/" context. - * @param host Virtual host name or null + * + * @param host Virtual host name or null * @param webapps Directory file name or URL to look for auto webapplication. - * @exception IOException + * @throws IOException */ public WebApplicationContext[] addWebApplications(String host, String webapps) - throws IOException - { - return addWebApplications(host,webapps,null,false); + throws IOException { + return addWebApplications(host, webapps, null, false); } - + /* ------------------------------------------------------------ */ - /** Add Web Applications. + + /** + * Add Web Applications. * Add auto webapplications to the server. The name of the * webapp directory or war is used as the context name. If the * webapp matches the rootWebApp it is added as the "/" context. - * @param host Virtual host name or null + * + * @param host Virtual host name or null * @param webapps Directory file name or URL to look for auto - * webapplication. + * webapplication. * @param extract If true, extract war files - * @exception IOException + * @throws IOException */ public WebApplicationContext[] addWebApplications(String host, String webapps, boolean extract) - throws IOException - { - return addWebApplications(host,webapps,null,extract); + throws IOException { + return addWebApplications(host, webapps, null, extract); } - + /* ------------------------------------------------------------ */ - /** Add Web Applications. + + /** + * Add Web Applications. * Add auto webapplications to the server. The name of the * webapp directory or war is used as the context name. If the * webapp matches the rootWebApp it is added as the "/" context. - * @param host Virtual host name or null - * @param webapps Directory file name or URL to look for auto - * webapplication. + * + * @param host Virtual host name or null + * @param webapps Directory file name or URL to look for auto + * webapplication. * @param defaults The defaults xml filename or URL which is - * loaded before any in the web app. Must respect the web.dtd. - * If null the default defaults file is used. If the empty string, then - * no defaults file is used. - * @param extract If true, extract war files - * @exception IOException + * loaded before any in the web app. Must respect the web.dtd. + * If null the default defaults file is used. If the empty string, then + * no defaults file is used. + * @param extract If true, extract war files + * @throws IOException */ public WebApplicationContext[] addWebApplications(String host, String webapps, String defaults, boolean extract) - throws IOException - { - return addWebApplications(host,webapps,defaults,extract,true); + throws IOException { + return addWebApplications(host, webapps, defaults, extract, true); } /* ------------------------------------------------------------ */ - /** Add Web Applications. + + /** + * Add Web Applications. * Add auto webapplications to the server. The name of the * webapp directory or war is used as the context name. If the * webapp matches the rootWebApp it is added as the "/" context. - * @param host Virtual host name or null - * @param webapps Directory file name or URL to look for auto - * webapplication. - * @param defaults The defaults xml filename or URL which is - * loaded before any in the web app. Must respect the web.dtd. - * If null the default defaults file is used. If the empty string, then - * no defaults file is used. - * @param extract If true, extract war files + * + * @param host Virtual host name or null + * @param webapps Directory file name or URL to look for auto + * webapplication. + * @param defaults The defaults xml filename or URL which is + * loaded before any in the web app. Must respect the web.dtd. + * If null the default defaults file is used. If the empty string, then + * no defaults file is used. + * @param extract If true, extract war files * @param java2CompliantClassLoader True if java2 compliance is applied to all webapplications - * @exception IOException + * @throws IOException */ public WebApplicationContext[] addWebApplications(String host, String webapps, String defaults, boolean extract, - boolean java2CompliantClassLoader) - throws IOException - { + boolean java2CompliantClassLoader) + throws IOException { ArrayList wacs = new ArrayList(); - Resource r=Resource.newResource(webapps); + Resource r = Resource.newResource(webapps); if (!r.exists()) - throw new IllegalArgumentException("No such webapps resource "+r); - + throw new IllegalArgumentException("No such webapps resource " + r); + if (!r.isDirectory()) - throw new IllegalArgumentException("Not directory webapps resource "+r); - - String[] files=r.list(); - - for (int f=0;files!=null && f + * Thread is hooked first time list of servers is changed. */ - private void createShutdownHook() { - if (!Boolean.getBoolean("JETTY_NO_SHUTDOWN_HOOK") && !hooked) { - try { - Method shutdownHook = java.lang.Runtime.class.getMethod("addShutdownHook", - new Class[] { java.lang.Thread.class }); - shutdownHook.invoke(Runtime.getRuntime(), new Object[] { this }); - this.hooked = true; - } catch (Exception e) { - if (log.isDebugEnabled()) log.debug("No shutdown hook in JVM ", e); + private static class ShutdownHookThread extends Thread { + private boolean hooked = false; + private ArrayList servers = new ArrayList(); + + /** + * Hooks this thread for shutdown. + * + * @see java.lang.Runtime#addShutdownHook(java.lang.Thread) + */ + private void createShutdownHook() { + if (!Boolean.getBoolean("JETTY_NO_SHUTDOWN_HOOK") && !hooked) { + try { + Method shutdownHook = java.lang.Runtime.class.getMethod("addShutdownHook", + new Class[]{java.lang.Thread.class}); + shutdownHook.invoke(Runtime.getRuntime(), new Object[]{this}); + this.hooked = true; + } catch (Exception e) { + if (log.isDebugEnabled()) log.debug("No shutdown hook in JVM ", e); + } + } } - } - } - /** - * Add Server to servers list. - */ - public boolean add(Server server) { - createShutdownHook(); - return this.servers.add(server); - } - - /** - * Contains Server in servers list? - */ - public boolean contains(Server server) { - return this.servers.contains(server); - } + /** + * Add Server to servers list. + */ + public boolean add(Server server) { + createShutdownHook(); + return this.servers.add(server); + } - /** - * Append all Servers from Collection - */ - public boolean addAll(Collection c) { - createShutdownHook(); - return this.servers.addAll(c); - } + /** + * Contains Server in servers list? + */ + public boolean contains(Server server) { + return this.servers.contains(server); + } - /** - * Clear list of Servers. - */ - public void clear() { - createShutdownHook(); - this.servers.clear(); - } + /** + * Append all Servers from Collection + */ + public boolean addAll(Collection c) { + createShutdownHook(); + return this.servers.addAll(c); + } - /** - * Remove Server from list. - */ - public boolean remove(Server server) { - createShutdownHook(); - return this.servers.remove(server); - } + /** + * Clear list of Servers. + */ + public void clear() { + createShutdownHook(); + this.servers.clear(); + } - /** - * Remove all Servers in Collection from list. - */ - public boolean removeAll(Collection c) { - createShutdownHook(); - return this.servers.removeAll(c); - } + /** + * Remove Server from list. + */ + public boolean remove(Server server) { + createShutdownHook(); + return this.servers.remove(server); + } - /** - * Stop all Servers in list. - */ - public void run() { - setName("Shutdown"); - log.info("Shutdown hook executing"); - Iterator it = servers.iterator(); - while (it.hasNext()) { - Server svr = (Server) it.next(); - if (svr == null) continue; - try { - svr.stop(); - } catch (Exception e) { - log.warn(LogSupport.EXCEPTION, e); + /** + * Remove all Servers in Collection from list. + */ + public boolean removeAll(Collection c) { + createShutdownHook(); + return this.servers.removeAll(c); } - log.info("Shutdown hook complete"); - // Try to avoid JVM crash - try { - Thread.sleep(1000); - } catch (Exception e) { - log.warn(LogSupport.EXCEPTION, e); + /** + * Stop all Servers in list. + */ + public void run() { + setName("Shutdown"); + log.info("Shutdown hook executing"); + Iterator it = servers.iterator(); + while (it.hasNext()) { + Server svr = (Server) it.next(); + if (svr == null) continue; + try { + svr.stop(); + } catch (Exception e) { + log.warn(LogSupport.EXCEPTION, e); + } + log.info("Shutdown hook complete"); + + // Try to avoid JVM crash + try { + Thread.sleep(1000); + } catch (Exception e) { + log.warn(LogSupport.EXCEPTION, e); + } + } } - } } - } } diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHandler.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHandler.java index 220af438d..78414813c 100644 --- a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHandler.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHandler.java @@ -30,6 +30,7 @@ import org.apache.commons.logging.Log; import javax.servlet.*; +import javax.servlet.descriptor.JspConfigDescriptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @@ -43,50 +44,52 @@ /* --------------------------------------------------------------------- */ -/** Servlet HttpHandler. + +/** + * Servlet HttpHandler. * This handler maps requests to servlets that implement the * javax.servlet.http.HttpServlet API. - *

+ *

* This handler does not implement the full J2EE features and is intended to * be used when a full web application is not required. Specifically filters * and request wrapping are not supported. - *

+ *

* If a SessionManager is not added to the handler before it is * initialized, then a HashSessionManager with a standard * java.util.Random generator is created. - *

- * @see net.lightbody.bmp.proxy.jetty.jetty.servlet.WebApplicationHandler - * @version $Id: ServletHandler.java,v 1.133 2006/03/15 14:43:00 gregwilkins Exp $ + *

+ * * @author Greg Wilkins + * @version $Id: ServletHandler.java,v 1.133 2006/03/15 14:43:00 gregwilkins Exp $ + * @see net.lightbody.bmp.proxy.jetty.jetty.servlet.WebApplicationHandler */ -public class ServletHandler extends Container implements HttpHandler -{ +public class ServletHandler extends Container implements HttpHandler { private static Log log = LogFactory.getLog(ServletHandler.class); /* ------------------------------------------------------------ */ - public static final String __DEFAULT_SERVLET="default"; - public static final String __J_S_CONTEXT_TEMPDIR="javax.servlet.context.tempdir"; - public static final String __J_S_ERROR_EXCEPTION="javax.servlet.error.exception"; - public static final String __J_S_ERROR_EXCEPTION_TYPE="javax.servlet.error.exception_type"; - public static final String __J_S_ERROR_MESSAGE="javax.servlet.error.message"; - public static final String __J_S_ERROR_REQUEST_URI="javax.servlet.error.request_uri"; - public static final String __J_S_ERROR_SERVLET_NAME="javax.servlet.error.servlet_name"; - public static final String __J_S_ERROR_STATUS_CODE="javax.servlet.error.status_code"; - + public static final String __DEFAULT_SERVLET = "default"; + public static final String __J_S_CONTEXT_TEMPDIR = "javax.servlet.context.tempdir"; + public static final String __J_S_ERROR_EXCEPTION = "javax.servlet.error.exception"; + public static final String __J_S_ERROR_EXCEPTION_TYPE = "javax.servlet.error.exception_type"; + public static final String __J_S_ERROR_MESSAGE = "javax.servlet.error.message"; + public static final String __J_S_ERROR_REQUEST_URI = "javax.servlet.error.request_uri"; + public static final String __J_S_ERROR_SERVLET_NAME = "javax.servlet.error.servlet_name"; + public static final String __J_S_ERROR_STATUS_CODE = "javax.servlet.error.status_code"; + /* ------------------------------------------------------------ */ - private static final boolean __Slosh2Slash=File.separatorChar=='\\'; - private static String __AllowString="GET, HEAD, POST, OPTIONS, TRACE"; + private static final boolean __Slosh2Slash = File.separatorChar == '\\'; + private static String __AllowString = "GET, HEAD, POST, OPTIONS, TRACE"; + - /* ------------------------------------------------------------ */ - private boolean _usingCookies=true; - private boolean _autoInitializeServlets=true; + private boolean _usingCookies = true; + private boolean _autoInitializeServlets = true; private String _name; - + /* ------------------------------------------------------------ */ - protected PathMap _servletMap=new PathMap(); - protected Map _nameMap=new HashMap(); - protected Map _attributes=new HashMap(3); + protected PathMap _servletMap = new PathMap(); + protected Map _nameMap = new HashMap(); + protected Map _attributes = new HashMap(3); protected String _formLoginPage; protected String _formErrorPage; protected SessionManager _sessionManager; @@ -97,830 +100,791 @@ public class ServletHandler extends Container implements HttpHandler protected transient HttpContext _httpContext; /* ------------------------------------------------------------ */ - /** Constructor. + + /** + * Constructor. */ - public ServletHandler() - { - _context=new Context(); + public ServletHandler() { + _context = new Context(); } /* ------------------------------------------------------------ */ - public void setName(String name) - { - _name=name; + public void setName(String name) { + _name = name; } - + /* ------------------------------------------------------------ */ - public String getName() - { - if (_name==null) - { - _name=this.getClass().getName(); + public String getName() { + if (_name == null) { + _name = this.getClass().getName(); if (!log.isDebugEnabled()) - _name=_name.substring(_name.lastIndexOf('.')+1); + _name = _name.substring(_name.lastIndexOf('.') + 1); } return _name; } - + /* ------------------------------------------------------------ */ - public HttpContext getHttpContext() - { + public HttpContext getHttpContext() { return _httpContext; } - + /* ------------------------------------------------------------ */ - public void initialize(HttpContext context) - { - SessionManager sessionManager=getSessionManager(); - + public void initialize(HttpContext context) { + SessionManager sessionManager = getSessionManager(); - if (_httpContext!=null&& _httpContext!=context) + + if (_httpContext != null && _httpContext != context) throw new IllegalStateException("Can't initialize handler for different context"); - _httpContext=context; - + _httpContext = context; + sessionManager.initialize(this); } /* ------------------------------------------------------------ */ public void formAuthInit(String formLoginPage, - String formErrorPage) - { - _formLoginPage=formLoginPage; - _formErrorPage=formErrorPage; - } - + String formErrorPage) { + _formLoginPage = formLoginPage; + _formErrorPage = formErrorPage; + } + /* ------------------------------------------------------------ */ - public void setSessionManager(SessionManager sm) - { + public void setSessionManager(SessionManager sm) { if (isStarted()) throw new IllegalStateException("Started"); - int mii=0; - boolean setMii=false; - - if ( _sessionManager!=null) - { - mii=_sessionManager.getMaxInactiveInterval(); - setMii=true; - if (getHttpContext()!=null) + int mii = 0; + boolean setMii = false; + + if (_sessionManager != null) { + mii = _sessionManager.getMaxInactiveInterval(); + setMii = true; + if (getHttpContext() != null) _sessionManager.initialize(null); removeComponent(_sessionManager); } - _sessionManager=sm; + _sessionManager = sm; - if (_sessionManager!=null) - { - if (getHttpContext()!=null) + if (_sessionManager != null) { + if (getHttpContext() != null) _sessionManager.initialize(this); if (setMii) _sessionManager.setMaxInactiveInterval(mii); addComponent(_sessionManager); - } - - _sessionManager=sm; + } + + _sessionManager = sm; } - + /* ------------------------------------------------------------ */ - public SessionManager getSessionManager() - { - if (_sessionManager==null) - { + public SessionManager getSessionManager() { + if (_sessionManager == null) { _sessionManager = new HashSessionManager(); addComponent(_sessionManager); } return _sessionManager; } - + /* ------------------------------------------------------------ */ - public ServletContext getServletContext() { return _context; } + public ServletContext getServletContext() { + return _context; + } /* ------------------------------------------------------------ */ - public PathMap getServletMap() { return _servletMap; } - + public PathMap getServletMap() { + return _servletMap; + } + /* ------------------------------------------------------------ */ - public boolean isUsingCookies() { return _usingCookies; } - + public boolean isUsingCookies() { + return _usingCookies; + } + /* ------------------------------------------------------------ */ - /** Set the dynamic servlet path. + + /** + * Set the dynamic servlet path. + * * @deprecated Use org.mortbay.jetty.servlet.Invoker */ - public void setDynamicServletPathSpec(String dynamicServletPathSpec) - { + public void setDynamicServletPathSpec(String dynamicServletPathSpec) { log.warn("setDynamicServletPathSpec is Deprecated."); } - + /* ------------------------------------------------------------ */ - /** Set dynamic servlet initial parameters. + + /** + * Set dynamic servlet initial parameters. + * * @deprecated Use org.mortbay.jetty.servlet.Invoker */ - public void setDynamicInitParams(Map initParams) - { + public void setDynamicInitParams(Map initParams) { log.warn("setDynamicInitParams is Deprecated."); } /* ------------------------------------------------------------ */ - /** Set serving dynamic system servlets. + + /** + * Set serving dynamic system servlets. + * * @deprecated Use org.mortbay.jetty.servlet.Invoker */ - public void setServeDynamicSystemServlets(boolean b) - { + public void setServeDynamicSystemServlets(boolean b) { log.warn("setServeDynamicSystemServlets is Deprecated."); } - + /* ------------------------------------------------------------ */ - public ClassLoader getClassLoader() - { + public ClassLoader getClassLoader() { return _loader; } /* ------------------------------------------------------------ */ - /** + + /** * @param uc If true, cookies are used for sessions */ - public void setUsingCookies(boolean uc) - { - _usingCookies=uc; + public void setUsingCookies(boolean uc) { + _usingCookies = uc; } /* ------------------------------------------------------------ */ public ServletHolder newServletHolder(String name, String servletClass, - String forcedPath) - { + String forcedPath) { if (_nameMap.containsKey(name)) - throw new IllegalArgumentException("Named servlet already exists: "+name); - - ServletHolder holder = new ServletHolder(this,name,servletClass,forcedPath); + throw new IllegalArgumentException("Named servlet already exists: " + name); + + ServletHolder holder = new ServletHolder(this, name, servletClass, forcedPath); addServletHolder(holder); - + return holder; } /* ------------------------------------------------------------ */ public ServletHolder newServletHolder(String name, - String servletClass) - { - return newServletHolder(name,servletClass,null); - } - + String servletClass) { + return newServletHolder(name, servletClass, null); + } + /* ------------------------------------------------------------ */ - public ServletHolder getServletHolder(String name) - { - return (ServletHolder)_nameMap.get(name); - } + public ServletHolder getServletHolder(String name) { + return (ServletHolder) _nameMap.get(name); + } /* ------------------------------------------------------------ */ - /** + + /** * Map a servlet to a pathSpec - * @param pathSpec The pathspec to map + * + * @param pathSpec The pathspec to map * @param servletName The name of the servlet, which must have already been added. * @return The servlet holder of the mapped servlet. */ public ServletHolder mapPathToServlet(String pathSpec, - String servletName) - { - ServletHolder holder =(ServletHolder)_nameMap.get(servletName); - - if (!pathSpec.startsWith("/") && !pathSpec.startsWith("*")) - { - log.warn("pathSpec should start with '/' or '*' : "+pathSpec); - pathSpec="/"+pathSpec; - } - - if (holder==null) - throw new IllegalArgumentException("Unknown servlet: "+servletName); - _servletMap.put(pathSpec,holder); + String servletName) { + ServletHolder holder = (ServletHolder) _nameMap.get(servletName); + + if (!pathSpec.startsWith("/") && !pathSpec.startsWith("*")) { + log.warn("pathSpec should start with '/' or '*' : " + pathSpec); + pathSpec = "/" + pathSpec; + } + + if (holder == null) + throw new IllegalArgumentException("Unknown servlet: " + servletName); + _servletMap.put(pathSpec, holder); return holder; } - + /* ------------------------------------------------------------ */ - /** Add a servlet. - * @param name The servlet name. - * @param pathSpec A path specification to map this servlet to. + + /** + * Add a servlet. + * + * @param name The servlet name. + * @param pathSpec A path specification to map this servlet to. * @param servletClass The class name of the servlet. - * @param forcedPath If non null, the request attribute - * javax.servlet.include.servlet_path will be set to this path before - * service is called. + * @param forcedPath If non null, the request attribute + * javax.servlet.include.servlet_path will be set to this path before + * service is called. * @return The ServletHolder for the servlet. */ public ServletHolder addServlet(String name, String pathSpec, String servletClass, - String forcedPath) - { + String forcedPath) { ServletHolder holder = getServletHolder(name); - if (holder==null) - holder = newServletHolder(name,servletClass,forcedPath); - mapPathToServlet(pathSpec,name); - if (isStarted() && !holder.isStarted()) - { - try{holder.start();} - catch(Exception e){log.warn(LogSupport.EXCEPTION,e);} + if (holder == null) + holder = newServletHolder(name, servletClass, forcedPath); + mapPathToServlet(pathSpec, name); + if (isStarted() && !holder.isStarted()) { + try { + holder.start(); + } catch (Exception e) { + log.warn(LogSupport.EXCEPTION, e); + } } return holder; } - + /* ------------------------------------------------------------ */ - /** Add a servlet. - * @param name The servlet name. - * @param pathSpec A path specification to map this servlet to. + + /** + * Add a servlet. + * + * @param name The servlet name. + * @param pathSpec A path specification to map this servlet to. * @param servletClass The class name of the servlet. * @return The ServletHolder for the servlet. */ public ServletHolder addServlet(String name, String pathSpec, - String servletClass) - { - return addServlet(name,pathSpec,servletClass,null); + String servletClass) { + return addServlet(name, pathSpec, servletClass, null); } - + /* ------------------------------------------------------------ */ + /** * Add a servlet instance to this handler and map it to a pathspec. - * @param pathSpec The pathmapping + * + * @param pathSpec The pathmapping * @param servletClass The class of the servlet * @return The created ServletHolder */ public ServletHolder addServlet(String pathSpec, - String servletClass) - { - return addServlet(servletClass,pathSpec,servletClass,null); + String servletClass) { + return addServlet(servletClass, pathSpec, servletClass, null); } - + /* ------------------------------------------------------------ */ + /** * Register an existing ServletHolder with this handler. + * * @param holder the ServletHolder to register. */ - public void addServletHolder(ServletHolder holder) - { + public void addServletHolder(ServletHolder holder) { ServletHolder existing = (ServletHolder) - _nameMap.get(holder.getName()); - if (existing==null) - _nameMap.put(holder.getName(),holder); - else if (existing!=holder) - throw new IllegalArgumentException("Holder already exists for name: "+holder.getName()); + _nameMap.get(holder.getName()); + if (existing == null) + _nameMap.put(holder.getName(), holder); + else if (existing != holder) + throw new IllegalArgumentException("Holder already exists for name: " + holder.getName()); addComponent(holder); } - + /* ------------------------------------------------------------ */ - public boolean isAutoInitializeServlets() - { + public boolean isAutoInitializeServlets() { return _autoInitializeServlets; } /* ------------------------------------------------------------ */ - public void setAutoInitializeServlets(boolean b) - { - _autoInitializeServlets=b; + public void setAutoInitializeServlets(boolean b) { + _autoInitializeServlets = b; } - + /* ----------------------------------------------------------------- */ protected synchronized void doStart() - throws Exception - { + throws Exception { if (isStarted()) return; - - _contextLog = LogFactory.getLog("net.lightbody.bmp.proxy.jetty.jetty.context."+getHttpContext().getHttpContextName()); - - if (_contextLog==null) - _contextLog=log; - - if (_sessionManager!=null) + + _contextLog = LogFactory.getLog("net.lightbody.bmp.proxy.jetty.jetty.context." + getHttpContext().getHttpContextName()); + + if (_contextLog == null) + _contextLog = log; + + if (_sessionManager != null) _sessionManager.start(); - + // Initialize classloader - _loader=getHttpContext().getClassLoader(); + _loader = getHttpContext().getClassLoader(); if (_autoInitializeServlets) initializeServlets(); - } - + } + /* ------------------------------------------------------------ */ - /** Get Servlets. + + /** + * Get Servlets. + * * @return Array of defined servlets */ - public ServletHolder[] getServlets() - { + public ServletHolder[] getServlets() { // Sort and Initialize servlets HashSet holder_set = new HashSet(_nameMap.size()); holder_set.addAll(_nameMap.values()); - ServletHolder holders [] = (ServletHolder []) - holder_set.toArray(new ServletHolder [holder_set.size()]); - java.util.Arrays.sort (holders); + ServletHolder[] holders = (ServletHolder[]) + holder_set.toArray(new ServletHolder[holder_set.size()]); + java.util.Arrays.sort(holders); return holders; } - + /* ------------------------------------------------------------ */ - /** Initialize load-on-startup servlets. + + /** + * Initialize load-on-startup servlets. * Called automatically from start if autoInitializeServlet is true. */ public void initializeServlets() - throws Exception - { + throws Exception { MultiException mx = new MultiException(); - + // Sort and Initialize servlets ServletHolder[] holders = getServlets(); - for (int i=0; i0;) - { - try - { + for (int i = holders.length; i-- > 0; ) { + try { if (holders[i].isStarted()) holders[i].stop(); + } catch (Exception e) { + log.warn(LogSupport.EXCEPTION, e); } - catch(Exception e){log.warn(LogSupport.EXCEPTION,e);} } - + // Stop the session manager _sessionManager.stop(); _attributes.clear(); - _loader=null; + _loader = null; } /* ------------------------------------------------------------ */ - public HttpSession getHttpSession(String id) - { + public HttpSession getHttpSession(String id) { return _sessionManager.getHttpSession(id); } - + /* ------------------------------------------------------------ */ - public HttpSession newHttpSession(HttpServletRequest request) - { + public HttpSession newHttpSession(HttpServletRequest request) { return _sessionManager.newHttpSession(request); } /* ------------------------------------------------------------ */ + /** * Set the session timeout interval in seconds. + * * @param seconds the length of the session timeout interval in seconds. */ - public void setSessionInactiveInterval(int seconds) - { + public void setSessionInactiveInterval(int seconds) { _sessionManager.setMaxInactiveInterval(seconds); } /* ----------------------------------------------------------------- */ - /** Handle request. + + /** + * Handle request. + * * @param pathInContext * @param pathParams * @param httpRequest - * @param httpResponse - * @exception IOException + * @param httpResponse + * @throws IOException */ public void handle(String pathInContext, String pathParams, HttpRequest httpRequest, HttpResponse httpResponse) - throws IOException - { + throws IOException { if (!isStarted()) return; - + // Handle TRACE - if (HttpRequest.__TRACE.equals(httpRequest.getMethod())) - { - handleTrace(httpRequest,httpResponse); + if (HttpRequest.__TRACE.equals(httpRequest.getMethod())) { + handleTrace(httpRequest, httpResponse); return; } // Look for existing request/response objects (from enterScope call) ServletHttpRequest request = (ServletHttpRequest) httpRequest.getWrapper(); ServletHttpResponse response = (ServletHttpResponse) httpResponse.getWrapper(); - if (request==null) - { + if (request == null) { // Not in ServletHttpContext, but bumble on anyway - request = new ServletHttpRequest(this,pathInContext,httpRequest); - response = new ServletHttpResponse(request,httpResponse); + request = new ServletHttpRequest(this, pathInContext, httpRequest); + response = new ServletHttpResponse(request, httpResponse); httpRequest.setWrapper(request); httpResponse.setWrapper(response); - } - else - { - request.recycle(this,pathInContext); + } else { + request.recycle(this, pathInContext); response.recycle(); } - + // Look for the servlet - Map.Entry servlet=getHolderEntry(pathInContext); - ServletHolder servletHolder=servlet==null?null:(ServletHolder)servlet.getValue(); - if(log.isDebugEnabled())log.debug("servlet="+servlet); - - try - { + Map.Entry servlet = getHolderEntry(pathInContext); + ServletHolder servletHolder = servlet == null ? null : (ServletHolder) servlet.getValue(); + if (log.isDebugEnabled()) log.debug("servlet=" + servlet); + + try { // Adjust request paths - if (servlet!=null) - { - String servletPathSpec=(String)servlet.getKey(); - request.setServletPaths(PathMap.pathMatch(servletPathSpec,pathInContext), - PathMap.pathInfo(servletPathSpec,pathInContext), - servletHolder); + if (servlet != null) { + String servletPathSpec = (String) servlet.getKey(); + request.setServletPaths(PathMap.pathMatch(servletPathSpec, pathInContext), + PathMap.pathInfo(servletPathSpec, pathInContext), + servletHolder); } - + // Handle the session ID request.setRequestedSessionId(pathParams); - HttpSession session=request.getSession(false); - if (session!=null) - ((SessionManager.Session)session).access(); - if(log.isDebugEnabled())log.debug("session="+session); - + HttpSession session = request.getSession(false); + if (session != null) + ((SessionManager.Session) session).access(); + if (log.isDebugEnabled()) log.debug("session=" + session); + // Do that funky filter and servlet thang! - if (servletHolder!=null) - dispatch(pathInContext,request,response,servletHolder, Dispatcher.__REQUEST); - } - catch(Exception e) - { - log.debug(LogSupport.EXCEPTION,e); - - Throwable th=e; - while (th instanceof ServletException) - { + if (servletHolder != null) + dispatch(pathInContext, request, response, servletHolder, Dispatcher.__REQUEST); + } catch (Exception e) { + log.debug(LogSupport.EXCEPTION, e); + + Throwable th = e; + while (th instanceof ServletException) { log.warn(LogSupport.EXCEPTION, th); - Throwable cause=((ServletException)th).getRootCause(); - if (cause==th || cause==null) + Throwable cause = ((ServletException) th).getRootCause(); + if (cause == th || cause == null) break; - th=cause; + th = cause; } - + if (th instanceof HttpException) - throw (HttpException)th; + throw (HttpException) th; if (th instanceof EOFException) - throw (IOException)th; - else if (log.isDebugEnabled() || !( th instanceof java.io.IOException)) - { - if (_contextLog!=null) - { - if (th instanceof RuntimeException) - _contextLog.error(httpRequest.getURI()+": ",th); + throw (IOException) th; + else if (log.isDebugEnabled() || !(th instanceof java.io.IOException)) { + if (_contextLog != null) { + if (th instanceof RuntimeException) + _contextLog.error(httpRequest.getURI() + ": ", th); else - _contextLog.warn(httpRequest.getURI()+": ",th); + _contextLog.warn(httpRequest.getURI() + ": ", th); } - - if(log.isDebugEnabled()) - { - if (th instanceof RuntimeException) - log.error(httpRequest.getURI()+": ",th); + + if (log.isDebugEnabled()) { + if (th instanceof RuntimeException) + log.error(httpRequest.getURI() + ": ", th); else - log.warn(httpRequest.getURI()+": ",th); + log.warn(httpRequest.getURI() + ": ", th); log.debug(httpRequest); } } - + httpResponse.getHttpConnection().forceClose(); - if (!httpResponse.isCommitted()) - { - request.setAttribute(ServletHandler.__J_S_ERROR_EXCEPTION_TYPE,th.getClass()); - request.setAttribute(ServletHandler.__J_S_ERROR_EXCEPTION,th); - if (th instanceof UnavailableException) - { - UnavailableException ue = (UnavailableException)th; + if (!httpResponse.isCommitted()) { + request.setAttribute(ServletHandler.__J_S_ERROR_EXCEPTION_TYPE, th.getClass()); + request.setAttribute(ServletHandler.__J_S_ERROR_EXCEPTION, th); + if (th instanceof UnavailableException) { + UnavailableException ue = (UnavailableException) th; if (ue.isPermanent()) - response.sendError(HttpResponse.__404_Not_Found,e.getMessage()); + response.sendError(HttpResponse.__404_Not_Found, e.getMessage()); else - response.sendError(HttpResponse.__503_Service_Unavailable,e.getMessage()); - } - else - response.sendError(HttpResponse.__500_Internal_Server_Error,e.getMessage()); - } - else - if(log.isDebugEnabled())log.debug("Response already committed for handling "+th); - } - catch(Error e) - { - log.warn("Error for "+httpRequest.getURI(),e); - if(log.isDebugEnabled())log.debug(httpRequest); - + response.sendError(HttpResponse.__503_Service_Unavailable, e.getMessage()); + } else + response.sendError(HttpResponse.__500_Internal_Server_Error, e.getMessage()); + } else if (log.isDebugEnabled()) log.debug("Response already committed for handling " + th); + } catch (Error e) { + log.warn("Error for " + httpRequest.getURI(), e); + if (log.isDebugEnabled()) log.debug(httpRequest); + httpResponse.getHttpConnection().forceClose(); - if (!httpResponse.isCommitted()) - { - request.setAttribute(ServletHandler.__J_S_ERROR_EXCEPTION_TYPE,e.getClass()); - request.setAttribute(ServletHandler.__J_S_ERROR_EXCEPTION,e); + if (!httpResponse.isCommitted()) { + request.setAttribute(ServletHandler.__J_S_ERROR_EXCEPTION_TYPE, e.getClass()); + request.setAttribute(ServletHandler.__J_S_ERROR_EXCEPTION, e); response.sendError(HttpResponse.__500_Internal_Server_Error, - e.getMessage()); - } - else - if(log.isDebugEnabled())log.debug("Response already committed for handling ",e); - } - finally - { - if (servletHolder!=null && response!=null) - { + e.getMessage()); + } else if (log.isDebugEnabled()) log.debug("Response already committed for handling ", e); + } finally { + if (servletHolder != null && response != null) { response.complete(); } } } /* ------------------------------------------------------------ */ - /** Dispatch to a servletHolder. + + /** + * Dispatch to a servletHolder. * This method may be specialized to insert extra handling in the * dispatch of a request to a specific servlet. This is used by * WebApplicatonHandler to implement dispatched filters. * The default implementation simply calls * ServletHolder.handle(request,response) + * * @param pathInContext The path used to select the servlet holder. - * @param request - * @param response - * @param servletHolder - * @param type the type of dispatch as defined in the Dispatcher class. - * @exception ServletException - * @exception UnavailableException - * @exception IOException + * @param request + * @param response + * @param servletHolder + * @param type the type of dispatch as defined in the Dispatcher class. + * @throws ServletException + * @throws IOException */ protected void dispatch(String pathInContext, HttpServletRequest request, HttpServletResponse response, - ServletHolder servletHolder, + ServletHolder servletHolder, int type) - throws ServletException, - UnavailableException, - IOException - { - servletHolder.handle(request,response); + throws ServletException, + IOException { + servletHolder.handle(request, response); } - - + + /* ------------------------------------------------------------ */ - /** ServletHolder matching path. + + /** + * ServletHolder matching path. + * * @param pathInContext Path within context. * @return PathMap Entries pathspec to ServletHolder */ - public Map.Entry getHolderEntry(String pathInContext) - { + public Map.Entry getHolderEntry(String pathInContext) { return _servletMap.getMatch(pathInContext); } - + /* ------------------------------------------------------------ */ - public Set getResourcePaths(String uriInContext) - { - try - { - uriInContext=URI.canonicalPath(uriInContext); - if (uriInContext==null) + public Set getResourcePaths(String uriInContext) { + try { + uriInContext = URI.canonicalPath(uriInContext); + if (uriInContext == null) return Collections.EMPTY_SET; - Resource resource=getHttpContext().getResource(uriInContext); - if (resource==null || !resource.isDirectory()) + Resource resource = getHttpContext().getResource(uriInContext); + if (resource == null || !resource.isDirectory()) return Collections.EMPTY_SET; - String[] contents=resource.list(); - if (contents==null || contents.length==0) + String[] contents = resource.list(); + if (contents == null || contents.length == 0) return Collections.EMPTY_SET; - HashSet set = new HashSet(contents.length*2); - for (int i=0;i0) - { - query=uriInContext.substring(q+1); - uriInContext=uriInContext.substring(0,q); + + try { + String query = null; + int q = 0; + if ((q = uriInContext.indexOf('?')) > 0) { + query = uriInContext.substring(q + 1); + uriInContext = uriInContext.substring(0, q); } - if ((q=uriInContext.indexOf(';'))>0) - uriInContext=uriInContext.substring(0,q); + if ((q = uriInContext.indexOf(';')) > 0) + uriInContext = uriInContext.substring(0, q); - String pathInContext=URI.canonicalPath(URI.decodePath(uriInContext)); - Map.Entry entry=getHolderEntry(pathInContext); - if (entry!=null) + String pathInContext = URI.canonicalPath(URI.decodePath(uriInContext)); + Map.Entry entry = getHolderEntry(pathInContext); + if (entry != null) return new Dispatcher(ServletHandler.this, - uriInContext, - pathInContext, - query, - entry); - } - catch(Exception e) - { - LogSupport.ignore(log,e); + uriInContext, + pathInContext, + query, + entry); + } catch (Exception e) { + LogSupport.ignore(log, e); } return null; } /* ------------------------------------------------------------ */ - /** Get Named dispatcher. + + /** + * Get Named dispatcher. + * * @param name The name of the servlet. If null or empty string, the - * containers default servlet is returned. + * containers default servlet is returned. * @return Request dispatcher for the named servlet. */ - public RequestDispatcher getNamedDispatcher(String name) - { - if (name == null || name.length()==0) - name=__DEFAULT_SERVLET; - - try { return new Dispatcher(ServletHandler.this,name); } - catch(Exception e) {LogSupport.ignore(log,e);} - + public RequestDispatcher getNamedDispatcher(String name) { + if (name == null || name.length() == 0) + name = __DEFAULT_SERVLET; + + try { + return new Dispatcher(ServletHandler.this, name); + } catch (Exception e) { + LogSupport.ignore(log, e); + } + return null; } - /* ------------------------------------------------------------ */ protected void notFound(HttpServletRequest request, - HttpServletResponse response) - throws IOException - { - if(log.isDebugEnabled())log.debug("Not Found "+request.getRequestURI()); - String method=request.getMethod(); - + HttpServletResponse response) + throws IOException { + if (log.isDebugEnabled()) log.debug("Not Found " + request.getRequestURI()); + String method = request.getMethod(); + // Not found special requests. - if (method.equals(HttpRequest.__GET) || - method.equals(HttpRequest.__HEAD) || - method.equals(HttpRequest.__POST)) - { + if (method.equals(HttpRequest.__GET) || + method.equals(HttpRequest.__HEAD) || + method.equals(HttpRequest.__POST)) { response.sendError(HttpResponse.__404_Not_Found); - } - else if (method.equals(HttpRequest.__TRACE)) - handleTrace(request,response); + } else if (method.equals(HttpRequest.__TRACE)) + handleTrace(request, response); else if (method.equals(HttpRequest.__OPTIONS)) - handleOptions(request,response); - else - { + handleOptions(request, response); + else { // Unknown METHOD - response.setHeader(HttpFields.__Allow,__AllowString); + response.setHeader(HttpFields.__Allow, __AllowString); response.sendError(HttpResponse.__405_Method_Not_Allowed); } } - + /* ------------------------------------------------------------ */ protected void handleTrace(HttpServletRequest request, - HttpServletResponse response) - throws IOException - { + HttpServletResponse response) + throws IOException { response.setHeader(HttpFields.__ContentType, - HttpFields.__MessageHttp); - OutputStream out = response.getOutputStream(); - ByteArrayISO8859Writer writer = new ByteArrayISO8859Writer(); - writer.write(request.toString()); - writer.flush(); - response.setIntHeader(HttpFields.__ContentLength,writer.size()); - writer.writeTo(out); - out.flush(); + HttpFields.__MessageHttp); + try (ByteArrayISO8859Writer writer = new ByteArrayISO8859Writer(); + OutputStream out = response.getOutputStream()) { + writer.write(request.toString()); + writer.flush(); + response.setIntHeader(HttpFields.__ContentLength, writer.size()); + writer.writeTo(out); + out.flush(); + } } - + /* ------------------------------------------------------------ */ protected void handleOptions(HttpServletRequest request, - HttpServletResponse response) - throws IOException - { + HttpServletResponse response) + throws IOException { // Handle OPTIONS request for entire server - if ("*".equals(request.getRequestURI())) - { + if ("*".equals(request.getRequestURI())) { // 9.2 - response.setIntHeader(HttpFields.__ContentLength,0); - response.setHeader(HttpFields.__Allow,__AllowString); + response.setIntHeader(HttpFields.__ContentLength, 0); + response.setHeader(HttpFields.__Allow, __AllowString); response.flushBuffer(); - } - else + } else response.sendError(HttpResponse.__404_Not_Found); } /* ------------------------------------------------------------ */ - public String getErrorPage(int status,ServletHttpRequest request) - { + public String getErrorPage(int status, ServletHttpRequest request) { return null; } - + /* ------------------------------------------------------------ */ - /** Get context attribute. + + /** + * Get context attribute. * Tries ServletHandler attributes and then delegated to HttpContext. + * * @param name attribute name. * @return attribute */ - protected Object getContextAttribute(String name) - { - if (ServletHandler.__J_S_CONTEXT_TEMPDIR.equals(name)) - { + protected Object getContextAttribute(String name) { + if (ServletHandler.__J_S_CONTEXT_TEMPDIR.equals(name)) { // Initialize temporary directory Object t = getHttpContext().getAttribute(ServletHandler.__J_S_CONTEXT_TEMPDIR); if (t instanceof File) - return (File)t; - + return t; + return getHttpContext().getTempDirectory(); } @@ -930,16 +894,17 @@ protected Object getContextAttribute(String name) } /* ------------------------------------------------------------ */ - /** Get context attribute names. + + /** + * Get context attribute names. * Combines ServletHandler and HttpContext attributes. */ - protected Enumeration getContextAttributeNames() - { - if (_attributes.size()==0) + protected Enumeration getContextAttributeNames() { + if (_attributes.size() == 0) return getHttpContext().getAttributeNames(); - HashSet set=new HashSet(_attributes.keySet()); - Enumeration e=getHttpContext().getAttributeNames(); - while(e.hasMoreElements()) + HashSet set = new HashSet(_attributes.keySet()); + Enumeration e = getHttpContext().getAttributeNames(); + while (e.hasMoreElements()) set.add(e.nextElement()); return Collections.enumeration(set); } @@ -948,290 +913,450 @@ protected Enumeration getContextAttributeNames() /* Set a Servlet context attribute. * Servlet Context attributes may hide HttpContext attributes. */ - protected void setContextAttribute(String name, Object value) - { - _attributes.put(name,value); + protected void setContextAttribute(String name, Object value) { + _attributes.put(name, value); } - + /* ------------------------------------------------------------ */ /* Remove a Servlet context attribute. * Servlet Context attributes may hide HttpContext attributes. */ - protected void removeContextAttribute(String name) - { + protected void removeContextAttribute(String name) { _attributes.remove(name); } - + /* ----------------------------------------------------------------- */ public void handleTrace(HttpRequest request, HttpResponse response) - throws IOException - { - boolean trace=getHttpContext().getHttpServer().getTrace(); - + throws IOException { + boolean trace = getHttpContext().getHttpServer().getTrace(); + // Handle TRACE by returning request header response.setField(HttpFields.__ContentType, - HttpFields.__MessageHttp); - if (trace) - { + HttpFields.__MessageHttp); + if (trace) { OutputStream out = response.getOutputStream(); ByteArrayISO8859Writer writer = new ByteArrayISO8859Writer(); writer.write(request.toString()); writer.flush(); - response.setIntField(HttpFields.__ContentLength,writer.size()); + response.setIntField(HttpFields.__ContentLength, writer.size()); writer.writeTo(out); out.flush(); } request.setHandled(true); } - + /* ----------------------------------------------------------------- */ - public void destroy() - { + public void destroy() { Iterator iter = _nameMap.values().iterator(); - while (iter.hasNext()) - { - Object sh=iter.next(); + while (iter.hasNext()) { + Object sh = iter.next(); iter.remove(); removeComponent(sh); } - - if (_sessionManager!=null) + + if (_sessionManager != null) removeComponent(_sessionManager); - _sessionManager=null; - _context=null; + _sessionManager = null; + _context = null; super.destroy(); } - + /* ----------------------------------------------------------------- */ - protected void finalize() throws Throwable - { + protected void finalize() throws Throwable { destroy(); } - + /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ - class Context implements ServletContext - { + class Context implements ServletContext { @Override public String getContextPath() { return null; } /* -------------------------------------------------------- */ - ServletHandler getServletHandler() - { + ServletHandler getServletHandler() { return ServletHandler.this; } - + /* -------------------------------------------------------- */ - public ServletContext getContext(String uri) - { - ServletHandler handler= (ServletHandler) - getHttpContext().getHttpServer() - .findHandler(net.lightbody.bmp.proxy.jetty.jetty.servlet.ServletHandler.class, - uri, - getHttpContext().getVirtualHosts()); - if (handler!=null) + public ServletContext getContext(String uri) { + ServletHandler handler = (ServletHandler) + getHttpContext().getHttpServer() + .findHandler(net.lightbody.bmp.proxy.jetty.jetty.servlet.ServletHandler.class, + uri, + getHttpContext().getVirtualHosts()); + if (handler != null) return handler.getServletContext(); return null; } /* ------------------------------------------------------------ */ - public int getMajorVersion() - { + public int getMajorVersion() { return 2; } /* ------------------------------------------------------------ */ - public int getMinorVersion() - { + public int getMinorVersion() { return 4; } + @Override + public int getEffectiveMajorVersion() { + return 0; + } + + @Override + public int getEffectiveMinorVersion() { + return 0; + } + /* ------------------------------------------------------------ */ - public String getMimeType(String file) - { + public String getMimeType(String file) { return getHttpContext().getMimeByExtension(file); } /* ------------------------------------------------------------ */ - public Set getResourcePaths(String uriInContext) - { + public Set getResourcePaths(String uriInContext) { return ServletHandler.this.getResourcePaths(uriInContext); } /* ------------------------------------------------------------ */ public URL getResource(String uriInContext) - throws MalformedURLException - { + throws MalformedURLException { return ServletHandler.this.getResource(uriInContext); } /* ------------------------------------------------------------ */ - public InputStream getResourceAsStream(String uriInContext) - { + public InputStream getResourceAsStream(String uriInContext) { return ServletHandler.this.getResourceAsStream(uriInContext); } /* ------------------------------------------------------------ */ - public String getRealPath(String path) - { + public String getRealPath(String path) { return ServletHandler.this.getRealPath(path); } /* ------------------------------------------------------------ */ - public RequestDispatcher getRequestDispatcher(String uriInContext) - { + public RequestDispatcher getRequestDispatcher(String uriInContext) { return ServletHandler.this.getRequestDispatcher(uriInContext); } /* ------------------------------------------------------------ */ - public RequestDispatcher getNamedDispatcher(String name) - { + public RequestDispatcher getNamedDispatcher(String name) { return ServletHandler.this.getNamedDispatcher(name); } - + /* ------------------------------------------------------------ */ + /** - * @deprecated + * @deprecated */ - public Servlet getServlet(String name) - { + public Servlet getServlet(String name) { return null; } /* ------------------------------------------------------------ */ + /** - * @deprecated + * @deprecated */ - public Enumeration getServlets() - { + public Enumeration getServlets() { return Collections.enumeration(Collections.EMPTY_LIST); } /* ------------------------------------------------------------ */ + /** - * @deprecated + * @deprecated */ - public Enumeration getServletNames() - { + public Enumeration getServletNames() { return Collections.enumeration(Collections.EMPTY_LIST); } - + /* ------------------------------------------------------------ */ - /** Servlet Log. + + /** + * Servlet Log. * Log message to servlet log. Use either the system log or a * LogSinkset via the context attribute * org.mortbay.jetty.servlet.Context.LogSink - * @param msg + * + * @param msg */ - public void log(String msg) - { + public void log(String msg) { _contextLog.info(msg); } /* ------------------------------------------------------------ */ + /** * @deprecated As of Java Servlet API 2.1, use - * {@link #log(String message, Throwable throwable)} - * instead. + * {@link #log(String message, Throwable throwable)} + * instead. */ - public void log(Exception e, String msg) - { - _contextLog.warn(msg,e); + public void log(Exception e, String msg) { + _contextLog.warn(msg, e); } /* ------------------------------------------------------------ */ - public void log(String msg, Throwable th) - { - _contextLog.warn(msg,th); + public void log(String msg, Throwable th) { + _contextLog.warn(msg, th); } /* ------------------------------------------------------------ */ - public String getServerInfo() - { + public String getServerInfo() { return Version.getImplVersion(); } /* ------------------------------------------------------------ */ - /** Get context init parameter. + + /** + * Get context init parameter. * Delegated to HttpContext. + * * @param param param name * @return param value or null */ - public String getInitParameter(String param) - { + public String getInitParameter(String param) { return getHttpContext().getInitParameter(param); } /* ------------------------------------------------------------ */ - /** Get context init parameter names. + + /** + * Get context init parameter names. * Delegated to HttpContext. + * * @return Enumeration of names */ - public Enumeration getInitParameterNames() - { + public Enumeration getInitParameterNames() { return getHttpContext().getInitParameterNames(); } - + @Override + public boolean setInitParameter(String s, String s1) { + return false; + } + + /* ------------------------------------------------------------ */ - /** Get context attribute. + + /** + * Get context attribute. * Tries ServletHandler attributes and then delegated to HttpContext. + * * @param name attribute name. * @return attribute */ - public Object getAttribute(String name) - { + public Object getAttribute(String name) { return getContextAttribute(name); } /* ------------------------------------------------------------ */ - /** Get context attribute names. + + /** + * Get context attribute names. * Combines ServletHandler and HttpContext attributes. */ - public Enumeration getAttributeNames() - { + public Enumeration getAttributeNames() { return getContextAttributeNames(); } /* ------------------------------------------------------------ */ - /** Set context attribute names. + + /** + * Set context attribute names. * Sets the ServletHandler attributes and may hide HttpContext attributes. - * @param name attribute name. + * + * @param name attribute name. * @param value attribute value */ - public void setAttribute(String name, Object value) - { + public void setAttribute(String name, Object value) { setContextAttribute(name, value); } /* ------------------------------------------------------------ */ - /** Remove context attribute. + + /** + * Remove context attribute. * Puts a null into the ServletHandler attributes and may hide a HttpContext attribute. + * * @param name attribute name. */ - public void removeAttribute(String name) - { + public void removeAttribute(String name) { removeContextAttribute(name); } - + /* ------------------------------------------------------------ */ - public String getServletContextName() - { + public String getServletContextName() { if (getHttpContext() instanceof WebApplicationContext) - return ((WebApplicationContext)getHttpContext()).getDisplayName(); + return ((WebApplicationContext) getHttpContext()).getDisplayName(); + return null; + } + + @Override + public ServletRegistration.Dynamic addServlet(String s, String s1) { + return null; + } + + @Override + public ServletRegistration.Dynamic addServlet(String s, Servlet servlet) { + return null; + } + + @Override + public ServletRegistration.Dynamic addServlet(String s, Class aClass) { + return null; + } + + @Override + public ServletRegistration.Dynamic addJspFile(String s, String s1) { + return null; + } + + @Override + public T createServlet(Class aClass) throws ServletException { + return null; + } + + @Override + public ServletRegistration getServletRegistration(String s) { + return null; + } + + @Override + public Map getServletRegistrations() { + return null; + } + + @Override + public FilterRegistration.Dynamic addFilter(String s, String s1) { + return null; + } + + @Override + public FilterRegistration.Dynamic addFilter(String s, Filter filter) { + return null; + } + + @Override + public FilterRegistration.Dynamic addFilter(String s, Class aClass) { return null; } + @Override + public T createFilter(Class aClass) throws ServletException { + return null; + } + + @Override + public FilterRegistration getFilterRegistration(String s) { + return null; + } + + @Override + public Map getFilterRegistrations() { + return null; + } + + @Override + public SessionCookieConfig getSessionCookieConfig() { + return null; + } + + @Override + public void setSessionTrackingModes(Set set) { + + } + + @Override + public Set getDefaultSessionTrackingModes() { + return null; + } + + @Override + public Set getEffectiveSessionTrackingModes() { + return null; + } + + @Override + public void addListener(String s) { + + } + + @Override + public void addListener(T t) { + + } + + @Override + public void addListener(Class aClass) { + + } + + @Override + public T createListener(Class aClass) throws ServletException { + return null; + } + + @Override + public JspConfigDescriptor getJspConfigDescriptor() { + return null; + } + + @Override + public ClassLoader getClassLoader() { + return null; + } + + @Override + public void declareRoles(String... strings) { + + } + + @Override + public String getVirtualServerName() { + return null; + } + + @Override + public int getSessionTimeout() { + return 0; + } + + @Override + public void setSessionTimeout(int i) { + + } + + @Override + public String getRequestCharacterEncoding() { + return null; + } + + @Override + public void setRequestCharacterEncoding(String s) { + + } + + @Override + public String getResponseCharacterEncoding() { + return null; + } + + @Override + public void setResponseCharacterEncoding(String s) { + + } + /* ------------------------------------------------------------ */ - public String toString() - { - return "ServletContext["+getHttpContext()+"]"; + public String toString() { + return "ServletContext[" + getHttpContext() + "]"; } - } + } } diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHttpRequest.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHttpRequest.java index 97b2f45aa..9a6cc4f02 100644 --- a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHttpRequest.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHttpRequest.java @@ -25,491 +25,437 @@ import net.lightbody.bmp.proxy.jetty.util.*; import org.apache.commons.logging.Log; -import javax.servlet.RequestDispatcher; -import javax.servlet.ServletInputStream; -import javax.servlet.ServletRequest; -import javax.servlet.ServletRequestWrapper; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; -import java.io.BufferedReader; -import java.io.File; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; +import javax.servlet.*; +import javax.servlet.http.*; +import java.io.*; import java.security.Principal; import java.util.*; /* ------------------------------------------------------------ */ -/** Servlet Request Wrapper. + +/** + * Servlet Request Wrapper. * This class wraps a Jetty HTTP request as a 2.2 Servlet * request. - *

+ *

* Note that this wrapper is not synchronized and if a request is to * be operated on by multiple threads, then higher level * synchronizations may be required. - * - * @version $Id: ServletHttpRequest.java,v 1.65 2005/08/13 00:01:27 gregwilkins Exp $ + * * @author Greg Wilkins (gregw) + * @version $Id: ServletHttpRequest.java,v 1.65 2005/08/13 00:01:27 gregwilkins Exp $ */ public class ServletHttpRequest - implements HttpServletRequest -{ + implements HttpServletRequest { private static Log log = LogFactory.getLog(ServletHttpRequest.class); /* -------------------------------------------------------------- */ public static final String - __SESSIONID_NOT_CHECKED = "not checked", - __SESSIONID_URL = "url", - __SESSIONID_COOKIE = "cookie", - __SESSIONID_NONE = "none"; + __SESSIONID_NOT_CHECKED = "not checked", + __SESSIONID_URL = "url", + __SESSIONID_COOKIE = "cookie", + __SESSIONID_NONE = "none"; - private static final Enumeration __emptyEnum = - Collections.enumeration(Collections.EMPTY_LIST); + private static final Enumeration __emptyEnum = + Collections.enumeration(Collections.EMPTY_LIST); private static final Collection __defaultLocale = - Collections.singleton(Locale.getDefault()); + Collections.singleton(Locale.getDefault()); - private ServletHandler _servletHandler; + private ServletHandler _servletHandler; private HttpRequest _httpRequest; private ServletHttpResponse _servletHttpResponse; - private String _contextPath=null; - private String _servletPath=null; - private String _pathInfo=null; - private String _query=null; - private String _pathTranslated=null; - private String _requestedSessionId=null; - private HttpSession _session=null; - private String _sessionIdState=__SESSIONID_NOT_CHECKED; - private ServletIn _in =null; - private BufferedReader _reader=null; - private int _inputState=0; + private String _contextPath = null; + private String _servletPath = null; + private String _pathInfo = null; + private String _query = null; + private String _pathTranslated = null; + private String _requestedSessionId = null; + private HttpSession _session = null; + private String _sessionIdState = __SESSIONID_NOT_CHECKED; + private ServletIn _in = null; + private BufferedReader _reader = null; + private int _inputState = 0; private ServletHolder _servletHolder; private String _pathInContext; - + /* ------------------------------------------------------------ */ - /** Constructor. + + /** + * Constructor. */ public ServletHttpRequest(ServletHandler servletHandler, - String pathInContext, - HttpRequest request) - { - _servletHandler=servletHandler; - _pathInContext=pathInContext; - _contextPath=_servletHandler.getHttpContext().getContextPath(); - if (_contextPath.length()<=1) - _contextPath=""; - - _httpRequest=request; - } - - /* ------------------------------------------------------------ */ - void recycle(ServletHandler servletHandler,String pathInContext) - { - _servletHandler=servletHandler; - _pathInContext=pathInContext; - _servletPath=null; - _pathInfo=null; - _query=null; - _pathTranslated=null; - _requestedSessionId=null; - _session=null; - _sessionIdState=__SESSIONID_NOT_CHECKED; - _in=null; - _reader=null; - _inputState=0; - _servletHolder=null; - - if (servletHandler!=null) - _contextPath=_servletHandler.getHttpContext().getContextPath(); - if (_contextPath!=null&&_contextPath.length()<=1) - _contextPath=""; - } - - - /* ------------------------------------------------------------ */ - ServletHandler getServletHandler() - { + String pathInContext, + HttpRequest request) { + _servletHandler = servletHandler; + _pathInContext = pathInContext; + _contextPath = _servletHandler.getHttpContext().getContextPath(); + if (_contextPath.length() <= 1) + _contextPath = ""; + + _httpRequest = request; + } + + /* ------------------------------------------------------------ */ + void recycle(ServletHandler servletHandler, String pathInContext) { + _servletHandler = servletHandler; + _pathInContext = pathInContext; + _servletPath = null; + _pathInfo = null; + _query = null; + _pathTranslated = null; + _requestedSessionId = null; + _session = null; + _sessionIdState = __SESSIONID_NOT_CHECKED; + _in = null; + _reader = null; + _inputState = 0; + _servletHolder = null; + + if (servletHandler != null) + _contextPath = _servletHandler.getHttpContext().getContextPath(); + if (_contextPath != null && _contextPath.length() <= 1) + _contextPath = ""; + } + + + /* ------------------------------------------------------------ */ + ServletHandler getServletHandler() { return _servletHandler; } /* ------------------------------------------------------------ */ - void setServletHandler(ServletHandler servletHandler) - { - _servletHandler=servletHandler; + void setServletHandler(ServletHandler servletHandler) { + _servletHandler = servletHandler; } - + /* ------------------------------------------------------------ */ - /** Set servletpath and pathInfo. + + /** + * Set servletpath and pathInfo. * Called by the Handler before passing a request to a particular * holder to split the context path into a servlet path and path info. - * @param servletPath - * @param pathInfo + * + * @param servletPath + * @param pathInfo */ void setServletPaths(String servletPath, String pathInfo, - ServletHolder holder) - { - _servletPath=servletPath; - _pathInfo=pathInfo; - _servletHolder=holder; + ServletHolder holder) { + _servletPath = servletPath; + _pathInfo = pathInfo; + _servletHolder = holder; } - + /* ------------------------------------------------------------ */ - ServletHolder getServletHolder() - { + ServletHolder getServletHolder() { return _servletHolder; } - + /* ------------------------------------------------------------ */ - String getPathInContext() - { + String getPathInContext() { return _pathInContext; } - + /* ------------------------------------------------------------ */ - HttpRequest getHttpRequest() - { + HttpRequest getHttpRequest() { return _httpRequest; } - + /* ------------------------------------------------------------ */ - public ServletHttpResponse getServletHttpResponse() - { + public ServletHttpResponse getServletHttpResponse() { return _servletHttpResponse; } - + /* ------------------------------------------------------------ */ - void setServletHttpResponse(ServletHttpResponse response) - { + void setServletHttpResponse(ServletHttpResponse response) { _servletHttpResponse = response; } /* ------------------------------------------------------------ */ - public Locale getLocale() - { + public Locale getLocale() { Enumeration enm = _httpRequest.getFieldValues(HttpFields.__AcceptLanguage, - HttpFields.__separators); + HttpFields.__separators); // handle no locale if (enm == null || !enm.hasMoreElements()) return Locale.getDefault(); - + // sort the list in quality order List acceptLanguage = HttpFields.qualityList(enm); - if (acceptLanguage.size()==0) - return Locale.getDefault(); + if (acceptLanguage.size() == 0) + return Locale.getDefault(); + + int size = acceptLanguage.size(); - int size=acceptLanguage.size(); - // convert to locals - for (int i=0; i -1) - { + if (dash > -1) { country = language.substring(dash + 1).trim(); - language = language.substring(0,dash).trim(); + language = language.substring(0, dash).trim(); } - return new Locale(language,country); + return new Locale(language, country); } - return Locale.getDefault(); + return Locale.getDefault(); } - + /* ------------------------------------------------------------ */ - public Enumeration getLocales() - { + public Enumeration getLocales() { Enumeration enm = _httpRequest.getFieldValues(HttpFields.__AcceptLanguage, - HttpFields.__separators); + HttpFields.__separators); // handle no locale if (enm == null || !enm.hasMoreElements()) return Collections.enumeration(__defaultLocale); - + // sort the list in quality order List acceptLanguage = HttpFields.qualityList(enm); - - if (acceptLanguage.size()==0) + + if (acceptLanguage.size() == 0) return - Collections.enumeration(__defaultLocale); + Collections.enumeration(__defaultLocale); Object langs = null; - int size=acceptLanguage.size(); - + int size = acceptLanguage.size(); + // convert to locals - for (int i=0; i -1) - { + if (dash > -1) { country = language.substring(dash + 1).trim(); - language = language.substring(0,dash).trim(); + language = language.substring(0, dash).trim(); } - langs=LazyList.ensureSize(langs,size); - langs=LazyList.add(langs,new Locale(language,country)); + langs = LazyList.ensureSize(langs, size); + langs = LazyList.add(langs, new Locale(language, country)); } - if (LazyList.size(langs)==0) + if (LazyList.size(langs) == 0) return Collections.enumeration(__defaultLocale); - + return Collections.enumeration(LazyList.getList(langs)); } - + /* ------------------------------------------------------------ */ - public boolean isSecure() - { + public boolean isSecure() { return _httpRequest.isConfidential(); } - + /* ------------------------------------------------------------ */ - public Cookie[] getCookies() - { + public Cookie[] getCookies() { Cookie[] cookies = _httpRequest.getCookies(); - if (cookies.length==0) + if (cookies.length == 0) return null; return cookies; } - + /* ------------------------------------------------------------ */ - public long getDateHeader(String name) - { + public long getDateHeader(String name) { return _httpRequest.getDateField(name); } - + /* ------------------------------------------------------------ */ - public Enumeration getHeaderNames() - { + public Enumeration getHeaderNames() { return _httpRequest.getFieldNames(); } - + /* ------------------------------------------------------------ */ - public String getHeader(String name) - { + public String getHeader(String name) { return _httpRequest.getField(name); } - + /* ------------------------------------------------------------ */ - public Enumeration getHeaders(String s) - { - Enumeration enm=_httpRequest.getFieldValues(s); - if (enm==null) + public Enumeration getHeaders(String s) { + Enumeration enm = _httpRequest.getFieldValues(s); + if (enm == null) return __emptyEnum; return enm; } - + /* ------------------------------------------------------------ */ public int getIntHeader(String name) - throws NumberFormatException - { + throws NumberFormatException { return _httpRequest.getIntField(name); } - + /* ------------------------------------------------------------ */ - public String getMethod() - { + public String getMethod() { return _httpRequest.getMethod(); } - + /* ------------------------------------------------------------ */ - public String getContextPath() - { + public String getContextPath() { return _contextPath; } - + /* ------------------------------------------------------------ */ - public String getPathInfo() - { - if (_servletPath==null) - return null; + public String getPathInfo() { + if (_servletPath == null) + return null; return _pathInfo; } - + /* ------------------------------------------------------------ */ - public String getPathTranslated() - { - if (_pathInfo==null || _pathInfo.length()==0) + public String getPathTranslated() { + if (_pathInfo == null || _pathInfo.length() == 0) return null; - if (_pathTranslated==null) - { + if (_pathTranslated == null) { Resource resource = - _servletHandler.getHttpContext().getBaseResource(); + _servletHandler.getHttpContext().getBaseResource(); - if (resource==null) + if (resource == null) return null; - try - { - resource=resource.addPath(_pathInfo); + try { + resource = resource.addPath(_pathInfo); File file = resource.getFile(); - if (file==null) + if (file == null) return null; - _pathTranslated=file.getAbsolutePath(); - } - catch(Exception e) - { - log.debug(LogSupport.EXCEPTION,e); + _pathTranslated = file.getAbsolutePath(); + } catch (Exception e) { + log.debug(LogSupport.EXCEPTION, e); } } - + return _pathTranslated; } - + /* ------------------------------------------------------------ */ - public String getQueryString() - { - if (_query==null) - _query =_httpRequest.getQuery(); + public String getQueryString() { + if (_query == null) + _query = _httpRequest.getQuery(); return _query; } - + /* ------------------------------------------------------------ */ - public String getAuthType() - { - String at= _httpRequest.getAuthType(); - if (at== SecurityConstraint.__BASIC_AUTH) + public String getAuthType() { + String at = _httpRequest.getAuthType(); + if (at == SecurityConstraint.__BASIC_AUTH) return HttpServletRequest.BASIC_AUTH; - if (at==SecurityConstraint.__FORM_AUTH) + if (at == SecurityConstraint.__FORM_AUTH) return HttpServletRequest.FORM_AUTH; - if (at==SecurityConstraint.__DIGEST_AUTH) + if (at == SecurityConstraint.__DIGEST_AUTH) return HttpServletRequest.DIGEST_AUTH; - if (at==SecurityConstraint.__CERT_AUTH) + if (at == SecurityConstraint.__CERT_AUTH) return HttpServletRequest.CLIENT_CERT_AUTH; - if (at==SecurityConstraint.__CERT_AUTH2) + if (at == SecurityConstraint.__CERT_AUTH2) return HttpServletRequest.CLIENT_CERT_AUTH; return at; } /* ------------------------------------------------------------ */ - public String getRemoteUser() - { + public String getRemoteUser() { return _httpRequest.getAuthUser(); } /* ------------------------------------------------------------ */ - public boolean isUserInRole(String role) - { - if (_servletHolder!=null) - role=_servletHolder.getUserRoleLink(role); + public boolean isUserInRole(String role) { + if (_servletHolder != null) + role = _servletHolder.getUserRoleLink(role); return _httpRequest.isUserInRole(role); } /* ------------------------------------------------------------ */ - public Principal getUserPrincipal() - { + public Principal getUserPrincipal() { return _httpRequest.getUserPrincipal(); } - + /* ------------------------------------------------------------ */ - void setRequestedSessionId(String pathParams) - { - _requestedSessionId=null; - + void setRequestedSessionId(String pathParams) { + _requestedSessionId = null; + // try cookies first - if (_servletHandler.isUsingCookies()) - { - Cookie[] cookies=_httpRequest.getCookies(); - if (cookies!=null && cookies.length>0) - { - for (int i=0;i 0) { + for (int i = 0; i < cookies.length; i++) { + if (SessionManager.__SessionCookie.equalsIgnoreCase(cookies[i].getName())) { + if (_requestedSessionId != null) { // Multiple jsessionid cookies. Probably due to // multiple paths and/or domains. Pick the first // known session or the last defined cookie. SessionManager manager = _servletHandler.getSessionManager(); - if (manager!=null && manager.getHttpSession(_requestedSessionId)!=null) + if (manager != null && manager.getHttpSession(_requestedSessionId) != null) break; log.debug("multiple session cookies"); } - - _requestedSessionId=cookies[i].getValue(); + + _requestedSessionId = cookies[i].getValue(); _sessionIdState = __SESSIONID_COOKIE; - if(log.isDebugEnabled())log.debug("Got Session "+_requestedSessionId+" from cookie"); + if (log.isDebugEnabled()) log.debug("Got Session " + _requestedSessionId + " from cookie"); } } } } - + // check if there is a url encoded session param. - if (pathParams!=null && pathParams.startsWith(SessionManager.__SessionURL)) - { + if (pathParams != null && pathParams.startsWith(SessionManager.__SessionURL)) { String id = - pathParams.substring(SessionManager.__SessionURL.length()+1); - if(log.isDebugEnabled())log.debug("Got Session "+id+" from URL"); - - if (_requestedSessionId==null) - { - _requestedSessionId=id; + pathParams.substring(SessionManager.__SessionURL.length() + 1); + if (log.isDebugEnabled()) log.debug("Got Session " + id + " from URL"); + + if (_requestedSessionId == null) { + _requestedSessionId = id; _sessionIdState = __SESSIONID_URL; - } - else if (!id.equals(_requestedSessionId)) + } else if (!id.equals(_requestedSessionId)) log.debug("Mismatched session IDs"); } - + if (_requestedSessionId == null) - _sessionIdState = __SESSIONID_NONE; + _sessionIdState = __SESSIONID_NONE; } - + /* ------------------------------------------------------------ */ - public String getRequestedSessionId() - { + public String getRequestedSessionId() { return _requestedSessionId; } - + /* ------------------------------------------------------------ */ - public String getRequestURI() - { + public String getRequestURI() { return _httpRequest.getEncodedPath(); } - + /* ------------------------------------------------------------ */ - public StringBuffer getRequestURL() - { + public StringBuffer getRequestURL() { StringBuffer buf = _httpRequest.getRootURL(); buf.append(getRequestURI()); return buf; } - + /* ------------------------------------------------------------ */ - public String getServletPath() - { - if (_servletPath==null) + public String getServletPath() { + if (_servletPath == null) return _pathInContext; return _servletPath; } - + /* ------------------------------------------------------------ */ - public HttpSession getSession(boolean create) - { - if (_session != null && ((SessionManager.Session)_session).isValid()) + public HttpSession getSession(boolean create) { + if (_session != null && ((SessionManager.Session) _session).isValid()) return _session; - - _session=null; - + + _session = null; + String id = getRequestedSessionId(); - - if (id != null) - { - _session=_servletHandler.getHttpSession(id); + + if (id != null) { + _session = _servletHandler.getHttpSession(id); if (_session == null && !create) return null; } - - if (_session == null && create) - { - _session=newSession(); + + if (_session == null && create) { + _session = newSession(); } - + return _session; } @@ -517,169 +463,187 @@ public HttpSession getSession(boolean create) /* Create a new HttpSession. * If cookies are being used a set cookie is added to the response. */ - HttpSession newSession() - { - HttpSession session=_servletHandler.newHttpSession(this); - Cookie cookie=_servletHandler.getSessionManager().getSessionCookie(session,isSecure()); - if (cookie!=null) + HttpSession newSession() { + HttpSession session = _servletHandler.newHttpSession(this); + Cookie cookie = _servletHandler.getSessionManager().getSessionCookie(session, isSecure()); + if (cookie != null) _servletHttpResponse.getHttpResponse().addSetCookie(cookie); return session; } - + /* ------------------------------------------------------------ */ - public HttpSession getSession() - { + public HttpSession getSession() { HttpSession session = getSession(true); return session; } - + + @Override + public String changeSessionId() { + return null; + } + /* ------------------------------------------------------------ */ - public boolean isRequestedSessionIdValid() - { + public boolean isRequestedSessionIdValid() { return _requestedSessionId != null && getSession(false) != null; } - + /* -------------------------------------------------------------- */ - public boolean isRequestedSessionIdFromCookie() - { + public boolean isRequestedSessionIdFromCookie() { return _sessionIdState == __SESSIONID_COOKIE; } - + /* -------------------------------------------------------------- */ - public boolean isRequestedSessionIdFromURL() - { + public boolean isRequestedSessionIdFromURL() { return _sessionIdState == __SESSIONID_URL; } - + /* -------------------------------------------------------------- */ + /** * @deprecated */ - public boolean isRequestedSessionIdFromUrl() - { + public boolean isRequestedSessionIdFromUrl() { return isRequestedSessionIdFromURL(); } - + + @Override + public boolean authenticate(HttpServletResponse httpServletResponse) throws IOException, ServletException { + return false; + } + + @Override + public void login(String s, String s1) throws ServletException { + + } + + @Override + public void logout() throws ServletException { + + } + + @Override + public Collection getParts() throws IOException, ServletException { + return null; + } + + @Override + public Part getPart(String s) throws IOException, ServletException { + return null; + } + + @Override + public T upgrade(Class aClass) throws IOException, ServletException { + return null; + } + /* -------------------------------------------------------------- */ - public Enumeration getAttributeNames() - { + public Enumeration getAttributeNames() { return _httpRequest.getAttributeNames(); } - + /* -------------------------------------------------------------- */ - public Object getAttribute(String name) - { + public Object getAttribute(String name) { return _httpRequest.getAttribute(name); } - + /* -------------------------------------------------------------- */ - public void setAttribute(String name, Object value) - { - _httpRequest.setAttribute(name,value); + public void setAttribute(String name, Object value) { + _httpRequest.setAttribute(name, value); } - + /* -------------------------------------------------------------- */ - public void removeAttribute(String name) - { + public void removeAttribute(String name) { _httpRequest.removeAttribute(name); } - + /* -------------------------------------------------------------- */ public void setCharacterEncoding(String encoding) - throws UnsupportedEncodingException - { - if (_inputState!=0) + throws UnsupportedEncodingException { + if (_inputState != 0) throw new IllegalStateException("getReader() or getInputStream() called"); "".getBytes(encoding); - _httpRequest.setCharacterEncoding(encoding,false); + _httpRequest.setCharacterEncoding(encoding, false); } - + /* -------------------------------------------------------------- */ - public String getCharacterEncoding() - { + public String getCharacterEncoding() { return _httpRequest.getCharacterEncoding(); } - + /* -------------------------------------------------------------- */ - public int getContentLength() - { + public int getContentLength() { return _httpRequest.getContentLength(); } - + + @Override + public long getContentLengthLong() { + return 0; + } + /* -------------------------------------------------------------- */ - public String getContentType() - { + public String getContentType() { return _httpRequest.getContentType(); } - + /* -------------------------------------------------------------- */ - public ServletInputStream getInputStream() - { - if (_inputState!=0 && _inputState!=1) + public ServletInputStream getInputStream() { + if (_inputState != 0 && _inputState != 1) throw new IllegalStateException(); - if (_in==null) - _in = new ServletIn((HttpInputStream)_httpRequest.getInputStream()); - _inputState=1; - _reader=null; - return _in; + if (_in == null) + _in = new ServletIn((HttpInputStream) _httpRequest.getInputStream()); + _inputState = 1; + _reader = null; + return (ServletInputStream) _in.getInputStream(); } - + /* -------------------------------------------------------------- */ + /** * This method is not recommended as it forces the generation of a * non-optimal data structure. */ - public Map getParameterMap() - { + public Map getParameterMap() { return Collections.unmodifiableMap(_httpRequest.getParameterStringArrayMap()); } - + /* -------------------------------------------------------------- */ - public String getParameter(String name) - { + public String getParameter(String name) { return _httpRequest.getParameter(name); } - + /* -------------------------------------------------------------- */ - public Enumeration getParameterNames() - { + public Enumeration getParameterNames() { return Collections.enumeration(_httpRequest.getParameterNames()); } - + /* -------------------------------------------------------------- */ - public String[] getParameterValues(String name) - { - List v=_httpRequest.getParameterValues(name); - if (v==null) + public String[] getParameterValues(String name) { + List v = _httpRequest.getParameterValues(name); + if (v == null) return null; - String[]a=new String[v.size()]; - return (String[])v.toArray(a); + String[] a = new String[v.size()]; + return (String[]) v.toArray(a); } - + /* -------------------------------------------------------------- */ - public String getProtocol() - { + public String getProtocol() { return _httpRequest.getVersion(); } - + /* -------------------------------------------------------------- */ - public String getScheme() - { + public String getScheme() { return _httpRequest.getScheme(); } - + /* -------------------------------------------------------------- */ - public String getServerName() - { + public String getServerName() { return _httpRequest.getHost(); } - + /* -------------------------------------------------------------- */ - public int getServerPort() - { + public int getServerPort() { int port = _httpRequest.getPort(); - if (port==0) - { + if (port == 0) { if (getScheme().equalsIgnoreCase("https")) return 443; return 80; @@ -688,136 +652,158 @@ public int getServerPort() } /* -------------------------------------------------------------- */ - public int getRemotePort() - { - HttpConnection connection= _httpRequest.getHttpConnection(); - if (connection!=null) + public int getRemotePort() { + HttpConnection connection = _httpRequest.getHttpConnection(); + if (connection != null) return connection.getRemotePort(); return 0; } - + /* -------------------------------------------------------------- */ - public String getLocalName() - { - HttpConnection connection= _httpRequest.getHttpConnection(); - if (connection!=null) + public String getLocalName() { + HttpConnection connection = _httpRequest.getHttpConnection(); + if (connection != null) return connection.getServerName(); return null; } - + /* -------------------------------------------------------------- */ - public String getLocalAddr() - { - HttpConnection connection= _httpRequest.getHttpConnection(); - if (connection!=null) + public String getLocalAddr() { + HttpConnection connection = _httpRequest.getHttpConnection(); + if (connection != null) return connection.getServerAddr(); return null; } - + /* -------------------------------------------------------------- */ - public int getLocalPort() - { - HttpConnection connection= _httpRequest.getHttpConnection(); - if (connection!=null) + public int getLocalPort() { + HttpConnection connection = _httpRequest.getHttpConnection(); + if (connection != null) return connection.getServerPort(); return 0; } - + + @Override + public ServletContext getServletContext() { + return null; + } + + @Override + public AsyncContext startAsync() throws IllegalStateException { + return null; + } + + @Override + public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException { + return null; + } + + @Override + public boolean isAsyncStarted() { + return false; + } + + @Override + public boolean isAsyncSupported() { + return false; + } + + @Override + public AsyncContext getAsyncContext() { + return null; + } + + @Override + public DispatcherType getDispatcherType() { + return null; + } + /* -------------------------------------------------------------- */ public BufferedReader getReader() - throws UnsupportedEncodingException - { - if (_inputState!=0 && _inputState!=2) + throws UnsupportedEncodingException { + if (_inputState != 0 && _inputState != 2) throw new IllegalStateException(); - if (_reader==null) - { - String encoding=getCharacterEncoding(); - if (encoding==null) - encoding=StringUtil.__ISO_8859_1; - _reader=new BufferedReader(new InputStreamReader(getInputStream(),encoding)); - + if (_reader == null) { + String encoding = getCharacterEncoding(); + if (encoding == null) + encoding = StringUtil.__ISO_8859_1; + _reader = new BufferedReader(new InputStreamReader(getInputStream(), encoding)); + } - _inputState=2; + _inputState = 2; return _reader; } - + /* -------------------------------------------------------------- */ - public String getRemoteAddr() - { + public String getRemoteAddr() { return _httpRequest.getRemoteAddr(); } - + /* -------------------------------------------------------------- */ - public String getRemoteHost() - { - if (_httpRequest.getHttpConnection()==null) + public String getRemoteHost() { + if (_httpRequest.getHttpConnection() == null) return null; return _httpRequest.getRemoteHost(); } /* -------------------------------------------------------------- */ + /** - * @deprecated As of Version 2.1 of the Java Servlet API, - * use {@link javax.servlet.ServletContext#getRealPath} instead. + * @deprecated As of Version 2.1 of the Java Servlet API, + * use {@link javax.servlet.ServletException#getRealPath(String)} instead. */ - public String getRealPath(String path) - { + public String getRealPath(String path) { return _servletHandler.getServletContext().getRealPath(path); } /* ------------------------------------------------------------ */ - public RequestDispatcher getRequestDispatcher(String url) - { + public RequestDispatcher getRequestDispatcher(String url) { if (url == null) return null; - if (!url.startsWith("/")) - { - String relTo=URI.addPaths(_servletPath,_pathInfo); - int slash=relTo.lastIndexOf("/"); - if (slash>1) - relTo=relTo.substring(0,slash+1); + if (!url.startsWith("/")) { + String relTo = URI.addPaths(_servletPath, _pathInfo); + int slash = relTo.lastIndexOf("/"); + if (slash > 1) + relTo = relTo.substring(0, slash + 1); else - relTo="/"; - url=URI.addPaths(relTo,url); + relTo = "/"; + url = URI.addPaths(relTo, url); } - + return _servletHandler.getServletContext().getRequestDispatcher(url); } /* ------------------------------------------------------------ */ - public String toString() - { + public String toString() { return - getContextPath()+"+"+getServletPath()+"+"+getPathInfo()+"\n"+ - _httpRequest.toString(); + getContextPath() + "+" + getServletPath() + "+" + getPathInfo() + "\n" + + _httpRequest.toString(); } - + /* ------------------------------------------------------------ */ - /** Unwrap a ServletRequest. + + /** + * Unwrap a ServletRequest. * + * @param request + * @return The core ServletHttpRequest which must be the + * underlying request object * @see javax.servlet.ServletRequestWrapper * @see javax.servlet.http.HttpServletRequestWrapper - * @param request - * @return The core ServletHttpRequest which must be the - * underlying request object */ - public static ServletHttpRequest unwrap(ServletRequest request) - { - while (!(request instanceof ServletHttpRequest)) - { - if (request instanceof ServletRequestWrapper) - { + public static ServletHttpRequest unwrap(ServletRequest request) { + while (!(request instanceof ServletHttpRequest)) { + if (request instanceof ServletRequestWrapper) { ServletRequestWrapper wrapper = - (ServletRequestWrapper)request; - request=wrapper.getRequest(); - } - else + (ServletRequestWrapper) request; + request = wrapper.getRequest(); + } else throw new IllegalArgumentException("Does not wrap ServletHttpRequest"); } - return (ServletHttpRequest)request; + return (ServletHttpRequest) request; } } diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHttpResponse.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHttpResponse.java index 2acb69aeb..864a4802e 100644 --- a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHttpResponse.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHttpResponse.java @@ -29,6 +29,7 @@ import javax.servlet.http.HttpSession; import java.io.IOException; import java.io.PrintWriter; +import java.util.Collection; import java.util.Locale; /* ------------------------------------------------------------ */ @@ -520,6 +521,26 @@ public void setStatus(int status, String message) _httpResponse.setReason(message); } + @Override + public int getStatus() { + return 0; + } + + @Override + public String getHeader(String s) { + return null; + } + + @Override + public Collection getHeaders(String s) { + return null; + } + + @Override + public Collection getHeaderNames() { + return null; + } + /* ------------------------------------------------------------ */ public ServletOutputStream getOutputStream() @@ -594,7 +615,12 @@ public void setContentLength(int len) if (!isCommitted()) setIntHeader(HttpFields.__ContentLength,len); } - + + @Override + public void setContentLengthLong(long l) { + + } + /* ------------------------------------------------------------ */ public String getContentType() { diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletIn.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletIn.java index a475eb2ae..010dfb5ea 100644 --- a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletIn.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletIn.java @@ -17,78 +17,76 @@ import net.lightbody.bmp.proxy.jetty.http.HttpInputStream; -import javax.servlet.ServletInputStream; import java.io.IOException; -class ServletIn extends ServletInputStream -{ +class ServletIn extends HttpInputStream { HttpInputStream _in; /* ------------------------------------------------------------ */ - ServletIn(HttpInputStream in) - { - _in=in; + ServletIn(HttpInputStream in) { + super(in); + _in = in; } - + /* ------------------------------------------------------------ */ + @Override public int read() - throws IOException - { + throws IOException { return _in.read(); } - + /* ------------------------------------------------------------ */ - public int read(byte b[]) throws IOException - { + @Override + public int read(byte b[]) throws IOException { return _in.read(b); } - + /* ------------------------------------------------------------ */ - public int read(byte b[], int off, int len) throws IOException - { - return _in.read(b,off,len); + @Override + public int read(byte b[], int off, int len) throws IOException { + return _in.read(b, off, len); } - + /* ------------------------------------------------------------ */ - public long skip(long len) throws IOException - { + @Override + public long skip(long len) throws IOException { return _in.skip(len); } - + /* ------------------------------------------------------------ */ + @Override public int available() - throws IOException - { + throws IOException { return _in.available(); } - + /* ------------------------------------------------------------ */ + @Override public void close() - throws IOException - { + throws IOException { _in.close(); } - + /* ------------------------------------------------------------ */ - public boolean markSupported() - { + @Override + public boolean markSupported() { return _in.markSupported(); } - + /* ------------------------------------------------------------ */ + @Override public void reset() - throws IOException - { + throws IOException { _in.reset(); } - + /* ------------------------------------------------------------ */ - public void mark(int readlimit) - { + @Override + public void mark(int readlimit) { _in.mark(readlimit); } - + } diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletOut.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletOut.java index 277b233c9..591db2e47 100644 --- a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletOut.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletOut.java @@ -18,73 +18,79 @@ import net.lightbody.bmp.proxy.jetty.util.IO; import javax.servlet.ServletOutputStream; +import javax.servlet.WriteListener; import java.io.IOException; import java.io.OutputStream; -class ServletOut extends ServletOutputStream -{ +class ServletOut extends ServletOutputStream { OutputStream _out; /* ------------------------------------------------------------ */ - ServletOut(OutputStream out) - { - _out=out; + ServletOut(OutputStream out) { + _out = out; } - + /* ------------------------------------------------------------ */ public void write(int ch) - throws IOException - { + throws IOException { _out.write(ch); } - + /* ------------------------------------------------------------ */ - public void write(byte[]b) - throws IOException - { + @Override + public void write(byte[] b) + throws IOException { _out.write(b); } - + /* ------------------------------------------------------------ */ - public void write(byte[]b,int o,int l) - throws IOException - { - _out.write(b,o,l); + @Override + public void write(byte[] b, int o, int l) + throws IOException { + _out.write(b, o, l); } /* ------------------------------------------------------------ */ + @Override public void flush() - throws IOException - { + throws IOException { _out.flush(); } - + /* ------------------------------------------------------------ */ + @Override public void close() - throws IOException - { + throws IOException { super.close(); _out.close(); } - + /* ------------------------------------------------------------ */ - public void disable() - throws IOException - { - _out=IO.getNullStream(); + public void disable() { + _out = IO.getNullStream(); } /* ------------------------------------------------------------ */ - public void print(String s) throws IOException - { - if (s!=null) write(s.getBytes()); + @Override + public void print(String s) throws IOException { + if (s != null) write(s.getBytes()); } /* ------------------------------------------------------------ */ - public void println(String s) throws IOException - { - if (s!=null) write(s.getBytes()); - write(IO.CRLF_BYTES); + @Override + public void println(String s) throws IOException { + if (s != null) write(s.getBytes()); + write(IO.CRLF_BYTES); + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setWriteListener(WriteListener writeListener) { + } } diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Container.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Container.java index 1c2782f22..251caa7af 100644 --- a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Container.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Container.java @@ -28,283 +28,259 @@ /* ------------------------------------------------------------ */ -/** Abstract Container. + +/** + * Abstract Container. * Provides base handling for LifeCycle and Component events. * - * @version $Id: Container.java,v 1.4 2005/08/13 08:49:59 gregwilkins Exp $ * @author Greg Wilkins (gregw) + * @version $Id: Container.java,v 1.4 2005/08/13 08:49:59 gregwilkins Exp $ */ -public abstract class Container implements LifeCycle,EventProvider,Serializable -{ +public abstract class Container implements LifeCycle, EventProvider, Serializable { private static Log log = LogFactory.getLog(Container.class); - + private Object _eventListeners; private Object _components; - + private transient boolean _started; private transient boolean _starting; private transient boolean _stopping; - + /* ------------------------------------------------------------ */ - /** Start the server. + + /** + * Start the server. * Generate LifeCycleEvents for starting and started either side of a call to doStart */ public synchronized final void start() - throws Exception - { + throws Exception { if (_started || _starting) return; - - _starting=true; - - if (log.isDebugEnabled()) log.debug("Starting "+this); + + _starting = true; + + if (log.isDebugEnabled()) log.debug("Starting " + this); LifeCycleEvent event = new LifeCycleEvent(this); - for(int i=0;i true); } } \ No newline at end of file diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/util/DeleteDirectoryTask.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/util/DeleteDirectoryTask.java index dffe0ade9..043d2662a 100644 --- a/browsermob-legacy/src/main/java/net/lightbody/bmp/util/DeleteDirectoryTask.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/util/DeleteDirectoryTask.java @@ -32,7 +32,7 @@ public void run() { try { Files.walkFileTree(directory, new SimpleFileVisitor() { @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { try { Files.delete(file); } catch (IOException e) { @@ -43,7 +43,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO } @Override - public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + public FileVisitResult postVisitDirectory(Path dir, IOException exc) { try { Files.delete(dir); } catch (IOException e) { diff --git a/browsermob-legacy/src/test/java/net/lightbody/bmp/proxy/HarTest.java b/browsermob-legacy/src/test/java/net/lightbody/bmp/proxy/HarTest.java index 346273133..d1e5901bd 100644 --- a/browsermob-legacy/src/test/java/net/lightbody/bmp/proxy/HarTest.java +++ b/browsermob-legacy/src/test/java/net/lightbody/bmp/proxy/HarTest.java @@ -80,252 +80,252 @@ public void testRequestAndResponseSizesAreSet() throws Exception { assertEquals(13, entry.getResponse().getBodySize()); } - @Test - public void testThatProxyCanCaptureBodyInHar() throws IOException, InterruptedException { - proxy.setCaptureContent(true); - proxy.newHar("Test"); + @Test + public void testThatProxyCanCaptureBodyInHar() throws IOException, InterruptedException { + proxy.setCaptureContent(true); + proxy.newHar("Test"); - String body = IOUtils.toStringAndClose(client.execute(new HttpGet(getLocalServerHostnameAndPort() + "/a.txt")).getEntity().getContent()); + String body = IOUtils.toStringAndClose(client.execute(new HttpGet(getLocalServerHostnameAndPort() + "/a.txt")).getEntity().getContent()); - assertThat(body, containsString("this is a.txt")); + assertThat(body, containsString("this is a.txt")); Thread.sleep(500); - Har har = proxy.getHar(); - assertNotNull("Har is null", har); - HarLog log = har.getLog(); - assertNotNull("Log is null", log); - List entries = log.getEntries(); - assertNotNull("Entries are null", entries); - HarEntry entry = entries.get(0); - assertNotNull("No entry found", entry); - HarResponse response = entry.getResponse(); - assertNotNull("Response is null", response); - HarContent content = response.getContent(); - assertNotNull("Content is null", content); - String mime = content.getMimeType(); - assertEquals("Mime not matched", "text/plain", mime); - String encoding = content.getEncoding(); - assertEquals("Encoding not matched", null, encoding); - String text = content.getText(); - assertEquals("Text not matched", "this is a.txt", text); - } - - @Test - public void testThatProxyCanCaptureJsonRpc() throws IOException, InterruptedException { - proxy.setCaptureContent(true); - proxy.newHar("Test"); - - HttpPost post = new HttpPost(getLocalServerHostnameAndPort() + "/jsonrpc"); - HttpEntity entity = new StringEntity("{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"test\",\"params\":{}}"); - post.setEntity(entity); - post.addHeader("Accept", "application/json-rpc"); - post.addHeader("Content-Type", "application/json; charset=UTF-8"); - - String body = IOUtils.toStringAndClose(client.execute(post).getEntity().getContent()); - - assertThat(body, containsString("{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{}}")); + Har har = proxy.getHar(); + assertNotNull("Har is null", har); + HarLog log = har.getLog(); + assertNotNull("Log is null", log); + List entries = log.getEntries(); + assertNotNull("Entries are null", entries); + HarEntry entry = entries.get(0); + assertNotNull("No entry found", entry); + HarResponse response = entry.getResponse(); + assertNotNull("Response is null", response); + HarContent content = response.getContent(); + assertNotNull("Content is null", content); + String mime = content.getMimeType(); + assertEquals("Mime not matched", "text/plain", mime); + String encoding = content.getEncoding(); + assertEquals("Encoding not matched", null, encoding); + String text = content.getText(); + assertEquals("Text not matched", "this is a.txt", text); + } + + @Test + public void testThatProxyCanCaptureJsonRpc() throws IOException, InterruptedException { + proxy.setCaptureContent(true); + proxy.newHar("Test"); + + HttpPost post = new HttpPost(getLocalServerHostnameAndPort() + "/jsonrpc"); + HttpEntity entity = new StringEntity("{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"test\",\"params\":{}}"); + post.setEntity(entity); + post.addHeader("Accept", "application/json-rpc"); + post.addHeader("Content-Type", "application/json; charset=UTF-8"); + + String body = IOUtils.toStringAndClose(client.execute(post).getEntity().getContent()); + + assertThat(body, containsString("{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{}}")); Thread.sleep(500); - Har har = proxy.getHar(); - assertNotNull("Har is null", har); - HarLog log = har.getLog(); - assertNotNull("Log is null", log); - List entries = log.getEntries(); - assertNotNull("Entries are null", entries); - HarEntry entry = entries.get(0); - assertNotNull("No entry found", entry); - HarResponse response = entry.getResponse(); - assertNotNull("Response is null", response); - HarRequest request = entry.getRequest(); - assertNotNull("Request is null", request); - HarPostData postdata = request.getPostData(); - assertNotNull("PostData is null", postdata); - assertThat(postdata.getText(), containsString("{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"test\",\"params\":{}}")); - } - - @Test - public void testThatTraditionalPostParamsAreCaptured() throws IOException, InterruptedException { - proxy.setCaptureContent(true); - proxy.newHar("Test"); - - HttpPost post = new HttpPost(getLocalServerHostnameAndPort() + "/jsonrpc"); - post.setEntity(new UrlEncodedFormEntity(Collections.singletonList(new BasicNameValuePair("foo", "bar")))); - - IOUtils.toStringAndClose(client.execute(post).getEntity().getContent()); + Har har = proxy.getHar(); + assertNotNull("Har is null", har); + HarLog log = har.getLog(); + assertNotNull("Log is null", log); + List entries = log.getEntries(); + assertNotNull("Entries are null", entries); + HarEntry entry = entries.get(0); + assertNotNull("No entry found", entry); + HarResponse response = entry.getResponse(); + assertNotNull("Response is null", response); + HarRequest request = entry.getRequest(); + assertNotNull("Request is null", request); + HarPostData postdata = request.getPostData(); + assertNotNull("PostData is null", postdata); + assertThat(postdata.getText(), containsString("{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"test\",\"params\":{}}")); + } + + @Test + public void testThatTraditionalPostParamsAreCaptured() throws IOException, InterruptedException { + proxy.setCaptureContent(true); + proxy.newHar("Test"); + + HttpPost post = new HttpPost(getLocalServerHostnameAndPort() + "/jsonrpc"); + post.setEntity(new UrlEncodedFormEntity(Collections.singletonList(new BasicNameValuePair("foo", "bar")))); + + IOUtils.toStringAndClose(client.execute(post).getEntity().getContent()); Thread.sleep(500); - Har har = proxy.getHar(); - assertNotNull("Har is null", har); - HarLog log = har.getLog(); - assertNotNull("Log is null", log); - List entries = log.getEntries(); - assertNotNull("Entries are null", entries); - HarEntry entry = entries.get(0); - assertNotNull("No entry found", entry); - HarResponse response = entry.getResponse(); - assertNotNull("Response is null", response); - HarRequest request = entry.getRequest(); - assertNotNull("Request is null", request); - HarPostData postdata = request.getPostData(); - assertNotNull("PostData is null", postdata); - assertEquals("application/x-www-form-urlencoded", postdata.getMimeType()); - assertEquals(1, postdata.getParams().size()); - assertEquals("foo", postdata.getParams().get(0).getName()); - assertEquals("bar", postdata.getParams().get(0).getValue()); - } - - @Test - public void testThatImagesAreCapturedAsBase64EncodedContent() throws IOException, InterruptedException { - proxy.setCaptureContent(true); - proxy.newHar("Test"); - - InputStream is1 = client.execute(new HttpGet(getLocalServerHostnameAndPort() + "/c.png")).getEntity().getContent(); - ByteArrayOutputStream o1 = new ByteArrayOutputStream(); + Har har = proxy.getHar(); + assertNotNull("Har is null", har); + HarLog log = har.getLog(); + assertNotNull("Log is null", log); + List entries = log.getEntries(); + assertNotNull("Entries are null", entries); + HarEntry entry = entries.get(0); + assertNotNull("No entry found", entry); + HarResponse response = entry.getResponse(); + assertNotNull("Response is null", response); + HarRequest request = entry.getRequest(); + assertNotNull("Request is null", request); + HarPostData postdata = request.getPostData(); + assertNotNull("PostData is null", postdata); + assertEquals("application/x-www-form-urlencoded", postdata.getMimeType()); + assertEquals(1, postdata.getParams().size()); + assertEquals("foo", postdata.getParams().get(0).getName()); + assertEquals("bar", postdata.getParams().get(0).getValue()); + } + + @Test + public void testThatImagesAreCapturedAsBase64EncodedContent() throws IOException, InterruptedException { + proxy.setCaptureContent(true); + proxy.newHar("Test"); + + InputStream is1 = client.execute(new HttpGet(getLocalServerHostnameAndPort() + "/c.png")).getEntity().getContent(); + ByteArrayOutputStream o1 = new ByteArrayOutputStream(); IOUtils.copyAndClose(is1, o1); - ByteArrayOutputStream o2 = new ByteArrayOutputStream(); + ByteArrayOutputStream o2 = new ByteArrayOutputStream(); IOUtils.copyAndClose(HarTest.class.getResourceAsStream("/local-server/c.png"), o2); - assertTrue("Image does not match file system", Arrays.equals(o1.toByteArray(), o2.toByteArray())); + assertTrue("Image does not match file system", Arrays.equals(o1.toByteArray(), o2.toByteArray())); Thread.sleep(500); - Har har = proxy.getHar(); - assertNotNull("Har is null", har); - HarLog log = har.getLog(); - assertNotNull("Log is null", log); - List entries = log.getEntries(); - assertNotNull("Entries are null", entries); - HarEntry entry = entries.get(0); - assertNotNull("No entry found", entry); - HarResponse response = entry.getResponse(); - assertNotNull("Response is null", response); - HarContent content = response.getContent(); - assertNotNull("Content is null", content); - String mime = content.getMimeType(); - assertEquals("Mime not matched", "image/png", mime); - String encoding = content.getEncoding(); - assertEquals("Encoding not matched", "base64", encoding); - String text = content.getText(); - String base64 = "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"; - assertEquals("Base64 not correct", base64, text); - } - - @Test - public void testThatUrlEncodedQueryStringIsParsedCorrecty() throws IOException, InterruptedException { - proxy.setCaptureContent(true); - proxy.newHar("Test"); - - HttpGet get = new HttpGet(getLocalServerHostnameAndPort() + "/a.txt?foo=bar&a=1%262"); - client.execute(get); + Har har = proxy.getHar(); + assertNotNull("Har is null", har); + HarLog log = har.getLog(); + assertNotNull("Log is null", log); + List entries = log.getEntries(); + assertNotNull("Entries are null", entries); + HarEntry entry = entries.get(0); + assertNotNull("No entry found", entry); + HarResponse response = entry.getResponse(); + assertNotNull("Response is null", response); + HarContent content = response.getContent(); + assertNotNull("Content is null", content); + String mime = content.getMimeType(); + assertEquals("Mime not matched", "image/png", mime); + String encoding = content.getEncoding(); + assertEquals("Encoding not matched", "base64", encoding); + String text = content.getText(); + String base64 = "iVBORw0KGgoAAAANSUhEUgAAATAAAAA5CAIAAAA+4eDYAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAIUBJREFUeNrsPQdYFMf3u8cdXaoICigi2LFhARsqKooajd1oYosaY0zU2KImamIsSdSfJjH2JJbE2KPGXmNXLIgFQelFpXeOK/t/dw/X5W53bg+B5B/vfXz3zc3NvJl58/rMLjTDMJQJTGCCfwfQJoE0wZsMidEPbp45mJed6eTq3ia4/18H/vj75GEra+ue/Ye//c54WiIxCaQJTFBF8Cjs4i/LpqtVKvyaklWYkVvA/jp07IfjPp5nEkgTmKAqQKko+W7qoIxnSfg1I68oJTOf28DSyvqPs+EWllZVOSupaWNM8P8dFPLihKiIEnmRnaOLWx0fM6lMTK+7F0+w0qhmmBc5hToNiosKUxLj6vo2gvK5Ywe3//QdAdvc5evqN25mEkgTvOlw5+LxA+uXFhXklZo1a5umAcGd3xpV08uX3PFJxA22nFMgV6rUPA4kXRpDFuTngnD6NWC6tefHZpf3dVGUs4XndImVr0kgTfDvgiePIq5dOEVu0yG4Nxoffdi7bX1xYSGhr5tH7e59B0Mh+emjXavnq9WvZKm4sCDs7KFb5w4HhAx6a9wsqbm5EJK05Hi2nJlfrN/AzExa06N2mXFdqA6tBUI81UVFOmXu9i5VGQJ5+9rfipISg50lZmbm5uBjW1d3renk7FL1KSkT/Dvh8rnjv29a4+RA1fUQzFDYy+57Vh9hZtOMNnfl1r94lrx59RIoNKnPWApI03MX1y4d7GmZ85Vjh7nSyALDMFeP702Nix67YI21rT0vktzMtFKPV6kulCv0GzRu7l/FAaSgQH674JOsjDSjEMnMzes3bu7nHwCqy8Ornokp32RIjImGz85tmXf6E1KGxwsfHbfwmGZReza39uals/BpY0V9PpUR1vDPCh+NlljXz05vTJhGXGT4+vkTJn653tbeSf/X4sJSLzenUM7bvWf/YVVPugqzaWBRH9y9uWvL9xMGdln4yZj056kmvnxjISH2CXy6uxluqcy5qFNz89I5+GzWiDHob6mLYr0aNAFjmJ5b+CQ160FC+v2EtMikjLgXOVn5xXh6kBofvX7BhILcLF6OxUJeEY8zWKde/a6hA/8fCyTXW7j+9+nJw3rcC7tqYs03EFQqZUpCLBQ8aho+UVPlhzOqvFfyqVCE37wMhZZNxLCaol3Xdkk5JalZBUUlSjWjOcJTqNQgYEkZeVEpmWj6nifGbPh8UmF+jh6janxd6FUg1xVI8FQ/W7ZOKpX+FwSyVOvkZC+aNu55SqKJQd80SEmMUyo1IZm7qxihUqpyr7HfIm5fKyosoGmqRWNRx+N//rE1OyeX96cSpSohLTfmebZcoQI7uXnxR/LiMokiicQMPguKFTon8TRNz1i00su34T9CvUpMwxQW5P28drmJQd84f/WpJoB0dqQsLUS1V2a/8lpvXtb4q14elIOdqL7XLl0nNwB5i07NfJFTmBB1/+evpykVr4whHldCA50u702eGRTy1j9FvcrNi146e7QgP8/Eo29YAKkRSA83sTfAlDmXXgnkRU1Gp1UTsX2zMnJExFDU8+yCJ8+y7t+6unPlZ2xW1tzCEj7zi0t0EjkjJnzyD1KvcgUSQoLH9++YePSNgsRSgRTbXl0YySg0KX0IcBLjNNmgFqIFEuywSCguUT5NzTpz/PC+dUtKA0VrGwggoZ5t065z908+X/HPClR5wtZO3fsMem+SSqVKe55yYMemx/fvEho/T0ky8egbBfHaMw93NyO6gJGUVX8bDzxsbShfL7Ede3ZSRMeIbQxSDr7rb9u3qijJ8I8WWFrbFsmVrOg3adFm3oqfzMxeK5FDy5xyszPPH/8z4vb1jBfPzC0s3Gt7t+nYtW2nYAxZK0UgHZxdGvq1wrJ/QNCI7i0xiOcFRUmZQ57szHTCdXbwImxsq2ndDOZheFhkxO2MtOclcvnYqXOxXsAnYUCzgl7ISk/Ly82GOKGavYOdvaOXT8P6TZqjZ8IL+Xm5OtPTAUsraytrm7I+Es/xrKOzSxllXFRYVFhAQCszt7CtxhMkpSYlPHl0LynuKUxMpVLC0HYOTu6163rW9dW5MkKG5IRYoEZqUlx+bg54aNY2ti6utbx8G/o08jM3tzDk4DGwRySaWFpZ2dhqLJtaFXHretSD8Mz05wqFYsqcr2iJBLrD/ClxKVZuGKkRSG0A2bwhI/6CSVBbRYnDJzs2bSUTnAtFJcpft26IurNFSltnF5b6q3Xqei5es+X1rwEc3HPo13U/yeXFNE351KF8fBgn6lL0uW33Trt0HLC0sX9opQgkF4D1be3sCVto7+jM/ToqpA1Benv0G/Lpl6sf3L255svZGIogjBg/lVcgn0beP7z710tnjgLn8S9PKmvaqm3ooFHtu/XWz2If3btj69qlhNV17T1gztIfuAIzth/PXcZfjlxxc38lMD99s/DEwd8JaMd9PG/o2A/Zr/LioiN7tp3884/4p1FCXWq4ubcK7AwLARUjyGqFBcf27QRUKYlxvA2A4dp06Bo6aGSrwCDBLEheLmhYwuTxoSQQnh+WzuNm0SfPXmxGSV6kJsFyjLWQqpxLipKSuze0Bx5NjePA/m+1CAw+sWPDqqvnTxYV5IsylQxz5TYw4SueyctMXDWzrX9g526DvrSy8yhngKaSbFy9Ggp+DZgxQxjPmtwfXzDF78df8K8duIk2d6tEgYyLjszJyiA0aOjXUjw2e6fqsNOLp40jCC1C+vPU75d+dv3v0wZopFTANsNfjZoeHy9Y3rp9l7IR/NBtP30Lga5Qd1D/3K8Rt/hPVkGDcAXy0b1bJIrLZCEDXl0BCb95ZcW8jzLTX5AX8uJZ8vEDv8Pf1PnL+gx+V7/BqcN7Nny3SEgxsZIPygv+GjdvDXiErpIa2CNH5zN/7fvu82m8nk6C1l+1r0ZVszECp1qeFHn7T5geGJbmjYx7HlBVGFXLM2T2krXgU8REPYx5/DA5ISYm6tGDOzfkxQXWVmDJKXkJ9fKZR61jSVMgLT5eDMwTPGRYR14BlZldtOf3k1s2nQrp2/W9aT/peEZiIDtHTdOS0YOY0K48S4BBHWW3cm51qeb3u5lty4oUyNTEONhUcMwSY58c2/8bwQX18w9wreUpHjP438vmfGhQGkEOV8ybWlhgRP4WNPeCKaP6Dh2NnlWp7+1UvUO33hdOHBLqBaYGvEfWvYy4dY1fIO/cCO4zqNTC5Oclcmw7bwTOeg0XTx1ZNncK+H5iI36pLCCop57SUa5aNOPsX/vFUwPCgakjQ6fM/br3wHeM3f2UhLifv18utOno1xhlHktjgfjD8OntqRFmo0BdVOpWQPjn26gZ/J0+snf/9k2eNRXzpqjBK9r8O335Fo1tQA57dmY6tmFs+J1TJiuHOn/tzNzRLcbO2tKibWejZlJUTE0YznTvSFIoEia38NF7Ns2OSSw8Kkwgw66chz/D3COTTfp0oVGYwW0z2ObGxTNfzZxAMGsEOLL7V3CNpn3xDU2X7hBYG4JAAttFPwxv2a4Tfr0nJJB3b7LlyHu3yc98g1LAAsRaKxfOEC+NAB2DQ51dXHVmaKw0loqxQrHmq9mgVd8e+b5RHf/au53wa0JpRsfop94t1RpnpHF9ozuqC8v4+RdP/7Xyi+luLsznU9W0hFqyVvI0QYvfgho5gOnZiXm58/zgaE+9HcL0Cy448Oc7JcVb2nYOET8Tz1rwZ3j+jCKjKGqyjd/hSsnSCgHE/fO/We/TyK9i0SbERC2ZNbF80ogA0d0fW1+Fhc1aB5KvwrNeKziNQhePgAvzcrJL/dUIkr/q5duwSYs2WP5t8xqQB+PipXfG6dTs2vJ9OaSRhY0rF4vRrcZskOEzj1y+QM/DJQNEpX5dQeMjbCGfaBOoGoANWrXoU4mEmT5ebWVJLfuxVBpBzFbMVYd0NiCNHE+EGtJHTcWPe3D7QmVIhyrvliKjCgWyfbdem/adC+wSUuGYv186r0Quf00kOzas4opW6KBRJIF8GE72V9mc8MsA8jbJPA55j+1y7fwpYpw5vP/wsW06dLV4mSWu37h5o2b+3Gawip0bVxP823cnf7pozc/9ho2hBTgRpvG/L2cZqxcIIOYQ8q+zPJOxtVbXcae8+B7XUiipK7cFJYlRF6mLS3dz69plRQX53TswXh7UwVN0VKw272hLLfxEXbOG0WtpWI+RR40pkedXhoyUJK+vOoG8fuH0ljVfV/hFVvAMCVJhlLe2d9sGbmrX3MLCoIW8F3aNOLcbyN+PIwQF0sraJvjlAwRZGWmEGLhrrwHTF343ec5XX/2wfdfZ8ImfLqxm7/DWiLE6zWAVBGdh/LT5IydOD+jcY8rcJVAgpMfIXqh4yEx/ASE3ZejM4wl/Dpjyb8rUcOapj0mgMrLIYeRj+Ex7lvL3qSOgefp0YwqKqD9PlsowxHW1XMu5Ip/a8sdnJ1aKkcy/oy6OqyKBVKmU544dnDQ4+NKZoxWIVoxv5l67LlgSqaG3qpw/fpB5eYUKeL1Tj75CLWGb8exRKMX6Mq9zE+0DciQvdAsdiCd42pwbyXm6fPb4vu0boh/dK5EXgxgPHDVh0/7zQT3LXLCE4PPcsQPk7BFbHkAMFI/u21mB/ir4io72wtpQJXkcQ6v4AuchffhdynuRdE4uOYzUjAvMBnsKttHNhbocRhdrHSkfLyqgJY92SC3scD9teFhkXfZJD4aSJRQOfpAz/XJk+yxOrtrd5gKjeCGeCLSZbXaxe54Is6rMvlAxSR2RAI7Q0tmTF6/9BfyuCkEYHnaFnIGc/+169JNBij6f+m7ck8dCjSHki4l6WK9hUza1c+bIPoKRrN+kudDhHuvZKkpKyJeW+g59jy07ODqDpAkdZ4Px3LTqK0r7RELter6t2nUK7jPIwal6GTsT+YB8yDFjzABKXMyUHB+TnBALuqwK/FUVY1uiyI9J5LmOI3Qf4FYE7exIMrkqbaL1/h3NRfNGPpqWd+6X/gTuq77EZNhtatC+Fyq1X9fM7dZwp6ODLK/G3qb1SsP73VtX+RV/h0+rSKVM1tMtIJhiKGBmF2Dd8Bdbie0Hgzv37RQb1I447fzwirGQECJ+t2Xft5v3Lly9ZdQHM5xrCO4ALHjVohnib1Eg9Ow/bO2Ov/ZdfLTz5K1PF69CRlEqlSkJJJHoN2w0G7W6uNWavnAleRS84YXQuHlrwuM2IJB4+4QAII0gk3gVkxdgCO65Hy2RdAw2fG8DCBgXHbl/x6YpI3otnj6ee+Qb/ySS3PfFs+QXqUnsH7mxzomrASMgkQx4Z/y6P07uv/Ro27HrH332NRCcJSnZX5VZa9L9dx7QIsdipG7gsubm0QYTrcnahzBrai9NxSaVttd/jCuTertuk14vVYDZyA+X7DvplEEN9XgpjQCDx3xy6MIrCZSnnxaby/ReRkvtgD61fZpu3kVnEw07I0+oGIF0dnFr2qqdn38ACMCoSTPW7Tqhcx2HC1npaWePGpEGHPH+xzMWrQSLZGNbzdnFtcdbQzfsPWvvVD0/N5t8QqCTQ2rQtIVjdRdSri87k/u1j3BqJ+rB3aR4wzcmwWslNGPTOSyMmTpXx+iR4er5E7PGD2a1W1ZGegW6MyC94htPnrX4g1mLves3trapVsPNve/Q0ZsPXDAzkyaKOIQ0t/UA3XczXKxAypw1aovM2eoiGJdBf8HGWnPQjz4nlJ0cdBvLZW3LzMfCokjSXC5tVtZWmymkr14OYk4liNJTUnuJdQMsgz8FzvCjJ6RlMsrsSokhQRrZkzpeCLt8XqyCsbEdPn6qfspRKpXyvstIJ3DVrVGSBFhdNo4J7jvI0spawB29lyxGIO/ewCfl9cHOwUk/TAV1s3LrfqNOhhJio9kzG/31vg6IvHSG3ke/l0epLOAt0AQR18ol5m6BQT3jk6m0TFHDWdToD1yRQ7wDwqgK1PIUifaVjZqXBigpPAnmvWMgL9S9WJafmyHP1/UgcjNfaSiZpIA9WSFNQy3HZjcunnkaqXGaydeVeM+rKyaGzMvNJvyakhgrNqnVsKnQBV9bO3uaJr1n/ezRA1y9cPvqBR0bqAPVHMo8ugPKPiikP+8dVHAU/9y1VTcDMebDPb+s49bcvHSOEdg2cMJlfO8jdK/jvXbHkavnTp49tj/8xmVCQoiFc8cOjvlojkb92xq40tIhuLfIJwwAxF+jA9+b9/WCYKAw+0V+EpKW1QjsEvz75rVh9+jeXQxwOTQ2q+YP8XZqUj4YHAtzgpGMAsMAdh5EVybVhKOgwHkjaKuC34oKxrPZtYhb12pYRzgokjPTxztVr8E6I3WcH5WZiQiBpNTFT26ui4iy2KZ9pbJnLc2L80i6SeZYKQJ54cSh29f+JjSQFxeLRFXN3kHQ1TG3cHOvnZoUL9Tg1KHd0H3gyAnAqdcvnVm3bAF5rDre9XVq+gx5V+hSuP5LMd8aNubEwV1cmRfyqEGP9Bk8ijc+3Pvrhv4jxoLkwB/6jfFPHh/YuZlAz+cpiSUlcqCGRx1v8gI/+fwbO3tHqqKhmp2DQEyuCeTMZVQNZwMW0rdxM/AOwu49693FoL8KZJGAnMC+g6QRMEMY6d2gcfSje2B7QQ6h5bM0/hsIrvaJl/cMtved7VrTIzzs6uXDy6ePZawsMyKO9S2o9rGzqyeIaOK99R+OfCVLKsqWEedLStO+vnqEVivoBt7Uh++qyTk1iaVXxQjkvbAra5fMUSqVhfl5MVEPyelHrU/rJBKzhCYtu1VAZ/KJ2f7tG+FPzEDAqT6NdB8rqN9YcxMSNtVgd9danuC5+fm3u3zmmMHGrQKDanrU0a+/duHU1rVLD/3x8zsTpvXoNwRMKMRj8Ofk4np7+N9k9xIEsmGzVlKpjHDvNyUhzs7Pkc2jRtwWfNsFqAyw4bTIaywCzRK1b5qr5Wogs0ub14CB2gX1OHFgR0GR0J3Sl9zprDm5cXDWRNo5uSSBVBVFte7QDVRk+CONG9WwHvMsjc7LpzKyKWc9BdK2/r2MrFEPb9MudsxnH5RO2K9ekrxkdtxTqkNtyrVV2WnYin0CBULWBVMZMf6tJiVbrXXFCGT80yjCg0L6AFxeIbo55O0RFXWE3f2tIbzuXOjgUWu+mm1YxgI0vnEz/0AxAtlv6Hu89Ud2b6O05/Kg3X75YUXH4N5NWwVYWlod+uMXEo/KZOC9o4/dtnPwlbPHhVoe2Llp7rIfgfvBFP+wbP6d6xeFWjZrHRgyYPhrkvTltXJDXqj24aPALiFH9+64+4AWfBG4JkfiILUL0Aiko0YgszWJVoZgIQODloMtzUx/cfMeoKXOa+9xgGMc0pmnl7Mj1d5ftx5c4gZ8boeFSyj1uKI9DdpM6tC1UpI6BiGoV/8KwQMWrH3XXq+PB3zaIaMn8/7UtfcAYHQxRo/SPssiJgXStlOwfn1yQixXQsD1Pbpv5zfzp3756ft3b1wiIGzQtCX7VPvw8R8TzBqEElNHhq5ePPODIT0I0qjBM+6j16dqglZHe9Q05ASZa+K0Fm06WFnb3CT6IlKnXiCU1Mvnvw3eDQBVNXqKRpnuOkw38WW8tc/DHT9PG8oGGgoMaVuZy9AKFwqpQxedV7ZXkUD6t+/i16pdRWGbOn8Z+TBDLBJnfiSWVtbd+hh4PS4tkbRs21GTCPFpiMaKAKEDR/KaYjD15ftHgNzjGdBQ5Gc1njyKAC8uIYbkzoQMGEF4XtkYC6lxWT1cDZgFWqYxd+Cf+wcG3XnAf2WHE0BSr1xWA4nWXKbkGdj5jsGhSanUjoP0xBGaZ6+Sn1NHztKvsy4b70W01K6ixYK28JjGr7AqVRpreXrN/HJ1BSIEQVqxcTfhKoKh8If+aN7SLr0GkDh+8CiDhhrlECSzacu2JC0olfXie+CwRF586tDucsw/IKhH19C3uTXjp80Hq15+dRkYNGXuV6+/L8VFhWnak0x3AynW6izLBXYNKSqmHkTTApJrK7UvVROOTi5al9XAHFTa6wHgpbfv1uvoOfrqHXrKe4yZhPr9EA1ObDntmOs4mavYR0bjk0UfrtYYZlbNv6oFsnX7Lmu2HxGyReWG2nV9V27d36BpC6PTg/YOny1fp39ArwN1fRvpPFTB668ikL1W4Dne5V86fZR9XEs8wFizlqzV8VHBfZ29ZO3AdyeW4z8d9Xp7xKL//Ux455B4SIx7AgYfuJ/8UIWE46S17ah575OQ1yp1DIbWZSykoSMhfFIZHNcF324YPHrykTOyO/cpkEmpGbVqE33uqnEyyTC0zH2Gdb0l4rvY1x135ILhtDaIopX3MkESVYYoAkMvWvPzkh93EI4xXgfc3Gv/79dDU+YuYQ+ODOg5maxn/2GbD1zo3LOfmPahRCPZKqAzR0gCCS2FhL9L7/6L1/wSENRTKu5fi0K4NWrSjOUbdvGePYIoTpzxxZpth8l6pIyRb9J86U+/TfviW5nwf2szjn3VTI9+QwYODTEjHnzSMleufhzx/sdWNfiv9WN+FaGmRx1A7utn4EY0+6QyyPn70+Z//9vRfEnQtv2SIaFM80bMTzvopT/SqeJuiReqvGybH7OqM5Ot8ahTD+ZAprCrq/3gT29HZw9QKAXFSmrf3rrRdkoi+HQR/1H7rPGDcnOyxPuBllbAKnbudby96zdq3b4rwaX8cFhPwhUT4PVJMxeJ5wNAdf3C6fMn/nwYHqb/730gIARD2qZjNyAl4XIfr0s5c9wgubyIT7bNweyz78tSq1Ufj+rL++o6Zxc3YHryQAX5eTcvnblx6ezj+3dTEmJ19gLE1aeRX8fuoeLnHxP18PThPXdvXI5/GqVPZ+Dsth27derRt6lwVF9YkDd9NCkJ1yWkv9CrhJU5l4tjSce/Muc+Fp4zdUeMHKMu1jleltj4HabNylycgjbQkmR5bFtY+ejGR6lJ8acO7Ym+f9FSdTcnTxUdS/l6MUHtqIY+jIuTjkmkMnIs1Zat3JvPtHLmd3wU6YfkSYIhmMxlsIX7FO1UE4oTVyszDoOSeLUkKx+LWh/IXIeTrSBdvtTCvxAy0p7DX152lkJZUs3Owd7BqVZtL/FXVf5xAOFMSYzNzswA8QaT6OBU3dPLB2x7+bCBTklOiM3PzSnIz5XJLCDo9axbT0wC+b8KoJ4yXjzLycqMjbzyLOlpQV62lC62MFc6ONpY2zq7ejap27ibrYNHRQ6pLlEVPmRKXoDvLbH0lliKepHnf0cgTWCC/wCYBNIEJvgXQRl39tatWyu0AIVyY8zKyjp9+nTVryQmJoYwbfKv/ywAuYBolbTwf2RKxs62koaryrEqXiAnTZo0dOjQLC1AAb6WDyOQoEePHlW/kj179sydO7d8v1YZAGH1tRWQ63UkindpqFiNZd/WrVtXyJTEzBboANSo1OF4KVN5S6tIgUSrGBYWtlwLUEBraXIhKpxFqszqvo49gUCme/fulTGxOXPmnDp1ChX3v9lS/fMCCZRydCw91oQCfEWBRIu3ceNGJy0I2RlojA24PAd9QenSNF2vXj3AwKooKEMN1AM2xIxllmuxFwAabZ1ebCV8giVH/OLVHosHRsFeXK0JyIE1CdoUVqrTHX0KqOHSB+mGlUhJ/Ak+habKSy6oROQ6IxIWDn2hcqMWhNACkXEV8InSi9ND74ZduBDZcUpQP1cL3NFxT1lasfyA9QBIAZwk2xfmoDMKlyywWCQmYOOlNhIEGYm7TCG1qMPMuAqoQR/BWA7R32sC88NAyO28vTS6EDUWowdQeUoLUACzCTW7d2sufD19+lSn5YYNG0CGwa5mZmb6+/sjNihDJXaEn6AM3RHtkCFD4FfEzC1DM0AOBUAILaHs7e2NGNiWUAmosMHEiRNhOKhE/KDUGQEAJPgrzAFawnBYCWVACD+BAmLnyV2sDh5udxgdpgcFmAOUcW5QRlRDtMAlGiBHSkJLfTrjunBo+MqSC4YA5EhPFid54TgNaAMFoV1gK2G2uAr4ijNhpyREduhCGB3wwK9YBsw4YWiMOHEj4CsuATDgKIiQOwoLLAdiA+iO1EZmQ2qzBGG3lbvpOtTmTh7HgmYwVawsB4fo7zWZ+bFSvxeUxd7UgQUjLvRt9B0kpAgMjC1RD8FXqMQwGpbEKkvAgxuJmNkyNAO6wFyhEhUPdxTsBQ0AJypRQIiGHWpwbmKcRmiJw2FfqIGvaCXgE37FMoyuj5PbHcgN24bzRFQoPKgO4SsQCsrQGFaE00ZuYD0RHczomEAZusAoSC5kYvQnMcI3uHAYCycDILQLUAkIEQ9yPE5P31M1luyAAacKnzguEpbdZRwdJ4mDsnvBHUXf10W6ASocAjcIqQ0kQrbGZZKdYWQ51Fmsb89WloND9PeawPwoIMgGOr1KXVZcp84a8CvXidVxinq8BGzMNkC8WAmw4iWgsPFi0/e40HXRibj0e/GOazAJzG2JZSArEBeog6wGBWiG24B+BQLyGXcVUEZCsZVsDRoKdIRgRWLiJZ2JsdwPPg84P9yQXvzChXYB2ReIDMjJka0+2YE+hNFh1ciRSEDULMjHRo0iFOgih+ByAC2KNG4T+n76BkOIzjCo/r4YyyG8e01gfhZ4OUTKpSBr3JAPUF3xpgfQxHOpyVKBu0I0/SwpxcgMUhktPnxl835Cu8g7rsgu7AazxgTLsDogEPwEBdZqsZaH2x1mi1vCro5lVpgPUBw8IlgOhDeAkMVD5jYu02OUBeoT2Y6NP41auNAuoEzCxGB6RqVwuHzMOzoqMlg4eumntWBw+SKHxogGV8GODppljhaQdckqhu3F1Szl5hDevRbD/LwcImGFlZtsQD7AoEJIx3R/Cag/YK5oQFhFDpXsV5gQKDBj07YGT0TRP0TviBvHE86aWIWN+KEZam50fnA5SG7UU+hoIUBZpzsAqi1EhR4I6jUM31kVy248YWIsuQA/Ok46cQG7RqGF67Md7y7gCQd2FDk9HbLjeoVGRwfPUQvo4MGI+hayHFlWfy0AYdEE4VmdjlIzyGbs5PWJXA4O4d1rMczPzyE6iRlcMBuP6qd82HBfP2uCG4DsiJWsakc7o49Bv4ypC5Y0GMHrtIRKnB7mBnBJ2Fhoktz4ntWvbFKBXSb2wgwESwH9DJZOd3bO3GXyrh1nq5O0YMdluwB+tg0bO2HMgwkYoYVzJ4mSIzQTdhXsWGxCDvNqOtkdHbLjeoVGx44sAQEt24bdCEx14HJ4R9FJ6rBfdTgEEznc5aCdhFUIJXUwXOQyuc6gxnIIL4UNMj9vA92rc6gYKukMqsoAPUk2YWCCigXQ66xzhMYK5d8Erw9SfWfjv7EwkzRWHoBGB0cRTTR4bmykZILXB9PlchOUB8CTwvAenEaR+W0TmATSBCYwCaQJTGCC8sL/CTAAKdXwRQT6S1AAAAAASUVORK5CYII="; + assertEquals("Base64 not correct", base64, text); + } + + @Test + public void testThatUrlEncodedQueryStringIsParsedCorrecty() throws IOException, InterruptedException { + proxy.setCaptureContent(true); + proxy.newHar("Test"); + + HttpGet get = new HttpGet(getLocalServerHostnameAndPort() + "/a.txt?foo=bar&a=1%262"); + client.execute(get); Thread.sleep(500); - Har har = proxy.getHar(); - assertNotNull("Har is null", har); - HarLog log = har.getLog(); - assertNotNull("Log is null", log); - List entries = log.getEntries(); - assertNotNull("Entries are null", entries); - HarEntry entry = entries.get(0); - assertNotNull("No entry found", entry); - HarRequest req = entry.getRequest(); - assertNotNull("No request found", req); - // the HAR spec is not clear on what order the parameters should show up in. intuitively, since getQueryString() - // returns a List, the order should match the query string itself, but this is not technically required. - boolean sawFoo = false; - boolean sawA = false; - for (HarNameValuePair queryStringParam : req.getQueryString()) { - if (queryStringParam.getName().equals("foo")) { - assertEquals("expected 'foo' query param's value to be 'bar'", "bar", queryStringParam.getValue()); - sawFoo = true; - } else if (queryStringParam.getName().equals("a")) { - assertEquals("expected 'a' query param's value to be '1&2'", "1&2", queryStringParam.getValue()); - sawA = true; - } else { - fail("Unexpected query param: " + queryStringParam.getName() + ", value: " + queryStringParam.getValue()); - } - } - - assertTrue("did not find query param 'foo'", sawFoo); - assertTrue("did not find query param 'a'", sawA); - } - - @Test - public void testThatGzippedContentIsProperlyCapturedInHar() throws IOException, InterruptedException { - proxy.setCaptureContent(true); - proxy.newHar("Test"); - - // gzip all requests - server.forceGzip(); - - HttpGet get = new HttpGet(getLocalServerHostnameAndPort() + "/a.txt"); - get.addHeader("Accept-Encoding", "gzip"); - String body = IOUtils.toStringAndClose(new GZIPInputStream(client.execute(get).getEntity().getContent())); - - assertThat(body, containsString("this is a.txt")); + Har har = proxy.getHar(); + assertNotNull("Har is null", har); + HarLog log = har.getLog(); + assertNotNull("Log is null", log); + List entries = log.getEntries(); + assertNotNull("Entries are null", entries); + HarEntry entry = entries.get(0); + assertNotNull("No entry found", entry); + HarRequest req = entry.getRequest(); + assertNotNull("No request found", req); + // the HAR spec is not clear on what order the parameters should show up in. intuitively, since getQueryString() + // returns a List, the order should match the query string itself, but this is not technically required. + boolean sawFoo = false; + boolean sawA = false; + for (HarNameValuePair queryStringParam : req.getQueryString()) { + if (queryStringParam.getName().equals("foo")) { + assertEquals("expected 'foo' query param's value to be 'bar'", "bar", queryStringParam.getValue()); + sawFoo = true; + } else if (queryStringParam.getName().equals("a")) { + assertEquals("expected 'a' query param's value to be '1&2'", "1&2", queryStringParam.getValue()); + sawA = true; + } else { + fail("Unexpected query param: " + queryStringParam.getName() + ", value: " + queryStringParam.getValue()); + } + } + + assertTrue("did not find query param 'foo'", sawFoo); + assertTrue("did not find query param 'a'", sawA); + } + + @Test + public void testThatGzippedContentIsProperlyCapturedInHar() throws IOException, InterruptedException { + proxy.setCaptureContent(true); + proxy.newHar("Test"); + + // gzip all requests + server.forceGzip(); + + HttpGet get = new HttpGet(getLocalServerHostnameAndPort() + "/a.txt"); + get.addHeader("Accept-Encoding", "gzip"); + String body = IOUtils.toStringAndClose(new GZIPInputStream(client.execute(get).getEntity().getContent())); + + assertThat(body, containsString("this is a.txt")); Thread.sleep(500); - Har har = proxy.getHar(); - assertNotNull("Har is null", har); - HarLog log = har.getLog(); - assertNotNull("Log is null", log); - List entries = log.getEntries(); - assertNotNull("Entries are null", entries); - HarEntry entry = entries.get(0); - assertNotNull("No entry found", entry); - HarResponse response = entry.getResponse(); - assertNotNull("Response is null", response); - HarContent content = response.getContent(); - assertNotNull("Content is null", content); - String mime = content.getMimeType(); - assertEquals("Mime not matched", "text/plain", mime); - String text = content.getText(); - assertEquals("Text not matched", "this is a.txt", text); - } - - @Test - public void testHarPagesPopulated() throws IOException, InterruptedException { - proxy.newHar("testpage1"); - - HttpGet get = new HttpGet(getLocalServerHostnameAndPort() + "/a.txt"); - IOUtils.toStringAndClose(client.execute(get).getEntity().getContent()); - - proxy.endPage(); - - proxy.newPage("testpage2"); - - IOUtils.toStringAndClose(client.execute(get).getEntity().getContent()); - IOUtils.toStringAndClose(client.execute(get).getEntity().getContent()); - - proxy.endPage(); + Har har = proxy.getHar(); + assertNotNull("Har is null", har); + HarLog log = har.getLog(); + assertNotNull("Log is null", log); + List entries = log.getEntries(); + assertNotNull("Entries are null", entries); + HarEntry entry = entries.get(0); + assertNotNull("No entry found", entry); + HarResponse response = entry.getResponse(); + assertNotNull("Response is null", response); + HarContent content = response.getContent(); + assertNotNull("Content is null", content); + String mime = content.getMimeType(); + assertEquals("Mime not matched", "text/plain", mime); + String text = content.getText(); + assertEquals("Text not matched", "this is a.txt", text); + } + + @Test + public void testHarPagesPopulated() throws IOException, InterruptedException { + proxy.newHar("testpage1"); + + HttpGet get = new HttpGet(getLocalServerHostnameAndPort() + "/a.txt"); + IOUtils.toStringAndClose(client.execute(get).getEntity().getContent()); + + proxy.endPage(); + + proxy.newPage("testpage2"); + + IOUtils.toStringAndClose(client.execute(get).getEntity().getContent()); + IOUtils.toStringAndClose(client.execute(get).getEntity().getContent()); + + proxy.endPage(); Thread.sleep(500); - Har har = proxy.getHar(); - assertNotNull("Har is null", har); - HarLog log = har.getLog(); - assertNotNull("Log is null", log); - - List entries = log.getEntries(); - assertNotNull("Entries are null", entries); - - assertNotNull("har pages are null", log.getPages()); - assertEquals("expected 2 har pages", 2, log.getPages().size()); - - HarPage page1 = log.getPages().get(0); - assertEquals("incorrect har page id", "testpage1", page1.getId()); - assertEquals("incorrect har page title", page1.getId(), page1.getTitle()); - assertNotNull("har page timings are null", page1.getPageTimings()); - - HarPageTimings timings1 = page1.getPageTimings(); - assertNotNull("har page onLoad timing is null", timings1.getOnLoad()); - assertNotEquals("har page onLoad timing should be greater than 0", 0L, timings1.getOnLoad().longValue()); - - HarPage page2 = log.getPages().get(1); - assertEquals("incorrect har page id", "testpage2", page2.getId()); - assertEquals("incorrect har page id", page2.getId(), page2.getTitle()); - assertNotNull("har page timings are null", page2.getPageTimings()); - HarPageTimings timings2 = page2.getPageTimings(); - assertNotNull("har page onLoad timing is null", timings2.getOnLoad()); - assertNotEquals("har page onLoad timing should be greater than 0", 0L, timings2.getOnLoad().longValue()); - } + Har har = proxy.getHar(); + assertNotNull("Har is null", har); + HarLog log = har.getLog(); + assertNotNull("Log is null", log); + + List entries = log.getEntries(); + assertNotNull("Entries are null", entries); + + assertNotNull("har pages are null", log.getPages()); + assertEquals("expected 2 har pages", 2, log.getPages().size()); + + HarPage page1 = log.getPages().get(0); + assertEquals("incorrect har page id", "testpage1", page1.getId()); + assertEquals("incorrect har page title", page1.getId(), page1.getTitle()); + assertNotNull("har page timings are null", page1.getPageTimings()); + + HarPageTimings timings1 = page1.getPageTimings(); + assertNotNull("har page onLoad timing is null", timings1.getOnLoad()); + assertNotEquals("har page onLoad timing should be greater than 0", 0L, timings1.getOnLoad().longValue()); + + HarPage page2 = log.getPages().get(1); + assertEquals("incorrect har page id", "testpage2", page2.getId()); + assertEquals("incorrect har page id", page2.getId(), page2.getTitle()); + assertNotNull("har page timings are null", page2.getPageTimings()); + HarPageTimings timings2 = page2.getPageTimings(); + assertNotNull("har page onLoad timing is null", timings2.getOnLoad()); + assertNotEquals("har page onLoad timing should be greater than 0", 0L, timings2.getOnLoad().longValue()); + } @Test public void testHarPageTitlePopulated() throws Exception { @@ -359,79 +359,79 @@ public void testHarPageTitlePopulated() throws Exception { assertEquals("incorrect har page title", "Test Page 2", page2.getTitle()); } - @Test - public void testIpAddressPopulatedForLocalhost() throws IOException, InterruptedException { - proxy.newHar("testIpAddressPopulated"); + @Test + public void testIpAddressPopulatedForLocalhost() throws IOException, InterruptedException { + proxy.newHar("testIpAddressPopulated"); - HttpGet get = new HttpGet("http://localhost:8080/a.txt"); - IOUtils.toStringAndClose(client.execute(get).getEntity().getContent()); + HttpGet get = new HttpGet("http://localhost:8080/a.txt"); + IOUtils.toStringAndClose(client.execute(get).getEntity().getContent()); - proxy.endPage(); + proxy.endPage(); Thread.sleep(500); - Har har = proxy.getHar(); - assertNotNull("Har is null", har); - HarLog log = har.getLog(); - assertNotNull("Log is null", log); + Har har = proxy.getHar(); + assertNotNull("Har is null", har); + HarLog log = har.getLog(); + assertNotNull("Log is null", log); - List entries = log.getEntries(); - assertNotNull("Entries are null", entries); - assertThat("Entries are empty", entries, not(empty())); + List entries = log.getEntries(); + assertNotNull("Entries are null", entries); + assertThat("Entries are empty", entries, not(empty())); - HarEntry entry = log.getEntries().get(0); - assertNotNull("entry startedDateTime is null", entry.getStartedDateTime()); + HarEntry entry = log.getEntries().get(0); + assertNotNull("entry startedDateTime is null", entry.getStartedDateTime()); - assertEquals("entry pageref is incorrect", "testIpAddressPopulated", entry.getPageref()); + assertEquals("entry pageref is incorrect", "testIpAddressPopulated", entry.getPageref()); - assertEquals("entry ip address is not correct", "127.0.0.1", entry.getServerIPAddress()); + assertEquals("entry ip address is not correct", "127.0.0.1", entry.getServerIPAddress()); } - @Test - public void testIpAddressPopulatedForIpAddressUrl() throws IOException, InterruptedException { - proxy.newHar("testIpAddressPopulatedForIpAddressUrl"); + @Test + public void testIpAddressPopulatedForIpAddressUrl() throws IOException, InterruptedException { + proxy.newHar("testIpAddressPopulatedForIpAddressUrl"); HttpGet get = new HttpGet(getLocalServerHostnameAndPort() + "/a.txt"); - IOUtils.toStringAndClose(client.execute(get).getEntity().getContent()); + IOUtils.toStringAndClose(client.execute(get).getEntity().getContent()); - proxy.endPage(); + proxy.endPage(); Thread.sleep(500); - Har har = proxy.getHar(); - assertNotNull("Har is null", har); - HarLog log = har.getLog(); - assertNotNull("Log is null", log); + Har har = proxy.getHar(); + assertNotNull("Har is null", har); + HarLog log = har.getLog(); + assertNotNull("Log is null", log); - List entries = log.getEntries(); - assertNotNull("Entries are null", entries); - assertThat("Entries are empty", entries, not(empty())); + List entries = log.getEntries(); + assertNotNull("Entries are null", entries); + assertThat("Entries are empty", entries, not(empty())); - HarEntry entry = log.getEntries().get(0); - assertNotNull("entry startedDateTime is null", entry.getStartedDateTime()); + HarEntry entry = log.getEntries().get(0); + assertNotNull("entry startedDateTime is null", entry.getStartedDateTime()); - assertEquals("entry pageref is incorrect", "testIpAddressPopulatedForIpAddressUrl", entry.getPageref()); + assertEquals("entry pageref is incorrect", "testIpAddressPopulatedForIpAddressUrl", entry.getPageref()); - assertEquals("entry ip address is not correct", "127.0.0.1", entry.getServerIPAddress()); - } + assertEquals("entry ip address is not correct", "127.0.0.1", entry.getServerIPAddress()); + } - @Test - public void testNonChunkedRequestPayloadSizesAreSet() throws Exception { - proxy.setCaptureContent(true); - proxy.newHar("test"); + @Test + public void testNonChunkedRequestPayloadSizesAreSet() throws Exception { + proxy.setCaptureContent(true); + proxy.newHar("test"); - HttpPost post = new HttpPost(getLocalServerHostnameAndPort() + "/jsonrpc"); - String jsonRpcString = "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"test\",\"params\":{}}"; - HttpEntity entity = new StringEntity(jsonRpcString); - post.setEntity(entity); - post.addHeader("Accept", "application/json-rpc"); - post.addHeader("Content-Type", "application/json; charset=UTF-8"); + HttpPost post = new HttpPost(getLocalServerHostnameAndPort() + "/jsonrpc"); + String jsonRpcString = "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"test\",\"params\":{}}"; + HttpEntity entity = new StringEntity(jsonRpcString); + post.setEntity(entity); + post.addHeader("Accept", "application/json-rpc"); + post.addHeader("Content-Type", "application/json; charset=UTF-8"); - String body = IOUtils.toStringAndClose(client.execute(post).getEntity().getContent()); + String body = IOUtils.toStringAndClose(client.execute(post).getEntity().getContent()); Thread.sleep(500); - Har har = proxy.getHar(); - HarLog log = har.getLog(); - List entries = log.getEntries(); - HarEntry entry = entries.get(0); + Har har = proxy.getHar(); + HarLog log = har.getLog(); + List entries = log.getEntries(); + HarEntry entry = entries.get(0); /* Request headers should be something like this: @@ -439,40 +439,40 @@ public void testNonChunkedRequestPayloadSizesAreSet() throws Exception { Host: 127.0.0.1:8080 User-Agent: bmp.lightbody.net/2.0-beta-10-SNAPSHOT */ - assertThat("Minimum header size not seen", entry.getRequest().getHeadersSize(), greaterThan(70L)); - assertEquals("Body size does not match POST data size", jsonRpcString.length(), entry.getRequest().getBodySize()); - } + assertThat("Minimum header size not seen", entry.getRequest().getHeadersSize(), greaterThan(70L)); + assertEquals("Body size does not match POST data size", jsonRpcString.length(), entry.getRequest().getBodySize()); + } - @Test - public void testChunkedResponseBodySizeSet() throws Exception { - proxy.setCaptureContent(true); - proxy.newHar("test"); + @Test + public void testChunkedResponseBodySizeSet() throws Exception { + proxy.setCaptureContent(true); + proxy.newHar("test"); - HttpPost post = new HttpPost(getLocalServerHostnameAndPort() + "/echopayload"); - String lengthyPost = createRandomString(100000); + HttpPost post = new HttpPost(getLocalServerHostnameAndPort() + "/echopayload"); + String lengthyPost = createRandomString(100000); - HttpEntity entity = new StringEntity(lengthyPost); - post.setEntity(entity); - post.addHeader("Content-Type", "text/unknown; charset=UTF-8"); + HttpEntity entity = new StringEntity(lengthyPost); + post.setEntity(entity); + post.addHeader("Content-Type", "text/unknown; charset=UTF-8"); - String body = IOUtils.toStringAndClose(client.execute(post).getEntity().getContent()); + String body = IOUtils.toStringAndClose(client.execute(post).getEntity().getContent()); Thread.sleep(500); - Har har = proxy.getHar(); - HarLog log = har.getLog(); - List entries = log.getEntries(); - HarEntry entry = entries.get(0); - - assertEquals("Expected response size to equal the size of the echoed POST request", lengthyPost.length(), entry.getResponse().getBodySize()); - } - - private String createRandomString(int length) { - Random random = new Random(); - StringBuilder lengthyPost = new StringBuilder(length); - for (int i = 0; i < length; i++) { - lengthyPost.append((char)(random.nextInt(94) + 32)); - } - - return lengthyPost.toString(); - } + Har har = proxy.getHar(); + HarLog log = har.getLog(); + List entries = log.getEntries(); + HarEntry entry = entries.get(0); + + assertEquals("Expected response size to equal the size of the echoed POST request", lengthyPost.length(), entry.getResponse().getBodySize()); + } + + private String createRandomString(int length) { + Random random = new Random(); + StringBuilder lengthyPost = new StringBuilder(length); + for (int i = 0; i < length; i++) { + lengthyPost.append((char) (random.nextInt(94) + 32)); + } + + return lengthyPost.toString(); + } } diff --git a/browsermob-legacy/src/test/java/net/lightbody/bmp/proxy/MailingListIssuesTest.java b/browsermob-legacy/src/test/java/net/lightbody/bmp/proxy/MailingListIssuesTest.java index d99c5c07f..d9b4850d7 100644 --- a/browsermob-legacy/src/test/java/net/lightbody/bmp/proxy/MailingListIssuesTest.java +++ b/browsermob-legacy/src/test/java/net/lightbody/bmp/proxy/MailingListIssuesTest.java @@ -35,12 +35,7 @@ public void testThatInterceptorIsCalled() throws IOException, InterruptedExcepti assumeFalse(Boolean.getBoolean("bmp.use.littleproxy")); final boolean[] interceptorHit = {false}; - proxy.addRequestInterceptor(new RequestInterceptor() { - @Override - public void process(BrowserMobHttpRequest request, Har har) { - interceptorHit[0] = true; - } - }); + proxy.addRequestInterceptor((RequestInterceptor) (request, har) -> interceptorHit[0] = true); String body = IOUtils.toStringAndClose(client.execute(new HttpGet(getLocalServerHostnameAndPort() + "/a.txt")).getEntity().getContent()); diff --git a/browsermob-legacy/src/test/java/net/lightbody/bmp/proxy/test/util/LocalServer.java b/browsermob-legacy/src/test/java/net/lightbody/bmp/proxy/test/util/LocalServer.java index 58f93b8f4..30494f884 100644 --- a/browsermob-legacy/src/test/java/net/lightbody/bmp/proxy/test/util/LocalServer.java +++ b/browsermob-legacy/src/test/java/net/lightbody/bmp/proxy/test/util/LocalServer.java @@ -4,15 +4,21 @@ import net.lightbody.bmp.proxy.test.servlet.EchoServlet; import net.lightbody.bmp.proxy.test.servlet.JsonServlet; import net.lightbody.bmp.proxy.test.servlet.SetCookieServlet; +import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.GzipHandler; +import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.HandlerList; import org.eclipse.jetty.server.handler.ResourceHandler; +import org.eclipse.jetty.server.handler.gzip.GzipHandler; +import org.eclipse.jetty.servlet.DefaultServlet; +import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHandler; +import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.resource.Resource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.nio.file.Paths; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -42,17 +48,17 @@ public void start() { servletHandler.addServletWithMapping(EchoServlet.class, "/echo"); servletHandler.addServletWithMapping(EchoPayloadServlet.class, "/echopayload"); - handlers.addHandler(servletHandler); // create a ResourceHandler to serve up static resources from the classpath at /local-server ResourceHandler resourceHandler = new ResourceHandler(); resourceHandler.setBaseResource(Resource.newClassPathResource("/local-server")); - handlers.addHandler(resourceHandler); // wrap the other handlers in a GzipHandler that does not gzip anything by default GzipHandler gzipHandler = new GzipHandler(); gzipHandler.setMinGzipSize(Integer.MAX_VALUE); + handlers.addHandler(resourceHandler); + handlers.addHandler(servletHandler); gzipHandler.setHandler(handlers); server.setHandler(gzipHandler); @@ -63,7 +69,7 @@ public void start() { throw new RuntimeException("Could not start local Jetty server for tests", e); } - this.port = server.getConnectors()[0].getLocalPort(); + this.port = server.getURI().getPort(); started.set(true); } @@ -78,7 +84,7 @@ public int getPort() { } /** - * Forces the server to gzip all responses (see {@link org.eclipse.jetty.server.handler.GzipHandler} for response codes that will + * Forces the server to gzip all responses (see {@link org.eclipse.jetty.server.handler.gzip.GzipHandler} for response codes that will * be gzipped). */ public void forceGzip() { diff --git a/browsermob-rest/pom.xml b/browsermob-rest/pom.xml index 16499a206..6c01e56fe 100644 --- a/browsermob-rest/pom.xml +++ b/browsermob-rest/pom.xml @@ -5,7 +5,7 @@ browsermob-proxy net.lightbody.bmp - 2.1.6-SNAPSHOT + 2.1.38 4.0.0 @@ -40,19 +40,19 @@ com.google.inject guice - 3.0 + 4.2.3 com.google.inject.extensions guice-servlet - 3.0 + 4.2.3 com.google.inject.extensions guice-multibindings - 3.0 + 4.2.3 @@ -63,19 +63,19 @@ org.eclipse.jetty jetty-server - 7.3.0.v20110203 + 9.4.31.v20200723 org.eclipse.jetty jetty-servlet - 7.3.0.v20110203 + 9.4.31.v20200723 net.sf.jopt-simple jopt-simple - 3.2 + 5.0.4 @@ -167,7 +167,7 @@ org.hamcrest - hamcrest-library + hamcrest test diff --git a/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/ProxyManager.java b/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/ProxyManager.java index 6830f8196..5cb31fe18 100644 --- a/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/ProxyManager.java +++ b/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/ProxyManager.java @@ -35,6 +35,7 @@ public class ProxyManager { private int lastPort; private final int minPort; private final int maxPort; + private final int maxResponseSizeBytes; private final Provider proxyServerProvider; // retain a reference to the Cache to allow the ProxyCleanupTask to .cleanUp(), since asMap() is just a view into the cache. // it would seem to make sense to pass the newly-built Cache directly to the ProxyCleanupTask and have it retain a WeakReference to it, and @@ -93,24 +94,23 @@ public void run() { } @Inject - public ProxyManager(Provider proxyServerProvider, @Named("minPort") Integer minPort, @Named("maxPort") Integer maxPort, final @Named("ttl") Integer ttl) { + public ProxyManager(Provider proxyServerProvider, @Named("minPort") Integer minPort, @Named("maxPort") Integer maxPort, final @Named("ttl") Integer ttl, final @Named("maxResponseSizeBytes") Integer maxResponseSizeBytes) { this.proxyServerProvider = proxyServerProvider; this.minPort = minPort; this.maxPort = maxPort; this.lastPort = maxPort; + this.maxResponseSizeBytes = maxResponseSizeBytes; if (ttl > 0) { // proxies should be evicted after the specified ttl, so set up an evicting cache and a listener to stop the proxies when they're evicted - RemovalListener removalListener = new RemovalListener() { - public void onRemoval(RemovalNotification removal) { - try { - LegacyProxyServer proxy = removal.getValue(); - if (proxy != null) { - LOG.info("Expiring ProxyServer on port {} after {} seconds without activity", proxy.getPort(), ttl); - proxy.stop(); - } - } catch (Exception ex) { - LOG.warn("Error while stopping an expired proxy on port " + removal.getKey(), ex); + RemovalListener removalListener = removal -> { + try { + LegacyProxyServer proxy = removal.getValue(); + if (proxy != null) { + LOG.info("Expiring ProxyServer on port {} after {} seconds without activity", proxy.getPort(), ttl); + proxy.stop(); } + } catch (Exception ex) { + LOG.warn("Error while stopping an expired proxy on port " + removal.getKey(), ex); } }; diff --git a/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/bricks/ProxyResource.java b/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/bricks/ProxyResource.java index 07654db32..0737ef602 100644 --- a/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/bricks/ProxyResource.java +++ b/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/bricks/ProxyResource.java @@ -28,26 +28,18 @@ import net.lightbody.bmp.proxy.http.RequestInterceptor; import net.lightbody.bmp.proxy.http.ResponseInterceptor; import net.lightbody.bmp.util.BrowserMobHttpUtil; +import org.apache.commons.lang3.StringUtils; import org.java_bandwidthlimiter.StreamManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.script.Bindings; -import javax.script.Compilable; -import javax.script.CompiledScript; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; +import javax.script.*; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.Map; +import java.util.*; @At("/proxy") @Service @@ -279,12 +271,16 @@ public Reply updateHeaders(@Named("port") int port, Request request) return Reply.saying().notFound(); } - Map headers = request.read(Map.class).as(Json.class); - for (Map.Entry entry : headers.entrySet()) { - String key = entry.getKey(); - String value = entry.getValue(); - proxy.addHeader(key, value); - } + Map mappedJsonRequest = request.read(Map.class).as(Json.class); + mappedJsonRequest.entrySet().stream() + .filter(stringStringEntry -> !stringStringEntry.getKey().equalsIgnoreCase("headersFilterRegexp")) + .forEach(stringStringEntry -> proxy.addHeader(stringStringEntry.getKey(), stringStringEntry.getValue())); + + mappedJsonRequest.entrySet().stream().filter(stringStringEntry -> + stringStringEntry.getKey().equalsIgnoreCase("headersFilterRegexp") + && StringUtils.isNotEmpty(stringStringEntry.getValue())).findFirst() + .ifPresent(stringStringEntry -> proxy.headerFilterRegexp(stringStringEntry.getValue())); + return Reply.saying().ok(); } @@ -684,12 +680,14 @@ public void setPort(int port) { public static class ProxyListDescriptor { private Collection proxyList; + private int numberOfProxies; public ProxyListDescriptor() { } public ProxyListDescriptor(Collection proxyList) { this.proxyList = proxyList; + this.numberOfProxies = proxyList.size(); } public Collection getProxyList() { @@ -699,6 +697,15 @@ public Collection getProxyList() { public void setProxyList(Collection proxyList) { this.proxyList = proxyList; } + + public int getNumberOfProxies() { + return numberOfProxies; + } + + public ProxyListDescriptor setNumberOfProxies(int numberOfProxies) { + this.numberOfProxies = numberOfProxies; + return this; + } } public static class BandwidthLimitDescriptor { diff --git a/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/ConfigModule.java b/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/ConfigModule.java index bf20e31b2..b988acf50 100644 --- a/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/ConfigModule.java +++ b/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/ConfigModule.java @@ -4,7 +4,6 @@ import com.google.inject.Binder; import com.google.inject.Key; import com.google.inject.Module; -import com.google.inject.Provider; import joptsimple.ArgumentAcceptingOptionSpec; import joptsimple.OptionParser; import joptsimple.OptionSet; @@ -29,31 +28,37 @@ public void configure(Binder binder) { ArgumentAcceptingOptionSpec portSpec = parser.accepts("port", "The port to listen on") .withOptionalArg().ofType(Integer.class).defaultsTo(8080); - + ArgumentAcceptingOptionSpec addressSpec = parser.accepts("address", "The address to bind to") - .withOptionalArg() - .ofType(String.class) - .defaultsTo("0.0.0.0"); - + .withOptionalArg() + .ofType(String.class) + .defaultsTo("0.0.0.0"); + ArgumentAcceptingOptionSpec proxyPortRange = parser.accepts("proxyPortRange", "The range of ports to use for proxies") - .withOptionalArg() - .ofType(Integer.class) - .defaultsTo(8081, 8581) - .withValuesSeparatedBy('-'); + .withOptionalArg() + .ofType(Integer.class) + .defaultsTo(8081, 8581) + .withValuesSeparatedBy('-'); ArgumentAcceptingOptionSpec ttlSpec = parser.accepts("ttl", "Time in seconds until an unused proxy is deleted") - .withOptionalArg() - .ofType(Integer.class) - .defaultsTo(0); + .withOptionalArg() + .ofType(Integer.class) + .defaultsTo(0); + + ArgumentAcceptingOptionSpec maxResponseSizeSpec = + parser.accepts("maxResponseSizeBytes", "Max response size") + .withOptionalArg() + .ofType(Integer.class) + .defaultsTo(2097152); ArgumentAcceptingOptionSpec useLittleProxy = parser.accepts("use-littleproxy", "Use the littleproxy backend instead of the legacy Jetty 5-based implementation") - .withOptionalArg() - .ofType(Boolean.class) - .defaultsTo(true); + .withOptionalArg() + .ofType(Boolean.class) + .defaultsTo(true); parser.acceptsAll(Arrays.asList("help", "?"), "This help text"); @@ -78,44 +83,44 @@ public void configure(Binder binder) { System.out.println("Running BrowserMob Proxy using legacy implementation."); } - List ports = options.valuesOf(proxyPortRange); - if(ports.size() < 2){ + List ports = options.valuesOf(proxyPortRange); + if (ports.size() < 2) { throw new IllegalArgumentException(); } Integer minPort; - Integer maxPort; - if(ports.get(1) > ports.get(0)){ + Integer maxPort; + if (ports.get(1) > ports.get(0)) { minPort = ports.get(0); maxPort = ports.get(1); - }else{ + } else { minPort = ports.get(1); maxPort = ports.get(0); - } + } Integer port = portSpec.value(options); - if(port >= minPort && port <= maxPort){ + if (port >= minPort && port <= maxPort) { int num = maxPort - minPort; minPort = port + 1; maxPort = minPort + num; } + System.setProperty("maxResponseSizeBytes", String.valueOf(maxResponseSizeSpec.value(options))); + binder.bind(Key.get(Integer.class, new NamedImpl("port"))).toInstance(port); binder.bind(Key.get(String.class, new NamedImpl("address"))).toInstance(addressSpec.value(options)); binder.bind(Key.get(Integer.class, new NamedImpl("minPort"))).toInstance(minPort); - binder.bind(Key.get(Integer.class, new NamedImpl("maxPort"))).toInstance(maxPort); + binder.bind(Key.get(Integer.class, new NamedImpl("maxPort"))).toInstance(maxPort); binder.bind(Key.get(Integer.class, new NamedImpl("ttl"))).toInstance(ttlSpec.value(options)); + binder.bind(Key.get(Integer.class, new NamedImpl("maxResponseSizeBytes"))).toInstance(maxResponseSizeSpec.value(options)); binder.bind(LegacyProxyServer.class).toProvider(LegacyProxyServerProvider.class); // bind an ObjectMapper provider that uses the system time zone instead of UTC by default - binder.bind(ObjectMapper.class).toProvider(new Provider() { - @Override - public ObjectMapper get() { - ObjectMapper objectMapper = new ObjectMapper(); + binder.bind(ObjectMapper.class).toProvider(() -> { + ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.setTimeZone(TimeZone.getDefault()); + objectMapper.setTimeZone(TimeZone.getDefault()); - return objectMapper; - } + return objectMapper; }); } } diff --git a/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/JettyServerProvider.java b/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/JettyServerProvider.java index 4584620ca..2fca0da09 100644 --- a/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/JettyServerProvider.java +++ b/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/JettyServerProvider.java @@ -4,13 +4,16 @@ import com.google.inject.Provider; import com.google.inject.name.Named; import com.google.inject.servlet.GuiceFilter; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.UnknownHostException; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletContextHandler; +import javax.servlet.DispatcherType; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.EnumSet; + public class JettyServerProvider implements Provider { private Server server; @@ -22,7 +25,7 @@ public JettyServerProvider(@Named("port") int port, @Named("address") String add ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/"); - context.addFilter(GuiceFilter.class, "/*", 0); + context.addFilter(GuiceFilter.class, "/*", EnumSet.allOf(DispatcherType.class)); context.addServlet(DefaultServlet.class, "/"); server.setHandler(context); diff --git a/browsermob-rest/src/test/java/net/lightbody/bmp/proxy/ExpiringProxyTest.java b/browsermob-rest/src/test/java/net/lightbody/bmp/proxy/ExpiringProxyTest.java index 85eacc06c..e7cafe652 100644 --- a/browsermob-rest/src/test/java/net/lightbody/bmp/proxy/ExpiringProxyTest.java +++ b/browsermob-rest/src/test/java/net/lightbody/bmp/proxy/ExpiringProxyTest.java @@ -1,5 +1,6 @@ package net.lightbody.bmp.proxy; +import net.lightbody.bmp.filters.ResponseFilterAdapter; import net.lightbody.bmp.proxy.guice.LegacyProxyServerProvider; import org.junit.Test; @@ -17,7 +18,7 @@ public void testExpiredProxyStops() throws InterruptedException { ProxyManager proxyManager = new ProxyManager(new LegacyProxyServerProvider(), minPort, minPort + 100, - 2); + 2, ResponseFilterAdapter.FilterSource.DEFAULT_MAXIMUM_RESPONSE_BUFFER_SIZE); LegacyProxyServer proxy = proxyManager.create(Collections.emptyMap()); int port = proxy.getPort(); @@ -44,7 +45,7 @@ public void testZeroTtlProxyDoesNotExpire() throws InterruptedException { ProxyManager proxyManager = new ProxyManager(new LegacyProxyServerProvider(), minPort, minPort + 100, - 0); + 0, ResponseFilterAdapter.FilterSource.DEFAULT_MAXIMUM_RESPONSE_BUFFER_SIZE); LegacyProxyServer proxy = proxyManager.create(Collections.emptyMap()); int port = proxy.getPort(); diff --git a/mitm/README.md b/mitm/README.md index d37ad8da3..878eedaa4 100644 --- a/mitm/README.md +++ b/mitm/README.md @@ -17,7 +17,7 @@ To use MITM with standalone LittleProxy, add a dependency on the `mitm` module i org.littleshoot littleproxy - 1.1.2 + 1.1.3 diff --git a/mitm/pom.xml b/mitm/pom.xml index ca0f5ecdb..a0738f622 100644 --- a/mitm/pom.xml +++ b/mitm/pom.xml @@ -1,9 +1,10 @@ - + browsermob-proxy net.lightbody.bmp - 2.1.6-SNAPSHOT + 2.1.38 4.0.0 @@ -14,9 +15,8 @@ - net.lightbody.bmp + org.littleshoot littleproxy - 1.1.0-beta-bmp-17 true @@ -88,7 +88,7 @@ org.hamcrest - hamcrest-library + hamcrest test @@ -111,5 +111,4 @@ - \ No newline at end of file diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/util/SslUtil.java b/mitm/src/main/java/net/lightbody/bmp/mitm/util/SslUtil.java index 198f194e1..a329eda68 100644 --- a/mitm/src/main/java/net/lightbody/bmp/mitm/util/SslUtil.java +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/util/SslUtil.java @@ -3,10 +3,7 @@ import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.io.CharStreams; -import io.netty.handler.ssl.OpenSsl; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.SupportedCipherSuiteFilter; +import io.netty.handler.ssl.*; import net.lightbody.bmp.mitm.trustmanager.InsecureTrustManagerFactory; import net.lightbody.bmp.mitm.TrustSource; import net.lightbody.bmp.mitm.exception.SslContextInitializationException; @@ -74,12 +71,12 @@ public List get() { * supply an appropriate trustSource except in extraordinary circumstances (e.g. testing with dynamically-generated * certificates). * - * @param cipherSuites cipher suites to allow when connecting to the upstream server - * @param trustSource the trust store that will be used to validate upstream servers' certificates, or null to accept all upstream server certificates + * @param cipherSuites cipher suites to allow when connecting to the upstream server + * @param trustSource the trust store that will be used to validate upstream servers' certificates, or null to accept all upstream server certificates * @return an SSLContext to connect to upstream servers with */ public static SslContext getUpstreamServerSslContext(Collection cipherSuites, TrustSource trustSource) { - SslContextBuilder sslContextBuilder = SslContextBuilder.forClient(); + SslContextBuilder sslContextBuilder = SslContextBuilder.forClient().sslProvider(SslProvider.JDK); if (trustSource == null) { log.warn("Disabling upstream server certificate verification. This will allow attackers to intercept communications with upstream servers."); diff --git a/mitm/src/main/java/net/lightbody/bmp/util/HttpUtil.java b/mitm/src/main/java/net/lightbody/bmp/util/HttpUtil.java index 2cc2a2158..3e927df0c 100644 --- a/mitm/src/main/java/net/lightbody/bmp/util/HttpUtil.java +++ b/mitm/src/main/java/net/lightbody/bmp/util/HttpUtil.java @@ -1,6 +1,7 @@ package net.lightbody.bmp.util; import com.google.common.net.HostAndPort; +import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpRequest; @@ -24,9 +25,9 @@ public static String getHostFromRequest(HttpRequest httpRequest) { // try to use the URI from the request first, if the URI starts with http:// or https://. checking for http/https avoids confusing // java's URI class when the request is for a malformed URL like '//some-resource'. String host = null; - if (startsWithHttpOrHttps(httpRequest.getUri())) { + if (startsWithHttpOrHttps(httpRequest.uri())) { try { - URI uri = new URI(httpRequest.getUri()); + URI uri = new URI(httpRequest.uri()); host = uri.getHost(); } catch (URISyntaxException e) { } @@ -48,9 +49,9 @@ public static String getHostFromRequest(HttpRequest httpRequest) { * @return host and port of the request */ public static String getHostAndPortFromRequest(HttpRequest httpRequest) { - if (startsWithHttpOrHttps(httpRequest.getUri())) { + if (startsWithHttpOrHttps(httpRequest.uri())) { try { - return getHostAndPortFromUri(httpRequest.getUri()); + return getHostAndPortFromUri(httpRequest.uri()); } catch (URISyntaxException e) { // the URI could not be parsed, so return the host and port in the Host header } @@ -106,7 +107,7 @@ public static String getHostAndPortFromUri(String uriString) throws URISyntaxExc */ private static String parseHostHeader(HttpRequest httpRequest, boolean includePort) { // this header parsing logic is adapted from ClientToProxyConnection#identifyHostAndPort. - List hosts = httpRequest.headers().getAll(HttpHeaders.Names.HOST); + List hosts = httpRequest.headers().getAll(HttpHeaderNames.HOST); if (!hosts.isEmpty()) { String hostAndPort = hosts.get(0); diff --git a/mitm/src/main/resources/default-ciphers.txt b/mitm/src/main/resources/default-ciphers.txt index 2295941f6..027acd018 100644 --- a/mitm/src/main/resources/default-ciphers.txt +++ b/mitm/src/main/resources/default-ciphers.txt @@ -1,29 +1,53 @@ -TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 -TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 -TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 -TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 -TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 +TLS_RSA_WITH_NULL_SHA +TLS_RSA_WITH_3DES_EDE_CBC_SHA +TLS_RSA_WITH_AES_128_GCM_SHA256 +TLS_RSA_WITH_AES_128_CBC_SHA +TLS_RSA_WITH_AES_128_CBC_SHA256 +TLS_RSA_WITH_AES_256_GCM_SHA384 +TLS_RSA_WITH_AES_256_CBC_SHA +TLS_RSA_WITH_AES_256_CBC_SHA256 +TLS_RSA_WITH_CAMELLIA_128_CBC_SHA +TLS_RSA_WITH_CAMELLIA_256_CBC_SHA +TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 +TLS_DHE_DSS_WITH_AES_128_CBC_SHA +TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 +TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 +TLS_DHE_DSS_WITH_AES_256_CBC_SHA +TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 +TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA +TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 -TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 -TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 -TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 -TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 +TLS_DHE_RSA_WITH_AES_128_CBC_SHA +TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 +TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 +TLS_DHE_RSA_WITH_AES_256_CBC_SHA +TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 +TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA +TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA +TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 +TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA +TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 +TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 +TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA +TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 +TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 +TLS_ECDH_RSA_WITH_AES_128_CBC_SHA +TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 +TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 +TLS_ECDH_RSA_WITH_AES_256_CBC_SHA +TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 +TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 +TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 -TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 -TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 +TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA -TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA -TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA +TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 +TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA -TLS_DHE_RSA_WITH_AES_256_CBC_SHA -TLS_DHE_RSA_WITH_AES_128_CBC_SHA -TLS_RSA_WITH_AES_256_GCM_SHA384 -TLS_RSA_WITH_AES_256_GCM_SHA384 -TLS_RSA_WITH_AES_128_GCM_SHA256 -TLS_RSA_WITH_AES_256_CBC_SHA -TLS_RSA_WITH_AES_128_CBC_SHA -SSL_RSA_WITH_3DES_EDE_CBC_SHA -TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 -TLS_DHE_DSS_WITH_AES_256_CBC_SHA -TLS_DHE_DSS_WITH_AES_128_CBC_SHA - +TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 +TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 +TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA +TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 +TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 +TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 +TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 \ No newline at end of file diff --git a/mitm/src/test/java/net/lightbody/bmp/mitm/integration/LittleProxyIntegrationTest.java b/mitm/src/test/java/net/lightbody/bmp/mitm/integration/LittleProxyIntegrationTest.java index 19e30ec57..2412342d3 100644 --- a/mitm/src/test/java/net/lightbody/bmp/mitm/integration/LittleProxyIntegrationTest.java +++ b/mitm/src/test/java/net/lightbody/bmp/mitm/integration/LittleProxyIntegrationTest.java @@ -8,24 +8,19 @@ import org.apache.http.HttpHost; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; +import org.apache.http.conn.ssl.DefaultHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; -import org.apache.http.conn.ssl.SSLContexts; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.SSLContexts; import org.apache.http.util.EntityUtils; import org.junit.Test; -import org.littleshoot.proxy.HttpFilters; -import org.littleshoot.proxy.HttpFiltersAdapter; -import org.littleshoot.proxy.HttpFiltersSource; -import org.littleshoot.proxy.HttpFiltersSourceAdapter; -import org.littleshoot.proxy.HttpProxyServer; +import org.littleshoot.proxy.*; import org.littleshoot.proxy.impl.DefaultHttpProxyServer; import javax.net.ssl.SSLContext; import java.io.IOException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; import java.util.concurrent.atomic.AtomicBoolean; import static org.junit.Assert.assertEquals; @@ -48,7 +43,7 @@ public HttpFilters filterRequest(HttpRequest originalRequest) { public HttpResponse proxyToServerRequest(HttpObject httpObject) { if (httpObject instanceof HttpRequest) { HttpRequest httpRequest = (HttpRequest) httpObject; - if (httpRequest.getMethod().equals(HttpMethod.GET)) { + if (httpRequest.method().equals(HttpMethod.GET)) { interceptedGetRequest.set(true); } } @@ -60,7 +55,7 @@ public HttpResponse proxyToServerRequest(HttpObject httpObject) { public HttpObject serverToProxyResponse(HttpObject httpObject) { if (httpObject instanceof HttpResponse) { HttpResponse httpResponse = (HttpResponse) httpObject; - if (httpResponse.getStatus().code() == 200) { + if (httpResponse.status().code() == 200) { interceptedGetResponse.set(true); } } @@ -100,19 +95,13 @@ public HttpObject serverToProxyResponse(HttpObject httpObject) { private static CloseableHttpClient getNewHttpClient(int proxyPort) { try { // Trust all certs -- under no circumstances should this ever be used outside of testing - SSLContext sslcontext = SSLContexts.custom() - .useTLS() - .loadTrustMaterial(null, new TrustStrategy() { - @Override - public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { - return true; - } - }) + SSLContext sslcontext = SSLContexts.custom().setProtocol("TLS") + .loadTrustMaterial(null, (TrustStrategy) (chain, authType) -> true) .build(); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslcontext, - SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + new DefaultHostnameVerifier()); CloseableHttpClient httpclient = HttpClients.custom() .setSSLSocketFactory(sslsf) diff --git a/pom.xml b/pom.xml index 2bd63686e..abd7e82b9 100644 --- a/pom.xml +++ b/pom.xml @@ -1,8 +1,9 @@ - + 4.0.0 net.lightbody.bmp browsermob-proxy - 2.1.6-SNAPSHOT + 2.1.38 browsermob-core browsermob-legacy @@ -46,38 +47,44 @@ HEAD - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - + + + bintray + Groovy Bintray + https://dl.bintray.com/groovy/maven + + never + + + false + + + UTF-8 UTF-8 - 1.7.25 - 2.53.1 + 1.7.28 + 3.141.59 - 2.8.9 + 2.10.0 + 1.13.0 - 3.0.2 + 3.1.1 - 2.9.0 + 2.13.3 - 2.4.12 - 2.4.3-01 + 3.0.5 + 2.5.8-02 - 4.0.51.Final + 4.1.51.Final - 4.1.15.Final - 1.58 + 1.66 + 3.0.1 + 11 + -Xdoclint:none @@ -87,7 +94,7 @@ org.apache.maven.plugins maven-clean-plugin - 3.0.0 + 3.1.0 org.apache.maven.plugins @@ -97,22 +104,22 @@ org.apache.maven.plugins maven-compiler-plugin - 3.6.1 + 3.8.1 - groovy-eclipse-compiler - 1.7 - 1.7 + ${java.version} + ${java.version} + UTF-8 org.codehaus.groovy groovy-eclipse-compiler - 2.9.2-01 + 3.3.0-01 org.codehaus.groovy groovy-eclipse-batch - ${groovy-eclipse-batch.version} + 2.5.6-01 @@ -140,13 +147,13 @@ maven-surefire-plugin 2.19.1 - -Xmx1g -XX:MaxPermSize=256m + -Xmx1g org.apache.maven.plugins maven-javadoc-plugin - 2.10.4 + 3.1.1 attach-javadocs @@ -155,7 +162,7 @@ jar - ${javadoc.opts} + ${additionalJOption} @@ -163,12 +170,30 @@ org.apache.maven.plugins maven-install-plugin - 2.5.2 + 3.0.0-M1 + + + install-external + false + install + + ${basedir}/resources/jar/littleproxy-1.1.6.jar + default + org.littleshoot + littleproxy + 1.1.6 + jar + + + install-file + + + org.apache.maven.plugins maven-deploy-plugin - 2.8.2 + 3.0.0-M1 org.apache.maven.plugins @@ -178,7 +203,7 @@ org.apache.maven.plugins maven-dependency-plugin - 3.0.0 + 3.1.1 @@ -208,11 +233,22 @@ ${slf4j.version} + + biz.paluch.logging + logstash-gelf + ${logstash-gelf.version} + + + + co.elastic.logging + log4j2-ecs-layout + 0.5.0 + + com.google.guava guava - - 23.0-android + 28.1-jre @@ -239,14 +275,19 @@ org.hamcrest - hamcrest-library - 1.3 + hamcrest + 2.1 org.mockito mockito-core - 2.8.47 + 3.5.2 + + + net.bytebuddy + byte-buddy + 1.10.14 @@ -268,18 +309,20 @@ org.codehaus.groovy groovy-all ${groovy.version} + pom - net.lightbody.bmp + org.littleshoot littleproxy - 1.1.0-beta-bmp-17 + 1.1.6 + org.mock-server mockserver-netty - 3.10.4 + 5.11.1 ch.qos.logback @@ -315,13 +358,13 @@ org.apache.httpcomponents httpclient - 4.5.3 + 4.5.12 org.apache.httpcomponents httpmime - 4.5.3 + 4.5.12 @@ -411,10 +454,16 @@ ${bouncycastle.version} + + com.timgroup + java-statsd-client + ${java-statsd-client.version} + + org.javassist javassist - 3.21.0-GA + 3.26.0-GA @@ -424,7 +473,8 @@ org.codehaus.groovy groovy-all - test + 2.5.8 + pom @@ -446,7 +496,7 @@ org.apache.maven.plugins maven-surefire-plugin - 2.19.1 + 3.0.0-M3 true @@ -454,7 +504,7 @@ org.apache.maven.plugins maven-source-plugin - 3.0.1 + 3.1.0 attach-sources @@ -468,7 +518,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.10.4 + 3.1.1 attach-javadocs @@ -499,7 +549,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.7 + 1.6.8 true ossrh @@ -523,21 +573,5 @@ - - doclint-java8-disable - - [1.8,) - - - -Xdoclint:none - - - - - netty-4.1 - - ${netty-4.1.version} - - diff --git a/resources/jar/littleproxy-1.1.6.jar b/resources/jar/littleproxy-1.1.6.jar new file mode 100644 index 000000000..927e769a3 Binary files /dev/null and b/resources/jar/littleproxy-1.1.6.jar differ