JS --> Delphi question (relates to Web Audio API)

Tested.

...
uses ... , WebAudio, WEBLib.REST, ...
...
procedure TForm1.WebButton1Click(Sender: TObject);
var
  AContext:  TJSAudioContext;
  AGainNode: TJSGainNode;
  ABufferSourceNode: TJSAudioBufferSourceNode;

  ARequest: TWebHTTPRequest;
  AResponse : TJSXMLHTTPRequest;

 begin
   AContext := TJSAudioContext.New;
   AGainNode := AContext.CreateGain;
   ABufferSourceNode := AContext.CreateBufferSource;
   ABufferSourceNode.Connect( AContext.Destination );

   ARequest := TWebHTTPRequest.Create(Self);
   ARequest.URL := 'test.mp3';
   ARequest.ResponseType := rtArrayBuffer;

   try
     AResponse := await( TJSXMLHTTPRequest, ARequest.Perform() );
     ABufferSourceNode.Buffer := await(TJSAudioBuffer, AContext.DecodeAudioData(TJSArrayBuffer(AResponse.Response)));
     ABufferSourceNode.Start;
   except on E: Exception do
     begin
       // file not found, etc.
       ShowMessage(E.ClassName+': '+E.Message);
     end;
   end;

end;

The test.mp3 file in this case was included in the Delphi project folder. Clicking the button loads and then plays the file. No JavaScript anywhere :smirk:

1 Like

Awesome, Andrew! Thanks!

You're welcome!

1 Like

When I change the file name to a URL, I get the following error:

Access to XMLHttpRequest at 'http://mydomain.com/snippet1.mp3' from origin 'http://localhost:8000 Rtl.BrowserLoadHelper.pas:148' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

I did add a line:

  ARequest := TWebHTTPRequest.Create(Self);
  ARequest.Headers.Add( 'Access-Control-Allow-Origin=*' );
  ARequest.URL := lbox1.Items[0];
  ARequest.ResponseType := rtArrayBuffer;

I have no problem accessing the file in a browser. It does exist.

Is this something I solve on the client side or the remote server where the file resides?

Actually, this seems rather strange to me. This is the CLIENT app making this request. Why is it going through my local web server?

Welcome to the CORS party. You can search around here for plenty of painful posts about CORS. It's hit me over the head more times than I care to think about.

The short version is that you probably need to add that header to the server, not to the client.

And yes, it can be the case that it works fine from the browser and not from the client. In fact, that's usually the case with CORS.

CORS is so troublesome, in fact, that Bruno recently pinned a new topic to the top of the TMS WEB Core section of the Support Center.

1 Like

The problem is that the Request header says this:

Referer: http://localhost:8000/

This the CLIENT code!

Why is it saying it's going thru my local host server?

Have you seen this: http://www.whateverorigin.org

Is there a simple way to do that here?

What's puzzling to me is that the same URL put into the FNC WX Audio player works fine. I look at the console, and it looks the same, except ... it works. But with the code above, I get this CORS issue. What did they do to make it work?

I've sent a ticket to my hosting provider to ask if there's some way to whitelist a folder or specific MIME types for CORS. This is an Apache server running cPanel.

If you have access to the server, and it is running Apache, you should be able to do something like this.

Should be able to do that via cPanel without any problems. There are other workarounds, certainly. But that's kind of the problem - workarounds and not actual solutions.

Not sure what the FNC WX Audio player is doing differently. Looking at the code, it doesn't appear that they do anything with JavaScript in this respect, but rather just add a src attribute to an <audio> tag, which means the browser handles the remote connection, not JavaScript, and thus might bypass CORS issues altogether. Check out the crossorigin attribute of the <audio> tag:

CORS is disabled without this tag being present. So like I said, welcome to the insanity that is CORS.

Yes, my hosting support guys suggested adding that to .htaccess in both the home folder and the folder with the files in it.

So far it hasn't helped. Waiting for them to reply again.

I also get different errors with and without this added to the reques:

ARequest.Headers.Add( 'Access-Control-Allow-Origin=*' );

This is a server issue, so I'd leave out the ARequest.Headers business, at least as far as this is concerned.

The Apache configuration files are notorious for being picky about where things are defined. .htaccess files aren't any better. Taken together, they can be just as much fun as CORS, and should be considered willing accomplices to this crime.

All I can suggest there is triple-check that you've updated the .htaccess files correctly, that they're being applied to the content in question (you don't usually have to restart Apache unless you're changing its configuration files directly) and that you've done a hard refresh on your browser page so that Apache is forced to regenerate whatever it is sending you.

The support folk said the slight difference between the Origin and Referer DOES make a difference:

OPTIONS /xyz/snippet1.mp3 HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,*;q=0.8
Access-Control-Request-Headers: access-control-allow-origin
Access-Control-Request-Method: GET
Connection: keep-alive
Host: tayshn.com
Origin: http://localhost:8000         <---
Referer: http://localhost:8000/       <---
Sec-Fetch-Mode: cors
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36

Again, I don't know why they're referring to the localhost in this case because they should be coming from the browser.

I'll fiddle with them and see what I can do.

Is that the response from your server hosting the audio files?

The idea behind CORS is that when you load a web page from a server, JavaScript makes a note of what the server is. In this case, it is your localhost:8000. Let's call it "Sally" This then becomes the "origin" for subsequent requests that JavaScript issues. And you can't really change it.

Later, whenever JavaScript issues a request to a remote server, it happily tells the server what this origin value is. If it is the same server, localhost:8000 (Sally), the server says "hey buddy, I know you! here's your stuff" and everything proceeds normally.

If it is a different server,not Sally, the server is saying "hey, buddy, I see you've been hanging out with Sally, and I don't want to play that game. "Click". And that's the end of the conversation.

What we're trying to do is sweet talk the other server, not Sally, to be a little more open-minded about who we hang out with, so they instead reply with "hey buddy, I don't care who you hang out with, here's your stuff".

Whenever you issue a fetch or an XMLHTTPRequest, which is what we're doing, JavaScript snitches and tells the server you're hanging out with Sally. When the browser contacts the same server to download files to the <audio> tag, it blatantly lies (well, a lie of omission I suppose) and says "who me? I don't hang out with anyone" so not Sally is none the wiser.

Let's have a look at your .htaccess files?

What we're looking for is a response from the server that includes this line:
access-control-allow-origin: *

Which should be set using the .htaccess file, as per the link I mentioned.

I understand all of that, but thanks for the simplier explanation. :slight_smile:

I put this into the .htaccess file in PUBLIC_HTML folder as well as the folder where the files are:

<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "*"
</IfModule>

These are the request headers:

OPTIONS /vSTtRVaQ3L3bzarx/sample-3s
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,*;q=0.8
Access-Control-Request-Headers: access-control-allow-origin
Access-Control-Request-Method: GET
Connection: keep-alive
Host: tayshn.com
Origin: http://localhost:8000       <---
Referer: http://localhost:8000/     <---
Sec-Fetch-Mode: cors
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36

Here's a test file to play with:

Client_CORS_test.zip (7.2 KB)

When you make a request from the server, what are the response headers? The request headers aren't the problem here, it is the other end.

The "localhost" is just Javascript snitching on you to "Not Sally".

The only other apache thing I've read is to check that the headers module is enabled, but it is enabled by default, so likely not an issue.

a2enmod headers

But the idea is that when it issues a response of any kind, that "access-control-allow-origin" setting should be appearing in the list of other stuff it responds with. For example, when XData is configured with the CORS middleware, there's a spot to set that value. So when it generates a response, I see this:

Again, its the response from the server we're trying to change.

I put the files from the TMSWeb/Debug folder onto a folder on two different servers:

  • the server where the file exists; and

  • another server

When I ran it from the server where the file exists, it actually PLAYED!

I guess I should be EXCITED because . . . hey . . . IT PLAYED!!!!

But when I ran it from the other server, it gave the same error as before. And the Origin and Referer fields still differed.

Try them here -- the first is where the file exists, the second one isn't.

This should answer all of your questions.

http://tayshn.com/test1/

http://g3q.com/cors_test/

I'm going to bed.

I couldn't get your 2nd link to work (got the same error as you) or your project (missing a dpr file or something) but I added the original sample code I gave you to one of my projects, replacing test.mp3 with your mp3 URL.

It originally worked fine on the dev machine (naturally) but when I deployed it to a proper web server, it didn't work. It was complaining about HTTPS and HTTP not matching, so I changed your link to be an https link and then it worked fine (my project is on an HTTPS domain).

I think your projects and the mp3 file can all be accessed over HTTPS as well. Maybe change your mp3 link to https and use https to access your web applications and see if that does the trick?

As developers, we're granted a lot of leeway with how things work locally, particularly with localhost, but once deployed, there are a ton of rules that start to kick in, like no more mixed content allowed (HTTPS and HTTP). And it is an evolving thing. I'm sure in no short order it won't be feasible to have an HTTP-only website.

I updated the test code so there are both http and https lines in the listbox. Just select one or the other. If you go with https for the app, then you need the https link to the audio -- for the on-site file. But the off-site files fails no matter what.

This can't be THAT hard to solve!

https://tayshn.com/test1

https://g3q.com/cors_test

Try removing the line starting with ARequest.Headers.Add
This isn't necessary and is likely to cause an error as the server isn't going to let you set that anyway.

Did you find it helped? I found that it didn't really seem to make a difference one way or the other. The on-site reference works, and the off-site doesn't, either way. But removing it generates a different error, which seems odd.

I tried your second link again but that still has the ARequest.Headers.Add line again. The error it is giving is that "you are not allowed to do that", so, well, don't do that :slight_smile:

1 Like