JAL-3851 Allow specifying a sequence index in highlight
[jalview.git] / src / jalview / rest / RestHandler.java
index 39fe26a..c967d3e 100644 (file)
@@ -22,16 +22,22 @@ package jalview.rest;
 
 import java.io.BufferedReader;
 import java.io.IOException;
+import java.io.InputStreamReader;
 import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
 import java.net.BindException;
 import java.util.HashMap;
 import java.util.Map;
 
+import javax.servlet.ServletInputStream;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import jalview.bin.Cache;
+import org.eclipse.jetty.server.handler.ContextHandler;
+
+import jalview.bin.Console;
 import jalview.httpserver.AbstractRequestHandler;
+import jalview.httpserver.HttpServer;
 
 /**
  * A simple handler to process (or delegate) HTTP requests on /jalview/rest
@@ -41,13 +47,19 @@ public class RestHandler extends AbstractRequestHandler
 
   public enum Status
   {
-    STARTED, IN_PROGRESS, FINISHED, ERROR
+    STARTED, IN_PROGRESS, FINISHED, ERROR, NOT_RUN
   }
 
   public interface EndpointI
   {
+    public String getPath();
+
     public String getName();
 
+    public String getParameters();
+
+    public String getDescription();
+
     public void processEndpoint(HttpServletRequest request,
             HttpServletResponse response);
 
@@ -62,13 +74,32 @@ public class RestHandler extends AbstractRequestHandler
   private boolean init = false;
 
   // map of method names and method handlers
-  private Map<String, EndpointI> endpoints = null;
+  private Map<String, AbstractEndpoint> endpoints = null;
 
-  protected Map<String, EndpointI> getEndpoints()
+  protected Map<String, AbstractEndpoint> getEndpoints()
   {
     return endpoints;
   }
 
+  protected AbstractEndpoint getNewEndpoint(String name)
+  {
+    if (getEndpoints() == null)
+    {
+      return null;
+    }
+    try
+    {
+      return getEndpoints().get(name).getClass()
+              .getDeclaredConstructor(API.class).newInstance(this);
+    } catch (InstantiationException | IllegalAccessException
+            | IllegalArgumentException | InvocationTargetException
+            | NoSuchMethodException | SecurityException e)
+    {
+      Console.debug("Could not instantiate new endpoint '" + name + "'", e);
+    }
+    return null;
+  }
+
   /**
    * Singleton instance of this class
    */
@@ -121,43 +152,63 @@ public class RestHandler extends AbstractRequestHandler
      * Currently just echoes the request; add helper classes as required to
      * process requests
      */
-    System.out.println(request.toString());
-    if (endpoints == null)
+    // This "pointless" call to request.getInputStream() seems to preserve the
+    // InputStream for use later in getRequestBody.
+    request.getInputStream();
+
+    String remoteAddr = request.getRemoteAddr();
+    if (!("127.0.0.1".equals(remoteAddr) || "localhost".equals(remoteAddr)))
+    {
+      returnError(request, response, "Not authorised: " + remoteAddr,
+              HttpServletResponse.SC_UNAUTHORIZED);
+      return;
+    }
+    if (getEndpoints() == null)
     {
       final String queryString = request.getQueryString();
       final String reply = "REST not yet implemented; received "
               + request.getMethod() + ": " + request.getRequestURL()
               + (queryString == null ? "" : "?" + queryString);
-      System.out.println(reply);
+      Console.error(reply);
 
       response.setHeader("Cache-Control", "no-cache/no-store");
       response.setHeader("Content-type", "text/plain");
       final PrintWriter writer = response.getWriter();
       writer.write(reply);
-      writer.close();
       return;
     }
 
     String endpointName = getRequestedEndpointName(request);
+    Console.debug(endpointName);
 
-    if (!endpoints.containsKey(endpointName)
-            || endpoints.get(endpointName) == null)
+    if (!getEndpoints().containsKey(endpointName)
+            || getEndpoints().get(endpointName) == null)
     {
+      Console.debug(
+              "RestHandler did not find endpoint '" + endpointName + "'");
 
       response.setHeader("Cache-Control", "no-cache/no-store");
       response.setHeader("Content-type", "text/plain");
-      response.setStatus(400);
       PrintWriter writer = response.getWriter();
       writer.write(missingEndpointMessage == null
               ? "REST endpoint '" + endpointName + "' not defined"
               : missingEndpointMessage);
-      writer.close();
+      writer.write("\n");
+      writer.write("Available endpoints are:\n");
+      ContextHandler ch = HttpServer.getInstance().getContextHandler(this);
+      String base = HttpServer.getInstance().getUri().toString();
+      String contextPath = ch == null ? "" : ch.getContextPath();
+      for (String key : getEndpoints().keySet())
+      {
+        writer.write(base + contextPath + "/" + key + "\n");
+      }
+      response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
       return;
     }
 
     response.setHeader("Cache-Control", "no-cache/no-store");
     response.setHeader("Content-type", "text/plain");
-    EndpointI ep = endpoints.get(endpointName);
+    EndpointI ep = getNewEndpoint(endpointName);
     ep.processEndpoint(request, response);
 
     return;
@@ -189,15 +240,14 @@ public class RestHandler extends AbstractRequestHandler
     // e.g. registerHandler and addEndpoints
   }
 
-  protected boolean addEndpoint(EndpointI ep)
+  protected void addEndpoint(AbstractEndpoint ep)
   {
-    if (endpoints == null)
+    if (getEndpoints() == null)
     {
       endpoints = new HashMap<>();
     }
-    Endpoint e = (Endpoint) ep;
-    endpoints.put(ep.getName(), ep);
-    return true;
+    endpoints.put(ep.getPath(), ep);
+    Console.debug("REST API, added endpoint '" + ep.getPath() + "'");
   }
 
   protected String getRequestedEndpointName(HttpServletRequest request)
@@ -208,29 +258,28 @@ public class RestHandler extends AbstractRequestHandler
             : pathInfo.substring(1);
   }
 
-  protected String[] getEndpointPathParameters(HttpServletRequest request)
+  protected void returnError(HttpServletRequest request,
+          HttpServletResponse response, String message)
   {
-    String pathInfo = request.getPathInfo();
-    int slashpos = pathInfo.indexOf('/', 1);
-    return slashpos < 1 ? null
-            : pathInfo.substring(slashpos + 1).split("/");
+    returnError(request, response, message,
+            HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
   }
 
   protected void returnError(HttpServletRequest request,
-          HttpServletResponse response, String message)
+          HttpServletResponse response, String message, int statusCode)
   {
-    response.setStatus(500); // set this to something better
+    response.setStatus(statusCode);
     String endpointName = getRequestedEndpointName(request);
-    Cache.error(this.MY_NAME + " error: endpoint " + endpointName
+    Console.error(getName() + " error: endpoint " + endpointName
             + " failed: '" + message + "'");
     try
     {
       PrintWriter writer = response.getWriter();
       writer.write("Endpoint " + endpointName + ": " + message);
-      writer.close();
     } catch (IOException e)
     {
-      Cache.debug(e);
+      Console.debug("Could not write to REST response for endpoint "
+              + endpointName, e);
     }
   }
 
@@ -246,27 +295,46 @@ public class RestHandler extends AbstractRequestHandler
         writer.write("status=" + status.toString() + "\n");
     } catch (IOException e)
     {
-      Cache.debug(e);
+      Console.debug("Could not write status to REST response for id:" + id,
+              e);
     }
   }
 
-  protected String getRequestBody(HttpServletRequest request)
-          throws IOException
+  protected String getRequestBody(HttpServletRequest request,
+          HttpServletResponse response) throws IOException
   {
     StringBuilder sb = new StringBuilder();
-    BufferedReader reader = request.getReader();
+    BufferedReader reader = null;
+    Console.debug("REQUEST=" + request.toString());
+    Console.debug("REQUEST.Content-Length=" + request.getContentLength());
     try
     {
-      String line;
-      while ((line = reader.readLine()) != null)
+      reader = request.getReader();
+      Console.debug("Using getReader()");
+    } catch (IllegalStateException e)
+    {
+      ServletInputStream is = request.getInputStream();
+      reader = new BufferedReader(new InputStreamReader(is));
+      Console.debug("Using getInputStream()");
+    }
+    if (reader != null)
+    {
+      try
       {
-        sb.append(line).append('\n');
+        String line;
+        while ((line = reader.readLine()) != null)
+        {
+          sb.append(line).append('\n');
+        }
+      } finally
+      {
+        reader.close();
       }
-    } finally
+    }
+    else
     {
-      reader.close();
+      returnError(request, response, "Error reading body of HTTP request");
     }
     return sb.toString();
   }
-
 }
\ No newline at end of file