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;\">&copy; 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;\">&copy; 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