escobar 0.1.90__py3-none-any.whl → 0.1.91__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- escobar/_version.py +1 -1
- escobar/handlers.py +57 -254
- escobar/labextension/package.json +2 -2
- escobar/labextension/schemas/escobar/package.json.orig +1 -1
- escobar/labextension/schemas/escobar/plugin.json +2 -2
- escobar/labextension/static/653.cd0a1dbb61ede2a5de00.js +1 -0
- escobar/labextension/static/{remoteEntry.c9a1accf3c70050fa880.js → remoteEntry.48a97be89d680e21e498.js} +1 -1
- {escobar-0.1.90.data → escobar-0.1.91.data}/data/share/jupyter/labextensions/escobar/package.json +2 -2
- {escobar-0.1.90.data → escobar-0.1.91.data}/data/share/jupyter/labextensions/escobar/schemas/escobar/package.json.orig +1 -1
- {escobar-0.1.90.data → escobar-0.1.91.data}/data/share/jupyter/labextensions/escobar/schemas/escobar/plugin.json +2 -2
- escobar-0.1.91.data/data/share/jupyter/labextensions/escobar/static/653.cd0a1dbb61ede2a5de00.js +1 -0
- escobar-0.1.90.data/data/share/jupyter/labextensions/escobar/static/remoteEntry.c9a1accf3c70050fa880.js → escobar-0.1.91.data/data/share/jupyter/labextensions/escobar/static/remoteEntry.48a97be89d680e21e498.js +1 -1
- {escobar-0.1.90.dist-info → escobar-0.1.91.dist-info}/METADATA +1 -1
- escobar-0.1.91.dist-info/RECORD +40 -0
- escobar/labextension/static/237.d012347a7c8fb32c64ae.js +0 -1
- escobar-0.1.90.data/data/share/jupyter/labextensions/escobar/static/237.d012347a7c8fb32c64ae.js +0 -1
- escobar-0.1.90.dist-info/RECORD +0 -40
- {escobar-0.1.90.data → escobar-0.1.91.data}/data/share/jupyter/labextensions/escobar/install.json +0 -0
- {escobar-0.1.90.data → escobar-0.1.91.data}/data/share/jupyter/labextensions/escobar/static/304.bf7e91be734e5b36cdc9.js +0 -0
- {escobar-0.1.90.data → escobar-0.1.91.data}/data/share/jupyter/labextensions/escobar/static/346.8a1e61ca6789b6fddfa7.js +0 -0
- {escobar-0.1.90.data → escobar-0.1.91.data}/data/share/jupyter/labextensions/escobar/static/379.40dd59dc19d4a6b42d25.js +0 -0
- {escobar-0.1.90.data → escobar-0.1.91.data}/data/share/jupyter/labextensions/escobar/static/57.17e53b4b9a954f39c4d8.js +0 -0
- {escobar-0.1.90.data → escobar-0.1.91.data}/data/share/jupyter/labextensions/escobar/static/648.a7d314faeacc762d891d.js +0 -0
- {escobar-0.1.90.data → escobar-0.1.91.data}/data/share/jupyter/labextensions/escobar/static/666.3bc65aac3a3be183ee19.js +0 -0
- {escobar-0.1.90.data → escobar-0.1.91.data}/data/share/jupyter/labextensions/escobar/static/874.c539726f31a1baa0267e.js +0 -0
- {escobar-0.1.90.data → escobar-0.1.91.data}/data/share/jupyter/labextensions/escobar/static/986.cbcf9d7c1cd8d06be435.js +0 -0
- {escobar-0.1.90.data → escobar-0.1.91.data}/data/share/jupyter/labextensions/escobar/static/style.js +0 -0
- {escobar-0.1.90.data → escobar-0.1.91.data}/data/share/jupyter/labextensions/escobar/static/third-party-licenses.json +0 -0
- {escobar-0.1.90.dist-info → escobar-0.1.91.dist-info}/WHEEL +0 -0
- {escobar-0.1.90.dist-info → escobar-0.1.91.dist-info}/entry_points.txt +0 -0
- {escobar-0.1.90.dist-info → escobar-0.1.91.dist-info}/licenses/LICENSE +0 -0
escobar/_version.py
CHANGED
escobar/handlers.py
CHANGED
@@ -6,8 +6,6 @@ import websockets
|
|
6
6
|
import ssl
|
7
7
|
import logging
|
8
8
|
import time
|
9
|
-
import threading
|
10
|
-
import psutil
|
11
9
|
from urllib.parse import urlparse, urlunparse
|
12
10
|
from jupyter_server.base.handlers import JupyterHandler
|
13
11
|
from jupyter_server.utils import url_path_join
|
@@ -17,10 +15,58 @@ import tornado.websocket
|
|
17
15
|
import aiohttp
|
18
16
|
from traitlets.config import LoggingConfigurable
|
19
17
|
import mimetypes
|
18
|
+
import requests
|
19
|
+
from google.oauth2 import id_token
|
20
|
+
from google.auth.transport import requests as google_requests
|
20
21
|
|
21
22
|
# Default proxy port
|
22
23
|
DEFAULT_PROXY_PORT = 3000
|
23
24
|
|
25
|
+
|
26
|
+
class OAuthCallbackHandler(JupyterHandler):
|
27
|
+
"""
|
28
|
+
Handler for /static/oauth-callback.html endpoint.
|
29
|
+
Serves the OAuth callback HTML file for Google authentication.
|
30
|
+
"""
|
31
|
+
|
32
|
+
async def get(self):
|
33
|
+
"""Handle GET requests to serve the OAuth callback HTML"""
|
34
|
+
try:
|
35
|
+
# Get the path to the static directory
|
36
|
+
import os
|
37
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
38
|
+
project_root = os.path.dirname(current_dir)
|
39
|
+
callback_file_path = os.path.join(project_root, 'static', 'oauth-callback.html')
|
40
|
+
|
41
|
+
self.log.info(f"🔐 CALLBACK: Serving OAuth callback from: {callback_file_path}")
|
42
|
+
|
43
|
+
# Check if file exists
|
44
|
+
if not os.path.exists(callback_file_path):
|
45
|
+
self.log.error(f"🔐 CALLBACK: OAuth callback file not found: {callback_file_path}")
|
46
|
+
self.set_status(404)
|
47
|
+
self.finish("OAuth callback file not found")
|
48
|
+
return
|
49
|
+
|
50
|
+
# Read and serve the HTML file
|
51
|
+
with open(callback_file_path, 'r', encoding='utf-8') as f:
|
52
|
+
html_content = f.read()
|
53
|
+
|
54
|
+
# Set proper headers
|
55
|
+
self.set_header('Content-Type', 'text/html; charset=UTF-8')
|
56
|
+
self.set_header('Cache-Control', 'no-cache, no-store, must-revalidate')
|
57
|
+
self.set_header('Pragma', 'no-cache')
|
58
|
+
self.set_header('Expires', '0')
|
59
|
+
|
60
|
+
# Write the HTML content
|
61
|
+
self.write(html_content)
|
62
|
+
self.log.info(f"🔐 CALLBACK: Successfully served OAuth callback HTML")
|
63
|
+
|
64
|
+
except Exception as e:
|
65
|
+
self.log.error(f"🔐 CALLBACK: Error serving OAuth callback: {e}")
|
66
|
+
self.set_status(500)
|
67
|
+
self.finish(f"Error serving OAuth callback: {str(e)}")
|
68
|
+
|
69
|
+
|
24
70
|
class ProxyHandler(JupyterHandler):
|
25
71
|
"""
|
26
72
|
Handler for /proxy endpoint.
|
@@ -154,91 +200,8 @@ class ProxyHandler(JupyterHandler):
|
|
154
200
|
class WebSocketProxyHandler(tornado.websocket.WebSocketHandler):
|
155
201
|
"""
|
156
202
|
WebSocket proxy handler that forwards connections from /ws to target server
|
157
|
-
Enhanced with comprehensive logging for debugging intermittent connection issues
|
158
203
|
"""
|
159
204
|
|
160
|
-
# Class-level connection tracking
|
161
|
-
_connection_count = 0
|
162
|
-
_active_connections = {}
|
163
|
-
|
164
|
-
def _log_system_state(self, context=""):
|
165
|
-
"""Log current system state for debugging"""
|
166
|
-
try:
|
167
|
-
# System resource information
|
168
|
-
cpu_percent = psutil.cpu_percent(interval=0.1)
|
169
|
-
memory = psutil.virtual_memory()
|
170
|
-
|
171
|
-
print(f"[ESCOBAR-WS] === SYSTEM STATE {context} ===")
|
172
|
-
print(f"[ESCOBAR-WS] CPU Usage: {cpu_percent}%")
|
173
|
-
print(f"[ESCOBAR-WS] Memory Usage: {memory.percent}% ({memory.used / 1024 / 1024:.1f}MB used)")
|
174
|
-
print(f"[ESCOBAR-WS] Active Connections: {len(self._active_connections)}")
|
175
|
-
print(f"[ESCOBAR-WS] Total Connections Created: {self._connection_count}")
|
176
|
-
print(f"[ESCOBAR-WS] Current Thread: {threading.current_thread().name}")
|
177
|
-
print(f"[ESCOBAR-WS] === END SYSTEM STATE ===")
|
178
|
-
except Exception as e:
|
179
|
-
print(f"[ESCOBAR-WS] Error logging system state: {e}")
|
180
|
-
|
181
|
-
def _log_connection_lifecycle(self, event, details=None):
|
182
|
-
"""Log connection lifecycle events with timing"""
|
183
|
-
timestamp = time.time()
|
184
|
-
connection_id = getattr(self, 'connection_id', 'UNKNOWN')
|
185
|
-
|
186
|
-
print(f"[ESCOBAR-WS] === CONNECTION LIFECYCLE EVENT ===")
|
187
|
-
print(f"[ESCOBAR-WS] Event: {event}")
|
188
|
-
print(f"[ESCOBAR-WS] Connection ID: {connection_id}")
|
189
|
-
print(f"[ESCOBAR-WS] Timestamp: {timestamp}")
|
190
|
-
print(f"[ESCOBAR-WS] Human Time: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(timestamp))}")
|
191
|
-
if details:
|
192
|
-
print(f"[ESCOBAR-WS] Details: {details}")
|
193
|
-
print(f"[ESCOBAR-WS] === END LIFECYCLE EVENT ===")
|
194
|
-
|
195
|
-
def _log_message_timing(self, direction, message_size, start_time=None):
|
196
|
-
"""Log message timing and throughput"""
|
197
|
-
current_time = time.time()
|
198
|
-
|
199
|
-
print(f"[ESCOBAR-WS] === MESSAGE TIMING ===")
|
200
|
-
print(f"[ESCOBAR-WS] Direction: {direction}")
|
201
|
-
print(f"[ESCOBAR-WS] Message Size: {message_size} bytes")
|
202
|
-
print(f"[ESCOBAR-WS] Timestamp: {current_time}")
|
203
|
-
|
204
|
-
if start_time:
|
205
|
-
duration = current_time - start_time
|
206
|
-
throughput = message_size / duration if duration > 0 else 0
|
207
|
-
print(f"[ESCOBAR-WS] Processing Duration: {duration:.4f}s")
|
208
|
-
print(f"[ESCOBAR-WS] Throughput: {throughput:.2f} bytes/sec")
|
209
|
-
|
210
|
-
print(f"[ESCOBAR-WS] === END MESSAGE TIMING ===")
|
211
|
-
|
212
|
-
def _log_target_connection_health(self):
|
213
|
-
"""Log target connection health metrics"""
|
214
|
-
if not self.target_ws:
|
215
|
-
print(f"[ESCOBAR-WS] === TARGET HEALTH: NO CONNECTION ===")
|
216
|
-
return
|
217
|
-
|
218
|
-
print(f"[ESCOBAR-WS] === TARGET CONNECTION HEALTH ===")
|
219
|
-
try:
|
220
|
-
print(f"[ESCOBAR-WS] Target State: {self.target_ws.state}")
|
221
|
-
print(f"[ESCOBAR-WS] Target State Name: {self.target_ws.state.name}")
|
222
|
-
print(f"[ESCOBAR-WS] Target State Value: {self.target_ws.state.value}")
|
223
|
-
|
224
|
-
# Check if we can access additional properties
|
225
|
-
if hasattr(self.target_ws, 'ping_interval'):
|
226
|
-
print(f"[ESCOBAR-WS] Ping Interval: {self.target_ws.ping_interval}")
|
227
|
-
if hasattr(self.target_ws, 'ping_timeout'):
|
228
|
-
print(f"[ESCOBAR-WS] Ping Timeout: {self.target_ws.ping_timeout}")
|
229
|
-
if hasattr(self.target_ws, 'close_timeout'):
|
230
|
-
print(f"[ESCOBAR-WS] Close Timeout: {self.target_ws.close_timeout}")
|
231
|
-
|
232
|
-
except Exception as e:
|
233
|
-
print(f"[ESCOBAR-WS] Error checking target health: {e}")
|
234
|
-
|
235
|
-
print(f"[ESCOBAR-WS] === END TARGET HEALTH ===")
|
236
|
-
|
237
|
-
def _generate_connection_id(self):
|
238
|
-
"""Generate unique connection ID for tracking"""
|
239
|
-
WebSocketProxyHandler._connection_count += 1
|
240
|
-
return f"CONN-{WebSocketProxyHandler._connection_count}-{int(time.time())}"
|
241
|
-
|
242
205
|
def _resolve_target_url_for_docker(self, url):
|
243
206
|
"""
|
244
207
|
Resolve target URL for Docker environment.
|
@@ -269,16 +232,11 @@ class WebSocketProxyHandler(tornado.websocket.WebSocketHandler):
|
|
269
232
|
# Determine if we're in Docker
|
270
233
|
is_docker = dockerenv_exists or docker_env or cgroup_docker
|
271
234
|
|
272
|
-
print(f"[ESCOBAR-WS] Docker detection indicators: {docker_indicators}")
|
273
|
-
print(f"[ESCOBAR-WS] Final Docker detection result: {is_docker}")
|
274
|
-
|
275
235
|
if not is_docker:
|
276
|
-
print(f"[ESCOBAR-WS] Not in Docker container, using original URL: {url}")
|
277
236
|
return url
|
278
237
|
|
279
238
|
# Parse the URL to extract components
|
280
239
|
parsed = urlparse(url)
|
281
|
-
print(f"[ESCOBAR-WS] Parsed URL - hostname: '{parsed.hostname}', netloc: '{parsed.netloc}'")
|
282
240
|
|
283
241
|
# Check if hostname is localhost or 127.0.0.1
|
284
242
|
if parsed.hostname in ['localhost', '127.0.0.1']:
|
@@ -287,10 +245,7 @@ class WebSocketProxyHandler(tornado.websocket.WebSocketHandler):
|
|
287
245
|
new_parsed = parsed._replace(netloc=new_netloc)
|
288
246
|
new_url = urlunparse(new_parsed)
|
289
247
|
|
290
|
-
print(f"[ESCOBAR-WS] Docker hostname resolution: {url} → {new_url}")
|
291
248
|
return new_url
|
292
|
-
else:
|
293
|
-
print(f"[ESCOBAR-WS] Docker container detected, but hostname '{parsed.hostname}' is not localhost/127.0.0.1, keeping original: {url}")
|
294
249
|
|
295
250
|
return url
|
296
251
|
|
@@ -299,25 +254,6 @@ class WebSocketProxyHandler(tornado.websocket.WebSocketHandler):
|
|
299
254
|
super().__init__(*args, **kwargs)
|
300
255
|
self.target_ws = None
|
301
256
|
|
302
|
-
# Generate unique connection ID for tracking
|
303
|
-
self.connection_id = self._generate_connection_id()
|
304
|
-
self.connection_start_time = time.time()
|
305
|
-
|
306
|
-
# Add to active connections tracking
|
307
|
-
WebSocketProxyHandler._active_connections[self.connection_id] = {
|
308
|
-
'start_time': self.connection_start_time,
|
309
|
-
'handler': self
|
310
|
-
}
|
311
|
-
|
312
|
-
# Log connection creation
|
313
|
-
self._log_connection_lifecycle("HANDLER_CREATED", {
|
314
|
-
'connection_id': self.connection_id,
|
315
|
-
'total_connections': len(WebSocketProxyHandler._active_connections)
|
316
|
-
})
|
317
|
-
|
318
|
-
# Log system state at connection creation
|
319
|
-
self._log_system_state("CONNECTION_INIT")
|
320
|
-
|
321
257
|
# Debug environment information
|
322
258
|
print(f"[ESCOBAR-WS] Environment WEBSOCKET_PROXY_TARGET: {os.getenv('WEBSOCKET_PROXY_TARGET', 'NOT_SET')}")
|
323
259
|
print(f"[ESCOBAR-WS] Running in container: {os.path.exists('/.dockerenv')}")
|
@@ -336,39 +272,28 @@ class WebSocketProxyHandler(tornado.websocket.WebSocketHandler):
|
|
336
272
|
print(f"[ESCOBAR-WS] All WEBSOCKET environment vars: {websocket_env_vars}")
|
337
273
|
|
338
274
|
self.is_closing = False
|
339
|
-
self.message_count_sent = 0
|
340
|
-
self.message_count_received = 0
|
341
|
-
self.last_activity_time = time.time()
|
342
275
|
|
343
276
|
def _get_user_bonnie_url(self):
|
344
277
|
"""
|
345
278
|
Get user-configured Bonnie URL from bonnie_url query parameter.
|
346
279
|
This allows the frontend to override the environment variable.
|
347
280
|
"""
|
348
|
-
print(f"[ESCOBAR-WS] === READING USER BONNIE URL FROM QUERY PARAMETER ===")
|
349
|
-
|
350
281
|
try:
|
351
282
|
# Get the bonnieUrl from query parameters
|
352
283
|
bonnie_url = self.get_argument('bonnie_url', None)
|
353
|
-
print(f"[ESCOBAR-WS] bonnie_url query parameter value: '{bonnie_url}'")
|
354
284
|
|
355
285
|
if bonnie_url and bonnie_url.strip():
|
356
286
|
# Validate that it's a WebSocket URL
|
357
287
|
if bonnie_url.startswith(('ws://', 'wss://')):
|
358
|
-
print(f"[ESCOBAR-WS] Valid user Bonnie URL from query parameter: {bonnie_url}")
|
359
288
|
return bonnie_url.strip()
|
360
289
|
else:
|
361
|
-
print(f"[ESCOBAR-WS] Invalid Bonnie URL format (must start with ws:// or wss://): {bonnie_url}")
|
362
290
|
return None
|
363
291
|
else:
|
364
|
-
print(f"[ESCOBAR-WS] No bonnie_url query parameter provided")
|
365
292
|
return None
|
366
293
|
|
367
294
|
except Exception as e:
|
368
295
|
print(f"[ESCOBAR-WS] Error reading bonnie_url query parameter: {e}")
|
369
296
|
return None
|
370
|
-
finally:
|
371
|
-
print(f"[ESCOBAR-WS] === END BONNIE URL QUERY PARAMETER READ ===")
|
372
297
|
|
373
298
|
def _get_target_url(self):
|
374
299
|
"""
|
@@ -482,136 +407,38 @@ class WebSocketProxyHandler(tornado.websocket.WebSocketHandler):
|
|
482
407
|
|
483
408
|
async def on_message(self, message):
|
484
409
|
"""Called when a message is received from the client"""
|
485
|
-
print(f"[ESCOBAR-WS] === CLIENT MESSAGE RECEIVED ===")
|
486
|
-
print(f"[ESCOBAR-WS] Message length: {len(message)}")
|
487
|
-
print(f"[ESCOBAR-WS] Message type: {type(message)}")
|
488
|
-
print(f"[ESCOBAR-WS] Message preview: {message[:500]}...")
|
489
|
-
print(f"[ESCOBAR-WS] Target WS exists: {self.target_ws is not None}")
|
490
|
-
print(f"[ESCOBAR-WS] Is closing: {self.is_closing}")
|
491
|
-
|
492
410
|
if self.target_ws and not self.is_closing:
|
493
|
-
# Enhanced debugging before forwarding
|
494
|
-
print(f"[ESCOBAR-WS] === PRE-FORWARD DIAGNOSTICS ===")
|
495
|
-
print(f"[ESCOBAR-WS] Target WS type: {type(self.target_ws)}")
|
496
|
-
print(f"[ESCOBAR-WS] Target WS state: {getattr(self.target_ws, 'state', 'NO_STATE_ATTR')}")
|
497
|
-
|
498
|
-
# Check if connection is actually open
|
499
|
-
try:
|
500
|
-
is_open = self.target_ws.state.name == 'OPEN'
|
501
|
-
print(f"[ESCOBAR-WS] Target WS is OPEN: {is_open}")
|
502
|
-
except AttributeError:
|
503
|
-
print(f"[ESCOBAR-WS] Cannot check target WS state - no state attribute")
|
504
|
-
is_open = True # Assume open and let send() fail with proper error
|
505
|
-
|
506
|
-
# Check connection properties
|
507
|
-
try:
|
508
|
-
print(f"[ESCOBAR-WS] Target WS closed property: {getattr(self.target_ws, 'closed', 'NO_CLOSED_ATTR')}")
|
509
|
-
except:
|
510
|
-
print(f"[ESCOBAR-WS] Cannot access target WS closed property")
|
511
|
-
|
512
|
-
# Message type analysis
|
513
|
-
if isinstance(message, str):
|
514
|
-
print(f"[ESCOBAR-WS] Message is TEXT (string)")
|
515
|
-
elif isinstance(message, bytes):
|
516
|
-
print(f"[ESCOBAR-WS] Message is BINARY (bytes)")
|
517
|
-
else:
|
518
|
-
print(f"[ESCOBAR-WS] Message is UNKNOWN type: {type(message)}")
|
519
|
-
|
520
|
-
print(f"[ESCOBAR-WS] === ATTEMPTING MESSAGE FORWARD ===")
|
521
|
-
|
522
411
|
try:
|
523
412
|
# Forward message to target server
|
524
413
|
await self.target_ws.send(message)
|
525
|
-
print(f"[ESCOBAR-WS] ✅ Message successfully forwarded to target")
|
526
414
|
logging.debug(f"Forwarded message to target: {message[:100]}...")
|
527
415
|
except Exception as e:
|
528
|
-
print(f"[ESCOBAR-WS]
|
529
|
-
print(f"[ESCOBAR-WS]
|
530
|
-
print(f"[ESCOBAR-WS]
|
531
|
-
print(f"[ESCOBAR-WS] Exception args: {getattr(e, 'args', 'NO_ARGS')}")
|
532
|
-
|
533
|
-
# Check target connection state after error
|
534
|
-
try:
|
535
|
-
print(f"[ESCOBAR-WS] Target WS state after error: {self.target_ws.state}")
|
536
|
-
except:
|
537
|
-
print(f"[ESCOBAR-WS] Cannot check target WS state after error")
|
538
|
-
|
539
|
-
# Additional exception details
|
540
|
-
if hasattr(e, '__dict__'):
|
541
|
-
print(f"[ESCOBAR-WS] Exception attributes: {e.__dict__}")
|
542
|
-
if hasattr(e, 'errno'):
|
543
|
-
print(f"[ESCOBAR-WS] Errno: {e.errno}")
|
544
|
-
if hasattr(e, 'strerror'):
|
545
|
-
print(f"[ESCOBAR-WS] Strerror: {e.strerror}")
|
546
|
-
|
547
|
-
# Import specific exception types for better diagnosis
|
548
|
-
import websockets.exceptions
|
549
|
-
if isinstance(e, websockets.exceptions.ConnectionClosed):
|
550
|
-
print(f"[ESCOBAR-WS] ConnectionClosed - code: {e.code}, reason: {e.reason}")
|
551
|
-
elif isinstance(e, websockets.exceptions.InvalidState):
|
552
|
-
print(f"[ESCOBAR-WS] InvalidState - connection in wrong state")
|
553
|
-
elif isinstance(e, websockets.exceptions.PayloadTooBig):
|
554
|
-
print(f"[ESCOBAR-WS] PayloadTooBig - message too large")
|
555
|
-
|
416
|
+
print(f"[ESCOBAR-WS] ERROR forwarding message to target:")
|
417
|
+
print(f"[ESCOBAR-WS] Error: {str(e)}")
|
418
|
+
print(f"[ESCOBAR-WS] Error type: {type(e).__name__}")
|
556
419
|
logging.error(f"Error forwarding message to target: {str(e)}")
|
557
|
-
|
558
|
-
# Don't close immediately - let's see if we can recover
|
559
|
-
print(f"[ESCOBAR-WS] Closing client connection due to forward error")
|
560
|
-
self.close(code=1011, reason=f"Target forward error: {type(e).__name__}")
|
561
|
-
else:
|
562
|
-
print(f"[ESCOBAR-WS] Cannot forward message - target_ws: {self.target_ws}, is_closing: {self.is_closing}")
|
563
|
-
print(f"[ESCOBAR-WS] === END CLIENT MESSAGE ===")
|
420
|
+
self.close(code=1011, reason="Target connection error")
|
564
421
|
|
565
422
|
async def _forward_from_target(self):
|
566
423
|
"""Forward messages from target server to client"""
|
567
|
-
print(f"[ESCOBAR-WS] === STARTING TARGET MESSAGE FORWARDING ===")
|
568
|
-
print(f"[ESCOBAR-WS] Target WS state: {self.target_ws.state if self.target_ws else 'None'}")
|
569
|
-
|
570
424
|
try:
|
571
|
-
message_count = 0
|
572
425
|
async for message in self.target_ws:
|
573
|
-
message_count += 1
|
574
|
-
print(f"[ESCOBAR-WS] === TARGET MESSAGE #{message_count} ===")
|
575
|
-
print(f"[ESCOBAR-WS] Message length: {len(message)}")
|
576
|
-
print(f"[ESCOBAR-WS] Message type: {type(message)}")
|
577
|
-
print(f"[ESCOBAR-WS] Message preview: {message[:500]}...")
|
578
|
-
print(f"[ESCOBAR-WS] Is closing: {self.is_closing}")
|
579
|
-
|
580
426
|
if not self.is_closing:
|
581
|
-
print(f"[ESCOBAR-WS] Forwarding message to client")
|
582
427
|
# Forward message to client
|
583
428
|
self.write_message(message)
|
584
|
-
print(f"[ESCOBAR-WS] Message successfully forwarded to client")
|
585
429
|
logging.debug(f"Forwarded message from target: {message[:100]}...")
|
586
430
|
else:
|
587
|
-
print(f"[ESCOBAR-WS] Breaking forwarding loop - connection is closing")
|
588
431
|
break
|
589
|
-
print(f"[ESCOBAR-WS] === END TARGET MESSAGE #{message_count} ===")
|
590
432
|
|
591
433
|
except websockets.exceptions.ConnectionClosed as e:
|
592
|
-
print(f"[ESCOBAR-WS] === TARGET CONNECTION CLOSED DETAILS ===")
|
593
|
-
print(f"[ESCOBAR-WS] Target websocket connection closed")
|
594
|
-
print(f"[ESCOBAR-WS] Close code: {getattr(e, 'code', 'NO_CODE')}")
|
595
|
-
print(f"[ESCOBAR-WS] Close reason: {getattr(e, 'reason', 'NO_REASON')}")
|
596
|
-
print(f"[ESCOBAR-WS] Exception type: {type(e).__name__}")
|
597
|
-
print(f"[ESCOBAR-WS] Exception str: {str(e)}")
|
598
|
-
print(f"[ESCOBAR-WS] Target WS final state: {getattr(self.target_ws, 'state', 'NO_STATE') if self.target_ws else 'NO_TARGET_WS'}")
|
599
|
-
print(f"[ESCOBAR-WS] Messages processed before disconnect: {message_count}")
|
600
|
-
print(f"[ESCOBAR-WS] === END TARGET CONNECTION CLOSED DETAILS ===")
|
601
|
-
|
602
434
|
logging.info(f"Target websocket connection closed - code: {getattr(e, 'code', 'NO_CODE')}, reason: {getattr(e, 'reason', 'NO_REASON')}")
|
603
435
|
|
604
436
|
if not self.is_closing:
|
605
|
-
print(f"[ESCOBAR-WS] Closing client connection due to target disconnect")
|
606
437
|
self.close(code=1011, reason="Target server disconnected")
|
607
438
|
except Exception as e:
|
608
439
|
print(f"[ESCOBAR-WS] === TARGET MESSAGE FORWARDING ERROR ===")
|
609
440
|
print(f"[ESCOBAR-WS] Error: {str(e)}")
|
610
441
|
print(f"[ESCOBAR-WS] Error type: {type(e).__name__}")
|
611
|
-
print(f"[ESCOBAR-WS] Messages processed before error: {message_count}")
|
612
|
-
print(f"[ESCOBAR-WS] Target WS state: {getattr(self.target_ws, 'state', 'NO_STATE') if self.target_ws else 'NO_TARGET_WS'}")
|
613
|
-
if hasattr(e, '__dict__'):
|
614
|
-
print(f"[ESCOBAR-WS] Error attributes: {e.__dict__}")
|
615
442
|
if hasattr(e, 'errno'):
|
616
443
|
print(f"[ESCOBAR-WS] Errno: {e.errno}")
|
617
444
|
if hasattr(e, 'strerror'):
|
@@ -620,66 +447,37 @@ class WebSocketProxyHandler(tornado.websocket.WebSocketHandler):
|
|
620
447
|
|
621
448
|
logging.error(f"Error receiving from target websocket: {str(e)}")
|
622
449
|
if not self.is_closing:
|
623
|
-
print(f"[ESCOBAR-WS] Closing client connection due to target error")
|
624
450
|
self.close(code=1011, reason="Target connection error")
|
625
|
-
|
626
|
-
print(f"[ESCOBAR-WS] === TARGET MESSAGE FORWARDING ENDED ===")
|
627
451
|
|
628
452
|
def on_close(self):
|
629
453
|
"""Called when websocket connection is closed"""
|
630
|
-
print(f"[ESCOBAR-WS] === CLIENT CONNECTION CLOSED ===")
|
631
|
-
print(f"[ESCOBAR-WS] Setting is_closing flag to True")
|
632
454
|
self.is_closing = True
|
633
|
-
print(f"[ESCOBAR-WS] Target WS exists: {self.target_ws is not None}")
|
634
455
|
|
635
456
|
logging.info("WebSocket connection closed")
|
636
457
|
|
637
458
|
# Close target connection if it exists
|
638
459
|
if self.target_ws:
|
639
|
-
print(f"[ESCOBAR-WS] Scheduling target connection cleanup")
|
640
460
|
asyncio.create_task(self._close_target_connection())
|
641
|
-
else:
|
642
|
-
print(f"[ESCOBAR-WS] No target connection to clean up")
|
643
|
-
|
644
|
-
print(f"[ESCOBAR-WS] === END CLIENT CONNECTION CLOSED ===")
|
645
461
|
|
646
462
|
async def _close_target_connection(self):
|
647
463
|
"""Safely close the target websocket connection"""
|
648
|
-
print(f"[ESCOBAR-WS] === CLOSING TARGET CONNECTION ===")
|
649
464
|
try:
|
650
465
|
if self.target_ws:
|
651
|
-
print(f"[ESCOBAR-WS] Target WS exists, checking state")
|
652
|
-
print(f"[ESCOBAR-WS] Target WS type: {type(self.target_ws)}")
|
653
|
-
print(f"[ESCOBAR-WS] Target WS state: {getattr(self.target_ws, 'state', 'NO_STATE_ATTR')}")
|
654
|
-
|
655
466
|
# Check if connection is already closed using the correct websockets library API
|
656
467
|
try:
|
657
468
|
is_closed = self.target_ws.state.name == 'CLOSED'
|
658
|
-
print(f"[ESCOBAR-WS] Target WS is closed: {is_closed}")
|
659
469
|
except AttributeError:
|
660
470
|
# Fallback: just try to close it regardless of state
|
661
|
-
print(f"[ESCOBAR-WS] Cannot check state, will attempt to close anyway")
|
662
471
|
is_closed = False
|
663
472
|
|
664
473
|
if not is_closed:
|
665
|
-
print(f"[ESCOBAR-WS] Target WS is open, closing it")
|
666
474
|
await self.target_ws.close()
|
667
|
-
print(f"[ESCOBAR-WS] Target connection closed successfully")
|
668
475
|
logging.info("Target websocket connection closed")
|
669
|
-
else:
|
670
|
-
print(f"[ESCOBAR-WS] Target WS already closed")
|
671
|
-
else:
|
672
|
-
print(f"[ESCOBAR-WS] No target WS to close")
|
673
476
|
except Exception as e:
|
674
477
|
print(f"[ESCOBAR-WS] ERROR closing target connection:")
|
675
478
|
print(f"[ESCOBAR-WS] Error: {str(e)}")
|
676
479
|
print(f"[ESCOBAR-WS] Error type: {type(e).__name__}")
|
677
|
-
print(f"[ESCOBAR-WS] Target WS type: {type(self.target_ws) if self.target_ws else 'None'}")
|
678
|
-
if hasattr(e, '__dict__'):
|
679
|
-
print(f"[ESCOBAR-WS] Error attributes: {e.__dict__}")
|
680
480
|
logging.error(f"Error closing target websocket: {str(e)}")
|
681
|
-
|
682
|
-
print(f"[ESCOBAR-WS] === END CLOSING TARGET CONNECTION ===")
|
683
481
|
|
684
482
|
|
685
483
|
def setup_handlers(web_app):
|
@@ -747,9 +545,14 @@ def setup_handlers(web_app):
|
|
747
545
|
for pattern in ws_patterns:
|
748
546
|
print(f"[ESCOBAR-WS] - {pattern}")
|
749
547
|
|
548
|
+
# Register the OAuth callback endpoint
|
549
|
+
oauth_callback_pattern = url_path_join(base_url, "static", "oauth-callback.html")
|
550
|
+
print(f"[ESCOBAR-WS] OAuth callback pattern: {oauth_callback_pattern}")
|
551
|
+
|
750
552
|
# Build handlers list with all WebSocket patterns
|
751
553
|
handlers = [
|
752
554
|
(proxy_pattern, ProxyHandler),
|
555
|
+
(oauth_callback_pattern, OAuthCallbackHandler),
|
753
556
|
*[(pattern, WebSocketProxyHandler) for pattern in ws_patterns]
|
754
557
|
]
|
755
558
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "escobar",
|
3
|
-
"version": "0.1.
|
3
|
+
"version": "0.1.91",
|
4
4
|
"description": "AI CHAT EXTENSION",
|
5
5
|
"keywords": [
|
6
6
|
"jupyter",
|
@@ -123,7 +123,7 @@
|
|
123
123
|
"outputDir": "escobar/labextension",
|
124
124
|
"schemaDir": "schema",
|
125
125
|
"_build": {
|
126
|
-
"load": "static/remoteEntry.
|
126
|
+
"load": "static/remoteEntry.48a97be89d680e21e498.js",
|
127
127
|
"extension": "./extension",
|
128
128
|
"style": "./style"
|
129
129
|
}
|
@@ -38,8 +38,8 @@
|
|
38
38
|
},
|
39
39
|
"googleClientId": {
|
40
40
|
"type": "string",
|
41
|
-
"title": "Google
|
42
|
-
"description": "Google OAuth
|
41
|
+
"title": "Google Client ID",
|
42
|
+
"description": "Google OAuth Client ID for authentication. Get this from Google Cloud Console.",
|
43
43
|
"default": ""
|
44
44
|
}
|
45
45
|
},
|