Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting net::ERR_INCOMPLETE_CHUNKED_ENCODING 200 when consuming event-stream using EventSource in ReactJs

I have a very simple node service exposing an endpoint aimed to use Server Send Events (SSE) connection and a very basic ReactJs client consuming it via EventSource.onmessage.

Firstly, when I set a debug point in updateAmountState (Chrome Dev) I can't see it evoked.

Secondly, I am getting net::ERR_INCOMPLETE_CHUNKED_ENCODING 200 (OK). According to https://github.com/aspnet/KestrelHttpServer/issues/1858 "ERR_INCOMPLETE_CHUNKED_ENCODING in chrome usually means that an uncaught exception was thrown from the application in the middle of writing to the response body". Then I checked the server side to see if I find any error. Well, I set break point in few places in server.js in both setTimeout(() => {... and I see it run periodically. I would expected each line to run once only. So it seems the front-end is trying permanently call the backend and getting some error.

The whole application, both front in ReactJs and the server in NodeJs can be found in https://github.com/jimisdrpc/hello-pocker-coins.

backend:

const http = require("http");

http
  .createServer((request, response) => {
    console.log("Requested url: " + request.url);

    if (request.url.toLowerCase() === "/coins") {
      response.writeHead(200, {
        Connection: "keep-alive",
        "Content-Type": "text/event-stream",
        "Cache-Control": "no-cache"
      });

      setTimeout(() => {
        response.write('data: {"player": "Player1", "amount": "90"}');
        response.write("\n\n");
      }, 3000);

      setTimeout(() => {
        response.write('data: {"player": "Player2", "amount": "95"}');
        response.write("\n\n");
      }, 6000);
    } else {
      response.writeHead(404);
      response.end();
    }
  })
  .listen(5000, () => {
    console.log("Server running at http://127.0.0.1:5000/");
  });

frontend:

import React, { Component } from "react";
import ReactTable from "react-table";
import "react-table/react-table.css";
import { getInitialCoinsData } from "./DataProvider";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      data: getInitialCoinsData()
    };

    this.columns = [
      {
        Header: "Player",
        accessor: "player"
      },
      {
        Header: "Amount",
        accessor: "amount"
      }
    ];
    this.eventSource = new EventSource("coins");
  }


  componentDidMount() {
    this.eventSource.onmessage = e =>
      this.updateAmountState(JSON.parse(e.data));
  }

  updateAmountState(amountState) {
    let newData = this.state.data.map(item => {
      if (item.amount === amountState.amount) {
        item.state = amountState.state;
      }
      return item;
    });

    this.setState(Object.assign({}, { data: newData }));
  }

  render() {
    return (
      <div className="App">
        <ReactTable data={this.state.data} columns={this.columns} />
      </div>
    );
  }
}

export default App;

The exception I can see on chrome:

enter image description here

So my straight question is: why I am getting ERR_INCOMPLETE_CHUNKED_ENCODING 200? Am I missing something in the backend or in the frontend?

Some tips may help me:

  1. Why do I see websocket in oending status since I am not using websocket at all? I know the basic difference (websocket is two-way, from front to back and from back to front and is a diferent protocol while SSE run over http and is only back to front). But it is not my intention to use websocket at all. (see blue line in printscreen belllow)

  2. Why do I see eventsource with 0 bytes and 236 bytes both failled. I understand that eventsource is exactly what I am trying to use when I coded "this.eventSource = new EventSource("coins");". (see read line in printscreen bellow)

  3. Very strange at least for me, some time when I kill the serve I could see updateAmountState methond evoked.

  4. If call the localhost:5000/coins in browser I can see the server answers the response (both json strings). Can I assume that I coded properly the server and the erros is something exclusevely in the frontend?

like image 208
Jim C Avatar asked Jun 04 '19 19:06

Jim C


2 Answers

Here are the answers to your questions.

  1. The websocket you see running is not related to the code you have posted here. It may be related to another NPM package that you are using in your app. You might be able to figure out where it is coming from by looking at the headers in the network request.
  2. The most likely cause of the eventsource requests failing is that they are timing out. The Chrome browser will kill an inactive stream after two minutes of inactivity. If you want to keep it alive, you need to add some code to send something from the server to the browser at least once every two minutes. Just to be safe, it is probably best to send something every minute. An example of what you need is below. It should do what you need if you add it after your second setTimeout in your server code.
const intervalId = setInterval(() => {
  res.write(`data: keep connection alive\n\n`);
  res.flush();
}, 60 * 1000);

req.on('close', () => {
  // Make sure to clean up after yourself when the connection is closed
  clearInterval(intervalId);
});
  1. I'm not sure why you are sometimes seeing the updateAmountState method being invoked. If you are not seeing it consistently, it's probably not a major concern, but it might help to clean up the setTimeouts in the case that the server stops before they complete. You can do this by declaring them as variables and then passing the variable names to a clearTimeout in a close event handler similar to what I did for the interval in the example in #2 above.
  2. Your code is set up properly, and the error you are seeing is due to the Chrome browser timeouts. Use something like the code in answer #2 above if you want to stop the errors from happening.
like image 66
NFab Avatar answered Nov 15 '22 08:11

NFab


I'm not a Node.js expert myself, but it looks like you miss "'Connection': 'keep-alive'" and a "\n" after that - i.e.:

response es.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive'
});
response.write('\n');

see https://jasonbutz.info/2018/08/server-sent-events-with-node/. Hope it works!

like image 43
Milad Avatar answered Nov 15 '22 07:11

Milad