Last active
March 25, 2017 14:52
-
-
Save menzerath/16dbf192de45e11eab48 to your computer and use it in GitHub Desktop.
Facharbeit: Funktionsweise des HTTP-Protokolls und Implementation eines einfachen Webservers in Java - http://menzerath.eu/artikel/wie-funktioniert-das-http-protokoll/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package de.menzerath.util; | |
import java.io.File; | |
import java.text.DecimalFormat; | |
import java.util.HashMap; | |
public class FileManager { | |
private static HashMap<String, String> mimeTypes = new HashMap<>(); | |
private static boolean mimeTypesInitCompleted = false; | |
/** | |
* Diese HashMap beinhaltet einige Dateiendungen, die vom Webserver nicht mit dem Standard-MimeType ausgeliefert werden sollen | |
* | |
* @return Alle konfigruierten Dateiendungen mit ihren MimeTypes | |
*/ | |
public static HashMap<String, String> getMimeTypes() { | |
if (mimeTypesInitCompleted) return mimeTypes; | |
// Bilder | |
mimeTypes.put(".gif", "image/gif"); | |
mimeTypes.put(".jpg", "image/jpeg"); | |
mimeTypes.put(".jpeg", "image/jpeg"); | |
mimeTypes.put(".png", "image/png"); | |
// Audio | |
mimeTypes.put(".mp3", "audio/mpeg"); | |
mimeTypes.put(".mp4", "video/mp4"); | |
mimeTypes.put(".flv", "video/x-flv"); | |
// Webseiten | |
mimeTypes.put(".html", "text/html"); | |
mimeTypes.put(".htm", "text/html"); | |
mimeTypes.put(".shtml", "text/html"); | |
mimeTypes.put(".xhtml", "text/html"); | |
mimeTypes.put(".css", "text/css"); | |
mimeTypes.put(".js", "text/js"); | |
// Anderes | |
mimeTypes.put(".txt", "text/plain"); | |
mimeTypes.put(".log", "text/plain"); | |
mimeTypes.put(".md", "text/x-markdown"); | |
mimeTypes.put(".pdf", "application/pdf"); | |
mimeTypes.put(".xml", "application/xml"); | |
mimeTypes.put(".java", "text/plain"); | |
mimeTypesInitCompleted = true; | |
return mimeTypes; | |
} | |
/** | |
* Wandelt die Dateigröße in einen lesbaren Wert (mit entsprechender Einheit) um | |
* Quelle: Mr Ed: Format file size as MB, GB etc, 08.04.2011 | |
* http://stackoverflow.com/questions/3263892/format-file-size-as-mb-gb-etc, 18.01.2014 | |
* | |
* @param size Dateigröße (in Bits) | |
* @return Lesbare Dateigröße | |
*/ | |
public static String getReadableFileSize(long size) { | |
if (size <= 0) return "0"; | |
final String[] units = new String[]{"B", "KB", "MB", "GB", "TB", "PB", "EB"}; | |
int digitGroups = (int) (Math.log10(size) / Math.log10(1024)); | |
return new DecimalFormat("#,##0.#").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups]; | |
} | |
/** | |
* Gibt den Content-Type (Mime-Type) für die entsprechende Dateiendung zurück | |
* | |
* @param file Zu prüfende Datei | |
* @return Content-Type der Datei | |
*/ | |
public static String getContentType(File file) { | |
return getMimeTypes().get(getFileExtension(file)); | |
} | |
/** | |
* Gibt die Dateiendung der gewählten Datei zurück (zB: "txt") | |
* | |
* @param file Zu prüfende Datei | |
* @return Dateiendung | |
*/ | |
private static String getFileExtension(File file) { | |
String filename = file.getName(); | |
int pos = filename.lastIndexOf("."); | |
if (pos >= 0) return filename.substring(pos).toLowerCase(); | |
return ""; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package de.menzerath.httpserver; | |
import de.menzerath.util.Logger; | |
import de.menzerath.util.ServerHelper; | |
import java.io.File; | |
import java.io.IOException; | |
import java.net.ServerSocket; | |
public class HTTPServer { | |
/** | |
* Einstiegspunkt der Anwendung; erstellt ein HTTPServer-Objekt | |
* @param args Beim Aufruf übergebene Argumente; werden aber ignoriert | |
*/ | |
public static void main(String[] args) { | |
new HTTPServer(8080, new File("./www"), true, new File("log.txt")); | |
} | |
/** | |
* Konstruktor; erstellt (wenn nötig) den Ordner für die Daten und startet schließlich den ConnectionListener | |
*/ | |
public HTTPServer(int port, final File webRoot, final boolean allowDirectoryListing, File logfile) { | |
Logger.setLogfile(logfile); | |
// Gib die IP-Adresse sowie den Port des Servers aus | |
// Passe die Ausgabe an die Länge der IP-Adresse + Port an | |
String lineOne = ""; | |
String lineUrl = "### http://" + ServerHelper.getServerIp() + ":" + port + " ###"; | |
String lineTitle = "### HTTP-Server"; | |
for (int i = 0; i < lineUrl.length(); i++) lineOne += "#"; | |
for (int i = 0; i < lineUrl.length() - 18; i++) lineTitle += " "; | |
lineTitle += "###"; | |
// Ausgabe der Informationen | |
System.out.println(lineOne); | |
System.out.println(lineTitle); | |
System.out.println(lineUrl); | |
System.out.println(lineOne); | |
// Erstellt einen Ordner für die Daten (falls nötig) | |
if (!webRoot.exists() && !webRoot.mkdir()) { | |
// Ordner existiert nicht & konnte nicht angelegt werden: Abbruch | |
Logger.exception("Konnte Daten-Verzeichnis nicht erstellen."); | |
Logger.exception("Beende..."); | |
System.exit(1); | |
} | |
// Erstelle einen ServerSocket mit dem angegebenen Port | |
ServerSocket socket = null; | |
try { | |
socket = new ServerSocket(port); | |
} catch (IOException | IllegalArgumentException e) { | |
// Port bereits belegt, darf nicht genutzt werden, ...: Abbruch | |
Logger.exception(e.getMessage()); | |
Logger.exception("Beende..."); | |
System.exit(1); | |
} | |
// Neuer Thread: wartet auf eingehende Verbindungen und "vermittelt" diese an einen neuen HTTPThread, der die Anfrage dann verarbeitet | |
final ServerSocket finalSocket = socket; | |
Thread connectionListener = new Thread(){ | |
public void run(){ | |
while (true) { | |
try { | |
HTTPThread thread = new HTTPThread(finalSocket.accept(), webRoot, allowDirectoryListing); | |
thread.start(); | |
} catch (IOException e) { | |
Logger.exception(e.getMessage()); | |
Logger.exception("Beende..."); | |
System.exit(1); | |
} | |
} | |
} | |
}; | |
connectionListener.start(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package de.menzerath.httpserver; | |
import de.menzerath.util.ServerHelper; | |
import de.menzerath.util.FileManager; | |
import de.menzerath.util.Logger; | |
import java.io.*; | |
import java.net.Socket; | |
import java.net.SocketException; | |
import java.net.URLDecoder; | |
import java.text.SimpleDateFormat; | |
import java.util.*; | |
public class HTTPThread extends Thread { | |
private Socket socket; | |
private File webRoot; | |
private boolean allowDirectoryListing; | |
/** | |
* Konstruktor; speichert die übergebenen Daten | |
* | |
* @param socket verwendeter Socket | |
* @param webRoot Pfad zum Hauptverzeichnis | |
* @param allowDirectoryListing Sollen Verzeichnisinhalte aufgelistet werden, falls keine Index-Datei vorliegt? | |
*/ | |
public HTTPThread(Socket socket, File webRoot, boolean allowDirectoryListing) { | |
this.socket = socket; | |
this.webRoot = webRoot; | |
this.allowDirectoryListing = allowDirectoryListing; | |
} | |
/** | |
* "Herz" des Servers: Verarbeitet den Request des Clients, und sendet schließlich die Response | |
*/ | |
public void run() { | |
// Vorbereitung und Einrichtung des BufferedReader und BufferedOutputStream | |
// zum Lesen des Requests und zur Ausgabe der Response | |
final BufferedReader in; | |
final BufferedOutputStream out; | |
try { | |
in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF8")); | |
out = new BufferedOutputStream(socket.getOutputStream()); | |
} catch (IOException e) { | |
Logger.exception(e.getMessage()); | |
return; | |
} | |
// Timeout für die Verbindung von 30 Sekunden | |
// Verhindert zu viele offengehaltene Verbindungen, aber auch Übertragung von großen Dateien | |
try { | |
socket.setSoTimeout(30000); | |
} catch (SocketException e) { | |
Logger.exception(e.getMessage()); | |
} | |
// Lesen des Request | |
String line; | |
ArrayList<String> request = new ArrayList<>(); | |
try { | |
while ((line = in.readLine()) != null && line.length() != 0 && !line.equals("")) { | |
request.add(line); | |
} | |
} catch (IOException e) { | |
// Request konnte nicht (korrekt) gelesen werden | |
sendError(out, 400, "Bad Request"); | |
Logger.exception(e.getMessage()); | |
return; | |
} | |
// Request war leer; sollte nicht auftreten | |
if (request.isEmpty()) return; | |
// Nur Requests mit dem HTTP 1.0 / 1.1 Protokoll erlaubt | |
if (!request.get(0).endsWith(" HTTP/1.0") && !request.get(0).endsWith(" HTTP/1.1")) { | |
sendError(out, 400, "Bad Request"); | |
Logger.error(400, "Bad Request: " + request.get(0), socket.getInetAddress().toString()); | |
return; | |
} | |
// Es muss ein GET- oder POST-Request sein | |
boolean isPostRequest = false; | |
if (!request.get(0).startsWith("GET ")) { | |
if (request.get(0).startsWith("POST ")) { | |
// POST-Requests werden gesondert behandelt | |
isPostRequest = true; | |
} else { | |
// Methode nicht implementiert oder unbekannt | |
sendError(out, 501, "Not Implemented"); | |
Logger.error(501, "Not Implemented: " + request.get(0), socket.getInetAddress().toString()); | |
return; | |
} | |
} | |
// Auf welche Datei / welchen Pfad wird zugegriffen? | |
String wantedFile; | |
String path; | |
File file; | |
// GET-Request ist wahrscheinlicher, daher wird zuerst diese Methode angenommen | |
wantedFile = request.get(0).substring(4, request.get(0).length() - 9); | |
if (isPostRequest) wantedFile = request.get(0).substring(5, request.get(0).length() - 9); | |
// GET-Request mit Argumenten: Entferne diese für die Pfad-Angabe | |
if (!isPostRequest && request.get(0).contains("?")) { | |
path = wantedFile.substring(0, wantedFile.indexOf("?")); | |
} else { | |
path = wantedFile; | |
} | |
// Bestimme nun die exakte Datei, bzw. das Verzeichnis, welche(s) angefordert wurde | |
try { | |
file = new File(webRoot, URLDecoder.decode(path, "UTF-8")).getCanonicalFile(); | |
} catch (IOException e) { | |
Logger.exception(e.getMessage()); | |
return; | |
} | |
// Falls ein Verzeichnis angezeigt werden soll, und eine Index-Datei vorhanden ist | |
// soll letztere angezeigt werden | |
if (file.isDirectory()) { | |
File indexFile = new File(file, "index.html"); | |
if (indexFile.exists() && !indexFile.isDirectory()) { | |
file = indexFile; | |
// "/index.html" an Verzeichnispfad anhängen | |
if (wantedFile.contains("?")) { | |
wantedFile = wantedFile.substring(0, wantedFile.indexOf("?")) + "/index.html" + wantedFile.substring(wantedFile.indexOf("?")); | |
} | |
} | |
} | |
if (!file.toString().startsWith(ServerHelper.getCanonicalWebRoot(webRoot))) { | |
// Datei liegt nicht innerhalb des Web-Roots: Zugriff verhindern und | |
// Fehlerseite senden | |
sendError(out, 403, "Forbidden"); | |
Logger.error(403, wantedFile, socket.getInetAddress().toString()); | |
return; | |
} else if (!file.exists()) { | |
// Datei existiert nicht: Fehlerseite senden | |
sendError(out, 404, "Not Found"); | |
Logger.error(404, wantedFile, socket.getInetAddress().toString()); | |
return; | |
} else if (file.isDirectory()) { | |
// Innerhalb eines Verzeichnis: Auflistung aller Dateien | |
// Verzeichnisauflistung verboten? | |
if (!allowDirectoryListing) { | |
// Fehlermeldung senden | |
sendError(out, 403, "Forbidden"); | |
Logger.error(403, wantedFile, socket.getInetAddress().toString()); | |
return; | |
} | |
// Ersetze alle "%20"-Leerzeichen mit einem "echten" Leerzeichen | |
path = path.replace("%20", " "); | |
File[] files = file.listFiles(); | |
// Das Verzeichnis ist leer? Sende eine entsprechende Fehlermeldung | |
if (files != null) { | |
if (files.length == 0) { | |
sendError(out, 404, "Not Found"); | |
Logger.error(404, wantedFile, socket.getInetAddress().toString()); | |
return; | |
} | |
} else { | |
// Kann unter Umständen auf Windows-Systemen vorkommen | |
// Beispiel: Aufruf von "Documents and Settings" anstelle von "Users" | |
sendError(out, 403, "Forbidden"); | |
Logger.error(403, wantedFile, socket.getInetAddress().toString()); | |
return; | |
} | |
// Alle Einträge alphabetisch sortieren: Zuerst Ordner, danach Dateien | |
Arrays.sort(files, new Comparator<File>() { | |
@Override | |
public int compare(File f1, File f2) { | |
if (f1.isDirectory() && !f2.isDirectory()) { | |
return -1; | |
} else if (!f1.isDirectory() && f2.isDirectory()) { | |
return 1; | |
} else { | |
return f1.toString().compareToIgnoreCase(f2.toString()); | |
} | |
} | |
}); | |
// Ausgabe in einer Tabelle vorbereiten | |
String content = "<table><tr><th></th><th>Name</th><th>Last modified</th><th>Size</th></tr>"; | |
// Einen "Ebene höher"-Eintrag anlegen, falls nicht im Web-Root gearbeitet wird | |
if (!path.equals("/")) { | |
String parentDirectory = path.substring(0, path.length() - 1); | |
int lastSlash = parentDirectory.lastIndexOf("/"); | |
if (lastSlash > 1) { | |
parentDirectory = parentDirectory.substring(0, lastSlash); | |
} else { | |
parentDirectory = "/"; | |
} | |
content += "<tr><td class=\"center\"><div class=\"back\"> </div></td>" + | |
"<td><a href=\"" + parentDirectory.replace(" ", "%20") + "\">Parent Directory</a></td>" + | |
"<td></td>" + | |
"<td></td></tr>"; | |
} | |
if (path.equals("/")) path = ""; // Anpassung für Dateiauflistung | |
// Jede Datei zur Ausgabe hinzufügen | |
for (File myFile : files) { | |
// Meta-Daten der Datei abrufen | |
String filename = myFile.getName(); | |
String img; | |
String fileSize = FileManager.getReadableFileSize(myFile.length()); | |
if (myFile.isDirectory()) { | |
img = "<div class=\"folder\"> </div>"; | |
fileSize = ""; | |
} else { | |
img = "<div class=\"file\"> </div>"; | |
} | |
// Datei in die Tabelle einfügen | |
content += "<tr><td class=\"center\">" + img + "</td>" + | |
"<td><a href=\"" + path.replace(" ", "%20") + "/" + filename.replace(" ", "%20") + "\">" + filename + "</a></td>" + | |
"<td>" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(myFile.lastModified()) + "</td>" + | |
"<td class=\"center\">" + fileSize + "</td></tr>"; | |
} | |
if (path.equals("")) path = "/"; // Rück-Anpassung für spätere Verwendung | |
// Tabelle schließen und mit Template zusammenfügen | |
content += "</table>"; | |
String output = WebResources.getDirectoryTemplate("Index of " + path, content); | |
// Abschließenden Slash an Verzeichnis anhängen | |
if (!wantedFile.endsWith("/")) wantedFile += "/"; | |
// Header und Inhalt senden | |
sendHeader(out, 200, "OK", "text/html", -1, System.currentTimeMillis()); | |
Logger.access(wantedFile, socket.getInetAddress().toString()); | |
try { | |
out.write(output.getBytes()); | |
} catch (IOException e) { | |
Logger.exception(e.getMessage()); | |
} | |
} else { | |
// Eine einzelne Datei wurde angefordert: Ausgabe via InputStream | |
// InputStream vorbereiten | |
InputStream reader = null; | |
try { | |
reader = new BufferedInputStream(new FileInputStream(file)); | |
} catch (FileNotFoundException e) { | |
Logger.exception(e.getMessage()); | |
} | |
// Datei existiert (erstaunlicherweise) nicht (mehr) | |
if (reader == null) { | |
sendError(out, 404, "Not Found"); | |
Logger.error(404, wantedFile, socket.getInetAddress().toString()); | |
return; | |
} | |
// Falls es keinen festgelegten ContentType zur Dateiendung gibt, wird der Download gestartet | |
String contentType = FileManager.getContentType(file); | |
if (contentType == null) { | |
contentType = "application/octet-stream"; | |
} | |
// Header senden, Zugriff loggen und Datei senden | |
sendHeader(out, 200, "OK", contentType, file.length(), file.lastModified()); | |
Logger.access(wantedFile, socket.getInetAddress().toString()); | |
try { | |
byte[] buffer = new byte[4096]; | |
int bytesRead; | |
while ((bytesRead = reader.read(buffer)) != -1) { | |
out.write(buffer, 0, bytesRead); | |
} | |
reader.close(); | |
} catch (NullPointerException | IOException e) { | |
// Wirft eine "Broken Pipe" oder "Socket Write Error" Exception, | |
// wenn der Download / Stream abgebrochen wird | |
Logger.exception(e.getMessage()); | |
} | |
} | |
// OutputStream leeren und schließen | |
try { | |
out.flush(); | |
out.close(); | |
} catch (IOException e) { | |
Logger.exception(e.getMessage()); | |
} | |
} | |
/** | |
* Sende den HTTP 1.1 Header zum Client | |
* | |
* @param out Genutzter OutputStream | |
* @param code Status-Code, der gesendet werden soll | |
* @param codeMessage Zum Status-Code gehörende Nachricht | |
* @param contentType ContentType des Inhalts | |
* @param contentLength Größe des Inhalts | |
* @param lastModified Wann die Datei zuletzt verändert wurde (zum Caching des Browsers) | |
*/ | |
private void sendHeader(BufferedOutputStream out, int code, String codeMessage, String contentType, long contentLength, long lastModified) { | |
try { | |
out.write(("HTTP/1.1 " + code + " " + codeMessage + "\r\n" + | |
"Date: " + new Date().toString() + "\r\n" + | |
"Server: Marvins HTTP-Server\r\n" + | |
"Content-Type: " + contentType + "; charset=utf-8\r\n" + | |
((contentLength != -1) ? "Content-Length: " + contentLength + "\r\n" : "") + | |
"Last-modified: " + new Date(lastModified).toString() + "\r\n" + | |
"\r\n").getBytes()); | |
} catch (IOException e) { | |
Logger.exception(e.getMessage()); | |
} | |
} | |
/** | |
* Sendet eine Fehlerseite zum Browser | |
* | |
* @param out Genutzter OutputStream | |
* @param code Fehler-Code, der gesendet werden soll (403, 404, ...) | |
* @param message Zusätzlicher Text ("Not Found", ...) | |
*/ | |
private void sendError(BufferedOutputStream out, int code, String message) { | |
// Bereitet Daten der Response vor | |
String output = WebResources.getErrorTemplate("Error " + code + ": " + message); | |
// Sendet Header der Response | |
sendHeader(out, code, message, "text/html", output.length(), System.currentTimeMillis()); | |
try { | |
// Sendet Daten der Response | |
out.write(output.getBytes()); | |
out.flush(); | |
out.close(); | |
// Schließt den Socket; "keep-alive" wird also ignoriert | |
socket.close(); | |
} catch (IOException e) { | |
Logger.exception(e.getMessage()); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package de.menzerath.util; | |
import java.io.File; | |
import java.io.FileOutputStream; | |
import java.io.IOException; | |
import java.io.PrintWriter; | |
import java.text.SimpleDateFormat; | |
public class Logger { | |
private static File logfile; | |
/** | |
* Ändert die Datei, in der alle Log-Meldungen gespeichert werden | |
* | |
* @param pLogfile Neue Log-Datei | |
*/ | |
public static void setLogfile(File pLogfile) { | |
logfile = pLogfile; | |
} | |
/** | |
* Zugriff auf eine Datei / ein Verzeichnis | |
* | |
* @param file Datei auf die zugegriffen wurde | |
* @param ip IP-Adresse des Clients | |
*/ | |
public static void access(String file, String ip) { | |
write("[200] [" + ip.replace("/", "") + "] " + file); | |
} | |
/** | |
* Fehler beim Zugriff (403, 404, ...) | |
* | |
* @param code HTTP-Status-Code | |
* @param file Datei auf die zugegriffen werden sollte | |
* @param ip IP-Adresse des Clients | |
*/ | |
public static void error(int code, String file, String ip) { | |
write("[" + code + "] [" + ip.replace("/", "") + "] " + file); | |
} | |
/** | |
* Interner Fehler (Abbruch eines Streams, ...) | |
* | |
* @param message Inhalt / Grund der Exception | |
*/ | |
public static void exception(String message) { | |
write("[EXC] " + message); | |
} | |
/** | |
* Gibt die Log-Meldung auf der Konsole aus und speichert sie in der Log-Datei | |
* | |
* @param message Inhalt der Meldung | |
*/ | |
private static void write(String message) { | |
// Zusammensetzung der Meldung | |
String out = "[" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(System.currentTimeMillis()) + "] " + message; | |
// Ausgabe auf der Konsole | |
System.out.println(out); | |
// Schreibe in Datei | |
try { | |
PrintWriter printWriter = new PrintWriter(new FileOutputStream(logfile, true)); | |
printWriter.append(out).append("\r\n"); | |
printWriter.close(); | |
} catch (IOException ignored) {} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package de.menzerath.util; | |
import java.io.File; | |
import java.io.IOException; | |
import java.net.Inet4Address; | |
import java.net.UnknownHostException; | |
public class ServerHelper { | |
/** | |
* Gibt die IP-Adresse des Servers im lokalen Netzwerk zurück | |
* @return IP-Adresse des Servers | |
*/ | |
public static String getServerIp() { | |
try { | |
return Inet4Address.getLocalHost().getHostAddress(); | |
} catch (UnknownHostException e) { | |
Logger.exception(e.getMessage()); | |
return "127.0.0.1"; | |
} | |
} | |
/** | |
* Gibt den "anerkannten" Webroot des Servers zurück; dh. einen validen und absoluten Pfad zum Ordner | |
* @return Pfad zum Webroot | |
*/ | |
public static String getCanonicalWebRoot(File webRoot) { | |
String canonicalWebRoot = ""; | |
try { | |
canonicalWebRoot = webRoot.getCanonicalPath(); | |
} catch (IOException e) { | |
Logger.exception(e.getMessage()); | |
} | |
return canonicalWebRoot; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package de.menzerath.httpserver; | |
/** | |
* Diese Klasse beinhaltet Ressourcen, die bei der Dateiauflistung und | |
* bei Fehlerseien angezeigt werden: | |
* CSS, Header, Footer | |
* | |
* Icons (base64-encodiert) in der Auflistung der Dateien: erstellt von Yannick Lung (yanlu.de) | |
*/ | |
public class WebResources { | |
private static final String STYLE = | |
"html * { font-family: sans-serif;!important; }" + | |
"table { border-collapse: collapse; margin-bottom: 1em; }" + | |
"th { background: #70a0b2; color: #fff; }" + | |
"td, tbody th { border: 1px solid #e1e1e1; font-size: 15px; padding: .5em .3em; }" + | |
"tr:hover td { background: #e9edf1 }" + | |
"td.center { text-align: center; }" + | |
"div.folder { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAAeUlEQVQ4je3UMQqDUAyA4e8VBw/RS/QKHbxPr+HiJUrv0Cu5OBREEKyLg/g6iD6hgz8EkhB+Qobw74RFfUex6LUo0a0RZrP8igfyH3MfVOt2PIAXenx3Ro9nwCC+5VaGMNmTcUkpO4WncIewSeirA27il7WVdyLPgYxpHR5nWoKFpAAAAABJRU5ErkJggg==); background-position: center center; background-repeat:no-repeat; height:20px; width:20px; }" + | |
"div.file { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAA3klEQVQ4je3TvUoDQRSG4cdgIekEQfACbC0s05pC8Dr2EqxTWlta5AZs7BMk9qb0BmxNEdjGxp/CWZgMM7tZSJHCDw4cds687MzLsOMcRH0VqisPeMYaq3TxMOrPcBw2lFKFuVPc4wbfJSDUWLYA66i/xi3uSsMT/GxRE4xC/4VxDBkk0IW/ey3VIpkfYIqT5kN65Eu8lo6Ac7zgLfmzYQn4gccWYIUrHGXW3nPAbaSMQsX5xDwHvMCsBdiZvlI6RfWVkksjKgvskpLLxnPtKyWX+PXsTMpT0/xL2UMpv3j7OZHPExvvAAAAAElFTkSuQmCC); background-position: center center; background-repeat:no-repeat; height:20px; width:20px; }" + | |
"div.back { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAA5ElEQVQ4je3UsUpDQRCF4Y+YQAqDIIgQIYVdCFZaikVAiI29teQJItha+gB2eQMLwUZEfAJLWyEWViGNWAQCYlJchWW5JnchnR4YWPbM/Mzsssu/IlVTC0pzvHOcLQNYQR+XKKcC44IN3GA/FfSjlWC9jQfsBnufqGMPDaxiiK9F4AOMMC0QH7jG4W+wU0wKwuK4w1YIK2EtGj1FR3hCK894X9DNGAO8yc429IbYjKFNvESJ9+jIbj9UBTvo4fk79zZvhHU8BsCLAmPDMV7RzjPLuEoEQg3deQknsue3VCV/Dn9QMzw6REIVdJIPAAAAAElFTkSuQmCC); background-position: center center; background-repeat:no-repeat; height:20px; width:20px; }"; | |
public static String getDirectoryTemplate(String title, String content) { | |
return "<!DOCTYPE html>" + | |
"<html>" + | |
"<head>" + | |
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">" + | |
"<link rel=\"icon\" type=\"image/png\" href=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAAeUlEQVQ4je3UMQqDUAyA4e8VBw/RS/QKHbxPr+HiJUrv0Cu5OBREEKyLg/g6iD6hgz8EkhB+Qobw74RFfUex6LUo0a0RZrP8igfyH3MfVOt2PIAXenx3Ro9nwCC+5VaGMNmTcUkpO4WncIewSeirA27il7WVdyLPgYxpHR5nWoKFpAAAAABJRU5ErkJggg==\" />" + | |
"<style>" + STYLE + "</style>" + | |
"<title>" + title + "</title>" + | |
"</head>" + | |
"<body>" + | |
"<h1>" + title + "</h1>" + | |
content + | |
"</body>" + | |
"</html>"; | |
} | |
public static String getErrorTemplate(String error) { | |
return "<!DOCTYPE html>" + | |
"<html>" + | |
"<head>" + | |
"<title>" + error + "</title>" + | |
"</head>" + | |
"<body>" + | |
"<h1>" + error + "</h1>" + | |
"</body>" + | |
"</html>"; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment