View Javadoc

    1   /*******************************************************************************
    2    * Copyright (c) 2011 Michael Mimo Moratti.
    3    *
    4    * Michael Mimo Moratti licenses this file to you under the Apache License, version 2.0
    5    * (the "License"); you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at:
    7    *     http://www.apache.org/licenses/LICENSE-2.0
    8    * Unless required by applicable law or agreed to in writing, software
    9    * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    10   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
    11   * License for the specific language governing permissions and limitations
    12   * under the License.
    13   *******************************************************************************/
    14  package ch.mimo.netty.handler.codec.icap;
    15
    16  import java.util.LinkedHashSet;
    17  import java.util.Map;
    18  import java.util.Set;
    19
    20  /**
    21   * Icap Headers
    22   *
    23   * This class provides a linked list implementation in order to store Icap headers.
    24   *
    25   * @author Michael Mimo Moratti (mimo@mimo.ch)
    26   *
    27   */
    28  // TODO add setDateheader format: "Fri, 20 May 2011 15:36:30 GMT"
    29  public final class IcapHeaders {
    30
    31  	private Entry base;
    32  	private Entry head;
    33
    34  	/**
    35  	 * The most common Icap Header names.
    36  	 *
    37  	 * @author Michael Mimo Moratti (mimo@mimo.ch)
    38  	 *
    39  	 */
    40  	public static final class Names {
    41          /**
    42           * {@code "Cache-Control"}
    43           */
    44  		public static final String CACHE_CONTROL = "Cache-Control";
    45          /**
    46           * {@code "Connection"}
    47           */
    48  		public static final String CONNECTION = "Connection";
    49          /**
    50           * {@code "Date"}
    51           */
    52  		public static final String DATE = "Date";
    53          /**
    54           * {@code "Expires"}
    55           */
    56  		public static final String EXPIRES = "Expires";
    57          /**
    58           * {@code "Pragma"}
    59           */
    60  		public static final String PRAGMA = "Pragma";
    61          /**
    62           * {@code "Trailer"}
    63           */
    64  		public static final String TRAILER = "Trailer";
    65          /**
    66           * {@code "Upgrade"}
    67           */
    68  		public static final String UPGRADE = "Upgrade";
    69          /**
    70           * {@code "Encapsulated"}
    71           */
    72  		public static final String ENCAPSULATED = "Encapsulated";
    73          /**
    74           * {@code "Authorization"}
    75           */
    76  		public static final String AUTHORIZATION = "Authorization";
    77          /**
    78           * {@code "Allow"}
    79           */
    80  		public static final String ALLOW = "Allow";
    81          /**
    82           * {@code "From"}
    83           */
    84  		public static final String FROM = "From";
    85          /**
    86           * {@code "Host"}
    87           */
    88  		public static final String HOST = "Host";
    89          /**
    90           * {@code "Referer"}
    91           */
    92  		public static final String REFERER = "Referer";
    93          /**
    94           * {@code "User-Agent"}
    95           */
    96  		public static final String USER_AGENT = "User-Agent";
    97          /**
    98           * {@code "Preview"}
    99           */
    100 		public static final String PREVIEW = "Preview";
    101
    102 		/**
    103 		 * {@code "ISTag"}
    104 		 */
    105 		public static final String ISTAG = "ISTag";
    106 	}
    107
    108 	public IcapHeaders() {
    109 	}
    110
    111 	public void clearHeaders() {
    112 		base = null;
    113 		head = null;
    114 	}
    115
    116 	/**
    117 	 * Adds a header key,value combination to the list
    118 	 * The existence of such a header name will have no impact. The header is simply added
    119 	 * to the end of the linked list.
    120 	 *
    121 	 * @param name Icap message header name
    122 	 * @param value Icap message header value. Can also be null
    123 	 */
    124 	public void addHeader(String name, Object value) {
    125 		Entry entry = new Entry(name,value);
    126 		if(base == null) {
    127 			base = entry;
    128 			head = entry;
    129 		} else {
    130 			Entry currentHead = head;
    131 			head = entry;
    132 			entry.before = currentHead;
    133 			currentHead.after = entry;
    134 		}
    135 	}
    136
    137 	/**
    138 	 * Sets one header at the end of the list.
    139 	 * Headers with the same name that are already in the list will be removed first!
    140 	 *
    141 	 * @param name Icap message header name
    142 	 * @param value Icap message header value. Can also be null
    143 	 */
    144 	public void setHeader(String name, Object value) {
    145 		removeHeader(name);
    146 		addHeader(name,value);
    147 	}
    148
    149 	/**
    150 	 * Sets one header with many values.
    151 	 * Headers with the same name that are already in the list will be removed first!
    152 	 *
    153 	 * @param name Icap message header name
    154 	 * @param values Icap message header value. Can also be null
    155 	 */
    156 	public void setHeader(String name, Iterable<?> values) {
    157 		removeHeader(name);
    158 		for(Object value : values) {
    159 			addHeader(name,value);
    160 		}
    161 	}
    162
    163 	/**
    164 	 * retrieves a header value from the list.
    165 	 * If no header exists with the given name null is returned.
    166 	 *
    167 	 * If there are multiple headers with the same name only the first occurence in the
    168 	 * list is returned.
    169 	 *
    170 	 * @param name Icap message header name
    171 	 * @return String value or null
    172 	 */
    173 	public String getHeader(String name) {
    174 		Entry entry = base;
    175 		while(entry != null) {
    176 			if(identicalKeys(entry.getKey(),name)) {
    177 				return entry.getValue();
    178 			}
    179 			entry = entry.after;
    180 		}
    181 		return null;
    182 	}
    183
    184 	/**
    185 	 * retrieves all values for one header name.
    186 	 * If no header exists with that given name an empty set is returned.
    187 	 *
    188 	 * @param name Icap message header name
    189 	 * @return Set of values from all headers with the same name, or empty set.
    190 	 */
    191 	public Set<String> getHeaders(String name) {
    192 		Set<String> values = new LinkedHashSet<String>();
    193 		Entry entry = base;
    194 		while(entry != null) {
    195 			if(identicalKeys(entry.getKey(),name)) {
    196 				values.add(entry.getValue());
    197 			}
    198 			entry = entry.after;
    199 		}
    200 		return values;
    201 	}
    202
    203 	/**
    204 	 * retrieval method for all headers that are currently in this list.
    205 	 *
    206 	 * @return Set of Map Entry instances.
    207 	 */
    208 	public Set<Map.Entry<String, String>> getHeaders() {
    209 		Set<Map.Entry<String, String>> headers = new LinkedHashSet<Map.Entry<String,String>>();
    210 		Entry entry = base;
    211 		while(entry != null) {
    212 			headers.add(entry);
    213 			entry = entry.after;
    214 		}
    215 		return headers;
    216 	}
    217
    218 	/**
    219 	 * check method to validate if a certain header does exists in the list.
    220 	 *
    221 	 * @param name Icap message header name
    222 	 * @return boolean true if the header exists.
    223 	 */
    224 	public boolean containsHeader(String name) {
    225 		return getHeader(name) != null;
    226 	}
    227
    228 	/**
    229 	 * removes all headers with the same name from the list.
    230 	 *
    231 	 * @param name Icap message header name
    232 	 */
    233 	public void removeHeader(String name) {
    234 		if(base == null) {
    235 			return;
    236 		}
    237 		Entry entry = null;
    238 		if(base.after == null) {
    239 			if(identicalKeys(base.getKey(),name)) {
    240 				base = null;
    241 				return;
    242 			}
    243 		} else {
    244 			entry = base.after;
    245 		}
    246 		while(entry != null) {
    247 			if(identicalKeys(entry.getKey(),name)) {
    248 				Entry before = entry.before;
    249 				Entry after = entry.after;
    250 				before.after = after;
    251 				after.before = before;
    252 				entry = after;
    253 			} else {
    254 				entry = entry.after;
    255 			}
    256 		}
    257 		if(identicalKeys(base.getKey(),name)) {
    258 			base = base.after;
    259 			base.before = null;
    260 		}
    261 	}
    262
    263 	/**
    264 	 * retrieval method for all header names.
    265 	 * this list is unique. If a header name has two entries the name is returned only once.
    266 	 *
    267 	 * @return unique set with all header names in the list.
    268 	 */
    269 	public Set<String> getHeaderNames() {
    270 		Set<String> names = new LinkedHashSet<String>();
    271 		Entry entry = base;
    272 		while(entry != null) {
    273 			names.add(entry.getKey());
    274 			entry = entry.after;
    275 		}
    276 		return names;
    277 	}
    278
    279 	/**
    280 	 * Convenience method to retrieve the @see {@link Integer} value from a Icap Preview header.
    281 	 * If the header does not exist the value 0 is returned.
    282 	 * If the header value cannot be parsed into a valid integer a @see {@link IcapDecodingError} is thrown.
    283 	 *
    284 	 * @return int value of preview header.
    285 	 */
    286 	public int getPreviewHeaderValue() {
    287 		String value = getHeader(Names.PREVIEW);
    288 		int result = 0;
    289 		try {
    290 			if(value != null) {
    291 				result = Integer.parseInt(value);
    292 			}
    293 		} catch(NumberFormatException nfe) {
    294 			throw new IcapDecodingError("Unable to understand the preview amount value [" + value + "]");
    295 		}
    296 		return result;
    297 	}
    298
    299 	private boolean identicalKeys(String key1, String key2) {
    300 		if(key1 != null & key2 != null) {
    301 			return key1.equalsIgnoreCase(key2);
    302 		}
    303 		return false;
    304 	}
    305
    306 	private static final class Entry implements Map.Entry<String, String> {
    307
    308 		private String key;
    309 		private String value;
    310
    311 		private Entry before, after;
    312
    313 		Entry(String key, Object value) {
    314 			IcapCodecUtil.validateHeaderName(key);
    315 			this.key = key;
    316 			if(value != null) {
    317 				this.value = value.toString();
    318 				IcapCodecUtil.validateHeaderValue(this.value);
    319 			}
    320 		}
    321
    322 		public String getKey() {
    323 			return key;
    324 		}
    325
    326 		public String getValue() {
    327 			return value;
    328 		}
    329
    330 		@Override
    331 		public String setValue(String value) {
    332 			return null;
    333 		}
    334 	}
    335
    336 	@Override
    337 	public String toString() {
    338 		StringBuilder builder = new StringBuilder();
    339 		for(String name : getHeaderNames()) {
    340 			builder.append("[" + name + "] = [" + getHeaders(name) + "]");
    341 		}
    342 		return builder.toString();
    343 	}
    344 }