Ultrakompakter HTTP Server
ulrich
2024-12-01 692dc7be9791f131bfb253c13363b75bc41b8467
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/*
  neon - Embeddable HTTP Server based on jdk.httpserver
  Copyright (C) 2024  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 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/>.
 */
package de.uhilger.neon;
 
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.Date;
 
/**
 * Helfer zur Beantwortung von HTTP-Anfragen
 * 
 * @author Ulrich Hilger
 * @version 1, 03.06.2021
 */
public class HttpResponder {
  
  /* Headernamen */
  public static final String ACCEPT_RANGES_HEADER = "Accept-Ranges";
  public static final String CONTENT_LENGTH = "Content-Length";
  public static final String CONTENT_TYPE = "Content-Type";
  public static final String LAST_MODIFIED_DATE_HEADER = "Last-Modified";
 
  /* Statuscodes */
  public static final int SC_OK = 200;
  public static final int SC_NOT_FOUND = 404;
  public static final int SC_METHOD_NOT_ALLOWED = 405;
  public static final int SC_UNPROCESSABLE_ENTITY = 422;
  public static final int SC_INTERNAL_SERVER_ERROR = 500;
 
  /* String Konstanten */
  public static final String STR_BYTES = "bytes";
  public static final String STR_NOT_FOUND = " not found.";
  public static final String LM_PATTERN = "EEE, dd MMM yyyy HH:mm:ss zzz";
  
  /**
   * Den Inhalt einer Datei ausliefern
   *
   * @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
   */
  public void serveFile(HttpExchange e, File file) throws IOException {
    if (file.exists()) {
      setHeaders(e, file);
      e.getResponseHeaders().set(CONTENT_LENGTH, Long.toString(file.length()));
      e.sendResponseHeaders(SC_OK, file.length());
      if(HttpHelper.HTTP_GET.equalsIgnoreCase(e.getRequestMethod())) {
        InputStream in = new FileInputStream(file);
        OutputStream os = e.getResponseBody();        
        write(in, os);
        finish(in, os);
      }
    } else {
      sendNotFound(e, file.getName());
    }
  }
 
  public void write(InputStream in, OutputStream out) throws IOException {
    byte[] b = new byte[4096];
    int bytesRead = in.read(b);
    while (bytesRead > -1) {
      out.write(b, 0, bytesRead);
      bytesRead = in.read(b);
    }
  }
  
  public void finish(InputStream in, OutputStream out) throws IOException {
    in.close();
    finish(out);
  }
 
  public void finish(OutputStream out) throws IOException {
    out.flush();
    out.close();
  }
  
  /**
   * Die Header erzeugen, die unabh&auml;ngig davon, ob der ganze 
   * Inhalt oder nur Teile davon ausgeliefert werden sollen, in der 
   * Antwort stehen sollen 
   * 
   * @param e das Objekt mit Methoden zur Untersuchung der Anfrage sowie zum
   * Anfertigen und Senden der Antwort
   * @param file  die Datei, f&uuml;r die die Header gelten
   * @throws IOException falls etwas schief geht entsteht dieser Fehler
   */
  public void setHeaders(HttpExchange e, File file) throws IOException {
    Headers resHeaders = e.getResponseHeaders();
    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
   * 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 {
    antwortSenden(e, SC_NOT_FOUND, fname + STR_NOT_FOUND);
  }  
 
  public void antwortSenden(HttpExchange exchange, int code, String antwort) throws IOException {
    byte[] bytes = antwort.getBytes();
    exchange.sendResponseHeaders(code, bytes.length);
    OutputStream os = exchange.getResponseBody();
    os.write(bytes);
    finish(os);
  }
 
 
  
}