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) {
}
}
}
上一篇: tqdm介绍及常用方法