blob: 8b4a7bc8362c13194388c5229e1d97011d5922d7 [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
142 session := TWinHTTPSessionImpl.Create('Apache Thrift Delphi Client');
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;
148 result := connect.OpenRequest( (url.Scheme = 'https'), 'POST', sPath, 'application/x-thrift');
149
150 // setting a timeout value to 0 (zero) means "no timeout" for that setting
151 result.SetTimeouts( DnsResolveTimeout, ConnectionTimeout, SendTimeout, ReadTimeout);
152
153 result.AddRequestHeader( 'Content-Type: application/x-thrift', WINHTTP_ADDREQ_FLAG_ADD);
154
155 for pair in FCustomHeaders do begin
156 Result.AddRequestHeader( pair.Key +': '+ pair.Value, WINHTTP_ADDREQ_FLAG_ADD);
157 end;
158end;
159
Jens Geyer47f63172019-06-06 22:42:58 +0200160
161function TWinHTTPClientImpl.SecureProtocolsAsWinHTTPFlags : Cardinal;
162const
163 PROTOCOL_MAPPING : array[TSecureProtocol] of Cardinal = (
164 WINHTTP_FLAG_SECURE_PROTOCOL_SSL2,
165 WINHTTP_FLAG_SECURE_PROTOCOL_SSL3,
166 WINHTTP_FLAG_SECURE_PROTOCOL_TLS1,
167 WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1,
168 WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2
169 );
170var
171 prot : TSecureProtocol;
172 protos : TSecureProtocols;
173begin
174 result := 0;
175 protos := GetSecureProtocols;
176 for prot := Low(TSecureProtocol) to High(TSecureProtocol) do begin
177 if prot in protos
178 then result := result or PROTOCOL_MAPPING[prot];
179 end;
180end;
181
182
Jens Geyer02230912019-04-03 01:12:51 +0200183function TWinHTTPClientImpl.GetDnsResolveTimeout: Integer;
184begin
185 Result := FDnsResolveTimeout;
186end;
187
188procedure TWinHTTPClientImpl.SetDnsResolveTimeout(const Value: Integer);
189begin
190 FDnsResolveTimeout := Value;
191end;
192
193function TWinHTTPClientImpl.GetConnectionTimeout: Integer;
194begin
195 Result := FConnectionTimeout;
196end;
197
198procedure TWinHTTPClientImpl.SetConnectionTimeout(const Value: Integer);
199begin
200 FConnectionTimeout := Value;
201end;
202
203function TWinHTTPClientImpl.GetSendTimeout: Integer;
204begin
205 Result := FSendTimeout;
206end;
207
208procedure TWinHTTPClientImpl.SetSendTimeout(const Value: Integer);
209begin
210 FSendTimeout := Value;
211end;
212
213function TWinHTTPClientImpl.GetReadTimeout: Integer;
214begin
215 Result := FReadTimeout;
216end;
217
218procedure TWinHTTPClientImpl.SetReadTimeout(const Value: Integer);
219begin
220 FReadTimeout := Value;
221end;
222
Jens Geyer47f63172019-06-06 22:42:58 +0200223function TWinHTTPClientImpl.GetSecureProtocols : TSecureProtocols;
224begin
225 Result := FSecureProtocols;
226end;
227
228procedure TWinHTTPClientImpl.SetSecureProtocols( const value : TSecureProtocols);
229begin
230 FSecureProtocols := Value;
231end;
232
Jens Geyer02230912019-04-03 01:12:51 +0200233function TWinHTTPClientImpl.GetCustomHeaders: IThriftDictionary<string,string>;
234begin
235 Result := FCustomHeaders;
236end;
237
238function TWinHTTPClientImpl.GetIsOpen: Boolean;
239begin
240 Result := True;
241end;
242
243procedure TWinHTTPClientImpl.Open;
244begin
245 FreeAndNil( FOutputMemoryStream);
246 FOutputMemoryStream := TMemoryStream.Create;
247end;
248
249procedure TWinHTTPClientImpl.Close;
250begin
251 FInputStream := nil;
252 FreeAndNil( FOutputMemoryStream);
253end;
254
255procedure TWinHTTPClientImpl.Flush;
256begin
257 try
258 SendRequest;
259 finally
260 FreeAndNil( FOutputMemoryStream);
261 FOutputMemoryStream := TMemoryStream.Create;
262 ASSERT( FOutputMemoryStream <> nil);
263 end;
264end;
265
266function TWinHTTPClientImpl.Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer;
267begin
268 if FInputStream = nil then begin
269 raise TTransportExceptionNotOpen.Create('No request has been sent');
270 end;
271
272 try
273 Result := FInputStream.Read( pBuf, buflen, off, len)
274 except
275 on E: Exception
276 do raise TTransportExceptionUnknown.Create(E.Message);
277 end;
278end;
279
280procedure TWinHTTPClientImpl.SendRequest;
281var
282 http : IWinHTTPRequest;
283 pData : PByte;
284 len : Integer;
285begin
286 http := CreateRequest;
287
288 pData := FOutputMemoryStream.Memory;
289 len := FOutputMemoryStream.Size;
290
291 // send all data immediately, since we have it in memory
292 if not http.SendRequest( pData, len, 0)
293 then raise TTransportExceptionUnknown.Create('send request error');
294
295 // end request and start receiving
296 if not http.FlushAndReceiveResponse
297 then raise TTransportExceptionInterrupted.Create('flush/receive error');
298
299 FInputStream := THTTPResponseStream.Create(http);
300end;
301
302procedure TWinHTTPClientImpl.Write( const pBuf : Pointer; off, len : Integer);
303var pTmp : PByte;
304begin
305 pTmp := pBuf;
306 Inc(pTmp,off);
307 FOutputMemoryStream.Write( pTmp^, len);
308end;
309
310
311{ TWinHTTPClientImpl.THTTPResponseStream }
312
313constructor TWinHTTPClientImpl.THTTPResponseStream.Create( const aRequest : IWinHTTPRequest);
314begin
315 inherited Create;
316 FRequest := aRequest;
317end;
318
319destructor TWinHTTPClientImpl.THTTPResponseStream.Destroy;
320begin
321 try
322 Close;
323 finally
324 inherited Destroy;
325 end;
326end;
327
328procedure TWinHTTPClientImpl.THTTPResponseStream.Close;
329begin
330 FRequest := nil;
331end;
332
333procedure TWinHTTPClientImpl.THTTPResponseStream.Flush;
334begin
335 raise ENotImplemented(ClassName+'.Flush');
336end;
337
338function TWinHTTPClientImpl.THTTPResponseStream.IsOpen: Boolean;
339begin
340 Result := FRequest <> nil;
341end;
342
343procedure TWinHTTPClientImpl.THTTPResponseStream.Open;
344begin
345 // nothing to do
346end;
347
348procedure TWinHTTPClientImpl.THTTPResponseStream.Write(const pBuf : Pointer; offset, count: Integer);
349begin
350 inherited; // check pointers
351 raise ENotImplemented(ClassName+'.Write');
352end;
353
354function TWinHTTPClientImpl.THTTPResponseStream.Read(const pBuf : Pointer; const buflen : Integer; offset, count: Integer): Integer;
355var pTmp : PByte;
356begin
357 inherited; // check pointers
358
359 if count >= buflen-offset
360 then count := buflen-offset;
361
362 if count > 0 then begin
363 pTmp := pBuf;
364 Inc( pTmp, offset);
365 Result := FRequest.ReadData( pTmp, count);
366 ASSERT( Result >= 0);
367 end
368 else Result := 0;
369end;
370
371function TWinHTTPClientImpl.THTTPResponseStream.ToArray: TBytes;
372begin
373 raise ENotImplemented(ClassName+'.ToArray');
374end;
375
376
377end.