2

We have a problem with a Datasnap REST (Delphi 10.1 Berlin) Server accessed by AngularJS clients. I can't activate Authorization because Angular can't send the dssession within a Pragma Header, seems to be a problem with CORS, because the browser is the one changing that Header (launching Chrome with the --disable-web-security flat everything runs fine).

Even running Angular and Datasnap on the same machine for testing (Angular at localhost:8080 and Datasnap at localhost:8081), the browser detects the calls as Cross-Origin calls and when Angular tries to send the dssession it doesn't arrive to Datasnap. Note: I allow cross-origin calls using the following: code http://delphi.org/2015/04/cors-on-datasnap-rest-server/

Running the Server as an StandAlone application I can see in the WebModuleBeforeDispatch event that the TWebRequest gets an Access-Control-Request-Headers with the value "Pragma" instead of the expected Pragma Header, so it looks like the browser is issuing a CORS Options request and Datasnap doesn't answers it (it raises a TDSServiceException with the "command closed or unassigned" message).

I have solved it for the StandAlone application, passing the dssession through the URL (it doesn't interfere with the normal pass of parameters because I only use POST calls from AngularJS), and then intercepting the Request on the WebModuleBeforeDispatch event and manually adding a Pragma Header with the dssession retrieved from the calling URL.

procedure TWebModule1.WebModuleBeforeDispatch(Sender: TObject; Request: TWebRequest; 
                                              Response: TWebResponse; var Handled: Boolean);
var Token: string;
begin
  Response.SetCustomHeader('Access-Control-Allow-Origin','*');     // Allow CORS calls

  Token := TIdHTTPAppRequest(Request).Query;         // Set session on Pragma from the URL
  if Copy(Token, 1, 10) = 'dssession=' then begin
    TIdHTTPAppRequest(Request).GetRequestInfo.RawHeaders.AddValue('Pragma', Token);
  end;

  if FServerFunctionInvokerAction <> nil then
    FServerFunctionInvokerAction.Enabled := AllowServerFunctionInvoker;
end;

It works fine on that StandAlone application, but when I recompile my code as an ISAPI module to deploy it on the final production environment, it doesn't add the dssession Pragma Header on the Requests, probably because it doesn't get the dssession passed through the URL, but I can't be sure of the reason because I can’t get my Delphi to debug that ISAPI Module.

I follow this Tutorial: http://edn.embarcadero.com/article/40873 and I can correctly set to run my ISAPI Module, but when I attach the w3wp.exe process to my Delphi debugger it doesn't stop to any break-point (they appear disabled, as like the code was compiled with a Release Build instead of a Debug Build), in fact, the w3wp.exe process seems to be frozen and doesn't attend any call until I detach it from the Delphi debugger.

So, I would appreciate any suggestion to be able to debug that module, and more importantly, to pass the dssession to an ISAPI Module when your browser detects them as Cross-Origin calls.

Thank you very much.

Marc Guillot
  • 6,090
  • 1
  • 15
  • 42
  • does the server CORS support include preflight handling? the last two comments on the link you provided suggests not – Jaromanda X Aug 05 '16 at 09:39
  • I can't find any documentation about it, but indeed, it seems Datasnap doesn't support preflight handling :-( (that's why I tried to avoid it, not using the customized header Pragma and send the dssession through the URL). – Marc Guillot Aug 05 '16 at 10:16
  • I don't know enough about Angular, but if it is indeed all client-side (javascript) files, why not host both from the same IIS instance? Optionally using virtual directories. The the cross-domain check would not fire. – Stijn Sanders Aug 05 '16 at 22:45
  • We are two different companies coding the front-end and the back-end, and our bosses want to have their code on their own servers. :-( – Marc Guillot Aug 08 '16 at 11:18

1 Answers1

4

I have finally found a neat solution to set Datasnap to answer to CORS requests as it's supposed to answer them.

When your Datasnap receives a COR request on the WebModule.Before dispatch event, you just have to answer to allow sending the customized header (Pragma), it's important to set Handled to True, so Datasnap won't try to manage that OPTION request as a normal request calling for a method.

procedure TWebModule1.WebModuleBeforeDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
  Response.SetCustomHeader('Access-Control-Allow-Origin','*');        

  if Trim(Request.GetFieldByName('Access-Control-Request-Headers')) <> '' then 
  begin 
    Response.SetCustomHeader('Access-Control-Allow-Headers', Request.GetFieldByName('Access-Control-Request-Headers'));        
    Handled := True;
  end;

  if FServerFunctionInvokerAction <> nil then
    FServerFunctionInvokerAction.Enabled := AllowServerFunctionInvoker;
end;
Marc Guillot
  • 6,090
  • 1
  • 15
  • 42
  • 1
    In my environment (Delphi XE2, simple WebBroker ISAPI module, apache 2.4 with mod_isapi) I had to change the Access-Control-Request-Headers to Access_Control_Request_Headers because in Delphi XE2 TISAPIRequest.GetFieldByName(...) uses HTTP_ prefix instead of HEADER_. For HTTP_ and HEADER_ See https://msdn.microsoft.com/en-us/library/ms524602%28v=vs.90%29.aspx?f=255&MSPPError=-2147217396 – Chris Sep 16 '16 at 14:17
  • 1
    I also added if Request.Method = 'OPTIONS' then Handled = true to handle any pre-flight requests – Maya Sep 10 '18 at 15:07