|
@@ -66,6 +66,7 @@ Type
|
|
private
|
|
private
|
|
FBusy: Boolean;
|
|
FBusy: Boolean;
|
|
FConnectionID: String;
|
|
FConnectionID: String;
|
|
|
|
+ FIsUpgraded: Boolean;
|
|
FOnRequestError: TRequestErrorHandler;
|
|
FOnRequestError: TRequestErrorHandler;
|
|
FOnUnexpectedError: TRequestErrorHandler;
|
|
FOnUnexpectedError: TRequestErrorHandler;
|
|
FServer: TFPCustomHTTPServer;
|
|
FServer: TFPCustomHTTPServer;
|
|
@@ -130,6 +131,8 @@ Type
|
|
Property LookupHostNames : Boolean Read GetLookupHostNames;
|
|
Property LookupHostNames : Boolean Read GetLookupHostNames;
|
|
// is the current connection set up for KeepAlive?
|
|
// is the current connection set up for KeepAlive?
|
|
Property KeepAlive: Boolean read FKeepAlive;
|
|
Property KeepAlive: Boolean read FKeepAlive;
|
|
|
|
+ // Is the current connection upgraded ?
|
|
|
|
+ Property IsUpgraded : Boolean Read FIsUpgraded;
|
|
end;
|
|
end;
|
|
|
|
|
|
{ TFPHTTPConnectionThread }
|
|
{ TFPHTTPConnectionThread }
|
|
@@ -254,6 +257,35 @@ Type
|
|
|
|
|
|
// List of server connection handlers TFPHTTPServerConnectionHandler
|
|
// List of server connection handlers TFPHTTPServerConnectionHandler
|
|
|
|
|
|
|
|
+
|
|
|
|
+ THandlesUpgradeEvent = procedure(aRequest : TFPHTTPConnectionRequest; var aHandlesUpgrade : Boolean) of object;
|
|
|
|
+ TUpgradeConnectionEvent = procedure(aConnection : TFPHTTPConnection; aRequest : TFPHTTPConnectionRequest) of object;
|
|
|
|
+
|
|
|
|
+ { TUpgradeHandlerItem }
|
|
|
|
+
|
|
|
|
+ TUpgradeHandlerItem = Class(TCollectionItem)
|
|
|
|
+ private
|
|
|
|
+ FName: String;
|
|
|
|
+ FOnHandlesUpgrade: THandlesUpgradeEvent;
|
|
|
|
+ FOnUpgrade: TUpgradeConnectionEvent;
|
|
|
|
+ Public
|
|
|
|
+ Property Name : String Read FName Write FName;
|
|
|
|
+ Property OnHandleUpgrade : THandlesUpgradeEvent Read FOnHandlesUpgrade Write FOnHandlesUpgrade;
|
|
|
|
+ Property OnUpgrade : TUpgradeConnectionEvent Read FOnUpgrade Write FOnUpgrade;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ { TUpgradeHandlerList }
|
|
|
|
+
|
|
|
|
+ TUpgradeHandlerList = Class(TCollection)
|
|
|
|
+ private
|
|
|
|
+ function GetHandlerItem(aIndex : Integer): TUpgradeHandlerItem;
|
|
|
|
+ Public
|
|
|
|
+ Function IndexOfName(const aName : String) : Integer;
|
|
|
|
+ Function HandlerByName(const aName : String) : TUpgradeHandlerItem;
|
|
|
|
+ Function AddHandler(const aName : String; aOnCheck : THandlesUpgradeEvent; aOnUpgrade : TUpgradeConnectionEvent) : TUpgradeHandlerItem; virtual;
|
|
|
|
+ Property Handler[aIndex :Integer] : TUpgradeHandlerItem Read GetHandlerItem; default;
|
|
|
|
+ end;
|
|
|
|
+
|
|
{ TConnectionList }
|
|
{ TConnectionList }
|
|
|
|
|
|
|
|
|
|
@@ -283,11 +315,14 @@ Type
|
|
FTreadMode: TThreadMode;
|
|
FTreadMode: TThreadMode;
|
|
FUseSSL: Boolean;
|
|
FUseSSL: Boolean;
|
|
FConnectionHandler : TFPHTTPServerConnectionHandler;
|
|
FConnectionHandler : TFPHTTPServerConnectionHandler;
|
|
|
|
+ FUpdateHandlers : TUpgradeHandlerList;
|
|
procedure DoCreateClientHandler(Sender: TObject; out AHandler: TSocketHandler);
|
|
procedure DoCreateClientHandler(Sender: TObject; out AHandler: TSocketHandler);
|
|
function GetActive: Boolean;
|
|
function GetActive: Boolean;
|
|
function GetConnectionCount: Integer;
|
|
function GetConnectionCount: Integer;
|
|
|
|
+ function GetHasUpdateHandlers: Boolean;
|
|
function GetHostName: string;
|
|
function GetHostName: string;
|
|
function GetThreaded: Boolean;
|
|
function GetThreaded: Boolean;
|
|
|
|
+ function GetUpdateHandlers: TUpgradeHandlerList;
|
|
procedure SetAcceptIdleTimeout(AValue: Cardinal);
|
|
procedure SetAcceptIdleTimeout(AValue: Cardinal);
|
|
procedure SetActive(const AValue: Boolean);
|
|
procedure SetActive(const AValue: Boolean);
|
|
procedure SetCertificateData(AValue: TCertificateData);
|
|
procedure SetCertificateData(AValue: TCertificateData);
|
|
@@ -302,6 +337,9 @@ Type
|
|
procedure SetupSocket;
|
|
procedure SetupSocket;
|
|
procedure SetupConnectionHandler;
|
|
procedure SetupConnectionHandler;
|
|
Protected
|
|
Protected
|
|
|
|
+ Function CheckUpgrade(aConnection : TFPHTTPConnection; aRequest : TFPHTTPConnectionRequest) : Boolean;
|
|
|
|
+ // Override this to create Descendent
|
|
|
|
+ Function CreateUpgradeHandlerList : TUpgradeHandlerList;
|
|
// Override this to create descendent
|
|
// Override this to create descendent
|
|
function CreateSSLSocketHandler: TSocketHandler;
|
|
function CreateSSLSocketHandler: TSocketHandler;
|
|
// Override this to create descendent
|
|
// Override this to create descendent
|
|
@@ -344,9 +382,15 @@ Type
|
|
Property Connectionhandler : TFPHTTPServerConnectionHandler Read FConnectionHandler;
|
|
Property Connectionhandler : TFPHTTPServerConnectionHandler Read FConnectionHandler;
|
|
// Connection count. Convenience shortcut for Connectionhandler.GetActiveConnectionCount;
|
|
// Connection count. Convenience shortcut for Connectionhandler.GetActiveConnectionCount;
|
|
Property ConnectionCount : Integer Read GetConnectionCount;
|
|
Property ConnectionCount : Integer Read GetConnectionCount;
|
|
|
|
+ // Upgrade handlers. Created on demand
|
|
|
|
+ Property UpdateHandlers : TUpgradeHandlerList Read GetUpdateHandlers;
|
|
|
|
+ // Has update handlers
|
|
|
|
+ Property HasUpdateHandlers : Boolean Read GetHasUpdateHandlers;
|
|
public
|
|
public
|
|
Constructor Create(AOwner : TComponent); override;
|
|
Constructor Create(AOwner : TComponent); override;
|
|
Destructor Destroy; override;
|
|
Destructor Destroy; override;
|
|
|
|
+ Function RegisterUpdateHandler(Const aName : string; OnCheck : THandlesUpgradeEvent; OnUpgrade : TUpgradeConnectionEvent) : TUpgradeHandlerItem;
|
|
|
|
+ Procedure UnRegisterUpdateHandler(Const aName : string);
|
|
protected
|
|
protected
|
|
// Set to true to start listening.
|
|
// Set to true to start listening.
|
|
Property Active : Boolean Read GetActive Write SetActive Default false;
|
|
Property Active : Boolean Read GetActive Write SetActive Default false;
|
|
@@ -421,6 +465,7 @@ resourcestring
|
|
SErrSocketActive = 'Operation not allowed while server is active';
|
|
SErrSocketActive = 'Operation not allowed while server is active';
|
|
SErrReadingSocket = 'Error reading data from the socket';
|
|
SErrReadingSocket = 'Error reading data from the socket';
|
|
SErrMissingProtocol = 'Missing HTTP protocol version in request';
|
|
SErrMissingProtocol = 'Missing HTTP protocol version in request';
|
|
|
|
+ SErrDuplicateUpgradeHandler = 'Duplicate upgrade handler';
|
|
|
|
|
|
{ TFPHTTPConnectionRequest }
|
|
{ TFPHTTPConnectionRequest }
|
|
Function GetStatusCode (ACode: Integer) : String;
|
|
Function GetStatusCode (ACode: Integer) : String;
|
|
@@ -444,6 +489,44 @@ begin
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+{ TUpgradeHandlerList }
|
|
|
|
+
|
|
|
|
+function TUpgradeHandlerList.GetHandlerItem(aIndex : Integer): TUpgradeHandlerItem;
|
|
|
|
+begin
|
|
|
|
+ Result:=TUpgradeHandlerItem(Items[aIndex]);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TUpgradeHandlerList.IndexOfName(const aName: String): Integer;
|
|
|
|
+begin
|
|
|
|
+ Result:=Count-1;
|
|
|
|
+ While (Result>=0) and Not SameText(aName,GetHandlerItem(Result).Name) do
|
|
|
|
+ Dec(Result);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TUpgradeHandlerList.HandlerByName(const aName: String): TUpgradeHandlerItem;
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ Idx : integer;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ Idx:=IndexOfName(aName);
|
|
|
|
+ if (Idx=-1) then
|
|
|
|
+ Result:=Nil
|
|
|
|
+ else
|
|
|
|
+ Result:=GetHandlerItem(Idx);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TUpgradeHandlerList.AddHandler(const aName: String; aOnCheck: THandlesUpgradeEvent; aOnUpgrade: TUpgradeConnectionEvent
|
|
|
|
+ ): TUpgradeHandlerItem;
|
|
|
|
+begin
|
|
|
|
+ if IndexOfName(aName)<>-1 then
|
|
|
|
+ Raise EHTTPServer.CreateFmt(SErrDuplicateUpgradeHandler,[aName]);
|
|
|
|
+ Result:=add as TUpgradeHandlerItem;
|
|
|
|
+ Result.Name:=aName;
|
|
|
|
+ Result.OnHandleUpgrade:=aOnCheck;
|
|
|
|
+ Result.OnUpgrade:=aOnUpgrade;
|
|
|
|
+end;
|
|
|
|
+
|
|
{ TFPPooledConnectionHandler.THandleRequestTask }
|
|
{ TFPPooledConnectionHandler.THandleRequestTask }
|
|
|
|
|
|
constructor TFPPooledConnectionHandler.THandleRequestTask.Create(aConnection: TFPHTTPConnection; aOnConnectionDone : TNotifyEvent);
|
|
constructor TFPPooledConnectionHandler.THandleRequestTask.Create(aConnection: TFPHTTPConnection; aOnConnectionDone : TNotifyEvent);
|
|
@@ -545,7 +628,8 @@ end;
|
|
|
|
|
|
procedure TFPHTTPServerConnectionListHandler.CloseConnectionSocket(aConnection: TFPHTTPConnection; var aContinue: boolean);
|
|
procedure TFPHTTPServerConnectionListHandler.CloseConnectionSocket(aConnection: TFPHTTPConnection; var aContinue: boolean);
|
|
begin
|
|
begin
|
|
- sockets.CloseSocket(aConnection.Socket.Handle);
|
|
|
|
|
|
+ if Not aConnection.IsUpgraded then
|
|
|
|
+ sockets.CloseSocket(aConnection.Socket.Handle);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TFPHTTPServerConnectionListHandler.Foreach(aIterator: TConnectionIterator);
|
|
procedure TFPHTTPServerConnectionListHandler.Foreach(aIterator: TConnectionIterator);
|
|
@@ -721,6 +805,8 @@ Var
|
|
S : String;
|
|
S : String;
|
|
I : Integer;
|
|
I : Integer;
|
|
begin
|
|
begin
|
|
|
|
+ if Connection.IsUpgraded then
|
|
|
|
+ exit;
|
|
S:=Format('HTTP/1.1 %3d %s'#13#10,[Code,GetHTTPStatusText(Code)]);
|
|
S:=Format('HTTP/1.1 %3d %s'#13#10,[Code,GetHTTPStatusText(Code)]);
|
|
For I:=0 to Headers.Count-1 do
|
|
For I:=0 to Headers.Count-1 do
|
|
S:=S+Headers[i]+#13#10;
|
|
S:=S+Headers[i]+#13#10;
|
|
@@ -730,6 +816,8 @@ end;
|
|
|
|
|
|
procedure TFPHTTPConnectionResponse.DoSendContent;
|
|
procedure TFPHTTPConnectionResponse.DoSendContent;
|
|
begin
|
|
begin
|
|
|
|
+ if Connection.IsUpgraded then
|
|
|
|
+ exit;
|
|
If Assigned(ContentStream) then
|
|
If Assigned(ContentStream) then
|
|
Connection.Socket.CopyFrom(ContentStream,0)
|
|
Connection.Socket.CopyFrom(ContentStream,0)
|
|
else
|
|
else
|
|
@@ -853,6 +941,8 @@ begin
|
|
Exit;
|
|
Exit;
|
|
end;
|
|
end;
|
|
N:=Copy(V,1,P-1);
|
|
N:=Copy(V,1,P-1);
|
|
|
|
+ if SameText(N,'Upgrade') then
|
|
|
|
+ V:=V;
|
|
Delete(V,1,P);
|
|
Delete(V,1,P);
|
|
V:=Trim(V);
|
|
V:=Trim(V);
|
|
ARequest.SetFieldByName(N,V);
|
|
ARequest.SetFieldByName(N,V);
|
|
@@ -919,7 +1009,7 @@ begin
|
|
end;
|
|
end;
|
|
P:=P+1;
|
|
P:=P+1;
|
|
R:=1;
|
|
R:=1;
|
|
- While (L<>0) and (R>0) do
|
|
|
|
|
|
+ While (L>=0) and (R>0) do
|
|
begin
|
|
begin
|
|
R:=FSocket.Read(S[p],L);
|
|
R:=FSocket.Read(S[p],L);
|
|
If R<0 then
|
|
If R<0 then
|
|
@@ -960,16 +1050,18 @@ end;
|
|
|
|
|
|
function TFPHTTPConnection.AllowNewRequest: Boolean;
|
|
function TFPHTTPConnection.AllowNewRequest: Boolean;
|
|
begin
|
|
begin
|
|
- Result:=not Busy and KeepConnections and KeepAlive and (Socket.LastError=0);
|
|
|
|
|
|
+ Result:=not (Busy or IsUpgraded);
|
|
|
|
+ Result:=Result and KeepConnections and KeepAlive and (Socket.LastError=0) ;
|
|
end;
|
|
end;
|
|
|
|
|
|
function TFPHTTPConnection.RequestPending: Boolean;
|
|
function TFPHTTPConnection.RequestPending: Boolean;
|
|
begin
|
|
begin
|
|
- Result:=Socket.CanRead(KeepConnectionTimeout);
|
|
|
|
|
|
+ Result:=(Not IsUpgraded) and Socket.CanRead(KeepConnectionTimeout);
|
|
end;
|
|
end;
|
|
|
|
|
|
constructor TFPHTTPConnection.Create(AServer: TFPCustomHttpServer; ASocket: TSocketStream);
|
|
constructor TFPHTTPConnection.Create(AServer: TFPCustomHttpServer; ASocket: TSocketStream);
|
|
begin
|
|
begin
|
|
|
|
+ FIsUpgraded:=False;
|
|
FIsSocketSetup:=False;
|
|
FIsSocketSetup:=False;
|
|
FSocket:=ASocket;
|
|
FSocket:=ASocket;
|
|
FServer:=AServer;
|
|
FServer:=AServer;
|
|
@@ -1011,6 +1103,8 @@ Var
|
|
Resp : TFPHTTPConnectionResponse;
|
|
Resp : TFPHTTPConnectionResponse;
|
|
|
|
|
|
begin
|
|
begin
|
|
|
|
+ if IsUpgraded then
|
|
|
|
+ exit;
|
|
// Read headers.
|
|
// Read headers.
|
|
Resp:=Nil;
|
|
Resp:=Nil;
|
|
Req:=ReadRequestHeaders;
|
|
Req:=ReadRequestHeaders;
|
|
@@ -1021,6 +1115,12 @@ begin
|
|
If Req.ContentLength>0 then
|
|
If Req.ContentLength>0 then
|
|
ReadRequestContent(Req);
|
|
ReadRequestContent(Req);
|
|
Req.InitRequestVars;
|
|
Req.InitRequestVars;
|
|
|
|
+ If Server.CheckUpgrade(Self,Req) then
|
|
|
|
+ begin
|
|
|
|
+ FSocket:=Nil; // Must have been taken over by upgrader
|
|
|
|
+ FIsUpgraded:=True;
|
|
|
|
+ Exit;
|
|
|
|
+ end;
|
|
if KeepConnections then
|
|
if KeepConnections then
|
|
begin
|
|
begin
|
|
// Read out keep-alive
|
|
// Read out keep-alive
|
|
@@ -1148,6 +1248,11 @@ begin
|
|
Result:=0;
|
|
Result:=0;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+function TFPCustomHttpServer.GetHasUpdateHandlers: Boolean;
|
|
|
|
+begin
|
|
|
|
+ Result:=FUpdateHandlers<>Nil;
|
|
|
|
+end;
|
|
|
|
+
|
|
procedure TFPCustomHttpServer.DoCreateClientHandler(Sender: TObject; out AHandler: TSocketHandler);
|
|
procedure TFPCustomHttpServer.DoCreateClientHandler(Sender: TObject; out AHandler: TSocketHandler);
|
|
begin
|
|
begin
|
|
AHandler:=GetSocketHandler(UseSSL);
|
|
AHandler:=GetSocketHandler(UseSSL);
|
|
@@ -1176,6 +1281,13 @@ begin
|
|
Result:=ThreadMode=tmThread;
|
|
Result:=ThreadMode=tmThread;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+function TFPCustomHttpServer.GetUpdateHandlers: TUpgradeHandlerList;
|
|
|
|
+begin
|
|
|
|
+ If FUpdateHandlers=Nil then
|
|
|
|
+ FUpdateHandlers:=CreateUpgradeHandlerList;
|
|
|
|
+ Result:=FUpdateHandlers;
|
|
|
|
+end;
|
|
|
|
+
|
|
procedure TFPCustomHttpServer.SetAcceptIdleTimeout(AValue: Cardinal);
|
|
procedure TFPCustomHttpServer.SetAcceptIdleTimeout(AValue: Cardinal);
|
|
begin
|
|
begin
|
|
if FAcceptIdleTimeout=AValue then Exit;
|
|
if FAcceptIdleTimeout=AValue then Exit;
|
|
@@ -1338,6 +1450,39 @@ begin
|
|
FConnectionHandler:=CreateConnectionHandler();
|
|
FConnectionHandler:=CreateConnectionHandler();
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+function TFPCustomHttpServer.CheckUpgrade(aConnection: TFPHTTPConnection; aRequest: TFPHTTPConnectionRequest): Boolean;
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ I : Integer;
|
|
|
|
+ Handler : TUpgradeHandlerItem;
|
|
|
|
+ S : String;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ Result:=HasUpdateHandlers;
|
|
|
|
+ If Result then
|
|
|
|
+ begin
|
|
|
|
+ Result:=False;
|
|
|
|
+ If Pos('upgrade',LowerCase(aRequest.GetHeader(hhConnection)))=0 then
|
|
|
|
+ Exit;
|
|
|
|
+ If (aRequest.GetHeader(hhUpgrade)='') then
|
|
|
|
+ Exit;
|
|
|
|
+ I:=0;
|
|
|
|
+ While (I<UpdateHandlers.Count) and not Result do
|
|
|
|
+ begin
|
|
|
|
+ Handler:=UpdateHandlers[i];
|
|
|
|
+ Handler.OnHandleUpgrade(aRequest,Result);
|
|
|
|
+ Inc(I);
|
|
|
|
+ end;
|
|
|
|
+ If Result then
|
|
|
|
+ Handler.OnUpgrade(aConnection,aRequest);
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TFPCustomHttpServer.CreateUpgradeHandlerList: TUpgradeHandlerList;
|
|
|
|
+begin
|
|
|
|
+ Result:=TUpgradeHandlerList.Create(TUpgradeHandlerItem);
|
|
|
|
+end;
|
|
|
|
+
|
|
procedure TFPCustomHttpServer.HandleUnexpectedError(Sender: TObject; E: Exception);
|
|
procedure TFPCustomHttpServer.HandleUnexpectedError(Sender: TObject; E: Exception);
|
|
begin
|
|
begin
|
|
If Assigned(FOnUnexpectedError) then
|
|
If Assigned(FOnUnexpectedError) then
|
|
@@ -1454,5 +1599,26 @@ begin
|
|
inherited Destroy;
|
|
inherited Destroy;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+function TFPCustomHttpServer.RegisterUpdateHandler(const aName: string; OnCheck: THandlesUpgradeEvent;
|
|
|
|
+ OnUpgrade: TUpgradeConnectionEvent): TUpgradeHandlerItem;
|
|
|
|
+begin
|
|
|
|
+ With UpdateHandlers do
|
|
|
|
+ Result:=AddHandler(aName,OnCheck,OnUpgrade)
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TFPCustomHttpServer.UnRegisterUpdateHandler(const aName: string);
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ Idx : Integer;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ With UpdateHandlers do
|
|
|
|
+ begin
|
|
|
|
+ Idx:=IndexOfName(aName);
|
|
|
|
+ if Idx<>-1 then
|
|
|
|
+ Delete(Idx);
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
end.
|
|
end.
|
|
|
|
|