HttpServer.cpp

00001 /*********************************************************************** 00002 * * 00003 * ViTooKi * 00004 * * 00005 * title: HttpServer.cpp * 00006 * * 00007 * * 00008 * * 00009 * ITEC institute of the University of Klagenfurt (Austria) * 00010 * http://www.itec.uni-klu.ac.at * 00011 * * 00012 * * 00013 * For more information visit the ViTooKi homepage: * 00014 * http://ViTooKi.sourceforge.net * 00015 * vitooki-user@lists.sourceforge.net * 00016 * vitooki-devel@lists.sourceforge.net * 00017 * * 00018 * This file is part of ViTooKi, a free video toolkit. * 00019 * ViTooKi is free software; you can redistribute it and/or * 00020 * modify it under the terms of the GNU General Public License * 00021 * as published by the Free Software Foundation; either version 2 * 00022 * of the License, or (at your option) any later version. * 00023 * * 00024 * This program is distributed in the hope that it will be useful, * 00025 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00026 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00027 * GNU General Public License for more details. * 00028 * * 00029 * You should have received a copy of the GNU General Public License * 00030 * along with this program; if not, write to the Free Software * 00031 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, * 00032 * MA 02111-1307, USA. * 00033 * * 00034 ***********************************************************************/ 00035 00036 /*********************************************************************** 00037 * * 00038 * REVISION HISTORY: * 00039 * * 00040 * * 00041 * * 00042 ***********************************************************************/ 00043 00044 /*********************************************************************** 00045 * Author: Klaus Schoeffmann * 00046 * original code by Michael Kropfberger (from Mohican :) * 00047 ************************************************************************/ 00048 00049 00050 #include "HttpServer.hpp" 00051 00052 00053 #ifdef WIN32 00054 //dummy implementations 00055 HttpServer::HttpServer(int port, const char *rootdir, const char *cgidir){} 00056 HttpServer::~HttpServer(){} 00057 void HttpServer::stop(){} 00058 char *HttpServer::findExtension(char *uri,char *ext){ return NULL; } 00059 int HttpServer::runCGI(int sock, char *uri, int method, char *body, int body_len){ return -1; } 00060 int HttpServer::sendResource(int sock, char *uri){ return -1; } 00061 void HttpServer::run(){} 00062 #else 00063 00064 /***********************************************************************/ 00065 HttpServer::HttpServer(int port, const char *rootdir, const char *cgidir) { 00066 this->port = port; 00067 this->rootdir = new char[strlen(rootdir)]; 00068 strcpy(this->rootdir, rootdir); 00069 this->cgidir = new char[strlen(cgidir)]; 00070 strcpy(this->cgidir, cgidir); 00071 00072 this->shouldBeActive = true; 00073 } 00074 00075 00076 /***********************************************************************/ 00077 HttpServer::~HttpServer() { 00078 stop(); 00079 delete this->rootdir; 00080 delete this->cgidir; 00081 } 00082 00083 00084 /***********************************************************************/ 00085 void HttpServer::stop() { 00086 this->shouldBeActive = false; 00087 00088 int MAXWAITTIME = 10; //wait a maximum of 10 seconds for mohican to exit! 00089 struct timeval startTime, currTime; 00090 gettimeofday(&startTime, NULL); 00091 00092 while (this->active == true) { 00093 msleep(1); 00094 gettimeofday(&currTime, NULL); 00095 if ((currTime.tv_sec - startTime.tv_sec) > MAXWAITTIME) { 00096 dprintf_full("\nterminating mohican unfriendly"); 00097 close(sock); 00098 this->active = false; 00099 } 00100 } 00101 } 00102 00103 00104 /***********************************************************************/ 00105 char *HttpServer::findExtension(char *uri,char *ext) { 00106 char *u, *e; 00107 00108 u= uri + strlen(uri); 00109 e= ext + strlen(ext); 00110 while ((u-- > uri) && (e-- > ext)) 00111 if (*u != *e) 00112 return NULL; 00113 00114 return u; 00115 } 00116 00117 00118 /***********************************************************************/ 00119 int HttpServer::runCGI(int sock, char *uri, int method, char *body, int body_len) { 00120 char cgi[MAX_LEN]; 00121 char *query; 00122 int fdin[2]; 00123 int fdout[2]; 00124 char buf[MAX_LEN]; 00125 int len; 00126 00127 //make script a local ./cgi-bin/bla.sh path (or do a chroot, which might be even better) 00128 strcpy(cgi,"."); 00129 strcat(cgi, uri); 00130 00131 query=strstr(cgi,"?"); //if no ? is found, NULL is returned! 00132 //extract the middle part of URI, which is the real script name 00133 if (query) { 00134 *query='\0'; //cut off query from cgi-script string 00135 query++; 00136 } 00137 00138 dprintf_full("HttpServer::runCGI: script is #%s# query is #%s# body is #%s#, len %i\n", 00139 cgi, query, body, body_len); 00140 00141 pipe(fdin); 00142 pipe(fdout); 00143 00144 if (fork() == 0) { //child 00145 close(fdin[1]); //close writing fd of fdin 00146 close(fdout[0]); //close reading fd of fdout 00147 dup2(fdin[0],0); //duplicate stdin fd 00148 dup2(fdout[1],1); //duplicate stdout fd 00149 setenv("REQUEST_METHOD",method==METHOD_GET?"GET":"POST",1); // set method GET or POST 00150 if (query) //GET 00151 setenv("QUERY_STRING",query,1); 00152 else { //POST 00153 setenv("QUERY_STRING","",1); //set empty 00154 sprintf(buf,"%i",body_len); 00155 setenv("CONTENT_LENGTH",buf,1); 00156 } 00157 //execl(cgi,cgi,NULL); 00158 execl(cgi,cgi,NULL); 00159 //normally never reached 00160 dprintf_err("HttpServer::runCGI: could not exec %s, errno %i\n",cgi,errno); 00161 _exit(0); 00162 } 00163 00164 00165 close(fdin[0]); //close reading fd of fdin 00166 close(fdout[1]); //close writing fd of fdout 00167 00168 //pass POST data 00169 if (!query){ 00170 write(fdin[1], body, body_len); 00171 write(fdin[1], "\r\n", 2); 00172 } 00173 00174 // send "HTTP/1.0 200 OK" 00175 sprintf(buf,"%s %s\r\n", VERSION,OK); 00176 write(sock,buf,strlen(buf)); 00177 00178 //pass CGI output to client 00179 while((len=read(fdout[0],buf,MAX_LEN)) != 0) { 00180 buf[len]='\0'; 00181 //printf("[%i] script says: #%s#",getpid(),buf); 00182 write(sock,buf,len); 00183 } 00184 close(fdout[0]); 00185 close(fdin[1]); 00186 close(sock); 00187 00188 return 0; 00189 } 00190 00191 00192 00193 /***********************************************************************/ 00194 int HttpServer::sendResource(int sock, char *uri) { 00195 char cmd[MAX_LEN]; 00196 char buf[MAX_LEN]; 00197 char line[MAX_LEN]; 00198 HttpMimeType type; 00199 FILE *f; 00200 long len; 00201 if (uri[strlen(uri)-1] == '/') 00202 sprintf(uri,"%s%s",uri,DEFAULT_PAGE); 00203 00204 // make URI absolute to server root 00205 sprintf(line,"%s/%s",rootdir,uri); 00206 strcpy(uri,line); 00207 00208 cmd[0]='\0'; 00209 if ( (f = fopen(uri,"r")) == NULL) { 00210 /* file not found or not accessible */ 00211 dprintf_full("NOT FOUND %s: 404!!!!\n",uri); 00212 sprintf(line,"%s %s\r\n", VERSION,NOT_FOUND); 00213 strcat(cmd,line); 00214 sprintf(uri,"%s/%s",rootdir,ERR404_PAGE); 00215 if ( (f = fopen(uri,"r")) == NULL) { 00216 printf("%s",uri); 00217 perror(""); 00218 return -1; 00219 } 00220 } else { 00221 /* transferring real data */ 00222 sprintf(cmd,"%s %s\r\n", VERSION,OK); 00223 } 00224 00225 /* calc payload size */ 00226 fseek(f,0,SEEK_END); 00227 len=ftell(f); 00228 fseek(f,0,SEEK_SET); 00229 sprintf(line,"Content-Length: %li\r\n",len); 00230 strcat(cmd,line); 00231 00232 /* find out Mime type */ 00233 type=mime_unknown; 00234 if (findExtension(uri,"html") > 0) 00235 type=mime_html; 00236 else 00237 if (findExtension(uri,"txt") > 0) 00238 type=mime_html; 00239 else 00240 if (findExtension(uri,"gif") > 0) 00241 type=mime_gif; 00242 else 00243 if (findExtension(uri,"jpg") > 0) 00244 type=mime_jpg; 00245 else 00246 if (findExtension(uri,"mp4") > 0) 00247 type=mime_mp4; 00248 else 00249 if (findExtension(uri,"vit") > 0) 00250 type=mime_vit; 00251 else 00252 if (findExtension(uri,"ogg") > 0) 00253 type=mime_ogg; 00254 else 00255 if (findExtension(uri,"mkv") > 0) 00256 type=mime_mkv; 00257 00258 00259 sprintf(line,"Content-Type: %s\r\n\r\n",HttpMimeTypeStrings[type]); 00260 strcat(cmd,line); 00261 00262 //send the header 00263 if (send(sock,cmd,strlen(cmd),0) < 0) { 00264 perror("send"); 00265 return -1; 00266 } 00267 00268 00269 //send the payload 00270 while(!feof(f)) { 00271 if ((len=fread(buf,1,sizeof(buf),f))<0) { 00272 perror("data fread"); 00273 return -1; 00274 } 00275 if (send(sock,buf,len,0) < 0) { 00276 perror("data send"); 00277 return -1; 00278 } 00279 } 00280 00281 fclose(f); 00282 return 0; 00283 } 00284 00285 00286 /***********************************************************************/ 00287 void HttpServer::run() { 00288 struct sockaddr_in sin; 00289 socklen_t len; 00290 int actlen; 00291 char buf[MAX_LEN]; 00292 char cmd[MAX_LEN]; 00293 char uri[MAX_LEN]; 00294 int new_sock; 00295 int method; //either GET or POST 00296 int isCGI = 0; 00297 00298 active = true; 00299 00300 if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { 00301 perror("init socket"); 00302 _exit(1); 00303 } 00304 00305 /* local port */ 00306 bzero((char *)&sin, sizeof(sin)); 00307 sin.sin_family = AF_INET; 00308 sin.sin_addr.s_addr = INADDR_ANY; 00309 sin.sin_port = htons(port); 00310 if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 00311 perror("init bind"); 00312 _exit(1); 00313 } 00314 00315 if (listen(sock,0) < 0) { 00316 perror("listen"); 00317 _exit(1); 00318 } 00319 00320 fd_set rfds; 00321 struct timeval tv; 00322 00323 /* big server loop */ 00324 dprintf_full("HttpServer: mohican lives :) ... accepting connections on port %i...\n", port); 00325 while(this->shouldBeActive) { 00326 len=sizeof(sin); 00327 00328 FD_ZERO(&rfds); 00329 FD_SET(sock, &rfds); 00330 tv.tv_sec = 0; 00331 tv.tv_usec = 50000; 00332 int retval = select(sock+1, &rfds, NULL, NULL, &tv); 00333 if (retval == -1) { 00334 perror("select"); 00335 _exit(1); 00336 } else if (retval && FD_ISSET(sock, &rfds)) { 00337 00338 if ((new_sock = accept(sock, (struct sockaddr*)&sin,&len)) < 0) { 00339 perror("accept"); 00340 _exit(1); 00341 } 00342 00343 if (fork() ==0) { // child 00344 /* recv full client request */ 00345 len = 0; 00346 do { 00347 if ((actlen=recv(new_sock, buf+len, sizeof(buf)-len,0)) < 0) { 00348 perror("recv"); 00349 _exit(1); 00350 } 00351 len += actlen; 00352 } while ( (strstr(buf,"\r\n\r\n") == NULL) || (len >= sizeof(buf))); 00353 00354 if (len < sizeof(buf)) 00355 buf[len]='\0'; 00356 else 00357 buf[sizeof(buf)-1]='\0'; 00358 00359 if (strncmp(buf,"GET",3) == 0) { 00360 method=METHOD_GET; 00361 } else if (strncmp(buf,"POST",4) == 0) { 00362 method=METHOD_POST; 00363 } else { 00364 sprintf(cmd,"%s %s\r\n\r\n",VERSION,NOT_IMPL); 00365 if (send(new_sock,cmd,strlen(cmd),0) < 0) { 00366 perror("send"); 00367 _exit(1); 00368 } 00369 } 00370 00371 if (strstr(buf,"HTTP/1.") != NULL) { 00372 sscanf(buf,"%*s %s HTTP/1.",uri); 00373 } else 00374 strcpy(uri,DEFAULT_PAGE); 00375 00376 char *tmp1 = strstr(buf, cgidir); 00377 char *tmp2 = strstr(buf, "HTTP/1."); 00378 //not enough: (cgidir could be included in "Referer:") 00379 //if (strstr(buf,cgidir) != NULL) 00380 if (tmp1 != NULL && tmp2 != NULL && tmp1 < tmp2) 00381 isCGI=1; 00382 else 00383 isCGI=0; 00384 00385 //access logging 00386 const time_t t = time(NULL); 00387 struct tm *date = localtime(&t); 00388 char dc[MAX_STR_LEN]; 00389 //FIXME: to be apache-log conformant, the month shouldnt be a number, but Jan/Feb/Mar/... 00390 sprintf(dc,"%02i/%02i/%04i:%02i:%02i:%02i +%04li",date->tm_mday,date->tm_mon+1,date->tm_year+1900, 00391 date->tm_hour, date->tm_min, date->tm_sec, 00392 date->tm_gmtoff/60/60*100); 00393 dprintf_full("HttpServer ACCESS_LOG: %s - - [%s] \"GET %s HTTP/1.1\"\n",inet_ntoa(sin.sin_addr),dc,uri); 00394 00395 if (isCGI) { 00396 //cut off headers, keep body 00397 char *body = strstr(buf,"\r\n\r\n")+4; 00398 int body_len = len - (body-buf); 00399 if (runCGI(new_sock,uri,method,body, body_len) != 0) 00400 dprintf_err("HttpServer::run: some error occured when executing CGI-Script... \n"); 00401 } else 00402 if (method==METHOD_GET) { 00403 if (sendResource(new_sock,uri) != 0) 00404 dprintf_err("HttpServer::run: some error occured when sending content... \n"); 00405 } else { 00406 dprintf_err("HttpServer::run: Unknown Method or POST on non-CGI-File!! \n"); 00407 } 00408 00409 close(new_sock); 00410 _exit(0); 00411 } //fork child 00412 00413 //parent 00414 close(new_sock); 00415 } 00416 } //server while 00417 close(sock); 00418 active = false; 00419 } 00420 #endif 00421