-/*
- * Copyright 2001-2010 Internet2
+/**
+ * Licensed to the University Corporation for Advanced Internet
+ * Development, Inc. (UCAID) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * UCAID licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the
+ * License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
*/
/** XMLRequestMapper.cpp
#include "AccessControl.h"
#include "RequestMapper.h"
#include "SPRequest.h"
+#include "util/CGIParser.h"
#include "util/DOMPropertySet.h"
#include "util/SPConstants.h"
#include <algorithm>
+#include <boost/shared_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/tokenizer.hpp>
+#include <boost/tuple/tuple.hpp>
+#include <boost/algorithm/string.hpp>
#include <xmltooling/util/NDC.h>
#include <xmltooling/util/ReloadableXMLFile.h>
+#include <xmltooling/util/Threads.h>
#include <xmltooling/util/XMLHelper.h>
#include <xercesc/util/XMLUniDefs.hpp>
#include <xercesc/util/regx/RegularExpression.hpp>
+using shibspconstants::SHIB2SPCONFIG_NS;
using namespace shibsp;
using namespace xmltooling;
+using namespace boost;
using namespace std;
namespace shibsp {
class Override : public DOMPropertySet, public DOMNodeFilter
{
public:
- Override() : m_acl(NULL) {}
- Override(const DOMElement* e, Category& log, const Override* base=NULL);
- ~Override();
+ Override(bool unicodeAware=false) : m_unicodeAware(unicodeAware) {}
+ Override(bool unicodeAware, const DOMElement* e, Category& log, const Override* base=nullptr);
+ ~Override() {}
// Provides filter to exclude special config elements.
#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE
}
const Override* locate(const HTTPRequest& request) const;
- AccessControl* getAC() const { return (m_acl ? m_acl : (getParent() ? dynamic_cast<const Override*>(getParent())->getAC() : NULL)); }
+ AccessControl* getAC() const { return (m_acl ? m_acl.get() : (getParent() ? dynamic_cast<const Override*>(getParent())->getAC() : nullptr)); }
protected:
void loadACL(const DOMElement* e, Category& log);
- map<string,Override*> m_map;
- vector< pair<RegularExpression*,Override*> > m_regexps;
- vector< pair< pair<string,RegularExpression*>,Override*> > m_queries;
+ bool m_unicodeAware;
+ map< string,boost::shared_ptr<Override> > m_map;
+ vector< pair< boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > > m_regexps;
+ vector< boost::tuple< string,boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > > m_queries;
private:
- AccessControl* m_acl;
+ scoped_ptr<AccessControl> m_acl;
};
class XMLRequestMapperImpl : public Override
const Override* findOverride(const char* vhost, const HTTPRequest& request) const;
private:
- map<string,Override*> m_extras;
DOMDocument* m_document;
};
class XMLRequestMapper : public RequestMapper, public ReloadableXMLFile
{
public:
- XMLRequestMapper(const DOMElement* e) : ReloadableXMLFile(e,Category::getInstance(SHIBSP_LOGCAT".RequestMapper")), m_impl(NULL) {
- load();
+ XMLRequestMapper(const DOMElement* e) : ReloadableXMLFile(e,Category::getInstance(SHIBSP_LOGCAT ".RequestMapper")) {
+ background_load();
}
~XMLRequestMapper() {
- delete m_impl;
+ shutdown();
}
Settings getSettings(const HTTPRequest& request) const;
protected:
- pair<bool,DOMElement*> load();
+ pair<bool,DOMElement*> background_load();
private:
- XMLRequestMapperImpl* m_impl;
+ scoped_ptr<XMLRequestMapperImpl> m_impl;
};
#if defined (_MSC_VER)
void Override::loadACL(const DOMElement* e, Category& log)
{
try {
- const DOMElement* acl=XMLHelper::getFirstChildElement(e,htaccess);
+ const DOMElement* acl = XMLHelper::getFirstChildElement(e,htaccess);
if (acl) {
log.info("building Apache htaccess AccessControl provider...");
- m_acl=SPConfig::getConfig().AccessControlManager.newPlugin(HT_ACCESS_CONTROL,acl);
+ m_acl.reset(SPConfig::getConfig().AccessControlManager.newPlugin(HT_ACCESS_CONTROL,acl));
}
else {
- acl=XMLHelper::getFirstChildElement(e,_AccessControl);
+ acl = XMLHelper::getFirstChildElement(e,_AccessControl);
if (acl) {
log.info("building XML-based AccessControl provider...");
- m_acl=SPConfig::getConfig().AccessControlManager.newPlugin(XML_ACCESS_CONTROL,acl);
+ m_acl.reset(SPConfig::getConfig().AccessControlManager.newPlugin(XML_ACCESS_CONTROL,acl));
}
else {
- acl=XMLHelper::getFirstChildElement(e,AccessControlProvider);
+ acl = XMLHelper::getFirstChildElement(e,AccessControlProvider);
if (acl) {
- auto_ptr_char type(acl->getAttributeNS(NULL,_type));
- log.info("building AccessControl provider of type %s...",type.get());
- m_acl=SPConfig::getConfig().AccessControlManager.newPlugin(type.get(),acl);
+ string t(XMLHelper::getAttrString(acl, nullptr, _type));
+ if (!t.empty()) {
+ log.info("building AccessControl provider of type %s...", t.c_str());
+ m_acl.reset(SPConfig::getConfig().AccessControlManager.newPlugin(t.c_str(), acl));
+ }
+ else {
+ throw ConfigurationException("<AccessControlProvider> missing type attribute.");
+ }
}
}
}
}
- catch (exception& ex) {
+ catch (std::exception& ex) {
log.crit("exception building AccessControl provider: %s", ex.what());
- m_acl = new AccessControlDummy();
+ m_acl.reset(new AccessControlDummy());
}
}
-Override::Override(const DOMElement* e, Category& log, const Override* base) : m_acl(NULL)
+Override::Override(bool unicodeAware, const DOMElement* e, Category& log, const Override* base)
+ : m_unicodeAware(unicodeAware)
{
- try {
- // Load the property set.
- load(e,NULL,this);
- setParent(base);
+ // Load the property set.
+ load(e, nullptr, this);
+ setParent(base);
- // Load any AccessControl provider.
- loadACL(e,log);
+ // Load any AccessControl provider.
+ loadACL(e, log);
- // Handle nested Paths.
- DOMElement* path = XMLHelper::getFirstChildElement(e,Path);
- for (int i=1; path; ++i, path=XMLHelper::getNextSiblingElement(path,Path)) {
- const XMLCh* n=path->getAttributeNS(NULL,name);
+ // Handle nested Paths.
+ DOMElement* path = XMLHelper::getFirstChildElement(e, Path);
+ for (int i = 1; path; ++i, path = XMLHelper::getNextSiblingElement(path, Path)) {
+ const XMLCh* n = path->getAttributeNS(nullptr,name);
- // Skip any leading slashes.
- while (n && *n==chForwardSlash)
- n++;
+ // Skip any leading slashes.
+ while (n && *n == chForwardSlash)
+ n++;
- // Check for empty name.
- if (!n || !*n) {
- log.warn("skipping Path element (%d) with empty name attribute", i);
- continue;
- }
+ // Check for empty name.
+ if (!n || !*n) {
+ log.warn("skipping Path element (%d) with empty name attribute", i);
+ continue;
+ }
- // Check for an embedded slash.
- int slash=XMLString::indexOf(n,chForwardSlash);
- if (slash>0) {
- // Copy the first path segment.
- XMLCh* namebuf=new XMLCh[slash + 1];
- for (int pos=0; pos < slash; pos++)
- namebuf[pos]=n[pos];
- namebuf[slash]=chNull;
-
- // Move past the slash in the original pathname.
- n=n+slash+1;
-
- // Skip any leading slashes again.
- while (*n==chForwardSlash)
- n++;
-
- if (*n) {
- // Create a placeholder Path element for the first path segment and replant under it.
- DOMElement* newpath=path->getOwnerDocument()->createElementNS(shibspconstants::SHIB2SPCONFIG_NS,Path);
- newpath->setAttributeNS(NULL,name,namebuf);
- path->setAttributeNS(NULL,name,n);
- path->getParentNode()->replaceChild(newpath,path);
- newpath->appendChild(path);
-
- // Repoint our locals at the new parent.
- path=newpath;
- n=path->getAttributeNS(NULL,name);
- }
- else {
- // All we had was a pathname with trailing slash(es), so just reset it without them.
- path->setAttributeNS(NULL,name,namebuf);
- n=path->getAttributeNS(NULL,name);
- }
- delete[] namebuf;
+ // Check for an embedded slash.
+ int slash = XMLString::indexOf(n, chForwardSlash);
+ if (slash > 0) {
+ // Copy the first path segment.
+ xstring namebuf;
+ for (int pos = 0; pos < slash; ++pos)
+ namebuf += n[pos];
+
+ // Move past the slash in the original pathname.
+ n = n + slash + 1;
+
+ // Skip any leading slashes again.
+ while (*n == chForwardSlash)
+ ++n;
+
+ if (*n) {
+ // Create a placeholder Path element for the first path segment and replant under it.
+ DOMElement* newpath = path->getOwnerDocument()->createElementNS(shibspconstants::SHIB2SPCONFIG_NS, Path);
+ newpath->setAttributeNS(nullptr, name, namebuf.c_str());
+ path->setAttributeNS(nullptr, name, n);
+ path->getParentNode()->replaceChild(newpath, path);
+ newpath->appendChild(path);
+
+ // Repoint our locals at the new parent.
+ path = newpath;
+ n = path->getAttributeNS(nullptr, name);
+ }
+ else {
+ // All we had was a pathname with trailing slash(es), so just reset it without them.
+ path->setAttributeNS(nullptr, name, namebuf.c_str());
+ n = path->getAttributeNS(nullptr, name);
}
+ }
- Override* o=new Override(path,log,this);
- pair<bool,const char*> name=o->getString("name");
- char* dup=strdup(name.second);
- for (char* pch=dup; *pch; pch++)
- *pch=tolower(*pch);
+ char* dup = nullptr;
+ try {
+ boost::shared_ptr<Override> o(new Override(m_unicodeAware, path, log, this));
+ if (m_unicodeAware) {
+ dup = toUTF8(o->getXMLString("name").second, true /* use malloc */);
+ }
+ else {
+ dup = strdup(o->getString("name").second);
+ for (char* pch = dup; *pch; ++pch)
+ *pch = tolower(*pch);
+ }
if (m_map.count(dup)) {
- log.warn("skipping duplicate Path element (%s)",dup);
- free(dup);
- delete o;
- continue;
+ log.warn("skipping duplicate Path element (%s)", dup);
+ }
+ else {
+ m_map[dup] = o;
+ log.debug("added Path mapping (%s)", dup);
}
- m_map[dup]=o;
- log.debug("added Path mapping (%s)", dup);
free(dup);
}
-
- if (!XMLString::equals(e->getLocalName(), PathRegex)) {
- // Handle nested PathRegexs.
- path = XMLHelper::getFirstChildElement(e,PathRegex);
- for (int i=1; path; ++i, path=XMLHelper::getNextSiblingElement(path,PathRegex)) {
- const XMLCh* n=path->getAttributeNS(NULL,regex);
- if (!n || !*n) {
- log.warn("skipping PathRegex element (%d) with empty regex attribute",i);
- continue;
- }
-
- auto_ptr<Override> o(new Override(path,log,this));
-
- const XMLCh* flag=path->getAttributeNS(NULL,ignoreCase);
- try {
- auto_ptr<RegularExpression> re(
- new RegularExpression(n, (flag && (*flag==chLatin_f || *flag==chDigit_0)) ? &chNull : ignoreOption)
- );
- m_regexps.push_back(make_pair(re.release(), o.release()));
- }
- catch (XMLException& ex) {
- auto_ptr_char tmp(ex.getMessage());
- log.error("caught exception while parsing PathRegex regular expression (%d): %s", i, tmp.get());
- throw ConfigurationException("Invalid regular expression in PathRegex element.");
- }
-
- if (log.isDebugEnabled())
- log.debug("added <PathRegex> mapping (%s)", m_regexps.back().second->getString("regex").second);
- }
+ catch (std::exception&) {
+ free(dup);
+ throw;
}
+ }
- // Handle nested Querys.
- path = XMLHelper::getFirstChildElement(e,Query);
- for (int i=1; path; ++i, path=XMLHelper::getNextSiblingElement(path,Query)) {
- const XMLCh* n=path->getAttributeNS(NULL,name);
+ if (!XMLString::equals(e->getLocalName(), PathRegex)) {
+ // Handle nested PathRegexs.
+ path = XMLHelper::getFirstChildElement(e, PathRegex);
+ for (int i = 1; path; ++i, path = XMLHelper::getNextSiblingElement(path, PathRegex)) {
+ const XMLCh* n = path->getAttributeNS(nullptr, regex);
if (!n || !*n) {
- log.warn("skipping Query element (%d) with empty name attribute",i);
+ log.warn("skipping PathRegex element (%d) with empty regex attribute",i);
continue;
}
- auto_ptr_char ntemp(n);
- const XMLCh* v=path->getAttributeNS(NULL,regex);
- auto_ptr<Override> o(new Override(path,log,this));
+ boost::shared_ptr<Override> o(new Override(m_unicodeAware, path, log, this));
+
+ bool flag = XMLHelper::getAttrBool(path, true, ignoreCase);
try {
- RegularExpression* re = NULL;
- if (v && *v)
- re = new RegularExpression(v);
- m_queries.push_back(make_pair(make_pair(string(ntemp.get()),re), o.release()));
+ boost::shared_ptr<RegularExpression> re(new RegularExpression(n, flag ? &chNull : ignoreOption));
+ m_regexps.push_back(make_pair(re, o));
}
catch (XMLException& ex) {
auto_ptr_char tmp(ex.getMessage());
- log.error("caught exception while parsing Query regular expression (%d): %s", i, tmp.get());
- throw ConfigurationException("Invalid regular expression in Query element.");
+ log.error("caught exception while parsing PathRegex regular expression (%d): %s", i, tmp.get());
+ throw ConfigurationException("Invalid regular expression in PathRegex element.");
}
- log.debug("added <Query> mapping (%s)", ntemp.get());
+ if (log.isDebugEnabled())
+ log.debug("added <PathRegex> mapping (%s)", o->getString("regex").second);
}
}
- catch (exception&) {
- delete m_acl;
- for_each(m_map.begin(),m_map.end(),xmltooling::cleanup_pair<string,Override>());
- for (vector< pair<RegularExpression*,Override*> >::iterator i = m_regexps.begin(); i != m_regexps.end(); ++i) {
- delete i->first;
- delete i->second;
+
+ // Handle nested Querys.
+ path = XMLHelper::getFirstChildElement(e, Query);
+ for (int i = 1; path; ++i, path = XMLHelper::getNextSiblingElement(path, Query)) {
+ const XMLCh* n = path->getAttributeNS(nullptr, name);
+ if (!n || !*n) {
+ log.warn("skipping Query element (%d) with empty name attribute",i);
+ continue;
+ }
+ auto_ptr_char ntemp(n);
+ const XMLCh* v = path->getAttributeNS(nullptr, regex);
+
+ try {
+ boost::shared_ptr<Override> o(new Override(m_unicodeAware, path, log, this));
+ boost::shared_ptr<RegularExpression> re((v && *v) ? new RegularExpression(v) : nullptr);
+ m_queries.push_back(boost::make_tuple(string(ntemp.get()), re, o));
}
- for (vector< pair< pair<string,RegularExpression*>,Override*> >::iterator j = m_queries.begin(); j != m_queries.end(); ++j) {
- delete j->first.second;
- delete j->second;
+ catch (XMLException& ex) {
+ auto_ptr_char tmp(ex.getMessage());
+ log.error("caught exception while parsing Query regular expression (%d): %s", i, tmp.get());
+ throw ConfigurationException("Invalid regular expression in Query element.");
}
- throw;
- }
-}
-Override::~Override()
-{
- delete m_acl;
- for_each(m_map.begin(),m_map.end(),xmltooling::cleanup_pair<string,Override>());
- for (vector< pair<RegularExpression*,Override*> >::iterator i = m_regexps.begin(); i != m_regexps.end(); ++i) {
- delete i->first;
- delete i->second;
- }
- for (vector< pair< pair<string,RegularExpression*>,Override*> >::iterator j = m_queries.begin(); j != m_queries.end(); ++j) {
- delete j->first.second;
- delete j->second;
+ log.debug("added <Query> mapping (%s)", ntemp.get());
}
}
if (*path == '/')
path++;
- // Now we copy the path, chop the query string, and lower case it.
- char* dup=strdup(path);
- char* sep=strchr(dup,'?');
- if (sep)
- *sep=0;
- for (char* pch=dup; *pch; pch++)
- *pch=tolower(*pch);
+ // Now we copy the path, chop the query string, and possibly lower case it.
+ string dup(path);
+ string::size_type sep = dup.find('?');
+ if (sep != string::npos)
+ dup = dup.substr(0, sep);
+ if (!m_unicodeAware) {
+ to_lower(dup);
+ }
// Default is for the current object to provide settings.
- const Override* o=this;
+ const Override* o = this;
// Tokenize the path by segment and try and map each segment.
-#ifdef HAVE_STRTOK_R
- char* pos=NULL;
- const char* token=strtok_r(dup,"/",&pos);
-#else
- const char* token=strtok(dup,"/");
-#endif
- while (token) {
- map<string,Override*>::const_iterator i=o->m_map.find(token);
- if (i==o->m_map.end())
+ tokenizer< char_separator<char> > tokens(dup, char_separator<char>("/"));
+ for (tokenizer< char_separator<char> >::iterator token = tokens.begin(); token != tokens.end(); ++token) {
+ map< string,boost::shared_ptr<Override> >::const_iterator i = o->m_map.find(*token);
+ if (i == o->m_map.end())
break; // Once there's no match, we've consumed as much of the path as possible here.
// We found a match, so reset the settings pointer.
- o=i->second;
+ o = i->second.get();
// We descended a step down the path, so we need to advance the original
// parameter for the regex step later.
- path += strlen(token);
+ path += token->length();
if (*path == '/')
path++;
-
- // Get the next segment, if any.
-#ifdef HAVE_STRTOK_R
- token=strtok_r(NULL,"/",&pos);
-#else
- token=strtok(NULL,"/");
-#endif
}
- free(dup);
-
// If there's anything left, we try for a regex match on the rest of the path minus the query string.
if (*path) {
string path2(path);
- path2 = path2.substr(0,path2.find('?'));
+ sep = path2.find('?');
+ if (sep != string::npos)
+ path2 = path2.substr(0, sep);
- for (vector< pair<RegularExpression*,Override*> >::const_iterator re = o->m_regexps.begin(); re != o->m_regexps.end(); ++re) {
+ for (vector< pair< boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > >::const_iterator re = o->m_regexps.begin(); re != o->m_regexps.end(); ++re) {
if (re->first->matches(path2.c_str())) {
- o = re->second;
+ o = re->second.get();
break;
}
}
}
// Finally, check for query string matches. This is another "unrolled" recursive descent in a loop.
- bool descended;
- do {
- descended = false;
- for (vector< pair< pair<string,RegularExpression*>,Override*> >::const_iterator q = o->m_queries.begin(); !descended && q != o->m_queries.end(); ++q) {
- vector<const char*> vals;
- if (request.getParameters(q->first.first.c_str(), vals)) {
- if (q->first.second) {
- // We have to match one of the values.
- for (vector<const char*>::const_iterator v = vals.begin(); v != vals.end(); ++v) {
- if (q->first.second->matches(*v)) {
- o = q->second;
- descended = true;
- break;
+ // To avoid consuming any POST data, we use a dedicated CGIParser.
+ if (!o->m_queries.empty()) {
+ bool descended;
+ CGIParser cgi(request, true);
+ do {
+ descended = false;
+ for (vector< boost::tuple< string,boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > >::const_iterator q = o->m_queries.begin(); !descended && q != o->m_queries.end(); ++q) {
+ pair<CGIParser::walker,CGIParser::walker> vals = cgi.getParameters(q->get<0>().c_str());
+ if (vals.first != vals.second) {
+ if (q->get<1>()) {
+ // We have to match one of the values.
+ while (vals.first != vals.second) {
+ if (q->get<1>()->matches(vals.first->second)) {
+ o = q->get<2>().get();
+ descended = true;
+ break;
+ }
+ ++vals.first;
}
}
- }
- else {
- // The simple presence of the parameter is sufficient to match.
- o = q->second;
- descended = true;
+ else {
+ // The simple presence of the parameter is sufficient to match.
+ o = q->get<2>().get();
+ descended = true;
+ }
}
}
- }
- } while (descended);
+ } while (descended);
+ }
return o;
}
-XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) : m_document(NULL)
+XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) : m_document(nullptr)
{
#ifdef _DEBUG
xmltooling::NDC ndc("XMLRequestMapperImpl");
#endif
+ static const XMLCh _RequestMap[] = UNICODE_LITERAL_10(R,e,q,u,e,s,t,M,a,p);
+
+ if (e && !XMLHelper::isNodeNamed(e, SHIB2SPCONFIG_NS, _RequestMap))
+ throw ConfigurationException("XML RequestMapper requires conf:RequestMap at root of configuration.");
// Load the property set.
- load(e,NULL,this);
+ load(e, nullptr, this);
+
+ // Inject "default" app ID if not explicit.
+ if (!getString("applicationId").first)
+ setProperty("applicationId", "default");
// Load any AccessControl provider.
- loadACL(e,log);
+ loadACL(e, log);
+
+ pair<bool,bool> unicodeAware = getBool("unicodeAware");
+ m_unicodeAware = (unicodeAware.first && unicodeAware.second);
// Loop over the HostRegex elements.
- const DOMElement* host = XMLHelper::getFirstChildElement(e,HostRegex);
- for (int i=1; host; ++i, host=XMLHelper::getNextSiblingElement(host,HostRegex)) {
- const XMLCh* n=host->getAttributeNS(NULL,regex);
+ const DOMElement* host = XMLHelper::getFirstChildElement(e, HostRegex);
+ for (int i = 1; host; ++i, host = XMLHelper::getNextSiblingElement(host, HostRegex)) {
+ const XMLCh* n = host->getAttributeNS(nullptr,regex);
if (!n || !*n) {
- log.warn("Skipping HostRegex element (%d) with empty regex attribute",i);
+ log.warn("Skipping HostRegex element (%d) with empty regex attribute", i);
continue;
}
- auto_ptr<Override> o(new Override(host,log,this));
+ boost::shared_ptr<Override> o(new Override(m_unicodeAware, host, log, this));
- const XMLCh* flag=host->getAttributeNS(NULL,ignoreCase);
+ const XMLCh* flag = host->getAttributeNS(nullptr,ignoreCase);
try {
- auto_ptr<RegularExpression> re(
+ boost::shared_ptr<RegularExpression> re(
new RegularExpression(n, (flag && (*flag==chLatin_f || *flag==chDigit_0)) ? &chNull : ignoreOption)
);
- m_regexps.push_back(make_pair(re.release(), o.release()));
+ m_regexps.push_back(make_pair(re, o));
}
catch (XMLException& ex) {
auto_ptr_char tmp(ex.getMessage());
}
// Loop over the Host elements.
- host = XMLHelper::getFirstChildElement(e,Host);
- for (int i=1; host; ++i, host=XMLHelper::getNextSiblingElement(host,Host)) {
- const XMLCh* n=host->getAttributeNS(NULL,name);
+ host = XMLHelper::getFirstChildElement(e, Host);
+ for (int i = 1; host; ++i, host = XMLHelper::getNextSiblingElement(host, Host)) {
+ const XMLCh* n=host->getAttributeNS(nullptr,name);
if (!n || !*n) {
- log.warn("Skipping Host element (%d) with empty name attribute",i);
+ log.warn("Skipping Host element (%d) with empty name attribute", i);
continue;
}
- Override* o=new Override(host,log,this);
+ boost::shared_ptr<Override> o(new Override(m_unicodeAware, host, log, this));
pair<bool,const char*> name=o->getString("name");
pair<bool,const char*> scheme=o->getString("scheme");
pair<bool,const char*> port=o->getString("port");
- char* dup=strdup(name.second);
- for (char* pch=dup; *pch; pch++)
- *pch=tolower(*pch);
- auto_ptr<char> dupwrap(dup);
+ string dup(name.first ? name.second : "");
+ to_lower(dup);
if (!scheme.first && port.first) {
// No scheme, but a port, so assume http.
(!strcmp(scheme.second,"ldap") && !strcmp(port.second,"389")) ||
(!strcmp(scheme.second,"ldaps") && !strcmp(port.second,"636"))) {
// First store a port-less version.
- if (m_map.count(url) || m_extras.count(url)) {
+ if (m_map.count(url)) {
log.warn("Skipping duplicate Host element (%s)",url.c_str());
- delete o;
continue;
}
- m_map[url]=o;
+ m_map[url] = o;
log.debug("Added <Host> mapping for %s",url.c_str());
- // Now append the port. We use the extras vector, to avoid double freeing the object later.
+ // Now append the port. The shared_ptr should refcount the Override to avoid double deletes.
url=url + ':' + port.second;
- m_extras[url]=o;
+ m_map[url] = o;
log.debug("Added <Host> mapping for %s",url.c_str());
}
else {
url=url + ':' + port.second;
- if (m_map.count(url) || m_extras.count(url)) {
+ if (m_map.count(url)) {
log.warn("Skipping duplicate Host element (%s)",url.c_str());
- delete o;
continue;
}
- m_map[url]=o;
+ m_map[url] = o;
log.debug("Added <Host> mapping for %s",url.c_str());
}
}
else {
// No scheme or port, so we enter dual hosts on http:80 and https:443
string url("http://");
- url = url + dup;
- if (m_map.count(url) || m_extras.count(url)) {
+ url += dup;
+ if (m_map.count(url)) {
log.warn("Skipping duplicate Host element (%s)",url.c_str());
- delete o;
continue;
}
- m_map[url]=o;
+ m_map[url] = o;
log.debug("Added <Host> mapping for %s",url.c_str());
- url = url + ":80";
- if (m_map.count(url) || m_extras.count(url)) {
+ url += ":80";
+ if (m_map.count(url)) {
log.warn("Skipping duplicate Host element (%s)",url.c_str());
continue;
}
- m_extras[url]=o;
+ m_map[url] = o;
log.debug("Added <Host> mapping for %s",url.c_str());
- url = "https://";
- url = url + dup;
- if (m_map.count(url) || m_extras.count(url)) {
+ url = "https://" + dup;
+ if (m_map.count(url)) {
log.warn("Skipping duplicate Host element (%s)",url.c_str());
continue;
}
- m_extras[url]=o;
+ m_map[url] = o;
log.debug("Added <Host> mapping for %s",url.c_str());
- url = url + ":443";
- if (m_map.count(url) || m_extras.count(url)) {
+ url += ":443";
+ if (m_map.count(url)) {
log.warn("Skipping duplicate Host element (%s)",url.c_str());
continue;
}
- m_extras[url]=o;
+ m_map[url] = o;
log.debug("Added <Host> mapping for %s",url.c_str());
}
}
const Override* XMLRequestMapperImpl::findOverride(const char* vhost, const HTTPRequest& request) const
{
- const Override* o=NULL;
- map<string,Override*>::const_iterator i=m_map.find(vhost);
- if (i!=m_map.end())
- o=i->second;
+ const Override* o = nullptr;
+ map< string,boost::shared_ptr<Override> >::const_iterator i = m_map.find(vhost);
+ if (i != m_map.end())
+ o = i->second.get();
else {
- i=m_extras.find(vhost);
- if (i!=m_extras.end())
- o=i->second;
- else {
- for (vector< pair<RegularExpression*,Override*> >::const_iterator re = m_regexps.begin(); !o && re != m_regexps.end(); ++re) {
- if (re->first->matches(vhost))
- o=re->second;
- }
+ for (vector< pair< boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > >::const_iterator re = m_regexps.begin(); !o && re != m_regexps.end(); ++re) {
+ if (re->first->matches(vhost))
+ o=re->second.get();
}
}
return o ? o->locate(request) : this;
}
-pair<bool,DOMElement*> XMLRequestMapper::load()
+pair<bool,DOMElement*> XMLRequestMapper::background_load()
{
// Load from source using base class.
pair<bool,DOMElement*> raw = ReloadableXMLFile::load();
// If we own it, wrap it.
- XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : NULL);
+ XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : nullptr);
- XMLRequestMapperImpl* impl = new XMLRequestMapperImpl(raw.second,m_log);
+ scoped_ptr<XMLRequestMapperImpl> impl(new XMLRequestMapperImpl(raw.second, m_log));
// If we held the document, transfer it to the impl. If we didn't, it's a no-op.
impl->setDocument(docjanitor.release());
- delete m_impl;
- m_impl = impl;
+ // Perform the swap inside a lock.
+ if (m_lock)
+ m_lock->wrlock();
+ SharedLock locker(m_lock, false);
+ m_impl.swap(impl);
- return make_pair(false,(DOMElement*)NULL);
+ return make_pair(false,(DOMElement*)nullptr);
}
RequestMapper::Settings XMLRequestMapper::getSettings(const HTTPRequest& request) const
{
try {
- ostringstream vhost;
- vhost << request.getScheme() << "://" << request.getHostname() << ':' << request.getPort();
- const Override* o=m_impl->findOverride(vhost.str().c_str(), request);
- return Settings(o,o->getAC());
+ string normalizedhost(request.getHostname());
+ to_lower(normalizedhost);
+ string vhost = string(request.getScheme()) + "://" + normalizedhost + ':' + lexical_cast<string>(request.getPort());
+ const Override* o = m_impl->findOverride(vhost.c_str(), request);
+ return Settings(o, o->getAC());
}
catch (XMLException& ex) {
auto_ptr_char tmp(ex.getMessage());