3

I'm writing a socket.io based webcam streaming application that should emit a 'streamComplete' event when the feed times out. The problem is, image resources requests don't seem to send the same headers/cookies as a normal AJAX request. This seems to make it impossible for socket.io to identify the request with a socket so that it can later tell the client that the stream is complete.

On the server side, I have a node.js proxy that serves up mjpeg (moving jpeg) images. When a client selects a feed in the app, the 'src' attribute of an img element in the DOM changes to point to the route of the camera feed.

I've looked into the HTML5 CORS enabled image specification which appears to try to address this issue, but as far as I can tell the feature isn't fully implemented in most browsers. If it is, I can't figure out how to allow those requests on the node.js side -- whenever I add crossorigin='use-credentials' to the image, an error saying

"Cross-origin image load denied by Cross-Origin Resource Sharing policy."

Obviously I should be able to get around that since I control the server side, but the following node header rules doesn't seem to be working even though they seem extremely relaxed:

res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With, XMLHttpRequest, X-HTTP-Method-Override, Content-Type");
res.header('Access-Control-Allow-Methods', 'POST, GET, PUT, OPTIONS');

One option that I have considered is tacking on the socketID to the image request and registering the listener on req.io.sockets.sockets[req.body.socketID]. That seems a little hacky though and I'm sure could bring up other concerns like race conditions. Here's the portion of my code where I am trying to accomplish this.

app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.session({secret: 'blahblahblah'}));

// Allow all cross origin requests.
app.use(allowCrossOrigin);

app.get('/streams/:cam_id/:framerate', function(req, res,next) {
    var camId = req.params.cam_id
      , framerate = req.params.framerate
      , type = (framerate === 0) ? 'jpeg' : 'mjpeg'
      , reqString = 'http://blahblah.com/feeds/'
      , proxy;

    // Query database for stream info.
    var query = new drupalInterface.Query(camId);

    // When query comes back build the URL request string and create proxy.
    query.on('results:available', function(results) {
        var reqArr = [
              results.site_name
            , results.camera_name
            , type
            , framerate
        ];

        reqString = reqString + reqArr.join('/') + '?status_frame=true&random=' + Math.random();

        // Create new proxy for current client.
        req.proxy = new MjpegProxy(reqString);

        // Make request to telepresence server.
        req.proxy.proxyRequest(req, res, next);

        // Forward to express.io route for emitting stream event info to clients.
        req.io.route('initializeProxyStream');
    });
});

app.io.route('initializeProxyStream', function(req) {
    var socket = app.io.sockets.sockets[req.query.socketID];
    // If connecting directly to /stream don't try to listen for socket connection.
    if(req.io) {
        req.proxy.once('streamComplete', function() {
            socket.emit('streamComplete');
        });
    }
});

I'm not sure express.io is even needed for what I am doing, but it seems like it makes sense in this context.

Was I on the right track with the crossorigin attribute? Is there some middleware that I'm missing?

zigzackattack
  • 343
  • 5
  • 11

0 Answers0