Exemplo n.º 1
0
// takes data and tries to figure out what it is.  it will loop through 
//   irsData as many times as it can as long as it gets a full line each time.
//   Depending on how much data it's gotten already, it will handle a line 
//   differently.. 
HttpRequest::HttpParseStates HttpRequest::ParseData ( std::string & irsData ///< buffer to look in for more data
	)
{


	std::string sLine;

	int nDoneWithCurrentData = 0;

	PME oHeaderRegex ( "^([^:]*):\\s+(.*)\\r\\n$" );

	while ( ! nDoneWithCurrentData && 
			nCurrentHttpParseState != HTTPPARSESTATE_INVALIDREQUEST &&
			nCurrentHttpParseState != HTTPPARSESTATE_COMPLETEREQUEST &&
			nCurrentHttpParseState != HTTPPARSESTATE_INVALID ) {

		switch ( nCurrentHttpParseState ) {
			
		case HTTPPARSESTATE_REQUEST:
			
			// get the request line
			GetNextLine ( sLine, irsData );
			
			// if we got a line, parse out the data..
			if ( sLine.length ( ) == 0 ) {

				nDoneWithCurrentData = 1;

			} 
			// if we got a line, look for a request line
			else {
				
				// everything must be uppercase according to rfc2616
				PME oRequestLineRegex ( "^(OPTIONS|GET|HEAD|POST|PUT|DELETE|TRACE|CONNECT) ([^ ]*) HTTP/([^ ]+)\\r\\n$" );

				if ( oRequestLineRegex.match ( sLine ) ) {

					// get the info from the request line
					nRequestMethod = GetRequestMethodFromString ( oRequestLineRegex [ 1 ] );
					sUri = oRequestLineRegex [ 2 ];
					sOriginalUri = oRequestLineRegex [ 2 ];
					sHttpVersionNumber = oRequestLineRegex [ 3 ];
					
					
					// check to see if the uri appeared to have form data in it
					GetFormDataFromString ( sUri );

					// on to the headers
					nCurrentHttpParseState = HTTPPARSESTATE_HEADERS;
					
				} 
				// if the regex failed
				else {

					nCurrentHttpParseState = HTTPPARSESTATE_INVALIDREQUEST;

				} // end match on request line
			
			} // end whether we got a line
			
			break;
			
		case HTTPPARSESTATE_HEADERS:
			
			// get the next line
			GetNextLine ( sLine, irsData );

			// if we didn't get a full line of data
			if ( sLine.length ( ) == 0 ) {

				nDoneWithCurrentData = 1;

			}
			// check to see if we're done with headers
			else if ( sLine == "\r\n" ) {

				// if content length is found
				if ( oRequestHeaders.find ( "content-length" ) !=
					 oRequestHeaders.end ( ) ) {
					
					nCurrentHttpParseState = HTTPPARSESTATE_BODY;
				
				} else {

					
					nCurrentHttpParseState = HTTPPARSESTATE_COMPLETEREQUEST;

				}

				// if this is an HTTP/1.1 request, then it had better have a Host: header
				if ( sHttpVersionNumber == "1.1" && 
					 oRequestHeaders [ "host" ].length ( ) == 0 ) {

					nCurrentHttpParseState = HTTPPARSESTATE_INVALIDREQUEST;

				}

			}
			// else if there is still data
			else if ( oHeaderRegex.match ( sLine ) ) {

				std::string sName = oHeaderRegex [ 1 ];
				std::string sValue = oHeaderRegex [ 2 ];

				if ( sName == "Transfer-Encoding" &&
					 sValue == "chunked" ) {
					
					fprintf ( stderr, "EHS DOES NOT SUPPORT CHUNKED ENCODING.  Send an email to [email protected] and tell him you want chunked encoding (or send a patch)\n" );
					nCurrentHttpParseState = HTTPPARSESTATE_INVALIDREQUEST;

				}

				sName = mytolower ( sName );

				if ( sName == "cookie" ) {

					ParseCookieData ( sValue );

				}

				oRequestHeaders [ sName ] = sValue;

				
				
			}
			// else we had some sort of error -- bail out
			else {

#ifdef EHS_DEBUG
				fprintf ( stderr, "[EHS_DEBUG] Error: Invalid header line: '%s'\n",
						  sLine.c_str ( ) );
#endif

				nCurrentHttpParseState = HTTPPARSESTATE_INVALIDREQUEST;
				nDoneWithCurrentData = 1;

			}

			break;

			
		case HTTPPARSESTATE_BODY:
		{

			// if a content length wasn't specified, we can't be here (we 
			//   don't know chunked encoding)
			if ( oRequestHeaders.find ( "content-length" ) == 
				 oRequestHeaders.end ( ) ) {

				nCurrentHttpParseState = HTTPPARSESTATE_INVALIDREQUEST;
				continue;

			}

			// get the content length
			unsigned int nContentLength = 
				atoi ( oRequestHeaders [ "content-length" ].c_str ( ) );

			// else if we haven't gotten all the data we're looking for,
			//   just hold off and try again when we get more
			if ( irsData.length ( ) < nContentLength ) {

#ifdef EHS_DEBUG
				fprintf ( stderr, "[EHS_DEBUG] Info: Not enough data yet -- %d < %d\n",
						  irsData.length ( ), nContentLength );
#endif
				nDoneWithCurrentData = 1;

			}
			// otherwise, we've gotten enough data from the client, handle it now
			else {

				// grab out the actual body from the request and leave the rest
				sBody = irsData.substr ( 0, nContentLength );

				irsData = irsData.substr ( nContentLength );

				
				// if we're dealing with multi-part form attachments
				if ( oRequestHeaders [ "content-type" ].substr ( 0, 9 ) == 
					 "multipart" ) {

					// handle the body as if it's multipart form data
					if ( ParseMultipartFormData ( ) == 
						 PARSEMULTIPARTFORMDATA_SUCCESS ) {

						nCurrentHttpParseState = HTTPPARSESTATE_COMPLETEREQUEST;

					} else {

#ifdef EHS_DEBUG						
						fprintf ( stderr, "[EHS_DEBUG] Error: Mishandled multi-part form data for unknown reason\n" );
#endif
						nCurrentHttpParseState = HTTPPARSESTATE_INVALIDREQUEST;

					}
	

				}
				// else the body is just one piece
				else {
					// check for any form data
					GetFormDataFromString ( sBody );
					
#ifdef EHS_DEBUG
					fprintf ( stderr, "Done with body, done with entire request\n" );
#endif
					
					nCurrentHttpParseState = HTTPPARSESTATE_COMPLETEREQUEST;
				}
				
			}
			
			nDoneWithCurrentData = 1;

		}

		break;


		case HTTPPARSESTATE_INVALID:
		default:
#ifdef EHS_DEBUG
			fprintf ( stderr, "[EHS_DEBUG] Critical error: Invalid internal state: %d.  Aborting\n", nCurrentHttpParseState );
#endif
			assert ( 0 );
			break;
		}
	}

	return nCurrentHttpParseState;
	
}
Exemplo n.º 2
0
void* webserver::Request(void* ptr_s)
#endif
{
	Socket* s = (reinterpret_cast<Socket*>(ptr_s));

	std::string line = s->ReceiveLine();
	if (line.empty()) 
	{
		s->Close();
		delete s;
		return 0;
	}

	http_request req;
	std::string path;
	std::map<std::string, std::string> params;
	size_t posStartPath = 0;

	if (line.find("GET") == 0) 
	{
		req.method_="GET";
		posStartPath = line.find_first_not_of(" ",3);
	}
	else if (line.find("POST") == 0) 
	{
		req.method_="POST";
		posStartPath = line.find_first_not_of(" ",4);
	}

	SplitGetReq(line.substr(posStartPath), path, params);

	req.status_ = "202 OK";
	req.s_      = s;
	req.path_   = path;
	req.params_ = params;

	static const char authorization[]   = "Authorization: Basic ";
	static const char accept[]          = "Accept: ";
	static const char accept_language[] = "Accept-Language: ";
	static const char accept_encoding[] = "Accept-Encoding: ";
	static const char user_agent[]      = "User-Agent: ";
	static const char content_length[]  = "Content-Length: ";
	static const char content_type[]    = "Content-Type: ";

	while(1)
	{
		line=s->ReceiveLine();
		if (line.empty())
		{
			break;
		}

		unsigned int pos_cr_lf = line.find_first_of("\x0a\x0d");
		if (pos_cr_lf == 0) break;

		line = line.substr(0,pos_cr_lf);

		if (line.compare(0, sizeof(authorization) - 1, authorization) == 0) 
		{
			req.authentication_given_ = true;
			std::string encoded = line.substr(sizeof(authorization) - 1);
			std::string decoded = base64_decode(encoded);

			unsigned int pos_colon = decoded.find(":");

			req.username_ = decoded.substr(0, pos_colon);
			req.password_ = decoded.substr(pos_colon + 1);
		}
		else if (line.compare(0, sizeof(accept) - 1, accept) == 0)
		{
			req.accept_ = line.substr(sizeof(accept) - 1);
		}
		else if (line.compare(0, sizeof(accept_language) - 1, accept_language) == 0)
		{
			req.accept_language_ = line.substr(sizeof(accept_language) - 1);
		}
		else if (line.compare(0, sizeof(accept_encoding) - 1, accept_encoding) == 0)
		{
			req.accept_encoding_ = line.substr(sizeof(accept_encoding) - 1);
		}
		else if (line.compare(0, sizeof(user_agent) - 1, user_agent) == 0)
		{
			req.user_agent_ = line.substr(sizeof(user_agent) - 1);
		}
		else if (line.compare(0, sizeof(content_length) - 1, content_length) == 0)
		{
			req.content_length_ = atoi(line.substr(sizeof(content_length) - 1).c_str() );
		}
		else if (line.compare(0, sizeof(content_type) - 1, content_type) == 0)
		{
			req.content_type_ = line.substr(sizeof(content_type) - 1);
		}
	}

	if( (req.method_.compare("POST") == 0) && (req.content_length_ > 0) )
	{
		const char FormUrlEncoded[] = "application/x-www-form-urlencoded";
		// The only content type we can parse at the moment, the default HTML post data
		if( req.content_type_.substr(0, strlen(FormUrlEncoded)).compare( FormUrlEncoded ) == 0 )
		{
			std::string Content = s->ReceiveBytes( req.content_length_ );
			Content.insert( 0, "/ ?" ); // Little hack, inserts dummy URL so that SplitGetReq() can work with this content

			std::string dummy;
			std::map<std::string, std::string> post_params;
			SplitGetReq(Content, dummy, post_params);

			req.params_post_ = post_params;
		}
		else 
		{
			ParseMultipartFormData( req, s );
		}
	}

	request_func_(&req);

	std::stringstream str_str;
	str_str << req.answer_.size();

	time_t ltime;
	time(&ltime);
	tm* gmt= gmtime(&ltime);

#ifdef _WIN32
	static const char serverName[] = "MCServerWebAdmin (Windows)";
#elif __APPLE__
	static const char serverName[] = "MCServerWebAdmin (MacOSX)";
#else
	static const char serverName[] = "MCServerWebAdmin (Linux)";
#endif


	char* asctime_remove_nl = std::asctime(gmt);
	asctime_remove_nl[24] = 0;

	s->SendBytes("HTTP/1.1 ");

	if (! req.auth_realm_.empty() ) 
	{
		s->SendLine("401 Unauthorized");
		s->SendBytes("WWW-Authenticate: Basic Realm=\"");
		s->SendBytes(req.auth_realm_);
		s->SendLine("\"");
	}
	else 
	{
		s->SendLine(req.status_);
	}
	s->SendLine(std::string("Date: ") + asctime_remove_nl + " GMT");
	s->SendLine(std::string("Server: ") + serverName);
	s->SendLine("Connection: close");
	s->SendLine("Content-Type: text/html; charset=ISO-8859-1");
	s->SendLine("Content-Length: " + str_str.str());
	s->SendLine("");
	s->SendLine(req.answer_);

	s->Close( true ); // true = wait for all data to be sent before closing
	delete s;


	return 0;
}