Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
| efinf:blc2016:networks:http [2016/12/08 07:57] – Ivo Blöchliger | efinf:blc2016:networks:http [2016/12/08 08:19] (current) – Ivo Blöchliger | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | {{backlinks> | ||
| + | ===== HTTP ===== | ||
| + | Hypertext Transfer Protocol: Transfer von Webseiten und Zugemüse (auch einfach Dateien). | ||
| + | |||
| + | ==== Aufgabe ==== | ||
| + | Programmieren Sie einen minimalen Webserver, der folgende Anforderungen erfüllt: | ||
| + | |||
| + | * Es gibt einen Ordner **html**, indem Dateien (und Unterordner) liegen, die der Webserver ausliefern kann. | ||
| + | * Prüfen Sie den angeforderten Pfad. Insbesondere darf es nicht möglich sein, auf Dateien ausserhalb des Verzeichnisses **html** zuzugreifen. | ||
| + | * Schreiben Sie eine minimale html-Datei, die auch ein Bild enthält und testen Sie ihren Webserver damit. | ||
| + | |||
| + | === Hilfestellungen === | ||
| + | == HTTP Protokoll == | ||
| + | Minimale HTTP-Anfrage (Achtung, verwendet " | ||
| + | <code text> | ||
| + | GET /bla.html HTTP/ | ||
| + | Host: fginfo.ksbg.ch: | ||
| + | Connection: close | ||
| + | \r\n | ||
| + | \r\n | ||
| + | </ | ||
| + | Die erste Zeile enthält Methode (GET), Pfad /bla.html und Protokollversion. | ||
| + | Die zweite Zeile enthält den Servernamen und evtl. den Port | ||
| + | Die dritte Zeile ist optional | ||
| + | Zwei leere Zeilen schliessen die Anfrage ab. | ||
| + | |||
| + | Die Minimale Antwort sieht wie folgt aus: | ||
| + | <code text> | ||
| + | HTTP/1.1 200 OK\r\n | ||
| + | Content-Type: | ||
| + | Content-Length: | ||
| + | \r\n | ||
| + | .... DATEN .... | ||
| + | </ | ||
| + | Oder natürlich | ||
| + | <code text> | ||
| + | HTTP/1.1 404 Not Found | ||
| + | </ | ||
| + | Als Content-Type kommt z.B. auch image/png oder imag/jpg in Frage. | ||
| + | |||
| + | == Ruby File Access == | ||
| + | Siehe auch https:// | ||
| + | |||
| + | * File.exists? | ||
| + | * File.read(pfad) | ||
| + | * File.absolute_path(pfad) | ||
| + | |||
| + | Um mit Verzeichnissen zu arbeiten, siehe auch https:// | ||
| + | * Dir.pwd | ||
| + | * Dir.glob(" | ||
| + | |||
| + | |||
| + | |||
| + | ==== Lösungsvorschlag ==== | ||
| + | <code ruby webserver.rb> | ||
| + | # coding: utf-8 | ||
| + | require ' | ||
| + | |||
| + | # Damit Ruby auch bei Fehlern im Thread abbricht mit Fehlermeldung | ||
| + | Thread.abort_on_exception=true | ||
| + | |||
| + | # 2 Methoden für Fehlermeldungen | ||
| + | |||
| + | def notfound(client, | ||
| + | msg = "< | ||
| + | client.puts " | ||
| + | Content-Type: | ||
| + | Content-Length: | ||
| + | client.puts msg | ||
| + | puts "Got a 404" | ||
| + | end | ||
| + | |||
| + | def badRequest(client, | ||
| + | msg = "< | ||
| + | client.puts " | ||
| + | Content-Type: | ||
| + | Content-Length: | ||
| + | client.puts msg | ||
| + | puts "Got a 400" | ||
| + | end | ||
| + | |||
| + | # Methode für Dirlisting | ||
| + | def dirlist(client, | ||
| + | relpfad = "/" | ||
| + | puts " | ||
| + | files = Dir.glob(abspfad+"/ | ||
| + | relpfad.gsub(/ | ||
| + | msg = "< | ||
| + | relpfad.gsub!(/ | ||
| + | files.each{|f| | ||
| + | f = File.basename(f) | ||
| + | puts relpfad | ||
| + | puts f | ||
| + | msg+="< | ||
| + | } | ||
| + | client.puts " | ||
| + | Content-Type: | ||
| + | Content-Length: | ||
| + | client.puts msg | ||
| + | puts " | ||
| + | end | ||
| + | |||
| + | |||
| + | # Server auf port 420XX laufen lassen | ||
| + | server = TCPServer.open(42001) | ||
| + | # Endlos-Schleife | ||
| + | loop { | ||
| + | # Auf Verbindung warten. | ||
| + | # Wenn verbunden, einen Thread damit beschäftigen | ||
| + | # und gleich auf nächste Verbindung warten | ||
| + | Thread.start(server.accept) { |client| | ||
| + | # Request einlesen, zeilenweise im Array all speichern | ||
| + | all = [] | ||
| + | while ((req=client.gets.chomp)!="" | ||
| + | puts req | ||
| + | all.push(req) | ||
| + | end | ||
| + | p all | ||
| + | # Erste Zeile analysieren | ||
| + | pfad = all[0].scan(/ | ||
| + | # Wenn kein Match, also bad request (nicht http-protokoll) | ||
| + | p pfad | ||
| + | if (pfad.size==0) | ||
| + | puts " | ||
| + | badRequest(client, | ||
| + | else | ||
| + | puts | ||
| + | # Dem Request den ordner ' | ||
| + | pfad = " | ||
| + | # Daraus den absoluten Pfad erstellen | ||
| + | pfad = File.absolute_path(pfad) | ||
| + | # Überprüfen, | ||
| + | # Aktueller Pfad plus html | ||
| + | richtig = File.absolute_path(Dir.pwd+"/ | ||
| + | p pfad | ||
| + | p richtig | ||
| + | if pfad.start_with? | ||
| + | if (File.directory? | ||
| + | dirlist(client, | ||
| + | else | ||
| + | puts "Got a 200" | ||
| + | # Typ bestimmen: Paare aus RegEx und MIME-Type | ||
| + | types = [[/ | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | type = " | ||
| + | types.each{|t| | ||
| + | if pfad =~ t[0] | ||
| + | type=t[1] | ||
| + | end | ||
| + | } | ||
| + | daten = File.read(pfad) | ||
| + | client.puts " | ||
| + | Content-Type: | ||
| + | Content-Length: | ||
| + | client.print daten | ||
| + | end | ||
| + | else | ||
| + | puts "Not found" | ||
| + | notfound(client, | ||
| + | end | ||
| + | end | ||
| + | client.close | ||
| + | } | ||
| + | } | ||
| + | </ | ||