Neon ist ein ultrakompakter, modular aufgebauter HTTP-Server auf der Grundlage des Java-Moduls jdk.httpserver
zum Einbetten in Apps und Microservices. In diesem Dokument sind Module zur Erweiterung von Neon beschrieben.
Module
Die folgenden Module sind in diesem Dokument beschrieben.
-
http-base - Dateien und Streams ausliefern
-
http-realm - Nutzerverzeichnis zur Authentifizierung
-
http-oauth - Bearer Authentication für Neon
-
http-up - Dateien zu Neon heraufladen
-
http-adoc - Asciidoctor mit Neon transformieren
-
http-image - Bilder mit Neon verwenden
-
http-cm - Dateien mit Neon verwalten
-
http-template - Mustache-Vorlagen mit Neon rendern
Für jedes der obigen Module bildet ein Java-Archiv eine Klassenbibliothek (.jar
-Datei), die zur Nutzung einfach dem Classpath einer App oder eines Microservice hinzugefügt wird. Nachfolgend sind die Funktionen der Module ausführlicher beschrieben.
http-base
http-base
stellt die zentrale Funktion bereit, die auf der Grundlage des Java-Modula jdk.httpserver
einen Server für statische Dateien bildet. Das folgende Beispiel zeigt, wie das Modul eingebunden wird.
FileHandler
aus http-base
// einen Server auf Port 9191 erzeugen
HttpServer server = HttpServer.create(new InetSocketAddress(9191, 0);
// einen neuen HttpContext auf dem Endpunkt /meine-app erstellen
// und einen FileHandler daran binden
HttpContext context = server.createContext("/meine-app", new FileHandler());
// den Pfad zu den Daten setzen
context.getAttributes.put(FileHandler.ATTR_FILE_BASE, "/home/fred/www");
// den Server starten
server.setExecutor(Executors.newCachedThreadPool());
server.start();
Mit dem obigen Code wird die Klasse FileHandler
des Moduls http-base
für den Kontext /meine-app
aktiviert. Die App liefert damit Dateien im Ordner /home/fred/www
über den Port 9191
aus.
Wird der obige Code als Teil einer App oder eines Microservice ausgeführt, ist der Inhalt von /home/fred/www
der betreffenden Maschine anschließend über den Uniform Resource Locator (URL) http://localhost:9191/meine-app/
via HTTP zugänglich.
Die Klasse FileHandler
liefert abhängig von den Angaben im HTTP-Header ganze Dateien oder umfangreiche Inhalte als Streams aus.
Welcome Files
Mit dem Attribut FileHander.ATTR_WELCOME_FILES
können dem FileHandler
Dateinamen angegeben werden, die probiert werden sollen, wenn kein Dateiname angegeben ist.
FileHandler
standardmäßig ausgeliefert werden sollen// welcome files angeben
context.getAttributes.put(FileHandler.ATTR_WELCOME_FILES, "index.html,index.htm");
Wird zusätzlich zum vorherigen Code-Beispiel die obige Einstellung gemacht, gibt der Aufruf eines URL wie http://localhost:9191/meine-app/infos/
die Datei /home/fred/www/infos/index.html
oder /home/fred/www/infos/index.htm
aus, sofern eine der Dateien an diesem Ort abgelegt ist.
Helfer
Neben der Hauptfunktion zum Ausliefern statischer Inhalte enthält das Modul http-base
noch Helfer zur Unterstützung häufiger Aufgaben.
- PatternDelegator
-
Mit der Klasse
PatternDelegator
könnenHttpHandler
an Muster geknüpft werden, die aus regulären Ausdrücken gebildet werden. So lassen sich beispielsweise alle Ressourcen mit einer bestimmten Dateiendung oder gewissen Namensbestandteilen an einen Handler binden. Ist ein Handler zusätzlich vonNeonHandler
abgeleitet, können dem Handler eigene Attribute mitgegeben werden. Auf diese Weise können verschiedene Instanzen derselben Handler-Klasse mit unterschiedlichen Parametern genutzt werden, ohne, dass die Attribute sich mit denen eines HttpContext überschneiden. - HandlerDescriptor
-
Die Klasse
HandlerDescriptor
erlaubt im Zusammenspiel mit einemPatternDelegator
die Deklaration einer Handler-Klasse und ihrer Attribute.
Mit dem Gespann aus HandlerDescriptor
und PatternDelegator
lassen sich leichtgewichtigere Server implementieren, da die Klasse PatternDescriptor
einen Handler jeweils erst beim HTTP-Aufruf instanziiert. Zudem können zur Laufzeit dynamisch unterschiedliche Klassen eingesetzt werden ohne den Code neu kompilieren zu müssen. Handler-Klassen müssen zu diesem Zweck einen Konstruktor ohne Parameter besitzen.
http-realm
Sollen Inhalte nur bestimmten Nutzern zugänglich sein, muss eine Authentifizierung feststellen, welcher Nutzer eine Anwendung bedient. Die Angaben des Nutzers für die dafür notwendige Authentisierung prüft die Anwendung gegen ein Nutzerverzeichnis und eine einfache Variante ist im Modul http-realm
implementiert.
Die Schnittstelle Realm
muss von Anwendungen implementiert werden, wenn eine Authentifizierung gegen ein Nutzerverzeichnis im Sinne des Moduls http-realm
verwendet werden soll. Die Klasse SimpleRealm
implementiert beispielhaft einen Realm auf der Grundlage einer Datei.
SimpleRealm
SimpleRealm realm = new SimpleRealm();
realm.readFromFile(new File("conf", my.realm));
realm.setName("/meine-app");
Für die Datei my.realm
erwartet die Klasse SimpleRealm
Einträge in folgender Struktur.
SimpleRealm
# Benutzer meiner App
# [userId]=[password],[roleId],[roleId],etc.
test=test,testRolle
ulrich=geheim,testRolle,andereRolle
fred=undisclosed,nutzerRolle,adminRolle
Der Ausdruck vor dem Gleichheitszeichen ist die Nutzer-Kennung, die zur Anmeldung dient. Der erste Eintrag nach dem Gleichheitszeichen ist das Kennwort, gefolgt von einer Liste mit Komma getrennter Rollen-IDs. http-realm
liefert damit zugleich die Ausgangsbasis für andere Implementierungen wie beispielsweise gegen eine Datenbank oder einen Web-Service.
Erweiterungen oder Abwandlungen der Klasse SimpleRealm
sollten auch eine Form der Verschlüsselung für das Kennwort einbauen, damit es nicht im Klartext für andere lesbar in der Datei vorliegt.
http-oauth
Mit http-oauth
wird Neon um eine Möglichkeit der Authentifizierung nach RFC 6750 Bearer Token Authentication erweitert. Dieses Modul basiert auf JJWT sowie Gson und erfordert neben der Datei http-oauth.jar
die Dateien jjwt-api.jar
, jjwt-impl.jar
, jjwt-gson.jar
sowie gson.jar
und ferner die Module http-base
und http-realm
.
BearerAuthenticator
Zum Einschalten der Authentifizierung für einen bestimmten HTTP-Service-Endpunkt kann die Klasse BearerAuthenticator
mit folgender Vorgehensweise dienen.
// einen Authenticator gegen unser Nutzerverzeichnis erstellen
BearerAuthenticator auth = new BearerAuthenticator();
auth.setRealm(realm);
auth.setExpireSeconds(7200); // 2 Stunden
auth.setPrincipalAuthRealm("/meine-app");
auth.setWWWAuthRealm("/meine-app");
auth.setRefreshExpireSeconds(86400); // 1 Tag
auth.setRefreshSeconds(3600); // 1 Stunde bis nach dem Refresh gefragt wird
context = server.createContext("/meine-app/admin", new Dateiablage("/home/fred/daten", "adminRolle"));
// der Authenticator stellt sicher, dass Nutzer sich authentifizieren
context.setAuthenticator(auth);
Im obigen Code ist die Klasse Dateiablage
ein Beispiel, das symbolisiert, dass hier eine Funktion verwendet wird, die an eine Rolle geknüpft ist. Diese Rolle muss ein Benutzer haben, wenn die Funktion genutzt werden soll. Die Überprüfung der Rolle ist nicht Teil des Beispiels, erfordert aber, dass Nutzer sich authentifizieren.
BearerLoginHandler
Ist eine Ressource mit der Klasse BearerAuthenticator
verbunden, wird gemäß Spezifikation eine Antwort mit Status 401 Unauthorized
gesendet, wenn eine HTTP-Anfrage ohne Authentifizerung für diese Ressource eingeht.
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="example"
Der Client muss dann einen HTTP-Service-Endpunkt aufrufen, der eine Authentifizierung ermöglicht. Ein solcher Endpunkt zum Login kann mit der Klasse BearerLoginHandler
wie in folgendem Beispiel implementiert werden.
// einen HttpContext für den login erstellen
HttpContext context = server.createContext("/meine-app/login", new BearerLoginHandler());
// den Authenticator fuer das Modul http-oauth bekannt machen
context.getAttributes().put(BearerLoginHandler.ATTR_AUTHENTICATOR, auth);
Über den Kontext /meine-app/login
kann eine Benutzeranmeldung erfolgen. Der BearerLoginHandler
erwartet den Aufruf per HTTP POST
mit {"name": "fred", "password": "secret"}
im Body der Anfrage. Zudem wird dem Kontext über das Attribut BearerLoginHandler.ATTR_AUTHENTICATOR
mitgegeben, welcher Authenticator beim Login verwendet werden soll.
Der Login Handler überprüft Nutzerangaben gegen den Realm des Authenticators und gibt bei erfolgreicher Authentifizierung den Authentifizerungs-Token und den Refresh-Token zurück. Diese benötigt der Client zur Angabe der Authentifizierung im Header von Anfragen an Ressourcen, die eine Authentifizierung erfordern.
Die Authentifizierungsangaben werden mit Hilfe der Klasse LoginResponse
im spezifikationsgemäßen Format als Ausdruck in JavaScript Object Notation (JSON) zurückgegeben.
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"mF_9.B5f-4.1JqM",
"token_type":"Bearer",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA"
}
BearerRefreshHandler
Ist ein Authentifizierungs-Token abgelaufen, erhält der Client bei Anfrage einer Ressource, die eine Authentifizierung erfordert, eine Ablehnung.
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="example",
error="invalid_token",
error_description="The access token expired"
Bei der Bearer Authentication muss der Client den Refresh Token aus der letzten Authentifizierung verwenden, um die Authentifizierung zu erneuern. Mit der Klasse BearerRefreshHandler
liefert http-oauth
das Mittel zur Bildung eines Service-Endpunkts, der einen Token erneuert.
// einen HttpContext fuer den token refresh erzeugen
HttpContext context = server.createContext("/meine-app/refresh", new BearerRefreshHandler());
// den Authenticator fuer das Modul http-oauth bekannt machen
context.getAttributes().put(BearerLoginHandler.ATTR_AUTHENTICATOR, auth);
Der Instanz von BearerRefreshHandler
wird wie beim Login Handler wieder der Authenticator mitgegeben, mit dessen Hilfe Refresh Tokens geprüft werden. Mit Aufruf des Endpunkts /meine-app/refresh
erwartet der BearerRefreshHandler
eine HTTP-Anfrage im spezifikationsgemäßen Format wie folgt:
POST /meine-app/refresh HTTP/1.1
Host: example.com
grant_type=refresh_token
&refresh_token=xxxxxxxxxxx
&client_id=xxxxxxxxxx
&client_secret=xxxxxxxxxx
Ist der Refresh Token gültig, antwortet der BearerRefreshHandler
so wie beim Login mit einem neuen Satz Tokens.
Zusammenfassung
Mit dem Modul http-oauth
kann eine App oder ein Microservice den Bearer Authentication Mechanismus für Ressourcen verwenden, wenn diese nur an berechtigte Nutzer ausgegeben werden sollen.
-
Zum Einschalten der Authentifizierung wird ein Service-Endpunkt mit einem Objekt der Klasse
BearerAuthenticator
verbunden. -
Mit einem Objekt der Klasse
BearerLoginHandler
wird ein Service-Endpunkt zur Authentifizerung geschaffen. -
Ein Objekt der Klasse
BearerRefreshHandler
erlaubt die Herstellung eines Service-Endpunktes zum Erneuern einer abgelaufenen Authentifizierung.
Damit ist für eine App oder einen Microservice eine Authentifizierung herstellbar, die stärker von etablierten Mechanismen wie Basic, Digest oder Form Based Authentication entkoppelt ist und zudem flexibler in evtl. bestehende andere Authentifizierungsdienste eingebunden werden kann.
Das Kapitel Authentifizierung zeigt ein Beispiel für den Einsatz eines Authenticators mit einem FileManager
. Im Dokument Authentifizierung für Webclients ist zudem beschrieben, wie die Authentifizierung auf der Seite des Clients implementiert werden kann.
http-up
Das Modul http-up
stellt die Klasse MultipartStream
aus dem Projekt Apache Commons mit geringfügigen Anpassungen bereit, mit denen die Klasse ohne weitere Abhängigkeiten eingesetzt werden kann. Das folgende Code-Beispiel zeigt, wie die Klasse auf der Server-Seite z.B. in einem HttpHandler
verwendet werden kann, um zum Server heraufgeladene Dateien entgegenzunehmen.
public static final String ATTR_FILENAME = "filename=";
public static final String TEMP_FILE_NAME = "temp.up";
private String fileBase = "/some/path/to/storage";
public void handle(HttpExchange exchange) throws IOException {
Headers headers = exchange.getRequestHeaders();
String ct = headers.getFirst(HttpHelper.CONTENT_TYPE);
String[] parts = ct.split("=");
String boundary = parts[1];
InputStream is = exchange.getRequestBody();
MultipartStream multipartStream = new MultipartStream(is, boundary.getBytes(), 4096, null);
File file = new File(fileBase, TEMP_FILE_NAME);
try {
String value = "";
boolean nextPart = multipartStream.skipPreamble();
while (nextPart) {
String header = multipartStream.readHeaders();
if(header.contains(ATTR_FILENAME)) {
// process file content
// perhaps insert some file name processing here too
// so that more than one uploaded files do not overwrite
// each other
OutputStream os = new FileOutputStream(file);
multipartStream.readBodyData(os);
} else {
// read value
ByteArrayOutputStream os = new ByteArrayOutputStream();
multipartStream.readBodyData(os);
value = os.toString().substring(fileContext.length());
}
nextPart = multipartStream.readBoundary();
}
} catch (MultipartStream.MalformedStreamException e) {
// the stream failed to follow required syntax
} catch (IOException e) {
// a read or write error occurred
}
// processing of files 'temp.up', 'temp-2.up', 'temp-3.up' etc.
// is required here, e.g. rename, move ..
}
Die Methode handle
im obigen Beispiel liest mit Hilfe der Klasse MultipartStream
eine HTTP-Anfrage mit Content-Type multipart/form-data
, wie sie bei HTTP-Uploads an den Server gesendet wird.
http-adoc
Mit http-adoc
wird Neon um die Möglichkeit erweitert, Dokumente, die im Asciidoctor-Format geschrieben sind, in HTML- oder PDF-Dokumente zu verwandeln. Neben der Datei http-adoc.jar
sind die Java-Archive aus dem Paket AsciidoctorJ erforderlich. Die Umwandlung geschieht 'on-demand', es muss kein besonderer Build-Prozess hergestellt werden. Die Aktivierung des AdocFilter
für eine Ressource wie nachfolgend beschrieben genügt.
// einen Descriptor fuer den AdocHandler erstellen
HandlerDescriptor hd = new HandlerDescriptor();
hd.setHandlerClassName("de.uhilger.httpserver.adoc.AdocHandler");
// einen regulären Ausdruck fuer das Muster *.adoc erstellen
// und den AdocHandler daran binden
PatternDelegator handler = new PatternDelegator();
handler.addHandler(".+\\.adoc", hd);
// einen neuen HttpContext erzeugen
HttpContext context = server.createContext("/meine-app/dok", handler);
// den AdocFilter fuer den neuen HttpContext aktivieren
context.getFilters().add(new AdocFilter());
// den Pfad zu den Daten setzen
context.getAttributes.put(FileHandler.ATTR_FILE_BASE, "/home/fred/www-daten");
Im obigen Code-Beispiel werden alle Inhalte des Ordners /home/fred/www-daten
über den Service-Endpunkt /meine-app/dok
via HTTP ausgegeben. Zudem wird der AdocFilter
für alle über /meine-app/dok
zugängliche Inhalte eingeschaltet.
Beispiel
Beim Aufruf der Ressource http://localhost:9191/meine-app/dok/hilfe/anleitung.adoc
wird mit obiger Konfiguration die Datei /home/fred/www-daten/hilfe/anleitung.adoc
nötigenfalls nach HTML transformiert, als anleitung.html
am selben Ort gespeichert und diese HTML-Datei ausgeliefert.
Bleibt die Quelldatei anleitung.adoc
unverändert, wird bei weiteren Aufrufen der Ressource die HTML-Datei unverändert ausgeliefert. Nach Änderungen an der Quelldatei erfolgt vor der ersten Auslieferung zunächst automatisch wieder eine Transformation.
Klassen
- AdocFilter
-
Der Filter prüft für jede HTTP-Anfrage, ob der Name der gewünschten Ressource mit
.adoc
endet. Wenn ja, wird geprüft, ob bereits eine HTML-Version der Datei existiert. Wenn nicht oder wenn die HTML-Version älter ist als die letzte Änderung der.adoc
-Datei veranlasst derAdocFilter
denAdocActor
eine neue HTML-Version zu erzeugen. - AdocHandler
-
Die Klasse
AdocHandler
dient zum 'Einklinken' der Funktion in die HttpHandler-Logik des Modulsjdk.httpserver
. DerAdocHandler
leitet Anfragen an Ressourcen mit Endung.adoc
um auf die Auslieferung ihrer HTML-Version. - AdocActor
-
Die eigentliche Transformation erledigt die Klasse
AdocActor
, die dafür die Funktionen von AsciidoctorJ nutzt. DerAdocActor
kann so losgelöst von Neon oder den Strukturen vonjdk.httpserver
auch für andere Programme verwendet werden, wenn sie eine solche Transformation benötigen.
Übrigens: Die Klassen HandlerDescriptor
, PatternDelegator
und FileHandler
sind im Modul http-base
enthalten, das für die allermeisten Verwendungen von Neon vermutlich ohnehin immer eingebunden wird.
http-image
Das Modul http-image
erzeugt verkleinerte Fassungen eines Originalbildes 'on demand'. Hierzu wird die Klasse ImageFilter
einem HttpContext
hinzugefügt. Neben http-image.jar
sind die Klassenbibliotheken http-base sowie Thumbnailator im Classpath erforderlich.
// einen HttpContext mit einem FileHandler erzeugen
HttpContext context = server.createContext("/meine-app/dateien", new FileHandler());
// den Ablageort von Inhalten angeben
context.getAttributes().put(FileHandler.ATTR_FILE_BASE, "/home/fred/www-daten"));
// den ImageFilter für den Context aktivieren
context.getFilters().add(new ImageFilter());
Damit wird automatisch für alle Dateien, die mit jpg
, jpeg
oder png
enden, der ImageFilter aktiv. Liegt unter /home/fred/www-daten
beispielsweise die Bilddatei mein-bild.jpg
, wird mit Aufruf von http://example.com/meine-app/dateien/mein-bild_tn.jpg
eine verkleinerte Fassung mit 120 Bildpunkten erzeugt. Die folgenden Bildgrößen werden dabei verarbeitet
Bildpunkte | Dateiname |
---|---|
120 |
|
240 |
|
500 |
|
700 |
|
1200 |
|
Desweiteren bewirkt der Zusatz _b64
die Erzeugung einer Base64-kodierten Fassung, die für Grafiken mit Data-URI dienen kann. Der Zusatz _b64
wird mit den obigen Namenszusätzen kombiniert indem er ihnen angehängt wird. Der Dateiname mein-bild_tn_b64.jpg
erzeugt eine Base64-kodierte und auf 120 Bildpunkte verkleinerte Fassung des Originalbildes namens mein-bild.jpg
.
http-cm
Das Modul http-cm
etabliert Verwaltungsfunktionen für den Inhalt eines Ordners im Dateisystem. Es erfordert neben der Datei http-cm.jar
die Dateien http-base.jar
, http-image.jar
, thumbnailator.jar
sowie gson.jar
.
Die Klasse FileManager
wird wie folgt eingeschaltet.
FileManager
aus dem Modul http-cm
// einen HttpContext mit einem FileManager erzeugen
HttpContext context = server.createContext("/daten/", new FileManager());
// den Ablageort von Inhalten angeben
context.getAttributes().put(FileHandler.ATTR_FILE_BASE, "/home/fred/www-daten"));
Damit werden alle Inhalte des Ordners /home/fred/www-daten
für die Dateiverwaltung über den Indikator /daten/
zur Bearbeitung zugänglich. Nachfolgend sind die Funktionen der Dateiverwaltung aufgelistet und im Detail beschrieben. Das Kapitel Authentifizierung ergänzt Angaben zum Einschalten der Authentifizierung.
Für die weitere Beschreibung wird angenommen, dass der FileManager
als Teil einer App gestartet wurde, die über den URL http://localhost:9292/
zugänglich ist.
Dateiinhalt lesen
Das Lesen von Dateiinhalten ist vergleichbar mit dem Aufruf eines URL im Webbrowser.
- Beispiel
-
Aufruf via HTTP GET an
http://localhost:9292/daten/texte/test.txt
Für einen URL, der nicht mit einem Schrägstrich endet ist die Annahme, dass eine Datei gemeint ist und der Inhalt der betreffenden Datei wird ausgeliefert. Der Inhalt der Datei /home/fred/www-daten/texte/test.txt
wird in der Antwort zurückgegeben.
Dateien auflisten
Für den Aufruf eines URL, der einen Ordner bezeichnet, wird der Ordnerinhalt aufgelistet.
- Beispiel
-
Aufruf via HTTP GET an
http://localhost:9292/daten/pg/neon/
Wenn der URL mit einem Schrägstrich endet, wird der Inhalt des betreffenden Ordners als Dateiliste im JSON-Format geliefert. Mit obigem Aufruf wird der Inhalt des Ordners /home/fred/www-daten/pg/neon/
ausgegeben.
{
"pfad": "/pg/neon/",
"dateien": [{
"name": "anleitung.adoc",
"typ": "datei",
"typKlasse": "icon-doc-inv",
"bild": false
}, {
"name": "anleitung.html",
"typ": "datei",
"typKlasse": "icon-doc-inv",
"bild": false
}, {
"name": "index.htmi",
"typ": "datei",
"typKlasse": "icon-doc-inv",
"bild": false
}, {
"name": "module",
"typ": "ordner",
"typKlasse": "icon-folder",
"bild": false
}]
}
Bilder
Für Dateien mit den Endungen jpg
, jpeg
oder png
erscheinen nur die Originaldateien in der Dateiliste. Varianten, wie sie mit dem Modul http-image entstehen, werden ausgeblendet. Ist etwa in einem Ordner eine Datei namens mein-bild.jpg
abgelegt und es wurde am selben Ort eine kleinere Fassung namens mein-bild_kl.jpg
gespeichert, erscheint nur der Eintrag für die Datei mein-bild.jpg
in der Dateiliste.
Datei schreiben (PUT)
Aufruf von HTTP PUT fuer eine Datei ueberschreibt eine bestehende Datei mit dem im Body der HTTP-Nachricht uebergebenen Inhalt oder legt eine Datei mit diesem Inhalt an.
- Beispiel
-
Aufruf via HTTP PUT an
http://localhost:9292/daten/texte/test.txt
Der Body des HTTP PUT Aufrufs wird in die angegebene Datei geschrieben. Mit obigem Aufruf wird der Inhalt im Body des Aufrufs in die Datei /home/fred/www-daten/texte/test.txt
geschrieben. Wenn die Datei existiert, wird sie überschrieben, sonst wird sie mit diesem Inhalt angelegt.
Datei schreiben (POST)
Aufruf von HTTP POST fuer eine Datei legt eine neue Datei mit dem im Body uebergebenen Inhalt an oder erzeugt eine neue Datei mit einer laufenden Nummer, falls diese Datei schon existiert.
- Beispiel
-
Aufruf via HTTP POST an
http://localhost:9292/daten/texte/test.txt
Der Body des HTTP POST Aufrufs wird in die unter dem angegebenen Namen neu angelegte Datei geschrieben. Mit obigem Aufruf wird die Datei /home/fred/www-daten/texte/test.txt
neu angelegt und der Inhalt im Body des Aufrufs in die Datei geschrieben. Wenn die Datei schon existiert, wird eine neue Datei angelegt, deren Name eine Nummer angehängt wird, z.B. test-1.txt
, und besagter Inhalt geschrieben.
Die Antwort liefert den Namen der Datei, die geschrieben wurde.
Ordner anlegen
Aufruf von HTTP POST fuer einen Ordner legt einen neuen Ordner an wenn er noch nicht existiert oder erzeugt einen HTTP-Fehler 422.
- Beispiel
-
Aufruf via HTTP POST an
http://localhost:9292/daten/pg/neon/dok/
Der Ordner dok
wird im Ordner /home/fred/www-daten/pg/neon/
angelegt, wenn er noch nicht existiert. Wenn es schon einen Ordner dok
am angegebenen Ort gibt, wird der HTTP-Fehler 422 Unprocessable Entity zurückgegeben.
Löschen
HTTP DELETE loescht die Liste der Dateien und Ordner im Body.
- Beispiel
-
Aufruf von HTTP DELETE an
http://localhost:9292/daten/pg/neon/
["test.txt","dok"]
Die Datei test.txt
und der Ordner dok
werden aus dem Ordner /home/fred/www-daten/pg/neon/
gelöscht. Das Löschen geschieht rekursiv, es werden also auch alle Inhalte von dok
gelöscht.
Vorsicht: Es gibt keine Sicherungsanfrage vorab, der Client muss selbst für geeignete Sicherheitsfunktionen sorgen, die dem Löschen vorgeschaltet werden.
Bilder
Beim Löschen von Dateien mit den Endungen jpg
, jpeg
oder png
werden alle Dateien gelöscht, auch Varianten, wie sie mit dem Modul http-image entstehen. Ist etwa in einem Ordner eine Datei namens mein-bild.jpg
abgelegt und es wurde am selben Ort eine kleinere Fassung namens mein-bild_kl.jpg
gespeichert, werden beide Fassungen gelöscht.
Kopieren
HTTP PUT mit dem Parameter ?copyFrom=pfad
kopiert die Liste der Datei- oder Ordnernamen im Body der Anfrage vom Pfad in copyFrom
zum Pfad dieser Anfrage.
- Beispiel
-
Aufruf von HTTP PUT an
http://localhost:9292/daten/pg/neon/?copyFrom=/entwurf/texte/
["anleitung.adoc","dok","ordner-2","bild.jpg"]
Die Dateien und Ordner aus der Liste im Body der Anfrage werden vom Ordner /home/fred/www-daten/entwurf/texte
in den Ordner /home/fred/www-daten/pg/neon
kopiert. Das Kopieren erfolgt rekursiv, d.h. alle Inhalte von Ordnern werden mit verschoben.
Jede Datei, die im Ziel bereits existiert, bekommt im Ziel einen neuen Namen mit einer laufenden Nummer. Bei Ordnern, die im Ziel bereits existieren, bekommt der betreffende Ordner im Ziel zunaechst einen neuen Namen mit einer laufenden Nummer, dann wird der Quellordner ans Ziel kopiert.
Die Angabe des Quellpfades in copyFrom
bezieht sich auf einen Pfad relativ zum Pfad, mit dem der FileManager
eingeschaltet wurde.
Bilder
Beim Kopieren von Dateien mit den Endungen jpg
, jpeg
oder png
werden alle Dateien kopiert, auch Varianten, wie sie mit dem Modul http-image entstehen. Ist etwa in einem Ordner eine Datei namens mein-bild.jpg
abgelegt und es wurde am selben Ort eine kleinere Fassung namens mein-bild_kl.jpg
gespeichert, werden beide Fassungen kopiert.
Verschieben
HTTP PUT mit dem Parameter ?moveFrom=pfad
verschiebt die Liste der Datei- oder Ordnernamen im Body der Anfrage vom Pfad in moveFrom
zum Pfad dieser Anfrage.
- Beispiel
-
Aufruf von HTTP PUT an
http://localhost:9292/daten/pg/neon/?moveFrom=/entwurf/texte/
["anleitung.adoc","dok","ordner-2","bild.jpg"]
Die Dateien und Ordner aus der Liste im Body der Anfrage werden vom Ordner /home/fred/www-daten/entwurf/texte
in den Ordner /home/fred/www-daten/pg/neon
verschoben. Das Verschieben erfolgt rekursiv, d.h. alle Inhalte von Ordnern werden mit verschoben.
Jede Datei, die im Ziel bereits existiert, bekommt im Ziel einen neuen Namen mit einer laufenden Nummer. Bei Ordnern, die im Ziel bereits existieren, bekommt der betreffende Ordner im Ziel zunaechst einen neuen Namen mit einer laufenden Nummer, dann wird der Quellordner ans Ziel verschoben.
Die Angabe des Quellpfades in moveFrom
bezieht sich auf einen Pfad relativ zum Pfad, mit dem der FileManager
eingeschaltet wurde.
Bilder
Beim Verschieben von Dateien mit den Endungen jpg
, jpeg
oder png
werden alle Dateien verschoben, auch Varianten, wie sie mit dem Modul http-image entstehen. Ist etwa in einem Ordner eine Datei namens mein-bild.jpg
abgelegt und es wurde am selben Ort eine kleinere Fassung namens mein-bild_kl.jpg
gespeichert, werden beide Fassungen verschoben.
Duplizieren
HTTP PUT mit dem Parameter ?duplicate
legt die Kopie einer Datei am gegenwärtigen Ablageort an.
- Beispiel
-
Aufruf von HTTP PUT an
http://localhost:9292/daten/texte/test.txt?duplicate
Im Ordner /home/fred/www-daten/texte
wird ein Duplikat der Datei test.txt
unter dem Namen test-Kopie.txt
angelegt. Wenn es die Datei bereits gibt, wird dem Namen eine laufende Nummer angehängt, z.B. test-Kopie-2.txt
.
Umbenennen
HTTP PUT mit dem Parameter ?renameTo=neuer Name
benennt die Datei oder den Ordner um, sofern der neue Name noch nicht vergeben ist.
- Beispiel
-
Aufruf von HTTP PUT an
http://localhost:9292/daten/texte/test.txt?renameTo=textdatei.txt
Die Datei /home/fred/www-daten/texte/test.txt
erhält den neuen Namen textdatei.txt
, sofern eine Datei solchen Namens in diesem Ordner noch nicht existiert.
Bilder
Beim Umbenennen von Dateien mit den Endungen jpg
, jpeg
oder png
werden alle Dateien umbenannt, auch Varianten, wie sie mit dem Modul http-image entstehen. Ist etwa in einem Ordner eine Datei namens mein-bild.jpg
abgelegt und es wurde am selben Ort eine kleinere Fassung namens mein-bild_kl.jpg
gespeichert, werden beide Fassungen umbenannt.
Packen
HTTP PUT mit dem Parameter ?zip
packt den Ordner.
- Beispiel
-
Aufruf von HTTP PUT an
http://localhost:9292/daten/texte/?zip
Der Inhalt des Ordners /home/fred/www-daten/texte
wird komprimiert und als Datei /home/fred/www-daten/texte.zip
erstellt. Das Packen erfolgt rekursiv, d.h. alle Inhalte des betreffenden Ordners werden ins komprimierte ZIP-Archiv gepackt.
Entpacken
HTTP PUT mit dem Parameter ?unzip
entpackt eine ZIP-Archivdatei.
- Beispiel
-
Aufruf von HTTP PUT an
http://localhost:9292/daten/archive/mein-archiv.zip?unzip
Der Inhalt der ZIP-Archivdatei /home/fred/www-daten/archive/mein-archiv.zip
wird in den Ordner /home/fred/www-daten/archive
entpackt.
Authentifizierung
Funktionen zur Verwaltung von Inhalten sind gewöhnlich nur autorisierten Nutzern zugänglich, was einen Authentifizierungsmechanismus erfordert. Dafür sind die Module http-oauth.jar
, http-realm.jar
, jjwt-api.jar
, jjwt-impl.jar
und jjwt-gson.jar
nötig.
Am Beispiel eines FileManager
wird hier stellvertretend für andere Module gezeigt, wie die Authentifizierung für ein Neon-Modul eingeschaltet wird. Wenn wie im Beispiel zum Einschalten der Bearer Token Authentifizierung ein Authenticator erstellt wurde, kann dieser dem FileManager
aus dem vorangegangenen Beispiel wie folgt beigegeben werden.
// den Authenticator fuer das Modul http-oauth bekannt machen
context.getAttributes().put(BearerLoginHandler.ATTR_AUTHENTICATOR, auth);
// die Rolle festlegen, die autorisierte Benutzer besitzen muessen
context.getAttributes().put(FileManager.ATTR_ROLE, "dateiAdminRolle");
// den Authenticator fuer diesen HttpContext aktivieren
context.setAuthenticator(auth);
Der Authenticator stellt sicher, dass nur angemeldete Benutzer, die die Rolle dateiAdminRolle
besitzen, die Funktionen des FileManager
verwenden.
Im Beispiel oben antwortet der Authenticator auf alle nicht authentifizierten Anfragen an /daten/
mit Login- und Refresh-Aufforderungen gemäß der Spezifikation für Bearer Token Authentication. Als nicht authentifiziert gelten dabei alle Anfragen ohne Token oder mit ungültigem Token.
Clients müssen als Reaktion darauf Login- und Refresh-Aufrufe implementierten. Die Einrichtung der serverseitigen Service-Endpunkte für diese Client-Aufrufe ist beim Modul http-oauth beschrieben. Wie sich diese Authentifizierung auf der Seite des Clients integrieren lässt beschreibt das Dokument Authentifizierung für Webclients.
http-template
Das Modul http-template
erweitert Neon um die Verarbeitung von Vorlagen (Templates) auf der Grundlage von Mustache. Neben http-template.jar
sind die Klassenbibliotheken http-base.jar
sowie compiler.jar
im Classpath erforderlich.
Die Klasse TemplateActor
des Moduls http-template
wird zur Mischung von Daten mit einer Vorlage verwendet. Ihre Methode render
fügt dem HTML aus einer Mustache-Vorlage die Daten hinzu, die ihr als HashMap übergeben werden.
public void handle(HttpExchange exchange) throws IOException {
// die gewuenschte Datei bestimmen
File file = getFile(exchange);
// die Daten fuer die Vorlage bereitstellen
Map data = new HashMap();
data.put("title", file.getName());
data.put("content", getFileContent(file));
// die Daten in die Vorlage eintragen
String html =
new TemplateActor().render(exchange, data, "vorlage.mustache");
byte[] bytes = html.getBytes();
// die Antwort ausgeben
exchange.sendResponseHeaders(FileHandler.SC_OK, bytes.length);
OutputStream os = exchange.getResponseBody();
os.write(bytes, 0, bytes.length);
os.flush();
os.close();
}
public String getFileContent(File file) {
BufferedReader reader = new BufferedReader(new FileReader(file));
StringWriter writer = new StringWriter();
String line = reader.readLine();
while (line != null) {
writer.write(line);
writer.write("\r\n");
line = reader.readLine();
}
reader.close();
return writer.toString();
}
public File getFile(HttpExchange exchange) {
HttpHelper helper = new HttpHelper();
String relativePath = helper.getFileName(exchange);
Map attributes = exchange.getHttpContext().getAttributes();
String fileBase = helper.getAttrStr(attributes,
FileHandler.ATTR_FILE_BASE, App.STR_DOT);
File file;
if(relativePath.endsWith(App.STR_SLASH)) {
file = helper.tryWelcomeFiles(exchange, relativePath);
} else {
file = new File(fileBase, relativePath);
}
return file;
}
Die Angabe der Vorlage erfolgt als einfacher Dateiname in Form eines String. Der TemplateActor
versucht, diese vom Ablageort zu lesen, der im Attribut FileHandler.ATTR_FILE_BASE
für den HttpContext angegeben wurde. Gelingt dies nicht, wird versucht, die Vorlage aus dem Classpath zu lesen. Ist das Lesen der Vorlage erfolgreich, werden die Daten aus der HashMap data
in die Vorlage eingebaut und das Ergebnis ausgegeben. Die HashMap data
wird dazu mit den Angaben beladen, die in der Vorlage als Platzhalter eingetragen sind.
<!DOCTYPE html>
<html>
<head>
<title>{{title}}</title>
</head>
<body>
<div class="inhalt">
{{content}}
</div>
</body>
</html>
Die Logik zur Bestimmung der Vorlage zum jeweiligen Inhalt ist in der Klasse TemplateActor
bewußt einfach gehalten. Sie lässt sich mit einer eigenen Implementierung der Klasse beliebig abwandeln. Zudem beinhaltet die Klassenbibliothek compiler.jar
, die die Java-Implementierung für Mustache bereitstellt, umfangreiche zusätzliche Möglichkeiten der Nutzung von Mustache. Die hier vorgestellte Implementierung stellt nur ein Beispiel und mithin den Ausgangspunkt dar, wie Mustache-Vorlagen aus Neon heraus verwendbar sind.
Änderungshistorie
Version 1
Vom 18. Juni 2021
-
http-base - Dateien und Streams ausliefern
-
http-realm - Nutzerverzeichnis zur Authentifizierung
-
http-oauth - Bearer Authentication für Neon
-
http-up - Dateien zu Neon heraufladen
-
http-adoc - Asciidoctor mit Neon transformieren
-
http-image - Bilder mit Neon verwenden
Version 2
Vom 2. Januar 2022
-
http-cm - Dateien mit Neon verwalten
-
http-template - Mustache-Vorlagen mit Neon verarbeiten