1 /*
2 * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3 *
4 * All rights reserved.
5 */
6
7 #include "Wt/WServer.h"
8 #include "Wt/WLogger.h"
9 #include "Wt/WResource.h"
10
11 #include "Configuration.h"
12 #include "WebUtils.h"
13
14 #include <boost/algorithm/string.hpp>
15 #ifdef WT_BOOST_CONF_LOCK
16 #include <boost/thread.hpp>
17 #endif
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <fcntl.h>
21 #include <algorithm>
22 #include <array>
23 #include <vector>
24 #include <iostream>
25 #include <fstream>
26 #include <stdlib.h>
27 #include <regex>
28
29 #include <Wt/AsioWrapper/system_error.hpp>
30
31 #include "3rdparty/rapidxml/rapidxml.hpp"
32 #include "3rdparty/rapidxml/rapidxml_print.hpp"
33
34 #ifdef WT_WIN32
35 #include <io.h>
36 #include <process.h>
37 #endif
38
39 #if defined(WT_STD_CONF_LOCK)
40 #define READ_LOCK std::shared_lock<std::shared_mutex> lock(mutex_)
41 #define WRITE_LOCK std::unique_lock<std::shared_mutex> lock(mutex_)
42 #elif defined(WT_BOOST_CONF_LOCK)
43 #define READ_LOCK boost::shared_lock<boost::shared_mutex> lock(mutex_)
44 #define WRITE_LOCK boost::lock_guard<boost::shared_mutex> lock(mutex_)
45 #else
46 #define READ_LOCK
47 #define WRITE_LOCK
48 #endif // WT_CONF_LOCK
49
50 using namespace Wt::rapidxml;
51
52 namespace {
53
54 static const std::string EMPTY_STR;
55
56 using namespace Wt;
57
regexMatchAny(const std::string & agent,const std::vector<std::string> & regexList)58 bool regexMatchAny(const std::string& agent,
59 const std::vector<std::string>& regexList) {
60 WT_USTRING s = WT_USTRING::fromUTF8(agent);
61 for (unsigned i = 0; i < regexList.size(); ++i) {
62 std::regex expr(regexList[i]);
63
64 if (std::regex_match(s.toUTF8(), expr))
65 return true;
66 }
67
68 return false;
69 }
70
singleChildElement(xml_node<> * element,const char * tagName)71 xml_node<> *singleChildElement(xml_node<> *element, const char* tagName)
72 {
73 xml_node<> *result = element->first_node(tagName);
74 if (result) {
75 xml_node<> *next = result->next_sibling(tagName);
76
77 if (next) {
78 throw WServer::Exception
79 (std::string("Expected only one child <") + tagName
80 + "> in <" + element->name() + ">");
81 }
82 }
83
84 return result;
85 }
86
attributeValue(xml_node<> * element,const char * attributeName,std::string & result)87 bool attributeValue(xml_node<> *element, const char *attributeName,
88 std::string& result)
89 {
90 xml_attribute<> *attr = element->first_attribute(attributeName);
91
92 if (attr) {
93 result = attr->value();
94
95 return true;
96 } else
97 return false;
98 }
99
elementValue(xml_node<> * element,const char * elementName)100 std::string elementValue(xml_node<> *element, const char *elementName)
101 {
102 for (xml_node<> *e = element->first_node(); e; e = e->next_sibling())
103 if (e->type() != node_data && e->type() != node_cdata)
104 throw WServer::Exception(std::string("<")
105 + elementName
106 + "> should only contain text.");
107
108 return element->value();
109 }
110
singleChildElementValue(xml_node<> * element,const char * tagName,const std::string & defaultValue)111 std::string singleChildElementValue(xml_node<> *element, const char* tagName,
112 const std::string& defaultValue)
113 {
114 xml_node<> *child = singleChildElement(element, tagName);
115
116 if (!child)
117 return defaultValue;
118 else
119 return elementValue(child, tagName);
120 }
121
setBoolean(xml_node<> * element,const char * tagName,bool & result)122 void setBoolean(xml_node<> *element, const char *tagName, bool& result)
123 {
124 std::string v = singleChildElementValue(element, tagName, "");
125
126 if (!v.empty()) {
127 if (v == "true")
128 result = true;
129 else if (v == "false")
130 result = false;
131 else
132 throw WServer::Exception("<" + std::string(tagName)
133 + ">: expecting 'true' or 'false'");
134 }
135 }
136
setInt(xml_node<> * element,const char * tagName,int & result)137 void setInt(xml_node<> *element, const char *tagName, int& result)
138 {
139 std::string v = singleChildElementValue(element, tagName, "");
140
141 if (!v.empty()) {
142 try {
143 result = Utils::stoi(v);
144 } catch (std::exception& e) {
145 throw WServer::Exception("<" + std::string(tagName)
146 + ">: expecting integer value");
147 }
148 }
149 }
150
childElements(xml_node<> * element,const char * tagName)151 std::vector<xml_node<> *> childElements(xml_node<> *element,
152 const char *tagName)
153 {
154 std::vector<xml_node<> *> result;
155
156 for (xml_node<> *r = element->first_node(tagName); r;
157 r = r->next_sibling(tagName))
158 result.push_back(r);
159
160 return result;
161 }
162
163 template<std::size_t N>
prefixMatches(const std::array<unsigned char,N> & networkBytes,const std::array<unsigned char,N> & addressBytes,const unsigned char prefixLength)164 bool prefixMatches(const std::array<unsigned char, N> &networkBytes,
165 const std::array<unsigned char, N> &addressBytes,
166 const unsigned char prefixLength)
167 {
168 for (std::size_t i = 0; i < N; ++i) {
169 if ((i + 1) * 8 < prefixLength) {
170 if (networkBytes[i] != addressBytes[i]) {
171 return false;
172 }
173 } else {
174 const unsigned char shift = (i + 1) * 8 - prefixLength;
175 return (networkBytes[i] >> shift) == (addressBytes[i] >> shift);
176 }
177 }
178 return true;
179 }
180
181 static std::string FORWARD_SLASH = "/";
182
183 }
184
185 namespace Wt {
186
187 LOGGER("config");
188
HeadMatter(std::string contents,std::string userAgent)189 HeadMatter::HeadMatter(std::string contents,
190 std::string userAgent)
191 : contents_(contents),
192 userAgent_(userAgent)
193 { }
194
fromString(const std::string & s)195 Configuration::Network Configuration::Network::fromString(const std::string &s)
196 {
197 const auto slashPos = s.find('/');
198 if (slashPos == std::string::npos) {
199 AsioWrapper::error_code ec;
200 const auto address = AsioWrapper::asio::ip::address::from_string(s, ec);
201 if (ec) {
202 throw std::invalid_argument("'" + s + "' is not a valid IP address");
203 }
204 const unsigned char prefixLength = address.is_v6() ? 128 : 32;
205 return Network { address, prefixLength };
206 } else {
207 AsioWrapper::error_code ec;
208 const auto address = AsioWrapper::asio::ip::address::from_string(s.substr(0, slashPos), ec);
209 if (ec) {
210 throw std::invalid_argument("'" + s + "' is not a valid IP address");
211 }
212 const unsigned int prefixLength = Utils::stoi(s.substr(slashPos + 1));
213 if (prefixLength < 0 ||
214 (address.is_v4() && prefixLength > 32) ||
215 (address.is_v6() && prefixLength > 128)) {
216 throw std::invalid_argument("Invalid prefix length " + s.substr(slashPos + 1) + " for IPv" +
217 std::string(address.is_v4() ? "4" : "6") + " address");
218 }
219 return Network { address, static_cast<unsigned char>(prefixLength) };
220 }
221 }
222
contains(const AsioWrapper::asio::ip::address & address)223 bool Configuration::Network::contains(const AsioWrapper::asio::ip::address &address) const
224 {
225 if (this->address.is_v6() && address.is_v6()) {
226 const auto networkBytes = this->address.to_v6().to_bytes();
227 const auto addressBytes = address.to_v6().to_bytes();
228 return prefixMatches(networkBytes, addressBytes, prefixLength);
229 } else if (this->address.is_v4() && address.is_v4()) {
230 const auto networkBytes = this->address.to_v4().to_bytes();
231 const auto addressBytes = address.to_v4().to_bytes();
232 return prefixMatches(networkBytes, addressBytes, prefixLength);
233 } else {
234 return false;
235 }
236 }
237
Configuration(const std::string & applicationPath,const std::string & appRoot,const std::string & configurationFile,WServer * server)238 Configuration::Configuration(const std::string& applicationPath,
239 const std::string& appRoot,
240 const std::string& configurationFile,
241 WServer *server)
242 : server_(server),
243 applicationPath_(applicationPath),
244 appRoot_(appRoot),
245 configurationFile_(configurationFile),
246 runDirectory_(RUNDIR),
247 connectorSlashException_(false), // need to use ?_=
248 connectorNeedReadBody_(false),
249 connectorWebSockets_(true),
250 defaultEntryPoint_("/")
251 {
252 reset();
253 readConfiguration(false);
254 }
255
reset()256 void Configuration::reset()
257 {
258 sessionPolicy_ = SharedProcess;
259 numProcesses_ = 1;
260 numThreads_ = 10;
261 maxNumSessions_ = 100;
262 maxRequestSize_ = 128 * 1024;
263 maxFormDataSize_ = 5 * 1024 * 1024;
264 maxPendingEvents_ = 1000;
265 isapiMaxMemoryRequestSize_ = 128 * 1024;
266 sessionTracking_ = URL;
267 reloadIsNewSession_ = true;
268 sessionTimeout_ = 600;
269 idleTimeout_ = -1;
270 bootstrapTimeout_ = 10;
271 indicatorTimeout_ = 500;
272 doubleClickTimeout_ = 200;
273 serverPushTimeout_ = 50;
274 valgrindPath_ = "";
275 errorReporting_ = ErrorMessage;
276 if (!runDirectory_.empty()) // disabled by connector
277 runDirectory_ = RUNDIR;
278 sessionIdLength_ = 16;
279 properties_.clear();
280 xhtmlMimeType_ = false;
281 behindReverseProxy_ = false;
282 originalIPHeader_ = "X-Forwarded-For";
283 trustedProxies_.clear();
284 redirectMsg_ = "Load basic HTML";
285 serializedEvents_ = false;
286 webSockets_ = false;
287 inlineCss_ = true;
288 ajaxAgentList_.clear();
289 botList_.clear();
290 ajaxAgentWhiteList_ = false;
291 persistentSessions_ = false;
292 splitScript_ = false;
293 maxPlainSessionsRatio_ = 1;
294 ajaxPuzzle_ = false;
295 sessionIdCookie_ = false;
296 cookieChecks_ = true;
297 webglDetection_ = true;
298 bootstrapConfig_.clear();
299 numSessionThreads_ = -1;
300 allowedOrigins_.clear();
301
302 if (!appRoot_.empty())
303 setAppRoot(appRoot_);
304 }
305
setAppRoot(const std::string & path)306 void Configuration::setAppRoot(const std::string& path)
307 {
308 appRoot_ = path;
309 properties_["appRoot"] = appRoot_;
310 }
311
sessionPolicy()312 Configuration::SessionPolicy Configuration::sessionPolicy() const
313 {
314 READ_LOCK;
315 return sessionPolicy_;
316 }
317
numProcesses()318 int Configuration::numProcesses() const
319 {
320 READ_LOCK;
321 return numProcesses_;
322 }
323
numThreads()324 int Configuration::numThreads() const
325 {
326 READ_LOCK;
327 return numThreads_;
328 }
329
maxNumSessions()330 int Configuration::maxNumSessions() const
331 {
332 READ_LOCK;
333 return maxNumSessions_;
334 }
335
maxRequestSize()336 ::int64_t Configuration::maxRequestSize() const
337 {
338 return maxRequestSize_;
339 }
340
341
maxFormDataSize()342 ::int64_t Configuration::maxFormDataSize() const
343 {
344 return maxFormDataSize_;
345 }
346
maxPendingEvents()347 int Configuration::maxPendingEvents() const
348 {
349 return maxPendingEvents_;
350 }
351
isapiMaxMemoryRequestSize()352 ::int64_t Configuration::isapiMaxMemoryRequestSize() const
353 {
354 READ_LOCK;
355 return isapiMaxMemoryRequestSize_;
356 }
357
sessionTracking()358 Configuration::SessionTracking Configuration::sessionTracking() const
359 {
360 READ_LOCK;
361 return sessionTracking_;
362 }
363
reloadIsNewSession()364 bool Configuration::reloadIsNewSession() const
365 {
366 READ_LOCK;
367 return reloadIsNewSession_;
368 }
369
sessionTimeout()370 int Configuration::sessionTimeout() const
371 {
372 READ_LOCK;
373 return sessionTimeout_;
374 }
375
idleTimeout()376 int Configuration::idleTimeout() const
377 {
378 READ_LOCK;
379 return idleTimeout_;
380 }
381
keepAlive()382 int Configuration::keepAlive() const
383 {
384 int timeout = sessionTimeout();
385 if (timeout == -1)
386 return 1000000;
387 else
388 return timeout / 2;
389 }
390
391 // The multisession cookie timeout should be longer than
392 // sessionTimeout() + keepAlive(), to avoid the situation
393 // where the session has not timed out yet, but the multi
394 // session cookie has expired. Let's just set
395 // multiSessionCookieTimeout() to sessionTimeout() * 2
multiSessionCookieTimeout()396 int Configuration::multiSessionCookieTimeout() const
397 {
398 return sessionTimeout() * 2;
399 }
400
bootstrapTimeout()401 int Configuration::bootstrapTimeout() const
402 {
403 READ_LOCK;
404 return bootstrapTimeout_;
405 }
406
indicatorTimeout()407 int Configuration::indicatorTimeout() const
408 {
409 READ_LOCK;
410 return indicatorTimeout_;
411 }
412
doubleClickTimeout()413 int Configuration::doubleClickTimeout() const
414 {
415 READ_LOCK;
416 return doubleClickTimeout_;
417 }
418
serverPushTimeout()419 int Configuration::serverPushTimeout() const
420 {
421 READ_LOCK;
422 return serverPushTimeout_;
423 }
424
valgrindPath()425 std::string Configuration::valgrindPath() const
426 {
427 READ_LOCK;
428 return valgrindPath_;
429 }
430
errorReporting()431 Configuration::ErrorReporting Configuration::errorReporting() const
432 {
433 READ_LOCK;
434 return errorReporting_;
435 }
436
debug()437 bool Configuration::debug() const
438 {
439 READ_LOCK;
440 return errorReporting_ != ErrorMessage;
441 }
442
runDirectory()443 std::string Configuration::runDirectory() const
444 {
445 READ_LOCK;
446 return runDirectory_;
447 }
448
sessionIdLength()449 int Configuration::sessionIdLength() const
450 {
451 READ_LOCK;
452 return sessionIdLength_;
453 }
454
sessionIdPrefix()455 std::string Configuration::sessionIdPrefix() const
456 {
457 READ_LOCK;
458 return connectorSessionIdPrefix_;
459 }
460
fullSessionIdLength()461 int Configuration::fullSessionIdLength() const
462 {
463 READ_LOCK;
464 return sessionIdLength_ + static_cast<int>(connectorSessionIdPrefix_.size());
465 }
466
behindReverseProxy()467 bool Configuration::behindReverseProxy() const
468 {
469 READ_LOCK;
470 return behindReverseProxy_;
471 }
472
originalIPHeader()473 std::string Configuration::originalIPHeader() const {
474 READ_LOCK;
475 return originalIPHeader_;
476 }
477
trustedProxies()478 std::vector<Configuration::Network> Configuration::trustedProxies() const {
479 READ_LOCK;
480 return trustedProxies_;
481 }
482
isTrustedProxy(const std::string & ipAddress)483 bool Configuration::isTrustedProxy(const std::string &ipAddress) const {
484 READ_LOCK;
485 AsioWrapper::error_code ec;
486 const auto address = AsioWrapper::asio::ip::address::from_string(ipAddress, ec);
487 if (ec) {
488 return false;
489 }
490 return std::any_of(begin(trustedProxies_), end(trustedProxies_), [&address](const Network &network) {
491 return network.contains(address);
492 });
493 }
494
redirectMessage()495 std::string Configuration::redirectMessage() const
496 {
497 READ_LOCK;
498 return redirectMsg_;
499 }
500
serializedEvents()501 bool Configuration::serializedEvents() const
502 {
503 READ_LOCK;
504 return serializedEvents_;
505 }
506
webSockets()507 bool Configuration::webSockets() const
508 {
509 return webSockets_;
510 }
511
inlineCss()512 bool Configuration::inlineCss() const
513 {
514 READ_LOCK;
515 return inlineCss_;
516 }
517
persistentSessions()518 bool Configuration::persistentSessions() const
519 {
520 READ_LOCK;
521 return persistentSessions_;
522 }
523
progressiveBoot(const std::string & internalPath)524 bool Configuration::progressiveBoot(const std::string& internalPath) const
525 {
526 READ_LOCK;
527 bool result = false;
528
529 for (unsigned i = 0; i < bootstrapConfig_.size(); ++i) {
530 const BootstrapEntry& e = bootstrapConfig_[i];
531 if (e.prefix) {
532 if (internalPath == e.path ||
533 boost::starts_with(internalPath, e.path + '/'))
534 result = e.method == Progressive;
535 } else
536 if (internalPath == e.path)
537 result = e.method == Progressive;
538 }
539
540 return result;
541 }
542
splitScript()543 bool Configuration::splitScript() const
544 {
545 READ_LOCK;
546 return splitScript_;
547 }
548
maxPlainSessionsRatio()549 float Configuration::maxPlainSessionsRatio() const
550 {
551 READ_LOCK;
552 return maxPlainSessionsRatio_;
553 }
554
ajaxPuzzle()555 bool Configuration::ajaxPuzzle() const
556 {
557 READ_LOCK;
558 return ajaxPuzzle_;
559 }
560
uaCompatible()561 std::string Configuration::uaCompatible() const
562 {
563 READ_LOCK;
564 return uaCompatible_;
565 }
566
sessionIdCookie()567 bool Configuration::sessionIdCookie() const
568 {
569 READ_LOCK;
570 return sessionIdCookie_;
571 }
572
cookieChecks()573 bool Configuration::cookieChecks() const
574 {
575 READ_LOCK;
576 return cookieChecks_;
577 }
578
useSlashExceptionForInternalPaths()579 bool Configuration::useSlashExceptionForInternalPaths() const
580 {
581 return connectorSlashException_;
582 }
583
needReadBodyBeforeResponse()584 bool Configuration::needReadBodyBeforeResponse() const
585 {
586 return connectorNeedReadBody_;
587 }
588
webglDetect()589 bool Configuration::webglDetect() const
590 {
591 READ_LOCK;
592 return webglDetection_;
593 }
594
numSessionThreads()595 int Configuration::numSessionThreads() const
596 {
597 READ_LOCK;
598 return numSessionThreads_;
599 }
600
isAllowedOrigin(const std::string & origin)601 bool Configuration::isAllowedOrigin(const std::string &origin) const
602 {
603 READ_LOCK;
604 if (allowedOrigins_.size() == 1 &&
605 allowedOrigins_[0] == "*")
606 return true;
607 else {
608 for (std::size_t i = 0; i < allowedOrigins_.size(); ++i) {
609 if (origin == allowedOrigins_[i])
610 return true;
611 }
612 return false;
613 }
614 }
615
agentIsBot(const std::string & agent)616 bool Configuration::agentIsBot(const std::string& agent) const
617 {
618 READ_LOCK;
619
620 return regexMatchAny(agent, botList_);
621 }
622
agentSupportsAjax(const std::string & agent)623 bool Configuration::agentSupportsAjax(const std::string& agent) const
624 {
625 READ_LOCK;
626
627 bool matches = regexMatchAny(agent, ajaxAgentList_);
628 if (ajaxAgentWhiteList_)
629 return matches;
630 else
631 return !matches;
632 }
633
appRoot()634 std::string Configuration::appRoot() const
635 {
636 READ_LOCK;
637
638 std::string approot;
639
640 if (!readConfigurationProperty("appRoot", approot)) {
641 return "";
642 }
643
644 if (!approot.empty() && approot[approot.length() - 1] != '/'
645 #ifdef WT_WIN32
646 && approot[approot.length() - 1] != '\\'
647 #endif
648 ) {
649 approot += "/";
650 }
651
652 return approot;
653 }
654
locateAppRoot()655 std::string Configuration::locateAppRoot()
656 {
657 char *value;
658
659 if ((value = ::getenv("WT_APP_ROOT")))
660 return value;
661 else
662 return std::string();
663 }
664
locateConfigFile(const std::string & appRoot)665 std::string Configuration::locateConfigFile(const std::string& appRoot)
666 {
667 char *value;
668
669 if ((value = ::getenv("WT_CONFIG_XML")))
670 return value;
671 else {
672 // Configuration file could be $WT_APP_ROOT/wt_config.xml
673 if (!appRoot.empty()) {
674 std::string result = appRoot + "/wt_config.xml";
675 std::ifstream s(result.c_str(), std::ios::in | std::ios::binary);
676 if (s)
677 return result;
678 }
679
680 return WT_CONFIG_XML;
681 }
682 }
683
registerEntryPoint(const EntryPoint & ep)684 void Configuration::registerEntryPoint(const EntryPoint &ep)
685 {
686 const std::string &path = ep.path();
687
688 assert(path[0] == '/');
689
690 // The PathSegment in the routing tree where this entrypoint will end up
691 PathSegment *pathSegment = &rootPathSegment_;
692
693 typedef boost::split_iterator<std::string::const_iterator> spliterator;
694 for (spliterator it = spliterator(path.begin() + 1, path.end(),
695 boost::first_finder(FORWARD_SLASH, boost::is_equal()));
696 it != spliterator(); ++it) {
697 PathSegment *childSegment = nullptr;
698 if (boost::starts_with(*it, "${") &&
699 boost::ends_with(*it, "}")) {
700 // This is a dynamic segment, e.g. ${var}
701 if (!pathSegment->dynamicChild) {
702 pathSegment->dynamicChild = std::unique_ptr<PathSegment>(new PathSegment("", pathSegment));
703 }
704 childSegment = pathSegment->dynamicChild.get();
705 } else {
706 // This is a normal segment
707 auto &children = pathSegment->children;
708 auto c = std::find_if(children.begin(), children.end(), [&it](const std::unique_ptr<PathSegment> &c) {
709 return c->segment == *it;
710 });
711 if (c != children.end())
712 childSegment = c->get();
713
714 if (!childSegment) {
715 if (it->empty()) {
716 // Empty part (entry point with trailing slash)
717 // Put it in front by convention
718 children.insert(children.begin(), std::make_unique<PathSegment>("", pathSegment));
719 childSegment = children.front().get();
720 } else {
721 children.push_back(std::make_unique<PathSegment>(boost::copy_range<std::string>(*it), pathSegment));
722 childSegment = children.back().get();
723 }
724 }
725 }
726 pathSegment = childSegment;
727 }
728
729 pathSegment->entryPoint = &ep;
730 }
731
addEntryPoint(const EntryPoint & ep)732 void Configuration::addEntryPoint(const EntryPoint& ep)
733 {
734 if (ep.type() == EntryPointType::StaticResource)
735 ep.resource()->currentUrl_ = ep.path();
736
737 WRITE_LOCK;
738 entryPoints_.push_back(ep);
739
740 registerEntryPoint(entryPoints_.back());
741 }
742
tryAddResource(const EntryPoint & ep)743 bool Configuration::tryAddResource(const EntryPoint& ep)
744 {
745 WRITE_LOCK;
746 for (std::size_t i = 0; i < entryPoints_.size(); ++i) {
747 if (entryPoints_[i].path() == ep.path()) {
748 return false;
749 }
750 }
751
752 if (ep.type() == EntryPointType::StaticResource)
753 ep.resource()->currentUrl_ = ep.path();
754
755 entryPoints_.push_back(ep);
756
757 registerEntryPoint(entryPoints_.back());
758
759 return true;
760 }
761
removeEntryPoint(const std::string & path)762 void Configuration::removeEntryPoint(const std::string& path)
763 {
764 for (unsigned i = 0; i < entryPoints_.size(); ++i) {
765 const EntryPoint &ep = entryPoints_[i];
766 if (ep.path() == path) {
767 rootPathSegment_.children.clear();
768 entryPoints_.erase(entryPoints_.begin() + i);
769 for (std::size_t j = 0; j < entryPoints_.size(); ++j) {
770 registerEntryPoint(entryPoints_[j]);
771 }
772 break;
773 }
774 }
775 }
776
setDefaultEntryPoint(const std::string & path)777 void Configuration::setDefaultEntryPoint(const std::string& path)
778 {
779 defaultEntryPoint_ = path;
780 }
781
matchEntryPoint(const std::string & scriptName,const std::string & path,bool matchAfterSlash)782 EntryPointMatch Configuration::matchEntryPoint(const std::string &scriptName,
783 const std::string &path,
784 bool matchAfterSlash) const
785 {
786 if (!scriptName.empty()) {
787 LOG_DEBUG("matchEntryPoint: matching entry point, scriptName: '" << scriptName << "', path: '" << path << '\'');
788 EntryPointMatch m = matchEntryPoint(EMPTY_STR, scriptName + path, matchAfterSlash);
789 if (m.entryPoint)
790 return m;
791 else
792 return matchEntryPoint(EMPTY_STR, path, matchAfterSlash);
793 }
794 LOG_DEBUG("matchEntryPoint: matching entry point, path: '" << path << '\'');
795
796 READ_LOCK;
797 // Only one default entry point.
798 if (entryPoints_.size() == 1
799 && entryPoints_[0].path().empty()) {
800 LOG_DEBUG("matchEntryPoint: only one entry point: matching path '" << path << "' with default entry point");
801 return EntryPointMatch(&entryPoints_[0], 0);
802 }
803
804 assert(path.empty() || path[0] == '/');
805
806 const PathSegment *pathSegment = &rootPathSegment_;
807
808 // Flag marking whether any of the matched segments is dynamic
809 bool dynamic = false;
810
811 typedef boost::split_iterator<std::string::const_iterator> spliterator;
812
813 spliterator it;
814 if (!path.empty()) {
815 it = spliterator(path.begin() + 1, path.end(),
816 boost::first_finder(FORWARD_SLASH, boost::is_equal()));
817 // Move down the routing tree, segment per segment
818 for (;it != spliterator(); ++it) {
819 // Find exact path match for segment
820 const auto &children = pathSegment->children;
821 const PathSegment *childSegment = nullptr;
822 auto c = std::find_if(children.begin(), children.end(), [&it](const std::unique_ptr<PathSegment> &c) {
823 return c->segment == *it;
824 });
825 if (c != children.end())
826 childSegment = c->get();
827
828 // No exact match, see if there is a dynamic segment
829 if (!childSegment &&
830 !it->empty() &&
831 pathSegment->dynamicChild) {
832 childSegment = pathSegment->dynamicChild.get();
833 dynamic = true;
834 }
835
836 // No match, deepest match reached
837 if (!childSegment)
838 break;
839
840 // Move to the next segment
841 pathSegment = childSegment;
842 }
843 } else {
844 matchAfterSlash = true;
845 }
846
847 // Move up from the found segment, until we find one that corresponds to an entrypoint
848 const EntryPoint *match = nullptr;
849 for (; pathSegment != nullptr; pathSegment = pathSegment->parent) {
850 // If matchAfterSlash is true,
851 // then the path /head/tail
852 // may match the entry point /head/
853 if (matchAfterSlash && (path.empty() || it != spliterator())) {
854 const auto &children = pathSegment->children;
855 if (!children.empty() && children.front()->segment.empty()) {
856 match = children.front()->entryPoint;
857 break;
858 }
859 }
860 // If the current segment has an entrypoint, we're done
861 if (pathSegment->entryPoint) {
862 match = pathSegment->entryPoint;
863 break;
864 }
865 }
866
867 if (match && dynamic) {
868 // Process path parameters
869 EntryPointMatch result;
870 result.entryPoint = match;
871 // Iterate concurrently over the path (it1),
872 // and the matched endpoint's path (it2)
873 spliterator it1 = spliterator(path.begin() + 1, path.end(),
874 boost::first_finder(FORWARD_SLASH, boost::is_equal()));
875 for (spliterator it2 = spliterator(match->path().begin() + 1, match->path().end(),
876 boost::first_finder(FORWARD_SLASH, boost::is_equal()));
877 it1 != spliterator() && it2 != spliterator(); ++it1, ++it2) {
878 // Check dynamic segment (e.g. "${var}")
879 if (boost::starts_with(*it2, "${") &&
880 boost::ends_with(*it2, "}")) {
881 auto range = boost::iterator_range<std::string::const_iterator>(it2->begin() + 2, it2->end() - 1);
882 result.urlParams.push_back(std::make_pair(boost::copy_range<std::string>(range),
883 boost::copy_range<std::string>(*it1)));
884 }
885 }
886 if (it1 == spliterator())
887 result.extra = path.size(); // no extra path
888 else
889 result.extra = std::distance(path.begin(), it1->begin()) - 1; // there's more
890
891 LOG_DEBUG("matchEntryPoint: path '" << path << "' matches dynamic entry point: '" << match->path() << '\'');
892 return result;
893 } else if (match) {
894 LOG_DEBUG("matchEntryPoint: path '" << path << "' matches entry point: '" << match->path() << '\'');
895 return EntryPointMatch(match, path.empty() ? 0 : match->path().size()); // simple match
896 } else {
897 LOG_DEBUG("matchEntryPoint: no entry point match found for path: '" << path << '\'');
898 return EntryPointMatch(); // no match
899 }
900 }
901
matchesPath(const std::string & path,const std::string & prefix,bool matchAfterSlash)902 bool Configuration::matchesPath(const std::string &path,
903 const std::string &prefix,
904 bool matchAfterSlash)
905 {
906 if (boost::starts_with(path, prefix)) {
907 std::size_t prefixLength = prefix.length();
908
909 if (path.length() > prefixLength) {
910 char next = path[prefixLength];
911
912 if (next == '/')
913 return true;
914 else if (matchAfterSlash) {
915 char last = prefix[prefixLength - 1];
916
917 if (last == '/')
918 return true;
919 }
920 } else
921 return true;
922 }
923
924 return false;
925 }
926
setSessionTimeout(int sessionTimeout)927 void Configuration::setSessionTimeout(int sessionTimeout)
928 {
929 sessionTimeout_ = sessionTimeout;
930 }
931
setSessionIdPrefix(const std::string & prefix)932 void Configuration::setSessionIdPrefix(const std::string& prefix)
933 {
934 connectorSessionIdPrefix_ = prefix;
935 }
936
setWebSockets(bool enabled)937 void Configuration::setWebSockets(bool enabled)
938 {
939 connectorWebSockets_ = enabled;
940 if (!enabled)
941 webSockets_ = false;
942 }
943
setNeedReadBodyBeforeResponse(bool needed)944 void Configuration::setNeedReadBodyBeforeResponse(bool needed)
945 {
946 connectorNeedReadBody_ = needed;
947 }
948
setUseSlashExceptionForInternalPaths(bool use)949 void Configuration::setUseSlashExceptionForInternalPaths(bool use)
950 {
951 connectorSlashException_ = use;
952 }
953
setRunDirectory(const std::string & path)954 void Configuration::setRunDirectory(const std::string& path)
955 {
956 runDirectory_ = path;
957 }
958
setNumThreads(int threads)959 void Configuration::setNumThreads(int threads)
960 {
961 numThreads_ = threads;
962 }
963
setBehindReverseProxy(bool enabled)964 void Configuration::setBehindReverseProxy(bool enabled)
965 {
966 behindReverseProxy_ = enabled;
967 }
968
setOriginalIPHeader(const std::string & originalIPHeader)969 void Configuration::setOriginalIPHeader(const std::string &originalIPHeader)
970 {
971 originalIPHeader_ = originalIPHeader;
972 }
973
setTrustedProxies(const std::vector<Network> & trustedProxies)974 void Configuration::setTrustedProxies(const std::vector<Network> &trustedProxies)
975 {
976 trustedProxies_ = trustedProxies;
977 }
978
setBootstrapMethod(BootstrapMethod method)979 void Configuration::setBootstrapMethod(BootstrapMethod method)
980 {
981 bootstrapConfig_.clear();
982 if (method == Progressive) {
983 bootstrapConfig_.insert(bootstrapConfig_.begin(), BootstrapEntry());
984 bootstrapConfig_.front().prefix = true;
985 bootstrapConfig_.front().method = Progressive;
986 }
987 }
988
readApplicationSettings(xml_node<> * app)989 void Configuration::readApplicationSettings(xml_node<> *app)
990 {
991 xml_node<> *sess = singleChildElement(app, "session-management");
992
993 if (sess) {
994 xml_node<> *dedicated = singleChildElement(sess, "dedicated-process");
995 xml_node<> *shared = singleChildElement(sess, "shared-process");
996 std::string tracking = singleChildElementValue(sess, "tracking", "");
997
998 if (dedicated && shared)
999 throw WServer::Exception("<application-settings> requires either "
1000 "<dedicated-process> or <shared-process>, "
1001 "not both");
1002
1003 if (dedicated) {
1004 sessionPolicy_ = DedicatedProcess;
1005 setInt(dedicated, "max-num-sessions", maxNumSessions_);
1006 setInt(dedicated, "num-session-threads", numSessionThreads_);
1007 }
1008
1009 if (shared) {
1010 sessionPolicy_ = SharedProcess;
1011
1012 setInt(shared, "num-processes", numProcesses_);
1013 }
1014
1015 if (!tracking.empty()) {
1016 if (tracking == "Auto")
1017 sessionTracking_ = CookiesURL;
1018 else if (tracking == "URL")
1019 sessionTracking_ = URL;
1020 else if (tracking == "Combined")
1021 sessionTracking_ = Combined;
1022 else
1023 throw WServer::Exception("<session-tracking>: expecting 'Auto', "
1024 "'URL', or 'Combined'");
1025 }
1026
1027 setInt(sess, "timeout", sessionTimeout_);
1028 setInt(sess, "idle-timeout", idleTimeout_);
1029 setInt(sess, "bootstrap-timeout", bootstrapTimeout_);
1030 setInt(sess, "server-push-timeout", serverPushTimeout_);
1031 setBoolean(sess, "reload-is-new-session", reloadIsNewSession_);
1032 }
1033
1034 std::string maxRequestStr
1035 = singleChildElementValue(app, "max-request-size", "");
1036 if (!maxRequestStr.empty())
1037 maxRequestSize_ = Utils::stoll(maxRequestStr) * 1024;
1038
1039 std::string maxFormDataStr =
1040 singleChildElementValue(app, "max-formdata-size", "");
1041 if (!maxFormDataStr.empty())
1042 maxFormDataSize_ = Utils::stoll(maxFormDataStr) * 1024;
1043
1044 std::string maxPendingEventsStr =
1045 singleChildElementValue(app, "max-pending-events", "");
1046 if (!maxPendingEventsStr.empty())
1047 maxPendingEvents_ = Utils::stoi(maxPendingEventsStr);
1048
1049 std::string debugStr = singleChildElementValue(app, "debug", "");
1050
1051 if (!debugStr.empty()) {
1052 if (debugStr == "stack" || debugStr == "false")
1053 errorReporting_ = ErrorMessage;
1054 else if (debugStr == "naked")
1055 errorReporting_ = NoErrors;
1056 else if (debugStr == "true")
1057 errorReporting_ = ServerSideOnly;
1058 else
1059 throw WServer::Exception("<debug>: expecting 'true', 'false',"
1060 "'naked', or 'stack'");
1061 }
1062
1063 setInt(app, "num-threads", numThreads_);
1064
1065 xml_node<> *fcgi = singleChildElement(app, "connector-fcgi");
1066 if (!fcgi)
1067 fcgi = app; // backward compatibility
1068
1069 valgrindPath_ = singleChildElementValue(fcgi, "valgrind-path",
1070 valgrindPath_);
1071 runDirectory_ = singleChildElementValue(fcgi, "run-directory",
1072 runDirectory_);
1073
1074 xml_node<> *isapi = singleChildElement(app, "connector-isapi");
1075 if (!isapi)
1076 isapi = app; // backward compatibility
1077
1078 std::string maxMemoryRequestSizeStr =
1079 singleChildElementValue(isapi, "max-memory-request-size", "");
1080 if (!maxMemoryRequestSizeStr.empty()) {
1081 isapiMaxMemoryRequestSize_ = Utils::stol(maxMemoryRequestSizeStr) * 1024;
1082 }
1083
1084 setInt(app, "session-id-length", sessionIdLength_);
1085
1086 /*
1087 * If a session-id-prefix is defined in the configuration file, then
1088 * we loose the prefix defined by the connector (e.g. wthttpd), but who
1089 * would do such a thing ? */
1090 connectorSessionIdPrefix_
1091 = singleChildElementValue(app,"session-id-prefix",
1092 connectorSessionIdPrefix_);
1093
1094 setBoolean(app, "send-xhtml-mime-type", xhtmlMimeType_);
1095 if (xhtmlMimeType_)
1096 LOG_WARN("ignoring send-xhtml-mime-type setting: HTML5 is now always used");
1097 redirectMsg_ = singleChildElementValue(app, "redirect-message", redirectMsg_);
1098
1099 setBoolean(app, "behind-reverse-proxy", behindReverseProxy_);
1100 if (behindReverseProxy_) {
1101 LOG_WARN("The behind-reverse-proxy configuration option is deprecated, "
1102 "use a <trusted-proxy-config> block instead");
1103 }
1104
1105 xml_node<> *trustedProxyCfg = singleChildElement(app, "trusted-proxy-config");
1106
1107 if (trustedProxyCfg) {
1108 originalIPHeader_ = singleChildElementValue(app, "original-ip-header", originalIPHeader_);
1109
1110 xml_node<> *trustedProxies = singleChildElement(trustedProxyCfg, "trusted-proxies");
1111
1112 std::vector<xml_node<> *> proxies = childElements(trustedProxies, "proxy");
1113 for (const auto *proxyNode : proxies) {
1114 try {
1115 std::string value = proxyNode->value();
1116 boost::trim(value);
1117 trustedProxies_.push_back(Network::fromString(value));
1118 } catch (const std::invalid_argument &e) {
1119 throw WServer::Exception("Invalid trusted <proxy>: " + std::string(e.what()));
1120 }
1121 }
1122 }
1123
1124 setBoolean(app, "strict-event-serialization", serializedEvents_);
1125 setBoolean(app, "web-sockets", webSockets_);
1126
1127 setBoolean(app, "inline-css", inlineCss_);
1128 setBoolean(app, "persistent-sessions", persistentSessions_);
1129
1130 uaCompatible_ = singleChildElementValue(app, "UA-Compatible", "");
1131
1132 bool progressive = false;
1133 setBoolean(app, "progressive-bootstrap", progressive);
1134
1135 xml_node<> *bootstrap = singleChildElement(app, "bootstrap-method");
1136 if (bootstrap) {
1137 progressive = std::string(bootstrap->value()) == "progressive";
1138
1139 std::vector<xml_node<> *> entries = childElements(bootstrap, "for");
1140 for (unsigned i = 0; i < entries.size(); ++i) {
1141 xml_node<> *entry = entries[i];
1142
1143 std::string path;
1144 if (!attributeValue(entry, "path", path) || path.empty())
1145 throw WServer::Exception("<for> requires attribute 'path'");
1146
1147 bootstrapConfig_.push_back(BootstrapEntry());
1148 BootstrapEntry& e = bootstrapConfig_.back();
1149
1150 e.prefix = path[path.length() - 1] == '*';
1151 e.method = std::string(entry->value()) == "progressive"
1152 ? Progressive : DetectAjax;
1153 if (e.prefix) {
1154 e.path = path.substr(0, path.length() - 1);
1155 if (!e.path.empty() && e.path[e.path.length() - 1] == '/')
1156 e.path.erase(e.path.length() - 1);
1157 } else
1158 e.path = path;
1159 }
1160 }
1161
1162 if (progressive) {
1163 bootstrapConfig_.insert(bootstrapConfig_.begin(), BootstrapEntry());
1164 bootstrapConfig_.front().prefix = true;
1165 bootstrapConfig_.front().method = Progressive;
1166 }
1167
1168 if (progressive)
1169 setBoolean(app, "split-script", splitScript_);
1170 setBoolean(app, "session-id-cookie", sessionIdCookie_);
1171 setBoolean(app, "cookie-checks", cookieChecks_);
1172 setBoolean(app, "webgl-detection", webglDetection_);
1173
1174 std::string plainAjaxSessionsRatioLimit
1175 = singleChildElementValue(app, "plain-ajax-sessions-ratio-limit", "");
1176
1177 if (!plainAjaxSessionsRatioLimit.empty())
1178 maxPlainSessionsRatio_ = Utils::stof(plainAjaxSessionsRatioLimit);
1179
1180 setBoolean(app, "ajax-puzzle", ajaxPuzzle_);
1181 setInt(app, "indicator-timeout", indicatorTimeout_);
1182 setInt(app, "double-click-timeout", doubleClickTimeout_);
1183
1184 std::vector<xml_node<> *> userAgents = childElements(app, "user-agents");
1185
1186 for (unsigned i = 0; i < userAgents.size(); ++i) {
1187 xml_node<> *userAgentsList = userAgents[i];
1188
1189 std::string type;
1190 if (!attributeValue(userAgentsList, "type", type))
1191 throw WServer::Exception("<user-agents> requires attribute 'type'");
1192
1193 std::string mode;
1194 attributeValue(userAgentsList, "mode", mode);
1195
1196 AgentList *list;
1197 if (type == "ajax") {
1198 list = &ajaxAgentList_;
1199 if (mode == "black-list")
1200 ajaxAgentWhiteList_ = false;
1201 else if (mode == "white-list")
1202 ajaxAgentWhiteList_ = true;
1203 else
1204 throw WServer::Exception
1205 ("<user-agents type=\"ajax\" requires attribute 'mode' with value "
1206 "\"white-list\" or \"black-list\"");
1207 } else if (type == "bot")
1208 list = &botList_;
1209 else
1210 throw WServer::Exception
1211 ("<user-agents> requires attribute 'type' with value "
1212 "\"ajax\" or \"bot\"");
1213
1214 std::vector<xml_node<> *> agents
1215 = childElements(userAgentsList, "user-agent");
1216
1217 for (unsigned j = 0; j < agents.size(); ++j)
1218 list->push_back(elementValue(agents[j], "user-agent"));
1219 }
1220
1221 xml_node<> *properties = singleChildElement(app, "properties");
1222
1223 if (properties) {
1224 std::vector<xml_node<> *> nodes = childElements(properties, "property");
1225
1226 for (unsigned i = 0; i < nodes.size(); ++i) {
1227 xml_node<> *property = nodes[i];
1228
1229 std::string name;
1230 if (!attributeValue(property, "name", name))
1231 throw WServer::Exception("<property> requires attribute 'name'");
1232
1233 std::string value = elementValue(property, "property");
1234
1235 if (name == "approot")
1236 name = "appRoot";
1237
1238 if (name == "appRoot" && !appRoot_.empty())
1239 LOG_WARN("ignoring configuration property 'appRoot' ("
1240 << value
1241 << ") because was already set to " << appRoot_);
1242 else
1243 properties_[name] = value;
1244 }
1245 }
1246
1247 // deprecated
1248 std::vector<xml_node<> *> metaHeaders = childElements(app, "meta-headers");
1249 for (unsigned i = 0; i < metaHeaders.size(); ++i) {
1250 xml_node<> *metaHeader = metaHeaders[i];
1251
1252 std::string userAgent;
1253 attributeValue(metaHeader, "user-agent", userAgent);
1254
1255 std::vector<xml_node<> *> metas = childElements(metaHeader, "meta");
1256 for (unsigned j = 0; j < metas.size(); ++j) {
1257 xml_node<> *meta = metas[j];
1258
1259 std::string name, property, httpEquiv, content;
1260 attributeValue(meta, "name", name);
1261 attributeValue(meta, "http-equiv", httpEquiv);
1262 attributeValue(meta, "property", property);
1263 attributeValue(meta, "content", content);
1264
1265 MetaHeaderType type;
1266 if (!name.empty())
1267 type = MetaHeaderType::Meta;
1268 else if (!httpEquiv.empty()) {
1269 type = MetaHeaderType::HttpHeader;
1270 name = httpEquiv;
1271 } else if (!property.empty()) {
1272 type = MetaHeaderType::Property;
1273 name = property;
1274 } else {
1275 throw WServer::Exception
1276 ("<meta> requires attribute 'name', 'property' or 'http-equiv'");
1277 }
1278
1279 metaHeaders_.push_back(MetaHeader(type, name, content, "", userAgent));
1280 }
1281 }
1282
1283 std::vector<xml_node<> *> headMatters = childElements(app, "head-matter");
1284 for (unsigned i = 0; i < headMatters.size(); ++i) {
1285 xml_node<> *headMatter = headMatters[i];
1286
1287 std::string userAgent;
1288 attributeValue(headMatter, "user-agent", userAgent);
1289
1290 std::stringstream ss;
1291 for (xml_node<> *r = headMatter->first_node(); r;
1292 r = r->next_sibling()) {
1293 rapidxml::print(static_cast<std::ostream&>(ss), *r);
1294 }
1295 headMatter_.push_back(HeadMatter(ss.str(), userAgent));
1296 }
1297
1298 std::string allowedOrigins
1299 = singleChildElementValue(app, "allowed-origins", "");
1300 boost::split(allowedOrigins_, allowedOrigins, boost::is_any_of(","));
1301 for (std::size_t i = 0; i < allowedOrigins_.size(); ++i)
1302 boost::trim(allowedOrigins_[i]);
1303 }
1304
rereadConfiguration()1305 void Configuration::rereadConfiguration()
1306 {
1307 WRITE_LOCK;
1308
1309 try {
1310 LOG_INFO("Rereading configuration...");
1311 Configuration conf(applicationPath_, appRoot_, configurationFile_, nullptr);
1312 reset();
1313 readConfiguration(true);
1314 LOG_INFO("New configuration read.");
1315 } catch (WException& e) {
1316 LOG_ERROR("Error reading configuration: " << e.what());
1317 }
1318 }
1319
readConfiguration(bool silent)1320 void Configuration::readConfiguration(bool silent)
1321 {
1322 std::ifstream s(configurationFile_.c_str(), std::ios::in | std::ios::binary);
1323
1324 if (!s) {
1325 if (configurationFile_ != WT_CONFIG_XML)
1326 throw WServer::Exception
1327 ("Error reading '" + configurationFile_ + "': could not open file.");
1328 else
1329 return;
1330 }
1331
1332 s.seekg(0, std::ios::end);
1333 int length = s.tellg();
1334 s.seekg(0, std::ios::beg);
1335
1336 std::unique_ptr<char[]> text(new char[length + 1]);
1337 s.read(text.get(), length);
1338 s.close();
1339 text[length] = 0;
1340
1341 try {
1342 xml_document<> doc;
1343 doc.parse<parse_normalize_whitespace
1344 | parse_trim_whitespace
1345 | parse_validate_closing_tags>(text.get());
1346
1347 xml_node<> *root = doc.first_node();
1348
1349 if (!root)
1350 throw WServer::Exception("<server> expected.");
1351
1352 std::vector<xml_node<> *> applications
1353 = childElements(root, "application-settings");
1354
1355 /*
1356 * Scan the config file first to determine the logFile, in order
1357 * to setup logging before parsing the other settings.
1358 */
1359 std::string logFile;
1360 std::string logConfig;
1361 for (unsigned i = 0; i < applications.size(); ++i) {
1362 xml_node<> *app = applications[i];
1363
1364 std::string appLocation;
1365 if (!attributeValue(app, "location", appLocation))
1366 throw WServer::Exception("<application-settings> requires attribute "
1367 "'location'");
1368
1369 if (appLocation == "*" || appLocation == applicationPath_) {
1370 logFile = singleChildElementValue(app, "log-file", logFile);
1371 logConfig = singleChildElementValue(app, "log-config", logConfig);
1372 }
1373 }
1374
1375 if (server_)
1376 server_->initLogger(logFile, logConfig);
1377
1378 if (!silent)
1379 LOG_INFO("reading Wt config file: " << configurationFile_
1380 << " (location = '" << applicationPath_ << "')");
1381
1382 /*
1383 * Now read application settings.
1384 */
1385 for (unsigned i = 0; i < applications.size(); ++i) {
1386 xml_node<> *app = applications[i];
1387
1388 std::string appLocation;
1389 attributeValue(app, "location", appLocation);
1390
1391 if (appLocation == "*" || appLocation == applicationPath_)
1392 readApplicationSettings(app);
1393 }
1394 } catch (std::exception& e) {
1395 throw WServer::Exception("Error reading: " + configurationFile_ + ": "
1396 + e.what());
1397 } catch (...) {
1398 throw WServer::Exception("Exception of unknown type!\n");
1399 }
1400 }
1401
registerSessionId(const std::string & oldId,const std::string & newId)1402 bool Configuration::registerSessionId(const std::string& oldId,
1403 const std::string& newId)
1404 {
1405 if (!runDirectory_.empty()) {
1406
1407 if (!newId.empty()) {
1408 std::string socketPath = sessionSocketPath(newId);
1409
1410 struct stat finfo;
1411 if (stat(socketPath.c_str(), &finfo) != -1)
1412 return false;
1413
1414 if (oldId.empty()) {
1415 if (sessionPolicy_ == SharedProcess) {
1416 std::ofstream f(socketPath.c_str());
1417 f << getpid() << std::endl;
1418 f.flush();
1419 }
1420 }
1421 }
1422
1423 if (!oldId.empty()) {
1424 if (newId.empty())
1425 unlink(sessionSocketPath(oldId).c_str());
1426 else
1427 std::rename(sessionSocketPath(oldId).c_str(),
1428 sessionSocketPath(newId).c_str());
1429 }
1430 }
1431
1432 return true;
1433 }
1434
generateSessionId()1435 std::string Configuration::generateSessionId()
1436 {
1437 std::string sessionId = sessionIdPrefix();
1438 sessionId += WRandom::generateId(sessionIdLength());
1439 return sessionId;
1440 }
1441
sessionSocketPath(const std::string & sessionId)1442 std::string Configuration::sessionSocketPath(const std::string& sessionId)
1443 {
1444 return runDirectory_ + "/" + sessionId;
1445 }
1446
readConfigurationProperty(const std::string & name,std::string & value)1447 bool Configuration::readConfigurationProperty(const std::string& name,
1448 std::string& value) const
1449 {
1450 PropertyMap::const_iterator i = properties_.find(name);
1451
1452 if (i != properties_.end()) {
1453 value = i->second;
1454 return true;
1455 } else
1456 return false;
1457 }
1458
log(const std::string & type)1459 WLogEntry Configuration::log(const std::string& type) const
1460 {
1461 if (server_)
1462 return server_->log(type);
1463 else
1464 return Wt::log(type);
1465 }
1466
1467 }
1468