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.