[英]Delphi - Authorization TIdHttp fails over HTTP proxy
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/ ). 我正在使用Delphi XE 6和TIdHttp组件(Indy 10.6.0.5122),并尝试通过HTTP代理(CCProxy- http:// www。 )使用SOAP服务-http: //www.webservicex.net/globalweather.asmx 。 youngzsoft.net/ccproxy/ )。 The issue is that at the first attempt to connect to the webservice I receive an "Unauthorized" respone: 问题是,在第一次尝试连接到Web服务时,我收到了“未经授权”的响应:
<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. 我已将IdSSLIOHandlerSocketOpenSSL和IdLogDebug1组件链接到IdHttp以便调试问题。
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. 现在,有趣的是,如果我再次尝试调用Web服务,则一切正常。 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 . PS:我已经读过这个问题- 授权失败TIdHTTP over HTTPS 。
ANSWER : based on the indications of Remy Lebeau problem was solved by setting up the 解答:根据Remy Lebeau的指示,通过设置
OnProxySelectAuthorization OnProxySelectAuthorization
event and add the hoInProcessAuth to the 事件,并将hoInProcessAuth添加到
IdHTTP1.HTTPOptions IdHTTP1.HTTPOptions
Make sure you have added the IdAuthentication
unit to your uses
clause so TIdHTTP
can process Proxy-Authorization
headers in a 407 reply, and also make sure you have a TIdHTTP.OnProxyAuthorization
event handler assigned (even if it just returns Handled:=True
) otherwise TIdHTTP
will not attempt proxy authorization while processing a 407 reply, even though you have provided a username/password in the TIdHTTP.ProxyParams
property. 请确保您已添加的IdAuthentication
单元到您的uses
子句,以便TIdHTTP
可以处理Proxy-Authorization
在407回复头,并且确保你有一个TIdHTTP.OnProxyAuthorization
分配(哪怕它只是返回的事件处理程序Handled:=True
,否则) TIdHTTP
不会尝试代理授权,同时处理407回复,即使你已经在提供的用户名/密码TIdHTTP.ProxyParams
财产。
What is most likely happening is that the TIdHTTP.ProxyParams.Authentication
property is initially nil during the first request, and gets filled in with a TIdBasicAuthentication
object while processing the 407 reply, but a missing OnProxyAuthorization
event handler causes TIdHTTP
to skip authorization, and then the TIdHTTP.ProxyParams.Authentication
property is not nil anymore when the second request is made, so it attempts authorization at that time. 最有可能发生的情况是, TIdHTTP.ProxyParams.Authentication
属性最初在第一个请求期间为nil,并且在处理407答复时被TIdBasicAuthentication
对象填充,但是缺少的OnProxyAuthorization
事件处理程序导致TIdHTTP
跳过授权,然后当发出第二个请求时, TIdHTTP.ProxyParams.Authentication
属性不再为nil,因此它会在那时尝试授权。
Skipping proxy authorization if the TIdHTTP.OnProxyAuthorization
event is not assigned appears to be a bug, IMHO. 如果未分配TIdHTTP.OnProxyAuthorization
事件,则跳过代理授权似乎是一个错误,恕我直言。 By comparison, the TIdHTTP.OnAuthorization
event can be unassigned as long as a non-empty value is assigned to the TIdHTTP.Request.Password
property. 相比之下,只要将非空值分配给TIdHTTP.Request.Password
属性,就可以取消分配TIdHTTP.OnAuthorization
事件。 I have now updated TIdHTTP
with similar logic for the OnProxyAuthorization
event regarding the TIdHTTP.ProxyParams.Password
property. 现在我已经更新TIdHTTP
用的类似的逻辑OnProxyAuthorization
关于该事件TIdHTTP.ProxyParams.Password
财产。
So, either update to the latest SVN snapshot, or just assign a TIdHTTP.OnProxyAuthorization
event handler, then you should be OK: 因此,要么更新到最新的SVN快照,要么只是分配一个TIdHTTP.OnProxyAuthorization
事件处理程序,那么您应该可以:
procedure TForm1.IdHTTP1ProxyAuthorization(Sender: TObject; Authentication: TIdAuthentication; var Handled: Boolean);
begin
// prompt the user for username/password (optional) and store them
// in the Authentication.Username and Authentication.Password
// properties, respectively. By default, they are initialized with
// the current values of the ProxyParams.UserName and
// ProxyParams.Password properties...
Handled := True;
end;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.