summaryrefslogtreecommitdiff
path: root/source/de/anomic/http/httpHeader.java
blob: 5adc8ad8718082c3f238acf36ee55497ef60bc8b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
// httpHeader.java 
// -----------------------
// (C) by Michael Peter Christen; mc@anomic.de
// first published on http://www.anomic.de
// Frankfurt, Germany, 2004
// last major change: 29.04.2004
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// Using this software in any meaning (reading, learning, copying, compiling,
// running) means that you agree that the Author(s) is (are) not responsible
// for cost, loss of data or any harm that may be caused directly or indirectly
// by usage of this softare or this documentation. The usage of this software
// is on your own risk. The installation and usage (starting/running) of this
// software may allow other people or application to access your computer and
// any attached devices and is highly dependent on the configuration of the
// software which must be done by the user of the software; the author(s) is
// (are) also not responsible for proper configuration and usage of the
// software, even if provoked by documentation provided together with
// the software.
//
// Any changes to this file according to the GPL as documented in the file
// gpl.txt aside this file in the shipment you received can be done to the
// lines that follows this copyright notice here, but changes must not be
// done inside the copyright notive above. A re-distribution must contain
// the intact and unchanged copyright notice.
// Contributions and changes to the program code must be marked as such.

/*
   Documentation:
   this class implements a key-value mapping, as a hashtable
   The difference to ordinary hashtable implementations is that the
   keys are not compared by the equal() method, but are always
   treated as string and compared as
   key.uppercase().equal(.uppercase(comparator))
   You use this class by first creation of a static HashMap
   that then is used a the reverse mapping cache for every new
   instance of this class.
*/

package de.anomic.http;

import java.io.*;
import java.util.*;
import java.text.*;
import de.anomic.server.*;

public class httpHeader extends TreeMap implements Map {

    private HashMap reverseMappingCache;

    private static Collator insensitiveCollator = Collator.getInstance(Locale.US);
    static {
	insensitiveCollator.setStrength(Collator.SECONDARY);
	insensitiveCollator.setDecomposition(Collator.NO_DECOMPOSITION);
    }

    public httpHeader() {
	this(null);
    }

    public httpHeader(HashMap reverseMappingCache) {
	// this creates a new TreeMap with a case insesitive mapping
	// to provide a put-method that translates given keys into their
	// 'proper' appearance, a translation cache is needed.
	// upon instantiation, such a mapping cache can be handed over
	// If the reverseMappingCache is null, none is used
	super(insensitiveCollator);
	this.reverseMappingCache = reverseMappingCache;
    }

    public httpHeader(HashMap reverseMappingCache, File f) throws IOException {
	// creates also a case insensitive map and loads it initially
	// with some values
	super(insensitiveCollator);
	this.reverseMappingCache = reverseMappingCache;

	// load with data
	BufferedReader br = new BufferedReader(new FileReader(f));
	String line;
	int pos;
	while ((line = br.readLine()) != null) {
	    pos = line.indexOf("=");
	    if (pos >= 0) put(line.substring(0, pos), line.substring(pos + 1));
	}
	br.close();
    }

    public httpHeader(HashMap reverseMappingCache, Map othermap)  {
	// creates a case insensitive map from another map
	super(insensitiveCollator);
	this.reverseMappingCache = reverseMappingCache;

	// load with data
	if (othermap != null) this.putAll(othermap);
    }


    // we override the put method to make use of the reverseMappingCache
    public Object put(Object key, Object value) {
	String k = (String) key;
	if (reverseMappingCache == null) {
	    return super.put(k, value);
	} else {
	    if (reverseMappingCache.containsKey(k.toUpperCase())) {
		// we put in the value using the reverse mapping
		return super.put(reverseMappingCache.get(k.toUpperCase()), value);
	    } else {
		// we put in without a cached key and store the key afterwards
		Object r = super.put(k, value);
		reverseMappingCache.put(k.toUpperCase(), k);
		return r;
	    }
	}
    }

    // a convenience method to access the map with fail-over deafults
    public Object get(Object key, Object dflt) {
	Object result = get(key);
	if (result == null) return dflt; else return result;
    }

    // convenience methods for storing and loading to a file system
    public void store(File f) throws IOException {
	FileOutputStream fos = new FileOutputStream(f);
	Iterator i = keySet().iterator();
	String key, value;
	while (i.hasNext()) {
	    key = (String) i.next();
	    value = (String) get(key);
	    fos.write((key + "=" + value + "\r\n").getBytes());
	}
	fos.flush();
	fos.close();
    }

    public String toString() {
        return super.toString();
    }
    	/*
	  Connection=close
	  Content-Encoding=gzip
	  Content-Length=7281
	  Content-Type=text/html
	  Date=Mon, 05 Jan 2004 11:55:10 GMT
	  Server=Apache/1.3.26
	*/
    
    private static TimeZone GMTTimeZone = TimeZone.getTimeZone("PST");
    private static SimpleDateFormat HTTPGMTFormatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'");
    private static SimpleDateFormat EMLFormatter     = new SimpleDateFormat("dd MMM yyyy HH:mm:ss", Locale.US);
    
    public static Date parseHTTPDate(String s) {
	if ((s == null) || (s.length() < 9)) return new Date();
	s = s.trim();
	if (s.charAt(3) == ',') s = s.substring(5).trim(); // we skip the name of the day
	if (s.charAt(9) == ' ') s = s.substring(0, 7) + "20" + s.substring(7); // short year version
	if (s.charAt(2) == ',') s = s.substring(0, 2) + s.substring(3); // ommit comma after day of week
	if ((s.charAt(0) > '9') && (s.length() > 20) && (s.charAt(2) == ' ')) s = s.substring(3);
	if (s.length() > 20) s = s.substring(0, 20).trim(); // truncate remaining, since that must be wrong
        if (s.indexOf("Mrz") > 0) s.replaceAll("Mrz", "March");
	try {
	    return EMLFormatter.parse(s);
	} catch (java.text.ParseException e) {
	    //System.out.println("ERROR long version parse: " + e.getMessage() +  " at position " +  e.getErrorOffset());
	    serverLog.logError("HTTPC-header", "DATE ERROR (Parse): " + s);
	    return new Date();
	} catch (java.lang.NumberFormatException e) {
	    //System.out.println("ERROR long version parse: " + e.getMessage() +  " at position " +  e.getErrorOffset());
	    serverLog.logError("HTTPC-header", "DATE ERROR (NumberFormat): " + s);
	    new Date();
	}
	return new Date();
    }

    private Date headerDate(String kind) {
        if (containsKey(kind)) return parseHTTPDate((String) get(kind));
        else return null;
    }

    private static boolean isTextType(String type) {
        return ((type != null)  &&
		((type.startsWith("text/html")) || (type.startsWith("text/plain")))
		);
    }
    
    public boolean isTextType() {
        return isTextType(mime());
    }
    
    public String mime() {
        return (String) get("CONTENT-TYPE", "application/octet-stream");
    }
    
    public Date date() {
        return headerDate("Date");
    }
    
    public Date expires() {
        return headerDate("Expires");
    }
    
    public Date lastModified() {
        return headerDate("Last-modified");
    }
    
    public Date ifModifiedSince() {
        return headerDate("IF-MODIFIED-SINCE");
    }
    
    public long age() {
        Date lm = lastModified();
        if (lm == null) return Long.MAX_VALUE; else return (new Date()).getTime() - lm.getTime();
    }
    
    public long contentLength() {
        if (containsKey("CONTENT-LENGTH")) {
            try {
                return Long.parseLong((String) get("CONTENT-LENGTH"));
            } catch (NumberFormatException e) {
                return -1;
            }
        } else {
            return -1;
        }
    }

    public boolean gzip() {
        return ((containsKey("CONTENT-ENCODING")) &&
		(((String) get("CONTENT-ENCODING")).toUpperCase().startsWith("GZIP")));
    }
    /*
    public static void main(String[] args) {
	Collator c;
	c = Collator.getInstance(Locale.US); c.setStrength(Collator.PRIMARY);
	System.out.println("PRIMARY:   compare(abc, ABC) = " + c.compare("abc", "ABC"));
	c = Collator.getInstance(Locale.US); c.setStrength(Collator.SECONDARY);
	System.out.println("SECONDARY: compare(abc, ABC) = " + c.compare("abc", "ABC"));
	c = Collator.getInstance(Locale.US); c.setStrength(Collator.TERTIARY);
	System.out.println("TERTIARY:  compare(abc, ABC) = " + c.compare("abc", "ABC"));
	c = Collator.getInstance(Locale.US); c.setStrength(Collator.IDENTICAL);
	System.out.println("IDENTICAL: compare(abc, ABC) = " + c.compare("abc", "ABC"));
    }
    */
}