Serveur web statique et dynamique (cgi-bin)

publicité
Réseaux
Introduction
Pour effectuer une connexion à un serveur (en python ou autre langage) il est nécessaire
d’utiliser des « sockets » (connecteur réseau, interface de connexion). Qui vont servir de canal
entre le serveur et le client. La « socket serveur » va écouter jusqu’à un évènement (l’envoie
d’un « stop » par exemple), elle sera associée un port et à l’adresse du serveur. Le client va
alors envoyer une requête au serveur avec comme « destination » le port de la « socket serveur »
(qui va receveur cette requête), et va éventuellement envoyer une réponse au client par le biais
de cette même socket client :
Web statique
L’exemple suivant présente un client « client.py » et un serveur « server.py » en Python. Il
s’agit d’un échange entre un client et le serveur statique car la requête n’est pas « interprétée ».
Il s’agit juste d’accéder à un fichier sur le serveur dont le contenu ne sera pas impacté par la
requête du client (avec le protocole http « GET ») :
client.py :
#!/usr/bin/env python
# coding: utf-8
import os, sys, traceback
import socket
#--------TRAITE LES ARGUMENTS-------def getParam():
if len(sys.argv) != 2:
print "USES :\n\tclient.py \"Protocol itemToCheck\"\n\tclient.py close\n-----------------------\nEXAMPLES :\n\tclient.py \"GET /index.html\""
sys.exit(0)
return sys.argv[1]
#--------PROGRAMME-------if __name__=="__main__":
message = getParam() #Controle les paramètres (« python client.py "requete" »)
hote = "localhost" #127.0.0.1
port = 8080
socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket.connect((hote, port))
print "Connection on {}".format(port)
socket.send(message) #Envoie la requête au serveur
response = socket.recv(255) #Ecoute la réponse (255 = taille max attendue)
print response
print "Close client"
socket.close()
server.py :
#!/usr/bin/env python
# coding: utf-8
import socket, sys, os, subprocess
#--------CREE LA SOCKET ET GERE LES PROBLEMES SI DEJA USE-------def startSocket():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', 8080)) #Respectivement sock.bind((adresse, port))
return sock
#--------TRAITE UNE REPONSE D'UN CLIENT-------def doResponseWork(response, sock, cli):
dir_path = os.environ.get('HTTP_ROOT', os.path.dirname(os.path.realpath(__file
__))) #Cherche le chemin dans la variable d’environnement « HTTP_ROOT », si elle
n’est pas trouvée alors on prend le répertoire où se situe le script server.py
if response[0:3] == "GET": #Contrôle le protocole
filePath = response[4:]
splitResult = filePath.split(' ')
filePath = dir_path + splitResult[0]
#--------REQUETE : VERIFICATION TYPE DE FICHIER-------isHtml = filePath.split('.')
#--------HTML : PRESENCE DU FICHIER-------elif isHtml[1] == "html" and os.path.exists(filePath):
fichier = open(filePath, "r")
cli.send(bytes("HTTP/1.0 200 OK\r\nContentType: text/html;\r\ncharset=utf-8\r\nContent-Language: fr\r\nConnection: keepalive\r\n\r\n".encode('UTF-8') + fichier.read()))
fichier.close()
else:
cli.send(b"HTTP/1.0 404 FILE NOT FOUND")
#--------CLOSE LA SOCKET SERVER-------elif response == "close":
print "Close server"
cli.close()
sock.close()
sys.exit(0)
#--------PROGRAMME-------if __name__=="__main__":
sock = startSocket()
while True:
sock.listen(5)
client, address = sock.accept()
print "{} connected".format( address )
response = client.recv(4096).decode('utf-8') #L’encodage est important (firefox)
if response!= "":
doResponseWork(response, sock, client)
client.close() #fermeture socket client une fois le travail fini
Web dynamique
Dans un contexte « dynamique » le serveur transmet la requête au logiciel pointé dans l’URL,
ce dernier va alors générer l’équivalent du contenu d’une page html (ou web dans le cas général)
en fonction d’arguments passés également par l’URL (dans le cas d’une requête http/GET).
On utilise la variable d’environnement « QUERY_STRING » dans laquelle se trouvent
l’ensemble des variables et leurs valeurs passées dans l’URL (http/GET) ou dans le corps de la
requête http (http/POST).
Il est important de noter que le langage utilisé pour créer le contenu web n’a aucune
importance, il n’est pas nécessaire qu’il soit identique à celui utilisé par le serveur.
Exemple avec un programme C, et le même serveur python que précédent avec l’ajout de « cgibin » (dossier contenant les exécutables à utiliser dans le cadre d’une génération dynamique) :
bonjour.c :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char* queryString = getenv("QUERY_STRING");
char* parts[2];
char* nom;
char* prenom;
int index = 0;
parts[index] = strtok(queryString, "&");
while( parts[index] != NULL ){
index++;
if(index>1)
break;
parts[index] = strtok(NULL, "&");
}
strtok(parts[0], "=");
if(strcmp(parts[0], "nom")==0)
nom = strtok(NULL, "=");
else if(strcmp(parts[0], "prenom")==0)
prenom = strtok(NULL, "=");
strtok(parts[1], "=");
if(strcmp(parts[1], "nom")==0)
nom = strtok(NULL, "=");
else if(strcmp(parts[1], "prenom")==0)
prenom = strtok(NULL, "=");
printf("<!DOCTYPE html> <html lang=\"fr\"><head><meta charset=\"utf8\"><TITLE>Bonjour</TITLE><link rel=\"shortcut icon\" href=\"favicon.ico\" />
</head><body><h1> Bonjour %s %s</h1></body></html>", prenom, nom);
return 0;
}
server.py (gère les cgi-bin avec comme protocole GET grâce à « QUERY_STRING » et ceux
avec le protocole POST via l’entrée standard) :
#!/usr/bin/env python
# coding: utf-8
import socket, sys, os, subprocess
#--------CREE LA SOCKET ET GERE LES PROBLEMES SI DEJA USE-------def startSocket():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', 8080)) #Respectivement sock.bind((adresse, port))
return sock
def getProtocol(response, sock, cli, dir_path):
filePath = response[4:]
splitResult = filePath.split(' ')
filePath = dir_path + splitResult[0]
#--------REQUETE : VERIFICATION TYPE DE FICHIER-------isHtml = filePath.split('.')
isCgiBin = filePath.split('?')
#--------CGI : PRESENCE DU FICHIER-------if os.path.exists(isCgiBin[0]) and "cgi-bin" in filePath:
os.environ["QUERY_STRING"] = isCgiBin[1]
popen = subprocess.Popen([isCgiBin[0]], stdout=subprocess.PIPE)
popen.wait()
output = popen.stdout.read() #Récupération de la sortie standard
cli.send(bytes("HTTP/1.0 200 OK\r\nContent-Type:
text/html;\r\ncharset=utf-8\r\nContent-Language: fr\r\nConnection: keepalive\r\n\r\n".encode('UTF-8') + output))
#--------HTML : PRESENCE DU FICHIER-------elif isHtml[1] == "html" and os.path.exists(filePath):
fichier = open(filePath, "r")
cli.send(bytes("HTTP/1.0 200 OK\r\nContent-Type:
text/html;\r\ncharset=utf-8\r\nContent-Language: fr\r\nConnection: keepalive\r\n\r\n".encode('UTF-8') + fichier.read()))
fichier.close()
else:
cli.send(b"HTTP/1.0 404 FILE NOT FOUND")
def postProtocol(response, sock, cli, dir_path):
filePath = response[5:]
splitResult = filePath.split(' ')
filePath = dir_path + splitResult[0]
isCgiBin = filePath.split('?')
#--------CGI : PRESENCE DU FICHIER-------if os.path.exists(isCgiBin[0]) and "cgi-bin" in filePath:
values = response.split("\r\n\r\n")
popen = subprocess.Popen([isCgiBin[0], values[1]],
stdout=subprocess.PIPE)
popen.wait()
output = popen.stdout.read()
cli.send(bytes("HTTP/1.0 200 OK\r\nContent-Type:
text/html;\r\ncharset=utf-8\r\nContent-Language: fr\r\nConnection: keepalive\r\n\r\n".encode('UTF-8') + output))
#--------TRAITE UNE REPONSE D'UN CLIENT-------def doResponseWork(response, sock, cli):
dir_path = os.environ.get('HTTP_ROOT',
os.path.dirname(os.path.realpath(__file__))) #Cherche le chemin dans la
variable d’environnement « HTTP_ROOT », si elle n’est pas trouvée alors on
prend le répertoire où se situe le script server.py
#--------PROTOCOLE GET-------if response[0:3] == "GET":
getProtocol(response, sock, cli, dir_path)
#--------PROTOCOLE POST-------elif response[0:4] == "POST":
postProtocol(response, sock, cli, dir_path)
#--------CLOSE LA SOCKET SERVER-------elif response == "close":
print "Close server"
cli.close()
sock.close()
sys.exit(0)
#--------PROGRAMME-------if __name__=="__main__":
sock = startSocket()
while True:
sock.listen(5)
client, address = sock.accept()
print "{} connected".format(address)
response = client.recv(4096).decode('utf-8') #L’encodage est important
(firefox)
if response!= "":
doResponseWork(response, sock, client)
client.close() #Fermeture socket client une fois le travail fini
Résultat
GET
URL : « adresseServeur:port/cgi-bin(dossier)/nomExe?clef=valeur&clef=valeur&… »
GET/POST
Le même cgi-bin (en supposant qu’il supporte les deux protocoles) :
<h1>Protocole GET : postGet</h1>
<form method="get" action="cgi-bin/postGet">
<p>
<label for="message">Votre message :</label>
<input type="text" name="message" id="message"/>
<br/>
<input type="submit" value="Submit">
</p>
</form>
<h1>Protocole POST : postGet</h1>
<form method="post" action="cgi-bin/postGet">
<p>
<label for="message">Votre message :</label>
<input type="text" name="message" id="message"/>
<br/>
<input type="submit" value="Submit">
</p>
</form>
Téléchargement