aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Makefile_Ecore_Con.am11
-rw-r--r--src/examples/ecore/.gitignore1
-rw-r--r--src/examples/ecore/Makefile.am10
-rw-r--r--src/examples/ecore/efl_io_copier_example.c113
-rw-r--r--src/examples/ecore/efl_net_dialer_http_example.c414
-rw-r--r--src/lib/ecore_con/Ecore_Con_Eo.h59
-rw-r--r--src/lib/ecore_con/ecore_con_url_curl.c203
-rw-r--r--src/lib/ecore_con/ecore_con_url_curl.h235
-rw-r--r--src/lib/ecore_con/efl_net_dialer_http.c1699
-rw-r--r--src/lib/ecore_con/efl_net_dialer_http.eo314
-rw-r--r--src/lib/ecore_con/efl_net_http_types.eot176
11 files changed, 3224 insertions, 11 deletions
diff --git a/src/Makefile_Ecore_Con.am b/src/Makefile_Ecore_Con.am
index ec98fd31dd..ceb2e90692 100644
--- a/src/Makefile_Ecore_Con.am
+++ b/src/Makefile_Ecore_Con.am
@@ -11,6 +11,7 @@ ecore_con_eolian_files = \
lib/ecore_con/efl_net_socket_tcp.eo \
lib/ecore_con/efl_net_dialer.eo \
lib/ecore_con/efl_net_dialer_tcp.eo \
+ lib/ecore_con/efl_net_dialer_http.eo \
lib/ecore_con/efl_net_server.eo \
lib/ecore_con/efl_net_server_fd.eo \
lib/ecore_con/efl_net_server_tcp.eo \
@@ -19,8 +20,13 @@ ecore_con_eolian_files = \
lib/ecore_con/ecore_con_eet_client_obj.eo \
lib/ecore_con/efl_network_url.eo
+ecore_con_eolian_type_files = \
+ lib/ecore_con/efl_net_http_types.eot
+
+
ecore_con_eolian_c = $(ecore_con_eolian_files:%.eo=%.eo.c)
ecore_con_eolian_h = $(ecore_con_eolian_files:%.eo=%.eo.h) \
+ $(ecore_con_eolian_type_files:%.eot=%.eot.h) \
$(ecore_con_eolian_files:%.eo=%.eo.legacy.h)
BUILT_SOURCES += \
@@ -29,11 +35,13 @@ BUILT_SOURCES += \
ecoreconeolianfilesdir = $(datadir)/eolian/include/ecore-@VMAJ@
ecoreconeolianfiles_DATA = \
- $(ecore_con_eolian_files)
+ $(ecore_con_eolian_files) \
+ $(ecore_con_eolian_type_files)
EXTRA_DIST2 += \
${ecoreconeolianfiles_DATA}
+
lib_LTLIBRARIES += lib/ecore_con/libecore_con.la
installed_ecoreconmainheadersdir = $(includedir)/ecore-con-@VMAJ@
@@ -66,6 +74,7 @@ lib/ecore_con/efl_net_socket_fd.c \
lib/ecore_con/efl_net_socket_tcp.c \
lib/ecore_con/efl_net_dialer.c \
lib/ecore_con/efl_net_dialer_tcp.c \
+lib/ecore_con/efl_net_dialer_http.c \
lib/ecore_con/efl_net_server.c \
lib/ecore_con/efl_net_server_fd.c \
lib/ecore_con/efl_net_server_tcp.c
diff --git a/src/examples/ecore/.gitignore b/src/examples/ecore/.gitignore
index 31e2bc0613..6339345b44 100644
--- a/src/examples/ecore/.gitignore
+++ b/src/examples/ecore/.gitignore
@@ -48,3 +48,4 @@
/ecore_buffer_provider_example
/efl_io_copier_example
/efl_net_server_example
+/efl_net_dialer_http_example
diff --git a/src/examples/ecore/Makefile.am b/src/examples/ecore/Makefile.am
index 17d6d2bd92..2e0f470fe5 100644
--- a/src/examples/ecore/Makefile.am
+++ b/src/examples/ecore/Makefile.am
@@ -78,7 +78,8 @@ ecore_getopt_example \
ecore_con_eet_client_example \
ecore_con_eet_server_example \
efl_io_copier_example \
-efl_net_server_example
+efl_net_server_example \
+efl_net_dialer_http_example
ECORE_COMMON_LDADD = \
$(top_builddir)/src/lib/ecore/libecore.la \
@@ -285,6 +286,9 @@ efl_io_copier_example_LDADD = $(ECORE_CON_COMMON_LDADD)
efl_net_server_example_SOURCES = efl_net_server_example.c
efl_net_server_example_LDADD = $(ECORE_CON_COMMON_LDADD)
+efl_net_dialer_http_example_SOURCES = efl_net_dialer_http_example.c
+efl_net_dialer_http_example_LDADD = $(ECORE_CON_COMMON_LDADD)
+
SRCS = \
ecore_animator_example.c \
ecore_buffer_example.c \
@@ -331,7 +335,9 @@ ecore_getopt_example.c \
ecore_con_eet_client_example.c \
ecore_con_eet_server_example.c \
ecore_con_eet_descriptor_example.c \
-efl_io_copier_example.c
+efl_io_copier_example.c \
+efl_net_server_example.c \
+efl_net_dialer_http_example.c
DATA_FILES = red.png Makefile.examples
diff --git a/src/examples/ecore/efl_io_copier_example.c b/src/examples/ecore/efl_io_copier_example.c
index be5352bff5..6fee399920 100644
--- a/src/examples/ecore/efl_io_copier_example.c
+++ b/src/examples/ecore/efl_io_copier_example.c
@@ -89,6 +89,63 @@ EFL_CALLBACKS_ARRAY_DEFINE(dialer_cbs,
{ EFL_NET_DIALER_EVENT_ERROR, _dialer_error },
{ EFL_NET_DIALER_EVENT_CONNECTED, _dialer_connected });
+static void
+_http_headers_done(void *data EINA_UNUSED, const Eo_Event *event)
+{
+ Eina_Iterator *itr;
+ Efl_Net_Http_Header *h;
+ Efl_Net_Http_Version ver = efl_net_dialer_http_version_get(event->object);
+ const char *response_content_type;
+ int64_t response_content_length;
+
+ fprintf(stderr, "INFO: HTTP/%d.%d connected to '%s', code=%d, headers:\n",
+ ver / 100, ver % 100,
+ efl_net_dialer_address_dial_get(event->object),
+ efl_net_dialer_http_response_status_get(event->object));
+
+ /* this is only for the last request, if allow_redirects and you want
+ * all of the headers, use efl_net_dialer_http_response_headers_all_get()
+ */
+ itr = efl_net_dialer_http_response_headers_get(event->object);
+
+ EINA_ITERATOR_FOREACH(itr, h)
+ fprintf(stderr, "INFO: Header '%s: %s'\n", h->key, h->value);
+
+ eina_iterator_free(itr);
+
+ /* be nice to memory, we do not need these anymore */
+ efl_net_dialer_http_response_headers_clear(event->object);
+
+ response_content_length = efl_net_dialer_http_response_content_length_get(event->object);
+ response_content_type = efl_net_dialer_http_response_content_type_get(event->object);
+ fprintf(stderr, "INFO: Download %" PRId64 " bytes of type %s\n",
+ response_content_length, response_content_type);
+
+ if (efl_net_dialer_http_primary_mode_get(event->object) == EFL_NET_DIALER_HTTP_PRIMARY_MODE_UPLOAD)
+ {
+ int64_t request_content_type = efl_net_dialer_http_request_content_length_get(event->object);
+ fprintf(stderr, "INFO: Upload %" PRId64 " bytes\n",
+ request_content_type);
+ }
+}
+
+static void
+_http_closed(void *data EINA_UNUSED, const Eo_Event *event)
+{
+ uint64_t dn, dt, un, ut;
+
+ efl_net_dialer_http_progress_download_get(event->object, &dn, &dt);
+ efl_net_dialer_http_progress_upload_get(event->object, &un, &ut);
+ fprintf(stderr, "INFO: http transfer info: "
+ "download=%" PRIu64 "/%" PRIu64 " "
+ "upload=%" PRIu64 "/%" PRIu64 "\n",
+ dn, dt, un, ut);
+}
+
+EFL_CALLBACKS_ARRAY_DEFINE(http_cbs,
+ { EFL_NET_DIALER_HTTP_EVENT_HEADERS_DONE, _http_headers_done },
+ { EFL_IO_CLOSER_EVENT_CLOSED, _http_closed });
+
/* copier events are of interest, you should hook to at least "done"
* and "error"
*/
@@ -274,6 +331,7 @@ static const Ecore_Getopt options = {
"The input file name or:\n"
":stdin: to read from stdin.\n"
"tcp://IP:PORT to connect using TCP and an IPv4 (A.B.C.D:PORT) or IPv6 ([A:B:C:D::E]:PORT).\n"
+ "http://address to do a GET request\n"
"",
"input-file"),
ECORE_GETOPT_STORE_METAVAR_STR(0, NULL,
@@ -283,6 +341,7 @@ static const Ecore_Getopt options = {
":memory: to write to a memory buffer.\n"
":none: to not use a destination object.\n"
"tcp://IP:PORT to connect using TCP and an IPv4 (A.B.C.D:PORT) or IPv6 ([A:B:C:D::E]:PORT).\n"
+ "http://address to do a PUT request\n"
"",
"output-file"),
ECORE_GETOPT_SENTINEL
@@ -321,6 +380,7 @@ main(int argc, char **argv)
ecore_init();
ecore_con_init();
+ ecore_con_url_init();
args = ecore_getopt_parse(&options, values, argc, argv);
if (args < 0)
@@ -386,6 +446,32 @@ main(int argc, char **argv)
goto end_input;
}
}
+ else if (strncmp(input_fname, "http://", strlen("http://")) == 0 ||
+ strncmp(input_fname, "https://", strlen("https://")) == 0)
+ {
+ Eina_Error err;
+
+ input = efl_add(EFL_NET_DIALER_HTTP_CLASS, ecore_main_loop_get(),
+ efl_net_dialer_http_method_set(efl_self, "GET"),
+ efl_event_callback_array_add(efl_self, input_cbs(), NULL), /* optional */
+ efl_event_callback_array_add(efl_self, dialer_cbs(), NULL), /* optional */
+ efl_event_callback_array_add(efl_self, http_cbs(), NULL) /* optional */
+ );
+ if (!input)
+ {
+ fprintf(stderr, "ERROR: could not create HTTP Dialer.\n");
+ retval = EXIT_FAILURE;
+ goto end;
+ }
+
+ err = efl_net_dialer_dial(input, input_fname);
+ if (err)
+ {
+ fprintf(stderr, "ERROR: could not HTTP dial %s: %s\n",
+ input_fname, eina_error_msg_get(err));
+ goto end_input;
+ }
+ }
else
{
/* regular file, open with flags: read-only and close-on-exec */
@@ -493,6 +579,32 @@ main(int argc, char **argv)
goto end_output;
}
}
+ else if (strncmp(output_fname, "http://", strlen("http://")) == 0 ||
+ strncmp(output_fname, "https://", strlen("https://")) == 0)
+ {
+ Eina_Error err;
+
+ output = efl_add(EFL_NET_DIALER_HTTP_CLASS, ecore_main_loop_get(),
+ efl_net_dialer_http_method_set(efl_self, "PUT"),
+ efl_event_callback_array_add(efl_self, output_cbs(), NULL), /* optional */
+ efl_event_callback_array_add(efl_self, dialer_cbs(), NULL), /* optional */
+ efl_event_callback_array_add(efl_self, http_cbs(), NULL) /* optional */
+ );
+ if (!output)
+ {
+ fprintf(stderr, "ERROR: could not create HTTP Dialer.\n");
+ retval = EXIT_FAILURE;
+ goto end_input;
+ }
+
+ err = efl_net_dialer_dial(output, output_fname);
+ if (err)
+ {
+ fprintf(stderr, "ERROR: could not HTTP dial %s: %s\n",
+ output_fname, eina_error_msg_get(err));
+ goto end_output;
+ }
+ }
else
{
/* regular file, open with flags: write-only, close-on-exec,
@@ -560,6 +672,7 @@ main(int argc, char **argv)
input = NULL;
end:
+ ecore_con_url_shutdown();
ecore_con_shutdown();
ecore_shutdown();
diff --git a/src/examples/ecore/efl_net_dialer_http_example.c b/src/examples/ecore/efl_net_dialer_http_example.c
new file mode 100644
index 0000000000..2ba9f896d1
--- /dev/null
+++ b/src/examples/ecore/efl_net_dialer_http_example.c
@@ -0,0 +1,414 @@
+#define EFL_BETA_API_SUPPORT 1
+#define EFL_EO_API_SUPPORT 1
+#include <Ecore.h>
+#include <Ecore_Con.h>
+#include <Ecore_Getopt.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+static int retval = EXIT_SUCCESS;
+static int waiting;
+
+static void
+_closed(void *data EINA_UNUSED, const Eo_Event *event)
+{
+ fprintf(stderr, "INFO: closed %s\n",
+ efl_name_get(event->object));
+}
+
+static void
+_eos(void *data EINA_UNUSED, const Eo_Event *event)
+{
+ fprintf(stderr, "INFO: eos %s\n",
+ efl_name_get(event->object));
+}
+
+static void
+_connected(void *data EINA_UNUSED, const Eo_Event *event)
+{
+ fprintf(stderr, "INFO: connected %s\n",
+ efl_net_dialer_address_dial_get(event->object));
+}
+
+static void
+_resolved(void *data EINA_UNUSED, const Eo_Event *event)
+{
+ fprintf(stderr, "INFO: resolved %s => %s\n",
+ efl_net_dialer_address_dial_get(event->object),
+ efl_net_socket_address_remote_get(event->object));
+}
+
+static void
+_error(void *data EINA_UNUSED, const Eo_Event *event)
+{
+ const Eina_Error *perr = event->info;
+ fprintf(stderr, "INFO: error: %d '%s'\n", *perr, eina_error_msg_get(*perr));
+ retval = EXIT_FAILURE;
+}
+
+static void
+_http_headers_done(void *data EINA_UNUSED, const Eo_Event *event)
+{
+ Eo *o = event->object;
+ Efl_Net_Http_Version ver = efl_net_dialer_http_version_get(o);
+ Eina_Iterator *itr;
+ const Efl_Net_Http_Header *h;
+
+ fprintf(stderr, "INFO: HTTP/%d.%d status=%d url=%s\n",
+ ver / 100, ver % 100,
+ efl_net_dialer_http_response_status_get(o),
+ efl_net_socket_address_remote_get(o));
+
+ itr = efl_net_dialer_http_response_headers_get(o);
+ EINA_ITERATOR_FOREACH(itr, h)
+ fprintf(stderr, "INFO: %s: %s\n", h->key, h->value);
+
+ eina_iterator_free(itr);
+
+ fprintf(stderr, "INFO: content-type: %s, content-length: %" PRId64 "\n",
+ efl_net_dialer_http_response_content_type_get(o),
+ efl_net_dialer_http_response_content_length_get(o));
+
+ fprintf(stderr, "INFO: to upload %zd bytes\n", efl_net_dialer_http_request_content_length_get(o));
+}
+
+EFL_CALLBACKS_ARRAY_DEFINE(dialer_cbs,
+ { EFL_NET_DIALER_HTTP_EVENT_HEADERS_DONE, _http_headers_done },
+ { EFL_NET_DIALER_EVENT_CONNECTED, _connected },
+ { EFL_NET_DIALER_EVENT_RESOLVED, _resolved },
+ { EFL_NET_DIALER_EVENT_ERROR, _error },
+ { EFL_IO_CLOSER_EVENT_CLOSED, _closed },
+ { EFL_IO_READER_EVENT_EOS, _eos });
+
+static void
+_done(void *data EINA_UNUSED, const Eo_Event *event)
+{
+ waiting--;
+ fprintf(stderr, "INFO: done %s, waiting=%d\n",
+ efl_name_get(event->object), waiting);
+ if (waiting == 0)
+ ecore_main_loop_quit();
+}
+
+EFL_CALLBACKS_ARRAY_DEFINE(copier_cbs,
+ { EFL_IO_COPIER_EVENT_DONE, _done },
+ { EFL_IO_CLOSER_EVENT_CLOSED, _closed });
+
+static const char *primary_mode_choices[] = {
+ "auto",
+ "download",
+ "upload",
+ NULL
+};
+
+static Efl_Net_Dialer_Http_Primary_Mode
+_parse_primary_mode(const char *str)
+{
+ if (strcmp(str, "upload") == 0)
+ return EFL_NET_DIALER_HTTP_PRIMARY_MODE_UPLOAD;
+ if (strcmp(str, "download") == 0)
+ return EFL_NET_DIALER_HTTP_PRIMARY_MODE_DOWNLOAD;
+
+ return EFL_NET_DIALER_HTTP_PRIMARY_MODE_AUTO;
+}
+
+static const char *http_version_choices[] = {
+ "1.0",
+ "1.1",
+ "2.0",
+ NULL
+};
+
+static Efl_Net_Http_Version
+_parse_http_version(const char *str)
+{
+ if (strcmp(str, "1.1") == 0)
+ return EFL_NET_HTTP_VERSION_V1_1;
+ if (strcmp(str, "2.0") == 0)
+ return EFL_NET_HTTP_VERSION_V2_0;
+
+ return EFL_NET_HTTP_VERSION_V1_0;
+}
+
+static const char *authentication_method_choices[] = {
+ "none",
+ "basic",
+ "digest",
+ "negotiate",
+ "ntlm",
+ "ntlm_winbind",
+ "any_safe",
+ "any",
+ NULL,
+};
+
+static Efl_Net_Http_Authentication_Method
+_parse_authentication_method(const char *str)
+{
+ if (strcmp(str, "basic") == 0)
+ return EFL_NET_HTTP_AUTHENTICATION_METHOD_BASIC;
+ if (strcmp(str, "digest") == 0)
+ return EFL_NET_HTTP_AUTHENTICATION_METHOD_DIGEST;
+ if (strcmp(str, "negotiate") == 0)
+ return EFL_NET_HTTP_AUTHENTICATION_METHOD_NEGOTIATE;
+ if (strcmp(str, "ntlm") == 0)
+ return EFL_NET_HTTP_AUTHENTICATION_METHOD_NTLM;
+ if (strcmp(str, "ntlm_winbind") == 0)
+ return EFL_NET_HTTP_AUTHENTICATION_METHOD_NTLM_WINBIND;
+ if (strcmp(str, "any_safe") == 0)
+ return EFL_NET_HTTP_AUTHENTICATION_METHOD_ANY_SAFE;
+ if (strcmp(str, "any") == 0)
+ return EFL_NET_HTTP_AUTHENTICATION_METHOD_ANY;
+
+ return EFL_NET_HTTP_AUTHENTICATION_METHOD_NONE;
+}
+
+static const Ecore_Getopt options = {
+ "efl_net_dialer_http_example", /* program name */
+ NULL, /* usage line */
+ "1", /* version */
+ "(C) 2016 Enlightenment Project", /* copyright */
+ "BSD 2-Clause", /* license */
+ /* long description, may be multiline and contain \n */
+ "Example of Efl_Net_Dialer_Http usage.\n",
+ EINA_FALSE,
+ {
+ ECORE_GETOPT_STORE_STR('m', "method", "HTTP method such as GET, POST, PUT..."),
+ ECORE_GETOPT_CHOICE('M', "primary-mode", "Define primary operation mode.", primary_mode_choices),
+ ECORE_GETOPT_CHOICE('v', "http-version", "HTTP protocol version to use", http_version_choices),
+ ECORE_GETOPT_STORE_STR('U', "username", "Authentication username"),
+ ECORE_GETOPT_STORE_STR('P', "password", "Authentication password"),
+ ECORE_GETOPT_CHOICE('A', "authentication-method", "Authentication method", authentication_method_choices),
+ ECORE_GETOPT_STORE_BOOL('R', "authentication-restricted", "Authentication method must be restricted"),
+ ECORE_GETOPT_STORE_BOOL('r', "allow-redirects", "allow redirections by following 'Location:' headers"),
+ ECORE_GETOPT_STORE_DOUBLE('t', "connect-timeout", "timeout in seconds for the connection phase"),
+ ECORE_GETOPT_APPEND('H', "header", "Add custom headers. Format must be 'Key: Value'", ECORE_GETOPT_TYPE_STR),
+ ECORE_GETOPT_APPEND('F', "form", "Add form field. Format must be 'key=value'", ECORE_GETOPT_TYPE_STR),
+ ECORE_GETOPT_STORE_STR('i', "input-file", "Input file to use when uploading"),
+ ECORE_GETOPT_VERSION('V', "version"),
+ ECORE_GETOPT_COPYRIGHT('C', "copyright"),
+ ECORE_GETOPT_LICENSE('L', "license"),
+ ECORE_GETOPT_HELP('h', "help"),
+ ECORE_GETOPT_STORE_METAVAR_STR(0, NULL,
+ "The address (URL) to dial", "address"),
+ ECORE_GETOPT_STORE_METAVAR_STR(0, NULL,
+ "The output file name or :stdout:",
+ "output-file"),
+ ECORE_GETOPT_SENTINEL
+ }
+};
+
+int
+main(int argc, char **argv)
+{
+ char *method = "GET";
+ char *primary_mode_str = "auto";
+ char *http_version_str = "1.1";
+ char *username = NULL;
+ char *password = NULL;
+ char *authentication_method_str = "basic";
+ char *address = NULL;
+ char *output_fname = NULL;
+ char *input_fname = NULL;
+ Eina_Bool quit_option = EINA_FALSE;
+ Eina_Bool authentication_restricted = EINA_FALSE;
+ Eina_Bool allow_redirects = EINA_TRUE;
+ double timeout_dial = 30.0;
+ Eina_List *headers = NULL;
+ Eina_List *form_fields = NULL;
+ Ecore_Getopt_Value values[] = {
+ ECORE_GETOPT_VALUE_STR(method),
+ ECORE_GETOPT_VALUE_STR(primary_mode_str),
+ ECORE_GETOPT_VALUE_STR(http_version_str),
+ ECORE_GETOPT_VALUE_STR(username),
+ ECORE_GETOPT_VALUE_STR(password),
+ ECORE_GETOPT_VALUE_STR(authentication_method_str),
+ ECORE_GETOPT_VALUE_BOOL(authentication_restricted),
+ ECORE_GETOPT_VALUE_BOOL(allow_redirects),
+ ECORE_GETOPT_VALUE_DOUBLE(timeout_dial),
+ ECORE_GETOPT_VALUE_LIST(headers),
+ ECORE_GETOPT_VALUE_LIST(form_fields),
+ ECORE_GETOPT_VALUE_STR(input_fname),
+
+ /* standard block to provide version, copyright, license and help */
+ ECORE_GETOPT_VALUE_BOOL(quit_option), /* -V/--version quits */
+ ECORE_GETOPT_VALUE_BOOL(quit_option), /* -C/--copyright quits */
+ ECORE_GETOPT_VALUE_BOOL(quit_option), /* -L/--license quits */
+ ECORE_GETOPT_VALUE_BOOL(quit_option), /* -h/--help quits */
+
+ /* positional argument */
+ ECORE_GETOPT_VALUE_STR(address),
+ ECORE_GETOPT_VALUE_STR(output_fname),
+
+ ECORE_GETOPT_VALUE_NONE /* sentinel */
+ };
+ int args;
+ Eo *input, *dialer, *output, *sender, *receiver, *loop;
+ Efl_Net_Dialer_Http_Primary_Mode primary_mode;
+ Efl_Net_Http_Version http_version;
+ Efl_Net_Http_Authentication_Method authentication_method;
+ Efl_Net_Http_Header *header;
+ Eina_Iterator *itr;
+ Eina_Error err;
+ char *str;
+
+ ecore_init();
+ ecore_con_init();
+ ecore_con_url_init();
+
+ args = ecore_getopt_parse(&options, values, argc, argv);
+ if (args < 0)
+ {
+ fputs("ERROR: Could not parse command line options.\n", stderr);
+ retval = EXIT_FAILURE;
+ goto end;
+ }
+
+ if (quit_option) goto end;
+
+ loop = ecore_main_loop_get();
+
+ args = ecore_getopt_parse_positional(&options, values, argc, argv, args);
+ if (args < 0)
+ {
+ fputs("ERROR: Could not parse positional arguments.\n", stderr);
+ retval = EXIT_FAILURE;
+ goto end;
+ }
+
+ if ((!input_fname) || (strcmp(input_fname, ":stdin:") == 0))
+ {
+ input = efl_add(EFL_IO_STDIN_CLASS, loop,
+ efl_name_set(efl_self, "stdin"),
+ efl_event_callback_add(efl_self, EFL_IO_READER_EVENT_EOS, _eos, NULL));
+ }
+ else
+ {
+ input = efl_add(EFL_IO_FILE_CLASS, loop,
+ efl_name_set(efl_self, "input"),
+ efl_file_set(efl_self, input_fname, NULL),
+ efl_io_file_flags_set(efl_self, O_RDONLY | O_CLOEXEC),
+ efl_event_callback_add(efl_self, EFL_IO_READER_EVENT_EOS, _eos, NULL));
+ }
+
+ if ((!output_fname) || (strcmp(output_fname, ":stdout:") == 0))
+ {
+ output = efl_add(EFL_IO_STDOUT_CLASS, loop,
+ efl_name_set(efl_self, "stdout"));
+ }
+ else
+ {
+ output = efl_add(EFL_IO_FILE_CLASS, loop,
+ efl_name_set(efl_self, "output"),
+ efl_file_set(efl_self, output_fname, NULL),
+ efl_io_file_mode_set(efl_self, 0644),
+ efl_io_file_flags_set(efl_self, O_WRONLY | O_CLOEXEC | O_TRUNC | O_CREAT));
+ }
+
+ primary_mode = _parse_primary_mode(primary_mode_str);
+ http_version = _parse_http_version(http_version_str);
+ authentication_method = _parse_authentication_method(authentication_method_str);
+
+ dialer = efl_add(EFL_NET_DIALER_HTTP_CLASS, loop,
+ efl_name_set(efl_self, "dialer"),
+ efl_net_dialer_http_method_set(efl_self, method),
+ efl_net_dialer_http_primary_mode_set(efl_self, primary_mode),
+ efl_net_dialer_http_version_set(efl_self, http_version),
+ efl_net_dialer_http_authentication_set(efl_self, username, password, authentication_method, authentication_restricted),
+ efl_net_dialer_http_allow_redirects_set(efl_self, allow_redirects),
+ efl_net_dialer_timeout_dial_set(efl_self, timeout_dial),
+ efl_event_callback_array_add(efl_self, dialer_cbs(), NULL));
+
+ EINA_LIST_FREE(headers, str)
+ {
+ char *p = strchr(str, ':');
+ if (p)
+ {
+ p[0] = '\0';
+ p++;
+ while ((p[0] != '\0') && isspace(p[0]))
+ p++;
+ }
+ efl_net_dialer_http_request_header_add(dialer, str, p);
+ free(str);
+ }
+
+ EINA_LIST_FREE(form_fields, str)
+ {
+ fprintf(stderr, "TODO: form_field %s", str);
+ free(str);
+ }
+
+ waiting = 1;
+ if (efl_net_dialer_http_primary_mode_get(dialer) == EFL_NET_DIALER_HTTP_PRIMARY_MODE_UPLOAD)
+ waiting++;
+
+ sender = efl_add(EFL_IO_COPIER_CLASS, loop,
+ efl_name_set(efl_self, "sender"),
+ efl_io_copier_source_set(efl_self, input),
+ efl_io_copier_destination_set(efl_self, dialer),
+ efl_event_callback_array_add(efl_self, copier_cbs(), NULL));
+
+ fprintf(stderr, "INFO: sending %s->%s\n",
+ efl_name_get(input),
+ efl_name_get(dialer));
+
+ receiver = efl_add(EFL_IO_COPIER_CLASS, loop,
+ efl_name_set(efl_self, "receiver"),
+ efl_io_copier_source_set(efl_self, dialer),
+ efl_io_copier_destination_set(efl_self, output),
+ efl_event_callback_array_add(efl_self, copier_cbs(), NULL));
+ fprintf(stderr, "INFO: receiving %s->%s\n",
+ efl_name_get(dialer),
+ efl_name_get(output));
+
+ err = efl_net_dialer_dial(dialer, address);
+ if (err != 0)
+ {
+ fprintf(stderr, "ERROR: could not dial '%s': %s",
+ address, eina_error_msg_get(err));
+ goto no_mainloop;
+ }
+
+ fprintf(stderr,
+ "INFO: dialed %s\n"
+ "INFO: - method=%s\n"
+ "INFO: - primary_mode=%d\n"
+ "INFO: - version=%d\n"
+ "INFO: - allow_redirects=%d\n"
+ "INFO: - timeout_dial=%fs\n"
+ "INFO: - request headers:\n",
+ efl_net_dialer_address_dial_get(dialer),
+ efl_net_dialer_http_method_get(dialer),
+ efl_net_dialer_http_primary_mode_get(dialer),
+ efl_net_dialer_http_version_get(dialer),
+ efl_net_dialer_http_allow_redirects_get(dialer),
+ efl_net_dialer_timeout_dial_get(dialer));
+
+ itr = efl_net_dialer_http_request_headers_get(dialer);
+ EINA_ITERATOR_FOREACH(itr, header)
+ fprintf(stderr, "INFO: %s: %s\n", header->key, header->value);
+ eina_iterator_free(itr);
+
+ ecore_main_loop_begin();
+
+ fprintf(stderr, "INFO: main loop finished.\n");
+
+ no_mainloop:
+ efl_io_closer_close(sender);
+ efl_del(sender);
+
+ efl_io_closer_close(receiver);
+ efl_del(receiver);
+
+ efl_del(dialer);
+ efl_del(output);
+ efl_del(input);
+
+ end:
+ ecore_con_url_shutdown();
+ ecore_con_shutdown();
+ ecore_shutdown();
+
+ return retval;
+}
diff --git a/src/lib/ecore_con/Ecore_Con_Eo.h b/src/lib/ecore_con/Ecore_Con_Eo.h
index c185aea437..22f1e70b49 100644
--- a/src/lib/ecore_con/Ecore_Con_Eo.h
+++ b/src/lib/ecore_con/Ecore_Con_Eo.h
@@ -14,3 +14,62 @@
#include "efl_net_socket_tcp.eo.h"
#include "efl_net_dialer_tcp.eo.h"
#include "efl_net_server_tcp.eo.h"
+
+#include "efl_net_http_types.eot.h"
+/* TODO: should be generated from 'var Efl.Net.Http.Error.*' */
+extern Eina_Error EFL_NET_HTTP_ERROR_BAD_CONTENT_ENCODING;
+extern Eina_Error EFL_NET_HTTP_ERROR_BAD_DOWNLOAD_RESUME;
+extern Eina_Error EFL_NET_HTTP_ERROR_BAD_FUNCTION_ARGUMENT;
+extern Eina_Error EFL_NET_HTTP_ERROR_CHUNK_FAILED;
+extern Eina_Error EFL_NET_HTTP_ERROR_CONV_FAILED;
+extern Eina_Error EFL_NET_HTTP_ERROR_CONV_REQD;
+extern Eina_Error EFL_NET_HTTP_ERROR_COULDNT_CONNECT;
+extern Eina_Error EFL_NET_HTTP_ERROR_COULDNT_RESOLVE_HOST;
+extern Eina_Error EFL_NET_HTTP_ERROR_COULDNT_RESOLVE_PROXY;
+extern Eina_Error EFL_NET_HTTP_ERROR_FAILED_INIT;
+extern Eina_Error EFL_NET_HTTP_ERROR_FILE_COULDNT_READ_FILE;
+extern Eina_Error EFL_NET_HTTP_ERROR_FILESIZE_EXCEEDED;
+extern Eina_Error EFL_NET_HTTP_ERROR_FUNCTION_NOT_FOUND;
+extern Eina_Error EFL_NET_HTTP_ERROR_GOT_NOTHING;
+extern Eina_Error EFL_NET_HTTP_ERROR_HTTP2;
+extern Eina_Error EFL_NET_HTTP_ERROR_HTTP2_STREAM;
+extern Eina_Error EFL_NET_HTTP_ERROR_HTTP_POST_ERROR;
+extern Eina_Error EFL_NET_HTTP_ERROR_HTTP_RETURNED_ERROR;
+extern Eina_Error EFL_NET_HTTP_ERROR_INTERFACE_FAILED;
+extern Eina_Error EFL_NET_HTTP_ERROR_LOGIN_DENIED;
+extern Eina_Error EFL_NET_HTTP_ERROR_NO_CONNECTION_AVAILABLE;
+extern Eina_Error EFL_NET_HTTP_ERROR_NOT_BUILT_IN;
+extern Eina_Error EFL_NET_HTTP_ERROR_OPERATION_TIMEDOUT;
+extern Eina_Error EFL_NET_HTTP_ERROR_PARTIAL_FILE;
+extern Eina_Error EFL_NET_HTTP_ERROR_PEER_FAILED_VERIFICATION;
+extern Eina_Error EFL_NET_HTTP_ERROR_RANGE_ERROR;
+extern Eina_Error EFL_NET_HTTP_ERROR_READ_ERROR;
+extern Eina_Error EFL_NET_HTTP_ERROR_RECV_ERROR;
+extern Eina_Error EFL_NET_HTTP_ERROR_REMOTE_ACCESS_DENIED;
+extern Eina_Error EFL_NET_HTTP_ERROR_REMOTE_DISK_FULL;
+extern Eina_Error EFL_NET_HTTP_ERROR_REMOTE_FILE_EXISTS;
+extern Eina_Error EFL_NET_HTTP_ERROR_REMOTE_FILE_NOT_FOUND;
+extern Eina_Error EFL_NET_HTTP_ERROR_SEND_ERROR;
+extern Eina_Error EFL_NET_HTTP_ERROR_SEND_FAIL_REWIND;
+extern Eina_Error EFL_NET_HTTP_ERROR_SSL_CACERT;
+extern Eina_Error EFL_NET_HTTP_ERROR_SSL_CACERT_BADFILE;
+extern Eina_Error EFL_NET_HTTP_ERROR_SSL_CERTPROBLEM;
+extern Eina_Error EFL_NET_HTTP_ERROR_SSL_CIPHER;
+extern Eina_Error EFL_NET_HTTP_ERROR_SSL_CONNECT_ERROR;
+extern Eina_Error EFL_NET_HTTP_ERROR_SSL_CRL_BADFILE;
+extern Eina_Error EFL_NET_HTTP_ERROR_SSL_ENGINE_INITFAILED;
+extern Eina_Error EFL_NET_HTTP_ERROR_SSL_ENGINE_NOTFOUND;
+extern Eina_Error EFL_NET_HTTP_ERROR_SSL_ENGINE_SETFAILED;
+extern Eina_Error EFL_NET_HTTP_ERROR_SSL_INVALIDCERTSTATUS;
+extern Eina_Error EFL_NET_HTTP_ERROR_SSL_ISSUER_ERROR;
+extern Eina_Error EFL_NET_HTTP_ERROR_SSL_PINNEDPUBKEYNOTMATCH;
+extern Eina_Error EFL_NET_HTTP_ERROR_SSL_SHUTDOWN_FAILED;
+extern Eina_Error EFL_NET_HTTP_ERROR_TOO_MANY_REDIRECTS;
+extern Eina_Error EFL_NET_HTTP_ERROR_UNKNOWN_OPTION;
+extern Eina_Error EFL_NET_HTTP_ERROR_UNSUPPORTED_PROTOCOL;
+extern Eina_Error EFL_NET_HTTP_ERROR_UPLOAD_FAILED;
+extern Eina_Error EFL_NET_HTTP_ERROR_URL_MALFORMAT;
+extern Eina_Error EFL_NET_HTTP_ERROR_USE_SSL_FAILED;
+extern Eina_Error EFL_NET_HTTP_ERROR_WRITE_ERROR;
+
+#include "efl_net_dialer_http.eo.h"
diff --git a/src/lib/ecore_con/ecore_con_url_curl.c b/src/lib/ecore_con/ecore_con_url_curl.c
index e241ecd6c1..92becd3741 100644
--- a/src/lib/ecore_con/ecore_con_url_curl.c
+++ b/src/lib/ecore_con/ecore_con_url_curl.c
@@ -26,6 +26,204 @@ Ecore_Con_Curl *_c = NULL;
Eina_Bool _c_fail = EINA_FALSE;
double _c_timeout = 0.0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_BAD_CONTENT_ENCODING = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_BAD_DOWNLOAD_RESUME = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_BAD_FUNCTION_ARGUMENT = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_CHUNK_FAILED = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_CONV_FAILED = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_CONV_REQD = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_COULDNT_CONNECT = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_COULDNT_RESOLVE_HOST = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_COULDNT_RESOLVE_PROXY = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_FAILED_INIT = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_FILE_COULDNT_READ_FILE = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_FILESIZE_EXCEEDED = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_FUNCTION_NOT_FOUND = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_GOT_NOTHING = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_HTTP2 = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_HTTP2_STREAM = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_HTTP_POST_ERROR = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_HTTP_RETURNED_ERROR = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_INTERFACE_FAILED = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_LOGIN_DENIED = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_NO_CONNECTION_AVAILABLE = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_NOT_BUILT_IN = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_OPERATION_TIMEDOUT = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_PARTIAL_FILE = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_PEER_FAILED_VERIFICATION = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_RANGE_ERROR = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_READ_ERROR = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_RECV_ERROR = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_REMOTE_ACCESS_DENIED = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_REMOTE_DISK_FULL = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_REMOTE_FILE_EXISTS = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_REMOTE_FILE_NOT_FOUND = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_SEND_ERROR = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_SEND_FAIL_REWIND = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_SSL_CACERT = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_SSL_CACERT_BADFILE = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_SSL_CERTPROBLEM = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_SSL_CIPHER = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_SSL_CONNECT_ERROR = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_SSL_CRL_BADFILE = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_SSL_ENGINE_INITFAILED = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_SSL_ENGINE_NOTFOUND = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_SSL_ENGINE_SETFAILED = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_SSL_INVALIDCERTSTATUS = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_SSL_ISSUER_ERROR = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_SSL_PINNEDPUBKEYNOTMATCH = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_SSL_SHUTDOWN_FAILED = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_TOO_MANY_REDIRECTS = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_UNKNOWN_OPTION = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_UNSUPPORTED_PROTOCOL = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_UPLOAD_FAILED = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_URL_MALFORMAT = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_USE_SSL_FAILED = 0;
+EAPI Eina_Error EFL_NET_HTTP_ERROR_WRITE_ERROR = 0;
+
+Eina_Error
+_curlcode_to_eina_error(const CURLcode code)
+{
+ switch (code) {
+ case CURLE_OK: return 0;
+ case CURLE_ABORTED_BY_CALLBACK: return ECONNABORTED;
+ case CURLE_AGAIN: return EAGAIN;
+ case CURLE_OUT_OF_MEMORY: return ENOMEM;
+
+#define _MAP(n) case CURLE_ ## n: return EFL_NET_HTTP_ERROR_ ## n
+
+ _MAP(BAD_CONTENT_ENCODING);
+ _MAP(BAD_DOWNLOAD_RESUME);
+ _MAP(BAD_FUNCTION_ARGUMENT);
+ _MAP(CHUNK_FAILED);
+ _MAP(CONV_FAILED);
+ _MAP(CONV_REQD);
+ _MAP(COULDNT_CONNECT);
+ _MAP(COULDNT_RESOLVE_HOST);
+ _MAP(COULDNT_RESOLVE_PROXY);
+ _MAP(FAILED_INIT);
+ _MAP(FILE_COULDNT_READ_FILE);
+ _MAP(FILESIZE_EXCEEDED);
+ _MAP(FUNCTION_NOT_FOUND);
+ _MAP(GOT_NOTHING);
+ _MAP(HTTP2);
+ _MAP(HTTP2_STREAM);
+ _MAP(HTTP_POST_ERROR);
+ _MAP(HTTP_RETURNED_ERROR);
+ _MAP(INTERFACE_FAILED);
+ _MAP(LOGIN_DENIED);
+ _MAP(NO_CONNECTION_AVAILABLE);
+ _MAP(NOT_BUILT_IN);
+ _MAP(OPERATION_TIMEDOUT);
+ _MAP(PARTIAL_FILE);
+ _MAP(PEER_FAILED_VERIFICATION);
+ _MAP(RANGE_ERROR);
+ _MAP(READ_ERROR);
+ _MAP(RECV_ERROR);
+ _MAP(REMOTE_ACCESS_DENIED);
+ _MAP(REMOTE_DISK_FULL);
+ _MAP(REMOTE_FILE_EXISTS);
+ _MAP(REMOTE_FILE_NOT_FOUND);
+ _MAP(SEND_ERROR);
+ _MAP(SEND_FAIL_REWIND);
+ _MAP(SSL_CACERT);
+ _MAP(SSL_CACERT_BADFILE);
+ _MAP(SSL_CERTPROBLEM);
+ _MAP(SSL_CIPHER);
+ _MAP(SSL_CONNECT_ERROR);
+ _MAP(SSL_CRL_BADFILE);
+ _MAP(SSL_ENGINE_INITFAILED);
+ _MAP(SSL_ENGINE_NOTFOUND);
+ _MAP(SSL_ENGINE_SETFAILED);
+ _MAP(SSL_INVALIDCERTSTATUS);
+ _MAP(SSL_ISSUER_ERROR);
+ _MAP(SSL_PINNEDPUBKEYNOTMATCH);
+ _MAP(SSL_SHUTDOWN_FAILED);
+ _MAP(TOO_MANY_REDIRECTS);
+ _MAP(UNKNOWN_OPTION);
+ _MAP(UNSUPPORTED_PROTOCOL);
+ _MAP(UPLOAD_FAILED);
+ _MAP(URL_MALFORMAT);
+ _MAP(USE_SSL_FAILED);
+ _MAP(WRITE_ERROR);
+#undef _MAP
+
+ default:
+ ERR("unexpected error CURcode=%d '%s', not mapped",
+ code, _c->curl_easy_strerror(code));
+ return EINVAL;
+ }
+}
+
+static void
+_c_init_errors(void)
+{
+ /* use from system: */
+ // CURLE_ABORTED_BY_CALLBACK = ECONNABORTED
+ // CURLE_AGAIN = EAGAIN
+ // CURLE_OUT_OF_MEMORY = ENOMEM
+
+ if (EFL_NET_HTTP_ERROR_BAD_CONTENT_ENCODING) return; /* only once in the whole runtime */
+
+#define _MAP(n) EFL_NET_HTTP_ERROR_ ## n = eina_error_msg_static_register(_c->curl_easy_strerror(CURLE_ ## n))
+ _MAP(BAD_CONTENT_ENCODING);
+ _MAP(BAD_DOWNLOAD_RESUME);
+ _MAP(BAD_FUNCTION_ARGUMENT);
+ _MAP(CHUNK_FAILED);
+ _MAP(CONV_FAILED);
+ _MAP(CONV_REQD);
+ _MAP(COULDNT_CONNECT);
+ _MAP(COULDNT_RESOLVE_HOST);
+ _MAP(COULDNT_RESOLVE_PROXY);
+ _MAP(FAILED_INIT);
+ _MAP(FILE_COULDNT_READ_FILE);
+ _MAP(FILESIZE_EXCEEDED);
+ _MAP(FUNCTION_NOT_FOUND);
+ _MAP(GOT_NOTHING);
+ _MAP(HTTP2);
+ _MAP(HTTP2_STREAM);
+ _MAP(HTTP_POST_ERROR);
+ _MAP(HTTP_RETURNED_ERROR);
+ _MAP(INTERFACE_FAILED);
+ _MAP(LOGIN_DENIED);
+ _MAP(NO_CONNECTION_AVAILABLE);
+ _MAP(NOT_BUILT_IN);
+ _MAP(OPERATION_TIMEDOUT);
+ _MAP(PARTIAL_FILE);
+ _MAP(PEER_FAILED_VERIFICATION);
+ _MAP(RANGE_ERROR);
+ _MAP(READ_ERROR);
+ _MAP(RECV_ERROR);
+ _MAP(REMOTE_ACCESS_DENIED);
+ _MAP(REMOTE_DISK_FULL);
+ _MAP(REMOTE_FILE_EXISTS);
+ _MAP(REMOTE_FILE_NOT_FOUND);
+ _MAP(SEND_ERROR);
+ _MAP(SEND_FAIL_REWIND);
+ _MAP(SSL_CACERT);
+ _MAP(SSL_CACERT_BADFILE);
+ _MAP(SSL_CERTPROBLEM);
+ _MAP(SSL_CIPHER);
+ _MAP(SSL_CONNECT_ERROR);
+ _MAP(SSL_CRL_BADFILE);
+ _MAP(SSL_ENGINE_INITFAILED);
+ _MAP(SSL_ENGINE_NOTFOUND);
+ _MAP(SSL_ENGINE_SETFAILED);
+ _MAP(SSL_INVALIDCERTSTATUS);
+ _MAP(SSL_ISSUER_ERROR);
+ _MAP(SSL_PINNEDPUBKEYNOTMATCH);
+ _MAP(SSL_SHUTDOWN_FAILED);
+ _MAP(TOO_MANY_REDIRECTS);
+ _MAP(UNKNOWN_OPTION);
+ _MAP(UNSUPPORTED_PROTOCOL);
+ _MAP(UPLOAD_FAILED);
+ _MAP(URL_MALFORMAT);
+ _MAP(USE_SSL_FAILED);
+ _MAP(WRITE_ERROR);
+#undef _MAP
+}
+
+
Eina_Bool
_c_init(void)
{
@@ -88,15 +286,20 @@ _c_init(void)
SYM(curl_multi_perform);
SYM(curl_multi_add_handle);
SYM(curl_multi_setopt);
+ SYM(curl_multi_socket_action);
+ SYM(curl_multi_assign);
SYM(curl_easy_init);
SYM(curl_easy_setopt);
SYM(curl_easy_strerror);
SYM(curl_easy_cleanup);
SYM(curl_easy_getinfo);
+ SYM(curl_easy_pause);
SYM(curl_slist_free_all);
SYM(curl_slist_append);
SYM(curl_version_info);
+ _c_init_errors();
+
// curl_global_init() is not thread safe!
if (_c->curl_global_init(CURL_GLOBAL_ALL)) goto error;
_c->_curlm = _c->curl_multi_init();
diff --git a/src/lib/ecore_con/ecore_con_url_curl.h b/src/lib/ecore_con/ecore_con_url_curl.h
index 2917fe6505..0f6b3756a3 100644
--- a/src/lib/ecore_con/ecore_con_url_curl.h
+++ b/src/lib/ecore_con/ecore_con_url_curl.h
@@ -1,6 +1,14 @@
#ifndef ECORE_CON_URL_CURL_H
#define ECORE_CON_URL_CURL_H 1
+#ifdef USE_CURL_H
+/* During development you can set USE_CURL_H to use the system's
+ * curl.h instead of the local replicated values, it will provide all
+ * constants and type-checking.
+ */
+#include <curl/curl.h>
+#else
+
// all the types, defines, enums etc. from curl that we actually USE.
// we have to add to this if we use more things from curl not already
// defined here. see curl headers to get them from
@@ -9,13 +17,173 @@ typedef enum
CURLM_CALL_MULTI_PERFORM = -1,
CURLM_OK = 0
} CURLMcode;
-typedef enum
-{
- CURLE_OK = 0,
- CURLE_OPERATION_TIMEDOUT = 28
+
+#ifndef curl_socket_typedef
+/* socket typedef */
+#if defined(WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H)
+typedef SOCKET curl_socket_t;
+#define CURL_SOCKET_BAD INVALID_SOCKET
+#else
+typedef int curl_socket_t;
+#define CURL_SOCKET_BAD -1
+#endif
+#define curl_socket_typedef
+#endif /* curl_socket_typedef */
+
+#define CURL_POLL_NONE 0
+#define CURL_POLL_IN 1
+#define CURL_POLL_OUT 2
+#define CURL_POLL_INOUT 3
+#define CURL_POLL_REMOVE 4
+
+#define CURL_SOCKET_TIMEOUT CURL_SOCKET_BAD
+
+#define CURL_CSELECT_IN 0x01
+#define CURL_CSELECT_OUT 0x02
+#define CURL_CSELECT_ERR 0x04
+
+typedef enum {
+ CURLINFO_TEXT = 0,
+ CURLINFO_HEADER_IN, /* 1 */
+ CURLINFO_HEADER_OUT, /* 2 */
+ CURLINFO_DATA_IN, /* 3 */
+ CURLINFO_DATA_OUT, /* 4 */
+ CURLINFO_SSL_DATA_IN, /* 5 */
+ CURLINFO_SSL_DATA_OUT, /* 6 */
+ CURLINFO_END
+} curl_infotype;
+
+typedef enum {
+ CURLE_OK = 0,
+ CURLE_UNSUPPORTED_PROTOCOL, /* 1 */
+ CURLE_FAILED_INIT, /* 2 */
+ CURLE_URL_MALFORMAT, /* 3 */
+ CURLE_NOT_BUILT_IN, /* 4 - [was obsoleted in August 2007 for
+ 7.17.0, reused in April 2011 for 7.21.5] */
+ CURLE_COULDNT_RESOLVE_PROXY, /* 5 */
+ CURLE_COULDNT_RESOLVE_HOST, /* 6 */
+ CURLE_COULDNT_CONNECT, /* 7 */
+ CURLE_FTP_WEIRD_SERVER_REPLY, /* 8 */
+ CURLE_REMOTE_ACCESS_DENIED, /* 9 a service was denied by the server
+ due to lack of access - when login fails
+ this is not returned. */
+ CURLE_FTP_ACCEPT_FAILED, /* 10 - [was obsoleted in April 2006 for
+ 7.15.4, reused in Dec 2011 for 7.24.0]*/
+ CURLE_FTP_WEIRD_PASS_REPLY, /* 11 */
+ CURLE_FTP_ACCEPT_TIMEOUT, /* 12 - timeout occurred accepting server
+ [was obsoleted in August 2007 for 7.17.0,
+ reused in Dec 2011 for 7.24.0]*/
+ CURLE_FTP_WEIRD_PASV_REPLY, /* 13 */
+ CURLE_FTP_WEIRD_227_FORMAT, /* 14 */
+ CURLE_FTP_CANT_GET_HOST, /* 15 */
+ CURLE_HTTP2, /* 16 - A problem in the http2 framing layer.
+ [was obsoleted in August 2007 for 7.17.0,
+ reused in July 2014 for 7.38.0] */
+ CURLE_FTP_COULDNT_SET_TYPE, /* 17 */
+ CURLE_PARTIAL_FILE, /* 18 */
+ CURLE_FTP_COULDNT_RETR_FILE, /* 19 */
+ CURLE_OBSOLETE20, /* 20 - NOT USED */
+ CURLE_QUOTE_ERROR, /* 21 - quote command failure */
+ CURLE_HTTP_RETURNED_ERROR, /* 22 */
+ CURLE_WRITE_ERROR, /* 23 */
+ CURLE_OBSOLETE24, /* 24 - NOT USED */
+ CURLE_UPLOAD_FAILED, /* 25 - failed upload "command" */
+ CURLE_READ_ERROR, /* 26 - couldn't open/read from file */
+ CURLE_OUT_OF_MEMORY, /* 27 */
+ /* Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error
+ instead of a memory allocation error if CURL_DOES_CONVERSIONS
+ is defined
+ */
+ CURLE_OPERATION_TIMEDOUT, /* 28 - the timeout time was reached */
+ CURLE_OBSOLETE29, /* 29 - NOT USED */
+ CURLE_FTP_PORT_FAILED, /* 30 - FTP PORT operation failed */
+ CURLE_FTP_COULDNT_USE_REST, /* 31 - the REST command failed */
+ CURLE_OBSOLETE32, /* 32 - NOT USED */
+ CURLE_RANGE_ERROR, /* 33 - RANGE "command" didn't work */
+ CURLE_HTTP_POST_ERROR, /* 34 */
+ CURLE_SSL_CONNECT_ERROR, /* 35 - wrong when connecting with SSL */
+ CURLE_BAD_DOWNLOAD_RESUME, /* 36 - couldn't resume download */
+ CURLE_FILE_COULDNT_READ_FILE, /* 37 */
+ CURLE_LDAP_CANNOT_BIND, /* 38 */
+ CURLE_LDAP_SEARCH_FAILED, /* 39 */
+ CURLE_OBSOLETE40, /* 40 - NOT USED */
+ CURLE_FUNCTION_NOT_FOUND, /* 41 */
+ CURLE_ABORTED_BY_CALLBACK, /* 42 */
+ CURLE_BAD_FUNCTION_ARGUMENT, /* 43 */
+ CURLE_OBSOLETE44, /* 44 - NOT USED */
+ CURLE_INTERFACE_FAILED, /* 45 - CURLOPT_INTERFACE failed */
+ CURLE_OBSOLETE46, /* 46 - NOT USED */
+ CURLE_TOO_MANY_REDIRECTS, /* 47 - catch endless re-direct loops */
+ CURLE_UNKNOWN_OPTION, /* 48 - User specified an unknown option */
+ CURLE_TELNET_OPTION_SYNTAX, /* 49 - Malformed telnet option */
+ CURLE_OBSOLETE50, /* 50 - NOT USED */
+ CURLE_PEER_FAILED_VERIFICATION, /* 51 - peer's certificate or fingerprint
+ wasn't verified fine */
+ CURLE_GOT_NOTHING, /* 52 - when this is a specific error */
+ CURLE_SSL_ENGINE_NOTFOUND, /* 53 - SSL crypto engine not found */
+ CURLE_SSL_ENGINE_SETFAILED, /* 54 - can not set SSL crypto engine as
+ default */
+ CURLE_SEND_ERROR, /* 55 - failed sending network data */
+ CURLE_RECV_ERROR, /* 56 - failure in receiving network data */
+ CURLE_OBSOLETE57, /* 57 - NOT IN USE */
+ CURLE_SSL_CERTPROBLEM, /* 58 - problem with the local certificate */
+ CURLE_SSL_CIPHER, /* 59 - couldn't use specified cipher */
+ CURLE_SSL_CACERT, /* 60 - problem with the CA cert (path?) */
+ CURLE_BAD_CONTENT_ENCODING, /* 61 - Unrecognized/bad encoding */
+ CURLE_LDAP_INVALID_URL, /* 62 - Invalid LDAP URL */
+ CURLE_FILESIZE_EXCEEDED, /* 63 - Maximum file size exceeded */
+ CURLE_USE_SSL_FAILED, /* 64 - Requested FTP SSL level failed */
+ CURLE_SEND_FAIL_REWIND, /* 65 - Sending the data requires a rewind
+ that failed */
+ CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialise ENGINE */
+ CURLE_LOGIN_DENIED, /* 67 - user, password or similar was not
+ accepted and we failed to login */
+ CURLE_TFTP_NOTFOUND, /* 68 - file not found on server */
+ CURLE_TFTP_PERM, /* 69 - permission problem on server */
+ CURLE_REMOTE_DISK_FULL, /* 70 - out of disk space on server */
+ CURLE_TFTP_ILLEGAL, /* 71 - Illegal TFTP operation */
+ CURLE_TFTP_UNKNOWNID, /* 72 - Unknown transfer ID */
+ CURLE_REMOTE_FILE_EXISTS, /* 73 - File already exists */
+ CURLE_TFTP_NOSUCHUSER, /* 74 - No such user */
+ CURLE_CONV_FAILED, /* 75 - conversion failed */
+ CURLE_CONV_REQD, /* 76 - caller must register conversion
+ callbacks using curl_easy_setopt options
+ CURLOPT_CONV_FROM_NETWORK_FUNCTION,
+ CURLOPT_CONV_TO_NETWORK_FUNCTION, and
+ CURLOPT_CONV_FROM_UTF8_FUNCTION */
+ CURLE_SSL_CACERT_BADFILE, /* 77 - could not load CACERT file, missing
+ or wrong format */
+ CURLE_REMOTE_FILE_NOT_FOUND, /* 78 - remote file not found */
+ CURLE_SSH, /* 79 - error from the SSH layer, somewhat
+ generic so the error message will be of
+ interest when this has happened */
+
+ CURLE_SSL_SHUTDOWN_FAILED, /* 80 - Failed to shut down the SSL
+ connection */
+ CURLE_AGAIN, /* 81 - socket is not ready for send/recv,
+ wait till it's ready and try again (Added
+ in 7.18.2) */
+ CURLE_SSL_CRL_BADFILE, /* 82 - could not load CRL file, missing or
+ wrong format (Added in 7.19.0) */
+ CURLE_SSL_ISSUER_ERROR, /* 83 - Issuer check failed. (Added in
+ 7.19.0) */
+ CURLE_FTP_PRET_FAILED, /* 84 - a PRET command failed */
+ CURLE_RTSP_CSEQ_ERROR, /* 85 - mismatch of RTSP CSeq numbers */
+ CURLE_RTSP_SESSION_ERROR, /* 86 - mismatch of RTSP Session Ids */
+ CURLE_FTP_BAD_FILE_LIST, /* 87 - unable to parse FTP file list */
+ CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error */
+ CURLE_NO_CONNECTION_AVAILABLE, /* 89 - No connection available, the
+ session will be queued */
+ CURLE_SSL_PINNEDPUBKEYNOTMATCH, /* 90 - specified pinned public key did not
+ match */
+ CURLE_SSL_INVALIDCERTSTATUS, /* 91 - invalid certificate status */
+ CURLE_HTTP2_STREAM, /* 92 - stream error in HTTP/2 framing layer
+ */
+ CURL_LAST /* never use! */
} CURLcode;
#define CURLOPTTYPE_LONG 0
#define CURLOPTTYPE_OBJECTPOINT 10000
+#define CURLOPTTYPE_STRINGPOINT 10000
#define CURLOPTTYPE_FUNCTIONPOINT 20000
#define CURLOPTTYPE_OFF_T 30000
#define CINIT(na, t, nu) CURLOPT_ ## na = CURLOPTTYPE_ ## t + nu
@@ -29,6 +197,7 @@ typedef enum
CINIT(WRITEFUNCTION, FUNCTIONPOINT, 11),
CINIT(READFUNCTION, FUNCTIONPOINT, 12),
CINIT(POSTFIELDS, OBJECTPOINT, 15),
+ CINIT(USERAGENT, STRINGPOINT, 18),
CINIT(HTTPHEADER, OBJECTPOINT, 23),
CINIT(WRITEHEADER, OBJECTPOINT, 29),
CINIT(COOKIEFILE, OBJECTPOINT, 31),
@@ -40,6 +209,7 @@ typedef enum
CINIT(NOBODY, LONG, 44),
CINIT(UPLOAD, LONG, 46),
CINIT(POST, LONG, 47),
+ CINIT(PUT, LONG, 54),
CINIT(FOLLOWLOCATION, LONG, 52),
CINIT(PROGRESSFUNCTION, FUNCTIONPOINT, 56),
CINIT(PROGRESSDATA, OBJECTPOINT, 57),
@@ -47,18 +217,26 @@ typedef enum
CINIT(SSL_VERIFYPEER, LONG, 64),
CINIT(CAINFO, OBJECTPOINT, 65),
CINIT(CONNECTTIMEOUT, LONG, 78),
+ CINIT(CONNECTTIMEOUT_MS, LONG, 156),
CINIT(HEADERFUNCTION, FUNCTIONPOINT, 79),
+ CINIT(HTTPGET, LONG, 80),
CINIT(COOKIEJAR, OBJECTPOINT, 82),
CINIT(HTTP_VERSION, LONG, 84),
CINIT(FTP_USE_EPSV, LONG, 85),
+ CINIT(DEBUGFUNCTION, FUNCTIONPOINT, 94),
+ CINIT(DEBUGDATA, OBJECTPOINT, 95),
CINIT(COOKIESESSION, LONG, 96),
CINIT(PROXYTYPE, LONG, 101),
CINIT(ACCEPT_ENCODING, OBJECTPOINT, 102),
+ CINIT(PRIVATE, OBJECTPOINT, 103),
CINIT(HTTPAUTH, LONG, 107),
CINIT(INFILESIZE_LARGE, OFF_T, 115),
+ CINIT(POSTFIELDSIZE_LARGE, OFF_T, 120),
CINIT(COOKIELIST, OBJECTPOINT, 135),
CINIT(USERNAME, OBJECTPOINT, 173),
- CINIT(PASSWORD, OBJECTPOINT, 174)
+ CINIT(PASSWORD, OBJECTPOINT, 174),
+ CINIT(XFERINFOFUNCTION, FUNCTIONPOINT, 219),
+#define CURLOPT_XFERINFODATA CURLOPT_PROGRESSDATA
} CURLoption;
#define CURLINFO_STRING 0x100000
#define CURLINFO_LONG 0x200000
@@ -68,7 +246,15 @@ typedef enum
#define CURLINFO_TYPEMASK 0xf00000
typedef enum
{
- CURLINFO_RESPONSE_CODE = CURLINFO_LONG + 2,
+ CURLINFO_EFFECTIVE_URL = CURLINFO_STRING + 1,
+ CURLINFO_RESPONSE_CODE = CURLINFO_LONG + 2,
+ CURLINFO_CONTENT_LENGTH_DOWNLOAD = CURLINFO_DOUBLE + 15,
+ CURLINFO_CONTENT_TYPE = CURLINFO_STRING + 18,
+ CURLINFO_PRIVATE = CURLINFO_STRING + 21,
+ CURLINFO_HTTP_VERSION = CURLINFO_LONG + 46,
+ CURLINFO_OS_ERRNO = CURLINFO_LONG + 25,
+ CURLINFO_LOCAL_IP = CURLINFO_STRING + 41,
+ CURLINFO_LOCAL_PORT = CURLINFO_LONG + 42,
} CURLINFO;
typedef enum
{
@@ -82,7 +268,11 @@ typedef enum
#define CINIT(name, type, num) CURLMOPT_ ## name = CURLOPTTYPE_ ## type + num
typedef enum
{
- CINIT(PIPELINING, LONG, 3)
+ CINIT(SOCKETFUNCTION, FUNCTIONPOINT, 1),
+ CINIT(SOCKETDATA, OBJECTPOINT, 2),
+ CINIT(PIPELINING, LONG, 3),
+ CINIT(TIMERFUNCTION, FUNCTIONPOINT, 4),
+ CINIT(TIMERDATA, OBJECTPOINT, 5)
} CURLMoption;
typedef enum
{
@@ -92,8 +282,10 @@ typedef enum
} curl_TimeCond;
enum
{
+ CURL_HTTP_VERSION_NONE = 0,
CURL_HTTP_VERSION_1_0 = 1,
CURL_HTTP_VERSION_1_1 = 2,
+ CURL_HTTP_VERSION_2_0 = 3
};
typedef enum
{
@@ -112,11 +304,26 @@ typedef enum
#define CURLOPT_READDATA CURLOPT_INFILE
#define CURLOPT_HEADERDATA CURLOPT_WRITEHEADER
#define CURLVERSION_NOW CURLVERSION_FOURTH
+#define CURLAUTH_NONE ((unsigned long)0)
#define CURLAUTH_BASIC (((unsigned long)1) << 0)
+#define CURLAUTH_DIGEST (((unsigned long)1)<<1)
+#define CURLAUTH_NEGOTIATE (((unsigned long)1)<<2)
#define CURLAUTH_DIGEST_IE (((unsigned long)1) << 4)
#define CURLAUTH_ANY (~CURLAUTH_DIGEST_IE)
-#define CURLAUTH_ANYSAFE (~(CURLAUTH_BASIC | CURLAUTH_DIGEST_IE))
+#define CURLAUTH_NTLM (((unsigned long)1)<<3)
+#define CURLAUTH_NTLM_WB (((unsigned long)1)<<5)
+#define CURLAUTH_ONLY (((unsigned long)1)<<31)
+#define CURLAUTH_ANY (~CURLAUTH_DIGEST_IE)
+#define CURLAUTH_ANYSAFE (~(CURLAUTH_BASIC|CURLAUTH_DIGEST_IE))
#define CURL_READFUNC_ABORT 0x10000000
+#define CURL_READFUNC_PAUSE 0x10000001
+#define CURL_WRITEFUNC_PAUSE 0x10000001
+
+#define CURLPAUSE_RECV (1<<0)
+#define CURLPAUSE_RECV_CONT (0)
+
+#define CURLPAUSE_SEND (1<<2)
+#define CURLPAUSE_SEND_CONT (0)
typedef void CURLM;
typedef void CURL;
@@ -153,6 +360,9 @@ typedef struct
} data;
} CURLMsg;
+#endif /* USE_CURL_H */
+
+
typedef struct _Ecore_Con_Curl Ecore_Con_Curl;
struct _Ecore_Con_Curl
@@ -183,11 +393,19 @@ struct _Ecore_Con_Curl
CURL *curl_handle);
CURLMcode (*curl_multi_setopt)(CURLM *multi_handle,
CURLMoption option, ...);
+ CURLMcode (*curl_multi_socket_action)(CURLM *multi_handle,
+ curl_socket_t fd,
+ int ev_bitmask,
+ int *running_handles);
+ CURLMcode (*curl_multi_assign)(CURLM *multi_handle,
+ curl_socket_t sockfd,
+ void *sockp);
CURL *(*curl_easy_init)(void);
CURLcode (*curl_easy_setopt)(CURL *curl, CURLoption option, ...);
const char *(*curl_easy_strerror)(CURLcode);
void (*curl_easy_cleanup)(CURL *curl);
CURLcode (*curl_easy_getinfo)(CURL *curl, CURLINFO info, ...);
+ CURLcode (*curl_easy_pause)(CURL *curl, int bitmask);
void (*curl_slist_free_all)(struct curl_slist *);
struct curl_slist *(*curl_slist_append)(struct curl_slist *list,
const char *string);
@@ -202,5 +420,6 @@ extern double _c_timeout;
Eina_Bool _c_init(void);
void _c_shutdown(void);
+Eina_Error _curlcode_to_eina_error(const CURLcode code);
#endif
diff --git a/src/lib/ecore_con/efl_net_dialer_http.c b/src/lib/ecore_con/efl_net_dialer_http.c
new file mode 100644
index 0000000000..f25c9ecd70
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_dialer_http.c
@@ -0,0 +1,1699 @@
+#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
+
+#define EFL_NET_DIALER_HTTP_BUFFER_RECEIVE_SIZE (1U << 14) /* 16Kb to receive */
+
+#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>
+
+/* 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__)
+
+#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;
+ Ecore_Fd_Handler *fdhandler;
+ Eina_Stringshare *address_dial;
+ Eina_Stringshare *address_local;
+ Eina_Stringshare *address_remote;
+ Eina_Stringshare *method;
+ Eina_Stringshare *user_agent;
+ 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;
+ 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;
+ 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;
+
+static void
+_efl_net_dialer_http_curlm_check(Efl_Net_Dialer_Http_Curlm *cm)
+{
+ CURLMsg *msg;
+ int remaining;
+
+ 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);
+ if (re == CURLE_OK)
+ {
+ Eo *dialer = (Eo *)priv;
+
+ 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_event_callback_call(dialer, EFL_NET_DIALER_EVENT_ERROR, &err);
+ }
+
+ if (msg->msg != CURLMSG_DONE) continue;
+
+ if (!efl_io_closer_closed_get(dialer))
+ efl_io_reader_eos_set(dialer, EINA_TRUE);
+ else
+ DBG("HTTP dialer=%p already closed", dialer);
+ }
+ }
+}
+
+static void
+_efl_net_dialer_http_curlm_timer_do(void *data, const Eo_Event *ev EINA_UNUSED)
+{
+ 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,
+ efl_loop_timer_interval_set(efl_self, seconds),
+ efl_event_callback_add(efl_self, EFL_LOOP_TIMER_EVENT_TICK, _efl_net_dialer_http_curlm_timer_do, cm));
+ EINA_SAFETY_ON_NULL_RETURN_VAL(cm->timer, -1);
+ }
+
+ return 0;
+}
+
+#if 0
+// it seems the Eo_Loop_Fd isn't working properly when we change connections...
+// as it's still built on top of Ecore_Fd_Handler, then use it directly.
+static void
+_efl_net_dialer_http_curlm_event_fd_read(void *data, const Eo_Event *event)
+{
+ Efl_Net_Dialer_Http_Curlm *cm = data;
+ int fd = efl_loop_fd_get(event->object);
+ CURLMcode r;
+
+ ERR("XXX socket=%d CURL_CSELECT_IN", fd);
+ r = curl_multi_socket_action(cm->multi, fd, CURL_CSELECT_IN, &cm->running);
+ if (r != CURLM_OK)
+ ERR("socket action CURL_CSELECT_IN fd=%d failed: %s", fd, curl_multi_strerror(r));
+
+ _efl_net_dialer_http_curlm_check(cm);
+}
+
+static void
+_efl_net_dialer_http_curlm_event_fd_write(void *data, const Eo_Event *event)
+{
+ Efl_Net_Dialer_Http_Curlm *cm = data;
+ int fd = efl_loop_fd_get(event->object);
+ CURLMcode r;
+
+ ERR("XXX socket=%d CURL_CSELECT_OUT", fd);
+ r = curl_multi_socket_action(cm->multi, fd, CURL_CSELECT_OUT, &cm->running);
+ if (r != CURLM_OK)
+ ERR("socket action CURL_CSELECT_OUT fd=%d failed: %s", fd, curl_multi_strerror(r));
+
+ _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);
+ 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
+ {
+ pd->fdhandler = fdhandler = efl_add(EFL_LOOP_FD_CLASS, cm->loop);
+ 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);
+
+ ERR("changed flags %#x -> %#x, read: %d/%d, write: %d/%d", flags, what, was_read, is_read, was_write, is_write);
+
+ if (was_read && !is_read)
+ {
+ //efl_event_callback_del(fdhandler, EFL_LOOP_FD_EVENT_READ, _efl_net_dialer_http_curlm_event_fd_read, cm);
+ ERR("XXX del %d read cb", fd);
+ }
+ 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);
+ ERR("XXX add %d read cb", fd);
+ }
+
+ if (was_write && !is_write)
+ {
+ //efl_event_callback_del(fdhandler, EFL_LOOP_FD_EVENT_WRITE, _efl_net_dialer_http_curlm_event_fd_write, cm);
+ ERR("XXX del %d write cb", fd);
+ }
+ 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);
+ ERR("XXX add %d write cb", fd);
+ }
+
+ efl_key_data_set(fdhandler, "curl_flags", (void *)(intptr_t)what);
+ }
+
+ ERR("XXX finished manage fd=%d, what=%#x, cm=%p, fdhandler=%p", fd, what, cm, fdhandler);
+
+ return 0;
+}
+#else
+// XXX BEGIN Legacy FD Handler:
+static Eina_Bool
+_efl_net_dialer_http_curlm_event_fd(void *data, Ecore_Fd_Handler *fdhandler)
+{
+ Efl_Net_Dialer_Http_Curlm *cm = data;
+ int fd, flags = 0;
+ CURLMcode r;
+
+ if (ecore_main_fd_handler_active_get(fdhandler, ECORE_FD_READ))
+ flags |= CURL_CSELECT_IN;
+ if (ecore_main_fd_handler_active_get(fdhandler, ECORE_FD_WRITE))
+ flags |= CURL_CSELECT_OUT;
+
+ fd = ecore_main_fd_handler_fd_get(fdhandler);
+ r = curl_multi_socket_action(cm->multi, fd, flags, &cm->running);
+ if (r != CURLM_OK)
+ ERR("socket action %#x fd=%d failed: %s", flags, fd, curl_multi_strerror(r));
+
+ _efl_net_dialer_http_curlm_check(cm);
+
+ return EINA_TRUE;
+}
+
+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;
+ Ecore_Fd_Handler *fdhandler = fdhandler_data;
+ Eo *dialer;
+ Efl_Net_Dialer_Http_Data *pd;
+ char *priv;
+ CURLcode re;
+
+ re = curl_easy_getinfo(e, CURLINFO_PRIVATE, &priv);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(re != CURLE_OK, -1);
+ 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;
+ ecore_main_fd_handler_del(fdhandler);
+ }
+ else
+ {
+ Ecore_Fd_Handler_Flags flags = 0;
+
+ if (what & CURL_POLL_IN) flags |= ECORE_FD_READ;
+ if (what & CURL_POLL_OUT) flags |= ECORE_FD_WRITE;
+
+ if (fdhandler)
+ ecore_main_fd_handler_active_set(fdhandler, flags);
+ else
+ {
+ pd->fdhandler = fdhandler = ecore_main_fd_handler_add(fd, flags, _efl_net_dialer_http_curlm_event_fd, cm, NULL, NULL);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(fdhandler, -1);
+ curl_multi_assign(cm->multi, fd, fdhandler);
+ }
+ }
+
+ DBG("dialer=%p fdhandler=%p, fd=%d, curl_easy=%p, flags=%#x",
+ dialer, pd->fdhandler, fd, e, what);
+
+ return 0;
+}
+// XXX END Legacy FD Handler.
+#endif
+
+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);
+ 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)
+ {
+ curl_multi_cleanup(cm->multi);
+ cm->multi = NULL;
+ }
+}
+
+// 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;
+ efl_event_callback_call(o, EFL_NET_DIALER_HTTP_EVENT_HEADERS_DONE, NULL);
+}
+
+/* take data from internal buffer filled with efl_io_writer_write()
+ * and send to curl.
+ */
+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};
+
+ 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;
+
+ static int i = 0; i++; if (i % 5 == 0) return 0x10000000;
+
+ if (rw_slice.len == 0)
+ {
+ pd->pause |= CURLPAUSE_SEND;
+ return CURL_READFUNC_PAUSE;
+ }
+
+ return rw_slice.len;
+}
+
+/* take data from curl into our internal buffer until
+ * efl_io_reader_read() consumes it
+ */
+static size_t
+_efl_net_dialer_http_receive_data(const void *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 = {
+ .bytes = pd->recv.bytes + pd->recv.used,
+ .len = pd->recv.limit - pd->recv.used,
+ };
+ Eina_Slice ro_slice = {
+ .bytes = buffer,
+ .len = count * nitems,
+ };
+
+ 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;
+ }
+
+ if (rw_slice.len == 0)
+ {
+ 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);
+
+ if (rw_slice.len == 0)
+ {
+ pd->pause |= CURLPAUSE_RECV;
+ return CURL_WRITEFUNC_PAUSE;
+ }
+
+ return rw_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);
+ size_t len = count * nitems;
+ Efl_Net_Http_Header *h;
+ char *p;
+
+ if (len == 0)
+ {
+ if (!pd->connected) _efl_net_dialer_http_connected(o, pd);
+ return 0;
+ }
+
+ h = malloc(sizeof(Efl_Net_Http_Header) + len + 1);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(h, 0);
+
+ h->key = p = (char *)h + sizeof(Efl_Net_Http_Header);
+ memcpy(p, buffer, len);
+ p[len] = '\0';
+
+ h->value = p = strchr(p, ':');
+ if (!p)
+ p = (char *)h->key + len - 1;
+ else
+ {
+ char *t;
+
+ p[0] = '\0';
+ p--;
+ h->value++;
+ while (h->value[0] && isspace(h->value[0]))
+ h->value++;
+
+ t = (char *)h->key + len - 1;
+ 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]))
+ {
+ free(h);
+ return len;
+ }
+
+ if (!h->value)
+ {
+ if (strncmp(h->key, "HTTP/", strlen("HTTP/")) != 0)
+ WRN("unexpected header '%.*s'", (int)len, buffer);
+ 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);
+
+ return len;
+}
+
+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);
+ curl_easy_setopt(pd->easy, CURLOPT_READFUNCTION, _efl_net_dialer_http_send_data);
+ curl_easy_setopt(pd->easy, CURLOPT_READDATA, o);
+
+ curl_easy_setopt(pd->easy, CURLOPT_NOPROGRESS, 0L);
+
+ 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);
+ efl_net_dialer_timeout_dial_set(o, 30.0);
+ return o;
+}
+
+EOLIAN static void
+_efl_net_dialer_http_efl_object_destructor(Eo *o, Efl_Net_Dialer_Http_Data *pd)
+{
+ if (!efl_io_closer_closed_get(o))
+ efl_io_closer_close(o);
+
+ efl_net_dialer_http_response_headers_clear(o);
+
+ if (pd->easy)
+ {
+ 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);
+ 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);
+}
+
+EOLIAN static Eina_Error
+_efl_net_dialer_http_efl_net_dialer_dial(Eo *o, Efl_Net_Dialer_Http_Data *pd, const char *address)
+{
+ Efl_Net_Dialer_Http_Curlm *cm;
+ 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);
+
+ // TODO: proxy
+
+ 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;
+ }
+
+ // TODO cookies
+
+ // TODO: move this to be per-loop once multiple mainloops are supported
+ // this would need to attach something to the loop
+ cm = &_cm_global;
+ if (!cm->loop) cm->loop = efl_loop_user_loop_get(o);
+ 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 ENOSYS;
+ }
+
+ pd->cm = cm;
+
+ 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 *
+_efl_net_dialer_http_efl_net_dialer_address_dial_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
+{
+ 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;
+ if (connected) efl_event_callback_call(o, EFL_NET_DIALER_EVENT_CONNECTED, NULL);
+}
+
+EOLIAN static Eina_Bool
+_efl_net_dialer_http_efl_net_dialer_connected_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
+{
+ return pd->connected;
+}
+
+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
+_efl_net_dialer_http_efl_net_dialer_timeout_dial_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
+{
+ 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 *
+_efl_net_dialer_http_efl_net_socket_address_local_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
+{
+ 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))
+ efl_event_callback_call(o, EFL_NET_DIALER_EVENT_RESOLVED, NULL);
+}
+
+EOLIAN static const char *
+_efl_net_dialer_http_efl_net_socket_address_remote_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
+{
+ return pd->address_remote;
+}
+
+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;
+ 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);
+
+ if ((pd->pause & CURLPAUSE_RECV) && (pd->recv.used < pd->recv.limit))
+ {
+ CURLcode r;
+ pd->pause &= ~CURLPAUSE_RECV;
+ r = curl_easy_pause(pd->easy, pd->pause);
+ if (r != CURLE_OK)
+ {
+ Eina_Error err = _curlcode_to_eina_error(r);
+ ERR("dialer=%p could not unpause receive (flags=%#x): %s",
+ o, pd->pause, eina_error_msg_get(err));
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+EOLIAN static Eina_Bool
+_efl_net_dialer_http_efl_io_reader_can_read_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
+{
+ 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;
+ efl_event_callback_call(o, EFL_IO_READER_EVENT_CAN_READ_CHANGED, NULL);
+}
+
+EOLIAN static Eina_Bool
+_efl_net_dialer_http_efl_io_reader_eos_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
+{
+ 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)
+ efl_event_callback_call(o, EFL_IO_READER_EVENT_EOS, NULL);
+}
+
+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;
+ CURLMcode r;
+
+ 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;
+ r = curl_easy_pause(pd->easy, pd->pause);
+ if (r != CURLM_OK)
+ {
+ err = _curlcode_to_eina_error(r);
+ ERR("dialer=%p could not unpause send (flags=%#x): %s",
+ o, pd->pause, eina_error_msg_get(err));
+ goto error;
+ }
+
+ pd->error = 0;
+ r = curl_multi_socket_action(pd->cm->multi,
+ ecore_main_fd_handler_fd_get(pd->fdhandler),
+ CURL_CSELECT_OUT, &pd->cm->running);
+ if (r != CURLM_OK)
+ {
+ err = _curlcode_to_eina_error(r);
+ ERR("dialer=%p could not trigger socket=%d action: %s",
+ o, ecore_main_fd_handler_fd_get(pd->fdhandler),
+ eina_error_msg_get(err));
+ 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
+_efl_net_dialer_http_efl_io_writer_can_write_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
+{
+ 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;
+ efl_event_callback_call(o, EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, NULL);
+}
+
+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);
+
+ if (!pd->easy) goto end;
+
+ if (pd->cm)
+ {
+ _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);
+ ecore_main_fd_handler_del(pd->fdhandler);
+ pd->fdhandler = NULL;
+ }
+
+ end:
+ efl_io_writer_can_write_set(o, EINA_FALSE);
+ efl_io_reader_can_read_set(o, EINA_FALSE);
+ efl_io_reader_eos_set(o, EINA_FALSE);
+ efl_net_dialer_connected_set(o, EINA_FALSE);
+ pd->closed = EINA_TRUE;
+ efl_event_callback_call(o, EFL_IO_CLOSER_EVENT_CLOSED, NULL);
+ return err;
+}
+
+EOLIAN static Eina_Bool
+_efl_net_dialer_http_efl_io_closer_closed_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
+{
+ return pd->closed;
+}
+
+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);
+ if (pm == EFL_NET_DIALER_HTTP_PRIMARY_MODE_UPLOAD)
+ {
+ 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
+_efl_net_dialer_http_efl_io_sizer_size_get(Eo *o, Efl_Net_Dialer_Http_Data *pd)
+{
+ 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;
+}
+
+EOLIAN static void
+_efl_net_dialer_http_method_set(Eo *o, Efl_Net_Dialer_Http_Data *pd, const char *method)
+{
+ CURLcode r;
+
+ EINA_SAFETY_ON_NULL_RETURN(method);
+
+ 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);
+ else if (strcasecmp(method, "PUT") == 0)
+ r = curl_easy_setopt(pd->easy, CURLOPT_PUT, 1L);
+ else
+ r = curl_easy_setopt(pd->easy, CURLOPT_CUSTOMREQUEST, method);
+ if (r != CURLE_OK)
+ ERR("dialer=%p could not configure HTTP method: %s: %s",
+ o, method, curl_easy_strerror(r));
+
+ eina_stringshare_replace(&pd->method, method);
+}
+
+EOLIAN static const char *
+_efl_net_dialer_http_method_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
+{
+ return pd->method;
+}
+
+EOLIAN static void
+_efl_net_dialer_http_primary_mode_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd, Efl_Net_Dialer_Http_Primary_Mode primary_mode)
+{
+ pd->primary_mode = primary_mode;
+}
+
+EOLIAN static Efl_Net_Dialer_Http_Primary_Mode
+_efl_net_dialer_http_primary_mode_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
+{
+ 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 *
+_efl_net_dialer_http_user_agent_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
+{
+ 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
+_efl_net_dialer_http_http_version_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
+{
+ 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
+_efl_net_dialer_http_authentication_get(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)
+{
+ 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
+_efl_net_dialer_http_allow_redirects_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
+{
+ return pd->allow_redirects;
+}
+
+EOLIAN static Efl_Net_Http_Status
+_efl_net_dialer_http_response_status_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
+{
+ 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)
+ efl_event_callback_call(o, EFL_IO_SIZER_EVENT_SIZE_CHANGED, NULL);
+}
+
+EOLIAN static int64_t
+_efl_net_dialer_http_request_content_length_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
+{
+ 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)
+ efl_event_callback_call(o, EFL_IO_SIZER_EVENT_SIZE_CHANGED, NULL);
+}
+
+EOLIAN static int64_t
+_efl_net_dialer_http_response_content_length_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
+{
+ 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 *
+_efl_net_dialer_http_response_content_type_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd)
+{
+ 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
+_efl_net_dialer_http_progress_download_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd, uint64_t *now, uint64_t *total)
+{
+ if (now) *now = pd->progress.download.now;
+ if (total) *total = pd->progress.download.total;
+}
+
+EOLIAN static void
+_efl_net_dialer_http_progress_upload_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Http_Data *pd, uint64_t *now, uint64_t *total)
+{
+ if (now) *now = pd->progress.upload.now;
+ if (total) *total = pd->progress.upload.total;
+}
+
+#include "efl_net_dialer_http.eo.c"
diff --git a/src/lib/ecore_con/efl_net_dialer_http.eo b/src/lib/ecore_con/efl_net_dialer_http.eo
new file mode 100644
index 0000000000..84e6a3f009
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_dialer_http.eo
@@ -0,0 +1,314 @@
+import efl_net_http_types;
+
+enum Efl.Net.Dialer.Http.Primary_Mode {
+ auto,
+ download,
+ upload,
+}
+
+class Efl.Net.Dialer.Http (Efl.Loop_User, Efl.Net.Dialer, Efl.Io.Reader, Efl.Io.Writer, Efl.Io.Sizer) { /* TODO: reader/writer should be from dialer->socket, but are being missed somehow... */
+ [[HTTP Dialer (Client).
+
+ The effective URL in use, if @.allow_redirects is $true will be
+ available as @Efl.Net.Socket.address_remote. The
+ @Efl.Net.Socket.address_local is an IP:PORT pair.
+
+ The dialer can do bi-directional information exchange. It can
+ also do a PUT and upload a file, or GET and download one. Anoter
+ case is to do a POST with some form values, including a file,
+ and retrieve its headers and response body. To make usage more
+ streamlined, choose your primary interest with @.primary_mode
+ then get some properties such as @Efl.Io.Sizer.size to report or
+ use what matters to your use case.
+
+ If @.allow_redirects is $true, multiple "connected" and
+ "resolved" signals will be dispatched, one for each
+ connection. Then @.response_status and @.response_headers_get
+ will keep changing. Using @.response_headers_all_get one can see
+ the whole history of headers and connections.
+
+ To enable debugging use EINA_LOG_LEVELS=ecore_con:4 environment
+ variable.
+
+ @since 1.19
+ ]]
+ methods {
+ @property method {
+ [[The HTTP method to use.
+
+ A string representing the HTTP method to use, such as
+ GET, POST, HEAD, PUT, DELETE...
+
+ This should be set before dialing.
+ ]]
+ get { }
+ set { }
+ values {
+ method: string;
+ }
+ }
+
+ @property primary_mode {
+ [[Is this request primarily a download or upload?
+
+ This property will change the behavior of @Efl.Io.Sizer:
+
+ - if @Efl.Net.Dialer.Http.Primary_Mode.auto, then
+ @Efl.Net.Dialer.Http.Primary_Mode.download or
+ @Efl.Net.Dialer.Http.Primary_Mode.upload will be
+ choosen based on the @.method: if "PUT", then it's
+ upload, otherwise it's download.
+
+ - if @Efl.Net.Dialer.Http.Primary_Mode.upload, applying
+ a new size with @Efl.Io.Sizer.resize or
+ @Efl.Io.Sizer.size.set will specify the
+ "Content-Length" to upload. If no size is previously
+ set, then the upload will happen in
+ "Transfer-encoding: chunked".
+
+ - if @Efl.Net.Dialer.Http.Primary_Mode.download, then
+ @Efl.Io.Sizer.size.get will report the
+ "Content-Length" provided by the server, if any.
+
+ If is worth to mention that one can provide and
+ retrieve these values using @.request_headers_get (to
+ send) and @.response_headers_get (what was received),
+ as well as using the specific properties
+ @.request_content_length (upload) and
+ @.response_content_length (download).
+ ]]
+ get {
+ [[The effective primary mode.
+
+ This will return one of
+ @Efl.Net.Dialer.Http.Primary_Mode.download or
+ @Efl.Net.Dialer.Http.Primary_Mode.upload. If "auto"
+ was set (the default), then it will pick the best
+ based on the @.method in use.
+ ]]
+ }
+ set { }
+ values {
+ primary_mode: Efl.Net.Dialer.Http.Primary_Mode;
+ }
+ }
+
+ @property user_agent {
+ [[The User-Agent to specify.
+
+ This should be set before dialing.
+ ]]
+ get { }
+ set { }
+ values {
+ user_agent: string;
+ }
+ }
+
+ @property http_version {
+ [[The HTTP version to use.
+
+ This should be set before dialing.
+
+ Once connected, it will change to the actual connection
+ HTTP version, so check after "connected" event.
+ ]]
+ get { }
+ set { }
+ values {
+ http_version: Efl.Net.Http.Version;
+ }
+ }
+
+ @property authentication {
+ [[HTTP authentication to use.
+
+ This should be set before dialing.
+ ]]
+ get { }
+ set { }
+ values {
+ username: string;
+ password: string;
+ method: Efl.Net.Http.Authentication_Method @optional; [[authentication method to use, defaults to @Efl.Net.Http.Authentication_Method.basic]]
+ restricted: bool @optional; [[restrict method]]
+ }
+ }
+
+ @property allow_redirects {
+ [[Allow HTTP redirects to be followed.
+
+ This should be set before dialing.
+ ]]
+ get { }
+ set { }
+ values {
+ allow_redirects: bool;
+ }
+ }
+
+ request_header_add {
+ [[Add a request header 'key: value'.
+
+ See @.request_headers_clear
+
+ This should be called before dialing.
+ ]]
+ params {
+ @in key: string;
+ @in value: string;
+ }
+ }
+
+ request_headers_clear {
+ [[Clear all request headers.
+
+ See @.request_header_add
+
+ This should be called before dialing.
+ ]]
+ }
+
+ request_headers_get {
+ [[Return an iterator to the key-value pairs for request headers]]
+ return: free(own(iterator<Efl.Net.Http.Header>), eina_iterator_free) @warn_unused;
+ }
+
+ @property request_content_length {
+ [["Content-Length:" Header used for uploading/sending.
+
+ To unset use -1
+ ]]
+ get { }
+ set { }
+ values {
+ length: int64;
+ }
+ }
+
+ @property response_content_length {
+ [["Content-Length:" Header used for downloading/receiving.
+
+ If unset is -1.
+ ]]
+ get { }
+ set @protected { }
+ values {
+ length: int64;
+ }
+ }
+
+ @property response_content_type {
+ [["Content-Type:" Header used for downloading/receiving]]
+ get { }
+ set @protected { }
+ values {
+ content_type: string;
+ }
+ }
+
+ @property response_status {
+ [[The HTTP response status of this request.
+
+ It will be 0 if not connected, otherwise will be what is
+ returned by the server, such as.
+
+ See https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
+
+ This will be usable after "connected" event is dispatched.
+ ]]
+ get { }
+ values {
+ status_code: Efl.Net.Http.Status;
+ }
+ }
+
+ response_headers_get {
+ [[Return an iterator to the key-value pairs for the last response headers.
+
+ Since multiple requests can happen if @.allow_redirects
+ is true, then all headers will be accumulated. This
+ method returns only the headers for the last request.
+
+ To know all the headers, check @.response_headers_all_get.
+
+ This will be usable after "headers,done" event is dispatched.
+ ]]
+ return: free(own(iterator<Efl.Net.Http.Header>), eina_iterator_free) @warn_unused;
+ }
+
+ response_headers_all_get {
+ [[Return an iterator to the key-value pairs for all response headers.
+
+ Since multiple requests can happen if @.allow_redirects
+ is true, then all headers will be accumulated. To know
+ when new request is started, check for headers with keys
+ being NULL, the value will be the "HTTP/VERSION RESPONSE"
+ string received from the host, such as:
+
+ - key=NULL, value="HTTP/1.1 302 Found"
+ - key="Location", value="http://someredirect.com"
+ - key=NULL, value="HTTP/1.1 200 Ok"
+ - key="Content-Type", value="text/html"
+
+ Which mean the original request had a redirect to
+ http://someredirect.com.
+
+ To receive an iterator to just the last request, use
+ @.response_headers_get
+
+ This will be usable after "headers,done" event is dispatched.
+ ]]
+ return: free(own(iterator<Efl.Net.Http.Header>), eina_iterator_free) @warn_unused;
+ }
+
+ response_headers_clear {
+ [[Save some memory by disposing the received headers]]
+ }
+
+ @property progress_download {
+ [[How many bytes were downloaded and how much was expected.]]
+ get { }
+ values {
+ downloaded: uint64 @optional;
+ total: uint64 @optional; [[0 if unknown]]
+ }
+ }
+
+ @property progress_upload {
+ [[How many bytes were uploaded and how much was expected.]]
+ get { }
+ values {
+ uploaded: uint64 @optional;
+ total: uint64 @optional; [[0 if unknown]]
+ }
+ }
+ }
+
+ events {
+ headers,done; [[Notifies all headers were parsed and are available.]]
+ }
+
+ implements {
+ Efl.Object.constructor;
+ Efl.Object.destructor;
+ Efl.Net.Dialer.dial;
+ Efl.Net.Dialer.address_dial;
+ Efl.Net.Dialer.connected;
+ Efl.Net.Dialer.timeout_dial;
+ Efl.Net.Socket.address_local;
+ Efl.Net.Socket.address_remote;
+ Efl.Io.Reader.read;
+ Efl.Io.Reader.can_read.get;
+ Efl.Io.Reader.can_read.set;
+ Efl.Io.Reader.eos.get;
+ Efl.Io.Reader.eos.set;
+ Efl.Io.Writer.write;
+ Efl.Io.Writer.can_write.get;
+ Efl.Io.Writer.can_write.set;
+ Efl.Io.Closer.close;
+ Efl.Io.Closer.closed.get;
+ Efl.Io.Sizer.resize;
+ Efl.Io.Sizer.size.get;
+ }
+}
diff --git a/src/lib/ecore_con/efl_net_http_types.eot b/src/lib/ecore_con/efl_net_http_types.eot
new file mode 100644
index 0000000000..6fb6911dec
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_http_types.eot
@@ -0,0 +1,176 @@
+import eina_types;
+
+enum Efl.Net.Http.Version {
+ v1_0 = 100, [[1.0]]
+ v1_1 = 101, [[1.1]]
+ v2_0 = 200, [[2.0]]
+}
+
+enum Efl.Net.Http.Authentication_Method {
+ none = 0,
+ basic = (1 << 0),
+ digest = (1 << 1),
+ negotiate = (1 << 2),
+ ntlm = (1 << 3),
+ ntlm_winbind = (1 << 4),
+ any_safe = Efl.Net.Http.Authentication_Method.digest | Efl.Net.Http.Authentication_Method.negotiate | Efl.Net.Http.Authentication_Method.ntlm | Efl.Net.Http.Authentication_Method.ntlm_winbind,
+ any = Efl.Net.Http.Authentication_Method.any_safe | Efl.Net.Http.Authentication_Method.basic,
+}
+
+enum Efl.Net.Http.Status {
+ [[Common HTTP status codes]]
+
+ unknown = 0, [[unknown status, likely not connected]]
+
+ /* Informational: 1xx */
+ continue = 100,
+ switching_protocols = 101,
+ checkpoint = 103, /* unofficial extension */
+ processing = 102,
+
+ /* success: 2xx */
+ ok = 200,
+ created = 201,
+ accepted = 202,
+ non_authoritative_information = 203,
+ no_content = 204,
+ reset_content = 205,
+ partial_content = 206,
+ multi_status = 207,
+ already_reported = 208,
+ im_used = 226,
+
+ /* redirection: 3xx */
+ multiple_choices = 300,
+ moved_permanently = 301,
+ found = 302,
+ see_other = 303,
+ not_modified = 304,
+ use_proxy = 305,
+ switch_proxy = 306,
+ temporary_redirect = 307,
+ permanent_redirect = 308,
+
+ /* client error: 4xx */
+ bad_request = 400,
+ unauthorized = 401,
+ payment_required = 402,
+ forbidden = 403,
+ not_found = 404,
+ method_not_allowed = 405,
+ not_acceptable = 406,
+ proxy_authentication_required = 407,
+ request_timeout = 408,
+ conflict = 409,
+ gone = 410,
+ length_required = 411,
+ precondition_failed = 412,
+ payload_too_large = 413,
+ uri_too_long = 414,
+ unsupported_media_type = 415,
+ range_not_satisfiable = 416,
+ expectation_failed = 417,
+ misdirected_request = 421,
+ unprocessable_entity = 422,
+ locked = 423,
+ failed_dependency = 424,
+ upgrade_required = 426,
+ precondition_required = 428,
+ too_many_requests = 429,
+ request_header_fields_too_large = 431,
+ login_timeout = 440, /* unofficial extension */
+ no_response = 444, /* unofficial extension */
+ retry_with = 449, /* unofficial extension */
+ blocked_by_windows_parental_controls = 450, /* unofficial extension */
+ unavailable_for_legal_reasons = 451,
+ ssl_certificate_error = 495, /* unofficial extension */
+ ssl_certificate_required = 496, /* unofficial extension */
+ http_request_sent_to_https_port = 497, /* unofficial extension */
+ request_has_been_forbidden_by_antivirus = 499,
+
+ /* server error: 5xx */
+ internal_server_error = 500,
+ not_implemented = 501,
+ bad_gateway = 502,
+ service_unavailable = 503,
+ gateway_timeout = 504,
+ http_version_not_supported = 505,
+ variant_also_negotiates = 506,
+ insufficient_storage = 507,
+ loop_detected = 508,
+ bandwidth_limit_exceeded = 509, /* unofficial extension */
+ not_extended = 510,
+ network_authentication_required = 511,
+}
+
+struct Efl.Net.Http.Header {
+ [[An HTTP Header.
+
+ Do not assume strings are Eina_Stringshare and they may be
+ NULL. The key and value should not include any trailing
+ whitespace.
+
+ There is a special case for response headers when
+ "allow_redirects" is enabled, in that case
+ efl_net_dialer_http_response_headers_all_get() will return some
+ items with key being NULL, that notifies of a new request as
+ described in the value "HTTP/1.1 200 Ok".
+ ]]
+ key: string; [[for response headers this may be null to indicate a new request response, then the value will be a line such as 'HTTP/1.1 200 Ok']]
+ value: string;
+}
+
+var @extern Efl.Net.Http.Error.BAD_CONTENT_ENCODING: Eina.Error;
+var @extern Efl.Net.Http.Error.BAD_DOWNLOAD_RESUME: Eina.Error;
+var @extern Efl.Net.Http.Error.BAD_FUNCTION_ARGUMENT: Eina.Error;
+var @extern Efl.Net.Http.Error.CHUNK_FAILED: Eina.Error;
+var @extern Efl.Net.Http.Error.CONV_FAILED: Eina.Error;
+var @extern Efl.Net.Http.Error.CONV_REQD: Eina.Error;
+var @extern Efl.Net.Http.Error.COULDNT_CONNECT: Eina.Error;
+var @extern Efl.Net.Http.Error.COULDNT_RESOLVE_HOST: Eina.Error;
+var @extern Efl.Net.Http.Error.COULDNT_RESOLVE_PROXY: Eina.Error;
+var @extern Efl.Net.Http.Error.FAILED_INIT: Eina.Error;
+var @extern Efl.Net.Http.Error.FILE_COULDNT_READ_FILE: Eina.Error;
+var @extern Efl.Net.Http.Error.FILESIZE_EXCEEDED: Eina.Error;
+var @extern Efl.Net.Http.Error.FUNCTION_NOT_FOUND: Eina.Error;
+var @extern Efl.Net.Http.Error.GOT_NOTHING: Eina.Error;
+var @extern Efl.Net.Http.Error.HTTP2: Eina.Error;
+var @extern Efl.Net.Http.Error.HTTP2_STREAM: Eina.Error;
+var @extern Efl.Net.Http.Error.HTTP_POST_ERROR: Eina.Error;
+var @extern Efl.Net.Http.Error.HTTP_RETURNED_ERROR: Eina.Error;
+var @extern Efl.Net.Http.Error.INTERFACE_FAILED: Eina.Error;
+var @extern Efl.Net.Http.Error.LOGIN_DENIED: Eina.Error;
+var @extern Efl.Net.Http.Error.NO_CONNECTION_AVAILABLE: Eina.Error;
+var @extern Efl.Net.Http.Error.NOT_BUILT_IN: Eina.Error;
+var @extern Efl.Net.Http.Error.OPERATION_TIMEDOUT: Eina.Error;
+var @extern Efl.Net.Http.Error.PARTIAL_FILE: Eina.Error;
+var @extern Efl.Net.Http.Error.PEER_FAILED_VERIFICATION: Eina.Error;
+var @extern Efl.Net.Http.Error.RANGE_ERROR: Eina.Error;
+var @extern Efl.Net.Http.Error.READ_ERROR: Eina.Error;
+var @extern Efl.Net.Http.Error.RECV_ERROR: Eina.Error;
+var @extern Efl.Net.Http.Error.REMOTE_ACCESS_DENIED: Eina.Error;
+var @extern Efl.Net.Http.Error.REMOTE_DISK_FULL: Eina.Error;
+var @extern Efl.Net.Http.Error.REMOTE_FILE_EXISTS: Eina.Error;
+var @extern Efl.Net.Http.Error.REMOTE_FILE_NOT_FOUND: Eina.Error;
+var @extern Efl.Net.Http.Error.SEND_ERROR: Eina.Error;
+var @extern Efl.Net.Http.Error.SEND_FAIL_REWIND: Eina.Error;
+var @extern Efl.Net.Http.Error.SSL_CACERT: Eina.Error;
+var @extern Efl.Net.Http.Error.SSL_CACERT_BADFILE: Eina.Error;
+var @extern Efl.Net.Http.Error.SSL_CERTPROBLEM: Eina.Error;
+var @extern Efl.Net.Http.Error.SSL_CIPHER: Eina.Error;
+var @extern Efl.Net.Http.Error.SSL_CONNECT_ERROR: Eina.Error;
+var @extern Efl.Net.Http.Error.SSL_CRL_BADFILE: Eina.Error;
+var @extern Efl.Net.Http.Error.SSL_ENGINE_INITFAILED: Eina.Error;
+var @extern Efl.Net.Http.Error.SSL_ENGINE_NOTFOUND: Eina.Error;
+var @extern Efl.Net.Http.Error.SSL_ENGINE_SETFAILED: Eina.Error;
+var @extern Efl.Net.Http.Error.SSL_INVALIDCERTSTATUS: Eina.Error;
+var @extern Efl.Net.Http.Error.SSL_ISSUER_ERROR: Eina.Error;
+var @extern Efl.Net.Http.Error.SSL_PINNEDPUBKEYNOTMATCH: Eina.Error;
+var @extern Efl.Net.Http.Error.SSL_SHUTDOWN_FAILED: Eina.Error;
+var @extern Efl.Net.Http.Error.TOO_MANY_REDIRECTS: Eina.Error;
+var @extern Efl.Net.Http.Error.UNKNOWN_OPTION: Eina.Error;
+var @extern Efl.Net.Http.Error.UNSUPPORTED_PROTOCOL: Eina.Error;
+var @extern Efl.Net.Http.Error.UPLOAD_FAILED: Eina.Error;
+var @extern Efl.Net.Http.Error.URL_MALFORMAT: Eina.Error;
+var @extern Efl.Net.Http.Error.USE_SSL_FAILED: Eina.Error;
+var @extern Efl.Net.Http.Error.WRITE_ERROR: Eina.Error;