From 51e1d5cc34ea03c3689700f5f5738d92eb1d9d5b Mon Sep 17 00:00:00 2001
From: ulrich
Date: Fri, 26 Mar 2021 15:41:25 +0000
Subject: [PATCH] FileHandler Logging erweitert

---
 src/de/uhilger/minsrv/handler/FileHandler.java |  331 ++++++++++++++++++++++++++++++++-----------------------
 1 files changed, 192 insertions(+), 139 deletions(-)

diff --git a/src/de/uhilger/minsrv/handler/FileHandler.java b/src/de/uhilger/minsrv/handler/FileHandler.java
index 5cf31b2..46929f6 100644
--- a/src/de/uhilger/minsrv/handler/FileHandler.java
+++ b/src/de/uhilger/minsrv/handler/FileHandler.java
@@ -1,19 +1,19 @@
 /*
-    mc2 - Mediacenter neu
-    Copyright (C) 2021  Ulrich Hilger
+  mini-server - Ein minimalistischer HTTP-Server
+  Copyright (C) 2021  Ulrich Hilger
 
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU Affero General Public License as
-    published by the Free Software Foundation, either version 3 of the
-    License, or (at your option) any later version.
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Affero General Public License as
+  published by the Free Software Foundation, either version 3 of the
+  License, or (at your option) any later version.
 
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU Affero General Public License for more details.
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Affero General Public License for more details.
 
-    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/>.
+  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.handler;
 
@@ -31,85 +31,109 @@
 import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 import java.util.logging.Logger;
 
 /**
- * Die Klasse FileHandler dient zur Auslieferung von Dateiinhalten 
- * &uuml;ber HTTP.
- * 
- * @author ulrich
+ * Die Klasse FileHandler dient zur Auslieferung von Dateiinhalten &uuml;ber
+ * HTTP.
+ *
+ * @author Ulrich Hilger
  * @version 0.1, 25. M&auml;rz 2021
  */
 public class FileHandler implements HttpHandler {
-  
+
+  /* Der Logger fuer diesen FileHandler */
   private static final Logger logger = Logger.getLogger(FileHandler.class.getName());
 
+  /* Header Namen */
   final static String RANGE_HEADER = "Range";
   final static String CONTENT_RANGE_HEADER = "Content-Range";
   final static String ACCEPT_RANGES_HEADER = "Accept-Ranges";
   final static String LAST_MODIFIED_DATE_HEADER = "Last-Modified";
   final static String CONTENT_TYPE = "Content-Type";
-  
+
+  /* Status Codes */
+  public static final int SC_OK = 200;
   public static final int SC_PARTIAL_CONTENT = 206;
-  
+  public static final int SC_NOT_FOUND = 404;
+
+  /* Ablageort fuer Webinhalte */
   private final String basePath;
 
   /**
    * Ein neues Objekt der Klasse FileHandler erzeugen
-   * 
-   * @param basePath der Pfad zu Inhalten, die von diesem Handler 
-   * ausgeliefert werden
+   *
+   * @param basePath der Pfad zu Inhalten, die von diesem Handler ausgeliefert
+   * werden
    */
   public FileHandler(String basePath) {
     this.basePath = basePath;
   }
 
   /**
-   * Die Datei ermitteln, die sich aus dem angefragten URL ergibt,
-   * pr&uuml;fen, ob die Datei existiert und den Inhalt der Datei 
-   * abh&auml;ngig davon, ob ein Range-Header vorhanden ist, 
-   * ganz oder teilweise ausliefern.
-   * 
-   * @param e  das Objekt mit Methoden zur Untersuchung 
-   * der Anfrage sowie zum Anfertigen und Senden der Antwort
-   * @throws IOException falls etwas schief geht entsteht dieser Fehler 
+   * Die Datei ermitteln, die sich aus dem angefragten URL ergibt, pr&uuml;fen,
+   * ob die Datei existiert und den Inhalt der Datei abh&auml;ngig davon, ob ein
+   * Range-Header vorhanden ist, ganz oder teilweise ausliefern.
+   *
+   * @param e das Objekt mit Methoden zur Untersuchung der Anfrage sowie zum
+   * Anfertigen und Senden der Antwort
+   * @throws IOException falls etwas schief geht entsteht dieser Fehler
    */
   @Override
   public void handle(HttpExchange e) throws IOException {
     String ctxPath = e.getHttpContext().getPath();
+    logger.finer(ctxPath);
     String uriPath = e.getRequestURI().getPath();
+    logger.finer(uriPath);
     String fName = uriPath.substring(ctxPath.length());
-    if(fName.startsWith(".")) {
-      throw new IOException("Mit einem Punkt beginnende Dateinamen sind ungueltig.");
-    }
+    logger.finer(fName);
+    
     Headers headers = e.getRequestHeaders();
-    if (headers.containsKey(RANGE_HEADER)) {
-      logger.info("has range header");
-      File file = new File(basePath, fName);
-      logger.info(file.getAbsolutePath());
-      serveFileParts(e, file);
+    Set keys = headers.keySet();
+    Iterator i = keys.iterator();
+    StringBuilder sb = new StringBuilder();
+    while(i.hasNext()) {
+      String key = i.next().toString();
+      sb.append("   ");
+      sb.append(key);
+      sb.append(": ");
+      sb.append(headers.getFirst(key));
+      sb.append("\r\n");
+    }
+    logger.finer(sb.toString());
+    
+    if (fName.startsWith(".")) {
+      sendNotFound(e, fName);
     } else {
-      logger.info("no range header");
-      if (fName.endsWith("/")) {
-        fName += "index.html";
+      //Headers headers = e.getRequestHeaders();
+      if (headers.containsKey(RANGE_HEADER)) {
+        logger.finer("has range header");
+        serveFileParts(e, new File(basePath, fName));
+      } else {
+        logger.finer("no range header");        
+        if (fName.endsWith("/")) {
+          fName += "index.html";
+        }
+        serveFile(e, new File(basePath, fName));
       }
-      File file = new File(basePath, fName);
-      serveFile(e, file);
     }
   }
 
   /**
    * Den Inhalt einer Datei ausliefern
-   * 
-   * @param e  das Objekt mit Methoden zur Untersuchung 
-   * der Anfrage sowie zum Anfertigen und Senden der Antwort
+   *
+   * @param e das Objekt mit Methoden zur Untersuchung der Anfrage sowie zum
+   * Anfertigen und Senden der Antwort
    * @param file die Datei, deren Inhalt ausgeliefert werden soll
-   * @throws IOException falls etwas schief geht entsteht dieser Fehler 
+   * @throws IOException falls etwas schief geht entsteht dieser Fehler
    */
   private void serveFile(HttpExchange e, File file) throws IOException {
-    OutputStream os = e.getResponseBody();
     if (file.exists()) {
-      e.sendResponseHeaders(200, file.length());
+      OutputStream os = e.getResponseBody();
+      Headers headers = e.getResponseHeaders();
+      headers.add(ACCEPT_RANGES_HEADER, "bytes");
+      e.sendResponseHeaders(SC_OK, file.length());
       InputStream in = new FileInputStream(file);
       int b = in.read();
       while (b > -1) {
@@ -117,99 +141,103 @@
         b = in.read();
       }
       in.close();
+      os.flush();
+      os.close();
     } else {
-      String response = file.getName() + " not found.";
-      byte[] bytes = response.getBytes(StandardCharsets.UTF_8);
-      e.sendResponseHeaders(404, bytes.length);
-      os.write(bytes);
+      sendNotFound(e, file.getName());
     }
-    os.flush();
-    os.close();
   }
-  
+
   /**
    * Einen Teil des Inhalts einer Datei ausliefern
-   * 
-   * @param e  das Objekt mit Methoden zur Untersuchung 
-   * der Anfrage sowie zum Anfertigen und Senden der Antwort
+   *
+   * Wenn eine Range angefragt wird, hat die Antwort einen Content-Range Header
+   * wie folgt:
+   *
+   * <code>
+   * Content-Range: bytes 0-1023/146515
+   * Content-Length: 1024
+   * </code>
+   *
+   * Wenn mehrere Ranges angefragt werden, hat die Antwort mehrere Content-Range
+   * Header als Multipart Response. Multipart Responses fehlen dieser
+   * Implementierung noch.
+   *
+   * (vgl. https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests)
+   *
+   * @param e das Objekt mit Methoden zur Untersuchung der Anfrage sowie zum
+   * Anfertigen und Senden der Antwort
    * @param file die Datei, deren Inhalt teilweise ausgeliefert werden soll
-   * @throws IOException falls etwas schief geht entsteht dieser Fehler 
+   * @throws IOException falls etwas schief geht entsteht dieser Fehler
    */
   /*
-    Wenn eine Range angefragt wird, hat die Antwort einen 
-    Content-Range Header wie folgt:
-
-    Content-Range: bytes 0-1023/146515
-    Content-Length: 1024
-  
-    Wenn mehrere Ranges angefragt werden, hat die Antwort mehrere 
-    Content-Range Header als Multipart Response. Multipart Responses fehlen 
-    dieser Implementierung noch.
-  
-    (vgl. https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests)
-  */
+   */
   private void serveFileParts(HttpExchange e, File file) throws IOException {
-    InputStream is = new FileInputStream(file);
-    OutputStream os = e.getResponseBody();
-    Headers resHeaders = e.getResponseHeaders();
-    long responseLength = 0;
-    long start = 0;
-    long end;
-    RangeGroup rangeGroup = parseRanges(e, file);
-    Iterator<Range> i = rangeGroup.getRanges();
-    while(i.hasNext()) {
-      Range range = i.next();
-      start = range.getStart();
-      end = range.getEnd();
-      StringBuilder sb = new StringBuilder();
-      sb.append("bytes ");
-      sb.append(range.getStart());
-      sb.append("-");
-      sb.append(range.getEnd());
-      sb.append("/");
-      sb.append(file.length());
-      resHeaders.add(CONTENT_RANGE_HEADER, sb.toString());      
-      logger.info(sb.toString());
-      responseLength += (end - start);
-      logger.info("responseLength: " + responseLength);
+    if (file.exists()) {
+      InputStream is = new FileInputStream(file);
+      OutputStream os = e.getResponseBody();
+      Headers resHeaders = e.getResponseHeaders();
+      resHeaders.add(ACCEPT_RANGES_HEADER, "bytes");
+      long responseLength = 0;
+      long start = 0;
+      long end;
+      RangeGroup rangeGroup = parseRanges(e, file);
+      Iterator<Range> i = rangeGroup.getRanges();
+      while (i.hasNext()) {
+        Range range = i.next();
+        start = range.getStart();
+        end = range.getEnd();
+        StringBuilder sb = new StringBuilder();
+        sb.append("bytes ");
+        sb.append(range.getStart());
+        sb.append("-");
+        sb.append(range.getEnd());
+        sb.append("/");
+        sb.append(file.length());
+        resHeaders.add(CONTENT_RANGE_HEADER, sb.toString());
+        logger.info(sb.toString());
+        responseLength += (end - start);
+        logger.info("responseLength: " + responseLength);
+      }
+      resHeaders.add(CONTENT_TYPE, "video/mp4");
+      SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz");
+      Date date = new Date(file.lastModified());
+      resHeaders.add(LAST_MODIFIED_DATE_HEADER, sdf.format(date));
+      e.sendResponseHeaders(SC_PARTIAL_CONTENT, responseLength);
+      if (start > 0) {
+        is.skip(start);
+      }
+      long count = 0;
+      int byteRead = is.read();
+      while (byteRead > -1 && count < responseLength) {
+        ++count;
+        os.write(byteRead);
+        byteRead = is.read();
+      }
+      os.flush();
+      os.close();
+      is.close();
+    } else {
+      sendNotFound(e, file.getName());
     }
-    resHeaders.add(CONTENT_TYPE, "video/mp4");    
-    SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz");
-    Date date = new Date(file.lastModified());
-    resHeaders.add(LAST_MODIFIED_DATE_HEADER, sdf.format(date));
-    e.sendResponseHeaders(SC_PARTIAL_CONTENT, responseLength);
-    if(start > 0) {
-      is.skip(start);
-    }
-    long count = 0;
-    int byteRead = is.read();
-    while(byteRead > -1 && count < responseLength) {
-      ++count;
-      os.write(byteRead);
-      byteRead = is.read();
-    }    
-    os.flush();
-    os.close();
-    is.close();
   }
-  
+
   /**
    * Die Byte-Ranges aus dem Range-Header ermitteln.
-   * 
+   *
    * Der Range-Header kann unterschiedliche Abschnitte bezeichnen, Beispiele:
-   * Range: bytes=200-1000, 2000-6576, 19000-
-   * Range: bytes=0-499, -500
-   * (vgl. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range)
-   * 
-   * @param e das Objekt mit Methoden zur Untersuchung 
-   * der Anfrage sowie zum Anfertigen und Senden der Antwort
+   * Range: bytes=200-1000, 2000-6576, 19000- Range: bytes=0-499, -500 (vgl.
+   * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range)
+   *
+   * @param e das Objekt mit Methoden zur Untersuchung der Anfrage sowie zum
+   * Anfertigen und Senden der Antwort
    * @param file die Datei, deren Inhalt ausgeliefert werden soll
    * @return die angefragten Byte-Ranges
    */
   private RangeGroup parseRanges(HttpExchange e, File file) {
     RangeGroup ranges = new RangeGroup();
     String rangeHeader = e.getRequestHeaders().get(RANGE_HEADER).toString();
-
+    logger.info(rangeHeader);
     /*
       Inhalt des Range-Headers von nicht benoetigten Angaben befreien
     
@@ -221,10 +249,10 @@
     
       Der regulaere Ausdruck "[^\\d-,]" bezeichnet alle Zeichen, die keine 
       Ziffern 0-9, Bindestrich oder Komma sind.
-    */    
+     */
     rangeHeader = rangeHeader.replaceAll("[^\\d-,]", "");
     logger.info(rangeHeader);
-    
+
     /*
       Die Ranges ermitteln. 
     
@@ -242,17 +270,17 @@
       values.length < 2: Fall 3 ist gegeben
       values.length > 1 und values[0].length < 1: Fall 1 ist gegeben
       ansonsten: Fall 2 ist gegeben
-    */
+     */
     String[] rangeArray = rangeHeader.split(",");
-    for(String rangeStr : rangeArray) {
+    for (String rangeStr : rangeArray) {
       Range range = new Range();
       String[] values = rangeStr.split("-");
-      if(values.length < 2) {
+      if (values.length < 2) {
         // Fall 3
         range.setStart(Long.parseLong(values[0]));
         range.setEnd(file.length());
       } else {
-        if(values[0].length() < 1) {
+        if (values[0].length() < 1) {
           // Fall 1
           range.setStart(0);
           range.setEnd(Long.parseLong(values[1]));
@@ -263,19 +291,39 @@
         }
       }
       ranges.addRange(range);
-    }    
+    }
     return ranges;
   }
-  
+
+  /**
+   * Eine nicht gefunden Antwort senden
+   * 
+   * @param e das Objekt mit Methoden zur Untersuchung der Anfrage sowie zum
+   * Anfertigen und Senden der Antwort
+   * @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 {
+    OutputStream os = e.getResponseBody();
+    String response = fname + " not found.";
+    byte[] bytes = response.getBytes(StandardCharsets.UTF_8);
+    e.sendResponseHeaders(SC_NOT_FOUND, bytes.length);
+    os.write(bytes);
+    os.flush();
+    os.close();
+  }
+
   /**
    * Eine Range
    */
   class Range {
+
     private long start;
     private long end;
 
     /**
      * Den Beginn dieser Range ermitteln
+     *
      * @return Beginn dieser Range
      */
     public long getStart() {
@@ -284,6 +332,7 @@
 
     /**
      * Den Beginn dieser Range angeben
+     *
      * @param start Beginn dieser Range
      */
     public void setStart(long start) {
@@ -292,6 +341,7 @@
 
     /**
      * Das Ende dieser Range ermitteln
+     *
      * @return Ende dieser Range
      */
     public long getEnd() {
@@ -300,49 +350,52 @@
 
     /**
      * Das Ende dieser Range angeben
+     *
      * @param end Ende dieser Range
      */
     public void setEnd(long end) {
       this.end = end;
     }
   }
-  
+
   /**
    * Eine Gruppe aus Ranges
    */
   class RangeGroup {
+
     private List<Range> ranges;
     private long totalSize;
-    
+
     /**
      * Ein neues Objekt der Klasse RangeGroup erzeugen
      */
     public RangeGroup() {
       ranges = new ArrayList();
     }
-    
+
     /**
      * Dieser RangeGroup eine Range hinzufuegen.
-     * 
+     *
      * @param range die Range, die dieser RangeGroup hinzugefuegt werden soll
      */
     public void addRange(Range range) {
       ranges.add(range);
       totalSize += range.getEnd() - range.getStart();
     }
-    
+
     /**
-     * Die Gesamtgr&ouml;&szlig;e dieser RangeGroup ermitteln, also die 
-     * Summe der Anzahl von Bytes aller ihrer Ranges.
-     * 
+     * Die Gesamtgr&ouml;&szlig;e dieser RangeGroup ermitteln, also die Summe
+     * der Anzahl von Bytes aller ihrer Ranges.
+     *
      * @return die Gr&ouml;&szlig;e dieser RangeGroup in Bytes
      */
     public long getSize() {
       return totalSize;
     }
-    
+
     /**
      * Einen Iterator &uuml;ber die Ranges dieser RangeGroup abrufen
+     *
      * @return Iterator &uuml;ber die Ranges dieser RangeGroup
      */
     public Iterator<Range> getRanges() {

--
Gitblit v1.9.3