Can anyone explain to me how to handle errors with TIdFTP?
For example, I try to connect as follows ...
with IdFTP do
begin
Username := xxxxx;
Password := '1000';
Host := xxx.xxx.xxx.xxx;
Port := 21;
ConnectTimeOut := 5000;
try
Connect;
except
On E: Exception do
ShowMessage(E.Message);
end;
end;
If I use the wrong password (as I have deliberately done in the above example to simulate what might happen if a user provides the wrong password), I get the error "Bad sequence of commands". That tells me nothing about the real problem. Is there any way to get a meaningful description of the actual error, ie: that the password is incorrect?
Related
I'm not sure if this is the best way to check if a TIdHTTP exception is raised, here is what I did:
HTTP := TIDHttp.Create(nil);
try
try
HTTP.Head(URL);
SizeF := HTTP.Response.ContentLength;
code := HTTP.ResponseCode;
except
on E: Exception do
begin
code := HTTP.ResponseCode;
ShowMessage(IntToStr(code));
end;
end;
finally
HTTP.Free;
end;
if code = 200 then
// go download the file using multiple threads.
What I want to achieve is raising an axception is case there is one (I guess I already did) otherwise the program keeps running and download the file.
So is this the correct way to do it? Thanks for your replies.
As a common rule: Only handle exceptions when you can and want to handle them.
Otherwise let them just flow to stop the current execution of your code. Without any interception you will just receive a dialog with the exception message (this is handled by TApplication).
In this case you can change your code to
HTTP := TIDHttp.Create(nil);
try
HTTP.Head(URL);
// if an exception is raised then the rest of the code will not be executed
// yes, the code in finally part will execute
SizeF := HTTP.Response.ContentLength;
code := HTTP.ResponseCode;
finally
HTTP.Free;
end;
// check if all conditions are met
if code <> 200 then
// if not, raise a custom exception as you like
raise Exception.Create( 'Not what I expected here' );
// go download the file using multiple threads.
I have a TIdPop3Server in one application that has a IdServerIOHandlerSSLOpenSSL1 attached to it and retrieves emails and sends them to a TIdPop3 client in another application (having TIdSSLIOHandlerSocketOpenSSL attached to it). Everything's fine when the connections are made insecure using port 110. But when I try to use SSL connection through port 995 I get error Connection Closed Gracefully after connect attemp from the client fails. This is my Pop3SeverOnConnect event :
procedure TMainForm.Pop3ServerConnect(AContext: TIdContext);
begin
if (AContext.Connection.IOHandler is TIdSSLIOHandlerSocketBase) then
TIdSSLIOHandlerSocketBase(AContext.Connection.IOHandler).PassThrough :=
(AContext.Binding.Port <> 995);
showmessage('SSL connection made!');
end;
And this is the client-side :
procedure TMainForm.btnCheckMailBoxClick(Sender: TObject);
begin
IdSSLIOHandlerSocketOpenSSL1.PassThrough := False;
POP3Client.IOHandler := IdSSLIOHandlerSocketOpenSSL1;
with POP3Client do begin
AuthType := patUserPass;
Host := myHost;
UserName := myUserName;
Password := myPass;
Port := myPort;
end;
try
POP3Client.Connect;
Except on e : Exception do
showmessage('error=' + e.Message);
end;
// code for retrieving message data
end;
And I always get an exception from Pop3Client.Connect like I've already mentioned above (The message SSL connection made! in the server application never shows up). If I use however another mail client like for example Mozilla Thunderbird I achieve a successful SSL connection for port 995. So the problem should be somewhere in the client's procedure but who knows - that's why I'm asking you guys for help.
In your client code, you need to set the TIdPOP3.UseTLS property instead of the TIdSSLIOHandlerSocketOpenSSL.PassThrough property directly, eg:
procedure TMainForm.btnCheckMailBoxClick(Sender: TObject);
begin
with POP3Client do
begin
IOHandler := IdSSLIOHandlerSocketOpenSSL1;
AuthType := patUserPass;
UseTLS := utUseImplicitTLS; // <-- here
Host := myHost;
UserName := myUserName;
Password := myPass;
Port := myPort;
end;
try
POP3Client.Connect;
try
// code for retrieving message data
finally
POP3Client.Disconnect;
end;
except
on e : Exception do
ShowMessage('error=' + e.Message);
end;
end;
In your server code, you need to get rid of the ShowMessage(). TIdPOP3Server is multi-threaded, the OnConnect event is fired in the context of a worker thread, and ShowMessage() is not thread-safe. If you must display a popup message, use Windows.MessageBox() instead.
I am using indy TIDHTTP to code a way to know whether my server on the internet is down or the address of the page on the same server is not available.
I copied the suggestion given in another thread on stackoverflow:
try
IdHTTP1.Get(mypage_address);
except
on E: EIdHTTPProtocolException do begin
if e.errorcode=404 then
showmessage('404 File not found');
// use E.ErrorCode, E.Message, and E.ErrorMessage as needed...
end;
end;
but this way I am only able to detect a server response code and not whether the server did not respond at all. I guess it's trivial but I do not know what is the way to do that?
An EIdHTTPProtocolException exception is raised when TIdHTTP successfully sends a request to the server and it sends an HTTP error reply back to TIdHTTP. If the server cannot be reached at all, a different exception (typically EIdSocketError, EIdConnectException, or EIdConnectTimeout) will be raised instead.
try
IdHTTP1.Head(mypage_address);
except
on E: EIdHTTPProtocolException do begin
ShowMessage(Format('HTTP Error: %d %s', [E.ErrorCode, E.Message]));
end;
on E: EIdConnectTimeout do begin
ShowMessage('Timeout trying to connect');
end;
on E: EIdSocketError do begin
ShowMessage(Format('Socket Error: %d %s', [E.LastError, E.Message]));
end;
on E: Exception do begin
ShowMessage(Format('Error: [%s] %s', [E.ClassName, E.Message]));
end;
end;
I attempted doing the server/site checking scientifically. but eventually simply came down to this:
function TFrameSiteChecker.GetSiteHeader(const AUrl: string): Integer;
begin
try
idhttp1.Head(AUrl);
Result := idhttp1.ResponseCode;
except
on E: exception do
Result := 0;
end;
end;
Logic being, getting the head reduces traffic, log sizes etc.
There is one one right result from the function - the return of status code 200, anything else is a fail.
Also I failed to force windows / the system / indy to not buffer/cache content, so also eventually, just run the checker every 30 minutes on a schedule. Otherwise (unless something else clears the cache) after the first connect it always succeeds, even if you unplug the machine from the network!
I am trying to create a little tool that will monitor how much bandwidth I have used and how much I have remaining from my ISP. They have a SOAP service which I must authenticate my UserName and Password and validate the client then I will get 2 GUIDS that must be passed into the function that will return the usage statistics from an array. These are then cached for 1 hour and I must authenticate again.
I have used the WSDL Importer from within Delphi 2010 and it has generated me a Unit. (I am not really sure that I should post this as it is quite large)?
I am trying to authenticate the first part with my Username and Password with the code below:
procedure TForm1.btnAuthClick(Sender: TObject);
var
fGUID: string;
begin
HTTPRIO1.URL := 'https://webservices.zen.co.uk/broadband/v3.11/serviceengine.asmx?WSDL';
try
fGUID := (HTTPRIO1 as ServiceEngineSoap).Authenticate(edtUserName.Text,edtPassword.Text);
Label1.Caption := fGUID;
except
on E: Exception do
Memo1.Lines.Text := E.Message;
end;
end;
The above code always returns the Error below:
Header http://schemas.xmlsoap.org/ws/2004/08/addressing:Action for ultimate recipient is required but not present in the message.
I have tried using the WSDLLocation instead with the Service and the Port:
procedure TForm1.btnAuthClick(Sender: TObject);
var
fGUID: string;
begin
HTTPRIO1.WSDLLocation := 'https://webservices.zen.co.uk/broadband/v3.11/serviceengine.asmx?WSDL';
HTTPRIO1.Service := 'ServiceEngine';
HTTPRIO1.Port := 'ServiceEngineSoap';
try
fGUID := (HTTPRIO1 as ServiceEngineSoap).Authenticate(edtUserName.Text,edtPassword.Text);
Label1.Caption := fGUID;
except
on E: Exception do
Memo1.Lines.Text := E.Message;
end;
end;
This will always generate the Error below:
Unable to retrieve the URL endpoint for Service/Port 'ServiceEngine'/'ServiceEngineSoap' from WSDL 'https://webservices.zen.co.uk/broadband/v3.11/serviceengine.asmx?WSDL'
What am I doing wrong here? If I should actually send a Header then how would I do this to authenticate myself to the service?
According to the docs
if the server requires authentication, use the properties of the
THTTPReqResp object to provide the necessary information
It's in the THTTPReqResp object that you set the user/pw (HTTPRIO1.HTTPWebNode.UserName := 'xxx'; HTTPRIO1.HTTPWebNode.Password := 'yyy')
Also note from the docs on using HTTPS:
THTTPReqResp uses WinInet to establish a connection to the server.
Wininet.dll must be installed on the client system. wininet.dll is
found in the Windows system directory if you have IE3 or higher
installed. WinInet has the advantage that it provides support for
secure connections (https). To use WinInet, compile your project
without the USE_INDY symbol defined
However, it looks like this user/pw may only be used in a Proxy situation according to the post below. Follow Jean-Marie Babet's link to the original workaround given from 2007 if they still haven't fixed this:
https://forums.codegear.com/thread.jspa?threadID=58755&tstart=0
Here's the workaround:
All you have to do is handle the OnBeforePost event on the
HTTPRIO.HTTPWebNode component and use InternetSetOption. Here's an
example from a sample that talks to MapPoint (MapPoint.NET uses
'digest' authentication - a variant of 'basic' authentication):
procedure TTestMapPointRender.HTTPRIO1HTTPWebNode1BeforePost(
const HTTPReqResp: THTTPReqResp; Data: Pointer);
var
UserName: string;
PassWord: string;
begin
UserName := GetWSToken('MapPoint', 'UserName');
Password := GetWSToken('MapPoint', 'Password');
if not InternetSetOption(Data,
INTERNET_OPTION_USERNAME,
PChar(UserName),
Length(UserName)) then
raiseException(SysErrorMessage(GetLastError));
if not InternetSetOption(Data,
INTERNET_OPTION_PASSWORD,
PChar(Password),
Length (Password)) then
raiseException(SysErrorMessage(GetLastError));
end;
I am having problem with IdTCPclient.connected function from Indy in Delphi. I am using Indy10 and Delphi2010 environment. My problem is every time i check the TCP connection with IdTCPclient.connected, it raises exception with these errors EidSocketError, EidReadTimeOut. Is there any way to disconnect and reconnect the connection? (like reset the connection).
Note: I set TCPClient.ReTimeout:= 30000;
The implemented coding for reset the connection is follow.
if IdTCPclient.connected then
begin
IdTCPclient.IOHandler.InputBuffer.Clear;
IdTCPclient.Disconnect;
end;
sleep(1000);
try
IdTCPclient.connect;
except
on E: Exception do
MessageDlg('Connecting Error: '+E.Message, mtError, [mbOk], 0);
end;
But some point, i get exception and it couldn't connect at all. I am not sure what i am doing wrong.
Should i do this?
Disconnect first
Clear input buffer
Destroy TCPclient
Re-create new TCPclient
And then connect it again
If it is the case, can someone provide me a way how to do it properly?
Also, there is another function to re-connecting the TCP in my coding. It also gives me exception as well. I check the connecting before i send a message to TCP. If there is no connection, i reconnect for five times.
result := IdTCPclient.connected
if not result then
begin
for k:=0 to 4 do
beign
sleep(1000);
try
TCPclient.connect;
except
on E: Exception do
MessageDlg('Connecting Error: '+E.Message, mtError, [mbOk], 0);
end
result := TCPclient.connected;
if result then break;
end;
With above two coding, program handles reconnecting and reset the connection pretty well. But some point the program cannot re-connect or reset the connection at all.
What should i do when i get exception? Should i reconnect from exception?
How do we build coding to check the connection regularly?
How do we build coding to to get back the connection when it lost?
Kind regards,
Connected() should not be raising any exceptions at all. If it is, then it is likely a bug. Please provide a stack trace showing that.
The best option is to simply avoid using Connected() whenever possible. When you need to perform an I/O operation, just do so, and let Indy raise an exception if a failure occurs. You can then handle it at that moment, eg:
try
IdTCPClient.DoSomething...
except
on E: EIdException do begin
Reconnect;
end;
end;
procedure Reconnect;
var
k: Integer;
begin
IdTCPClient.Disconnect;
if IdTCPClient.IOHandler <> nil then
IdTCPClient.IOHandler.InputBuffer.Clear;
for k := 0 to 4 do
begin
Sleep(1000);
try
IdTCPClient.Connect;
Exit;
except
on E: Exception do
begin
MessageDlg('Connecting Error: '+E.Message, mtError, [mbOk], 0);
if k = 4 then
raise;
end;
end;
end;
end;
before you connect make sure that the passive Boolean of idftp is false
when you need file transfert change it to true with binary file option