README.md | ●●●●● patch | view | raw | blame | history | |
src/de/uhilger/minsrv/App.java | ●●●●● patch | view | raw | blame | history | |
src/de/uhilger/minsrv/Server.java | ●●●●● patch | view | raw | blame | history | |
src/de/uhilger/minsrv/handler/FileHandler.java | ●●●●● patch | view | raw | blame | history | |
src/de/uhilger/minsrv/handler/StopServerHandler.java | ●●●●● patch | view | raw | blame | history |
README.md
New file @@ -0,0 +1,24 @@ # Mini-Server Ein minimalistischer HTTP-Server auf der Basis der Java-Package [`com.sun.net.httpserver`](https://docs.oracle.com/en/java/javase/11/docs/api/jdk.httpserver/com/sun/net/httpserver/package-summary.html). ## Nutzungsvoraussetzungen Zur Ausführung des Mini-Server wird eine Java-Ablaufumgebung (Java Runtime Environment, JRE) benötigt. Auf der Kommandozeile kann mit dem folgenden Kommando ermittelt werden ob das JRE vorhanden ist. ``` java -version ``` Andernfalls kann es beispielsweise von folgenden Webseiten geladen werden: . [Azul](https://www.azul.com/downloads/zulu-community/) oder . [AdoptOpenJDK](https://adoptopenjdk.net/) ## Nutzung Hier noch beschreiben src/de/uhilger/minsrv/App.java
@@ -14,8 +14,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ */ package de.uhilger.minsrv; import java.io.IOException; @@ -25,76 +24,84 @@ /** * Die Hauptklasse des mini-server * * * @author Ulrich Hilger * @version 0.1, 25.03.2021 */ public class App { private static final Logger logger = Logger.getLogger(App.class.getName()); public static final String IP_PORT = "port"; public static final String IP_WWW_DATA = "www-data"; public static final String IP_CTX = "ctx"; private static HashMap initParams; private static HashMap initParams; /** * Start-Methode dieser Anwendung * * Folgende Kommandozeilenparameter werden verarbeitet * ctx - Kontext des Servers * www-data - lokales Datenverzeichnis * port - Port * * Beispiel: * java -jar mini-server.jar ctx="srv" www-data="/home/fred/www" port=9090 * * Startet den Server auf http://localhost:9090/srv * und liefert Inhalte aus dem Verzeichnis /home/fred/www aus. * * Ein Aufruf von http://localhost:9090/srv/pfad/zum/inhalt/index.html * liefert also die Datei 'index.html' aus dem Ordner * /home/fred/www/pfad/zum/inhalt aus. * * * Folgende Kommandozeilenparameter werden verarbeitet ctx - Kontext des * Servers www-data - lokales Datenverzeichnis port - Port * * Beispiel: java -jar mini-server.jar ctx="srv" www-data="/home/fred/www" * port=9090 * * Startet den Server auf http://localhost:9090/srv und liefert Inhalte aus * dem Verzeichnis /home/fred/www aus. * * Ein Aufruf von http://localhost:9090/srv/pfad/zum/inhalt/index.html liefert * also die Datei 'index.html' aus dem Ordner /home/fred/www/pfad/zum/inhalt * aus. * * @param args Kommandozeilenparameter */ public static void main(String[] args) { initParams = new HashMap(); for(String arg: args) { for (String arg : args) { String[] argParts = arg.split("="); initParams.put(argParts[0], argParts[1]); } Server server = new Server(Integer.parseInt(getInitParameter(IP_PORT))); try { server.setContextName(getInitParameter(IP_CTX)); server.start(); } catch (IOException ex) { Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); String portStr = getInitParameter(IP_PORT); if (portStr != null) { Server server = new Server(Integer.parseInt(portStr)); try { String ctxName = getInitParameter(IP_CTX); if (ctxName != null) { server.setContextName(ctxName); server.start(); } else { logger.severe("Der Parameter " + IP_CTX + " muss angegeben werden."); } } catch (IOException ex) { Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); } } else { logger.severe("Der Parameter " + IP_PORT + " muss angegeben werden."); } } /** * Diese Anwendung stoppen */ public static void stop() { System.exit(0); } /** * Einen Kommandozeilenparameter ermitteln * * * @param pname Names des Parameters * @return Inhalt des Parameters oder null, wenn der Parameter * nicht gefunden wurde * @return Inhalt des Parameters oder null, wenn der Parameter nicht gefunden * wurde */ public static String getInitParameter(String pname) { String param = null; Object o = initParams.get(pname); if(o != null) { if (o != null) { param = o.toString(); } return param; } } } src/de/uhilger/minsrv/Server.java
@@ -37,10 +37,17 @@ private static final Logger logger = Logger.getLogger(Server.class.getName()); public static final String STR_SLASH = "/"; private int port; private String ctxName; private String ctx; /** * Ein neues Objekt der Kalsse Server erzeugen * @param port der Port, über den dieser Server erreichbar sein soll */ public Server(int port) { this.port = port; } @@ -54,8 +61,17 @@ this.port = port; } /** * Den Namen des Kontexts angeben, über den dieser Server * erreichbar sein soll * @param ctxName */ public void setContextName(String ctxName) { this.ctxName = ctxName; if(!ctxName.startsWith(STR_SLASH)) { this.ctx = STR_SLASH + ctxName; } else { this.ctx = ctxName; } } /** @@ -69,8 +85,8 @@ logger.info("Server starting on port " + port); HttpServer server = HttpServer.create(new InetSocketAddress(port), 0); server.createContext(ctxName + "/av", new FileHandler(App.getInitParameter(App.IP_WWW_DATA))); server.createContext(ctxName + "/server/stop", new StopServerHandler()); server.createContext(ctx + "/av", new FileHandler(App.getInitParameter(App.IP_WWW_DATA))); server.createContext(ctx + "/server/stop", new StopServerHandler()); server.setExecutor(Executors.newFixedThreadPool(20)); server.start(); } src/de/uhilger/minsrv/handler/FileHandler.java
@@ -20,6 +20,7 @@ import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import de.uhilger.minsrv.Server; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -67,7 +68,6 @@ public static final String STR_BLANK = " "; public static final String STR_DASH = "-"; public static final String STR_COMMA = ","; public static final String STR_SLASH = "/"; public static final String STR_DOT = "."; public static final String STR_NOT_FOUND = " not found."; public static final String LM_PATTERN = "EEE, dd MMM yyyy HH:mm:ss zzz"; @@ -109,7 +109,7 @@ if (headers.containsKey(RANGE_HEADER)) { serveFileParts(e, new File(basePath, fName)); } else { if (fName.endsWith(STR_SLASH)) { if (fName.endsWith(Server.STR_SLASH)) { fName += WELCOME_FILE; } serveFile(e, new File(basePath, fName)); @@ -206,29 +206,6 @@ } } private String contentRangeHdr(Range range, File file) { StringBuilder sb = new StringBuilder(); sb.append(STR_BYTES); sb.append(STR_BLANK); sb.append(range.getStart()); sb.append(STR_DASH); sb.append(range.getEnd()); sb.append(STR_SLASH); sb.append(file.length()); return sb.toString(); } private void setCommonHeaders(Headers resHeaders, File file) throws IOException { resHeaders.add(ACCEPT_RANGES_HEADER, STR_BYTES); String mimeType = Files.probeContentType(file.toPath()); if (mimeType != null) { resHeaders.add(CONTENT_TYPE, mimeType); } SimpleDateFormat sdf = new SimpleDateFormat(LM_PATTERN); Date date = new Date(file.lastModified()); resHeaders.add(LAST_MODIFIED_DATE_HEADER, sdf.format(date)); } /** * Die Byte-Ranges aus dem Range-Header ermitteln. * @@ -302,6 +279,46 @@ } /** * Einen Content-Range Header erzeugen * * @param range die Range, aus deren Inhalt der Header erzeugt werden soll * @param file die Datei, die den Inhalt liefert, der vom Header * bezeichnet wird * @return der Inhalt des Content-Range Headers */ private String contentRangeHdr(Range range, File file) { StringBuilder sb = new StringBuilder(); sb.append(STR_BYTES); sb.append(STR_BLANK); sb.append(range.getStart()); sb.append(STR_DASH); sb.append(range.getEnd()); sb.append(Server.STR_SLASH); sb.append(file.length()); return sb.toString(); } /** * Die Header erzeugen, die unabhängig davon, ob der ganze * Inhalt oder nur Teile davon ausgeliefert werden sollen, in der * Antwort stehen sollen * * @param resHeaders das Objekt, in das die Header erzeugt werden * @param file die Datei, für die die Header gelten * @throws IOException falls etwas schief geht entsteht dieser Fehler */ private void setCommonHeaders(Headers resHeaders, File file) throws IOException { resHeaders.add(ACCEPT_RANGES_HEADER, STR_BYTES); String mimeType = Files.probeContentType(file.toPath()); if (mimeType != null) { resHeaders.add(CONTENT_TYPE, mimeType); } SimpleDateFormat sdf = new SimpleDateFormat(LM_PATTERN); Date date = new Date(file.lastModified()); resHeaders.add(LAST_MODIFIED_DATE_HEADER, sdf.format(date)); } /** * Eine nicht gefunden Antwort senden * * @param e das Objekt mit Methoden zur Untersuchung der Anfrage sowie zum @@ -309,7 +326,7 @@ * @param fname Name der Datei, die nicht gefunden wurde * @throws IOException falls etwas schief geht entsteht dieser Fehler */ public void sendNotFound(HttpExchange e, String fname) throws IOException { private void sendNotFound(HttpExchange e, String fname) throws IOException { OutputStream os = e.getResponseBody(); String response = fname + STR_NOT_FOUND; byte[] bytes = response.getBytes(StandardCharsets.UTF_8); @@ -320,7 +337,9 @@ } /** * Eine Range * Eine Range bezeichnet einen zusammenhängenden Bereich * aus Bytes, der sich aus den Bytepositionen des Beginns und Endes * des Bereiches ergibt. */ class Range { src/de/uhilger/minsrv/handler/StopServerHandler.java
@@ -26,7 +26,8 @@ import java.util.logging.Logger; /** * * Ein HTTP-Handler zum Stoppen der Anwendung * * @author Ulrich Hilger */ public class StopServerHandler implements HttpHandler {