cduss Claude Opus 4.5 commited on
Commit
9389ef0
·
1 Parent(s): 83671f4

Use fetch-based SSE with Authorization headers for private Space

Browse files

Switch from EventSource to fetch with ReadableStream for SSE connection.
This allows adding Authorization headers which are required for private
HuggingFace Spaces.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Files changed (1) hide show
  1. index.html +65 -42
index.html CHANGED
@@ -221,7 +221,6 @@
221
  const SIGNALING_SERVER = 'https://cduss-reachy-mini-central.hf.space';
222
 
223
  // State
224
- let eventSource = null;
225
  let peerConnection = null;
226
  let dataChannel = null;
227
  let selectedProducerId = null;
@@ -338,7 +337,10 @@
338
  try {
339
  const response = await fetch(`${SIGNALING_SERVER}/send?token=${encodeURIComponent(userToken)}`, {
340
  method: 'POST',
341
- headers: { 'Content-Type': 'application/json' },
 
 
 
342
  body: JSON.stringify(message)
343
  });
344
 
@@ -354,8 +356,10 @@
354
  }
355
  }
356
 
357
- // Signaling via SSE
358
- function connectSignaling() {
 
 
359
  if (!userToken) {
360
  log('Not authenticated', 'error');
361
  return;
@@ -365,56 +369,75 @@
365
  log('Connecting to signaling server (SSE)...');
366
  updateSignalingStatus('connecting');
367
 
 
 
368
  try {
369
- eventSource = new EventSource(url);
370
-
371
- eventSource.onopen = () => {
372
- log('Connected to signaling server!', 'success');
373
- updateSignalingStatus('connected');
374
- document.getElementById('connectBtn').disabled = true;
375
- document.getElementById('disconnectBtn').disabled = false;
376
- };
377
-
378
- eventSource.addEventListener('message', (event) => {
379
- try {
380
- const msg = JSON.parse(event.data);
381
- log(`Received: ${msg.type}`);
382
- handleSignalingMessage(msg);
383
- } catch (e) {
384
- console.error('Failed to parse message:', e);
385
- }
386
  });
387
 
388
- eventSource.addEventListener('ping', () => {
389
- // Keepalive, ignore
390
- });
391
 
392
- eventSource.onerror = (error) => {
393
- log('Connection error', 'error');
394
- updateSignalingStatus('disconnected');
395
- document.getElementById('connectBtn').disabled = false;
396
- document.getElementById('disconnectBtn').disabled = true;
397
- document.getElementById('startStreamBtn').disabled = true;
398
- selectedProducerId = null;
399
- myPeerId = null;
400
-
401
- // Auto-reconnect after 5 seconds
402
- if (eventSource) {
403
- eventSource.close();
404
- eventSource = null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
405
  }
406
- };
407
 
408
  } catch (e) {
409
- log(`Failed to connect: ${e.message}`, 'error');
 
 
 
 
410
  updateSignalingStatus('disconnected');
 
 
 
 
 
411
  }
412
  }
413
 
414
  function disconnectSignaling() {
415
- if (eventSource) {
416
- eventSource.close();
417
- eventSource = null;
418
  }
419
  updateSignalingStatus('disconnected');
420
  document.getElementById('connectBtn').disabled = false;
 
221
  const SIGNALING_SERVER = 'https://cduss-reachy-mini-central.hf.space';
222
 
223
  // State
 
224
  let peerConnection = null;
225
  let dataChannel = null;
226
  let selectedProducerId = null;
 
337
  try {
338
  const response = await fetch(`${SIGNALING_SERVER}/send?token=${encodeURIComponent(userToken)}`, {
339
  method: 'POST',
340
+ headers: {
341
+ 'Content-Type': 'application/json',
342
+ 'Authorization': `Bearer ${userToken}`,
343
+ },
344
  body: JSON.stringify(message)
345
  });
346
 
 
356
  }
357
  }
358
 
359
+ // Signaling via SSE (using fetch for header support)
360
+ let sseAbortController = null;
361
+
362
+ async function connectSignaling() {
363
  if (!userToken) {
364
  log('Not authenticated', 'error');
365
  return;
 
369
  log('Connecting to signaling server (SSE)...');
370
  updateSignalingStatus('connecting');
371
 
372
+ sseAbortController = new AbortController();
373
+
374
  try {
375
+ const response = await fetch(url, {
376
+ method: 'GET',
377
+ headers: {
378
+ 'Authorization': `Bearer ${userToken}`,
379
+ 'Accept': 'text/event-stream',
380
+ },
381
+ signal: sseAbortController.signal,
 
 
 
 
 
 
 
 
 
 
382
  });
383
 
384
+ if (!response.ok) {
385
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
386
+ }
387
 
388
+ log('Connected to signaling server!', 'success');
389
+ updateSignalingStatus('connected');
390
+ document.getElementById('connectBtn').disabled = true;
391
+ document.getElementById('disconnectBtn').disabled = false;
392
+
393
+ // Read the SSE stream
394
+ const reader = response.body.getReader();
395
+ const decoder = new TextDecoder();
396
+ let buffer = '';
397
+
398
+ while (true) {
399
+ const { done, value } = await reader.read();
400
+ if (done) break;
401
+
402
+ buffer += decoder.decode(value, { stream: true });
403
+ const lines = buffer.split('\n');
404
+ buffer = lines.pop(); // Keep incomplete line in buffer
405
+
406
+ for (const line of lines) {
407
+ if (line.startsWith('data:')) {
408
+ const data = line.slice(5).trim();
409
+ if (data) {
410
+ try {
411
+ const msg = JSON.parse(data);
412
+ log(`Received: ${msg.type}`);
413
+ handleSignalingMessage(msg);
414
+ } catch (e) {
415
+ console.error('Failed to parse:', data, e);
416
+ }
417
+ }
418
+ }
419
  }
420
+ }
421
 
422
  } catch (e) {
423
+ if (e.name === 'AbortError') {
424
+ log('Disconnected', 'info');
425
+ } else {
426
+ log(`Connection error: ${e.message}`, 'error');
427
+ }
428
  updateSignalingStatus('disconnected');
429
+ document.getElementById('connectBtn').disabled = false;
430
+ document.getElementById('disconnectBtn').disabled = true;
431
+ document.getElementById('startStreamBtn').disabled = true;
432
+ selectedProducerId = null;
433
+ myPeerId = null;
434
  }
435
  }
436
 
437
  function disconnectSignaling() {
438
+ if (sseAbortController) {
439
+ sseAbortController.abort();
440
+ sseAbortController = null;
441
  }
442
  updateSignalingStatus('disconnected');
443
  document.getElementById('connectBtn').disabled = false;