2016-08-19 20:55:26 -07:00
|
|
|
#define EFL_NET_DIALER_HTTP_PROTECTED 1
|
|
|
|
#define EFL_NET_DIALER_PROTECTED 1
|
|
|
|
#define EFL_NET_SOCKET_PROTECTED 1
|
|
|
|
#define EFL_IO_READER_PROTECTED 1
|
|
|
|
#define EFL_IO_WRITER_PROTECTED 1
|
|
|
|
#define EFL_IO_CLOSER_PROTECTED 1
|
|
|
|
#define EFL_IO_SIZER_PROTECTED 1
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include <config.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "Ecore.h"
|
|
|
|
#include "Ecore_Con.h"
|
|
|
|
#include "ecore_con_private.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* uncomment to test with system's curl.h, by default uses a local
|
|
|
|
* replica with required values.
|
|
|
|
*/
|
|
|
|
//#define USE_CURL_H 1
|
|
|
|
#include "ecore_con_url_curl.h"
|
|
|
|
|
|
|
|
#include <ctype.h>
|
2016-09-12 08:23:29 -07:00
|
|
|
#include <fcntl.h>
|
2016-08-19 20:55:26 -07:00
|
|
|
|
|
|
|
/* improve usage of lazy-loaded library in _c-> */
|
|
|
|
#define curl_easy_strerror(...) _c->curl_easy_strerror(__VA_ARGS__)
|
|
|
|
#define curl_easy_init(...) _c->curl_easy_init(__VA_ARGS__)
|
|
|
|
#define curl_easy_cleanup(...) _c->curl_easy_cleanup(__VA_ARGS__)
|
|
|
|
#define curl_easy_pause(...) _c->curl_easy_pause(__VA_ARGS__)
|
2016-11-28 19:19:40 -08:00
|
|
|
#define curl_getdate(s, unused) _c->curl_getdate(s, unused)
|
2016-08-19 20:55:26 -07:00
|
|
|
|
|
|
|
#ifdef curl_easy_setopt
|
|
|
|
#undef curl_easy_setopt
|
|
|
|
#endif
|
|
|
|
#ifndef __CURL_TYPECHECK_GCC_H
|
|
|
|
#define curl_easy_setopt(easy, option, value) _c->curl_easy_setopt(easy, option, value)
|
|
|
|
#else
|
|
|
|
/* curl.h was used with type-checking, so replicate it here from typecheck-gcc.h */
|
|
|
|
#define curl_easy_setopt(handle, option, value) \
|
|
|
|
__extension__ ({ \
|
|
|
|
__typeof__ (option) _curl_opt = option; \
|
|
|
|
if(__builtin_constant_p(_curl_opt)) { \
|
|
|
|
if(_curl_is_long_option(_curl_opt)) \
|
|
|
|
if(!_curl_is_long(value)) \
|
|
|
|
_curl_easy_setopt_err_long(); \
|
|
|
|
if(_curl_is_off_t_option(_curl_opt)) \
|
|
|
|
if(!_curl_is_off_t(value)) \
|
|
|
|
_curl_easy_setopt_err_curl_off_t(); \
|
|
|
|
if(_curl_is_string_option(_curl_opt)) \
|
|
|
|
if(!_curl_is_string(value)) \
|
|
|
|
_curl_easy_setopt_err_string(); \
|
|
|
|
if(_curl_is_write_cb_option(_curl_opt)) \
|
|
|
|
if(!_curl_is_write_cb(value)) \
|
|
|
|
_curl_easy_setopt_err_write_callback(); \
|
|
|
|
if((_curl_opt) == CURLOPT_READFUNCTION) \
|
|
|
|
if(!_curl_is_read_cb(value)) \
|
|
|
|
_curl_easy_setopt_err_read_cb(); \
|
|
|
|
if((_curl_opt) == CURLOPT_IOCTLFUNCTION) \
|
|
|
|
if(!_curl_is_ioctl_cb(value)) \
|
|
|
|
_curl_easy_setopt_err_ioctl_cb(); \
|
|
|
|
if((_curl_opt) == CURLOPT_SOCKOPTFUNCTION) \
|
|
|
|
if(!_curl_is_sockopt_cb(value)) \
|
|
|
|
_curl_easy_setopt_err_sockopt_cb(); \
|
|
|
|
if((_curl_opt) == CURLOPT_OPENSOCKETFUNCTION) \
|
|
|
|
if(!_curl_is_opensocket_cb(value)) \
|
|
|
|
_curl_easy_setopt_err_opensocket_cb(); \
|
|
|
|
if((_curl_opt) == CURLOPT_PROGRESSFUNCTION) \
|
|
|
|
if(!_curl_is_progress_cb(value)) \
|
|
|
|
_curl_easy_setopt_err_progress_cb(); \
|
|
|
|
if((_curl_opt) == CURLOPT_DEBUGFUNCTION) \
|
|
|
|
if(!_curl_is_debug_cb(value)) \
|
|
|
|
_curl_easy_setopt_err_debug_cb(); \
|
|
|
|
if((_curl_opt) == CURLOPT_SSL_CTX_FUNCTION) \
|
|
|
|
if(!_curl_is_ssl_ctx_cb(value)) \
|
|
|
|
_curl_easy_setopt_err_ssl_ctx_cb(); \
|
|
|
|
if(_curl_is_conv_cb_option(_curl_opt)) \
|
|
|
|
if(!_curl_is_conv_cb(value)) \
|
|
|
|
_curl_easy_setopt_err_conv_cb(); \
|
|
|
|
if((_curl_opt) == CURLOPT_SEEKFUNCTION) \
|
|
|
|
if(!_curl_is_seek_cb(value)) \
|
|
|
|
_curl_easy_setopt_err_seek_cb(); \
|
|
|
|
if(_curl_is_cb_data_option(_curl_opt)) \
|
|
|
|
if(!_curl_is_cb_data(value)) \
|
|
|
|
_curl_easy_setopt_err_cb_data(); \
|
|
|
|
if((_curl_opt) == CURLOPT_ERRORBUFFER) \
|
|
|
|
if(!_curl_is_error_buffer(value)) \
|
|
|
|
_curl_easy_setopt_err_error_buffer(); \
|
|
|
|
if((_curl_opt) == CURLOPT_STDERR) \
|
|
|
|
if(!_curl_is_FILE(value)) \
|
|
|
|
_curl_easy_setopt_err_FILE(); \
|
|
|
|
if(_curl_is_postfields_option(_curl_opt)) \
|
|
|
|
if(!_curl_is_postfields(value)) \
|
|
|
|
_curl_easy_setopt_err_postfields(); \
|
|
|
|
if((_curl_opt) == CURLOPT_HTTPPOST) \
|
|
|
|
if(!_curl_is_arr((value), struct curl_httppost)) \
|
|
|
|
_curl_easy_setopt_err_curl_httpost(); \
|
|
|
|
if(_curl_is_slist_option(_curl_opt)) \
|
|
|
|
if(!_curl_is_arr((value), struct curl_slist)) \
|
|
|
|
_curl_easy_setopt_err_curl_slist(); \
|
|
|
|
if((_curl_opt) == CURLOPT_SHARE) \
|
|
|
|
if(!_curl_is_ptr((value), CURLSH)) \
|
|
|
|
_curl_easy_setopt_err_CURLSH(); \
|
|
|
|
} \
|
|
|
|
_c->curl_easy_setopt(handle, _curl_opt, value); \
|
|
|
|
})
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef curl_easy_getinfo
|
|
|
|
#undef curl_easy_getinfo
|
|
|
|
#endif
|
|
|
|
#ifndef __CURL_TYPECHECK_GCC_H
|
|
|
|
#define curl_easy_getinfo(easy, info, arg) _c->curl_easy_getinfo(easy, info, arg)
|
|
|
|
#else
|
|
|
|
/* curl.h was used with type-checking, so replicate it here from typecheck-gcc.h */
|
|
|
|
|
|
|
|
/* wraps curl_easy_getinfo() with typechecking */
|
|
|
|
/* FIXME: don't allow const pointers */
|
|
|
|
#define curl_easy_getinfo(handle, info, arg) \
|
|
|
|
__extension__ ({ \
|
|
|
|
__typeof__ (info) _curl_info = info; \
|
|
|
|
if(__builtin_constant_p(_curl_info)) { \
|
|
|
|
if(_curl_is_string_info(_curl_info)) \
|
|
|
|
if(!_curl_is_arr((arg), char *)) \
|
|
|
|
_curl_easy_getinfo_err_string(); \
|
|
|
|
if(_curl_is_long_info(_curl_info)) \
|
|
|
|
if(!_curl_is_arr((arg), long)) \
|
|
|
|
_curl_easy_getinfo_err_long(); \
|
|
|
|
if(_curl_is_double_info(_curl_info)) \
|
|
|
|
if(!_curl_is_arr((arg), double)) \
|
|
|
|
_curl_easy_getinfo_err_double(); \
|
|
|
|
if(_curl_is_slist_info(_curl_info)) \
|
|
|
|
if(!_curl_is_arr((arg), struct curl_slist *)) \
|
|
|
|
_curl_easy_getinfo_err_curl_slist(); \
|
|
|
|
} \
|
|
|
|
_c->curl_easy_getinfo(handle, _curl_info, arg); \
|
|
|
|
})
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define curl_multi_strerror(...) _c->curl_multi_strerror(__VA_ARGS__)
|
|
|
|
#define curl_multi_init(...) _c->curl_multi_init(__VA_ARGS__)
|
|
|
|
#define curl_multi_cleanup(...) _c->curl_multi_cleanup(__VA_ARGS__)
|
|
|
|
#ifdef curl_multi_setopt
|
|
|
|
#undef curl_multi_setopt
|
|
|
|
#endif
|
|
|
|
#define curl_multi_setopt(multi, opt, value) _c->curl_multi_setopt(multi, opt, value)
|
|
|
|
|
|
|
|
#define curl_multi_add_handle(...) _c->curl_multi_add_handle(__VA_ARGS__)
|
|
|
|
#define curl_multi_remove_handle(...) _c->curl_multi_remove_handle(__VA_ARGS__)
|
|
|
|
|
|
|
|
#define curl_multi_assign(...) _c->curl_multi_assign(__VA_ARGS__)
|
|
|
|
#define curl_multi_socket_action(...) _c->curl_multi_socket_action(__VA_ARGS__)
|
|
|
|
#define curl_multi_info_read(...) _c->curl_multi_info_read(__VA_ARGS__)
|
|
|
|
|
|
|
|
#define curl_slist_append(...) _c->curl_slist_append(__VA_ARGS__)
|
|
|
|
#define curl_slist_free_all(...) _c->curl_slist_free_all(__VA_ARGS__)
|
|
|
|
|
|
|
|
#define MY_CLASS EFL_NET_DIALER_HTTP_CLASS
|
|
|
|
|
|
|
|
typedef struct _Efl_Net_Dialer_Http_Curlm {
|
|
|
|
Eo *loop;
|
|
|
|
CURLM *multi;
|
|
|
|
Eina_List *users;
|
|
|
|
Eo *timer;
|
|
|
|
int running;
|
|
|
|
unsigned int pending_init;
|
|
|
|
} Efl_Net_Dialer_Http_Curlm;
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
CURL *easy;
|
|
|
|
Efl_Net_Dialer_Http_Curlm *cm;
|
2016-11-23 20:13:32 -08:00
|
|
|
Eo *fdhandler;
|
2016-08-19 20:55:26 -07:00
|
|
|
Eina_Stringshare *address_dial;
|
2016-08-22 14:51:38 -07:00
|
|
|
Eina_Stringshare *proxy;
|
2016-08-22 15:24:13 -07:00
|
|
|
Eina_Stringshare *cookie_jar;
|
2016-08-19 20:55:26 -07:00
|
|
|
Eina_Stringshare *address_local;
|
|
|
|
Eina_Stringshare *address_remote;
|
|
|
|
Eina_Stringshare *method;
|
|
|
|
Eina_Stringshare *user_agent;
|
2016-09-19 06:00:36 -07:00
|
|
|
Ecore_Thread *libproxy_thread;
|
2016-08-19 20:55:26 -07:00
|
|
|
struct {
|
|
|
|
struct curl_slist *headers;
|
|
|
|
int64_t content_length;
|
|
|
|
} request;
|
|
|
|
struct {
|
|
|
|
Eina_Slice slice;
|
|
|
|
} send;
|
|
|
|
struct {
|
|
|
|
uint8_t *bytes;
|
|
|
|
size_t used;
|
|
|
|
size_t limit;
|
|
|
|
} recv;
|
|
|
|
uint64_t size;
|
|
|
|
double timeout_dial;
|
|
|
|
struct {
|
|
|
|
Eina_Stringshare *username;
|
|
|
|
char *password;
|
|
|
|
Efl_Net_Http_Authentication_Method method;
|
|
|
|
Eina_Bool restricted;
|
|
|
|
} authentication;
|
2016-11-28 20:16:47 -08:00
|
|
|
struct {
|
|
|
|
Eina_Stringshare *ca;
|
|
|
|
Eina_Stringshare *crl;
|
|
|
|
Eina_Bool verify_peer;
|
|
|
|
Eina_Bool verify_hostname;
|
|
|
|
} ssl;
|
2017-08-30 13:24:27 -07:00
|
|
|
Eina_Future *pending_close;
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
unsigned int in_curl_callback;
|
2016-10-22 16:49:01 -07:00
|
|
|
SOCKET fd;
|
2016-08-19 20:55:26 -07:00
|
|
|
Eina_Error error;
|
|
|
|
Efl_Net_Http_Version version;
|
|
|
|
Efl_Net_Dialer_Http_Primary_Mode primary_mode;
|
|
|
|
Eina_Bool allow_redirects;
|
|
|
|
uint8_t pause;
|
|
|
|
Eina_Bool connected;
|
|
|
|
Eina_Bool closed;
|
2016-09-12 08:23:29 -07:00
|
|
|
Eina_Bool close_on_exec;
|
2018-04-17 16:17:29 -07:00
|
|
|
Eina_Bool close_on_invalidate;
|
2016-10-26 17:54:42 -07:00
|
|
|
Eina_Bool pending_eos;
|
2016-08-19 20:55:26 -07:00
|
|
|
Eina_Bool eos;
|
|
|
|
Eina_Bool can_read;
|
|
|
|
Eina_Bool can_write;
|
|
|
|
Eina_Bool pending_headers_done;
|
|
|
|
struct {
|
|
|
|
Eina_List *headers;
|
|
|
|
const Eina_List *last_request_header;
|
|
|
|
Efl_Net_Http_Status status;
|
|
|
|
Eina_Stringshare *content_type;
|
|
|
|
int64_t content_length;
|
|
|
|
} response;
|
|
|
|
struct {
|
|
|
|
struct {
|
|
|
|
uint64_t now;
|
|
|
|
uint64_t total;
|
|
|
|
} download;
|
|
|
|
struct {
|
|
|
|
uint64_t now;
|
|
|
|
uint64_t total;
|
|
|
|
} upload;
|
|
|
|
} progress;
|
|
|
|
} Efl_Net_Dialer_Http_Data;
|
|
|
|
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
static void
|
|
|
|
_efl_net_dialer_http_curlm_check_finished_object_deleted(void *data, const Efl_Event *event)
|
|
|
|
{
|
|
|
|
Eina_List **p_finished = data;
|
|
|
|
*p_finished = eina_list_remove(*p_finished, event->object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_efl_net_dialer_http_curlm_check_finished_object_add(Eina_List **p_finished, Eo *dialer)
|
|
|
|
{
|
|
|
|
if (eina_list_data_find(*p_finished, dialer)) return;
|
|
|
|
|
|
|
|
efl_event_callback_add(dialer, EFL_EVENT_DEL,
|
|
|
|
_efl_net_dialer_http_curlm_check_finished_object_deleted,
|
|
|
|
p_finished);
|
|
|
|
*p_finished = eina_list_append(*p_finished, dialer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_efl_net_dialer_http_curlm_check_finished_object_remove(Eina_List **p_finished, Eo *dialer)
|
|
|
|
{
|
|
|
|
efl_event_callback_del(dialer, EFL_EVENT_DEL,
|
|
|
|
_efl_net_dialer_http_curlm_check_finished_object_deleted,
|
|
|
|
p_finished);
|
|
|
|
*p_finished = eina_list_remove(*p_finished, dialer);
|
|
|
|
}
|
|
|
|
|
2016-08-19 20:55:26 -07:00
|
|
|
static void
|
|
|
|
_efl_net_dialer_http_curlm_check(Efl_Net_Dialer_Http_Curlm *cm)
|
|
|
|
{
|
|
|
|
CURLMsg *msg;
|
|
|
|
int remaining;
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
Eina_List *finished = NULL;
|
|
|
|
Eo *dialer;
|
2016-08-19 20:55:26 -07:00
|
|
|
|
|
|
|
while ((msg = curl_multi_info_read(cm->multi, &remaining)))
|
|
|
|
{
|
|
|
|
CURLcode re;
|
|
|
|
char *priv; /* CURLINFO_PRIVATE checks for this type */
|
|
|
|
|
|
|
|
re = curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &priv);
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
if ((re == CURLE_OK) && (priv))
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
dialer = (Eo *)priv;
|
2016-08-19 20:55:26 -07:00
|
|
|
|
|
|
|
if (msg->data.result != CURLE_OK)
|
|
|
|
{
|
|
|
|
Eina_Error err = _curlcode_to_eina_error(msg->data.result);
|
|
|
|
Efl_Net_Dialer_Http_Data *pd = efl_data_scope_get(dialer, MY_CLASS);
|
|
|
|
DBG("HTTP dialer=%p error: #%d '%s'",
|
|
|
|
dialer, err, eina_error_msg_get(err));
|
|
|
|
pd->error = err;
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
_efl_net_dialer_http_curlm_check_finished_object_add(&finished, dialer);
|
2016-08-19 20:55:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (msg->msg != CURLMSG_DONE) continue;
|
|
|
|
|
|
|
|
if (!efl_io_closer_closed_get(dialer))
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
{
|
|
|
|
DBG("HTTP dialer=%p eos", dialer);
|
|
|
|
_efl_net_dialer_http_curlm_check_finished_object_add(&finished, dialer);
|
|
|
|
}
|
2016-08-19 20:55:26 -07:00
|
|
|
else
|
|
|
|
DBG("HTTP dialer=%p already closed", dialer);
|
|
|
|
}
|
|
|
|
}
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
|
|
|
|
while (finished)
|
|
|
|
{
|
|
|
|
Efl_Net_Dialer_Http_Data *pd;
|
|
|
|
|
|
|
|
dialer = finished->data;
|
|
|
|
efl_ref(dialer);
|
|
|
|
pd = efl_data_scope_get(dialer, MY_CLASS);
|
|
|
|
if (pd->error)
|
|
|
|
efl_event_callback_call(dialer, EFL_NET_DIALER_EVENT_ERROR, &pd->error);
|
2016-10-26 17:54:42 -07:00
|
|
|
if (pd->recv.used > 0) pd->pending_eos = EINA_TRUE;
|
|
|
|
else
|
|
|
|
{
|
2016-11-28 13:15:29 -08:00
|
|
|
if (!efl_io_closer_closed_get(dialer))
|
2017-07-28 07:04:23 -07:00
|
|
|
{
|
|
|
|
efl_io_reader_eos_set(dialer, EINA_TRUE);
|
2017-12-17 18:59:57 -08:00
|
|
|
if (!efl_io_closer_closed_get(dialer))
|
|
|
|
efl_io_closer_close(dialer);
|
2017-07-28 07:04:23 -07:00
|
|
|
}
|
2016-10-26 17:54:42 -07:00
|
|
|
}
|
|
|
|
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
_efl_net_dialer_http_curlm_check_finished_object_remove(&finished, dialer);
|
|
|
|
efl_unref(dialer);
|
|
|
|
}
|
2016-08-19 20:55:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-08-30 05:34:10 -07:00
|
|
|
_efl_net_dialer_http_curlm_timer_do(void *data, const Efl_Event *ev EINA_UNUSED)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
Efl_Net_Dialer_Http_Curlm *cm = data;
|
|
|
|
CURLMcode r;
|
|
|
|
|
|
|
|
/* expected to trigger only once then reschedule */
|
|
|
|
efl_event_freeze(cm->timer);
|
|
|
|
|
|
|
|
r = curl_multi_socket_action(cm->multi,
|
|
|
|
CURL_SOCKET_TIMEOUT, 0, &cm->running);
|
|
|
|
if (r != CURLM_OK)
|
|
|
|
ERR("socket action CURL_SOCKET_TIMEOUT failed: %s", curl_multi_strerror(r));
|
|
|
|
|
|
|
|
_efl_net_dialer_http_curlm_check(cm);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
_efl_net_dialer_http_curlm_timer_schedule(CURLM *multi EINA_UNUSED, long timeout_ms, void *data)
|
|
|
|
{
|
|
|
|
Efl_Net_Dialer_Http_Curlm *cm = data;
|
|
|
|
double seconds = timeout_ms / 1000.0;
|
|
|
|
|
|
|
|
if (cm->timer)
|
|
|
|
{
|
|
|
|
efl_loop_timer_interval_set(cm->timer, seconds);
|
|
|
|
efl_loop_timer_reset(cm->timer);
|
|
|
|
while (efl_event_freeze_count_get(cm->timer) > 0)
|
|
|
|
efl_event_thaw(cm->timer);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cm->timer = efl_add(EFL_LOOP_TIMER_CLASS, cm->loop,
|
2016-09-05 08:57:35 -07:00
|
|
|
efl_loop_timer_interval_set(efl_added, seconds),
|
|
|
|
efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TICK, _efl_net_dialer_http_curlm_timer_do, cm));
|
2016-08-19 20:55:26 -07:00
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(cm->timer, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-08-30 05:34:10 -07:00
|
|
|
_efl_net_dialer_http_curlm_event_fd_read(void *data, const Efl_Event *event)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
Efl_Net_Dialer_Http_Curlm *cm = data;
|
2016-10-22 16:49:01 -07:00
|
|
|
SOCKET fd = efl_loop_fd_get(event->object);
|
2016-08-19 20:55:26 -07:00
|
|
|
CURLMcode r;
|
|
|
|
|
|
|
|
r = curl_multi_socket_action(cm->multi, fd, CURL_CSELECT_IN, &cm->running);
|
|
|
|
if (r != CURLM_OK)
|
2016-11-18 06:17:08 -08:00
|
|
|
ERR("socket action CURL_CSELECT_IN fd=" SOCKET_FMT " failed: %s", fd, curl_multi_strerror(r));
|
2016-08-19 20:55:26 -07:00
|
|
|
|
|
|
|
_efl_net_dialer_http_curlm_check(cm);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-08-30 05:34:10 -07:00
|
|
|
_efl_net_dialer_http_curlm_event_fd_write(void *data, const Efl_Event *event)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
Efl_Net_Dialer_Http_Curlm *cm = data;
|
2016-10-22 16:49:01 -07:00
|
|
|
SOCKET fd = efl_loop_fd_get(event->object);
|
2016-08-19 20:55:26 -07:00
|
|
|
CURLMcode r;
|
|
|
|
|
|
|
|
r = curl_multi_socket_action(cm->multi, fd, CURL_CSELECT_OUT, &cm->running);
|
|
|
|
if (r != CURLM_OK)
|
2016-11-18 06:17:08 -08:00
|
|
|
ERR("socket action CURL_CSELECT_OUT fd=" SOCKET_FMT " failed: %s", fd, curl_multi_strerror(r));
|
2016-08-19 20:55:26 -07:00
|
|
|
|
|
|
|
_efl_net_dialer_http_curlm_check(cm);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
_efl_net_dialer_http_curlm_socket_manage(CURL *e, curl_socket_t fd, int what, void *cm_data, void *fdhandler_data)
|
|
|
|
{
|
|
|
|
Efl_Net_Dialer_Http_Curlm *cm = cm_data;
|
|
|
|
Efl_Net_Dialer_Http_Data *pd;
|
|
|
|
Eo *dialer, *fdhandler = fdhandler_data;
|
|
|
|
char *priv;
|
|
|
|
CURLcode re;
|
|
|
|
|
|
|
|
re = curl_easy_getinfo(e, CURLINFO_PRIVATE, &priv);
|
|
|
|
EINA_SAFETY_ON_TRUE_RETURN_VAL(re != CURLE_OK, -1);
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
if (!priv) return -1;
|
2016-08-19 20:55:26 -07:00
|
|
|
dialer = (Eo *)priv;
|
|
|
|
pd = efl_data_scope_get(dialer, MY_CLASS);
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(pd, -1);
|
|
|
|
|
|
|
|
if (what == CURL_POLL_REMOVE)
|
|
|
|
{
|
|
|
|
pd->fdhandler = NULL;
|
|
|
|
efl_del(fdhandler);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int flags;
|
|
|
|
Eina_Bool was_read, is_read, was_write, is_write;
|
|
|
|
|
|
|
|
if (fdhandler)
|
|
|
|
flags = (intptr_t)efl_key_data_get(fdhandler, "curl_flags");
|
|
|
|
else
|
|
|
|
{
|
2016-11-23 20:13:32 -08:00
|
|
|
pd->fdhandler = fdhandler = efl_add(EFL_LOOP_FD_CLASS, cm->loop,
|
|
|
|
efl_loop_fd_set(efl_added, fd));
|
2016-08-19 20:55:26 -07:00
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(fdhandler, -1);
|
|
|
|
curl_multi_assign(cm->multi, fd, fdhandler);
|
|
|
|
flags = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (what == flags)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
was_read = !!(flags & CURL_POLL_IN);
|
|
|
|
was_write = !!(flags & CURL_POLL_OUT);
|
|
|
|
|
|
|
|
is_read = !!(what & CURL_POLL_IN);
|
|
|
|
is_write = !!(what & CURL_POLL_OUT);
|
|
|
|
|
|
|
|
if (was_read && !is_read)
|
|
|
|
{
|
2016-11-23 20:13:32 -08:00
|
|
|
efl_event_callback_del(fdhandler, EFL_LOOP_FD_EVENT_READ, _efl_net_dialer_http_curlm_event_fd_read, cm);
|
2016-08-19 20:55:26 -07:00
|
|
|
}
|
|
|
|
else if (!was_read && is_read)
|
|
|
|
{
|
|
|
|
efl_event_callback_add(fdhandler, EFL_LOOP_FD_EVENT_READ, _efl_net_dialer_http_curlm_event_fd_read, cm);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (was_write && !is_write)
|
|
|
|
{
|
2016-11-23 20:13:32 -08:00
|
|
|
efl_event_callback_del(fdhandler, EFL_LOOP_FD_EVENT_WRITE, _efl_net_dialer_http_curlm_event_fd_write, cm);
|
2016-08-19 20:55:26 -07:00
|
|
|
}
|
|
|
|
else if (!was_write && is_write)
|
|
|
|
{
|
|
|
|
efl_event_callback_add(fdhandler, EFL_LOOP_FD_EVENT_WRITE, _efl_net_dialer_http_curlm_event_fd_write, cm);
|
|
|
|
}
|
|
|
|
|
|
|
|
efl_key_data_set(fdhandler, "curl_flags", (void *)(intptr_t)what);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Eina_Bool
|
|
|
|
_efl_net_dialer_http_curlm_add(Efl_Net_Dialer_Http_Curlm *cm, Eo *o, CURL *handle)
|
|
|
|
{
|
|
|
|
CURLMcode r;
|
|
|
|
|
|
|
|
if (!cm->multi)
|
|
|
|
{
|
|
|
|
cm->multi = curl_multi_init();
|
|
|
|
if (!cm->multi)
|
|
|
|
{
|
|
|
|
ERR("could not create curl multi handle");
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
curl_multi_setopt(cm->multi, CURLMOPT_SOCKETFUNCTION, _efl_net_dialer_http_curlm_socket_manage);
|
|
|
|
curl_multi_setopt(cm->multi, CURLMOPT_SOCKETDATA, cm);
|
|
|
|
curl_multi_setopt(cm->multi, CURLMOPT_TIMERFUNCTION, _efl_net_dialer_http_curlm_timer_schedule);
|
|
|
|
curl_multi_setopt(cm->multi, CURLMOPT_TIMERDATA, cm);
|
|
|
|
}
|
|
|
|
|
|
|
|
r = curl_multi_add_handle(cm->multi, handle);
|
|
|
|
if (r != CURLM_OK)
|
|
|
|
{
|
|
|
|
ERR("could not register curl multi handle %p: %s",
|
|
|
|
handle, curl_multi_strerror(r));
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
cm->users = eina_list_append(cm->users, o);
|
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_efl_net_dialer_http_curlm_remove(Efl_Net_Dialer_Http_Curlm *cm, Eo *o, CURL *handle)
|
|
|
|
{
|
|
|
|
CURLMcode r = curl_multi_remove_handle(cm->multi, handle);
|
2016-08-29 10:49:03 -07:00
|
|
|
|
|
|
|
DBG("removed handle cm=%p multi=%p easy=%p: %s",
|
|
|
|
cm, cm->multi, handle, curl_multi_strerror(r));
|
2016-08-19 20:55:26 -07:00
|
|
|
if (r != CURLM_OK)
|
|
|
|
{
|
|
|
|
ERR("could not unregister curl multi handle %p: %s",
|
|
|
|
handle, curl_multi_strerror(r));
|
|
|
|
}
|
|
|
|
|
|
|
|
cm->users = eina_list_remove(cm->users, o);
|
|
|
|
if (!cm->users)
|
|
|
|
{
|
2016-08-29 10:49:03 -07:00
|
|
|
DBG("cleaned up cm=%p multi=%p", cm, cm->multi);
|
2016-08-19 20:55:26 -07:00
|
|
|
curl_multi_cleanup(cm->multi);
|
|
|
|
cm->multi = NULL;
|
2016-08-29 10:49:03 -07:00
|
|
|
|
|
|
|
if (cm->timer)
|
|
|
|
{
|
|
|
|
efl_del(cm->timer);
|
|
|
|
cm->timer = NULL;
|
|
|
|
}
|
2016-08-19 20:55:26 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: move this per-loop when multiple main loops are possible
|
|
|
|
static Efl_Net_Dialer_Http_Curlm _cm_global;
|
|
|
|
|
|
|
|
static long
|
|
|
|
_efl_net_http_version_to_curl(Efl_Net_Http_Version version)
|
|
|
|
{
|
|
|
|
switch (version)
|
|
|
|
{
|
|
|
|
case EFL_NET_HTTP_VERSION_V1_0: return CURL_HTTP_VERSION_1_0;
|
|
|
|
case EFL_NET_HTTP_VERSION_V1_1: return CURL_HTTP_VERSION_1_1;
|
|
|
|
case EFL_NET_HTTP_VERSION_V2_0: return CURL_HTTP_VERSION_2_0;
|
|
|
|
default:
|
|
|
|
ERR("unsupported HTTP version code %d", version);
|
|
|
|
return CURL_HTTP_VERSION_NONE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static Efl_Net_Http_Version
|
|
|
|
_efl_net_http_version_from_curl(long version)
|
|
|
|
{
|
|
|
|
switch (version)
|
|
|
|
{
|
|
|
|
case CURL_HTTP_VERSION_1_0: return EFL_NET_HTTP_VERSION_V1_0;
|
|
|
|
case CURL_HTTP_VERSION_1_1: return EFL_NET_HTTP_VERSION_V1_1;
|
|
|
|
case CURL_HTTP_VERSION_2_0: return EFL_NET_HTTP_VERSION_V2_0;
|
|
|
|
default:
|
|
|
|
ERR("unsupported HTTP version from CURL: %ld", version);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static Efl_Net_Dialer_Http_Primary_Mode
|
|
|
|
_efl_net_dialer_http_primary_mode_effective_get(const Efl_Net_Dialer_Http_Data *pd)
|
|
|
|
{
|
|
|
|
if (pd->primary_mode == EFL_NET_DIALER_HTTP_PRIMARY_MODE_DOWNLOAD)
|
|
|
|
return EFL_NET_DIALER_HTTP_PRIMARY_MODE_DOWNLOAD;
|
|
|
|
else if (pd->primary_mode == EFL_NET_DIALER_HTTP_PRIMARY_MODE_UPLOAD)
|
|
|
|
return EFL_NET_DIALER_HTTP_PRIMARY_MODE_UPLOAD;
|
|
|
|
else if (strcasecmp(pd->method, "PUT") == 0)
|
|
|
|
return EFL_NET_DIALER_HTTP_PRIMARY_MODE_UPLOAD;
|
|
|
|
else
|
|
|
|
return EFL_NET_DIALER_HTTP_PRIMARY_MODE_DOWNLOAD;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long
|
|
|
|
_efl_net_http_authentication_method_to_curl(Efl_Net_Http_Authentication_Method method, Eina_Bool restricted)
|
|
|
|
{
|
|
|
|
long flags = 0;
|
|
|
|
|
|
|
|
switch (method) {
|
|
|
|
case EFL_NET_HTTP_AUTHENTICATION_METHOD_NONE:
|
|
|
|
flags |= CURLAUTH_NONE;
|
|
|
|
break;
|
|
|
|
case EFL_NET_HTTP_AUTHENTICATION_METHOD_BASIC:
|
|
|
|
flags |= CURLAUTH_BASIC;
|
|
|
|
break;
|
|
|
|
case EFL_NET_HTTP_AUTHENTICATION_METHOD_DIGEST:
|
|
|
|
flags |= CURLAUTH_DIGEST;
|
|
|
|
break;
|
|
|
|
case EFL_NET_HTTP_AUTHENTICATION_METHOD_NEGOTIATE:
|
|
|
|
flags |= CURLAUTH_NEGOTIATE;
|
|
|
|
break;
|
|
|
|
case EFL_NET_HTTP_AUTHENTICATION_METHOD_NTLM:
|
|
|
|
flags |= CURLAUTH_NTLM;
|
|
|
|
break;
|
|
|
|
case EFL_NET_HTTP_AUTHENTICATION_METHOD_NTLM_WINBIND:
|
|
|
|
flags |= CURLAUTH_NTLM_WB;
|
|
|
|
break;
|
|
|
|
case EFL_NET_HTTP_AUTHENTICATION_METHOD_ANY_SAFE:
|
|
|
|
flags |= CURLAUTH_ANYSAFE;
|
|
|
|
break;
|
|
|
|
case EFL_NET_HTTP_AUTHENTICATION_METHOD_ANY:
|
|
|
|
flags |= CURLAUTH_ANY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (restricted) flags |= CURLAUTH_ONLY;
|
|
|
|
|
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_secure_free(char **pstr)
|
|
|
|
{
|
|
|
|
char *str = *pstr;
|
|
|
|
if (!str) return;
|
|
|
|
memset(str, 0, strlen(str));
|
|
|
|
__asm__ __volatile__ ("" : : "g" (str) : "memory");
|
|
|
|
free(str);
|
|
|
|
*pstr = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
_efl_net_dialer_http_debug(CURL *easy EINA_UNUSED, curl_infotype type, char *msg, size_t size, void *data)
|
|
|
|
{
|
|
|
|
Eo *o = data;
|
|
|
|
const char *cls = efl_class_name_get(efl_class_get(o));
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case CURLINFO_TEXT:
|
|
|
|
while ((size > 0) && isspace(msg[size - 1])) size--;
|
|
|
|
DBG("%s=%p curl said: %.*s", cls, o, (int)size, msg);
|
|
|
|
break;
|
|
|
|
case CURLINFO_HEADER_IN:
|
|
|
|
while ((size > 0) && isspace(msg[size - 1])) size--;
|
|
|
|
DBG("%s=%p received header: %.*s", cls, o, (int)size, msg);
|
|
|
|
break;
|
|
|
|
case CURLINFO_HEADER_OUT:
|
|
|
|
while ((size > 0) && isspace(msg[size - 1])) size--;
|
|
|
|
DBG("%s=%p sent header: %.*s", cls, o, (int)size, msg);
|
|
|
|
break;
|
|
|
|
case CURLINFO_DATA_IN:
|
|
|
|
DBG("%s=%p received %zd bytes", cls, o, size);
|
|
|
|
break;
|
|
|
|
case CURLINFO_DATA_OUT:
|
|
|
|
DBG("%s=%p sent %zd bytes", cls, o, size);
|
|
|
|
break;
|
|
|
|
case CURLINFO_SSL_DATA_IN:
|
|
|
|
DBG("%s=%p received SSL %zd bytes", cls, o, size);
|
|
|
|
break;
|
|
|
|
case CURLINFO_SSL_DATA_OUT:
|
|
|
|
DBG("%s=%p sent SSL %zd bytes", cls, o, size);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
DBG("%s=%p unkown debug type %d, msg=%p, size=%zd", cls, o, type, msg, size);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
_efl_net_dialer_http_xferinfo(void *data, int64_t dltotal, int64_t dlnow, int64_t ultotal, int64_t ulnow)
|
|
|
|
{
|
|
|
|
Eo *o = data;
|
|
|
|
Efl_Net_Dialer_Http_Data *pd = efl_data_scope_get(o, MY_CLASS);
|
|
|
|
|
|
|
|
pd->progress.download.total = dltotal;
|
|
|
|
pd->progress.download.now = dlnow;
|
|
|
|
pd->progress.upload.total = ultotal;
|
|
|
|
pd->progress.upload.now = ulnow;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_efl_net_dialer_http_connected(Eo *o, Efl_Net_Dialer_Http_Data *pd)
|
|
|
|
{
|
|
|
|
CURLcode r;
|
|
|
|
long n;
|
|
|
|
const char *s;
|
|
|
|
|
|
|
|
r = curl_easy_getinfo(pd->easy, CURLINFO_RESPONSE_CODE, &n);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not get response code: %s", o, curl_easy_strerror(r));
|
|
|
|
else
|
|
|
|
pd->response.status = n;
|
|
|
|
|
|
|
|
r = curl_easy_getinfo(pd->easy, CURLINFO_EFFECTIVE_URL, &s);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not get effective url: %s", o, curl_easy_strerror(r));
|
|
|
|
else
|
|
|
|
efl_net_socket_address_remote_set(o, s);
|
|
|
|
|
|
|
|
r = curl_easy_getinfo(pd->easy, CURLINFO_LOCAL_IP, &s);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not get local IP: %s", o, curl_easy_strerror(r));
|
|
|
|
else
|
|
|
|
{
|
|
|
|
r = curl_easy_getinfo(pd->easy, CURLINFO_LOCAL_PORT, &n);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not get local port: %s", o, curl_easy_strerror(r));
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char buf[256];
|
|
|
|
|
|
|
|
if (strchr(s, ':'))
|
|
|
|
snprintf(buf, sizeof(buf), "[%s]:%ld", s, n);
|
|
|
|
else
|
|
|
|
snprintf(buf, sizeof(buf), "%s:%ld", s, n);
|
|
|
|
|
|
|
|
efl_net_socket_address_local_set(o, buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
r = curl_easy_getinfo(pd->easy, CURLINFO_HTTP_VERSION, &n);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not get effective HTTP version: %s", o, curl_easy_strerror(r));
|
|
|
|
else
|
|
|
|
pd->version = _efl_net_http_version_from_curl(n);
|
|
|
|
|
|
|
|
pd->pending_headers_done = EINA_TRUE;
|
|
|
|
efl_net_dialer_connected_set(o, EINA_TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_efl_net_dialer_http_headers_done(Eo *o, Efl_Net_Dialer_Http_Data *pd)
|
|
|
|
{
|
|
|
|
double d;
|
|
|
|
const char *s;
|
|
|
|
CURLcode r;
|
|
|
|
|
|
|
|
r = curl_easy_getinfo(pd->easy, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
{
|
|
|
|
DBG("could not query content-length for reponse: %s",
|
|
|
|
curl_easy_strerror(r));
|
|
|
|
d = -1;
|
|
|
|
}
|
|
|
|
efl_net_dialer_http_response_content_length_set(o, d);
|
|
|
|
|
|
|
|
r = curl_easy_getinfo(pd->easy, CURLINFO_CONTENT_TYPE, &s);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
{
|
|
|
|
DBG("could not query content-type for response: %s",
|
|
|
|
curl_easy_strerror(r));
|
|
|
|
s = NULL;
|
|
|
|
}
|
|
|
|
efl_net_dialer_http_response_content_type_set(o, s);
|
|
|
|
|
|
|
|
pd->pending_headers_done = EINA_FALSE;
|
2016-08-29 16:13:22 -07:00
|
|
|
efl_event_callback_call(o, EFL_NET_DIALER_HTTP_EVENT_HEADERS_DONE, NULL);
|
2016-08-19 20:55:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* take data from internal buffer filled with efl_io_writer_write()
|
|
|
|
* and send to curl.
|
|
|
|
*/
|
|
|
|
static size_t
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
_efl_net_dialer_http_send_data_safe(Eo *o, Efl_Net_Dialer_Http_Data *pd, Eina_Rw_Slice rw_slice)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
if (pd->pending_headers_done) _efl_net_dialer_http_headers_done(o, pd);
|
|
|
|
if ((!pd->send.slice.mem) || (pd->send.slice.len == 0))
|
|
|
|
{
|
|
|
|
efl_io_writer_can_write_set(o, EINA_TRUE);
|
|
|
|
pd->pause |= CURLPAUSE_SEND;
|
|
|
|
return CURL_READFUNC_PAUSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
rw_slice = eina_rw_slice_copy(rw_slice, pd->send.slice);
|
|
|
|
pd->send.slice.len -= rw_slice.len;
|
|
|
|
pd->send.slice.bytes += rw_slice.len;
|
|
|
|
|
|
|
|
if (rw_slice.len == 0)
|
|
|
|
{
|
|
|
|
pd->pause |= CURLPAUSE_SEND;
|
|
|
|
return CURL_READFUNC_PAUSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rw_slice.len;
|
|
|
|
}
|
|
|
|
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
static CURL *
|
|
|
|
_efl_net_dialer_http_curl_safe_begin(Eo *o, Efl_Net_Dialer_Http_Data *pd)
|
|
|
|
{
|
|
|
|
if (!o || !pd)
|
|
|
|
return NULL;
|
|
|
|
if (efl_io_closer_closed_get(o))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
pd->in_curl_callback++;
|
|
|
|
efl_ref(o);
|
|
|
|
|
|
|
|
return pd->easy;
|
|
|
|
}
|
|
|
|
|
2017-08-30 13:24:27 -07:00
|
|
|
static Eina_Value
|
|
|
|
_efl_net_dialer_http_curl_cleanup(void *data, const Eina_Value v)
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
{
|
|
|
|
CURL *easy = data;
|
|
|
|
DBG("cleanup curl=%p", easy);
|
|
|
|
curl_easy_cleanup(easy);
|
2017-08-30 13:24:27 -07:00
|
|
|
return v;
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
}
|
|
|
|
|
2017-08-30 13:24:27 -07:00
|
|
|
static Eina_Value
|
|
|
|
_efl_net_dialer_http_curl_cleanup_error(void *data, const Eina_Error err)
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
{
|
|
|
|
CURL *easy = data;
|
2017-08-30 13:24:27 -07:00
|
|
|
DBG("cleanup curl=%p, promise error=%d '%s'", easy, err, eina_error_msg_get(err));
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
curl_easy_cleanup(easy);
|
2017-08-30 13:24:27 -07:00
|
|
|
return EINA_VALUE_EMPTY;
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_efl_net_dialer_http_curl_safe_end(Eo *o, Efl_Net_Dialer_Http_Data *pd, CURL *easy)
|
|
|
|
{
|
2017-08-30 13:24:27 -07:00
|
|
|
Eina_Future *f;
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
int refs;
|
|
|
|
|
2017-11-08 01:30:42 -08:00
|
|
|
refs = efl_ref_count(o);
|
2016-12-11 21:16:07 -08:00
|
|
|
if (refs >= 2)
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
{
|
|
|
|
pd->in_curl_callback--;
|
2016-12-09 16:10:28 -08:00
|
|
|
efl_unref(o);
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-12-09 16:10:28 -08:00
|
|
|
if (pd->pending_close)
|
2017-08-30 13:24:27 -07:00
|
|
|
eina_future_cancel(pd->pending_close);
|
2016-12-09 16:10:28 -08:00
|
|
|
|
|
|
|
if (!pd->easy)
|
|
|
|
{
|
|
|
|
efl_unref(o);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pd->easy = NULL;
|
|
|
|
efl_unref(o);
|
|
|
|
|
|
|
|
curl_easy_setopt(easy, CURLOPT_PRIVATE, NULL);
|
|
|
|
curl_easy_setopt(easy, CURLOPT_DEBUGFUNCTION, NULL);
|
|
|
|
curl_easy_setopt(easy, CURLOPT_DEBUGDATA, NULL);
|
|
|
|
curl_easy_setopt(easy, CURLOPT_XFERINFOFUNCTION, NULL);
|
|
|
|
curl_easy_setopt(easy, CURLOPT_XFERINFODATA, NULL);
|
|
|
|
|
|
|
|
curl_easy_setopt(easy, CURLOPT_HEADERFUNCTION, NULL);
|
|
|
|
curl_easy_setopt(easy, CURLOPT_HEADERDATA, NULL);
|
|
|
|
curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, NULL);
|
|
|
|
curl_easy_setopt(easy, CURLOPT_WRITEDATA, NULL);
|
|
|
|
curl_easy_setopt(easy, CURLOPT_READFUNCTION, NULL);
|
|
|
|
curl_easy_setopt(easy, CURLOPT_READDATA, NULL);
|
|
|
|
|
|
|
|
curl_easy_setopt(easy, CURLOPT_OPENSOCKETFUNCTION, NULL);
|
|
|
|
curl_easy_setopt(easy, CURLOPT_OPENSOCKETDATA, NULL);
|
|
|
|
|
|
|
|
curl_easy_setopt(easy, CURLOPT_NOPROGRESS, 1L);
|
|
|
|
curl_easy_setopt(easy, CURLOPT_VERBOSE, 0L);
|
|
|
|
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
/* object deleted from CURL callback, CURL* easy was
|
|
|
|
* dissociated and we must delete it ourselves.
|
|
|
|
*/
|
2018-01-03 12:49:10 -08:00
|
|
|
f = efl_loop_job(efl_loop_get(o));
|
2017-08-30 13:24:27 -07:00
|
|
|
eina_future_then_from_desc(f, eina_future_cb_easy(.success = _efl_net_dialer_http_curl_cleanup,
|
|
|
|
.error = _efl_net_dialer_http_curl_cleanup_error,
|
|
|
|
.data = easy));
|
2016-09-15 21:48:42 -07:00
|
|
|
DBG("dialer=%p deleted from CURL callback, cleanup curl from job=%p.", o, f);
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
_efl_net_dialer_http_send_data(char *buffer, size_t count, size_t nitems, void *data)
|
|
|
|
{
|
|
|
|
Eo *o = data;
|
|
|
|
Efl_Net_Dialer_Http_Data *pd = efl_data_scope_get(o, MY_CLASS);
|
|
|
|
Eina_Rw_Slice rw_slice = {.mem = buffer, .len = count * nitems};
|
|
|
|
CURL *easy;
|
|
|
|
size_t ret;
|
|
|
|
|
|
|
|
easy = _efl_net_dialer_http_curl_safe_begin(o, pd);
|
|
|
|
if (!easy) return CURL_READFUNC_PAUSE;
|
|
|
|
|
|
|
|
ret = _efl_net_dialer_http_send_data_safe(o, pd, rw_slice);
|
|
|
|
|
|
|
|
_efl_net_dialer_http_curl_safe_end(o, pd, easy);
|
|
|
|
DBG("dialer=%p in=%zd used=%zd", o, rw_slice.len, ret);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-08-19 20:55:26 -07:00
|
|
|
/* take data from curl into our internal buffer until
|
|
|
|
* efl_io_reader_read() consumes it
|
|
|
|
*/
|
|
|
|
static size_t
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
_efl_net_dialer_http_receive_data_safe(Eo *o, Efl_Net_Dialer_Http_Data *pd, Eina_Slice ro_slice)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
Eina_Rw_Slice rw_slice = {
|
|
|
|
.bytes = pd->recv.bytes + pd->recv.used,
|
|
|
|
.len = pd->recv.limit - pd->recv.used,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (pd->pending_headers_done) _efl_net_dialer_http_headers_done(o, pd);
|
|
|
|
|
|
|
|
if (ro_slice.len == 0)
|
|
|
|
{
|
|
|
|
efl_io_reader_can_read_set(o, EINA_FALSE);
|
|
|
|
efl_io_reader_eos_set(o, EINA_TRUE);
|
|
|
|
return ro_slice.len;
|
|
|
|
}
|
|
|
|
|
2016-09-01 08:18:40 -07:00
|
|
|
if (rw_slice.len < ro_slice.len)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
2016-09-01 08:18:40 -07:00
|
|
|
/* throttle CURL and let users read */
|
|
|
|
DBG("dialer=%p in=%zd, available %zd (limit=%zd)",
|
|
|
|
o, ro_slice.len, pd->recv.limit - pd->recv.used, pd->recv.limit);
|
|
|
|
efl_io_reader_can_read_set(o, EINA_TRUE);
|
2016-08-19 20:55:26 -07:00
|
|
|
pd->pause |= CURLPAUSE_RECV;
|
|
|
|
return CURL_WRITEFUNC_PAUSE;
|
|
|
|
}
|
|
|
|
rw_slice = eina_rw_slice_copy(rw_slice, ro_slice);
|
|
|
|
pd->recv.used += rw_slice.len;
|
|
|
|
|
|
|
|
// TODO: optimize readers from immediate event
|
|
|
|
// with pd->tmp_buf + pd->tmp_buflen that is read after
|
|
|
|
// pd->buf.recv inside _efl_io_reader_read()
|
|
|
|
efl_io_reader_can_read_set(o, EINA_TRUE);
|
|
|
|
|
|
|
|
return rw_slice.len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
_efl_net_dialer_http_receive_data(const void *buffer, size_t count, size_t nitems, void *data)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
Eo *o = data;
|
|
|
|
Efl_Net_Dialer_Http_Data *pd = efl_data_scope_get(o, MY_CLASS);
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
Eina_Slice ro_slice = {
|
|
|
|
.bytes = buffer,
|
|
|
|
.len = count * nitems,
|
|
|
|
};
|
|
|
|
CURL *easy;
|
|
|
|
size_t ret;
|
|
|
|
|
|
|
|
easy = _efl_net_dialer_http_curl_safe_begin(o, pd);
|
|
|
|
if (!easy) return CURL_WRITEFUNC_PAUSE;
|
|
|
|
|
|
|
|
ret = _efl_net_dialer_http_receive_data_safe(o, pd, ro_slice);
|
|
|
|
|
|
|
|
_efl_net_dialer_http_curl_safe_end(o, pd, easy);
|
2016-09-01 08:18:40 -07:00
|
|
|
if (ret == CURL_WRITEFUNC_PAUSE)
|
|
|
|
DBG("dialer=%p in=%zd is now paused", o, ro_slice.len);
|
|
|
|
else
|
|
|
|
DBG("dialer=%p in=%zd used=%zd", o, ro_slice.len, ret);
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
_efl_net_dialer_http_receive_header_safe(Eo *o, Efl_Net_Dialer_Http_Data *pd, Eina_Slice ro_slice)
|
|
|
|
{
|
2016-08-19 20:55:26 -07:00
|
|
|
Efl_Net_Http_Header *h;
|
|
|
|
char *p;
|
|
|
|
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
if (ro_slice.len == 0)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
if (!pd->connected) _efl_net_dialer_http_connected(o, pd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
h = malloc(sizeof(Efl_Net_Http_Header) + ro_slice.len + 1);
|
2016-08-19 20:55:26 -07:00
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(h, 0);
|
|
|
|
|
|
|
|
h->key = p = (char *)h + sizeof(Efl_Net_Http_Header);
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
memcpy(p, ro_slice.mem, ro_slice.len);
|
|
|
|
p[ro_slice.len] = '\0';
|
2016-08-19 20:55:26 -07:00
|
|
|
|
|
|
|
h->value = p = strchr(p, ':');
|
|
|
|
if (!p)
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
p = (char *)h->key + ro_slice.len - 1;
|
2016-08-19 20:55:26 -07:00
|
|
|
else
|
|
|
|
{
|
|
|
|
char *t;
|
|
|
|
|
|
|
|
p[0] = '\0';
|
|
|
|
p--;
|
|
|
|
h->value++;
|
|
|
|
while (h->value[0] && isspace(h->value[0]))
|
|
|
|
h->value++;
|
|
|
|
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
t = (char *)h->key + ro_slice.len - 1;
|
2016-08-19 20:55:26 -07:00
|
|
|
while ((t > h->value) && isspace(t[0]))
|
|
|
|
{
|
|
|
|
t[0] = '\0';
|
|
|
|
t--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while (h->key[0] && isspace(h->key[0]))
|
|
|
|
h->key++;
|
|
|
|
|
|
|
|
while ((p > h->key) && isspace(p[0]))
|
|
|
|
{
|
|
|
|
p[0] = '\0';
|
|
|
|
p--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((!h->key[0]) && (!h->value || !h->value[0]))
|
|
|
|
{
|
2016-08-24 10:55:09 -07:00
|
|
|
if (!pd->connected) _efl_net_dialer_http_connected(o, pd);
|
|
|
|
if (pd->pending_headers_done) _efl_net_dialer_http_headers_done(o, pd);
|
2016-08-19 20:55:26 -07:00
|
|
|
free(h);
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
return ro_slice.len;
|
2016-08-19 20:55:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!h->value)
|
|
|
|
{
|
|
|
|
if (strncmp(h->key, "HTTP/", strlen("HTTP/")) != 0)
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
DBG("unexpected header '" EINA_SLICE_STR_FMT "'", EINA_SLICE_STR_PRINT(ro_slice));
|
2016-08-19 20:55:26 -07:00
|
|
|
else
|
|
|
|
{
|
|
|
|
h->value = h->key;
|
|
|
|
h->key = NULL; /* documented as a start of a new request */
|
|
|
|
/* notify headers of the previous request */
|
|
|
|
if (pd->pending_headers_done) _efl_net_dialer_http_headers_done(o, pd);
|
|
|
|
/* reload properties for the previous request */
|
|
|
|
_efl_net_dialer_http_connected(o, pd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pd->response.headers = eina_list_append(pd->response.headers, h);
|
|
|
|
if (!h->key)
|
|
|
|
pd->response.last_request_header = eina_list_last(pd->response.headers);
|
|
|
|
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
return ro_slice.len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
_efl_net_dialer_http_receive_header(const char *buffer, size_t count, size_t nitems, void *data)
|
|
|
|
{
|
|
|
|
Eo *o = data;
|
|
|
|
Efl_Net_Dialer_Http_Data *pd = efl_data_scope_get(o, MY_CLASS);
|
|
|
|
Eina_Slice ro_slice = {
|
|
|
|
.mem = buffer,
|
|
|
|
.len = count * nitems,
|
|
|
|
};
|
|
|
|
CURL *easy;
|
|
|
|
size_t ret;
|
|
|
|
|
|
|
|
easy = _efl_net_dialer_http_curl_safe_begin(o, pd);
|
|
|
|
if (!easy) return 0;
|
|
|
|
|
|
|
|
ret = _efl_net_dialer_http_receive_header_safe(o, pd, ro_slice);
|
|
|
|
|
|
|
|
_efl_net_dialer_http_curl_safe_end(o, pd, easy);
|
|
|
|
DBG("dialer=%p in=%zd used=%zd", o, ro_slice.len, ret);
|
|
|
|
|
|
|
|
return ret;
|
2016-08-19 20:55:26 -07:00
|
|
|
}
|
|
|
|
|
efl_net_dialer_http: keep fd directly.
provide curl with CURLOPT_OPENSOCKETFUNCTION and keep the fd in our
private data.
This is required because on _efl_net_dialer_http_efl_io_writer_write()
we may have no fdhandler.
It happened to me while implementing the WebSocket that uses a
bi-directional communication on top of HTTP and the server sent the
whole message, CURL reads:
recvfrom(7, "...", 16384, 0, NULL, NULL) = 86
recvfrom(7, "", 16384, 0, NULL, NULL) = 0
After the empty (second) recvfrom(), CURL will remove the fdhandler:
DBG:ecore_con lib/ecore_con/efl_net_dialer_http.c:482 _efl_net_dialer_http_curlm_socket_manage() dialer=0x4000000040000005 fdhandler=(nil), fd=7, curl_easy=0x5561846ca8d0, flags=0x4
However I should be able to write to this socket, in my case I need to
reply to a PING request with a PONG.
2016-08-29 19:35:31 -07:00
|
|
|
static curl_socket_t
|
|
|
|
_efl_net_dialer_http_socket_open(void *data, curlsocktype purpose EINA_UNUSED, struct curl_sockaddr *addr)
|
|
|
|
{
|
|
|
|
Eo *o = data;
|
|
|
|
Efl_Net_Dialer_Http_Data *pd = efl_data_scope_get(o, MY_CLASS);
|
|
|
|
|
2016-09-12 08:23:29 -07:00
|
|
|
pd->fd = efl_net_socket4(addr->family, addr->socktype, addr->protocol, pd->close_on_exec);
|
2016-10-22 08:15:16 -07:00
|
|
|
if (pd->fd == INVALID_SOCKET)
|
efl_net_dialer_http: keep fd directly.
provide curl with CURLOPT_OPENSOCKETFUNCTION and keep the fd in our
private data.
This is required because on _efl_net_dialer_http_efl_io_writer_write()
we may have no fdhandler.
It happened to me while implementing the WebSocket that uses a
bi-directional communication on top of HTTP and the server sent the
whole message, CURL reads:
recvfrom(7, "...", 16384, 0, NULL, NULL) = 86
recvfrom(7, "", 16384, 0, NULL, NULL) = 0
After the empty (second) recvfrom(), CURL will remove the fdhandler:
DBG:ecore_con lib/ecore_con/efl_net_dialer_http.c:482 _efl_net_dialer_http_curlm_socket_manage() dialer=0x4000000040000005 fdhandler=(nil), fd=7, curl_easy=0x5561846ca8d0, flags=0x4
However I should be able to write to this socket, in my case I need to
reply to a PING request with a PONG.
2016-08-29 19:35:31 -07:00
|
|
|
ERR("could not create curl socket family=%d, type=%d, protocol=%d",
|
|
|
|
addr->family, addr->socktype, addr->protocol);
|
|
|
|
else
|
2016-11-18 06:17:08 -08:00
|
|
|
DBG("socket(%d, %d, %d) = " SOCKET_FMT,
|
efl_net_dialer_http: keep fd directly.
provide curl with CURLOPT_OPENSOCKETFUNCTION and keep the fd in our
private data.
This is required because on _efl_net_dialer_http_efl_io_writer_write()
we may have no fdhandler.
It happened to me while implementing the WebSocket that uses a
bi-directional communication on top of HTTP and the server sent the
whole message, CURL reads:
recvfrom(7, "...", 16384, 0, NULL, NULL) = 86
recvfrom(7, "", 16384, 0, NULL, NULL) = 0
After the empty (second) recvfrom(), CURL will remove the fdhandler:
DBG:ecore_con lib/ecore_con/efl_net_dialer_http.c:482 _efl_net_dialer_http_curlm_socket_manage() dialer=0x4000000040000005 fdhandler=(nil), fd=7, curl_easy=0x5561846ca8d0, flags=0x4
However I should be able to write to this socket, in my case I need to
reply to a PING request with a PONG.
2016-08-29 19:35:31 -07:00
|
|
|
addr->family, addr->socktype, addr->protocol, pd->fd);
|
|
|
|
|
|
|
|
return pd->fd;
|
|
|
|
}
|
|
|
|
|
2016-08-19 20:55:26 -07:00
|
|
|
EOLIAN static Efl_Object *
|
|
|
|
_efl_net_dialer_http_efl_object_constructor(Eo *o, Efl_Net_Dialer_Http_Data *pd)
|
|
|
|
{
|
|
|
|
if (!_c_init())
|
|
|
|
{
|
|
|
|
ERR("dialer=%p failed to initialize CURL", o);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
pd->primary_mode = EFL_NET_DIALER_HTTP_PRIMARY_MODE_AUTO;
|
|
|
|
|
|
|
|
pd->recv.limit = EFL_NET_DIALER_HTTP_BUFFER_RECEIVE_SIZE;
|
|
|
|
pd->recv.used = 0;
|
|
|
|
pd->recv.bytes = malloc(pd->recv.limit);
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(pd->recv.bytes, NULL);
|
|
|
|
|
|
|
|
pd->easy = curl_easy_init();
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(pd->easy, NULL);
|
|
|
|
|
|
|
|
curl_easy_setopt(pd->easy, CURLOPT_PRIVATE, o);
|
|
|
|
|
|
|
|
curl_easy_setopt(pd->easy, CURLOPT_DEBUGFUNCTION, _efl_net_dialer_http_debug);
|
|
|
|
curl_easy_setopt(pd->easy, CURLOPT_DEBUGDATA, o);
|
|
|
|
curl_easy_setopt(pd->easy, CURLOPT_XFERINFOFUNCTION, _efl_net_dialer_http_xferinfo);
|
|
|
|
curl_easy_setopt(pd->easy, CURLOPT_XFERINFODATA, o);
|
|
|
|
|
|
|
|
curl_easy_setopt(pd->easy, CURLOPT_HEADERFUNCTION, _efl_net_dialer_http_receive_header);
|
|
|
|
curl_easy_setopt(pd->easy, CURLOPT_HEADERDATA, o);
|
|
|
|
curl_easy_setopt(pd->easy, CURLOPT_WRITEFUNCTION, _efl_net_dialer_http_receive_data);
|
|
|
|
curl_easy_setopt(pd->easy, CURLOPT_WRITEDATA, o);
|
2016-09-01 08:18:40 -07:00
|
|
|
curl_easy_setopt(pd->easy, CURLOPT_BUFFERSIZE, (long)pd->recv.limit);
|
2016-08-19 20:55:26 -07:00
|
|
|
curl_easy_setopt(pd->easy, CURLOPT_READFUNCTION, _efl_net_dialer_http_send_data);
|
|
|
|
curl_easy_setopt(pd->easy, CURLOPT_READDATA, o);
|
|
|
|
|
efl_net_dialer_http: keep fd directly.
provide curl with CURLOPT_OPENSOCKETFUNCTION and keep the fd in our
private data.
This is required because on _efl_net_dialer_http_efl_io_writer_write()
we may have no fdhandler.
It happened to me while implementing the WebSocket that uses a
bi-directional communication on top of HTTP and the server sent the
whole message, CURL reads:
recvfrom(7, "...", 16384, 0, NULL, NULL) = 86
recvfrom(7, "", 16384, 0, NULL, NULL) = 0
After the empty (second) recvfrom(), CURL will remove the fdhandler:
DBG:ecore_con lib/ecore_con/efl_net_dialer_http.c:482 _efl_net_dialer_http_curlm_socket_manage() dialer=0x4000000040000005 fdhandler=(nil), fd=7, curl_easy=0x5561846ca8d0, flags=0x4
However I should be able to write to this socket, in my case I need to
reply to a PING request with a PONG.
2016-08-29 19:35:31 -07:00
|
|
|
curl_easy_setopt(pd->easy, CURLOPT_OPENSOCKETFUNCTION, _efl_net_dialer_http_socket_open);
|
|
|
|
curl_easy_setopt(pd->easy, CURLOPT_OPENSOCKETDATA, o);
|
|
|
|
|
2016-08-19 20:55:26 -07:00
|
|
|
curl_easy_setopt(pd->easy, CURLOPT_NOPROGRESS, 0L);
|
2016-09-01 10:21:55 -07:00
|
|
|
curl_easy_setopt(pd->easy, CURLOPT_NOSIGNAL, 1L);
|
2016-08-19 20:55:26 -07:00
|
|
|
|
|
|
|
curl_easy_setopt(pd->easy, CURLOPT_VERBOSE, (long)(eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG)));
|
|
|
|
|
|
|
|
o = efl_constructor(efl_super(o, MY_CLASS));
|
|
|
|
if (!o) return NULL;
|
|
|
|
|
|
|
|
efl_net_dialer_http_method_set(o, "GET");
|
|
|
|
efl_net_dialer_http_version_set(o, EFL_NET_HTTP_VERSION_V1_1);
|
|
|
|
efl_net_dialer_http_allow_redirects_set(o, EINA_TRUE);
|
2016-11-28 20:16:47 -08:00
|
|
|
efl_net_dialer_http_ssl_verify_set(o, EINA_TRUE, EINA_TRUE);
|
2016-08-19 20:55:26 -07:00
|
|
|
efl_net_dialer_timeout_dial_set(o, 30.0);
|
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
2018-04-17 16:17:29 -07:00
|
|
|
_efl_net_dialer_http_efl_object_invalidate(Eo *o, Efl_Net_Dialer_Http_Data *pd)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
2016-09-19 06:00:36 -07:00
|
|
|
if (pd->libproxy_thread)
|
|
|
|
{
|
|
|
|
ecore_thread_cancel(pd->libproxy_thread);
|
|
|
|
pd->libproxy_thread = NULL;
|
|
|
|
}
|
2016-09-20 11:00:17 -07:00
|
|
|
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
if (pd->in_curl_callback)
|
|
|
|
{
|
2016-12-09 16:10:28 -08:00
|
|
|
EINA_SAFETY_ON_TRUE_RETURN(pd->easy != NULL);
|
|
|
|
EINA_SAFETY_ON_TRUE_RETURN(pd->pending_close != NULL);
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
}
|
|
|
|
else if (pd->pending_close)
|
|
|
|
{
|
2017-08-30 13:24:27 -07:00
|
|
|
eina_future_cancel(pd->pending_close);
|
2016-12-19 14:31:11 -08:00
|
|
|
efl_event_freeze(o);
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
efl_io_closer_close(o);
|
2016-12-19 14:31:11 -08:00
|
|
|
efl_event_thaw(o);
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
}
|
2018-04-17 16:17:29 -07:00
|
|
|
else if (efl_io_closer_close_on_invalidate_get(o) &&
|
2016-09-12 08:23:29 -07:00
|
|
|
(!efl_io_closer_closed_get(o)))
|
2016-12-19 14:31:11 -08:00
|
|
|
{
|
|
|
|
efl_event_freeze(o);
|
|
|
|
efl_io_closer_close(o);
|
|
|
|
efl_event_thaw(o);
|
|
|
|
}
|
2016-08-19 20:55:26 -07:00
|
|
|
|
2018-04-17 16:17:29 -07:00
|
|
|
efl_invalidate(efl_super(o, MY_CLASS));
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_efl_object_destructor(Eo *o, Efl_Net_Dialer_Http_Data *pd)
|
|
|
|
{
|
2016-08-19 20:55:26 -07:00
|
|
|
efl_net_dialer_http_response_headers_clear(o);
|
|
|
|
|
|
|
|
if (pd->easy)
|
|
|
|
{
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
DBG("cleanup curl=%p", pd->easy);
|
2016-08-19 20:55:26 -07:00
|
|
|
curl_easy_cleanup(pd->easy);
|
|
|
|
pd->easy = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
efl_destructor(efl_super(o, MY_CLASS));
|
|
|
|
|
|
|
|
if (pd->recv.bytes)
|
|
|
|
{
|
|
|
|
free(pd->recv.bytes);
|
|
|
|
pd->recv.bytes = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pd->request.headers)
|
|
|
|
{
|
|
|
|
curl_slist_free_all(pd->request.headers);
|
|
|
|
pd->request.headers = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
eina_stringshare_replace(&pd->address_dial, NULL);
|
2016-08-22 14:51:38 -07:00
|
|
|
eina_stringshare_replace(&pd->proxy, NULL);
|
2016-08-22 15:24:13 -07:00
|
|
|
eina_stringshare_replace(&pd->cookie_jar, NULL);
|
2016-08-19 20:55:26 -07:00
|
|
|
eina_stringshare_replace(&pd->address_local, NULL);
|
|
|
|
eina_stringshare_replace(&pd->address_remote, NULL);
|
|
|
|
eina_stringshare_replace(&pd->method, NULL);
|
|
|
|
eina_stringshare_replace(&pd->user_agent, NULL);
|
|
|
|
eina_stringshare_replace(&pd->response.content_type, NULL);
|
|
|
|
eina_stringshare_replace(&pd->authentication.username, NULL);
|
|
|
|
_secure_free(&pd->authentication.password);
|
2016-11-28 20:16:47 -08:00
|
|
|
eina_stringshare_replace(&pd->ssl.ca, NULL);
|
|
|
|
eina_stringshare_replace(&pd->ssl.crl, NULL);
|
2016-08-19 20:55:26 -07:00
|
|
|
}
|
|
|
|
|
2016-09-19 06:00:36 -07:00
|
|
|
static void
|
|
|
|
_efl_net_dialer_http_curl_start(Eo *o, Efl_Net_Dialer_Http_Data *pd)
|
|
|
|
{
|
|
|
|
Efl_Net_Dialer_Http_Curlm *cm;
|
|
|
|
|
|
|
|
// TODO: move this to be per-loop once multiple mainloops are supported
|
|
|
|
// this would need to attach something to the loop
|
|
|
|
cm = &_cm_global;
|
2017-12-17 19:02:45 -08:00
|
|
|
cm->loop = efl_loop_get(o);
|
2016-09-19 06:00:36 -07:00
|
|
|
if (!_efl_net_dialer_http_curlm_add(cm, o, pd->easy))
|
|
|
|
{
|
|
|
|
ERR("dialer=%p could not add curl easy handle to multi manager", o);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pd->cm = cm;
|
|
|
|
DBG("started curl request easy=%p, cm=%p", pd->easy, pd->cm);
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct _Efl_Net_Dialer_Http_Libproxy_Context {
|
|
|
|
Eo *o;
|
|
|
|
char *url;
|
|
|
|
char *proxy;
|
|
|
|
} Efl_Net_Dialer_Http_Libproxy_Context;
|
|
|
|
|
|
|
|
static void
|
2018-06-25 12:13:22 -07:00
|
|
|
_efl_net_dialer_http_libproxy_run(void *data, Ecore_Thread *thread)
|
2016-09-19 06:00:36 -07:00
|
|
|
{
|
|
|
|
Efl_Net_Dialer_Http_Libproxy_Context *ctx = data;
|
2018-06-25 12:13:22 -07:00
|
|
|
char **proxies = ecore_con_libproxy_proxies_get(ctx->url, thread);
|
2016-09-19 06:00:36 -07:00
|
|
|
char **itr;
|
|
|
|
|
|
|
|
if (!proxies) return;
|
|
|
|
|
|
|
|
for (itr = proxies; *itr != NULL; itr++)
|
|
|
|
{
|
|
|
|
if (itr != proxies) free(*itr);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (strcmp(*itr, "direct://") != 0) ctx->proxy = *itr;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* NULL not "" so we let curl use envvars */
|
|
|
|
ctx->proxy = NULL;
|
|
|
|
free(*itr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(proxies);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_efl_net_dialer_http_libproxy_context_free(Efl_Net_Dialer_Http_Libproxy_Context *ctx)
|
|
|
|
{
|
|
|
|
free(ctx->proxy);
|
|
|
|
free(ctx->url);
|
|
|
|
free(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-11-09 18:05:49 -08:00
|
|
|
_efl_net_dialer_http_libproxy_end(void *data, Ecore_Thread *thread)
|
2016-09-19 06:00:36 -07:00
|
|
|
{
|
|
|
|
Efl_Net_Dialer_Http_Libproxy_Context *ctx = data;
|
|
|
|
Eo *o = ctx->o;
|
|
|
|
Efl_Net_Dialer_Http_Data *pd = efl_data_scope_get(o, MY_CLASS);
|
|
|
|
|
2016-11-09 18:05:49 -08:00
|
|
|
EINA_SAFETY_ON_NULL_RETURN(pd);
|
|
|
|
|
|
|
|
if (pd->libproxy_thread == thread)
|
|
|
|
pd->libproxy_thread = NULL;
|
|
|
|
|
2016-09-19 06:00:36 -07:00
|
|
|
if (ctx->proxy)
|
|
|
|
{
|
|
|
|
CURLcode r = curl_easy_setopt(pd->easy, CURLOPT_PROXY, ctx->proxy);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not set proxy to '%s': %s",
|
|
|
|
o, ctx->proxy, curl_easy_strerror(r));
|
|
|
|
else
|
|
|
|
DBG("libproxy said %s for %s", ctx->proxy, pd->address_dial);
|
|
|
|
}
|
|
|
|
|
|
|
|
_efl_net_dialer_http_libproxy_context_free(ctx);
|
|
|
|
|
|
|
|
_efl_net_dialer_http_curl_start(o, pd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_efl_net_dialer_http_libproxy_cancel(void *data, Ecore_Thread *thread EINA_UNUSED)
|
|
|
|
{
|
|
|
|
Efl_Net_Dialer_Http_Libproxy_Context *ctx = data;
|
|
|
|
_efl_net_dialer_http_libproxy_context_free(ctx);
|
|
|
|
}
|
|
|
|
|
2016-08-19 20:55:26 -07:00
|
|
|
EOLIAN static Eina_Error
|
|
|
|
_efl_net_dialer_http_efl_net_dialer_dial(Eo *o, Efl_Net_Dialer_Http_Data *pd, const char *address)
|
|
|
|
{
|
2016-11-28 19:19:40 -08:00
|
|
|
struct curl_slist *sl_it;
|
2016-08-19 20:55:26 -07:00
|
|
|
CURLcode r;
|
|
|
|
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(pd->method, EINVAL);
|
|
|
|
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_net_dialer_connected_get(o), EISCONN);
|
|
|
|
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EBADF);
|
|
|
|
EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->cm != NULL, EALREADY);
|
|
|
|
|
|
|
|
pd->pending_headers_done = EINA_FALSE;
|
|
|
|
|
|
|
|
efl_net_dialer_address_dial_set(o, address);
|
|
|
|
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_HTTPHEADER, pd->request.headers);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
{
|
|
|
|
ERR("dialer=%p could not set HTTP headers: %s",
|
|
|
|
o, curl_easy_strerror(r));
|
|
|
|
return EINVAL;
|
|
|
|
}
|
2016-11-28 19:19:40 -08:00
|
|
|
for (sl_it = pd->request.headers; sl_it != NULL; sl_it = sl_it->next)
|
|
|
|
{
|
|
|
|
#define IS_HEADER(x) (strncasecmp(sl_it->data, x ":", strlen(x ":")) == 0)
|
|
|
|
if (IS_HEADER("Accept-Encoding"))
|
|
|
|
{
|
|
|
|
const char *value = strchr(sl_it->data, ':') + 1;
|
|
|
|
while (*value && isspace(*value))
|
|
|
|
value++;
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_ENCODING, value);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
{
|
|
|
|
ERR("dialer=%p could not set HTTP Accept-Encoding '%s': %s",
|
|
|
|
o, value, curl_easy_strerror(r));
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
DBG("Using Accept-Encoding: %s", value);
|
|
|
|
}
|
|
|
|
else if (IS_HEADER("If-Modified-Since"))
|
|
|
|
{
|
|
|
|
const char *value = strchr(sl_it->data, ':') + 1;
|
|
|
|
long t;
|
|
|
|
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
{
|
|
|
|
ERR("dialer=%p could not set HTTP If-Modified-Since condition: %s",
|
|
|
|
o, curl_easy_strerror(r));
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (*value && isspace(*value))
|
|
|
|
value++;
|
|
|
|
t = curl_getdate(value, NULL);
|
|
|
|
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_TIMEVALUE, t);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
{
|
|
|
|
ERR("dialer=%p could not set HTTP If-Modified-Since value=%ld: %s",
|
|
|
|
o, t, curl_easy_strerror(r));
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
DBG("Using If-Modified-Since: %s [t=%ld]", value, t);
|
|
|
|
}
|
|
|
|
else if (IS_HEADER("If-Unmodified-Since"))
|
|
|
|
{
|
|
|
|
const char *value = strchr(sl_it->data, ':') + 1;
|
|
|
|
long t;
|
|
|
|
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFUNMODSINCE);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
{
|
|
|
|
ERR("dialer=%p could not set HTTP If-Unmodified-Since condition: %s",
|
|
|
|
o, curl_easy_strerror(r));
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (*value && isspace(*value))
|
|
|
|
value++;
|
|
|
|
t = curl_getdate(value, NULL);
|
|
|
|
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_TIMEVALUE, t);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
{
|
|
|
|
ERR("dialer=%p could not set HTTP If-Unmodified-Since value=%ld: %s",
|
|
|
|
o, t, curl_easy_strerror(r));
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
DBG("Using If-Unmodified-Since: %s [t=%ld]", value, t);
|
|
|
|
}
|
|
|
|
#undef IS_HEADER
|
|
|
|
}
|
2016-08-19 20:55:26 -07:00
|
|
|
|
2017-01-08 05:57:54 -08:00
|
|
|
if (!pd->proxy)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
2016-09-19 06:00:36 -07:00
|
|
|
Efl_Net_Dialer_Http_Libproxy_Context *ctx;
|
|
|
|
|
|
|
|
if (pd->libproxy_thread)
|
2016-11-09 18:05:49 -08:00
|
|
|
{
|
|
|
|
ecore_thread_cancel(pd->libproxy_thread);
|
|
|
|
pd->libproxy_thread = NULL;
|
|
|
|
}
|
2016-09-19 06:00:36 -07:00
|
|
|
|
|
|
|
ctx = calloc(1, sizeof(Efl_Net_Dialer_Http_Libproxy_Context));
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, ENOMEM);
|
|
|
|
|
|
|
|
if (strstr(address, "://")) ctx->url = strdup(address);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ctx->url = malloc(strlen("http://") + strlen(address) + 1);
|
|
|
|
if (ctx->url)
|
|
|
|
{
|
|
|
|
memcpy(ctx->url, "http://", strlen("http://"));
|
|
|
|
memcpy(ctx->url + strlen("http://"), address, strlen(address) + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EINA_SAFETY_ON_NULL_GOTO(ctx->url, url_error);
|
|
|
|
|
|
|
|
ctx->o = o;
|
|
|
|
|
2017-02-02 21:57:49 -08:00
|
|
|
pd->libproxy_thread = ecore_thread_feedback_run(_efl_net_dialer_http_libproxy_run,
|
|
|
|
NULL,
|
|
|
|
_efl_net_dialer_http_libproxy_end,
|
|
|
|
_efl_net_dialer_http_libproxy_cancel,
|
|
|
|
ctx, EINA_TRUE);
|
2016-09-19 06:00:36 -07:00
|
|
|
return 0;
|
|
|
|
url_error:
|
|
|
|
free(ctx);
|
|
|
|
return ENOMEM;
|
2016-08-19 20:55:26 -07:00
|
|
|
}
|
|
|
|
|
2016-09-19 06:00:36 -07:00
|
|
|
_efl_net_dialer_http_curl_start(o, pd);
|
2016-08-19 20:55:26 -07:00
|
|
|
|
|
|
|
DBG("HTTP dialer=%p, curl_easy=%p, address='%s'",
|
|
|
|
o, pd->easy, pd->address_dial);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_efl_net_dialer_address_dial_set(Eo *o, Efl_Net_Dialer_Http_Data *pd, const char *address)
|
|
|
|
{
|
|
|
|
CURLcode r;
|
|
|
|
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_URL, address);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not set HTTP URL '%s': %s",
|
|
|
|
o, address, curl_easy_strerror(r));
|
|
|
|
|
|
|
|
eina_stringshare_replace(&pd->address_dial, address);
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static const char *
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_efl_net_dialer_address_dial_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
return pd->address_dial;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_efl_net_dialer_connected_set(Eo *o, Efl_Net_Dialer_Http_Data *pd, Eina_Bool connected)
|
|
|
|
{
|
|
|
|
/* always set and emit connected...
|
|
|
|
* allow_redirects will trigger more than once
|
|
|
|
*/
|
|
|
|
pd->connected = connected;
|
2016-08-29 16:13:22 -07:00
|
|
|
if (connected) efl_event_callback_call(o, EFL_NET_DIALER_EVENT_CONNECTED, NULL);
|
2016-08-19 20:55:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static Eina_Bool
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_efl_net_dialer_connected_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
return pd->connected;
|
|
|
|
}
|
|
|
|
|
2016-08-22 14:51:38 -07:00
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_efl_net_dialer_proxy_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd, const char *proxy_url)
|
|
|
|
{
|
|
|
|
CURLcode r;
|
|
|
|
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_PROXY, proxy_url);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not set proxy to '%s': %s",
|
|
|
|
o, proxy_url, curl_easy_strerror(r));
|
|
|
|
|
|
|
|
eina_stringshare_replace(&pd->proxy, proxy_url);
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static const char *
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_efl_net_dialer_proxy_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-08-22 14:51:38 -07:00
|
|
|
{
|
|
|
|
return pd->proxy;
|
|
|
|
}
|
|
|
|
|
2016-08-19 20:55:26 -07:00
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_efl_net_dialer_timeout_dial_set(Eo *o, Efl_Net_Dialer_Http_Data *pd, double seconds)
|
|
|
|
{
|
|
|
|
CURLcode r;
|
|
|
|
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_CONNECTTIMEOUT_MS,
|
|
|
|
(long)(seconds * 1000));
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not connection timeout %f seconds: %s",
|
|
|
|
o, seconds, curl_easy_strerror(r));
|
|
|
|
|
|
|
|
pd->timeout_dial = seconds;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static double
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_efl_net_dialer_timeout_dial_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
return pd->timeout_dial;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_efl_net_socket_address_local_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd, const char *address)
|
|
|
|
{
|
|
|
|
eina_stringshare_replace(&pd->address_local, address);
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static const char *
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_efl_net_socket_address_local_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
return pd->address_local;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_efl_net_socket_address_remote_set(Eo *o, Efl_Net_Dialer_Http_Data *pd, const char *address)
|
|
|
|
{
|
|
|
|
if (eina_stringshare_replace(&pd->address_remote, address))
|
2016-08-29 16:13:22 -07:00
|
|
|
efl_event_callback_call(o, EFL_NET_DIALER_EVENT_RESOLVED, NULL);
|
2016-08-19 20:55:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static const char *
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_efl_net_socket_address_remote_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
return pd->address_remote;
|
|
|
|
}
|
|
|
|
|
2016-09-01 08:18:40 -07:00
|
|
|
static Eina_Error
|
|
|
|
_efl_net_dialer_http_pause_reset(Eo *o, Efl_Net_Dialer_Http_Data *pd)
|
|
|
|
{
|
|
|
|
CURLcode re;
|
|
|
|
CURLMcode rm;
|
|
|
|
Eina_Error err;
|
|
|
|
|
2017-09-17 02:14:37 -07:00
|
|
|
EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->cm == NULL, EALREADY);
|
|
|
|
|
2016-09-01 08:18:40 -07:00
|
|
|
re = curl_easy_pause(pd->easy, pd->pause);
|
|
|
|
if (re != CURLE_OK)
|
|
|
|
{
|
|
|
|
err = _curlcode_to_eina_error(re);
|
|
|
|
ERR("dialer=%p could not unpause receive (flags=%#x): %s",
|
|
|
|
o, pd->pause, eina_error_msg_get(err));
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
rm = curl_multi_socket_action(pd->cm->multi, CURL_SOCKET_TIMEOUT, 0, &pd->cm->running);
|
|
|
|
if (rm != CURLM_OK)
|
|
|
|
{
|
|
|
|
err = _curlmcode_to_eina_error(rm);
|
|
|
|
ERR("dialer=%p could trigger timeout action: %s",
|
|
|
|
o, eina_error_msg_get(err));
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2017-02-01 10:23:11 -08:00
|
|
|
_efl_net_dialer_http_curlm_check(pd->cm);
|
|
|
|
|
2016-09-01 08:18:40 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-08-19 20:55:26 -07:00
|
|
|
EOLIAN static Eina_Error
|
|
|
|
_efl_net_dialer_http_efl_io_reader_read(Eo *o, Efl_Net_Dialer_Http_Data *pd, Eina_Rw_Slice *rw_slice)
|
|
|
|
{
|
|
|
|
Eina_Slice ro_slice;
|
|
|
|
size_t remaining;
|
|
|
|
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(rw_slice, EINVAL);
|
|
|
|
|
|
|
|
ro_slice.len = pd->recv.used;
|
|
|
|
if (ro_slice.len == 0)
|
|
|
|
{
|
|
|
|
rw_slice->len = 0;
|
2016-10-26 17:54:42 -07:00
|
|
|
if (pd->pending_eos)
|
|
|
|
{
|
|
|
|
efl_io_reader_eos_set(o, EINA_TRUE);
|
|
|
|
efl_io_closer_close(o);
|
|
|
|
}
|
2016-08-19 20:55:26 -07:00
|
|
|
return EAGAIN;
|
|
|
|
}
|
|
|
|
ro_slice.bytes = pd->recv.bytes;
|
|
|
|
|
|
|
|
*rw_slice = eina_rw_slice_copy(*rw_slice, ro_slice);
|
|
|
|
|
|
|
|
remaining = pd->recv.used - rw_slice->len;
|
|
|
|
if (remaining)
|
|
|
|
memmove(pd->recv.bytes, pd->recv.bytes + rw_slice->len, remaining);
|
|
|
|
|
|
|
|
pd->recv.used = remaining;
|
|
|
|
efl_io_reader_can_read_set(o, remaining > 0);
|
|
|
|
|
2016-10-26 17:54:42 -07:00
|
|
|
if ((pd->pending_eos) && (remaining == 0))
|
|
|
|
{
|
|
|
|
efl_io_reader_eos_set(o, EINA_TRUE);
|
|
|
|
efl_io_closer_close(o);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-08-19 20:55:26 -07:00
|
|
|
if ((pd->pause & CURLPAUSE_RECV) && (pd->recv.used < pd->recv.limit))
|
|
|
|
{
|
2016-09-01 08:18:40 -07:00
|
|
|
Eina_Error err;
|
2016-08-19 20:55:26 -07:00
|
|
|
pd->pause &= ~CURLPAUSE_RECV;
|
2016-09-01 08:18:40 -07:00
|
|
|
err = _efl_net_dialer_http_pause_reset(o, pd);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-08-19 20:55:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static Eina_Bool
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_efl_io_reader_can_read_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
return pd->can_read;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_efl_io_reader_can_read_set(Eo *o, Efl_Net_Dialer_Http_Data *pd, Eina_Bool can_read)
|
|
|
|
{
|
|
|
|
EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o));
|
|
|
|
if (pd->can_read == can_read) return;
|
|
|
|
pd->can_read = can_read;
|
2016-08-29 16:13:22 -07:00
|
|
|
efl_event_callback_call(o, EFL_IO_READER_EVENT_CAN_READ_CHANGED, NULL);
|
2016-08-19 20:55:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static Eina_Bool
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_efl_io_reader_eos_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
return pd->eos;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_efl_io_reader_eos_set(Eo *o, Efl_Net_Dialer_Http_Data *pd, Eina_Bool is_eos)
|
|
|
|
{
|
|
|
|
EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o));
|
|
|
|
if (pd->eos == is_eos) return;
|
|
|
|
pd->eos = is_eos;
|
|
|
|
if (is_eos) pd->connected = EINA_FALSE;
|
|
|
|
if (is_eos)
|
2016-08-29 16:13:22 -07:00
|
|
|
efl_event_callback_call(o, EFL_IO_READER_EVENT_EOS, NULL);
|
2016-08-19 20:55:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static Eina_Error
|
|
|
|
_efl_net_dialer_http_efl_io_writer_write(Eo *o, Efl_Net_Dialer_Http_Data *pd, Eina_Slice *slice, Eina_Slice *remaining)
|
|
|
|
{
|
|
|
|
Eina_Error err = EINVAL;
|
2016-08-24 10:33:33 -07:00
|
|
|
CURLMcode rm;
|
2016-08-19 20:55:26 -07:00
|
|
|
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(slice, EINVAL);
|
|
|
|
EINA_SAFETY_ON_TRUE_GOTO(efl_io_closer_closed_get(o), error);
|
|
|
|
err = EBUSY;
|
|
|
|
EINA_SAFETY_ON_TRUE_GOTO(pd->send.slice.mem != NULL, error);
|
|
|
|
|
|
|
|
pd->send.slice = *slice;
|
|
|
|
efl_io_writer_can_write_set(o, EINA_FALSE);
|
|
|
|
pd->pause &= ~CURLPAUSE_SEND;
|
2016-09-01 08:18:40 -07:00
|
|
|
err = _efl_net_dialer_http_pause_reset(o, pd);
|
|
|
|
if (err) goto error;
|
2016-08-19 20:55:26 -07:00
|
|
|
|
2017-09-17 02:14:37 -07:00
|
|
|
if (!pd->cm)
|
|
|
|
{
|
|
|
|
err = EISCONN;
|
|
|
|
goto error;
|
|
|
|
}
|
2016-08-19 20:55:26 -07:00
|
|
|
pd->error = 0;
|
2016-08-24 10:33:33 -07:00
|
|
|
rm = curl_multi_socket_action(pd->cm->multi,
|
efl_net_dialer_http: keep fd directly.
provide curl with CURLOPT_OPENSOCKETFUNCTION and keep the fd in our
private data.
This is required because on _efl_net_dialer_http_efl_io_writer_write()
we may have no fdhandler.
It happened to me while implementing the WebSocket that uses a
bi-directional communication on top of HTTP and the server sent the
whole message, CURL reads:
recvfrom(7, "...", 16384, 0, NULL, NULL) = 86
recvfrom(7, "", 16384, 0, NULL, NULL) = 0
After the empty (second) recvfrom(), CURL will remove the fdhandler:
DBG:ecore_con lib/ecore_con/efl_net_dialer_http.c:482 _efl_net_dialer_http_curlm_socket_manage() dialer=0x4000000040000005 fdhandler=(nil), fd=7, curl_easy=0x5561846ca8d0, flags=0x4
However I should be able to write to this socket, in my case I need to
reply to a PING request with a PONG.
2016-08-29 19:35:31 -07:00
|
|
|
pd->fd,
|
2016-08-24 10:33:33 -07:00
|
|
|
CURL_CSELECT_OUT, &pd->cm->running);
|
|
|
|
if (rm != CURLM_OK)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
2016-08-24 10:33:33 -07:00
|
|
|
err = _curlcode_to_eina_error(rm);
|
2016-11-18 06:17:08 -08:00
|
|
|
ERR("dialer=%p could not trigger socket=" SOCKET_FMT " (fdhandler=%p) action: %s",
|
efl_net_dialer_http: keep fd directly.
provide curl with CURLOPT_OPENSOCKETFUNCTION and keep the fd in our
private data.
This is required because on _efl_net_dialer_http_efl_io_writer_write()
we may have no fdhandler.
It happened to me while implementing the WebSocket that uses a
bi-directional communication on top of HTTP and the server sent the
whole message, CURL reads:
recvfrom(7, "...", 16384, 0, NULL, NULL) = 86
recvfrom(7, "", 16384, 0, NULL, NULL) = 0
After the empty (second) recvfrom(), CURL will remove the fdhandler:
DBG:ecore_con lib/ecore_con/efl_net_dialer_http.c:482 _efl_net_dialer_http_curlm_socket_manage() dialer=0x4000000040000005 fdhandler=(nil), fd=7, curl_easy=0x5561846ca8d0, flags=0x4
However I should be able to write to this socket, in my case I need to
reply to a PING request with a PONG.
2016-08-29 19:35:31 -07:00
|
|
|
o, pd->fd, pd->fdhandler, eina_error_msg_get(err));
|
2016-08-19 20:55:26 -07:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
_efl_net_dialer_http_curlm_check(pd->cm);
|
|
|
|
if (pd->error) return pd->error;
|
|
|
|
|
|
|
|
if (remaining) *remaining = pd->send.slice;
|
|
|
|
slice->len -= pd->send.slice.len;
|
|
|
|
slice->bytes += pd->send.slice.len;
|
|
|
|
pd->send.slice.mem = NULL;
|
|
|
|
pd->send.slice.len = 0;
|
|
|
|
|
|
|
|
if (slice->len == 0)
|
|
|
|
return EAGAIN;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error:
|
|
|
|
if (remaining) *remaining = *slice;
|
|
|
|
slice->len = 0;
|
|
|
|
slice->mem = NULL;
|
|
|
|
pd->send.slice.mem = NULL;
|
|
|
|
pd->send.slice.len = 0;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static Eina_Bool
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_efl_io_writer_can_write_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
return pd->can_write;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_efl_io_writer_can_write_set(Eo *o, Efl_Net_Dialer_Http_Data *pd, Eina_Bool can_write)
|
|
|
|
{
|
|
|
|
EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o));
|
|
|
|
if (pd->can_write == can_write) return;
|
|
|
|
pd->can_write = can_write;
|
2016-08-29 16:13:22 -07:00
|
|
|
efl_event_callback_call(o, EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, NULL);
|
2016-08-19 20:55:26 -07:00
|
|
|
}
|
|
|
|
|
2018-12-07 03:15:16 -08:00
|
|
|
static Eina_Value _efl_net_dialer_http_pending_close(Eo *o, void *data, Eina_Value value);
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
|
2016-08-19 20:55:26 -07:00
|
|
|
EOLIAN static Eina_Error
|
|
|
|
_efl_net_dialer_http_efl_io_closer_close(Eo *o, Efl_Net_Dialer_Http_Data *pd)
|
|
|
|
{
|
|
|
|
Eina_Error err = 0;
|
|
|
|
|
|
|
|
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EBADF);
|
|
|
|
|
2016-10-22 08:15:16 -07:00
|
|
|
pd->fd = INVALID_SOCKET;
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
|
|
|
|
if (pd->in_curl_callback)
|
|
|
|
{
|
2016-12-09 16:10:28 -08:00
|
|
|
if ((!pd->pending_close) && (pd->easy))
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
{
|
2018-11-21 17:33:10 -08:00
|
|
|
efl_future_then(o, efl_loop_job(efl_loop_get(o)),
|
2018-12-07 03:15:16 -08:00
|
|
|
.success = _efl_net_dialer_http_pending_close,
|
|
|
|
.storage = &pd->pending_close);
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
DBG("dialer=%p closed from CURL callback, schedule close job=%p", o, pd->pending_close);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-08-19 20:55:26 -07:00
|
|
|
if (!pd->easy) goto end;
|
|
|
|
|
|
|
|
if (pd->cm)
|
|
|
|
{
|
efl_net_dialer_http: keep fd directly.
provide curl with CURLOPT_OPENSOCKETFUNCTION and keep the fd in our
private data.
This is required because on _efl_net_dialer_http_efl_io_writer_write()
we may have no fdhandler.
It happened to me while implementing the WebSocket that uses a
bi-directional communication on top of HTTP and the server sent the
whole message, CURL reads:
recvfrom(7, "...", 16384, 0, NULL, NULL) = 86
recvfrom(7, "", 16384, 0, NULL, NULL) = 0
After the empty (second) recvfrom(), CURL will remove the fdhandler:
DBG:ecore_con lib/ecore_con/efl_net_dialer_http.c:482 _efl_net_dialer_http_curlm_socket_manage() dialer=0x4000000040000005 fdhandler=(nil), fd=7, curl_easy=0x5561846ca8d0, flags=0x4
However I should be able to write to this socket, in my case I need to
reply to a PING request with a PONG.
2016-08-29 19:35:31 -07:00
|
|
|
DBG("close dialer=%p, cm=%p, easy=%p", o, pd->cm, pd->easy);
|
2016-08-19 20:55:26 -07:00
|
|
|
_efl_net_dialer_http_curlm_remove(pd->cm, o, pd->easy);
|
|
|
|
pd->cm = NULL;
|
|
|
|
}
|
|
|
|
if (pd->fdhandler)
|
|
|
|
{
|
|
|
|
ERR("dialer=%p fdhandler=%p still alive!", o, pd->fdhandler);
|
2016-11-23 20:13:32 -08:00
|
|
|
efl_del(pd->fdhandler);
|
2016-08-19 20:55:26 -07:00
|
|
|
pd->fdhandler = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
end:
|
|
|
|
efl_io_writer_can_write_set(o, EINA_FALSE);
|
|
|
|
efl_io_reader_can_read_set(o, EINA_FALSE);
|
2016-09-12 08:46:05 -07:00
|
|
|
efl_io_reader_eos_set(o, EINA_TRUE);
|
2016-08-19 20:55:26 -07:00
|
|
|
efl_net_dialer_connected_set(o, EINA_FALSE);
|
|
|
|
pd->closed = EINA_TRUE;
|
2016-08-29 16:13:22 -07:00
|
|
|
efl_event_callback_call(o, EFL_IO_CLOSER_EVENT_CLOSED, NULL);
|
2016-08-19 20:55:26 -07:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2017-08-30 13:24:27 -07:00
|
|
|
static Eina_Value
|
2018-12-07 03:15:16 -08:00
|
|
|
_efl_net_dialer_http_pending_close(Eo *o, void *data EINA_UNUSED, const Eina_Value value EINA_UNUSED)
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
{
|
|
|
|
Efl_Net_Dialer_Http_Data *pd = efl_data_scope_get(o, MY_CLASS);
|
|
|
|
|
|
|
|
_efl_net_dialer_http_efl_io_closer_close(o, pd);
|
2017-08-30 13:24:27 -07:00
|
|
|
return value;
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
}
|
|
|
|
|
2016-08-19 20:55:26 -07:00
|
|
|
EOLIAN static Eina_Bool
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_efl_io_closer_closed_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
efl_net_dialer_http: allow delete/close from CURL callbacks.
CURL doesn't play nice if handles are deleted or modified while it's
dispatching the callbacks, then we must not touch the CURL* easy
handle in those cases, just dissociate the handle from object and
schedule a job to do the deletion later.
Also, since from CURL callbacks we do not have the reference to the
object, if they are deleted from inside the callback, users of 'pd'
will crash. Thus keep an extra reference while the object and its
private data are in use.
The curl_multi_info_read() is used to notify of errors and
end-of-stream, if we do callback directly from there, the user may
efl_del(dialer), which will result in the "pd->easy" being destroyed
with curl_easy_cleanup() then "cm" and "cm->multi" being destroyed.
Thus postpone that action and keep a list of finished objects, calling
their event handlers which can delete the object (or siblings), thus
ref before dispatching and unref afterwards, taking care to monitor
EFL_EVENT_DEL so we do not use stale objects.
2016-08-31 06:19:04 -07:00
|
|
|
return pd->closed || (!!pd->pending_close);
|
2016-08-19 20:55:26 -07:00
|
|
|
}
|
|
|
|
|
2016-09-12 08:23:29 -07:00
|
|
|
EOLIAN static Eina_Bool
|
|
|
|
_efl_net_dialer_http_efl_io_closer_close_on_exec_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd, Eina_Bool close_on_exec)
|
|
|
|
{
|
|
|
|
#ifdef _WIN32
|
|
|
|
DBG("close on exec is not supported on windows");
|
|
|
|
pd->close_on_exec = close_on_exec;
|
|
|
|
return EINA_FALSE;
|
|
|
|
#else
|
|
|
|
Eina_Bool old = pd->close_on_exec;
|
|
|
|
|
|
|
|
pd->close_on_exec = close_on_exec;
|
|
|
|
|
2016-10-22 08:15:16 -07:00
|
|
|
if (pd->fd == INVALID_SOCKET) return EINA_TRUE; /* postpone until _efl_net_dialer_http_socket_open */
|
2016-09-12 08:23:29 -07:00
|
|
|
|
2017-04-18 16:56:01 -07:00
|
|
|
if (!eina_file_close_on_exec(pd->fd, close_on_exec))
|
2016-09-12 08:23:29 -07:00
|
|
|
{
|
2017-04-19 07:57:43 -07:00
|
|
|
ERR("fcntl(" SOCKET_FMT ", F_SETFD): %s", pd->fd, strerror(errno));
|
2016-09-12 08:23:29 -07:00
|
|
|
pd->close_on_exec = old;
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static Eina_Bool
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_efl_io_closer_close_on_exec_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-09-12 08:23:29 -07:00
|
|
|
{
|
|
|
|
return pd->close_on_exec;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
2018-04-17 16:17:29 -07:00
|
|
|
_efl_net_dialer_http_efl_io_closer_close_on_invalidate_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd, Eina_Bool close_on_invalidate)
|
2016-09-12 08:23:29 -07:00
|
|
|
{
|
2018-04-17 16:17:29 -07:00
|
|
|
pd->close_on_invalidate = close_on_invalidate;
|
2016-09-12 08:23:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static Eina_Bool
|
2018-04-17 16:17:29 -07:00
|
|
|
_efl_net_dialer_http_efl_io_closer_close_on_invalidate_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-09-12 08:23:29 -07:00
|
|
|
{
|
2018-04-17 16:17:29 -07:00
|
|
|
return pd->close_on_invalidate;
|
2016-09-12 08:23:29 -07:00
|
|
|
}
|
|
|
|
|
2016-08-19 20:55:26 -07:00
|
|
|
EOLIAN static Eina_Error
|
|
|
|
_efl_net_dialer_http_efl_io_sizer_resize(Eo *o, Efl_Net_Dialer_Http_Data *pd, uint64_t size)
|
|
|
|
{
|
|
|
|
Efl_Net_Dialer_Http_Primary_Mode pm;
|
|
|
|
|
|
|
|
EINA_SAFETY_ON_TRUE_RETURN_VAL(size > INT64_MAX, ERANGE);
|
|
|
|
|
|
|
|
pm = _efl_net_dialer_http_primary_mode_effective_get(pd);
|
2016-11-28 20:56:18 -08:00
|
|
|
if ((pm == EFL_NET_DIALER_HTTP_PRIMARY_MODE_UPLOAD) ||
|
|
|
|
(strcmp(pd->method, "POST") == 0))
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
efl_net_dialer_http_request_content_length_set(o, size);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ERR("dialer=%p cannot resize when EFL_NET_DIALER_HTTP_PRIMARY_MODE_DOWNLOAD", o);
|
|
|
|
return EPERM;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static uint64_t
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_efl_io_sizer_size_get(const Eo *o, Efl_Net_Dialer_Http_Data *pd)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
Efl_Net_Dialer_Http_Primary_Mode pm;
|
|
|
|
int64_t len;
|
|
|
|
|
|
|
|
pm = _efl_net_dialer_http_primary_mode_effective_get(pd);
|
|
|
|
if (pm == EFL_NET_DIALER_HTTP_PRIMARY_MODE_UPLOAD)
|
|
|
|
len = efl_net_dialer_http_request_content_length_get(o);
|
|
|
|
else
|
|
|
|
len = efl_net_dialer_http_response_content_length_get(o);
|
|
|
|
|
|
|
|
if (len < 0)
|
|
|
|
return 0;
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2016-08-29 10:50:33 -07:00
|
|
|
static void
|
|
|
|
_efl_net_dialer_http_request_apply(Eo *o, Efl_Net_Dialer_Http_Data *pd, const char *method, Efl_Net_Dialer_Http_Primary_Mode primary_mode)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
CURLcode r;
|
|
|
|
|
2016-08-29 10:50:33 -07:00
|
|
|
if (primary_mode == EFL_NET_DIALER_HTTP_PRIMARY_MODE_UPLOAD)
|
|
|
|
{
|
|
|
|
if (strcasecmp(method, "PUT") == 0)
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_PUT, 1L);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* upload with non PUT method:
|
|
|
|
* 1 - set CURLOPT_UPLOAD = 1, this forces httpreq = PUT
|
|
|
|
* 2 - set CURLOPT_CUSTOMREQUEST = method, this overrides
|
|
|
|
* the string to send.
|
|
|
|
*/
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_UPLOAD, 1L);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not configure upload mode: %s",
|
|
|
|
o, curl_easy_strerror(r));
|
|
|
|
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_CUSTOMREQUEST, method);
|
|
|
|
}
|
|
|
|
}
|
2016-08-19 20:55:26 -07:00
|
|
|
else
|
2016-08-29 10:50:33 -07:00
|
|
|
{
|
|
|
|
if (strcasecmp(method, "GET") == 0)
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_HTTPGET, 1L);
|
|
|
|
else if (strcasecmp(method, "POST") == 0)
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_POST, 1L);
|
2016-12-11 21:17:35 -08:00
|
|
|
else if (strcasecmp(method, "HEAD") == 0)
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_NOBODY, 1L);
|
2016-08-29 10:50:33 -07:00
|
|
|
else if (strcasecmp(method, "PUT") == 0)
|
|
|
|
{
|
|
|
|
if (primary_mode == EFL_NET_DIALER_HTTP_PRIMARY_MODE_AUTO)
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_PUT, 1L);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_UPLOAD, 0L);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not configure no-upload mode: %s",
|
|
|
|
o, curl_easy_strerror(r));
|
|
|
|
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_CUSTOMREQUEST, method);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_CUSTOMREQUEST, method);
|
|
|
|
}
|
|
|
|
|
2016-08-19 20:55:26 -07:00
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not configure HTTP method: %s: %s",
|
|
|
|
o, method, curl_easy_strerror(r));
|
2016-08-29 10:50:33 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_method_set(Eo *o, Efl_Net_Dialer_Http_Data *pd, const char *method)
|
|
|
|
{
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(method);
|
2016-08-19 20:55:26 -07:00
|
|
|
|
2016-08-29 10:50:33 -07:00
|
|
|
_efl_net_dialer_http_request_apply(o, pd, method, pd->primary_mode);
|
2016-08-19 20:55:26 -07:00
|
|
|
eina_stringshare_replace(&pd->method, method);
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static const char *
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_method_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
return pd->method;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
2016-08-29 10:50:33 -07:00
|
|
|
_efl_net_dialer_http_primary_mode_set(Eo *o, Efl_Net_Dialer_Http_Data *pd, Efl_Net_Dialer_Http_Primary_Mode primary_mode)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
2016-08-29 10:50:33 -07:00
|
|
|
_efl_net_dialer_http_request_apply(o, pd, pd->method, primary_mode);
|
2016-08-19 20:55:26 -07:00
|
|
|
pd->primary_mode = primary_mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static Efl_Net_Dialer_Http_Primary_Mode
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_primary_mode_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
return _efl_net_dialer_http_primary_mode_effective_get(pd);
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_user_agent_set(Eo *o, Efl_Net_Dialer_Http_Data *pd, const char *user_agent)
|
|
|
|
{
|
|
|
|
CURLcode r;
|
|
|
|
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_USERAGENT, user_agent);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not set user-agent '%s': %s",
|
|
|
|
o, user_agent, curl_easy_strerror(r));
|
|
|
|
|
|
|
|
eina_stringshare_replace(&pd->user_agent, user_agent);
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static const char *
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_user_agent_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
return pd->user_agent;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_http_version_set(Eo *o, Efl_Net_Dialer_Http_Data *pd, Efl_Net_Http_Version http_version)
|
|
|
|
{
|
|
|
|
CURLcode r;
|
|
|
|
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_HTTP_VERSION,
|
|
|
|
_efl_net_http_version_to_curl(http_version));
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not configure HTTP version code %d: %s",
|
|
|
|
o, http_version, curl_easy_strerror(r));
|
|
|
|
pd->version = http_version;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static Efl_Net_Http_Version
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_http_version_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
return pd->version;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_authentication_set(Eo *o, Efl_Net_Dialer_Http_Data *pd, const char *username, const char *password, Efl_Net_Http_Authentication_Method method, Eina_Bool restricted)
|
|
|
|
{
|
|
|
|
CURLcode r;
|
|
|
|
long flags;
|
|
|
|
char *tmp;
|
|
|
|
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_USERNAME, username);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not set username '%s': %s",
|
|
|
|
o, username, curl_easy_strerror(r));
|
|
|
|
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_PASSWORD, password);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not set password: %s", o, curl_easy_strerror(r));
|
|
|
|
|
|
|
|
flags = _efl_net_http_authentication_method_to_curl(method, restricted);
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_HTTPAUTH, flags);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not set HTTP authentication method %#x restricted %hhu (%#lx): %s",
|
|
|
|
o, method, restricted, flags, curl_easy_strerror(r));
|
|
|
|
|
|
|
|
eina_stringshare_replace(&pd->authentication.username, username);
|
|
|
|
|
|
|
|
tmp = password ? strdup(password) : NULL;
|
|
|
|
_secure_free(&pd->authentication.password);
|
|
|
|
pd->authentication.password = tmp;
|
|
|
|
|
|
|
|
pd->authentication.method = method;
|
|
|
|
pd->authentication.restricted = restricted;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_authentication_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd, const char **username, const char **password, Efl_Net_Http_Authentication_Method *method, Eina_Bool *restricted)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
if (username) *username = pd->authentication.username;
|
|
|
|
if (password) *password = pd->authentication.password;
|
|
|
|
if (method) *method = pd->authentication.method;
|
|
|
|
if (restricted) *restricted = pd->authentication.restricted;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_allow_redirects_set(Eo *o, Efl_Net_Dialer_Http_Data *pd, Eina_Bool allow_redirects)
|
|
|
|
{
|
|
|
|
CURLcode r;
|
|
|
|
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_FOLLOWLOCATION, (long)allow_redirects);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not set allow redirects %d: %s",
|
|
|
|
o, allow_redirects, curl_easy_strerror(r));
|
|
|
|
|
|
|
|
pd->allow_redirects = allow_redirects;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static Eina_Bool
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_allow_redirects_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
return pd->allow_redirects;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static Efl_Net_Http_Status
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_response_status_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
return pd->response.status;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_request_header_add(Eo *o, Efl_Net_Dialer_Http_Data *pd, const char *key, const char *value)
|
|
|
|
{
|
|
|
|
char *s = NULL;
|
|
|
|
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(key);
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(value);
|
|
|
|
|
|
|
|
if (asprintf(&s, "%s: %s", key, value) < 0)
|
|
|
|
{
|
|
|
|
ERR("dialer=%p could not allocate header string '%s: %s'",
|
|
|
|
o, key, value);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pd->request.headers = curl_slist_append(pd->request.headers, s);
|
|
|
|
free(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_request_headers_clear(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
|
|
|
{
|
|
|
|
curl_slist_free_all(pd->request.headers);
|
|
|
|
pd->request.headers = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct _Eina_Iterator_Curl_Slist_Header
|
|
|
|
{
|
|
|
|
Eina_Iterator iterator;
|
|
|
|
const struct curl_slist *head;
|
|
|
|
const struct curl_slist *current;
|
|
|
|
Efl_Net_Http_Header header;
|
|
|
|
char *mem;
|
|
|
|
} Eina_Iterator_Curl_Slist_Header;
|
|
|
|
|
|
|
|
static Eina_Bool
|
|
|
|
eina_iterator_curl_slist_header_next(Eina_Iterator_Curl_Slist_Header *it, void **data)
|
|
|
|
{
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
if (!it->current)
|
|
|
|
return EINA_FALSE;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(it->current->data, EINA_FALSE);
|
|
|
|
|
|
|
|
free(it->mem);
|
|
|
|
it->mem = strdup(it->current->data);
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(it->mem, EINA_FALSE);
|
|
|
|
|
|
|
|
it->header.key = it->mem;
|
|
|
|
|
|
|
|
p = strchr(it->mem, ':');
|
|
|
|
if (!p)
|
|
|
|
it->header.value = "";
|
|
|
|
else
|
|
|
|
{
|
|
|
|
p[0] = '\0';
|
|
|
|
p++;
|
|
|
|
while ((p[0] != '\0') && (isspace(p[0])))
|
|
|
|
p++;
|
|
|
|
|
|
|
|
it->header.value = p;
|
|
|
|
}
|
|
|
|
|
|
|
|
*data = &it->header;
|
|
|
|
it->current = it->current->next;
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct curl_slist *
|
|
|
|
eina_iterator_curl_slist_header_get_container(Eina_Iterator_Curl_Slist_Header *it)
|
|
|
|
{
|
|
|
|
return it->head;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
eina_iterator_curl_slist_header_free(Eina_Iterator_Curl_Slist_Header *it)
|
|
|
|
{
|
|
|
|
free(it->mem);
|
|
|
|
free(it);
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static Eina_Iterator *
|
|
|
|
_efl_net_dialer_http_request_headers_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
|
|
|
{
|
|
|
|
Eina_Iterator_Curl_Slist_Header *it;
|
|
|
|
|
|
|
|
it = calloc(1, sizeof(Eina_Iterator_Curl_Slist_Header));
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(it, NULL);
|
|
|
|
|
|
|
|
it->head = pd->request.headers;
|
|
|
|
it->current = it->head;
|
|
|
|
|
|
|
|
EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
|
|
|
|
it->iterator.version = EINA_ITERATOR_VERSION;
|
|
|
|
it->iterator.next = FUNC_ITERATOR_NEXT(eina_iterator_curl_slist_header_next);
|
|
|
|
it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(eina_iterator_curl_slist_header_get_container);
|
|
|
|
it->iterator.free = FUNC_ITERATOR_FREE(eina_iterator_curl_slist_header_free);
|
|
|
|
|
|
|
|
return &it->iterator;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_request_content_length_set(Eo *o, Efl_Net_Dialer_Http_Data *pd, int64_t length)
|
|
|
|
{
|
|
|
|
Efl_Net_Dialer_Http_Primary_Mode pm;
|
|
|
|
CURLcode r;
|
|
|
|
|
|
|
|
if (strcmp(pd->method, "POST") == 0)
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_POSTFIELDSIZE_LARGE, length);
|
|
|
|
else
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_INFILESIZE_LARGE, length);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not set file size %" PRId64 ": %s",
|
|
|
|
o, length, curl_easy_strerror(r));
|
|
|
|
|
|
|
|
pd->request.content_length = length;
|
|
|
|
if (length < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pm = _efl_net_dialer_http_primary_mode_effective_get(pd);
|
|
|
|
if (pm == EFL_NET_DIALER_HTTP_PRIMARY_MODE_UPLOAD)
|
2016-08-29 16:13:22 -07:00
|
|
|
efl_event_callback_call(o, EFL_IO_SIZER_EVENT_SIZE_CHANGED, NULL);
|
2016-08-19 20:55:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static int64_t
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_request_content_length_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
return pd->request.content_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_response_content_length_set(Eo *o, Efl_Net_Dialer_Http_Data *pd, int64_t length)
|
|
|
|
{
|
|
|
|
Efl_Net_Dialer_Http_Primary_Mode pm;
|
|
|
|
|
|
|
|
pd->response.content_length = length;
|
|
|
|
if (length < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pm = _efl_net_dialer_http_primary_mode_effective_get(pd);
|
|
|
|
if (pm == EFL_NET_DIALER_HTTP_PRIMARY_MODE_DOWNLOAD)
|
2016-08-29 16:13:22 -07:00
|
|
|
efl_event_callback_call(o, EFL_IO_SIZER_EVENT_SIZE_CHANGED, NULL);
|
2016-08-19 20:55:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static int64_t
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_response_content_length_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
return pd->response.content_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_response_content_type_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd, const char *content_type)
|
|
|
|
{
|
|
|
|
eina_stringshare_replace(&pd->response.content_type, content_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static const char *
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_response_content_type_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
return pd->response.content_type;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static Eina_Iterator *
|
|
|
|
_efl_net_dialer_http_response_headers_all_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
|
|
|
{
|
|
|
|
return eina_list_iterator_new(pd->response.headers);
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static Eina_Iterator *
|
|
|
|
_efl_net_dialer_http_response_headers_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
|
|
|
{
|
|
|
|
const Eina_List *lst = pd->response.last_request_header;
|
|
|
|
|
|
|
|
if (lst) lst = lst->next;
|
|
|
|
else lst = pd->response.headers;
|
|
|
|
|
|
|
|
return eina_list_iterator_new(lst);
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_response_headers_clear(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
|
|
|
{
|
|
|
|
void *mem;
|
|
|
|
|
|
|
|
EINA_LIST_FREE(pd->response.headers, mem)
|
|
|
|
free(mem); /* key and value are in the same memory */
|
|
|
|
|
|
|
|
pd->response.last_request_header = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_progress_download_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd, uint64_t *now, uint64_t *total)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
if (now) *now = pd->progress.download.now;
|
|
|
|
if (total) *total = pd->progress.download.total;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_progress_upload_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd, uint64_t *now, uint64_t *total)
|
2016-08-19 20:55:26 -07:00
|
|
|
{
|
|
|
|
if (now) *now = pd->progress.upload.now;
|
|
|
|
if (total) *total = pd->progress.upload.total;
|
|
|
|
}
|
|
|
|
|
2016-08-22 15:24:13 -07:00
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_cookie_jar_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd, const char *path)
|
|
|
|
{
|
|
|
|
CURLcode r;
|
|
|
|
|
|
|
|
if (pd->cookie_jar)
|
|
|
|
{
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_COOKIELIST, "FLUSH");
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not flush cookie_jar to '%s': %s",
|
|
|
|
o, pd->cookie_jar, curl_easy_strerror(r));
|
|
|
|
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_COOKIELIST, "ALL");
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not empty cookie_jar: %s",
|
|
|
|
o, curl_easy_strerror(r));
|
|
|
|
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_COOKIEFILE, NULL);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not unset cookie_jar (read): %s",
|
|
|
|
o, curl_easy_strerror(r));
|
|
|
|
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_COOKIEJAR, NULL);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not unset cookie_jar (write): %s",
|
|
|
|
o, curl_easy_strerror(r));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!path) goto end;
|
|
|
|
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_COOKIEFILE, path);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not set cookie_jar (read) to '%s': %s",
|
|
|
|
o, path, curl_easy_strerror(r));
|
|
|
|
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_COOKIEJAR, path);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not set cookie_jar (write) to '%s': %s",
|
|
|
|
o, path, curl_easy_strerror(r));
|
|
|
|
|
|
|
|
end:
|
|
|
|
eina_stringshare_replace(&pd->cookie_jar, path);
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static const char *
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_cookie_jar_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-08-22 15:24:13 -07:00
|
|
|
{
|
|
|
|
return pd->cookie_jar;
|
|
|
|
}
|
|
|
|
|
2016-11-28 20:16:47 -08:00
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_ssl_verify_set(Eo *o, Efl_Net_Dialer_Http_Data *pd, Eina_Bool peer, Eina_Bool hostname)
|
|
|
|
{
|
|
|
|
CURLcode r;
|
|
|
|
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_SSL_VERIFYPEER, (long)peer);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not set SSL verify peer %d: %s",
|
|
|
|
o, peer, curl_easy_strerror(r));
|
|
|
|
|
|
|
|
/* VERIFYHOST used to take 1 as debug,
|
|
|
|
* it requires 2 to really enable it.
|
|
|
|
*/
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_SSL_VERIFYHOST, (long)hostname * 2);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not set SSL verify hostname %d: %s",
|
|
|
|
o, hostname, curl_easy_strerror(r));
|
|
|
|
|
|
|
|
pd->ssl.verify_peer = peer;
|
|
|
|
pd->ssl.verify_hostname = hostname;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_ssl_verify_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd, Eina_Bool *peer, Eina_Bool *hostname)
|
2016-11-28 20:16:47 -08:00
|
|
|
{
|
|
|
|
if (peer) *peer = pd->ssl.verify_peer;
|
|
|
|
if (hostname) *hostname = pd->ssl.verify_hostname;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_dialer_http_ssl_certificate_authority_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd, const char *path)
|
|
|
|
{
|
|
|
|
CURLcode r;
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
if (path && (stat(path, &st) == 0) && (S_ISDIR(st.st_mode)))
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_CAPATH, path);
|
|
|
|
else
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_CAINFO, path);
|
|
|
|
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not set SSL CA '%s': %s",
|
|
|
|
o, path, curl_easy_strerror(r));
|
|
|
|
|
|
|
|
eina_stringshare_replace(&pd->ssl.ca, path);
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static const char *
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_ssl_certificate_authority_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-11-28 20:16:47 -08:00
|
|
|
{
|
|
|
|
return pd->ssl.ca;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static void
|
2016-12-10 04:21:56 -08:00
|
|
|
_efl_net_dialer_http_ssl_certificate_revocation_list_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd, const char *path)
|
2016-11-28 20:16:47 -08:00
|
|
|
{
|
|
|
|
CURLcode r;
|
|
|
|
|
|
|
|
r = curl_easy_setopt(pd->easy, CURLOPT_CRLFILE, path);
|
|
|
|
if (r != CURLE_OK)
|
|
|
|
ERR("dialer=%p could not set SSL CRL '%s': %s",
|
|
|
|
o, path, curl_easy_strerror(r));
|
|
|
|
|
|
|
|
eina_stringshare_replace(&pd->ssl.crl, path);
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static const char *
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_dialer_http_ssl_certificate_revocation_list_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
|
2016-11-28 20:16:47 -08:00
|
|
|
{
|
|
|
|
return pd->ssl.crl;
|
|
|
|
}
|
|
|
|
|
2016-12-09 13:23:22 -08:00
|
|
|
EOLIAN static int64_t
|
2016-11-28 19:19:40 -08:00
|
|
|
_efl_net_dialer_http_date_parse(Efl_Class *cls EINA_UNUSED, void *cd EINA_UNUSED, const char *str)
|
|
|
|
{
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(str, 0);
|
|
|
|
return curl_getdate(str, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLIAN static char *
|
2016-12-09 13:23:22 -08:00
|
|
|
_efl_net_dialer_http_date_serialize(Efl_Class *cls EINA_UNUSED, void *cd EINA_UNUSED, int64_t ts)
|
2016-11-28 19:19:40 -08:00
|
|
|
{
|
|
|
|
static const char *const wkday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
|
|
|
|
static const char * const month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
|
|
|
char buf[128];
|
2016-12-09 13:23:22 -08:00
|
|
|
time_t t = ts;
|
|
|
|
struct tm *tm;
|
|
|
|
#ifdef HAVE_GMTIME_R
|
|
|
|
struct tm storage;
|
2016-11-28 19:19:40 -08:00
|
|
|
tm = gmtime_r(&t, &storage);
|
2016-12-09 13:23:22 -08:00
|
|
|
#else
|
|
|
|
tm = gmtime(&t);
|
|
|
|
#endif
|
|
|
|
|
2016-11-28 19:19:40 -08:00
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(tm, NULL);
|
|
|
|
|
|
|
|
snprintf(buf, sizeof(buf),
|
|
|
|
"%s, %02d %s %4d %02d:%02d:%02d GMT",
|
|
|
|
wkday[tm->tm_wday],
|
|
|
|
tm->tm_mday,
|
|
|
|
month[tm->tm_mon],
|
|
|
|
tm->tm_year + 1900,
|
|
|
|
tm->tm_hour,
|
|
|
|
tm->tm_min,
|
|
|
|
tm->tm_sec);
|
|
|
|
return strdup(buf);
|
|
|
|
}
|
|
|
|
|
2016-11-28 13:06:30 -08:00
|
|
|
CURL *
|
|
|
|
efl_net_dialer_http_curl_get(const Eo *o)
|
|
|
|
{
|
|
|
|
Efl_Net_Dialer_Http_Data *pd = efl_data_scope_get(o, MY_CLASS);
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(pd, NULL);
|
|
|
|
return pd->easy;
|
|
|
|
}
|
|
|
|
|
2016-08-19 20:55:26 -07:00
|
|
|
#include "efl_net_dialer_http.eo.c"
|