Skip to content

Commit

Permalink
Merge branch 'enable_offline_caching_with_mitm' into mitm_offline
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ganskef committed May 22, 2016
2 parents c051fd0 + 3424eb2 commit c81c0fc
Show file tree
Hide file tree
Showing 14 changed files with 240 additions and 115 deletions.
29 changes: 15 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[![Build Status](https://travis-ci.org/adamfisk/LittleProxy.png?branch=master)](https://travis-ci.org/adamfisk/LittleProxy)
[![Build Status](https://travis-ci.org/ganskef/LittleProxy-parent.png?branch=master)](https://travis-ci.org/ganskef/LittleProxy-parent)

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.

Expand All @@ -16,7 +16,7 @@ You can embed LittleProxy in your own projects through Maven with the following:
<dependency>
<groupId>org.littleshoot</groupId>
<artifactId>littleproxy</artifactId>
<version>1.1.0-beta1</version>
<version>1.1.0-beta2</version>
</dependency>
```

Expand All @@ -38,18 +38,19 @@ HttpProxyServer server =
.withPort(8080)
.withFiltersSource(new HttpFiltersSourceAdapter() {
public HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerContext ctx) {
return new HttpFiltersAdapter(originalRequest) {
@Override
public HttpResponse clientToProxyRequest(HttpObject httpObject) {
// TODO: implement your filtering here
return null;
}

@Override
public HttpObject serverToProxyResponse(HttpObject httpObject) {
// TODO: implement your filtering here
return httpObject;
}
return new HttpFiltersAdapter(originalRequest) {
@Override
public HttpResponse clientToProxyRequest(HttpObject httpObject) {
// TODO: implement your filtering here
return null;
}

@Override
public HttpObject serverToProxyResponse(HttpObject httpObject) {
// TODO: implement your filtering here
return httpObject;
}
};
}
})
.start();
Expand Down
17 changes: 9 additions & 8 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<groupId>org.littleshoot</groupId>
<artifactId>littleproxy</artifactId>
<packaging>jar</packaging>
<version>1.1.0-beta2-SNAPSHOT</version>
<version>1.1.0-beta3-offline</version>
<name>LittleProxy</name>
<description>
LittleProxy is a high performance HTTP proxy written in Java and using the Netty networking framework.
Expand All @@ -14,7 +14,8 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<github.global.server>github</github.global.server>
<netty.version>4.0.23.Final</netty.version>
<netty.version>4.0.35.Final</netty.version>
<slf4j.version>1.7.19</slf4j.version>
</properties>

<organization>
Expand Down Expand Up @@ -83,7 +84,7 @@
<profile>
<id>netty-4.1</id>
<properties>
<netty.version>4.1.0.Beta6</netty.version>
<netty.version>4.1.0.CR4</netty.version>
</properties>
</profile>
</profiles>
Expand Down Expand Up @@ -150,14 +151,14 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.0.31-beta</version>
<version>2.0.44-beta</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-netty</artifactId>
<version>3.9.17</version>
<version>3.10.4</version>
<scope>test</scope>
<exclusions>
<exclusion>
Expand Down Expand Up @@ -190,7 +191,7 @@
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5</version>
<version>4.5.2</version>
<scope>test</scope>
</dependency>

Expand Down Expand Up @@ -228,14 +229,14 @@
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.12</version>
<version>${slf4j.version}</version>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.12</version>
<version>${slf4j.version}</version>
</dependency>

<!-- Test dependencies -->
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/org/littleshoot/proxy/HttpFiltersAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
* Convenience base class for implementations of {@link HttpFilters}.
*/
public class HttpFiltersAdapter implements HttpFilters {
/**
* A default, stateless, no-op {@link HttpFilters} instance.
*/
public static final HttpFiltersAdapter NOOP_FILTER = new HttpFiltersAdapter(null);

protected final HttpRequest originalRequest;
protected final ChannelHandlerContext ctx;

Expand Down
10 changes: 10 additions & 0 deletions src/main/java/org/littleshoot/proxy/HttpProxyServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ public interface HttpProxyServer {

void setIdleConnectionTimeout(int idleConnectionTimeout);

/**
* Returns the maximum time to wait, in milliseconds, to connect to a server.
*/
int getConnectTimeout();

/**
* Sets the maximum time to wait, in milliseconds, to connect to a server.
*/
void setConnectTimeout(int connectTimeoutMs);

/**
* <p>
* Clone the existing server, with a port 1 higher and everything else the
Expand Down
39 changes: 20 additions & 19 deletions src/main/java/org/littleshoot/proxy/MitmManager.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.littleshoot.proxy;

import io.netty.handler.codec.http.HttpRequest;

import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSession;

Expand All @@ -9,20 +11,23 @@
*/
public interface MitmManager {
/**
* Creates an {@link SSLEngine} for encrypting the server connection.
*
* Note: Peer information is needed to send the server_name extension in
* handshake with Server Name Indication (SNI).
* Creates an {@link SSLEngine} for encrypting the server connection. The SSLEngine created by this method
* may use the given peer information to send SNI information when connecting to the upstream host.
*
* @param peerHost to start a client connection to the server.
* @param peerPort to start a client connection to the server.
*
* @param peerHost
* to start a client connection to the server.
* @param peerPort
* to start a client connection to the server.
*
* @return
* @return an SSLEngine used to connect to an upstream server
*/
SSLEngine serverSslEngine(String peerHost, int peerPort);

/**
* Creates an {@link SSLEngine} for encrypting the server connection.
*
* @return an SSLEngine used to connect to an upstream server
*/
SSLEngine serverSslEngine();

/**
* <p>
* Creates an {@link SSLEngine} for encrypting the client connection based
Expand All @@ -40,14 +45,10 @@ public interface MitmManager {
* by issuing replacement certificates signed by the proxy's own
* certificate.
* </p>
*
* @param serverSslSession
* the {@link SSLSession} that's been established with the server
* @param serverHostAndPort
* the server host name, optionally with port, to create the
* dynamic certificate for
* @return
*
* @param httpRequest the HTTP CONNECT request that is being man-in-the-middled
* @param serverSslSession the {@link SSLSession} that's been established with the server
* @return the SSLEngine used to connect to the client
*/
SSLEngine clientSslEngineFor(SSLSession serverSslSession,
String serverHostAndPort);
SSLEngine clientSslEngineFor(HttpRequest httpRequest, SSLSession serverSslSession);
}
9 changes: 9 additions & 0 deletions src/main/java/org/littleshoot/proxy/ProxyAuthenticator.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,13 @@ public interface ProxyAuthenticator {
* <code>false</code>.
*/
boolean authenticate(String userName, String password);

/**
* The realm value to be used in the request for proxy authentication
* ("Proxy-Authenticate" header). Returning null will cause the string
* "Restricted Files" to be used by default.
*
* @return
*/
String getRealm();
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package org.littleshoot.proxy.extras;

import io.netty.handler.codec.http.HttpRequest;
import org.littleshoot.proxy.MitmManager;

import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSession;

import org.littleshoot.proxy.MitmManager;

/**
* {@link MitmManager} that uses self-signed certs for everything.
*/
Expand All @@ -18,8 +19,12 @@ public SSLEngine serverSslEngine(String peerHost, int peerPort) {
}

@Override
public SSLEngine clientSslEngineFor(SSLSession serverSslSession, String
serverHostAndPort) {
public SSLEngine serverSslEngine() {
return selfSignedSslEngineSource.newSslEngine();
}

@Override
public SSLEngine clientSslEngineFor(HttpRequest httpRequest, SSLSession serverSslSession) {
return selfSignedSslEngineSource.newSslEngine();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.channels.ClosedChannelException;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.List;
Expand Down Expand Up @@ -124,7 +123,7 @@ public class ClientToProxyConnection extends ProxyConnection<HttpRequest> {
/**
* The current filters to apply to incoming requests/chunks.
*/
private volatile HttpFilters currentFilters = new HttpFiltersAdapter(null);
private volatile HttpFilters currentFilters = HttpFiltersAdapter.NOOP_FILTER;

private volatile SSLSession clientSslSession;

Expand Down Expand Up @@ -220,6 +219,8 @@ private ConnectionState doReadHTTPInitial(HttpRequest httpRequest) {
HttpFilters filterInstance = proxyServer.getFiltersSource().filterRequest(currentRequest, ctx);
if (filterInstance != null) {
currentFilters = filterInstance;
} else {
currentFilters = HttpFiltersAdapter.NOOP_FILTER;
}

// Send the request through the clientToProxyRequest filter, and respond with the short-circuit response if required
Expand Down Expand Up @@ -587,14 +588,14 @@ protected boolean serverConnectionFailed(
resumeReadingIfNecessary();
HttpRequest initialRequest = serverConnection.getInitialRequest();
try {
if (serverConnection.connectionFailed(cause)) {
LOG.info(
"Failed to connect via chained proxy, falling back to next chained proxy. Last state before failure: {}",
boolean retrying = serverConnection.connectionFailed(cause);
if (retrying) {
LOG.debug("Failed to connect to upstream server or chained proxy. Retrying connection. Last state before failure: {}",
lastStateBeforeFailure, cause);
return true;
} else {
LOG.debug(
"Connection to server failed: {}. Last state before failure: {}",
"Connection to upstream server or chained proxy failed: {}. Last state before failure: {}",
serverConnection.getRemoteAddress(),
lastStateBeforeFailure,
cause);
Expand Down Expand Up @@ -946,7 +947,7 @@ private boolean authenticationRequired(HttpRequest request) {
return false;

if (!request.headers().contains(HttpHeaders.Names.PROXY_AUTHORIZATION)) {
writeAuthenticationRequired();
writeAuthenticationRequired(authenticator.getRealm());
return true;
}

Expand All @@ -961,21 +962,21 @@ private boolean authenticationRequired(HttpRequest request) {
String userName = StringUtils.substringBefore(decodedString, ":");
String password = StringUtils.substringAfter(decodedString, ":");
if (!authenticator.authenticate(userName, password)) {
writeAuthenticationRequired();
writeAuthenticationRequired(authenticator.getRealm());
return true;
}

LOG.info("Got proxy authorization!");
LOG.debug("Got proxy authorization!");
// We need to remove the header before sending the request on.
String authentication = request.headers().get(
HttpHeaders.Names.PROXY_AUTHORIZATION);
LOG.info(authentication);
LOG.debug(authentication);
request.headers().remove(HttpHeaders.Names.PROXY_AUTHORIZATION);
authenticated.set(true);
return false;
}

private void writeAuthenticationRequired() {
private void writeAuthenticationRequired(String realm) {
String body = "<!DOCTYPE HTML \"-//IETF//DTD HTML 2.0//EN\">\n"
+ "<html><head>\n"
+ "<title>407 Proxy Authentication Required</title>\n"
Expand All @@ -991,7 +992,7 @@ private void writeAuthenticationRequired() {
HttpResponseStatus.PROXY_AUTHENTICATION_REQUIRED, body);
HttpHeaders.setDate(response, new Date());
response.headers().set("Proxy-Authenticate",
"Basic realm=\"Restricted Files\"");
"Basic realm=\"" + (realm == null ? "Restricted Files" : realm) + "\"");
write(response);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public class DefaultHttpProxyServer implements HttpProxyServer {
private final MitmManager mitmManager;
private final HttpFiltersSource filtersSource;
private final boolean transparent;
private final int connectTimeout;
private volatile int connectTimeout;
private volatile int idleConnectionTimeout;
private final HostResolver serverResolver;
private volatile GlobalTrafficShapingHandler globalTrafficShapingHandler;
Expand Down Expand Up @@ -299,18 +299,26 @@ boolean isTransparent() {
return transparent;
}

@Override
public int getIdleConnectionTimeout() {
return idleConnectionTimeout;
}

@Override
public void setIdleConnectionTimeout(int idleConnectionTimeout) {
this.idleConnectionTimeout = idleConnectionTimeout;
}

@Override
public int getConnectTimeout() {
return connectTimeout;
}

@Override
public void setConnectTimeout(int connectTimeoutMs) {
this.connectTimeout = connectTimeoutMs;
}

public HostResolver getServerResolver() {
return serverResolver;
}
Expand Down
Loading

0 comments on commit c81c0fc

Please sign in to comment.