All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.gallery.db.util.ProxyUtil Maven / Gradle / Ivy

package io.gallery.db.util;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Base64;
import java.util.Date;

/**
 * 网络代理服务器,隧道模式
 */
public class ProxyUtil {
    private static final int bufferSize = 8092;

    public static void main(String[] args) {
        startService(808);
    }

    public static void startService(int localPort) {
        try {
            //开启一个ServerSocket服务器,监听请求的到来.
            ServerSocket localServerSocket = new ServerSocket(localPort);
            log("Proxy running at " + localServerSocket.getInetAddress().getHostAddress() + ":" + localPort);
            // 一直监听,接收到新连接,则开启新线程去处理
            while (true) {
                new ProxySocketThread(localServerSocket.accept()).start();
            }
        } catch (IOException e) {
            error("exception : %s %s", e.getClass(), e.getLocalizedMessage());
        }
    }

    private static void log(Object message, Object... args) {
        Date dat = new Date();
        String msg = String.format("%1$tF %1$tT %2$-5s %3$s%n", dat, Thread.currentThread().getId(), String.format(message.toString(), args));
        System.out.print(msg);
    }

    private static void error(Object message, Object... args) {
        Date dat = new Date();
        String msg = String.format("%1$tF %1$tT %2$-5s %3$s%n", dat, Thread.currentThread().getId(), String.format(message.toString(), args));
        System.out.print(msg);
    }

    /**
     * IO操作中共同的关闭方法
     *
     * @param socket Socket
     */
    protected static void closeIo(Socket socket) {
        if (null != socket) {
            try {
                socket.close();
            } catch (IOException e) {
                error("exception : %s %s", e.getClass(), e.getLocalizedMessage());
            }
        }
    }

    /**
     * IO操作中共同的关闭方法
     *
     * @param closeable Closeable
     */
    protected static void closeIo(Closeable closeable) {
        if (null != closeable) {
            try {
                closeable.close();
            } catch (IOException e) {
                error("exception : %s %s", e.getClass(), e.getLocalizedMessage());
            }
        }
    }

    private static class ProxySocketThread extends Thread {
        private Socket localSocket;
        private Socket remoteSocket;
        private InputStream lin;
        private InputStream rin;
        private OutputStream lout;
        private OutputStream rout;

        public ProxySocketThread(Socket socket) {
            this.localSocket = socket;
        }

        public void run() {
            //获取远程socket的地址,然后进行打印
            String addr = localSocket.getRemoteSocketAddress().toString();
            log("------ start socket : %s ------", addr);
            try {
                lin = localSocket.getInputStream();
                lout = localSocket.getOutputStream();

                StringBuilder headStr = new StringBuilder();
                BufferedReader br = new BufferedReader(new InputStreamReader(lin));
                //读取HTTP请求头,并拿到HOST请求头和method
                String line;
                String host = "";
                String proxy_Authorization = "";
                boolean isNeedLogin = false;
                while ((line = br.readLine()) != null) {
                    //打印http协议头
                    log(line);
                    headStr.append(line).append("\r\n");
                    if (line.length() == 0) {
                        break;
                    } else {
                        String[] temp = line.split(" ");
                        if (temp[0].contains("Host")) {
                            host = temp[1];
                        }
                        //如果配置了需要登陆,就解析消息头里面的Proxy-Authorization字段,认证的账号和密码信息是通过base64加密传过来的。
                        if (isNeedLogin && temp[0].contains("Proxy-Authorization")) {//获取认证信息
                            proxy_Authorization = temp[2];
                        }
                    }
                }

                String type = headStr.substring(0, headStr.indexOf(" "));
                //根据host头解析出目标服务器的host和port
                String[] hostTemp = host.split(":");
                host = hostTemp[0];
                int port = 80;
                if (hostTemp.length > 1) {
                    port = Integer.parseInt(hostTemp[1]);
                }

                boolean isLogin = false;
                //如果需要登录,校验登录是否通过
                if (isNeedLogin) {
                    //通过username和password进行base64加密得到一个串,然后和请求里面传过来的Proxy-Authorization比较,一致的话就认证成功。
                    String username = "admin";
                    String password = "nimda";
                    String authenticationEncoding = Base64.getEncoder().encodeToString((username + ":" + password).getBytes());
                    if (proxy_Authorization.equals(authenticationEncoding)) {
                        isLogin = true;//登录通过
                        log("login success, basic: %s", proxy_Authorization);
                    } else {
                        log("httpproxy server need login,but login failed .");
                    }
                }

                //不需要登录或已被校验登录成功,才进入代理,否则直接程序结束,关闭连接
                if (!isNeedLogin || isLogin) {
                    //连接到目标服务器
                    remoteSocket = new Socket(host, port);//进行远程连接
                    rin = remoteSocket.getInputStream();
                    rout = remoteSocket.getOutputStream();
                    log("connect target %s %s %s", host, port, type);
                    //根据HTTP method来判断是https还是http请求
                    if ("CONNECT".equalsIgnoreCase(type)) {//https先建立隧道
                        lout.write("HTTP/1.1 200 Connection Established\r\n\r\n".getBytes());
                        lout.flush();
                    } else {//http直接将请求头转发
                        rout.write(headStr.toString().getBytes());
                        rout.flush();
                    }
                    new ReadThread().start();
                    //设置超时,超过时间未收到客户端请求,关闭资源
                    //remoteSocket.setSoTimeout(10000);
                    //写数据,负责读取客户端发送过来的数据,转发给远程
                    byte[] data = new byte[bufferSize];
                    int len = 0;
                    while ((len = lin.read(data)) > 0) {
                        if (len == bufferSize) {//读到了缓存大小一致的数据,不需要拷贝,直接使用
                            rout.write(data);
                        } else {//读到了比缓存大小的数据,需要拷贝到新数组然后再使用
                            byte[] dest = new byte[len];
                            System.arraycopy(data, 0, dest, 0, len);
                            rout.write(dest);
                        }
                        rout.flush();
                    }
                }
            } catch (Exception e) {
                error("exception : %s %s", e.getClass(), e.getLocalizedMessage());
            } finally {
                log("------ close socket :  %s ------", addr);
                closeIo(lin);
                closeIo(rin);
                closeIo(lout);
                closeIo(rout);
                closeIo(localSocket);
                closeIo(remoteSocket);
            }
        }

        //读数据线程负责读取远程数据后回写到客户端
        class ReadThread extends Thread {
            @Override
            public void run() {
                try {
                    byte[] data = new byte[bufferSize];
                    int len = 0;
                    while ((len = rin.read(data)) > 0) {
                        if (len == bufferSize) {//读到了缓存大小一致的数据,不需要拷贝,直接使用
                            lout.write(data);
                        } else {//读到了比缓存大小的数据,需要拷贝到新数组然后再使用
                            byte[] dest = new byte[len];
                            System.arraycopy(data, 0, dest, 0, len);
                            lout.write(dest);
                        }
                        lout.flush();
                    }
                } catch (IOException e) {
                    log(remoteSocket.getLocalAddress() + ":" + remoteSocket.getPort() + " remoteSocket InputStream disconnected.");
                }
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy