From e7d8329ad29a046488390a0d4d41e5878104cb75 Mon Sep 17 00:00:00 2001 From: fsbs Date: Sun, 31 Oct 2021 04:06:36 +0000 Subject: Wrap curl_slist in a Python object The goal is for each curl_slist to have its own reference count that is managed by Python. This allows for sharing of curl_slist data between cloned handles (curl_easy_duphandle). --- src/easy.c | 127 +++++++++++++++++++++++++++++++++++----------------------- src/easyopt.c | 30 +++++++------- src/module.c | 7 ++++ src/pycurl.h | 31 +++++++++----- 4 files changed, 118 insertions(+), 77 deletions(-) diff --git a/src/easy.c b/src/easy.c index a22ad9c..9fd8be9 100644 --- a/src/easy.c +++ b/src/easy.c @@ -1,6 +1,41 @@ #include "pycurl.h" #include "docstrings.h" +/************************************************************************* +// CurlSlistObject +**************************************************************************/ + +PYCURL_INTERNAL void +util_curlslist_update(CurlSlistObject **old, struct curl_slist *slist) +{ + /* Decref previous object */ + Py_XDECREF(*old); + /* Create a new object */ + *old = PyObject_New(CurlSlistObject, p_CurlSlist_Type); + assert(*old != NULL); + /* Store curl_slist into the new object */ + (*old)->slist = slist; +} + +PYCURL_INTERNAL void +do_curl_slist_dealloc(CurlSlistObject *self) { + if (self->slist != NULL) { + curl_slist_free_all(self->slist); + self->slist = NULL; + } + Py_TYPE(self)->tp_free((PyObject *) self); +} + +/* TODO: Python 2 compatible */ +PYCURL_INTERNAL PyTypeObject CurlSlist_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "pycurl.CurlSlist", + .tp_basicsize = sizeof(CurlSlistObject), + .tp_itemsize = 0, + .tp_dealloc = (destructor) do_curl_slist_dealloc, +}; + + /************************************************************************* // static utility functions **************************************************************************/ @@ -202,6 +237,28 @@ util_curl_xdecref(CurlObject *self, int flags, CURL *handle) /* Decrement refcounts for ca certs related references. */ Py_CLEAR(self->ca_certs_obj); } + + if (flags & PYCURL_MEMGROUP_SLIST) { + /* Decrement refcounts for slist objects. */ + Py_CLEAR(self->httpheader); +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 37, 0) + Py_CLEAR(self->proxyheader); +#endif + Py_CLEAR(self->http200aliases); + Py_CLEAR(self->quote); + Py_CLEAR(self->postquote); + Py_CLEAR(self->prequote); + Py_CLEAR(self->telnetoptions); +#ifdef HAVE_CURLOPT_RESOLVE + Py_CLEAR(self->resolve); +#endif +#ifdef HAVE_CURL_7_20_0_OPTS + Py_CLEAR(self->mail_rcpt); +#endif +#ifdef HAVE_CURLOPT_CONNECT_TO + Py_CLEAR(self->connect_to); +#endif + } } @@ -247,32 +304,6 @@ util_curl_close(CurlObject *self) if (self->weakreflist != NULL) { PyObject_ClearWeakRefs((PyObject *) self); } - - /* Free all variables allocated by setopt */ -#undef SFREE -#define SFREE(v) if ((v) != NULL) (curl_formfree(v), (v) = NULL) - SFREE(self->httppost); -#undef SFREE -#define SFREE(v) if ((v) != NULL) (curl_slist_free_all(v), (v) = NULL) - SFREE(self->httpheader); -#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 37, 0) - SFREE(self->proxyheader); -#endif - SFREE(self->http200aliases); - SFREE(self->quote); - SFREE(self->postquote); - SFREE(self->prequote); - SFREE(self->telnetoptions); -#ifdef HAVE_CURLOPT_RESOLVE - SFREE(self->resolve); -#endif -#ifdef HAVE_CURL_7_20_0_OPTS - SFREE(self->mail_rcpt); -#endif -#ifdef HAVE_CURLOPT_CONNECT_TO - SFREE(self->connect_to); -#endif -#undef SFREE } @@ -351,6 +382,25 @@ do_curl_traverse(CurlObject *self, visitproc visit, void *arg) VISIT(self->ca_certs_obj); + VISIT((PyObject *) self->httpheader); +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 37, 0) + VISIT((PyObject *) self->proxyheader); +#endif + VISIT((PyObject *) self->http200aliases); + VISIT((PyObject *) self->quote); + VISIT((PyObject *) self->postquote); + VISIT((PyObject *) self->prequote); + VISIT((PyObject *) self->telnetoptions); +#ifdef HAVE_CURLOPT_RESOLVE + VISIT((PyObject *) self->resolve); +#endif +#ifdef HAVE_CURL_7_20_0_OPTS + VISIT((PyObject *) self->mail_rcpt); +#endif +#ifdef HAVE_CURLOPT_CONNECT_TO + VISIT((PyObject *) self->connect_to); +#endif + return 0; #undef VISIT } @@ -368,31 +418,6 @@ do_curl_reset(CurlObject *self) /* Decref easy interface related objects */ util_curl_xdecref(self, PYCURL_MEMGROUP_EASY, self->handle); - /* Free all variables allocated by setopt */ -#undef SFREE -#define SFREE(v) if ((v) != NULL) (curl_formfree(v), (v) = NULL) - SFREE(self->httppost); -#undef SFREE -#define SFREE(v) if ((v) != NULL) (curl_slist_free_all(v), (v) = NULL) - SFREE(self->httpheader); -#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 37, 0) - SFREE(self->proxyheader); -#endif - SFREE(self->http200aliases); - SFREE(self->quote); - SFREE(self->postquote); - SFREE(self->prequote); - SFREE(self->telnetoptions); -#ifdef HAVE_CURLOPT_RESOLVE - SFREE(self->resolve); -#endif -#ifdef HAVE_CURL_7_20_0_OPTS - SFREE(self->mail_rcpt); -#endif -#ifdef HAVE_CURLOPT_CONNECT_TO - SFREE(self->connect_to); -#endif -#undef SFREE res = util_curl_init(self); if (res < 0) { Py_DECREF(self); /* this also closes self->handle */ diff --git a/src/easyopt.c b/src/easyopt.c index ba4108c..5c6f29e 100644 --- a/src/easyopt.c +++ b/src/easyopt.c @@ -713,48 +713,48 @@ error: static PyObject * do_curl_setopt_list(CurlObject *self, int option, int which, PyObject *obj) { - struct curl_slist **old_slist = NULL; + CurlSlistObject **old_slist_obj = NULL; struct curl_slist *slist = NULL; Py_ssize_t len; int res; switch (option) { case CURLOPT_HTTP200ALIASES: - old_slist = &self->http200aliases; + old_slist_obj = &self->http200aliases; break; case CURLOPT_HTTPHEADER: - old_slist = &self->httpheader; + old_slist_obj = &self->httpheader; break; #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 37, 0) case CURLOPT_PROXYHEADER: - old_slist = &self->proxyheader; + old_slist_obj = &self->proxyheader; break; #endif case CURLOPT_POSTQUOTE: - old_slist = &self->postquote; + old_slist_obj = &self->postquote; break; case CURLOPT_PREQUOTE: - old_slist = &self->prequote; + old_slist_obj = &self->prequote; break; case CURLOPT_QUOTE: - old_slist = &self->quote; + old_slist_obj = &self->quote; break; case CURLOPT_TELNETOPTIONS: - old_slist = &self->telnetoptions; + old_slist_obj = &self->telnetoptions; break; #ifdef HAVE_CURLOPT_RESOLVE case CURLOPT_RESOLVE: - old_slist = &self->resolve; + old_slist_obj = &self->resolve; break; #endif #ifdef HAVE_CURL_7_20_0_OPTS case CURLOPT_MAIL_RCPT: - old_slist = &self->mail_rcpt; + old_slist_obj = &self->mail_rcpt; break; #endif #ifdef HAVE_CURLOPT_CONNECT_TO case CURLOPT_CONNECT_TO: - old_slist = &self->connect_to; + old_slist_obj = &self->connect_to; break; #endif default: @@ -768,7 +768,7 @@ do_curl_setopt_list(CurlObject *self, int option, int which, PyObject *obj) Py_RETURN_NONE; /* Just to be sure we do not bug off here */ - assert(old_slist != NULL && slist == NULL); + assert(old_slist_obj != NULL && slist == NULL); /* Handle regular list operations on the other options */ slist = pycurl_list_or_tuple_to_slist(which, obj, len); @@ -781,9 +781,9 @@ do_curl_setopt_list(CurlObject *self, int option, int which, PyObject *obj) curl_slist_free_all(slist); CURLERROR_RETVAL(); } - /* Finally, free previously allocated list and update */ - curl_slist_free_all(*old_slist); - *old_slist = slist; + /* Finally, decref previous slist object and replace it with a + * new one. */ + util_curlslist_update(old_slist_obj, slist); Py_RETURN_NONE; } diff --git a/src/module.c b/src/module.c index 2331168..2f40e98 100644 --- a/src/module.c +++ b/src/module.c @@ -28,6 +28,7 @@ PYCURL_INTERNAL char *g_pycurl_useragent = NULL; /* Type objects */ PYCURL_INTERNAL PyObject *ErrorObject = NULL; PYCURL_INTERNAL PyTypeObject *p_Curl_Type = NULL; +PYCURL_INTERNAL PyTypeObject *p_CurlSlist_Type = NULL; PYCURL_INTERNAL PyTypeObject *p_CurlMulti_Type = NULL; PYCURL_INTERNAL PyTypeObject *p_CurlShare_Type = NULL; #ifdef HAVE_CURL_7_19_6_OPTS @@ -416,9 +417,11 @@ initpycurl(void) /* Initialize the type of the new type objects here; doing it here * is required for portability to Windows without requiring C++. */ p_Curl_Type = &Curl_Type; + p_CurlSlist_Type = &CurlSlist_Type; p_CurlMulti_Type = &CurlMulti_Type; p_CurlShare_Type = &CurlShare_Type; Py_SET_TYPE(&Curl_Type, &PyType_Type); + Py_SET_TYPE(&CurlSlist_Type, &PyType_Type); Py_SET_TYPE(&CurlMulti_Type, &PyType_Type); Py_SET_TYPE(&CurlShare_Type, &PyType_Type); @@ -426,12 +429,16 @@ initpycurl(void) if (PyType_Ready(&Curl_Type) < 0) goto error; + if (PyType_Ready(&CurlSlist_Type) < 0) + goto error; + if (PyType_Ready(&CurlMulti_Type) < 0) goto error; if (PyType_Ready(&CurlShare_Type) < 0) goto error; + #if PY_MAJOR_VERSION >= 3 m = PyModule_Create(&curlmodule); if (m == NULL) diff --git a/src/pycurl.h b/src/pycurl.h index 31d1eac..f4f71f6 100644 --- a/src/pycurl.h +++ b/src/pycurl.h @@ -371,16 +371,23 @@ create_and_set_error_object(struct CurlObject *self, int code); #define PYCURL_MEMGROUP_POSTFIELDS 64 /* CA certs object */ #define PYCURL_MEMGROUP_CACERTS 128 +/* Curl slist objects */ +#define PYCURL_MEMGROUP_SLIST 256 #define PYCURL_MEMGROUP_EASY \ (PYCURL_MEMGROUP_CALLBACK | PYCURL_MEMGROUP_FILE | \ PYCURL_MEMGROUP_HTTPPOST | PYCURL_MEMGROUP_POSTFIELDS | \ - PYCURL_MEMGROUP_CACERTS) + PYCURL_MEMGROUP_CACERTS | PYCURL_MEMGROUP_SLIST) #define PYCURL_MEMGROUP_ALL \ (PYCURL_MEMGROUP_ATTRDICT | PYCURL_MEMGROUP_EASY | \ PYCURL_MEMGROUP_MULTI | PYCURL_MEMGROUP_SHARE) +typedef struct CurlSlistObject { + PyObject_HEAD + struct curl_slist *slist; +} CurlSlistObject; + typedef struct CurlObject { PyObject_HEAD PyObject *dict; /* Python attributes dictionary */ @@ -395,23 +402,23 @@ typedef struct CurlObject { struct curl_httppost *httppost; /* List of INC'ed references associated with httppost. */ PyObject *httppost_ref_list; - struct curl_slist *httpheader; + struct CurlSlistObject *httpheader; #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 37, 0) - struct curl_slist *proxyheader; + struct CurlSlistObject *proxyheader; #endif - struct curl_slist *http200aliases; - struct curl_slist *quote; - struct curl_slist *postquote; - struct curl_slist *prequote; - struct curl_slist *telnetoptions; + struct CurlSlistObject *http200aliases; + struct CurlSlistObject *quote; + struct CurlSlistObject *postquote; + struct CurlSlistObject *prequote; + struct CurlSlistObject *telnetoptions; #ifdef HAVE_CURLOPT_RESOLVE - struct curl_slist *resolve; + struct CurlSlistObject *resolve; #endif #ifdef HAVE_CURL_7_20_0_OPTS - struct curl_slist *mail_rcpt; + struct CurlSlistObject *mail_rcpt; #endif #ifdef HAVE_CURLOPT_CONNECT_TO - struct curl_slist *connect_to; + struct CurlSlistObject *connect_to; #endif /* callbacks */ PyObject *w_cb; @@ -619,11 +626,13 @@ ssl_ctx_callback(CURL *curl, void *ssl_ctx, void *ptr); #if !defined(PYCURL_SINGLE_FILE) /* Type objects */ extern PyTypeObject Curl_Type; +extern PyTypeObject CurlSlist_Type; extern PyTypeObject CurlMulti_Type; extern PyTypeObject CurlShare_Type; extern PyObject *ErrorObject; extern PyTypeObject *p_Curl_Type; +extern PyTypeObject *p_CurlSlist_Type; extern PyTypeObject *p_CurlMulti_Type; extern PyTypeObject *p_CurlShare_Type; extern PyObject *khkey_type; -- cgit v1.2.1