-<?xml version="1.0" encoding="UTF-8"?><?fileVersion 4.0.0?><cproject>\r
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>\r
+<?fileVersion 4.0.0?>\r
+\r
+<cproject>\r
<storageModule moduleId="org.eclipse.cdt.core.settings">\r
<cconfiguration id="org.eclipse.linuxtools.cdt.autotools.configuration.build.1769801814">\r
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="org.eclipse.linuxtools.cdt.autotools.configuration.build.1769801814" moduleId="org.eclipse.cdt.core.settings" name="Build (GNU)">\r
<pathentry kind="out" path="debug"/>\r
<pathentry kind="con" path="org.eclipse.cdt.make.core.DISCOVERED_SCANNER_INFO"/>\r
<pathentry excluding="util/|io/|impl/|validation/|signature/|signature/impl/|encryption/|encryption/impl/|security/|security/impl/|soap/|soap/impl/" kind="src" path="xmltooling"/>\r
-<pathentry kind="src" path="xmltooling/impl"/>\r
-<pathentry kind="src" path="xmltooling/io"/>\r
-<pathentry excluding="impl/" kind="src" path="xmltooling/encryption"/>\r
-<pathentry kind="src" path="xmltooling/encryption/impl"/>\r
-<pathentry excluding="impl/" kind="src" path="xmltooling/signature"/>\r
-<pathentry kind="src" path="xmltooling/signature/impl"/>\r
-<pathentry excluding="impl/" kind="src" path="xmltooling/security"/>\r
-<pathentry kind="src" path="xmltooling/security/impl"/>\r
-<pathentry excluding="impl/" kind="src" path="xmltooling/soap"/>\r
-<pathentry kind="src" path="xmltooling/soap/impl"/>\r
-<pathentry kind="src" path="xmltooling/util"/>\r
-<pathentry kind="src" path="xmltooling/validation"/>\r
-<pathentry kind="src" path="xmltoolingtest"/>\r
</storageModule>\r
<storageModule moduleId="cdtBuildSystem" version="4.0.0">\r
<configuration artifactName="cpp-xmltooling" buildArtefactType="org.eclipse.linuxtools.cdt.autotools.buildArtefactType.autotools" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.linuxtools.cdt.autotools.buildArtefactType.autotools" cleanCommand="rm -rf" description="" errorParsers="org.eclipse.cdt.core.MakeErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GLDErrorParser;org.eclipse.cdt.core.GASErrorParser" id="org.eclipse.linuxtools.cdt.autotools.configuration.build.1769801814" name="Build (GNU)" parent="org.eclipse.linuxtools.cdt.autotools.configuration.build">\r
</folderInfo>\r
<sourceEntries>\r
<entry excluding="xmltooling|xmltoolingtest|debug/|release/|x64/" flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name=""/>\r
-<entry excluding="Debug/|Release/|x64/|xmltooling-lite-Debug/|xmltooling-lite-Release/" flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="xmltooling"/>\r
+<entry excluding="encryption|security|signature|soap|io|util|impl|validation|Debug/|Release/|x64/|xmltooling-lite-Debug/|xmltooling-lite-Release/" flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="xmltooling"/>\r
+<entry excluding="impl" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="xmltooling/encryption"/>\r
+<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="xmltooling/encryption/impl"/>\r
+<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="xmltooling/impl"/>\r
+<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="xmltooling/io"/>\r
+<entry excluding="impl" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="xmltooling/security"/>\r
+<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="xmltooling/security/impl"/>\r
+<entry excluding="impl" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="xmltooling/signature"/>\r
+<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="xmltooling/signature/impl"/>\r
+<entry excluding="impl" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="xmltooling/soap"/>\r
+<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="xmltooling/soap/impl"/>\r
+<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="xmltooling/validation"/>\r
+<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="xmltooling/util"/>\r
<entry excluding="Debug/|Release/|x64/" flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="xmltoolingtest"/>\r
</sourceEntries>\r
</configuration>\r
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:xt="http://www.opensaml.org/xmltooling"
targetNamespace="http://www.opensaml.org/xmltooling"
- elementFormDefault="qualified">
+ elementFormDefault="qualified"
+ version="1.4">
<element name="exception" type="xt:XMLToolingExceptionType"/>
<complexType name="XMLToolingExceptionType">
<attribute name="type" type="string" use="required"/>
</complexType>
+ <element name="URLInputSourceStatus" type="unsignedShort"/>
+
</schema>
enum status_t {
XMLTOOLING_HTTP_STATUS_OK = 200,
XMLTOOLING_HTTP_STATUS_MOVED = 302,
+ XMLTOOLING_HTTP_STATUS_NOTMODIFIED = 304,
XMLTOOLING_HTTP_STATUS_UNAUTHORIZED = 401,
XMLTOOLING_HTTP_STATUS_FORBIDDEN = 403,
XMLTOOLING_HTTP_STATUS_NOTFOUND = 404,
#include "internal.h"
#include <xmltooling/util/CurlURLInputStream.h>
+#include <xmltooling/util/ParserPool.h>
#include <xmltooling/util/XMLHelper.h>
#include <openssl/ssl.h>
using namespace xmltooling;
using namespace xercesc;
+using namespace std;
namespace {
static const XMLCh _CURL[] = UNICODE_LITERAL_4(C,U,R,L);
return CURLE_OK;
}
+
+ size_t curl_header_hook(void* ptr, size_t size, size_t nmemb, void* stream)
+ {
+ // only handle single-byte data
+ if (size!=1 || nmemb<5 || !stream)
+ return nmemb;
+ string* cacheTag = reinterpret_cast<string*>(stream);
+ const char* hdr = reinterpret_cast<char*>(ptr);
+ if (strncmp(hdr, "ETag:", 5) == 0) {
+ hdr += 5;
+ size_t remaining = nmemb - 5;
+ // skip leading spaces
+ while (remaining > 0) {
+ if (*hdr == ' ') {
+ ++hdr;
+ --remaining;
+ continue;
+ }
+ break;
+ }
+ // append until whitespace
+ while (remaining > 0) {
+ if (!isspace(*hdr)) {
+ (*cacheTag) += *hdr++;
+ --remaining;
+ continue;
+ }
+ break;
+ }
+ }
+
+ return nmemb;
+ }
}
-CurlURLInputStream::CurlURLInputStream(const char* url)
+CurlURLInputStream::CurlURLInputStream(const char* url, string* cacheTag)
: fLog(logging::Category::getInstance(XMLTOOLING_LOGCAT".libcurl.InputStream"))
- , fURL(url)
+ , fCacheTag(cacheTag)
+ , fURL(url ? url : "")
, fMulti(0)
, fEasy(0)
+ , fHeaders(0)
, fTotalBytesRead(0)
, fWritePtr(0)
, fBytesRead(0)
, fBufferHeadPtr(fBuffer)
, fBufferTailPtr(fBuffer)
, fContentType(0)
+ , fStatusCode(200)
{
+ if (fURL.empty())
+ throw IOException("No URL supplied to CurlURLInputStream constructor.");
init();
}
-CurlURLInputStream::CurlURLInputStream(const XMLCh* url)
+CurlURLInputStream::CurlURLInputStream(const XMLCh* url, string* cacheTag)
: fLog(logging::Category::getInstance(XMLTOOLING_LOGCAT".libcurl.InputStream"))
+ , fCacheTag(cacheTag)
, fMulti(0)
, fEasy(0)
+ , fHeaders(0)
, fTotalBytesRead(0)
, fWritePtr(0)
, fBytesRead(0)
, fBufferHeadPtr(fBuffer)
, fBufferTailPtr(fBuffer)
, fContentType(0)
+ , fStatusCode(200)
{
- auto_ptr_char temp(url);
- fURL = temp.get();
+ if (url) {
+ auto_ptr_char temp(url);
+ fURL = temp.get();
+ }
+ if (fURL.empty())
+ throw IOException("No URL supplied to CurlURLInputStream constructor.");
init();
}
-CurlURLInputStream::CurlURLInputStream(const DOMElement* e)
+CurlURLInputStream::CurlURLInputStream(const DOMElement* e, string* cacheTag)
: fLog(logging::Category::getInstance(XMLTOOLING_LOGCAT".libcurl.InputStream"))
+ , fCacheTag(cacheTag)
, fMulti(0)
, fEasy(0)
+ , fHeaders(0)
, fTotalBytesRead(0)
, fWritePtr(0)
, fBytesRead(0)
, fBufferHeadPtr(fBuffer)
, fBufferTailPtr(fBuffer)
, fContentType(0)
+ , fStatusCode(200)
{
const XMLCh* attr = e->getAttributeNS(NULL, url);
if (!attr || !*attr) {
curl_multi_cleanup(fMulti);
}
+ if (fHeaders) {
+ curl_slist_free_all(fHeaders);
+ }
+
XMLString::release(&fContentType);
}
fError[0] = 0;
curl_easy_setopt(fEasy, CURLOPT_ERRORBUFFER, fError);
+ // Check for cache tag.
+ if (fCacheTag) {
+ // Outgoing tag.
+ if (!fCacheTag->empty()) {
+ string hdr("If-None-Match: ");
+ hdr += *fCacheTag;
+ fHeaders = curl_slist_append(fHeaders, hdr.c_str());
+ curl_easy_setopt(fEasy, CURLOPT_HTTPHEADER, fHeaders);
+ }
+ // Incoming tag.
+ curl_easy_setopt(fEasy, CURLOPT_HEADERFUNCTION, curl_header_hook);
+ curl_easy_setopt(fEasy, CURLOPT_HEADERDATA, fCacheTag);
+ }
+
if (e) {
const XMLCh* flag = e->getAttributeNS(NULL, verifyHost);
if (flag && (*flag == chLatin_f || *flag == chDigit_0))
try {
readMore(&runningHandles);
}
- catch (XMLException& ex) {
+ catch (XMLException&) {
curl_multi_remove_handle(fMulti, fEasy);
curl_easy_cleanup(fEasy);
fEasy = NULL;
curl_multi_cleanup(fMulti);
fMulti = NULL;
- auto_ptr_char msg(ex.getMessage());
- throw IOException(msg.get());
+ throw;
}
if(runningHandles == 0) break;
}
+ // Check for a response code.
+ if (curl_easy_getinfo(fEasy, CURLINFO_RESPONSE_CODE, &fStatusCode) == CURLE_OK) {
+ if (fStatusCode >= 300 ) {
+ // Short-circuit usual processing by storing a special XML document in the buffer.
+ ostringstream specialdoc;
+ specialdoc << '<' << URLInputSource::asciiStatusCodeElementName << " xmlns=\"http://www.opensaml.org/xmltooling\">"
+ << fStatusCode
+ << "</" << URLInputSource::asciiStatusCodeElementName << '>';
+ string specialxml = specialdoc.str();
+ memcpy(fBuffer, specialxml.c_str(), specialxml.length());
+ fBufferHeadPtr += specialxml.length();
+ }
+ }
+ else {
+ fStatusCode = 200; // reset to 200 to ensure no special processing occurs
+ }
+
// Find the content type
char* contentType8 = NULL;
- curl_easy_getinfo(fEasy, CURLINFO_CONTENT_TYPE, &contentType8);
- if(contentType8)
+ if(curl_easy_getinfo(fEasy, CURLINFO_CONTENT_TYPE, &contentType8) == CURLE_OK && contentType8)
fContentType = XMLString::transcode(contentType8);
}
ThrowXML1(NetAccessorException, XMLExcepts::NetAcc_ConnSocket, fURL.c_str());
break;
+ case CURLE_OPERATION_TIMEDOUT:
+ ThrowXML1(NetAccessorException, XMLExcepts::NetAcc_ConnSocket, fURL.c_str());
+ break;
+
case CURLE_RECV_ERROR:
ThrowXML1(NetAccessorException, XMLExcepts::NetAcc_ReadSocket, fURL.c_str());
break;
continue;
}
+ // Check for a non-2xx status that means to ignore the curl response.
+ if (fStatusCode >= 300)
+ break;
+
// Ask the curl to do some work
int runningHandles = 0;
tryAgain = readMore(&runningHandles);
/**
* Constructor.
*
- * @param url the URL of the resource to fetch
+ * @param url the URL of the resource to fetch
+ * @param cacheTag optional pointer to string used for cache management
*/
- CurlURLInputStream(const char* url);
+ CurlURLInputStream(const char* url, std::string* cacheTag=NULL);
/**
* Constructor.
*
- * @param url the URL of the resource to fetch
+ * @param url the URL of the resource to fetch
+ * @param cacheTag optional pointer to string used for cache management
*/
- CurlURLInputStream(const XMLCh* url);
+ CurlURLInputStream(const XMLCh* url, std::string* cacheTag=NULL);
/**
* Constructor taking a DOM element supporting the following content:
* <dd><TransportOption provider="CURL" option="150">0</TransportOption></dd>
* </dl>
*
- * @param e DOM to supply configuration
+ * @param e DOM to supply configuration
+ * @param cacheTag optional pointer to string used for cache management
*/
- CurlURLInputStream(const xercesc::DOMElement* e);
+ CurlURLInputStream(const xercesc::DOMElement* e, std::string* cacheTag=NULL);
~CurlURLInputStream();
bool readMore(int *runningHandles);
logging::Category& fLog;
+ std::string* fCacheTag;
std::string fURL;
std::vector<std::string> fSavedOptions;
CURLM* fMulti;
CURL* fEasy;
+ struct curl_slist* fHeaders;
unsigned long fTotalBytesRead;
XMLByte* fWritePtr;
XMLByte* fBufferTailPtr;
XMLCh* fContentType;
+ long fStatusCode;
char fError[CURL_ERROR_SIZE];
};
#ifdef XMLTOOLING_LITE
-URLInputSource::URLInputSource(const XMLCh* url, const char* systemId) : InputSource(systemId), m_url(url)
+URLInputSource::URLInputSource(const XMLCh* url, const char* systemId, string* cacheTag) : InputSource(systemId), m_url(url)
{
}
-URLInputSource::URLInputSource(const DOMElement* e, const char* systemId) : InputSource(systemId)
+URLInputSource::URLInputSource(const DOMElement* e, const char* systemId, string* cacheTag) : InputSource(systemId)
{
static const XMLCh uri[] = UNICODE_LITERAL_3(u,r,i);
static const XMLCh url[] = UNICODE_LITERAL_3(u,r,l);
#else
-URLInputSource::URLInputSource(const XMLCh* url, const char* systemId)
- : InputSource(systemId), m_url(url), m_root(NULL)
+URLInputSource::URLInputSource(const XMLCh* url, const char* systemId, string* cacheTag)
+ : InputSource(systemId), m_cacheTag(cacheTag), m_url(url), m_root(NULL)
{
}
-URLInputSource::URLInputSource(const DOMElement* e, const char* systemId)
- : InputSource(systemId), m_root(e)
+URLInputSource::URLInputSource(const DOMElement* e, const char* systemId, string* cacheTag)
+ : InputSource(systemId), m_cacheTag(cacheTag), m_root(e)
{
}
BinInputStream* URLInputSource::makeStream() const
{
- return m_root ? new CurlURLInputStream(m_root) : new CurlURLInputStream(m_url.get());
+ return m_root ? new CurlURLInputStream(m_root, m_cacheTag) : new CurlURLInputStream(m_url.get(), m_cacheTag);
}
#endif
+
+const char URLInputSource::asciiStatusCodeElementName[] = "URLInputSourceStatus";
+
+const XMLCh URLInputSource::utf16StatusCodeElementName[] = UNICODE_LITERAL_20(U,R,L,I,n,p,u,t,S,o,u,r,c,e,S,t,a,t,u,s);
#include <map>
#include <stack>
+#include <string>
#include <istream>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/sax/InputSource.hpp>
*
* @param url source of input
* @param systemId optional system identifier to attach to the source
+ * @param cacheTag optional pointer to string used for cache management
*/
- URLInputSource(const XMLCh* url, const char* systemId=NULL);
+ URLInputSource(const XMLCh* url, const char* systemId=NULL, std::string* cacheTag=NULL);
/**
* Constructor taking a DOM element supporting the following content:
*
* @param e DOM to supply configuration
* @param systemId optional system identifier to attach to the source
+ * @param cacheTag optional pointer to string used for cache management
*/
- URLInputSource(const xercesc::DOMElement* e, const char* systemId=NULL);
+ URLInputSource(const xercesc::DOMElement* e, const char* systemId=NULL, std::string* cacheTag=NULL);
/// @cond off
virtual xercesc::BinInputStream* makeStream() const;
/// @endcond
+ /** Element name used to signal a non-successful response when fetching a remote document. */
+ static const char asciiStatusCodeElementName[];
+
+ /** Element name used to signal a non-successful response when fetching a remote document. */
+ static const XMLCh utf16StatusCodeElementName[];
private:
#ifdef XMLTOOLING_LITE
xercesc::XMLURL m_url;
#else
+ std::string* m_cacheTag;
xmltooling::auto_ptr_char m_url;
const xercesc::DOMElement* m_root;
#endif
*/
#include "internal.h"
+#include "io/HTTPResponse.h"
#include "util/NDC.h"
#include "util/PathResolver.h"
#include "util/ReloadableXMLFile.h"
doc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
}
else {
- URLInputSource src(m_root);
+ URLInputSource src(m_root, NULL, &m_cacheTag);
Wrapper4InputSource dsrc(&src,false);
if (m_validate)
doc=XMLToolingConfig::getConfig().getValidatingParser().parse(dsrc);
else
doc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
+
+ // Check for a response code signal.
+ if (XMLHelper::isNodeNamed(doc->getDocumentElement(), xmlconstants::XMLTOOLING_NS, URLInputSource::utf16StatusCodeElementName)) {
+ int responseCode = XMLString::parseInt(doc->getDocumentElement()->getFirstChild()->getNodeValue());
+ doc->release();
+ if (responseCode == HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED) {
+ throw responseCode; // toss out as a "known" case to handle gracefully
+ }
+ else {
+ m_log.warn("remote resource fetch returned atypical status code (%d)", responseCode);
+ throw IOException("remote resource fetch failed, check log for status code of response");
+ }
+ }
}
m_log.infoStream() << "loaded XML resource (" << (backup ? m_backing : m_source) << ")" << logging::eol;
throw XMLParserException(msg.get());
}
catch (exception& e) {
- m_log.errorStream() << "error while loading configuration from ("
+ m_log.errorStream() << "error while loading resource ("
<< (m_source.empty() ? "inline" : (backup ? m_backing : m_source)) << "): " << e.what() << logging::eol;
if (!backup && !m_backing.empty())
return load(true);
pair<bool,DOMElement*> ret=load();
if (ret.first)
ret.second->getOwnerDocument()->release();
- } catch (exception& ex) {
+ }
+ catch (int& ex) {
+ if (ex == HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED) {
+ m_log.info("remote resource (%s) unchanged from cached version", m_source.c_str());
+ }
+ else {
+ // Shouldn't happen, we should only get codes intended to be gracefully handled.
+ m_log.crit("maintaining existing configuration, remote resource fetch returned atypical status code (%d)", ex);
+ }
+ }
+ catch (exception& ex) {
m_log.crit("maintaining existing configuration, error reloading resource (%s): %s", m_source.c_str(), ex.what());
}
/** Resource location, may be a local path or a URI. */
std::string m_source;
- /** Path to backup copy for remote resources. */
+ /** Path to backup copy for remote resource. */
std::string m_backing;
/** Last modification of local resource or reload of remote resource. */
/** Time in seconds to wait before trying for new copy of remote resource. */
time_t m_reloadInterval;
+ /** Caching tag associated with remote resource. */
+ std::string m_cacheTag;
+
/** Shared lock for guarding reloads. */
RWLock* m_lock;