001/* 002 * ==================================================================== 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 * ==================================================================== 020 * 021 * This software consists of voluntary contributions made by many 022 * individuals on behalf of the Apache Software Foundation. For more 023 * information on the Apache Software Foundation, please see 024 * <http://www.apache.org/>. 025 * 026 */ 027 028package org.apache.http.message; 029 030import org.apache.http.HeaderElement; 031import org.apache.http.NameValuePair; 032import org.apache.http.annotation.ThreadingBehavior; 033import org.apache.http.annotation.Contract; 034import org.apache.http.util.Args; 035import org.apache.http.util.CharArrayBuffer; 036 037/** 038 * Basic implementation for formatting header value elements. 039 * Instances of this class are stateless and thread-safe. 040 * Derived classes are expected to maintain these properties. 041 * 042 * @since 4.0 043 */ 044@Contract(threading = ThreadingBehavior.IMMUTABLE) 045public class BasicHeaderValueFormatter implements HeaderValueFormatter { 046 047 /** 048 * A default instance of this class, for use as default or fallback. 049 * Note that {@link BasicHeaderValueFormatter} is not a singleton, there 050 * can be many instances of the class itself and of derived classes. 051 * The instance here provides non-customized, default behavior. 052 * 053 * @deprecated (4.3) use {@link #INSTANCE} 054 */ 055 @Deprecated 056 public final static BasicHeaderValueFormatter DEFAULT = new BasicHeaderValueFormatter(); 057 058 public final static BasicHeaderValueFormatter INSTANCE = new BasicHeaderValueFormatter(); 059 060 /** 061 * Special characters that can be used as separators in HTTP parameters. 062 * These special characters MUST be in a quoted string to be used within 063 * a parameter value . 064 */ 065 public final static String SEPARATORS = " ;,:@()<>\\\"/[]?={}\t"; 066 067 /** 068 * Unsafe special characters that must be escaped using the backslash 069 * character 070 */ 071 public final static String UNSAFE_CHARS = "\"\\"; 072 073 public BasicHeaderValueFormatter() { 074 super(); 075 } 076 077 /** 078 * Formats an array of header elements. 079 * 080 * @param elems the header elements to format 081 * @param quote {@code true} to always format with quoted values, 082 * {@code false} to use quotes only when necessary 083 * @param formatter the formatter to use, or {@code null} 084 * for the {@link #INSTANCE default} 085 * 086 * @return the formatted header elements 087 */ 088 public static 089 String formatElements(final HeaderElement[] elems, 090 final boolean quote, 091 final HeaderValueFormatter formatter) { 092 return (formatter != null ? formatter : BasicHeaderValueFormatter.INSTANCE) 093 .formatElements(null, elems, quote).toString(); 094 } 095 096 097 // non-javadoc, see interface HeaderValueFormatter 098 @Override 099 public CharArrayBuffer formatElements(final CharArrayBuffer charBuffer, 100 final HeaderElement[] elems, 101 final boolean quote) { 102 Args.notNull(elems, "Header element array"); 103 final int len = estimateElementsLen(elems); 104 CharArrayBuffer buffer = charBuffer; 105 if (buffer == null) { 106 buffer = new CharArrayBuffer(len); 107 } else { 108 buffer.ensureCapacity(len); 109 } 110 111 for (int i=0; i<elems.length; i++) { 112 if (i > 0) { 113 buffer.append(", "); 114 } 115 formatHeaderElement(buffer, elems[i], quote); 116 } 117 118 return buffer; 119 } 120 121 122 /** 123 * Estimates the length of formatted header elements. 124 * 125 * @param elems the header elements to format, or {@code null} 126 * 127 * @return a length estimate, in number of characters 128 */ 129 protected int estimateElementsLen(final HeaderElement[] elems) { 130 if ((elems == null) || (elems.length < 1)) { 131 return 0; 132 } 133 134 int result = (elems.length-1) * 2; // elements separated by ", " 135 for (final HeaderElement elem : elems) { 136 result += estimateHeaderElementLen(elem); 137 } 138 139 return result; 140 } 141 142 143 144 /** 145 * Formats a header element. 146 * 147 * @param elem the header element to format 148 * @param quote {@code true} to always format with quoted values, 149 * {@code false} to use quotes only when necessary 150 * @param formatter the formatter to use, or {@code null} 151 * for the {@link #INSTANCE default} 152 * 153 * @return the formatted header element 154 */ 155 public static 156 String formatHeaderElement(final HeaderElement elem, 157 final boolean quote, 158 final HeaderValueFormatter formatter) { 159 return (formatter != null ? formatter : BasicHeaderValueFormatter.INSTANCE) 160 .formatHeaderElement(null, elem, quote).toString(); 161 } 162 163 164 // non-javadoc, see interface HeaderValueFormatter 165 @Override 166 public CharArrayBuffer formatHeaderElement(final CharArrayBuffer charBuffer, 167 final HeaderElement elem, 168 final boolean quote) { 169 Args.notNull(elem, "Header element"); 170 final int len = estimateHeaderElementLen(elem); 171 CharArrayBuffer buffer = charBuffer; 172 if (buffer == null) { 173 buffer = new CharArrayBuffer(len); 174 } else { 175 buffer.ensureCapacity(len); 176 } 177 178 buffer.append(elem.getName()); 179 final String value = elem.getValue(); 180 if (value != null) { 181 buffer.append('='); 182 doFormatValue(buffer, value, quote); 183 } 184 185 final int parcnt = elem.getParameterCount(); 186 if (parcnt > 0) { 187 for (int i=0; i<parcnt; i++) { 188 buffer.append("; "); 189 formatNameValuePair(buffer, elem.getParameter(i), quote); 190 } 191 } 192 193 return buffer; 194 } 195 196 197 /** 198 * Estimates the length of a formatted header element. 199 * 200 * @param elem the header element to format, or {@code null} 201 * 202 * @return a length estimate, in number of characters 203 */ 204 protected int estimateHeaderElementLen(final HeaderElement elem) { 205 if (elem == null) { 206 return 0; 207 } 208 209 int result = elem.getName().length(); // name 210 final String value = elem.getValue(); 211 if (value != null) { 212 // assume quotes, but no escaped characters 213 result += 3 + value.length(); // ="value" 214 } 215 216 final int parcnt = elem.getParameterCount(); 217 if (parcnt > 0) { 218 for (int i=0; i<parcnt; i++) { 219 result += 2 + // ; <param> 220 estimateNameValuePairLen(elem.getParameter(i)); 221 } 222 } 223 224 return result; 225 } 226 227 228 229 230 /** 231 * Formats a set of parameters. 232 * 233 * @param nvps the parameters to format 234 * @param quote {@code true} to always format with quoted values, 235 * {@code false} to use quotes only when necessary 236 * @param formatter the formatter to use, or {@code null} 237 * for the {@link #INSTANCE default} 238 * 239 * @return the formatted parameters 240 */ 241 public static 242 String formatParameters(final NameValuePair[] nvps, 243 final boolean quote, 244 final HeaderValueFormatter formatter) { 245 return (formatter != null ? formatter : BasicHeaderValueFormatter.INSTANCE) 246 .formatParameters(null, nvps, quote).toString(); 247 } 248 249 250 // non-javadoc, see interface HeaderValueFormatter 251 @Override 252 public CharArrayBuffer formatParameters(final CharArrayBuffer charBuffer, 253 final NameValuePair[] nvps, 254 final boolean quote) { 255 Args.notNull(nvps, "Header parameter array"); 256 final int len = estimateParametersLen(nvps); 257 CharArrayBuffer buffer = charBuffer; 258 if (buffer == null) { 259 buffer = new CharArrayBuffer(len); 260 } else { 261 buffer.ensureCapacity(len); 262 } 263 264 for (int i = 0; i < nvps.length; i++) { 265 if (i > 0) { 266 buffer.append("; "); 267 } 268 formatNameValuePair(buffer, nvps[i], quote); 269 } 270 271 return buffer; 272 } 273 274 275 /** 276 * Estimates the length of formatted parameters. 277 * 278 * @param nvps the parameters to format, or {@code null} 279 * 280 * @return a length estimate, in number of characters 281 */ 282 protected int estimateParametersLen(final NameValuePair[] nvps) { 283 if ((nvps == null) || (nvps.length < 1)) { 284 return 0; 285 } 286 287 int result = (nvps.length-1) * 2; // "; " between the parameters 288 for (final NameValuePair nvp : nvps) { 289 result += estimateNameValuePairLen(nvp); 290 } 291 292 return result; 293 } 294 295 296 /** 297 * Formats a name-value pair. 298 * 299 * @param nvp the name-value pair to format 300 * @param quote {@code true} to always format with a quoted value, 301 * {@code false} to use quotes only when necessary 302 * @param formatter the formatter to use, or {@code null} 303 * for the {@link #INSTANCE default} 304 * 305 * @return the formatted name-value pair 306 */ 307 public static 308 String formatNameValuePair(final NameValuePair nvp, 309 final boolean quote, 310 final HeaderValueFormatter formatter) { 311 return (formatter != null ? formatter : BasicHeaderValueFormatter.INSTANCE) 312 .formatNameValuePair(null, nvp, quote).toString(); 313 } 314 315 316 // non-javadoc, see interface HeaderValueFormatter 317 @Override 318 public CharArrayBuffer formatNameValuePair(final CharArrayBuffer charBuffer, 319 final NameValuePair nvp, 320 final boolean quote) { 321 Args.notNull(nvp, "Name / value pair"); 322 final int len = estimateNameValuePairLen(nvp); 323 CharArrayBuffer buffer = charBuffer; 324 if (buffer == null) { 325 buffer = new CharArrayBuffer(len); 326 } else { 327 buffer.ensureCapacity(len); 328 } 329 330 buffer.append(nvp.getName()); 331 final String value = nvp.getValue(); 332 if (value != null) { 333 buffer.append('='); 334 doFormatValue(buffer, value, quote); 335 } 336 337 return buffer; 338 } 339 340 341 /** 342 * Estimates the length of a formatted name-value pair. 343 * 344 * @param nvp the name-value pair to format, or {@code null} 345 * 346 * @return a length estimate, in number of characters 347 */ 348 protected int estimateNameValuePairLen(final NameValuePair nvp) { 349 if (nvp == null) { 350 return 0; 351 } 352 353 int result = nvp.getName().length(); // name 354 final String value = nvp.getValue(); 355 if (value != null) { 356 // assume quotes, but no escaped characters 357 result += 3 + value.length(); // ="value" 358 } 359 return result; 360 } 361 362 363 /** 364 * Actually formats the value of a name-value pair. 365 * This does not include a leading = character. 366 * Called from {@link #formatNameValuePair formatNameValuePair}. 367 * 368 * @param buffer the buffer to append to, never {@code null} 369 * @param value the value to append, never {@code null} 370 * @param quote {@code true} to always format with quotes, 371 * {@code false} to use quotes only when necessary 372 */ 373 protected void doFormatValue(final CharArrayBuffer buffer, 374 final String value, 375 final boolean quote) { 376 377 boolean quoteFlag = quote; 378 if (!quoteFlag) { 379 for (int i = 0; (i < value.length()) && !quoteFlag; i++) { 380 quoteFlag = isSeparator(value.charAt(i)); 381 } 382 } 383 384 if (quoteFlag) { 385 buffer.append('"'); 386 } 387 for (int i = 0; i < value.length(); i++) { 388 final char ch = value.charAt(i); 389 if (isUnsafe(ch)) { 390 buffer.append('\\'); 391 } 392 buffer.append(ch); 393 } 394 if (quoteFlag) { 395 buffer.append('"'); 396 } 397 } 398 399 400 /** 401 * Checks whether a character is a {@link #SEPARATORS separator}. 402 * 403 * @param ch the character to check 404 * 405 * @return {@code true} if the character is a separator, 406 * {@code false} otherwise 407 */ 408 protected boolean isSeparator(final char ch) { 409 return SEPARATORS.indexOf(ch) >= 0; 410 } 411 412 413 /** 414 * Checks whether a character is {@link #UNSAFE_CHARS unsafe}. 415 * 416 * @param ch the character to check 417 * 418 * @return {@code true} if the character is unsafe, 419 * {@code false} otherwise 420 */ 421 protected boolean isUnsafe(final char ch) { 422 return UNSAFE_CHARS.indexOf(ch) >= 0; 423 } 424 425 426} // class BasicHeaderValueFormatter