001: /*
002: * ProxyHandler.java
003: *
004: * Brazil project web application Framework,
005: * export version: 1.1
006: * Copyright (c) 1999-2000 Sun Microsystems, Inc.
007: *
008: * Sun Public License Notice
009: *
010: * The contents of this file are subject to the Sun Public License Version
011: * 1.0 (the "License"). You may not use this file except in compliance with
012: * the License. A copy of the License is included as the file "license.terms",
013: * and also available at http://www.sun.com/
014: *
015: * The Original Code is from:
016: * Brazil project web application Framework release 1.1.
017: * The Initial Developer of the Original Code is: cstevens.
018: * Portions created by cstevens are Copyright (C) Sun Microsystems, Inc.
019: * All Rights Reserved.
020: *
021: * Contributor(s): cstevens, suhler.
022: *
023: * Version: 1.20
024: * Created by cstevens on 99/09/15
025: * Last modified by suhler on 00/12/11 13:31:25
026: */
027:
028: package sunlabs.brazil.proxy;
029:
030: import sunlabs.brazil.server.Handler;
031: import sunlabs.brazil.server.Request;
032: import sunlabs.brazil.server.Server;
033:
034: import sunlabs.brazil.util.http.HttpInputStream;
035: import sunlabs.brazil.util.http.HttpRequest;
036: import sunlabs.brazil.util.http.MimeHeaders; /**/
037: import java.io.EOFException;
038: import java.io.IOException;
039: import java.io.InterruptedIOException;
040: import java.io.OutputStream;
041: import java.net.UnknownHostException;
042: import java.net.ConnectException;
043: import java.util.Properties;
044:
045: /**
046: * Handler for implementing a web proxy.
047: * By default, this is a dumb proxy. It can be combined with other
048: * handlers to generate side effects, such as content rewriting.
049: * <p>
050: * Properties:
051: * <dl class=props>
052: * <dt>useproxy <dd>The name of the SocketFactory class to use for
053: * this handler. If additional properties are required to set up
054: * the SocketFactory, it should be configured as a handler instead.
055: * This is here for convenience only.
056: * <dt>auth <dd>The value of the proxy-authenticate header (if any) sent to the upstream proxy
057: * <dt>proxyHost<dd>If specified, the name of the upstream proxy
058: * <dt>proxyPort<dd>The up stream proxys port, if a proxyHost is pecified (defaults to 80)
059: * </dl>
060: *
061: * @author Stephen Uhler
062: * @version 1.20, 12/11/00
063: */
064: public class ProxyHandler implements Handler {
065: public static final String PROXY_HOST = "proxyHost";
066: public static final String PROXY_PORT = "proxyPort";
067: public static final String AUTH = "auth";
068: public static final String USE_PROXY = "useproxy";
069:
070: String via;
071:
072: /**
073: * The proxy server.
074: */
075: public String proxyHost;
076:
077: /**
078: * The proxy server's port. Default is 80.
079: */
080: public int proxyPort = 80;
081:
082: /**
083: * The string to send as the value for the "Proxy-Authorization"
084: * HTTP header (if needed).
085: */
086: public String auth;
087:
088: UseProxy proxyTester;
089:
090: /**
091: * Do one-time setup.
092: * get and process the properties file options, and make sure
093: */
094: public boolean init(Server server, String prefix) {
095: String str;
096: Properties props = server.props;
097:
098: proxyHost = props.getProperty(prefix + PROXY_HOST);
099:
100: str = props.getProperty(prefix + PROXY_PORT);
101: try {
102: proxyPort = Integer.decode(str).intValue();
103: } catch (Exception e) {
104: }
105: ;
106:
107: auth = props.getProperty(prefix + AUTH);
108:
109: /*
110: * Set a proxy. If more sophisicated initialization is required than newinstance(),
111: * set up the proxy in a separate handler
112: */
113:
114: String useproxy = props.getProperty(prefix + USE_PROXY);
115: if (useproxy != null) {
116: try {
117: Class type = Class.forName(useproxy);
118: proxyTester = (UseProxy) type.newInstance();
119: } catch (Exception e) {
120: server.log(Server.LOG_WARNING, prefix,
121: "Proxy installation error : " + e);
122: }
123: }
124: if (proxyTester == null) {
125: proxyTester = new UseProxy() {
126: public boolean useProxy(String host, int port) {
127: return true;
128: }
129: };
130: }
131:
132: via = " " + server.hostName + ":"
133: + server.listen.getLocalPort() + " (" + server.name
134: + ")";
135:
136: return true;
137: }
138:
139: /**
140: * @see Handler#respond
141: */
142: public boolean respond(Request client) throws IOException {
143: String url = client.url;
144:
145: if (url.startsWith("http:") == false) {
146: return false;
147: }
148: if ((client.query != null) && (client.query.length() > 0)) {
149: url += "?" + client.query;
150: }
151:
152: MimeHeaders clientHeaders = client.headers;
153:
154: /*
155: * "Proxy-Connection" may be used (instead of just "Connection")
156: * to keep alive a connection between a client and this proxy.
157: */
158: String pc = clientHeaders.get("Proxy-Connection");
159: if (pc != null) {
160: client.connectionHeader = "Proxy-Connection";
161: client.keepAlive = pc.equalsIgnoreCase("Keep-Alive");
162: }
163:
164: HttpRequest.removePointToPointHeaders(clientHeaders, false);
165:
166: HttpRequest target = new HttpRequest(url);
167: try {
168: MimeHeaders targetHeaders = target.requestHeaders;
169:
170: target.setMethod(client.method);
171: clientHeaders.copyTo(targetHeaders);
172: /* targetHeaders.add("Via", client.protocol + via);*/
173:
174: /*
175: * We might need to authenticate to a target proxy.
176: */
177:
178: if ((proxyHost != null)
179: && proxyTester.useProxy(target.host, target.port)) {
180: target.setProxy(proxyHost, proxyPort);
181: if (auth != null) {
182: targetHeaders.add("Proxy-Authorization", auth);
183: }
184: }
185:
186: if (client.postData != null) {
187: OutputStream out = target.getOutputStream();
188: out.write(client.postData);
189: out.close();
190: }
191:
192: target.connect();
193:
194: targetHeaders = target.responseHeaders;
195: HttpRequest.removePointToPointHeaders(targetHeaders, true);
196:
197: clientHeaders = client.responseHeaders;
198: targetHeaders.copyTo(clientHeaders);
199: try {
200: clientHeaders.add("Via", target.status.substring(0, 8)
201: + via);
202: } catch (StringIndexOutOfBoundsException e) {
203: clientHeaders.add("Via", via);
204: }
205:
206: client
207: .sendResponse(target.getInputStream(), target
208: .getContentLength(), null, target
209: .getResponseCode());
210: } catch (InterruptedIOException e) {
211: /*
212: * Read timeout while reading from the remote side. We use a
213: * read timeout in case the target never responds.
214: */
215: client.sendError(408, "Timeout / No response");
216: } catch (EOFException e) {
217: client.sendError(500, "No response");
218: } catch (UnknownHostException e) {
219: client.sendError(500, "Unknown host");
220: } catch (ConnectException e) {
221: client.sendError(500, "Connection refused");
222: } catch (IOException e) {
223: /*
224: * An IOException will happen if we can't communicate with the
225: * target or the client. Rather than attempting to discriminate,
226: * just send an error message to the client, and let the send
227: * fail if the client was the one that was in error.
228: */
229:
230: String msg = "Error from proxy";
231: if (e.getMessage() != null) {
232: msg += ": " + e.getMessage();
233: }
234: client.sendError(500, msg);
235: } finally {
236: target.close();
237: return true;
238: }
239: }
240: }
|