Skip to content

feature: implement proxy connectivity testing and improve Android sys…#405

Merged
rainxchzed merged 2 commits intomainfrom
proxy-tester-impl
Apr 10, 2026
Merged

feature: implement proxy connectivity testing and improve Android sys…#405
rainxchzed merged 2 commits intomainfrom
proxy-tester-impl

Conversation

@rainxchzed
Copy link
Copy Markdown
Member

@rainxchzed rainxchzed commented Apr 10, 2026

…tem proxy resolution

  • Add ProxyTester interface and ProxyTesterImpl to verify network reachability through a configured proxy using the GitHub API.
  • Implement a "Test" button in the network settings UI to trigger proxy validation and display latency or specific error messages (DNS, timeout, auth, etc.).
  • Add localized strings for proxy test outcomes in English, Arabic, Bengali, Spanish, French, Hindi, Italian, Japanese, Korean, Polish, Russian, Turkish, and Simplified Chinese.
  • Improve Android system proxy support by explicitly resolving https.proxyHost and http.proxyHost properties, ensuring traffic (including downloads) correctly follows device-level proxy settings.
  • Update TweaksViewModel and TweaksState to manage proxy testing logic and loading states.

Summary by CodeRabbit

  • New Features

    • Added proxy connectivity testing: a "Test" button shows progress, reports latency on success, and displays error messages.
    • Result feedback surfaced via snackbars; test prevents overlapping runs and disables Save while running.
    • Localized proxy test messages added for many languages.
  • Bug Fixes

    • Improved Android system proxy detection and handling for more reliable proxy behavior.

…tem proxy resolution

- Add `ProxyTester` interface and `ProxyTesterImpl` to verify network reachability through a configured proxy using the GitHub API.
- Implement a "Test" button in the network settings UI to trigger proxy validation and display latency or specific error messages (DNS, timeout, auth, etc.).
- Add localized strings for proxy test outcomes in English, Arabic, Bengali, Spanish, French, Hindi, Italian, Japanese, Korean, Polish, Russian, Turkish, and Simplified Chinese.
- Improve Android system proxy support by explicitly resolving `https.proxyHost` and `http.proxyHost` properties, ensuring traffic (including downloads) correctly follows device-level proxy settings.
- Update `TweaksViewModel` and `TweaksState` to manage proxy testing logic and loading states.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 10, 2026

Walkthrough

Added a proxy connectivity test feature: domain interface and outcome types, data-layer tester implementation and Android proxy resolution, DI binding, localized strings (13 locales), and Tweaks UI/VM/state/event wiring including a test button and result snackbars.

Changes

Cohort / File(s) Summary
Configuration
\.claude/settings.local.json
Expanded allowed local commands to include Gradle wrapper tasks (:composeApp:assembleDebug, :composeApp:jvmJar, combined invocation).
Domain
core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/network/ProxyTester.kt
Added ProxyTester interface and ProxyTestOutcome sealed hierarchy (Success with latency; Failure cases: DnsFailure, ProxyUnreachable, Timeout, ProxyAuthRequired, UnexpectedResponse, Unknown).
Data — Implementation
core/data/src/commonMain/kotlin/zed/rainxch/core/data/network/ProxyTesterImpl.kt
New ProxyTesterImpl using Ktor client, 8s timeouts, latency measurement, exception-to-outcome mapping, and ensured client close.
Data — DI
core/data/src/commonMain/kotlin/.../SharedModule.kt
Registered single<ProxyTester> { ProxyTesterImpl() } in Koin module.
Android HTTP Client
core/data/src/androidMain/kotlin/.../HttpClientFactory.android.kt, core/data/src/androidMain/kotlin/.../AndroidDownloader.kt
Replaced ProxySelector.getDefault() usage with resolveAndroidSystemProxy() that reads https/http proxyHost/port system properties and applies resulting Proxy to OkHttp client.
Presentation — Strings (13 locales)
core/presentation/src/commonMain/composeResources/values.../strings-*.xml
Added proxy test UI strings (action, in-progress, success with %1$d ms, and multiple error messages) across 13 locales (ar, bn, zh-rCN, es, fr, hi, it, ja, ko, pl, ru, tr, default/EN).
Tweaks — Actions/Events/State
feature/tweaks/.../TweaksAction.kt, TweaksEvent.kt, TweaksState.kt
Added OnProxyTest action, OnProxyTestSuccess(latencyMs) and OnProxyTestError(message) events, and isProxyTestInProgress: Boolean state flag.
Tweaks — ViewModel Logic
feature/tweaks/.../TweaksViewModel.kt
Added proxy test handler: validates host/port, builds ProxyConfig, prevents concurrent tests, calls proxyTester.test(), maps outcomes to events, and emits localized error/success events; injected ProxyTester dependency.
Tweaks — UI / Root
feature/tweaks/.../TweaksRoot.kt, components/sections/Network.kt
Added ProxyTestButton composable, integrated progress UI, disabled Save while testing, and Root shows snackbars for test success/error events.

Sequence Diagram

sequenceDiagram
    actor User
    participant UI as "Network UI\n(Composable)"
    participant VM as "TweaksViewModel"
    participant Tester as "ProxyTester / ProxyTesterImpl"
    participant Client as "Ktor/OkHttp Client"
    participant Server as "Remote Server"

    User->>UI: Click "Proxy test"
    UI->>VM: dispatch OnProxyTest
    VM->>VM: validate host/port
    VM->>VM: build ProxyConfig, set isProxyTestInProgress=true
    VM->>Tester: test(config)
    Tester->>Client: create client with proxy + timeouts
    Client->>Server: GET test URL (via proxy)
    alt HTTP 200-299
        Server-->>Client: 2xx response
        Client-->>Tester: response received
        Tester->>Tester: measure latency
        Tester-->>VM: Success(latencyMs)
    else HTTP 407
        Server-->>Client: 407 Proxy Authentication Required
        Client-->>Tester: 407 response
        Tester-->>VM: Failure.ProxyAuthRequired
    else Network/Timeout/Error
        Client--xTester: exception (DNS/Connect/Timeout/IO)
        Tester->>Tester: map exception to Failure.*
        Tester-->>VM: Failure.<type>
    end
    VM->>VM: convert outcome -> event, set isProxyTestInProgress=false
    VM->>UI: emit OnProxyTestSuccess or OnProxyTestError
    UI->>User: show snackbar with result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐇 I hopped through hosts and ports to see,

eight thousand ms, a latency for me.
DNS and timeouts, I sniff and test,
in many tongues I help you rest.
A tiny hop, a network cheer—proxy results are here!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title describes the main feature—implementing proxy connectivity testing—but is truncated and incomplete, making full comprehension unclear. Complete the title to clarify what 'improve Android sys' refers to. Consider: 'feature: implement proxy connectivity testing and improve Android system proxy resolution'
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch proxy-tester-impl

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.claude/settings.local.json:
- Line 4: Replace the machine-specific absolute path in the command string
"Bash(grep -r \"\\.FLATPAK\\|Flatpak\\|flatpak\" D:/development/Github-Store
--include=*.kt)" with a repo-relative path (e.g., use "." or "${repoRoot}") so
the command becomes portable across environments; update the string in
.claude/settings.local.json to use the relative target and keep the rest of the
grep flags identical.

In
`@core/data/src/androidMain/kotlin/zed/rainxch/core/data/network/HttpClientFactory.android.kt`:
- Around line 84-93: The proxy port values (httpsPort/httpPort) are parsed with
toIntOrNull but not range-validated, so invalid ports (e.g. -1 or >65535) can
cause InetSocketAddress to throw; update the logic in
HttpClientFactory.android.kt around httpsHost/httpsPort and httpHost/httpPort to
accept the parsed Int only if it is within 0..65535 (e.g., check httpsPort in
0..65535 and httpPort in 0..65535 before returning Proxy(Proxy.Type.HTTP,
InetSocketAddress(...))), and only create/return the Proxy when both host is
non-blank and port is in that valid range.

In
`@core/data/src/commonMain/kotlin/zed/rainxch/core/data/network/ProxyTesterImpl.kt`:
- Around line 59-60: The catch block in ProxyTesterImpl.kt currently catches
Throwable and converts fatal errors into ProxyTestOutcome.Failure.Unknown;
change the catch to catch (e: Exception) so only recoverable exceptions are
caught and fatal errors (e.g., OutOfMemoryError, StackOverflowError) will
propagate; update the catch clause that returns ProxyTestOutcome.Failure.Unknown
to use Exception instead of Throwable and leave the rest of the handling for
ProxyTestOutcome.Failure.Unknown unchanged.
- Around line 11-14: ProxyTesterImpl currently imports JVM-only exceptions
(IOException, ConnectException, SocketTimeoutException, UnknownHostException) in
commonMain and uses them in its catch blocks; remove those java.* imports and
the JVM-specific catches from
core/data/src/commonMain/kotlin/zed/rainxch/core/data/network/ProxyTesterImpl.kt,
create an expect/actual mapping or platform-specific helper (e.g., expect fun
mapNetworkException(e: Throwable): ProxyTestResult or expect class
NetworkExceptionMapper with actual implementations in androidMain/jvmMain) and
replace the direct exception checks in the ProxyTesterImpl test method with a
call to that platform mapper (or use Ktor-native exception types if available)
so that platform-specific exception handling lives in actual implementations
while commonMain only depends on the abstract mapping API.

In
`@feature/tweaks/presentation/src/commonMain/kotlin/zed/rainxch/tweaks/presentation/TweaksViewModel.kt`:
- Around line 580-583: In TweaksViewModel, when handling
ProxyTestOutcome.Failure.Unknown, stop passing the platform-specific raw message
into TweaksEvent.OnProxyTestError; always use the localized fallback via
getString(Res.string.proxy_test_error_unknown) and, if you need diagnostics, log
the raw message separately (e.g., using logger.debug or processLogger) rather
than forwarding it to the UI. Ensure the branch that matches
ProxyTestOutcome.Failure.Unknown constructs the event with
getString(Res.string.proxy_test_error_unknown) only and move any use of the
original message into a non-UI log call.
- Around line 408-418: The OnProxyTest handler currently wraps the suspending
proxyTester.test(config) in runCatching which swallows CancellationException and
also fails to reliably reset isProxyTestInProgress; replace the runCatching
block inside viewModelScope.launch with an explicit try { val outcome =
proxyTester.test(config); _events.send(outcome.toEvent()) } catch (e:
CancellationException) { throw e } catch (t: Throwable) {
_events.send(ProxyTestOutcome.Failure.Unknown(t.message).toEvent()) } finally {
_state.update { it.copy(isProxyTestInProgress = false) } } (use the existing
symbols TweaksAction.OnProxyTest, buildProxyConfigForTest(),
proxyTester.test(config), _state and _events.send) so cancellation is propagated
and the in-progress flag is always cleared; optionally track/cancel the Job if
you need to prevent late results from earlier jobs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3d734487-86f9-4cf2-8753-7843c1b0220d

📥 Commits

Reviewing files that changed from the base of the PR and between 8f5198c and 0762481.

📒 Files selected for processing (27)
  • .claude/settings.local.json
  • composeApp/release/baselineProfiles/0/composeApp-release.dm
  • composeApp/release/baselineProfiles/1/composeApp-release.dm
  • core/data/src/androidMain/kotlin/zed/rainxch/core/data/network/HttpClientFactory.android.kt
  • core/data/src/androidMain/kotlin/zed/rainxch/core/data/services/AndroidDownloader.kt
  • core/data/src/commonMain/kotlin/zed/rainxch/core/data/di/SharedModule.kt
  • core/data/src/commonMain/kotlin/zed/rainxch/core/data/network/ProxyTesterImpl.kt
  • core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/network/ProxyTester.kt
  • core/presentation/src/commonMain/composeResources/values-ar/strings-ar.xml
  • core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml
  • core/presentation/src/commonMain/composeResources/values-es/strings-es.xml
  • core/presentation/src/commonMain/composeResources/values-fr/strings-fr.xml
  • core/presentation/src/commonMain/composeResources/values-hi/strings-hi.xml
  • core/presentation/src/commonMain/composeResources/values-it/strings-it.xml
  • core/presentation/src/commonMain/composeResources/values-ja/strings-ja.xml
  • core/presentation/src/commonMain/composeResources/values-ko/strings-ko.xml
  • core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml
  • core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml
  • core/presentation/src/commonMain/composeResources/values-tr/strings-tr.xml
  • core/presentation/src/commonMain/composeResources/values-zh-rCN/strings-zh-rCN.xml
  • core/presentation/src/commonMain/composeResources/values/strings.xml
  • feature/tweaks/presentation/src/commonMain/kotlin/zed/rainxch/tweaks/presentation/TweaksAction.kt
  • feature/tweaks/presentation/src/commonMain/kotlin/zed/rainxch/tweaks/presentation/TweaksEvent.kt
  • feature/tweaks/presentation/src/commonMain/kotlin/zed/rainxch/tweaks/presentation/TweaksRoot.kt
  • feature/tweaks/presentation/src/commonMain/kotlin/zed/rainxch/tweaks/presentation/TweaksState.kt
  • feature/tweaks/presentation/src/commonMain/kotlin/zed/rainxch/tweaks/presentation/TweaksViewModel.kt
  • feature/tweaks/presentation/src/commonMain/kotlin/zed/rainxch/tweaks/presentation/components/sections/Network.kt

Comment on lines +11 to +14
import java.io.IOException
import java.net.ConnectException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Declared Kotlin targets =="
rg -n --glob '**/*.gradle.kts' --glob '**/*.gradle' 'ios\(|macos|linuxX64|mingwX64|js\(|wasm|androidTarget|jvm\('

echo
echo "== JVM imports inside commonMain =="
rg -n --glob '**/src/commonMain/**/*.kt' '^import java\.'

Repository: OpenHub-Store/GitHub-Store

Length of output: 99


🏁 Script executed:

# Confirm the file exists and view its imports
cat -n "core/data/src/commonMain/kotlin/zed/rainxch/core/data/network/ProxyTesterImpl.kt" | head -20

Repository: OpenHub-Store/GitHub-Store

Length of output: 995


🏁 Script executed:

# Search for Kotlin Multiplatform configuration
find . -name "build.gradle.kts" -o -name "build.gradle" -o -name "settings.gradle.kts" | head -10

Repository: OpenHub-Store/GitHub-Store

Length of output: 412


🏁 Script executed:

# Check for kotlin block in any gradle files more broadly
rg -l "kotlin\s*\{" --glob "**/*.gradle.kts" --glob "**/*.gradle"

Repository: OpenHub-Store/GitHub-Store

Length of output: 1434


🏁 Script executed:

# Read the core/data/build.gradle.kts to check KMP target configuration
cat "core/data/build.gradle.kts"

Repository: OpenHub-Store/GitHub-Store

Length of output: 1285


🏁 Script executed:

# Check the convention plugin definition for KMP library
cat "build-logic/convention/build.gradle.kts"

Repository: OpenHub-Store/GitHub-Store

Length of output: 2739


🏁 Script executed:

# Also search for where the convention plugin targets are defined
find "build-logic" -name "*.gradle.kts" -o -name "*.kt" | grep -i kmp | head -5

Repository: OpenHub-Store/GitHub-Store

Length of output: 138


🏁 Script executed:

# Read the KMP Library convention plugin
cat "build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt"

Repository: OpenHub-Store/GitHub-Store

Length of output: 1325


🏁 Script executed:

# Search for the configureKotlinMultiplatform function
rg -n "fun configureKotlinMultiplatform" "build-logic/convention/src/main/kotlin/"

Repository: OpenHub-Store/GitHub-Store

Length of output: 52


🏁 Script executed:

# Look for convention files that define multiplatform config
fd "\.kt$" "build-logic/convention/src/main/kotlin/" | xargs grep -l "kotlin\s*\{" | head -10

Repository: OpenHub-Store/GitHub-Store

Length of output: 88


🏁 Script executed:

# Search for configureKotlinMultiplatform more broadly
rg "configureKotlinMultiplatform" "build-logic/convention/src/main/kotlin/"

Repository: OpenHub-Store/GitHub-Store

Length of output: 643


🏁 Script executed:

# List all .kt files in the convention directory
fd "\.kt$" "build-logic/convention/src/main/kotlin/" | sort

Repository: OpenHub-Store/GitHub-Store

Length of output: 1352


🏁 Script executed:

# Read the KotlinMultiplatform.kt file
cat "build-logic/convention/src/main/kotlin/zed/rainxch/githubstore/convention/KotlinMultiplatform.kt"

Repository: OpenHub-Store/GitHub-Store

Length of output: 900


🏁 Script executed:

# Read the full ProxyTesterImpl.kt to see how java.* exceptions are used
cat "core/data/src/commonMain/kotlin/zed/rainxch/core/data/network/ProxyTesterImpl.kt"

Repository: OpenHub-Store/GitHub-Store

Length of output: 2699


🏁 Script executed:

# Search for other ProxyTester implementations
rg -n "class.*ProxyTester" --glob "**/*.kt"

Repository: OpenHub-Store/GitHub-Store

Length of output: 191


🏁 Script executed:

# Check if these java.* exceptions are actually needed or if Ktor exceptions would suffice
rg -n "SocketTimeoutException|UnknownHostException|ConnectException|IOException" "core/data/src/commonMain/kotlin/zed/rainxch/core/data/network/ProxyTesterImpl.kt"

Repository: OpenHub-Store/GitHub-Store

Length of output: 394


Move JVM-specific exception handling out of commonMain.

Lines 11-14 import java.* exceptions in commonMain, violating the platform API guideline. These imports are actively used in catch blocks (lines 49, 53, 55, 57). Move JVM-only exception mapping to androidMain and jvmMain via expect/actual pattern, or replace with Ktor-native exception handling where available.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@core/data/src/commonMain/kotlin/zed/rainxch/core/data/network/ProxyTesterImpl.kt`
around lines 11 - 14, ProxyTesterImpl currently imports JVM-only exceptions
(IOException, ConnectException, SocketTimeoutException, UnknownHostException) in
commonMain and uses them in its catch blocks; remove those java.* imports and
the JVM-specific catches from
core/data/src/commonMain/kotlin/zed/rainxch/core/data/network/ProxyTesterImpl.kt,
create an expect/actual mapping or platform-specific helper (e.g., expect fun
mapNetworkException(e: Throwable): ProxyTestResult or expect class
NetworkExceptionMapper with actual implementations in androidMain/jvmMain) and
replace the direct exception checks in the ProxyTesterImpl test method with a
call to that platform mapper (or use Ktor-native exception types if available)
so that platform-specific exception handling lives in actual implementations
while commonMain only depends on the abstract mapping API.

…uration

- Refactor `TweaksViewModel` to explicitly handle `CancellationException` during proxy tests to preserve structured concurrency.
- Update `TweaksViewModel` to use localized fallback strings for unknown proxy test errors instead of leaking raw exception messages.
- Strengthen proxy port validation in `resolveAndroidSystemProxy` to ensure values are within the valid 1-65535 range before creating `InetSocketAddress`.
- Narrow exception catching in `ProxyTesterImpl` from `Throwable` to `Exception`.
- Update `.claude/settings.local.json` to use relative paths in grep commands and add a combined Gradle build task.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
core/data/src/commonMain/kotlin/zed/rainxch/core/data/network/ProxyTesterImpl.kt (1)

11-14: ⚠️ Potential issue | 🔴 Critical

Move JVM exception mapping out of commonMain.

java.* imports at Line 11–14 and JVM-specific catches at Line 49–58 couple shared code to JVM APIs. Keep ProxyTesterImpl in common by delegating exception mapping via an expect/actual mapper (actuals in androidMain/jvmMain), or map only common/Ktor exceptions in this layer.

As per coding guidelines: Shared code must be in commonMain source set and use expect/actual for platform-specific APIs.

#!/bin/bash
set -euo pipefail

echo "== JVM imports inside commonMain =="
rg -n --glob '**/src/commonMain/**/*.kt' '^import java\.'

echo
echo "== Declared multiplatform targets =="
rg -n --glob '**/*.gradle.kts' 'androidTarget|jvm\(|ios\(|macos|linuxX64|mingwX64|js\(|wasm'

Also applies to: 49-58


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1e9225fd-deb8-4fae-9715-608707592054

📥 Commits

Reviewing files that changed from the base of the PR and between 0762481 and fc8bd13.

📒 Files selected for processing (4)
  • .claude/settings.local.json
  • core/data/src/androidMain/kotlin/zed/rainxch/core/data/network/HttpClientFactory.android.kt
  • core/data/src/commonMain/kotlin/zed/rainxch/core/data/network/ProxyTesterImpl.kt
  • feature/tweaks/presentation/src/commonMain/kotlin/zed/rainxch/tweaks/presentation/TweaksViewModel.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • .claude/settings.local.json
  • feature/tweaks/presentation/src/commonMain/kotlin/zed/rainxch/tweaks/presentation/TweaksViewModel.kt

@rainxchzed rainxchzed merged commit 637aca1 into main Apr 10, 2026
1 check passed
@rainxchzed rainxchzed deleted the proxy-tester-impl branch April 10, 2026 17:43
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.

1 participant