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 <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(std::string str)
00041     {
00042         for (std::string::iterator i = str.begin(); i != str.end(); ++i)
00043             *i = static_cast<char>(tolower(*i));
00044 
00045         return str;
00046     }
00047 }
00048 
00049 
00050 namespace sf
00051 {
00055 Http::Request::Request(Method method, const std::string& URI, const std::string& body)
00056 {
00057     SetMethod(method);
00058     SetURI(URI);
00059     SetHttpVersion(1, 0);
00060     SetBody(body);
00061 }
00062 
00063 
00067 void Http::Request::SetField(const std::string& field, const std::string& value)
00068 {
00069     myFields[ToLower(field)] = value;
00070 }
00071 
00072 
00077 void Http::Request::SetMethod(Http::Request::Method method)
00078 {
00079     myMethod = method;
00080 }
00081 
00082 
00087 void Http::Request::SetURI(const std::string& URI)
00088 {
00089     myURI = URI;
00090 
00091     // Make sure it starts with a '/'
00092     if (myURI.empty() || (myURI[0] != '/'))
00093         myURI.insert(0, "/");
00094 }
00095 
00096 
00101 void Http::Request::SetHttpVersion(unsigned int major, unsigned int minor)
00102 {
00103     myMajorVersion = major;
00104     myMinorVersion = minor;
00105 }
00106 
00107 
00113 void Http::Request::SetBody(const std::string& body)
00114 {
00115     myBody = body;
00116 }
00117 
00118 
00122 std::string Http::Request::ToString() const
00123 {
00124     std::ostringstream out;
00125 
00126     // Convert the method to its string representation
00127     std::string method;
00128     switch (myMethod)
00129     {
00130         default :
00131         case Get :  method = "GET";  break;
00132         case Post : method = "POST"; break;
00133         case Head : method = "HEAD"; break;
00134     }
00135 
00136     // Write the first line containing the request type
00137     out << method << " " << myURI << " ";
00138     out << "HTTP/" << myMajorVersion << "." << myMinorVersion << "\r\n";
00139 
00140     // Write fields
00141     for (FieldTable::const_iterator i = myFields.begin(); i != myFields.end(); ++i)
00142     {
00143         out << i->first << ": " << i->second << "\r\n";
00144     }
00145 
00146     // Use an extra \r\n to separate the header from the body
00147     out << "\r\n";
00148 
00149     // Add the body
00150     out << myBody;
00151 
00152     return out.str();
00153 }
00154 
00155 
00159 bool Http::Request::HasField(const std::string& field) const
00160 {
00161     return myFields.find(field) != myFields.end();
00162 }
00163 
00164 
00168 Http::Response::Response() :
00169 myStatus      (ConnectionFailed),
00170 myMajorVersion(0),
00171 myMinorVersion(0)
00172 {
00173 
00174 }
00175 
00176 
00180 const std::string& Http::Response::GetField(const std::string& field) const
00181 {
00182     FieldTable::const_iterator it = myFields.find(ToLower(field));
00183     if (it != myFields.end())
00184     {
00185         return it->second;
00186     }
00187     else
00188     {
00189         static const std::string empty = "";
00190         return empty;
00191     }
00192 }
00193 
00194 
00198 Http::Response::Status Http::Response::GetStatus() const
00199 {
00200     return myStatus;
00201 }
00202 
00203 
00207 unsigned int Http::Response::GetMajorHttpVersion() const
00208 {
00209     return myMajorVersion;
00210 }
00211 
00212 
00216 unsigned int Http::Response::GetMinorHttpVersion() const
00217 {
00218     return myMinorVersion;
00219 }
00220 
00221 
00229 const std::string& Http::Response::GetBody() const
00230 {
00231     return myBody;
00232 }
00233 
00234 
00238 void Http::Response::FromString(const std::string& data)
00239 {
00240     std::istringstream in(data);
00241 
00242     // Extract the HTTP version from the first line
00243     std::string version;
00244     if (in >> version)
00245     {
00246         if ((version.size() >= 8) && (version[6] == '.') &&
00247             (ToLower(version.substr(0, 5)) == "http/")   &&
00248              isdigit(version[5]) && isdigit(version[7]))
00249         {
00250             myMajorVersion = version[5] - '0';
00251             myMinorVersion = version[7] - '0';
00252         }
00253         else
00254         {
00255             // Invalid HTTP version
00256             myStatus = InvalidResponse;
00257             return;
00258         }
00259     }
00260 
00261     // Extract the status code from the first line
00262     int status;
00263     if (in >> status)
00264     {
00265         myStatus = static_cast<Status>(status);
00266     }
00267     else
00268     {
00269         // Invalid status code
00270         myStatus = InvalidResponse;
00271         return;
00272     }
00273 
00274     // Ignore the end of the first line
00275     in.ignore(10000, '\n');
00276 
00277     // Parse the other lines, which contain fields, one by one
00278     std::string line;
00279     while (std::getline(in, line) && (line.size() > 2))
00280     {
00281         std::string::size_type pos = line.find(": ");
00282         if (pos != std::string::npos)
00283         {
00284             // Extract the field name and its value
00285             std::string field = line.substr(0, pos);
00286             std::string value = line.substr(pos + 2);
00287 
00288             // Remove any trailing \r
00289             if (!value.empty() && (*value.rbegin() == '\r'))
00290                 value.erase(value.size() - 1);
00291 
00292             // Add the field
00293             myFields[ToLower(field)] = value;
00294         }
00295     }
00296 
00297     // Finally extract the body
00298     myBody.clear();
00299     std::copy(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>(), std::back_inserter(myBody));
00300 }
00301 
00302 
00306 Http::Http() :
00307 myHost(),
00308 myPort(0)
00309 {
00310 
00311 }
00312 
00313 
00317 Http::Http(const std::string& host, unsigned short port)
00318 {
00319     SetHost(host, port);
00320 }
00321 
00322 
00326 void Http::SetHost(const std::string& host, unsigned short port)
00327 {
00328     // Detect the protocol used
00329     std::string protocol = ToLower(host.substr(0, 8));
00330     if (protocol.substr(0, 7) == "http://")
00331     {
00332         // HTTP protocol
00333         myHostName = host.substr(7);
00334         myPort     = (port != 0 ? port : 80);
00335     }
00336     else if (protocol == "https://")
00337     {
00338         // HTTPS protocol
00339         myHostName = host.substr(8);
00340         myPort     = (port != 0 ? port : 443);
00341     }
00342     else
00343     {
00344         // Undefined protocol - use HTTP
00345         myHostName = host;
00346         myPort     = (port != 0 ? port : 80);
00347     }
00348 
00349     // Remove any trailing '/' from the host name
00350     if (!myHostName.empty() && (*myHostName.rbegin() == '/'))
00351         myHostName.erase(myHostName.size() - 1);
00352 
00353     myHost = IPAddress(myHostName);
00354 }
00355 
00356 
00365 Http::Response Http::SendRequest(const Http::Request& request, float timeout)
00366 {
00367     // First make sure the request is valid -- add missing mandatory fields
00368     Request toSend(request);
00369     if (!toSend.HasField("From"))
00370     {
00371         toSend.SetField("From", "user@sfml-dev.org");
00372     }
00373     if (!toSend.HasField("User-Agent"))
00374     {
00375         toSend.SetField("User-Agent", "libsfml-network/2.x");
00376     }
00377     if (!toSend.HasField("Host"))
00378     {
00379         toSend.SetField("Host", myHostName);
00380     }
00381     if (!toSend.HasField("Content-Length"))
00382     {
00383         std::ostringstream out;
00384         out << toSend.myBody.size();
00385         toSend.SetField("Content-Length", out.str());
00386     }
00387     if ((toSend.myMethod == Request::Post) && !toSend.HasField("Content-Type"))
00388     {
00389         toSend.SetField("Content-Type", "application/x-www-form-urlencoded");
00390     }
00391     if ((toSend.myMajorVersion * 10 + toSend.myMinorVersion >= 11) && !toSend.HasField("Connection"))
00392     {
00393         toSend.SetField("Connection", "close");
00394     }
00395 
00396     // Prepare the response
00397     Response received;
00398 
00399     // Connect the socket to the host
00400     if (myConnection.Connect(myPort, myHost, timeout) == Socket::Done)
00401     {
00402         // Convert the request to string and send it through the connected socket
00403         std::string requestStr = toSend.ToString();
00404 
00405         if (!requestStr.empty())
00406         {
00407             // Send it through the socket
00408             if (myConnection.Send(requestStr.c_str(), requestStr.size()) == Socket::Done)
00409             {
00410                 // Wait for the server's response
00411                 std::string receivedStr;
00412                 std::size_t size = 0;
00413                 char buffer[1024];
00414                 while (myConnection.Receive(buffer, sizeof(buffer), size) == Socket::Done)
00415                 {
00416                     receivedStr.append(buffer, buffer + size);
00417                 }
00418 
00419                 // Build the Response object from the received data
00420                 received.FromString(receivedStr);
00421             }
00422         }
00423 
00424         // Close the connection
00425         myConnection.Close();
00426     }
00427 
00428     return received;
00429 }
00430 
00431 } // namespace sf

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