Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KTOR-6182 Support throwing UnknownServiceException if there is no cleartext traffic permitted #3725

Open
wants to merge 13 commits into
base: main
Choose a base branch
from

Conversation

Copy link
Contributor

@rsinukov rsinukov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR.

Can you explain what is the current behaviour and what is the benefit of this change, please?

@rsinukov rsinukov self-assigned this Aug 15, 2023
@Goooler
Copy link
Author

Goooler commented Aug 15, 2023

If we are using CIO to request an HTTP URL on Android 9+, there is no exception thrown (an example here), but thrown with the use of OkHttp or Android engine, cause the isCleartextTrafficPermitted check had been implemented by them, like this, this is a suggested behavior on Android:

This flag is honored on a best effort basis because it's impossible to prevent all cleartext traffic from Android applications given the level of access provided to them.

We should catch up what Okhttp did on Android, to provide the same experience.

Comment on lines 203 to 207
if (!Platform.isCleartextTrafficPermitted(host)) {
throw UnknownServiceException(
"CLEARTEXT communication to $host not permitted by network security policy"
)
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not very sure if we should put this check here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can do it earlier, before creating connection. Maybe even in the CIOEngine.executemethod. Also, don't you need to check that the traffic is clear text?

Copy link
Author

@Goooler Goooler Aug 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we can, but I see this check had been seated at a simular position in Okhttp, not very sure why.

@yschimke Can you take a look?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know enough about CIO to comment, is this HTTP only here? Or could it be HTTPS also?

Probably worth adding tests to validate the behaviour for both cases.

Some extra context for OkHttp. We position it

  • After application interceptors have a chance to modify the request, in case they have an AlwaysSecureInterceptor configured.
  • Before we connect via various options - proxies, or IPv4/IPv6 fallback, which might involve connecting to an IP instead of a host. So we avoid working around the platform restrictions.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could be HTTP or HTTPS here. Thanks for your explanation, especially for the second point.

Comment on lines 203 to 207
if (!Platform.isCleartextTrafficPermitted(host)) {
throw UnknownServiceException(
"CLEARTEXT communication to $host not permitted by network security policy"
)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can do it earlier, before creating connection. Maybe even in the CIOEngine.executemethod. Also, don't you need to check that the traffic is clear text?

*/
private val isAndroid: Boolean = System.getProperty("java.vm.name") == "Dalvik"

actual fun isCleartextTrafficPermitted(hostname: String): Boolean {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These aren't the full story for OkHttp.

We compile against robolectric android-all in order to call these APIs directly.

https://github.com/square/okhttp/blob/okhttp_4x/okhttp/src/main/kotlin/okhttp3/internal/platform/AndroidPlatform.kt

It's way simpler than the reflection.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I borrowed these code from the old implementation, cause I see there is no Android Runtime applied in CIO module.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for OkHttp. It's a compile only dependency

Copy link
Member

@e5l e5l left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @Goooler, thank you for the PR.

It looks like this code is Android specific, it also could be helpful not only for CIO.
Could you please extract this check to the separate client plugin?

@Goooler
Copy link
Author

Goooler commented Jan 12, 2024

What do you mean? You mean I should move Platform into a new module under ktor-client/ktor-client-plugins?

@e5l
Copy link
Member

e5l commented Jan 12, 2024

Yep! We also can install this plugin by default in case the VM is Dalvik

import io.ktor.client.request.host
import io.ktor.utils.io.errors.UnknownServiceException

public val Platforming: ClientPlugin<Any> = createClientPlugin("Platforming", ::Any) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can I install this plugin for CIO by default?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's make this in the separate PR, it looks like it require service loader or some similar mechanism


package io.ktor.client.plugins.platform

internal actual object Platform {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems nonJvm source set hasn't been recognized, how can I implement this actual object for non-jvm platforms?

image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @Goooler, it will need to be implemented for js and posix source sets.

To check the implementation in ide, you can invert the flag ktor.ide.jvmAndCommonOnly in gradle.properties https://github.com/ktorio/ktor/blob/92ece71d433f6d96754ef7d18ab2691e1f36160a/gradle.properties#L7C1-L7C1 and import project again

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Goooler
Copy link
Author

Goooler commented Jan 17, 2024

Seems the style check failure is not related to my change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants