• Main Page
  • Classes
  • Files
  • File List
  • File Members

Beesnest/DcRequest.cpp

Go to the documentation of this file.
00001 /*
00002 Copyright 2007 Erez Bibi (erezbibi@users.sourceforge.net)
00003 This file is part of Beesnest.
00004 
00005 Beesnest is free software; you can redistribute it and/or modify
00006 it under the terms of the GNU General Public License as published by
00007 the Free Software Foundation; either version 2 of the License, or
00008 (at your option) any later version.
00009 
00010 Beesnest is distributed in the hope that it will be useful,
00011 but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 GNU General Public License for more details.
00014 
00015 You should have received a copy of the GNU General Public License
00016 along with Beesnest; if not, write to the Free Software
00017 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00018 */
00019 
00020 
00021 /**
00022  * DcRequest.cpp: Implamentation of CdcRequst class.
00023  * for documentation see the header file.
00024  */
00025 
00026 #include "DcRequest.h"
00027 
00028 CdcRequest::CdcRequest (CdcSocket* psock)
00029 {
00030     if (psock == NULL)
00031         throw (CdcException ("Socket pointer is null"));
00032     pSocket = psock;
00033     sBuffer.reserve (1024);
00034 }
00035 
00036 
00037 void CdcRequest::ReadNextRequest ()
00038 {
00039     int place;
00040     ResetRequestFields ();
00041     pSocket->StartNewRequest ();
00042     /* Find end of header, if not found read from socket. */
00043     while ((place = sBuffer.Find (CRLFCRLF)) == -1)
00044         pSocket->Read (sBuffer);
00045     place += CRLFCRLF_SIZE;
00046     /* Parse header. */
00047     ParseHeader (sBuffer.Left (place));
00048     /* Trim buffer to rest of request. */
00049     if (place < sBuffer.GetLength ())
00050         sBuffer = sBuffer.Mid (place);
00051     else
00052         sBuffer = EMPTY_STR;
00053     /* if no body - return. */
00054     if (sMethod != POST) return;
00055     /* send the 100-Continue message anyway if it is required. */
00056     Handle100Continue ();
00057     /* Check if this is a chunk body. */
00058     CdcString ste = GetHeaderField ("Transfer-Encoding");
00059     ste.MakeLower ();
00060     CdcString msg_body;
00061     if (ste.Find ("chunked") != -1)
00062         DoChunkRequest (msg_body);  /* Will fill msg_body. */
00063     else
00064     {   /* The length of the body determent by "Content-Length". */
00065         CdcString slng = GetHeaderField ("Content-Length");
00066         if (slng.IsEmpty ())
00067             throw (CdcExceptionParse ("Cannot find the message length",
00068             DC_PARSE_EXP));
00069         /* Read entire body. */
00070         int lng = atoi (slng.GetBuffer ());
00071         while (sBuffer.GetLength () < lng)
00072             pSocket->Read (sBuffer);
00073         /* Extract body. */
00074         msg_body = sBuffer.Left (lng);
00075         /* Trim buffer to rest of request. */
00076         if (lng < sBuffer.GetLength ())
00077             sBuffer = sBuffer.Mid (lng);
00078         else
00079             sBuffer = EMPTY_STR;
00080     }
00081     /* Try to get variables from POST body (Ignore exceptions).
00082     Insert msg_body to vars map. */
00083     SetVariable ("BN_Body", msg_body);
00084     try { ParseQueryLine (msg_body); }
00085     catch (CdcExceptionParse& e) {e.GetExpCode ();}
00086 }
00087 
00088 
00089 void CdcRequest::Handle100Continue ()
00090 {
00091     /* Don't send Continue message if HTTP 1.0 */
00092     if (sVersion == PROTOCOL10_VRS)
00093         return;
00094 
00095     if (GetHeaderField ("Expect") == "100-continue")
00096     {   /* Send the Continue message. */
00097         pSocket->Write (CONTINUE_MSG, strlen (CONTINUE_MSG));
00098         pSocket->Write (CRLFCRLF, CRLFCRLF_SIZE);
00099     }
00100     return;
00101 }
00102 
00103 void CdcRequest::DoChunkRequest (CdcString& msg_body) throw (CdcException)
00104 {
00105 PrintString ("Chunked Request", PS_DBG);
00106     int place = 0, lng;
00107     /* Read from the socket until a blank line. */
00108     while (sBuffer.Find (CRLFCRLF) == -1)
00109         pSocket->Read (sBuffer);
00110     /* Read first the "Length" line. */
00111     lng = strtoul (sBuffer.GetBuffer (), NULL, 16);
00112     place = sBuffer.Find (CRLF, place) + CRLF_SIZE;
00113     if (place == -1)
00114         throw (CdcExceptionParse ("Bad format of chuncked request",
00115         DC_CNT_PRS_CNK_EXP));
00116     /* Extract chunks. */
00117     while (lng > 0)
00118     {
00119         if (sBuffer.GetLength () - place < lng)
00120             throw (CdcExceptionParse ("Bad format of chuncked request",
00121             DC_CNT_PRS_CNK_EXP));
00122         /* Copy chunk of message. */
00123         msg_body += sBuffer.Mid (place, lng);
00124         place += lng + CRLF_SIZE;
00125         /* Read next "Length" line. */
00126         if (sBuffer.GetLength () < place)
00127             throw (CdcExceptionParse ("Bad format of chuncked request",
00128             DC_CNT_PRS_CNK_EXP));
00129         lng = strtoul (sBuffer.Mid (place).GetBuffer (), NULL, 16);
00130         place = sBuffer.Find (CRLF, place) + CRLF_SIZE;
00131         if (place == -1)
00132             throw (CdcExceptionParse ("Bad format of chuncked request",
00133             DC_CNT_PRS_CNK_EXP));
00134     }
00135     /* Extract trailer lines. */
00136     while (1)
00137     {   /* Until CRLF is in the beginning (end of request)... */
00138         lng = sBuffer.Find (CRLF, place) - place;
00139         if (lng < 0 || sBuffer.GetLength () - place < lng)
00140             throw (CdcExceptionParse ("Bad format of chuncked request",
00141             DC_CNT_PRS_CNK_EXP));
00142         if (lng == place)
00143             break;
00144         ParseHeaderLine (sBuffer.Mid (place, lng - place));
00145         place = lng + CRLF_SIZE;
00146     }
00147 }
00148 
00149 
00150 void CdcRequest::FindHost () throw (CdcException)
00151 {
00152     /* Check if not HTTP 1.0. */
00153     sHost = "Default-Host";
00154     sReqHost = EMPTY_STR;
00155     if (sVersion == PROTOCOL10_VRS) return;
00156     /* Get the Host: header field. */
00157     sReqHost = GetHeaderField ("Host");
00158     if (sReqHost.IsEmpty ()) throw (CdcExceptionParse (
00159         "HTTP 1.1 request MUST include the Host: header", DC_PARSE_EXP));
00160     /* Don't take the port number (if exist). */
00161     int place = sReqHost.Find (':');
00162     if (place != -1) sReqHost = sReqHost.Left (place);
00163 
00164     CdcString tmp = sReqHost;
00165     while (1)
00166     {
00167         sHost = CdcParameters::GetParam ("Server->Aliases->" + tmp);
00168         if (sHost.IsEmpty () == false) return;
00169         if (tmp == "*") break;
00170         if (tmp[0] == '*' && tmp[1] == '.')
00171             tmp = tmp.Mid (2);  /* Drop *. if exist. */
00172         place = tmp.Find (".");
00173         if (place != -1)
00174             tmp = "*." + tmp.Mid (place + 1);
00175         else
00176             tmp = '*';
00177     }
00178     throw (CdcExceptionParse (
00179         "Wrong host name in the Host: header.", DC_PARSE_EXP));
00180 }
00181 
00182 
00183 void CdcRequest::ResetRequestFields ()
00184 {
00185     mHeaderFields.clear ();
00186     mVariables.clear ();
00187 
00188     sMethod = EMPTY_STR;
00189     sURL = EMPTY_STR;
00190     sVersion = EMPTY_STR;
00191 
00192     /* Set the client IP address. */
00193     SetVariable ("BN_ClinetIP", pSocket->GetClientAddress ());
00194     SetVariable ("BN_AppPath", CdcFileUtil::GetProgramPath ());
00195     SetVariable ("BN_Server", SERVER_NAME);
00196     SetVariable ("BN_Port", CdcParameters::GetParam ("Server->Port"));
00197 }
00198 
00199 /* Parsing section. */
00200 
00201 void CdcRequest::ParseHeader (CdcString& msg) throw (CdcException)
00202 {
00203     int endline, place;
00204     /* Extract Request Line. */
00205     place = msg.Find (CRLF);
00206     if (place == -1)
00207         throw (CdcExceptionParse ("Cannot parse the Request message",
00208         DC_CNT_PRS_REQ_EXP));
00209     ParseRequsetLine (msg.Left (place));
00210     /* Extract header lines. */
00211     place += CRLF_SIZE;
00212     while ((endline = msg.Find (CRLF, place)) > place)
00213     {
00214         CdcString header = msg.Mid (place, endline - place);
00215         if (header.Left (6).CompareNoCase ("Cookie") == 0)
00216             ParseCookiesHeader (header);
00217         ParseHeaderLine (header);
00218         place = endline + CRLF_SIZE;
00219     }
00220     if (endline == -1)
00221         throw (CdcExceptionParse ("Cannot parse the Request message",
00222         DC_CNT_PRS_REQ_EXP));
00223     /* Find the host this request refer to (multi-hosts system). */
00224     FindHost ();
00225     /* Set special request variables */
00226     SetVariable ("BN_RootPath", CdcFileUtil::GetWorkPath (
00227         CdcParameters::GetParam ("Server->Hosts->" + sHost + "->Root")));
00228     SetVariable ("BN_Host", sHost);
00229     SetVariable ("BN_Alias", sReqHost);
00230     SetVariable ("BN_Version", sVersion);
00231     SetVariable ("BN_Method", sMethod);
00232     SetVariable ("BN_Protocol", sProtocol);
00233     SetVariable ("BN_URL", sURL);
00234     SetVariable ("BN_FullURL", sFullURL);
00235     SetVariable ("BN_Address", sProtocol + "://" + sReqHost + sFullURL);
00236 }
00237 
00238 
00239 void CdcRequest::ParseRequsetLine (CdcString& line) throw (CdcException)
00240 {
00241     int place;
00242     /* Extract the three parts of the line. */
00243     place = line.Find (' ');
00244     if (place == -1)
00245         throw (CdcExceptionParse ("Cannot find the url part of request line",
00246         DC_CNT_PRS_REQ_LINE_EXP));
00247     sMethod = line.Left (place);
00248     sMethod.TrimLeft ();
00249     /* Check the request method. */
00250     if (!(sMethod == GET || sMethod == POST || sMethod == HEAD))
00251         throw (CdcExceptionParse ("Method not Implemented", DC_NOT_SUPPORT));
00252     /* Extract the version. */
00253     line = line.Mid (place + 1);
00254     place = line.Find (' ');
00255     if (place == -1)
00256         throw (CdcExceptionParse ("Cannot find the version part of request line",
00257         DC_CNT_PRS_REQ_LINE_EXP));
00258     sFullURL = line.Left (place);
00259     sVersion = line.Mid (place + 1);
00260     sVersion.TrimLeft ();
00261     /* Parse the URL. Separate the URL from the query, host ,port
00262         and protocol */
00263     /* Discard the protocol part. */
00264     place = sFullURL.Find ("://");
00265     if (place == -1) /* if not found 'place' should be 0. */
00266         place = -3;
00267     place += 3;
00268     if (place  > 0 && sFullURL.Left (place) != "http://")
00269         throw (CdcExceptionParse ("Server Support just HTTP protocol",
00270         DC_CNT_PRS_REQ_LINE_EXP));
00271     sProtocol = "http";
00272     /* Find the starting of the path part. */
00273     place = sFullURL.Find ('/', place);
00274     if (place == -1)
00275         throw (CdcExceptionParse ("Cannot find '/' in the url",
00276         DC_CNT_PRS_REQ_LINE_EXP));
00277     sURL = sFullURL.Mid (place);
00278     /* Extract the query if exists. */
00279     place = sURL.Find ('?');
00280     if (place != -1)
00281     {   /* Parse query w/o '?'. */
00282         CdcString tmp = sURL.Mid (place + 1);
00283         SetVariable ("BN_Query", tmp);
00284         ParseQueryLine (tmp);
00285         sURL = sURL.Left (place);
00286     }
00287 }
00288 
00289 
00290 void CdcRequest::ParseQueryLine (CdcString& line, char limit) throw (CdcException)
00291 {
00292     CdcString name, val;
00293     int place, end;
00294 
00295     do
00296     {   /* Find the name of the variable. */
00297         place = line.Find ('=');
00298         if (place == -1)
00299             throw (CdcExceptionParse ("Cannot parse the query part of the url.",
00300             DC_CNT_PRS_QUERY_EXP));
00301         name = line.Left (place);
00302         name.TrimLeft ();
00303         name.TrimRight ();
00304         URIDecode (name);
00305         place++;
00306         while (place < line.GetLength () && line[place] == '=')
00307             ++place; /* That is for cookies. */
00308         /* Find the value of the variable. */
00309         end = line.Find (limit, place);
00310         if (end == -1)
00311             val = line.Mid (place);
00312         else
00313             val = line.Mid (place, end - place);
00314         val.TrimLeft ();
00315         val.TrimRight ();
00316         URIDecode (val);
00317         /* insert to the variables map. */
00318         if (mVariables.find (name) == mVariables.end ())
00319             mVariables [name] = val;
00320         else
00321             mVariables [name] += ',' + val;
00322         /* Trim the line to the next variable,
00323             if no '&' sign this line does nothing. */
00324         line = line.Mid (end + 1);
00325     }
00326     while (end != -1);
00327 }
00328 
00329 
00330 void CdcRequest::ParseCookiesHeader (CdcString& line)
00331 {
00332     /* Discard header name. */
00333     int place = line.Find (':');
00334     if (place == -1) return;
00335     ++place;
00336 
00337     CdcString tmp = line.Mid (place);
00338     SetVariable ("BN_Cookies", tmp);
00339     /* Parse the line (ignore exceptions). */
00340     try
00341     { ParseQueryLine (tmp, ';'); }
00342     catch (CdcExceptionParse& e)
00343     { e.GetExpCode (); }
00344 }
00345 
00346 void CdcRequest::URIDecode (CdcString& str)
00347 {
00348     char* stop;
00349     CdcString new_str;
00350     int place, old_place = 0;
00351     /* Replace all '+' to space. */
00352     str.Replace ('+', ' ');
00353     while (1)
00354     {   /* Find next '%'. */
00355         place = str.Find ('%', old_place);
00356         if (place == -1)
00357         {   /* If not found - copy rest of string and exit. */
00358             new_str += str.Mid (old_place);
00359             str = new_str;
00360             return;
00361         }
00362         else
00363         {   /* If found - copy string until '%', add char, and continue. */
00364             new_str += str.Mid (old_place, place - old_place);
00365             new_str += (char) strtol (str.Mid (place + 1, 2), &stop, 16);
00366             old_place = place + 3;
00367         }
00368     }
00369 }
00370 
00371 
00372 void CdcRequest::ParseHeaderLine (CdcString& line) throw (CdcException)
00373 {
00374     int place = line.Find (':');
00375     if (place == -1)
00376         throw (CdcExceptionParse ("Cannot parse the header field",
00377         DC_CNT_PRS_HEADER_EXP));
00378     /* Get the header field name. */
00379     CdcString name = line.Left (place);
00380     name.TrimLeft ();
00381     name.TrimRight ();
00382     name.MakeLower ();
00383     /* Get the header field value. */
00384     CdcString val = line.Mid (place + 1);
00385     val.TrimLeft ();
00386     val.TrimRight ();
00387     /* Insert to Header Fields map. */
00388     mHeaderFields [name] = val;
00389 }
00390 
00391 void CdcRequest::EraseHeaderField (const CdcString& name)
00392 {
00393     CdcString temp = name;
00394     temp.MakeLower ();
00395     mHeaderFields.erase (temp);
00396 }
00397 
00398 const string& CdcRequest::__GetHeaderField (const CdcString& name) const
00399 {
00400     CdcString temp = name;
00401     temp.MakeLower ();
00402     t_const_itr itr = mHeaderFields.find (temp);
00403     if (itr == mHeaderFields.end ())
00404         return EMPTY_STR;
00405     return itr->second;
00406 }
00407 
00408 const string& CdcRequest::__GetVariable (const CdcString& name) const
00409 {
00410     t_const_itr itr = mVariables.find (name);
00411     if (itr == mVariables.end ())
00412         return EMPTY_STR;
00413     return itr->second;
00414 }
00415 
00416 BufferHolder CdcRequest::GetRequestVarsNamesList (BufferHolder sep)
00417 {
00418     sVarList.clear ();
00419     CdcString s_sep (sep);
00420     t_map* pmap;
00421     t_map::const_iterator pos;
00422 
00423     if (s_sep[0] == 'H')
00424     {
00425         pmap = &mHeaderFields;
00426         s_sep = s_sep.Mid (1);
00427     }
00428     else
00429         pmap = &mVariables;
00430 
00431     for (pos = pmap->begin ();
00432          pos != pmap->end (); ++pos)
00433     {
00434         if (sVarList.empty () == false)
00435             sVarList += s_sep;
00436         sVarList += pos->first;
00437     }
00438 
00439     return BufferHolder (sVarList.c_str ());
00440 }
00441 

Generated on Mon Oct 11 2010 16:23:24 for Beesnest by  doxygen 1.7.2