Skip to content

Instantly share code, notes, and snippets.

@zhong-j-yu
Last active February 12, 2016 22:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zhong-j-yu/0ca082a264082e267cd4 to your computer and use it in GitHub Desktop.
Save zhong-j-yu/0ca082a264082e267cd4 to your computer and use it in GitHub Desktop.
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