Last active
February 12, 2016 22:50
-
-
Save zhong-j-yu/0ca082a264082e267cd4 to your computer and use it in GitHub Desktop.
A proxy that changes any `http://` requests to `https://`. See http://stackoverflow.com/questions/35106482/is-there-an-easy-way-to-implement-a-proxys-http-requests-and-translates-them-to
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import bayou.async.Async; | |
import bayou.bytes.ByteSource; | |
import bayou.gzip.GunzipByteSource; | |
import bayou.http.*; | |
import bayou.mime.Headers; | |
import bayou.ssl.SslConf; | |
import bayou.text.TextHttpEntity; | |
import bayou.util.function.ConsumerX; | |
import javax.net.ssl.SSLEngine; | |
import javax.net.ssl.SSLParameters; | |
// a proxy that changes any `http://` requests to `https://` | |
public class ToHttpsProxy | |
{ | |
public static void main(String[] args) throws Exception | |
{ | |
int port = 9090; | |
SslConf sslConf = new SslConf() | |
.trustAll(); | |
ConsumerX<SSLEngine> sslEngineConf = engine-> | |
{ | |
SSLParameters sslParameters = engine.getSSLParameters(); | |
sslParameters.setEndpointIdentificationAlgorithm(null); | |
engine.setSSLParameters(sslParameters); | |
}; | |
HttpClientConf clientConf = new HttpClientConf() | |
.sslEngineConf(sslEngineConf) | |
.sslContext(sslConf.createContext()) | |
//.trafficDump(System.err::print) | |
; | |
HttpClient downstream = new HttpClient(clientConf); | |
HttpHandler handler = request-> | |
{ | |
if(request.method().equals("CONNECT")) | |
{ | |
System.err.println("## TUNNEL ## "+request.host()); | |
return HttpResponse.text(200, "tunneling request granted"); | |
} | |
request = modRequest(request); | |
//System.out.println(request.absoluteUri()); | |
return downstream | |
.send0(request, null) | |
.then(response -> modResponse(response)); | |
}; | |
HttpServer proxy = new HttpServer(handler); | |
proxy.conf() | |
.port(port) | |
.setProxyDefaults() | |
// .trafficDump(System.out::print) | |
; | |
proxy.start(); | |
System.out.printf("Proxy, port = %s %n", port); | |
} | |
static HttpRequestImpl modRequest(HttpRequest request) | |
{ | |
HttpRequestImpl newReq = new HttpRequestImpl(request); | |
// change all requests to HTTPS | |
newReq.isHttps(true); | |
return newReq; | |
} | |
static Async<HttpResponse> modResponse(HttpResponse response) | |
{ | |
return fixRedirect(response) | |
.then(res->fixBody(res)); | |
} | |
static Async<HttpResponse> fixRedirect(HttpResponse serverResponse) | |
{ | |
if(serverResponse.statusCode()!=303) | |
return Async.success(serverResponse); | |
String location = serverResponse.header(Headers.Location); | |
if(location==null || !location.startsWith("https")) | |
return Async.success(serverResponse); | |
// server tries to redirect to an https://xxx url. change it to http://xxx | |
location = "http" + location.substring(5); | |
return new HttpResponseImpl(serverResponse) | |
.header(Headers.Location, location); | |
} | |
static final int MAX_BODY_LENGTH = 1000_000; | |
static Async<HttpResponse> fixBody(HttpResponse response) | |
{ | |
HttpEntity entity = response.entity(); | |
if(entity==null) // no body | |
return Async.success(response); | |
if(!entity.contentType().type().equals("text")) // not text body | |
return Async.success(response); | |
if(entity.contentEncoding()!=null) // body is encoded | |
{ | |
if(!entity.contentEncoding().equals("gzip")) // unknown encoding | |
return Async.success(response); | |
// unzip it | |
entity = new GunzipEntity(entity); | |
} | |
return entity.bodyString(MAX_BODY_LENGTH) | |
.then(body->fixBody2(response, body)); | |
} | |
static Async<HttpResponse> fixBody2(HttpResponse response, String bodyStr) | |
{ | |
if(bodyStr.contains("https://")) | |
{ | |
System.out.println(HttpRequest.current().absoluteUri()); | |
System.out.println("### fix body https:// to http://"); | |
} | |
bodyStr = bodyStr.replace("https://", "http://"); | |
HttpEntity entity = new TextHttpEntity(response.entity().contentType(), bodyStr); | |
return new HttpResponseImpl(response) | |
.header(Headers.Content_Encoding, null) | |
.header(Headers.Content_Length, null) | |
.entity(entity) | |
; | |
} | |
static class GunzipEntity implements HttpEntityWrapper | |
{ | |
HttpEntity origin; | |
GunzipEntity(HttpEntity origin) | |
{ | |
this.origin = origin; | |
} | |
@Override | |
public ByteSource body() | |
{ | |
return new GunzipByteSource(origin.body()); | |
} | |
@Override | |
public HttpEntity getOriginEntity() | |
{ | |
return origin; | |
} | |
@Override | |
public Long contentLength() | |
{ | |
return null; // don't know the length after compression | |
} | |
@Override | |
public String contentEncoding() | |
{ | |
return null; // "gzip"->null | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment