BrowserSync / browser-sync

Keep multiple browsers & devices in sync when building websites. https://browsersync.io
https://discord.gg/2d2xUThp
Apache License 2.0
12.19k stars 757 forks source link

Support for node native HTTP2 module #1517

Open henryoliver opened 6 years ago

henryoliver commented 6 years ago

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

Affected platforms

Browsersync use-case

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 commented 6 years ago

@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 commented 6 years ago

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

henryoliver commented 6 years ago

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

Thanks!

lunelson commented 6 years ago

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 commented 6 years ago

@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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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

dman777 commented 5 years ago

@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 commented 5 years ago

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 commented 5 years ago

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 commented 3 years ago

@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 commented 3 years ago

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: https://github.com/BrowserSync/browser-sync/blob/764a437c9b23484b94b060aac2827d53eeeeb838/packages/browser-sync/lib/server/utils.js#L104-L125

Both of these modules have a createServer method, which is called by getServer: https://github.com/BrowserSync/browser-sync/blob/764a437c9b23484b94b060aac2827d53eeeeb838/packages/browser-sync/lib/server/utils.js#L82-L103

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 commented 3 years ago

@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 commented 3 years ago

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

jfstephe commented 3 years ago

@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 commented 3 years ago

@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