4 커밋 5dec0aa8f7 ... 4093cec5b6

작성자 SHA1 메시지 날짜
  Benoît Hubert 4093cec5b6 5.5 sur tests et écriture FT de base 7 년 전
  Benoît Hubert e4b1f461f1 Explications sur req. POST 7 년 전
  Benoît Hubert f1f2bacd01 update 5.4 7 년 전
  Benoît Hubert 45785e716d Fini 5.4 7 년 전

+ 33 - 0
examples/mini-serveur-ft.py

@@ -0,0 +1,33 @@
+from selenium import webdriver
+
+browser = webdriver.Firefox()
+
+# Edith has heard about a cool new online to-do app. She goes
+# to check out its homepage
+browser.get('http://localhost:8081')
+
+# She notices the page title and header mention to-do lists
+assert 'Mini-Serveur' in browser.title
+
+# She is invited to enter a to-do item straight away
+
+# She types "Buy peacock feathers" into a text box (Edith's hobby
+# is tying fly-fishing lures)
+
+# When she hits enter, the page updates, and now the page lists
+# "1: Buy peacock feathers" as an item in a to-do list
+
+# There is still a text box inviting her to add another item. She
+# enters "Use peacock feathers to make a fly" (Edith is very methodical)
+
+# The page updates again, and now shows both items on her list
+
+# Edith wonders whether the site will remember her list. Then she sees
+# that the site has generated a unique URL for her -- there is some
+# explanatory text to that effect.
+
+# She visits that URL - her to-do list is still there.
+
+# Satisfied, she goes back to sleep
+
+browser.quit()

+ 121 - 2
react-tuto/src/markdown/5. Mini-serveur web/04. Telnet comme client HTTP.md

@@ -1,6 +1,21 @@
-Revenons à notre Telnet. Tu vas émettre une requête au mini-serveur, en saisissant `GET / HTTP/1.1`, et en validant *deux fois* par entrée.
+##### Notre première requête !
 
-Tu dois voir s'afficher :
+Revenons à notre Telnet. Tu vas émettre une requête au mini-serveur. Saisis :
+
+```text
+GET / HTTP/1.1
+```
+
+Puis *deux fois* par entrée : ceci équivaut à écrire une ligne vide après la rêquête, ce qui nous permet de signifier au serveur que notre requête est terminée et qu'il peut la traiter.
+
+Si on se penche sur l'anatomie de la requête, elle est composé de 3 segments séparés par des espaces :
+
+- la méthode, ici `GET`
+- l'URL, ici `/`
+- le protocole, `HTTP` suivi de sa version, ici 1.1, séparés par un slash.
+
+
+Tu dois voir s'afficher la réponse brute du serveur :
 ```text
 HTTP/1.0 200 OK
 Server: BaseHTTP/0.6 Python/3.5.2
@@ -9,3 +24,107 @@ Content-type: text/html
 
 Hello world!Connection closed by foreign host.
 ```
+
+Précision : la partie `Connection closed by foreign host.` à la fin ne fait pas partie de la réponse ! C'est simplement le texte affiché par Telnet quand il quitte parce que la connexion avec le serveur s'est rompue.
+
+En effet, à chaque requête, le client ouvre une nouvelle connexion vers le serveur, et celui-ci la rompt une fois qu'il a fini d'envoyer la réponse. Cela rejoint la notion de protocole sans état.
+
+Ce bout de texte se trouve sur la même ligne, tout simplement parce qu'on n'a pas mis de retour chariot dans la chaîne `"Hello world!"` envoyée par le mini-serveur.
+
+Que contient la réponse ?
+
+- Sur la première ligne : la version de HTTP utilisée par le serveur `HTTP/1.0`, suivie du *code de réponse* numérique 200, suivi de la traduction en langage intelligible par un humain de ce code : OK, tout s'est bien passé.
+- Sur les trois lignes suivantes : les *en-têtes de réponse* (*response headers*). Ce sont des paires clé-valeur. Le serveur nous dit qui il est, via l'en-tête `Server`, nous indique la `Date` précise à laquelle il a répondu, et nous indique quel type de contenu il a envoyé : du HTML (encore qu'ici ce soit du texte).
+- Puis une ligne vide, puis le *corps de la réponse* (*response body*).
+
+Remarque que :
+- le code 200 correspond à ce qui a été spécifié via la ligne `self.send_response(200)` dans le code du serveur.
+- l'en-tête `Content-type` correspond aussi à une ligne de code du serveur.
+- les autres en-têtes `Server` et `Date` ont été ajoutées automatiquement par le serveur.
+
+##### Une requête incorrecte !
+
+Relance `telnet 127.0.0.1 8081` puis saisis : `ABOULE / HTTP/1.1`, et valide deux fois. Voilà la réponse :
+```text
+HTTP/1.0 501 Unsupported method ('ABOULE')
+Server: BaseHTTP/0.6 Python/3.6.4
+Date: Mon, 12 Feb 2018 02:55:20 GMT
+Connection: close
+Content-Type: text/html;charset=utf-8
+Content-Length: 499
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+        "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+        <title>Error response</title>
+    </head>
+    <body>
+        <h1>Error response</h1>
+        <p>Error code: 501</p>
+        <p>Message: Unsupported method ('ABOULE').</p>
+        <p>Error code explanation: HTTPStatus.NOT_IMPLEMENTED - Server does not support this operation.</p>
+    </body>
+</html>
+```
+
+Le serveur est beaucoup plus bavard ! Regarde la première ligne : tout code autre que 20x signifie une erreur. Ici c'est simplement une *méthode* HTTP qui n'existe pas. La méthode, c'est souvent un *verbe* qui indique la nature de notre requête au serveur (veut-on simplement retirer - `GET` - ou envoyer - `POST` - des données).
+
+Les *méthodes* sont standardisées, et c'est heureux, à moins de tenir spécialement à ce que le navigateur et le serveur parlent en argot (abouler viendrait de l'occitan *abolar* pour la petite histoire !).
+
+Pourquoi a-t-on plus de contenu ici, que dans la réponse à la requête correcte précédente ? Simplement parce que le serveur a des "modèles de page" tout prêts qu'il utilise dans certains cas d'erreur.
+
+Note qu'on a un nouvel en-tête, `Content-Length`, qui indique le nombre d'octets reçus, en comptant le *corps* de réponse uniquement et pas les en-têtes. Si tu copies/colles la réponse, y compris le retour chariot, dans un éditeur de texte, et que tu sauves dans un fichier, celui-ci fera la taille exacte indiquée par `Content-Length`.
+
+
+##### Une requête POST
+
+Utilisons maintenant une méthode standard : `POST`.
+
+Relance `telnet 127.0.0.1 8081` puis saisis ceci, et **attends un peu avant de valider** :
+```text
+POST /login HTTP/1.1
+Content-Type: application/x-www-form-urlencoded
+Content-Length: 45
+
+username=jonsnow&password=123456&action=login
+```
+
+Note la différence avec la requête GET envoyée avant :
+
+- On a toujours la première ligne avec méthode, URL, HTTP suivi de sa version
+- On a ensuite deux lignes d'en-têtes. Le serveur envoie des en-têtes de réponse pour nous indiquer comment prendre en charge celle-ci, mais le client peut lui aussi communiquer au serveur des informations utiles... voire indispensables ! Comme on *envoie* des données, on doit dire au serveur deux choses :
+    - Comment celles-ci sont encodées, grâce à `Content-Type`. La valeur de ce header est `application/x-www-form-urlencoded` : on envoie les données avec un formatage proche de celui utilisé pour passer des paramètres à une requête GET.
+    - Quelle quantité de données on envoie, grâce à `Content-Length`. Sa valeur est le nombre d'octets envoyés.
+- Note également qu'après les en-têtes, on a... Une ligne vide ! N'ai-je pas dit avant que ça signifiait que notre requête était terminée ? C'était vrai pour une requête GET car après la ligne contenant la méthode et les éventuelles en-têtes, elle n'est pas censée *envoyer* des données. Mais une requête POST doit envoyer un *corps* (*body*). Pour marquer la fin des headers, et le début du body, on laisse une ligne vide entre les deux.
+
+Ensuite, cette fois, si on laisse à nouveau une ligne vide, la requête sera considérée comme achevée, et pourra être traitée par le serveur.
+
+Donc, à nouveau, valide deux fois. La réponse est presque la même qu'avant :
+
+```text
+HTTP/1.0 501 Unsupported method ('POST')
+Server: BaseHTTP/0.6 Python/3.6.4
+Date: Mon, 12 Feb 2018 03:20:10 GMT
+Connection: close
+Content-Type: text/html;charset=utf-8
+Content-Length: 497
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+        "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+        <title>Error response</title>
+    </head>
+    <body>
+        <h1>Error response</h1>
+        <p>Error code: 501</p>
+        <p>Message: Unsupported method ('POST').</p>
+        <p>Error code explanation: HTTPStatus.NOT_IMPLEMENTED - Server does not support this operation.</p>
+    </body>
+</html>
+```
+
+Pas si étrange : `POST` est une méthode standard, mais on n'a rien dans notre *handler* pour gérer une requête `POST`. Page suivante, on va l'ajouter !

+ 48 - 0
react-tuto/src/markdown/5. Mini-serveur web/05. Et les tests dans tout ça.md

@@ -0,0 +1,48 @@
+En voyant le titre de cette page, tu te rappelles sûrement qu'avant de s'embarquer dans cette exploration de HTTP, on parlait de Python et de TDD !
+
+Peut-être ne vois-tu pas encore le rapport entre notre travail préparatoire sur le TDD, et ces nouvelles infos sur HTTP.
+
+Alors voici un rappel du plan :
+
+- Apprendre un minimum sur Python et le TDD : *fait*.
+- Comprendre le fonctionnement de HTTP et d'un serveur web : *en cours*.
+- Ajouter des fonctionnalités à ce mini-serveur web : *en cours*, du moins l'intention, à la page précédente, était d'ajouter la possibilité de gérer les requêtes POST !
+
+<img src="https://media.giphy.com/media/WL950i62D3HQA/giphy.gif" alt="kata" style="float: left; padding: 0 25px 25px 0;" />
+
+Le fait que j'allais me lancer tête baissée dans le handler POST est significatif : maintenir la *discipline* du TDD est difficile, au début, et même quand on a déjà l'habitude d'écrire des tests.
+
+On est souvent pressé d'ajouter des fonctionnalités à tout prix. Et c'est mal ! C'est pourquoi j'insiste sur l'importance de prendre de bonnes habitudes dès maintenant.
+
+Les experts en TDD comparent son apprentissage à la pratique des katas en arts martiaux : des mouvements qui sont au début peu naturels, mais qu'à force de répéter, on assimile.
+
+<div style="clear:both;"></div>
+
+##### Donc, retour aux tests
+
+Nous avons terminé la section précédente, "Bases de Python", avec un exercice de TDD. Nous avons écrit la fonction `est_bissextile()` en écrivant d'abord des tests.
+
+Ce type de test que nous avons écrit est appelé **test unitaire**. Ce sont des tests qui permettent de vérifier le fonctionnement d'un composant précis de l'application que l'on construit. Si jamais on change l'implémentation, le test doit toujours passer, sinon on a cassé quelque chose, et on parle de *régression*.
+
+Un autre type de test est le **test fonctionnel** ou **test d'intégration**. Il sert à tester l'application avec une vue d'ensemble, plus globale que les tests unitaires. En fait, le test fonctionnel vise à simuler l'interaction d'un véritable utilisateur avec l'application.
+
+C'est là que va intervenir Selenium : il permet de contrôler un navigateur (ici Firefox) via geckodriver qu'on a téléchargé au début.
+
+Avant d'aller plus loin et d'ajouter d'autres choses à notre mini-serveur, on va écrire un test dans lequel on va décrire ce qu'on voudrait que notre page web affiche, et comment on voudrait pouvoir interagir avec.
+
+Commençons par faire simple :
+
+```python
+from selenium import webdriver
+
+# Instance de Firefox contrôlée par webdriver
+browser = webdriver.Firefox()
+
+# On pointe ce browser vers l'URL de notre mini-serveur
+browser.get('http://localhost:8081')
+
+# En toute logique, on voudrait que le HTML généré par notre serveur ait un titre
+assert 'Mini-Serveur' in browser.title
+
+browser.quit()
+```

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1 - 1
react-tuto/src/resources/markdown.json