Bắt đầu từ this post tôi cố gắng thực hiện một máy chủ proxy ít để xử lý GET
và POST
yêu cầu cũng như (chỉ cần thay thế các lớp Handler
bởi hình dưới đây):Java Proxy - không thể trao đổi dữ liệu từ yêu cầu HTTP GET/POST đúng
public static class Handler extends Thread {
public static final Pattern CONNECT_PATTERN
= Pattern.compile("CONNECT (.+):(.+) HTTP/(1\\.[01])", Pattern.CASE_INSENSITIVE);
public static final Pattern GET_POST_PATTERN
= Pattern.compile("(GET|POST) (?:http)://([^/:]*)(?::([^/]*))?(/.*) HTTP/(1\\.[01])", Pattern.CASE_INSENSITIVE);
private final Socket clientSocket;
private boolean previousWasR = false;
public Handler(Socket clientSocket) {
this.clientSocket = clientSocket;
}
@Override
public void run() {
try {
String request = readLine(clientSocket, Integer.MAX_VALUE);
Matcher connectMatcher = CONNECT_PATTERN.matcher(request);
Matcher getNpostMatcher = GET_POST_PATTERN.matcher(request);
System.out.println("Request: " +request);
if (connectMatcher.matches()) {
// ...
} else if (getNpostMatcher.matches()) {
String method = getNpostMatcher.group(1);
String hostString = getNpostMatcher.group(2);
String portString = getNpostMatcher.group(3);
String lengthString = null;
String line;
ArrayList<String> buffer = new ArrayList<String>();
Integer port = portString == null || "".equals(portString) ? 80 : Integer.parseInt(portString);
Integer length = null;
buffer.add(request);
while ((line = readLine(clientSocket, Integer.MAX_VALUE)) != null) {
buffer.add(line);
if ("".equals(line)) break;
if (lengthString == null && line.startsWith("Content-Length: ")) {
lengthString = line.substring(16);
length = Integer.parseInt(lengthString);
}
}
try {
final Socket forwardSocket;
try {
forwardSocket = new Socket(hostString, port);
System.out.println(" " + forwardSocket);
} catch (IOException | NumberFormatException e) {
OutputStreamWriter outputStreamWriter
= new OutputStreamWriter(clientSocket.getOutputStream(), "ISO-8859-1");
e.printStackTrace();
outputStreamWriter.write("HTTP/" + connectMatcher.group(3) + " 502 Bad Gateway\r\n");
outputStreamWriter.write("Proxy-agent: Simple/0.1\r\n");
outputStreamWriter.write("\r\n");
outputStreamWriter.flush();
return;
}
PrintWriter printWriter = new PrintWriter(forwardSocket.getOutputStream());
for (String bufferedLine : buffer) {
printWriter.println(bufferedLine);
}
printWriter.flush();
if ("POST".equals(method) && length > 0) {
System.out.println ("Posting data ...");
if (previousWasR) { // skip \n if existing
int read = clientSocket.getInputStream().read();
if (read != '\n') {
forwardSocket.getOutputStream().write(read);
}
forwardData(threadId, clientSocket, forwardSocket, length, true); // only forward "Content-length" bytes
} else {
forwardData(threadId, clientSocket, forwardSocket, length, true); // only forward "Content-length" bytes
}
}
System.out.println ("Forwarding response ...");
forwardData(threadId, forwardSocket, clientSocket, null, false);
if (forwardSocket != null) {
forwardSocket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static void forwardData(int threadId, Socket inputSocket, Socket outputSocket, Integer length, boolean isPost) {
try {
InputStream inputStream = inputSocket.getInputStream();
try {
OutputStream outputStream = outputSocket.getOutputStream();
try {
byte[] buffer = new byte[4096];
int read;
if (length == null || length > 0) {
do {
if ((read = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, read);
if (inputStream.available() < 1) {
outputStream.flush();
}
if (length != null) {
length = length - read;
}
}
} while (read >= 0 && (length == null || length > 0));
}
} finally {
if (!outputSocket.isOutputShutdown()) {
if (!isPost) {
outputSocket.shutdownOutput();
}
}
}
} finally {
if (!inputSocket.isInputShutdown()) {
inputSocket.shutdownInput();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private String readLine(Socket socket, Integer noOfBytes) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int next;
readerLoop:
while (noOfBytes-- > 0 && (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");
}
}
Sau khi có một số vấn đề với yêu cầu POST, tôi đã tìm ra rằng điều quan trọng là phải tắt luồng đúng cách. Vì vậy, cuối cùng mã ở trên hoạt động khá tốt khi sử dụng Internet Explorer. Tuy nhiên,
Sử dụng các trình duyệt khác, có vẻ như các luồng/ổ cắm không được đóng đúng cách, vì đôi khi các chỉ báo tải đang chạy khá lâu, mặc dù nội dung có vẻ đã được tải. Đôi khi, các trang web không được tải hoàn toàn và các chủ đề dường như bị treo tại ...
if ((read = inputStream.read(buffer)) > 0) {
in forwardData(...)
. Tôi không biết làm thế nào tôi có thể tìm hiểu, cho dù dòng có thể cung cấp một số dữ liệu hay không - hoặc làm thế nào để tránh cuộc gọi chặn này của read
ở tất cả.
Không ai biết tôi đang làm gì sai và cách tôi có thể chuyển tiếp dữ liệu đúng cách để tất cả trình duyệt tải nội dung chính xác mà không có sự chậm trễ không cần thiết?
Điều này đã cho tôi chắc chắn là một bước tiến lớn. Cảm ơn bạn đã giúp đỡ! – Trinimon