node.jshls.jsmountebank

Mountebank piping video file stream to response not working


I am trying to get Mountebank to return the read in file stream pipe from my .ts MPEG-2 file to the Mountebank injection so the client can receive the stream data and play the video. There is a useful Node implementation on how to read in the files and I got it to read in. the .m3u8 file but now it seems to get stuck with the first .ts file. Node seems to be reading the .ts files as a stream and piping it to the response stream.pipe(res). I'm not sure how that translates to Mountebank. Here is what I have for Mountbank code:

Mountbank:

function (request, state, logger, callback) {
    const path = require('path');
    const fs = require('fs');
    function _handleError(errorMessage, statusCode) {
        logger.error(`[video-qoe-injection:responses]`, errorMessage);
        return {
            statusCode,
            body: {}
        }
    }
    function _sendFile(filepath) {
        const data = fs.readFileSync(filepath, 'utf-8');
        return {
            statusCode: 200,
            headers: {'Content-Type': 'application/vnd.apple.mpegurl'},
            body: data
        }
    }
    if (request && request.method === 'GET') {
         if (path.extname(uri) === '.m3u8') {
                filepath = path.join(M3U8_FILE_PATH);
                return _sendFile(filepath);
         }
    } else if (path.extname(uri) === '.ts') {
        filepath = path.join(TS_FILE_PATH)
        let body = '';
        const stream = fs.createReadStream(filepath, { bufferSize: 64 * 1024 });
        stream.on('data', (data) => {
            body += data.toString();
        });
        stream.on('end', () => {
            const stubResponse = {
                statusCode: 200,
                headers: {'Content-Type': 'video/MP2T'},
                body
            }
            callback(stubResponse);
       });
    }
}

I tried this with the pure Node implementation and it works, however, when I port this to Mountebank I get a console log error with the following error:

hls.0.12.4.min.js:1 Uncaught (in promise) DOMException: The play() request was interrupted by a new load request.
player.js:57 Player error: mediaError - fragParsingError

Not sure what I'm missing here, does Mountebank not support stream responses? I even tried reading the .ts file with const data = fs.readFileSync(filepath) but hls.js doesn't seem to like that and getting a promise rejection.


Solution

  • I think you might need to simulate this using something a little more crude - from the documentation:

    HTTP bodies will always be recorded as text, but mountebank does have the ability to respond in binary. If you want to set up a canned binary response, set the _mode to binary and base64 encode the body. mountebank will also try to preserve binary responses in proxies by looking at the Content-Encoding and Content-Type headers.

    I have used this in the past, but only with a simple PDF file in a non-JS injection stub:

    "responses": [
    {
      "is": {
        "_mode": "binary",
        "statusCode": 200,
        "headers": {
          "Content-Type": "application/pdf",
          "Access-Control-Allow-Origin": "*",
          "Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE"
        },
        "body": "U29tZVJhbmRvbUJhc2U2NA...<etc>"
        }
      }
    ]
    

    I'd suggest trying a couple of things:

    1. Try adding the '_mode' property onto your stubResponse objects in your JS file and adding conversion of the streamed response to Base 64 on the way out.
    2. Alternatively, try encoding your video file in Base 64 and providing the encoded text as the body in a standard stub. You might also find it less cumbersome to add the Base 64 file as an EJS reference:
        "responses": [
        {
          "is": {
            "_mode": "binary",
            "statusCode": 200,
            "headers": {
              "Content-Type": "video/MP2T",
              "Access-Control-Allow-Origin": "*",
              "Access-Control-Allow-Methods": "GET"
            },
            "body": "<% include ./YourBase64EncodedFilePathHere.txt %>"
            }
          }
        ]