9 static const char rcsid[] __attribute__((used)) =
"$Id: HTTP.cpp 1664 2016-04-04 03:57:40Z sella $";
13 #include "callbacks.h"
14 #include "../util/CommonMacro.h"
15 #include "../util/String.h"
16 #include "../util/File.h"
22 using namespace sella::server;
23 using namespace sella::net;
24 using namespace sella::util;
26 HTTP::HTTP() throw (ServerException) :
27 libutilxx__log(::libutilxx__log),
29 listeners(ListenersDefault()),
30 secureListeners(SecureListenersDefault()),
32 preferServerCiphers(false),
33 requestBodyLimit(RequestBodyLimitDefault),
34 connectionLimit(ConnectionLimitDefault),
36 indexes(DirectoryIndexesDefault()),
37 banner(BannerDefault()),
38 allow(AllowDefault()),
39 fallbackAcceptType(FallbackAcceptTypeDefault),
40 compressionThreshold(CompressionThresholdDefault),
41 compressionLevel(CompressionLevelDefault),
44 reqCallback(http::request_callback),
45 errorCallback(http::error_callback)
48 HTTP::HTTP(Log::shared log)
throw (ServerException) :
51 listeners(ListenersDefault()),
52 secureListeners(SecureListenersDefault()),
53 protocols(SecureTCPSocket::DefaultProtocols),
54 preferServerCiphers(
false),
55 requestBodyLimit(RequestBodyLimitDefault),
56 connectionLimit(ConnectionLimitDefault),
58 indexes(DirectoryIndexesDefault()),
59 banner(BannerDefault()),
60 allow(AllowDefault()),
61 fallbackAcceptType(FallbackAcceptTypeDefault),
62 compressionThreshold(CompressionThresholdDefault),
63 compressionLevel(CompressionLevelDefault),
66 reqCallback(http::request_callback),
67 errorCallback(http::error_callback)
70 HTTP::~HTTP() throw () {
78 bool HTTP::receive(
void) throw (ServerException) {
79 timeval tv = { 0, 0 };
84 bool HTTP::receive(timeval *tv)
throw (ServerException) {
87 if (servers.empty()) {
91 if ((c = select(tv)) > 0) {
92 while (!pending.empty()) {
93 HTTPConnection::shared connection = pending.front();
103 bool HTTP::run(
size_t workers)
throw (ServerException) {
105 ThreadPool::shared pool = ThreadPool::shared_ptr(workers);
108 }
catch (ThreadPoolException &e) {
109 RETHROW(ServerException, e);
112 ::select(1, NULL, NULL, NULL, NULL);
117 bool HTTP::run(
size_t maximumWorkers,
size_t minimumWorkers,
size_t spareWorkers)
throw (ServerException) {
119 ThreadPool::shared pool = ThreadPool::shared_ptr(maximumWorkers, minimumWorkers, spareWorkers);
122 }
catch (ThreadPoolException &e) {
123 RETHROW(ServerException, e);
126 ::select(1, NULL, NULL, NULL, NULL);
131 void HTTP::start(
size_t workers)
throw (ServerException) {
133 ThreadPool::shared pool = ThreadPool::shared_ptr(workers);
136 }
catch (ThreadPoolException &e) {
137 RETHROW(ServerException, e);
141 void HTTP::start(
size_t maximumWorkers,
size_t minimumWorkers,
size_t spareWorkers)
throw (ServerException) {
143 ThreadPool::shared pool = ThreadPool::shared_ptr(maximumWorkers, minimumWorkers, spareWorkers);
146 }
catch (ThreadPoolException &e) {
147 RETHROW(ServerException, e);
151 void HTTP::start(ThreadPool::shared pool)
throw (ServerException) {
152 std::lock_guard<std::mutex> lg(mutex);
155 THROW(ServerException,
"HTTP server is already running");
156 }
else if (pool == NULL) {
157 THROW(ServerException,
"ThreadPool is null");
165 manager = std::thread(&HTTP::server,
this);
166 }
catch (std::exception &e) {
167 TRACE(PACKAGE,
"HTTP server failed to start");
172 RETHROW_CODE(ServerException, 0, e);
175 TRACE(PACKAGE,
"HTTP server started");
178 bool HTTP::stop(
void) throw () {
179 TRACE(PACKAGE,
"stop");
190 if (manager.joinable()) {
204 void HTTP::setListeners(
const sella::net::IPAddressPort::vector &listeners)
throw () {
205 if (!listeners.empty()) {
206 this->listeners = listeners;
208 this->listeners = ListenersDefault();
212 void HTTP::setListeners(
const std::string &listeners)
throw () {
213 this->listeners.clear();
215 const auto &list = String::split(listeners,
",; ",
false,
false);
217 for (
auto l = list.begin(); l != list.end(); ++l) {
220 }
catch (Exception &e) {
221 TRACE(PACKAGE,
"Bad listener: %s", e.what());
225 if (this->listeners.empty()) {
226 this->listeners = ListenersDefault();
230 void HTTP::setSecureListeners(
const sella::net::IPAddressPort::vector &secureListeners)
throw () {
231 if (!secureListeners.empty()) {
232 this->secureListeners = secureListeners;
234 this->secureListeners = SecureListenersDefault();
238 void HTTP::setSecureListeners(
const std::string &secureListeners)
throw () {
239 this->secureListeners.clear();
241 const auto &list = String::split(secureListeners,
",; ",
false,
false);
243 for (
auto l = list.begin(); l != list.end(); ++l) {
246 }
catch (Exception &e) {
247 TRACE(PACKAGE,
"Bad listener: %s", e.what());
251 if (this->secureListeners.empty()) {
252 this->secureListeners = SecureListenersDefault();
256 void HTTP::setSecureProtocols(
const std::set<SecureTCPSocket::Protocol> &protocols)
throw () {
257 this->protocols = protocols;
260 void HTTP::setSecureProtocol(SecureTCPSocket::Protocol protocol,
bool on)
throw () {
262 this->protocols.insert(protocol);
264 this->protocols.erase(protocol);
268 void HTTP::setSecureCipher(
const std::string &ciphers)
throw () {
269 this->ciphers = ciphers;
272 void HTTP::setSecurePreferServerCipher(
bool on)
throw () {
273 this->preferServerCiphers = on;
276 void HTTP::setCertificateFile(
const std::string &certificateFile)
throw () {
277 this->certificateFile = certificateFile;
280 void HTTP::setPrivateKeyFile(
const std::string &privateKeyFile)
throw () {
281 this->privateKeyFile = privateKeyFile;
284 void HTTP::setPasswordFile(
const std::string &passwordFile)
throw () {
285 this->passwordFile = passwordFile;
288 void HTTP::setPassword(
const std::string &password)
throw () {
290 for (
size_t i = 0; i < this->password.size(); i++) {
291 this->password.at(i) = 0;
294 this->password = password;
297 void HTTP::setCertificateChainFile(
const std::string &certificateChainFile)
throw () {
298 this->certificateChainFile = certificateChainFile;
301 void HTTP::setCACertificateFile(
const std::string &caCertificateFile)
throw () {
302 this->caCertificateFile = caCertificateFile;
305 void HTTP::setRequestBodyLimit(
size_t requestBodyLimit)
throw () {
306 if (requestBodyLimit > 0) {
307 this->requestBodyLimit = requestBodyLimit;
309 this->requestBodyLimit = RequestBodyLimitDefault;
313 void HTTP::setConnectionLimit(
size_t connectionLimit)
throw () {
314 if (connectionLimit > 0) {
315 this->connectionLimit = connectionLimit;
317 this->connectionLimit = ConnectionLimitDefault;
321 void HTTP::setRoot(
const std::string &root)
throw (ServerException) {
323 this->root = File::getRealPath(root);
324 }
catch (FileException &e) {
325 RETHROW(ServerException, e);
329 void HTTP::setDirectoryIndex(
const std::vector<std::string> &indexes)
throw () {
330 this->indexes = indexes;
333 void HTTP::addDirectoryIndex(
const std::string &index)
throw () {
334 if (std::find(indexes.begin(), indexes.end(), index) != indexes.end()) {
335 this->indexes.push_back(index);
339 void HTTP::setBanner(
const std::string &banner)
throw () {
340 this->banner = banner;
343 void HTTP::setCustomHeaders(
const std::vector<std::string> &headers)
throw () {
344 this->headers = headers;
347 void HTTP::setAllows(
const std::set<HTTPRequest::Method> &methods)
throw () {
351 void HTTP::setAllow(HTTPRequest::Method method)
throw () {
352 allow.insert(method);
355 void HTTP::setFallbackAcceptType(HTTPRequest::AcceptType fallbackAcceptType)
throw () {
356 this->fallbackAcceptType = fallbackAcceptType;
359 void HTTP::setCompressionThreshold(
size_t threshold)
throw () {
361 this->compressionThreshold = 0;
363 this->compressionThreshold = threshold;
367 void HTTP::setCompressionLevel(
int level)
throw () {
369 this->compressionLevel = 0;
370 }
else if (level > 9) {
371 this->compressionLevel = 9;
373 this->compressionLevel = level;
377 void HTTP::setACLCallback(
const acl_callback &callback)
throw () {
379 this->aclCallback = callback;
381 this->aclCallback = NULL;
385 void HTTP::setAuthenticationCallback(
const authentication_callback &callback)
throw () {
387 this->authCallback = callback;
389 this->authCallback = NULL;
393 void HTTP::setRequestCallback(
const request_callback &callback)
throw () {
395 this->reqCallback = callback;
397 this->reqCallback = http::request_callback;
401 void HTTP::setErrorCallback(
const error_callback &callback)
throw () {
403 this->errorCallback = callback;
405 this->errorCallback = http::error_callback;
409 IPAddressPort::vector HTTP::getListeners(
void)
const throw () {
413 std::string HTTP::getListenersString(
void)
const throw () {
416 if (!listeners.empty()) {
417 for (
auto l = listeners.begin(); l != listeners.end(); ++l) {
418 buf += l->str() +
",";
421 return buf.erase(buf.size() - 1);
427 IPAddressPort::vector HTTP::getSecureListeners(
void)
const throw () {
428 return secureListeners;
431 std::string HTTP::getSecureListenersString(
void)
const throw () {
434 if (!secureListeners.empty()) {
435 for (
auto l = secureListeners.begin(); l != secureListeners.end(); ++l) {
436 buf += l->str() +
",";
439 return buf.erase(buf.size() - 1);
445 size_t HTTP::getRequestBodyLimit(
void)
const throw () {
446 return requestBodyLimit;
449 size_t HTTP::getConnectionLimit(
void)
const throw () {
450 return connectionLimit;
453 const std::string& HTTP::getRoot(
void)
const throw () {
457 const std::vector<std::string>& HTTP::getDirectoryIndexes(
void)
const throw () {
461 const std::string& HTTP::getBanner(
void)
const throw () {
465 const std::vector<std::string>& HTTP::getCustomHeaders(
void)
const throw () {
469 std::set<HTTPRequest::Method> HTTP::getAllow(
void)
const throw () {
473 HTTPRequest::AcceptType HTTP::getFallbackAcceptType(
void)
const throw () {
474 return fallbackAcceptType;
477 size_t HTTP::getCompressionThreshold(
void)
const throw () {
478 return compressionThreshold;
481 int HTTP::getCompressionLevel(
void)
const throw () {
482 return compressionLevel;
485 void HTTP::clearAllow(HTTPRequest::Method method)
throw () {
489 bool HTTP::isRunning(
void)
const throw () {
493 void HTTP::clear(
void) {
501 listeners = ListenersDefault();
502 secureListeners = SecureListenersDefault();
503 protocols = SecureTCPSocket::DefaultProtocols;
505 preferServerCiphers =
false;
506 certificateFile.clear();
507 privateKeyFile.clear();
510 for (
size_t i = 0; i < password.size(); i++) {
514 passwordFile.clear();
516 certificateChainFile.clear();
517 caCertificateFile.clear();
519 std::queue<HTTPConnection::shared>().swap(pending);
520 requestBodyLimit = RequestBodyLimitDefault;
521 connectionLimit = ConnectionLimitDefault;
522 root = RootDefault();
523 indexes = DirectoryIndexesDefault();
524 banner = BannerDefault();
526 allow = AllowDefault();
527 compressionThreshold = CompressionThresholdDefault;
528 compressionLevel = CompressionLevelDefault;
531 reqCallback = http::request_callback;
532 errorCallback = http::error_callback;
535 void HTTP::print(
void) {
537 INFO(
" listeners: %s", getListenersString().c_str());
538 INFO(
" secureListeners: %s", getSecureListenersString().c_str());
540 if (!protocols.empty()) {
542 for (
auto p = protocols.begin(); p != protocols.end(); ++p) {
544 case SecureTCPSocket::SSLv3:
545 buf.append(
"SSLv3 ");
548 case SecureTCPSocket::TLSv1_0:
549 buf.append(
"TLSv1 ");
552 case SecureTCPSocket::TLSv1_1:
553 buf.append(
"TLSv1.1 ");
556 case SecureTCPSocket::TLSv1_2:
557 buf.append(
"TLSv1.2 ");
562 INFO(
" protocols: { %s}", buf.c_str());
564 INFO(
" protocols: all");
567 INFO(
" ciphers: %s", ciphers.c_str());
568 INFO(
" preferServerCiphers: %s", (preferServerCiphers) ?
"true" :
"false");
569 INFO(
" certificateFile: %s", certificateFile.c_str());
570 INFO(
" privateKeyFile: %s", privateKeyFile.c_str());
571 INFO(
" passwordFile: %s", passwordFile.c_str());
572 INFO(
" password: %s", password.c_str());
573 INFO(
" certificateChainFile: %s", certificateChainFile.c_str());
574 INFO(
" caCertificateFile: %s", caCertificateFile.c_str());
575 INFO(
" requestBodyLimit: %zd", requestBodyLimit);
576 INFO(
" connectionLimit: %zd (pending: %zd)", connectionLimit, pending.size());
577 INFO(
" root: %s", root.c_str());
578 INFO(
" indexes: %s", String::join(indexes,
",").c_str());
579 INFO(
" banner: %s", banner.c_str());
580 INFO(
" headers: %zd", headers.size());
582 INFO(
" compressionThreshold: %zd", compressionThreshold);
583 INFO(
" compressionLevel: %zd", compressionLevel);
584 INFO(
" aclCallback: %s", (aclCallback) ?
"set" :
"null");
585 INFO(
" authCallback: %s", (authCallback) ?
"set" :
"null");
586 INFO(
" reqCallback: %s", (reqCallback) ?
"set" :
"null");
587 INFO(
" errorCallback: %s", (errorCallback) ?
"set" :
"null");
590 const HTTP::shared HTTP::shared_ptr() throw (ServerException) {
591 return std::make_shared<HTTP>();
594 const HTTP::shared HTTP::shared_ptr(Log::shared log)
throw (ServerException) {
595 return std::make_shared<HTTP>(log);
598 void HTTP::startup(
void) throw (ServerException) {
599 mux.insertRead(trigger.getSocket());
601 for (
auto l = listeners.begin(); l != listeners.end(); ++l) {
603 TCPSocket::shared s = TCPSocket::shared_ptr(l->getFamily());
607 if (s->getFamily() == AF_INET6) {
608 s->setIpv6V6Only(
true);
614 servers.insert(s->getFD());
617 }
catch (SocketException &e) {
618 RETHROW(ServerException, e);
622 for (
auto l = secureListeners.begin(); l != secureListeners.end(); ++l) {
624 SecureTCPSocket::shared s = SecureTCPSocket::shared_ptr(l->getFamily());
628 s->setProtocols(protocols);
629 s->setCiphers(ciphers);
630 s->setPreferServerCiphers(preferServerCiphers);
631 s->setCertificateFile(certificateFile);
632 s->setPrivateKeyFile(privateKeyFile);
633 s->setPasswordFile(passwordFile);
634 s->setPassword(password);
635 s->setCertificateChainFile(certificateChainFile);
636 s->setCACertificateFile(caCertificateFile);
638 if (s->getFamily() == AF_INET6) {
639 s->setIpv6V6Only(
true);
645 servers.insert(s->getFD());
648 }
catch (SocketException &e) {
649 RETHROW(ServerException, e);
654 void HTTP::shutdown(
void) throw () {
659 int HTTP::select(timeval *tv)
throw (ServerException) {
662 if ((c = mux.rselect(tv)) > 0) {
663 const SocketMux::shared_flist &sockets = mux.getReadList();
664 for (
auto s = sockets.cbegin(); s != sockets.cend(); ++s) {
665 if (*s == trigger.getSocket()) {
671 HTTPConnection::shared connection = accept(*s);
673 if (pending.size() < connectionLimit) {
674 pending.push(std::move(connection));
677 errorCallback(connection, Code::ServiceUnavailable, std::string());
678 }
catch (ServerException &e) {
679 WARNING(
"HTTP::errorCallback() failed with code %d: %s\n", e.getCode(), e.what());
691 HTTPConnection::shared HTTP::accept(
const Socket::shared &socket)
throw (ServerException) {
693 TCPSocket::shared server(std::dynamic_pointer_cast<TCPSocket>(socket));
694 TCPSocket::shared client = server->accept();
696 if (client->isSecure()) {
697 TRACE(PACKAGE
"-detail",
"Incoming HTTPS connection from %s", client->getRemoteIPAddressPort().c_str());
699 TRACE(PACKAGE
"-detail",
"Incoming HTTP connection from %s", client->getRemoteIPAddressPort().c_str());
702 return HTTPConnection::shared_ptr(*
this, client);
703 }
catch (SocketException &e) {
704 RETHROW(ServerException, e);
708 void HTTP::process(HTTPConnection::shared &connection)
throw () {
710 if (aclCallback && !aclCallback(connection)) {
712 errorCallback(connection, Code::Forbidden, std::string());
713 }
catch (ServerException &e) {
714 WARNING(
"HTTP::errorCallback() failed with code %d: %s\n", e.getCode(), e.what());
716 }
else if (authCallback && !authCallback(connection)) {
718 errorCallback(connection, Code::Unauthorized, std::string());
719 }
catch (ServerException &e) {
720 WARNING(
"HTTP::errorCallback() failed with code %d: %s\n", e.getCode(), e.what());
723 reqCallback(connection);
725 }
catch (ServerException &e) {
727 if (e.getCode() > 0) {
728 errorCallback(connection, e.getCode(), e.getMessage());
730 errorCallback(connection, Code::InternalServerError, std::string());
732 }
catch (ServerException &e) {
733 WARNING(
"HTTP::errorCallback() failed with code %d: %s\n", e.getCode(), e.what());
735 }
catch (std::exception &e) {
737 errorCallback(connection, Code::InternalServerError, std::string());
738 }
catch (ServerException &e) {
739 WARNING(
"HTTP::errorCallback() failed with code %d: %s\n", e.getCode(), e.what());
744 void HTTP::server(
void) throw () {
748 Task::callback
function;
752 tv = { DelaySecondsDefault, 0 };
754 while ((c = select(&tv)) > 0) {
755 while (!pending.empty()) {
756 HTTPConnection::shared connection = pending.front();
759 task = Task::shared_ptr();
760 function = std::bind(&HTTP::worker,
this, connection);
761 task->setCallback(
function);
763 pool->schedule(task);
766 }
catch (ServerException &e) {
767 WARNING(
"ServerException: %s", e.what());
771 TRACE(PACKAGE,
"HTTP server stopping...");
773 std::lock_guard<std::mutex> lg(mutex);
776 std::queue<HTTPConnection::shared>().swap(pending);
781 TRACE(PACKAGE,
"HTTP server stopped");
784 void HTTP::worker(HTTPConnection::shared &connection)
throw () {