Skip to content

Commit 2b1832c

Browse files
committed
wip
1 parent 90f5354 commit 2b1832c

File tree

4 files changed

+122
-39
lines changed

4 files changed

+122
-39
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,5 @@ dmypy.json
133133
*~
134134

135135
# Project specific
136-
*.bak
136+
*.bak
137+
build/

retunnel/client/cli.py

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,16 @@ async def _run_tunnel(
511511
status_text = f"[{RETUNNEL_THEME['success']}]● Active[/{RETUNNEL_THEME['success']}]"
512512
tunnel_table.add_row("Status", status_text)
513513

514+
# Add auth token display (last 4 characters)
515+
if client.auth_token:
516+
token_display = f"****{client.auth_token[-4:]}"
517+
else:
518+
token_display = "None"
519+
tunnel_table.add_row(
520+
"Token",
521+
f"[{RETUNNEL_THEME['dim']}]{token_display}[/{RETUNNEL_THEME['dim']}]",
522+
)
523+
514524
tunnel_panel = Panel(
515525
tunnel_table,
516526
title="[bold]Tunnel Details[/bold]",
@@ -563,29 +573,47 @@ async def _run_tunnel(
563573
status_text = f"[{RETUNNEL_THEME['warning']}]⟳ {client.connection_status}[/{RETUNNEL_THEME['warning']}]"
564574
else:
565575
status_text = f"[{RETUNNEL_THEME['error']}]● {client.connection_status}[/{RETUNNEL_THEME['error']}]"
566-
576+
567577
# Rebuild tunnel table with updated status
568-
tunnel_table = Table(show_header=False, box=None, padding=(0, 2))
578+
tunnel_table = Table(
579+
show_header=False, box=None, padding=(0, 2)
580+
)
569581
tunnel_table.add_column(style=f"{RETUNNEL_THEME['dim']}")
570582
tunnel_table.add_column(style="bold")
571-
583+
572584
tunnel_table.add_row(
573585
"URL",
574586
f"[bold {RETUNNEL_THEME['success']}]{tunnel.url}[/bold {RETUNNEL_THEME['success']}]",
575587
)
576588
tunnel_table.add_row("Protocol", tunnel.protocol.upper())
577-
tunnel_table.add_row("Local Port", str(tunnel.config.local_port))
589+
tunnel_table.add_row(
590+
"Local Port", str(tunnel.config.local_port)
591+
)
578592
tunnel_table.add_row("Status", status_text)
579-
593+
594+
# Add auth token display (last 4 characters)
595+
if client.auth_token:
596+
token_display = f"****{client.auth_token[-4:]}"
597+
else:
598+
token_display = "None"
599+
tunnel_table.add_row(
600+
"Token",
601+
f"[{RETUNNEL_THEME['dim']}]{token_display}[/{RETUNNEL_THEME['dim']}]",
602+
)
603+
580604
tunnel_panel = Panel(
581605
tunnel_table,
582606
title="[bold]Tunnel Details[/bold]",
583-
border_style=RETUNNEL_THEME["success"] if client.is_connected else RETUNNEL_THEME["warning"],
607+
border_style=(
608+
RETUNNEL_THEME["success"]
609+
if client.is_connected
610+
else RETUNNEL_THEME["warning"]
611+
),
584612
padding=(1, 2),
585613
)
586-
614+
587615
layout["info"].update(tunnel_panel)
588-
616+
589617
# Update traffic stats
590618
stats = tunnel.get_stats()
591619
in_bytes = _format_bytes(stats["bytes_in"])

retunnel/client/config_manager.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import json
99
import os
10+
import sys
1011
from dataclasses import dataclass
1112
from pathlib import Path
1213
from typing import Any, Optional
@@ -63,6 +64,7 @@ async def load(self) -> ClientConfig:
6364

6465
if not self.config_path.exists():
6566
# Create default config
67+
# Use logger instead of print to avoid output pollution
6668
self._config = ClientConfig()
6769
await self.save()
6870
return self._config
@@ -71,8 +73,10 @@ async def load(self) -> ClientConfig:
7173
async with aiofiles.open(self.config_path, "r") as f:
7274
data = json.loads(await f.read())
7375
self._config = ClientConfig.from_dict(data)
74-
except Exception:
76+
# Remove print statement that was polluting output
77+
except Exception as e:
7578
# If config is corrupted, create new one
79+
print(f"Error loading config: {e}", file=sys.stderr)
7680
self._config = ClientConfig()
7781
await self.save()
7882

retunnel/client/high_performance_model.py

Lines changed: 79 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ async def _ensure_auth_token(self) -> None:
113113
# If no auth token provided, check config file
114114
if not self.auth_token:
115115
self.auth_token = await config_manager.get_auth_token()
116+
if self.auth_token:
117+
self.logger.info(
118+
f"Loaded auth token from config: ****{self.auth_token[-4:]}"
119+
)
116120

117121
# If still no token, register new user
118122
if not self.auth_token:
@@ -140,8 +144,17 @@ async def _ensure_auth_token(self) -> None:
140144
self.logger.error(f"Failed to register user: {e}")
141145
raise
142146

143-
# Verify token is still valid and refresh if needed
147+
# Skip token verification for now - just use the token from config
148+
# This was causing issues with multiple registrations
144149
if self.auth_token:
150+
self.logger.info(
151+
f"Using auth token from config: ****{self.auth_token[-4:]}"
152+
)
153+
return
154+
155+
# OLD CODE - disabled for now
156+
# Verify token is still valid and refresh if needed
157+
if False and self.auth_token:
145158
api_url = await config_manager.get_api_url()
146159

147160
# Handle local development
@@ -268,12 +281,16 @@ async def connect(self) -> None:
268281
if resp.get("ClientId"):
269282
self.client_id = resp["ClientId"]
270283

271-
self.logger.info(f"Connected as client {self.client_id}")
272-
284+
self.logger.info(
285+
f"Connected as client {self.client_id} with token ****{self.auth_token[-4:] if self.auth_token else 'None'}"
286+
)
287+
273288
# Update connection state
274289
self.is_connected = True
275290
self.connection_status = "Connected"
276-
self._reconnect_delay = 1.0 # Reset reconnect delay on successful connection
291+
self._reconnect_delay = (
292+
1.0 # Reset reconnect delay on successful connection
293+
)
277294

278295
# Start background tasks
279296
self._start_background_tasks()
@@ -307,12 +324,21 @@ async def request_tunnel(self, config: TunnelConfig) -> Tunnel:
307324

308325
# Check for error response
309326
if resp.get("Type") == "ErrorResp":
310-
error_code = resp.get("ErrorCode", "UNKNOWN")
311-
message = resp.get("Message", "Unknown error")
327+
# The error fields are at the root level, not in Payload
328+
error_code = resp.get("error_code", "UNKNOWN")
329+
message = resp.get("message", "Unknown error")
330+
331+
# Debug log
332+
self.logger.debug(f"ErrorResp received: {resp}")
333+
312334
if error_code == "OVER_CAPACITY":
313335
raise Exception(
314336
"No subdomains available. Please try again later."
315337
)
338+
elif error_code == "FREE_TIER_LIMIT_REACHED":
339+
raise Exception(
340+
"Free tier limit reached. You can have a maximum of 2 active tunnels."
341+
)
316342
else:
317343
raise Exception(f"{error_code}: {message}")
318344

@@ -400,6 +426,12 @@ async def _receive_message(
400426

401427
async def _handle_message(self, msg: Dict[str, Any]) -> None:
402428
"""Handle incoming control messages"""
429+
# Normalize message - handle both uppercase and lowercase field names
430+
if "type" in msg and "Type" not in msg:
431+
msg["Type"] = msg["type"]
432+
if "payload" in msg and "Payload" not in msg:
433+
msg["Payload"] = msg["payload"]
434+
403435
msg_type = msg.get("Type")
404436

405437
if msg_type == "NewTunnel":
@@ -411,7 +443,7 @@ async def _handle_message(self, msg: Dict[str, Any]) -> None:
411443

412444
elif msg_type == "ErrorResp":
413445
# Error response
414-
req_id = msg.get("ReqId")
446+
req_id = msg.get("ReqId") or msg.get("Payload", {}).get("req_id")
415447
future = self.pending_requests.get(req_id) if req_id else None
416448
if future and not future.done():
417449
future.set_result(msg)
@@ -711,7 +743,11 @@ async def _subdomain_heartbeat(self, subdomain: str) -> None:
711743
async def _control_loop(self) -> None:
712744
"""Listen for control messages"""
713745
try:
714-
while self._running and self.control_ws and not self.control_ws.closed:
746+
while (
747+
self._running
748+
and self.control_ws
749+
and not self.control_ws.closed
750+
):
715751
try:
716752
msg = await self._receive_message(self.control_ws)
717753
await self._handle_message(msg)
@@ -722,7 +758,9 @@ async def _control_loop(self) -> None:
722758
self.connection_status = "Disconnected"
723759
# Trigger reconnection
724760
if self._running and not self._reconnecting:
725-
self._reconnect_task = asyncio.create_task(self._reconnect())
761+
self._reconnect_task = asyncio.create_task(
762+
self._reconnect()
763+
)
726764
break
727765
except asyncio.CancelledError:
728766
# Normal shutdown
@@ -744,61 +782,73 @@ async def _maintain_proxy_pool(self) -> None:
744782
async def _reconnect(self) -> None:
745783
"""Reconnect with exponential backoff"""
746784
self._reconnecting = True
747-
785+
748786
while self._running:
749787
try:
750-
self.connection_status = f"Reconnecting in {self._reconnect_delay:.0f}s..."
751-
self.logger.info(f"Attempting reconnection in {self._reconnect_delay} seconds")
752-
788+
self.connection_status = (
789+
f"Reconnecting in {self._reconnect_delay:.0f}s..."
790+
)
791+
self.logger.info(
792+
f"Attempting reconnection in {self._reconnect_delay} seconds"
793+
)
794+
753795
# Wait with exponential backoff
754796
await asyncio.sleep(self._reconnect_delay)
755-
797+
756798
# Update status
757799
self.connection_status = "Connecting..."
758-
800+
759801
# Close existing connection if any
760802
if self.control_ws and not self.control_ws.closed:
761803
await self.control_ws.close()
762-
804+
763805
# Close and recreate session
764806
if self.session and not self.session.closed:
765807
await self.session.close()
766808
await asyncio.sleep(0.1)
767-
809+
768810
# Re-establish connection
769811
await self.connect()
770-
812+
771813
# Re-request tunnels with same configs
772814
for tunnel in list(self.tunnels.values()):
773815
try:
774816
# Request tunnel with same subdomain
775817
config = tunnel.config
776818
# For HTTP tunnels, try to get the same subdomain
777-
if tunnel.protocol == "http" and hasattr(tunnel, "subdomain"):
819+
if tunnel.protocol == "http" and hasattr(
820+
tunnel, "subdomain"
821+
):
778822
config.subdomain = tunnel.subdomain
779-
823+
780824
new_tunnel = await self.request_tunnel(config)
781-
self.logger.info(f"Re-established tunnel: {new_tunnel.url}")
825+
self.logger.info(
826+
f"Re-established tunnel: {new_tunnel.url}"
827+
)
782828
except Exception as e:
783-
self.logger.error(f"Failed to re-establish tunnel: {e}")
784-
829+
self.logger.error(
830+
f"Failed to re-establish tunnel: {e}"
831+
)
832+
785833
# Success - exit reconnection loop
786834
self._reconnecting = False
787835
break
788-
836+
789837
except Exception as e:
790838
self.logger.error(f"Reconnection failed: {e}")
791-
self.connection_status = f"Reconnection failed, retrying..."
792-
839+
self.connection_status = "Reconnection failed, retrying..."
840+
793841
# Increase delay with exponential backoff
794-
self._reconnect_delay = min(self._reconnect_delay * 2, self._max_reconnect_delay)
795-
842+
self._reconnect_delay = min(
843+
self._reconnect_delay * 2, self._max_reconnect_delay
844+
)
845+
796846
self._reconnecting = False
797847

798848
async def close(self) -> None:
799849
"""Close all connections and clean up"""
800850
self.logger.info("Closing client")
801-
851+
802852
# Stop running
803853
self._running = False
804854
self.is_connected = False

0 commit comments

Comments
 (0)