ktor client multiplatform for raspberry pi? - raspberry-pi

Is there a chance to use ktor client (multiplatform) on a Raspberry Pi or is this platform not yet supported?
kotlin {
linuxArm32Hfp("native") {
binaries {
executable {
entryPoint = "main"
baseName = "example"
}
}
compilations.main.cinterops {
linuxCanInterop {
...
}
}
}
}
dependencies {
// ??
implementation("io.ktor:ktor-client-curl:1.4.1")
nativeMainImplementation("io.ktor:ktor-client-curl:1.4.1")
}

Raspberry Pi is not supported by Ktor now. The main reason is that kotlinx.coroutines
and kotlinx.serialization libraries don't support this platform too.

Related

KMM, after involving XCTest, get build error "Building for iOS Simulator, but linking in dylib built for iOS..."

I did an experiment to add iOS UI test to project that was created with Kotlin Multiplatform Mobile(KMM). By start to follow the official guide, I was able to connect shared library in Xcode and launch the iOS app or perform a Unit test from Android Studio. But when I try to involve XCTest to add a few UI test, the Xcode complaint as below screenshot.
I have searched out the internet a lot, still without luck. Guys, if you are face the same issue before, please give me some hint about how to tracking down this arch problem.
From the build log error, I think first Gradle Task :shared:linkDebugFrameworkIosSimulatorArm64 FAILED and below it said XCTest is built for iOS arm64 arch, which is not aligned with iOS Simulator.
I'm using a Mac M1 machine, it could be the reason. So I switch Xcode to Rosetta mode, this time command embedAndSignAppleFrameworkForXcode which is from Run Script has NO-SOURCE and followed one iOS Simulator version alignment complain.
XCTest.def
language = Objective-C
package = platform.XCTest
depends = UIKit
modules = XCTest
linkerOpts= -weak_framework XCTest -L/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework -F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Frameworks/
compilerOpts= -weak_framework XCTest -F/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks/
build.gradle file
import com.android.build.gradle.internal.scope.ProjectInfo.Companion.getBaseName
plugins {
kotlin("multiplatform")
id("com.android.library")
}
kotlin {
android {
}
listOf(
iosArm64(),
iosSimulatorArm64()
).forEach {
it.binaries.framework {
baseName = "shared"
embedBitcode = org.jetbrains.kotlin.gradle.plugin.mpp.Framework.BitcodeEmbeddingMode.DISABLE
}
it.compilations.getByName("main") {
val xctest by cinterops.creating {
// Def-file describing the native API.
defFile(project.file("src/iosMain/xctest.def"))
}
}
}
sourceSets {
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-common")
// implementation(
// "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:1.3.5-native-mt"
// )
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
val androidMain by getting {
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.5-native-mt")
implementation("androidx.test.espresso:espresso-core:3.2.0")
implementation("androidx.test.espresso:espresso-contrib:3.2.0")
implementation("androidx.test:core:1.4.0")
implementation("androidx.test.ext:junit:1.1.3")
implementation("androidx.test.uiautomator:uiautomator:2.2.0")
}
}
val androidTest by getting {
dependencies {
}
}
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
val iosMain by creating {
dependsOn(commonMain)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
}
val iosArm64Test by getting
val iosSimulatorArm64Test by getting
val iosTest by creating {
dependsOn(commonTest)
iosArm64Test.dependsOn(this)
iosSimulatorArm64Test.dependsOn(this)
}
}
}
android {
namespace = "com.bsc.radiant_hope_test"
compileSdk = 32
defaultConfig {
minSdk = 21
targetSdk = 32
}
}
Shared library is connected.
You don't need to use Rosetta in this case
As I see in your XCTest.def file, you are trying to link all binaries created with Kotlin tooling with only iPhoneOS.platform which doesn't contain needed parts for running simulator.
Please try to add the following lines to your XCTest.def file
#Linker options for devices
linkerOpts.ios_arm64 = -weak_framework XCTest -L/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework -F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Frameworks/
#Linker options for simulators
linkerOpts.ios_x64 = -weak_framework XCTest -L/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/UIKit.framework -F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Frameworks/
linkerOpts.ios_simulator_arm64 = -weak_framework XCTest -L/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/UIKit.framework -F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Frameworks/

How to call mqtt client in background service flutter

My project uses mqtt for notifications. And need real time notification.
I have mqtt class like this
class Mqtt {
...
Mqtt._() {
mqttClient = MqttServerClient.withPort(server, clientIdentifier, port);
///Successful connection callback
mqttClient.onConnected = _onConnected;
///Disconnected callback
mqttClient.onDisconnected = _onDisconnected;
///Subscription success callback
mqttClient.onSubscribed = _onSubscribed;
///Subscription failure callback
mqttClient.onSubscribeFail = _onSubscribeFail;
}
static Mqtt getInstance() {
if (_instance == null) {
_instance = Mqtt._();
}
return _instance!;
}
///connection
connect() {
mqttClient.connect();
_log("connecting");
}
...
}
And I call like this
Mqtt mqtt = Mqtt.getInstance();
mqtt.subTopic = 'NOTIFICATION/';
mqtt.connect();
But mqtt disconnects when app is stopped.
How to call mqtt client in background service flutter?
Check flutter foreground service it might help you.

embedding golang server with flutter

I have web server written in golang which uses graphql package gqlgen and gorm for database.
Since golang can be compiled and run on android I wanted to create offline version of my app where sqlite can be used for offline storage and import my whole server as an aar.
I have successfully built aar and add it on my flutter using gomobile by following instructions here
When I run my app, server is started on android it seems to work just fine and when opening http://localhost:8080/ on emulator's chrome app I am seeing graphql playground runs without any problem just like I see on browser in windows.
The only problem I face is that flutter app just shows a blank screen while server runs in background. The following are the logs printed when app is started
Launching lib\main.dart on sdk gphone64 x86 64 in debug mode...
Running Gradle task 'assembleDebug'...
√ Built build\app\outputs\flutter-apk\app-debug.apk.
Installing build\app\outputs\flutter-apk\app.apk...
Debug service listening on ws://127.0.0.1:62561/DyGpOhyuekw=/ws
Syncing files to device sdk gphone64 x86 64...
I/GoLog ( 6295): connect to http://localhost:8080/ for GraphQL playground
W/ux.offline( 6295): type=1400 audit(0.0:38): avc: denied { read } for name="somaxconn" dev="proc" ino=74990 scontext=u:r:untrusted_app:s0:c149,c256,c512,c768 tcontext=u:object_r:proc_net:s0 tclass=file permissive=0 app=com.nux.offline
I think maybe problem lies on the above logs avc: denied { read } for name="somaxconn" or something is causing the blocking of ui thread since its like flutter don't render a thing.
I am using flutter plugin to start server and this is ServerPlugin.kt
package com.mahesabu.server.server
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import lib.Lib.startServer
/** ServerPlugin */
class ServerPlugin : FlutterPlugin, MethodCallHandler {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private lateinit var channel: MethodChannel
override fun onAttachedToEngine(#NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "server")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(#NonNull call: MethodCall, #NonNull result: Result) {
if (call.method == "startServer") {
try {
val port = startServer() //from golang bindings
result.success(port)
} catch (e: Exception) {
e.printStackTrace();
result.error("Error in starting server", "${e.message}", null);
}
} else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(#NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}
And this is is dart code
class Server {
static const MethodChannel _channel = MethodChannel('server');
static Future<String?> startServer() async {
try {
final String? port = await _channel.invokeMethod('startServer');
return port;
} catch (e) {
log('startServer error: ${e.toString()}');
return null;
}
}
}
and my app's main is as follows
import 'package:flutter/material.dart';
import 'package:server/server.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final port = await Server.startServer(); //problem
print('port $port'); //This don't print
runApp(const MyApp());
}
On go side this how i start server
//start.go
package util
import (
"github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/playground"
"log"
"my-project/graph"
"my-project/graph/generated"
"net/http"
"os"
)
const defaultPort = "8080"
// StartServer This way so that it can be invoked via libs
func StartServer(Offline bool) string {
port := os.Getenv("PORT")
if port == "" {
port = defaultPort
}
db := InitDB(Offline)
config := generated.Config{Resolvers: &graph.Resolver{
DB: db,
}}
srv := handler.NewDefaultServer(generated.NewExecutableSchema(config))
http.Handle("/", playground.Handler("GraphQL playground", "/query"))
http.Handle("/query", srv)
log.Printf("connect to http://localhost:%s/ for GraphQL playground", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
return port
}
and this is lib for generating bindings with gomobile
//lib.go
package lib
import "my-project/util"
// StartServer This way so that it can be invoked via libs
func StartServer() string {
return util.StartServer(true)
}
Any help on fixing this will be appreciated.
Edit
I think problem occurs when embedded server tries to create a new port. I don't know if it is possible for an app to open a new port in android just like nodejs, golang open things like http://localhost:8080.
Now I think if there is a way to create port then I can run my app successfully but I don't know how exactly.
I was thinking if I can find any available port on android and use to start server maybe this stack could be possible. In kotlin something like this may work in finding port.
import java.net.ServerSocket
fun main() {
val serverPort = ServerSocket(0)
print(serverPort.toString())
}
but it crashes on android app when I try similar thing.
I have uploaded a repository on GitHub showing what I intend to do. It's just a simple golang server using gin and android app (with no flutter) it is available here.
"I don't know if it is possible for an app to open a new port in android just like nodejs, golang open things like localhost:8080"
To find out the root cause, try to run an HTTP server in android, such as How to create a HTTP server in Android?. If that succeeds, try to find the differences about how they deal with ports.
In addition, please be sure you have correct permission in androidmanifest.xml.
(Rephrased from my comments)

Ktor server app keep increasing open connections

Hi i recently deploy ktor server project on server as rest api backend for my app using. I'm using netty and running it on server with system service. Ktor server is running on port 7171 and whenever i check connections with port 7171 it keep increasing. I'm checking with this command
ss -ant | grep :7171 | wc -l
After one day connection numbers 20k+ and server crash nothing work.
I think some connections keep open. IN logs i don't get any error except few errors like connection reset by peer.
I'm also using HttpClient with Apache and for caching list of data I'm storing data in companion object so not fetching it every time from database.
I reviewed code and have only doubt about above two things.
These are my gradle dependencies
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version")
implementation("io.ktor:ktor-server-netty:$ktor_version")
implementation("io.ktor:ktor-client-apache:$ktor_version")
implementation("io.ktor:ktor-client-logging-native:$ktor_version")
implementation("io.ktor:ktor-gson:$ktor_version")
implementation("ch.qos.logback:logback-classic:$logback_version")
implementation("io.ktor:ktor-metrics:$ktor_version")
implementation("io.ktor:ktor-server-core:$ktor_version")
implementation("io.ktor:ktor-server-sessions:$ktor_version")
implementation("io.ktor:ktor-auth-jwt:$ktor_version")
implementation("org.jooq:jooq")
jooqGeneratorRuntime("mysql:mysql-connector-java:8.0.19")
implementation("mysql:mysql-connector-java:8.0.19")
implementation(group = "com.zaxxer", name = "HikariCP", version = "3.4.2")
implementation("io.sentry:sentry:1.7.30")
implementation("software.amazon.awssdk:s3:2.8.7")
Currently i have about 7k users and max concurrent users are 450.
Please guide me how i can check issue and figure out problem.
Here is HttClient code:
suspend fun post(
url: String,
params: Map<String, String> = emptyMap(),
headersMap: Map<String, String> = emptyMap()
): Result<String> {
val httpClient = getHttpClient()
return kotlin.runCatching {
httpClient.post<String>(url) {
body = MultiPartFormDataContent(
formData {
params.forEach {
append(it.key, it.value)
}
}
)
if (headersMap.isNotEmpty()) {
headersMap.forEach { (key, value) ->
header(key, value)
}
}
}.also {
httpClient.close()
}
}.onFailure { httpClient.close() }
}
private fun getHttpClient(): HttpClient {
return HttpClient(Apache) {
install(HttpTimeout) {
requestTimeoutMillis = 60000
}
engine {
customizeClient {
sslContext = SSLContextBuilder.create().loadTrustMaterial(object : TrustStrategy {
override fun isTrusted(chain: Array<out X509Certificate>?, authType: String?): Boolean {
return true
}
}).build()
setSSLHostnameVerifier(NoopHostnameVerifier())
}
}
}
}
Also please check my api response header i think keepalive should have some expiry?

How can I determine which platform is running?

How can I determine which device is running?
My app runs well on Windows, Linux and OSX. but doesn't on CrOS(Chromebook).
So I'd like to sperate code like a..
> if (chrome.systemInfo.platform == CrOS) {
> // code only for CrOs }
Is there anyway to do this way? anyway to get the information of platform?
ChromeOS has the navigator.userAgent of "CrOS". So the base on the fact, you could have a conditional statement like...:
if (navigator.userAgent.indexOf('CrOS') != -1) {
console.log('ChromeOS');
} else {
console.log('Other platform');
}