⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rtcisapisrvprov.pas

📁 Delphi快速开发Web Server
💻 PAS
字号:
{
  "ISAPI Server Connection Provider" - Copyright (c) Danijel Tkalcec
  @html(<br>)

  @exclude
}
unit rtcISAPISrvProv;

{$INCLUDE rtcDefs.inc}

interface

uses
  Windows, Classes, SysUtils,

  isapi2,

  rtcFastStrings,
  rtcLog, rtcSyncObjs, rtcConn,
  rtcConnProv, rtcThrConnProv;

type
  TRtcISAPIServerProvider = class(TRtcNoThrServerProvider)
  private
    ECB: TEXTENSION_CONTROL_BLOCK;

    FCS:TRtcCritSec;
    FRequest:TRtcServerRequest;
    FResponse:TRtcServerResponse;

    FRequestBuffer:TRtcHugeString;

    LenToWrite:int64; // number of bytes to write out using inherited Write()
    LenToSend:int64;

    FHeaderOut:boolean;

  protected
    procedure Enter; override;
    procedure Leave; override;

    procedure CopyFrom(Dup:TRtcConnectionProvider);

    function ServerVariable(s:string; len:cardinal):string;

  public
    constructor Create; override;
    destructor Destroy; override;

    function GetParent:TRtcConnectionProvider; override;

    procedure Connect(var _ECB: TEXTENSION_CONTROL_BLOCK);
    procedure ExecuteRequest;

    procedure Listen; override;
    procedure Disconnect; override;
    procedure InternalDisconnect; override;

    procedure TriggerDataSent; override;
    procedure TriggerDataOut; override;

    procedure WriteHeader; overload;
    procedure WriteHeader(const Header_Text:string); overload;

    procedure Write(const ResultData:string; SendNow:boolean=True); override;
    function Read:string; override;

    property Request:TRtcServerRequest read FRequest write FRequest;
    property Response:TRtcServerResponse read FResponse write FResponse;
    end;

implementation

{ TRtcISAPIServerProvider }

const
  CRLF:string=#13#10;

constructor TRtcISAPIServerProvider.Create;
  begin
  inherited;
  FCS:=TRtcCritSec.Create;

  FRequestBuffer:=TRtcHugeString.Create;

  LenToWrite:=0;
  LenToSend:=0;
  FHeaderOut:=False;

  FRequest:=nil;
  FResponse:=nil;
  end;

destructor TRtcISAPIServerProvider.Destroy;
  begin
  Enter;
  try
    FRequestBuffer.Free;

    LenToWrite:=0;
    LenToSend:=0;
    FHeaderOut:=False;
  finally
    Leave;
    FCS.Free;
    FCS:=nil;
    end;
  inherited;
  end;

procedure TRtcISAPIServerProvider.WriteHeader;
  var
    s1,s2:string;
    len:DWORD;
  begin
  if FHeaderOut then
    raise Exception.Create('Last header intercepted with new header, before data sent out.');

  s1:=IntToStr(Response.StatusCode)+' '+Response.StatusText;
  s2:=Response.HeaderText;
  if Request.Close then s2:=s2+'Connection: close'+CRLF;
  s2:=s2+CRLF;

  Response.Sending:=True;
  Response.Started:=True;

  if Response.SendContent and
    (Response['CONTENT-LENGTH']='')  then // streaming data
    begin
    raise Exception.Create('Streaming content not supported by ISAPI.');
    LenToWrite:=-1;
    LenToSend:=-1;
    end
  else
    begin
    if not Response.SendContent then
      Response['CONTENT-LENGTH']:='';

    LenToWrite:=Response.ContentLength;
    LenToSend:=LenToWrite;
    end;

  Response.Sent:=LenToWrite=0;
  if Response.Sent then
    TriggerLastWrite;
    
  FHeaderOut:=True;

  s1:=s1+#0;
  s2:=s2+#0;
  len:=length(s1);

  ECB.dwHttpStatusCode:=Response.StatusCode;
  ECB.ServerSupportFunction(ECB.ConnID, HSE_REQ_SEND_RESPONSE_HEADER, @s1[1], @len, @s2[1]);

  FDataOut:=0;
  end;

procedure TRtcISAPIServerProvider.WriteHeader(const Header_Text:string);
  var
    s1,s2:string;
    len:DWORD;
  begin
  if FHeaderOut then
    raise Exception.Create('Last header intercepted with new header, before data sent out.');

  if Header_Text<>'' then
    begin
    Response.HeaderText:=Header_Text;

    s1:=IntToStr(Response.StatusCode)+' '+Response.StatusText;
    s2:=Response.HeaderText;
    if Request.Close then s2:=s2+'Connection: close'+CRLF;
    s2:=s2+CRLF;
    end
  else
    begin
    raise Exception.Create('Streaming content not supported by ISAPI.');
    s1:='';
    s2:='';
    Request.Close:=True;
    end;

  Response.Sending:=True;
  Response.Started:=True;

  if Response.SendContent and
    (Response['CONTENT-LENGTH']='')  then // streaming data
    begin
    raise Exception.Create('Streaming content not supported by ISAPI.');
    LenToWrite:=-1;
    LenToSend:=-1;
    end
  else
    begin
    if not Response.SendContent then
      Response['CONTENT-LENGTH']:='';

    LenToWrite:=Response.ContentLength;
    LenToSend:=LenToWrite;
    end;

  Response.Sent:=LenToWrite=0;
  if Response.Sent then
    TriggerLastWrite;

  FHeaderOut:=True;

  FHeaderOut:=True;

  len:=length(s1);
  s1:=s1+#0;
  s2:=s2+#0;

  ECB.dwHttpStatusCode:=Response.StatusCode;
  ECB.ServerSupportFunction(ECB.ConnID, HSE_REQ_SEND_RESPONSE_HEADER, @s1[1], @len, @s2[1]);

  FDataOut:=0;
  end;

procedure TRtcISAPIServerProvider.Write(const ResultData: string; SendNow:boolean=True);
  var
    len,len2,loc:cardinal;
  begin
  if length(ResultData)=0 then Exit;

  if not FHeaderOut then
    raise Exception.Create('Trying to send Data without Header. Call WriteHeader before Write.');

  if LenToWrite>=0 then
    begin
    if length(ResultData)>LenToWrite then
      raise Exception.Create('Trying to send more Data out than specified in Header.');

    Dec(LenToWrite, length(ResultData));
    end;

  Response.Sent:=LenToWrite=0;
  Response.ContentOut:=Response.ContentOut + length(ResultData);

  if Response.Sent then
    TriggerLastWrite;

  len:=length(ResultData);
  len2:=len;

  loc:=1;
  repeat
    if not ECB.WriteClient(ECB.ConnID,@ResultData[loc],len,0) then
      raise Exception.Create('Error sending data out (WriteClient returned FALSE).')
    else if len=0 then
      raise Exception.Create('Error sending data out (WriteClient length = 0).');
    Inc(loc,len); len2:=len2-len;
    len:=len2;
    until len2=0;

  FDataOut:=length(ResultData);
  if FDataOut>0 then
    try
      TriggerDataOut;
    finally
      FDataOut:=0;
      TriggerDataSent;
      end;
  end;

function TRtcISAPIServerProvider.Read: string;
  begin
  if FRequestBuffer.Size>0 then
    begin
    Result:=FRequestBuffer.Get;
    FRequestBuffer.Clear;
    end
  else
    Result:='';
  end;

procedure TRtcISAPIServerProvider.TriggerDataSent;
  begin
  if Response.Sending then
    Response.Started:=False;

  inherited TriggerDataSent;
  end;

procedure TRtcISAPIServerProvider.TriggerDataOut;
  begin
  if Response.Sending then
    begin
    if LenToSend>=0 then
      begin
      Dec(LenToSend, DataOut);
      Response.Done := LenToSend=0;
      end;

    if Response.Done then
      begin
      Request.Started:=False;
      Request.Active:=False;
      Response.Started:=False;
      Response.Sending:=False;
      FHeaderOut:=False;
      end;
    end;

  inherited TriggerDataOut;
  end;

function TRtcISAPIServerProvider.GetParent: TRtcConnectionProvider;
  begin
  Result:=nil;
  end;

procedure TRtcISAPIServerProvider.Listen;
  begin
  State:=conListening;
  TriggerListenStart;
  end;

procedure TRtcISAPIServerProvider.Disconnect;
  begin
  InternalDisconnect;
  end;

procedure TRtcISAPIServerProvider.InternalDisconnect;
  begin
  if State<>conInactive then
    begin
    if State=conActive then
      begin
      TriggerDisconnecting;
      TriggerDisconnect;
      TriggerConnectionLost;
      end
    else if State=conListening then
      TriggerListenStop;
    State:=conInactive;
    end;
  end;

procedure TRtcISAPIServerProvider.CopyFrom(Dup: TRtcConnectionProvider);
  begin
  //
  end;

procedure TRtcISAPIServerProvider.Enter;
  begin
  FCS.Enter;
  end;

procedure TRtcISAPIServerProvider.Leave;
  begin
  FCS.Leave;
  end;

procedure TRtcISAPIServerProvider.Connect(var _ECB: TEXTENSION_CONTROL_BLOCK);
  begin
  ECB:=_ECB;

  LocalAddr:=ServerVariable('SERVER_ADDR',128);
  LocalPort:=ServerVariable('SERVER_PORT',32);
  PeerAddr:=ServerVariable('REMOTE_ADDR',128);
  PeerPort:=ServerVariable('REMOTE_PORT',32);

  if not (State=conActive) then
    begin
    State:=conActive;
    TriggerConnectionAccepted;
    TriggerConnecting;
    TriggerConnect;
    end;
  end;

procedure TRtcISAPIServerProvider.ExecuteRequest;
  var
    s:string;
    len,len2:cardinal;
    MyPos:integer;
  begin
  Request.Clear;
  Response.Clear;

  s:=ServerVariable('URL',1024);
  if s<>'' then
    begin
    Request.FileName:='/';
    Request.URI:=s;
    end;

  Request.Method:=ECB.lpszMethod;
  Request.FileName:=ECB.lpszPathInfo;

  Request.Query.Text:=ECB.lpszQueryString;
  Request.ContentLength:=ECB.cbTotalBytes;

  s:=ServerVariable('ALL_RAW',8192);
  if s<>'' then Request.HeaderText:=s;

  if CompareText(Copy(Request.ContentType,1,19),'MULTIPART/FORM-DATA')=0 then
    begin
    MyPos:=Pos('BOUNDARY=',UpperCase(Request.ContentType));
    if MyPos>0 then
      // Get MULTIPART Boundary (Params.Delimiter)
      begin
      Request.Params.Delimiter:=
        Copy(Request.ContentType, MyPos+9, length(Request.ContentType)-MyPos-8);
      if (Copy(Request.Params.Delimiter,1,1)='"') and
         (Copy(Request.Params.Delimiter,
               length(Request.Params.Delimiter),1)='"') then
        begin
        Request.Params.Delimiter:=
           Copy(Request.Params.Delimiter, 2, length(Request.Params.Delimiter)-2);
        end;
      end;
    end;

  if ECB.cbAvailable>0 then
    begin
    Request.ContentIn:=ECB.cbAvailable;
    SetString(s,PChar(ECB.lpbData),ECB.cbAvailable);
    len:=length(s);
    FrequestBuffer.Add(s);
    end;

  FDataIn:=length(Request.URI)+length(Request.HeaderText);
  TriggerDataIn;

  Request.Started:=True;
  Request.Active:=True;
  Request.Complete:= Request.ContentLength=Request.ContentIn;

  if Request.FileName<>'' then
    TriggerDataReceived
  else if Request.Complete then
    begin
    Response.StatusCode:=301;
    Response.StatusText:='Moved Permanently';
    Response['LOCATION']:= Request.URI+'/';
    Response.ContentLength:=0;
    WriteHeader;
    end;

  Request.Started:=False;
  while Request.ContentIn<Request.ContentLength do
    begin
    len:=Request.ContentLength-Request.ContentIn;
    if len>32000 then len:=32000;

    len2:=len;
    SetLength(s,len);
    if not ECB.ReadClient(ECB.ConnID,@s[1],len) then
      raise Exception.Create('Error receiving data (ReadClient returned FALSE).')
    else if len<len2 then
      begin
      if len>0 then
        SetLength(s,len)
      else
        raise Exception.Create('Error receiving data (ReadClient returned 0 bytes).');
      end;

    FDataIn:=len;
    TriggerDataIn;

    Request.ContentIn:=Request.ContentIn+len;
    FrequestBuffer.Add(s);
    Request.Complete:= Request.ContentIn=Request.ContentLength;

    if Request.FileName<>'' then
      begin
      TriggerDataReceived;
      Sleep(0);
      end
    else if Request.Complete then
      begin
      Response.StatusCode:=301;
      Response.StatusText:='Moved Permanently';
      Response['LOCATION']:= Request.URI+'/';
      Response.ContentLength:=0;
      WriteHeader;
      end;
    end;

  FHeaderOut:=False;
  end;

function TRtcISAPIServerProvider.ServerVariable(s: string; len:cardinal): string;
  var
    len2:cardinal;
  begin
  len2:=len;
  SetLength(Result,len);
  if not ECB.GetServerVariable(ECB.ConnID, PChar(s),@Result[1],len) then
    begin
    if len>len2 then // more data available
      begin
      SetLength(Result,len);
      if not ECB.GetServerVariable(ECB.ConnID, PChar(s),@Result[1],len) then
        len:=1;
      end
    else
      len:=1;
    end;
  SetLength(Result,len-1);
  end;

end.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -