Skip to content
Permalink
Browse files

small fixes and enhancements to HTTP connection

  • Loading branch information
Arnaud Bouchez
Arnaud Bouchez committed Jan 30, 2020
1 parent 94a6f16 commit ee840ed0dc67dc3c5646165954c829beb9cf9bb7
Showing with 69 additions and 23 deletions.
  1. +51 −11 SynBidirSock.pas
  2. +17 −11 SynCrtSock.pas
  3. +1 −1 SynopseCommit.inc
@@ -73,14 +73,13 @@ interface
Windows,
SynWinSock,
{$else}
SynFPCSock, // shared with Kylix
{$ifdef KYLIX3}
LibC,
Types,
SynFPCSock, // shared with Kylix
SynKylix,
{$endif}
{$ifdef FPC}
SynFPCSock,
SynFPCLinux,
{$endif}
{$endif}
@@ -156,6 +155,10 @@ THttpRequestCached = class(TSynPersistent)
end;


/// will remove most usual HTTP headers which are to be recomputed on sending
function PurgeHeaders(P: PUTF8Char): RawUTF8;


{ ------------ client or server asynchronous process of multiple connections }

type
@@ -1067,10 +1070,11 @@ TWebSocketServer = class(THttpServer)
// - in the current implementation, the ServerThreadPoolCount parameter will
// use two threads by default to handle shortliving HTTP/1.0 "connection: close"
// requests, and one thread will be maintained per keep-alive/websockets client
// - by design, the KeepAliveTimeOut=0 value is ignored with this server
// - by design, the KeepAliveTimeOut value is ignored with this server
// once it has been upgraded to WebSockets
constructor Create(const aPort: SockString; OnStart,OnStop: TNotifyThreadEvent;
const ProcessName: SockString; ServerThreadPoolCount: integer=2;
KeepAliveTimeOut: integer=3000); override;
KeepAliveTimeOut: integer=30000); override;
/// close the server
destructor Destroy; override;
/// will send a given frame to all connected clients
@@ -1150,6 +1154,10 @@ function ToText(opcode: TWebSocketFrameOpCode): PShortString; overload;
/// used to return the text corresponding to a specified WebSockets sending mode
function ToText(mode: TWebSocketProcessNotifyCallback): PShortString; overload;

/// low-level intitialization of a TWebSocketFrame for proper REST content
procedure FrameInit(opcode: TWebSocketFrameOpCode; const Content, ContentType: RawByteString;
out frame: TWebSocketFrame);


{ -------------- WebSockets Client classes for bidirectional remote access }

@@ -1392,6 +1400,32 @@ function THttpRequestCached.Flush(const aAddress: SockString): boolean;
end;


function PurgeHeaders(P: PUTF8Char): RawUTF8;
var
tmp: TTextWriterStackBuffer;
next: PUTF8Char;
W: TTextWriter;
begin
result := '';
W := nil;
try
while P <> nil do begin
next := GotoNextLine(P);
if IdemPCharArray(P, ['CONTENT-', 'CONNECTION:', 'KEEP-ALIVE:', 'TRANSFER-',
'X-POWERED', 'USER-AGENT', 'REMOTEIP:', 'HOST:', 'ACCEPT:']) < 0 then begin
if W = nil then
W := TTextWriter.CreateOwnedStream(tmp);
W.AddNoJSONEscape(P, next - P);
end;
P := next;
end;
if W <> nil then
W.SetText(result);
finally
W.Free;
end;
end;


{ -------------- WebSockets shared classes for bidirectional remote access }

@@ -2020,19 +2054,25 @@ function TWebSocketProtocolBinary.Clone(const aClientURI: RawUTF8): TWebSocketPr
const
FRAME_HEAD_SEP = #1;

procedure FrameInit(opcode: TWebSocketFrameOpCode; const Content, ContentType: RawByteString;
out frame: TWebSocketFrame);
begin
frame.opcode := opcode;
if (ContentType<>'') and (Content<>'') and
not IdemPChar(pointer(ContentType),'TEXT/') and
IsContentCompressed(pointer(Content),length(Content)) then
frame.content := [fopAlreadyCompressed] else
frame.content := [];
end;

procedure TWebSocketProtocolBinary.FrameCompress(const Head: RawUTF8;
const Values: array of const; const Content, ContentType: RawByteString;
var frame: TWebSocketFrame);
var item: RawUTF8;
i: integer;
W: TFileBufferWriter;
begin
frame.opcode := focBinary;
if (ContentType<>'') and (Content<>'') and
not IdemPChar(pointer(ContentType),'TEXT/') and
IsContentCompressed(pointer(Content),length(Content)) then
frame.content := [fopAlreadyCompressed] else
frame.content := [];
FrameInit(focBinary,Content,ContentType,frame);
W := TFileBufferWriter.Create(TRawByteStringStream);
try
W.WriteBinary(Head);
@@ -2620,7 +2660,7 @@ function TWebSocketProcess.State: TWebSocketProcessState;

function TWebSocketProcess.RemoteIP: SockString;
begin
if (self=nil) or (fProtocol=nil) then
if (self=nil) or (fProtocol=nil) or fProtocol.fRemoteLocalhost then
result := '' else
result := fProtocol.fRemoteIP;
end;
@@ -5383,13 +5383,13 @@ procedure TCrtSocket.SockRecv(Buffer: pointer; Length: integer);
function TCrtSocket.TrySockRecv(Buffer: pointer; var Length: integer;
StopBeforeLength: boolean): boolean;
var expected,read: PtrInt;
endtix: Int64;
start, diff: Int64;
begin
result := false;
if (self<>nil) and (fSock>0) and (Buffer<>nil) and (Length>0) then begin
expected := Length;
Length := 0;
endtix := GetTick64+TimeOut;
start := GetTick64;
repeat
read := expected-Length;
{$ifdef MSWINDOWS}
@@ -5415,13 +5415,16 @@ function TCrtSocket.TrySockRecv(Buffer: pointer; var Length: integer;
break; // good enough for now
inc(PByte(Buffer),read);
end;
if GetTick64>endtix then begin
diff := GetTick64-start;
if diff>=TimeOut then begin
{$ifdef SYNCRTDEBUGLOW}
TSynLog.Add.Log(sllCustom2, 'TrySockRecv: timeout',self);
TSynLog.Add.Log(sllCustom2, 'TrySockRecv: timeout (diff=%>%)',[diff,TimeOut],self);
{$endif}
exit; // identify read timeout as error
end;
SleepHiRes(1);
if diff<100 then
SleepHiRes(0) else
SleepHiRes(1);
until false;
result := true;
end;
@@ -10465,7 +10468,8 @@ function THttpApiWebSocketServer.UpgradeToWebSocket(Ctxt: THttpServerRequest): c
ProtocolHeaderFound := false;
p := PHTTP_REQUEST(Ctxt.HttpApiRequest)^.Headers.pUnknownHeaders;
for j := 1 to PHTTP_REQUEST(Ctxt.HttpApiRequest)^.Headers.UnknownHeaderCount do begin
if (p.NameLength=Length(sProtocolHeader)) and IdemPChar(p.pName,Pointer(sProtocolHeader)) then begin
if (p.NameLength=Length(sProtocolHeader)) and
IdemPChar(p.pName,Pointer(sProtocolHeader)) then begin
ProtocolHeaderFound := True;
for i := 0 to Length(fRegisteredProtocols^) - 1 do begin
ch := p.pRawValue;
@@ -10629,18 +10633,20 @@ constructor TSynThreadPoolHttpApiWebSocketServer.Create(Server: THttpApiWebSocke

procedure TSynWebSocketGuard.Execute;
var i, j: Integer;
prot: THttpApiWebSocketServerProtocol;
begin
if fServer.fPingTimeout>0 then
while not Terminated do begin
if fServer<>nil then
for i := 0 to Length(fServer.fRegisteredProtocols^)-1 do begin
EnterCriticalSection(fServer.fRegisteredProtocols^[i].fSafe);
prot := fServer.fRegisteredProtocols^[i];
EnterCriticalSection(prot.fSafe);
try
for j := 0 to fServer.fRegisteredProtocols^[i].fConnectionsCount - 1 do
if Assigned(fServer.fRegisteredProtocols^[i].fConnections[j]) then
fServer.fRegisteredProtocols^[i].fConnections[j].CheckIsActive;
for j := 0 to prot.fConnectionsCount - 1 do
if Assigned(prot.fConnections[j]) then
prot.fConnections[j].CheckIsActive;
finally
LeaveCriticalSection(fServer.fRegisteredProtocols^[i].fSafe);
LeaveCriticalSection(prot.fSafe);
end;
end;
i := 0;
@@ -1 +1 @@
'1.18.5418'
'1.18.5419'

0 comments on commit ee840ed

Please sign in to comment.
You can’t perform that action at this time.