Show pageOld revisionsBacklinksBack to top This page is read only. You can view the source, but not change it. Ask your administrator if you think this is wrong. {{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 "\r\n" für einen Zeilenumbruch (wie DOS), nicht nur einfach "\n" (wie Unix). <code text> GET /bla.html HTTP/1.1\r\n Host: fginfo.ksbg.ch:42001\r\n Connection: close \r\n \r\n </code> 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: text/html\r\n Content-Length: 1354\r\n \r\n .... DATEN .... </code> Oder natürlich <code text> HTTP/1.1 404 Not Found </code> Als Content-Type kommt z.B. auch image/png oder imag/jpg in Frage. == Ruby File Access == Siehe auch https://ruby-doc.org/core-2.2.0/File.html * File.exists?(pfad) Überprüft, ob die Datei existiert. * File.read(pfad) Liefert den Inhalt einer Datei als String zurück * File.absolute_path(pfad) Liefert den absoluten Pfad zurück (z.B. /home/hansli/server/html/index.html) Um mit Verzeichnissen zu arbeiten, siehe auch https://ruby-doc.org/core-2.2.0/File.html * Dir.pwd liefert den aktuellen Pfad * Dir.glob("html/*") liefert ein Array mit allen Dateien im Verzeichnis html ==== Lösungsvorschlag ==== <code ruby webserver.rb> # coding: utf-8 require 'socket' # Damit Ruby auch bei Fehlern im Thread abbricht mit Fehlermeldung Thread.abort_on_exception=true # 2 Methoden für Fehlermeldungen def notfound(client, pfad, debug="") msg = "<!DOCTYPE html><html><body><h1>404 Not found</h1>Sorry, ich bin halt nur ein Webserver... #{pfad} habe ich nicht gefunden...<br><br><h2>Debug Info</h2><pre>#{debug}</pre></body></html>" client.puts "HTTP/1.1 404 Not Found\r Content-Type: text/html\r Content-Length: #{msg.size}\r\n\r\n" client.puts msg puts "Got a 404" end def badRequest(client, req, debug="") msg = "<!DOCTYPE html><html><body><h1>400 Bad request</h1>Sorry, die Anfrage #{req} verstehe ich nicht.<br><br><h2>Debug Info</h2><pre>#{debug}</pre></body></html>" client.puts "HTTP/1.1 400 Bad request\r Content-Type: text/html\r Content-Length: #{msg.size}\r\n\r\n" client.puts msg puts "Got a 400" end # Methode für Dirlisting def dirlist(client,basepfad, abspfad, relpfad) relpfad = "/" if (relpfad=="") puts "Dirlist für #{abspfad}, #{relpfad}" files = Dir.glob(abspfad+"/*") # Alle Dateien im Verzeichnis relpfad.gsub(/\/+$/,"") # Remove trailing slash msg = "<!DOCTYPE html><html><body><h1>Directory #{relpfad}</h1><ul>" relpfad.gsub!(/\/+$/,"") # Remove trailing slashes files.each{|f| f = File.basename(f) puts relpfad puts f msg+="<li><a href=\"#{relpfad + "/" +f}\">#{f}</a></li>\n" } client.puts "HTTP/1.1 200 OK\r Content-Type: text/html\r Content-Length: #{msg.size}\r\n\r\n" client.puts msg puts "Listed #{abspfad}" 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(/^GET ([^ ]+)/) # Wenn kein Match, also bad request (nicht http-protokoll) p pfad if (pfad.size==0) puts "400!" badRequest(client, all[0], all.join("\n")) else puts # Dem Request den ordner 'html' vorne anfügen pfad = "html"+pfad[0][0] # Daraus den absoluten Pfad erstellen pfad = File.absolute_path(pfad) # Überprüfen, ob auch im richtigen verzeichnis # Aktueller Pfad plus html richtig = File.absolute_path(Dir.pwd+"/html") p pfad p richtig if pfad.start_with?(richtig) && File.exists?(pfad) if (File.directory?(pfad)) dirlist(client,richtig, pfad, pfad.gsub(richtig,"")) else puts "Got a 200" # Typ bestimmen: Paare aus RegEx und MIME-Type (/i macht die regex case-insensitive) types = [[/html?$/i,"text/html"], [/jpe?g$/i,'image/jpg'], [/png$/i,'image/png'], [/gif/i, 'image/gif'], [/txt/i, 'text/plain']] type = "text/html" # Default, wenn nichts anderes gefunden types.each{|t| if pfad =~ t[0] type=t[1] end } daten = File.read(pfad) client.puts "HTTP/1.1 200 OK\r Content-Type: #{type}\r Content-Length: #{daten.size}\r\n\r\n" client.print daten end else puts "Not found" notfound(client, pfad, all.join("\n")) end end client.close } } </code> efinf/blc2016/networks/http.txt Last modified: 2016/12/08 08:19by Ivo Blöchliger