CORS headers for accessing a file on another domain - javascript

I am attempting to create an audio visualization program on Codepen. I have created my own Ubuntu web server with apache which allows me direct access to modify headers and configuration of the server.
While browsers can access files on a different domain, it requires special CORS headers to read the frequencies within the audio. To read audio frequencies, I must use createMediaElementSource to access audio information including the frequencies. When the browser sees this JavaScript method, it knows that there must be certain headers set on the server to allow access. Which brings us to the motives of this question: What headers need to be set?
All of the browsers I have tested return a variation of a CORS error.
This is what the error looks like in Firefox although I've tested it in Chrome, Opera, and Safari with similar results:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://williamgreen.hopto.org/audioVisualization/song.mp3. (Reason: CORS header ‘Access-Control-Allow-Origin’ does not match ‘(null)’).
The file returns 206 partial content.
Here are the returned server headers (currently):
Here are the sent headers (currently):
function log(text) {
document.getElementById("log").textContent += text + "\n";
}
var audio, source, context;
var url = "http://williamgreen.hopto.org/audioVisualization/song.mp3";
document.addEventListener("DOMContentLoaded", function() {
log("URL: " + url);
log("Creating Audio instance from audio file");
audio = new Audio(url);
audio.crossOrigin="anonymous";
audio.addEventListener("canplay", function() {
log("Playing audio file through HTML5 Audio for 3 seconds");
audio.play();
setTimeout(function() {
log("Creating Web Audio context");
context = new (typeof AudioContext != "undefined" ? AudioContext : webkitAudioContext)();
log("Calling createMediaElementSource on audio (switching to Web Audio)");
source = context.createMediaElementSource(audio);
setTimeout(function() {
log("Connecting source to context destination");
source.connect(context.destination);
log("\nIf no sound can be heard right now, the problem was reproduced.");
}, 1000);
}, 3000);
});
});
<div id="log"></div>
What do I need to change to get this working?

My first thought is that the problem is your
Access-Control-Allow-Origin: *, *
I don't think it is understanding the *, * thing. Try just *.
Edit: you can check what the header really looks like with a command like this:
curl -v -o /dev/null http://williamgreen.hopto.org/audioVisualization/song.mp3
And, lo, it even works for me, yielding (among a lot of other headers):
< Access-Control-Allow-Origin: *
So that is hunky-dory.
Second, are you running this from a file: origin? That doesn't work on Chrome (I thought it would work on Firefox, but maybe that's changed). You have to run it from an http: or https: origin.
By "running from an file: origin" I mean, is the HTML file that is running that Javascript being loaded from an URL that beings with "file:". If so, that is not likely to work.

Related

HTTP PUT to google storage fails in firefox for larger files

I am using resumable upload with no problem on chromium browsers. I use js, specifically angular.
let headers = new HttpHeaders(
{
'Content-Type': 'application/octet-stream',
'Content-Range': 'bytes 0-*/*',
});
if (offset) {
headers = headers.set('Content-Range', 'bytes ' + (offset + 1) + '-*/*');
params = (params.slice(offset + 1) as File);
}
const req = new HttpRequest(
'PUT',
url,
params,
{ headers, reportProgress: true },
);
return this.http.request<File>(req);
On firefox, for some reason, if the file is over 5GB, it fails nondeterministically of time, uploaded size or percent of upload. Usually it is around 300s and/or 2-3GBs. The upload starts fine, but somewhere on the way it fails.
I get a CORS error at that point and am unable to resume the upload.
Cross-Origin Request Blocked: The Same Origin Policy disallows reading
the remote resource at
https://storage.googleapis.com/repo-name/uploads/guid?upload_id=blahblah&uploadType=resumable.
(Reason: CORS request did not succeed)
I tried manually upload the same set of files to google drive manually in firefox and sure enough, I also get the CORS error, but their implementation is better I guess, because they are able to resume the upload, but after a while it throws the same error.
The only difference I see is I am trying to do one PUT request (unless it fails, then I send the other chunks in seperate one), on google drive, they chunk it and do a bunch of PUTs. They also correctly identify the content-type as ie. video/x-matroska, I use octet-stream.
It works for files under 4GB in firefox and works regardless of size in Chromium browsers. Any tips on what I am doing worng?

How to define SharedArrayBuffer in Chrome?

I am working on React + WASM + FFmpeg app following this tutorial
On Chrome, I got the error Uncaught (in promise) ReferenceError: SharedArrayBuffer is not defined error.
I followed the doc reference, and it says the below, which I don't understand.
Cross-origin isolation overview
You can make a page cross-origin isolated by serving the page with these headers:
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
What does serving the pages with these headers mean?
How do I implement that?
I even found Chrome dev's official video, but even that does not explain any implementation detail.
Edit2: The best instruction I can find is here, but even that is too vague for me. What does it mean by setting a header? I'm not requesting anything to begin with.
Edit: My React version is already 17.0.2, so this should have been fixed but I am somehow getting this error as well...
[Deprecation] SharedArrayBuffer will require cross-origin isolation as of M92, around July 2021.
The SharedArrayBuffer serves the purpose of sharing data buffers between your main browsing context and another agent (another web page or a worker).
However, to minimize the possible security vulnerabilities, the specification states that this API can only be operated in a secure context.
In addition to this security prerequisite, it is also necessary for any top-level document to be in a Cross Origin Isolation. This means, the party serving the contents should tell the browser that it should block the loading of cross-origin resources. (A document from domain A trying to get data from Domain B)
This is done by the servers by specifying the following two response eaders when serving the website:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
How to specify them in your debug environment?
You need to set up a standard file server such as Apache. Then you will have to tweak its configuration to serve the above headers with the files.
Quick setup: https://www.wampserver.com/en/
An answer provisioning the header info: https://stackoverflow.com/a/35566100/4185234
Let's start off by saying what serving the pages with these headers means.
If you have ever built an API with Express.js for example, you'll be familiar with this. Essentially, it means that when the user makes a GET request to see the web page, you will have to send some additional information in the form of HTTP headers.
Specifically, the first header prevents your page from loading any cross-origin resources that don't explicitly grant permission. The second one means that you can't share a browsing context group with any cross-origin documents. Both of these are used as safety measures to prevent cross-origin attacks. Even though you may not be requesting anything, you have to apply them.
Now onto your problem, I would recommend installing the Chrome extension CORS. I don't know exactly how it works, but I have used it in the past and it will be a temporary solution. I skimmed through the tutorial you're following and I didn't see a server setup (as in Express.js/Node's http for instance). If you had any of these you could pass the headers as arguments to the servers.
To check if the CORS settings are working as intended, add the following code to your app:
if (crossOriginIsolated) {
// Works
}
I ran into this recently and found your question and the answers above
What does serving the pages with these headers mean?
It means configuring the server serving your pages to include those headers. How you configure a server depends on the software you're using, Apache, Nginx, Caddy, or if you're using one of the 1000s of others or writing your own.
I've written 2 (or more correctly, I've used a library to write 2) and I added the option to include those headers. Both are open source so you can look inside if you want to see how they work.
If you prefer a UI then there's this one. If you prefer the command line then install node.js and then you can use this one
Here's a static server example for node.js
var http = require("http");
var fs = require("fs");
var path = require("path");
http
.createServer(function (request, response) {
console.log("request starting...");
response.setHeader("Cross-Origin-Opener-Policy", "same-origin");
response.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
var filePath = "." + request.url;
if (filePath == "./") filePath = "./index.html";
var extname = path.extname(filePath);
var contentType = "text/html";
switch (extname) {
case ".js":
contentType = "text/javascript";
break;
case ".css":
contentType = "text/css";
break;
case ".json":
contentType = "application/json";
break;
case ".png":
contentType = "image/png";
break;
case ".jpg":
contentType = "image/jpg";
break;
case ".wav":
contentType = "audio/wav";
break;
}
fs.readFile(filePath, function (error, content) {
if (error) {
if (error.code == "ENOENT") {
fs.readFile("./404.html", function (error, content) {
response.writeHead(200, { "Content-Type": contentType });
response.end(content, "utf-8");
});
} else {
response.writeHead(500);
response.end(
"Sorry, check with the site admin for error: " +
error.code +
" ..\n"
);
response.end();
}
} else {
response.writeHead(200, { "Content-Type": contentType });
response.end(content, "utf-8");
}
});
})
.listen(80);
console.log("Server running at http://127.0.0.1:80/");

How to check if a user can access YouTube with Javascript

I need to programmatically check if a user can access YouTube, as YouTube is blocked in some parts of the world. I need to be able to do this in JavaScript.
What I have tried:
1) Creating an image dynamically and setting the source to be a known image on the YouTube domain - I ran into CORS issues here
2) Creating a simple GET request using XMLHttpRequest (same problem)
3) Looking at the YouTube API.. it does not appear to expose a method which can tell me whether a GET request was successful.. and of course, all calls are routed through https://www.googleapis.com/youtube/v3 which is not, strictly speaking, exactly the same as a request to http://www.youtube.com
Is there something obvious I'm missing here? Would appreciate any and all insight..
Thanks!
CORS is a server-side header. If you're getting a CORS header, you're hitting Youtube's servers which are responding with response headers, which likely means that youtube is not "blocked".
So something like this should work just fine:
async function canAccessYoutube() {
try {
const request = await fetch(
"https://www.youtube.com/",
{
mode: "no-cors"
}
);
return true;
} catch {
return false;
}
}
Assuming the blocking is DNS based, you can test the functionality by blocking Youtube on your own computer. If you have a mac or linux computer, add the following to your /etc/hosts file:
0.0.0.0 youtube.com
0.0.0.0 www.youtube.com
::0 www.youtube.com
::0 youtube.com
You may need to restart your computer for it to take effect.

MediaElementAudioSource outputs zeroes due to CORS access restrictions

<script>
// Create a new instance of an audio object and adjust some of its properties
var audio = new Audio();
audio.src = 'http://subdomain.domain.org:port/;stream/1';
audio.controls = true;
audio.loop = true;
audio.autoplay = true;
audio.crossorigin="anonymous";
// Establish all variables that your Analyser will use
var canvas, ctx, source, context, analyser, fbc_array, bars, bar_x, bar_width, bar_height;
// Initialize the MP3 player after the page loads all of its HTML into the window
window.addEventListener("load", initMp3Player, false);
function initMp3Player(){
document.getElementById('audio_box').appendChild(audio);
context = new (window.AudioContext || window.webkitAudioContext)(); // AudioContext object instance // AudioContext object instance
analyser = context.createAnalyser(); // AnalyserNode method
canvas = document.getElementById('analyser_render');
ctx = canvas.getContext('2d');
// Re-route audio playback into the processing graph of the AudioContext
source = context.createMediaElementSource(audio);
source.crossOrigin = 'anonymous'
source.connect(analyser);
analyser.connect(context.destination);
frameLooper();
}
// frameLooper() animates any style of graphics you wish to the audio frequency
// Looping at the default frame rate that the browser provides(approx. 60 FPS)
function frameLooper(){
(requestAnimationFrame || webkitRequestAnimationFrame)(frameLooper);
fbc_array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(fbc_array);//get frequency
ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas
ctx.fillStyle = '#00CCFF'; // Color of the bars
bars = 100;
for (var i = 0; i < bars; i++) {
bar_x = i * 3;
bar_width = 2;
bar_height = -(fbc_array[i] / 2);
// fillRect( x, y, width, height ) // Explanation of the parameters below
ctx.fillRect(bar_x, canvas.height, bar_width, bar_height);
}
}
</script>
Audio API gives MediaElementAudioSource outputs zeroes due to CORS access restrictions because I'm trying to play a SHOUTcast URL. I don't know what to do; I have tried all solutions on the internet but nothing worked. Any help will be appreciated.
The URL works perfectly with audio element so its not about the URL; I have even tried something like http://subdomain.domain.org:port/file.mp3. And I found on the internet people using Icecast which is .ogg have same problem. How to fix this?
In my response I will assume the following setup:
Your stream URL is http://stream.radio.com:8000/mount (or http://stream.radio.com:8000/;stream/1 for Shoutcast)
Your paget URL where you place your HTML/JS code URL is http://radio.com/player
To get this working you need:
Set the "Access-Control-Allow-Origin" header of your stream to your domain or *
In javascript, set audio tag crossOrigin property to "anonymous" audio.crossOrigin="anonymous";
Another option it to move you stream URL to the original domain using reverse proxy.
With Icecast you cat set the "Access-Control-Allow-Origin" header using configuration file, just add the following lines to your icecast.xml, I usually add them right after the opening <icecast> tag:
<http-headers>
<header name="Access-Control-Allow-Origin" value="*" />
<header name="Access-Control-Allow-Headers" value="Origin, Accept, X-Requested-With, Content-Type, If-Modified-Since" />
<header name="Access-Control-Allow-Methods" value="GET, OPTIONS, HEAD" />
</http-headers>
Don't forget to restart Icecast after these changes. When your Icecast will be back online you can check the headers with this command:
lynx -head -dump http://stream.radio.com:8000/mount
Response should look something like this:
Server: Icecast 2.4.2
....
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Origin, Accept, X-Requested-With, Content-Type, If
-Modified-Since
Access-Control-Allow-Methods: GET, OPTIONS, HEAD
As you can see, "Access-Control-Allow-Origin: *" header is present.
Shoutcast
Unfortunately, Shoutcast does not allow you to set HTTP headers (.htaccess is not an option too), but we can create a reverse proxy in web server configuration, this will allow you to host the stream from the main domain - radio.com. I will provide proxy configurations for Nginx and Apache.
Nginx
You can add additional headers with "proxy_set_header", but the basic example is:
server {
listen 80;
server_name radio.com;
....
location /stream {
proxy_set_header X-Forwarded-For $remote_addr;
proxy_pass http://stream.radio.com:8000/mount;
}
....
}
Apache
Activate Apache proxy modules, and update radio.com virtual host configuration configuration:
<VirtualHost *:80>
ServerName radio.com
....
ProxyPass /stream http://stream.radio.com:8000/mount
</VirtualHost>
Now you can access your stream using http://radio.com/stream URL and the CORS policy will not apply. This solution also brings some additional perks:
you can convert your http Shoutcast/Icecast stream to https, so the browsers will not complain about accessing unsecure content when you will embed your stream to the page hosted with https. (Icecast supports SSL configuration itself)
8000 port will be replaced with port 80, that will allow listeners with 8000 port behind firewall to access your stream.
That is an HTTP header. You would configure your webserver or webapp to send this header. Perhaps in htaccess or PHP. remove the below line
<header name = "Access-Control-Allow-Origin" value = "*" />
SHOUTcast servers do not support CORS. There is nothing you can do to change this if you are going to continue to use SHOUTcast.
First of all, MediaElementAudioSource doesn't have a property named "crossOrigin".
I just find this problem, and mad with the Message:MediaElementAudioSource outputs zeroes due to CORS access restrictions for. But it's just a message, i can still hear the audio.
And I googled lots of this, think this link will be helpful:http://www.codingforums.com/javascript-programming/342454-audio-api-js.html
The createMediaElementSource method should create an object that uses the MediaElementAudioSourceNode interface. Such objects are subject to Cross-Origin Resource Sharing (CORS) restrictions based on the latest draft of the Web Audio API spec. (Note that this restriction doesn't appear to be in the outdated W3C version of the spec.) According to the spec, silence should be played when CORS restrictions block access to a resource, which would explain the "outputs zeroes" message; presumably, zero is equivalent to no sound.
To lift the restriction, the owner of the page at
http://morebassradio.no-ip.org:8214/;stream/1 would need to configure
their server to output an Access-Control-Allow-Origin header with
either a list of domains (including yours) or the * value to lift it
for all domains. Given that this stream appears to already be
unrestricted, public-facing content, maybe you can convince the owners
to output that header. You can test whether the header is being sent
by pressing Ctrl+Shift+Q in Firefox to open the Network panel, loading
the stream through the address bar, and then inspecting the headers
associated with that HTTP request in the Network panel.
Note that they can't use a meta element here since the audio stream
is, obviously, not an HTML document; that technique only works for
HTML and XHTML documents.
(While you're messing with Firefox panels, you may want to make sure
Security errors and warnings are enabled (by clicking the Security
button or its arrow) in the Console panel (Ctrl+Shift+K). I'm not sure
if there's a corresponding CORS message in Firefox like in Chrome, but
there might be. I wasted a bunch of time wondering why a page wasn't
working one day while troubleshooting a similar technology, Content
Security Policy (CSP), only to find that I had the relevant Firefox
messages hidden.)
You shouldn't need to mess with the crossorigin property/attribute
unless you set crossorigin = "use-credentials" (JavaScript) or
crossorigin="use-credentials" (HTML) somewhere, but you probably
didn't do that because that part of the HTML spec isn't finalized yet,
and it would almost certainly cause your content to "break" after
doing so since credentials would be required at that point.
I'm not familiar with the Web Audio API, so I wasn't able to figure
out how to output a MediaElementAudioSourceNode and trigger an error
message for my own troubleshooting. If I use createMediaElementSource
with an HTMLMediaElement (HTMLAudioElement), the result doesn't seem
to be a MediaElementAudioSourceNode based on testing using the
instanceof operator even though the spec says it should be if I'm
reading it right.
Then in my situation, i get the HTTP response Header:
HTTP/1.1 206 Partial Content
Date: Thu, 02 Jun 2016 06:50:43 GMT
Content-Type: audio/mpeg
Accept-Ranges: bytes
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Log, X-Reqid
Access-Control-Max-Age: 2592000
Content-Disposition: inline; filename="653ab5685893b4bf.mp3"
Content-Transfer-Encoding: binary
Last-Modified: Mon, 16 May 2016 02:00:05 GMT
Server: nginx
Cache-Control: public, max-age=31536000
ETag: "FpGQqtcf_s2Ce8W_4Mv6ZqSVkVTK"
X-Log: mc.g;IO:2/304
X-Reqid: 71cAAFQgUBiJMVQU
X-Qiniu-Zone: 0
Content-Range: bytes 0-1219327/1219328
Content-Length: 1219328
Age: 1
X-Via: 1.1 xinxiazai211:88 (Cdn Cache Server V2.0), 1.1 hn13:8 (Cdn Cache Server V2.0)
Connection: keep-alive
Note that "Access-Control-Allow-Origin: *", i think this just the right thing, but i still get the message. Hope it help you.

Remote calls via javascript

I run a service where there is a javascript file that is called and self executed on a user's site.
This then calls an external server every 10 or so seconds with a bunch of variables.
I used to do this by using a createElement('script') and then setting the path to a file on the external server and passing the required variables across by means of GET variables. (works well for small URI's)
This worked really well and seemed to work cross browser as well with no undesired effects.
The problem I then ran into was when I needed to extend the amount or size of the variables that were being sent across. So obviously I decided to change from GET method to POST, but by doing that I could no longer use the createElement('script') trick and had to opt for the XMLHttpRequest() (ala Ajax - without jQuery) method which worked really well, except for the minor problem of having to also cater for Internet Explorer and Opera which didn't really play ball too well (big shock). So I used the following:
function createCORSRequest(method, url){
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr){
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined"){
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
xhr = null;
}
return xhr;
}
var request = createCORSRequest("post", "http://xx.xxxx.com/");
if (request){
request.onload = function(){
//do something with request.responseText
};
request.send(myPostObjectDataVariableGoeshere);
}
..which I found over at this page
This is basically just a fallback to using the XDomainRequest() method which InternetExplorer wants you to use instead..
Fantastic, BUT -> Looking in the Console of Developer Tools in IE it says:
SEC7118: XMLHttpRequest for http://xx.xxxx.com/ required Cross Origin Resource Sharing (CORS).
SEC7120: Origin null not found in Access-Control-Allow-Origin header.
SCRIPT7002: XMLHttpRequest: Network Error 0x80070005, Access is denied.
But what's really odd about this is that I've already got the following as the first line in my backend PHP file that is being called (which works for other browsers...)
header('Access-Control-Allow-Origin: *');
Someone please tell me what's wrong here.. Also if there is a better way to be doing this instead of fighting the browser wars..
Note: I cannot use jQuery for this task!
You should try jQuery for this task. Its much easier and don't have that problem with IE.
http://api.jquery.com/jQuery.ajax/
IE unfortunately block Cross Origin requests, i believe there is no simple way to get around it by script only, but you can try tuning the options or via my proxy script.
Tuning the options
Internet Explorer ignores Access-Control-Allow headers and by default prohibits cross-origin access for Internet Zone. To enable CORS go to Tools->Internet Options->Security tab, click on “Custom Level” button. Find the Miscellaneous -> Access data sources across domains setting and select “Enable” option.
Proxy Script on local server as a Bridge
Previous post:
Remote POST request with jQuery and Ajax
This is for you to place a PHP script on a local server and do a local AJAX request and proxy to the remote server for good.

Categories