java - Java http 代理服务器 - Http/1.1 400 错误请求
问题描述
我正在尝试运行一个基本的 java http 代理服务器(暂时不处理 https)
我已复制此答案中提供的 https 示例:https ://stackoverflow.com/a/41368670/6346653
并简单地删除检查 CONNECT 模式的部分并删除客户端套接字标头信息。剩下的应该解析初始请求行以获取主机名,然后转发初始请求数据而不进一步篡改(端口 80 目前硬编码用于远程套接字连接)。下面的代码:
package com.test.proxytest;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SampleProxyServer extends Thread {
public static void main(String[] args) {
(new SampleProxyServer()).run();
}
public SampleProxyServer() {
super("Server Thread");
}
@Override
public void run() {
try (ServerSocket serverSocket = new ServerSocket(8084)) {
Socket socket;
try {
while ((socket = serverSocket.accept()) != null) {
(new Handler(socket)).start();
}
} catch (IOException e) {
e.printStackTrace(); // TODO: implement catch
}
} catch (IOException e) {
e.printStackTrace(); // TODO: implement catch
return;
}
}
public static class Handler extends Thread {
private final Socket clientSocket;
private boolean previousWasR = false;
public Handler(Socket clientSocket) {
this.clientSocket = clientSocket;
}
@Override
public void run() {
try {
String request = readLine(clientSocket);
System.out.println(request);
final Pattern HTTP_PATTERN = Pattern.compile("(HEAD|GET|POST|PUT) http:\\/\\/([^\\/]+).* HTTP\\/(1\\.[01])",
Pattern.CASE_INSENSITIVE);
Matcher matcher = HTTP_PATTERN.matcher(request);
if (matcher.matches()) {
final Socket forwardSocket;
try {
forwardSocket = new Socket(matcher.group(2), 80);
System.out.println(forwardSocket);
} catch (IOException e) {
e.printStackTrace(); // TODO: implement catch
return;
}
try {
Thread remoteToClient = new Thread() {
@Override
public void run() {
forwardData(forwardSocket, clientSocket);
}
};
remoteToClient.start();
try {
if (previousWasR) {
int read = clientSocket.getInputStream().read();
if (read != -1) {
if (read != '\n') {
forwardSocket.getOutputStream().write(read);
}
forwardData(clientSocket, forwardSocket);
} else {
if (!forwardSocket.isOutputShutdown()) {
forwardSocket.shutdownOutput();
}
if (!clientSocket.isInputShutdown()) {
clientSocket.shutdownInput();
}
}
} else {
forwardData(clientSocket, forwardSocket);
}
} finally {
try {
remoteToClient.join();
} catch (InterruptedException e) {
e.printStackTrace(); // TODO: implement catch
}
}
} finally {
forwardSocket.close();
}
}
} catch (IOException e) {
e.printStackTrace(); // TODO: implement catch
} finally {
try {
clientSocket.close();
} catch (IOException e) {
e.printStackTrace(); // TODO: implement catch
}
}
}
private static void forwardData(Socket inputSocket, Socket outputSocket) {
try {
InputStream inputStream = inputSocket.getInputStream();
try {
OutputStream outputStream = outputSocket.getOutputStream();
try {
byte[] buffer = new byte[4096];
int read;
do {
read = inputStream.read(buffer);
if (read > 0) {
outputStream.write(buffer, 0, read);
if (inputStream.available() < 1) {
outputStream.flush();
}
}
} while (read >= 0);
} finally {
if (!outputSocket.isOutputShutdown()) {
outputSocket.shutdownOutput();
}
}
} finally {
if (!inputSocket.isInputShutdown()) {
inputSocket.shutdownInput();
}
}
} catch (IOException e) {
e.printStackTrace(); // TODO: implement catch
}
}
private String readLine(Socket socket) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int next;
readerLoop:
while ((next = socket.getInputStream().read()) != -1) {
if (previousWasR && next == '\n') {
previousWasR = false;
continue;
}
previousWasR = false;
switch (next) {
case '\r':
previousWasR = true;
break readerLoop;
case '\n':
break readerLoop;
default:
byteArrayOutputStream.write(next);
break;
}
}
return byteArrayOutputStream.toString("ISO-8859-1");
}
}
}
但是,所有请求当前都得到 HTTP 400 响应。
解决方案
推荐阅读
- python - 使用漂亮的汤进行网络抓取会给出不准确的结果
- entity-framework - 实体框架 6:“清除”旧迁移的资源以减小程序集大小是否安全?
- git - 是否有一个 gitkraken 功能可以显示背后有多少提交?前面一个分支与另一个分支进行比较?
- laravel - “错误”:“invalid_grant” Laravel 7 护照
- flutter - 模型类中的属性更改后更新 UI
- amazon-web-services - 数据管道(DynamoDB 到 S3) - 如何格式化 S3 文件?
- c# - 使用嵌套对象键生成 JSON
- excel - 在 Excel 中的 2 个团队之间平均分配球员
- azure - 无法使用“客户”托管加密密钥选项在 Azure 认知搜索中创建索引/同义词映射
- xml - 使用 xslt1.0 基于输入 xml 动态构建节点集