Author: slotia
Date: 2012-08-20 15:12:19 -0700 (Mon, 20 Aug 2012)
New Revision: 30233

Added:
   
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/AddAccessControlAllowOriginHeaderAfterResponse.java
   
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpAfterResponse.java
   
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpBeforeResponse.java
   
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpRequest.java
   
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpRequestImpl.java
   
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpResponder.java
   
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpResponse.java
   
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpResponseFactory.java
   
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpResponseFactoryImpl.java
   
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpd.java
   
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpdFactory.java
   
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpdFactoryImpl.java
   
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/LocalhostServerSocketFactory.java
   
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/OriginOptionsBeforeResponse.java
   
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/ScreenOriginsBeforeResponse.java
   
core3/impl/trunk/app-impl/src/test/java/org/cytoscape/app/internal/net/server/
   
core3/impl/trunk/app-impl/src/test/java/org/cytoscape/app/internal/net/server/AddAccessControlAllowOriginHeaderAfterResponseTest.java
   
core3/impl/trunk/app-impl/src/test/java/org/cytoscape/app/internal/net/server/CyHttpdImplTest.java
   
core3/impl/trunk/app-impl/src/test/java/org/cytoscape/app/internal/net/server/OriginOptionsBeforeResponseTest.java
   
core3/impl/trunk/app-impl/src/test/java/org/cytoscape/app/internal/net/server/ScreenOriginsBeforeResponseTest.java
Removed:
   
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/LocalhostServerSocketFactoryImpl.java
Modified:
   core3/impl/trunk/app-impl/pom.xml
   
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/CyActivator.java
Log:
Initial commit of refactored http server

Modified: core3/impl/trunk/app-impl/pom.xml
===================================================================
--- core3/impl/trunk/app-impl/pom.xml   2012-08-20 21:58:29 UTC (rev 30232)
+++ core3/impl/trunk/app-impl/pom.xml   2012-08-20 22:12:19 UTC (rev 30233)
@@ -50,6 +50,14 @@
                                        </instructions>
                                </configuration>
                        </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <forkMode>pertest</forkMode>
+                    
<argLine>-Dsun.net.http.allowRestrictedHeaders=true</argLine>
+                </configuration>
+            </plugin>
                </plugins>
        </build>
 

Modified: 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/CyActivator.java
===================================================================
--- 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/CyActivator.java
 2012-08-20 21:58:29 UTC (rev 30232)
+++ 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/CyActivator.java
 2012-08-20 22:12:19 UTC (rev 30233)
@@ -9,7 +9,7 @@
 import org.cytoscape.app.internal.net.server.AppGetResponder;
 import org.cytoscape.app.internal.net.server.LocalHttpServer;
 import org.cytoscape.app.internal.net.server.ServerSocketFactory;
-import org.cytoscape.app.internal.net.server.LocalhostServerSocketFactoryImpl;
+import org.cytoscape.app.internal.net.server.LocalhostServerSocketFactory;
 import org.cytoscape.app.internal.net.server.LocalHttpServer.Response;
 import org.cytoscape.app.swing.CySwingAppAdapter;
 import org.cytoscape.application.CyVersion;
@@ -377,7 +377,7 @@
                        
                        @Override
                        public void run() {
-                final ServerSocketFactory serverSocketFactory = new 
LocalhostServerSocketFactoryImpl(2608);
+                final ServerSocketFactory serverSocketFactory = new 
LocalhostServerSocketFactory(2608);
                                server = new 
LocalHttpServer(serverSocketFactory, Executors.newSingleThreadExecutor());
                                server.addGetResponder(new 
AppGetResponder(appManager));
                                

Added: 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/AddAccessControlAllowOriginHeaderAfterResponse.java
===================================================================
--- 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/AddAccessControlAllowOriginHeaderAfterResponse.java
                           (rev 0)
+++ 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/AddAccessControlAllowOriginHeaderAfterResponse.java
   2012-08-20 22:12:19 UTC (rev 30233)
@@ -0,0 +1,13 @@
+package org.cytoscape.app.internal.net.server;
+
+public class AddAccessControlAllowOriginHeaderAfterResponse implements 
CyHttpAfterResponse
+{
+    static final CyHttpResponseFactory responseFactory = new 
CyHttpResponseFactoryImpl();
+
+    public CyHttpResponse intercept(CyHttpRequest request, CyHttpResponse 
response)
+    {
+        final String origin = request.getHeaders().get("Origin");
+        response.getHeaders().put("Access-Control-Allow-Origin", (origin == 
null ? "*" : origin));
+        return response;
+    }
+}

Added: 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpAfterResponse.java
===================================================================
--- 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpAfterResponse.java
                              (rev 0)
+++ 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpAfterResponse.java
      2012-08-20 22:12:19 UTC (rev 30233)
@@ -0,0 +1,10 @@
+package org.cytoscape.app.internal.net.server;
+
+/**
+ * Intercepts a {@link CyHttpResponse} before it is sent out to the client.
+ * This is typically used to add headers to the response.
+ */
+public interface CyHttpAfterResponse
+{
+    public CyHttpResponse intercept(CyHttpRequest request, CyHttpResponse 
pendingResponse);
+}

Added: 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpBeforeResponse.java
===================================================================
--- 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpBeforeResponse.java
                             (rev 0)
+++ 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpBeforeResponse.java
     2012-08-20 22:12:19 UTC (rev 30233)
@@ -0,0 +1,15 @@
+package org.cytoscape.app.internal.net.server;
+
+/**
+ * Intercepts a request before it is handled by a {@link CyHttpResponder}.
+ * This is used typically to respond to the {@code OPTIONS} method and
+ * to deny requests without sufficient credentials.
+ */
+public interface CyHttpBeforeResponse
+{
+    /**
+     * @return null to allow the server to normally use its {@link 
CyHttpResponder}s,
+     * or a {@link CyHttpResponse} to break the response chain and intercept 
the request and respond directly.
+     */
+    public CyHttpResponse intercept(CyHttpRequest request);
+}

Added: 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpRequest.java
===================================================================
--- 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpRequest.java
                            (rev 0)
+++ 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpRequest.java
    2012-08-20 22:12:19 UTC (rev 30233)
@@ -0,0 +1,62 @@
+package org.cytoscape.app.internal.net.server;
+
+import java.util.Map;
+
+/**
+ * An Http request issued by the client.
+ */
+public interface CyHttpRequest
+{
+    /**
+     * The URI the client requested.
+     * This will not include the protocol or server address.
+     * For example, if the client requested {@code 
http://myserver:8000/my/resource},
+     * the URI will be {@code /my/resource}.
+     */
+    String getURI();
+
+    /**
+     * The http method.
+     * Typically the method is {@code GET} or {@code POST}.
+     * @return the http method in all uppercase characters.
+     */
+    String getMethod();
+    
+    /**
+     * The http request's headers.
+     * This will join headers that appear more than once.
+     * If the http request's headers are:
+     * <pre>
+     * {@code
+     *   Header-1: a
+     *   Header-2: b
+     *   Header-1: c
+     *   Header-3: d
+     * }
+     * </pre>
+     * This will return the following map:
+     * <pre>
+     * {@code
+     *  {
+     *   "Header-1": "a\nc"
+     *   "Header-2": "b",
+     *   "Header-3": "d"
+     *  }
+     * }
+     * </pre>
+     */
+    Map<String,String> getHeaders();
+
+    /**
+     * Get the content of the request.
+     * For {@code GET} methods, this will typically return an empty string.
+     * Parameters of the {@code GET} method are provided in the URI.
+     * For {@code POST} methods, this will return the body of the request.
+     */
+    String getContent();
+
+    /**
+     * Get the MIME type of the content.
+     */
+    String getContentType();
+}

Added: 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpRequestImpl.java
===================================================================
--- 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpRequestImpl.java
                                (rev 0)
+++ 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpRequestImpl.java
        2012-08-20 22:12:19 UTC (rev 30233)
@@ -0,0 +1,91 @@
+package org.cytoscape.app.internal.net.server;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.util.EntityUtils;
+import org.apache.http.Header;
+import java.net.URLDecoder;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+public class CyHttpRequestImpl implements CyHttpRequest
+{
+    final String uri;
+    final String method;
+    final Map<String,String> headers;
+    final String content;
+    final String contentType;
+
+    public CyHttpRequestImpl(final HttpRequest request)
+    {
+        final String uriEncoded = request.getRequestLine().getUri();
+        try
+        {
+            uri = URLDecoder.decode(uriEncoded, "UTF-8");
+        }
+        catch (UnsupportedEncodingException e)
+        {
+            throw new IllegalArgumentException("Unable to parse uri: " + 
uriEncoded, e);
+        }
+
+        method = request.getRequestLine().getMethod().toUpperCase();
+
+        headers = new HashMap<String,String>();
+        for (final Header header : request.getAllHeaders())
+        {
+            final String name = header.getName();
+            String value = header.getValue();
+            if (headers.containsKey(name))
+                value = headers.get(name) + '\n' + value;
+            headers.put(name, value);
+        }
+
+        if (request instanceof HttpEntityEnclosingRequest)
+        {
+            final HttpEntity entity = ((HttpEntityEnclosingRequest) 
request).getEntity();
+            contentType = entity.getContentType().getValue();
+            try
+            {
+                content = EntityUtils.toString(entity);
+            }
+            catch (IOException e)
+            {
+                throw new IllegalArgumentException("Unable to read entity as 
string", e);
+            }
+        }
+        else
+        {
+            contentType = null;
+            content = null;
+        }
+    }
+
+    public String getURI()
+    {
+        return uri;
+    }
+
+    public String getMethod()
+    {
+        return method;
+    }
+    
+    public Map<String,String> getHeaders()
+    {
+        return Collections.unmodifiableMap(headers);
+    }
+
+    public String getContent()
+    {
+        return content;
+    }
+
+    public String getContentType()
+    {
+        return contentType;
+    }
+}

Added: 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpResponder.java
===================================================================
--- 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpResponder.java
                          (rev 0)
+++ 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpResponder.java
  2012-08-20 22:12:19 UTC (rev 30233)
@@ -0,0 +1,36 @@
+package org.cytoscape.app.internal.net.server;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+/**
+ * Provides a response for a given request.
+ * When {@link CyHttpd} receives a request,
+ * it goes through each of its {@code CyHttpResponder}s.
+ * When a {@code CyHttpResponder}'s URI pattern matches the
+ * request's URI, that {@code CyHttpResponder} will be invoked
+ * to handle the request.
+ */
+public interface CyHttpResponder
+{
+    /**
+     * Get the regular expression that matches the responder to the request's 
URI.
+     * <p>
+     * The request URI does not include the protocol, server name, or port. If 
the client issues
+     * a request to the URL {@code http://localhost:2607/a/b/c}, the server 
receives the
+     * request URI to be {@code /a/b/c}.
+     * </p>
+     * <p>
+     * Groups can be included in the regular expression, as the {@link Matcher}
+     * object is passed to the {@code respond} method.
+     * </p>
+     */
+    public Pattern getURIPattern();
+
+    /**
+     * Invoked by {@link CyHttpd} to respond to a request.
+     * @param matchedURI The {@link Matcher} object that matches against the 
request's URI;
+     * useful for retrieving groups in the regular expression.
+     */
+    public CyHttpResponse respond(CyHttpRequest request, Matcher matchedURI);
+}

Added: 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpResponse.java
===================================================================
--- 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpResponse.java
                           (rev 0)
+++ 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpResponse.java
   2012-08-20 22:12:19 UTC (rev 30233)
@@ -0,0 +1,32 @@
+package org.cytoscape.app.internal.net.server;
+
+import java.util.Map;
+
+/**
+ * The response the server sends back to the client.
+ */
+public interface CyHttpResponse 
+{
+    /**
+     * The http protocol status code of the response.
+     * Use values from {@link org.apache.http.HttpStatus} to fill this in.
+     */
+    public int getStatusCode();
+
+    /**
+     * The response's content.
+     */
+    public String getContent();
+
+    /**
+     * Get the MIME type of the content.
+     */
+    public String getContentType();
+
+    /**
+     * Get the headers of the response.
+     * This should return a mutable map so that {@link CyHttpAfterResponse}
+     * can add or modify headers before a response is sent back to the server.
+     */
+    public Map<String,String> getHeaders();
+}

Added: 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpResponseFactory.java
===================================================================
--- 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpResponseFactory.java
                            (rev 0)
+++ 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpResponseFactory.java
    2012-08-20 22:12:19 UTC (rev 30233)
@@ -0,0 +1,14 @@
+package org.cytoscape.app.internal.net.server;
+
+public interface CyHttpResponseFactory
+{
+    /**
+     * Create a response with no content.
+     */
+    CyHttpResponse createHttpResponse(int statusCode);
+
+    /**
+     * Create a response with content.
+     */
+    CyHttpResponse createHttpResponse(int statusCode, String content, String 
contentType);
+}

Added: 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpResponseFactoryImpl.java
===================================================================
--- 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpResponseFactoryImpl.java
                                (rev 0)
+++ 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpResponseFactoryImpl.java
        2012-08-20 22:12:19 UTC (rev 30233)
@@ -0,0 +1,40 @@
+package org.cytoscape.app.internal.net.server;
+
+import java.util.Map;
+import java.util.HashMap;
+
+public class CyHttpResponseFactoryImpl implements CyHttpResponseFactory
+{
+    public CyHttpResponse createHttpResponse(final int statusCode)
+    {
+        return createHttpResponse(statusCode, null, null);
+    }
+
+    public CyHttpResponse createHttpResponse(final int statusCode, final 
String content, final String contentType)
+    {
+        return new CyHttpResponse()
+        {
+            final Map<String,String> headers = new HashMap<String,String>();
+
+            public int getStatusCode()
+            {
+                return statusCode;
+            }
+
+            public String getContent()
+            {
+                return content;
+            }
+
+            public String getContentType()
+            {
+                return contentType;
+            }
+
+            public Map<String,String> getHeaders()
+            {
+                return headers;
+            }
+        };
+    }
+}

Added: 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpd.java
===================================================================
--- 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpd.java
                          (rev 0)
+++ 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpd.java
  2012-08-20 22:12:19 UTC (rev 30233)
@@ -0,0 +1,76 @@
+package org.cytoscape.app.internal.net.server;
+
+/**
+ * The http server.
+ * 
+ * <h4>The Response Chain</h4>
+ * <p>When the server receives a request,
+ * it follows the <i>response chain</i>:</p>
+ * <ol>
+ *  <li>
+ *   It goes through its list of {@link CyHttpBeforeResponse}s.
+ *   If any of them return a {@link CyHttpResponse}, the server 
+ *   immediately responds to the client with the
+ *   {@code CyHttpBeforeResponse}'s {@code CyHttpResponse}.
+ *   In this case, no {@code CyHttpResponder}s are invoked.
+ *  </li>
+ *  <li>
+ *   If all of the {@code CyHttpBeforeResponse}s return null,
+ *   the server goes through each of its {@link CyHttpResponder}s.
+ *   The first {@code CyHttpResponder} whose regular expression
+ *   matches the request's URI gets invoked. If none of the
+ *   {@code CyHttpResponder}s match the URI, a 404 error is returned
+ *   to the client.
+ *  </li>
+ *  <li>
+ *   After a {@code CyHttpResponder} returns with a response,
+ *   all of the {@link CyHttpAfterResponse}s are invoked to further
+ *   process the response.
+ *  </li>
+ *  <li>
+ *   The {@code CyHttpResponder} is sent to the client.
+ *  </li>
+ * </ol>
+ *
+ * <h4>Implications of the Response Chain</h4>
+ * <ul>
+ *  <li>
+ *   The {@code OPTIONS} method can be in a
+ *   {@code CyHttpBeforeResponse}.
+ *  </li>
+ *  <li>
+ *   Clients can be denied if its {@code Origin} is not allowed
+ *   through a {@code CyHttpBeforeResponse}.
+ *  </li>
+ *  <li>
+ *   Headers can be appended to responses in an 
+ *   {@code CyHttpAfterResponse}.
+ *  </li>
+ * </ul>
+ */
+public interface CyHttpd
+{
+    /**
+     * Starts the server to service requests from the client.
+     */
+    void start();
+
+    /**
+     * Stops the server from servicing new requests.
+     */
+    void stop();
+
+    boolean isRunning();
+
+    void addResponder(CyHttpResponder responder);
+    void removeResponder(CyHttpResponder responder);
+    Iterable<CyHttpResponder> getResponders();
+
+    void addBeforeResponse(CyHttpBeforeResponse beforeResponse);
+    void removeBeforeResponse(CyHttpBeforeResponse beforeResponse);
+    Iterable<CyHttpBeforeResponse> getBeforeResponses();
+
+    void addAfterResponse(CyHttpAfterResponse afterResponse);
+    void removeAfterResponse(CyHttpAfterResponse afterResponse);
+    Iterable<CyHttpAfterResponse> getAfterResponses();
+}

Added: 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpdFactory.java
===================================================================
--- 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpdFactory.java
                           (rev 0)
+++ 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpdFactory.java
   2012-08-20 22:12:19 UTC (rev 30233)
@@ -0,0 +1,6 @@
+package org.cytoscape.app.internal.net.server;
+
+public interface CyHttpdFactory
+{
+    CyHttpd createHttpd(ServerSocketFactory serverSocketFactory);
+}

Added: 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpdFactoryImpl.java
===================================================================
--- 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpdFactoryImpl.java
                               (rev 0)
+++ 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpdFactoryImpl.java
       2012-08-20 22:12:19 UTC (rev 30233)
@@ -0,0 +1,324 @@
+package org.cytoscape.app.internal.net.server;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.regex.Matcher;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.http.protocol.HttpService;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.SyncBasicHttpParams;
+import org.apache.http.params.CoreConnectionPNames;
+import org.apache.http.params.CoreProtocolPNames;
+import org.apache.http.protocol.HttpProcessor;
+import org.apache.http.protocol.ImmutableHttpProcessor;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.apache.http.HttpResponseInterceptor;
+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.protocol.HttpRequestHandlerRegistry;
+import org.apache.http.impl.DefaultConnectionReuseStrategy;
+import org.apache.http.impl.DefaultHttpResponseFactory;
+import org.apache.http.HttpServerConnection;
+import org.apache.http.impl.DefaultHttpServerConnection;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.Header;
+import org.apache.http.HttpException;
+import org.apache.http.ConnectionClosedException;
+import org.apache.http.MethodNotSupportedException;
+import org.apache.http.RequestLine;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.entity.ContentType;
+import org.apache.http.HttpStatus;
+
+public class CyHttpdFactoryImpl implements CyHttpdFactory
+{
+    public CyHttpd createHttpd(final ServerSocketFactory serverSocketFactory)
+    {
+        return new CyHttpdImpl(serverSocketFactory);
+    }
+}
+
+class CyHttpdImpl implements CyHttpd
+{
+    static final Logger logger = LoggerFactory.getLogger(CyHttpdImpl.class);
+
+    final ServerSocketFactory serverSocketFactory;
+    final List<CyHttpResponder> responders = new ArrayList<CyHttpResponder>();
+    final List<CyHttpBeforeResponse> beforeResponses = new 
ArrayList<CyHttpBeforeResponse>();
+    final List<CyHttpAfterResponse> afterResponses = new 
ArrayList<CyHttpAfterResponse>();
+
+    boolean running = false;
+    ExecutorService executor = null;
+
+    final HttpParams params;
+    final HttpService service;
+
+    public CyHttpdImpl(final ServerSocketFactory serverSocketFactory)
+    {
+        if (serverSocketFactory == null)
+            throw new IllegalArgumentException("serverSocketFactory == null");
+        this.serverSocketFactory = serverSocketFactory;
+
+
+               // Setup params
+       
+               params = (new SyncBasicHttpParams())
+                   .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");
+       
+               // Setup service
+       
+               final HttpProcessor proc = new ImmutableHttpProcessor(new 
HttpResponseInterceptor[] {
+                       new ResponseDate(),
+                       new ResponseServer(),
+                       new ResponseContent(),
+                       new ResponseConnControl()
+           });
+                   
+               final HttpRequestHandlerRegistry registry = new 
HttpRequestHandlerRegistry();
+               registry.register("*", new RequestHandlerDispatcher());
+                   
+               service = new HttpService(proc, 
+                                         new DefaultConnectionReuseStrategy(), 
+                                         new DefaultHttpResponseFactory(),
+                                         registry,
+                                         params);
+    }
+
+    public void start()
+    {
+        if (running)
+            throw new IllegalStateException("server is running");
+        running = true;
+        executor = Executors.newCachedThreadPool();
+        executor.execute(new ServerThread());
+    }
+
+    public void stop()
+    {
+        if (!running)
+            throw new IllegalStateException("server is not running");
+        running = false;
+        executor.shutdown();
+        executor = null;
+    }
+
+    public boolean isRunning()
+    {
+        return running;
+    }
+
+    public void addResponder(CyHttpResponder responder)
+    {
+        responders.add(responder);
+    }
+
+    public void removeResponder(CyHttpResponder responder)
+    {
+        responders.remove(responder);
+    }
+
+    public Iterable<CyHttpResponder> getResponders()
+    {
+        return responders;
+    }
+
+    public void addBeforeResponse(CyHttpBeforeResponse beforeResponse)
+    {
+        beforeResponses.add(beforeResponse);
+    }
+
+    public void removeBeforeResponse(CyHttpBeforeResponse beforeResponse)
+    {
+        beforeResponses.remove(beforeResponse);
+    }
+
+    public Iterable<CyHttpBeforeResponse> getBeforeResponses()
+    {
+        return beforeResponses;
+    }
+
+    public void addAfterResponse(CyHttpAfterResponse afterResponse)
+    {
+        afterResponses.add(afterResponse);
+    }
+
+    public void removeAfterResponse(CyHttpAfterResponse afterResponse)
+    {
+        afterResponses.remove(afterResponse);
+    }
+
+    public Iterable<CyHttpAfterResponse> getAfterResponses()
+    {
+        return afterResponses;
+    }
+
+    class ServerThread implements Runnable
+    {
+        public void run()
+        {
+            // Create a server socket
+            ServerSocket serverSocket = null;
+            try
+            {
+                serverSocket = serverSocketFactory.createServerSocket();
+            }
+            catch (IOException e)
+            {
+                logger.error("Failed to create server socket", e);
+                return;
+            }
+        
+            logger.info("Server socket started on {}", String.format("%s:%d", 
serverSocket.getInetAddress().getHostAddress(), serverSocket.getLocalPort()));
+            
+            // Keep servicing incoming connections until we're told to stop
+            while (running)
+            {
+                
+                // Create a new http server connection from the incoming socket
+                DefaultHttpServerConnection connection = null;
+                try {
+                    final Socket socket = serverSocket.accept();
+                    logger.info("Server socket received connection from {}", 
socket.getInetAddress().getHostAddress());
+                    connection = new DefaultHttpServerConnection();
+                    connection.bind(socket, params);
+                } catch (IOException e) {
+                    logger.error("Failed to initiate connection with client", 
e);
+                    continue;
+                }
+        
+                // Dispatch an incoming connection to ConnectionHandler
+                final ConnectionHandler connectionHandler = new 
ConnectionHandler(service, connection);
+                executor.execute(connectionHandler);
+            }
+        
+            logger.info("Server socket stopped");
+        }
+    }
+
+    class ConnectionHandler implements Runnable
+    {
+        final HttpService service;
+        final HttpServerConnection connection;
+        
+        public ConnectionHandler(final HttpService service, final 
HttpServerConnection connection)
+        {
+                   this.service = service;
+                   this.connection = connection;
+        }
+       
+        public void run()
+        {
+            final HttpContext context = new BasicHttpContext(null);
+            try
+            {
+                while (running && connection.isOpen())
+                    service.handleRequest(connection, context);
+            } catch (SocketTimeoutException e) {
+                // ignore, this happens normally
+            } catch (ConnectionClosedException e) {
+                // ignore, this happens normally
+            } catch (IOException e) {
+                logger.warn("Failed to complete communication with client; 
connection closing", e);
+            } catch (HttpException e) {
+                logger.warn("Client violated http; connection closing", e);
+            } finally {
+                try
+                {
+                    connection.shutdown();
+                }
+                catch (IOException e)
+                {
+                    // We don't care if an exception happened during shutdown
+                }
+            }
+        }
+    }
+
+    class RequestHandlerDispatcher implements HttpRequestHandler
+    {
+               public void handle(final HttpRequest httpRequest, final 
HttpResponse httpResponse, final HttpContext httpContext) throws HttpException
+        {
+            for (org.apache.http.Header header : httpRequest.getAllHeaders())
+                logger.info("{}: {}", header.getName(), header.getValue());
+            final CyHttpRequest request = new CyHttpRequestImpl(httpRequest);
+            final CyHttpResponse response = handle(request);
+            if (response == null)
+            {
+                setHttpResponse(HttpStatus.SC_NOT_FOUND, "<html><body><h1>404 
Not Found</h1></body></html>", "text/html", httpResponse);
+            }
+            else
+            {
+                setHttpResponse(response, httpResponse);
+            }
+        }
+
+        private CyHttpResponse handle(final CyHttpRequest request)
+        {
+            CyHttpResponse response = null;
+
+            for (final CyHttpBeforeResponse beforeResponse : beforeResponses)
+            {
+                response = beforeResponse.intercept(request);
+                if (response != null)
+                    return response;
+            }
+
+            final String uri = request.getURI();
+            logger.info("Received request: {}", uri);
+            for (final CyHttpResponder responder : responders)
+            {
+                final Matcher matcher = responder.getURIPattern().matcher(uri);
+                if (matcher.matches())
+                {
+                    response = responder.respond(request, matcher);
+                    break;
+                }
+            }
+            if (response == null)
+                return null;
+
+            for (final CyHttpAfterResponse afterResponse : afterResponses)
+                response = afterResponse.intercept(request, response);
+
+            return response;
+        }
+    }
+
+    static final void setHttpResponse(final CyHttpResponse response, final 
HttpResponse httpResponse)
+    {
+        setHttpResponse(response.getStatusCode(), response.getContent(), 
response.getContentType(), httpResponse);
+        for (final Map.Entry<String,String> entry : 
response.getHeaders().entrySet())
+            httpResponse.addHeader(entry.getKey(), entry.getValue());
+    }
+
+    static final void setHttpResponse(final int statusCode, final String 
content, final String contentType, final HttpResponse httpResponse)
+    {
+               httpResponse.setStatusCode(statusCode);
+        if (content != null && contentType != null)
+            httpResponse.setEntity(new StringEntity(content, 
ContentType.create(contentType, "UTF-8")));
+    }
+}

Added: 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/LocalhostServerSocketFactory.java
===================================================================
--- 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/LocalhostServerSocketFactory.java
                             (rev 0)
+++ 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/LocalhostServerSocketFactory.java
     2012-08-20 22:12:19 UTC (rev 30233)
@@ -0,0 +1,29 @@
+package org.cytoscape.app.internal.net.server;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.InetAddress;
+
+/**
+ * An implementation of {@link ServerSocketFactory} that only accepts 
connections
+ * from localhost.
+ */
+public class LocalhostServerSocketFactory implements ServerSocketFactory
+{
+    final int port;
+
+    public LocalhostServerSocketFactory(int port)
+    {
+        if (port <= 0)
+            throw new IllegalArgumentException("port <= 0");
+        this.port = port;
+    }
+
+    /**
+     * Create a server socket with the given port and default backlog.
+     */
+    public ServerSocket createServerSocket() throws IOException
+    {
+        return new ServerSocket(port, 0, InetAddress.getByName(null));
+    }
+}

Deleted: 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/LocalhostServerSocketFactoryImpl.java
===================================================================
--- 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/LocalhostServerSocketFactoryImpl.java
 2012-08-20 21:58:29 UTC (rev 30232)
+++ 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/LocalhostServerSocketFactoryImpl.java
 2012-08-20 22:12:19 UTC (rev 30233)
@@ -1,29 +0,0 @@
-package org.cytoscape.app.internal.net.server;
-
-import java.io.IOException;
-import java.net.ServerSocket;
-import java.net.InetAddress;
-
-/**
- * An implementation of {@link ServerSocketFactory} that only accepts 
connections
- * from localhost.
- */
-public class LocalhostServerSocketFactoryImpl implements ServerSocketFactory
-{
-    final int port;
-
-    public LocalhostServerSocketFactoryImpl(int port)
-    {
-        if (port <= 0)
-            throw new IllegalArgumentException("port <= 0");
-        this.port = port;
-    }
-
-    /**
-     * Create a server socket with the given port and default backlog.
-     */
-    public ServerSocket createServerSocket() throws IOException
-    {
-        return new ServerSocket(port, 0, InetAddress.getByName(null));
-    }
-}

Added: 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/OriginOptionsBeforeResponse.java
===================================================================
--- 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/OriginOptionsBeforeResponse.java
                              (rev 0)
+++ 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/OriginOptionsBeforeResponse.java
      2012-08-20 22:12:19 UTC (rev 30233)
@@ -0,0 +1,42 @@
+package org.cytoscape.app.internal.net.server;
+
+import org.apache.http.HttpStatus;
+
+/**
+ * Supports responses to the {@code OPTIONS} http method requests.
+ * This is not a completely accurate implementation of {@code OPTIONS}.
+ * If the client requests {@code OPTIONS} on a non-existant URL, this will not
+ * return 404. But that's okay. If the client later requests {@code GET} on the
+ * same, non-existant URL, it will get the 404.
+ */
+public class OriginOptionsBeforeResponse implements CyHttpBeforeResponse
+{
+    static final CyHttpResponseFactory responseFactory = new 
CyHttpResponseFactoryImpl();
+    final String allowedHeadersFmt;
+
+    public OriginOptionsBeforeResponse(final String... allowedHeaders)
+    {
+        final StringBuffer allowedHeadersBuffer = new StringBuffer("origin, 
accept");
+        for (final String allowedHeader : allowedHeaders)
+        {
+            allowedHeadersBuffer.append(", ");
+            allowedHeadersBuffer.append(allowedHeader);
+        }
+        this.allowedHeadersFmt = allowedHeadersBuffer.toString();
+    }
+
+    public CyHttpResponse intercept(CyHttpRequest request)
+    {
+        if (!"OPTIONS".equals(request.getMethod()))
+            return null;
+
+        final String origin = request.getHeaders().get("Origin");
+
+        final CyHttpResponse response = 
responseFactory.createHttpResponse(HttpStatus.SC_OK);
+        response.getHeaders().put("Access-Control-Allow-Origin", (origin == 
null ? "*" : origin));
+        response.getHeaders().put("Access-Control-Allow-Methods", "POST, PUT, 
GET, OPTIONS");
+        response.getHeaders().put("Access-Control-Max-Age", "1");
+        response.getHeaders().put("Access-Control-Allow-Headers", 
allowedHeadersFmt);
+        return response;
+    }
+}

Added: 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/ScreenOriginsBeforeResponse.java
===================================================================
--- 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/ScreenOriginsBeforeResponse.java
                              (rev 0)
+++ 
core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/ScreenOriginsBeforeResponse.java
      2012-08-20 22:12:19 UTC (rev 30233)
@@ -0,0 +1,32 @@
+package org.cytoscape.app.internal.net.server;
+
+import org.apache.http.HttpStatus;
+
+public class ScreenOriginsBeforeResponse implements CyHttpBeforeResponse
+{
+    static final CyHttpResponseFactory responseFactory = new 
CyHttpResponseFactoryImpl();
+    final String[] allowedOrigins;
+
+    /**
+     * @param allowedOrigins 
+     */
+    public ScreenOriginsBeforeResponse(final String... allowedOrigins)
+    {
+        this.allowedOrigins = allowedOrigins;
+    }
+
+    public CyHttpResponse intercept(CyHttpRequest request)
+    {
+        final String origin = request.getHeaders().get("Origin");
+        if (origin == null)
+            return responseFactory.createHttpResponse(HttpStatus.SC_FORBIDDEN, 
"<html><body><h1>403 Forbidden</h1>No <tt>Origin</tt> header 
specified.</body></html>", "text/html");
+
+        for (final String allowedOrigin : allowedOrigins)
+        {
+            if (allowedOrigin.equals(origin))
+                return null;
+        }
+
+        return responseFactory.createHttpResponse(HttpStatus.SC_FORBIDDEN, 
String.format("<html><body><h1>403 Forbidden</h1><tt>Origin: %s</tt> not 
allowed.</body></html>", origin), "text/html");
+    }
+}

Added: 
core3/impl/trunk/app-impl/src/test/java/org/cytoscape/app/internal/net/server/AddAccessControlAllowOriginHeaderAfterResponseTest.java
===================================================================
--- 
core3/impl/trunk/app-impl/src/test/java/org/cytoscape/app/internal/net/server/AddAccessControlAllowOriginHeaderAfterResponseTest.java
                               (rev 0)
+++ 
core3/impl/trunk/app-impl/src/test/java/org/cytoscape/app/internal/net/server/AddAccessControlAllowOriginHeaderAfterResponseTest.java
       2012-08-20 22:12:19 UTC (rev 30233)
@@ -0,0 +1,79 @@
+package org.cytoscape.app.internal.net.server;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+import org.apache.http.HttpStatus;
+
+import java.net.URL;
+import java.net.HttpURLConnection;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+public class AddAccessControlAllowOriginHeaderAfterResponseTest {
+       @Test
+       public void testAddAccessControlAllowOriginHeader() throws Exception
+    {
+        final CyHttpd httpd = (new CyHttpdFactoryImpl()).createHttpd(new 
LocalhostServerSocketFactory(2611));
+        final CyHttpResponseFactory responseFactory = new 
CyHttpResponseFactoryImpl();
+        httpd.addResponder(new CyHttpResponder()
+        {
+            public Pattern getURIPattern()
+            {
+                return Pattern.compile("^/test$");
+            }
+
+            public CyHttpResponse respond(CyHttpRequest request, Matcher 
matchedURI)
+            {
+                return responseFactory.createHttpResponse(HttpStatus.SC_OK, 
"test response ok", "text/html");
+            }
+        });
+        httpd.addAfterResponse(new 
AddAccessControlAllowOriginHeaderAfterResponse());
+        httpd.start();
+
+        HttpURLConnection connection = null;
+        final String url = "http://localhost:2611/test";;
+
+        connection = connectToURL(url, "GET");
+        assertTrue(connection.getResponseCode() == HttpURLConnection.HTTP_OK);
+        assertEquals(connection.getHeaderField("Access-Control-Allow-Origin"), 
"*");
+        assertEquals(readConnection(connection), "test response ok");
+
+        httpd.stop();
+       }
+
+    private static HttpURLConnection connectToURL(String urlString, String 
method) throws Exception
+    {
+        final URL url = new URL(urlString);
+        final HttpURLConnection connection = (HttpURLConnection) 
url.openConnection();
+        connection.setRequestMethod(method);
+        connection.setDoOutput(true);
+        connection.setConnectTimeout(1000);
+        connection.connect();
+        return connection;
+    }
+
+    private static String readConnection(HttpURLConnection connection) throws 
Exception
+    {
+        final StringBuffer buffer = new StringBuffer();
+        final BufferedReader reader = new BufferedReader(new 
InputStreamReader(connection.getInputStream()));
+        while (true)
+        {
+            final String line = reader.readLine();
+            if (line == null)
+                break;
+            buffer.append(line);
+        }
+
+        return buffer.toString();
+    }
+}

Added: 
core3/impl/trunk/app-impl/src/test/java/org/cytoscape/app/internal/net/server/CyHttpdImplTest.java
===================================================================
--- 
core3/impl/trunk/app-impl/src/test/java/org/cytoscape/app/internal/net/server/CyHttpdImplTest.java
                          (rev 0)
+++ 
core3/impl/trunk/app-impl/src/test/java/org/cytoscape/app/internal/net/server/CyHttpdImplTest.java
  2012-08-20 22:12:19 UTC (rev 30233)
@@ -0,0 +1,131 @@
+package org.cytoscape.app.internal.net.server;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+import org.apache.http.HttpStatus;
+
+import java.net.URL;
+import java.net.HttpURLConnection;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+public class CyHttpdImplTest {
+
+       @Test
+       public void testHttpd() throws Exception
+    {
+        final CyHttpd httpd = (new CyHttpdFactoryImpl()).createHttpd(new 
LocalhostServerSocketFactory(2607)); // why 2607? 'cuz it's my birfday
+        final CyHttpResponseFactory responseFactory = new 
CyHttpResponseFactoryImpl();
+        httpd.addResponder(new CyHttpResponder()
+        {
+            public Pattern getURIPattern()
+            {
+                return Pattern.compile("^/testA$");
+            }
+
+            public CyHttpResponse respond(CyHttpRequest request, Matcher 
matchedURI)
+            {
+                return responseFactory.createHttpResponse(HttpStatus.SC_OK, 
"testA response ok", "text/html");
+            }
+        });
+
+        httpd.addResponder(new CyHttpResponder()
+        {
+            public Pattern getURIPattern()
+            {
+                return Pattern.compile("^/testB$");
+            }
+
+            public CyHttpResponse respond(CyHttpRequest request, Matcher 
matchedURI)
+            {
+                return responseFactory.createHttpResponse(HttpStatus.SC_OK, 
"testB response ok", "text/html");
+            }
+        });
+
+        httpd.addBeforeResponse(new CyHttpBeforeResponse()
+        {
+            public CyHttpResponse intercept(CyHttpRequest request)
+            {
+                if (request.getMethod().equals("OPTIONS"))
+                    return 
responseFactory.createHttpResponse(HttpStatus.SC_OK, "options intercepted", 
"text/html");
+                else
+                    return null;
+            }
+        });
+
+        httpd.addAfterResponse(new CyHttpAfterResponse()
+        {
+            public CyHttpResponse intercept(CyHttpRequest request, 
CyHttpResponse response)
+            {
+                response.getHeaders().put("SomeRandomHeader", 
"WowInterceptWorks");
+                return response;
+            }
+        });
+
+
+        assertFalse(httpd.isRunning());
+        httpd.start();
+        assertTrue(httpd.isRunning());
+
+        // test if normal response works
+        HttpURLConnection connection = 
connectToURL("http://localhost:2607/testA";, "GET");
+        assertTrue(connection.getResponseCode() == HttpURLConnection.HTTP_OK);
+        assertEquals(readConnection(connection), "testA response ok");
+
+        connection = connectToURL("http://localhost:2607/testB";, "GET");
+        assertTrue(connection.getResponseCode() == HttpURLConnection.HTTP_OK);
+        assertEquals(readConnection(connection), "testB response ok");
+
+        // test if 404 response works
+        connection = connectToURL("http://localhost:2607/testX";, "GET");
+        assertTrue(connection.getResponseCode() == 
HttpURLConnection.HTTP_NOT_FOUND);
+
+        // test if before intercept works
+        connection = connectToURL("http://localhost:2607/testA";, "OPTIONS");
+        assertTrue(connection.getResponseCode() == HttpURLConnection.HTTP_OK);
+        assertEquals(readConnection(connection), "options intercepted");
+
+        // test if after intercept works
+        connection = connectToURL("http://localhost:2607/testA";, "GET");
+        assertTrue(connection.getResponseCode() == HttpURLConnection.HTTP_OK);
+        assertEquals(connection.getHeaderField("SomeRandomHeader"), 
"WowInterceptWorks");
+
+        httpd.stop();
+        assertFalse(httpd.isRunning());
+       }
+
+    private static HttpURLConnection connectToURL(String urlString, String 
method) throws Exception
+    {
+        final URL url = new URL(urlString);
+        final HttpURLConnection connection = (HttpURLConnection) 
url.openConnection();
+        connection.setRequestMethod(method);
+        connection.setDoOutput(true);
+        connection.connect();
+        return connection;
+    }
+
+    private static String readConnection(HttpURLConnection connection) throws 
Exception
+    {
+        final StringBuffer buffer = new StringBuffer();
+        final BufferedReader reader = new BufferedReader(new 
InputStreamReader(connection.getInputStream()));
+        while (true)
+        {
+            final String line = reader.readLine();
+            if (line == null)
+                break;
+            buffer.append(line);
+        }
+
+        return buffer.toString();
+    }
+}

Added: 
core3/impl/trunk/app-impl/src/test/java/org/cytoscape/app/internal/net/server/OriginOptionsBeforeResponseTest.java
===================================================================
--- 
core3/impl/trunk/app-impl/src/test/java/org/cytoscape/app/internal/net/server/OriginOptionsBeforeResponseTest.java
                          (rev 0)
+++ 
core3/impl/trunk/app-impl/src/test/java/org/cytoscape/app/internal/net/server/OriginOptionsBeforeResponseTest.java
  2012-08-20 22:12:19 UTC (rev 30233)
@@ -0,0 +1,85 @@
+package org.cytoscape.app.internal.net.server;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+import org.apache.http.HttpStatus;
+
+import java.net.URL;
+import java.net.HttpURLConnection;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+public class OriginOptionsBeforeResponseTest {
+       @Test
+       public void testOptions() throws Exception
+    {
+        final CyHttpd httpd = (new CyHttpdFactoryImpl()).createHttpd(new 
LocalhostServerSocketFactory(2610));
+        final CyHttpResponseFactory responseFactory = new 
CyHttpResponseFactoryImpl();
+        httpd.addResponder(new CyHttpResponder()
+        {
+            public Pattern getURIPattern()
+            {
+                return Pattern.compile("^/test$");
+            }
+
+            public CyHttpResponse respond(CyHttpRequest request, Matcher 
matchedURI)
+            {
+                return responseFactory.createHttpResponse(HttpStatus.SC_OK, 
"test response ok", "text/html");
+            }
+        });
+        httpd.addBeforeResponse(new OriginOptionsBeforeResponse());
+        httpd.start();
+
+        HttpURLConnection connection = null;
+        final String url = "http://localhost:2610/test";;
+
+        connection = connectToURL(url, "OPTIONS");
+        assertTrue(connection.getResponseCode() == HttpURLConnection.HTTP_OK);
+        assertEquals(connection.getHeaderField("Access-Control-Allow-Origin"), 
"*");
+        
assertEquals(connection.getHeaderField("Access-Control-Allow-Methods"), "POST, 
PUT, GET, OPTIONS");
+        assertEquals(connection.getHeaderField("Access-Control-Max-Age"), "1");
+        
assertEquals(connection.getHeaderField("Access-Control-Allow-Headers"), 
"origin, accept");
+
+        connection = connectToURL(url, "GET");
+        assertTrue(connection.getResponseCode() == HttpURLConnection.HTTP_OK);
+        assertEquals(readConnection(connection), "test response ok");
+
+        httpd.stop();
+       }
+
+    private static HttpURLConnection connectToURL(String urlString, String 
method) throws Exception
+    {
+        final URL url = new URL(urlString);
+        final HttpURLConnection connection = (HttpURLConnection) 
url.openConnection();
+        connection.setRequestMethod(method);
+        connection.setDoOutput(true);
+        connection.setConnectTimeout(1000);
+        connection.connect();
+        return connection;
+    }
+
+    private static String readConnection(HttpURLConnection connection) throws 
Exception
+    {
+        final StringBuffer buffer = new StringBuffer();
+        final BufferedReader reader = new BufferedReader(new 
InputStreamReader(connection.getInputStream()));
+        while (true)
+        {
+            final String line = reader.readLine();
+            if (line == null)
+                break;
+            buffer.append(line);
+        }
+
+        return buffer.toString();
+    }
+}

Added: 
core3/impl/trunk/app-impl/src/test/java/org/cytoscape/app/internal/net/server/ScreenOriginsBeforeResponseTest.java
===================================================================
--- 
core3/impl/trunk/app-impl/src/test/java/org/cytoscape/app/internal/net/server/ScreenOriginsBeforeResponseTest.java
                          (rev 0)
+++ 
core3/impl/trunk/app-impl/src/test/java/org/cytoscape/app/internal/net/server/ScreenOriginsBeforeResponseTest.java
  2012-08-20 22:12:19 UTC (rev 30233)
@@ -0,0 +1,73 @@
+package org.cytoscape.app.internal.net.server;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+import org.apache.http.HttpStatus;
+
+import java.net.URL;
+import java.net.HttpURLConnection;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+public class ScreenOriginsBeforeResponseTest {
+       @Test
+       public void testScreenOrigins() throws Exception
+    {
+        final CyHttpd httpd = (new CyHttpdFactoryImpl()).createHttpd(new 
LocalhostServerSocketFactory(2609));
+        final CyHttpResponseFactory responseFactory = new 
CyHttpResponseFactoryImpl();
+        httpd.addResponder(new CyHttpResponder()
+        {
+            public Pattern getURIPattern()
+            {
+                return Pattern.compile("^/test$");
+            }
+
+            public CyHttpResponse respond(CyHttpRequest request, Matcher 
matchedURI)
+            {
+                return responseFactory.createHttpResponse(HttpStatus.SC_OK, 
"test response ok", "text/html");
+            }
+        });
+        httpd.addBeforeResponse(new ScreenOriginsBeforeResponse("http://x";, 
"http://y";));
+        httpd.start();
+
+        HttpURLConnection connection = null;
+        final String url = "http://localhost:2609/test";;
+
+        connection = connectToURL(url , "GET", null);
+        assertTrue(connection.getResponseCode() == 
HttpURLConnection.HTTP_FORBIDDEN);
+
+        connection = connectToURL(url, "GET", "http://x";);
+        assertTrue(connection.getResponseCode() == HttpURLConnection.HTTP_OK);
+
+        connection = connectToURL(url, "GET", "http://y";);
+        assertTrue(connection.getResponseCode() == HttpURLConnection.HTTP_OK);
+
+        connection = connectToURL(url, "GET", "http://z";);
+        assertTrue(connection.getResponseCode() == 
HttpURLConnection.HTTP_FORBIDDEN);
+
+        httpd.stop();
+       }
+
+    private static HttpURLConnection connectToURL(String urlString, String 
method, String origin) throws Exception
+    {
+        final URL url = new URL(urlString);
+        final HttpURLConnection connection = (HttpURLConnection) 
url.openConnection();
+        connection.setRequestMethod(method);
+        connection.setDoOutput(true);
+        if (origin != null)
+            connection.setRequestProperty("Origin", origin);
+        connection.setConnectTimeout(1000);
+        connection.connect();
+        return connection;
+    }
+}

-- 
You received this message because you are subscribed to the Google Groups 
"cytoscape-cvs" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/cytoscape-cvs?hl=en.

Reply via email to