\documentclass[a4paper,11pt]{article}
\newcommand{\UPSUDSKIPFONTS}{}
\input{td-upsud}


\newcommand{\matiere}{Web 1}
\newcommand{\parcours}{Agrégation d'Informatique}
\newcommand{\jour}{20 octobre 2021}
\newcommand{\titre}{
{\LARGE \bf TP n\up{o} 2}
}

\begin{document}

\entete{}
Le but de ce TP est d'écrire de petits serveurs Web en utilisant le module
\texttt{http.server} de Python. Ces derniers sont là uniquement pour illustrer
les concepts du cours :
\begin{itemize}
  \item passage de paramètres
  \item \emph{Cookies}
  \item Sessions HTTP
  \item architecture REST
\end{itemize}

En particulier, ce n'est pas un bon \emph{framework} pour faire des applications
Web modernes. On utilisera plutôt des \emph{framework} éprouvés (par exemple
\emph{Django} en Python, Java/JSP en Java, OCsigen en OCaml, \ldots) pour créer
des applications Web réalistes.
\section{Les classes \texttt{HTTPHandler} et \texttt{HTTPServer}}

En Python, un serveur Web peut s'écrire assez simplement de la façon suivante :
\begin{python}
import http.server
import socketserver

#Petit wrapper pour qui crée un serveur TCP/IP permettant d'être interrompu
#en pressant CTRL-C dans la console
class MyHttpServer(socketserver.TCPServer):

    def server_bind(self):
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.socket.bind(self.server_address)

    def serve_until_interrupted(self):
        try:
            self.serve_forever()
        except KeyboardInterrupt:
            self.shutdown()
        finally:
            self.server_close()


if __name__ == "__main__":
    HTTPD = MyHttpServer(("localhost", 8080), http.server.SimpleHTTPRequestHandler)
    HTTPD.serve_until_interrupted()

\end{python}
Le fonctionnement est le suivant. Lorsqu'une connection TCP est établie sur le
port 8080, le contenu du message est traîté par le code de la classe
\pythoninline{SimpleHTTPRequestHandler}. Cette dernière dérive de
\pythoninline{BaseHTTPRequestHandler}
et possède plusieurs
attributs et méthodes utiles, documentées ici :
\begin{center}
    \url{https://docs.python.org/3/library/http.server.html#http.server.BaseHTTPRequestHandler}
\end{center}

\subsection{Framework Web, à la main}

Créer une classe \pythoninline{CustomHTTPRequestHandler} (ainsi que des classes
auxiliaires) qui permette d'avoir le comportement suivant (on pourra rajouter
incrémentalement chacune de ces fonctionalités):

\begin{itemize}
    \item redéfinit la méthode \pythoninline{do_GET}. Si la ressource demandée
    est un chemin existant, alors on appelle simplement
    \pythoninline{super().do_GET()}. Le comportement sera alors de renvoyer le
    fichier en HTTP. Sinon, la fonction décode l'URL (utiliser la fonction
    \pythoninline{parser_url} du module \pythoninline{urllib.parse}) et
    recherche dans la variable globale \pythoninline{HTTP_ACTION} (supposée contenir
    un dictionnaire) une entrée qui correspond au chemin demandé. Si cette
    entrée existe, on suppose qu'elle est associée à une fonction attendant
    quatre paramètres : l'objet \pythoninline{self}, le chemin demandé, les paramètres de requête et un
    booléen qui vaut \pythoninline{True} si la requête est de type \texttt{GET}.
    Ainsi, si on accède à l'URL
    \url{http://localhost:8080/test?param=1&foo=bar}, on s'attend à ce que la
    méthode \pythoninline{do_GET} appelle
    \begin{python}
HTTP_ACTION['/test'](self, '/test', {'param': '1', 'foo': 'bar'}, True)
    \end{python}
    Si aucune action n'est disponible, le serveur renvoie une erreur 404 et un
    petit fichier HTML contenant un message d'erreur.

    \item modifier la classe \texttt{CustomHTTPRequestHandler} pour que cette
    dernière dispose de deux méthodes \texttt{getCookies()} renvoyant tous les
    cookies sous forme d'un dictionnaire et \texttt{setCookie(n, v, a)} qui
    permette de positionner un cookie de nom $n$, de valeur $v$ et d'expiration
    $a$. On utilisera l'attribut \texttt{headers} et on redéfiniera la méthode
    \texttt{end\_headers()}
    
    \item Une fois les cookies mis en place, ajouter un mécanisme de session.
     Ce dernier sera implémenté par un dictionnaire globale
     \texttt{HTTP\_SESSION} et une méhode \texttt{getSession()} sur la classe 
     \texttt{CustomHTTPRequestHandler}. Cette dernier renvoie un dictionnaire
     associé à la session (éventuellement vide si la session vient d'être crée).
\end{itemize}


\section{Applications}
Se servir de la classe \texttt{CustomHTTPRequestHandler} pour créer des serveurs
répondants aux spécifications suivantes :
\begin{enumerate}
    \item Un compteur : le serveur supporte une unique ressource \texttt{/count}
    qui renvoie la valeur d'un compteur. Ce dernier est incrémenté à chaque
    rechargemde la ressource. Le compteur doit être stocké dans la session. On
    vérifiera en lançant une fenêtre de navigation privée et en constante que
    l'on a des compteurs différents.

    \item Une \textit{todo} list. Le serveur propose une page HTML contenant un
    formulaire permettant de créer une page avec une liste de choses à faire,
    stockée dans la session.
    \begin{itemize}
    \item    L'URL \texttt{/reset} réinitialise la liste.
    \item    L'URL \texttt{/add?todo=tache} ajoute une tâche à la liste.
    \item    L'URL \texttt{/remove?id=n} supprime la tâche d'identifiant $n$.
    \end{itemize}
\end{enumerate}

\end{document}
