mORMot and Open Source friends
Check-in [51f977e95f]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:added SendTimeout and ReceiveTimeout optional parameters (in ms) to TWinHttpAPI constructors (instead of published properties which were not taken in account by the API) and to TSQLHttpClientWinHTTP / TSQLHttpClientWinINet constructors - fix for feature request [bfe485b678]
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 51f977e95f427b74bdd27987c1aca606c58e79f9
User & Date: User 2014-03-31 07:09:34
Context
2014-03-31
07:33
added TTimeLogBits.FromUnixTime/FromUnixMSTime/ToUnixTime/ToUnixMSTime methods check-in: 58d6fdcc44 user: User tags: trunk
07:09
added SendTimeout and ReceiveTimeout optional parameters (in ms) to TWinHttpAPI constructors (instead of published properties which were not taken in account by the API) and to TSQLHttpClientWinHTTP / TSQLHttpClientWinINet constructors - fix for feature request [bfe485b678] check-in: 51f977e95f user: User tags: trunk
2014-03-30
13:31
enhanced TTimeLogBits for enhanced speed, and silent failure when value is invalid check-in: c05f0fa20a user: User tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to SQLite3/mORMotHttpClient.pas.

101
102
103
104
105
106
107



108
109
110
111
112
113
114
...
202
203
204
205
206
207
208

209
210
211
212
213
214
215
216
217
218
219

220

221
222
223
224
225
226
227
228
229

230
231
232
233
234
235





236
237
238


239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
...
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
...
413
414
415
416
417
418
419
420

421
422
423
424
425


426
427
428
429
430
431
432
433
434



435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
...
470
471
472
473
474
475
476
477
478
479

480
481
482
483
484
485
486
487

488
489
490
491
492
	   - classes TSQLite3HttpClient* renamed as TSQLHttpClient*
     - fixed TSQLHttpClientGeneric.InternalURI() method to raise an explicit
       exception on connection error (as expected by TSQLRestClientURI.URI)
     - TSQLHttpClient* classes will now handle properly reconnection in case
       of connection break via overriden InternalCheckOpen/InternalClose methods
     - introducing TSQLHttpClientGeneric.Compression property to set the handled
       compression schemes at runtime, i.e. SynLZ, deflate or SynLZ+SHA/AES




}

interface

{.$define USETCPPREFIX}
{ if defined, a prefix will be added to the TCP/IP stream so that it won't be
................................................................................
      var Header, Data, DataType: RawUTF8): Int64Rec; override;
    /// overriden protected method to handle HTTP connection
    function InternalCheckOpen: boolean; override;
    /// overriden protected method to close HTTP connection
    procedure InternalClose; override;
  public
    /// internal HTTP/1.1 compatible client

    property Socket: THttpClientSocket read fSocket;
  end;

{$ifdef USEWININET}
  /// HTTP/1.1 RESTFUL JSON mORMot Client abstract class using either WinINet
  // either TWinHTTP API
  // - not to be called directly, but via TSQLHttpClientWinINet or (even
  // better) TSQLHttpClientWinHTTP overriden classes
  TSQLHttpClientWinGeneric = class(TSQLHttpClientGeneric)
  protected
    fWinAPI: TWinHttpAPI;

    fProxyName, fProxyByPass: AnsiString;

    fHttps: boolean;
    /// call fWinAPI.Request()
    function InternalRequest(const url, method: RawUTF8;
      var Header, Data, DataType: RawUTF8): Int64Rec; override;
    /// overriden protected method to close HTTP connection
    procedure InternalClose; override;
    /// overriden protected method to handle HTTP connection
    function InternalCheckOpen: boolean; override;
    /// create a fWinAPI instance if needed

    procedure InternalCreate; virtual; abstract;
  public
    /// connect to TSQLHttpServer on aServer:aPort
    // - optional aProxyName may contain the name of the proxy server to use,
    // and aProxyByPass an optional semicolon delimited list of host names or
    // IP addresses, or both, that should not be routed through the proxy





    constructor Create(const aServer, aPort: AnsiString; aModel: TSQLModel;
      aHttps: boolean=false; const aProxyName: AnsiString='';
      const aProxyByPass: AnsiString=''); reintroduce;


    /// internal class instance used for the connection
    // - will return either a TWinINet, either a TWinHTTP class instance
    // - you can use this property to access to the low-level Timeout values,
    // for instance
    property WinAPI: TWinHttpAPI read fWinAPI;
  end;

  /// HTTP/1.1 RESTFUL JSON mORMot Client class using WinINet API
  // - this class is 15/20 times slower than TSQLHttpClient using SynCrtSock
  // on a local machine, but was found to be faster throughout local networks
  // - this class is able to connect via the secure HTTPS protocol
  // - it will retrieve by default the Internet Explorer proxy settings, and 
  // display some error messages or authentification dialog on screen
  // - you can optionaly specify manual Proxy settings at constructor level
  // - by design, the WinINet API should not be used from a service
  // - is implemented by creating a TWinINet internal class instance
  TSQLHttpClientWinINet = class(TSQLHttpClientWinGeneric)
  protected
    procedure InternalCreate; override;
  end;

  {{ HTTP/1.1 RESTFUL JSON Client class using WinHTTP API
   - has a common behavior as THttpClientSocket() but seems to be faster
     over a network and is able to retrieve the current proxy settings
     (if available) and handle secure HTTPS connection - so it seems to be used
     in your client programs: TSQLHttpClient will therefore map to this class
   - WinHTTP does not share directly any proxy settings with Internet Explorer.
................................................................................
     Vista/Seven, to configure applications using the 32 bit WinHttp settings,
     call netsh or proxycfg bits from %SystemRoot%\SysWOW64 folder explicitely)
   - you can optionaly specify manual Proxy settings at constructor level
   - by design, the WinHTTP API can be used from a service or a server
   - is implemented by creating a TWinHTTP internal class instance }
  TSQLHttpClientWinHTTP = class(TSQLHttpClientWinGeneric)
  protected
    procedure InternalCreate; override;
  end;

  /// HTTP/1.1 RESTFUL JSON default mORMot Client class
  // - under Windows, maps the TSQLHttpClientWinHTTP class 
  TSQLHttpClient = TSQLHttpClientWinHTTP;
{$else}
  /// HTTP/1.1 RESTFUL JSON deault mORMot Client class
................................................................................


{$ifdef USEWININET}

{ TSQLHttpClientWinGeneric }

constructor TSQLHttpClientWinGeneric.Create(const aServer, aPort: AnsiString;
  aModel: TSQLModel; aHttps: boolean; const aProxyName, aProxyByPass: AnsiString);

begin
  inherited Create(aServer,aPort,aModel);
  fHttps := aHttps;
  fProxyName := aProxyName;
  fProxyByPass := aProxyByPass;


end;

function TSQLHttpClientWinGeneric.InternalCheckOpen: boolean;
begin
  result := false;
  if fWinAPI=nil then
  try
    InternalCreate;
    if fWinAPI<>nil then begin



      // note that first registered algo will be the prefered one
      if hcSynShaAes in Compression then
        // global SHA-256 / AES-256-CTR encryption + SynLZ compression
        fWinAPI.RegisterCompress(CompressShaAes,0); // CompressMinSize=0
      if hcSynLz in Compression then
        // SynLZ is very fast and efficient, perfect for a Delphi Client
        fWinAPI.RegisterCompress(CompressSynLZ);
      if hcDeflate in Compression then
        // standard (slower) AJAX/HTTP zip/deflate compression
        fWinAPI.RegisterCompress(CompressDeflate);
      result := true;
    end;
  except
    on Exception do
      fWinAPI := nil;
  end else
    result := true;
end;

procedure TSQLHttpClientWinGeneric.InternalClose;
begin
  FreeAndNil(fWinAPI);
................................................................................
    Data := OutData;
  end;
end;


{ TSQLHttpClientWinINet }

procedure TSQLHttpClientWinINet.InternalCreate;
begin
  fWinAPI := TWinINet.Create(fServer,fPort,fHttps,fProxyName,fProxyByPass);

end;


{ TSQLHttpClientWinHTTP }

procedure TSQLHttpClientWinHTTP.InternalCreate;
begin
  fWinAPI := TWinHTTP.Create(fServer,fPort,fHttps,fProxyName,fProxyByPass);

end;

{$endif}

end.






>
>
>







 







>





|





>

>








|
>
|





>
>
>
>
>


|
>
>


<
<







|






|
|







 







|







 







|
>





>
>







|
|
>
>
>
|
|
|
|
|
|
|
|
|
|
|
<


|







 







|

|
>





|

|
>





101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
...
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254


255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
...
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
...
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463

464
465
466
467
468
469
470
471
472
473
...
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
	   - classes TSQLite3HttpClient* renamed as TSQLHttpClient*
     - fixed TSQLHttpClientGeneric.InternalURI() method to raise an explicit
       exception on connection error (as expected by TSQLRestClientURI.URI)
     - TSQLHttpClient* classes will now handle properly reconnection in case
       of connection break via overriden InternalCheckOpen/InternalClose methods
     - introducing TSQLHttpClientGeneric.Compression property to set the handled
       compression schemes at runtime, i.e. SynLZ, deflate or SynLZ+SHA/AES
     - added SendTimeout and ReceiveTimeout optional parameters (in ms) to
       TSQLHttpClientWinHTTP / TSQLHttpClientWinINet constructors [bfe485b678]
        

}

interface

{.$define USETCPPREFIX}
{ if defined, a prefix will be added to the TCP/IP stream so that it won't be
................................................................................
      var Header, Data, DataType: RawUTF8): Int64Rec; override;
    /// overriden protected method to handle HTTP connection
    function InternalCheckOpen: boolean; override;
    /// overriden protected method to close HTTP connection
    procedure InternalClose; override;
  public
    /// internal HTTP/1.1 compatible client
    // - can be used e.g. to access SendTimeout and ReceiveTimeout properties
    property Socket: THttpClientSocket read fSocket;
  end;

{$ifdef USEWININET}
  /// HTTP/1.1 RESTFUL JSON mORMot Client abstract class using either WinINet
  // or WinHTTP API
  // - not to be called directly, but via TSQLHttpClientWinINet or (even
  // better) TSQLHttpClientWinHTTP overriden classes
  TSQLHttpClientWinGeneric = class(TSQLHttpClientGeneric)
  protected
    fWinAPI: TWinHttpAPI;
    fWinAPIClass: TWinHttpAPIClass;
    fProxyName, fProxyByPass: AnsiString;
    fSendTimeout, fReceiveTimeout: DWORD;
    fHttps: boolean;
    /// call fWinAPI.Request()
    function InternalRequest(const url, method: RawUTF8;
      var Header, Data, DataType: RawUTF8): Int64Rec; override;
    /// overriden protected method to close HTTP connection
    procedure InternalClose; override;
    /// overriden protected method to handle HTTP connection
    function InternalCheckOpen: boolean; override;
    /// set the fWinAPI class
    // - the overriden implementation should set the expected fWinAPIClass 
    procedure InternalSetClass; virtual; abstract;
  public
    /// connect to TSQLHttpServer on aServer:aPort
    // - optional aProxyName may contain the name of the proxy server to use,
    // and aProxyByPass an optional semicolon delimited list of host names or
    // IP addresses, or both, that should not be routed through the proxy
    // - you can customize the default client timeouts by setting appropriate
    // SendTimeout and ReceiveTimeout parameters (in ms) - note that after
    // creation of this instance, the connection is tied to those initial
    // parameters, so we won't publish any properties to change those
    // initial values once created
    constructor Create(const aServer, aPort: AnsiString; aModel: TSQLModel;
      aHttps: boolean=false; const aProxyName: AnsiString='';
      const aProxyByPass: AnsiString='';
      SendTimeout: DWORD=HTTP_DEFAULT_SENDTIMEOUT;
      ReceiveTimeout: DWORD=HTTP_DEFAULT_RECEIVETIMEOUT); reintroduce;
    /// internal class instance used for the connection
    // - will return either a TWinINet, either a TWinHTTP class instance


    property WinAPI: TWinHttpAPI read fWinAPI;
  end;

  /// HTTP/1.1 RESTFUL JSON mORMot Client class using WinINet API
  // - this class is 15/20 times slower than TSQLHttpClient using SynCrtSock
  // on a local machine, but was found to be faster throughout local networks
  // - this class is able to connect via the secure HTTPS protocol
  // - it will retrieve by default the Internet Explorer proxy settings, and
  // display some error messages or authentification dialog on screen
  // - you can optionaly specify manual Proxy settings at constructor level
  // - by design, the WinINet API should not be used from a service
  // - is implemented by creating a TWinINet internal class instance
  TSQLHttpClientWinINet = class(TSQLHttpClientWinGeneric)
  protected
    procedure InternalSetClass; override;
  end;                

  {{ HTTP/1.1 RESTFUL JSON Client class using WinHTTP API
   - has a common behavior as THttpClientSocket() but seems to be faster
     over a network and is able to retrieve the current proxy settings
     (if available) and handle secure HTTPS connection - so it seems to be used
     in your client programs: TSQLHttpClient will therefore map to this class
   - WinHTTP does not share directly any proxy settings with Internet Explorer.
................................................................................
     Vista/Seven, to configure applications using the 32 bit WinHttp settings,
     call netsh or proxycfg bits from %SystemRoot%\SysWOW64 folder explicitely)
   - you can optionaly specify manual Proxy settings at constructor level
   - by design, the WinHTTP API can be used from a service or a server
   - is implemented by creating a TWinHTTP internal class instance }
  TSQLHttpClientWinHTTP = class(TSQLHttpClientWinGeneric)
  protected
    procedure InternalSetClass; override;
  end;

  /// HTTP/1.1 RESTFUL JSON default mORMot Client class
  // - under Windows, maps the TSQLHttpClientWinHTTP class 
  TSQLHttpClient = TSQLHttpClientWinHTTP;
{$else}
  /// HTTP/1.1 RESTFUL JSON deault mORMot Client class
................................................................................


{$ifdef USEWININET}

{ TSQLHttpClientWinGeneric }

constructor TSQLHttpClientWinGeneric.Create(const aServer, aPort: AnsiString;
  aModel: TSQLModel; aHttps: boolean; const aProxyName, aProxyByPass: AnsiString;
  SendTimeout,ReceiveTimeout: DWORD);
begin
  inherited Create(aServer,aPort,aModel);
  fHttps := aHttps;
  fProxyName := aProxyName;
  fProxyByPass := aProxyByPass;
  fSendTimeout := SendTimeout;
  fReceiveTimeout := ReceiveTimeout;
end;

function TSQLHttpClientWinGeneric.InternalCheckOpen: boolean;
begin
  result := false;
  if fWinAPI=nil then
  try
    InternalSetClass;
    if fWinAPIClass=nil then
      raise ECommunicationException.CreateFmt('fWinAPIClass=nil for %s',[ClassName]);
    fWinAPI := fWinAPIClass.Create(fServer,fPort,fHttps,fProxyName,fProxyByPass,
      fSendTimeout,fReceiveTimeout);
    // note that first registered algo will be the prefered one
    if hcSynShaAes in Compression then
      // global SHA-256 / AES-256-CTR encryption + SynLZ compression
      fWinAPI.RegisterCompress(CompressShaAes,0); // CompressMinSize=0
    if hcSynLz in Compression then
      // SynLZ is very fast and efficient, perfect for a Delphi Client
      fWinAPI.RegisterCompress(CompressSynLZ);
    if hcDeflate in Compression then
      // standard (slower) AJAX/HTTP zip/deflate compression
      fWinAPI.RegisterCompress(CompressDeflate);
    result := true;

  except
    on Exception do
      FreeAndNil(fWinAPI);
  end else
    result := true;
end;

procedure TSQLHttpClientWinGeneric.InternalClose;
begin
  FreeAndNil(fWinAPI);
................................................................................
    Data := OutData;
  end;
end;


{ TSQLHttpClientWinINet }

procedure TSQLHttpClientWinINet.InternalSetClass;
begin
  fWinAPIClass := TWinINet;
  inherited;
end;


{ TSQLHttpClientWinHTTP }

procedure TSQLHttpClientWinHTTP.InternalSetClass;
begin
  fWinAPIClass := TWinHTTP;
  inherited;
end;

{$endif}

end.

Changes to SynCrtSock.pas.

149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
...
221
222
223
224
225
226
227
























228
229
230
231
232
233
234
....
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080





1081
1082


1083
1084
1085
1086
1087
1088
1089
....
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
....
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
....
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
....
5284
5285
5286
5287
5288
5289
5290
5291
5292
5293
5294
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
5312
5313
....
5486
5487
5488
5489
5490
5491
5492
5493
5494
5495
5496
5497
5498
5499
5500
5501
5502




5503
5504
5505
5506
5507
5508
5509
....
5536
5537
5538
5539
5540
5541
5542
5543
5544
5545
5546
5547
5548
5549
5550
5551
5552
5553
5554
5555
....
5620
5621
5622
5623
5624
5625
5626
5627
5628
5629
5630
5631
5632
5633
5634
5635




5636
5637
5638
5639
5640
5641
5642
....
5675
5676
5677
5678
5679
5680
5681
5682
5683
5684
5685
5686
5687
5688
5689
5690
5691
  - WinSock-based THttpServer will avoid creating a thread per connection,
    when the maximum of 64 threads is reached in the pool, with an exception
    of kept-alife or huge body requets (avoiding DoS attacks by limiting the
    total number of created threads)
  - let WinSock-based THttpServer.Process() handle HTTP_RESP_STATICFILE
  - force disable range checking and other compiler options for this unit
  - included more detailed information to HTTP client User-Agent header
  - added ResolveTimeout/ConnectTimeout/SendTimeout/ReceiveTimeout properties
    to TWinHttpAPI classes - feature request [bfe485b678]
  - added optional aCompressMinSize parameter to RegisterCompress() methods
  - added TWinHttpAPI.Get/Post/Put/Delete() class functions for easy remote
    resource retrieval using either WinHTTP or WinINet APIs
  - added TURI structure, ready to parse a supplied HTTP URI
  - added 'ConnectionID: 1234578' to the HTTP headers - request [0636eeec54]
  - fixed ticket [82df275784] TWinHttpAPI with responses without Content-Length
  - fixed ticket [f0749956af] TWinINet does not work with HTTPS servers
................................................................................
{$endif}
  SysUtils,
  Classes;

const
  /// the corresponding version of the freeware Synopse framework 
  SYNOPSE_FRAMEWORK_VERSION = '1.18'{$ifdef LVCL}+' LVCL'{$endif};

























type
{$ifndef UNICODE}
  /// define RawByteString, as it does exist in Delphi 2009 and up
  // - to be used for byte storage into an AnsiString
  RawByteString = AnsiString;
{$endif}
................................................................................
    fCompress: THttpSocketCompressRecDynArray;
    /// set by RegisterCompress method
    fCompressAcceptEncoding: RawByteString;
    /// set index of protocol in fCompress[], from ACCEPT-ENCODING: header
    fCompressHeader: THttpSocketCompressSet;
    /// used for internal connection
    fSession, fConnection, fRequest: HINTERNET;
    fResolveTimeout: DWORD;
    fConnectTimeout: DWORD;
    fSendTimeout: DWORD;
    fReceiveTimeout: DWORD;
    procedure InternalConnect; virtual; abstract;
    procedure InternalRequest(const method, aURL: RawByteString); virtual; abstract;
    procedure InternalCloseRequest; virtual; abstract;
    procedure InternalAddHeader(const hdr: RawByteString); virtual; abstract;
    procedure InternalSendRequest(const aData: RawByteString); virtual; abstract;
    function InternalGetInfo(Info: DWORD): RawByteString; virtual; abstract;
    function InternalGetInfo32(Info: DWORD): DWORD; virtual; abstract;
    function InternalReadData(var Data: RawByteString; Read: integer): cardinal; virtual; abstract;
    class function InternalREST(const url,method,data,header: RawByteString): RawByteString;
  public
    /// connect to http://aServer:aPort or https://aServer:aPort
    // - optional aProxyName may contain the name of the proxy server to use,
    // and aProxyByPass an optional semicolon delimited list of host names or
    // IP addresses, or both, that should not be routed through the proxy





    constructor Create(const aServer, aPort: RawByteString; aHttps: boolean;
      const aProxyName: RawByteString=''; const aProxyByPass: RawByteString='');



    /// low-level HTTP/1.1 request
    // - after an Create(server,port), return 200,202,204 if OK,
    // http status error otherwize
    function Request(const url, method: RawByteString; KeepAlive: cardinal;
      const InHeader, InData, InDataType: RawByteString;
      out OutHeader, OutData: RawByteString): integer; virtual;
................................................................................
    /// if the remote server uses HTTPS, as specified to the class constructor
    property Https: boolean read fHttps;
    /// the remote server optional proxy, as specified to the class constructor
    property ProxyName: RawByteString read fProxyName;
    /// the remote server optional proxy by-pass list, as specified to the class
    // constructor
    property ProxyByPass: RawByteString read fProxyByPass;
    /// time-out time, in milliseconds, to use for name resolution
    // - not implemented with TWinINet class
    property ResolveTimeout: DWORD read fResolveTimeout write fResolveTimeout;
    /// time-out time, in milliseconds, to use for server connection requests
    property ConnectTimeout: DWORD read fConnectTimeout write fConnectTimeout;
    /// time-out time, in milliseconds, to use for sending requests
    property SendTimeout: DWORD read fSendTimeout write fSendTimeout;
    /// time-out time, in milliseconds, to receive a response to a request
    property ReceiveTimeout: DWORD read fReceiveTimeout write fReceiveTimeout;
  end;

  {/ a class to handle HTTP/1.1 request using the WinINet API
   - has a common behavior as THttpClientSocket()
   - The Microsoft Windows Internet (WinINet) application programming interface
     (API) enables applications to access standard Internet protocols, such as
     FTP and HTTP/HTTPS.
   - by design, the WinINet API should not be used from a service
   - note: WinINet is MUCH slower than THttpClientSocket: do not use this, only
     if you find some performance improvements on some networks }
  TWinINet = class(TWinHttpAPI)
  protected
    // those internal methods will raise an EWinINet exception on error
    procedure InternalConnect; override;
    procedure InternalRequest(const method, aURL: RawByteString); override;
    procedure InternalCloseRequest; override;
    procedure InternalAddHeader(const hdr: RawByteString); override;
    procedure InternalSendRequest(const aData: RawByteString); override;
    function InternalGetInfo(Info: DWORD): RawByteString; override;
    function InternalGetInfo32(Info: DWORD): DWORD; override;
    function InternalReadData(var Data: RawByteString; Read: integer): cardinal; override;
................................................................................
     call netsh or proxycfg bits from %SystemRoot%\SysWOW64 folder explicitely)
   - Microsoft Windows HTTP Services (WinHTTP) is targeted at middle-tier and
     back-end server applications that require access to an HTTP client stack }
  TWinHTTP = class(TWinHttpAPI)
  private
  protected
    // those internal methods will raise an EOSError exception on error
    procedure InternalConnect; override;
    procedure InternalRequest(const method, aURL: RawByteString); override;
    procedure InternalCloseRequest; override;
    procedure InternalAddHeader(const hdr: RawByteString); override;
    procedure InternalSendRequest(const aData: RawByteString); override;
    function InternalGetInfo(Info: DWORD): RawByteString; override;
    function InternalGetInfo32(Info: DWORD): DWORD; override;
    function InternalReadData(var Data: RawByteString; Read: integer): cardinal; override;
................................................................................
  TWinHttpAPIClass = class of TWinHttpAPI;

  /// WinHTTP exception type
  EWinHTTP = class(Exception);

{$endif}

const
  /// used by THttpApiServer.Request for http.sys to send a static file
  // - the OutCustomHeader should contain the proper 'Content-type: ....'
  // corresponding to the file (e.g. by calling GetMimeContentType() function
  // from SynCommons supplyings the file name)
  // - should match HTML_CONTENT_STATICFILE constant defined in mORMot.pas unit
  HTTP_RESP_STATICFILE = '!STATICFILE';

  // some timeout default values
  HTTP_DEFAULT_RESOLVETIMEOUT = 0;
  HTTP_DEFAULT_CONNECTTIMEOUT = 60000;
  HTTP_DEFAULT_SENDTIMEOUT = 30000;
  HTTP_DEFAULT_RECEIVETIMEOUT = 30000;


/// create a TCrtSocket, returning nil on error
// (usefull to easily catch socket error exception ECrtSocket)
function Open(const aServer, aPort: RawByteString): TCrtSocket;

/// create a THttpClientSocket, returning nil on error
// (usefull to easily catch socket error exception ECrtSocket)
................................................................................
{ ************ WinHttp / WinINet HTTP clients }

{$ifdef USEWININET}

{ TWinHttpAPI }

constructor TWinHttpAPI.Create(const aServer, aPort: RawByteString; aHttps: boolean;
  const aProxyName: RawByteString=''; const aProxyByPass: RawByteString='');
begin
  fPort := GetCardinal(pointer(aPort));
  if fPort=0 then
    if aHttps then
      fPort := INTERNET_DEFAULT_HTTPS_PORT else
      fPort := INTERNET_DEFAULT_HTTP_PORT;
  fServer := aServer;
  fHttps := aHttps;
  fProxyName := aProxyName;
  fProxyByPass := aProxyByPass;
  fResolveTimeout := HTTP_DEFAULT_RESOLVETIMEOUT;
  fConnectTimeout := HTTP_DEFAULT_CONNECTTIMEOUT;
  fSendTimeout := HTTP_DEFAULT_SENDTIMEOUT;
  fReceiveTimeout := HTTP_DEFAULT_RECEIVETIMEOUT;
  InternalConnect; // should raise an exception on error
end;

function TWinHttpAPI.RegisterCompress(aFunction: THttpSocketCompress;
  aCompressMinSize: integer): boolean;
begin
  result := RegisterCompressFunc(fCompress,aFunction,fCompressAcceptEncoding,aCompressMinSize)<>'';
end;
................................................................................
begin
  if fRequest<>nil then begin
    InternetCloseHandle(fRequest);
    fRequest := nil;
  end;
end;

procedure TWinINet.InternalConnect;
var OpenType: integer;
begin
  if fProxyName='' then
   OpenType := INTERNET_OPEN_TYPE_PRECONFIG else
   OpenType := INTERNET_OPEN_TYPE_PROXY;
  fSession := InternetOpenA(Pointer(DefaultUserAgent(self)), OpenType,
    pointer(fProxyName), pointer(fProxyByPass), 0);
  if fSession=nil then
    raise EWinINet.Create;




  fConnection := InternetConnectA(fSession, pointer(fServer), fPort, nil, nil,
    INTERNET_SERVICE_HTTP, 0, 0);
  if fConnection=nil then
    raise EWinINet.Create;
end;

function TWinINet.InternalGetInfo(Info: DWORD): RawByteString;
................................................................................
    raise EWinINet.Create;
end;

procedure TWinINet.InternalRequest(const method, aURL: RawByteString);
const ALL_ACCEPT: array[0..1] of PAnsiChar = ('*/*',nil);
var Flags: DWORD;
begin
  InternetSetOption(fConnection,INTERNET_OPTION_CONNECT_TIMEOUT,
    @fConnectTimeout,SizeOf(fConnectTimeout));
  InternetSetOption(fConnection,INTERNET_OPTION_SEND_TIMEOUT,
    @fSendTimeout,SizeOf(fSendTimeout));
  InternetSetOption(fConnection,INTERNET_OPTION_RECEIVE_TIMEOUT,
    @fReceiveTimeout,SizeOf(fReceiveTimeout));
  Flags := INTERNET_FLAG_HYPERLINK or INTERNET_FLAG_PRAGMA_NOCACHE or
    INTERNET_FLAG_RESYNCHRONIZE; // options for a true RESTful request
  if fKeepAlive<>0 then
    Flags := Flags or INTERNET_FLAG_KEEP_CONNECTION;
  if fHttps then
    Flags := Flags or INTERNET_FLAG_SECURE;
  FRequest := HttpOpenRequestA(FConnection, Pointer(method), Pointer(aURL), nil,
................................................................................
begin
  if fRequest<>nil then begin
    WinHttpCloseHandle(fRequest);
    FRequest := nil;
  end;
end;

procedure TWinHTTP.InternalConnect;
var OpenType: integer;
begin
  if fProxyName='' then
    OpenType := WINHTTP_ACCESS_TYPE_DEFAULT_PROXY else
    OpenType := WINHTTP_ACCESS_TYPE_NAMED_PROXY;
  fSession := WinHttpOpen(pointer(Ansi7ToUnicode(DefaultUserAgent(self))), OpenType,
    pointer(Ansi7ToUnicode(fProxyName)), pointer(Ansi7ToUnicode(fProxyByPass)), 0);
  if fSession=nil then




    RaiseLastOSError;
  fConnection := WinHttpConnect(fSession, pointer(Ansi7ToUnicode(FServer)), fPort, 0);
  if fConnection=nil then
    RaiseLastOSError;
end;

function TWinHTTP.InternalGetInfo(Info: DWORD): RawByteString;
................................................................................
    RaiseLastOSError;
end;

procedure TWinHTTP.InternalRequest(const method, aURL: RawByteString);
const ALL_ACCEPT: array[0..1] of PWideChar = ('*/*',nil);
var Flags: DWORD;
begin
  // cf. http://msdn.microsoft.com/en-us/library/windows/desktop/aa384116
  if not WinHttpSetTimeouts(fSession,fResolveTimeout,fConnectTimeout,fSendTimeout,fReceiveTimeout) then
    RaiseLastOSError;
  Flags := WINHTTP_FLAG_REFRESH; // options for a true RESTful request
  if fHttps then
    Flags := Flags or WINHTTP_FLAG_SECURE;
  fRequest := WinHttpOpenRequest(fConnection, pointer(Ansi7ToUnicode(method)),
    pointer(Ansi7ToUnicode(aURL)), nil, nil, @ALL_ACCEPT, Flags);
  if fRequest=nil then
    RaiseLastOSError;






|
|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







<
<
<
|
<













>
>
>
>
>

|
>
>







 







<
<
<
<
<
<
<
<
<













|







 







|







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







|










<
<
<
<
|







 







|









>
>
>
>







 







<
<
<
<
<
<







 







|








>
>
>
>







 







<
<
<







149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
...
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
....
1080
1081
1082
1083
1084
1085
1086



1087

1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
....
1165
1166
1167
1168
1169
1170
1171









1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
....
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
....
1242
1243
1244
1245
1246
1247
1248














1249
1250
1251
1252
1253
1254
1255
....
5288
5289
5290
5291
5292
5293
5294
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305




5306
5307
5308
5309
5310
5311
5312
5313
....
5486
5487
5488
5489
5490
5491
5492
5493
5494
5495
5496
5497
5498
5499
5500
5501
5502
5503
5504
5505
5506
5507
5508
5509
5510
5511
5512
5513
....
5540
5541
5542
5543
5544
5545
5546






5547
5548
5549
5550
5551
5552
5553
....
5618
5619
5620
5621
5622
5623
5624
5625
5626
5627
5628
5629
5630
5631
5632
5633
5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
5644
....
5677
5678
5679
5680
5681
5682
5683



5684
5685
5686
5687
5688
5689
5690
  - WinSock-based THttpServer will avoid creating a thread per connection,
    when the maximum of 64 threads is reached in the pool, with an exception
    of kept-alife or huge body requets (avoiding DoS attacks by limiting the
    total number of created threads)
  - let WinSock-based THttpServer.Process() handle HTTP_RESP_STATICFILE
  - force disable range checking and other compiler options for this unit
  - included more detailed information to HTTP client User-Agent header
  - added SendTimeout and ReceiveTimeout optional parameters to TWinHttpAPI
    constructors - feature request [bfe485b678]
  - added optional aCompressMinSize parameter to RegisterCompress() methods
  - added TWinHttpAPI.Get/Post/Put/Delete() class functions for easy remote
    resource retrieval using either WinHTTP or WinINet APIs
  - added TURI structure, ready to parse a supplied HTTP URI
  - added 'ConnectionID: 1234578' to the HTTP headers - request [0636eeec54]
  - fixed ticket [82df275784] TWinHttpAPI with responses without Content-Length
  - fixed ticket [f0749956af] TWinINet does not work with HTTPS servers
................................................................................
{$endif}
  SysUtils,
  Classes;

const
  /// the corresponding version of the freeware Synopse framework 
  SYNOPSE_FRAMEWORK_VERSION = '1.18'{$ifdef LVCL}+' LVCL'{$endif};

  /// used by THttpApiServer.Request for http.sys to send a static file
  // - the OutCustomHeader should contain the proper 'Content-type: ....'
  // corresponding to the file (e.g. by calling GetMimeContentType() function
  // from SynCommons supplyings the file name)
  // - should match HTML_CONTENT_STATICFILE constant defined in mORMot.pas unit
  HTTP_RESP_STATICFILE = '!STATICFILE';

  /// TWinHttpAPI timeout default value for DNS resolution
  // - leaving to 0 will let system default value be used
  HTTP_DEFAULT_RESOLVETIMEOUT = 0;
  /// TWinHttpAPI timeout default value for remote connection
  // - default is 60 seconds
  HTTP_DEFAULT_CONNECTTIMEOUT = 60000;
  /// TWinHttpAPI timeout default value for data sending
  // - default is 30 seconds
  // - you can override this value by setting the corresponding parameter in
  // TWinHttpAPI.Create() constructor
  HTTP_DEFAULT_SENDTIMEOUT = 30000;
  /// TWinHttpAPI timeout default value for data receiving
  // - default is 30 seconds
  // - you can override this value by setting the corresponding parameter in
  // TWinHttpAPI.Create() constructor
  HTTP_DEFAULT_RECEIVETIMEOUT = 30000;

type
{$ifndef UNICODE}
  /// define RawByteString, as it does exist in Delphi 2009 and up
  // - to be used for byte storage into an AnsiString
  RawByteString = AnsiString;
{$endif}
................................................................................
    fCompress: THttpSocketCompressRecDynArray;
    /// set by RegisterCompress method
    fCompressAcceptEncoding: RawByteString;
    /// set index of protocol in fCompress[], from ACCEPT-ENCODING: header
    fCompressHeader: THttpSocketCompressSet;
    /// used for internal connection
    fSession, fConnection, fRequest: HINTERNET;



    procedure InternalConnect(SendTimeout,ReceiveTimeout: DWORD); virtual; abstract;

    procedure InternalRequest(const method, aURL: RawByteString); virtual; abstract;
    procedure InternalCloseRequest; virtual; abstract;
    procedure InternalAddHeader(const hdr: RawByteString); virtual; abstract;
    procedure InternalSendRequest(const aData: RawByteString); virtual; abstract;
    function InternalGetInfo(Info: DWORD): RawByteString; virtual; abstract;
    function InternalGetInfo32(Info: DWORD): DWORD; virtual; abstract;
    function InternalReadData(var Data: RawByteString; Read: integer): cardinal; virtual; abstract;
    class function InternalREST(const url,method,data,header: RawByteString): RawByteString;
  public
    /// connect to http://aServer:aPort or https://aServer:aPort
    // - optional aProxyName may contain the name of the proxy server to use,
    // and aProxyByPass an optional semicolon delimited list of host names or
    // IP addresses, or both, that should not be routed through the proxy
    // - you can customize the default client timeouts by setting appropriate
    // SendTimeout and ReceiveTimeout parameters (in ms) - note that after
    // creation of this instance, the connection is tied to the initial
    // parameters, so we won't publish any properties to change those
    // initial values once created
    constructor Create(const aServer, aPort: RawByteString; aHttps: boolean;
      const aProxyName: RawByteString=''; const aProxyByPass: RawByteString='';
      SendTimeout: DWORD=HTTP_DEFAULT_SENDTIMEOUT;
      ReceiveTimeout: DWORD=HTTP_DEFAULT_RECEIVETIMEOUT);

    /// low-level HTTP/1.1 request
    // - after an Create(server,port), return 200,202,204 if OK,
    // http status error otherwize
    function Request(const url, method: RawByteString; KeepAlive: cardinal;
      const InHeader, InData, InDataType: RawByteString;
      out OutHeader, OutData: RawByteString): integer; virtual;
................................................................................
    /// if the remote server uses HTTPS, as specified to the class constructor
    property Https: boolean read fHttps;
    /// the remote server optional proxy, as specified to the class constructor
    property ProxyName: RawByteString read fProxyName;
    /// the remote server optional proxy by-pass list, as specified to the class
    // constructor
    property ProxyByPass: RawByteString read fProxyByPass;









  end;

  {/ a class to handle HTTP/1.1 request using the WinINet API
   - has a common behavior as THttpClientSocket()
   - The Microsoft Windows Internet (WinINet) application programming interface
     (API) enables applications to access standard Internet protocols, such as
     FTP and HTTP/HTTPS.
   - by design, the WinINet API should not be used from a service
   - note: WinINet is MUCH slower than THttpClientSocket: do not use this, only
     if you find some performance improvements on some networks }
  TWinINet = class(TWinHttpAPI)
  protected
    // those internal methods will raise an EWinINet exception on error
    procedure InternalConnect(SendTimeout,ReceiveTimeout: DWORD); override;
    procedure InternalRequest(const method, aURL: RawByteString); override;
    procedure InternalCloseRequest; override;
    procedure InternalAddHeader(const hdr: RawByteString); override;
    procedure InternalSendRequest(const aData: RawByteString); override;
    function InternalGetInfo(Info: DWORD): RawByteString; override;
    function InternalGetInfo32(Info: DWORD): DWORD; override;
    function InternalReadData(var Data: RawByteString; Read: integer): cardinal; override;
................................................................................
     call netsh or proxycfg bits from %SystemRoot%\SysWOW64 folder explicitely)
   - Microsoft Windows HTTP Services (WinHTTP) is targeted at middle-tier and
     back-end server applications that require access to an HTTP client stack }
  TWinHTTP = class(TWinHttpAPI)
  private
  protected
    // those internal methods will raise an EOSError exception on error
    procedure InternalConnect(SendTimeout,ReceiveTimeout: DWORD); override;
    procedure InternalRequest(const method, aURL: RawByteString); override;
    procedure InternalCloseRequest; override;
    procedure InternalAddHeader(const hdr: RawByteString); override;
    procedure InternalSendRequest(const aData: RawByteString); override;
    function InternalGetInfo(Info: DWORD): RawByteString; override;
    function InternalGetInfo32(Info: DWORD): DWORD; override;
    function InternalReadData(var Data: RawByteString; Read: integer): cardinal; override;
................................................................................
  TWinHttpAPIClass = class of TWinHttpAPI;

  /// WinHTTP exception type
  EWinHTTP = class(Exception);

{$endif}
















/// create a TCrtSocket, returning nil on error
// (usefull to easily catch socket error exception ECrtSocket)
function Open(const aServer, aPort: RawByteString): TCrtSocket;

/// create a THttpClientSocket, returning nil on error
// (usefull to easily catch socket error exception ECrtSocket)
................................................................................
{ ************ WinHttp / WinINet HTTP clients }

{$ifdef USEWININET}

{ TWinHttpAPI }

constructor TWinHttpAPI.Create(const aServer, aPort: RawByteString; aHttps: boolean;
  const aProxyName,aProxyByPass: RawByteString; SendTimeout,ReceiveTimeout: DWORD);
begin
  fPort := GetCardinal(pointer(aPort));
  if fPort=0 then
    if aHttps then
      fPort := INTERNET_DEFAULT_HTTPS_PORT else
      fPort := INTERNET_DEFAULT_HTTP_PORT;
  fServer := aServer;
  fHttps := aHttps;
  fProxyName := aProxyName;
  fProxyByPass := aProxyByPass;




  InternalConnect(SendTimeout,ReceiveTimeout); // should raise an exception on error
end;

function TWinHttpAPI.RegisterCompress(aFunction: THttpSocketCompress;
  aCompressMinSize: integer): boolean;
begin
  result := RegisterCompressFunc(fCompress,aFunction,fCompressAcceptEncoding,aCompressMinSize)<>'';
end;
................................................................................
begin
  if fRequest<>nil then begin
    InternetCloseHandle(fRequest);
    fRequest := nil;
  end;
end;

procedure TWinINet.InternalConnect(SendTimeout,ReceiveTimeout: DWORD);
var OpenType: integer;
begin
  if fProxyName='' then
   OpenType := INTERNET_OPEN_TYPE_PRECONFIG else
   OpenType := INTERNET_OPEN_TYPE_PROXY;
  fSession := InternetOpenA(Pointer(DefaultUserAgent(self)), OpenType,
    pointer(fProxyName), pointer(fProxyByPass), 0);
  if fSession=nil then
    raise EWinINet.Create;
  InternetSetOption(fConnection,INTERNET_OPTION_SEND_TIMEOUT,
    @SendTimeout,SizeOf(SendTimeout));
  InternetSetOption(fConnection,INTERNET_OPTION_RECEIVE_TIMEOUT,
    @ReceiveTimeout,SizeOf(ReceiveTimeout));
  fConnection := InternetConnectA(fSession, pointer(fServer), fPort, nil, nil,
    INTERNET_SERVICE_HTTP, 0, 0);
  if fConnection=nil then
    raise EWinINet.Create;
end;

function TWinINet.InternalGetInfo(Info: DWORD): RawByteString;
................................................................................
    raise EWinINet.Create;
end;

procedure TWinINet.InternalRequest(const method, aURL: RawByteString);
const ALL_ACCEPT: array[0..1] of PAnsiChar = ('*/*',nil);
var Flags: DWORD;
begin






  Flags := INTERNET_FLAG_HYPERLINK or INTERNET_FLAG_PRAGMA_NOCACHE or
    INTERNET_FLAG_RESYNCHRONIZE; // options for a true RESTful request
  if fKeepAlive<>0 then
    Flags := Flags or INTERNET_FLAG_KEEP_CONNECTION;
  if fHttps then
    Flags := Flags or INTERNET_FLAG_SECURE;
  FRequest := HttpOpenRequestA(FConnection, Pointer(method), Pointer(aURL), nil,
................................................................................
begin
  if fRequest<>nil then begin
    WinHttpCloseHandle(fRequest);
    FRequest := nil;
  end;
end;

procedure TWinHTTP.InternalConnect(SendTimeout,ReceiveTimeout: DWORD);
var OpenType: integer;
begin
  if fProxyName='' then
    OpenType := WINHTTP_ACCESS_TYPE_DEFAULT_PROXY else
    OpenType := WINHTTP_ACCESS_TYPE_NAMED_PROXY;
  fSession := WinHttpOpen(pointer(Ansi7ToUnicode(DefaultUserAgent(self))), OpenType,
    pointer(Ansi7ToUnicode(fProxyName)), pointer(Ansi7ToUnicode(fProxyByPass)), 0);
  if fSession=nil then
    RaiseLastOSError;
  // cf. http://msdn.microsoft.com/en-us/library/windows/desktop/aa384116
  if not WinHttpSetTimeouts(fSession,HTTP_DEFAULT_RESOLVETIMEOUT,
     HTTP_DEFAULT_CONNECTTIMEOUT,SendTimeout,ReceiveTimeout) then
    RaiseLastOSError;
  fConnection := WinHttpConnect(fSession, pointer(Ansi7ToUnicode(FServer)), fPort, 0);
  if fConnection=nil then
    RaiseLastOSError;
end;

function TWinHTTP.InternalGetInfo(Info: DWORD): RawByteString;
................................................................................
    RaiseLastOSError;
end;

procedure TWinHTTP.InternalRequest(const method, aURL: RawByteString);
const ALL_ACCEPT: array[0..1] of PWideChar = ('*/*',nil);
var Flags: DWORD;
begin



  Flags := WINHTTP_FLAG_REFRESH; // options for a true RESTful request
  if fHttps then
    Flags := Flags or WINHTTP_FLAG_SECURE;
  fRequest := WinHttpOpenRequest(fConnection, pointer(Ansi7ToUnicode(method)),
    pointer(Ansi7ToUnicode(aURL)), nil, nil, @ALL_ACCEPT, Flags);
  if fRequest=nil then
    RaiseLastOSError;