electron: Stream protocols and fs.createReadStream sometimes never complete in Electron 7

Preflight Checklist

  • I have read the Contributing Guidelines for this project.
  • I agree to follow the Code of Conduct that this project adheres to.
  • I have searched the issue tracker for an issue that matches the one I want to file, without success.

Issue Details

  • Electron Version:
    • 7.0.0-beta.4 - 8.0.0-beta.2
  • Operating System:
    • macOS 10.14.6, Windows 10
  • Last Known Working Electron version:
    • 7.0.0-beta.3

Expected Behavior

According to the docs, one should be able to register a stream protocol handler which serves files from the file system.

The following code snippet is suggested by the docs:

const { protocol } = require('electron')
const fs = require('fs')

protocol.registerStreamProtocol('atom', (request, callback) => {
  callback(fs.createReadStream('index.html'))
}, (error) => {
  if (error) console.error('Failed to register protocol')
})

The expected behavior is that Electron should read from the stream and (assuming no read errors etc.) serve a valid response for the custom protocol request.

Actual Behavior

Starting with Electron 7.0.0-beta.4 it looks like the request sometimes randomly never completes on the Renderer side. This can be seen in the Network tab as well as by inspecting AJAX calls at runtime. The read stream seems to be created successfully, and the response starts to be sent back to the renderer, but the final state change never happens.

To Reproduce

Run the following Fiddle to see the behavior change. In Electron < 7.0.0-beta4, the page can make an AJAX request to the custom scheme in a loop and print the contents. In Electron >= 7.0.0-beta4 the page can occasionally make a few AJAX requests, but it will eventually halt when one request never reaches a XMLHttpRequest.DONE state.

https://gist.github.com/33a9b357c606c7cbe7c2cac99bbda973

Good console output (7.0.0-beta3 and bellow) looks like:

index.html:22 ajaxStateChange readyState: 1 statusCode: 0
index.html:22 ajaxStateChange readyState: 2 statusCode: 200
index.html:22 ajaxStateChange readyState: 3 statusCode: 200
index.html:22 ajaxStateChange readyState: 4 statusCode: 200
index.html:25 // Just using this file as an example file to be served by the custom scheme handler
index.html:22 ajaxStateChange readyState: 1 statusCode: 0
index.html:22 ajaxStateChange readyState: 2 statusCode: 200
index.html:22 ajaxStateChange readyState: 3 statusCode: 200
index.html:22 ajaxStateChange readyState: 4 statusCode: 200
index.html:25 // Just using this file as an example file to be served by the custom scheme handler
index.html:22 ajaxStateChange readyState: 1 statusCode: 0
index.html:22 ajaxStateChange readyState: 2 statusCode: 200
index.html:22 ajaxStateChange readyState: 3 statusCode: 200
index.html:22 ajaxStateChange readyState: 4 statusCode: 200
index.html:25 // Just using this file as an example file to be served by the custom scheme handler
... and repeat

Bad console output (7.0.0-beta4+) looks like:

index.html:22 ajaxStateChange readyState: 1 statusCode: 0
index.html:22 ajaxStateChange readyState: 2 statusCode: 200
index.html:22 ajaxStateChange readyState: 3 statusCode: 200
index.html:22 ajaxStateChange readyState: 4 statusCode: 200
index.html:25 // Just using this file as an example file to be served by the custom scheme handler
index.html:22 ajaxStateChange readyState: 1 statusCode: 0
index.html:22 ajaxStateChange readyState: 2 statusCode: 200
index.html:22 ajaxStateChange readyState: 3 statusCode: 200
index.html:22 ajaxStateChange readyState: 4 statusCode: 200
index.html:25 // Just using this file as an example file to be served by the custom scheme handler
index.html:22 ajaxStateChange readyState: 1 statusCode: 0
index.html:22 ajaxStateChange readyState: 2 statusCode: 200
index.html:22 ajaxStateChange readyState: 3 statusCode: 200  <-- stop getting updates here

Screenshots

Network info for request that completes successfully:

image

image

image

Network info for request that never completes:

image

image

image

Additional Information

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 6
  • Comments: 43 (27 by maintainers)

Most upvoted comments

I confirm that registerStreamProtocol() “works” in Electron 11.0.1, including with stream: true in registerSchemesAsPrivileged(). However, audio seeking (and presumably, video, although I haven’t had time to test this yet) fails. I am seeing the following event sequence emitted by the HTML audio element, but no requests are made to registerStreamProtocol() when seeking with a mouse click towards the end of the audio resource (i.e. where buffers haven’t been fetched yet … or for that matter, even if they have been fetched already):

  1. seeking
  2. waiting
  3. seeked
  4. canplay
  5. playing
  6. canplaythrough

Anybody experiencing seeking issues?

Okay I was able to produce an example case, this time I did need to use the fs and a <video> element. You’ll need to change the fiddle to point to some test mp4 on your device.

https://gist.github.com/pfrazee/9200ecc8edb63c415748a9b825b67a2a

Okay here’s all I’ve found:

Based on some logging, I’ve found the failure seems to consistently occur when mojo tells the producer to wait (MOJO_RESULT_SHOULD_WAIT). This should be handled correctly by the DataPipeProducer. However, when the watcher attempts to resume, I appear to see two kinds of errors:

  • An error where DidWrite() fails with MOJO_RESULT_FAILED_PRECONDITION, or
  • An error where DidWrite() does not fail, but NotifyComplete() is called with the generic net failure code (-2) after data_pipe_producer_dispatcher.cc hits the “peer closed” condition.

I can’t venture any guesses toward a cause, but finding the MOJO_RESULT_SHOULD_WAIT condition to be consistently involved may be a helpful clue.

I updated my example project to directly show the video streaming bug in electron 7, 8, and 9. After building the project, the video at the top does not load. When building the same project with electron 6, the video does load and can be viewed (update the package.json, run yarn, yarn build).

The proposed fix does not fix this problem for me. After upgrading electron from version 6 to 7.1.2, I noticed that images randomly did not load when they have display: none; on them with an onload handler that shows them.

As the original project is quite big, I created an example project based on itsananderson’s gist which shows that images sometimes do not complete loading. Also, you can see that the CAUTION: request is not finished yet message from itsananderson’s screenshots still appears for requests that don’t complete.

Feel free to clone the example project, build and run the app. Please note that the bug does not appear when starting the app with electron ..

My configuration:

  • Electron Version: 7.1.2
  • Operating System: macOS 10.14.6