Ktor: [ktor-client-android] 302 redirect is not handled for POST request

Created on 14 Apr 2020  路  3Comments  路  Source: ktorio/ktor

Ktor Version and Engine Used (client or server and name)
1.3.2, ktor-client-android

Describe the bug
The redirect is not working for 302 status code if the request method was POST (I assume for every not GET method)

To Reproduce
Steps to reproduce the behavior:

  1. Use the simple instance of android HttpClient:
private val androidClient by lazy {
        HttpClient(Android.config {
                connectTimeout = 60_000
                socketTimeout = 40_000
            }
        ) {
            install(HttpCookies) {
                storage = AcceptAllCookiesStorage()
            }
        }
    }
  1. Execute post request for url:
androidClient.post("http://www.mocky.io/v2/5e95a5f12f00005b410252ca")
  1. See exception.
    Note that error is not reproduced if request has GET method

Expected behavior
Should get a response:

{
    "text": "Hello world"
}

Actual result
io.ktor.client.features.RedirectResponseException: Unhandled redirect:

W/System.err: io.ktor.client.features.RedirectResponseException: Unhandled redirect: http://www.mocky.io/v2/5e95a5f12f00005b410252ca. Status: 302 Found
2020-04-14 17:51:32.736 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.client.features.DefaultResponseValidationKt$addDefaultResponseValidation$1$1.invokeSuspend(DefaultResponseValidation.kt:34)
2020-04-14 17:51:32.736 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.client.features.DefaultResponseValidationKt$addDefaultResponseValidation$1$1.invoke(Unknown Source:10)
2020-04-14 17:51:32.736 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.client.features.HttpCallValidator.validateResponse(HttpCallValidator.kt:37)
2020-04-14 17:51:32.736 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.client.features.HttpCallValidator$Companion$install$2.invokeSuspend(HttpCallValidator.kt:99)
2020-04-14 17:51:32.737 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.client.features.HttpCallValidator$Companion$install$2.invoke(Unknown Source:14)
2020-04-14 17:51:32.737 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:318)
2020-04-14 17:51:32.737 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:163)
2020-04-14 17:51:32.737 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:183)
2020-04-14 17:51:32.738 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:27)
2020-04-14 17:51:32.738 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.client.call.HttpClientCall.receive(HttpClientCall.kt:75)
2020-04-14 17:51:32.738 8777-8777/com.testapp.android.ktorbug W/System.err:     at com.testapp.android.ktorbug.testappViewModel.executeAndroidRequest(testappViewModel.kt:396)
2020-04-14 17:51:32.738 8777-8777/com.testapp.android.ktorbug W/System.err:     at com.testapp.android.ktorbug.testappViewModel$executeAndroidRequest$1.invokeSuspend(Unknown Source:12)
2020-04-14 17:51:32.739 8777-8777/com.testapp.android.ktorbug W/System.err:     at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
2020-04-14 17:51:32.739 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(PipelineContext.kt:238)
2020-04-14 17:51:32.739 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:194)
2020-04-14 17:51:32.739 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
2020-04-14 17:51:32.740 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(PipelineContext.kt:144)
2020-04-14 17:51:32.740 8777-8777/com.testapp.android.ktorbug W/System.err:     at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
2020-04-14 17:51:32.740 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(PipelineContext.kt:238)
2020-04-14 17:51:32.740 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:194)
2020-04-14 17:51:32.740 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
2020-04-14 17:51:32.740 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(PipelineContext.kt:144)
2020-04-14 17:51:32.741 8777-8777/com.testapp.android.ktorbug W/System.err:     at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
2020-04-14 17:51:32.741 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(PipelineContext.kt:238)
2020-04-14 17:51:32.741 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:194)
2020-04-14 17:51:32.741 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
2020-04-14 17:51:32.741 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(PipelineContext.kt:144)
2020-04-14 17:51:32.742 8777-8777/com.testapp.android.ktorbug W/System.err:     at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
2020-04-14 17:51:32.742 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(PipelineContext.kt:238)
2020-04-14 17:51:32.742 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:194)
2020-04-14 17:51:32.742 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
2020-04-14 17:51:32.742 8777-8777/com.testapp.android.ktorbug W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(PipelineContext.kt:144)
2020-04-14 17:51:32.743 8777-8777/com.testapp.android.ktorbug W/System.err:     at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
2020-04-14 17:51:32.743 8777-8777/com.testapp.android.ktorbug W/System.err:     at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
2020-04-14 17:51:32.743 8777-8777/com.testapp.android.ktorbug W/System.err:     at android.os.Handler.handleCallback(Handler.java:873)
2020-04-14 17:51:32.743 8777-8777/com.testapp.android.ktorbug W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:99)
2020-04-14 17:51:32.743 8777-8777/com.testapp.android.ktorbug W/System.err:     at android.os.Looper.loop(Looper.java:193)
2020-04-14 17:51:32.743 8777-8777/com.testapp.android.ktorbug W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:6723)
2020-04-14 17:51:32.744 8777-8777/com.testapp.android.ktorbug W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
2020-04-14 17:51:32.744 8777-8777/com.testapp.android.ktorbug W/System.err:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:495)
2020-04-14 17:51:32.744 8777-8777/com.testapp.android.ktorbug W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

Screenshots
If applicable, add screenshots to help explain your problem.

bug

Most helpful comment

Well, @gabin8, as far as I understand in your case the POST after the redirect should become a GET method. This case is a bit simpler and you can implement it manually specifying the following redirect feature configuration:

    val androidClient = HttpClient(Android) {
        engine {
            connectTimeout = 60_000
            socketTimeout = 40_000
        }

        install(HttpRedirect) {
            checkHttpMethod = false
        }
    }

    val res = androidClient.post<String>("http://www.mocky.io/v2/5e95a5f12f00005b410252ca")

This workaround works well, so let me close the issue. In case of any additional questions feel free to reopen it.

All 3 comments

Hi @gabin8,

Yes, that's true. This problem was already mentioned in https://github.com/ktorio/ktor/issues/1623. We don't support the POST method in a redirect.

@dmitrievanthony Can you offer any workaround for this? Especially in case when I don't have control over server. Also it would be nice to make such redirect handling optional or even build-in. Because from what I see - the browsers, postman and other network clients handle such redirects and don't throw an exception

Well, @gabin8, as far as I understand in your case the POST after the redirect should become a GET method. This case is a bit simpler and you can implement it manually specifying the following redirect feature configuration:

    val androidClient = HttpClient(Android) {
        engine {
            connectTimeout = 60_000
            socketTimeout = 40_000
        }

        install(HttpRedirect) {
            checkHttpMethod = false
        }
    }

    val res = androidClient.post<String>("http://www.mocky.io/v2/5e95a5f12f00005b410252ca")

This workaround works well, so let me close the issue. In case of any additional questions feel free to reopen it.

Was this page helpful?
0 / 5 - 0 ratings