Advertisement
Guest User

Patch from Bruneau Babet

a guest
Aug 20th, 2014
1,924
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Delphi 52.92 KB | None | 0 0
  1. Here is the code saved from a now deleted forum post.
  2.  
  3. > Hello,
  4. >
  5. > Below is a patched version of the *2010* version of SOAPHTTPTrans.pas
  6. > to  resolve the problem described in this thread. As with the C++
  7. > patch, I'll  illustrate the problem. You can see the problem in a
  8. > simple Button Click  handler. For example:
  9.  
  10.  
  11.  
  12.    const
  13.      Request =
  14.      '<?xml version="1.0"?>' +
  15.      '<SOAP-ENV:Envelope ' +
  16.      ' xmlns:SOAP-ENV='+
  17.      '"http://schemas.xmlsoap.org/soap/envelope/">' +
  18.       ' <SOAP-ENV:Body>' +
  19.       '  <x_Person xmlns="http://soapinterop.org/xsd" ' +
  20.       '      Name="ЗАКУСКИ" ' +
  21.       '      Male="true"> ' +
  22.       '    <Age>45.5</Age>' +
  23.       '    <ID>1234.5678</ID>' +
  24.       '  </x_Person>' +
  25.       ' </SOAP-ENV:Body>' +
  26.       '</SOAP-ENV:Envelope>';
  27.    
  28.     const URL= 'http://mssoapinterop.org/asmx/wsdl/compound1.asmx';
  29.    
  30.    
  31.     procedure TForm29.InvokeEchoPersonClick(Sender: TObject);
  32.     var
  33.       RR: THTTPReqResp;
  34.       Response: TMemoryStream;
  35.       U8: UTF8String;
  36.     begin
  37.       RR := THTTPReqResp.Create(nil);
  38.       try
  39.         RR.URL := URL;
  40.         RR.UseUTF8InHeader := True;
  41.         RR.SoapAction := 'http://soapinterop/echoPerson';
  42.         Response := TMemoryStream.Create;
  43.         try
  44.           RR.Execute(Request, Response);
  45.           SetLength(U8, Response.Size);
  46.           Response.Position := 0;
  47.           Response.Read(U8[1], Length(U8));
  48.           ShowMessage(U8);
  49.         finally
  50.           Response.Free;
  51.         end;
  52.       finally
  53.         RR.Free;
  54.       end;
  55.     end;
  56.     {code}
  57.    
  58.     The above example invokes the EchoPerson service available here -
  59.     http://mssoapinterop.org/asmx/wsdl/compound1.asmx. The response comes back
  60.     nicely and is displayed in a MessageBox. Now, update the URL variable to
  61.     something invalid, such as http://mssoapintero.org/asmx/wsdl/compound1.asmx.
  62.     (NOTE: I've dropped the ending 'p' on the domain name). When you run you
  63.    should get an error message about being unable to resolve the server name.
  64.    Instead, you get an empty MessageBox. The issue is that the code failed to
  65.    probably catch the fact that the HTTP POST failed. In this case, we're
  66.     failing to connect. But let's say we have a case where we can connect but
  67.    the Server is too busy and it cannot process the posted data. The 'Send'
  68.    will fail. However, the runtime will fail to detect this and it will process
  69.    to receiving the response - at which point WinInet will say "Handle is in
  70.    the wrong state for the requested operation".
  71.    
  72.    So the core issue is the failure to detect a failed WinInet operation. The
  73.    updated SOAPHTTPTrans.pas below remedies.
  74.    
  75.    Cheers,
  76.    
  77.    Bruneau
  78.    
  79.    {code}
  80.    {*******************************************************}
  81.    {                                                       }
  82.    {            Delphi Visual Component Library            }
  83.    {         SOAP Transports                               }
  84.    {                                                       }
  85.    { Copyright(c) 1995-2010 Embarcadero Technologies, Inc. }
  86.    {                                                       }
  87.    {*******************************************************}
  88.    
  89.    unit SOAPHTTPTrans;
  90.    
  91.    {$IFDEF LINUX}
  92.      {$DEFINE USE_INDY}
  93.    {$ENDIF}
  94.    {$IFDEF MSWINDOWS}
  95.    //  {$DEFINE USE_INDY}
  96.    {$ENDIF}
  97.    
  98.    {$IFNDEF VER150}
  99.    {$INCLUDE 'CompVer.inc'}
  100.    {$ENDIF}
  101.    
  102.    {$IFDEF HIGHLANDER_UP}
  103.      {$DEFINE INDY_CUSTOM_IOHANDLER}
  104.    {$ENDIF}
  105.    
  106.    {$IFDEF TIBURON_UP}
  107.      // Default to v10 of Indy for Tiburon and up unless INDY_9 is defined
  108.      {$IFNDEF INDY_9}
  109.        {$DEFINE INDY_10}
  110.      {$ENDIF}
  111.    {$ENDIF}
  112.    
  113.    interface
  114.    
  115.    uses
  116.      SysUtils, Classes, WebNode, WSDLNode, Types, IntfInfo, WSDLIntf,
  117.    SOAPAttachIntf,
  118.    {$IFDEF USE_INDY}
  119.    IdHTTP, IdIOHandler, IdIOHandlerSocket, IdSSLOpenSSL;
  120.    {$ELSE}
  121.      WinSock, WinInet;
  122.    (*$HPPEMIT '#pragma link "wininet.lib"' *)
  123.    {$ENDIF}
  124.    type
  125.    
  126.      ESOAPHTTPException = class(Exception)
  127.      private
  128.        FStatusCode: Integer;
  129.      public
  130.    {$IF CompilerVersion <= 15.0}
  131.        constructor Create(const Msg: string; SCode: Integer = 0);
  132.    {$ELSE}
  133.        constructor Create(const Msg: string; SCode: Integer = 0; Dummy: Integer
  134.    = 0);
  135.    {$IFEND}
  136.        constructor CreateFmt(const Msg: string; const Args: array of const;
  137.    SCode: Integer = 0; Dummy: Integer = 0);
  138.    
  139.        property StatusCode: Integer read FStatusCode write FStatusCode;
  140.      end;
  141.    
  142.      SOAPInvokeOptions = (soNoValueForEmptySOAPAction,   { Send "" or
  143.    absolutely no value for empty SOAPAction }
  144.                           soIgnoreInvalidCerts,          { Handle Invalid
  145.    Server Cert and ask HTTP runtime to ignore }
  146.                           soNoSOAPActionHeader,          { Don't send
  147.     SOAPAction - use very very carefully!! }
  148.                            soAutoCheckAccessPointViaUDDI, { if we get a status
  149.     code 404/405/410 - contact UDDI }
  150.                            soPickFirstClientCertificate   { WinInet Only }
  151.                            );
  152.       TSOAPInvokeOptions= set of SOAPInvokeOptions;
  153.    
  154.       THTTPReqResp = class;
  155.    
  156.       { Provides access to HTTPReqResp component }
  157.       IHTTPReqResp = interface
  158.       ['{5FA6A197-32DE-4225-BC85-216CB80D1561}']
  159.         function GetHTTPReqResp: THTTPReqResp;
  160.       end;
  161.    
  162.       TBeforePostEvent = procedure(const HTTPReqResp: THTTPReqResp; Data:
  163.     Pointer) of object;
  164.       TPostingDataEvent= procedure(Sent: Integer; Total: Integer) of object;
  165.       TReceivingDataEvent= procedure(Read: Integer; Total: Integer) of object;
  166.       TWinInetErrorEvent = function(LastError: DWord; Request: Pointer): DWord
  167.     of object;
  168.    
  169.       THTTPReqResp = class(TComponent, IInterface, IWebNode, IHTTPReqResp)
  170.       private
  171.         FUserSetURL: Boolean;
  172.         FRefCount: Integer;
  173.         FOwnerIsComponent: Boolean;
  174.         FConnected: Boolean;
  175.         FURL: string;
  176.         FAgent: string;
  177.         FBindingType: TWebServiceBindingType;
  178.         FMimeBoundary: string;
  179.         FWebNodeOptions: WebNodeOptions;
  180.         FContentType: string;
  181.         FUserName: string;
  182.         FPassword: string;
  183.         FURLHost: string;
  184.         FURLSite: string;
  185.         FURLPort: Integer;
  186.         FURLScheme: Integer;
  187.         FProxy: string;
  188.         FProxyByPass: string;
  189.     {$IFNDEF USE_INDY}
  190.         FInetRoot: HINTERNET;
  191.         FInetConnect: HINTERNET;
  192.     {$ENDIF}
  193.         FConnectTimeout: Integer;
  194.         FSendTimeout: Integer;
  195.         FReceiveTimeout: Integer;
  196.         FWSDLView: TWSDLView;
  197.         FSoapAction: string;
  198.         FUseUTF8InHeader: Boolean;
  199.         FInvokeOptions: TSOAPInvokeOptions;
  200.         FUDDIBindingKey: WideString;
  201.         FUDDIOperator: String;
  202.         FOnBeforePost: TBeforePostEvent;
  203.         FOnPostingData: TPostingDataEvent;
  204.         FOnReceivingData: TReceivingDataEvent;
  205.         FMaxSinglePostSize: Integer;
  206.         FOnWinInetError: TWinInetErrorEvent;
  207.    
  208.     {$IFDEF USE_INDY}
  209.       {$IFDEF INDY_CUSTOM_IOHANDLER}
  210.         FIOHandler: TIdIOHandler;
  211.       {$ENDIF}
  212.     {$ENDIF}
  213.    
  214.         procedure SetURL(const Value: string);
  215.         function  GetSOAPAction: string;
  216.         procedure SetSOAPAction(const SOAPAction: string);
  217.         procedure SetWSDLView(const WSDLVIew: TWSDLView);
  218.         function  GetSOAPActionHeader: string;
  219.         procedure InitURL(const Value: string);
  220.         procedure SetUsername(const NameValue: string);
  221.         procedure SetPassword(const PasswordValue: string);
  222.         procedure SetProxy(const ProxyValue: string);
  223.     {$IFDEF DEXTER_UP}
  224.         function  GetAgentIsStored:Boolean;
  225.     {$ENDIF}
  226.       protected
  227.         function _AddRef: Integer; stdcall;
  228.         function _Release: Integer; stdcall;
  229.         function GetMimeBoundary: string;
  230.         procedure SetMimeBoundary(Value: string);
  231.         function  GetWebNodeOptions: WebNodeOptions;
  232.         procedure SetWebNodeOptions(Value: WebNodeOptions);
  233.       public
  234.         constructor Create(Owner: TComponent); override;
  235.         class function NewInstance: TObject; override;
  236.         procedure AfterConstruction; override;
  237.         destructor Destroy; override;
  238.         function  GetHTTPReqResp: THTTPReqResp;
  239.         procedure CheckContentType;
  240.     {$IFNDEF USE_INDY}
  241.         procedure Check(Error: Boolean; ShowSOAPAction: Boolean = False);
  242.         procedure Connect(Value: Boolean);
  243.         function  Send(const ASrc: TStream): Integer; virtual;
  244.         function  SendGet: Integer; virtual;
  245.         procedure Receive(Context: Integer; Resp: TStream; IsGet: Boolean =
  246.     False); virtual;
  247.         function  HandleWinInetError(LastError: DWord; Request: HINTERNET):
  248.     DWord;
  249.     {$ENDIF}
  250.     {$IFDEF USE_INDY}
  251.         procedure SetupIndy(IndyHttp: TIDHttp; Request: TStream);
  252.     {$ENDIF}
  253.         procedure Get(Resp: TStream); virtual;
  254.         {IWebNode}
  255.         procedure BeforeExecute(const IntfMD: TIntfMetaData;
  256.                                 const MethMD: TIntfMethEntry;
  257.                                 MethodIndex: Integer;
  258.                                 AttachHandler: IMimeAttachmentHandler);
  259.         procedure Execute(const DataMsg: String; Resp: TStream); overload;
  260.     virtual;
  261.         procedure Execute(const Request: TStream; Response: TStream); overload;
  262.     virtual;
  263.         function  Execute(const Request: TStream): TStream; overload; virtual;
  264.         property  URL: string read FURL write SetURL;
  265.         property  SoapAction: string read GetSOAPAction write SetSOAPAction;
  266.         { Can these be exposed when using Indy too?? }
  267.         property  ConnectTimeout: Integer read FConnectTimeout write
  268.     FConnectTimeout;
  269.         property  SendTimeout: Integer read FSendTimeout write FSendTimeout;
  270.         property  ReceiveTimeout: Integer read FReceiveTimeout write
  271.     FReceiveTimeout;
  272.         property  MaxSinglePostSize: Integer read FMaxSinglePostSize write
  273.     FMaxSinglePostSize;
  274.    
  275.     {$IFDEF USE_INDY}
  276.       {$IFDEF INDY_CUSTOM_IOHANDLER}
  277.         property IOHandler: TIdIOHandler read FIOHandler write FIOHandler;
  278.       {$ENDIF}
  279.     {$ENDIF}
  280.    
  281.       published
  282.         property  WSDLView: TWSDLView read FWSDLView write SetWSDLView;
  283.     {$IFDEF DEXTER_UP}
  284.         property  Agent: string read FAgent write FAgent stored
  285.     GetAgentIsStored;
  286.     {$ELSE}
  287.         property  Agent: string read FAgent write FAgent;
  288.     {$ENDIF}
  289.         property  UserName: string read FUserName write SetUserName;
  290.         property  Password: string read FPassword write SetPassword;
  291.         property  Proxy: string read FProxy write SetProxy;
  292.         property  ProxyByPass: string read FProxyByPass write FProxyByPass;
  293.     {$IFDEF DEXTER_UP}
  294.         property  UseUTF8InHeader: Boolean read FUseUTF8InHeader write
  295.     FUseUTF8InHeader default False;
  296.     {$ELSE}
  297.         property  UseUTF8InHeader: Boolean read FUseUTF8InHeader write
  298.     FUseUTF8InHeader;
  299.     {$ENDIF}
  300.         property  InvokeOptions: TSOAPInvokeOptions read FInvokeOptions write
  301.     FInvokeOptions;
  302.         property  WebNodeOptions: WebNodeOptions read FWebNodeOptions write
  303.     FWebNodeOptions;
  304.         property  UDDIBindingKey: WideString read FUDDIBindingKey write
  305.     FUDDIBindingKey;
  306.         property  UDDIOperator: String read FUDDIOperator write FUDDIOperator;
  307.    
  308.         { Events }
  309.         property  OnBeforePost: TBeforePostEvent read FOnBeforePost write
  310.     FOnBeforePost;
  311.         property  OnPostingData: TPostingDataEvent read FOnPostingData write
  312.     FOnPostingData;
  313.         property  OnReceivingData: TReceivingDataEvent read FOnReceivingData
  314.     write FOnReceivingData;
  315.         property  OnWinInetError: TWinInetErrorEvent read FOnWinInetError write
  316.     FOnWinInetError;
  317.       end;
  318.    
  319.       { Since we cannot modify THTTPReqResp for the update but want to add
  320.         support for a Client Serial Number, we'll slip this through the
  321.         backdoor }
  322.       IClientCertInfo = interface
  323.       ['{4EA73902-DD19-4952-A94D-CCCE7B995F5C}']
  324.         function GetCertSerialNumber: string;
  325.         procedure SetCertSerialNumber(const ASerialNum: string);
  326.         function GetCertName: string;
  327.         procedure SetCertName(const AName: string);
  328.         function GetCertIssuer: string;
  329.         procedure SetCertIssuer(const AIssuer: string);
  330.         function GetCertStore: Pointer;
  331.         procedure SetCertStore(APointer: Pointer);
  332.         function GetCertContext: Pointer;
  333.         procedure SetCertContext(AContext: Pointer);
  334.       end;
  335.    
  336.    
  337.     implementation
  338.    
  339.    
  340.     uses Variants, SOAPConst, XMLDoc, XMLIntf, InvokeRegistry, WSDLItems,
  341.          SOAPAttach, UDDIHelper,
  342.     {$IFDEF MSWINDOWS}
  343.          Windows,
  344.       {$IFDEF CLIENT_CERTIFICATE_SUPPORT}
  345.         {$IFDEF HAS_CERTHELPER}
  346.          CertHelper,
  347.         {$ENDIF}
  348.       {$ENDIF}
  349.     {$ENDIF}
  350.     {$IFNDEF USE_INDY}
  351.          xmldom;
  352.     {$ELSE}
  353.       {$IFDEF INDY_10}
  354.          IdAssignedNumbers,
  355.       {$ENDIF}
  356.          IdIntercept, IdException, IdURI, IdGlobal, IdHeaderList,
  357.     IdHTTPHeaderInfo;
  358.     {$ENDIF}
  359.    
  360.    
  361.     {$IFDEF CLIENT_CERTIFICATE_SUPPORT}
  362.       {$IFNDEF HAS_CERTHELPER}
  363.         {$DEFINE INLINE_CERTHELPER}
  364.         {$INCLUDE 'CompVer.inc'}
  365.       {$ENDIF}
  366.     {$ENDIF}
  367.    
  368.     const
  369.       SOAP_AGENT = 'CodeGear SOAP 1.3'; { Do not localize }
  370.    
  371.     {$IFDEF USE_INDY}
  372.     procedure ParseURI(AURI: string; var VProtocol, VHost, VPath, VDocument,
  373.                            VPort, VBookmark : string);
  374.     var
  375.       URI: TIdURI;
  376.     begin
  377.       URI := TIdURI.Create(AURI);
  378.       try
  379.         VProtocol := URI.Protocol;
  380.         VHost := URI.Host;
  381.         VPath := URI.Path;
  382.         VDocument := URI.Document;
  383.         VPort := URI.Port;
  384.         VBookmark := URI.Bookmark;
  385.       finally
  386.         URI.Free;
  387.       end;
  388.     end;
  389.     {$ENDIF}
  390.    
  391.     {$IF CompilerVersion <= 15.0}
  392.     constructor ESOAPHTTPException.Create(const Msg: string; SCode: Integer =
  393.     0);
  394.     {$ELSE}
  395.     constructor ESOAPHTTPException.Create(const Msg: string; SCode: Integer = 0;
  396.     Dummy: Integer = 0);
  397.     {$IFEND}
  398.     begin
  399.       inherited Create(Msg);
  400.       FStatusCode := SCode;
  401.     end;
  402.    
  403.     constructor ESOAPHTTPException.CreateFmt(const Msg: string; const Args:
  404.     array of const; SCode: Integer; Dummy: Integer);
  405.     begin
  406.       inherited CreateFmt(Msg, Args);
  407.       FStatusCode := SCode;
  408.     end;
  409.    
  410.     constructor THTTPReqResp.Create(Owner: TComponent);
  411.     begin
  412.       inherited;
  413.     {$IFNDEF USE_INDY}
  414.       FInetRoot := nil;
  415.       FInetConnect := nil;
  416.     {$ENDIF}
  417.       FUserSetURL := False;
  418.       FInvokeOptions := [soIgnoreInvalidCerts, soAutoCheckAccessPointViaUDDI];
  419.       FAgent := SOAP_AGENT;
  420.       FMaxSinglePostSize := $8000;
  421.       { Default this to true to allow Clients to send International Characters
  422.     without having to
  423.         explicit set this.
  424.         NOTE: This is a change from previous versions but it seems better based
  425.     on the number of
  426.               reports whose ultimate solution is related to not having enabled
  427.     this property
  428.               The property still specifies the default as False as we cannot
  429.     break interfaces for
  430.               this release. We'll reconsider the 'default' in a subsequent
  431.     release. }
  432.       FUseUTF8InHeader := True;
  433.     end;
  434.    
  435.     destructor THTTPReqResp.Destroy;
  436.     begin
  437.     {$IFNDEF USE_INDY}
  438.       if Assigned(FInetConnect) then
  439.         InternetCloseHandle(FInetConnect);
  440.       FInetConnect := nil;
  441.       if Assigned(FInetRoot) then
  442.         InternetCloseHandle(FInetRoot);
  443.       FInetRoot := nil;
  444.     {$ENDIF}
  445.       FConnected := False;
  446.       inherited;
  447.     end;
  448.    
  449.     class function THTTPReqResp.NewInstance: TObject;
  450.     begin
  451.       Result := inherited NewInstance;
  452.       THTTPReqResp(Result).FRefCount := 1;
  453.     end;
  454.    
  455.     procedure THTTPReqResp.AfterConstruction;
  456.     begin
  457.       inherited;
  458.       FOwnerIsComponent := Assigned(Owner) and (Owner is TComponent);
  459.       InterlockedDecrement(FRefCount);
  460.     end;
  461.    
  462.     { IInterface }
  463.    
  464.     function THTTPReqResp._AddRef: Integer;
  465.     begin
  466.       Result := InterlockedIncrement(FRefCount)
  467.     end;
  468.    
  469.     function THTTPReqResp._Release: Integer;
  470.     begin
  471.       Result := InterlockedDecrement(FRefCount);
  472.       { If we are not being used as a TComponent, then use refcount to manage
  473.     our
  474.         lifetime as with TInterfacedObject. }
  475.       if (Result = 0) and not FOwnerIsComponent then
  476.         Destroy;
  477.     end;
  478.    
  479.     {$IFNDEF USE_INDY}
  480.    
  481.     type
  482.       THTTPReqRespHelper = class helper for THTTPReqResp
  483.       protected
  484.         function  HandleWinInetErrorEx(LastError: DWord; Request: HINTERNET;
  485.                                        RaiseError: Boolean = False): DWord;
  486.         procedure RaiseCheck(ErrCode: DWORD; ShowSOAPAction: Boolean = False);
  487.       end;
  488.    
  489.     procedure THTTPReqResp.Check(Error: Boolean; ShowSOAPAction: Boolean);
  490.     var
  491.       ErrCode: Integer;
  492.       S: string;
  493.     begin
  494.       if Error then
  495.       begin
  496.         ErrCode := GetLastError;
  497.         if (ErrCode <> 0) then
  498.         begin
  499.           RaiseCheck(ErrCode, ShowSOAPAction);
  500.         end;
  501.       end;
  502.     end;
  503.    
  504.     procedure THTTPReqRespHelper.RaiseCheck(ErrCode: DWORD; ShowSOAPAction:
  505.     Boolean);
  506.     var
  507.       S: string;
  508.     begin
  509.       if (ErrCode <> 0) then
  510.       begin
  511.         SetLength(S, 256);
  512.         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_FROM_HMODULE,
  513.     Pointer(GetModuleHandle('wininet.dll')),
  514.           ErrCode, 0, PChar(S), Length(S), nil);
  515.         SetLength(S, StrLen(PChar(S)));
  516.         while (Length(S) > 0) and CharInSet(S[Length(S)], [#10, #13]) do
  517.           SetLength(S, Length(S) - 1);
  518.         raise ESOAPHTTPException.CreateFmt('%s - URL:%s - SOAPAction:%s', [S,
  519.     FURL, SoapAction], ErrCode);      { Do not localize }
  520.       end;
  521.     end;
  522.    
  523.     {$ELSE}
  524.     procedure THTTPReqResp.IndyProxyAuthorization(Sender: TObject;
  525.                                                   Authentication:
  526.     TIdAuthentication;
  527.                                                   var Handled: Boolean);
  528.     begin
  529.       Authentication.UserName := FUserName;
  530.       Authentication.Password := FPassword;
  531.       Handled := True;
  532.     end;
  533.     {$ENDIF}
  534.    
  535.     function THTTPReqResp.GetHTTPReqResp: THTTPReqResp;
  536.     begin
  537.       Result := Self;
  538.     end;
  539.    
  540.     function THTTPReqResp.GetSOAPAction: string;
  541.     begin
  542.       if (FSoapAction = '') and not (soNoValueForEmptySOAPAction in
  543.     FInvokeOptions) then
  544.         Result := '""'
  545.       else
  546.         Result := FSoapAction;
  547.     end;
  548.    
  549.     procedure THTTPReqResp.SetSOAPAction(const SOAPAction: string);
  550.     begin
  551.       FSoapAction := SOAPAction;
  552.     end;
  553.    
  554.     procedure THTTPReqResp.SetWSDLView(const WSDLVIew: TWSDLView);
  555.     begin
  556.       FWSDLView := WSDLView;
  557.     end;
  558.    
  559.     procedure THTTPReqResp.SetURL(const Value: string);
  560.     begin
  561.       if Value <> '' then
  562.         FUserSetURL := True
  563.       else
  564.         FUserSetURL := False;
  565.       InitURL(Value);
  566.     {$IFNDEF USE_INDY}
  567.       { Here we always disconnect if a new URL comes in...
  568.         this ensures that we don't keep a connection to
  569.         a wrong host }
  570.       Connect(False);
  571.     {$ENDIF}
  572.     end;
  573.    
  574.     procedure THTTPReqResp.InitURL(const Value: string);
  575.     {$IFNDEF USE_INDY}
  576.     var
  577.       URLComp: TURLComponents;
  578.       P: PChar;
  579.     {$ELSE}
  580.     const
  581.       http = 'http://';
  582.     var
  583.       IndyHTTP: TIDHttp;
  584.       URI, Protocol, Host, path, Document, Port, Bookmark: string;
  585.     {$ENDIF}
  586.     begin
  587.       if Value <> '' then
  588.       begin
  589.     {$IFNDEF USE_INDY}
  590.         FillChar(URLComp, SizeOf(URLComp), 0);
  591.         URLComp.dwStructSize := SizeOf(URLComp);
  592.         URLComp.dwSchemeLength := 1;
  593.         URLComp.dwHostNameLength := 1;
  594.         URLComp.dwURLPathLength := 1;
  595.         P := PChar(Value);
  596.         InternetCrackUrl(P, 0, 0, URLComp);
  597.         if not (URLComp.nScheme in [INTERNET_SCHEME_HTTP,
  598.     INTERNET_SCHEME_HTTPS]) then
  599.           raise ESOAPHTTPException.CreateFmt(SInvalidURL, [Value]);
  600.         FURLScheme := URLComp.nScheme;
  601.         FURLPort := URLComp.nPort;
  602.         FURLHost := Copy(Value, URLComp.lpszHostName - P + 1,
  603.     URLComp.dwHostNameLength);
  604.         FURLSite := Copy(Value, URLComp.lpszUrlPath - P + 1,
  605.     URLComp.dwUrlPathLength);
  606.     {$ELSE}
  607.         IndyHTTP := TIDHttp.Create(Nil);
  608.         try
  609.           URI := Value;
  610.           ParseURI(URI, Protocol, Host, Path, Document, Port, Bookmark);
  611.           if Port <> '' then
  612.             FURLPort := StrToInt(Port)
  613.           else
  614.     {$IFDEF INDY_10}
  615.             FURLPort := IdPORT_HTTP;
  616.     {$ELSE}
  617.             FURLPort := IndyHTTP.Port;
  618.     {$ENDIF}
  619.           if Host <> '' then
  620.             FURLHost := Host
  621.           else
  622.             FURLHost := Copy(Value, Length(http)+1,
  623.                   Pos(':' + IntToStr(FURLPort), Value) - (Length(http)+1));
  624.         finally
  625.           IndyHTTP.Free;
  626.         end;
  627.     {$ENDIF}
  628.       end else
  629.       begin
  630.         FURLPort := 0;
  631.         FURLHost := '';
  632.         FURLSite := '';
  633.         FURLScheme := 0;
  634.       end;
  635.       FURL := Value;
  636.     end;
  637.    
  638.     procedure THTTPReqResp.SetMimeBoundary(Value: string);
  639.     begin
  640.       FMimeBoundary := Value;
  641.     end;
  642.    
  643.     function THTTPReqResp.GetMimeBoundary: string;
  644.     begin
  645.       Result := FMimeBoundary;
  646.     end;
  647.    
  648.     function THTTPReqResp.GetWebNodeOptions: WebNodeOptions;
  649.     begin
  650.       Result := FWebNodeOptions;
  651.     end;
  652.    
  653.     procedure THTTPReqResp.SetWebNodeOptions(Value: WebNodeOptions);
  654.     begin
  655.       FWebNodeOptions := Value;
  656.     end;
  657.    
  658.     procedure THTTPReqResp.SetUsername(const NameValue: string);
  659.     begin
  660.       FUserName := NameValue;
  661.       if Assigned(WSDLView) then
  662.         WSDLView.UserName := NameValue;
  663.     end;
  664.    
  665.     procedure THTTPReqResp.SetPassword(const PasswordValue: string);
  666.     begin
  667.       FPassword := PasswordValue;
  668.       if Assigned(WSDLView) then
  669.         WSDLView.Password := PasswordValue;
  670.     end;
  671.    
  672.     procedure THTTPReqResp.SetProxy(const ProxyValue: string);
  673.     begin
  674.       FProxy := ProxyValue;
  675.       if Assigned(WSDLView) then
  676.         WSDLView.Proxy := ProxyValue;
  677.     end;
  678.    
  679.    
  680.     const
  681.       MaxStatusTest = 4096;
  682.       MaxContentType= 256;
  683.    
  684.     function THTTPReqResp.GetSOAPActionHeader: string;
  685.     begin
  686.       if (SoapAction = '') then
  687.         Result := SHTTPSoapAction + ':'
  688.       else if (SoapAction = '""') then
  689.         Result := SHTTPSoapAction + ': ""'
  690.       else
  691.         Result := SHTTPSoapAction + ': ' + '"' + SoapAction + '"';
  692.     end;
  693.    
  694.    
  695.     {$IFNDEF USE_INDY}
  696.    
  697.     procedure THTTPReqResp.Connect(Value: Boolean);
  698.     var
  699.       AccessType: Integer;
  700.     begin
  701.       if Value then
  702.       begin
  703.         { Yes, but what if we're connected to a different Host/Port?? }
  704.         { So take advantage of a cached handle, we'll assume that
  705.           Connect(False) will be called explicitly when we're switching
  706.           Host. To that end, SetURL always disconnects }
  707.         if (FConnected) then
  708.           Exit;
  709.    
  710.         { Proxy?? }
  711.         if Length(FProxy) > 0 then
  712.           AccessType := INTERNET_OPEN_TYPE_PROXY
  713.         else
  714.           AccessType := INTERNET_OPEN_TYPE_PRECONFIG;
  715.    
  716.         { Also, could switch to new API introduced in IE4/Preview2}
  717.         if InternetAttemptConnect(0) <> ERROR_SUCCESS then
  718.           SysUtils.Abort;
  719.    
  720.         FInetRoot := InternetOpen(PChar(FAgent), AccessType, PChar(FProxy),
  721.     PChar(FProxyByPass), 0);
  722.         Check(not Assigned(FInetRoot));
  723.         try
  724.           FInetConnect := InternetConnect(FInetRoot, PChar(FURLHost), FURLPort,
  725.     PChar(FUserName),
  726.             PChar(FPassword), INTERNET_SERVICE_HTTP, 0, Cardinal(Self));
  727.           Check(not Assigned(FInetConnect));
  728.           FConnected := True;
  729.         except
  730.           InternetCloseHandle(FInetRoot);
  731.           FInetRoot := nil;
  732.           raise;
  733.         end;
  734.       end
  735.       else
  736.       begin
  737.         if Assigned(FInetConnect) then
  738.           InternetCloseHandle(FInetConnect);
  739.         FInetConnect := nil;
  740.         if Assigned(FInetRoot) then
  741.           InternetCloseHandle(FInetRoot);
  742.         FInetRoot := nil;
  743.         FConnected := False;
  744.       end;
  745.     end;
  746.    
  747.     procedure THTTPReqResp.Receive(Context: Integer; Resp: TStream; IsGet:
  748.     Boolean);
  749.     var
  750.       Size, Downloaded, Status, Len, Index: DWord;
  751.       S: string;
  752.     {$IFDEF UNICODE}
  753.       bytes: TBytes;
  754.     {$ENDIF}
  755.     begin
  756.       Len := SizeOf(Status);
  757.       Index := 0;
  758.    
  759.       { Handle error }
  760.       if HttpQueryInfo(Pointer(Context), HTTP_QUERY_STATUS_CODE or
  761.     HTTP_QUERY_FLAG_NUMBER,
  762.         @Status, Len, Index) and (Status >= 300) and (Status <> 500) then
  763.       begin
  764.         Index := 0;
  765.         Size := MaxStatusTest;
  766.         SetLength(S, Size);
  767.         if HttpQueryInfo(Pointer(Context), HTTP_QUERY_STATUS_TEXT, @S[1], Size,
  768.     Index) then
  769.         begin
  770.           SetLength(S, Size div sizeof(Char));
  771.           raise ESOAPHTTPException.CreateFmt('%s (%d) - ''%s''', [S, Status,
  772.     FURL], Status);
  773.         end;
  774.       end;
  775.    
  776.       { Ask for Content-Type }
  777.       Size := MaxContentType;
  778.       SetLength(FContentType, MaxContentType);
  779.       HttpQueryInfo(Pointer(Context), HTTP_QUERY_CONTENT_TYPE, @FContentType[1],
  780.     Size, Index);
  781.       SetLength(FContentType, Size div sizeof(Char));
  782.    
  783.       { Extract Mime-Boundary }
  784.       FMimeBoundary := GetMimeBoundaryFromType(FContentType);
  785.    
  786.       { Read data }
  787.       Len := 0;
  788.       repeat
  789.         Check(not InternetQueryDataAvailable(Pointer(Context), Size, 0, 0));
  790.         if Size > 0 then
  791.         begin
  792.     {$IFDEF UNICODE}
  793.           SetLength(bytes, Size);
  794.           Check(not InternetReadFile(Pointer(Context), bytes, Size,
  795.     Downloaded));
  796.           Resp.Write(bytes[0], Size);
  797.     {$ELSE}
  798.           SetLength(S, Size);
  799.           Check(not InternetReadFile(Pointer(Context), @S[1], Size,
  800.     Downloaded));
  801.           Resp.Write(S[1], Size);
  802.     {$ENDIF}
  803.    
  804.           { Receiving Data event }
  805.           if Assigned(FOnReceivingData) then
  806.             FOnReceivingData(Size, Downloaded)
  807.         end;
  808.       until Size = 0;
  809.    
  810.       { Check that we have a valid content type}
  811.       { Ideally, we would always check but there are several WebServers out
  812.     there
  813.         that send files with .wsdl extension with the content type 'text/plain'
  814.     or
  815.         'text/html' ?? }
  816.       if not IsGet then
  817.         CheckContentType;
  818.     end;
  819.    
  820.    
  821.     function THTTPReqResp.HandleWinInetError(LastError: DWord; Request:
  822.     HINTERNET): DWord;
  823.     begin
  824.       Result := HandleWinInetErrorEx(LastError, Request, False);
  825.     end;
  826.    
  827.     function THTTPReqRespHelper.HandleWinInetErrorEx(LastError: DWord;
  828.                                                      Request: HINTERNET;
  829.                                                      RaiseError: Boolean):
  830.     DWord;
  831.    
  832.       function CallInternetErrorDlg: DWord;
  833.       var
  834.         P: Pointer;
  835.       begin
  836.         Result := InternetErrorDlg(GetDesktopWindow(), Request, LastError,
  837.                                    FLAGS_ERROR_UI_FILTER_FOR_ERRORS or
  838.                                    FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS or
  839.                                    FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, P);
  840.    
  841.         { After selecting client certificate send request again,
  842.           Note: InternetErrorDlg always returns ERROR_SUCCESS when called with
  843.                 ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED }
  844.         if LastError = ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED then
  845.           Result := ERROR_INTERNET_FORCE_RETRY;
  846.       end;
  847.    
  848.     const
  849.       { Missing from our WinInet currently }
  850.       INTERNET_OPTION_CLIENT_CERT_CONTEXT = 84;
  851.    
  852.     var
  853.       Flags, FlagsLen, DWCert, DWCertLen: DWord;
  854.       ClientCertInfo: IClientCertInfo;
  855.       CertSerialNum: string;
  856.     {$IFDEF CLIENT_CERTIFICATE_SUPPORT}
  857.       hStore: HCERTSTORE;
  858.       CertContext: PCERT_CONTEXT;
  859.     {$ENDIF}
  860.     begin
  861.       { Dispatch to custom handler, if there's one }
  862.       if Assigned(FOnWinInetError) then
  863.         Result := FOnWinInetError(LastError, Request)
  864.       else
  865.       begin
  866.         Result := ERROR_INTERNET_FORCE_RETRY;
  867.         { Handle INVALID_CA discreetly }
  868.         if (LastError = ERROR_INTERNET_INVALID_CA) and (soIgnoreInvalidCerts in
  869.     InvokeOptions) then
  870.         begin
  871.           FlagsLen := SizeOf(Flags);
  872.           InternetQueryOption(Request, INTERNET_OPTION_SECURITY_FLAGS,
  873.     Pointer(@Flags), FlagsLen);
  874.           Flags := Flags or SECURITY_FLAG_IGNORE_UNKNOWN_CA;
  875.           InternetSetOption(Request, INTERNET_OPTION_SECURITY_FLAGS,
  876.     Pointer(@Flags), FlagsLen);
  877.         end
  878.     {$IFDEF CLIENT_CERTIFICATE_SUPPORT}
  879.         else if (LastError = ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED) and
  880.                  Supports(Self, IClientCertInfo, ClientCertInfo) and
  881.                  (ClientCertInfo.GetCertSerialNumber <> '') then
  882.         begin
  883.           CertSerialNum := ClientCertInfo.GetCertSerialNumber();
  884.           hStore := ClientCertInfo.GetCertStore();
  885.           if hStore = nil then
  886.           begin
  887.             hStore := CertOpenSystemStore(0, PChar('MY'));
  888.             ClientCertInfo.SetCertStore(hStore);
  889.           end;
  890.           CertContext := FindCertWithSerialNumber(hStore, CertSerialNum);
  891.           if CertContext <> nil then
  892.           begin
  893.             ClientCertInfo.SetCertContext(CertContext);
  894.             InternetSetOption(Request, INTERNET_OPTION_CLIENT_CERT_CONTEXT,
  895.                               CertContext, SizeOf(CERT_CONTEXT));
  896.           end
  897.           else
  898.           begin
  899.             if RaiseError then
  900.               RaiseCheck(LastError);
  901.             Result := CallInternetErrorDlg;
  902.           end;
  903.         end
  904.     {$ENDIF}
  905.         else if (LastError = ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED) and
  906.     (soPickFirstClientCertificate in InvokeOptions) then
  907.         begin
  908.           { This instructs WinInet to pick the first (a random?) client
  909.     cerficated }
  910.           DWCertLen := SizeOf(DWCert);
  911.           DWCert := 0;
  912.           InternetSetOption(Request,
  913.     INTERNET_OPTION_SECURITY_SELECT_CLIENT_CERT,
  914.                             Pointer(@DWCert), DWCertLen);
  915.         end
  916.         else
  917.         begin
  918.           if RaiseError then
  919.             RaiseCheck(LastError);
  920.           Result := CallInternetErrorDlg;
  921.         end;
  922.       end;
  923.     end;
  924.    
  925.    
  926.     function THTTPReqResp.Send(const ASrc: TStream): Integer;
  927.     const
  928.       ContentTypeFormat: array[Boolean] of string = (ContentTypeTemplate,
  929.     ContentTypeWithActionFmt);
  930.     var
  931.       Request: HINTERNET;
  932.       RetVal, Flags: DWord;
  933.       ActionHeader: string;
  934.       ContentHeader: string;
  935.       BuffSize, Len: Integer;
  936.       INBuffer: INTERNET_BUFFERS;
  937.       WithAction: Boolean;
  938.       Buffer: TMemoryStream;
  939.       WinInetResult: BOOL;
  940.     {$IFDEF UNICODE}
  941.       DatStr: TBytesStream;
  942.     {$ELSE}
  943.       DatStr: TStringStream;
  944.     {$ENDIF}
  945.       UseSendRequestEx: Boolean;
  946.     begin
  947.       { Connect }
  948.       Connect(True);
  949.    
  950.       Flags := INTERNET_FLAG_KEEP_CONNECTION or INTERNET_FLAG_NO_CACHE_WRITE;
  951.       if FURLScheme = INTERNET_SCHEME_HTTPS then
  952.       begin
  953.         Flags := Flags or INTERNET_FLAG_SECURE;
  954.         if (soIgnoreInvalidCerts in InvokeOptions) then
  955.           Flags := Flags or (INTERNET_FLAG_IGNORE_CERT_CN_INVALID or
  956.                              INTERNET_FLAG_IGNORE_CERT_DATE_INVALID or
  957.                              SECURITY_FLAG_IGNORE_UNKNOWN_CA or
  958.                              SECURITY_FLAG_IGNORE_REVOCATION);
  959.       end;
  960.    
  961.       Request := nil;
  962.       try
  963.         Request := HttpOpenRequest(FInetConnect, 'POST', PChar(FURLSite), nil,
  964.                                    nil, nil, Flags, 0{Integer(Self)});
  965.         Check(not Assigned(Request));
  966.    
  967.         { Timeouts }
  968.         if FConnectTimeout > 0 then
  969.           Check(not InternetSetOption(Request, INTERNET_OPTION_CONNECT_TIMEOUT,
  970.     Pointer(@FConnectTimeout), SizeOf(FConnectTimeout)));
  971.         if FSendTimeout > 0 then
  972.           Check(not InternetSetOption(Request, INTERNET_OPTION_SEND_TIMEOUT,
  973.     Pointer(@FSendTimeout), SizeOf(FSendTimeout)));
  974.         if FReceiveTimeout > 0 then
  975.           Check(not InternetSetOption(Request, INTERNET_OPTION_RECEIVE_TIMEOUT,
  976.     Pointer(@FReceiveTimeout), SizeOf(FReceiveTimeout)));
  977.    
  978.         if (soIgnoreInvalidCerts in InvokeOptions) then
  979.           InternetSetOption(Request, INTERNET_OPTION_SECURITY_FLAGS,
  980.     Pointer(@Flags), Sizeof(Flags));
  981.    
  982.         { Setup packet based on Content-Type/Binding }
  983.         if FBindingType = btMIME then
  984.         begin
  985.           ContentHeader := Format(ContentHeaderMIME, [FMimeBoundary]);
  986.           ContentHeader := Format(ContentTypeTemplate, [ContentHeader]);
  987.           HttpAddRequestHeaders(Request, PChar(MIMEVersion),
  988.     Length(MIMEVersion), HTTP_ADDREQ_FLAG_ADD);
  989.    
  990.           { SOAPAction header }
  991.           { NOTE: It's not really clear whether this should be sent in the case
  992.                   of MIME Binding. Investigate interoperability ?? }
  993.           if not (soNoSOAPActionHeader in FInvokeOptions) then
  994.           begin
  995.             ActionHeader:= GetSOAPActionHeader;
  996.             HttpAddRequestHeaders(Request, PChar(ActionHeader),
  997.     Length(ActionHeader), HTTP_ADDREQ_FLAG_ADD);
  998.           end;
  999.    
  1000.         end else { Assume btSOAP }
  1001.         begin
  1002.           { SOAPAction header }
  1003.           WithAction := not (soNoSOAPActionHeader in FInvokeOptions);
  1004.           {Content Type Header }
  1005.           if not (wnoSOAP12 in GetWebNodeOptions) then
  1006.           begin
  1007.             if not (soNoSOAPActionHeader in FInvokeOptions) then
  1008.             begin
  1009.               ActionHeader := GetSOAPActionHeader;
  1010.               HttpAddRequestHeaders(Request, PChar(ActionHeader),
  1011.     Length(ActionHeader), HTTP_ADDREQ_FLAG_ADD);
  1012.             end;
  1013.    
  1014.             if UseUTF8InHeader then
  1015.               ContentHeader := Format(ContentTypeTemplate, [ContentTypeUTF8])
  1016.             else
  1017.               ContentHeader := Format(ContentTypeTemplate, [ContentTypeNoUTF8]);
  1018.           end
  1019.           else
  1020.           begin
  1021.             if UseUTF8InHeader then
  1022.               ContentHeader := Format(ContentTypeFormat[WithAction],
  1023.     [ContentType12UTF8, GetSOAPAction])
  1024.             else
  1025.               ContentHeader := Format(ContentTypeFormat[WithAction],
  1026.     [ContentType12NoUTF8, GetSOAPAction]);
  1027.           end;
  1028.         end;
  1029.    
  1030.         { Content-Type }
  1031.         HttpAddRequestHeaders(Request, PChar(ContentHeader),
  1032.     Length(ContentHeader), HTTP_ADDREQ_FLAG_ADD);
  1033.    
  1034.         { Before we pump data, see if user wants to handle something - like set
  1035.     Basic-Auth data?? }
  1036.         if Assigned(FOnBeforePost) then
  1037.           FOnBeforePost(Self, Request);
  1038.    
  1039.         ASrc.Position := 0;
  1040.         BuffSize := ASrc.Size;
  1041.         if BuffSize > FMaxSinglePostSize then
  1042.         begin
  1043.           UseSendRequestEx := True;
  1044.    
  1045.           Buffer := TMemoryStream.Create;
  1046.           try
  1047.             Buffer.SetSize(FMaxSinglePostSize);
  1048.    
  1049.             { Init Input Buffer }
  1050.             INBuffer.dwStructSize := SizeOf(INBuffer);
  1051.             INBuffer.Next := nil;
  1052.             INBuffer.lpcszHeader := nil;
  1053.             INBuffer.dwHeadersLength := 0;
  1054.             INBuffer.dwHeadersTotal := 0;
  1055.             INBuffer.lpvBuffer := nil;
  1056.             INBuffer.dwBufferLength := 0;
  1057.             INBuffer.dwBufferTotal := BuffSize;
  1058.             INBuffer.dwOffsetLow := 0;
  1059.             INBuffer.dwOffsetHigh := 0;
  1060.    
  1061.             while UseSendRequestEx do
  1062.             begin
  1063.               ASrc.Position := 0;
  1064.    
  1065.               { Don't assume we're coming back }
  1066.               UseSendRequestEx := False;
  1067.    
  1068.               { Start POST }
  1069.               Check(not HttpSendRequestEx(Request, @INBuffer, nil,
  1070.                                           0(*HSR_INITIATE or HSR_SYNC*), 0));
  1071.               try
  1072.                 while True do
  1073.                 begin
  1074.                   { Calc length of data to send }
  1075.                   Len := BuffSize - ASrc.Position;
  1076.                   if Len > FMaxSinglePostSize then
  1077.                     Len := FMaxSinglePostSize;
  1078.                   { Bail out if zip.. }
  1079.                   if Len = 0 then
  1080.                     break;
  1081.                   { Read data in buffer and write out}
  1082.                   Len := ASrc.Read(Buffer.Memory^, Len);
  1083.                   if Len = 0 then
  1084.                     raise ESOAPHTTPException.Create(SInvalidHTTPRequest);
  1085.    
  1086.    
  1087.                   RetVal := ERROR_SUCCESS;
  1088.                   if not InternetWriteFile(Request, @Buffer.Memory^, Len,
  1089.     RetVal) then
  1090.                     RetVal := HandleWinInetErrorEx(GetLastError, Request, True);
  1091.    
  1092.                   case RetVal of
  1093.                     ERROR_SUCCESS:;
  1094.                     ERROR_CANCELLED: SysUtils.Abort;
  1095.                     ERROR_INTERNET_FORCE_RETRY: {Retry the operation};
  1096.                   end;
  1097.    
  1098.                   { Posting Data Event }
  1099.                   if Assigned(FOnPostingData) then
  1100.                     FOnPostingData(ASrc.Position, BuffSize);
  1101.                 end;
  1102.               finally
  1103.                 RetVal := ERROR_SUCCESS;
  1104.                 if not HttpEndRequest(Request, nil, 0, 0) then
  1105.                     RetVal := HandleWinInetErrorEx(GetLastError, Request, True);
  1106.    
  1107.                 case RetVal of
  1108.                   ERROR_SUCCESS: ;
  1109.                   ERROR_CANCELLED: SysUtils.Abort;
  1110.                   ERROR_INTERNET_FORCE_RETRY:
  1111.                     { We're going back again pal:( }
  1112.                     { See the following URL:
  1113.                     http://www.archivum.info/microsoft.public.inetsdk.programming.wininet/2006-08/00013/Re:_ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED_from_HttpEndRequest
  1114.                     }
  1115.                     UseSendRequestEx := True;
  1116.                 end;
  1117.               end;
  1118.             end;
  1119.           finally
  1120.             Buffer.Free;
  1121.           end;
  1122.         end else
  1123.         begin
  1124.     {$IFDEF UNICODE}
  1125.           DatStr := TBytesStream.Create;
  1126.     {$ELSE}
  1127.           DatStr := TStringStream.Create('');
  1128.     {$ENDIF}
  1129.           try
  1130.             DatStr.CopyFrom(ASrc, 0);
  1131.             while True do
  1132.             begin
  1133.    
  1134.               { Posting Data Event }
  1135.               if Assigned(FOnPostingData) then
  1136.                 FOnPostingData(DatStr.Size, BuffSize);
  1137.    
  1138.               RetVal := ERROR_SUCCESS;
  1139.     {$IFDEF UNICODE}
  1140.               WinInetResult := HttpSendRequest(Request, nil, 0,
  1141.                                                DatStr.Bytes, DatStr.Size);
  1142.     {$ELSE}
  1143.               WinInetResult := HttpSendRequest(Request, nil, 0,
  1144.                                                @DatStr.DataString[1],
  1145.                                                Length(DatStr.DataString));
  1146.     {$ENDIF}
  1147.    
  1148.               if not WinInetResult then
  1149.                 RetVal := HandleWinInetErrorEx(GetLastError, Request, True);
  1150.    
  1151.               case RetVal of
  1152.                 ERROR_SUCCESS: break;
  1153.                 ERROR_CANCELLED: SysUtils.Abort;
  1154.                 ERROR_INTERNET_FORCE_RETRY: {Retry the operation};
  1155.               end;
  1156.             end;
  1157.           finally
  1158.             DatStr.Free;
  1159.           end;
  1160.         end;
  1161.       except
  1162.         if (Request <> nil) then
  1163.           InternetCloseHandle(Request);
  1164.         Connect(False);
  1165.         raise;
  1166.       end;
  1167.       Result := Integer(Request);
  1168.     end;
  1169.    
  1170.     function THTTPReqResp.SendGet: Integer;
  1171.     var
  1172.       Request: HINTERNET;
  1173.       RetVal, Flags : DWord;
  1174.       AcceptTypes: array of PChar;
  1175.     begin
  1176.       { Connect }
  1177.       Connect(True);
  1178.    
  1179.       SetLength(AcceptTypes, 2);
  1180.       AcceptTypes[0] := PChar('*/*');  { Do not localize }
  1181.       AcceptTypes[1] := nil;
  1182.       Flags := INTERNET_FLAG_DONT_CACHE;
  1183.       if FURLScheme = INTERNET_SCHEME_HTTPS then
  1184.       begin
  1185.         Flags := Flags or INTERNET_FLAG_SECURE;
  1186.         if (soIgnoreInvalidCerts in InvokeOptions) then
  1187.           Flags := Flags or (INTERNET_FLAG_IGNORE_CERT_CN_INVALID or
  1188.                              INTERNET_FLAG_IGNORE_CERT_DATE_INVALID or
  1189.                              SECURITY_FLAG_IGNORE_UNKNOWN_CA or
  1190.                              SECURITY_FLAG_IGNORE_REVOCATION);
  1191.       end;
  1192.    
  1193.       Request := nil;
  1194.       try
  1195.         Request := HttpOpenRequest(FInetConnect, 'GET', PChar(FURLSite), nil,
  1196.     { Do not localize }
  1197.           nil, Pointer(AcceptTypes), Flags, Integer(Self));
  1198.         Check(not Assigned(Request), False);
  1199.    
  1200.         while True do
  1201.         begin
  1202.           if (not HttpSendRequest(Request, nil, 0, nil, 0)) then
  1203.           begin
  1204.             RetVal := HandleWinInetError(GetLastError(), Request);
  1205.             case RetVal of
  1206.               ERROR_CANCELLED: SysUtils.Abort;
  1207.               ERROR_SUCCESS: Break;
  1208.               ERROR_INTERNET_FORCE_RETRY: {Retry the operation};
  1209.             end;
  1210.           end
  1211.           else
  1212.             Break;
  1213.         end;
  1214.       except
  1215.         if (Request <> nil) then
  1216.           InternetCloseHandle(Request);
  1217.         Connect(False);
  1218.         raise;
  1219.       end;
  1220.       Result := Integer(Request);
  1221.     end;
  1222.     {$ENDIF}
  1223.    
  1224.     {$IFDEF USE_INDY}
  1225.     procedure THTTPReqResp.SetupIndy(IndyHttp: TIDHttp; Request: TStream);
  1226.    
  1227.       procedure GetHostAndPort(const AURL: string; var AHost, APort: string);
  1228.       var
  1229.         Index: Integer;
  1230.       begin
  1231.         Index := Pos(':', AURL);
  1232.         if Index > 0 then
  1233.         begin
  1234.           AHost := Copy(AURL, 1, Index-1);
  1235.           APort := Copy(AURL, Index+1, MaxInt);
  1236.         end;
  1237.     end;
  1238.    
  1239.       function IsHTTPS: Boolean;
  1240.       var
  1241.         Protocol, Host, path, Document, Port, Bookmark: string;
  1242.       begin
  1243.         ParseURI(FUrl, Protocol, Host, path, Document, Port, Bookmark);
  1244.         Result := AnsiSameText(Protocol, 'HTTPS');
  1245.       end;
  1246.    
  1247.     var
  1248.       Protocol, Host, Path, Document, Port, Bookmark: string;
  1249.     begin
  1250.     {$IFDEF INDY_CUSTOM_IOHANDLER}
  1251.       if FIOHandler <> nil then
  1252.         IndyHttp.IOHandler := FIOHandler
  1253.       else
  1254.     {$ENDIF}
  1255.       begin
  1256.         if IsHttps then
  1257.         begin
  1258.     {$IFDEF INDY_10}
  1259.           IndyHttp.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  1260.     {$ELSE}
  1261.           IndyHttp.IOHandler := TIdSSLIOHandlerSocket.Create(nil);
  1262.     {$ENDIF}
  1263.         end;
  1264.       end;
  1265.    
  1266.     {  if Request is TMimeAttachmentHandler then }
  1267.       if FBindingType = btMIME then
  1268.       begin
  1269.         IndyHttp.Request.ContentType := Format(ContentHeaderMIME,
  1270.     [FMimeBoundary]);
  1271.         IndyHttp.Request.CustomHeaders.Add(MimeVersion);
  1272.       end else { Assume btSOAP }
  1273.       begin
  1274.         IndyHttp.Request.ContentType := sTextXML;
  1275.         IndyHttp.Request.CustomHeaders.Add(GetSOAPActionHeader);
  1276.       end;
  1277.    
  1278.       IndyHttp.Request.Accept := '*/*';
  1279.       IndyHttp.Request.UserAgent := Self.FAgent;
  1280.    
  1281.       { Proxy support configuration }
  1282.       if FProxy <> '' then
  1283.       begin
  1284.         { first check for 'http://localhost:####' }
  1285.         ParseURI(FProxy, Protocol, Host, Path, Document, Port, Bookmark);
  1286.         { if fail then check for 'localhost:####' }
  1287.         if Host = '' then
  1288.           GetHostAndPort(FProxy, Host, Port);
  1289.         IndyHttp.ProxyParams.ProxyServer := Host;
  1290.         if Port <> '' then
  1291.           IndyHttp.ProxyParams.ProxyPort := StrToInt(Port);
  1292.    
  1293.         { If name/password is used in conjunction with proxy, it's passed
  1294.           along for proxy authentication }
  1295.         IndyHttp.ProxyParams.ProxyUsername := FUserName;
  1296.         IndyHttp.ProxyParams.ProxyPassword := FPassword;
  1297.       end else
  1298.       begin
  1299.         { no proxy with Username/Password implies basic authentication }
  1300.         IndyHttp.Request.Username := FUserName;
  1301.         IndyHttp.Request.Password := FPassword;
  1302.       end;
  1303.     {$IFNDEF INDY_10}
  1304.       IndyHttp.Host := FUrlHost;
  1305.       IndyHttp.Port := FUrlPort;
  1306.     {$ENDIF}
  1307.     end;
  1308.     {$ENDIF}
  1309.    
  1310.     procedure THTTPReqResp.Get(Resp: TStream);
  1311.     {$IFNDEF USE_INDY}
  1312.     var
  1313.       Context: Integer;
  1314.     {$ENDIF}
  1315.     {$IFDEF USE_INDY}
  1316.       procedure LoadFromURL(URL: string; Stream: TStream);
  1317.       var
  1318.         IndyHTTP: TIDHttp;
  1319.         Protocol, Host, Path, Document, Port, Bookmark: string;
  1320.       begin
  1321.         IndyHTTP := TIDHttp.Create(Nil);
  1322.         try
  1323.           IndyHttp.Request.Accept := '*/*';
  1324.           IndyHttp.Request.UserAgent := Self.FAgent;
  1325.           IndyHttp.Request.ContentType := sTextXml;
  1326.           if FProxy <> '' then
  1327.           begin
  1328.             ParseURI(FProxy, Protocol, Host, Path, Document, Port, Bookmark);
  1329.             IndyHttp.ProxyParams.ProxyServer := Host;
  1330.             IndyHttp.ProxyParams.ProxyPort := StrToInt(Port);
  1331.             IndyHttp.ProxyParams.ProxyUsername := FUserName;
  1332.             IndyHttp.ProxyParams.ProxyPassword := FPassword;
  1333.           end else
  1334.           begin
  1335.             { no proxy with Username/Password implies basic authentication }
  1336.             IndyHttp.Request.Username := FUserName;
  1337.             IndyHttp.Request.Password := FPassword;
  1338.           end;
  1339.           { IndyHttp.Intercept := FIntercept; }
  1340.           IndyHttp.Get(URL, Stream);
  1341.         finally
  1342.           IndyHTTP.Free;
  1343.         end;
  1344.       end;
  1345.     {$ENDIF}
  1346.     begin
  1347.       { GETs require a URL }
  1348.       if URL = '' then
  1349.         raise ESOAPHTTPException.Create(SEmptyURL);
  1350.     {$IFDEF USE_INDY}
  1351.       { GET with INDY }
  1352.       LoadFromURL(URL, Resp);
  1353.     {$ELSE}
  1354.       Context := SendGet;
  1355.       try
  1356.         Receive(Context, Resp, True);
  1357.       finally
  1358.         if Context <> 0  then
  1359.           InternetCloseHandle(Pointer(Context));
  1360.         Connect(False);
  1361.       end;
  1362.     {$ENDIF}
  1363.     end;
  1364.    
  1365.     { Here the RIO can perform any transports specific setup before call - XML
  1366.     serialization is done }
  1367.     procedure THTTPReqResp.BeforeExecute(const IntfMD: TIntfMetaData;
  1368.                                          const MethMD: TIntfMethEntry;
  1369.                                          MethodIndex: Integer;
  1370.                                          AttachHandler: IMimeAttachmentHandler);
  1371.     var
  1372.       MethName: InvString;
  1373.       Binding: InvString;
  1374.       QBinding: IQualifiedName;
  1375.       SOAPVersion: TSOAPVersion;
  1376.     begin
  1377.       if FUserSetURL then
  1378.       begin
  1379.         MethName := InvRegistry.GetMethExternalName(IntfMD.Info, MethMD.Name);
  1380.         FSoapAction := InvRegistry.GetActionURIOfInfo(IntfMD.Info, MethName,
  1381.     MethodIndex);
  1382.       end
  1383.       else
  1384.       begin
  1385.         { User did *NOT* set a URL }
  1386.         if WSDLView <> nil then
  1387.         begin
  1388.           if ioSOAP12 in InvRegistry.GetIntfInvokeOptions(IntfMD.Info) then
  1389.             SOAPVersion := svSOAP12
  1390.           else
  1391.             SOAPVersion := svSOAP11;
  1392.    
  1393.           { Make sure WSDL is active }
  1394.           WSDLView.Activate;
  1395.           QBinding := WSDLView.WSDL.GetBindingForServicePort(WSDLView.Service,
  1396.     WSDLView.Port);
  1397.           if QBinding <> nil then
  1398.           begin
  1399.             Binding := QBinding.Name;
  1400.             MethName:= InvRegistry.GetMethExternalName(WSDLView.IntfInfo,
  1401.     WSDLView.Operation);
  1402.    
  1403.             FSoapAction := WSDLView.WSDL.GetSoapAction(Binding, MethName, 0,
  1404.     SOAPVersion);
  1405.           end;
  1406.    
  1407.           {NOTE: In case we can't get the SOAPAction - see if we have something
  1408.     in the registry }
  1409.           {      It can't hurt:) }
  1410.           if FSoapAction = '' then
  1411.             InvRegistry.GetActionURIOfInfo(IntfMD.Info, MethName, MethodIndex);
  1412.    
  1413.           { Retrieve URL }
  1414.           FURL := WSDLView.WSDL.GetSoapAddressForServicePort(WSDLView.Service,
  1415.     WSDLView.Port, SOAPVersion);
  1416.           if (FURL = '') then
  1417.             raise ESOAPHTTPException.CreateFmt(sCantGetURL,
  1418.                                                [WSDLView.Service, WSDLView.Port,
  1419.     WSDLView.WSDL.FileName]);
  1420.           InitURL(FURL);
  1421.         end
  1422.         else
  1423.           raise ESOAPHTTPException.Create(sNoWSDLURL);
  1424.       end;
  1425.    
  1426.       { Are we sending attachments?? }
  1427.       if AttachHandler <> nil then
  1428.       begin
  1429.         FBindingType := btMIME;
  1430.         { If yes, ask MIME handler what MIME boundary it's using to build the
  1431.     Multipart
  1432.           packet }
  1433.         FMimeBoundary := AttachHandler.MIMEBoundary;
  1434.    
  1435.         { Also customize the MIME packet for transport specific items }
  1436.         if UseUTF8InHeader then
  1437.           AttachHandler.AddSoapHeader(Format(ContentTypeTemplate,
  1438.     [ContentTypeUTF8]))
  1439.         else
  1440.           AttachHandler.AddSoapHeader(Format(ContentTypeTemplate,
  1441.     [ContentTypeNoUTF8]));
  1442.         AttachHandler.AddSoapHeader(GetSOAPActionHeader);
  1443.       end else
  1444.         FBindingType := btSOAP;
  1445.     end;
  1446.    
  1447.     procedure THTTPReqResp.Execute(const DataMsg: String; Resp: TStream);
  1448.     var
  1449.       Stream: TMemoryStream;
  1450.     {$IFDEF UNICODE}
  1451.       AStr: AnsiString;
  1452.     {$ENDIF}
  1453.     begin
  1454.     {$IFDEF UNICODE}
  1455.       AStr := UTF8Encode(DataMsg);
  1456.     {$ENDIF}
  1457.       Stream := TMemoryStream.Create;
  1458.       try
  1459.     {$IFDEF UNICODE}
  1460.         Stream.SetSize(Length(AStr));
  1461.         Stream.Write(AStr[1], Length(AStr));
  1462.     {$ELSE}
  1463.         Stream.SetSize(Length(DataMsg));
  1464.         Stream.Write(DataMsg[1], Length(DataMsg));
  1465.     {$ENDIF}
  1466.         Execute(Stream, Resp);
  1467.       finally
  1468.         Stream.Free;
  1469.       end;
  1470.     end;
  1471.    
  1472.     function THTTPReqResp.Execute(const Request: TStream): TStream;
  1473.     begin
  1474.       Result := TMemoryStream.Create;
  1475.       Execute(Request, Result);
  1476.     end;
  1477.    
  1478.     procedure THTTPReqResp.CheckContentType;
  1479.     begin
  1480.       { NOTE: Content-Types are case insensitive! }
  1481.       {       Here we're not validating that we
  1482.               have a valid content-type; rather
  1483.               we're checking for some common invalid
  1484.               ones }
  1485.       if SameText(FContentType, ContentTypeTextPlain) or
  1486.          SameText(FContentType, STextHtml) then
  1487.         raise ESOAPHTTPException.CreateFmt(SInvalidContentType, [FContentType]);
  1488.     end;
  1489.    
  1490.     procedure THTTPReqResp.Execute(const Request: TStream; Response: TStream);
  1491.    
  1492.       function IsErrorStatusCode(Code: Integer): Boolean;
  1493.       begin
  1494.         case Code of
  1495.           404, 405, 410:
  1496.             Result := True;
  1497.           else
  1498.             Result := False;
  1499.         end;
  1500.       end;
  1501.    
  1502.     {$IFDEF USE_INDY}
  1503.       procedure PostData(const Request: TStream; Response: TStream);
  1504.       var
  1505.         IndyHTTP: TIDHttp;
  1506.       begin
  1507.         IndyHTTP := TIDHttp.Create(Nil);
  1508.         try
  1509.           SetupIndy(IndyHTTP, Request);
  1510.           IndyHttp.Post(FURL, Request, Response);
  1511.           FContentType := IndyHttp.Response.RawHeaders.Values[SContentType];
  1512.           FMimeBoundary := GetMimeBoundaryFromType(FContentType);
  1513.           if Response.Size = 0 then
  1514.             raise ESOAPHTTPException.Create(SInvalidHTTPResponse);
  1515.           CheckContentType;
  1516.         finally
  1517.           if Assigned(IndyHttp.IOHandler) then
  1518.     {$IFDEF INDY_CUSTOM_IOHANDLER}
  1519.             { Don't free the IOHandler if we did not create it }
  1520.             if FIOHandler = nil then
  1521.     {$ENDIF}
  1522.             IndyHttp.IOHandler.Free;
  1523.           FreeAndNil(IndyHTTP);
  1524.         end;
  1525.       end;
  1526.    
  1527.    
  1528.     {$ELSE}
  1529.     var
  1530.       Context: Integer;
  1531.       CanRetry: Boolean;
  1532.       LookUpUDDI: Boolean;
  1533.       AccessPoint: String;
  1534.       PrevError: String;
  1535.     {$ENDIF}
  1536.     begin
  1537.     {$IFNDEF USE_INDY}
  1538.       LookUpUDDI := False;
  1539.       CanRetry := (soAutoCheckAccessPointViaUDDI in FInvokeOptions) and
  1540.                   (Length(FUDDIBindingKey) > 0) and
  1541.                   (Length(FUDDIOperator) > 0);
  1542.     {$ENDIF}
  1543.     {$IFDEF USE_INDY}
  1544.       PostData(Request, Response);
  1545.     {$ELSE}
  1546.       while (True) do
  1547.       begin
  1548.         { Look up URL from UDDI?? }
  1549.         if LookUpUDDI and CanRetry then
  1550.         begin
  1551.           try
  1552.             CanRetry := False;
  1553.             AccessPoint := '';
  1554.             AccessPoint := GetBindingkeyAccessPoint(FUDDIOperator,
  1555.     FUDDIBindingKey);
  1556.           except
  1557.             { Ignore UDDI lookup error }
  1558.           end;
  1559.           { If UDDI lookup failed or we got back the same URL we used...
  1560.             raise the previous execption message }
  1561.           if (AccessPoint = '') or SameText(AccessPoint, FURL) then
  1562.             raise ESOAPHTTPException.Create(PrevError);
  1563.           SetURL(AccessPoint);
  1564.         end;
  1565.    
  1566.         Context := Send(Request);
  1567.         try
  1568.           try
  1569.             Receive(Context, Response);
  1570.             Exit;
  1571.           except
  1572.             on Ex: ESOAPHTTPException do
  1573.             begin
  1574.               Connect(False);
  1575.               if not CanRetry or not IsErrorStatusCode(Ex.StatusCode) then
  1576.                 raise;
  1577.               { Trigger UDDI Lookup }
  1578.               LookUpUDDI := True;
  1579.               PrevError := Ex.Message;
  1580.             end;
  1581.             else
  1582.             begin
  1583.               Connect(False);
  1584.               raise;
  1585.             end;
  1586.           end;
  1587.         finally
  1588.           if Context <> 0  then
  1589.             InternetCloseHandle(Pointer(Context));
  1590.         end;
  1591.       end;
  1592.     {$ENDIF}
  1593.     end;
  1594.    
  1595.     {$IFDEF DEXTER_UP}
  1596.     function THTTPReqResp.GetAgentIsStored: Boolean;
  1597.     begin
  1598.       Result := FAgent <> SOAP_AGENT;
  1599.     end;
  1600.     {$ENDIF}
  1601.    
  1602.    
  1603.     end.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement