users@grizzly.java.net

Re: Make an http proxy with grizzly

From: yann Blazart <yann.blazart_at_gmail.com>
Date: Fri, 17 Aug 2012 12:04:51 +0200

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 ?
>>
>
>