Author: scottbw
Date: Sun Aug 21 17:22:01 2011
New Revision: 1160014

URL: http://svn.apache.org/viewvc?rev=1160014&view=rev
Log:
Added a very basic file download function for responding to a GET request for 
an exported widget instance ("flatpack"). This gets around various 
configuration issues associated with "default servlets" on different servlet 
containers (e.g. Jetty vs Tomcat) - see WOOKIE-231 and WOOKIE-182. Limitations 
in terms of security have also been noted in the code comments, and associated 
functional tests were also updated. Note that requests for flatpacks are now 
targeted at GET /flatpack/{id} and not /exports/{id}, but that exported files 
are still physically located in /exports - however the /exports folder itself 
is not published. 

Modified:
    incubator/wookie/trunk/WebContent/WEB-INF/web.xml
    
incubator/wookie/trunk/src-tests/org/apache/wookie/tests/functional/FlatpackControllerTest.java
    
incubator/wookie/trunk/src/org/apache/wookie/flatpack/FlatpackController.java

Modified: incubator/wookie/trunk/WebContent/WEB-INF/web.xml
URL: 
http://svn.apache.org/viewvc/incubator/wookie/trunk/WebContent/WEB-INF/web.xml?rev=1160014&r1=1160013&r2=1160014&view=diff
==============================================================================
--- incubator/wookie/trunk/WebContent/WEB-INF/web.xml (original)
+++ incubator/wookie/trunk/WebContent/WEB-INF/web.xml Sun Aug 21 17:22:01 2011
@@ -135,7 +135,6 @@
                <url-pattern>/updates/*</url-pattern>
        </servlet-mapping>
        
-       
        <servlet>
                <description></description>
                <display-name>Flatpack</display-name>
@@ -149,10 +148,6 @@
                <servlet-name>Flatpack</servlet-name>
                <url-pattern>/flatpack/*</url-pattern>
        </servlet-mapping>
-       <servlet-mapping>
-               <servlet-name>default</servlet-name>
-               <url-pattern>/export/*</url-pattern>    
-       </servlet-mapping>
 
        <servlet>
                <description></description>

Modified: 
incubator/wookie/trunk/src-tests/org/apache/wookie/tests/functional/FlatpackControllerTest.java
URL: 
http://svn.apache.org/viewvc/incubator/wookie/trunk/src-tests/org/apache/wookie/tests/functional/FlatpackControllerTest.java?rev=1160014&r1=1160013&r2=1160014&view=diff
==============================================================================
--- 
incubator/wookie/trunk/src-tests/org/apache/wookie/tests/functional/FlatpackControllerTest.java
 (original)
+++ 
incubator/wookie/trunk/src-tests/org/apache/wookie/tests/functional/FlatpackControllerTest.java
 Sun Aug 21 17:22:01 2011
@@ -33,7 +33,7 @@ public class FlatpackControllerTest exte
   private static final String TEST_FLATPACK_SERVICE_URL_VALID = 
TEST_SERVER_LOCATION
       + "flatpack";
   private static final String TEST_EXPORT_SERVICE_URL_VALID = 
TEST_SERVER_LOCATION
-      + "export";
+      + "flatpack";
   private static final String TEST_WIDGET_ID_JQM = 
"http://wookie.apache.org/widgets/freeder";;
   private static String test_id_key = "";
 
@@ -81,12 +81,7 @@ public class FlatpackControllerTest exte
     GetMethod get = new GetMethod(TEST_EXPORT_SERVICE_URL_VALID);
     client.executeMethod(get);
     int code = get.getStatusCode();
-    if (code != 404) {
-      String html = get.getResponseBodyAsString();
-      System.out.println(html);
-      assertEquals(html.length(), 0);
-    }
-
+    assertEquals(403, code);
   }
 
   /**

Modified: 
incubator/wookie/trunk/src/org/apache/wookie/flatpack/FlatpackController.java
URL: 
http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/flatpack/FlatpackController.java?rev=1160014&r1=1160013&r2=1160014&view=diff
==============================================================================
--- 
incubator/wookie/trunk/src/org/apache/wookie/flatpack/FlatpackController.java 
(original)
+++ 
incubator/wookie/trunk/src/org/apache/wookie/flatpack/FlatpackController.java 
Sun Aug 21 17:22:01 2011
@@ -13,7 +13,10 @@
  */
 package org.apache.wookie.flatpack;
 
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.net.URL;
 
@@ -27,6 +30,7 @@ import org.apache.wookie.beans.IWidgetIn
 import org.apache.wookie.controller.Controller;
 import org.apache.wookie.controller.WidgetInstancesController;
 import org.apache.wookie.exceptions.InvalidParametersException;
+import org.apache.wookie.exceptions.ResourceNotFoundException;
 import org.apache.wookie.exceptions.UnauthorizedAccessException;
 import org.apache.wookie.helpers.WidgetKeyManager;
 
@@ -36,6 +40,8 @@ import org.apache.wookie.helpers.WidgetK
  * This class provides a controller front end for the FlatpackFactory class, 
enabling the export of Widget Instances via a HTTP POST request.
  * 
  * POST /flatpack/ {params: api_key, instance_params OR id_key} creates a new 
W3C Widget package (.wgt) with an opaque file name for the specified widget 
instance, and returns the download URL. 
+ * GET /flatpack/id.wgt download a previously created flatpack
+ * 
  * If an invalid API key is supplied, a 401 error code is returned. If no 
instance can be found, or the parameters supplied are invalid, a 400 error code 
is returned.
  */
 public class FlatpackController extends Controller {
@@ -43,16 +49,131 @@ public class FlatpackController extends 
        private static final long serialVersionUID = 2907712805939515004L;
        static Logger _logger = 
Logger.getLogger(FlatpackController.class.getName());   
 
+       /* (non-Javadoc)
+   * @see org.apache.wookie.controller.Controller#show(java.lang.String, 
javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+   */
        /**
-        * Deny access to the listing of the flatpack folder
+        * Downloads a previously generated export file.
+        * 
+        * <p><b>Note:</b> currently there is no authentication for this 
method, which relies solely
+        * on the hard-to-guess exported widget name. </p>
+        * 
+        * <p>For the future we may want to:</p>
+        * 
+        * <ul>
+        * <li> require an access token of some kind before allowing download. 
</ul>
+        * <li> delete exported widgets after they have been downloaded. </ul>
+        * <li> delete exported widgets at a set period after they have been 
created. </ul>
+        * </ul>
         */
-       @Override
-       protected void doGet(HttpServletRequest request,
-                       HttpServletResponse response) throws ServletException, 
IOException {
-               response.sendError(HttpServletResponse.SC_FORBIDDEN);
-       }
+  @Override
+  protected void show(String resourceId, HttpServletRequest request,
+      HttpServletResponse response) throws ResourceNotFoundException,
+      UnauthorizedAccessException, IOException {
+    
+    //
+    // If there is no resource part of the requested path, or the request is 
not for a ".wgt" file, return a 404 immediately
+    //
+    if (resourceId == null || resourceId.trim().length() == 0 || 
!resourceId.endsWith(".wgt")) {
+      throw new ResourceNotFoundException();
+    }
+    
+    //
+    // Get the file path for the requested item
+    //
+    String requestedPackageFilePath = 
request.getSession().getServletContext().getRealPath(FlatpackFactory.DEFAULT_FLATPACK_FOLDER+"/"+resourceId);
+    
+    //
+    // Get the widget package corresponding to the path, and throw a 404 if it 
doesn't exist
+    //
+    File widgetPackage = new File(requestedPackageFilePath);
+    if (!widgetPackage.exists()){
+      throw new ResourceNotFoundException();
+    }
+    
+    //
+    // Log the download and the IP used
+    //
+    _logger.info("exported widget package " + resourceId + " downloaded; IP:" 
+ request.getRemoteAddr());
+    
+    //
+    // Set the content-type of the response to application/widget, which
+    // is the standard MIME type for widgets.
+    //
+    final String contentType = "application/widget";
+    
+    //
+    // Set buffer size of response to 10k
+    //
+    final int bufferSize = 10240;
+    
+    //
+    // Initialize the response
+    //
+    response.reset();
+    response.setBufferSize(bufferSize);
+    response.setContentType(contentType);
+    response.setHeader("Content-Length", 
String.valueOf(widgetPackage.length()));
+    
+    //
+    // We can override the browser default behaviour to force it to open a 
save dialog box;
+    // however at least Opera will treat this as a Widget package and do 
something smarter, and
+    // perhaps other browsers may do so in future, so leaving this commented 
out.
+    //
+    //response.setHeader("Content-Disposition", "attachment; filename=\"" + 
widgetPackage.getName() + "\"");
+
+    //
+    // Prepare streams
+    //
+    BufferedInputStream input = null;
+    BufferedOutputStream output = null;
+
+    //
+    // Send the file
+    //
+    try {
+      
+        //
+        // Open streams
+        //
+        input = new BufferedInputStream(new FileInputStream(widgetPackage), 
bufferSize);
+        output = new BufferedOutputStream(response.getOutputStream(), 
bufferSize);
+
+        //
+        // Stream the file to the response
+        //
+        byte[] buffer = new byte[bufferSize];
+        int length;
+        while ((length = input.read(buffer)) > 0) {
+            output.write(buffer, 0, length);
+        }
+        
+    } finally {
+      
+        //
+        // Close streams
+        //
+        output.close();
+        input.close();
+    }
+
+  }
+
+  /* (non-Javadoc)
+   * @see 
org.apache.wookie.controller.Controller#index(javax.servlet.http.HttpServletRequest,
 javax.servlet.http.HttpServletResponse)
+   */
+  /**
+   * Deny access to the listing of the flatpack folder
+   */
+  @Override
+  protected void index(HttpServletRequest request, HttpServletResponse 
response)
+      throws UnauthorizedAccessException, IOException {
+    response.sendError(HttpServletResponse.SC_FORBIDDEN);
+  }
 
-       /**
+
+
+  /**
         * We override the default POST method from Controller as we need to 
return the package URL in the Response to the client
         */
        @Override


Reply via email to