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
8 changes: 0 additions & 8 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 13 additions & 26 deletions src/main/kotlin/at/bitfire/dav4jvm/ktor/DavResource.kt
Original file line number Diff line number Diff line change
Expand Up @@ -220,14 +220,7 @@ open class DavResource(
header(HttpHeaders.Overwrite, "F")
}
}) { response ->
checkStatus(response)

if (response.status == HttpStatusCode.MultiStatus) {
/* Multiple resources were to be affected by the MOVE, but errors on some
of them prevented the operation from taking place.
[_] (RFC 4918 9.9.4. Status Codes for MOVE Method) */
throw HttpException.fromResponse(response)
}
checkStatus(response, multiStatusIsError = true)

// update location
val nPath = response.headers[HttpHeaders.Location] ?: destination.toString()
Expand Down Expand Up @@ -258,13 +251,7 @@ open class DavResource(
header("Overwrite", "F")
}
}) { response ->
checkStatus(response)

if (response.status == HttpStatusCode.MultiStatus)
/* Multiple resources were to be affected by the COPY, but errors on some
of them prevented the operation from taking place.
[_] (RFC 4918 9.8.5. Status Codes for COPY Method) */
throw HttpException.fromResponse(response)
checkStatus(response, multiStatusIsError = true)

callback.onResponse(response)
}
Expand Down Expand Up @@ -301,7 +288,7 @@ open class DavResource(
setBody(xmlBody)
}
}) { response ->
checkStatus(response)
checkStatus(response, multiStatusIsError = true)
callback.onResponse(response)
}
}
Expand Down Expand Up @@ -470,13 +457,7 @@ open class DavResource(
headers.appendAll(additionalHeaders)
}
}) { response ->
checkStatus(response)

if (response.status == HttpStatusCode.MultiStatus)
/* If an error occurs deleting a member resource (a resource other than
the resource identified in the Request-URI), then the response can be
a 207 (Multi-Status). […] (RFC 4918 9.6.1. DELETE for Collections) */
throw HttpException.fromResponse(response)
checkStatus(response, multiStatusIsError = true)

callback.onResponse(response)
}
Expand Down Expand Up @@ -597,12 +578,18 @@ open class DavResource(
/**
* Checks the status from an HTTP response and throws a specific exception in case of an error.
*
* @param multiStatusIsError when true, 207 Multi-Status is treated as an error
* @throws HttpException in case of an HTTP error
*/
protected suspend fun checkStatus(response: HttpResponse) {
if (response.status.isSuccess())
return // everything OK
protected suspend fun checkStatus(response: HttpResponse, multiStatusIsError: Boolean = false) {
// handle 2xx response codes
if (response.status.isSuccess()) {
if (response.status == HttpStatusCode.MultiStatus && multiStatusIsError)
throw HttpException.fromResponse(response)
return
}

// handle other response codes
throw HttpException.fromResponse(response)
}

Expand Down
36 changes: 12 additions & 24 deletions src/main/kotlin/at/bitfire/dav4jvm/okhttp/DavResource.kt
Original file line number Diff line number Diff line change
Expand Up @@ -208,12 +208,7 @@ open class DavResource @JvmOverloads constructor(
.build())
.execute()
}.use { response ->
checkStatus(response)
if (response.code == HTTP_MULTISTATUS)
/* Multiple resources were to be affected by the MOVE, but errors on some
of them prevented the operation from taking place.
[_] (RFC 4918 9.9.4. Status Codes for MOVE Method) */
throw HttpException(response)
checkStatus(response, multiStatusIsError = true)

// update location
location.resolve(response.header("Location") ?: destination.toString())?.let {
Expand Down Expand Up @@ -250,13 +245,7 @@ open class DavResource @JvmOverloads constructor(
.build())
.execute()
}.use{ response ->
checkStatus(response)

if (response.code == HTTP_MULTISTATUS)
/* Multiple resources were to be affected by the COPY, but errors on some
of them prevented the operation from taking place.
[_] (RFC 4918 9.8.5. Status Codes for COPY Method) */
throw HttpException(response)
checkStatus(response, multiStatusIsError = true)

callback.onResponse(response)
}
Expand Down Expand Up @@ -290,7 +279,7 @@ open class DavResource @JvmOverloads constructor(
followRedirects {
httpClient.newCall(request.build()).execute()
}.use { response ->
checkStatus(response)
checkStatus(response, multiStatusIsError = true)
callback.onResponse(response)
}
Comment thread
rfc2822 marked this conversation as resolved.
}
Expand Down Expand Up @@ -541,13 +530,7 @@ open class DavResource @JvmOverloads constructor(

httpClient.newCall(builder.build()).execute()
}.use { response ->
checkStatus(response)

if (response.code == HTTP_MULTISTATUS)
/* If an error occurs deleting a member resource (a resource other than
the resource identified in the Request-URI), then the response can be
a 207 (Multi-Status). […] (RFC 4918 9.6.1. DELETE for Collections) */
throw HttpException(response)
checkStatus(response, multiStatusIsError = true)

callback.onResponse(response)
}
Expand Down Expand Up @@ -662,13 +645,18 @@ open class DavResource @JvmOverloads constructor(
/**
* Checks the status from an HTTP response and throws an exception in case of an error.
*
* @param multiStatusIsError when true, 207 Multi-Status is treated as an error
* @throws HttpException in case of an HTTP error
*/
protected fun checkStatus(response: Response) {
if (response.code / 100 == 2)
// everything OK
protected fun checkStatus(response: Response, multiStatusIsError: Boolean = false) {
// handle 2xx response codes
if (response.code / 100 == 2) {
if (response.code == HTTP_MULTISTATUS && multiStatusIsError)
throw HttpException(response)
return
}

// handle other response codes
throw when (response.code) {
401 -> UnauthorizedException(response)
403 -> ForbiddenException(response)
Expand Down
44 changes: 20 additions & 24 deletions src/test/kotlin/at/bitfire/dav4jvm/ktor/DavResourceTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,12 @@ import at.bitfire.dav4jvm.property.webdav.DisplayName
import at.bitfire.dav4jvm.property.webdav.GetETag
import at.bitfire.dav4jvm.property.webdav.ResourceType
import at.bitfire.dav4jvm.property.webdav.WebDAV
import io.ktor.client.HttpClient
import io.ktor.client.engine.mock.MockEngine
import io.ktor.client.engine.mock.respond
import io.ktor.client.engine.mock.respondError
import io.ktor.client.engine.mock.respondOk
import io.ktor.client.engine.mock.respondRedirect
import io.ktor.client.request.get
import io.ktor.client.request.prepareGet
import io.ktor.client.statement.bodyAsChannel
import io.ktor.client.statement.bodyAsText
import io.ktor.client.statement.request
import io.ktor.http.ContentType
import io.ktor.http.HeadersBuilder
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMethod
import io.ktor.http.HttpStatusCode
import io.ktor.http.URLBuilder
import io.ktor.http.Url
import io.ktor.http.content.TextContent
import io.ktor.http.contentType
import io.ktor.http.fullPath
import io.ktor.http.headersOf
import io.ktor.http.takeFrom
import io.ktor.http.withCharset
import io.ktor.client.*
import io.ktor.client.engine.mock.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.http.content.*
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
Expand Down Expand Up @@ -532,6 +514,20 @@ class DavResourceTest {
}
}

@Test
fun `MkCol NEGATIVE TEST CASES 207 multi-status (MKCOL and MKCALENDAR should not return 207)`() = runTest {
val mockEngine = MockEngine { respond("", HttpStatusCode.MultiStatus) }
val httpClient = HttpClient(mockEngine)
var called = false

try {
DavResource(httpClient, sampleUrl).mkCol("") { called = true }
fail("Expected HttpException")
} catch (_: HttpException) {
assertFalse(called)
}
}

@Test
fun `Options with capabilities`() = runTest {
val mockEngine = MockEngine {
Expand Down
Loading