I'm doing a GET request with the apache HttpClient. Is there a way to detect when the server disconnects while reading from the InputStream?
EOS on the input stream (read() returning -1, readLine() returning null, readXXX() throwing EOFException for any other XXX) is the primary mechanism, otherwise an IOException, typically 'connection reset'. Very rarely you may see a SocketException. If you are using read timeouts, a SocketTimeoutException.
Sure, check out the Exception Handling section of the docs.
Related
From the javadoc of java.net.Socket#setSoTimeout, it says:
Enable/disable SO_TIMEOUT with the specified timeout, in
milliseconds. With this option set to a non-zero timeout,
a read() call on the InputStream associated with this Socket
will block for only this amount of time. If the timeout expires,
a <B>java.net.SocketTimeoutException</B> is raised, though the
Socket is still valid.
For a http put operation, the client may upload a huge file, that the client is always writing, and never reading data from server.
In this case, if I set the SocketTimeout for the http client, will it throw TimeoutException during uploading?
No it won't. It may throw a timeout exception when reading the response code however. If the peer closes the connection during the upload, it will throw an IOException 'connection reset by peer'.
The doc explains that the HttpServletResponse#sendError() method throws an IOException if an input or output exception occurs (DRY ;).
I couldn't find any scenario that makes this method throw that exception, is there any?
HTTP is sent over TCP so you can safely assume that somewhere in the underlying HttpServletRequest and HttpServletResponse there is a SocketInputStream and a SocketOutputStream.
If a user closes their browser or the network goes down client-side or server-side, then the server won't be able to receive requests or send responses. If the disconnection happens while the server was in the process of sendError(), then an IOException will occur while writing to the SocketOutputStream.
From Socket documentation:
shutdownInput
public void shutdownInput()
throws IOException
Places the input stream for this socket at "end of stream". Any data sent to the input stream side of the socket is acknowledged and then silently discarded.
If you read from a socket input stream after invoking shutdownInput() on the socket, the stream will return EOF.
In order to test interaction between clients in a server, I've written some client bots.
These bots generate somewhat random client requests. Since these only write to the server, they have no need for the input stream, they do not need to read the updates the server sends. This is the main body of code for the bots:
private void runWriteBot(PrintWriter out) throws IOException {
//socket.shutdownInput();
String request;
System.out.println("Write bot ready.");
while (!quit) {
request = randomRequest();
out.println(request);
sleep();
}
}
If I uncomment the shutdownInput, an exception is thrown in the server's client handler:
Connection reset
I wasn't expecting an exception to be thrown on the other side of the socket. The documentation suggests (to me, at least) that anything sent by the other side will just be silently discarded, causing no interference with the other end's activity, ie without having the other side throw an exception.
Can I just ignore what the server sends, or should I drain what comes to the input stream?
Is there any automagic way of doing it, or do I need to regularly read and ignore?
The behaviour when you call shutdownInput() is platform-dependent.
BSD Unix will silently discard any further input.
Linux will keep buffering the input, which will eventually block the sender, or cause him to get EAGAIN/EWOULDBLOCK if he is in non-blocking mode.
Windows will reset the connection if any further data arrives.
This is determined by the platform, not by Java.
I don't see any need for calling shutdownInput() in most situations. The only thing it is really useful for is unblocking a read. In your situation you are going to have to read the server responses.
I have tried writing to the response, because there is no proper disconnect callback:
private boolean write(byte[] output, AsyncContext context) {
try {
OutputStream stream = context.getResponse().getOutputStream();
stream.write(output);
stream.flush();
return true;
} catch (IOException ex) {
//client disconnected
log.error(ex);
removeAsyncContext(context);
return false;
}
}
But this didn't seem to the trick. When the client is disconnected, writing and flushing the buffer did not throw an exception.
The weird thing is, the second time you try to write to the output stream (after the disconnect), the write does throw an exception. It looks like the first time you write/flush it,some internal state is set to error, without notifying.
I have tried on both Jetty 8 and Tomcat 7 and I see the same behavior.
Is there a solution to find out whether the message is received by the client? Am I missing something?
I recognize you are looking for a proper way of detecting disconnects, but for those who don't mind a kludge:
Note: This method periodically sends space characters to the client that must be trimmed before interpreting the results. This is the kludgey part.
Start a thread that has access to the writer/outputstream of the servlet response. This thread sends space characters periodically (I used 1 second intervals) to the client. Wrap in a IOException try/catch block that sets your abort flag.
If the connection is closed, most servlets will throw a flavor of EOFException (which is an IOException) when data cannot be delivered to the client. You are catching this exception and setting your abort flag.
When the abort flag is caught, you have options. You can nuke the executing thread, have all your processing periodically check for the abort flag, push an exception into the executing thread, or do any number of things (not detailed here).
Should the process finish successfully, you will get your results prefixed by a number of spaces. Again, remember to trim these on your client.
In my experience when a client disconnects from a servlet there is an exception referring to a Broken Pipe.
For example: Broken Pipe when writing bytes in ServletOutputStream
I would suggest catching java.net.SocketException and looking at the exception details to verify if it is a broken pipe (as a starting point):
Caused by: ClientAbortException: java.net.SocketException: Broken pipe
at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:358)
at org.apache.tomcat.util.buf.ByteChunk.append(ByteChunk.java:354)
at org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:381)
at org.apache.catalina.connector.OutputBuffer.write(OutputBuffer.java:370)
at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:89)
How can I detect that the client side of a tomcat servlet request has disconnected? I've read that I should do a response.getOutputStream().print(), then a response.getOutputStream().flush() and catch an IOException, but is there a way I can detect this without writing any data?
EDIT:
The servlet sends out a data stream that doesn't necessarily end, but doesn't necessarily have any data flowing through it (it's a stream of real time events). I need to actually detect when the client disconnects because I have some cleanup I have to do at that point (resources to release, etcetera). If I have the HttpServletRequest available, will trying to read from that throw an IOException if the client disconnects?
is there a way I can detect this
without writing any data?
No because there isn't a way in TCP/IP to detect it without writing any data.
Don't worry about it. Just complete the request actions and write the response. If the client has disappeared, that will cause an IOException: connection reset, which will be thrown into the servlet container. Nothing you have to do about that.
I need to actually detect when the client disconnects because I have some cleanup I have to do at that point (resources to release, etcetera).
There the finally block is for. It will be executed regardless of the outcome. E.g.
OutputStream output = null;
try {
output = response.getOutputStream();
// ...
output.flush();
// ...
} finally {
// Do your cleanup here.
}
If I have the HttpServletRequest available, will trying to read from that throw an IOException if the client disconnects?
Depends on how you're reading from it and how much of request body is already in server memory. In case of normal form encoded requests, whenever you call getParameter() beforehand, it will usually be fully parsed and stored in server memory. Calling the getInputStream() won't be useful at all. Better do it on the response instead.
Have you tried to flush the buffer of the response:
response.flushBuffer();
Seems to throw an IOException when the client disconnected.