Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/main/kotlin/at/bitfire/dav4jvm/ktor/MultiStatusParser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class MultiStatusParser(

suspend fun parseResponse(parser: XmlPullParser): List<Property> {
val responseProperties = mutableListOf<Property>()
val responseParser = ResponseParser(location, callback)
val responseParser = ResponseParser(location)

// <!ELEMENT multistatus (response*, responsedescription?,
// sync-token?) >
Expand All @@ -40,7 +40,7 @@ class MultiStatusParser(
if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1)
when (parser.propertyName()) {
WebDAV.Response ->
responseParser.parseResponse(parser)
responseParser.parseResponse(parser, callback)
WebDAV.SyncToken ->
XmlReader(parser).readText()?.let {
responseProperties += SyncToken(it)
Expand Down
12 changes: 6 additions & 6 deletions src/main/kotlin/at/bitfire/dav4jvm/ktor/ResponseParser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ import java.util.logging.Logger
* @param location location of the request (used to resolve possible relative `<href>`)
*/
class ResponseParser(
private val location: Url,
private val callback: MultiResponseCallback
private val location: Url
) {

private val logger
Expand All @@ -49,8 +48,11 @@ class ResponseParser(
*
* So if you want PROPFIND results to have a trailing slash when they are collections, make sure
* that you query [at.bitfire.dav4jvm.property.webdav.ResourceType].
*
* @param parser XML document to parse
* @param callback callback to be called for every multistatus `<response>`
*/
suspend fun parseResponse(parser: XmlPullParser) {
suspend fun parseResponse(parser: XmlPullParser, callback: MultiResponseCallback) {
val depth = parser.depth

var hrefOrNull: Url? = null
Expand All @@ -72,7 +74,7 @@ class ResponseParser(
WebDAV.Error ->
error = Error.parseError(parser)
WebDAV.Location ->
newLocation = Url(parser.nextText()) // TODO: Need to catch exception here?
newLocation = parser.nextText().toUrlOrNull()
}
eventType = parser.next()
}
Expand All @@ -94,8 +96,6 @@ class ResponseParser(
href = UrlUtils.withTrailingSlash(href)
}

//log.log(Level.FINE, "Received properties for $href", if (status != null) status else propStat)

// Which resource does this <response> represent?
val relation = when {
UrlUtils.omitTrailingSlash(href).equalsForWebDAV(UrlUtils.omitTrailingSlash(location)) ->
Expand Down
113 changes: 103 additions & 10 deletions src/test/kotlin/at/bitfire/dav4jvm/ktor/ResponseParserTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,45 +10,138 @@

package at.bitfire.dav4jvm.ktor

import at.bitfire.dav4jvm.XmlUtils
import io.ktor.http.Url
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Test
import java.io.StringReader

class ResponseParserTest {

val baseUrl = Url("https://example.com/collection/")
val parser = ResponseParser(baseUrl, callback = { _, _ ->
// no-op
})
private val baseUrl = Url("http://www.example.com/container/")
private val parser = ResponseParser(baseUrl)


@Test
fun `parseResponse relation=SELF`() = runTest {
val xml = XmlUtils.newPullParser()
xml.setInput(StringReader("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" +
"<multistatus xmlns=\"DAV:\">\n" +
"<response>\n" +
" <href>http://www.example.com/container/</href>\n" +
" <propstat>\n" +
" <prop xmlns:R=\"http://ns.example.com/boxschema/\">\n" +
" <R:bigbox/>\n" +
" <R:author/>\n" +
" <creationdate/>\n" +
" <displayname/>\n" +
" <resourcetype/>\n" +
" <supportedlock/>\n" +
" </prop>\n" +
" <status>HTTP/1.1 200 OK</status>\n" +
" </propstat>\n" +
"</response>"
))
xml.nextTag() // multistatus
xml.nextTag() // response
parser.parseResponse(xml) { response, relation ->
assertEquals(Url("http://www.example.com/container/"), response.href)
assertEquals(Response.HrefRelation.SELF, relation)
}
}

@Test
fun `parseResponse relation=MEMBER`() = runTest {
val xml = XmlUtils.newPullParser()
xml.setInput(StringReader("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" +
"<multistatus xmlns=\"DAV:\">\n" +
"<response>\n" +
" <href>http://www.example.com/container/front.html</href>\n" +
" <propstat>\n" +
" <prop xmlns:R=\"http://ns.example.com/boxschema/\">\n" +
" <R:bigbox/>\n" +
" <creationdate/>\n" +
" <displayname/>\n" +
" <getcontentlength/>\n" +
" <getcontenttype/>\n" +
" <getetag/>\n" +
" <getlastmodified/>\n" +
" <resourcetype/>\n" +
" <supportedlock/>\n" +
" </prop>\n" +
" <status>HTTP/1.1 200 OK</status>\n" +
" </propstat>\n" +
"</response>"
))
xml.nextTag() // multistatus
xml.nextTag() // response
parser.parseResponse(xml) { response, relation ->
assertEquals(Url("http://www.example.com/container/front.html"), response.href)
assertEquals(Response.HrefRelation.MEMBER, relation)
}
}

@Test
fun `parseResponse relation=OTHER`() = runTest {
val xml = XmlUtils.newPullParser()
xml.setInput(StringReader("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" +
"<multistatus xmlns=\"DAV:\">\n" +
"<response>\n" +
" <href>http://other.example.com/was-not-requested</href>\n" +
" <propstat>\n" +
" <prop xmlns:R=\"http://ns.example.com/boxschema/\">\n" +
" <R:bigbox/>\n" +
" <creationdate/>\n" +
" <displayname/>\n" +
" <getcontentlength/>\n" +
" <getcontenttype/>\n" +
" <getetag/>\n" +
" <getlastmodified/>\n" +
" <resourcetype/>\n" +
" <supportedlock/>\n" +
" </prop>\n" +
" <status>HTTP/1.1 200 OK</status>\n" +
" </propstat>\n" +
"</response>"
))
xml.nextTag() // multistatus
xml.nextTag() // response
parser.parseResponse(xml) { response, relation ->
assertEquals(Url("http://other.example.com/was-not-requested"), response.href)
assertEquals(Response.HrefRelation.OTHER, relation)
}
}


@Test
fun `resolveHref with absolute URL`() {
assertEquals(
Url("https://example.com/collection/member"),
parser.resolveHref("https://example.com/collection/member")
Url("http://www.example.com/container/member"),
parser.resolveHref("http://www.example.com/container/member")
)
}

@Test
fun `resolveHref with absolute path`() {
assertEquals(
Url("https://example.com/collection/member"),
parser.resolveHref("/collection/member")
Url("http://www.example.com/container/member"),
parser.resolveHref("/container/member")
)
}

@Test
fun `resolveHref with relative path`() {
assertEquals(
Url("https://example.com/collection/member"),
Url("http://www.example.com/container/member"),
parser.resolveHref("member")
)
}

@Test
fun `resolveHref with relative path with colon`() {
assertEquals(
Url("https://example.com/collection/mem:ber"),
Url("http://www.example.com/container/mem:ber"),
parser.resolveHref("mem:ber")
)
}
Expand Down