spdy-http2 / node-spdy

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

Feature request: auto protocol upgrade from http/1.x to h2 #243

Open djphoenix opened 8 years ago

djphoenix commented 8 years ago

It seems some browsers may use "upgrade: h2" header to up an connection from plain http to http2 (without tls, so NPN/ALPN unavailable). This is example that works with nghttp -u:

var spdy = require('spdy');
var spdy_transport = require('spdy/node_modules/spdy-transport');
var util = require('util');

function http2_upgrade(req,sock,head) {
  var conn = (req.headers['connection'] || '').toLowerCase().split(/,\s*/);
  var upg = (req.headers['upgrade'] || '').toLowerCase();
  if (
    conn.indexOf('upgrade')!=-1 &&
    conn.indexOf('http2-settings')!=-1 &&
    /^h2c/.test(upg) &&
    'http2-settings' in req.headers
  ) {
    sock.write([
      'HTTP/1.1 101 Switching Protocols',
      'Connection: Upgrade',
      'Upgrade: '+upg,
      '',''
    ].join('\r\n'));
    delete req.headers['upgrade'];
    delete req.headers['connection'];
    delete req.headers['http2-settings'];
    var connection = spdy_transport.connection.create(sock, util._extend({
      protocol: 'http2',
      isServer: true
    }, this._spdyState.options.connection || {}));
    connection.start(4);
    connection.on('error', sock.destroy.bind(sock));
    connection.on('stream', this._onStream.bind(this));
    connection._handleFrame({
      type:'HEADERS',
      id: 1,
      priority: { parent: 0, exclusive: false, weight: 16 },
      fin: false,
      writable: true,
      headers:util._extend(req.headers, {
        ':method': req.method,
        ':path': req.url,
        ':scheme': 'http',
        ':authority': req.headers.host,
      }),
      path: req.url
    });
    return true;
  }
}

module.exports = function(config){
  var spdy_server=spdy.createServer({ spdy: { plain: true, ssl: false } });
  spdy_server.on('upgrade',function(req,sock,head){
    if (http2_upgrade.call(this,req,sock,head)) return;
    if (config.upgrade) return config.upgrade.call(this,req,sock,head);
    sock.end([
      'HTTP/1.1 501 Not Implemented',
      '',''
    ].join('\r\n'));
  });
  if (config.request)
    spdy_server.on('request',config.request);
  if (config.checkContinue)
    spdy_server.on('checkContinue',config.checkContinue);
  spdy_server.listen(80);
  return spdy_server;
}

It's just a "dirty hack". But it works. Why not implement it with right way in right place?

indutny commented 8 years ago

Perhaps, one day! :)