Activité Entrée, sortie et sortie d’erreur
Situation
Dans le but de vous familiariser avec les opérations d’entrée/sortie et les instructions de choix du Bash (Bourne Again Shell) de Linux, vous décidez de réaliser un serveur HTTP élémentaire.
Consigne
Réalisez le serveur HTTP en suivant les instructions et répondez aux questions qui vous sont posées.
Attention : Ce serveur HTTP est réalisé à des fins pédagogiques uniquement, il ne doit en aucun cas être utilisé en production.
Objectifs
À la fin de ce travail, vous devez :
- Être capable d’utiliser la commande case.
- Être capable de rediriger un fichier vers l’entrée standard d’une commande (
<
), et de rediriger la sortie standard ou la sortie d’erreur standard vers un fichier (>
et2>
). - Être capable de rediriger la sortie standard d’une commande vers la sortie d’erreur standard du script avec
>&2
.
Résultat attendu
- Un bref rapport qui décrit votre travail et votre démarche.
- Votre script Bash.
Ressources
Document:
Logiciel :
- Machine virtuelle de développement
- NetBeans
- Émulateur de terminal
- SymbolHound (moteur de recherche supportant les caractères spéciaux)
Mise en route
Créez un répertoire pour votre projet dans votre répertoire personnel. Dans ce répertoire, créez un fichier nommé « httpsrv.sh » et copiez le code suivant :
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
#!/usr/bin/env bash
documentRoot=./doc
# Lire la ligne de requête depuis l’entrée standard (stdin).
read -r request_line
read -r http_method uri http_version <<< $request_line
# Affiche les données de la requête dans la sortie d’erreur standard (stderr).
printf "Requète reçue : \n" >&2
printf " METHOD : %s\n" $http_method >&2
printf " URI : %s\n" $uri >&2
printf " VERSION : %s\n" $http_version >&2
# Si l’URI est la racine, demander une redirection vers l’index.
# TODO (4) (seulement lorsque tout le reste est fait)
# Si l’URI est "/", envoyez une réponse HTTP avec le statut 301.
# Éssaie d’associer l’URL à un fichier.
case $uri in
/index)
filename="$documentRoot/index.html"
mimetype="text/html"
;;
/info/intro)
filename="$documentRoot/fichier1.html"
mimetype="text/html"
;;
/page2)
filename="$documentRoot/fichier2.html"
mimetype="text/html"
;;
*.html)
filename="$documentRoot$uri"
mimetype="text/html"
;;
# TODO (3)
# Ajouter un cas pour les fichiers .css.
esac
# Test si l’URL a pu être associée à un fichier.
# TODO (2)
# Tester si le fichier existe avant d’écrire la réponse avec
# le statut 200 et si le fichier n’existe pas écrire une
# avec le statut 400
# Générer la réponse avec le statut 200 et le contenu du fichier
printf " Fichier : '%s'\n" $filename >&2
# Écrit la ligne de statut (Status-Line)
printf "HTTP/1.1 200 Ok\r\n"
# Écrit les en-têtes (headers).
printf "Content-Type: %s\r\n" $mimetype
printf "Content-length: %s\r\n" $(stat -c'%s' "$filename")
printf "Connection : close\r\n"
# Écrit les caractères CRLF qui marquent la fin des en-têtes.
printf "\r\n"
# Écrit le contenu (message-body)
# TODO (1)
# Replacer la commande echo par une commande qui permet d'afficher dans
# la sortie standard le contenu du fichier dont le chemin se trouve dans
# la variable $filename.
echo "<html><body><h1>Impec ! ça fonctionne !</h1></body><html>"
exit 0
Dans le répertoire du projet, créez un répertoire « doc ». Dans ce répertoire, créez un fichier nommé « fichier1.html » et copiez le code suivant :
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
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Fichier 1</title>
<link rel="stylesheet" type="text/css" href="/css/style.css">
</head>
<body>
<div class="content">
<h1>Introduction</h1>
<p>
Ceci est le contenu du fichier "fichier1.html". Ce contenu a été
utilisé pour construire la représentation de la ressource
identifiée par l’URL "/info/into" que vous avez fournie dans
votre requête.
</p>
<p>
La ressource est le document html contenu dans ce fichier et non
pas le fichier lui-même. L’URL identifie cette ressource et ne
donne aucune indication sur la manière dont celle-ci construite
ou stockée.
</p>
</div>
</body>
</html>
Créez ensuite un fichier « fichier2.html » et copiez le code suivant :
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
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Fichier 2</title>
<link rel="stylesheet" type="text/css" href="/css/style.css">
</head>
<body>
<div class="content">
<h1>Page 2</h1>
<p>
Ceci est le contenu du fichier "fichier2.html". Ce contenu a été
utilisé pour construire la représentation de la ressource
identifiée par l’URL "/page2" que vous avez fournie dans
votre requête.
</p>
<p>
La ressource est le document html contenu dans ce fichier et non
pas le fichier lui-même. L’URL identifie cette ressource et ne
donne aucune indication sur la manière dont celle-ci construite
ou stockée.
</p>
</div>
</body>
</html>
Créez encore un fichier « index.html » avec le code suivant :
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
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Index</title>
<link rel="stylesheet" type="text/css" href="/css/style.css">
</head>
<body>
<div class="content">
<h1>Index</h1>
<p>
Cette page est servie par un serveur HTTP réalisé à l’aide
d’un script shell qui accepte une requête HTTP de l’entrée
standard (stdin) et qui écrit la réponse correspondante dans
la sortie standard (stdout). Un utilitaire comme netcast (nc)
ou socat est utilisé pour écouter un port et rediriger l’entrée
et la sortie standard du script vers la connexion TCP lorsque
celle-ci est établie.
</p>
<ul>
<li><a href="/info/intro">introduction</a></li>
<li><a href="/page2">page 2</a></li>
<li><a href="/other/fichier3.html">page 3</a></li>
</ul>
</div>
</body>
</html>
Questions
Question1
Si ce n’est pas fait, ouvrez une fenêtre de terminal et allez dans le répertoire du projet. Dans ce répertoire, lancez la commande suivante :
1
dev@vmdev:~/projects/httpsrv$ chmod +x httpsrv.sh
Expliquez ce que fait cette commande.
Question 2
Exécutez la commande suivante :
1
dev@vmdev:~/projects/httpsrv$ ./httpsrv.sh
Le script attend que vous saisissiez une ligne de texte. Tapez le texte suivant et appuyé sur « Entrée » :
1
GET /index HTTP/1.1
Observez le résultat.
Exécutez maintenant la commande suivante :
1
dev@vmdev:~/projects/httpsrv$ ./httpsrv.sh >response.txt
Le script attend de nouveau que vous saisissiez une ligne de texte. Tapez le même texte que précédemment et observez le résultat. Vous pouvez afficher le contenu du fichier « response.txt » à l’aide de la commande suivante :
1
dev@vmdev:~/projects/httpsrv$ cat response.txt
Créez un fichier de texte dans le répertoire du projet à l’aide de la commande ci-dessous et affichez son contenu à l’aide de la commande cat
.
1
dev@vmdev:~/projects/httpsrv$ echo "GET /index HTTP/1.1" >request.txt
Exécutez la commande ci-dessous et observez le résultat.
1
dev@vmdev:~/projects/httpsrv$ ./httpsrv.sh 2>log.txt <request.txt
Étudiez le code du script et expliquez ce que vous avez observé.
Question 3
Reprenez le code du script et complétez les TODO 1, 2 et 3. Comment vous y prenez-vous pour vérifier que votre code fonctionne correctement ?
Question 4
Dans le répertoire du projet, dans le répertoire « doc », créez un répertoire « css » et créez un fichier « style.css » avec le code suivant :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
body, h1, h2, h3, h4, h5, h6, p, ul, ol, dl {
font-family: Tahoma, Geneva, Helvetica, sans-serif;
font-size: 120%;
color: #333;
line-height: 150%;
}
h1 {
font-size: 1.7em;
border-bottom: 1px solid #333
}
.content {
max-width: 45em;
margin-left: auto;
margin-right: auto;
}
Créez un répertoire « other » et, à l’interieur, un fichier « fichier3.html » avec le code suivant :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Fichier 3</title>
<link rel="stylesheet" type="text/css" href="/css/style.css">
</head>
<body>
<div class="content">
<h1>Page 3</h1>
<p>
Ceci est le contenu du fichier "fichier3.html". Ce contenu a été
utilisé pour construire la représentation de la ressource
identifiée par l’URL que vous avez fournie dans votre requête.
</p>
<p>
La ressource est le document html contenu dans ce fichier et non
pas le fichier lui-même. L’URL identifie cette ressource et ne
donne aucune indication sur la manière dont celle-ci construite
ou stockée.
</p>
</div>
</body>
</html>
Enfin, dans le répertoire du projet, créez un fichier « httpd.sh » avec le code suivant :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
port=$1
if [ "$port" = "" ]
then
port="8080"
fi
printf "\nLe serveur HTTP a démarré sur localhost et écoute le port %s.\n" $port
printf "Appuyez sur ctrl-c pour quitter.\n"
# Va dans le répertoire du script et sauvegarde le répertoire courant.
pushd $(dirname "$0") > /dev/null
# Affecte le chemin du répertoire courant (le répertoire du script)
# à la variable "basedir".
basedir=$(pwd)
# Restaure le répertoire courant précédent.
popd > /dev/null
# Ecoute le port 8080 et exécute le script httpsrv.sh dans
# un nouveau processus pour chaque entrante.
socat TCP4-LISTEN:$port,reuseaddr,fork EXEC:"$basedir/httpsrv.sh"
Exécutez les commandes suivantes pour installer l’utilitaire socat et rendre le script exécutable :
1
2
dev@vmdev:~/projects/httpsrv$ sudo apt-get install socat
dev@vmdev:~/projects/httpsrv$ chmod +x httpd.sh
Vous pouvez maintenant lancer le serveur HTTP avec la commande :
1
dev@vmdev:~/projects/httpsrv$ ./httpd.sh 8080
Ouvrez un navigateur et tapez l’url suivante : http://localhost:8080/index
Expliquez comment cela fonctionne.
Question 5
Complétez le TODO 4 dans le fichier « httpsrv.sh ». Naviguez sur la racine à plusieurs reprises, que constatez-vous ? Ce comportement est-il normal ?