mirror of
https://github.com/SFML/SFML.git
synced 2025-02-14 20:38:01 +08:00
git-svn-id: https://sfml.svn.sourceforge.net/svnroot/sfml/branches/sfml2@1565 4e206d99-4929-0410-ac5d-dfc041789085
380 lines
10 KiB
C++
380 lines
10 KiB
C++
////////////////////////////////////////////////////////////
|
|
//
|
|
// SFML - Simple and Fast Multimedia Library
|
|
// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
|
|
//
|
|
// This software is provided 'as-is', without any express or implied warranty.
|
|
// In no event will the authors be held liable for any damages arising from the use of this software.
|
|
//
|
|
// Permission is granted to anyone to use this software for any purpose,
|
|
// including commercial applications, and to alter it and redistribute it freely,
|
|
// subject to the following restrictions:
|
|
//
|
|
// 1. The origin of this software must not be misrepresented;
|
|
// you must not claim that you wrote the original software.
|
|
// If you use this software in a product, an acknowledgment
|
|
// in the product documentation would be appreciated but is not required.
|
|
//
|
|
// 2. Altered source versions must be plainly marked as such,
|
|
// and must not be misrepresented as being the original software.
|
|
//
|
|
// 3. This notice may not be removed or altered from any source distribution.
|
|
//
|
|
////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// Headers
|
|
////////////////////////////////////////////////////////////
|
|
#include <SFML/Network/Http.hpp>
|
|
#include <cctype>
|
|
#include <algorithm>
|
|
#include <iterator>
|
|
#include <sstream>
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// Private data
|
|
////////////////////////////////////////////////////////////
|
|
namespace
|
|
{
|
|
// Convert a string to lower case
|
|
std::string ToLower(std::string str)
|
|
{
|
|
for (std::string::iterator i = str.begin(); i != str.end(); ++i)
|
|
*i = static_cast<char>(std::tolower(*i));
|
|
return str;
|
|
}
|
|
}
|
|
|
|
|
|
namespace sf
|
|
{
|
|
////////////////////////////////////////////////////////////
|
|
Http::Request::Request(const std::string& uri, Method method, const std::string& body)
|
|
{
|
|
SetMethod(method);
|
|
SetUri(uri);
|
|
SetHttpVersion(1, 0);
|
|
SetBody(body);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
void Http::Request::SetField(const std::string& field, const std::string& value)
|
|
{
|
|
myFields[ToLower(field)] = value;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
void Http::Request::SetMethod(Http::Request::Method method)
|
|
{
|
|
myMethod = method;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
void Http::Request::SetUri(const std::string& uri)
|
|
{
|
|
myURI = uri;
|
|
|
|
// Make sure it starts with a '/'
|
|
if (myURI.empty() || (myURI[0] != '/'))
|
|
myURI.insert(0, "/");
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
void Http::Request::SetHttpVersion(unsigned int major, unsigned int minor)
|
|
{
|
|
myMajorVersion = major;
|
|
myMinorVersion = minor;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
void Http::Request::SetBody(const std::string& body)
|
|
{
|
|
myBody = body;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
std::string Http::Request::Prepare() const
|
|
{
|
|
std::ostringstream out;
|
|
|
|
// Convert the method to its string representation
|
|
std::string method;
|
|
switch (myMethod)
|
|
{
|
|
default :
|
|
case Get : method = "GET"; break;
|
|
case Post : method = "POST"; break;
|
|
case Head : method = "HEAD"; break;
|
|
}
|
|
|
|
// Write the first line containing the request type
|
|
out << method << " " << myURI << " ";
|
|
out << "HTTP/" << myMajorVersion << "." << myMinorVersion << "\r\n";
|
|
|
|
// Write fields
|
|
for (FieldTable::const_iterator i = myFields.begin(); i != myFields.end(); ++i)
|
|
{
|
|
out << i->first << ": " << i->second << "\r\n";
|
|
}
|
|
|
|
// Use an extra \r\n to separate the header from the body
|
|
out << "\r\n";
|
|
|
|
// Add the body
|
|
out << myBody;
|
|
|
|
return out.str();
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
bool Http::Request::HasField(const std::string& field) const
|
|
{
|
|
return myFields.find(ToLower(field)) != myFields.end();
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
Http::Response::Response() :
|
|
myStatus (ConnectionFailed),
|
|
myMajorVersion(0),
|
|
myMinorVersion(0)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
const std::string& Http::Response::GetField(const std::string& field) const
|
|
{
|
|
FieldTable::const_iterator it = myFields.find(ToLower(field));
|
|
if (it != myFields.end())
|
|
{
|
|
return it->second;
|
|
}
|
|
else
|
|
{
|
|
static const std::string empty = "";
|
|
return empty;
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
Http::Response::Status Http::Response::GetStatus() const
|
|
{
|
|
return myStatus;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
unsigned int Http::Response::GetMajorHttpVersion() const
|
|
{
|
|
return myMajorVersion;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
unsigned int Http::Response::GetMinorHttpVersion() const
|
|
{
|
|
return myMinorVersion;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
const std::string& Http::Response::GetBody() const
|
|
{
|
|
return myBody;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
void Http::Response::Parse(const std::string& data)
|
|
{
|
|
std::istringstream in(data);
|
|
|
|
// Extract the HTTP version from the first line
|
|
std::string version;
|
|
if (in >> version)
|
|
{
|
|
if ((version.size() >= 8) && (version[6] == '.') &&
|
|
(ToLower(version.substr(0, 5)) == "http/") &&
|
|
isdigit(version[5]) && isdigit(version[7]))
|
|
{
|
|
myMajorVersion = version[5] - '0';
|
|
myMinorVersion = version[7] - '0';
|
|
}
|
|
else
|
|
{
|
|
// Invalid HTTP version
|
|
myStatus = InvalidResponse;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Extract the status code from the first line
|
|
int status;
|
|
if (in >> status)
|
|
{
|
|
myStatus = static_cast<Status>(status);
|
|
}
|
|
else
|
|
{
|
|
// Invalid status code
|
|
myStatus = InvalidResponse;
|
|
return;
|
|
}
|
|
|
|
// Ignore the end of the first line
|
|
in.ignore(10000, '\n');
|
|
|
|
// Parse the other lines, which contain fields, one by one
|
|
std::string line;
|
|
while (std::getline(in, line) && (line.size() > 2))
|
|
{
|
|
std::string::size_type pos = line.find(": ");
|
|
if (pos != std::string::npos)
|
|
{
|
|
// Extract the field name and its value
|
|
std::string field = line.substr(0, pos);
|
|
std::string value = line.substr(pos + 2);
|
|
|
|
// Remove any trailing \r
|
|
if (!value.empty() && (*value.rbegin() == '\r'))
|
|
value.erase(value.size() - 1);
|
|
|
|
// Add the field
|
|
myFields[ToLower(field)] = value;
|
|
}
|
|
}
|
|
|
|
// Finally extract the body
|
|
myBody.clear();
|
|
std::copy(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>(), std::back_inserter(myBody));
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
Http::Http() :
|
|
myHost(),
|
|
myPort(0)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
Http::Http(const std::string& host, unsigned short port)
|
|
{
|
|
SetHost(host, port);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
void Http::SetHost(const std::string& host, unsigned short port)
|
|
{
|
|
// Detect the protocol used
|
|
std::string protocol = ToLower(host.substr(0, 8));
|
|
if (protocol.substr(0, 7) == "http://")
|
|
{
|
|
// HTTP protocol
|
|
myHostName = host.substr(7);
|
|
myPort = (port != 0 ? port : 80);
|
|
}
|
|
else if (protocol == "https://")
|
|
{
|
|
// HTTPS protocol
|
|
myHostName = host.substr(8);
|
|
myPort = (port != 0 ? port : 443);
|
|
}
|
|
else
|
|
{
|
|
// Undefined protocol - use HTTP
|
|
myHostName = host;
|
|
myPort = (port != 0 ? port : 80);
|
|
}
|
|
|
|
// Remove any trailing '/' from the host name
|
|
if (!myHostName.empty() && (*myHostName.rbegin() == '/'))
|
|
myHostName.erase(myHostName.size() - 1);
|
|
|
|
myHost = IpAddress(myHostName);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
Http::Response Http::SendRequest(const Http::Request& request, float timeout)
|
|
{
|
|
// First make sure that the request is valid -- add missing mandatory fields
|
|
Request toSend(request);
|
|
if (!toSend.HasField("From"))
|
|
{
|
|
toSend.SetField("From", "user@sfml-dev.org");
|
|
}
|
|
if (!toSend.HasField("User-Agent"))
|
|
{
|
|
toSend.SetField("User-Agent", "libsfml-network/2.x");
|
|
}
|
|
if (!toSend.HasField("Host"))
|
|
{
|
|
toSend.SetField("Host", myHostName);
|
|
}
|
|
if (!toSend.HasField("Content-Length"))
|
|
{
|
|
std::ostringstream out;
|
|
out << toSend.myBody.size();
|
|
toSend.SetField("Content-Length", out.str());
|
|
}
|
|
if ((toSend.myMethod == Request::Post) && !toSend.HasField("Content-Type"))
|
|
{
|
|
toSend.SetField("Content-Type", "application/x-www-form-urlencoded");
|
|
}
|
|
if ((toSend.myMajorVersion * 10 + toSend.myMinorVersion >= 11) && !toSend.HasField("Connection"))
|
|
{
|
|
toSend.SetField("Connection", "close");
|
|
}
|
|
|
|
// Prepare the response
|
|
Response received;
|
|
|
|
// Connect the socket to the host
|
|
if (myConnection.Connect(myHost, myPort, timeout) == Socket::Done)
|
|
{
|
|
// Convert the request to string and send it through the connected socket
|
|
std::string requestStr = toSend.Prepare();
|
|
|
|
if (!requestStr.empty())
|
|
{
|
|
// Send it through the socket
|
|
if (myConnection.Send(requestStr.c_str(), requestStr.size()) == Socket::Done)
|
|
{
|
|
// Wait for the server's response
|
|
std::string receivedStr;
|
|
std::size_t size = 0;
|
|
char buffer[1024];
|
|
while (myConnection.Receive(buffer, sizeof(buffer), size) == Socket::Done)
|
|
{
|
|
receivedStr.append(buffer, buffer + size);
|
|
}
|
|
|
|
// Build the Response object from the received data
|
|
received.Parse(receivedStr);
|
|
}
|
|
}
|
|
|
|
// Close the connection
|
|
myConnection.Disconnect();
|
|
}
|
|
|
|
return received;
|
|
}
|
|
|
|
} // namespace sf
|