我试图在Indy中编写一个代理服务器,以接收来自外部客户端的HTTPS调用,并将它们以HTTP转发给同一台计算机上的另一个服务器应用程序。原因是其他应用程序不支持SSL,因此我想将其流量包装在SSL层中以确保外部安全。
我当前的方法是将TIdHTTPserver与SSL IOHandler一起使用,并在其OnCommandGet处理程序中动态创建一个TIdHTTP客户端,该客户端从内部应用程序获取TFileStream ContentStream并将该流作为Response.ContentStream返回给外部调用方。
这种方法的问题是由于在开始发送外部流之前必须等待内部内容流被完全接收而导致的延迟。例如,它不适用于流媒体。
我的问题是:是否有更好的方法将HTTPS代理到适用于流的HTTP?即无需使用中间文件流。
最佳答案
如果发出请求的客户端支持HTTP 1.1分块(请参见RFC 2616 Section 3.6.1),则可以从目标服务器读取数据并将其立即实时发送到客户端。
如果使用的是Indy的较新版本,则TIdHTTP
具有一个OnChunkReceived
事件,并且其hoNoReadChunked
属性中具有一个HTTPOptions
标志:
New TIdHTTP flags and OnChunkReceived event
在TIdHTTPServer.OnCommand...
事件处理程序中,可以根据需要填充AResponseInfo
。确保:
保留AResponseInfo.ContentText
和AResponseInfo.ContentStream
未分配
将AResponseInfo.ContentLength
设置为0
将AResponseInfo.TransferEncoding
设置为'chunked'
然后直接调用AResponseInfo.WriteHeader()
方法(例如在TIdHTTP.OnHeadersRecceived
事件中),以将响应标头发送到客户端。
然后,您可以使用OnChunkedReceived
或hoNoReadChunked
读取目标服务器的响应主体,并使用AContext.Connection.IOHandler
直接将每个接收到的块写入客户端。
但是,对此有一些警告:
如果使用TIdHTTP.OnChunkReceived
事件,则仍需要向TStream
提供输出TIdHTTP
,否则事件不会被触发(此限制可能会在将来的版本中删除)。但是,您可以使用TIdEventStream
而不为其分配OnWrite
事件处理程序。或编写一个覆盖虚拟TStream
方法的自定义Write()
类而不执行任何操作。或者,仅使用所需的任何TStream
,并让OnChunkReceived
事件处理程序清除接收到的Chunk
,因此没有任何内容可写入TStream
。
如果使用hoNoReadChunked
标志,则允许您在TIdHTTP.IOHandler
退出后直接从TIdHTTP
手动读取HTTP块。只需确保启用HTTP保持活动状态,否则TIdHTTP
将在您有机会读取服务器响应正文之前关闭与服务器的连接。
如果您使用的是Indy的旧版本,或者目标服务器不支持分块,则所有内容都不会丢失。您应该能够编写一个自定义的TStream
类,该类将覆盖虚拟的Write()
方法,以将提供的数据块作为HTTP块写入到客户端。然后,您可以将该类用作TStream
的输出TIdHTTP
。
如果客户端不支持HTTP分块,或者这些方法对您不起作用,则您可能不得不直接使用TIdTCPServer
而不是TIdHTTPServer
并从头开始实现整个HTTP协议,那么您可以根据需要处理自己的流式传输。看一下TIdHTTPProxyServer
的源代码以获得一些想法(TIdHTTPProxyServer
本身不适合您的特定情况,但通常会向您展示如何在连接之间实时传递HTTP请求/响应)。