当前位置: 动力学知识库 > 问答 > 编程问答 >

java - Getting a ConnectionClosedException with Http.Core talking to Http.Client 4?

问题描述:

I am trying my hand at using http.core & client 4.3. In general it works well, and is quite pleasant to deal with. However, I am getting a ConnectionClosedException on one of my transfers and I can't see why. Others work just fine as far as I can tell.

Everything follows the examples in a pretty straight forward way. If it didn't, it was re-written to as much as possible in an effort to get rid of this.

  1. There are 2 servers, both running the same code [A & B]
  2. A HttpClient sends a request "AX" (POST) to B
  3. B HttpService receives the "AX" post, processes it
  4. B HttpClient sends a reply "BR" (POST) to A on a different port

    1. Later This should happen after the connection to A is closed, or as close as possible
    2. Right now the code doesn't actually care
  5. A receives the reply from B (on a different thread) and does things

In the problem scenario, A is running as the server, and B is sending a POST. Sorry it isn't always clear, since in one transaction both sides end up running server and client code.

  1. A Sends POST to B:8080. Get back a proper response inline, everything ok.
  2. POST Connection to B:8080 gets closed properly
  3. B sends new POST (like an ACK) to A (ex... B:53991 => A:9000).

    1. A Processs everything. No issues
    2. A rasies ConnectionClosedException

Since I don't why it's happening for sure, I tried to put everything I think is relevant in there. My only thought right now is that it has something to with making sure I add/change connection control headers, but I can't see how that would affect anything.

Stack Trace from machine "A", when the reply from B comes

org.apache.http.ConnectionClosedException: Client closed connection

at org.apache.http.impl.io.DefaultHttpRequestParser.parseHead(DefaultHttpRequestParser.java:133)

at org.apache.http.impl.io.DefaultHttpRequestParser.parseHead(DefaultHttpRequestParser.java:54)

at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:260)

at org.apache.http.impl.DefaultBHttpServerConnection.receiveRequestHeader(DefaultBHttpServerConnection.java:131)

at org.apache.http.protocol.HttpService.handleRequest(HttpService.java:307)

at com.me.HttpRequestHandlerThread.processConnection(HttpRequestHandlerThread.java:45)

at com.me.net.http.HttpRequestHandlerThread.run(HttpRequestHandlerThread.java:70)

com.me.ExceptionHolder: Client closed connection

at com.me.log.Log.logIdiocy(Log.java:77)

at com.me.log.Log.error(Log.java:54)

at com.me.net.http.HttpRequestHandlerThread.run(HttpRequestHandlerThread.java:72)

Caused by: org.apache.http.ConnectionClosedException: Client closed connection

at org.apache.http.impl.io.DefaultHttpRequestParser.parseHead(DefaultHttpRequestParser.java:133)

at org.apache.http.impl.io.DefaultHttpRequestParser.parseHead(DefaultHttpRequestParser.java:54)

at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:260)

at org.apache.http.impl.DefaultBHttpServerConnection.receiveRequestHeader(DefaultBHttpServerConnection.java:131)

at org.apache.http.protocol.HttpService.handleRequest(HttpService.java:307)

at com.me.net.http.HttpRequestHandlerThread.processConnection(HttpRequestHandlerThread.java:45)

at com.me.net.http.HttpRequestHandlerThread.run(HttpRequestHandlerThread.java:70)

This is the code running on B, the "client" in this scenario. It is trying to POST the reply acknowledging that the first POST from A was received properly. There really isn't much to transmit, and the response should only be an HTTP 200:

try (CloseableHttpClient client = HttpClients.createDefault()) {

final HttpPost post = new HttpPost(url);

post.setHeaders(/* create application specific headers */);

ByteArrayEntity entity = new ByteArrayEntity(IOUtils.toByteArray(myStream));

post.setEntity(entity);

ResponseHandler<Void> responseHandler = new ResponseHandler<Void>() {

@Override

public Void handleResponse(HttpResponse response) throws ClientProtocolException, IOException {

StatusLine status = response.getStatusLine();

if (!NetUtil.isValidResponseCode(response)) {

throw new ClientProtocolException("Unexpected Error! Oops");

}

// consume the response, if there is one, so the connection will close properly

EntityUtils.consumeQuietly(response.getEntity());

return null;

}

};

try {

client.execute(post, responseHandler);

} catch (ClientProtocolException ex) {

// logic to queue a resend for 10 minutes later. not triggered

throw ex;

}

}

On A: This is called async because the response doesn't come in over the same http connection.

The main request handler does a lot more work, but it is amazing how little code there is actually controlling the HTTP in the handler/server side. Great library... that I am misusing somehow. This is the actual handler, with everything simplified a bit, validation removed, etc.

public class AsyncReceiverHandler implements HttpRequestHandler {

@Override

public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException {

// error if not post, other logic. not touching http. no errors

DefaultBHttpServerConnection connection = (DefaultBHttpServerConnection) context.getAttribute("connection");

Package pkg = NetUtil.createPackageFrom(connection); // just reads sender ip/port

NetUtil.copyHttpHeaders(request, pkg);

try {

switch (recieive(request, pkg)) {

case EH_OK:

response.setStatusCode(HttpStatus.SC_OK);

break;

case OHNOES_BAD_INPUT:

response.setStatusCode(HttpStatus.SC_BAD_REQUEST);

response.setEntity(new StringEntity("No MDN entity found in request body"));

// bunch of other cases, but are not triggered. xfer was a-ok

}

} catch (Exception ex) {

//log

}

}

private MyStatus receiveMdn(HttpRequest request, Package pkg) throws Exceptions..., IOException {

// validate request, get entity, make package, no issues

HttpEntity resEntity = ((HttpEntityEnclosingRequest) request).getEntity();

try {

byte[] data = EntityUtils.toByteArray(resEntity);

// package processing logic, validation, fairly quick, no errors thrown

} catch (Exceptions... ex) {

throw ExceptionHolder(ex);

}

}

}

This is the request handler thread. This and the server are taken pretty much verbatim from the samples. The service handler just starts the service and accept()s the socket. When it gets one, it creates a new copy of this, and calls start():

public HttpRequestHandlerThread(final HttpService httpService, final HttpServerConnection conn, HttpReceiverModule ownerModule) {

super();

this.httpService = httpService;

this.conn = (DefaultBHttpServerConnection) conn;

}

private void processConnection() throws IOException, HttpException {

while (!Thread.interrupted() && this.conn.isOpen()) {

/* have the service create a handler and pass it the processed request/response/context */

HttpContext context = new BasicHttpContext(null);

this.httpService.handleRequest(this.conn, context);

}

}

@Override

public void run() {

// just runs the main logic and reports exceptions.

try {

processConnection();

} catch (ConnectionClosedException ignored) {

// logs error here (and others).

} finally {

try { this.conn.shutdown(); } catch (IOException ignored) {}

}

}

}

网友答案:

Well, this seems stupid now, and really obvious. I ignored the issue for a while and moved on to other things, and the answer bubbled up from the subconscious, as they will.

I added this header back and it all cleared up:

post.setHeader("Connection", "close, TE")

Somehow the line to set the Connection header got removed, probably accidentally by me. A lot of them get set, and it was still there, just wrong in this code path. Basically, the server expects this connection to close immediately but the header was reverting to the default keep-alive. Since the client closes the connection as soon as it is done with it this was surprising the server, who was told otherwise, and rightly compliained :D In the reverse path everything was OK.

Since I had just changed the old stack to use HttpComponents I didn't look at headers and such, and I just assumed I was using it wrong. The old stack didn't mind it.

分享给朋友:
您可能感兴趣的文章:
随机阅读: