Add a runnable local Linux demo (server + client + curl)#244
Conversation
- Apply the Gradle `application` plugin to `server/` and `client/` and set `mainClass` so `./gradlew :server:run` and `:client:run` work. - Change LinuxProxyClient.main's no-arg default server from 10.0.0.114 to 127.0.0.1 (the previous value was a stray hardcoded LAN IP that made the no-arg path useless for a local demo). - Add `client/scripts/demo.sh` that brings up the kanon TUN device, starts the server and client via Gradle, adds a host route through the TUN, and runs a curl against the chosen target IP. - Extend `client/scripts/cleanup.sh` to also kill GradleWorkerMain, ProxyServer, and LinuxProxyClient processes and to drop any host routes still pointed at the kanon device. - Document the demo in the top-level README with both the scripted one-shot path and the step-by-step manual commands, plus a Wireshark attach hint. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a runnable local Linux “server + client + curl” demo path by making :server:run / :client:run work out of the box, providing demo/cleanup scripts, and documenting the workflow in the README.
Changes:
- Apply Gradle
applicationplugin toserver/andclient/with configuredmainClassso:server:runand:client:runare runnable. - Update
LinuxProxyClientdefault no-arg server address to127.0.0.1for local loopback demos. - Add
client/scripts/demo.sh, extendclient/scripts/cleanup.sh, and document the end-to-end demo inREADME.md.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
server/build.gradle.kts |
Adds application plugin + main class to make server runnable via Gradle. |
client/build.gradle.kts |
Adds application plugin + main class to make client runnable via Gradle. |
client/src/main/kotlin/com/jasonernst/kanonproxy/LinuxProxyClient.kt |
Switches default server address to localhost for no-arg runs. |
client/scripts/demo.sh |
New end-to-end Linux demo script (TUN + server + client + route + curl). |
client/scripts/cleanup.sh |
Expands cleanup to kill additional processes and remove kanon-bound routes. |
README.md |
Documents scripted and manual demo steps plus Wireshark attachment info. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #244 +/- ##
============================================
+ Coverage 60.38% 60.86% +0.48%
- Complexity 226 230 +4
============================================
Files 15 15
Lines 2070 2070
Branches 315 315
============================================
+ Hits 1250 1260 +10
+ Misses 644 627 -17
- Partials 176 183 +7
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
# Conflicts: # README.md
Previously the demo added a host route for the curl target through the kanon device. That caused a loop: the proxy server's own outbound TCP SYN to the same target also matched the kanon route and got pulled back into the TUN, which the client then forwarded to the server again, and around it went until curl timed out. Switching to `sudo curl --interface kanon http://<target>/` avoids the loop entirely. SO_BINDTODEVICE pins only curl's socket to the TUN, so the proxy server's outbound TCP stays unbound and follows the normal default route to the real internet. No changes to the kernel route table needed. Updates demo.sh and README to remove `ip route add/del` and use `--interface kanon` (with sudo, since SO_BINDTODEVICE needs CAP_NET_RAW). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously the whole cleanup body was wrapped in `{ ... } &> /dev/null`
which hid every error - so when the kanon TUN failed to delete you got
no feedback. Combined with `pkill` being asynchronous, the script
could race the kernel's fd release and leave the interface stuck.
This rewrite:
- Drops the blanket `&> /dev/null` and prints diagnostics; only the
individual `ip` commands silence their own no-op errors via
`2>/dev/null || true`.
- SIGTERM the JVM processes first, sleep 1s for them to release the
TUN fd, then SIGKILL anything still around.
- Retry `ip tuntap del` up to 5 times with 1-second gaps to handle
any remaining race with kernel cleanup.
- Print a clear warning and an `lsof /dev/net/tun` hint if the
interface is still present afterwards, so the user can investigate
who's still holding it.
- Remove `rm *.jar *.so`. It depended on cwd and was never relevant
to clean teardown of the proxy.
- Skip everything if the kanon interface isn't there in the first
place (idempotent rerun).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Restructures the Local Linux demo into Path A (scripted) and Path B (manual, 4-terminal) to match what was actually verified end-to-end. Adds: - The expected log lines and verification commands per terminal (e.g. ss -lun | grep 8080) so users know what success looks like. - Notes that the script leaves server/client running and shows how to fire additional curls (and tail the session log). - A "Watching packets" section that calls out `sudo wireshark -i kanon` as the easy native-libpcap option on Linux, alongside the in-process pcap-ng TCP dumpers, with a one-line explanation of when each one is the right choice. - A reminder about the cleanup.sh "kanon interface is still present" warning and the `sudo lsof /dev/net/tun` follow-up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- demo.sh: track SERVER_READY in the wait loop, exit non-zero on timeout, and bail early if the server PID exits before binding the UDP port. Previously the loop would always fall through after 30 seconds and the script would happily start the client and curl even if the server had crashed. - cleanup.sh: scope all process kills to the current user via `pgrep -u $UID -f` instead of an unscoped `pkill -f`. Patterns like "ProxyServer" / "LinuxProxyClient" are otherwise broad enough to match unrelated JVMs (other Gradle builds, IDEs, etc.) on a shared host. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Embeds a clickable YouTube poster thumbnail for the new Linux demo (https://youtu.be/_ypo_3PYqTM) at the top of the Local Linux demo section, matching the thumbnail-link pattern already used for the Android demos at the top of the README. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Test plan
🤖 Generated with Claude Code