[英]Multipart Post request in Apache Httpclient 4.5.6 not working
[英]multithread issue with NTLM and Java Apache httpclient 4.5.6
我有一個獨立的Java客戶端,試圖通過NTLM代理執行RMI。 它是多線程的。 我正在使用Apache httpclient 4.5.6。 我有一個5分鍾的超時周期的代理。
基本情況是可行的,只要受到代理的挑戰,每5分鍾就會重新進行身份驗證,只要兩個線程在代理超時的確切時間未同時發出請求即可。 然后失敗。 一旦失敗,所有后續嘗試都會失敗。
我附上了wireshark屏幕截圖以進行澄清(屏幕截圖來自4.5.2,但我升級到4.5.6並看到了相同的行為)。
一個好的周期看起來像
糟糕的周期看起來像
對我來說,這看起來像非線程安全代碼中的多線程競爭條件。
使用Apache httpclient 4.5.2,它只是傳播了407,我在CloseableHttpResponse.getStatusLine()。getStatusCode()中檢測到它。 使用Apache httpclient 4.5.6,我看到了
java.lang.IllegalStateException: Auth scheme is null
at org.apache.http.util.Asserts.notNull(Asserts.java:52)
at org.apache.http.impl.auth.HttpAuthenticator.ensureAuthScheme(HttpAuthenticator.java:229)
at org.apache.http.impl.auth.HttpAuthenticator.generateAuthResponse(HttpAuthenticator.java:184)
at org.apache.http.impl.execchain.MainClientExec.createTunnelToTarget(MainClientExec.java:484)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:411)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:237)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
有什么想法可以防止這種情況發生或如何解決或從中恢復嗎? (除了同步通話,這會大大降低已經很慢的應用的運行速度)
該應用程序的一些代碼段:
// this is done only once
HttpClientBuilder builder = HttpClients.custom();
SocketConfig.Builder socketConfig = SocketConfig.custom();
RequestConfig.Builder requestConfig = RequestConfig.custom();
HttpHost proxy = new HttpHost(proxyHost, proxyPort);
builder.setProxy(proxy);
requestConfig.setProxy(proxy);
builder.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy());
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
String localHost = getLocalHostname();
credentialsProvider.setCredentials(
new AuthScope(proxyHost, proxyPort, AuthScope.ANY_REALM, "ntlm"),
new NTCredentials(user, password, localHost, domain));
builder.setDefaultCredentialsProvider(credentialsProvider);
builder.setDefaultSocketConfig(socketConfig.build());
builder.setDefaultRequestConfig(requestConfig.build());
CloseableHttpClient client = builder.build();
...
// cached, we use the same one every time in accordance with section 4.7 of
// https://hc.apache.org/httpcomponents-client-4.5.x/tutorial/html/authentication.html
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credentialsProvider);
...
// new HttpPost every time
HttpPost postMethod = new HttpPost(uri);
postMethod.setEntity(new ByteArrayEntity(bytesOut.toByteArray()));
response = client.execute(postMethod, context);
HttpContext
實例是完全線程安全的。 但是,存儲在上下文中的某些屬性(如身份驗證握手狀態)顯然不是。 確保HttpContext
實例不會同時更新,並且該問題應該消失。
謝謝奧列格(Oleg),這是我所做的,到目前為止,它似乎一直在起作用(對於您的答案發表評論的時間太長,但我想分享我的代碼)
// I use the base version when not going through a proxy
public class HttpClientContextFactory {
public HttpClientContext create() {
return HttpClientContext.create();
}
}
// I use this when I go through a NTLM proxy
private HttpClientContextFactory getNtlmContextFactory(
final CredentialsProvider credentialsProvider) {
return new HttpClientContextFactory() {
ThreadLocal<HttpClientContext> tlContext = ThreadLocal
.<HttpClientContext> withInitial(() -> {
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credentialsProvider);
return context;
});
@Override
public HttpClientContext create() {
return tlContext.get();
}
};
}
// then do this when I connect to the server
response = client.execute(postMethod, contextFactory.create());
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.