Upload
others
View
1
Download
0
Embed Size (px)
Citation preview
5 Implementierung eines HTTP-Servers
Ziel dieses Kapitels ist es, eine Einfü hrung in das HypertextTransfer Protocol (HTIP) zu geben und einen einfachen Webserver zu entwickeln.
5.1 Das Protokoll HTTPHTTP ist ein Protokoll der Anwendungsschicht im TCP/IPSchichtenmodell und regelt insbesondere, wie ein Webbrowsermit einem Webserver im World Wide Web (WWW) kommuniziert HTIP verwendet auf der Transportschicht TCP,
Damit ein Webbrowser eine Webseite im \XT\Xf\Xl abrufen kann,muss er sie zunächst adressieren.
Ein Uniform Resource Locator (URL) ist eine standardisierte URLAdresse, mit der eine beliebige Ressource (z. B. eine HTML-Seite,ein GIF-Bild, eine PDF-Datei, ein Programm) lokalisiert werdenkann.
So lokalisiert z: B,
ht tp://www hs nied errhein.de/index. html
die Website der Hochschule Niederrhein.
Der URL hat im Allgemeinen den folgenden Aufbau:
protoco1 :I /hos t[ :port] [! path] [/ f i 1e] [#sect i on]
Hierbei sind die eingeklammerten Teile optional.
Die Angaben bedeuten:
prot oco1 Protokollname, hier: http
hast Domain-Name oder IP-Adresse des Rechners
port Portnummer, unter der der Server läuft; die standardmäßige Portnummer für einen HTTP-Server ist 80 undmuss nicht angegeben werden
path Name eines Verzeichnisses auf dem Server; die Angabe ist relativ zur Wurzel des Webverzeichnisses
fi l e Name einer Datei in dem spezifiziertenVerzeichnis
sectlon verweist auf eine bestimmte Stelle innerhalbeiner HTML-Seite
path und fl le müssen keine reale Datei bezeichnen. Sie könnenauf der Serverseite als logischer Name zur eindeutigen Bezeichnung einer Ressource interpretiert werden.
166
AblaufeinerllTJP-Transaktion
HTIP istzustandslos
5 Implementierung eines HTTP-Servers
Die Interaktion zwischen HTIP-Client und HTIP-Server für eineAnfrage umfasst die folgenden Schritte,
1. Der Server wartet auf eine eingehende HTIP-Anfrage.
2. Der eIient erzeugt einen URL http:/ t .
3. Der eIient versucht. eine TCP-Verbindung zum Server aufzubauen.
4. Der Server akzeptiert den Verbindungswunsch.
5. Der eIient sendet eine Nachricht (HTIP-Anfrage) an denServer und fordert die Ressource mit dem spezifizierten URLan.
6. Der Server verarbeitet die Anfrage (z. B. Ausführung einerDatenbankabfrage und Generierung einer HTML-Seite mitdem Abfrageergebnis).
7. Der Server sendet eine Rückantwort (HTTP-Antwort) an denClient, die die angeforderte Ressource oder eine Fehlermeldung enthält.
8. Der Client verarbeitet die Antwort.
9. Der eIient und/oder der Server schließen die TCP-Verbindung.
Der Server hat keine Kenntnis über vorangegangene Anfragendesselben eIient. Jede Anfrage wird unabhängig von vorhergehenden Anfragen bearbeitet. HTTP ist also ein zustandslosesProtokoll.
Für jede HTIP-Anfrage wird bei HTIP 1.0 in der Regel eineneue TCP-Verbindung aufgebaut. Enthält z. B. eine angeforderteHTML-Seite mehrere Grafiken. so muss jede dieser Grafikenseparat angefordert werden (jeweils mit Verbindungsaufbau und-abbau).
eaa s«,Eine HTTP
Transaktion
Webbrowser
GET Iprodukte/index.html HTTP/1.1
HTTP
HTIP/1 .1 200 OKConte nt-Type: text/htm I
<htm I> ... </htm I>
.00 •
Webserver
5.1 Das Protokoll HTIP
HITP 1.0 ist im RPC 1945 der IETP spezifiziert (siehe http: HTIP 1.0//www.i etf.org/rfc/rfcI945.txt).
Die Version HTTP 1.1 unterstützt so genannte persistente Verbin - HTIP 1.1dungen. Während die TCP-Verbindung steht. können mehrereHTTP-Anfragen über diese Verbindung durchgeführt werden.Die Verbindung kann vom Client oder Server abgebaut werden.
HTTP 1.1 ist im RFC 2616 der IETF spezifiziert (siehe http://www.ietf.org/rfc/rfc2616.txt).
Das folgende Programm zeigt den Inhalt der Nachricht (HTIPAnfrage). die ein Webbrowser an den Webserver schickt.
167
lmport java.lo FlleNotFoundExceptlon;lmport java.lo FlleOutputStream;lmport java.lo. IOExceptlon;lmport java.lo. InputStream;lmport java.lo.OutputStream;lmport java net.ServerSocket;lmport java net.Socket;lmport java net. SocketTl meoutExceptlon;
public class Reporter (prl vate lnt port;prl vate OutputStream out;prl vate ServerSocket server;
public Repor ter(int port . String file)throws Fi l eNotFoundExcept i on (
this.port ~ port:out ~ new Fi l eOut put St ream(fi le ) :
Runtime.getRuntime().addShutdown Hook(new Thread()public void run() {
try {if (out !~ null )
out. fl usht ):out.close():
}i f (server !~ null)
server. cl ase () ;catch (IOException e )
}}) :
Programm 5.1
168 5 Implementierung eines HTTP-Servers
public void doWork ()try (
server = new ServerSocket(port);whi le (true) (
Socket cllent = server.accept();client.setSoTimeout(3000);Input St ream in ~ client.get lnputStream();
try {l nt c :while ((c ~ in.read()) !~ -1) (
out.write(c);}catch (SocketTimeoutException e)finally {try {
if (in !~ null)in.close();
if (client !~ null )cl i ent. cl ose();
out.write ('lr');out.write(' In');out. fl ush() ;catch (IOException e)
}catch (IOException e) (System.err.println(e.getMessage());
publ i c static void ma in(String[J args) {if (args.length !~ 2) (
System.err.prlntln("java Reporter <port> <flle>");System.exit(l);
int port ~ Int eger .parsel nt (args[OJ ) ;String f i l e ~ args[ lJ ;try (
new Reporter(port, file).doWork();catch (FileNotFound Exception e)System. err.println(e);
5.1 Das Protokoll HTIP
Das Programm protokolliert die empfangenen Daten in einerDatei, deren Name beim Aufruf als Parameter mitgegeben wird.Da das Programm keine HTIP-Antwort schickt. wird die Timeout-Steuerung verwendet, um die Transaktion zu beenden.
Aufruf des Programms, Test
java -cp bin Reporter 50000 log.txt
Anforderung einer fiktiven HTML-Seite im Webbrowser (hier,Firefox 3.5}
http://localhos t:50000/abc/xyz.html
Inhalt der Datei log.txt.
GET labc/xyz.html HTTP/1 .1Host : loca lhost:50000User-Agent: Mo zi l l a/ 5. 0 (Windows: U: Wi ndows NT 6.0: de:Accept: textihtml.application/xhtml+xml.application/xml:Accept-Language: de-de.de:q~0.8.en-us:q~0.5.en:q~0.3
Accept- Encoding: gzip.deflateAccept-Charset: I S O - 88 59 -1 . ut f - 8 : q ~0 . 7 . * : q ~0 . 7
Keep-Ali ve: 300Connectl on: keep-all ve
169
Eine HTTP-Anfrage hat den folgenden Aufbau,
• Kap/zeileSie enthält die HTTP-Methode (im Beispiel GET), den Namender angeforderten Ressource ohne Protokoll und DomainNamen (im Beispiel Iabcl xyz. htm1) und die verwendete Protokollversion (im Beispiel HTIP/1.l).
• Anfragepararneter (optional)Anfrageparameter liefern dem Server zusätzliche Informationen über den Client und seine Anfrage. Jeder Parameterbenutzt eine eigene Zeile und besteht aus dem Namen, einemDoppelpunkt und dem Wert.
• eine Leerzeile
• Nutzdatenteil Coptional)Hier stehen z. B. die in einem Formular eingetragenen Datenbei Anwendung der HTIP-Methode POST.
Kopfzeile, Anfrageparameter und Leerzeile enden jeweils mitCarriage Return und Linefeed. IrIn.
HTIP-Anfrage
170 5 Implementierung eines HTTP-Servers
sua s.».Struktur einerHTIP-Anfrage
Me thod e Re s s ourc e Ve rs ion\ r\ n
Name : Wer t \r \n
...\ r\ n
xxxxxxxxxxxxxxxxxxxxxxx
Kopfzeile
} Anfrageparameter
Leerzeile
Nutzdaten
HTIP-Methoden
Im einfachsten Fall reicht eine Anfrage der Form
GET lindex.html HTTP/1 . 0
aus, um bereits von einem Webserver verstanden zu werden.
Die HTTP-Methode spezifiziert die vom Server durchzuführendeAktion.
Wichtige HTIP-Methoden sind,
• GETDiese Methode fordert eine Ressource an.
• POSTDiese Methode überträgt Benutzerdaten an den Server.
• HEAODiese Methode fordert Informationen über die Ressource an;die Ressource selbst wird nicht benötigt.
• PUTDiese Methode wird verwendet, um eine Ressource auf demServer zu erstellen bzw. zu ändern.
• OELETEDiese Methode wird verwendet, um eine Ressource auf demServer zu löschen.
PUT und OELETE werden aus Sicherheitsgründen von den meistenWebservern ignoriert.
Anfrageparameter Anfragepararneter können in beliebiger Reihenfolge angegebenwerden. Groß- und Kleinschreibung wird ignoriert.
Die gebräuchlichsten Anfrageparameter sind (siehe obiges Testbeispiel):
• HastRechnemame und optionale Portnummer des Servers
• Us er-AgentKenndaten über den HTIP-Client
5.1 Das Protokoll HTIP
• ConnectlonDieser Parameter wird benutzt, um eine persistente TCPVerbindung anzufordem bzw. zu schließen.
• Content-LengthLänge der Daten (in Byte) im Nutzdatenteil
• Accept-LanguageDieser Parameter gibt die vom Client bevorzugte Sprache an.Der Server kann dann z. B. eine HTML-Seite, die in mehrerenSprachvarianten vorliegt, in der vom Client gewünschtenSprache senden.
• Accept- EncodlngMit diesem Parameter gibt der Client an, welche Komprimierungsalgorithmen er versteht. Der Server kann z. B. große Dateien komprimieren, um die Übertragungszeit zu minimieren.
• Accept-CharsetDieser Parameter gibt die vom Client bevorzugten Zeichensätze an.
• AcceptDieser Parameter gibt die vom Client akzeptierten Medientypen an. Die gültigen Parameterwerte sind durch den MIMEStandard definiert.
MIME steht für den Standard Multipurpose Internet Mail MIMEExtension. der ursprünglich für E-Mails entworfen wurde.
MIME-Formatangaben werden von HTIP-Clients und HTIPServern benutzt. Clients nutzen sie, um dem Server mitzuteilen,welche Medientypen sie handhaben können. Server nutzen sie,um den Client über den Inhaltstyp der gesendeten Ressource zuinformieren.
Die MIME-Formatangabe besteht aus einer Typ- und einer Subtypangabe,
typ/subtyp
Der MIME-Standard ist im RFC 1521 der IETF spezifiziert (siehehttp://www .i etf.org/rfc/rfcI521.txt).
171
Typ/Subtyp
text/htrn1
text/piain
t ext/xm1
image/ gi f
image/ jpeg
Beschreibung und üblicheErweiteru ng
HTML-Datei ('.hIm, '.html)
ASCII-Text ('.txt)
XML-Datei ('.xml, '.dtd)
GIF-Bild ('.gil)
JPEG-Bild ('.jpeg, '.jpg)
Beispiele vonMedientypen
172 5 Implementie rung eines HTTP-Servers
i mage / png
ap>1i cat i 00/ pdf
app1i cati 00/ octetstream
PNG-Bild ('png)
PDF-Dalei [t.pdf]
Binardaten [t.bin. 'exe)
epojtce t t co/ zt p ZIP-Datei Czip)
Für nicht standardisierte subtypen wird das Präfix x- benutzt, z.B. b ezeichn et auct io/x-wa v Audio-Dateien * .wav.
Daten zum Serversenden
Mit der H1TP -Methcde PO ST können Benutzerdaten zum Servergeschickt werden.
Das folgende Beispiel zeigt ein HTML-Formular, dessen Datendurc h Betätigen des Buttons "Senden" zum Server (hier Programm 5.1) gesendet werden.
name= "nr " size="5"/>name= "bez" s i ze="3Q "/ >name= "preis " s ize= "l0 "/>
t ype="text "typ e="t ext "t ype="text "
<input<input<input
<htnl >
<heed'< t : t l e>POST</ ti t l e></heact><body><form act i on= "http :// local host: 50000/xxx " meth od= "POST"><pre>Arti kel nummer:Bezei chnung:Prei s:-vpre>Beschrei bung:<brv><t exteree name= "b2schr " co1s="6O " rClills= "5"></textarea ><p/><in put type="submi t " va1 ue="Senden "/><i npu t type="reset " va1 ue="Z urückset zen"/ ></form></body>-vht ml>
H7ML-Code desForm ulars
Bild 5..3:
Ein FormularArtlll:~ l ",,,,,,,,~r : ~'c",,'-cccc~cccccccc---B~r~1Ch'lU"\I : Akku-Handslaubsauge r
15.99
[ Se nde n I[ Zurücksetze n I
Das Programm Reporterprotokollie rt:
5.1 Das Protokoll HTIP
POST lxxx HTTP/1.1Host: loca1host:50000
Content Type: appllcation/x-www forrn-urlencodedContent Length: 112
nr~4711&bez~Akku-Handstaubsauger&preis~15.gg&beschr~3+Ze11en.+
Kabe11os.+Wandha1terung.+Fugend%FCse+und+B%FCrste.
Die im Formular eingetragenen Daten werden im URL-codierten URL-codiertesFormat (appli cati on/ x-www-form-urlencoded) übertragen. Format
Die im Formular definierten Variablennamen (im Beispiel: nr,bez, preis, beschr) sind mit den vom Benutzer eingegebenenWerten verknüpft. Variable und Wert werden jeweils durch einGleichheitszeichen voneinander getrennt. Die einzelnen Variable/Wert-Paare sind durch das Zeichen & getrennt. Alle Zeichenaußer a-z, A-Z, 0-9, -, * werden zuerst in Bytes nach einemCodierungsschema (z. B. 150-8859-1 oder UTF-8) konvertiert.Jedes Byte wird dann durch ein Prozentzeichen. gefolgt vomHexadezimalwert des Bytes dargestellt. Leerzeichen werdendurch + ersetzt.
Formulare können auch die GET-Methode nutzen, um Daten zuübertragen. Die URL-codierten Daten werden nach einem Fragezeichen ? an den URL angehängt. 1m HTML-Code des obigenFormulars muss nur POST durch GET ersetzt werden. Die Kopfzeileder HTIP-Anfrage hat dann das folgende Aussehen,
GET Ixxx?nr~4711&bez~Akku-Handstaubsauger&preis~15.gg&beschr~
3+Ze11en.+Kabe11os.+Wandha1terung.+Fugend%FCse+und+B%FCrste.HTTPI1.1
Die so codierte Anfragezeichenkette nach dem Fragezeichen Query Stringwird auch als Query String bezeichnet.
Eine HTTP-Antworthat den folgenden Aufbau, HTIP-Antwort
• Kap/zeileSie besteht aus der Protokollversion, dem Status-Code und einer optionalen Status-Meldung.
• Antwortparameter (optional)Antwortparameter liefern dem Client zusätzliche Informationen über den Server und die Antwort.
• eine Leerzeile
• Nutzdatenteil (optional)Dieser enthält die angeforderte Ressource.
173
174 5 Implementierung eines HTTP-Servers
sua s.«.Struktur einerHTIP-Antwort
Ve rs ion Code Meldung\ r\ n
Name : Wert \r \n
.. .
\ r\ n
xxxxxxxxxxxxxxxxxxxxxxx
Kopfzeile
} Antwortparameter
Leerzeile
Nutzdaten
100 199
200 299
300 399
400 499
500 599
Beispiele:
Status-Code
Beispiel,
HTTP/l. 1 200 OKDate: Fr i. 19 Feb 2010 11:51:42 GHTServer: Apache/1.3.41 (Oarwin) PH P/ 4.4.9
Content Length: 3205Content Type : text /htrnl
<htrnl> ... </html >
Der Status-Code teilt dem Client mit, wie die gewünschte Aktionvom Server ausgeführt "WUrde.
Die Status-Codes sind wie folgt gruppiert
Informative Meldungen
Die Anfrage war erfolgreich
Die Anfrage "WUrde weitergeleitet
Die Anfrage war fehlerhaft
Server-Fehler
200 OK
400 Bad Request
404 Not Found
500 Internal Server Error
501 Not Irnplernented
Siehe auch:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.htrnl
5.2 Ein einfacher File-Server
Gebräuchliche Antwortparameter sind:
• DateAktuelles Datum des Servers zum Zeitpunkt der Beantwortung der Anfrage
• ServerKenndaten über den HTIP-SelVer
• Content-TypeMIME-Format der Nutzdaten
• Content-LengthLänge der Daten (in Byte) im Nutzdatenteil. Werden Webseiten dynamisch erzeugt, ist die Länge oft nicht bekannt, weshalb dieser Parameter dann weggelassen wird.
5.2 Ein einfacher File-Server
Einige Antwortparameter
175
Das folgende Programm ist ein HTTP-Server, der als einzigen Programm 5.2Dienst nach Verbindungsaufnahme mit einem Client immer die-selbe Datei sendet. Zu diesem Zweck muss er also die HTIP-Anfrage des Client gar nicht auswerten.
Der Server wird mit dem Dateinamen als Parameter aufgerufen.Anhand der Dateiendung wird der passende MIME-Typ festgelegt. Die HTIP-Antwort besteht aus der Kopfzeile mit StatusCode 200. den beiden Antwortparametem Content- Type und Content-Lenqt h, einer Leerzeile und dem Inhalt der Datei.
lmport java.ia BufferedOutputStream;import java.ia BufferedReader;import java.ia File;import java.la Flle InputStream;lmport java.lo.IOExceptlon;lmport j ava. jo . InputSt reamReader ;lmport java.lo.OutputStream;lmport java net.InetAddress;lmport java net.ServerSocket;lmport java net.Socket;
public class SingleFileSer verprlvate lnt port;pri vate File file;pri vate String type;
public SingleFileServer(int port. File file) (this.port ~ port;this.file ~ file;type ~ getMi meType(file);
176 5 Implementierung eines HTTP-Servers
public void startServer() {try (
ServerSocket server = new ServerSocket(port);
InetAddress addr ~ InetAddress.getLocalHost();System.out.println("SingleFileServer auf"
+ addr. getHostName() + "/" + addr. getHostAddress()+ '"." + port + " gestartet ... ");
whi le (true) (Socket cllent = server.accept();new SingleFileThread(cli ent).start();
}catch (IOException e)System.err.println(e);
private class SingleFileThread extends Thread (prl vate Socket ellent;
public SingleFileThread(Socket client)thls.client = client;
public void run() {try (
BufferedReader in ~ new BufferedReader(new Input St reamReader (cl i ent .get l nput St ream() )) ;
String line;whi 1e ((l i ne ~ in. readLi ne()) !~ null
&& !line.equals("")) (
BufferedOutputStream out ~ new BufferedOutputStream(client.getOutputStream());
String header ~ "HTTP/LO 200 OKlrln"+ "Content-Type; " + type + "IrIn"+ "Content-Length; " + file.length() + "IrInIrIn";
out.write(header.getBytes());writeFile(file. out);
out. fl ush() ;out.close() ;in.close();catch (IOException e)System.err.println(e);
5.2 Ein einfacher File-Server
finally {try {
if (el ient !~ null)el ient.elose();
}eateh (IOExeeption e)
pri vate String getMimeType(File file)String filename ~ file.getName();if (filename.endsWith(".html")
11 filename.endsWith(" .htm"))return "textihtml";
else if (filename.endsWith(" .txt")11 filename.endsWith(" .java"))
re turn "textlplaln";else if (filename.endsWith(".gif"))
return "lmage/gl f":else if (filename.endsWith(".jpg"))
return "image/ jpeg";else if (filename.endsWith(" .png"))
return "image/png";else if (filename.endsWith(".pdf"))
return "applieation/pdf";else
return "appl i cat i on/ octet- st ream";
pri vate void writeFile(File file. OutputStream out)throws IOExeeption (
FilelnputStream is ~ new FilelnputStream(file);int c;while ((e ~ iS.read()) !~ -1) (
out. write(e);}is .elose();
publie statie void main(String[] args) {if (args.length !~ 2) (
System.err.println("java SingleFlleServer <port> <file>");
System.exi t (1) ;
177
178
TCP-MonilOr
Bild 505 ,
Konfiguration
5 Implementierung eines HTIP-Servers
i nt port = Integer .parse l nt Cargs[ O] );File fil e = nee Fi le(a rgs [l] );t f Ufil e.i sFil e()) {
Sys tem.er r . pr i nt l nC "Datei ... + args [l]+ .. . i st ni cht vor handen") :
Sys tem.exl t r1) ;
new Singl eFil eServer( port . f i le ) . star tServer( ) :
Au fruf des Servers:
j ava -ce bin St m lel t leier ve- 50000 s rc /St rcler tl ese-v e-vj eva
Eingabe im Webbrowser:
http : / /localhost :50000/
Im Internet s ind Tools verfügbar, mit denen die Deren. die übere ine TCP-Verbindung gesendet werden, angezeigt werden kön nen (sieh e Q uellenangabe am Ende des Buches ). Der TCPMonitor wird zwischen Clien r und Server platziert . Der Gientverbindet sich mit dem TCP-MonilOf. Dieser le itet die Datenweiter an den Server und protoko lliert di e Ein- un d Ausga be.
http: / /loca lhos t : 80 80/
Browser TCP-Monitor
8080
Creilte a New TCP Monil '" ConneCll on:
l ocal Port: ~IS~"~O~~~~Server Name: 1127.0.0.\
Server Port: 150000
1- - ",
Server
50000
5.3 Ein HTIP-Server für SQL-Abfragen
5.3 Ein HTTP-Server für SOL-AbfragenWir stellen zunächst Methoden vor, die eine Zeichenkette in einURL-codiertes Format transformieren bzw. eine so codierte Zeichenkette wieder decodieren können.
Die Klasse java. net. URL Encoder enthält eine Klassenmethode zur URLEncoder
URL-Codierung einer Zeichenkette.
statlc Strlng encode(Strlng s, String enc)throws Unsupported EncodingException
wandelt Zeichen aus 5 in das URL-codierte Format x-WW'w-form
ur l encoded um, wobei für die Zeichenkodierung das Codierungsschema enc zugrunde gelegt wird (z. B. lSO-8859-1 oder UTF-8).
Wird das Codierungsschema enc nicht unterstützt, so löst dieseMethode eine Ausnahme vom Typ
java.io.UnsupportedEncod ingException
(Subklasse von IO Exception) aus.
Die Klasse java. net. URLDecoder dient der Decodierung einer URLDecoder
URL-codierten Zeichenkette.
static String decode(String s. String enc)throws Unsupported EncodlngExceptlon
decodiert eine URL-codierte Zeichenkette, wobei für die Zeichenkodierung das Codierungsschema enc zugrunde gelegt wird.
Das zu entwickelnde Programm Sql Server ermöglicht es, beliebi- Programm 53ge SQL-Anfragen (Abfragen und Änderungen) an eine Daten-bank über den Webbrowser zu schicken.
Dieser spezielle HTIP-Server
• sendet ein Eingabeformular zur Erfassung des SQL-Befehls anden Webbrowser.
• analysiert den SQL-Befehl und führt ihn über ]DBC aus.
• sendet evtl. SQL-Fehlermeldungen und
• sendet das Abfrageergebnis bzw. Informationen über Datenbankänderungen an den Webbrowser.
179
180
SqlSeroer
5 Implementierung eines HTTP-Servers
lmport ja va lo.BufferedReader;lmport ja va lo. Flle InputStream;lmport ja va lo. IO Exceptlon;lmport ja va lo. InputStreamReader;lmport ja va lo.PrlntWrlter;lmport ja va net. InetAddress;lmport ja va net.ServerSocket;lmport ja va net.Socket;lmport ja va net.URL Decoder;lmport java sql .Connectlon;l mport java sql .DrlverManager ;i mpor t java sql.ResultSet ;i mpor t java sql.ResultSetl'etaData;i mpor t java sql .SQL Exception;import java sql.Staterrent;import java sql.Types;lmpor t java utll . Propertles;
public class SqlServer (pri vate static final int WlX_RQ WS ~ 1000;pri vate static final int WlX_L ENGTH ~ 1000;prlvate i nt port;private Properties prop;
public SqlServer(int port)this port ~ port;
public void startServer() {try (
// Properties einlesenprop ~ new Properties();Fi l el nput St re am in ~ new Fi l elnput St ream(
"dbconnect.properties");prop.load(in) ;in.close();
ServerSocket server = new ServerSocket(port);Inet Address addr ~ Inet Addr es s . getLocal Host ( ) ;System. out. pri nt1n( "Sq1Server auf " + addr. getHostName()
+ "/" + addr. getHostAddress () + ":" + port+ " gestartet ... ");
whi le (true) (Socket client = server.accept();new Sql Thread(client. prop).start();
}catch (Exception e) (System.err.println(e);System.exit(l);
5.3 Ein HTIP-Server für SQL-Abfragen
pri vate class SqlThread extends Threadprivate Socket client;private Properties prop;private Connection con;private BufferedReader in;private PrintWriter out;
public SqlThread(Socket client, Properties prop) (this.client = client;this,prop ~ prop;
publ ic void run() {try (
// Verbindung zur Datenbank herstellencon = Dri verManager.getConnection(prop
,getProper ty(" ur1"), prcp. get Property( "user". ""),prop,getProperty ("password", ""));
in = new BufferedReader(new Input St reamReader (cl i ent,get lnputStream()));
out ~ new PrintWriter(client,getOutputStream(), true);
// HTTP-Request analysierenString sql ~ readRequest();
// HTML-Formul ar sendensendForrn(sq1);
// SOL-Befehl ausführenif(sql !~null)
execute(sql) ;
in.close t ) ;out,close();catch (Exception e) (System,err,println(e);finally {try {
if (con !~ null)con.cl ose C):
if (cli ent !~ null)cl ient.close();
}catch (Exception e)
181
182
Die MethodereadRequest
5 Implementierung eines HTTP-Servers
// De r Ouellcode der folgenden Me t hoden ist weiter unten/ / abgedruckt:pri vate String readRequest() throws IOExcept i on ( )pri vate void sendForm (String sql) throws IOExcept i on (pri vate void execute(String sql) throws SOL Exception (
public static voi d main(String [] args)if (args.length !- I ) (
System.err.pr l ntln("java SqlServer <por t>");System.exit (l):
int port - Int eger .parseI nt (args [ O] ) :new SqlServ er(port).startServer():
Datenbankspezifische Angaben (URL der Datenbank, User undPasswort) sind in der Datei dbconnect.properties zusammengefasst und werden zu Beginn als Properties eingelesen.
Innerhalb der run-Methode der inneren Klasse SqlThread erfolgtdie Interaktion mit dem Client.
Zunächst wird die Verbindung zur Datenbank hergestellt. DieMethode readRequest analysiert dann die HTIP-Anfrage undliefert den vom Benutzer eingegebenen SQL-Befehl.
Die Rückantwort des Servers enthält eine HTML-Seite, die imoberen Teil ein Formular zur Eingabe eines SQL-Befehls und imunteren Teil das Ergebnis der letzten Anfrage enthält.
Die Methode sendForm sendet das Formular mit dem zuletzt eingegebenen SQL-Befehl an den Client zurück
Die Methode execute führt den SQL-Befehl aus und sendet dasErgebnis bzw. eine Fehlermeldung. Damit ist dann die HTMLSeite vollständig erzeugt.
Zu Beginn der Sitzung wird nur ein leeres Formular an denClient geschickt.
pr ivate Str ing readRequest() t hrows IOExcept i on (String line - in.readLine():if (line -- null)
throw new IOExcept i on("Ke i n Input ") :
// SO L-Befehle werden über ein Formular mittels POST// gesendetif (!line.startsWith("POST") )
return null;
5.3 Ein HTIP-Server für SQL-Abfragen
// BlS zur Leerzelle lesen und Content Length ermlttelnint 1ength ~ 0;whi1e ((line ~ in.readLine()) !~ null
&& !1i ne .equa 1s ( "" )) (I i ne = line.toLowerCase();i f (1 i ne. startsWi th( "content-l ength"))
1ength ~ Integer.parselnt(line.substring(16));
// Zeichen nach der Leerzeile einlesenint c;StringBui1der sb ~ new StringBui1der();for (int i ~ 0; i < 1ength; i++) {
c ~ in. read () ;if (c ~~ -1)
break;sb.append((char) cl;
// Decodierung der "x-WW'w-form urlencoded" ZeichenString query ~ sb.toString();int i ~ query.indexOf('~');
i f (i < 0)
return null;return URLlJecoder decode ique ry.xubst.ri nqt i + 1), "UTF-8");
Es wird nur die HTIP-Methode POST zugelassen. Somit muss derAnfrageparameter Content-Length vorhanden sein. Die Längewird extrahiert und in eine Zahl vom Typ i nt konvertiert. Zeichenweise werden nun die Daten des Nutzdatenteils in einenPuffer übertragen. Die Daten nach dem Gleichheitszeichenwerden extrahiert und gemäß UTF-8 decodiert.
private void sendForm(String sq1) throws IOExceptionout.print("HTTP/l.O 200 OKlrln"
+ "Content- Type; textihtm1 IrInIrIn" );out print("<htm1><head><tit1e>SQL</tit1e></head>");out print( "<body>"}:out print("<b>" + prop.getProperty("ur1") + "</b><p/>");out pri nt( "<form method~' PQST' accept-charset~' UTF -8' >") ;out pri nt( "<textarea co15=' 80' rows=' 4' narre=' sq 1 '>"):
if(sq1 !~null)
out. print(sq1);
out print("</textarea><p/>");out pri nt( "<i nput type=' submi t ' val ue=' Senden' ><pi>");out print("</form>");
Die MethodesendForm
183
184
Die Methodeexecute
5 Implementierung eines HTTP-Servers
i f (sql ~~ null)out. pri nt ("</body></htm1>") ;
out. fl ush() ;
Ist der Parameter sq1 gleich null (d. h. es wurden keine Benutzerdaten gesendet). so erzeugt diese Methode die kompletteHTIP-Antwort ein HTML-Formular. Andernfalls generiert sie nurden ersten Teil der Antwort: ein Formular mit dem zuletzt gesendeten SQL-BefehI. Für die Dateneingabe ist das Codierungsschema vorgegeben; accept-charset~"UTF-8".
private void execute(String sql) throws SQLExceptiontry (
Statement stmt = con.createStatement();
i f (! stmt. execute (sq1)) (
out.println(stmt.getUpdateCount() + " Zeile(n)");stmt.close();return;
// Ausgabe der Zeilen in Form einer HTML-Tabelle
ResultSet rs ~ stmt.getResultSet();ResultSetMetaData rm ~ rS.getMetaData();int n ~ rm.getColumnCount();String[J align ~ new String[nJ;
out.println("<table bgcolor~'white'
+ "border~'l' cellpadding~'5'>");
// Spaltenüberschriften der Tabelleout.println("<tr>") ;for (int i ~ 1; i <~ r: i++) (
// Zahlen werden rechtsbündig ausgerichtetif (rm.getColumnType(i) ~~ Types.TINYINT
11 rm.getColumnType(i) ~~ Types.SMALLINT11 rm.getColumnType(i) ~~ Types. INTEGER11 rm.getColumnType(i) ~~ Types.BIGINT11 rm. getColumnType (i) ~~ Types. REAL11 rm.getColumnType(i) ~~ Types.FLOAT11 rm.getColumnType(i) ~~ Types.DOUBLE11 rm.getColumnType(i) ~~ Types.NUMERIC1 1 rm. getCo1umnType (i) ~~ Types. DEC lMAl)
align[i 1J ~ "right";
5.3 Ein HTIP-Server für SQL-Abfragen
elseal ign[i IJ ~ "left";
out.println("<th aligw'" + align[i IJ + "'>"+ rm.getColumnNalTl2(i) + "</th>");
)out.println("</tr>");
// Tabellenzeilen// Anzahl Zellen und Länge elnes Spaltenwerts slnd// begrenztint count = 0;while (rs.next()) {
if (++count > MAX ROWS)break;
185
out. pri nt 1n( "<tr>");for tint l = 1; l <= n ; l++)
String s ~ rS.getString(i);if (rs.wasNull ()) {
s ~ "[NULL]";else (if (s.length() > MAX_LENGTH) (
s ~ s.substring(Q, MAX LENGTH) + "
)out.println("<td valigw'top' aligw'"
+ allgn[l 1J + "'>" + 5 + "<Ztd>"):)out. pri nt 1n( "</tr>") ;
out. pri nt 1n( "</tab1e>");if (count> MAX_ROWS)
out.println("Es werden maximal + MAX ROWS+ " Zellen angezelQt.");
rs .close();stmt. cl ose () ;catch (SOLException e)out.println(e);finally (out. pri nt 1n( "</body></html>");
" .
Der SQL-Befehl wird ausgeführt (Details zur Handhabung desJDBC-API findet man im Kapitel 2). Das Ergebnis der Datenbankänderung bzw. Abfrage ergibt den zweiten Teil der Antwort anden Client. Das Abfrageergebnis wird in einer HTML-Tabelle
186
Bild 5 .6 ,SQL-A bfrage
Hupserver
5 Implementieru ng ein es HITP~Servers
dargestellt. Um die an den Browser ZU ü benragende Datenmenge zu begrenzen , werden maximal MAX_RQWS Zeilen und je Spa ltenw ert einer Zeile maximal M\X_LENGTH Zeichen gesendet.
Iab. Iufo. Ilit t'!- - - I
1 3 . ~ s 7 -:~0998 . 3 1 Did:ms, C!>afb I ~"olas :-'~
1 3-1 ~ 8 . 33()().1 -6 1 Dieb ..., Chafb I~,:i<olars ;"Iddd>y
13-338-06658-: IDid:mo;, C'1Iafb I:-"-oddas ~dId>y
13-538-06982-1 IDidms. Chafb I:-.tdda< :-'Iddd>y
5.4 Ein einfacher WebserverSUfi bietet seit Java SE 6 ein einfaches API zur Entw icklung vonHl'Tl'<Servem. Hierbei han delr es sich um die be iden Paketecom. sun. net. httpserv er und com. sun. net. ht tcserver . sct.
Die abstrakte Klasse com. sun. net .httpserverHt tpSer ver besitztdie statische Methode cr eate, mit der eine konkrete Implementieru ng bereitgestellt wird:
stat t c HttpServer creat e( Ine tSocketAddres s addr, i nt backlo g)t hrows IOExcept i on
j eva. net . InetSocketAddress repräsentiert eine Socke t-Adresse.Der Konstruktor InetSocketAddress ( i nt por- t ) erzeugt einenSock et mit der vorgegebenen Porrnummer. back1og bestimmt diemaximale Länge der Warteschlange (siehe Kapire14 .2).
Methoden der Klasse HttpServer :
abstract HttpCon text createContext (St r i ng path,HttpHandler handl erl
liefert eine Instanz vom Typ com.sun. net. nttose-ver .HttpContext,die eine Verbindung eines Pfads (z. B. ~r) mit einer Instanz vomTyp des Interfaces com.sun. net. httpserver .HttpHandler, die dieHTfP-Anfrage bearbeitet , festlegt.
Pur einen HTfP-5erver können mehrere Kontexte (mit unterschiedlichen Handlem) eingerichtet werden.
5.4 Ein einfacher Webserver
Ein vom Browser übermittelter URL wird auf den so genanntenKontext-Pfad "bestmöglich" abgebildet.
Beispiele,
Kontext 1, 1Kontext 2, 1demol
http 111 oca1host: 500001 wird abgebildet auf Kontext 1http 1Ilocalhost:50000/demo/xyz wird abgebildet auf Kontext 2http 111 oca1host: 50000/webapp wird abgebildet auf Kontext 1
abstract vOld setExecutor( Executor executor)legt das java .uti 1 .concurrent . Executor-Objekt fest. Alle HTIPAnfragen werden diesem Objekt zur Ausführung übergeben(siehe Kapitel 4.8).
abstract void start()startet den Server .
abstract voi d stop(int delay)stoppt den Server, wobei maximal de1ay Sekunden auf die Beendigung der laufenden Anfragen gewartet wird.
Im Folgenden entwickeln wir mit Hilfe dieses API einen einfa- Programm 5.4ehen Webserver, der nur die HTIP-Methode GET versteht, QueryStrings aber nicht verarbeitet. Das Programm wird mit zwei Pa-rametern aufgerufen:
• Portnummer port und
• Name des so genannten Raot-Verzeichnisses dir.
dir ist die Wurzel des Webverzeichnisses, das alle abrufbarenRessourcen enthält.
187
lmport java.lo. IOExceptlon;lmport java net. InetSocketAddress;lmport java uti l .concurrent. Execut or s ;
lmport com .sun.n et.httpserver. HttpServer;
public class Mini WebServer (public static void main(String[] args) throws IOExcept i on {
if (args.length !- 2) (System.out.println ("java Mini WebServer <port> <root>");System.exi t( 0) :
int port - Int eger .parselnt (args [O] ) :String root - args[ I J:
MiniWebServer
188
HttpHandler
HttpExchange
5 Implementierung eines HTTP-Servers
f l nal Ht t pSer ver server = Ht t pServer create(new Inet Socket Address (por t ) , 0);
Runtime,getRuntime(),addShutdownHook(new Thread()public void run() (
if (server !- null)server,stop(O);
}System, out, pri nt 1n( "Server gestoppt");
}}) ;
server createContext(" t", new Mi ni HttpHandl er (root ) ) ;server setExecutor( Executors.newCachedThreadPool());ser ver. start();System, out, pri nt 1n( "Server gestartet ",");
Die Klasse Mi ni Ht t pHandl er implementiert HttpH andl er und nutztdiverse Methoden der Klasse HttpExchange,
Ein Handler, der die HTIP-Anfrage bearbeitet, muss das Interface cam.sun. net. httpserver . Ht t pHandl e r implementieren.
HttpHandler enthält die Methode;
void handle( HttpExchange exchange) throws IOExcept i on
Die abstrakte Klasse cam. sun. net. httpserver . HttpExchange kapseitHTIP-Anfrage und -Antwort und stellt insbesondere die folgenden Methoden zur Verfügung;
abstract String getRequestMethod()liefert die Il'FTl'<Methode.
abstract UR I getRequestURI()liefert den Uniform Resource Identifier (UR!) der Anfrage, eineVerallgemeinerung des Uniform Resource Locator (URL),
Die java .net. URI-Methode
String getPath()
liefert den Pfad des URI als Zeichenkette.
abstract Inet Socket Address getRemoteAddress()liefert die Socket-Adresse des Client.
abstract Headers getResponseHeaders()liefert eine Map zur Speicherung der Antwortparameter.
5.4 Ein einfacher Webserver
In dieser Map (com.sun. net. httpserver Headers) können mit Hilfeder Methode
void add(Str ing key. String value)
Parameter aufgenommen werden.
abstract vOld sendResponseHeaders(lnt reode,long responseLength) throws IOExcepti on
sendet die Kopfzeile mit dem Status-Code rCode und die Antwortparameter. responseLength gibt die Anzahl Bytes der Nutzdaten an (0 = nicht festgelegt).
abstract OutputStream getResponseBody()liefert den Datenstrom, in den die Nutzdaten geschrieben werden.
lmport java.lo Flle;lmport java.lo Flle InputStream;lmport java.lo FlleNotFoundExceptlon;lmport java.lo. IOExceptlon;lmport java.lo.OutputStream;lmport java net. HttpURLConnectlon;import java net.UR I ;lmport java net.URLConnectlon;lmport java text.SlmpleDateFormat;lmport java.utll.Date;
lmport com .sun net httpserver. Headers;lmport com.sun net httpserver. HttpExchange ;lmport com.sun net httpserver. HttpHandler;
public class Min iHttpHandler imp lements Ht t pHandlerprlvate Strlng root;
public Mini HttpHandler(String root) (thls.root = root;
public voi d handle(HttpExchange t) throws IOExcept ion (log(t) ;
String method ~ t.getRequestMethod();if (!method.equals("GET")) (
hand le Error(t Ht t pURLConnect l on HTTP NOT_ IMPL EMENTED."Nur die GET -Methode ist zu1ässi g");
return;
UR I uri ~ t.getRequestURI();Stri ng path ~ uri. get Path();
189
MiniHttpHandler
190 5 Implementierung eines HTTP-Servers
if (path.ends With ("/"))path ~ path + "index. htrnl":
Strlng fllename = root + path;Fi l e file ~ new Fi l e(f i l enarre ) ;
Fi l el nput St ream fis ~ null;OutputStream os ~ null;try (
fis ~ new Fi l el nput St ream(f i l e) ;
String type ~ URLConnection.getFileNa rreMap().getContentTypeFor(fil enarre);
i f (type ~~ null) (type = "appllcatlon/octet-stream";
Headers headers = t .getResponseHeaders();headers .add ("Content-Type". type);t.sendResponseHeaders (HttpURLConnection HTTP_OK. file
.length()) ;
os ~ t.getResponseBody();byte data[] ~ new byte[ 1024];i nt cnt;while ((cnt ~ fis.read(data)) !~ -1) (
os.write(data. O. cnt);}catch (Fil eNotFoundException e) {handleError(t Ht t pURLConnect lon HTTP NOT_FOUNO. e
.getMessage ());f inally {try {
i f (fis !~ null)fis .close();
if (os !~ null)os. fl ush();os.close();
}catch (I OExcept ion e)
private void 10g(HttpExchange t) (SimpleOateFormat f ~ new SimpleOateFormat(
"dd.MM.yyyy Hh .nm .ss'"):System.out.println("[" + f.format(new Oate()) + " "
+ t.getRermteAddress() + "] " + t.getRequestMethod()+ " " + t ,getRequestURI ());
5.4 Ein einfacher Webserver
private void handleError(HttpExchange t. int status.String msg) throws 10Exception (
Headers headers = t.getResponseHeaders();headers. add( "Content-Type". "textihtml");StringBuilder response ~ new StringBuilder();response.append(
"<html><head><tltle>Fehler</tltle></head>");response.append("<hl>HTTP-Status-Code; " + status
+ "</hl>");response.append("<body>" + msg + "</body></html>");t.sendResponseHeaders(status. response.length());OutputStream os ~ t.getResponseBody();os. write (response. toStri ng() .getBytes (" ISO-8859- I")) ;oS.flush();os .close();
Die Methode handle führt die folgenden Schritte zur Bearbeitungeiner HTIP-Anfrage aus.
1. Ausgabe einer Protokollzeile (Datum/Zeit. Adresse. Methode.URL)
2. Ermittlung der HTIP-Methode. Stimmt diese nicht mit GETüberein, wird eine Fehlerseite zurückgesendet,
3. Ermittlung der angefragten Ressource (Datei). Endet der Pfadmit li/li, so wird die Datei lndex.html im entsprechenden Verzeichnis angenommen.
4. Öffnen der Datei. Wird diese nicht gefunden. wird eine Feh-lerseite zurückgesendet,
5. Bestimmung des MIME-Typs
6. Setzen des Headers Content- Type
7. Senden der Kopfzeile und der Antwortparameter der HTIPAntwort
8. Lesen der Datei und Schreiben der Nutzdaten der HTIPAntwort
Der MIME-Typ der Ressource wird mit der Methode
URLConnect ion. getF i 1eNameMap () .getContentTypeFor (. .. )
ermittelt.
Die abstrakte Klasse java. net. URLConnectl on ist die Superklassederjenigen Klassen, die eine Netzverbindung zu einer durch denURL adressierten Ressource repräsentieren.
191
192
Programm 55
XMiniWebSeroer
5 Implementierung eines HTTP-Servers
static FileNameMap getFileNameMap()lädt die Tabelle der MIME-Typen aus der Datei lib\contenttypes.properties im ]RE-Verzeichnis.
Das Interface java .net. Fi 1eNameMap enthält die Methode
String getContentTypeFor(String fileName).
Sie liefert den MIME-Typ zu einem Dateinamen.
java. net. Ht t pURLConnect i on ist Subklasse von URLConnecti on undenthält insbesondere die HTTP-Status-Codes.
5.5 Webseiten dynamisch erzeugenWir erweitern nun den Mini-Webserver aus Kapitel 5.4 um zusätzliche Funktionen.
So können dann z. B. Formulare ausgewertet, Daten in einerDatenbank abgespeichert oder abgefragt werden. Zu diesemZweck unterstützt der neue Server auch die POST-Methode.
Die neue Anwendungsfunktionalität zur dynamischen Erzeugungvon HTML-Seiten wird in eigenen Klassen - unabhängig vomWebserver - realisiert. Diese Klassen können nach Bedarf zurLaufzeit des Webservers dynamisch hinzu geladen werden.
Die Klassen des erweiterten Mini-Webservers befinden sich alleim Paket httpserver .
XMl niWebServ e r unterscheidet sich von der Klasse Mi niWebServerinsbesondere dadurch, dass zwei Kontexte erzeugt werden:
// Stat ische Websei tenser ver. createContext(" t", new Mi ni HttpH andler (root ) ) ;
II Dynamlsche Webseltenser ver.createContext("/prog". new XMini Http Handler());
Der Kontextpfad "I" wird zum Abruf statischer Webseiten verwendet. Hier wird der Mi ni HttpH andl er aus Kapitel 5.4 zugeordnet. über den Kontextpfad "/prog" werden mit Hilfe vonXMi ni HttpHandle r Methoden zur dynamischen Erzeugung vonWebseiten ausgeführt. Der URL der verarbeitenden Anwendungmuss die folgende Form haben: /prog/Klasse/methode
5.5 Webseiten dynamisch erzeugen
Beispiel, /prog/Testitest1
Der Handler sorgt dann dafür, dass die statische Methode test1der Klasse prog.Test aufgerufen wird.
Der Methodenkopf muss wie folgt aussehen,
public static void methode(HttpQuery query. HttpExchange t)throws IOExceptlon
193
package httpserver ; XMiniHttpHandlerlmport java.lo.IOExceptlon;lmport java.lo.InputStream;lmport java.lo.OutputStream;import java.lang.reflect.Method;import java.net.HttpURLConnection;import java.net.URI;lmport java.text.SlmpleDateFormat;lmport java.utll.Date;
lmport com.sun net httpserver.Headers;lmport com.sun net httpserver.HttpExchange;lmport com.sun net httpserver.HttpHandler;
public class XMiniHttpHandler implements HttpHandler (public void handle(HttpExchange t) throws IOException
log(t) ;
String method ~ t.getRequestMethod();URI uri ~ t.getRequestURI();Stri ng path ~ uri. getPath();
int lastSlash ~ path.lastlndexOf("/");String classNarre ~ path.substring(I. lastSlash).replace(
'/'. '.');
String objectMethod ~ path.substring(lastSlash + I);
String q ~ null;if (rrethod.equals("GET"))
q ~ uri .getRawQuery();else if (method.equals("POST"))q ~ getRawQuery(t);else (handleError(t. HttpURLConnection.HTTP_NOT_IMPLEMENTED.
"Nur GET und POST sind zulässig");return ;
HttpQuery query ~ new HttpQuery(q);
194 5 Implementierung eines HTTP-Servers
try (Cl ass<?> c = Class.forName(className);Class<?>[] types ~ { Ht t pQuery.cl ass .
Ht t pExchange .cl ass };Method m ~ c.getMethod(objectMethod. types);Object[] args ~ { query. t };m.invoke(null. args);catch (Exception e) (System.err.println(e);
privat e String getRawQuery( HttpExchange t)throws IOExcept ion (
Headers headers ~ t.getRequestHeaders();String value ~ headers.getFirst("Content Length");int length;i f (value !~null ) (
length ~ Int eger .parsel nt (val ue) ;else (length ~ - I;
Input St ream in ~ t.getRequest BJdy();l nt c :StringBuilder sb ~ new String Bui lder();for (int i ~ 0; i < length; i++) (
c ~ in. read();if (c ~~-I)
break;sb.append((char) cl;
return sb.toString();
privat e void log(HttpExchange t ) (SimpleOateFormat f ~ new SimpleOateFormat(
"dd.MM.yyyy HH ;rrrn ;ss" ) ;System.out.println ("[" + f.format (new Oate()) + " "
+ t.getRelTDteAddress() + "] " + t.getRequestMethod()+ " " + t ,getRequestURI ());
private void handleError( HttpExchange t. i nt status.String msg) throws IOExcept ion (
Headers headers ~ t.getResponseHeaders();headers .add ("Content-Type". "textihtm1") ;StringBuilder response ~ new StringBuilder();response.append(
"<html><head><tltle>Fehler</tltle></head>");
5.5 Webseiten dynamisch erzeugen
response.append ("<h1>HTIP-Status Code: " + status+ "</h1>"):
response.append("<body>" + msg + "</body></html>"):t.sendResponse Headers(status. response.length()):OutputStream os ~ t.getResponseBody():os. write (response. toStr i ng() .getBytes (" ISO-8859-1" ) ) :os .close():
Die Methode handle extrahiert aus dem URL den Klassennamenmit Paketangabe sowie den Methodennamen. Dann wird derQuery-String im Falle der HTIP-Methode GET ermittelt. Hierzuwird die java. net. UR I-Methode getRawCuery benutzt.
public String getRawQuery()liefert den Query-String im URL-codierten Format.
Im Falle der HTIP-Methode POST werden die Nutzdaten derHTTP-Anfrage entnommen. Um die Anzahl Bytes der Nutzdatenzu bestimmen, werden zunächst die Anfrageparameter mit dercom. sun. net. httpserver .HttpExchange-Methode
getRequestHeaders
ermittelt.
abstract Headers getRequestHeaders()
Die com. sun. net. httpserver .Headers-Methode get Fl rst liefert denersten Wert eines evtl. aus mehreren einzelnen Werten bestehenden Anfrageparameters (hier: Content- Length):
String getFirst(String key)
abstract InputSt ream getRequestBody()liefert die Nutzdaten als Eingabedatenstrom im URL-codiertenFormat.
Mit Hilfe der so ermittelten Nutzdaten wird ein Objekt vom TypHt t pOuery erzeugt (Quellcode siehe weiter unten).
Nun wird die Klasse prog.Klasse geladen und die aus dem URLextrahierte Methode über das RefiRction-API bereitgestellt. Anschließend wird die (statische) Methode mit i nvoke aufgernfen.
Die Class-Methode
M2 t hod getMethod(Stri ng name. Cl ass . parameterTypes )throws NoSuchMethodException
195
196
HttpQuery
5 Implementierung eines HTTP-Servers
liefert ein l'ethod-Objekt. narre ist der Name der gewünschtenpub1i c-Methode, parameterTypes sind die Parametertypen dieserMethode, durch Cl ass-Objekte repräsentiert.
Die Klasse java.1 ang. refl ect. ~thod repräsentiert eine Methode.
Die l'ethod-Methode
Object invoke(Object ob.j , Object. args)throws IllegalAccessExceptlon,java.lang.reflect. InvocationTargetException
ruft die durch dieses l'ethod-Objekt repräsentierte Methode auf.obj ist das Objekt, für das die Methode ausgeführt werden soll,args sind die Argumente für den Methodenaufruf. Bei einfachenDatentypen werden hier die entsprechenden Hüllobjekte eingesetzt. Ist die Methode statisch, so wird obj ignoriert und kannnull sein.
Dieser indirekte Mechanismus zum Laden von Klassen und Aufruf von Methoden ermöglicht es, dass die Klasse zum Zeitpunktder Compilierung nicht bekannt sein muss.
Die Klasse HttpOuery enthält Methoden, die Parameterwerte imURL-codierten Format in decodierter Form bereitstellen. ZurDecodierung wird das Codierungsschema UTF-8 verwendet. Zubeachten ist, dass ein Parametername auch mehrfach auftretenkann. Parametername und -wert werden in einer Hashtable gespeichert. Der Parametername stellt den Schlüssel dar, die evtl,mehrfach auftretenden Parameterwerte zum gleichen Namenwerden in einem Vektor zusammengefasst, der dann als Wertzum Schlüssel in die Hashtable eingetragen wird.
package httpserver;lmport java.lo.UnsupportedEncodlngExceptlon;lmport java.net.URLDecoder;import java. uti 1.Hashtab1e;lmport java.utll.StrlngTokenlzer;lmport java.utll .Vector;
public class HttpOuery (prlvate Hashtable<Strlng, Vector<Strlng» h;
pub1i c HttpOuery(String query) (h = new Hashtable<String, Vector<String»();parseOueryString(query);
public String getParameter(S tring name) (Vector<String> v = h.get(name);if (v ~~ null)
5.5 Webseiten dynamisch erzeugen
return null;else
return v.get(O);
public String[] getParameterValues(String name) (Vector<Strlng> v = h.get(name);if(v~~null)
return null;else (
String[] result ~ new String[v size()];v.copylnto(result);return result;
/** Allgemelne Form elnes Query Strlngs:* namel=valuel&name2=value2.* Eln Feldname kann auch mehrfach auftreten.*/
private void parseQueryString(String query) (StrlngTokenizer params = new StringTokenizer(query, "&");
StringTokenizer param;String name, value;
while (params.hasMoreTokens()) (param = new Stri ngTokeni zer(params. nextToken(), "=");
name = param.nextToken();if (param.hasMoreTokens())
value = param.nextToken();else
value "".
if (name !~ null)String dvalue ~ "".try (
dvalue ~ URLl:ecoder.decode(value. "UTF-8");catch (UnsupportedEncodingException e) (
// Prüfen. ob der Name bereits in der Hashtable// vorkommt. Die Werte werden jeweils in einem// Vektor gespeichert.
Vector<String> v = h.get(name);if(v~~null){
v = new Vector<String>();v.add(dvalue);h put(na me. v);
197
198
posthtml
Test
5 Implementierung eines HTTP-Servers
else (v.add(dvalue);
Die Klasse prog. Test soll die neue Funktionalität demonstrieren.Die Methode testl wertet ein Formular aus.
<html><head><title>Test (POST)</title></head><body>
<form actiow"/prog/Testitest1" method~"POST"
accept-charset~"UTF-8">
Name: <lnput type="text" slze="60" narre="narre"/><p/><lnput type="checkbox" narre="sprache"
value="Java"/>Java<br/><lnput type="checkbox" narre="sprache"
val ue="C++" />C++<br/><input type~"checkbox" narre~"sprache" value~"C#"/>C#<p/>
<lnput type="submlt" value="Senden"/><lnput type=" reset" val ue-" Löschen" />
</form></body></html>
package prog;
import httpserver HttpOuery;
lmport java lo.IOExceptlon;lmport java lO.OutputStream;import java net. HttpURLConnecti on;
lmport com.sun net.httpserver Headers;lmport com.sun net.httpserver HttpExchange;
public class Test (public static void test1(HttpOuery query. HttpExchange t)
throws IOException (Strl ng narre = query. getPararreter( "name");String[] sprachen ~ query.getPararreterVal ues("sprache");
Headers headers = t.getResponseHeaders();headers .add ("Content-Type". "textihtm1") ;
5,5 Webseiten dynamisch erze uge n
Strin[ßuilder resPJnse - new Strin[ßuilder(),response append(
"<html ><head><ti t l e>Test</ti tl e></head><body>") ,resPJnse append("<b>Name </b> " + name + "<p/>" ) ,response eppenoc '<b-Pron-amnerspr-echen </b><br/> "),i f (sprachen 1- null) (
for (int i - 0, i < sprachen ierutn: i ++)response append(sprachen[iJ + "<br v>" ) .
}response append(
"<p>.e hret-.' /index html '>Zurück</a></p>"),response append("</body></html> "),
t sendkesponselieaders (HttpURLConnection HTTP_OK, response1enutncn:
199
Outpu tStream osos wr -t t erresronseos fl usno:os ctoseö:
:\lII:ne: Hugo Mei""
::ll1a\':3"c_"J C#
t getResPJnseBodyO,toString() getBytes( " ISO-8859 1")) ,
Bild5.7-Formularpost btmt
I..Senden I1 Losehen I
.:"'a m..: Hugo ~![Oer
P.".grammi..rspra c~ ..a :J",-a
C,
Die Anwendung der GET-Methode (im Formular ist POST durchI1:T zu ersetze n) führt zu ei nem entsp reche nde n Erg ebnis ,
au s.e,Ergebnis
200
MyRepotter
5 Implementierung eines HTTP-Servers
5.6 Protokollierung von HTTP-NachrichtenDas Programm MyReport er kann genutzt werden, um die zwischen Client und Server ausgetauschten Daten aufzuzeichnen.
Ein Shutdown hook sorgt für das ordnungsgemäße Schließen derLog-Dateien, wenn das Programm mit Strg+C beendet wird.
lmport ja va lo. FlleOutputStream;lmport ja va lo. IO Exceptlon;lmport ja va lo. InputStream;lmport ja va lO.OutputStream;lmport ja va net.ServerSocket;lmport ja va net.Socket;lmport ja va net.Socket Exceptlon;
publ i c class MyReporter ext ends Thread (prlvate statlc OutputStream logRequest;prlvate statlc OutputStream logResponse;prlvate statlc ServerSocket srv;prlvate statlc lnt counter;
prlvat e Input St ream fro mCllent;prl vate OutputStream toServer;
public MyReporter( InputStream fro mClient,OutputStream toServer) (
th is,fromClient ~ fro mClient;thls.toServer = toSer ver;
public void run() {try (
l nt c :logRequest,write(("#" + counter + "lrln"),get Bytes());while ((c ~ fro mClient,read()) !~ - 1) (
logRequest,write(c);toSer ver.wrlte(c) ;
}catch ( IO Exception e)
try (1ogRequest, wri te("1 rlnl rln" ,getBytes()) ;catch ( IO Exception e) (
public static voi d main(String[J args)int port ~ Int eger ,parsel nt (args[OJ ) ;String remoteHost ~ args[ IJ;
5.6 Protokollierung von HTIP-Nachrichten
int remotePort ~ Integer parseInt(args[2]);String filel args[3];String file2 ~ args[4];
// ShutdownHook reglstrlerenRuntime.getRuntime().addShutdownHook(new Thread()
public void run() (try (
if (logRequest !~ null)logRequest.flush();logRequest.close();
}if (logResponse !~ null)
logResponse. fl ush();logResponse.close();
}if (srv !~ null)
srv.close();catch (IOException e) (
)}) ;
try (logRequest ~ new FileOutputStream(filel);logResponse ~ new FileOutputStream(file2);srv = new ServerSocket(port);
whi 1e (true) (Socket client ~ srv.accept();InputStream fromClient ~ client.getInputStream();OutputStream toClient ~ client.getOutputStream();
Socket socket = new Socket(remoteHost, remotePort);InputStream fromServer ~ socket.getInputStream();OutputStream toServer ~ socket.getOutputStream();
counter++;
// Welterleltung vom Cllent an den Server(new MyReporter(fromClient. toServer)).start();
// Welterleltung vom Server an den Cllentlnt c;logResponse.wrlte(
("#" + counter + "lrln").getBytes());while ((c ~ fromServer read()) !~ -1) (
logResponse.write(c);toClient.write(c);
}logResponse. write("lrlnlrln" .getBytes());
201
202
sua s».MyReporterzeichnetHTIP-Anfrage und-Antwort auf
5 Implementierung eines HTTP-Servers
toClient. fl ush();client.close();socket.close();
}catch (SocketException e)catch ( IOException e) (System.err.println(e);
fromcne nt tos ev er
Client MyReporter Server
toClient Port 40000 fromServer Port 50000
Icgpeqc;l ~peopcq o e~ ~
Das Programm kann wie folgt gestartet werden (Kommando ineiner Zeile}
java -cp bln MyReporter <localPort> <remoteHost> <remotePort><logRequest> <logResponse>
Beispiel,
java -cp bin MyReporter 40000 localhost 50000 10gRequest.txtlogResponse .txt
5.7 Aufgaben
1. Entwickeln Sie ein Programm, mit dem Dateien vom Webserver mittels HTTP herunter geladen werden können. Nutzen Sie hierzu die Socket-Programmierung mit TCP (sieheKapitel 4).
2. Realisieren Sie eine Variante zum Downloadprogramm inAufgabe 1, indem Sie die folgenden Methoden der KlassenURL und URLConnecti on benutzen.
Die Klasse java. net . URL repräsentiert einen Uniform Resource Locator.
5.7 Aufgaben
URL(String spec) throws MaiformedU RL Exceptionerzeugt ein URL-Objekt aus der Zeichenkette spec. java. net.Ma lformedURL Exception ist von java. i o. IOExcepti on abgeleitet.
Die URL-MethodeURLConnectlon openConnection() throws IOExcept lonliefert ein java. net. URLConnecti on-Objekt, das eine Verbindung zu einer durch den URL adressierten Ressource repräsentiert.
URLConnect i on-Methoden:
int getContentLength ()liefert den Wert des Antwortparameters Content Length.
Input St re am getlnputStream() throws IOExcept i onliefert einen Eingabestrom zum Lesen über diese Verbindung .
3. Entwickeln Sie für den erweiterten Webserver XMlnl WebServer aus Kapitel 5.5 eine Klasse Buecherllste, deren Methode getTable die zu einem vom Benutzer in einem Formular eingegebenen Wort Angaben zu denjenigen Büchernaus einer Bücher-Datenbank anzeigt, deren Titel diesesWort enthalten. Die Daten sollen in einer HTML-Tabelledargestellt werden.
Entwickeln Sie eine weitere Methode, die das Ergebnis imXML-Forrnat ausgibt.
4. Entwickeln Sie mit Hilfe der in Aufgabe 2 genannten Methoden die Klasse BuecherCl l ent, die die Bücherliste ausAufgabe 3 im XML-Format anfordert und in einer Datei(result.xml) speichert.
Aufrufbeispiel (Kommando in einer Zeile}
java -cp bin BuecherCiient iocaihost 50000prog/ Buecheriiste/getXML?titei~Topkapi result xmi
203