comparison src/com/go/trove/net/HttpHeaderMap.java @ 0:3dc0c5604566

Initial checkin of blitz 2.0 fcs - no installer yet.
author Dan Creswell <dan.creswell@gmail.com>
date Sat, 21 Mar 2009 11:00:06 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:3dc0c5604566
1 /* ====================================================================
2 * Trove - Copyright (c) 1997-2000 Walt Disney Internet Group
3 * ====================================================================
4 * The Tea Software License, Version 1.1
5 *
6 * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * 3. The end-user documentation included with the redistribution,
21 * if any, must include the following acknowledgment:
22 * "This product includes software developed by the
23 * Walt Disney Internet Group (http://opensource.go.com/)."
24 * Alternately, this acknowledgment may appear in the software itself,
25 * if and wherever such third-party acknowledgments normally appear.
26 *
27 * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
28 * not be used to endorse or promote products derived from this
29 * software without prior written permission. For written
30 * permission, please contact opensource@dig.com.
31 *
32 * 5. Products derived from this software may not be called "Tea",
33 * "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
34 * "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
35 * written permission of the Walt Disney Internet Group.
36 *
37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40 * DISCLAIMED. IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
41 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
42 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
43 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
44 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
45 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
47 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48 * ====================================================================
49 *
50 * For more information about Tea, please see http://opensource.go.com/.
51 */
52
53 package com.go.trove.net;
54
55 import java.util.*;
56 import java.text.*;
57 import java.io.*;
58 import com.go.trove.io.*;
59
60 /******************************************************************************
61 *
62 * @author Brian S O'Neill
63 * @version
64 * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/07/16 <!-- $-->
65 */
66 public class HttpHeaderMap implements Map, Serializable {
67 private final static TimeZone GMT_ZONE;
68 private final static DateFormat DATE_PARSER_1;
69 private final static DateFormat DATE_PARSER_2;
70 private final static String[] DAYS;
71 private final static String[] MONTHS;
72
73 static {
74 GMT_ZONE = TimeZone.getTimeZone("GMT");
75
76 DATE_PARSER_1 =
77 new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss", Locale.US);
78 DATE_PARSER_1.setTimeZone(GMT_ZONE);
79 DATE_PARSER_1.setLenient(true);
80
81 DATE_PARSER_2 =
82 new SimpleDateFormat("EEEE, dd-MMM-yy HH:mm:ss", Locale.US);
83 DATE_PARSER_2.setTimeZone(GMT_ZONE);
84 DATE_PARSER_2.setLenient(true);
85
86 DateFormatSymbols symbols = new DateFormatSymbols(Locale.US);
87 DAYS = symbols.getShortWeekdays();
88 MONTHS = symbols.getShortMonths();
89 }
90
91 private static void appendDate(CharToByteBuffer buffer, Date d)
92 throws IOException
93 {
94 Calendar c = new GregorianCalendar(GMT_ZONE, Locale.US);
95 c.setTime(d);
96
97 buffer.append(DAYS[c.get(Calendar.DAY_OF_WEEK)]);
98 buffer.append(", ");
99 append2Digit(buffer, c.get(Calendar.DAY_OF_MONTH));
100 buffer.append(' ');
101 buffer.append(MONTHS[c.get(Calendar.MONTH)]);
102 buffer.append(' ');
103 append4Digit(buffer, c.get(Calendar.YEAR));
104 buffer.append(' ');
105 append2Digit(buffer, c.get(Calendar.HOUR_OF_DAY));
106 buffer.append(':');
107 append2Digit(buffer, c.get(Calendar.MINUTE));
108 buffer.append(':');
109 append2Digit(buffer, c.get(Calendar.SECOND));
110 buffer.append(" GMT");
111 }
112
113 private static void append2Digit(CharToByteBuffer buffer, int val)
114 throws IOException
115 {
116 buffer.append((char)(val / 10 + '0'));
117 buffer.append((char)(val % 10 + '0'));
118 }
119
120 private static void append4Digit(CharToByteBuffer buffer, int val)
121 throws IOException
122 {
123 if (val < 1000 || val > 9999) {
124 buffer.append(Integer.toString(val));
125 }
126 else {
127 buffer.append((char)(val / 1000 + '0'));
128 buffer.append((char)(val / 100 % 10 + '0'));
129 buffer.append((char)(val / 10 % 10 + '0'));
130 buffer.append((char)(val % 10 + '0'));
131 }
132 }
133
134 private Map mMap;
135
136 public HttpHeaderMap() {
137 mMap = new TreeMap(String.CASE_INSENSITIVE_ORDER);
138 }
139
140 /**
141 * Read and parse headers from the given InputStream until a blank line is
142 * reached. Except for Cookies, all other headers that contain multiple
143 * fields delimited by commas are parsed into multiple headers.
144 *
145 * @param in stream to read from
146 */
147 public void readFrom(InputStream in) throws IOException {
148 readFrom(in, new byte[80]);
149 }
150
151 /**
152 * Read and parse headers from the given InputStream until a blank line is
153 * reached. Except for Cookies, all other headers that contain multiple
154 * fields delimited by commas are parsed into multiple headers.
155 *
156 * @param in stream to read from
157 * @param buffer temporary buffer to use
158 */
159 public void readFrom(InputStream in, byte[] buffer) throws IOException {
160 String header;
161 while ((header = HttpUtils.readLine(in, buffer, 4000)) != null) {
162 if (header.length() == 0) {
163 break;
164 }
165 processHeaderLine(header);
166 }
167 }
168
169 /**
170 * Read and parse headers from the given InputStream until a blank line is
171 * reached. Except for Cookies, all other headers that contain multiple
172 * fields delimited by commas are parsed into multiple headers.
173 *
174 * @param in stream to read from
175 * @param buffer temporary buffer to use
176 */
177 public void readFrom(InputStream in, char[] buffer) throws IOException {
178 String header;
179 while ((header = HttpUtils.readLine(in, buffer, 4000)) != null) {
180 if (header.length() == 0) {
181 break;
182 }
183 processHeaderLine(header);
184 }
185 }
186
187 private void processHeaderLine(String header) {
188 int index = header.indexOf(':');
189 if (index < 0) {
190 return;
191 }
192
193 String name = header.substring(0, index);
194
195 String value;
196 int length = header.length();
197 parseValue: {
198 do {
199 if (++index >= length) {
200 value = "";
201 break parseValue;
202 }
203 } while (header.charAt(index) == ' ');
204
205 value = header.substring(index);
206 }
207
208 if ("Cookie".equalsIgnoreCase(name) ||
209 "Set-Cookie".equalsIgnoreCase(name) ||
210 (value.indexOf(',') == 3 &&
211 (value.startsWith("Mon") ||
212 value.startsWith("Tue") ||
213 value.startsWith("Wed") ||
214 value.startsWith("Thu") ||
215 value.startsWith("Fri") ||
216 value.startsWith("Sat") ||
217 value.startsWith("Sun")))) {
218
219 add(name, value.trim());
220 }
221 else {
222 // Parse up header by commas unless its a Cookie or value
223 // is a date.
224 while ((index = value.indexOf(',')) >= 0) {
225 add(name, value.substring(0, index).trim());
226 value = value.substring(index + 1);
227 }
228
229 add(name, value.trim());
230 }
231 }
232
233 public void writeTo(OutputStream out) throws IOException {
234 CharToByteBuffer buffer = new FastCharToByteBuffer
235 (new DefaultByteBuffer(), "8859_1");
236 buffer = new InternedCharToByteBuffer(buffer);
237 appendTo(buffer);
238 buffer.writeTo(out);
239 }
240
241 public void appendTo(CharToByteBuffer buffer) throws IOException {
242 Iterator it = mMap.entrySet().iterator();
243 while (it.hasNext()) {
244 Map.Entry entry = (Map.Entry)it.next();
245 Object key = entry.getKey();
246 Object value = entry.getValue();
247
248 if (key == null || value == null) {
249 continue;
250 }
251
252 String strKey = key.toString();
253
254 if (value instanceof List) {
255 Iterator values = ((List)value).iterator();
256 if ("Set-Cookie".equalsIgnoreCase(strKey)) {
257 while (values.hasNext()) {
258 buffer.append(strKey);
259 buffer.append(": ");
260 value = values.next();
261 if (value instanceof Date) {
262 appendDate(buffer, (Date)value);
263 }
264 else {
265 buffer.append(value.toString());
266 }
267 buffer.append("\r\n");
268 }
269 }
270 else {
271 // Write multiple headers together except Cookies.
272 int count = 0;
273 while (values.hasNext()) {
274 value = values.next();
275
276 if (count < 0) {
277 buffer.append("\r\n");
278 count = 0;
279 }
280 if (count++ == 0) {
281 buffer.append(strKey);
282 buffer.append(": ");
283 }
284
285 if (value instanceof Date) {
286 // Comma in date, so must isolate header.
287 if (count > 1) {
288 buffer.append("\r\n");
289 buffer.append(strKey);
290 buffer.append(": ");
291 }
292 appendDate(buffer, (Date)value);
293 count = -1;
294 }
295 else {
296 String strVal = value.toString();
297 if (strVal.indexOf(',') < 0) {
298 if (count > 1) {
299 buffer.append(',');
300 }
301 buffer.append(strVal);
302 }
303 else {
304 // Comma in value, so must isolate header.
305 if (count > 1) {
306 buffer.append("\r\n");
307 buffer.append(strKey);
308 buffer.append(": ");
309 }
310 buffer.append(strVal);
311 count = -1;
312 }
313 }
314 }
315 buffer.append("\r\n");
316 }
317 }
318 else {
319 buffer.append(strKey);
320 buffer.append(": ");
321 if (value instanceof Date) {
322 appendDate(buffer, (Date)value);
323 }
324 else {
325 buffer.append(value.toString());
326 }
327 buffer.append("\r\n");
328 }
329 }
330 }
331
332 public int size() {
333 return mMap.size();
334 }
335
336 public boolean isEmpty() {
337 return mMap.isEmpty();
338 }
339
340 public boolean containsKey(Object key) {
341 return mMap.containsKey(key);
342 }
343
344 public boolean containsValue(Object value) {
345 Iterator it = mMap.values().iterator();
346 while (it.hasNext()) {
347 Object obj = it.next();
348 if (obj instanceof List) {
349 Iterator it2 = ((List)obj).iterator();
350 while (it2.hasNext()) {
351 obj = it2.next();
352 return (value == null) ? obj == null : value.equals(obj);
353 }
354 }
355 else {
356 return (value == null) ? obj == null : value.equals(obj);
357 }
358 }
359 return false;
360 }
361
362 /**
363 * Returns the first value associated with the given key.
364 */
365 public Object get(Object key) {
366 Object value = mMap.get(key);
367 if (value instanceof List) {
368 return ((List)value).get(0);
369 }
370 else {
371 return value;
372 }
373 }
374
375 public String getString(Object key) {
376 Object obj = get(key);
377 if (obj instanceof String) {
378 return (String)obj;
379 }
380 else if (obj != null) {
381 return obj.toString();
382 }
383 return null;
384 }
385
386 public Integer getInteger(Object key) {
387 Object obj = get(key);
388 if (obj instanceof Integer) {
389 return (Integer)obj;
390 }
391 else if (obj != null) {
392 try {
393 return new Integer(obj.toString());
394 }
395 catch (NumberFormatException e) {
396 }
397 }
398 return null;
399 }
400
401 public Date getDate(Object key) {
402 Object obj = get(key);
403 if (obj instanceof Date) {
404 return (Date)obj;
405 }
406 else if (obj == null) {
407 return null;
408 }
409
410 String val = obj.toString();
411
412 // Trim after a possible ';' to separate the date
413 // from other optional data.
414 int index = val.indexOf(';');
415 if (index >= 0) {
416 val = val.substring(0, index);
417 }
418
419 Date date;
420
421 try {
422 synchronized (DATE_PARSER_1) {
423 date = DATE_PARSER_1.parse(val);
424 }
425 }
426 catch (ParseException e) {
427 try {
428 synchronized (DATE_PARSER_2) {
429 date = DATE_PARSER_2.parse(val);
430 }
431 }
432 catch (ParseException e2) {
433 return null;
434 }
435 }
436
437 return date;
438 }
439
440
441 /**
442 * Returns all the values associated with the given key. Changes to the
443 * returned list will be reflected in this map.
444 */
445 public List getAll(Object key) {
446 Object value = mMap.get(key);
447 if (value instanceof List) {
448 return ((List)value);
449 }
450 else {
451 List list = new ArrayList();
452 if (value != null || mMap.containsKey(key)) {
453 list.add(value);
454 }
455 mMap.put(key, list);
456 return list;
457 }
458 }
459
460 /**
461 * May return a list if the key previously mapped to multiple values.
462 */
463 public Object put(Object key, Object value) {
464 if (value instanceof List) {
465 return mMap.put(key, new ArrayList((List)value));
466 }
467 else {
468 return mMap.put(key, value);
469 }
470 }
471
472 /**
473 * Add more than one value associated with the given key.
474 */
475 public void add(Object key, Object value) {
476 Object existing = mMap.get(key);
477 if (existing instanceof List) {
478 if (value instanceof List) {
479 ((List)existing).addAll((List)value);
480 }
481 else {
482 ((List)existing).add(value);
483 }
484 }
485 else if (existing == null && !mMap.containsKey(key)) {
486 if (value instanceof List) {
487 mMap.put(key, new ArrayList((List)value));
488 }
489 else {
490 mMap.put(key, value);
491 }
492 }
493 else {
494 List list = new ArrayList();
495 list.add(existing);
496 if (value instanceof List) {
497 list.addAll((List)value);
498 }
499 else {
500 list.add(value);
501 }
502 mMap.put(key, list);
503 }
504 }
505
506 public Object remove(Object key) {
507 return mMap.remove(key);
508 }
509
510 public void putAll(Map map) {
511 mMap.putAll(map);
512 }
513
514 public void clear() {
515 mMap.clear();
516 }
517
518 public Set keySet() {
519 return mMap.keySet();
520 }
521
522 public Collection values() {
523 return mMap.values();
524 }
525
526 public Set entrySet() {
527 return mMap.entrySet();
528 }
529
530 public boolean equals(Object obj) {
531 return mMap.equals(obj);
532 }
533
534 public int hashCode() {
535 return mMap.hashCode();
536 }
537
538 public String toString() {
539 return super.toString() + ":" + mMap.toString();
540 }
541 }