001: //icapHeader.java
002: //-----------------------
003: //(C) by Michael Peter Christen; mc@anomic.de
004: //first published on http://www.anomic.de
005: //Frankfurt, Germany, 2004
006: //
007: //This file is contributed by Martin Thelian
008: //last major change: $LastChangedDate: 2008-01-28 18:21:08 +0000 (Mo, 28 Jan 2008) $ by $LastChangedBy: orbiter $
009: //Revision: $LastChangedRevision: 4411 $
010: //
011: //This program is free software; you can redistribute it and/or modify
012: //it under the terms of the GNU General Public License as published by
013: //the Free Software Foundation; either version 2 of the License, or
014: //(at your option) any later version.
015: //
016: //This program is distributed in the hope that it will be useful,
017: //but WITHOUT ANY WARRANTY; without even the implied warranty of
018: //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
019: //GNU General Public License for more details.
020: //
021: //You should have received a copy of the GNU General Public License
022: //along with this program; if not, write to the Free Software
023: //Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024: //
025: //Using this software in any meaning (reading, learning, copying, compiling,
026: //running) means that you agree that the Author(s) is (are) not responsible
027: //for cost, loss of data or any harm that may be caused directly or indirectly
028: //by usage of this softare or this documentation. The usage of this software
029: //is on your own risk. The installation and usage (starting/running) of this
030: //software may allow other people or application to access your computer and
031: //any attached devices and is highly dependent on the configuration of the
032: //software which must be done by the user of the software; the author(s) is
033: //(are) also not responsible for proper configuration and usage of the
034: //software, even if provoked by documentation provided together with
035: //the software.
036: //
037: //Any changes to this file according to the GPL as documented in the file
038: //gpl.txt aside this file in the shipment you received can be done to the
039: //lines that follows this copyright notice here, but changes must not be
040: //done inside the copyright notive above. A re-distribution must contain
041: //the intact and unchanged copyright notice.
042: //Contributions and changes to the program code must be marked as such.
043:
044: package de.anomic.icap;
045:
046: import java.text.Collator;
047: import java.util.HashMap;
048: import java.util.Iterator;
049: import java.util.Locale;
050: import java.util.Map;
051: import java.util.Properties;
052: import java.util.TreeMap;
053:
054: import de.anomic.server.serverCore;
055:
056: public class icapHeader extends TreeMap<String, String> implements
057: Map<String, String> {
058:
059: private static final long serialVersionUID = 1L;
060:
061: /* =============================================================
062: * Constants defining icap methods
063: * ============================================================= */
064: public static final String METHOD_REQMOD = "REQMOD";
065: public static final String METHOD_RESPMOD = "RESPMOD";
066: public static final String METHOD_OPTIONS = "OPTIONS";
067:
068: /* =============================================================
069: * Constants defining http header names
070: * ============================================================= */
071: public static final String HOST = "Host";
072: public static final String USER_AGENT = "User-Agent";
073: public static final String CONNECTION = "Connection";
074: public static final String DATE = "Date";
075: public static final String SERVER = "Server";
076: public static final String ISTAG = "ISTAG";
077: public static final String METHODS = "Methods";
078: public static final String ALLOW = "Allow";
079: public static final String ENCAPSULATED = "Encapsulated";
080: public static final String MAX_CONNECTIONS = "Max-Connections";
081: public static final String OPTIONS_TTL = "Options-TTL";
082: public static final String SERVICE = "Service";
083: public static final String SERVICE_ID = "Service-ID";
084: public static final String PREVIEW = "Preview";
085: public static final String TRANSFER_PREVIEW = "Transfer-Preview";
086: public static final String TRANSFER_IGNORE = "Transfer-Ignore";
087: public static final String TRANSFER_COMPLETE = "Transfer-Complete";
088:
089: public static final String X_YACY_KEEP_ALIVE_REQUEST_COUNT = "X-Keep-Alive-Request-Count";
090:
091: /* =============================================================
092: * defining default icap status messages
093: * ============================================================= */
094: public static final HashMap<String, String> icap1_0 = new HashMap<String, String>();
095: static {
096: // (1yz) Informational codes
097: icap1_0.put("100", "Continue after ICAP preview");
098:
099: // (2yz) Success codes:
100: icap1_0.put("200", "OK");
101: icap1_0.put("204", "No modifications needed");
102:
103: // (4yz) Client error codes:
104: icap1_0.put("400", "Bad request");
105: icap1_0.put("404", "ICAP Service not found");
106: icap1_0.put("405", "Method not allowed for service");
107: icap1_0.put("408", "Request timeout");
108:
109: // (5yz) Server error codes:
110: icap1_0.put("500", "Server error");
111: icap1_0.put("501", "Method not implemented");
112: icap1_0.put("502", "Bad Gateway");
113: icap1_0.put("503", "Service overloaded");
114: icap1_0.put("505", "ICAP version not supported by server");
115: }
116:
117: /* PROPERTIES: General properties */
118: public static final String CONNECTION_PROP_ICAP_VER = "ICAP";
119: public static final String CONNECTION_PROP_HOST = "HOST";
120: public static final String CONNECTION_PROP_PATH = "PATH";
121: public static final String CONNECTION_PROP_EXT = "EXT";
122: public static final String CONNECTION_PROP_METHOD = "METHOD";
123: public static final String CONNECTION_PROP_REQUESTLINE = "REQUESTLINE";
124: public static final String CONNECTION_PROP_CLIENTIP = "CLIENTIP";
125: public static final String CONNECTION_PROP_URL = "URL";
126: public static final String CONNECTION_PROP_ARGS = "ARGS";
127: public static final String CONNECTION_PROP_PERSISTENT = "PERSISTENT";
128: public static final String CONNECTION_PROP_KEEP_ALIVE_COUNT = "KEEP-ALIVE_COUNT";
129:
130: private static final Collator insensitiveCollator = Collator
131: .getInstance(Locale.US);
132: static {
133: insensitiveCollator.setStrength(Collator.SECONDARY);
134: insensitiveCollator.setDecomposition(Collator.NO_DECOMPOSITION);
135: }
136:
137: public icapHeader() {
138: super (insensitiveCollator);
139: }
140:
141: public boolean allow(int statusCode) {
142: if (!super .containsKey("Allow"))
143: return false;
144:
145: String allow = (String) get("Allow");
146: return (allow.indexOf(Integer.toString(statusCode)) != -1);
147: }
148:
149: // to make the occurrence of multiple keys possible, we add them using a counter
150: public String add(String key, String value) {
151: int c = keyCount(key);
152: if (c == 0)
153: return put(key, value);
154: return put("*" + key + "-" + c, value);
155: }
156:
157: public int keyCount(String key) {
158: if (!(containsKey(key)))
159: return 0;
160: int c = 1;
161: while (containsKey("*" + key + "-" + c))
162: c++;
163: return c;
164: }
165:
166: // a convenience method to access the map with fail-over defaults
167: public Object get(Object key, Object dflt) {
168: Object result = get(key);
169: if (result == null)
170: return dflt;
171: return result;
172: }
173:
174: // return multiple results
175: public Object getSingle(Object key, int count) {
176: if (count == 0)
177: return get(key, null);
178: return get("*" + key + "-" + count, null);
179: }
180:
181: public StringBuffer toHeaderString(String icapVersion,
182: int icapStatusCode, String icapStatusText) {
183:
184: if ((icapStatusText == null) || (icapStatusText.length() == 0)) {
185: if (icapVersion.equals("ICAP/1.0")
186: && icapHeader.icap1_0.containsKey(Integer
187: .toString(icapStatusCode)))
188: icapStatusText = (String) icapHeader.icap1_0
189: .get(Integer.toString(icapStatusCode));
190: }
191:
192: StringBuffer theHeader = new StringBuffer();
193:
194: // write status line
195: theHeader.append(icapVersion).append(" ").append(
196: Integer.toString(icapStatusCode)).append(" ").append(
197: icapStatusText).append("\r\n");
198:
199: // write header
200: Iterator<String> i = keySet().iterator();
201: String key;
202: char tag;
203: int count;
204: while (i.hasNext()) {
205: key = (String) i.next();
206: tag = key.charAt(0);
207: if ((tag != '*') && (tag != '#')) { // '#' in key is reserved for proxy attributes as artificial header values
208: count = keyCount(key);
209: for (int j = 0; j < count; j++) {
210: theHeader.append(key).append(": ").append(
211: (String) getSingle(key, j)).append("\r\n");
212: }
213: }
214: }
215: // end header
216: theHeader.append("\r\n");
217:
218: return theHeader;
219: }
220:
221: public static Properties parseRequestLine(String cmd, String s,
222: Properties prop, String virtualHost) {
223:
224: // reset property from previous run
225: prop.clear();
226:
227: // storing informations about the request
228: prop.setProperty(CONNECTION_PROP_METHOD, cmd);
229: prop.setProperty(CONNECTION_PROP_REQUESTLINE, cmd + " " + s);
230:
231: // this parses a whole URL
232: if (s.length() == 0) {
233: prop.setProperty(CONNECTION_PROP_HOST, virtualHost);
234: prop.setProperty(CONNECTION_PROP_PATH, "/");
235: prop.setProperty(CONNECTION_PROP_ICAP_VER, "ICAP/1.0");
236: prop.setProperty(CONNECTION_PROP_EXT, "");
237: return prop;
238: }
239:
240: // store the version propery "ICAP" and cut the query at both ends
241: int sep = s.indexOf(" ");
242: if (sep >= 0) {
243: // ICAP version is given
244: prop.setProperty(CONNECTION_PROP_ICAP_VER, s.substring(
245: sep + 1).trim());
246: s = s.substring(0, sep).trim(); // cut off ICAP version mark
247: } else {
248: // ICAP version is not given, it will be treated as ver 0.9
249: prop.setProperty(CONNECTION_PROP_ICAP_VER, "ICAP/1.0");
250: }
251:
252: String argsString = "";
253: sep = s.indexOf("?");
254: if (sep >= 0) {
255: // there are values attached to the query string
256: argsString = s.substring(sep + 1); // cut haed from tail of query
257: s = s.substring(0, sep);
258: }
259: prop.setProperty(CONNECTION_PROP_URL, s); // store URL
260: if (argsString.length() != 0)
261: prop.setProperty(CONNECTION_PROP_ARGS, argsString); // store arguments in original form
262:
263: // finally find host string
264: if (s.toUpperCase().startsWith("ICAP://")) {
265: // a host was given. extract it and set path
266: s = s.substring(7);
267: sep = s.indexOf("/");
268: if (sep < 0) {
269: // this is a malformed url, something like
270: // http://index.html
271: // we are lazy and guess that it means
272: // /index.html
273: // which is a localhost access to the file servlet
274: prop.setProperty(CONNECTION_PROP_HOST, virtualHost);
275: prop.setProperty(CONNECTION_PROP_PATH, "/" + s);
276: } else {
277: // THIS IS THE "GOOD" CASE
278: // a perfect formulated url
279: prop.setProperty(CONNECTION_PROP_HOST, s.substring(0,
280: sep));
281: prop
282: .setProperty(CONNECTION_PROP_PATH, s
283: .substring(sep)); // yes, including beginning "/"
284: }
285: } else {
286: // no host in url. set path
287: if (s.startsWith("/")) {
288: // thats also fine, its a perfect localhost access
289: // in this case, we simulate a
290: // http://localhost/s
291: // access by setting a virtual host
292: prop.setProperty(CONNECTION_PROP_HOST, virtualHost);
293: prop.setProperty(CONNECTION_PROP_PATH, s);
294: } else {
295: // the client 'forgot' to set a leading '/'
296: // this is the same case as above, with some lazyness
297: prop.setProperty(CONNECTION_PROP_HOST, virtualHost);
298: prop.setProperty(CONNECTION_PROP_PATH, "/" + s);
299: }
300: }
301: return prop;
302:
303: }
304:
305: public static icapHeader readHeader(Properties prop,
306: serverCore.Session theSession) {
307: // reading all headers
308: icapHeader header = new icapHeader();
309: int p;
310: String line;
311: while ((line = theSession.readLineAsString()) != null) {
312: if (line.length() == 0)
313: break; // this seperates the header of the HTTP request from the body
314: // parse the header line: a property seperated with the ':' sign
315: if ((p = line.indexOf(":")) >= 0) {
316: // store a property
317: header.add(line.substring(0, p).trim(), line.substring(
318: p + 1).trim());
319: }
320: }
321:
322: return header;
323: }
324: }
|