Deze oefening is deel van de DEA Course aan de Hogeschool Arnhem/Nijmegen. Onderwerp is het bekend raken met het Http-protocol in een simpele Java-server.
Web servers, gebruikt op het internet zijn feitelijk server-software die voldoen aan het Http-protocol. Het Http-protocol, zoals ook alle andere internet protocols, is gespecificeerd in een Request for Comments (RFC). De RFC voor Http kan hier gevonden worden: https://tools.ietf.org/html/rfc7230.
Bekijk de startcode in de master-branch. De server is op te starten via de main-methode van de HttpServer
klasse. Zorg ervoor dat je de startcode op een plaats (directory) zet waar geen spaties in voorkomen.
Voer de main-methode uit. De Server is nu opgestart. Navigeer via je browser naar http://localhost:8383/index.html om te zien wat de server teruggeeft. Mocht je een Connection-error krijgen in de log van IntelliJ bij het gebruiken van Chrome, probeer dan Edge of Firefox te gebruiken.
Bekijk de startcode in de klasse HttpServer
en probeer te begrijpen op welk moment de server wacht op Connecties van
een client. Gebruik Google met de zoektermen java server socket om je analyse te ondersteunen.
Wat opvalt is dat je Server momenteel maar één Http-request kan afhandelen. Pas dit aan zodat het mogelijk wordt om meerdere Http-requests af te handelen.
- Gebruik je kennis van multithreading om de
HttpServer
klasse zo aan te passen dat ieder Http-request op een eigenThread
wordt afgehandeld. - Pas de server zodanig aan dat de
Threads
in een oneindige lus worden opgestart. Op deze manier wordt de server niet meer afgesloten na een Http-request.
Bekijk de klasse ConnectionHandler
. Hierin kun je zien dat de server momenteel niks doet met de inhoud van het
Http-request en altijd dezelfde inhoud en status retourneert. Je kunt dit testen door het wijzigen van een deel van
URL in je browser. ook de URL http://localhost:8383/what-a-terribly-bigoted-server.html zal precies dezelfde content
retourneren. In dit onderdeel zul je dit gaan verbeteren.
De inhoud van de response staat nu nog hard-coded in de ConnectionHandler
. We gaan deze inhoud verplaatsen naar een
index.html, die we hierna zullen inlezen en retourneren via het response.
- Creëer de map src/main/resources. Dit is de default plaats binnen een Maven project om extra bestanden te plaatsen. Hierbij kun je denken aan plaatjes, configuratiebestanden of html-bestanden.
- Voeg in deze nieuwe map een submap pages/ toe en plaats hierin een index.html met de volgende content
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Simple Http Server</title>
</head>
<body>
<h1>Hi DEA folks!</h1>
<p>This is a simple line in html.</p>
</body>
</html>
Pas de ConnectionHandler
zodanig aan dat voor de body van het HTTP-request de file index.html wordt gebruikt.
- Gebruik voor het inlezen van een bestand de onderstaande methode.
- Plaats deze in een aparte klasse genaamd `HtmlPageReader.
- Merk op dat onderstaande methode verwacht dat het html-bestand in de map pages/ staat.
- Gebruik deze methode om nu de inhoud van index.html te retourneren. De constante
HTTP_BODY
uitConnectionHandler
kun je nu verwijderen.
public class HtmlPageReader {
public String readFile(String filename) {
var fullFileName = "pages/".concat(filename);
try {
ClassLoader classLoader = getClass().getClassLoader();
var file = new File(classLoader.getResource(fullFileName).getFile()).toPath();
var fileAsString = new String(Files.readAllBytes(file));
return fileAsString;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Voeg nu wat extra inhoud toe aan je index.html, herstart je server en bekijk het resultaat. De inhoud zal nu niet meer volledig gerenderd worden. Dit komt omdat de content-length in de Http-header niet meer correct is.
- Voeg aan de klasse
HtmlPageReader
een methode toe die op de correcte content-length kan bepalen van een gegeven bestand. Bekijk de RFC om te begrijpen hoe de content-length moeten worden berekend. - Gebruik de nieuwe methode om programmatisch een correcte Http-header te genereren.
Voeg ook een automatisch bepaalde datum aan het Date veld van de Http-header toe. Gebruik hierbij weer de RFC om te bepalen welk formaat deze moet hebben.
We gaan nu de afhandeling van de aangevraagde resource (bestand) verwerken. Merk op dat de Server nu altijd dezelfde index.html teruggeeft, los van de url van het daarwerkelijke request. Voeg de functionaliteit toe die ervoor zorgt
- De eerste regel van een Http-request, de start-line, bevat de Http-methode (in dit geval: GET), de request-target (in dit geval het html-bestand) en de versie van het Http Protocol. Bekijk de RFC voor meer informatie over de start-line.
- Zorg ervoor dat de
ConnectionHandler
van het Http-request, de start-line gebruikt om te achterhalen welke html pagina geretourneert moet worden. - Voeg een tweede pagina, bijvoorbeeld about.html toe en test of deze geretourneert wordt door naar http://localhost:8383/about.html te navigeren.
- Merk op dat het nu enkel mogelijk moet zijn bestaande pagina's op te vragen, de happy-flow.
Momenteel wordt nog standaard de Http statuscode 200 geretourneerd. Zorg ervoor dat de ConnectionHandler
de een
correcte statuscode retourneert indien de request-target niet bestaat.
- Zorg ervoor dat de relevante methodes uit
HtmlPageReader
een Exception gooien indien er een Html pagina geladen moet worden die niet bestaat. - Gebruik deze exceptie in de
ConnectionHandler
om zowel de juiste Http-Header, als Body te retourneren.
-
Verbeter de foutafhandeling. Momenteel wordt enkel onderscheid gemaakt tussen 404 en 200. Voeg ook, op een correcte en zinnig manier ondersteuning voor de colgende foutcode's toe:
- 500 INTERNAL SERVER ERROR
- 501 NOT IMPLEMENTED
- 505 HTTP VERSION NOT SUPPORTED
-
Het Http-protocol kent meer methodes dan enkel de GET. Lees de RFC voor meer informatie en implementeer ook de methode HEAD.