5

I am trying to implement a server which send large amount of data (objects, not files) every 500 ms. After some reading, Server-sent events over http2 seems like the fastest option (due to http2 being a binary protocol and SSE reduce the traffic overhead)

After playing a bit with SSE on http/1.1 I've been trying to do the same on http2. I've tried to do so with stream and pushStream, but without success. However, using the same manner I used for http/1.1 seems to work.

My question is - why server 1 (see below) which using stream is not working, while server 2 seems to work fine? am I missing something when working node streams?

I'm using node v10.9.0 and chrome 68.0.3440.106

I've read the following questions and posts, but still couldn't solve this issue:

Server 1 - http2 with stream (Not working - client don't get events. chrome describe the request as unfinished request):

const fs = require('fs');
const http2 = require('http2');

const HTTPSoptions = {
    key: fs.readFileSync('./cert/selfsigned.key'),
    cert: fs.readFileSync('./cert/selfsigned.crt'),
};
const template = `
<!DOCTYPE html> 
<html>
<body>
    <script type="text/javascript">
        const  source = new EventSource('/sse/');
        source.onmessage = function(e) { 
            document.body.innerHTML += e.data + '<br>';
        };
    </script>
</body>
</html>
`;


const server = http2.createSecureServer(HTTPSoptions);

server.on('stream', (stream, headers, flags) => {
    if (stream.url === 'sse/') {
        console.log(stream.respond);
        stream.respond({
            ':status': 200,
            'content-type': 'text/event-stream'
        });
        setInterval(() => stream ? res.write(`data: ${Math.random()}\n\n`) : '', 200);
    }
});

server.on('request', (req, res) => {
    if(req.url === '/') {
        res.end(template);
    }
});



server.listen(3001);

Server 2 - http2 with stream (working just fine):

const fs = require('fs');
const http2 = require('http2');

const HTTPSoptions = {
    key: fs.readFileSync('./cert/selfsigned.key'),
    cert: fs.readFileSync('./cert/selfsigned.crt'),
};
const template = `
<!DOCTYPE html> 
<html>
<body>
    <script type="text/javascript">
        const  source = new EventSource('/sse/');
        source.onmessage = function(e) { 
            document.body.innerHTML += e.data + '<br>';
        };
    </script>
</body>
</html>
`;


const server = http2.createSecureServer(HTTPSoptions);

server.on('request', (req, res) => {
    req.socket.setKeepAlive(true);

    if(req.url === '/sse/') {

        res.writeHead(200, {
            'Content-Type': 'text/event-stream', // <- Important headers
            'Cache-Control': 'no-cache'
        });
        res.write('\n');

        setInterval(() => res.write(`data: ${Math.random()}\n\n`), 200);
    } else {
        res.end(template);
    }
});

server.listen(3001);
yuval.bl
  • 4,890
  • 3
  • 17
  • 31

1 Answers1

3

To get the request path for a http2 stream, you have to look at the :path header (docs):

if (headers[':path'] === '/sse/') {

You also tried to use res.write, while res should be stream.

This is a working handler function based on your "Server 1" implementation:

server.on('stream', (stream, headers, flags) => {
    if (headers[':path'] === '/sse/') {
        stream.respond({
            ':status': 200,
            'content-type': 'text/event-stream'
        });
        setInterval(() => stream.write(`data: ${Math.random()}\n\n`), 200);
    }
});
MiniGod
  • 3,683
  • 1
  • 26
  • 27