/* * This file is a part of Winix * and is distributed under the 2-Clause BSD licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2010-2022, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #ifndef headerfile_winix_utils_http #define headerfile_winix_utils_http #include #include #include "core/synchro.h" #include "textstream/textstream.h" #include "core/winixbase.h" namespace Winix { class Http : public WinixBase { public: static const long HTTP_STATUS_200_OK = 200; static const long HTTP_STATUS_301_Moved_Permanently = 301; static const long HTTP_STATUS_302_Found = 302; enum Method { method_get, method_post, method_put, method_patch, method_delete, method_options, }; Http(); ~Http(); Http(const Http &) = delete; Http(Http &&) = delete; /* * start a new request * called automatically from cctor * you have to call it manually to make another requests * */ Http & begin(); /* * add one header (should not end with \r\n or \n ) * */ Http & add_header(const pt::TextStream & header); Http & add_header(const pt::WTextStream & header); Http & add_header(const std::string & header); Http & add_header(const std::wstring & header); Http & add_header(const char * str, size_t len); Http & add_header(const wchar_t * str, size_t len); /* * additional headers (we do not copy it too) * headers can be separated with \r\n or just with \n * last \n (or \r\n) can be omitted * empty lines will be ignored * * headers from wchar_t* will be replaced to char* by using pt::wide_to_utf8 */ Http & add_headers(const char * headers); Http & add_headers(const wchar_t * headers); Http & add_headers(const std::string & headers); Http & add_headers(const std::wstring & headers); /* * we do not copy the space structure but only get a pointer to it * so you have to preserve the structure until get()/put() is called */ Http & add_headers(pt::Space * headers); Http & add_headers(pt::Space & headers); /* * output headers will be provided in a Space structure as key/value pairs (object) * * the first line (http status) will be inserted too (the value part will be empty), e.g. * key="http/1.1 200 ok" * value="" * */ Http & get_output_headers_to(pt::Space * out_headers, bool change_names_to_lower = true); /* * if we don't need all headers but just content-type */ Http & get_output_content_type_to(std::wstring * out_content_type); /* * you don't have to call these methods * if the curl is not initialized it will be initialized automatically from get/put methods */ void initialize_curl_if_needed(); void uninitialize_curl(); /* * if true set additional information how the curl library works when connection is made * debug_info will be a table in such a case * * if debug is false then debug_info can be nullptr (if not null then will be set to Space null) * */ void use_debug_mode(bool debug, pt::Space * debug_info); /* * we do not copy the string but only get a pointer to its c_str() * so you have to preserve the string until get()/put() is called */ Http & add_bearer_token(const wchar_t * token); Http & add_bearer_token(const std::wstring & token); /* * if allow_redirects is true then we follow any Location http headers * default: true */ void allow_redirects(bool allow_redirects); /* * set ssl version to use, values for CURLOPT_SSLVERSION * https://curl.se/libcurl/c/CURLOPT_SSLVERSION.html * * CURL_SSLVERSION_DEFAULT * The default acceptable version range. The minimum acceptable version is by default TLS v1.0 since 7.39.0 (unless the TLS library has a stricter rule). * * CURL_SSLVERSION_TLSv1 * TLS v1.0 or later * * CURL_SSLVERSION_SSLv2 * SSL v2 - refused * * CURL_SSLVERSION_SSLv3 * SSL v3 - refused * * CURL_SSLVERSION_TLSv1_0 * TLS v1.0 or later (Added in 7.34.0) * * CURL_SSLVERSION_TLSv1_1 * TLS v1.1 or later (Added in 7.34.0) * * CURL_SSLVERSION_TLSv1_2 * TLS v1.2 or later (Added in 7.34.0) * * CURL_SSLVERSION_TLSv1_3 * TLS v1.3 or later (Added in 7.52.0) * * The maximum TLS version can be set by using one of the CURL_SSLVERSION_MAX_ macros below. * It is also possible to OR one of the CURL_SSLVERSION_ macros with one of the CURL_SSLVERSION_MAX_ macros. * The MAX macros are not supported for WolfSSL. * CURL_SSLVERSION_MAX_DEFAULT * * The flag defines the maximum supported TLS version by libcurl, or the default value from the SSL library is used. * libcurl will use a sensible default maximum, which was TLS v1.2 up to before 7.61.0 and is TLS v1.3 since * then - assuming the TLS library support it. (Added in 7.54.0) * CURL_SSLVERSION_MAX_TLSv1_0 * * The flag defines maximum supported TLS version as TLS v1.0. (Added in 7.54.0) * CURL_SSLVERSION_MAX_TLSv1_1 * * The flag defines maximum supported TLS version as TLS v1.1. (Added in 7.54.0) * CURL_SSLVERSION_MAX_TLSv1_2 * * The flag defines maximum supported TLS version as TLS v1.2. (Added in 7.54.0) * CURL_SSLVERSION_MAX_TLSv1_3 * * The flag defines maximum supported TLS version as TLS v1.3. (Added in 7.54.0) * In versions of curl prior to 7.54 the CURL_SSLVERSION_TLS options were documented to allow only the specified * TLS version, but behavior was inconsistent depending on the TLS library. */ void set_ssl_version(long ssl_version); /* * verify the peer's SSL certificate * default is true */ void verify_ssl(bool verify); /* * a path to the CA to verify the peer * used with CURLOPT_CAINFO * https://curl.se/libcurl/c/CURLOPT_CAINFO.html * * we do not copy the string but only get a pointer to its c_str() */ void set_ca_file(const wchar_t * path); void set_ca_file(const std::wstring & path); /* * set a client certificate and a private key * * used with: * https://curl.se/libcurl/c/CURLOPT_SSLCERT.html * https://curl.se/libcurl/c/CURLOPT_SSLKEY.html * * we do not copy the string but only get a pointer to its c_str() */ void set_client_cert(const wchar_t * client_cert, const wchar_t * client_key); void set_client_cert(const std::wstring & client_cert, const std::wstring & client_key); /* * in can be a null pointer * in such a case a body payload is not sent */ bool fetch(Method method, const wchar_t * url, const std::string * in, pt::WTextStream & out, bool clear_stream = true); bool fetch(Method method, const std::wstring & url, const std::string * in, pt::WTextStream & out, bool clear_stream = true); bool fetch(Method method, const wchar_t * url, pt::WTextStream * in, pt::WTextStream & out, bool clear_stream = true); bool get(const wchar_t * url, std::wstring & out, bool clear_str = true); bool get(const std::wstring & url, std::wstring & out, bool clear_str = true); bool get(const pt::WTextStream & url, std::wstring & out, bool clear_str = true); bool get(const wchar_t * url, pt::WTextStream & out, bool clear_stream = true); bool get(const std::wstring & url, pt::WTextStream & out, bool clear_stream = true); bool get(const pt::WTextStream & url, pt::WTextStream & out, bool clear_stream = true); bool post(const wchar_t * url, const std::string & in, pt::WTextStream & out, bool clear_stream = true); bool post(const std::wstring & url, const std::string & in, pt::WTextStream & out, bool clear_stream = true); bool post(const wchar_t * url, pt::WTextStream & in, pt::WTextStream & out, bool clear_stream = true); bool put(const wchar_t * url, const std::string & in, pt::WTextStream & out, bool clear_stream = true); bool put(const std::wstring & url, const std::string & in, pt::WTextStream & out, bool clear_stream = true); bool put(const wchar_t * url, pt::WTextStream & in, pt::WTextStream & out, bool clear_stream = true); /* * return the last http status */ long get_status(); static const wchar_t * method_to_str(Method method); private: CURL * curl; char error_buf[CURL_ERROR_SIZE]; std::string browser_name; int conn_timeout; // timeout in seconds size_t read_function_index; const std::string * read_function_input; curl_slist * http_headers; pt::Space * additional_space_headers_to_send; const char * additional_string_headers_to_send; const wchar_t * additional_wstring_headers_to_send; pt::Space * output_headers_space; const wchar_t * bearer_token = nullptr; pt::TextStream out_headers_stream; bool change_header_names_to_lower; std::wstring * output_content_type; long debug_mode; pt::Space * debug_info; bool follow_location; bool verify_ssl_cert; bool forse_ssl_version; long ssl_version; const wchar_t * ca_path; std::string ca_path_utf8; const wchar_t * client_cert; const wchar_t * client_key; std::string client_cert_utf8; std::string client_key_utf8; std::wstring temp_header; std::string temp_header_ascii; std::wstring temp_header_value; std::string temp_header_value_ascii; bool fetch_internal(Method method, const char * url, const std::string * in, pt::TextStream & out); void put_method(Method & method); static size_t fetch_read_function(char * ptr, size_t size, size_t nmemb, void * userdata); static int fetch_seek_set(Http * http, curl_off_t offset); static int fetch_seek_cur(Http * http, curl_off_t offset); static int fetch_seek_end(Http * http, curl_off_t offset); static int fetch_seek_function(void * userdata, curl_off_t offset, int origin); static size_t fetch_write_function(char * ptr, size_t size, size_t nmemb, void * userdata); static size_t fetch_header_function(char * ptr, size_t size, size_t nmemb, void * userdata); static void debug_function_append(pt::Space & debug, const char * prefix, const char * start, size_t part_size); static void debug_function_append(Http * http, const char * prefix, char * data, size_t size); static int debug_function(CURL * handle, curl_infotype type, char * data, size_t size, void * userdata); void clear_tmp_objects(); void reset_headers(); void add_additional_space_headers(); void add_additional_string_headers(); void add_bearer_token(); void skip_white(pt::TextStream::iterator & i); void parse_header_name(pt::TextStream::iterator & i, std::string & name); void parse_header_value(pt::TextStream::iterator & i, std::string & value); void parse_headers(); template void add_additional_string_headers(StringType * str) { StringType * part_start = str; size_t part_size = 0; size_t i = 0; while( str[i] != 0 ) { if( str[i]== '\r' && str[i+1]=='\n' ) { add_header(part_start, part_size); i += 2; part_start = str + i; part_size = 0; } else if( str[i]== '\n' ) { add_header(part_start, part_size); i += 1; part_start = str + i; part_size = 0; } else { part_size += 1; i += 1; } } add_header(part_start, part_size); } }; } #endif