Source Code Cross Referenced for Protocol.java in  » 6.0-JDK-Modules » j2me » com » sun » midp » io » j2me » http » 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 » 6.0 JDK Modules » j2me » com.sun.midp.io.j2me.http 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         *   
0003:         *
0004:         * Copyright  1990-2007 Sun Microsystems, Inc. All Rights Reserved.
0005:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0006:         * 
0007:         * This program is free software; you can redistribute it and/or
0008:         * modify it under the terms of the GNU General Public License version
0009:         * 2 only, as published by the Free Software Foundation.
0010:         * 
0011:         * This program is distributed in the hope that it will be useful, but
0012:         * WITHOUT ANY WARRANTY; without even the implied warranty of
0013:         * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014:         * General Public License version 2 for more details (a copy is
0015:         * included at /legal/license.txt).
0016:         * 
0017:         * You should have received a copy of the GNU General Public License
0018:         * version 2 along with this work; if not, write to the Free Software
0019:         * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020:         * 02110-1301 USA
0021:         * 
0022:         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0023:         * Clara, CA 95054 or visit www.sun.com if you need additional
0024:         * information or have any questions.
0025:         */
0026:
0027:        package com.sun.midp.io.j2me.http;
0028:
0029:        /**
0030:         * A class representing a http connection. An http connection consists of
0031:         * stream connection as well as input and output streams for read/write data to
0032:         * and from a web server. This version supports HTTP1.1 persistent connections
0033:         * allowing connects to be shared from a connection pool. This pool and the
0034:         * maximum number of connections can be configured for a particular platform.
0035:         * Proxy connections are also allowed through this interface.
0036:         *
0037:         * <p> The actual connection to the web server does not take place until the
0038:         * application needs an, (1) input stream, (2) flush data, (3)request some 
0039:         * header info or closes the connection (with outstanding data in the 
0040:         * output stream). Because of this issue the state transition must allow for
0041:         * some flexibility to move backwards for WRITE state conditions.
0042:         *
0043:         * <p> Persistent connections are provided through the use of a connection
0044:         * pool that tracks the connect status. There are maximum threshold values
0045:         * defined and these values can be overriden using property key/value pars.
0046:         * The connection pool provides a synchronized interface for managing the
0047:         * maximum configurable connections. Persistent connections will only be 
0048:         * supported for HTTP1.1 connections - otherwise the connections will be
0049:         * closed and disregarded after its done (HTTP1.0 behavior).
0050:         *
0051:         * <p> This class extends the ConnectionBaseAdapter where Connector type
0052:         * objects (like this) use various features. Output and Input streams are
0053:         * created and managed in the adapter class.
0054:         *
0055:         * <p> The reading and writing of data through the input and output streams
0056:         * are configured, buffered and managed depending on the ability of a 
0057:         * platform to read/write on those streams. 
0058:         *
0059:         */
0060:
0061:        import java.io.IOException;
0062:        import java.io.InputStream;
0063:        import java.io.InterruptedIOException;
0064:        import java.io.OutputStream;
0065:        import java.io.DataInputStream;
0066:        import java.io.DataOutputStream;
0067:        import java.io.ByteArrayOutputStream;
0068:
0069:        import java.util.Enumeration;
0070:        import java.util.Hashtable;
0071:
0072:        import javax.microedition.io.SocketConnection;
0073:        import javax.microedition.io.StreamConnection;
0074:        import javax.microedition.io.HttpConnection;
0075:        import javax.microedition.io.Connector;
0076:        import javax.microedition.io.Connection;
0077:        import javax.microedition.io.ConnectionNotFoundException;
0078:
0079:        import com.sun.midp.main.Configuration;
0080:
0081:        import com.sun.midp.io.ConnectionBaseAdapter;
0082:        import com.sun.midp.io.HttpUrl;
0083:        import com.sun.midp.io.NetworkConnectionBase;
0084:
0085:        import com.sun.midp.midlet.MIDletStateHandler;
0086:        import com.sun.midp.midlet.MIDletSuite;
0087:
0088:        import com.sun.midp.security.SecurityToken;
0089:        import com.sun.midp.security.Permissions;
0090:        import com.sun.midp.security.SecurityInitializer;
0091:        import com.sun.midp.security.ImplicitlyTrustedClass;
0092:
0093:        import com.sun.midp.util.DateParser;
0094:        import com.sun.midp.util.Properties;
0095:
0096:        /**
0097:         * This class implements the necessary functionality
0098:         * for an HTTP connection.
0099:         */
0100:        public class Protocol extends ConnectionBaseAdapter implements 
0101:                HttpConnection {
0102:
0103:            /** HTTP version string to use with all outgoing HTTP requests. */
0104:            protected static final String HTTP_VERSION = "HTTP/1.1";
0105:            /** Where to start the data in the output buffer. */
0106:            private static final int HTTP_OUTPUT_DATA_OFFSET = 24;
0107:            /** How must extra room for the chunk terminator. */
0108:            private static final int HTTP_OUTPUT_EXTRA_ROOM = 8;
0109:
0110:            /**
0111:             * Inner class to request security token from SecurityInitializer.
0112:             * SecurityInitializer should be able to check this inner class name.
0113:             */
0114:            static private class SecurityTrusted implements 
0115:                    ImplicitlyTrustedClass {
0116:            };
0117:
0118:            /** This class has a different security domain than the MIDlet suite */
0119:            private static SecurityToken classSecurityToken = SecurityInitializer
0120:                    .requestToken(new SecurityTrusted());
0121:
0122:            /** Default size for input buffer. */
0123:            private static int inputBufferSize = 256;
0124:            /** Default size for output buffer. */
0125:            private static int outputBufferSize = 2048;
0126:            /** How much data can be put in the output buffer. */
0127:            private static int outputDataSize;
0128:            /** The "host:port" value to use for HTTP proxied requests. */
0129:            private static String http_proxy;
0130:            /** Maximum number of persistent connections. */
0131:            private static int maxNumberOfPersistentConnections = 4;
0132:            /** Connection linger time in the pool, default 60 seconds. */
0133:            private static long connectionLingerTime = 60000;
0134:            /** Persistent connection pool. */
0135:            protected static StreamConnectionPool connectionPool;
0136:            /** True if com.sun.midp.io.http.force_non_persistent = true. */
0137:            private static boolean nonPersistentFlag;
0138:            /**
0139:             * The methods other than openPrim need to know that the
0140:             * permission occurred.
0141:             */
0142:            private boolean permissionChecked;
0143:            /** True if the owner of this connection is trusted. */
0144:            private boolean ownerTrusted;
0145:
0146:            /** Get the configuration values for this class. */
0147:            static {
0148:                String prop;
0149:                int temp;
0150:
0151:                /*
0152:                 * Get the proxy here instead of the connector,
0153:                 * so when this method subclassed by HTTPS http_proxy will be null
0154:                 * and the proxy will not be added into the request.
0155:                 */
0156:                http_proxy = Configuration
0157:                        .getProperty("com.sun.midp.io.http.proxy");
0158:
0159:                /*
0160:                 * CR#4455443 - allows for configuration options to shut off 
0161:                 * the persistent connection feature for http 
0162:                 */
0163:                String flag = Configuration
0164:                        .getProperty("com.sun.midp.io.http.force_non_persistent");
0165:                if ((flag != null) && (flag.equals("true"))) {
0166:                    nonPersistentFlag = true;
0167:                }
0168:
0169:                /*
0170:                 * Get the  maximum number of persistent connections
0171:                 * from the configuration file if there is one.
0172:                 */
0173:                maxNumberOfPersistentConnections = Configuration
0174:                        .getNonNegativeIntProperty(
0175:                                "com.sun.midp.io.http.max_persistent_connections",
0176:                                maxNumberOfPersistentConnections);
0177:
0178:                // Get how long a "not in use" connection should stay in the pool.
0179:                connectionLingerTime = (long) Configuration
0180:                        .getNonNegativeIntProperty(
0181:                                "com.sun.midp.io.http.persistent_connection_linger_time",
0182:                                (int) connectionLingerTime);
0183:
0184:                connectionPool = new StreamConnectionPool(
0185:                        maxNumberOfPersistentConnections, connectionLingerTime);
0186:
0187:                /*
0188:                 * Get the buffer sizes from the configuration file.
0189:                 * 0 for the input buffer size shuts off input buffering.
0190:                 * Output buffer must always be positive.
0191:                 */
0192:                inputBufferSize = Configuration.getNonNegativeIntProperty(
0193:                        "com.sun.midp.io.http.input_buffer_size",
0194:                        inputBufferSize);
0195:
0196:                temp = outputBufferSize;
0197:                outputBufferSize = Configuration.getPositiveIntProperty(
0198:                        "com.sun.midp.io.http.output_buffer_size",
0199:                        outputBufferSize);
0200:                if (outputBufferSize <= (HTTP_OUTPUT_DATA_OFFSET + HTTP_OUTPUT_EXTRA_ROOM)) {
0201:                    outputBufferSize = temp;
0202:                }
0203:
0204:                outputDataSize = outputBufferSize - HTTP_OUTPUT_DATA_OFFSET
0205:                        - HTTP_OUTPUT_EXTRA_ROOM;
0206:            }
0207:
0208:            /** The protocol (or scheme) for the URL of the connection. */
0209:            protected String protocol;
0210:            /** Default port number for this protocol. */
0211:            protected int default_port;
0212:            /** Parsed Url. */
0213:            protected HttpUrl url;
0214:            /** url.host + ":" + url.port. */
0215:            protected String hostAndPort;
0216:
0217:            /** Numeric code returned from HTTP response header. */
0218:            protected int responseCode;
0219:            /** Message string from HTTP response header. */
0220:            protected String responseMsg;
0221:
0222:            /** Collection of request headers as name/value pairs. */
0223:            protected Properties reqProperties;
0224:
0225:            /** Collection of response headers as name/value pairs. */
0226:            protected Properties headerFields;
0227:
0228:            /** HTTP method type for the current request. */
0229:            protected String method;
0230:
0231:            /*
0232:             * The streams from the underlying socket connection.
0233:             */
0234:            /** Low level socket connection used for the HTTP requests. */
0235:            private StreamConnection streamConnection;
0236:            /** Low level socket output stream. */
0237:            private DataOutputStream streamOutput;
0238:            /** Low level socket input stream. */
0239:            private DataInputStream streamInput;
0240:            /** A shared temporary header buffer. */
0241:            private StringBuffer stringbuffer;
0242:            /** HTTP version string set with all incoming HTTP responses. */
0243:            private String httpVer = null;
0244:            /** Used when appl calls setRequestProperty("Connection", "close"). */
0245:            private boolean ConnectionCloseFlag;
0246:            /** Content-Length from response header, or -1 if missing. */
0247:            private int contentLength = -1;
0248:            /**
0249:             * Total number of bytes in the current chunk or content-length when
0250:             * data is sent as one big chunk.
0251:             */
0252:            private int chunksize = -1;
0253:            /**
0254:             * Number of bytes read from the stream for non-chunked data or
0255:             * the bytes read from the current chunk.
0256:             */
0257:            private int totalbytesread;
0258:            /** True if Transfer-Encoding: chunkedIn. */
0259:            private boolean chunkedIn;
0260:            /** True if Transfer-Encoding: chunkedOut. */
0261:            private boolean chunkedOut;
0262:            /** True after the first chunk has been sent. */
0263:            private boolean firstChunkSent;
0264:            /** True if the request is being sent. */
0265:            private boolean sendingRequest;
0266:            /** True if the entire request has been sent to the server. */
0267:            private boolean requestFinished;
0268:            /** True if eof seen. */
0269:            private boolean eof;
0270:            /** Internal stream buffer to minimize the number of TCP socket reads. */
0271:            private byte[] readbuf;
0272:            /** Number of bytes left in internal input stream buffer. */
0273:            private int bytesleft;
0274:            /** Number of bytes read from the internal input stream buffer. */
0275:            private int bytesread;
0276:            /** Buffered data output for content length calculation. */
0277:            private byte[] writebuf;
0278:            /** Number of bytes of data that need to be written from the buffer. */
0279:            private int bytesToWrite;
0280:            /** Collection of "Proxy-" headers as name/value pairs. */
0281:            private Properties proxyHeaders = new Properties();
0282:            /** Last handshake error. */
0283:            private byte handshakeError;
0284:            /**
0285:             * Holds the state the readBytes call. So if close is called in another
0286:             * thread than the read thread the close will be directly on the stream,
0287:             * instead of putting the connection back in the persistent connection
0288:             * pool, forcing an IOException on the read thread.
0289:             */
0290:            private boolean readInProgress;
0291:
0292:            /**
0293:             * Create a new instance of this class and intialize variables.
0294:             * Initially an http connection is unconnected to the network.
0295:             */
0296:            public Protocol() {
0297:                reqProperties = new Properties();
0298:                headerFields = new Properties();
0299:                stringbuffer = new StringBuffer(32);
0300:
0301:                method = GET;
0302:                responseCode = -1;
0303:                protocol = "http";
0304:                default_port = 80;
0305:
0306:                if (nonPersistentFlag) {
0307:                    ConnectionCloseFlag = true;
0308:                }
0309:
0310:                readbuf = new byte[inputBufferSize];
0311:            }
0312:
0313:            /**
0314:             * Sets up the state of the connection, but
0315:             * does not actually connect to the server until there's something
0316:             * to do. This is method by system classes.
0317:             *
0318:             * @param token Token with the HTTP permission set to the allowed level
0319:             * @param fullUrl   Full URL of the connection
0320:             *
0321:             * @return reference to this connection
0322:             *
0323:             * @exception IllegalArgumentException If a parameter is invalid.
0324:             * @exception ConnectionNotFoundException If the connection cannot be
0325:             *             found.
0326:             * @exception IOException  If some other kind of I/O error occurs.
0327:             */
0328:            public Connection openPrim(SecurityToken token, String fullUrl)
0329:                    throws IOException, IllegalArgumentException,
0330:                    ConnectionNotFoundException {
0331:
0332:                checkIfPermissionAllowed(token);
0333:                return open(new HttpUrl(fullUrl), Connector.READ_WRITE);
0334:            }
0335:
0336:            /**
0337:             * Sets up the state of the connection, but
0338:             * does not actually connect to the server until there's something
0339:             * to do.
0340:             *
0341:             * @param name             The URL for the connection, without the
0342:             *                         without the protocol part.
0343:             * @param mode             The access mode, ignored
0344:             * @param timeouts         A flag to indicate that the called wants
0345:             *                         timeout exceptions, ignored
0346:             *
0347:             * @return reference to this connection
0348:             *
0349:             * @exception IllegalArgumentException If a parameter is invalid.
0350:             * @exception ConnectionNotFoundException If the connection cannot be
0351:             *             found.
0352:             * @exception IOException  If some other kind of I/O error occurs.
0353:             */
0354:            public Connection openPrim(String name, int mode, boolean timeouts)
0355:                    throws IOException, IllegalArgumentException,
0356:                    ConnectionNotFoundException {
0357:
0358:                checkForPermission(name);
0359:                return open(new HttpUrl(protocol, name), mode);
0360:            }
0361:
0362:            /**
0363:             * Sets up the state of the connection, but
0364:             * does not actually connect to the server until there's something
0365:             * to do.
0366:             *
0367:             * @param theUrl           URL object
0368:             * @param mode             The access mode, ignored
0369:             *                         timeout exceptions, ignored
0370:             *
0371:             * @return reference to this connection
0372:             *
0373:             * @exception IllegalArgumentException If a parameter is invalid.
0374:             * @exception ConnectionNotFoundException If the connection cannot be
0375:             *             found.
0376:             * @exception IOException  If some other kind of I/O error occurs.
0377:             */
0378:            private Connection open(HttpUrl theUrl, int mode)
0379:                    throws IOException, IllegalArgumentException,
0380:                    ConnectionNotFoundException {
0381:
0382:                url = theUrl;
0383:
0384:                initStreamConnection(mode);
0385:
0386:                if (url.port == -1) {
0387:                    url.port = default_port;
0388:                }
0389:
0390:                if (url.host == null) {
0391:                    throw new IllegalArgumentException("missing host in URL");
0392:                }
0393:
0394:                hostAndPort = url.host + ":" + url.port;
0395:
0396:                return this ;
0397:            }
0398:
0399:            /**
0400:             * Check if the required permission has been set to allowed. Allowed
0401:             * is required because this method will not prompt the user. Only
0402:             * trusted callers have the a permission set to the allowed level.
0403:             *
0404:             * @param token token with the HTTP permission set to the allowed level
0405:             */
0406:            private void checkIfPermissionAllowed(SecurityToken token) {
0407:                token.checkIfPermissionAllowed(Permissions.HTTP);
0408:                ownerTrusted = true;
0409:                permissionChecked = true;
0410:            }
0411:
0412:            /**
0413:             * Check for the required permission.
0414:             *
0415:             * @param name name of resource to insert into the permission question
0416:             *
0417:             * @exception IOInterruptedException if another thread interrupts the
0418:             *   calling thread while this method is waiting to preempt the
0419:             *   display.
0420:             */
0421:            private void checkForPermission(String name)
0422:                    throws InterruptedIOException {
0423:                MIDletStateHandler midletStateHandler;
0424:                MIDletSuite midletSuite;
0425:
0426:                midletStateHandler = MIDletStateHandler.getMidletStateHandler();
0427:                midletSuite = midletStateHandler.getMIDletSuite();
0428:
0429:                if (midletSuite == null) {
0430:                    throw new IllegalStateException("This class can't be used "
0431:                            + "before a suite is started.");
0432:                }
0433:
0434:                name = protocol + ":" + name;
0435:
0436:                try {
0437:                    midletSuite.checkForPermission(Permissions.HTTP, name);
0438:                    ownerTrusted = midletSuite.isTrusted();
0439:                    permissionChecked = true;
0440:                } catch (InterruptedException ie) {
0441:                    throw new InterruptedIOException(
0442:                            "Interrupted while trying to ask the user permission");
0443:                }
0444:            }
0445:
0446:            /**
0447:             * Open the input stream if it has not already been opened.
0448:             *
0449:             * @exception IOException is thrown if it has already been opened.
0450:             * @return input stream for the current connection
0451:             */
0452:            public InputStream openInputStream() throws IOException {
0453:                InputStream in;
0454:
0455:                /*
0456:                 * Call into parent to create input stream passed back to the user
0457:                 */
0458:                in = super .openInputStream();
0459:
0460:                /*
0461:                 * Send a request to the web server if there wasn't one
0462:                 * sent already
0463:                 */
0464:                sendRequest();
0465:
0466:                return in;
0467:            }
0468:
0469:            /**
0470:             * Open the output stream if it has not already been opened.
0471:             *
0472:             * @exception IOException is thrown if it has already been opened.
0473:             * @return output stream for the current connection
0474:             */
0475:            public OutputStream openOutputStream() throws IOException {
0476:                OutputStream out;
0477:
0478:                /*
0479:                 * call into parent to create output stream passed back to the user
0480:                 */
0481:                out = super .openOutputStream();
0482:
0483:                /*
0484:                 * Create a byte array output stream for output buffering
0485:                 * once the user calls flush() this gets written to stream
0486:                 */
0487:                writebuf = new byte[outputBufferSize];
0488:
0489:                return out;
0490:            }
0491:
0492:            /**
0493:             * Reads up to <code>len</code> bytes of data from the input stream into
0494:             * an array of bytes.
0495:             * This method reads NonChunked http connection input streams.
0496:             * This method can only be called after the InputStream setup is complete.
0497:             *
0498:             * @param      b     the buffer into which the data is read.
0499:             * @param      off   the start offset in array <code>b</code>
0500:             *                   at which the data is written.
0501:             * @param      len   the maximum number of bytes to read.
0502:             * @return     the total number of bytes read into the buffer, or
0503:             *             <code>-1</code> if there is no more data because the end of
0504:             *             the stream has been reached.
0505:             * @exception  IOException  if an I/O error occurs.
0506:             */
0507:            protected int readBytes(byte b[], int off, int len)
0508:                    throws IOException {
0509:
0510:                int rc;
0511:
0512:                /*
0513:                 * Be consistent about returning EOF once encountered.
0514:                 */
0515:                if (eof) {
0516:                    return (-1);
0517:                }
0518:
0519:                /*
0520:                 * The InputStream close behavior will be different if close is called
0521:                 * from another thread when reading.
0522:                 */
0523:                synchronized (streamInput) {
0524:                    readInProgress = true;
0525:                }
0526:
0527:                try {
0528:                    /*
0529:                     * If the http connection is chunked, call the readBytesChunked
0530:                     * method
0531:                     */
0532:                    if (chunkedIn || chunksize > 0) {
0533:                        /*
0534:                         * Non-chunked data of known length is treated as one big chunk
0535:                         */
0536:                        return readBytesChunked(b, off, len);
0537:                    }
0538:
0539:                    /*
0540:                     * Non-chunked unknown length
0541:                     */
0542:
0543:                    if (bytesleft == 0) {
0544:                        /*
0545:                         * the internal input stream buffer is empty, read from the
0546:                         * stream
0547:                         */
0548:                        if (len >= inputBufferSize) {
0549:                            /*
0550:                             * No need to buffer, if the caller has given a big buffer.
0551:                             */
0552:                            rc = streamInput.read(b, off, len);
0553:                        } else {
0554:                            rc = streamInput.read(readbuf, 0, inputBufferSize);
0555:                            bytesleft = rc;
0556:                            bytesread = 0;
0557:                        }
0558:
0559:                        if (rc == -1) {
0560:                            /*
0561:                             * The next call to this method should not read.
0562:                             */
0563:                            eof = true;
0564:                            return -1;
0565:                        }
0566:
0567:                        totalbytesread += rc;
0568:
0569:                        if (bytesleft == 0) {
0570:                            /*
0571:                             * The data was read directly into the caller's buffer.
0572:                             */
0573:                            return rc;
0574:                        }
0575:                    }
0576:
0577:                    rc = readFromBuffer(b, off, len);
0578:                    return rc;
0579:                } finally {
0580:                    synchronized (streamInput) {
0581:                        readInProgress = false;
0582:                    }
0583:                }
0584:            }
0585:
0586:            /**
0587:             * Reads up to <code>len</code> bytes of data from the internal buffer into
0588:             * an array of bytes.
0589:             *
0590:             * @param      b     the buffer into which the data is read.
0591:             * @param      off   the start offset in array <code>b</code>
0592:             *                   at which the data is written.
0593:             * @param      len   the maximum number of bytes to read.
0594:             * @return     the total number of bytes read into the buffer, or
0595:             *             <code>-1</code> if there is no more data because the end of
0596:             *             the stream has been reached.
0597:             * @exception  IOException  if an I/O error occurs.
0598:             */
0599:            private int readFromBuffer(byte b[], int off, int len)
0600:                    throws IOException {
0601:
0602:                /*
0603:                 * copy http buffer data into user buffer, then
0604:                 * increment and decrement counters
0605:                 */
0606:
0607:                int rc;
0608:
0609:                if (len > bytesleft) {
0610:                    rc = bytesleft;
0611:                } else {
0612:                    rc = len;
0613:                }
0614:
0615:                System.arraycopy(readbuf, bytesread, b, off, rc);
0616:
0617:                bytesleft -= rc;
0618:                bytesread += rc;
0619:
0620:                return rc;
0621:            }
0622:
0623:            /**
0624:             * Returns the number of bytes that can be read (or skipped over) from
0625:             * this input stream without blocking by the next caller of a method for
0626:             * this input stream.  The next caller might be the same thread or
0627:             * another thread.
0628:             *
0629:             * @return     the number of bytes that can be read from this input stream
0630:             *             without blocking.
0631:             * @exception  IOException  if an I/O error occurs.
0632:             */
0633:            public int available() throws IOException {
0634:                int bytesAvailable;
0635:
0636:                /* 
0637:                 * Only after all the headers have been processed can
0638:                 * an accurate available count be provided.
0639:                 */
0640:                if (!requestFinished || eof) {
0641:                    return 0;
0642:                }
0643:
0644:                /*
0645:                 * Regardless of chunked or non-chunked transfers -
0646:                 * if data is already buffered return the amount 
0647:                 * buffered.
0648:                 */
0649:                if (bytesleft > 0) {
0650:                    return bytesleft;
0651:                }
0652:
0653:                if (chunkedIn && totalbytesread == chunksize) {
0654:                    /* 
0655:                     * Check if a new chunk size header is available.
0656:                     */
0657:                    return readChunkSizeNonBlocking();
0658:                }
0659:
0660:                /*
0661:                 * Otherwise rely on the lower level stream available
0662:                 * count for the nonchunked input stream.
0663:                 */
0664:                bytesAvailable = streamInput.available();
0665:                if (chunksize <= bytesAvailable) {
0666:                    return chunksize;
0667:                }
0668:
0669:                return bytesAvailable;
0670:            }
0671:
0672:            /** 
0673:             * Read a chunk size header into the readLine buffer
0674:             * without blocking. The stringbuffer is populated
0675:             * with characters one at a time. This routine is design
0676:             * so that a partial chunk size header could be read
0677:             * and then completed by a blocking read of the chunk 
0678:             * or a subsequent call to available.
0679:             *
0680:             * @return available data that can be read
0681:             */
0682:            int readChunkSizeNonBlocking() throws IOException {
0683:                /*
0684:                 * Check the underlying stream to see how many bytes are
0685:                 * available. Do not read beyond the available characters,
0686:                 * because that would block.
0687:                 */
0688:                int len = streamInput.available();
0689:
0690:                /* Reset the last character from the current readLine buffer. */
0691:                int sblen = stringbuffer.length();
0692:                char lastchar = '\0';
0693:                if (sblen > 0) {
0694:                    lastchar = stringbuffer.charAt(sblen - 1);
0695:                }
0696:
0697:                int size = -1;
0698:                /*
0699:                 * Loop through the available characters until a full 
0700:                 * chunk size header is in the readLine buffer.
0701:                 */
0702:                for (; len > 0; len--) {
0703:                    char c = (char) streamInput.read();
0704:
0705:                    if (lastchar == '\r' && c == '\n') {
0706:                        // remove the '\r' from the buffer
0707:                        stringbuffer.setLength(stringbuffer.length() - 1);
0708:
0709:                        if (stringbuffer.length() > 0) {
0710:                            // this is a size, not the CRLF at the end of a chunk
0711:                            try {
0712:                                String temp = stringbuffer.toString();
0713:                                int semi = temp.indexOf(';');
0714:
0715:                                // skip extensions
0716:                                if (semi > 0) {
0717:                                    temp = temp.substring(0, semi);
0718:                                }
0719:
0720:                                /*
0721:                                 * Reset the string buffer length so readline() will
0722:                                 * not parse this line.
0723:                                 */
0724:                                stringbuffer.setLength(0);
0725:
0726:                                size = Integer.parseInt(temp, 16);
0727:                            } catch (NumberFormatException nfe) {
0728:                                throw new IOException(
0729:                                        "invalid chunk size number format");
0730:                            }
0731:                            break;
0732:                        }
0733:                    } else {
0734:                        stringbuffer.append(c);
0735:                        lastchar = c;
0736:                    }
0737:                }
0738:
0739:                if (size < 0) {
0740:                    // did not get the size
0741:                    return 0;
0742:                }
0743:
0744:                /*
0745:                 * Update the chunksize and the total bytes that have been
0746:                 * read from the chunk. This will trigger the next call to
0747:                 * readBytes to refill the buffers as needed.
0748:                 */
0749:                chunksize = size;
0750:                if (size == 0) {
0751:                    eof = true;
0752:                    return 0;
0753:                }
0754:
0755:                totalbytesread = 0;
0756:
0757:                /*
0758:                 * If the full chunk is available, return chunksize,
0759:                 * otherwise return the remainder of the available
0760:                 * bytes (e.g. partial chunk).
0761:                 */
0762:                return (chunksize < len ? chunksize : len);
0763:
0764:            }
0765:
0766:            /**
0767:             * Reads up to <code>len</code> bytes of data from the input stream into
0768:             * an array of bytes.
0769:             * This method reads Chunked and known length non-chunked http connection
0770:             * input streams. For non-chunked set the field <code>chunkedIn</code>
0771:             * should be false.
0772:             *
0773:             * @param      b     the buffer into which the data is read.
0774:             * @param      off   the start offset in array <code>b</code>
0775:             *                   at which the data is written.
0776:             * @param      len   the maximum number of bytes to read.
0777:             * @return     the total number of bytes read into the buffer, or
0778:             *             <code>-1</code> if there is no more data because the end of
0779:             *             the stream has been reached.
0780:             * @exception  IOException  if an I/O error occurs.
0781:             */
0782:            protected int readBytesChunked(byte b[], int off, int len)
0783:                    throws IOException {
0784:
0785:                int rc;
0786:
0787:                if (bytesleft == 0) {
0788:                    /*
0789:                     * the internal input stream buffer is empty, read from the stream
0790:                     */
0791:
0792:                    if (totalbytesread == chunksize) {
0793:                        /*
0794:                         * read the end of the chunk and get the size of the
0795:                         * the next if there is one
0796:                         */
0797:
0798:                        if (!chunkedIn) {
0799:                            /*
0800:                             * non-chucked data is treated as one big chunk so there
0801:                             * is no more data so just return as if there are no
0802:                             * more chunks
0803:                             */
0804:                            eof = true;
0805:                            return -1;
0806:                        }
0807:
0808:                        skipEndOfChunkCRLF();
0809:
0810:                        chunksize = readChunkSize();
0811:                        if (chunksize == 0) {
0812:                            eof = true;
0813:
0814:                            /*
0815:                             * REFERENCE: HTTP1.1 document 
0816:                             * SECTION: 3.6.1 Chunked Transfer Coding
0817:                             * in some cases there may be an OPTIONAL trailer
0818:                             * containing entity-header fields. since we don't support
0819:                             * the available() method for TCP socket input streams and
0820:                             * for performance and reuse reasons we do not attempt to
0821:                             * clean up the current connections input stream. 
0822:                             * check readResponseMessage() method in this class for
0823:                             * more details
0824:                             */
0825:                            return -1;
0826:                        }
0827:
0828:                        /*
0829:                         * we have not read any bytes from this new chunk
0830:                         */
0831:                        totalbytesread = 0;
0832:                    }
0833:
0834:                    int bytesToRead = chunksize - totalbytesread;
0835:
0836:                    if (len >= bytesToRead) {
0837:
0838:                        /*
0839:                         * No need to buffer, if the caller has given a big buffer.
0840:                         */
0841:                        rc = streamInput.read(b, off, bytesToRead);
0842:
0843:                    } else if (len >= inputBufferSize) {
0844:                        /*
0845:                         * No need to buffer, if the caller has given a big buffer.
0846:                         */
0847:                        rc = streamInput.read(b, off, len);
0848:                    } else {
0849:                        if (inputBufferSize >= bytesToRead) {
0850:                            rc = streamInput.read(readbuf, 0, bytesToRead);
0851:                        } else {
0852:                            rc = streamInput.read(readbuf, 0, inputBufferSize);
0853:                        }
0854:
0855:                        bytesleft = rc;
0856:                        bytesread = 0;
0857:                    }
0858:
0859:                    if (rc == -1) {
0860:                        /*
0861:                         * Network problem or the wrong length was sent by the server.
0862:                         */
0863:                        eof = true;
0864:                        throw new IOException("unexpected end of stream");
0865:                    }
0866:
0867:                    totalbytesread += rc;
0868:
0869:                    if (bytesleft == 0) {
0870:                        /*
0871:                         * The data was read directly into the caller's buffer.
0872:                         */
0873:                        return rc;
0874:                    }
0875:                }
0876:
0877:                rc = readFromBuffer(b, off, len);
0878:
0879:                return rc;
0880:            }
0881:
0882:            /** 
0883:             * Read the chunk size from the input.
0884:             * It is a hex length followed by optional headers (ignored).
0885:             * and terminated with CRLF.
0886:             *
0887:             * @return size of the buffered read
0888:             */
0889:            private int readChunkSize() throws IOException {
0890:                int size = -1;
0891:
0892:                try {
0893:                    String chunk = null;
0894:
0895:                    try {
0896:                        chunk = readLine(streamInput);
0897:                    } catch (IOException ioe) {
0898:                        /* throw new IOException(ioe.getMessage()); */
0899:                    }
0900:
0901:                    if (chunk == null) {
0902:                        throw new IOException("No Chunk Size");
0903:                    }
0904:
0905:                    int i;
0906:                    for (i = 0; i < chunk.length(); i++) {
0907:                        char ch = chunk.charAt(i);
0908:                        if (Character.digit(ch, 16) == -1)
0909:                            break;
0910:                    }
0911:
0912:                    /* look at extensions?.... */
0913:                    size = Integer.parseInt(chunk.substring(0, i), 16);
0914:                } catch (NumberFormatException e) {
0915:                    throw new IOException("invalid chunk size number format");
0916:                }
0917:
0918:                return size;
0919:            }
0920:
0921:            /**
0922:             * Skips the CRLF at the end of each chunk in the InputStream.
0923:             *
0924:             * @exception IOException if the LF half of the ending CRLF
0925:             * is missing.
0926:             */
0927:            private void skipEndOfChunkCRLF() throws IOException {
0928:                int ch;
0929:
0930:                if (stringbuffer.length() > 1) {
0931:                    /*
0932:                     * readChunkSizeNonBlocking does not leave CRLF in the buffer
0933:                     * so assume that the ending CRLF has been skipped already
0934:                     */
0935:                    return;
0936:                }
0937:
0938:                // readChunkSizeNonBlocking could have left a \r single in the buffer
0939:                if (stringbuffer.length() == 1) {
0940:                    if (stringbuffer.charAt(0) != '\r') {
0941:                        // assume that the ending CRLF has been skipped already
0942:                        return;
0943:                    }
0944:
0945:                    // remove the '\r'
0946:                    stringbuffer.setLength(0);
0947:                    ch = streamInput.read();
0948:                    if (ch != '\n') {
0949:                        throw new IOException(
0950:                                "missing the LF of an expected CRLF");
0951:                    }
0952:
0953:                    return;
0954:                }
0955:
0956:                ch = streamInput.read();
0957:                if (ch != '\r') {
0958:                    /*
0959:                     * assume readChunkSizeNonBlocking has read the end of the chunk
0960:                     * and that this is the next chunk size, so put the char in the
0961:                     * buffer for readChunkSize and return
0962:                     */
0963:                    stringbuffer.append(ch);
0964:                    return;
0965:                }
0966:
0967:                ch = streamInput.read();
0968:                if (ch != '\n') {
0969:                    throw new IOException("missing the LF of an expected CRLF");
0970:                }
0971:            }
0972:
0973:            /**
0974:             * Writes <code>len</code> bytes from the specified byte array
0975:             * starting at offset <code>off</code> to this output stream.
0976:             *
0977:             * <p>
0978:             * This method can only be called after an OutputStream setup has be
0979:             * done.
0980:             *
0981:             * @param      b     the data.
0982:             * @param      off   the start offset in the data.
0983:             * @param      len   the number of bytes to write.
0984:             *
0985:             * @return     int   number of bytes written to stream
0986:             *
0987:             * @exception  IOException  if an I/O error occurs. In particular,
0988:             *             an <code>IOException</code> is thrown if the output
0989:             *             stream is closed.
0990:             *
0991:             * @exception  IllegalStateException if an attempt was made to
0992:             *             write after the request has finished.
0993:             */
0994:            protected int writeBytes(byte b[], int off, int len)
0995:                    throws IOException {
0996:
0997:                int bytesToCopy;
0998:
0999:                if (requestFinished) {
1000:                    throw new IllegalStateException(
1001:                            "Write attempted after request finished");
1002:                }
1003:
1004:                if (bytesToWrite == outputDataSize) {
1005:                    /*
1006:                     * Send the bytes in the write buffer as a chunk to the server
1007:                     * so more bytes can be put in the buffer.
1008:                     */
1009:                    sendRequest(true, false);
1010:                }
1011:
1012:                /*
1013:                 * Our parent class will call this method in a loop until all the bytes
1014:                 * are written. So this method does not have to process all the bytes
1015:                 * in one call.
1016:                 */
1017:                bytesToCopy = outputDataSize - bytesToWrite;
1018:                if (len < bytesToCopy) {
1019:                    bytesToCopy = len;
1020:                }
1021:
1022:                System.arraycopy(b, off, writebuf, HTTP_OUTPUT_DATA_OFFSET
1023:                        + bytesToWrite, bytesToCopy);
1024:                bytesToWrite += bytesToCopy;
1025:
1026:                return bytesToCopy;
1027:            }
1028:
1029:            /**
1030:             * If any output data, turn on chunking send it to the server.
1031:             *
1032:             * @exception IOException if an I/O error occurs
1033:             *
1034:             * @exception IllegalStateException if there was an attempt was made to
1035:             *            flush after the request has finished.
1036:             */
1037:            public void flush() throws IOException {
1038:
1039:                if (bytesToWrite <= 0) {
1040:                    return;
1041:                }
1042:
1043:                if (requestFinished) {
1044:                    throw new IllegalStateException(
1045:                            "Flush attempted after request finished");
1046:                }
1047:
1048:                sendRequest(true, false);
1049:            }
1050:
1051:            /** 
1052:             * Get the original URL used to open the HTTP connection.
1053:             *
1054:             * @return HTTP URL used in the current connection
1055:             */
1056:            public String getURL() {
1057:                return url.toString();
1058:            }
1059:
1060:            /** 
1061:             * Get the protocol scheme parsed from the URL.
1062:             *
1063:             * @return protocol scheme is "http" 
1064:             */
1065:            public String getProtocol() {
1066:                return protocol;
1067:            }
1068:
1069:            /** 
1070:             * Get the host name parsed from the URL.
1071:             *
1072:             * @return host name from the parsed URL
1073:             */
1074:            public String getHost() {
1075:                return url.host;
1076:            }
1077:
1078:            /** 
1079:             * Get the file path name parsed from the URL.
1080:             *
1081:             * @return file path name from the parsed URL
1082:             */
1083:            public String getFile() {
1084:                return url.path;
1085:            }
1086:
1087:            /** 
1088:             * Get the fragment identifier  parsed from the URL.
1089:             *
1090:             * @return reference component from the parsed URL
1091:             */
1092:            public String getRef() {
1093:                return url.fragment;
1094:            }
1095:
1096:            /** 
1097:             * Get the query string  parsed from the URL.
1098:             *
1099:             * @return query string from the parsed URL
1100:             */
1101:            public String getQuery() {
1102:                return url.query;
1103:            }
1104:
1105:            /** 
1106:             * Get the query string  parsed from the URL.
1107:             *
1108:             * @return query string from the parsed URL
1109:             */
1110:            public int getPort() {
1111:                return url.port;
1112:            }
1113:
1114:            /** 
1115:             * Get the request method of the current connection.
1116:             *
1117:             * @return request method is GET, HEAD or POST
1118:             * @see #setRequestMethod
1119:             */
1120:            public String getRequestMethod() {
1121:                return method;
1122:            }
1123:
1124:            /** 
1125:             * Set the request method of the current connection.
1126:             *
1127:             * @param method request method is GET, HEAD or POST
1128:             * @exception IOException is thrown if the connection is already open
1129:             * @see #getRequestMethod
1130:             */
1131:            public void setRequestMethod(String method) throws IOException {
1132:                ensureOpen();
1133:
1134:                if (streamConnection != null) {
1135:                    throw new IOException("connection already open");
1136:                }
1137:
1138:                /* 
1139:                 * The request method can not be changed once the output
1140:                 * stream has been opened.
1141:                 */
1142:                if (maxOStreams == 0) {
1143:                    return;
1144:                }
1145:
1146:                if (!method.equals(HEAD) && !method.equals(GET)
1147:                        && !method.equals(POST)) {
1148:                    throw new IOException("unsupported method: " + method);
1149:                }
1150:
1151:                this .method = method;
1152:            }
1153:
1154:            /** 
1155:             * Get the request header value for the named property.
1156:             *
1157:             * @param key property name of specific HTTP 1.1 header field
1158:             * @return value of the named property, if found, null otherwise.
1159:             * @see #setRequestProperty
1160:             */
1161:            public String getRequestProperty(String key) {
1162:
1163:                /* https handles the proxy fields in a different way */
1164:                if (key.toLowerCase().startsWith("proxy-")) {
1165:                    return proxyHeaders.getPropertyIgnoreCase(key);
1166:                }
1167:
1168:                return reqProperties.getPropertyIgnoreCase(key);
1169:            }
1170:
1171:            /**
1172:             * Set the request header name/value of specific HTTP 1.1 header field.
1173:             *
1174:             * @param key property name 
1175:             * @param value property value
1176:             * @exception IllegalArgumentException is thrown if value contains CRLF.
1177:             * @exception IOException  If some other kind of I/O error occurs.
1178:             * @see #getRequestProperty
1179:             */
1180:            public void setRequestProperty(String key, String value)
1181:                    throws IOException {
1182:                int index = 0;
1183:
1184:                ensureOpen();
1185:
1186:                if (streamConnection != null) {
1187:                    throw new IOException("connection already open");
1188:                }
1189:
1190:                /* 
1191:                 * The request headers can not be changed once the output 
1192:                 * stream has been opened.
1193:                 */
1194:                if (maxOStreams == 0) {
1195:                    return;
1196:                }
1197:
1198:                // Look to see if a programmer embedded any extra fields.
1199:                for (;;) {
1200:                    index = value.indexOf("\r\n", index);
1201:
1202:                    if (index == -1) {
1203:                        break;
1204:                    }
1205:
1206:                    // Allow legal header value continuations. CRLF + (SP|HT)
1207:                    index += 2;
1208:
1209:                    if (index >= value.length()
1210:                            || (value.charAt(index) != ' ' && value
1211:                                    .charAt(index) != '\t')) {
1212:                        // illegal values passed for properties - raise an exception
1213:                        throw new IllegalArgumentException(
1214:                                "illegal value found");
1215:                    }
1216:                }
1217:
1218:                setRequestField(key, value);
1219:            }
1220:
1221:            /**
1222:             * Add the named field to the list of request fields.
1223:             * This method is where a subclass should override properties.
1224:             *
1225:             * @param key key for the request header field.
1226:             * @param value the value for the request header field.
1227:             */
1228:            protected void setRequestField(String key, String value) {
1229:
1230:                /* https handles the proxy fields in a different way */
1231:                if (key.toLowerCase().startsWith("proxy-")) {
1232:                    proxyHeaders.setPropertyIgnoreCase(key, value);
1233:                    return;
1234:                }
1235:
1236:                /*
1237:                 * If application setRequestProperties("Connection", "close")
1238:                 * then we need to know this & take appropriate default close action
1239:                 */
1240:                if ((key.equalsIgnoreCase("connection"))
1241:                        && (value.equalsIgnoreCase("close"))) {
1242:                    ConnectionCloseFlag = true;
1243:                }
1244:
1245:                /* 
1246:                 * Ref . Section 3.6 of RFC2616 : 
1247:                 * All transfer-coding values are case-insensitive.
1248:                 */
1249:                if ((key.equalsIgnoreCase("transfer-encoding"))
1250:                        && (value.equalsIgnoreCase("chunked"))) {
1251:                    chunkedOut = true;
1252:                }
1253:
1254:                reqProperties.setPropertyIgnoreCase(key, value);
1255:            }
1256:
1257:            /** 
1258:             * Get the response code of the current request.
1259:             *
1260:             * @return numeric value of the parsed response code
1261:             * @exception IOException is thrown if a network error occurs
1262:             */
1263:            public int getResponseCode() throws IOException {
1264:                ensureOpen();
1265:
1266:                sendRequest();
1267:
1268:                return responseCode;
1269:            }
1270:
1271:            /** 
1272:             * Get the response message of the current request.
1273:             *
1274:             * @return message associated with the current response header
1275:             * @exception IOException is thrown if a network error occurs
1276:             */
1277:            public String getResponseMessage() throws IOException {
1278:                ensureOpen();
1279:
1280:                sendRequest();
1281:
1282:                return responseMsg;
1283:            }
1284:
1285:            /** 
1286:             * Get the Content-Length for the current response.
1287:             *
1288:             * @return length of data to be transmitted after the response headers
1289:             */
1290:            public long getLength() {
1291:                try {
1292:                    ensureOpen();
1293:
1294:                    sendRequest();
1295:                } catch (IOException ioe) {
1296:                    // Fall through to return -1 for length
1297:                }
1298:
1299:                return contentLength;
1300:
1301:            }
1302:
1303:            /** 
1304:             * Get the Content-Type for the current response.
1305:             *
1306:             * @return MIME type of data to be transmitted after the response header
1307:             */
1308:            public String getType() {
1309:                try {
1310:                    return getHeaderField("content-type");
1311:                } catch (IOException x) {
1312:                    return null;
1313:                }
1314:
1315:            }
1316:
1317:            /** 
1318:             * Get the Content-Encoding for the current response.
1319:             *
1320:             * @return encoding type of data to be transmitted after the 
1321:             *         response headers
1322:             */
1323:            public String getEncoding() {
1324:                try {
1325:                    return getHeaderField("content-encoding");
1326:                } catch (IOException x) {
1327:                    return null;
1328:                }
1329:
1330:            }
1331:
1332:            /** 
1333:             * Get the Expires header for the current response.
1334:             *
1335:             * @return expiration data for the transmitted data
1336:             *
1337:             * @exception IOException is thrown if a network error occurs
1338:             */
1339:            public long getExpiration() throws IOException {
1340:                return getHeaderFieldDate("expires", 0);
1341:            }
1342:
1343:            /** 
1344:             * Get the Date header for the current response.
1345:             *
1346:             * @return timestamp for the data transmission event
1347:             *
1348:             * @exception IOException is thrown if a network error occurs
1349:             */
1350:            public long getDate() throws IOException {
1351:                return getHeaderFieldDate("date", 0);
1352:            }
1353:
1354:            /** 
1355:             * Get the Last-Modified date header for the current response.
1356:             *
1357:             * @return timestamp for the transmitted data last modification
1358:             *
1359:             * @exception IOException is thrown if a network error occurs
1360:             */
1361:            public long getLastModified() throws IOException {
1362:                return getHeaderFieldDate("last-modified", 0);
1363:            }
1364:
1365:            /** 
1366:             * Get the named header field  for the current response.
1367:             *
1368:             * @param name header field to be examined
1369:             * @return value of requested header, if found, otherwise null
1370:             *
1371:             * @exception IOException is thrown if a network error occurs
1372:             */
1373:            public String getHeaderField(String name) throws IOException {
1374:                ensureOpen();
1375:
1376:                sendRequest();
1377:
1378:                return (headerFields.getPropertyIgnoreCase(name));
1379:
1380:            }
1381:
1382:            /** 
1383:             * Get the indexed header field  for the current response.
1384:             *
1385:             * @param index header field offset to be examined
1386:             * @return key name of requested header, if found, otherwise null
1387:             *
1388:             * @exception IOException is thrown if a network error occurs
1389:             */
1390:            public String getHeaderField(int index) throws IOException {
1391:                ensureOpen();
1392:
1393:                sendRequest();
1394:
1395:                if (index >= headerFields.size()) {
1396:                    return null;
1397:                }
1398:
1399:                return (headerFields.getValueAt(index));
1400:            }
1401:
1402:            /** 
1403:             * Get the indexed header field value for the current response.
1404:             *
1405:             * @param index header field value offset to be examined
1406:             * @return value of requested header, if found, otherwise null
1407:             *
1408:             * @exception IOException is thrown if a network error occurs
1409:             */
1410:            public String getHeaderFieldKey(int index) throws IOException {
1411:                ensureOpen();
1412:
1413:                sendRequest();
1414:
1415:                if (index >= headerFields.size())
1416:                    return null;
1417:
1418:                return ((String) (headerFields.getKeyAt(index)));
1419:            }
1420:
1421:            /** 
1422:             * Get the named header field  for the current response and return a 
1423:             * numeric value for the parsed field, with a supplied default value  
1424:             * if the field does not exist or can not be parsed cleanly.
1425:             *
1426:             * @param name of the field to be examined
1427:             * @param def default value to use, if field is not parsable
1428:             * @return numeric value of requested header, if found, otherwise 
1429:             *         supplied default is returned
1430:             *
1431:             * @exception IOException is thrown if a network error occurs
1432:             */
1433:            public int getHeaderFieldInt(String name, int def)
1434:                    throws IOException {
1435:                ensureOpen();
1436:
1437:                sendRequest();
1438:
1439:                try {
1440:                    return Integer.parseInt(getHeaderField(name));
1441:                } catch (IllegalArgumentException iae) {
1442:                    // fall through
1443:                } catch (NullPointerException npe) {
1444:                    // fall through
1445:                }
1446:
1447:                return def;
1448:            }
1449:
1450:            /** 
1451:             * Get the named header field for the current response and return a date
1452:             * value for the parsed field,with a supplied default value if the field
1453:             * does not exist or can not be parsed cleanly.
1454:             *
1455:             * @param name of the field to be examined
1456:             * @param def default value to use, if field is not parsable
1457:             * @return date value of requested header, if found, otherwise 
1458:             *         supplied default is returned
1459:             *
1460:             * @exception IOException is thrown if a network error occurs
1461:             */
1462:            public long getHeaderFieldDate(String name, long def)
1463:                    throws IOException {
1464:                ensureOpen();
1465:
1466:                sendRequest();
1467:
1468:                try {
1469:                    return DateParser.parse(getHeaderField(name));
1470:                } catch (NumberFormatException nfe) {
1471:                    // fall through
1472:                } catch (IllegalArgumentException iae) {
1473:                    // fall through
1474:                } catch (NullPointerException npe) {
1475:                    // fall through
1476:                }
1477:
1478:                return def;
1479:            }
1480:
1481:            /** 
1482:             * If not connected, connect to the underlying socket transport 
1483:             * and send the HTTP request and get the response header.
1484:             * <P>
1485:             * If an http_proxy was specified the socket connection will be made to 
1486:             * the proxy server and the requested URL will include the full http URL.
1487:             * <P> 
1488:             * On output the Content-Length header is included in the request based
1489:             * on the size of the buffered output array.
1490:             * <P> 
1491:             * This routine inserts the Host header needed for HTTP 1.1 virtual host
1492:             * addressing.
1493:             * <P>
1494:             * This routine also receives the reply response and parses the headers
1495:             * for easier access. After the headers are parsed the application has
1496:             * access to the raw data from the socket stream connection.
1497:             *
1498:             * @exception IOException is thrown if the connection cannot be opened
1499:             */
1500:            protected void sendRequest() throws IOException {
1501:                sendRequest(false, true);
1502:            }
1503:
1504:            /** 
1505:             * If not connected, connect to the underlying socket transport 
1506:             * and send the HTTP request and get the response header.
1507:             * <P>
1508:             * If an http_proxy was specified the socket connection will be made to 
1509:             * the proxy server and the requested URL will include the full http URL.
1510:             * <P> 
1511:             * On output the Content-Length header is included in the request based
1512:             * on the size of the buffered output array.
1513:             * <P> 
1514:             * This routine inserts the Host header needed for HTTP 1.1 virtual host
1515:             * addressing.
1516:             * <P>
1517:             * This routine also receives the reply response and parses the headers
1518:             * for easier access. After the headers are parsed the application has
1519:             * access to the raw data from the socket stream connection.
1520:             *
1521:             * @param chunkData if true chunk data sent to the server
1522:             * @param readResponseHeader if true, read the response header
1523:             *
1524:             * @exception IOException is thrown if the connection cannot be opened
1525:             */
1526:            private void sendRequest(boolean chunkData,
1527:                    boolean readResponseHeader) throws IOException {
1528:                int bytesToRetry;
1529:
1530:                if (sendingRequest || requestFinished) {
1531:                    return;
1532:                }
1533:
1534:                sendingRequest = true;
1535:
1536:                try {
1537:                    if (chunkData) {
1538:                        chunkedOut = true;
1539:                    }
1540:
1541:                    bytesToRetry = bytesToWrite;
1542:
1543:                    try {
1544:                        startRequest();
1545:                        sendRequestBody();
1546:
1547:                        if (readResponseHeader) {
1548:                            finishRequestGetResponseHeader();
1549:                        }
1550:                    } catch (IOException ioe) {
1551:                        if (!(streamConnection instanceof  StreamConnectionElement)) {
1552:                            /*
1553:                             * This was a connection opened during this transaction.
1554:                             * So do not try to recover.
1555:                             */
1556:                            throw ioe;
1557:                        }
1558:
1559:                        try {
1560:                            connectionPool
1561:                                    .remove((StreamConnectionElement) streamConnection);
1562:                        } catch (Exception e) {
1563:                            // do not over throw the previous exception
1564:                        }
1565:
1566:                        if (firstChunkSent) {
1567:                            // can't retry since we do not have the previous chunk
1568:                            throw new IOException(
1569:                                    "Persistent connection dropped "
1570:                                            + "after first chunk sent, cannot retry");
1571:                        }
1572:
1573:                        streamConnection = null;
1574:                        streamInput = null;
1575:                        streamOutput = null;
1576:                        bytesToWrite = bytesToRetry;
1577:
1578:                        startRequest();
1579:                        sendRequestBody();
1580:
1581:                        if (readResponseHeader) {
1582:                            finishRequestGetResponseHeader();
1583:                        }
1584:                    }
1585:
1586:                    if (chunkedOut) {
1587:                        firstChunkSent = true;
1588:                    }
1589:                } finally {
1590:                    sendingRequest = false;
1591:                }
1592:            }
1593:
1594:            /** 
1595:             * If not connected, connect to the underlying socket transport 
1596:             * and send the HTTP request headers.
1597:             * <P>
1598:             * If an http_proxy was specified the socket connection will be made to 
1599:             * the proxy server and the requested URL will include the full http URL.
1600:             * <P> 
1601:             * On output the Content-Length header is included in the request based
1602:             * on the size of the buffered output array.
1603:             * <P> 
1604:             * This routine inserts the Host header needed for HTTP 1.1 virtual host
1605:             * addressing.
1606:             * <P>
1607:             * This routine also receives the reply response and parses the headers
1608:             * for easier access. After the headers are parsed the application has
1609:             * access to the raw data from the socket stream connection.
1610:             *
1611:             * @exception IOException is thrown if the connection cannot be opened
1612:             */
1613:            void startRequest() throws IOException {
1614:                if (streamConnection != null) {
1615:                    return;
1616:                }
1617:
1618:                streamConnect();
1619:                sendRequestHeader();
1620:            }
1621:
1622:            /**
1623:             * Find a previous connection in the pool or try to connect to the
1624:             * underlying stream transport.
1625:             *
1626:             * @exception IOException is thrown if the connection cannot be opened
1627:             */
1628:            private void streamConnect() throws IOException {
1629:                streamConnection = connect();
1630:
1631:                /*
1632:                 * Because StreamConnection.open*Stream cannot be called twice
1633:                 * the HTTP connect method may have already open the streams
1634:                 * to connect to the proxy and saved them in the field variables
1635:                 * already.
1636:                 */
1637:                if (streamOutput != null) {
1638:                    return;
1639:                }
1640:
1641:                streamOutput = streamConnection.openDataOutputStream();
1642:                streamInput = streamConnection.openDataInputStream();
1643:            }
1644:
1645:            /**
1646:             * Gets the underlying stream connection.
1647:             *
1648:             * @return underlying stream connection
1649:             */
1650:            protected StreamConnection getStreamConnection() {
1651:                return streamConnection;
1652:            }
1653:
1654:            /**
1655:             * Simplifies the sendRequest() method header functionality into one method
1656:             * this is extremely helpful for persistent connection support and
1657:             * retries.
1658:             *
1659:             * @exception IOException is thrown if the connection cannot be opened
1660:             */
1661:            private void sendRequestHeader() throws IOException {
1662:                StringBuffer reqLine;
1663:                String filename;
1664:                int numberOfKeys;
1665:
1666:                /*
1667:                 * JTWI security policy for untrusted MIDlets says to add a
1668:                 * user-agent field with the value "UNTRUSTED/1.0" but still include
1669:                 * the applications value. The TCP and SSL protocols will deny the
1670:                 * standard ports to untrusted applications so they cannot get around
1671:                 * this field being added to their HTTP(S) requests.
1672:                 */
1673:                if (!ownerTrusted) {
1674:                    String newUserAgentValue;
1675:                    String origUserAgentValue = reqProperties
1676:                            .getPropertyIgnoreCase("User-Agent");
1677:                    if (origUserAgentValue != null) {
1678:                        /*
1679:                         * HTTP header values can be concatenated, so original value
1680:                         * of the "User-Agent" header field should not be ignored in 
1681:                         * this case
1682:                         */
1683:                        newUserAgentValue = "UNTRUSTED/1.0 "
1684:                                + origUserAgentValue;
1685:                    } else {
1686:                        newUserAgentValue = "UNTRUSTED/1.0";
1687:                    }
1688:                    reqProperties.setPropertyIgnoreCase("User-Agent",
1689:                            newUserAgentValue);
1690:                }
1691:
1692:                // HTTP 1.0 requests must contain content length for proxies
1693:                if (getRequestProperty("Content-Length") == null) {
1694:                    setRequestField("Content-Length", Integer
1695:                            .toString(bytesToWrite));
1696:                }
1697:
1698:                reqLine = new StringBuffer(256);
1699:
1700:                /*
1701:                 * HTTP RFC and CR#4402149,
1702:                 * if there is no path then add a slash ("/").
1703:                 */
1704:                filename = url.path;
1705:                if (filename == null) {
1706:                    filename = "/";
1707:                }
1708:
1709:                /*
1710:                 * Note: the "ref" or fragment, is not sent to the server.
1711:                 */
1712:                reqLine.append(method);
1713:                reqLine.append(" ");
1714:
1715:                /*
1716:                 * Since we now use a tunnel instead of a proxy, we do not have
1717:                 * to send an absolute URI. The difference is that a proxy will
1718:                 * strip scheme and authority from the URI and a tunnel will not.
1719:                 *
1720:                 * For HTTPS purposes we will use the relative URI.
1721:                 *
1722:                 * Some HTTPS server's do not like to see "https" as the scheme of an 
1723:                 * URI and only recognize "http".
1724:                 * examples: www.wellsfargo.com sends back html with not HTTP headers,
1725:                 * e-banking.abbeynational.co.uk sends back a 404 to all requests.
1726:                 *
1727:                 * It is better to not use the absolute URL, than to hardcode the
1728:                 * the scheme to "http" all the time since that did not work with
1729:                 * e-banking.abbeynational.co.uk.
1730:                 *
1731:                 * if (http_proxy != null) {
1732:                 *     reqLine.append(protocol);
1733:                 *     reqLine.append("://");
1734:                 *     reqLine.append(url.authority);
1735:                 * }
1736:                 */
1737:
1738:                reqLine.append(filename);
1739:
1740:                if (url.query != null) {
1741:                    reqLine.append("?");
1742:                    reqLine.append(url.query);
1743:                }
1744:
1745:                reqLine.append(" ");
1746:                reqLine.append(HTTP_VERSION);
1747:                reqLine.append("\r\n");
1748:
1749:                /*
1750:                 * HTTP 1/1 requests require the Host header to distinguish
1751:                 * virtual host locations.
1752:                 */
1753:
1754:                setRequestField("Host", url.authority);
1755:
1756:                if (chunkedOut) {
1757:                    /*
1758:                     * Signal the server that the body is chunked
1759:                     * by setting the Transfer-Encoding property to "chunked".
1760:                     */
1761:                    setRequestField("Transfer-Encoding", "chunked");
1762:                }
1763:
1764:                /*
1765:                 * Setup the various http header field/values defined and/or
1766:                 * required.
1767:                 */
1768:                numberOfKeys = reqProperties.size();
1769:                for (int i = 0; i < numberOfKeys; i++) {
1770:                    String key = (String) reqProperties.getKeyAt(i);
1771:
1772:                    if (key.equals("Content-Length")) {
1773:                        /*
1774:                         * If its CHUNK data - no content-length: size required.
1775:                         */
1776:                        if (chunkedOut) {
1777:                            continue;
1778:                        } else {
1779:                            /*
1780:                             * Check that the output stream has been opened.
1781:                             */
1782:                            if (writebuf == null) {
1783:                                reqLine.append("Content-Length: 0");
1784:                            } else {
1785:                                reqLine.append("Content-Length: ");
1786:                                reqLine.append(bytesToWrite);
1787:                            }
1788:
1789:                            reqLine.append("\r\n");
1790:                        }
1791:                    } else {
1792:                        reqLine.append(key);
1793:                        reqLine.append(": ");
1794:                        reqLine.append(reqProperties.getValueAt(i));
1795:                        reqLine.append("\r\n");
1796:                    }
1797:                }
1798:
1799:                reqLine.append("\r\n");
1800:
1801:                streamOutput.write(reqLine.toString().getBytes());
1802:            }
1803:
1804:            /**
1805:             * Write the http request body bytes to the output stream.
1806:             *
1807:             * @exception IOException 
1808:             */
1809:            protected void sendRequestBody() throws IOException {
1810:
1811:                int start;
1812:                int endOfData;
1813:                int length;
1814:
1815:                if ((writebuf == null) || (bytesToWrite == 0)) {
1816:                    return;
1817:                }
1818:
1819:                start = HTTP_OUTPUT_DATA_OFFSET;
1820:                endOfData = HTTP_OUTPUT_DATA_OFFSET + bytesToWrite;
1821:                length = bytesToWrite;
1822:
1823:                /*
1824:                 * If a CHUNKed session then write out the chunk size first 
1825:                 * with a trailing CRLF.
1826:                 *
1827:                 * reference: RFC2616 - section 3 protocol parameters
1828:                 * 3.6 transfer coding:
1829:                 * 3.6.1 chunked transfer coding:
1830:                 * chunk-body = chunk / last chunk / trailer / CRLF
1831:                 *       * chunk =      chunk-size / chunk-data / CRLF
1832:                 *         last_chunk = "0" / CRLF
1833:                 *         trailer    = " " / CRLF
1834:                 * *indicates its done here.
1835:                 */
1836:                if (chunkedOut) {
1837:                    /*
1838:                     * For CHUNKed write out the chunk size with CRLF.
1839:                     * Put this before the data in write buffer.
1840:                     */
1841:                    String temp = Integer.toHexString(bytesToWrite);
1842:                    int tempLen = temp.length();
1843:
1844:                    writebuf[--start] = (byte) '\n';
1845:                    writebuf[--start] = (byte) '\r';
1846:                    for (int i = tempLen - 1; i >= 0; i--) {
1847:                        writebuf[--start] = (byte) temp.charAt(i);
1848:                    }
1849:
1850:                    length += tempLen + 2;
1851:
1852:                    /*
1853:                     * If a CHUNKed session then write out another CRLF and flush().
1854:                     * Put this after the data in the write buffer.
1855:                     */
1856:                    writebuf[endOfData++] = (byte) '\r';
1857:                    writebuf[endOfData++] = (byte) '\n';
1858:                    length += 2;
1859:                }
1860:
1861:                streamOutput.write(writebuf, start, length);
1862:                bytesToWrite = 0;
1863:            }
1864:
1865:            /**
1866:             * Finish the http request and reads the response headers.
1867:             *
1868:             * @exception IOException is thrown, if an I/O error occurs for final
1869:             *            stream output or on reading the response message line
1870:             */
1871:            protected void finishRequestGetResponseHeader() throws IOException {
1872:
1873:                // Even if we get an exception this request is finished
1874:                requestFinished = true;
1875:
1876:                /*
1877:                 * if this is a CHUNKed session write out the last set of CRLF
1878:                 */
1879:                if (chunkedOut) {
1880:                    /*
1881:                     * reference: RFC2616 - section 3 protocol parameters
1882:                     * 3.6 transfer coding:
1883:                     * 3.6.1 chunked transfer coding:
1884:                     * chunk-body = chunk / last chunk / trailer / CRLF
1885:                     *         chunk =      chunk-size / chunk-data / CRLF
1886:                     *       * last_chunk = "0" / CRLF
1887:                     *       * trailer    = " " / CRLF
1888:                     * * indicates its done here.
1889:                     */
1890:
1891:                    /*
1892:                     * write the last chunk (size=0 / CRLF) and the dummy trailer 
1893:                     */
1894:                    streamOutput.write("0\r\n\r\n".getBytes());
1895:                }
1896:
1897:                streamOutput.flush();
1898:
1899:                readResponseMessage(streamInput);
1900:
1901:                readHeaders(streamInput);
1902:
1903:                /*
1904:                 * Ignore a continuation header and read the true headers again.
1905:                 * (CR# 4382226 discovered with Jetty HTTP 1.1 web server.
1906:                 */
1907:                if (responseCode == 100) {
1908:                    readResponseMessage(streamInput);
1909:                    readHeaders(streamInput);
1910:                }
1911:            }
1912:
1913:            /**
1914:             * Connect to the underlying network TCP transport.
1915:             * If the proxy is configured, connect to it as tunnel first.
1916:             * <p>
1917:             * Warning: A subclass that implements this method, should not call this
1918:             * method and should implement the disconnect method.
1919:             *
1920:             * @return network stream connection
1921:             * @exception IOException is thrown if the connection cannot be opened
1922:             */
1923:            protected StreamConnection connect() throws IOException {
1924:                StreamConnection sc;
1925:                com.sun.midp.io.j2me.socket.Protocol conn;
1926:
1927:                if (!permissionChecked) {
1928:                    throw new SecurityException();
1929:                }
1930:
1931:                sc = connectionPool.get(classSecurityToken, protocol, url.host,
1932:                        url.port);
1933:
1934:                if (sc != null) {
1935:                    return sc;
1936:                }
1937:
1938:                conn = new com.sun.midp.io.j2me.socket.Protocol();
1939:
1940:                if (http_proxy == null) {
1941:                    conn.openPrim(classSecurityToken, "//" + hostAndPort);
1942:
1943:                    // Do not delay request since this delays the response.
1944:                    conn.setSocketOption(SocketConnection.DELAY, 0);
1945:                    return conn;
1946:                }
1947:
1948:                conn.openPrim(classSecurityToken, "//" + http_proxy);
1949:
1950:                // Do not delay request since this delays the response.
1951:                conn.setSocketOption(SocketConnection.DELAY, 0);
1952:
1953:                // openData*Stream cannot be call twice, so save them for later
1954:                streamOutput = conn.openDataOutputStream();
1955:                streamInput = conn.openDataInputStream();
1956:
1957:                try {
1958:                    doTunnelHandshake(streamOutput, streamInput);
1959:                } catch (IOException ioe) {
1960:                    String response = ioe.getMessage();
1961:
1962:                    try {
1963:                        disconnect(conn, streamInput, streamOutput);
1964:                    } catch (Exception e) {
1965:                        // do not over throw the handshake exception
1966:                    }
1967:
1968:                    streamOutput = null;
1969:                    streamInput = null;
1970:
1971:                    if ((response != null) && (response.indexOf(" 500 ") > -1)) {
1972:                        throw new ConnectionNotFoundException(response);
1973:                    } else {
1974:                        throw ioe;
1975:                    }
1976:                }
1977:
1978:                return conn;
1979:            }
1980:
1981:            /**
1982:             * Connects to the SSL tunnel and completes the initialization of the
1983:             * tunnel (handshake). The handshake based on the Internet-Draft
1984:             *  "A. Luotonen, Tunneling TCP based protocols through Web proxy servers,
1985:             *  February 1999".
1986:             * @param os output stream for secure handshake
1987:             * @param is input stream for secure handshake
1988:             * @exception IOException is thrown if an error occurs in the SSL handshake
1989:             */
1990:            protected void doTunnelHandshake(OutputStream os, InputStream is)
1991:                    throws IOException {
1992:                String required;
1993:                String optional;
1994:                String endOfLine = "\r\n";
1995:                String emptyLine = endOfLine;
1996:                int numberOfKeys;
1997:                StringBuffer temp;
1998:                boolean newline;
1999:                String response;
2000:
2001:                /*
2002:                 * request = required *optional emptyLine
2003:                 * required = "CONNECT" SP HOST ":" PORT SP HTTP_VERSION endOfLine
2004:                 * optional = HTTP_HEADER endOfLine ; proxy dependent: most likely
2005:                 *                                  ; used for authorization.
2006:                 * emptyLine = endOfLine
2007:                 * endOfLine = *1CR LF
2008:                 *
2009:                 * example:
2010:                 * CONNECT home.acme.com:443 HTTP/1.0
2011:                 * Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
2012:                 *
2013:                 */
2014:                required = "CONNECT " + hostAndPort + " " + HTTP_VERSION
2015:                        + endOfLine;
2016:
2017:                os.write(required.getBytes());
2018:
2019:                numberOfKeys = proxyHeaders.size();
2020:                for (int i = 0; i < numberOfKeys; i++) {
2021:                    optional = proxyHeaders.getKeyAt(i) + ": "
2022:                            + proxyHeaders.getValueAt(i) + endOfLine;
2023:                    os.write(optional.getBytes());
2024:                }
2025:
2026:                os.write(emptyLine.getBytes());
2027:                os.flush();
2028:
2029:                /*
2030:                 * response = status *optional emptyLine
2031:                 * status = HTTP_VERSION SP STATUSCODE STATUS_MESSAGE *1CR LF
2032:                 * optional = HTTP_HEADER *1CR LF
2033:                 * emptyLine = *1CR LF
2034:                 *
2035:                 * example:
2036:                 * HTTP/1.0 200 Connection established
2037:                 *
2038:                 */
2039:
2040:                // Read in response until an empty line is found (1*CR LF 1*CR LF)
2041:                temp = new StringBuffer();
2042:                newline = false;
2043:                while (true) {
2044:                    int c = is.read();
2045:                    if (c == -1) {
2046:                        break;
2047:                    } else if (c == '\n') {
2048:                        if (newline) {
2049:                            break;
2050:                        }
2051:                        newline = true;
2052:                    } else if (c != '\r') {
2053:                        newline = false;
2054:                    }
2055:
2056:                    temp.append((char) c);
2057:                }
2058:
2059:                if (temp.length() == 0) {
2060:                    temp.append("none");
2061:                }
2062:
2063:                response = temp.toString();
2064:
2065:                if (response.indexOf(" 200 ") == -1) {
2066:                    throw new IOException(
2067:                            "Error initializing HTTP tunnel connection: \n"
2068:                                    + response);
2069:                }
2070:            }
2071:
2072:            /** 
2073:             * Check the initial response message looking for the 
2074:             * appropriate HTTP version string. Parse the response 
2075:             * code for easy application branching on condition codes.
2076:             *
2077:             * @param in input stream where the response headers are read
2078:             * @exception IOException  is thrown if the header response can 
2079:             *                         not be parsed
2080:             */
2081:            private void readResponseMessage(InputStream in) throws IOException {
2082:
2083:                String line = null;
2084:                responseCode = -1;
2085:                responseMsg = null;
2086:
2087:                line = readLine(in);
2088:
2089:                /*
2090:                 * REFERENCE: HTTP1.1 document 
2091:                 * SECTION: 3.6.1 Chunked Transfer Coding
2092:                 * in some cases there may be an OPTIONAL trailer containing 
2093:                 * entity-header fields. since we don't support the available()
2094:                 * method for inputstreams and for performance reasons we
2095:                 * do not attempt to clean up the previous connections input
2096:                 * stream. the first thing we do here is read the stream and 
2097:                 * discard it.
2098:                 */
2099:                if (line != null && line.length() == 0) {
2100:                    line = readLine(in);
2101:                }
2102:
2103:                int httpEnd, codeEnd;
2104:
2105:                responseCode = -1;
2106:                responseMsg = null;
2107:
2108:                if (line == null) {
2109:                    throw new IOException("response empty");
2110:                }
2111:
2112:                httpEnd = line.indexOf(' ');
2113:                if (httpEnd < 0) {
2114:                    if (line.length() > 10) {
2115:                        // only put the first 10 chars in the exception
2116:                        line = line.substring(0, 10);
2117:                    }
2118:
2119:                    throw new IOException(
2120:                            "cannot find status code in response: " + line);
2121:                }
2122:
2123:                String temp = line.substring(0, httpEnd);
2124:                if (!temp.startsWith("HTTP")) {
2125:                    if (httpEnd > 10) {
2126:                        // only put the first 10 chars in the exception
2127:                        temp = temp.substring(0, 10);
2128:                    }
2129:
2130:                    throw new IOException("response does not start with HTTP "
2131:                            + "it starts with: " + temp);
2132:                }
2133:
2134:                httpVer = temp;
2135:                if (line.length() <= httpEnd) {
2136:                    throw new IOException("status line ends after HTTP version");
2137:                }
2138:
2139:                codeEnd = line.substring(httpEnd + 1).indexOf(' ');
2140:                if (codeEnd < 0) {
2141:                    throw new IOException(
2142:                            "cannot find reason phrase in response");
2143:                }
2144:
2145:                codeEnd += (httpEnd + 1);
2146:                if (line.length() <= codeEnd) {
2147:                    throw new IOException("status line end after status code");
2148:                }
2149:
2150:                try {
2151:                    responseCode = Integer.parseInt(line.substring(httpEnd + 1,
2152:                            codeEnd));
2153:                } catch (NumberFormatException nfe) {
2154:                    throw new IOException(
2155:                            "status code in response is not a number");
2156:                }
2157:
2158:                responseMsg = line.substring(codeEnd + 1);
2159:            }
2160:
2161:            /** 
2162:             * Read the response message headers.
2163:             * Parse the response headers name value pairs for easy application use.
2164:             *
2165:             * @param in input stream where the response headers are read
2166:             * @exception IOException  is thrown if the response headers cannot 
2167:             *                         be parsed
2168:             */
2169:            private void readHeaders(InputStream in) throws IOException {
2170:                String line;
2171:                String key = null;
2172:                int prevPropIndex = headerFields.size() - 1;
2173:                boolean firstLine = true;
2174:                String value;
2175:                String prevValue = null;
2176:                int index;
2177:
2178:                /*
2179:                 * Initialize and set the current input stream variables
2180:                 */
2181:                bytesleft = 0;
2182:                chunksize = -1;
2183:                bytesread = 0;
2184:                totalbytesread = 0;
2185:                chunkedIn = false;
2186:                eof = false;
2187:
2188:                for (;;) {
2189:                    try {
2190:                        line = readLine(in);
2191:                    } catch (IOException ioe) {
2192:                        throw new IOException(ioe.getMessage());
2193:                    }
2194:
2195:                    if (line == null || line.equals(""))
2196:                        break;
2197:
2198:                    if ((!firstLine)
2199:                            && (line.charAt(0) == ' ' || line.charAt(0) == '\t')) {
2200:                        // This line is a continuation of the previous line.
2201:
2202:                        /*
2203:                         * The continuation is for the user readablility so restore
2204:                         * the CR LF when appending.
2205:                         */
2206:                        value = prevValue + "\r\n" + line;
2207:
2208:                        /*
2209:                         * Set value by index, since there can be multiple properties
2210:                         * with the same key.
2211:                         */
2212:                        headerFields.setPropertyAt(prevPropIndex, value);
2213:                        prevValue = value;
2214:                        continue;
2215:                    }
2216:
2217:                    index = line.indexOf(':');
2218:                    if (index < 0) {
2219:                        throw new IOException("malformed header field " + line);
2220:                    }
2221:
2222:                    key = line.substring(0, index);
2223:                    if (key.length() == 0) {
2224:                        throw new IOException("malformed header field, no key "
2225:                                + line);
2226:                    }
2227:
2228:                    if (line.length() <= index + 1)
2229:                        value = "";
2230:                    else
2231:                        value = line.substring(index + 1).trim();
2232:
2233:                    /**
2234:                     * Check the response header to see if the server would like
2235:                     * to close the connection.
2236:                     * CR#4492849
2237:                     */
2238:                    if ((key.equalsIgnoreCase("connection"))
2239:                            && (value.equalsIgnoreCase("close"))) {
2240:                        ConnectionCloseFlag = true;
2241:                    }
2242:
2243:                    /*
2244:                     * Determine if this is a chunked data transfer. Transfer-Encoding 
2245:                     * header values are treated as case-insensitive 
2246:                     */
2247:                    if ((key.equalsIgnoreCase("transfer-encoding"))
2248:                            && (value.equalsIgnoreCase("chunked"))) {
2249:                        chunkedIn = true;
2250:                    }
2251:
2252:                    /*
2253:                     * Update the Content-Length based on the header value.
2254:                     */
2255:                    if (key.equalsIgnoreCase("content-length")) {
2256:                        try {
2257:                            contentLength = Integer.parseInt(value);
2258:                        } catch (IllegalArgumentException iae) {
2259:                            // fall through
2260:                        } catch (NullPointerException npe) {
2261:                            // fall through
2262:                        }
2263:                    }
2264:
2265:                    /* Save the response key value pairs. */
2266:                    headerFields.addProperty(key, value);
2267:                    firstLine = false;
2268:                    prevPropIndex++;
2269:                    prevValue = value;
2270:
2271:                }
2272:
2273:                /* Initialize the amount of data expected. */
2274:                if (chunkedIn) {
2275:                    chunksize = readChunkSize();
2276:                } else {
2277:                    // do not let the read block if there is no data.
2278:                    if (method.equals(HEAD)) {
2279:                        chunksize = 0;
2280:                    } else {
2281:                        // treat non chunked data of known length as one big chunk
2282:                        chunksize = contentLength;
2283:                    }
2284:                }
2285:
2286:                /* Last chunk or zero length response data. */
2287:                if (chunksize == 0) {
2288:                    eof = true;
2289:                }
2290:            }
2291:
2292:            /**
2293:             * Uses the shared stringbuffer to read a line terminated by CRLF 
2294:             * and return it as string. Blocks until the line is done or end of
2295:             * stream.
2296:             *
2297:             * @param     in  InputStream to read the data
2298:             * @return    one line of input header or null if end of stream
2299:             * @exception IOException if error encountered while reading headers
2300:             */
2301:            private String readLine(InputStream in) throws IOException {
2302:                int c;
2303:
2304:                try {
2305:                    for (;;) {
2306:                        c = in.read();
2307:                        if (c < 0) {
2308:                            return null;
2309:                        }
2310:
2311:                        if (c == '\r') {
2312:                            continue;
2313:                        }
2314:
2315:                        if (c == '\n') {
2316:                            break;
2317:                        }
2318:
2319:                        stringbuffer.append((char) c);
2320:                    }
2321:
2322:                    /* Return a whole line and reset the string buffer. */
2323:                    String line = stringbuffer.toString();
2324:
2325:                    return line;
2326:                } finally {
2327:                    stringbuffer.setLength(0);
2328:                }
2329:            }
2330:
2331:            /**
2332:             * Close the OutputStream and transition to connected state.
2333:             *
2334:             * @exception IOException if the subclass throws one
2335:             */
2336:            protected void closeOutputStream() throws IOException {
2337:                try {
2338:                    /*
2339:                     * Send a request to the web server if there wasn't one
2340:                     * sent already
2341:                     */
2342:                    sendRequest();
2343:
2344:                    /* Finish the common close processing. */
2345:                    super .closeOutputStream();
2346:                } catch (Exception e) {
2347:                    /* Finish the common close processing. */
2348:                    super .closeOutputStream();
2349:                    if (e instanceof  IOException) {
2350:                        throw (IOException) e;
2351:                    }
2352:
2353:                    throw (RuntimeException) e;
2354:                }
2355:            }
2356:
2357:            /**
2358:             * Disconnect the current low level socket connection. If the connection
2359:             * is an HTTP1.1 connection that connection will be put back in the pool
2360:             * for another session to use to connect.
2361:             */
2362:            protected void disconnect() throws IOException {
2363:                if (streamConnection == null) {
2364:                    return;
2365:                }
2366:
2367:                /*
2368:                 * If the response had content length and it was not chunked
2369:                 * and the caller did not read more than the content length
2370:                 * eof was not set, so do that now.
2371:                 */
2372:                if (!eof && !chunkedIn && chunksize >= 0
2373:                        && totalbytesread == chunksize) {
2374:                    eof = true;
2375:                }
2376:
2377:                /*
2378:                 * reasons for not reusing the connection are:
2379:                 *
2380:                 * 1. only part of the chucked request body was sent
2381:                 * 2. caller left response data in the stream
2382:                 * 3. it is a 1.0 connection
2383:                 * 4. there was a signal to close the connection
2384:                 * 5. reading in progress on this connection in another thread
2385:                 */
2386:                synchronized (streamInput) {
2387:                    if (readInProgress) {
2388:                        // do not save the connection
2389:                        ConnectionCloseFlag = true;
2390:                    }
2391:                }
2392:
2393:                if (!requestFinished || !eof || httpVer.equals("HTTP/1.0")
2394:                        || ConnectionCloseFlag) {
2395:                    if (streamConnection instanceof  StreamConnectionElement) {
2396:                        // we got this connection from the pool
2397:                        connectionPool
2398:                                .remove((StreamConnectionElement) streamConnection);
2399:                    } else {
2400:                        disconnect(streamConnection, streamInput, streamOutput);
2401:                    }
2402:
2403:                    return;
2404:                }
2405:
2406:                if (streamConnection instanceof  StreamConnectionElement) {
2407:                    // we got this connection from the pool
2408:                    connectionPool
2409:                            .returnForReuse((StreamConnectionElement) streamConnection);
2410:                    return;
2411:                }
2412:
2413:                // save the connection for reuse
2414:                if (!connectionPool.add(protocol, url.host, url.port,
2415:                        streamConnection, streamOutput, streamInput)) {
2416:                    // pool full, disconnect
2417:                    disconnect(streamConnection, streamInput, streamOutput);
2418:                }
2419:            }
2420:
2421:            /** 
2422:             * Disconnect from the underlying socket transport.
2423:             * Closes the low level socket connection and the input and 
2424:             * output streams used by the socket.
2425:             * <p>
2426:             * Warning: A subclass that implements connect, should also implement this
2427:             * method without calling this method.
2428:             *
2429:             * @param connection connection return from {@link #connect()}
2430:             * @param inputStream input stream opened from <code>connection</code>
2431:             * @param outputStream output stream opened from <code>connection</code>
2432:             * @exception IOException if an I/O error occurs while
2433:             *                  the connection is terminated.
2434:             * @exception IOException is thrown if the connection or 
2435:             *                        associated streams cannot be closed
2436:             */
2437:            protected void disconnect(StreamConnection connection,
2438:                    InputStream inputStream, OutputStream outputStream)
2439:                    throws IOException {
2440:                try {
2441:                    if (connection != null) {
2442:                        connection.close();
2443:                    }
2444:                } finally {
2445:                    try {
2446:                        if (outputStream != null) {
2447:                            outputStream.close();
2448:                        }
2449:                    } finally {
2450:                        if (inputStream != null) {
2451:                            inputStream.close();
2452:                        }
2453:                    }
2454:                }
2455:            }
2456:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.