blob: d7eefa86298fffd9f560b3fb08069b9596f55f6f [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.WinHTTP;
20
21{$I Thrift.Defines.inc}
22{$SCOPEDENUMS ON}
23
24interface
25
26uses
27 Classes,
28 SysUtils,
29 Math,
30 Generics.Collections,
31 Thrift.Collections,
32 Thrift.Transport,
33 Thrift.Exception,
34 Thrift.Utils,
35 Thrift.WinHTTP,
36 Thrift.Stream;
37
38type
39 TWinHTTPClientImpl = class( TTransportImpl, IHTTPClient)
40 private
41 FUri : string;
42 FInputStream : IThriftStream;
43 FOutputMemoryStream : TMemoryStream;
44 FDnsResolveTimeout : Integer;
45 FConnectionTimeout : Integer;
46 FSendTimeout : Integer;
47 FReadTimeout : Integer;
48 FCustomHeaders : IThriftDictionary<string,string>;
Jens Geyer47f63172019-06-06 22:42:58 +020049 FSecureProtocols : TSecureProtocols;
Jens Geyer02230912019-04-03 01:12:51 +020050
51 function CreateRequest: IWinHTTPRequest;
Jens Geyer47f63172019-06-06 22:42:58 +020052 function SecureProtocolsAsWinHTTPFlags : Cardinal;
Jens Geyer02230912019-04-03 01:12:51 +020053
Jens Geyer19fdca82019-06-12 22:09:05 +020054 private
55 type
56 TErrorInfo = ( SplitUrl, WinHTTPSession, WinHTTPConnection, WinHTTPRequest, RequestSetup, AutoProxy );
57
Jens Geyer02230912019-04-03 01:12:51 +020058 THTTPResponseStream = class( TThriftStreamImpl)
59 private
60 FRequest : IWinHTTPRequest;
61 protected
62 procedure Write( const pBuf : Pointer; offset: Integer; count: Integer); override;
63 function Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; override;
64 procedure Open; override;
65 procedure Close; override;
66 procedure Flush; override;
67 function IsOpen: Boolean; override;
68 function ToArray: TBytes; override;
69 public
70 constructor Create( const aRequest : IWinHTTPRequest);
71 destructor Destroy; override;
72 end;
73
74 protected
75 function GetIsOpen: Boolean; override;
76 procedure Open(); override;
77 procedure Close(); override;
78 function Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; override;
79 procedure Write( const pBuf : Pointer; off, len : Integer); override;
80 procedure Flush; override;
81
82 procedure SetDnsResolveTimeout(const Value: Integer);
83 function GetDnsResolveTimeout: Integer;
84 procedure SetConnectionTimeout(const Value: Integer);
85 function GetConnectionTimeout: Integer;
86 procedure SetSendTimeout(const Value: Integer);
87 function GetSendTimeout: Integer;
88 procedure SetReadTimeout(const Value: Integer);
89 function GetReadTimeout: Integer;
Jens Geyer47f63172019-06-06 22:42:58 +020090 function GetSecureProtocols : TSecureProtocols;
91 procedure SetSecureProtocols( const value : TSecureProtocols);
Jens Geyer02230912019-04-03 01:12:51 +020092
93 function GetCustomHeaders: IThriftDictionary<string,string>;
94 procedure SendRequest;
Jens Geyer47f63172019-06-06 22:42:58 +020095
Jens Geyer02230912019-04-03 01:12:51 +020096 property DnsResolveTimeout: Integer read GetDnsResolveTimeout write SetDnsResolveTimeout;
97 property ConnectionTimeout: Integer read GetConnectionTimeout write SetConnectionTimeout;
98 property SendTimeout: Integer read GetSendTimeout write SetSendTimeout;
99 property ReadTimeout: Integer read GetReadTimeout write SetReadTimeout;
100 property CustomHeaders: IThriftDictionary<string,string> read GetCustomHeaders;
101 public
102 constructor Create( const AUri: string);
103 destructor Destroy; override;
104 end;
105
106implementation
107
108
109{ TWinHTTPClientImpl }
110
111constructor TWinHTTPClientImpl.Create(const AUri: string);
112begin
113 inherited Create;
114 FUri := AUri;
115
116 // defaults according to MSDN
117 FDnsResolveTimeout := 0; // no timeout
118 FConnectionTimeout := 60 * 1000;
119 FSendTimeout := 30 * 1000;
120 FReadTimeout := 30 * 1000;
121
Jens Geyer47f63172019-06-06 22:42:58 +0200122 FSecureProtocols := DEFAULT_THRIFT_SECUREPROTOCOLS;
123
Jens Geyer02230912019-04-03 01:12:51 +0200124 FCustomHeaders := TThriftDictionaryImpl<string,string>.Create;
125 FOutputMemoryStream := TMemoryStream.Create;
126end;
127
128destructor TWinHTTPClientImpl.Destroy;
129begin
130 Close;
131 FreeAndNil( FOutputMemoryStream);
132 inherited;
133end;
134
135function TWinHTTPClientImpl.CreateRequest: IWinHTTPRequest;
136var
Jens Geyer19fdca82019-06-12 22:09:05 +0200137 pair : TPair<string,string>;
Jens Geyer02230912019-04-03 01:12:51 +0200138 session : IWinHTTPSession;
139 connect : IWinHTTPConnection;
140 url : IWinHTTPUrl;
141 sPath : string;
Jens Geyer19fdca82019-06-12 22:09:05 +0200142 info : TErrorInfo;
Jens Geyer02230912019-04-03 01:12:51 +0200143begin
Jens Geyer19fdca82019-06-12 22:09:05 +0200144 info := TErrorInfo.SplitUrl;
145 try
146 url := TWinHTTPUrlImpl.Create( FUri);
Jens Geyer02230912019-04-03 01:12:51 +0200147
Jens Geyer19fdca82019-06-12 22:09:05 +0200148 info := TErrorInfo.WinHTTPSession;
149 session := TWinHTTPSessionImpl.Create('Apache Thrift Delphi WinHTTP');
150 session.EnableSecureProtocols( SecureProtocolsAsWinHTTPFlags);
Jens Geyer47f63172019-06-06 22:42:58 +0200151
Jens Geyer19fdca82019-06-12 22:09:05 +0200152 info := TErrorInfo.WinHTTPConnection;
153 connect := session.Connect( url.HostName, url.Port);
Jens Geyer02230912019-04-03 01:12:51 +0200154
Jens Geyer19fdca82019-06-12 22:09:05 +0200155 info := TErrorInfo.WinHTTPRequest;
156 sPath := url.UrlPath + url.ExtraInfo;
157 result := connect.OpenRequest( (url.Scheme = 'https'), 'POST', sPath, THRIFT_MIMETYPE);
Jens Geyer02230912019-04-03 01:12:51 +0200158
Jens Geyer19fdca82019-06-12 22:09:05 +0200159 // setting a timeout value to 0 (zero) means "no timeout" for that setting
160 info := TErrorInfo.RequestSetup;
161 result.SetTimeouts( DnsResolveTimeout, ConnectionTimeout, SendTimeout, ReadTimeout);
Jens Geyer02230912019-04-03 01:12:51 +0200162
Jens Geyer19fdca82019-06-12 22:09:05 +0200163 // headers
164 result.AddRequestHeader( 'Content-Type: '+THRIFT_MIMETYPE, WINHTTP_ADDREQ_FLAG_ADD);
165 for pair in FCustomHeaders do begin
166 Result.AddRequestHeader( pair.Key +': '+ pair.Value, WINHTTP_ADDREQ_FLAG_ADD);
167 end;
168
169 // AutoProxy support
170 info := TErrorInfo.AutoProxy;
171 result.TryAutoProxy( FUri);
172 except
173 on e:TException do raise;
174 on e:Exception do raise TTransportExceptionUnknown.Create( e.Message+' (at '+EnumUtils<TErrorInfo>.ToString(Ord(info))+')');
Jens Geyer02230912019-04-03 01:12:51 +0200175 end;
176end;
177
Jens Geyer47f63172019-06-06 22:42:58 +0200178
179function TWinHTTPClientImpl.SecureProtocolsAsWinHTTPFlags : Cardinal;
180const
181 PROTOCOL_MAPPING : array[TSecureProtocol] of Cardinal = (
182 WINHTTP_FLAG_SECURE_PROTOCOL_SSL2,
183 WINHTTP_FLAG_SECURE_PROTOCOL_SSL3,
184 WINHTTP_FLAG_SECURE_PROTOCOL_TLS1,
185 WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1,
186 WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2
187 );
188var
189 prot : TSecureProtocol;
190 protos : TSecureProtocols;
191begin
192 result := 0;
193 protos := GetSecureProtocols;
194 for prot := Low(TSecureProtocol) to High(TSecureProtocol) do begin
195 if prot in protos
196 then result := result or PROTOCOL_MAPPING[prot];
197 end;
198end;
199
200
Jens Geyer02230912019-04-03 01:12:51 +0200201function TWinHTTPClientImpl.GetDnsResolveTimeout: Integer;
202begin
203 Result := FDnsResolveTimeout;
204end;
205
206procedure TWinHTTPClientImpl.SetDnsResolveTimeout(const Value: Integer);
207begin
208 FDnsResolveTimeout := Value;
209end;
210
211function TWinHTTPClientImpl.GetConnectionTimeout: Integer;
212begin
213 Result := FConnectionTimeout;
214end;
215
216procedure TWinHTTPClientImpl.SetConnectionTimeout(const Value: Integer);
217begin
218 FConnectionTimeout := Value;
219end;
220
221function TWinHTTPClientImpl.GetSendTimeout: Integer;
222begin
223 Result := FSendTimeout;
224end;
225
226procedure TWinHTTPClientImpl.SetSendTimeout(const Value: Integer);
227begin
228 FSendTimeout := Value;
229end;
230
231function TWinHTTPClientImpl.GetReadTimeout: Integer;
232begin
233 Result := FReadTimeout;
234end;
235
236procedure TWinHTTPClientImpl.SetReadTimeout(const Value: Integer);
237begin
238 FReadTimeout := Value;
239end;
240
Jens Geyer47f63172019-06-06 22:42:58 +0200241function TWinHTTPClientImpl.GetSecureProtocols : TSecureProtocols;
242begin
243 Result := FSecureProtocols;
244end;
245
246procedure TWinHTTPClientImpl.SetSecureProtocols( const value : TSecureProtocols);
247begin
248 FSecureProtocols := Value;
249end;
250
Jens Geyer02230912019-04-03 01:12:51 +0200251function TWinHTTPClientImpl.GetCustomHeaders: IThriftDictionary<string,string>;
252begin
253 Result := FCustomHeaders;
254end;
255
256function TWinHTTPClientImpl.GetIsOpen: Boolean;
257begin
258 Result := True;
259end;
260
261procedure TWinHTTPClientImpl.Open;
262begin
263 FreeAndNil( FOutputMemoryStream);
264 FOutputMemoryStream := TMemoryStream.Create;
265end;
266
267procedure TWinHTTPClientImpl.Close;
268begin
269 FInputStream := nil;
270 FreeAndNil( FOutputMemoryStream);
271end;
272
273procedure TWinHTTPClientImpl.Flush;
274begin
275 try
276 SendRequest;
277 finally
278 FreeAndNil( FOutputMemoryStream);
279 FOutputMemoryStream := TMemoryStream.Create;
280 ASSERT( FOutputMemoryStream <> nil);
281 end;
282end;
283
284function TWinHTTPClientImpl.Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer;
285begin
286 if FInputStream = nil then begin
287 raise TTransportExceptionNotOpen.Create('No request has been sent');
288 end;
289
290 try
291 Result := FInputStream.Read( pBuf, buflen, off, len)
292 except
293 on E: Exception
294 do raise TTransportExceptionUnknown.Create(E.Message);
295 end;
296end;
297
298procedure TWinHTTPClientImpl.SendRequest;
299var
300 http : IWinHTTPRequest;
301 pData : PByte;
302 len : Integer;
303begin
304 http := CreateRequest;
305
306 pData := FOutputMemoryStream.Memory;
307 len := FOutputMemoryStream.Size;
308
309 // send all data immediately, since we have it in memory
310 if not http.SendRequest( pData, len, 0)
Jens Geyer83ff7532019-06-06 22:46:03 +0200311 then raise TTransportExceptionUnknown.Create('send request error '+IntToStr(GetLastError));
Jens Geyer02230912019-04-03 01:12:51 +0200312
313 // end request and start receiving
314 if not http.FlushAndReceiveResponse
Jens Geyer83ff7532019-06-06 22:46:03 +0200315 then raise TTransportExceptionInterrupted.Create('flush/receive error '+IntToStr(GetLastError));
Jens Geyer02230912019-04-03 01:12:51 +0200316
317 FInputStream := THTTPResponseStream.Create(http);
318end;
319
320procedure TWinHTTPClientImpl.Write( const pBuf : Pointer; off, len : Integer);
321var pTmp : PByte;
322begin
323 pTmp := pBuf;
324 Inc(pTmp,off);
325 FOutputMemoryStream.Write( pTmp^, len);
326end;
327
328
329{ TWinHTTPClientImpl.THTTPResponseStream }
330
331constructor TWinHTTPClientImpl.THTTPResponseStream.Create( const aRequest : IWinHTTPRequest);
332begin
333 inherited Create;
334 FRequest := aRequest;
335end;
336
337destructor TWinHTTPClientImpl.THTTPResponseStream.Destroy;
338begin
339 try
340 Close;
341 finally
342 inherited Destroy;
343 end;
344end;
345
346procedure TWinHTTPClientImpl.THTTPResponseStream.Close;
347begin
348 FRequest := nil;
349end;
350
351procedure TWinHTTPClientImpl.THTTPResponseStream.Flush;
352begin
353 raise ENotImplemented(ClassName+'.Flush');
354end;
355
356function TWinHTTPClientImpl.THTTPResponseStream.IsOpen: Boolean;
357begin
358 Result := FRequest <> nil;
359end;
360
361procedure TWinHTTPClientImpl.THTTPResponseStream.Open;
362begin
363 // nothing to do
364end;
365
366procedure TWinHTTPClientImpl.THTTPResponseStream.Write(const pBuf : Pointer; offset, count: Integer);
367begin
368 inherited; // check pointers
369 raise ENotImplemented(ClassName+'.Write');
370end;
371
372function TWinHTTPClientImpl.THTTPResponseStream.Read(const pBuf : Pointer; const buflen : Integer; offset, count: Integer): Integer;
373var pTmp : PByte;
374begin
375 inherited; // check pointers
376
377 if count >= buflen-offset
378 then count := buflen-offset;
379
380 if count > 0 then begin
381 pTmp := pBuf;
382 Inc( pTmp, offset);
383 Result := FRequest.ReadData( pTmp, count);
384 ASSERT( Result >= 0);
385 end
386 else Result := 0;
387end;
388
389function TWinHTTPClientImpl.THTTPResponseStream.ToArray: TBytes;
390begin
391 raise ENotImplemented(ClassName+'.ToArray');
392end;
393
394
395end.