protected void doRead(HttpServletRequest request,
            HttpServletResponse response, String tunnelSessionToken)
            throws GuacamoleException {

        // Get tunnel, ensure tunnel exists
        GuacamoleTunnel tunnel = getTunnel(tunnelSessionToken);

        // Ensure tunnel is open
        if (!tunnel.isOpen())
            throw new GuacamoleResourceNotFoundException("Tunnel is closed.");

        // Obtain exclusive read access
        GuacamoleReader reader = tunnel.acquireReader();

        try {

            // Note that although we are sending text, Webkit browsers will
            // buffer 1024 bytes before starting a normal stream if we use
            // anything but application/octet-stream.
            response.setContentType("application/octet-stream");
            response.setHeader("Cache-Control", "no-cache");

            // Get writer for response
            Writer out = new BufferedWriter(new OutputStreamWriter(
                    response.getOutputStream(), "UTF-8"));

            // Stream data to response, ensuring output stream is closed
            try {

                // Deregister tunnel and throw error if we reach EOF without
                // having ever sent any data
                char[] message = reader.read();
                if (message == null)
                    throw new GuacamoleConnectionClosedException("Tunnel reached end of stream.");

                // For all messages, until another stream is ready (we send at least one message)
                do {

                    // Get message output bytes
                    out.write(message, 0, message.length);

                    // Flush if we expect to wait
                    if (!reader.available()) {
                        out.flush();
                        response.flushBuffer();
                    }

                    // No more messages another stream can take over
                    if (tunnel.hasQueuedReaderThreads())
                        break;

                } while (tunnel.isOpen() && (message = reader.read()) != null);

                // Close tunnel immediately upon EOF
                if (message == null) {
                    deregisterTunnel(tunnelSessionToken);
                    tunnel.close();
                }

                // End-of-instructions marker
                out.write("0.;");
                out.flush();
                response.flushBuffer();
            }

            // Send end-of-stream marker and close tunnel if connection is closed
            catch (GuacamoleConnectionClosedException e) {

                // Deregister and close
                deregisterTunnel(tunnelSessionToken);
                tunnel.close();

                // End-of-instructions marker
                out.write("0.;");
                out.flush();
                response.flushBuffer();

            }

            catch (GuacamoleException e) {

                // Deregister and close
                deregisterTunnel(tunnelSessionToken);
                tunnel.close();

                throw e;
            }

            // Always close output stream
            finally {
                out.close();
            }

        }
        catch (IOException e) {

            // Log typically frequent I/O error if desired
            logger.debug("Error writing to servlet output stream", e);

            // Deregister and close
            deregisterTunnel(tunnelSessionToken);
            tunnel.close();

        }
        finally {
            tunnel.releaseReader();
        }

    }
Called whenever the JavaScript Guacamole client makes a read request.
This function should in general not be overridden, as it already
contains a proper implementation of the read operation.

@param request
    The HttpServletRequest associated with the read request received.

@param response
    The HttpServletResponse associated with the write request received.
    Any data to be sent to the client in response to the write request
    should be written to the response body of this HttpServletResponse.

@param tunnelSessionToken
    The tunnel-specific session token of the HTTP tunnel to read from,
    as specified in the read request. This tunnel must have been created
    by a previous call to doConnect().

@throws GuacamoleException
    If an error occurs while handling the read request.
guacamole-common/src/main/java/org/apache/guacamole/servlet/GuacamoleHTTPTunnelServlet.java