手写HTTP代理服务器,使用灵活


手写HTTP代理转发工具

  • 本文地址:
  • 手写通过系统设置的代理:

背景

代理工具实际上有很多,最常用的就是ngnix了,能代理非常多的场景,但是在生产开发验证的时候,遇到了一个问题,某些平台的回调地址必须使用域名才能访问,当回调地址使用公网IP又无权限设置域名解析的时候,只有使用ngnix做代理转发,在ngnix中设置proxy路径的代理,配置为:

location /proxy/ {
	proxy_redirect off;
	proxy_set_header Host $http_host;
	proxy_set_header X-Real-IP $remote_addr;
	proxy_set_header REMOTE-HOST $remote_addr;
	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	proxy_pass http://公网IP:8888/test/;
}

但是访问代理后的路径又出现一个了问题,访问地址出现如下图所示的限制:

于是自己手写代理绕过这个限制,

原理


如需要访问的Tomcat的路径为:http://公网IP:8888/test/,原始Get请求资源路径为:http://公网IP:8888/test/index.html?name=hello,其中公网IP可能是阿里云、腾讯云等,域名访问会被要求备案
1.设置代理
经过url编码后作为参数传递: GET /proxy/setProxy?url=http%3A%2F%2F公网IP%3A8888%2Ftest%2F&content=tomcat
请求回家tomcat的代理加入proxyContainer容器,可以设置多个哦
2.发起请求
请求当前路径地址:GET /proxy/tomcat/index.html?name=hello,就会后台自动请求http://公网IP:8888/test/index.html?name=hello,并将结果返回给客户端

源码实现

ProxyController.java

//package me.muphy;

import cn.hutool.http.HttpUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * 手写代理转发工具
 * 如Tomcat的路径为:http://127.0.0.1:8888/test/,Get请求资源路径为:http://127.0.0.1:8888/test/user/page?page=1&pageSize=10
 * 1.通过请求设置代理
 * 经过url编码后,调用: GET /proxy/setProxy?url=http%3A%2F%2F127.0.0.1%3A8888%2Ftest%2F&content=tomcat
 * 2.发起请求
 * 请求当前路径地址:GET /proxy/tomcat/user/page?page=1&pageSize=10,就会后台自动请求http://127.0.0.1:8888/test/user/page?page=1&pageSize=10,并将结果返回给客户端
 *
 * @author gzhx
 * @date 2022-02-14
 */
@RestController
@RequestMapping("/proxy")
public class ProxyController {

    /**
     * 代理容器
     */
    private static final Map proxyContainer = new HashMap<>();

    /**
     * 转发GET请求
     *
     * @author 子安
     * @date 2022-05-01
     */
    @GetMapping("/**")
    public Object proxyGet(HttpServletRequest request) {
        String url = getUrl(request);
        System.out.println("url:" + url);
        if (url == null) {
            return "404 " + request.getRequestURI();
        }
        String forObject = HttpUtil.get(url, 30000);
        System.out.println("result:" + forObject);
        return forObject;
    }

    /**
     * 转发POST代理
     * 如: http://localhost:8080/proxy/
     *
     * @author 子安
     * @date 2022-05-01
     */
    @PostMapping("/**")
    public Object proxyPost(HttpServletRequest request, String body) {
        String url = getUrl(request);
        System.out.println("url:" + url);
        if (url == null) {
            return "404 " + request.getRequestURI();
        }
        String forObject = HttpUtil.post(url, body, 30000);
        System.out.println("result:" + forObject);
        return forObject;
    }

    /**
     * 代理PUT代理
     *
     * @author 子安
     * @date 2022-05-01
     */
    // @PutMapping("/**")
    // public Object proxyPut(HttpServletRequest request, String body) {
    // 	if (StringUtils.isEmpty(proxy)) {
    // 		return "url为空!";
    // 	}
    // 	String requestURI = request.getRequestURI();
    // 	requestURI = requestURI.replaceAll("/?sdzfw/corpwx/proxy/*", "/");
    // 	//String url = (proxy + requestURI).replaceAll("/+", "/");
    // 	String url = proxy + requestURI;
    // 	String queryString = request.getQueryString();
    // 	if(StringUtils.isNotBlank(queryString)){
    // 		url +="?" + queryString;
    // 	}
    // 	System.out.println("url:" + url);
    // 	restTemplate.put(url, body, Map.class);
    // 	return "ok";
    // }

    /**
     * 设置代理
     *
     * @param url     代理的地址,如: http://127.0.0.1:8888/test/
     * @param content 代理的应用路径,如: tomcat
     * @author 子安
     * @date 2022-05-01
     */
    @GetMapping("/setProxy")
    public Object setProxy(HttpServletRequest request, String url, String content) {
        if (StringUtils.isEmpty(url)) {
            return "url不能为空!";
        }
        if (StringUtils.isEmpty(content)) {
            return "content不能为空!";
        }
        if (url.endsWith("/")) {
            url.replaceAll("/+$", "");
        }
        return proxyContainer.put(content, url);
    }

    /**
     * 查询代理
     *
     * @author 子安
     * @date 2022-05-01
     */
    @GetMapping("/getProxy")
    public Object getProxy(HttpServletRequest request, String content) {
        if (StringUtils.isEmpty(content)) {
            return proxyContainer;
        }
        if (proxyContainer.containsKey(content)) {
            return content + "代理不存在!";
        }
        return proxyContainer.get(content);
    }

    /**
     * 删除代理
     *
     * @author 子安
     * @date 2022-05-01
     */
    @GetMapping("/delProxy")
    public Object delProxy(HttpServletRequest request, String content) {
        if (StringUtils.isEmpty(content)) {
            return "content不能为空!";
        }
        if (proxyContainer.containsKey(content)) {
            return content + "代理不存在!";
        }
        String remove = proxyContainer.remove(content);
        return "删除成功,代理信息:" + content + "=" + remove;
    }

    private String getUrl(HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        for (Map.Entry entry : proxyContainer.entrySet()) {
            if ((requestURI + "/").startsWith("/proxy/" + entry.getKey() + "/")) {
                String url = entry.getValue() + requestURI.replaceAll("^/proxy/" + entry.getKey(), "/").replaceAll("/+", "/");
                String queryString = request.getQueryString();
                if (StringUtils.isNotBlank(queryString)) {
                    url += "?" + queryString;
                }
                return url;
            }
        }
        return null;
    }
}