Http.cpp
1
2//
3// SFML - Simple and Fast Multimedia Library
4// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
5//
6// This software is provided 'as-is', without any express or implied warranty.
7// In no event will the authors be held liable for any damages arising from the use of this software.
8//
9// Permission is granted to anyone to use this software for any purpose,
10// including commercial applications, and to alter it and redistribute it freely,
11// subject to the following restrictions:
12//
13// 1. The origin of this software must not be misrepresented;
14// you must not claim that you wrote the original software.
15// If you use this software in a product, an acknowledgment
16// in the product documentation would be appreciated but is not required.
17//
18// 2. Altered source versions must be plainly marked as such,
19// and must not be misrepresented as being the original software.
20//
21// 3. This notice may not be removed or altered from any source distribution.
22//
24
26// Headers
28#include <SFML/Network/Http.hpp>
29#include <ctype.h>
30#include <algorithm>
31#include <iterator>
32#include <sstream>
33
34
35namespace
36{
38 // Convenience function to convert a string to lower case
40 std::string ToLower(const std::string& Str)
41 {
42 std::string Ret = Str;
43 for (std::string::iterator i = Ret.begin(); i != Ret.end(); ++i)
44 *i = static_cast<char>(tolower(*i));
45
46 return Ret;
47 }
48}
49
50
51namespace sf
52{
56Http::Request::Request(Method RequestMethod, const std::string& URI, const std::string& Body)
57{
58 SetMethod(RequestMethod);
59 SetURI(URI);
60 SetHttpVersion(1, 0);
61 SetBody(Body);
62}
63
64
68void Http::Request::SetField(const std::string& Field, const std::string& Value)
69{
70 myFields[ToLower(Field)] = Value;
71}
72
73
79{
80 myMethod = RequestMethod;
81}
82
83
88void Http::Request::SetURI(const std::string& URI)
89{
90 myURI = URI;
91
92 // Make sure it starts with a '/'
93 if (myURI.empty() || (myURI[0] != '/'))
94 myURI.insert(0, "/");
95}
96
97
102void Http::Request::SetHttpVersion(unsigned int Major, unsigned int Minor)
103{
104 myMajorVersion = Major;
105 myMinorVersion = Minor;
106}
107
108
114void Http::Request::SetBody(const std::string& Body)
115{
116 myBody = Body;
117}
118
119
123std::string Http::Request::ToString() const
124{
125 std::ostringstream Out;
126
127 // Convert the method to its string representation
128 std::string RequestMethod;
129 switch (myMethod)
130 {
131 default :
132 case Get : RequestMethod = "GET"; break;
133 case Post : RequestMethod = "POST"; break;
134 case Head : RequestMethod = "HEAD"; break;
135 }
136
137 // Write the first line containing the request type
138 Out << RequestMethod << " " << myURI << " ";
139 Out << "HTTP/" << myMajorVersion << "." << myMinorVersion << "\r\n";
140
141 // Write fields
142 for (FieldTable::const_iterator i = myFields.begin(); i != myFields.end(); ++i)
143 {
144 Out << i->first << ": " << i->second << "\r\n";
145 }
146
147 // Use an extra \r\n to separate the header from the body
148 Out << "\r\n";
149
150 // Add the body
151 Out << myBody;
152
153 return Out.str();
154}
155
156
160bool Http::Request::HasField(const std::string& Field) const
161{
162 return myFields.find(Field) != myFields.end();
163}
164
165
170myStatus (ConnectionFailed),
171myMajorVersion(0),
172myMinorVersion(0)
173{
174
175}
176
177
181const std::string& Http::Response::GetField(const std::string& Field) const
182{
183 FieldTable::const_iterator It = myFields.find(ToLower(Field));
184 if (It != myFields.end())
185 {
186 return It->second;
187 }
188 else
189 {
190 static const std::string Empty = "";
191 return Empty;
192 }
193}
194
195
200{
201 return myStatus;
202}
203
204
209{
210 return myMajorVersion;
211}
212
213
218{
219 return myMinorVersion;
220}
221
222
230const std::string& Http::Response::GetBody() const
231{
232 return myBody;
233}
234
235
239void Http::Response::FromString(const std::string& Data)
240{
241 std::istringstream In(Data);
242
243 // Extract the HTTP version from the first line
244 std::string Version;
245 if (In >> Version)
246 {
247 if ((Version.size() >= 8) && (Version[6] == '.') &&
248 (ToLower(Version.substr(0, 5)) == "http/") &&
249 isdigit(Version[5]) && isdigit(Version[7]))
250 {
251 myMajorVersion = Version[5] - '0';
252 myMinorVersion = Version[7] - '0';
253 }
254 else
255 {
256 // Invalid HTTP version
257 myStatus = InvalidResponse;
258 return;
259 }
260 }
261
262 // Extract the status code from the first line
263 int StatusCode;
264 if (In >> StatusCode)
265 {
266 myStatus = static_cast<Status>(StatusCode);
267 }
268 else
269 {
270 // Invalid status code
271 myStatus = InvalidResponse;
272 return;
273 }
274
275 // Ignore the end of the first line
276 In.ignore(10000, '\n');
277
278 // Parse the other lines, which contain fields, one by one
279 std::string Line;
280 while (std::getline(In, Line) && (Line.size() > 2))
281 {
282 std::string::size_type Pos = Line.find(": ");
283 if (Pos != std::string::npos)
284 {
285 // Extract the field name and its value
286 std::string Field = Line.substr(0, Pos);
287 std::string Value = Line.substr(Pos + 2);
288
289 // Remove any trailing \r
290 if (!Value.empty() && (*Value.rbegin() == '\r'))
291 Value.erase(Value.size() - 1);
292
293 // Add the field
294 myFields[ToLower(Field)] = Value;
295 }
296 }
297
298 // Finally extract the body
299 myBody.clear();
300 std::copy(std::istreambuf_iterator<char>(In), std::istreambuf_iterator<char>(), std::back_inserter(myBody));
301}
302
303
308myHost(),
309myPort(0)
310{
311
312}
313
314
318Http::Http(const std::string& Host, unsigned short Port)
319{
320 SetHost(Host, Port);
321}
322
323
327void Http::SetHost(const std::string& Host, unsigned short Port)
328{
329 // Detect the protocol used
330 std::string Protocol = ToLower(Host.substr(0, 8));
331 if (Protocol.substr(0, 7) == "http://")
332 {
333 // HTTP protocol
334 myHostName = Host.substr(7);
335 myPort = (Port != 0 ? Port : 80);
336 }
337 else if (Protocol == "https://")
338 {
339 // HTTPS protocol
340 myHostName = Host.substr(8);
341 myPort = (Port != 0 ? Port : 443);
342 }
343 else
344 {
345 // Undefined protocol - use HTTP
346 myHostName = Host;
347 myPort = (Port != 0 ? Port : 80);
348 }
349
350 // Remove any trailing '/' from the host name
351 if (!myHostName.empty() && (*myHostName.rbegin() == '/'))
352 myHostName.erase(myHostName.size() - 1);
353
354 myHost = sf::IPAddress(myHostName);
355}
356
357
367{
368 // First make sure the request is valid -- add missing mandatory fields
369 Request ToSend(Req);
370 if (!ToSend.HasField("From"))
371 {
372 ToSend.SetField("From", "user@sfml-dev.org");
373 }
374 if (!ToSend.HasField("User-Agent"))
375 {
376 ToSend.SetField("User-Agent", "libsfml-network/1.x");
377 }
378 if (!ToSend.HasField("Host"))
379 {
380 ToSend.SetField("Host", myHostName);
381 }
382 if (!ToSend.HasField("Content-Length"))
383 {
384 std::ostringstream Out;
385 Out << ToSend.myBody.size();
386 ToSend.SetField("Content-Length", Out.str());
387 }
388 if ((ToSend.myMethod == Request::Post) && !ToSend.HasField("Content-Type"))
389 {
390 ToSend.SetField("Content-Type", "application/x-www-form-urlencoded");
391 }
392 if ((ToSend.myMajorVersion * 10 + ToSend.myMinorVersion >= 11) && !ToSend.HasField("Connection"))
393 {
394 ToSend.SetField("Connection", "close");
395 }
396
397 // Prepare the response
398 Response Received;
399
400 // Connect the socket to the host
401 if (myConnection.Connect(myPort, myHost, Timeout) == Socket::Done)
402 {
403 // Convert the request to string and send it through the connected socket
404 std::string RequestStr = ToSend.ToString();
405
406 if (!RequestStr.empty())
407 {
408 // Send it through the socket
409 if (myConnection.Send(RequestStr.c_str(), RequestStr.size()) == sf::Socket::Done)
410 {
411 // Wait for the server's response
412 std::string ReceivedStr;
413 std::size_t Size = 0;
414 char Buffer[1024];
415 while (myConnection.Receive(Buffer, sizeof(Buffer), Size) == sf::Socket::Done)
416 {
417 ReceivedStr.append(Buffer, Buffer + Size);
418 }
419
420 // Build the Response object from the received data
421 Received.FromString(ReceivedStr);
422 }
423 }
424
425 // Close the connection
426 myConnection.Close();
427 }
428
429 return Received;
430}
431
432} // namespace sf
This class wraps an HTTP request, which is basically :
Definition Http.hpp:55
void SetField(const std::string &Field, const std::string &Value)
Set the value of a field; the field is added if it doesn't exist.
Definition Http.cpp:68
void SetBody(const std::string &Body)
Set the body of the request.
Definition Http.cpp:114
void SetMethod(Method RequestMethod)
Set the request method.
Definition Http.cpp:78
void SetURI(const std::string &URI)
Set the target URI of the request.
Definition Http.cpp:88
Method
Enumerate the available HTTP methods for a request.
Definition Http.hpp:62
@ Post
Request in post mode, usually to send data to a page.
Definition Http.hpp:64
Request(Method RequestMethod=Get, const std::string &URI="/", const std::string &Body="")
Default constructor.
Definition Http.cpp:56
void SetHttpVersion(unsigned int Major, unsigned int Minor)
Set the HTTP version of the request.
Definition Http.cpp:102
This class wraps an HTTP response, which is basically :
Definition Http.hpp:169
Response()
Default constructor.
Definition Http.cpp:169
const std::string & GetField(const std::string &Field) const
Get the value of a field.
Definition Http.cpp:181
Status GetStatus() const
Get the header's status code.
Definition Http.cpp:199
Status
Enumerate all the valid status codes returned in a HTTP response.
Definition Http.hpp:177
@ ConnectionFailed
Connection with server failed.
Definition Http.hpp:204
const std::string & GetBody() const
Get the body of the response.
Definition Http.cpp:230
unsigned int GetMinorHttpVersion() const
Get the major HTTP version number of the response.
Definition Http.cpp:217
unsigned int GetMajorHttpVersion() const
Get the major HTTP version number of the response.
Definition Http.cpp:208
void SetHost(const std::string &Host, unsigned short Port=0)
Set the target host.
Definition Http.cpp:327
Response SendRequest(const Request &Req, float Timeout=0.f)
Send a HTTP request and return the server's response.
Definition Http.cpp:366
Http()
Default constructor.
Definition Http.cpp:307
IPAddress provides easy manipulation of IP v4 addresses.
Definition IPAddress.hpp:43