[cpp-sp] 01/01: SPPCPP-926 - Replace XML serialization in remoting layer
Scott Cantor
cantor.2 at osu.edu
Tue May 4 19:45:57 UTC 2021
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch dev/4.0.0
in repository cpp-sp.
View the commit online:
http://git.shibboleth.net/view/?p=cpp-sp.git;a=commit;h=500527276c58ccf45d3c823d6cbd365656201e07
commit 500527276c58ccf45d3c823d6cbd365656201e07
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue May 4 15:45:03 2021 -0400
SPPCPP-926 - Replace XML serialization in remoting layer
https://issues.shibboleth.net/jira/browse/SSPCPP-926
Draft implementation of a record-oriented format.
---
shibsp/remoting/impl/ddf.cpp | 403 ++++++++++++++++++++++---------------------
1 file changed, 204 insertions(+), 199 deletions(-)
diff --git a/shibsp/remoting/impl/ddf.cpp b/shibsp/remoting/impl/ddf.cpp
index 868d91a9..8b919a1c 100644
--- a/shibsp/remoting/impl/ddf.cpp
+++ b/shibsp/remoting/impl/ddf.cpp
@@ -28,17 +28,12 @@
#include "remoting/ddf.h"
#include <stdexcept>
+#include <iomanip>
+#include <xmltooling/exceptions.h>
#include <boost/lexical_cast.hpp>
-#include <xercesc/dom/DOM.hpp>
-#include <xercesc/util/XMLUniDefs.hpp>
-#include <xmltooling/XMLToolingConfig.h>
-#include <xmltooling/util/ParserPool.h>
-#include <xmltooling/util/URLEncoder.h>
-#include <xmltooling/util/XMLHelper.h>
using namespace shibsp;
using namespace xmltooling;
-using namespace xercesc;
using namespace std;
// defensive string functions
@@ -100,8 +95,7 @@ struct shibsp::ddf_body_t {
DDF_FLOAT,
DDF_STRUCT,
DDF_LIST,
- DDF_POINTER,
- DDF_STRING_UNSAFE
+ DDF_POINTER
} type; // data type of node
union {
@@ -130,11 +124,11 @@ DDF::DDF(const char* n)
name(n);
}
-DDF::DDF(const char* n, const char* val, bool safe)
+DDF::DDF(const char* n, const char* val, bool)
{
m_handle=new(nothrow) ddf_body_t;
name(n);
- string(const_cast<char*>(val), true, safe);
+ string(const_cast<char*>(val), true);
}
DDF::DDF(const char* n, long val)
@@ -175,7 +169,6 @@ DDF DDF::copy() const
case ddf_body_t::DDF_EMPTY:
return DDF(m_handle->name);
case ddf_body_t::DDF_STRING:
- case ddf_body_t::DDF_STRING_UNSAFE:
return DDF(m_handle->name,m_handle->value.string,(m_handle->type==ddf_body_t::DDF_STRING));
case ddf_body_t::DDF_INT:
return DDF(m_handle->name,m_handle->value.integer);
@@ -245,7 +238,7 @@ bool DDF::isempty() const
bool DDF::isstring() const
{
- return m_handle ? (m_handle->type==ddf_body_t::DDF_STRING || m_handle->type==ddf_body_t::DDF_STRING_UNSAFE) : false;
+ return m_handle ? (m_handle->type==ddf_body_t::DDF_STRING) : false;
}
bool DDF::isint() const
@@ -287,11 +280,12 @@ long DDF::integer() const
case ddf_body_t::DDF_FLOAT:
return static_cast<long>(m_handle->value.floating);
case ddf_body_t::DDF_STRING:
- case ddf_body_t::DDF_STRING_UNSAFE:
return m_handle->value.string ? atol(m_handle->value.string) : 0;
case ddf_body_t::DDF_STRUCT:
case ddf_body_t::DDF_LIST:
return m_handle->value.children.count;
+ default:
+ break;
}
}
return 0;
@@ -306,11 +300,12 @@ double DDF::floating() const
case ddf_body_t::DDF_FLOAT:
return m_handle->value.floating;
case ddf_body_t::DDF_STRING:
- case ddf_body_t::DDF_STRING_UNSAFE:
return m_handle->value.string ? atof(m_handle->value.string) : 0;
case ddf_body_t::DDF_STRUCT:
case ddf_body_t::DDF_LIST:
return m_handle->value.children.count;
+ default:
+ break;
}
}
return 0;
@@ -339,10 +334,10 @@ DDF& DDF::empty()
if (m_handle) {
switch (m_handle->type) {
case ddf_body_t::DDF_STRING:
- case ddf_body_t::DDF_STRING_UNSAFE:
if (m_handle->value.string)
free(m_handle->value.string);
break;
+
case ddf_body_t::DDF_LIST:
case ddf_body_t::DDF_STRUCT:
{
@@ -353,19 +348,22 @@ DDF& DDF::empty()
temp.destroy();
}
}
+
+ default:
+ break;
}
m_handle->type=ddf_body_t::DDF_EMPTY;
}
return *this;
}
-DDF& DDF::string(char* val, bool copyit, bool safe)
+DDF& DDF::string(char* val, bool copyit, bool)
{
if (empty().m_handle) {
m_handle->value.string = copyit ? ddf_strdup(val) : val;
if (!m_handle->value.string && val && *val)
return destroy();
- m_handle->type=(safe ? ddf_body_t::DDF_STRING : ddf_body_t::DDF_STRING_UNSAFE);
+ m_handle->type=ddf_body_t::DDF_STRING;
}
return *this;
}
@@ -713,7 +711,6 @@ void DDF::dump(FILE* f, int indent) const
break;
case ddf_body_t::DDF_STRING:
- case ddf_body_t::DDF_STRING_UNSAFE:
if (m_handle->name)
fprintf(f,"char* %s = ",m_handle->name);
else
@@ -800,13 +797,48 @@ void DDF::dump(FILE* f, int indent) const
fprintf(f,";\n");
}
-// Serialization is fairly easy. We have to walk the DDF and hand-generate a
-// wddxPacket XML fragment, with some simple extensions. We escape the four major
-// special characters, which requires that we output strings one char at a time.
+/*
+ Serialization is now reliant on a line-centric, type-prefixed format.
+
+ <URL-encoded name>|space <typenum> <type-specific>
+ DDF_EMPTY:
+ DDF_POINTER:
+ 0
+ DDF_STRING:
+ 1 URL-encoded string
+ DDF_INT:
+ 2 32-bit integer
+ DDF_FLOAT:
+ 3 fixed format double
+ DDF_STRUCT:
+ 4 32-bit count of children
+ DDF_LIST:
+ 5 32-bit count of children
+*/
-static
-bool
-is32bitSafe(long what)
+static inline char hexchar(unsigned short s)
+{
+ return (s<=9) ? ('0' + s) : ('A' + s - 10);
+}
+
+ostream& encode(ostream& os, const char* s)
+{
+ for (; *s; s++) {
+ if (*s <= 0x20 || *s >= 0x7F) {
+ os << '%';
+ os << hexchar((unsigned char)*s >> 4);
+ os << hexchar((unsigned char)*s & 0x0F);
+ } else if (*s == '%') {
+ os << "%25";
+ }
+ else {
+ os << *s;
+ }
+ }
+ return os;
+}
+
+static bool is32bitSafe(long what)
{
if (sizeof(what) <= 4) return true;
unsigned long uWhat = (what < 0) ? -what : what;
@@ -815,111 +847,58 @@ is32bitSafe(long what)
return (0 == upperOfuWhat);
}
-void serialize(ddf_body_t* p, ostream& os, bool name_attr=true)
+void serialize(ddf_body_t* p, ostream& os)
{
if (p) {
+ if (p->name) {
+ encode(os, p->name);
+ } else {
+ os << '.';
+ }
+ os << ' ';
+
switch (p->type) {
+ case ddf_body_t::DDF_EMPTY:
+ case ddf_body_t::DDF_POINTER:
+ os << ddf_body_t::DDF_EMPTY << endl;
+ break;
case ddf_body_t::DDF_STRING:
- case ddf_body_t::DDF_STRING_UNSAFE:
- os << "<string";
- if (name_attr && p->name) {
- os << " name=\"";
- XMLHelper::encode(os,p->name);
- os << '"';
- }
+ os << ddf_body_t::DDF_STRING;
if (p->value.string) {
- if (p->type == ddf_body_t::DDF_STRING) {
- os << '>';
- XMLHelper::encode(os,p->value.string);
- }
- else {
- os << " unsafe=\"1\">";
- XMLHelper::encode(os,XMLToolingConfig::getConfig().getURLEncoder()->encode(p->value.string).c_str());
- }
- os << "</string>";
+ os << ' ';
+ encode(os, p->value.string);
}
- else
- os << "/>";
+ os << endl;
break;
case ddf_body_t::DDF_INT:
if (!is32bitSafe(p->value.integer))
throw IOException("Integer Overflow");
- os << "<number";
- if (name_attr && p->name) {
- os << " name=\"";
- XMLHelper::encode(os,p->name);
- os << '"';
- }
- os << '>' << p->value.integer << "</number>";
+ os << ddf_body_t::DDF_INT << ' ' << p->value.integer << endl;
break;
case ddf_body_t::DDF_FLOAT:
- os << "<number";
- if (name_attr && p->name) {
- os << " name=\"";
- XMLHelper::encode(os,p->name);
- os << '"';
- }
- os << '>' << fixed << p->value.floating << dec << "</number>";
+ os << ddf_body_t::DDF_FLOAT << ' ' << setprecision(15) << fixed
+ << p->value.floating << dec << endl;
break;
case ddf_body_t::DDF_STRUCT:
- {
- os << "<struct";
- if (name_attr && p->name) {
- os << " name=\"";
- XMLHelper::encode(os,p->name);
- os << '"';
- }
- os << '>';
- ddf_body_t* child=p->value.children.first;
- while (child) {
- os << "<var name=\"";
- XMLHelper::encode(os,child->name);
- os << "\">";
- serialize(child,os,false);
- os << "</var>";
- child=child->next;
- }
- os << "</struct>";
- break;
- }
-
case ddf_body_t::DDF_LIST:
{
- os << "<array length=\"" << p->value.children.count << '"';
- if (name_attr && p->name) {
- os << " name=\"";
- XMLHelper::encode(os,p->name);
- os << '"';
- }
- os << '>';
- ddf_body_t* child=p->value.children.first;
+ os << p->type << ' ' << p->value.children.count << endl;
+ ddf_body_t* child = p->value.children.first;
while (child) {
- serialize(child,os);
- child=child->next;
+ serialize(child, os);
+ child = child->next;
}
- os << "</array>";
break;
}
- case ddf_body_t::DDF_EMPTY:
- case ddf_body_t::DDF_POINTER:
default:
- os << "<null";
- if (name_attr && p->name) {
- os << " name=\"";
- XMLHelper::encode(os,p->name);
- os << '"';
- }
- os << "/>";
break;
}
}
- else
- os << "<null/>";
}
// The stream insertion will work for any ostream-based object.
@@ -927,114 +906,145 @@ void serialize(ddf_body_t* p, ostream& os, bool name_attr=true)
SHIBSP_API ostream& shibsp::operator<<(ostream& os, const DDF& obj)
{
os.precision(15);
- os << "<wddxPacket version=\"1.0\" lowercase=\"no\"><header/><data>";
- serialize(obj.m_handle,os);
- os << "</data></wddxPacket>";
+ serialize(obj.m_handle, os);
return os;
}
-// This is a DTD internal subset based on a compatible permutation of the WDDX spec, with the
-// extension of a name attribute on all the typed elements, which DDF has, but WDDX does not.
+static char x2c(char *what)
+{
+ register char digit;
-/*
-static const char* g_DocType=
-"\
-<!DOCTYPE wddxPacket [\n\
-<!ELEMENT wddxPacket (header, data)>\n\
-<!ATTLIST wddxPacket version CDATA #FIXED \"1.0\" lowercase (yes|no) \"yes\">\n\
-<!ELEMENT header (comment?)>\n\
-<!ELEMENT comment (#PCDATA)>\n\
-<!ELEMENT data (null | number | string | array | struct)>\n\
-<!ELEMENT null EMPTY>\n\
-<!ATTLIST null name CDATA #IMPLIED type CDATA #IMPLIED>\n\
-<!ELEMENT string (#PCDATA | char)*>\n\
-<!ATTLIST string name CDATA #IMPLIED type CDATA #IMPLIED>\n\
-<!ELEMENT char EMPTY>\n\
-<!ATTLIST char code CDATA #REQUIRED>\n\
-<!ELEMENT number (#PCDATA)>\n\
-<!ATTLIST number name CDATA #IMPLIED type CDATA #IMPLIED>\n\
-<!ELEMENT array (null | number | string | array | struct)*>\n\
-<!ATTLIST array length CDATA #REQUIRED name CDATA #IMPLIED type CDATA #IMPLIED>\n\
-<!ELEMENT struct (var*)>\n\
-<!ATTLIST struct name CDATA #IMPLIED type CDATA #IMPLIED>\n\
-<!ELEMENT var (null | number | string | array | struct)>\n\
-<!ATTLIST var name CDATA #REQUIRED>\n\
-]>\n";
-*/
+ digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
+ digit *= 16;
+ digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
+ return(digit);
+}
-// This function constructs a DDF object equivalent to the wddx data element rooted
-// by the input.
+DDF deserialize(istream& is)
+{
+ string line;
+ getline(is, line);
+ if (!is) {
+ return DDF();
+ }
-static const XMLCh _no[] = UNICODE_LITERAL_2(n,o);
-static const XMLCh _name[] = UNICODE_LITERAL_4(n,a,m,e);
-static const XMLCh _var[] = UNICODE_LITERAL_3(v,a,r);
-static const XMLCh _string[] = UNICODE_LITERAL_6(s,t,r,i,n,g);
-static const XMLCh _number[] = UNICODE_LITERAL_6(n,u,m,b,e,r);
-static const XMLCh _array[] = UNICODE_LITERAL_5(a,r,r,a,y);
-static const XMLCh _struct[] = UNICODE_LITERAL_6(s,t,r,u,c,t);
-static const XMLCh _lowercase[] = UNICODE_LITERAL_9(l,o,w,e,r,c,a,s,e);
-static const XMLCh _unsafe[] = UNICODE_LITERAL_6(u,n,s,a,f,e);
+ istringstream source(line);
+
+ // Name is first.
+ string name;
+ source >> name;
+ if (!source || name.empty()) {
+ return DDF();
+ }
-DDF deserialize(DOMElement* root, bool lowercase)
-{
DDF obj(nullptr);
- auto_ptr_char name_val(root->getAttributeNS(nullptr, _name));
- if (name_val.get() && *name_val.get()) {
- if (lowercase)
- for (char* pch=const_cast<char*>(name_val.get()); *pch=tolower(*pch); pch++);
- obj.name(name_val.get());
+ if (name != ".") {
+ obj.name(name.c_str());
}
- const XMLCh* tag=root->getTagName();
- if (XMLString::equals(tag,_var)) {
- root=XMLHelper::getFirstChildElement(root);
- tag=(root ? root->getTagName() : &chNull);
+ // Type is next and should match enums.
+ short type;
+ source >> type;
+ if (!source) {
+ return obj;
}
+
+ switch (type) {
+ case ddf_body_t::DDF_EMPTY:
+ case ddf_body_t::DDF_POINTER:
+ // Nothing to do, it's already empty.
+ return obj;
- if (XMLString::equals(tag,_string)) {
- DOMNode* child=root->getFirstChild();
- if (child && child->getNodeType()==DOMNode::TEXT_NODE) {
- const XMLCh* unsafe = root->getAttributeNS(nullptr, _unsafe);
- if (unsafe && *unsafe==chDigit_1) {
- // If it's unsafe, it's not UTF-8 data, so we have to convert to ASCII and decode it.
- char* encoded = XMLString::transcode(child->getNodeValue());
- XMLToolingConfig::getConfig().getURLEncoder()->decode(encoded);
- obj.string(encoded, true, false); // re-copy into free-able buffer, plus mark unsafe
- XMLString::release(&encoded);
+ case ddf_body_t::DDF_STRING:
+ {
+ string valstr;
+ source >> valstr;
+ if (!source || valstr.empty()) {
+ obj.string(valstr.c_str());
+ return obj;
+ }
+ char* dup = strdup(valstr.c_str());
+ if (!dup) {
+ return obj;
+ }
+
+ // Walk the string and decode any %XX sequences and plus to space.
+ register int x,y;
+ for(x=0,y=0; dup[y]; ++x,++y) {
+ if((dup[x] = dup[y]) == '%' && isxdigit(dup[y+1]) && isxdigit(dup[y+2])) {
+ dup[x] = x2c(&dup[y+1]);
+ y+=2;
+ }
+ }
+ dup[x] = '\0';
+
+ // Store without extra copy.
+ obj.string(dup, false);
}
- else {
- char* val = toUTF8(child->getNodeValue(), true); // use malloc
- obj.string(val, false); // don't re-copy the string
+ break;
+
+ case ddf_body_t::DDF_INT:
+ {
+ long value = 0;
+ source >> value;
+ if (!source) {
+ return obj;
+ }
+ obj.integer(value);
}
- }
- }
- else if (XMLString::equals(tag,_number)) {
- DOMNode* child=root->getFirstChild();
- if (child && child->getNodeType()==DOMNode::TEXT_NODE) {
- auto_ptr_char val(child->getNodeValue());
- if (val.get() && strchr(val.get(),'.'))
- obj.floating(val.get());
- else
- obj.integer(val.get());
- }
- }
- else if (XMLString::equals(tag,_array)) {
- obj.list();
- DOMNodeList* children=root->getChildNodes();
- for (unsigned int i=0; children && i<children->getLength(); i++)
- if (children->item(i)->getNodeType()==DOMNode::ELEMENT_NODE) {
- DDF temp=deserialize(static_cast<DOMElement*>(children->item(i)),lowercase);
- obj.add(temp);
+ break;
+
+ case ddf_body_t::DDF_FLOAT:
+ {
+ double value = 0;
+ source >> fixed >> value >> dec;
+ if (!source) {
+ return obj;
+ }
+ obj.floating(value);
}
- }
- else if (XMLString::equals(tag,_struct)) {
- obj.structure();
- DOMNodeList* children=root->getChildNodes();
- for (unsigned int i=0; children && i<children->getLength(); i++)
- if (children->item(i)->getNodeType()==DOMNode::ELEMENT_NODE) {
- DDF temp=deserialize(static_cast<DOMElement*>(children->item(i)),lowercase);
- obj.add(temp);
+ break;
+
+ case ddf_body_t::DDF_STRUCT:
+ {
+ long count = 0;
+ source >> count;
+ if (!source) {
+ return obj;
+ }
+ obj.structure();
+ for (; count; --count) {
+ DDF child = deserialize(is);
+ if (!child.isnull()) {
+ obj.add(child);
+ } else {
+ break;
+ }
+ }
}
+ break;
+
+ case ddf_body_t::DDF_LIST:
+ {
+ long count = 0;
+ source >> count;
+ if (!source) {
+ return obj;
+ }
+ obj.list();
+ for (; count; --count) {
+ DDF child = deserialize(is);
+ if (!child.isnull()) {
+ obj.add(child);
+ } else {
+ break;
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
}
return obj;
@@ -1042,12 +1052,7 @@ DDF deserialize(DOMElement* root, bool lowercase)
SHIBSP_API istream& shibsp::operator>>(istream& is, DDF& obj)
{
- // Parse the input stream into a DOM tree and construct the equivalent DDF.
- DOMDocument* doc = XMLToolingConfig::getConfig().getParser().parse(is);
- XercesJanitor<DOMDocument> docj(doc);
- const XMLCh* lowercase=doc->getDocumentElement()->getAttribute(_lowercase);
- DOMElement* first=XMLHelper::getFirstChildElement(XMLHelper::getLastChildElement(doc->getDocumentElement()));
obj.destroy();
- obj=deserialize(first,XMLString::compareString(lowercase,_no)!=0);
+ obj = deserialize(is);
return is;
}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list