You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2011/11/18 15:34:00 UTC

svn commit: r1203669 - in /commons/proper/vfs/trunk: ./ core/ core/src/test/java/org/apache/commons/vfs2/provider/http/test/ src/changes/

Author: ggregory
Date: Fri Nov 18 14:33:59 2011
New Revision: 1203669

URL: http://svn.apache.org/viewvc?rev=1203669&view=rev
Log:
[VFS-386] Build tests HTTP file system with an embedded HTTP server (Apache HttpComponent Core)

Added:
    commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/provider/http/test/NHttpServer.java
Modified:
    commons/proper/vfs/trunk/core/pom.xml
    commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/provider/http/test/HttpProviderTestCase.java
    commons/proper/vfs/trunk/pom.xml
    commons/proper/vfs/trunk/src/changes/changes.xml

Modified: commons/proper/vfs/trunk/core/pom.xml
URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/core/pom.xml?rev=1203669&r1=1203668&r2=1203669&view=diff
==============================================================================
--- commons/proper/vfs/trunk/core/pom.xml (original)
+++ commons/proper/vfs/trunk/core/pom.xml Fri Nov 18 14:33:59 2011
@@ -16,7 +16,7 @@
    See the License for the specific language governing permissions and
    limitations under the License.
   -->
- 
+
 <project
   xmlns="http://maven.apache.org/POM/4.0.0"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -113,6 +113,12 @@
       <artifactId>commons-io</artifactId>
       <scope>test</scope>
     </dependency>
+    <!-- Test HTTP with Apache HttpComponent Core -->
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpcore-nio</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <properties>

Modified: commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/provider/http/test/HttpProviderTestCase.java
URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/provider/http/test/HttpProviderTestCase.java?rev=1203669&r1=1203668&r2=1203669&view=diff
==============================================================================
--- commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/provider/http/test/HttpProviderTestCase.java (original)
+++ commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/provider/http/test/HttpProviderTestCase.java Fri Nov 18 14:33:59 2011
@@ -16,6 +16,8 @@
  */
 package org.apache.commons.vfs2.provider.http.test;
 
+import java.io.IOException;
+
 import junit.framework.Test;
 
 import org.apache.commons.vfs2.FileObject;
@@ -24,46 +26,109 @@ import org.apache.commons.vfs2.impl.Defa
 import org.apache.commons.vfs2.provider.http.HttpFileProvider;
 import org.apache.commons.vfs2.test.AbstractProviderTestConfig;
 import org.apache.commons.vfs2.test.ProviderTestSuite;
+import org.apache.commons.vfs2.util.FreeSocketPortUtil;
 
 /**
  * Test cases for the HTTP provider.
- *
+ * 
  * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
  */
-public class HttpProviderTestCase
-    extends AbstractProviderTestConfig
+public class HttpProviderTestCase extends AbstractProviderTestConfig
 {
+    private static NHttpServer Server;
+
+    private static int SocketPort;
+
     private static final String TEST_URI = "test.http.uri";
-    public static Test suite() throws Exception
+
+    /**
+     * Use %40 for @ in URLs
+     */
+    private static String ConnectionUri;
+
+    private static String getSystemTestUriOverride()
     {
-        if (System.getProperty(TEST_URI) != null)
+        return System.getProperty(TEST_URI);
+    }
+
+    /**
+     * Creates and starts an embedded Apache HTTP Server ().
+     * 
+     * @throws Exception
+     */
+    private static void setUpClass() throws Exception
+    {
+        Server = new NHttpServer();
+        if (!Server.run(SocketPort, getTestDirectory(), 5000))
         {
-            return new ProviderTestSuite(new HttpProviderTestCase());
+            throw new IllegalStateException("The embedded HTTP server has not completed startup, increase wait time");
         }
-        else
+    }
+
+    public static Test suite() throws Exception
+    {
+        return new ProviderTestSuite(new HttpProviderTestCase())
         {
-            return notConfigured(HttpProviderTestCase.class);
-        }
+            @Override
+            protected void setUp() throws Exception
+            {
+                if (getSystemTestUriOverride() == null)
+                {
+                    setUpClass();
+                }
+                super.setUp();
+            }
+
+            @Override
+            protected void tearDown() throws Exception
+            {
+                tearDownClass();
+                super.tearDown();
+            }
+        };
     }
 
     /**
-     * Prepares the file system manager.
+     * Stops the embedded Apache HTTP Server ().
+     * 
+     * @throws IOException
      */
-    @Override
-    public void prepare(final DefaultFileSystemManager manager)
-        throws Exception
+    private static void tearDownClass() throws IOException
     {
-        manager.addProvider("http", new HttpFileProvider());
+        if (Server != null)
+        {
+            Server.stop();
+        }
+
+    }
+
+    public HttpProviderTestCase() throws IOException
+    {
+        SocketPort = FreeSocketPortUtil.findFreeLocalPort();
+        // Use %40 for @ in the a URL a @
+        ConnectionUri = "http://localhost:" + SocketPort;
     }
 
     /**
      * Returns the base folder for tests.
      */
     @Override
-    public FileObject getBaseTestFolder(final FileSystemManager manager)
-        throws Exception
+    public FileObject getBaseTestFolder(final FileSystemManager manager) throws Exception
     {
-        final String uri = System.getProperty(TEST_URI);
+        String uri = getSystemTestUriOverride();
+        if (uri == null)
+        {
+            uri = ConnectionUri;
+        }
         return manager.resolveFile(uri);
     }
+
+    /**
+     * Prepares the file system manager.
+     */
+    @Override
+    public void prepare(final DefaultFileSystemManager manager) throws Exception
+    {
+        manager.addProvider("http", new HttpFileProvider());
+    }
 }

Added: commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/provider/http/test/NHttpServer.java
URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/provider/http/test/NHttpServer.java?rev=1203669&view=auto
==============================================================================
--- commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/provider/http/test/NHttpServer.java (added)
+++ commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/provider/http/test/NHttpServer.java Fri Nov 18 14:33:59 2011
@@ -0,0 +1,298 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.commons.vfs2.provider.http.test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.Date;
+import java.util.Locale;
+import java.util.concurrent.Executors;
+
+import org.apache.commons.httpclient.util.DateUtil;
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.HttpStatus;
+import org.apache.http.MethodNotSupportedException;
+import org.apache.http.impl.DefaultConnectionReuseStrategy;
+import org.apache.http.impl.DefaultHttpResponseFactory;
+import org.apache.http.impl.nio.DefaultServerIOEventDispatch;
+import org.apache.http.impl.nio.reactor.DefaultListeningIOReactor;
+import org.apache.http.nio.NHttpConnection;
+import org.apache.http.nio.entity.NFileEntity;
+import org.apache.http.nio.entity.NStringEntity;
+import org.apache.http.nio.protocol.BufferingHttpServiceHandler;
+import org.apache.http.nio.protocol.EventListener;
+import org.apache.http.nio.reactor.IOEventDispatch;
+import org.apache.http.nio.reactor.IOReactorException;
+import org.apache.http.nio.reactor.ListeningIOReactor;
+import org.apache.http.params.CoreConnectionPNames;
+import org.apache.http.params.CoreProtocolPNames;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.SyncBasicHttpParams;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpProcessor;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.apache.http.protocol.HttpRequestHandlerRegistry;
+import org.apache.http.protocol.ImmutableHttpProcessor;
+import org.apache.http.protocol.ResponseConnControl;
+import org.apache.http.protocol.ResponseContent;
+import org.apache.http.protocol.ResponseDate;
+import org.apache.http.protocol.ResponseServer;
+import org.apache.http.util.EntityUtils;
+
+/**
+ * Adapted from org.apache.http.examples.nio.NHttpServer.
+ * 
+ * <p>
+ * Basic, yet fully functional and spec compliant, HTTP/1.1 server based on the non-blocking I/O model.
+ * </p>
+ * <p>
+ * Please note the purpose of this application is demonstrate the usage of HttpCore APIs. It is NOT intended to demonstrate the most
+ * efficient way of building an HTTP server.
+ * </p>
+ * 
+ * @version $Id: $
+ * @since 2.1
+ */
+public class NHttpServer
+{
+
+    static class EventLogger implements EventListener
+    {
+        public void connectionClosed(final NHttpConnection conn)
+        {
+            System.out.println("Connection closed: " + conn);
+        }
+
+        public void connectionOpen(final NHttpConnection conn)
+        {
+            System.out.println("Connection open: " + conn);
+        }
+
+        public void connectionTimeout(final NHttpConnection conn)
+        {
+            System.out.println("Connection timed out: " + conn);
+        }
+
+        public void fatalIOException(final IOException ex, final NHttpConnection conn)
+        {
+            System.err.println("I/O error: " + ex.getMessage());
+        }
+
+        public void fatalProtocolException(final HttpException ex, final NHttpConnection conn)
+        {
+            System.err.println("HTTP error: " + ex.getMessage());
+        }
+    }
+
+    static class HttpFileHandler implements HttpRequestHandler
+    {
+        private final String docRoot;
+
+        public HttpFileHandler(final String docRoot)
+        {
+            super();
+            this.docRoot = docRoot;
+        }
+
+        public void handle(final HttpRequest request, final HttpResponse response, final HttpContext context)
+                throws HttpException, IOException
+        {
+            String method = request.getRequestLine().getMethod().toUpperCase(Locale.ENGLISH);
+            if (!method.equals("GET") && !method.equals("HEAD") && !method.equals("POST"))
+            {
+                throw new MethodNotSupportedException(method + " method not supported");
+            }
+
+            if (request instanceof HttpEntityEnclosingRequest)
+            {
+                HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
+                byte[] entityContent = EntityUtils.toByteArray(entity);
+                System.out.println("Incoming entity content (bytes): " + entityContent.length);
+            }
+
+            String target = request.getRequestLine().getUri();
+            final File file = new File(this.docRoot, URLDecoder.decode(target, "UTF-8"));
+            if (!file.exists())
+            {
+                response.setStatusCode(HttpStatus.SC_NOT_FOUND);
+                NStringEntity entity = new NStringEntity("<html><body><h1>File" + file.getPath()
+                        + " not found</h1></body></html>", "UTF-8");
+                entity.setContentType("text/html; charset=UTF-8");
+                response.setEntity(entity);
+                System.out.println("File " + file.getPath() + " not found");
+
+            } else if (!file.canRead())
+            {
+                response.setStatusCode(HttpStatus.SC_FORBIDDEN);
+                NStringEntity entity = new NStringEntity("<html><body><h1>Access denied</h1></body></html>", "UTF-8");
+                entity.setContentType("text/html; charset=UTF-8");
+                response.setEntity(entity);
+                System.out.println("Cannot read file " + file.getPath());
+
+            } else
+            {
+                response.setStatusCode(HttpStatus.SC_OK);
+                NFileEntity body = new NFileEntity(file, "text/html");
+                response.setEntity(body);
+                if (!response.containsHeader(HttpHeaders.LAST_MODIFIED))
+                {
+                    response.addHeader(HttpHeaders.LAST_MODIFIED, DateUtil.formatDate(new Date(file.lastModified())));
+                }
+                System.out.println("Serving file " + file.getPath());
+            }
+        }
+    }
+
+    public static void main(String[] args) throws Exception
+    {
+        new NHttpServer().run(0, null, 0);
+    }
+
+    public volatile ListeningIOReactor ioReactor;
+
+    public boolean run(final int port, final String docRoot, long waitMillis) throws IOReactorException,
+            InterruptedException
+    {
+        Executors.newSingleThreadExecutor().execute(new Runnable()
+        {
+            public void run()
+            {
+                try
+                {
+                    runBlock(port, docRoot);
+                } catch (IOReactorException e)
+                {
+                    throw new IllegalStateException(e);
+                }
+            }
+        });
+        return waitForServerStartup(port, waitMillis);
+    }
+
+    private void runBlock(final int port, final String docRoot) throws IOReactorException
+    {
+        HttpParams params = new SyncBasicHttpParams();
+        params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 5000)
+                .setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024)
+                .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
+                .setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
+                .setParameter(CoreProtocolPNames.ORIGIN_SERVER, "HttpComponents/1.1");
+
+        HttpProcessor httpproc = new ImmutableHttpProcessor(new HttpResponseInterceptor[]
+        { new ResponseDate(), new ResponseServer(), new ResponseContent(), new ResponseConnControl() });
+
+        BufferingHttpServiceHandler handler = new BufferingHttpServiceHandler(httpproc,
+                new DefaultHttpResponseFactory(), new DefaultConnectionReuseStrategy(), params);
+
+        // Set up request handlers
+        HttpRequestHandlerRegistry reqistry = new HttpRequestHandlerRegistry();
+        reqistry.register("*", new HttpFileHandler(docRoot));
+
+        handler.setHandlerResolver(reqistry);
+
+        // Provide an event logger
+        handler.setEventListener(new EventLogger());
+
+        IOEventDispatch ioEventDispatch = new DefaultServerIOEventDispatch(handler, params);
+        ioReactor = new DefaultListeningIOReactor(2, params);
+        try
+        {
+            ioReactor.listen(new InetSocketAddress(port));
+            ioReactor.execute(ioEventDispatch);
+        } catch (InterruptedIOException ex)
+        {
+            System.err.println("Interrupted");
+        } catch (IOException e)
+        {
+            System.err.println("I/O error: " + e.getMessage());
+        }
+        System.out.println("Shutdown");
+    }
+
+    public void stop() throws IOException
+    {
+        if (this.ioReactor != null)
+        {
+            this.ioReactor.shutdown(2000);
+        }
+
+    }
+
+    /**
+     * Waits {@code waitMillis} for the server to start on the given {@code port}
+     * 
+     * @param port
+     *            The port the server is running on
+     * @param waitMillis
+     *            How long to wail in milliseconds.
+     * @throws InterruptedException
+     *             If waiting is interrupted
+     */
+    private boolean waitForServerStartup(final int port, long waitMillis) throws InterruptedException
+    {
+        final long endWait = System.currentTimeMillis() + waitMillis;
+        final String urlSpec = "http://localhost:" + port;
+        try
+        {
+            URL url = new URL(urlSpec);
+            InputStream inputStream = null;
+            while (System.currentTimeMillis() < endWait && inputStream == null)
+            {
+                try
+                {
+                    inputStream = url.openStream();
+                    if (inputStream != null)
+                    {
+                        IOUtils.closeQuietly(inputStream);
+                        return true;
+                    }
+                } catch (IOException e)
+                {
+                    // ignore
+                }
+                Thread.sleep(100);
+            }
+        } catch (MalformedURLException e)
+        {
+            throw new IllegalStateException("Error in test code for URL " + urlSpec);
+        }
+        return false;
+    }
+}

Modified: commons/proper/vfs/trunk/pom.xml
URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/pom.xml?rev=1203669&r1=1203668&r2=1203669&view=diff
==============================================================================
--- commons/proper/vfs/trunk/pom.xml (original)
+++ commons/proper/vfs/trunk/pom.xml Fri Nov 18 14:33:59 2011
@@ -17,7 +17,6 @@
    limitations under the License.
   -->
 
-
 <project
   xmlns="http://maven.apache.org/POM/4.0.0"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -399,6 +398,13 @@
         <version>2.1</version>
         <scope>test</scope>
       </dependency>
+      <!-- Test HTTP with Apache HttpComponent Core -->
+      <dependency>
+        <groupId>org.apache.httpcomponents</groupId>
+        <artifactId>httpcore-nio</artifactId>
+        <version>4.1.3</version>
+        <scope>test</scope>
+      </dependency>
     </dependencies>
   </dependencyManagement>
 

Modified: commons/proper/vfs/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/src/changes/changes.xml?rev=1203669&r1=1203668&r2=1203669&view=diff
==============================================================================
--- commons/proper/vfs/trunk/src/changes/changes.xml (original)
+++ commons/proper/vfs/trunk/src/changes/changes.xml Fri Nov 18 14:33:59 2011
@@ -23,6 +23,9 @@
 
   <body>
     <release version="2.1" date="TBD" description="">
+      <action issue="VFS-386" dev="ggregory" type="update">
+        Build tests HTTP file system with an embedded HTTP server (Apache HttpComponent Core).
+      </action>
       <action issue="VFS-385" dev="ggregory" type="update">
         Add HTTP status code to HTTP file provider exception messages when available.
       </action>