efl_net_dialer_http: fix interaction with CURL on receiving data.

When CURLOPT_WRITEFUNCTION returns less then the requested amount,
CURL will fail, not call us back with the remaining data.

Then in such cases we must pause CURL and read nothing.

When unpausing we need to kick curl with timeout action so FD handlers
will be re-arranged.

Last but not least, sync our buffer limit with CURL, otherwise it may
always fail if we're smaller than CURL.
This commit is contained in:
Gustavo Sverzut Barbieri 2016-09-01 12:18:40 -03:00
parent d12ff74f5f
commit 6ddd01bffb
2 changed files with 45 additions and 26 deletions

View File

@ -253,6 +253,7 @@ typedef enum
CINIT(DEBUGFUNCTION, FUNCTIONPOINT, 94),
CINIT(DEBUGDATA, OBJECTPOINT, 95),
CINIT(COOKIESESSION, LONG, 96),
CINIT(BUFFERSIZE, LONG, 98),
CINIT(PROXYTYPE, LONG, 101),
CINIT(ACCEPT_ENCODING, OBJECTPOINT, 102),
CINIT(PRIVATE, OBJECTPOINT, 103),

View File

@ -942,8 +942,12 @@ _efl_net_dialer_http_receive_data_safe(Eo *o, Efl_Net_Dialer_Http_Data *pd, Eina
return ro_slice.len;
}
if (rw_slice.len == 0)
if (rw_slice.len < ro_slice.len)
{
/* throttle CURL and let users read */
DBG("dialer=%p in=%zd, available %zd (limit=%zd)",
o, ro_slice.len, pd->recv.limit - pd->recv.used, pd->recv.limit);
efl_io_reader_can_read_set(o, EINA_TRUE);
pd->pause |= CURLPAUSE_RECV;
return CURL_WRITEFUNC_PAUSE;
}
@ -955,12 +959,6 @@ _efl_net_dialer_http_receive_data_safe(Eo *o, Efl_Net_Dialer_Http_Data *pd, Eina
// 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;
}
@ -982,7 +980,10 @@ _efl_net_dialer_http_receive_data(const void *buffer, size_t count, size_t nitem
ret = _efl_net_dialer_http_receive_data_safe(o, pd, ro_slice);
_efl_net_dialer_http_curl_safe_end(o, pd, easy);
DBG("dialer=%p in=%zd used=%zd", o, ro_slice.len, ret);
if (ret == CURL_WRITEFUNC_PAUSE)
DBG("dialer=%p in=%zd is now paused", o, ro_slice.len);
else
DBG("dialer=%p in=%zd used=%zd", o, ro_slice.len, ret);
return ret;
}
@ -1136,6 +1137,7 @@ _efl_net_dialer_http_efl_object_constructor(Eo *o, Efl_Net_Dialer_Http_Data *pd)
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_BUFFERSIZE, (long)pd->recv.limit);
curl_easy_setopt(pd->easy, CURLOPT_READFUNCTION, _efl_net_dialer_http_send_data);
curl_easy_setopt(pd->easy, CURLOPT_READDATA, o);
@ -1368,6 +1370,34 @@ _efl_net_dialer_http_efl_net_socket_address_remote_get(Eo *o EINA_UNUSED, Efl_Ne
return pd->address_remote;
}
static Eina_Error
_efl_net_dialer_http_pause_reset(Eo *o, Efl_Net_Dialer_Http_Data *pd)
{
CURLcode re;
CURLMcode rm;
Eina_Error err;
re = curl_easy_pause(pd->easy, pd->pause);
if (re != CURLE_OK)
{
err = _curlcode_to_eina_error(re);
ERR("dialer=%p could not unpause receive (flags=%#x): %s",
o, pd->pause, eina_error_msg_get(err));
return err;
}
rm = curl_multi_socket_action(pd->cm->multi, CURL_SOCKET_TIMEOUT, 0, &pd->cm->running);
if (rm != CURLM_OK)
{
err = _curlmcode_to_eina_error(rm);
ERR("dialer=%p could trigger timeout action: %s",
o, eina_error_msg_get(err));
return err;
}
return 0;
}
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)
{
@ -1395,16 +1425,11 @@ _efl_net_dialer_http_efl_io_reader_read(Eo *o, Efl_Net_Dialer_Http_Data *pd, Ein
if ((pd->pause & CURLPAUSE_RECV) && (pd->recv.used < pd->recv.limit))
{
CURLcode r;
Eina_Error err;
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;
}
err = _efl_net_dialer_http_pause_reset(o, pd);
if (err)
return err;
}
return 0;
@ -1447,7 +1472,6 @@ _efl_net_dialer_http_efl_io_writer_write(Eo *o, Efl_Net_Dialer_Http_Data *pd, Ei
{
Eina_Error err = EINVAL;
CURLMcode rm;
CURLcode re;
EINA_SAFETY_ON_NULL_RETURN_VAL(slice, EINVAL);
EINA_SAFETY_ON_TRUE_GOTO(efl_io_closer_closed_get(o), error);
@ -1457,14 +1481,8 @@ _efl_net_dialer_http_efl_io_writer_write(Eo *o, Efl_Net_Dialer_Http_Data *pd, Ei
pd->send.slice = *slice;
efl_io_writer_can_write_set(o, EINA_FALSE);
pd->pause &= ~CURLPAUSE_SEND;
re = curl_easy_pause(pd->easy, pd->pause);
if (re != CURLE_OK)
{
err = _curlcode_to_eina_error(re);
ERR("dialer=%p could not unpause send (flags=%#x): %s",
o, pd->pause, eina_error_msg_get(err));
goto error;
}
err = _efl_net_dialer_http_pause_reset(o, pd);
if (err) goto error;
pd->error = 0;
rm = curl_multi_socket_action(pd->cm->multi,