Delphi - 授权TIdHttp通过HTTP代理进行故障转移

时间:2014-11-18 12:31:08

标签: web-services delphi soap indy10 delphi-xe6

我正在使用Delphi XE 6和TIdHttp组件(Indy 10.6.0.5122)并试图通过http代理(CCProxy - http://www.webservicex.net/globalweather.asmx)使用SOAP服务 - http://www.youngzsoft.net/ccproxy/。问题是,在第一次尝试连接到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>

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

执行的操作日志

***********************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服务,一切正常。记录操作:

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

我应该如何进行身份验证才能在第一次尝试时使用?

PS:我已经读过这个问题了 - Authorization failure TIdHTTP over HTTPS

答案:根据Remy Lebeau的指示,问题通过设置来解决

  

OnProxySelectAuthorization

事件并将 hoInProcessAuth 添加到

  

IdHTTP1.HTTPOptions

1 个答案:

答案 0 :(得分:2)

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

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

如果未分配TIdHTTP.ProxyParams.Authentication事件,则跳过代理授权似乎是一个错误,恕我直言。相比之下,只要为TIdHTTP.OnProxyAuthorization属性分配了非空值,就可以取消分配TIdHTTP.OnAuthorization事件。我现在更新了TIdHTTP.Request.Password,其TIdHTTP属性的OnProxyAuthorization事件的逻辑类似。

因此,要么更新到最新的SVN快照,要么只是分配一个TIdHTTP.ProxyParams.Password事件处理程序,那么你应该没问题:

TIdHTTP.OnProxyAuthorization