blob: e3886a56bd0e8d47e3df31a5e127847ee07460e9 [file] [log] [blame]
Jens Geyer02230912019-04-03 01:12:51 +02001(*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 *)
19unit Thrift.Transport.MsxmlHTTP;
20
21{$I Thrift.Defines.inc}
22{$SCOPEDENUMS ON}
23
24interface
25
26uses
27 Classes,
28 SysUtils,
29 Math,
30 Generics.Collections,
31 {$IFDEF OLD_UNIT_NAMES}
32 ActiveX, msxml,
33 {$ELSE}
34 Winapi.ActiveX, Winapi.msxml,
35 {$ENDIF}
36 Thrift.Collections,
Jens Geyera019cda2019-11-09 23:24:52 +010037 Thrift.Configuration,
Jens Geyer02230912019-04-03 01:12:51 +020038 Thrift.Transport,
39 Thrift.Exception,
40 Thrift.Utils,
41 Thrift.Stream;
42
43type
Jens Geyera019cda2019-11-09 23:24:52 +010044 TMsxmlHTTPClientImpl = class( TEndpointTransportBase, IHTTPClient)
Jens Geyerfad7fd32019-11-09 23:24:52 +010045 strict private
Jens Geyer02230912019-04-03 01:12:51 +020046 FUri : string;
47 FInputStream : IThriftStream;
48 FOutputStream : IThriftStream;
49 FDnsResolveTimeout : Integer;
50 FConnectionTimeout : Integer;
51 FSendTimeout : Integer;
52 FReadTimeout : Integer;
53 FCustomHeaders : IThriftDictionary<string,string>;
54
55 function CreateRequest: IXMLHTTPRequest;
Jens Geyer72c81112025-03-10 21:46:20 +010056 class procedure EnsureSuccessHttpStatus( const aRequest : IXMLHTTPRequest);
57
Jens Geyerfad7fd32019-11-09 23:24:52 +010058 strict protected
Jens Geyer02230912019-04-03 01:12:51 +020059 function GetIsOpen: Boolean; override;
60 procedure Open(); override;
61 procedure Close(); override;
62 function Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; override;
63 procedure Write( const pBuf : Pointer; off, len : Integer); override;
64 procedure Flush; override;
65
66 procedure SetDnsResolveTimeout(const Value: Integer);
67 function GetDnsResolveTimeout: Integer;
68 procedure SetConnectionTimeout(const Value: Integer);
69 function GetConnectionTimeout: Integer;
70 procedure SetSendTimeout(const Value: Integer);
71 function GetSendTimeout: Integer;
72 procedure SetReadTimeout(const Value: Integer);
73 function GetReadTimeout: Integer;
Jens Geyer47f63172019-06-06 22:42:58 +020074 function GetSecureProtocols : TSecureProtocols;
75 procedure SetSecureProtocols( const value : TSecureProtocols);
Jens Geyer02230912019-04-03 01:12:51 +020076
77 function GetCustomHeaders: IThriftDictionary<string,string>;
78 procedure SendRequest;
Jens Geyer47f63172019-06-06 22:42:58 +020079
Jens Geyer02230912019-04-03 01:12:51 +020080 property DnsResolveTimeout: Integer read GetDnsResolveTimeout write SetDnsResolveTimeout;
81 property ConnectionTimeout: Integer read GetConnectionTimeout write SetConnectionTimeout;
82 property SendTimeout: Integer read GetSendTimeout write SetSendTimeout;
83 property ReadTimeout: Integer read GetReadTimeout write SetReadTimeout;
84 property CustomHeaders: IThriftDictionary<string,string> read GetCustomHeaders;
85 public
Jens Geyera019cda2019-11-09 23:24:52 +010086 constructor Create( const aUri: string; const aConfig : IThriftConfiguration); reintroduce;
Jens Geyer02230912019-04-03 01:12:51 +020087 destructor Destroy; override;
88 end;
89
90
91implementation
92
Jens Geyera019cda2019-11-09 23:24:52 +010093const
94 XMLHTTP_CONNECTION_TIMEOUT = 60 * 1000;
95 XMLHTTP_SENDRECV_TIMEOUT = 30 * 1000;
Jens Geyer02230912019-04-03 01:12:51 +020096
97{ TMsxmlHTTPClientImpl }
98
Jens Geyera019cda2019-11-09 23:24:52 +010099constructor TMsxmlHTTPClientImpl.Create( const aUri: string; const aConfig : IThriftConfiguration);
Jens Geyer02230912019-04-03 01:12:51 +0200100begin
Jens Geyera019cda2019-11-09 23:24:52 +0100101 inherited Create( aConfig);
102 FUri := aUri;
Jens Geyer02230912019-04-03 01:12:51 +0200103
104 // defaults according to MSDN
105 FDnsResolveTimeout := 0; // no timeout
Jens Geyera019cda2019-11-09 23:24:52 +0100106 FConnectionTimeout := XMLHTTP_CONNECTION_TIMEOUT;
107 FSendTimeout := XMLHTTP_SENDRECV_TIMEOUT;
108 FReadTimeout := XMLHTTP_SENDRECV_TIMEOUT;
Jens Geyer02230912019-04-03 01:12:51 +0200109
110 FCustomHeaders := TThriftDictionaryImpl<string,string>.Create;
Jens Geyerf726ae32021-06-04 11:17:26 +0200111 FOutputStream := TThriftStreamAdapterDelphi.Create( TThriftMemoryStream.Create, True);
Jens Geyer02230912019-04-03 01:12:51 +0200112end;
113
114function TMsxmlHTTPClientImpl.CreateRequest: IXMLHTTPRequest;
115var
116 pair : TPair<string,string>;
117 srvHttp : IServerXMLHTTPRequest;
118begin
119 {$IF CompilerVersion >= 21.0}
120 Result := CoServerXMLHTTP.Create;
121 {$ELSE}
122 Result := CoXMLHTTPRequest.Create;
123 {$IFEND}
124
125 // setting a timeout value to 0 (zero) means "no timeout" for that setting
126 if Supports( result, IServerXMLHTTPRequest, srvHttp)
127 then srvHttp.setTimeouts( DnsResolveTimeout, ConnectionTimeout, SendTimeout, ReadTimeout);
128
129 Result.open('POST', FUri, False, '', '');
Jens Geyer83ff7532019-06-06 22:46:03 +0200130 Result.setRequestHeader( 'Content-Type', THRIFT_MIMETYPE);
131 Result.setRequestHeader( 'Accept', THRIFT_MIMETYPE);
Jens Geyeraad75832022-06-01 22:06:29 +0200132 Result.setRequestHeader( 'User-Agent', 'ApacheThriftDelphi/msxml');
Jens Geyer02230912019-04-03 01:12:51 +0200133
134 for pair in FCustomHeaders do begin
135 Result.setRequestHeader( pair.Key, pair.Value );
136 end;
137end;
138
139destructor TMsxmlHTTPClientImpl.Destroy;
140begin
141 Close;
142 inherited;
143end;
144
145function TMsxmlHTTPClientImpl.GetDnsResolveTimeout: Integer;
146begin
147 Result := FDnsResolveTimeout;
148end;
149
150procedure TMsxmlHTTPClientImpl.SetDnsResolveTimeout(const Value: Integer);
151begin
152 FDnsResolveTimeout := Value;
153end;
154
155function TMsxmlHTTPClientImpl.GetConnectionTimeout: Integer;
156begin
157 Result := FConnectionTimeout;
158end;
159
160procedure TMsxmlHTTPClientImpl.SetConnectionTimeout(const Value: Integer);
161begin
162 FConnectionTimeout := Value;
163end;
164
165function TMsxmlHTTPClientImpl.GetSendTimeout: Integer;
166begin
167 Result := FSendTimeout;
168end;
169
170procedure TMsxmlHTTPClientImpl.SetSendTimeout(const Value: Integer);
171begin
172 FSendTimeout := Value;
173end;
174
175function TMsxmlHTTPClientImpl.GetReadTimeout: Integer;
176begin
177 Result := FReadTimeout;
178end;
179
180procedure TMsxmlHTTPClientImpl.SetReadTimeout(const Value: Integer);
181begin
182 FReadTimeout := Value;
183end;
184
Jens Geyer47f63172019-06-06 22:42:58 +0200185function TMsxmlHTTPClientImpl.GetSecureProtocols : TSecureProtocols;
186begin
187 Result := [];
188end;
189
190procedure TMsxmlHTTPClientImpl.SetSecureProtocols( const value : TSecureProtocols);
191begin
192 raise TTransportExceptionBadArgs.Create('SetSecureProtocols: Not supported with '+ClassName);
193end;
194
Jens Geyer02230912019-04-03 01:12:51 +0200195function TMsxmlHTTPClientImpl.GetCustomHeaders: IThriftDictionary<string,string>;
196begin
197 Result := FCustomHeaders;
198end;
199
200function TMsxmlHTTPClientImpl.GetIsOpen: Boolean;
201begin
Jens Geyercc8c2c62021-03-29 22:38:30 +0200202 Result := Assigned(FOutputStream);
Jens Geyer02230912019-04-03 01:12:51 +0200203end;
204
205procedure TMsxmlHTTPClientImpl.Open;
206begin
Jens Geyerf726ae32021-06-04 11:17:26 +0200207 FOutputStream := TThriftStreamAdapterDelphi.Create( TThriftMemoryStream.Create, True);
Jens Geyer02230912019-04-03 01:12:51 +0200208end;
209
210procedure TMsxmlHTTPClientImpl.Close;
211begin
212 FInputStream := nil;
213 FOutputStream := nil;
214end;
215
216procedure TMsxmlHTTPClientImpl.Flush;
217begin
218 try
219 SendRequest;
220 finally
221 FOutputStream := nil;
Jens Geyerf726ae32021-06-04 11:17:26 +0200222 FOutputStream := TThriftStreamAdapterDelphi.Create( TThriftMemoryStream.Create, True);
Jens Geyer02230912019-04-03 01:12:51 +0200223 ASSERT( FOutputStream <> nil);
224 end;
225end;
226
227function TMsxmlHTTPClientImpl.Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer;
228begin
229 if FInputStream = nil then begin
230 raise TTransportExceptionNotOpen.Create('No request has been sent');
231 end;
232
233 try
Jens Geyer41f47af2019-11-09 23:24:52 +0100234 Result := FInputStream.Read( pBuf, buflen, off, len);
Jens Geyer02230912019-04-03 01:12:51 +0200235 except
236 on E: Exception
237 do raise TTransportExceptionUnknown.Create(E.Message);
238 end;
239end;
240
241procedure TMsxmlHTTPClientImpl.SendRequest;
242var
243 xmlhttp : IXMLHTTPRequest;
Jens Geyerf726ae32021-06-04 11:17:26 +0200244 ms : TThriftMemoryStream;
Jens Geyer02230912019-04-03 01:12:51 +0200245 a : TBytes;
246 len : Integer;
247begin
248 xmlhttp := CreateRequest;
249
Jens Geyerf726ae32021-06-04 11:17:26 +0200250 ms := TThriftMemoryStream.Create;
Jens Geyer02230912019-04-03 01:12:51 +0200251 try
252 a := FOutputStream.ToArray;
253 len := Length(a);
254 if len > 0 then begin
255 ms.WriteBuffer( Pointer(@a[0])^, len);
256 end;
257 ms.Position := 0;
258 xmlhttp.send( IUnknown( TStreamAdapter.Create( ms, soReference )));
259 FInputStream := nil;
Jens Geyer72c81112025-03-10 21:46:20 +0100260 EnsureSuccessHttpStatus(xmlhttp); // throws if not
Jens Geyer02230912019-04-03 01:12:51 +0200261 FInputStream := TThriftStreamAdapterCOM.Create( IUnknown( xmlhttp.responseStream) as IStream);
Jens Geyer5a781c22025-02-04 23:35:55 +0100262 ResetMessageSizeAndConsumedBytes;
Jens Geyera019cda2019-11-09 23:24:52 +0100263 UpdateKnownMessageSize( FInputStream.Size);
Jens Geyer02230912019-04-03 01:12:51 +0200264 finally
265 ms.Free;
266 end;
267end;
268
269procedure TMsxmlHTTPClientImpl.Write( const pBuf : Pointer; off, len : Integer);
270begin
Jens Geyercc8c2c62021-03-29 22:38:30 +0200271 if FOutputStream <> nil
272 then FOutputStream.Write( pBuf, off, len)
273 else raise TTransportExceptionNotOpen.Create('Transport closed');
Jens Geyer02230912019-04-03 01:12:51 +0200274end;
275
276
Jens Geyer72c81112025-03-10 21:46:20 +0100277class procedure TMsxmlHTTPClientImpl.EnsureSuccessHttpStatus( const aRequest : IXMLHTTPRequest);
278var iStatus : Integer;
279 sText : string;
280begin
281 if aRequest = nil
282 then raise TTransportExceptionNotOpen.Create('Invalid HTTP request data');
283
284 iStatus := aRequest.status;
285 sText := aRequest.statusText;
286
287 if (200 > iStatus) or (iStatus > 299)
288 then raise TTransportExceptionEndOfFile.Create('HTTP '+IntToStr(iStatus)+' '+sText);
289end;
290
Jens Geyer02230912019-04-03 01:12:51 +0200291
292end.
293