electron: Registering and intercepting stream protocols fails (interceptStreamProtocol)

  • 2.0.2 and 2.0.3:

  • Operating System (Platform and Version):

Ubuntu 18.04 and MacOS (latest)

  • Last known working Electron version:

None

Expected Behavior

Registering or intercepting a stream protocol should work and the browser should show the proper content.

Actual behavior

The browser actually shows an empty document with no content. No errors present.

I’ve verified that the same code works fine with ‘string’ protocols. Just not ‘stream’ protocols.

I created a repository here:

https://github.com/burtonator/electron-protocol-examples

With all my examples.

The key ones are:

intercept-stream-protocol-http.js : FAILS intercept-string-protocol-http.js: WORKS

I simplified the code greatly and I use the examples in the documentation. I’ve read the protocol documentation 2-3x to see if I was missing something. Does’t seem to be the case.

Additionally there is this case:

intercept-stream-protocol-with-default-protocol.js

… I’d like to have a way to revert to the default protocol handler if possible. A comment on my previous issue stated that this was possible but it still doesn’t appear to work. Doesn’t work with string protocols either.

To Reproduce

Just clone my repository and run the examples.

https://github.com/burtonator/electron-protocol-examples

git clone https://github.com/burtonator/electron-protocol-examples.git
cd electron-protocol-examples
npm install

## this will work and the content will be shown in the browser.
./node_modules/.bin/electron intercept-string-protocol-http.js

## this will NOT work and no content will be shown to the browser
./node_modules/.bin/electron intercept-stream-protocol-http.js

Screenshots

with sting protocol (working)

image

with stream protocol (not working)

image

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 2
  • Comments: 30 (12 by maintainers)

Most upvoted comments

@sofianguy I think this problem returned or were never fixed. I’m using electron 8.0.0-beta.6 on macOS Catalina 10.15.2 and got the same results as mentioned above. But in my case I can’t see any connection between async/ or sync nature of callback, it behave randomly 😢

I’m 100% sure it works with the buffers, check #13623

Hi,

I think the issue might be in the passthrough code.

I was seeing that the request remained stuck in loading when inspecting it through the devtools window. It would just remain in limbo for a very long time. I think that the networking code relies on ReadableStream to emit the close event which in its turn closes the request? If the close event is emitted before it protocol handler can latch on to it, it probably doesn’t resubmit.

Note that the createStream had an additional delay to writing the null byte to indicate it has finished. Even a small timeout of 1 ms helped still worked.

  function createStream (text) {
    const rv = new PassThrough() // PassThrough is also a Readable stream
    rv.push(text)
    setTimeout(() => { rv.push(null); }, 1000);
    return rv
  }

  protocol.registerStreamProtocol('atom', (request, callback) => {
    callback({
      statusCode: 200,
      headers: {
        'content-type': 'text/html'
      },
      data: createStream('<h5>Response</h5>')
    })
  }, (error) => {
    if (error) console.error('Failed to register protocol')
  })

EDIT: a better and more reliable fix would be the following: We pause the PassThrough stream, using pause(). We return it to the network callback(..) and then resume() the stream which will force the close event to trigger.

  function createStream (text) {
    const rv = new PassThrough() // PassThrough is also a Readable stream
    rv.pause();
    rv.push(text)
    rv.push(null); 
    return rv
  }

  protocol.registerStreamProtocol('atom', (request, callback) => {
    let rv = createStream('<h5>Response</h5>');
    callback({
      statusCode: 200,
      headers: {
        'content-type': 'text/html'
      },
      data: rv,
    })
   rv.resume();
  }, (error) => {
    if (error) console.error('Failed to register protocol')
  })

@burtonator I am very sorry that the reply is late. You can try this code,it work well on my mac。You must use async/await or setTimeout in the interceptStreamProtocol callback,It is very strange,maybe it is a bug or feature?

const electron = require(‘electron’); const app = electron.app; const protocol = electron.protocol; const BrowserWindow = electron.BrowserWindow; const {PassThrough} = require(‘stream’)

/**

  • STATUS: fails.

  • @param request

  • @param callback */ function interceptStreamProtocol(request, callback) {

    // The usage is similar to the other register{Any}Protocol, except that the

    // callback should be called with either a Readable object or an object that // has the data, statusCode, and headers properties. setTimeout(()=> { callback({ statusCode: 200, headers: { ‘content-type’: ‘text/html’ }, data: createStream(‘HTTP 200 OK\r\n

    Response
    ’) }); }, 2000)

}

function createStream (text) { const rv = new PassThrough(); rv.push(text); rv.push(null); return rv; }

function createMainWindow() {

let mainWindow = new BrowserWindow();

let url = "http://example.com";
mainWindow.loadURL(url);
return mainWindow;

}

app.on(‘ready’, async function() {

protocol.interceptStreamProtocol('http', interceptStreamProtocol, (error) => {

    if (error) {
        console.error('failed to register protocol handler for HTTP');
        return;
    }

    console.log("we're registered now.")

    let mainWindow = createMainWindow();

});

});

@bpasero Yes. I’m 95% certain it works with register/intercept buffer protocol. I can update the code I posted to reflect this. I think this is just stream protocol. Stream is the important one though as it’s cleaner , faster, more real time, and supports headers.