Author: slotia Date: 2012-08-21 17:20:19 -0700 (Tue, 21 Aug 2012) New Revision: 30246
Removed: core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/LocalHttpServer.java Modified: core3/impl/trunk/app-impl/api-notes.txt core3/impl/trunk/app-impl/pom.xml 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/action/AppManagerAction.java 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/AppGetResponder.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/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/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/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/main/java/org/cytoscape/app/internal/net/server/ServerSocketFactory.java core3/impl/trunk/app-impl/src/test/java/org/cytoscape/app/internal/net/server/CyHttpdImplTest.java Log: Incorporated refactored httpd Modified: core3/impl/trunk/app-impl/api-notes.txt =================================================================== --- core3/impl/trunk/app-impl/api-notes.txt 2012-08-21 22:59:42 UTC (rev 30245) +++ core3/impl/trunk/app-impl/api-notes.txt 2012-08-22 00:20:19 UTC (rev 30246) @@ -7,7 +7,7 @@ URL --- -GET http://127.0.0.1:2608/status?appname=<app_name> +GET http://127.0.0.1:2607/status/<app_name> Notes: App_name is the full app name. For web-downloaded apps, it will be the same as the Cytoscape-App-Name entry in the manifest. @@ -30,7 +30,7 @@ URL --- -GET http://127.0.0.1:2608/install?appname=<app_name>&version=<version> +GET http://127.0.0.1:2607/install/<app_name>/<version> Response -------- Modified: core3/impl/trunk/app-impl/pom.xml =================================================================== --- core3/impl/trunk/app-impl/pom.xml 2012-08-21 22:59:42 UTC (rev 30245) +++ core3/impl/trunk/app-impl/pom.xml 2012-08-22 00:20:19 UTC (rev 30246) @@ -50,6 +50,14 @@ </instructions> </configuration> </plugin> + <!-- + The Sun JVM's implementation of java.net.HttpURLConnection does not + normally allow setting the http header "Origin" (see the + "restrictedHeaders" field in sun.net.www.protocol.http.HttpURLConnection). + We need to be able to set this header to run tests on + ScreenOriginsBeforeResponseTest. Adding the argument below to the JVM + allows us to set the "Origin" header. + --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> 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-21 22:59:42 UTC (rev 30245) +++ core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/CyActivator.java 2012-08-22 00:20:19 UTC (rev 30246) @@ -7,10 +7,13 @@ import org.cytoscape.app.internal.manager.AppManager; import org.cytoscape.app.internal.net.WebQuerier; import org.cytoscape.app.internal.net.server.AppGetResponder; -import org.cytoscape.app.internal.net.server.LocalHttpServer; +import org.cytoscape.app.internal.net.server.CyHttpd; +import org.cytoscape.app.internal.net.server.CyHttpdFactoryImpl; import org.cytoscape.app.internal.net.server.ServerSocketFactory; import org.cytoscape.app.internal.net.server.LocalhostServerSocketFactory; -import org.cytoscape.app.internal.net.server.LocalHttpServer.Response; +import org.cytoscape.app.internal.net.server.ScreenOriginsBeforeResponse; +import org.cytoscape.app.internal.net.server.AddAccessControlAllowOriginHeaderAfterResponse; +import org.cytoscape.app.internal.net.server.OriginOptionsBeforeResponse; import org.cytoscape.app.swing.CySwingAppAdapter; import org.cytoscape.application.CyVersion; import org.cytoscape.session.CySessionManager; @@ -369,23 +372,16 @@ fileUtilServiceRef, dialogTaskManagerRef, cyServiceRegistrarRef); registerService(bc, appManagerAction, CyAction.class, new Properties()); - // Start thread for local server that reports app installation status to the app store when requested, + // Start local server that reports app installation status to the app store when requested, // also able to install an app when told by the app store - Thread serverThread = new Thread() { - - private LocalHttpServer server; - - @Override - public void run() { - final ServerSocketFactory serverSocketFactory = new LocalhostServerSocketFactory(2608); - server = new LocalHttpServer(serverSocketFactory, Executors.newSingleThreadExecutor()); - server.addGetResponder(new AppGetResponder(appManager)); - - server.run(); - } - }; - serverThread.setDaemon(true); - Executors.newSingleThreadExecutor().execute(serverThread); + final AppGetResponder appGetResponder = new AppGetResponder(appManager); + final CyHttpd httpd = (new CyHttpdFactoryImpl()).createHttpd(new LocalhostServerSocketFactory(2607)); + httpd.addBeforeResponse(new ScreenOriginsBeforeResponse("http://apps.cytoscape.org", "http://apps3.nrnb.org")); + httpd.addBeforeResponse(new OriginOptionsBeforeResponse("x-csrftoken")); + httpd.addAfterResponse(new AddAccessControlAllowOriginHeaderAfterResponse()); + httpd.addResponder(appGetResponder.new StatusResponder()); + httpd.addResponder(appGetResponder.new InstallResponder()); + httpd.start(); // cyPropertyRef.getProperties().put("testkey1", "testval1"); // cyPropertyRef.getProperties().setProperty("testkey2", "testval2"); Modified: core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/action/AppManagerAction.java =================================================================== --- core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/action/AppManagerAction.java 2012-08-21 22:59:42 UTC (rev 30245) +++ core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/action/AppManagerAction.java 2012-08-22 00:20:19 UTC (rev 30246) @@ -10,7 +10,6 @@ import org.cytoscape.app.internal.manager.AppManager; import org.cytoscape.app.internal.net.WebApp; import org.cytoscape.app.internal.net.WebQuerier; -import org.cytoscape.app.internal.net.server.LocalHttpServer; import org.cytoscape.app.internal.ui.AppManagerDialog; import org.cytoscape.application.events.CyShutdownEvent; import org.cytoscape.application.events.CyShutdownListener; @@ -53,8 +52,6 @@ */ private CyServiceRegistrar serviceRegistrar; - private LocalHttpServer server; - private AppManagerDialog appManagerDialog = null; Modified: 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 2012-08-21 22:59:42 UTC (rev 30245) +++ core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/AddAccessControlAllowOriginHeaderAfterResponse.java 2012-08-22 00:20:19 UTC (rev 30246) @@ -1,5 +1,8 @@ package org.cytoscape.app.internal.net.server; +/** + * Adds the {@code Access-Control-Allow-Origin} http header to responses. + */ public class AddAccessControlAllowOriginHeaderAfterResponse implements CyHttpAfterResponse { static final CyHttpResponseFactory responseFactory = new CyHttpResponseFactoryImpl(); Modified: core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/AppGetResponder.java =================================================================== --- core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/AppGetResponder.java 2012-08-21 22:59:42 UTC (rev 30245) +++ core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/AppGetResponder.java 2012-08-22 00:20:19 UTC (rev 30246) @@ -4,84 +4,90 @@ import java.util.HashMap; import java.util.Map; +import java.util.regex.Pattern; +import java.util.regex.Matcher; + import org.cytoscape.app.internal.exception.AppDownloadException; import org.cytoscape.app.internal.exception.AppInstallException; import org.cytoscape.app.internal.exception.AppParsingException; import org.cytoscape.app.internal.manager.App; import org.cytoscape.app.internal.manager.AppManager; import org.cytoscape.app.internal.net.WebApp; -import org.cytoscape.app.internal.net.server.LocalHttpServer.Response; import org.cytoscape.app.internal.util.DebugHelper; import org.json.JSONObject; /** * This class is responsible for handling GET requests received by the local HTTP server. */ -public class AppGetResponder implements LocalHttpServer.GetResponder{ +public class AppGetResponder { + private static final CyHttpResponseFactory responseFactory = new CyHttpResponseFactoryImpl(); - private static final String STATUS_QUERY_URL = "status"; - private static final String STATUS_QUERY_APP_NAME = "appname"; - - private static final String INSTALL_QUERY_URL = "install"; - private static final String INSTALL_QUERY_APP_NAME = "appname"; - private static final String INSTALL_QUERY_APP_VERSION = "version"; - private AppManager appManager; - @Override - public boolean canRespondTo(String url) throws Exception { - return true; + public AppGetResponder(AppManager appManager) { + this.appManager = appManager; } - @Override - public Response respond(String url) throws Exception { - Map<String, String> parsed = parseEncodedUrl(url); - - DebugHelper.print("Request received. Url: " + url); - DebugHelper.print("Url request prefix: " + getUrlRequestPrefix(url)); - DebugHelper.print("Parsed result of encoded url: " + parsed); + private abstract static class JsonResponder implements CyHttpResponder { + protected abstract Map<String,String> jsonRespond(CyHttpRequest request, Matcher matchedURI); - String responseBody = "Empty"; - - String urlRequestPrefix = getUrlRequestPrefix(url); - - Map<String, String> responseData = new HashMap<String, String>(); - if (urlRequestPrefix.equalsIgnoreCase(STATUS_QUERY_URL)) { + public CyHttpResponse respond(CyHttpRequest request, Matcher matchedURI) { + final Map<String,String> responseData = jsonRespond(request, matchedURI); + JSONObject jsonObject = new JSONObject(responseData); + return responseFactory.createHttpResponse(jsonObject.toString(), "application/json"); + } + } + + public class StatusResponder extends JsonResponder { + final Pattern pattern = Pattern.compile("^/status/(.*)$"); + + public Pattern getURIPattern() { + return pattern; + } + + protected Map<String,String> jsonRespond(CyHttpRequest request, Matcher matchedURI) { + Map<String, String> responseData = new HashMap<String, String>(); + String appName = matchedURI.group(1); + if (appName != null && appName.length() != 0) { + String status = "not-found"; + String version = "not-found"; + + // Searches web apps first. If not found, searches other apps using manifest name field. + for (App app : appManager.getApps()) { + if (app.getAppName().equalsIgnoreCase(appName)) { + if (app.getStatus() != null) { + status = app.getStatus().toString().toLowerCase(); + } + + if (app.getVersion() != null) { + version = app.getVersion(); + } + } + } + + responseData.put("request_name", appName); // web unique identifier + responseData.put("status", status); + responseData.put("version", version); + } + return responseData; + } + } + + public class InstallResponder extends JsonResponder { + final Pattern pattern = Pattern.compile("^/install/(.+)/(.+)$"); + + public Pattern getURIPattern() { + return pattern; + } + + protected Map<String,String> jsonRespond(CyHttpRequest request, Matcher matchedURI) { + Map<String, String> responseData = new HashMap<String, String>(); + String appName = matchedURI.group(1); + String version = matchedURI.group(2); - String appName = parsed.get(STATUS_QUERY_APP_NAME); - boolean appFound = false; - - if (appName != null) { - - String status = "not-found"; - String version = "not-found"; - - // Searches web apps first. If not found, searches other apps using manifest name field. - for (App app : appManager.getApps()) { - if (app.getAppName().equalsIgnoreCase(appName)) { - if (app.getStatus() != null) { - status = app.getStatus().toString().toLowerCase(); - } - - if (app.getVersion() != null) { - version = app.getVersion(); - } - } - } - - responseData.put("request_name", appName); // web unique identifier - responseData.put("status", status); - responseData.put("version", version); - } - // Are we being told to install an app remotely? - } else if (urlRequestPrefix.equalsIgnoreCase(INSTALL_QUERY_URL)) { - - String appName = parsed.get(INSTALL_QUERY_APP_NAME); - String version = parsed.get(INSTALL_QUERY_APP_VERSION); - - if (appName != null && version != null) { + if (appName != null && appName.length() != 0 && version != null && version.length() != 0) { // Use the WebQuerier to obtain the app from the app store using the app name and version - responseBody = "Will obtain \"" + appName + "\", version " + version; + //responseBody = "Will obtain \"" + appName + "\", version " + version; String installStatus = "app-not-found"; String installError = ""; @@ -140,78 +146,8 @@ responseData.put("install_status", installStatus); responseData.put("install_error", installError); - - } else if (version == null) { - - responseData.put("install_status", "version-not-found"); - responseData.put("install_error", "The query to the app manager did not specify a desired app version."); - } - } - - JSONObject jsonObject = new JSONObject(responseData); - - responseBody = jsonObject.toString(); - responseBody += "\n"; - LocalHttpServer.Response response = new LocalHttpServer.Response(responseBody, "application/json"); - - return response; - } - - public AppGetResponder(AppManager appManager) { - this.appManager = appManager; - - } - - /** - * Parses the parameters from an URL encoded in the application/x-www-form-urlencoded form, - * which is the default form. This method uses a simple parsing method, only scanning text after the last '?' symbol - * and splitting with the '=' symbol. A more comprehensive (and possibly securer) parser can be found in the URLEncodedUtils - * class of the Apache HttpClient library. - * - * @param url The encoded URL in String form - * @return A map containing the encoded keys as keys and the corresponding encoded values as values. - */ - private Map<String, String> parseEncodedUrl(String url) { - Map<String, String> parameters = new HashMap<String, String>(); - - int lastIndex = url.lastIndexOf("?"); - - if (lastIndex != -1) { - String paramSubstring = url.substring(lastIndex + 1); - - String[] splitParameters = paramSubstring.split("&"); - - - String key, value; - int equalSignIndex; - for (int i = 0; i < splitParameters.length; i++) { - equalSignIndex = splitParameters[i].indexOf('='); - - if (equalSignIndex != -1) { - key = splitParameters[i].substring(0, equalSignIndex); - value = splitParameters[i].substring(equalSignIndex + 1); - - parameters.put(key, value); - } - } - } - - return parameters; - } - - /** - * Returns the request prefix for a URL encoded in the application/x-www-form-urlencoded form, ie. - * returns "installed" for "http://127.0.0.1:2608/installed?appname=3d&version=1.0.0" - */ - private String getUrlRequestPrefix(String url) { - - // Use the last occurrence of the question mark - int lastIndex = url.lastIndexOf("?"); - - if (lastIndex == -1) { - return url.substring(1); - } else { - return url.substring(url.indexOf("/") + 1, lastIndex); - } - } + } + return responseData; + } + } } Modified: 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 2012-08-21 22:59:42 UTC (rev 30245) +++ core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpAfterResponse.java 2012-08-22 00:20:19 UTC (rev 30246) @@ -1,7 +1,8 @@ package org.cytoscape.app.internal.net.server; /** - * Intercepts a {@link CyHttpResponse} before it is sent out to the client. + * Intercepts a {@link CyHttpResponse} after it has been made by + * a {@link CyHttpResponder} but before it is sent out to the client. * This is typically used to add headers to the response. */ public interface CyHttpAfterResponse Modified: 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 2012-08-21 22:59:42 UTC (rev 30245) +++ core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpRequest.java 2012-08-22 00:20:19 UTC (rev 30246) @@ -3,7 +3,12 @@ import java.util.Map; /** - * An Http request issued by the client. + * An http request issued by the client. + * This interface assumes that the client is + * sending text. This assumption greatly simplifies + * this interface. In reality, the client could + * be sending binary data, which is not properly + * handled by this interface. */ public interface CyHttpRequest { Modified: 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 2012-08-21 22:59:42 UTC (rev 30245) +++ core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpRequestImpl.java 2012-08-22 00:20:19 UTC (rev 30246) @@ -12,6 +12,10 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; +/** + * This is used internally to translate HttpCore's {@link HttpRequest} + * into a {@link CyHttpRequest}. + */ public class CyHttpRequestImpl implements CyHttpRequest { final String uri; Modified: 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 2012-08-21 22:59:42 UTC (rev 30245) +++ core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpResponse.java 2012-08-22 00:20:19 UTC (rev 30246) @@ -3,7 +3,12 @@ import java.util.Map; /** - * The response the server sends back to the client. + * The http response the server sends back to the client. + * This interface assumes that all responses are in text. + * This makes it easier to work with responses. However, + * sending binary data back to the client is not possible + * unless the data is encoded into a string that the client + * understands. */ public interface CyHttpResponse { Modified: 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 2012-08-21 22:59:42 UTC (rev 30245) +++ core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpResponseFactory.java 2012-08-22 00:20:19 UTC (rev 30246) @@ -1,14 +1,35 @@ package org.cytoscape.app.internal.net.server; +/** + * Creates response objects. Implementations of {@link CyHttpResponder}, + * {@link CyHttpBeforeResponse}, and {@link CyHttpAfterResponse} + * will use an this interface to create response objects + * as its return values. + */ public interface CyHttpResponseFactory { /** * Create a response with no content. + * @param statusCode the http response status code; use a + * static field in {@link java.net.HttpURLConnection} to fill this in. */ CyHttpResponse createHttpResponse(int statusCode); /** * Create a response with content. + * @param statusCode the http response status code; use a + * static field in {@link java.net.HttpURLConnection} to fill this in. + * @param content the text content to send back to the client + * @param contentType the MIME type of {@code content}; examples of valid + * MIME types: {@code text/html}, {@code application/json}, {@code application/xml}. */ CyHttpResponse createHttpResponse(int statusCode, String content, String contentType); + + /** + * Create a response with content with 200 OK. + * @param content the text content to send back to the client + * @param contentType the MIME type of {@code content}; examples of valid + * MIME types: {@code text/html}, {@code application/json}, {@code application/xml}. + */ + CyHttpResponse createHttpResponse(String content, String contentType); } Modified: 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 2012-08-21 22:59:42 UTC (rev 30245) +++ core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpResponseFactoryImpl.java 2012-08-22 00:20:19 UTC (rev 30246) @@ -1,4 +1,5 @@ package org.cytoscape.app.internal.net.server; +import org.apache.http.HttpStatus; import java.util.Map; import java.util.HashMap; @@ -10,6 +11,11 @@ return createHttpResponse(statusCode, null, null); } + public CyHttpResponse createHttpResponse(final String content, final String contentType) + { + return createHttpResponse(HttpStatus.SC_OK, content, contentType); + } + public CyHttpResponse createHttpResponse(final int statusCode, final String content, final String contentType) { return new CyHttpResponse() Modified: 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 2012-08-21 22:59:42 UTC (rev 30245) +++ core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpd.java 2012-08-22 00:20:19 UTC (rev 30246) @@ -1,5 +1,7 @@ package org.cytoscape.app.internal.net.server; +import java.util.Collection; + /** * The http server. * @@ -12,7 +14,7 @@ * 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. + * When this happens, no {@code CyHttpResponder}s are invoked. * </li> * <li> * If all of the {@code CyHttpBeforeResponse}s return null, @@ -28,7 +30,7 @@ * process the response. * </li> * <li> - * The {@code CyHttpResponder} is sent to the client. + * The {@code CyHttpResponse} is sent to the client. * </li> * </ol> * @@ -64,13 +66,13 @@ void addResponder(CyHttpResponder responder); void removeResponder(CyHttpResponder responder); - Iterable<CyHttpResponder> getResponders(); + Collection<CyHttpResponder> getResponders(); void addBeforeResponse(CyHttpBeforeResponse beforeResponse); void removeBeforeResponse(CyHttpBeforeResponse beforeResponse); - Iterable<CyHttpBeforeResponse> getBeforeResponses(); + Collection<CyHttpBeforeResponse> getBeforeResponses(); void addAfterResponse(CyHttpAfterResponse afterResponse); void removeAfterResponse(CyHttpAfterResponse afterResponse); - Iterable<CyHttpAfterResponse> getAfterResponses(); + Collection<CyHttpAfterResponse> getAfterResponses(); } Modified: 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 2012-08-21 22:59:42 UTC (rev 30245) +++ core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/CyHttpdFactoryImpl.java 2012-08-22 00:20:19 UTC (rev 30246) @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Map; import java.util.HashMap; +import java.util.Collection; import java.util.regex.Matcher; import java.util.concurrent.ExecutorService; @@ -141,7 +142,7 @@ responders.remove(responder); } - public Iterable<CyHttpResponder> getResponders() + public Collection<CyHttpResponder> getResponders() { return responders; } @@ -156,7 +157,7 @@ beforeResponses.remove(beforeResponse); } - public Iterable<CyHttpBeforeResponse> getBeforeResponses() + public Collection<CyHttpBeforeResponse> getBeforeResponses() { return beforeResponses; } @@ -171,7 +172,7 @@ afterResponses.remove(afterResponse); } - public Iterable<CyHttpAfterResponse> getAfterResponses() + public Collection<CyHttpAfterResponse> getAfterResponses() { return afterResponses; } Deleted: core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/LocalHttpServer.java =================================================================== --- core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/LocalHttpServer.java 2012-08-21 22:59:42 UTC (rev 30245) +++ core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/LocalHttpServer.java 2012-08-22 00:20:19 UTC (rev 30246) @@ -1,405 +0,0 @@ -package org.cytoscape.app.internal.net.server; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; - -import java.util.Map; -import java.util.List; -import java.util.ArrayList; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.io.StringWriter; -import java.io.PrintWriter; - -import java.net.ServerSocket; -import java.net.InetAddress; -import java.net.Socket; -import java.net.URLDecoder; -import java.net.SocketTimeoutException; - -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.HttpEntity; -import org.apache.http.HttpEntityEnclosingRequest; -import org.apache.http.entity.StringEntity; -import org.apache.http.util.EntityUtils; -import org.apache.http.HttpRequest; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.cytoscape.app.internal.util.DebugHelper; - -/** - * Creates a server socket and serves http connections from localhost clients only. - */ -public class LocalHttpServer implements Runnable { - - // The list of addresses allowed to query the server - public static final String[] ALLOWED_ORIGINS = new String[]{ - "http://apps3.nrnb.org", - "http://apps.cytoscape.org" - }; - - public static class Response { - final String body; - final String contentType; - - public Response(final String body, final String contentType) { - if (body == null) { - throw new IllegalArgumentException("body == null"); - } - - if (contentType == null) { - throw new IllegalArgumentException("contentType == null"); - } - - this.body = body; - this.contentType = contentType; - } - - public String getBody() { - return body; - } - - public String getContentType() { - return contentType; - } - } - - public interface GetResponder { - public boolean canRespondTo(String url) throws Exception; - public Response respond(String url) throws Exception; - } - - public interface PostResponder { - public boolean canRespondTo(String url) throws Exception; - public Response respond(String url, String body) throws Exception; - } - - private static final Logger logger = LoggerFactory.getLogger(LocalHttpServer.class); - - final List<GetResponder> getResponders = new ArrayList<GetResponder>(); - final List<PostResponder> postResponders = new ArrayList<PostResponder>(); - - final ServerSocketFactory serverSocketFactory; - final Executor connectionHandlerExecutor; - - final HttpParams params; - final HttpService service; - - /** - * Initializes local http server but does not start serving incoming connections. - * @param port The port on which the http server should receive connections--should be greater - * than 1024 if the server is being executed by a non-root user. - * @param connectionHandlerExecutor executes a connection handler when an incoming socket connection is received - */ - public LocalHttpServer(final ServerSocketFactory serverSocketFactory, final Executor connectionHandlerExecutor) { - if (serverSocketFactory == null) { - throw new IllegalArgumentException("serverSocketFactory == null"); - } - - this.serverSocketFactory = serverSocketFactory; - - if (connectionHandlerExecutor == null) { - throw new IllegalArgumentException("connectionHandlerExecutor == null"); - } - - this.connectionHandlerExecutor = connectionHandlerExecutor; - - // 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 addGetResponder(final GetResponder getResponder) { - getResponders.add(getResponder); - } - - public void addPostResponder(final PostResponder postResponder) { - postResponders.add(postResponder); - } - - 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 this thread is flagged as interrupted - while (!Thread.interrupted()) { // TODO: **interrupted is deprecated? - - // 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()); - //System.out.println("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); - connectionHandlerExecutor.execute(connectionHandler); - } - - logger.info("Server socket stopped"); - } - - /** - * Handles an incoming http connection when it is received by <code>ConnectionsReceiver</code>. - */ - 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 (!Thread.interrupted() && 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) { - - } - } - } - } - - class RequestHandlerDispatcher implements HttpRequestHandler { - public void handle(final HttpRequest request, final HttpResponse httpResponse, final HttpContext context) throws HttpException { - - // decode the uri - - final String uri = request.getRequestLine().getUri(); - String url = null; - try { - url = URLDecoder.decode(uri, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new HttpException("unable to parse uri: " + uri, e); - } - - // determine http method - - final String method = request.getRequestLine().getMethod().toLowerCase(); - DebugHelper.print("Request received. Method: " + method); - - // System.out.println("Request recvd: " + requestLine.toString()); - - // Obtain origin - Header originHeader = request.getFirstHeader("Origin"); - - String originValue = null; - if (originHeader != null) { - originValue = originHeader.getValue(); - } - - boolean originFound = false; - - for (int i = 0; i < ALLOWED_ORIGINS.length; i++) { - String allowedOrigin = ALLOWED_ORIGINS[i]; - - if (originValue != null && allowedOrigin.equals(originValue)) { - originFound = true; - } - } - - // System.out.println("Origin found? " + originFound); - - if (!originFound) { - if (originValue != null) { - setHttpResponseToError(httpResponse, HttpStatus.SC_FORBIDDEN, "Access denied", - "The app manager does not recognize the orgin " + originValue); - } else { - setHttpResponseToError(httpResponse, HttpStatus.SC_FORBIDDEN, "Access denied", - "Origin not found."); - } - return; - } - - if (method.equals("options")) { - // If the origin is on the allowed list, echo it back as the allowed origin - httpResponse.addHeader("Access-Control-Allow-Origin", originValue); - httpResponse.addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); - httpResponse.addHeader("Access-Control-Max-Age", "1"); - httpResponse.addHeader("Access-Control-Allow-Headers", "origin, x-csrftoken, accept"); - return; - } - - // loop thru responders and see if any of them produce a response - Response response = null; - if (method.equals("get") || method.equals("head")) { - - DebugHelper.print("Number of GET responders: " + getResponders.size()); - for (final GetResponder getResponder : getResponders) { - - // catch any exceptions emitted by the responder so our server thread doesn't prematurely terminate - // and can send a coherent message back to the user - try { - if (getResponder.canRespondTo(url)) { - DebugHelper.print("Responder able to respond. Responding.."); - response = getResponder.respond(url); - break; - } - } catch (Exception e) { - setHttpResponseToInternalError(httpResponse, e); - return; - } - } - } else if (method.equals("post")) { - - // obtain the body of the post request - - String postBody = null; - if (request instanceof HttpEntityEnclosingRequest) { - final HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity(); - try { - postBody = EntityUtils.toString(entity); - } catch (IOException e) { - - } - } - - DebugHelper.print("Number of POST responders: " + postResponders.size()); - for (final PostResponder postResponder : postResponders) { - - // catch any exceptions emitted by the responder so our server thread doesn't prematurely terminate - // and can send a coherent message back to the user - - try { - if (postResponder.canRespondTo(uri)) { - DebugHelper.print("Found responder. Responding.."); - response = postResponder.respond(url, postBody); - break; - } - } catch (Exception e) { - DebugHelper.print("Found exception: " + e); - setHttpResponseToInternalError(httpResponse, e); - return; - } - } - } else { - // none of the http methods are valid, so issue an error to the client - throw new MethodNotSupportedException(String.format("\"%s\" method not supported", method)); - } - - if (response == null) { - setHttpResponseToError(httpResponse, HttpStatus.SC_NOT_FOUND, "Resource not found", null); - } else { - final String body = response.getBody(); - if (body == null) { - setHttpResponseToInternalError(httpResponse, "Responder \"%s\" returned null"); - } else { - setHttpResponse(httpResponse, HttpStatus.SC_OK, body, response.getContentType()); - httpResponse.addHeader("Access-Control-Allow-Origin", originValue != null? originValue : "*"); - } - } - } - } - - private static void setHttpResponse(final HttpResponse httpResponse, final int code, final String msg, final String contentType) throws HttpException { - httpResponse.setStatusCode(code); - try { - httpResponse.setEntity(new StringEntity(msg, contentType, "UTF-8")); - - } catch (UnsupportedEncodingException e) { - throw new HttpException("failed to issue output to client", e); - } - } - - private static void setHttpResponseToError(final HttpResponse httpResponse, final int code, final String title, final String msg) throws HttpException { - setHttpResponse(httpResponse, code, (msg == null ? - String.format("<html><body><h1>%s</h1></body></html>", title) : - String.format("<html><body><h1>%s</h1><pre>%s</pre></body></html>", title, msg)), "text/html"); - } - - private void setHttpResponseToInternalError(final HttpResponse httpResponse, final String msg) throws HttpException { - logger.warn("Failed to respond to client: {}", msg); - setHttpResponseToError(httpResponse, HttpStatus.SC_INTERNAL_SERVER_ERROR, "Internal Error", msg); - } - - private void setHttpResponseToInternalError(final HttpResponse httpResponse, final Throwable throwable) throws HttpException { - logger.warn("Failed to respond to client", throwable); - final StringWriter stringWriter = new StringWriter(); - throwable.printStackTrace(new PrintWriter(stringWriter)); - final String stacktrace = stringWriter.toString(); - setHttpResponseToError(httpResponse, HttpStatus.SC_INTERNAL_SERVER_ERROR, "Internal Error", stacktrace); - } -} Modified: 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 2012-08-21 22:59:42 UTC (rev 30245) +++ core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/LocalhostServerSocketFactory.java 2012-08-22 00:20:19 UTC (rev 30246) @@ -5,7 +5,7 @@ import java.net.InetAddress; /** - * An implementation of {@link ServerSocketFactory} that only accepts connections + * Creates {@link ServerSocket}s that only accepts connections * from localhost. */ public class LocalhostServerSocketFactory implements ServerSocketFactory Modified: 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 2012-08-21 22:59:42 UTC (rev 30245) +++ core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/OriginOptionsBeforeResponse.java 2012-08-22 00:20:19 UTC (rev 30246) @@ -6,7 +6,7 @@ * 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 + * return 404. But that's okay. If the client subsequently requests {@code GET} on the * same, non-existant URL, it will get the 404. */ public class OriginOptionsBeforeResponse implements CyHttpBeforeResponse Modified: 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 2012-08-21 22:59:42 UTC (rev 30245) +++ core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/ScreenOriginsBeforeResponse.java 2012-08-22 00:20:19 UTC (rev 30246) @@ -2,13 +2,17 @@ import org.apache.http.HttpStatus; +/** + * Reject requests if the client does not send an {@code Origin} header + * and if its {@code Origin} is not allowed. + */ public class ScreenOriginsBeforeResponse implements CyHttpBeforeResponse { static final CyHttpResponseFactory responseFactory = new CyHttpResponseFactoryImpl(); final String[] allowedOrigins; /** - * @param allowedOrigins + * @param allowedOrigins A list of strings containing the full URLs of allowed origins. */ public ScreenOriginsBeforeResponse(final String... allowedOrigins) { Modified: core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/ServerSocketFactory.java =================================================================== --- core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/ServerSocketFactory.java 2012-08-21 22:59:42 UTC (rev 30245) +++ core3/impl/trunk/app-impl/src/main/java/org/cytoscape/app/internal/net/server/ServerSocketFactory.java 2012-08-22 00:20:19 UTC (rev 30246) @@ -5,9 +5,9 @@ /** * Used by the http server to create {@link ServerSocket}s. - * You can control the creation of a {@code ServerSocket} that the - * http server will use to serve clients. - * By creating {@code ServerSocket}s yourself, you can control the + * By implementing this interface, you can control the creation + * of a {@code ServerSocket} that the http server will use to serve clients. + * This means that you can control the * port the http server listens to and the IP addresses the http server accepts. * This is not to be confused with {@link javax.net.ServerSocketFactory}, which * is just a list of convenience methods for creating {@code ServerSocket}s. Modified: 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 2012-08-21 22:59:42 UTC (rev 30245) +++ core3/impl/trunk/app-impl/src/test/java/org/cytoscape/app/internal/net/server/CyHttpdImplTest.java 2012-08-22 00:20:19 UTC (rev 30246) @@ -24,7 +24,7 @@ @Test public void testHttpd() throws Exception { - final CyHttpd httpd = (new CyHttpdFactoryImpl()).createHttpd(new LocalhostServerSocketFactory(2607)); // why 2607? 'cuz it's my birfday + final CyHttpd httpd = (new CyHttpdFactoryImpl()).createHttpd(new LocalhostServerSocketFactory(2608)); final CyHttpResponseFactory responseFactory = new CyHttpResponseFactoryImpl(); httpd.addResponder(new CyHttpResponder() { @@ -77,26 +77,28 @@ httpd.start(); assertTrue(httpd.isRunning()); + final String url = "http://localhost:2608/"; + // test if normal response works - HttpURLConnection connection = connectToURL("http://localhost:2607/testA", "GET"); + HttpURLConnection connection = connectToURL(url + "testA", "GET"); assertTrue(connection.getResponseCode() == HttpURLConnection.HTTP_OK); assertEquals(readConnection(connection), "testA response ok"); - connection = connectToURL("http://localhost:2607/testB", "GET"); + connection = connectToURL(url + "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"); + connection = connectToURL(url + "testX", "GET"); assertTrue(connection.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND); // test if before intercept works - connection = connectToURL("http://localhost:2607/testA", "OPTIONS"); + connection = connectToURL(url + "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"); + connection = connectToURL(url + "testA", "GET"); assertTrue(connection.getResponseCode() == HttpURLConnection.HTTP_OK); assertEquals(connection.getHeaderField("SomeRandomHeader"), "WowInterceptWorks"); -- 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.
