Serveur HTTP en Java : solution DIY
- Envisageable pour un serveur simple
-
Points délicats à traiter :
- Modèle multithreadé ou non bloquant à selecteur pour gérer les flots de communication : passage à l'échelle
- Analyse des requêtes envoyées par le client : respect du protocole HTTP/1.1, sécurité...
- Configurabilité du serveur
- Le protocole HTTP/2.0 est beaucoup plus complexe à implanter "à la main" (multiplexage, compression Huffman obligatoire, chiffrement quasi-obligatoire...)
Quelques implantations de serveur HTTP en Java
-
Implantation basique avec com.sun.net.httpserver.HttpServer :
- On créé un serveur avec HttpServer.create(InetSocketAddress addr, int backlog)
- On rajoute un HttpHandler pour gérer chaque requête avec createContext(String path, HttpHandler handler
- NanoHTTPD (BSD) : une implantation HTTP/1.1 qui tient en un seul fichier Java (~ 1300 LOC)
- Xerver: une implantation supportant les scripts CGI
- Grizzli : un framework pour réaliser des serveurs HTTP en mode non-bloquant (nio)
- ...
yoctoHTTPD : un serveur HTTP concurrent
package fr.upem.jacosa.net;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class HttpServer
{
public static void main(String[] args) throws IOException
{
int port = Integer.parseInt(args[0]);
final File directory = new File(args[0]);
ServerSocket ss = new ServerSocket(port);
ExecutorService es = Executors.newCachedThreadPool();
while (! Thread.interrupted())
{
final Socket s = ss.accept();
es.submit(new Runnable() {
@Override public void run()
{
try {
try (BufferedReader r = new BufferedReader(new InputStreamReader(s.getInputStream(), "ASCII"))) {
String[] header = r.readLine().split(" ");
s.shutdownInput(); // Discard the remaining lines
int errorCode = 405;
File f = null;
if (header[0].equals("GET")) {
f = new File(directory, header[1].substring(1));
errorCode = f.exists() ? 200 : 404;
}
s.getOutputStream().write(("HTTP/1.0 " + errorCode
+ " nomessage\r\nContent-Type: application/octet-stream\r\n\r\n").getBytes("ASCII"));
switch (errorCode) {
case 200:
try (FileInputStream fis = new FileInputStream(f)) {
fis.transferTo(s.getOutputStream());
}
break;
default:
s.getOutputStream().write("An error was encountered".getBytes("ASCII"));
break;
}
s.getOutputStream().flush();
s.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
es.shutdown();
}
}