簡體   English   中英

Apache HTTPClient流式HTTP POST請求?

[英]Apache HTTPClient Streaming HTTP POST Request?

我正在嘗試使用Apache HTTPClient構建“全雙工”HTTP流式傳輸請求。

在我的第一次嘗試中,我嘗試使用以下請求代碼:

URL url=new URL(/* code goes here */);

HttpPost request=new HttpPost(url.toString());

request.addHeader("Connection", "close");

PipedOutputStream requestOutput=new PipedOutputStream();
PipedInputStream requestInput=new PipedInputStream(requestOutput, DEFAULT_PIPE_SIZE);
ContentType requestContentType=getContentType();
InputStreamEntity requestEntity=new InputStreamEntity(requestInput, -1, requestContentType);
request.setEntity(requestEntity);

HttpEntity responseEntity=null;
HttpResponse response=getHttpClient().execute(request); // <-- Hanging here
try {
    if(response.getStatusLine().getStatusCode() != 200)
        throw new IOException("Unexpected status code: "+response.getStatusLine().getStatusCode());

    responseEntity = response.getEntity();
}
finally {
    if(responseEntity == null)
        request.abort();
}

InputStream responseInput=responseEntity.getContent();
ContentType responseContentType;
if(responseEntity.getContentType() != null)
    responseContentType = ContentType.parse(responseEntity.getContentType().getValue());
else
    responseContentType = DEFAULT_CONTENT_TYPE;

Reader responseStream=decode(responseInput, responseContentType);
Writer requestStream=encode(requestOutput, getContentType());

請求掛在上面指定的行。 似乎代碼試圖在獲得響應之前發送整個請求。 回想起來,這是有道理的。 但是,這不是我所希望的。 :)

相反,我希望使用Transfer-Encoding: chunked發送請求標頭Transfer-Encoding: chunked ,接收HTTP/1.1 200 OK的響應標頭和Transfer-Encoding: chunked自己的Transfer-Encoding: chunked標頭,然后我會有一個全雙工流要使用的HTTP連接。

令人高興的是,我的HTTPClient有另一個基於NIO的異步客戶端,它有很好的用法示例(比如這個 )。 我的問題是:

  1. 我對同步HTTPClient行為的解釋是否正確? 或者我能做些什么來繼續以我描述的方式使用(更簡單的)同步HTTPClient?
  2. 在尋求響應之前,基於NIO的客戶端是否等待發送整個請求? 或者我是否能夠遞增地發送請求並同時以增量方式接收響應?

如果HTTPClient不支持這種模式,那么會有另一個HTTP客戶端庫嗎? 或者我應該計划編寫一個(最小的)HTTP客戶端來支持這種模式?

這是我對skim閱讀代碼的看法:

  1. 我不能完全同意非200響應意味着失敗的事實。 所有2XX響應大多有效。 查看wiki以獲取更多詳細信息

  2. 對於任何TCP請求,我建議收到整個響應以確認它是有效的。 我這樣說是因為,部分響應可能主要被視為錯誤的響應,因為大多數客戶端實現都無法使用它。 (想象一下服務器響應2MB數據並在此期間發生故障的情況)

一個單獨的線程必須寫入OutputStream才能使代碼正常工作。

  • 上面的代碼為HTTPClient提供了PipedInputStream。
  • PipedInputStream在將字節寫入相應的OutputStream時使字節可用。
  • 上面的代碼不會寫入OutputStream(必須由一個單獨的線程完成)。
  • 因此,代碼正好懸掛在您的評論所在的位置。
  • 在引擎蓋下,Apache客戶端說“inputStream.read()”,在管道流的情況下,需要先調用outputStream.write(bytes)(由一個單獨的線程)。
  • 由於您沒有從單獨的線程將字節泵入關聯的OutputStream,因此InputStream只是坐着並等待“其他線程”寫入OutputStream。

來自JavaDocs:

管道輸入流應連接到管道輸出流; 然后,管道輸入流提供寫入管道輸出流的任何數據字節。

通常,一個線程從PipedInputStream對象讀取數據,並且某些其他線程將數據寫入相應的PipedOutputStream。

建議不要嘗試使用單個線程中的兩個對象,因為它可能使線程死鎖。

管道輸入流包含緩沖區,在一定限度內將讀操作與寫操作分離。 如果為連接的管道輸出流提供數據字節的線程不再存在,則稱管道被“斷開”。

注意 :對我來說,因為在你的問題陳述中沒有提到管道流和並發,所以沒有必要。 嘗試使用Entity對象包裝ByteArrayInputStream(),然后首先進行健全性檢查......這應該可以幫助您縮小問題范圍。

更新

順便說一句,我寫了一個Apache的HTTP客戶端API [PipedApacheClientOutputStream]的反轉,它使用Apache Commons HTTP Client 4.3.4為HTTP POST提供了一個OutputStream接口。 這可能接近你正在尋找的......

調用代碼如下所示:

// Calling-code manages thread-pool
ExecutorService es = Executors.newCachedThreadPool(
  new ThreadFactoryBuilder()
  .setNameFormat("apache-client-executor-thread-%d")
  .build());


// Build configuration
PipedApacheClientOutputStreamConfig config = new      
  PipedApacheClientOutputStreamConfig();
config.setUrl("http://localhost:3000");
config.setPipeBufferSizeBytes(1024);
config.setThreadPool(es);
config.setHttpClient(HttpClientBuilder.create().build());

// Instantiate OutputStream
PipedApacheClientOutputStream os = new     
PipedApacheClientOutputStream(config);

// Write to OutputStream
os.write(...);

try {
  os.close();
} catch (IOException e) {
  logger.error(e.getLocalizedMessage(), e);
}

// Do stuff with HTTP response
...

// Close the HTTP response
os.getResponse().close();

// Finally, shut down thread pool
// This must occur after retrieving response (after is) if interested   
// in POST result
es.shutdown();

-

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM