spdy-http2 / node-spdy

SPDY server on Node.js
2.8k stars 196 forks source link

How to send GOAWAY frame manually? #238

Closed dy93 closed 8 years ago

dy93 commented 8 years ago

In some case i want to gracefully shutdown old server processes and replace it with new ones.

I found that http2's spec says GOAWAY frame can be used to stop accepting new streams and help me gracefully shut down my server process [https://http2.github.io/http2-spec/#GOAWAY]

currently I use following code to send GOAWAY, but after sending GOAWAY frame, the current working stream will fail in the end

req.connection._handle.getStream().connection._goaway({
    lastId: req.connection._handle.getStream().connection._spdyState.stream.lastId.both,
    code: 'OK',
    send: false
});

here is the code to explain my situation

var spdy = require('spdy'),
    fs = require('fs');

var options = {
    key: fs.readFileSync(__dirname + '/my.key'),
    cert: fs.readFileSync(__dirname + '/my.crt'),

    // **optional** SPDY-specific options
    spdy: {
        protocols: ['h2'],
        plain: false,

        connection: {
            windowSize: 1024 * 1024, // Server's window size
            // **optional** if true - server will send 3.1 frames on 3.0 *plain* spdy
            autoSpdy31: false
        }
    }
};

var server = spdy.createServer(options, function (req, res) {
    if (req.url !== '/file') {
        // simple web page to show download link
        res.writeHead(200);
        res.end("<html><head><title>test</title></head><body><a href='file'>get file</a></body></html>");
        return;
    }

    var rs = fs.createReadStream('./large.bin');
    res.writeHead(200, { 'Content-Type': 'application/octet-stream' });
    rs.pipe(res, { end: true });

    setTimeout(function () { // send GOAWAY frame to stop accepting new stream after 2 seconds
        req.connection._handle.getStream().connection._goaway({
            lastId: req.connection._handle.getStream().connection._spdyState.stream.lastId.both,
            code: 'OK',
            send: false
        });
    }, 2000);
});

server.listen(3000);

this will start a simple server listen at 3000 port and show a download link when you visit it, here large.bin is a large file which will take a few seconds to download(ex: 1GB random file). my expect result is:

  1. when click get file link, large.bin can be successful download
  2. requests after GOAWAY frame should create new http2 connection

but in practice, download process will fail in the end, my chrome simply show "download fail" after data transfer finished.

here is my environment: client: chrome 47.0.2526.106 m / firefox 41.0.2 server: node v4.2.3

Thank you!

indutny commented 8 years ago

I think that the simpler way would be to use getStream().connection.end(), but it is not really a public API. So I will have to introduce one anyway.

Thank you for suggestion!

dy93 commented 8 years ago

OK, I'll try it.

indutny commented 8 years ago

@dy93 4a88e6e should help. With it - it will look like req.spdyStream.connection.end() which is IMO much better ;)