Memòria del TFC: Creació del nucli d'un servidor Web; XicHttpd | ||
---|---|---|
Anterior | Capítol 2. Anàlisi i Disseny del servidor Web | Següent |
El llenguatge de programació escollit per implementar el servidor Web és el C, i la plataforma de treball i execució serà el sistema operatiu Linux (compatible i386), junt al compilador que porta per defecte GCC.
Els directori arrel de treball seguirà l'estructura típica per aquest entorn:
src : Contindrà els fitxers en codi font (.c)
src/include : Contindrà els fitxers de capçalera (.h)
bin : Contindrà el programa executable resultat de la compilació.
doc : Contindrà la documentació del servidor web.
etc : Directori per defecte on residirà el fitxer de configuració del servidor xichttpd.conf.
log : Directori per defecte on s'escriuran els fitxers de log del servidor.
html : Directori per defecte on hi ficarem els fitxers html, imatges, i qualsevol recurs que hagi de servir el nostre servidor Web.
html/cgi-bin : Directori per defecte que contindrà els CGIs.
Les llibreries més rellevants del llenguatge C que s'utilitzaràn són:
socket : Conté totes les funcions necessàries per la creació i el control dels canals de comunicació en xarxa.
pthread : Conté totes les funcions necessàries per la creació i el control de fils d'execució, i defineix maneres segures d'accedir a les variables compartides.
zlib : Llibreries de compressió.
Per tal de tenir les funcionalitats estructurades, les fonts dels programes es distribuiran en diversos fitxers:
Fitxers de capçaleres
server.h - Contindrà les definicions de les estructures necessàries per al funcionament del servidor, i el nom d'algunes variables globals. Entre elles destaquem la que fa referència a una connexió del client i la que conté les dades de configuració del servidor:
typedef struct { pthread_t fil; // El fil assignat a la connexió unsigned long int id; // L'identificador int socket; // L'identificador del socket struct sockaddr_in adreca_client; time_t ctemps; // Temps en que s'ha creat time_t atemps; // Temps de l'últim accés c_request *dades; // Capçaleres del client (http.h) } connexio; typedef struct { char fitxer_config[255]; char dir_base[255]; char dir_cgi[255]; char dir_html[255]; char f_log[255]; char f_err[255]; char mime_types[255]; char nom_host[64]; short int port; struct sockaddr_in addr; int min_threads; int max_threads; time_t timeout; } sconf;
http.h - Conté les definicions de les estructures per emmagatzemar les capçaleres per les peticions i per les respostes, així com les definicions dels codis d'estat del servidor. En destaquem les que fan referència a les del capçaleres de les peticions i a la de les respostes:
typedef struct { char metode[16]; // El mètode solicitat char versio[16]; // La versió HTTP char URI[1024]; // El recurs char Host[64]; // Capçalera Host char AcceptEncoding[16]; // Capçalera Accept-Encoding char get_p[1924]; // Paràmetres del get int ContentLenght; // Capçalera Content-Length (POST) char Connection[64]; // Capçalera Connection (per als close) } c_request; typedef struct { char estat[64]; // El codi d'estat char Date[64]; // Capçaleres de la resposta char Server[128]; char LastModified[64]; char ContentLength[64]; char ContentType[128]; char ContentEncoding[16]; } c_response; /* Necessari per afegir al buffer de sortida dades binaries */ typedef struct { char sbuf[MAX_SBUF]; // El buffer de sortir int mida; // La mida del mateix } buf_out;
mime.h - Contindrà la definició de la llista amb els tipus mime, i un punter al primer element:
typedef struct mime { char ext[16]; // L'extensió char tipus_mime[64]; // El tipus mime struct mime *next; // Punter al següent de la llista } mime; mime *mime_types; // Punter al primer de la llista
errors.h - Contindrà les definicions dels missatges d'error del servidor.
main.h - Contindrà tots els include necessaris per l'aplicació, tan els de les llibreries de necessàries com els nostres. Tots els fitxers font inclouran aquest fitxer de capçaleres per evitar duplicitats.
Fitxers font
main.c - Contindrà el mètode principal que acceptarà un paràmetre amb el nom del fitxer de configuració que volem utilitzar si és diferent del que s'agafarà per defecte. També hi haurà les funcions de logging necessàries.
server.c - Contindrà les funcions necessàries per l'execució del servidor. Les funcions principals són:
void init (); - Passos necessaris per arrencar el servidor, bàsicament es cridarà a les funcions que segueixen.
void llegir_config (); - Llegirà el fitxer de configuració i actualitzarà les variables necessàries.
void crea_socket (); - Crearà el socket del servidor actualitzant la variable global server_sock, posteriorment obtindrà l'adreça IP del servidor fent una cerca DNS seguint l'ordre establert a /etc/hosts.conf en funció del nom de host especificat al fitxer de configuració. Finalment s'assignarà l'adreça al socket. Qualsevol error detectat provocarà la finalització del servidor.
void crea_fils_estatics (); - Crearà els threads estàtics i els deixarà a punt per ser activats en rebre peticions.
void escolta (int socket); - Entrarà en un bucle infinit a l'espera de rebre connexions pel socket passat per paràmetre. Quan arribi una connexió se li assignarà un fil de la cua si n'hi ha de lliures, i si no sen crearà un de nou sempre hi quan no s'hagi arribat al límit de clients.
void prepara_senyals (); - Canviarà la rutina d'atenció a alguns signals.
http.c - Contindrà la implementació del protocol que haurà d'executar cada fil. El paràmetre d'entrada cid d'algunes funcions fa referència a l'índex de la taula de clients on es troben les dades del client actual, de manera que en puguem llegir o escriure els diferents valors. Les funcions principals són:
int deco_request (int cid); - Decodificarà la petició del client. Els possibles valors de retorn seran:
CE400 : Si el request està mal construït.
CE411 : Un POST sense Content-Length.
CE414 : Si l'URI és massa llarga.
CE501 : Si el mètode demanat no està implementat.
CE505 : Si la versió HTTP no està suportada.
0 : Si tot és correcte.
-1 : Si per qualsevol motiu no hi ha més dades per llegir però encara no hem decodificat el request completament.
void set_mime (c_response *resposta, char *extensio); - Escriurà la capçalera Content-Type de la resposta actual apuntada per resposta en funció de extensio. Els tipus MIME seran carregats a l'inicialitzar el servidor. Si el fitxer no té extensió, el tipus serà text/plain, i si l'extensió és desconeguda el tipus per defecte serà application/octet-stream.
int envia (int sock, char *dades, int mida_dades, buf_out *sbuf, int mida_sbuf); - Enviarà dades pel socket sock. Si hi ha pipelining les dades s'afegiran al buffer de sortida sbuf, si no hi ha pipelining o el buffer de sortida és ple s'enviaran les dades directament. Si dades és NULL es força l'enviament immediat de buffer de sortida.
int prepara_error (int ce, int cid, buf_out *sbuf); - Prepararà el missatge d'error indicat per ce amb el codi d'estat, la versió, les capçaleres necessàries, a més de construir el missatge html descriptiu visible per al client. Tot el missatge s'afegirà al final del buffer de sortida sbuf, i es cridarà a la funció envia.
int cerca_recurs (int cid, buf_out *sbuf); - Cercarà el recurs demanat en la petició dins el sistema de fitxers local, si tot és correcte prepara el missatge de resposta dins sbuf i cridarà a la funció envia on el paràmetre dades apuntarà al recurs. Els possibles valors de retorn seran els següents, excepte en els casos que es cridi a les funció xic_cgi que retornarà el que aquesta retorni (veure més avall):
CE403 : Si no es té permís de lectura sobre el recurs demanat, o sobre el directori on es troba el recurs (Forbidden).
CE404 : Si no es troba el recurs demanat.
0 : Si tot és correcte.
-1 : Si es produeix algun error en accedir al recurs.
int xic_cgi (int cid, buf_out *sbuf, char *f_cgi); - Executarà el cgi especificat a f_cgi, esperarà a que aquest finalitzi i forçarà el tancament de la connexió. Els possibles valors de retorn són:
CE500 : Error intern del servidor en executar el cgi.
0 : Si tot ha anat correctament.
-1 : Si sorgeix algun altre error inesperat.
int xic_gzip (int cid, char *file_in, int mida_in, c_response *resposta, buf_out *sbuf); : S'encarrega de la compressió en gzip del fitxer file_in, donat que aquesta funció sempre serà cridada per cerca_recurs si per qualsevol motiu falla la compressió es retornarà un valor diferent de 0, aixì cerca_recurs encara podrà intentar d'enviar el recurs sense comprimir. Per qualsevol error es retornarà -1.
void http_control (int cid); - Aquesta funció és la que controlarà totes les altres, i és la que serà cridada a l'inici de cada thread.
Fitxer de configuració
xichttpd.conf - A no ser que s'especifiqui el contrari per la línia de comandes, per defecte es llegirà aquest fitxer del directori /etc, ../etc, ./ per aquest ordre de cerca, la sintaxis i les variables que s'acceptaran es mostren a continuació, qualsevol línia que comenci amb el caràcter # serà ignorada:
# Directori base on resideixen l'estructura de directoris del servidor DIR_BASE = "/usr/local/xichttpd" # Directori on residiran els cgi DIR_CGI = "path/contingut/html/cgi-bin" # Directori on residirà el lloc Web (fitxers html, imatges, fitxers, etc.) DIR_HTML = "path/contingut/html" # Nom del fitxer per logar els accessos al servidor amb el path complert FITXER_LOG = "/usr/local/xichttpd/log/acces.log" # Nom del fitxer per logar els errors interns del servidor FITXER_ERR = "/usr/local/xichttpd/log/error.log" # Path i nom del fitxer d'on carregar els tipus MIME MIME_TYPES = "/etc/mime.types" # El nom o l'adreça IP del host on s'executa el servidor # Cal que estigui present al fitxer /etc/hosts, és a dir, # ha de tenir una entrada DNS vàlida NOM_HOST = "Natasza" # El nombre mínim de threads en espera MIN_THREADS = "5" # El nombre màxim de clients simultanis MAX_CLIENTS = "150" # El valor del timeout de les connexions en segons SERVER_TIMEOUT = "60" # El port on escoltarà el servidor PORT = "80"