JAVA实现网络请求代理之HTTP篇

‍‍

JAVA实现网络请求代理之HTTP篇 (一)

JAVA实现网络请求代理之Socket篇(二)

Java代理服务器之截取,篡改HTTP请求(应用篇)


首先,需要弄明白代理服务的原理,和http协议几种请求类型的构成。

代理服务器原理

JAVA实现网络请求代理之HTTP篇_第1张图片

http协议详解

1、请求行以一个方法符号开头,以空格分开,后面跟着请求的URI和协议的版本,格式如下:

Method RequestURI HTTP-Version CRLF  
其中 Method表示请求方法;Request-URI是一个统一资源标识符;HTTP-Version表示请求的HTTP协议版本;CRLF表示回车和换行(除了作为结尾的CRLF外,不允许出现单独的CR或LF字符)。

例如:

GET请求

GET http://www.baidu.com/ HTTP/1.1                  注:请求行     【方法  统一资源标识符 HTTP协议版本】

Host: www.baidu.com                                            注:主机

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0        注:浏览器信息

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8                               注:返回数据类型

Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3                                          注:语言权重值

Accept-Encoding: gzip, deflate                        注:压缩格式

Cookie: BAIDUID=528AC525A03A636F088835EED7DFA00F:FG=1;     BIDUPSID=F7A856B7B734889B5BB1227150199A90; PSTM=1441605421; BD_UPN=13314752    注:cookie

Connection: keep-alive     注:连接类型

POST请求

POST /foo.php HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729)
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: 300
Connection: keep-alive
Referer: 
http://localhost/test.php
Content-Type: application/x-www-form-urlencoded
Content-Length: 43                                                                注:内容长度
first_name=John&last_name=Doe&action=Submit                      注:提交数据

接下来就介绍如何具体实现HTTP代理服务器。

新建一个处理Http请求的服务线程,监听808端口

package com.mato.proxy.http;


import java.net.ServerSocket;
import java.net.Socket;

/**
 * Created by cjl on 2015/9/8.
 */
public class HttpProxy extends Thread{
    private ServerSocket server;
    public HttpProxy(ServerSocket _server){
        server=_server;
        start();
    }
    public void run(){
    // 线程运行函数
        Socket connection;
        while(true){
            try{
                connection=server.accept();
                //接受到请求,就新建一个处理请求的服务线程,将当前请求传递到线程里面
                HTTPServerThread handler =new HTTPServerThread(connection);
            }
            catch(Exception e){}
        }
    }
}

接下来就是每一个请求处理线程的处理逻辑

package com.mato.proxy.http;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

/**
 * Created by cjl on 2015/9/8.
 */
public class HTTPServerThread extends Thread {

    private Socket client;

    public HTTPServerThread(Socket _connection) {
        client = _connection;
        start();
    }

    public void run() { // 线程运行函数
        byte buf[] = new byte[10000], buf1[] = new byte[10000], buf2[] = new byte[10000];
        int creadlen = 0, sreadlen = 0;
        int i;
        String s = null, s1 = null, s2 = null;
        Socket server = null;
        int port = 80;
        DataInputStream cin = null, //客户端输入流
                        sin = null; //服务端输入流
        DataOutputStream cout = null, //客户端输出流
                        sout = null;  //服务端输出流
        int method = 0;
        try {
            cin = new DataInputStream(client.getInputStream());
            cout = new DataOutputStream(client.getOutputStream());
            if (cin != null && cout != null) {
                creadlen = cin.read(buf, 0, 10000); // 从客户端读数据
                if (creadlen > 0) { // 读到数据
                    s = new String(buf);
                    if (s.indexOf("\r\n") != -1)
                        s = s.substring(0, s.indexOf("\r\n"));
                    if (s.indexOf("GET ") != -1)
                        method = 0;// 如读到 GET 请求
                    if (s.indexOf("CONNECT ") != -1) {
                        // 读到 CONNECT 请求 , 返回 HTTP 应答
                        s1 = s.substring(s.indexOf("CONNECT ") + 8, s.indexOf
                                ("HTTP/"));
                        s2 = s1;
                        s1 = s1.substring(0, s1.indexOf(":"));
                        s2 = s2.substring(s2.indexOf(":") + 1);
                        s2 = s2.substring(0, s2.indexOf(" "));
                        port = Integer.parseInt(s2);
                        method = 1;
                        sendAck(cout);
                    }
                    if (s.indexOf("POST ") != -1)// 如读到 POST 请求
                        method = 2;

                    if (s.indexOf("http://") != -1 && s.indexOf("HTTP/") != -1) {
                        // 从所读数据中取域名和端口号
                        s1 = s.substring(s.indexOf("http://") + 7, s.indexOf("HTTP/"));
                        s1 = s1.substring(0, s1.indexOf("/"));
                        if (s1.indexOf(":") != -1) {
                            s2 = s1;
                            s1 = s1.substring(0, s1.indexOf(":"));
                            s2 = s2.substring(s2.indexOf(":") + 1);
                            port = Integer.parseInt(s2);
                            method = 0;
                        }
                    }
                    if (s1 != null) {
                        server = new Socket(s1, port);
                        // 根据读到的域名和端口号建立套接字
                        sin = new DataInputStream(server.getInputStream());
                        sout = new DataOutputStream(server.getOutputStream());
                        if (sin != null && sout != null && server != null) {
                            if (method == 0) {
                                sreadlen = doGet(buf, creadlen, sin, cout, sout);
                            }
                            if (method == 1) { // 如读到 CONNECT 请求
                                sreadlen = doConnect(buf, sreadlen, cin, sin, cout, sout);
                            }
                            if (method == 2) { // 如读到 POST 请求
                                // 向外网发送 POST 请求
                                doPost(buf, creadlen, sreadlen, cin, sin, cout, sout);
                            }
                        }
                    }
                }
            }
            // 执行关闭操作
            if (sin != null) sin.close();
            if (sout != null) sout.close();
            if (server != null) server.close();
            if (cin != null) cin.close();
            if (cout != null) cout.close();
            if (client != null) client.close();
        } catch (IOException e) {
        }
    }

    /**
     * 发生确认
     *
     * @param cout  输出流
     * @throws IOException
     */
    private void sendAck(DataOutputStream cout) throws IOException {
        String s2;
        byte[] buf2;
        s2 = "HTTP/1.0 200 Connection established\r\n";
        s2 = s2 + "Proxy-agent: proxy\r\n\r\n";
        buf2 = s2.getBytes();
        cout.write(buf2);
        cout.flush();
    }

    /**
     * 处理POST 请求
     * @param buf   数据缓存区
     * @param creadlen   客户端读取的buf的长度
     * @param sreadlen    服务端读取的buf的长度
     * @param cin        客户端输入流
     * @param sin        服务端输入流
     * @param cout       客户端输出流
     * @param sout       服务端输出流
     * @throws IOException
     */
    private void doPost(byte[] buf, int creadlen, int sreadlen, DataInputStream cin, DataInputStream sin, DataOutputStream cout, DataOutputStream sout) throws IOException {
        write(buf, creadlen, sout);
        // 建立线程 , 用于从外网读数据 , 并返回给内网客户端
        HTTPChannel thread1 = new HTTPChannel(sin, cout);
        while ((sreadlen = cin.read(buf, 0, 10000))!=-1) { // 循环
            try {
                System.out.println("post>>"+new String(buf));
                if (sreadlen > 0) { // 读到数据 , 则发送给外网
                    write(buf, sreadlen, sout);
                }
            } catch (Exception e1) {
                break;
            }
        }
    }

    /**
     * 写数据
     * @param buf    缓冲区
     * @param creadlen   读取的偏移量
     * @param sout       输出流
     * @throws IOException
     */
    private void write(byte[] buf, int creadlen, DataOutputStream sout) throws IOException {
        sout.write(buf, 0, creadlen);
        sout.flush();
    }

    /**
     * 处理HTTPconnect  待定
     * @param buf       缓冲区
     * @param sreadlen    服务端读取的buf的长度
     * @param cin        客户端输入流
     * @param sin        服务端输入流
     * @param cout       客户端输出流
     * @param sout       服务端输出流
     * @return
     */
    private int doConnect(byte[] buf, int sreadlen, DataInputStream cin, DataInputStream sin, DataOutputStream cout, DataOutputStream sout) {
        // 建立线程 , 用于从外网读数据 , 并返回给内网客户端
        HTTPChannel thread1 = new HTTPChannel(sin, cout);
        try {
            while ((sreadlen = cin.read(buf, 0, 10000))!=-1) { // 循环
                    // 从内网读数据
                    if (sreadlen > 0) {
                    // 读到数据 , 则发送给外网
                        System.out.println("CONN>>" + new String(buf));
                        write(buf, sreadlen, sout);
                    }
            }
        } catch (Exception e1) {

        }

        return sreadlen;
    }

    /**
     * 处理GET请求
     * @param buf    缓冲区
     * @param creadlen    客户端读取buf的长度
     * @param sin       服务端输入流
     * @param cout      客户端输出流
     * @param sout      服务端输出流
     * @return
     * @throws IOException
     */
    private int doGet(byte[] buf, int creadlen, DataInputStream sin, DataOutputStream cout, DataOutputStream sout) throws IOException {
        int sreadlen;// 如读到 GET 请求,向外网发出 GET 请求
        System.out.println("[get] >> " + new String(buf));
        write(buf, creadlen, sout);
        while ((sreadlen = sin.read(buf, 0, 10000))!=-1) { // 循环
            try {
                if (sreadlen > 0) {
                    System.out.println("[get] << " + new String(buf));
                    write(buf, sreadlen, cout);
                }
            } catch (Exception e) {
                break;
            } // 异常则退出
        }
        return sreadlen;
    }
}


再接下来就是上面线程用到的通道类

package com.mato.proxy.http;

import java.io.DataInputStream;
import java.io.DataOutputStream;

/**
 * Created by cjl on 2015/9/8.
 */
public class HTTPChannel extends Thread{
    private DataInputStream in;
    private DataOutputStream out;
    public HTTPChannel(DataInputStream sin, DataOutputStream cout) {
        in = sin;
        out = cout;
        start();
    }

    @Override
    public void run() {
        int len = 0;
        byte buf[] = new byte[10240];
        while (true) {
            try {
                if (len == -1) {
                    break;
                }
                if (len > 0) {
                    System.out.println("post<<"+new String(buf));
                    out.write(buf, 0, len);
                    out.flush();
                }
            } catch (Exception e) {
                break;
            }
        }
    }
}

最后就是启动代理服务器的代码

public static void main(String[] args) {
    try{
        ServerSocket httpserver=new ServerSocket(808);
        // 建立 HTTP 侦听套接字
        System.out.println ("HTTP Proxy started on "+httpserver.getLocalPort());
        HttpProxy httpproxy=new HttpProxy(httpserver); // 建立HTTP 侦听线程
        
    }catch(IOException e){}
}

这样整个HTTP代理服务就构建好了,不过暂时只能代理HTTP请求,不能代理HTTPS

要使用代理服务的话还要在浏览器里面配置代理地址和上面的端口808,过程就不赘述了。可以自行百度。

这是我的火狐浏览器的配置:

JAVA实现网络请求代理之HTTP篇_第2张图片

最后效果图:


JAVA实现网络请求代理之HTTP篇_第3张图片

到这里就告一段落了,后面介绍Socket代理服务的实现


你可能感兴趣的:(JAVA代理服务器,HTTP代理服务器的实现。)