From 0f946fa6f65965f844e420fc20d33d2b7e21dac0 Mon Sep 17 00:00:00 2001 From: Szabo Bogdan Date: Thu, 5 Mar 2015 01:13:52 +0200 Subject: [PATCH] Work on props litmus test suite --- source/vibedav/base.d | 146 +++++++++++++++++++++--------------------- source/vibedav/file.d | 2 +- source/vibedav/prop.d | 108 ++++++++++++++++++++++--------- 3 files changed, 149 insertions(+), 107 deletions(-) diff --git a/source/vibedav/base.d b/source/vibedav/base.d index ee8c6fc..4ad5126 100644 --- a/source/vibedav/base.d +++ b/source/vibedav/base.d @@ -207,7 +207,6 @@ class DavLockInfo { } a ~= `urn:uuid:`~token~``; - a ~= ``~root.fullURL~``; a ~= ``; @@ -222,9 +221,7 @@ class DavResource { URL url; DavProp properties; - HTTPStatus statusCode; - bool isCollection; protected { Dav dav; @@ -239,49 +236,78 @@ class DavResource { if(strUrl !in dav.resourcePropStorage) { dav.resourcePropStorage[strUrl] = new DavProp; dav.resourcePropStorage[strUrl].addNamespace("d", "DAV:"); + dav.resourcePropStorage[strUrl]["d:resourcetype"] = ""; } this.properties = dav.resourcePropStorage[strUrl]; } - @property - string name() { - return href.baseName; - } + @property { + string name() { + return href.baseName; + } + + string fullURL() { + return url.toString; + } + + void isCollection(bool value) { + if(value) + properties["d:resourcetype"]["d:collection"] = ""; + else + properties["d:resourcetype"].remove("d:collection"); + } - @property - string fullURL() { - return url.toString; + bool isCollection() { + if("d:collection" in properties["d:resourcetype"]) + return true; + else + return false; + } } - string propXmlString(bool[string] props = cast(bool[string])[]) { - string str = `` ~ url.to!string ~ ``; - str ~= ``; + void filterProps(DavProp parent, bool[string] props = cast(bool[string])[]) { + DavProp item = new DavProp; + item.parent = parent; - foreach(key, val; properties) { - auto key1 = key.toLower; + DavProp[][int] result; - writeln("is ", key1, " in ", props, " ", key1 in props); + item[`d:href`] = url.to!string; - bool add = true; - if(props.length > 0 && (key1 in props) is null) - add = false; + foreach(key; props.keys) { + DavProp p; + auto splitPos = key.indexOf(":"); + auto tagName = key[0..splitPos]; + auto tagNameSpace = key[splitPos+1..$]; - if(add) - str ~= val.toString; + try { + p = properties[key]; + result[200] ~= p; + } catch (DavException e) { + p = new DavProp; + p.name = tagName; + p.namespaceAttr = tagNameSpace; + result[e.status] ~= p; + } } - if(props.length == 0 || (props.length > 0 && ("d:resourcetype" in props) !is null)) { - if(isCollection) - str ~= ``; - else - str ~= ``; + foreach(code; result.keys) { + auto propStat = new DavProp; + propStat.parent = item; + propStat.name = "d:propstat"; + propStat["d:prop"] = ""; + + foreach(p; result[code]) { + propStat["d:prop"].addChild(p); + } + + propStat["d:status"] = `HTTP/1.1 ` ~ code.to!string ~ ` ` ~ httpStatusText(code); + item.addChild(propStat); } - str ~= ``; - str ~= `HTTP/1.1 ` ~ statusCode.to!int.to!string ~ ` ` ~ httpStatusText(statusCode) ~ ``; + item["d:status"] = `HTTP/1.1 ` ~ statusCode.to!int.to!string ~ ` ` ~ httpStatusText(statusCode); - return str; + parent.addChild(item); } bool hasChild(Path path) { @@ -308,7 +334,6 @@ class DavResource { foreach(prop; setList) { foreach(string key, p; prop) { - writeln("=>", key); properties[key] = p; result ~= `` ~ p.toString ~ ``; HTTPStatus status = HTTPStatus.ok; @@ -316,43 +341,18 @@ class DavResource { } } - /* //remove properties - XmlNode[] removeList = document.parseXPath("d:propertyupdate/d:remove/d:prop") ~ document.parseXPath("propertyupdate/remove/prop"); - - foreach(prop; removeList) { - auto properties = prop.getChildren; - - foreach(p; properties) { - string tagName = p.getName; - string namespace = ""; - - if(p.hasAttribute("xmlns")) - namespace = p.getAttribute("xmlns"); - - DavProp resProp; - HTTPStatus status = HTTPStatus.ok; - - if(tagName in this.properties) { - resProp = this.properties[tagName]; - resProp.value = ""; - } else { - resProp = new DavProp(tagName, namespace); - status = HTTPStatus.notFound; - } - - //result ~= `` ~ resProp.toTag(tagName) ~ ``; - - try removeProp(tagName); - catch (DavException e) { - status = e.status; - description ~= e.msg; - } + auto removeList = [document].getTagChilds("propertyupdate") + .getTagChilds("remove") + .getTagChilds("prop"); - result ~= `HTTP/1.1 ` ~ status.to!int.to!string ~ ` ` ~ - status.to!string ~ ``; + foreach(prop; removeList) + foreach(string key, p; prop) { + properties.remove(key); + result ~= `` ~ p.toString ~ ``; + HTTPStatus status = HTTPStatus.notFound; + result ~= `HTTP/1.1 ` ~ status.to!int.to!string ~ ` ` ~ status.to!string ~ ``; } - }*/ if(description != "") result ~= `` ~ description ~ ``; @@ -408,14 +408,13 @@ struct PropfindResponse { string toStringProps(bool[string] props) { string str = ``; - str ~= ``; + auto response = parseXMLProp(``); - foreach(res; list) - str ~= `` ~ res.propXmlString(props) ~ ``; - - str ~= ``; + foreach(item; list) { + item.filterProps(response["d:multistatus"]["d:response"], props); + } - return str; + return str ~ response.toString; } } @@ -459,9 +458,7 @@ abstract class Dav { if(properties.length > 0) foreach(string key, p; properties) - list[key.toLower] = true; - - writeln("LIST: ", list); + list[key ~ ":" ~ p.namespace] = true; return list; } @@ -492,7 +489,7 @@ abstract class Dav { void propfind(HTTPServerRequest req, HTTPServerResponse res) { string path = getRequestPath(req); int depth = getDepth(req).to!int; - bool[string] properties; + bool[string] requestedProperties; string requestXml = cast(string)req.bodyReader.readAllUTF8; @@ -646,6 +643,7 @@ HTTPServerRequestDelegate serveDav(T : Dav)(Path path) { debug { if("X-Litmus" in req.headers) { writeln("\n\n\n"); + writeln(req.fullURL); writeln(req.headers["X-Litmus"]); writeln("Method: ", req.method); writeln("=========================="); diff --git a/source/vibedav/file.d b/source/vibedav/file.d index efef3f4..c55775b 100644 --- a/source/vibedav/file.d +++ b/source/vibedav/file.d @@ -112,7 +112,7 @@ final class DavFileResource : DavResource { properties["d:getlastmodified"] = new DavProp(lastModified); properties["d:creationdate"] = new DavProp(creationDate); - if(!isCollection) { + if(!pathstr.isDir) { properties["d:getcontentlength"] = new DavProp(dirent.size.to!string); properties["d:getcontenttype"] = new DavProp(getMimeTypeForFile(pathstr)); } diff --git a/source/vibedav/prop.d b/source/vibedav/prop.d index f116450..82725de 100644 --- a/source/vibedav/prop.d +++ b/source/vibedav/prop.d @@ -31,18 +31,18 @@ import std.uri; import std.uuid; -class DavPropException : Exception { +class DavPropException : DavException { /// - this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) + this(HTTPStatus status, string msg, string mime = "plain/text", string file = __FILE__, size_t line = __LINE__, Throwable next = null) { - super(msg, file, line, next); + super(status, msg, mime, file, line, next); } /// - this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__) + this(HTTPStatus status, string msg, Throwable next, string mime = "plain/text", string file = __FILE__, size_t line = __LINE__) { - super(msg, next, file, line); + super(status, msg, next, mime, file, line); } } @@ -55,11 +55,11 @@ class DavProp { string name; string value; - string namespace; + string namespaceAttr; - this(string value, string namespace) { + this(string value, string namespaceAttr) { this(value); - this.namespace = namespace; + this.namespaceAttr = namespaceAttr; } this(string value) { @@ -70,20 +70,22 @@ class DavProp { this() {} private { - ulong getKeyPos(string key) inout { - foreach(i; 0..properties.length) - if(properties[i].name == key || properties[i].tagName == key) - return i; + foreach(i; 0..properties.length) { + if(properties[i].name == key || + properties[i].tagName == key || + properties[i].tagName ~ ":" ~ properties[i].namespace == key) + return i; + } - throw new DavPropException("Key `" ~ key ~ "` not found"); + throw new DavPropException(HTTPStatus.notFound, "Key `" ~ key ~ "` not found"); } - string getNamespaceAttributes() { + string getNamespaceAttributes() inout { string ns; - if(namespace != "") - ns = ` xmlns="` ~ namespace ~ `"`; + if(namespaceAttr != "") + ns = ` xmlns="` ~ namespaceAttr ~ `"`; if(namespaces.length > 0) foreach(string key, string value; namespaces) @@ -98,7 +100,7 @@ class DavProp { } @property { - string prefix() { + string prefix() inout { auto pos = name.indexOf(":"); if (pos == -1) return ""; @@ -125,6 +127,29 @@ class DavProp { ulong length() { return properties.length; } + + string namespace() inout { + if(namespaceAttr != "") + return namespaceAttr; + + if(prefix != "") + return getNamespaceForPrefix(prefix); + + if(parent !is null) + return parent.namespace; + + return ""; + } + } + + string getNamespaceForPrefix(string prefix) inout { + if(prefix in namespaces) + return namespaces[prefix]; + + if(parent !is null) + return parent.getNamespaceForPrefix(prefix); + + throw new DavPropException(HTTPStatus.internalServerError, `Undefined '` ~ prefix ~ `' namespace.`); } bool isNameSpaceDefined(string prefix) { @@ -205,6 +230,7 @@ class DavProp { if(key in this) { auto pos = getKeyPos(key); properties.remove(pos); + properties.length--; } } @@ -268,17 +294,17 @@ class DavProp { void addNamespace(string prefix, string namespace) { if(isNameSpaceDefined(prefix)) - throw new DavPropException("Prefix `"~prefix~"` is already defined."); + throw new DavPropException(HTTPStatus.internalServerError, "Prefix `"~prefix~"` is already defined."); if(namespace == "") - throw new DavPropException("Empty namespace for prefix `"~prefix~"`."); + throw new DavPropException(HTTPStatus.internalServerError, "Empty namespace for prefix `"~prefix~"`."); namespaces[prefix] = namespace; } void checkNamespacePrefixes() { if(prefix != "" && !isPrefixNameSpaceDefined) - throw new DavPropException(`Undefined '` ~ prefix ~ `' namespace.`); + throw new DavPropException(HTTPStatus.internalServerError, `Undefined '` ~ prefix ~ `' namespace.`); foreach(DavProp p; properties) { p.checkNamespacePrefixes; @@ -319,6 +345,18 @@ unittest { assert(prop["sub"] == subProp); } +unittest { + DavProp properties = new DavProp; + + properties["prop1"] = "value1"; + properties["prop2"] = "value2"; + properties["prop3"] = "value2"; + + properties.remove("prop1"); + + assert(properties.length == 2); +} + unittest { DavProp properties = new DavProp; auto prop1 = new DavProp; @@ -347,7 +385,7 @@ unittest { unittest { auto prop = new DavProp; prop["name"] = "value"; - prop["name"].namespace = "ns"; + prop["name"].namespaceAttr = "ns"; assert(prop.toString == `value`); } @@ -386,6 +424,16 @@ unittest { assert(raised); } +unittest { + auto prop = new DavProp; + prop.namespaces["D"] = "DAV:"; + prop.name = "root"; + prop["D:name"] = ""; + prop["D:name"]["D:val"] = ""; + + assert(prop["D:name"]["D:val"].namespace == "DAV:"); +} + string normalize(const string text) { bool isWs(const char ch) { if(ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t') @@ -491,7 +539,7 @@ DavProp parseXMLProp(string xmlText, DavProp parent = null) { DavProp parseXMLPropText(string xmlNodeText, ref ulong end) { if(xmlNodeText[0] == '<') - throw new DavPropException("The text node can not start with `<`."); + throw new DavPropException(HTTPStatus.internalServerError, "The text node can not start with `<`."); DavProp node = new DavProp; @@ -508,8 +556,6 @@ DavProp parseXMLPropText(string xmlNodeText, ref ulong end) { } DavProp parseXMLPropNode(string xmlNodeText, DavProp parent, ref ulong end) { - writeln("xmlNodeText ", xmlNodeText); - DavProp node = new DavProp; node.parent = parent; string startTag; @@ -543,14 +589,14 @@ DavProp parseXMLPropNode(string xmlNodeText, DavProp parent, ref ulong end) { tags--; if(tags < 0) - throw new DavPropException("Invalid end tag for `" ~ node.name ~ "`."); + throw new DavPropException(HTTPStatus.internalServerError, "Invalid end tag for `" ~ node.name ~ "`."); else if(tags == 0) return xmlNodeText[end..i]; i++; } - throw new DavPropException("Can not find the `" ~ node.name ~ "` end tag."); + throw new DavPropException(HTTPStatus.internalServerError, "Can not find the `" ~ node.name ~ "` end tag."); } void setNameSpaces() { @@ -583,7 +629,7 @@ DavProp parseXMLPropNode(string xmlNodeText, DavProp parent, ref ulong end) { } if(xmlNodeText[0] != '<') - throw new DavPropException("The node must start with `<`."); + throw new DavPropException(HTTPStatus.internalServerError, "The node must start with `<`."); ///get tag attributes foreach(i; 0..xmlNodeText.length) { @@ -603,10 +649,10 @@ DavProp parseXMLPropNode(string xmlNodeText, DavProp parent, ref ulong end) { tagPieces = startTag.split(" "); if(tagPieces.length == 0) - throw new DavPropException("Invalid node content."); + throw new DavPropException(HTTPStatus.internalServerError, "Invalid node content."); else { setNameSpaces; - node.namespace = getAttrValue("xmlns"); + node.namespaceAttr = getAttrValue("xmlns"); } if(tagPieces[0] == "?xml") @@ -684,7 +730,7 @@ unittest { unittest { auto prop = parseXMLProp(``)[0]; - assert(prop.namespace == `DAV:`); + assert(prop.namespaceAttr == `DAV:`); } unittest { @@ -728,9 +774,7 @@ unittest { unittest { //allow to have childrens with the same tag name - writeln("=========================="); auto prop = parseXMLProp(`c1c2`); - writeln(prop.toString); assert(prop.toString == `c1c2`); }