0

My goal here is to make a simple HTTP proxy that can perform GET/POST requests, trying to learn about Java Sockets. Would be appreciated if anyone can point me in that direction.

// This example is from _Java Examples in a Nutshell_. (http://www.oreilly.com)
// Copyright (c) 1997 by David Flanagan
// This example is provided WITHOUT ANY WARRANTY either expressed or implied.
// You may study, use, modify, and distribute it for non-commercial purposes.
// For any commercial use, see http://www.davidflanagan.com/javaexamples

import java.io.*;
import java.net.*;

/**
 * This class implements a simple single-threaded proxy server.
 **/
public class SimpleProxyServer {
  /** The main method parses arguments and passes them to runServer */
  public static void main(String[] args) throws IOException {
    try {
      // Check the number of arguments
      if (args.length != 3) 
        throw new IllegalArgumentException("Wrong number of arguments.");

      // Get the command-line arguments: the host and port we are proxy for
      // and the local port that we listen for connections on
      String host = args[0];
      int remoteport = Integer.parseInt(args[1]);
      int localport = Integer.parseInt(args[2]);
      // Print a start-up message
      System.out.println("Starting proxy for " + host + ":" + remoteport +
                         " on port " + localport);
      // And start running the server
      runServer(host, remoteport, localport);   // never returns
    } 
    catch (Exception e) {
      System.err.println(e);
      System.err.println("Usage: java SimpleProxyServer " +
                         "<host> <remoteport> <localport>");
    }
  }

  /**
   * This method runs a single-threaded proxy server for 
   * host:remoteport on the specified local port.  It never returns.
   **/
  public static void runServer(String host, int remoteport, int localport) 
       throws IOException {
    // Create a ServerSocket to listen for connections with
    ServerSocket ss = new ServerSocket(localport);

    // Create buffers for client-to-server and server-to-client communication.
    // We make one final so it can be used in an anonymous class below.
    // Note the assumptions about the volume of traffic in each direction...
    final byte[] request = new byte[1024];
    byte[] reply = new byte[4096];

    // This is a server that never returns, so enter an infinite loop.
    while(true) { 
      // Variables to hold the sockets to the client and to the server.
      Socket client = null, server = null;
      try {
        // Wait for a connection on the local port
        client = ss.accept();

        // Get client streams.  Make them final so they can
        // be used in the anonymous thread below.
        final InputStream from_client = client.getInputStream();
        final OutputStream to_client= client.getOutputStream();

        // Make a connection to the real server
        // If we cannot connect to the server, send an error to the 
        // client, disconnect, then continue waiting for another connection.
        try { server = new Socket(host, remoteport); } 
        catch (IOException e) {
          PrintWriter out = new PrintWriter(new OutputStreamWriter(to_client));
          out.println("Proxy server cannot connect to " + host + ":" +
                      remoteport + ":\n" + e);
          out.flush();
          client.close();
          continue;
        }

        // Get server streams.
        final InputStream from_server = server.getInputStream();
        final OutputStream to_server = server.getOutputStream();

        // Make a thread to read the client's requests and pass them to the 
        // server.  We have to use a separate thread because requests and
        // responses may be asynchronous.
        Thread t = new Thread() {
          public void run() {
            int bytes_read;
            try {
              while((bytes_read = from_client.read(request)) != -1) {
                to_server.write(request, 0, bytes_read);
                to_server.flush();
              }
            }
            catch (IOException e) {}

            // the client closed the connection to us, so  close our 
            // connection to the server.  This will also cause the 
            // server-to-client loop in the main thread exit.
            try {to_server.close();} catch (IOException e) {}
          }
        };

        // Start the client-to-server request thread running
        t.start();  

        // Meanwhile, in the main thread, read the server's responses
        // and pass them back to the client.  This will be done in
        // parallel with the client-to-server request thread above.
        int bytes_read;
        try {
          while((bytes_read = from_server.read(reply)) != -1) {
            to_client.write(reply, 0, bytes_read);
            to_client.flush();
          }
        }
        catch(IOException e) {}

        // The server closed its connection to us, so close our 
        // connection to our client.  This will make the other thread exit.
        to_client.close();
      }
      catch (IOException e) { System.err.println(e); }
      // Close the sockets no matter what happens each time through the loop.
      finally { 
        try { 
          if (server != null) server.close(); 
          if (client != null) client.close(); 
        }
        catch(IOException e) {}
      }
    }
  }
}

Code obtained from http://examples.oreilly.com/jenut/SimpleProxyServer.java

I was wondering how I would be able to extract the HOSTNAME from the inputstream and use that information extracted to pass to the method below.

        try { server = new Socket(host, remoteport); } 
        catch (IOException e) {
          PrintWriter out = new PrintWriter(new OutputStreamWriter(to_client));
          out.println("Proxy server cannot connect to " + host + ":" +
                      remoteport + ":\n" + e);
          out.flush();
          client.close();
          continue;
        }

I've tried creating a method that converts the InputStream into a String format but it seems to make the program get stuck after assigning it to the variable. (Tried something like this over here - Read/convert an InputStream to a String)

Community
  • 1
  • 1
HelloWorld
  • 83
  • 1
  • 8
  • If you're trying to write an HTTP proxy don't start from here. This code is from 1997 and only works for HTTP 1.0 absent the `Connection: keepalive` feature. In any case this is no way to write an HTTP proxy. You need to read enough of the headers to determine the target and then just copy bytes in both directions simultaneously. – user207421 Jan 29 '17 at 03:35
  • @EJP What happens if I don't use threading? Will it not work at all? Or will it just be slow? – HelloWorld Jan 29 '17 at 07:48
  • It won't work at all unless you write a lot more code. You would have to do a lot more work parsing the headers and possibly the content to know where the end of the request was, and then switch to receiving the response, and ditto to know where the end of the response was, and then switch back to reading another request. It's too hard. – user207421 Feb 02 '17 at 17:49

1 Answers1

1

You can create a separate ByteArrayOutputStream to get the information from the InputStream.

...
 final OutputStream to_client= client.getOutputStream();
 ByteArrayOutputStream baos = new ByteArrayOutputStream();
...

And then in the while loop you can write to baos as well

...
    while((bytes_read = from_server.read(reply)) != -1) {
                to_client.write(reply, 0, bytes_read);
                to_client.flush();
                baos.write(reply, 0, bytes_read);
              }
    baos.flush();
...

And you can finally get the string from baos.

String requestString = new String(baos.toByteArray());

Then, you can search the Host header by doing this:

String[] headers = requestString.split("\n");
for (int i = 0; i < headers.length; i++) {
    if (headers[i].startsWith("Host")) {
        String[] hostHeader = headers[i].split(":");
        if (hostHeader.length > 1) {
            host = hostHeader[1];
        }
    }
 }
alayor
  • 4,537
  • 6
  • 27
  • 47
  • I'm looking to specifically extract the host/whatever the client wants to connect to from here: final InputStream from_client = client.getInputStream(); But for some reason if I mess with this specific InputStream and attempt to extract the hostname, it hangs the program. – HelloWorld Jan 29 '17 at 01:26
  • I tried that method, for some reason it still hangs. – HelloWorld Jan 29 '17 at 02:22
  • In which line it hangs? – alayor Jan 29 '17 at 04:22