KNETTimeoutConfig pozwala na granularną kontrolę timeoutów
dla różnych faz połączenia HTTP - connect, read, write i całkowity czas operacji.
import rip.nerd.kitsunenet.util.KNETTimeoutConfig
import rip.nerd.kitsunenet.util.withTimeout
import rip.nerd.kitsunenet.util.KNETTimeoutHelper
// Prosty timeout (wszystkie wartości takie same)
val timeout = KNETTimeoutConfig.simple(30_000) // 30 sekund
// Granularne timeouty
val timeout = KNETTimeoutConfig(
connectMs = 10_000, // 10s na nawiązanie połączenia
readMs = 60_000, // 60s na odczyt danych
writeMs = 120_000, // 120s na zapis (upload)
callMs = 300_000 // 5min całkowity czas operacji
)
// Użyj z requestem
val request = KNETRequest.get("https://api.example.com/data")
.withTimeout(timeout)
val response = client.request(request)
| Timeout | Opis | Typowa wartość |
|---|---|---|
connectMs |
Czas na nawiązanie połączenia TCP + TLS handshake | 10-30 sekund |
readMs |
Czas między pakietami danych przy odczycie | 30-60 sekund (lub 0 dla streaming) |
writeMs |
Czas między pakietami danych przy zapisie | 30-120 sekund (zależnie od uploadu) |
callMs |
Całkowity czas operacji od początku do końca | Zależnie od operacji (lub null = brak limitu) |
readMs to timeout między pakietami - jeśli dane nadchodzą ciągle,
nie zostanie przekroczony nawet przy długim transferze.
callMs to całkowity czas operacji - niezależnie od przepływu danych.
// Domyślny (30s wszystko)
val timeout = KNETTimeoutConfig.default()
// Szybkie API (krótkie timeouty)
val timeout = KNETTimeoutConfig.quick()
// Upload dużych plików
val timeout = KNETTimeoutConfig.forUpload()
// Download dużych plików
val timeout = KNETTimeoutConfig.forDownload()
// Streaming (nieograniczony read)
val timeout = KNETTimeoutConfig.forStreaming()
// WebSocket
val timeout = KNETTimeoutConfig.forWebSocket()
// Wolne API
val timeout = KNETTimeoutConfig.forSlowApi()
// Zoptymalizowane dla mobile
val timeout = KNETTimeoutConfig.forMobile()
// Brak timeoutów (ostrożnie!)
val timeout = KNETTimeoutConfig.none()
| Preset | Connect | Read | Write | Call | Użycie |
|---|---|---|---|---|---|
default() |
30s | 30s | 30s | - | Domyślne API |
quick() |
5s | 10s | 10s | 15s | Szybkie API |
forUpload() |
30s | 30s | 5min | 10min | Upload plików |
forDownload() |
30s | 5min | 30s | 10min | Download plików |
forStreaming() |
30s | ∞ | 30s | ∞ | SSE, streaming |
forWebSocket() |
15s | ∞ | 30s | ∞ | WebSocket |
forSlowApi() |
60s | 2min | 60s | 5min | Wolne serwery |
forMobile() |
15s | 30s | 30s | 60s | Oszczędność baterii |
KNETTimeoutConfigdata class KNETTimeoutConfig(
val connectMs: Long = 30_000,
val readMs: Long = 30_000,
val writeMs: Long = 30_000,
val callMs: Long? = null // null = brak limitu
) {
val totalMaxMs: Long // Maksymalny czas (callMs lub suma)
fun createOkHttpClient(): OkHttpClient
fun applyTo(client: OkHttpClient): OkHttpClient
}
withTimeout() ExtensionExtension function dla KNETRequest.
fun KNETRequest.withTimeout(config: KNETTimeoutConfig): KNETRequest
val request = KNETRequest.get("https://api.example.com/data")
.withTimeout(KNETTimeoutConfig.quick())
// Lub inline
val response = client.request(
KNETRequest.get(url).withTimeout(KNETTimeoutConfig.forDownload())
)
KNETTimeoutHelperHelpery do pracy z timeoutami.
calculateTimeout()Oblicza timeout na podstawie rozmiaru danych.
fun calculateTimeout(
dataSizeBytes: Long,
bytesPerSecond: Long = 100_000, // ~100KB/s
minTimeoutMs: Long = 30_000,
maxTimeoutMs: Long = 600_000
): Long
val fileSize = 50 * 1024 * 1024L // 50 MB
val timeout = KNETTimeoutHelper.calculateTimeout(fileSize)
println("Obliczony timeout: ${timeout / 1000}s")
// Output: Obliczony timeout: 500s (dla 100KB/s)
forUploadSize() / forDownloadSize()Tworzy config dla określonego rozmiaru.
fun forUploadSize(sizeBytes: Long): KNETTimeoutConfig
fun forDownloadSize(sizeBytes: Long): KNETTimeoutConfig
// Upload pliku
val file = File("large_video.mp4")
val uploadTimeout = KNETTimeoutHelper.forUploadSize(file.length())
val request = uploadRequest.withTimeout(uploadTimeout)
val response = client.request(request)
// Download z Content-Length
val headResponse = client.head(downloadUrl)
val contentLength = headResponse.headers["Content-Length"]?.toLongOrNull() ?: 0
val downloadTimeout = KNETTimeoutHelper.forDownloadSize(contentLength)
val downloadRequest = KNETRequest.get(downloadUrl).withTimeout(downloadTimeout)
execute()Wykonuje request z określonym timeout config.
suspend fun execute(
request: KNETRequest,
config: KNETTimeoutConfig
): KNETResponse
createClient()Tworzy klienta z timeout config.
fun createClient(config: KNETTimeoutConfig): KNETClient
class FileUploader(private val client: KNETClient) {
suspend fun uploadFile(file: File, url: String): Boolean {
// Oblicz timeout na podstawie rozmiaru
val timeout = KNETTimeoutHelper.forUploadSize(file.length())
Log.d("Upload", "Plik: ${file.length() / 1024}KB, timeout: ${timeout.writeMs / 1000}s")
// Utwórz klienta z tym timeout
val uploadClient = KNETClient(okHttpClient = timeout.createOkHttpClient())
return try {
val response = uploadClient.post(url, mapOf(
"file" to file.readBytes().toBase64()
))
response.isSuccessful
} catch (e: SocketTimeoutException) {
Log.e("Upload", "Timeout - plik za duży lub połączenie za wolne")
false
}
}
}
class StreamingClient {
fun streamEvents(url: String, onEvent: (String) -> Unit) {
val timeout = KNETTimeoutConfig.forStreaming()
val client = KNETClient(okHttpClient = timeout.createOkHttpClient())
// Streaming nigdy nie timeout'uje na read
scope.launch {
KNETBufferedStreamingClient.sse(url)
.collect { event ->
onEvent(event.data)
}
}
}
}
class ApiClient {
private val defaultClient = KNETClient()
private val uploadClient = KNETClient(
okHttpClient = KNETTimeoutConfig.forUpload().createOkHttpClient()
)
private val quickClient = KNETClient(
okHttpClient = KNETTimeoutConfig.quick().createOkHttpClient()
)
// Szybkie operacje
suspend fun getUser(id: String): User {
return quickClient.get("$baseUrl/users/$id").json()
}
// Standardowe operacje
suspend fun createUser(user: User): User {
return defaultClient.post("$baseUrl/users", user.toMap()).json()
}
// Upload
suspend fun uploadAvatar(userId: String, image: ByteArray): Boolean {
return uploadClient.post(
"$baseUrl/users/$userId/avatar",
mapOf("image" to image.toBase64())
).isSuccessful
}
}
class AdaptiveClient(context: Context) {
private val connectionQuality = KNETConnectionQuality.Builder(context).build()
suspend fun request(request: KNETRequest): KNETResponse {
val quality = connectionQuality.currentQuality
// Dostosuj timeout do jakości połączenia
val timeout = when (quality.level) {
ConnectionLevel.EXCELLENT -> KNETTimeoutConfig.quick()
ConnectionLevel.GOOD -> KNETTimeoutConfig.default()
ConnectionLevel.FAIR -> KNETTimeoutConfig.forSlowApi()
ConnectionLevel.POOR -> KNETTimeoutConfig(
connectMs = 60_000,
readMs = 180_000,
writeMs = 180_000
)
ConnectionLevel.OFFLINE -> throw IOException("No connection")
}
val client = KNETClient(okHttpClient = timeout.createOkHttpClient())
return client.request(request)
}
}
try {
val response = client.request(request.withTimeout(KNETTimeoutConfig.quick()))
// Sukces
} catch (e: SocketTimeoutException) {
// Timeout na read/write
when {
e.message?.contains("connect") == true -> {
showError("Nie można połączyć z serwerem")
}
e.message?.contains("read") == true -> {
showError("Serwer nie odpowiada")
}
else -> {
showError("Przekroczono czas oczekiwania")
}
}
} catch (e: IOException) {
showError("Błąd sieci: ${e.message}")
}