我使用 httpclient-4.5.3(org.apache.httpcomponents).
创建了一个具有基本身份验证的 HTTP Post 请求 CredentialsProvider provider = new BasicCredentialsProvider();
provider.setCredentials(AuthScope.ANY,new UsernamePasswordCredentials("user","pass"));
RequestConfig requestConfig = RequestConfig.custom().
setConnectTimeout(2000).
setSocketTimeout(2000).
build();
try (CloseableHttpClient httpClient = HttpClients.custom().
setDefaultCredentialsProvider(provider).
setDefaultRequestConfig(requestConfig).
build()) {
String reqString = mapper.writeValueAsString(obj); // here obj is a Java class of request payload
StringEntity params = new StringEntity(reqString);
HttpPost httpPost = new HttpPost(url); // server url where double requests are going
httpPost.setHeader("Accept", "application/json");
httpPost.setHeader("Content-type", "application/json");
httpPost.setEntity(params);
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
final String jsonString = EntityUtils.toString(response.getEntity());
if (200 == response.getStatusLine().getStatusCode()) {
mapper = new ObjectMapper();
}
}
}
当 CloseableHttpResponse response = httpClient.execute(httpPost) 执行时,2 个请求发送到服务器,第一个请求在标头中没有基本授权,第二个请求在标头中带有基本授权。这是什么原因呢?
我深入研究了 httpclient-4.5.3 库,发现双重请求生成的实际点是 org.apache.http.impl.execchain.MainClientExec。在该类的 execute 方法中包含一个 for 循环,该循环实际上执行了 2 次。我在下面给出 for 循环...
for (int execCount = 1;; execCount++) {
if (execCount > 1 && !RequestEntityProxy.isRepeatable(request)) {
throw new NonRepeatableRequestException("Cannot retry request " +
"with a non-repeatable request entity.");
}
if (execAware != null && execAware.isAborted()) {
throw new RequestAbortedException("Request aborted");
}
if (!managedConn.isOpen()) {
this.log.debug("Opening connection " + route);
try {
establishRoute(proxyAuthState, managedConn, route, request, context);
} catch (final TunnelRefusedException ex) {
if (this.log.isDebugEnabled()) {
this.log.debug(ex.getMessage());
}
response = ex.getResponse();
break;
}
}
final int timeout = config.getSocketTimeout();
if (timeout >= 0) {
managedConn.setSocketTimeout(timeout);
}
if (execAware != null && execAware.isAborted()) {
throw new RequestAbortedException("Request aborted");
}
if (this.log.isDebugEnabled()) {
this.log.debug("Executing request " + request.getRequestLine());
}
if (!request.containsHeader(AUTH.WWW_AUTH_RESP)) {
if (this.log.isDebugEnabled()) {
this.log.debug("Target auth state: " + targetAuthState.getState());
}
this.authenticator.generateAuthResponse(request, targetAuthState, context);
}
if (!request.containsHeader(AUTH.PROXY_AUTH_RESP) && !route.isTunnelled()) {
if (this.log.isDebugEnabled()) {
this.log.debug("Proxy auth state: " + proxyAuthState.getState());
}
this.authenticator.generateAuthResponse(request, proxyAuthState, context);
}
response = requestExecutor.execute(request, managedConn, context);
// The connection is in or can be brought to a re-usable state.
if (reuseStrategy.keepAlive(response, context)) {
// Set the idle duration of this connection
final long duration = keepAliveStrategy.getKeepAliveDuration(response, context);
if (this.log.isDebugEnabled()) {
final String s;
if (duration > 0) {
s = "for " + duration + " " + TimeUnit.MILLISECONDS;
} else {
s = "indefinitely";
}
this.log.debug("Connection can be kept alive " + s);
}
connHolder.setValidFor(duration, TimeUnit.MILLISECONDS);
connHolder.markReusable();
} else {
connHolder.markNonReusable();
}
if (needAuthentication(
targetAuthState, proxyAuthState, route, response, context)) {
// Make sure the response body is fully consumed, if present
final HttpEntity entity = response.getEntity();
if (connHolder.isReusable()) {
EntityUtils.consume(entity);
} else {
managedConn.close();
if (proxyAuthState.getState() == AuthProtocolState.SUCCESS
&& proxyAuthState.getAuthScheme() != null
&& proxyAuthState.getAuthScheme().isConnectionBased()) {
this.log.debug("Resetting proxy auth state");
proxyAuthState.reset();
}
if (targetAuthState.getState() == AuthProtocolState.SUCCESS
&& targetAuthState.getAuthScheme() != null
&& targetAuthState.getAuthScheme().isConnectionBased()) {
this.log.debug("Resetting target auth state");
targetAuthState.reset();
}
}
// discard previous auth headers
final HttpRequest original = request.getOriginal();
if (!original.containsHeader(AUTH.WWW_AUTH_RESP)) {
request.removeHeaders(AUTH.WWW_AUTH_RESP);
}
if (!original.containsHeader(AUTH.PROXY_AUTH_RESP)) {
request.removeHeaders(AUTH.PROXY_AUTH_RESP);
}
} else {
break;
}
}
任何人都可以告诉我为什么第一个请求在标头中不包含任何基本授权,但第二个请求包含该内容?