10

i already have a working HTTP proxy server that can handle multiple HTTP request. now my problem is how do I handle https request?

here's a simplified code i am using:

class Daemon
{
    public static void main(String[] args)
    {
        ServerSocket cDaemonSocket = new ServerSocket(3128);

        while(true)
        {
          try
          {
             Socket ClientSocket = cDaemonSocket.accept();
             (new ClientHandler(ClientSocket )).start();
          }catch(Exception e) { }
        }
    }

}

and the ClientHandler

class ClientHandler extends Thread
{
        private Socket socket = null;
        private Socket remoteSocket = null;
        private HTTPReqHeader request = null;
        ClientHandler(Socket socket)
        {
           this.socket = socket;
           request = new HTTPReqHeader();
           request.parse(socket); // I read and parse the HTTP request here
        }

       public void run()
       {
            if(!request.isSecure() )
            {
              remoteSocket = new Socket(request.url,request.port);
            }
            else
            {
              // now what should I do to established a secured socket?
            }

            // start connecting remoteSocket and clientSocket 
            ...........
       }
}

}

I really did try searching how, I have encounter SSL tunneling, certificate,handshaking, SSLSocket, SSLFactory, trustStore and etc. something like that but still could not make it work.. I just need to know what are the things I need and the steps to established a connection to a SSL-enabled web server.

4
  • Please read the faq and How to Ask Commented Feb 20, 2012 at 7:29
  • Check stackoverflow.com/questions/516323/… and stackoverflow.com/questions/753191/… for explanation how HTTPS over "proxy" works. Commented Feb 20, 2012 at 7:43
  • @JimGarrison ohh so sorry about that.. I'll do my best to ask properly next time. just urgently need an answer. Commented Feb 22, 2012 at 4:12
  • @EugeneMayevski'EldoSCorp thanks this is so much helpful.. Commented Feb 22, 2012 at 4:12

3 Answers 3

16

Please find below java code to create HTTPS proxy. It doesn't modify the response. To integrate it with HTTP write HTTP code in else clause. You can find HTTP code for proxy at a number of places.

Basically what is happening is when the client sends an HTTPS request to proxy it comes with CONNECT keyword. You have to send HTTP/1.1 200 OK to client after establishing connection with upstream server. After that you have to supply client's incoming input stream without headers/host etc to upstream server and incoming stream from upstream server to client.

You don't need to think about SSL at all.

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created for http://stackoverflow.com/q/16351413/1266906.
 */
public class Server extends Thread {

    public static void main(String[] args) {
        (new Server()).run();
    }

    public Server() {
        super("Server Thread");
    }

    @Override
    public void run() {
        try (ServerSocket serverSocket = new ServerSocket(9999)) {
            Socket socket;
            try {
                while ((socket = serverSocket.accept()) != null) {
                    (new Handler(socket)).start();
                }
            } catch (IOException e) {
                e.printStackTrace();  // TODO: implement catch
            }
        } catch (IOException e) {
            e.printStackTrace();  // TODO: implement catch
            return;
        }
    }

    public static class Handler extends Thread {
        public static final Pattern CONNECT_PATTERN = Pattern.compile("CONNECT (.+):(.+) HTTP/(1\\.[01])",
                                                                      Pattern.CASE_INSENSITIVE);
        private final Socket clientSocket;
        private boolean previousWasR = false;

        public Handler(Socket clientSocket) {
            this.clientSocket = clientSocket;
        }

        @Override
        public void run() {
            try {
                String request = readLine(clientSocket);
                System.out.println(request);
                Matcher matcher = CONNECT_PATTERN.matcher(request);
                if (matcher.matches()) {
                    String header;
                    do {
                        header = readLine(clientSocket);
                    } while (!"".equals(header));
                    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(clientSocket.getOutputStream(),
                                                                                   "ISO-8859-1");

                    final Socket forwardSocket;
                    try {
                        forwardSocket = new Socket(matcher.group(1), Integer.parseInt(matcher.group(2)));
                        System.out.println(forwardSocket);
                    } catch (IOException | NumberFormatException e) {
                        e.printStackTrace();  // TODO: implement catch
                        outputStreamWriter.write("HTTP/" + matcher.group(3) + " 502 Bad Gateway\r\n");
                        outputStreamWriter.write("Proxy-agent: Simple/0.1\r\n");
                        outputStreamWriter.write("\r\n");
                        outputStreamWriter.flush();
                        return;
                    }
                    try {
                        outputStreamWriter.write("HTTP/" + matcher.group(3) + " 200 Connection established\r\n");
                        outputStreamWriter.write("Proxy-agent: Simple/0.1\r\n");
                        outputStreamWriter.write("\r\n");
                        outputStreamWriter.flush();

                        Thread remoteToClient = new Thread() {
                            @Override
                            public void run() {
                                forwardData(forwardSocket, clientSocket);
                            }
                        };
                        remoteToClient.start();
                        try {
                            if (previousWasR) {
                                int read = clientSocket.getInputStream().read();
                                if (read != -1) {
                                    if (read != '\n') {
                                        forwardSocket.getOutputStream().write(read);
                                    }
                                    forwardData(clientSocket, forwardSocket);
                                } else {
                                    if (!forwardSocket.isOutputShutdown()) {
                                        forwardSocket.shutdownOutput();
                                    }
                                    if (!clientSocket.isInputShutdown()) {
                                        clientSocket.shutdownInput();
                                    }
                                }
                            } else {
                                forwardData(clientSocket, forwardSocket);
                            }
                        } finally {
                            try {
                                remoteToClient.join();
                            } catch (InterruptedException e) {
                                e.printStackTrace();  // TODO: implement catch
                            }
                        }
                    } finally {
                        forwardSocket.close();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();  // TODO: implement catch
            } finally {
                try {
                    clientSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();  // TODO: implement catch
                }
            }
        }

        private static void forwardData(Socket inputSocket, Socket outputSocket) {
            try {
                InputStream inputStream = inputSocket.getInputStream();
                try {
                    OutputStream outputStream = outputSocket.getOutputStream();
                    try {
                        byte[] buffer = new byte[4096];
                        int read;
                        do {
                            read = inputStream.read(buffer);
                            if (read > 0) {
                                outputStream.write(buffer, 0, read);
                                if (inputStream.available() < 1) {
                                    outputStream.flush();
                                }
                            }
                        } while (read >= 0);
                    } finally {
                        if (!outputSocket.isOutputShutdown()) {
                            outputSocket.shutdownOutput();
                        }
                    }
                } finally {
                    if (!inputSocket.isInputShutdown()) {
                        inputSocket.shutdownInput();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();  // TODO: implement catch
            }
        }

        private String readLine(Socket socket) throws IOException {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            int next;
            readerLoop:
            while ((next = socket.getInputStream().read()) != -1) {
                if (previousWasR && next == '\n') {
                    previousWasR = false;
                    continue;
                }
                previousWasR = false;
                switch (next) {
                    case '\r':
                        previousWasR = true;
                        break readerLoop;
                    case '\n':
                        break readerLoop;
                    default:
                        byteArrayOutputStream.write(next);
                        break;
                }
            }
            return byteArrayOutputStream.toString("ISO-8859-1");
        }
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Nice one.Please note that this is transparent proxy. On the plus side the SSL cert integrity is preserved. On the negative side if you're looking to decrypt data and see the HTTP contents this won't work.
7

I finally got it.

I only need to use normal socket and send a message to client that a connection is established. then proceed to tunneling.

here is a working code:

private Socket socket = null;
        private Socket remoteSocket = null;
        private HTTPReqHeader request = null;
        ClientHandler(Socket socket)
        {
           this.socket = socket;
           request = new HTTPReqHeader();
           request.parse(socket); // I read and parse the HTTP request here
        }

       public void run()
       {

            remoteSocket = new Socket(request.url,request.port);

            if(request.isSecure() )
            {
                 // send ok message to client
                 String ConnectResponse = "HTTP/1.0 200 Connection established\n" +
                                          "Proxy-agent: ProxyServer/1.0\n" +
                                          "\r\n";
                try
                {
           DataOutputStream out =  new DataOutputStream(socket.getOutputStream());
                   out.writeByte(ConnectResponse);
                    out.flush();
                } catch(Exception e) {} 

            }

            // start connecting remoteSocket and clientSocket 
            ...........
       }

here's a good explanation on how proxy server handles CONNECT. http://curl.haxx.se/rfc/draft-luotonen-web-proxy-tunneling-01.txt

3 Comments

The line terminator in HTTP is defined as \r\n, not \n.
Where does class HTTPReqHeader come from? Furthermore, out.writeByte(ConnectResponse) probably does not work because you are writing a String, not a byte. Please update your answer to show some working code, ideally an SSCCE, also mentioning external dependencies (libraries).
Как выполнить parse(socket) если данные зашифрованы под https?
-8

Google "https server in java" and you may find a relevant tutorial, a related RFC and standard documentation. I hope this will help :).

2 Comments

Although the links might be interesting for someone who needs to using SSL in Java, this is off topic here. The OP is asking for an HTTP proxy that can handle HTTPS requests: in this case, the proxy itself doesn't need to know anything about SSL at all.
This question comes up for that search.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.