blob: aac2aeaf2982f623920ca108d77bc88e5d8c6abe [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>;
49
50 function CreateRequest: IWinHTTPRequest;
51
52 private type
53 THTTPResponseStream = class( TThriftStreamImpl)
54 private
55 FRequest : IWinHTTPRequest;
56 protected
57 procedure Write( const pBuf : Pointer; offset: Integer; count: Integer); override;
58 function Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; override;
59 procedure Open; override;
60 procedure Close; override;
61 procedure Flush; override;
62 function IsOpen: Boolean; override;
63 function ToArray: TBytes; override;
64 public
65 constructor Create( const aRequest : IWinHTTPRequest);
66 destructor Destroy; override;
67 end;
68
69 protected
70 function GetIsOpen: Boolean; override;
71 procedure Open(); override;
72 procedure Close(); override;
73 function Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; override;
74 procedure Write( const pBuf : Pointer; off, len : Integer); override;
75 procedure Flush; override;
76
77 procedure SetDnsResolveTimeout(const Value: Integer);
78 function GetDnsResolveTimeout: Integer;
79 procedure SetConnectionTimeout(const Value: Integer);
80 function GetConnectionTimeout: Integer;
81 procedure SetSendTimeout(const Value: Integer);
82 function GetSendTimeout: Integer;
83 procedure SetReadTimeout(const Value: Integer);
84 function GetReadTimeout: Integer;
85
86 function GetCustomHeaders: IThriftDictionary<string,string>;
87 procedure SendRequest;
88 property DnsResolveTimeout: Integer read GetDnsResolveTimeout write SetDnsResolveTimeout;
89 property ConnectionTimeout: Integer read GetConnectionTimeout write SetConnectionTimeout;
90 property SendTimeout: Integer read GetSendTimeout write SetSendTimeout;
91 property ReadTimeout: Integer read GetReadTimeout write SetReadTimeout;
92 property CustomHeaders: IThriftDictionary<string,string> read GetCustomHeaders;
93 public
94 constructor Create( const AUri: string);
95 destructor Destroy; override;
96 end;
97
98implementation
99
100
101{ TWinHTTPClientImpl }
102
103constructor TWinHTTPClientImpl.Create(const AUri: string);
104begin
105 inherited Create;
106 FUri := AUri;
107
108 // defaults according to MSDN
109 FDnsResolveTimeout := 0; // no timeout
110 FConnectionTimeout := 60 * 1000;
111 FSendTimeout := 30 * 1000;
112 FReadTimeout := 30 * 1000;
113
114 FCustomHeaders := TThriftDictionaryImpl<string,string>.Create;
115 FOutputMemoryStream := TMemoryStream.Create;
116end;
117
118destructor TWinHTTPClientImpl.Destroy;
119begin
120 Close;
121 FreeAndNil( FOutputMemoryStream);
122 inherited;
123end;
124
125function TWinHTTPClientImpl.CreateRequest: IWinHTTPRequest;
126var
127 pair : TPair<string,string>;
128 session : IWinHTTPSession;
129 connect : IWinHTTPConnection;
130 url : IWinHTTPUrl;
131 sPath : string;
132begin
133 url := TWinHTTPUrlImpl.Create( FUri);
134
135 session := TWinHTTPSessionImpl.Create('Apache Thrift Delphi Client');
136 connect := session.Connect( url.HostName, url.Port);
137
138 sPath := url.UrlPath + url.ExtraInfo;
139 result := connect.OpenRequest( (url.Scheme = 'https'), 'POST', sPath, 'application/x-thrift');
140
141 // setting a timeout value to 0 (zero) means "no timeout" for that setting
142 result.SetTimeouts( DnsResolveTimeout, ConnectionTimeout, SendTimeout, ReadTimeout);
143
144 result.AddRequestHeader( 'Content-Type: application/x-thrift', WINHTTP_ADDREQ_FLAG_ADD);
145
146 for pair in FCustomHeaders do begin
147 Result.AddRequestHeader( pair.Key +': '+ pair.Value, WINHTTP_ADDREQ_FLAG_ADD);
148 end;
149end;
150
151function TWinHTTPClientImpl.GetDnsResolveTimeout: Integer;
152begin
153 Result := FDnsResolveTimeout;
154end;
155
156procedure TWinHTTPClientImpl.SetDnsResolveTimeout(const Value: Integer);
157begin
158 FDnsResolveTimeout := Value;
159end;
160
161function TWinHTTPClientImpl.GetConnectionTimeout: Integer;
162begin
163 Result := FConnectionTimeout;
164end;
165
166procedure TWinHTTPClientImpl.SetConnectionTimeout(const Value: Integer);
167begin
168 FConnectionTimeout := Value;
169end;
170
171function TWinHTTPClientImpl.GetSendTimeout: Integer;
172begin
173 Result := FSendTimeout;
174end;
175
176procedure TWinHTTPClientImpl.SetSendTimeout(const Value: Integer);
177begin
178 FSendTimeout := Value;
179end;
180
181function TWinHTTPClientImpl.GetReadTimeout: Integer;
182begin
183 Result := FReadTimeout;
184end;
185
186procedure TWinHTTPClientImpl.SetReadTimeout(const Value: Integer);
187begin
188 FReadTimeout := Value;
189end;
190
191function TWinHTTPClientImpl.GetCustomHeaders: IThriftDictionary<string,string>;
192begin
193 Result := FCustomHeaders;
194end;
195
196function TWinHTTPClientImpl.GetIsOpen: Boolean;
197begin
198 Result := True;
199end;
200
201procedure TWinHTTPClientImpl.Open;
202begin
203 FreeAndNil( FOutputMemoryStream);
204 FOutputMemoryStream := TMemoryStream.Create;
205end;
206
207procedure TWinHTTPClientImpl.Close;
208begin
209 FInputStream := nil;
210 FreeAndNil( FOutputMemoryStream);
211end;
212
213procedure TWinHTTPClientImpl.Flush;
214begin
215 try
216 SendRequest;
217 finally
218 FreeAndNil( FOutputMemoryStream);
219 FOutputMemoryStream := TMemoryStream.Create;
220 ASSERT( FOutputMemoryStream <> nil);
221 end;
222end;
223
224function TWinHTTPClientImpl.Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer;
225begin
226 if FInputStream = nil then begin
227 raise TTransportExceptionNotOpen.Create('No request has been sent');
228 end;
229
230 try
231 Result := FInputStream.Read( pBuf, buflen, off, len)
232 except
233 on E: Exception
234 do raise TTransportExceptionUnknown.Create(E.Message);
235 end;
236end;
237
238procedure TWinHTTPClientImpl.SendRequest;
239var
240 http : IWinHTTPRequest;
241 pData : PByte;
242 len : Integer;
243begin
244 http := CreateRequest;
245
246 pData := FOutputMemoryStream.Memory;
247 len := FOutputMemoryStream.Size;
248
249 // send all data immediately, since we have it in memory
250 if not http.SendRequest( pData, len, 0)
251 then raise TTransportExceptionUnknown.Create('send request error');
252
253 // end request and start receiving
254 if not http.FlushAndReceiveResponse
255 then raise TTransportExceptionInterrupted.Create('flush/receive error');
256
257 FInputStream := THTTPResponseStream.Create(http);
258end;
259
260procedure TWinHTTPClientImpl.Write( const pBuf : Pointer; off, len : Integer);
261var pTmp : PByte;
262begin
263 pTmp := pBuf;
264 Inc(pTmp,off);
265 FOutputMemoryStream.Write( pTmp^, len);
266end;
267
268
269{ TWinHTTPClientImpl.THTTPResponseStream }
270
271constructor TWinHTTPClientImpl.THTTPResponseStream.Create( const aRequest : IWinHTTPRequest);
272begin
273 inherited Create;
274 FRequest := aRequest;
275end;
276
277destructor TWinHTTPClientImpl.THTTPResponseStream.Destroy;
278begin
279 try
280 Close;
281 finally
282 inherited Destroy;
283 end;
284end;
285
286procedure TWinHTTPClientImpl.THTTPResponseStream.Close;
287begin
288 FRequest := nil;
289end;
290
291procedure TWinHTTPClientImpl.THTTPResponseStream.Flush;
292begin
293 raise ENotImplemented(ClassName+'.Flush');
294end;
295
296function TWinHTTPClientImpl.THTTPResponseStream.IsOpen: Boolean;
297begin
298 Result := FRequest <> nil;
299end;
300
301procedure TWinHTTPClientImpl.THTTPResponseStream.Open;
302begin
303 // nothing to do
304end;
305
306procedure TWinHTTPClientImpl.THTTPResponseStream.Write(const pBuf : Pointer; offset, count: Integer);
307begin
308 inherited; // check pointers
309 raise ENotImplemented(ClassName+'.Write');
310end;
311
312function TWinHTTPClientImpl.THTTPResponseStream.Read(const pBuf : Pointer; const buflen : Integer; offset, count: Integer): Integer;
313var pTmp : PByte;
314begin
315 inherited; // check pointers
316
317 if count >= buflen-offset
318 then count := buflen-offset;
319
320 if count > 0 then begin
321 pTmp := pBuf;
322 Inc( pTmp, offset);
323 Result := FRequest.ReadData( pTmp, count);
324 ASSERT( Result >= 0);
325 end
326 else Result := 0;
327end;
328
329function TWinHTTPClientImpl.THTTPResponseStream.ToArray: TBytes;
330begin
331 raise ENotImplemented(ClassName+'.ToArray');
332end;
333
334
335end.