1

This is one of the first times where I felt I needed to ask a question as I can't seem to find an answer anywhere.

So, in this app I made an easy website page that uses html5 video for an background for my friend that's an photographer. To allow my friend to make future changes, I wanted to use the appengine blobstore to serve videos uploaded for the background. Everything seems to be fine except for in Safari, the video is never received or is received incorrectly.

Here's my servlet class that does the serving:

public class Serve extends HttpServlet {
private static final long serialVersionUID = 1L;

private BlobstoreService blobstore = BlobstoreServiceFactory.getBlobstoreService();
private DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

public void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {
      String format = req.getParameter("video");
      Key key = KeyFactory.createKey("Video", "None");
      if(format.equals("mp4")){
          key = KeyFactory.createKey("Video", "MP4");
          resp.setHeader("Content-type", "video/mp4");
      }
      if(format.equals("webm")){
          key = KeyFactory.createKey("Video", "WebM");
          resp.setHeader("Content-type", "video/webm");
      }
      try{
          Entity e = datastore.get(key);
          String blobKey = (String)e.getProperty("Blob-Key");
          BlobKey bKey = new BlobKey(blobKey);
          resp.setHeader("Cache-Control", "max-age=31557600, must-revalidate");
          //resp.setHeader("Transfer-Encoding", "chunked");
          blobstore.serve(bKey, resp);
      }
      catch (EntityNotFoundException error){
          resp.getWriter().print("No video found");
      }
}
}

Edit: Removed Transfer-Encoding header.

Now on the development server I get this Exception when I try to access /serve?video=mp4 only in Safari:

[java] WARNING: IOException thrown while closing Closeable.
 [java] org.mortbay.jetty.EofException
 [java]     at org.mortbay.jetty.HttpGenerator.flush(HttpGenerator.java:787)
 [java]     at org.mortbay.jetty.HttpConnection.flushResponse(HttpConnection.java:693)
 [java]     at org.mortbay.jetty.HttpConnection$Output.close(HttpConnection.java:992)
 [java]     at com.google.appengine.repackaged.com.google.common.io.Closeables.close(Closeables.java:77)
 [java]     at com.google.appengine.api.blobstore.dev.ServeBlobFilter.serveBlob(ServeBlobFilter.java:195)
 [java]     at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:67)
 [java]     at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 [java]     at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
 [java]     at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 [java]     at com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:125)
 [java]     at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 [java]     at com.google.appengine.tools.development.DevAppServerModulesFilter.doDirectRequest(DevAppServerModulesFilter.java:368)
 [java]     at com.google.appengine.tools.development.DevAppServerModulesFilter.doDirectModuleRequest(DevAppServerModulesFilter.java:351)
 [java]     at com.google.appengine.tools.development.DevAppServerModulesFilter.doFilter(DevAppServerModulesFilter.java:116)
 [java]     at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 [java]     at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
 [java]     at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
 [java]     at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
 [java]     at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
 [java]     at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
 [java]     at com.google.appengine.tools.development.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:97)
 [java]     at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
 [java]     at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:485)
 [java]     at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
 [java]     at org.mortbay.jetty.Server.handle(Server.java:326)
 [java]     at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
 [java]     at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
 [java]     at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:547)
 [java]     at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
 [java]     at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
 [java]     at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
 [java]     at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
 [java] Caused by: java.io.IOException: Broken pipe
 [java]     at sun.nio.ch.FileDispatcherImpl.write0(Native Method)
 [java]     at sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:47)
 [java]     at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:89)
 [java]     at sun.nio.ch.IOUtil.write(IOUtil.java:46)
 [java]     at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:450)
 [java]     at org.mortbay.io.nio.ChannelEndPoint.flush(ChannelEndPoint.java:169)
 [java]     at org.mortbay.io.nio.SelectChannelEndPoint.flush(SelectChannelEndPoint.java:221)
 [java]     at org.mortbay.jetty.HttpGenerator.flush(HttpGenerator.java:721)
 [java]     ... 31 more
 [java] 
 [java] Sep 20, 2013 2:48:25 PM com.google.appengine.repackaged.com.google.common.io.Closeables close
 [java] WARNING: IOException thrown while closing Closeable.
 [java] org.mortbay.jetty.EofException
 [java]     at org.mortbay.jetty.HttpGenerator.flush(HttpGenerator.java:787)
 [java]     at org.mortbay.jetty.HttpConnection.flushResponse(HttpConnection.java:693)
 [java]     at org.mortbay.jetty.HttpConnection$Output.close(HttpConnection.java:992)
 [java]     at com.google.appengine.repackaged.com.google.common.io.Closeables.close(Closeables.java:77)
 [java]     at com.google.appengine.api.blobstore.dev.ServeBlobFilter.serveBlob(ServeBlobFilter.java:195)
 [java]     at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:67)
 [java]     at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 [java]     at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
 [java]     at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 [java]     at com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:125)
 [java]     at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 [java]     at com.google.appengine.tools.development.DevAppServerModulesFilter.doDirectRequest(DevAppServerModulesFilter.java:368)
 [java]     at com.google.appengine.tools.development.DevAppServerModulesFilter.doDirectModuleRequest(DevAppServerModulesFilter.java:351)
 [java]     at com.google.appengine.tools.development.DevAppServerModulesFilter.doFilter(DevAppServerModulesFilter.java:116)
 [java]     at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 [java]     at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
 [java]     at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
 [java]     at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
 [java]     at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
 [java]     at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
 [java]     at com.google.appengine.tools.development.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:97)
 [java]     at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
 [java]     at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:485)
 [java]     at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
 [java]     at org.mortbay.jetty.Server.handle(Server.java:326)
 [java]     at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
 [java]     at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
 [java]     at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:547)
 [java]     at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
 [java]     at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
 [java]     at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
 [java]     at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
 [java] Caused by: java.io.IOException: Broken pipe
 [java]     at sun.nio.ch.FileDispatcherImpl.write0(Native Method)
 [java]     at sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:47)
 [java]     at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:89)
 [java]     at sun.nio.ch.IOUtil.write(IOUtil.java:46)
 [java]     at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:450)
 [java]     at org.mortbay.io.nio.ChannelEndPoint.flush(ChannelEndPoint.java:169)
 [java]     at org.mortbay.io.nio.SelectChannelEndPoint.flush(SelectChannelEndPoint.java:221)
 [java]     at org.mortbay.jetty.HttpGenerator.flush(HttpGenerator.java:721)
 [java]     ... 31 more

The video will still show on this page, however, in production nothing occurs. Also, I'm not getting any logs in the appengine console so I don't if there are other errors. I know that the video was uploaded properly as it works in google chrome. Google Chrome will send back the response headers and play the video. I've also tried using curl and it sends everything back also. Is there something I'm missing or something I need to take out for it to work properly in Safari.

Edit: I'm also wondering does the response header ever be received in Safari as it does in Firefox, Chrome, and Opera for blobstore requests?

Thanks for any advice given.

Tyrice Clark
  • 83
  • 1
  • 6
  • 1
    Try removing `Transfer-Encoding: chunked` header. – Peter Knego Sep 21 '13 at 07:26
  • @peter It actually randomly worked before I removed the header. Still, the localserver throws the same exception with the header removed or not, so I'm not sure if the problem is just local or it just takes more than a couple of hours for it to be served. Now I wonder whether Safari is ever able to get a response header sent back. – Tyrice Clark Sep 21 '13 at 10:40
  • Do you have a public url of a mp4 video we could try? – Peter Knego Sep 21 '13 at 10:56
  • 1
    Also, it's possible that safari does not recognise the video encoding and drops the connection - hence the error. See this answer for encoding settings: http://stackoverflow.com/a/13402284/248432 – Peter Knego Sep 21 '13 at 10:57
  • @peter Here's a url, www.jonnejohnson.com/serve?video=mp4 – Tyrice Clark Sep 21 '13 at 18:35
  • @peter I will try changing the video encoding now and see if that fixes it, however that solution is for iPads, iphones, etc., which I already configured to dismiss the video for. I programmed this site to show a background image on devices that are not desktops. – Tyrice Clark Sep 21 '13 at 18:39

0 Answers0