diff --git a/htroot/ViewImage.java b/htroot/ViewImage.java index d6838dc8b..08d0dd341 100644 --- a/htroot/ViewImage.java +++ b/htroot/ViewImage.java @@ -121,26 +121,30 @@ public class ViewImage { encodedImage = new EncodedImage(image, ext, post.getBoolean("isStatic")); } else { - String urlExt = MultiProtocolURL.getFileExtension(url.getFileName()); - if (ext != null && ext.equalsIgnoreCase(urlExt) && isBrowserRendered(urlExt)) { - return openInputStream(post, sb.loader, auth, url); - } - ImageInputStream imageInStream = null; InputStream inStream = null; - /* - * When opening a file, the most efficient is to open - * ImageInputStream directly on file - */ - if (url.isFile()) { - imageInStream = ImageIO.createImageInputStream(url.getFSFile()); - } else { - inStream = openInputStream(post, sb.loader, auth, url); - imageInStream = ImageIO.createImageInputStream(inStream); - } try { + String urlExt = MultiProtocolURL.getFileExtension(url.getFileName()); + if (ext != null && ext.equalsIgnoreCase(urlExt) && isBrowserRendered(urlExt)) { + return openInputStream(post, sb.loader, auth, url); + } + /* + * When opening a file, the most efficient is to open + * ImageInputStream directly on file + */ + if (url.isFile()) { + imageInStream = ImageIO.createImageInputStream(url.getFSFile()); + } else { + inStream = openInputStream(post, sb.loader, auth, url); + imageInStream = ImageIO.createImageInputStream(inStream); + } // read image encodedImage = parseAndScale(post, auth, urlString, ext, imageInStream); + } catch(Exception e) { + /* Exceptions are not propagated here : many error causes are possible, network errors, + * incorrect or unsupported format, bad ImageIO plugin... + * Instead return an empty EncodedImage. Caller is responsible for handling this correctly */ + encodedImage = new EncodedImage(new byte[0], ext, true); } finally { /* * imageInStream.close() method doesn't close source input @@ -172,7 +176,7 @@ public class ViewImage { * image url. * @return an open input stream instance (don't forget to close it). * @throws IOException - * when a read/write error occured. + * when a read/write error occured. */ private static InputStream openInputStream(final serverObjects post, final LoaderDispatcher loader, final boolean auth, DigestURL url) throws IOException { @@ -185,7 +189,8 @@ public class ViewImage { inStream = loader.openInputStream(loader.request(url, false, true), CacheStrategy.IFEXIST, BlacklistType.SEARCH, agent); } catch (final IOException e) { - ConcurrentLog.fine("ViewImage", "cannot load: " + e.getMessage()); + /** No need to log full stack trace (in most cases resource is not available because of a network error) */ + ConcurrentLog.fine("ViewImage", "cannot load image. URL : " + url); throw e; } } @@ -230,11 +235,11 @@ public class ViewImage { * open stream on image content. Must not be null. * @return an EncodedImage instance. * @throws IOException - * when image could not be parsed or encoded to specified format + * when image could not be parsed or encoded to specified format. */ protected static EncodedImage parseAndScale(serverObjects post, boolean auth, String urlString, String ext, ImageInputStream imageInStream) throws IOException { - EncodedImage encodedImage = null; + EncodedImage encodedImage; // BufferedImage image = ImageIO.read(imageInStream); Iterator readers = ImageIO.getImageReaders(imageInStream); @@ -244,11 +249,13 @@ public class ViewImage { imageInStream.close(); } catch (IOException ignoredException) { } + String errorMessage = "Image format (" + ext + ") is not supported."; + ConcurrentLog.fine("ViewImage", errorMessage + "Image URL : " + urlString); /* * Throw an exception, wich will end in a HTTP 500 response, better * handled by browsers than an empty image */ - throw new IOException("Image format is not supported."); + throw new IOException(errorMessage); } ImageReader reader = readers.next(); reader.setInput(imageInStream, true, true); @@ -319,11 +326,13 @@ public class ViewImage { } else { /* * An error can still occur when transcoding from buffered image to - * target ext : in that case return null + * target ext : in that case EncodedImage.getImage() is empty. */ encodedImage = new EncodedImage(image, ext, isStatic); if (encodedImage.getImage().length() == 0) { - throw new IOException("Image could not be encoded to format : " + ext); + String errorMessage = "Image could not be encoded to format : " + ext; + ConcurrentLog.fine("ViewImage", errorMessage + ". Image URL : " + urlString); + throw new IOException(errorMessage); } } diff --git a/source/net/yacy/http/servlets/YaCyDefaultServlet.java b/source/net/yacy/http/servlets/YaCyDefaultServlet.java index 5ad95e9d0..1606b6650 100644 --- a/source/net/yacy/http/servlets/YaCyDefaultServlet.java +++ b/source/net/yacy/http/servlets/YaCyDefaultServlet.java @@ -851,6 +851,13 @@ public class YaCyDefaultServlet extends HttpServlet { } else if (tmp instanceof EncodedImage) { final EncodedImage yp = (EncodedImage) tmp; result = yp.getImage(); + /** When encodedImage is empty, return a code 500 rather than only an empty response + * as it is better handled across different browsers */ + if(result == null || result.length() == 0) { + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + result.close(); + return; + } if (yp.isStatic()) { response.setDateHeader(HeaderFramework.EXPIRES, now + 600000); // expires in ten minutes } @@ -1004,6 +1011,11 @@ public class YaCyDefaultServlet extends HttpServlet { size += l; } response.setContentLength(size); + } catch(IOException e){ + /** No need to log full stack trace (in most cases resource is not available because of a network error) */ + ConcurrentLog.fine("FILEHANDLER", "YaCyDefaultServlet: resource content stream could not be written to response."); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + return; } finally { try { inStream.close();