Delphi XE7 Indy Rest Api post with Ssl Connection Timeout 10060 - delphi

I am trying post under the ssl address service and i got connection timeout 10060., My ssl library and Indy SSl configurations is true because i used same code on email sending with gmail and another services.
I posted with postman it works.
my code
const
Api = 'https://xxxx.xxxx.com/api/detection/Insert';
procedure TRestSender.SendThreats(CustomerId: Integer;
DetectionName, Filename: String);
var
PostData: TStringList;
res: string;
Https: TIdHttp;
IdSSL: TIdSSLIOHandlerSocketOpenSSL;
begin
Https := Tidhttp.Create(nil);
PostData := TStringList.Create;
IdSSL := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
Https.ReadTimeout := 10000;
Https.ConnectTimeout:= 10000;
IdSSL.SSLOptions.Method := sslvTLSv1;
// IdSSL.OnStatusInfo:= ssl1StatusInfo;
IdSSL.SSLOptions.Mode := sslmClient;
Https.IOHandler := IdSSL;
try
PostData.Add('Content-Type:application/x-www-form-urlencoded');
PostData.Add('CustomerId=' + IntToStr(CustomerId));
PostData.Add('DetectionName=' + DetectionName);
PostData.Add('DeviceName=' + ComputerName());
PostData.Add('Filename=' + Filename);
PostData.Add('ApiUser=' + 'some-code');
PostData.Add('ApiPass=' + 'some-paswd');
res := Https.Post(Api, PostData);
finally
PostData.Free;
Https.Free;
IdSSL.Free;
end;
end;

I have two suggestions:
Wrong TLS version: More and more services disable TLS 1.0 and/or TLS1.1. The default version is TLS 1.0.
const
DEF_SSLVERSION = sslvTLSv1;
DEF_SSLVERSIONS = [sslvTLSv1];
So add the following line:
IdSSL.SSLOptions.SSLVersions := [sslvTLSv1_2, sslvTLSv1_1, sslvTLSv1];
Missing SNI support (an example for SNI).

Related

Delphi: TOAuth2Authenticator with client_credentials

A while ago I wrote a method in Delphi 2010 to get the OAuth2 token using the Indy components (TidHttp). See code below.
I am now doing something new in Delphi 10.4 and would like to use the REST components such as RESTClient, RESTRequest, TOAuth2Authenticator, etc.
Our grant type is Client Credentials but in none of the examples on the net could I find on how to use TOAuth2Authenticator with Client Credentials. Is it even possible?
We have a client id, client secret and token URL. We do not have authorization or redirect endpoints. In Insomnia, the setup will look like this:
Does somebody know how to get the token using TOAuth2Authenticator with grant type = client_credentials?
Here is the Delphi 2010 code:
procedure TfrmToken.btnGetTokenClick(Sender: TObject);
var
IdHTTP: TidHttp;
lsHttpError: string;
loRequest: TStringStream;
loRespJson: TMemoryStream;
liSuper: iSuperObject;
ldtExpiry: TDateTime;
begin
IdHTTP := TIdHTTP.Create();
loRespJson := TMemoryStream.Create();
try
IdHTTP.HandleRedirects := False;
loRequest := TStringStream.Create('grant_type=client_credentials&client_id=' +
edtKey.Text + '&client_secret='+edtSecret.Text);
try
IdHttp.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(IdHttp);
IdHttp.Request.ContentType := 'application/x-www-form-urlencoded';
try
IdHTTP.Post(edtURL.Text, loRequest, loRespJson);
except
on E: EIdHTTPProtocolException do
begin
lsHttpError := E.ErrorMessage;
end;
on E: Exception do
begin
lsHttpError := E.Message;
end;
end;
if idHTTP.ResponseCode = 200 then
begin
liSuper := SO(StreamToString(loRespJSon));
edtToken.Text := liSuper.S['access_token'];
ldtExpiry := IncSecond(Now, liSuper.i['expires_in']);
edtExpiry.Text := 'Expires in ' + liSuper.S['expires_in'] +
' seconds. Time: ' +
FormatDateTime('yyyy/dd/mm hh:nn:ss', ldtExpiry);
end
else
begin
liSuper := SO(lsHttpError);
edtToken.Text := IdHTTP.ResponseText;
edtExpiry.Text := '';
end;
finally
FreeAndNil(loRequest);
end;
finally
FreeAndNil(IdHTTP);
FreeAndNil(loRespJson);
end;
end;

How to send a push notification to APNS via TIdHttp indy component?

I have been using Indy's TIdTCPClient component successfully for quite a while now to send push notifications to my mobile app users. Lately, due to new push notification requirements of Apple, this feature has stopped being functional for iOS users.
For the past three days, I've been trying to reconstruct this function by using the TIdHTTP component instead, following the new HTTP/2 format advised by Apple to send push notifications. Unfortunately, I've yet to come up with something operational.
Here's my failing code:
procedure TForm5.BtnPushClick(Sender: TObject);
var
vHTTP: TIdHTTP;
SslIOHandlerHTTP : TIdSSLIOHandlerSocketOpenSSL;
RequestBody: TStream;
ResponseBody, vCertFileName: string;
vJsonToSend : TStringStream;
begin
try
vCertFileName := 'apns-dist.pem';
vIdHTTP := TIdHTTP.Create(nil);
SslIOHandlerHTTP := TIdSSLIOHandlerSocketOpenSSL.Create(vHTTP);
SslIOHandlerHTTP.OnVerifyPeer := SslIOHandlerHTTP1VerifyPeer;
SslIOHandlerHTTP.OnGetPassword := SslIOHandlerHTTP1GetPassword;
SslIOHandlerHTTP.SSLOptions.CertFile := vCertFileName;
SslIOHandlerHTTP.SSLOptions.KeyFile := vCertFileName;
SslIOHandlerHTTP.SSLOptions.Method := sslvTLSv1_2;
SslIOHandlerHTTP.SSLOptions.Mode := sslmBoth;
SslIOHandlerHTTP.SSLOptions.SSLVersions := [sslvTLSv1_2];
SslIOHandlerHTTP.SSLOptions.VerifyMode := [sslvrfPeer];
SslIOHandlerHTTP.SSLOptions.VerifyDepth := 2;
//--
vHTTP.Request.CustomHeaders.AddValue(':method', 'POST');
vHTTP.Request.CustomHeaders.AddValue(':scheme', 'https');
vHTTP.Request.CustomHeaders.AddValue(':path', '/3/device/111890052839d3ed4f596c94712329e20812464afe30ebd28487c4e65986c6f0');
vHTTP.Request.CustomHeaders.AddValue('host', 'api.push.apple.com');
vHTTP.Request.CustomHeaders.AddValue('apns-id', 'eabeae54-14a8-11e5-b60b-1697f925ec7b');
vHTTP.Request.CustomHeaders.AddValue('apns-push-type', 'alert');
vHTTP.Request.CustomHeaders.AddValue('apns-expiration', '0');
vHTTP.Request.CustomHeaders.AddValue('apns-priority', '10');
vHTTP.Request.CustomHeaders.AddValue('apns-topic', 'com.TS.JACK');
//--
vHTTP.IOHandler := SslIOHandlerHTTP;
vHTTP.Request.Accept := 'application/json';
vHTTP.Request.ContentType := 'application/json';
vHTTP.HTTPOptions := [hoForceEncodeParams];
vHTTP.ProtocolVersion := pv1_1;
SslIOHandlerHTTP.PassThrough := true;
vJsonToSend := TStringStream.Create('{ "aps" : { "alert" : "Hello" } }', TEncoding.UTF8);
ResponseBody := vHTTP.Post('https://api.push.apple.com', vJsonToSend);
except
on E: Exception do
begin
Memo1.Lines.Add(IntToStr(vHTTP.ResponseCode)+':' + E.Message);
end;
end;

IdHTTP + SOCKS + Socks5

I'm getting a general Indy error when using the IdHTTP client component in
conjunction with a SOCKS5 proxy server and using SSL.
If I use the IdHTTP component with my SOCKS5 proxy (and a non https URL),
everything works without problems.
If I use the IdHTTP component with an SSL URL (and no SOCKS5 proxy),
everything works without problems.
If I use the IdHTTP component with an SSL URL and a SOCKS5 proxy i get following error:
Line 405 of the error output
idSocks.pas (raise EIdSocksServerGeneralError.Create(RSSocksServerGeneralError);
Here is my code
var
HTTP : TIdHTTP;
Cookie : TIdCookieManager;
SSL : TIdSSLIOHandlerSocketOpenSSL;
Params : TStringList;
HTMLSource : String;
CurrentProxy : String;
ProxyPort : Integer;
ProxyHost : String;
ProxyUsername : String;
ProxyPW : String;
begin
Synchronize(AddItem);
HTTP := TIdHTTP.Create(NIL);
Cookie := TIdCookieManager.Create(HTTP);
SSL := TIdSSLIOHandlerSocketOpenSSL.Create(HTTP);
HTTP.CookieManager := Cookie;
HTTP.IOHandler := SSL;
HTTP.HandleRedirects := True;
Params := TStringList.Create;
HTTP.Request.UserAgent := Task^.Useragent;
try
while True do begin
if terminated then Exit;
Params.Clear;
Cookie.CookieCollection.Clear;
if Task^.Proxytype >= 0 then begin // if proxy enabled
CurrentProxy := Task^.Form.GetProxyFromPool;
ProxyHost := ParsingW(':', CurrentProxy, 1);
ProxyPort := strtoint(ParsingW(':', CurrentProxy, 2));
HTTP.ConnectTimeout := (Task^.Proxytimeout * 1000);
if Task^.ProxyAuth then begin
ProxyUsername := ParsingW(':', CurrentProxy, 3);
ProxyPW := ParsingW(':', CurrentProxy, 4);
end;
end;
if Task^.Proxytype = 0 then begin //HTTP(s)
HTTP.ProxyParams.ProxyServer := ProxyHost;
HTTP.ProxyParams.ProxyPort := ProxyPort;
if Task^.ProxyAuth then begin
HTTP.ProxyParams.ProxyUsername := ProxyUsername;
HTTP.ProxyParams.ProxyPassword := ProxyPW;
end;
end;
if (Task^.Proxytype = 1) or (Task^.Proxytype = 2) then begin // Socks 4 or 5
SSL.TransparentProxy := TIdSocksInfo.Create(HTTP);
(SSL.TransparentProxy as TIdSocksInfo).Port := ProxyPort;
(SSL.TransparentProxy as TIdSocksInfo).Host := ProxyHost;
if Task^.ProxyAuth then begin
(SSL.TransparentProxy as TIdSocksInfo).Username := ProxyUsername;
(SSL.TransparentProxy as TIdSocksInfo).Password := ProxyPW;
(SSL.TransparentProxy as TIdSocksInfo).Authentication := saUsernamePassword;
end else begin
(SSL.TransparentProxy as TIdSocksInfo).Authentication := saNoAuthentication;
end;
if (Task^.Proxytype = 1) then (SSL.TransparentProxy as TIdSocksInfo).Version := svSocks4;
if (Task^.Proxytype = 2) then (SSL.TransparentProxy as TIdSocksInfo).Version := svSocks5;
end;
Did I miss something or is it not possible to connect to a a SSL site with a Socks5 Proxy?
The fact that you are getting an EIdSocksServerGeneralError raised means that TIdHTTP is successfully communicating with the SOCKS proxy and it is validating your request to access it with no authentication, but it is then failing to establish a connection with the target server that you specified in your HTTPS url. The proxy is replying with error code 1 (general failure). Make sure that the url is accurate. Either the proxy cannot resolve the hostname to an IP (try specifying an IP instead of a hostname in the url and see if it makes a difference), or the proxy does not have a valid route to reach that IP, or some other error is occurring on the proxy end. If you have access to the proxy, try looking at its logs to see what actually went wrong.

Sending message to gmail fails with "Start SSL negotiation command failed." error

Tips i followed is found here.
I do have libeay32.dll and ssleay32.dll in win32 folder.
dfm file:
object tidSMTP: TIdSMTP
IOHandler = tidSMTP_SSL
SASLMechanisms = <>
UseTLS = utUseExplicitTLS
end
object tidSMTP_SSL: TIdSSLIOHandlerSocketOpenSSL
Destination = 'smtp.gmail.com:587'
Host = 'smtp.gmail.com'
MaxLineAction = maException
Port = 587
DefaultPort = 0
SSLOptions.Mode = sslmUnassigned
SSLOptions.VerifyMode = []
SSLOptions.VerifyDepth = 0
end
and Send button click event:
procedure TForm1.btnSendClick(Sender: TObject);
var
mes:TIdMessage;
fromAddress:TIdEmailAddressItem;
toAddress:TIdEMailAddressItem;
begin
tidSMTP.Username := txtUsername.Text;
tidSMTP.Password := txtPassword.Text;
tidSMTP.Host := txtSMTPserver.Text; //smtp.gmail.com
tidSMTP.Port := StrToInt(txtSMTPport.Text); //587
fromAddress := TIdEMailAddressItem.Create;
fromAddress.Address := txtUsername.Text;
toAddress := TIdEMailAddressItem.Create;
toAddress.Address := txtTo.Text;
mes := TIdMessage.Create;
mes.ContentType := 'text/plain';
mes.From := fromAddress;
mes.ReceiptRecipient := toAddress;
mes.Subject := txtSubject.Text;
mes.Body := memoText.Lines;
tidSMTP.Connect;
tidSMTP.Send(mes);
tidSMTP.Disconnect;
end;
Any help would be appreciated!
Set you SSL Method to SSL version 3 (tidSMTP_SSL.SSLOptions.Method). I think it defaults to SSL version 2, but GMail does not support that.
SSLOptions.Method := sslvSSLv3;
Edit:
You can log the SSL Status info by assigning an eventhandler to the OnStatusInfo event of your IOHandler:
tidSMTP_SSL.OnStatusInfo := DoOnStatusInfo;
proceudre TForm1.DoOnStatusInfo(Msg: string);
begin
// when running from IDE, message will appear in
// EventLog (Ctrl+Alt+V), otherwise,
// use DebugViewer.exe
OutputDebugString(PChar(Msg));
end;
Maybe this will give you a clue about the failing negotation.
PS: I'm on Indy 9.0.0.18, so things may have changed for you.
Edit2:
If above does not help, please check if there is not a firewall / antivirus that is blocking smtp.gmail.com or port 587
I successfully make it worked like this:
procedure TForm1.btn2Click(Sender: TObject);
var
email : TIdMessage;
idSMTPGMail: TIdSMTP;
idSSLGMail : TIdSSLIOHandlerSocketOpenSSL;
begin
idSSLGMail := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
idSSLGMail.SSLOptions.Method := sslvTLSv1;
idSSLGMail.SSLOptions.Mode := sslmUnassigned;
idSMTPGMail := TIdSMTP.Create(nil);
idSMTPGMail.IOHandler := idSSLGMail;
idSMTPGMail.UseTLS := utUseExplicitTLS;
email := TIdMessage.Create(nil);
email.From.Address := txtUsername.Text;
email.Recipients.EMailAddresses := txtTo.Text;
email.Subject := txtSubject.Text;
email.Body.Text := memoText.Text;
idSMTPGMail.Host := 'smtp.gmail.com';
idSMTPGMail.Port := 587;
idSMTPGMail.UserName := txtUsername.Text;
idSMTPGMail.Password := txtPassword.Text;
idSMTPGMail.Connect;
idSMTPGMail.Send(email);
idSMTPGMail.Disconnect;
email.Free;
idSSLGMail.Free;
idSMTPGMail.Free;
Beep;
end;
I use the same TEdit, TMemo, but dynamically create the Indy components...

HTTPS post - what I'm doing wrong?

I'm making requests to the webaddress to get XML files throught the HTTPS connection. But this connection works like 50%. In most cases it fails. Usual error is "socket error #10060". Or "Error connecting with SSL. EOF was observed that violates the protocol". What I'm doing wrong?
function SendRequest(parameters: string): IXMLDocument;
var
sPostData: TStringList;
sHttpSocket: TIdHTTP;
sshSocketHandler: TIdSSLIOHandlerSocketOpenSSL;
resStream: TStringStream;
xDoc: IXMLDocument;
begin
sPostData := TStringList.Create;
try
sPostData.Add('add some parameter to post' + '&');
sPostData.Add('add some parameter to post' + '&');
sPostData.Add('add some parameter to post' + '&');
sPostData.Add(parameters);
sHttpSocket := TIdHTTP.Create;
sshSocketHandler := TIdSSLIOHandlerSocketOpenSSL.Create;
sHttpSocket.IOHandler := sshSocketHandler;
sHttpSocket.Request.ContentType := 'application/x-www-form-urlencoded';
sHttpSocket.Request.Method := 'POST';
resStream := TStringStream.Create;
sHttpSocket.Post(Self.sUrl, sPostData, resStream);
xDoc := CreateXMLDoc;
xDoc.LoadFromStream(resStream);
Result := xDoc;
resStream.Free;
sHttpSocket.Free;
sshSocketHandler.Free;
sPostData.Free;
except on E: Exception do
begin
TCommon.ErrorLog('errorLog.txt', DateTimeToStr(Now) + ' ' + E.Message);
end
end;
end;
Maybe I can do this in another way, that works like 100%, when internet connection is available?
Regards,
evilone
An "EOF" error suggests you are connnecting to a server that is not actually using SSL to begin with, or the SSL data may be corrupted.
Besides that, why are you including explicit '&' characters between your post data parameters? Don't do that, Indy will just encode them and send its own '&' characters. Also, consider using TMemoryStream instead of TStringStream to ensure IXMLDocumect.LoadFromStream() is loading the server's original raw XML data as-is, and not an altered version that the RTL/VCL produces due to Unicode handling (TStringStream is TEncoding-enabled).
Edit: Given the URL you provided, an example of calling verifyUser() would look like this:
const
ERPLYAccountCode = '...';
function verifyUser(const user, pass: string; const sessionLength: Integer = 3600): IXMLDocument;
var
sPostData: TStringList;
sHttpSocket: TIdHTTP;
sshSocketHandler: TIdSSLIOHandlerSocketOpenSSL;
resStream: TMemoryStream;
xDoc: IXMLDocument;
begin
Result := nil;
try
resStream := TMemoryStream.Create;
try
sPostData := TStringList.Create;
try
sPostData.Add('clientCode=' + ERPLYAccountCode);
sPostData.Add('request=verifyUser');
sPostData.Add('version=1.0');
sPostData.Add('responseType=XML');
sPostData.Add('responseMode=normal');
sPostData.Add('username=' + user);
sPostData.Add('password=' + pass);
sPostData.Add('sessionLength=' + IntToStr(sessionLength));
sHttpSocket := TIdHTTP.Create;
try
sshSocketHandler := TIdSSLIOHandlerSocketOpenSSL.Create(sHttpSocket);
sHttpSocket.IOHandler := sshSocketHandler;
sHttpSocket.Request.ContentType := 'application/x-www-form-urlencoded';
sHttpSocket.Post('https://www.erply.net/api/', sPostData, resStream);
finally
sHttpSocket.Free;
end;
finally
sPostData.Free;
end;
resStream.Position := 0;
xDoc := CreateXMLDoc;
xDoc.LoadFromStream(resStream);
Result := xDoc;
finally
resStream.Free;
end;
except
on E: Exception do
begin
TCommon.ErrorLog('errorLog.txt', DateTimeToStr(Now) + ' ' + E.Message);
end;
end;
end;

Resources