Source Code Cross Referenced for HTTPParserImpl.java in  » Web-Server » JicarillaHTTP » org » jicarilla » 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 » Web Server » JicarillaHTTP » org.jicarilla.http 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /* ====================================================================
0002:         The Jicarilla Software License
0003:
0004:         Copyright (c) 2003 Leo Simons.
0005:         All rights reserved.
0006:
0007:         Permission is hereby granted, free of charge, to any person obtaining
0008:         a copy of this software and associated documentation files (the
0009:         "Software"), to deal in the Software without restriction, including
0010:         without limitation the rights to use, copy, modify, merge, publish,
0011:         distribute, sublicense, and/or sell copies of the Software, and to
0012:         permit persons to whom the Software is furnished to do so, subject to
0013:         the following conditions:
0014:
0015:         The above copyright notice and this permission notice shall be
0016:         included in all copies or substantial portions of the Software.
0017:
0018:         THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
0019:         EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
0020:         MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
0021:         IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
0022:         CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
0023:         TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
0024:         SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0025:        ==================================================================== */
0026:        package org.jicarilla.http;
0027:
0028:        import org.jicarilla.http.util.Iso646;
0029:        import org.jicarilla.http.util.NioUtil;
0030:        import org.jicarilla.lang.Assert;
0031:
0032:        import java.nio.ByteBuffer;
0033:
0034:        /**
0035:         * <p>A component that understands HTTP. Fires off events to a
0036:         * handler similarly to the way SAX works. Is *not* threadsafe.</p>
0037:         *
0038:         * <p>The most common state machine failures are:</p>
0039:         * <ul>
0040:         *     <li>AssertionError if an expected LF doesn't follow on CR</li>
0041:         *     <li>413 if buffer exceeds MAX_BUFFER_SIZE (for some message parts
0042:         *         only)</li>
0043:         *     <li>400 if a byte is encountered that is not acceptable for the current
0044:         *         part of a message.</li>
0045:         * 
0046:         * @todo probably replace all uses of Assert with exception throwing
0047:         * @todo more javadocs
0048:         * @author <a href="lsimons at jicarilla dot org">Leo Simons</a>
0049:         * @version $Id: HTTPParserImpl.java,v 1.10 2004/04/03 10:13:24 lsimons Exp $
0050:         */
0051:        public class HTTPParserImpl implements  HTTPParser {
0052:            /**
0053:             * If the current view buffer exceeds this value,
0054:             * we throw an HTTP Exception. This prevents us from
0055:             * hanging on really big requests. (10kb worth of
0056:             * buffer should be plenty)
0057:             */
0058:            public final static int MAX_BUFFER_SIZE = 10000;
0059:
0060:            // ----------------------------------------------------------------------
0061:            //  Properties
0062:            // ----------------------------------------------------------------------
0063:            /**
0064:             * The basic container for all aspects of the parser state.
0065:             */
0066:            protected Context c;
0067:
0068:            /**
0069:             * The handler that will be notified whenever some piece of information
0070:             * about the message is found.
0071:             */
0072:            protected HTTPHandler m_handler;
0073:            /**
0074:             * The handler that will be notified whenever any kind of problem is
0075:             * encountered.
0076:             */
0077:            protected HTTPErrorHandler m_errorHandler;
0078:
0079:            // ----------------------------------------------------------------------
0080:            // State Machine States
0081:            // ----------------------------------------------------------------------
0082:            /**
0083:             * <pre>
0084:             * Description:
0085:             *   An internal error exists somewhere. Unused.
0086:             * 
0087:             * Next state:
0088:             *   Not applicable.
0089:             * </pre>
0090:             */
0091:            public final static int INVALID = -2; // unused
0092:            /**
0093:             * <pre>
0094:             * Description:
0095:             *   Haven't read a single byte yet of the upcoming message.
0096:             * 
0097:             * Next state:
0098:             *   - {@link SKIPPING_HEADER_NEWLINES}, always.
0099:             *     before: send newMessage().
0100:             * </pre>
0101:             */
0102:            public final static int NOT_STARTED = -1;
0103:            /**
0104:             * <pre>
0105:             * Description:
0106:             *   Ignoring CRLF sequences before the start of the message.
0107:             * 
0108:             * Next state:
0109:             *   - {@link SKIPPING_HEADER_NEWLINES}, if there's more CRLF.
0110:             *   - {@link LOOKING_FOR_FIELD1}, if there's anything else.
0111:             * </pre>
0112:             */
0113:            public final static int SKIPPING_HEADER_NEWLINES = 0;
0114:            /**
0115:             * <pre>
0116:             * Description:
0117:             *   Accumulating the first start line field of the message.
0118:             * 
0119:             * Next state:
0120:             *   - {@link LOOKING_FOR_FIELD1}, if the field hasn't ended yet.
0121:             *   - {@link LOOKING_FOR_FIELD2}, if SP is encountered.
0122:             *     before: send foundStartLineFirstField().
0123:             * </pre>
0124:             */
0125:            public final static int LOOKING_FOR_FIELD1 = 10;
0126:            /**
0127:             * <pre>
0128:             * Description:
0129:             *   Accumulating the second start line field of the message.
0130:             * 
0131:             * Next state:
0132:             *   - {@link LOOKING_FOR_FIELD2}, if the field hasn't ended yet.
0133:             *   - {@link LOOKING_FOR_FIELD3}, if SP is encountered.
0134:             *     before: send foundStartLineSecondField().
0135:             * </pre>
0136:             */
0137:            public final static int LOOKING_FOR_FIELD2 = 20;
0138:            /**
0139:             * <pre>
0140:             * Description:
0141:             *   Accumulating the third start line field of the message.
0142:             * 
0143:             * Next state:
0144:             *   - {@link LOOKING_FOR_FIELD3}, if the field hasn't ended yet.
0145:             *   - {@link LOOKING_FOR_NEW_HEADER}, if CR is encountered.
0146:             *     before: send foundStartLineThirdField().
0147:             * </pre>
0148:             */
0149:            public final static int LOOKING_FOR_FIELD3 = 30;
0150:            /**
0151:             * <pre>
0152:             * Description:
0153:             *   Deciding on state change to new header or body.
0154:             * 
0155:             * Next state:
0156:             *   - {@link LOOKING_FOR_HEADER_NAME}, if a header is coming.
0157:             *   - {@link LOOKING_FOR_BODY}, if CR is encountered.
0158:             * </pre>
0159:             */
0160:            public final static int LOOKING_FOR_NEW_HEADER = 40;
0161:            /**
0162:             * <pre>
0163:             * Description:
0164:             *   Accumulating the name of the current header.
0165:             * 
0166:             * Next state:
0167:             *   - {@link LOOKING_FOR_HEADER_NAME}, if the name hasn't finished yet.
0168:             *   - {@link SKIPPING_WHITESPACE_AFTER_HEADER_NAME}, if COLON is
0169:             *     encountered.
0170:             *     before: send foundHeaderName().
0171:             * </pre>
0172:             */
0173:            public final static int LOOKING_FOR_HEADER_NAME = 50;
0174:            /**
0175:             * <pre>
0176:             * Description:
0177:             *   Ignoring whitespace before the header value.
0178:             * 
0179:             * Next state:
0180:             *   - {@link SKIPPING_WHITESPACE_AFTER_HEADER_NAME}, if there's more
0181:             *     whitespace.
0182:             *   - {@link LOOKING_FOR_HEADER_VALUE}, if non-whitespace is
0183:             *     encountered.
0184:             *   - {@link LOOKING_FOR_HEADER_VALUE_OR_NEXT_HEADER}, if CR is
0185:             *     encountered.
0186:             * </pre>
0187:             */
0188:            public final static int SKIPPING_WHITESPACE_AFTER_HEADER_NAME = 60;
0189:            /**
0190:             * <pre>
0191:             * Description:
0192:             *   Accumulating the value of the current header.
0193:             * 
0194:             * Next state:
0195:             *   - {@link LOOKING_FOR_HEADER_VALUE}, if the header value hasn't ended
0196:             *     yet.
0197:             *   - {@link LOOKING_FOR_HEADER_VALUE_OR_NEXT_HEADER}, if CR is
0198:             *     encountered.
0199:             * </pre>
0200:             */
0201:            public final static int LOOKING_FOR_HEADER_VALUE = 70;
0202:            /**
0203:             * <pre>
0204:             * Description:
0205:             *   Decide whether a header value is complete or whether it is continued
0206:             *   on the next line.
0207:             * 
0208:             * Next state:
0209:             *   - {@link SKIPPING_HEADER_VALUE_WHITESPACE}, if a SP is encountered.
0210:             *   - {@link LOOKING_FOR_BODY}, if a CR is encountered.
0211:             *     before: send foundHeaderValue().
0212:             *   - {@link LOOKING_FOR_HEADER_NAME}, if another header is coming.
0213:             *     before: send foundHeaderValue().
0214:             * </pre>
0215:             */
0216:            public final static int LOOKING_FOR_HEADER_VALUE_OR_NEXT_HEADER = 71;
0217:            /**
0218:             * <pre>
0219:             * Description:
0220:             *   Ignoring the continuation whitespace in a multiline header. 
0221:             * 
0222:             * Next state:
0223:             *   - {@link SKIPPING_HEADER_VALUE_WHITESPACE}, if SP or HT is
0224:             *     encountered.
0225:             *   - {@link LOOKING_FOR_HEADER_VALUE}, if the continuation whitespace
0226:             *     ends.
0227:             * </pre>
0228:             */
0229:            public final static int SKIPPING_HEADER_VALUE_WHITESPACE = 80;
0230:            /**
0231:             * <pre>
0232:             * Description:
0233:             *   Decide whether there is a body. This is not actually in use as
0234:             *   a state, but rather a special function is called from other
0235:             *   states which are said to change into this one. This function
0236:             *   decides what to look for by querying the handler using
0237:             *   getBodyType(). 
0238:             * 
0239:             * Next state:
0240:             *   - {@link DONE}, if there is no body.
0241:             *   - {@link LOOKING_FOR_CHUNKED_BODY}, if a body with chunked
0242:             *     transfer-coding is coming.
0243:             *   - {@link LOOKING_FOR_NORMAL_BODY_UP_TO_SIZE}, if a body without
0244:             *     transfer-coding (or with an unknown transfer-coding) is coming.
0245:             * </pre>
0246:             */
0247:            public final static int LOOKING_FOR_BODY = 90; // unused
0248:            /**
0249:             * <pre>
0250:             * Description:
0251:             *   There is no body to look for. This is not actually in use as
0252:             *   a state, but is included purely to make behaviour of the body
0253:             *   type selection clearer and consistent with getBodyType()
0254:             *   contracts.
0255:             * 
0256:             * Next state:
0257:             *   not applicable.
0258:             * </pre>
0259:             */
0260:            public final static int LOOKING_FOR_NO_BODY = 91; // unused
0261:            /**
0262:             * <pre>
0263:             * Description:
0264:             *   Preparing for a body with chunked transfer coding.
0265:             * 
0266:             * Next state:
0267:             *   - {@link LOOKING_FOR_CHUNKED_BODY_CHUNK_SIZE}, always.
0268:             * </pre>
0269:             */
0270:            public final static int LOOKING_FOR_CHUNKED_BODY = 92;
0271:            /**
0272:             * <pre>
0273:             * Description:
0274:             *   Preparing for a body for which no size is known yet.
0275:             * 
0276:             * Next state:
0277:             *   - {@link LOOKING_FOR_NORMAL_BODY_UP_TO_SIZE}, if the handler can
0278:             *     provide the expected size for the body.
0279:             * 
0280:             * Failure:
0281:             *   - 411 if the handler cannot produce an expected size for the body.
0282:             * </pre>
0283:             */
0284:            public final static int LOOKING_FOR_NORMAL_BODY = 93;
0285:            /**
0286:             * <pre>
0287:             * Description:
0288:             *   Accumulating the body up to its specified size.
0289:             * 
0290:             * Next state:
0291:             *   - {@link LOOKING_FOR_NORMAL_BODY_UP_TO_SIZE}, if the whole body has
0292:             *     not been read yet.
0293:             *   - {@link DONE}, if the whole body has been read.
0294:             *     before: send foundBody().
0295:             *     before: send endMessage().
0296:             * </pre>
0297:             */
0298:            public final static int LOOKING_FOR_NORMAL_BODY_UP_TO_SIZE = 94;
0299:            /**
0300:             * <pre>
0301:             * Description:
0302:             *   Accumulate the hex size of the current chunk.
0303:             * 
0304:             * Next state:
0305:             *   - {@link LOOKING_FOR_CHUNKED_BODY_CHUNK_SIZE}, if the field is not
0306:             *     complete yet.
0307:             *   - {@link LOOKING_FOR_CHUNKED_BODY_CHUNK_CONTENTS}, if CR is
0308:             *     encountered and the size is not 0.
0309:             *   - {@link LOOKING_FOR_TRAILER}, if CR is
0310:             *     encountered and the size is 0.
0311:             * </pre>
0312:             */
0313:            public static final int LOOKING_FOR_CHUNKED_BODY_CHUNK_SIZE = 95;
0314:            /**
0315:             * <pre>
0316:             * Description:
0317:             *   Accumulate the current chunk up to its specified size.
0318:             * 
0319:             * Next state:
0320:             *   - {@link LOOKING_FOR_CHUNKED_BODY_CHUNK_CONTENTS}, if the whole
0321:             *     chunk hasn't been read yet.
0322:             *   - {@link LOOKING_FOR_CHUNKED_BODY_CHUNK_SIZE}, if the whole
0323:             *     chunk has been read.
0324:             *     before: send foundBody().
0325:             * </pre>
0326:             */
0327:            public static final int LOOKING_FOR_CHUNKED_BODY_CHUNK_CONTENTS = 96;
0328:            /**
0329:             * <pre>
0330:             * Description:
0331:             *   Decide whether a trailer is coming by calling hasTrailers() on
0332:             *   the handler.
0333:             * 
0334:             * Next state:
0335:             *   - {@link LOOKING_FOR_TRAILER_NAME}, if trailers are coming.
0336:             *   - {@link DONE}, if no trailers are coming.
0337:             *     before: send endMessage().
0338:             * </pre>
0339:             */
0340:            public final static int LOOKING_FOR_TRAILER = 100;
0341:            /**
0342:             * <pre>
0343:             * Description:
0344:             *   Accumulating the name of the current trailer.
0345:             * 
0346:             * Next state:
0347:             *   - {@link LOOKING_FOR_TRAILER_NAME}, if the trailer hasn't finished
0348:             *     yet.
0349:             *   - {@link SKIPPING_WHITESPACE_AFTER_TRAILER_NAME}, if COLON is
0350:             *     encountered.
0351:             *     before: send foundFooterName().
0352:             * </pre>
0353:             */
0354:            public final static int LOOKING_FOR_TRAILER_NAME = 110;
0355:            /**
0356:             * <pre>
0357:             * Description:
0358:             *   Ignoring whitespace before the trailer value.
0359:             * 
0360:             * Next state:
0361:             *   - {@link SKIPPING_WHITESPACE_AFTER_TRAILER_NAME}, if there's more
0362:             *     whitespace.
0363:             *   - {@link LOOKING_FOR_TRAILER_VALUE}, if non-whitespace is
0364:             *     encountered.
0365:             *   - {@link LOOKING_FOR_TRAILER_VALUE_OR_NEXT_TRAILER}, if CR is
0366:             *     encountered.
0367:             * </pre>
0368:             */
0369:            public final static int SKIPPING_WHITESPACE_AFTER_TRAILER_NAME = 120;
0370:            /**
0371:             * <pre>
0372:             * Description:
0373:             *   Accumulating the value of the current trailer.
0374:             * 
0375:             * Next state:
0376:             *   - {@link LOOKING_FOR_HEADER_VALUE}, if the trailer value hasn't ended
0377:             *     yet.
0378:             *   - {@link LOOKING_FOR_HEADER_VALUE_OR_NEXT_HEADER}, if CR is
0379:             *     encountered.
0380:             * </pre>
0381:             */
0382:            public final static int LOOKING_FOR_TRAILER_VALUE = 130;
0383:            /**
0384:             * <pre>
0385:             * Description:
0386:             *   Decide whether a trailer value is complete or whether it is continued
0387:             *   on the next line.
0388:             * 
0389:             * Next state:
0390:             *   - {@link SKIPPING_HEADER_VALUE_WHITESPACE}, if a SP is encountered.
0391:             *   - {@link LOOKING_FOR_BODY}, if a CR is encountered.
0392:             *     before: send foundTrailerValue().
0393:             *   - {@link LOOKING_FOR_HEADER_NAME}, if another header is coming.
0394:             *     before: send foundTrailerValue().
0395:             * </pre>
0396:             */
0397:            public final static int LOOKING_FOR_TRAILER_VALUE_OR_NEXT_TRAILER = 131;
0398:            /**
0399:             * <pre>
0400:             * Description:
0401:             *   Ignoring the continuation whitespace in a multiline trailer. 
0402:             * 
0403:             * Next state:
0404:             *   - {@link SKIPPING_TRAILER_VALUE_WHITESPACE}, if SP or HT is
0405:             *     encountered.
0406:             *   - {@link LOOKING_FOR_TRAILER_VALUE}, if the continuation whitespace
0407:             *     ends.
0408:             * </pre>
0409:             */
0410:            public final static int SKIPPING_TRAILER_VALUE_WHITESPACE = 140;
0411:            /**
0412:             * <pre>
0413:             * Description:
0414:             *   Alias for {@link NOT_STARTED}.
0415:             * </pre>
0416:             */
0417:            public final static int DONE = NOT_STARTED; // loop!
0418:
0419:            // ----------------------------------------------------------------------
0420:            //  Constructor
0421:            // ----------------------------------------------------------------------
0422:            /**
0423:             * Create a new instance of the parser. You should create a parser instance
0424:             * per thread of execution, since the parser is single-threaded and it
0425:             * saves state between invocations of {@link #parse(ByteBuffer)}.
0426:             * 
0427:             * @param handler the {@link HTTPHandler} where we should direct parsing
0428:             *    events as they occur. Should not be null.
0429:             * @param errorHandler the {@link HTTPErrorHandler} where we should direct
0430:             *    parsing problems as they occur. Should not be null.
0431:             */
0432:            public HTTPParserImpl(final HTTPHandler handler,
0433:                    final HTTPErrorHandler errorHandler) {
0434:                setHandler(handler);
0435:                setErrorHandler(errorHandler);
0436:
0437:                setContext(new Context());
0438:            }
0439:
0440:            // ----------------------------------------------------------------------
0441:            //  Getters/Setters
0442:            // ----------------------------------------------------------------------
0443:            /**
0444:             * Get the {@link HTTPHandler} where we should direct parsing
0445:             * events as they occur.
0446:             *  
0447:             * @return the {@link HTTPHandler} where we should direct parsing
0448:             * events as they occur.
0449:             */
0450:            public HTTPHandler getHandler() {
0451:                return m_handler;
0452:            }
0453:
0454:            /**
0455:             * Set the {@link HTTPHandler} where we should direct parsing
0456:             * events as they occur. Should not be null.
0457:             * 
0458:             * @param handler the {@link HTTPHandler} where we should direct parsing
0459:             * events as they occur.
0460:             */
0461:            public void setHandler(final HTTPHandler handler) {
0462:                Assert.assertNotNull(handler);
0463:                m_handler = handler;
0464:            }
0465:
0466:            /**
0467:             * Get the {@link HTTPErrorHandler} where we should direct
0468:             * parsing problems as they occur.
0469:             * 
0470:             * @return the {@link HTTPErrorHandler} where we should direct
0471:             *    parsing problems as they occur.
0472:             */
0473:            public HTTPErrorHandler getErrorHandler() {
0474:                return m_errorHandler;
0475:            }
0476:
0477:            /**
0478:             * Set the {@link HTTPErrorHandler} where we should direct
0479:             * parsing problems as they occur. Should not be null.
0480:             * 
0481:             * @param errorHandler the {@link HTTPErrorHandler} where we should direct
0482:             *    parsing problems as they occur.
0483:             */
0484:            public void setErrorHandler(final HTTPErrorHandler errorHandler) {
0485:                Assert.assertNotNull(errorHandler);
0486:                m_errorHandler = errorHandler;
0487:            }
0488:
0489:            /**
0490:             * Get the basic container for all aspects of the parser state.
0491:             * 
0492:             * @return the basic container for all aspects of the parser state.
0493:             */
0494:            protected Context getContext() {
0495:                return c;
0496:            }
0497:
0498:            /**
0499:             * Set the basic container for all aspects of the parser state. Should
0500:             * not be null.
0501:             * 
0502:             * @param context the basic container for all aspects of the parser state.
0503:             */
0504:            protected void setContext(final Context context) {
0505:                this .c = context;
0506:            }
0507:
0508:            // ----------------------------------------------------------------------
0509:            //  Work Interface: HTTPParser
0510:            // ----------------------------------------------------------------------
0511:
0512:            /**
0513:             * Re-initialize the parser, throwing away all current parser state.
0514:             * 
0515:             * @see HTTPParser#reset().
0516:             */
0517:            public void reset() {
0518:                //try { getHandler().endMessage(); }
0519:                //catch( Exception e ) { }
0520:                getHandler().endMessage();
0521:
0522:                getContext().recycle();
0523:            }
0524:
0525:            /**
0526:             * <p>Parse a piece of an HTTP message. Equivalent to calling</p>
0527:             *
0528:             * <pre>
0529:             * parse( source, source.limit() );
0530:             * </pre>
0531:             *
0532:             * @param source the buffer to read from. Note that you should
0533:             *   never modify this buffer after passing it to the parser!
0534:             * @throws HTTPException
0535:             * @see HTTPParser#parse(ByteBuffer).
0536:             */
0537:            public void parse(final ByteBuffer source) throws HTTPException {
0538:                Assert.assertNotNull(source);
0539:                parse(source, source.limit());
0540:            }
0541:
0542:            /**
0543:             * Parse a piece of an HTTP message. The buffer will be read
0544:             * from its current position up to its limit. On return, the
0545:             * buffers position will be at its end.
0546:             *
0547:             * @param source the buffer to read from. Note that you should
0548:             *   never modify this buffer after passing it to the parser!
0549:             * @param limit the number of bytes to read from the source
0550:             * @throws HTTPException
0551:             * @see HTTPParser#parse(ByteBuffer,int).
0552:             */
0553:            public void parse(final ByteBuffer source, final int limit)
0554:                    throws HTTPException {
0555:                // validate arguments
0556:                Assert.assertNotNull(source);
0557:                if (source.remaining() <= 0 || limit <= 0)
0558:                    return;
0559:
0560:                getContext().checkAgainstReallyBigBuffers(limit);
0561:                doPreParsingSetup(source, limit);
0562:                doParsingLoop();
0563:                doPostParsingCleanup();
0564:            }
0565:
0566:            // ----------------------------------------------------------------------
0567:            //  Parser Core
0568:            // ----------------------------------------------------------------------
0569:
0570:            /**
0571:             * Prepare for running the state machine and initialize the basic
0572:             * {@link c context}.
0573:             * 
0574:             * @param source the source for the HTTP message.
0575:             * @param limit up to where to read from the source.
0576:             * @throws HTTPException if the source is too large.
0577:             */
0578:            protected void doPreParsingSetup(final ByteBuffer source,
0579:                    final int limit) throws HTTPException {
0580:                // set up per-invocation state
0581:                getContext().source = source;
0582:
0583:                if (limit > getContext().source.limit())
0584:                    getContext().limit = getContext().source.limit();
0585:                else
0586:                    getContext().limit = limit;
0587:
0588:                if (!getContext().mergeSourceWithLeftovers())
0589:                    getContext().newView(); // happened already for leftovers
0590:            }
0591:
0592:            /**
0593:             * Run the state machine.
0594:             * 
0595:             * @throws HTTPException if an exception occurs in the state machine.
0596:             */
0597:            protected void doParsingLoop() throws HTTPException {
0598:                // parse byte-by-byte
0599:                // todo test against this
0600:                // while( getContext().source.hasRemaining() )
0601:                for (int i = 0; i < getContext().limit
0602:                        && getContext().source.hasRemaining(); i++) {
0603:                    //try
0604:                    //{
0605:                    getContext().next();
0606:                    doParse();
0607:                    //}
0608:                    //catch( BufferUnderflowException bue )
0609:                    //{
0610:                    //    getErrorHandler().exceptionOccurred(
0611:                    //            new HTTPException( HTTPEncoding.STATUS_400_Bad_Request )
0612:                    //    );
0613:                    //}
0614:                }
0615:            }
0616:
0617:            /**
0618:             * Clean up after the state machine and make sure the basic
0619:             * {@link c context} is ready for another {@link #parse(ByteBuffer)}
0620:             * invocation.
0621:             */
0622:            protected void doPostParsingCleanup() {
0623:                // keep any leftovers around
0624:                if (getContext().view != null
0625:                        && getContext().view.capacity() != 0) {
0626:                    getContext().leftovers = getContext().view;
0627:                    getContext().view = null;
0628:                }
0629:            }
0630:
0631:            // ----------------------------------------------------------------------
0632:            //  The Big Fat Ugly State Machine
0633:            // ----------------------------------------------------------------------
0634:            /**
0635:             * This is a nearly clean finite state machine. We take a few shortcuts
0636:             * to handle things like CRLF. Also, we sometimes "fall through" to the
0637:             * next state immediately rather than back up a character and run the
0638:             * machine again.
0639:             * To undestand the internals of this machine and the choices made, you'll
0640:             * likely want to refer to the HTTP spec on a routine basis. Also, make
0641:             * sure to understand the possible states and read their descriptions.
0642:             * 
0643:             * @throws HTTPException
0644:             */
0645:            protected void doParse() throws HTTPException {
0646:                // skip line breaks
0647:                if (getContext().doSkipLF())
0648:                    return;
0649:                //if( getContext().doSkipCRLF() ) return;
0650:
0651:                // skip garbage
0652:                /*if( getContext().ch == Iso646.NULL
0653:                    && getContext().state < 90 ) return;*/
0654:
0655:                switch (getContext().state) {
0656:                // Set up a little state
0657:                case NOT_STARTED: // == DONE
0658:                    getHandler().newMessage();
0659:                    getContext().state = SKIPPING_HEADER_NEWLINES;
0660:                    // fall through (we've already got the first character)
0661:
0662:                    // Skip any CRLF sequences before the actual message starts
0663:                case SKIPPING_HEADER_NEWLINES:
0664:                    if (getContext().ch == HTTPEncoding.CR) {
0665:                        getContext().skipLF();
0666:                        break;
0667:                    } else {
0668:                        getContext().newViewOffByOne();
0669:                        getContext().state = LOOKING_FOR_FIELD1;
0670:                        // fall through (we've already got the first character)
0671:                    }
0672:
0673:                    // ----------------------------------------------------------------------
0674:                    //  Start line
0675:                    // ----------------------------------------------------------------------
0676:                    // find the first field on the start line (up to the first SP, that is)
0677:                case LOOKING_FOR_FIELD1:
0678:                    if (getContext().ch == HTTPEncoding.SP)
0679:                        foundStartLineFirstField();
0680:                    else
0681:                        checkStartLineFirstFieldValidity();
0682:                    break;
0683:
0684:                // find the second field on the start line (up to the second SP, that is)
0685:                case LOOKING_FOR_FIELD2:
0686:                    if (getContext().ch == HTTPEncoding.SP)
0687:                        foundStartLineSecondField();
0688:                    else
0689:                        checkStartLineSecondFieldValidity();
0690:                    break;
0691:
0692:                // find the third field on the start line (up to the first CR, that is)
0693:                case LOOKING_FOR_FIELD3:
0694:                    if (getContext().ch == HTTPEncoding.CR)
0695:                        foundStartLineThirdField();
0696:                    else
0697:                        checkStartLineThirdFieldValidity();
0698:                    break;
0699:
0700:                // find a header, or the body
0701:                case LOOKING_FOR_NEW_HEADER:
0702:                    if (getContext().ch == HTTPEncoding.CR) {
0703:                        getContext().skipLF();
0704:                        gotoBodyState();
0705:                        break;
0706:                    } else {
0707:                        getContext().newViewOffByOne();
0708:                        getContext().state = LOOKING_FOR_HEADER_NAME;
0709:                        // fall through: we've already got the first character of the header name
0710:                    }
0711:
0712:                    // ----------------------------------------------------------------------
0713:                    //  Headers
0714:                    // ----------------------------------------------------------------------
0715:                    // find a header name, including an ugly state to handle the whitespace
0716:                case LOOKING_FOR_HEADER_NAME:
0717:                    if (getContext().ch == Iso646.COLON)
0718:                        foundHeaderName();
0719:                    else
0720:                        checkHeaderNameValidity();
0721:                    break; // get rid of the colon
0722:
0723:                case SKIPPING_WHITESPACE_AFTER_HEADER_NAME:
0724:                    if (getContext().ch != HTTPEncoding.SP
0725:                            && getContext().ch != HTTPEncoding.HT) {
0726:                        if (getContext().ch == HTTPEncoding.CR) {
0727:                            // someone lame started the value with a CR. grmbl.
0728:                            getContext().next();
0729:                            Assert
0730:                                    .assertTrue(getContext().ch == HTTPEncoding.LF);
0731:                            getContext().newView();
0732:                            getContext().state = LOOKING_FOR_HEADER_VALUE_OR_NEXT_HEADER;
0733:                            break;
0734:                        } else {
0735:                            getContext().newViewOffByOne();
0736:                            getContext().state = LOOKING_FOR_HEADER_VALUE;
0737:                            // fall through: we've already got the first character of header value
0738:                        }
0739:                    } else
0740:                        break;
0741:
0742:                    // find a header value, including two ugly states to handle the whitespace
0743:                case LOOKING_FOR_HEADER_VALUE:
0744:                    if (getContext().ch == HTTPEncoding.CR) {
0745:                        getContext().skipLF();
0746:                        getContext().state = LOOKING_FOR_HEADER_VALUE_OR_NEXT_HEADER;
0747:                    } else
0748:                        checkHeaderValueValidity();
0749:                    break;
0750:
0751:                case LOOKING_FOR_HEADER_VALUE_OR_NEXT_HEADER:
0752:                    if (getContext().ch == HTTPEncoding.SP)
0753:                        getContext().state = SKIPPING_HEADER_VALUE_WHITESPACE;
0754:                    else
0755:                        foundHeaderValue();
0756:                    break;
0757:
0758:                case SKIPPING_HEADER_VALUE_WHITESPACE:
0759:                    if (getContext().ch != HTTPEncoding.SP
0760:                            && getContext().ch != HTTPEncoding.HT)
0761:                        lookForHeaderValue();
0762:                    break;
0763:
0764:                // ----------------------------------------------------------------------
0765:                //  Body
0766:                // ----------------------------------------------------------------------
0767:                // note that we will always arrive in this state through a call to
0768:                // getBodyState(), which means we'll always start in just one of the three
0769:                // possible states
0770:
0771:                // look for a body of unspecified length (ie, determine the length)
0772:                case LOOKING_FOR_NORMAL_BODY:
0773:                    findSizeOfNormalBody();
0774:                    // need to fall through! break;
0775:
0776:                    // look for a body of a specified length
0777:                case LOOKING_FOR_NORMAL_BODY_UP_TO_SIZE:
0778:                    foundNormalBody();
0779:
0780:                    break;
0781:
0782:                // look for a body with chunked transfer coding
0783:                case LOOKING_FOR_CHUNKED_BODY:
0784:                    // start reading the size into a new view
0785:                    getContext().newViewOffByOne();
0786:                    getContext().state = LOOKING_FOR_CHUNKED_BODY_CHUNK_SIZE;
0787:                    // fall through
0788:
0789:                case LOOKING_FOR_CHUNKED_BODY_CHUNK_SIZE:
0790:                    // found the end of the size field
0791:                    if (getContext().ch == HTTPEncoding.CR) {
0792:                        // CR followed by LF...make sure it is discarded on the
0793:                        // next round
0794:                        getContext().skipLF();
0795:
0796:                        // mark the end in the view buffer and keep the size around
0797:                        // for the next state
0798:                        getContext().foundChunkSize();
0799:
0800:                        // found the end of the body?
0801:                        if (getContext().chunkSize == 0) {
0802:                            if (getHandler().hasTrailers()) {
0803:                                getContext().state = LOOKING_FOR_TRAILER;
0804:                            } else {
0805:                                // ...which is the end of the message in this case
0806:                                getContext().state = DONE;
0807:                                getHandler().endMessage();
0808:                            }
0809:                        } else {
0810:                            // not the end, another chunk is coming
0811:                            getContext().state = LOOKING_FOR_CHUNKED_BODY_CHUNK_CONTENTS;
0812:                        }
0813:                    }
0814:                    break;
0815:
0816:                case LOOKING_FOR_CHUNKED_BODY_CHUNK_CONTENTS:
0817:                    // start reading the chunk contents into a new view buffer
0818:                    getContext().newViewOffByOne();
0819:
0820:                    // read up to chunkSize, or remaining(), whichever is _less_
0821:                    int remaining = getContext().view.remaining();
0822:                    if (remaining > getContext().chunkSize)
0823:                        remaining = getContext().chunkSize;
0824:                    getContext().view.limit(remaining);
0825:
0826:                    // send whatever part of the body we've found so far
0827:                    if (remaining > 0)
0828:                        getHandler().foundBody(getContext().getAndUnsetView());
0829:
0830:                    // these already were equal, or we had more left than the
0831:                    // message length and we just set it them to be equal...
0832:                    // in either case, equality means we're done with this chunk!
0833:                    if (remaining == getContext().chunkSize) {
0834:                        // skip the rest of the chunk and the closing CRLF
0835:                        // in the source buffer
0836:                        getContext().source.position(getContext().source
0837:                                .position()
0838:                                + remaining + 1);
0839:
0840:                        // we'll always expect another chunk, though it could have
0841:                        // a size of zero
0842:                        getContext().state = LOOKING_FOR_CHUNKED_BODY_CHUNK_SIZE;
0843:                        getContext().newView();
0844:                    } else {
0845:                        // this was an incomplete chunk, skip up to the end
0846:                        // and get the rest of the chunk from the next buffer
0847:                        getContext().source.position(getContext().source
0848:                                .limit());
0849:                        getContext().chunkSize -= remaining;
0850:
0851:                        // that next run will create a new view anyway...no need
0852:                        // to create one here...
0853:                    }
0854:                    break;
0855:
0856:                // ----------------------------------------------------------------------
0857:                //  Trailers
0858:                // ----------------------------------------------------------------------
0859:                // find a trailer name, including an ugly state to handle the whitespace
0860:                case LOOKING_FOR_TRAILER:
0861:                    getContext().expectedTrailers = m_handler.getTrailerNames();
0862:                    getContext().newViewOffByOne();
0863:                    getContext().state = LOOKING_FOR_TRAILER_NAME;
0864:                    // fall through
0865:
0866:                case LOOKING_FOR_TRAILER_NAME:
0867:                    if (getContext().ch == Iso646.COLON)
0868:                        foundTrailerName();
0869:                    else
0870:                        checkHeaderNameValidity();
0871:                    break; // get rid of the colon
0872:
0873:                case SKIPPING_WHITESPACE_AFTER_TRAILER_NAME:
0874:                    if (getContext().ch != HTTPEncoding.SP
0875:                            && getContext().ch != HTTPEncoding.HT) {
0876:                        if (getContext().ch == HTTPEncoding.CR) {
0877:                            // someone lame started the value with a CR. grmbl.
0878:                            getContext().next();
0879:                            Assert
0880:                                    .assertTrue(getContext().ch == HTTPEncoding.LF);
0881:                            getContext().newView();
0882:                            getContext().state = LOOKING_FOR_TRAILER_VALUE_OR_NEXT_TRAILER;
0883:                            break;
0884:                        } else {
0885:                            getContext().newViewOffByOne();
0886:                            getContext().state = LOOKING_FOR_TRAILER_VALUE;
0887:                            // fall through: we've already got the first character of trailer value
0888:                        }
0889:                    } else
0890:                        break;
0891:
0892:                    // find a trailer value, including two ugly states to handle the whitespace
0893:                case LOOKING_FOR_TRAILER_VALUE:
0894:                    if (getContext().ch == HTTPEncoding.CR) {
0895:                        getContext().skipLF();
0896:
0897:                        // todo check for CR
0898:                        if (getContext().view.remaining() <= 1) {
0899:                            foundTrailerValue();
0900:                        }
0901:                        getContext().state = LOOKING_FOR_TRAILER_VALUE_OR_NEXT_TRAILER;
0902:                    } else
0903:                        checkHeaderValueValidity();
0904:                    break;
0905:
0906:                case LOOKING_FOR_TRAILER_VALUE_OR_NEXT_TRAILER:
0907:                    if (getContext().ch == HTTPEncoding.SP)
0908:                        getContext().state = SKIPPING_TRAILER_VALUE_WHITESPACE;
0909:                    else
0910:                        foundTrailerValue();
0911:                    break;
0912:
0913:                case SKIPPING_TRAILER_VALUE_WHITESPACE:
0914:                    if (getContext().ch != HTTPEncoding.SP
0915:                            && getContext().ch != HTTPEncoding.HT)
0916:                        lookForTrailerValue();
0917:                    break;
0918:
0919:                // this is a bug!
0920:                //case INVALID:
0921:                //default:
0922:                //    internalErrorOccurred();
0923:                }
0924:            }
0925:
0926:            // ----------------------------------------------------------------------
0927:            //  End of The Big Fat Ugly State Machine
0928:            // ----------------------------------------------------------------------
0929:
0930:            // ----------------------------------------------------------------------
0931:            //  State Machine protocol verifiers
0932:            // ----------------------------------------------------------------------
0933:
0934:            /**
0935:             * Verify if the character being added to this field is valid.
0936:             * 
0937:             * @throws HTTPException if the character being added to this field is
0938:             *    invalid.
0939:             */
0940:            protected void checkStartLineFirstFieldValidity()
0941:                    throws HTTPException {
0942:                if (!HTTPEncoding.isTokenChar(getContext().ch)
0943:                        && !HTTPEncoding.isTextChar(getContext().ch))
0944:                    getErrorHandler().exceptionOccurred(
0945:                            new HTTPException(
0946:                                    HTTPEncoding.STATUS_400_Bad_Request,
0947:                                    "Illegal character '" + getContext().ch
0948:                                            + "' (with integer value "
0949:                                            + (int) getContext().ch
0950:                                            + ") in first field!"));
0951:            }
0952:
0953:            /**
0954:             * Verify if the character being added to this field is valid.
0955:             * 
0956:             * @throws HTTPException if the character being added to this field is
0957:             *    invalid.
0958:             */
0959:            protected void checkStartLineSecondFieldValidity()
0960:                    throws HTTPException {
0961:                if (!HTTPEncoding.isTokenChar(getContext().ch)
0962:                        && !HTTPEncoding.isTextChar(getContext().ch))
0963:                    getErrorHandler().exceptionOccurred(
0964:                            new HTTPException(
0965:                                    HTTPEncoding.STATUS_400_Bad_Request,
0966:                                    "Illegal character '" + getContext().ch
0967:                                            + "' in second field!"));
0968:            }
0969:
0970:            /**
0971:             * Verify if the character being added to this field is valid.
0972:             * 
0973:             * @throws HTTPException if the character being added to this field is
0974:             *    invalid.
0975:             */
0976:            protected void checkStartLineThirdFieldValidity()
0977:                    throws HTTPException {
0978:                if (!HTTPEncoding.isTokenChar(getContext().ch)
0979:                        && !HTTPEncoding.isTextChar(getContext().ch))
0980:                    getErrorHandler().exceptionOccurred(
0981:                            new HTTPException(
0982:                                    HTTPEncoding.STATUS_400_Bad_Request,
0983:                                    "Illegal character '" + getContext().ch
0984:                                            + "' (with integer value "
0985:                                            + (int) getContext().ch
0986:                                            + ") in third field!"));
0987:            }
0988:
0989:            /**
0990:             * Verify if the character being added to this name is valid.
0991:             * 
0992:             * @throws HTTPException if the character being added to this name is
0993:             *    invalid.
0994:             */
0995:            protected void checkHeaderNameValidity() throws HTTPException {
0996:                if (!HTTPEncoding.isTokenChar(getContext().ch)
0997:                        && !HTTPEncoding.isTextChar(getContext().ch))
0998:                    getErrorHandler().exceptionOccurred(
0999:                            new HTTPException(
1000:                                    HTTPEncoding.STATUS_400_Bad_Request,
1001:                                    "Illegal character '" + getContext().ch
1002:                                            + "' (with integer value "
1003:                                            + (int) getContext().ch
1004:                                            + ") in header name!"));
1005:            }
1006:
1007:            /**
1008:             * Verify if the character being added to this value is valid.
1009:             * 
1010:             * @throws HTTPException if the character being added to this value is
1011:             *    invalid.
1012:             */
1013:            protected void checkHeaderValueValidity() throws HTTPException {
1014:                if (!HTTPEncoding.isTokenChar(getContext().ch)
1015:                        && !HTTPEncoding.isTextChar(getContext().ch))
1016:                    getErrorHandler().exceptionOccurred(
1017:                            new HTTPException(
1018:                                    HTTPEncoding.STATUS_400_Bad_Request,
1019:                                    "Illegal character '" + getContext().ch
1020:                                            + "' (with integer value "
1021:                                            + (int) getContext().ch
1022:                                            + ") in header value!"));
1023:            }
1024:
1025:            // ----------------------------------------------------------------------
1026:            //  State Machine transitions
1027:            // ----------------------------------------------------------------------
1028:
1029:            /**
1030:             * Handle transition from {@link LOOKING_FOR_FIELD1} to
1031:             * {@link LOOKING_FOR_FIELD2}.
1032:             */
1033:            protected void foundStartLineFirstField() {
1034:                getContext().markFieldLimit();
1035:                getHandler().foundStartLineFirstField(
1036:                        getContext().getAndResetView());
1037:
1038:                getContext().state = LOOKING_FOR_FIELD2;
1039:            }
1040:
1041:            /**
1042:             * Handle transition from {@link LOOKING_FOR_FIELD2} to
1043:             * {@link LOOKING_FOR_FIELD3}.
1044:             */
1045:            protected void foundStartLineSecondField() {
1046:                getContext().markFieldLimit();
1047:                getHandler().foundStartLineSecondField(
1048:                        getContext().getAndResetView());
1049:
1050:                getContext().state = LOOKING_FOR_FIELD3;
1051:            }
1052:
1053:            /**
1054:             * Handle transition from {@link LOOKING_FOR_FIELD3} to
1055:             * {@link LOOKING_FOR_NEW_HEADER}.
1056:             */
1057:            protected void foundStartLineThirdField() {
1058:                getContext().skipLF();
1059:                //getContext().skipCRLF();
1060:
1061:                getContext().markFieldLimit();
1062:                getHandler().foundStartLineThirdField(
1063:                        getContext().getAndResetView());
1064:
1065:                getContext().state = LOOKING_FOR_NEW_HEADER;
1066:            }
1067:
1068:            /**
1069:             * Handle transition from {@link LOOKING_FOR_HEADER_NAME} to
1070:             * {@link SKIPPING_WHITESPACE_AFTER_HEADER_NAME}.
1071:             */
1072:            protected void foundHeaderName() {
1073:                getContext().view.limit(getContext().source.position()
1074:                        - getContext().slice - 1);
1075:                getHandler().foundHeaderName(getContext().getAndUnsetView());
1076:
1077:                getContext().state = SKIPPING_WHITESPACE_AFTER_HEADER_NAME;
1078:            }
1079:
1080:            /**
1081:             * Handle transition from {@link LOOKING_FOR_HEADER_VALUE} and
1082:             * closely related states to {@link LOOKING_FOR_HEADER_NAME} or
1083:             * a {@link LOOKING_FOR_BODY body state}.
1084:             */
1085:            protected void foundHeaderValue() {
1086:                final int newLimit = getContext().source.position()
1087:                        - getContext().slice - 3;
1088:                getContext().view.limit(newLimit);
1089:
1090:                getHandler().foundHeaderValue(getContext().getAndUnsetView());
1091:
1092:                if (getContext().ch == HTTPEncoding.CR) {
1093:                    getContext().skipLF();
1094:                    gotoBodyState();
1095:                } else {
1096:                    getContext().newViewOffByOne();
1097:                    getContext().state = LOOKING_FOR_HEADER_NAME;
1098:                }
1099:            }
1100:
1101:            /**
1102:             * Handle transition from {@link SKIPPING_HEADER_VALUE_WHITESPACE} to
1103:             * {@link LOOKING_FOR_HEADER_VALUE}.
1104:             */
1105:            protected void lookForHeaderValue() {
1106:                getContext().state = LOOKING_FOR_HEADER_VALUE;
1107:            }
1108:
1109:            /**
1110:             * Handle transition from {@link LOOKING_FOR_NORMAL_BODY} to
1111:             * {@link LOOKING_FOR_NORMAL_BODY_UP_TO_SIZE} or
1112:             * {@link DONE}.
1113:             */
1114:            protected void findSizeOfNormalBody() throws HTTPException {
1115:                getContext().bodySize = getHandler().getBodySize();
1116:                if (getContext().bodySize < -1)
1117:                    getContext().bodySize = -1;
1118:
1119:                switch (getContext().bodySize) {
1120:                case 0: // no body, current character is already the next request!
1121:                    getContext().prev();
1122:                    getHandler().endMessage();
1123:                    getContext().state = DONE;
1124:                    break;
1125:                case -1: // that's not allowed
1126:                    // since we're not chunking!
1127:                    // well, actually, this is
1128:                    // "multipart/byteranges",
1129:                    // perhaps
1130:                    // todo: support byteranges
1131:                    final HTTPException ex = new HTTPException(
1132:                            HTTPEncoding.STATUS_411_Length_Required);
1133:                    m_errorHandler.exceptionOccurred(ex);
1134:
1135:                default:
1136:                    //getContext().skipCRLF();
1137:                    //getContext().prev();
1138:                    //getContext().prev();
1139:                    getContext().state = LOOKING_FOR_NORMAL_BODY_UP_TO_SIZE;
1140:                }
1141:            }
1142:
1143:            /**
1144:             * Handle transition from {@link LOOKING_FOR_NORMAL_BODY_UP_TO_SIZE} to
1145:             * {@link DONE}.
1146:             */
1147:            protected void foundNormalBody() {
1148:                getContext().newViewOffByOne();
1149:
1150:                if (getContext().state == DONE)
1151:                    return;
1152:
1153:                // read up to bodySize, or remaining(), whichever is _less_
1154:                int remaining = getContext().view.remaining();
1155:                if (remaining > getContext().bodySize)
1156:                    remaining = getContext().bodySize;
1157:                getContext().view.limit(remaining);
1158:
1159:                if (remaining == getContext().bodySize)
1160:                // already was equal, or we had more
1161:                // left than the message length and we
1162:                // just set it to equal
1163:                // this means we're done!
1164:                {
1165:                    getContext().state = DONE;
1166:                    getHandler().foundBody(getContext().getAndUnsetView());
1167:                    getHandler().endMessage();
1168:
1169:                    // done with body
1170:                    getContext().source.position(getContext().source.position()
1171:                            + remaining - 1);
1172:                } else {
1173:                    // exhausted
1174:                    getContext().bodySize -= remaining;
1175:                    getHandler().foundBody(getContext().getAndUnsetView());
1176:                    getContext().source.position(getContext().source.limit());
1177:                }
1178:            }
1179:
1180:            /**
1181:             * Interact with the {@link m_handler handler} to determine what body type
1182:             * to expect and handle the state transition associated with that body
1183:             * type. Used for transitioning into the body state.
1184:             */
1185:            protected void gotoBodyState() {
1186:                final int bodyType = getHandler().getBodyType();
1187:                Assert.assertTrue(bodyType == BODY_TYPE_NONE
1188:                        || bodyType == BODY_TYPE_CHUNKING
1189:                        || bodyType == BODY_TYPE_NORMAL);
1190:                if (bodyType == BODY_TYPE_NONE) {
1191:                    //getContext().skipCRLF(); // empty line
1192:                    getHandler().endMessage();
1193:                    getContext().state = DONE;
1194:                } else {
1195:                    getContext().state = LOOKING_FOR_BODY + bodyType;
1196:                }
1197:            }
1198:
1199:            protected void foundTrailerName() throws HTTPException {
1200:                getContext().view.limit(getContext().source.position()
1201:                        - getContext().slice - 1);
1202:
1203:                ByteBuffer name = getContext().getAndUnsetView();
1204:
1205:                String nameString = NioUtil.toString(name);
1206:                boolean valid = false;
1207:                for (int i = 0; i < getContext().expectedTrailers.length; i++) {
1208:                    String trailer = getContext().expectedTrailers[i];
1209:                    if (trailer == null)
1210:                        continue;
1211:                    if (trailer.equalsIgnoreCase(nameString)) {
1212:                        getContext().expectedTrailers[i] = null;
1213:                        valid = true;
1214:                    }
1215:                }
1216:                if (!valid)
1217:                    m_errorHandler.exceptionOccurred(new HTTPException(
1218:                            HTTPEncoding.STATUS_400_Bad_Request,
1219:                            "Unexpected trailer '" + nameString + "'!"));
1220:
1221:                getHandler().foundTrailerName(name);
1222:
1223:                getContext().state = SKIPPING_WHITESPACE_AFTER_TRAILER_NAME;
1224:            }
1225:
1226:            protected void foundTrailerValue() {
1227:                final int newLimit = getContext().source.position()
1228:                        - getContext().slice - 3;
1229:                getContext().view.limit(newLimit);
1230:
1231:                getHandler().foundTrailerValue(getContext().getAndUnsetView());
1232:
1233:                boolean trailersFinished = true;
1234:                for (int i = 0; i < getContext().expectedTrailers.length; i++) {
1235:                    String trailer = getContext().expectedTrailers[i];
1236:                    if (trailer != null)
1237:                        trailersFinished = false;
1238:                }
1239:
1240:                if (trailersFinished) {
1241:                    getContext().state = DONE;
1242:                    getHandler().endMessage();
1243:                } else {
1244:                    getContext().newViewOffByOne();
1245:                    getContext().state = LOOKING_FOR_TRAILER_NAME;
1246:                }
1247:
1248:            }
1249:
1250:            private void lookForTrailerValue() {
1251:                getContext().state = LOOKING_FOR_TRAILER_VALUE;
1252:            }
1253:
1254:            // ----------------------------------------------------------------------
1255:            //  Inner Class: Context
1256:            // ----------------------------------------------------------------------
1257:            /**
1258:             * The basic container for all aspects of the parser state.
1259:             */
1260:            protected final class Context {
1261:                // ------------------------------------------------------------------
1262:                //  Properties
1263:                // ------------------------------------------------------------------
1264:                /** The state machine state (used in the big switch/case) */
1265:                public int state;
1266:
1267:                /** The current byte as a character. */
1268:                public char ch;
1269:
1270:                /**
1271:                 * The position at which we sliced the source to
1272:                 * create the view.
1273:                 */
1274:                public int slice;
1275:
1276:                /**
1277:                 * The size of the message body.
1278:                 */
1279:                public int bodySize;
1280:
1281:                /**
1282:                 * The position in the current source buffer up to which
1283:                 * we can read. We use this to allow clients to forget about
1284:                 * setting the limit.
1285:                 */
1286:                public int limit;
1287:
1288:                /**
1289:                 * If set, the next character we're going to retrieve from the
1290:                 * source buffer is expected to be a LF, and it will
1291:                 * be ignored.
1292:                 */
1293:                public boolean expectingToSkipLF;
1294:
1295:                /**
1296:                 * The buffer we're currently reading from. We'll read from
1297:                 * whatever position we've been given up to the buffer its
1298:                 * limit.
1299:                 */
1300:                public ByteBuffer source;
1301:
1302:                /**
1303:                 * The view of the source buffer that we send to the
1304:                 * HTTPHandler. A particular piece of data will start at
1305:                 * 0 and go up to the buffer's limit. The handler should
1306:                 * not read beyond the limit, nor change the limit.
1307:                 */
1308:                public ByteBuffer view;
1309:
1310:                /**
1311:                 * Contains the view buffer from the previous parse() invocation
1312:                 * if there were some bytes left at the end of that invocation.
1313:                 */
1314:                public ByteBuffer leftovers;
1315:
1316:                /**
1317:                 * Contains the size of the next chunk expected for a body that has
1318:                 * a transfer-coding of chunked.
1319:                 */
1320:                public int chunkSize;
1321:
1322:                /**
1323:                 * Contains the 
1324:                 */
1325:                public String[] expectedTrailers;
1326:
1327:                // ------------------------------------------------------------------
1328:                //  Constructor
1329:                // ------------------------------------------------------------------
1330:                /**
1331:                 * Create a new context.
1332:                 * 
1333:                 * @see #recycle() if you don't really need a new instance, but simply
1334:                 *     clearing out the current one will do as well, use
1335:                 *     <code>recycle()</code> instead.
1336:                 */
1337:                protected Context() {
1338:                    recycle();
1339:                }
1340:
1341:                /**
1342:                 * Get the next character from the source.
1343:                 * 
1344:                 * @return the next character.
1345:                 */
1346:                public char next() {
1347:                    ch = (char) source.get();
1348:                    return ch;
1349:                }
1350:
1351:                /** Move the source back one character. */
1352:                public void prev() {
1353:                    Assert.assertTrue(source.position() > 0);
1354:                    source.position(source.position() - 1);
1355:                }
1356:
1357:                /** sets expectingToSkipLF to true. */
1358:                public void skipLF() {
1359:                    Assert.assertFalse(expectingToSkipLF);
1360:                    expectingToSkipLF = true;
1361:                }
1362:
1363:                /** retrieves expectingToSkipLF and sets it to false. */
1364:                public boolean doSkipLF() {
1365:                    final boolean tmp = expectingToSkipLF;
1366:                    expectingToSkipLF = false;
1367:
1368:                    if (tmp)
1369:                        Assert.assertTrue(ch == HTTPEncoding.LF);
1370:                    return tmp;
1371:                }
1372:
1373:                /** Create a new view of the current source buffer. */
1374:                public ByteBuffer newView() {
1375:                    view = source.slice();
1376:                    slice = source.position();
1377:                    int newLimit = limit - slice;
1378:                    if (newLimit < 0)
1379:                        newLimit = 0;
1380:                    view.limit(newLimit);
1381:                    //view = view.asReadOnlyBuffer();
1382:
1383:                    return view;
1384:                }
1385:
1386:                /**
1387:                 * Create a new view of the current source buffer,
1388:                 * but rewinded one position.
1389:                 */
1390:                public ByteBuffer newViewOffByOne() {
1391:                    prev(); // rewind
1392:                    newView();
1393:                    next(); // forward
1394:
1395:                    return view;
1396:                }
1397:
1398:                /**
1399:                 * Appends the current source onto any leftovers
1400:                 * and sets the source to be the union of the two.
1401:                 * The position of the new source will be just
1402:                 * after the leftovers (ie the same position as the
1403:                 * old source).
1404:                 *
1405:                 * The size of the new source will be the sum of
1406:                 * the limits of the leftovers and the
1407:                 *
1408:                 * @return true if we already called newView(),
1409:                 *   false otherwise
1410:                 */
1411:                public boolean mergeSourceWithLeftovers() throws HTTPException {
1412:                    if (leftovers == null)
1413:                        return false;
1414:
1415:                    final int leftoversize = leftovers.limit();
1416:                    //if( leftoversize <= 0 )
1417:                    //{
1418:                    //    newView();
1419:                    //    return true;
1420:                    //}
1421:
1422:                    // make stuff don't get outta hand
1423:                    checkAgainstReallyBigBuffers(leftoversize);
1424:
1425:                    final int sourcesize = source.limit();
1426:                    //if( sourcesize <= 0 )
1427:                    //{
1428:                    //    source = leftovers;
1429:                    //    leftovers = null;
1430:                    //    newView();
1431:                    //    return true;
1432:                    //}
1433:
1434:                    final int mark = leftoversize;
1435:
1436:                    final ByteBuffer union = ByteBuffer.allocate(leftoversize
1437:                            + sourcesize);
1438:                    union.put(leftovers);
1439:                    union.put(source);
1440:                    union.rewind();
1441:                    source = union;
1442:                    limit += mark;
1443:                    leftovers = null;
1444:
1445:                    newView();
1446:
1447:                    source.position(mark);
1448:                    //source.mark();
1449:
1450:                    return true;
1451:                }
1452:
1453:                /**
1454:                 * Retrieves the view buffer, then nulls it.
1455:                 *
1456:                 * @return
1457:                 */
1458:                public ByteBuffer getAndUnsetView() {
1459:                    final ByteBuffer tmp = view;
1460:                    view = null;
1461:                    return tmp;
1462:                }
1463:
1464:                /**
1465:                 * Retries the view buffer, then creates a new
1466:                 * view.
1467:                 *
1468:                 * @return
1469:                 */
1470:                public ByteBuffer getAndResetView() {
1471:                    final ByteBuffer tmp = view;
1472:                    newView();
1473:                    return tmp;
1474:                }
1475:
1476:                public void recycle() {
1477:                    state = NOT_STARTED;
1478:                    ch = Iso646.NULL;
1479:                    slice = -1;
1480:                    bodySize = -1;
1481:                    chunkSize = -1;
1482:
1483:                    limit = Integer.MAX_VALUE;
1484:
1485:                    expectingToSkipLF = false;
1486:                    //expectingToSkipCRLF = false;
1487:
1488:                    source = null;
1489:                    view = null;
1490:                    leftovers = null;
1491:
1492:                    expectedTrailers = null;
1493:                }
1494:
1495:                /**
1496:                 * Guards against really big start lines, headers or trailers by
1497:                 * throwing a {@link HTTPException} if the specified size is bigger
1498:                 * than {@link MAX_BUFFER_SIZE}.
1499:                 * 
1500:                 * @param bufferSize the integer to tests against
1501:                 *     <code>MAX_BUFFER_SIZE</code>.
1502:                 * @throws HTTPException if the tested integer is larger than
1503:                 *     <code>MAX_BUFFER_SIZE</code> and we're not currently reading
1504:                 *     the body of a message.
1505:                 */
1506:                protected void checkAgainstReallyBigBuffers(final int bufferSize)
1507:                        throws HTTPException {
1508:                    if (state < LOOKING_FOR_BODY
1509:                            || state >= LOOKING_FOR_TRAILER) {
1510:                        if (bufferSize > MAX_BUFFER_SIZE) {
1511:                            m_errorHandler
1512:                                    .exceptionOccurred(new HTTPException(
1513:                                            HTTPEncoding.STATUS_413_Request_Entity_Too_Large));
1514:                        }
1515:                    }
1516:                }
1517:
1518:                /**
1519:                 * Sets {@link chunkSize}.
1520:                 */
1521:                public void foundChunkSize() {
1522:                    getContext().markFieldLimit();
1523:                    final ByteBuffer size = getContext().getAndUnsetView();
1524:
1525:                    Assert.assertTrue("size may not be empty", size
1526:                            .hasRemaining());
1527:
1528:                    chunkSize = Integer.parseInt(NioUtil.toString(size), 16);
1529:                }
1530:
1531:                /**
1532:                 * Update the current buffer {@link Context#view view}, setting its
1533:                 * {@link ByteBuffer#limit(int) limit} to the current location. Used
1534:                 * during state transitions.
1535:                 */
1536:                public void markFieldLimit() {
1537:                    getContext().view.limit(getContext().source.position()
1538:                            - getContext().slice - 1);
1539:                }
1540:
1541:            }
1542:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.