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
10 * http://www.apache.org/licenses/LICENSE-2.0
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
19 unit Thrift.Transport.WinHTTP;
21 {$I Thrift.Defines.inc}
39 TWinHTTPClientImpl = class( TTransportImpl, IHTTPClient)
42 FInputStream : IThriftStream;
43 FOutputMemoryStream : TMemoryStream;
44 FDnsResolveTimeout : Integer;
45 FConnectionTimeout : Integer;
46 FSendTimeout : Integer;
47 FReadTimeout : Integer;
48 FCustomHeaders : IThriftDictionary<string,string>;
49 FSecureProtocols : TSecureProtocols;
51 function CreateRequest: IWinHTTPRequest;
52 function SecureProtocolsAsWinHTTPFlags : Cardinal;
56 TErrorInfo = ( SplitUrl, WinHTTPSession, WinHTTPConnection, WinHTTPRequest, RequestSetup, AutoProxy );
58 THTTPResponseStream = class( TThriftStreamImpl)
60 FRequest : IWinHTTPRequest;
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;
70 constructor Create( const aRequest : IWinHTTPRequest);
71 destructor Destroy; override;
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;
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;
90 function GetSecureProtocols : TSecureProtocols;
91 procedure SetSecureProtocols( const value : TSecureProtocols);
93 function GetCustomHeaders: IThriftDictionary<string,string>;
94 procedure SendRequest;
96 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;
102 constructor Create( const AUri: string);
103 destructor Destroy; override;
109 { TWinHTTPClientImpl }
111 constructor TWinHTTPClientImpl.Create(const AUri: string);
116 // defaults according to MSDN
117 FDnsResolveTimeout := 0; // no timeout
118 FConnectionTimeout := 60 * 1000;
119 FSendTimeout := 30 * 1000;
120 FReadTimeout := 30 * 1000;
122 FSecureProtocols := DEFAULT_THRIFT_SECUREPROTOCOLS;
124 FCustomHeaders := TThriftDictionaryImpl<string,string>.Create;
125 FOutputMemoryStream := TMemoryStream.Create;
128 destructor TWinHTTPClientImpl.Destroy;
131 FreeAndNil( FOutputMemoryStream);
135 function TWinHTTPClientImpl.CreateRequest: IWinHTTPRequest;
137 pair : TPair<string,string>;
138 session : IWinHTTPSession;
139 connect : IWinHTTPConnection;
144 info := TErrorInfo.SplitUrl;
146 url := TWinHTTPUrlImpl.Create( FUri);
148 info := TErrorInfo.WinHTTPSession;
149 session := TWinHTTPSessionImpl.Create('Apache Thrift Delphi WinHTTP');
150 session.EnableSecureProtocols( SecureProtocolsAsWinHTTPFlags);
152 info := TErrorInfo.WinHTTPConnection;
153 connect := session.Connect( url.HostName, url.Port);
155 info := TErrorInfo.WinHTTPRequest;
156 sPath := url.UrlPath + url.ExtraInfo;
157 result := connect.OpenRequest( (url.Scheme = 'https'), 'POST', sPath, THRIFT_MIMETYPE);
159 // setting a timeout value to 0 (zero) means "no timeout" for that setting
160 info := TErrorInfo.RequestSetup;
161 result.SetTimeouts( DnsResolveTimeout, ConnectionTimeout, SendTimeout, ReadTimeout);
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);
169 // enable automatic gzip,deflate decompression
170 result.EnableAutomaticContentDecompression(TRUE);
173 info := TErrorInfo.AutoProxy;
174 result.TryAutoProxy( FUri);
176 on e:TException do raise;
177 on e:Exception do raise TTransportExceptionUnknown.Create( e.Message+' (at '+EnumUtils<TErrorInfo>.ToString(Ord(info))+')');
182 function TWinHTTPClientImpl.SecureProtocolsAsWinHTTPFlags : Cardinal;
184 PROTOCOL_MAPPING : array[TSecureProtocol] of Cardinal = (
185 WINHTTP_FLAG_SECURE_PROTOCOL_SSL2,
186 WINHTTP_FLAG_SECURE_PROTOCOL_SSL3,
187 WINHTTP_FLAG_SECURE_PROTOCOL_TLS1,
188 WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1,
189 WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2
192 prot : TSecureProtocol;
193 protos : TSecureProtocols;
196 protos := GetSecureProtocols;
197 for prot := Low(TSecureProtocol) to High(TSecureProtocol) do begin
199 then result := result or PROTOCOL_MAPPING[prot];
204 function TWinHTTPClientImpl.GetDnsResolveTimeout: Integer;
206 Result := FDnsResolveTimeout;
209 procedure TWinHTTPClientImpl.SetDnsResolveTimeout(const Value: Integer);
211 FDnsResolveTimeout := Value;
214 function TWinHTTPClientImpl.GetConnectionTimeout: Integer;
216 Result := FConnectionTimeout;
219 procedure TWinHTTPClientImpl.SetConnectionTimeout(const Value: Integer);
221 FConnectionTimeout := Value;
224 function TWinHTTPClientImpl.GetSendTimeout: Integer;
226 Result := FSendTimeout;
229 procedure TWinHTTPClientImpl.SetSendTimeout(const Value: Integer);
231 FSendTimeout := Value;
234 function TWinHTTPClientImpl.GetReadTimeout: Integer;
236 Result := FReadTimeout;
239 procedure TWinHTTPClientImpl.SetReadTimeout(const Value: Integer);
241 FReadTimeout := Value;
244 function TWinHTTPClientImpl.GetSecureProtocols : TSecureProtocols;
246 Result := FSecureProtocols;
249 procedure TWinHTTPClientImpl.SetSecureProtocols( const value : TSecureProtocols);
251 FSecureProtocols := Value;
254 function TWinHTTPClientImpl.GetCustomHeaders: IThriftDictionary<string,string>;
256 Result := FCustomHeaders;
259 function TWinHTTPClientImpl.GetIsOpen: Boolean;
264 procedure TWinHTTPClientImpl.Open;
266 FreeAndNil( FOutputMemoryStream);
267 FOutputMemoryStream := TMemoryStream.Create;
270 procedure TWinHTTPClientImpl.Close;
273 FreeAndNil( FOutputMemoryStream);
276 procedure TWinHTTPClientImpl.Flush;
281 FreeAndNil( FOutputMemoryStream);
282 FOutputMemoryStream := TMemoryStream.Create;
283 ASSERT( FOutputMemoryStream <> nil);
287 function TWinHTTPClientImpl.Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer;
289 if FInputStream = nil then begin
290 raise TTransportExceptionNotOpen.Create('No request has been sent');
294 Result := FInputStream.Read( pBuf, buflen, off, len)
297 do raise TTransportExceptionUnknown.Create(E.Message);
301 procedure TWinHTTPClientImpl.SendRequest;
303 http : IWinHTTPRequest;
309 http := CreateRequest;
311 pData := FOutputMemoryStream.Memory;
312 len := FOutputMemoryStream.Size;
314 // send all data immediately, since we have it in memory
315 if not http.SendRequest( pData, len, 0) then begin
316 error := Cardinal( GetLastError);
317 sMsg := 'WinHTTP send error '+IntToStr(Int64(error))+' '+WinHttpSysErrorMessage(error);
318 raise TTransportExceptionUnknown.Create(sMsg);
321 // end request and start receiving
322 if not http.FlushAndReceiveResponse then begin
323 error := Cardinal( GetLastError);
324 sMsg := 'WinHTTP recv error '+IntToStr(Int64(error))+' '+WinHttpSysErrorMessage(error);
325 if error = ERROR_WINHTTP_TIMEOUT
326 then raise TTransportExceptionTimedOut.Create( sMsg)
327 else raise TTransportExceptionInterrupted.Create( sMsg);
330 FInputStream := THTTPResponseStream.Create(http);
333 procedure TWinHTTPClientImpl.Write( const pBuf : Pointer; off, len : Integer);
338 FOutputMemoryStream.Write( pTmp^, len);
342 { TWinHTTPClientImpl.THTTPResponseStream }
344 constructor TWinHTTPClientImpl.THTTPResponseStream.Create( const aRequest : IWinHTTPRequest);
347 FRequest := aRequest;
350 destructor TWinHTTPClientImpl.THTTPResponseStream.Destroy;
359 procedure TWinHTTPClientImpl.THTTPResponseStream.Close;
364 procedure TWinHTTPClientImpl.THTTPResponseStream.Flush;
366 raise ENotImplemented(ClassName+'.Flush');
369 function TWinHTTPClientImpl.THTTPResponseStream.IsOpen: Boolean;
371 Result := FRequest <> nil;
374 procedure TWinHTTPClientImpl.THTTPResponseStream.Open;
379 procedure TWinHTTPClientImpl.THTTPResponseStream.Write(const pBuf : Pointer; offset, count: Integer);
381 inherited; // check pointers
382 raise ENotImplemented(ClassName+'.Write');
385 function TWinHTTPClientImpl.THTTPResponseStream.Read(const pBuf : Pointer; const buflen : Integer; offset, count: Integer): Integer;
388 inherited; // check pointers
390 if count >= buflen-offset
391 then count := buflen-offset;
393 if count > 0 then begin
396 Result := FRequest.ReadData( pTmp, count);
397 ASSERT( Result >= 0);
402 function TWinHTTPClientImpl.THTTPResponseStream.ToArray: TBytes;
404 raise ENotImplemented(ClassName+'.ToArray');