libutil++  1.9.3
 All Classes Functions Variables
HTTP.cpp
1 /*
2 ** libutil++
3 ** $Id: HTTP.cpp 1664 2016-04-04 03:57:40Z sella $
4 ** Copyright (c) 2011-2016 Digital Genesis, LLC. All Rights Reserved.
5 ** Released under the LGPL Version 2.1 License.
6 ** http://www.digitalgenesis.com
7 */
8 
9 static const char rcsid[] __attribute__((used)) = "$Id: HTTP.cpp 1664 2016-04-04 03:57:40Z sella $";
10 
11 #include "HTTP.h"
12 #include "Code.h"
13 #include "callbacks.h"
14 #include "../util/CommonMacro.h"
15 #include "../util/String.h"
16 #include "../util/File.h"
17 
18 #include <cstddef>
19 #include <string>
20 #include <algorithm>
21 
22 using namespace sella::server;
23 using namespace sella::net;
24 using namespace sella::util;
25 
26 HTTP::HTTP() throw (ServerException) :
27  libutilxx__log(::libutilxx__log),
28  running(false),
29  listeners(ListenersDefault()),
30  secureListeners(SecureListenersDefault()),
31  protocols(SecureTCPSocket::DefaultProtocols),
32  preferServerCiphers(false),
33  requestBodyLimit(RequestBodyLimitDefault),
34  connectionLimit(ConnectionLimitDefault),
35  root(RootDefault()),
36  indexes(DirectoryIndexesDefault()),
37  banner(BannerDefault()),
38  allow(AllowDefault()),
39  fallbackAcceptType(FallbackAcceptTypeDefault),
40  compressionThreshold(CompressionThresholdDefault),
41  compressionLevel(CompressionLevelDefault),
42  aclCallback(NULL),
43  authCallback(NULL),
44  reqCallback(http::request_callback),
45  errorCallback(http::error_callback)
46 { }
47 
48 HTTP::HTTP(Log::shared log) throw (ServerException) :
49  libutilxx__log(log),
50  running(false),
51  listeners(ListenersDefault()),
52  secureListeners(SecureListenersDefault()),
53  protocols(SecureTCPSocket::DefaultProtocols),
54  preferServerCiphers(false),
55  requestBodyLimit(RequestBodyLimitDefault),
56  connectionLimit(ConnectionLimitDefault),
57  root(RootDefault()),
58  indexes(DirectoryIndexesDefault()),
59  banner(BannerDefault()),
60  allow(AllowDefault()),
61  fallbackAcceptType(FallbackAcceptTypeDefault),
62  compressionThreshold(CompressionThresholdDefault),
63  compressionLevel(CompressionLevelDefault),
64  aclCallback(NULL),
65  authCallback(NULL),
66  reqCallback(http::request_callback),
67  errorCallback(http::error_callback)
68 { }
69 
70 HTTP::~HTTP() throw () {
71  if (isRunning()) {
72  stop();
73  } else {
74  shutdown();
75  }
76 }
77 
78 bool HTTP::receive(void) throw (ServerException) {
79  timeval tv = { 0, 0 };
80 
81  return receive(&tv);
82 }
83 
84 bool HTTP::receive(timeval *tv) throw (ServerException) {
85  int c;
86 
87  if (servers.empty()) {
88  startup();
89  }
90 
91  if ((c = select(tv)) > 0) {
92  while (!pending.empty()) {
93  HTTPConnection::shared connection = pending.front();
94  pending.pop();
95 
96  worker(connection);
97  }
98  }
99 
100  return (c > 0);
101 }
102 
103 bool HTTP::run(size_t workers) throw (ServerException) {
104  try {
105  ThreadPool::shared pool = ThreadPool::shared_ptr(workers);
106 
107  start(pool);
108  } catch (ThreadPoolException &e) {
109  RETHROW(ServerException, e);
110  }
111 
112  ::select(1, NULL, NULL, NULL, NULL);
113 
114  return stop();
115 }
116 
117 bool HTTP::run(size_t maximumWorkers, size_t minimumWorkers, size_t spareWorkers) throw (ServerException) {
118  try {
119  ThreadPool::shared pool = ThreadPool::shared_ptr(maximumWorkers, minimumWorkers, spareWorkers);
120 
121  start(pool);
122  } catch (ThreadPoolException &e) {
123  RETHROW(ServerException, e);
124  }
125 
126  ::select(1, NULL, NULL, NULL, NULL);
127 
128  return stop();
129 }
130 
131 void HTTP::start(size_t workers) throw (ServerException) {
132  try {
133  ThreadPool::shared pool = ThreadPool::shared_ptr(workers);
134 
135  start(pool);
136  } catch (ThreadPoolException &e) {
137  RETHROW(ServerException, e);
138  }
139 }
140 
141 void HTTP::start(size_t maximumWorkers, size_t minimumWorkers, size_t spareWorkers) throw (ServerException) {
142  try {
143  ThreadPool::shared pool = ThreadPool::shared_ptr(maximumWorkers, minimumWorkers, spareWorkers);
144 
145  start(pool);
146  } catch (ThreadPoolException &e) {
147  RETHROW(ServerException, e);
148  }
149 }
150 
151 void HTTP::start(ThreadPool::shared pool) throw (ServerException) {
152  std::lock_guard<std::mutex> lg(mutex);
153 
154  if (running) {
155  THROW(ServerException, "HTTP server is already running");
156  } else if (pool == NULL) {
157  THROW(ServerException, "ThreadPool is null");
158  }
159 
160  startup();
161  this->pool = pool;
162  running = true;
163 
164  try {
165  manager = std::thread(&HTTP::server, this);
166  } catch (std::exception &e) {
167  TRACE(PACKAGE, "HTTP server failed to start");
168 
169  this->pool.reset();
170  running = false;
171 
172  RETHROW_CODE(ServerException, 0, e);
173  }
174 
175  TRACE(PACKAGE, "HTTP server started");
176 }
177 
178 bool HTTP::stop(void) throw () {
179  TRACE(PACKAGE, "stop");
180 
181  mutex.lock();
182 
183  if (running) {
184  running = false;
185 
186  trigger.set();
187 
188  mutex.unlock();
189 
190  if (manager.joinable()) {
191  manager.join();
192  }
193 
194  pool.reset();
195 
196  return true;
197  }
198 
199  mutex.unlock();
200 
201  return false;
202 }
203 
204 void HTTP::setListeners(const sella::net::IPAddressPort::vector &listeners) throw () {
205  if (!listeners.empty()) {
206  this->listeners = listeners;
207  } else {
208  this->listeners = ListenersDefault();
209  }
210 }
211 
212 void HTTP::setListeners(const std::string &listeners) throw () {
213  this->listeners.clear();
214 
215  const auto &list = String::split(listeners, ",; ", false, false);
216 
217  for (auto l = list.begin(); l != list.end(); ++l) {
218  try {
219  this->listeners.push_back(IPAddressPort(*l));
220  } catch (Exception &e) {
221  TRACE(PACKAGE, "Bad listener: %s", e.what());
222  }
223  }
224 
225  if (this->listeners.empty()) {
226  this->listeners = ListenersDefault();
227  }
228 }
229 
230 void HTTP::setSecureListeners(const sella::net::IPAddressPort::vector &secureListeners) throw () {
231  if (!secureListeners.empty()) {
232  this->secureListeners = secureListeners;
233  } else {
234  this->secureListeners = SecureListenersDefault();
235  }
236 }
237 
238 void HTTP::setSecureListeners(const std::string &secureListeners) throw () {
239  this->secureListeners.clear();
240 
241  const auto &list = String::split(secureListeners, ",; ", false, false);
242 
243  for (auto l = list.begin(); l != list.end(); ++l) {
244  try {
245  this->secureListeners.push_back(IPAddressPort(*l));
246  } catch (Exception &e) {
247  TRACE(PACKAGE, "Bad listener: %s", e.what());
248  }
249  }
250 
251  if (this->secureListeners.empty()) {
252  this->secureListeners = SecureListenersDefault();
253  }
254 }
255 
256 void HTTP::setSecureProtocols(const std::set<SecureTCPSocket::Protocol> &protocols) throw () {
257  this->protocols = protocols;
258 }
259 
260 void HTTP::setSecureProtocol(SecureTCPSocket::Protocol protocol, bool on) throw () {
261  if (on) {
262  this->protocols.insert(protocol);
263  } else {
264  this->protocols.erase(protocol);
265  }
266 }
267 
268 void HTTP::setSecureCipher(const std::string &ciphers) throw () {
269  this->ciphers = ciphers;
270 }
271 
272 void HTTP::setSecurePreferServerCipher(bool on) throw () {
273  this->preferServerCiphers = on;
274 }
275 
276 void HTTP::setCertificateFile(const std::string &certificateFile) throw () {
277  this->certificateFile = certificateFile;
278 }
279 
280 void HTTP::setPrivateKeyFile(const std::string &privateKeyFile) throw () {
281  this->privateKeyFile = privateKeyFile;
282 }
283 
284 void HTTP::setPasswordFile(const std::string &passwordFile) throw () {
285  this->passwordFile = passwordFile;
286 }
287 
288 void HTTP::setPassword(const std::string &password) throw () {
289  /* Scrub previous buffer before release back to stack. */
290  for (size_t i = 0; i < this->password.size(); i++) {
291  this->password.at(i) = 0;
292  }
293 
294  this->password = password;
295 }
296 
297 void HTTP::setCertificateChainFile(const std::string &certificateChainFile) throw () {
298  this->certificateChainFile = certificateChainFile;
299 }
300 
301 void HTTP::setCACertificateFile(const std::string &caCertificateFile) throw () {
302  this->caCertificateFile = caCertificateFile;
303 }
304 
305 void HTTP::setRequestBodyLimit(size_t requestBodyLimit) throw () {
306  if (requestBodyLimit > 0) {
307  this->requestBodyLimit = requestBodyLimit;
308  } else {
309  this->requestBodyLimit = RequestBodyLimitDefault;
310  }
311 }
312 
313 void HTTP::setConnectionLimit(size_t connectionLimit) throw () {
314  if (connectionLimit > 0) {
315  this->connectionLimit = connectionLimit;
316  } else {
317  this->connectionLimit = ConnectionLimitDefault;
318  }
319 }
320 
321 void HTTP::setRoot(const std::string &root) throw (ServerException) {
322  try {
323  this->root = File::getRealPath(root);
324  } catch (FileException &e) {
325  RETHROW(ServerException, e);
326  }
327 }
328 
329 void HTTP::setDirectoryIndex(const std::vector<std::string> &indexes) throw () {
330  this->indexes = indexes;
331 }
332 
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);
336  }
337 }
338 
339 void HTTP::setBanner(const std::string &banner) throw () {
340  this->banner = banner;
341 }
342 
343 void HTTP::setCustomHeaders(const std::vector<std::string> &headers) throw () {
344  this->headers = headers;
345 }
346 
347 void HTTP::setAllows(const std::set<HTTPRequest::Method> &methods) throw () {
348  allow = methods;
349 }
350 
351 void HTTP::setAllow(HTTPRequest::Method method) throw () {
352  allow.insert(method);
353 }
354 
355 void HTTP::setFallbackAcceptType(HTTPRequest::AcceptType fallbackAcceptType) throw () {
356  this->fallbackAcceptType = fallbackAcceptType;
357 }
358 
359 void HTTP::setCompressionThreshold(size_t threshold) throw () {
360  if (threshold < 1) {
361  this->compressionThreshold = 0;
362  } else {
363  this->compressionThreshold = threshold;
364  }
365 }
366 
367 void HTTP::setCompressionLevel(int level) throw () {
368  if (level < 0) {
369  this->compressionLevel = 0;
370  } else if (level > 9) {
371  this->compressionLevel = 9;
372  } else {
373  this->compressionLevel = level;
374  }
375 }
376 
377 void HTTP::setACLCallback(const acl_callback &callback) throw () {
378  if (callback) {
379  this->aclCallback = callback;
380  } else {
381  this->aclCallback = NULL;
382  }
383 }
384 
385 void HTTP::setAuthenticationCallback(const authentication_callback &callback) throw () {
386  if (callback) {
387  this->authCallback = callback;
388  } else {
389  this->authCallback = NULL;
390  }
391 }
392 
393 void HTTP::setRequestCallback(const request_callback &callback) throw () {
394  if (callback) {
395  this->reqCallback = callback;
396  } else {
397  this->reqCallback = http::request_callback;
398  }
399 }
400 
401 void HTTP::setErrorCallback(const error_callback &callback) throw () {
402  if (callback) {
403  this->errorCallback = callback;
404  } else {
405  this->errorCallback = http::error_callback;
406  }
407 }
408 
409 IPAddressPort::vector HTTP::getListeners(void) const throw () {
410  return listeners;
411 }
412 
413 std::string HTTP::getListenersString(void) const throw () {
414  std::string buf;
415 
416  if (!listeners.empty()) {
417  for (auto l = listeners.begin(); l != listeners.end(); ++l) {
418  buf += l->str() + ",";
419  }
420 
421  return buf.erase(buf.size() - 1);
422  }
423 
424  return buf;
425 }
426 
427 IPAddressPort::vector HTTP::getSecureListeners(void) const throw () {
428  return secureListeners;
429 }
430 
431 std::string HTTP::getSecureListenersString(void) const throw () {
432  std::string buf;
433 
434  if (!secureListeners.empty()) {
435  for (auto l = secureListeners.begin(); l != secureListeners.end(); ++l) {
436  buf += l->str() + ",";
437  }
438 
439  return buf.erase(buf.size() - 1);
440  }
441 
442  return buf;
443 }
444 
445 size_t HTTP::getRequestBodyLimit(void) const throw () {
446  return requestBodyLimit;
447 }
448 
449 size_t HTTP::getConnectionLimit(void) const throw () {
450  return connectionLimit;
451 }
452 
453 const std::string& HTTP::getRoot(void) const throw () {
454  return root;
455 }
456 
457 const std::vector<std::string>& HTTP::getDirectoryIndexes(void) const throw () {
458  return indexes;
459 }
460 
461 const std::string& HTTP::getBanner(void) const throw () {
462  return banner;
463 }
464 
465 const std::vector<std::string>& HTTP::getCustomHeaders(void) const throw () {
466  return headers;
467 }
468 
469 std::set<HTTPRequest::Method> HTTP::getAllow(void) const throw () {
470  return allow;
471 }
472 
473 HTTPRequest::AcceptType HTTP::getFallbackAcceptType(void) const throw () {
474  return fallbackAcceptType;
475 }
476 
477 size_t HTTP::getCompressionThreshold(void) const throw () {
478  return compressionThreshold;
479 }
480 
481 int HTTP::getCompressionLevel(void) const throw () {
482  return compressionLevel;
483 }
484 
485 void HTTP::clearAllow(HTTPRequest::Method method) throw () {
486  allow.erase(method);
487 }
488 
489 bool HTTP::isRunning(void) const throw () {
490  return running;
491 }
492 
493 void HTTP::clear(void) {
494  if (isRunning()) {
495  stop();
496  }
497 
498  pool.reset();
499  mux.clear();
500  servers.clear();
501  listeners = ListenersDefault();
502  secureListeners = SecureListenersDefault();
503  protocols = SecureTCPSocket::DefaultProtocols;
504  ciphers.clear();
505  preferServerCiphers = false;
506  certificateFile.clear();
507  privateKeyFile.clear();
508 
509  /* Scrub buffer before release back to stack. */
510  for (size_t i = 0; i < password.size(); i++) {
511  password.at(i) = 0;
512  }
513 
514  passwordFile.clear();
515  password.clear();
516  certificateChainFile.clear();
517  caCertificateFile.clear();
518  running = false;
519  std::queue<HTTPConnection::shared>().swap(pending);
520  requestBodyLimit = RequestBodyLimitDefault;
521  connectionLimit = ConnectionLimitDefault;
522  root = RootDefault();
523  indexes = DirectoryIndexesDefault();
524  banner = BannerDefault();
525  headers.clear();
526  allow = AllowDefault();
527  compressionThreshold = CompressionThresholdDefault;
528  compressionLevel = CompressionLevelDefault;
529  aclCallback = NULL;
530  authCallback = NULL;
531  reqCallback = http::request_callback;
532  errorCallback = http::error_callback;
533 }
534 
535 void HTTP::print(void) {
536  INFO("[HTTP]");
537  INFO(" listeners: %s", getListenersString().c_str());
538  INFO(" secureListeners: %s", getSecureListenersString().c_str());
539 
540  if (!protocols.empty()) {
541  std::string buf;
542  for (auto p = protocols.begin(); p != protocols.end(); ++p) {
543  switch (*p) {
544  case SecureTCPSocket::SSLv3:
545  buf.append("SSLv3 ");
546  break;
547 
548  case SecureTCPSocket::TLSv1_0:
549  buf.append("TLSv1 ");
550  break;
551 
552  case SecureTCPSocket::TLSv1_1:
553  buf.append("TLSv1.1 ");
554  break;
555 
556  case SecureTCPSocket::TLSv1_2:
557  buf.append("TLSv1.2 ");
558  break;
559  }
560  }
561 
562  INFO(" protocols: { %s}", buf.c_str());
563  } else {
564  INFO(" protocols: all");
565  }
566 
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());
581 // INFO(" allow: %s", allow);
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");
588 }
589 
590 const HTTP::shared HTTP::shared_ptr() throw (ServerException) {
591  return std::make_shared<HTTP>();
592 }
593 
594 const HTTP::shared HTTP::shared_ptr(Log::shared log) throw (ServerException) {
595  return std::make_shared<HTTP>(log);
596 }
597 
598 void HTTP::startup(void) throw (ServerException) {
599  mux.insertRead(trigger.getSocket());
600 
601  for (auto l = listeners.begin(); l != listeners.end(); ++l) {
602  try {
603  TCPSocket::shared s = TCPSocket::shared_ptr(l->getFamily());
604  s->setSoReuseAddr();
605  s->setBlock(true);
606 
607  if (s->getFamily() == AF_INET6) {
608  s->setIpv6V6Only(true);
609  }
610 
611  s->bind(*l);
612 
613  mux.insertRead(s);
614  servers.insert(s->getFD());
615 
616  s->listen();
617  } catch (SocketException &e) {
618  RETHROW(ServerException, e);
619  }
620  }
621 
622  for (auto l = secureListeners.begin(); l != secureListeners.end(); ++l) {
623  try {
624  SecureTCPSocket::shared s = SecureTCPSocket::shared_ptr(l->getFamily());
625  s->setSoReuseAddr();
626  s->setBlock(true);
627 
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);
637 
638  if (s->getFamily() == AF_INET6) {
639  s->setIpv6V6Only(true);
640  }
641 
642  s->bind(*l);
643 
644  mux.insertRead(s);
645  servers.insert(s->getFD());
646 
647  s->listen();
648  } catch (SocketException &e) {
649  RETHROW(ServerException, e);
650  }
651  }
652 }
653 
654 void HTTP::shutdown(void) throw () {
655  mux.clear();
656  servers.clear();
657 }
658 
659 int HTTP::select(timeval *tv) throw (ServerException) {
660  int c;
661 
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()) {
666  trigger.clear();
667 
668  return 0;
669  }
670 
671  HTTPConnection::shared connection = accept(*s);
672 
673  if (pending.size() < connectionLimit) {
674  pending.push(std::move(connection));
675  } else {
676  try {
677  errorCallback(connection, Code::ServiceUnavailable, std::string()); /* Service Unavailable */
678  } catch (ServerException &e) {
679  WARNING("HTTP::errorCallback() failed with code %d: %s\n", e.getCode(), e.what());
680  }
681 
682  connection->clear();
683  c--; /* Reduce new socket count. */
684  }
685  }
686  }
687 
688  return c;
689 }
690 
691 HTTPConnection::shared HTTP::accept(const Socket::shared &socket) throw (ServerException) {
692  try {
693  TCPSocket::shared server(std::dynamic_pointer_cast<TCPSocket>(socket));
694  TCPSocket::shared client = server->accept();
695 
696  if (client->isSecure()) {
697  TRACE(PACKAGE "-detail", "Incoming HTTPS connection from %s", client->getRemoteIPAddressPort().c_str());
698  } else {
699  TRACE(PACKAGE "-detail", "Incoming HTTP connection from %s", client->getRemoteIPAddressPort().c_str());
700  }
701 
702  return HTTPConnection::shared_ptr(*this, client);
703  } catch (SocketException &e) {
704  RETHROW(ServerException, e);
705  }
706 }
707 
708 void HTTP::process(HTTPConnection::shared &connection) throw () {
709  try {
710  if (aclCallback && !aclCallback(connection)) {
711  try {
712  errorCallback(connection, Code::Forbidden, std::string()); /* Forbidden */
713  } catch (ServerException &e) {
714  WARNING("HTTP::errorCallback() failed with code %d: %s\n", e.getCode(), e.what());
715  }
716  } else if (authCallback && !authCallback(connection)) {
717  try {
718  errorCallback(connection, Code::Unauthorized, std::string()); /* Unauthorized */
719  } catch (ServerException &e) {
720  WARNING("HTTP::errorCallback() failed with code %d: %s\n", e.getCode(), e.what());
721  }
722  } else {
723  reqCallback(connection);
724  }
725  } catch (ServerException &e) {
726  try {
727  if (e.getCode() > 0) {
728  errorCallback(connection, e.getCode(), e.getMessage());
729  } else {
730  errorCallback(connection, Code::InternalServerError, std::string()); /* Internal Server Error */
731  }
732  } catch (ServerException &e) {
733  WARNING("HTTP::errorCallback() failed with code %d: %s\n", e.getCode(), e.what());
734  }
735  } catch (std::exception &e) {
736  try {
737  errorCallback(connection, Code::InternalServerError, std::string()); /* Internal Server Error */
738  } catch (ServerException &e) {
739  WARNING("HTTP::errorCallback() failed with code %d: %s\n", e.getCode(), e.what());
740  }
741  }
742 }
743 
744 void HTTP::server(void) throw () {
745  int c;
746  timeval tv;
747  Task::shared task;
748  Task::callback function;
749 
750  while (running) {
751  try {
752  tv = { DelaySecondsDefault, 0 };
753 
754  while ((c = select(&tv)) > 0) {
755  while (!pending.empty()) {
756  HTTPConnection::shared connection = pending.front();
757  pending.pop();
758 
759  task = Task::shared_ptr();
760  function = std::bind(&HTTP::worker, this, connection);
761  task->setCallback(function);
762 
763  pool->schedule(task);
764  }
765  }
766  } catch (ServerException &e) {
767  WARNING("ServerException: %s", e.what());
768  }
769  }
770 
771  TRACE(PACKAGE, "HTTP server stopping...");
772 
773  std::lock_guard<std::mutex> lg(mutex);
774 
775  shutdown();
776  std::queue<HTTPConnection::shared>().swap(pending);
777 
778  pool->clear();
779  pool->wait();
780 
781  TRACE(PACKAGE, "HTTP server stopped");
782 }
783 
784 void HTTP::worker(HTTPConnection::shared &connection) throw () {
785  process(connection);
786 
787  connection->clear();
788 }
789 
790 /*
791 ** vim: noet ts=3 sw=3
792 */