1 /*
2 DSFML - The Simple and Fast Multimedia Library for D
3 
4 Copyright (c) 2013 - 2015 Jeremy DeHaan (dehaan.jeremiah@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, including commercial applications,
10 and to alter it and redistribute it freely, subject to the following restrictions:
11 
12 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
13 If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
14 
15 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
16 
17 3. This notice may not be removed or altered from any source distribution
18 */
19 
20 ///A module containing the Http class.
21 module dsfml.network.http;
22 
23 import core.time;
24 
25 /**
26  *A HTTP client.
27  *
28  *sf::Http is a very simple HTTP client that allows you to communicate with a web server.
29  *
30  *You can retrieve web pages, send data to an interactive resource, download a remote file, etc.
31  */
32 class Http
33 {
34 	package sfHttp* sfPtr;
35 	
36 	///Default constructor
37 	this()
38 	{
39 		sfPtr = sfHttp_create();
40 	}
41 
42 	///Construct the HTTP client with the target host.
43 	///
44 	///This is equivalent to calling setHost(host, port). The port has a default value of 0, which means that the HTTP client will use the right port according to the protocol used (80 for HTTP, 443 for HTTPS). You should leave it like this unless you really need a port other than the standard one, or use an unknown protocol.
45 	///
46 	///Params:
47     ///		host = Web server to connect to.
48     ///		port = Port to use for connection. 
49 	this(string host, ushort port = 0)
50 	{
51 		import dsfml.system.string;
52 		sfPtr = sfHttp_create();
53 		sfHttp_setHost(sfPtr, toStringz(host),port);
54 	}
55 	
56 	///Destructor
57 	~this()
58 	{
59 		import dsfml.system.config;
60 		mixin(destructorOutput);
61 		sfHttp_destroy(sfPtr);
62 	}
63 	
64 	///Set the target host.
65 	///
66 	///This function just stores the host address and port, it doesn't actually connect to it until you send a request. The port has a default value of 0, which means that the HTTP client will use the right port according to the protocol used (80 for HTTP, 443 for HTTPS). You should leave it like this unless you really need a port other than the standard one, or use an unknown protocol.
67 	///
68 	///Params:
69     ///		host = Web server to connect to.
70     ///		port = Port to use for connection. 
71 	void setHost(string host, ushort port = 0)
72 	{
73 		import dsfml.system.string;
74 		sfHttp_setHost(sfPtr, toStringz(host),port);
75 	}
76 
77 	///Send a HTTP request and return the server's response.
78 	///
79 	///You must have a valid host before sending a request (see setHost). Any missing mandatory header field in the request will be added with an appropriate value. Warning: this function waits for the server's response and may not return instantly; use a thread if you don't want to block your application, or use a timeout to limit the time to wait. A value of Time::Zero means that the client will use the system defaut timeout (which is usually pretty long).
80 	///
81 	///Params:
82     ///		request = Request to send.
83     ///		timeout = Maximum time to wait.
84 	Response sendRequest(Request request, Duration timeout = Duration.zero())
85 	{
86 		return new Response(sfHttp_sendRequest(sfPtr,request.sfPtrRequest,timeout.total!"usecs"));
87 	}
88 	
89 	///Define a HTTP request. 
90 	static class Request
91 	{
92 		///Enumerate the available HTTP methods for a request. 
93 		enum Method
94 		{
95 			///Request in get mode, standard method to retrieve a page. 
96 			Get,
97 			///Request in post mode, usually to send data to a page.
98 			Post,
99 			///Request a page's header only. 
100 			Head
101 		}
102 
103 		package sfHttpRequest* sfPtrRequest;
104 
105 		///This constructor creates a GET request, with the root URI ("/") and an empty body.
106 		///
107 		///Params:
108     	///		uri = 	Target URI.
109     	///		method = Method to use for the request.
110     	///		body = Content of the request's body.
111 		this(string uri = "/", Method method = Method.Get, string requestBody = "")
112 		{
113 			import dsfml.system.string;
114 			sfPtrRequest = sfHttpRequest_create();
115 			sfHttpRequest_setUri(sfPtrRequest, toStringz(uri));
116 			sfHttpRequest_setMethod(sfPtrRequest, method);
117 			sfHttpRequest_setBody(sfPtrRequest,toStringz(requestBody));
118 		}
119 		
120 		///Destructor
121 		~this()
122 		{
123 			import std.stdio;
124 			writeln("Destroying HTTP Request");
125 			sfHttpRequest_destroy(sfPtrRequest);
126 		}
127 
128 		///Set the body of the request.
129 		///
130 		///The body of a request is optional and only makes sense for POST requests. It is ignored for all other methods. The body is empty by default.
131 		///
132 		///Params:
133     	///		body = Content of the body.
134 		void setBody(string requestBody)
135 		{
136 			import dsfml.system.string;
137 			sfHttpRequest_setBody(sfPtrRequest,toStringz(requestBody));
138 		}
139 
140 		///Set the value of a field.
141 		///
142 		///The field is created if it doesn't exist. The name of the field is case insensitive. By default, a request doesn't contain any field (but the mandatory fields are added later by the HTTP client when sending the request).
143 		///
144 		///Params:
145     	///		field = Name of the field to set.
146     	///		value = Value of the field.
147 		void setField(string feild, string value)
148 		{
149 			import dsfml.system.string;
150 			sfHttpRequest_setField(sfPtrRequest,toStringz(feild),toStringz(value));
151 		}
152 
153 		///Set the HTTP version for the request.
154 		///
155 		///The HTTP version is 1.0 by default.
156 		///
157 		///Parameters
158     	///		major = Major HTTP version number.
159     	///		minor = Minor HTTP version number.
160 		void setHttpVersion(uint major, uint minor)
161 		{
162 			sfHttpRequest_setHttpVersion(sfPtrRequest,major, minor);
163 		}
164 
165 		///Set the request method.
166 		///
167 		///See the Method enumeration for a complete list of all the availale methods. The method is Http::Request::Get by default.
168 		///
169 		///Params
170     	///		method = Method to use for the request.
171 		void setMethod(Method method)
172 		{
173 			sfHttpRequest_setMethod(sfPtrRequest,method);
174 		}
175 		
176 		///Set the requested URI.
177 		///
178 		///The URI is the resource (usually a web page or a file) that you want to get or post. The URI is "/" (the root page) by default.
179 		///
180 		///Params
181     	///		uri = URI to request, relative to the host.
182 		void setUri(string uri)
183 		{
184 			import dsfml.system.string;
185 			sfHttpRequest_setUri(sfPtrRequest,toStringz(uri));
186 		}
187 	}
188 	
189 	///Define a HTTP response.
190 	class Response
191 	{
192 		///Enumerate all the valid status codes for a response.
193 		enum Status
194 		{
195 			Ok = 200,
196 			Created = 201,
197 			Accepted = 202,
198 			NoContent = 204,
199 			ResetContent = 205,
200 			PartialContent = 206,
201 
202 			MultipleChoices = 300,
203 			MovedPermanently = 301,
204 			MovedTemporarily = 302,
205 			NotModified = 304,
206 
207 			BadRequest = 400,
208 			Unauthorized = 401,
209 			Forbidden = 403,
210 			NotFound = 404,
211 			RangeNotSatisfiable = 407,
212 
213 			InternalServerError = 500,
214 			NotImplemented = 501,
215 			BadGateway = 502,
216 			ServiceNotAvailable = 503,
217 			GatewayTimeout = 504,
218 			VersionNotSupported = 505,
219 
220 			InvalidResponse = 1000,
221 			ConnectionFailed = 1001
222 			
223 		}
224 
225 		package sfHttpResponse* sfPtrResponse;
226 
227 		//Internally used constructor
228 		package this(sfHttpResponse* response)
229 		{
230 			sfPtrResponse = response;
231 		}
232 
233 		///Get the body of the response.
234 		///
235 		///Returns: The response body.
236 		string getBody()
237 		{
238 			import dsfml.system.string;
239 			return dsfml.system..string.toString(sfHttpResponse_getBody(sfPtrResponse));
240 		}
241 
242 		///Get the value of a field.
243 		///
244 		///If the field field is not found in the response header, the empty string is returned. This function uses case-insensitive comparisons.
245 		///
246 		///Params:
247     	///		field = Name of the field to get.
248     	///
249 		///Returns: Value of the field, or empty string if not found.
250 		string getField(string field)
251 		{
252 			import dsfml.system.string;
253 			return dsfml.system..string.toString(sfHttpResponse_getField(sfPtrResponse,toStringz(field)));
254 		}
255 
256 		///Get the major HTTP version number of the response.
257 		///
258 		///Returns: Major HTTP version number.
259 		uint getMajorHttpVersion()
260 		{
261 			return sfHttpResponse_getMajorVersion(sfPtrResponse);
262 		}
263 		
264 		///Get the minor HTTP version number of the response. 
265 		///
266 		///Returns: Minor HTTP version number.
267 		uint getMinorHttpVersion()
268 		{
269 			return sfHttpResponse_getMinorVersion(sfPtrResponse);
270 		}
271 		
272 		///Get the response status code.
273 		///
274 		///The status code should be the first thing to be checked after receiving a response, it defines whether it is a success, a failure or anything else (see the Status enumeration).
275 		///
276 		///Returns: Status code of the response.
277 		Status getStatus()
278 		{
279 			return sfHttpResponse_getStatus(sfPtrResponse);
280 		}
281 	}
282 }
283 
284 unittest
285 {
286 	version(DSFML_Unittest_Network)
287 	{
288 		import std.stdio;
289 		
290 		writeln("Unittest for Http");
291 
292 		auto http = new Http();
293 
294 		http.setHost("http://www.sfml-dev.org");
295 		
296 		// Prepare a request to get the 'features.php' page
297 		auto request = new Http.Request("learn.php");
298 
299 		// Send the request
300 		auto response = http.sendRequest(request);
301 
302 		// Check the status code and display the result
303 		auto status = response.getStatus();
304 
305 		if (status == Http.Response.Status.Ok)
306 		{
307 			writeln("Found the site!");
308 		}
309 		else
310 		{
311 			writeln("Error: ", status);
312 		}   
313 		writeln();
314 	}
315 }
316 
317 private extern(C):
318 
319 struct sfHttpRequest;
320 struct sfHttpResponse;
321 struct sfHttp;
322 
323 ///Create a new HTTP request
324 sfHttpRequest* sfHttpRequest_create();
325 	
326 	
327 ///Destroy a HTTP request
328 void sfHttpRequest_destroy(sfHttpRequest* httpRequest);
329 	
330 	
331 ///Set the value of a header field of a HTTP request
332 void sfHttpRequest_setField(sfHttpRequest* httpRequest, const(char)* field, const(char)* value);
333 	
334 	
335 ///Set a HTTP request method
336 void sfHttpRequest_setMethod(sfHttpRequest* httpRequest, int method);
337 	
338 	
339 ///Set a HTTP request URI
340 void sfHttpRequest_setUri(sfHttpRequest* httpRequest, const(char)* uri);
341 	
342 	
343 ///Set the HTTP version of a HTTP request
344 void sfHttpRequest_setHttpVersion(sfHttpRequest* httpRequest,uint major, uint minor);
345 	
346 	
347 ///Set the body of a HTTP request
348 void sfHttpRequest_setBody(sfHttpRequest* httpRequest, const(char)* ody);
349 	
350 	
351 //HTTP Response Functions
352 	
353 ///Destroy a HTTP response
354 void sfHttpResponse_destroy(sfHttpResponse* httpResponse);
355 	
356 	
357 ///Get the value of a field of a HTTP response
358 const(char)* sfHttpResponse_getField(const sfHttpResponse* httpResponse, const(char)* field);
359 	
360 	
361 ///Get the status code of a HTTP reponse
362 Http.Response.Status sfHttpResponse_getStatus(const sfHttpResponse* httpResponse);
363 	
364 	
365 ///Get the major HTTP version number of a HTTP response
366 uint sfHttpResponse_getMajorVersion(const sfHttpResponse* httpResponse);
367 	
368 	
369 ///Get the minor HTTP version number of a HTTP response
370 uint sfHttpResponse_getMinorVersion(const sfHttpResponse* httpResponse);
371 	
372 	
373 ///Get the body of a HTTP response
374 const(char)* sfHttpResponse_getBody(const sfHttpResponse* httpResponse);
375 	
376 	
377 //HTTP Functions
378 	
379 ///Create a new Http object
380 sfHttp* sfHttp_create();
381 	
382 	
383 ///Destroy a Http object
384 void sfHttp_destroy(sfHttp* http);
385 	
386 	
387 ///Set the target host of a HTTP object
388 void sfHttp_setHost(sfHttp* http, const(char)* host, ushort port);
389 	
390 	
391 ///Send a HTTP request and return the server's response.
392 sfHttpResponse* sfHttp_sendRequest(sfHttp* http, const(sfHttpRequest)* request, long timeout);