Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In html-embedded Javascript, opening an EventSource to the server breaks Chrome's Sources and Network tabs in DevTools?

I've been working on a new JavaScript project to render a frontend for a bunch of data that is being collected in Python and streamed to the frontend. Something that's been causing me a lot of trouble is the fact that the Chrome DevTools don't work properly while this stream is open. For example, if I bring up the Sources tab no sources are displayed. If I bring up the Network tab, no connections are displayed there.

While the Sources tab is open, if I kill the backend and thereby kill the stream's TCP connection, the source for the page pops up. Similarly, the Network tab shows nothing while the stream is open. Not only does it not show the running stream (which I know is open because the updates are being displayed in the page) but it doesn't even show the load of localhost:8000/. Killing the backend causes the Network tab to display the failed load of favicon.ico and all the retries of the stream, but not the initial load of / nor the initial run of the stream.

I've stripped this down to a very simple repro example.

#!/usr/bin/env python3

from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from threading import Thread
import time
from urllib.parse import urlparse, parse_qs

index = '''
<html>
<head>
<title>devtools lockup demo</title>
</head>
<body>
<div id='counter'>No data received yet.</div>
<script type='text/javascript' defer>

/*TODO: this doesn't really need to be a class.*/
class DataRelay {
  constructor() {
    const stream_url = '/stream/';
    this.event_source = new EventSource(stream_url);
    this.event_source.onmessage = (event) => {
      document.getElementById('counter').textContent = event.data;
    };
    this.event_source.onerror = (error) => {
      console.error('event_source.onerror:', error);
    };
    console.log('data stream handler is set up');
  }
}

let data_relay = new DataRelay();
</script>
</body>
'''

def encode_as_wire_message(data):
  # The "data: " preamble, the "\n\n" terminator, and the utf8 encoding are all
  # mandatory for streams.
  return bytes('data: ' + data + '\n\n', 'utf8')

#TODO: Get this constant in the class
class RequestHandler(BaseHTTPRequestHandler):
  def add_misc_headers(self, content_type):
    self.send_header('Content-type', content_type)
    self.send_header('Cache-Control', 'no-cache')
    self.send_header('Connection', 'keep-alive')
    self.send_header('Access-Control-Allow-Credentials', 'true')
    self.send_header('Access-Control-Allow-Origin', '*')

  def serve_index(self):
    self.send_response(200)
    self.add_misc_headers('text/html')
    self.end_headers()

    self.wfile.write(bytes(index, 'utf8'))

  def serve_stream(self):
    self.send_response(200)
    self.add_misc_headers('text/event-stream')
    self.end_headers()

    print('Beginning to serve stream...')

    for x in range(1000000):
      message = encode_as_wire_message(str(x))
      print(message)
      self.wfile.write(message)
      self.wfile.flush()
      time.sleep(1.0)

  def do_GET(self):
    parsed_url = urlparse(self.path)
    if parsed_url.path == '/':
      self.serve_index()
    elif parsed_url.path == '/stream/':
      self.serve_stream()

def run(server_class=ThreadingHTTPServer, handler_class=RequestHandler):
  server_address = ('', 8000) # serve on all interfaces, port 8000
  httpd = server_class(server_address, handler_class)
  print('starting httpd... Open a connection to http://localhost:8000')
  httpd.serve_forever()

run()

Edit

The networks tab is the least of the concerns. Without the source tab, I cannot debug. Since I've established that the network connection is working, I can see the exact details of what is being sent without the network tab just by looking at the live log of the backend or with tcpdump.

like image 863
Sniggerfardimungus Avatar asked Dec 09 '25 16:12

Sniggerfardimungus


1 Answers

The networks tab of Chrome doesn't store the history unless you open it. What you should do is open the Devtools and the network tab before loading the url localhost:8000.

Or, you can also add a button that manually starts the stream instead of automatically doing when page loads. This way, you can open the devtools and once the Network Tab is open, you click the button and you'll be able to see the stream

You can replace the body tag in your index HTML with:

<body>
<h1>devtools lockup demo. Open the devtools networks tab and click the start button</h1>
<button onclick='startStream()'>Start stream</button>
<div id='counter'>No data received yet.</div>
<script type='text/javascript' defer>

/*TODO: this doesn't really need to be a class.*/
class DataRelay {
  constructor() {
    const stream_url = '/stream/';
    this.event_source = new EventSource(stream_url);
    this.event_source.onmessage = (event) => {
      document.getElementById('counter').textContent = event.data;
    };
    this.event_source.onerror = (error) => {
      console.error('event_source.onerror:', error);
    };
    console.log('data stream handler is set up');
  }
}
const startStream = () => {
let data_relay = new DataRelay();
};
</script>
</body>
like image 139
Manas Ashwin Avatar answered Dec 11 '25 06:12

Manas Ashwin