Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for node native HTTP2 module #1517

Open
8 tasks
henryoliver opened this issue Feb 27, 2018 · 17 comments
Open
8 tasks

Support for node native HTTP2 module #1517

henryoliver opened this issue Feb 27, 2018 · 17 comments

Comments

@henryoliver
Copy link

Issue details

Please update browser-sync so it no longer seeks for a third-party node module when you set httpModule: 'http2', but instead uses the node native one.

Please specify which version of Browsersync, node and npm you're running

  • Browsersync [ ^2.23.6 ]
  • Node [ v9.6.1 ]
  • Npm [ 5.6.0 ]

Affected platforms

  • linux
  • windows
  • [ X ] OS X
  • freebsd
  • solaris
  • other (please specify which)

Browsersync use-case

  • API
  • Gulp
  • Grunt
  • [ X ] CLI

If CLI, please paste the entire command below

node ./.tasks/browsersync.js

for all other use-cases, (gulp, grunt etc), please show us exactly how you're using Browsersync

    httpModule: 'http2',
    https: {
        key: `${ os.homedir() }/.ssh/key.pem`,
        cert: `${ os.homedir() }/.ssh/cert.pem`
    },
@shakyShane
Copy link
Contributor

@henryoliver yes I'd like to implement http2 by default, but there were issues implementing response modifications and other vital features that Browsersync uses.

it could be the case that this is no longer a problem, so I will address this asap 👍

@henryoliver
Copy link
Author

Thank you @shakyShane ! I'm a fan of BrowserSync and had been using it in many projects. Great job guys!

@henryoliver
Copy link
Author

@shakyShane just checking, any update on the http2 implementation?

Thanks!

@lunelson
Copy link

lunelson commented Sep 29, 2018

I have a few questions about this:

  1. in this comment on nodejs/http2 it is explained that built-ins always take precedence over installed modules—and the official http2 built-in is now called. http2. So I find, for example, running node 9.x, regardless of whether I have molnarg's node-http http2 module installed, if I require('http2') I get the native (built-in) one—with the attendant warning about http2 being an "experimental module". So already resolving the right module is one issue;

  2. another potential issue with the httpModule option as documented is the fact that it's meant to accept a string module name. This is not usable by tools that integrate browser-sync and are installed globally and not locally, because the module won't be found, as it's installed with the global one but the local node_modules directory will be searched. (This sort of use-case is the reason AFAIK that webpack for example, allows the search directories for 'loaders' to be configured);

  3. but finally, since I was hacking around anyway trying to solve this for my own tool which integrates browser-sync, is installed globally, and where I run node 9.x, I ended up with the following solution to make sure the module was found and that it was molnarg's and not the native one...it seems to work; do you see any problem with this? Am I missing or misunderstanding something about how browser-sync assembles and configures things?

EDIT: code below updated to use browserify's implementation of resolve.

const requireResolve = require('resolve');

// get molnarg's http2 module
const http2Module = require(requireResolve.sync('http2', { basedir: process.cwd() }));

bsync.init({
  /*...other options...*/
  httpModule: http2Module,
  https: true,
}, cb);

@aj-dev
Copy link

aj-dev commented Oct 25, 2018

@shakyShane any news on Node's built-in HTTP2 support? The LTS release v10 is out so it's not in experimental mode anymore.

@lunelson
Copy link

lunelson commented Apr 11, 2019

I'd like to point out as I implied in my comment above: the native http2 module is actually being loaded if you are on a recent version of Node and specify { httpModule: 'http2' } in the options.

As of—at latest—Node 9.x, require.resolve() which is what browser-sync is using internally will resolve to the native http2 module.

The workaround is to use the resolve package which mimic's the original module resolution behavior. @shakyShane can you confirm, whether BS is actually compatible with native http2 now or not? If not, maybe the docs should add this workaround...

// Node v 9.x+
const resolve = require('resolve');

resolve.sync('http2') // will resolve the molnarg http2 module if you have it installed
require.resolve('http2') // will always resolve the native http2 module

@dman777
Copy link

dman777 commented Oct 30, 2019

I am trying to get browsersync to load in http2.0. Browser sync runs with no issues, but I when I try to access the app:

$ curl -I https://foo-demo.com
curl: (35) error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol

I don't have this issue with https and http 1, only http 2 and https. Anyone know what is wrong?

develop /home/one/gitlab/sp6-client # node -v
v10.16.1

develop /home/one/gitlab/sp6-client # browser-sync --version
2.26.7
const express = require('express');
const app = express();
const resolve = require('resolve');
const http2 = require.resolve('http2');
const historyApiFallback = require('connect-history-api-fallback');
const httpProxy = require('http-proxy');

const path = require('path');
const bs = require('browser-sync').create();

const proxyBackEnd = httpProxy.createProxyServer({
     target: 'http://127.0.0.1:5901/'
});

const proxyMiddleware = function(req, res, next) {
   if (
     req.url.match(/\WV1\W/)
     || req.url.match(/\Wv1\W/)
   ) {
     try {
        proxyBackEnd.web(req, res);
     } catch(e) {
       console.log(e);
     }
   } else {
     next();
   }
};

app.use(historyApiFallback());

bs.init({
    server: true,
    httpModule: http2,
    baseDir: ['/'],
    notify: false,
    "files": [
      'index.*',
      'src/*',
      'src/css/*.css',
      'src/**/*',
      'src/**/**/*',
      'src/**/**/**/*'
    ],
    middleware: [proxyMiddleware, historyApiFallback(), app],
    https: {
        key: "work-ssl/ssl/private-key.pem",
        cert: "work-ssl/ssl/certificate.pem",
        ca: "work-ssl/ssl/ca.pem"
    },
    port: 443,
    ui: {
      port: 3075,
    },
});

@lunelson
Copy link

I'm not sure if you can use http2/https with a proxying setup 🤷‍♂

@dman777
Copy link

dman777 commented Oct 31, 2019

@lunelson Thank you for the reply. I also ran it without the proxy at but still hitting the issue. I am wondering if it has to do with the browsersync version? Maybe some kind of regression in the 2.26.7? Do you know of a version that works for sure with http2?

@lunelson
Copy link

lunelson commented Nov 2, 2019

I'm using 2.26.7 actually, but in a different kind of setup, not involving a second server, just letting browsersync serve (which uses connect internally)

@kristian
Copy link

kristian commented Nov 5, 2019

We have the same issue as @dman777 w/ 2.26.7. When trying to run browsersync with HTTPS and HTTP/2 in a proxy set-up we get an ERR_SSL_PROTOCOL_ERROR in Chrome and a PR_END_OF_FILE_ERROR in Firefox.

@jfstephe
Copy link

jfstephe commented Aug 17, 2021

@lunelson - did you get anywhere with this? I'm having the same issue with v2.27.5 on node 10. Documentation suggests everything works with:

// First run: npm install browser-sync http2
const bs = require('browser-sync').create();

bs.init({
    server: './app',
    httpModule: 'http2',
    https: true
});

but it just doesn't work. Tried 'requiring in' http2 as per @dman777 suggestion above but no joy. Not running as a proxy, browserSync is hosting.

Super frustrating.

@Andrew-Bx
Copy link

I struggled with this too.

I think the problem might be that browser-sync doesn't really support https with http2.

In getHttpModule we see that if the httpModule option is not specified then we'll return either the http or https module:

getHttpModule: function(options) {
/**
* Users may provide a string to be used by nodes
* require lookup.
*/
var httpModule = options.get("httpModule");
if (typeof httpModule === "string") {
/**
* Note, this could throw, but let that happen as
* the error message good enough.
*/
var maybe = require.resolve(httpModule);
return require(maybe);
}
if (options.get("scheme") === "https") {
return https;
}
return http;
},

Both of these modules have a createServer method, which is called by getServer:

/**
* Get either http or https server
* or use the httpModule provided in options if present
*/
getServer: function(app, options) {
return {
server: (function() {
var httpModule = serverUtils.getHttpModule(options);
if (
options.get("scheme") === "https" ||
options.get("httpModule") === "http2"
) {
var opts = serverUtils.getHttpsOptions(options);
return httpModule.createServer(opts.toJS(), app);
}
return httpModule.createServer(app);
})(),
app: app
};
},

But there isn't a separate module module for 'http2s'; the native http2 module has separate createServer and createSecureServer methods:
https://github.com/nodejs/node/blob/7ca38f05a023666274569343f128c5aed81599f3/lib/internal/http2/core.js#L3304-L3314
but browser-sync never uses the createSecureServer method.

I hacked-around this via:

const http2 = require('http2');
http2.createServer = http2.createSecureServer;

which got http2 working in browser-sync (along with the aforementioned config options:

    https: true,
    httpModule: 'http2',

)

@lunelson
Copy link

@jfstephe it's been a long time since I used this but back then the solutions I outlined above did work: the non-native http2 module was compatible with BS, the trick was getting that one instead of the native one, since they had the same name

@dman777
Copy link

dman777 commented Aug 18, 2021

Interesting... is there a way to get the non native http2 module?

@jfstephe
Copy link

@lunelson thanks for responding. I had missed the 'molnarg http2' bit in your comment above (doh!). I've got that installed now but still no joy. I can't see that it's loading that module. Seems to just be using the default.

PS I'm actually running on node 10, in case that affects anything?!

@lunelson
Copy link

lunelson commented Aug 26, 2021

@jfstephe my solution from back then probably depends on the specific versions of the resolve and http2 packages at the time. NOTE that @dman777 is using require.resolve in his code above; but this will get the native module not the 3rd party one. You need resolve.sync from the resolve package. OTOH @Andrew-Bx 's insight about createSecureServer vs createServer looks like a better fix if it works

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants