00001
00002
00003
00004
00005
00006
00007
00008
00009 static const char rcsid[] __attribute__((used)) = "$Id: HTTP.cpp 1202 2014-10-18 21:14:37Z sella $";
00010
00011 #include "HTTP.h"
00012 #include "Code.h"
00013 #include "callbacks.h"
00014 #include "../util/CommonMacro.h"
00015 #include "../util/String.h"
00016 #include "../util/File.h"
00017
00018 #include <cstddef>
00019 #include <string>
00020 #include <algorithm>
00021
00022 using namespace sella::server;
00023 using namespace sella::net;
00024 using namespace sella::util;
00025
00026 const IPAddressPort::vector HTTP::ListenersDefault = { IPAddressPort("0.0.0.0:1080"), IPAddressPort("[::]:1080") };
00027 const std::string HTTP::RootDefault(".");
00028 const std::vector<std::string> HTTP::DirectoryIndexesDefault({ "index.html", "index.htm" });
00029 const std::string HTTP::BannerDefault("Digital Genesis HTTP Server " PACKAGE "/" VERSION);
00030 const std::set<HTTPRequest::Method> HTTP::AllowDefault = { HTTPRequest::Options, HTTPRequest::Get, HTTPRequest::Head, HTTPRequest::Trace };
00031
00032 HTTP::HTTP() throw (ServerException) :
00033 libutilxx__log(::libutilxx__log),
00034 running(false),
00035 listeners(ListenersDefault),
00036 requestBodyLimit(RequestBodyLimitDefault),
00037 connectionLimit(ConnectionLimitDefault),
00038 root(RootDefault),
00039 indexes(DirectoryIndexesDefault),
00040 banner(BannerDefault),
00041 allow(AllowDefault),
00042 aclCallback(NULL),
00043 authCallback(NULL),
00044 reqCallback(http::request_callback),
00045 errorCallback(http::error_callback)
00046 { }
00047
00048 HTTP::HTTP(Log::shared log) throw (ServerException) :
00049 libutilxx__log(log),
00050 running(false),
00051 listeners(ListenersDefault),
00052 requestBodyLimit(RequestBodyLimitDefault),
00053 connectionLimit(ConnectionLimitDefault),
00054 root(RootDefault),
00055 indexes(DirectoryIndexesDefault),
00056 banner(BannerDefault),
00057 allow(AllowDefault),
00058 aclCallback(NULL),
00059 authCallback(NULL),
00060 reqCallback(http::request_callback),
00061 errorCallback(http::error_callback)
00062 { }
00063
00064 HTTP::~HTTP() throw () {
00065 if (isRunning()) {
00066 stop();
00067 } else {
00068 shutdown();
00069 }
00070 }
00071
00072 bool HTTP::receive(void) throw (ServerException) {
00073 timeval tv = { 0, 0 };
00074
00075 return receive(&tv);
00076 }
00077
00078 bool HTTP::receive(timeval *tv) throw (ServerException) {
00079 int c;
00080
00081 if (servers.empty()) {
00082 startup();
00083 }
00084
00085 if ((c = select(tv)) > 0) {
00086 while (!pending.empty()) {
00087 HTTPConnection::shared connection = pending.front();
00088 pending.pop();
00089
00090 worker(connection);
00091 }
00092 }
00093
00094 return (c > 0);
00095 }
00096
00097 bool HTTP::run(size_t workers) throw (ServerException) {
00098 ThreadPool::shared pool = ThreadPool::shared_ptr(workers);
00099
00100 start(pool);
00101
00102 ::select(1, NULL, NULL, NULL, NULL);
00103
00104 return stop();
00105 }
00106
00107 void HTTP::start(size_t workers) throw (ServerException) {
00108 ThreadPool::shared pool = ThreadPool::shared_ptr(workers);
00109
00110 start(pool);
00111 }
00112
00113 void HTTP::start(size_t maximumWorkers, size_t minimumWorkers, size_t spareWorkers) throw (ServerException) {
00114 ThreadPool::shared pool = ThreadPool::shared_ptr(maximumWorkers, minimumWorkers, spareWorkers);
00115
00116 start(pool);
00117 }
00118
00119 void HTTP::start(ThreadPool::shared pool) throw (ServerException) {
00120 std::lock_guard<std::mutex> lg(mutex);
00121
00122 if (running) {
00123 THROW(ServerException, "HTTP server is already running");
00124 } else if (pool == NULL) {
00125 THROW(ServerException, "ThreadPool is null");
00126 }
00127
00128 startup();
00129 this->pool = pool;
00130 running = true;
00131
00132 try {
00133 manager = std::thread(&HTTP::server, this);
00134 } catch (std::exception &e) {
00135 TRACE(PACKAGE, "HTTP server failed to start");
00136
00137 this->pool.reset();
00138 running = false;
00139
00140 RETHROW_CODE(ServerException, 0, e);
00141 }
00142
00143 TRACE(PACKAGE, "HTTP server started");
00144 }
00145
00146 bool HTTP::stop(void) throw () {
00147 TRACE(PACKAGE, "stop");
00148
00149 mutex.lock();
00150
00151 if (running) {
00152 running = false;
00153
00154 trigger.set();
00155
00156 mutex.unlock();
00157
00158 if (manager.joinable()) {
00159 manager.join();
00160 }
00161
00162 pool.reset();
00163
00164 return true;
00165 }
00166
00167 mutex.unlock();
00168
00169 return false;
00170 }
00171
00172 void HTTP::setListeners(const sella::net::IPAddressPort::vector &listeners) throw () {
00173 if (!listeners.empty()) {
00174 this->listeners = listeners;
00175 } else {
00176 this->listeners = ListenersDefault;
00177 }
00178 }
00179
00180 void HTTP::setListeners(const std::string &listeners) throw () {
00181 this->listeners.clear();
00182
00183 const auto &list = String::split(listeners, ",; ", false, false);
00184
00185 for (auto l = list.begin(); l != list.end(); ++l) {
00186 try {
00187 this->listeners.push_back(IPAddressPort(*l));
00188 } catch (Exception &e) {
00189 TRACE(PACKAGE, "Bad listener: %s", e.what());
00190 }
00191 }
00192
00193 if (this->listeners.empty()) {
00194 this->listeners = ListenersDefault;
00195 }
00196 }
00197
00198 void HTTP::setRequestBodyLimit(size_t requestBodyLimit) throw () {
00199 if (requestBodyLimit > 0) {
00200 this->requestBodyLimit = requestBodyLimit;
00201 } else {
00202 this->requestBodyLimit = RequestBodyLimitDefault;
00203 }
00204 }
00205
00206 void HTTP::setConnectionLimit(size_t connectionLimit) throw () {
00207 if (connectionLimit > 0) {
00208 this->connectionLimit = connectionLimit;
00209 } else {
00210 this->connectionLimit = ConnectionLimitDefault;
00211 }
00212 }
00213
00214 void HTTP::setRoot(const std::string &root) throw (ServerException) {
00215 try {
00216 this->root = File::getRealPath(root);
00217 } catch (FileException &e) {
00218 RETHROW(ServerException, e);
00219 }
00220 }
00221
00222 void HTTP::setDirectoryIndex(const std::vector<std::string> &indexes) throw () {
00223 this->indexes = indexes;
00224 }
00225
00226 void HTTP::addDirectoryIndex(const std::string &index) throw () {
00227 if (std::find(indexes.begin(), indexes.end(), index) != indexes.end()) {
00228 this->indexes.push_back(index);
00229 }
00230 }
00231
00232 void HTTP::setBanner(const std::string &banner) throw () {
00233 this->banner = banner;
00234 }
00235
00236 void HTTP::setCustomHeaders(const std::vector<std::string> &headers) throw () {
00237 this->headers = headers;
00238 }
00239
00240 void HTTP::setAllow(HTTPRequest::Method method) throw () {
00241 allow.insert(method);
00242 }
00243
00244 void HTTP::setACLCallback(const acl_callback &callback) throw () {
00245 if (callback) {
00246 this->aclCallback = callback;
00247 } else {
00248 this->aclCallback = NULL;
00249 }
00250 }
00251
00252 void HTTP::setAuthenticationCallback(const authentication_callback &callback) throw () {
00253 if (callback) {
00254 this->authCallback = callback;
00255 } else {
00256 this->authCallback = NULL;
00257 }
00258 }
00259
00260 void HTTP::setRequestCallback(const request_callback &callback) throw () {
00261 if (callback) {
00262 this->reqCallback = callback;
00263 } else {
00264 this->reqCallback = http::request_callback;
00265 }
00266 }
00267
00268 void HTTP::setErrorCallback(const error_callback &callback) throw () {
00269 if (callback) {
00270 this->errorCallback = callback;
00271 } else {
00272 this->errorCallback = http::error_callback;
00273 }
00274 }
00275
00276 IPAddressPort::vector HTTP::getListeners(void) const throw () {
00277 return listeners;
00278 }
00279
00280 std::string HTTP::getListenersString(void) const throw () {
00281 std::string buf;
00282
00283 if (!listeners.empty()) {
00284 for (auto l = listeners.begin(); l != listeners.end(); ++l) {
00285 buf += l->str() + ",";
00286 }
00287
00288 return buf.erase(buf.size() - 1);
00289 }
00290
00291 return buf;
00292 }
00293
00294 size_t HTTP::getRequestBodyLimit(void) const throw () {
00295 return requestBodyLimit;
00296 }
00297
00298 size_t HTTP::getConnectionLimit(void) const throw () {
00299 return connectionLimit;
00300 }
00301
00302 const std::string& HTTP::getRoot(void) const throw () {
00303 return root;
00304 }
00305
00306 const std::vector<std::string>& HTTP::getDirectoryIndexes(void) const throw () {
00307 return indexes;
00308 }
00309
00310 const std::string& HTTP::getBanner(void) const throw () {
00311 return banner;
00312 }
00313
00314 const std::vector<std::string>& HTTP::getCustomHeaders(void) const throw () {
00315 return headers;
00316 }
00317
00318 std::set<HTTPRequest::Method> HTTP::getAllow(void) const throw () {
00319 return allow;
00320 }
00321
00322 void HTTP::clearAllow(HTTPRequest::Method method) throw () {
00323 allow.erase(method);
00324 }
00325
00326 bool HTTP::isRunning(void) const throw () {
00327 return running;
00328 }
00329
00330 void HTTP::clear(void) {
00331 if (isRunning()) {
00332 stop();
00333 }
00334
00335 pool.reset();
00336 mux.clear();
00337 servers.clear();
00338 listeners = ListenersDefault;
00339 running = false;
00340 std::queue<HTTPConnection::shared>().swap(pending);
00341 requestBodyLimit = RequestBodyLimitDefault;
00342 connectionLimit = ConnectionLimitDefault;
00343 root = RootDefault;
00344 indexes = DirectoryIndexesDefault;
00345 banner = BannerDefault;
00346 headers.clear();
00347 allow = AllowDefault;
00348 aclCallback = NULL;
00349 authCallback = NULL;
00350 reqCallback = http::request_callback;
00351 errorCallback = http::error_callback;
00352 }
00353
00354 void HTTP::print(void) {
00355 INFO("[HTTP]");
00356 INFO(" listeners: %s", getListenersString().c_str());
00357 INFO(" requestBodyLimit: %zd", requestBodyLimit);
00358 INFO(" connectionLimit: %zd (pending: %zd)", connectionLimit, pending.size());
00359 INFO(" root: %s", root.c_str());
00360 INFO(" indexes: %s", String::join(indexes, ",").c_str());
00361 INFO(" banner: %s", banner.c_str());
00362 INFO(" headers: %zd", headers.size());
00363 INFO(" aclCallback: %s", (aclCallback) ? "set" : "null");
00364 INFO(" authCallback: %s", (authCallback) ? "set" : "null");
00365 INFO(" reqCallback: %s", (reqCallback) ? "set" : "null");
00366 INFO(" errorCallback: %s", (errorCallback) ? "set" : "null");
00367 }
00368
00369 const HTTP::shared HTTP::shared_ptr() throw (ServerException) {
00370 return std::make_shared<HTTP>();
00371 }
00372
00373 const HTTP::shared HTTP::shared_ptr(Log::shared log) throw (ServerException) {
00374 return std::make_shared<HTTP>(log);
00375 }
00376
00377 void HTTP::startup(void) throw (ServerException) {
00378 TCPSocket::shared socket;
00379
00380 mux.insertRead(trigger.getSocket());
00381
00382 for (auto l = listeners.begin(); l != listeners.end(); ++l) {
00383 try {
00384 socket = TCPSocket::shared_ptr(l->getFamily());
00385 socket->setSoReuseAddr();
00386 socket->setBlock(true);
00387
00388 if (socket->getFamily() == AF_INET6) {
00389 socket->setIpv6V6Only(true);
00390 }
00391
00392 socket->bind(*l);
00393
00394 mux.insertRead(socket);
00395 servers.insert(socket->getFD());
00396
00397 socket->listen();
00398 } catch (SocketException &e) {
00399 RETHROW(ServerException, e);
00400 }
00401 }
00402 }
00403
00404 void HTTP::shutdown(void) throw () {
00405 mux.clear();
00406 servers.clear();
00407 }
00408
00409 int HTTP::select(timeval *tv) throw (ServerException) {
00410 int c;
00411
00412 if ((c = mux.rselect(tv)) > 0) {
00413 const SocketMux::shared_flist &sockets = mux.getReadList();
00414 for (auto s = sockets.cbegin(); s != sockets.cend(); ++s) {
00415 if (*s == trigger.getSocket()) {
00416 trigger.clear();
00417
00418 return 0;
00419 }
00420
00421 try {
00422 HTTPConnection::shared connection = accept(*s);
00423
00424 if (pending.size() < connectionLimit) {
00425 pending.push(std::move(connection));
00426 } else {
00427 try {
00428 errorCallback(connection, Code::ServiceUnavailable, std::string());
00429 } catch (ServerException &e) {
00430 WARNING("HTTP::errorCallback() failed with code %d: %s\n", e.getCode(), e.what());
00431 }
00432
00433 connection->clear();
00434 c--;
00435 }
00436 } catch (SocketException &e) {
00437 WARNING(e);
00438 }
00439 }
00440 }
00441
00442 return c;
00443 }
00444
00445 HTTPConnection::shared HTTP::accept(const Socket::shared &socket) throw (ServerException) {
00446 try {
00447 TCPSocket::shared server(std::dynamic_pointer_cast<TCPSocket>(socket));
00448 TCPSocket::shared client = server->accept();
00449
00450 TRACE(PACKAGE "-detail", "Incoming HTTP connection from %s", client->getRemoteIPAddressPort().c_str());
00451
00452 return HTTPConnection::shared_ptr(*this, client);
00453 } catch (SocketException &e) {
00454 RETHROW(ServerException, e);
00455 }
00456 }
00457
00458 void HTTP::process(HTTPConnection::shared &connection) throw () {
00459 try {
00460 if (aclCallback && !aclCallback(connection)) {
00461 try {
00462 errorCallback(connection, Code::Forbidden, std::string());
00463 } catch (ServerException &e) {
00464 WARNING("HTTP::errorCallback() failed with code %d: %s\n", e.getCode(), e.what());
00465 }
00466 } else if (authCallback && !authCallback(connection)) {
00467 try {
00468 errorCallback(connection, Code::Unauthorized, std::string());
00469 } catch (ServerException &e) {
00470 WARNING("HTTP::errorCallback() failed with code %d: %s\n", e.getCode(), e.what());
00471 }
00472 } else {
00473 reqCallback(connection);
00474 }
00475 } catch (ServerException &e) {
00476 try {
00477 if (e.getCode() > 0) {
00478 errorCallback(connection, e.getCode(), e.getMessage());
00479 } else {
00480 errorCallback(connection, Code::InternalServerError, std::string());
00481 }
00482 } catch (ServerException &e) {
00483 WARNING("HTTP::errorCallback() failed with code %d: %s\n", e.getCode(), e.what());
00484 }
00485 } catch (std::exception &e) {
00486 try {
00487 errorCallback(connection, Code::InternalServerError, std::string());
00488 } catch (ServerException &e) {
00489 WARNING("HTTP::errorCallback() failed with code %d: %s\n", e.getCode(), e.what());
00490 }
00491 }
00492 }
00493
00494 void HTTP::server(void) throw () {
00495 int c;
00496 timeval tv;
00497 Task::shared task;
00498 Task::callback function;
00499
00500 while (running) {
00501 tv = { DelaySecondsDefault, 0 };
00502
00503 while ((c = select(&tv)) > 0) {
00504 while (!pending.empty()) {
00505 HTTPConnection::shared connection = pending.front();
00506 pending.pop();
00507
00508 task = Task::shared_ptr();
00509 function = std::bind(&HTTP::worker, this, connection);
00510 task->setCallback(function);
00511
00512 pool->schedule(task);
00513 }
00514 }
00515 }
00516
00517 TRACE(PACKAGE, "HTTP server stopping...");
00518
00519 std::lock_guard<std::mutex> lg(mutex);
00520
00521 shutdown();
00522 std::queue<HTTPConnection::shared>().swap(pending);
00523
00524 pool->clear();
00525 pool->wait();
00526
00527 TRACE(PACKAGE, "HTTP server stopped");
00528 }
00529
00530 void HTTP::worker(HTTPConnection::shared &connection) throw () {
00531 process(connection);
00532
00533 connection->clear();
00534 }
00535
00536
00537
00538