0

I have created a website in Node Js, and it supports range requests and gives the proper ranged responses.

However, anything loaded with the <video> tag does not work, this is the code for the server (stripped but still the same), and you can see an example of audio and video not working here: https://yoinkyest.com/message/

This is a mystery to me, as I debugged it as much as I could. This clearly is not a simple issue, or simplicity is beyond me.

THIS IS NOT AN HTML ISSUE!!!

I know the code is inefficient, don't talk about it unless you're optimizing it.

Below is the Node Js code, try it yourself. (you will need an audio file at ./content/yoinkyest.com/pathtoaudiofile.mp3 ex. localhost:80/a.mp3 will be at ./content/yoinkyest.com/a.mp3)

const mime = require('mime-types');
const http = require('http');
const fs = require('fs');
const port = 80;

var headers = {
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Methods": "*",
    "Accept-Ranges": "bytes"
};
function contentType(path) {
    try {
        return mime.lookup("." + path.split(".")[path.split(".").length - 1]);
    } catch (a) {
        return "";
    }
}
async function serveContent(req, res, code, header, content, settings) {
    settings = {
        "maxEndRange": 200000000000,
        "type": "chunk",
        "includeHeaders": true,
        ...settings
    }
    if (settings.type == "chunk") {
        content = Buffer.from(content);
    }
    if (code.toString()[0] == "2" && req.headers.range) {
        try {
            var range = req.headers.range.replace("bytes", "").replace("=", "").replace(" ", "");
            code = 206;
            if (req.headers.range.includes(",")) {
                res.writeHead(416, headers);
                res.end("This server does not support multiple byte ranges, send these requests separately.");
            } else if (settings.type == "chunk") {
       //not yet implemented
            } else if (settings.type == "path") {
                fs.stat(content, function (err, stats) {
                    var fullChunkLength = stats.size;
                    var endByte = Math.min(parseInt((range.split("-")[1] == "") ? ((fullChunkLength > settings.maxEndRange) ? (parseInt(range.split("-")[0]) + settings.maxEndRange) : fullChunkLength) : parseInt(range.split("-")[1])), fullChunkLength);
                    var file = fs.createReadStream(content, { start: parseInt(range.split("-")[0]), end: endByte });
                    console.log(range.split("-")[0], endByte);
                    res.writeHead(code, { "Content-Length": ((endByte - parseInt(range.split("-")[0]) + ((range.split("-")[1] == "") ? 0 : 1))), "Content-Range": "bytes " + range.split("-")[0] + "-" + endByte + "/" + fullChunkLength, ...header, ...((settings.includeHeaders) ? headers : {}) });
                    file.pipe(res);
                    console.log("pip");
                });
            }
        } catch {
            res.writeHead(416, headers);
            res.end("range issues ig");
        }
    } else {
        if (settings.type == "chunk") {
            res.writeHead(code, {"Content-Length": content.length, ...header, ...((settings.includeHeaders) ? headers : {}) });
            res.end(content);
        } else if (settings.type == "path") {
            var file = fs.createReadStream(content);
            res.writeHead(code, {"Content-Length": (await fs.promises.stat(content)).size, ...header, ...((settings.includeHeaders) ? headers : {}) });
            file.pipe(res);
        }
    }
}

async function handleRequest(req, res, body, chunkedBody) {
    let host = req.headers["x-forwarded-host"] || req.headers.host || "yoinkyest.com";
    var now = new Date();
    if (req.method == "OPTIONS") {
        res.writeHead(200, { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Headers": "auth, Auth, Accept, Accept-CH, Accept-Charset, Accept-Datetime, Accept-Encoding, Accept-Ext, Accept-Features, Accept-Language, Accept-Params, Accept-Ranges, Access-Control-Allow-Credentials, Access-Control-Allow-Headers, Access-Control-Allow-Methods, Access-Control-Allow-Origin, Access-Control-Expose-Headers, Access-Control-Max-Age, Access-Control-Request-Headers, Access-Control-Request-Method, Age, Allow, Alternates, Authentication-Info, Authorization, C-Ext, C-Man, C-Opt, C-PEP, C-PEP-Info, CONNECT, Cache-Control, Compliance, Connection, Content-Base, Content-Disposition, Content-Encoding, Content-ID, Content-Language, Content-Length, Content-Location, Content-MD5, Content-Range, Content-Script-Type, Content-Security-Policy, Content-Style-Type, Content-Transfer-Encoding, Content-Type, Content-Version, Cookie, Cost, DAV, DELETE, DNT, DPR, Date, Default-Style, Delta-Base, Depth, Derived-From, Destination, Differential-ID, Digest, ETag, Expect, Expires, Ext, From, GET, GetProfile, HEAD, http-date, Host, IM, If, If-Match, If-Modified-Since, If-None-Match, If-Range, If-Unmodified-Since, Keep-Alive, Label, Last-Event-ID, Last-Modified, Link, Location, Lock-Token, MIME-Version, Man, Max-Forwards, Media-Range, Message-ID, Meter, Negotiate, Non-Compliance, OPTION, OPTIONS, OWS, Opt, Optional, Ordering-Type, Origin, Overwrite, P3P, PEP, PICS-Label, POST, PUT, Pep-Info, Permanent, Position, Pragma, ProfileObject, Protocol, Protocol-Query, Protocol-Request, Proxy-Authenticate, Proxy-Authentication-Info, Proxy-Authorization, Proxy-Features, Proxy-Instruction, Public, RWS, Range, Referer, Refresh, Resolution-Hint, Resolver-Location, Retry-After, Safe, Sec-Websocket-Extensions, Sec-Websocket-Key, Sec-Websocket-Origin, Sec-Websocket-Protocol, Sec-Websocket-Version, Security-Scheme, Server, Set-Cookie, Set-Cookie2, SetProfile, SoapAction, Status, Status-URI, Strict-Transport-Security, SubOK, Subst, Surrogate-Capability, Surrogate-Control, TCN, TE, TRACE, Timeout, Title, Trailer, Transfer-Encoding, UA-Color, UA-Media, UA-Pixels, UA-Resolution, UA-Windowpixels, URI, Upgrade, User-Agent, Variant-Vary, Vary, Version, Via, Viewport-Width, WWW-Authenticate, Want-Digest, Warning, Width, X-Content-Duration, X-Content-Security-Policy, X-Content-Type-Options, X-CustomHeader, X-DNSPrefetch-Control, X-Forwarded-For, X-Forwarded-Port, X-Forwarded-Proto, X-Frame-Options, X-Modified, X-OTHER, X-PING, X-PINGOTHER, X-Powered-By, X-Requested-With", ...headers });
        res.end();
        return;
    } else {
        let noSearch = req.url.split("?")[0];
        let path = "./content/yoinkyest.com" + noSearch;
        let urlSearch = new URLSearchParams(req.url.split("?")[1]);
        if (req.method == "GET") {
            serveContent(req, res, 200, { "Content-Type": contentType(path) }, path, { "type": "path" })
            return;
        }
    }
}

const server = http.createServer((req, res) => {
    var body = "";
    var chunkedBody = [];
    req.on("data", (chunk) => {
        body += chunk;
        chunkedBody.push(chunk);
    });
    req.on("end", () => {
        handleRequest(req, res, body, chunkedBody);
    });
});

server.listen(port, () => {
});
2
  • 1
    I think your numbers are off? MDN on the Content-Range header gives the following example, content-length: 1024 with content-range: bytes 0-1023/146515. Your example mp3 gets me content-length: 1738117 with content-range: bytes 0-1738117/1738117 Commented Oct 8 at 8:13
  • @C3roe The code was off by one! I checked what headers i should have using an iis server, and the content range for the displaying the whole file was wrong. Range: 0- should provide Content-Range: bytes 0-1738116/1738117 but it was providing bytes 0-1738117/1738117 It works now! It was a little weird, as i thought / should have meant out of, so 1738117 out of 1738117 would make more sense, i guess it stands in for the length. Commented Oct 8 at 14:04

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.