mORMot and Open Source friends
Check-in [9098c15a11]
Not logged in

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

Overview
Comment:can make TCP/IP stream not HTTP compliant (against antivirus slowdown)
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 9098c15a115c3df6a3b48b6ce7fb89f2695b2072
User & Date: ab 2011-03-02 08:12:54
Context
2011-03-02
13:58
added horizontal scaling for GDI enumeration in case of text kerning (could occur for small fonts) check-in: 66177c6117 user: ab tags: trunk
08:12
can make TCP/IP stream not HTTP compliant (against antivirus slowdown) check-in: 9098c15a11 user: ab tags: trunk
2011-03-01
19:13
compatibility fix for Delphi 2009/2010/XE check-in: f35f39f17a user: ab tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to SQLite3/SQLite3HttpClient.pas.

77
78
79
80
81
82
83

84
85
86
87
88
89
90
..
96
97
98
99
100
101
102







103
104
105
106
107
108
109
...
142
143
144
145
146
147
148



149
150
151
152
153
154
155
      Version 1.13
        - now can compress its content using deflate or faster SynLZ algorithm:
          by default, the SynLZ algorithm will be used between a Delphi Client
          and Server over HTTP/1.1 - there will be no speed penalty on the
          server side, whereas deflate would use much more CPU 
        - by default, will handle SynLZ compression for TSQLite3HttpServer


}

interface

{$define COMPRESSSYNLZ}
{ if defined, will use SynLZ for content compression
................................................................................
  - is defined by default for a Delphi client }

{.$define COMPRESSDEFLATE}
{ if defined, will use deflate/zip for content compression
  - can be set globally for Client and Server applications
  - not defined by default for a Delphi client (SynLZ is more convenient) }








uses
  Windows,
  SysUtils,
{$ifdef COMPRESSDEFLATE}
  SynZip,
{$endif}
{$ifdef COMPRESSSYNLZ}
................................................................................
{ TSQLite3HttpClient }

constructor TSQLite3HttpClient.Create(const aServer, aPort: AnsiString;
  aModel: TSQLModel);
begin
  inherited Create(aModel);
  fSocket := THttpClientSocket.Open(aServer,aPort);



  KeepAliveMS := 20000; // 20 seconds connection keep alive by default
{$ifdef COMPRESSSYNLZ}
   // SynLZ is very fast and efficient, perfect for a Delphi Client
   fSocket.RegisterCompress(CompressSynLZ);
{$endif}
{$ifdef COMPRESSDEFLATE}
   fSocket.RegisterCompress(CompressDeflate);






>







 







>
>
>
>
>
>
>







 







>
>
>







77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
..
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
...
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
      Version 1.13
        - now can compress its content using deflate or faster SynLZ algorithm:
          by default, the SynLZ algorithm will be used between a Delphi Client
          and Server over HTTP/1.1 - there will be no speed penalty on the
          server side, whereas deflate would use much more CPU 
        - by default, will handle SynLZ compression for TSQLite3HttpServer
        - can make TCP/IP stream not HTTP compliant (against antivirus slowdown)

}

interface

{$define COMPRESSSYNLZ}
{ if defined, will use SynLZ for content compression
................................................................................
  - is defined by default for a Delphi client }

{.$define COMPRESSDEFLATE}
{ if defined, will use deflate/zip for content compression
  - can be set globally for Client and Server applications
  - not defined by default for a Delphi client (SynLZ is more convenient) }

{.$define USETCPPREFIX}
{ if defined, a prefix will be added to the TCP/IP stream so that it won't be
  valid HTTP content any more: it could increase the client/server speed with
  some anti-virus software, but the remote access won't work any more with
  Internet Browsers nor AJAX applications
  - not defined by default - should be set globally to the project conditionals } 

uses
  Windows,
  SysUtils,
{$ifdef COMPRESSDEFLATE}
  SynZip,
{$endif}
{$ifdef COMPRESSSYNLZ}
................................................................................
{ TSQLite3HttpClient }

constructor TSQLite3HttpClient.Create(const aServer, aPort: AnsiString;
  aModel: TSQLModel);
begin
  inherited Create(aModel);
  fSocket := THttpClientSocket.Open(aServer,aPort);
{$ifdef USETCPPREFIX}
  fSocket.TCPPrefix := 'magic';
{$endif}
  KeepAliveMS := 20000; // 20 seconds connection keep alive by default
{$ifdef COMPRESSSYNLZ}
   // SynLZ is very fast and efficient, perfect for a Delphi Client
   fSocket.RegisterCompress(CompressSynLZ);
{$endif}
{$ifdef COMPRESSDEFLATE}
   fSocket.RegisterCompress(CompressDeflate);

Changes to SQLite3/SQLite3HttpServer.pas.

85
86
87
88
89
90
91

92
93
94
95
96
97
98
...
106
107
108
109
110
111
112







113
114
115
116
117
118
119
...
325
326
327
328
329
330
331



332
333
334
335
336
337
338
          per one server (dispatching requests via the Root URI used)
        - new AddServer method, to register a TSQLRestServer after launch
        - new TSQLRestServer.OnlyJSONRequests property

      Version 1.13
        - now can compress its content using deflate or faster SynLZ algorithm
        - by default, will handle SynLZ compression for TSQLite3HttpClient

}

interface

{$define COMPRESSSYNLZ}
{ if defined, will use SynLZ for content compression
  - SynLZ is much faster than deflate/zip, so is preferred
................................................................................
{ if defined, will use deflate/zip for content compression
  - can be set global for Client and Server applications
  - SynLZ is faster but only known by Delphi clients: you can enable deflate
    when the server is connected an AJAX application (not defined by default)
  - if you define both COMPRESSSYNLZ and COMPRESSDEFLATE, the server will use
    SynLZ if available, and deflate if not called from a Delphi client }








{.$define HTTPEMBEDSQLITE3}
{ if defined, some additional methods will be included, in order to initialize
  directly a SQLite3 database engine
  -> it's cleaner to instantiate the TSQLRestServerDB instance first, then
    assign it to the TSQLite3HttpServer
  -> don't define it, even for TTestSQLite3Remote or TestSQL3.dpr }

................................................................................
      if aServers[j].Model.Root=Root then
        ErrMsg:= 'Duplicated Root URI';
  if ErrMsg<>'' then
     raise Exception.Create('TSQLite3HttpServer.Create: '+ErrMsg);
  SetLength(fDBServers,length(aServers));
  for i := 0 to high(aServers) do
    fDBServers[i] := aServers[i];



{$ifdef COMPRESSSYNLZ}
   RegisterCompress(CompressSynLZ);
{$endif}
{$ifdef COMPRESSDEFLATE}
   RegisterCompress(CompressDeflate);
{$endif}
end;






>







 







>
>
>
>
>
>
>







 







>
>
>







85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
...
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
...
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
          per one server (dispatching requests via the Root URI used)
        - new AddServer method, to register a TSQLRestServer after launch
        - new TSQLRestServer.OnlyJSONRequests property

      Version 1.13
        - now can compress its content using deflate or faster SynLZ algorithm
        - by default, will handle SynLZ compression for TSQLite3HttpClient
        - can make TCP/IP stream not HTTP compliant (against antivirus slowdown)
}

interface

{$define COMPRESSSYNLZ}
{ if defined, will use SynLZ for content compression
  - SynLZ is much faster than deflate/zip, so is preferred
................................................................................
{ if defined, will use deflate/zip for content compression
  - can be set global for Client and Server applications
  - SynLZ is faster but only known by Delphi clients: you can enable deflate
    when the server is connected an AJAX application (not defined by default)
  - if you define both COMPRESSSYNLZ and COMPRESSDEFLATE, the server will use
    SynLZ if available, and deflate if not called from a Delphi client }

{.$define USETCPPREFIX}
{ if defined, a prefix will be added to the TCP/IP stream so that it won't be
  valid HTTP content any more: it could increase the client/server speed with
  some anti-virus software, but the remote access won't work any more with
  Internet Browsers nor AJAX applications
  - not defined by default - should be set globally to the project conditionals } 

{.$define HTTPEMBEDSQLITE3}
{ if defined, some additional methods will be included, in order to initialize
  directly a SQLite3 database engine
  -> it's cleaner to instantiate the TSQLRestServerDB instance first, then
    assign it to the TSQLite3HttpServer
  -> don't define it, even for TTestSQLite3Remote or TestSQL3.dpr }

................................................................................
      if aServers[j].Model.Root=Root then
        ErrMsg:= 'Duplicated Root URI';
  if ErrMsg<>'' then
     raise Exception.Create('TSQLite3HttpServer.Create: '+ErrMsg);
  SetLength(fDBServers,length(aServers));
  for i := 0 to high(aServers) do
    fDBServers[i] := aServers[i];
{$ifdef USETCPPREFIX}
  TCPPrefix := 'magic';
{$endif}
{$ifdef COMPRESSSYNLZ}
   RegisterCompress(CompressSynLZ);
{$endif}
{$ifdef COMPRESSDEFLATE}
   RegisterCompress(CompressDeflate);
{$endif}
end;

Changes to SynCrtSock.pas.

309
310
311
312
313
314
315








316
317
318
319
320
321
322
...
500
501
502
503
504
505
506








507
508
509
510
511
512
513
....
1380
1381
1382
1383
1384
1385
1386


1387
1388
1389
1390
1391
1392
1393
....
1404
1405
1406
1407
1408
1409
1410






1411
1412
1413
1414
1415
1416
1417
....
1669
1670
1671
1672
1673
1674
1675


1676
1677
1678
1679
1680
1681
1682
....
2096
2097
2098
2099
2100
2101
2102






2103
2104
2105
2106
2107
2108
2109
....
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132


2133
2134
2135
2136
2137
2138
2139
    /// decode 'CONTENT-ENCODING: ' parameter
    procedure SetfCompressHeader(P: PAnsiChar);
    /// 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;
  public








    /// 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: TSockData;
    /// will contain the header lines after a Request - use HeaderValue() to get one
    Headers: array of TSockData;
    /// will contain the data retrieved from the server, after the Request
................................................................................
    Sock: TCrtSocket;
    /// will contain the total number of connection to the server
    // - it's the global count since the server started
    ServerConnectionCount: cardinal;
    /// time, in milliseconds, for the HTTP.1/1 connections to be kept alive;
    // default is 3000 ms
    ServerKeepAliveTimeOut: cardinal;









    /// create a Server Thread, binded and listening on a port
    // - this constructor will raise a EHttpServer exception if binding failed
    // - you can specify a number of threads to be initialized to handle
    // incoming connections (default is 32, which may be sufficient for most
    // cases, maximum is 64)
    constructor Create(const aPort: AnsiString
................................................................................
    DoRetry(404) else // socket closed (e.g. KeepAlive=0) -> reconnect
  try
  try
{$ifdef DEBUG23}system.write(' Send');{$endif}
    // send request - we use SockSend because writeln() is calling flush()
    // -> all header will be sent at once
    DataLen := length(Data);


    SockSend([method, ' ', url, ' HTTP/1.1'#13#10+
      'Accept: */*'#13#10'Host: ', Server]);
    if UserAgent='' then
      SockSend('User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows; FREE)') else
      SockSend(['User-Agent: ',UserAgent]);
    // for POST/PUT: always put content byte count to be sent (even 0)
    SockSend(['Content-Length: ', DataLen]);
................................................................................
    {$ifdef DEBUG23} SndBuf[SndBufLen+1] := #0; system.Writeln(#13#10'HeaderOut ',PAnsiChar(SndBuf));{$endif}
    SockSendFlush; // flush all pending data (i.e. headers) to network
    if DataLen<>0 then // for POST and PUT methods: content to be sent
      SndLow(pointer(Data),DataLen); // no CRLF at the end of data
{$ifdef DEBUG23}system.write('OK ');{$endif}
    // get headers
    SockRecvLn(Command); // will raise ECrtSocket on any error






{$ifdef DEBUG23}system.write(Command);{$endif}
    P := pointer(Command);
    if IdemPChar(P,'HTTP/1.') then begin
      if P[7]='0' then
        KeepAlive := 0; // HTTP/1.0 -> force connection close
      inc(P,9);
      result := GetCardinal(P); // get http numeric status code
................................................................................
  if not(Code in [200,201]) and (Data='') then begin
    Setlength(ClientSock.Headers,0);
    ClientSock.ContentType := 'text/html'; // create message to display
    Data := TSockData(ClassName+' Server Error '+IntToStr(Code)
      +'<hr>'+StringReplace(string(s),#13#10,'<br>',[rfReplaceAll]));
  end;
  // 1. send HTTP status command


  if ClientSock.KeepAliveClient then
    ClientSock.SockSend(['HTTP/1.1 ',Code,' OK']) else
    ClientSock.SockSend(['HTTP/1.0 ',Code,' OK']);
  // 2. send headers
  // 2.1. custom headers from Request() method
  for i := 0 to high(ClientSock.Headers) do begin
    s := ClientSock.Headers[i];
................................................................................
var P: PAnsiChar;
    StartTix, EndTix: cardinal;
begin
  try
    StartTix := GetTickCount;
    // 1st line is command: 'GET /path HTTP/1.1' e.g.
    SockRecvLn(Command);






    P := pointer(Command);
    Method := GetNextItem(P,' '); // 'GET'
    URL := GetNextItem(P,' ');    // '/path'
    KeepAliveClient := IdemPChar(P,'HTTP/1.1');
    Content := '';
    // get headers and content
    GetHeader;
................................................................................
end;


constructor THttpServerSocket.Create(aServer: THttpServer);
var i: integer;
begin
  inherited Create;
  if aServer<>nil then
    for i := 0 to aServer.fCompress.Count-1 do
      RegisterCompress(aServer.fCompress[i]);


end;

{ ECrtSocket }

constructor ECrtSocket.Create(const Msg: string);
begin
  inherited CreateFmt('%s %d',[Msg,WSAGetLastError]);






>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>







 







>
>







 







>
>
>
>
>
>







 







>
>







 







>
>
>
>
>
>







 







|


>
>







309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
...
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
....
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
....
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
....
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
....
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
....
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
    /// decode 'CONTENT-ENCODING: ' parameter
    procedure SetfCompressHeader(P: PAnsiChar);
    /// 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;
  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: TSockData;
    /// 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: TSockData;
    /// will contain the header lines after a Request - use HeaderValue() to get one
    Headers: array of TSockData;
    /// will contain the data retrieved from the server, after the Request
................................................................................
    Sock: TCrtSocket;
    /// will contain the total number of connection to the server
    // - it's the global count since the server started
    ServerConnectionCount: cardinal;
    /// time, in milliseconds, for the HTTP.1/1 connections to be kept alive;
    // default is 3000 ms
    ServerKeepAliveTimeOut: cardinal;
    /// 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: TSockData;

    /// create a Server Thread, binded and listening on a port
    // - this constructor will raise a EHttpServer exception if binding failed
    // - you can specify a number of threads to be initialized to handle
    // incoming connections (default is 32, which may be sufficient for most
    // cases, maximum is 64)
    constructor Create(const aPort: AnsiString
................................................................................
    DoRetry(404) else // socket closed (e.g. KeepAlive=0) -> reconnect
  try
  try
{$ifdef DEBUG23}system.write(' Send');{$endif}
    // send request - we use SockSend because writeln() is calling flush()
    // -> all header will be sent at once
    DataLen := length(Data);
    if TCPPrefix<>'' then
      SockSend(TCPPrefix);
    SockSend([method, ' ', url, ' HTTP/1.1'#13#10+
      'Accept: */*'#13#10'Host: ', Server]);
    if UserAgent='' then
      SockSend('User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows; FREE)') else
      SockSend(['User-Agent: ',UserAgent]);
    // for POST/PUT: always put content byte count to be sent (even 0)
    SockSend(['Content-Length: ', DataLen]);
................................................................................
    {$ifdef DEBUG23} SndBuf[SndBufLen+1] := #0; system.Writeln(#13#10'HeaderOut ',PAnsiChar(SndBuf));{$endif}
    SockSendFlush; // flush all pending data (i.e. headers) to network
    if DataLen<>0 then // for POST and PUT methods: content to be sent
      SndLow(pointer(Data),DataLen); // no CRLF at the end of data
{$ifdef DEBUG23}system.write('OK ');{$endif}
    // get headers
    SockRecvLn(Command); // will raise ECrtSocket on any error
    if TCPPrefix<>'' then
      if Command<>TCPPrefix then begin
        result :=  505;
        exit;
      end else
      SockRecvLn(Command);
{$ifdef DEBUG23}system.write(Command);{$endif}
    P := pointer(Command);
    if IdemPChar(P,'HTTP/1.') then begin
      if P[7]='0' then
        KeepAlive := 0; // HTTP/1.0 -> force connection close
      inc(P,9);
      result := GetCardinal(P); // get http numeric status code
................................................................................
  if not(Code in [200,201]) and (Data='') then begin
    Setlength(ClientSock.Headers,0);
    ClientSock.ContentType := 'text/html'; // create message to display
    Data := TSockData(ClassName+' Server Error '+IntToStr(Code)
      +'<hr>'+StringReplace(string(s),#13#10,'<br>',[rfReplaceAll]));
  end;
  // 1. send HTTP status command
  if ClientSock.TCPPrefix<>'' then
    ClientSock.SockSend(ClientSock.TCPPrefix);
  if ClientSock.KeepAliveClient then
    ClientSock.SockSend(['HTTP/1.1 ',Code,' OK']) else
    ClientSock.SockSend(['HTTP/1.0 ',Code,' OK']);
  // 2. send headers
  // 2.1. custom headers from Request() method
  for i := 0 to high(ClientSock.Headers) do begin
    s := ClientSock.Headers[i];
................................................................................
var P: PAnsiChar;
    StartTix, EndTix: cardinal;
begin
  try
    StartTix := GetTickCount;
    // 1st line is command: 'GET /path HTTP/1.1' e.g.
    SockRecvLn(Command);
    if TCPPrefix<>'' then
      if TCPPrefix<>Command then begin
        result := false;
        exit
      end else
      SockRecvLn(Command);
    P := pointer(Command);
    Method := GetNextItem(P,' '); // 'GET'
    URL := GetNextItem(P,' ');    // '/path'
    KeepAliveClient := IdemPChar(P,'HTTP/1.1');
    Content := '';
    // get headers and content
    GetHeader;
................................................................................
end;


constructor THttpServerSocket.Create(aServer: THttpServer);
var i: integer;
begin
  inherited Create;
  if aServer<>nil then begin
    for i := 0 to aServer.fCompress.Count-1 do
      RegisterCompress(aServer.fCompress[i]);
    TCPPrefix := aServer.TCPPrefix;  
  end;
end;

{ ECrtSocket }

constructor ECrtSocket.Create(const Msg: string);
begin
  inherited CreateFmt('%s %d',[Msg,WSAGetLastError]);

Changes to SynLZ.pas.

182
183
184
185
186
187
188
189

190
191
192
193
194
195
196
/// 2nd compression method optimizes pattern copy -> a bit smaller, but slower
function SynLZdecompress2(src: PAnsiChar; size: integer; dst: PAnsiChar): Integer;


/// compress a data content using the SynLZ algorithm
// - as expected by THttpSocket.RegisterCompress
// - will return 'synlz' as ACCEPT-ENCODING: header parameter
// - will use internaly a level compression of 1, i.e. fastest available

function CompressSynLZ(var Data: AnsiString; Compress: boolean): AnsiString;



implementation

{$ifndef FPC}






|
>







182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/// 2nd compression method optimizes pattern copy -> a bit smaller, but slower
function SynLZdecompress2(src: PAnsiChar; size: integer; dst: PAnsiChar): Integer;


/// compress a data content using the SynLZ algorithm
// - as expected by THttpSocket.RegisterCompress
// - will return 'synlz' as ACCEPT-ENCODING: header parameter
// - will store a hash of both compressed and uncompressed stream: if the
// data is corrupted during transmission, will instantly return ''
function CompressSynLZ(var Data: AnsiString; Compress: boolean): AnsiString;



implementation

{$ifndef FPC}

Changes to SynLZO.pas.

163
164
165
166
167
168
169
170
171


172
173
174
175
176
177
178
/// uncompress in_p(in_len) into out_p (must be allocated before call), returns out_len
// - may write up to out_len+3 bytes in out_p
// - the decompression mode is "fast-unsafe" -> CRC/Adler32 in_p data before call
function lzopas_decompress(in_p: PAnsiChar; in_len: integer; out_p: PAnsiChar): Integer;

/// (de)compress a data content using the SynLZO algorithm
// - as expected by THttpSocket.RegisterCompress
// - will use internaly a level compression of 1, i.e. fastest available
// - will return 'synlzo' as ACCEPT-ENCODING: header parameter


function CompressSynLZO(var Data: AnsiString; Compress: boolean): AnsiString;



{$ifdef LZOFILE}

{$ifdef WIN32}






<

>
>







163
164
165
166
167
168
169

170
171
172
173
174
175
176
177
178
179
/// uncompress in_p(in_len) into out_p (must be allocated before call), returns out_len
// - may write up to out_len+3 bytes in out_p
// - the decompression mode is "fast-unsafe" -> CRC/Adler32 in_p data before call
function lzopas_decompress(in_p: PAnsiChar; in_len: integer; out_p: PAnsiChar): Integer;

/// (de)compress a data content using the SynLZO algorithm
// - as expected by THttpSocket.RegisterCompress

// - will return 'synlzo' as ACCEPT-ENCODING: header parameter
// - will store a hash of both compressed and uncompressed stream: if the
// data is corrupted during transmission, will instantly return ''
function CompressSynLZO(var Data: AnsiString; Compress: boolean): AnsiString;



{$ifdef LZOFILE}

{$ifdef WIN32}