forked from synopse/mORMot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
SynCrtSock.pas
7371 lines (6874 loc) · 275 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 HTTP/1.1 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) 2015 Arnaud Bouchez
Synopse Informatique - http://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
http://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) 2015
the Initial Developer. All Rights Reserved.
Contributor(s):
- Alfred Glaenzer (alf)
- EMartin
- Eric Grange
- EvaF
- Maciej Izak (hnb)
- Marius Maximus
- Pavel (mpv)
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 *****
TCP/IP and HTTP/1.1 Client and Server
***************************************
Initial version: 2009 May, by Arnaud Bouchez
Version 1.4 - February 8, 2010
- whole Synopse SQLite3 database framework released under the GNU Lesser
General Public License version 3, instead of generic "Public Domain"
- fix a bug happening when multiple HTTP connections were opened and
closed in the same program
Version 1.5 - March 1, 2010
- new generic unix implementation, using libc sockets, in SynLibcSock.pas
Version 1.9
- avoid some GPF during client deconnection when the server shut down
- rewrite HTTP Server handle request loop keep alive timing
- HTTP Server now use a Thread Pool to speed up multi-connections: this
speed up a lot HTTP/1.0 requests, by creating a Thread only if
necessary
Version 1.9.2
- deleted deprecated DOS related code (formerly used with DWPL Dos Extender)
- a dedicated thread is now used if the incoming HTTP request has
POSTed a body content of more than 16 KB (to avoid Deny Of Service, and
preserve the Thread Pool to only real small processes)
- new CROnly parameter for TCrtSocket.SockRecvLn, to handle #13 as
line delimiter: by default, #10 or #13#10 are line delimiters
(as for normal Window/Linux text files)
Version 1.12
- added connection check and exception handling in
THttpServerSocket.GetRequest, which now is a function returning a boolean
- added DOS / TCP SYN Flood detection if THttpServerSocket.GetRequest
spent more than 2 seconds to get header from HTTP Client
Version 1.13
- code modifications to compile with Delphi 5 compiler
- new THttpApiServer class, using fast http.sys kernel-mode server
for better performance and less resource usage
- DOS / TCP SYN Flood detection time enhanced to 5 seconds
- fixed HTTP client stream layout (to be more RFC compliant)
- new generic compression handling mechanism: can handle gzip, deflate
or custom synlz / synlzo algorithms via THttpSocketCompress functions
- new THttpServerGeneric.Request virtual abstract method prototype
- new TWinINet class, using WinINet API (very slow, do not use)
- new TWinHTTP class, using WinHTTP API (faster than THttpClientSocket):
this is the class to be used
Version 1.15
- unit now tested with Delphi XE2 (32 Bit)
- fixed issue in HTTP_RESPONSE.SetHeaders()
Version 1.16
- fixed issue in case of wrong void parameter e.g. in THttpApiServer.AddUrl
- circumvent some bugs of Delphi XE2 background compiler (main compiler is OK)
- added 'RemoteIP: 127.0.0.1' to the retrieved HTTP headers
- major speed up of THttpApiServer for Windows Vista and up, by processing
huge content in chunks: upload of 100Mb file take 25 sec before and 6 sec
after changes, according to feedback by MPV - ticket 711247b998
- new THttpServerGeneric.OnHttpThreadTerminate event, available to clean-up
any process in the thread context, when it is terminated (to call e.g.
TSQLDBConnectionPropertiesThreadSafe.EndCurrentThread in order to call
CoUnInitialize from thread in which CoInitialize was initialy made) - see
http://synopse.info/fossil/tktview?name=213544b2f5
Version 1.17
- replaced TSockData string type to the generic RawByteString type (and
the default AnsiString for non-Unicode version of Delphi)
- added optional aProxyName, aProxyByPass parameters to TWinHttpAPI /
TWinInet and TWinHTTP constructors
- added THttpServerGeneric.OnHttpThreadStart property, and associated
TNotifyThreadEvent event prototype
- handle 'Range: bytes=***-***' request in THttpApiServer
Version 1.18
- replaced RawByteString type (defined locally for non-Unicode version of
Delphi) by a dedicated SockString type, used for data storage and for
simple ASCII content (like URIs or port number)
- added and tested Linux support (FPC/CrossKylix), via sockets or libcurl API
- introducing THttpServerRequest class for HTTP server context and THttpRequest
(replacing TWinHttpAPI) as abstract parent for HTTP client classes
- http.sys kernel-mode server now handles HTTP API 2.0 (available since
Windows Vista / Server 2008), or fall back to HTTP API 1.0 (for Windows XP
or Server 2003) - thanks pavel for the feedback and initial patch!
- deep code refactoring of thread process, especially for TSynThreadPool as
used by THttpServer: introducing TSynThread and TSynThreadPoolSubThread;
as a result, it fixes OnHttpThreadStart and OnHttpThreadTerminate to be
triggered from every thread, and propagated from THttpApiServer's clones
- added TCrtSocket.TCPNoDelay/SendTimeout/ReceiveTimeout/KeepAlive properties
- added optional parameter to set buffer size for TCrtSocket.CreateSockIn
and TCrtSocket.CreateSockOut methods
- renamed misleading TCrtSocket.Snd method as overloaded SockSend, and
refactored public fields as read/only properties
- added THttpServerRequest.UseSSL property to check if connection is secured
- added optional queue name for THttpApiServer.Create constructor [149cf42383]
- added THttpApiServer.RemoveUrl() method
- added THttpApiServer.HTTPQueueLength property (for HTTP API 2.0 only)
- added THttpApiServer.MaxBandwidth and THttpApiServer.MaxConnections
properties (for HTTP API 2.0 only) - thanks mpv for the proposal!
- added THttpApiServer.LogStart/LogStop for HTTP API 2.0 integrated logging
- introducing new THttpApiServer.SetAuthenticationSchemes() method for HTTP
API 2.0, and the corresponding THttpServerRequest.AuthenticationStatus and
AuthenticatedUser properties
- added THttpApiServer.SetTimeOutLimits() method for HTTP API 2.0
- added THttpApiServer.ServerSessionID and UrlGroupID read-only properties
- let HTTP_RESPONSE.AddCustomHeader() recognize all known headers
- THttpApiServer won't try to send an error message when connection is broken
- added error check for HttpSendHttpResponse() API call
- added EWinHTTP exception, raised when TWinHttp client fails to connect
- added aTimeOut optional parameter to TCrtSocket.Open() constructor
- added function HtmlEncode()
- some code cleaning about 64 bit compilation (including [540628f498])
- refactored HTTP_DATA_CHUNK record definition into HTTP_DATA_CHUNK_* records
to circumvent XE3 alignemnt issue
- WinSock-based THttpServer will avoid creating a thread per connection,
when the maximum of 64 threads is reached in the pool, with an exception
of kept-alife or huge body requets (avoiding DoS attacks by limiting the
total number of created threads)
- allow WinSock-based THttpServer to set a server address ('1.2.3.4:1234')
- let WinSock-based THttpServer.Process() handle HTTP_RESP_STATICFILE
- force disable range checking and other compiler options for this unit
- included more detailed information to HTTP client User-Agent header
- added ConnectionTimeOut, SendTimeout and ReceiveTimeout optional parameters
to THttpRequest constructors - feature request [bfe485b678]
- added optional aCompressMinSize parameter to RegisterCompress() methods
- added THttpRequest.Get/Post/Put/Delete() class functions for easy remote
resource retrieval using either WinHTTP or WinINet APIs
- added TURI structure, ready to parse a supplied HTTP URI
- added ConnectionID the HTTP server context - see request [0636eeec54]
- added THttpRequest.IgnoreSSLCertificateErrors property (proposal by EMartin)
- added THttpRequest AuthScheme and AuthUserName/AuthPassword properties, for
authentication - only implemented at TWinHttp level (thanks Eric Grange)
- fixed TCrtSocket.BytesIn and TCrtSocket.BytesOut properties
- fixed ticket [82df275784] THttpRequest with responses without Content-Length
- fixed ticket [f0749956af] TWinINet does not work with HTTPS servers
- fixed ticket [842a5ae15a] THttpApiServer.Execute/SendError message
- fixed ticket [f2ae4022a4] EWinINet error handling
- fixed ticket [73da2c17b1] about Accept-Encoding header in THttpApiServer
- fixed ticket [cbcbb3b2fc] about PtrInt definition
- fixed ticket [91f8f3ec6f] about error retrieving unknown headers
- fixed ticket [f79ff5714b] about potential finalization issues as .bpl in IDE
- fixed ticket [2d53fc43e3] about unneeded port 80
- fixed ticket [11b327bd77] about TCrtSocket not working with Delphi 2009+
- fixed ticket [0f6ecdaf55] for better compatibility with HTTP/1.1 cache
- fixed ticket [814f6bd65a] about missing OnHttpThreadStart in CreateClone
- fixed ticket [51a9c086f3] about THttpApiServer.SetHTTPQueueLength()
- fixed potential Access Violation error at THttpServerResp shutdown
- removed several compilation hints when assertions are set to off
- added aRegisterURI optional parameter to THttpApiServer.AddUrl() method
- made exception error messages more explicit (tuned per module)
- fixed several issues when releasing THttpApiServer and THttpServer instances
- allow to use any Unicode content for SendEmail() - also includes
SendEmailSubject() function, for feature request [0a5fdf9129]
}
{$I Synopse.inc} // define HASINLINE USETYPEINFO CPU32 CPU64 OWNNORMTOUPPER
interface
{.$define DEBUGAPI}
{.$define DEBUG23}
{$ifdef DEBUG2}
{.$define DEBUG}
{$endif}
uses
{$ifdef MSWINDOWS}
Windows,
SynWinSock,
{$ifdef USEWININET}
WinInet,
{$endif}
{$else MSWINDOWS}
{$undef USEWININET}
{$ifdef CONDITIONALEXPRESSIONS}
Types,
{$endif}
{$ifdef FPC}
Sockets,
SynFPCSock,
SynFPCLinux,
{$endif}
{$ifdef KYLIX3}
LibC,
KernelIoctl,
SynFPCSock, // shared with Kylix
SynKylix,
{$endif}
{$endif MSWINDOWS}
{$ifndef LVCL}
Contnrs,
{$endif}
SysUtils,
Classes;
const
/// the current version number of the freeware Synopse framework
// - match the value defined in SynCommons.pas
SYNOPSE_FRAMEWORK_VERSION = {$I SynopseCommit.inc};
/// the full text of the current Synopse mORMot framework version
XPOWEREDPROGRAM = 'Synopse mORMot '+SYNOPSE_FRAMEWORK_VERSION;
/// used by THttpApiServer.Request for http.sys to send a static file
// - the OutCustomHeader should contain the proper 'Content-type: ....'
// corresponding to the file (e.g. by calling GetMimeContentType() function
// from SynCommons supplyings the file name)
// - should match HTML_CONTENT_STATICFILE constant defined in mORMot.pas unit
HTTP_RESP_STATICFILE = '!STATICFILE';
/// 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';
/// THttpRequest timeout default value for DNS resolution
// - leaving to 0 will let system default value be used
HTTP_DEFAULT_RESOLVETIMEOUT = 0;
/// THttpRequest timeout default value for remote connection
// - default is 60 seconds
HTTP_DEFAULT_CONNECTTIMEOUT = 60000;
/// THttpRequest timeout default value for data sending
// - default is 30 seconds
// - you can override this value by setting the corresponding parameter in
// THttpRequest.Create() constructor
HTTP_DEFAULT_SENDTIMEOUT = 30000;
/// THttpRequest timeout default value for data receiving
// - default is 30 seconds
// - you can override this value by setting the corresponding parameter in
// THttpRequest.Create() constructor
HTTP_DEFAULT_RECEIVETIMEOUT = 30000;
type
{$ifdef UNICODE}
/// define the fastest Unicode string type of the compiler
SynUnicode = UnicodeString;
/// define a raw storage string type, used for data buffer management
SockString = type RawByteString;
{$else}
/// define the fastest Unicode string type of the compiler
SynUnicode = WideString;
/// define a raw storage string type, used for data buffer management
SockString = type AnsiString;
{$endif}
{$ifndef CONDITIONALEXPRESSIONS}
// not defined in Delphi 5 or older
PPointer = ^Pointer;
TTextLineBreakStyle = (tlbsLF, tlbsCRLF);
UTF8String = AnsiString;
UTF8Encode = AnsiString;
{$endif}
{$ifndef FPC}
/// FPC 64 compatibility integer type
{$ifdef UNICODE}
PtrInt = NativeInt;
PtrUInt = NativeUInt;
{$else}
PtrInt = integer;
PtrUInt = cardinal;
{$endif}
/// FPC 64 compatibility pointer type
PPtrInt = ^PtrInt;
PPtrUInt = ^PtrUInt;
{$endif}
/// exception thrown by the classes of this unit
ECrtSocket = class(Exception)
protected
fSocketError: 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;
/// the associated WSAGetLastError value
property SocketError: integer read fSocketError;
end;
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: cardinal;
fBytesIn: Int64;
fBytesOut: Int64;
fSockInEof: boolean;
/// updated by every SockSend() call
fSndBuf: SockString;
fSndBufLen: integer;
/// close and shutdown the connection (called from Destroy)
procedure Close;
procedure SetInt32OptionByIndex(OptName, OptVal: integer);
public
/// common initialization of all constructors
// - do not call directly, but use Open / Bind constructors instead
constructor Create(aTimeOut: cardinal=10000); reintroduce; virtual;
/// connect to aServer:aPort
constructor Open(const aServer, aPort: SockString; aLayer: TCrtSocketLayer=cslTCP;
aTimeOut: cardinal=10000);
/// bind to a Port
// - expects the port to be specified as Ansi string, e.g. '1234'
// - you can optionally specify a server address to bind to, e.g.
// '1.2.3.4:1234'
constructor Bind(const aPort: SockString; aLayer: TCrtSocketLayer=cslTCP);
/// low-level internal method called by Open() and Bind() constructors
// - raise an ECrtSocket exception on error
procedure OpenBind(const aServer, aPort: SockString; doBind: boolean;
aSock: integer=-1; aLayer: TCrtSocketLayer=cslTCP);
/// 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);
/// 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
// - will wait up to the specified aTimeOut value (in milliseconds) for
// incoming data
// - returns -1 in case of a socket error (e.g. broken connection); you
// can raise a ECrtSocket exception to propagate the error
function SockInPending(aTimeOut: integer): integer;
/// check the connection status of the socket
function SockConnected: boolean;
/// simulate writeln() with direct use of Send(Sock, ..)
// - 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
procedure SockSend(const Line: SockString=''); overload;
/// append P^ data into SndBuf (used by SockSend(), e.g.)
// - call SockSendFlush to send it through the network via SndLow()
procedure SockSend(P: pointer; Len: integer); overload;
/// flush all pending data to be sent
procedure SockSendFlush;
/// 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
function SockReceivePending(TimeOut: cardinal): 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 error, true on success
function TrySockRecv(Buffer: pointer; Length: integer): boolean;
/// call readln(SockIn^,Line) 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
// - 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 CR/CRLF is appened to the block
procedure Write(const Data: SockString);
/// 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 http://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 http://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 http://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 http://msdn.microsoft.com/en-us/library/windows/desktop/ee470551
property KeepAlive: Integer index SO_KEEPALIVE 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,s) to send a line to the opened socket
property SockOut: PTextFile read fSockOut;
published
/// initialized after Open() with socket
property Sock: TSocket read fSock;
/// initialized after Open() with Server name
property Server: SockString read fServer;
/// 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: cardinal 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
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
/// true if the TRANSFER-ENCODING: CHUNKED was set in headers
Chunked: boolean;
/// to call GetBody only once
fBodyRetrieved: boolean;
/// used by RegisterCompress method
fCompress: THttpSocketCompressRecDynArray;
/// set by RegisterCompress method
fCompressAcceptEncoding: SockString;
/// GetHeader set index of protocol in fCompress[], from ACCEPT-ENCODING:
fCompressHeader: THttpSocketCompressSet;
/// same as HeaderValue('Content-Encoding'), but retrieved during Request
// and mapped into the fCompress[] array
fContentCompress: integer;
/// retrieve the HTTP headers into Headers[] and fill most properties below
procedure GetHeader;
/// retrieve the HTTP body (after uncompression if necessary) into Content
procedure GetBody;
/// 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);
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 the header lines after a Request - use HeaderValue() to get one
Headers: array of SockString;
/// will contain the data retrieved from the server, after the Request
Content: SockString;
/// same as HeaderValue('Content-Length'), but retrieved during Request
// - is overridden with real Content length during HTTP body retrieval
ContentLength: integer;
/// same as HeaderValue('Content-Type'), but retrieved during Request
ContentType: SockString;
/// same as HeaderValue('Connection')='Close', but retrieved during Request
ConnectionClose: boolean;
/// same as HeaderValue('Connection')='Upgrade', but retrieved during Request
ConnectionUpgrade: boolean;
/// add an header entry, returning the just entered entry index in Headers[]
function HeaderAdd(const aValue: SockString): integer;
/// set all Header values at once, from CRLF delimited text
procedure HeaderSetText(const aText: SockString);
/// get all Header values at once, as CRLF delimited text
function HeaderGetText: SockString; virtual;
/// HeaderValue('Content-Type')='text/html', e.g.
function HeaderValue(aName: 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;
/// Socket API based HTTP/1.1 server class used by THttpServer Threads
THttpServerSocket = class(THttpSocket)
private
public
/// contains the method ('GET','POST'.. e.g.) after GetRequest()
Method: SockString;
/// contains the URL ('/' e.g.) after GetRequest()
URL: SockString;
/// 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")
KeepAliveClient: boolean;
/// the recognized client IP, after a call to InitRequest()
RemoteIP: SockString;
/// create the socket according to a server
// - will register the THttpSocketCompress functions from the server
constructor Create(aServer: THttpServer); reintroduce;
/// main object function called after aClientSock := Accept + Create:
// - get initialize the socket with the supplied accepted socket
// - caller will then use the GetRequest method below to
// get the request
procedure InitRequest(aClientSock: TSocket);
/// 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 ContentLength<>0)
// - return false if the socket was not connected any more, or if
// any exception occured during the process
function GetRequest(withBody: boolean=true): boolean;
/// get all Header values at once, as CRLF delimited text
// - this overridden version will add the 'RemoteIP: 1.2.3.4' header
function HeaderGetText: SockString; override;
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;
procedure RequestSendHeader(const url, method: SockString); virtual;
public
/// common initialization of all constructors
// - this overridden method will set the UserAgent with some default value
constructor Create(aTimeOut: cardinal=10000); 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 - 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;
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+}
/// 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 fOnTerminate event handler
// - also define a Start method for compatibility with older versions of Delphi
TSynThread = class(TThread)
protected
// ensure fOnTerminate is called only if NotifyThreadStart has been done
fStartNotified: TObject;
{$ifndef LVCL} // already available in LVCL
// we re-defined an fOnTerminate event which would be run in the terminated
// thread context (whereas TThread.OnTerminate is called in the main thread)
// -> see THttpServerGeneric.OnHttpThreadTerminate event property
fOnTerminate: TNotifyThreadEvent;
procedure DoTerminate; override;
{$endif}
public
{$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}
end;
{$M-}
{$ifdef USETHREADPOOL}
TSynThreadPoolTHttpServer = class;
{$endif}
/// HTTP response Thread as used by THttpServer Socket API based class
// - Execute procedure get the request and calculate the answer
// - 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;
{$ifdef USETHREADPOOL}
fThreadPool: TSynThreadPoolTHttpServer;
{$endif}
fClientSock: TSocket;
fConnectionID: integer;
/// 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; aServer: THttpServer); 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
{$ifdef USETHREADPOOL}; aThreadPool: TSynThreadPoolTHttpServer{$endif});
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: integer read fConnectionID;
end;
THttpServerRespClass = class of THttpServerResp;
{$ifdef USETHREADPOOL}
TSynThreadPool = class;
/// defines the sub-threads used by TSynThreadPool
TSynThreadPoolSubThread = class(TSynThread)
protected
fOwner: TSynThreadPool;
public
/// initialize the thread
constructor Create(Owner: TSynThreadPool);
/// will loop for any pending IOCP commands, and execute fOwner.Task()
procedure Execute; override;
end;
/// a simple Thread Pool, used for fast handling HTTP requests
// - will handle multi-connection with less overhead than creating a thread
// for each incoming request
// - this Thread Pool is implemented over I/O Completion Ports, which is a faster
// method than keeping a TThread list, and resume them on request: I/O completion
// just has the thread running while there is pending tasks, with no pause/resume
TSynThreadPool = class
protected
FRequestQueue: THandle;
FThread: TObjectList; // of TSynThreadPoolSubThread
FThreadID: array[0..63] of THandle; // WaitForMultipleObjects() limit=64
FGeneratedThreadCount: integer;
FOnHttpThreadTerminate: TNotifyThreadEvent;
/// process to be executed after notification
procedure Task(aCaller: TSynThreadPoolSubThread; aContext: Pointer); virtual; abstract;
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 64 threads can be associated to a Thread Pool
constructor Create(NumberOfThreads: Integer=32);
/// shut down the Thread pool, releasing all associated threads
destructor Destroy; override;
end;
/// a simple Thread Pool, used for fast handling HTTP requests of a THttpServer
// - will create a THttpServerResp response thread, if the incoming request
// is identified as HTTP/1.1 keep alive
TSynThreadPoolTHttpServer = class(TSynThreadPool)
protected
fServer: THttpServer;
procedure Task(aCaller: TSynThreadPoolSubThread; 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 64 threads can be associated to a Thread Pool
constructor Create(Server: THttpServer; NumberOfThreads: Integer=32); reintroduce;
/// add an incoming HTTP request to the Thread Pool
function Push(aClientSock: TSocket): Boolean;
end;
{$endif USETHREADPOOL}
{$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
fURL, fMethod, fInHeaders, fInContent, fInContentType: SockString;
fOutContent, fOutContentType, fOutCustomHeaders: SockString;
fServer: THttpServerGeneric;
fConnectionID: Int64;
fConnectionThread: TSynThread;
fUseSSL: boolean;
fAuthenticationStatus: THttpServerRequestAuthentication;
fAuthenticatedUser: SockString;
public
/// initialize the context, associated to a HTTP server instance
constructor Create(aServer: THttpServerGeneric; aConnectionID: Int64;
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: SockString);
/// append some lines to the InHeaders input parameter
procedure AddInHeader(additionalHeader: SockString);
/// input parameter containing the caller URI
property URL: SockString read fURL;
/// input parameter containing the caller method (GET/POST...)
property Method: SockString read fMethod;
/// input parameter containing the caller message headers
property InHeaders: SockString read fInHeaders;
/// input parameter containing the caller message body
// - e.g. some GET/POST/PUT JSON data can be specified here
property InContent: SockString read fInContent;
// input parameter defining the caller message body content type
property InContentType: SockString read fInContentType;
/// output parameter to be set to the response message body
property OutContent: SockString read fOutContent write fOutContent ;
/// output parameter to define the reponse message body content type
// - if OutContentType is HTTP_RESP_STATICFILE (i.e. '!STATICFILE', defined
// as STATICFILE_CONTENT_TYPE in mORMot.pas), then OutContent is the UTF-8
// file name of a file which must be sent to the client via http.sys (much
// faster than manual buffering/sending)
// - if OutContentType is HTTP_RESP_NORESPONSE (i.e. '!NORESPONSE', defined
// as NORESPONSE_CONTENT_TYPE in mORMot.pas), then the actual transmission
// protocol may not wait for any answer - used e.g. for WebSockets
property OutContentType: SockString read fOutContentType write fOutContentType;
/// output parameter to be sent back as the response message header
// - e.g. to set Content-Type/Location
property OutCustomHeaders: SockString read fOutCustomHeaders write fOutCustomHeaders;
/// the associated server instance
// - may be a THttpServer or a THttpApiServer class
property Server: THttpServerGeneric read fServer;
/// the ID of the connection which called this execution context
// - e.g. SynCrtSock's TWebSocketProcess.NotifyCallback method would use
// this property to specify the client connection to be notified
// - is set as an Int64 to match http.sys ID type, but will be an
// increasing integer sequence for (web)socket-based servers
property ConnectionID: Int64 read fConnectionID;
/// the thread which owns the connection of this execution context
// - depending on the HTTP server used, may not follow ConnectionID
property ConnectionThread: TSynThread read fConnectionThread;
/// is TRUE if the caller is connected via HTTPS
// - only set for THttpApiServer class yet
property UseSSL: boolean read fUseSSL;
/// contains the THttpServer-side authentication status
// - e.g. when using http.sys authentication with HTTP API 2.0
property AuthenticationStatus: THttpServerRequestAuthentication
read fAuthenticationStatus;
/// contains the THttpServer-side authenticated user name, UTF-8 encoded
// - e.g. when using http.sys authentication with HTTP API 2.0, the
// domain user name is retrieved from the supplied AccessToken
// - could also be set by the THttpServerGeneric.Request() method, after
// proper authentication, so that it would be logged as expected
property AuthenticatedUser: SockString read fAuthenticatedUser;
end;
/// event handler used by THttpServerGeneric.OnRequest property
// - Ctxt defines both input and output parameters
// - result of the function is the HTTP error code (200 if OK, e.g.)
// - OutCustomHeader will handle Content-Type/Location
// - if OutContentType is HTTP_RESP_STATICFILE (i.e. '!STATICFILE' aka
// STATICFILE_CONTENT_TYPE in mORMot.pas), then OutContent is the UTF-8 file
// name of a file which must be sent to the client via http.sys (much faster
// than manual buffering/sending) and the OutCustomHeader should
// contain the proper 'Content-type: ....'
TOnHttpServerRequest = function(Ctxt: THttpServerRequest): cardinal of object;
{$M+} { to have existing RTTI for published properties }
/// abstract class to implement a HTTP server
// - do not use this class, but rather the THttpServer or THttpApiServer
THttpServerGeneric = class(TSynThread)
protected
/// optional event handler for the virtual Request method
fOnRequest: TOnHttpServerRequest;
/// list of all registered compression algorithms
fCompress: THttpSocketCompressRecDynArray;
/// set by RegisterCompress method
fCompressAcceptEncoding: SockString;
fOnHttpThreadStart: TNotifyThreadEvent;
fServerName: SockString;
fCurrentConnectionID: integer;
fCanNotifyCallback: boolean;
procedure SetOnTerminate(const Event: TNotifyThreadEvent); virtual;
function GetAPIVersion: string; virtual; abstract;
procedure NotifyThreadStart(Sender: TSynThread);
procedure SetServerName(const aName: SockString); virtual;
function NextConnectionID: integer;
public
/// initialize the server instance, in non suspended state
constructor Create(CreateSuspended: Boolean); reintroduce;
/// override this function to customize your http server
// - InURL/InMethod/InContent properties are input parameters
// - OutContent/OutContentType/OutCustomHeader are output parameters
// - result of the function is the HTTP error code (200 if OK, e.g.),
// - OutCustomHeader is available to handle Content-Type/Location
// - if OutContentType is HTTP_RESP_STATICFILE (i.e. '!STATICFILE' or
// STATICFILE_CONTENT_TYPE defined in mORMot.pas), then OutContent is the
// UTF-8 file name of a file which must be sent to the client via http.sys
// (much faster than manual buffering/sending) and the OutCustomHeader should
// contain the proper 'Content-type: ....'
// - default implementation is to call the OnRequest event (if existing),
// and will return STATUS_NOTFOUND if OnRequest was not set
// - warning: this process must be thread-safe (can be called by several
// threads simultaneously, but with a given Ctxt instance for each)
function Request(Ctxt: THttpServerRequest): cardinal; virtual;
/// will register a compression algorithm
// - used e.g. to compress on the fly the data, with standard gzip/deflate
// or custom (synlzo/synlz) protocols
// - 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
procedure RegisterCompress(aFunction: THttpSocketCompress;
aCompressMinSize: integer=1024); virtual;
/// event handler called by the default implementation of the
// virtual Request method
// - warning: this process must be thread-safe (can be called by several