/** * Send given content string as the HTTP response. * * @param contents the string to return as the HTTP response. * @param res the HttpServletResponse * @throws IOException if an I/O error occurs while writing the response. */ public static void returnString(String contents, HttpServletResponse res) throws IOException { try { ServletOutputStream out = res.getOutputStream(); IO.copy(new ByteArrayInputStream(contents.getBytes()), out); log.info( UsageLog.closingMessageForRequestContext(HttpServletResponse.SC_OK, contents.length())); } catch (IOException e) { log.error(" IOException sending string: ", e); log.info(UsageLog.closingMessageForRequestContext(HttpServletResponse.SC_NOT_FOUND, 0)); res.sendError(HttpServletResponse.SC_NOT_FOUND, "Problem sending string: " + e.getMessage()); } }
/** * Write a file to the response stream. Handles Range requests. * * @param servlet called from here * @param req the request * @param res the response * @param file to serve * @param contentType content type, if null, will try to guess * @throws IOException on write error */ public static void returnFile( HttpServlet servlet, HttpServletRequest req, HttpServletResponse res, File file, String contentType) throws IOException { // No file, nothing to view if (file == null) { log.info( "returnFile(): " + UsageLog.closingMessageForRequestContext(HttpServletResponse.SC_NOT_FOUND, 0)); res.sendError(HttpServletResponse.SC_NOT_FOUND); return; } // check that it exists if (!file.exists()) { log.info( "returnFile(): " + UsageLog.closingMessageForRequestContext(HttpServletResponse.SC_NOT_FOUND, 0)); res.sendError(HttpServletResponse.SC_NOT_FOUND); return; } // not a directory if (!file.isFile()) { log.info( "returnFile(): " + UsageLog.closingMessageForRequestContext(HttpServletResponse.SC_BAD_REQUEST, 0)); res.sendError(HttpServletResponse.SC_BAD_REQUEST); return; } // Set the type of the file String filename = file.getPath(); if (null == contentType) { if (filename.endsWith(".html")) contentType = "text/html; charset=iso-8859-1"; else if (filename.endsWith(".xml")) contentType = "text/xml; charset=iso-8859-1"; else if (filename.endsWith(".txt") || (filename.endsWith(".log"))) contentType = CONTENT_TEXT; else if (filename.indexOf(".log.") > 0) contentType = CONTENT_TEXT; else if (filename.endsWith(".nc")) contentType = "application/x-netcdf"; else contentType = servlet.getServletContext().getMimeType(filename); if (contentType == null) contentType = "application/octet-stream"; } returnFile(req, res, file, contentType); }
/** * Write a file to the response stream. * * @param servlet called from here * @param contentPath file root path * @param path file path reletive to the root * @param req the request * @param res the response * @param contentType content type, or null * @throws IOException on write error */ public static void returnFile( HttpServlet servlet, String contentPath, String path, HttpServletRequest req, HttpServletResponse res, String contentType) throws IOException { String filename = ServletUtil.formFilename(contentPath, path); log.debug("returnFile(): returning file <" + filename + ">."); // No file, nothing to view if (filename == null) { log.info( "returnFile(): " + UsageLog.closingMessageForRequestContext(HttpServletResponse.SC_NOT_FOUND, 0)); res.sendError(HttpServletResponse.SC_NOT_FOUND); return; } // dontallow .. if (filename.indexOf("..") != -1) { log.info( "returnFile(): " + UsageLog.closingMessageForRequestContext(HttpServletResponse.SC_FORBIDDEN, 0)); res.sendError(HttpServletResponse.SC_FORBIDDEN); return; } // dont allow access to WEB-INF or META-INF String upper = filename.toUpperCase(); if (upper.indexOf("WEB-INF") != -1 || upper.indexOf("META-INF") != -1) { log.info( "returnFile(): " + UsageLog.closingMessageForRequestContext(HttpServletResponse.SC_FORBIDDEN, 0)); res.sendError(HttpServletResponse.SC_FORBIDDEN); return; } returnFile(servlet, req, res, new File(filename), contentType); }
public static boolean saveFile( HttpServlet servlet, String contentPath, String path, HttpServletRequest req, HttpServletResponse res) { // @todo Need to use logServerAccess() below here. boolean debugRequest = Debug.isSet("SaveFile"); if (debugRequest) log.debug(" saveFile(): path= " + path); String filename = contentPath + path; // absolute path File want = new File(filename); // backup current version if it exists int version = getBackupVersion(want.getParent(), want.getName()); String fileSave = filename + "~" + version; File file = new File(filename); if (file.exists()) { try { IO.copyFile(filename, fileSave); } catch (IOException e) { log.error( "saveFile(): Unable to save copy of file " + filename + " to " + fileSave + "\n" + e.getMessage()); return false; } } // save new file try { OutputStream out = new BufferedOutputStream(new FileOutputStream(filename)); IO.copy(req.getInputStream(), out); out.close(); if (debugRequest) log.debug("saveFile(): ok= " + filename); res.setStatus(HttpServletResponse.SC_CREATED); log.info(UsageLog.closingMessageForRequestContext(HttpServletResponse.SC_CREATED, -1)); return true; } catch (IOException e) { log.error( "saveFile(): Unable to PUT file " + filename + " to " + fileSave + "\n" + e.getMessage()); return false; } }
/** * ************************************************************************ Sends an error to the * client. * * @param t The exception that caused the problem. * @param res The <code>HttpServletResponse</code> for the client. */ public static void handleException(Throwable t, HttpServletResponse res) { try { String message = t.getMessage(); if (message == null) message = "NULL message " + t.getClass().getName(); if (Debug.isSet("trustedMode")) { // security issue: only show stack if trusted ByteArrayOutputStream bs = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(bs); t.printStackTrace(ps); message = new String(bs.toByteArray()); } log.info( UsageLog.closingMessageForRequestContext( HttpServletResponse.SC_BAD_REQUEST, message.length())); log.error("handleException", t); t.printStackTrace(); // debugging - log.error not showing stack trace !! if (!res.isCommitted()) res.sendError(HttpServletResponse.SC_BAD_REQUEST, message); } catch (Throwable e) { log.error("handleException() had problem reporting Exception", e); t.printStackTrace(); } }
/** * Write a file to the response stream. Handles Range requests. * * @param req request * @param res response * @param file must exists and not be a directory * @param contentType must not be null * @throws IOException or error */ public static void returnFile( HttpServletRequest req, HttpServletResponse res, File file, String contentType) throws IOException { res.setContentType(contentType); // see if its a Range Request boolean isRangeRequest = false; long startPos = 0, endPos = Long.MAX_VALUE; String rangeRequest = req.getHeader("Range"); if (rangeRequest != null) { // bytes=12-34 or bytes=12- int pos = rangeRequest.indexOf("="); if (pos > 0) { int pos2 = rangeRequest.indexOf("-"); if (pos2 > 0) { String startString = rangeRequest.substring(pos + 1, pos2); String endString = rangeRequest.substring(pos2 + 1); startPos = Long.parseLong(startString); if (endString.length() > 0) endPos = Long.parseLong(endString) + 1; isRangeRequest = true; } } } // set content length long fileSize = file.length(); long contentLength = fileSize; if (isRangeRequest) { endPos = Math.min(endPos, fileSize); contentLength = endPos - startPos; } if (contentLength > Integer.MAX_VALUE) res.addHeader( "Content-Length", Long.toString(contentLength)); // allow content length > MAX_INT else res.setContentLength((int) contentLength); // note HEAD only allows this String filename = file.getPath(); boolean debugRequest = Debug.isSet("returnFile"); if (debugRequest) log.debug( "returnFile(): filename = " + filename + " contentType = " + contentType + " contentLength = " + contentLength); // indicate we allow Range Requests res.addHeader("Accept-Ranges", "bytes"); if (req.getMethod().equals("HEAD")) { log.info( "returnFile(): " + UsageLog.closingMessageForRequestContext(HttpServletResponse.SC_OK, 0)); return; } try { if (isRangeRequest) { // set before content is sent res.addHeader("Content-Range", "bytes " + startPos + "-" + (endPos - 1) + "/" + fileSize); res.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); FileCacheRaf.Raf craf = null; try { craf = fileCacheRaf.acquire(filename); IO.copyRafB( craf.getRaf(), startPos, contentLength, res.getOutputStream(), new byte[60000]); log.info( "returnFile(): " + UsageLog.closingMessageForRequestContext( HttpServletResponse.SC_PARTIAL_CONTENT, contentLength)); return; } finally { if (craf != null) fileCacheRaf.release(craf); } } // Return the file ServletOutputStream out = res.getOutputStream(); IO.copyFileB(file, out, 60000); res.flushBuffer(); out.close(); if (debugRequest) log.debug("returnFile(): returnFile ok = " + filename); log.info( "returnFile(): " + UsageLog.closingMessageForRequestContext(HttpServletResponse.SC_OK, contentLength)); } // @todo Split up this exception handling: those from file access vs those from dealing with // response // File access: catch and res.sendError() // response: don't catch (let bubble up out of doGet() etc) catch (FileNotFoundException e) { log.error("returnFile(): FileNotFoundException= " + filename); log.info( "returnFile(): " + UsageLog.closingMessageForRequestContext(HttpServletResponse.SC_NOT_FOUND, 0)); if (!res.isCommitted()) res.sendError(HttpServletResponse.SC_NOT_FOUND); } catch (java.net.SocketException e) { log.info("returnFile(): SocketException sending file: " + filename + " " + e.getMessage()); log.info("returnFile(): " + UsageLog.closingMessageForRequestContext(STATUS_CLIENT_ABORT, 0)); } catch (IOException e) { String eName = e.getClass().getName(); // dont want compile time dependency on ClientAbortException if (eName.equals("org.apache.catalina.connector.ClientAbortException")) { log.info( "returnFile(): ClientAbortException while sending file: " + filename + " " + e.getMessage()); log.info( "returnFile(): " + UsageLog.closingMessageForRequestContext(STATUS_CLIENT_ABORT, 0)); return; } log.error("returnFile(): IOException (" + e.getClass().getName() + ") sending file ", e); log.error( "returnFile(): " + UsageLog.closingMessageForRequestContext(HttpServletResponse.SC_NOT_FOUND, 0)); if (!res.isCommitted()) res.sendError(HttpServletResponse.SC_NOT_FOUND, "Problem sending file: " + e.getMessage()); } }
/** * Send a permanent redirect (HTTP status 301 "Moved Permanently") response with the given target * path. * * <p>The given target path may be relative or absolute. If it is relative, it will be resolved * against the request URL. * * @param targetPath the path to which the client is redirected. * @param req the HttpServletRequest * @param res the HttpServletResponse * @throws IOException if can't write the response. */ public static void sendPermanentRedirect( String targetPath, HttpServletRequest req, HttpServletResponse res) throws IOException { // Absolute URL needed so resolve the target path against the request URL. URI uri; try { uri = new URI(req.getRequestURL().toString()); } catch (URISyntaxException e) { log.error( "sendPermanentRedirect(): Bad syntax on request URL <" + req.getRequestURL() + ">.", e); log.info( "sendPermanentRedirect(): " + UsageLog.closingMessageForRequestContext( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 0)); if (!res.isCommitted()) res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; } String absolutePath = uri.resolve(targetPath).toString(); absolutePath = res.encodeRedirectURL(absolutePath); res.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); res.addHeader("Location", absolutePath); String title = "Permanently Moved - 301"; String body = new StringBuilder() .append("<p>") .append("The requested URL <") .append(req.getRequestURL()) .append("> has been permanently moved (HTTP status code 301).") .append(" Instead, please use the following URL: <a href=\"") .append(absolutePath) .append("\">") .append(absolutePath) .append("</a>.") .append("</p>") .toString(); String htmlResp = new StringBuilder() .append(HtmlWriter.getInstance().getHtmlDoctypeAndOpenTag()) .append("<head><title>") .append(title) .append("</title></head><body>") .append("<h1>") .append(title) .append("</h1>") .append(body) .append("</body></html>") .toString(); log.info("sendPermanentRedirect(): redirect to " + absolutePath); log.info( "sendPermanentRedirect(): " + UsageLog.closingMessageForRequestContext( HttpServletResponse.SC_MOVED_PERMANENTLY, htmlResp.length())); // Write the catalog out. PrintWriter out = res.getWriter(); res.setContentType("text/html"); out.print(htmlResp); out.flush(); }
/** * Convenience routine used by handleRequestForContentFile() and handleRequestForRootFile(). * * @param pathPrefix * @param path * @param servlet * @param req request * @param res response * @throws IOException on IO error */ private static void handleRequestForContentOrRootFile( String pathPrefix, String path, HttpServlet servlet, HttpServletRequest req, HttpServletResponse res) throws IOException { if (!pathPrefix.equals("/content/") && !pathPrefix.equals("/root/")) { log.error( "handleRequestForContentFile(): The path prefix <" + pathPrefix + "> must be \"/content/\" or \"/root/\"."); throw new IllegalArgumentException("Path prefix must be \"/content/\" or \"/root/\"."); } if (!path.startsWith(pathPrefix)) { log.error( "handleRequestForContentFile(): path <" + path + "> must start with \"" + pathPrefix + "\"."); throw new IllegalArgumentException("Path must start with \"" + pathPrefix + "\"."); } // Don't allow ".." directories in path. if (path.indexOf("/../") != -1 || path.equals("..") || path.startsWith("../") || path.endsWith("/..")) { res.sendError(HttpServletResponse.SC_FORBIDDEN, "Path cannot contain \"..\" directory."); log.info(UsageLog.closingMessageForRequestContext(HttpServletResponse.SC_FORBIDDEN, -1)); return; } // Find the requested file. File file = new File( ServletUtil.formFilename(getContentPath(), path.substring(pathPrefix.length() - 1))); if (file.exists()) { // Do not allow request for a directory. if (file.isDirectory()) { if (!path.endsWith("/")) { String redirectPath = req.getRequestURL().append("/").toString(); ServletUtil.sendPermanentRedirect(redirectPath, req, res); return; } int i = HtmlWriter.getInstance().writeDirectory(res, file, path); int status = i == 0 ? HttpServletResponse.SC_NOT_FOUND : HttpServletResponse.SC_OK; log.info(UsageLog.closingMessageForRequestContext(status, i)); return; } // Return the requested file. ServletUtil.returnFile(servlet, req, res, file, null); } else { // Requested file not found. log.info(UsageLog.closingMessageForRequestContext(HttpServletResponse.SC_NOT_FOUND, -1)); res.sendError(HttpServletResponse.SC_NOT_FOUND); // 404 } }
/** * Handle a request for a raw/static file (i.e., not a catalog or dataset request). * * <p>Look in the content (user) directory then the root (distribution) directory for a file that * matches the given path and, if found, return it as the content of the HttpServletResponse. If * the file is forbidden (i.e., the path contains a "..", "WEB-INF", or "META-INF" directory), * send a HttpServletResponse.SC_FORBIDDEN response. If no file matches the request (including an * "index.html" file if the path ends in "/"), send an HttpServletResponse.SC_NOT_FOUND.. * * <p> * * <ol> * <li>Make sure the path does not contain ".." directories. * <li>Make sure the path does not contain "WEB-INF" or "META-INF". * <li>Check for requested file in the content directory (if the path is a directory, make sure * the path ends with "/" and check for an "index.html" file). * <li>Check for requested file in the root directory (if the path is a directory, make sure the * path ends with "/" and check for an "index.html" file). </ol * * @param path the requested path * @param servlet the servlet handling the request * @param req the HttpServletRequest * @param res the HttpServletResponse * @throws IOException if can't complete request due to IO problems. */ public static void handleRequestForRawFile( String path, HttpServlet servlet, HttpServletRequest req, HttpServletResponse res) throws IOException { // Don't allow ".." directories in path. if (path.indexOf("/../") != -1 || path.equals("..") || path.startsWith("../") || path.endsWith("/..")) { res.sendError(HttpServletResponse.SC_FORBIDDEN, "Path cannot contain \"..\" directory."); log.info(UsageLog.closingMessageForRequestContext(HttpServletResponse.SC_FORBIDDEN, -1)); return; } // Don't allow access to WEB-INF or META-INF directories. String upper = path.toUpperCase(); if (upper.indexOf("WEB-INF") != -1 || upper.indexOf("META-INF") != -1) { res.sendError( HttpServletResponse.SC_FORBIDDEN, "Path cannot contain \"WEB-INF\" or \"META-INF\"."); log.info(UsageLog.closingMessageForRequestContext(HttpServletResponse.SC_FORBIDDEN, -1)); return; } // Find a regular file File regFile = null; // Look in content directory for regular file. File cFile = new File(ServletUtil.formFilename(getContentPath(), path)); if (cFile.exists()) { if (cFile.isDirectory()) { if (!path.endsWith("/")) { String newPath = req.getRequestURL().append("/").toString(); ServletUtil.sendPermanentRedirect(newPath, req, res); } // If request path is a directory, check for index.html file. cFile = new File(cFile, "index.html"); if (cFile.exists() && !cFile.isDirectory()) regFile = cFile; } // If not a directory, use this file. else regFile = cFile; } if (regFile == null) { // Look in root directory. File rFile = new File(ServletUtil.formFilename(getRootPath(), path)); if (rFile.exists()) { if (rFile.isDirectory()) { if (!path.endsWith("/")) { String newPath = req.getRequestURL().append("/").toString(); ServletUtil.sendPermanentRedirect(newPath, req, res); } rFile = new File(rFile, "index.html"); if (rFile.exists() && !rFile.isDirectory()) regFile = rFile; } else regFile = rFile; } } if (regFile == null) { res.sendError(HttpServletResponse.SC_NOT_FOUND); // 404 log.info(UsageLog.closingMessageForRequestContext(HttpServletResponse.SC_NOT_FOUND, -1)); return; } ServletUtil.returnFile(servlet, req, res, regFile, null); }