Modified: trunk/rails-integration/src/main/java/org/jruby/webapp/FileServlet.java (772 => 773)
--- trunk/rails-integration/src/main/java/org/jruby/webapp/FileServlet.java 2007-10-16 12:32:16 UTC (rev 772)
+++ trunk/rails-integration/src/main/java/org/jruby/webapp/FileServlet.java 2007-10-17 15:20:11 UTC (rev 773)
@@ -1,321 +1,322 @@
-package org.jruby.webapp;
-
-import org.jruby.webapp.util.FileUtil;
-import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.File;
-import java.io.IOException;
-import java.io.FileNotFoundException;
-import java.io.OutputStream;
-import java.io.FileInputStream;
-import java.util.Calendar;
-import java.util.Date;
-import java.text.SimpleDateFormat;
-import java.nio.channels.FileChannel;
-import java.nio.ByteBuffer;
-
-/**
- * This servlet returns a static file.
- *
- * @author Robert Egglestone
- */
-public class FileServlet extends HttpServlet {
-
- private static final String METHOD_DELETE = "DELETE";
- private static final String METHOD_HEAD = "HEAD";
- private static final String METHOD_GET = "GET";
- private static final String METHOD_OPTIONS = "OPTIONS";
- private static final String METHOD_POST = "POST";
- private static final String METHOD_PUT = "PUT";
- private static final String METHOD_TRACE = "TRACE";
-
- public static final String FALLBACK_SERVLET_PROPERTY = "files.default";
-
- private static final String[] DEFAULT_WELCOME_FILES = {"index.html", "index.htm"};
-
- public final static String CACHE_CONTROL_HEADER = "Cache-Control";
- public final static String EXPIRES_HEADER = "Expires";
- public final static String DATE_HEADER = "Date";
-
- private int bufferSize = 1024;
-
- private File root;
-
- private String prefix;
-
- private boolean setCacheHeaders;
- private int maxAge;
-
- /**
- * A servlet to pass control to if the file does not exist.
- */
- private RequestDispatcher defaultServletDispatcher;
-
- /**
- * Initialize the servlet, and determine the webapp root.
- */
- public void init() throws ServletException {
- prefix = findPrefix();
- root = findRoot();
-
- // determine the cache values
- setCacheHeaders = getServletConfig().getInitParameter("maxAge") != null;
- if (setCacheHeaders) {
- maxAge = Integer.parseInt(getServletConfig().getInitParameter("maxAge"));
- }
-
- // check for default fallback servlet
- ServletContext context = getServletContext();
- String defaultServletName = getServletConfig().getInitParameter("defaultServlet");
- if (defaultServletName == null) {
- defaultServletName = context.getInitParameter(FileServlet.FALLBACK_SERVLET_PROPERTY);
- }
- if (defaultServletName != null && defaultServletName.length() != 0) {
- defaultServletDispatcher = context.getNamedDispatcher(defaultServletName);
- }
- }
-
- /**
- * A prefix to prepend on the path when translating from URL to file location, typically "/public".
- */
- protected String findPrefix() {
- String prefix = getServletContext().getInitParameter("files.prefix");
- if (prefix == null) prefix = "/public";
- // prefix must start with a slash if it's specified
- if (prefix.length() > 0 && !prefix.startsWith("/")) {
- prefix = "/" + prefix;
- }
- return prefix;
- }
-
- /**
- * Root of the webapp, may be null in which case it is determined from the servlet api.
- * The root should be an absolute path that refers to a directory.
- */
- protected File findRoot() throws ServletException {
- String rootPath = getServletContext().getInitParameter("files.root");
- if (rootPath == null) {
- rootPath = FileUtil.getPath(getServletContext(), "/");
- }
-
- File root = new File(rootPath);
- if (!root.isDirectory()) {
- throw new ServletException("Webapp root does not point to a directory");
- }
-
- return root;
- }
-
- public String[] getWelcomeFiles() {
- String[] welcomeFiles;
-
- String welcomeFilesString = getServletContext().getInitParameter("files.welcome");
- if (welcomeFilesString != null) {
- welcomeFiles = parseCommaList(welcomeFilesString);
- } else {
- welcomeFiles = DEFAULT_WELCOME_FILES;
- }
-
- return welcomeFiles;
- }
-
- private String[] parseCommaList(String commaList) {
- String[] parts = commaList.split(",");
- for(int i=0; i<parts.length; i++) {
- parts[i] = parts[i].trim();
- }
- return parts;
- }
-
- /**
- * Look for a file matching the request.
- */
- protected File getFile(HttpServletRequest request) {
- // find the location of the file
- String contextPath = request.getContextPath();
- String relativePath = request.getRequestURI().substring(contextPath.length());
-
- // normalize the path
- relativePath = relativePath.replaceAll("\\\\", "/").replaceAll("//", "/");
-
- // determine the file path to check for
- String filePath;
- if (root == null) {
- filePath = prefix + relativePath;
- } else {
- filePath = root.getAbsolutePath() + prefix + relativePath;
- }
-
- return getFile(filePath);
- }
-
- /**
- * Look for a file matching the specified path.
- * This should also check default extensions, and for index files in the case of a directory.
- */
- protected File getFile(String filePath) {
- // try the exact match
- File fileLocation = getExactFile(filePath);
- if (fileLocation != null) return fileLocation;
-
- // try default extension
- fileLocation = getExactFile(filePath + ".html");
- if (fileLocation != null) return fileLocation;
-
- // try welcome files
- String[] welcomeFiles = getWelcomeFiles();
- for (int i = 0; i < welcomeFiles.length; i++) {
-
- fileLocation = getExactFile(filePath + "/" + welcomeFiles[i]);
- if (fileLocation != null) return fileLocation;
- }
-
- // no match was found
- return null;
- }
-
- /**
- * Look for a file with this exact path.
- */
- protected File getExactFile(String path) {
- // try to load the resource
- File filePath = new File(path);
- if (!filePath.isFile()) return null;
- return filePath;
- }
-
- private String formatDateForHeader(Date date) {
- String safari3OnlyAccessThisStyleDataFormat = "EEE, d MMM yyyy HH:mm:ss z";
- return new SimpleDateFormat(safari3OnlyAccessThisStyleDataFormat).format(date);
- }
-
- /**
- * Transfer the file.
- */
- protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- try {
- // check the file and open it
- File fileLocation = getFile(request);
- if (fileLocation != null) {
- // file was found, all good
- } else if (defaultServletDispatcher != null) {
- // forward request to the default servlet
- defaultServletDispatcher.forward(request, response);
- return;
- } else {
- // file not found
- log("File not found: " + request.getRequestURI());
- throw new FileNotFoundException(request.getRequestURI());
- }
-
- // check for modifications
- long ifModifiedSince = request.getDateHeader("If-Modified-Since");
- if (ifModifiedSince != -1) {
- long lastModified = fileLocation.lastModified();
- if (lastModified == 0) {
- // last modified date is not known
- } else if (lastModified < ifModifiedSince) {
- throw new NotModifiedException();
- } else {
- response.setDateHeader("Last-Modified", lastModified);
- }
- }
-
- // set cache headers
- if (setCacheHeaders) {
- response.setHeader(CACHE_CONTROL_HEADER, "max-age=" + maxAge);
- Calendar now = Calendar.getInstance();
- response.setHeader(DATE_HEADER, formatDateForHeader(now.getTime()));
- now.add(Calendar.SECOND, maxAge);
- response.setHeader(EXPIRES_HEADER, formatDateForHeader(now.getTime()));
- }
-
- // set the content type
- String contentType = guessContentTypeFromName(fileLocation.getName());
- response.setContentType(contentType);
-
- if (request.getMethod().equals(METHOD_HEAD)) {
- // head requests don't send the body
- } else if (request.getMethod().equals(METHOD_GET) || request.getMethod().equals(METHOD_POST)) {
- // transfer the content
- sendFile(fileLocation, response);
- } else {
- // anything else cannot be processed on the file
- // alternatively we could forward to rails, but this
- // approach is probably more consistent with other web servers
- response.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
- }
-
- } catch (NotModifiedException e) {
- response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
- } catch (FileNotFoundException e) {
- response.sendError(HttpServletResponse.SC_NOT_FOUND);
- } catch (IOException e) {
- response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
- }
- }
-
- /**
- * Send the file, faster, but requires the file is accessible on the file system.
- */
- private void sendFile(File file, HttpServletResponse response) throws IOException {
- // setup IO streams
- ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
- FileChannel in = null;
- try {
- in = new FileInputStream(file).getChannel();
-
- // start returning the response
- OutputStream out = response.getOutputStream();
-
- // read the bytes, returning them in the response
- while (in.read(buffer) != -1) {
- out.write(buffer.array(), 0, buffer.position());
- buffer.clear();
- }
- out.close();
- } finally {
- try {
- if (in != null) in.close();
- } catch (IOException ignore) {
- }
- }
- }
-
- /**
- * Return the content-type the would be returned for this file name.
- */
- public String guessContentTypeFromName(String fileName) {
- // quick hack for types that are necessary, but not handled
- String lowerName = fileName.toLowerCase();
- if (lowerName.endsWith(".css")) {
- return "text/css";
- } else if (lowerName.endsWith(".js")) {
- return "text/js";
- }
- try {
- // everything else
- javax.activation.FileTypeMap typeMap =
- javax.activation.FileTypeMap.getDefaultFileTypeMap();
- return typeMap.getContentType(fileName);
- } catch (Throwable t) {
- // allow activation.jar to be missing
- return "application/octet-stream";
- }
- }
-
- /**
- * An exception when the source object has not been modified. While this
- * condition is not a failure, it is a break from the normal flow of
- * execution.
- */
- private static class NotModifiedException extends IOException {
- public NotModifiedException() {
- }
- }
-
-}
+package org.jruby.webapp;
+
+import org.jruby.webapp.util.FileUtil;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.io.IOException;
+import java.io.FileNotFoundException;
+import java.io.OutputStream;
+import java.io.FileInputStream;
+import java.util.Calendar;
+import java.util.Date;
+import java.text.SimpleDateFormat;
+import java.nio.channels.FileChannel;
+import java.nio.ByteBuffer;
+
+/**
+ * This servlet returns a static file.
+ *
+ * @author Robert Egglestone
+ */
+public class FileServlet extends HttpServlet {
+
+ private static final String METHOD_DELETE = "DELETE";
+ private static final String METHOD_HEAD = "HEAD";
+ private static final String METHOD_GET = "GET";
+ private static final String METHOD_OPTIONS = "OPTIONS";
+ private static final String METHOD_POST = "POST";
+ private static final String METHOD_PUT = "PUT";
+ private static final String METHOD_TRACE = "TRACE";
+
+ public static final String FALLBACK_SERVLET_PROPERTY = "files.default";
+
+ private static final String[] DEFAULT_WELCOME_FILES = {"index.html", "index.htm"};
+
+ public final static String CACHE_CONTROL_HEADER = "Cache-Control";
+ public final static String EXPIRES_HEADER = "Expires";
+ public final static String DATE_HEADER = "Date";
+
+ private int bufferSize = 1024;
+
+ private File root;
+
+ private String prefix;
+
+ private boolean setCacheHeaders;
+ private int maxAge;
+
+ /**
+ * A servlet to pass control to if the file does not exist.
+ */
+
+ private String defaultServletDispatcherName;
+
+ /**
+ * Initialize the servlet, and determine the webapp root.
+ */
+ public void init() throws ServletException {
+ prefix = findPrefix();
+ root = findRoot();
+
+ // determine the cache values
+ setCacheHeaders = getServletConfig().getInitParameter("maxAge") != null;
+ if (setCacheHeaders) {
+ maxAge = Integer.parseInt(getServletConfig().getInitParameter("maxAge"));
+ }
+
+ // check for default fallback servlet
+ ServletContext context = getServletContext();
+ String defaultServletName = getServletConfig().getInitParameter("defaultServlet");
+ if (defaultServletName == null) {
+ defaultServletName = context.getInitParameter(FileServlet.FALLBACK_SERVLET_PROPERTY);
+ }
+ if (defaultServletName != null && defaultServletName.length() != 0) {
+ defaultServletDispatcherName = defaultServletName;
+ }
+ }
+
+ /**
+ * A prefix to prepend on the path when translating from URL to file location, typically "/public".
+ */
+ protected String findPrefix() {
+ String prefix = getServletContext().getInitParameter("files.prefix");
+ if (prefix == null) prefix = "/public";
+ // prefix must start with a slash if it's specified
+ if (prefix.length() > 0 && !prefix.startsWith("/")) {
+ prefix = "/" + prefix;
+ }
+ return prefix;
+ }
+
+ /**
+ * Root of the webapp, may be null in which case it is determined from the servlet api.
+ * The root should be an absolute path that refers to a directory.
+ */
+ protected File findRoot() throws ServletException {
+ String rootPath = getServletContext().getInitParameter("files.root");
+ if (rootPath == null) {
+ rootPath = FileUtil.getPath(getServletContext(), "/");
+ }
+
+ File root = new File(rootPath);
+ if (!root.isDirectory()) {
+ throw new ServletException("Webapp root does not point to a directory");
+ }
+
+ return root;
+ }
+
+ public String[] getWelcomeFiles() {
+ String[] welcomeFiles;
+
+ String welcomeFilesString = getServletContext().getInitParameter("files.welcome");
+ if (welcomeFilesString != null) {
+ welcomeFiles = parseCommaList(welcomeFilesString);
+ } else {
+ welcomeFiles = DEFAULT_WELCOME_FILES;
+ }
+
+ return welcomeFiles;
+ }
+
+ private String[] parseCommaList(String commaList) {
+ String[] parts = commaList.split(",");
+ for(int i=0; i<parts.length; i++) {
+ parts[i] = parts[i].trim();
+ }
+ return parts;
+ }
+
+ /**
+ * Look for a file matching the request.
+ */
+ protected File getFile(HttpServletRequest request) {
+ // find the location of the file
+ String contextPath = request.getContextPath();
+ String relativePath = request.getRequestURI().substring(contextPath.length());
+
+ // normalize the path
+ relativePath = relativePath.replaceAll("\\\\", "/").replaceAll("//", "/");
+
+ // determine the file path to check for
+ String filePath;
+ if (root == null) {
+ filePath = prefix + relativePath;
+ } else {
+ filePath = root.getAbsolutePath() + prefix + relativePath;
+ }
+
+ return getFile(filePath);
+ }
+
+ /**
+ * Look for a file matching the specified path.
+ * This should also check default extensions, and for index files in the case of a directory.
+ */
+ protected File getFile(String filePath) {
+ // try the exact match
+ File fileLocation = getExactFile(filePath);
+ if (fileLocation != null) return fileLocation;
+
+ // try default extension
+ fileLocation = getExactFile(filePath + ".html");
+ if (fileLocation != null) return fileLocation;
+
+ // try welcome files
+ String[] welcomeFiles = getWelcomeFiles();
+ for (int i = 0; i < welcomeFiles.length; i++) {
+
+ fileLocation = getExactFile(filePath + "/" + welcomeFiles[i]);
+ if (fileLocation != null) return fileLocation;
+ }
+
+ // no match was found
+ return null;
+ }
+
+ /**
+ * Look for a file with this exact path.
+ */
+ protected File getExactFile(String path) {
+ // try to load the resource
+ File filePath = new File(path);
+ if (!filePath.isFile()) return null;
+ return filePath;
+ }
+
+ private String formatDateForHeader(Date date) {
+ String safari3OnlyAccessThisStyleDataFormat = "EEE, d MMM yyyy HH:mm:ss z";
+ return new SimpleDateFormat(safari3OnlyAccessThisStyleDataFormat).format(date);
+ }
+
+ /**
+ * Transfer the file.
+ */
+ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ try {
+ // check the file and open it
+ File fileLocation = getFile(request);
+ if (fileLocation != null) {
+ // file was found, all good
+ } else if (defaultServletDispatcherName != null) {
+ // forward request to the default servlet
+ getServletContext().getNamedDispatcher(defaultServletDispatcherName).forward(request, response);
+ return;
+ } else {
+ // file not found
+ log("File not found: " + request.getRequestURI());
+ throw new FileNotFoundException(request.getRequestURI());
+ }
+
+ // check for modifications
+ long ifModifiedSince = request.getDateHeader("If-Modified-Since");
+ if (ifModifiedSince != -1) {
+ long lastModified = fileLocation.lastModified();
+ if (lastModified == 0) {
+ // last modified date is not known
+ } else if (lastModified < ifModifiedSince) {
+ throw new NotModifiedException();
+ } else {
+ response.setDateHeader("Last-Modified", lastModified);
+ }
+ }
+
+ // set cache headers
+ if (setCacheHeaders) {
+ response.setHeader(CACHE_CONTROL_HEADER, "max-age=" + maxAge);
+ Calendar now = Calendar.getInstance();
+ response.setHeader(DATE_HEADER, formatDateForHeader(now.getTime()));
+ now.add(Calendar.SECOND, maxAge);
+ response.setHeader(EXPIRES_HEADER, formatDateForHeader(now.getTime()));
+ }
+
+ // set the content type
+ String contentType = guessContentTypeFromName(fileLocation.getName());
+ response.setContentType(contentType);
+
+ if (request.getMethod().equals(METHOD_HEAD)) {
+ // head requests don't send the body
+ } else if (request.getMethod().equals(METHOD_GET) || request.getMethod().equals(METHOD_POST)) {
+ // transfer the content
+ sendFile(fileLocation, response);
+ } else {
+ // anything else cannot be processed on the file
+ // alternatively we could forward to rails, but this
+ // approach is probably more consistent with other web servers
+ response.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
+ }
+
+ } catch (NotModifiedException e) {
+ response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+ } catch (FileNotFoundException e) {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ } catch (IOException e) {
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
+ }
+ }
+
+ /**
+ * Send the file, faster, but requires the file is accessible on the file system.
+ */
+ private void sendFile(File file, HttpServletResponse response) throws IOException {
+ // setup IO streams
+ ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
+ FileChannel in = null;
+ try {
+ in = new FileInputStream(file).getChannel();
+
+ // start returning the response
+ OutputStream out = response.getOutputStream();
+
+ // read the bytes, returning them in the response
+ while (in.read(buffer) != -1) {
+ out.write(buffer.array(), 0, buffer.position());
+ buffer.clear();
+ }
+ out.close();
+ } finally {
+ try {
+ if (in != null) in.close();
+ } catch (IOException ignore) {
+ }
+ }
+ }
+
+ /**
+ * Return the content-type the would be returned for this file name.
+ */
+ public String guessContentTypeFromName(String fileName) {
+ // quick hack for types that are necessary, but not handled
+ String lowerName = fileName.toLowerCase();
+ if (lowerName.endsWith(".css")) {
+ return "text/css";
+ } else if (lowerName.endsWith(".js")) {
+ return "text/js";
+ }
+ try {
+ // everything else
+ javax.activation.FileTypeMap typeMap =
+ javax.activation.FileTypeMap.getDefaultFileTypeMap();
+ return typeMap.getContentType(fileName);
+ } catch (Throwable t) {
+ // allow activation.jar to be missing
+ return "application/octet-stream";
+ }
+ }
+
+ /**
+ * An exception when the source object has not been modified. While this
+ * condition is not a failure, it is a break from the normal flow of
+ * execution.
+ */
+ private static class NotModifiedException extends IOException {
+ public NotModifiedException() {
+ }
+ }
+
+}
Modified: trunk/rails-integration/src/main/java/org/jruby/webapp/RailsServlet.java (772 => 773)
--- trunk/rails-integration/src/main/java/org/jruby/webapp/RailsServlet.java 2007-10-16 12:32:16 UTC (rev 772)
+++ trunk/rails-integration/src/main/java/org/jruby/webapp/RailsServlet.java 2007-10-17 15:20:11 UTC (rev 773)
@@ -110,12 +110,42 @@
return cgiClass.newInstance(cgiArgs, Block.NULL_BLOCK);
}
+ /*
+ * This ugly hack is because Oracle servlets doesn't handle URIs like
+ * /people;browse correctly. We need to add this manually.
+ */
+ private String addMissingSemiColonParameterForOracle(String requestUriBefore, HttpServletRequest request) {
+ try {
+ java.lang.reflect.Field reqField = request.getClass().getField("requestURI");
+ Object reqData = reqField.get(request);
+ byte[] data = ""
+ int offset = reqData.getClass().getField("offset").getInt(reqData);
+ int length = reqData.getClass().getField("length").getInt(reqData);
+
+ int p = offset+length;
+ int len = data.length;
+ if(p < len && data[p++] == ';') {
+ int start = p;
+ // Delimited by either ? or a space - possibly not a 100% safe
+ while(p < len && data[p] != '?' && data[p] != ' ') {
+ p++;
+ }
+ return requestUriBefore + ";" + new String(data, start, p-start);
+ }
+ } catch(Throwable e) {}
+ return requestUriBefore;
+ }
+
protected void setupEnvironment(Ruby runtime, HttpServletRequest request) {
RubyHash env = (RubyHash)runtime.getObject().getConstant("ENV");
String requestUri = chomp(request.getRequestURI(), "/");
if (requestUri.length() == 0) requestUri = "/";
+ if(request.getClass().getName().indexOf("EvermindHttpServletRequest") != -1) {
+ requestUri = addMissingSemiColonParameterForOracle(requestUri, request);
+ }
+
// RFC3875 The Common Gateway Interface (CGI) Version 1.1
setEnv(env, "AUTH_TYPE", request.getAuthType());
if (request.getContentLength() != -1) {