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 ::