Sebxoii I've made a huge tiny mistake. | Salut à tous,
Je dois réaliser un petit serveur HTTP en TP cette année, et je me retrouve complètement bloqué face à une erreur que je n'avais jamais rencontrée auparavant.
D'après gdb, le problème se situe à la sortie de manageRequest ( ligne 265 ), mais je n'ai pas réussi à lui soutirer plus d'informations.
De plus, le problème ne survient que lors de l'appel à la fonction execCGI, même si le programme ne plante pas directement dans cette fonction. Dans le cas d'une demande d'un fichier html ou d'une image, tout se passe normalement et le thread se termine sans soucis.
Si cela peut aider certains d'entre vous, voici le code source complet ainsi que les fichiers qui me servent à tester le serveur : http://sebxoiii.free.fr/Programmation/httpServer.zip
Merci d'avance.
Code :
- /*--------------------------------------------------------------------------*/
- #include "netUtils.h"
- /*--------------------------------------------------------------------------*/
- #define BUFFER_SIZE 0x1000
- typedef struct {
- int extFd;
- SSL * ssl;
- int intFd;
- struct
- {
- char buffer[BUFFER_SIZE];
- char * ptr;
- unsigned int remaining;
- } incoming,outgoing;
- char requestMethod[0x100];
- char requestUri[0x100];
- char contentLength[0x100];
- char fileName[0x100];
- } Request;
- Request *createRequest(int fd,SSL_CTX * ctx)
- {
- Request * req=(Request *)malloc(sizeof(Request));
- req->extFd=fd;
- req->ssl=(SSL *)0;
- req->intFd=-1;
- req->incoming.buffer[0]='\0';
- req->incoming.ptr=(char *)0;
- req->incoming.remaining=0;
- req->outgoing.buffer[0]='\0';
- req->outgoing.ptr=(char *)0;
- req->outgoing.remaining=0;
- req->requestMethod[0]='\0';
- req->requestUri[0]='\0';
- req->contentLength[0]='\0';
- req->fileName[0]='\0';
- if(ctx)
- {
- /* ... A COMPLETER ... : initialiser req->ssl */
- }
- return req;
- }
- void destroyRequest(Request * req)
- {
- int r;
- if(req->ssl)
- {
- /* ... A COMPLETER ... : detruire req->ssl */
- }
- RESTART_SYSCALL(r,close(req->extFd));
- if(req->intFd!=-1)
- {
- RESTART_SYSCALL(r,close(req->intFd));
- }
- free(req);
- }
- int requestRead(Request * req,void * buffer,unsigned int size)
- {
- return req->ssl ? errno=EBADF,-1 /* ... A COMPLETER ... : lire req->ssl */
- : read(req->extFd,buffer,size);
- }
- int requestWrite(Request * req,const void * buffer,unsigned int size)
- {
- return req->ssl ? errno=EBADF,-1 /* ... A COMPLETER ... : ecrire req->ssl */
- : write(req->extFd,buffer,size);
- }
- MAKE_READLINE_FUNCTION(requestReadLine,requestRead,Request *)
- MAKE_WRITEFULLY_FUNCTION(requestWriteFully,requestWrite,Request *)
- /*--------------------------------------------------------------------------*/
- void contentType(Request *req, char *type)
- {
- char *buffer=req->fileName;
- unsigned int i=0;
- /* Détection du point qui précède l'extension. */
- do
- {
- i++;
- }
- while(buffer[i]!='.' && i!=strlen(buffer));
- /* Une fois le point détecté, modification de l'adresse du début du buffer avant qu'elle coincide avec celle du début de l'extension. */
- buffer=&buffer[i+1];
- /* Il ne reste ensuite plus qu'à comparer le chaine "buffer" qui ne contient désormais plus que l'extension avec les extensions courantes.*/
- if(strcmp(buffer,"jpg" )==0) {strcpy(type,"image/jpeg" );}
- else if(strcmp(buffer,"gif" )==0) {strcpy(type,"image/gif" );}
- else if(strcmp(buffer,"c" )==0) {strcpy(type,"text/plain" );}
- else if(strcmp(buffer,"txt" )==0) {strcpy(type,"text/plain" );}
- else if(strcmp(buffer,"html" )==0) {strcpy(type,"text/html" );}
- else if(strcmp(buffer,"bin" )==0) {strcpy(type,"application/octet-stream" );}
- /*else if(strcmp(buffer,"cgi" )==0) {strcpy(type,"cgi" );}*/ /*Si CGI ou autre exécutable le test est réalisé au niveau de access. */
- else {strcpy(type,"application/octet-stream" );}
- }
- void suppressionParametresCGI(Request *req,char *parametresCGI)
- {
- unsigned int i=0;
- /* Detection du premier point d'interrogation. */
- do
- {
- i++;
- }
- while(req->fileName[i]!='?' && i!=strlen(req->fileName));
- /* Sauvegarde des paramètres CGI. */
- strcpy(parametresCGI,&(req->fileName[i]));
- /* Suppression des parametres CGI du champ fileName. */
- req->fileName[i]='\0';
- }
- void execCGI(Request *req)
- {
- int fdCGI[2],r=0;
- pid_t result;
- r=pipe(fdCGI); /* Création d'un tube de communication : 2 file descriptors (un en écriture, un en lecture). */
- if(r==-1){ perror("pipe" ); exit(EXIT_FAILURE); }
- result=fork(); /* Création d'un nouveau processus. */
- switch(result)
- {
- case -1: perror("fork" ); exit(EXIT_FAILURE);
- /* Processus enfant :*/
- case 0: close(fdCGI[0]); /* Fermeture de l'un des file descriptors. */
- dup2(fdCGI[1],STDOUT_FILENO); /* Inversion de l'entrée standard et du descripteur de fichier créé en écriture. */
- execlp(req->fileName,req->fileName,NULL); /* Remplacement du processus courant par un processus lançant le script cgi. */
- /* Processus parent :*/
- default: close(fdCGI[1]); /* Fermeture de l'un des file descriptors. */
- do /* Boucle utilisée pour lire le code HTML créé par le script CGI et envoyé par le tube. */
- {
- req->outgoing.remaining=readLine(fdCGI[0],req->outgoing.buffer,BUFFER_SIZE);
- req->outgoing.remaining=requestWriteFully(req,req->outgoing.buffer,req->outgoing.remaining);
- } while (req->outgoing.remaining != 0);
- close(fdCGI[0]); /* Fermeture du file descriptor restant. */
- }
- }
- void envoiBuffer(Request *req)
- {
- req->outgoing.remaining=requestWriteFully(req,req->outgoing.buffer,strlen(req->outgoing.buffer));
- }
- void badRequest(Request *req)
- {
- strcpy(req->outgoing.buffer,"HTTP/1.1 400 Bad Request\n" );
- req->outgoing.remaining=strlen(req->outgoing.buffer);
- envoiBuffer(req);
- }
- void okRequest(Request *req)
- {
- strcpy(req->outgoing.buffer,"HTTP/1.1 200 OK\n" );
- req->outgoing.remaining=strlen(req->outgoing.buffer);
- envoiBuffer(req);
- }
- void notFoundRequest(Request *req)
- {
- strcpy(req->outgoing.buffer,"404 Not Found\n" );
- req->outgoing.remaining=strlen(req->outgoing.buffer);
- envoiBuffer(req);
- }
- void *manageRequest(void * data)
- {
- int r=0,ficD=0;
- char parametresCGI[0x20];
- Request * req=(Request *)data;
- pthread_detach(pthread_self());
- /* Récupération de la première ligne. */
- req->incoming.remaining=requestReadLine(req,req->incoming.buffer,BUFFER_SIZE);
- sscanf(req->incoming.buffer,"%s %s",req->requestMethod,req->requestUri);
- printf("Requete : %s %s\n",req->requestMethod,req->requestUri);
- /* Récupération du reste de l'entête de la requête HTTP. */
- do
- {
- req->incoming.remaining=requestReadLine(req,req->incoming.buffer,BUFFER_SIZE);
- printf("%s",req->incoming.buffer);
- } while(strcmp(req->incoming.buffer,"\n" )!=0 && strcmp(req->incoming.buffer,"\r\n" )!=0);
- /* Traitement de la requête. */
- if(strcmp(req->requestMethod,"GET" )!=0 && strcmp(req->requestMethod,"POST" )!=0) /* Si ce n'est pas une requête GET. */
- {
- badRequest(req);
- }
- else if(strcmp(req->requestMethod,"GET" )==0)/* Si c'est une requête GET. */
- {
- okRequest(req);
- /* Fabrication du champ fileName. */
- strcpy(req->fileName,"." );
- strcat(req->fileName,req->requestUri);
- /* Suppression des des éventuels paramètres CGI. */
- suppressionParametresCGI(req,parametresCGI);
- /* Récupération des informations. */
- {
- struct stat infos;
- RESTART_SYSCALL(r,stat(req->fileName,&infos));
- if(r==-1) { perror("stat" ); return (void *)0; }
- /* S'il s'agit d'un dossier on ajoute index.html*/
- if(S_ISDIR(infos.st_mode))
- {
- strcat(req->fileName,"/index.html" );
- }
- /* Sinon, on n'ajoute rien et on ouvre le fichier. */
- RESTART_SYSCALL(ficD,open(req->fileName,O_RDONLY));
- if(ficD==-1) /* Si l'ouverture pose problème. */
- {
- notFoundRequest(req);
- return (void *)0;
- }
- /* Envoi de l'entête. */
- {
- char type[0x10];
- contentType(req,type);
- if(access(req->fileName,X_OK!=-1)) /* S'il ne s'agit pas d'un fichier exécutable : */
- {
- /* Envoi de la taille de l'entête. */
- sprintf(req->outgoing.buffer,"Content-Length : %lu\n",infos.st_size);
- envoiBuffer(req);
- /* Envoi du type de l'entête */
- sprintf(req->outgoing.buffer,"Content-Type : %s\n\n",type);
- envoiBuffer(req);
- /* Si l'ouverture fonctionne sans problème. */
- do
- {
- req->outgoing.remaining=readFully(ficD,req->outgoing.buffer,BUFFER_SIZE);
- req->outgoing.remaining=requestWriteFully(req,req->outgoing.buffer,req->outgoing.remaining); /* Impossibilité d'utiliser envoiBuffer car la taille du buffer est ici déterminée par le nombre d'octets lu par readFully. */
- } while (req->outgoing.remaining != 0);
- close(ficD); /* Fermeture du descripteur de fichier. */
- }
- else /* S'il s'agit d'un exécutable : */
- {
- execCGI(req);
- }
- }
- }
- }
- else if(strcmp(req->requestMethod,"POST" )==0)/* Si c'est une requête POST. */
- {
- printf("Filename : %s, Uri : %s\n",req->fileName,req->requestUri);
- }
- destroyRequest(req);
- return (void *)0;
- }
- /*--------------------------------------------------------------------------*/
- int main(void)
- {
- int httpSock=-1,r;
- struct sigaction act;
- pthread_t th;
- /* Ecoute sur le port 8080. */
- httpSock=listenTcp(8080);
- if(httpSock==-1)
- {
- perror("listen http" );
- exit(EXIT_FAILURE);
- }
- /* Ignorer une écriture impossible dans un pipe */
- memset(&act,0,sizeof(struct sigaction));
- act.sa_handler=SIG_IGN;
- RESTART_SYSCALL(r,sigaction(SIGPIPE,&act,(struct sigaction *)0));
- for(;;)
- {
- unsigned long ipAddress;
- int portNumber;
- int fd=acceptTcp(httpSock,&ipAddress,&portNumber);
- if(fd==-1)
- {
- perror("acceptTcp" );
- }
- else
- {
- Request * req=createRequest(fd,(SSL_CTX *)0);
- r=pthread_create(&th,(pthread_attr_t *)0,&manageRequest,req);
- if(r)
- {
- fprintf(stderr,"pthread_create: %s\n",strerror(errno));
- destroyRequest(req);
- }
- }
- }
- return EXIT_SUCCESS; /* never reached */
- }
- /*--------------------------------------------------------------------------*/
|
|