blob: 48b74a6648fe99bb5603d25f0ac0379c70a7389d [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
54 private type
55 THTTPResponseStream = class( TThriftStreamImpl)
56 private
57 FRequest : IWinHTTPRequest;
58 protected
59 procedure Write( const pBuf : Pointer; offset: Integer; count: Integer); override;
60 function Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; override;
61 procedure Open; override;
62 procedure Close; override;
63 procedure Flush; override;
64 function IsOpen: Boolean; override;
65 function ToArray: TBytes; override;
66 public
67 constructor Create( const aRequest : IWinHTTPRequest);
68 destructor Destroy; override;
69 end;
70
71 protected
72 function GetIsOpen: Boolean; override;
73 procedure Open(); override;
74 procedure Close(); override;
75 function Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; override;
76 procedure Write( const pBuf : Pointer; off, len : Integer); override;
77 procedure Flush; override;
78
79 procedure SetDnsResolveTimeout(const Value: Integer);
80 function GetDnsResolveTimeout: Integer;
81 procedure SetConnectionTimeout(const Value: Integer);
82 function GetConnectionTimeout: Integer;
83 procedure SetSendTimeout(const Value: Integer);
84 function GetSendTimeout: Integer;
85 procedure SetReadTimeout(const Value: Integer);
86 function GetReadTimeout: Integer;
Jens Geyer47f63172019-06-06 22:42:58 +020087 function GetSecureProtocols : TSecureProtocols;
88 procedure SetSecureProtocols( const value : TSecureProtocols);
Jens Geyer02230912019-04-03 01:12:51 +020089
90 function GetCustomHeaders: IThriftDictionary<string,string>;
91 procedure SendRequest;
Jens Geyer47f63172019-06-06 22:42:58 +020092
Jens Geyer02230912019-04-03 01:12:51 +020093 property DnsResolveTimeout: Integer read GetDnsResolveTimeout write SetDnsResolveTimeout;
94 property ConnectionTimeout: Integer read GetConnectionTimeout write SetConnectionTimeout;
95 property SendTimeout: Integer read GetSendTimeout write SetSendTimeout;
96 property ReadTimeout: Integer read GetReadTimeout write SetReadTimeout;
97 property CustomHeaders: IThriftDictionary<string,string> read GetCustomHeaders;
98 public
99 constructor Create( const AUri: string);
100 destructor Destroy; override;
101 end;
102
103implementation
104
105
106{ TWinHTTPClientImpl }
107
108constructor TWinHTTPClientImpl.Create(const AUri: string);
109begin
110 inherited Create;
111 FUri := AUri;
112
113 // defaults according to MSDN
114 FDnsResolveTimeout := 0; // no timeout
115 FConnectionTimeout := 60 * 1000;
116 FSendTimeout := 30 * 1000;
117 FReadTimeout := 30 * 1000;
118
Jens Geyer47f63172019-06-06 22:42:58 +0200119 FSecureProtocols := DEFAULT_THRIFT_SECUREPROTOCOLS;
120
Jens Geyer02230912019-04-03 01:12:51 +0200121 FCustomHeaders := TThriftDictionaryImpl<string,string>.Create;
122 FOutputMemoryStream := TMemoryStream.Create;
123end;
124
125destructor TWinHTTPClientImpl.Destroy;
126begin
127 Close;
128 FreeAndNil( FOutputMemoryStream);
129 inherited;
130end;
131
132function TWinHTTPClientImpl.CreateRequest: IWinHTTPRequest;
133var
134 pair : TPair<string,string>;
135 session : IWinHTTPSession;
136 connect : IWinHTTPConnection;
137 url : IWinHTTPUrl;
138 sPath : string;
139begin
140 url := TWinHTTPUrlImpl.Create( FUri);
141
Jens Geyer83ff7532019-06-06 22:46:03 +0200142 session := TWinHTTPSessionImpl.Create('Apache Thrift Delphi WinHTTP');
Jens Geyer47f63172019-06-06 22:42:58 +0200143 session.EnableSecureProtocols( SecureProtocolsAsWinHTTPFlags);
144
Jens Geyer02230912019-04-03 01:12:51 +0200145 connect := session.Connect( url.HostName, url.Port);
146
147 sPath := url.UrlPath + url.ExtraInfo;
Jens Geyer83ff7532019-06-06 22:46:03 +0200148 result := connect.OpenRequest( (url.Scheme = 'https'), 'POST', sPath, THRIFT_MIMETYPE);
Jens Geyer02230912019-04-03 01:12:51 +0200149
150 // setting a timeout value to 0 (zero) means "no timeout" for that setting
151 result.SetTimeouts( DnsResolveTimeout, ConnectionTimeout, SendTimeout, ReadTimeout);
152
Jens Geyer83ff7532019-06-06 22:46:03 +0200153 // headers
154 result.AddRequestHeader( 'Content-Type: '+THRIFT_MIMETYPE, WINHTTP_ADDREQ_FLAG_ADD);
Jens Geyer02230912019-04-03 01:12:51 +0200155 for pair in FCustomHeaders do begin
156 Result.AddRequestHeader( pair.Key +': '+ pair.Value, WINHTTP_ADDREQ_FLAG_ADD);
157 end;
Jens Geyer83ff7532019-06-06 22:46:03 +0200158
159 // AutoProxy support
160 result.TryAutoProxy( FUri);
Jens Geyer02230912019-04-03 01:12:51 +0200161end;
162
Jens Geyer47f63172019-06-06 22:42:58 +0200163
164function TWinHTTPClientImpl.SecureProtocolsAsWinHTTPFlags : Cardinal;
165const
166 PROTOCOL_MAPPING : array[TSecureProtocol] of Cardinal = (
167 WINHTTP_FLAG_SECURE_PROTOCOL_SSL2,
168 WINHTTP_FLAG_SECURE_PROTOCOL_SSL3,
169 WINHTTP_FLAG_SECURE_PROTOCOL_TLS1,
170 WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1,
171 WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2
172 );
173var
174 prot : TSecureProtocol;
175 protos : TSecureProtocols;
176begin
177 result := 0;
178 protos := GetSecureProtocols;
179 for prot := Low(TSecureProtocol) to High(TSecureProtocol) do begin
180 if prot in protos
181 then result := result or PROTOCOL_MAPPING[prot];
182 end;
183end;
184
185
Jens Geyer02230912019-04-03 01:12:51 +0200186function TWinHTTPClientImpl.GetDnsResolveTimeout: Integer;
187begin
188 Result := FDnsResolveTimeout;
189end;
190
191procedure TWinHTTPClientImpl.SetDnsResolveTimeout(const Value: Integer);
192begin
193 FDnsResolveTimeout := Value;
194end;
195
196function TWinHTTPClientImpl.GetConnectionTimeout: Integer;
197begin
198 Result := FConnectionTimeout;
199end;
200
201procedure TWinHTTPClientImpl.SetConnectionTimeout(const Value: Integer);
202begin
203 FConnectionTimeout := Value;
204end;
205
206function TWinHTTPClientImpl.GetSendTimeout: Integer;
207begin
208 Result := FSendTimeout;
209end;
210
211procedure TWinHTTPClientImpl.SetSendTimeout(const Value: Integer);
212begin
213 FSendTimeout := Value;
214end;
215
216function TWinHTTPClientImpl.GetReadTimeout: Integer;
217begin
218 Result := FReadTimeout;
219end;
220
221procedure TWinHTTPClientImpl.SetReadTimeout(const Value: Integer);
222begin
223 FReadTimeout := Value;
224end;
225
Jens Geyer47f63172019-06-06 22:42:58 +0200226function TWinHTTPClientImpl.GetSecureProtocols : TSecureProtocols;
227begin
228 Result := FSecureProtocols;
229end;
230
231procedure TWinHTTPClientImpl.SetSecureProtocols( const value : TSecureProtocols);
232begin
233 FSecureProtocols := Value;
234end;
235
Jens Geyer02230912019-04-03 01:12:51 +0200236function TWinHTTPClientImpl.GetCustomHeaders: IThriftDictionary<string,string>;
237begin
238 Result := FCustomHeaders;
239end;
240
241function TWinHTTPClientImpl.GetIsOpen: Boolean;
242begin
243 Result := True;
244end;
245
246procedure TWinHTTPClientImpl.Open;
247begin
248 FreeAndNil( FOutputMemoryStream);
249 FOutputMemoryStream := TMemoryStream.Create;
250end;
251
252procedure TWinHTTPClientImpl.Close;
253begin
254 FInputStream := nil;
255 FreeAndNil( FOutputMemoryStream);
256end;
257
258procedure TWinHTTPClientImpl.Flush;
259begin
260 try
261 SendRequest;
262 finally
263 FreeAndNil( FOutputMemoryStream);
264 FOutputMemoryStream := TMemoryStream.Create;
265 ASSERT( FOutputMemoryStream <> nil);
266 end;
267end;
268
269function TWinHTTPClientImpl.Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer;
270begin
271 if FInputStream = nil then begin
272 raise TTransportExceptionNotOpen.Create('No request has been sent');
273 end;
274
275 try
276 Result := FInputStream.Read( pBuf, buflen, off, len)
277 except
278 on E: Exception
279 do raise TTransportExceptionUnknown.Create(E.Message);
280 end;
281end;
282
283procedure TWinHTTPClientImpl.SendRequest;
284var
285 http : IWinHTTPRequest;
286 pData : PByte;
287 len : Integer;
288begin
289 http := CreateRequest;
290
291 pData := FOutputMemoryStream.Memory;
292 len := FOutputMemoryStream.Size;
293
294 // send all data immediately, since we have it in memory
295 if not http.SendRequest( pData, len, 0)
Jens Geyer83ff7532019-06-06 22:46:03 +0200296 then raise TTransportExceptionUnknown.Create('send request error '+IntToStr(GetLastError));
Jens Geyer02230912019-04-03 01:12:51 +0200297
298 // end request and start receiving
299 if not http.FlushAndReceiveResponse
Jens Geyer83ff7532019-06-06 22:46:03 +0200300 then raise TTransportExceptionInterrupted.Create('flush/receive error '+IntToStr(GetLastError));
Jens Geyer02230912019-04-03 01:12:51 +0200301
302 FInputStream := THTTPResponseStream.Create(http);
303end;
304
305procedure TWinHTTPClientImpl.Write( const pBuf : Pointer; off, len : Integer);
306var pTmp : PByte;
307begin
308 pTmp := pBuf;
309 Inc(pTmp,off);
310 FOutputMemoryStream.Write( pTmp^, len);
311end;
312
313
314{ TWinHTTPClientImpl.THTTPResponseStream }
315
316constructor TWinHTTPClientImpl.THTTPResponseStream.Create( const aRequest : IWinHTTPRequest);
317begin
318 inherited Create;
319 FRequest := aRequest;
320end;
321
322destructor TWinHTTPClientImpl.THTTPResponseStream.Destroy;
323begin
324 try
325 Close;
326 finally
327 inherited Destroy;
328 end;
329end;
330
331procedure TWinHTTPClientImpl.THTTPResponseStream.Close;
332begin
333 FRequest := nil;
334end;
335
336procedure TWinHTTPClientImpl.THTTPResponseStream.Flush;
337begin
338 raise ENotImplemented(ClassName+'.Flush');
339end;
340
341function TWinHTTPClientImpl.THTTPResponseStream.IsOpen: Boolean;
342begin
343 Result := FRequest <> nil;
344end;
345
346procedure TWinHTTPClientImpl.THTTPResponseStream.Open;
347begin
348 // nothing to do
349end;
350
351procedure TWinHTTPClientImpl.THTTPResponseStream.Write(const pBuf : Pointer; offset, count: Integer);
352begin
353 inherited; // check pointers
354 raise ENotImplemented(ClassName+'.Write');
355end;
356
357function TWinHTTPClientImpl.THTTPResponseStream.Read(const pBuf : Pointer; const buflen : Integer; offset, count: Integer): Integer;
358var pTmp : PByte;
359begin
360 inherited; // check pointers
361
362 if count >= buflen-offset
363 then count := buflen-offset;
364
365 if count > 0 then begin
366 pTmp := pBuf;
367 Inc( pTmp, offset);
368 Result := FRequest.ReadData( pTmp, count);
369 ASSERT( Result >= 0);
370 end
371 else Result := 0;
372end;
373
374function TWinHTTPClientImpl.THTTPResponseStream.ToArray: TBytes;
375begin
376 raise ENotImplemented(ClassName+'.ToArray');
377end;
378
379
380end.