Skip to content

Commit c81c0fc

Browse files
committed
Merge branch 'enable_offline_caching_with_mitm' into mitm_offline
Conflicts: src/main/java/org/littleshoot/proxy/MitmManager.java src/main/java/org/littleshoot/proxy/extras/SelfSignedMitmManager.java src/main/java/org/littleshoot/proxy/impl/ProxyToServerConnection.java
2 parents c051fd0 + 3424eb2 commit c81c0fc

14 files changed

+240
-115
lines changed

README.md

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[![Build Status](https://travis-ci.org/adamfisk/LittleProxy.png?branch=master)](https://travis-ci.org/adamfisk/LittleProxy)
1+
[![Build Status](https://travis-ci.org/ganskef/LittleProxy-parent.png?branch=master)](https://travis-ci.org/ganskef/LittleProxy-parent)
22

33
LittleProxy is a high performance HTTP proxy written in Java atop Trustin Lee's excellent [Netty](netty.io) event-based networking library. It's quite stable, performs well, and is easy to integrate into your projects.
44

@@ -16,7 +16,7 @@ You can embed LittleProxy in your own projects through Maven with the following:
1616
<dependency>
1717
<groupId>org.littleshoot</groupId>
1818
<artifactId>littleproxy</artifactId>
19-
<version>1.1.0-beta1</version>
19+
<version>1.1.0-beta2</version>
2020
</dependency>
2121
```
2222

@@ -38,18 +38,19 @@ HttpProxyServer server =
3838
.withPort(8080)
3939
.withFiltersSource(new HttpFiltersSourceAdapter() {
4040
public HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerContext ctx) {
41-
return new HttpFiltersAdapter(originalRequest) {
42-
@Override
43-
public HttpResponse clientToProxyRequest(HttpObject httpObject) {
44-
// TODO: implement your filtering here
45-
return null;
46-
}
47-
48-
@Override
49-
public HttpObject serverToProxyResponse(HttpObject httpObject) {
50-
// TODO: implement your filtering here
51-
return httpObject;
52-
}
41+
return new HttpFiltersAdapter(originalRequest) {
42+
@Override
43+
public HttpResponse clientToProxyRequest(HttpObject httpObject) {
44+
// TODO: implement your filtering here
45+
return null;
46+
}
47+
48+
@Override
49+
public HttpObject serverToProxyResponse(HttpObject httpObject) {
50+
// TODO: implement your filtering here
51+
return httpObject;
52+
}
53+
};
5354
}
5455
})
5556
.start();

pom.xml

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<groupId>org.littleshoot</groupId>
44
<artifactId>littleproxy</artifactId>
55
<packaging>jar</packaging>
6-
<version>1.1.0-beta2-SNAPSHOT</version>
6+
<version>1.1.0-beta3-offline</version>
77
<name>LittleProxy</name>
88
<description>
99
LittleProxy is a high performance HTTP proxy written in Java and using the Netty networking framework.
@@ -14,7 +14,8 @@
1414
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
1515
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
1616
<github.global.server>github</github.global.server>
17-
<netty.version>4.0.23.Final</netty.version>
17+
<netty.version>4.0.35.Final</netty.version>
18+
<slf4j.version>1.7.19</slf4j.version>
1819
</properties>
1920

2021
<organization>
@@ -83,7 +84,7 @@
8384
<profile>
8485
<id>netty-4.1</id>
8586
<properties>
86-
<netty.version>4.1.0.Beta6</netty.version>
87+
<netty.version>4.1.0.CR4</netty.version>
8788
</properties>
8889
</profile>
8990
</profiles>
@@ -150,14 +151,14 @@
150151
<dependency>
151152
<groupId>org.mockito</groupId>
152153
<artifactId>mockito-core</artifactId>
153-
<version>2.0.31-beta</version>
154+
<version>2.0.44-beta</version>
154155
<scope>test</scope>
155156
</dependency>
156157

157158
<dependency>
158159
<groupId>org.mock-server</groupId>
159160
<artifactId>mockserver-netty</artifactId>
160-
<version>3.9.17</version>
161+
<version>3.10.4</version>
161162
<scope>test</scope>
162163
<exclusions>
163164
<exclusion>
@@ -190,7 +191,7 @@
190191
<dependency>
191192
<groupId>org.apache.httpcomponents</groupId>
192193
<artifactId>httpclient</artifactId>
193-
<version>4.5</version>
194+
<version>4.5.2</version>
194195
<scope>test</scope>
195196
</dependency>
196197

@@ -228,14 +229,14 @@
228229
<dependency>
229230
<groupId>org.slf4j</groupId>
230231
<artifactId>slf4j-log4j12</artifactId>
231-
<version>1.7.12</version>
232+
<version>${slf4j.version}</version>
232233
<optional>true</optional>
233234
</dependency>
234235

235236
<dependency>
236237
<groupId>org.slf4j</groupId>
237238
<artifactId>slf4j-api</artifactId>
238-
<version>1.7.12</version>
239+
<version>${slf4j.version}</version>
239240
</dependency>
240241

241242
<!-- Test dependencies -->

src/main/java/org/littleshoot/proxy/HttpFiltersAdapter.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
* Convenience base class for implementations of {@link HttpFilters}.
1212
*/
1313
public class HttpFiltersAdapter implements HttpFilters {
14+
/**
15+
* A default, stateless, no-op {@link HttpFilters} instance.
16+
*/
17+
public static final HttpFiltersAdapter NOOP_FILTER = new HttpFiltersAdapter(null);
18+
1419
protected final HttpRequest originalRequest;
1520
protected final ChannelHandlerContext ctx;
1621

src/main/java/org/littleshoot/proxy/HttpProxyServer.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ public interface HttpProxyServer {
1111

1212
void setIdleConnectionTimeout(int idleConnectionTimeout);
1313

14+
/**
15+
* Returns the maximum time to wait, in milliseconds, to connect to a server.
16+
*/
17+
int getConnectTimeout();
18+
19+
/**
20+
* Sets the maximum time to wait, in milliseconds, to connect to a server.
21+
*/
22+
void setConnectTimeout(int connectTimeoutMs);
23+
1424
/**
1525
* <p>
1626
* Clone the existing server, with a port 1 higher and everything else the
Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.littleshoot.proxy;
22

3+
import io.netty.handler.codec.http.HttpRequest;
4+
35
import javax.net.ssl.SSLEngine;
46
import javax.net.ssl.SSLSession;
57

@@ -9,20 +11,23 @@
911
*/
1012
public interface MitmManager {
1113
/**
12-
* Creates an {@link SSLEngine} for encrypting the server connection.
13-
*
14-
* Note: Peer information is needed to send the server_name extension in
15-
* handshake with Server Name Indication (SNI).
14+
* Creates an {@link SSLEngine} for encrypting the server connection. The SSLEngine created by this method
15+
* may use the given peer information to send SNI information when connecting to the upstream host.
16+
*
17+
* @param peerHost to start a client connection to the server.
18+
* @param peerPort to start a client connection to the server.
1619
*
17-
* @param peerHost
18-
* to start a client connection to the server.
19-
* @param peerPort
20-
* to start a client connection to the server.
21-
*
22-
* @return
20+
* @return an SSLEngine used to connect to an upstream server
2321
*/
2422
SSLEngine serverSslEngine(String peerHost, int peerPort);
2523

24+
/**
25+
* Creates an {@link SSLEngine} for encrypting the server connection.
26+
*
27+
* @return an SSLEngine used to connect to an upstream server
28+
*/
29+
SSLEngine serverSslEngine();
30+
2631
/**
2732
* <p>
2833
* Creates an {@link SSLEngine} for encrypting the client connection based
@@ -40,14 +45,10 @@ public interface MitmManager {
4045
* by issuing replacement certificates signed by the proxy's own
4146
* certificate.
4247
* </p>
43-
*
44-
* @param serverSslSession
45-
* the {@link SSLSession} that's been established with the server
46-
* @param serverHostAndPort
47-
* the server host name, optionally with port, to create the
48-
* dynamic certificate for
49-
* @return
48+
*
49+
* @param httpRequest the HTTP CONNECT request that is being man-in-the-middled
50+
* @param serverSslSession the {@link SSLSession} that's been established with the server
51+
* @return the SSLEngine used to connect to the client
5052
*/
51-
SSLEngine clientSslEngineFor(SSLSession serverSslSession,
52-
String serverHostAndPort);
53+
SSLEngine clientSslEngineFor(HttpRequest httpRequest, SSLSession serverSslSession);
5354
}

src/main/java/org/littleshoot/proxy/ProxyAuthenticator.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,13 @@ public interface ProxyAuthenticator {
1616
* <code>false</code>.
1717
*/
1818
boolean authenticate(String userName, String password);
19+
20+
/**
21+
* The realm value to be used in the request for proxy authentication
22+
* ("Proxy-Authenticate" header). Returning null will cause the string
23+
* "Restricted Files" to be used by default.
24+
*
25+
* @return
26+
*/
27+
String getRealm();
1928
}

src/main/java/org/littleshoot/proxy/extras/SelfSignedMitmManager.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package org.littleshoot.proxy.extras;
22

3+
import io.netty.handler.codec.http.HttpRequest;
4+
import org.littleshoot.proxy.MitmManager;
5+
36
import javax.net.ssl.SSLEngine;
47
import javax.net.ssl.SSLSession;
58

6-
import org.littleshoot.proxy.MitmManager;
7-
89
/**
910
* {@link MitmManager} that uses self-signed certs for everything.
1011
*/
@@ -18,8 +19,12 @@ public SSLEngine serverSslEngine(String peerHost, int peerPort) {
1819
}
1920

2021
@Override
21-
public SSLEngine clientSslEngineFor(SSLSession serverSslSession, String
22-
serverHostAndPort) {
22+
public SSLEngine serverSslEngine() {
23+
return selfSignedSslEngineSource.newSslEngine();
24+
}
25+
26+
@Override
27+
public SSLEngine clientSslEngineFor(HttpRequest httpRequest, SSLSession serverSslSession) {
2328
return selfSignedSslEngineSource.newSslEngine();
2429
}
2530
}

src/main/java/org/littleshoot/proxy/impl/ClientToProxyConnection.java

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import java.io.IOException;
3737
import java.net.InetSocketAddress;
3838
import java.net.UnknownHostException;
39-
import java.nio.channels.ClosedChannelException;
4039
import java.nio.charset.Charset;
4140
import java.util.Date;
4241
import java.util.List;
@@ -124,7 +123,7 @@ public class ClientToProxyConnection extends ProxyConnection<HttpRequest> {
124123
/**
125124
* The current filters to apply to incoming requests/chunks.
126125
*/
127-
private volatile HttpFilters currentFilters = new HttpFiltersAdapter(null);
126+
private volatile HttpFilters currentFilters = HttpFiltersAdapter.NOOP_FILTER;
128127

129128
private volatile SSLSession clientSslSession;
130129

@@ -220,6 +219,8 @@ private ConnectionState doReadHTTPInitial(HttpRequest httpRequest) {
220219
HttpFilters filterInstance = proxyServer.getFiltersSource().filterRequest(currentRequest, ctx);
221220
if (filterInstance != null) {
222221
currentFilters = filterInstance;
222+
} else {
223+
currentFilters = HttpFiltersAdapter.NOOP_FILTER;
223224
}
224225

225226
// Send the request through the clientToProxyRequest filter, and respond with the short-circuit response if required
@@ -587,14 +588,14 @@ protected boolean serverConnectionFailed(
587588
resumeReadingIfNecessary();
588589
HttpRequest initialRequest = serverConnection.getInitialRequest();
589590
try {
590-
if (serverConnection.connectionFailed(cause)) {
591-
LOG.info(
592-
"Failed to connect via chained proxy, falling back to next chained proxy. Last state before failure: {}",
591+
boolean retrying = serverConnection.connectionFailed(cause);
592+
if (retrying) {
593+
LOG.debug("Failed to connect to upstream server or chained proxy. Retrying connection. Last state before failure: {}",
593594
lastStateBeforeFailure, cause);
594595
return true;
595596
} else {
596597
LOG.debug(
597-
"Connection to server failed: {}. Last state before failure: {}",
598+
"Connection to upstream server or chained proxy failed: {}. Last state before failure: {}",
598599
serverConnection.getRemoteAddress(),
599600
lastStateBeforeFailure,
600601
cause);
@@ -946,7 +947,7 @@ private boolean authenticationRequired(HttpRequest request) {
946947
return false;
947948

948949
if (!request.headers().contains(HttpHeaders.Names.PROXY_AUTHORIZATION)) {
949-
writeAuthenticationRequired();
950+
writeAuthenticationRequired(authenticator.getRealm());
950951
return true;
951952
}
952953

@@ -961,21 +962,21 @@ private boolean authenticationRequired(HttpRequest request) {
961962
String userName = StringUtils.substringBefore(decodedString, ":");
962963
String password = StringUtils.substringAfter(decodedString, ":");
963964
if (!authenticator.authenticate(userName, password)) {
964-
writeAuthenticationRequired();
965+
writeAuthenticationRequired(authenticator.getRealm());
965966
return true;
966967
}
967968

968-
LOG.info("Got proxy authorization!");
969+
LOG.debug("Got proxy authorization!");
969970
// We need to remove the header before sending the request on.
970971
String authentication = request.headers().get(
971972
HttpHeaders.Names.PROXY_AUTHORIZATION);
972-
LOG.info(authentication);
973+
LOG.debug(authentication);
973974
request.headers().remove(HttpHeaders.Names.PROXY_AUTHORIZATION);
974975
authenticated.set(true);
975976
return false;
976977
}
977978

978-
private void writeAuthenticationRequired() {
979+
private void writeAuthenticationRequired(String realm) {
979980
String body = "<!DOCTYPE HTML \"-//IETF//DTD HTML 2.0//EN\">\n"
980981
+ "<html><head>\n"
981982
+ "<title>407 Proxy Authentication Required</title>\n"
@@ -991,7 +992,7 @@ private void writeAuthenticationRequired() {
991992
HttpResponseStatus.PROXY_AUTHENTICATION_REQUIRED, body);
992993
HttpHeaders.setDate(response, new Date());
993994
response.headers().set("Proxy-Authenticate",
994-
"Basic realm=\"Restricted Files\"");
995+
"Basic realm=\"" + (realm == null ? "Restricted Files" : realm) + "\"");
995996
write(response);
996997
}
997998

src/main/java/org/littleshoot/proxy/impl/DefaultHttpProxyServer.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public class DefaultHttpProxyServer implements HttpProxyServer {
106106
private final MitmManager mitmManager;
107107
private final HttpFiltersSource filtersSource;
108108
private final boolean transparent;
109-
private final int connectTimeout;
109+
private volatile int connectTimeout;
110110
private volatile int idleConnectionTimeout;
111111
private final HostResolver serverResolver;
112112
private volatile GlobalTrafficShapingHandler globalTrafficShapingHandler;
@@ -299,18 +299,26 @@ boolean isTransparent() {
299299
return transparent;
300300
}
301301

302+
@Override
302303
public int getIdleConnectionTimeout() {
303304
return idleConnectionTimeout;
304305
}
305306

307+
@Override
306308
public void setIdleConnectionTimeout(int idleConnectionTimeout) {
307309
this.idleConnectionTimeout = idleConnectionTimeout;
308310
}
309311

312+
@Override
310313
public int getConnectTimeout() {
311314
return connectTimeout;
312315
}
313316

317+
@Override
318+
public void setConnectTimeout(int connectTimeoutMs) {
319+
this.connectTimeout = connectTimeoutMs;
320+
}
321+
314322
public HostResolver getServerResolver() {
315323
return serverResolver;
316324
}

0 commit comments

Comments
 (0)