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