A partire da this post ho cercato di implementare un piccolo server proxy che gestisce GET
e POST
richieste pure (basta sostituire la classe Handler
da quella qui sotto):Java Proxy - non può scambiare dati dalla richiesta HTTP GET/POST correttamente
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");
}
}
Dopo aver riscontrato alcuni problemi con le richieste POST, ho capito che è importante arrestare i flussi nel modo giusto. Quindi finalmente il codice sopra funziona abbastanza bene quando si usa Internet Explorer.
Utilizzando altri browser, tuttavia, sembra che gli stream/socket non siano chiusi correttamente, perché a volte gli indicatori di caricamento sono in esecuzione da un po 'di tempo, anche se il contenuto sembra già essere caricato. A volte, i siti non sono completamente caricati e le discussioni sembrano appendere al ...
if ((read = inputStream.read(buffer)) > 0) {
in forwardData(...)
. Non so come potrei scoprire, se lo stream potrebbe fornire alcuni dati o meno - o come evitare questa chiamata di blocco di read
.
Qualcuno sa cosa sto facendo male e come posso inoltrare correttamente i dati, in modo che tutti i browser caricino i contenuti correttamente senza inutili ritardi?
Questo mi ha portato decisamente un grande passo avanti. Grazie per l'aiuto! – Trinimon