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 package org.apache.commons.httpclient.util;
32
33 import org.apache.commons.httpclient.NameValuePair;
34
35 /***
36 * <p>
37 * This formatter produces a textual representation of attribute/value pairs. It
38 * comforms to the generic grammar and formatting rules outlined in the
39 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.1">Section 2.1</a>
40 * and
41 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6">Section 3.6</a>
42 * of <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt">RFC 2616</a>
43 * </p>
44 * <h>2.1 Augmented BNF</h>
45 * <p>
46 * Many HTTP/1.1 header field values consist of words separated by LWS or special
47 * characters. These special characters MUST be in a quoted string to be used within
48 * a parameter value (as defined in section 3.6).
49 * <p>
50 * <pre>
51 * token = 1*<any CHAR except CTLs or separators>
52 * separators = "(" | ")" | "<" | ">" | "@"
53 * | "," | ";" | ":" | "\" | <">
54 * | "/" | "[" | "]" | "?" | "="
55 * | "{" | "}" | SP | HT
56 * </pre>
57 * <p>
58 * A string of text is parsed as a single word if it is quoted using double-quote marks.
59 * </p>
60 * <pre>
61 * quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
62 * qdtext = <any TEXT except <">>
63 * </pre>
64 * <p>
65 * The backslash character ("\") MAY be used as a single-character quoting mechanism only
66 * within quoted-string and comment constructs.
67 * </p>
68 * <pre>
69 * quoted-pair = "\" CHAR
70 * </pre>
71 * <h>3.6 Transfer Codings</h>
72 * <p>
73 * Parameters are in the form of attribute/value pairs.
74 * </p>
75 * <pre>
76 * parameter = attribute "=" value
77 * attribute = token
78 * value = token | quoted-string
79 * </pre>
80 *
81 * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
82 *
83 * @since 3.0
84 */
85 public class ParameterFormatter {
86
87 /***
88 * Special characters that can be used as separators in HTTP parameters.
89 * These special characters MUST be in a quoted string to be used within
90 * a parameter value
91 */
92 private static final char[] SEPARATORS = {
93 '(', ')', '<', '>', '@',
94 ',', ';', ':', '//', '"',
95 '/', '[', ']', '?', '=',
96 '{', '}', ' ', '\t'
97 };
98
99 /***
100 * Unsafe special characters that must be escaped using the backslash
101 * character
102 */
103 private static final char[] UNSAFE_CHARS = {
104 '"', '//'
105 };
106
107 /***
108 * This flag determines whether all parameter values must be enclosed in
109 * quotation marks, even if they do not contain any special characters
110 */
111 private boolean alwaysUseQuotes = true;
112
113 /*** Default ParameterFormatter constructor */
114 public ParameterFormatter() {
115 super();
116 }
117
118 private static boolean isOneOf(char[] chars, char ch) {
119 for (int i = 0; i < chars.length; i++) {
120 if (ch == chars[i]) {
121 return true;
122 }
123 }
124 return false;
125 }
126
127 private static boolean isUnsafeChar(char ch) {
128 return isOneOf(UNSAFE_CHARS, ch);
129 }
130
131 private static boolean isSeparator(char ch) {
132 return isOneOf(SEPARATORS, ch);
133 }
134
135 /***
136 * Determines whether all parameter values must be enclosed in quotation
137 * marks, even if they do not contain any special characters
138 *
139 * @return <tt>true</tt> if all parameter values must be enclosed in
140 * quotation marks, <tt>false</tt> otherwise
141 */
142 public boolean isAlwaysUseQuotes() {
143 return alwaysUseQuotes;
144 }
145
146 /***
147 * Defines whether all parameter values must be enclosed in quotation
148 * marks, even if they do not contain any special characters
149 *
150 * @param alwaysUseQuotes
151 */
152 public void setAlwaysUseQuotes(boolean alwaysUseQuotes) {
153 this.alwaysUseQuotes = alwaysUseQuotes;
154 }
155
156 /***
157 * Formats the given parameter value using formatting rules defined
158 * in RFC 2616
159 *
160 * @param buffer output buffer
161 * @param value the parameter value to be formatted
162 * @param alwaysUseQuotes <tt>true</tt> if the parameter value must
163 * be enclosed in quotation marks, even if it does not contain any special
164 * characters<tt>, false</tt> only if the parameter value contains
165 * potentially unsafe special characters
166 */
167 public static void formatValue(
168 final StringBuffer buffer, final String value, boolean alwaysUseQuotes) {
169 if (buffer == null) {
170 throw new IllegalArgumentException("String buffer may not be null");
171 }
172 if (value == null) {
173 throw new IllegalArgumentException("Value buffer may not be null");
174 }
175 if (alwaysUseQuotes) {
176 buffer.append('"');
177 for (int i = 0; i < value.length(); i++) {
178 char ch = value.charAt(i);
179 if (isUnsafeChar(ch)) {
180 buffer.append('//');
181 }
182 buffer.append(ch);
183 }
184 buffer.append('"');
185 } else {
186 int offset = buffer.length();
187 boolean unsafe = false;
188 for (int i = 0; i < value.length(); i++) {
189 char ch = value.charAt(i);
190 if (isSeparator(ch)) {
191 unsafe = true;
192 }
193 if (isUnsafeChar(ch)) {
194 buffer.append('//');
195 }
196 buffer.append(ch);
197 }
198 if (unsafe) {
199 buffer.insert(offset, '"');
200 buffer.append('"');
201 }
202 }
203 }
204
205 /***
206 * Produces textual representaion of the attribute/value pair using
207 * formatting rules defined in RFC 2616
208 *
209 * @param buffer output buffer
210 * @param param the parameter to be formatted
211 */
212 public void format(final StringBuffer buffer, final NameValuePair param) {
213 if (buffer == null) {
214 throw new IllegalArgumentException("String buffer may not be null");
215 }
216 if (param == null) {
217 throw new IllegalArgumentException("Parameter may not be null");
218 }
219 buffer.append(param.getName());
220 String value = param.getValue();
221 if (value != null) {
222 buffer.append("=");
223 formatValue(buffer, value, this.alwaysUseQuotes);
224 }
225 }
226
227 /***
228 * Produces textual representaion of the attribute/value pair using
229 * formatting rules defined in RFC 2616
230 *
231 * @param param the parameter to be formatted
232 *
233 * @return RFC 2616 conformant textual representaion of the
234 * attribute/value pair
235 */
236 public String format(final NameValuePair param) {
237 StringBuffer buffer = new StringBuffer();
238 format(buffer, param);
239 return buffer.toString();
240 }
241
242 }