-
-
Notifications
You must be signed in to change notification settings - Fork 135
/
mormot.net.client.pas
5236 lines (4834 loc) · 195 KB
/
mormot.net.client.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
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
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
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
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
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
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/// HTTP/HTTPS Client Classes
// - this unit is a part of the Open Source Synopse mORMot framework 2,
// licensed under a MPL/GPL/LGPL three license - see LICENSE.md
unit mormot.net.client;
{
*****************************************************************************
HTTP Client Classes
- THttpMultiPartStream for multipart/formdata HTTP POST
- THttpClientSocket Implementing HTTP client over plain sockets
- Additional Client Protocols Support
- THttpRequest Abstract HTTP client class
- TWinHttp TWinINet TCurlHttp classes
- IHttpClient / TSimpleHttpClient Wrappers
- TJsonClient JSON requests over HTTP
- Cached HTTP Connection to a Remote Server
- Send Email using the SMTP Protocol
- DNS Resolution Cache for mormot.net.sock NewSocket()
*****************************************************************************
}
interface
{$I ..\mormot.defines.inc}
uses
sysutils,
classes,
mormot.core.base,
mormot.core.os,
mormot.core.unicode,
mormot.core.text,
mormot.core.buffers,
mormot.core.data,
mormot.core.datetime,
mormot.core.rtti,
mormot.core.json, // TSynDictionary for THttpRequestCached
mormot.core.variants,
mormot.net.sock,
mormot.net.http,
{$ifdef USEWININET} // as set in mormot.defines.inc
wininet,
mormot.lib.winhttp,
{$ifdef FORCE_OPENSSL}
mormot.lib.openssl11, // bypass SChannel for a given project
{$endif FORCE_OPENSSL}
{$endif USEWININET}
{$ifdef USELIBCURL} // as set in mormot.defines.inc
mormot.lib.curl,
{$endif USELIBCURL}
{$ifdef DOMAINRESTAUTH}
mormot.lib.sspi, // do-nothing units on non compliant systems
mormot.lib.gssapi,
{$endif DOMAINRESTAUTH}
mormot.crypt.secure;
{ ******************** THttpMultiPartStream for multipart/formdata HTTP POST }
type
/// low-level section information as stored by THttpMultiPartStream
THttpMultiPartStreamSection = record
Name: RawUtf8;
FileName: RawUtf8;
Content: RawByteString;
ContentType: RawUtf8;
ContentFile: TFileName;
end;
PHttpMultiPartStreamSection = ^THttpMultiPartStreamSection;
THttpMultiPartStreamSections = array of THttpMultiPartStreamSection;
/// a TStream descendant implementing client multipart/formdata HTTP POST
// - AddContent/AddFileContent/AddFile will append name/contents sections
// to this instance, then after Flush, send Read() data via TCP to include the
// proper multipart formatting as defined by RFC 2488 / RFC 1341
// - AddFile() won't load the file content into memory so it is more
// efficient than MultiPartFormDataEncode() from mormot.core.buffers
THttpMultiPartStream = class(TNestedStreamReader)
protected
fSections: THttpMultiPartStreamSections;
fBounds: TRawUtf8DynArray;
fBound: RawUtf8;
fMultipartContentType: RawUtf8;
fFilesCount: integer;
fRfc2388NestedFiles: boolean;
function Add(const name, content, contenttype,
filename, encoding: RawUtf8): PHttpMultiPartStreamSection;
public
/// append a content section from a binary/text buffer
// - warning: should be called before AddFile/AddFileContent
procedure AddContent(const name: RawUtf8; const content: RawByteString;
const contenttype: RawUtf8 = ''; const encoding: RawUtf8 = '');
/// append a file upload section from a binary/text buffer
// - warning: should be called after AddContent
procedure AddFileContent(const name, filename: RawUtf8;
const content: RawByteString; const contenttype: RawUtf8 = '';
const encoding: RawUtf8 = '');
/// append a file upload section from a local file
// - the supplied file won't be loaded into memory, but created as an
// internal TFileStreamEx to be retrieved by successive Read() calls
// - warning: should be called after AddContent
procedure AddFile(const name: RawUtf8; const filename: TFileName;
const contenttype: RawUtf8 = '');
/// call this method before any Read() call to sent data to HTTP server
// - it is called also when Seek(0, soBeginning) is called
procedure Flush; override;
/// the content-type header value for this multipart content
// - equals '' if no section has been added
// - includes a random boundary field
property MultipartContentType: RawUtf8
read fMultipartContentType;
/// will force the deprecated nested "multipart/mixed" format
property Rfc2388NestedFiles: boolean
read fRfc2388NestedFiles write fRfc2388NestedFiles;
/// high-level sections parameters as provided to Add* methods
// - can be used e.g. by libcurl which makes its own encoding
property Sections: THttpMultiPartStreamSections
read fSections;
/// how many AddFile/AddFileContent have been appended
property FilesCount: integer
read fFilesCount;
end;
{ ************** THttpClientSocket Implementing HTTP client over plain sockets }
{ high-level definitions, shared with both THttpRequest and THttpClientSocket }
type
/// the supported authentication schemes which may be used by HTTP clients
// - supported only by TWinHttp class yet, and TCurlHttp
THttpRequestAuthentication = (
wraNone,
wraBasic,
wraDigest,
wraNegotiate,
wraBearer);
/// pointer to some extended options for HTTP clients
PHttpRequestExtendedOptions = ^THttpRequestExtendedOptions;
/// a record to set some extended options for HTTP clients
// - allow easy propagation e.g. from a TRestHttpClient* wrapper class to
// the actual mormot.net.http's THttpRequest implementation class
{$ifdef USERECORDWITHMETHODS}
THttpRequestExtendedOptions = record
{$else}
THttpRequestExtendedOptions = object
{$endif USERECORDWITHMETHODS}
private
procedure AuthorizeUserPassword(const UserName, Password: RawUtf8;
Scheme: THttpRequestAuthentication);
public
/// allow to customize the HTTPS process
// - used only during initial connection
TLS: TNetTlsContext;
/// optional Proxy URI
// - if kept to its default '', will try to use the system PROXY
// - if set to 'none', won't use any proxy
// - otherwise, will use this value as explicit proxy server name
// - used only during initial connection
Proxy: RawUtf8;
/// the timeout to be used for the whole connection, as supplied to Create()
CreateTimeoutMS: integer;
/// allow HTTP/HTTPS authentication to take place at server request
Auth: record
Scheme: THttpRequestAuthentication;
UserName: RawUtf8;
Password: SpiUtf8;
Token: SpiUtf8;
end;
/// how many times THttpClientSocket/TWinHttp should redirect 30x responses
RedirectMax: integer;
/// allow to customize the User-Agent header
// - for TWinHttp, should be set at constructor level
UserAgent: RawUtf8;
/// may be used to initialize this record on stack
procedure Init;
/// reset this record, calling FillZero() on Password/Token SpiUtf8 values
procedure Clear;
/// setup web authentication using the Basic access algorithm
procedure AuthorizeBasic(const UserName: RawUtf8; const Password: SpiUtf8);
/// setup web authentication using the Digest access algorithm
procedure AuthorizeDigest(const UserName: RawUtf8; const Password: SpiUtf8);
/// setup web authentication using Kerberos/NTLM via SSPI/GSSAPI and credentials
// - if you want to authenticate with the current logged user, just set
// ! Auth.Scheme := wraNegotiate;
procedure AuthorizeSspiUser(const UserName: RawUtf8; const Password: SpiUtf8);
/// setup web authentication using a given Bearer in the request headers
procedure AuthorizeBearer(const Value: SpiUtf8);
/// compare the Auth fields, depending on their scheme
function SameAuth(Another: PHttpRequestExtendedOptions): boolean;
end;
function ToText(wra: THttpRequestAuthentication): PShortString; overload;
var
/// THttpRequest timeout default value for DNS resolution
// - only used by TWinHttp class - other clients will ignore it
// - leaving to 0 will let system default value be used
HTTP_DEFAULT_RESOLVETIMEOUT: integer = 0;
/// THttpRequest timeout default value for remote connection
// - default is 30 seconds
// - used e.g. by THttpClientSocket, THttpRequest, TRestHttpClientRequest and
// TRestHttpClientGeneric
HTTP_DEFAULT_CONNECTTIMEOUT: integer = 30000;
/// THttpRequest timeout default value for data sending
// - default is 30 seconds
// - used e.g. by THttpClientSocket, THttpRequest, TRestHttpClientRequest and
// TRestHttpClientGeneric
// - you can override this value by setting the corresponding parameter in
// the class constructor
HTTP_DEFAULT_SENDTIMEOUT: integer = 30000;
/// THttpRequest timeout default value for data receiving
// - default is 30 seconds
// - used e.g. by THttpClientSocket, THttpRequest, TRestHttpClientRequest and
// TRestHttpClientGeneric
// - you can override this value by setting the corresponding parameter in
// the class constructor
HTTP_DEFAULT_RECEIVETIMEOUT: integer = 30000;
const
/// standard text used to identify the WebSockets protocol
HTTP_WEBSOCKET_PROTOCOL: RawUtf8 = 'SEC-WEBSOCKET-PROTOCOL';
type
THttpClientSocket = class;
IWGetAlternate = interface;
/// available THttpClientSocketWGet.Alternate file operations
// - by default, cached file is temporary but could be kept forever on disk
// if waoPermanentCache is defined
// - waoNoHeadFirst will call OnDownload() first then fallback to GET so
// may be preferred e.g. if the main server has a huge latency
// - waoNoMinimalSize should let OnDownload() accept even the smallest files
// - waoTryLastPeer/waoTryAllPeers/waoBroadcastNotAlone will force homonymous
// pcoTryLastPeer/pcoTryAllPeers/pcoBroadcastNotAlone THttpPeerCacheOption
// - waoNoProgressiveDownloading will disable pcfResponsePartial requests
TWGetAlternateOption = (
waoPermanentCache,
waoNoHeadFirst,
waoNoMinimalSize,
waoTryLastPeer,
waoTryAllPeers,
waoBroadcastNotAlone,
waoNoProgressiveDownloading);
/// define how THttpClientSocketWGet.Alternate should operate this file
TWGetAlternateOptions = set of TWGetAlternateOption;
/// the various steps of THttpClientSocket.WGet() process
TWGetStep = (
wgsUrlFile,
wgsFileName,
wgsHashed,
wgsAlreadExisting,
wgsFromCache,
wgsResume,
wgsHead,
wgsAbortResume,
wgsFromScratch,
wgsNewStream,
wgsDeleteFile,
wgsAlternateFromCache,
wgsAlternateLastPeer,
wgsAlternateBroadcast,
wgsAlternateGet,
wgsAlternateGetNext,
wgsAlternateSuccess,
wgsAlternateFailed,
wgsAlternateReset,
wgsAlternateAlreadyInCache,
wgsAlternateWrongSizeInCache,
wgsAlternateCopiedInCache,
wgsProgressive,
wgsProgressiveFailed,
wgsGet,
wgsSetDate,
wgsLastMod);
/// which steps have been performed during THttpClientSocket.WGet() process
TWGetSteps = set of TWGetStep;
/// callback event for THttpClientSocket.WGet() process
// - as set to THttpClientSocketWGet.OnStep
// - an additional text information, depending on each step, is supplied
TOnWGetStep = procedure(Step: TWGetStep; const Context: RawUtf8) of object;
/// parameters set for THttpClientSocket.WGet() process
// - some parameters are optional, and you should call Clear by default
// - you could call redirectly the WGet method after having called Clear
// and set the appropriated variables
{$ifdef USERECORDWITHMETHODS}
THttpClientSocketWGet = record
{$else}
THttpClientSocketWGet = object
{$endif USERECORDWITHMETHODS}
/// optional callback event called during download process
// - typical usage is to assign e.g. TStreamRedirect.ProgressStreamToConsole
// - note that by default, THttpClientSocket.OnLog will always be called
OnProgress: TOnStreamProgress;
/// optional callback if TFileStreamEx.Create(FileName, Mode) is not good enough
OnStreamCreate: TOnStreamCreate;
/// optional callback event raised during WGet() process
// - if OutSteps: TWGetSteps field is not enough
// - alternative for business logic tracking: the OnProgress callback is
// more about periodic human interaction in GUI or console
OnStep: TOnWGetStep;
/// optional callback to allow an alternate download method
// - can be used for a local peer-to-peer download cache via THttpPeerCache
// - if defined, it will make a HEAD on the server to ensure the file still
// exist (and that the client has the right to access it), then try to call
// OnDownload() to get it from THttpPeerCache instances in the vicinity,
// with a fallback to a plain GET if this file is not known by the peers
// - OnDownloaded() will be called once a file has been received
Alternate: IWGetAlternate;
/// how Alternate should operate this file
AlternateOptions: TWGetAlternateOptions;
/// how much time this connection should be kept alive
// - as redirected to the internal Request() parameter
KeepAlive: cardinal;
/// allow to continue an existing .part file download
// - during the download phase, url + '.part' is used locally to avoid
// confusion in case of process shutdown - you can use this parameter to
// continue the download from the existing content (useful for huge files)
Resume: boolean;
/// try to download the Hash value from the server, e.g. from url + '.md5'
// - the hash URI extension is retrieved from TStreamRedirect.GetHashFileExt
HashFromServer: boolean;
/// allow custom hashing of the content
// - if not set, a plain TStreamRedirect with no hashing instance will be
// used for correct streaming to the destination file
// - typical classes are TStreamRedirectMd5 or TStreamRedirectSha256 from
// mormot.crypt.secure - you may use e.g. HASH_STREAMREDIRECT[] constants
Hasher: TStreamRedirectClass;
/// the expected hash value, to be compared with Hasher.GetHash return
// - if supplied, the downloaded content will be checked against this value
// - see also HashFromServer and HashCacheDir parameters
Hash: RawUtf8;
/// an optional folder to lookup for existing content
// - the Hash parameter will be used to validate the content
HashCacheDir: TFileName;
/// allow to customize request headers, e.g. a cookie or Auth-Bearer
Header: RawUtf8;
/// can be used to reduce the download speed into supplied bytes per second
LimitBandwidth: integer;
/// will raise ESynException after TimeOutSec seconds are elapsed
// - WGet(sockettimeout) is the TCP connect/receive/send raw timeout for
// each packet, whereas this property is about the global time elapsed
// during the whole download process
TimeOutSec: integer;
/// when WGet() has been called, contains all the steps involved
OutSteps: TWGetSteps;
/// to optionally log all SetStep() content during process
LogSteps: TSynLogProc;
/// initialize the default parameters - reset all fields to 0 / nil / ''
procedure Clear;
/// method used internally during process to notify Steps and OnStep()
procedure SetStep(Step: TWGetStep; const Context: array of const);
/// after Clear, instantiate and wrap THttpClientSocket.WGet
// - would return the downloaded file name on success
function WGet(const url: RawUtf8; const destfile: TFileName;
const tunnel: RawUtf8 = ''; tls: PNetTlsContext = nil;
sockettimeout: cardinal = 10000; redirectmax: integer = 0): TFileName;
end;
/// interface called by THttpClientSocket.WGet() for alternate download
// - THttpPeerCache implements e.g. a local peer-to-peer download cache
// - as set to THttpClientSocketWGet.Alternate optional parameter
IWGetAlternate = interface
/// try to download a resource from the alternative source
// - e.g. THttpPeerCache will broadcast and retrieve the file from its peers
// - this method is called after a HEAD on the server to retrieve the file
// size and ensure the client is authorized to get the resource
// - Params.Hasher/Hash are expected to be populated
// - should behave the same (including RangeStart/RangeEnd support) as
// ! Sender.Request(Url, 'GET', Params^.KeepAlive, Params^.Header, '', '',
// ! {retry=}false, {instream=}nil, OutStream)
// - OutStream.LimitPerSecond may be overriden during the call, e.g. if
// operating on a local network
// - should return HTTP_SUCCESS or HTTP_PARTIALCONTENT on success
// - should return 0 if hash was not found, to fallback to a regular GET
function OnDownload(Sender: THttpClientSocket;
var Params: THttpClientSocketWGet; const Url: RawUtf8;
ExpectedFullSize: Int64; OutStream: TStreamRedirect): integer;
/// notify the alternate download implementation that there is a file
// currently downloading into a .partial local file content
// - e.g. THttpPeerCache will make this file available as pcfResponsePartial
// - Params.Hasher/Hash are expected to be populated
// - returns an integer OnDownloadingID > 0 to be supplied to OnDowloaded()
// or OnDownloadingFailed()
function OnDownloading(const Params: THttpClientSocketWGet;
const Partial: TFileName; ExpectedFullSize: Int64): THttpPartialID;
/// put a downloaded file into the alternative source cache
// - e.g. THttpPeerCache will add this file to its cache, and resume any
// pcfResponsePartial with the new file name
// - this method is called after any file has been successfully downloaded
// - Params.Hasher/Hash are expected to be populated
procedure OnDowloaded(var Params: THttpClientSocketWGet;
const Partial: TFileName; OnDownloadingID: THttpPartialID);
/// notify the alternate download implementation that the data supplied
// by OnDownload() was incorrect
// - e.g. THttpPeerCache will delete this file from its cache
// - mainly if the resulting hash does not match
procedure OnDownloadFailed(const Params: THttpClientSocketWGet);
/// notify the alternate download implementation that OnDownloading() failed
// - e.g. THttpPeerCache will abort publishing this partial file
procedure OnDownloadingFailed(OnDownloadingID: THttpPartialID);
/// check if the network interface defined in Settings did actually change
// - you may want to recreate the alternate downloading instance
function NetworkInterfaceChanged: boolean;
end;
/// internal low-level execution context for THttpClientSocket.Request
THttpClientRequest = record
Url, Method, Header: RawUtf8;
Data: RawByteString;
DataMimeType: RawUtf8;
Status, Redirected: integer;
InStream, OutStream: TStream;
KeepAliveSec: cardinal;
Retry: set of (rMain, rAuth, rAuthProxy); // auth + retry state machine
OutStreamInitialPos: Int64;
end;
/// callback used by THttpClientSocket.Request on HTTP_UNAUTHORIZED (401)
// or HTTP_PROXYAUTHREQUIRED (407) errors
// - as set to OnAuthorize/OnProxyAuthorize event properties
// - Authenticate contains the "WWW-Authenticate" response header value
// - could e.g. set Sender.AuthBearer/BasicAuthUserPassword then return
// true to let the caller try again with the new headers
// - if you return false, nothing happens and the 401 is reported back
// - more complex schemes (like SSPI/Kerberos) could be implemented within the
// callback - see e.g. THttpClientSocket.OnAuthorizeSspi class method
TOnHttpClientSocketAuthorize = function(Sender: THttpClientSocket;
var Context: THttpClientRequest; const Authenticate: RawUtf8): boolean of object;
/// callback used by THttpClientSocket.Request before/after every request
// - return true to continue execution, false to abort normal process
TOnHttpClientSocketRequest = function(Sender: THttpClientSocket;
var Context: THttpClientRequest): boolean of object;
/// callback used e.g. by THttpClientSocket.Request to process any custom protocol
TOnHttpClientRequest = function(
var http: THttpRequestContext): integer of object;
/// Socket API based REST and HTTP/1.1 compatible client class
// - this component is HTTP/1.1 compatible, according to RFC 2068 document
// - the REST commands (GET/POST/PUT/DELETE) are directly available
// - open connection with the server with inherited Open(server,port) function
// - if KeepAlive>0, the connection is not broken: a further request (within
// KeepAlive milliseconds) will use the existing connection if available,
// or recreate a new one if the former is outdated or reset by server
// (will retry only once); this is faster, uses less resources (especialy
// under Windows), and is the recommended way to implement a HTTP/1.1 server
// - on any error (timeout, connection closed) will retry once to get the value
// - note that this client is not thread-safe: either use a critical section
// (as we do in TRestClientUri), or create one instance per thread
// - don't forget to use Free procedure when you are finished
THttpClientSocket = class(THttpSocket)
protected
fExtendedOptions: THttpRequestExtendedOptions;
fReferer: RawUtf8;
fAccept: RawUtf8;
fProcessName: RawUtf8;
fRedirected: RawUtf8;
fRangeStart, fRangeEnd: Int64;
fAuthDigestAlgo: TDigestAlgo;
fOnAuthorize, fOnProxyAuthorize: TOnHttpClientSocketAuthorize;
fOnBeforeRequest: TOnHttpClientSocketRequest;
fOnProtocolRequest: TOnHttpClientRequest;
fOnAfterRequest: TOnHttpClientSocketRequest;
fOnRedirect: TOnHttpClientSocketRequest;
{$ifdef DOMAINRESTAUTH}
fAuthorizeSspiSpn: RawUtf8;
{$endif DOMAINRESTAUTH}
procedure SetAuthBearer(const Value: SpiUtf8);
procedure RequestSendHeader(const url, method: RawUtf8); virtual;
procedure RequestClear; virtual;
function OnAuthorizeDigest(Sender: THttpClientSocket;
var Context: THttpClientRequest; const Authenticate: RawUtf8): boolean;
public
/// common initialization of all constructors
// - this overridden method will set the UserAgent with some default value
// - you can customize the default client timeouts by setting appropriate
// aTimeout parameters (in ms) if you left the 0 default parameters,
// it would use global HTTP_DEFAULT_RECEIVETIMEOUT variable values
constructor Create(aTimeOut: PtrInt = 0); override;
/// finalize this instance
destructor Destroy; override;
/// constructor to create a client connection to a given URI
// - returns TUri.Address as parsed from aUri
// - overriden to support custom RegisterNetClientProtocol()
constructor OpenUri(const aUri: RawUtf8; out aAddress: RawUtf8;
const aTunnel: RawUtf8 = ''; aTimeOut: cardinal = 10000;
aTLSContext: PNetTlsContext = nil); override;
/// constructor to create a client connection to a given TUri and options
// - will use specified options, including TLS and Auth members, just like
// the overloaded THttpRequest.Create(TUri,PHttpRequestExtendedOptions)
// - raise an exception on connection error
// - as used e.g. by TSimpleHttpClient
constructor OpenOptions(const aUri: TUri;
var aOptions: THttpRequestExtendedOptions);
/// compare TUri and its options with the actual connection
// - returns true if no new instance - i.e. Free + OpenOptions() - is needed
// - only supports HTTP/HTTPS, not any custom RegisterNetClientProtocol()
function SameOpenOptions(const aUri: TUri;
const aOptions: THttpRequestExtendedOptions): boolean; virtual;
/// low-level HTTP/1.1 request
// - called by all Get/Head/Post/Put/Delete REST methods
// - after an Open(server,port), return 200,202,204 if OK, or an http
// status error otherwise
// - retry is usually false, but could be recursively recalled as true
// - use either Data or InStream for sending its body request (with its MIME
// content type as DataMimeType e.g. JSON_CONTENT_TYPE)
// - response body will be either in Content or in OutStream
// - wrapper around RequestInternal() with OnBeforeRequest/OnAfterRequest
// and RedirectMax handling
function Request(const url, method: RawUtf8; KeepAlive: cardinal;
const Header: RawUtf8; const Data: RawByteString = '';
const DataMimeType: RawUtf8 = ''; retry: boolean = false;
InStream: TStream = nil; OutStream: TStream = nil): integer; virtual;
/// low-level processing method called from Request()
// - can be used e.g. when implementing callbacks like OnAuthorize or
// OnBeforeRequest/OnAfterRequest
procedure RequestInternal(var ctxt: THttpClientRequest); virtual;
/// after an Open(server,port), return 200 if OK, http status error otherwise
// - get the page data in Content
function Get(const url: RawUtf8; KeepAlive: cardinal = 0;
const header: RawUtf8 = ''): integer;
/// after an Open(server,port), return 200 if OK, http status error otherwise
// - get the page data in Content
// - if AuthToken<>'', will add an header with 'Authorization: Bearer '+AuthToken
function GetAuth(const url, AuthToken: RawUtf8; KeepAlive: cardinal = 0): integer;
/// download a (huge) file with proper resume and optional caching
// - DestFile is the file name to use to put the downloaded content - if
// left void, will compute and return a file name from the url value
// - fine tuning of the process could be done using params
function WGet(const url: RawUtf8; const destfile: TFileName;
var params: THttpClientSocketWGet): TFileName;
/// after an Open(server,port), return 200 if OK, http status error otherwise - only
// header is read from server: Content is always '', but Headers are set
function Head(const url: RawUtf8; KeepAlive: cardinal = 0;
const header: RawUtf8 = ''): integer;
/// after an Open(server,port), return 200,201,204 if OK, http status error otherwise
function Post(const url: RawUtf8; const Data: RawByteString;
const DataMimeType: RawUtf8; KeepAlive: cardinal = 0;
const header: RawUtf8 = ''): integer; overload;
/// after an Open(server,port), return 200,201,204 if OK, http status error otherwise
// - this overloaded method accepts a TStream for its output body content
// - you could use a THttpMultiPartStream for multipart/formdata HTTP POST
function Post(const url: RawUtf8; Data: TStream;
const DataMimeType: RawUtf8; KeepAlive: cardinal = 0;
const header: RawUtf8 = ''): integer; overload;
/// after an Open(server,port), return 200,201,204 if OK, http status error otherwise
function Put(const url: RawUtf8; const Data: RawByteString;
const DataMimeType: RawUtf8; KeepAlive: cardinal = 0;
const header: RawUtf8 = ''): integer;
/// after an Open(server,port), return 200,202,204 if OK, http status error otherwise
function Delete(const url: RawUtf8; KeepAlive: cardinal = 0;
const header: RawUtf8 = ''): integer;
/// setup web authentication using the Basic access algorithm
procedure AuthorizeBasic(const UserName: RawUtf8; const Password: SpiUtf8);
/// setup web authentication using the Digest access algorithm
procedure AuthorizeDigest(const UserName: RawUtf8; const Password: SpiUtf8;
Algo: TDigestAlgo = daMD5_Sess);
{$ifdef DOMAINRESTAUTH}
/// setup web authentication using Kerberos/NTLM via SSPI/GSSAPI for this instance
// - will store the user/paswword credentials, and set OnAuthorizeSspi callback
// - if Password is '', will search for an existing Kerberos token on UserName
// - an in-memory token will be used to authenticate the connection
// - WARNING: on MacOS, the default system GSSAPI stack seems to create a
// session-wide token (like kinit), not a transient token in memory - you
// may prefer to load a proper libgssapi_krb5.dylib instead
procedure AuthorizeSspiUser(const UserName: RawUtf8; const Password: SpiUtf8;
const KerberosSpn: RawUtf8 = '');
/// web authentication callback of the current logged user using Kerberos/NTLM
// - calling the Security Support Provider Interface (SSPI) API on Windows,
// or GSSAPI on Linux (only Kerboros)
// - match the OnAuthorize: TOnHttpClientSocketAuthorize callback signature
// - see also ClientForceSpn() and AuthorizeSspiSpn property
class function OnAuthorizeSspi(Sender: THttpClientSocket;
var Context: THttpClientRequest; const Authenticate: RawUtf8): boolean;
/// proxy authentication callback of the current logged user using Kerberos/NTLM
// - calling the Security Support Provider Interface (SSPI) API on Windows,
// or GSSAPI on Linux (only Kerboros)
// - match the OnProxyAuthorize: TOnHttpClientSocketAuthorize signature
// - see also ClientForceSpn() and AuthorizeSspiSpn property
class function OnProxyAuthorizeSspi(Sender: THttpClientSocket;
var Context: THttpClientRequest; const Authenticate: RawUtf8): boolean;
/// the Kerberos Service Principal Name, as registered in domain
// - e.g. 'mymormotservice/[email protected]'
// - used by class procedure OnAuthorizeSspi/OnProxyAuthorizeSspi callbacks
// - on Linux/GSSAPI either this property or ClientForceSpn() is mandatory
property AuthorizeSspiSpn: RawUtf8
read fAuthorizeSspiSpn write fAuthorizeSspiSpn;
{$endif DOMAINRESTAUTH}
/// the optional 'Accept: ' header value
property Accept: RawUtf8
read fAccept write fAccept;
/// the optional 'Referer: ' header value
property Referer: RawUtf8
read fReferer write fReferer;
/// the associated process name, to be set and used by the external context
property ProcessName: RawUtf8
read fProcessName write fProcessName;
/// optional begining position of a request
// - is reset once the Request has been sent
property RangeStart: Int64
read fRangeStart write fRangeStart;
/// optional ending position of a request
// - is reset once the Request has been sent
property RangeEnd: Int64
read fRangeEnd write fRangeEnd;
/// how many 3xx status code redirections are allowed
// - default is 0 - i.e. no redirection
property RedirectMax: integer
read fExtendedOptions.RedirectMax write fExtendedOptions.RedirectMax;
/// the effective 'Location:' URI after 3xx redirection(s) of Request()
property Redirected: RawUtf8
read fRedirected;
/// optional Authentication Scheme
// - may still be wraNone if OnAuthorize has been manually set
property AuthScheme: THttpRequestAuthentication
read fExtendedOptions.Auth.Scheme;
/// optional Authorization: Bearer header value
property AuthBearer: SpiUtf8
read fExtendedOptions.Auth.Token write SetAuthBearer;
/// contain the body data retrieved from the server - from inherited Http
property Content: RawByteString
read Http.Content;
/// contain the response headers retrieved from the server - from inherited Http
property Headers: RawUtf8
read Http.Headers;
/// optional authorization callback
// - is triggered by Request() on HTTP_UNAUTHORIZED (401) status
// - as set by AuthorizeDigest() AuthorizeSspiUser()
// - as reset by AuthorizeBasic()
// - see e.g. THttpClientSocket.OnAuthorizeSspi class method for SSPI auth
property OnAuthorize: TOnHttpClientSocketAuthorize
read fOnAuthorize write fOnAuthorize;
/// optional proxy authorization callback
// - is triggered by Request() on HTTP_PROXYAUTHREQUIRED (407) status
// - see e.g. THttpClientSocket.OnProxyAuthorizeSspi class method for SSPI auth
property OnProxyAuthorize: TOnHttpClientSocketAuthorize
read fOnProxyAuthorize write fOnProxyAuthorize;
/// optional callback called before each Request()
property OnBeforeRequest: TOnHttpClientSocketRequest
read fOnBeforeRequest write fOnBeforeRequest;
/// optional callback called after each Request()
property OnAfterRequest: TOnHttpClientSocketRequest
read fOnAfterRequest write fOnAfterRequest;
/// optional callback called before a Request() internal redirection
// - ctxt.Status contains e.g. 301 (HTTP_MOVEDPERMANENTLY)
// - ctxt.Url contains the redirected URI retrieved from 'Location:' header
property OnRedirect: TOnHttpClientSocketRequest
read fOnRedirect write fOnRedirect;
published
/// by default, the client is identified as IE 5.5, which is very
// friendly welcome by most servers :(
// - you can specify a custom value here
property UserAgent: RawUtf8
read fExtendedOptions.UserAgent write fExtendedOptions.UserAgent;
/// contain the body data length retrieved from the server
property ContentLength: Int64
read Http.ContentLength;
/// contain the body type retrieved from the server
property ContentType: RawUtf8
read Http.ContentType;
end;
/// class-reference type (metaclass) of a HTTP client socket access
// - may be either THttpClientSocket or THttpClientWebSockets (from
// mormot.net.websock unit)
THttpClientSocketClass = class of THttpClientSocket;
/// returns the HTTP User-Agent header value of a mORMot client including
// the Instance class name in its minified/uppercase-only translation
// - typical value is "Mozilla/5.0 (Linux x64; mORMot) HCS/2 Tests/3"
// for THttpClientSocket from a Tests.exe application in version 3.x
// - framework is identified as '/2' with no release number, for security
// - note: the framework would identify the 'mORMot' pattern in the user-agent
// header to enable advanced behavior e.g. about JSON transmission
function DefaultUserAgent(Instance: TObject): RawUtf8;
/// create a THttpClientSocket, returning nil on error
// - useful to easily catch socket error exception ENetSock
function OpenHttp(const aServer, aPort: RawUtf8; aTLS: boolean = false;
aLayer: TNetLayer = nlTcp; const aUrlForProxy: RawUtf8 = '';
aTimeout: integer = 0; aTLSContext: PNetTlsContext = nil): THttpClientSocket; overload;
/// create a THttpClientSocket, returning nil on error
// - useful to easily catch socket error exception ENetSock
function OpenHttp(const aUri: RawUtf8;
aAddress: PRawUtf8 = nil): THttpClientSocket; overload;
/// retrieve the content of a web page, using the HTTP/1.1 protocol and GET method
// - this method will use a low-level THttpClientSock socket: if you want
// something able to use your computer proxy, take a look at TWinINet.Get()
// and the overloaded HttpGet() functions
function OpenHttpGet(const server, port, url, inHeaders: RawUtf8;
outHeaders: PRawUtf8 = nil; aLayer: TNetLayer = nlTcp;
aTLS: boolean = false; outStatus: PInteger = nil;
aTimeout: integer = 0; ignoreTlsCertError: boolean = false): RawByteString; overload;
/// download some potentially huge file, with proper resume
// - is a wrapper around THttpClientSocket.WGet() method
// - returns '' on success, or an error message otherwise
function WGet(const url: RawUtf8; const destfile: TFileName;
const tunnel: RawUtf8 = ''; hasher: TStreamRedirectClass = nil;
const hash: RawUtf8 = ''; tls: PNetTlsContext = nil;
sockettimeout: cardinal = 10000; redirectmax: integer = 0;
consoledisplay: boolean = false): string;
function ToText(wgs: TWGetStep): PShortString; overload;
function ToText(wgs: TWGetSteps; trimmed: boolean = true): RawUtf8; overload;
var
/// global overriden value for the GetSystemProxyUri() function
// - as used by OpenHttp/OpenHttpGet and TSimpleHttpClient
// - can be set manually to a forced global value
DefaultHttpClientSocketProxy: TUri;
/// force GetProxyForUri(fromSystem=true) in GetSystemProxyUri() function
DefaultHttpClientSocketProxyAuto: boolean;
/// ask the Operating System to return the Tunnel/Proxy settings for a given URI
// - as used internally by OpenHttp/OpenHttpGet and TSimpleHttpClient to call
// THttpClientSocket.Open() constructor
// - if proxy is set, will return its value from @temp, otherwise return
// @DefaultHttpClientSocketProxy or call GetProxyForUri() to fill and return @temp
// - return nil if no proxy is to be used for this URI
function GetSystemProxyUri(const uri, proxy: RawUtf8; var temp: TUri): PUri;
/// ask the Operating System to return the Tunnel/Proxy settings for a given URI
// - return DefaultHttpClientSocketProxy.URI or call GetProxyForUri()
// - return '' if no proxy is to be used for this URI
function GetSystemProxy(const uri: RawUtf8): RawUtf8;
/// ask the Operating System to return the Tunnel/Proxy setting for a given URI
// - will always use or HTTP_PROXY/HTTPS_PROXY environment variables
// - if no environment variable is set, on Windows fromSystem=true will call
// WinHttpGetProxyInfo from mormot.lib.winhttp to use the Internet Explorer
// settings or system PAC file
// - return '' if no proxy is defined
function GetProxyForUri(const uri: RawUtf8;
fromSystem: boolean = true): RawUtf8;
/// parse a URI into its final resource name
// - optionally sanitize the output to be filename-compatible
// - note that it returns an UTF-8 string as resource URI, not TFileName
function ExtractResourceName(const uri: RawUtf8; sanitize: boolean = true): RawUtf8;
{ ******************** Additional Client Protocols Support }
var
/// raw thread-safe access to <Name:RawUtf8,TOnHttpClientRequest> pairs
NetClientProtocols: TSynDictionary;
/// register a INetClientProtocol
// - you can unregister a protocol by setting OnRequest = nil
// - note that the class instance used by OnRequest will be owned by thit unit
procedure RegisterNetClientProtocol(
const Name: RawUtf8; const OnRequest: TOnHttpClientRequest);
{ ******************** THttpRequest Abstract HTTP client class }
{$ifdef USEHTTPREQUEST} // as set in mormot.defines.inc
type
{$M+} // to have existing RTTI for published properties
THttpRequest = class;
{$M-}
/// event callback to track up/download process
TOnHttpRequest = procedure(Sender: THttpRequest; Done: Boolean) of object;
/// event callback to track up/download progress
TOnHttpRequestProgress = procedure(Sender: THttpRequest;
Current, Total: Int64) of object;
{$M+} // to have existing RTTI for published properties
/// abstract class to handle HTTP/1.1 request
// - never instantiate this class, but inherited TWinHttp, TWinINet or TCurlHttp
THttpRequest = class
protected
fServer: RawUtf8;
fProxyName: RawUtf8;
fProxyByPass: RawUtf8;
fPort: TNetPort;
fLayer: TNetLayer;
fKeepAlive: cardinal;
fHttps: boolean;
/// used by RegisterCompress method
fCompress: THttpSocketCompressRecDynArray;
/// set by RegisterCompress method
fCompressAcceptEncoding: RawUtf8;
/// set index of protocol in fCompress[], from ACCEPT-ENCODING: header
fCompressAcceptHeader: THttpSocketCompressSet;
fExtendedOptions: THttpRequestExtendedOptions;
fTag: PtrInt;
fOnUpload: TOnHttpRequest;
fOnUploadProgress: TOnHttpRequestProgress;
fOnDownload: TOnHttpRequest;
fOnDownloadProgress: TOnHttpRequestProgress;
class function InternalREST(const url, method: RawUtf8;
const data: RawByteString; const header: RawUtf8;
aIgnoreTlsCertificateErrors: boolean; timeout: integer;
outHeaders: PRawUtf8; outStatus: PInteger): RawByteString;
// inherited class should override those abstract methods
procedure InternalConnect(ConnectionTimeOut, SendTimeout, ReceiveTimeout: cardinal); virtual; abstract;
procedure InternalCreateRequest(const aMethod, aUrl: RawUtf8); virtual; abstract;
procedure InternalSendRequest(const aMethod: RawUtf8; const aData:
RawByteString); virtual; abstract;
function InternalRetrieveAnswer(var Header, Encoding, AcceptEncoding: RawUtf8;
var Data: RawByteString): integer; virtual; abstract;
procedure InternalCloseRequest; virtual; abstract;
procedure InternalAddHeader(const hdr: RawUtf8); virtual; abstract;
public
/// returns TRUE if the class is actually supported on this system
class function IsAvailable: boolean; virtual; abstract;
/// 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:
// aProxyName/aProxyByPass will be recognized by TWinHttp and TWinINet,
// and aProxyName will set the CURLOPT_PROXY option to TCurlHttp
// (see https://curl.haxx.se/libcurl/c/CURLOPT_PROXY.html as reference)
// - 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 - if you left the 0 default parameters, it
// would use global HTTP_DEFAULT_CONNECTTIMEOUT, HTTP_DEFAULT_SENDTIMEOUT
// and HTTP_DEFAULT_RECEIVETIMEOUT variable values
// - *TimeOut parameters are currently ignored by TCurlHttp
// - aUserAgent is needed for TWinHttp, which requires this info at connection
constructor Create(const aServer, aPort: RawUtf8; aHttps: boolean;
const aProxyName: RawUtf8 = ''; const aProxyByPass: RawUtf8 = '';
ConnectionTimeOut: cardinal = 0; SendTimeout: cardinal = 0;
ReceiveTimeout: cardinal = 0; aLayer: TNetLayer = nlTcp;
const aUserAgent: RawUtf8 = ''); overload; virtual;
/// connect to the supplied URI, supplied as TUri
// - overloaded constructor to accept optional THttpRequestExtendedOptions
constructor Create(const aUri: TUri;
aOptions: PHttpRequestExtendedOptions = nil); overload;
/// connect to the supplied URI
// - is just a wrapper around TUri and the overloaded Create() constructor
constructor Create(const aUri: RawUtf8; const aProxyName: RawUtf8 = '';
const aProxyByPass: RawUtf8 = ''; ConnectionTimeOut: cardinal = 0;
SendTimeout: cardinal = 0; ReceiveTimeout: cardinal = 0;
aIgnoreTlsCertificateErrors: boolean = false); overload;
/// finalize this instance
destructor Destroy; override;
/// low-level HTTP/1.1 request
// - after an Create(server,port), return 200,202,204 if OK,
// http status error otherwise
// - KeepAlive is in milliseconds, 0 for "Connection: Close" HTTP/1.0 requests
function Request(const url, method: RawUtf8; KeepAlive: cardinal;
const InHeader: RawUtf8; const InData: RawByteString; const InDataType: RawUtf8;
out OutHeader: RawUtf8; out OutData: RawByteString): integer; virtual;
/// wrapper method to retrieve a resource via an HTTP GET
// - will parse the supplied URI to check for the http protocol (HTTP/HTTPS),
// server name and port, and resource name
// - aIgnoreTlsCertificateErrors will ignore the error when using untrusted certificates
// - it will internally create a THttpRequest inherited instance: do not use
// THttpRequest.Get() but either TWinHttp.Get(), TWinINet.Get() or
// TCurlHttp.Get() methods
class function Get(const aUri: RawUtf8; const aHeader: RawUtf8 = '';
aIgnoreTlsCertificateErrors: boolean = false; outHeaders: PRawUtf8 = nil;
outStatus: PInteger = nil; timeout: integer = 0): RawByteString;
/// wrapper method to create a resource via an HTTP POST
// - will parse the supplied URI to check for the http protocol (HTTP/HTTPS),
// server name and port, and resource name
// - aIgnoreTlsCertificateErrors will ignore the error when using untrusted certificates
// - the supplied aData content is POSTed to the server, with an optional
// aHeader content
// - it will internally create a THttpRequest inherited instance: do not use
// THttpRequest.Post() but either TWinHttp.Post(), TWinINet.Post() or
// TCurlHttp.Post() methods
class function Post(const aUri: RawUtf8; const aData: RawByteString;
const aHeader: RawUtf8 = ''; aIgnoreTlsCertificateErrors: boolean = false;
outHeaders: PRawUtf8 = nil; outStatus: PInteger = nil;
timeout: integer = 0): RawByteString;
/// wrapper method to update a resource via an HTTP PUT
// - will parse the supplied URI to check for the http protocol (HTTP/HTTPS),
// server name and port, and resource name
// - aIgnoreTlsCertificateErrors will ignore the error when using untrusted certificates
// - the supplied aData content is PUT to the server, with an optional
// aHeader content
// - it will internally create a THttpRequest inherited instance: do not use
// THttpRequest.Put() but either TWinHttp.Put(), TWinINet.Put() or
// TCurlHttp.Put() methods
class function Put(const aUri: RawUtf8; const aData: RawByteString;
const aHeader: RawUtf8 = ''; aIgnoreTlsCertificateErrors: boolean = false;
outHeaders: PRawUtf8 = nil; outStatus: PInteger = nil;
timeout: integer = 0): RawByteString;
/// wrapper method to delete a resource via an HTTP DELETE
// - will parse the supplied URI to check for the http protocol (HTTP/HTTPS),
// server name and port, and resource name
// - aIgnoreTlsCertificateErrors will ignore the error when using untrusted certificates
// - it will internally create a THttpRequest inherited instance: do not use
// THttpRequest.Delete() but either TWinHttp.Delete(), TWinINet.Delete() or
// TCurlHttp.Delete() methods
class function Delete(const aUri: RawUtf8; const aHeader: RawUtf8 = '';
aIgnoreTlsCertificateErrors: boolean = false; outHeaders: PRawUtf8 = nil;
outStatus: PInteger = nil; timeout: integer = 0): RawByteString;
/// will register a compression algorithm
// - used e.g. to compress on the fly the data, with standard gzip/deflate
// or custom (synlz) protocols
// - returns true on success, false if this function or this
// ACCEPT-ENCODING: header was already registered
// - you can specify a minimal size (in bytes) below which the content won't
// be compressed (1024 by default, corresponding to a MTU of 1500 bytes)
// - the first registered algorithm will be the prefered one for compression
// within each priority level (the lower aPriority first)
function RegisterCompress(aFunction: THttpSocketCompress;
aCompressMinSize: integer = 1024; aPriority: integer = 10): boolean;
/// allows to ignore untrusted TLS certificates
// - similar to adding a security exception for a domain in the browser
property IgnoreTlsCertificateErrors: boolean
read fExtendedOptions.TLS.IgnoreCertificateErrors
write fExtendedOptions.TLS.IgnoreCertificateErrors;
{$ifndef PUREMORMOT2}
property IgnoreSslCertificateErrors: boolean
read fExtendedOptions.TLS.IgnoreCertificateErrors
write fExtendedOptions.TLS.IgnoreCertificateErrors;
{$endif PUREMORMOT2}
/// optional Authentication Scheme
property AuthScheme: THttpRequestAuthentication
read fExtendedOptions.Auth.Scheme
write fExtendedOptions.Auth.Scheme;
/// optional User Name for Authentication
property AuthUserName: RawUtf8
read fExtendedOptions.Auth.UserName
write fExtendedOptions.Auth.UserName;
/// optional Password for Authentication
property AuthPassword: SpiUtf8
read fExtendedOptions.Auth.Password
write fExtendedOptions.Auth.Password;
/// optional Token for TCurlHttp Authentication
property AuthToken: SpiUtf8
read fExtendedOptions.Auth.Token
write fExtendedOptions.Auth.Token;
/// custom HTTP "User Agent:" header value
property UserAgent: RawUtf8
read fExtendedOptions.UserAgent
write fExtendedOptions.UserAgent;
/// how many 3xx status code redirections are allowed
// - default is 0 - i.e. no redirection
// - implemented for TWinHttp only
property RedirectMax: integer
read fExtendedOptions.RedirectMax write fExtendedOptions.RedirectMax;
/// internal structure used to store extended options
// - will be replicated by TLS.IgnoreCertificateErrors and Auth* properties
property ExtendedOptions: THttpRequestExtendedOptions
read fExtendedOptions
write fExtendedOptions;
/// some internal field, which may be used by end-user code
property Tag: PtrInt
read fTag write fTag;
published
/// the remote server host name, as stated specified to the class constructor
property Server: RawUtf8
read fServer;
/// the remote server port number, as specified to the class constructor
property Port: TNetPort
read fPort;
/// 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
// - you may set 'none' to disable any Proxy, or keep '' to use the OS proxy
property ProxyName: RawUtf8
read fProxyName;
/// the remote server optional proxy by-pass list, as specified to the class
// constructor
property ProxyByPass: RawUtf8
read fProxyByPass;
/// called before and after Upload process
property OnUpload: TOnHttpRequest
read fOnUpload write fOnUpload;
/// called during Upload progression
// - only implemented by TCurlHttp yet
property OnUploadProgress: TOnHttpRequestProgress
read fOnUploadProgress write fOnUploadProgress;
/// called before and after Download process
property OnDownload: TOnHttpRequest
read fOnDownload write fOnDownload;
/// called during Download progression