001: // RelocateFrame.java
002: // $Id: RelocateFrame.java,v 1.19 2002/04/11 11:58:36 ylafon Exp $
003: // (c) COPYRIGHT MIT and INRIA, 1996.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005:
006: package org.w3c.jigsaw.frames;
007:
008: import java.io.InputStream;
009:
010: import java.net.MalformedURLException;
011: import java.net.URL;
012:
013: import org.w3c.tools.resources.Attribute;
014: import org.w3c.tools.resources.AttributeHolder;
015: import org.w3c.tools.resources.AttributeRegistry;
016: import org.w3c.tools.resources.BooleanAttribute;
017: import org.w3c.tools.resources.LookupResult;
018: import org.w3c.tools.resources.LookupState;
019: import org.w3c.tools.resources.ProtocolException;
020: import org.w3c.tools.resources.Resource;
021: import org.w3c.tools.resources.ResourceException;
022: import org.w3c.tools.resources.ResourceFrame;
023: import org.w3c.tools.resources.ServerInterface;
024: import org.w3c.tools.resources.StringArrayAttribute;
025: import org.w3c.tools.resources.StringAttribute;
026:
027: import org.w3c.jigsaw.http.HTTPException;
028: import org.w3c.jigsaw.http.Reply;
029: import org.w3c.jigsaw.http.Request;
030: import org.w3c.jigsaw.http.httpd;
031:
032: import org.w3c.jigsaw.html.HtmlGenerator;
033:
034: import org.w3c.www.http.HTTP;
035: import org.w3c.www.http.HttpMessage;
036: import org.w3c.www.http.HttpReplyMessage;
037: import org.w3c.www.http.HttpRequestMessage;
038:
039: import org.w3c.tools.resources.ProtocolException;
040: import org.w3c.tools.resources.ResourceException;
041:
042: /**
043: * Emit a HTTP redirect.
044: */
045: public class RelocateFrame extends HTTPFrame {
046:
047: /**
048: * Name of the state to hold the PATH_INFO in the request.
049: */
050: public final static String PATH_INFO = "org.w3c.jigsaw.resources.RelocateResource.PathInfo";
051:
052: /**
053: * Attribute index - The relocation location.
054: */
055: protected static int ATTR_LOCATION = -1;
056: /**
057: * Attribute index - Should we also handle extra path infos ?
058: */
059: protected static int ATTR_HANDLE_PATHINFO = -1;
060: /**
061: * Attribute index - Is the relocation permanent?
062: */
063: protected static int ATTR_PERMANENT_REDIRECT = -1;
064: /**
065: * Attribute index - SHould we use the ambiguous 302?
066: */
067: protected static int ATTR_USE_302 = -1;
068: /**
069: * Attribute index - The methods affected by this frame
070: */
071: protected static int ATTR_METHODS = -1;
072:
073: static {
074: Attribute a = null;
075: Class c = null;
076:
077: try {
078: c = Class.forName("org.w3c.jigsaw.frames.RelocateFrame");
079: } catch (Exception ex) {
080: ex.printStackTrace();
081: System.exit(1);
082: }
083: // The location attribute
084: a = new StringAttribute("location", null, Attribute.EDITABLE
085: | Attribute.MANDATORY);
086: ATTR_LOCATION = AttributeRegistry.registerAttribute(c, a);
087: // The handle path info attribute
088: a = new BooleanAttribute("handle-pathinfo", Boolean.TRUE,
089: Attribute.EDITABLE);
090: ATTR_HANDLE_PATHINFO = AttributeRegistry
091: .registerAttribute(c, a);
092: // the permanent redirection attribute
093: a = new BooleanAttribute("permanent-redirect", Boolean.FALSE,
094: Attribute.EDITABLE);
095: ATTR_PERMANENT_REDIRECT = AttributeRegistry.registerAttribute(
096: c, a);
097: // should we use the ambiguous 302 response code?
098: a = new BooleanAttribute("use-usual-response", Boolean.TRUE,
099: Attribute.EDITABLE);
100: ATTR_USE_302 = AttributeRegistry.registerAttribute(c, a);
101: // The affected methods
102: a = new StringArrayAttribute("methods", null,
103: Attribute.EDITABLE);
104: ATTR_METHODS = AttributeRegistry.registerAttribute(c, a);
105: }
106:
107: /**
108: * Get the location for the relocation
109: * @return a string, containing the relative path or absolute
110: */
111: public String getLocation() {
112: return (String) getValue(ATTR_LOCATION, null);
113: }
114:
115: /**
116: * Get the list of methods affected by the redirect
117: * @return An array of String giving the name of the redirected methods,
118: * or <strong>null</strong>, in wich case <em>all</em> methods are
119: * to be redirected.
120: */
121: public String[] getMethods() {
122: return (String[]) getValue(ATTR_METHODS, null);
123: }
124:
125: /**
126: * Get the path info value
127: * @return a boolean
128: */
129: public boolean checkHandlePathInfo() {
130: return getBoolean(ATTR_HANDLE_PATHINFO, true);
131: }
132:
133: /**
134: * Get the permanent redirect flag
135: * @return a boolean
136: */
137: public boolean checkPermanentRedirect() {
138: return getBoolean(ATTR_PERMANENT_REDIRECT, false);
139: }
140:
141: /**
142: * Get the "use ambigous 302 response code" flag
143: * @return a boolean
144: */
145: public boolean checkUse302() {
146: return getBoolean(ATTR_USE_302, true);
147: }
148:
149: /**
150: * Lookup the target resource (dispath to more specific lookup methods).
151: * @param ls The current lookup state
152: * @param lr The result
153: * @return true if lookup is done.
154: * @exception ProtocolException If an error relative to the protocol occurs
155: * @see #lookupDirectory
156: * @see #lookupFile
157: * @see #lookupOther
158: */
159: protected boolean lookupResource(LookupState ls, LookupResult lr)
160: throws ProtocolException {
161: String methods[] = getMethods();
162:
163: if (ls.hasRequest() && (methods != null)) {
164: Request request = (Request) ls.getRequest();
165: String reqmeth = request.getMethod();
166: boolean affected = false;
167: for (int i = 0; i < methods.length; i++) {
168: if (reqmeth.equals(methods[i])) {
169: affected = true;
170: break;
171: }
172: }
173: if (!affected) {
174: return super .lookupResource(ls, lr);
175: }
176: }
177: // Perform our super-class lookup strategy:
178: if (super .lookupOther(ls, lr)) {
179: return true;
180: } else if (!checkHandlePathInfo()) {
181: return false;
182: }
183: // Compute PATH INFO, store it as a piece of state in
184: // the request:
185: StringBuffer pathinfo = new StringBuffer();
186: while (ls.hasMoreComponents()) {
187: pathinfo.append('/');
188: pathinfo.append(ls.getNextComponent());
189: }
190: if (ls.hasRequest()) {
191: Request request = (Request) ls.getRequest();
192: String reqfile = request.getURL().getFile();
193: if (reqfile.endsWith("/")) {
194: pathinfo.append('/');
195: }
196: request.setState(PATH_INFO, pathinfo.toString());
197: }
198: lr.setTarget(resource.getResourceReference());
199: return true;
200: }
201:
202: /**
203: * build the redirect reply based on the request and the current
204: * configuration
205: * @param request The request to handle.
206: * @exception ProtocolException If processsing the request failed.
207: * @return a Reply
208: */
209: private Reply getRedirectReply(Request request)
210: throws ProtocolException {
211: String location = getLocation();
212: if (location == null) {
213: Reply error = request.makeReply(HTTP.INTERNAL_SERVER_ERROR);
214: error
215: .setContent("The target RelocateResource doesn't define the"
216: + " relocation location. The server is "
217: + " misconfigured.");
218: throw new HTTPException(error);
219: } else {
220: Reply reply = null;
221: URL loc = null;
222: if (checkUse302()) {
223: reply = request.makeReply(HTTP.FOUND);
224: } else {
225: if (checkPermanentRedirect()) {
226: reply = request.makeReply(HTTP.MOVED_PERMANENTLY);
227: } else {
228: reply = request.makeReply(HTTP.TEMPORARY_REDIRECT);
229: }
230: }
231: try {
232: httpd server = (httpd) getServer();
233: String host = request.getHost();
234: if (host == null)
235: loc = new URL(server.getURL(), location);
236: else {
237: int ic = host.indexOf(':');
238: if (ic < 0) {
239: loc = new URL(new URL(server.getURL()
240: .getProtocol(), host, server.getURL()
241: .getFile()), location);
242: } else {
243: loc = new URL(new URL(server.getURL()
244: .getProtocol(), host.substring(0, ic),
245: Integer
246: .parseInt(host
247: .substring(ic + 1)),
248: server.getURL().getFile()), location);
249: }
250: }
251: if (checkHandlePathInfo()) {
252: String pathinfo = (String) request
253: .getState(PATH_INFO);
254: // Given the way pathinfo is computed, it starts with a /
255: try {
256: if (pathinfo != null) {
257: loc = new URL(loc.toExternalForm()
258: + pathinfo);
259: }
260: } catch (MalformedURLException ex) {
261: resource
262: .getServer()
263: .errlog(
264: resource,
265: "This resource handle Pathinfo "
266: + "but the request has an invalid "
267: + "PATH_INFO state.");
268: }
269: if (request.hasQueryString()) {
270: try {
271: loc = new URL(loc.toExternalForm() + "?"
272: + request.getQueryString());
273: } catch (MalformedURLException ex) {
274: resource.getServer().errlog(
275: resource,
276: "This resource handle "
277: + "Pathinfo but the "
278: + "request has an "
279: + "invalid "
280: + "PATH_INFO state.");
281: }
282: }
283: }
284: } catch (Exception ex) {
285: ex.printStackTrace();
286: }
287: reply.setLocation(loc);
288: HtmlGenerator g = new HtmlGenerator("Moved");
289: g
290: .append("<P>This resources has moved, click on the link if your"
291: + " browser doesn't support automatic redirection<BR>"
292: + "<A HREF=\""
293: + loc.toExternalForm()
294: + "\">" + loc.toExternalForm() + "</A>");
295: reply.setStream(g);
296: return reply;
297: }
298: }
299:
300: /**
301: * The GET method, may emit a redirect
302: * @param request The request to handle.
303: * @exception ProtocolException If processsing the request failed.
304: * @exception ResourceException If the resource got a fatal error.
305: */
306: public Reply get(Request request) throws ProtocolException,
307: ResourceException {
308: String methods[] = getMethods();
309:
310: if (methods != null) {
311: String reqmeth = request.getMethod();
312: boolean affected = false;
313: for (int i = 0; i < methods.length; i++) {
314: if (reqmeth.equals(methods[i])) {
315: affected = true;
316: break;
317: }
318: }
319: if (!affected) {
320: return super .get(request);
321: }
322: }
323: // now we can modify it :)
324: return getRedirectReply(request);
325: }
326:
327: /**
328: * The HEAD method, may emit a redirect
329: * @param request The request to handle.
330: * @exception ProtocolException If processsing the request failed.
331: * @exception ResourceException If the resource got a fatal error.
332: */
333: public Reply head(Request request) throws ProtocolException,
334: ResourceException {
335: String methods[] = getMethods();
336:
337: if (methods != null) {
338: String reqmeth = request.getMethod();
339: boolean affected = false;
340: for (int i = 0; i < methods.length; i++) {
341: if (reqmeth.equals(methods[i])) {
342: affected = true;
343: break;
344: }
345: }
346: if (!affected) {
347: return super .head(request);
348: }
349: }
350: // now we can modify it :)
351: Reply reply = getRedirectReply(request);
352: reply.setStream((InputStream) null);
353: return reply;
354: }
355:
356: /**
357: * The PUT method, may emit a redirect, otherwise uses its parent put
358: * @param request The request to handle.
359: * @exception ProtocolException If processsing the request failed.
360: * @exception ResourceException If the resource got a fatal error.
361: */
362: public Reply put(Request request) throws ProtocolException,
363: ResourceException {
364: String methods[] = getMethods();
365:
366: if (methods != null) {
367: String reqmeth = request.getMethod();
368: boolean affected = false;
369: for (int i = 0; i < methods.length; i++) {
370: if (reqmeth.equals(methods[i])) {
371: affected = true;
372: break;
373: }
374: }
375: if (!affected) {
376: return super .put(request);
377: }
378: }
379: // now we can modify it :)
380: return getRedirectReply(request);
381: }
382:
383: /**
384: * The POST method, may emit a redirect, otherwise uses its parent put
385: * @param request The request to handle.
386: * @exception ProtocolException If processsing the request failed.
387: * @exception ResourceException If the resource got a fatal error.
388: */
389: public Reply post(Request request) throws ProtocolException,
390: ResourceException {
391: String methods[] = getMethods();
392:
393: if (methods != null) {
394: String reqmeth = request.getMethod();
395: boolean affected = false;
396: for (int i = 0; i < methods.length; i++) {
397: if (reqmeth.equals(methods[i])) {
398: affected = true;
399: break;
400: }
401: }
402: if (!affected) {
403: return super .post(request);
404: }
405: }
406: // now we can modify it :)
407: return getRedirectReply(request);
408: }
409: }
|