How to access request headers in DataSnap Server? - delphi

I am using Delphi XE7. I need to access the request headers in DataSnap Server, but it does no seem to have this option. In DataSnap REST, it is possible, because TWebModule is available.
This code does not work, like in REST:
function TServerMethods1.EchoString(Value: string): string;
var
Module: TWebModule;
begin
Module := GetDataSnapWebModule;
Result := Module.Request.RemoteIP + ': ' + Value;
end;
Does anyone have an idea about DataSnap Server?

I update my project for REST, and resolved my problem in access Header. With the following code:
Need declare Web.HTTPApp at uses
var
oWebModule: TWebModule;
sHeader: String;
begin
oWebModule := GetDataSnapWebModule;
sHeader := oWebModule.Request.Content;
end;

Related

How to download a very simple HTTPS page in Delphi?

I tried a code I saw here but it didn't work for HTTPS. I need to download this page as a String, and add some Break lines on it to put the informations in order in a TMemo.
How to do it? I tried to use Indy but I failed because of the SSL.
I tried the solutions of this page: How to download a web page into a variable?
How to download this page https://api.rastrearpedidos.com.br/api/rastreio/v1?codigo=OP133496280BR thats just pure text and put in in a String? and also format it like that, in the lines of a TMemo:
"Objeto em trĂ¢nsito - por favor aguarde"
"cidade":"SAO JOSE DOS CAMPOS"
"uf":"SP"
"dataHora":"18/06/2021 16:53"
"descricao":"Objeto postado","cidade":"SAO JOSE DOS CAMPOS","uf":"SP"
It's portuguese, English isn't my first language. Thanks if you guys could help me. I Use the Embarcadero Delphi 10.2 Tokyo.
When using Indy, assuming you are using Indy's default TIdSSLIOHandlerSocketOpenSSL component for SSL/TLS support, then make sure you put the 2 OpenSSL DLLs, ssleay32.dll and libeay32.dll, in the same folder as your EXE. You can get them from here:
https://github.com/IndySockets/OpenSSL-Binaries
Note that TIdSSLIOHandlerSocketOpenSSL only supports up to OpenSSL 1.0.2. If you need to use OpenSSL 1.1.x instead (for TLS 1.3+, etc), then use this SSLIOHandler instead. You will have to obtain the relevant OpenSSL DLLs from elsewhere, or compile them yourself.
Either way, once you decide which SSLIOHandler you want to use, the code is fairly simple:
var
HTTP: TIdHTTP;
SSL: TIdSSLIOHandlerSocketOpenSSL;
Response: string;
begin
HTTP := TIdHTTP.Create(nil);
try
// configure HTTP as needed (version, headers, etc)...
SSL := TIdSSLIOHandlerSocketOpenSSL.Create(HTTP);
// configure SSL as needed (TLS versions, certificates, etc)...
HTTP.IOHandler := SSL;
Response := HTTP.Get('https://api.rastrearpedidos.com.br/api/rastreio/v1?codigo=OP133496280BR');
finally
HTTP.Free;
end;
// use Response as needed...
end;
You also can use:
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
Request: TScHttpWebRequest;
Response: TScHttpWebResponse;
ResponseStr: String;
JSonValue: TJSonValue;
JsonArray: TJsonArray;
Name: String;
UserName: String;
Email: String;
begin
Request := TScHttpWebRequest.Create('https://jsonplaceholder.typicode.com/users');
try
Response := Request.GetResponse;
try
if Request.IsSecure then begin
ResponseStr := Response.ReadAsString;
JsonArray := TJSonObject.ParseJSONValue(ResponseStr) as TJsonArray;
try
for i:=0 to JsonArray.Count - 1 do begin
JsonValue := JsonArray.Items[i];
Name := JsonValue.GetValue('name');
UserName := JsonValue.GetValue('username');
Email := JsonValue.GetValue('email');
Memo1.Lines.Add('Name: ' + Name + ' - UserName: ' + UserName + ' - Email: ' + Email);
end;
finally
JsonArray.Free;
end;
end;
finally
Response.Free;
end;
finally
Request.Free;
end;
end;

Delphi XE 5 - THTTPRIO - preemptive connection

I have a web service client created with DELPHI XE 5, i used THTTPRIO, imported wsdl and created the ".pas" files.
The server of web service request a preemptive basic authentication (that work fine with SoapUI);
I know how to make authentication with user and password of THTTPReqResp.
rio := THTTPRIO.Create(nil);
HTTPReqResp1 := THTTPReqResp.Create(rio);
HTTPReqResp1.UserName := sUserName;
HTTPReqResp1.Password := sPasswordEncrypted;
But I don't connect to the web service because I don't know ho to make preemptive and if it's possible in Delphi.
Can anyone help me ? :)
I just found how It's possible to use "preemprive" connexion in DELPHI.
You have to modify the OnBeforePost of your "rio.HTTPWebNode" like this :
var
rio : THTTPRIO;
HTTPReqResp1: THTTPReqResp;
uselessObject: TUselessClass;
try
rio := THTTPRIO.Create(nil);
uselessObject := TUselessClass.Create();
rio.HTTPWebNode.GetHTTPReqResp.OnBeforePost :=
uselessObject.HTTPRIOHTTPWebNode1BeforePost;
With this procedure :
procedure TUselessClass.HTTPRIOHTTPWebNode1BeforePost(const HTTPReqResp: THTTPReqResp; Data: Pointer);
var
auth,toEncode: string;
begin
toEncode := Format('%s:%s',[sUserName,sPasswordEncrypted]);
auth := 'Authorization: Basic ' + TIdEncoderMIME.EncodeString(toEncode);
HttpAddRequestHeaders(Data, PChar(auth), Length(auth), HTTP_ADDREQ_FLAG_ADD);
end;

HTTP.post versus downloadURL using Delphi XE2

I am trying to use the same procedure for two types of downloads. both are working.
I would like to use TDownloadURL or HTTP.Post on both, but cannot determine how. Which method and how to do this? Thank you.
First operation --
procedure TfrmMain.get1Click(Sender: TObject);
var
json: string;
lHTTP: TIdHTTP;
lParamList: TStringList;
result:string;
begin
json := CRLF +
'{' + CRLF +
' "resource_id": "391792b5-9c0a-48a1-918f-2ee63caa1c54",' + CRLF +
' "filters": {' + CRLF +
' "provider_id": 393303' + CRLF +
' }' + CRLF +
'}';
lParamList := TStringList.Create;
try
lParamList.Add('somename='+json);
lHTTP := TIdHTTP.Create(nil);
try
Result := lHTTP.Post('http://hub.Healthdata.gov/api/action/datastore_search', lParamList);
finally
lHTTP.Free;
end;
finally
lParamList.Free;
end;
end;
Second operation --
procedure TfrmMain.get2Click(Sender: TObject);
var
dl: TDownloadURL;
url:string;
begin
url:='http://api.census.gov/data/2010/sf1?key=KEY&get=P0010001,NAME&for=state:*';
dl := TDownloadURL.Create(self);
try
dl.URL := url;
dl.FileName := execpath+'api1.txt'; dl.ExecuteTarget(nil); dl.Free;
except
dl.Free;
end;
end;
TDownloadURL uses the GET HTTP method. TIdHTTP.Post obviously uses the POST method. In general, neither is appropriate for use in place of the other. That's why both methods exist in the first place.
A POST request can include all the information that a GET request does, plus more, which makes it seem like it should be able to do everything GET can do, plus more. However, servers are not required to (and should not be expected to) handle POST requests the same way they do GET.
As the one writing the HTTP client, you're not really in control of the situation. The server dictates which methods it will honor. Clients need to either do what's expected of them or be denied access.
The Indy components support both methods, so if you just want to make your POST code and your GET code look similar, then you can replace TDownloadURL with TIdHTTP.Get.

How to handle TIdHTTPServer with TIdMultiPartFormDataStream

Hi i need help on how to retrieved the parameters and data using IdHttpServer from indy.
many of my application uses TIdMultiPartFormDataStream to send data over the php. I would like to use the TIdHTTPServer to verify parameters for some reason and forward the request to its destination.
i created a short example for you to see.
uses
IdContext, IdMultipartFormData;
// Server Side------------------------------------------------
IdHTTPServer1.Defaultport := 88;
IdHTTPServer1.active := True;
procedure TForm1.IdHTTPServer1CommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
// the request will be pass through its destination by POST/GET
// and send the result back to the client apps.
AResponseInfo.ContentText := ARequestInfo.Params.Text;
end;
// Client Side------------------------------------------------
// This will work using the standard Post or Get
procedure TForm1.btnPost1Click(Sender: TObject);
var
sl: TStringList;
res: String;
begin
sl := TStringList.Create;
try
sl.Add('Param1=Data1');
sl.Add('Param2=Data1');
sl.Add('Param3=Data2');
sl.Add('Param4=Data3');
res := IdHTTP1.Post('http://localhost:88/some.php', sl);
ShowMessage(res);
finally
sl.Free;
end;
end;
//how can i get the parameters and value for this code in my IdHttpServer
procedure TForm1.btnPost2Click(Sender: TObject);
var
mfd: TIdMultiPartFormDataStream;
res: String;
begin
mfd := TIdMultiPartFormDataStream.Create;
try
mfd.AddFormField('Param1', 'Data1');
mfd.AddFormField('Param2', 'Data1');
mfd.AddFormField('Param3', 'Data2');
mfd.AddFormField('Param4', 'Data3');
res := IdHTTP1.Post('http://localhost:88/some.php', mfd);
ShowMessage(res);
finally
mfd.Free;
end;
end;
and how would i know if the Client apps pass a TIdMultiPartFormDataStream type of parameter?
This has been asked and answered many times before in the Embarcadero and Indy forums. Please search through their archives, as well as other archives, like Google Groups, to find code examples.
In a nutshell, when the TIdHTTPServer.OnCommandGet event is triggered, if the AResponseInfo.ContentType property says multipart/form-data (the version of TIdHTTP.Post() you are using will send application/x-www-form-urlencoded instead), the AResponseInfo.PostStream property will contain the raw MIME data that the client posted. You can use the TIdMessageDecoderMIME class to parse it. However, that class was never intended to be used server-side, so it is not very intuitive to use, but it is possible nontheless.
In Indy 11, I am planning on implementing native multipart/form-data parsing directly into TIdHTTPServer itself, but there is no ETA on that yet as we have not started work on Indy 11 yet.

SOAP client in Delphi "The handle is in the wrong state for the requested operation"

I have built the worlds dumbest and most simple SOAP server, in about 3 clicks, in visual studio. The exact steps in visual studio 2010: First create a new project as a web application, Then add a new item of type web service. (See accepted answer here for picture.) That soap server service Service1 has a simple method GetData:
A snippet from clientService1.pas, created using WSDL importer...
IService1 = interface(IInvokable)
['{967498E8-4F67-AAA5-A38F-F74D8C7E346A}']
function GetData(const value: Integer): string; stdcall;
function GetDataUsingDataContract(const composite: CompositeType2): CompositeType2; stdcall;
end;
When I try to run this method, like this:
procedure TForm3.Button1Click(Sender: TObject);
var
rio : THTTPRIO;
sv:IService1;
addr : string;
data : string;
begin
//addr := '....'; // url from visual studio 2010 live debug instance.
rio := THTTPRIO.Create(nil);
sv := GetIService1( true, addr, rio );
try
data := sv.GetData( 0);
Button1.Caption := data;
finally
sv := nil;
rio.Free;
end;
end;
The error I get is this:
ESOAPHTTPException:
The handle is in the wrong state for the requested operation -
URL:http://localhost:8732/Design_Time_Addresses/WcfServiceLibrary1/Service1/ -
SOAPAction:http://tempuri.org/IService1/GetData'.
The URL works fine when I paste the url above into a web browser, so the usual answer that the SOAP code in Delphi has the tendency to not notice an HTTP failure, does not seem likely. Rather it seems that I am either (a) experiencing breakage in WinInet (known to happen in some versions of windows), or (b) doing something wrong?
It seems to me that anybody who has visual studio and delphi both installed, should be able to try to get the dummy starter Soap server in Visual Studio talking to the soap client in Delphi, without any effort at all. But I can not figure out the simplest things.
At one time there was a discussion about the error in a conversation now long since deleted from Embarcadero forums, by Bruneau Babet, an embarcadero staffer.
Bruno said:
Hello,
I've posted a patched version of SOAPHTTPTrans.pas that contains a fix
for this issue here:
[forum link redacted, it didn't work anymore anyways, the post is gone]
You may still override the event as described in the C++Builder
section referred; or, much simpler, at least for Delphi users, simply
add the updated SOAPHTTPTrans.pas to your app's project. Let us know
if that does not work for you.
Cheers,
Bruneau
You can get the repair and the notes about it in its original forum formatting from the following pastebin link and on bitbucket so you don't have to extract the file from the surrounding text.
Warren Update 2016: I have been informed by someone who tried to use the fix on Delphi XE that this fix does NOT work for them in Delphi XE. Any further updates to the code in bitbucket that resolve the remaining bugs would be appreciated.
I ran into the The handle is in the wrong state for the requested operation issue in November 2018 using Delphi Tokyo 10.2.3, then looked at the code patch in the pastebin link under Arjen's answer.
That code is very old and the test code no longer works (SOAP service unavailable). Also, it is unclear from Bruneau's code what he patched exactly.
Comparing that source and the one from my Delphi version it seems that these are the (two) required modifications in the HandleWinInetError procedure ('PATCH HERE'):
function THTTPReqResp.HandleWinInetError(LastError: DWord;
Request: HINTERNET;
RaiseError: Boolean): DWord;
function CallInternetErrorDlg: DWord;
var
P: Pointer;
begin
Result := InternetErrorDlg(GetDesktopWindow(), Request, LastError,
FLAGS_ERROR_UI_FILTER_FOR_ERRORS or
FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS or
FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, P);
{ After selecting client certificate send request again,
Note: InternetErrorDlg always returns ERROR_SUCCESS when called with
ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED }
if LastError = ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED then
Result := ERROR_INTERNET_FORCE_RETRY;
end;
const
{ Missing from our WinInet currently }
INTERNET_OPTION_CLIENT_CERT_CONTEXT = 84;
var
Flags, FlagsLen, DWCert, DWCertLen: DWord;
ClientCertInfo: IClientCertInfo;
CertSerialNum: string;
{$IFDEF CLIENT_CERTIFICATE_SUPPORT}
hStore: HCERTSTORE;
CertContext: PCERT_CONTEXT;
{$ENDIF}
begin
{ Dispatch to custom handler, if there's one }
if Assigned(FOnWinInetError) then
Result := FOnWinInetError(LastError, Request)
else
begin
Result := ERROR_INTERNET_FORCE_RETRY;
{ Handle INVALID_CA discreetly }
if (LastError = ERROR_INTERNET_INVALID_CA) and (soIgnoreInvalidCerts in InvokeOptions) then
begin
FlagsLen := SizeOf(Flags);
InternetQueryOption(Request, INTERNET_OPTION_SECURITY_FLAGS, Pointer(#Flags), FlagsLen);
Flags := Flags or SECURITY_FLAG_IGNORE_UNKNOWN_CA;
InternetSetOption(Request, INTERNET_OPTION_SECURITY_FLAGS, Pointer(#Flags), FlagsLen);
end
else if (LastError = ERROR_INTERNET_SEC_CERT_REV_FAILED) and (soIgnoreInvalidCerts in InvokeOptions) then
begin
FlagsLen := SizeOf(Flags);
InternetQueryOption(Request, INTERNET_OPTION_SECURITY_FLAGS, Pointer(#Flags), FlagsLen);
Flags := Flags or SECURITY_FLAG_IGNORE_REVOCATION;
InternetSetOption(Request, INTERNET_OPTION_SECURITY_FLAGS, Pointer(#Flags), FlagsLen);
end
{$IFDEF CLIENT_CERTIFICATE_SUPPORT}
else if (LastError = ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED) and
Supports(Self, IClientCertInfo, ClientCertInfo) and
(ClientCertInfo.GetCertSerialNumber <> '') then
begin
CertSerialNum := ClientCertInfo.GetCertSerialNumber();
hStore := ClientCertInfo.GetCertStore();
if hStore = nil then
begin
hStore := CertOpenSystemStore(0, PChar('MY'));
ClientCertInfo.SetCertStore(hStore);
end;
CertContext := FindCertWithSerialNumber(hStore, CertSerialNum);
if CertContext <> nil then
begin
ClientCertInfo.SetCertContext(CertContext);
InternetSetOption(Request, INTERNET_OPTION_CLIENT_CERT_CONTEXT,
CertContext, SizeOf(CERT_CONTEXT));
end
else
begin
if RaiseError then RaiseCheck(LastError); // PATCH HERE
Result := CallInternetErrorDlg;
end;
end
{$ENDIF}
else if (LastError = ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED) and (soPickFirstClientCertificate in InvokeOptions) then
begin
{ This instructs WinInet to pick the first (a random?) client cerficate }
DWCertLen := SizeOf(DWCert);
DWCert := 0;
InternetSetOption(Request, INTERNET_OPTION_SECURITY_SELECT_CLIENT_CERT,
Pointer(#DWCert), DWCertLen);
end
else
begin
if RaiseError then RaiseCheck(LastError); // PATCH HERE
Result := CallInternetErrorDlg;
end;
end;
end;
Note that the RaiseError procedure parameter was not even used before this patch ;-)
Here is some test code using the SOAP service from NOAA's National Digital Forecast Database (NDFD) SOAP Web Service:
Uses SOAP.SOAPHTTPTrans;
const Request2 =
'<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ndf="http://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl">' +
' <soapenv:Header/>' +
' <soapenv:Body>' +
' <ndf:NDFDgenByDay soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">' +
' <latitude xsi:type="xsd:decimal">38.9936</latitude>' +
' <longitude xsi:type="xsd:decimal">-77.0224</longitude>' +
' <startDate xsi:type="xsd:date">%tomorrow%</startDate>' +
' <numDays xsi:type="xsd:integer">5</numDays>' +
' <Unit xsi:type="dwml:unitType" xmlns:dwml="http://graphical.weather.gov/xml/DWMLgen/schema/DWML.xsd">e</Unit>' +
' <format xsi:type="dwml:formatType" xmlns:dwml="http://graphical.weather.gov/xml/DWMLgen/schema/DWML.xsd">12 hourly</format>' +
' </ndf:NDFDgenByDay>' +
' </soapenv:Body>' +
'</soapenv:Envelope>';
const URL2= 'https://graphical.weather.gov:443/xml/SOAP_server/ndfdXMLserver.php';
procedure TFrmHandleWinINetError.Button1Click(Sender: TObject);
var
RR: THTTPReqResp;
Response: TMemoryStream;
U8: UTF8String;
begin
RR := THTTPReqResp.Create(nil);
try
try
RR.URL := URL2;
RR.UseUTF8InHeader := True;
RR.SoapAction := 'NDFDgenByDay';
Response := TMemoryStream.Create;
RR.Execute(Request2, Response);
SetLength(U8, Response.Size);
Response.Position := 0;
Response.Read(U8[1], Length(U8));
ShowMessage(String(U8));
except
on E:Exception do ShowMessage('ERROR CAUGHT: ' + e.message);
end;
finally
Response.Free;
RR.Free;
end;
end;
end;
Without the patch errors in the tail end of the URL are caught, but errors in the domain name just trigger an empty error message.
With the patch those are also caught.
I have a reported the issue in the RAD Studio Quality Portal under number RSP-21862
Use at your own risk and please report any additional findings.
Addition: The issue was fixed in Dec 2018 in Delphi 10.3 Rio and the Quality Portal issue was closed with the following remark:
In RAD Studio 10.3 the implementation of THTTPReqResp was changed and replaced with THTTPClient. So, this issue no longer applies.
I have not verified this.

Resources