--- /dev/null
+// RelocateFrame.java\r
+// $Id: RelocateFrame.java,v 1.2 2010/06/15 17:52:52 smhuang Exp $\r
+// (c) COPYRIGHT MIT and INRIA, 1996.\r
+// Please first read the full copyright statement in file COPYRIGHT.html\r
+\r
+package org.w3c.jigsaw.frames;\r
+\r
+import java.io.InputStream;\r
+\r
+import java.net.MalformedURLException;\r
+import java.net.URL;\r
+\r
+import org.w3c.tools.resources.Attribute;\r
+import org.w3c.tools.resources.AttributeHolder;\r
+import org.w3c.tools.resources.AttributeRegistry;\r
+import org.w3c.tools.resources.BooleanAttribute;\r
+import org.w3c.tools.resources.LookupResult;\r
+import org.w3c.tools.resources.LookupState;\r
+import org.w3c.tools.resources.ProtocolException;\r
+import org.w3c.tools.resources.Resource;\r
+import org.w3c.tools.resources.ResourceException;\r
+import org.w3c.tools.resources.ResourceFrame;\r
+import org.w3c.tools.resources.ServerInterface;\r
+import org.w3c.tools.resources.StringArrayAttribute;\r
+import org.w3c.tools.resources.StringAttribute;\r
+\r
+import org.w3c.jigsaw.http.HTTPException;\r
+import org.w3c.jigsaw.http.Reply;\r
+import org.w3c.jigsaw.http.Request;\r
+import org.w3c.jigsaw.http.httpd;\r
+\r
+import org.w3c.jigsaw.html.HtmlGenerator;\r
+\r
+import org.w3c.www.http.HTTP;\r
+import org.w3c.www.http.HttpMessage;\r
+import org.w3c.www.http.HttpReplyMessage;\r
+import org.w3c.www.http.HttpRequestMessage;\r
+\r
+import org.w3c.tools.resources.ProtocolException;\r
+import org.w3c.tools.resources.ResourceException;\r
+\r
+/**\r
+ * Emit a HTTP redirect.\r
+ */\r
+public class RelocateFrame extends HTTPFrame {\r
+\r
+ /**\r
+ * Name of the state to hold the PATH_INFO in the request.\r
+ */\r
+ public final static \r
+ String PATH_INFO = \r
+ "org.w3c.jigsaw.resources.RelocateResource.PathInfo";\r
+\r
+ /**\r
+ * Attribute index - The relocation location.\r
+ */\r
+ protected static int ATTR_LOCATION = -1 ;\r
+ /**\r
+ * Attribute index - Should we also handle extra path infos ?\r
+ */\r
+ protected static int ATTR_HANDLE_PATHINFO = -1;\r
+ /**\r
+ * Attribute index - Is the relocation permanent?\r
+ */\r
+ protected static int ATTR_PERMANENT_REDIRECT = -1;\r
+ /**\r
+ * Attribute index - SHould we use the ambiguous 302?\r
+ */\r
+ protected static int ATTR_USE_302 = -1;\r
+ /**\r
+ * Attribute index - The methods affected by this frame\r
+ */\r
+ protected static int ATTR_METHODS = -1 ;\r
+\r
+ static {\r
+ Attribute a = null ;\r
+ Class c = null ;\r
+\r
+ try {\r
+ c = Class.forName("org.w3c.jigsaw.frames.RelocateFrame");\r
+ //Added by Jeff Huang\r
+ //TODO: FIXIT\r
+ } catch (Exception ex) {\r
+ ex.printStackTrace() ;\r
+ System.exit(1) ;\r
+ }\r
+ // The location attribute\r
+ a = new StringAttribute("location"\r
+ , null\r
+ , Attribute.EDITABLE|Attribute.MANDATORY) ;\r
+ ATTR_LOCATION = AttributeRegistry.registerAttribute(c, a) ;\r
+ // The handle path info attribute\r
+ a = new BooleanAttribute("handle-pathinfo"\r
+ , Boolean.TRUE\r
+ , Attribute.EDITABLE);\r
+ ATTR_HANDLE_PATHINFO = AttributeRegistry.registerAttribute(c, a);\r
+ // the permanent redirection attribute\r
+ a = new BooleanAttribute("permanent-redirect"\r
+ , Boolean.FALSE\r
+ , Attribute.EDITABLE);\r
+ ATTR_PERMANENT_REDIRECT = AttributeRegistry.registerAttribute(c, a);\r
+ // should we use the ambiguous 302 response code?\r
+ a = new BooleanAttribute("use-usual-response"\r
+ , Boolean.TRUE\r
+ , Attribute.EDITABLE);\r
+ ATTR_USE_302 = AttributeRegistry.registerAttribute(c, a);\r
+ // The affected methods\r
+ a = new StringArrayAttribute("methods"\r
+ , null\r
+ , Attribute.EDITABLE) ;\r
+ ATTR_METHODS = AttributeRegistry.registerAttribute(c, a) ;\r
+ }\r
+\r
+ /**\r
+ * Get the location for the relocation\r
+ * @return a string, containing the relative path or absolute\r
+ */\r
+ public String getLocation() {\r
+ return (String) getValue(ATTR_LOCATION, null) ;\r
+ }\r
+\r
+ /**\r
+ * Get the list of methods affected by the redirect\r
+ * @return An array of String giving the name of the redirected methods,\r
+ * or <strong>null</strong>, in wich case <em>all</em> methods are\r
+ * to be redirected.\r
+ */\r
+ public String[] getMethods() {\r
+ return (String[]) getValue(ATTR_METHODS, null) ;\r
+ }\r
+\r
+ /**\r
+ * Get the path info value\r
+ * @return a boolean\r
+ */\r
+ public boolean checkHandlePathInfo() {\r
+ return getBoolean(ATTR_HANDLE_PATHINFO, true);\r
+ }\r
+\r
+ /**\r
+ * Get the permanent redirect flag\r
+ * @return a boolean\r
+ */\r
+ public boolean checkPermanentRedirect() {\r
+ return getBoolean(ATTR_PERMANENT_REDIRECT, false);\r
+ }\r
+\r
+ /**\r
+ * Get the "use ambigous 302 response code" flag\r
+ * @return a boolean\r
+ */\r
+ public boolean checkUse302() {\r
+ return getBoolean(ATTR_USE_302, true);\r
+ } \r
+\r
+ /**\r
+ * Lookup the target resource (dispath to more specific lookup methods).\r
+ * @param ls The current lookup state\r
+ * @param lr The result\r
+ * @return true if lookup is done.\r
+ * @exception ProtocolException If an error relative to the protocol occurs\r
+ * @see #lookupDirectory\r
+ * @see #lookupFile\r
+ * @see #lookupOther\r
+ */\r
+ protected boolean lookupResource(LookupState ls, LookupResult lr) \r
+ throws ProtocolException\r
+ {\r
+ String methods[] = getMethods();\r
+ \r
+ if (ls.hasRequest() && (methods != null)) {\r
+ Request request = (Request) ls.getRequest();\r
+ String reqmeth = request.getMethod();\r
+ boolean affected = false;\r
+ for (int i=0; i< methods.length; i++) {\r
+ if (reqmeth.equals(methods[i])) {\r
+ affected = true;\r
+ break;\r
+ }\r
+ }\r
+ if (!affected) {\r
+ return super.lookupResource(ls, lr);\r
+ }\r
+ }\r
+ // Perform our super-class lookup strategy:\r
+ if ( super.lookupOther(ls, lr) ) {\r
+ return true;\r
+ } else if ( ! checkHandlePathInfo() ) {\r
+ return false;\r
+ }\r
+ // Compute PATH INFO, store it as a piece of state in\r
+ // the request:\r
+ StringBuffer pathinfo = new StringBuffer();\r
+ while ( ls.hasMoreComponents() ) {\r
+ pathinfo.append('/');\r
+ pathinfo.append(ls.getNextComponent());\r
+ }\r
+ if (ls.hasRequest() ) {\r
+ Request request = (Request) ls.getRequest();\r
+ String reqfile = request.getURL().getFile();\r
+ if (reqfile.endsWith("/")) {\r
+ pathinfo.append('/');\r
+ }\r
+ request.setState(PATH_INFO, pathinfo.toString());\r
+ }\r
+ lr.setTarget(resource.getResourceReference());\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * build the redirect reply based on the request and the current\r
+ * configuration\r
+ * @param request The request to handle.\r
+ * @exception ProtocolException If processsing the request failed.\r
+ * @return a Reply\r
+ */\r
+ private Reply getRedirectReply(Request request)\r
+ throws ProtocolException\r
+ {\r
+ String location = getLocation() ;\r
+ if ( location == null ) {\r
+ Reply error = request.makeReply(HTTP.INTERNAL_SERVER_ERROR) ;\r
+ error.setContent("The target RelocateResource doesn't define the"\r
+ + " relocation location. The server is "\r
+ + " misconfigured.") ;\r
+ throw new HTTPException(error) ;\r
+ } else {\r
+ Reply reply = null;\r
+ URL loc = null;\r
+ if (checkUse302()) {\r
+ reply = request.makeReply(HTTP.FOUND) ;\r
+ } else {\r
+ if (checkPermanentRedirect()) {\r
+ reply = request.makeReply(HTTP.MOVED_PERMANENTLY) ;\r
+ } else {\r
+ reply = request.makeReply(HTTP.TEMPORARY_REDIRECT) ;\r
+ }\r
+ }\r
+ try {\r
+ httpd server = (httpd) getServer();\r
+ String host = request.getHost(); \r
+ if (host == null)\r
+ loc = new URL(server.getURL(), location);\r
+ else {\r
+ int ic = host.indexOf(':');\r
+ if (ic < 0 ) {\r
+ loc = new URL(new URL(server.getURL().getProtocol(),\r
+ host,server.getURL().getFile()),\r
+ location);\r
+ } else {\r
+ loc = new URL(new URL(server.getURL().getProtocol(),\r
+ host.substring(0, ic),\r
+ Integer.parseInt(\r
+ host.substring(ic+1))\r
+ ,server.getURL().getFile()),\r
+ location);\r
+ }\r
+ }\r
+ if (checkHandlePathInfo()) {\r
+ String pathinfo = (String) request.getState(PATH_INFO);\r
+ // Given the way pathinfo is computed, it starts with a /\r
+ try {\r
+ if (pathinfo != null) {\r
+ loc = new URL(loc.toExternalForm()+pathinfo);\r
+ }\r
+ } catch (MalformedURLException ex) {\r
+ resource.getServer().errlog(resource, \r
+ "This resource handle Pathinfo "+\r
+ "but the request has an invalid "+\r
+ "PATH_INFO state.");\r
+ }\r
+ if (request.hasQueryString()) {\r
+ try {\r
+ loc = new URL(loc.toExternalForm() + "?" +\r
+ request.getQueryString());\r
+ } catch (MalformedURLException ex) {\r
+ resource.getServer().errlog(resource, \r
+ "This resource handle "\r
+ +"Pathinfo but the "\r
+ +"request has an " \r
+ +"invalid "+\r
+ "PATH_INFO state.");\r
+ }\r
+ }\r
+ }\r
+ } catch (Exception ex) {\r
+ ex.printStackTrace();\r
+ }\r
+ reply.setLocation(loc);\r
+ HtmlGenerator g = new HtmlGenerator("Moved");\r
+ g.append("<P>This resources has moved, click on the link if your"\r
+ + " browser doesn't support automatic redirection<BR>"+\r
+ "<A HREF=\""+loc.toExternalForm()+"\">"+\r
+ loc.toExternalForm()+"</A>");\r
+ reply.setStream(g);\r
+ return reply ;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * The GET method, may emit a redirect \r
+ * @param request The request to handle.\r
+ * @exception ProtocolException If processsing the request failed.\r
+ * @exception ResourceException If the resource got a fatal error.\r
+ */\r
+ public Reply get(Request request)\r
+ throws ProtocolException, ResourceException\r
+ {\r
+ String methods[] = getMethods();\r
+ \r
+ if (methods != null) {\r
+ String reqmeth = request.getMethod();\r
+ boolean affected = false;\r
+ for (int i=0; i< methods.length; i++) {\r
+ if (reqmeth.equals(methods[i])) {\r
+ affected = true;\r
+ break;\r
+ }\r
+ }\r
+ if (!affected) {\r
+ return super.get(request);\r
+ }\r
+ }\r
+ // now we can modify it :)\r
+ return getRedirectReply(request);\r
+ }\r
+\r
+ /**\r
+ * The HEAD method, may emit a redirect \r
+ * @param request The request to handle.\r
+ * @exception ProtocolException If processsing the request failed.\r
+ * @exception ResourceException If the resource got a fatal error.\r
+ */\r
+ public Reply head(Request request)\r
+ throws ProtocolException, ResourceException\r
+ {\r
+ String methods[] = getMethods();\r
+ \r
+ if (methods != null) {\r
+ String reqmeth = request.getMethod();\r
+ boolean affected = false;\r
+ for (int i=0; i< methods.length; i++) {\r
+ if (reqmeth.equals(methods[i])) {\r
+ affected = true;\r
+ break;\r
+ }\r
+ }\r
+ if (!affected) {\r
+ return super.head(request);\r
+ }\r
+ }\r
+ // now we can modify it :)\r
+ Reply reply = getRedirectReply(request);\r
+ reply.setStream((InputStream) null);\r
+ return reply ;\r
+ }\r
+\r
+ /**\r
+ * The PUT method, may emit a redirect, otherwise uses its parent put\r
+ * @param request The request to handle.\r
+ * @exception ProtocolException If processsing the request failed.\r
+ * @exception ResourceException If the resource got a fatal error.\r
+ */\r
+ public Reply put(Request request)\r
+ throws ProtocolException, ResourceException\r
+ {\r
+ String methods[] = getMethods();\r
+ \r
+ if (methods != null) {\r
+ String reqmeth = request.getMethod();\r
+ boolean affected = false;\r
+ for (int i=0; i< methods.length; i++) {\r
+ if (reqmeth.equals(methods[i])) {\r
+ affected = true;\r
+ break;\r
+ }\r
+ }\r
+ if (!affected) {\r
+ return super.put(request);\r
+ }\r
+ }\r
+ // now we can modify it :)\r
+ return getRedirectReply(request);\r
+ }\r
+ /**\r
+ * The POST method, may emit a redirect, otherwise uses its parent put\r
+ * @param request The request to handle.\r
+ * @exception ProtocolException If processsing the request failed.\r
+ * @exception ResourceException If the resource got a fatal error.\r
+ */\r
+ public Reply post(Request request)\r
+ throws ProtocolException, ResourceException\r
+ {\r
+ String methods[] = getMethods();\r
+ \r
+ if (methods != null) {\r
+ String reqmeth = request.getMethod();\r
+ boolean affected = false;\r
+ for (int i=0; i< methods.length; i++) {\r
+ if (reqmeth.equals(methods[i])) {\r
+ affected = true;\r
+ break;\r
+ }\r
+ }\r
+ if (!affected) {\r
+ return super.post(request);\r
+ }\r
+ }\r
+ // now we can modify it :)\r
+ return getRedirectReply(request);\r
+ }\r
+}\r