欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JDK 自带 HttpURLConnection 实现简单HTTP代理服务器

程序员文章站 2024-02-04 20:36:28
...

自己撸的透传方式全代理实现HTTP请求和响应的代码  分享给有需要的人,代码不解释了,自己看,亲测可以代理,包括下载大文件。

public class UnionPayProxyServlet extends HttpServlet {

    private String pathPrefix;
    private boolean includePathPrefix;
    private String host;
    private String port;

    // 上下文信息
    static class ContextInfo {
        public String ctxPath;
        public String method;
        public String requestURI;
        public String queryString;
        public String queryStringPart;
        public String targetUri;
        public String targetUrl;
    }

    @Override
    public void init() {
        this.pathPrefix = getInitParameter("pathPrefix");
        this.includePathPrefix = Boolean.parseBoolean(getInitParameter("includePathPrefix"));
        this.host = getInitParameter("host");
        this.port = getInitParameter("port");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        ContextInfo ctxInfo = getContextInfo(req);
        Log.info("接收到了网络请求(" + ctxInfo.method + "):" + ctxInfo.requestURI + ctxInfo.queryStringPart);
        Log.info("目标网络请求URL为:" + ctxInfo.targetUrl);
        // 获取目标地址的连接
        HttpURLConnection connection = null;
        try {
            connection = getConnection(ctxInfo);
        } catch (Exception e) {
            Log.error("获取目标地址" + ctxInfo.targetUrl + "的连接失败:" + e.getMessage());
            safeCloseConnection(connection);
            resp.setStatus(HttpStatus.BAD_GATEWAY.value());
            resp.getOutputStream().flush();
            return;
        }
        try {
            doProxy(ctxInfo, connection, req, resp);
        } catch (Exception e) {
            Log.error("代理地址" + req.getRequestURI() + "失败:" + e.getMessage());
            resp.setStatus(HttpStatus.BAD_GATEWAY.value());
            resp.getOutputStream().flush();
        } finally {
            safeCloseConnection(connection);
        }
    }

    // 获取要代理的目标地址URL
    private ContextInfo getContextInfo(HttpServletRequest request) {
        ContextInfo ctxInfo = new ContextInfo();
        ctxInfo.ctxPath = request.getContextPath();
        ctxInfo.method = request.getMethod();
        ctxInfo.requestURI = request.getRequestURI();
        ctxInfo.queryString = request.getQueryString();
        ctxInfo.queryStringPart = ctxInfo.queryString == null ? "" : "?" + ctxInfo.queryString;
        // 目标url组装
        String targetUri = ctxInfo.requestURI.substring(ctxInfo.requestURI.indexOf(ctxInfo.ctxPath) + ctxInfo.ctxPath.length());
        if (!includePathPrefix) {
            targetUri = targetUri.substring(targetUri.indexOf(pathPrefix) + pathPrefix.length());
        }
        ctxInfo.targetUri = targetUri;
        ctxInfo.targetUrl = host + ":" + port + targetUri + ctxInfo.queryStringPart;
        return ctxInfo;
    }

    // 获得目标地址的连接对象
    private HttpURLConnection getConnection(ContextInfo ctxInfo) throws Exception {
        String targetUrl = ctxInfo.targetUrl;
        String method = ctxInfo.method;
        // 配置Connection
        HttpURLConnection connection = (HttpURLConnection) new URL(targetUrl).openConnection();
        connection.setUseCaches(false);
        connection.setConnectTimeout(10000);
        connection.setReadTimeout(10000);
        connection.setRequestMethod(method);
        connection.setDoInput(true);
        if (method.toUpperCase().equals("GET")) {
            connection.setDoOutput(false);
        } else {
            connection.setDoOutput(true);
        }
        return connection;
    }

    // 全代理核心逻辑实现
    private void doProxy(ContextInfo ctxInfo, HttpURLConnection connection, HttpServletRequest request, HttpServletResponse response) throws Exception {
        String targetUrl = ctxInfo.targetUrl;
        String method = ctxInfo.method;
        // 透传请求header
        try {
            Enumeration<String> headerNames = request.getHeaderNames();
            while (headerNames.hasMoreElements()) {
                String headerName = headerNames.nextElement();
                String headerValue = request.getHeader(headerName);
                connection.setRequestProperty(headerName, headerValue);
            }
        } catch (Exception e) {
            Log.error("透传目标地址" + targetUrl + "请求header失败:" + e.getMessage());
            safeCloseConnection(connection);
            response.setStatus(HttpStatus.BAD_GATEWAY.value());
            response.getOutputStream().flush();
            return;
        }
        // 请求头打印 调试用
        Map<String, List<String>> passHeaders = connection.getRequestProperties();
        StringBuilder headerStr = new StringBuilder();
        for (Map.Entry<String, List<String>> header : passHeaders.entrySet()) {
            List<String> headerValues = header.getValue();
            StringBuilder headerValStr = new StringBuilder();
            for (String value : headerValues) {
                headerValStr.append(value).append(";");
            }
            headerStr.append(header.getKey()).append(":").append(headerValStr.toString()).append("\n");
        }
        Log.info("透传的请求头为:\n" + headerStr.toString());

        try {
            // 透传请求body
            if (!method.toUpperCase().equals("GET")) {
                IOUtils.copy(request.getInputStream(), connection.getOutputStream());
            }
        } catch (Exception e) {
            Log.error("透传目标地址" + targetUrl + "请求体失败:" + e.getMessage());
            safeCloseConnection(connection);
            response.setStatus(HttpStatus.BAD_GATEWAY.value());
            response.getOutputStream().flush();
            return;
        }

        // 响应处理开始---------------
        // 透传响应行信息
        try {
            int respCode = connection.getResponseCode();
            String respMessage = connection.getResponseMessage();
            if (connection.getHeaderFieldKey(0) == null) {
                String statusLine = connection.getHeaderField(0);
                Log.info("响应行消息为:" + statusLine);
            } else {
                Log.info("响应行消息为:" + respCode + " " + respMessage);
            }
            response.setStatus(respCode);
        } catch (Exception e) {
            Log.error("获取目标地址" + targetUrl + "响应状态码失败:" + e.getMessage());
            safeCloseConnection(connection);
            response.setStatus(HttpStatus.BAD_GATEWAY.value());
            response.getOutputStream().flush();
            return;
        }

        // 透传响应header
        try {
            response.setContentType(connection.getHeaderField("Content-Type"));
            Map<String, List<String>> map = connection.getHeaderFields();
            StringBuilder respHeaderStr = new StringBuilder();
            for (Map.Entry<String, List<String>> entry : map.entrySet()) {
                StringBuilder headerValStr = new StringBuilder();
                for (String value : entry.getValue()) {
                    headerValStr.append(value).append(";");
                }
                String key = entry.getKey();
                if (key != null) {
                    respHeaderStr.append(key).append(":").append(headerValStr.toString()).append("\n");
                }
                for (String value : entry.getValue()) {
                    if (key != null) {
                        response.addHeader(key, value);
                    }
                }
            }
            Log.info("响应头信息为:\n" + respHeaderStr.toString());
        } catch (Exception e) {
            Log.error("透传目标地址" + targetUrl + "响应头失败:" + e.getMessage());
            safeCloseConnection(connection);
            response.setStatus(HttpStatus.BAD_GATEWAY.value());
            response.getOutputStream().flush();
            return;
        }

        try {
            // 透传响应体
            InputStream responseInputStream;
            if (connection.getResponseCode() < HttpsURLConnection.HTTP_BAD_REQUEST) {
                responseInputStream = connection.getInputStream();
            } else {
                responseInputStream = connection.getErrorStream();
            }
            IOUtils.copy(responseInputStream, response.getOutputStream());
        } catch (Exception e) {
            Log.error("透传目标地址" + targetUrl + "响应体失败:" + e.getMessage());
            safeCloseConnection(connection);
            response.setStatus(HttpStatus.BAD_GATEWAY.value());
            response.getOutputStream().flush();
            return;
        }
        response.getOutputStream().flush();
    }

    // 关闭网络连接
    private void safeCloseConnection(HttpURLConnection connection) {
        if (connection == null) {
            return;
        }
        try {
            connection.getInputStream().close();
        } catch (Exception e) {
        }
        try {
            connection.getOutputStream().close();
        } catch (Exception e) {
        }
        try {
            connection.disconnect();
        } catch (Exception e) {
        }
    }
}

 

相关标签: JAVA HTTP代理