我正在构建一个带有netty的HTTP proxy,它支持HTTP流水线操作。因此,我在单个通道上收到多个HttpRequest
对象,并获得匹配的HttpResponse
个对象。 HttpResponse
写入的顺序与HttpRequest
的顺序相同。如果写了HttpResponse
,则在HttpProxyHandler
收到writeComplete
事件时会写下一个。{/ p>
管道应该很方便:
final ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", new HttpRequestDecoder());
pipeline.addLast("encoder", new HttpResponseEncoder());
pipeline.addLast("writer", new HttpResponseWriteDelayHandler());
pipeline.addLast("deflater", new HttpContentCompressor(9));
pipeline.addLast("handler", new HttpProxyHandler());
关于this question,只有写调用的顺序应该是重要的,但是为了确保我构建另一个Handler(HttpResponseWriteDelayHandler
)来抑制writeComplete
事件,直到整个响应被写入
为了测试这一点,我在Firefox中启用了network.http.proxy.pipelining
并访问了包含许多图像和连接的页面(a news page)。问题是,尽管代理的日志认为它们已成功发送,但浏览器仍未收到一些响应。
我有一些发现:
304 - Not Modified
个响应(考虑浏览器缓存刷新页面),则不会出现此问题。bootstrap.setOption("sendBufferSize", 1048576);
或更高版本无效writeComplete
中发送HttpResponseWriteDelayHandler
事件之前,依赖于响应体大小的时间范围可以解决问题,但这是一个非常糟糕的解决方案。答案 0 :(得分:0)
我找到了解决方案,想要分享它,如果其他人有类似的问题:
HttpResponse
的内容太大了。要分析整个HTML文档在缓冲区中的内容。这必须再次在Chunks中拆分才能正确发送。如果HttpResponse
没有分块,我写了一个简单的解决方案来做到这一点。需要在逻辑处理程序旁边放一个ChunkedWriteHandler
并编写这个类而不是响应本身:
public class ChunkedHttpResponse implements ChunkedInput {
private final static int CHUNK_SIZE = 8196;
private final HttpResponse response;
private final Queue<HttpChunk> chunks;
private boolean isResponseWritten;
public ChunkedHttpResponse(final HttpResponse response) {
if (response.isChunked())
throw new IllegalArgumentException("response must not be chunked");
this.chunks = new LinkedList<HttpChunk>();
this.response = response;
this.isResponseWritten = false;
if (response.getContent().readableBytes() > CHUNK_SIZE) {
while (CHUNK_SIZE < response.getContent().readableBytes()) {
chunks.add(new DefaultHttpChunk(response.getContent().readSlice(CHUNK_SIZE)));
}
chunks.add(new DefaultHttpChunk(response.getContent().readSlice(response.getContent().readableBytes())));
chunks.add(HttpChunk.LAST_CHUNK);
response.setContent(ChannelBuffers.EMPTY_BUFFER);
response.setChunked(true);
response.setHeader(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED);
}
}
@Override
public boolean hasNextChunk() throws Exception {
return !isResponseWritten || !chunks.isEmpty();
}
@Override
public Object nextChunk() throws Exception {
if (!isResponseWritten) {
isResponseWritten = true;
return response;
} else {
HttpChunk chunk = chunks.poll();
return chunk;
}
}
@Override
public boolean isEndOfInput() throws Exception {
return isResponseWritten && chunks.isEmpty();
}
@Override
public void close() {}
}
然后可以只调用channel.write(new ChunkedHttpResponse(response)
,并在需要时自动完成分块。