Proxy servlet hangs on HttpClient.execute() - java

I need to write a servlet that basically just proxies each incoming request to the same URL path on a different host. Here's what I came up with using Apache Commons Http Client 4.1.3:
#WebServlet("/data/*")
public class ProxyServlet extends HttpServlet {
protected void doGet (HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpClient client = new DefaultHttpClient();
try {
String url = getMappedServiceUrlFromRequest(request);
HttpGet get = new HttpGet(url);
copyRequestHeaders(request, get);
HttpResponse getResp = client.execute(get);
response.setStatus(getResp.getStatusLine().getStatusCode());
copyResponseHeaders(getResp, response);
HttpEntity entity = getResp.getEntity();
if (entity != null) {
OutputStream os = response.getOutputStream();
try {
entity.writeTo(os);
} finally {
try { os.close(); } catch (Exception ignored) { }
}
}
} catch (Exception e) {
throw new ServletException(e);
} finally {
client.getConnectionManager().shutdown();
}
}
private void getMappedServiceUrlFromRequest (...)
private void copyResponseHeaders (...)
private void copyRequestHeaders (...)
}
This works just fine the first time the servlet is called. However, after the first time, the servlet hangs on the line client.execute(get).
There are plenty of Google hits for "HttpClient execute hangs", most of which suggest using an instance of ThreadSafeClientConnManager. Tried that, sadly didn't help.
I've spent several hours googling for the problem, but I haven't found anything that fixes it yet. I'd seriously appreciate any pointers as to what I am doing wrong here.

I suggest you are doing this the hard way. Just write a Filter that does the redirect.
Or even just a TCP server that listens at the port and just copies bytes back and forth. You don't really need to engage in the HTTP protocol at all in a proxy, unless you are implementing the CONNECT command, in which case that's the only piece of HTTP you need to understand, and its reply is the only HTTP response you need to know about. Everything else is just bytes.

Related

Cannot find a handler for POST with boundary

I'm in the midst of testing my application which is using an HTTP-server. Instead of mocking I decided to go with a HTTP server fixture. Meaning that I do not have to mock any productional code. To accomplish this goal I currently chose for a free to use 3rd party library fixd.
I was able to successfully create several unit tests - which are working by means of a GET request. Most are quite simple, i.e.:
#Test
public void verifyConnectionTest()
{
try
{
final String body = FileUtils.readFileToString(RESOURCE);
final String path = "/";
this.server.handle(Method.GET, path).with(
new HttpRequestHandler() {
#Override
public void handle(final HttpRequest request,
final HttpResponse response)
{
response.setStatusCode(200);
response.setContentType("text/xml");
response.setBody(body);
}
});
// Setting up my HTTP client
// Execute some tasks
// asserting of everything was valid
}
catch (final IOException e)
{
fail(e.getMessage());
}
}
But I now have to send a POST request with multipart/form-data. Which does not make much of a difference other than changing the method and content-type:
#Test
public void executeStepTest()
{
try
{
final String body = FileUtils.readFileToString(SERVICE_RESPONSE);
final String path = "/";
this.server.handle(Method.POST, path, "multipart/form-data").with(
new HttpRequestHandler() {
#Override
public void handle(final HttpRequest request,
final HttpResponse response)
{
response.setStatusCode(200);
response.setContentType("text/xml");
response.setBody(body);
}
});
// Setting up my HTTP client
// Execute some tasks
// asserting of everything was valid
}
catch (final IOException e)
{
fail(e.getMessage());
}
}
However I get the following error: [ERROR] could not find a handler for POST - / - multipart/form-data; boundary=bqCBI7t-VW1xaJW7BADmTiGMg9w_YM2sHH8ukJYx and my guess is that fixd doesn't recognize the boundary-party. Since the documentation does not show an example I'm quite stuck on this part.
I tried using some wildcards such as '*', no succes. Thus; I need a way to either tell fixd to accept that boundary or use some wildcards I didn't yet discover. Any help would be great, thanks!
I've been making some debug and it seems to be that the problem is in the fixd core.
Basically, fixd indexes every RequestHandlerImpl by a HandlerKey (which includes ContentType as part of the key) in the map handlerMap. See method org.bigtesting.fixd.core.FixtureContainer#resolve.
...
HandlerKey key = new HandlerKey(method, route, contentType);
RequestHandlerImpl handler = handlerMap.get(key);
if (handler == null) {
// Error
}
...
Problem: When the request is multipart/form-data, boundary data (which it's generated dinamically every request) is part of the content type. So, any handler is found in handlerMap because the key changes with every running.
I've made a little test only to check that this is the cause of the problem, passing the contentType to fixd server.handle after the creation of the multipart request, and it works fine.
See the test below:
#Test
public void verifyConnectionTest_multipart() {
try {
// 1. Create multipart request (example with http-commons 3.1)
PostMethod filePost = new PostMethod(url);
Part[] parts = { new StringPart("param", "value") };
MultipartRequestEntity request = new MultipartRequestEntity(parts, filePost.getParams());
filePost.setRequestEntity(request);
// 2. fixd server handle (passing the request content type)
this.server.handle(Method.POST, "/", request.getContentType()).with(
new HttpRequestHandler() {
#Override
public void handle(final HttpRequest request,
final HttpResponse response) {
response.setStatusCode(200);
response.setContentType("text/xml");
}
});
// 3. Execute multipart request
HttpClient client = new HttpClient();
int status = client.executeMethod(filePost);
// 4. Assertions
Assert.assertEquals(200, status);
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
Hope it helps you to clarify the problem. Cheers
This was a bug in fixd, and has been fixed in version 1.0.3. Your original code should work using this new version of fixd.

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.
There are 2 servers, both running the same code [A & B]
A HttpClient sends a request "AX" (POST) to B
B HttpService receives the "AX" post, processes it
B HttpClient sends a reply "BR" (POST) to A on a different port
Later This should happen after the connection to A is closed, or as close as possible
Right now the code doesn't actually care
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.
A Sends POST to B:8080. Get back a proper response inline, everything ok.
POST Connection to B:8080 gets closed properly
B sends new POST (like an ACK) to A (ex... B:53991 => A:9000).
A Processs everything. No issues
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.

Java - HttpServlet: When is the response sent to the client?

I was little experimenting with some HttpServlet stuff to understand it better. I wanted to build the scenario that a request is incoming and I need to send the response accordingly and as fast as possible and to do later some more work within the servlet. From my current understanding, the response should only be send to the client when the doGet or doPost method was returned. But from my example, the response is already sent back to the client within the processing of the commands in the servlet. So it is already returned when I was not expecting it to be.
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
Logger.getLogger(DisplayHeader.class.getName()).log(Level.SEVERE, null, ex);
}
response.setContentType("text/plain; charset=ISO-8859-1");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
final StringWriter sw = new StringWriter();
PrintWriter out = new PrintWriter(sw);
//TODO most be implemented SynchronizedStatusCodeDimo
out.println("StatusCode=0");
out.println("StatusText=Accepted");
out.println("paymentType=PaymentXY");
out = response.getWriter();
out.print(sw.toString());
out.flush();
out.close();
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(DisplayHeader.class.getName()).log(Level.SEVERE, null, ex);
} }
What is happening here, via Firebug I see that I already received the response generated after 510ms. I thought I would need more then 1500ms because of the sleeps. My understanding was based on this post here: Link
The HttpServletResponse will be controlled by your servlet container (Tomcat, Jetty etc.) .
If you write into the response the servlet container automatically flush the response after a defined buffer size (e.g Tomcat after 9000 byte). Usually you can configure it (in Tomcat with the parameter socketBuffer).
This is the way it works, if you do not control it by yourself.
In your case, you control the response by your self and after you call response.flush() the response will be send to the client.
If you had writte more the 9000 Byte (in Tomcat), the response will be send automatically (in the middle of it all).
(excuse my bad english)

Architecture for http client

I am developing application, with http client, and I wonder to make some elegant issue.
This is standard java http client whose work in background thread, and passing data by event's (witch realized by override methods). I have special class for background requests, that implements method sendRequest()
protected void sendRequest(final String url) {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
URI website = null;
try {
website = new URI(url);
} catch (URISyntaxException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
HttpGet request = new HttpGet();
request.setURI(website);
HttpResponse response = null;
try {
response = client.execute(request, httpContext);
} catch (IOException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
if (response != null)
{
HttpEntity entity = response.getEntity();
try {
InputStream is = entity.getContent();
if (Debug.isDebuggerConnected()==true)
{
String data = convertStreamToString(is);
int code = response.getStatusLine().getStatusCode();
if (httpEvent!=null)
httpEvent.HttpResponseArrived(data, code);
}
else
httpEvent.HttpResponseArrived(convertStreamToString(is),response.getStatusLine().getStatusCode());
}
catch (IOException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
}
});
t.start();
}
And also child class, for API to web server, wich have methods like that:
public void getSomeData(SomeParams param)
{
sendRequest("http://xxx.yy"+gson.toJson(param));
httpEvent = new HttpHandler()
{
#Override
public void HttpResponseArrived(String data, int code)
{
switch (code)
{
case 200:
//some code
break;
case 401:
//some code
break;
}
}
};
}
And my question: how elegant to handle server errors, for example 401? I need to do this in one place, in method that sending requests - sendRequest(). At first sight it is very easy: just handle 401, and if it's because expired cookie - call method Login() (in my design, it's look like getSomeData). But I want, not just login again, I need to request data, that I failed to get because the error. Of course, I can implement calling Login() method in every switch, like this:
case 401:
{
Login(CurrentContext.AuthData.Login, CurrentContext.AuthData.Password);
break;
}
But the login event implemented in Login() method;
Also, I can just write sendRequest(string authdata), subscrube for HttpHandler and by recursion call method thats implements this code. But I thind, it's not very good decision.
I really hope, that somebody already solve this problem, and there is the way, to turn it's in beautiful code!
Thanks, if you could to read this to the end:)
As for answer not comment.
Try to use http client framework - I prefer Apache HTTPClient. It provides wide controll over request and responses. Moreover it supports most common methods like GET POST etc. Cookie management, redirection handling and SSL support is also provided. Don't invent something that is already invented.
HttpClient - use v4.x

HttpServletResponse.flushBuffer() on timed out connection does not throw IOException

I am facing quite an unusual situation.
I have two Jboss (7.1) instances which communicate via HTTP.
Instance A opens an HTTP connection to instance B and sends some data to be processed. The connection has timeout set, so after N seconds if no response is read it throws SocketTimeoutEception. Some cleanup is performed and the connection is closed.
Instance B has a servlet, listening for such http requests and when one is received some computation is done. After that the response is populated and returned to the client.
The problem is that if the computation takes too much time, the client (A) will close the connection due to the time out, but server (B) will proceed as normal and will try to send the response after some time. I want to be able to detect that the connection is closed and do some house keeping, however I can't seem to be able to do that.
I have tried calling HttpServletResponse.flushBuffer(), but no exception is thrown. I have also explicitly set in the http request "Connection: close" to avoid persistent connection, but this had no effect. The http servlet resonse is processed as normal and disappears in the void without any exception. I do not know what I am doing wrong, I've read other questions on this site like:
Java's HttpServletResponse doesn't have isClientConnected method
Tomcat - Servlet response blocking - problems with flush
but they do not work in my case.
I think there might be something specific to the jboss servlet container, which causes to ignore or buffer the response, or perhaps the http connection is reused despite my efforts to close it from the client (A). I'd be happy if you could provide some pointers to where to look for the problem. I have spend several days on this and no relevant progress was made, so I need to resolve this urgently.
Here is the relevant code:
Client code (server A):
private static int postContent(URL destination, String fileName, InputStream content)
throws IOException, CRPostException
{
HttpURLConnection connection = null;
//Create the connection object
connection = (HttpURLConnection)destination.openConnection();
// Prepare the HTTP headers
connection.setDoOutput(true);
connection.setInstanceFollowRedirects(false);
connection.setRequestMethod("POST");
connection.setRequestProperty("content-type", "text/xml; charset=UTF-8");
connection.setRequestProperty("Content-Encoding", "zip");
connection.setRequestProperty("Connection", "close");//Try to make it non-persistent
//Timouts
connection.setConnectTimeout(20000);//20 sec timout
connection.setReadTimeout(20000);//20 sec read timeout
// Connect to the remote system
connection.connect();
try
{
//Write something to the output stream
writeContent(connection, fileName, content);
//Handle response from server
return handleResponse(connection);
}
finally
{
try
{
try
{
connection.getInputStream().close();//Try to explicitly close the connection
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
connection.disconnect();//Close the connection??
}
catch (Exception e)
{
logger.warning("Failed to disconnect the HTTP connection");
}
}
}
private static int handleResponse(HttpURLConnection connection)
throws IOException, CRPostException
{
String responseMessage = connection.getResponseMessage();//Where it blocks until server returns the response
int statusCode = connection.getResponseCode();
if (statusCode == HttpURLConnection.HTTP_OK)
{
logger.debug("HTTP status code OK");
InputStream in = connection.getInputStream();
try
{
if (in != null)
{
//Read the result, parse it and return it
....
}
}
catch (JAXBException e)
{
}
}// if
//return error state
return STATE_REJECTED;
}//handleResponse()
Server code (Server B):
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
String crXML = null;
MediaType mediaType = null;
Object result;
// Get the media type of the received CR XML
try
{
mediaType = getMediaType(request);
crXML = loadDatatoString(mediaType, request);
result = apply(crXML);
}
catch (Exception e)
{
logger.error("Application of uploaded data has failed");
//Return response that error has occured
....
return;
}
// Finally prepare the OK response
buildStatusResponse(response, result);
// Try to detect that the connection is broken
// and the resonse never got to the client
// and do some housekeeping if so
try
{
response.getOutputStream().flush();
response.flushBuffer();
}
catch (Throwable thr)
{
// Exception is never thrown !!!
// I expect to get an IO exception if the connection has timed out on the client
// but this never happens
thr.printStackTrace();
}
}// doPost(..)
public static void buildStatusResponse(HttpServletResponse responseArg, Object result)
{
responseArg.setHeader("Connection", "close");//Try to set non persistent connection on the response too - no effect
responseArg.setStatus(HttpServletResponse.SC_OK);
// write response object
ByteArrayOutputStream respBinaryOut = null;
try
{
respBinaryOut = new ByteArrayOutputStream();
OutputStreamWriter respWriter = new OutputStreamWriter(respBinaryOut, "UTF-8");
JAXBTools.marshalStatusResponse(result, respWriter);
}
catch (Exception e)
{
logger.error("Failed to write the response object", e);
return;
}
try
{
responseArg.setContentType(ICRConstants.HTTP_CONTENTTYPE_XML_UTF8);
responseArg.getOutputStream().write(respBinaryOut.toByteArray());
}
catch (IOException e)
{
logger.error("Failed to write response object in the HTTP response body!", e);
}
}//buildStatusResponse()
You are running into HTTP connection pooling at the client end. The physical connection isn't really closed, it is returned to a pool for possible later reuse. If it is idle for some timeout it is closed and removed from the pool. So at the moment the server flushBuffer() happened the connection was still alive.
OR
The data being flushed was small enough to fit into the socket send buffer at the sender, so the underlying write returned immediately and successfully, and the disconnect was only discovered later, asynchronously, by TCP.

Categories