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