SFML logo
  • Main Page
  • Namespaces
  • Classes
  • Files
  • File List

Http.cpp

00001 
00002 //
00003 // SFML - Simple and Fast Multimedia Library
00004 // Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
00005 //
00006 // This software is provided 'as-is', without any express or implied warranty.
00007 // In no event will the authors be held liable for any damages arising from the use of this software.
00008 //
00009 // Permission is granted to anyone to use this software for any purpose,
00010 // including commercial applications, and to alter it and redistribute it freely,
00011 // subject to the following restrictions:
00012 //
00013 // 1. The origin of this software must not be misrepresented;
00014 //    you must not claim that you wrote the original software.
00015 //    If you use this software in a product, an acknowledgment
00016 //    in the product documentation would be appreciated but is not required.
00017 //
00018 // 2. Altered source versions must be plainly marked as such,
00019 //    and must not be misrepresented as being the original software.
00020 //
00021 // 3. This notice may not be removed or altered from any source distribution.
00022 //
00024 
00026 // Headers
00028 #include <SFML/Network/Http.hpp>
00029 #include <ctype.h>
00030 #include <sstream>
00031 
00032 
00033 namespace
00034 {
00036     // Convenience function to convert a string to lower case
00038     std::string ToLower(const std::string& Str)
00039     {
00040         std::string Ret = Str;
00041         for (std::string::iterator i = Ret.begin(); i != Ret.end(); ++i)
00042             *i = static_cast<char>(tolower(*i));
00043 
00044         return Ret;
00045     }
00046 }
00047 
00048 
00049 namespace sf
00050 {
00054 Http::Request::Request(Method RequestMethod, const std::string& URI, const std::string& Body) :
00055 myMethod      (RequestMethod),
00056 myURI         (URI),
00057 myMajorVersion(1),
00058 myMinorVersion(0),
00059 myBody        (Body)
00060 {
00061 
00062 }
00063 
00064 
00068 void Http::Request::SetField(const std::string& Field, const std::string& Value)
00069 {
00070     myFields[ToLower(Field)] = Value;
00071 }
00072 
00073 
00078 void Http::Request::SetMethod(Http::Request::Method RequestMethod)
00079 {
00080     myMethod = RequestMethod;
00081 }
00082 
00083 
00088 void Http::Request::SetURI(const std::string& URI)
00089 {
00090     myURI = URI;
00091 
00092     // Make sure it starts with a '/'
00093     if (myURI.empty() || (myURI[0] != '/'))
00094         myURI.insert(0, "/");
00095 }
00096 
00097 
00102 void Http::Request::SetHttpVersion(unsigned int Major, unsigned int Minor)
00103 {
00104     myMajorVersion = Major;
00105     myMinorVersion = Minor;
00106 }
00107 
00108 
00114 void Http::Request::SetBody(const std::string& Body)
00115 {
00116     myBody = Body;
00117 }
00118 
00119 
00123 std::string Http::Request::ToString() const
00124 {
00125     std::ostringstream Out;
00126 
00127     // Convert the method to its string representation
00128     std::string RequestMethod;
00129     switch (myMethod)
00130     {
00131         default :
00132         case Get :  RequestMethod = "GET";  break;
00133         case Post : RequestMethod = "POST"; break;
00134         case Head : RequestMethod = "HEAD"; break;
00135     }
00136 
00137     // Write the first line containing the request type
00138     Out << RequestMethod << " " << myURI << " ";
00139     Out << "HTTP/" << myMajorVersion << "." << myMinorVersion << "\r\n";
00140 
00141     // Write fields
00142     for (FieldTable::const_iterator i = myFields.begin(); i != myFields.end(); ++i)
00143     {
00144         Out << i->first << ": " << i->second << "\r\n";
00145     }
00146 
00147     // Use an extra \r\n to separate the header from the body
00148     Out << "\r\n";
00149 
00150     // Add the body
00151     Out << myBody;
00152 
00153     return Out.str();
00154 }
00155 
00156 
00160 bool Http::Request::HasField(const std::string& Field) const
00161 {
00162     return myFields.find(Field) != myFields.end();
00163 }
00164 
00165 
00169 Http::Response::Response() :
00170 myStatus      (ConnectionFailed),
00171 myMajorVersion(0),
00172 myMinorVersion(0)
00173 {
00174 
00175 }
00176 
00177 
00181 const std::string& Http::Response::GetField(const std::string& Field) const
00182 {
00183     FieldTable::const_iterator It = myFields.find(Field);
00184     if (It != myFields.end())
00185     {
00186         return It->second;
00187     }
00188     else
00189     {
00190         static const std::string Empty = "";
00191         return Empty;
00192     }
00193 }
00194 
00195 
00199 Http::Response::Status Http::Response::GetStatus() const
00200 {
00201     return myStatus;
00202 }
00203 
00204 
00208 unsigned int Http::Response::GetMajorHttpVersion() const
00209 {
00210     return myMajorVersion;
00211 }
00212 
00213 
00217 unsigned int Http::Response::GetMinorHttpVersion() const
00218 {
00219     return myMinorVersion;
00220 }
00221 
00222 
00230 const std::string& Http::Response::GetBody() const
00231 {
00232     return myBody;
00233 }
00234 
00235 
00239 void Http::Response::FromString(const std::string& Data)
00240 {
00241     std::istringstream In(Data);
00242 
00243     // Extract the HTTP version from the first line
00244     std::string Version;
00245     if (In >> Version)
00246     {
00247         if ((Version.size() >= 8) && (Version[6] == '.') &&
00248             (ToLower(Version.substr(0, 5)) == "http/")   &&
00249              isdigit(Version[5]) && isdigit(Version[7]))
00250         {
00251             myMajorVersion = Version[5] - '0';
00252             myMinorVersion = Version[7] - '0';
00253         }
00254         else
00255         {
00256             // Invalid HTTP version
00257             myStatus = InvalidResponse;
00258             return;
00259         }
00260     }
00261 
00262     // Extract the status code from the first line
00263     int StatusCode;
00264     if (In >> StatusCode)
00265     {
00266         myStatus = static_cast<Status>(StatusCode);
00267     }
00268     else
00269     {
00270         // Invalid status code
00271         myStatus = InvalidResponse;
00272         return;
00273     }
00274 
00275     // Ignore the end of the first line
00276     In.ignore(10000, '\n');
00277 
00278     // Parse the other lines, which contain fields, one by one
00279     std::string Line;
00280     while (std::getline(In, Line) && (Line.size() > 2))
00281     {
00282         std::string::size_type Pos = Line.find(": ");
00283         if (Pos != std::string::npos)
00284         {
00285             // Extract the field name and its value
00286             std::string Field = Line.substr(0, Pos);
00287             std::string Value = Line.substr(Pos + 2);
00288 
00289             // Remove any trailing \r
00290             if (!Value.empty() && (*Value.rbegin() == '\r'))
00291                 Value.erase(Value.size() - 1);
00292 
00293             // Add the field
00294             myFields[ToLower(Field)] = Value;
00295         }
00296     }
00297 
00298     // Finally extract the body
00299     myBody.clear();
00300     while (std::getline(In, Line))
00301         myBody += Line + "\n";
00302 }
00303 
00304 
00308 Http::Http() :
00309 myHost(),
00310 myPort(0)
00311 {
00312 
00313 }
00314 
00315 
00319 Http::Http(const std::string& Host, unsigned short Port)
00320 {
00321     SetHost(Host, Port);
00322 }
00323 
00324 
00328 void Http::SetHost(const std::string& Host, unsigned short Port)
00329 {
00330     // Detect the protocol used
00331     std::string Protocol = ToLower(Host.substr(0, 8));
00332     if (Protocol.substr(0, 7) == "http://")
00333     {
00334         // HTTP protocol
00335         myHostName = Host.substr(7);
00336         myPort     = (Port != 0 ? Port : 80);
00337     }
00338     else if (Protocol == "https://")
00339     {
00340         // HTTPS protocol
00341         myHostName = Host.substr(8);
00342         myPort     = (Port != 0 ? Port : 443);
00343     }
00344     else
00345     {
00346         // Undefined protocol - use HTTP
00347         myHostName = Host;
00348         myPort     = (Port != 0 ? Port : 80);
00349     }
00350 
00351     // Remove any trailing '/' from the host name
00352     if (!myHostName.empty() && (*myHostName.rbegin() == '/'))
00353         myHostName.erase(myHostName.size() - 1);
00354 
00355     myHost = sf::IPAddress(myHostName);
00356 }
00357 
00358 
00367 Http::Response Http::SendRequest(const Http::Request& Req, float Timeout)
00368 {
00369     // First make sure the request is valid -- add missing mandatory fields
00370     Request ToSend(Req);
00371     if (!ToSend.HasField("From"))
00372     {
00373         ToSend.SetField("From", "user@sfml-dev.org");
00374     }
00375     if (!ToSend.HasField("User-Agent"))
00376     {
00377         ToSend.SetField("User-Agent", "libsfml-network/1.x");
00378     }
00379     if (!ToSend.HasField("Host"))
00380     {
00381         ToSend.SetField("Host", myHostName);
00382     }
00383     if (!ToSend.HasField("Content-Length"))
00384     {
00385         std::ostringstream Out;
00386         Out << ToSend.myBody.size();
00387         ToSend.SetField("Content-Length", Out.str());
00388     }
00389 
00390     // Prepare the response
00391     Response Received;
00392 
00393     // Connect the socket to the host
00394     if (myConnection.Connect(myPort, myHost, Timeout) == Socket::Done)
00395     {
00396         // Convert the request to string and send it through the connected socket
00397         std::string RequestStr = ToSend.ToString();
00398 
00399         if (!RequestStr.empty())
00400         {
00401             // Send it through the socket
00402             if (myConnection.Send(RequestStr.c_str(), RequestStr.size()) == sf::Socket::Done)
00403             {
00404                 // Wait for the server's response
00405                 std::string ReceivedStr;
00406                 std::size_t Size = 0;
00407                 char Buffer[1024];
00408                 while (myConnection.Receive(Buffer, sizeof(Buffer), Size) == sf::Socket::Done)
00409                 {
00410                     ReceivedStr.append(Buffer, Buffer + Size);
00411                 }
00412 
00413                 // Build the Response object from the received data
00414                 Received.FromString(ReceivedStr);
00415             }
00416         }
00417 
00418         // Close the connection
00419         myConnection.Close();
00420     }
00421 
00422     return Received;
00423 }
00424 
00425 } // namespace sf

 ::  Copyright © 2007-2008 Laurent Gomila, all rights reserved  ::  Documentation generated by doxygen 1.5.2  ::