Delphi-授权TIdHttp对HTTP代理进行故障转移 [英] Delphi - Authorization TIdHttp fails over HTTP proxy

查看:154
本文介绍了Delphi-授权TIdHttp对HTTP代理进行故障转移的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Delphi XE 6和TIdHttp组件(Indy 10.6.0.5122)并尝试使用SOAP服务-通过http代理访问的http://www.webservicex.net/globalweather.asmx (CCProxy- http://www.youngzsoft.net/ccproxy/ )。问题是,在第一次尝试连接到Web服务时,我收到未经授权的响应:

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>

我已将IdSSLIOHandlerSocketOpenSSL和IdLogDebug1组件链接到IdHttp以便调试问题。

I've linked an IdSSLIOHandlerSocketOpenSSL and IdLogDebug1 components to IdHttp in order to debug the issue.

已执行操作的日志

***********************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.

现在,有趣的是,如果我再次尝试调用Web服务,则一切正常。操作日志:

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>&lt;NewDataSet&gt;
  &lt;Table&gt;
    &lt;Country&gt;Romania&lt;/Country&gt;
    &lt;City&gt;Arad&lt;/City&gt;
  &lt;/Table&gt;
  &lt;Table&gt;
    &lt;Country&gt;Romania&lt;/Country&gt;
    &lt;City&gt;Bacau&lt;/City&gt;
  &lt;/Table&gt;
  ......

答案是正确的。

应用代码

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:我已经阅读了此问题-授权失败TIdHTTP通过HTTPS

PS : I've already read this question - Authorization failure TIdHTTP over HTTPS.

ANSWER:基于Remy Lebeau的迹象,通过设置

ANSWER : based on the indications of Remy Lebeau problem was solved by setting up the


解决了问题

OnProxySelectAuthorization

OnProxySelectAuthorization

事件并将 hoInProcessAuth 添加到


IdHTTP1.HTTPOptions

IdHTTP1.HTTPOptions


推荐答案

制作确保已将 IdAuthentication 单元添加到 uses 子句中,以便 TIdHTTP 可以在407回复中处理 Proxy-Authorization 标头,并确保您有 TIdHTTP.OnProxyAuthorization 事件分配的处理程序(即使它仅返回 Handled:= True ),否则 TIdHTTP 在处理407时不会尝试代理授权即使您在 TIdHTTP.ProxyParams 属性中提供了用户名/密码,也可以进行回复。

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.

最有可能的是发生的是 TIdHTTP.Pr oxyParams.Authentication 属性最初在第一个请求期间为零,并且在处理407答复时被填充 TIdBasicAuthentication 对象,但缺少 OnProxyAuthorization 事件处理程序会导致 TIdHTTP 跳过授权,然后跳过 TIdHTTP.ProxyParams.Authentication 属性在再次发出第二个请求时不再为零,因此它会尝试进行授权。

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.OnProxyAuthorization 事件似乎是一个错误,恕我直言。相比之下, TIdHTTP.OnAuthorization 事件可以取消分配,只要将非空值分配给 TIdHTTP.Request.Password 属性。现在,对于有关 TIdHTTP.ProxyParams的 OnProxyAuthorization 事件,我用类似的逻辑更新了 TIdHTTP 。密码属性。

Skipping proxy authorization if the TIdHTTP.OnProxyAuthorization event is not assigned appears to be a bug, IMHO. By comparison, the TIdHTTP.OnAuthorization event can be unassigned as long as a non-empty value is assigned to the TIdHTTP.Request.Password property. I have now updated TIdHTTP with similar logic for the OnProxyAuthorization event regarding the TIdHTTP.ProxyParams.Password property.

因此,要么更新到最新的SVN快照,要么只是分配一个 TIdHTTP.OnProxyAuthorization 事件处理程序,那么您应该可以:

So, either update to the latest SVN snapshot, or just assign a TIdHTTP.OnProxyAuthorization event handler, then you should be OK:

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;

这篇关于Delphi-授权TIdHttp对HTTP代理进行故障转移的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆