Exemplo n.º 1
0
		bool headers_almost_done (int ch, ParserSettings settings) {

			if (LF != ch) {
				return false;
			}

			if (0 != (flags & F_TRAILING)) {
				/* End of a chunked request */

				settings.RaiseOnHeadersComplete(this);
				settings.RaiseOnMessageComplete(this);

				state = new_message(); 

				return true;
			}

			nread = 0;

			if (0 != (flags & F_UPGRADE) || HttpMethod.HTTP_CONNECT == method) upgrade = true;

			/* Here we call the headers_complete callback. This is somewhat
			 * different than other callbacks because if the user returns 1, we
			 * will interpret that as saying that this message has no body. This
			 * is needed for the annoying case of recieving a response to a HEAD
			 * request.
			 */

			/* (responses to HEAD request contain a CONTENT-LENGTH header
			 * but no content)
			 *
			 * Consider what to do here: I don't like the idea of the callback
			 * interface having a different contract in the case of HEAD
			 * responses. The alternatives would be either to:
			 *
			 * a.) require the header_complete callback to implement a different
			 * interface or
			 *
			 * b.) provide an overridden execute(bla, bla, bool
			 * parsingHeader) implementation ...
			 */

			/*TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO */ 
			if (null != settings.OnHeadersComplete) {
				settings.RaiseOnHeadersComplete(this);
				//return;
			}
    
			//        if (null != settings.on_headers_complete) {
			//          switch (settings.on_headers_complete.cb(parser)) {
			//            case 0:
			//              break;
			//
			//            case 1:
			//              flags |= F_SKIPBODY;
			//              break;
			//
			//            default:
			//              return p - data; /* Error */ // TODO // RuntimeException ?
			//          }
			//        }


			// Exit, the rest of the connect is in a different protocol.
			if (upgrade) {
				settings.RaiseOnMessageComplete(this);
				return true;
			}

			if (0 != (flags & F_SKIPBODY)) {
				settings.RaiseOnMessageComplete(this);
				state = new_message(); 
			} else if (0 != (flags & F_CHUNKED)) {
				/* chunked encoding - ignore Content-Length header */
				state = State.chunk_size_start;
			} else {
				if (content_length == 0) {
				/* Content-Length header given but zero: Content-Length: 0\r\n */
					settings.RaiseOnMessageComplete(this);
					state = new_message(); 
				} else if (content_length > 0) {
				/* Content-Length header given and non-zero */
					state = State.body_identity;
				} else {
					if (type == ParserType.HTTP_REQUEST || http_should_keep_alive()) {
						/* Assume content-length 0 - read the next */
						settings.RaiseOnMessageComplete(this);
						state = new_message(); 
					} else {
						/* Read body until EOF */
						state = State.body_identity_eof;
					}
				}
			}
			return true;
		} // headers_almost_fone
Exemplo n.º 2
0
		/** Execute the parser with the currently available data contained in
		 * the buffer. The buffers position() and limit() need to be set
		 * correctly (obviously) and a will be updated approriately when the
		 * method returns to reflect the consumed data.
		 */
		public void Execute (ParserSettings settings, MemoryStream data)
		{
			int p     = (int) data.Position;
			int p_err = p; // this is used for pretty printing errors.

			// In case the headers don't provide information about the content
			// length, `execute` needs to be called with an empty buffer to
			// indicate that all the data has been send be the client/server,
			// else there is no way of knowing the message is complete. 
			int len = (int) (data.Length - data.Position);
			if (0 == len) {
				if (State.body_identity_eof == state)
					settings.RaiseOnMessageComplete(this);
			}

		
			// in case the _previous_ call to the parser only has data to get to
			// the middle of certain fields, we need to update marks to point at
			// the beginning of the current buffer.
			switch (state) {
			case State.header_field:
				header_field_mark = p;
				break;
			case State.header_value:
				header_value_mark = p;
				break;
			case State.req_fragment:
				fragment_mark = p;
				url_mark = p;
				break;
			case State.req_query_string:
				query_string_mark = p;
				url_mark = p;
				break;
			case State.req_path:
				path_mark = p;
				// JACKSON ADDED, I assume java can fall through?
				url_mark = p;
				break;
			case State.req_host:
			case State.req_schema:
			case State.req_schema_slash:
			case State.req_schema_slash_slash:
			case State.req_port:
			case State.req_query_string_start:
			case State.req_fragment_start:
				url_mark = p;
				break;
			}

			// this is where the work gets done, traverse the available data...
			while (data.Position != data.Length) {

				p = (int) data.Position;
				int  pe = (int) data.Length;
      
				int ch      = (byte) data.ReadByte ();  // the current character to process.
				int c       = -1;          // utility variably used for up- and downcasing etc.
				int to_read =  0;          // used to keep track of how much of body, etc. is left to read

				if (parsing_header (state)) {
					++nread;
					if (nread > HTTP_MAX_HEADER_SIZE) {
						settings.RaiseOnError (this, "possible buffer overflow", data, p_err);
					}
				}

				switch (state) {
					/*
					 * this state is used after a 'Connection: close' message
					 * the parser will error out if it reads another message
					 */
				case State.dead:
					settings.RaiseOnError (this, "Connection already closed", data, p_err);
					// JACKSON: Added this break
					break;

				case State.start_res_or_res:
					if (CR == ch || LF == ch){
						break;
					}
					flags = 0;
					content_length = -1;

					settings.RaiseOnMessageBegin (this);
          
					if (H == ch) 
						state = State.res_or_resp_H;
					else {
						type   = ParserType.HTTP_REQUEST;  
						method = start_req_method_assign (ch);
						if (HttpMethod.ERROR == method)
							settings.RaiseOnError (this, "invalid method", data, p_err);
						index  = 1;
						state  = State.req_method;
					}
					break;

				case State.res_or_resp_H:
					if (T == ch) {
						type  = ParserType.HTTP_RESPONSE;
						state = State.res_HT;
					} else {
						if (E != ch)
							settings.RaiseOnError (this, "not E", data, p_err);

						type   = ParserType.HTTP_REQUEST;
						method = HttpMethod.HTTP_HEAD;
						index  = 2;
						state  = State.req_method;
					}
					break;

				case State.start_res:
					flags = 0;
					content_length = -1;

					settings.RaiseOnMessageBegin (this);
					
					switch (ch) {
					case H:
						state = State.res_H;
						break;
					case CR:
					case LF:
						break;
					default:
						settings.RaiseOnError (this, "Not H or CR/LF", data, p_err);
						break;
					}
					break;


				case State.res_H:
					if (strict && T != ch)
						settings.RaiseOnError (this, "Not T", data, p_err);
					state = State.res_HT;
					break;
				case State.res_HT:
					if (strict && T != ch)
						settings.RaiseOnError (this, "Not T2", data, p_err);
					state = State.res_HTT;
					break;
				case State.res_HTT:
					if (strict && P != ch)
						settings.RaiseOnError (this, "Not P", data, p_err);
					state = State.res_HTTP;
					break;
				case State.res_HTTP:
					if (strict && SLASH != ch)
						settings.RaiseOnError (this, "Not '/'", data, p_err);
					state = State.res_first_http_major;
					break;

					
					
				case State.res_first_http_major:
					if (!isDigit (ch))
						settings.RaiseOnError (this, "Not a digit", data, p_err);
					http_major = (int) ch - 0x30;
					state = State.res_http_major;
					break;

					/* major HTTP version or dot */
				case State.res_http_major:
					if (DOT == ch) {
						state = State.res_http_minor;
						break;
					}
					if (!isDigit (ch))
						settings.RaiseOnError(this, "Not a digit", data, p_err);
					http_major *= 10;
					http_major += (ch - 0x30);

					if (http_major > 999)
						settings.RaiseOnError(this, "invalid http major version: " + http_major, data, p_err);
					break;
          
					/* first digit of minor HTTP version */
				case State.res_first_http_minor:
					if (!isDigit (ch))
						settings.RaiseOnError (this, "Not a digit", data, p_err);
					http_minor = (int)ch - 0x30;
					state = State.res_http_minor;
					break;

					/* minor HTTP version or end of request line */
				case State.res_http_minor:
					if (SPACE == ch) {
						state = State.res_first_status_code;
						break;
					}
					if (!isDigit (ch))
						settings.RaiseOnError(this, "Not a digit", data, p_err);
					http_minor *= 10;
					http_minor += (ch - 0x30);

					if (http_minor > 999)
						settings.RaiseOnError(this, "invalid http minor version: " + http_minor, data, p_err);
					break;



				case State.res_first_status_code:
					if (!isDigit (ch)) {
						if (SPACE == ch)
							break;
						settings.RaiseOnError (this, "Not a digit (status code)", data, p_err);
					}
					status_code = (int)ch - 0x30;
					state = State.res_status_code;
					break;

				case State.res_status_code:
					if (!isDigit (ch)) {
						switch (ch) {
						case SPACE:
							state = State.res_status;
							break;
						case CR:
							state = State.res_line_almost_done;
							break;
						case LF:
							state = State.header_field_start;
							break;
						default:
							settings.RaiseOnError(this, "not a valid status code", data, p_err);
							break;
						}
						break;
					}
					status_code *= 10;
					status_code += (int)ch - 0x30;
					if (status_code > 999)
						settings.RaiseOnError(this, "ridiculous status code:"+status_code, data, p_err);
					break;

				case State.res_status:
					/* the human readable status. e.g. "NOT FOUND"
					 * we are not humans so just ignore this
					 * we are not men, we are devo. */

					if (CR == ch) {
						state = State.res_line_almost_done;
						break;
					}
					if (LF == ch) { 
						state = State.header_field_start;
						break;
					}
					break;

				case State.res_line_almost_done:
					if (strict && LF != ch)
						settings.RaiseOnError (this, "not LF", data, p_err);
					state = State.header_field_start;
					break;



				case State.start_req:
					if (CR==ch || LF == LF)
						break;
					flags = 0;
					content_length = -1;
					settings.RaiseOnMessageBegin (this);
					method = start_req_method_assign (ch);
					if (HttpMethod.ERROR == method)
						settings.RaiseOnError (this, "invalid method", data, p_err);
					
					index  = 1;
					state  = State.req_method;
					break;
				


				case State.req_method:
					if (0 == ch)
						settings.RaiseOnError( this, "NULL in method", data, p_err);
          
					byte [] arr = HttpMethodBytes.GetBytes (method);

					if (SPACE == ch && index == arr.Length)
						state = State.req_spaces_before_url;
					else if (arr[index] == ch) {
						// wuhu!	
					} else if (HttpMethod.HTTP_CONNECT == method) {
						if (1 == index && H == ch) {
							method = HttpMethod.HTTP_CHECKOUT;
						} else if (2 == index && P == ch) {
							method = HttpMethod.HTTP_COPY;
						}
					} else if (HttpMethod.HTTP_MKCOL == method) {
						if        (1 == index && O == ch) {
							method = HttpMethod.HTTP_MOVE;
						} else if (1 == index && E == ch) {
							method = HttpMethod.HTTP_MERGE;
						} else if (2 == index && A == ch) {
							method = HttpMethod.HTTP_MKACTIVITY;
						}
					} else if (1 == index && HttpMethod.HTTP_POST == method && R == ch) {
						method = HttpMethod.HTTP_PROPFIND;
					} else if (1 == index && HttpMethod.HTTP_POST == method && U == ch) {
						method = HttpMethod.HTTP_PUT;
					} else if (4 == index && HttpMethod.HTTP_PROPFIND == method && P == ch) {
						method = HttpMethod.HTTP_PROPPATCH;
					} else {
						settings.RaiseOnError (this, "Invalid HTTP method", data, p_err);
					}

					++index;
					break;
      


				/******************* URL *******************/
				case State.req_spaces_before_url:
					if (SPACE == ch)
						break;
					if (SLASH == ch) {
						url_mark  = p;
						path_mark = p;
						state = State.req_path;
						break;
					}
					if (isAtoZ (ch)) {
						url_mark = p;
						state = State.req_schema;
						break;
					}
					settings.RaiseOnError (this, "Invalid something", data, p_err);
					break;

				case State.req_schema:
					if (isAtoZ (ch))
						break;
					if (COLON == ch) {
						state = State.req_schema_slash;
						break;
					} else if (DOT == ch) {
						state = State.req_host;
						break;
					}
					settings.RaiseOnError (this, "invalid char in schema: "+ch, data, p_err);
					break;

				case State.req_schema_slash:
					if (strict && SLASH != ch)
						settings.RaiseOnError (this, "invalid char in schema, not /", data, p_err);
					state = State.req_schema_slash_slash;
					break;

				case State.req_schema_slash_slash:
					if (strict && SLASH != ch)
						settings.RaiseOnError(this, "invalid char in schema, not /", data, p_err);
					state = State.req_host;
					break;
        
				case State.req_host:
					if (isAtoZ (ch))
						break;
					if (isDigit (ch) || DOT == ch || DASH == ch)
						break;
					switch (ch) {
					case COLON:
						state = State.req_port;
						break;
					case SLASH:
						path_mark = p;
						break;
					case SPACE:
						/* The request line looks like:
						 *   "GET http://foo.bar.com HTTP/1.1"	
						 * That is, there is no path.	
						 */
						settings.RaiseOnUrl (this, data, url_mark, p-url_mark);
						url_mark = -1;
						state = State.req_http_start;
						break;
					default:
						settings.RaiseOnError(this, "host error in method line", data, p_err);
						break;
					}
					break;

				case State.req_port:
					if (isDigit (ch))
						break;
					switch (ch) {
					case SLASH:
						path_mark = p; 
						state = State.req_path;
						break;
					case SPACE:
						/* The request line looks like:
						 *   "GET http://foo.bar.com:1234 HTTP/1.1"
						 * That is, there is no path.
						 */
						settings.RaiseOnUrl (this,data,url_mark,p-url_mark);
						url_mark = -1;
						state = State.req_http_start;
						break;
					default:
						settings.RaiseOnError (this, "invalid port", data, p_err);
						break;
					}
					break;
      
				case State.req_path:
					if (usual (ch))
						break;
					switch (ch) {
					case SPACE:
						settings.RaiseOnUrl (this,data,url_mark, p-url_mark);
						url_mark = -1;

						settings.RaiseOnPath(this,data,path_mark, p-path_mark);
						path_mark = -1;

						state = State.req_http_start;
						break;

					case CR:
						settings.RaiseOnUrl(this,data,url_mark, p-url_mark);
						url_mark = -1;
              
						settings.RaiseOnPath(this,data,path_mark, p-path_mark);
						path_mark = -1;
              
						http_minor = 9;
						state = State.res_line_almost_done;
						break;

					case LF:
						settings.RaiseOnUrl(this,data,url_mark, p-url_mark);
						url_mark = -1;
					
						settings.RaiseOnPath(this,data,path_mark, p-path_mark);
						path_mark = -1;
              
						http_minor = 9;
						state = State.header_field_start;
						break;

					case QMARK:
						settings.RaiseOnPath(this,data,path_mark, p-path_mark);
						path_mark = -1;
              
						state = State.req_query_string_start;
						break;
            
					case HASH:
						settings.RaiseOnPath(this,data,path_mark, p-path_mark);
						path_mark = -1;
              
						state = State.req_fragment_start;
						break;
            
					default:
						settings.RaiseOnError(this, "unexpected char in path", data, p_err);
						break;
					}
					break;
      
				case State.req_query_string_start:
					if (usual(ch)) {
						query_string_mark = p;
						state = State.req_query_string;
						break;
					}

					switch (ch) {
					case QMARK: break;
					case SPACE: 
						settings.RaiseOnUrl(this, data, url_mark, p-url_mark);
						url_mark = -1;
						state = State.req_http_start;
						break;
					case CR:
						settings.RaiseOnUrl(this,data,url_mark, p-url_mark);
						url_mark = -1; 
						http_minor = 9;
						state = State.res_line_almost_done;
						break;
					case LF:
						settings.RaiseOnUrl(this,data,url_mark, p-url_mark);
						url_mark = -1;
						http_minor = 9;
						state = State.header_field_start;
						break;
					case HASH:
						state = State.req_fragment_start;
						break;
					default:
						settings.RaiseOnError(this, "unexpected char in path", data, p_err);
						break;
					}
					break;
        
				case State.req_query_string:
					if (usual(ch)) {
						break;
					}

					switch (ch) {
					case QMARK: break; // allow extra '?' in query string
					case SPACE: 
						settings.RaiseOnUrl(this, data, url_mark, p-url_mark);
						url_mark = -1;

						settings.RaiseOnQueryString(this, data, query_string_mark, p-query_string_mark);
						query_string_mark = -1;

						state = State.req_http_start;
						break;
					case CR:
						settings.RaiseOnUrl(this,data,url_mark, p-url_mark);
						url_mark = -1; 

						settings.RaiseOnQueryString(this, data, query_string_mark, p-query_string_mark);
						query_string_mark = -1;
              
						http_minor = 9;
						state = State.res_line_almost_done;
						break;
					case LF:
						settings.RaiseOnUrl(this,data,url_mark, p-url_mark);
						url_mark = -1;

						settings.RaiseOnQueryString(this, data, query_string_mark, p-query_string_mark);
						query_string_mark = -1;
						http_minor = 9;

						state = State.header_field_start;
						break;
					case HASH:
						settings.RaiseOnQueryString(this, data, query_string_mark, p-query_string_mark);
						query_string_mark = -1;
              
						state = State.req_fragment_start;
						break;
					default:
						settings.RaiseOnError(this, "unexpected char in path", data, p_err);
						break;
					}
					break;

				case State.req_fragment_start:
					if (usual(ch)) {
						fragment_mark = p;
						state = State.req_fragment;
						break;
					}

					switch (ch) {
					case SPACE: 
						settings.RaiseOnUrl(this, data, url_mark, p-url_mark);
						url_mark = -1;
     
						state = State.req_http_start;
						break;
					case CR:
						settings.RaiseOnUrl(this,data,url_mark, p-url_mark);
						url_mark = -1; 

						http_minor = 9;
						state = State.res_line_almost_done;
						break;
					case LF:
						settings.RaiseOnUrl(this,data,url_mark, p-url_mark);
						url_mark = -1;
              
						http_minor = 9;
						state = State.header_field_start;
						break;
					case QMARK:
						fragment_mark = p;
						state = State.req_fragment;
						break;
					case HASH:
						break;
					default:
						settings.RaiseOnError(this, "unexpected char in path", data, p_err);
						break;
					}
					break;

				case State.req_fragment:
					if (usual(ch)) {
						break;
					}

					switch (ch) {
					case SPACE: 
						settings.RaiseOnUrl(this, data, url_mark, p-url_mark);
						url_mark = -1;
          
						settings.RaiseOnFragment(this, data, fragment_mark, p-fragment_mark);
						fragment_mark = -1;
              
						state = State.req_http_start;
						break;
					case CR:
						settings.RaiseOnUrl(this,data,url_mark, p-url_mark);
						url_mark = -1; 
              
						settings.RaiseOnFragment(this, data, query_string_mark, p-query_string_mark);
						fragment_mark = -1;
              
						http_minor = 9;
						state = State.res_line_almost_done;
						break;
					case LF:
						settings.RaiseOnUrl(this,data,url_mark, p-url_mark);
						url_mark = -1;
              
						settings.RaiseOnFragment(this, data, query_string_mark, p-query_string_mark);
						fragment_mark = -1;
              
						http_minor = 9;
						state = State.header_field_start;
						break;
					case QMARK:
					case HASH:
						break;
					default:
						settings.RaiseOnError(this, "unexpected char in path", data, p_err);
						break;
					}
					break;
				/******************* URL *******************/



				/******************* HTTP 1.1 *******************/
				case State.req_http_start:
					switch (ch) {
					case H:
						state = State.req_http_H;
						break;
					case SPACE:
						break;
					default:
						settings.RaiseOnError(this, "error in req_http_H", data, p_err);
						break;
					}
					break;

				case State.req_http_H:
					if (strict && T != ch)
						settings.RaiseOnError(this, "unexpected char", data, p_err);
					state = State.req_http_HT;
					break;

				case State.req_http_HT:
					if (strict && T != ch)
						settings.RaiseOnError(this, "unexpected char", data, p_err);
					state = State.req_http_HTT;
					break;

				case State.req_http_HTT:
					if (strict && P != ch)
						settings.RaiseOnError(this, "unexpected char", data, p_err);
					state = State.req_http_HTTP;
					break;

				case State.req_http_HTTP:
					if (strict && SLASH != ch)
						settings.RaiseOnError(this, "unexpected char", data, p_err);
					state = State.req_first_http_major;
					break;

				/* first digit of major HTTP version */
				case State.req_first_http_major:
					if (!isDigit(ch))
						settings.RaiseOnError(this, "non digit in http major", data, p_err);
					http_major = (int)ch - 0x30;
					state = State.req_http_major;
					break;

				/* major HTTP version or dot */
				case State.req_http_major:
					if (DOT == ch) {
						state = State.req_first_http_minor;
						break;
					}

					if (!isDigit(ch))
						settings.RaiseOnError(this, "non digit in http major", data, p_err);

					http_major *= 10;
					http_major += (int)ch - 0x30;

					if (http_major > 999)
						settings.RaiseOnError(this, "ridiculous http major", data, p_err);
					break;
        
				/* first digit of minor HTTP version */
				case State.req_first_http_minor:
					if (!isDigit(ch))
						settings.RaiseOnError(this, "non digit in http minor", data, p_err);
					http_minor = (int)ch - 0x30;
					state = State.req_http_minor;
					break;

				case State.req_http_minor:
					if (ch == CR) {
						state = State.req_line_almost_done;
						break;
					}

					if (ch == LF) {
						state = State.header_field_start;
						break;
					}

				/* XXX allow spaces after digit? */

					if (!isDigit(ch))
						settings.RaiseOnError(this, "non digit in http minor", data, p_err);

					http_minor *= 10;
					http_minor += (int)ch - 0x30;

         
					if (http_minor > 999)
						settings.RaiseOnError(this, "ridiculous http minor", data, p_err);
   
					break;

				/* end of request line */
				case State.req_line_almost_done:
				{
					if (ch != LF)
						settings.RaiseOnError(this, "missing LF after request line", data, p_err);
					state = State.header_field_start;
					break;
				}

				/******************* HTTP 1.1 *******************/



			/******************* Header *******************/
				case State.header_field_start:
				{
					if (ch == CR) {
						state = State.headers_almost_done;
						break;
					}

					if (ch == LF) {
						/* they might be just sending \n instead of \r\n so this would be
						 * the second \n to denote the end of headers*/
						state = State.headers_almost_done;
						if (!headers_almost_done(ch, settings))
							settings.RaiseOnError(this, "header not properly completed", data, p_err);
						break;
					}

					c = upper(ch);

					if (c < A || Z < c) {
						settings.RaiseOnError(this, "invalid char in header", data, p_err);
					};

					header_field_mark = p;

					index = 0;
					state = State.header_field;

					switch (c) {
					case C: 
						header_state = HState.C;
						break;

					case P:
						header_state = HState.matching_proxy_connection;
						break;

					case T:
						header_state = HState.matching_transfer_encoding;
						break;

					case U:
						header_state = HState.matching_upgrade;
						break;

					default:
						header_state = HState.general;
						break;
					}
					break;
				}



				case State.header_field:
				{
					c = UPCASE[ch];

					if (0 != c) {  
						switch (header_state) {
						case HState.general:
							break;

						case HState.C:
							index++;
							header_state = (O == c ? HState.CO : HState.general);
							break;

						case HState.CO:
							index++;
							header_state = (N == c ? HState.CON : HState.general);
							break;

						case HState.CON:
							index++;
							switch (c) {
							case N:
								header_state = HState.matching_connection;
								break;
							case T:
								header_state = HState.matching_content_length;
								break;
							default:
								header_state = HState.general;
								break;
							}
							break;

							/* connection */

						case HState.matching_connection:
							index++;
							if (index > CONNECTION.Length || c != CONNECTION[index]) {
								header_state = HState.general;
							} else if (index == CONNECTION.Length-1) {
								header_state = HState.connection;
							}
							break;

							/* proxy-connection */

						case HState.matching_proxy_connection:
							index++;
							if (index > PROXY_CONNECTION.Length || c != PROXY_CONNECTION[index]) {
								header_state = HState.general;
							} else if (index == PROXY_CONNECTION.Length-1) {
								header_state = HState.connection;
							}
							break;

							/* content-length */

						case HState.matching_content_length:
							index++;
							if (index > CONTENT_LENGTH.Length || c != CONTENT_LENGTH[index]) {
								header_state = HState.general;
							} else if (index == CONTENT_LENGTH.Length-1) {
								header_state = HState.content_length;
							}
							break;

							/* transfer-encoding */

						case HState.matching_transfer_encoding:
							index++;
							if (index > TRANSFER_ENCODING.Length || c != TRANSFER_ENCODING[index]) {
								header_state = HState.general;
							} else if (index == TRANSFER_ENCODING.Length-1) {
								header_state = HState.transfer_encoding;
							}
							break;

							/* upgrade */

						case HState.matching_upgrade:
							index++;
							if (index > UPGRADE.Length || c != UPGRADE[index]) {
								header_state = HState.general;
							} else if (index == UPGRADE.Length-1) {
								header_state = HState.upgrade;
							}
							break;

						case HState.connection:
						case HState.content_length:
						case HState.transfer_encoding:
						case HState. upgrade:
							if (SPACE != ch) header_state = HState.general;
							break;

						default:
							settings.RaiseOnError(this, "Unknown Header State", data, p_err);
							break;
						} // switch: header_state
						break;
					} // 0 != c

					if (COLON == ch)  {
						settings.RaiseOnHeaderField(this, data, header_field_mark, p-header_field_mark);
						header_field_mark = -1;

						state = State.header_value_start;
						break;
					}

					if (CR == ch) {
						state = State.header_almost_done;
						settings.RaiseOnHeaderField(this, data, header_field_mark, p-header_field_mark);
            
						header_field_mark = -1;
						break;
					}

					if (ch == LF) {
						settings.RaiseOnHeaderField(this, data, header_field_mark, p-header_field_mark);
						header_field_mark = -1;
            
						state = State.header_field_start;
						break;
					}

					settings.RaiseOnError(this, "invalid header field", data, p_err);
					break;
				}



				case State.header_value_start:
				{
					if (SPACE == ch) break;

					header_value_mark = p;

					state = State.header_value;
					index = 0;

					c = UPCASE[ch];

					if (c == 0) {
						if (CR == ch) {
							settings.RaiseOnHeaderValue(this, data, header_value_mark, p-header_value_mark);
							header_value_mark = -1;

							header_state = HState.general;
							state = State.header_almost_done;
							break;
						}

						if (LF == ch) {
							settings.RaiseOnHeaderValue(this, data, header_value_mark, p-header_value_mark);
							header_value_mark = -1;
              
							state = State.header_field_start;
							break;
						}

						header_state = HState.general;
						break;
					}

					switch (header_state) {
					case HState.upgrade:
						flags |= F_UPGRADE;
						header_state = HState.general;
						break;

					case HState.transfer_encoding:
						/* looking for 'Transfer-Encoding: chunked' */
						if (C == c) {
							header_state = HState.matching_transfer_encoding_chunked;
						} else {
							header_state = HState.general;
						}
						break;

					case HState.content_length:
						if (!isDigit(ch)) {
							settings.RaiseOnError(this, "Content-Length not numeric", data, p_err);
						} 
						content_length = (int)ch - 0x30;
						break;

					case HState.connection:
						/* looking for 'Connection: keep-alive' */
						if (K == c) {
							header_state = HState.matching_connection_keep_alive;
							/* looking for 'Connection: close' */
						} else if (C == c) {
							header_state = HState.matching_connection_close;
						} else {
							header_state = HState.general;
						}
						break;

					default:
						header_state = HState.general;
						break;
					}
					break;
				} // header value start



				case State.header_value:
				{
					c = UPCASE[ch];
					if (c == 0) {
						if (CR == ch) {
							settings.RaiseOnHeaderValue(this, data, header_value_mark, p-header_value_mark);
							header_value_mark = -1;

							state = State.header_almost_done;
							break;
						}

						if (LF == ch) {
							settings.RaiseOnHeaderValue(this, data, header_value_mark, p-header_value_mark);
							header_value_mark = -1;
              
							if (!header_almost_done(ch)) {
								settings.RaiseOnError(this,"incorrect header ending, expection LF", data, p_err);
							}
							break;
						}
						break;
					}

					switch (header_state) {
					case HState.general:
						break;

					case HState.connection:
					case HState.transfer_encoding:
						settings.RaiseOnError(this, "Shouldn't be here", data, p_err);
						break;

					case HState.content_length:
						if (!isDigit(ch))
							settings.RaiseOnError(this, "Content-Length not numeric", data, p_err);

						content_length *= 10;
						content_length += (int)ch - 0x30;
						break;

						/* Transfer-Encoding: chunked */
					case HState.matching_transfer_encoding_chunked:
						index++;
						if (index > CHUNKED.Length || c != CHUNKED[index]) {
							header_state = HState.general;
						} else if (index == CHUNKED.Length-1) {
							header_state = HState.transfer_encoding_chunked;
						}
						break;

						/* looking for 'Connection: keep-alive' */
					case HState.matching_connection_keep_alive:
						index++;
						if (index > KEEP_ALIVE.Length || c != KEEP_ALIVE[index]) {
							header_state = HState.general;
						} else if (index == KEEP_ALIVE.Length-1) {
							header_state = HState.connection_keep_alive;
						}
						break;

						/* looking for 'Connection: close' */
					case HState.matching_connection_close:
						index++;
						if (index > CLOSE.Length || c != CLOSE[index]) {
							header_state = HState.general;
						} else if (index == CLOSE.Length-1) {
							header_state = HState.connection_close;
						}
						break;

					case HState.transfer_encoding_chunked:
					case HState.connection_keep_alive:
					case HState.connection_close:
						if (SPACE != ch) header_state = HState.general;
						break;

					default:
						state = State.header_value;
						header_state = HState.general;
						break;
					}
					break;
				} // header_value



				case State.header_almost_done:
					if (!header_almost_done(ch))
						settings.RaiseOnError(this,"incorrect header ending, expection LF", data, p_err);
					break;

				case State.headers_almost_done:
					if (!headers_almost_done(ch, settings))
						settings.RaiseOnError(this, "header not properly completed", data, p_err);
					break;

				/******************* Header *******************/




				/******************* Body *******************/
				case State.body_identity:
					to_read = min(pe - p, content_length); //TODO change to use buffer? 

					if (to_read > 0) {
						settings.RaiseOnBody(this, data, p, to_read); 
						data.Position = p+to_read;
						content_length -= to_read;
						if (content_length == 0) {
							settings.RaiseOnMessageComplete(this);
							state = new_message(); 
						}
					}
					break;



				case State.body_identity_eof:
					to_read = pe - p;  // TODO change to use buffer ?
					if (to_read > 0) {
						settings.RaiseOnBody(this, data, p, to_read); 
						data.Position = p+to_read;
					}
					break;
				/******************* Body *******************/



				/******************* Chunk *******************/
				case State.chunk_size_start:
					if (0 == (flags & F_CHUNKED))
						settings.RaiseOnError(this, "not chunked", data, p_err);

					c = UNHEX[ch];
					if (c == -1)
						settings.RaiseOnError(this, "invalid hex char in chunk content length", data, p_err);
					content_length = c;
					state = State.chunk_size;
					break;



				case State.chunk_size:
					if (0 == (flags & F_CHUNKED))
						settings.RaiseOnError(this, "not chunked", data, p_err);

					if (CR == ch) {
						state = State.chunk_size_almost_done;
						break;
					}

					c = UNHEX[ch];

					if (c == -1) {
						if (SEMI == ch || SPACE == ch) {
							state = State.chunk_parameters;
							break;
						}
						settings.RaiseOnError(this, "invalid hex char in chunk content length", data, p_err);
					}

					content_length *= 16;
					content_length += c;
					break;



				case State.chunk_parameters:
					if (0 == (flags & F_CHUNKED))
						settings.RaiseOnError(this, "not chunked", data, p_err);

				/* just ignore this shit. TODO check for overflow */
					if (CR == ch) {
						state = State.chunk_size_almost_done;
						break;
					}
					break;
          


				case State.chunk_size_almost_done:
					if (0 == (flags & F_CHUNKED)) {
						settings.RaiseOnError(this, "not chunked", data, p_err);
					}
					if (strict && LF != ch) {
						settings.RaiseOnError(this, "expected LF at end of chunk size", data, p_err);
					}

					if (0 == content_length) {
						flags |= F_TRAILING;
						state = State.header_field_start;
					} else {
						state = State.chunk_data;
					}
					break;



				case State.chunk_data:
				{
					if (0 == (flags & F_CHUNKED)) {
						settings.RaiseOnError(this, "not chunked", data, p_err);
					}

					to_read = min(pe-p, content_length);
					if (to_read > 0) {
						settings.RaiseOnBody(this, data, p, to_read);
						data.Position = p+to_read;
					}

					if (to_read == content_length) {
						state = State.chunk_data_almost_done;
					}

					content_length -= to_read;
					break;
				}



				case State.chunk_data_almost_done:
					if (0 == (flags & F_CHUNKED)) {
						settings.RaiseOnError(this, "not chunked", data, p_err);
					}
					if (strict && CR != ch) {
						settings.RaiseOnError(this, "chunk data terminated incorrectly, expected CR", data, p_err);
					}
					state = State.chunk_data_done;
					break;



				case State.chunk_data_done:
					if (0 == (flags & F_CHUNKED)) {
						settings.RaiseOnError(this, "not chunked", data, p_err);
					}
					if (strict && LF != ch) {
						settings.RaiseOnError(this, "chunk data terminated incorrectly, expected LF", data, p_err);
					}
					state = State.chunk_size_start;
					break;
				/******************* Chunk *******************/
    
        
        
				default:
					settings.RaiseOnError(this, "unhandled state", data, p_err);
					break;
          
				} // switch
			} // while

			p = (int) data.Position;


		/* Reaching this point assumes that we only received part of a
		 * message, inform the callbacks about the progress made so far*/
    
			settings.RaiseOnHeaderField (this, data, header_field_mark, p-header_field_mark);
			settings.RaiseOnHeaderValue (this, data, header_value_mark, p-header_value_mark);
			settings.RaiseOnFragment    (this, data, fragment_mark,     p-fragment_mark);
			settings.RaiseOnQueryString (this, data, query_string_mark, p-query_string_mark);
			settings.RaiseOnPath        (this, data, path_mark,         p-path_mark);
			settings.RaiseOnUrl         (this, data, url_mark,          p-url_mark);
	
		} // execute