0

I'm currently trying to load a video from a database and stream it via JSP to a browser. Everything is working fine on most browsers except when using Safari, which only shows an empty player that doesn't show any video.

The problem is in the outputStream socket,

while ((bytesRead = inStream.read(buffer)) != -1) {
    outStream.write(buffer, 0, bytesRead);
}

Safari restarts the video stream a couple of times and i get a socket exception.

org.apache.catalina.connector.ClientAbortException: java.net.SocketException: Connection reset by peer: socket write error

Safari keeps requesting a specific byte-range in the http-header:

"bytes=0-1"

code:

<%@ page import="java.io.*"%>
<%@ page import="java.net.*"%>
<%@ page import="com.mysql.jdbc.*"%>
<%@ page import="java.sql.*"%>
<%@page language="java" trimDirectiveWhitespaces="true"%>

<%@include file="settings.jsp"%>
<%
    session = request.getSession(false);

    if (session != null) {
        int userId = 0;
        try {
            userId = (new Integer(session.getAttribute("user_id").toString())).intValue();
        }
        catch (Exception ex) {
            // nothing
        }

        int videoId = 0;
        try {
            if (request.getParameter("video_id") != null) {
                videoId = (new Integer(request.getParameter("video_id"))).intValue();
            }
        }
        catch (Exception ex) {
            // nothing
        }

        int videoType = 0;
        try {
            if (request.getParameter("video_type") != null) {
                videoType = (new Integer(request.getParameter("video_type"))).intValue();
            }
        }
        catch (Exception ex) {
            // nothing      
        }

        if (userId > 0 && videoId > 0 && videoType > 0) {
            java.sql.Connection connection = null;
            java.sql.PreparedStatement queryStatement = null;
            StringBuffer sql = null;
            try {

                String connectionURL = "jdbc:mysql://" + DB_HOST_NAME + ":" + DB_PORT + "/" + DB_NAME;

                Class.forName("com.mysql.jdbc.Driver").newInstance();
                connection = DriverManager.getConnection(connectionURL, DB_USER_NAME, DB_USER_PASSWORD);
                if (!connection.isClosed()) {
                    sql = new StringBuffer();

                    sql.append("SELECT ");
                    sql.append("video.id AS id, ");
                    sql.append("video.title AS title, ");
                    sql.append("video.video_url_hd AS video_url_hd, ");
                    sql.append("video.video_url_sd AS video_url_sd, ");
                    sql.append("video.video_img_url AS video_img_url, ");
                    sql.append("video.description AS description ");
                    sql.append("FROM " + DB_NAME + ".video ");
                    sql.append("INNER JOIN " + DB_NAME + ".video_visibility ON video_visibility.video_id = video.id ");
                    sql.append("INNER JOIN " + DB_NAME + ".users ON users.id = ? ");
                    sql.append("WHERE video_visibility.right_gr_id IN (users.right_gr_id, 0) AND video.id = ? ");
                    sql.append("ORDER BY video.sequence ");

                    queryStatement = connection.prepareStatement(sql.toString());

                    queryStatement.setInt(1, userId);
                    queryStatement.setInt(2, videoId);

                    ResultSet rs = queryStatement.executeQuery();
                    if (rs != null) {
                        if (rs.next()) {
                            String title = rs.getString("title");
                            String imgURL = rs.getString("video_img_url");
                            String videoSDURL = rs.getString("video_url_sd");
                            String videoHDURL = rs.getString("video_url_hd");
                            rs.close();

                            /*
                            * Add logging
                            */
                            String logSql = "INSERT INTO " + DB_LOG_NAME + ".webclient_log (client_type, user_id, video_id, message, client_ip) " + "VALUES('"
                                    + CLIENT_TYPE + "', '" + userId + "', '" + videoId + "', 'Show Video:" + title + " / videoType=" + videoType + "', '"
                                    + request.getRemoteAddr() + "' )";

                            queryStatement.close();
                            queryStatement = connection.prepareStatement(logSql);
                            queryStatement.executeUpdate();

                            /*
                            * Stream File
                            */
                            String filePath = null;
                            if (videoType == 1) {
                                String path = "/video/" + videoSDURL;
                                filePath = getServletContext().getRealPath(path);

                            }
                            else if (videoType == 2) {
                                String path = "/video/" + videoHDURL;
                                filePath = getServletContext().getRealPath(path);
                            }

                            File downloadFile = new File(filePath);
                            FileInputStream inStream = new FileInputStream(downloadFile);

                            // if you want to use a relative path to context root:
                            String relativePath = getServletContext().getRealPath("");

                            // obtains ServletContext
                            ServletContext context = getServletContext();

                            // gets MIME type of the file
                            String mimeType = context.getMimeType(filePath);
                            if (mimeType == null) {
                                // set to binary type if MIME mapping not found
                                mimeType = "application/octet-stream";
                            }

                            // modifies response
                            response.setContentType(mimeType);

                            response.setContentLength((int) downloadFile.length());
                            //we support skipping
                            response.setHeader("Accept-Ranges", "bytes");

                            // obtains response's output stream
                            OutputStream outStream = response.getOutputStream();

                            byte[] buffer = new byte[4096];
                            int bytesRead = -1;

                            while ((bytesRead = inStream.read(buffer)) != -1) {
                                outStream.write(buffer, 0, bytesRead);
                            }
                            outStream.flush();

                            inStream.close();
                            outStream.close();
                        }
                    }
                }
            }
            catch (Exception ex) {
                System.out.println(ex.toString());
            }
            finally {
                if (queryStatement != null)
                    queryStatement.close();

                if (connection != null)
                    connection.close();

                sql = null;
            }
        }
    }
%>
MPSL
  • 59
  • 4
  • I'm going to ask the obvious question: have you tried using a servlet instead? If you're not restricted in any way to using JSPs, I wouldn't use them for outputting something other than text/html. There's also the chance that your container is old enough that ignores trimDirectiveWhitespaces - which means that the spaces in the JSP end up in your response and corrupt the vide. – Ana Vinatoru Jan 06 '16 at 16:23
  • currently im its not possible for me to change the output, which means i cant try servlets. ive also narrowed down the problem to the outStream.write which whenever it tries to write a chunk of bytes gives a socket write error, safari then reopens the socket 3 times and loops until it timeouts somehow the browser closes the socket before my programm can write anything chrome does the same thing btw but after 1 cycle of retries it works and it shows a video, firefox works without socket errors – MPSL Jan 07 '16 at 07:55
  • Is it possible that settings.jsp doesn't trim the whitespaces? – Ana Vinatoru Jan 07 '16 at 13:39
  • ive got <%@page language="java" trimDirectiveWhitespaces="true"%> in the header of the jsp – MPSL Jan 07 '16 at 14:39
  • also ive narrowed down the problem now, Safari always requests "bytes=0-1" in the range header and then shuts down the socket, i dont really know how to prevent this, is it possible to disallow range requests? – MPSL Jan 07 '16 at 14:39

0 Answers0