What is the easiest way to do an HTTPS POST request in Delphi? I'm not having problems with making HTTP POST requests, but how can I do it using SSL? I've googled around and haven't found anything that explains this well enough.
Here's the code I tried:
procedure TForm1.FormCreate(Sender: TObject);
var
responseXML:TMemoryStream;
responseFromServer:string;
begin
responseXML := TMemoryStream.Create;
IdSSLIOHandlerSocketOpenSSL1 := TIdSSLIOHandlerSocketOpenSSL.Create(self);
with idSSLIOHandlerSocketOpenSSL1 do
begin
SSLOptions.Method := sslvSSLv2;
SSLOptions.Mode := sslmUnassigned;
SSLOptions.VerifyMode := [];
SSLOptions.VerifyDepth := 0;
host := '';
end;
IdHTTP1 := TIdHTTP.Create(Self);
with IdHTTP1 do
begin
IOHandler := IdSSLIOHandlerSocketOpenSSL1;
AllowCookies := True;
ProxyParams.BasicAuthentication := False;
ProxyParams.ProxyPort := 0;
Request.ContentLength := -1;
Request.ContentRangeEnd := 0;
Request.ContentRangeStart := 0;
Request.Accept := 'text/html, */*';
Request.BasicAuthentication := False;
Request.UserAgent := 'Mozilla/3.0 (compatible; Indy Library)';
HTTPOptions := [hoForceEncodeParams];
end;
responsefromserver := IdHTTP1.Post('https://.../','name1=value1&name2=value2&....');
end;
When I try to run it I get the following error:
Project myProject.exe raised exception class EFOpenError with message 'Cannot open file "C:\...\Projects\Debug\Win32\name1=value1name2=value2 The system cannot find the file specified'.
I don't understand that. I sent parameters, though the errors sounds like I would have sent a file.
Also I have included libeay32.dll and ssleay32.dll within my myProject.exe folder.
You didn't specified your Delphi version or indy version, but I had some problems before with the bundled Indy with Delphi 2009 and HTTPS, and when I got the latest source from indy svn, the problem solved.
http://chee-yang.blogspot.com/2008/03/using-indy-https-client-to-consume.html
var S: TStringList;
M: TStream;
begin
S := TStringList.Create;
M := TMemoryStream.Create;
try
S.Values['Email'] := 'your google account';
S.Values['Passwd'] := 'your password';
S.Values['source'] := 'estream-sqloffice-1.1.1.1';
S.Values['service'] := 'cl';
IdHTTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL1;
IdHTTP1.Request.ContentType := 'application/x-www-form-urlencoded';
IdHTTP1.Post('https://www.google.com/accounts/ClientLogin', S, M);
Memo1.Lines.Add(Format('Response Code: %d', [IdHTTP1.ResponseCode]));
Memo1.Lines.Add(Format('Response Text: %s', [IdHTTP1.ResponseText]));
M.Position := 0;
S.LoadFromStream(M);
Memo1.Lines.AddStrings(S);
finally
S.Free;
M.Free;
end;
end;
So the short answer is simple, use a COM-Object with flexible late binding, example of a translate service with language detection, implements in maXbox script:
function getPostTranslateLibre(feedstream: string): string;
var
Url,API_KEY, source: string;
jo, locate: TJSONObject;
httpReq,hr: Olevariant;
strm: TStringStream;
begin
httpReq:= CreateOleObject('WinHttp.WinHttpRequest.5.1');
// Open the HTTPs connection.
try
hr:= httpReq.Open('POST','https://libretranslate.pussthecat.org/detect', false);
httpReq.setRequestheader('user-agent',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:98.0) Gecko/20100101 Firefox/98.0');
httpReq.setRequestheader('content-type','application/x-www-form-urlencoded');
//httpReq.setRequestheader('X-RapidAPI-Host','nlp-translation.p.rapidapi.com');
//httpReq.setRequestheader('X-RapidAPI-Key','...333');
if hr= S_OK then HttpReq.Send('q='+HTTPEncode(feedstream));
/// Send HTTP Post Request & get Responses.
If HttpReq.Status = 200 Then
result:= HttpReq.responseText
Else result:= 'Failed at getting response:'+itoa(HttpReq.Status)+HttpReq.responseText;
//writeln('debug response '+HttpReq.GetAllResponseHeaders);
finally
httpreq:= unassigned;
end;
end;
As early binding compiled we use LHttpClient: TALWininetHttpClient;
https://github.com/infussolucoes/usercontrol-sd/blob/master/Source/Terceiros/Alcinoe/ALHttpClient.pas
Another alternative to Indy is Synapse.
This class library offers full control of the post, but also offers a simple one liner post method as well:
function HttpPostURL(const URL, URLData: string; const Data: TStream): Boolean;
Related
I am writing an image downloader software. But I have problem to download some images such as:
https://books.google.com/books/content?id=8_pCYmpCu6UC&pg=PT4&img=1&zoom=3&hl=en&bul=1&sig=ACfU3U10JF_yd0n7gvCllBts88O_ufTWxA&w=1280
You can check it in your browser (You should see a page with 1.Introduction title. If you could not see, it is related to your region restriction. Using US proxy would solve it).
I'm using Delphi 10.3 update 2 in Win 10. My project is 32 version.
1-I used TIdHttp to download the file but I got another image (An Image with this content: "Image not available").
procedure DownloadeImage(FURL, FFileName :string);
var
HTTPClient: TIdHTTP;
FileStream: TFileStream;
begin
try
HTTPClient := TIdHTTP.Create;
HTTPClient.HandleRedirects := True;
HTTPClient.AllowCookies := True;
//HTTPClient.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
//(HTTPClient.IOHandler as TIdOpenSSLIOHandlerClient).Options.VerifyServerCertificate := False;
HTTPClient.Request.UserAgent := 'Mozilla/5.0'; //'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0';
HTTPClient.Request.Connection := 'keep-alive';
try
FileStream := TFileStream.Create(FFileName, fmCreate);
try
HTTPClient.Get(FURL, FileStream); //(TIdURI.URLEncode(FURL), FileStream);
finally
FileStream.Free;
end;
finally
HTTPClient.Free;
end;
except
// error handling ignored for this example
end;
end;
2-The same result when using TNetHttpClient
procedure DownloadeImage(FURL, FFileName :string);
var
HTTPClient: TNetHTTPClient;
FileStream: TFileStream;
begin
try
HTTPClient := TNetHTTPClient.Create(nil);
HTTPClient.HandleRedirects := True;
HTTPClient.AllowCookies := True;
//HTTPClient.SecureProtocols := [THTTPSecureProtocol.SSL2, THTTPSecureProtocol.SSL3, THTTPSecureProtocol.TLS1, //THTTPSecureProtocol.TLS11, THTTPSecureProtocol.TLS12];
//HTTPClient.Accept := 'image/png, image/gif, image/jpg, image/jpeg, image/tif, image/tiff, image/bmp, //image/x-bmp;q=0.9,*/*;q=0.8';
HTTPClient.UserAgent := 'Mozilla/5.0';
try
FileStream := TFileStream.Create(FFileName, fmCreate);
try
HTTPClient.Get(FURL, FileStream); //(TIdURI.URLEncode(FURL), FileStream);
finally
FileStream.Free;
end;
finally
HTTPClient.Free;
end;
except
// error handling ignored for this example
end;
end;
3-But I get the goal using the following code:
URLMon.URLDownloadToFile(nil, PChar(FURL), PChar(FFileName), 0, nil)
Why?!!!
I think it is related to some default value of TIdhttp and TNetHttpClient or about some SSL protocol.
Can anyone guide me?
I am new to Delphi and I am assigned with a Delphi 2007 desktop application and have been asked to enable https in that application current it uses Indy 10.1.5.
Below is the code which I have written but could not get expected output.
I am not sure if the DLLs that I use are compatible with it or not.
Error I get is "error connecting with ssl"
var
IdHTTP: TIdHTTP;
Id_HandlerSocket : TIdSSLIOHandlerSocketOpenSSL;
begin
IdHTTP := TIdHTTP.Create;
try
with IdHTTP do
begin
Name := 'IdHTTP';
AllowCookies := True;
RedirectMaximum := 25;
ProxyParams.BasicAuthentication := False;
ProxyParams.ProxyPort := 0;
Request.ContentLength := -1;
Request.Accept := 'text/html, */*';
Request.BasicAuthentication := False;
Request.UserAgent := 'Mozilla/3.0 (compatible; Indy Library)';
HTTPOptions := [hoForceEncodeParams];
end;
Id_HandlerSocket := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP);
Id_HandlerSocket.SSLOptions.Method := sslvSSLv23;
Id_HandlerSocket.SSLOptions.Mode := sslmClient;
// Id_HandlerSocket.Port:= 443 ;
IdHTTP.IOHandler := Id_HandlerSocket;
try
Result := IdHTTP.Get('https://'+WebServer+'/myfol/test_file.txt'); // this is the place where I get error
except on E : Exception do
Begin
Result := E.Message +' || '+E.ClassName ;
End
end;
finally
IdHTTP.Free;
end;
I have to send an XML-File to a Website with a secure Connection. Delphi 2010, Indy 10.5.9. The Code used is as follows:
Params: TIdMultiPartFormDataStream;
ResponseStr: string;
begin
result := 0;
sRootCertFile := 'xxx\Digital.pem';
sCertFile := 'xxx\Digital.pem';
sKeyFile := 'xxx\Digital.pem';
with FAdminSetup.RDWSSLHandler do
begin
SSLOptions.VerifyMode := [];
SSLOptions.VerifyDepth := 0;
SSLOptions.RootCertFile := sRootCertFile;
SSLOptions.CertFile := sCertFile;
SSLOptions.KeyFile := sKeyFile;
end;
sURL := 'https://xxx/xxxservice';
begin
IdHttpVVO := TIdHttp.Create(nil);
try
// IdHttpVVO.Request.ContentType := 'multipart/form-data';
// IdHttpVVO.ProtocolVersion := pv1_1;
// IdHttpVVO.HTTPOptions := [hoKeepOrigProtocol,hoForceEncodeParams];
// IdHttpVVO.Request.Connection := 'Keep-Alive';
// IdHttpVVO.Request.CacheControl := 'no-cache';
// IdHttpVVO.Request.ContentLength := Length(sAnsiXML); // <-- new
IdHttpVVO.IOHandler := FAdminSetup.RDWSSLHandler;
Params := TIdMultiPartFormDataStream.Create;
try
with Params do
begin
AddFile('file', filename, GetMIMETypeFromFile(filename));
end;
resultStr := IdHttpVVO.Post(sURL, Params);
finally
Params.Free;
end;
ShowMessage(resultstr);
The result is always the same:
'HTTP/1.0 500 Error'
when doing the post part.
All the remarks have been tried and did not give any change. The Password for the connection is supplied as follows:
procedure TFAdminSetup.RDWSSLHandlerGetPasswordEx(ASender: TObject;
var VPassword: AnsiString; const AIsWrite: Boolean);
begin
VPassword := 'xxx';
end;
The Website is working, the certificates seem to be ok, as there is a small tool included written in C that works.
Where is my mistake? Thanks
I am working on a program which share data to LinkedIn User’s Profile. I am using Delphi XE2, OAuth and LinkedIn API for the same. I am able to get Access token. Then I want to update status. So my code is
procedure TForm1.Button2Click(Sender: TObject);
var
IdSSLIOHandlerSocketOpenSSL1: TIdSSLIOHandlerSocketOpenSSL ;
Url,sign : String;
Response : TStringStream;
slist : TStringList;
str : WideString;
Arequest1 : TOAuthRequest;
AuthHeader : WideString;
begin
with http do
Begin
IdSSLIOHandlerSocketOpenSSL1 := TIdSSLIOHandlerSOcketOpenSSL.Create(nil);
with idSSLIOHandlerSocketOpenSSL1 do
begin
SSLOptions.Method := sslvTLSv1;
SSLOptions.SSLVersions := [sslvTLSv1];
SSLOptions.Mode := sslmBoth;
SSLOptions.VerifyMode := [];
SSLOptions.VerifyDepth := 0;
host := '';
end;
IOHandler := IdSSLIOHandlerSocketOpenSSL1;
AllowCookies := True;
Request.ContentRangeEnd := 0;
Request.ContentRangeStart := 0;
Request.ContentType := 'application/xml';
Request.ContentEncoding := 'utf-8';
Request.BasicAuthentication := False;
Request.Connection := 'Keep-Alive';
request.host := 'api.linkedin.com';
Request.Accept := 'text/xml, */*';
Request.UserAgent := 'Mozilla/5.0 (Windows NT 5.1; rv:13.0) Gecko/20100101 Firefox/13.0' ;
HTTPOptions := [hoForceEncodeParams];
end;
Url := 'https://api.linkedin.com/v1/people/~/shares';
Consumer := nil;
Consumer := TOAuthConsumer.Create(ConsumerKey, ConsumerSecret);
Arequest1 := TOAuthRequest.Create(Url);
sign := HMAC.build_signature(Arequest1,Consumer,Token);
sign := TOAuthUtil.urlEncodeRFC3986(sign);
http.Request.CustomHeaders.Clear;
AuthHeader := 'OAuth '+ 'oauth_nonce="'+Arequest1.GenerateNonce+'",'+ 'oauth_signature_method="'+HMAC.get_name+'",'+ 'oauth_timestamp="'+Timestamp+'",'+ 'oauth_consumer_key="'+Consumer.Key+'",'+ 'oauth_token="'+Token.Key+'",'+ 'oauth_signature="'+sign+'",'+ 'oauth_version="1.0"';
http.Request.CustomHeaders.Add(AuthHeader);
slist := TStringList.Create;
slist.Text := '<?xml version="1.0" encoding="UTF-8"?><share><comment>Posting from the API using XML</comment>'+
'<content><title>A title for your share</title><submitted-url>http://developer.linkedin.com</submitted-url>'+
'<submitted-image-url>http://lnkd.in/Vjc5ec</submitted-image-url></content><visibility>'+
'<code>anyone</code></visibility></share>';
Response := TStringStream.Create;
http.Request.ContentLength := length(slist.Text);
try
http.post(Url,slist,Response);
Finally
ShowMessage(Response.DataString);
http.Free;
ARequest.Free;
end;
end;
I am facing 401 unauthorized. Please check my ques #A1rPun. Please help me
Thank you for providing a full code sample. I've seen you commented on your previous question that you use indy 10 in Delphi XE2.
The problem could be that there is something wrong with the SSL authentication within indy 10. Full explanation here What you can do is trying to get a stable version of the indy components.
The code seem to have no errors except for a couple of resources that could have been freed ;).
I really hope when updating your indy your problem will solve.
I am using Indy10 with Delphi and trying to get Google's OAuth to work. The first step is to make a request to the OAuthGetRequestToken method. The code below returns a 400 error. Any help would be greatly appreciated.
procedure TForm1.Button1Click(Sender: TObject);
var
IdHTTP: TIdHTTP;
IdSSLIOHandlerSocket1: TIdSSLIOHandlerSocketOpenSSL;
Params: TStringList;
mString: String;
begin
Params := tstringlist.create;
IdSSLIOHandlerSocket1 := TIdSSLIOHandlerSocketOpenSSL.create(nil);
IdHTTP := TIdHTTP.create(nil);
with IdSSLIOHandlerSocket1 do begin
SSLOptions.Method := sslvSSLv3;
SSLOptions.Mode := sslmUnassigned;
SSLOptions.VerifyMode := [];
SSLOptions.VerifyDepth := 2;
end;
with IdHTTP do begin
IOHandler := IdSSLIOHandlerSocket1;
ReadTimeout := 0;
AllowCookies := True;
ProxyParams.BasicAuthentication := False;
ProxyParams.ProxyPort := 0;
Request.ContentLength := -1;
Request.ContentRangeEnd := 0;
Request.ContentRangeStart := 0;
Request.ContentType := 'application/x-www-form-urlencoded';
request.host := 'https://www.google.com';
Request.Accept := 'text/html, */*';
Request.BasicAuthentication := False;
Request.UserAgent := 'Mozilla/3.0 (compatible; Indy Library)';
HTTPOptions := [hoForceEncodeParams];
end;
Params.Add('scope=https://www.google.com/analytics/feeds/');
Params.Add('oauth_consumer_key=anonymous');
Params.Add('oauth_signature_method=HMAC-SHA1');
Params.Add('oauth_signature=wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D');
Params.Add('oauth_timestamp=137131200');
Params.Add('oauth_nonce=4572616e48616d6d65724c61686176');
showmessage(HTTPDecode(IdHTTP.Post('https://www.google.com/accounts/OAuthGetRequestToken',Params)));
end;
Look at the response DataString (where the Exception is raised), it will give you a more detail reason for the problem.
For instance, trying with your code, I first got a "timestamp too far from current time", then after updating the oauth_timestamp, I got an "Invalid Signature"...
Check if you get a return message in the response header, too, besides the 400. Most web services provide some kind of textual description on what is going wrong.
Also verify that the OpenSSL library is actually loaded, otherwise Indy might fallback to unencoded communication, which the server might not accept.
Try routing your request through HTTP Fiddler.
I found that to be an excellent tool to trace what goes wrong on http/https level.
--jeroen