9

I have an S3 bucket as an origin, and a CloudFront distribution serving streaming a/v from it.

I also have an EC2 instance serving a web page from a test domain that uses videojs to display the video as a test.

If I start the VLC media player and use the CloudFront URL to view the video, I both see and hear the media. So, this configuration does serve the content, if I do not have to deal with CORS.

However, when I attempt to view the content from my EC2 instance's web page, I run into CORS issues.

Using Firefox, and observing the Network tab in the developer tools, I see that Firefox fetches the initial .mpd URL without issue. However, the subsequent files (*.cmfv, *.cmfa, all embedded in the .mpd XML) show "CORS Missing Allow Origin" for the preflight check (the OPTIONS method call). The GET requests that evoked the OPTIONS requests then error with NS_ERROR_DOM_BAD_URI, and are never sent.

Using curl, I mimic the request from Firefox thusly:

curl -H "origin: https://my.happy.url" -H "referer: https://my.happy.url" -H "Access-Control-Request-Headers: range" -H "Access-Control-Request-Method: GET" -X OPTIONS -v https://gobbledygook.cloudfront.net/content/10%20Minutes%20Of%20Coding%20Torture_6.cmfv

And I get this response:

> OPTIONS /content/10%20Minutes%20Of%20Coding%20Torture_6.cmfv HTTP/2
> Host: gobbledygook.cloudfront.net
> user-agent: curl/7.79.1
> accept: */*
> origin: https://my.happy.url
> referer: https://my.happy.url
> access-control-request-headers: range
> access-control-request-method: GET
> 

* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 200 
< content-length: 0
< date: Tue, 16 Nov 2021 19:25:54 GMT
< server: AmazonS3
< vary: Origin,Access-Control-Request-Headers,Access-Control-Request-Method
< x-cache: Hit from cloudfront
< via: 1.1 aaaaaaaiiiiiiiggggghhhhh.cloudfront.net (CloudFront)
< x-amz-cf-pop: IUD69-C2
< x-amz-cf-id: pHptphptmQ2lILrG9dpKVZIXT7Dhm_HSDVnBPijf7KcS7ZsLkKA==
< age: 1603
< 
* Connection #0 to host gobbledygook.cloudfront.net left intact

... where I see a distinct lack of Access-Control-Allow-Origin, a complaint I see in the JavaScript console logging.

Yet, if I use curl to mimic the initial GET request with this:

curl -H "origin: https://my.happy.url" -H "referer: https://my.happy.url" -H "range: bytes=658-4657" -v https://gobbledygook.cloudfront.net/content/10%20Minutes%20Of%20Coding%20Torture_6.cmfv

... I get this:

> Host: gobbledygook.cloudfront.net
> user-agent: curl/7.79.1
> accept: */*
> origin: https://my.happy.url
> referer: https://my.happy.url
> range: bytes=658-4657
> 
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 206 
< content-type: video/mp4
< content-length: 4000
< date: Wed, 17 Nov 2021 11:54:27 GMT
< last-modified: Wed, 10 Nov 2021 15:36:31 GMT
< etag: "a2155000203fcc7e173acdc053a75cd1"
< x-amz-version-id: mfCll81lDxmeTyDIYmsoKFINElW2AmE1
< accept-ranges: bytes
< server: AmazonS3
< vary: Origin,Access-Control-Request-Headers,Access-Control-Request-Method
< content-range: bytes 658-4657/8320198
< access-control-allow-origin: *
< x-cache: Miss from cloudfront
< via: 1.1 aaaaaaaiiiiiiiggggghhhhh.cloudfront.net (CloudFront)
< x-amz-cf-pop: IAD66-C2
< x-amz-cf-id: hd-iBmLikeSmoNEYznT3SoWKNDERIGHE-gtjH2U3JlQDlwccL6SdQ==
< 

Note the inclusion of the access-control-allow-origin: * header that went missing for OPTIONS. It did actually fetch the contents. So the only thing holding this up appears, to me, to be the missing access-control-allow-origin header for the OPTIONS query. Unless I am missing something (which is possible at this point... I've spent perhaps too much time on this, and could be tunnel-visioned from seeing the solution).

The CloudFront distribution's behavior is set to allow GET, HEAD, and OPTIONS methods, and the 'Cache HTTP methods' OPTIONS checkbox is enabled, so it should cache OPTIONS.

The Cache policy for the distribution's behavior includes the following headers:

  • Origin
  • Access-Control-Request-Method
  • Access-Control-Allow-Origin
  • Access-Control-Request-Headers

and all cookies are enabled (not that this matters for this situation).

The TTL settings are set for a minimum of 0 and a maximum of 31536000, with a default of 86400, but I doubt this matters.

The Origin request policy for the distribution's behavior includes the following headers:

  • Origin
  • Access-Control-Request-Headers
  • Access-Control-Request-Method

This is the CORS-S3Origin managed policy.

The Response headers policy for the distribution's behavior has 'Configure CORS' enabled, with the following:

  • Access-Control-Allow-Origin: All origins
  • Access-Control-Allow-Headers: All headers
  • Access-Control-Allow-Methods: All HTTP methods
  • Access-Control-Expose-Headers: All headers
  • Access-Control-Max-Age: 600
  • Origin override is checked

The S3 bucket has the CORS settings within its Permissions tab:

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "GET",
            "HEAD"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": [
            "ETag",
            "Access-Control-Allow-Origin",
            "Connection",
            "Content-Length"
        ],
        "MaxAgeSeconds": 3000
    }
]

The web page uses the following code (with a small dot of PHP) to play the content (in a script tag):

        const dashjsCallback = (player, mediaPlayer) => {
            if (videojs && videojs.log) {
                mediaPlayer.getDebug().setLogTimestampVisible(false);
            }
        };

        videojs.Html5DashJS.hook('beforeinitialize', dashjsCallback);

        const el = document.getElementsByTagName('video')[0];

        const manifest = "/content/<?php echo $policy_stream_name ?>";
        const mimeType = "application/dash+xml";

        const player = videojs(el, {
            "controls":true,
            "autoplay":true,
            "preload":"auto",
            "fluid":"true",
        });
        player.src({
            src: 'https://gobbledygook.cloudfront.net' + manifest,
            type: mimeType,
        });
        player.play();

with the following HTML in the document:

    <video 
        id="my-video"
        class="video-js vjs-default-skin"
        controls
        preload="auto"
        height="480"
        data-setup="{}"
        crossorigin="anonymous"
    >
        <p class="vjs-no-js">This video requires JavaScript.</p>
    </video>

I have also invalidated the cache contents of the distribution with these settings, and waited 24 hours before attempting a view again, but I still cannot stream the video through the videojs player. I've exhausted my Google-fu and now turn to you, gentle reader, for help. How may I convince videojs to play this content that plays perfectly fine within VLC?

6
  • Small note... I can browse to the page using chrome's --disable-web-security parameter and view the content without issue. But that isn't how I want this to work. Commented Nov 17, 2021 at 13:21
  • Another note... I made sure that I invalidated the cache after viewing the video from within VLC. But I still see the offending behavior in curl. Commented Nov 17, 2021 at 13:57
  • Well... I assume AWS has some kind of bug or something, as I can't see how this will ever work. Instead, I will use this stack and modify it to my needs... which is kind of a shame: aws.amazon.com/solutions/implementations/video-on-demand-on-aws Commented Dec 8, 2021 at 16:09
  • Well, in the end, I wound up running AWS's stack to generate Lambda functions, etc. and going with that instead of this approach. I suspect AWS's recent CORS support isn't quite working according to expectations. Commented Dec 15, 2021 at 19:06
  • Do you resolve this issue? I am facing the same problem
    – royallife
    Commented Jul 18, 2022 at 12:47

1 Answer 1

1

I solved this by setting up the CORS in CloudFront along with setting up Bucket CORS. My Bucket CORS was set but did not work, You need to set CORS for CloudFront CORS too. Here you can find the solution which helped me.

CloudFront CORS

5
  • Oh, that's interesting... I should try to put something together to test this. I might need to attempt this sort of thing again. Commented Jul 27, 2022 at 15:55
  • Yes, for me setting ExposeHeaders values within Cross-origin resource sharing section of the bucket settings seemed to have been what was missing. Thereafter it was sending the access-control-allow-origin header within the response. A bit confusing as I had figured that it was dealt with on the CloudFront side within the response policy. Commented Oct 23, 2022 at 17:20
  • To what did you set the "ExposeHeaders" value @CraigvanTonder ?
    – IARI
    Commented Oct 25, 2022 at 16:29
  • @IARI It would depend on your own use case as to which values you would specify. This said, it seems that you do need to specify Access-Control-Allow-Origin within the ExposeHeaders array if you want it to be sent back to the client. For my use case: "ExposeHeaders": [ "ETag", "Access-Control-Allow-Origin", "Connection", "Content-Length" ]. Commented Oct 26, 2022 at 8:04
  • I don't know if I'll ever get to test this, so I'm just going to accept this as the answer, after all this time. Sorry to take so long, but I really did want to test this. Commented Apr 16 at 10:21

Not the answer you're looking for? Browse other questions tagged or ask your own question.