KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > maverick > http > HttpResponse


1 /*
2  * SSL-Explorer
3  *
4  * Copyright (C) 2003-2006 3SP LTD. All Rights Reserved
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */

19             
20 package com.maverick.http;
21
22 import java.io.ByteArrayInputStream JavaDoc;
23 import java.io.ByteArrayOutputStream JavaDoc;
24 import java.io.EOFException JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.text.MessageFormat JavaDoc;
28 import java.util.NoSuchElementException JavaDoc;
29 import java.util.StringTokenizer JavaDoc;
30
31 /**
32  *
33  * @author Lee David Painter <a HREF="mailto:lee@3sp.com">&lt;lee@3sp.com&gt;</a>
34  */

35 public class HttpResponse extends HttpHeader {
36
37     // #ifdef DEBUG
38
static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(HttpResponse.class);
39     // #endif
40

41     protected ByteArrayOutputStream JavaDoc chunked;
42     protected String JavaDoc begin;
43
44     private String JavaDoc version = ""; //$NON-NLS-1$
45
private int status;
46     private String JavaDoc reason = ""; //$NON-NLS-1$
47

48     boolean closeConnection = false;
49     int contentLength = 0;
50
51     boolean foundContinue = false;
52     boolean release = true;
53
54     InputStream JavaDoc in = new ByteArrayInputStream JavaDoc(new byte[] {});
55
56     HttpConnection con;
57     HttpContinue cont;
58
59     public HttpResponse(HttpConnection con) throws IOException JavaDoc {
60         this(con, false);
61     }
62
63     public HttpResponse(HttpConnection con, HttpContinue cont) throws IOException JavaDoc {
64         this.con = con;
65         this.cont = cont;
66         doResponse(false);
67     }
68
69     public HttpResponse(HttpConnection con, boolean headerOnly) throws IOException JavaDoc {
70         this.con = con;
71         doResponse(headerOnly);
72     }
73
74     private void doResponse(boolean headerOnly) throws IOException JavaDoc {
75
76         do {
77
78             begin = readLine(con.getInputStream());
79
80             while (begin.trim().length() == 0) {
81                 begin = readLine(con.getInputStream());
82             }
83
84             // #ifdef DEBUG
85
log.debug(MessageFormat.format(Messages.getString("HttpResponse.startLine"), new Object JavaDoc[] { begin })); //$NON-NLS-1$
86
// #endif
87
processResponse();
88
89             if (status == 100) {
90                 if (cont != null)
91                     cont.continueRequest(con);
92                 foundContinue = true;
93
94                 String JavaDoc tmp;
95                 while (true) {
96
97                     /**
98                      * Fix for IIS web servers, they are sending additional
99                      * headers with the 100 Continue response.
100                      */

101                     tmp = readLine(con.getInputStream());
102
103                     // #ifdef DEBUG
104
log.debug(MessageFormat.format(Messages.getString("HttpResponse.received100"), new Object JavaDoc[] { tmp })); //$NON-NLS-1$
105
// #endif
106

107                     if (tmp.equals("")) //$NON-NLS-1$
108
break;
109                 }
110
111             }
112         } while (status >= 100 && status < 200);
113
114         processHeaderFields(con.getInputStream());
115
116         if (!headerOnly) {
117
118             /**
119              * LDP - Moved transfer encoding check to here, seems the more
120              * logical place to have it, plus I needed to work around the stream
121              * being overidden by the Connection: close header.
122              */

123             if (getHeaderField("transfer-encoding") != null) { //$NON-NLS-1$
124
if (getHeaderField("transfer-encoding").equalsIgnoreCase( //$NON-NLS-1$
125
"chunked")) { //$NON-NLS-1$
126
in = new ChunkedInputStream(con.getInputStream());
127                     // Remove the transfer-encoding header
128
removeFields("transfer-encoding"); //$NON-NLS-1$
129
}
130             } else if (getHeaderField("Content-Length") != null) { //$NON-NLS-1$
131
contentLength = Integer.parseInt(getHeaderField("Content-Length")); //$NON-NLS-1$
132
in = new ContentInputStream(contentLength);
133             } else if (getHeaderField("Connection") != null && //$NON-NLS-1$
134
getHeaderField("Connection").equalsIgnoreCase("close")) { //$NON-NLS-1$ //$NON-NLS-2$
135

136                 // Set the connection as unusable by others
137
con.canReuse = false;
138
139                 /**
140                  * LDP - Since the connection is being closed we could have some
141                  * content and no content-length header so just make this
142                  * responses inputstream the connections inputstream, it will go
143                  * EOF once drained.
144                  */

145                 in = con.getInputStream();
146             } else if (getHeaderField("Proxy-Connection") != null && //$NON-NLS-1$
147
getHeaderField("Proxy-Connection").equalsIgnoreCase("close")) { //$NON-NLS-1$ //$NON-NLS-2$
148

149                 // Set the connection as unusable by others
150
con.canReuse = false;
151
152                 /**
153                  * LDP - Since the connection is being closed we could have some
154                  * content and no content-length header so just make this
155                  * responses inputstream the connections inputstream, it will go
156                  * EOF once drained.
157                  */

158                 in = con.getInputStream();
159             } else {
160                 /**
161                  * No data to read so return an empty stream.
162                  */

163                 in = new ByteArrayInputStream JavaDoc(new byte[] {});
164             }
165         }
166
167         /**
168          * Finally check the connection close status again to set the canReuse
169          * flag on the HttpConnection.
170          */

171         if (getHeaderField("Connection") != null && //$NON-NLS-1$
172
getHeaderField("Connection").equalsIgnoreCase("close")) { //$NON-NLS-1$ //$NON-NLS-2$
173

174             // Set the connection as unusable by others
175
con.canReuse = false;
176
177         } else if (getHeaderField("Proxy-Connection") != null && //$NON-NLS-1$
178
getHeaderField("Proxy-Connection").equalsIgnoreCase("close")) { //$NON-NLS-1$ //$NON-NLS-2$
179

180             // Set the connection as unusable by others
181
con.canReuse = false;
182         }
183
184     }
185
186     public void close() {
187         close(true);
188     }
189
190     public boolean hasContinue() {
191         return foundContinue;
192     }
193
194     public synchronized void close(boolean release) {
195
196         /**
197          * LDP - I've added this release flag because the doAuthentication
198          * method of HttpClient was calling close to drain the connection so
199          * that it could be reused. This was correct but the connection was
200          * prematurely being put back into the connection manager, and was being
201          * reused by other threads. Since we want to keep hold of the existing
202          * connection, in case of NTLM authentication we want to drain but not
203          * release.
204          */

205         if (con == null)
206             return;
207
208         try {
209             if (in instanceof ChunkedInputStream) {
210                 ((ChunkedInputStream) in).drain();
211             } else if (in instanceof ContentInputStream) {
212                 ((ContentInputStream) in).drain();
213             }
214         } catch (IOException JavaDoc ex) {
215             // Exception during skip better close this connection
216
con.canReuse = false;
217         } finally {
218             if (release) {
219                 con.release();
220                 con = null;
221             }
222         }
223     }
224     
225     public String JavaDoc getContentType() {
226         return getHeaderField("Content-Type");
227     }
228     
229     public String JavaDoc getContentTypeWithoutParameter() {
230         String JavaDoc contentType = getContentType();
231         if(contentType == null) {
232             return null;
233         }
234         int idx = contentType.indexOf(';');
235         return idx == -1 ? contentType : contentType.substring(0, idx);
236     }
237
238     protected String JavaDoc readLine(InputStream JavaDoc in) throws IOException JavaDoc {
239         StringBuffer JavaDoc lineBuf = new StringBuffer JavaDoc();
240         int c;
241
242         while (true) {
243             c = in.read();
244
245             if (c == -1) {
246                 if (lineBuf.length() == 0)
247                     throw new EOFException JavaDoc(Messages.getString("HttpResponse.unexpectedEOF")); //$NON-NLS-1$
248

249                 break;
250             }
251
252             if (c != '\n') {
253                 lineBuf.append((char) c);
254             } else {
255                 break;
256             }
257         }
258
259         return lineBuf.toString().trim();
260     }
261
262     public String JavaDoc getStartLine() {
263         return begin;
264     }
265
266     protected void processHeaderFields(InputStream JavaDoc in) throws IOException JavaDoc {
267         clearHeaderFields();
268
269         StringBuffer JavaDoc lineBuf = new StringBuffer JavaDoc();
270         String JavaDoc lastHeaderName = null;
271         int c;
272
273         while (true) {
274             c = in.read();
275
276             if (c == -1) {
277                 throw new IOException JavaDoc(Messages.getString("HttpResponse.headerCorrupt")); //$NON-NLS-1$
278
}
279
280             if (c != '\n') {
281                 lineBuf.append((char) c);
282             } else {
283                 String JavaDoc line = lineBuf.toString().trim();
284                 lineBuf.setLength(0);
285                 if (line.length() != 0) {
286                     lastHeaderName = processNextLine(line, lastHeaderName);
287                 } else {
288                     break;
289                 }
290             }
291         }
292     }
293
294     private String JavaDoc processNextLine(String JavaDoc line, String JavaDoc lastHeaderName) throws IOException JavaDoc {
295         String JavaDoc name;
296         String JavaDoc value;
297         char c = line.charAt(0);
298
299         if ((c == ' ') || (c == '\t')) {
300             name = lastHeaderName;
301             value = getHeaderField(lastHeaderName) + " " + line.trim(); //$NON-NLS-1$
302
} else {
303             int n = line.indexOf(':');
304
305             if (n == -1) {
306                 throw new IOException JavaDoc(MessageFormat.format(Messages.getString("HttpResponse.corruptField"), new Object JavaDoc[] { line })); //$NON-NLS-1$ //$NON-NLS-2$
307
}
308
309             name = line.substring(0, n);
310             value = line.substring(n + 1).trim();
311         }
312
313         // #ifdef DEBUG
314
log.debug(MessageFormat.format(Messages.getString("HttpResponse.receivedHeader"), new Object JavaDoc[] { name, value })); //$NON-NLS-1$
315
// #endif
316
addHeaderField(name, value);
317
318         return name;
319     }
320
321     public InputStream JavaDoc getInputStream() {
322         return in;
323     }
324
325     public HttpConnection getConnection() {
326         return con;
327     }
328
329     public String JavaDoc getVersion() {
330         return version;
331     }
332
333     public int getStatus() {
334         return status;
335     }
336
337     public String JavaDoc getReason() {
338         return reason;
339     }
340
341     private void processResponse() throws IOException JavaDoc {
342         StringTokenizer JavaDoc tokens = new StringTokenizer JavaDoc(begin, WHITE_SPACE, false);
343         reason = ""; //$NON-NLS-1$
344
try {
345             version = tokens.nextToken();
346             status = Integer.parseInt(tokens.nextToken());
347             while (tokens.hasMoreTokens()) {
348                 reason += tokens.nextToken() + " "; //$NON-NLS-1$
349
}
350             reason = reason.trim();
351         } catch (NoSuchElementException JavaDoc e) {
352             throw new IOException JavaDoc(MessageFormat.format(Messages.getString("HttpResponse.failedToReadResponse"), new Object JavaDoc[] { begin })); //$NON-NLS-1$
353
} catch (NumberFormatException JavaDoc e) {
354             throw new IOException JavaDoc(MessageFormat.format(Messages.getString("HttpResponse.failedToReadResponse"), new Object JavaDoc[] { begin })); //$NON-NLS-1$
355
}
356     }
357
358     /**
359      * We will use this stream to return data that is encoded using the
360      * "Transfer-Encoding: chunked" header.
361      */

362     class ChunkedInputStream extends InputStream JavaDoc {
363
364         long chunkLength;
365         InputStream JavaDoc in;
366
367         ChunkedInputStream(InputStream JavaDoc in) throws IOException JavaDoc {
368
369             this.in = in;
370             // Read from the InputStream until we receive chunk size of zero
371
chunkLength = Long.parseLong(readLine(in), 16);
372         }
373
374         public int read() throws IOException JavaDoc {
375             byte[] b = new byte[1];
376             int read = read(b, 0, 1);
377             if (read == -1)
378                 return -1;
379             else
380                 return b[0] & 0xFF;
381         }
382
383         public void drain() throws IOException JavaDoc {
384             long len;
385             byte[] buf = new byte[65535];
386             while (contentLength > 0) {
387                 len = con.getInputStream().read(buf, 0, buf.length);
388
389                 if (contentLength > 0)
390                     contentLength -= len;
391                 else
392                     break;
393             }
394         }
395
396         public synchronized int read(byte[] buf, int off, int len) throws IOException JavaDoc {
397
398             if (chunkLength == 0 || con == null) {
399                 return -1;
400             }
401             int read;
402             int count = 0;
403             while (len > 0 && chunkLength > 0) {
404
405                 read = in.read(buf, off, (int) (len > chunkLength ? chunkLength : len));
406
407                 if (read == -1)
408                     throw new EOFException JavaDoc(Messages.getString("HttpResponse.unexpectedEOFDuringChunking")); //$NON-NLS-1$
409

410                 chunkLength -= read;
411                 len -= read;
412                 off += read;
413                 count += read;
414
415                 if (chunkLength == 0) {
416                     readLine(in);
417                     chunkLength = Long.parseLong(readLine(in), 16);
418                     if (chunkLength == 0)
419                         close();
420                 }
421             }
422
423             return count;
424         }
425
426         public synchronized void close() throws IOException JavaDoc {
427
428             if (con != null) {
429                 readLine(in);
430                 HttpResponse.this.close(true);
431             }
432         }
433     }
434
435     /**
436      * We will use this to return standard content
437      */

438     class ContentInputStream extends InputStream JavaDoc {
439
440         long contentLength;
441
442         ContentInputStream(long contentLength) {
443             this.contentLength = contentLength;
444         }
445
446         public synchronized int available() {
447             return (int) contentLength;
448         }
449
450         public synchronized long skip(long length) throws IOException JavaDoc {
451             return con.getInputStream().skip(length);
452         }
453
454         public void drain() throws IOException JavaDoc {
455             long len;
456
457             while (contentLength > 0) {
458                 len = con.getInputStream().skip(contentLength);
459
460                 if (contentLength > 0)
461                     contentLength -= len;
462                 else
463                     break;
464             }
465         }
466
467         public synchronized int read() throws IOException JavaDoc {
468
469             if (contentLength == 0 || con == null) {
470                 return -1;
471             } else {
472                 int b = con.getInputStream().read();
473                 if (b == -1)
474                     throw new EOFException JavaDoc(MessageFormat.format(Messages.getString("HttpResponse.unexpectedEOFInResponseExpected"), new Object JavaDoc[] { new Long JavaDoc(contentLength) })); //$NON-NLS-1$
475
contentLength--;
476
477                 if (contentLength == 0)
478                     close();
479
480                 return b;
481             }
482         }
483
484         public synchronized int read(byte[] buf, int off, int len) throws IOException JavaDoc {
485             if (contentLength == 0 || con == null) {
486                 return -1;
487             } else {
488                 int read = con.getInputStream().read(buf, off, (contentLength > len ? len : (int) contentLength));
489                 if (read == -1)
490                     throw new EOFException JavaDoc(MessageFormat.format(Messages.getString("HttpResponse.unexpectedEOFInResponseExpected"), new Object JavaDoc[] { new Long JavaDoc(contentLength) })); //$NON-NLS-1$ //$NON-NLS-2$
491
contentLength -= read;
492
493                 if (contentLength == 0)
494                     close();
495
496                 return read;
497             }
498
499         }
500
501         public synchronized void close() {
502             // Release the connection back to the client pool
503
if (con != null)
504                 HttpResponse.this.close(true);
505         }
506     }
507
508 }
Free Books   Free Magazines  
Popular Tags