I'm using Delphi XE 6 and TIdHttp component(Indy 10.6.0.5122) and trying to consume a SOAP service - http://www.webservicex.net/globalweather.asmx over a http proxy (CCProxy - http://www.youngzsoft.net/ccproxy/). The issue is that at the first attempt to connect to the webservice I receive an "Unauthorized" respone:
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head><body><h1>Unauthorized ...</h1>
<h2>IP Address: xxx.xxx.xxx.:61295<br>
MAC Address: <br>
Server Time: 2014-11-18 14:19:00<br>
Auth Result: </h2></body></html>
I've linked an IdSSLIOHandlerSocketOpenSSL and IdLogDebug1 components to IdHttp in order to debug the issue.
Logs of the operations performed
***********************IdSSLIOHandlerSocketOpenSSL1Status
Connecting to xxx.xxx.xxx.xxx.
***********************IdLogDebug1Send
POST http://www.webservicex.net/globalweather.asmx HTTP/1.1
Content-Type: text/xml; charset=utf-8
Content-Length: 388
SOAPAction: http://www.webserviceX.NET/GetCitiesByCountry
Host: www.webservicex.net
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: identity
User-Agent: Mozilla/3.0 (compatible; Indy Library)
***********************IdLogDebug1Send
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetCitiesByCountry xmlns="http://www.webserviceX.NET">
<CountryName>Romania</CountryName>
</GetCitiesByCountry>
</soap:Body>
</soap:Envelope>
***********************IdLogDebug1Receive
HTTP/1.0 407 Unauthorized
Server: Proxy
Proxy-Authenticate: Basic realm="CCProxy Authorization"
Cache-control: no-cache
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head><body><h1>Unauthorized ...</h1>
<h2>IP Address: xxx.xxx.xxx.xxx:61295<br>
MAC Address: <br>
Server Time: 2014-11-18 14:19:00<br>
Auth Result: </h2></body></html>
***********************IdSSLIOHandlerSocketOpenSSL1Status
Disconnected.
Now, what is interesting is that if I'm trying again to call the webservice everything works correctly. Log of the operations:
***********************IdSSLIOHandlerSocketOpenSSL1Status
Connecting to xxx.xxx.xxx.xxx.
***********************IdLogDebug1Send
POST http://www.webservicex.net/globalweather.asmx HTTP/1.1
Content-Type: text/xml; charset=utf-8
Content-Length: 388
SOAPAction: http://www.webserviceX.NET/GetCitiesByCountry
Host: www.webservicex.net
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: identity
User-Agent: Mozilla/3.0 (compatible; Indy Library)
Proxy-Authorization: Basic YW1ibzphbWJvIQ==
***********************IdLogDebug1Send
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetCitiesByCountry xmlns="http://www.webserviceX.NET">
<CountryName>Romania</CountryName>
</GetCitiesByCountry>
</soap:Body>
</soap:Envelope>
***********************IdLogDebug1Receive
HTTP/1.1 200 OK
Cache-Control: private, max-age=0
Content-Length: 2456
***********************IdLogDebug1Receive
Content-Type: text/xml; charset=utf-8
Server: Microsoft-IIS/7.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Tue, 18 Nov 2014 12:26:21 GMT
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><GetCitiesByCountryResponse xmlns="http://www.webserviceX.NET"><GetCitiesByCountryResult><NewDataSet>
<Table>
<Country>Romania</Country>
<City>Arad</City>
</Table>
<Table>
<Country>Romania</Country>
<City>Bacau</City>
</Table>
......
And the response it's correct.
Code of the application
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, IdBaseComponent,
IdComponent, IdTCPConnection, IdTCPClient, IdHTTP, Soap.SOAPHTTPTrans,
IdAuthentication, IdHeaderList, IdIntercept, IdLogBase, IdLogDebug,
IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack, IdSSL, IdSSLOpenSSL
,IdGlobal;
type
TForm1 = class(TForm)
IdHTTP1: TIdHTTP;
Button1: TButton;
HTTPReqResp1: THTTPReqResp;
Memo1: TMemo;
Button2: TButton;
Button3: TButton;
Button4: TButton;
IdSSLIOHandlerSocketOpenSSL1: TIdSSLIOHandlerSocketOpenSSL;
IdLogDebug1: TIdLogDebug;
Memo2: TMemo;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure IdHTTP1ProxyAuthorization(Sender: TObject;
Authentication: TIdAuthentication; var Handled: Boolean);
procedure IdSSLIOHandlerSocketOpenSSL1StatusInfo(const AMsg: string);
procedure IdSSLIOHandlerSocketOpenSSL1Status(ASender: TObject;
const AStatus: TIdStatus; const AStatusText: string);
procedure IdLogDebug1Receive(ASender: TIdConnectionIntercept;
var ABuffer: TIdBytes);
procedure IdLogDebug1Send(ASender: TIdConnectionIntercept;
var ABuffer: TIdBytes);
procedure IdHTTP1Authorization(Sender: TObject;
Authentication: TIdAuthentication; var Handled: Boolean);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
postData: TMemoryStream;
begin
postData := TMemoryStream.Create;
try
Memo1.Lines.Clear;
postData.LoadFromFile('..\..\soap1.1.txt');
IdHTTP1.Request.ContentType := 'text/xml';
IdHTTP1.Request.Charset := 'utf-8';
IdHTTP1.Request.CustomHeaders.Values['SOAPAction'] := 'http://www.webserviceX.NET/GetCitiesByCountry';
IdHTTP1.ProtocolVersion := pv1_1;
IdHTTP1.HTTPOptions := IdHTTP1.HTTPOptions + [hoKeepOrigProtocol];
Memo1.Lines.Text := IdHTTP1.Post('http://www.webservicex.net/globalweather.asmx', postData);
finally
postData.Free;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
with IdHTTP1.ProxyParams do
begin
ProxyServer := 'xxx.xxx.xxx.xxx';
ProxyPort := 808;
ProxyUsername := 'User-001';
ProxyPassword := 'User-001!';
end;
end;
procedure TForm1.IdHTTP1Authorization(Sender: TObject;
Authentication: TIdAuthentication; var Handled: Boolean);
begin
//
Authentication.Username := 'User-001';
Authentication.Password := 'User-001!';
end;
procedure TForm1.IdHTTP1ProxyAuthorization(Sender: TObject;
Authentication: TIdAuthentication; var Handled: Boolean);
begin
//
// Authentication.Username := 'User-001';
// Authentication.Password := 'User-001!';
// Handled := true;
end;
procedure TForm1.IdLogDebug1Receive(ASender: TIdConnectionIntercept;
var ABuffer: TIdBytes);
begin
Memo2.Lines.Add(#13#10+'***********************IdLogDebug1Receive '+#13#10+BytesToString(ABuffer))
end;
procedure TForm1.IdLogDebug1Send(ASender: TIdConnectionIntercept;
var ABuffer: TIdBytes);
begin
Memo2.Lines.Add(#13#10+'***********************IdLogDebug1Send '+#13#10+BytesToString(ABuffer))
end;
procedure TForm1.IdSSLIOHandlerSocketOpenSSL1Status(ASender: TObject;
const AStatus: TIdStatus; const AStatusText: string);
begin
Memo2.Lines.Add(#13#10+'***********************IdSSLIOHandlerSocketOpenSSL1Status '+#13#10+AStatusText)
end;
procedure TForm1.IdSSLIOHandlerSocketOpenSSL1StatusInfo(const AMsg: string);
begin
Memo2.Lines.Add(#13#10+'***********************IdSSLIOHandlerSocketOpenSSL1StatusInfo '+#13#10+AMsg)
end;
end.
How should I make the authentication in order to work from the first attempt?
PS : I've already read this question - Authorization failure TIdHTTP over HTTPS.
ANSWER : based on the indications of Remy Lebeau problem was solved by setting up the
OnProxySelectAuthorization
event and add the hoInProcessAuth to the
IdHTTP1.HTTPOptions
Make sure you have added the
IdAuthenticationunit to yourusesclause soTIdHTTPcan processProxy-Authorizationheaders in a 407 reply, and also make sure you have aTIdHTTP.OnProxyAuthorizationevent handler assigned (even if it just returnsHandled:=True) otherwiseTIdHTTPwill not attempt proxy authorization while processing a 407 reply, even though you have provided a username/password in theTIdHTTP.ProxyParamsproperty.What is most likely happening is that the
TIdHTTP.ProxyParams.Authenticationproperty is initially nil during the first request, and gets filled in with aTIdBasicAuthenticationobject while processing the 407 reply, but a missingOnProxyAuthorizationevent handler causesTIdHTTPto skip authorization, and then theTIdHTTP.ProxyParams.Authenticationproperty is not nil anymore when the second request is made, so it attempts authorization at that time.Skipping proxy authorization if the
TIdHTTP.OnProxyAuthorizationevent is not assigned appears to be a bug, IMHO. By comparison, theTIdHTTP.OnAuthorizationevent can be unassigned as long as a non-empty value is assigned to theTIdHTTP.Request.Passwordproperty. I have now updatedTIdHTTPwith similar logic for theOnProxyAuthorizationevent regarding theTIdHTTP.ProxyParams.Passwordproperty.So, either update to the latest SVN snapshot, or just assign a
TIdHTTP.OnProxyAuthorizationevent handler, then you should be OK: