高级查看HTTP标头



是否有可能在IndyTIdHTTPServer.OnConnect事件中读取HTTP标头(特别是GET标头)而不干扰随后的OnCommandGet事件?

如果我试图用ReadLn的循环拉它们,那么OnCommandGet永远不会着火。我需要在不从输入缓冲区中取出它们的情况下对它们进行高级浏览。

是否有可能在IndyTIdHTTPServer.OnConnect事件中读取HTTP标头(特别是GET标头)而不干扰随后的OnCommandGet事件?

如果它可能,因为您可以使用TIdIOHandler.WaitFor()方法等待头终止符到达TIdIOHandler.InputBuffer,返回在它之前收到的所有内容,而不从缓冲区中删除任何内容,例如:

procedure TMyForm.IdHTTPServer1Connect(AContext: TIdContext);
var
headers: String;
begin
header := AContext.Connection.IOHandler.WaitFor(EOL+EOL, False);
...
end;

但是,这有一些限制:

  • 它假设每一行都以字节序列$0D $0A结束,因此报头以字节序列$0D $0A $0D $0A结束。根据HTTP标准,这在技术上是正确的,并且通常就是这种情况。然而,一些客户端只使用$0A终止行,因此头将由$0A $0A终止。TIdHTTPServer通常会处理得很好,但使用WaitFor()就不行了。

    一个更健壮的解决方案是在循环中使用TIdIOHandler.CheckForDataOnSource(),手动扫描TIdIOHandler.InputBuffer,直到在缓冲区中找到$0D $0A $0D $0A$0A $0A

  • 如果在同一个连接上有多个HTTP请求,这将不起作用,如果使用HTTP keep-alive或HTTP pipelining,可能会发生这种情况。你会"偷看"只有第一个HTTP请求的头。

如果我尝试用ReadLn的循环拉它们,那么OnCommandGet永远不会触发。

正确,因为TIdHTTPServer期望从InputBuffer读取它们。如果你事先自己阅读它们,那么TIdHTTPServer就不会有任何东西可以阅读,所以它甚至不会知道每个HTTP请求是什么样子的。

我需要在不从输入缓冲区中取出它们的情况下对它们进行高级预览。

为什么?如果你能得到它们,你想用它们做什么?

您应该检查TIdHTTPServer.OnHeadersAvailable事件是否适合您的需要。它在每个HTTP请求开始时触发,在从InputBuffer读取报头之后,但在读取请求正文之前。

根据Remy的建议,我通过窥视Inputbuffer使其工作:

procedure TForm1.IdHTTPServer1Connect(AContext: TIdContext);
var
s: string;
Done: boolean;
begin
Done := False;
repeat
Sleep(10);
if AContext.Connection.IOHandler.CheckForDataOnSource then
begin
s := AContext.Connection.IOHandler.InputBuffer.AsString;
if (Pos(#13#10#13#10, s) > 0) or (Pos(#10#10, s) > 0) then Done := True;
end;
until Done;
...
end;

我可以看到发生的一个问题是一个机器人在我的端口上建立TCP连接,并且循环永远不会结束,因为没有头来了。我需要添加一些超时检查

使用OnHeadersAvailable的另一个建议不会为我工作,因为它被称为OnCommandGet之前每次(即,每次连接多次当KeepAlive是True),所以我不妨只是把测试在OnCommandGet如果我走了那条路。

编辑:

我也刚刚尝试在OnConnect处理程序中这样做:

s := AContext.Connection.IOHandler.WaitFor(#10, False, True, nil, 1000);

因为我只需要GET行,而且如果包含它,它将始终是第一个(对吗?),所以我只需要找到第一个换行字符。它解决了行终止符问题,还有一个超时参数解决了bot问题。虽然这确实读取第一行标题,但它也会导致立即断开连接,并且永远不会调用CommandGet。我做错了什么?