305 lines
9.2 KiB
Java
305 lines
9.2 KiB
Java
// Netzprogrammierung
|
|
// Gruppe 01 (Übung 1)
|
|
// Aufgabe 1-4
|
|
|
|
/*
|
|
ProxyServer.java
|
|
|
|
Anmerkungen:
|
|
- Dem Proxyserver können auf der Kommandozeile optional Port und Debug Level als int-Werte
|
|
übergeben werden (Silent Mode, nur Header, etc.). Als default werden Port 10000 und Debug
|
|
Level 3 genutzt.
|
|
|
|
Konzepte:
|
|
- Die Eingabeströme von Client und v.a. Server werden über binäre Inputstreams abgewickelt,
|
|
um Bilder und andere Binärdaten zu erhalten
|
|
- Aus dem Client-Request werden Server und Port geparst, so kann bspw. auch http über Port
|
|
8080 abgefragt werden
|
|
- Der vom Client entgegengenommene Header wird teilweise bereinigt, da er sich an einen
|
|
Proxy richtet (Zeile 148 ff.)
|
|
- Die Antwort vom Server wird über die private Methode readLine() (Z. 242 ff.) abgewickelt, so
|
|
kann der InputStream komfortabel gehandhabt werden
|
|
- Die empfangenen Headerzeilen werden aufsummiert und durch die angeforderten Ressourcen
|
|
geteilt. Das Ergebnis wird über den Header an den Client gesendet (Z. 190 ff.)
|
|
- Die meisten Exceptions werden nur abgefangen, der Proxyserver läuft danach wieder an, da
|
|
die meisten Fehler durch Abbruch der Verbindung durch den Client u.ä. entstehen, bemerkt
|
|
man dies im Betrieb i.d.R. nicht
|
|
- Die einzige gesondert behandelte Exception ist die Connection Exception (Z. 214): Tritt sie
|
|
auf, wird HTTP-Fehlercode 500 an den Client übermittelt
|
|
*/
|
|
|
|
package uebung01.aufgabe04;
|
|
|
|
import java.io.BufferedInputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.PrintStream;
|
|
import java.net.ConnectException;
|
|
import java.net.ServerSocket;
|
|
import java.net.Socket;
|
|
import java.net.SocketException;
|
|
import java.net.SocketTimeoutException;
|
|
import java.util.Iterator;
|
|
import java.util.LinkedList;
|
|
|
|
|
|
/**
|
|
* Ein einfacher Proxy-Server, der die durchschnittliche Anzahl empfangener Headerzeilen ermittelt.
|
|
*
|
|
* @author Tilman Walther
|
|
*/
|
|
public class ProxyServer {
|
|
|
|
// da unser einfacher Proxy die content-length nicht beruecksichtigt (die
|
|
// auch nicht von jedem Server gesendet wird), muessen Verbindungen oft
|
|
// per Timeout beendet werden.
|
|
final int SERVER_TIMEOUT = 2500;
|
|
|
|
// da Java den Linefeed dem System anpasst, nutzen wir einen
|
|
// String, der sich nach dem HTTP-Protokoll richtet
|
|
final String LF = new String(""+((char) 13)+((char) 10));
|
|
|
|
long headerLineCount = 0;
|
|
long itemCount = 0;
|
|
|
|
StringBuffer lineBuffer = new StringBuffer(600);
|
|
|
|
public ProxyServer(int port, int message) throws IOException {
|
|
|
|
System.out.println("PROXY: Starting ProxyServer on port "+port);
|
|
|
|
ServerSocket proxySocket = new ServerSocket(port);
|
|
|
|
Socket client;
|
|
BufferedInputStream clientIn;
|
|
PrintStream clientOut;
|
|
|
|
Socket server;
|
|
BufferedInputStream serverIn;
|
|
PrintStream serverOut;
|
|
|
|
String request;
|
|
String domain;
|
|
int serverport;
|
|
LinkedList requestStrings = new LinkedList();
|
|
|
|
String s;
|
|
byte[] buffer = new byte[1024];
|
|
int in1;
|
|
long headerLineAverage;
|
|
|
|
while (true) {
|
|
|
|
if (message > 1) System.out.println("PROXY: Listening... (press Ctrl+C to end)");
|
|
client = proxySocket.accept();
|
|
|
|
clientIn = new BufferedInputStream(client.getInputStream());
|
|
clientOut = new PrintStream(client.getOutputStream(), true);
|
|
|
|
try {
|
|
|
|
// Anfrage vom Browser holen
|
|
s = readLine(clientIn);
|
|
|
|
// Ressource extrahieren
|
|
request = s.substring(0, s.indexOf(" ")+1) + s.substring(s.indexOf("/", s.indexOf("//")+2));
|
|
|
|
// Domain und Port extrahieren
|
|
domain = s.substring(s.indexOf("//")+2);
|
|
domain = domain.substring(0, domain.indexOf("/"));
|
|
|
|
if ((in1 = domain.indexOf((int) ':')) > -1 ) {
|
|
serverport = Integer.parseInt(domain.substring(in1+1));
|
|
domain = domain.substring(0, in1);
|
|
}
|
|
else {
|
|
serverport = 80;
|
|
}
|
|
|
|
if (message > 1) System.out.print("PROXY: Request from client: "+s);
|
|
|
|
// Restlichen Anfrage-Header holen
|
|
requestStrings.clear();
|
|
do {
|
|
s = readLine(clientIn);
|
|
requestStrings.add(s);
|
|
if (message > 4) System.out.print(" "+s);
|
|
} while (!s.equals(LF));
|
|
|
|
long time = System.currentTimeMillis();
|
|
|
|
try {
|
|
if (message > 1) System.out.println("PROXY: Connecting server "+domain+":"+serverport);
|
|
|
|
server = new Socket(domain, serverport);
|
|
server.setSoTimeout(SERVER_TIMEOUT);
|
|
serverIn = new BufferedInputStream(server.getInputStream());
|
|
serverOut = new PrintStream(server.getOutputStream(), true);
|
|
|
|
if (message > 1) System.out.print("PROXY: Sending request to server: "+request);
|
|
serverOut.print(request);
|
|
|
|
// Angepassten Header an Server schicken
|
|
Iterator iter = requestStrings.iterator();
|
|
while (iter.hasNext()) {
|
|
|
|
s = (String) iter.next();
|
|
|
|
if (s.startsWith("Proxy-Connection")) {
|
|
serverOut.print("Connection: close"+LF);
|
|
if (message > 2) System.out.print(" Connection: close"+LF);
|
|
}
|
|
else if (s.startsWith("Transfer-Encoding")) {
|
|
serverOut.print("Transfer-Encoding: identity"+LF);
|
|
if (message > 2) System.out.print(" Transfer-Encoding: identity"+LF);
|
|
}
|
|
else if (s.startsWith("Accept-Encoding")) {
|
|
|
|
s = s.substring(0,(s.length()-2)).concat(",identity"+LF);
|
|
//s = "Accept-Encoding: identity"+LF;
|
|
|
|
serverOut.print(s);
|
|
if (message > 2) System.out.print(" "+s);
|
|
}
|
|
else {
|
|
serverOut.print(s);
|
|
if (message > 2) System.out.print(" "+s);
|
|
}
|
|
}
|
|
|
|
// Antwort vom Server entgegennehmen
|
|
try {
|
|
|
|
// Header empfangen
|
|
s = readLine(serverIn);
|
|
|
|
// --> Hier koennen HTTP response codes abgefangen werden
|
|
if (message > 1) System.out.print("PROXY: Receiving data from server: "+s);
|
|
|
|
itemCount++;
|
|
|
|
while (!s.equals(LF)) {
|
|
|
|
headerLineCount++;
|
|
|
|
if (message > 2) System.out.print(" "+s);
|
|
clientOut.print(s);
|
|
s = readLine(serverIn);
|
|
}
|
|
|
|
// durchschnittliche Anzahl der Headerzeilen berechnen
|
|
headerLineAverage = headerLineCount / itemCount;
|
|
|
|
if (message > 2) System.out.print(" X-mean-headercount: "+ headerLineAverage + LF + LF);
|
|
clientOut.print("X-mean-headercount: "+ headerLineAverage + LF + LF);
|
|
|
|
// Daten empfangen
|
|
while ((in1 = serverIn.read(buffer)) > 0) {
|
|
if (message > 3) System.out.write(buffer, 0, in1);
|
|
clientOut.write(buffer, 0, in1);
|
|
}
|
|
}
|
|
catch (SocketTimeoutException ste) {
|
|
if (message > 1) System.err.println("PROXY: Connection timed out");
|
|
}
|
|
|
|
if (message > 1) System.out.println("PROXY: Time "+(System.currentTimeMillis()-time)+" ms");
|
|
if (message > 1) System.out.println("PROXY: Unconnect...\n");
|
|
|
|
serverIn.close();
|
|
serverOut.close();
|
|
server.close();
|
|
|
|
}
|
|
catch (ConnectException ce) {
|
|
|
|
if (message > 0) System.err.println("ConnectException, sending code 500 to client");
|
|
buffer = ("HTTP/1.0 500\nContent Type: text/plain\n\nError while connecting server: "+domain+"\n").getBytes();
|
|
clientOut.write(buffer, 0, ("HTTP/1.0 500\nContent Type: text/plain\n\nError while connecting server: "+domain+"\n").length());
|
|
|
|
}
|
|
|
|
clientIn.close();
|
|
clientOut.close();
|
|
client.close();
|
|
}
|
|
catch (SocketException se) {
|
|
if (message > 4) System.err.println("SocketException");
|
|
}
|
|
catch (Exception e) {
|
|
// nothing, just try again
|
|
if (message > 4) e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
//proxySocket.close();
|
|
}
|
|
|
|
|
|
/**
|
|
* Liest eine Zeile vom binären Eingabestrom.
|
|
* (Auf diese Weise bleiben binäre Daten korrekt erhalten.)
|
|
*/
|
|
private String readLine(InputStream in) throws IOException
|
|
{
|
|
int i;
|
|
lineBuffer.setLength(0);
|
|
|
|
while (((i = in.read()) > 0) && (i != 13)){
|
|
lineBuffer.append((char) i);
|
|
}
|
|
|
|
|
|
// nach HTTP 1.1 müssten die Zeilenenden mit #13#10 markiert sein
|
|
// (durch die Aufteilung schlucken wir auch Unix/Macintosh)
|
|
if (i == 13) {
|
|
|
|
lineBuffer.append((char) 13);
|
|
in.mark(1);
|
|
|
|
if (in.read() != 10)
|
|
in.reset();
|
|
else
|
|
lineBuffer.append((char) 10);
|
|
}
|
|
|
|
return lineBuffer.toString();
|
|
}
|
|
|
|
|
|
public static void main(String[] args) throws IOException {
|
|
|
|
int port = 10000; // default port
|
|
int message = 3; // default output level
|
|
|
|
if (args.length > 2) {
|
|
System.out.println("usage: MultServer [port number] [output level]");
|
|
}
|
|
else {
|
|
|
|
if (args.length > 0) {
|
|
try {
|
|
port = Integer.parseInt(args[0]);
|
|
}
|
|
catch (NumberFormatException nfe) {
|
|
System.out.println(args[0]+" is not a valid port number.\nusage: MultServer [port number] [output level]");
|
|
System.exit(1);
|
|
}
|
|
}
|
|
|
|
if (args.length > 1) {
|
|
try {
|
|
port = Integer.parseInt(args[1]);
|
|
}
|
|
catch (NumberFormatException nfe) {
|
|
System.out.println(args[0]+" is not a valid port number.\nusage: MultServer [port number] [output level]");
|
|
System.exit(1);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
new ProxyServer(port, message);
|
|
}
|
|
}
|