Loading build.gradle.kts +1 −1 Original line number Diff line number Diff line Loading @@ -2,7 +2,7 @@ import org.jetbrains.dokka.gradle.DokkaTask object Libs { // okhttp HTTP library const val okhttpVersion = "4.7.2" const val okhttpVersion = "4.8.0" // XmlPullParser library const val xpp3Version = "1.1.6" Loading src/main/kotlin/at/bitfire/dav4jvm/DavResource.kt +15 −4 Original line number Diff line number Diff line Loading @@ -82,6 +82,7 @@ open class DavResource @JvmOverloads constructor( * * @throws IOException on I/O error * @throws HttpException on HTTP error * @throws DavException on HTTPS -> HTTP redirect */ @Throws(IOException::class, HttpException::class) fun options(callback: (davCapabilities: Set<String>, response: Response) -> Unit) { Loading @@ -104,7 +105,7 @@ open class DavResource @JvmOverloads constructor( * * @throws IOException on I/O error * @throws HttpException on HTTP error * @throws DavException on WebDAV error * @throws DavException on WebDAV error or HTTPS -> HTTP redirect */ @Throws(IOException::class, HttpException::class, DavException::class) fun move(destination: HttpUrl, forceOverride: Boolean, callback: (response: Response) -> Unit) { Loading Loading @@ -145,7 +146,7 @@ open class DavResource @JvmOverloads constructor( * * @throws IOException on I/O error * @throws HttpException on HTTP error * @throws DavException on WebDAV error * @throws DavException on WebDAV error or HTTPS -> HTTP redirect */ @Throws(IOException::class, HttpException::class, DavException::class) fun copy(destination:HttpUrl, forceOverride:Boolean, callback: (response: Response) -> Unit) { Loading Loading @@ -179,6 +180,7 @@ open class DavResource @JvmOverloads constructor( * * @throws IOException on I/O error * @throws HttpException on HTTP error * @throws DavException on HTTPS -> HTTP redirect */ @Throws(IOException::class, HttpException::class) fun mkCol(xmlBody: String?, callback: (response: Response) -> Unit) { Loading Loading @@ -206,6 +208,7 @@ open class DavResource @JvmOverloads constructor( * * @throws IOException on I/O error * @throws HttpException on HTTP error * @throws DavException on HTTPS -> HTTP redirect */ @Throws(IOException::class, HttpException::class) fun get(accept: String, callback: (response: Response) -> Unit) { Loading Loading @@ -235,6 +238,7 @@ open class DavResource @JvmOverloads constructor( * * @throws IOException on I/O error * @throws HttpException on HTTP error * @throws DavException on HTTPS -> HTTP redirect */ @Throws(IOException::class, HttpException::class) fun put(body: RequestBody, ifETag: String? = null, ifScheduleTag: String? = null, ifNoneMatch: Boolean = false, callback: (Response) -> Unit) { Loading Loading @@ -273,6 +277,7 @@ open class DavResource @JvmOverloads constructor( * @throws IOException on I/O error * @throws HttpException on HTTP errors, or when 207 Multi-Status is returned * (because then there was probably a problem with a member resource) * @throws DavException on HTTPS -> HTTP redirect */ @Throws(IOException::class, HttpException::class) fun delete(ifETag: String? = null, ifScheduleTag: String? = null, callback: (Response) -> Unit) { Loading Loading @@ -310,7 +315,7 @@ open class DavResource @JvmOverloads constructor( * * @throws IOException on I/O error * @throws HttpException on HTTP error * @throws DavException on WebDAV error (like no 207 Multi-Status response) * @throws DavException on WebDAV error (like no 207 Multi-Status response) or HTTPS -> HTTP redirect */ @Throws(IOException::class, HttpException::class, DavException::class) fun propfind(depth: Int, vararg reqProp: Property.Name, callback: DavResponseCallback) { Loading Loading @@ -386,8 +391,10 @@ open class DavResource @JvmOverloads constructor( * @param sendRequest called to send the request (may be called multiple times) * * @return response of the last request (whether it is a redirect or not) * * @throws DavException on HTTPS -> HTTP redirect */ protected fun followRedirects(sendRequest: () -> Response): Response { internal fun followRedirects(sendRequest: () -> Response): Response { lateinit var response: Response for (attempt in 1..MAX_REDIRECTS) { response = sendRequest() Loading @@ -397,6 +404,10 @@ open class DavResource @JvmOverloads constructor( val target = it.header("Location")?.let { location.resolve(it) } if (target != null) { log.fine("Redirected, new location = $target") if (location.isHttps && !target.isHttps) throw DavException("Received redirect from HTTPS to HTTP") location = target } else throw DavException("Redirected without new Location") Loading src/test/kotlin/at/bitfire/dav4jvm/DavResourceTest.kt +57 −0 Original line number Diff line number Diff line Loading @@ -13,9 +13,13 @@ import at.bitfire.dav4jvm.property.DisplayName import at.bitfire.dav4jvm.property.GetContentType import at.bitfire.dav4jvm.property.GetETag import at.bitfire.dav4jvm.property.ResourceType import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.Protocol import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.ResponseBody.Companion.toResponseBody import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer import org.junit.After Loading Loading @@ -722,4 +726,57 @@ class DavResourceTest { assertTrue(called) } @Test fun testFollowRedirects_302() { val url = sampleUrl() val dav = DavResource(httpClient, url) var i = 0 dav.followRedirects { if (i++ == 0) okhttp3.Response.Builder() .protocol(Protocol.HTTP_1_1) .code(302) .message("Found") .header("Location", "http://to.com/") .request(Request.Builder() .get() .url("http://from.com/") .build()) .body("New location!".toResponseBody()) .build() else okhttp3.Response.Builder() .protocol(Protocol.HTTP_1_1) .code(204) .message("No Content") .request(Request.Builder() .get() .url("http://to.com/") .build()) .build() }.let { response -> assertEquals(204, response.code) assertEquals("http://to.com/".toHttpUrl(), dav.location) } } @Test(expected = DavException::class) fun testFollowRedirects_HttpsToHttp() { val dav = DavResource(httpClient, "https://from.com".toHttpUrl()) dav.followRedirects { okhttp3.Response.Builder() .protocol(Protocol.HTTP_1_1) .code(302) .message("Found") .header("Location", "http://to.com/") .request(Request.Builder() .get() .url("https://from.com/") .build()) .body("New location!".toResponseBody()) .build() } } } Loading
build.gradle.kts +1 −1 Original line number Diff line number Diff line Loading @@ -2,7 +2,7 @@ import org.jetbrains.dokka.gradle.DokkaTask object Libs { // okhttp HTTP library const val okhttpVersion = "4.7.2" const val okhttpVersion = "4.8.0" // XmlPullParser library const val xpp3Version = "1.1.6" Loading
src/main/kotlin/at/bitfire/dav4jvm/DavResource.kt +15 −4 Original line number Diff line number Diff line Loading @@ -82,6 +82,7 @@ open class DavResource @JvmOverloads constructor( * * @throws IOException on I/O error * @throws HttpException on HTTP error * @throws DavException on HTTPS -> HTTP redirect */ @Throws(IOException::class, HttpException::class) fun options(callback: (davCapabilities: Set<String>, response: Response) -> Unit) { Loading @@ -104,7 +105,7 @@ open class DavResource @JvmOverloads constructor( * * @throws IOException on I/O error * @throws HttpException on HTTP error * @throws DavException on WebDAV error * @throws DavException on WebDAV error or HTTPS -> HTTP redirect */ @Throws(IOException::class, HttpException::class, DavException::class) fun move(destination: HttpUrl, forceOverride: Boolean, callback: (response: Response) -> Unit) { Loading Loading @@ -145,7 +146,7 @@ open class DavResource @JvmOverloads constructor( * * @throws IOException on I/O error * @throws HttpException on HTTP error * @throws DavException on WebDAV error * @throws DavException on WebDAV error or HTTPS -> HTTP redirect */ @Throws(IOException::class, HttpException::class, DavException::class) fun copy(destination:HttpUrl, forceOverride:Boolean, callback: (response: Response) -> Unit) { Loading Loading @@ -179,6 +180,7 @@ open class DavResource @JvmOverloads constructor( * * @throws IOException on I/O error * @throws HttpException on HTTP error * @throws DavException on HTTPS -> HTTP redirect */ @Throws(IOException::class, HttpException::class) fun mkCol(xmlBody: String?, callback: (response: Response) -> Unit) { Loading Loading @@ -206,6 +208,7 @@ open class DavResource @JvmOverloads constructor( * * @throws IOException on I/O error * @throws HttpException on HTTP error * @throws DavException on HTTPS -> HTTP redirect */ @Throws(IOException::class, HttpException::class) fun get(accept: String, callback: (response: Response) -> Unit) { Loading Loading @@ -235,6 +238,7 @@ open class DavResource @JvmOverloads constructor( * * @throws IOException on I/O error * @throws HttpException on HTTP error * @throws DavException on HTTPS -> HTTP redirect */ @Throws(IOException::class, HttpException::class) fun put(body: RequestBody, ifETag: String? = null, ifScheduleTag: String? = null, ifNoneMatch: Boolean = false, callback: (Response) -> Unit) { Loading Loading @@ -273,6 +277,7 @@ open class DavResource @JvmOverloads constructor( * @throws IOException on I/O error * @throws HttpException on HTTP errors, or when 207 Multi-Status is returned * (because then there was probably a problem with a member resource) * @throws DavException on HTTPS -> HTTP redirect */ @Throws(IOException::class, HttpException::class) fun delete(ifETag: String? = null, ifScheduleTag: String? = null, callback: (Response) -> Unit) { Loading Loading @@ -310,7 +315,7 @@ open class DavResource @JvmOverloads constructor( * * @throws IOException on I/O error * @throws HttpException on HTTP error * @throws DavException on WebDAV error (like no 207 Multi-Status response) * @throws DavException on WebDAV error (like no 207 Multi-Status response) or HTTPS -> HTTP redirect */ @Throws(IOException::class, HttpException::class, DavException::class) fun propfind(depth: Int, vararg reqProp: Property.Name, callback: DavResponseCallback) { Loading Loading @@ -386,8 +391,10 @@ open class DavResource @JvmOverloads constructor( * @param sendRequest called to send the request (may be called multiple times) * * @return response of the last request (whether it is a redirect or not) * * @throws DavException on HTTPS -> HTTP redirect */ protected fun followRedirects(sendRequest: () -> Response): Response { internal fun followRedirects(sendRequest: () -> Response): Response { lateinit var response: Response for (attempt in 1..MAX_REDIRECTS) { response = sendRequest() Loading @@ -397,6 +404,10 @@ open class DavResource @JvmOverloads constructor( val target = it.header("Location")?.let { location.resolve(it) } if (target != null) { log.fine("Redirected, new location = $target") if (location.isHttps && !target.isHttps) throw DavException("Received redirect from HTTPS to HTTP") location = target } else throw DavException("Redirected without new Location") Loading
src/test/kotlin/at/bitfire/dav4jvm/DavResourceTest.kt +57 −0 Original line number Diff line number Diff line Loading @@ -13,9 +13,13 @@ import at.bitfire.dav4jvm.property.DisplayName import at.bitfire.dav4jvm.property.GetContentType import at.bitfire.dav4jvm.property.GetETag import at.bitfire.dav4jvm.property.ResourceType import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.Protocol import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.ResponseBody.Companion.toResponseBody import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer import org.junit.After Loading Loading @@ -722,4 +726,57 @@ class DavResourceTest { assertTrue(called) } @Test fun testFollowRedirects_302() { val url = sampleUrl() val dav = DavResource(httpClient, url) var i = 0 dav.followRedirects { if (i++ == 0) okhttp3.Response.Builder() .protocol(Protocol.HTTP_1_1) .code(302) .message("Found") .header("Location", "http://to.com/") .request(Request.Builder() .get() .url("http://from.com/") .build()) .body("New location!".toResponseBody()) .build() else okhttp3.Response.Builder() .protocol(Protocol.HTTP_1_1) .code(204) .message("No Content") .request(Request.Builder() .get() .url("http://to.com/") .build()) .build() }.let { response -> assertEquals(204, response.code) assertEquals("http://to.com/".toHttpUrl(), dav.location) } } @Test(expected = DavException::class) fun testFollowRedirects_HttpsToHttp() { val dav = DavResource(httpClient, "https://from.com".toHttpUrl()) dav.followRedirects { okhttp3.Response.Builder() .protocol(Protocol.HTTP_1_1) .code(302) .message("Found") .header("Location", "http://to.com/") .request(Request.Builder() .get() .url("https://from.com/") .build()) .body("New location!".toResponseBody()) .build() } } }