001: // ProxyFrame.java
002: // $Id: ProxyFrame.java,v 1.17 2000/08/16 21:37:43 ylafon Exp $
003: // (c) COPYRIGHT MIT and INRIA, 1998.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005:
006: package org.w3c.jigsaw.proxy;
007:
008: import java.net.InetAddress;
009: import java.net.URL;
010: import java.net.UnknownHostException;
011:
012: import java.util.Date;
013: import java.util.Hashtable;
014:
015: import org.w3c.www.http.HTTP;
016: import org.w3c.www.http.HttpMessage;
017: import org.w3c.www.http.HttpRequestMessage;
018:
019: import org.w3c.jigsaw.html.HtmlGenerator;
020:
021: import org.w3c.tools.resources.Attribute;
022: import org.w3c.tools.resources.AttributeHolder;
023: import org.w3c.tools.resources.AttributeRegistry;
024: import org.w3c.tools.resources.BooleanAttribute;
025: import org.w3c.tools.resources.DirectoryResource;
026: import org.w3c.tools.resources.DummyResourceReference;
027: import org.w3c.tools.resources.FramedResource;
028: import org.w3c.tools.resources.InvalidResourceException;
029: import org.w3c.tools.resources.LookupResult;
030: import org.w3c.tools.resources.LookupState;
031: import org.w3c.tools.resources.ProtocolException;
032: import org.w3c.tools.resources.Resource;
033: import org.w3c.tools.resources.ResourceFrame;
034: import org.w3c.tools.resources.ResourceReference;
035: import org.w3c.tools.resources.ServerInterface;
036:
037: import org.w3c.jigsaw.http.Reply;
038: import org.w3c.jigsaw.http.Request;
039:
040: import org.w3c.jigsaw.frames.HTTPFrame;
041: import org.w3c.jigsaw.http.socket.SocketClientFactory;
042: import org.w3c.util.ObservableProperties;
043:
044: /*
045: class FtpTunnel extends HTTPFrame {
046:
047: public Reply get(Request request)
048: throws HTTPException
049: {
050: try {
051: MimeType mt;
052: URLConnection c = request.getURL().openConnection();
053: InputStream in = c.getInputStream();
054: boolean markUsable = in.markSupported();
055: // We don't know the content length
056: Reply reply = createDefaultReply(request, HTTP.OK);
057: reply.addPragma("no-cache");
058: reply.setNoCache();
059: reply.setContentLength(c.getContentLength());
060: if (markUsable)
061: in.mark(Integer.MAX_VALUE);
062: try {
063: mt = new MimeType(c.getContentType());
064: } catch (MimeTypeFormatException me) {
065: mt = MimeType.TEXT_PLAIN;
066: }
067: reply.setContentType(mt);
068: if (markUsable) // cope with a well known java bug
069: in.reset();
070: reply.setStream(in);
071: return reply;
072: } catch (Exception ex) {
073: throw new HTTPException("Unable to ftp \""
074: + request.getURL()
075: + ", details \""+ex.getMessage()+"\"");
076: }
077: }
078:
079: }
080: */
081:
082: class Stats extends HTTPFrame {
083: ProxyFrame proxy = null;
084: Date startdate = null;
085: boolean hasICP = true;
086:
087: protected String percentage(int part, int tot) {
088: double p = ((double) part / ((double) tot)) * ((double) 100);
089: return Integer.toString((int) p) + "%";
090: }
091:
092: public Reply get(Request request) {
093: Reply r = createDefaultReply(request, HTTP.OK);
094: HtmlGenerator g = new HtmlGenerator("Proxy statistics");
095: int c = proxy.reqcount + proxy.reqerred;
096:
097: if (c == 0)
098: c = 1;
099: g.addMeta("Refresh", "30");
100: proxy.addStyleSheet(g);
101: g.append("<h1>Proxy statistics</h1>");
102: g.append("<p>The proxy was last started at: <em>" + startdate
103: + "</em>");
104: g.append("<p><table align=\"center\" border=\"1\"");
105: g
106: .append("<tr><th colspan=\"3\">Counter<th>count<th>percentage");
107: // The total number of hits to the proxy:
108: g
109: .append("<tr><td colspan=\"3\">Total number of handled requests");
110: g.append("<td align=center>", Integer.toString(c));
111: g.append("<td align=center>", percentage(c, c));
112: // The total number of errors:
113: g.append("<tr><td width=50><td colspan=\"2\">Erred requests");
114: g.append("<td align=center>", Integer.toString(proxy.reqerred));
115: g.append("<td align=center>", percentage(proxy.reqerred, c));
116: // The total number of ICP redirects:
117: g.append("<tr><td width=50><td colspan=\"2\">ICP redirects");
118: g.append("<td align=center>", Integer
119: .toString(proxy.cache_icps));
120: g.append("<td align=center>", percentage(proxy.cache_icps, c));
121: // The total number of no-cache:
122: g.append("<tr><td width=50><td colspan=\"2\">Non cacheable");
123: g.append("<td align=center>", Integer
124: .toString(proxy.cache_nocache));
125: g.append("<td align=center>",
126: percentage(proxy.cache_nocache, c));
127: // Cache accesses:
128: int cached = (proxy.cache_hits + proxy.cache_misses
129: + proxy.cache_revalidations + proxy.cache_retrievals);
130: g.append("<tr><td width=50><td colspan=\"2\">Cache Accesses");
131: g.append("<td align=center>", Integer.toString(cached));
132: g.append("<td align=center>", percentage(cached, c));
133: // Hits (served by cache)
134: g
135: .append("<tr><td width=50><td width=50><td>Hits (served by cache)");
136: g.append("<td align=center>", Integer
137: .toString(proxy.cache_hits));
138: g.append("<td align=center>", percentage(proxy.cache_hits, c));
139: // Hits (revalidations)
140: g
141: .append("<tr><td width=50><td width=50><td>Hits (revalidations)");
142: g.append("<td align=center>", Integer
143: .toString(proxy.cache_revalidations));
144: g.append("<td align=center>", percentage(
145: proxy.cache_revalidations, c));
146: // Misses (no cache entry)
147: g.append("<tr><td width=50><td width=50><td>Misses (no entry)");
148: g.append("<td align=center>", Integer
149: .toString(proxy.cache_misses));
150: g
151: .append("<td align=center>", percentage(
152: proxy.cache_misses, c));
153: // Misses (retrievals)
154: g
155: .append("<tr><td width=50><td width=50><td>Misses (retrievals)");
156: g.append("<td align=center>", Integer
157: .toString(proxy.cache_retrievals));
158: g.append("<td align=center>", percentage(
159: proxy.cache_retrievals, c));
160: g.append("</table>");
161: // Some goodies:
162: g.append("<hr>Generated by <i>", proxy.getServer().getURL()
163: .toExternalForm());
164: r.setStream(g);
165: r.addPragma("no-cache");
166: r.setNoCache();
167: return r;
168: }
169:
170: Stats(ProxyFrame proxy) {
171: this .proxy = proxy;
172: this .startdate = new Date(System.currentTimeMillis());
173: }
174: }
175:
176: /**
177: * A proxy module for Jigsaw.
178: */
179:
180: public class ProxyFrame extends ForwardFrame {
181: /**
182: * Attribute index - Should we tunnel ftp accesses ?
183: */
184: protected static int ATTR_HANDLEFTP = -1;
185:
186: static {
187: Attribute a = null;
188: Class c = null;
189: try {
190: c = Class.forName("org.w3c.jigsaw.proxy.ProxyFrame");
191: } catch (Exception ex) {
192: ex.printStackTrace();
193: System.exit(1);
194: }
195: // Register the handle-ftp attribute:
196: a = new BooleanAttribute("handle-ftp", Boolean.FALSE,
197: Attribute.EDITABLE);
198: ATTR_HANDLEFTP = AttributeRegistry.registerAttribute(c, a);
199: }
200:
201: URL url = null;
202: // FtpTunnel ftphandler = null;
203: Stats statistics = null;
204: DummyResourceReference drr = null;
205: FramedResource statsres = null;
206: String default_hostaddr = null;
207:
208: /**
209: * Trap changes to the handleftp attribute.
210: * @param idx The attribute being set.
211: * @param value The new value for that attribute.
212: */
213:
214: public void setValue(int idx, Object value) {
215: super .setValue(idx, value);
216: if (idx == ATTR_HANDLEFTP) {
217: boolean b = ((Boolean) value).booleanValue();
218: // if ( b ) {
219: // ftphandler = new FtpTunnel();
220: // } else {
221: // ftphandler = null;
222: // }
223: }
224: }
225:
226: /**
227: * Do we handle ftp ?
228: * @return A boolean.
229: */
230:
231: public boolean checkHandleFTP() {
232: return getBoolean(ATTR_HANDLEFTP, false);
233: }
234:
235: /**
236: * Lookup for an proxied resource.
237: * @param request The request whose URI is to be looked up.
238: * @param ls The current lookup state
239: * @param lr The result
240: * @exception org.w3c.tools.resources.ProtocolException If something fails.
241: */
242: public boolean lookupOther(LookupState ls, LookupResult lr)
243: throws org.w3c.tools.resources.ProtocolException {
244: // Get the full URL from the request:
245: Request request = (Request) ls.getRequest();
246: URL requrl = ((request != null) ? request.getURL() : null);
247: boolean host_equiv = false;
248:
249: // loop check
250: if (request != null) {
251: String vias[] = request.getVia();
252: if ((url != null) && (requrl.getPort() == url.getPort())
253: && (vias != null && vias.length > 5)) {
254: // maybe a loop, let's try to sort it out with an expensive
255: // checking on IPs
256: String hostaddr;
257:
258: ObservableProperties props = getServer()
259: .getProperties();
260: hostaddr = props.getString(
261: SocketClientFactory.BINDADDR_P,
262: default_hostaddr);
263: if (requrl != null) {
264: InetAddress targhost;
265: String reqhostaddr;
266:
267: try {
268: targhost = InetAddress.getByName(requrl
269: .getHost());
270: reqhostaddr = targhost.getHostAddress();
271: host_equiv = reqhostaddr.equals(hostaddr);
272: } catch (UnknownHostException uhex) {
273: }
274: ;
275: }
276: }
277: }
278: if (((url != null)
279: && (requrl != null)
280: && ((requrl.getPort() == url.getPort()) || (requrl
281: .getPort()
282: * url.getPort() == -80)) && (host_equiv || (requrl
283: .getHost().equalsIgnoreCase(url.getHost()))))
284: || (ls.isInternal())) {
285: // Call super.lookup:
286: super .lookupOther(ls, lr);
287: if (ls.hasMoreComponents()) {
288: ResourceReference root = getLocalRootResource();
289: if (root == null) {
290: lr.setTarget(this .getResource()
291: .getResourceReference());
292: return true;
293: }
294: try {
295: // because the root eats the lookup state components
296: // we have to return true.
297: // Should not be continued by the caller.
298: FramedResource res = (FramedResource) root.lock();
299: boolean done = res.lookup(ls, lr);
300: if (!done)
301: lr.setTarget(null);
302: return true;
303: } catch (InvalidResourceException ex) {
304: // should never happen with the root resource
305: ex.printStackTrace();
306: } finally {
307: root.unlock();
308: }
309: return true; // should never be reached
310: } else {
311: request.setState(STATE_CONTENT_LOCATION, "true");
312: // return the index file.
313: String index = getIndex();
314: if (index != null && index.length() > 0) {
315: ResourceReference root = getLocalRootResource();
316: try {
317: DirectoryResource dir = (DirectoryResource) root
318: .lock();
319: ResourceReference rr = dir.lookup(index);
320: if (rr != null) {
321: try {
322: FramedResource rindex = (FramedResource) rr
323: .lock();
324: return rindex.lookup(ls, lr);
325: } catch (InvalidResourceException ex) {
326: } finally {
327: rr.unlock();
328: }
329: }
330: } catch (InvalidResourceException ex) {
331: root.unlock();
332: }
333: }
334: lr.setTarget(drr);
335: return true;
336: }
337: } else {
338: // Always invoke super lookup, after notification that its a proxy
339: request.setProxy(true);
340: super .lookupOther(ls, lr);
341: if (requrl.getProtocol().equals("ftp")) {
342: // if (ftphandler != null)
343: // lr.setTarget(ftphandler);
344: // else
345: lr.setTarget(null);
346: return true;
347: } else {
348: lr.setTarget(this .getResource().getResourceReference());
349: return true;
350: }
351: }
352: }
353:
354: /**
355: * do the normal lookup, and set the proxy boolean flag if needed
356: * @param ls The current lookup state
357: * @param lr The result
358: * @return true if lookup is done.
359: * @exception org.w3c.tools.resources.ProtocolException If an error
360: * relative to the protocol occurs
361: */
362:
363: public boolean lookup(LookupState ls, LookupResult lr)
364: throws org.w3c.tools.resources.ProtocolException {
365: // Internal lookup:
366: if (ls.isInternal())
367: return super .lookup(ls, lr);
368:
369: Request request = (Request) ls.getRequest();
370: URL requrl = request.getURL();
371:
372: if ((url != null)
373: && ((requrl.getPort() == url.getPort()) || (requrl
374: .getPort()
375: * url.getPort() == -80))
376: && (requrl.getHost().equalsIgnoreCase(url.getHost()))) {
377: return super .lookup(ls, lr);
378: } else {
379: String vias[] = request.getVia();
380: if ((url != null) && (requrl.getPort() == url.getPort())
381: && (vias != null && vias.length > 5)) {
382: // maybe a loop, let's try to sort it out with an expensive
383: // checking on IPs
384: String hostaddr = null;
385: ObservableProperties props = getServer()
386: .getProperties();
387: hostaddr = props.getString(
388: SocketClientFactory.BINDADDR_P,
389: default_hostaddr);
390: if (requrl != null) {
391: InetAddress targhost;
392: String reqhostaddr;
393:
394: try {
395: targhost = InetAddress.getByName(requrl
396: .getHost());
397: reqhostaddr = targhost.getHostAddress();
398: if (reqhostaddr.equals(hostaddr))
399: return super .lookup(ls, lr);
400: } catch (Exception ex) {
401: // can't get it, let the proxy bark!
402: }
403: }
404: }
405: // not internal, so set it as a proxied call for lookup
406: request.setProxy(true);
407: }
408: return super .lookup(ls, lr);
409: }
410:
411: /**
412: * companion to initialize, called after the register
413: */
414:
415: public void registerResource(FramedResource resource) {
416: super .registerResource(resource);
417: // Our home url:
418: url = getServer().getURL();
419: // If we do handle ftp, initialize:
420: // if ( checkHandleFTP() )
421: // ftphandler = new FtpTunnel();
422: // Initialize the stats:
423: ResourceFrame frame = null;
424: try {
425: frame = getFrame(Class
426: .forName("org.w3c.jigsaw.proxy.Stats"));
427: } catch (Exception ex) {
428: // don't give a damn
429: }
430: if (frame != null)
431: return;
432: statistics = new Stats(this );
433: statsres = new FramedResource(); // create an empty resource
434: statsres.registerFrame(statistics, new Hashtable(1));
435: drr = new DummyResourceReference(statsres);
436: }
437:
438: /**
439: * Update the URL in which we are installed.
440: * @param values The default attribute values.
441: */
442:
443: public void initialize(Object values[]) {
444: super .initialize(values);
445: try {
446: default_hostaddr = InetAddress.getLocalHost()
447: .getHostAddress();
448: } catch (UnknownHostException ex) {
449: // can't get localhost? Force it (ugly but...)
450: default_hostaddr = "127.0.0.1";
451: }
452: }
453: }
|