Delphi IdHTTPServer (Indy 10.6): 从 OnWorkEnd 事件中的 TIdTCPConnection 中检索一些 request/response 信息

Delphi IdHTTPServer (Indy 10.6): retrive some request/response info from TIdTCPConnection in OnWorkEnd event

当 TIdContext.Connection 触发 OnWorkEnd 事件时,可以从 TIdTCPConnection 检索一些信息(用于记录目的)?

我需要如下信息: - 用户 ip 地址(在 Socket.Binding.PeerIP 中找到我自己) - Browser/client 用户代理 - 日期时间开始请求 - 请求的总大小 - 字节发送 - 发送文件的文件名

我的服务器非常简单,在每次请求时,用文件流响应。

procedure TMyHttpServer.OnCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
     AResponseInfo.ContentStream   := TFileStream.Create('C:\server\file.exe', fmOpenRead or fmShareDenyNone);
     AContext.Connection.OnWorkEnd := MyOnWorkEnd;
end;


procedure TMyHttpServer.MyOnWorkEnd(ASender: TObject; AWorkMode: TWorkMode);
var
    aConnection : TIdTCPConnection;
    aIPAddress, aFileName, aDateStart, aByteSend, aFileSize, aUserAgent : string;
    aDateEnd   : TDateTime;
begin
    aConnection := TIdTCPConnection(ASender);

    aIPAddress := aConnection.Socket.Binding.PeerIP;

    aFileName  := ''; // Filename download 
    aDateStart := ''; // Date start download
    aDateEnd   := Now; 
    aByteSend  := ''; // byte send
    aFileSize  := ''; // file size
    aUserAgent := ''; // user agent

    WriteLog(aFileName  + ' ' + aDateStart +' '+aDateEnd +' etc.');

end;

无法在 OnWork... 事件中直接访问请求和响应信息。您将不得不手动传递信息。我会建议:

  1. TFileStream派生一个新的class来存储所需的信息,然后在服务器释放时在class的析构函数中处理信息ContentStream响应传输完成后。

  2. TIdServerContext 派生一个新的 class 来保存指向 TIdHTTPRequestInfoTIdHTTPResponseInfo 对象的指针:

    type
      TMyContext = class(TIdServerContext)
      public
        Request: TIdHTTPRequestInfo;
        Response: TIdHTTPResponseInfo;
      end;
    

    然后您可以在激活服务器之前将 class 类型分配给服务器的 ContextClass 属性,并在 OnCommandGet 中对 AContext 参数进行类型转换事件到你的 class 类型,这样你就可以分配它的指针,并将 AContext 对象分配给 AContext.Connection.Tag 属性:

    MyHttpServer.ContextClass := TMyContext;
    
    ...
    
    procedure TMyHttpServer.OnCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
    begin
      TMyContext(AContext).Request := ARequestInfo;
      TMyContext(AContext).Response := AResponseInfo;
      AContext.Connection.Tag := NativeInt(AContext);
      //...
    end;
    

    OnWork... 事件中,您可以对 Sender 参数进行类型转换以达到其 Tag,并将其类型转换为您的自定义 class 以到达其存储的 request/response 指针:

    procedure TMyHttpServer.MyOnWorkEnd(ASender: TObject; AWorkMode: TWorkMode);
    var
      aConnection : TIdTCPConnection;
      aContext: TMyContext;
      //...
    begin
      aConnection := TIdTCPConnection(ASender);
      aContext := TMyClass(aConnection.Tag);
      //...
    end;
    
  3. #2 的一个细微变化是操纵 OnWorkEnd 事件处理程序的 Self 指针,将 Context 对象直接传递给处理程序,而无需使用 Connection.Tag 属性:

    type
      TMyContext = class(TIdServerContext)
      public
        Request: TIdHTTPRequestInfo;
        Response: TIdHTTPResponseInfo;
        MyServer: TMyHttpServer;
      end;
    
    ...
    
    procedure TMyHttpServer.OnCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
    var
      Handler: TWorkEndEvent;
    begin
      TMyContext(AContext).Request := ARequestInfo;
      TMyContext(AContext).Response := AResponseInfo;
      TMyContext(AContext).MyServer := Self;
      Handler := MyOnWorkEnd;
      TMethod(Handler).Data := TMyContext(AContext);
      AContext.Connection.OnWorkEnd := Handler
      //...
    end;
    
    procedure TMyHttpServer.MyOnWorkEnd(ASender: TObject; AWorkMode: TWorkMode);
    var
      aConnection : TIdTCPConnection;
      aContext: TMyContext;
      aServer: TMyHttpServer;
      //...
    begin
      aConnection := TIdTCPConnection(ASender);
      aContext := TMyClass(Self);
      aServer := aContext. MyServer;
      //...
    end;