1 //-----------------------------------------------------------------------------
2 //
3 //	HttpClient.h
4 //
5 //	Cross-platform HttpClient
6 //
7 //  Originally based upon minihttp client
8 //	Copyright (c) 2016 Justin Hammond <Justin@dynam.ac>
9 //
10 //	SOFTWARE NOTICE AND LICENSE
11 //
12 //	This file is part of OpenZWave.
13 //
14 //	OpenZWave is free software: you can redistribute it and/or modify
15 //	it under the terms of the GNU Lesser General Public License as published
16 //	by the Free Software Foundation, either version 3 of the License,
17 //	or (at your option) any later version.
18 //
19 //	OpenZWave is distributed in the hope that it will be useful,
20 //	but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //	GNU Lesser General Public License for more details.
23 //
24 //	You should have received a copy of the GNU Lesser General Public License
25 //	along with OpenZWave.  If not, see <http://www.gnu.org/licenses/>.
26 //
27 //-----------------------------------------------------------------------------
28 
29 // Original License Text:
30 /* This program is free software. It comes without any warranty, to
31  * the extent permitted by applicable law. You can redistribute it
32  * and/or modify it under the terms of the Do What The Fuck You Want
33  * To Public License, Version 2, as published by Sam Hocevar.
34  * See http://sam.zoy.org/wtfpl/COPYING for more details. */
35 
36 #ifndef MINIHTTPSOCKET_H
37 #define MINIHTTPSOCKET_H
38 
39 // ---- Compile config -----
40 #define MINIHTTP_SUPPORT_HTTP
41 #define MINIHTTP_SUPPORT_SOCKET_SET
42 // -------------------------
43 
44 // Intentionally avoid pulling in any other headers
45 
46 #include <string>
47 #include <map>
48 #include <queue>
49 
50 namespace OpenZWave
51 {
52 	/** \defgroup SimpleHttpClient Basic HTTP(s) Client
53 	 *
54 	 * a Basic HTTP Client for talking to webservers
55 	 */
56 
57 	namespace Internal
58 	{
59 		namespace Platform
60 		{
61 			/** \brief Initialize the Network for HTTP requests
62 			 * \ingroup SimpleHttpClient
63 			 *
64 			 * Initializes the Network for HTTP requests
65 			 *
66 			 * \returns success/failure
67 			 */
68 			bool InitNetwork();
69 
70 			/** \brief Stop the Network for HTTP requests
71 			 * \ingroup SimpleHttpClient
72 			 *
73 			 * Stops the Network for HTTP requests and releases resources associated with it
74 			 */
75 			void StopNetwork();
76 
77 			/** \brief Indicates if we support HTTPS requests
78 			 * \ingroup SimpleHttpClient
79 			 *
80 			 * Indicates if we support HTTPS requests
81 			 *
82 			 * \returns success/failure
83 			 * \todo SSL is not actually implemented yet.
84 			 */
85 			bool HasSSL();
86 
87 			/** \brief Split a URL into its different parts/ports etc
88 			 * \ingroup SimpleHttpClient
89 			 *
90 			 * Split a URL Into the different parts/ports
91 			 *
92 			 * \param uri the URL to parse
93 			 * \param host the Hostname of the URL
94 			 * \param file the directory/file name of the URL
95 			 * \param port the port number of the URL, or 80 if not specified
96 			 *
97 			 * \returns success/failure
98 			 */
99 			bool SplitURI(const std::string& uri, std::string& host, std::string& file, int& port);
100 
101 			/** \brief Encode a String suitable for sending as a URL request (eg Get)
102 			 * \ingroup SimpleHttpClient
103 			 *
104 			 * Encode a String so it can be sent as part of a URL request
105 			 *
106 			 * \param s the string to encode
107 			 * \param enc the encoded version of the string that is returned
108 			 *
109 			 */
110 			void URLEncode(const std::string& s, std::string& enc);
111 
112 			/** \brief Result Codes for SSL operations
113 			 * \ingroup SimpleHttpClient
114 			 *
115 			 */
116 			enum SSLResult
117 			{
118 				SSLR_OK = 0x0, /**< No Error */
119 				SSLR_NO_SSL = 0x1, /**< SSL Is not required */
120 				SSLR_FAIL = 0x2, /**< Internal SSL Engine Failure */
121 				SSLR_CERT_EXPIRED = 0x4, /**< SSL Certificate has expired */
122 				SSLR_CERT_REVOKED = 0x8, /**< SSL Certificate is revoked */
123 				SSLR_CERT_CN_MISMATCH = 0x10, /**< SSL CN Name does not match Hostname */
124 				SSLR_CERT_NOT_TRUSTED = 0x20, /**< SSL Certificate is not trusted */
125 				SSLR_CERT_MISSING = 0x40, /**< SSL Certificate is missing */
126 				SSLR_CERT_SKIP_VERIFY = 0x80, /**< SSL Certificate Verification is disabled */
127 				SSLR_CERT_FUTURE = 0x100, /**< SSL Certificate is valid in the future */
128 
129 				_SSLR_FORCE32BIT = 0x7fffffff
130 			};
131 
132 			/** \brief a TCP Socket that can optionally be protected via SSL
133 			 * \ingroup SimpleHttpClient
134 			 *
135 			 * This represents a TCP Socket that can be encrypted via SSL and
136 			 * is used to connect to a TCP Server (in this case, a HTTP(s) Server
137 			 */
138 			class TcpSocket
139 			{
140 				public:
141 					TcpSocket();
142 					virtual ~TcpSocket();
143 
HasPendingTask()144 					virtual bool HasPendingTask() const
145 					{
146 						return false;
147 					}
148 
149 					bool open(const char *addr = NULL, unsigned int port = 0);
150 					void close();
151 					bool update(); // returns true if something interesting happened (incoming data, closed connection, etc)
152 
153 					bool isOpen(void);
154 
155 					void SetBufsizeIn(unsigned int s);
156 					bool SetNonBlocking(bool nonblock);
GetBufSize()157 					unsigned int GetBufSize()
158 					{
159 						return _inbufSize;
160 					}
GetHost(void)161 					const char *GetHost(void)
162 					{
163 						return _host.c_str();
164 					}
165 					bool SendBytes(const void *buf, unsigned int len);
166 
167 					// SSL related
168 					bool initSSL(const char *certs);
hasSSL()169 					bool hasSSL() const
170 					{
171 						return !!_sslctx;
172 					}
173 					void shutdownSSL();
174 					SSLResult verifySSL();
175 
176 				protected:
177 					virtual void _OnCloseInternal();
178 					virtual void _OnData(); // data received callback. Internal, should only be overloaded to call _OnRecv()
179 
180 					virtual void _OnRecv(void *buf, unsigned int size) = 0;
_OnClose()181 					virtual void _OnClose()
182 					{
183 					}
184 					; // close callback
_OnOpen()185 					virtual void _OnOpen()
186 					{
187 					} // called when opened
_OnUpdate()188 					virtual bool _OnUpdate()
189 					{
190 						return true;
191 					} // called before reading from the socket
192 
193 					void _ShiftBuffer();
194 
195 					char *_inbuf;
196 					char *_readptr; // part of inbuf, optionally skipped header
197 					char *_writeptr; // passed to recv(). usually equal to _inbuf, but may point inside the buffer in case of a partial transfer.
198 
199 					unsigned int _inbufSize; // size of internal buffer
200 					unsigned int _writeSize; // how many bytes can be written to _writeptr;
201 					unsigned int _recvSize; // incoming size, max _inbufSize - 1
202 
203 					unsigned int _lastport; // port used in last open() call
204 
205 					bool _nonblocking; // Default true. If false, the current thread is blocked while waiting for input.
206 
207 #ifdef _WIN32
208 					intptr_t _s; // socket handle. really an int, but to be sure its 64 bit compatible as it seems required on windows, we use this.
209 #else
210 					long _s;
211 #endif
212 
213 					std::string _host;
214 
215 				private:
216 					int _writeBytes(const unsigned char *buf, size_t len);
217 					int _readBytes(unsigned char *buf, size_t maxlen);
218 					void *_sslctx;
219 			};
220 
221 #ifdef MINIHTTP_SUPPORT_HTTP
222 
223 			enum HttpCode
224 			{
225 				HTTP_OK = 200,
226 				HTTP_NOTFOUND = 404,
227 			};
228 
229 			/** \brief This class is used for Posting data to a HTTP(s) server
230 			 * \ingroup SimpleHttpClient
231 			 *
232 			 * Post some data to a HTTP(s) server
233 			 *
234 			 */
235 			class POST
236 			{
237 				public:
reserve(size_t res)238 					void reserve(size_t res)
239 					{
240 						data.reserve(res);
241 					}
242 					POST& add(const char *key, const char *value);
c_str()243 					const char *c_str() const
244 					{
245 						return data.c_str();
246 					}
str()247 					const std::string& str() const
248 					{
249 						return data;
250 					}
empty()251 					bool empty() const
252 					{
253 						return data.empty();
254 					}
length()255 					size_t length() const
256 					{
257 						return data.length();
258 					}
259 				private:
260 					std::string data;
261 			};
262 
263 			/** \brief Main class for making a HTTP request to a HTTP(s) server
264 			 * \ingroup SimpleHttpClient
265 			 *
266 			 * Make a request to a HTTP Server
267 			 *
268 			 */
269 			struct Request
270 			{
RequestRequest271 					Request() :
272 							port(80), user(NULL)
273 					{
274 					}
275 					Request(const std::string& h, const std::string& res, int p = 80, void *u = NULL) :
hostRequest276 							host(h), resource(res), port(80), user(u), useSSL(false)
277 					{
278 					}
279 
280 					std::string protocol;
281 					std::string host;
282 					std::string header; // set by socket
283 					std::string resource;
284 					std::string extraGetHeaders;
285 					int port;
286 					void *user;
287 					bool useSSL;
288 					POST post; // if this is empty, it's a GET request, otherwise a POST request
289 			};
290 
291 			/** \brief a Socket that speaks HTTP protocol.
292 			 * \ingroup SimpleHttpClient
293 			 *
294 			 * Talk to a HTTP(s) server
295 			 *
296 			 */
297 			class HttpSocket: public OpenZWave::Internal::Platform::TcpSocket
298 			{
299 				public:
300 
301 					HttpSocket();
302 					virtual ~HttpSocket();
303 
HasPendingTask()304 					virtual bool HasPendingTask() const
305 					{
306 						return ExpectMoreData() || _requestQ.size();
307 					}
308 
SetKeepAlive(unsigned int secs)309 					void SetKeepAlive(unsigned int secs)
310 					{
311 						_keep_alive = secs;
312 					}
SetUserAgent(const std::string & s)313 					void SetUserAgent(const std::string &s)
314 					{
315 						_user_agent = s;
316 					}
SetAcceptEncoding(const std::string & s)317 					void SetAcceptEncoding(const std::string& s)
318 					{
319 						_accept_encoding = s;
320 					}
SetFollowRedirect(bool follow)321 					void SetFollowRedirect(bool follow)
322 					{
323 						_followRedir = follow;
324 					}
SetAlwaysHandle(bool h)325 					void SetAlwaysHandle(bool h)
326 					{
327 						_alwaysHandle = h;
328 					}
SetDownloadFile(std::string filename)329 					void SetDownloadFile(std::string filename)
330 					{
331 						_filename = filename;
332 					}
333 
334 					bool Download(const std::string& url, const char *extraRequest = NULL, void *user = NULL, const POST *post = NULL);
335 					bool SendRequest(Request& what, bool enqueue);
336 					bool SendRequest(const std::string what, const char *extraRequest = NULL, void *user = NULL);
337 					bool QueueRequest(const std::string what, const char *extraRequest = NULL, void *user = NULL);
338 
GetRemaining()339 					unsigned int GetRemaining() const
340 					{
341 						return _remaining;
342 					}
343 
GetStatusCode()344 					unsigned int GetStatusCode() const
345 					{
346 						return _status;
347 					}
GetContentLen()348 					unsigned int GetContentLen() const
349 					{
350 						return _contentLen;
351 					}
ChunkedTransfer()352 					bool ChunkedTransfer() const
353 					{
354 						return _chunkedTransfer;
355 					}
ExpectMoreData()356 					bool ExpectMoreData() const
357 					{
358 						return _remaining || _chunkedTransfer;
359 					}
360 
GetCurrentRequest()361 					const Request &GetCurrentRequest() const
362 					{
363 						return _curRequest;
364 					}
365 					const char *Hdr(const char *h) const;
366 
367 					bool IsRedirecting() const;
368 					bool IsSuccess() const;
369 
370 				protected:
371 					virtual void _OnCloseInternal();
372 					virtual void _OnClose();
373 					virtual void _OnData(); // data received callback. Internal, should only be overloaded to call _OnRecv()
374 					virtual void _OnRecv(void *buf, unsigned int size);
375 					virtual void _OnOpen(); // called when opene
376 					virtual bool _OnUpdate(); // called before reading from the socket
377 
378 					// new ones:
_OnRequestDone()379 					virtual void _OnRequestDone()
380 					{
381 					}
382 
383 					bool _Redirect(std::string loc, bool forceGET);
384 
385 					void _ProcessChunk();
386 					bool _EnqueueOrSend(const Request& req, bool forceQueue = false);
387 					void _DequeueMore();
388 					bool _OpenRequest(const Request& req);
389 					void _ParseHeader();
390 					void _ParseHeaderFields(const char *s, size_t size);
391 					bool _HandleStatus(); // Returns whether the processed request was successful, or not
392 					void _FinishRequest();
393 					void _OnRecvInternal(void *buf, unsigned int size);
394 
395 					std::string _user_agent;
396 					std::string _accept_encoding; // Default empty.
397 					std::string _tmpHdr; // used to save the http header if the incoming buffer was not large enough
398 
399 					unsigned int _keep_alive; // http related
400 					unsigned int _remaining; // http "Content-Length: X" - already recvd. 0 if ready for next packet.
401 											 // For chunked transfer encoding, this holds the remaining size of the current chunk
402 					unsigned int _contentLen; // as reported by server
403 					unsigned int _status; // http status code, HTTP_OK if things are good
404 
405 					std::queue<Request> _requestQ;
406 					std::map<std::string, std::string> _hdrs; // Maps HTTP header fields to their values
407 
408 					Request _curRequest;
409 
410 					bool _inProgress;
411 					bool _chunkedTransfer;
412 					bool _mustClose; // keep-alive specified, or not
413 					bool _followRedir; // Default true. Follow 3xx redirects if this is set.
414 					bool _alwaysHandle; // Also deliver to _OnRecv() if a non-success code was received.
415 					std::string _filename;
416 					FILE* _pFile;
417 			};
418 
419 #endif
420 
421 // ------------------------------------------------------------------------
422 
423 #ifdef MINIHTTP_SUPPORT_SOCKET_SET
424 
425 			/** \brief Support Multiple TCP Socket connections
426 			 * \ingroup SimpleHttpClient
427 			 *
428 			 * to Support multiple TCP Socket Connections
429 			 *
430 			 */
431 			class SocketSet
432 			{
433 				public:
434 					virtual ~SocketSet();
435 					void deleteAll();
436 					bool update();
437 					void add(TcpSocket *s, bool deleteWhenDone = true);
438 					bool has(TcpSocket *s);
439 					void remove(TcpSocket *s);
size()440 					inline size_t size()
441 					{
442 						return _store.size();
443 					}
444 
445 //protected:
446 
447 					struct SocketSetData
448 					{
449 							bool deleteWhenDone;
450 							// To be extended
451 					};
452 
453 					typedef std::map<TcpSocket*, SocketSetData> Store;
454 
455 					Store _store;
456 			};
457 
458 #endif
459 		} // namespace Platform
460 	} // namespace Internal
461 } // namespace OpenZWave
462 
463 #endif
464