深入了解 HTTP headers
Advanced look at HTTP headers
是否可以在 Indy TIdHTTPServer.OnConnect
事件中读取 HTTP headers(特别是 GET
header)而不干扰后续 OnCommandGet
事件?
如果我尝试用 ReadLn
的循环拉动它们,那么 OnCommandGet
永远不会触发。我需要在不将它们从输入缓冲区中拉出的情况下深入了解它们。
Is it possible to read the HTTP headers (specifically the GET
header) in the Indy TIdHTTPServer.OnConnect
event without interfering with the subsequent OnCommandGet
event?
它 可能 ,因为您可以使用 TIdIOHandler.WaitFor()
方法等待 header 终止符到达 TIdIOHandler.InputBuffer
,返回之前收到的所有内容而不从缓冲区中删除任何内容,例如:
procedure TMyForm.IdHTTPServer1Connect(AContext: TIdContext);
var
headers: String;
begin
header := AContext.Connection.IOHandler.WaitFor(EOL+EOL, False);
...
end;
但是,这有一些限制:
它假定每一行都以字节序列 [=16=]D [=16=]A
结束,因此 header 以字节序列 [=17=]D [=17=]A [=17=]D [=17=]A
结束。根据 HTTP 标准,这在技术上是正确的,通常 会是这种情况。但是,一些客户端 do 仅以 [=18=]A
终止行,因此 header 将由 [=19=]A [=19=]A
终止。 TIdHTTPServer
通常会处理得很好,但使用 WaitFor()
就不行。
更强大的解决方案是在循环中使用 TIdIOHandler.CheckForDataOnSource()
,手动扫描 TIdIOHandler.InputBuffer
,直到在缓冲区中找到 [=17=]D [=17=]A [=17=]D [=17=]A
或 [=19=]A [=19=]A
。
如果同一连接上有多个 HTTP 请求,这将不起作用,如果使用 HTTP keep-alives 或 HTTP 流水线,则可能发生这种情况。您将“偷看”连接上的第一个 HTTP 请求的 header。
If I try to pull them with a loop of ReadLn
's then OnCommandGet
never fires.
正确,因为 TIdHTTPServer
期望是从 InputBuffer
读取它们的人。如果您事先自己阅读它们,TIdHTTPServer
将没有任何内容可供阅读,因此它甚至不知道每个 HTTP 请求是什么样的。
I need an advanced peek at them without pulling them from the input buffer.
为什么?如果你能得到它们,你想用它们做什么?
您应该检查 TIdHTTPServer.OnHeadersAvailable
活动是否符合您的需要。它在每个 HTTP 请求开始时触发,在从 InputBuffer
中读取 header 之后但在读取请求 body 之前。
根据 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 连接并且该循环永无止境,因为没有 header 出现。我需要添加某种超时检查。
另一个使用 OnHeadersAvailable 的建议对我不起作用,因为它每次都在 OnCommandGet 之前被调用(即,当 KeepAlive 为 True 时每个连接调用多次)所以如果我去了,我还不如将测试放在 OnCommandGet 中那条路线。
编辑:
我也刚刚尝试在 OnConnect 处理程序中这样做:
s := AContext.Connection.IOHandler.WaitFor(#10, False, True, nil, 1000);
因为我只需要 GET 行而且它总是第一个(对吧?)如果它被包含在内,我只需要找到第一个换行符。这解决了行终止符问题,并且有一个超时参数可以解决机器人问题。虽然这确实读取了第一行 header,但它也会导致立即断开连接并且永远不会调用 CommandGet。我做错了什么?
是否可以在 Indy TIdHTTPServer.OnConnect
事件中读取 HTTP headers(特别是 GET
header)而不干扰后续 OnCommandGet
事件?
如果我尝试用 ReadLn
的循环拉动它们,那么 OnCommandGet
永远不会触发。我需要在不将它们从输入缓冲区中拉出的情况下深入了解它们。
Is it possible to read the HTTP headers (specifically the
GET
header) in the IndyTIdHTTPServer.OnConnect
event without interfering with the subsequentOnCommandGet
event?
它 可能 ,因为您可以使用 TIdIOHandler.WaitFor()
方法等待 header 终止符到达 TIdIOHandler.InputBuffer
,返回之前收到的所有内容而不从缓冲区中删除任何内容,例如:
procedure TMyForm.IdHTTPServer1Connect(AContext: TIdContext);
var
headers: String;
begin
header := AContext.Connection.IOHandler.WaitFor(EOL+EOL, False);
...
end;
但是,这有一些限制:
它假定每一行都以字节序列
[=16=]D [=16=]A
结束,因此 header 以字节序列[=17=]D [=17=]A [=17=]D [=17=]A
结束。根据 HTTP 标准,这在技术上是正确的,通常 会是这种情况。但是,一些客户端 do 仅以[=18=]A
终止行,因此 header 将由[=19=]A [=19=]A
终止。TIdHTTPServer
通常会处理得很好,但使用WaitFor()
就不行。更强大的解决方案是在循环中使用
TIdIOHandler.CheckForDataOnSource()
,手动扫描TIdIOHandler.InputBuffer
,直到在缓冲区中找到[=17=]D [=17=]A [=17=]D [=17=]A
或[=19=]A [=19=]A
。如果同一连接上有多个 HTTP 请求,这将不起作用,如果使用 HTTP keep-alives 或 HTTP 流水线,则可能发生这种情况。您将“偷看”连接上的第一个 HTTP 请求的 header。
If I try to pull them with a loop of
ReadLn
's thenOnCommandGet
never fires.
正确,因为 TIdHTTPServer
期望是从 InputBuffer
读取它们的人。如果您事先自己阅读它们,TIdHTTPServer
将没有任何内容可供阅读,因此它甚至不知道每个 HTTP 请求是什么样的。
I need an advanced peek at them without pulling them from the input buffer.
为什么?如果你能得到它们,你想用它们做什么?
您应该检查 TIdHTTPServer.OnHeadersAvailable
活动是否符合您的需要。它在每个 HTTP 请求开始时触发,在从 InputBuffer
中读取 header 之后但在读取请求 body 之前。
根据 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 连接并且该循环永无止境,因为没有 header 出现。我需要添加某种超时检查。
另一个使用 OnHeadersAvailable 的建议对我不起作用,因为它每次都在 OnCommandGet 之前被调用(即,当 KeepAlive 为 True 时每个连接调用多次)所以如果我去了,我还不如将测试放在 OnCommandGet 中那条路线。
编辑:
我也刚刚尝试在 OnConnect 处理程序中这样做:
s := AContext.Connection.IOHandler.WaitFor(#10, False, True, nil, 1000);
因为我只需要 GET 行而且它总是第一个(对吧?)如果它被包含在内,我只需要找到第一个换行符。这解决了行终止符问题,并且有一个超时参数可以解决机器人问题。虽然这确实读取了第一行 header,但它也会导致立即断开连接并且永远不会调用 CommandGet。我做错了什么?