1

HTTP パイプラインをサポートする netty を使用してHTTP プロキシを構築しています。したがってHttpRequest、1 つのチャネルで複数のオブジェクトを受け取り、一致するHttpResponseオブジェクトを取得しました。HttpResponse書き込みの順序は、私が取得したものと同じですHttpRequest。a が書き込まれた場合、がイベントを受け取るHttpResponseと、次のものが書き込まれます。HttpProxyHandlerwriteComplete

パイプラインは便利なはずです:

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());

この質問に関しては、書き込み呼び出しの順序のみが重要ですが、応答全体が書き込まれるまでイベントHttpResponseWriteDelayHandlerを抑制する別の Handler ( ) を確実に作成します。writeComplete

これをテストするために、私network.http.proxy.pipeliningは Firefox で有効にして、多くの画像と接続を含むページ (ニュース ページ) にアクセスしました。問題は、プロキシのログが正常に送信されたと見なしているにもかかわらず、ブラウザーが一部の応答を受信しないことです。

いくつかの発見があります:

  • この問題は、プロキシからサーバーへの接続がプロキシからブラウザへの接続よりも高速な場合にのみ発生します。
  • この問題は、その接続でより大きな画像 (20kB など) を送信した後に、より頻繁に発生します。
  • 304 - Not Modified応答のみが送信された場合 (ブラウザのキャッシュを考慮してページを更新)、問題は発生しません。
  • 設定bootstrap.setOption("sendBufferSize", 1048576);以上では役に立たない
  • writeCompleteイベントを送信する前に、応答本文のサイズに応じた時間枠をスリープ状態にHttpResponseWriteDelayHandlerすることで問題は解決しますが、これは非常に悪い解決策です。
4

1 に答える 1

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)、チャンク化は必要に応じて自動的に行われます。

于 2012-07-12T05:31:45.690 に答える