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