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: }
|