1 /*
2 * ========================================================================
3 *
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements. See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership.
7 * The ASF licenses this file to You under the Apache License, Version 2.0
8 * (the "License"); you may not use this file except in compliance with
9 * 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, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 * ========================================================================
20 */
21 package org.apache.cactus.internal.client.connector.http;
22
23 import java.io.BufferedReader;
24 import java.io.ByteArrayInputStream;
25 import java.io.ByteArrayOutputStream;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.InputStreamReader;
29 import java.io.OutputStream;
30
31 import java.net.HttpURLConnection;
32 import java.net.ProtocolException;
33 import java.net.URL;
34
35 import java.security.Permission;
36
37 import org.apache.commons.logging.Log;
38 import org.apache.commons.logging.LogFactory;
39
40 /**
41 * Wrapper class for the real <code>HttpURLConnection</code> to the test servlet
42 * that reads the complete input stream into an internal buffer on
43 * the first call to getInputStream(). This is to ensure that the test servlet
44 * is not blocked on i/o when the test caller asks for the results.
45 * <p>
46 * The wrapper returns the buffered input stream from getInputStream and
47 * delegates the rest of the calls.
48 * <p>
49 * This class is final so we don't have to provide access to protected instance
50 * variables and methods of the wrapped connection.
51 *
52 * @version $Id: AutoReadHttpURLConnection.java 239010 2004-06-19 15:10:53Z vmassol $
53 */
54 final class AutoReadHttpURLConnection extends HttpURLConnection
55 {
56 /**
57 * The logger.
58 */
59 private static final Log LOGGER =
60 LogFactory.getLog(AutoReadHttpURLConnection.class);
61
62 /**
63 * Default size of array for copying data.
64 */
65 private static final int DEFAULT_CHUNK_SIZE = 16384;
66
67 /**
68 * The wrapped connection.
69 */
70 private HttpURLConnection delegate;
71
72 /**
73 * The read input stream.
74 */
75 private InputStream streamBuffer;
76
77 /**
78 * Constructs a an <code>AutoReadHttpURLConnection</code> object from an
79 * <code>HttpURLConnection</code>.
80 *
81 * @param theConnection the original connection to wrap
82 */
83 AutoReadHttpURLConnection(HttpURLConnection theConnection)
84 {
85 super(null);
86 this.delegate = theConnection;
87 }
88
89 /**
90 * Returns an input stream containing the fully read contents of
91 * the wrapped connection's input stream.
92 *
93 * @return the input stream
94 * @exception IOException if an error occurs when reading the input stream
95 */
96 public synchronized InputStream getInputStream() throws IOException
97 {
98 // Catch IOException to log the content of the error stream
99 try
100 {
101 if (this.streamBuffer == null)
102 {
103 LOGGER.debug("Original connection = " + this.delegate);
104
105 InputStream is = this.delegate.getInputStream();
106
107 this.streamBuffer = getBufferedInputStream(is);
108 }
109 }
110 catch (IOException e)
111 {
112 logErrorStream(this.delegate.getErrorStream());
113 throw e;
114 }
115
116 return this.streamBuffer;
117 }
118
119 /**
120 * Logs the HTTP error stream (used to get more information when we fail
121 * to read from the HTTP URL connection).
122 *
123 * @param theErrorStream the error stream containing the error description
124 * @exception IOException if an error occurs when reading the input stream
125 */
126 private void logErrorStream(InputStream theErrorStream) throws IOException
127 {
128 if (theErrorStream != null)
129 {
130 // Log content of error stream
131 BufferedReader errorStream =
132 new BufferedReader(new InputStreamReader(theErrorStream));
133 String buffer;
134
135 while ((buffer = errorStream.readLine()) != null)
136 {
137 LOGGER.debug("ErrorStream [" + buffer + "]");
138 }
139 }
140 }
141
142 /**
143 * Fully read the HTTP Connection response stream until there is no
144 * more bytes to read.
145 *
146 * @param theInputStream the input stream to fully read
147 * @return the data read as a buffered input stream
148 * @exception IOException if an error occurs when reading the input stream
149 */
150 private InputStream getBufferedInputStream(InputStream theInputStream)
151 throws IOException
152 {
153 ByteArrayOutputStream os =
154 new ByteArrayOutputStream(DEFAULT_CHUNK_SIZE);
155
156 copy(theInputStream, os);
157
158 ByteArrayInputStream bais = new ByteArrayInputStream(os.toByteArray());
159
160 return bais;
161 }
162
163 /**
164 * Copies the input stream passed as parameter to the output stream also
165 * passed as parameter. The full stream is read until there is no more
166 * bytes to read.
167 *
168 * @param theInputStream the input stream to read from
169 * @param theOutputStream the output stream to write to
170 * @exception IOException if an error occurs when reading the input stream
171 */
172 private void copy(InputStream theInputStream, OutputStream theOutputStream)
173 throws IOException
174 {
175 // Only copy if there are data to copy ... The problem is that not
176 // all servers return a content-length header. If there is no header
177 // getContentLength() returns -1. It seems to work and it seems
178 // that all servers that return no content-length header also do
179 // not block on read() operations !
180 LOGGER.debug("Content-Length : [" + this.delegate.getContentLength()
181 + "]");
182
183 if (theInputStream != null && this.delegate.getContentLength() != 0)
184 {
185 byte[] buf = new byte[DEFAULT_CHUNK_SIZE];
186 int count;
187
188 while (-1 != (count = theInputStream.read(buf)))
189 {
190 // log read data
191 printReadLogs(count, buf);
192 theOutputStream.write(buf, 0, count);
193 }
194 }
195 }
196
197 /**
198 * Format log data read from socket for pretty printing (replaces
199 * asc char 10 by "\r", asc char 13 by "\n").
200 *
201 * @param theCount the number of bytes read in the buffer
202 * @param theBuffer the buffer containing the data to print
203 */
204 private void printReadLogs(int theCount, byte[] theBuffer)
205 {
206 // Log portion of read data and replace asc 10 by \r and asc
207 // 13 by /n
208 StringBuffer prefix = new StringBuffer();
209
210 for (int i = 0; i < theCount; i++)
211 {
212 if (theBuffer[i] == 10)
213 {
214 prefix.append("\\r");
215 }
216 else if (theBuffer[i] == 13)
217 {
218 prefix.append("\\n");
219 }
220 else
221 {
222 prefix.append((char) theBuffer[i]);
223 }
224 }
225
226 LOGGER.debug("Read [" + theCount + "]: [" + prefix + "]");
227 }
228
229 // Delegated methods
230
231 /**
232 * {@inheritDoc}
233 * @see java.net.HttpURLConnection#connect()
234 */
235 public void connect() throws IOException
236 {
237 this.delegate.connect();
238 }
239
240 /**
241 * {@inheritDoc}
242 * @see java.net.HttpURLConnection#getAllowUserInteraction()
243 */
244 public boolean getAllowUserInteraction()
245 {
246 return this.delegate.getAllowUserInteraction();
247 }
248
249 /**
250 * {@inheritDoc}
251 * @see java.net.HttpURLConnection#getContent()
252 */
253 public Object getContent() throws IOException
254 {
255 return this.delegate.getContent();
256 }
257
258 /**
259 * {@inheritDoc}
260 * @see java.net.HttpURLConnection#getContentEncoding()
261 */
262 public String getContentEncoding()
263 {
264 return this.delegate.getContentEncoding();
265 }
266
267 /**
268 * {@inheritDoc}
269 * @see java.net.HttpURLConnection#getContentLength()
270 */
271 public int getContentLength()
272 {
273 return this.delegate.getContentLength();
274 }
275
276 /**
277 * {@inheritDoc}
278 * @see java.net.HttpURLConnection#getContentType()
279 */
280 public String getContentType()
281 {
282 return this.delegate.getContentType();
283 }
284
285 /**
286 * {@inheritDoc}
287 * @see java.net.HttpURLConnection#getDate()
288 */
289 public long getDate()
290 {
291 return this.delegate.getDate();
292 }
293
294 /**
295 * {@inheritDoc}
296 * @see java.net.HttpURLConnection#getDefaultUseCaches()
297 */
298 public boolean getDefaultUseCaches()
299 {
300 return this.delegate.getDefaultUseCaches();
301 }
302
303 /**
304 * {@inheritDoc}
305 * @see java.net.HttpURLConnection#getDoInput()
306 */
307 public boolean getDoInput()
308 {
309 return this.delegate.getDoInput();
310 }
311
312 /**{@inheritDoc}
313 * @see java.net.HttpURLConnection#getDoOutput()
314 */
315 public boolean getDoOutput()
316 {
317 return this.delegate.getDoOutput();
318 }
319
320 /**
321 * {@inheritDoc}
322 * @see java.net.HttpURLConnection#getExpiration()
323 */
324 public long getExpiration()
325 {
326 return this.delegate.getExpiration();
327 }
328
329 /**
330 * {@inheritDoc}
331 * @see java.net.HttpURLConnection#getHeaderField(int)
332 */
333 public String getHeaderField(int thePosition)
334 {
335 return this.delegate.getHeaderField(thePosition);
336 }
337
338 /**
339 * {@inheritDoc}
340 * @see java.net.HttpURLConnection#getHeaderField(String)
341 */
342 public String getHeaderField(String theName)
343 {
344 return this.delegate.getHeaderField(theName);
345 }
346
347 /**
348 * {@inheritDoc}
349 * @see java.net.HttpURLConnection#getHeaderFieldDate(String, long)
350 */
351 public long getHeaderFieldDate(String theName, long theDefaultValue)
352 {
353 return this.delegate.getHeaderFieldDate(theName, theDefaultValue);
354 }
355
356 /**
357 * {@inheritDoc}
358 * @see java.net.HttpURLConnection#getHeaderFieldInt(String, int)
359 */
360 public int getHeaderFieldInt(String theName, int theDefaultValue)
361 {
362 return this.delegate.getHeaderFieldInt(theName, theDefaultValue);
363 }
364
365 /**
366 * {@inheritDoc}
367 * @see java.net.HttpURLConnection#getHeaderFieldKey(int)
368 */
369 public String getHeaderFieldKey(int thePosition)
370 {
371 return this.delegate.getHeaderFieldKey(thePosition);
372 }
373
374 /**
375 * {@inheritDoc}
376 * @see java.net.HttpURLConnection#getIfModifiedSince()
377 */
378 public long getIfModifiedSince()
379 {
380 return this.delegate.getIfModifiedSince();
381 }
382
383 /**
384 * {@inheritDoc}
385 * @see java.net.HttpURLConnection#getLastModified()
386 */
387 public long getLastModified()
388 {
389 return this.delegate.getLastModified();
390 }
391
392 /**
393 * {@inheritDoc}
394 * @see java.net.HttpURLConnection#getOutputStream()
395 */
396 public OutputStream getOutputStream() throws IOException
397 {
398 return this.delegate.getOutputStream();
399 }
400
401 /**
402 * {@inheritDoc}
403 * @see java.net.HttpURLConnection#getPermission()
404 */
405 public Permission getPermission() throws IOException
406 {
407 return this.delegate.getPermission();
408 }
409
410 /**
411 * {@inheritDoc}
412 * @see java.net.HttpURLConnection#getRequestProperty(String)
413 */
414 public String getRequestProperty(String theKey)
415 {
416 return this.delegate.getRequestProperty(theKey);
417 }
418
419 /**
420 * {@inheritDoc}
421 * @see java.net.HttpURLConnection#getURL()
422 */
423 public URL getURL()
424 {
425 return this.delegate.getURL();
426 }
427
428 /**
429 * {@inheritDoc}
430 * @see java.net.HttpURLConnection#getUseCaches()
431 */
432 public boolean getUseCaches()
433 {
434 return this.delegate.getUseCaches();
435 }
436
437 /**
438 * {@inheritDoc}
439 * @see java.net.HttpURLConnection#setAllowUserInteraction(boolean)
440 */
441 public void setAllowUserInteraction(boolean hasInteraction)
442 {
443 this.delegate.setAllowUserInteraction(hasInteraction);
444 }
445
446 /**
447 * {@inheritDoc}
448 * @see java.net.HttpURLConnection#setDefaultUseCaches(boolean)
449 */
450 public void setDefaultUseCaches(boolean isUsingDefaultCache)
451 {
452 this.delegate.setDefaultUseCaches(isUsingDefaultCache);
453 }
454
455 /**
456 * {@inheritDoc}
457 * @see java.net.HttpURLConnection#setDoInput(boolean)
458 */
459 public void setDoInput(boolean isInput)
460 {
461 this.delegate.setDoInput(isInput);
462 }
463
464 /**
465 * {@inheritDoc}
466 * @see java.net.HttpURLConnection#setDoOutput(boolean)
467 */
468 public void setDoOutput(boolean isOutput)
469 {
470 this.delegate.setDoOutput(isOutput);
471 }
472
473 /**
474 * {@inheritDoc}
475 * @see java.net.HttpURLConnection#setIfModifiedSince(long)
476 */
477 public void setIfModifiedSince(long isModifiedSince)
478 {
479 this.delegate.setIfModifiedSince(isModifiedSince);
480 }
481
482 /**
483 * {@inheritDoc}
484 * @see java.net.HttpURLConnection#setRequestProperty(String, String)
485 */
486 public void setRequestProperty(String theKey, String theValue)
487 {
488 this.delegate.setRequestProperty(theKey, theValue);
489 }
490
491 /**
492 * {@inheritDoc}
493 * @see java.net.HttpURLConnection#setUseCaches(boolean)
494 */
495 public void setUseCaches(boolean isUsingCaches)
496 {
497 this.delegate.setUseCaches(isUsingCaches);
498 }
499
500 /**
501 * {@inheritDoc}
502 * @see java.net.HttpURLConnection#toString()
503 */
504 public String toString()
505 {
506 return this.delegate.toString();
507 }
508
509 /**
510 * {@inheritDoc}
511 * @see java.net.HttpURLConnection#disconnect()
512 */
513 public void disconnect()
514 {
515 this.delegate.disconnect();
516 }
517
518 /**
519 * {@inheritDoc}
520 * @see java.net.HttpURLConnection#getErrorStream()
521 */
522 public InputStream getErrorStream()
523 {
524 return this.delegate.getErrorStream();
525 }
526
527 /**
528 * {@inheritDoc}
529 * @see java.net.HttpURLConnection#getRequestMethod()
530 */
531 public String getRequestMethod()
532 {
533 return this.delegate.getRequestMethod();
534 }
535
536 /**
537 * {@inheritDoc}
538 * @see java.net.HttpURLConnection#getResponseCode()
539 */
540 public int getResponseCode() throws IOException
541 {
542 return this.delegate.getResponseCode();
543 }
544
545 /**
546 * {@inheritDoc}
547 * @see java.net.HttpURLConnection#getResponseMessage()
548 */
549 public String getResponseMessage() throws IOException
550 {
551 return this.delegate.getResponseMessage();
552 }
553
554 /**
555 * {@inheritDoc}
556 * @see java.net.HttpURLConnection#setRequestMethod(String)
557 */
558 public void setRequestMethod(String theMethod) throws ProtocolException
559 {
560 this.delegate.setRequestMethod(theMethod);
561 }
562
563 /**
564 * {@inheritDoc}
565 * @see java.net.HttpURLConnection#usingProxy()
566 */
567 public boolean usingProxy()
568 {
569 return this.delegate.usingProxy();
570 }
571 }