Andreas Korthaus: HTTP-Proxyserver programmieren

Beitrag lesen

Hallo!

Da ich mich mit Java und Kommunikation über HTTP beschäftigen muss, habe ich mir überlegt zu "Übungszwecken" einen transparenten HTTP-Proxy zu programmieren. Gut, ich habe mir das RFC 1919 angesehen, naja, wenn ich es richtig  machen will wird das wohl recht kompliziert, aber es soll ja nur eine Übung sein, daher will ich nur dass es grundlegend funktioniert, also dass ich den Proxy im Browser eintragen kann und mich normal im Internet bewegen kann.

Im Prinzip funktioniert das auch schon, aber da sind noch einige Probleme die ich irgendwie nicht gelöst bekomme. Vor allem habe ich noch Probleme mit dem Lesen und Schreiben der Streams bei Binärdaten (Bilder, gzip...), ein anderes Problem ist das Parsen der HTTP-Header, da diese ja verändert/ausgetauscht werden müssen. Zur Vereinfachung habe ich mir überlegt, dass ich zunächst keine persistenten Verbindungen implementieren werde.

Erstmal zum Parsing-Problem: Zum lesen verwende ich

BufferedReader in =     new BufferedReader(new InputStreamReader(sverb.getInputStream()));

Dann lese ich in einer Schleife mit

line = in.readLine(); (was vermutlich für die Probleme mit Binärdaten verantwortlich ist)

Die erste Zeile des HTTP-Requests vom Browser parse ich so

StringTokenizer tokenizer = new StringTokenizer( line );   methode = tokenizer.nextToken();   url = tokenizer.nextToken();   proto = tokenizer.nextToken();

URL u = new URL( url );   host = u.getHost();   path = u.getPath();   query = u.getQuery();

Die Frage ist jetzt, wie kann ich die restlichen Header-Zeilen parsen. Bis jetzt schicke ich die einfach unverändert weiter, aber z.B. "proxy-conection", muss ich rauslöschen und dafür "connection" einfügen. Ich würde aber auch gerne andere Header lesen, entfernen oder verändern können. Ich kann einfach nicht glauben dass das ganze nur so kompliziert geht wie ich es in der ersten Zeile des Requests gemacht habe, außerdem ist das fehleranfällig. Es gibt zwar die spezielle URL und URLConnection Klasse, aber die sind IMHO nicht flexibel genug.

Das Problem ist grundsätzlicher Art. Sollte ich den Request zeilenweise parsen, oder sollte ich das anders einlesen? Wie bekomme ich die einzelnen Header am einfachsten aus dem Request/Response?

Das 2. Problem besteht darin, dass binbäre Daten nicht korrekt übertragen werden. Das betrifft also nur den eingehenden Stream vom Webserver und den ausgehenden Stream an den Client. Bisher verwende ich sowohl readline() zum lesen und println() zum schreiben auf den Stream. Wobei, theoretisch müsste readline() binäre Daten ja in einer Zeile einlesen, und println() dann auch als eine Zeile ausgeben, Problem würe dann evtl. ein Zeilenumbruch zu viel am Ende. Wenn Zeichen innerhalb binärer Daten als Umbruch interpretiert würden, müsste das doch entsprechend bei println() genaus wiederhergestellt werden. Naja, aber man sollte es wohl lieber anders machen, kann man nicht den eingehenden Stream direkt zum Client weiterleiten?

Naja, die Konzepte von Streams und Tolkens sind noch recht neu für mich, ich bin mir sicher dass das sehr mächtig ist und sich das ganze erheblich schöner erledigen lässt, nur komme ich nicht so recht dahinter, wie ;-)

Java kommt hier zwar nicht ganz so oft vor, aber ich hoffe das es hier den ein oder anderne gibt der vielleicht ne kleinen Tipp auf Lager hat :)

Viele Grüße Andreas

PS: Bisher sieht der komplette Code wie folgt aus:

import java.io.; import java.net.; import java.util.*;

public class proxy {

public static void main(String args[]) {

int anz = 1;         try {             ServerSocket ss = new ServerSocket(7788);             while(true) {                 Socket sverb = ss.accept();                 System.out.println(" Verbindung " + anz);                 new ServerThread(sverb,anz).start();                 anz++;             }         }         catch (Exception e) {             System.out.println(e);         }     } }

class ServerThread extends Thread {     Socket sverb;     int    nverb;

ServerThread(Socket s , int n) {         this.sverb = s;         this.nverb = n;     }

public void run () {

try {

BufferedReader in =        new BufferedReader(new InputStreamReader(sverb.getInputStream()));

PrintStream out =                 new PrintStream(sverb.getOutputStream());

boolean weiter = true;             boolean firstline = true;             String retline = "";             String line = "";             String methode = "";             String url = "";             String host = "";             String path = "";             String query = "";             String proto = "";             String request = "";

while (weiter) {

line = in.readLine();                 if (firstline) {

StringTokenizer tokenizer = new StringTokenizer( line );                     methode = tokenizer.nextToken();                     url = tokenizer.nextToken();                     proto = tokenizer.nextToken();

URL u = new URL( url );                     host = u.getHost();                     path = u.getPath();                     query = u.getQuery();

firstline = false;                     continue;                 }                 else {                     if (line.trim().equals("")) {

try {

Socket t = new Socket( host, 80 );

BufferedReader from_serv = new BufferedReader(                                 new InputStreamReader( t.getInputStream()));

PrintStream to_serv = new PrintStream( t.getOutputStream() );                             to_serv.println( methode + " " + path + "?" + query + " " + proto + "\r\nhost: " + request );                             while (true) {                                 retline = from_serv.readLine();                                 if (retline == null) {                                     break;                                 }                                 out.println( retline );                             }                         }                         catch (IOException e) {                             System.out.println(e);                         }

break;                     }                     else {                         request += line + "\r\n";                         continue;                     }                 }             }             sverb.close();         }         catch (IOException e) {             System.out.println(e);         }     } }