1

I'm trying to use the hls.js library for displaying Reddit hosted videos from the HLS/DASH playlist. However, accessing any Reddit HLS/DASH url, such as this one through XHR will fail due to, what the error says is a Same Origin Policy violation:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://v.redd.it/5r0nz8sywgl41/DASHPlaylist.mpd. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).

Weird thing is, the request prior GET is an OPTIONS one, which does return the Access-Control-Allow-Origin header with the correct origin url. I can see the response in the developer console, but the request still "fails". If I use the "Allow CORS: Access-Control-Allow-Origin" extension, everything works fine. What am I doing incorrectly?

OPTIONS request headers:

Host: v.redd.it
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0
Accept: */*
Accept-Language: lt,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate, br
Access-Control-Request-Method: GET
Access-Control-Request-Headers: authorization
Referer: http://localhost:3000/
Origin: http://localhost:3000
DNT: 1
Connection: keep-alive
Save-Data: on
Pragma: no-cache
Cache-Control: no-cache
TE: Trailers

OPTIONS response headers:

HTTP/2 200 OK
retry-after: 0
access-control-allow-origin: http://localhost:3000
access-control-allow-headers: authorization
access-control-allow-methods: GET
access-control-max-age: 3000
date: Sun, 08 Mar 2020 18:40:29 GMT
via: 1.1 varnish
x-served-by: cache-hhn4029-HHN
x-cache: HIT
x-cache-hits: 0
x-timer: S1583692829.044409,VS0,VE0
server: snooserv
accept-ranges: bytes
x-cdn-server-region: EU-East
x-cdn-client-region: EU
x-cdn-name: fastly
access-control-expose-headers: x-cdn-server-region, x-cdn-client-region, x-cdn-name
cache-control: public, max-age=604800, s-maxage=86400, must-revalidate
vary: Access-Control-Request-Headers, Access-Control-Request-Method,Origin
content-length: 0
X-Firefox-Spdy: h2

GET request headers:

Host: v.redd.it
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0
Accept: */*
Accept-Language: lt,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate, br
Referer: http://localhost:3000/
Authorization: Bearer --------------------------- (actual token here)
Origin: http://localhost:3000
DNT: 1
Connection: keep-alive
Save-Data: on
Pragma: no-cache
Cache-Control: no-cache
TE: Trailers

GET response headers:

HTTP/2 200 OK
last-modified: Sun, 08 Mar 2020 15:34:11 GMT
etag: "0abfb243cb8d03188bf34ff29d6d4af8"
content-type: application/dash+xml
via: 1.1 varnish
date: Sun, 08 Mar 2020 18:40:29 GMT
via: 1.1 varnish
x-served-by: cache-bwi5138-BWI, cache-hhn4029-HHN
x-cache: HIT, HIT
x-cache-hits: 2, 34
x-timer: S1583692829.127808,VS0,VE0
server: snooserv
accept-ranges: bytes
x-cdn-server-region: EU-East
x-cdn-client-region: EU
x-cdn-name: fastly
access-control-expose-headers: x-cdn-server-region, x-cdn-client-region, x-cdn-name
cache-control: public, max-age=604800, s-maxage=86400, must-revalidate
vary: Origin
content-length: 1976
X-Firefox-Spdy: h2

GET response:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<MPD mediaPresentationDuration="PT15.034S" minBufferTime="PT1.500S" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" type="static" xmlns="urn:mpeg:dash:schema:mpd:2011">
    <Period duration="PT15.034S">
        <AdaptationSet segmentAlignment="true" subsegmentAlignment="true" subsegmentStartsWithSAP="1">
            <Representation bandwidth="1172841" codecs="avc1.4d401f" frameRate="30" height="480" id="VIDEO-1" mimeType="video/mp4" startWithSAP="1" width="720">
                <BaseURL>DASH_480</BaseURL>
                <SegmentBase indexRange="913-992" indexRangeExact="true">
                    <Initialization range="0-912"/>
                </SegmentBase>
            </Representation>
            <Representation bandwidth="784684" codecs="avc1.4d401e" frameRate="30" height="360" id="VIDEO-2" mimeType="video/mp4" startWithSAP="1" width="540">
                <BaseURL>DASH_360</BaseURL>
                <SegmentBase indexRange="915-994" indexRangeExact="true">
                    <Initialization range="0-914"/>
                </SegmentBase>
            </Representation>
            <Representation bandwidth="592784" codecs="avc1.4d401e" frameRate="30" height="240" id="VIDEO-3" mimeType="video/mp4" startWithSAP="1" width="360">
                <BaseURL>DASH_240</BaseURL>
                <SegmentBase indexRange="915-994" indexRangeExact="true">
                    <Initialization range="0-914"/>
                </SegmentBase>
            </Representation>
            <Representation bandwidth="91690" codecs="avc1.4d400a" frameRate="30" height="96" id="VIDEO-4" mimeType="video/mp4" startWithSAP="1" width="144">
                <BaseURL>DASH_96</BaseURL>
                <SegmentBase indexRange="912-991" indexRangeExact="true">
                    <Initialization range="0-911"/>
                </SegmentBase>
            </Representation>
        </AdaptationSet>
    </Period>
</MPD>

Thanks a lot for your help.

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197

1 Answers1

1

The problem is just: the response from https://v.redd.it/5r0nz8sywgl41/DASHPlaylist.mpd to the GET request from your code doesn’t include the Access-Control-Allow-Origin response header. Strangely though, it does include the Access-Control-Expose-Headers response header. So the cause is just that the server is misconfigured; it’s not actually properly CORS-enabled.

Specifically: even though the server sends a Access-Control-Allow-Origin header in response to the OPTIONS preflight, that on its own isn’t sufficient to make browsers allow your frontend code to access the response to the actual GET request in your code. For your code to work, the server must also send the Access-Control-Allow-Origin header in response to that GET request too.

But you can actually work around the https://v.redd.it misconfiguration and access Reddit HLS/DASH URLs from your frontend code without needing a browser extension — by making your request through CORS proxy, as in the following example.

const proxyurl = "https://cors-anywhere.herokuapp.com/";
const url = "https://v.redd.it/5r0nz8sywgl41/DASHPlaylist.mpd";
fetch(proxyurl + url)
.then(response => response.text())
.then(contents => console.log(contents))

For explanation, see the answer at https://stackoverflow.com/a/43881141/441757.

You can easily run your own proxy using code from https://github.com/Rob--W/cors-anywhere/, and you can quickly deploy your own proxy to Heroku in literally just 2-3 minutes, with 5 commands:

git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git push heroku master

After running those commands, you’ll end up with your own CORS Anywhere server running at, e.g., https://cryptic-headland-94862.herokuapp.com/. So then rather than prefixing your request URL with https://cors-anywhere.herokuapp.com, prefix it instead with the URL for your own instance; e.g., https://cryptic-headland-94862.herokuapp.com/https://example.com.

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
  • Thanks for the additional info. However, isn't this violating Reddit content access policies or something along those lines? – Emilis Kiškis Mar 12 '20 at 13:00
  • I don’t know what policy Reddit could have that would prohibit you from using a proxy (your own or someone else’s) to make requests to their site. You can already make requests to the Reddit server using curl or wget or any HTTP client or any server-side runtime environment without running into any problems. The only place where any restrictions on access to Reddit URLs are imposed is by browsers. – sideshowbarker Mar 12 '20 at 13:07
  • Or to frame the policy issue in other terms: The presence of the Access-Control-Allow-Origin response header is just an explicit indicator to browsers to lift the restrictions that browsers already impose by default. But the absence of the Access-Control-Allow-Origin response header is not an explicit indicator that the site intentionally means to prohibit frontend JavaScript code from accessing responses. Instead it just be that the site owners have just neglected to take the time to CORS-enable their site, or that they’ve tried to CORS-enable the site but instead have it misconfigured. – sideshowbarker Mar 12 '20 at 13:10
  • So CORS is more like a tool for clients to make browsing unknown sources safer? – Emilis Kiškis Mar 12 '20 at 15:10
  • CORS was designed almost solely for intranets, and specifically, confused-deputy privilege-escalation attacks within intranets. The problem case that CORS was designed to address is the case of intranets wrongly assuming that any request which comes from an origin inside the intranet, behind the firewall, is a “safe” client — and so naively/insecurely doing authentication based only on IP address. But a malicious third-party web app can run from a user’s machine inside the intranet, from that user’s IP address, with that user’s privileges. – sideshowbarker Mar 12 '20 at 17:49