users@grizzly.java.net

Re: Make an http proxy with grizzly

From: yann Blazart <yann.blazart_at_gmail.com>
Date: Sat, 18 Aug 2012 15:14:19 +0200

Hi ! Thanks for your patch. But it doesn't seem to work....

The read in the inputstream of the request is blocked again.

I made a test with a stupid ServerSocket with old IO and it works. I would
prefer to use Grizzly....

2012/8/18 Oleksiy Stashok <oleksiy.stashok_at_oracle.com>

> Thanks.
>
> it was an issue in Grizzly
> http://java.net/jira/browse/GRIZZLY-1317
>
> I've fixed it on trunk and added unit-test.
> You can either checkout the Grizzly trunk or use Grizzly 3.0-SNAPSHOT
> maven dependency (artifacts will be available soon @ snapshots repository
> [1]).
>
> Let me know if it works for you.
>
> Thanks.
>
> WBR,
> Alexey.
>
> [1] https://maven.java.net/content/repositories/snapshots/
>
>
> On 08/17/2012 12:04 PM, yann Blazart wrote:
>
> Of course. This is prototype code. The part for CONNECT is in
> "getGoodRequest()"
>
> /*
> * To change this template, choose Tools | Templates
> * and open the template in the editor.
> */
> package fr.bycode.tools.jntlmproxy;
>
> import fr.bycode.tools.jntlmproxy.ntlm.NTLMSchemeFactory;
> import java.io.BufferedInputStream;
> import java.io.BufferedOutputStream;
> import java.io.IOException;
> import java.io.InputStream;
> import java.io.OutputStream;
> import java.net.Socket;
> import java.net.URI;
> import java.net.UnknownHostException;
> import java.util.HashSet;
> import org.apache.commons.io.IOUtils;
> import org.apache.http.Header;
> import org.apache.http.HttpEntity;
> import org.apache.http.HttpEntityEnclosingRequest;
> import org.apache.http.HttpException;
> import org.apache.http.HttpHeaders;
> import org.apache.http.HttpHost;
> import org.apache.http.HttpRequest;
> import org.apache.http.HttpResponse;
> import org.apache.http.ProtocolException;
> import org.apache.http.auth.AuthScope;
> import org.apache.http.auth.Credentials;
> import org.apache.http.auth.NTCredentials;
> import org.apache.http.auth.UsernamePasswordCredentials;
> import org.apache.http.client.RedirectStrategy;
> import org.apache.http.client.methods.HttpDelete;
> import org.apache.http.client.methods.HttpGet;
> import org.apache.http.client.methods.HttpHead;
> import org.apache.http.client.methods.HttpOptions;
> import org.apache.http.client.methods.HttpPost;
> import org.apache.http.client.methods.HttpPut;
> import org.apache.http.client.methods.HttpRequestBase;
> import org.apache.http.client.methods.HttpTrace;
> import org.apache.http.client.methods.HttpUriRequest;
> import org.apache.http.conn.params.ConnRoutePNames;
> import org.apache.http.entity.InputStreamEntity;
> import org.apache.http.impl.client.DefaultHttpClient;
> import org.apache.http.impl.client.ProxyClient;
> import org.apache.http.impl.conn.PoolingClientConnectionManager;
> import org.apache.http.protocol.HttpContext;
> import org.glassfish.grizzly.http.server.AfterServiceListener;
> import org.glassfish.grizzly.http.server.HttpHandler;
> import org.glassfish.grizzly.http.server.Request;
> import org.glassfish.grizzly.http.server.Response;
>
> /**
> *
> * @author yblazart
> */
> public class ProxyHttpHandler extends HttpHandler {
>
> private DefaultHttpClient httpClient;
> private Credentials credentials;
> private HttpHost proxyHost;
> private static String via = "Via: 1.1 JNTLMProxy\r\n";
> /**
> * Constant for the headers for an OK response to an HTTP connect
> request.
> */
> public static final String CONNECT_OK_HEADERS =
> "Connection: Keep-Alive\r\n"
> + "Proxy-Connection: Keep-Alive\r\n"
> + via
> + "\r\n";
> /**
> * Constant for the headers for a proxy error response.
> */
> public static final String PROXY_ERROR_HEADERS =
> "Connection: close\r\n"
> + "Proxy-Connection: close\r\n"
> + "Pragma: no-cache\r\n"
> + "Cache-Control: no-cache\r\n"
> + via
> + "\r\n";
> protected HashSet _DontProxyHeaders = new HashSet();
>
> {
> _DontProxyHeaders.add("proxy-connection");
> _DontProxyHeaders.add("connection");
> _DontProxyHeaders.add("keep-ali(\"ntlm\", new
> NTLMSchemeFactory());ve");
> _DontProxyHeaders.add("transfer-encoding");
> _DontProxyHeaders.add("te");
> _DontProxyHeaders.add("trailer");
> _DontProxyHeaders.add("proxy-authorization");
> _DontProxyHeaders.add("proxy-authenticate");
> _DontProxyHeaders.add("upgrade");
> }
>
> public ProxyHttpHandler(String externalProxyHost, int
> externalProxyPort, String userName, String password) {
> super("SimpleProxyHttpHandler");
> credentials = new UsernamePasswordCredentials(userName, password);
> init(externalProxyHost, externalProxyPort, credentials);
> }
>
> public ProxyHttpHandler(String externalProxyHost, int
> externalProxyPort, String userName, String password, boolean ntlm, String
> workstation, String domain) {
> super("NTLMProxyHttpHandler");
> credentials = new NTCredentials(userName, password, workstation,
> domain);
> init(externalProxyHost, externalProxyPort, credentials);
> httpClient.getAuthSchemes().register("ntlm", new
> NTLMSchemeFactory());
> }
>
> public ProxyHttpHandler() {
> super("DirectHttpHandler");
> init();
> }
>
> @Override
> public void service(Request request, Response response) throws
> Exception {
> System.out.println("request = " + request.getMethod() + "," +
> request.getRequestURI());
> HttpRequestBase requestBase = getGoodRequest(request, response);
> if (requestBase == null) {
> return;
> }
> String requestURL = request.getRequestURL().toString();
> System.out.println("requestURL = " +
> requestBase.getClass().getSimpleName() + " : " + requestURL);
>
> // copy the URL
> requestBase.setURI(new URI(requestURL));
>
> //copy the headers
> boolean hasContent = false;
> for (String headerName : request.getHeaderNames()) {
> // Exclude content length
> if (!headerName.equals(HttpHeaders.CONTENT_LENGTH) &&
> !_DontProxyHeaders.contains(headerName.toLowerCase())) {
> requestBase.setHeader(headerName,
> request.getHeader(headerName));
> System.out.println(" - copy header " + headerName + " =
> " + request.getHeader(headerName));
> }
> if (headerName.equals(HttpHeaders.CONTENT_TYPE)) {
> hasContent = true;
> }
> }
> // copy the headers
>
> if (hasContent && requestBase instanceof
> HttpEntityEnclosingRequest) {
> ((HttpEntityEnclosingRequest) requestBase).setEntity(new
> InputStreamEntity(request.getInputStream(),
> request.getContentLengthLong()));
> }
>
> // response
> HttpResponse executed = httpClient.execute(requestBase);
>
>
> Header[] allHeaders = executed.getAllHeaders();
> response.setStatus(executed.getStatusLine().getStatusCode());
> System.out.println(" --> ret status " +
> executed.getStatusLine());
>
> for (Header header : allHeaders) {
> if
> (!_DontProxyHeaders.contains(header.getName().toLowerCase())) {
> response.setHeader(header.getName(), header.getValue());
> System.out.println(" --> set header " +
> header.getName() + " = " + header.getValue());
> }
> }
> // force to close the connection at the end
> response.setHeader(HttpHeaders.CONNECTION, "close");
> //
> HttpEntity entity = executed.getEntity();
> if (entity != null) {
> response.setContentLengthLong(entity.getContentLength());
> Header contentType = entity.getContentType();
> if (contentType != null && contentType.getValue() != null) {
> response.setContentType(contentType.getValue());
> }
> Header contentEncoding = entity.getContentEncoding();
> if (contentEncoding != null && contentEncoding.getValue() !=
> null) {
> response.setCharacterEncoding(contentEncoding.getValue());
> }
> BufferedInputStream bufferedInputStream = new
> BufferedInputStream(entity.getContent());
> BufferedOutputStream bufferedOutputStream = new
> BufferedOutputStream(response.getOutputStream());
> byte[] buffer = new byte[4096];
> int readed;
> while ((readed = bufferedInputStream.read(buffer)) > -1) {
> bufferedOutputStream.write(buffer, 0, readed);
> bufferedOutputStream.flush();
> }
> bufferedOutputStream.close();
> bufferedInputStream.close();
> entity.getContent().close();
> }
>
> }
>
> private void init() {
> PoolingClientConnectionManager cm = new
> PoolingClientConnectionManager();
> cm.setMaxTotal(100);
> httpClient = new DefaultHttpClient(cm);
> httpClient.setRedirectStrategy(new RedirectStrategy() {
> @Override
> public boolean isRedirected(HttpRequest request, HttpResponse
> response, HttpContext context) throws ProtocolException {
> return false;
> }
>
> @Override
> public HttpUriRequest getRedirect(HttpRequest request,
> HttpResponse response, HttpContext context) throws ProtocolException {
> return null;
> }
> });
> //http://www.google.fr/webhp
> //
> httpClient.getParams().setParameter("http.protocol.single-cookie-header",
> true);
> }
>
> private void init(String externalProxyHost, int externalProxyPort,
> Credentials credentials) {
> init();
> proxyHost = new HttpHost(externalProxyHost, externalProxyPort);
> httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,
> proxyHost);
> httpClient.getCredentialsProvider().setCredentials(
> new AuthScope(null, -1),
> credentials);
>
> }
>
> private HttpRequestBase getGoodRequest(Request request, Response
> response) throws UnknownHostException, IOException, HttpException,
> InterruptedException {
> String methodString = request.getMethod().getMethodString();
> if ("GET".equals(methodString)) {
> return new HttpGet();
> }
> if ("POST".equals(methodString)) {
> return new HttpPost();
> }
> if ("PUT".equals(methodString)) {
> return new HttpPut();
> }
> if ("DELETE".equals(methodString)) {
> return new HttpDelete();
> }
> if ("OPTIONS".equals(methodString)) {
> return new HttpOptions();
> }
> if ("TRACE".equals(methodString)) {
> return new HttpTrace();
> }
> if ("HEAD".equals(methodString)) {
> return new HttpHead();
> }
> if ("CONNECT".equals(methodString)) {
> for (String headerName : request.getHeaderNames()) {
> // Exclude content length
> System.out.println(" - header " + headerName + " = " +
> request.getHeader(headerName));
> }
>
> String host = request.getRequestURI();
> String port = host.substring(host.indexOf(":") + 1);
> host = host.substring(0, host.indexOf(":"));
>
> HttpHost target = new HttpHost(host, Integer.parseInt(port));
> Socket socket;
> if (proxyHost != null) {
> ProxyClient proxyClient = new ProxyClient();
> if (credentials instanceof NTCredentials) {
> proxyClient.getAuthSchemeRegistry().register("ntlm",
> new NTLMSchemeFactory());
> }
> System.out.println("Proxy tunnel");
> socket = proxyClient.tunnel(proxyHost, target,
> credentials);
> } else {
> socket = new Socket(target.getHostName(),
> target.getPort());
> }
> // HttpParams params = new BasicHttpParams();
> // params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS,
> 30);
> //
> params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new
> ConnPerRouteBean(30));
> // params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE,
> false);
> // HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
> // SSLSocketFactory socketFactory =
> SSLSocketFactory.getSocketFactory();
> // final Socket secSocket =
> socketFactory.createLayeredSocket(socket, host, Integer.parseInt(port),
> params);
> final Socket secSocket = socket;
>
>
> final InputStream in = request.createInputStream();
> final OutputStream out = response.getOutputStream();
>
> // response.setStatus(200);
> //response.setHeader("Connection", "close");
> out.write(("HTTP/1.1 200 Connection
> etablished\r\n"+CONNECT_OK_HEADERS).getBytes());
> out.flush();
> System.out.println("200 sended ");
> // response.flush();
>
> Thread tread = new Thread(new Runnable() {
> @Override
> public void run() {
> try {
> System.out.println("read from socket");
> byte[] buffer = new byte[400];
> int readed = 0;
> while ((readed =
> secSocket.getInputStream().read(buffer)) > -1) {
> System.out.println("readed = " + readed);
> out.write(buffer, 0, readed);
> }
> // int l = IOUtils.copy(secSocket.getInputStream(),
> out);
> System.out.println(" --> stop copy from socket
> to out : " + readed);
>
> } catch (IOException ex) {
> throw new RuntimeException(ex);
> }
> }
> });
> Thread twrite = new Thread(new Runnable() {
> @Override
> public void run() {
> try {
> System.out.println("Write to socket");
> byte[] buffer = new byte[400];
> int retry = 5;
> int readed = 0;
> while ((readed = in.read(buffer)) > -1 || retry >
> 0) {
> System.out.println("readed = " + readed);
>
> if (readed >= 0) {
> secSocket.getOutputStream().write(buffer,
> 0, readed);
> } else {
> System.out.println("Wait from in 1s");
> Thread.sleep(1000);
> }
> }
>
> // int l = IOUtils.copy(in,
> secSocket.getOutputStream());
> System.out.println(" --> stop copy from in to
> socket : " + readed);
>
> } catch (Exception ex) {
> throw new RuntimeException(ex);
> }
> }
> });
>
>
> tread.start();
> twrite.start();
> tread.join();
> twrite.join();
> IOUtils.closeQuietly(secSocket);
> return null;
> }
> throw new IllegalArgumentException("Unknown method : " +
> methodString);
> }
> }
>
>
> The test is
>
> /*
> * To change this template, choose Tools | Templates
> * and open the template in the editor.
> */
> package fr.bycode.tools.jntlmproxy;
>
> import java.io.File;
> import java.io.FileInputStream;
> import java.io.IOException;
> import java.net.Socket;
> import java.security.KeyStore;
> import org.apache.http.HttpHost;
> import org.apache.http.HttpResponse;
> import org.apache.http.client.HttpClient;
> import org.apache.http.client.methods.HttpGet;
> import org.apache.http.conn.params.ConnRoutePNames;
> import org.apache.http.conn.scheme.Scheme;
> import org.apache.http.conn.ssl.SSLSocketFactory;
> import org.apache.http.impl.client.DefaultHttpClient;
> import org.apache.http.util.EntityUtils;
> import org.junit.Test;
>
> /**
> *
> * @author yblazart
> */
> public class ProxyServerTest2 {
>
> @Test
> public void test() throws Exception {
> System.setProperty("javax.net.debug", "ssl");
> ProxyHttpHandler proxyHttpHandler = new ProxyHttpHandler("proxy",
> 8080, "qjqx4250", "Gajdb5411&", true, "10.197.64.86", "AD");
> // ProxyHttpHandler proxyHttpHandler = new ProxyHttpHandler();
> MiniServer proxyServer = new MiniServer("0.0.0.0", 58080, "PROXY",
> proxyHttpHandler);
> proxyServer.start();
>
>
>
> // HttpClient hc = WebClientDevWrapper.wrapClient(new
> DefaultHttpClient());
> HttpClient hc = new DefaultHttpClient();
> HttpHost proxy = new HttpHost("10.158.80.132", 58080);
> hc.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
> //
> //
> HttpResponse execute = hc.execute(new HttpGet("
> http://www.google.fr/"));
> String toString = EntityUtils.toString(execute.getEntity());
> // System.out.println("toString = " + toString);
>
>
> execute = hc.execute(new HttpGet("https://mail.google.com/"));
> System.out.println("xxxxx");
> toString = EntityUtils.toString(execute.getEntity());
> System.out.println("toString = " + toString);
>
>
> while (System.in.read() < 1) {
> Thread.sleep(1000);
> }
>
> proxyServer.stop();
>
>
> }
> }
>
>
> 2012/8/17 Oleksiy Stashok <oleksiy.stashok_at_oracle.com>
>
>> Hi,
>>
>> can you pls. share the sources (or at least the part responsible for
>> CONNECT) so we can reproduce that?
>>
>> Thanks.
>>
>> WBR,
>> Alexey.
>>
>>
>> On 08/17/2012 11:49 AM, yann Blazart wrote:
>>
>>> Hi ! I'm trying to implements my own proxy with grizzly, to pass my f...
>>> NTML entreprise proxy.
>>> I'm using httpclient 4.2 to get out.
>>>
>>> Well, everything is ok for Normal Http Method (get, put, delete ...).
>>>
>>> But I've some problem with connect method.
>>>
>>> In fact, I've writed my own HttpHandler, with the public void
>>> service(Request request, Response response) method.
>>>
>>> In this, in case of connect method, I made the connection thought the
>>> ProxyClient of HttpClient and make a tunnel by getting the socket.
>>> I write in the response.getOutputStream the "HTTP/1.1 200 Connection
>>> established".
>>>
>>> This is ok because in my unit test, the client receive it and after it
>>> try to make the SSL handshake.
>>>
>>> After this I made two Threads that make copy form request.getInputStream
>>> to socket output stream and from socket.getInputStream to
>>> request.getOutputStream.
>>>
>>> The problem is that the client write to grizzly his SSL things, but the
>>> request.getInputStream().read(...) return -1...
>>>
>>> Any idea ?
>>>
>>
>>
>
>
>