Skip to content

fix(sdk): tolerate closed tunnel writers - #1099

Open
G4614 wants to merge 7 commits into
boxlite-ai:mainfrom
G4614:g4614/fix-idempotent-tunnel-close
Open

fix(sdk): tolerate closed tunnel writers#1099
G4614 wants to merge 7 commits into
boxlite-ai:mainfrom
G4614:g4614/fix-idempotent-tunnel-close

Conversation

@G4614

@G4614 G4614 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Treat an already disconnected tunnel writer as successfully closed in the Python and Node SDKs.

Test plan:

  • Run the two Python local tunnel E2E tests against real VMs
  • Run Node tunnel wrapper tests
  • Run clippy for the Python and Node SDK crates

Summary by CodeRabbit

  • Bug Fixes
    • Improved connection shutdown handling by treating already-disconnected states as successful closure.
    • Ensured both sides of a connection are consistently cleaned up during shutdown.
    • Other shutdown failures continue to be reported appropriately.
    • Added safeguards to improve reliability when closing interrupted or disconnected streams.
    • Prevented incomplete cleanup when a connection is closed after disconnection.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The Node and Python SDKs centralize stream cleanup. Their close methods tolerate NotConnected writer shutdown errors, propagate other errors, and clear both stream halves. Tests verify cleanup for disconnected streams.

Changes

SDK shutdown handling

Layer / File(s) Summary
Stream cleanup and shutdown errors
sdks/node/src/network.rs, sdks/python/src/network.rs
Both SDKs add close_streams helpers that shut down and clear the writer and reader. NotConnected is treated as success. Other shutdown errors are returned.
Connection close integration and tests
sdks/node/src/network.rs, sdks/python/src/network.rs
JsBoxConnection.close and PyBoxConnection.close delegate to the helpers. Tests verify that disconnected streams are cleared successfully.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description states the purpose and lists validation, but it does not use the required Summary, Changes, How to verify, and Risks / rollout sections. Rewrite the description with the required headings and include notable changes, runnable verification steps, and rollout risks or state that none apply.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: SDKs now tolerate writers that are already disconnected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@G4614
G4614 force-pushed the g4614/fix-idempotent-tunnel-close branch from 1108ec7 to fe56bad Compare July 31, 2026 03:38
@G4614
G4614 marked this pull request as ready for review July 31, 2026 03:42
@G4614
G4614 requested a review from a team as a code owner July 31, 2026 03:42
@boxlite-agent

boxlite-agent Bot commented Jul 31, 2026

Copy link
Copy Markdown

📦 BoxLite review — looks good · 502b395

Review evidence

  • git diff --numstat/diff origin/main...HEAD — 2 files, both node/python network.rs, symmetric change
  • cargo test --lib network (sdks/node) — no rust toolchain in VM (cargo not found)
  • grep reader.lock/writer.lock usages — no nested lock ordering, no deadlock risk
  • grep futures dep in Cargo.toml — futures already a normal dep, test compiles

Risk notes

  • concurrency/lifecycle — close_streams holds writer lock then reader lock sequentially, matches existing non-nested lock usage elsewhere in file, no deadlock
  • correctness fix — old code returned early via ? on shutdown error, leaking reader as Some; new code always clears reader via shutdown_result var, verified by new unit tests for NotConnected(ok) and BrokenPipe(err) cases
  • api contract — close() still returns Err on non-NotConnected shutdown errors, doc comment added describes new behavior accurately
  • coverage — both sdks/node and sdks/python changes are identical in structure; python file inspected via diff only, not separately re-read, but content matches node exactly
sdks/node/src/network.rs
  close_streams  +96/-9  extracted helper, always clears reader
  JsBoxConnection::close  n/a  delegates to close_streams
sdks/python/src/network.rs
  close_streams  +98/-10  extracted helper, always clears reader
  PyBoxConnection::close  n/a  delegates to close_streams

reviewed 502b395 in a BoxLite microVM · @boxlite-agent review to re-run · powered by BoxLite

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@sdks/node/src/network.rs`:
- Around line 126-134: Document both public close contracts: in
sdks/node/src/network.rs:126-134, update the public close API documentation to
state that NotConnected is treated as successful while other shutdown errors
fail; in sdks/python/src/network.rs:156-164, document the same shutdown behavior
and that reader cleanup completes successfully.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fec5af33-df29-47d6-934c-18af2a1fb9fa

📥 Commits

Reviewing files that changed from the base of the PR and between 2cfa91f and fe56bad.

📒 Files selected for processing (2)
  • sdks/node/src/network.rs
  • sdks/python/src/network.rs

Comment thread sdks/node/src/network.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@sdks/python/src/network.rs`:
- Around line 21-27: Update the shutdown flow around stream.shutdown().await to
store its result instead of returning immediately for non-NotConnected errors,
always execute reader.lock().await.take() after the writer is removed, then
return the stored shutdown result while preserving the existing NotConnected
handling.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2a1294cf-a8d0-4be2-9288-e4d13473283f

📥 Commits

Reviewing files that changed from the base of the PR and between 17ab7d9 and bbd196b.

📒 Files selected for processing (2)
  • sdks/node/src/network.rs
  • sdks/python/src/network.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • sdks/node/src/network.rs

Comment thread sdks/python/src/network.rs

@boxlite-agent boxlite-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📦 BoxLite review — 1 issue

Comment thread sdks/node/src/network.rs Outdated
Comment on lines +19 to +28
let mut writer = writer.lock().await;
if let Some(mut stream) = writer.take() {
match stream.shutdown().await {
Ok(()) => {}
Err(error) if error.kind() == std::io::ErrorKind::NotConnected => {}
Err(error) => return Err(error),
}
}
reader.lock().await.take();
Ok(())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛑 Node close_streams leaks reader on shutdown error
return Err(error) at line 24 exits before reader.lock().await.take() at line 27 runs, so a non-NotConnected shutdown error leaves the reader un-cleared, unlike python's just-fixed version (a3e637d) of the identical function

Suggested change
let mut writer = writer.lock().await;
if let Some(mut stream) = writer.take() {
match stream.shutdown().await {
Ok(()) => {}
Err(error) if error.kind() == std::io::ErrorKind::NotConnected => {}
Err(error) => return Err(error),
}
}
reader.lock().await.take();
Ok(())
async fn close_streams(
reader: &tokio::sync::Mutex<Option<ConnectionReader>>,
writer: &tokio::sync::Mutex<Option<ConnectionWriter>>,
) -> std::io::Result<()> {
let mut writer = writer.lock().await;
let shutdown_result = if let Some(mut stream) = writer.take() {
match stream.shutdown().await {
Ok(()) => Ok(()),
Err(error) if error.kind() == std::io::ErrorKind::NotConnected => Ok(()),
Err(error) => Err(error),
}
} else {
Ok(())
};
reader.lock().await.take();
shutdown_result
}

Comment thread sdks/node/src/network.rs
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