Recreated HTTP server...
This commit is contained in:
parent
6666d795e3
commit
876f2da5b7
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright © 2012,2013 Naim A.
|
||||
* Copyright © 2013 Naim A.
|
||||
*
|
||||
* This file is part of UDPT.
|
||||
*
|
||||
|
@ -17,315 +17,486 @@
|
|||
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "httpserver.hpp"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include "../tools.h"
|
||||
#include <map>
|
||||
#include "httpserver.hpp"
|
||||
|
||||
#define REQBUFFSZ 2048 // enough for all headers.
|
||||
using namespace std;
|
||||
|
||||
namespace UDPT
|
||||
{
|
||||
namespace API
|
||||
namespace Server
|
||||
{
|
||||
/* HTTPServer */
|
||||
HTTPServer::HTTPServer (uint16_t port, int threads)
|
||||
{
|
||||
int r;
|
||||
SOCKADDR_IN sa;
|
||||
|
||||
this->thread_count = threads;
|
||||
this->threads = new HANDLE [threads];
|
||||
this->threads = new HANDLE[threads];
|
||||
this->isRunning = false;
|
||||
|
||||
SOCKADDR_IN endpoint;
|
||||
endpoint.sin_family = AF_INET;
|
||||
endpoint.sin_port = m_hton16(port);
|
||||
endpoint.sin_addr.s_addr = 0L;
|
||||
|
||||
this->sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (this->sock == INVALID_SOCKET)
|
||||
throw APIException("Invalid Socket");
|
||||
|
||||
r = bind(this->sock, (SOCKADDR*)&endpoint, sizeof(SOCKADDR_IN));
|
||||
if (r == SOCKET_ERROR)
|
||||
throw APIException("Failed to bind port.");
|
||||
|
||||
this->isRunning = true;
|
||||
|
||||
this->rootNode.name = "";
|
||||
this->rootNode.children.clear();
|
||||
this->rootNode.callback = NULL;
|
||||
|
||||
this->srv = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (this->srv == INVALID_SOCKET)
|
||||
{
|
||||
throw ServerException (1, "Failed to create Socket");
|
||||
}
|
||||
|
||||
sa.sin_addr.s_addr = 0L;
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons (port);
|
||||
|
||||
r = bind (this->srv, (SOCKADDR*)&sa, sizeof(sa));
|
||||
if (r == SOCKET_ERROR)
|
||||
{
|
||||
throw ServerException (2, "Failed to bind socket");
|
||||
}
|
||||
|
||||
this->isRunning = true;
|
||||
for (int i = 0;i < threads;i++)
|
||||
{
|
||||
#ifdef WIN32
|
||||
this->threads[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&HTTPServer::doServe, (LPVOID)this, 0, NULL);
|
||||
this->threads[i] = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)_thread_start, this, 0, NULL);
|
||||
#else
|
||||
pthread_create (&this->threads[0], NULL, HTTPServer::doServe, (void*)this);
|
||||
pthread_create (&this->threads[i], NULL, &HTTPServer::_thread_start, this);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
DWORD HTTPServer::doServe (LPVOID arg)
|
||||
DWORD HTTPServer::_thread_start (LPVOID arg)
|
||||
#else
|
||||
void* HTTPServer::doServe (void* arg)
|
||||
void* HTTPServer::_thread_start (void *arg)
|
||||
#endif
|
||||
{
|
||||
HTTPServer *srv = (HTTPServer*)arg;
|
||||
HTTPServer *s = (HTTPServer*)arg;
|
||||
doSrv:
|
||||
try {
|
||||
HTTPServer::handleConnections (s);
|
||||
} catch (ServerException &se)
|
||||
{
|
||||
cerr << "SRV ERR #" << se.getErrorCode() << ": " << se.getErrorMsg () << endl;
|
||||
goto doSrv;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void HTTPServer::handleConnections (HTTPServer *server)
|
||||
{
|
||||
int r;
|
||||
SOCKADDR addr;
|
||||
|
||||
#ifdef linux
|
||||
socklen_t addrSz;
|
||||
#else
|
||||
#ifdef WIN32
|
||||
int addrSz;
|
||||
#else
|
||||
socklen_t addrSz;
|
||||
#endif
|
||||
SOCKADDR_IN addr;
|
||||
SOCKET cli;
|
||||
|
||||
addrSz = sizeof (addr);
|
||||
SOCKET conn;
|
||||
|
||||
while (srv->isRunning)
|
||||
while (server->isRunning)
|
||||
{
|
||||
r = listen (srv->sock, SOMAXCONN);
|
||||
r = listen (server->srv, 50);
|
||||
if (r == SOCKET_ERROR)
|
||||
throw APIException("Failed to listen");
|
||||
|
||||
addrSz = sizeof (addr);
|
||||
|
||||
conn = accept(srv->sock, &addr, &addrSz);
|
||||
if (conn == INVALID_SOCKET)
|
||||
{
|
||||
#ifdef WIN32
|
||||
Sleep (500);
|
||||
#else
|
||||
sleep (1);
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
addrSz = sizeof addr;
|
||||
cli = accept (server->srv, (SOCKADDR*)&addr, &addrSz);
|
||||
if (cli == INVALID_SOCKET)
|
||||
continue;
|
||||
|
||||
Response resp (cli); // doesn't throw exceptions.
|
||||
|
||||
try {
|
||||
Request req = Request (conn, &addr);
|
||||
Response resp = Response (conn);
|
||||
|
||||
HTTPServer::handleConnection(srv, &req, &resp);
|
||||
} catch (...) {
|
||||
cout << "ERR OCC" << endl;
|
||||
}
|
||||
closesocket(conn);
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
return 0;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
void HTTPServer::handleConnection (HTTPServer *srv, Request *req, Response *resp)
|
||||
Request req (cli, &addr); // may throw exceptions.
|
||||
reqCallback *cb = getRequestHandler (&server->rootNode, req.getPath());
|
||||
if (cb == NULL)
|
||||
{
|
||||
// follow path...
|
||||
serveNode *cNode = &srv->rootNode;
|
||||
list<string>::iterator it;
|
||||
for (it = req->path.begin();(it != req->path.end() && cNode != NULL);it++)
|
||||
{
|
||||
if ((*it).length() == 0)
|
||||
continue; // same node.
|
||||
|
||||
map<string, serveNode>::iterator np;
|
||||
np = cNode->children.find((*it));
|
||||
if (np == srv->rootNode.children.end())
|
||||
{
|
||||
cNode = NULL;
|
||||
break;
|
||||
// error 404
|
||||
resp.setStatus (404, "Not Found");
|
||||
resp.addHeader ("Content-Type", "text/html; charset=US-ASCII");
|
||||
stringstream stream;
|
||||
stream << "<html>";
|
||||
stream << "<head><title>Not Found</title></head>";
|
||||
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. | ContactMe server</div></body>";
|
||||
stream << "</html>";
|
||||
string str = stream.str();
|
||||
resp.write (str.c_str(), str.length());
|
||||
}
|
||||
else
|
||||
cNode = &np->second;
|
||||
}
|
||||
|
||||
if (cNode->callback != NULL)
|
||||
cNode->callback (req, resp);
|
||||
else
|
||||
{
|
||||
// TODO: add HTTP error handler (404 NOT FOUND...)
|
||||
cout << "Page Not Found" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
list<string> HTTPServer::split (const string str, const string del, int limit)
|
||||
try {
|
||||
cb (server, &req, &resp);
|
||||
} catch (...)
|
||||
{
|
||||
list<string> lst;
|
||||
// error 500
|
||||
}
|
||||
}
|
||||
} catch (ServerException &e)
|
||||
{
|
||||
// Error 400 Bad Request!
|
||||
}
|
||||
|
||||
unsigned s, e;
|
||||
s = e = 0;
|
||||
closesocket (cli);
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPServer::addApp (list<string> *path, reqCallback *cb)
|
||||
{
|
||||
list<string>::iterator it = path->begin();
|
||||
appNode *node = &this->rootNode;
|
||||
while (it != path->end())
|
||||
{
|
||||
map<string, appNode>::iterator se;
|
||||
se = node->nodes.find (*it);
|
||||
if (se == node->nodes.end())
|
||||
{
|
||||
node->nodes[*it].callback = NULL;
|
||||
}
|
||||
node = &node->nodes[*it];
|
||||
it++;
|
||||
}
|
||||
node->callback = cb;
|
||||
}
|
||||
|
||||
HTTPServer::reqCallback* HTTPServer::getRequestHandler (appNode *node, list<string> *path)
|
||||
{
|
||||
appNode *cn = node;
|
||||
list<string>::iterator it = path->begin(),
|
||||
end = path->end();
|
||||
map<string, appNode>::iterator n;
|
||||
while (true)
|
||||
{
|
||||
e = str.find(del, s);
|
||||
|
||||
if (e == string::npos || limit - 1 == 0)
|
||||
e = str.length();
|
||||
|
||||
lst.push_back(str.substr(s, e - s));
|
||||
|
||||
if (e >= str.length() || limit - 1 == 0)
|
||||
break;
|
||||
s = e + del.length();
|
||||
limit--;
|
||||
}
|
||||
|
||||
return lst;
|
||||
}
|
||||
|
||||
void HTTPServer::addApplication (const string path, srvCallback *callback)
|
||||
if (it == end)
|
||||
{
|
||||
list<string> p = split (path, "/");
|
||||
list<string>::iterator it;
|
||||
|
||||
serveNode *node = &this->rootNode;
|
||||
|
||||
for (it = p.begin();it != p.end();it++)
|
||||
{
|
||||
if ((*it).length() == 0)
|
||||
continue; // same node...
|
||||
|
||||
node = &node->children[*it];
|
||||
node->name = *it;
|
||||
}
|
||||
node->callback = callback;
|
||||
return cn->callback;
|
||||
}
|
||||
|
||||
HTTPServer::~HTTPServer()
|
||||
n = cn->nodes.find (*it);
|
||||
if (n == cn->nodes.end())
|
||||
return NULL; // node not found!
|
||||
cn = &n->second;
|
||||
|
||||
it++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HTTPServer::~HTTPServer ()
|
||||
{
|
||||
if (this->srv != INVALID_SOCKET)
|
||||
closesocket (this->srv);
|
||||
|
||||
if (this->isRunning)
|
||||
{
|
||||
this->isRunning = false;
|
||||
closesocket(this->sock);
|
||||
for (int i = 0;i < this->thread_count;i++)
|
||||
{
|
||||
#ifdef WIN32
|
||||
TerminateThread(this->threads[i], 0);
|
||||
TerminateThread (this->threads[i], 0x00);
|
||||
#else
|
||||
pthread_detach (this->threads[i]);
|
||||
pthread_cancel (this->threads[i]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
delete[] this->threads;
|
||||
cout << "ST" << endl;
|
||||
}
|
||||
|
||||
|
||||
HTTPServer::Request::Request(SOCKET sock, const SOCKADDR *sa)
|
||||
/* HTTPServer::Request */
|
||||
HTTPServer::Request::Request (SOCKET cli, const SOCKADDR_IN *addr)
|
||||
{
|
||||
this->sock = sock;
|
||||
this->sock_addr = sa;
|
||||
this->conn = cli;
|
||||
this->addr = addr;
|
||||
|
||||
this->loadAndParse();
|
||||
this->parseRequest ();
|
||||
}
|
||||
|
||||
void HTTPServer::Request::loadAndParse ()
|
||||
inline static char* nextReqLine (int &cPos, char *buff, int len)
|
||||
{
|
||||
char buffer [REQBUFFSZ];
|
||||
int r;
|
||||
|
||||
this->httpVersion.major = 0;
|
||||
this->httpVersion.minor = 0;
|
||||
this->requestMethod = RM_UNKNOWN;
|
||||
this->path.clear();
|
||||
this->headers.clear();
|
||||
this->query.clear ();
|
||||
this->str_requestMethod = "";
|
||||
|
||||
r = recv (this->sock, buffer, REQBUFFSZ, 0);
|
||||
if (r <= 0)
|
||||
throw APIException("No data received from client.");
|
||||
|
||||
string request = string (buffer);
|
||||
list<string> lines = HTTPServer::split(request, "\r\n");
|
||||
list<string>::iterator it, begin, end;
|
||||
begin = lines.begin();
|
||||
end = lines.end();
|
||||
for (it = begin;it != end;it++)
|
||||
for (int i = cPos;i < len - 1;i++)
|
||||
{
|
||||
if (it == begin)
|
||||
if (buff[i] == '\r' && buff[i + 1] == '\n')
|
||||
{
|
||||
list<string> hLine = HTTPServer::split(*it, " ");
|
||||
if (hLine.size() < 3)
|
||||
throw APIException("Bad Request");
|
||||
this->str_requestMethod = hLine.front();
|
||||
string httpVersion = hLine.back();
|
||||
if (strncmp(httpVersion.c_str(), "HTTP/", 5) != 0)
|
||||
throw APIException("Unsupported HTTP Version");
|
||||
string vn = httpVersion.substr(5);
|
||||
this->httpVersion.major = atoi (vn.substr(0, vn.find('.')).c_str());
|
||||
this->httpVersion.minor = atoi (vn.substr(vn.find('.') + 1).c_str());
|
||||
buff[i] = '\0';
|
||||
|
||||
hLine.pop_front();
|
||||
hLine.pop_back();
|
||||
string path;
|
||||
bool isF = true;
|
||||
while (!hLine.empty())
|
||||
{
|
||||
if (isF)
|
||||
isF = false;
|
||||
else
|
||||
path.append(" ");
|
||||
path.append(hLine.front());
|
||||
hLine.pop_front();
|
||||
}
|
||||
|
||||
list<string> parts = HTTPServer::split(path, "?", 2);
|
||||
if (!parts.empty())
|
||||
{
|
||||
this->path = HTTPServer::split(parts.front(), "/");
|
||||
parts.pop_front();
|
||||
}
|
||||
if (!parts.empty())
|
||||
{
|
||||
string qData = parts.front();
|
||||
parts.pop_front();
|
||||
|
||||
string::size_type sK, sV, eK, eV;
|
||||
sK = sV = eK = eV = 0;
|
||||
|
||||
while (sK < qData.length())
|
||||
{
|
||||
eK = qData.find('=', sK);
|
||||
if (eK == string::npos) // not valid key
|
||||
break;
|
||||
sV = eK + 1;
|
||||
eV = qData.find('&', sV);
|
||||
if (eV == string::npos)
|
||||
eV = qData.length();
|
||||
|
||||
this->query [qData.substr(sK, eK - sK)] = qData.substr(sV, eV - sV);
|
||||
|
||||
if (eV >= qData.length())
|
||||
break;
|
||||
sK = eV + 1;
|
||||
int r = cPos;
|
||||
cPos = i + 2;
|
||||
return (buff + r);
|
||||
}
|
||||
}
|
||||
|
||||
return (buff + len); // end
|
||||
}
|
||||
else
|
||||
|
||||
inline void parseURL (string request, list<string> *path, map<string, string> *params)
|
||||
{
|
||||
string::size_type p = (*it).find(": ");
|
||||
string::size_type p;
|
||||
string query, url;
|
||||
p = request.find ('?');
|
||||
if (p == string::npos)
|
||||
continue;
|
||||
this->headers.insert(pair<string,string>(
|
||||
(*it).substr(0, p),
|
||||
(*it).substr(p+2)
|
||||
));
|
||||
{
|
||||
p = request.length();
|
||||
}
|
||||
else
|
||||
{
|
||||
query = request.substr (p + 1);
|
||||
}
|
||||
url = request.substr (0, p);
|
||||
|
||||
path->clear ();
|
||||
string::size_type s, e;
|
||||
s = 0;
|
||||
while (true)
|
||||
{
|
||||
e = url.find ('/', s);
|
||||
if (e == string::npos)
|
||||
e = url.length();
|
||||
|
||||
string x = url.substr (s, e - s);
|
||||
if (!(x.length() == 0 || x == "."))
|
||||
{
|
||||
if (x == "..")
|
||||
{
|
||||
if (path->empty())
|
||||
throw ServerException (1, "Hack attempt");
|
||||
else
|
||||
path->pop_back ();
|
||||
}
|
||||
path->push_back (x);
|
||||
}
|
||||
|
||||
if (e == url.length())
|
||||
break;
|
||||
s = e + 1;
|
||||
}
|
||||
|
||||
string::size_type vS, vE, kS, kE;
|
||||
vS = vE = kS = kE = 0;
|
||||
while (kS < query.length())
|
||||
{
|
||||
kE = query.find ('=', kS);
|
||||
if (kE == string::npos) break;
|
||||
vS = kE + 1;
|
||||
vE = query.find ('&', vS);
|
||||
if (vE == string::npos) vE = query.length();
|
||||
|
||||
params->insert (pair<string, string>( query.substr (kS, kE - kS), query.substr (vS, vE - vS) ));
|
||||
|
||||
kS = vE + 1;
|
||||
}
|
||||
}
|
||||
|
||||
HTTPServer::Response::Response(SOCKET sock)
|
||||
inline void setCookies (string &data, map<string, string> *cookies)
|
||||
{
|
||||
this->sock = sock;
|
||||
this->isHeaderSent = false;
|
||||
setStatus(200, "OK");
|
||||
string::size_type kS, kE, vS, vE;
|
||||
kS = 0;
|
||||
while (kS < data.length ())
|
||||
{
|
||||
kE = data.find ('=', kS);
|
||||
if (kE == string::npos)
|
||||
break;
|
||||
vS = kE + 1;
|
||||
vE = data.find ("; ", vS);
|
||||
if (vE == string::npos)
|
||||
vE = data.length();
|
||||
|
||||
(*cookies) [data.substr (kS, kE-kS)] = data.substr (vS, vE-vS);
|
||||
|
||||
kS = vE + 2;
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPServer::Response::setStatus (int code, string msg)
|
||||
void HTTPServer::Request::parseRequest ()
|
||||
{
|
||||
this->statusCode = code;
|
||||
this->statusMsg = msg;
|
||||
char buffer [REQUEST_BUFFER_SIZE];
|
||||
int r;
|
||||
r = recv (this->conn, buffer, REQUEST_BUFFER_SIZE, 0);
|
||||
if (r == REQUEST_BUFFER_SIZE)
|
||||
throw ServerException (1, "Request Size too big.");
|
||||
if (r <= 0)
|
||||
throw ServerException (2, "Socket Error");
|
||||
|
||||
char *cLine;
|
||||
int n = 0;
|
||||
int pos = 0;
|
||||
string::size_type p;
|
||||
while ( (cLine = nextReqLine (pos, buffer, r)) < (buffer + r))
|
||||
{
|
||||
string line = string (cLine);
|
||||
if (line.length() == 0) break; // CRLF CRLF = end of headers.
|
||||
n++;
|
||||
|
||||
if (n == 1)
|
||||
{
|
||||
string::size_type uS, uE;
|
||||
p = line.find (' ');
|
||||
if (p == string::npos)
|
||||
throw ServerException (5, "Malformed request method");
|
||||
uS = p + 1;
|
||||
this->requestMethod.str = line.substr (0, p);
|
||||
|
||||
if (this->requestMethod.str == "GET")
|
||||
this->requestMethod.rm = RM_GET;
|
||||
else if (this->requestMethod.str == "POST")
|
||||
this->requestMethod.rm = RM_POST;
|
||||
else
|
||||
this->requestMethod.rm = RM_UNKNOWN;
|
||||
|
||||
uE = uS;
|
||||
while (p < line.length())
|
||||
{
|
||||
if (p == string::npos)
|
||||
break;
|
||||
p = line.find (' ', p + 1);
|
||||
if (p == string::npos)
|
||||
break;
|
||||
uE = p;
|
||||
}
|
||||
if (uE + 1 >= line.length())
|
||||
throw ServerException (6, "Malformed request");
|
||||
string httpVersion = line.substr (uE + 1);
|
||||
|
||||
|
||||
parseURL (line.substr (uS, uE - uS), &this->path, &this->params);
|
||||
}
|
||||
else
|
||||
{
|
||||
p = line.find (": ");
|
||||
if (p == string::npos)
|
||||
throw ServerException (4, "Malformed headers");
|
||||
string key = line.substr (0, p);
|
||||
string value = line.substr (p + 2);
|
||||
if (key != "Cookie")
|
||||
this->headers.insert(pair<string, string>( key, value));
|
||||
else
|
||||
setCookies (value, &this->cookies);
|
||||
}
|
||||
}
|
||||
if (n == 0)
|
||||
throw ServerException (3, "No Request header.");
|
||||
}
|
||||
|
||||
void HTTPServer::Response::sendRaw (void *data, int sz)
|
||||
list<string>* HTTPServer::Request::getPath ()
|
||||
{
|
||||
send (this->sock, (const char*)data, sz, 0);
|
||||
return &this->path;
|
||||
}
|
||||
|
||||
string HTTPServer::Request::getParam (const string key)
|
||||
{
|
||||
map<string, string>::iterator it = this->params.find (key);
|
||||
if (it == this->params.end())
|
||||
return "";
|
||||
else
|
||||
return it->second;
|
||||
}
|
||||
|
||||
multimap<string, string>::iterator HTTPServer::Request::getHeader (const string name)
|
||||
{
|
||||
multimap<string, string>::iterator it = this->headers.find (name);
|
||||
return it;
|
||||
}
|
||||
|
||||
HTTPServer::Request::RequestMethod HTTPServer::Request::getRequestMethod ()
|
||||
{
|
||||
return this->requestMethod.rm;
|
||||
}
|
||||
|
||||
string HTTPServer::Request::getRequestMethodStr ()
|
||||
{
|
||||
return this->requestMethod.str;
|
||||
}
|
||||
|
||||
string HTTPServer::Request::getCookie (const string name)
|
||||
{
|
||||
map<string, string>::iterator it = this->cookies.find (name);
|
||||
if (it == this->cookies.end())
|
||||
return "";
|
||||
else
|
||||
return it->second;
|
||||
}
|
||||
|
||||
const SOCKADDR_IN* HTTPServer::Request::getAddress ()
|
||||
{
|
||||
return this->addr;
|
||||
}
|
||||
|
||||
/* HTTPServer::Response */
|
||||
HTTPServer::Response::Response (SOCKET cli)
|
||||
{
|
||||
this->conn = cli;
|
||||
this->headerSent = false;
|
||||
|
||||
setStatus (200, "OK");
|
||||
}
|
||||
|
||||
void HTTPServer::Response::setStatus (int c, const string m)
|
||||
{
|
||||
if (headerSent)
|
||||
throw ServerException (2, "Can't set status.");
|
||||
|
||||
this->status_code = c;
|
||||
this->status_msg = m;
|
||||
}
|
||||
|
||||
void HTTPServer::Response::addHeader (string key, string value)
|
||||
{
|
||||
if (headerSent)
|
||||
throw ServerException (1, "Headers already sent.");
|
||||
this->headers.insert (pair<string, string>(key, value));
|
||||
}
|
||||
|
||||
void HTTPServer::Response::write (const char *data, int len)
|
||||
{
|
||||
if (!this->headerSent)
|
||||
sendHeaders ();
|
||||
if (len < 0)
|
||||
len = strlen (data);
|
||||
writeRaw (data, len);
|
||||
}
|
||||
|
||||
void HTTPServer::Response::sendHeaders ()
|
||||
{
|
||||
if (this->headerSent)
|
||||
return;
|
||||
|
||||
this->headerSent = true;
|
||||
|
||||
addHeader ("Server", "ContactMe");
|
||||
|
||||
stringstream stream;
|
||||
stream << "HTTP/1.1 " << this->status_code << " " << this->status_msg << "\r\n";
|
||||
stream << "Connection: Close\r\n";
|
||||
|
||||
multimap<string, string>::iterator it;
|
||||
for (it = this->headers.begin(); it != this->headers.end(); it++)
|
||||
{
|
||||
stream << it->first << ": " << it->second << "\r\n";
|
||||
}
|
||||
this->headers.clear();
|
||||
stream << "\r\n";
|
||||
string str = stream.str();
|
||||
writeRaw (str.c_str(), str.length());
|
||||
}
|
||||
|
||||
bool HTTPServer::Response::isHeadersSent () const
|
||||
{
|
||||
return this->headerSent;
|
||||
}
|
||||
|
||||
int HTTPServer::Response::writeRaw (const char *data, int len)
|
||||
{
|
||||
return send (this->conn, data, len, 0);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright © 2012,2013 Naim A.
|
||||
* Copyright © 2013 Naim A.
|
||||
*
|
||||
* This file is part of UDPT.
|
||||
*
|
||||
|
@ -17,39 +17,48 @@
|
|||
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef HTTPSERVER_HPP_
|
||||
#define HTTPSERVER_HPP_
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include "../multiplatform.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define REQUEST_BUFFER_SIZE 2048
|
||||
|
||||
namespace UDPT
|
||||
{
|
||||
namespace API
|
||||
namespace Server
|
||||
{
|
||||
class APIException
|
||||
class ServerException
|
||||
{
|
||||
public:
|
||||
inline
|
||||
APIException (const string msg)
|
||||
inline ServerException (int ec)
|
||||
{
|
||||
this->msg = msg;
|
||||
this->ec = ec;
|
||||
this->em = NULL;
|
||||
}
|
||||
|
||||
inline
|
||||
const string& getMessage ()
|
||||
inline ServerException (int ec, const char *em)
|
||||
{
|
||||
return this->msg;
|
||||
this->ec = ec;
|
||||
this->em = em;
|
||||
}
|
||||
|
||||
inline const char *getErrorMsg () const
|
||||
{
|
||||
return this->em;
|
||||
}
|
||||
|
||||
inline int getErrorCode () const
|
||||
{
|
||||
return this->ec;
|
||||
}
|
||||
private:
|
||||
string msg;
|
||||
int ec;
|
||||
const char *em;
|
||||
};
|
||||
|
||||
class HTTPServer
|
||||
|
@ -58,78 +67,91 @@ namespace UDPT
|
|||
class Request
|
||||
{
|
||||
public:
|
||||
enum RequestMethod {
|
||||
enum RequestMethod
|
||||
{
|
||||
RM_UNKNOWN = 0,
|
||||
RM_GET = 1
|
||||
RM_GET = 1,
|
||||
RM_POST = 2
|
||||
};
|
||||
|
||||
Request (SOCKET sock, const SOCKADDR *sock_addr);
|
||||
Request (SOCKET, const SOCKADDR_IN *);
|
||||
list<string>* getPath ();
|
||||
|
||||
string getParam (const string key);
|
||||
multimap<string, string>::iterator getHeader (const string name);
|
||||
RequestMethod getRequestMethod ();
|
||||
string getRequestMethodStr ();
|
||||
string getCookie (const string name);
|
||||
const SOCKADDR_IN* getAddress ();
|
||||
|
||||
private:
|
||||
friend class HTTPServer;
|
||||
|
||||
enum RequestMethod requestMethod;
|
||||
string str_requestMethod;
|
||||
const SOCKADDR_IN *addr;
|
||||
SOCKET conn;
|
||||
struct {
|
||||
unsigned int major;
|
||||
unsigned int minor;
|
||||
} httpVersion;
|
||||
SOCKET sock;
|
||||
int major;
|
||||
int minor;
|
||||
} httpVer;
|
||||
struct {
|
||||
string str;
|
||||
RequestMethod rm;
|
||||
} requestMethod;
|
||||
list<string> path;
|
||||
map<string, string> params;
|
||||
map<string, string> cookies;
|
||||
multimap<string, string> headers;
|
||||
list<string> path; // /some/path
|
||||
map<string, string> query; // a=b&c=d
|
||||
const SOCKADDR *sock_addr; // IP address+family
|
||||
|
||||
void loadAndParse ();
|
||||
void parseRequest ();
|
||||
};
|
||||
|
||||
class Response
|
||||
{
|
||||
public:
|
||||
Response (SOCKET sock);
|
||||
|
||||
void sendRaw (void*, int);
|
||||
void setStatus (int code, string msg);
|
||||
Response (SOCKET conn);
|
||||
|
||||
void setStatus (int, const string);
|
||||
void addHeader (string key, string value);
|
||||
void sendHeaders ();
|
||||
int writeRaw (const char *data, int len);
|
||||
void write (const char *data, int len = -1);
|
||||
bool isHeadersSent () const;
|
||||
private:
|
||||
friend class HTTPServer;
|
||||
SOCKET sock;
|
||||
bool isHeaderSent;
|
||||
string statusMsg;
|
||||
int statusCode;
|
||||
SOCKET conn;
|
||||
int status_code;
|
||||
string status_msg;
|
||||
multimap<string, string> headers;
|
||||
bool headerSent;
|
||||
};
|
||||
|
||||
typedef int (srvCallback) (Request *, Response *);
|
||||
typedef void (reqCallback)(HTTPServer*,Request*,Response*);
|
||||
|
||||
HTTPServer (uint16_t port, int threads);
|
||||
|
||||
void addApplication (const string path, srvCallback *callback);
|
||||
void addApp (list<string> *path, reqCallback *);
|
||||
|
||||
virtual ~HTTPServer ();
|
||||
|
||||
|
||||
static list<string> split (const string str, const string del, int limit = -1);
|
||||
private:
|
||||
typedef struct _serve_node {
|
||||
string name; // part of path name
|
||||
map<string, struct _serve_node> children;
|
||||
srvCallback *callback;
|
||||
} serveNode;
|
||||
typedef struct appNode
|
||||
{
|
||||
reqCallback *callback;
|
||||
map<string, appNode> nodes;
|
||||
} appNode;
|
||||
|
||||
bool isRunning;
|
||||
serveNode rootNode;
|
||||
SOCKET sock;
|
||||
SOCKET srv;
|
||||
int thread_count;
|
||||
HANDLE *threads;
|
||||
bool isRunning;
|
||||
appNode rootNode;
|
||||
|
||||
static void handleConnections (HTTPServer *);
|
||||
|
||||
#ifdef WIN32
|
||||
static DWORD doServe (LPVOID arg);
|
||||
static DWORD _thread_start (LPVOID);
|
||||
#else
|
||||
static void* doServe (void* arg);
|
||||
static void* _thread_start (void*);
|
||||
#endif
|
||||
|
||||
static void handleConnection (HTTPServer *, Request *, Response *);
|
||||
static reqCallback* getRequestHandler (appNode *, list<string> *);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
#endif /* HTTPSERVER_HPP_ */
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
using namespace std;
|
||||
using namespace UDPT;
|
||||
using namespace UDPT::Server;
|
||||
|
||||
static void _print_usage ()
|
||||
{
|
||||
|
@ -79,7 +80,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
usi = new UDPTracker (settings);
|
||||
|
||||
API::HTTPServer *apiSrv = NULL;
|
||||
HTTPServer *apiSrv = NULL;
|
||||
|
||||
r = usi->start();
|
||||
if (r != UDPTracker::START_OK)
|
||||
|
@ -101,10 +102,10 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
try{
|
||||
apiSrv = new API::HTTPServer(6969, 2);
|
||||
} catch (API::APIException &ex)
|
||||
apiSrv = new HTTPServer(6969, 8);
|
||||
} catch (ServerException &ex)
|
||||
{
|
||||
cerr << "APIException: " << ex.getMessage() << endl;
|
||||
cerr << "ServerException #" << ex.getErrorCode() << ": " << ex.getErrorMsg() << endl;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue