【问题标题】:TIdHTTPServer + TIdServerIOHandlerSSLOpenSSL = unstable workTIdHTTPServer + TIdServerIOHandlerSSLOpenSSL = 工作不稳定
【发布时间】:2020-03-19 11:03:59
【问题描述】:

我在表单上放置了 TIdHTTPServer 和 TIdServerIOHandlerSSLOpenSSL,为 TIdServerIOHandlerSSLOpenSSL 设置了证书文件路径
在 TIdHTTPServer onCommandGet 事件中,我以这种方式发送答案:

FullResponce:=RespHeaders+#13#10+RespData;
//dump for debug
FileHandle := CreateFileW('c:\lastresp.txt', GENERIC_WRITE, FILE_SHARE_READ, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if FileHandle <> INVALID_HANDLE_VALUE then
begin
  try
    WriteFile(FileHandle, FullResponce[1], length(FullResponce), LongWord(ReadRes), nil);
  finally
    CloseHandle(FileHandle);
  end;
end;
//response 
AContext.Connection.IOHandler.WriteDirect(@FullResponce[1],length(FullResponce),0);
exit;

当我尝试在 Internet Explorer 中打开地址“127.0.0.1:433”时,有时会打开页面,有时会显示“无法显示页面”
这没有任何模式或逻辑,我只是​​在资源管理器中刷新页面 10 次,例如尝试 1,2,4,6,7,9,10 打开页面,其余以错误
lastresp.txt 中答案的副本始终正确且始终相同
当错误发生时,IE在网络控制台显示他收到0字节

任何想法如何解决这个问题?
Indy 中的错误?

更新
已解决,问题不在代码中,而是在用于控制流量的嗅探器应用中(

【问题讨论】:

    标签: delphi https indy


    【解决方案1】:

    您如何实现OnCommandGet 事件并不是向客户端发送响应的正确方式。

    首先,您根本不应该直接使用TIdIOHandler.WriteDirect()。但是如果你这样做了,你必须给它一个TIdBytes,即一个动态的字节数组,而不是一个字符串。

    但更重要的是,要向客户端发送 HTTP 响应,正确的方法是填充AResponseInfo 参数,让TIdHTTPServer 为您处理实际的套接字写入。

    例如:

    procedure TMyForm.IdHTTPServer1CommandGet(AContext: TIdContext;
      ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
    begin
      ...
      AResponseInfo.ResponseCode := 200;
      AResponseInfo.ContentText := RespData;
      // set other AResponseInfo properties as needed, like ContentType, etc...
    end;
    

    OnCommandGet 处理程序退出时,TIdHTTPServer 会将AResponseInfo 的内容发送给客户端(如果尚未发送)。

    此外,如果您使用的是 Delphi 2009+ 并且FullResponce 是一个普通字符串(即UnicodeString),那么您对WriteFile() 的使用是不正确的。它需要看起来像这样:

    WriteFile(FileHandle, PChar(FullResponce)^, Length(FullResponce) * SizeOf(Char), LongWord(ReadRes), nil);
    

    考虑改用TFile.WriteAllText()

    TFile.WriteAllText('c:\lastresp.txt', FullResponce);
    

    但是,将日志记录添加到 TIdHTTPServer 代码的另一种方法是将 TIdLogFile 组件分配给 AContext.Connection.IOHandler.Intercept 属性,并让 Indy 自己准确记录它发送给客户端的内容,例如:

    uses
      ..., IdLogFile;
    
    procedure TMyForm.IdHTTPServer1CommandGet(AContext: TIdContext;
      ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
    var
      Log: TIdLogFile;
    begin
      Log := TIdLogFile.Create(nil);
      try
        Log.Filename := 'c:\lastresp.txt';
        Log.LogTime := False;
        Log.ReplaceCRLF := False;
        Log.Active := True;
    
        AContext.Connection.IOHandler.Intercept := Log;
    
        ...
    
        AResponseInfo.ResponseCode := 200;
        AResponseInfo.ContentText := RespData;
        // set other AResponse properties as needed, like ContentType, etc...
        ...
    
        AResponse.WriteContent;
      finally
        Log.Free;
      end;
    end;
    

    【讨论】:

    • 感谢您的回答,但我认为 WriteDirect 是正确的方法,考虑到我已经有一个有效的标题和响应内容,在这种情况下要求 Indy 重新生成标题是没有意义的跨度>
    • 附言。当然FullResponce是AnsiString,否则会和WriteDirect的TIdBytes参数不兼容
    • @Vlad__1 作为 Indy 的当前维护者,我是 Indy 所有事情的官方权威,我告诉你你在做什么不是正确的方法。我在回答中概述的是正确的方法。您不应该创建自己的响应标头。如果你这样做了,使用TIdHTTPServer 没有意义,只需使用TIdTCPServer 代替。但是,即使您制作自己的标头,WriteDirect() 仍然不是可行的方法,请改用Write() 重载。您不能将AnsiString 直接传递给WriteDirect(),它与TIdBytes 参数不在内存中二进制兼容
    • “作为 Indy 的当前维护者”我很惊讶,请原谅我的讽刺,但是......你能解释一下 AnsiString 和放置在内存中的字节数组之间的区别,如果他们的数据是相同的?) PS,我能问一下,为什么方法 TIdSSLContext.LoadXxxx(Cert, Keys, etc) 根本不起作用? (当 SSLOptions.XXXFile 设置正确时,异常 'File "" not found')
    • @Vlad__1 AnsiStringTIdBytes 在内存中有不同的元数据。它们的 payloads 可能是二进制兼容的(1 字节元素),但它们的 元数据 不是。 AnsiString 在其AnsiChar 数据前面有一个StrRecTIdBytes 在其Byte 数据前面有一个TDynArrayRec。这两种类型不是二进制兼容的。如果您尝试像使用TIdBytes 一样使用AnsiString,编译器将查找不存在的TDynArrayRec。反之亦然,如果您尝试像使用 AnsiString 一样使用 TIdBytes,编译器将查找不存在的 StrRec
    猜你喜欢
    • 1970-01-01
    • 2016-10-29
    • 1970-01-01
    • 2012-12-19
    • 1970-01-01
    • 1970-01-01
    • 2021-04-05
    • 1970-01-01
    • 2015-03-16
    相关资源
    最近更新 更多