16 #include <openssl/ssl.h> 17 #include <openssl/err.h> 19 #define CARRIAGE_RETURN '\r' 20 #define LINE_FEED '\n' 21 #define DOMAIN_SEPERATOR '\\' 24 #define SUPPORTED_CIPHERS_LIST \ 25 "AES128-SHA256:AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:"\ 26 "ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:" 29 #define CURL_2GB_LIMIT 0x80000000UL 60 struct curl_slist *header_list);
66 #if MCL_LOG_ENABLED_COMPILE_TIME(MCL_LOG_LEVEL_DEBUG) 67 static int _curl_debug_callback(CURL *curl, curl_infotype info_type,
char *
data,
mcl_size_t size,
void *debug_data);
74 MCL_DEBUG_ENTRY(
"mcl_http_client_configuration_t *configuration = <%p>, mcl_http_client_t *http_client = <%p>", configuration, http_client);
86 (*http_client)->certificates =
MCL_NULL;
94 (void)SSL_library_init();
104 (*http_client)->curl = curl_easy_init();
106 if (
MCL_NULL == (*http_client)->curl)
116 CURL *curl = (*http_client)->curl;
119 curl_easy_setopt(curl, CURLOPT_PORT, configuration->
port);
127 curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
130 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2);
131 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1);
133 #if MCL_LOG_ENABLED_COMPILE_TIME(MCL_LOG_LEVEL_DEBUG) 135 curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
136 curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, _curl_debug_callback);
140 curl_easy_setopt(curl, CURLOPT_HEADER, 0);
149 curl_easy_setopt(curl, CURLOPT_HEADEROPT, CURLHEADER_UNIFIED);
152 curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1);
155 curl_easy_setopt(curl, CURLOPT_USERAGENT, configuration->
user_agent);
168 CURL *curl = (*http_client)->curl;
170 curl_easy_setopt(curl, CURLOPT_PROXY, configuration->
proxy_hostname);
171 curl_easy_setopt(curl, CURLOPT_PROXYPORT, configuration->
proxy_port);
172 curl_easy_setopt(curl, CURLOPT_PROXYTYPE, configuration->
proxy_type);
173 curl_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
179 char *proxy_username;
185 mcl_size_t proxy_entire_username_length = proxy_domain_length + proxy_username_length + 1;
200 curl_easy_setopt(curl, CURLOPT_PROXYUSERNAME, proxy_username);
201 free(proxy_username);
206 curl_easy_setopt(curl, CURLOPT_PROXYUSERNAME, configuration->
proxy_username);
209 curl_easy_setopt(curl, CURLOPT_PROXYPASSWORD, configuration->
proxy_password);
228 MCL_DEBUG_ENTRY(
"mcl_http_client_t *http_client = <%p>, const char *certificate = <%p>, mcl_bool_t certificate_is_file = <%u>",
229 http_client, certificate, is_file);
239 certificate_item->
is_file = is_file;
259 curl_easy_setopt(http_client->
curl, CURLOPT_CAINFO, certificate_item->
content);
263 curl_easy_setopt(http_client->
curl, CURLOPT_SSL_CTX_DATA, http_client->
certificates);
282 struct curl_slist *request_header_list;
291 MCL_DEBUG_ENTRY(
"mcl_http_client_t *http_client = <%p>, mcl_http_request_t *http_request = <%p>, mcl_http_response_t **http_response = <%p>",
292 http_client, http_request, http_response);
300 if (
MCL_OK == return_code)
303 curl_easy_setopt(http_client->
curl, CURLOPT_HEADERDATA, response_header);
310 response_payload->
size = 0;
311 curl_easy_setopt(http_client->
curl, CURLOPT_WRITEDATA, response_payload);
319 if (
MCL_OK == return_code)
322 MCL_INFO(
"Sending HTTP request...");
324 curl_code = curl_easy_perform(http_client->
curl);
326 MCL_INFO(
"HTTP request sent. Result code = <%u>", return_code);
330 curl_slist_free_all(request_header_list);
332 if (
MCL_OK == return_code)
335 curl_easy_getinfo(http_client->
curl, CURLINFO_RESPONSE_CODE, &response_code);
341 if (
MCL_OK != return_code)
358 MCL_DEBUG_ENTRY(
"mcl_http_client_t **http_client = <%p>", http_client);
362 if (
MCL_NULL != (*http_client)->curl)
364 curl_easy_cleanup((*http_client)->curl);
370 MCL_DEBUG(
"Http client handle is destroyed.");
374 MCL_DEBUG(
"Http client is already null.");
383 CURLcode curl_code = CURLE_OK;
388 MCL_DEBUG_ENTRY(
"CURL *curl = <%p>, void *ssl_context = <%p>, void *certificate_list = <%p>", curl, ssl_context, certificates);
393 if (0 == certificate_list_local->
count)
395 MCL_INFO(
"No certificate is provided by the user for peer verification. Continuing with the existing CA certificate store.");
400 store = SSL_CTX_get_cert_store((SSL_CTX *)ssl_context);
404 while ((
MCL_OK ==
mcl_list_next(certificate_list_local, &certificate_node)) && (CURLE_OK == curl_code))
408 int certificates_number = 0;
409 struct stack_st_X509_INFO *certificate_info =
MCL_NULL;
414 bio = BIO_new_file(certificate->
content,
"r");
418 MCL_ERROR(
"Certificate file could not be read: <%s>", certificate->
content);
419 curl_code = CURLE_READ_ERROR;
428 curl_code = CURLE_OUT_OF_MEMORY;
432 if (CURLE_OK == curl_code)
441 certificates_number = sk_X509_INFO_num(certificate_info);
444 for (index = 0; (index < certificates_number) && (CURLE_OK == curl_code); ++index)
446 X509_INFO *temp_info = sk_X509_INFO_value(certificate_info, index);
451 if (0 == X509_STORE_add_cert(store, temp_info->x509))
455 unsigned long error = ERR_peek_last_error();
457 if ((ERR_GET_LIB(error) != ERR_LIB_X509) || (ERR_GET_REASON(error) != X509_R_CERT_ALREADY_IN_HASH_TABLE))
460 curl_code = CURLE_SSL_CERTPROBLEM;
464 MCL_INFO(
"Did not add the certificate to store since it is already in the store.");
468 else if ((
MCL_NULL != temp_info->crl) && (0 == X509_STORE_add_crl(store, temp_info->crl)))
471 curl_code = CURLE_SSL_CERTPROBLEM;
477 curl_code = CURLE_SSL_CERTPROBLEM;
482 sk_X509_INFO_pop_free(certificate_info, X509_INFO_free);
497 MCL_DEBUG_ENTRY(
"void *received_data = <%p>, mcl_size_t size = <%u>, mcl_size_t count = <%u>, void *response_payload = <%p>",
498 received_data, size, count, response_payload);
513 payload->
size += received_data_size;
516 return received_data_size;
528 MCL_DEBUG_ENTRY(
"void *received_data = <%p>, mcl_size_t size = <%u>, mcl_size_t count = <%u>, void *response_header = <%p>",
529 received_data, size, count, response_header);
535 return received_data_size;
539 header_line = (
char *)
MCL_MALLOC(received_data_size - 1);
563 return received_data_size;
573 mcl_size_t payload_size = (buffer_size > remaining_size) ? remaining_size : buffer_size;
575 MCL_DEBUG_ENTRY(
"char *buffer = <%s>, mcl_size_t size = <%u>, mcl_size_t count = <%u>, void *user_context = <%p>", buffer, size, count, user_context);
577 if (0 == payload_size)
579 MCL_DEBUG(
"Remaining payload size is zero. Nothing will be copied.");
608 struct curl_slist *request_header_list =
MCL_NULL;
609 char *request_header_line =
MCL_NULL;
612 MCL_DEBUG_ENTRY(
"CURL *curl = <%p>, mcl_http_request_t *http_request = <%p>, default_callback_user_context_t *user_context = <%p>",
613 curl, http_request, user_context);
617 curl_easy_setopt(curl, CURLOPT_URL, http_request->
uri);
620 curl_easy_setopt(curl, CURLOPT_HTTPGET, 0);
621 curl_easy_setopt(curl, CURLOPT_POST, 0);
622 curl_easy_setopt(curl, CURLOPT_UPLOAD, 0);
623 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST,
MCL_NULL);
625 curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1);
626 curl_easy_setopt(curl, CURLOPT_INFILESIZE, -1);
627 curl_easy_setopt(curl, CURLOPT_READDATA,
MCL_NULL);
628 curl_easy_setopt(curl, CURLOPT_POSTFIELDS,
MCL_NULL);
630 switch (http_request->
method)
633 curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
637 curl_easy_setopt(curl, CURLOPT_POST, 1);
643 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, (
void *)http_request->
payload);
646 curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, http_request->
payload_size);
651 curl_easy_setopt(curl, CURLOPT_READFUNCTION, http_request->
stream_callback);
652 curl_easy_setopt(curl, CURLOPT_READDATA, http_request->
stream_data);
658 curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
659 request_header_list =
_set_payload_options(curl, http_request, user_context, request_header_list);
663 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST,
"PATCH");
664 curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
665 request_header_list =
_set_payload_options(curl, http_request, user_context, request_header_list);
677 request_header_line = header_node->
data;
678 request_header_list = curl_slist_append(request_header_list, request_header_line);
683 request_header_list = curl_slist_append(request_header_list,
"Expect:");
686 curl_easy_setopt(curl, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE);
687 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, request_header_list);
688 curl_easy_setopt(curl, CURLOPT_PROXYHEADER,
MCL_NULL);
691 return request_header_list;
696 struct curl_slist *header_list)
698 struct curl_slist *new_header_list =
MCL_NULL;
700 MCL_DEBUG_ENTRY(
"CURL *curl = <%p>, mcl_http_request_t *http_request = <%p>, default_callback_user_context_t *user_context = <%p>, "\
701 "struct curl_slist *header_list = <%p>", curl, http_request, user_context, header_list);
711 curl_easy_setopt(curl, CURLOPT_READDATA, user_context);
717 curl_easy_setopt(curl, CURLOPT_READFUNCTION, http_request->
stream_callback);
718 curl_easy_setopt(curl, CURLOPT_READDATA, http_request->
stream_data);
732 return new_header_list;
737 MCL_DEBUG_ENTRY(
"CURL *curl = <%p>, mcl_size_t payload_size = <%u>", curl, payload_size);
741 curl_easy_setopt(curl, CURLOPT_INFILESIZE, (
long) payload_size);
745 curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t) payload_size);
751 #if MCL_LOG_ENABLED_COMPILE_TIME(MCL_LOG_LEVEL_DEBUG) 752 static int _curl_debug_callback(CURL *curl, curl_infotype info_type,
char *
data,
mcl_size_t size,
void *debug_data)
764 case CURLINFO_HEADER_OUT :
768 case CURLINFO_DATA_OUT :
772 case CURLINFO_SSL_DATA_OUT :
773 MCL_DEBUG(
"Curl: sent SSL/TLS (binary) data.");
776 case CURLINFO_HEADER_IN :
780 case CURLINFO_DATA_IN :
781 MCL_DEBUG(
"Curl: received protocol data.");
784 case CURLINFO_SSL_DATA_IN :
785 MCL_DEBUG(
"Curl: received SSL/TLS (binary) data.");
808 case CURLE_COULDNT_RESOLVE_PROXY :
812 case CURLE_COULDNT_RESOLVE_HOST :
816 case CURLE_COULDNT_CONNECT :
820 case CURLE_OUT_OF_MEMORY :
824 case CURLE_SSL_CONNECT_ERROR :
828 case CURLE_PEER_FAILED_VERIFICATION :
832 case CURLE_SEND_ERROR :
836 case CURLE_RECV_ERROR :
840 case CURLE_SSL_CERTPROBLEM :
844 case CURLE_OPERATION_TIMEDOUT :
864 MCL_DEBUG_ENTRY(
"mcl_certificate_t **certificate = <%p>", certificate);
HTTP definitions module header file.
MCL failed to connect to the host or proxy.
#define MCL_FUNCTION_LEAVE_LABEL
MCL_CORE_EXPORT void * mcl_memory_calloc(mcl_size_t count, mcl_size_t bytes)
char * string_util_strdup(const char *string)
Assert module header file.
static mcl_size_t _response_payload_callback(void *received_data, mcl_size_t size, mcl_size_t count, void *response_payload)
mcl_error_t mcl_http_client_initialize(mcl_http_client_configuration_t *configuration, mcl_http_client_t **http_client)
static void _header_list_destroy_callback(void **item)
char * uri
Uri of http request.
static mcl_size_t _response_header_callback(void *received_data, mcl_size_t size, mcl_size_t count, void *response_header)
static void _set_in_file_size(CURL *curl, mcl_size_t payload_size)
MCL_CORE_EXPORT mcl_error_t mcl_http_response_initialize(mcl_list_t *header, mcl_uint8_t *payload, mcl_size_t payload_size, E_MCL_HTTP_STATUS_CODE status_code, mcl_http_response_t **http_response)
A problem occured during SSL/TLS handshake.
void mcl_http_client_destroy(mcl_http_client_t **http_client)
#define MCL_DEBUG_ENTRY(...)
mcl_size_t payload_size
Payload size of http request.
void * stream_data
Stream data.
void(* mcl_list_item_destroy_callback)(void **item)
#define SUPPORTED_CIPHERS_LIST
const char * user_agent
User agent.
static mcl_size_t _request_payload_callback_for_put(char *buffer, mcl_size_t size, mcl_size_t count, void *user_context)
const char * proxy_username
Proxy username. Optional if proxy host name is set, ineffective otherwise.
E_MCL_PROXY proxy_type
Proxy type E_MCL_PROXY. Mandatory if proxy host name is set, ineffective otherwise.
String utility module header file.
const char * http_header_names[HTTP_HEADER_NAMES_END]
mcl_error_t mcl_http_client_add_certificate(mcl_http_client_t *http_client, const char *certificate, mcl_bool_t is_file)
MCL_CORE_EXPORT mcl_error_t mcl_list_next(mcl_list_t *list, mcl_list_node_t **node)
#define MCL_ERROR_STRING(string)
MCL_CORE_EXPORT void * mcl_memory_realloc(void *p, mcl_size_t bytes)
static struct curl_slist * _set_payload_options(CURL *curl, mcl_http_request_t *http_request, default_callback_user_context_t *user_context, struct curl_slist *header_list)
#define MCL_ASSERT_CODE_MESSAGE(condition, return_code,...)
HTTP client libcurl module header file.
mcl_bool_t certificate_is_file
Flag to check if certificate is given as file or string.
mcl_uint16_t proxy_port
Proxy port number. Mandatory if proxy host name is set, ineffective otherwise.
Mindsphere certificate was not verified.
struct mcl_list_node_t * next
Next node in the list.
Host name given as a configuration parameter could not be resolved.
const char * certificate
Certificate. If it is NULL, default CA certificate store will be used (if available).
MCL_CORE_EXPORT mcl_error_t mcl_list_add(mcl_list_t *list, void *data)
static void _certificate_list_destroy_callback(mcl_certificate_t **certificate)
static mcl_error_t _convert_to_mcl_return_code(CURLcode curl_code)
static CURLcode _ssl_context_callback(CURL *curl, void *ssl_context, void *certificates)
#define MCL_RESIZE(p, bytes)
void string_util_memcpy(void *destination, const void *source, mcl_size_t count)
E_MCL_HTTP_METHOD method
Http method of http request.
MCL_CORE_EXPORT void mcl_list_destroy_with_content(mcl_list_t **list, mcl_list_item_destroy_callback callback)
MCL_CORE_EXPORT void mcl_memory_free(void *p)
mcl_error_t mcl_http_client_send(mcl_http_client_t *http_client, mcl_http_request_t *http_request, mcl_http_response_t **http_response)
#define MCL_ASSERT_NOT_NULL(argument, return_variable)
A problem occured when sending data to the network.
MCL_CORE_EXPORT void mcl_list_reset(mcl_list_t *list)
The server did not respond within a timeout period.
#define MCL_ERROR_RETURN(return_value,...)
mcl_uint32_t http_request_timeout
Timeout value (in seconds) for HTTP requests. Default timeout is 300 seconds.
static struct curl_slist * _set_request_options(CURL *curl, mcl_http_request_t *http_request, default_callback_user_context_t *user_context)
The server certificate provided is in improper format and it can not be parsed.
static mcl_bool_t _is_empty_line(char *line)
Http transfer encoding chunked header.
A problem occured when receiving data from the network.
mcl_http_payload_callback stream_callback
Callback to be used with chunked Transfer-Encoding. If not used, it must be NULL. ...
const char * proxy_domain
Proxy domain. Optional if proxy host name and proxy username are set, ineffective otherwise...
const char * proxy_password
Proxy password. Mandatory if proxy host name and proxy username are set, ineffective otherwise...
mcl_uint16_t port
Port number.
mcl_uint8_t * payload
Payload of http request.
mcl_list_node_t * current
Current node of the list.
mcl_http_request_t * http_request
MCL_CORE_EXPORT mcl_error_t mcl_list_initialize(mcl_list_t **list)
#define MCL_NULL_CHAR_SIZE
mcl_size_t count
Node count of the list.
#define MCL_MALLOC(bytes)
mcl_list_t * certificates
List of server certificates.
const char * proxy_hostname
Proxy hostname. Optional.
mcl_size_t callback_offset
#define MCL_DEBUG_LEAVE(...)
MCL_CORE_EXPORT void * mcl_memory_malloc(mcl_size_t size)
mcl_list_t * header
Header of http request.
Proxy host name given as a configuration parameter could not be resolved.
static mcl_bool_t curl_global_initialized
void * data
Data of the node.
mcl_size_t string_util_strlen(const char *buffer)
Memory module interface header file.