2012-07-03 67 views
1

我正在用netty構建一個HTTP proxy,它支持HTTP流水線。因此,我在單個通道上收到多個HttpRequest對象並獲得匹配的對象。 HttpResponse寫入的順序與我得到HttpRequest的順序相同。如果編寫了HttpResponse,則在HttpProxyHandler收到writeComplete事件時將寫入下一個。寫入多個http響應是否有任何限制?

管道應方便:

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只寫調用的順序應該是重要的,但可以肯定我建一個處理程序(HttpResponseWriteDelayHandler)這抑制了writeComplete事件,直到整個響應被寫了。

爲了測試這個功能,我在Firefox中啓用了network.http.proxy.pipelining,並訪問了一個包含許多圖像和連接的頁面(a news page)。問題是,儘管代理的日誌認爲它們已成功發送,但瀏覽器仍未收到一些響應。

我有一些研究結果:

  • 只有從代理到服務器的連接比從代理到瀏覽器連接速度更快,會出現問題。
  • 在該連接上發送較大圖像後,該問題發生得更多,例如, 20KB
  • 的問題不會發生,如果只是304 - Not Modified反應被送往(清爽考慮瀏覽器緩存的頁面)
  • 設置bootstrap.setOption("sendBufferSize", 1048576);以上沒有幫助
  • 睡依賴於反應機身尺寸爲一個時間段中發送writeComplete前在HttpResponseWriteDelayHandler事件解決了這個問題,但是是一個非常糟糕的解決方案。

回答

0

我找到了解決方案,並希望分享它,如果任何人有類似的問題:

HttpResponse的內容是太大。要分析整個HTML文檔的內容在緩衝區中。這必須再次分塊,才能正確發送。如果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),如果需要分塊是自動完成的。

相關問題