acoconut
10/25/2021, 3:14 PMStefan Oltmann
10/26/2021, 10:13 AMMartin Rajniak
10/26/2021, 2:50 PMrnett
10/26/2021, 5:46 PMAnton Afanasev
10/26/2021, 6:53 PMktor 2.0
going to have support for multiplatform websockets (Client side)?Stephen Edwards
10/26/2021, 8:54 PMdarkmoon_uk
10/26/2021, 10:47 PMCharles Jo
10/27/2021, 1:04 AMhhariri
10/27/2021, 8:46 AMhttps://www.youtube.com/watch?v=mye9NjvxVSU&t=1s▾
ESchouten
10/27/2021, 12:11 PMJacopo Franzoi
10/27/2021, 8:41 PMNikolay Lebedev
10/27/2021, 9:37 PMembeddedServer
in my main function.
I guess I have to use a coroutine for regular database queries, but how do I then pass results to websocket connections?Alex Styl
10/28/2021, 10:40 AMSlackbot
10/28/2021, 10:47 AMnilTheDev
10/28/2021, 10:51 AMget("/{...}") {
val startTime = System.currentTimeMillis()
// this is a suspend function
call.respondHandlingLongCalculation(random, delayProvider, startTime)
}
In this code, call.respondHandlingLongCalculation(...)
is a suspend function and it is called without using any launch
block. It seems that there is always a coroutine scope lying around in the ktor application.
Q1. How can I use coroutines in a ktor application? If there is an implicit scope then what are the best practices to use it? Is there any guidelines?
Q2. The scope of this question is little bit broad. How exactly a ktor server handle client connections? Does it launch a whole new instance of the application in a coroutine and it lives until the connection is closed? If not then how it manages concurrent connections? Let's say user A and user B connected to the server simultaneously. Though servers should be as stateless as possible but suppose I made a list of something for serving user A. Something like that,
// this is a global variable
var list: List<Int>? = null
fun ... (){
get("..."){
call.respond(list)
}
}
Is there any chance that user A's data can also get served to user B if they hit the same endpoint?albrechtroehm
10/29/2021, 3:32 PMfun main() {
val server = GrpcServer(50051)
server.start()
// server.blockUntilShutdown()
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
installPlugins()
configureModules()
}.start(wait = true)
}
nilTheDev
10/30/2021, 8:34 AMcall.receiveOrNull<T>()
isn't working as expected. This is how you can reproduce,
1. Install the ContentNegotiation
plugin for automatic deserialization of json
content. (I am using kotlinx.serialization
)
2. Define a model for the expected data
3. Send request with missing json field
4. Instead of returning null, it's throwing the exception and causing a 500 internal server error
Code,
fun Application.module() {
install(ContentNegotiation) {
json(
Json { ignoreUnknownKeys = true }
)
}
routing {
post("/") {
val user = call.receiveOrNull<User>()
?: return@post call.respondText(
text = "invalid request",
status = HttpStatusCode.BadRequest
)
call.respondText(text = "valid request", status = HttpStatusCode.Accepted)
}
}
}
Model,
@Serializable
data class User(
@SerialName("username")
val userName: String,
val password: String
)
Sent request,
POST / HTTP/1.1
Accept: application/json, */*;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 24
Content-Type: application/json
Host: localhost:8080
User-Agent: HTTPie/2.5.0
{
"username": "nilanjan"
}
Received response,
HTTP/1.1 500 Internal Server Error
Connection: keep-alive
Content-Length: 0
Exception thrown in the server,
kotlinx.serialization.MissingFieldException: Field 'password' is required for type with serial name 'com.jetbrains.handson.httpapi.User'
As a workaround I had to define my own receiveOrNull
extension function.
suspend inline fun <reified T : Any> ApplicationCall.receiveOrNull(): T? {
return try {
receive<T>()
} catch (cause: Exception) {
null
}
}
The actual receiveOrNull
function in the io.ktor.request
package looks like that,
public suspend fun <T : Any> ApplicationCall.receiveOrNull(type: KType): T? {
return try {
receive<T>(type)
} catch (cause: ContentTransformationException) {
application.log.debug("Conversion failed, null returned", cause)
null
}
}
Apparently, it's only catching the ContentTransformationException
nothing else.
This issue in YourTrack is similar to my issue,
https://youtrack.jetbrains.com/issue/KTOR-545
So, my questions are,
1. Is this how the receiveOrNull
function supposed to work? Or it is a bug? Do I have to report it?
2. If it's not a bug then why it is designed this way? Isn't it a good design to catch all kind of errors related to deserialization in the receiveOrNull
function rather than causing an internal server error?Andreas Scheja
10/30/2021, 3:02 PMMockEngine
? I want to test my client which is building a MultiPartFormDataContent
using the formData
builder (which by the way seems to lack support for appending files?).
Now my problem is that the request body in the MockEngine
is just a MultiPartFormDataContent
only exposing properties of OutgoingContent.WriteChannelContent
, which means I can only
get the full body with all boundary strings and headers written in it. My current
workaround is to more or less split the full body into somehow manageable parts, sadly
all methods I could find for parsing multipart/form-data
in ktor are either internal
already or marked as deprecated as they will become internal
in the future.
Am I overlooking something or is ktor's api just lacking in this area as of now?Rescribet
10/30/2021, 8:39 PMUrl
marked as @Serializable
OOTB (where it’s representation would be the url string).
On a different note, thinking of Url
as a (kotlinx) package might be neat too, so the ecosystem can center around a multiplatform type rather than existing platform-specific implementationsdarkmoon_uk
10/31/2021, 1:21 PMmbonnin
10/31/2021, 9:36 PM2.0.0-eap-259
?darkmoon_uk
11/01/2021, 3:22 AMHttpClient
instances on hand, and pick one? Or, is there a lighter way to opt to use the installed authentication, or not, per call?Edoardo Luppi
11/01/2021, 10:41 AMmaxmello
11/01/2021, 11:13 AMAlexander Suraphel
11/01/2021, 3:42 PMRescribet
11/01/2021, 4:50 PMMd Hanif
11/01/2021, 5:12 PMAuth
module in my kmm.
Now when i add auth module in my http client it gets added successfully and whenever i receive UnAuthorized user error from any api it calls my refresh token api. The issue is even though it calls my refresh token api but on success of refresh token it does not call the other api from which I have received UnAuthorized user error.
It works as expected in android but the only issue is in ios client.
Expected (Works fine in Android Http client) :-
- Call any api -> if received any UnAuthorized user error call refresh token api -> onSuccess of refresh token api -> Call the first api again with the updated refresh token.
Issue i am facing :-
- Call any api -> if received any UnAuthorized user error call refresh token api -> onSuccess of refresh token api -> Does nothing on this step just throws unauthorized user error from the first api.
HttpClient for iOS :-
actual class HttpBaseClient {
actual val tokenClient = HttpClient {
defaultRequest {
host = ApiEndPoints.Base.url
url {
protocol = URLProtocol.HTTPS
}
contentType(ContentType.Application.Json)
header(CONNECTION, CLOSE)
}
install(JsonFeature) {
val json = kotlinx.serialization.json.Json {
ignoreUnknownKeys = true
coerceInputValues = true
}
serializer = KotlinxSerializer(json)
}
}
actual val httpClient: HttpClient = HttpClient {
defaultRequest {
host = ApiEndPoints.Base.url
url {
protocol = URLProtocol.HTTPS
}
contentType(ContentType.Application.Json)
header(CONNECTION, CLOSE)
}
// Validate Response
expectSuccess = false
// Install Auth
install(Auth) {
lateinit var refreshTokenInfo : LoginResponse
bearer {
refreshTokens { unauthorizedResponse: HttpResponse ->
NSLog("Unauthorized response received")
BaseAPIClass().refreshAuthToken().fold(
failed = {
// On Failed
NSLog("Token Failed"). // No Callback received here
},
succeeded = { response ->
refreshTokenInfo = response
NSLog("Token Updated") // No Callback received here even when api is success
}
)
BearerTokens(
accessToken = refreshTokenInfo.accessToken ?: "",
refreshToken = refreshTokenInfo.refreshToken ?: ""
)
}
}
}
// JSON Deserializer
install(JsonFeature) {
val json = kotlinx.serialization.json.Json {
ignoreUnknownKeys = true
coerceInputValues = true
}
serializer = KotlinxSerializer(json)
}
Android Client (Pretty much same):-
actual class HttpBaseClient {
actual val tokenClient = HttpClient {
defaultRequest {
host = ApiEndPoints.Base.url
url {
protocol = URLProtocol.HTTPS
}
contentType(ContentType.Application.Json)
header(CONNECTION, CLOSE)
}
install(JsonFeature) {
val json = kotlinx.serialization.json.Json {
ignoreUnknownKeys = true
coerceInputValues = true
}
serializer = KotlinxSerializer(json)
}
install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.ALL
}
}
actual val httpClient: HttpClient = HttpClient {
defaultRequest {
host = ApiEndPoints.Base.url
url {
protocol = URLProtocol.HTTPS
}
contentType(ContentType.Application.Json)
header(CONNECTION, CLOSE)
}
// Validate Response
expectSuccess = false
//Authentication
install(Auth) {
lateinit var refreshTokenInfo : LoginResponse
bearer {
refreshTokens { unauthorizedResponse: HttpResponse ->
BaseAPIClass().refreshAuthToken().fold(
failed = {
// On Failed
},
succeeded = { response ->
refreshTokenInfo = response
}
)
BearerTokens(
accessToken = refreshTokenInfo.accessToken ?: "",
refreshToken = refreshTokenInfo.refreshToken ?: ""
)
}
}
}
Ktor version :- 1.6.2 (tried 1.6.4 as well after reading this :- https://youtrack.jetbrains.com/issue/KTOR-491 but didn't work)Dave Leeds
11/01/2021, 9:37 PMHttpClient
alive. I’m using the Ktor client inside a REST service, and that client will be used to call out to other services. A few options for lifetimes might include:
• Create one Ktor client instance for the lifetime of the service instance. All outbound requests will be made through that one client instance (across all inbound request threads) until the service instance is shut down, at which point we would call close()
on the Ktor client.
• Create one Ktor client instance for the lifetime of the inbound HTTP request. In this case, I’d call close()
after we’re done processing the inbound request.
• Create one Ktor client instance for each outbound HTTP request that we need to make. In this case, we’d call use { }
in order to make sure the instance was properly closed once the outbound request has been completed.
What do you recommend, and why do you recommend it? Thanks!dave08
11/02/2021, 10:00 AMNikolay Kasyanov
11/02/2021, 1:59 PMNikolay Kasyanov
11/02/2021, 1:59 PMBig Chungus
11/02/2021, 3:45 PM