1 /* 2 * Copyright © 2013 Naim A. 3 * 4 * This file is part of UDPT. 5 * 6 * UDPT is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * UDPT is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with UDPT. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include <iostream> 21 #include <sstream> 22 #include <string> 23 #include <cstring> 24 #include <map> 25 #include "httpserver.hpp" 26 27 using namespace std; 28 29 namespace UDPT 30 { 31 namespace Server 32 { 33 /* HTTPServer */ HTTPServer(uint16_t port,int threads)34 HTTPServer::HTTPServer (uint16_t port, int threads) 35 { 36 int r; 37 SOCKADDR_IN sa; 38 39 this->thread_count = threads; 40 this->threads = new HANDLE[threads]; 41 this->isRunning = false; 42 43 this->rootNode.callback = NULL; 44 45 this->srv = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); 46 if (this->srv == INVALID_SOCKET) 47 { 48 throw ServerException (1, "Failed to create Socket"); 49 } 50 51 sa.sin_addr.s_addr = 0L; 52 sa.sin_family = AF_INET; 53 sa.sin_port = htons (port); 54 55 r = ::bind (this->srv, (SOCKADDR*)&sa, sizeof(sa)); 56 if (r == SOCKET_ERROR) 57 { 58 throw ServerException (2, "Failed to bind socket"); 59 } 60 61 this->isRunning = true; 62 for (int i = 0;i < threads;i++) 63 { 64 #ifdef WIN32 65 this->threads[i] = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)_thread_start, this, 0, NULL); 66 #else 67 pthread_create (&this->threads[i], NULL, &HTTPServer::_thread_start, this); 68 #endif 69 } 70 } 71 72 #ifdef WIN32 _thread_start(LPVOID arg)73 DWORD HTTPServer::_thread_start (LPVOID arg) 74 #else 75 void* HTTPServer::_thread_start (void *arg) 76 #endif 77 { 78 HTTPServer *s = (HTTPServer*)arg; 79 doSrv: 80 try { 81 HTTPServer::handleConnections (s); 82 } catch (ServerException &se) 83 { 84 cerr << "SRV ERR #" << se.getErrorCode() << ": " << se.getErrorMsg () << endl; 85 goto doSrv; 86 } 87 return 0; 88 } 89 handleConnections(HTTPServer * server)90 void HTTPServer::handleConnections (HTTPServer *server) 91 { 92 int r; 93 #ifdef WIN32 94 int addrSz; 95 #else 96 socklen_t addrSz; 97 #endif 98 SOCKADDR_IN addr; 99 SOCKET cli; 100 101 while (server->isRunning) 102 { 103 r = listen (server->srv, 50); 104 if (r == SOCKET_ERROR) 105 { 106 #ifdef WIN32 107 Sleep (500); 108 #else 109 sleep (1); 110 #endif 111 continue; 112 } 113 addrSz = sizeof addr; 114 cli = accept (server->srv, (SOCKADDR*)&addr, &addrSz); 115 if (cli == INVALID_SOCKET) 116 continue; 117 118 Response resp (cli); // doesn't throw exceptions. 119 120 try { 121 Request req (cli, &addr); // may throw exceptions. 122 reqCallback *cb = getRequestHandler (&server->rootNode, req.getPath()); 123 if (cb == NULL) 124 { 125 // error 404 126 resp.setStatus (404, "Not Found"); 127 resp.addHeader ("Content-Type", "text/html; charset=US-ASCII"); 128 stringstream stream; 129 stream << "<html>"; 130 stream << "<head><title>Not Found</title></head>"; 131 stream << "<body><h1>Not Found</h1><div>The server couldn't find the request resource.</div><br /><hr /><div style=\"font-size:small;text-align:center;\">© 2013 Naim A. | <a href=\"http://udpt.googlecode.com/\">The UDPT Project</a></div></body>"; 132 stream << "</html>"; 133 string str = stream.str(); 134 resp.write (str.c_str(), str.length()); 135 } 136 else 137 { 138 try { 139 cb (server, &req, &resp); 140 } catch (...) 141 { 142 resp.setStatus(500, "Internal Server Error"); 143 resp.addHeader ("Content-Type", "text/html; charset=US-ASCII"); 144 stringstream stream; 145 stream << "<html>"; 146 stream << "<head><title>Internal Server Error</title></head>"; 147 stream << "<body><h1>Internal Server Error</h1><div>An Error Occurred while trying to process your request.</div><br /><hr /><div style=\"font-size:small;text-align:center;\">© 2013 Naim A. | <a href=\"http://udpt.googlecode.com/\">The UDPT Project</a></div></body>"; 148 stream << "</html>"; 149 string str = stream.str(); 150 resp.write (str.c_str(), str.length()); 151 } 152 } 153 resp.finalize(); 154 } catch (ServerException &e) 155 { 156 // Error 400 Bad Request! 157 } 158 159 closesocket (cli); 160 } 161 } 162 addApp(list<string> * path,reqCallback * cb)163 void HTTPServer::addApp (list<string> *path, reqCallback *cb) 164 { 165 list<string>::iterator it = path->begin(); 166 appNode *node = &this->rootNode; 167 while (it != path->end()) 168 { 169 map<string, appNode>::iterator se; 170 se = node->nodes.find (*it); 171 if (se == node->nodes.end()) 172 { 173 node->nodes[*it].callback = NULL; 174 } 175 node = &node->nodes[*it]; 176 it++; 177 } 178 node->callback = cb; 179 } 180 getRequestHandler(appNode * node,list<string> * path)181 HTTPServer::reqCallback* HTTPServer::getRequestHandler (appNode *node, list<string> *path) 182 { 183 appNode *cn = node; 184 list<string>::iterator it = path->begin(), 185 end = path->end(); 186 map<string, appNode>::iterator n; 187 while (true) 188 { 189 if (it == end) 190 { 191 return cn->callback; 192 } 193 194 n = cn->nodes.find (*it); 195 if (n == cn->nodes.end()) 196 return NULL; // node not found! 197 cn = &n->second; 198 199 it++; 200 } 201 return NULL; 202 } 203 setData(string k,void * d)204 void HTTPServer::setData(string k, void *d) 205 { 206 this->customData[k] = d; 207 } 208 getData(string k)209 void* HTTPServer::getData(string k) 210 { 211 map<string, void*>::iterator it = this->customData.find(k); 212 if (it == this->customData.end()) 213 return NULL; 214 return it->second; 215 } 216 ~HTTPServer()217 HTTPServer::~HTTPServer () 218 { 219 if (this->srv != INVALID_SOCKET) 220 closesocket (this->srv); 221 222 if (this->isRunning) 223 { 224 for (int i = 0;i < this->thread_count;i++) 225 { 226 #ifdef WIN32 227 TerminateThread (this->threads[i], 0x00); 228 #else 229 pthread_detach (this->threads[i]); 230 pthread_cancel (this->threads[i]); 231 #endif 232 } 233 } 234 235 delete[] this->threads; 236 } 237 238 /* HTTPServer::Request */ Request(SOCKET cli,const SOCKADDR_IN * addr)239 HTTPServer::Request::Request (SOCKET cli, const SOCKADDR_IN *addr) 240 { 241 this->conn = cli; 242 this->addr = addr; 243 244 this->parseRequest (); 245 } 246 nextReqLine(int & cPos,char * buff,int len)247 inline static char* nextReqLine (int &cPos, char *buff, int len) 248 { 249 for (int i = cPos;i < len - 1;i++) 250 { 251 if (buff[i] == '\r' && buff[i + 1] == '\n') 252 { 253 buff[i] = '\0'; 254 255 int r = cPos; 256 cPos = i + 2; 257 return (buff + r); 258 } 259 } 260 261 return (buff + len); // end 262 } 263 parseURL(string request,list<string> * path,map<string,string> * params)264 inline void parseURL (string request, list<string> *path, map<string, string> *params) 265 { 266 string::size_type p; 267 string query, url; 268 p = request.find ('?'); 269 if (p == string::npos) 270 { 271 p = request.length(); 272 } 273 else 274 { 275 query = request.substr (p + 1); 276 } 277 url = request.substr (0, p); 278 279 path->clear (); 280 string::size_type s, e; 281 s = 0; 282 while (true) 283 { 284 e = url.find ('/', s); 285 if (e == string::npos) 286 e = url.length(); 287 288 string x = url.substr (s, e - s); 289 if (!(x.length() == 0 || x == ".")) 290 { 291 if (x == "..") 292 { 293 if (path->empty()) 294 throw ServerException (1, "Hack attempt"); 295 else 296 path->pop_back (); 297 } 298 path->push_back (x); 299 } 300 301 if (e == url.length()) 302 break; 303 s = e + 1; 304 } 305 306 string::size_type vS, vE, kS, kE; 307 vS = vE = kS = kE = 0; 308 while (kS < query.length()) 309 { 310 kE = query.find ('=', kS); 311 if (kE == string::npos) break; 312 vS = kE + 1; 313 vE = query.find ('&', vS); 314 if (vE == string::npos) vE = query.length(); 315 316 params->insert (pair<string, string>( query.substr (kS, kE - kS), query.substr (vS, vE - vS) )); 317 318 kS = vE + 1; 319 } 320 } 321 setCookies(string & data,map<string,string> * cookies)322 inline void setCookies (string &data, map<string, string> *cookies) 323 { 324 string::size_type kS, kE, vS, vE; 325 kS = 0; 326 while (kS < data.length ()) 327 { 328 kE = data.find ('=', kS); 329 if (kE == string::npos) 330 break; 331 vS = kE + 1; 332 vE = data.find ("; ", vS); 333 if (vE == string::npos) 334 vE = data.length(); 335 336 (*cookies) [data.substr (kS, kE-kS)] = data.substr (vS, vE-vS); 337 338 kS = vE + 2; 339 } 340 } 341 parseRequest()342 void HTTPServer::Request::parseRequest () 343 { 344 char buffer [REQUEST_BUFFER_SIZE]; 345 int r; 346 r = recv (this->conn, buffer, REQUEST_BUFFER_SIZE, 0); 347 if (r == REQUEST_BUFFER_SIZE) 348 throw ServerException (1, "Request Size too big."); 349 if (r <= 0) 350 throw ServerException (2, "Socket Error"); 351 352 char *cLine; 353 int n = 0; 354 int pos = 0; 355 string::size_type p; 356 while ( (cLine = nextReqLine (pos, buffer, r)) < (buffer + r)) 357 { 358 string line = string (cLine); 359 if (line.length() == 0) break; // CRLF CRLF = end of headers. 360 n++; 361 362 if (n == 1) 363 { 364 string::size_type uS, uE; 365 p = line.find (' '); 366 if (p == string::npos) 367 throw ServerException (5, "Malformed request method"); 368 uS = p + 1; 369 this->requestMethod.str = line.substr (0, p); 370 371 if (this->requestMethod.str == "GET") 372 this->requestMethod.rm = RM_GET; 373 else if (this->requestMethod.str == "POST") 374 this->requestMethod.rm = RM_POST; 375 else 376 this->requestMethod.rm = RM_UNKNOWN; 377 378 uE = uS; 379 while (p < line.length()) 380 { 381 if (p == string::npos) 382 break; 383 p = line.find (' ', p + 1); 384 if (p == string::npos) 385 break; 386 uE = p; 387 } 388 if (uE + 1 >= line.length()) 389 throw ServerException (6, "Malformed request"); 390 string httpVersion = line.substr (uE + 1); 391 392 393 parseURL (line.substr (uS, uE - uS), &this->path, &this->params); 394 } 395 else 396 { 397 p = line.find (": "); 398 if (p == string::npos) 399 throw ServerException (4, "Malformed headers"); 400 string key = line.substr (0, p); 401 string value = line.substr (p + 2); 402 if (key != "Cookie") 403 this->headers.insert(pair<string, string>( key, value)); 404 else 405 setCookies (value, &this->cookies); 406 } 407 } 408 if (n == 0) 409 throw ServerException (3, "No Request header."); 410 } 411 getPath()412 list<string>* HTTPServer::Request::getPath () 413 { 414 return &this->path; 415 } 416 getParam(const string key)417 string HTTPServer::Request::getParam (const string key) 418 { 419 map<string, string>::iterator it = this->params.find (key); 420 if (it == this->params.end()) 421 return ""; 422 else 423 return it->second; 424 } 425 getHeader(const string name)426 multimap<string, string>::iterator HTTPServer::Request::getHeader (const string name) 427 { 428 multimap<string, string>::iterator it = this->headers.find (name); 429 return it; 430 } 431 getRequestMethod()432 HTTPServer::Request::RequestMethod HTTPServer::Request::getRequestMethod () 433 { 434 return this->requestMethod.rm; 435 } 436 getRequestMethodStr()437 string HTTPServer::Request::getRequestMethodStr () 438 { 439 return this->requestMethod.str; 440 } 441 getCookie(const string name)442 string HTTPServer::Request::getCookie (const string name) 443 { 444 map<string, string>::iterator it = this->cookies.find (name); 445 if (it == this->cookies.end()) 446 return ""; 447 else 448 return it->second; 449 } 450 getAddress()451 const SOCKADDR_IN* HTTPServer::Request::getAddress () 452 { 453 return this->addr; 454 } 455 456 /* HTTPServer::Response */ Response(SOCKET cli)457 HTTPServer::Response::Response (SOCKET cli) 458 { 459 this->conn = cli; 460 461 setStatus (200, "OK"); 462 } 463 setStatus(int c,const string m)464 void HTTPServer::Response::setStatus (int c, const string m) 465 { 466 this->status_code = c; 467 this->status_msg = m; 468 } 469 addHeader(string key,string value)470 void HTTPServer::Response::addHeader (string key, string value) 471 { 472 this->headers.insert (pair<string, string>(key, value)); 473 } 474 write(const char * data,int len)475 void HTTPServer::Response::write (const char *data, int len) 476 { 477 if (len < 0) 478 len = strlen (data); 479 msg.write(data, len); 480 } 481 finalize()482 void HTTPServer::Response::finalize () 483 { 484 stringstream x; 485 x << "HTTP/1.1 " << this->status_code << " " << this->status_msg << "\r\n"; 486 multimap<string, string>::iterator it, end; 487 end = this->headers.end(); 488 for (it = this->headers.begin(); it != end;it++) 489 { 490 x << it->first << ": " << it->second << "\r\n"; 491 } 492 x << "Connection: Close\r\n"; 493 x << "Content-Length: " << this->msg.tellp() << "\r\n"; 494 x << "Server: udpt\r\n"; 495 x << "\r\n"; 496 x << this->msg.str(); 497 498 // write to socket 499 send (this->conn, x.str().c_str(), x.str().length(), 0); 500 } 501 502 }; 503 }; 504