Back to index

libsfml  1.6+dfsg2
Http.cpp
Go to the documentation of this file.
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 <algorithm>
00031 #include <iterator>
00032 #include <sstream>
00033 
00034 
00035 namespace
00036 {
00038     // Convenience function to convert a string to lower case
00040     std::string ToLower(const std::string& Str)
00041     {
00042         std::string Ret = Str;
00043         for (std::string::iterator i = Ret.begin(); i != Ret.end(); ++i)
00044             *i = static_cast<char>(tolower(*i));
00045 
00046         return Ret;
00047     }
00048 }
00049 
00050 
00051 namespace sf
00052 {
00056 Http::Request::Request(Method RequestMethod, const std::string& URI, const std::string& Body)
00057 {
00058     SetMethod(RequestMethod);
00059     SetURI(URI);
00060     SetHttpVersion(1, 0);
00061     SetBody(Body);
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(ToLower(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     std::copy(std::istreambuf_iterator<char>(In), std::istreambuf_iterator<char>(), std::back_inserter(myBody));
00301 }
00302 
00303 
00307 Http::Http() :
00308 myHost(),
00309 myPort(0)
00310 {
00311 
00312 }
00313 
00314 
00318 Http::Http(const std::string& Host, unsigned short Port)
00319 {
00320     SetHost(Host, Port);
00321 }
00322 
00323 
00327 void Http::SetHost(const std::string& Host, unsigned short Port)
00328 {
00329     // Detect the protocol used
00330     std::string Protocol = ToLower(Host.substr(0, 8));
00331     if (Protocol.substr(0, 7) == "http://")
00332     {
00333         // HTTP protocol
00334         myHostName = Host.substr(7);
00335         myPort     = (Port != 0 ? Port : 80);
00336     }
00337     else if (Protocol == "https://")
00338     {
00339         // HTTPS protocol
00340         myHostName = Host.substr(8);
00341         myPort     = (Port != 0 ? Port : 443);
00342     }
00343     else
00344     {
00345         // Undefined protocol - use HTTP
00346         myHostName = Host;
00347         myPort     = (Port != 0 ? Port : 80);
00348     }
00349 
00350     // Remove any trailing '/' from the host name
00351     if (!myHostName.empty() && (*myHostName.rbegin() == '/'))
00352         myHostName.erase(myHostName.size() - 1);
00353 
00354     myHost = sf::IPAddress(myHostName);
00355 }
00356 
00357 
00366 Http::Response Http::SendRequest(const Http::Request& Req, float Timeout)
00367 {
00368     // First make sure the request is valid -- add missing mandatory fields
00369     Request ToSend(Req);
00370     if (!ToSend.HasField("From"))
00371     {
00372         ToSend.SetField("From", "user@sfml-dev.org");
00373     }
00374     if (!ToSend.HasField("User-Agent"))
00375     {
00376         ToSend.SetField("User-Agent", "libsfml-network/1.x");
00377     }
00378     if (!ToSend.HasField("Host"))
00379     {
00380         ToSend.SetField("Host", myHostName);
00381     }
00382     if (!ToSend.HasField("Content-Length"))
00383     {
00384         std::ostringstream Out;
00385         Out << ToSend.myBody.size();
00386         ToSend.SetField("Content-Length", Out.str());
00387     }
00388     if ((ToSend.myMethod == Request::Post) && !ToSend.HasField("Content-Type"))
00389     {
00390         ToSend.SetField("Content-Type", "application/x-www-form-urlencoded");
00391     }
00392     if ((ToSend.myMajorVersion * 10 + ToSend.myMinorVersion >= 11) && !ToSend.HasField("Connection"))
00393     {
00394         ToSend.SetField("Connection", "close");
00395     }
00396 
00397     // Prepare the response
00398     Response Received;
00399 
00400     // Connect the socket to the host
00401     if (myConnection.Connect(myPort, myHost, Timeout) == Socket::Done)
00402     {
00403         // Convert the request to string and send it through the connected socket
00404         std::string RequestStr = ToSend.ToString();
00405 
00406         if (!RequestStr.empty())
00407         {
00408             // Send it through the socket
00409             if (myConnection.Send(RequestStr.c_str(), RequestStr.size()) == sf::Socket::Done)
00410             {
00411                 // Wait for the server's response
00412                 std::string ReceivedStr;
00413                 std::size_t Size = 0;
00414                 char Buffer[1024];
00415                 while (myConnection.Receive(Buffer, sizeof(Buffer), Size) == sf::Socket::Done)
00416                 {
00417                     ReceivedStr.append(Buffer, Buffer + Size);
00418                 }
00419 
00420                 // Build the Response object from the received data
00421                 Received.FromString(ReceivedStr);
00422             }
00423         }
00424 
00425         // Close the connection
00426         myConnection.Close();
00427     }
00428 
00429     return Received;
00430 }
00431 
00432 } // namespace sf