LIB_CFILES = \
altsvc.c \
amigaos.c \
+ api.c \
asyn-ares.c \
asyn-base.c \
asyn-thrdd.c \
LIB_HFILES = \
altsvc.h \
amigaos.h \
+ api.h \
arpa_telnet.h \
asyn.h \
bufq.h \
--- /dev/null
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#include "urldata.h"
+#include "api.h"
+#include "multiif.h"
+
+struct Curl_eapi_fn_props {
+ Curl_eapi_fn fn;
+ uint8_t data_is_killed; /* easy handle is killed in call */
+ uint8_t recurse; /* may be called when another call is in progress */
+ uint8_t no_event_cb; /* may not be called during a multi event callback */
+};
+
+static const struct Curl_eapi_fn_props eapi_fn_props[CURL_EAPI_FN_LAST] = {
+ /* function kill rec !ev */
+ { CURL_EAPI_FN_easy_cleanup, 1, 0, 0 },
+ { CURL_EAPI_FN_easy_duphandle, 0, 1, 0 },
+ { CURL_EAPI_FN_easy_getinfo, 0, 1, 0 },
+ { CURL_EAPI_FN_easy_pause, 0, 1, 1 },
+ { CURL_EAPI_FN_easy_perform_ev, 0, 0, 0 },
+ { CURL_EAPI_FN_easy_perform, 0, 0, 0 },
+ { CURL_EAPI_FN_easy_recv, 0, 0, 0 },
+ { CURL_EAPI_FN_easy_reset, 0, 0, 0 },
+ { CURL_EAPI_FN_easy_send, 0, 0, 0 },
+ { CURL_EAPI_FN_easy_setopt, 0, 1, 0 },
+ { CURL_EAPI_FN_easy_ssls_export, 0, 0, 0 },
+ { CURL_EAPI_FN_easy_ssls_import, 0, 0, 0 },
+ { CURL_EAPI_FN_easy_upkeep, 0, 0, 0 },
+ { CURL_EAPI_FN_ws_recv, 0, 1, 0 },
+ { CURL_EAPI_FN_ws_send, 0, 1, 0 },
+ { CURL_EAPI_FN_ws_start_frame, 0, 1, 0 },
+};
+
+struct Curl_mapi_fn_props {
+ Curl_mapi_fn fn;
+ uint8_t multi_is_killed; /* multi handle is killed during call */
+ uint8_t recurse; /* may be called when another call is in progress */
+ uint8_t allow_ntfy_cb; /* may be called during a notify callback */
+};
+
+static const struct Curl_mapi_fn_props mapi_fn_props[CURL_MAPI_FN_LAST] = {
+ /* function kill rec ntfy */
+ { CURL_MAPI_FN_multi_add_handle, 0, 0, 1 },
+ { CURL_MAPI_FN_multi_assign, 0, 1, 1 },
+ { CURL_MAPI_FN_multi_cleanup, 1, 0, 0 },
+ { CURL_MAPI_FN_multi_fdset, 0, 0, 1 },
+ { CURL_MAPI_FN_multi_get_handles, 0, 1, 1 },
+ { CURL_MAPI_FN_multi_get_offt, 0, 1, 1 },
+ { CURL_MAPI_FN_multi_info_read, 0, 1, 1 },
+ { CURL_MAPI_FN_multi_notify_disable, 0, 1, 1 },
+ { CURL_MAPI_FN_multi_notify_enable, 0, 1, 1 },
+ { CURL_MAPI_FN_multi_perform, 0, 0, 0 },
+ { CURL_MAPI_FN_multi_poll, 0, 0, 1 },
+ { CURL_MAPI_FN_multi_remove_handle, 0, 0, 1 },
+ { CURL_MAPI_FN_multi_setopt, 0, 0, 1 },
+ { CURL_MAPI_FN_multi_socket_action, 0, 0, 0 },
+ { CURL_MAPI_FN_multi_socket_all, 0, 0, 0 },
+ { CURL_MAPI_FN_multi_socket, 0, 0, 0 },
+ { CURL_MAPI_FN_multi_timeout, 0, 0, 1 },
+ { CURL_MAPI_FN_multi_wait, 0, 0, 1 },
+ { CURL_MAPI_FN_multi_waitfds, 0, 0, 1 },
+};
+
+struct Curl_cbapi_fn_props {
+ Curl_cbapi_fn fn;
+ uint8_t is_event_cb; /* is a multi event processing callback */
+};
+
+static const struct Curl_cbapi_fn_props
+cbapi_fn_props[CURL_CBAPI_FN_LAST - CURL_CBAPI_FN_START] = {
+ { CURL_CBAPI_FN_easy_chunk_bgn, 0 },
+ { CURL_CBAPI_FN_easy_chunk_end, 0 },
+ { CURL_CBAPI_FN_easy_closesocket, 0 },
+ { CURL_CBAPI_FN_easy_cr_in_read, 0 },
+ { CURL_CBAPI_FN_easy_cr_in_resume_from, 0 },
+ { CURL_CBAPI_FN_easy_cw_out_cb, 0 },
+ { CURL_CBAPI_FN_easy_fdebug, 0 },
+ { CURL_CBAPI_FN_easy_fnmatch_data, 0 },
+ { CURL_CBAPI_FN_easy_fopensocket, 0 },
+ { CURL_CBAPI_FN_easy_fprereq, 0 },
+ { CURL_CBAPI_FN_easy_fprogress, 0 },
+ { CURL_CBAPI_FN_easy_fread_func, 0 },
+ { CURL_CBAPI_FN_easy_fsockopt, 0 },
+ { CURL_CBAPI_FN_easy_fsslctx, 0 },
+ { CURL_CBAPI_FN_easy_fwrite_rtp, 0 },
+ { CURL_CBAPI_FN_easy_fxferinfo, 0 },
+ { CURL_CBAPI_FN_easy_ioctl_func, 0 },
+ { CURL_CBAPI_FN_easy_resolver_start, 0 },
+ { CURL_CBAPI_FN_easy_seek_func, 0 },
+ { CURL_CBAPI_FN_easy_ssh_hostkeyfunc, 0 },
+ { CURL_CBAPI_FN_easy_ssh_keyfunc, 0 },
+ { CURL_CBAPI_FN_easy_trailer_callback, 0 },
+
+ { CURL_CBAPI_FN_multi_ntfy_cb, 0 },
+ { CURL_CBAPI_FN_multi_push_cb, 0 },
+ { CURL_CBAPI_FN_multi_socket_cb, 1 },
+ { CURL_CBAPI_FN_multi_timer_cb, 1 },
+};
+
+static bool eapi_in_event_cb(struct Curl_easy *data)
+{
+ struct Curl_multi *multi = data->multi;
+ if(multi && multi->callstack.count) {
+ size_t i;
+ for(i = 0; i < multi->callstack.count; ++i) {
+ if(multi->callstack.calls[i] >= CURL_CBAPI_FN_START) {
+ uint16_t fn = multi->callstack.calls[i];
+ if((fn < CURL_CBAPI_FN_LAST) &&
+ cbapi_fn_props[fn - CURL_CBAPI_FN_START].is_event_cb)
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+static bool mapi_in_ntfy_cb(struct Curl_multi *multi)
+{
+ if(multi && multi->callstack.count) {
+ size_t i;
+ for(i = 0; i < multi->callstack.count; ++i) {
+ if(multi->callstack.calls[i] == CURL_CBAPI_FN_multi_ntfy_cb)
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+bool Curl_api_multi_is_in_callback(struct Curl_multi *multi)
+{
+ if(multi && multi->callstack.count) {
+ size_t i;
+ for(i = 0; i < multi->callstack.count; ++i) {
+ if(multi->callstack.calls[i] >= CURL_CBAPI_FN_START)
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+bool Curl_api_is_in_callback(struct Curl_easy *data)
+{
+ if(data && data->multi) {
+ return Curl_api_multi_is_in_callback(data->multi);
+ }
+ return FALSE;
+}
+
+bool Curl_eapi_enter(struct Curl_eapi_guard *guard,
+ CURL *curl,
+ Curl_eapi_fn fn,
+ CURLcode *presult)
+{
+ struct Curl_easy *data = curl;
+ const struct Curl_eapi_fn_props *fn_props;
+ CURLcode result = CURLE_OK;
+
+ guard->depth = 0;
+
+ /* Verify that we got an easy handle we can work with. */
+ if(!GOOD_EASY_HANDLE(data)) {
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+ /* verify that is either not added to a multi handle OR has a
+ * GOOD multi handle that knows `data` for `data->mid`. */
+ if(data->mid != UINT32_MAX) {
+ if(GOOD_MULTI_HANDLE(data->multi)) {
+ if(!Curl_multi_knows_easy(data->multi, data)) {
+ /* But multi does not know it, something is fishy, better deny call */
+ DEBUGASSERT(0);
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+ }
+ else {
+ DEBUGASSERT(0); /* data needs to have a GOOD multi handle */
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+ }
+ else if(data->multi) {
+ DEBUGASSERT(0); /* data should not have a multi handle */
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+ /* verify that the call `fn` we're about to enter is known
+ * and check call properties to be admitting. */
+ if(fn >= CURL_EAPI_FN_LAST) {
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+ fn_props = &eapi_fn_props[fn];
+ DEBUGASSERT(fn_props->fn == fn);
+ if(!fn_props->recurse) {
+ if(data->callstack.count) {
+#ifdef CURLVERBOSE
+ DEBUGF(curl_mfprintf(stderr,
+ "EAPI guard: calling %u with call to %u ongoing\n", (uint16_t)fn,
+ data->callstack.calls[data->callstack.count-1]));
+#endif
+ result = CURLE_RECURSIVE_API_CALL;
+ goto out;
+ }
+ if(data->multi && data->multi->callstack.count) {
+#ifdef CURLVERBOSE
+
+ DEBUGF(curl_mfprintf(stderr,
+ "EAPI guard: calling %u with multi call to %u ongoing\n", (uint16_t)fn,
+ data->multi->callstack.calls[data->multi->callstack.count-1]));
+#endif
+ result = CURLE_RECURSIVE_API_CALL;
+ goto out;
+ }
+ }
+
+ if(fn_props->no_event_cb && eapi_in_event_cb(data)) {
+ /* Not allowed to be invoked while an event cb is ongoing */
+#ifdef CURLVERBOSE
+ DEBUGF(curl_mfprintf(stderr,
+ "EAPI guard: calling %u while event callback ongoing\n",
+ (uint16_t)fn));
+#endif
+ result = CURLE_RECURSIVE_API_CALL;
+ goto out;
+ }
+
+ /* all fine, add to data's callstack */
+ if(data->callstack.count >= CURL_EAPI_MAX_RECURSION) {
+ result = CURLE_RECURSIVE_API_CALL;
+ goto out;
+ }
+ data->callstack.calls[data->callstack.count] = (uint16_t)fn;
+ ++data->callstack.count;
+ guard->depth = data->callstack.count;
+ guard->data = fn_props->data_is_killed ? NULL : data;
+
+out:
+ if(presult)
+ *presult = result;
+ return guard->depth > 0;
+}
+
+void Curl_eapi_leave(struct Curl_eapi_guard *guard)
+{
+ if(guard->depth) {
+ /* guard->data is set when handle is supposed to stay alive during call */
+ if(guard->data && GOOD_EASY_HANDLE(guard->data)) {
+ if(guard->depth > guard->data->callstack.count) {
+ DEBUGASSERT(0); /* something very wrong */
+ }
+ else {
+ if(guard->depth < guard->data->callstack.count) {
+ DEBUGASSERT(0); /* someone forgot to clean up */
+ }
+ /* reset to depth the guard was entered in */
+ guard->data->callstack.count = (uint16_t)(guard->depth - 1);
+ }
+ }
+ }
+}
+
+bool Curl_mapi_enter(struct Curl_mapi_guard *guard,
+ CURLM *m,
+ Curl_mapi_fn fn,
+ CURLMcode *pmresult)
+{
+ struct Curl_multi *multi = m;
+ const struct Curl_mapi_fn_props *fn_props;
+ CURLMcode mresult = CURLM_OK;
+
+ guard->depth = 0;
+
+ /* Verify that we got an easy handle we can work with. */
+ if(!GOOD_MULTI_HANDLE(multi)) {
+ mresult = CURLM_BAD_HANDLE;
+ goto out;
+ }
+ if(fn >= CURL_MAPI_FN_LAST) {
+ mresult = CURLM_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+ fn_props = &mapi_fn_props[fn];
+ DEBUGASSERT(fn_props->fn == fn);
+ if(fn_props->allow_ntfy_cb && mapi_in_ntfy_cb(multi)) {
+ /* explicitly allowed, even though normal recursion may not */
+ }
+ else if(!fn_props->recurse && multi->callstack.count) {
+#ifdef CURLVERBOSE
+ DEBUGF(curl_mfprintf(stderr,
+ "MAPI guard: calling %u with call to %u ongoing\n", (uint16_t)fn,
+ multi->callstack.calls[multi->callstack.count-1]));
+#endif
+ mresult = CURLM_RECURSIVE_API_CALL;
+ goto out;
+ }
+
+ /* all fine, add to data's callstack */
+ if(multi->callstack.count >= CURL_MAPI_MAX_RECURSION) {
+ mresult = CURLM_RECURSIVE_API_CALL;
+ goto out;
+ }
+ multi->callstack.calls[multi->callstack.count] = (uint16_t)fn;
+ ++multi->callstack.count;
+ guard->depth = multi->callstack.count;
+ guard->multi = fn_props->multi_is_killed ? NULL : multi;
+
+out:
+ if(pmresult)
+ *pmresult = mresult;
+ return guard->depth > 0;
+}
+
+void Curl_mapi_leave(struct Curl_mapi_guard *guard)
+{
+ if(guard->depth) {
+ /* guard->data is set when handle is supposed to stay alive during call */
+ if(guard->multi && GOOD_MULTI_HANDLE(guard->multi)) {
+ if(guard->depth > guard->multi->callstack.count) {
+ DEBUGASSERT(0); /* something very wrong */
+ }
+ else {
+ if(guard->depth < guard->multi->callstack.count) {
+ DEBUGASSERT(0); /* someone forgot to clean up */
+ }
+ /* reset to depth the guard was entered in */
+ guard->multi->callstack.count = (uint16_t)(guard->depth - 1);
+ }
+ }
+ }
+}
+
+void Curl_cbapi_enter(struct Curl_mapi_guard *guard,
+ struct Curl_easy *data,
+ struct Curl_multi *multi,
+ Curl_cbapi_fn fn)
+{
+ guard->depth = 0;
+
+ if(!multi)
+ multi = data ? data->multi : NULL;
+ /* if not multi is involved here, just leave */
+ if(!multi)
+ return;
+ /* invalid callback specifier? */
+ if((fn >= CURL_CBAPI_FN_LAST) || (fn < CURL_CBAPI_FN_START)) {
+ DEBUGASSERT(0);
+ return;
+ }
+ DEBUGASSERT(cbapi_fn_props[fn - CURL_CBAPI_FN_START].fn == fn);
+ if(multi->callstack.count) {
+ size_t i;
+ for(i = multi->callstack.count; i; --i) {
+ if(multi->callstack.calls[i - 1] == fn) {
+ /* recursive invocation of the same callback */
+ DEBUGASSERT(0);
+ return;
+ }
+ }
+ }
+
+ /* all fine, add to data's callstack */
+ /* if multi callstack already at max depth, leave */
+ if(multi->callstack.count >= CURL_MAPI_MAX_RECURSION)
+ return;
+ multi->callstack.calls[multi->callstack.count] = (uint16_t)fn;
+ ++multi->callstack.count;
+ guard->depth = multi->callstack.count;
+ guard->multi = multi;
+}
+
+void Curl_cbapi_leave(struct Curl_mapi_guard *guard)
+{
+ Curl_mapi_leave(guard);
+}
--- /dev/null
+#ifndef HEADER_CURL_API_H
+#define HEADER_CURL_API_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#define CURLEASY_MAGIC_NUMBER 0xc0dedbadU
+#ifdef DEBUGBUILD
+/* On a debug build, we want to fail hard on easy handles that
+ * are not NULL, but no longer have the MAGIC touch. This gives
+ * us early warning on things only discovered by valgrind otherwise. */
+#define GOOD_EASY_HANDLE(x) \
+ (((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) ? TRUE : \
+ (DEBUGASSERT(!(x)), FALSE))
+#else
+#define GOOD_EASY_HANDLE(x) \
+ ((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER))
+#endif
+
+#define CURLMULTI_MAGIC_NUMBER 0x000bab1e
+
+#ifdef DEBUGBUILD
+/* On a debug build, we want to fail hard on multi handles that
+ * are not NULL, but no longer have the MAGIC touch. This gives
+ * us early warning on things only discovered by valgrind otherwise. */
+#define GOOD_MULTI_HANDLE(x) \
+ (((x) && (x)->magic == CURLMULTI_MAGIC_NUMBER) ? TRUE : \
+ (DEBUGASSERT(!(x)), FALSE))
+#else
+#define GOOD_MULTI_HANDLE(x) \
+ ((x) && (x)->magic == CURLMULTI_MAGIC_NUMBER)
+#endif
+
+/* the API functions called on a CURL* */
+typedef enum {
+ CURL_EAPI_FN_easy_cleanup,
+ CURL_EAPI_FN_easy_duphandle,
+ CURL_EAPI_FN_easy_getinfo,
+ CURL_EAPI_FN_easy_pause,
+ CURL_EAPI_FN_easy_perform_ev,
+ CURL_EAPI_FN_easy_perform,
+ CURL_EAPI_FN_easy_recv,
+ CURL_EAPI_FN_easy_reset,
+ CURL_EAPI_FN_easy_send,
+ CURL_EAPI_FN_easy_setopt,
+ CURL_EAPI_FN_easy_ssls_export,
+ CURL_EAPI_FN_easy_ssls_import,
+ CURL_EAPI_FN_easy_upkeep,
+ CURL_EAPI_FN_ws_recv,
+ CURL_EAPI_FN_ws_send,
+ CURL_EAPI_FN_ws_start_frame,
+ CURL_EAPI_FN_LAST
+} Curl_eapi_fn;
+
+/* the API functions called on a CURLM* */
+typedef enum {
+ CURL_MAPI_FN_multi_add_handle,
+ CURL_MAPI_FN_multi_assign,
+ CURL_MAPI_FN_multi_cleanup,
+ CURL_MAPI_FN_multi_fdset,
+ CURL_MAPI_FN_multi_get_handles,
+ CURL_MAPI_FN_multi_get_offt,
+ CURL_MAPI_FN_multi_info_read,
+ CURL_MAPI_FN_multi_notify_disable,
+ CURL_MAPI_FN_multi_notify_enable,
+ CURL_MAPI_FN_multi_perform,
+ CURL_MAPI_FN_multi_poll,
+ CURL_MAPI_FN_multi_remove_handle,
+ CURL_MAPI_FN_multi_setopt,
+ CURL_MAPI_FN_multi_socket_action,
+ CURL_MAPI_FN_multi_socket_all,
+ CURL_MAPI_FN_multi_socket,
+ CURL_MAPI_FN_multi_timeout,
+ CURL_MAPI_FN_multi_wait,
+ CURL_MAPI_FN_multi_waitfds,
+ CURL_MAPI_FN_LAST
+} Curl_mapi_fn;
+
+#define CURL_CBAPI_FN_START (16 * 1024)
+
+/* the callback functions */
+typedef enum {
+ CURL_CBAPI_FN_easy_chunk_bgn = CURL_CBAPI_FN_START,
+ CURL_CBAPI_FN_easy_chunk_end,
+ CURL_CBAPI_FN_easy_closesocket,
+ CURL_CBAPI_FN_easy_cr_in_read,
+ CURL_CBAPI_FN_easy_cr_in_resume_from,
+ CURL_CBAPI_FN_easy_cw_out_cb,
+ CURL_CBAPI_FN_easy_fdebug,
+ CURL_CBAPI_FN_easy_fnmatch_data,
+ CURL_CBAPI_FN_easy_fopensocket,
+ CURL_CBAPI_FN_easy_fprereq,
+ CURL_CBAPI_FN_easy_fprogress,
+ CURL_CBAPI_FN_easy_fread_func,
+ CURL_CBAPI_FN_easy_fsockopt,
+ CURL_CBAPI_FN_easy_fsslctx,
+ CURL_CBAPI_FN_easy_fwrite_rtp,
+ CURL_CBAPI_FN_easy_fxferinfo,
+ CURL_CBAPI_FN_easy_ioctl_func,
+ CURL_CBAPI_FN_easy_resolver_start,
+ CURL_CBAPI_FN_easy_seek_func,
+ CURL_CBAPI_FN_easy_ssh_hostkeyfunc,
+ CURL_CBAPI_FN_easy_ssh_keyfunc,
+ CURL_CBAPI_FN_easy_trailer_callback,
+
+ CURL_CBAPI_FN_multi_ntfy_cb,
+ CURL_CBAPI_FN_multi_push_cb,
+ CURL_CBAPI_FN_multi_socket_cb,
+ CURL_CBAPI_FN_multi_timer_cb,
+
+ CURL_CBAPI_FN_LAST
+} Curl_cbapi_fn;
+
+
+#define CURL_EAPI_MAX_RECURSION 7
+
+struct Curl_eapi_stack {
+ uint16_t count;
+ uint16_t calls[CURL_EAPI_MAX_RECURSION];
+};
+
+struct Curl_eapi_guard {
+ struct Curl_easy *data; /* != NULL if handle stays */
+ uint16_t depth; /* > 0 if this guard was entered */
+};
+
+bool Curl_eapi_enter(struct Curl_eapi_guard *guard,
+ CURL *curl,
+ Curl_eapi_fn fn,
+ CURLcode *presult);
+void Curl_eapi_leave(struct Curl_eapi_guard *guard);
+
+/* Curl_eapi_enter() checks for curl being NULL, but windows compiler
+ * analyzers do not realize this. *sigh* */
+#define CURL_EAPI_ENTER(g, curl, fn, r) \
+ Curl_eapi_enter((g), (curl), CURL_EAPI_FN_##fn, (r)) && (curl)
+#define CURL_EAPI_LEAVE(g) \
+ Curl_eapi_leave(g)
+
+
+#define CURL_MAPI_MAX_RECURSION 15
+
+struct Curl_mapi_stack {
+ uint16_t count;
+ uint16_t calls[CURL_MAPI_MAX_RECURSION];
+};
+
+struct Curl_mapi_guard {
+ struct Curl_multi *multi; /* != NULL if handle stays */
+ uint16_t depth; /* > 0 if this guard was entered */
+};
+
+bool Curl_mapi_enter(struct Curl_mapi_guard *guard,
+ CURLM *m,
+ Curl_mapi_fn fn,
+ CURLMcode *pmresult);
+void Curl_mapi_leave(struct Curl_mapi_guard *guard);
+
+/* Curl_mapi_enter() checks for m being NULL, but windows compiler
+ * analyzers do not realize this. *sigh* */
+#define CURL_MAPI_ENTER(g, m, fn, r) \
+ Curl_mapi_enter((g), (m), CURL_MAPI_FN_##fn, (r)) && (m)
+#define CURL_MAPI_LEAVE(g) \
+ Curl_mapi_leave(g)
+
+
+void Curl_cbapi_enter(struct Curl_mapi_guard *guard,
+ struct Curl_easy *data,
+ struct Curl_multi *multi,
+ Curl_cbapi_fn fn);
+void Curl_cbapi_leave(struct Curl_mapi_guard *guard);
+
+#define CURL_CBAPI_START(g, d, fn) \
+ Curl_cbapi_enter((g), (d), NULL, CURL_CBAPI_FN_##fn)
+#define CURL_CBAPI_MULTI_START(g, m, fn) \
+ Curl_cbapi_enter((g), NULL, (m), CURL_CBAPI_FN_##fn)
+#define CURL_CBAPI_END(g) \
+ Curl_cbapi_leave(g)
+#define CURL_CBAPI_MULTI_END(g) \
+ Curl_cbapi_leave(g)
+
+
+bool Curl_api_is_in_callback(struct Curl_easy *data);
+bool Curl_api_multi_is_in_callback(struct Curl_multi *multi);
+
+#endif /* HEADER_CURL_API_H */
* might have been changed and this 'new' address will actually be used
* here to connect.
*/
- Curl_set_in_callback(data, TRUE);
+ struct Curl_mapi_guard guard;
+ CURL_CBAPI_START(&guard, data, easy_fopensocket);
*sockfd = data->set.fopensocket(data->set.opensocket_client,
CURLSOCKTYPE_IPCXN,
(struct curl_sockaddr *)addr);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
}
else {
/* opensocket callback not set, so create the socket now */
return 0;
if(use_callback && conn && conn->fclosesocket) {
+ struct Curl_mapi_guard guard;
int rc;
Curl_multi_will_close(data, sock);
- Curl_set_in_callback(data, TRUE);
+ CURL_CBAPI_START(&guard, data, easy_closesocket);
rc = conn->fclosesocket(conn->closesocket_client, sock);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
return rc;
}
if(data->set.fsockopt) {
/* activate callback for setting socket options */
- Curl_set_in_callback(data, TRUE);
+ struct Curl_mapi_guard guard;
+ CURL_CBAPI_START(&guard, data, easy_fsockopt);
error = data->set.fsockopt(data->set.sockopt_client,
ctx->sock,
CURLSOCKTYPE_IPCXN);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
isconnected = TRUE;
ctx->sock, ctx->ip.remote_ip, ctx->ip.remote_port);
if(data->set.fsockopt) {
+ struct Curl_mapi_guard guard;
int error = 0;
/* activate callback for setting socket options */
- Curl_set_in_callback(data, TRUE);
+ CURL_CBAPI_START(&guard, data, easy_fsockopt);
error = data->set.fsockopt(data->set.sockopt_client,
ctx->sock, CURLSOCKTYPE_ACCEPT);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
if(error)
return CURLE_ABORTED_BY_CALLBACK;
{
if(data->set.verbose) {
if(data->set.fdebug) {
- uint8_t inCallback = Curl_is_in_callback(data);
- Curl_set_in_callback(data, TRUE);
+ struct Curl_mapi_guard guard;
+ CURL_CBAPI_START(&guard, data, easy_fdebug);
(void)(*data->set.fdebug)(data, type, CURL_UNCONST(ptr), size,
data->set.debugdata);
- Curl_set_in_callback(data, inCallback);
+ CURL_CBAPI_END(&guard);
}
else {
static const char s_infotype[CURLINFO_END][3] = {
char buf[TRC_LINE_MAX];
size_t len;
if(data->set.fdebug) {
- uint8_t inCallback = Curl_is_in_callback(data);
-
+ struct Curl_mapi_guard guard;
if(CURL_TRC_IDS(data) && (size < TRC_LINE_MAX)) {
len = trc_print_ids(data, buf, TRC_LINE_MAX);
len += curl_msnprintf(buf + len, TRC_LINE_MAX - len, "%.*s",
(int)size, ptr);
len = trc_end_buf(buf, len, TRC_LINE_MAX, FALSE);
- Curl_set_in_callback(data, TRUE);
+ CURL_CBAPI_START(&guard, data, easy_fdebug);
(void)(*data->set.fdebug)(data, type, buf, len, data->set.debugdata);
- Curl_set_in_callback(data, inCallback);
+ CURL_CBAPI_END(&guard);
}
else {
- Curl_set_in_callback(data, TRUE);
+ CURL_CBAPI_START(&guard, data, easy_fdebug);
(void)(*data->set.fdebug)(data, type, CURL_UNCONST(ptr),
size, data->set.debugdata);
- Curl_set_in_callback(data, inCallback);
+ CURL_CBAPI_END(&guard);
}
}
else {
DEBUGASSERT(data->conn);
*pnwritten = 0;
- Curl_set_in_callback(data, TRUE);
- nwritten = wcb((char *)CURL_UNCONST(buf), 1, blen, wcb_data);
- Curl_set_in_callback(data, FALSE);
+ {
+ struct Curl_mapi_guard guard;
+ CURL_CBAPI_START(&guard, data, easy_cw_out_cb);
+ nwritten = wcb((char *)CURL_UNCONST(buf), 1, blen, wcb_data);
+ CURL_CBAPI_END(&guard);
+ }
CURL_TRC_WRITE(data, "[OUT] wrote %zu %s bytes -> %zu",
blen, (otype == CW_OUT_HDS) ? "header" : "body",
nwritten);
private_data via CURLOPT_PRIVATE if they so choose. */
DEBUGASSERT(!doh->set.private_data);
- if(curl_multi_add_handle(multi, doh))
+ if(Curl_multi_add_handle(multi, doh))
goto error;
*pmid = doh->mid;
continue;
}
/* data->multi might already be reset at this time */
- curl_multi_remove_handle(data->multi, probe_data);
+ Curl_multi_remove_handle(data->multi, probe_data);
Curl_close(&probe_data);
}
data->sub_xfer_done = NULL;
#endif
#include "urldata.h"
+#include "api.h"
#include "transfer.h"
#include "vtls/vtls.h"
#include "vtls/vtls_scache.h"
return CURLE_OUT_OF_MEMORY;
}
- if(multi->in_callback)
+ if(Curl_api_multi_is_in_callback(multi))
return CURLE_RECURSIVE_API_CALL;
/* Copy relevant easy options to the multi handle */
curl_multi_setopt(multi, CURLMOPT_QUICK_EXIT, (long)data->set.quick_exit);
data->multi_easy = NULL; /* pretend it does not exist */
- mresult = curl_multi_add_handle(multi, data);
+ mresult = Curl_multi_add_handle(multi, data);
if(mresult) {
curl_multi_cleanup(multi);
if(mresult == CURLM_OUT_OF_MEMORY)
/* ignoring the return code is not nice, but atm we cannot really handle
a failure here, room for future improvement! */
- (void)curl_multi_remove_handle(multi, data);
+ (void)Curl_multi_remove_handle(multi, data);
sigpipe_restore(&sigpipe_ctx);
*/
CURLcode curl_easy_perform(CURL *curl)
{
- return easy_perform(curl, FALSE);
+ struct Curl_eapi_guard guard;
+ CURLcode result;
+
+ if(CURL_EAPI_ENTER(&guard, curl, easy_perform, &result)) {
+ result = easy_perform(curl, FALSE);
+ }
+ CURL_EAPI_LEAVE(&guard);
+ return result;
}
#ifdef DEBUGBUILD
*/
CURLcode curl_easy_perform_ev(struct Curl_easy *easy)
{
- return easy_perform(easy, TRUE);
+ struct Curl_eapi_guard guard;
+ CURLcode result;
+
+ if(CURL_EAPI_ENTER(&guard, easy, easy_perform_ev, &result)) {
+ result = easy_perform(easy, TRUE);
+ }
+ CURL_EAPI_LEAVE(&guard);
+ return result;
}
#endif
*/
void curl_easy_cleanup(CURL *curl)
{
- struct Curl_easy *data = curl;
- if(GOOD_EASY_HANDLE(data)) {
+ struct Curl_eapi_guard guard;
+
+ if(CURL_EAPI_ENTER(&guard, curl, easy_cleanup, NULL)) {
+ struct Curl_easy *data = curl;
struct Curl_sigpipe_ctx sigpipe_ctx;
sigpipe_ignore(data, &sigpipe_ctx);
Curl_close(&data);
sigpipe_restore(&sigpipe_ctx);
}
+ CURL_EAPI_LEAVE(&guard);
}
/*
#undef curl_easy_getinfo
CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...)
{
- struct Curl_easy *data = curl;
- va_list arg;
- void *paramp;
+ struct Curl_eapi_guard guard;
CURLcode result;
- if(!GOOD_EASY_HANDLE(data))
- return CURLE_BAD_FUNCTION_ARGUMENT;
+ if(CURL_EAPI_ENTER(&guard, curl, easy_getinfo, &result)) {
+ struct Curl_easy *data = curl;
+ va_list arg;
+ void *paramp;
- va_start(arg, info);
- paramp = va_arg(arg, void *);
+ va_start(arg, info);
+ paramp = va_arg(arg, void *);
- result = Curl_getinfo(data, info, paramp);
+ result = Curl_getinfo(data, info, paramp);
- va_end(arg);
+ va_end(arg);
+ }
+ CURL_EAPI_LEAVE(&guard);
return result;
}
*/
CURL *curl_easy_duphandle(CURL *curl)
{
- struct Curl_easy *data = curl;
+ struct Curl_eapi_guard guard;
struct Curl_easy *outcurl = NULL;
- if(!GOOD_EASY_HANDLE(data))
- goto fail;
- outcurl = curlx_calloc(1, sizeof(struct Curl_easy));
- if(!outcurl)
- goto fail;
+ if(CURL_EAPI_ENTER(&guard, curl, easy_duphandle, NULL)) {
+ struct Curl_easy *data = curl;
+
+ outcurl = curlx_calloc(1, sizeof(struct Curl_easy));
+ if(!outcurl)
+ goto fail;
- /*
- * We setup a few buffers we need. We should probably make them
- * get setup on-demand in the code, as that would probably decrease
- * the likeliness of us forgetting to init a buffer here in the future.
- */
- outcurl->set.buffer_size = data->set.buffer_size;
-
- Curl_hash_init(&outcurl->meta_hash, 23,
- Curl_hash_str, curlx_str_key_compare, dupeasy_meta_freeentry);
- curlx_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER);
- Curl_bufref_init(&outcurl->state.url);
- Curl_bufref_init(&outcurl->state.referer);
- Curl_netrc_init(&outcurl->state.netrc);
-
- /* the connection pool is setup on demand */
- outcurl->state.lastconnect_id = -1;
- outcurl->state.recent_conn_id = -1;
- outcurl->id = -1;
- outcurl->mid = UINT32_MAX;
- outcurl->master_mid = UINT32_MAX;
+ /*
+ * We setup a few buffers we need. We should probably make them
+ * get setup on-demand in the code, as that would probably decrease
+ * the likeliness of us forgetting to init a buffer here in the future.
+ */
+ outcurl->set.buffer_size = data->set.buffer_size;
+
+ Curl_hash_init(&outcurl->meta_hash, 23,
+ Curl_hash_str, curlx_str_key_compare,
+ dupeasy_meta_freeentry);
+ curlx_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER);
+ Curl_bufref_init(&outcurl->state.url);
+ Curl_bufref_init(&outcurl->state.referer);
+ Curl_netrc_init(&outcurl->state.netrc);
+
+ /* the connection pool is setup on demand */
+ outcurl->state.lastconnect_id = -1;
+ outcurl->state.recent_conn_id = -1;
+ outcurl->id = -1;
+ outcurl->mid = UINT32_MAX;
+ outcurl->master_mid = UINT32_MAX;
#ifndef CURL_DISABLE_HTTP
- Curl_llist_init(&outcurl->state.httphdrs, NULL);
+ Curl_llist_init(&outcurl->state.httphdrs, NULL);
#endif
- Curl_initinfo(outcurl);
+ Curl_initinfo(outcurl);
- /* copy all userdefined values */
- if(dupset(outcurl, data))
- goto fail;
+ /* copy all userdefined values */
+ if(dupset(outcurl, data))
+ goto fail;
- outcurl->progress.hide = data->progress.hide;
- outcurl->progress.callback = data->progress.callback;
+ outcurl->progress.hide = data->progress.hide;
+ outcurl->progress.callback = data->progress.callback;
#ifndef CURL_DISABLE_COOKIES
- outcurl->state.cookielist = NULL;
- if(data->cookies && data->state.cookie_engine) {
- /* If cookies are enabled in the parent handle, we enable them
- in the clone as well! */
- outcurl->cookies = Curl_cookie_init();
- if(!outcurl->cookies)
- goto fail;
- outcurl->state.cookie_engine = TRUE;
- }
+ outcurl->state.cookielist = NULL;
+ if(data->cookies && data->state.cookie_engine) {
+ /* If cookies are enabled in the parent handle, we enable them
+ in the clone as well! */
+ outcurl->cookies = Curl_cookie_init();
+ if(!outcurl->cookies)
+ goto fail;
+ outcurl->state.cookie_engine = TRUE;
+ }
- if(data->state.cookielist) {
- outcurl->state.cookielist = Curl_slist_duplicate(data->state.cookielist);
- if(!outcurl->state.cookielist)
- goto fail;
- }
+ if(data->state.cookielist) {
+ outcurl->state.cookielist = Curl_slist_duplicate(data->state.cookielist);
+ if(!outcurl->state.cookielist)
+ goto fail;
+ }
#endif
- if(Curl_bufref_ptr(&data->state.url)) {
- Curl_bufref_set(&outcurl->state.url,
- Curl_bufref_dup(&data->state.url), 0,
- curl_free);
- if(!Curl_bufref_ptr(&outcurl->state.url))
- goto fail;
- }
- if(Curl_bufref_ptr(&data->state.referer)) {
- Curl_bufref_set(&outcurl->state.referer,
- Curl_bufref_dup(&data->state.referer), 0,
- curl_free);
- if(!Curl_bufref_ptr(&outcurl->state.referer))
- goto fail;
- }
+ if(Curl_bufref_ptr(&data->state.url)) {
+ Curl_bufref_set(&outcurl->state.url,
+ Curl_bufref_dup(&data->state.url), 0,
+ curl_free);
+ if(!Curl_bufref_ptr(&outcurl->state.url))
+ goto fail;
+ }
+ if(Curl_bufref_ptr(&data->state.referer)) {
+ Curl_bufref_set(&outcurl->state.referer,
+ Curl_bufref_dup(&data->state.referer), 0,
+ curl_free);
+ if(!Curl_bufref_ptr(&outcurl->state.referer))
+ goto fail;
+ }
- /* Reinitialize an SSL engine for the new handle
- * note: the engine name has already been copied by dupset */
- if(outcurl->set.str[STRING_SSL_ENGINE]) {
- if(Curl_ssl_set_engine(outcurl, outcurl->set.str[STRING_SSL_ENGINE]))
- goto fail;
- }
+ /* Reinitialize an SSL engine for the new handle
+ * note: the engine name has already been copied by dupset */
+ if(outcurl->set.str[STRING_SSL_ENGINE]) {
+ if(Curl_ssl_set_engine(outcurl, outcurl->set.str[STRING_SSL_ENGINE]))
+ goto fail;
+ }
#ifndef CURL_DISABLE_ALTSVC
- if(data->asi) {
- outcurl->asi = Curl_altsvc_init();
- if(!outcurl->asi)
- goto fail;
- if(outcurl->set.str[STRING_ALTSVC])
- (void)Curl_altsvc_load(outcurl->asi, outcurl->set.str[STRING_ALTSVC]);
- }
+ if(data->asi) {
+ outcurl->asi = Curl_altsvc_init();
+ if(!outcurl->asi)
+ goto fail;
+ if(outcurl->set.str[STRING_ALTSVC])
+ (void)Curl_altsvc_load(outcurl->asi, outcurl->set.str[STRING_ALTSVC]);
+ }
#endif
#ifndef CURL_DISABLE_HSTS
- if(data->hsts) {
- outcurl->hsts = Curl_hsts_init();
- if(!outcurl->hsts)
- goto fail;
- if(outcurl->set.str[STRING_HSTS])
- (void)Curl_hsts_loadfile(outcurl,
- outcurl->hsts, outcurl->set.str[STRING_HSTS]);
- (void)Curl_hsts_loadcb(outcurl, outcurl->hsts);
-
- /* Copy entries learned at runtime. (E.g. Strict-Transport-Security
- headers.) */
- if(Curl_hsts_copy(outcurl->hsts, data->hsts))
- goto fail;
- }
+ if(data->hsts) {
+ outcurl->hsts = Curl_hsts_init();
+ if(!outcurl->hsts)
+ goto fail;
+ if(outcurl->set.str[STRING_HSTS])
+ (void)Curl_hsts_loadfile(outcurl,
+ outcurl->hsts, outcurl->set.str[STRING_HSTS]);
+ (void)Curl_hsts_loadcb(outcurl, outcurl->hsts);
+
+ /* Copy entries learned at runtime. (E.g. Strict-Transport-Security
+ headers.) */
+ if(Curl_hsts_copy(outcurl->hsts, data->hsts))
+ goto fail;
+ }
#endif
- outcurl->magic = CURLEASY_MAGIC_NUMBER;
-
- /* we reach this point and thus we are OK */
-
+ /* we reach this point and thus we are OK */
+ outcurl->magic = CURLEASY_MAGIC_NUMBER;
+ }
+ CURL_EAPI_LEAVE(&guard);
return outcurl;
fail:
-
if(outcurl) {
#ifndef CURL_DISABLE_COOKIES
curlx_free(outcurl->cookies);
curlx_free(outcurl);
}
+ CURL_EAPI_LEAVE(&guard);
return NULL;
}
*/
void curl_easy_reset(CURL *curl)
{
- struct Curl_easy *data = curl;
- if(!GOOD_EASY_HANDLE(data))
- return;
+ struct Curl_eapi_guard guard;
- Curl_req_hard_reset(&data->req, data);
- Curl_hash_clean(&data->meta_hash);
+ if(CURL_EAPI_ENTER(&guard, curl, easy_reset, NULL)) {
+ struct Curl_easy *data = curl;
- /* clear all meta data */
- Curl_meta_reset(data);
- /* zero out UserDefined data: */
- Curl_freeset(data);
- memset(&data->set, 0, sizeof(struct UserDefined));
- Curl_init_userdefined(data);
+ Curl_req_hard_reset(&data->req, data);
+ Curl_hash_clean(&data->meta_hash);
- /* zero out Progress data: */
- memset(&data->progress, 0, sizeof(struct Progress));
+ /* clear all meta data */
+ Curl_meta_reset(data);
+ /* zero out UserDefined data: */
+ Curl_freeset(data);
+ memset(&data->set, 0, sizeof(struct UserDefined));
+ Curl_init_userdefined(data);
- /* zero out PureInfo data: */
- Curl_initinfo(data);
+ /* zero out Progress data: */
+ memset(&data->progress, 0, sizeof(struct Progress));
- data->progress.hide = TRUE;
- data->state.current_speed = -1; /* init to negative == impossible */
- data->state.recent_conn_id = -1; /* clear remembered connection id */
+ /* zero out PureInfo data: */
+ Curl_initinfo(data);
- /* zero out authentication data: */
- memset(&data->state.authhost, 0, sizeof(struct auth));
- memset(&data->state.authproxy, 0, sizeof(struct auth));
+ data->progress.hide = TRUE;
+ data->state.current_speed = -1; /* init to negative == impossible */
+ data->state.recent_conn_id = -1; /* clear remembered connection id */
+
+ /* zero out authentication data: */
+ memset(&data->state.authhost, 0, sizeof(struct auth));
+ memset(&data->state.authproxy, 0, sizeof(struct auth));
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH)
- Curl_http_auth_cleanup_digest(data);
+ Curl_http_auth_cleanup_digest(data);
#endif
- data->master_mid = UINT32_MAX;
+ data->master_mid = UINT32_MAX;
+ }
+ CURL_EAPI_LEAVE(&guard);
}
/*
*/
CURLcode curl_easy_pause(CURL *curl, int action)
{
+ struct Curl_eapi_guard guard;
CURLcode result = CURLE_OK;
- bool changed = FALSE;
- struct Curl_easy *data = curl;
- bool recv_paused, recv_paused_new;
- bool send_paused, send_paused_new;
- uint8_t in_c;
-
- if(!GOOD_EASY_HANDLE(data) || !data->conn)
- /* crazy input, do not continue */
- return CURLE_BAD_FUNCTION_ARGUMENT;
-
- in_c = Curl_is_in_callback(data);
- if(in_c == IN_CALLBACK_FORBID_EASY_PAUSE)
- return CURLE_RECURSIVE_API_CALL;
- recv_paused = Curl_xfer_recv_is_paused(data);
- recv_paused_new = (action & CURLPAUSE_RECV);
- send_paused = Curl_xfer_send_is_paused(data);
- send_paused_new = (action & CURLPAUSE_SEND);
+ if(CURL_EAPI_ENTER(&guard, curl, easy_pause, &result)) {
+ bool changed = FALSE;
+ struct Curl_easy *data = curl;
+ bool recv_paused, recv_paused_new;
+ bool send_paused, send_paused_new;
- if((send_paused != send_paused_new) ||
- (send_paused_new != Curl_creader_is_paused(data))) {
- changed = TRUE;
- result = Curl_1st_fatal(
- result, Curl_xfer_pause_send(data, send_paused_new));
- }
+ if(!data->conn) {
+ /* crazy input, do not continue */
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
- if(recv_paused != recv_paused_new) {
- changed = TRUE;
- result = Curl_1st_fatal(
- result, Curl_xfer_pause_recv(data, recv_paused_new));
- }
+ recv_paused = Curl_xfer_recv_is_paused(data);
+ recv_paused_new = (action & CURLPAUSE_RECV);
+ send_paused = Curl_xfer_send_is_paused(data);
+ send_paused_new = (action & CURLPAUSE_SEND);
- /* If not completely pausing both directions now, run again in any case. */
- if(!Curl_xfer_is_blocked(data)) {
- /* reset the too-slow time keeper */
- data->state.keeps_speed.tv_sec = 0;
- if(data->multi) {
- Curl_multi_mark_dirty(data); /* make it run */
- /* On changes, tell application to update its timers. */
- if(changed) {
- if(Curl_update_timer(data->multi) && !result)
- result = CURLE_ABORTED_BY_CALLBACK;
- }
+ if((send_paused != send_paused_new) ||
+ (send_paused_new != Curl_creader_is_paused(data))) {
+ changed = TRUE;
+ result = Curl_1st_fatal(
+ result, Curl_xfer_pause_send(data, send_paused_new));
}
- }
- if(!result && changed && !data->state.done && data->multi)
- /* pause/unpausing may result in multi event changes */
- if(Curl_multi_ev_assess_xfer(data->multi, data) && !result)
- result = CURLE_ABORTED_BY_CALLBACK;
+ if(recv_paused != recv_paused_new) {
+ changed = TRUE;
+ result = Curl_1st_fatal(
+ result, Curl_xfer_pause_recv(data, recv_paused_new));
+ }
- if(in_c)
- /* this might have called a callback recursively which might have set this
- to false again on exit */
- Curl_set_in_callback(data, in_c);
+ /* If not completely pausing both directions, run again in any case. */
+ if(!Curl_xfer_is_blocked(data)) {
+ /* reset the too-slow time keeper */
+ data->state.keeps_speed.tv_sec = 0;
+ if(data->multi) {
+ Curl_multi_mark_dirty(data); /* make it run */
+ /* On changes, tell application to update its timers. */
+ if(changed) {
+ if(Curl_update_timer(data->multi) && !result)
+ result = CURLE_ABORTED_BY_CALLBACK;
+ }
+ }
+ }
+ if(!result && changed && !data->state.done && data->multi)
+ /* pause/unpausing may result in multi event changes */
+ if(Curl_multi_ev_assess_xfer(data->multi, data) && !result)
+ result = CURLE_ABORTED_BY_CALLBACK;
+ }
+out:
+ CURL_EAPI_LEAVE(&guard);
return result;
}
* curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
* Returns CURLE_OK on success, error code on error.
*/
-CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n)
+CURLcode Curl_easy_recv(struct Curl_easy *data,
+ void *buffer, size_t buflen, size_t *n)
{
CURLcode result;
struct connectdata *c;
- struct Curl_easy *data = curl;
-
- if(!GOOD_EASY_HANDLE(data))
- return CURLE_BAD_FUNCTION_ARGUMENT;
- if(Curl_is_in_callback(data))
- return CURLE_RECURSIVE_API_CALL;
result = easy_connection(data, &c);
if(result)
return Curl_conn_recv(data, FIRSTSOCKET, buffer, buflen, n);
}
+CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n)
+{
+ struct Curl_eapi_guard guard;
+ CURLcode result;
+
+ if(CURL_EAPI_ENTER(&guard, curl, easy_recv, &result)) {
+ result = Curl_easy_recv(curl, buffer, buflen, n);
+ }
+ CURL_EAPI_LEAVE(&guard);
+ return result;
+}
+
#ifndef CURL_DISABLE_WEBSOCKETS
CURLcode Curl_connect_only_attach(struct Curl_easy *data)
{
CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen,
size_t *n)
{
- size_t written = 0;
+ struct Curl_eapi_guard guard;
CURLcode result;
- struct Curl_easy *data = curl;
- if(!GOOD_EASY_HANDLE(data))
- return CURLE_BAD_FUNCTION_ARGUMENT;
- if(Curl_is_in_callback(data))
- return CURLE_RECURSIVE_API_CALL;
- result = Curl_senddata(data, buffer, buflen, &written);
- *n = written;
+ if(CURL_EAPI_ENTER(&guard, curl, easy_send, &result)) {
+ struct Curl_easy *data = curl;
+ size_t written = 0;
+
+ result = Curl_senddata(data, buffer, buflen, &written);
+ *n = written;
+ }
+ CURL_EAPI_LEAVE(&guard);
return result;
}
*/
CURLcode curl_easy_upkeep(CURL *curl)
{
- struct Curl_easy *data = curl;
- /* Verify that we got an easy handle we can work with. */
- if(!GOOD_EASY_HANDLE(data))
- return CURLE_BAD_FUNCTION_ARGUMENT;
-
- if(Curl_is_in_callback(data))
- return CURLE_RECURSIVE_API_CALL;
+ struct Curl_eapi_guard guard;
+ CURLcode result;
- /* Use the common function to keep connections alive. */
- return Curl_cpool_upkeep(data);
+ if(CURL_EAPI_ENTER(&guard, curl, easy_upkeep, &result)) {
+ /* Use the common function to keep connections alive. */
+ result = Curl_cpool_upkeep((struct Curl_easy *)curl);
+ }
+ CURL_EAPI_LEAVE(&guard);
+ return result;
}
CURLcode curl_easy_ssls_import(CURL *curl, const char *session_key,
const unsigned char *sdata, size_t sdata_len)
{
#if defined(USE_SSL) && defined(USE_SSLS_EXPORT)
- struct Curl_easy *data = curl;
- if(!GOOD_EASY_HANDLE(data))
- return CURLE_BAD_FUNCTION_ARGUMENT;
- if(Curl_is_in_callback(data) || Curl_ssl_scache_is_locked(data))
- return CURLE_RECURSIVE_API_CALL;
- return Curl_ssl_session_import(data, session_key,
- shmac, shmac_len, sdata, sdata_len);
+ struct Curl_eapi_guard guard;
+ CURLcode result;
+
+ if(CURL_EAPI_ENTER(&guard, curl, easy_ssls_import, &result)) {
+ result = Curl_ssl_session_import((struct Curl_easy *)curl, session_key,
+ shmac, shmac_len, sdata, sdata_len);
+ }
+ CURL_EAPI_LEAVE(&guard);
+ return result;
#else
(void)curl;
(void)session_key;
void *userptr)
{
#if defined(USE_SSL) && defined(USE_SSLS_EXPORT)
- struct Curl_easy *data = curl;
- if(!GOOD_EASY_HANDLE(data))
- return CURLE_BAD_FUNCTION_ARGUMENT;
- if(Curl_is_in_callback(data) || Curl_ssl_scache_is_locked(data))
- return CURLE_RECURSIVE_API_CALL;
- return Curl_ssl_session_export(data, export_fn, userptr);
+ struct Curl_eapi_guard guard;
+ CURLcode result;
+
+ if(CURL_EAPI_ENTER(&guard, curl, easy_ssls_export, &result)) {
+ result = Curl_ssl_session_export((struct Curl_easy *)curl,
+ export_fn, userptr);
+ }
+ CURL_EAPI_LEAVE(&guard);
+ return result;
#else
(void)curl;
(void)export_fn;
/* Let's read off the proper amount of bytes from the input. */
if(data->set.seek_func) {
- Curl_set_in_callback(data, TRUE);
+ struct Curl_mapi_guard guard;
+ CURL_CBAPI_START(&guard, data, easy_seek_func);
seekerr = data->set.seek_func(data->set.seek_client,
data->state.resume_from, SEEK_SET);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
}
if(seekerr != CURL_SEEKFUNC_OK) {
{
if(data->state.wildcardmatch) {
if(data->set.chunk_end && ftpc->file) {
- Curl_set_in_callback(data, TRUE);
+ struct Curl_mapi_guard guard;
+ CURL_CBAPI_START(&guard, data, easy_chunk_end);
data->set.chunk_end(data->set.wildcardptr);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
freedirs(ftpc);
}
ftpc->known_filesize = -1;
infof(data, "Wildcard - START of \"%s\"", finfo->filename);
if(data->set.chunk_bgn) {
long userresponse;
- Curl_set_in_callback(data, TRUE);
+ struct Curl_mapi_guard guard;
+ CURL_CBAPI_START(&guard, data, easy_chunk_bgn);
userresponse = data->set.chunk_bgn(
finfo, data->set.wildcardptr,
(int)Curl_llist_count(&wildcard->filelist));
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
switch(userresponse) {
case CURL_CHUNK_BGN_FUNC_SKIP:
infof(data, "Wildcard - \"%s\" skipped by user", finfo->filename);
case CURLWC_SKIP: {
if(data->set.chunk_end) {
- Curl_set_in_callback(data, TRUE);
+ struct Curl_mapi_guard guard;
+ CURL_CBAPI_START(&guard, data, easy_chunk_end);
data->set.chunk_end(data->set.wildcardptr);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
}
Curl_node_remove(Curl_llist_head(&wildcard->filelist));
wildcard->state = (Curl_llist_count(&wildcard->filelist) == 0) ?
compare = Curl_fnmatch;
/* filter pattern-corresponding filenames */
- Curl_set_in_callback(data, TRUE);
- if(compare(data->set.fnmatch_data, wc->pattern, finfo->filename) == 0) {
- /* discard symlink which is containing multiple " -> " */
- if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target &&
- (strstr(finfo->strings.target, " -> "))) {
+ {
+ struct Curl_mapi_guard guard;
+ CURL_CBAPI_START(&guard, data, easy_fnmatch_data);
+ if(compare(data->set.fnmatch_data, wc->pattern, finfo->filename) == 0) {
+ /* discard symlink which is containing multiple " -> " */
+ if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target &&
+ (strstr(finfo->strings.target, " -> "))) {
+ add = FALSE;
+ }
+ }
+ else {
add = FALSE;
}
+ CURL_CBAPI_END(&guard);
}
- else {
- add = FALSE;
- }
- Curl_set_in_callback(data, FALSE);
if(add) {
Curl_llist_append(llist, finfo, &infop->list);
void *resolver)
{
if(data->set.resolver_start) {
+ struct Curl_mapi_guard guard;
int rc;
CURL_TRC_DNS(data, "announcing resolve to application");
- Curl_set_in_callback(data, TRUE);
+ CURL_CBAPI_START(&guard, data, easy_resolver_start);
rc = data->set.resolver_start(resolver, NULL,
data->set.resolver_start_client);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
if(rc) {
CURL_TRC_DNS(data, "application aborted resolve");
return CURLE_ABORTED_BY_CALLBACK;
goto fail;
}
- Curl_set_in_callback(data, TRUE);
- rv = data->multi->push_cb(data, newhandle,
- stream->push_headers_used, &heads,
- data->multi->push_userp);
- Curl_set_in_callback(data, FALSE);
+ {
+ struct Curl_mapi_guard guard;
+ CURL_CBAPI_START(&guard, data, multi_push_cb);
+ rv = data->multi->push_cb(data, newhandle,
+ stream->push_headers_used, &heads,
+ data->multi->push_userp);
+ CURL_CBAPI_END(&guard);
+ }
/* free the headers again */
free_push_headers(stream);
if(result)
goto out;
- Curl_set_in_callback(data, TRUE);
- rc = data->set.trailer_callback(&trailers, data->set.trailer_data);
- Curl_set_in_callback(data, FALSE);
+ {
+ struct Curl_mapi_guard guard;
+ CURL_CBAPI_START(&guard, data, easy_trailer_callback);
+ rc = data->set.trailer_callback(&trailers, data->set.trailer_data);
+ CURL_CBAPI_END(&guard);
+ }
if(rc != CURL_TRAILERFUNC_OK) {
failf(data, "operation aborted by trailing headers callback");
#define CURL_TLS_SESSION_SIZE 25
#endif
-#define CURL_MULTI_HANDLE 0x000bab1e
-
-#ifdef DEBUGBUILD
-/* On a debug build, we want to fail hard on multi handles that
- * are not NULL, but no longer have the MAGIC touch. This gives
- * us early warning on things only discovered by valgrind otherwise. */
-#define GOOD_MULTI_HANDLE(x) \
- (((x) && (x)->magic == CURL_MULTI_HANDLE) ? TRUE : \
- (DEBUGASSERT(!(x)), FALSE))
-#else
-#define GOOD_MULTI_HANDLE(x) \
- ((x) && (x)->magic == CURL_MULTI_HANDLE)
-#endif
-
static void move_pending_to_connect(struct Curl_multi *multi,
struct Curl_easy *data);
static CURLMcode add_next_timeout(const struct curltime *pnow,
if(!multi)
return NULL;
- multi->magic = CURL_MULTI_HANDLE;
+ multi->magic = CURLMULTI_MAGIC_NUMBER;
Curl_dnscache_init(&multi->dnscache, dnssize);
Curl_mntfy_init(multi);
return CURLM_OK;
}
-CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl)
+CURLMcode Curl_multi_add_handle(struct Curl_multi *multi,
+ struct Curl_easy *data)
{
CURLMcode mresult;
- struct Curl_multi *multi = m;
- struct Curl_easy *data = curl;
-
- /* First, make some basic checks that the CURLM handle is a good handle */
- if(!GOOD_MULTI_HANDLE(multi))
- return CURLM_BAD_HANDLE;
-
- /* Verify that we got a somewhat good easy handle too */
- if(!GOOD_EASY_HANDLE(data))
- return CURLM_BAD_EASY_HANDLE;
/* Prevent users from adding same easy handle more than once and prevent
adding to more than one multi stack */
if(data->multi)
return CURLM_ADDED_ALREADY;
- if(multi->in_callback)
- return CURLM_RECURSIVE_API_CALL;
-
if(multi->dead) {
/* a "dead" handle cannot get added transfers while any existing easy
handles are still alive - but if there are none alive anymore, it is
Curl_llist_init(&data->state.timeoutlist, NULL);
/*
- * No failure allowed in this function beyond this point. No modification of
- * easy nor multi handle allowed before this except for potential multi's
- * connection pool growing which will not be undone in this function no
- * matter what.
+ * No failure allowed in this function beyond this point. No modification
+ * of easy nor multi handle allowed before this except for potential
+ * multi's connection pool growing which will not be undone in this
+ * function no matter what.
*/
if(data->set.errorbuffer)
data->set.errorbuffer[0] = 0;
data->state.os_errno = 0;
- /* make the Curl_easy refer back to this multi handle - before Curl_expire()
- is called. */
+ /* make the Curl_easy refer back to this multi handle - before
+ Curl_expire() is called. */
data->multi = multi;
/* set the easy handle */
return CURLM_OK;
}
+CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl)
+{
+ struct Curl_mapi_guard guard;
+ CURLMcode mresult;
+
+ if(CURL_MAPI_ENTER(&guard, m, multi_add_handle, &mresult)) {
+ struct Curl_easy *data = curl;
+ /* Verify that we got a somewhat good easy handle too */
+ if(!GOOD_EASY_HANDLE(data))
+ mresult = CURLM_BAD_EASY_HANDLE;
+ else
+ mresult = Curl_multi_add_handle(m, data);
+ }
+ CURL_MAPI_LEAVE(&guard);
+ return mresult;
+}
+
#if 0
/* Debug-function, used like this:
*
connclose(conn, "Removing connect-only easy handle");
}
-CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl)
+CURLMcode Curl_multi_remove_handle(struct Curl_multi *multi,
+ struct Curl_easy *data)
{
- struct Curl_multi *multi = m;
- struct Curl_easy *data = curl;
+ CURLMcode mresult;
bool premature;
struct Curl_llist_node *e;
- CURLMcode mresult;
uint32_t mid;
- /* First, make some basic checks that the CURLM handle is a good handle */
- if(!GOOD_MULTI_HANDLE(multi))
- return CURLM_BAD_HANDLE;
-
- /* Verify that we got a somewhat good easy handle too */
- if(!GOOD_EASY_HANDLE(data))
- return CURLM_BAD_EASY_HANDLE;
-
/* Prevent users from trying to remove same easy handle more than once */
if(!data->multi)
return CURLM_OK; /* it is already removed so let's say it is fine! */
return CURLM_INTERNAL_ERROR;
}
- if(multi->in_callback)
- return CURLM_RECURSIVE_API_CALL;
-
premature = (data->mstate < MSTATE_COMPLETED);
/* If the 'state' is not INIT or COMPLETED, we might need to do something
return CURLM_OK;
}
+CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl)
+{
+ struct Curl_mapi_guard guard;
+ CURLMcode mresult;
+
+ if(CURL_MAPI_ENTER(&guard, m, multi_remove_handle, &mresult)) {
+ struct Curl_easy *data = curl;
+ if(!GOOD_EASY_HANDLE(data))
+ mresult = CURLM_BAD_EASY_HANDLE;
+ else
+ mresult = Curl_multi_remove_handle(m, data);
+ }
+ CURL_MAPI_LEAVE(&guard);
+ return mresult;
+}
+
/* Return TRUE if the application asked for multiplexing */
bool Curl_multiplex_wanted(const struct Curl_multi *multi)
{
fd_set *read_fd_set, fd_set *write_fd_set,
fd_set *exc_fd_set, int *max_fd)
{
- /* Scan through all the easy handles to get the file descriptors set.
- Some easy handles may not have connected to the remote host yet,
- and then we must make sure that is done. */
- int this_max_fd = -1;
- struct Curl_multi *multi = m;
- struct easy_pollset ps;
- unsigned int i;
- uint32_t mid;
- (void)exc_fd_set;
-
- if(!GOOD_MULTI_HANDLE(multi))
- return CURLM_BAD_HANDLE;
-
- if(multi->in_callback)
- return CURLM_RECURSIVE_API_CALL;
+ struct Curl_mapi_guard guard;
+ CURLMcode mresult;
- Curl_pollset_init(&ps);
- if(Curl_uint32_bset_first(&multi->process, &mid)) {
- do {
- struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
+ if(CURL_MAPI_ENTER(&guard, m, multi_fdset, &mresult)) {
+ /* Scan through all the easy handles to get the file descriptors set.
+ Some easy handles may not have connected to the remote host yet,
+ and then we must make sure that is done. */
+ struct Curl_multi *multi = m;
+ struct easy_pollset ps;
+ int this_max_fd = -1;
+ unsigned int i;
+ uint32_t mid;
+ (void)exc_fd_set;
- if(!data) {
- DEBUGASSERT(0);
- continue;
- }
+ Curl_pollset_init(&ps);
+ if(Curl_uint32_bset_first(&multi->process, &mid)) {
+ do {
+ struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
- Curl_multi_pollset(data, &ps);
- for(i = 0; i < ps.n; i++) {
- if(!FDSET_SOCK(ps.sockets[i]))
- /* pretend it does not exist */
+ if(!data) {
+ DEBUGASSERT(0);
continue;
- if(ps.actions[i] & CURL_POLL_IN)
- FD_SET(ps.sockets[i], read_fd_set);
- if(ps.actions[i] & CURL_POLL_OUT)
- FD_SET(ps.sockets[i], write_fd_set);
- if((int)ps.sockets[i] > this_max_fd)
- this_max_fd = (int)ps.sockets[i];
- }
- } while(Curl_uint32_bset_next(&multi->process, mid, &mid));
- }
+ }
- Curl_cshutdn_setfds(&multi->cshutdn, multi->admin,
- read_fd_set, write_fd_set, &this_max_fd);
+ Curl_multi_pollset(data, &ps);
+ for(i = 0; i < ps.n; i++) {
+ if(!FDSET_SOCK(ps.sockets[i]))
+ /* pretend it does not exist */
+ continue;
+ if(ps.actions[i] & CURL_POLL_IN)
+ FD_SET(ps.sockets[i], read_fd_set);
+ if(ps.actions[i] & CURL_POLL_OUT)
+ FD_SET(ps.sockets[i], write_fd_set);
+ if((int)ps.sockets[i] > this_max_fd)
+ this_max_fd = (int)ps.sockets[i];
+ }
+ } while(Curl_uint32_bset_next(&multi->process, mid, &mid));
+ }
- *max_fd = this_max_fd;
- Curl_pollset_cleanup(&ps);
+ Curl_cshutdn_setfds(&multi->cshutdn, multi->admin,
+ read_fd_set, write_fd_set, &this_max_fd);
- return CURLM_OK;
+ *max_fd = this_max_fd;
+ Curl_pollset_cleanup(&ps);
+
+ mresult = CURLM_OK;
+ }
+ CURL_MAPI_LEAVE(&guard);
+ return mresult;
}
CURLMcode curl_multi_waitfds(CURLM *m,
unsigned int size,
unsigned int *fd_count)
{
- struct Curl_waitfds cwfds;
- CURLMcode mresult = CURLM_OK;
- struct Curl_multi *multi = m;
- struct easy_pollset ps;
- unsigned int need = 0;
- uint32_t mid;
-
- if(!ufds && (size || !fd_count))
- return CURLM_BAD_FUNCTION_ARGUMENT;
+ struct Curl_mapi_guard guard;
+ CURLMcode mresult;
- if(!GOOD_MULTI_HANDLE(multi))
- return CURLM_BAD_HANDLE;
+ if(CURL_MAPI_ENTER(&guard, m, multi_waitfds, &mresult)) {
+ struct Curl_waitfds cwfds;
+ struct Curl_multi *multi = m;
+ struct easy_pollset ps;
+ unsigned int need = 0;
+ uint32_t mid;
- if(multi->in_callback)
- return CURLM_RECURSIVE_API_CALL;
+ if(!ufds && (size || !fd_count)) {
+ mresult = CURLM_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
- Curl_pollset_init(&ps);
- Curl_waitfds_init(&cwfds, ufds, size);
- if(Curl_uint32_bset_first(&multi->process, &mid)) {
- do {
- struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
- if(!data) {
- DEBUGASSERT(0);
- Curl_uint32_bset_remove(&multi->process, mid);
- Curl_uint32_bset_remove(&multi->dirty, mid);
- continue;
- }
- Curl_multi_pollset(data, &ps);
- need += Curl_waitfds_add_ps(&cwfds, &ps);
- } while(Curl_uint32_bset_next(&multi->process, mid, &mid));
- }
+ Curl_pollset_init(&ps);
+ Curl_waitfds_init(&cwfds, ufds, size);
+ mresult = CURLM_OK;
+ if(Curl_uint32_bset_first(&multi->process, &mid)) {
+ do {
+ struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
+ if(!data) {
+ DEBUGASSERT(0);
+ Curl_uint32_bset_remove(&multi->process, mid);
+ Curl_uint32_bset_remove(&multi->dirty, mid);
+ continue;
+ }
+ Curl_multi_pollset(data, &ps);
+ need += Curl_waitfds_add_ps(&cwfds, &ps);
+ } while(Curl_uint32_bset_next(&multi->process, mid, &mid));
+ }
- need += Curl_cshutdn_add_waitfds(&multi->cshutdn, multi->admin, &cwfds);
+ need += Curl_cshutdn_add_waitfds(&multi->cshutdn, multi->admin, &cwfds);
- if(need != cwfds.n && ufds)
- mresult = CURLM_OUT_OF_MEMORY;
+ if(need != cwfds.n && ufds)
+ mresult = CURLM_OUT_OF_MEMORY;
- if(fd_count)
- *fd_count = need;
- Curl_pollset_cleanup(&ps);
+ if(fd_count)
+ *fd_count = need;
+ Curl_pollset_cleanup(&ps);
+ }
+out:
+ CURL_MAPI_LEAVE(&guard);
return mresult;
}
int wakeup_idx = -1;
#endif
- if(!GOOD_MULTI_HANDLE(multi))
- return CURLM_BAD_HANDLE;
-
- if(multi->in_callback)
- return CURLM_RECURSIVE_API_CALL;
-
if(timeout_ms < 0)
return CURLM_BAD_FUNCTION_ARGUMENT;
int timeout_ms,
int *ret)
{
- return multi_wait(m, extra_fds, extra_nfds, timeout_ms, ret, FALSE);
+ struct Curl_mapi_guard guard;
+ CURLMcode mresult;
+
+ if(CURL_MAPI_ENTER(&guard, m, multi_wait, &mresult)) {
+ mresult = multi_wait(m, extra_fds, extra_nfds, timeout_ms, ret, FALSE);
+ }
+ CURL_MAPI_LEAVE(&guard);
+ return mresult;
}
CURLMcode curl_multi_poll(CURLM *m,
int timeout_ms,
int *ret)
{
- return multi_wait(m, extra_fds, extra_nfds, timeout_ms, ret, TRUE);
+ struct Curl_mapi_guard guard;
+ CURLMcode mresult;
+
+ if(CURL_MAPI_ENTER(&guard, m, multi_poll, &mresult)) {
+ mresult = multi_wait(m, extra_fds, extra_nfds, timeout_ms, ret, TRUE);
+ }
+ CURL_MAPI_LEAVE(&guard);
+ return mresult;
}
CURLMcode curl_multi_wakeup(CURLM *m)
{
CURLMcode mresult;
- if(multi->in_callback)
- return CURLM_RECURSIVE_API_CALL;
-
- mresult = curl_multi_add_handle(multi, data);
+ mresult = Curl_multi_add_handle(multi, data);
if(!mresult) {
CURLcode result;
connection, only this transfer */
result = Curl_init_do(data, NULL);
if(result) {
- curl_multi_remove_handle(multi, data);
+ Curl_multi_remove_handle(multi, data);
return CURLM_INTERNAL_ERROR;
}
return CURLE_OK;
}
-static void set_in_callback(struct Curl_multi *multi, bool value)
-{
- multi->in_callback = value;
-}
-
/*
* posttransfer() is called immediately after a transfer ends
*/
CURLMcode mresult = CURLM_OK;
CURLcode result = CURLE_OK;
if(data->set.fprereq) {
+ struct Curl_mapi_guard guard;
int prereq_rc;
/* call the prerequest callback function */
- Curl_set_in_callback(data, TRUE);
+ CURL_CBAPI_START(&guard, data, easy_fprereq);
prereq_rc = data->set.fprereq(data->set.prereq_userp,
data->info.primary.remote_ip,
data->info.primary.local_ip,
data->info.primary.remote_port,
data->info.primary.local_port);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
if(prereq_rc != CURL_PREREQFUNC_OK) {
failf(data, "operation aborted by pre-request callback");
/* failure in pre-request callback - do not do any other processing */
CURLMcode mresult;
CURLcode result = CURLE_OK;
- if(!GOOD_EASY_HANDLE(data))
- return CURLM_BAD_EASY_HANDLE;
-
if(multi->dead) {
/* a multi-level callback returned error before, meaning every individual
transfer now has failed */
uint32_t mid;
struct Curl_sigpipe_ctx sigpipe_ctx;
- if(multi->in_callback)
- return CURLM_RECURSIVE_API_CALL;
-
- if(multi->in_ntfy_callback)
- return CURLM_RECURSIVE_API_CALL;
-
sigpipe_init(&sigpipe_ctx);
if(Curl_uint32_bset_first(&multi->process, &mid)) {
CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
{
- struct Curl_multi *multi = m;
-
- if(!GOOD_MULTI_HANDLE(multi))
- return CURLM_BAD_HANDLE;
+ struct Curl_mapi_guard guard;
+ CURLMcode mresult;
- return multi_perform(multi, running_handles);
+ if(CURL_MAPI_ENTER(&guard, m, multi_perform, &mresult)) {
+ mresult = multi_perform(m, running_handles);
+ }
+ CURL_MAPI_LEAVE(&guard);
+ return mresult;
}
CURLMcode curl_multi_cleanup(CURLM *m)
{
- struct Curl_multi *multi = m;
- if(GOOD_MULTI_HANDLE(multi)) {
+ struct Curl_mapi_guard guard;
+ CURLMcode mresult;
+
+ if(CURL_MAPI_ENTER(&guard, m, multi_cleanup, &mresult)) {
+ struct Curl_multi *multi = m;
void *entry;
uint32_t mid;
- if(multi->in_callback)
- return CURLM_RECURSIVE_API_CALL;
- if(multi->in_ntfy_callback)
- return CURLM_RECURSIVE_API_CALL;
/* First remove all remaining easy handles,
* close internal ones. admin handle is special */
if(Curl_uint32_tbl_first(&multi->xfers, &mid, &entry)) {
do {
struct Curl_easy *data = entry;
- if(!GOOD_EASY_HANDLE(data))
- return CURLM_BAD_HANDLE;
+ if(!GOOD_EASY_HANDLE(data)) {
+ mresult = CURLM_BAD_HANDLE;
+ goto out;
+ }
#ifdef DEBUGBUILD
if(mid != data->mid) {
Curl_uint32_tbl_destroy(&multi->xfers);
curlx_free(multi);
- return CURLM_OK;
+ mresult = CURLM_OK;
}
- return CURLM_BAD_HANDLE;
+out:
+ CURL_MAPI_LEAVE(&guard);
+ return mresult;
}
/*
CURLMsg *curl_multi_info_read(CURLM *m, int *msgs_in_queue)
{
- struct Curl_message *msg;
- struct Curl_multi *multi = m;
+ struct Curl_mapi_guard guard;
+ CURLMsg *msg_result = NULL;
*msgs_in_queue = 0; /* default to none */
+ if(CURL_MAPI_ENTER(&guard, m, multi_info_read, NULL)) {
+ struct Curl_multi *multi = m;
+ if(Curl_llist_count(&multi->msglist)) {
+ /* there is one or more messages in the list */
+ struct Curl_llist_node *e;
+ struct Curl_message *msg;
- if(GOOD_MULTI_HANDLE(multi) &&
- !multi->in_callback &&
- Curl_llist_count(&multi->msglist)) {
- /* there is one or more messages in the list */
- struct Curl_llist_node *e;
-
- /* extract the head of the list to return */
- e = Curl_llist_head(&multi->msglist);
+ /* extract the head of the list to return */
+ e = Curl_llist_head(&multi->msglist);
- msg = Curl_node_elem(e);
+ msg = Curl_node_elem(e);
- /* remove the extracted entry */
- Curl_node_remove(e);
+ /* remove the extracted entry */
+ Curl_node_remove(e);
- *msgs_in_queue = curlx_uztosi(Curl_llist_count(&multi->msglist));
+ *msgs_in_queue = curlx_uztosi(Curl_llist_count(&multi->msglist));
- return &msg->extmsg;
+ msg_result = &msg->extmsg;
+ }
}
- return NULL;
+ CURL_MAPI_LEAVE(&guard);
+ return msg_result;
}
void Curl_multi_will_close(struct Curl_easy *data, curl_socket_t s)
#undef curl_multi_setopt
CURLMcode curl_multi_setopt(CURLM *m, CURLMoption option, ...)
{
+ struct Curl_mapi_guard guard;
CURLMcode mresult = CURLM_OK;
- va_list param;
- unsigned long uarg;
- struct Curl_multi *multi = m;
- if(!GOOD_MULTI_HANDLE(multi))
- return CURLM_BAD_HANDLE;
+ if(CURL_MAPI_ENTER(&guard, m, multi_setopt, &mresult)) {
+ struct Curl_multi *multi = m;
+ va_list param;
+ unsigned long uarg;
- if(multi->in_callback)
- return CURLM_RECURSIVE_API_CALL;
+ va_start(param, option);
- va_start(param, option);
-
- switch(option) {
- case CURLMOPT_SOCKETFUNCTION:
- multi->socket_cb = va_arg(param, curl_socket_callback);
- break;
- case CURLMOPT_SOCKETDATA:
- multi->socket_userp = va_arg(param, void *);
- break;
- case CURLMOPT_PUSHFUNCTION:
- multi->push_cb = va_arg(param, curl_push_callback);
- break;
- case CURLMOPT_PUSHDATA:
- multi->push_userp = va_arg(param, void *);
- break;
- case CURLMOPT_PIPELINING:
- multi->multiplexing = va_arg(param, long) & CURLPIPE_MULTIPLEX ? 1 : 0;
- break;
- case CURLMOPT_TIMERFUNCTION:
- multi->timer_cb = va_arg(param, curl_multi_timer_callback);
- break;
- case CURLMOPT_TIMERDATA:
- multi->timer_userp = va_arg(param, void *);
- break;
- case CURLMOPT_MAXCONNECTS:
- uarg = va_arg(param, unsigned long);
- if(uarg <= UINT_MAX)
- multi->maxconnects = (unsigned int)uarg;
- break;
- case CURLMOPT_MAX_HOST_CONNECTIONS:
- if(!curlx_sltouz(va_arg(param, long), &multi->max_host_connections))
- mresult = CURLM_BAD_FUNCTION_ARGUMENT;
- break;
- case CURLMOPT_MAX_TOTAL_CONNECTIONS:
- if(!curlx_sltouz(va_arg(param, long), &multi->max_total_connections))
- mresult = CURLM_BAD_FUNCTION_ARGUMENT;
- break;
- /* options formerly used for pipelining */
- case CURLMOPT_MAX_PIPELINE_LENGTH:
- break;
- case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE:
- break;
- case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE:
- break;
- case CURLMOPT_PIPELINING_SITE_BL:
- break;
- case CURLMOPT_PIPELINING_SERVER_BL:
- break;
- case CURLMOPT_MAX_CONCURRENT_STREAMS: {
- long streams = va_arg(param, long);
- if((streams < 1) || (streams > INT_MAX))
- streams = 100;
- multi->max_concurrent_streams = (unsigned int)streams;
- break;
- }
- case CURLMOPT_NETWORK_CHANGED: {
- long val = va_arg(param, long);
- if(val & CURLMNWC_CLEAR_ALL)
- /* In the beginning, all values available to set were 1 by mistake. We
- converted this to mean "all", thus setting all the bits
- automatically */
- val = CURLMNWC_CLEAR_DNS | CURLMNWC_CLEAR_CONNS;
- if(val & CURLMNWC_CLEAR_DNS) {
- Curl_dnscache_clear(multi->admin);
+ switch(option) {
+ case CURLMOPT_SOCKETFUNCTION:
+ multi->socket_cb = va_arg(param, curl_socket_callback);
+ break;
+ case CURLMOPT_SOCKETDATA:
+ multi->socket_userp = va_arg(param, void *);
+ break;
+ case CURLMOPT_PUSHFUNCTION:
+ multi->push_cb = va_arg(param, curl_push_callback);
+ break;
+ case CURLMOPT_PUSHDATA:
+ multi->push_userp = va_arg(param, void *);
+ break;
+ case CURLMOPT_PIPELINING:
+ multi->multiplexing = va_arg(param, long) & CURLPIPE_MULTIPLEX ? 1 : 0;
+ break;
+ case CURLMOPT_TIMERFUNCTION:
+ multi->timer_cb = va_arg(param, curl_multi_timer_callback);
+ break;
+ case CURLMOPT_TIMERDATA:
+ multi->timer_userp = va_arg(param, void *);
+ break;
+ case CURLMOPT_MAXCONNECTS:
+ uarg = va_arg(param, unsigned long);
+ if(uarg <= UINT_MAX)
+ multi->maxconnects = (unsigned int)uarg;
+ break;
+ case CURLMOPT_MAX_HOST_CONNECTIONS:
+ if(!curlx_sltouz(va_arg(param, long), &multi->max_host_connections))
+ mresult = CURLM_BAD_FUNCTION_ARGUMENT;
+ break;
+ case CURLMOPT_MAX_TOTAL_CONNECTIONS:
+ if(!curlx_sltouz(va_arg(param, long), &multi->max_total_connections))
+ mresult = CURLM_BAD_FUNCTION_ARGUMENT;
+ break;
+ /* options formerly used for pipelining */
+ case CURLMOPT_MAX_PIPELINE_LENGTH:
+ break;
+ case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE:
+ break;
+ case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE:
+ break;
+ case CURLMOPT_PIPELINING_SITE_BL:
+ break;
+ case CURLMOPT_PIPELINING_SERVER_BL:
+ break;
+ case CURLMOPT_MAX_CONCURRENT_STREAMS: {
+ long streams = va_arg(param, long);
+ if((streams < 1) || (streams > INT_MAX))
+ streams = 100;
+ multi->max_concurrent_streams = (unsigned int)streams;
+ break;
}
- if(val & CURLMNWC_CLEAR_CONNS) {
- Curl_cpool_nw_changed(multi->admin);
+ case CURLMOPT_NETWORK_CHANGED: {
+ long val = va_arg(param, long);
+ if(val & CURLMNWC_CLEAR_ALL)
+ /* In the beginning, all values available to set were 1 by mistake. We
+ converted this to mean "all", thus setting all the bits
+ automatically */
+ val = CURLMNWC_CLEAR_DNS | CURLMNWC_CLEAR_CONNS;
+ if(val & CURLMNWC_CLEAR_DNS) {
+ Curl_dnscache_clear(multi->admin);
+ }
+ if(val & CURLMNWC_CLEAR_CONNS) {
+ Curl_cpool_nw_changed(multi->admin);
+ }
+ break;
}
- break;
- }
- case CURLMOPT_NOTIFYFUNCTION:
- multi->ntfy.ntfy_cb = va_arg(param, curl_notify_callback);
- break;
- case CURLMOPT_NOTIFYDATA:
- multi->ntfy.ntfy_cb_data = va_arg(param, void *);
- break;
- case CURLMOPT_RESOLVE_THREADS_MAX:
+ case CURLMOPT_NOTIFYFUNCTION:
+ multi->ntfy.ntfy_cb = va_arg(param, curl_notify_callback);
+ break;
+ case CURLMOPT_NOTIFYDATA:
+ multi->ntfy.ntfy_cb_data = va_arg(param, void *);
+ break;
+ case CURLMOPT_RESOLVE_THREADS_MAX:
#ifdef USE_RESOLV_THREADED
- uarg = va_arg(param, long);
- if((uarg <= 0) || (uarg > UINT32_MAX))
- mresult = CURLM_BAD_FUNCTION_ARGUMENT;
- else {
- CURLcode result = Curl_async_thrdd_multi_set_props(
- multi, 0, (uint32_t)uarg, 2000);
- switch(result) {
- case CURLE_OK:
- mresult = CURLM_OK;
- break;
- case CURLE_BAD_FUNCTION_ARGUMENT:
+ uarg = va_arg(param, long);
+ if((uarg <= 0) || (uarg > UINT32_MAX))
mresult = CURLM_BAD_FUNCTION_ARGUMENT;
- break;
- case CURLE_OUT_OF_MEMORY:
- mresult = CURLM_OUT_OF_MEMORY;
- break;
- default:
- mresult = CURLM_INTERNAL_ERROR;
- break;
+ else {
+ CURLcode result = Curl_async_thrdd_multi_set_props(
+ multi, 0, (uint32_t)uarg, 2000);
+ switch(result) {
+ case CURLE_OK:
+ mresult = CURLM_OK;
+ break;
+ case CURLE_BAD_FUNCTION_ARGUMENT:
+ mresult = CURLM_BAD_FUNCTION_ARGUMENT;
+ break;
+ case CURLE_OUT_OF_MEMORY:
+ mresult = CURLM_OUT_OF_MEMORY;
+ break;
+ default:
+ mresult = CURLM_INTERNAL_ERROR;
+ break;
+ }
}
- }
#endif
- break;
- case CURLMOPT_QUICK_EXIT:
- multi->quick_exit = va_arg(param, long) ? 1 : 0;
- break;
- default:
- mresult = CURLM_UNKNOWN_OPTION;
- break;
+ break;
+ case CURLMOPT_QUICK_EXIT:
+ multi->quick_exit = va_arg(param, long) ? 1 : 0;
+ break;
+ default:
+ mresult = CURLM_UNKNOWN_OPTION;
+ break;
+ }
+ va_end(param);
}
- va_end(param);
+ CURL_MAPI_LEAVE(&guard);
return mresult;
}
CURLMcode curl_multi_socket(CURLM *m, curl_socket_t s, int *running_handles)
{
- struct Curl_multi *multi = m;
- if(multi->in_callback)
- return CURLM_RECURSIVE_API_CALL;
- if(multi->in_ntfy_callback)
- return CURLM_RECURSIVE_API_CALL;
- return multi_socket(multi, FALSE, s, 0, running_handles);
+ struct Curl_mapi_guard guard;
+ CURLMcode mresult;
+
+ if(CURL_MAPI_ENTER(&guard, m, multi_socket, &mresult)) {
+ mresult = multi_socket(m, FALSE, s, 0, running_handles);
+ }
+ CURL_MAPI_LEAVE(&guard);
+ return mresult;
}
CURLMcode curl_multi_socket_action(CURLM *m, curl_socket_t s,
int ev_bitmask, int *running_handles)
{
- struct Curl_multi *multi = m;
- if(multi->in_callback)
- return CURLM_RECURSIVE_API_CALL;
- if(multi->in_ntfy_callback)
- return CURLM_RECURSIVE_API_CALL;
- return multi_socket(multi, FALSE, s, ev_bitmask, running_handles);
+ struct Curl_mapi_guard guard;
+ CURLMcode mresult;
+
+ if(CURL_MAPI_ENTER(&guard, m, multi_socket_action, &mresult)) {
+ mresult = multi_socket(m, FALSE, s, ev_bitmask, running_handles);
+ }
+ CURL_MAPI_LEAVE(&guard);
+ return mresult;
}
CURLMcode curl_multi_socket_all(CURLM *m, int *running_handles)
{
- struct Curl_multi *multi = m;
- if(multi->in_callback)
- return CURLM_RECURSIVE_API_CALL;
- if(multi->in_ntfy_callback)
- return CURLM_RECURSIVE_API_CALL;
- return multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles);
+ struct Curl_mapi_guard guard;
+ CURLMcode mresult;
+
+ if(CURL_MAPI_ENTER(&guard, m, multi_socket_all, &mresult)) {
+ mresult = multi_socket(m, TRUE, CURL_SOCKET_BAD, 0, running_handles);
+ }
+ CURL_MAPI_LEAVE(&guard);
+ return mresult;
}
static bool multi_has_dirties(struct Curl_multi *multi)
VERBOSE(struct Curl_easy *data = NULL);
if(multi->dead) {
+ *expire_time = tv_zero;
*timeout_ms = 0;
return;
}
CURLMcode curl_multi_timeout(CURLM *m,
long *timeout_ms)
{
- struct curltime expire_time;
- struct Curl_multi *multi = m;
-
- /* First, make some basic checks that the CURLM handle is a good handle */
- if(!GOOD_MULTI_HANDLE(multi))
- return CURLM_BAD_HANDLE;
+ struct Curl_mapi_guard guard;
+ CURLMcode mresult;
- if(multi->in_callback)
- return CURLM_RECURSIVE_API_CALL;
+ if(CURL_MAPI_ENTER(&guard, m, multi_timeout, &mresult)) {
+ struct curltime expire_time;
- multi_timeout(multi, &expire_time, timeout_ms);
- return CURLM_OK;
+ multi_timeout(m, &expire_time, timeout_ms);
+ mresult = CURLM_OK;
+ }
+ CURL_MAPI_LEAVE(&guard);
+ return mresult;
}
/*
*/
CURLMcode Curl_update_timer(struct Curl_multi *multi)
{
- struct curltime expire_ts;
+ struct curltime expire_ts = { 0, 0 };
long timeout_ms;
int rc;
bool set_value = FALSE;
}
if(set_value) {
+ struct Curl_mapi_guard guard;
+
multi->last_expire_ts = expire_ts;
multi->last_timeout_ms = timeout_ms;
- set_in_callback(multi, TRUE);
+ CURL_CBAPI_MULTI_START(&guard, multi, multi_timer_cb);
rc = multi->timer_cb(multi, timeout_ms, multi->timer_userp);
- set_in_callback(multi, FALSE);
+ CURL_CBAPI_MULTI_END(&guard);
if(rc == -1) {
multi->dead = TRUE;
return CURLM_ABORTED_BY_CALLBACK;
CURLMcode curl_multi_assign(CURLM *m, curl_socket_t sockfd,
void *sockp)
{
- struct Curl_multi *multi = m;
- if(!GOOD_MULTI_HANDLE(multi))
- return CURLM_BAD_HANDLE;
+ struct Curl_mapi_guard guard;
+ CURLMcode mresult;
- return Curl_multi_ev_assign(multi, sockfd, sockp);
+ if(CURL_MAPI_ENTER(&guard, m, multi_assign, &mresult)) {
+ mresult = Curl_multi_ev_assign(m, sockfd, sockp);
+ }
+ CURL_MAPI_LEAVE(&guard);
+ return mresult;
}
static void move_pending_to_connect(struct Curl_multi *multi,
}
}
-/* 'value' used to be a boolean but can now also contain more info */
-void Curl_set_in_callback(struct Curl_easy *data, uint8_t value)
-{
- if(data && data->multi)
- data->multi->in_callback = value;
-}
-
-uint8_t Curl_is_in_callback(struct Curl_easy *data)
-{
- return (data && data->multi) ? data->multi->in_callback : IN_CALLBACK_NO;
-}
-
unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi)
{
DEBUGASSERT(multi);
CURL **curl_multi_get_handles(CURLM *m)
{
- struct Curl_multi *multi = m;
- void *entry;
- size_t count = Curl_uint32_tbl_count(&multi->xfers);
- CURL **a = curlx_malloc(sizeof(struct Curl_easy *) * (count + 1));
- if(a) {
- unsigned int i = 0;
- uint32_t mid;
+ struct Curl_mapi_guard guard;
+ CURL **a = NULL;
- if(Curl_uint32_tbl_first(&multi->xfers, &mid, &entry)) {
- do {
- struct Curl_easy *data = entry;
- DEBUGASSERT(i < count);
- if(!data->state.internal)
- a[i++] = data;
- } while(Curl_uint32_tbl_next(&multi->xfers, mid, &mid, &entry));
+ if(CURL_MAPI_ENTER(&guard, m, multi_get_handles, NULL)) {
+ struct Curl_multi *multi = m;
+ void *entry;
+ size_t count = Curl_uint32_tbl_count(&multi->xfers);
+
+ a = curlx_malloc(sizeof(struct Curl_easy *) * (count + 1));
+ if(a) {
+ unsigned int i = 0;
+ uint32_t mid;
+
+ if(Curl_uint32_tbl_first(&multi->xfers, &mid, &entry)) {
+ do {
+ struct Curl_easy *data = entry;
+ DEBUGASSERT(i < count);
+ if(!data->state.internal)
+ a[i++] = data;
+ } while(Curl_uint32_tbl_next(&multi->xfers, mid, &mid, &entry));
+ }
+ a[i] = NULL; /* last entry is a NULL */
}
- a[i] = NULL; /* last entry is a NULL */
}
+ CURL_MAPI_LEAVE(&guard);
return a;
}
CURLMinfo_offt info,
curl_off_t *pvalue)
{
- struct Curl_multi *multi = m;
- uint32_t n;
+ struct Curl_mapi_guard guard;
+ CURLMcode mresult = CURLM_OK;
- if(!GOOD_MULTI_HANDLE(multi))
- return CURLM_BAD_HANDLE;
- if(!pvalue)
- return CURLM_BAD_FUNCTION_ARGUMENT;
+ if(CURL_MAPI_ENTER(&guard, m, multi_get_offt, &mresult)) {
+ struct Curl_multi *multi = m;
+ uint32_t n;
- switch(info) {
- case CURLMINFO_XFERS_CURRENT:
- n = Curl_uint32_tbl_count(&multi->xfers);
- if(n && multi->admin)
- --n;
- *pvalue = (curl_off_t)n;
- return CURLM_OK;
- case CURLMINFO_XFERS_RUNNING:
- n = Curl_uint32_bset_count(&multi->process);
- if(n && Curl_uint32_bset_contains(&multi->process, multi->admin->mid))
- --n;
- *pvalue = (curl_off_t)n;
- return CURLM_OK;
- case CURLMINFO_XFERS_PENDING:
- *pvalue = (curl_off_t)Curl_uint32_bset_count(&multi->pending);
- return CURLM_OK;
- case CURLMINFO_XFERS_DONE:
- *pvalue = (curl_off_t)Curl_uint32_bset_count(&multi->msgsent);
- return CURLM_OK;
- case CURLMINFO_XFERS_ADDED:
- *pvalue = multi->xfers_total_ever;
- return CURLM_OK;
- default:
- *pvalue = -1;
- return CURLM_UNKNOWN_OPTION;
+ if(!pvalue) {
+ mresult = CURLM_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+
+ switch(info) {
+ case CURLMINFO_XFERS_CURRENT:
+ n = Curl_uint32_tbl_count(&multi->xfers);
+ if(n && multi->admin)
+ --n;
+ *pvalue = (curl_off_t)n;
+ break;
+ case CURLMINFO_XFERS_RUNNING:
+ n = Curl_uint32_bset_count(&multi->process);
+ if(n && Curl_uint32_bset_contains(&multi->process, multi->admin->mid))
+ --n;
+ *pvalue = (curl_off_t)n;
+ break;
+ case CURLMINFO_XFERS_PENDING:
+ *pvalue = (curl_off_t)Curl_uint32_bset_count(&multi->pending);
+ break;
+ case CURLMINFO_XFERS_DONE:
+ *pvalue = (curl_off_t)Curl_uint32_bset_count(&multi->msgsent);
+ break;
+ case CURLMINFO_XFERS_ADDED:
+ *pvalue = multi->xfers_total_ever;
+ break;
+ default:
+ *pvalue = -1;
+ mresult = CURLM_UNKNOWN_OPTION;
+ break;
+ }
}
+out:
+ CURL_MAPI_LEAVE(&guard);
+ return mresult;
}
CURLcode Curl_multi_xfer_buf_borrow(struct Curl_easy *data,
return NULL;
}
+bool Curl_multi_knows_easy(struct Curl_multi *multi, struct Curl_easy *data)
+{
+ return Curl_uint32_tbl_get(&multi->xfers, data->mid) == data;
+}
+
unsigned int Curl_multi_xfers_running(struct Curl_multi *multi)
{
DEBUGASSERT(multi);
CURLMcode curl_multi_notify_enable(CURLM *m, unsigned int notification)
{
- struct Curl_multi *multi = m;
+ struct Curl_mapi_guard guard;
+ CURLMcode mresult = CURLM_OK;
- if(!GOOD_MULTI_HANDLE(multi))
- return CURLM_BAD_HANDLE;
- return Curl_mntfy_enable(multi, notification);
+ if(CURL_MAPI_ENTER(&guard, m, multi_notify_enable, &mresult)) {
+ mresult = Curl_mntfy_enable(m, notification);
+ }
+ CURL_MAPI_LEAVE(&guard);
+ return mresult;
}
CURLMcode curl_multi_notify_disable(CURLM *m, unsigned int notification)
{
- struct Curl_multi *multi = m;
+ struct Curl_mapi_guard guard;
+ CURLMcode mresult = CURLM_OK;
- if(!GOOD_MULTI_HANDLE(multi))
- return CURLM_BAD_HANDLE;
- return Curl_mntfy_disable(multi, notification);
+ if(CURL_MAPI_ENTER(&guard, m, multi_notify_disable, &mresult)) {
+ mresult = Curl_mntfy_disable(m, notification);
+ }
+ CURL_MAPI_LEAVE(&guard);
+ return mresult;
}
#ifdef DEBUGBUILD
#include "uint-spbset.h"
#include "multihandle.h"
-static void mev_in_callback(struct Curl_multi *multi, uint8_t value)
-{
- multi->in_callback = value;
-}
-
#ifdef DEBUGBUILD
#define SH_ENTRY_MAGIC 0x570091d
#endif
/* We managed this socket before, tell the socket callback to forget it. */
if(entry->announced && multi->socket_cb) {
+ struct Curl_mapi_guard guard;
+
NOVERBOSE((void)cause);
CURL_TRC_M(data, "ev %s, call(fd=%" FMT_SOCKET_T ", ev=REMOVE)", cause, s);
- mev_in_callback(multi, IN_CALLBACK_FORBID_EASY_PAUSE);
+ CURL_CBAPI_MULTI_START(&guard, multi, multi_socket_cb);
rc = multi->socket_cb(data, s, CURL_POLL_REMOVE,
multi->socket_userp, entry->user_data);
- mev_in_callback(multi, IN_CALLBACK_NO);
+ CURL_CBAPI_END(&guard);
entry = mev_sh_entry_get(&multi->ev.sh_entries, s);
if(entry)
entry->announced = FALSE;
CURL_TRC_M(data, "ev update call(fd=%" FMT_SOCKET_T ", ev=%s%s)",
s, (comboaction & CURL_POLL_IN) ? "IN" : "",
(comboaction & CURL_POLL_OUT) ? "OUT" : "");
- mev_in_callback(multi, IN_CALLBACK_FORBID_EASY_PAUSE);
- rc = multi->socket_cb(data, s, comboaction, multi->socket_userp,
- entry->user_data);
- mev_in_callback(multi, IN_CALLBACK_NO);
+ {
+ struct Curl_mapi_guard guard;
+ CURL_CBAPI_MULTI_START(&guard, multi, multi_socket_cb);
+ rc = multi->socket_cb(data, s, comboaction, multi->socket_userp,
+ entry->user_data);
+ CURL_CBAPI_MULTI_END(&guard);
+ }
if(rc == -1) {
multi->dead = TRUE;
return CURLM_ABORTED_BY_CALLBACK;
CURLMcode Curl_mntfy_dispatch_all(struct Curl_multi *multi)
{
- DEBUGASSERT(!multi->in_ntfy_callback);
- multi->in_ntfy_callback = TRUE;
+ struct Curl_mapi_guard guard;
+
+ if(!multi)
+ return CURLM_BAD_FUNCTION_ARGUMENT;
+
+ CURL_CBAPI_MULTI_START(&guard, multi, multi_ntfy_cb);
+
while(multi->ntfy.head && !multi->ntfy.failure) {
struct mntfy_chunk *chunk = multi->ntfy.head;
/* this may cause new notifications to be added! */
multi->ntfy.head = chunk->next;
mnfty_chunk_destroy(chunk);
}
- multi->in_ntfy_callback = FALSE;
+
+ CURL_CBAPI_MULTI_END(&guard);
if(multi->ntfy.failure) {
CURLMcode mresult = multi->ntfy.failure;
* SPDX-License-Identifier: curl
*
***************************************************************************/
+#include "api.h"
#include "llist.h"
#include "hash.h"
#include "conncache.h"
struct CURLMsg extmsg;
};
-#define IN_CALLBACK_NO 0
-#define IN_CALLBACK_YES 1
-#define IN_CALLBACK_FORBID_EASY_PAUSE 2
-
/* NOTE: if you add a state here, add the name to the statenames[] array
* in curl_trc.c as well!
*/
/* This is the struct known as CURLM on the outside */
struct Curl_multi {
/* First a simple identifier to easier detect if a user mix up
- this multi handle with an easy handle. Set this to CURL_MULTI_HANDLE. */
- unsigned int magic;
+ this multi handle with an easy handle.
+ Set this to CURLMULTI_MAGIC_NUMBER. */
+ uint32_t magic;
+ uint32_t xfers_alive; /* amount of added transfers that have
+ not yet reached COMPLETE state */
+ uint32_t xfers_really_alive; /* amount of added transfers that have
+ passed INIT state but are not COMPLETE yet */
- unsigned int xfers_alive; /* amount of added transfers that have
- not yet reached COMPLETE state */
- unsigned int xfers_really_alive; /* amount of added transfers that have
- passed INIT state but are not COMPLETE yet */
- curl_off_t xfers_total_ever; /* total of added transfers, ever. */
struct uint32_tbl xfers; /* transfers added to this multi */
/* Each transfer's mid may be present in at most one of these */
struct uint32_bset process; /* transfer being processed */
struct uint32_bset pending; /* transfers in waiting (conn limit etc.) */
struct uint32_bset msgsent; /* transfers done with message for application */
+ struct Curl_mapi_stack callstack; /* multi api calls ongoing */
+
struct Curl_llist msglist; /* a list of messages from completed transfers */
+ curl_off_t xfers_total_ever; /* total of added transfers, ever. */
+
struct Curl_easy *admin; /* internal easy handle for admin operations.
gets assigned `mid` 0 on multi init */
#endif
uint32_t last_pending_mid; /* mid of last pending transfer rescheduled */
uint32_t last_resolv_id; /* id of the last DNS resolve operation */
- uint8_t in_callback; /* conditions for executing in callbacks */
BIT(ipv6_works);
BIT(multiplexing); /* multiplexing wanted */
BIT(recheckstate); /* see Curl_multi_connchanged */
- BIT(in_ntfy_callback); /* true while dispatching notifications */
#ifdef USE_OPENSSL
BIT(ssl_seeded);
#endif
struct connectdata *conn);
void Curl_detach_connection(struct Curl_easy *data);
bool Curl_multiplex_wanted(const struct Curl_multi *multi);
-void Curl_set_in_callback(struct Curl_easy *data, uint8_t value);
-uint8_t Curl_is_in_callback(struct Curl_easy *data);
CURLcode Curl_preconnect(struct Curl_easy *data);
bool Curl_is_connecting(struct Curl_easy *data);
void Curl_multi_connchanged(struct Curl_multi *multi);
+CURLMcode Curl_multi_add_handle(struct Curl_multi *multi,
+ struct Curl_easy *data);
+CURLMcode Curl_multi_remove_handle(struct Curl_multi *multi,
+ struct Curl_easy *data);
+
/* Internal version of curl_multi_init() accepts size parameters for the
socket, connection and dns hashes */
struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size,
/**
* Get the easy handle for the given mid.
- * Returns NULL if not found.
+ * Returns NULL if not found or not a GOOD_EASY_HANDLE()
*/
struct Curl_easy *Curl_multi_get_easy(struct Curl_multi *multi,
uint32_t mid);
+/* TRUE if multi knows about data via its `mid` */
+bool Curl_multi_knows_easy(struct Curl_multi *multi, struct Curl_easy *data);
+
/* Get the # of transfers current in process/pending. */
unsigned int Curl_multi_xfers_running(struct Curl_multi *multi);
int rc;
if(data->set.fxferinfo) {
/* There is a callback set, call that */
- Curl_set_in_callback(data, TRUE);
+ struct Curl_mapi_guard guard;
+ CURL_CBAPI_START(&guard, data, easy_fxferinfo);
rc = data->set.fxferinfo(data->set.progress_client,
data->progress.dl.total_size,
data->progress.dl.cur_size,
data->progress.ul.total_size,
data->progress.ul.cur_size);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
if(rc != CURL_PROGRESSFUNC_CONTINUE) {
if(rc) {
failf(data, "Callback aborted");
}
else if(data->set.fprogress) {
/* The older deprecated callback is set, call that */
- Curl_set_in_callback(data, TRUE);
+ struct Curl_mapi_guard guard;
+ CURL_CBAPI_START(&guard, data, easy_fprogress);
rc = data->set.fprogress(data->set.progress_client,
(double)data->progress.dl.total_size,
(double)data->progress.dl.cur_size,
(double)data->progress.ul.total_size,
(double)data->progress.ul.cur_size);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
if(rc != CURL_PROGRESSFUNC_CONTINUE) {
if(rc) {
failf(data, "Callback aborted");
static CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr,
size_t len)
{
+ struct Curl_mapi_guard guard;
size_t wrote;
curl_write_callback writeit;
void *user_ptr;
user_ptr = data->set.out;
}
- Curl_set_in_callback(data, TRUE);
+ CURL_CBAPI_START(&guard, data, easy_fwrite_rtp);
wrote = writeit((char *)CURL_UNCONST(ptr), 1, len, user_ptr);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
if(wrote == CURL_WRITEFUNC_PAUSE) {
failf(data, "Cannot pause RTP");
}
nread = 0;
if(ctx->read_cb && blen) {
- Curl_set_in_callback(data, TRUE);
+ struct Curl_mapi_guard guard;
+ CURL_CBAPI_START(&guard, data, easy_cr_in_read);
nread = ctx->read_cb(buf, 1, blen, ctx->cb_user_data);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
ctx->has_used_cb = TRUE;
}
return CURLE_READ_ERROR;
if(data->set.seek_func) {
- Curl_set_in_callback(data, TRUE);
+ struct Curl_mapi_guard guard;
+ CURL_CBAPI_START(&guard, data, easy_seek_func);
seekerr = data->set.seek_func(data->set.seek_client, offset, SEEK_SET);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
}
if(seekerr != CURL_SEEKFUNC_OK) {
}
/* when seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */
do {
+ struct Curl_mapi_guard guard;
char scratch[4 * 1024];
size_t readthisamountnow =
(offset - passed > (curl_off_t)sizeof(scratch)) ?
curlx_sotouz(offset - passed);
size_t actuallyread;
- Curl_set_in_callback(data, TRUE);
+ CURL_CBAPI_START(&guard, data, easy_cr_in_resume_from);
actuallyread = ctx->read_cb(scratch, 1, readthisamountnow,
ctx->cb_user_data);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
passed += actuallyread;
if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
return CURLE_OK;
if(data->set.seek_func) {
+ struct Curl_mapi_guard guard;
int err;
- Curl_set_in_callback(data, TRUE);
+ CURL_CBAPI_START(&guard, data, easy_seek_func);
err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
CURL_TRC_READ(data, "cr_in, rewind via set.seek_func -> %d", err);
if(err) {
failf(data, "seek callback returned error %d", err);
}
}
else if(data->set.ioctl_func) {
+ struct Curl_mapi_guard guard;
curlioerr err;
- Curl_set_in_callback(data, TRUE);
+ CURL_CBAPI_START(&guard, data, easy_ioctl_func);
err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD,
data->set.ioctl_client);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
CURL_TRC_READ(data, "cr_in, rewind via set.ioctl_func -> %d", (int)err);
if(err) {
failf(data, "ioctl callback returned error %d", (int)err);
* NOTE: This is one of few API functions that are allowed to be called from
* within a callback.
*/
-
#undef curl_easy_setopt
CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...)
{
- va_list arg;
+ struct Curl_eapi_guard guard;
CURLcode result;
- struct Curl_easy *data = curl;
- if(!data)
- return CURLE_BAD_FUNCTION_ARGUMENT;
+ if(CURL_EAPI_ENTER(&guard, curl, easy_setopt, &result)) {
+ struct Curl_easy *data = curl;
+ va_list arg;
- va_start(arg, option);
+ va_start(arg, option);
- result = Curl_vsetopt(data, option, arg);
+ result = Curl_vsetopt(data, option, arg);
- va_end(arg);
- if(result == CURLE_BAD_FUNCTION_ARGUMENT)
- failf(data, "setopt 0x%x got bad argument", (unsigned int)option);
+ va_end(arg);
+ if(result == CURLE_BAD_FUNCTION_ARGUMENT)
+ failf(data, "setopt 0x%x got bad argument", (unsigned int)option);
+ }
+ CURL_EAPI_LEAVE(&guard);
return result;
}
* use a forward proxy. */
bool Curl_xfer_is_secure(struct Curl_easy *data);
+/* Internal variant of the API function */
+CURLcode Curl_easy_recv(struct Curl_easy *data,
+ void *buffer, size_t buflen, size_t *n);
+
#endif /* HEADER_CURL_TRANSFER_H */
/* This handle is still part of a multi handle, take care of this first
and detach this handle from there.
This detaches the connection. */
- curl_multi_remove_handle(data->multi, data);
+ Curl_multi_remove_handle(data->multi, data);
}
else {
/* Detach connection if any is left. This should not be normal, but can be
#include "curlx/timeval.h"
+#include "api.h"
#include "asyn.h"
#include "cookie.h"
#include "creds.h"
#define UPLOADBUFFER_MAX (2 * 1024 * 1024)
#define UPLOADBUFFER_MIN CURL_MAX_WRITE_SIZE
-#define CURLEASY_MAGIC_NUMBER 0xc0dedbadU
-#ifdef DEBUGBUILD
-/* On a debug build, we want to fail hard on easy handles that
- * are not NULL, but no longer have the MAGIC touch. This gives
- * us early warning on things only discovered by valgrind otherwise. */
-#define GOOD_EASY_HANDLE(x) \
- (((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) ? TRUE : \
- (DEBUGASSERT(!(x)), FALSE))
-#else
-#define GOOD_EASY_HANDLE(x) \
- ((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER))
-#endif
-
#ifdef USE_WINDOWS_SSPI
#include "curl_sspi.h"
#endif
/* First a simple identifier to easier detect if a user mix up this easy
handle with a multi handle. Set this to CURLEASY_MAGIC_NUMBER */
uint32_t magic;
- /* once an easy handle is tied to a connection pool a non-negative number to
- distinguish this transfer from other using the same pool. For easier
- tracking in log output. This may wrap around after LONG_MAX to 0 again,
- so it has no uniqueness guarantee for large processings. Note: it has no
- uniqueness either IFF more than one connection pool is used by the
- libcurl application. */
- curl_off_t id;
/* once an easy handle is added to a multi, either explicitly by the
* libcurl application or implicitly during `curl_easy_perform()`,
* a unique identifier inside this one multi instance. */
uint32_t mid;
- uint32_t master_mid; /* if set, this transfer belongs to a master */
- multi_sub_xfer_done_cb *sub_xfer_done;
-
- struct connectdata *conn;
-
CURLMstate mstate; /* the handle's state */
CURLcode result; /* previous result */
- struct Curl_message msg; /* A single posted message. */
-
+ struct connectdata *conn;
struct Curl_multi *multi; /* if non-NULL, points to the multi handle
struct to which this "belongs" when used by
the multi interface */
+ struct Curl_eapi_stack callstack; /* easy api calls ongoing */
+
+ struct Curl_share *share; /* Share, handles global variable mutexing */
+
struct Curl_multi *multi_easy; /* if non-NULL, points to the multi handle
struct to which this "belongs" when used
by the easy interface */
- struct Curl_share *share; /* Share, handles global variable mutexing */
+ struct Curl_message msg; /* A single posted message. */
+
+ /* once an easy handle is tied to a connection pool a non-negative number to
+ distinguish this transfer from other using the same pool. For easier
+ tracking in log output. This may wrap around after LONG_MAX to 0 again,
+ so it has no uniqueness guarantee for large processings. Note: it has no
+ uniqueness either IFF more than one connection pool is used by the
+ libcurl application. */
+ curl_off_t id;
+ uint32_t master_mid; /* if set, this transfer belongs to a master */
+ multi_sub_xfer_done_cb *sub_xfer_done;
/* `meta_hash` is a general key-value store for implementations
* with the lifetime of the easy handle.
}
if(func) { /* use callback to determine action */
+ struct Curl_mapi_guard guard;
+
rc = ssh_pki_export_pubkey_base64(pubkey, &found_base64);
if(rc != SSH_OK)
goto cleanup;
goto cleanup;
}
- Curl_set_in_callback(data, TRUE);
+ CURL_CBAPI_START(&guard, data, easy_ssh_keyfunc);
rc = func(data, knownkeyp, /* from the knownhosts file */
&foundkey, /* from the remote host */
keymatch, data->set.ssh_keyfunc_userp);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
switch(rc) {
case CURLKHSTAT_FINE_ADD_TO_FILE:
int seekerr = CURL_SEEKFUNC_OK;
/* Let's read off the proper amount of bytes from the input. */
if(data->set.seek_func) {
- Curl_set_in_callback(data, TRUE);
+ struct Curl_mapi_guard guard;
+ CURL_CBAPI_START(&guard, data, easy_seek_func);
seekerr = data->set.seek_func(data->set.seek_client,
data->state.resume_from, SEEK_SET);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
}
if(seekerr != CURL_SEEKFUNC_OK) {
* What hostname does OpenSSH store in its file if an IDN name is
* used?
*/
+ struct Curl_mapi_guard guard;
enum curl_khmatch keymatch;
curl_sshkeycallback func =
data->set.ssh_keyfunc ? data->set.ssh_keyfunc : sshkeycallback;
keymatch = (enum curl_khmatch)keycheck;
/* Ask the callback how to behave */
- Curl_set_in_callback(data, TRUE);
+ CURL_CBAPI_START(&guard, data, easy_ssh_keyfunc);
rc = func(data, knownkeyp, /* from the knownhosts file */
&foundkey, /* from the remote host */
keymatch, data->set.ssh_keyfunc_userp);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
}
}
else {
const char *remotekey = libssh2_session_hostkey(sshc->ssh_session,
&keylen, &sshkeytype);
if(remotekey) {
+ struct Curl_mapi_guard guard;
enum curl_khtype keytype = convert_ssh2_keytype(sshkeytype);
- Curl_set_in_callback(data, TRUE);
+ CURL_CBAPI_START(&guard, data, easy_ssh_hostkeyfunc);
rc = data->set.ssh_hostkeyfunc(data->set.ssh_hostkeyfunc_userp,
(int)keytype, remotekey, keylen);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
if(rc != CURLKHMATCH_OK) {
myssh_to(data, sshc, SSH_SESSION_FREE);
failf(data, "SSH: callback failed host public key verification");
int seekerr = CURL_SEEKFUNC_OK;
/* Let's read off the proper amount of bytes from the input. */
if(data->set.seek_func) {
- Curl_set_in_callback(data, TRUE);
+ struct Curl_mapi_guard guard;
+ CURL_CBAPI_START(&guard, data, easy_seek_func);
seekerr = data->set.seek_func(data->set.seek_client,
data->state.resume_from, SEEK_SET);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
}
if(seekerr != CURL_SEEKFUNC_OK) {
}
/* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */
do {
+ struct Curl_mapi_guard guard;
char scratch[4 * 1024];
size_t readthisamountnow =
(data->state.resume_from - passed >
sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed);
size_t actuallyread;
- Curl_set_in_callback(data, TRUE);
+ CURL_CBAPI_START(&guard, data, easy_fread_func);
actuallyread = data->state.fread_func(scratch, 1,
readthisamountnow,
data->state.in);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
passed += actuallyread;
if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
/* give application a chance to interfere with SSL set up. */
if(data->set.ssl.fsslctx) {
+ struct Curl_mapi_guard guard;
/* When a user callback is installed to modify the SSL_CTX,
* we need to do the full initialization before calling it.
* See: #11800 */
return result;
octx->x509_store_setup = TRUE;
}
- Curl_set_in_callback(data, TRUE);
+ CURL_CBAPI_START(&guard, data, easy_fsslctx);
result = (*data->set.ssl.fsslctx)(data, octx->ssl_ctx,
data->set.ssl.fsslctxp);
- Curl_set_in_callback(data, FALSE);
+ CURL_CBAPI_END(&guard);
if(result) {
failf(data, "error signaled by SSL ctx callback");
return result;
out:
if(!result) {
- result = ws_flush(data, ws, Curl_is_in_callback(data));
+ result = ws_flush(data, ws, Curl_api_is_in_callback(data));
if(result == CURLE_AGAIN)
result = CURLE_OK;
}
}
}
else {
- result = ws_flush(data, ws, Curl_is_in_callback(data));
+ result = ws_flush(data, ws, Curl_api_is_in_callback(data));
if(result)
return result;
}
/* flush, blocking when in callback */
- result = ws_flush(data, ws, Curl_is_in_callback(data));
+ result = ws_flush(data, ws, Curl_api_is_in_callback(data));
if(!result && ws->sendbuf_payload > 0) {
*pnsent += ws->sendbuf_payload;
buffer += ws->sendbuf_payload;
size_t *pnread)
{
struct Curl_easy *data = reader_ctx;
- return curl_easy_recv(data, buf, buflen, pnread);
+ return Curl_easy_recv(data, buf, buflen, pnread);
}
CURLcode curl_ws_recv(CURL *curl, void *buffer,
size_t buflen, size_t *recv,
const struct curl_ws_frame **metap)
{
- struct Curl_easy *data = curl;
- struct connectdata *conn;
- struct websocket *ws;
- struct ws_collect ctx;
+ struct Curl_eapi_guard guard;
+ CURLcode result = CURLE_OK;
*recv = 0;
*metap = NULL;
- if(!GOOD_EASY_HANDLE(data) || (buflen && !buffer))
- return CURLE_BAD_FUNCTION_ARGUMENT;
+ if(CURL_EAPI_ENTER(&guard, curl, ws_recv, &result)) {
+ struct Curl_easy *data = curl;
+ struct connectdata *conn;
+ struct websocket *ws;
+ struct ws_collect ctx;
- conn = data->conn;
- if(!conn) {
- /* Unhappy hack with lifetimes of transfers and connection */
- if(!data->set.connect_only) {
- failf(data, "[WS] CONNECT_ONLY is required");
- return CURLE_UNSUPPORTED_PROTOCOL;
+ if(buflen && !buffer) {
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
}
- Curl_getconnectinfo(data, &conn);
+ conn = data->conn;
if(!conn) {
- failf(data, "[WS] connection not found");
- return CURLE_BAD_FUNCTION_ARGUMENT;
- }
- }
- ws = Curl_conn_meta_get(conn, CURL_META_PROTO_WS_CONN);
- if(!ws) {
- failf(data, "[WS] connection is not setup for websocket");
- return CURLE_BAD_FUNCTION_ARGUMENT;
- }
-
- memset(&ctx, 0, sizeof(ctx));
- ctx.data = data;
- ctx.ws = ws;
- ctx.buffer = buffer;
- ctx.buflen = buflen;
-
- while(1) {
- CURLcode result;
-
- /* receive more when our buffer is empty */
- if(Curl_bufq_is_empty(&ws->recvbuf)) {
- size_t n;
- result = Curl_bufq_slurp(&ws->recvbuf, nw_in_recv, data, &n);
- if(result)
- return result;
- else if(n == 0) {
- /* connection closed */
- infof(data, "[WS] connection expectedly closed?");
- return CURLE_GOT_NOTHING;
+ /* Unhappy hack with lifetimes of transfers and connection */
+ if(!data->set.connect_only) {
+ failf(data, "[WS] CONNECT_ONLY is required");
+ result = CURLE_UNSUPPORTED_PROTOCOL;
+ goto out;
}
- CURL_TRC_WS(data, "curl_ws_recv, added %zu bytes from network",
- Curl_bufq_len(&ws->recvbuf));
- }
- result = ws_dec_pass(&ws->dec, data, &ws->recvbuf,
- ws_client_collect, &ctx);
- if(result == CURLE_AGAIN) {
- if(!ctx.written) {
- ws_dec_info(&ws->dec, data, "need more input");
- continue; /* nothing written, try more input */
+ Curl_getconnectinfo(data, &conn);
+ if(!conn) {
+ failf(data, "[WS] connection not found");
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
}
- break;
}
- else if(result) {
- return result;
+ ws = Curl_conn_meta_get(conn, CURL_META_PROTO_WS_CONN);
+ if(!ws) {
+ failf(data, "[WS] connection is not setup for websocket");
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
}
- else if(ctx.written) {
- /* The decoded frame is passed back to our caller.
- * There are frames like PING were we auto-respond to and
- * that we do not return. For these `ctx.written` is not set. */
- break;
+
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.data = data;
+ ctx.ws = ws;
+ ctx.buffer = buffer;
+ ctx.buflen = buflen;
+
+ while(1) {
+ /* receive more when our buffer is empty */
+ if(Curl_bufq_is_empty(&ws->recvbuf)) {
+ size_t n;
+ result = Curl_bufq_slurp(&ws->recvbuf, nw_in_recv, data, &n);
+ if(result)
+ goto out;
+ else if(n == 0) {
+ /* connection closed */
+ infof(data, "[WS] connection expectedly closed?");
+ result = CURLE_GOT_NOTHING;
+ goto out;
+ }
+ CURL_TRC_WS(data, "curl_ws_recv, added %zu bytes from network",
+ Curl_bufq_len(&ws->recvbuf));
+ }
+
+ result = ws_dec_pass(&ws->dec, data, &ws->recvbuf,
+ ws_client_collect, &ctx);
+ if(result == CURLE_AGAIN) {
+ if(!ctx.written) {
+ ws_dec_info(&ws->dec, data, "need more input");
+ continue; /* nothing written, try more input */
+ }
+ break;
+ }
+ else if(result) {
+ goto out;
+ }
+ else if(ctx.written) {
+ /* The decoded frame is passed back to our caller.
+ * There are frames like PING were we auto-respond to and
+ * that we do not return. For these `ctx.written` is not set. */
+ break;
+ }
}
- }
- /* update frame information to be passed back */
- update_meta(ws, ctx.frame_age, ctx.frame_flags, ctx.payload_offset,
- ctx.payload_len, ctx.bufidx);
- *metap = &ws->recvframe;
- *recv = ws->recvframe.len;
- CURL_TRC_WS(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %"
- FMT_OFF_T ", %" FMT_OFF_T " left)",
- buflen, *recv, ws->recvframe.offset,
- ws->recvframe.bytesleft);
- /* all's well, try to send any pending control. we do not know
- * when the application will call `curl_ws_send()` again. */
- if(!data->set.ws_raw_mode && ws->pending.type) {
- CURLcode r2 = ws_enc_add_pending(data, ws);
- if(!r2)
- (void)ws_flush(data, ws, Curl_is_in_callback(data));
+ /* update frame information to be passed back */
+ update_meta(ws, ctx.frame_age, ctx.frame_flags, ctx.payload_offset,
+ ctx.payload_len, ctx.bufidx);
+ *metap = &ws->recvframe;
+ *recv = ws->recvframe.len;
+ CURL_TRC_WS(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %"
+ FMT_OFF_T ", %" FMT_OFF_T " left)",
+ buflen, *recv, ws->recvframe.offset,
+ ws->recvframe.bytesleft);
+ /* all's well, try to send any pending control. we do not know
+ * when the application will call `curl_ws_send()` again. */
+ if(!data->set.ws_raw_mode && ws->pending.type) {
+ CURLcode r2 = ws_enc_add_pending(data, ws);
+ if(!r2)
+ (void)ws_flush(data, ws, Curl_api_is_in_callback(data));
+ }
+ result = CURLE_OK;
}
- return CURLE_OK;
+out:
+ CURL_EAPI_LEAVE(&guard);
+ return result;
}
static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws,
result = ws_send_raw_blocking(data, ws, (const char *)out, outlen);
n = result ? 0 : outlen;
}
- else if(data->set.connect_only || Curl_is_in_callback(data))
+ else if(data->set.connect_only || Curl_api_is_in_callback(data))
result = Curl_senddata(data, out, outlen, &n);
else {
result = Curl_xfer_send(data, out, outlen, FALSE, &n);
if(!buflen)
return CURLE_OK;
- if(Curl_is_in_callback(data)) {
+ if(Curl_api_is_in_callback(data)) {
/* When invoked from inside callbacks, we do a blocking send as the
* callback will probably not implement partial writes that may then
* mess up the ws framing subsequently.
curl_off_t fragsize,
unsigned int flags)
{
- struct websocket *ws;
- const uint8_t *buffer = buffer_arg;
+ struct Curl_eapi_guard guard;
CURLcode result = CURLE_OK;
- struct Curl_easy *data = curl;
- size_t ndummy;
- size_t *pnsent = sent ? sent : &ndummy;
-
- if(!GOOD_EASY_HANDLE(data))
- return CURLE_BAD_FUNCTION_ARGUMENT;
- CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T
- ", flags=%x), raw=%d",
- buflen, fragsize, flags, data->set.ws_raw_mode);
- *pnsent = 0;
+ if(CURL_EAPI_ENTER(&guard, curl, ws_send, &result)) {
+ struct websocket *ws;
+ const uint8_t *buffer = buffer_arg;
+ struct Curl_easy *data = curl;
+ size_t ndummy;
+ size_t *pnsent = sent ? sent : &ndummy;
- if(!buffer && buflen) {
- failf(data, "[WS] buffer is NULL when buflen is not");
- result = CURLE_BAD_FUNCTION_ARGUMENT;
- goto out;
- }
+ CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T
+ ", flags=%x), raw=%d",
+ buflen, fragsize, flags, data->set.ws_raw_mode);
- if(!data->conn && data->set.connect_only) {
- result = Curl_connect_only_attach(data);
- if(result)
- goto out;
- }
- if(!data->conn) {
- failf(data, "[WS] No associated connection");
- result = CURLE_SEND_ERROR;
- goto out;
- }
- ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
- if(!ws) {
- failf(data, "[WS] Not a websocket transfer");
- result = CURLE_SEND_ERROR;
- goto out;
- }
+ *pnsent = 0;
- if(data->set.ws_raw_mode) {
- /* In raw mode, we write directly to the connection */
- /* try flushing any content still waiting to be sent. */
- result = ws_flush(data, ws, FALSE);
- if(result)
+ if(!buffer && buflen) {
+ failf(data, "[WS] buffer is NULL when buflen is not");
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
goto out;
+ }
- if(!buffer) {
- failf(data, "[WS] buffer is NULL in raw mode");
- return CURLE_BAD_FUNCTION_ARGUMENT;
+ if(!data->conn && data->set.connect_only) {
+ result = Curl_connect_only_attach(data);
+ if(result)
+ goto out;
}
- if(!sent) {
- failf(data, "[WS] sent is NULL in raw mode");
- return CURLE_BAD_FUNCTION_ARGUMENT;
+ if(!data->conn) {
+ failf(data, "[WS] No associated connection");
+ result = CURLE_SEND_ERROR;
+ goto out;
}
- if(fragsize || flags) {
- failf(data, "[WS] fragsize and flags must be zero in raw mode");
- return CURLE_BAD_FUNCTION_ARGUMENT;
+ ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
+ if(!ws) {
+ failf(data, "[WS] Not a websocket transfer");
+ result = CURLE_SEND_ERROR;
+ goto out;
}
- result = ws_send_raw(data, buffer, buflen, pnsent);
- goto out;
- }
- /* Not RAW mode, we do the frame encoding */
- result = ws_enc_send(data, ws, buffer, buflen, fragsize, flags, pnsent);
+ if(data->set.ws_raw_mode) {
+ /* In raw mode, we write directly to the connection */
+ /* try flushing any content still waiting to be sent. */
+ result = ws_flush(data, ws, FALSE);
+ if(result)
+ goto out;
+
+ if(!buffer) {
+ failf(data, "[WS] buffer is NULL in raw mode");
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+ if(!sent) {
+ failf(data, "[WS] sent is NULL in raw mode");
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+ if(fragsize || flags) {
+ failf(data, "[WS] fragsize and flags must be zero in raw mode");
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+ result = ws_send_raw(data, buffer, buflen, pnsent);
+ goto out;
+ }
+ /* Not RAW mode, we do the frame encoding */
+ result = ws_enc_send(data, ws, buffer, buflen, fragsize, flags, pnsent);
+ CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T
+ ", flags=%x, raw=%d) -> %d, %zu",
+ buflen, fragsize, flags, data->set.ws_raw_mode, (int)result,
+ *pnsent);
+ }
out:
- CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T
- ", flags=%x, raw=%d) -> %d, %zu",
- buflen, fragsize, flags, data->set.ws_raw_mode, (int)result,
- *pnsent);
+ CURL_EAPI_LEAVE(&guard);
return result;
}
/* we only return something for websocket, called from within the callback
when not using raw mode */
struct Curl_easy *data = curl;
- if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) &&
+ if(GOOD_EASY_HANDLE(data) && Curl_api_is_in_callback(data) &&
data->conn && !data->set.ws_raw_mode) {
struct websocket *ws;
ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
unsigned int flags,
curl_off_t frame_len)
{
- struct websocket *ws;
+ struct Curl_eapi_guard guard;
CURLcode result = CURLE_OK;
- struct Curl_easy *data = curl;
-
- if(!GOOD_EASY_HANDLE(data))
- return CURLE_BAD_FUNCTION_ARGUMENT;
- if(data->set.ws_raw_mode) {
- failf(data, "cannot curl_ws_start_frame() with CURLWS_RAW_MODE enabled");
- return CURLE_FAILED_INIT;
- }
+ if(CURL_EAPI_ENTER(&guard, curl, ws_start_frame, &result)) {
+ struct Curl_easy *data = curl;
+ struct websocket *ws;
- CURL_TRC_WS(data, "curl_ws_start_frame(flags=%x, frame_len=%" FMT_OFF_T,
- flags, frame_len);
+ if(data->set.ws_raw_mode) {
+ failf(data, "cannot curl_ws_start_frame() with CURLWS_RAW_MODE enabled");
+ result = CURLE_FAILED_INIT;
+ goto out;
+ }
- if(!data->conn) {
- failf(data, "[WS] No associated connection");
- result = CURLE_SEND_ERROR;
- goto out;
- }
- ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
- if(!ws) {
- failf(data, "[WS] Not a websocket transfer");
- result = CURLE_SEND_ERROR;
- goto out;
- }
+ CURL_TRC_WS(data, "curl_ws_start_frame(flags=%x, frame_len=%" FMT_OFF_T,
+ flags, frame_len);
- if(ws->enc.payload_remain) {
- failf(data, "[WS] previous frame not finished");
- result = CURLE_SEND_ERROR;
- goto out;
- }
+ if(!data->conn) {
+ failf(data, "[WS] No associated connection");
+ result = CURLE_SEND_ERROR;
+ goto out;
+ }
+ ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
+ if(!ws) {
+ failf(data, "[WS] Not a websocket transfer");
+ result = CURLE_SEND_ERROR;
+ goto out;
+ }
- result = ws_enc_write_head(data, ws, &ws->enc, flags, frame_len,
- &ws->sendbuf);
- if(result)
- CURL_TRC_WS(data, "curl_start_frame(), error adding frame head %d",
- (int)result);
+ if(ws->enc.payload_remain) {
+ failf(data, "[WS] previous frame not finished");
+ result = CURLE_SEND_ERROR;
+ goto out;
+ }
+ result = ws_enc_write_head(data, ws, &ws->enc, flags, frame_len,
+ &ws->sendbuf);
+ if(result)
+ CURL_TRC_WS(data, "curl_start_frame(), error adding frame head %d",
+ (int)result);
+ }
out:
+ CURL_EAPI_LEAVE(&guard);
return result;
}
These sizes were chosen with platforms with 64-bit pointers in mind. */
#define MAX_CURL_EASY 5370
#define MAX_CONNECTDATA 1300
-#define MAX_CURL_MULTI 850
+#define MAX_CURL_MULTI 920
#define MAX_CURL_HTTPPOST 112
#define MAX_CURL_SLIST 16
#define MAX_CURL_KHKEY 24