Http.cpp
00001 00002 // 00003 // SFML - Simple and Fast Multimedia Library 00004 // Copyright (C) 2007-2009 Laurent Gomila ([email protected]) 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", "[email protected]"); 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
:: Copyright © 2007-2008 Laurent Gomila, all rights reserved :: Documentation generated by doxygen 1.5.2 ::