Table des matières

Comment programmer un serveur web ?

C'est ce que nous allons voir ici.

PHP

PHP est le langage par excellence pour programmer côté serveur. Statistique 2018 :

According to W3Techs' data, PHP is used by 78.9% of all websites with a known server-side programming language.

Pour démarrer avec PHP, la méthode est assez simple :

Avantages :

Python

Dans toute la suite, on supposera qu'un classique serveur web Apache tourne sur la machine. On supposera aussi que le VirtualHost soit réglé en AllowOverride All (c'est la valeur par défaut pour certaines versions d'Apache ; cela permet de définir des paramètres directement dans les fichiers .htaccess, ce qui est pratique):

<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /home/www/example/
    <Directory />
      AllowOverride All
      Require all granted
    </Directory>
  </VirtualHost> 

Méthode simple (old-school): mod_cgi

NB: Utiliser CGI revient à démarrer un nouveau process (= un nouvel interpréteur Python) pour chaque requête, ce qui est potentiellement plus lent (dans mes tests 90ms Python 2.7 / 140ms Python 3, au lieu de 34ms avec mod_python ou mod_wsgi) que les méthodes détaillées ci-après.

Méthode simple: mod_python

Il y a une méthode relativement simple pour faire de la programmation web en Python, avec une logique similaire à celle détaillée en PHP :

  1. Installer le module mod_python :

     apt-get install libapache2-mod-python
  2. Créer un fichier .htaccess à la racine du site, pour indiquer que les fichiers .py doivent être traités par Python, contenant ceci :

     AddHandler mod_python .py
     PythonHandler mod_python.publisher
  3. Créer un fichier test.py :

     def index(req):
         return("<html><body>Hello world</body></html>")
  4. Ouvrir https://www.example.com/test.py dans le navigateur, ça marche !

Avantages :

Méthode classique: avec un framework web (Bottle, Flask, etc.)

La méthode la plus courante pour faire du web en Python est assez différente des 2 méthodes présentées précédemment.

Contrairement à avoir, comme précédemment, pour chaque requête, un script qui s'exécute puis se termine, il s'agit ici d'avoir un process Python qui tourne en permanence, et attend les requêtes au fur et à mesure, dans une boucle infinie (Event loop).

Exemple :

from bottle import route, run, template

@route('/hello/<name>')
def hello(name):
    return template('<b>Bonjour {{name}}</b>!', name=name)
    
@route('/')
def index():
    return 'Bienvenue sur le site!'

run(host='localhost', port=8080)  # boucle infinie, qui attend des requêtes sur le port 8080

On utilise classiquement un framework pour ça :

Le code précédent fonctionne bien, mais "écoute" sur le port 8080, donc on peut y accéder avec https://example.com:8080/.

Comment donc "relier" ce process Python avec Apache qui écoute, quant à lui, sur le port 80 ?

Javascript

Oui on peut faire du Javascript côté serveur, c'est le cas notamment avec le framework très populaire NodeJS.

Le principe est le même que dans le paragraphe précédemment, on a un process qui tourne en permanence, avec une boucle d'événements (event loop). On le démarre avec :

node app.js

Voici le code pour app.js:

var express = require('express');
var app = express();
app.get('/', function (req, res) {
  res.send('Hello World!');
});
app.listen(8080, function () {
  console.log('Example app running!');
});

C

Pour les puristes ! In progress...

A voir (divers)

A lire...

Pour bien voir la différence CGI / FastCGI / mod_wsgi / mod_python:

https://www.electricmonk.nl/docs/apache_fastcgi_python/apache_fastcgi_python.html

Comparaison de performance mod_php vs. mod_python

Créons un fichier test.py:

import time
def index(req):
    return(str(time.time()))

un fichier test.php:

<?php
    echo time();
?>

le .htaccess qui permet d'utiliser mod_python:

AddHandler mod_python .py
PythonHandler mod_python.publisher

et enfin index.html pour tester le temps que prend chaque requête:

<div id="a">Click test.py</div>
<div id="b">Click test.php</div>

<script type="text/javascript">
var start;

document.getElementById("a").onclick = function() { 
    start = Date.now()
    var xhr = new XMLHttpRequest();
    xhr.open("GET", "test.py");
    xhr.onreadystatechange = function() { if (xhr.readyState === 4 && xhr.status === 200) { console.log(xhr.responseText); console.log(Date.now() - start); } }
    xhr.send("");
}

document.getElementById("b").onclick = function() { 
    start = Date.now()
    var xhr = new XMLHttpRequest();
    xhr.open("GET", "test.php");
    xhr.onreadystatechange = function() { if (xhr.readyState === 4 && xhr.status === 200) { console.log(xhr.responseText); console.log(Date.now() - start); } }
    xhr.send("");
}
</script>

Ouvrons index.html dans le navigateur et regardons le contenu de la console.

Bilan :

, , , , , ,