兼容https和http协议的java代理服务器代码

最近做的一个http代理小程序,同时支持http和http。
1、获取http代理请求的头部信息,区分https还是http,做不同的处理

[java] view plain copy
/** 
 * 解析头部信息 
 * 
 */  
public final class HttpHeader {  
  
    private List<String> header=new ArrayList<String>();  
      
    private String method;  
    private String host;  
    private String port;  
      
    public static final int MAXLINESIZE = 4096;  
      
    public static final String METHOD_GET="GET";  
    public static final String METHOD_POST="POST";  
    public static final String METHOD_CONNECT="CONNECT";  
      
    private HttpHeader(){}  
      
    /** 
     * 从数据流中读取请求头部信息,必须在放在流开启之后,任何数据读取之前 
     * @param in 
     * @return 
     * @throws IOException 
     */  
    public static final HttpHeader readHeader(InputStream in) throws IOException {  
        HttpHeader header = new HttpHeader();  
        StringBuilder sb = new StringBuilder();  
        //先读出交互协议来,  
        char c = 0;  
        while ((c = (char) in.read()) != '\n') {  
            sb.append(c);  
            if (sb.length() == MAXLINESIZE) {//不接受过长的头部字段  
                break;  
            }  
        }  
        //如能识别出请求方式则则继续,不能则退出  
        if(header.addHeaderMethod(sb.toString())!=null){  
            do {  
                sb = new StringBuilder();  
                while ((c = (char) in.read()) != '\n') {  
                    sb.append(c);  
                    if (sb.length() == MAXLINESIZE) {//不接受过长的头部字段  
                        break;  
                    }  
                }  
                if (sb.length() > 1 && header.notTooLong()) {//如果头部包含信息过多,抛弃剩下的部分  
                    header.addHeaderString(sb.substring(0, sb.length() - 1));  
                } else {  
                    break;  
                }  
            } while (true);  
        }  
          
        return header;  
    }  
      
    /** 
     *  
     * @param str 
     */  
    private void addHeaderString(String str){  
        str=str.replaceAll("\r", "");  
        header.add(str);  
        if(str.startsWith("Host")){//解析主机和端口  
            String[] hosts= str.split(":");  
            host=hosts[1].trim();  
            if(method.endsWith(METHOD_CONNECT)){  
                port=hosts.length==3?hosts[2]:"443";//https默认端口为443  
            }else if(method.endsWith(METHOD_GET)||method.endsWith(METHOD_POST)){  
                port=hosts.length==3?hosts[2]:"80";//http默认端口为80  
            }  
        }  
    }  
      
    /** 
     * 判定请求方式 
     * @param str 
     * @return 
     */  
    private String addHeaderMethod(String str){  
        str=str.replaceAll("\r", "");  
        header.add(str);  
        if(str.startsWith(METHOD_CONNECT)){//https链接请求代理  
            method=METHOD_CONNECT;  
        }else if(str.startsWith(METHOD_GET)){//http GET请求  
            method=METHOD_GET;  
        }else if(str.startsWith(METHOD_POST)){//http POST请求  
            method=METHOD_POST;  
        }  
        return method;  
    }  
      
      
    @Override  
    public String toString() {  
        StringBuilder sb=new StringBuilder();  
        for(String str : header){  
            sb.append(str).append("\r\n");  
        }  
        sb.append("\r\n");  
        return sb.toString();  
    }  
      
    public boolean notTooLong(){  
        return header.size()<=16;  
    }  
  
  
    public List<String> getHeader() {  
        return header;  
    }  
  
  
    public void setHeader(List<String> header) {  
        this.header = header;  
    }  
  
  
    public String getMethod() {  
        return method;  
    }  
  
  
    public void setMethod(String method) {  
        this.method = method;  
    }  
  
    public String getHost() {  
        return host;  
    }  
  
  
    public void setHost(String host) {  
        this.host = host;  
    }  
  
  
    public String getPort() {  
        return port;  
    }  
  
  
    public void setPort(String port) {  
        this.port = port;  
    }  
      
}  

2、任务处理
[java] view plain copy
/** 
 * 将客户端发送过来的数据转发给请求的服务器端,并将服务器返回的数据转发给客户端 
 * 
 */  
public class ProxyTask implements Runnable {  
    private Socket socketIn;  
    private Socket socketOut;  
      
    private long totalUpload=0l;//总计上行比特数  
    private long totalDownload=0l;//总计下行比特数  
  
    public ProxyTask(Socket socket) {  
        this.socketIn = socket;  
    }  
      
    private static final SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");  
    /** 已连接到请求的服务器 */  
    private static final String AUTHORED = "HTTP/1.1 200 Connection established\r\n\r\n";  
    /** 本代理登陆失败(此应用暂时不涉及登陆操作) */  
    //private static final String UNAUTHORED="HTTP/1.1 407 Unauthorized\r\n\r\n";  
    /** 内部错误 */  
    private static final String SERVERERROR = "HTTP/1.1 500 Connection FAILED\r\n\r\n";  
      
    @Override  
    public void run() {  
          
        StringBuilder builder=new StringBuilder();  
        try {  
            builder.append("\r\n").append("Request Time  :" + sdf.format(new Date()));  
              
            InputStream isIn = socketIn.getInputStream();  
            OutputStream osIn = socketIn.getOutputStream();  
            //从客户端流数据中读取头部,获得请求主机和端口  
            HttpHeader header = HttpHeader.readHeader(isIn);  
              
            //添加请求日志信息  
            builder.append("\r\n").append("From    Host  :" + socketIn.getInetAddress());  
            builder.append("\r\n").append("From    Port  :" + socketIn.getPort());  
            builder.append("\r\n").append("Proxy   Method:" + header.getMethod());  
            builder.append("\r\n").append("Request Host  :" + header.getHost());  
            builder.append("\r\n").append("Request Port  :" + header.getPort());  
              
            //如果没解析出请求请求地址和端口,则返回错误信息  
            if (header.getHost() == null || header.getPort() == null) {  
                osIn.write(SERVERERROR.getBytes());  
                osIn.flush();  
                return ;  
            }  
              
            // 查找主机和端口  
            socketOut = new Socket(header.getHost(), Integer.parseInt(header.getPort()));  
            socketOut.setKeepAlive(true);  
            InputStream isOut = socketOut.getInputStream();  
            OutputStream osOut = socketOut.getOutputStream();  
            //新开一个线程将返回的数据转发给客户端,串行会出问题,尚没搞明白原因  
            Thread ot = new DataSendThread(isOut, osIn);  
            ot.start();  
            if (header.getMethod().equals(HttpHeader.METHOD_CONNECT)) {  
                // 将已联通信号返回给请求页面  
                osIn.write(AUTHORED.getBytes());  
                osIn.flush();  
            }else{  
                //http请求需要将请求头部也转发出去  
                byte[] headerData=header.toString().getBytes();  
                totalUpload+=headerData.length;  
                osOut.write(headerData);  
                osOut.flush();  
            }  
            //读取客户端请求过来的数据转发给服务器  
            readForwardDate(isIn, osOut);  
            //等待向客户端转发的线程结束  
            ot.join();  
        } catch (Exception e) {  
            e.printStackTrace();  
            if(!socketIn.isOutputShutdown()){  
                //如果还可以返回错误状态的话,返回内部错误  
                try {  
                    socketIn.getOutputStream().write(SERVERERROR.getBytes());  
                } catch (IOException e1) {}  
            }  
        } finally {  
            try {  
                if (socketIn != null) {  
                    socketIn.close();  
                }  
            } catch (IOException e) {}  
            if (socketOut != null) {  
                try {  
                    socketOut.close();  
                } catch (IOException e) {}  
            }  
            //纪录上下行数据量和最后结束时间并打印  
            builder.append("\r\n").append("Up    Bytes  :" + totalUpload);  
            builder.append("\r\n").append("Down  Bytes  :" + totalDownload);  
            builder.append("\r\n").append("Closed Time  :" + sdf.format(new Date()));  
            builder.append("\r\n");  
            logRequestMsg(builder.toString());  
        }     
    }  
      
    /** 
     * 避免多线程竞争把日志打串行了 
     * @param msg 
     */  
    private synchronized void logRequestMsg(String msg){  
        System.out.println(msg);  
    }  
  
    /** 
     * 读取客户端发送过来的数据,发送给服务器端 
     *  
     * @param isIn 
     * @param osOut 
     */  
    private void readForwardDate(InputStream isIn, OutputStream osOut) {  
        byte[] buffer = new byte[4096];  
        try {  
            int len;  
            while ((len = isIn.read(buffer)) != -1) {  
                if (len > 0) {  
                    osOut.write(buffer, 0, len);  
                    osOut.flush();  
                }  
                totalUpload+=len;  
                if (socketIn.isClosed() || socketOut.isClosed()) {  
                    break;  
                }  
            }  
        } catch (Exception e) {  
            try {  
                socketOut.close();// 尝试关闭远程服务器连接,中断转发线程的读阻塞状态  
            } catch (IOException e1) {}  
        }  
    }  
  
    /** 
     * 将服务器端返回的数据转发给客户端 
     *  
     * @param isOut 
     * @param osIn 
     */  
    class DataSendThread extends Thread {  
        private InputStream isOut;  
        private OutputStream osIn;  
  
        DataSendThread(InputStream isOut, OutputStream osIn) {  
            this.isOut = isOut;  
            this.osIn = osIn;  
        }  
  
        @Override  
        public void run() {  
            byte[] buffer = new byte[4096];  
            try {  
                int len;  
                while ((len = isOut.read(buffer)) != -1) {  
                    if (len > 0) {  
                        // logData(buffer, 0, len);  
                        osIn.write(buffer, 0, len);  
                        osIn.flush();  
                        totalDownload+=len;  
                    }  
                    if (socketIn.isOutputShutdown() || socketOut.isClosed()) {  
                        break;  
                    }  
                }  
            } catch (Exception e) {}  
        }  
    }  
  
}  

3、用线程池分发任务
[java] view plain copy
/** 
 * http 代理程序 
 * @author lulaijun 
 * 
 */  
public class SocketProxy {  
      
    static final int listenPort=8002;  
  
    public static void main(String[] args) throws Exception {  
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");  
        ServerSocket serverSocket = new ServerSocket(listenPort);  
        final ExecutorService tpe=Executors.newCachedThreadPool();  
        System.out.println("Proxy Server Start At "+sdf.format(new Date()));  
        System.out.println("listening port:"+listenPort+"……");  
        System.out.println();  
        System.out.println();  
      
        while (true) {  
            Socket socket = null;  
            try {  
                socket = serverSocket.accept();  
                socket.setKeepAlive(true);  
                //加入任务列表,等待处理  
                tpe.execute(new ProxyTask(socket));  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
        }  
    }  
      
}  

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值