forked from synopse/mORMot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
SynCrtSock.pas
13158 lines (12281 loc) · 496 KB
/
SynCrtSock.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
/// classes implementing TCP/UDP/HTTP client and server protocol
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SynCrtSock;
{
This file is part of Synopse framework.
Synopse framework. Copyright (c) Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
Version: MPL 1.1/GPL 2.0/LGPL 2.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
https://www.mozilla.org/MPL
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the License.
The Original Code is Synopse mORMot framework.
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (c)
the Initial Developer. All Rights Reserved.
Contributor(s):
- Alfred Glaenzer (alf)
- Cybexr
- Darian Miller
- EMartin
- Eric Grange
- Eugene Ilyin
- EvaF
- f-vicente
- macc2010
- Maciej Izak (hnb)
- Marius Maximus
- Mr Yang (ysair)
- Pavel Mashlyakovskii (mpv)
- Willo vd Merwe
Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****
}
{$I Synopse.inc} // define HASINLINE ONLYUSEHTTPSOCKET USELIBCURL SYNCRTDEBUGLOW
{.$define SYNCRTDEBUGLOW}
// internal use: enable some low-level log messages for HTTP socket debugging
interface
uses
SysUtils, // put first to use SynFPCLinux/SynKylix GetTickCount64
{$ifndef LVCL}
Contnrs,
SyncObjs, // for TEvent (in Classes.pas for LVCL)
{$endif LVCL}
{$ifdef SYNCRTDEBUGLOW}
SynCommons,
SynLog,
{$endif SYNCRTDEBUGLOW}
{$ifdef USELIBCURL}
SynCurl,
{$endif USELIBCURL}
{$ifdef FPC}
dynlibs,
{$endif FPC}
{$ifdef MSWINDOWS}
Windows,
SynWinSock,
{$ifdef USEWININET}
WinInet,
{$endif USEWININET}
{$ifndef DELPHI5OROLDER}
Types,
{$endif DELPHI5OROLDER}
{$else MSWINDOWS}
{$undef USEWININET}
{$ifdef FPC}
SynFPCSock,
SynFPCLinux,
BaseUnix, // for fpgetrlimit/fpsetrlimit
{$ifdef LINUXNOTBSD}
Linux,
{$endif LINUXNOTBSD}
{$else}
{$ifndef DELPHI5OROLDER}
Types,
{$endif DELPHI5OROLDER}
{$endif FPC}
{$ifdef KYLIX3}
KernelIoctl, // for IoctlSocket/ioctl FION* constants
LibC,
SynFPCSock, // shared with Kylix
SynKylix,
{$endif KYLIX3}
{$endif MSWINDOWS}
Classes;
const
/// the full text of the current Synopse mORMot framework version
// - match the value defined in SynCommons.pas and SynopseCommit.inc
// - we don't supply full version number with build revision, to reduce
// potential attack surface
XPOWEREDPROGRAM = 'mORMot 1.18';
/// the running Operating System
XPOWEREDOS = {$ifdef MSWINDOWS} 'Windows' {$else}
{$ifdef LINUXNOTBSD} 'Linux' {$else} 'Posix' {$endif LINUXNOTBSD}
{$endif MSWINDOWS};
/// internal HTTP content-type for efficient static file sending
// - detected e.g. by http.sys' THttpApiServer.Request or via the NGINX
// X-Accel-Redirect header's THttpServer.Process (see
// THttpServer.NginxSendFileFrom) for direct sending with no local bufferring
// - 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';
/// used to notify e.g. the THttpServerRequest not to wait for any response
// from the client
// - is not to be used in normal HTTP process, but may be used e.g. by
// TWebSocketProtocolRest.ProcessFrame() to avoid to wait for an incoming
// response from the other endpoint
// - should match NORESPONSE_CONTENT_TYPE constant defined in mORMot.pas unit
HTTP_RESP_NORESPONSE = '!NORESPONSE';
var
/// THttpRequest timeout default value for DNS resolution
// - leaving to 0 will let system default value be used
HTTP_DEFAULT_RESOLVETIMEOUT: integer = 0;
/// THttpRequest timeout default value for remote connection
// - default is 60 seconds
// - used e.g. by THttpRequest, TSQLHttpClientRequest and TSQLHttpClientGeneric
HTTP_DEFAULT_CONNECTTIMEOUT: integer = 60000;
/// THttpRequest timeout default value for data sending
// - default is 30 seconds
// - used e.g. by THttpRequest, TSQLHttpClientRequest and TSQLHttpClientGeneric
// - you can override this value by setting the corresponding parameter in
// THttpRequest.Create() constructor
HTTP_DEFAULT_SENDTIMEOUT: integer = 30000;
/// THttpRequest timeout default value for data receiving
// - default is 30 seconds
// - used e.g. by THttpRequest, TSQLHttpClientRequest and TSQLHttpClientGeneric
// - you can override this value by setting the corresponding parameter in
// THttpRequest.Create() constructor
HTTP_DEFAULT_RECEIVETIMEOUT: integer = 30000;
type
{$ifdef HASCODEPAGE} // FPC may expect a CP, e.g. to compare two string constants
SockString = type RawByteString;
{$else}
/// define a 8-bit raw storage string type, used for data buffer management
SockString = type AnsiString;
{$endif}
/// points to a 8-bit raw storage variable, used for data buffer management
PSockString = ^SockString;
/// defines a dynamic array of SockString
TSockStringDynArray = array of SockString;
{$ifdef HASVARUSTRING}
SockUnicode = UnicodeString;
{$else}
/// define the fastest 16-bit Unicode string type of the compiler
SockUnicode = WideString;
{$endif}
{$ifdef DELPHI5OROLDER}
// not defined in Delphi 5 or older
PPointer = ^Pointer;
TTextLineBreakStyle = (tlbsLF, tlbsCRLF);
UTF8String = AnsiString;
UTF8Encode = AnsiString;
{$endif}
{$ifndef FPC}
/// FPC 64-bit compatibility integer type
{$ifdef CPU64}
PtrInt = NativeInt;
PtrUInt = NativeUInt;
{$else}
PtrInt = integer;
PtrUInt = cardinal;
{$endif}
PPtrInt = ^PtrInt;
PPtrUInt = ^PtrUInt;
{$endif FPC}
{$M+}
/// exception thrown by the classes of this unit
ECrtSocket = class(Exception)
protected
fLastError: integer;
public
/// will concat the message with the WSAGetLastError information
constructor Create(const Msg: string); overload;
/// will concat the message with the supplied WSAGetLastError information
constructor Create(const Msg: string; Error: integer); overload;
/// will concat the message with the supplied WSAGetLastError information
constructor CreateFmt(const Msg: string; const Args: array of const; Error: integer); overload;
published
/// the associated WSAGetLastError value
property LastError: integer read fLastError;
end;
{$M-}
TCrtSocketClass = class of TCrtSocket;
/// the available available network transport layer
// - either TCP/IP, UDP/IP or Unix sockets
TCrtSocketLayer = (cslTCP, cslUDP, cslUNIX);
/// identify the incoming data availability in TCrtSocket.SockReceivePending
TCrtSocketPending = (cspSocketError, cspNoData, cspDataAvailable);
PTextFile = ^TextFile;
{$M+}
/// Fast low-level Socket implementation
// - direct access to the OS (Windows, Linux) network layer API
// - use Open constructor to create a client to be connected to a server
// - use Bind constructor to initialize a server
// - use SockIn and SockOut (after CreateSock*) to read/readln or write/writeln
// as with standard Delphi text files (see SendEmail implementation)
// - even if you do not use read(SockIn^), you may call CreateSockIn then
// read the (binary) content via SockInRead/SockInPending methods, which would
// benefit of the SockIn^ input buffer to maximize reading speed
// - to write data, CreateSockOut and write(SockOut^) is not mandatory: you
// rather may use SockSend() overloaded methods, followed by a SockFlush call
// - in fact, you can decide whatever to use none, one or both SockIn/SockOut
// - since this class rely on its internal optimized buffering system,
// TCP_NODELAY is set to disable the Nagle algorithm
// - our classes are (much) faster than the Indy or Synapse implementation
TCrtSocket = class
protected
fSock: TSocket;
fServer: SockString;
fPort: SockString;
fSockIn: PTextFile;
fSockOut: PTextFile;
fTimeOut: PtrInt;
fBytesIn: Int64;
fBytesOut: Int64;
fSocketLayer: TCrtSocketLayer;
fSockInEofError: integer;
fTLS, fWasBind: boolean;
// updated by every SockSend() call
fSndBuf: SockString;
fSndBufLen: integer;
// set by AcceptRequest() from TVarSin
fRemoteIP: SockString;
// updated during UDP connection, accessed via PeerAddress/PeerPort
fPeerAddr: TSockAddr;
{$ifdef MSWINDOWS}
fSecure: TSChannelClient;
{$endif MSWINDOWS}
procedure SetInt32OptionByIndex(OptName, OptVal: integer); virtual;
public
/// common initialization of all constructors
// - do not call directly, but use Open / Bind constructors instead
constructor Create(aTimeOut: PtrInt=10000); reintroduce; virtual;
/// connect to aServer:aPort
// - you may ask for a TLS secured client connection (only available under
// Windows by now, using the SChannel API)
constructor Open(const aServer, aPort: SockString; aLayer: TCrtSocketLayer=cslTCP;
aTimeOut: cardinal=10000; aTLS: boolean=false);
/// bind to an address
// - aAddr='1234' - bind to a port on all interfaces, the same as '0.0.0.0:1234'
// - aAddr='IP:port' - bind to specified interface only, e.g. '1.2.3.4:1234'
// - aAddr='unix:/path/to/file' - bind to unix domain socket, e.g. 'unix:/run/mormot.sock'
// - aAddr='' - bind to systemd descriptor on linux. See
// https://0pointer.de/blog/projects/socket-activation.html
constructor Bind(const aAddr: SockString; aLayer: TCrtSocketLayer=cslTCP;
aTimeOut: integer=10000);
/// low-level internal method called by Open() and Bind() constructors
// - raise an ECrtSocket exception on error
// - you may ask for a TLS secured client connection (only available under
// Windows by now, using the SChannel API)
procedure OpenBind(const aServer, aPort: SockString; doBind: boolean;
aSock: integer=-1; aLayer: TCrtSocketLayer=cslTCP; aTLS: boolean=false);
/// initialize the instance with the supplied accepted socket
// - is called from a bound TCP Server, just after Accept()
procedure AcceptRequest(aClientSock: TSocket; aClientSin: PVarSin);
/// initialize SockIn for receiving with read[ln](SockIn^,...)
// - data is buffered, filled as the data is available
// - read(char) or readln() is indeed very fast
// - multithread applications would also use this SockIn pseudo-text file
// - by default, expect CR+LF as line feed (i.e. the HTTP way)
procedure CreateSockIn(LineBreak: TTextLineBreakStyle=tlbsCRLF;
InputBufferSize: Integer=1024);
/// initialize SockOut for sending with write[ln](SockOut^,....)
// - data is sent (flushed) after each writeln() - it's a compiler feature
// - use rather SockSend() + SockSendFlush to send headers at once e.g.
// since writeln(SockOut^,..) flush buffer each time
procedure CreateSockOut(OutputBufferSize: Integer=1024);
/// finalize SockIn receiving buffer
// - you may call this method when you are sure that you don't need the
// input buffering feature on this connection any more (e.g. after having
// parsed the HTTP header, then rely on direct socket comunication)
procedure CloseSockIn;
/// finalize SockOut receiving buffer
// - you may call this method when you are sure that you don't need the
// output buffering feature on this connection any more (e.g. after having
// parsed the HTTP header, then rely on direct socket comunication)
procedure CloseSockOut;
/// close and shutdown the connection (called from Destroy)
procedure Close;
/// close the opened socket, and corresponding SockIn/SockOut
destructor Destroy; override;
/// read Length bytes from SockIn buffer + Sock if necessary
// - if SockIn is available, it first gets data from SockIn^.Buffer,
// then directly receive data from socket if UseOnlySockIn=false
// - if UseOnlySockIn=true, it will return the data available in SockIn^,
// and returns the number of bytes
// - can be used also without SockIn: it will call directly SockRecv()
// in such case (assuming UseOnlySockin=false)
function SockInRead(Content: PAnsiChar; Length: integer;
UseOnlySockIn: boolean=false): integer;
/// returns the number of bytes in SockIn buffer or pending in Sock
// - if SockIn is available, it first check from any data in SockIn^.Buffer,
// then call InputSock to try to receive any pending data if the buffer is void
// - if aPendingAlsoInSocket is TRUE, returns the bytes available in both the buffer
// and the socket (sometimes needed, e.g. to process a whole block at once)
// - will wait up to the specified aTimeOutMS value (in milliseconds) for
// incoming data - may wait a little less time on Windows due to a select bug
// - returns -1 in case of a socket error (e.g. broken/closed connection);
// you can raise a ECrtSocket exception to propagate the error
function SockInPending(aTimeOutMS: integer; aPendingAlsoInSocket: boolean=false): integer;
/// check the connection status of the socket
function SockConnected: boolean;
/// simulate writeln() with direct use of Send(Sock, ..) - includes trailing #13#10
// - useful on multi-treaded environnement (as in THttpServer.Process)
// - no temp buffer is used
// - handle SockString, ShortString, Char, Integer parameters
// - raise ECrtSocket exception on socket error
procedure SockSend(const Values: array of const); overload;
/// simulate writeln() with a single line - includes trailing #13#10
procedure SockSend(const Line: SockString=''); overload;
/// append P^ data into SndBuf (used by SockSend(), e.g.) - no trailing #13#10
// - call SockSendFlush to send it through the network via SndLow()
procedure SockSend(P: pointer; Len: integer); overload;
/// flush all pending data to be sent, optionally with some body content
// - raise ECrtSocket on error
procedure SockSendFlush(const aBody: SockString=''); virtual;
/// flush all pending data to be sent
// - returning true on success
function TrySockSendFlush: boolean;
/// how many bytes could be added by SockSend() in the internal buffer
function SockSendRemainingSize: integer;
/// fill the Buffer with Length bytes
// - use TimeOut milliseconds wait for incoming data
// - bypass the SockIn^ buffers
// - raise ECrtSocket exception on socket error
procedure SockRecv(Buffer: pointer; Length: integer);
/// check if there are some pending bytes in the input sockets API buffer
// - returns cspSocketError if the connection is broken or closed
// - warning: on Windows, may wait a little less than TimeOutMS (select bug)
function SockReceivePending(TimeOutMS: integer): TCrtSocketPending;
/// returns the socket input stream as a string
function SockReceiveString: SockString;
/// fill the Buffer with Length bytes
// - use TimeOut milliseconds wait for incoming data
// - bypass the SockIn^ buffers
// - return false on any fatal socket error, true on success
// - call Close if the socket is identified as shutdown from the other side
// - you may optionally set StopBeforeLength=true, then the read bytes count
// are set in Length, even if not all expected data has been received - in
// this case, Close method won't be called
function TrySockRecv(Buffer: pointer; var Length: integer; StopBeforeLength: boolean=false): boolean;
/// call readln(SockIn^,Line) or simulate it with direct use of Recv(Sock, ..)
// - char are read one by one if needed
// - use TimeOut milliseconds wait for incoming data
// - raise ECrtSocket exception on socket error
// - by default, will handle #10 or #13#10 as line delimiter (as normal text
// files), but you can delimit lines using #13 if CROnly is TRUE
procedure SockRecvLn(out Line: SockString; CROnly: boolean=false); overload;
/// call readln(SockIn^) or simulate it with direct use of Recv(Sock, ..)
// - char are read one by one
// - use TimeOut milliseconds wait for incoming data
// - raise ECrtSocket exception on socket error
// - line content is ignored
procedure SockRecvLn; overload;
/// direct send data through network
// - raise a ECrtSocket exception on any error
// - bypass the SockSend() or SockOut^ buffers
procedure SndLow(P: pointer; Len: integer);
/// direct send data through network
// - return false on any error, true on success
// - bypass the SndBuf or SockOut^ buffers
function TrySndLow(P: pointer; Len: integer): boolean;
/// returns the low-level error number
// - i.e. returns WSAGetLastError
function LastLowSocketError: Integer;
/// direct send data through network
// - raise a ECrtSocket exception on any error
// - bypass the SndBuf or SockOut^ buffers
// - raw Data is sent directly to OS: no LF/CRLF is appened to the block
procedure Write(const Data: SockString);
/// direct accept an new incoming connection on a bound socket
// - instance should have been setup as a server via a previous Bind() call
// - returns nil on error or a ResultClass instance on success
// - if ResultClass is nil, will return a plain TCrtSocket, but you may
// specify e.g. THttpServerSocket if you expect incoming HTTP requests
function AcceptIncoming(ResultClass: TCrtSocketClass=nil): TCrtSocket;
/// remote IP address after AcceptRequest() call over TCP
// - is either the raw connection IP to the current server socket, or
// a custom header value set by a local proxy as retrieved by inherited
// THttpServerSocket.GetRequest, searching the header named in
// THttpServerGeneric.RemoteIPHeader (e.g. 'X-Real-IP' for nginx)
property RemoteIP: SockString read fRemoteIP write fRemoteIP;
/// remote IP address of the last packet received (SocketLayer=slUDP only)
function PeerAddress: SockString;
/// remote IP port of the last packet received (SocketLayer=slUDP only)
function PeerPort: integer;
/// set the TCP_NODELAY option for the connection
// - default 1 (true) will disable the Nagle buffering algorithm; it should
// only be set for applications that send frequent small bursts of information
// without getting an immediate response, where timely delivery of data
// is required - so it expects buffering before calling Write() or SndLow()
// - you can set 0 (false) here to enable the Nagle algorithm, if needed
// - see https://www.unixguide.net/network/socketfaq/2.16.shtml
property TCPNoDelay: Integer index TCP_NODELAY write SetInt32OptionByIndex;
/// set the SO_SNDTIMEO option for the connection
// - i.e. the timeout, in milliseconds, for blocking send calls
// - see https://msdn.microsoft.com/en-us/library/windows/desktop/ms740476
property SendTimeout: Integer index SO_SNDTIMEO write SetInt32OptionByIndex;
/// set the SO_RCVTIMEO option for the connection
// - i.e. the timeout, in milliseconds, for blocking receive calls
// - see https://msdn.microsoft.com/en-us/library/windows/desktop/ms740476
property ReceiveTimeout: Integer index SO_RCVTIMEO write SetInt32OptionByIndex;
/// set the SO_KEEPALIVE option for the connection
// - 1 (true) will enable keep-alive packets for the connection
// - see https://msdn.microsoft.com/en-us/library/windows/desktop/ee470551
property KeepAlive: Integer index SO_KEEPALIVE write SetInt32OptionByIndex;
/// set the SO_LINGER option for the connection, to control its shutdown
// - by default (or Linger<0), Close will return immediately to the caller,
// and any pending data will be delivered if possible
// - Linger > 0 represents the time in seconds for the timeout period
// to be applied at Close; under Linux, will also set SO_REUSEADDR; under
// Darwin, set SO_NOSIGPIPE
// - Linger = 0 causes the connection to be aborted and any pending data
// is immediately discarded at Close
property Linger: Integer index SO_LINGER write SetInt32OptionByIndex;
/// after CreateSockIn, use Readln(SockIn^,s) to read a line from the opened socket
property SockIn: PTextFile read fSockIn;
/// after CreateSockOut, use Writeln(SockOut^,s) to send a line to the opened socket
property SockOut: PTextFile read fSockOut;
published
/// low-level socket handle, initialized after Open() with socket
property Sock: TSocket read fSock write fSock;
/// low-level socket type, initialized after Open() with socket
property SocketLayer: TCrtSocketLayer read fSocketLayer;
/// IP address, initialized after Open() with Server name
property Server: SockString read fServer;
/// IP port, initialized after Open() with port number
property Port: SockString read fPort;
/// if higher than 0, read loop will wait for incoming data till
// TimeOut milliseconds (default value is 10000) - used also in SockSend()
property TimeOut: PtrInt read fTimeOut;
/// total bytes received
property BytesIn: Int64 read fBytesIn;
/// total bytes sent
property BytesOut: Int64 read fBytesOut;
end;
{$M-}
/// event used to compress or uncompress some data during HTTP protocol
// - should always return the protocol name for ACCEPT-ENCODING: header
// e.g. 'gzip' or 'deflate' for standard HTTP format, but you can add
// your own (like 'synlzo' or 'synlz')
// - the data is compressed (if Compress=TRUE) or uncompressed (if
// Compress=FALSE) in the Data variable (i.e. it is modified in-place)
// - to be used with THttpSocket.RegisterCompress method
// - DataRawByteStringtype should be a generic AnsiString/RawByteString, which
// should be in practice a SockString or a RawByteString
THttpSocketCompress = function(var DataRawByteString; Compress: boolean): AnsiString;
/// used to maintain a list of known compression algorithms
THttpSocketCompressRec = record
/// the compression name, as in ACCEPT-ENCODING: header (gzip,deflate,synlz)
Name: SockString;
/// the function handling compression and decompression
Func: THttpSocketCompress;
/// the size in bytes after which compress will take place
// - will be 1024 e.g. for 'zip' or 'deflate'
// - could be 0 e.g. when encrypting the content, meaning "always compress"
CompressMinSize: integer;
end;
/// list of known compression algorithms
THttpSocketCompressRecDynArray = array of THttpSocketCompressRec;
/// identify some items in a list of known compression algorithms
// - filled from ACCEPT-ENCODING: header value
THttpSocketCompressSet = set of 0..31;
/// parent of THttpClientSocket and THttpServerSocket classes
// - contain properties for implementing HTTP/1.1 using the Socket API
// - handle chunking of body content
// - can optionaly compress and uncompress on the fly the data, with
// standard gzip/deflate or custom (synlzo/synlz) protocols
THttpSocket = class(TCrtSocket)
protected
/// used by RegisterCompress method
fCompress: THttpSocketCompressRecDynArray;
/// set by RegisterCompress method
fCompressAcceptEncoding: SockString;
/// GetHeader set index of protocol in fCompress[], from ACCEPT-ENCODING:
fCompressAcceptHeader: THttpSocketCompressSet;
/// same as HeaderGetValue('CONTENT-ENCODING'), but retrieved during Request
// and mapped into the fCompress[] array
fContentCompress: integer;
/// to call GetBody only once
fBodyRetrieved: boolean;
/// compress the data, adding corresponding headers via SockSend()
// - always add a 'Content-Length: ' header entry (even if length=0)
// - e.g. 'Content-Encoding: synlz' header if compressed using synlz
// - and if Data is not '', will add 'Content-Type: ' header
procedure CompressDataAndWriteHeaders(const OutContentType: SockString;
var OutContent: SockString; OutContentLength: PtrInt = -1);
public
/// TCP/IP prefix to mask HTTP protocol
// - if not set, will create full HTTP/1.0 or HTTP/1.1 compliant content
// - in order to make the TCP/IP stream not HTTP compliant, you can specify
// a prefix which will be put before the first header line: in this case,
// the TCP/IP stream won't be recognized as HTTP, and will be ignored by
// most AntiVirus programs, and increase security - but you won't be able
// to use an Internet Browser nor AJAX application for remote access any more
TCPPrefix: SockString;
/// will contain the first header line:
// - 'GET /path HTTP/1.1' for a GET request with THttpServer, e.g.
// - 'HTTP/1.0 200 OK' for a GET response after Get() e.g.
Command: SockString;
/// will contain all header lines after a Request
// - use HeaderGetValue() to get one HTTP header item value by name
Headers: SockString;
/// will contain the data retrieved from the server, after the Request
Content: SockString;
/// same as HeaderGetValue('CONTENT-LENGTH'), but retrieved during Request
// - is overridden with real Content length during HTTP body retrieval
ContentLength: integer;
/// same as HeaderGetValue('SERVER-INTERNALSTATE'), but retrieved during Request
// - proprietary header, used with our RESTful ORM access
ServerInternalState: integer;
/// same as HeaderGetValue('CONTENT-TYPE'), but retrieved during Request
ContentType: SockString;
/// same as HeaderGetValue('UPGRADE'), but retrieved during Request
Upgrade: SockString;
/// same as HeaderGetValue('X-POWERED-BY'), but retrieved during Request
XPoweredBy: SockString;
/// map the presence of some HTTP headers, but retrieved during Request
HeaderFlags: set of(transferChuked,
connectionClose, connectionUpgrade, connectionKeepAlive, hasRemoteIP);
/// retrieve the HTTP headers into Headers[] and fill most properties below
// - only relevant headers are retrieved, unless HeadersUnFiltered is set
procedure GetHeader(HeadersUnFiltered: boolean=false);
/// retrieve the HTTP body (after uncompression if necessary) into Content
procedure GetBody;
/// add an header 'name: value' entry
procedure HeaderAdd(const aValue: SockString);
/// set all Header values at once, from CRLF delimited text
procedure HeaderSetText(const aText: SockString;
const aForcedContentType: SockString='');
/// get all Header values at once, as CRLF delimited text
// - you can optionally specify a value to be added as 'RemoteIP: ' header
function HeaderGetText(const aRemoteIP: SockString=''): SockString;
/// HeaderGetValue('CONTENT-TYPE')='text/html', e.g.
// - supplied aUpperName should be already uppercased
function HeaderGetValue(const aUpperName: SockString): SockString;
/// will register a compression algorithm
// - used e.g. to compress on the fly the data, with standard gzip/deflate
// or custom (synlzo/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) before 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
function RegisterCompress(aFunction: THttpSocketCompress;
aCompressMinSize: integer=1024): boolean;
end;
THttpServer = class;
/// results of THttpServerSocket.GetRequest virtual method
// - return grError if the socket was not connected any more, or grException
// if any exception occured during the process
// - grOversizedPayload is returned when MaximumAllowedContentLength is reached
// - grRejected is returned when OnBeforeBody returned not 200
// - grTimeout is returned when HeaderRetrieveAbortDelay is reached
// - grHeaderReceived is returned for GetRequest({withbody=}false)
// - grBodyReceived is returned for GetRequest({withbody=}true)
// - grOwned indicates that this connection is now handled by another thread,
// e.g. asynchronous WebSockets
THttpServerSocketGetRequestResult = (
grError, grException, grOversizedPayload, grRejected, grTimeout,
grHeaderReceived, grBodyReceived, grOwned);
/// a genuine identifier for a given client connection on server side
// - maps http.sys ID, or is a genuine 31-bit value from increasing sequence
THttpServerConnectionID = Int64;
/// a dynamic array of client connection identifiers, e.g. for broadcasting
THttpServerConnectionIDDynArray = array of THttpServerConnectionID;
/// Socket API based HTTP/1.1 server class used by THttpServer Threads
THttpServerSocket = class(THttpSocket)
protected
fMethod: SockString;
fURL: SockString;
fKeepAliveClient: boolean;
fRemoteConnectionID: THttpServerConnectionID;
fServer: THttpServer;
public
/// create the socket according to a server
// - will register the THttpSocketCompress functions from the server
// - once created, caller should call AcceptRequest() to accept the socket
constructor Create(aServer: THttpServer); reintroduce;
/// main object function called after aClientSock := Accept + Create:
// - get Command, Method, URL, Headers and Body (if withBody is TRUE)
// - get sent data in Content (if withBody=true and ContentLength<>0)
// - returned enumeration will indicates the processing state
function GetRequest(withBody: boolean; headerMaxTix: Int64): THttpServerSocketGetRequestResult; virtual;
/// contains the method ('GET','POST'.. e.g.) after GetRequest()
property Method: SockString read fMethod;
/// contains the URL ('/' e.g.) after GetRequest()
property URL: SockString read fURL;
/// true if the client is HTTP/1.1 and 'Connection: Close' is not set
// - default HTTP/1.1 behavior is "keep alive", unless 'Connection: Close'
// is specified, cf. RFC 2068 page 108: "HTTP/1.1 applications that do not
// support persistent connections MUST include the "close" connection option
// in every message"
property KeepAliveClient: boolean read fKeepAliveClient write fKeepAliveClient;
/// the recognized connection ID, after a call to GetRequest()
// - identifies either the raw connection on the current server, or is
// a custom header value set by a local proxy, e.g.
// THttpServerGeneric.RemoteConnIDHeader='X-Conn-ID' for nginx
property RemoteConnectionID: THttpServerConnectionID read fRemoteConnectionID;
end;
/// 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
// - don't forget to use Free procedure when you are finished
THttpClientSocket = class(THttpSocket)
protected
fUserAgent: SockString;
fProcessName: SockString;
procedure RequestSendHeader(const url, method: SockString); virtual;
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;
/// 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, http status error otherwise
// - retry is false by caller, and will be recursively called with true to retry once
function Request(const url, method: SockString; KeepAlive: cardinal;
const header, Data, DataType: SockString; retry: boolean): integer; virtual;
/// after an Open(server,port), return 200 if OK, http status error otherwise
// - get the page data in Content
function Get(const url: SockString; KeepAlive: cardinal=0; const header: SockString=''): 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: SockString; KeepAlive: cardinal=0): integer;
/// 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: SockString; KeepAlive: cardinal=0; const header: SockString=''): integer;
/// after an Open(server,port), return 200,201,204 if OK, http status error otherwise
function Post(const url, Data, DataType: SockString; KeepAlive: cardinal=0;
const header: SockString=''): integer;
/// after an Open(server,port), return 200,201,204 if OK, http status error otherwise
function Put(const url, Data, DataType: SockString; KeepAlive: cardinal=0;
const header: SockString=''): integer;
/// after an Open(server,port), return 200,202,204 if OK, http status error otherwise
function Delete(const url: SockString; KeepAlive: cardinal=0; const header: SockString=''): integer;
/// 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: SockString read fUserAgent write fUserAgent;
/// the associated process name
property ProcessName: SockString read fProcessName write fProcessName;
end;
/// class-reference type (metaclass) of a HTTP client socket access
// - may be either THttpClientSocket or THttpClientWebSockets (from
// SynBidirSock unit)
THttpClientSocketClass = class of THttpClientSocket;
{$ifndef LVCL}
/// event prototype used e.g. by THttpServerGeneric.OnHttpThreadStart
TNotifyThreadEvent = procedure(Sender: TThread) of object;
{$endif}
{$M+}
TSynThreadPool = class;
/// a simple TThread with a "Terminate" event run in the thread context
// - the TThread.OnTerminate event is run within Synchronize() so did not
// match our expectations to be able to release the resources in the thread
// context which created them (e.g. for COM objects, or some DB drivers)
// - used internally by THttpServerGeneric.NotifyThreadStart() - you should
// not have to use the protected fOnThreadTerminate event handler
// - also define a Start method for compatibility with older versions of Delphi
TSynThread = class(TThread)
protected
// ensure fOnThreadTerminate is called only if NotifyThreadStart has been done
fStartNotified: TObject;
{$ifndef LVCL} // already available in LVCL
// we defined an fOnThreadTerminate event which would be run in the terminated
// thread context (whereas TThread.OnTerminate is called in the main thread)
// -> see THttpServerGeneric.OnHttpThreadTerminate event property
fOnThreadTerminate: TNotifyThreadEvent;
procedure DoTerminate; override;
{$endif}
public
/// initialize the server instance, in non suspended state
constructor Create(CreateSuspended: boolean); reintroduce; virtual;
{$ifndef HASTTHREADSTART}
/// method to be called when the thread was created as suspended
// - Resume is deprecated in the newest RTL, since some OS - e.g. Linux -
// do not implement this pause/resume feature
// - we define here this method for older versions of Delphi
procedure Start;
{$endif}
/// safe version of Sleep() which won't break the thread process
// - returns TRUE if the thread was Terminated
// - returns FALSE if successfully waited up to MS milliseconds
function SleepOrTerminated(MS: cardinal): boolean;
/// defined as public since may be used to terminate the processing methods
property Terminated;
end;
{$M-}
/// HTTP response Thread as used by THttpServer Socket API based class
// - Execute procedure get the request and calculate the answer, using
// the thread for a single client connection, until it is closed
// - you don't have to overload the protected THttpServerResp Execute method:
// override THttpServer.Request() function or, if you need a lower-level access
// (change the protocol, e.g.) THttpServer.Process() method itself
THttpServerResp = class(TSynThread)
protected
fServer: THttpServer;
fServerSock: THttpServerSocket;
fClientSock: TSocket;
fClientSin: TVarSin;
fConnectionID: THttpServerConnectionID;
/// main thread loop: read request from socket, send back answer
procedure Execute; override;
public
/// initialize the response thread for the corresponding incoming socket
// - this version will get the request directly from an incoming socket
constructor Create(aSock: TSocket; const aSin: TVarSin; aServer: THttpServer); reintroduce; overload;
/// initialize the response thread for the corresponding incoming socket
// - this version will handle KeepAlive, for such an incoming request
constructor Create(aServerSock: THttpServerSocket; aServer: THttpServer);
reintroduce; overload; virtual;
/// the associated socket to communicate with the client
property ServerSock: THttpServerSocket read fServerSock;
/// the associated main HTTP server instance
property Server: THttpServer read fServer;
/// the unique identifier of this connection
property ConnectionID: THttpServerConnectionID read fConnectionID;
end;
/// metaclass of HTTP response Thread
THttpServerRespClass = class of THttpServerResp;
{$ifdef MSWINDOWS}
// I/O completion ports API is the best option under Windows
// under Linux/POSIX, we fallback to a classical event-driven pool
{$define USE_WINIOCP}
{$endif MSWINDOWS}
/// defines the sub-threads used by TSynThreadPool
TSynThreadPoolWorkThread = class(TSynThread)
protected
fOwner: TSynThreadPool;
fNotifyThreadStartName: AnsiString;
fThreadNumber: integer;
{$ifndef USE_WINIOCP}
fProcessingContext: pointer;
fEvent: TEvent;
{$endif USE_WINIOCP}
procedure NotifyThreadStart(Sender: TSynThread);
procedure DoTask(Context: pointer); // exception-safe call of fOwner.Task()
public
/// initialize the thread
constructor Create(Owner: TSynThreadPool); reintroduce;
/// finalize the thread
destructor Destroy; override;
/// will loop for any pending task, and execute fOwner.Task()
procedure Execute; override;
end;
TSynThreadPoolWorkThreads = array of TSynThreadPoolWorkThread;
{$M+}
/// a simple Thread Pool, used e.g. for fast handling HTTP requests
// - implemented over I/O Completion Ports under Windows, or a classical
// Event-driven approach under Linux/POSIX
TSynThreadPool = class
protected
fWorkThread: TSynThreadPoolWorkThreads;
fWorkThreadCount: integer;
fRunningThreads: integer;
fExceptionsCount: integer;
fOnThreadTerminate: TNotifyThreadEvent;
fOnThreadStart: TNotifyThreadEvent;
fTerminated: boolean;
fContentionAbortCount: cardinal;
fContentionTime: Int64;
fContentionCount: cardinal;
fContentionAbortDelay: integer;
{$ifdef USE_WINIOCP}
fRequestQueue: THandle; // IOCSP has its own internal queue
{$else}
fQueuePendingContext: boolean;
fPendingContext: array of pointer;
fPendingContextCount: integer;
fSafe: TRTLCriticalSection;
function GetPendingContextCount: integer;
function PopPendingContext: pointer;
function QueueLength: integer; virtual;
{$endif USE_WINIOCP}
/// end thread on IO error
function NeedStopOnIOError: boolean; virtual;
/// process to be executed after notification
procedure Task(aCaller: TSynThread; aContext: Pointer); virtual; abstract;
procedure TaskAbort(aContext: Pointer); virtual;
public
/// initialize a thread pool with the supplied number of threads
// - abstract Task() virtual method will be called by one of the threads
// - up to 256 threads can be associated to a Thread Pool
// - can optionaly accept aOverlapHandle - a handle previously
// opened for overlapped I/O (IOCP) under Windows
// - aQueuePendingContext=true will store the pending context into
// an internal queue, so that Push() always returns true
constructor Create(NumberOfThreads: Integer=32;
{$ifdef USE_WINIOCP}aOverlapHandle: THandle=INVALID_HANDLE_VALUE
{$else}aQueuePendingContext: boolean=false{$endif});
/// shut down the Thread pool, releasing all associated threads
destructor Destroy; override;
/// let a task (specified as a pointer) be processed by the Thread Pool
// - returns false if there is no idle thread available in the pool and
// Create(aQueuePendingContext=false) was used (caller should retry later);
// if aQueuePendingContext was true in Create, or IOCP is used, the supplied
// context will be added to an internal list and handled when possible
// - if aWaitOnContention is default false, returns immediately when the
// queue is full; set aWaitOnContention=true to wait up to
// ContentionAbortDelay ms and retry to queue the task
function Push(aContext: pointer; aWaitOnContention: boolean=false): boolean;
{$ifndef USE_WINIOCP}
/// may be called after Push() returned false to see if queue was actually full
// - returns false if QueuePendingContext is false
function QueueIsFull: boolean;
/// parameter as supplied to Create constructor
property QueuePendingContext: boolean read fQueuePendingContext;
{$endif USE_WINIOCP}
/// low-level access to the threads defined in this thread pool
property WorkThread: TSynThreadPoolWorkThreads read fWorkThread;
published
/// how many threads have been defined in this thread pool
property WorkThreadCount: integer read fWorkThreadCount;
/// how many threads are currently running in this thread pool
property RunningThreads: integer read fRunningThreads;
/// how many tasks were rejected due to thread pool contention
// - if this number is high, consider setting a higher number of threads,
// or profile and tune the Task method
property ContentionAbortCount: cardinal read fContentionAbortCount;
/// milliseconds delay to reject a connection due to contention
// - default is 5000, i.e. 5 seconds wait for some room to be available
// in the IOCP or aQueuePendingContext internal list
// - during this delay, no new connection is available (i.e. Accept is not
// called), so that a load balancer could detect the contention and switch
// to another instance in the pool, or a direct client may eventually have
// its connection rejected, so won't start sending data
property ContentionAbortDelay: integer read fContentionAbortDelay
write fContentionAbortDelay;
/// total milliseconds spent waiting for an available slot in the queue
// - contention won't fail immediately, but will retry until ContentionAbortDelay
// - any high number here requires code refactoring of the Task method
property ContentionTime: Int64 read fContentionTime;
/// how many times the pool waited for an available slot in the queue
// - contention won't fail immediately, but will retry until ContentionAbortDelay
// - any high number here may better increase the threads count
// - use this property and ContentionTime to compute the average contention time
property ContentionCount: cardinal read fContentionCount;
{$ifndef USE_WINIOCP}
/// how many input tasks are currently waiting to be affected to threads
property PendingContextCount: integer read GetPendingContextCount;
{$endif}
end;
{$M-}
/// a simple Thread Pool, used for fast handling HTTP requests of a THttpServer
// - will handle multi-connection with less overhead than creating a thread
// for each incoming request
// - will create a THttpServerResp response thread, if the incoming request is
// identified as HTTP/1.1 keep alive, or HTTP body length is bigger than 1 MB
TSynThreadPoolTHttpServer = class(TSynThreadPool)
protected
fServer: THttpServer;
{$ifndef USE_WINIOCP}
function QueueLength: integer; override;
{$endif}
// here aContext is a THttpServerSocket instance
procedure Task(aCaller: TSynThread; aContext: Pointer); override;
procedure TaskAbort(aContext: Pointer); override;
public
/// initialize a thread pool with the supplied number of threads
// - Task() overridden method processs the HTTP request set by Push()
// - up to 256 threads can be associated to a Thread Pool
constructor Create(Server: THttpServer; NumberOfThreads: Integer=32); reintroduce;
end;
{$M+} // to have existing RTTI for published properties
THttpServerGeneric = class;
{$M-}
/// the server-side available authentication schemes
// - as used by THttpServerRequest.AuthenticationStatus
// - hraNone..hraKerberos will match low-level HTTP_REQUEST_AUTH_TYPE enum as
// defined in HTTP 2.0 API and
THttpServerRequestAuthentication = (
hraNone, hraFailed, hraBasic, hraDigest, hraNtlm, hraNegotiate, hraKerberos);
/// a generic input/output structure used for HTTP server requests
// - URL/Method/InHeaders/InContent properties are input parameters
// - OutContent/OutContentType/OutCustomHeader are output parameters
THttpServerRequest = class
protected
fRemoteIP, fURL, fMethod, fInHeaders, fInContent, fInContentType,
fAuthenticatedUser, fOutContent, fOutContentType, fOutCustomHeaders: SockString;
fServer: THttpServerGeneric;
fRequestID: integer;
fConnectionID: THttpServerConnectionID;
fConnectionThread: TSynThread;
fUseSSL: boolean;
fAuthenticationStatus: THttpServerRequestAuthentication;
{$ifdef MSWINDOWS}
fHttpApiRequest: Pointer;
fFullURL: SockUnicode;
{$endif}
public
/// low-level property which may be used during requests processing
Status: integer;
/// initialize the context, associated to a HTTP server instance
constructor Create(aServer: THttpServerGeneric;
aConnectionID: THttpServerConnectionID; aConnectionThread: TSynThread); virtual;
/// prepare an incoming request
// - will set input parameters URL/Method/InHeaders/InContent/InContentType
// - will reset output parameters
procedure Prepare(const aURL,aMethod,aInHeaders,aInContent,aInContentType,
aRemoteIP: SockString; aUseSSL: boolean=false);
/// append some lines to the InHeaders input parameter
procedure AddInHeader(additionalHeader: SockString);
{$ifdef MSWINDOWS}
/// input parameter containing the caller Full URL
property FullURL: SockUnicode read fFullURL;
{$endif}
/// input parameter containing the caller URI
property URL: SockString read fURL write fUrl;
/// input parameter containing the caller method (GET/POST...)
property Method: SockString read fMethod write fMethod;
/// input parameter containing the caller message headers