Expose our async libcurl support to the Python api.

Kore already exposed parts of this via the kore.httpclient() method but
this commit takes it a bit further and exposes the libcurl interface
completely (including the setopt options).

tldr:

handle = kore.curl("ftp://ftp.eu.openbsd.org/pub/OpenBSD/README")
handle.setopt(kore.CURLOPT_TIMEOUT, 5)

data = await handle.run()
print("%s" % data.decode())
This commit is contained in:
Joris Vink 2020-01-18 19:43:38 +01:00
parent 6f31e14e83
commit 2d380cac3f
5 changed files with 581 additions and 12 deletions

View File

@ -11,8 +11,10 @@ MAN_DIR?=$(PREFIX)/share/man
SHARE_DIR=$(PREFIX)/share/kore
INCLUDE_DIR=$(PREFIX)/include/kore
GENERATED=
PLATFORM=platform.h
VERSION=src/version.c
PYTHON_CURLOPT=misc/curl/python_curlopt.h
S_SRC= src/kore.c src/buf.c src/config.c src/connection.c \
src/domain.c src/filemap.c src/fileref.c src/json.c src/mem.c \
@ -88,6 +90,7 @@ endif
ifneq ("$(PYTHON)", "")
S_SRC+=src/python.c
GENERATED+=$(PYTHON_CURLOPT)
KORE_PYTHON_LIB?=$(shell ./misc/python3-config.sh --ldflags)
KORE_PYTHON_INC?=$(shell ./misc/python3-config.sh --includes)
LDFLAGS+=$(KORE_PYTHON_LIB)
@ -142,13 +145,16 @@ endif
S_OBJS= $(S_SRC:src/%.c=$(OBJDIR)/%.o)
all: $(PLATFORM) $(VERSION) $(KORE) $(KODEV)
all: $(PLATFORM) $(GENERATED) $(VERSION) $(KORE) $(KODEV)
$(PLATFORM): $(OBJDIR) force
@if [ -f misc/$(OSNAME)-platform.sh ]; then \
misc/$(OSNAME)-platform.sh > $(OBJDIR)/$(PLATFORM) ; \
fi
$(PYTHON_CURLOPT): $(OBJDIR) force
@cp $(PYTHON_CURLOPT) $(OBJDIR)
$(VERSION): force
@if [ -d .git ]; then \
GIT_REVISION=`git rev-parse --short=8 HEAD`; \
@ -171,7 +177,7 @@ $(KORE): $(OBJDIR) $(S_OBJS)
$(CC) $(S_OBJS) $(LDFLAGS) -o $(KORE)
@echo $(FEATURES) $(FEATURES_INC) > kore.features
objects: $(OBJDIR) $(PLATFORM) $(S_OBJS)
objects: $(OBJDIR) $(PLATFORM) $(GENERATED) $(S_OBJS)
@echo $(LDFLAGS) > $(OBJDIR)/ldflags
@echo "$(FEATURES) $(FEATURES_INC)" > $(OBJDIR)/features

View File

@ -67,6 +67,7 @@ static PyObject *python_kore_pgsql_register(PyObject *, PyObject *);
#endif
#if defined(KORE_USE_CURL)
static PyObject *python_kore_curl_handle(PyObject *, PyObject *);
static PyObject *python_kore_httpclient(PyObject *,
PyObject *, PyObject *);
#endif
@ -109,6 +110,7 @@ static struct PyMethodDef pykore_methods[] = {
METH_VARARGS | METH_KEYWORDS),
#endif
#if defined(KORE_USE_CURL)
METHOD("curl", python_kore_curl_handle, METH_VARARGS),
METHOD("httpclient", python_kore_httpclient,
METH_VARARGS | METH_KEYWORDS),
#endif
@ -788,6 +790,70 @@ static PyTypeObject pyhttp_file_type = {
};
#if defined(KORE_USE_CURL)
#define CURL_CLIENT_OP_RUN 1
#define CURL_CLIENT_OP_RESULT 2
struct pycurl_handle {
PyObject_HEAD
char *url;
struct kore_curl curl;
};
struct pycurl_handle_op {
PyObject_HEAD
int state;
struct python_coro *coro;
struct pycurl_handle *handle;
};
static PyObject *pycurl_handle_op_await(PyObject *);
static PyObject *pycurl_handle_op_iternext(struct pycurl_handle_op *);
static void pycurl_handle_dealloc(struct pycurl_handle *);
static void pycurl_handle_op_dealloc(struct pycurl_handle_op *);
static PyObject *pycurl_handle_run(struct pycurl_handle *, PyObject *);
static PyObject *pycurl_handle_setopt(struct pycurl_handle *, PyObject *);
static PyObject *pycurl_handle_setopt_string(struct pycurl_handle *,
int, PyObject *);
static PyObject *pycurl_handle_setopt_long(struct pycurl_handle *,
int, PyObject *);
static PyMethodDef pycurl_handle_methods[] = {
METHOD("run", pycurl_handle_run, METH_VARARGS),
METHOD("setopt", pycurl_handle_setopt, METH_VARARGS),
METHOD(NULL, NULL, -1)
};
static PyTypeObject pycurl_handle_type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "kore.curl",
.tp_doc = "An asynchronous CURL handle",
.tp_methods = pycurl_handle_methods,
.tp_basicsize = sizeof(struct pycurl_handle),
.tp_dealloc = (destructor)pycurl_handle_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
};
static PyAsyncMethods pycurl_handle_op_async = {
(unaryfunc)pycurl_handle_op_await,
NULL,
NULL
};
static PyTypeObject pycurl_handle_op_type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "kore.curlop",
.tp_doc = "Asynchronous CURL operation",
.tp_as_async = &pycurl_handle_op_async,
.tp_iternext = (iternextfunc)pycurl_handle_op_iternext,
.tp_basicsize = sizeof(struct pycurl_handle_op),
.tp_dealloc = (destructor)pycurl_handle_op_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
};
struct pyhttp_client {
PyObject_HEAD
char *url;
@ -798,9 +864,6 @@ struct pyhttp_client {
int tlsverify;
};
#define PYHTTP_CLIENT_OP_RUN 1
#define PYHTTP_CLIENT_OP_RESULT 2
struct pyhttp_client_op {
PyObject_HEAD
int state;

46
misc/curl-extract-opt.sh Executable file
View File

@ -0,0 +1,46 @@
#!/bin/sh
if [ $# -ne 1 ]; then
echo "Usage: curl-extract-opt.sh /path/to/include/curl"
exit 1
fi
if [ ! -d $1 ]; then
echo "given argument is not a directory"
exit 1
fi
if [ ! -f "$1/curl.h" ]; then
echo "$1 does not contain curl.h"
exit 1
fi
if [ ! -f "$1/curlver.h" ]; then
echo "$1 does not contain curlver.h"
fi
version=`egrep "#define LIBCURL_VERSION " "$1/curlver.h" | awk '{ print $3 }' | sed s/\"//g`
echo "/* Auto generated on `date` from $version */"
cat << __EOF
struct {
const char *name;
int value;
PyObject *(*cb)(struct pycurl_handle *, int, PyObject *);
} py_curlopt[] = {
__EOF
egrep "^.*CINIT\(.*\),$" "$1/curl.h" | \
cut -d'(' -f 2 | cut -d ')' -f 1 | sed 's/,/ /g' | \
sed 's/OBJECTPOINT/NULL/g' | \
sed 's/STRINGPOINT/pycurl_handle_setopt_string/g' | \
sed 's/LONG/pycurl_handle_setopt_long/g' | \
sed 's/SLISTPOINT/NULL/g' | \
sed 's/FUNCTIONPOINT/NULL/g' | \
sed 's/OFF_T/NULL/g' | \
awk '{ printf "\t{ \"CURLOPT_%s\", %s, %s },\n", $1, $3, $2 } '
echo "\t{ NULL, 0, 0 }"
echo "};"

262
misc/curl/python_curlopt.h Normal file
View File

@ -0,0 +1,262 @@
/* Auto generated on Sat Jan 18 19:42:48 CET 2020 from 7.64.1 */
struct {
const char *name;
int value;
PyObject *(*cb)(struct pycurl_handle *, int, PyObject *);
} py_curlopt[] = {
{ "CURLOPT_WRITEDATA", 1, NULL },
{ "CURLOPT_URL", 2, pycurl_handle_setopt_string },
{ "CURLOPT_PORT", 3, pycurl_handle_setopt_long },
{ "CURLOPT_PROXY", 4, pycurl_handle_setopt_string },
{ "CURLOPT_USERPWD", 5, pycurl_handle_setopt_string },
{ "CURLOPT_PROXYUSERPWD", 6, pycurl_handle_setopt_string },
{ "CURLOPT_RANGE", 7, pycurl_handle_setopt_string },
{ "CURLOPT_READDATA", 9, NULL },
{ "CURLOPT_ERRORBUFFER", 10, NULL },
{ "CURLOPT_WRITEFUNCTION", 11, NULL },
{ "CURLOPT_READFUNCTION", 12, NULL },
{ "CURLOPT_TIMEOUT", 13, pycurl_handle_setopt_long },
{ "CURLOPT_INFILESIZE", 14, pycurl_handle_setopt_long },
{ "CURLOPT_POSTFIELDS", 15, NULL },
{ "CURLOPT_REFERER", 16, pycurl_handle_setopt_string },
{ "CURLOPT_FTPPORT", 17, pycurl_handle_setopt_string },
{ "CURLOPT_USERAGENT", 18, pycurl_handle_setopt_string },
{ "CURLOPT_LOW_SPEED_LIMIT", 19, pycurl_handle_setopt_long },
{ "CURLOPT_LOW_SPEED_TIME", 20, pycurl_handle_setopt_long },
{ "CURLOPT_RESUME_FROM", 21, pycurl_handle_setopt_long },
{ "CURLOPT_COOKIE", 22, pycurl_handle_setopt_string },
{ "CURLOPT_HTTPHEADER", 23, NULL },
{ "CURLOPT_HTTPPOST", 24, NULL },
{ "CURLOPT_SSLCERT", 25, pycurl_handle_setopt_string },
{ "CURLOPT_KEYPASSWD", 26, pycurl_handle_setopt_string },
{ "CURLOPT_CRLF", 27, pycurl_handle_setopt_long },
{ "CURLOPT_QUOTE", 28, NULL },
{ "CURLOPT_HEADERDATA", 29, NULL },
{ "CURLOPT_COOKIEFILE", 31, pycurl_handle_setopt_string },
{ "CURLOPT_SSLVERSION", 32, pycurl_handle_setopt_long },
{ "CURLOPT_TIMECONDITION", 33, pycurl_handle_setopt_long },
{ "CURLOPT_TIMEVALUE", 34, pycurl_handle_setopt_long },
{ "CURLOPT_CUSTOMREQUEST", 36, pycurl_handle_setopt_string },
{ "CURLOPT_STDERR", 37, NULL },
{ "CURLOPT_POSTQUOTE", 39, NULL },
{ "CURLOPT_NETRC", 51, pycurl_handle_setopt_long },
{ "CURLOPT_PROGRESSFUNCTION", 56, NULL },
{ "CURLOPT_PROGRESSDATA", 57, NULL },
{ "CURLOPT_AUTOREFERER", 58, pycurl_handle_setopt_long },
{ "CURLOPT_PROXYPORT", 59, pycurl_handle_setopt_long },
{ "CURLOPT_POSTFIELDSIZE", 60, pycurl_handle_setopt_long },
{ "CURLOPT_HTTPPROXYTUNNEL", 61, pycurl_handle_setopt_long },
{ "CURLOPT_INTERFACE", 62, pycurl_handle_setopt_string },
{ "CURLOPT_KRBLEVEL", 63, pycurl_handle_setopt_string },
{ "CURLOPT_SSL_VERIFYPEER", 64, pycurl_handle_setopt_long },
{ "CURLOPT_CAINFO", 65, pycurl_handle_setopt_string },
{ "CURLOPT_MAXREDIRS", 68, pycurl_handle_setopt_long },
{ "CURLOPT_FILETIME", 69, pycurl_handle_setopt_long },
{ "CURLOPT_TELNETOPTIONS", 70, NULL },
{ "CURLOPT_MAXCONNECTS", 71, pycurl_handle_setopt_long },
{ "CURLOPT_FRESH_CONNECT", 74, pycurl_handle_setopt_long },
{ "CURLOPT_FORBID_REUSE", 75, pycurl_handle_setopt_long },
{ "CURLOPT_RANDOM_FILE", 76, pycurl_handle_setopt_string },
{ "CURLOPT_EGDSOCKET", 77, pycurl_handle_setopt_string },
{ "CURLOPT_CONNECTTIMEOUT", 78, pycurl_handle_setopt_long },
{ "CURLOPT_HEADERFUNCTION", 79, NULL },
{ "CURLOPT_HTTPGET", 80, pycurl_handle_setopt_long },
{ "CURLOPT_SSL_VERIFYHOST", 81, pycurl_handle_setopt_long },
{ "CURLOPT_COOKIEJAR", 82, pycurl_handle_setopt_string },
{ "CURLOPT_SSL_CIPHER_LIST", 83, pycurl_handle_setopt_string },
{ "CURLOPT_HTTP_VERSION", 84, pycurl_handle_setopt_long },
{ "CURLOPT_FTP_USE_EPSV", 85, pycurl_handle_setopt_long },
{ "CURLOPT_SSLCERTTYPE", 86, pycurl_handle_setopt_string },
{ "CURLOPT_SSLKEY", 87, pycurl_handle_setopt_string },
{ "CURLOPT_SSLKEYTYPE", 88, pycurl_handle_setopt_string },
{ "CURLOPT_SSLENGINE", 89, pycurl_handle_setopt_string },
{ "CURLOPT_SSLENGINE_DEFAULT", 90, pycurl_handle_setopt_long },
{ "CURLOPT_DNS_CACHE_TIMEOUT", 92, pycurl_handle_setopt_long },
{ "CURLOPT_PREQUOTE", 93, NULL },
{ "CURLOPT_DEBUGFUNCTION", 94, NULL },
{ "CURLOPT_DEBUGDATA", 95, NULL },
{ "CURLOPT_COOKIESESSION", 96, pycurl_handle_setopt_long },
{ "CURLOPT_CAPATH", 97, pycurl_handle_setopt_string },
{ "CURLOPT_BUFFERSIZE", 98, pycurl_handle_setopt_long },
{ "CURLOPT_NOSIGNAL", 99, pycurl_handle_setopt_long },
{ "CURLOPT_SHARE", 100, NULL },
{ "CURLOPT_PROXYTYPE", 101, pycurl_handle_setopt_long },
{ "CURLOPT_ACCEPT_ENCODING", 102, pycurl_handle_setopt_string },
{ "CURLOPT_PRIVATE", 103, NULL },
{ "CURLOPT_HTTP200ALIASES", 104, NULL },
{ "CURLOPT_UNRESTRICTED_AUTH", 105, pycurl_handle_setopt_long },
{ "CURLOPT_FTP_USE_EPRT", 106, pycurl_handle_setopt_long },
{ "CURLOPT_HTTPAUTH", 107, pycurl_handle_setopt_long },
{ "CURLOPT_SSL_CTX_FUNCTION", 108, NULL },
{ "CURLOPT_SSL_CTX_DATA", 109, NULL },
{ "CURLOPT_FTP_CREATE_MISSING_DIRS", 110, pycurl_handle_setopt_long },
{ "CURLOPT_PROXYAUTH", 111, pycurl_handle_setopt_long },
{ "CURLOPT_FTP_RESPONSE_TIMEOUT", 112, pycurl_handle_setopt_long },
{ "CURLOPT_IPRESOLVE", 113, pycurl_handle_setopt_long },
{ "CURLOPT_MAXFILESIZE", 114, pycurl_handle_setopt_long },
{ "CURLOPT_INFILESIZE_LARGE", 115, NULL },
{ "CURLOPT_RESUME_FROM_LARGE", 116, NULL },
{ "CURLOPT_MAXFILESIZE_LARGE", 117, NULL },
{ "CURLOPT_NETRC_FILE", 118, pycurl_handle_setopt_string },
{ "CURLOPT_USE_SSL", 119, pycurl_handle_setopt_long },
{ "CURLOPT_POSTFIELDSIZE_LARGE", 120, NULL },
{ "CURLOPT_TCP_NODELAY", 121, pycurl_handle_setopt_long },
{ "CURLOPT_FTPSSLAUTH", 129, pycurl_handle_setopt_long },
{ "CURLOPT_IOCTLFUNCTION", 130, NULL },
{ "CURLOPT_IOCTLDATA", 131, NULL },
{ "CURLOPT_FTP_ACCOUNT", 134, pycurl_handle_setopt_string },
{ "CURLOPT_COOKIELIST", 135, pycurl_handle_setopt_string },
{ "CURLOPT_IGNORE_CONTENT_LENGTH", 136, pycurl_handle_setopt_long },
{ "CURLOPT_FTP_SKIP_PASV_IP", 137, pycurl_handle_setopt_long },
{ "CURLOPT_FTP_FILEMETHOD", 138, pycurl_handle_setopt_long },
{ "CURLOPT_LOCALPORT", 139, pycurl_handle_setopt_long },
{ "CURLOPT_LOCALPORTRANGE", 140, pycurl_handle_setopt_long },
{ "CURLOPT_CONNECT_ONLY", 141, pycurl_handle_setopt_long },
{ "CURLOPT_CONV_FROM_NETWORK_FUNCTION", 142, NULL },
{ "CURLOPT_CONV_TO_NETWORK_FUNCTION", 143, NULL },
{ "CURLOPT_CONV_FROM_UTF8_FUNCTION", 144, NULL },
{ "CURLOPT_MAX_SEND_SPEED_LARGE", 145, NULL },
{ "CURLOPT_MAX_RECV_SPEED_LARGE", 146, NULL },
{ "CURLOPT_FTP_ALTERNATIVE_TO_USER", 147, pycurl_handle_setopt_string },
{ "CURLOPT_SOCKOPTFUNCTION", 148, NULL },
{ "CURLOPT_SOCKOPTDATA", 149, NULL },
{ "CURLOPT_SSL_SESSIONID_CACHE", 150, pycurl_handle_setopt_long },
{ "CURLOPT_SSH_AUTH_TYPES", 151, pycurl_handle_setopt_long },
{ "CURLOPT_SSH_PUBLIC_KEYFILE", 152, pycurl_handle_setopt_string },
{ "CURLOPT_SSH_PRIVATE_KEYFILE", 153, pycurl_handle_setopt_string },
{ "CURLOPT_FTP_SSL_CCC", 154, pycurl_handle_setopt_long },
{ "CURLOPT_TIMEOUT_MS", 155, pycurl_handle_setopt_long },
{ "CURLOPT_CONNECTTIMEOUT_MS", 156, pycurl_handle_setopt_long },
{ "CURLOPT_HTTP_TRANSFER_DECODING", 157, pycurl_handle_setopt_long },
{ "CURLOPT_HTTP_CONTENT_DECODING", 158, pycurl_handle_setopt_long },
{ "CURLOPT_NEW_FILE_PERMS", 159, pycurl_handle_setopt_long },
{ "CURLOPT_NEW_DIRECTORY_PERMS", 160, pycurl_handle_setopt_long },
{ "CURLOPT_POSTREDIR", 161, pycurl_handle_setopt_long },
{ "CURLOPT_SSH_HOST_PUBLIC_KEY_MD5", 162, pycurl_handle_setopt_string },
{ "CURLOPT_OPENSOCKETFUNCTION", 163, NULL },
{ "CURLOPT_OPENSOCKETDATA", 164, NULL },
{ "CURLOPT_COPYPOSTFIELDS", 165, NULL },
{ "CURLOPT_PROXY_TRANSFER_MODE", 166, pycurl_handle_setopt_long },
{ "CURLOPT_SEEKFUNCTION", 167, NULL },
{ "CURLOPT_SEEKDATA", 168, NULL },
{ "CURLOPT_CRLFILE", 169, pycurl_handle_setopt_string },
{ "CURLOPT_ISSUERCERT", 170, pycurl_handle_setopt_string },
{ "CURLOPT_ADDRESS_SCOPE", 171, pycurl_handle_setopt_long },
{ "CURLOPT_CERTINFO", 172, pycurl_handle_setopt_long },
{ "CURLOPT_USERNAME", 173, pycurl_handle_setopt_string },
{ "CURLOPT_PASSWORD", 174, pycurl_handle_setopt_string },
{ "CURLOPT_PROXYUSERNAME", 175, pycurl_handle_setopt_string },
{ "CURLOPT_PROXYPASSWORD", 176, pycurl_handle_setopt_string },
{ "CURLOPT_NOPROXY", 177, pycurl_handle_setopt_string },
{ "CURLOPT_TFTP_BLKSIZE", 178, pycurl_handle_setopt_long },
{ "CURLOPT_SOCKS5_GSSAPI_NEC", 180, pycurl_handle_setopt_long },
{ "CURLOPT_PROTOCOLS", 181, pycurl_handle_setopt_long },
{ "CURLOPT_REDIR_PROTOCOLS", 182, pycurl_handle_setopt_long },
{ "CURLOPT_SSH_KNOWNHOSTS", 183, pycurl_handle_setopt_string },
{ "CURLOPT_SSH_KEYFUNCTION", 184, NULL },
{ "CURLOPT_SSH_KEYDATA", 185, NULL },
{ "CURLOPT_MAIL_FROM", 186, pycurl_handle_setopt_string },
{ "CURLOPT_MAIL_RCPT", 187, NULL },
{ "CURLOPT_FTP_USE_PRET", 188, pycurl_handle_setopt_long },
{ "CURLOPT_RTSP_REQUEST", 189, pycurl_handle_setopt_long },
{ "CURLOPT_RTSP_SESSION_ID", 190, pycurl_handle_setopt_string },
{ "CURLOPT_RTSP_STREAM_URI", 191, pycurl_handle_setopt_string },
{ "CURLOPT_RTSP_TRANSPORT", 192, pycurl_handle_setopt_string },
{ "CURLOPT_RTSP_CLIENT_CSEQ", 193, pycurl_handle_setopt_long },
{ "CURLOPT_RTSP_SERVER_CSEQ", 194, pycurl_handle_setopt_long },
{ "CURLOPT_INTERLEAVEDATA", 195, NULL },
{ "CURLOPT_INTERLEAVEFUNCTION", 196, NULL },
{ "CURLOPT_WILDCARDMATCH", 197, pycurl_handle_setopt_long },
{ "CURLOPT_CHUNK_BGN_FUNCTION", 198, NULL },
{ "CURLOPT_CHUNK_END_FUNCTION", 199, NULL },
{ "CURLOPT_FNMATCH_FUNCTION", 200, NULL },
{ "CURLOPT_CHUNK_DATA", 201, NULL },
{ "CURLOPT_FNMATCH_DATA", 202, NULL },
{ "CURLOPT_RESOLVE", 203, NULL },
{ "CURLOPT_TLSAUTH_USERNAME", 204, pycurl_handle_setopt_string },
{ "CURLOPT_TLSAUTH_PASSWORD", 205, pycurl_handle_setopt_string },
{ "CURLOPT_TLSAUTH_TYPE", 206, pycurl_handle_setopt_string },
{ "CURLOPT_TRANSFER_ENCODING", 207, pycurl_handle_setopt_long },
{ "CURLOPT_CLOSESOCKETFUNCTION", 208, NULL },
{ "CURLOPT_CLOSESOCKETDATA", 209, NULL },
{ "CURLOPT_GSSAPI_DELEGATION", 210, pycurl_handle_setopt_long },
{ "CURLOPT_DNS_SERVERS", 211, pycurl_handle_setopt_string },
{ "CURLOPT_ACCEPTTIMEOUT_MS", 212, pycurl_handle_setopt_long },
{ "CURLOPT_TCP_KEEPALIVE", 213, pycurl_handle_setopt_long },
{ "CURLOPT_TCP_KEEPIDLE", 214, pycurl_handle_setopt_long },
{ "CURLOPT_TCP_KEEPINTVL", 215, pycurl_handle_setopt_long },
{ "CURLOPT_SSL_OPTIONS", 216, pycurl_handle_setopt_long },
{ "CURLOPT_MAIL_AUTH", 217, pycurl_handle_setopt_string },
{ "CURLOPT_SASL_IR", 218, pycurl_handle_setopt_long },
{ "CURLOPT_XFERINFOFUNCTION", 219, NULL },
{ "CURLOPT_XOAUTH2_BEARER", 220, pycurl_handle_setopt_string },
{ "CURLOPT_DNS_INTERFACE", 221, pycurl_handle_setopt_string },
{ "CURLOPT_DNS_LOCAL_IP4", 222, pycurl_handle_setopt_string },
{ "CURLOPT_DNS_LOCAL_IP6", 223, pycurl_handle_setopt_string },
{ "CURLOPT_LOGIN_OPTIONS", 224, pycurl_handle_setopt_string },
{ "CURLOPT_SSL_ENABLE_NPN", 225, pycurl_handle_setopt_long },
{ "CURLOPT_SSL_ENABLE_ALPN", 226, pycurl_handle_setopt_long },
{ "CURLOPT_EXPECT_100_TIMEOUT_MS", 227, pycurl_handle_setopt_long },
{ "CURLOPT_PROXYHEADER", 228, NULL },
{ "CURLOPT_HEADEROPT", 229, pycurl_handle_setopt_long },
{ "CURLOPT_PINNEDPUBLICKEY", 230, pycurl_handle_setopt_string },
{ "CURLOPT_UNIX_SOCKET_PATH", 231, pycurl_handle_setopt_string },
{ "CURLOPT_SSL_VERIFYSTATUS", 232, pycurl_handle_setopt_long },
{ "CURLOPT_SSL_FALSESTART", 233, pycurl_handle_setopt_long },
{ "CURLOPT_PATH_AS_IS", 234, pycurl_handle_setopt_long },
{ "CURLOPT_PROXY_SERVICE_NAME", 235, pycurl_handle_setopt_string },
{ "CURLOPT_SERVICE_NAME", 236, pycurl_handle_setopt_string },
{ "CURLOPT_PIPEWAIT", 237, pycurl_handle_setopt_long },
{ "CURLOPT_DEFAULT_PROTOCOL", 238, pycurl_handle_setopt_string },
{ "CURLOPT_STREAM_WEIGHT", 239, pycurl_handle_setopt_long },
{ "CURLOPT_STREAM_DEPENDS", 240, NULL },
{ "CURLOPT_STREAM_DEPENDS_E", 241, NULL },
{ "CURLOPT_TFTP_NO_OPTIONS", 242, pycurl_handle_setopt_long },
{ "CURLOPT_CONNECT_TO", 243, NULL },
{ "CURLOPT_TCP_FASTOPEN", 244, pycurl_handle_setopt_long },
{ "CURLOPT_KEEP_SENDING_ON_ERROR", 245, pycurl_handle_setopt_long },
{ "CURLOPT_PROXY_CAINFO", 246, pycurl_handle_setopt_string },
{ "CURLOPT_PROXY_CAPATH", 247, pycurl_handle_setopt_string },
{ "CURLOPT_PROXY_SSL_VERIFYPEER", 248, pycurl_handle_setopt_long },
{ "CURLOPT_PROXY_SSL_VERIFYHOST", 249, pycurl_handle_setopt_long },
{ "CURLOPT_PROXY_SSLVERSION", 250, pycurl_handle_setopt_long },
{ "CURLOPT_PROXY_TLSAUTH_USERNAME", 251, pycurl_handle_setopt_string },
{ "CURLOPT_PROXY_TLSAUTH_PASSWORD", 252, pycurl_handle_setopt_string },
{ "CURLOPT_PROXY_TLSAUTH_TYPE", 253, pycurl_handle_setopt_string },
{ "CURLOPT_PROXY_SSLCERT", 254, pycurl_handle_setopt_string },
{ "CURLOPT_PROXY_SSLCERTTYPE", 255, pycurl_handle_setopt_string },
{ "CURLOPT_PROXY_SSLKEY", 256, pycurl_handle_setopt_string },
{ "CURLOPT_PROXY_SSLKEYTYPE", 257, pycurl_handle_setopt_string },
{ "CURLOPT_PROXY_KEYPASSWD", 258, pycurl_handle_setopt_string },
{ "CURLOPT_PROXY_SSL_CIPHER_LIST", 259, pycurl_handle_setopt_string },
{ "CURLOPT_PROXY_CRLFILE", 260, pycurl_handle_setopt_string },
{ "CURLOPT_PROXY_SSL_OPTIONS", 261, pycurl_handle_setopt_long },
{ "CURLOPT_PRE_PROXY", 262, pycurl_handle_setopt_string },
{ "CURLOPT_PROXY_PINNEDPUBLICKEY", 263, pycurl_handle_setopt_string },
{ "CURLOPT_ABSTRACT_UNIX_SOCKET", 264, pycurl_handle_setopt_string },
{ "CURLOPT_SUPPRESS_CONNECT_HEADERS", 265, pycurl_handle_setopt_long },
{ "CURLOPT_REQUEST_TARGET", 266, pycurl_handle_setopt_string },
{ "CURLOPT_SOCKS5_AUTH", 267, pycurl_handle_setopt_long },
{ "CURLOPT_SSH_COMPRESSION", 268, pycurl_handle_setopt_long },
{ "CURLOPT_MIMEPOST", 269, NULL },
{ "CURLOPT_TIMEVALUE_LARGE", 270, NULL },
{ "CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS", 271, pycurl_handle_setopt_long },
{ "CURLOPT_RESOLVER_START_FUNCTION", 272, NULL },
{ "CURLOPT_RESOLVER_START_DATA", 273, NULL },
{ "CURLOPT_HAPROXYPROTOCOL", 274, pycurl_handle_setopt_long },
{ "CURLOPT_DNS_SHUFFLE_ADDRESSES", 275, pycurl_handle_setopt_long },
{ "CURLOPT_TLS13_CIPHERS", 276, pycurl_handle_setopt_string },
{ "CURLOPT_PROXY_TLS13_CIPHERS", 277, pycurl_handle_setopt_string },
{ "CURLOPT_DISALLOW_USERNAME_IN_URL", 278, pycurl_handle_setopt_long },
{ "CURLOPT_DOH_URL", 279, pycurl_handle_setopt_string },
{ "CURLOPT_UPLOAD_BUFFERSIZE", 280, pycurl_handle_setopt_long },
{ "CURLOPT_UPKEEP_INTERVAL_MS", 281, pycurl_handle_setopt_long },
{ "CURLOPT_CURLU", 282, NULL },
{ "CURLOPT_TRAILERFUNCTION", 283, NULL },
{ "CURLOPT_TRAILERDATA", 284, NULL },
{ "CURLOPT_HTTP09_ALLOWED", 285, pycurl_handle_setopt_long },
{ "CURLOPT_ALTSVC_CTRL", 286, pycurl_handle_setopt_long },
{ "CURLOPT_ALTSVC", 287, pycurl_handle_setopt_string },
{ NULL, 0, 0 }
};

View File

@ -46,6 +46,10 @@
#include "python_api.h"
#include "python_methods.h"
#if defined(KORE_USE_CURL)
#include "python_curlopt.h"
#endif
#include <frameobject.h>
struct reqcall {
@ -115,7 +119,8 @@ static int pykore_pgsql_params(struct pykore_pgsql *, PyObject *);
#endif
#if defined(KORE_USE_CURL)
static void python_curl_callback(struct kore_curl *, void *);
static void python_curl_http_callback(struct kore_curl *, void *);
static void python_curl_handle_callback(struct kore_curl *, void *);
static PyObject *pyhttp_client_request(struct pyhttp_client *, int,
PyObject *);
#endif
@ -383,7 +388,7 @@ kore_python_coro_run(void)
#if defined(KORE_USE_CURL)
/*
* If a coroutine fired off an httpclient instance, immediately
* If a coroutine fired off a curl instance, immediately
* let it make progress.
*/
kore_curl_do_timeout();
@ -1506,7 +1511,13 @@ python_module_init(void)
#endif
#if defined(KORE_USE_CURL)
python_push_type("pycurlhandle", pykore, &pycurl_handle_type);
python_push_type("pyhttpclient", pykore, &pyhttp_client_type);
for (i = 0; py_curlopt[i].name != NULL; i++) {
python_push_integer(pykore, py_curlopt[i].name,
py_curlopt[i].value);
}
#endif
python_push_type("pyhttp_file", pykore, &pyhttp_file_type);
@ -5348,6 +5359,176 @@ pykore_pgsql_result(struct pykore_pgsql *pysql)
#endif
#if defined(KORE_USE_CURL)
static PyObject *
python_kore_curl_handle(PyObject *self, PyObject *args)
{
const char *url;
struct pycurl_handle *handle;
if (!PyArg_ParseTuple(args, "s", &url))
return (NULL);
handle = PyObject_New(struct pycurl_handle, &pycurl_handle_type);
if (handle == NULL)
return (NULL);
handle->url = kore_strdup(url);
memset(&handle->curl, 0, sizeof(handle->curl));
if (!kore_curl_init(&handle->curl, handle->url, KORE_CURL_ASYNC)) {
Py_DECREF((PyObject *)handle);
PyErr_SetString(PyExc_RuntimeError, "failed to setup call");
return (NULL);
}
return ((PyObject *)handle);
}
static void
pycurl_handle_dealloc(struct pycurl_handle *handle)
{
kore_free(handle->url);
kore_curl_cleanup(&handle->curl);
PyObject_Del((PyObject *)handle);
}
static PyObject *
pycurl_handle_setopt(struct pycurl_handle *handle, PyObject *args)
{
int i, opt;
PyObject *value;
if (!PyArg_ParseTuple(args, "iO", &opt, &value))
return (NULL);
for (i = 0; py_curlopt[i].name != NULL; i++) {
if (py_curlopt[i].value == opt)
break;
}
if (py_curlopt[i].name == NULL) {
PyErr_Format(PyExc_RuntimeError, "invalid option '%d'", opt);
return (NULL);
}
if (py_curlopt[i].cb == NULL) {
PyErr_Format(PyExc_RuntimeError, "option '%s' not implemented",
py_curlopt[i].name);
return (NULL);
}
return (py_curlopt[i].cb(handle, i, value));
}
static PyObject *
pycurl_handle_setopt_string(struct pycurl_handle *handle, int idx,
PyObject *obj)
{
const char *str;
if (!PyUnicode_Check(obj)) {
PyErr_Format(PyExc_RuntimeError,
"option '%s' requires a string as argument",
py_curlopt[idx]);
return (NULL);
}
if ((str = PyUnicode_AsUTF8(obj)) == NULL)
return (NULL);
curl_easy_setopt(&handle->curl, py_curlopt[idx].value, str);
Py_RETURN_TRUE;
}
static PyObject *
pycurl_handle_setopt_long(struct pycurl_handle *handle, int idx, PyObject *obj)
{
long val;
if (!PyLong_CheckExact(obj)) {
PyErr_Format(PyExc_RuntimeError,
"option '%s' requires a long as argument", py_curlopt[idx]);
return (NULL);
}
PyErr_Clear();
val = PyLong_AsLong(obj);
if (val == -1 && PyErr_Occurred())
return (NULL);
curl_easy_setopt(&handle->curl, py_curlopt[idx].value, val);
Py_RETURN_TRUE;
}
static PyObject *
pycurl_handle_run(struct pycurl_handle *handle, PyObject *args)
{
struct pycurl_handle_op *op;
op = PyObject_New(struct pycurl_handle_op, &pycurl_handle_op_type);
if (op == NULL)
return (NULL);
Py_INCREF(handle);
op->handle = handle;
op->coro = coro_running;
op->state = CURL_CLIENT_OP_RUN;
kore_curl_bind_callback(&handle->curl, python_curl_handle_callback, op);
return ((PyObject *)op);
}
static void
pycurl_handle_op_dealloc(struct pycurl_handle_op *op)
{
Py_DECREF(op->handle);
PyObject_Del((PyObject *)op);
}
static PyObject *
pycurl_handle_op_await(PyObject *op)
{
Py_INCREF(op);
return (op);
}
static PyObject *
pycurl_handle_op_iternext(struct pycurl_handle_op *op)
{
size_t len;
PyObject *result;
const u_int8_t *response;
if (op->state == CURL_CLIENT_OP_RUN) {
kore_curl_run(&op->handle->curl);
op->state = CURL_CLIENT_OP_RESULT;
Py_RETURN_NONE;
}
if (!kore_curl_success(&op->handle->curl)) {
/* Do not log the url here, may contain some sensitive data. */
PyErr_Format(PyExc_RuntimeError, "request failed: %s",
kore_curl_strerror(&op->handle->curl));
return (NULL);
}
kore_curl_response_as_bytes(&op->handle->curl, &response, &len);
if ((result = PyBytes_FromStringAndSize((const char *)response,
len)) == NULL)
return (NULL);
PyErr_SetObject(PyExc_StopIteration, result);
Py_DECREF(result);
return (NULL);
}
static PyObject *
python_kore_httpclient(PyObject *self, PyObject *args, PyObject *kwargs)
{
@ -5524,13 +5705,13 @@ pyhttp_client_request(struct pyhttp_client *client, int m, PyObject *kwargs)
op->headers = 0;
op->coro = coro_running;
op->state = PYHTTP_CLIENT_OP_RUN;
op->state = CURL_CLIENT_OP_RUN;
Py_INCREF(client);
op->client = client;
kore_curl_http_setup(&op->curl, m, ptr, length);
kore_curl_bind_callback(&op->curl, python_curl_callback, op);
kore_curl_bind_callback(&op->curl, python_curl_http_callback, op);
/* Go in with our own bare hands. */
if (client->unix != NULL) {
@ -5611,9 +5792,9 @@ pyhttp_client_op_iternext(struct pyhttp_client_op *op)
const u_int8_t *response;
PyObject *result, *tuple, *dict, *value;
if (op->state == PYHTTP_CLIENT_OP_RUN) {
if (op->state == CURL_CLIENT_OP_RUN) {
kore_curl_run(&op->curl);
op->state = PYHTTP_CLIENT_OP_RESULT;
op->state = CURL_CLIENT_OP_RESULT;
Py_RETURN_NONE;
}
@ -5673,7 +5854,7 @@ pyhttp_client_op_iternext(struct pyhttp_client_op *op)
}
static void
python_curl_callback(struct kore_curl *curl, void *arg)
python_curl_http_callback(struct kore_curl *curl, void *arg)
{
struct pyhttp_client_op *op = arg;
@ -5682,4 +5863,15 @@ python_curl_callback(struct kore_curl *curl, void *arg)
else
python_coro_wakeup(op->coro);
}
static void
python_curl_handle_callback(struct kore_curl *curl, void *arg)
{
struct pycurl_handle_op *op = arg;
if (op->coro->request != NULL)
http_request_wakeup(op->coro->request);
else
python_coro_wakeup(op->coro);
}
#endif