前言
为避免将外部系统的视频等资源文件的地址直接暴露给用户,有一种思路是再服务端使用http代理隐藏真实地址。
可以使用netty实现http代理服务器,但是可以使用更简单的一段代码进行接口级代理,代理接口收到的请求可以进行鉴权等操作,然后进行正常的协议报文交互。
实现要求是支持视频的下载,代理服务不对文件进行临时下载,直接把文件流交给浏览器。
暂时实现的只有get请求代理,其他类型请求自行修改吧。
实现思路
流程
将客户端请求的headers给RestTemplate示例的代理请求的headers
将地址提供给RestTemplate示例
返回的代理响应的headers也设置到客户端请求的response的headers
响应码也设置到客户端请求的response的headers,适应HTTP/206 “Partial Content等情况
技术
使用的重载restTemplate.execute,会对给定的URI模板执行HTTP方法,使用RequestCallback准备请求,并使用ResponseExtractor读取响应。
代码
punlich void htttGetProxy(HttpServletRequest clientRequest, HttpServletResponse clientResponse,
String url, int windowSize) {
try {
Enumeration<String> clientHeaderNames = clientRequest.getHeaderNames();
HttpHeaders proxyHeaders = new HttpHeaders();
while (clientHeaderNames.hasMoreElements()) {
String headerName = clientHeaderNames.nextElement();
String header = clientRequest.getHeader(headerName);
proxyHeaders.add(headerName, header);
}
RestTemplate restTemplate = new RestTemplate();
RequestCallback requestCallback = proxyRequest -> proxyRequest.getHeaders().addAll(proxyHeaders);
restTemplate.execute(url, HttpMethod.GET, requestCallback, proxyResponse -> {
proxyResponse.getHeaders().forEach((key, value) -> value.forEach(it -> clientResponse.setHeader(key, it)));
clientResponse.setStatus(proxyResponse.getStatusCode().value());
copy(proxyResponse.getBody(), clientResponse.getOutputStream(), windowSize);
return null;
});
} catch (Exception e) {
log.error("http proxy error", e);
clientResponse.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
}
}
public static int copy(InputStream in, OutputStream out, int windowSize) throws IOException {
org.springframework.util.Assert.notNull(in, "No InputStream specified");
org.springframework.util.Assert.notNull(out, "No OutputStream specified");
int byteCount = 0;
int bytesRead;
for (byte[] buffer = new byte[windowSize]; (bytesRead = in.read(buffer)) != -1; byteCount += bytesRead) {
out.write(buffer, 0, bytesRead);
}
out.flush();
return byteCount;
}