【发布时间】:2018-08-14 23:51:28
【问题描述】:
我有一个独立的 Java 客户端尝试通过 NTLM 代理执行 RMI。 它是多线程的。 我正在使用 Apache httpclient 4.5.6。 我有一个 5 分钟超时周期的代理。
基本情况有效,只要 2 个线程没有在代理超时的确切时间同时发出请求,代理就会每 5 分钟重新验证一次。然后它失败了。一旦失败,所有后续尝试都会失败。
我附上了一个 wireshark 截图来澄清(截图来自 4.5.2,但我升级到 4.5.6 并看到了相同的行为)。
一个好的循环看起来像
- 客户端尝试 CONNECT(无 NTML 标志)
- 代理回复 407(无 NTML 标志)
- 客户端再次尝试使用 ntlm 消息类型 NTLMSSP_NEGOTIATE 连接
- 代理回复 407 NTLMSSP_CHALLENGE
- 客户端使用 NTLMSSP_AUTH 和我的凭据进行连接。
- 代理回复 200,我们可以再等 5 分钟。
一个糟糕的循环看起来像
- 客户端尝试 CONNECT(无 NTML 标志)
- 代理回复 407(无 NTML 标志)
- 客户端再次尝试使用 ntlm 消息类型 NTLMSSP_NEGOTIATE 连接
- 客户端尝试 CONNECT(无 NTML 标志)
- 代理回复 407(无 NTML 标志)
- 代理回复 407 NTLMSSP_CHALLENGE
- 几秒钟内就会出现一大堆没有 NTML 标志的 CONNECT 和 407。
对我来说,这看起来像是非线程安全代码中的多线程竞争条件。
使用 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)
任何想法如何防止这种情况或解决它或从中恢复? (除了通话同步,这会大大降低已经很慢的应用程序)
应用中的一些代码 sn-ps:
// 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);
【问题讨论】:
-
HttpClientContext是否被多个线程同时使用? -
是的。 hc.apache.org/httpcomponents-client-4.5.x/tutorial/html/… 的第 4.7 节说总是使用同一个,尽管它没有提到线程。
-
不要 不要。
标签: java multithreading apache-httpclient-4.x ntlm