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

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

Overview
SHA1:f14b1b2be544f0f42bed3fa21e42ac12061b0b5c
Date: 2015-08-15 11:52:23
User: ab
Comment:{1792} BREAKING CHANGE: optimizations of our proprietary WebSockets binary protocol - compression/encryption is done for the whole jumbo-frame (if any), to reduce CPU and network bandwith - internal binary layout uses TFileBufferWriter to avoid most memory allocations - no change to the WebSockets JSON protocol
Tags And Properties
Context
2015-08-16
14:20
[b8f8661506] {1793} introducing jsonUnquotedPropNameCompact option to TTextWriter.AddJSONReformat() and its JSONBufferReformat() and JSONReformat() wrappers (user: ab, tags: trunk)
2015-08-15
11:52
[f14b1b2be5] {1792} BREAKING CHANGE: optimizations of our proprietary WebSockets binary protocol - compression/encryption is done for the whole jumbo-frame (if any), to reduce CPU and network bandwith - internal binary layout uses TFileBufferWriter to avoid most memory allocations - no change to the WebSockets JSON protocol (user: ab, tags: trunk)
11:35
[58f2ed96c3] {1791} fixed issue in TODBCStatement.ColumnBlob - thanks zed for the patch! (user: ab, tags: trunk)
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to SynBidirSock.pas.

155
156
157
158
159
160
161


162
163
164
165
166
167
168
...
267
268
269
270
271
272
273


274
275
276
277
278
279
280
...
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
...
728
729
730
731
732
733
734








735
736
737
738
739
740
741
...
824
825
826
827
828
829
830

831
832
833
834
835
836



837
838
839
840
841
842
843
844
....
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106





1107
1108
1109
1110
1111
1112
1113
....
1125
1126
1127
1128
1129
1130
1131

1132
1133
1134
1135
1136
1137
1138
1139
1140

1141


1142
1143










1144
1145
1146
1147
1148












1149
1150

1151
1152
1153
1154



1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175


1176












1177
1178
1179
1180
1181
1182
1183
1184
1185
1186

1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198


1199
1200
1201
1202
1203
1204
1205
1206
1207
....
1430
1431
1432
1433
1434
1435
1436


1437
1438
1439
1440
1441
1442
1443
....
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473


1474
1475
1476
1477
1478
1479
1480
  protected
    fName: RawUTF8;
    fURI: RawUTF8;
    procedure ProcessIncomingFrame(Sender: TWebSocketProcess;
      var request: TWebSocketFrame; const info: RawUTF8); virtual; abstract;
    function SendFrames(Owner: TWebSocketProcess;
      var Frames: TWebSocketFrameDynArray; var FramesCount: integer): boolean; virtual;


    function FrameIs(const frame: TWebSocketFrame; const Head: RawUTF8): boolean; virtual;
    function FrameType(const frame: TWebSocketFrame): RawUTF8; virtual;
  public
    /// abstract constructor to initialize the protocol
    // - the protocol should be named, so that the client may be able to request
    // for a given protocol
    // - if aURI is '', any URI would potentially upgrade to this protocol; you can
................................................................................
  protected
    fEncryption: TAESAbstract;
    fCompressed: boolean;
    procedure FrameCompress(const Head: RawUTF8; const Values: array of const;
      const Content,ContentType: RawByteString; var frame: TWebSocketFrame); override;
    function FrameDecompress(const frame: TWebSocketFrame; const Head: RawUTF8;
      const values: array of PRawByteString; var contentType,content: RawByteString): Boolean; override;


    function FrameIs(const frame: TWebSocketFrame; const Head: RawUTF8): boolean; override;
    function FrameType(const frame: TWebSocketFrame): RawUTF8; override;
    function SendFrames(Owner: TWebSocketProcess;
      var Frames: TWebSocketFrameDynArray; var FramesCount: integer): boolean; override;
    procedure ProcessIncomingFrame(Sender: TWebSocketProcess;
      var request: TWebSocketFrame; const info: RawUTF8); override;
  public
................................................................................
    fLastSocketTicks: Int64;
    fSettings: TWebSocketProcessSettings;
    fInvalidPingSendCount: cardinal;
    fProcessCount: integer;
    fSafeIn, fSafeOut: TSynLocker;
    /// low level WebSockets framing protocol
    function GetFrame(out Frame: TWebSocketFrame; TimeOut: cardinal; IgnoreExceptions: boolean): boolean;
    function SendFrame(const Frame: TWebSocketFrame): boolean;
    /// methods run e.g. by TWebSocketServerRest.WebSocketProcessLoop
    procedure ProcessStart; virtual;
    procedure ProcessStop; virtual;
    procedure ProcessLoop; virtual;
    function ComputeContext(out RequestProcess: TOnHttpServerRequest): THttpServerRequest; virtual; abstract;
    procedure HiResDelay(const start: Int64);
    procedure Log(const frame: TWebSocketFrame; const aMethodName: RawUTF8;
................................................................................
procedure TWebSocketProcessSettings.SetFullLog;
begin
  LogDetails := [logHeartbeat,logTextFrameContent];
end;


{ TWebSocketProtocol }









constructor TWebSocketProtocol.Create(const aName,aURI: RawUTF8);
begin
  fName := aName;
  fURI := aURI;
end;

................................................................................
  if Assigned(OnInComingFrame) and
     Sender.InheritsFrom(TWebSocketProcessServer) then
    OnIncomingFrame(TWebSocketProcessServer(Sender).fServerResp,request);
end;

function TWebSocketProtocolChat.SendFrame(Sender: THttpServerResp;
  const frame: TWebSocketFrame): boolean;

begin
  result := false;
  if (self=nil) or (Sender=nil) or TThreadHook(Sender).Terminated or
     not (Frame.opcode in [focText,focBinary]) then
    exit;
  if (Sender.Server as TWebSocketServer).IsActiveWebSocket(Sender)=Sender then



    result := (Sender as TWebSocketServerResp).fProcess.SendFrame(frame)
end;


{ TWebSocketProtocolRest }

procedure TWebSocketProtocolRest.ProcessIncomingFrame(Sender: TWebSocketProcess;
  var request: TWebSocketFrame; const info: RawUTF8);
................................................................................
  FreeAndNil(fEncryption);
  inherited;
end;

procedure TWebSocketProtocolBinary.FrameCompress(const Head: RawUTF8;
  const Values: array of const; const Content, ContentType: RawByteString;
  var frame: TWebSocketFrame);
var tmp,value: RawByteString;
    item: RawUTF8;
    i: integer;
begin
  frame.opcode := focBinary;
  for i := 0 to high(Values) do begin
    VarRecToUTF8(Values[i],item);
    tmp := tmp+item+#1;
  end;
  tmp := tmp+ContentType+#1+Content;
  if fCompressed then
    SynLZCompress(pointer(tmp),length(tmp),value,512) else
    value := tmp;
  if fEncryption<>nil then
    value := fEncryption.EncryptPKCS7(value,true);
  frame.payload := Head+#1+value;





end;

function TWebSocketProtocolBinary.FrameIs(const frame: TWebSocketFrame;
  const Head: RawUTF8): boolean;
var headLen: integer;
begin
  headLen := length(Head);
................................................................................
    i := 0 else
    i := PosEx(#1,frame.payload);
  if i=0 then
    result := '' else
    result := copy(frame.payload,1,i-1);
end;


function TWebSocketProtocolBinary.FrameDecompress(
  const frame: TWebSocketFrame; const Head: RawUTF8;
  const values: array of PRawByteString; var contentType,content: RawByteString): Boolean;
var tmp,value: RawByteString;
    i: integer;
    P: PUTF8Char;
begin
  result := false;
  if not FrameIs(frame,Head) then

    exit;


  tmp := copy(frame.payload,length(Head)+2,maxInt);
  if fEncryption<>nil then










    tmp := fEncryption.DecryptPKCS7(tmp,true);
  if fCompressed then
    SynLZDecompress(pointer(tmp),length(tmp),value) else
    value := tmp;
  if length(value)<4 then












    exit;
  P := pointer(value);

  for i := 0 to high(values) do
    values[i]^ := GetNextItem(P,#1);
  contentType := GetNextItem(P,#1);
  if P<>nil then



    SetString(content,P,length(value)-(P-pointer(value)));
  result := true;
end;

function TWebSocketProtocolBinary.SendFrames(Owner: TWebSocketProcess;
  var Frames: TWebSocketFrameDynArray; var FramesCount: integer): boolean;
var jumboFrame: TWebSocketFrame;
    fr: TDynArray;
begin
  if (FramesCount=0) or (Owner=nil) then begin
    result := true;
    exit;
  end;
  if FramesCount=1 then begin
    FramesCount := 0;
    result := Owner.SendFrame(Frames[0]);
    exit;
  end;
  fr.Init(TypeInfo(TWebSocketFrameDynArray),Frames);
  fr.UseExternalCount(FramesCount);
  jumboFrame.opcode := focBinary;


  jumboFrame.payload := 'frames'#1+fr.SaveTo; // each frame is already encrypted












  FramesCount := 0;
  Frames := nil;
  result := Owner.SendFrame(jumboFrame); // send all frames at once
end;

procedure TWebSocketProtocolBinary.ProcessIncomingFrame(Sender: TWebSocketProcess;
  var request: TWebSocketFrame; const info: RawUTF8);
var jumbo,jumboInfo: RawByteString;
    i: integer;
    frames: TWebSocketFrameDynArray;

begin
  if FrameIs(request,'frames') then begin
    jumbo := copy(request.payload,8,maxInt);
    if DynArrayLoad(frames,pointer(jumbo),TypeInfo(TWebSocketFrameDynArray))=nil then
      raise ESynBidirSocket.CreateUTF8(
        'Invalid content for %.ProcessIncomingFrame(frames)',[self]);
    for i := 0 to high(frames) do begin
      if i=0 then
        jumboInfo:= 'Sec-WebSocket-Frame: [0]' else
      if i=high(frames) then
        jumboInfo := 'Sec-WebSocket-Frame: [1]' else
        jumboInfo := '';


      Sender.Log(frames[i],'GetSubFrame');
      inherited ProcessIncomingFrame(Sender,frames[i],jumboInfo);
    end;
  end else
    inherited ProcessIncomingFrame(Sender,request,info);
end;


{ TWebSocketProtocolList }
................................................................................
      GetData(data);
      Frame.payload := Frame.payload+data;
    end;
    {$ifdef UNICODE}
    if opcode=focText then
      SetCodePage(Frame.payload,CP_UTF8,false); // identify text value as UTF-8
    {$endif}


    Log(frame,'GetFrame');
    SetLastPingTicks;
    result := true;
  finally
    fSafeIn.UnLock;
  end;
end;
................................................................................
  try
    fSettings.OnClientDisconnected(Self);
  except
  end;
end;

function TWebSocketProcess.SendFrame(
  const Frame: TWebSocketFrame): boolean;
var hdr: TFrameHeader;
    len: cardinal;
begin
  fSafeOut.Lock;
  try
    Log(frame,'SendFrame');
    try
      result := true;


      len := Length(Frame.payload);
      hdr.first := byte(Frame.opcode) or FRAME_FIN;
      if fMaskSentFrames<>0 then begin
        hdr.mask := (GetTickCount64 xor PtrInt(self))*Random(MaxInt);
        ProcessMask(pointer(Frame.payload),hdr.mask,len);
      end;
      if len<FRAME_LEN2BYTES then begin







>
>







 







>
>







 







|







 







>
>
>
>
>
>
>
>







 







>





|
>
>
>
|







 







<
|



|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>







 







>
|
<
<
|
<
<

<
<
>

>
>
|

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

|
>

|
|
<
>
>
>
|






|










<
<

>
>
|
>
>
>
>
>
>
>
>
>
>
>
>







|
|
|
>


|
<
|
|
|


|


>
>
|
|







 







>
>







 







|








>
>







155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
...
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
...
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
...
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
...
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
....
1100
1101
1102
1103
1104
1105
1106

1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
....
1145
1146
1147
1148
1149
1150
1151
1152
1153


1154


1155


1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174


1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192

1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213


1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243

1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
....
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
....
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
  protected
    fName: RawUTF8;
    fURI: RawUTF8;
    procedure ProcessIncomingFrame(Sender: TWebSocketProcess;
      var request: TWebSocketFrame; const info: RawUTF8); virtual; abstract;
    function SendFrames(Owner: TWebSocketProcess;
      var Frames: TWebSocketFrameDynArray; var FramesCount: integer): boolean; virtual;
    procedure AfterGetFrame(var frame: TWebSocketFrame); virtual;
    procedure BeforeSendFrame(var frame: TWebSocketFrame); virtual;
    function FrameIs(const frame: TWebSocketFrame; const Head: RawUTF8): boolean; virtual;
    function FrameType(const frame: TWebSocketFrame): RawUTF8; virtual;
  public
    /// abstract constructor to initialize the protocol
    // - the protocol should be named, so that the client may be able to request
    // for a given protocol
    // - if aURI is '', any URI would potentially upgrade to this protocol; you can
................................................................................
  protected
    fEncryption: TAESAbstract;
    fCompressed: boolean;
    procedure FrameCompress(const Head: RawUTF8; const Values: array of const;
      const Content,ContentType: RawByteString; var frame: TWebSocketFrame); override;
    function FrameDecompress(const frame: TWebSocketFrame; const Head: RawUTF8;
      const values: array of PRawByteString; var contentType,content: RawByteString): Boolean; override;
    procedure AfterGetFrame(var frame: TWebSocketFrame); override;
    procedure BeforeSendFrame(var frame: TWebSocketFrame); override;
    function FrameIs(const frame: TWebSocketFrame; const Head: RawUTF8): boolean; override;
    function FrameType(const frame: TWebSocketFrame): RawUTF8; override;
    function SendFrames(Owner: TWebSocketProcess;
      var Frames: TWebSocketFrameDynArray; var FramesCount: integer): boolean; override;
    procedure ProcessIncomingFrame(Sender: TWebSocketProcess;
      var request: TWebSocketFrame; const info: RawUTF8); override;
  public
................................................................................
    fLastSocketTicks: Int64;
    fSettings: TWebSocketProcessSettings;
    fInvalidPingSendCount: cardinal;
    fProcessCount: integer;
    fSafeIn, fSafeOut: TSynLocker;
    /// low level WebSockets framing protocol
    function GetFrame(out Frame: TWebSocketFrame; TimeOut: cardinal; IgnoreExceptions: boolean): boolean;
    function SendFrame(var Frame: TWebSocketFrame): boolean;
    /// methods run e.g. by TWebSocketServerRest.WebSocketProcessLoop
    procedure ProcessStart; virtual;
    procedure ProcessStop; virtual;
    procedure ProcessLoop; virtual;
    function ComputeContext(out RequestProcess: TOnHttpServerRequest): THttpServerRequest; virtual; abstract;
    procedure HiResDelay(const start: Int64);
    procedure Log(const frame: TWebSocketFrame; const aMethodName: RawUTF8;
................................................................................
procedure TWebSocketProcessSettings.SetFullLog;
begin
  LogDetails := [logHeartbeat,logTextFrameContent];
end;


{ TWebSocketProtocol }

procedure TWebSocketProtocol.AfterGetFrame(var frame: TWebSocketFrame);
begin // nothing done by default
end;

procedure TWebSocketProtocol.BeforeSendFrame(var frame: TWebSocketFrame);
begin // nothing done by default
end;

constructor TWebSocketProtocol.Create(const aName,aURI: RawUTF8);
begin
  fName := aName;
  fURI := aURI;
end;

................................................................................
  if Assigned(OnInComingFrame) and
     Sender.InheritsFrom(TWebSocketProcessServer) then
    OnIncomingFrame(TWebSocketProcessServer(Sender).fServerResp,request);
end;

function TWebSocketProtocolChat.SendFrame(Sender: THttpServerResp;
  const frame: TWebSocketFrame): boolean;
var tmp: TWebSocketFrame; // SendFrame() may change frame content
begin
  result := false;
  if (self=nil) or (Sender=nil) or TThreadHook(Sender).Terminated or
     not (Frame.opcode in [focText,focBinary]) then
    exit;
  if (Sender.Server as TWebSocketServer).IsActiveWebSocket(Sender)<>Sender then
    exit;
  tmp.opcode := frame.opcode;
  SetString(tmp.payload,PAnsiChar(Pointer(frame.payload)),length(frame.payload));
  result := (Sender as TWebSocketServerResp).fProcess.SendFrame(tmp)
end;


{ TWebSocketProtocolRest }

procedure TWebSocketProtocolRest.ProcessIncomingFrame(Sender: TWebSocketProcess;
  var request: TWebSocketFrame; const info: RawUTF8);
................................................................................
  FreeAndNil(fEncryption);
  inherited;
end;

procedure TWebSocketProtocolBinary.FrameCompress(const Head: RawUTF8;
  const Values: array of const; const Content, ContentType: RawByteString;
  var frame: TWebSocketFrame);

var item: RawUTF8;
    i: integer;
begin
  frame.opcode := focBinary;
  with TFileBufferWriter.Create(TRawByteStringStream) do
  try
    WriteBinary(Head);
    Write1(1);
    for i := 0 to high(Values) do
    with Values[i] do begin
      VarRecToUTF8(Values[i],item);
      Write(item);
    end;
    Write(ContentType);
    WriteBinary(Content);
    Flush;
    frame.payload := TRawByteStringStream(Stream).DataString;
  finally
    Free;
  end;
end;

function TWebSocketProtocolBinary.FrameIs(const frame: TWebSocketFrame;
  const Head: RawUTF8): boolean;
var headLen: integer;
begin
  headLen := length(Head);
................................................................................
    i := 0 else
    i := PosEx(#1,frame.payload);
  if i=0 then
    result := '' else
    result := copy(frame.payload,1,i-1);
end;


procedure TWebSocketProtocolBinary.BeforeSendFrame(var frame: TWebSocketFrame);


var value: RawByteString;


begin


  if frame.opcode<>focBinary then
    exit;
  if fCompressed then
    SynLZCompress(pointer(frame.payload),length(frame.payload),value,512) else
    value := frame.payload;
  if fEncryption<>nil then
    frame.payload := fEncryption.EncryptPKCS7(value,true) else
    frame.payload := value;
end;

procedure TWebSocketProtocolBinary.AfterGetFrame(var frame: TWebSocketFrame);
var value: RawByteString;
begin
  if frame.opcode<>focBinary then
    exit;
  if fEncryption<>nil then
    frame.payload := fEncryption.DecryptPKCS7(frame.payload,true);
  if fCompressed then begin
    SynLZDecompress(pointer(frame.payload),length(frame.payload),value);


    frame.payload := value;
  end;
end;

function TWebSocketProtocolBinary.FrameDecompress(const frame: TWebSocketFrame;
  const Head: RawUTF8; const values: array of PRawByteString;
  var contentType,content: RawByteString): Boolean;
var i: integer;
    P: PByte;
begin
  result := false;
  if not FrameIs(frame,Head) then
    exit;
  P := pointer(frame.payload);
  inc(P,Length(Head)+1);
  for i := 0 to high(values) do
    values[i]^ := FromVarString(P);
  contentType := FromVarString(P);

  i := length(frame.payload)-(PAnsiChar(P)-pointer(frame.payload));
  if i<0 then
    exit;
  SetString(content,PAnsiChar(P),i);
  result := true;
end;

function TWebSocketProtocolBinary.SendFrames(Owner: TWebSocketProcess;
  var Frames: TWebSocketFrameDynArray; var FramesCount: integer): boolean;
var jumboFrame: TWebSocketFrame;
    i: integer;
begin
  if (FramesCount=0) or (Owner=nil) then begin
    result := true;
    exit;
  end;
  if FramesCount=1 then begin
    FramesCount := 0;
    result := Owner.SendFrame(Frames[0]);
    exit;
  end;


  jumboFrame.opcode := focBinary;
  with TFileBufferWriter.Create(TRawByteStringStream) do
  try
    WriteBinary('frames'#1);
    dec(FramesCount);
    WriteVarUInt32(FramesCount);
    for i := 0 to FramesCount do
      if Frames[i].opcode=focBinary then
        Write(Frames[i].payload) else
        raise ESynBidirSocket.CreateUTF8('%.SendFrames[%]: Unexpected opcode=%',
          [self,i,ord(Frames[i].opcode)]);
    Flush;
    jumboFrame.payload := TRawByteStringStream(Stream).DataString;
  finally
    Free;
  end;
  FramesCount := 0;
  Frames := nil;
  result := Owner.SendFrame(jumboFrame); // send all frames at once
end;

procedure TWebSocketProtocolBinary.ProcessIncomingFrame(Sender: TWebSocketProcess;
  var request: TWebSocketFrame; const info: RawUTF8);
var jumboInfo: RawByteString;
    n,i: integer;
    frame: TWebSocketFrame;
    P: PByte;
begin
  if FrameIs(request,'frames') then begin
    P := pointer(request.payload);

    inc(P,7); // jump 'frames'#1
    n := FromVarUInt32(P);
    for i := 0 to n do begin
      if i=0 then
        jumboInfo:= 'Sec-WebSocket-Frame: [0]' else
      if i=n then
        jumboInfo := 'Sec-WebSocket-Frame: [1]' else
        jumboInfo := '';
      frame.opcode := focBinary;
      frame.payload := FromVarString(P);
      Sender.Log(frame,'GetSubFrame');
      inherited ProcessIncomingFrame(Sender,frame,jumboInfo);
    end;
  end else
    inherited ProcessIncomingFrame(Sender,request,info);
end;


{ TWebSocketProtocolList }
................................................................................
      GetData(data);
      Frame.payload := Frame.payload+data;
    end;
    {$ifdef UNICODE}
    if opcode=focText then
      SetCodePage(Frame.payload,CP_UTF8,false); // identify text value as UTF-8
    {$endif}
    if (fProtocol<>nil) and (Frame.payload<>'') then
      fProtocol.AfterGetFrame(Frame);
    Log(frame,'GetFrame');
    SetLastPingTicks;
    result := true;
  finally
    fSafeIn.UnLock;
  end;
end;
................................................................................
  try
    fSettings.OnClientDisconnected(Self);
  except
  end;
end;

function TWebSocketProcess.SendFrame(
  var Frame: TWebSocketFrame): boolean;
var hdr: TFrameHeader;
    len: cardinal;
begin
  fSafeOut.Lock;
  try
    Log(frame,'SendFrame');
    try
      result := true;
      if (fProtocol<>nil) and (Frame.payload<>'') then
        fProtocol.BeforeSendFrame(Frame);
      len := Length(Frame.payload);
      hdr.first := byte(Frame.opcode) or FRAME_FIN;
      if fMaskSentFrames<>0 then begin
        hdr.mask := (GetTickCount64 xor PtrInt(self))*Random(MaxInt);
        ProcessMask(pointer(Frame.payload),hdr.mask,len);
      end;
      if len<FRAME_LEN2BYTES then begin

Changes to SynopseCommit.inc.

1
'1.18.1791'
|
1
'1.18.1792'