Source Code Cross Referenced for Response.java in  » Net » SkunkDAV » HTTPClient » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Net » SkunkDAV » HTTPClient 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * @(#)Response.java					0.3-2 18/06/1999
0003:         *
0004:         *  This file is part of the HTTPClient package
0005:         *  Copyright (C) 1996-1999  Ronald Tschalär
0006:         *
0007:         *  This library is free software; you can redistribute it and/or
0008:         *  modify it under the terms of the GNU Lesser General Public
0009:         *  License as published by the Free Software Foundation; either
0010:         *  version 2 of the License, or (at your option) any later version.
0011:         *
0012:         *  This library is distributed in the hope that it will be useful,
0013:         *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0014:         *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015:         *  Lesser General Public License for more details.
0016:         *
0017:         *  You should have received a copy of the GNU Lesser General Public
0018:         *  License along with this library; if not, write to the Free
0019:         *  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
0020:         *  MA 02111-1307, USA
0021:         *
0022:         *  For questions, suggestions, bug-reports, enhancement-requests etc.
0023:         *  I may be contacted at:
0024:         *
0025:         *  ronald@innovation.ch
0026:         *
0027:         */
0028:
0029:        package HTTPClient;
0030:
0031:        import java.io.InputStream;
0032:        import java.io.SequenceInputStream;
0033:        import java.io.ByteArrayInputStream;
0034:        import java.io.IOException;
0035:        import java.io.InterruptedIOException;
0036:        import java.io.EOFException;
0037:        import java.net.URL;
0038:        import java.net.ProtocolException;
0039:        import java.util.Date;
0040:        import java.util.Vector;
0041:        import java.util.StringTokenizer;
0042:        import java.util.NoSuchElementException;
0043:
0044:        /**
0045:         * This class represents an intermediate response. It's used internally by the
0046:         * modules. When all modules have handled the response then the HTTPResponse
0047:         * fills in its fields with the data from this class.
0048:         *
0049:         * @version	0.3-2  18/06/1999
0050:         * @author	Ronald Tschalär
0051:         */
0052:
0053:        public final class Response implements  RoResponse, GlobalConstants {
0054:            /** our http connection */
0055:            private HTTPConnection connection;
0056:
0057:            /** our stream demux */
0058:            private StreamDemultiplexor stream_handler;
0059:
0060:            /** the HTTPResponse we're coupled with */
0061:            HTTPResponse http_resp;
0062:
0063:            /** the timeout for read operations */
0064:            int timeout = 0;
0065:
0066:            /** our input stream (usually from the stream demux). Push input streams
0067:             *  onto this if necessary. */
0068:            public InputStream inp_stream;
0069:
0070:            /** our response input stream from the stream demux */
0071:            private RespInputStream resp_inp_stream = null;
0072:
0073:            /** the method used in the request */
0074:            private String method;
0075:
0076:            /** the resource in the request (for debugging purposes) */
0077:            String resource;
0078:
0079:            /** was a proxy used for the request? */
0080:            private boolean used_proxy;
0081:
0082:            /** did the request contain an entity? */
0083:            private boolean sent_entity;
0084:
0085:            /** the status code returned. */
0086:            int StatusCode = 0;
0087:
0088:            /** the reason line associated with the status code. */
0089:            String ReasonLine;
0090:
0091:            /** the HTTP version of the response. */
0092:            String Version;
0093:
0094:            /** the final URI of the document. */
0095:            URI EffectiveURI = null;
0096:
0097:            /** any headers which were received and do not fit in the above list. */
0098:            CIHashtable Headers = new CIHashtable();
0099:
0100:            /** any trailers which were received and do not fit in the above list. */
0101:            CIHashtable Trailers = new CIHashtable();
0102:
0103:            /** the message length of the response if either there is no data (in which
0104:             *  case ContentLength=0) or if the message length is controlled by a
0105:             *  Content-Length header. If neither of these, then it's -1  */
0106:            int ContentLength = -1;
0107:
0108:            /** this indicates how the length of the entity body is determined */
0109:            int cd_type = CD_HDRS;
0110:
0111:            /** the data (body) returned. */
0112:            byte[] Data = null;
0113:
0114:            /** signals if we in the process of reading the headers */
0115:            boolean reading_headers = false;
0116:
0117:            /** signals if we have got and parsed the headers yet */
0118:            boolean got_headers = false;
0119:
0120:            /** signals if we have got and parsed the trailers yet */
0121:            boolean got_trailers = false;
0122:
0123:            /** remembers any exception received while reading/parsing headers */
0124:            private IOException exception = null;
0125:
0126:            /** should this response be handled further? */
0127:            boolean final_resp = false;
0128:
0129:            // Constructors
0130:
0131:            /**
0132:             * Creates a new Response and registers it with the stream-demultiplexor.
0133:             */
0134:            Response(Request request, boolean used_proxy,
0135:                    StreamDemultiplexor stream_handler) throws IOException {
0136:                this .connection = request.getConnection();
0137:                this .method = request.getMethod();
0138:                this .resource = request.getRequestURI();
0139:                this .used_proxy = used_proxy;
0140:                this .stream_handler = stream_handler;
0141:                sent_entity = (request.getData() != null) ? true : false;
0142:
0143:                stream_handler.register(this , request);
0144:                resp_inp_stream = stream_handler.getStream(this );
0145:                inp_stream = resp_inp_stream;
0146:            }
0147:
0148:            /**
0149:             * Creates a new Response that reads from the given stream. This is
0150:             * used for the CONNECT subrequest which is used in establishing an
0151:             * SSL tunnel through a proxy.
0152:             *
0153:             * @param request the subrequest
0154:             * @param is      the input stream from which to read the headers and
0155:             *                data.
0156:             */
0157:            Response(Request request, InputStream is) throws IOException {
0158:                this .connection = request.getConnection();
0159:                this .method = request.getMethod();
0160:                this .resource = request.getRequestURI();
0161:                used_proxy = false;
0162:                stream_handler = null;
0163:                sent_entity = (request.getData() != null) ? true : false;
0164:                inp_stream = is;
0165:            }
0166:
0167:            /**
0168:             * Create a new response with the given info. This is used when
0169:             * creating a response in a requestHandler().
0170:             *
0171:             * <P>If <var>data</var> is not null then that is used; else if the
0172:             * <var>is</var> is not null that is used; else the entity is empty.
0173:             * If the input stream is used then <var>cont_len</var> specifies
0174:             * the length of the data that can be read from it, or -1 if unknown.
0175:             *
0176:             * @param version  the response version (such as "HTTP/1.1")
0177:             * @param status   the status code
0178:             * @param reason   the reason line
0179:             * @param headers  the response headers
0180:             * @param data     the response entity
0181:             * @param is       the response entity as an InputStream
0182:             * @param cont_len the length of the data in the InputStream
0183:             */
0184:            public Response(String version, int status, String reason,
0185:                    NVPair[] headers, byte[] data, InputStream is, int cont_len) {
0186:                this .Version = version;
0187:                this .StatusCode = status;
0188:                this .ReasonLine = reason;
0189:                if (headers != null)
0190:                    for (int idx = 0; idx < headers.length; idx++)
0191:                        setHeader(headers[idx].getName(), headers[idx]
0192:                                .getValue());
0193:                if (data != null)
0194:                    this .Data = data;
0195:                else if (is == null)
0196:                    this .Data = new byte[0];
0197:                else {
0198:                    this .inp_stream = is;
0199:                    ContentLength = cont_len;
0200:                }
0201:
0202:                got_headers = true;
0203:                got_trailers = true;
0204:            }
0205:
0206:            // Methods
0207:
0208:            /**
0209:             * give the status code for this request. These are grouped as follows:
0210:             * <UL>
0211:             *   <LI> 1xx - Informational (new in HTTP/1.1)
0212:             *   <LI> 2xx - Success
0213:             *   <LI> 3xx - Redirection
0214:             *   <LI> 4xx - Client Error
0215:             *   <LI> 5xx - Server Error
0216:             * </UL>
0217:             *
0218:             * @exception IOException If any exception occurs on the socket.
0219:             */
0220:            public final int getStatusCode() throws IOException {
0221:                if (!got_headers)
0222:                    getHeaders(true);
0223:                return StatusCode;
0224:            }
0225:
0226:            /**
0227:             * give the reason line associated with the status code.
0228:             *
0229:             * @exception IOException If any exception occurs on the socket.
0230:             */
0231:            public final String getReasonLine() throws IOException {
0232:                if (!got_headers)
0233:                    getHeaders(true);
0234:                return ReasonLine;
0235:            }
0236:
0237:            /**
0238:             * get the HTTP version used for the response.
0239:             *
0240:             * @exception IOException If any exception occurs on the socket.
0241:             */
0242:            public final String getVersion() throws IOException {
0243:                if (!got_headers)
0244:                    getHeaders(true);
0245:                return Version;
0246:            }
0247:
0248:            /**
0249:             * Wait for either a '100 Continue' or an error.
0250:             *
0251:             * @return the return status.
0252:             */
0253:            int getContinue() throws IOException {
0254:                getHeaders(false);
0255:                return StatusCode;
0256:            }
0257:
0258:            /**
0259:             * get the final URI of the document. This is set if the original
0260:             * request was deferred via the "moved" (301, 302, or 303) return
0261:             * status.
0262:             *
0263:             * @return the new URI, or null if not redirected
0264:             * @exception IOException If any exception occurs on the socket.
0265:             */
0266:            public final URI getEffectiveURI() throws IOException {
0267:                if (!got_headers)
0268:                    getHeaders(true);
0269:                return EffectiveURI;
0270:            }
0271:
0272:            /**
0273:             * set the final URI of the document. This is only for internal use.
0274:             */
0275:            public void setEffectiveURI(URI final_uri) {
0276:                EffectiveURI = final_uri;
0277:            }
0278:
0279:            /**
0280:             * get the final URL of the document. This is set if the original
0281:             * request was deferred via the "moved" (301, 302, or 303) return
0282:             * status.
0283:             *
0284:             * @exception IOException If any exception occurs on the socket.
0285:             * @deprecated use getEffectiveURI() instead
0286:             * @see #getEffectiveURI
0287:             */
0288:            public final URL getEffectiveURL() throws IOException {
0289:                return getEffectiveURI().toURL();
0290:            }
0291:
0292:            /**
0293:             * set the final URL of the document. This is only for internal use.
0294:             *
0295:             * @deprecated use setEffectiveURI() instead
0296:             * @see #setEffectiveURI
0297:             */
0298:            public void setEffectiveURL(URL final_url) {
0299:                try {
0300:                    setEffectiveURI(new URI(final_url));
0301:                } catch (ParseException pe) {
0302:                    throw new Error(pe.toString());
0303:                } // shouldn't happen
0304:            }
0305:
0306:            /**
0307:             * retrieves the field for a given header.
0308:             *
0309:             * @param  hdr the header name.
0310:             * @return the value for the header, or null if non-existent.
0311:             * @exception IOException If any exception occurs on the socket.
0312:             */
0313:            public String getHeader(String hdr) throws IOException {
0314:                if (!got_headers)
0315:                    getHeaders(true);
0316:                return (String) Headers.get(hdr.trim());
0317:            }
0318:
0319:            /**
0320:             * retrieves the field for a given header. The value is parsed as an
0321:             * int.
0322:             *
0323:             * @param  hdr the header name.
0324:             * @return the value for the header if the header exists
0325:             * @exception NumberFormatException if the header's value is not a number
0326:             *                                  or if the header does not exist.
0327:             * @exception IOException if any exception occurs on the socket.
0328:             */
0329:            public int getHeaderAsInt(String hdr) throws IOException,
0330:                    NumberFormatException {
0331:                return Integer.parseInt(getHeader(hdr));
0332:            }
0333:
0334:            /**
0335:             * retrieves the field for a given header. The value is parsed as a
0336:             * date; if this fails it is parsed as a long representing the number
0337:             * of seconds since 12:00 AM, Jan 1st, 1970. If this also fails an
0338:             * IllegalArgumentException is thrown.
0339:             * 
0340:             * <P>Note: When sending dates use Util.httpDate().
0341:             *
0342:             * @param  hdr the header name.
0343:             * @return the value for the header, or null if non-existent.
0344:             * @exception IOException If any exception occurs on the socket.
0345:             * @exception IllegalArgumentException If the header cannot be parsed
0346:             *            as a date or time.
0347:             */
0348:            public Date getHeaderAsDate(String hdr) throws IOException,
0349:                    IllegalArgumentException {
0350:                String raw_date = getHeader(hdr);
0351:                if (raw_date == null)
0352:                    return null;
0353:
0354:                // asctime() format is missing an explicit GMT specifier
0355:                if (raw_date.toUpperCase().indexOf("GMT") == -1)
0356:                    raw_date += " GMT";
0357:
0358:                Date date;
0359:
0360:                try {
0361:                    date = new Date(raw_date);
0362:                } catch (IllegalArgumentException iae) {
0363:                    long time;
0364:                    try {
0365:                        time = Long.parseLong(raw_date);
0366:                    } catch (NumberFormatException nfe) {
0367:                        throw iae;
0368:                    }
0369:                    if (time < 0)
0370:                        time = 0;
0371:                    date = new Date(time * 1000L);
0372:                }
0373:
0374:                return date;
0375:            }
0376:
0377:            /**
0378:             * Set a header field in the list of headers. If the header already
0379:             * exists it will be overwritten; otherwise the header will be added
0380:             * to the list. This is used by some modules when they process the
0381:             * header so that higher level stuff doesn't get confused when the
0382:             * headers and data don't match.
0383:             *
0384:             * @param header The name of header field to set.
0385:             * @param value  The value to set the field to.
0386:             */
0387:            public void setHeader(String header, String value) {
0388:                Headers.put(header.trim(), value.trim());
0389:            }
0390:
0391:            /**
0392:             * Removes a header field from the list of headers. This is used by
0393:             * some modules when they process the header so that higher level stuff
0394:             * doesn't get confused when the headers and data don't match.
0395:             *
0396:             * @param header The name of header field to remove.
0397:             */
0398:            public void deleteHeader(String header) {
0399:                Headers.remove(header.trim());
0400:            }
0401:
0402:            /**
0403:             * Retrieves the field for a given trailer. Note that this should not
0404:             * be invoked until all the response data has been read. If invoked
0405:             * before, it will force the data to be read via <code>getData()</code>.
0406:             *
0407:             * @param  trailer the trailer name.
0408:             * @return the value for the trailer, or null if non-existent.
0409:             * @exception IOException If any exception occurs on the socket.
0410:             */
0411:            public String getTrailer(String trailer) throws IOException {
0412:                if (!got_trailers)
0413:                    getTrailers();
0414:                return (String) Trailers.get(trailer.trim());
0415:            }
0416:
0417:            /**
0418:             * Retrieves the field for a given tailer. The value is parsed as an
0419:             * int.
0420:             *
0421:             * @param  trailer the tailer name.
0422:             * @return the value for the trailer if the trailer exists
0423:             * @exception NumberFormatException if the trailer's value is not a number
0424:             *                                  or if the trailer does not exist.
0425:             * @exception IOException if any exception occurs on the socket.
0426:             */
0427:            public int getTrailerAsInt(String trailer) throws IOException,
0428:                    NumberFormatException {
0429:                return Integer.parseInt(getTrailer(trailer));
0430:            }
0431:
0432:            /**
0433:             * Retrieves the field for a given trailer. The value is parsed as a
0434:             * date; if this fails it is parsed as a long representing the number
0435:             * of seconds since 12:00 AM, Jan 1st, 1970. If this also fails an
0436:             * IllegalArgumentException is thrown.
0437:             *
0438:             * <P>Note: When sending dates use Util.httpDate().
0439:             *
0440:             * @param  trailer the trailer name.
0441:             * @return the value for the trailer, or null if non-existent.
0442:             * @exception IllegalArgumentException if the trailer's value is neither a
0443:             *            legal date nor a number.
0444:             * @exception IOException if any exception occurs on the socket.
0445:             * @exception IllegalArgumentException If the header cannot be parsed
0446:             *            as a date or time.
0447:             */
0448:            public Date getTrailerAsDate(String trailer) throws IOException,
0449:                    IllegalArgumentException {
0450:                String raw_date = getTrailer(trailer);
0451:                if (raw_date == null)
0452:                    return null;
0453:
0454:                // asctime() format is missing an explicit GMT specifier
0455:                if (raw_date.toUpperCase().indexOf("GMT") == -1)
0456:                    raw_date += " GMT";
0457:
0458:                Date date;
0459:
0460:                try {
0461:                    date = new Date(raw_date);
0462:                } catch (IllegalArgumentException iae) {
0463:                    // some servers erroneously send a number, so let's try that
0464:                    long time;
0465:                    try {
0466:                        time = Long.parseLong(raw_date);
0467:                    } catch (NumberFormatException nfe) {
0468:                        throw iae;
0469:                    } // give up
0470:                    if (time < 0)
0471:                        time = 0;
0472:                    date = new Date(time * 1000L);
0473:                }
0474:
0475:                return date;
0476:            }
0477:
0478:            /**
0479:             * Set a trailer field in the list of trailers. If the trailer already
0480:             * exists it will be overwritten; otherwise the trailer will be added
0481:             * to the list. This is used by some modules when they process the
0482:             * trailer so that higher level stuff doesn't get confused when the
0483:             * trailer and data don't match.
0484:             *
0485:             * @param trailer The name of trailer field to set.
0486:             * @param value   The value to set the field to.
0487:             */
0488:            public void setTrailer(String trailer, String value) {
0489:                Trailers.put(trailer.trim(), value.trim());
0490:            }
0491:
0492:            /**
0493:             * Removes a trailer field from the list of trailers. This is used by
0494:             * some modules when they process the trailer so that higher level stuff
0495:             * doesn't get confused when the trailers and data don't match.
0496:             *
0497:             * @param trailer The name of trailer field to remove.
0498:             */
0499:            public void deleteTrailer(String trailer) {
0500:                Trailers.remove(trailer.trim());
0501:            }
0502:
0503:            /**
0504:             * Reads all the response data into a byte array. Note that this method
0505:             * won't return until <em>all</em> the data has been received (so for
0506:             * instance don't invoke this method if the server is doing a server
0507:             * push). If getInputStream() had been previously called then this method
0508:             * only returns any unread data remaining on the stream and then closes
0509:             * it.
0510:             *
0511:             * @see #getInputStream()
0512:             * @return an array containing the data (body) returned. If no data
0513:             *         was returned then it's set to a zero-length array.
0514:             * @exception IOException If any io exception occured while reading
0515:             *			      the data
0516:             */
0517:            public synchronized byte[] getData() throws IOException {
0518:                if (!got_headers)
0519:                    getHeaders(true);
0520:
0521:                if (Data == null) {
0522:                    try {
0523:                        readResponseData(inp_stream);
0524:                    } catch (InterruptedIOException ie) // don't intercept
0525:                    {
0526:                        throw ie;
0527:                    } catch (IOException ioe) {
0528:                        if (DebugResp) {
0529:                            System.err.print("Resp:  (" + inp_stream.hashCode()
0530:                                    + ") (" + Thread.currentThread() + ")");
0531:                            ioe.printStackTrace();
0532:                        }
0533:                        try {
0534:                            inp_stream.close();
0535:                        } catch (Exception e) {
0536:                        }
0537:                        throw ioe;
0538:                    }
0539:
0540:                    inp_stream.close();
0541:                }
0542:
0543:                return Data;
0544:            }
0545:
0546:            /**
0547:             * Gets an input stream from which the returned data can be read. Note
0548:             * that if getData() had been previously called it will actually return
0549:             * a ByteArrayInputStream created from that data.
0550:             *
0551:             * @see #getData()
0552:             * @return the InputStream.
0553:             * @exception IOException If any exception occurs on the socket.
0554:             */
0555:            public synchronized InputStream getInputStream() throws IOException {
0556:                if (!got_headers)
0557:                    getHeaders(true);
0558:
0559:                if (Data == null)
0560:                    return inp_stream;
0561:                else
0562:                    return new ByteArrayInputStream(Data);
0563:            }
0564:
0565:            /**
0566:             * Some responses such as those from a HEAD or with certain status
0567:             * codes don't have an entity. This is detected by the client and
0568:             * can be queried here. Note that this won't try to do a read() on
0569:             * the input stream (it will however cause the headers to be read
0570:             * and parsed if not already done).
0571:             *
0572:             * @return true if the response has an entity, false otherwise
0573:             * @since V0.3-1
0574:             */
0575:            public synchronized boolean hasEntity() throws IOException {
0576:                if (!got_headers)
0577:                    getHeaders(true);
0578:
0579:                return (cd_type != CD_0);
0580:            }
0581:
0582:            // Helper Methods
0583:
0584:            /**
0585:             * Gets and parses the headers. Sets up Data if no data will be received.
0586:             *
0587:             * @param skip_cont  if true skips over '100 Continue' status codes.
0588:             * @exception IOException If any exception occurs while reading the headers.
0589:             */
0590:            private synchronized void getHeaders(boolean skip_cont)
0591:                    throws IOException {
0592:                if (got_headers)
0593:                    return;
0594:                if (exception != null)
0595:                    throw (IOException) exception.fillInStackTrace();
0596:
0597:                reading_headers = true;
0598:                try {
0599:                    do {
0600:                        Headers.clear(); // clear any headers from 100 Continue
0601:                        String headers = readResponseHeaders(inp_stream);
0602:                        parseResponseHeaders(headers);
0603:                    } while ((StatusCode == 100 && skip_cont) || // Continue
0604:                            (StatusCode > 101 && StatusCode < 200)); // Unknown
0605:                } catch (IOException ioe) {
0606:                    if (!(ioe instanceof  InterruptedIOException))
0607:                        exception = ioe;
0608:                    if (ioe instanceof  ProtocolException) // thrown internally
0609:                    {
0610:                        cd_type = CD_CLOSE;
0611:                        if (stream_handler != null)
0612:                            stream_handler.markForClose(this );
0613:                    }
0614:                    throw ioe;
0615:                } finally {
0616:                    reading_headers = false;
0617:                }
0618:                if (StatusCode == 100)
0619:                    return;
0620:
0621:                // parse the Content-Length header
0622:
0623:                int cont_len = -1;
0624:                String cl_hdr = (String) Headers.get("Content-Length");
0625:                if (cl_hdr != null) {
0626:                    try {
0627:                        cont_len = Integer.parseInt(cl_hdr);
0628:                        if (cont_len < 0)
0629:                            throw new NumberFormatException();
0630:                    } catch (NumberFormatException nfe) {
0631:                        throw new ProtocolException(
0632:                                "Invalid Content-length header" + " received: "
0633:                                        + cl_hdr);
0634:                    }
0635:                }
0636:
0637:                // parse the Transfer-Encoding header
0638:
0639:                boolean te_chunked = false, te_is_identity = true, ct_mpbr = false;
0640:                Vector te_hdr = null;
0641:                try {
0642:                    te_hdr = Util.parseHeader((String) Headers
0643:                            .get("Transfer-Encoding"));
0644:                } catch (ParseException pe) {
0645:                }
0646:                if (te_hdr != null) {
0647:                    te_chunked = ((HttpHeaderElement) te_hdr.lastElement())
0648:                            .getName().equalsIgnoreCase("chunked");
0649:                    for (int idx = 0; idx < te_hdr.size(); idx++)
0650:                        if (((HttpHeaderElement) te_hdr.elementAt(idx))
0651:                                .getName().equalsIgnoreCase("identity"))
0652:                            te_hdr.removeElementAt(idx--);
0653:                        else
0654:                            te_is_identity = false;
0655:                }
0656:
0657:                // parse Content-Type header
0658:
0659:                try {
0660:                    String hdr;
0661:                    if ((hdr = (String) Headers.get("Content-Type")) != null) {
0662:                        Vector phdr = Util.parseHeader(hdr);
0663:                        ct_mpbr = phdr.contains(new HttpHeaderElement(
0664:                                "multipart/byteranges"))
0665:                                || phdr.contains(new HttpHeaderElement(
0666:                                        "multipart/x-byteranges"));
0667:                    }
0668:                } catch (ParseException pe) {
0669:                }
0670:
0671:                // now determine content-delimiter
0672:
0673:                if (StatusCode < 200 || StatusCode == 204 || StatusCode == 205
0674:                        || StatusCode == 304) {
0675:                    cd_type = CD_0;
0676:                } else if (te_chunked) {
0677:                    cd_type = CD_CHUNKED;
0678:
0679:                    te_hdr.removeElementAt(te_hdr.size() - 1);
0680:                    if (te_hdr.size() > 0)
0681:                        setHeader("Transfer-Encoding", Util
0682:                                .assembleHeader(te_hdr));
0683:                    else
0684:                        deleteHeader("Transfer-Encoding");
0685:                } else if (cont_len != -1 && te_is_identity)
0686:                    cd_type = CD_CONTLEN;
0687:                else if (ct_mpbr && te_is_identity)
0688:                    cd_type = CD_MP_BR;
0689:                else if (!method.equals("HEAD")) {
0690:                    cd_type = CD_CLOSE;
0691:                    if (stream_handler != null)
0692:                        stream_handler.markForClose(this );
0693:
0694:                    if (Version.equals("HTTP/0.9")) {
0695:                        inp_stream = new SequenceInputStream(
0696:                                new ByteArrayInputStream(Data), inp_stream);
0697:                        Data = null;
0698:                    }
0699:                }
0700:
0701:                if (cd_type == CD_CONTLEN)
0702:                    ContentLength = cont_len;
0703:                else
0704:                    deleteHeader("Content-Length"); // Content-Length is not valid in this case
0705:
0706:                /* We treat HEAD specially down here because the above code needs
0707:                 * to know whether to remove the Content-length header or not.
0708:                 */
0709:                if (method.equals("HEAD"))
0710:                    cd_type = CD_0;
0711:
0712:                if (cd_type == CD_0) {
0713:                    ContentLength = 0;
0714:                    Data = new byte[0];
0715:                    inp_stream.close(); // we will not receive any more data
0716:                }
0717:
0718:                if (DebugResp) {
0719:                    System.err
0720:                            .println("Resp:  Response entity delimiter: "
0721:                                    + (cd_type == CD_0 ? "No Entity"
0722:                                            : cd_type == CD_CLOSE ? "Close"
0723:                                                    : cd_type == CD_CONTLEN ? "Content-Length"
0724:                                                            : cd_type == CD_CHUNKED ? "Chunked"
0725:                                                                    : cd_type == CD_MP_BR ? "Multipart"
0726:                                                                            : "???")
0727:                                    + " (" + inp_stream.hashCode() + ") ("
0728:                                    + Thread.currentThread() + ")");
0729:                }
0730:
0731:                // remove erroneous connection tokens
0732:
0733:                if (connection.ServerProtocolVersion >= HTTP_1_1)
0734:                    deleteHeader("Proxy-Connection");
0735:                else // HTTP/1.0
0736:                {
0737:                    if (connection.getProxyHost() != null)
0738:                        deleteHeader("Connection");
0739:                    else
0740:                        deleteHeader("Proxy-Connection");
0741:
0742:                    Vector pco;
0743:                    try {
0744:                        pco = Util.parseHeader((String) Headers
0745:                                .get("Connection"));
0746:                    } catch (ParseException pe) {
0747:                        pco = null;
0748:                    }
0749:
0750:                    if (pco != null) {
0751:                        for (int idx = 0; idx < pco.size(); idx++) {
0752:                            String name = ((HttpHeaderElement) pco
0753:                                    .elementAt(idx)).getName();
0754:                            if (!name.equalsIgnoreCase("keep-alive")) {
0755:                                pco.removeElementAt(idx);
0756:                                deleteHeader(name);
0757:                                idx--;
0758:                            }
0759:                        }
0760:
0761:                        if (pco.size() > 0)
0762:                            setHeader("Connection", Util.assembleHeader(pco));
0763:                        else
0764:                            deleteHeader("Connection");
0765:                    }
0766:
0767:                    try {
0768:                        pco = Util.parseHeader((String) Headers
0769:                                .get("Proxy-Connection"));
0770:                    } catch (ParseException pe) {
0771:                        pco = null;
0772:                    }
0773:
0774:                    if (pco != null) {
0775:                        for (int idx = 0; idx < pco.size(); idx++) {
0776:                            String name = ((HttpHeaderElement) pco
0777:                                    .elementAt(idx)).getName();
0778:                            if (!name.equalsIgnoreCase("keep-alive")) {
0779:                                pco.removeElementAt(idx);
0780:                                deleteHeader(name);
0781:                                idx--;
0782:                            }
0783:                        }
0784:
0785:                        if (pco.size() > 0)
0786:                            setHeader("Proxy-Connection", Util
0787:                                    .assembleHeader(pco));
0788:                        else
0789:                            deleteHeader("Proxy-Connection");
0790:                    }
0791:                }
0792:
0793:                // this must be set before we invoke handleFirstRequest()
0794:                got_headers = true;
0795:
0796:                // special handling if this is the first response received
0797:                if (isFirstResponse) {
0798:                    if (!connection.handleFirstRequest(req, this )) {
0799:                        // got a buggy server - need to redo the request
0800:                        Response resp;
0801:                        try {
0802:                            resp = connection.sendRequest(req, timeout);
0803:                        } catch (ModuleException me) {
0804:                            throw new IOException(me.toString());
0805:                        }
0806:                        resp.getVersion();
0807:
0808:                        this .StatusCode = resp.StatusCode;
0809:                        this .ReasonLine = resp.ReasonLine;
0810:                        this .Version = resp.Version;
0811:                        this .EffectiveURI = resp.EffectiveURI;
0812:                        this .ContentLength = resp.ContentLength;
0813:                        this .Headers = resp.Headers;
0814:                        this .inp_stream = resp.inp_stream;
0815:                        this .Data = resp.Data;
0816:
0817:                        req = null;
0818:                    }
0819:                }
0820:            }
0821:
0822:            /* these are external to readResponseHeaders() because we need to be
0823:             * able to restart after an InterruptedIOException
0824:             */
0825:            private byte[] buf = new byte[7];
0826:            private int buf_pos = 0;
0827:            private StringBuffer hdrs = new StringBuffer(400);
0828:            private boolean reading_lines = false;
0829:            private boolean bol = true;
0830:            private boolean got_cr = false;
0831:
0832:            /**
0833:             * Reads the response headers received, folding continued lines.
0834:             *
0835:             * <P>Some of the code is a bit convoluted because we have to be able
0836:             * restart after an InterruptedIOException.
0837:             *
0838:             * @inp    the input stream from which to read the response
0839:             * @return a (newline separated) list of headers
0840:             * @exception IOException if any read on the input stream fails
0841:             */
0842:            private String readResponseHeaders(InputStream inp)
0843:                    throws IOException {
0844:                if (DebugResp) {
0845:                    if (buf_pos == 0)
0846:                        System.err.println("Resp:  Reading Response headers "
0847:                                + inp_stream.hashCode() + " ("
0848:                                + Thread.currentThread() + ")");
0849:                    else
0850:                        System.err
0851:                                .println("Resp:  Resuming reading Response headers "
0852:                                        + inp_stream.hashCode()
0853:                                        + " ("
0854:                                        + Thread.currentThread() + ")");
0855:                }
0856:
0857:                // read 7 bytes to see type of response
0858:                if (!reading_lines) {
0859:                    try {
0860:                        // Skip any leading white space to accomodate buggy responses
0861:                        if (buf_pos == 0) {
0862:                            int c;
0863:                            do {
0864:                                if ((c = inp.read()) == -1)
0865:                                    throw new EOFException(
0866:                                            "Encountered premature EOF "
0867:                                                    + "while reading Version");
0868:                            } while (Character.isSpace((char) (c & 0xFF)));
0869:                            buf[0] = (byte) (c & 0xFF);
0870:                            buf_pos = 1;
0871:                        }
0872:
0873:                        // Now read first seven bytes (the version string)
0874:                        while (buf_pos < buf.length) {
0875:                            int got = inp.read(buf, buf_pos, buf.length
0876:                                    - buf_pos);
0877:                            if (got == -1)
0878:                                throw new EOFException(
0879:                                        "Encountered premature EOF "
0880:                                                + "while reading Version");
0881:                            buf_pos += got;
0882:                        }
0883:                    } catch (EOFException eof) {
0884:                        if (DebugResp) {
0885:                            System.err.print("Resp:  (" + inp_stream.hashCode()
0886:                                    + ") (" + Thread.currentThread() + ")");
0887:                            eof.printStackTrace();
0888:                        }
0889:                        throw eof;
0890:                    }
0891:                    for (int idx = 0; idx < buf.length; idx++)
0892:                        hdrs.append((char) buf[idx]);
0893:
0894:                    reading_lines = true;
0895:                }
0896:
0897:                if (hdrs.toString().startsWith("HTTP/") || // It's x.x
0898:                        hdrs.toString().startsWith("HTTP ")) // NCSA bug
0899:                    readLines(inp);
0900:
0901:                // reset variables for next round
0902:                buf_pos = 0;
0903:                reading_lines = false;
0904:                bol = true;
0905:                got_cr = false;
0906:
0907:                String tmp = hdrs.toString();
0908:                hdrs.setLength(0);
0909:                return tmp;
0910:            }
0911:
0912:            boolean trailers_read = false;
0913:
0914:            /**
0915:             * This is called by the StreamDemultiplexor to read all the trailers
0916:             * of a chunked encoded entity.
0917:             *
0918:             * @param inp the raw input stream to read from
0919:             * @exception IOException if any IOException is thrown by the stream
0920:             */
0921:            void readTrailers(InputStream inp) throws IOException {
0922:                try {
0923:                    readLines(inp);
0924:                    trailers_read = true;
0925:                } catch (IOException ioe) {
0926:                    if (!(ioe instanceof  InterruptedIOException))
0927:                        exception = ioe;
0928:                    throw ioe;
0929:                }
0930:            }
0931:
0932:            /**
0933:             * This reads a set of lines up to and including the first empty line.
0934:             * A line is terminated by either a <CR><LF> or <LF>. The lines are
0935:             * stored in the <var>hdrs</var> buffers. Continued lines are merged
0936:             * and stored as one line.
0937:             *
0938:             * <P>This method is restartable after an InterruptedIOException.
0939:             *
0940:             * @param inp the input stream to read from
0941:             * @exception IOException if any IOException is thrown by the stream
0942:             */
0943:            private void readLines(InputStream inp) throws IOException {
0944:                /* This loop is a merge of readLine() from DataInputStream and
0945:                 * the necessary header logic to merge continued lines and terminate
0946:                 * after an empty line. The reason this is explicit is because of
0947:                 * the need to handle InterruptedIOExceptions.
0948:                 */
0949:                loop: while (true) {
0950:                    int b = inp.read();
0951:                    switch (b) {
0952:                    case -1:
0953:                        throw new EOFException(
0954:                                "Encountered premature EOF while reading headers:\n"
0955:                                        + hdrs);
0956:                    case '\r':
0957:                        got_cr = true;
0958:                        break;
0959:                    case '\n':
0960:                        if (bol)
0961:                            break loop; // all headers read
0962:                        hdrs.append('\n');
0963:                        bol = true;
0964:                        got_cr = false;
0965:                        break;
0966:                    case ' ':
0967:                    case '\t':
0968:                        if (bol) // a continued line
0969:                        {
0970:                            // replace previous \n with SP
0971:                            hdrs.setCharAt(hdrs.length() - 1, ' ');
0972:                            bol = false;
0973:                            break;
0974:                        }
0975:                    default:
0976:                        if (got_cr) {
0977:                            hdrs.append('\r');
0978:                            got_cr = false;
0979:                        }
0980:                        hdrs.append((char) (b & 0xFF));
0981:                        bol = false;
0982:                        break;
0983:                    }
0984:                }
0985:            }
0986:
0987:            /**
0988:             * Parses the headers received into a new Response structure.
0989:             *
0990:             * @param  headers a (newline separated) list of headers
0991:             * @exception ProtocolException if any part of the headers do not
0992:             *            conform
0993:             */
0994:            private void parseResponseHeaders(String headers)
0995:                    throws ProtocolException {
0996:                String sts_line = null;
0997:                StringTokenizer lines = new StringTokenizer(headers, "\r\n"), elem;
0998:
0999:                if (DebugResp)
1000:                    System.err
1001:                            .println("Resp:  Parsing Response headers from Request "
1002:                                    + "\""
1003:                                    + method
1004:                                    + " "
1005:                                    + resource
1006:                                    + "\":  ("
1007:                                    + inp_stream.hashCode()
1008:                                    + ") ("
1009:                                    + Thread.currentThread()
1010:                                    + ")\n\n"
1011:                                    + headers);
1012:
1013:                // Detect and handle HTTP/0.9 responses
1014:
1015:                if (!headers.regionMatches(true, 0, "HTTP/", 0, 5)
1016:                        && !headers.regionMatches(true, 0, "HTTP ", 0, 5)) // NCSA bug
1017:                {
1018:                    Version = "HTTP/0.9";
1019:                    StatusCode = 200;
1020:                    ReasonLine = "OK";
1021:
1022:                    Data = new byte[headers.length()];
1023:                    headers.getBytes(0, headers.length(), Data, 0);
1024:
1025:                    return;
1026:                }
1027:
1028:                // get the status line
1029:
1030:                try {
1031:                    sts_line = lines.nextToken();
1032:                    elem = new StringTokenizer(sts_line, " \t");
1033:
1034:                    Version = elem.nextToken();
1035:                    StatusCode = Integer.valueOf(elem.nextToken()).intValue();
1036:
1037:                    if (Version.equalsIgnoreCase("HTTP")) // NCSA bug
1038:                        Version = "HTTP/1.0";
1039:                } catch (NoSuchElementException e) {
1040:                    throw new ProtocolException(
1041:                            "Invalid HTTP status line received: " + sts_line);
1042:                }
1043:                try {
1044:                    ReasonLine = elem.nextToken("").trim();
1045:                } catch (NoSuchElementException e) {
1046:                    ReasonLine = "";
1047:                }
1048:
1049:                /* If the status code shows an error and we're sending (or have sent)
1050:                 * an entity and it's length is delimited by a Content-length header,
1051:                 * then we must close the the connection (if indeed it hasn't already
1052:                 * been done) - RFC-2068, Section 8.2 .
1053:                 */
1054:                if (StatusCode >= 300 && sent_entity) {
1055:                    if (stream_handler != null)
1056:                        stream_handler.markForClose(this );
1057:                }
1058:
1059:                // get the rest of the headers
1060:
1061:                parseHeaderFields(lines, Headers);
1062:
1063:                /* make sure the connection isn't closed prematurely if we have
1064:                 * trailer fields
1065:                 */
1066:                if (Headers.get("Trailer") != null && resp_inp_stream != null)
1067:                    resp_inp_stream.dontTruncate();
1068:
1069:                // Mark the end of the connection if it's not to be kept alive
1070:
1071:                int vers;
1072:                if (Version.equalsIgnoreCase("HTTP/0.9")
1073:                        || Version.equalsIgnoreCase("HTTP/1.0"))
1074:                    vers = 0;
1075:                else
1076:                    vers = 1;
1077:
1078:                try {
1079:                    String con = (String) Headers.get("Connection"), pcon = (String) Headers
1080:                            .get("Proxy-Connection");
1081:
1082:                    // parse connection header
1083:                    if ((vers == 1 && con != null && Util
1084:                            .hasToken(con, "close"))
1085:                            || (vers == 0 && !((!used_proxy && con != null && Util
1086:                                    .hasToken(con, "keep-alive")) || (used_proxy
1087:                                    && pcon != null && Util.hasToken(pcon,
1088:                                    "keep-alive")))))
1089:                        if (stream_handler != null)
1090:                            stream_handler.markForClose(this );
1091:                } catch (ParseException pe) {
1092:                }
1093:            }
1094:
1095:            /**
1096:             * If the trailers have not been read it calls <code>getData()</code>
1097:             * to first force all data and trailers to be read. Then the trailers
1098:             * parsed into the <var>Trailers</var> hashtable.
1099:             *
1100:             * @exception IOException if any exception occured during reading of the
1101:             *                        response
1102:             */
1103:            private synchronized void getTrailers() throws IOException {
1104:                if (got_trailers)
1105:                    return;
1106:                if (exception != null)
1107:                    throw (IOException) exception.fillInStackTrace();
1108:
1109:                if (DebugResp)
1110:                    System.err.println("Resp:  Reading Response trailers "
1111:                            + inp_stream.hashCode() + " ("
1112:                            + Thread.currentThread() + ")");
1113:
1114:                try {
1115:                    if (!trailers_read) {
1116:                        if (resp_inp_stream != null)
1117:                            resp_inp_stream.readAll(timeout);
1118:                    }
1119:
1120:                    if (trailers_read) {
1121:                        if (DebugResp)
1122:                            System.err
1123:                                    .println("Resp:  Parsing Response trailers from "
1124:                                            + "Request \""
1125:                                            + method
1126:                                            + " "
1127:                                            + resource
1128:                                            + "\":  ("
1129:                                            + inp_stream.hashCode()
1130:                                            + ") ("
1131:                                            + Thread.currentThread()
1132:                                            + ")\n\n"
1133:                                            + hdrs);
1134:
1135:                        parseHeaderFields(new StringTokenizer(hdrs.toString(),
1136:                                "\r\n"), Trailers);
1137:                    }
1138:                } finally {
1139:                    got_trailers = true;
1140:                }
1141:            }
1142:
1143:            /**
1144:             * Parses the given lines as header fields of the form "<name>: <value>"
1145:             * into the given list.
1146:             *
1147:             * @param lines the header or trailer lines, one header field per line
1148:             * @param list  the Hashtable to store the parsed fields in
1149:             * @exception ProtocolException if any part of the headers do not
1150:             *                              conform
1151:             */
1152:            private void parseHeaderFields(StringTokenizer lines,
1153:                    CIHashtable list) throws ProtocolException {
1154:                while (lines.hasMoreTokens()) {
1155:                    String hdr = lines.nextToken();
1156:                    int sep = hdr.indexOf(':');
1157:
1158:                    /* Once again we have to deal with broken servers and try
1159:                     * to wing it here. If no ':' is found, try using the first
1160:                     * space:
1161:                     */
1162:                    if (sep == -1)
1163:                        sep = hdr.indexOf(' ');
1164:                    if (sep == -1) {
1165:                        throw new ProtocolException(
1166:                                "Invalid HTTP header received: " + hdr);
1167:                    }
1168:
1169:                    String hdr_name = hdr.substring(0, sep).trim();
1170:
1171:                    int len = hdr.length();
1172:                    sep++;
1173:                    while (sep < len && Character.isSpace(hdr.charAt(sep)))
1174:                        sep++;
1175:                    String hdr_value = hdr.substring(sep);
1176:
1177:                    String old_value = (String) list.get(hdr_name);
1178:                    if (old_value == null)
1179:                        list.put(hdr_name, hdr_value);
1180:                    else
1181:                        list.put(hdr_name, old_value + ", " + hdr_value);
1182:                }
1183:            }
1184:
1185:            /**
1186:             * Reads the response data received. Does not return until either
1187:             * Content-Length bytes have been read or EOF is reached.
1188:             *
1189:             * @inp       the input stream from which to read the data
1190:             * @exception IOException if any read on the input stream fails
1191:             */
1192:            private void readResponseData(InputStream inp) throws IOException {
1193:                if (ContentLength == 0)
1194:                    return;
1195:
1196:                if (Data == null)
1197:                    Data = new byte[0];
1198:
1199:                // read response data
1200:
1201:                int off = Data.length;
1202:
1203:                try {
1204:                    // check Content-length header in case CE-Module removed it
1205:                    if (getHeader("Content-Length") != null) {
1206:                        int rcvd = 0;
1207:                        Data = new byte[ContentLength];
1208:
1209:                        do {
1210:                            off += rcvd;
1211:                            rcvd = inp.read(Data, off, ContentLength - off);
1212:                        } while (rcvd != -1 && off + rcvd < ContentLength);
1213:
1214:                        /* Don't do this!
1215:                         * If we do, then getData() won't work after a getInputStream()
1216:                         * because we'll never get all the expected data. Instead, let
1217:                         * the underlying RespInputStream throw the EOF.
1218:                        if (rcvd == -1)	// premature EOF
1219:                        {
1220:                            throw new EOFException("Encountered premature EOF while " +
1221:                        			    "reading headers: received " + off +
1222:                        			    " bytes instead of the expected " +
1223:                        			    ContentLength + " bytes");
1224:                        }
1225:                         */
1226:                    } else {
1227:                        int inc = 1000, rcvd = 0;
1228:
1229:                        do {
1230:                            off += rcvd;
1231:                            Data = Util.resizeArray(Data, off + inc);
1232:                        } while ((rcvd = inp.read(Data, off, inc)) != -1);
1233:
1234:                        Data = Util.resizeArray(Data, off);
1235:                    }
1236:                } catch (IOException ioe) {
1237:                    Data = Util.resizeArray(Data, off);
1238:                    throw ioe;
1239:                } finally {
1240:                    try {
1241:                        inp.close();
1242:                    } catch (IOException ioe) {
1243:                    }
1244:                }
1245:            }
1246:
1247:            Request req = null;
1248:            boolean isFirstResponse = false;
1249:
1250:            /**
1251:             * This marks this response as belonging to the first request made
1252:             * over an HTTPConnection. The <var>con</var> and <var>req</var>
1253:             * parameters are needed in case we have to do a resend of the request -
1254:             * this is to handle buggy servers which barf upon receiving a request
1255:             * marked as HTTP/1.1 .
1256:             *
1257:             * @param con The HTTPConnection used
1258:             * @param req The Request sent
1259:             */
1260:            void markAsFirstResponse(Request req) {
1261:                this .req = req;
1262:                isFirstResponse = true;
1263:            }
1264:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.