/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sun.jini.jeri.internal.http;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TimeZone;
/**
* Class representing HTTP message header.
*
* @author Sun Microsystems, Inc.
*
*/
class Header {
private static final SimpleDateFormat dateFormat;
static {
dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'");
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
}
private Map fields = new HashMap(5);
/**
* Creates new header with no field entries.
*/
Header() {
}
/**
* Reads in new header from the given input stream.
*/
Header(InputStream in) throws IOException {
String line = MessageReader.readLine(in);
while (line != null && line.length() > 0) {
String next = MessageReader.readLine(in);
while (next != null &&
next.length() > 0 &&
isSpaceOrTab(next.charAt(0)))
{
line += next;
next = MessageReader.readLine(in);
}
int sepidx = line.indexOf(':');
if (sepidx < 0) {
throw new HttpParseException("header line missing separator");
}
String name = line.substring(0, sepidx).trim();
String value = line.substring(sepidx + 1).trim();
if (name.length() == 0) {
throw new HttpParseException("invalid header field name");
}
addField(name, value);
line = next;
}
if (line == null) {
throw new HttpParseException("unexpected EOF in message header");
}
}
/**
* Returns value associated with named field, or null if field not present
* in this header.
*/
String getField(String name) {
return (String) fields.get(new FieldKey(name));
}
/**
* If given value is non-null, enters it as value of named field;
* otherwise, removes field (if present) from this header.
*/
void setField(String name, String value) {
FieldKey key = new FieldKey(name);
if (value != null) {
fields.put(key, value);
} else {
fields.remove(key);
}
}
/**
* Returns true if named field's associated value either contains (as an
* element of a comma-separated list) or is equal to the given value.
*/
boolean containsValue(String name, String value, boolean ignoreCase) {
String vlist = getField(name);
if (vlist != null) {
value = value.trim();
StringTokenizer tok = new StringTokenizer(vlist, ",");
while (tok.hasMoreTokens()) {
String v = tok.nextToken().trim();
if (ignoreCase ? value.equalsIgnoreCase(v) : value.equals(v)) {
return true;
}
}
}
return false;
}
/**
* Returns number of field entries in header.
*/
int size() {
return fields.size();
}
/**
* If given header is non-null, adds its field entries to this header. Any
* overlapping field values are appended to the values in this header with
* a comma in between.
*/
void merge(Header header) {
if (header != null) {
Iterator ents = header.fields.entrySet().iterator();
while (ents.hasNext()) {
Map.Entry e = (Map.Entry) ents.next();
addField(((FieldKey) e.getKey()).name, (String) e.getValue());
}
}
}
/**
* Writes header to given output stream.
*/
void write(OutputStream out) throws IOException {
Iterator ents = fields.entrySet().iterator();
while (ents.hasNext()) {
Map.Entry e = (Map.Entry) ents.next();
MessageWriter.writeLine(out,
((FieldKey) e.getKey()).name + ": " + (String) e.getValue());
}
MessageWriter.writeLine(out, "");
}
/**
* Returns formatted date string for given time.
*/
static String getDateString(long time) {
return dateFormat.format(new Date(time));
}
private static boolean isSpaceOrTab(char c) {
return c == ' ' || c == '\t';
}
/**
* Associates additional value with named field. If the field is already
* present in this header, the field's value is set to the given value
* appended to the old value with a comma in between.
*/
private void addField(String name, String value) {
if (value != null) {
FieldKey key = new FieldKey(name);
String oldv = (String) fields.get(key);
String newv = (oldv != null) ? (oldv + ", " + value) : value;
fields.put(key, newv);
}
}
/**
* Field lookup key. Field name comparisons are case-insensitive; however,
* the original field name string is retained for use when writing the
* header to a stream.
*/
private static class FieldKey {
final String name;
private final int hash;
FieldKey(String name) {
this.name = name;
hash = name.toLowerCase().hashCode();
}
public boolean equals(Object obj) {
if (obj instanceof FieldKey) {
return name.equalsIgnoreCase(((FieldKey) obj).name);
}
return false;
}
public int hashCode() {
return hash;
}
}
}