Trying to create a proxy, doesn't work

2019-06-24 05:44发布

问题:

I have an assignment to write a proxy server. Simple tests work, but when I configure firefox to use the proxy, the response input stream is never ready. Can you help?

ProxyServer (the important method)

public void start() {
        while (true) {
            Socket serverSocket;
            Socket clientSocket;
            BufferedWriter toClient;
            BufferedWriter toServer;
            try {
                //The client is meant to put data on the port, read the socket.
                clientSocket = listeningSocket.accept();
                Request request = new Request(clientSocket.getInputStream());
                System.out.println("Accepted a request!\n" + request);
                while(request.busy);
                //Make a connection to a real proxy.
                //Host & Port - should be read from the request
                URL url = null;
                try {
                    url = new URL(request.getRequestURL());
                } catch (MalformedURLException e){
                    url = new URL("http:\\"+request.getRequestHost()+request.getRequestURL());
                }

                //remove entry from cache if needed
                if (!request.getCacheControl().equals(CacheControl.CACHE) && cache.containsRequest(request)) {
                    cache.remove(request);
                }

                Response response = null;

                if (request.getRequestType() == RequestType.GET && request.getCacheControl().equals(CacheControl.CACHE) && cache.containsRequest(request)) {
                    response = cache.get(request);
                } else {
                    //Get the response from the destination
                    int remotePort = (url.getPort() == -1) ? 80 : url.getPort();
                    System.out.println("I am going to try to connect to: " + url.getHost() + " at port " + remotePort);
                    serverSocket = new Socket(url.getHost(), remotePort);
                    System.out.println("Connected.");

                    //write to the server - keep it open.
                    System.out.println("Writing to the server's buffer...");
                    toServer = new BufferedWriter(new OutputStreamWriter(serverSocket.getOutputStream()));
                    toServer.write(request.getFullRequest());
                    toServer.flush();
                    System.out.println("flushed.");

                    System.out.println("Getting a response...");
                    response = new Response(serverSocket.getInputStream());
                    System.out.println("Got a response!\n" + response);
                    //wait for the response
                    while(response.isBusy());   
                }

                if (request.getRequestType() == RequestType.GET && request.getCacheControl().equals(CacheControl.CACHE)) {
                    cache.put(request, response);
                }

                response = filter.filter(response);

                // Return the response to the client
                toClient = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
                toClient.write(response.getFullResponse());
                toClient.flush();
                toClient.close();

            } catch (IOException e) {
                e.printStackTrace();
                continue;
            }
        }
    }

The Response and Request classes are just very simple parsers for HTTP requests/responses. When I try to load a website through the proxy, I get:


EDIT 2

Here's another attempt. I added a debug print just before the line toClient.write(response.getFullResponse());

Accepted a request!
Request
==============================

GET http://t2.technion.ac.il/~srachum/ HTTP/1.1
Host: t2.technion.ac.il
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:2.0) Gecko/20100101 Firefox/4.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Proxy-Connection: keep-alive


I am going to try to connect to: t2.technion.ac.il at port 80
Connected.
Writing to the server's buffer...
flushed.
Getting a response...
Got a response!
Response
==============================

HTTP/1.1 200 OK
Date: Sat, 23 Apr 2011 15:54:08 GMT
Server: Apache/2.0.52 (Red Hat)
Last-Modified: Fri, 18 Mar 2011 23:45:24 GMT
ETag: "14928fc-877-49eca5f29cd00"
Accept-Ranges: bytes
Content-Length: 2167
Connection: close
Content-Type: text/html
X-Pad: avoid browser bug

<html>
    ...
</html>


I am going to write the following response:

HTTP/1.1 200 OK
Date: Sat, 23 Apr 2011 15:54:08 GMT
Server: Apache/2.0.52 (Red Hat)
Last-Modified: Fri, 18 Mar 2011 23:45:24 GMT
ETag: "14928fc-877-49eca5f29cd00"
Accept-Ranges: bytes
Content-Length: 2167
Connection: close
Content-Type: text/html
X-Pad: avoid browser bug

<html>
    ...
</html>

EDIT 3:

Request

package cs236369.proxy;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import cs236369.proxy.types.CacheControl;
import cs236369.proxy.types.HttpPatterns;
import cs236369.proxy.types.RequestHeader;
import cs236369.proxy.types.RequestType;


public class Request {
    private String fullRequest = "";
    private BufferedReader reader;
    private RequestHeader requestHeader;
    private String requestHost;
    boolean busy = true;
    private CacheControl cacheControl = CacheControl.CACHE;

    public CacheControl getCacheControl() {
        return cacheControl;
    }

    Request(String request) {
        this(new ByteArrayInputStream(request.getBytes()));
    }

    Request(InputStream input){
        reader = new BufferedReader(new InputStreamReader(input));
        try {
            while(!reader.ready()); //wait for initialization.

            String line;
            while ((line = reader.readLine()) != null) {
                fullRequest += "\r\n" + line;

                if (HttpPatterns.CACHE_CONTROL.matches(line)) {
                    cacheControl = (CacheControl) HttpPatterns.RESPONSE_CODE.process(line);
                } else if (HttpPatterns.REQUEST_HEADER.matches(line)) {
                    requestHeader = (RequestHeader) HttpPatterns.REQUEST_HEADER.process(line);
                } else if (HttpPatterns.HOST.matches(line)) {
                    requestHost = (String) HttpPatterns.HOST.process(line);
                }
            }
            fullRequest = "\r\n" + fullRequest.trim() + "\r\n\r\n";
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        busy = false;
    }

    public String getFullRequest() {
        return fullRequest;
    }

    public RequestType getRequestType() {
        return requestHeader.type;
    }

    public String getRequestURL() {
        return requestHeader.url;
    }

    public String getRequestProtocol() {
        return requestHeader.protocol;
    }

    public String getRequestHost() {
        return requestHost;
    }

    public boolean isBusy() {
        return busy;
    }

    @Override
    public String toString() {
        return "Request\n==============================\n" + fullRequest;
    }
}

Response

package cs236369.proxy;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import cs236369.proxy.types.CacheControl;
import cs236369.proxy.types.HttpPatterns;

public class Response {
    private String fullResponse = "";
    private BufferedReader reader;
    private boolean busy = true;
    private int responseCode;
    private CacheControl cacheControl;

    public Response(String input) {
        this(new ByteArrayInputStream(input.getBytes()));
    }

    public Response(InputStream input) {
        reader = new BufferedReader(new InputStreamReader(input));
        try {
            while (!reader.ready());//wait for initialization.

            String line;
            while ((line = reader.readLine()) != null) {
                fullResponse += "\r\n" + line;

                if (HttpPatterns.RESPONSE_CODE.matches(line)) {
                    responseCode = (Integer) HttpPatterns.RESPONSE_CODE.process(line);
                } else if (HttpPatterns.CACHE_CONTROL.matches(line)) {
                    cacheControl = (CacheControl) HttpPatterns.CACHE_CONTROL.process(line);
                }
            }
            reader.close();
            fullResponse = "\r\n" + fullResponse.trim() + "\r\n\r\n";
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
        busy = false;
    }

    public CacheControl getCacheControl() {
        return cacheControl;
    }

    public String getFullResponse() {
        return fullResponse;
    }

    public boolean isBusy() {
        return busy;
    }

    public int getResponseCode() {
        return responseCode;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((fullResponse == null) ? 0 : fullResponse.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (!(obj instanceof Response))
            return false;
        Response other = (Response) obj;
        if (fullResponse == null) {
            if (other.fullResponse != null)
                return false;
        } else if (!fullResponse.equals(other.fullResponse))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "Response\n==============================\n" + fullResponse;
    }
}

回答1:

Once I was writing a program to send HTTP requests... My code was like this:

String host="www.google.com";
String request="GET / HTTP/1.0\r\nHost: "+host+"\r\nAccept-Encoding: gzip\r\n\r\n";
System.out.println(request);
Socket sock=new Socket(host,80);
InputStream inp=sock.getInputStream();
OutputStream outp=sock.getOutputStream();
outp.write(request.getBytes());
byte[] buff=new byte[999];
while(true){
    int n=inp.read(buff);
    if(n<0) break;
    System.out.println(new String(buff,0,n));
}
inp.close();
outp.close();
sock.close();

This code works. At first sight, it looks like yours. You can try combining the two and watch when problems start to occur. Maybe there is something wrong in your Response parser?

P.S. Are you sure that the original request you got from the browser ends with two line breaks? In the log you posted it seems that there is only one blank line...

EDIT: I compiled your code with minor modifications, and it works fine. The things I did:

  • Commented out everything about CacheControl, filtering and RequestHeader because I don't have sources for these classes;
  • Added simple parsing of URL and host because it won't work without them;
  • Added a check to remove Accept-Encoding because many servers use gzip which gets corrupted in this program;
  • Added a check to stop parsing the request after two line breaks.

This program serves as a proxy server for Firefox and works fine with HTML code. Please compile my version and try whether it works for you. Probably there is something to do with Firefox settings?

Please note that this proxy server is corrupting binary data such as images and gzipped HTML. This must be caused by the usage of InputStreamReader and OutputStreamWriter; they convert bytes to characters and vice versa, this is good for text but for binary data you'd better use InputStream and OutputStream "as is".

public class AmirRachum {

    public static void main(String[] args) {
        try{
            int port=38824;

            ServerSocket listeningSocket=new ServerSocket(port);
            System.out.println("Socket created");

            while (true) {
                Socket serverSocket;
                Socket clientSocket;
                BufferedWriter toClient;
                BufferedWriter toServer;
                try {
                    //The client is meant to put data on the port, read the socket.
                    clientSocket = listeningSocket.accept();
                    Request request = new Request(clientSocket.getInputStream());
                    System.out.println("Accepted a request!\n" + request);
                    while(request.busy);
                    //Make a connection to a real proxy.
                    //Host & Port - should be read from the request
                    URL url = null;
                    try {
                        url = new URL(request.getRequestURL());
                    } catch (MalformedURLException e){
                        url = new URL("http:\\"+request.getRequestHost()+request.getRequestURL());
                    }

                    //remove entry from cache if needed
             /*       if (!request.getCacheControl().equals(CacheControl.CACHE) && cache.containsRequest(request)) {
                        cache.remove(request);
                    }*/

                    Response response = null;

             /*       if (request.getRequestType() == RequestType.GET && request.getCacheControl().equals(CacheControl.CACHE) && cache.containsRequest(request)) {
                        response = cache.get(request);
                    } else*/ {
                        //Get the response from the destination
                        int remotePort = (url.getPort() == -1) ? 80 : url.getPort();
                        System.out.println("I am going to try to connect to: " + url.getHost() + " at port " + remotePort);
                        serverSocket = new Socket(url.getHost(), remotePort);
                        System.out.println("Connected.");

                        //write to the server - keep it open.
                        System.out.println("Writing to the server's buffer...");
                        toServer = new BufferedWriter(new OutputStreamWriter(serverSocket.getOutputStream()));
                        toServer.write(request.getFullRequest());
                        toServer.flush();
                        System.out.println("flushed.");

                        System.out.println("Getting a response...");
                        response = new Response(serverSocket.getInputStream());
                        System.out.println("Got a response!\n" + response);
                        //wait for the response
                        while(response.isBusy());   
                    }

            /*        if (request.getRequestType() == RequestType.GET && request.getCacheControl().equals(CacheControl.CACHE)) {
                        cache.put(request, response);
                    }

                    response = filter.filter(response);*/

                    // Return the response to the client
                    toClient = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
                    toClient.write(response.getFullResponse());
                    toClient.flush();
                    toClient.close();

                } catch (IOException e) {
                    e.printStackTrace();
                    continue;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static class Request {
        private String fullRequest = "";
        private BufferedReader reader;
    //    private RequestHeader requestHeader;
        private String requestHost;
        private String requestURL;
        boolean busy = true;
    //    private CacheControl cacheControl = CacheControl.CACHE;

     /*   public CacheControl getCacheControl() {
            return cacheControl;
        }*/

        Request(String request) {
            this(new ByteArrayInputStream(request.getBytes()));
        }

        Request(InputStream input){
            reader = new BufferedReader(new InputStreamReader(input));
            try {
                while(!reader.ready()); //wait for initialization.

                String line;
                while ((line = reader.readLine()) != null) {
                    if(!line.startsWith("Accept-Encoding:")) fullRequest += "\r\n" + line;

                /*    if (HttpPatterns.CACHE_CONTROL.matches(line)) {
                        cacheControl = (CacheControl) HttpPatterns.RESPONSE_CODE.process(line);
                    } else if (HttpPatterns.REQUEST_HEADER.matches(line)) {
                        requestHeader = (RequestHeader) HttpPatterns.REQUEST_HEADER.process(line);
                    } else if (HttpPatterns.HOST.matches(line)) {
                        requestHost = (String) HttpPatterns.HOST.process(line);
                    }*/
                    if(line.startsWith("GET ")){requestURL=line.split(" ")[1];System.out.println("url \""+requestURL+"\"");}
                    if(line.startsWith("Host:")){requestHost=line.substring(6);System.out.println("Host \""+requestHost+"\"");}
                    if(line.length()==0){System.out.println("empty line");break;}
                }
                fullRequest = "\r\n" + fullRequest.trim() + "\r\n\r\n";
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            busy = false;
        }

        public String getFullRequest() {
            return fullRequest;
        }

    /*    public RequestType getRequestType() {
            return requestHeader.type;
        }*/

        public String getRequestURL() {
            return requestURL;
        }

    /*    public String getRequestProtocol() {
            return requestHeader.protocol;
        }*/

        public String getRequestHost() {
            return requestHost;
        }

        public boolean isBusy() {
            return busy;
        }

        //@Override
        public String toString() {
            return "Request\n==============================\n" + fullRequest;
        }
    }

    public static class Response {
        private String fullResponse = "";
        private BufferedReader reader;
        private boolean busy = true;
    //    private int responseCode;
    //    private CacheControl cacheControl;

        public Response(String input) {
            this(new ByteArrayInputStream(input.getBytes()));
        }

        public Response(InputStream input) {
            reader = new BufferedReader(new InputStreamReader(input));
            try {
                while (!reader.ready());//wait for initialization.

                String line;
                while ((line = reader.readLine()) != null) {
                    fullResponse += "\r\n" + line;

               /*     if (HttpPatterns.RESPONSE_CODE.matches(line)) {
                        responseCode = (Integer) HttpPatterns.RESPONSE_CODE.process(line);
                    }/* else if (HttpPatterns.CACHE_CONTROL.matches(line)) {
                        cacheControl = (CacheControl) HttpPatterns.CACHE_CONTROL.process(line);
                    }*/
                }
                reader.close();
                fullResponse = "\r\n" + fullResponse.trim() + "\r\n\r\n";
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } 
            busy = false;
        }

    /*    public CacheControl getCacheControl() {
            return cacheControl;
        }*/

        public String getFullResponse() {
            return fullResponse;
        }

        public boolean isBusy() {
            return busy;
        }

     /*   public int getResponseCode() {
            return responseCode;
        }*/

        //@Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result
                    + ((fullResponse == null) ? 0 : fullResponse.hashCode());
            return result;
        }

        //@Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (!(obj instanceof Response))
                return false;
            Response other = (Response) obj;
            if (fullResponse == null) {
                if (other.fullResponse != null)
                    return false;
            } else if (!fullResponse.equals(other.fullResponse))
                return false;
            return true;
        }

        //@Override
        public String toString() {
            return "Response\n==============================\n" + fullResponse;
        }
    }

}