1 /*
2 * ====================================================================
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 * ====================================================================
20 *
21 * This software consists of voluntary contributions made by many
22 * individuals on behalf of the Apache Software Foundation. For more
23 * information on the Apache Software Foundation, please see
24 * <http://www.apache.org/>.
25 *
26 */
27
28 package org.apache.http.message;
29
30 import org.apache.http.FormattedHeader;
31 import org.apache.http.Header;
32 import org.apache.http.ProtocolVersion;
33 import org.apache.http.RequestLine;
34 import org.apache.http.StatusLine;
35 import org.apache.http.annotation.Contract;
36 import org.apache.http.annotation.ThreadingBehavior;
37 import org.apache.http.util.Args;
38 import org.apache.http.util.CharArrayBuffer;
39
40 /**
41 * Interface for formatting elements of the HEAD section of an HTTP message.
42 * This is the complement to {@link LineParser}.
43 * There are individual methods for formatting a request line, a
44 * status line, or a header line. The formatting does <i>not</i> include the
45 * trailing line break sequence CR-LF.
46 * The formatted lines are returned in memory, the formatter does not depend
47 * on any specific IO mechanism.
48 * Instances of this interface are expected to be stateless and thread-safe.
49 *
50 * @since 4.0
51 */
52 @Contract(threading = ThreadingBehavior.IMMUTABLE)
53 public class BasicLineFormatter implements LineFormatter {
54
55 /**
56 * A default instance of this class, for use as default or fallback.
57 * Note that {@link BasicLineFormatter} is not a singleton, there can
58 * be many instances of the class itself and of derived classes.
59 * The instance here provides non-customized, default behavior.
60 *
61 * @deprecated (4.3) use {@link #INSTANCE}
62 */
63 @Deprecated
64 public final static BasicLineFormatter DEFAULT = new BasicLineFormatter();
65
66 public final static BasicLineFormatter INSTANCE = new BasicLineFormatter();
67
68 public BasicLineFormatter() {
69 super();
70 }
71
72 /**
73 * Obtains a buffer for formatting.
74 *
75 * @param charBuffer a buffer already available, or {@code null}
76 *
77 * @return the cleared argument buffer if there is one, or
78 * a new empty buffer that can be used for formatting
79 */
80 protected CharArrayBuffer initBuffer(final CharArrayBuffer charBuffer) {
81 CharArrayBuffer buffer = charBuffer;
82 if (buffer != null) {
83 buffer.clear();
84 } else {
85 buffer = new CharArrayBuffer(64);
86 }
87 return buffer;
88 }
89
90
91 /**
92 * Formats a protocol version.
93 *
94 * @param version the protocol version to format
95 * @param formatter the formatter to use, or
96 * {@code null} for the
97 * {@link #INSTANCE default}
98 *
99 * @return the formatted protocol version
100 */
101 public static
102 String formatProtocolVersion(final ProtocolVersion version,
103 final LineFormatter formatter) {
104 return (formatter != null ? formatter : BasicLineFormatter.INSTANCE)
105 .appendProtocolVersion(null, version).toString();
106 }
107
108
109 // non-javadoc, see interface LineFormatter
110 @Override
111 public CharArrayBuffer appendProtocolVersion(final CharArrayBuffer buffer,
112 final ProtocolVersion version) {
113 Args.notNull(version, "Protocol version");
114 // can't use initBuffer, that would clear the argument!
115 CharArrayBuffer result = buffer;
116 final int len = estimateProtocolVersionLen(version);
117 if (result == null) {
118 result = new CharArrayBuffer(len);
119 } else {
120 result.ensureCapacity(len);
121 }
122
123 result.append(version.getProtocol());
124 result.append('/');
125 result.append(Integer.toString(version.getMajor()));
126 result.append('.');
127 result.append(Integer.toString(version.getMinor()));
128
129 return result;
130 }
131
132
133 /**
134 * Guesses the length of a formatted protocol version.
135 * Needed to guess the length of a formatted request or status line.
136 *
137 * @param version the protocol version to format, or {@code null}
138 *
139 * @return the estimated length of the formatted protocol version,
140 * in characters
141 */
142 protected int estimateProtocolVersionLen(final ProtocolVersion version) {
143 return version.getProtocol().length() + 4; // room for "HTTP/1.1"
144 }
145
146
147 /**
148 * Formats a request line.
149 *
150 * @param reqline the request line to format
151 * @param formatter the formatter to use, or
152 * {@code null} for the
153 * {@link #INSTANCE default}
154 *
155 * @return the formatted request line
156 */
157 public static String formatRequestLine(final RequestLine reqline,
158 final LineFormatter formatter) {
159 return (formatter != null ? formatter : BasicLineFormatter.INSTANCE)
160 .formatRequestLine(null, reqline).toString();
161 }
162
163
164 // non-javadoc, see interface LineFormatter
165 @Override
166 public CharArrayBuffer formatRequestLine(final CharArrayBuffer buffer,
167 final RequestLine reqline) {
168 Args.notNull(reqline, "Request line");
169 final CharArrayBuffer result = initBuffer(buffer);
170 doFormatRequestLine(result, reqline);
171
172 return result;
173 }
174
175
176 /**
177 * Actually formats a request line.
178 * Called from {@link #formatRequestLine}.
179 *
180 * @param buffer the empty buffer into which to format,
181 * never {@code null}
182 * @param reqline the request line to format, never {@code null}
183 */
184 protected void doFormatRequestLine(final CharArrayBuffer buffer,
185 final RequestLine reqline) {
186 final String method = reqline.getMethod();
187 final String uri = reqline.getUri();
188
189 // room for "GET /index.html HTTP/1.1"
190 final int len = method.length() + 1 + uri.length() + 1 +
191 estimateProtocolVersionLen(reqline.getProtocolVersion());
192 buffer.ensureCapacity(len);
193
194 buffer.append(method);
195 buffer.append(' ');
196 buffer.append(uri);
197 buffer.append(' ');
198 appendProtocolVersion(buffer, reqline.getProtocolVersion());
199 }
200
201
202
203 /**
204 * Formats a status line.
205 *
206 * @param statline the status line to format
207 * @param formatter the formatter to use, or
208 * {@code null} for the
209 * {@link #INSTANCE default}
210 *
211 * @return the formatted status line
212 */
213 public static String formatStatusLine(final StatusLine statline,
214 final LineFormatter formatter) {
215 return (formatter != null ? formatter : BasicLineFormatter.INSTANCE)
216 .formatStatusLine(null, statline).toString();
217 }
218
219
220 // non-javadoc, see interface LineFormatter
221 @Override
222 public CharArrayBuffer formatStatusLine(final CharArrayBuffer buffer,
223 final StatusLine statline) {
224 Args.notNull(statline, "Status line");
225 final CharArrayBuffer result = initBuffer(buffer);
226 doFormatStatusLine(result, statline);
227
228 return result;
229 }
230
231
232 /**
233 * Actually formats a status line.
234 * Called from {@link #formatStatusLine}.
235 *
236 * @param buffer the empty buffer into which to format,
237 * never {@code null}
238 * @param statline the status line to format, never {@code null}
239 */
240 protected void doFormatStatusLine(final CharArrayBuffer buffer,
241 final StatusLine statline) {
242
243 int len = estimateProtocolVersionLen(statline.getProtocolVersion())
244 + 1 + 3 + 1; // room for "HTTP/1.1 200 "
245 final String reason = statline.getReasonPhrase();
246 if (reason != null) {
247 len += reason.length();
248 }
249 buffer.ensureCapacity(len);
250
251 appendProtocolVersion(buffer, statline.getProtocolVersion());
252 buffer.append(' ');
253 buffer.append(Integer.toString(statline.getStatusCode()));
254 buffer.append(' '); // keep whitespace even if reason phrase is empty
255 if (reason != null) {
256 buffer.append(reason);
257 }
258 }
259
260
261 /**
262 * Formats a header.
263 *
264 * @param header the header to format
265 * @param formatter the formatter to use, or
266 * {@code null} for the
267 * {@link #INSTANCE default}
268 *
269 * @return the formatted header
270 */
271 public static String formatHeader(final Header header,
272 final LineFormatter formatter) {
273 return (formatter != null ? formatter : BasicLineFormatter.INSTANCE)
274 .formatHeader(null, header).toString();
275 }
276
277
278 // non-javadoc, see interface LineFormatter
279 @Override
280 public CharArrayBuffer formatHeader(final CharArrayBuffer buffer,
281 final Header header) {
282 Args.notNull(header, "Header");
283 final CharArrayBuffer result;
284
285 if (header instanceof FormattedHeader) {
286 // If the header is backed by a buffer, re-use the buffer
287 result = ((FormattedHeader)header).getBuffer();
288 } else {
289 result = initBuffer(buffer);
290 doFormatHeader(result, header);
291 }
292 return result;
293
294 } // formatHeader
295
296
297 /**
298 * Actually formats a header.
299 * Called from {@link #formatHeader}.
300 *
301 * @param buffer the empty buffer into which to format,
302 * never {@code null}
303 * @param header the header to format, never {@code null}
304 */
305 protected void doFormatHeader(final CharArrayBuffer buffer,
306 final Header header) {
307 final String name = header.getName();
308 final String value = header.getValue();
309
310 int len = name.length() + 2;
311 if (value != null) {
312 len += value.length();
313 }
314 buffer.ensureCapacity(len);
315
316 buffer.append(name);
317 buffer.append(": ");
318 if (value != null) {
319 buffer.ensureCapacity(buffer.length() + value.length());
320 for (int valueIndex = 0; valueIndex < value.length(); valueIndex++) {
321 char valueChar = value.charAt(valueIndex);
322 if (valueChar == '\r'
323 || valueChar == '\n'
324 || valueChar == '\f'
325 || valueChar == 0x0b) {
326 valueChar = ' ';
327 }
328 buffer.append(valueChar);
329 }
330 }
331 }
332
333
334 } // class BasicLineFormatter
335