// 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; }
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(<ime); tm* gmt= gmtime(<ime); #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; }