序
本文主要研究一下抛出NoHttpResponseException的调用链
异常堆栈
org.apache.http.NoHttpResponseException: xxx failed to respond
at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:141)
at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:56)
at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:259)
at org.apache.http.impl.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:163)
at org.apache.http.impl.conn.CPoolProxy.receiveResponseHeader(CPoolProxy.java:157)
at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:273)
at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:125)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:272)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186)
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)
HttpRequestExecutor.execute
org/apache/http/protocol/HttpRequestExecutor.java
/**
* Sends the request and obtain a response.
*
* @param request the request to execute.
* @param conn the connection over which to execute the request.
*
* @return the response to the request.
*
* @throws IOException in case of an I/O error.
* @throws HttpException in case of HTTP protocol violation or a processing
* problem.
*/
public HttpResponse execute(
final HttpRequest request,
final HttpClientConnection conn,
final HttpContext context) throws IOException, HttpException {
Args.notNull(request, "HTTP request");
Args.notNull(conn, "Client connection");
Args.notNull(context, "HTTP context");
try {
HttpResponse response = doSendRequest(request, conn, context);
if (response == null) {
response = doReceiveResponse(request, conn, context);
}
return response;
} catch (final IOException ex) {
closeConnection(conn);
throw ex;
} catch (final HttpException ex) {
closeConnection(conn);
throw ex;
} catch (final RuntimeException ex) {
closeConnection(conn);
throw ex;
}
}
doSendRequest返回null,则执行doReceiveResponse,这里会抛出NoHttpResponseException
doReceiveResponse
org/apache/http/protocol/HttpRequestExecutor.java
/**
* Waits for and receives a response.
* This method will automatically ignore intermediate responses
* with status code 1xx.
*
* @param request the request for which to obtain the response
* @param conn the connection over which the request was sent
* @param context the context for receiving the response
*
* @return the terminal response, not yet post-processed
*
* @throws IOException in case of an I/O error.
* @throws HttpException in case of HTTP protocol violation or a processing
* problem.
*/
protected HttpResponse doReceiveResponse(
final HttpRequest request,
final HttpClientConnection conn,
final HttpContext context) throws HttpException, IOException {
Args.notNull(request, "HTTP request");
Args.notNull(conn, "Client connection");
Args.notNull(context, "HTTP context");
HttpResponse response = null;
int statusCode = 0;
while (response == null || statusCode < HttpStatus.SC_OK) {
response = conn.receiveResponseHeader();
statusCode = response.getStatusLine().getStatusCode();
if (statusCode < HttpStatus.SC_CONTINUE) {
throw new ProtocolException("Invalid response: " + response.getStatusLine());
}
if (canResponseHaveBody(request, response)) {
conn.receiveResponseEntity(response);
}
} // while intermediate response
return response;
}
这里conn.receiveResponseHeader()会抛出NoHttpResponseException
receiveResponseHeader
org/apache/http/impl/DefaultBHttpClientConnection.java
public HttpResponse receiveResponseHeader() throws HttpException, IOException {
ensureOpen();
final HttpResponse response = this.responseParser.parse();
onResponseReceived(response);
if (response.getStatusLine().getStatusCode() >= HttpStatus.SC_OK) {
incrementResponseCount();
}
return response;
}
DefaultBHttpClientConnection的receiveResponseHeader方法在执行responseParser.parse()的时候会抛出NoHttpResponseException
parse
org/apache/http/impl/io/AbstractMessageParser.java
public T parse() throws IOException, HttpException {
final int st = this.state;
switch (st) {
case HEAD_LINE:
try {
this.message = parseHead(this.sessionBuffer);
} catch (final ParseException px) {
throw new ProtocolException(px.getMessage(), px);
}
this.state = HEADERS;
//$FALL-THROUGH$
case HEADERS:
final Header[] headers = AbstractMessageParser.parseHeaders(
this.sessionBuffer,
this.messageConstraints.getMaxHeaderCount(),
this.messageConstraints.getMaxLineLength(),
this.lineParser,
this.headerLines);
this.message.setHeaders(headers);
final T result = this.message;
this.message = null;
this.headerLines.clear();
this.state = HEAD_LINE;
return result;
default:
throw new IllegalStateException("Inconsistent parser state");
}
}
AbstractMessageParser的parse方法执行parseHead会抛出NoHttpResponseException
parseHead
org/apache/http/impl/conn/DefaultHttpResponseParser.java
protected HttpResponse parseHead(
final SessionInputBuffer sessionBuffer) throws IOException, HttpException {
//read out the HTTP status string
int count = 0;
ParserCursor cursor = null;
do {
// clear the buffer
this.lineBuf.clear();
final int i = sessionBuffer.readLine(this.lineBuf);
if (i == -1 && count == 0) {
// The server just dropped connection on us
throw new NoHttpResponseException("The target server failed to respond");
}
cursor = new ParserCursor(0, this.lineBuf.length());
if (lineParser.hasProtocolVersion(this.lineBuf, cursor)) {
// Got one
break;
} else if (i == -1 || reject(this.lineBuf, count)) {
// Giving up
throw new ProtocolException("The server failed to respond with a " +
"valid HTTP response");
}
if (this.log.isDebugEnabled()) {
this.log.debug("Garbage in response: " + this.lineBuf.toString());
}
count++;
} while(true);
//create the status line from the status string
final StatusLine statusline = lineParser.parseStatusLine(this.lineBuf, cursor);
return this.responseFactory.newHttpResponse(statusline, null);
}
DefaultHttpResponseParser的parseHead在通过sessionBuffer.readLine(this.lineBuf)读取为-1且count=0时抛出NoHttpResponseException
doSendRequest
org/apache/http/protocol/HttpRequestExecutor.java
/**
* Send the given request over the given connection.
* <p>
* This method also handles the expect-continue handshake if necessary.
* If it does not have to handle an expect-continue handshake, it will
* not use the connection for reading or anything else that depends on
* data coming in over the connection.
*
* @param request the request to send, already
* {@link #preProcess preprocessed}
* @param conn the connection over which to send the request,
* already established
* @param context the context for sending the request
*
* @return a terminal response received as part of an expect-continue
* handshake, or
* {@code null} if the expect-continue handshake is not used
*
* @throws IOException in case of an I/O error.
* @throws HttpException in case of HTTP protocol violation or a processing
* problem.
*/
protected HttpResponse doSendRequest(
final HttpRequest request,
final HttpClientConnection conn,
final HttpContext context) throws IOException, HttpException {
Args.notNull(request, "HTTP request");
Args.notNull(conn, "Client connection");
Args.notNull(context, "HTTP context");
HttpResponse response = null;
context.setAttribute(HttpCoreContext.HTTP_CONNECTION, conn);
context.setAttribute(HttpCoreContext.HTTP_REQ_SENT, Boolean.FALSE);
conn.sendRequestHeader(request);
if (request instanceof HttpEntityEnclosingRequest) {
// Check for expect-continue handshake. We have to flush the
// headers and wait for an 100-continue response to handle it.
// If we get a different response, we must not send the entity.
boolean sendentity = true;
final ProtocolVersion ver =
request.getRequestLine().getProtocolVersion();
if (((HttpEntityEnclosingRequest) request).expectContinue() &&
!ver.lessEquals(HttpVersion.HTTP_1_0)) {
conn.flush();
// As suggested by RFC 2616 section 8.2.3, we don't wait for a
// 100-continue response forever. On timeout, send the entity.
if (conn.isResponseAvailable(this.waitForContinue)) {
response = conn.receiveResponseHeader();
if (canResponseHaveBody(request, response)) {
conn.receiveResponseEntity(response);
}
final int status = response.getStatusLine().getStatusCode();
if (status < 200) {
if (status != HttpStatus.SC_CONTINUE) {
throw new ProtocolException(
"Unexpected response: " + response.getStatusLine());
}
// discard 100-continue
response = null;
} else {
sendentity = false;
}
}
}
if (sendentity) {
conn.sendRequestEntity((HttpEntityEnclosingRequest) request);
}
}
conn.flush();
context.setAttribute(HttpCoreContext.HTTP_REQ_SENT, Boolean.TRUE);
return response;
}
doSendRequest在request的header指定需要expect且是http1.1的时候才会通过conn.receiveResponseHeader()去接收response,否则response会返回null;这里先conn.sendRequestHeader(request),针对post等执行conn.sendRequestEntity,接着flush把等待发送的数据发送出去
小结
HttpRequestExecutor.execute方法,doSendRequest返回null,则执行doReceiveResponse,这里会抛出NoHttpResponseException;doSendRequest先执行conn.sendRequestHeader(request),针对post等执行conn.sendRequestEntity,接着flush把等待发送的数据发送出去,针对需要expect且是http1.1的这里的response才可能有值。
单纯从HttpRequestExecutor.execute代码上看抛出NoHttpResponseException时,不法断定服务端是否接收到请求
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。