https://kotlinlang.org logo
#ktor
Title
# ktor
r

robin

06/20/2022, 10:51 AM
After updating our Ktor webservice from Ktor 1.6 to 2.0, we've been having troubles to get the CORS plugin to handle preflight requests correctly. Basically, OPTIONS requests will always be answered with a 405 Method Not Allowed no matter what you do, unless you explicitly specify the options route in your routing plugin. This seems to not be a problem with the CORS plugin itself, but rather a side effect of this issue being solved, which prevents the CORS plugin from ever being called for OPTIONS requests to a route where no options handler is installed, but another method handler exists. Is there a recommended workaround for that? The only solution I've found so far that fixes this for all routes without specifying an explicit options handler for each one individually is adding this generic catch-all handler to the top level routing:
Copy code
options("{...}") { }
But that feels like a hack, and I'm not even sure it doesn't produce unwanted side effects, even though CORS seems to work correctly with it. Isn't there a way to let CORS handle preflight requests fully automatically without having to add this?
a

Aleksei Tirman [JB]

06/20/2022, 12:12 PM
Could you please share a code snippet for the server and an example request to reproduce your issue?
r

robin

06/20/2022, 7:36 PM
Sure, it's actually really simple to reproduce. Use this code for the server:
Copy code
fun main() {
    embeddedServer(Netty, port = 8080) {
        install(CallLogging)
        routing {
            install(CORS) {
                anyHost()
                allowMethod(HttpMethod.Put)
            }
            // options("{...}") { }
            put("test") {
                call.respond("Hello World")
            }
        }
    }.start(true)
}
And then try to run this request against it:
Copy code
OPTIONS <http://localhost:8080/test>
Access-Control-Request-Method: PUT
Origin: <https://example.com>
With ktor 2.0.2, this gives me a 405, where I would have expected it to give me a 200. If you remove the
allowMethod
call, I would instead expect a 403, but again, 405. If you uncomment the
options
handler, then it works fine. In creating this reproduction snippet I now noticed that installing the CORS feature globally, instead of inside the routing, it works properly even without the additional
options
handler. However, in our production app that's not a solution for us because we do need different CORS handling depending on the route and installing CORS both globally as well as into a specific route throws an error on startup that this is not a supported use case.
By setting breakpoints inside of the CORS plugin's code I was able to tell that in the cases where I'm getting the 405s, the CORS plugin is never called and doesn't have any chance of handling the call. That's why I'm thinking the problem is not the CORS plugin itself - maybe it's its installation logic or some other part preventing it from running.
a

Aleksei Tirman [JB]

06/21/2022, 9:00 AM
Thank you for explanation. I’ve created an issue to address this problem.
r

robin

06/21/2022, 9:12 AM
Thank you!
a

Aleksei Tirman [JB]

06/21/2022, 9:18 AM
You can install the
CORS
plugin into an application instead of a routing to solve your problem.
The same as
CallLogging
r

robin

06/21/2022, 9:19 AM
Yeah, I did figure out that that solves the issue in the reproduction example, but we do need different CORS handling for different routes in our production app, so that doesn't work for us. The workaround with specifying the catch-all
options
handler works well enough for us for now though until this is fixed, we'll keep a close eye on if something doesn't work right with that.
38 Views