Apache HttpClient 4.xは、より大きいファイルをアップロードするときに奇妙な動作をしますか?

私は、Java(およびscala)を使用して、少しストレートなクライアント/サーバーアプリケーションを開発し、テストしています。

サーバーは、 com.sun.net.httpserver.HttpServer に基づいており、POSTおよびPUT操作を使用して基本的なRESTfulインターフェイス経由でファイルをアップロードできます。アップロード操作は、Googleが独自に実装したダイジェスト認証を使用して制限されています。ブラウザ、カール、および Apache HttpClient をクリックします。

クライアントをアップロードすると Apache HttpClient 4.1.2 がラップされ、ファイルエンティティをアップロードするためにhttp経由でPUT操作が実行されます。ファイルのコンテンツタイプはヘッダーに application/xml として指定され、一度に1つのファイルのみがアップロードされます。

さまざまなサイズのファイルをアップロードすると、奇妙な動作が観察されることがあります:

  • サイズが1.076.006バイト以下のファイルがアップロードされます 正常
  • サイズが1.122.158バイト以上のファイル java.net.SocketException:壊れたパイプで失敗します。

最大作業サイズに近似するように異なるサイズのファイルを手動で作成したため、正確なクリティカルサイズは不明です

破損したパイプの理由は、クライアントがサーバーログで文書化されているように、 www-authenticate レスポンスをアップロードしたファイルを無視したためです。 「無視」とは、認証ヘッダーを一切含まない複数のメッセージを送信するという意味です。 しかし、より小さいファイルがうまく動作し、クライアントは www-authenticate レスポンスの直後に正しいchallenge-responseを持つ認証要求を正しく送信します。

アップロードはすべてのサイズのファイルでカールしているので、問題はありません。

だからこの時点で、あなたのクライアントにはいくつかのバグがあります。わかりました。しかし、私は親切ですが、オープンソースのJavaを試しました。 RESTclient (これはapache httpclientもラップしています)、同じ動作を正確に持っています!

私たちはインターネット上でこのクライアントを使って試してみました。だから今、私は、この誤った振る舞いにつながる Apache HttpClient に何か重要なものを置くことを忘れてしまい、オープンソースのRESTクライアントの開発者がそれを見逃してしまったのではないかと思います。それは素晴らしいだろう!

4

1 答え

ほとんどの場合、この状況につながるいくつかの要素の組み合わせです

(1)認証ヘッダーを含まないリクエストで大量のリクエストエンティティを送信するときに、クライアントが 'expect-continue'ハンドシェイクを使用しない可能性があります。

(2)サーバは、リクエストが期待通りに失敗したことを早期に検出し、フルのリクエストボディを読み込んで破棄するのではなく、401ステータスで早期に応答し、最後に接続を閉じます。私の意見では、これはサーバー側のHTTPプロトコル違反です。

(3)一部のHTTPエージェントは早期の応答に対処できますが、Apache HttpClientはJavaブロッキングI/O(実行スレッドはブロッキングソケットから読み書きできますが、両方ではありません)の制限のためにできません。

問題を解決するには複数の方法がありますが、「最も期待している」ハンドシェイクは最も簡単で最も自然なものです。代わりに、簡単なHEADまたはGET要求を実行して、大きなPOSTまたはPUT要求を実行する前にHTTP認証を強制することもできます。 HttpClientは、同じ論理HTTPセッションで後続の要求に認証データを再利用することができます。

6
追加された
@mtszそれは私のビジネスの一つではありませんが、最近、たくさんのまともな組み込み可能なHTTPサーバーがあると、SunのServerImplを使用していますか?
追加された 著者 oleg,
説明をありがとう、それは完全に理にかなっています!今、私は 'expect-continue'の解決策に行きます。クライアントでは、ブール値を反転しているだけです。サーバーでのハンドシェイクは現在作業中ですが、私はこれが問題を解決するはずと確信しています。
追加された 著者 mtsz,
不思議なことに、太陽のhttpserver 常にはアプリケーションを使わずに100-continueで応答します。プロトコルに違反しているようです(RFC 2616「100(継続)ステータスの使用」、httpserver-source: docjar.com/html/api/sun/net/httpserver/ServerImpl.java.html )‌しかし、大量のデータを送信する前に、2番目のリクエストで認証をトリガーする2番目のソリューションを実装しました。これはうまくいきます。
追加された 著者 mtsz,
私は大学でプロジェクトをやっているので、私の教授が選択して埋め込んだことは...私はそれを誰も使用していないことに気づいた。しかし、私はいくつか面白いことをこの難しい方法を学んだので、合計でそれはすべて悪いわけではありません;)少なくとも私はまともなサーバーをより感謝します!
追加された 著者 mtsz,